[PATCH v17 04/14] compositor-drm: Add modifiers to GBM dmabuf import
Daniel Stone
daniels at collabora.com
Mon Jul 9 13:23:10 UTC 2018
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>
Tested-by: Emre Ucan <eucan at de.adit-jv.com>
---
configure.ac | 6 +-
libweston/compositor-drm.c | 201 +++++++++++++++++++++++++++++--------
2 files changed, 160 insertions(+), 47 deletions(-)
diff --git a/configure.ac b/configure.ac
index adb998dda..ef55ace36 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 29fa98da6..ae4a4a542 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,145 @@ 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;
+
+ BUILD_BUG_ON(ARRAY_LENGTH(import_mod.fds) !=
+ ARRAY_LENGTH(dmabuf->attributes.fd));
+ BUILD_BUG_ON(sizeof(import_mod.fds) != sizeof(dmabuf->attributes.fd));
+ memcpy(import_mod.fds, dmabuf->attributes.fd, sizeof(import_mod.fds));
+
+ BUILD_BUG_ON(ARRAY_LENGTH(import_mod.strides) !=
+ ARRAY_LENGTH(dmabuf->attributes.stride));
+ BUILD_BUG_ON(sizeof(import_mod.strides) !=
+ sizeof(dmabuf->attributes.stride));
+ memcpy(import_mod.strides, dmabuf->attributes.stride,
+ sizeof(import_mod.strides));
+
+ BUILD_BUG_ON(ARRAY_LENGTH(import_mod.offsets) !=
+ ARRAY_LENGTH(dmabuf->attributes.offset));
+ BUILD_BUG_ON(sizeof(import_mod.offsets) !=
+ sizeof(dmabuf->attributes.offset));
+ 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;
+
+ BUILD_BUG_ON(ARRAY_LENGTH(fb->strides) !=
+ ARRAY_LENGTH(dmabuf->attributes.stride));
+ BUILD_BUG_ON(sizeof(fb->strides) != sizeof(dmabuf->attributes.stride));
+ memcpy(fb->strides, dmabuf->attributes.stride, sizeof(fb->strides));
+ BUILD_BUG_ON(ARRAY_LENGTH(fb->offsets) !=
+ ARRAY_LENGTH(dmabuf->attributes.offset));
+ BUILD_BUG_ON(sizeof(fb->offsets) != sizeof(dmabuf->attributes.offset));
+ 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 +1243,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 +1268,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 +1527,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 +1547,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.17.1
More information about the wayland-devel
mailing list