[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