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 <daniels@collabora.com> Reviewed-by: Pekka Paalanen <pekka.paalanen@collabora.co.uk> Tested-by: Emre Ucan <eucan@de.adit-jv.com> Fixes: https://gitlab.freedesktop.org/wayland/weston/issues/113
This commit is contained in:
+3
-3
@@ -212,9 +212,9 @@ if test x$enable_drm_compositor = xyes; then
|
|||||||
PKG_CHECK_MODULES(DRM_COMPOSITOR_ATOMIC, [libdrm >= 2.4.78],
|
PKG_CHECK_MODULES(DRM_COMPOSITOR_ATOMIC, [libdrm >= 2.4.78],
|
||||||
[AC_DEFINE([HAVE_DRM_ATOMIC], 1, [libdrm supports atomic API])],
|
[AC_DEFINE([HAVE_DRM_ATOMIC], 1, [libdrm supports atomic API])],
|
||||||
[AC_MSG_WARN([libdrm does not support atomic modesetting, will omit that capability])])
|
[AC_MSG_WARN([libdrm does not support atomic modesetting, will omit that capability])])
|
||||||
PKG_CHECK_MODULES(DRM_COMPOSITOR_GBM, [gbm >= 10.2],
|
PKG_CHECK_MODULES(DRM_COMPOSITOR_GBM, [gbm >= 17.2],
|
||||||
[AC_DEFINE([HAVE_GBM_FD_IMPORT], 1, [gbm supports dmabuf import])],
|
[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])])
|
[AC_MSG_WARN([GBM does not support dmabuf import, will omit that capability])])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+162
-39
@@ -326,6 +326,7 @@ struct drm_mode {
|
|||||||
enum drm_fb_type {
|
enum drm_fb_type {
|
||||||
BUFFER_INVALID = 0, /**< never used */
|
BUFFER_INVALID = 0, /**< never used */
|
||||||
BUFFER_CLIENT, /**< directly sourced from client */
|
BUFFER_CLIENT, /**< directly sourced from client */
|
||||||
|
BUFFER_DMABUF, /**< imported from linux_dmabuf client */
|
||||||
BUFFER_PIXMAN_DUMB, /**< internal Pixman rendering */
|
BUFFER_PIXMAN_DUMB, /**< internal Pixman rendering */
|
||||||
BUFFER_GBM_SURFACE, /**< internal EGL rendering */
|
BUFFER_GBM_SURFACE, /**< internal EGL rendering */
|
||||||
BUFFER_CURSOR, /**< internal cursor buffer */
|
BUFFER_CURSOR, /**< internal cursor buffer */
|
||||||
@@ -1038,6 +1039,155 @@ drm_fb_ref(struct drm_fb *fb)
|
|||||||
return 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 *
|
static struct drm_fb *
|
||||||
drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend,
|
drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend,
|
||||||
bool is_opaque, enum drm_fb_type type)
|
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)
|
drm_fb_set_buffer(struct drm_fb *fb, struct weston_buffer *buffer)
|
||||||
{
|
{
|
||||||
assert(fb->buffer_ref.buffer == NULL);
|
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);
|
weston_buffer_reference(&fb->buffer_ref, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1128,6 +1278,9 @@ drm_fb_unref(struct drm_fb *fb)
|
|||||||
case BUFFER_GBM_SURFACE:
|
case BUFFER_GBM_SURFACE:
|
||||||
gbm_surface_release_buffer(fb->gbm_surface, fb->bo);
|
gbm_surface_release_buffer(fb->gbm_surface, fb->bo);
|
||||||
break;
|
break;
|
||||||
|
case BUFFER_DMABUF:
|
||||||
|
drm_fb_destroy_dmabuf(fb);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
assert(NULL);
|
assert(NULL);
|
||||||
break;
|
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_output *output = state->output;
|
||||||
struct drm_backend *b = to_drm_backend(output->base.compositor);
|
struct drm_backend *b = to_drm_backend(output->base.compositor);
|
||||||
struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
|
struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
|
||||||
|
bool is_opaque = drm_view_is_opaque(ev);
|
||||||
struct linux_dmabuf_buffer *dmabuf;
|
struct linux_dmabuf_buffer *dmabuf;
|
||||||
struct drm_fb *fb;
|
struct drm_fb *fb;
|
||||||
struct gbm_bo *bo;
|
|
||||||
|
|
||||||
/* Don't import buffers which span multiple outputs. */
|
/* Don't import buffers which span multiple outputs. */
|
||||||
if (ev->output_mask != (1u << output->base.id))
|
if (ev->output_mask != (1u << output->base.id))
|
||||||
@@ -1404,59 +1557,29 @@ drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev)
|
|||||||
if (wl_shm_buffer_get(buffer->resource))
|
if (wl_shm_buffer_get(buffer->resource))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
/* GBM is used for dmabuf import as well as from client wl_buffer. */
|
||||||
if (!b->gbm)
|
if (!b->gbm)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
dmabuf = linux_dmabuf_buffer_get(buffer->resource);
|
dmabuf = linux_dmabuf_buffer_get(buffer->resource);
|
||||||
if (dmabuf) {
|
if (dmabuf) {
|
||||||
#ifdef HAVE_GBM_FD_IMPORT
|
fb = drm_fb_get_from_dmabuf(dmabuf, b, is_opaque);
|
||||||
/* XXX: TODO:
|
if (!fb)
|
||||||
*
|
|
||||||
* 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)
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_FD, &gbm_dmabuf,
|
|
||||||
GBM_BO_USE_SCANOUT);
|
|
||||||
#else
|
|
||||||
return NULL;
|
|
||||||
#endif
|
|
||||||
} else {
|
} else {
|
||||||
|
struct gbm_bo *bo;
|
||||||
|
|
||||||
bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_WL_BUFFER,
|
bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_WL_BUFFER,
|
||||||
buffer->resource, GBM_BO_USE_SCANOUT);
|
buffer->resource, GBM_BO_USE_SCANOUT);
|
||||||
}
|
|
||||||
|
|
||||||
if (!bo)
|
if (!bo)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
fb = drm_fb_get_from_bo(bo, b, drm_view_is_opaque(ev), BUFFER_CLIENT);
|
fb = drm_fb_get_from_bo(bo, b, is_opaque, BUFFER_CLIENT);
|
||||||
if (!fb) {
|
if (!fb) {
|
||||||
gbm_bo_destroy(bo);
|
gbm_bo_destroy(bo);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
drm_fb_set_buffer(fb, buffer);
|
drm_fb_set_buffer(fb, buffer);
|
||||||
return fb;
|
return fb;
|
||||||
|
|||||||
Reference in New Issue
Block a user