From f522e2261131eb9ed9f83157420f114540cfcbf1 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Fri, 18 Nov 2016 12:31:26 +0000 Subject: [PATCH] compositor-drm: Add modifiers to GBM dmabuf import Add support for the GBM_BO_IMPORT_FD_MODIFIER path, which allows us to import multi-plane dmabufs, as well as format modifiers. Signed-off-by: Daniel Stone Reviewed-by: Pekka Paalanen Tested-by: Emre Ucan Fixes: https://gitlab.freedesktop.org/wayland/weston/issues/113 --- configure.ac | 6 +- libweston/compositor-drm.c | 211 +++++++++++++++++++++++++++++++++++---------- 2 files changed, 170 insertions(+), 47 deletions(-) diff --git a/configure.ac b/configure.ac index adb998d..ef55ace 100644 --- a/configure.ac +++ b/configure.ac @@ -212,9 +212,9 @@ if test x$enable_drm_compositor = xyes; then PKG_CHECK_MODULES(DRM_COMPOSITOR_ATOMIC, [libdrm >= 2.4.78], [AC_DEFINE([HAVE_DRM_ATOMIC], 1, [libdrm supports atomic API])], [AC_MSG_WARN([libdrm does not support atomic modesetting, will omit that capability])]) - PKG_CHECK_MODULES(DRM_COMPOSITOR_GBM, [gbm >= 10.2], - [AC_DEFINE([HAVE_GBM_FD_IMPORT], 1, [gbm supports dmabuf import])], - [AC_MSG_WARN([gbm does not support dmabuf import, will omit that capability])]) + PKG_CHECK_MODULES(DRM_COMPOSITOR_GBM, [gbm >= 17.2], + [AC_DEFINE([HAVE_GBM_FD_IMPORT], 1, [gbm supports import with modifiers])], + [AC_MSG_WARN([GBM does not support dmabuf import, will omit that capability])]) fi diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c index 29fa98d..87e3e5d 100644 --- a/libweston/compositor-drm.c +++ b/libweston/compositor-drm.c @@ -326,6 +326,7 @@ struct drm_mode { enum drm_fb_type { BUFFER_INVALID = 0, /**< never used */ BUFFER_CLIENT, /**< directly sourced from client */ + BUFFER_DMABUF, /**< imported from linux_dmabuf client */ BUFFER_PIXMAN_DUMB, /**< internal Pixman rendering */ BUFFER_GBM_SURFACE, /**< internal EGL rendering */ BUFFER_CURSOR, /**< internal cursor buffer */ @@ -1038,6 +1039,155 @@ drm_fb_ref(struct drm_fb *fb) return fb; } +static void +drm_fb_destroy_dmabuf(struct drm_fb *fb) +{ + /* We deliberately do not close the GEM handles here; GBM manages + * their lifetime through the BO. */ + if (fb->bo) + gbm_bo_destroy(fb->bo); + drm_fb_destroy(fb); +} + +static struct drm_fb * +drm_fb_get_from_dmabuf(struct linux_dmabuf_buffer *dmabuf, + struct drm_backend *backend, bool is_opaque) +{ +#ifdef HAVE_GBM_FD_IMPORT + struct drm_fb *fb; + struct gbm_import_fd_data import_legacy = { + .width = dmabuf->attributes.width, + .height = dmabuf->attributes.height, + .format = dmabuf->attributes.format, + .stride = dmabuf->attributes.stride[0], + .fd = dmabuf->attributes.fd[0], + }; + struct gbm_import_fd_modifier_data import_mod = { + .width = dmabuf->attributes.width, + .height = dmabuf->attributes.height, + .format = dmabuf->attributes.format, + .num_fds = dmabuf->attributes.n_planes, + .modifier = dmabuf->attributes.modifier[0], + }; + int i; + + /* XXX: TODO: + * + * Currently the buffer is rejected if any dmabuf attribute + * flag is set. This keeps us from passing an inverted / + * interlaced / bottom-first buffer (or any other type that may + * be added in the future) through to an overlay. Ultimately, + * these types of buffers should be handled through buffer + * transforms and not as spot-checks requiring specific + * knowledge. */ + if (dmabuf->attributes.flags) + return NULL; + + fb = zalloc(sizeof *fb); + if (fb == NULL) + return NULL; + + fb->refcnt = 1; + fb->type = BUFFER_DMABUF; + + static_assert(ARRAY_LENGTH(import_mod.fds) == + ARRAY_LENGTH(dmabuf->attributes.fd), + "GBM and linux_dmabuf FD size must match"); + static_assert(sizeof(import_mod.fds) == sizeof(dmabuf->attributes.fd), + "GBM and linux_dmabuf FD size must match"); + memcpy(import_mod.fds, dmabuf->attributes.fd, sizeof(import_mod.fds)); + + static_assert(ARRAY_LENGTH(import_mod.strides) == + ARRAY_LENGTH(dmabuf->attributes.stride), + "GBM and linux_dmabuf stride size must match"); + static_assert(sizeof(import_mod.strides) == + sizeof(dmabuf->attributes.stride), + "GBM and linux_dmabuf stride size must match"); + memcpy(import_mod.strides, dmabuf->attributes.stride, + sizeof(import_mod.strides)); + + static_assert(ARRAY_LENGTH(import_mod.offsets) == + ARRAY_LENGTH(dmabuf->attributes.offset), + "GBM and linux_dmabuf offset size must match"); + static_assert(sizeof(import_mod.offsets) == + sizeof(dmabuf->attributes.offset), + "GBM and linux_dmabuf offset size must match"); + memcpy(import_mod.offsets, dmabuf->attributes.offset, + sizeof(import_mod.offsets)); + + /* The legacy FD-import path does not allow us to supply modifiers, + * multiple planes, or buffer offsets. */ + if (dmabuf->attributes.modifier[0] != DRM_FORMAT_MOD_INVALID || + import_mod.num_fds > 1 || + import_mod.offsets[0] > 0) { + fb->bo = gbm_bo_import(backend->gbm, GBM_BO_IMPORT_FD_MODIFIER, + &import_mod, + GBM_BO_USE_SCANOUT); + } else { + fb->bo = gbm_bo_import(backend->gbm, GBM_BO_IMPORT_FD, + &import_legacy, + GBM_BO_USE_SCANOUT); + } + + if (!fb->bo) + goto err_free; + + fb->width = dmabuf->attributes.width; + fb->height = dmabuf->attributes.height; + fb->modifier = dmabuf->attributes.modifier[0]; + fb->size = 0; + fb->fd = backend->drm.fd; + + static_assert(ARRAY_LENGTH(fb->strides) == + ARRAY_LENGTH(dmabuf->attributes.stride), + "drm_fb and dmabuf stride size must match"); + static_assert(sizeof(fb->strides) == sizeof(dmabuf->attributes.stride), + "drm_fb and dmabuf stride size must match"); + memcpy(fb->strides, dmabuf->attributes.stride, sizeof(fb->strides)); + static_assert(ARRAY_LENGTH(fb->offsets) == + ARRAY_LENGTH(dmabuf->attributes.offset), + "drm_fb and dmabuf offset size must match"); + static_assert(sizeof(fb->offsets) == sizeof(dmabuf->attributes.offset), + "drm_fb and dmabuf offset size must match"); + memcpy(fb->offsets, dmabuf->attributes.offset, sizeof(fb->offsets)); + + fb->format = pixel_format_get_info(dmabuf->attributes.format); + if (!fb->format) { + weston_log("couldn't look up format info for 0x%lx\n", + (unsigned long) dmabuf->attributes.format); + goto err_free; + } + + if (is_opaque) + fb->format = pixel_format_get_opaque_substitute(fb->format); + + if (backend->min_width > fb->width || + fb->width > backend->max_width || + backend->min_height > fb->height || + fb->height > backend->max_height) { + weston_log("bo geometry out of bounds\n"); + goto err_free; + } + + for (i = 0; i < dmabuf->attributes.n_planes; i++) { + fb->handles[i] = gbm_bo_get_handle_for_plane(fb->bo, i).u32; + if (!fb->handles[i]) + goto err_free; + } + + if (drm_fb_addfb(fb) != 0) { + weston_log("failed to create kms fb: %m\n"); + goto err_free; + } + + return fb; + +err_free: + drm_fb_destroy_dmabuf(fb); +#endif + return NULL; +} + static struct drm_fb * drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend, bool is_opaque, enum drm_fb_type type) @@ -1103,7 +1253,7 @@ static void drm_fb_set_buffer(struct drm_fb *fb, struct weston_buffer *buffer) { assert(fb->buffer_ref.buffer == NULL); - assert(fb->type == BUFFER_CLIENT); + assert(fb->type == BUFFER_CLIENT || fb->type == BUFFER_DMABUF); weston_buffer_reference(&fb->buffer_ref, buffer); } @@ -1128,6 +1278,9 @@ drm_fb_unref(struct drm_fb *fb) case BUFFER_GBM_SURFACE: gbm_surface_release_buffer(fb->gbm_surface, fb->bo); break; + case BUFFER_DMABUF: + drm_fb_destroy_dmabuf(fb); + break; default: assert(NULL); break; @@ -1384,9 +1537,9 @@ drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev) struct drm_output *output = state->output; struct drm_backend *b = to_drm_backend(output->base.compositor); struct weston_buffer *buffer = ev->surface->buffer_ref.buffer; + bool is_opaque = drm_view_is_opaque(ev); struct linux_dmabuf_buffer *dmabuf; struct drm_fb *fb; - struct gbm_bo *bo; /* Don't import buffers which span multiple outputs. */ if (ev->output_mask != (1u << output->base.id)) @@ -1404,58 +1557,28 @@ drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev) if (wl_shm_buffer_get(buffer->resource)) return NULL; + /* GBM is used for dmabuf import as well as from client wl_buffer. */ if (!b->gbm) return NULL; dmabuf = linux_dmabuf_buffer_get(buffer->resource); if (dmabuf) { -#ifdef HAVE_GBM_FD_IMPORT - /* XXX: TODO: - * - * Use AddFB2 directly, do not go via GBM. - * Add support for multiplanar formats. - * Both require refactoring in the DRM-backend to - * support a mix of gbm_bos and drmfbs. - */ - struct gbm_import_fd_data gbm_dmabuf = { - .fd = dmabuf->attributes.fd[0], - .width = dmabuf->attributes.width, - .height = dmabuf->attributes.height, - .stride = dmabuf->attributes.stride[0], - .format = dmabuf->attributes.format - }; - - /* XXX: TODO: - * - * Currently the buffer is rejected if any dmabuf attribute - * flag is set. This keeps us from passing an inverted / - * interlaced / bottom-first buffer (or any other type that may - * be added in the future) through to an overlay. Ultimately, - * these types of buffers should be handled through buffer - * transforms and not as spot-checks requiring specific - * knowledge. */ - if (dmabuf->attributes.n_planes != 1 || - dmabuf->attributes.offset[0] != 0 || - dmabuf->attributes.flags) + fb = drm_fb_get_from_dmabuf(dmabuf, b, is_opaque); + if (!fb) return NULL; - - bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_FD, &gbm_dmabuf, - GBM_BO_USE_SCANOUT); -#else - return NULL; -#endif } else { + struct gbm_bo *bo; + bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_WL_BUFFER, buffer->resource, GBM_BO_USE_SCANOUT); - } - - if (!bo) - return NULL; + if (!bo) + return NULL; - fb = drm_fb_get_from_bo(bo, b, drm_view_is_opaque(ev), BUFFER_CLIENT); - if (!fb) { - gbm_bo_destroy(bo); - return NULL; + fb = drm_fb_get_from_bo(bo, b, is_opaque, BUFFER_CLIENT); + if (!fb) { + gbm_bo_destroy(bo); + return NULL; + } } drm_fb_set_buffer(fb, buffer); -- 2.7.4