[PATCH weston v12 27/40] compositor-drm: Add modifiers to GBM dmabuf import
Matt Hoosier
matt.hoosier at gmail.com
Fri Nov 3 16:29:09 UTC 2017
On Tue, Sep 26, 2017 at 12:16 PM, Daniel Stone <daniels at collabora.com> wrote:
> 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 <daniels at collabora.com>
> ---
> configure.ac | 3 -
> libweston/compositor-drm.c | 181 +++++++++++++++++++++++++++++++++------------
> 2 files changed, 133 insertions(+), 51 deletions(-)
>
> diff --git a/configure.ac b/configure.ac
> index f09d0e04..3996c80c 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -203,9 +203,6 @@ 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])])
> fi
>
>
> diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
> index 7557ef55..65934a01 100644
> --- a/libweston/compositor-drm.c
> +++ b/libweston/compositor-drm.c
> @@ -273,6 +273,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 */
> @@ -928,6 +929,118 @@ 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. */
> + 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)
> +{
> + 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;
> +
> + memcpy(import_mod.fds, dmabuf->attributes.fd, sizeof(import_mod.fds));
> + memcpy(import_mod.strides, dmabuf->attributes.stride,
> + sizeof(import_mod.fds));
> + memcpy(import_mod.offsets, dmabuf->attributes.offset,
> + sizeof(import_mod.fds));
> +
> + if (dmabuf->attributes.modifier[0] != DRM_FORMAT_MOD_INVALID) {
> + 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;
> + memcpy(fb->strides, dmabuf->attributes.stride, sizeof(fb->strides));
> + memcpy(fb->offsets, dmabuf->attributes.offset, sizeof(fb->offsets));
> + fb->format = pixel_format_get_info(dmabuf->attributes.format);
> + //fb->modifier = dmabuf->attributes.modifier;
> + fb->size = 0;
> + fb->fd = backend->drm.fd;
> +
> + if (!fb->format) {
> + weston_log("couldn't look up format info for 0x%lx\n",
> + (unsigned long) fb->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);
> + 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)
> @@ -990,7 +1103,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);
> }
>
> @@ -1015,6 +1128,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;
> @@ -1217,9 +1333,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))
> @@ -1234,58 +1350,27 @@ drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev)
> if (wl_shm_buffer_get(buffer->resource))
> return NULL;
>
> - 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)
> - goto err;
> -
> - bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_FD, &gbm_dmabuf,
> - GBM_BO_USE_SCANOUT);
> -#else
> - return NULL;
> -#endif
> + fb = drm_fb_get_from_dmabuf(dmabuf, b, is_opaque);
> + if (!fb)
> + return NULL;
> } else {
> + struct gbm_bo *bo;
> +
> + if (!b->gbm)
> + return NULL;
> +
> bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_WL_BUFFER,
> buffer->resource, GBM_BO_USE_SCANOUT);
> - }
There seems to be a small hole in the logic here. Views whose buffers
are from wl_drm (Mesa GLES clients) are falling through all this logic
and failing to import. linux_dmabuf_buffer_get() succeeds on them,
which causes control to go down the successful case in "if (dmabuf) {
... }". But then drm_fb_get_from_dmabuf() itself fails.
It appears that the import would still actually work if control went
down the gbm_bo_import(GBM_BO_IMPORT_WL_BUFFER) path instead, since
the Wayland-EGL builtin for importing bo's from Wayland buffers would
succeed.
I did the testing on v13 of your patch series, which has a slightly
different implementation of than your v12 edition picture heref. But
the problem seems the same.
> -
> - 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.14.1
>
> _______________________________________________
> wayland-devel mailing list
> wayland-devel at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/wayland-devel
More information about the wayland-devel
mailing list