From e9700f62af4003f6a6c3fd64309b0e119a670ab6 Mon Sep 17 00:00:00 2001 From: Alexandros Frantzis Date: Tue, 8 Jan 2019 12:32:12 +0200 Subject: [PATCH] clients/simple-dmabuf-egl: Support dmabuf format modifiers Take into account format modifiers advertised by the compositor and the EGL implementation and supported by the buffer creation mechanism, to select the optimal buffer modifier. Signed-off-by: Alexandros Frantzis --- clients/meson.build | 2 +- clients/simple-dmabuf-egl.c | 264 ++++++++++++++++++++++++++++++------ 2 files changed, 224 insertions(+), 42 deletions(-) diff --git a/clients/meson.build b/clients/meson.build index e4da973b..3c4d86ab 100644 --- a/clients/meson.build +++ b/clients/meson.build @@ -61,7 +61,7 @@ simple_clients = [ ], 'dep_objs': [ dep_wayland_client, - dep_libdrm_headers + dep_libdrm ], 'deps': [ 'egl', 'glesv2', 'gbm' ] }, diff --git a/clients/simple-dmabuf-egl.c b/clients/simple-dmabuf-egl.c index b78d6b7b..f7d41f9b 100644 --- a/clients/simple-dmabuf-egl.c +++ b/clients/simple-dmabuf-egl.c @@ -39,9 +39,11 @@ #include #include +#include #include #include +#include "shared/helpers.h" #include "shared/platform.h" #include "shared/zalloc.h" #include "xdg-shell-unstable-v6-client-protocol.h" @@ -62,6 +64,9 @@ /* Possible options that affect the displayed image */ #define OPT_IMMEDIATE 1 /* create wl_buffer immediately */ +#define BUFFER_FORMAT DRM_FORMAT_XRGB8888 +#define MAX_BUFFER_PLANES 4 + struct display { struct wl_display *display; struct wl_registry *registry; @@ -69,11 +74,14 @@ struct display { struct zxdg_shell_v6 *shell; struct zwp_fullscreen_shell_v1 *fshell; struct zwp_linux_dmabuf_v1 *dmabuf; - int xrgb8888_format_found; + uint64_t *modifiers; + int modifiers_count; int req_dmabuf_immediate; struct { EGLDisplay display; EGLContext context; + bool has_dma_buf_import_modifiers; + PFNEGLQUERYDMABUFMODIFIERSEXTPROC query_dma_buf_modifiers; PFNEGLCREATEIMAGEKHRPROC create_image; PFNEGLDESTROYIMAGEKHRPROC destroy_image; PFNGLEGLIMAGETARGETTEXTURE2DOESPROC image_target_texture_2d; @@ -91,12 +99,14 @@ struct buffer { struct gbm_bo *bo; - int dmabuf_fd; - int width; int height; - uint32_t stride; int format; + uint64_t modifier; + int plane_count; + int dmabuf_fds[MAX_BUFFER_PLANES]; + uint32_t strides[MAX_BUFFER_PLANES]; + uint32_t offsets[MAX_BUFFER_PLANES]; EGLImageKHR egl_image; GLuint gl_texture; @@ -137,6 +147,8 @@ static const struct wl_buffer_listener buffer_listener = { static void buffer_free(struct buffer *buf) { + int i; + if (buf->gl_fbo) glDeleteFramebuffers(1, &buf->gl_fbo); @@ -154,8 +166,10 @@ buffer_free(struct buffer *buf) if (buf->bo) gbm_bo_destroy(buf->bo); - if (buf->dmabuf_fd >= 0) - close(buf->dmabuf_fd); + for (i = 0; i < buf->plane_count; ++i) { + if (buf->dmabuf_fds[i] >= 0) + close(buf->dmabuf_fds[i]); + } } static void @@ -192,15 +206,52 @@ static const struct zwp_linux_buffer_params_v1_listener params_listener = { static bool create_fbo_for_buffer(struct display *display, struct buffer *buffer) { - EGLint attribs[] = { - EGL_WIDTH, buffer->width, - EGL_HEIGHT, buffer->height, - EGL_LINUX_DRM_FOURCC_EXT, buffer->format, - EGL_DMA_BUF_PLANE0_FD_EXT, buffer->dmabuf_fd, - EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0, - EGL_DMA_BUF_PLANE0_PITCH_EXT, (int) buffer->stride, - EGL_NONE - }; + static const int general_attribs = 3; + static const int plane_attribs = 5; + static const int entries_per_attrib = 2; + EGLint attribs[(general_attribs + plane_attribs * MAX_BUFFER_PLANES) * + entries_per_attrib + 1]; + unsigned int atti = 0; + + attribs[atti++] = EGL_WIDTH; + attribs[atti++] = buffer->width; + attribs[atti++] = EGL_HEIGHT; + attribs[atti++] = buffer->height; + attribs[atti++] = EGL_LINUX_DRM_FOURCC_EXT; + attribs[atti++] = buffer->format; + +#define ADD_PLANE_ATTRIBS(plane_idx) { \ + attribs[atti++] = EGL_DMA_BUF_PLANE ## plane_idx ## _FD_EXT; \ + attribs[atti++] = buffer->dmabuf_fds[plane_idx]; \ + attribs[atti++] = EGL_DMA_BUF_PLANE ## plane_idx ## _OFFSET_EXT; \ + attribs[atti++] = (int) buffer->offsets[plane_idx]; \ + attribs[atti++] = EGL_DMA_BUF_PLANE ## plane_idx ## _PITCH_EXT; \ + attribs[atti++] = (int) buffer->strides[plane_idx]; \ + if (display->egl.has_dma_buf_import_modifiers) { \ + attribs[atti++] = EGL_DMA_BUF_PLANE ## plane_idx ## _MODIFIER_LO_EXT; \ + attribs[atti++] = buffer->modifier & 0xFFFFFFFF; \ + attribs[atti++] = EGL_DMA_BUF_PLANE ## plane_idx ## _MODIFIER_HI_EXT; \ + attribs[atti++] = buffer->modifier >> 32; \ + } \ + } + + if (buffer->plane_count > 0) + ADD_PLANE_ATTRIBS(0); + + if (buffer->plane_count > 1) + ADD_PLANE_ATTRIBS(1); + + if (buffer->plane_count > 2) + ADD_PLANE_ATTRIBS(2); + + if (buffer->plane_count > 3) + ADD_PLANE_ATTRIBS(3); + +#undef ADD_PLANE_ATTRIBS + + attribs[atti] = EGL_NONE; + + assert(atti < ARRAY_LENGTH(attribs)); buffer->egl_image = display->egl.create_image(display->egl.display, EGL_NO_CONTEXT, @@ -238,42 +289,79 @@ create_fbo_for_buffer(struct display *display, struct buffer *buffer) static int create_dmabuf_buffer(struct display *display, struct buffer *buffer, - int width, int height, int format) + int width, int height) { - static const uint32_t flags = 0; - static const uint64_t modifier = DRM_FORMAT_MOD_INVALID; + /* Y-Invert the buffer image, since we are going to renderer to the + * buffer through a FBO. */ + static const uint32_t flags = ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT; struct zwp_linux_buffer_params_v1 *params; + int i; buffer->display = display; buffer->width = width; buffer->height = height; - buffer->format = format; + buffer->format = BUFFER_FORMAT; + +#ifdef HAVE_GBM_MODIFIERS + if (display->modifiers_count > 0) { + buffer->bo = gbm_bo_create_with_modifiers(display->gbm.device, + buffer->width, + buffer->height, + buffer->format, + display->modifiers, + display->modifiers_count); + if (buffer->bo) + buffer->modifier = gbm_bo_get_modifier(buffer->bo); + } +#endif + + if (!buffer->bo) { + buffer->bo = gbm_bo_create(display->gbm.device, + buffer->width, + buffer->height, + buffer->format, + GBM_BO_USE_RENDERING); + buffer->modifier = DRM_FORMAT_MOD_INVALID; + } - buffer->bo = gbm_bo_create(display->gbm.device, - buffer->width, buffer->height, - format, - GBM_BO_USE_RENDERING); if (!buffer->bo) { fprintf(stderr, "create_bo failed\n"); goto error; } - buffer->stride = gbm_bo_get_stride(buffer->bo); - buffer->dmabuf_fd = gbm_bo_get_fd(buffer->bo); - - if (buffer->dmabuf_fd < 0) { - fprintf(stderr, "error: dmabuf_fd < 0\n"); +#ifdef HAVE_GBM_MODIFIERS + buffer->plane_count = gbm_bo_get_plane_count(buffer->bo); + for (i = 0; i < buffer->plane_count; ++i) { + uint32_t handle = gbm_bo_get_handle_for_plane(buffer->bo, i).u32; + int ret = drmPrimeHandleToFD(display->gbm.drm_fd, handle, 0, + &buffer->dmabuf_fds[i]); + if (ret < 0 || buffer->dmabuf_fds[i] < 0) { + fprintf(stderr, "error: failed to get dmabuf_fd\n"); + goto error; + } + buffer->strides[i] = gbm_bo_get_stride_for_plane(buffer->bo, i); + buffer->offsets[i] = gbm_bo_get_offset(buffer->bo, i); + } +#else + buffer->plane_count = 1; + buffer->strides[0] = gbm_bo_get_stride(buffer->bo); + buffer->dmabuf_fds[0] = gbm_bo_get_fd(buffer->bo); + if (buffer->dmabuf_fds[0] < 0) { + fprintf(stderr, "error: failed to get dmabuf_fd\n"); goto error; } +#endif params = zwp_linux_dmabuf_v1_create_params(display->dmabuf); - zwp_linux_buffer_params_v1_add(params, - buffer->dmabuf_fd, - 0, /* plane_idx */ - 0, /* offset */ - buffer->stride, - modifier >> 32, - modifier & 0xffffffff); + for (i = 0; i < buffer->plane_count; ++i) { + zwp_linux_buffer_params_v1_add(params, + buffer->dmabuf_fds[i], + i, + buffer->offsets[i], + buffer->strides[i], + buffer->modifier >> 32, + buffer->modifier & 0xffffffff); + } zwp_linux_buffer_params_v1_add_listener(params, ¶ms_listener, buffer); if (display->req_dmabuf_immediate) { @@ -281,7 +369,7 @@ create_dmabuf_buffer(struct display *display, struct buffer *buffer, zwp_linux_buffer_params_v1_create_immed(params, buffer->width, buffer->height, - format, + buffer->format, flags); wl_buffer_add_listener(buffer->buffer, &buffer_listener, buffer); } @@ -289,7 +377,7 @@ create_dmabuf_buffer(struct display *display, struct buffer *buffer, zwp_linux_buffer_params_v1_create(params, buffer->width, buffer->height, - format, + buffer->format, flags); } @@ -386,9 +474,16 @@ create_window(struct display *display, int width, int height) assert(0); } + for (i = 0; i < NUM_BUFFERS; ++i) { + int j; + for (j = 0; j < MAX_BUFFER_PLANES; ++j) + window->buffers[i].dmabuf_fds[j] = -1; + + } + for (i = 0; i < NUM_BUFFERS; ++i) { ret = create_dmabuf_buffer(display, &window->buffers[i], - width, height, DRM_FORMAT_XRGB8888); + width, height); if (ret < 0) return NULL; @@ -488,8 +583,12 @@ dmabuf_modifiers(void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf, struct display *d = data; switch (format) { - case DRM_FORMAT_XRGB8888: - d->xrgb8888_format_found = 1; + case BUFFER_FORMAT: + ++d->modifiers_count; + d->modifiers = realloc(d->modifiers, + d->modifiers_count * sizeof(*d->modifiers)); + d->modifiers[d->modifiers_count - 1] = + ((uint64_t)modifier_hi << 32) | modifier_lo; break; default: break; @@ -569,6 +668,8 @@ destroy_display(struct display *display) if (display->egl.display != EGL_NO_DISPLAY) eglTerminate(display->egl.display); + free(display->modifiers); + if (display->dmabuf) zwp_linux_dmabuf_v1_destroy(display->dmabuf); @@ -663,6 +764,14 @@ display_set_up_egl(struct display *display) goto error; } + if (weston_check_egl_extension(egl_extensions, + "EGL_EXT_image_dma_buf_import_modifiers")) { + display->egl.has_dma_buf_import_modifiers = true; + display->egl.query_dma_buf_modifiers = + (void *) eglGetProcAddress("eglQueryDmaBufModifiersEXT"); + assert(display->egl.query_dma_buf_modifiers); + } + display->egl.create_image = (void *) eglGetProcAddress("eglCreateImageKHR"); assert(display->egl.create_image); @@ -681,6 +790,76 @@ error: return false; } +static bool +display_update_supported_modifiers_for_egl(struct display *d) +{ + uint64_t *egl_modifiers = NULL; + int num_egl_modifiers = 0; + EGLBoolean ret; + int i; + + /* If EGL doesn't support modifiers, don't use them at all. */ + if (!d->egl.has_dma_buf_import_modifiers) { + d->modifiers_count = 0; + free(d->modifiers); + d->modifiers = NULL; + return true; + } + + ret = d->egl.query_dma_buf_modifiers(d->egl.display, + BUFFER_FORMAT, + 0, /* max_modifiers */ + NULL, /* modifiers */ + NULL, /* external_only */ + &num_egl_modifiers); + if (ret == EGL_FALSE || num_egl_modifiers == 0) { + fprintf(stderr, "Failed to query num EGL modifiers for format\n"); + goto error; + } + + egl_modifiers = zalloc(num_egl_modifiers * sizeof(*egl_modifiers)); + + ret = d->egl.query_dma_buf_modifiers(d->egl.display, + BUFFER_FORMAT, + num_egl_modifiers, + egl_modifiers, + NULL, /* external_only */ + &num_egl_modifiers); + if (ret == EGL_FALSE) { + fprintf(stderr, "Failed to query EGL modifiers for format\n"); + goto error; + } + + /* Poor person's set intersection: d->modifiers INTERSECT + * egl_modifiers. If a modifier is not supported, replace it with + * DRM_FORMAT_MOD_INVALID in the d->modifiers array. + */ + for (i = 0; i < d->modifiers_count; ++i) { + uint64_t mod = d->modifiers[i]; + bool egl_supported = false; + int j; + + for (j = 0; j < num_egl_modifiers; ++j) { + if (egl_modifiers[j] == mod) { + egl_supported = true; + break; + } + } + + if (!egl_supported) + d->modifiers[i] = DRM_FORMAT_MOD_INVALID; + } + + free(egl_modifiers); + + return true; + +error: + free(egl_modifiers); + + return false; +} + static bool display_set_up_gbm(struct display *display, char const* drm_render_node) { @@ -729,7 +908,7 @@ create_display(char const *drm_render_node, int opts) wl_display_roundtrip(display->display); - if (!display->xrgb8888_format_found) { + if (!display->modifiers_count) { fprintf(stderr, "format XRGB8888 is not available\n"); goto error; } @@ -737,6 +916,9 @@ create_display(char const *drm_render_node, int opts) if (!display_set_up_egl(display)) goto error; + if (!display_update_supported_modifiers_for_egl(display)) + goto error; + if (!display_set_up_gbm(display, drm_render_node)) goto error;