Here is a second version of the patch based on feedback from Kristian. Instead of modifying the existing eglCreateWaylandBufferFromImageWL function it adds a second version to support passing multiple images. The worry is that the original version of the extension is already being used so it's a bit painful to change it in an incompatible way now. The original function is probably a good convenience wrapper for the common case where only one image is used anyway so I don't think it's too messy to leave it in.
------- >8 --------------- (use git am --scissors to automatically chop here) An additional function is added to the eglCreateWaylandBufferFromImageWL extension which instead of just taking a single image can take a set of images. Each EGLImage will represent a plane from a planar image. The overall format for the combined set of images isn't stored in the EGLImages so instead of trying to deduce it the new function also has a format parameter. This should contain the EGLenum returned in the EGL_TEXTURE_FORMAT attribute retreived via eglQueryWaylandBufferWL. The exact wl_drm_format value to use is deduced from the EGL format and the size of the images. The number of images passed must match the number of planes required by the texture format. The EGLImages must all point to the same buffer and this is determined by checking the names of the images. If any of these criteria fail then the function will report EGL_BAD_MATCH. --- .../specs/WL_create_wayland_buffer_from_image.spec | 77 ++++-- include/EGL/eglmesaext.h | 2 + src/egl/drivers/dri2/platform_wayland.c | 274 ++++++++++++++++++--- src/egl/main/eglapi.c | 35 +++ src/egl/main/eglapi.h | 4 +- 5 files changed, 343 insertions(+), 49 deletions(-) diff --git a/docs/specs/WL_create_wayland_buffer_from_image.spec b/docs/specs/WL_create_wayland_buffer_from_image.spec index aa5eb4d..b08c8a2 100644 --- a/docs/specs/WL_create_wayland_buffer_from_image.spec +++ b/docs/specs/WL_create_wayland_buffer_from_image.spec @@ -22,7 +22,7 @@ Status Version - Version 2, October 25, 2013 + Version 3, February 5, 2014 Number @@ -35,20 +35,22 @@ Dependencies EGL_KHR_base_image is required. + EGL_WL_bind_wayland_display affects the definition of this extension. + Overview - This extension provides an entry point to create a wl_buffer which shares - its contents with a given EGLImage. The expected use case for this is in a - nested Wayland compositor which is using subsurfaces to present buffers - from its clients. Using this extension it can attach the client buffers - directly to the subsurface without having to blit the contents into an - intermediate buffer. The compositing can then be done in the parent - compositor. + This extension provides entry points to create a wl_buffer which shares + its contents with a given EGLImage or a set of planar EGLImages. The + expected use case for this is in a nested Wayland compositor which is + using subsurfaces to present buffers from its clients. Using this + extension it can attach the client buffers directly to the subsurface + without having to blit the contents into an intermediate buffer. The + compositing can then be done in the parent compositor. - The nested compositor can create an EGLImage from a client buffer resource - using the existing WL_bind_wayland_display extension. It should also be - possible to create buffers using other types of images although there is - no expected use case for that. + The nested compositor can create the EGLImages from a client buffer + resource using the existing WL_bind_wayland_display extension. It should + also be possible to create buffers using other types of images although + there is no expected use case for that. IP Status @@ -59,6 +61,11 @@ New Procedures and Functions struct wl_buffer *eglCreateWaylandBufferFromImageWL(EGLDisplay dpy, EGLImageKHR image); + struct wl_buffer *eglCreateWaylandBufferFromPlanarImagesWL(EGLDisplay dpy, + EGLenum format, + EGLImageKHR *ims, + EGLint n_images); + New Tokens None. @@ -67,8 +74,8 @@ Additions to the EGL 1.4 Specification: To create a client-side wl_buffer from an EGLImage call - struct wl_buffer *eglCreateWaylandBufferFromImageWL(EGLDisplay dpy, - EGLImageKHR image); + struct wl_buffer *eglCreateWaylandBufferFromImageWL(EGLDisplay dpy, + EGLImageKHR image); The returned buffer will share the contents with the given EGLImage. Any updates to the image will also be updated in the wl_buffer. Typically the @@ -82,13 +89,50 @@ Additions to the EGL 1.4 Specification: format or tiling mode or that the buffer is in memory that is inaccessible to the GPU that the given EGLDisplay is using. + Alternatively it is possible to create a buffer from a planar image where + each plane is contained in a separate EGLImage using the following + function: + + struct wl_buffer *eglCreateWaylandBufferFromPlanarImagesWL(EGLDisplay dpy, + EGLenum format, + EGLImageKHR *ims, + EGLint n_images); + + The number of images passed should match the number of planes required for + the format given in the format parameter. For non-planar formats this will + just be 1. Each image represents one plane within the buffer. + + The formats accepted in the format parameter are: + + EGL_TEXTURE_RGB A single image is used which contains RGB + components. + EGL_TEXTURE_RGBA A single image is used which contains RGBA + components. + EGL_TEXTURE_Y_U_V_WL Three images are used; one containing the + luminance component, one containing the + U component of the chrominance data and another + containing the V component. + EGL_TEXTURE_Y_UV_WL Two images are used; one containing a single + luminance component and the other containing the + two chrominance components. + EGL_TEXTURE_Y_XUXV_WL Two images are used; one containing a single + luminance component and the other containing the + two chrominance components along with two unused + components. + +Dependencies on EGL_WL_bind_wayland_display + + If EGL_WL_bind_wayland_display is not implemented then references to + EGL_TEXTURE_Y_U_V_WL, EGL_TEXTURE_Y_UV_WL and EGL_TEXTURE_Y_XUXV_WL are + void. + Issues 1) Under what circumstances can the EGL_BAD_MATCH error be generated? Does this include for example unsupported tiling modes? RESOLVED: Yes, the EGL_BAD_MATCH error can be generated for any reason - which prevents the implementation from representing the image as a + which prevents the implementation from representing the images as a wl_buffer. For example, these problems can be but are not limited to unsupported tiling modes, inaccessible memory or an unsupported pixel format. @@ -99,3 +143,6 @@ Revision History Initial draft (Neil Roberts) Version 2, October 25, 2013 Added a note about more possible reasons for returning EGL_BAD_FORMAT. + Version 3, February 5, 2014 + Support for passing multiple images in order to create a planar + buffer. diff --git a/include/EGL/eglmesaext.h b/include/EGL/eglmesaext.h index b3229e3..b78aa8b 100644 --- a/include/EGL/eglmesaext.h +++ b/include/EGL/eglmesaext.h @@ -139,8 +139,10 @@ typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYWAYLANDBUFFERWL) (EGLDisplay dpy, st #ifdef EGL_EGLEXT_PROTOTYPES EGLAPI struct wl_buffer * EGLAPIENTRY eglCreateWaylandBufferFromImageWL(EGLDisplay dpy, EGLImageKHR image); +EGLAPI struct wl_buffer * EGLAPIENTRY eglCreateWaylandBufferFromPlanarImagesWL(EGLDisplay dpy, EGLenum format, EGLImageKHR *images, EGLint n_images); #endif typedef struct wl_buffer * (EGLAPIENTRYP PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWL) (EGLDisplay dpy, EGLImageKHR image); +typedef struct wl_buffer * (EGLAPIENTRYP PFNEGLCREATEWAYLANDBUFFERFROMPLANARIMAGESWL) (EGLDisplay dpy, EGLenum format, EGLImageKHR *images, EGLint n_images); #endif diff --git a/src/egl/drivers/dri2/platform_wayland.c b/src/egl/drivers/dri2/platform_wayland.c index 089ed62..556a45a 100644 --- a/src/egl/drivers/dri2/platform_wayland.c +++ b/src/egl/drivers/dri2/platform_wayland.c @@ -698,64 +698,238 @@ dri2_can_handle_format(struct dri2_egl_display *dri2_dpy, return EGL_FALSE; } +struct plane_attribs { + struct dri2_egl_image *dri2_img; + __DRIimage *image; + int offset; + int stride; + int width; + int height; + int format; +}; + +struct planar_format { + enum wl_drm_format wl_format; + EGLenum components; + int n_planes; + struct { + int width_shift; + int height_shift; + uint32_t dri_format; + } planes[3]; +}; + +static const struct planar_format +planar_formats[] = { + { WL_DRM_FORMAT_ARGB8888, EGL_TEXTURE_RGBA, 1, + { { 0, 0, __DRI_IMAGE_FORMAT_ARGB8888 } } }, + + { WL_DRM_FORMAT_XRGB8888, EGL_TEXTURE_RGB, 1, + { { 0, 0, __DRI_IMAGE_FORMAT_XRGB8888 }, } }, + + { WL_DRM_FORMAT_RGB565, EGL_TEXTURE_RGB, 1, + { { 0, 0, __DRI_IMAGE_FORMAT_RGB565 } } }, + + { WL_DRM_FORMAT_YUV410, EGL_TEXTURE_Y_U_V_WL, 3, + { { 0, 0, __DRI_IMAGE_FORMAT_R8 }, + { 2, 2, __DRI_IMAGE_FORMAT_R8 }, + { 2, 2, __DRI_IMAGE_FORMAT_R8 } } }, + + { WL_DRM_FORMAT_YUV411, EGL_TEXTURE_Y_U_V_WL, 3, + { { 0, 0, __DRI_IMAGE_FORMAT_R8 }, + { 2, 0, __DRI_IMAGE_FORMAT_R8 }, + { 2, 0, __DRI_IMAGE_FORMAT_R8 } } }, + + { WL_DRM_FORMAT_YUV420, EGL_TEXTURE_Y_U_V_WL, 3, + { { 0, 0, __DRI_IMAGE_FORMAT_R8 }, + { 1, 1, __DRI_IMAGE_FORMAT_R8 }, + { 1, 1, __DRI_IMAGE_FORMAT_R8 } } }, + + { WL_DRM_FORMAT_YUV422, EGL_TEXTURE_Y_U_V_WL, 3, + { { 0, 0, __DRI_IMAGE_FORMAT_R8 }, + { 1, 0, __DRI_IMAGE_FORMAT_R8 }, + { 1, 0, __DRI_IMAGE_FORMAT_R8 } } }, + + { WL_DRM_FORMAT_YUV444, EGL_TEXTURE_Y_U_V_WL, 3, + { { 0, 0, __DRI_IMAGE_FORMAT_R8 }, + { 0, 0, __DRI_IMAGE_FORMAT_R8 }, + { 0, 0, __DRI_IMAGE_FORMAT_R8 } } }, + + { WL_DRM_FORMAT_NV12, EGL_TEXTURE_Y_UV_WL, 2, + { { 0, 0, __DRI_IMAGE_FORMAT_R8 }, + { 1, 1, __DRI_IMAGE_FORMAT_GR88 } } }, + + { WL_DRM_FORMAT_NV16, EGL_TEXTURE_Y_UV_WL, 2, + { { 0, 0, __DRI_IMAGE_FORMAT_R8 }, + { 1, 0, __DRI_IMAGE_FORMAT_GR88 } } }, + + { WL_DRM_FORMAT_YUYV, EGL_TEXTURE_Y_XUXV_WL, 2, + { { 0, 0, __DRI_IMAGE_FORMAT_GR88 }, + { 1, 0, __DRI_IMAGE_FORMAT_ARGB8888 } } } +}; + +static void +get_planar_format_max_shifts(const struct planar_format *planar_format, + int *max_width_shift, + int *max_height_shift) +{ + int i; + + *max_width_shift = 0; + *max_height_shift = 0; + + for (i = 0; i < planar_format->n_planes; i++) { + if (planar_format->planes[i].width_shift > *max_width_shift) + *max_width_shift = planar_format->planes[i].width_shift; + if (planar_format->planes[i].height_shift > *max_height_shift) + *max_height_shift = planar_format->planes[i].height_shift; + } +} + +static EGLBoolean +check_plane_attribs_match_format(const struct plane_attribs *planes, + const struct planar_format *planar_format) +{ + int width, height; + int i; + + for (i = 0; i < planar_format->n_planes; i++) { + if (planes[i].format != planar_format->planes[i].dri_format) + return EGL_FALSE; + + width = planes[0].width >> planar_format->planes[i].width_shift; + height = planes[0].height >> planar_format->planes[i].height_shift; + + if (planes[i].width != width || planes[i].height != height) + return EGL_FALSE; + } + + return EGL_TRUE; +} + +static EGLBoolean +find_planar_format(EGLenum components, + const struct plane_attribs *planes, + int n_planes, + enum wl_drm_format *wl_format) +{ + const struct planar_format *planar_format; + int max_width_shift, max_height_shift; + int i; + + for (i = 0; i < ARRAY_SIZE(planar_formats); i++) { + planar_format = planar_formats + i; + + if (n_planes != planar_format->n_planes || + components != planar_format->components) + continue; + + /* Check if the size of the first plane is a multiple of the maximum + * shift */ + get_planar_format_max_shifts(planar_format, + &max_width_shift, + &max_height_shift); + if ((planes[0].width & ((1 << max_width_shift) - 1)) != 0 || + (planes[0].height & ((1 << max_height_shift) - 1)) != 0) + continue; + + if (!check_plane_attribs_match_format(planes, planar_format)) + continue; + + *wl_format = planar_format->wl_format; + return EGL_TRUE; + } + + return EGL_FALSE; +} + static struct wl_buffer * -dri2_create_wayland_buffer_from_image_wl(_EGLDriver *drv, - _EGLDisplay *disp, - _EGLImage *img) +dri2_create_wayland_buffer_from_planar_images_wl(_EGLDriver *drv, + _EGLDisplay *disp, + EGLenum format, + _EGLImage **images, + EGLint n_images) { struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); - struct dri2_egl_image *dri2_img = dri2_egl_image(img); - __DRIimage *image = dri2_img->dri_image; struct wl_buffer *buffer; - int width, height, format, pitch; enum wl_drm_format wl_format; + struct plane_attribs plane[3]; + int name, other_name; + int i; - dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_FORMAT, &format); + if (n_images < 1 || n_images > 3) + goto bad_format; - switch (format) { - case __DRI_IMAGE_FORMAT_ARGB8888: - if (!dri2_can_handle_format(dri2_dpy, WL_DRM_FORMAT_ARGB8888)) - goto bad_format; - wl_format = WL_DRM_FORMAT_ARGB8888; - break; - case __DRI_IMAGE_FORMAT_XRGB8888: - if (!dri2_can_handle_format(dri2_dpy, WL_DRM_FORMAT_XRGB8888)) + for (i = 0; i < n_images; i++) { + plane[i].dri2_img = dri2_egl_image(images[i]); + plane[i].image = plane[i].dri2_img->dri_image; + } + + /* Verify the all of the images have the same name (ie, they point to the + * same buffer) */ + dri2_dpy->image->queryImage(plane[0].image, __DRI_IMAGE_ATTRIB_NAME, &name); + for (i = 1; i < n_images; i++) { + dri2_dpy->image->queryImage(plane[i].image, + __DRI_IMAGE_ATTRIB_NAME, + &other_name); + if (other_name != name) goto bad_format; - wl_format = WL_DRM_FORMAT_XRGB8888; - break; - default: - goto bad_format; } - dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_WIDTH, &width); - dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_HEIGHT, &height); - dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_STRIDE, &pitch); + for (i = 0; i < n_images; i++) { + dri2_dpy->image->queryImage(plane[i].image, + __DRI_IMAGE_ATTRIB_FORMAT, + &plane[i].format); + dri2_dpy->image->queryImage(plane[i].image, + __DRI_IMAGE_ATTRIB_WIDTH, + &plane[i].width); + dri2_dpy->image->queryImage(plane[i].image, + __DRI_IMAGE_ATTRIB_HEIGHT, + &plane[i].height); + dri2_dpy->image->queryImage(plane[i].image, + __DRI_IMAGE_ATTRIB_STRIDE, + &plane[i].stride); + + if (dri2_dpy->image->base.version >= 9) + dri2_dpy->image->queryImage(plane[i].image, + __DRI_IMAGE_ATTRIB_OFFSET, + &plane[i].offset); + else + plane[i].offset = 0; + } + + /* We need prime to create planar buffers */ + if (n_images > 1 && + (dri2_dpy->capabilities & WL_DRM_CAPABILITY_PRIME) == 0) + goto bad_format; + + if (!find_planar_format(format, plane, n_images, &wl_format)) + goto bad_format; + + if (!dri2_can_handle_format(dri2_dpy, wl_format)) + goto bad_format; if (dri2_dpy->capabilities & WL_DRM_CAPABILITY_PRIME) { int fd; - dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_FD, &fd); + dri2_dpy->image->queryImage(plane[0].image, __DRI_IMAGE_ATTRIB_FD, &fd); buffer = wl_drm_create_prime_buffer(dri2_dpy->wl_drm, fd, - width, height, + plane[0].width, plane[0].height, wl_format, - 0, pitch, - 0, 0, - 0, 0); + plane[0].offset, plane[0].stride, + plane[1].offset, plane[1].stride, + plane[2].offset, plane[2].stride); close(fd); } else { - int name; - - dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_NAME, &name); - buffer = wl_drm_create_buffer(dri2_dpy->wl_drm, name, - width, height, - pitch, + plane[0].width, plane[0].height, + plane[0].stride, wl_format); } @@ -773,6 +947,38 @@ bad_format: return NULL; } +static struct wl_buffer * +dri2_create_wayland_buffer_from_image_wl(_EGLDriver *drv, + _EGLDisplay *disp, + _EGLImage *img) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + struct dri2_egl_image *dri2_img = dri2_egl_image(img); + __DRIimage *image = dri2_img->dri_image; + EGLenum components; + int format; + + dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_FORMAT, &format); + + switch (format) { + case __DRI_IMAGE_FORMAT_ARGB8888: + components = EGL_TEXTURE_RGBA; + break; + case __DRI_IMAGE_FORMAT_XRGB8888: + components = EGL_TEXTURE_RGB; + break; + default: + _eglError(EGL_BAD_MATCH, "unsupported image format"); + return NULL; + } + + return dri2_create_wayland_buffer_from_planar_images_wl(drv, + disp, + components, + &img, + 1 /* n_imgs */); +} + static int dri2_wayland_authenticate(_EGLDisplay *disp, uint32_t id) { @@ -1013,6 +1219,8 @@ dri2_initialize_wayland(_EGLDriver *drv, _EGLDisplay *disp) drv->API.CreateWaylandBufferFromImageWL = dri2_create_wayland_buffer_from_image_wl; + drv->API.CreateWaylandBufferFromPlanarImagesWL = + dri2_create_wayland_buffer_from_planar_images_wl; dri2_dpy = calloc(1, sizeof *dri2_dpy); if (!dri2_dpy) diff --git a/src/egl/main/eglapi.c b/src/egl/main/eglapi.c index 59e214c..b0b4e72 100644 --- a/src/egl/main/eglapi.c +++ b/src/egl/main/eglapi.c @@ -975,6 +975,7 @@ eglGetProcAddress(const char *procname) #endif #ifdef EGL_WL_create_wayland_buffer_from_image { "eglCreateWaylandBufferFromImageWL", (_EGLProc) eglCreateWaylandBufferFromImageWL }, + { "eglCreateWaylandBufferFromPlanarImagesWL", (_EGLProc) eglCreateWaylandBufferFromPlanarImagesWL }, #endif { "eglPostSubBufferNV", (_EGLProc) eglPostSubBufferNV }, #ifdef EGL_EXT_swap_buffers_with_damage @@ -1606,6 +1607,40 @@ eglQueryWaylandBufferWL(EGLDisplay dpy, struct wl_resource *buffer, #ifdef EGL_WL_create_wayland_buffer_from_image struct wl_buffer * EGLAPIENTRY +eglCreateWaylandBufferFromPlanarImagesWL(EGLDisplay dpy, + EGLenum format, + EGLImageKHR *images, + EGLint n_images) +{ + _EGLDisplay *disp = _eglLockDisplay(dpy); + _EGLImage *imgs[3]; + _EGLDriver *drv; + struct wl_buffer *ret; + int i; + + _EGL_CHECK_DISPLAY(disp, NULL, drv); + assert(disp->Extensions.WL_create_wayland_buffer_from_image); + + if (n_images < 0 || n_images > ARRAY_SIZE(imgs)) + RETURN_EGL_ERROR(disp, EGL_BAD_PARAMETER, NULL); + + for (i = 0; i < n_images; i++) { + imgs[i] = _eglLookupImage(images[i], disp); + + if (!imgs[i]) + RETURN_EGL_ERROR(disp, EGL_BAD_PARAMETER, NULL); + } + + ret = drv->API.CreateWaylandBufferFromPlanarImagesWL(drv, + disp, + format, + imgs, + n_images); + + RETURN_EGL_EVAL(disp, ret); +} + +struct wl_buffer * EGLAPIENTRY eglCreateWaylandBufferFromImageWL(EGLDisplay dpy, EGLImageKHR image) { _EGLDisplay *disp = _eglLockDisplay(dpy); diff --git a/src/egl/main/eglapi.h b/src/egl/main/eglapi.h index 091c09c..7e8d797 100644 --- a/src/egl/main/eglapi.h +++ b/src/egl/main/eglapi.h @@ -127,7 +127,8 @@ typedef EGLBoolean (*QueryWaylandBufferWL_t)(_EGLDriver *drv, _EGLDisplay *displ #endif #ifdef EGL_WL_create_wayland_buffer_from_image -typedef struct wl_buffer * (*CreateWaylandBufferFromImageWL_t)(_EGLDriver *drv, _EGLDisplay *disp, _EGLImage *img); +typedef struct wl_buffer * (*CreateWaylandBufferFromImageWL_t)(_EGLDriver *drv, _EGLDisplay *disp, _EGLImage *images); +typedef struct wl_buffer * (*CreateWaylandBufferFromPlanarImagesWL_t)(_EGLDriver *drv, _EGLDisplay *disp, EGLenum format, _EGLImage **images, EGLint n_images); #endif typedef EGLBoolean (*PostSubBufferNV_t)(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surface, EGLint x, EGLint y, EGLint width, EGLint height); @@ -216,6 +217,7 @@ struct _egl_api #ifdef EGL_WL_create_wayland_buffer_from_image CreateWaylandBufferFromImageWL_t CreateWaylandBufferFromImageWL; + CreateWaylandBufferFromPlanarImagesWL_t CreateWaylandBufferFromPlanarImagesWL; #endif #ifdef EGL_EXT_swap_buffers_with_damage -- 1.8.5.3 _______________________________________________ wayland-devel mailing list wayland-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/wayland-devel