clients/simple-dmabuf-egl: Support dmabuf format modifiers

Take into account format modifiers advertised by the compositor and the
EGL implementation and supported by the buffer creation mechanism, to
select the optimal buffer modifier.

Signed-off-by: Alexandros Frantzis <alexandros.frantzis@collabora.com>
This commit is contained in:
Alexandros Frantzis 2019-01-08 12:32:12 +02:00
parent 29a6803817
commit e9700f62af
2 changed files with 224 additions and 42 deletions

View File

@ -61,7 +61,7 @@ simple_clients = [
], ],
'dep_objs': [ 'dep_objs': [
dep_wayland_client, dep_wayland_client,
dep_libdrm_headers dep_libdrm
], ],
'deps': [ 'egl', 'glesv2', 'gbm' ] 'deps': [ 'egl', 'glesv2', 'gbm' ]
}, },

View File

@ -39,9 +39,11 @@
#include <unistd.h> #include <unistd.h>
#include <drm_fourcc.h> #include <drm_fourcc.h>
#include <xf86drm.h>
#include <gbm.h> #include <gbm.h>
#include <wayland-client.h> #include <wayland-client.h>
#include "shared/helpers.h"
#include "shared/platform.h" #include "shared/platform.h"
#include "shared/zalloc.h" #include "shared/zalloc.h"
#include "xdg-shell-unstable-v6-client-protocol.h" #include "xdg-shell-unstable-v6-client-protocol.h"
@ -62,6 +64,9 @@
/* Possible options that affect the displayed image */ /* Possible options that affect the displayed image */
#define OPT_IMMEDIATE 1 /* create wl_buffer immediately */ #define OPT_IMMEDIATE 1 /* create wl_buffer immediately */
#define BUFFER_FORMAT DRM_FORMAT_XRGB8888
#define MAX_BUFFER_PLANES 4
struct display { struct display {
struct wl_display *display; struct wl_display *display;
struct wl_registry *registry; struct wl_registry *registry;
@ -69,11 +74,14 @@ struct display {
struct zxdg_shell_v6 *shell; struct zxdg_shell_v6 *shell;
struct zwp_fullscreen_shell_v1 *fshell; struct zwp_fullscreen_shell_v1 *fshell;
struct zwp_linux_dmabuf_v1 *dmabuf; struct zwp_linux_dmabuf_v1 *dmabuf;
int xrgb8888_format_found; uint64_t *modifiers;
int modifiers_count;
int req_dmabuf_immediate; int req_dmabuf_immediate;
struct { struct {
EGLDisplay display; EGLDisplay display;
EGLContext context; EGLContext context;
bool has_dma_buf_import_modifiers;
PFNEGLQUERYDMABUFMODIFIERSEXTPROC query_dma_buf_modifiers;
PFNEGLCREATEIMAGEKHRPROC create_image; PFNEGLCREATEIMAGEKHRPROC create_image;
PFNEGLDESTROYIMAGEKHRPROC destroy_image; PFNEGLDESTROYIMAGEKHRPROC destroy_image;
PFNGLEGLIMAGETARGETTEXTURE2DOESPROC image_target_texture_2d; PFNGLEGLIMAGETARGETTEXTURE2DOESPROC image_target_texture_2d;
@ -91,12 +99,14 @@ struct buffer {
struct gbm_bo *bo; struct gbm_bo *bo;
int dmabuf_fd;
int width; int width;
int height; int height;
uint32_t stride;
int format; int format;
uint64_t modifier;
int plane_count;
int dmabuf_fds[MAX_BUFFER_PLANES];
uint32_t strides[MAX_BUFFER_PLANES];
uint32_t offsets[MAX_BUFFER_PLANES];
EGLImageKHR egl_image; EGLImageKHR egl_image;
GLuint gl_texture; GLuint gl_texture;
@ -137,6 +147,8 @@ static const struct wl_buffer_listener buffer_listener = {
static void static void
buffer_free(struct buffer *buf) buffer_free(struct buffer *buf)
{ {
int i;
if (buf->gl_fbo) if (buf->gl_fbo)
glDeleteFramebuffers(1, &buf->gl_fbo); glDeleteFramebuffers(1, &buf->gl_fbo);
@ -154,8 +166,10 @@ buffer_free(struct buffer *buf)
if (buf->bo) if (buf->bo)
gbm_bo_destroy(buf->bo); gbm_bo_destroy(buf->bo);
if (buf->dmabuf_fd >= 0) for (i = 0; i < buf->plane_count; ++i) {
close(buf->dmabuf_fd); if (buf->dmabuf_fds[i] >= 0)
close(buf->dmabuf_fds[i]);
}
} }
static void static void
@ -192,15 +206,52 @@ static const struct zwp_linux_buffer_params_v1_listener params_listener = {
static bool static bool
create_fbo_for_buffer(struct display *display, struct buffer *buffer) create_fbo_for_buffer(struct display *display, struct buffer *buffer)
{ {
EGLint attribs[] = { static const int general_attribs = 3;
EGL_WIDTH, buffer->width, static const int plane_attribs = 5;
EGL_HEIGHT, buffer->height, static const int entries_per_attrib = 2;
EGL_LINUX_DRM_FOURCC_EXT, buffer->format, EGLint attribs[(general_attribs + plane_attribs * MAX_BUFFER_PLANES) *
EGL_DMA_BUF_PLANE0_FD_EXT, buffer->dmabuf_fd, entries_per_attrib + 1];
EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0, unsigned int atti = 0;
EGL_DMA_BUF_PLANE0_PITCH_EXT, (int) buffer->stride,
EGL_NONE attribs[atti++] = EGL_WIDTH;
}; attribs[atti++] = buffer->width;
attribs[atti++] = EGL_HEIGHT;
attribs[atti++] = buffer->height;
attribs[atti++] = EGL_LINUX_DRM_FOURCC_EXT;
attribs[atti++] = buffer->format;
#define ADD_PLANE_ATTRIBS(plane_idx) { \
attribs[atti++] = EGL_DMA_BUF_PLANE ## plane_idx ## _FD_EXT; \
attribs[atti++] = buffer->dmabuf_fds[plane_idx]; \
attribs[atti++] = EGL_DMA_BUF_PLANE ## plane_idx ## _OFFSET_EXT; \
attribs[atti++] = (int) buffer->offsets[plane_idx]; \
attribs[atti++] = EGL_DMA_BUF_PLANE ## plane_idx ## _PITCH_EXT; \
attribs[atti++] = (int) buffer->strides[plane_idx]; \
if (display->egl.has_dma_buf_import_modifiers) { \
attribs[atti++] = EGL_DMA_BUF_PLANE ## plane_idx ## _MODIFIER_LO_EXT; \
attribs[atti++] = buffer->modifier & 0xFFFFFFFF; \
attribs[atti++] = EGL_DMA_BUF_PLANE ## plane_idx ## _MODIFIER_HI_EXT; \
attribs[atti++] = buffer->modifier >> 32; \
} \
}
if (buffer->plane_count > 0)
ADD_PLANE_ATTRIBS(0);
if (buffer->plane_count > 1)
ADD_PLANE_ATTRIBS(1);
if (buffer->plane_count > 2)
ADD_PLANE_ATTRIBS(2);
if (buffer->plane_count > 3)
ADD_PLANE_ATTRIBS(3);
#undef ADD_PLANE_ATTRIBS
attribs[atti] = EGL_NONE;
assert(atti < ARRAY_LENGTH(attribs));
buffer->egl_image = display->egl.create_image(display->egl.display, buffer->egl_image = display->egl.create_image(display->egl.display,
EGL_NO_CONTEXT, EGL_NO_CONTEXT,
@ -238,42 +289,79 @@ create_fbo_for_buffer(struct display *display, struct buffer *buffer)
static int static int
create_dmabuf_buffer(struct display *display, struct buffer *buffer, create_dmabuf_buffer(struct display *display, struct buffer *buffer,
int width, int height, int format) int width, int height)
{ {
static const uint32_t flags = 0; /* Y-Invert the buffer image, since we are going to renderer to the
static const uint64_t modifier = DRM_FORMAT_MOD_INVALID; * buffer through a FBO. */
static const uint32_t flags = ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT;
struct zwp_linux_buffer_params_v1 *params; struct zwp_linux_buffer_params_v1 *params;
int i;
buffer->display = display; buffer->display = display;
buffer->width = width; buffer->width = width;
buffer->height = height; buffer->height = height;
buffer->format = format; buffer->format = BUFFER_FORMAT;
#ifdef HAVE_GBM_MODIFIERS
if (display->modifiers_count > 0) {
buffer->bo = gbm_bo_create_with_modifiers(display->gbm.device,
buffer->width,
buffer->height,
buffer->format,
display->modifiers,
display->modifiers_count);
if (buffer->bo)
buffer->modifier = gbm_bo_get_modifier(buffer->bo);
}
#endif
if (!buffer->bo) {
buffer->bo = gbm_bo_create(display->gbm.device,
buffer->width,
buffer->height,
buffer->format,
GBM_BO_USE_RENDERING);
buffer->modifier = DRM_FORMAT_MOD_INVALID;
}
buffer->bo = gbm_bo_create(display->gbm.device,
buffer->width, buffer->height,
format,
GBM_BO_USE_RENDERING);
if (!buffer->bo) { if (!buffer->bo) {
fprintf(stderr, "create_bo failed\n"); fprintf(stderr, "create_bo failed\n");
goto error; goto error;
} }
buffer->stride = gbm_bo_get_stride(buffer->bo); #ifdef HAVE_GBM_MODIFIERS
buffer->dmabuf_fd = gbm_bo_get_fd(buffer->bo); buffer->plane_count = gbm_bo_get_plane_count(buffer->bo);
for (i = 0; i < buffer->plane_count; ++i) {
if (buffer->dmabuf_fd < 0) { uint32_t handle = gbm_bo_get_handle_for_plane(buffer->bo, i).u32;
fprintf(stderr, "error: dmabuf_fd < 0\n"); int ret = drmPrimeHandleToFD(display->gbm.drm_fd, handle, 0,
&buffer->dmabuf_fds[i]);
if (ret < 0 || buffer->dmabuf_fds[i] < 0) {
fprintf(stderr, "error: failed to get dmabuf_fd\n");
goto error;
}
buffer->strides[i] = gbm_bo_get_stride_for_plane(buffer->bo, i);
buffer->offsets[i] = gbm_bo_get_offset(buffer->bo, i);
}
#else
buffer->plane_count = 1;
buffer->strides[0] = gbm_bo_get_stride(buffer->bo);
buffer->dmabuf_fds[0] = gbm_bo_get_fd(buffer->bo);
if (buffer->dmabuf_fds[0] < 0) {
fprintf(stderr, "error: failed to get dmabuf_fd\n");
goto error; goto error;
} }
#endif
params = zwp_linux_dmabuf_v1_create_params(display->dmabuf); params = zwp_linux_dmabuf_v1_create_params(display->dmabuf);
zwp_linux_buffer_params_v1_add(params, for (i = 0; i < buffer->plane_count; ++i) {
buffer->dmabuf_fd, zwp_linux_buffer_params_v1_add(params,
0, /* plane_idx */ buffer->dmabuf_fds[i],
0, /* offset */ i,
buffer->stride, buffer->offsets[i],
modifier >> 32, buffer->strides[i],
modifier & 0xffffffff); buffer->modifier >> 32,
buffer->modifier & 0xffffffff);
}
zwp_linux_buffer_params_v1_add_listener(params, &params_listener, buffer); zwp_linux_buffer_params_v1_add_listener(params, &params_listener, buffer);
if (display->req_dmabuf_immediate) { if (display->req_dmabuf_immediate) {
@ -281,7 +369,7 @@ create_dmabuf_buffer(struct display *display, struct buffer *buffer,
zwp_linux_buffer_params_v1_create_immed(params, zwp_linux_buffer_params_v1_create_immed(params,
buffer->width, buffer->width,
buffer->height, buffer->height,
format, buffer->format,
flags); flags);
wl_buffer_add_listener(buffer->buffer, &buffer_listener, buffer); wl_buffer_add_listener(buffer->buffer, &buffer_listener, buffer);
} }
@ -289,7 +377,7 @@ create_dmabuf_buffer(struct display *display, struct buffer *buffer,
zwp_linux_buffer_params_v1_create(params, zwp_linux_buffer_params_v1_create(params,
buffer->width, buffer->width,
buffer->height, buffer->height,
format, buffer->format,
flags); flags);
} }
@ -386,9 +474,16 @@ create_window(struct display *display, int width, int height)
assert(0); assert(0);
} }
for (i = 0; i < NUM_BUFFERS; ++i) {
int j;
for (j = 0; j < MAX_BUFFER_PLANES; ++j)
window->buffers[i].dmabuf_fds[j] = -1;
}
for (i = 0; i < NUM_BUFFERS; ++i) { for (i = 0; i < NUM_BUFFERS; ++i) {
ret = create_dmabuf_buffer(display, &window->buffers[i], ret = create_dmabuf_buffer(display, &window->buffers[i],
width, height, DRM_FORMAT_XRGB8888); width, height);
if (ret < 0) if (ret < 0)
return NULL; return NULL;
@ -488,8 +583,12 @@ dmabuf_modifiers(void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf,
struct display *d = data; struct display *d = data;
switch (format) { switch (format) {
case DRM_FORMAT_XRGB8888: case BUFFER_FORMAT:
d->xrgb8888_format_found = 1; ++d->modifiers_count;
d->modifiers = realloc(d->modifiers,
d->modifiers_count * sizeof(*d->modifiers));
d->modifiers[d->modifiers_count - 1] =
((uint64_t)modifier_hi << 32) | modifier_lo;
break; break;
default: default:
break; break;
@ -569,6 +668,8 @@ destroy_display(struct display *display)
if (display->egl.display != EGL_NO_DISPLAY) if (display->egl.display != EGL_NO_DISPLAY)
eglTerminate(display->egl.display); eglTerminate(display->egl.display);
free(display->modifiers);
if (display->dmabuf) if (display->dmabuf)
zwp_linux_dmabuf_v1_destroy(display->dmabuf); zwp_linux_dmabuf_v1_destroy(display->dmabuf);
@ -663,6 +764,14 @@ display_set_up_egl(struct display *display)
goto error; goto error;
} }
if (weston_check_egl_extension(egl_extensions,
"EGL_EXT_image_dma_buf_import_modifiers")) {
display->egl.has_dma_buf_import_modifiers = true;
display->egl.query_dma_buf_modifiers =
(void *) eglGetProcAddress("eglQueryDmaBufModifiersEXT");
assert(display->egl.query_dma_buf_modifiers);
}
display->egl.create_image = display->egl.create_image =
(void *) eglGetProcAddress("eglCreateImageKHR"); (void *) eglGetProcAddress("eglCreateImageKHR");
assert(display->egl.create_image); assert(display->egl.create_image);
@ -681,6 +790,76 @@ error:
return false; return false;
} }
static bool
display_update_supported_modifiers_for_egl(struct display *d)
{
uint64_t *egl_modifiers = NULL;
int num_egl_modifiers = 0;
EGLBoolean ret;
int i;
/* If EGL doesn't support modifiers, don't use them at all. */
if (!d->egl.has_dma_buf_import_modifiers) {
d->modifiers_count = 0;
free(d->modifiers);
d->modifiers = NULL;
return true;
}
ret = d->egl.query_dma_buf_modifiers(d->egl.display,
BUFFER_FORMAT,
0, /* max_modifiers */
NULL, /* modifiers */
NULL, /* external_only */
&num_egl_modifiers);
if (ret == EGL_FALSE || num_egl_modifiers == 0) {
fprintf(stderr, "Failed to query num EGL modifiers for format\n");
goto error;
}
egl_modifiers = zalloc(num_egl_modifiers * sizeof(*egl_modifiers));
ret = d->egl.query_dma_buf_modifiers(d->egl.display,
BUFFER_FORMAT,
num_egl_modifiers,
egl_modifiers,
NULL, /* external_only */
&num_egl_modifiers);
if (ret == EGL_FALSE) {
fprintf(stderr, "Failed to query EGL modifiers for format\n");
goto error;
}
/* Poor person's set intersection: d->modifiers INTERSECT
* egl_modifiers. If a modifier is not supported, replace it with
* DRM_FORMAT_MOD_INVALID in the d->modifiers array.
*/
for (i = 0; i < d->modifiers_count; ++i) {
uint64_t mod = d->modifiers[i];
bool egl_supported = false;
int j;
for (j = 0; j < num_egl_modifiers; ++j) {
if (egl_modifiers[j] == mod) {
egl_supported = true;
break;
}
}
if (!egl_supported)
d->modifiers[i] = DRM_FORMAT_MOD_INVALID;
}
free(egl_modifiers);
return true;
error:
free(egl_modifiers);
return false;
}
static bool static bool
display_set_up_gbm(struct display *display, char const* drm_render_node) display_set_up_gbm(struct display *display, char const* drm_render_node)
{ {
@ -729,7 +908,7 @@ create_display(char const *drm_render_node, int opts)
wl_display_roundtrip(display->display); wl_display_roundtrip(display->display);
if (!display->xrgb8888_format_found) { if (!display->modifiers_count) {
fprintf(stderr, "format XRGB8888 is not available\n"); fprintf(stderr, "format XRGB8888 is not available\n");
goto error; goto error;
} }
@ -737,6 +916,9 @@ create_display(char const *drm_render_node, int opts)
if (!display_set_up_egl(display)) if (!display_set_up_egl(display))
goto error; goto error;
if (!display_update_supported_modifiers_for_egl(display))
goto error;
if (!display_set_up_gbm(display, drm_render_node)) if (!display_set_up_gbm(display, drm_render_node))
goto error; goto error;