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:
parent
29a6803817
commit
e9700f62af
@ -61,7 +61,7 @@ simple_clients = [
|
||||
],
|
||||
'dep_objs': [
|
||||
dep_wayland_client,
|
||||
dep_libdrm_headers
|
||||
dep_libdrm
|
||||
],
|
||||
'deps': [ 'egl', 'glesv2', 'gbm' ]
|
||||
},
|
||||
|
@ -39,9 +39,11 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include <drm_fourcc.h>
|
||||
#include <xf86drm.h>
|
||||
#include <gbm.h>
|
||||
|
||||
#include <wayland-client.h>
|
||||
#include "shared/helpers.h"
|
||||
#include "shared/platform.h"
|
||||
#include "shared/zalloc.h"
|
||||
#include "xdg-shell-unstable-v6-client-protocol.h"
|
||||
@ -62,6 +64,9 @@
|
||||
/* Possible options that affect the displayed image */
|
||||
#define OPT_IMMEDIATE 1 /* create wl_buffer immediately */
|
||||
|
||||
#define BUFFER_FORMAT DRM_FORMAT_XRGB8888
|
||||
#define MAX_BUFFER_PLANES 4
|
||||
|
||||
struct display {
|
||||
struct wl_display *display;
|
||||
struct wl_registry *registry;
|
||||
@ -69,11 +74,14 @@ struct display {
|
||||
struct zxdg_shell_v6 *shell;
|
||||
struct zwp_fullscreen_shell_v1 *fshell;
|
||||
struct zwp_linux_dmabuf_v1 *dmabuf;
|
||||
int xrgb8888_format_found;
|
||||
uint64_t *modifiers;
|
||||
int modifiers_count;
|
||||
int req_dmabuf_immediate;
|
||||
struct {
|
||||
EGLDisplay display;
|
||||
EGLContext context;
|
||||
bool has_dma_buf_import_modifiers;
|
||||
PFNEGLQUERYDMABUFMODIFIERSEXTPROC query_dma_buf_modifiers;
|
||||
PFNEGLCREATEIMAGEKHRPROC create_image;
|
||||
PFNEGLDESTROYIMAGEKHRPROC destroy_image;
|
||||
PFNGLEGLIMAGETARGETTEXTURE2DOESPROC image_target_texture_2d;
|
||||
@ -91,12 +99,14 @@ struct buffer {
|
||||
|
||||
struct gbm_bo *bo;
|
||||
|
||||
int dmabuf_fd;
|
||||
|
||||
int width;
|
||||
int height;
|
||||
uint32_t stride;
|
||||
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;
|
||||
GLuint gl_texture;
|
||||
@ -137,6 +147,8 @@ static const struct wl_buffer_listener buffer_listener = {
|
||||
static void
|
||||
buffer_free(struct buffer *buf)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (buf->gl_fbo)
|
||||
glDeleteFramebuffers(1, &buf->gl_fbo);
|
||||
|
||||
@ -154,8 +166,10 @@ buffer_free(struct buffer *buf)
|
||||
if (buf->bo)
|
||||
gbm_bo_destroy(buf->bo);
|
||||
|
||||
if (buf->dmabuf_fd >= 0)
|
||||
close(buf->dmabuf_fd);
|
||||
for (i = 0; i < buf->plane_count; ++i) {
|
||||
if (buf->dmabuf_fds[i] >= 0)
|
||||
close(buf->dmabuf_fds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@ -192,15 +206,52 @@ static const struct zwp_linux_buffer_params_v1_listener params_listener = {
|
||||
static bool
|
||||
create_fbo_for_buffer(struct display *display, struct buffer *buffer)
|
||||
{
|
||||
EGLint attribs[] = {
|
||||
EGL_WIDTH, buffer->width,
|
||||
EGL_HEIGHT, buffer->height,
|
||||
EGL_LINUX_DRM_FOURCC_EXT, buffer->format,
|
||||
EGL_DMA_BUF_PLANE0_FD_EXT, buffer->dmabuf_fd,
|
||||
EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0,
|
||||
EGL_DMA_BUF_PLANE0_PITCH_EXT, (int) buffer->stride,
|
||||
EGL_NONE
|
||||
};
|
||||
static const int general_attribs = 3;
|
||||
static const int plane_attribs = 5;
|
||||
static const int entries_per_attrib = 2;
|
||||
EGLint attribs[(general_attribs + plane_attribs * MAX_BUFFER_PLANES) *
|
||||
entries_per_attrib + 1];
|
||||
unsigned int atti = 0;
|
||||
|
||||
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,
|
||||
EGL_NO_CONTEXT,
|
||||
@ -238,42 +289,79 @@ create_fbo_for_buffer(struct display *display, struct buffer *buffer)
|
||||
|
||||
static int
|
||||
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;
|
||||
static const uint64_t modifier = DRM_FORMAT_MOD_INVALID;
|
||||
/* Y-Invert the buffer image, since we are going to renderer to the
|
||||
* buffer through a FBO. */
|
||||
static const uint32_t flags = ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT;
|
||||
struct zwp_linux_buffer_params_v1 *params;
|
||||
int i;
|
||||
|
||||
buffer->display = display;
|
||||
buffer->width = width;
|
||||
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) {
|
||||
fprintf(stderr, "create_bo failed\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
buffer->stride = gbm_bo_get_stride(buffer->bo);
|
||||
buffer->dmabuf_fd = gbm_bo_get_fd(buffer->bo);
|
||||
|
||||
if (buffer->dmabuf_fd < 0) {
|
||||
fprintf(stderr, "error: dmabuf_fd < 0\n");
|
||||
#ifdef HAVE_GBM_MODIFIERS
|
||||
buffer->plane_count = gbm_bo_get_plane_count(buffer->bo);
|
||||
for (i = 0; i < buffer->plane_count; ++i) {
|
||||
uint32_t handle = gbm_bo_get_handle_for_plane(buffer->bo, i).u32;
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
|
||||
params = zwp_linux_dmabuf_v1_create_params(display->dmabuf);
|
||||
zwp_linux_buffer_params_v1_add(params,
|
||||
buffer->dmabuf_fd,
|
||||
0, /* plane_idx */
|
||||
0, /* offset */
|
||||
buffer->stride,
|
||||
modifier >> 32,
|
||||
modifier & 0xffffffff);
|
||||
for (i = 0; i < buffer->plane_count; ++i) {
|
||||
zwp_linux_buffer_params_v1_add(params,
|
||||
buffer->dmabuf_fds[i],
|
||||
i,
|
||||
buffer->offsets[i],
|
||||
buffer->strides[i],
|
||||
buffer->modifier >> 32,
|
||||
buffer->modifier & 0xffffffff);
|
||||
}
|
||||
|
||||
zwp_linux_buffer_params_v1_add_listener(params, ¶ms_listener, buffer);
|
||||
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,
|
||||
buffer->width,
|
||||
buffer->height,
|
||||
format,
|
||||
buffer->format,
|
||||
flags);
|
||||
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,
|
||||
buffer->width,
|
||||
buffer->height,
|
||||
format,
|
||||
buffer->format,
|
||||
flags);
|
||||
}
|
||||
|
||||
@ -386,9 +474,16 @@ create_window(struct display *display, int width, int height)
|
||||
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) {
|
||||
ret = create_dmabuf_buffer(display, &window->buffers[i],
|
||||
width, height, DRM_FORMAT_XRGB8888);
|
||||
width, height);
|
||||
|
||||
if (ret < 0)
|
||||
return NULL;
|
||||
@ -488,8 +583,12 @@ dmabuf_modifiers(void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf,
|
||||
struct display *d = data;
|
||||
|
||||
switch (format) {
|
||||
case DRM_FORMAT_XRGB8888:
|
||||
d->xrgb8888_format_found = 1;
|
||||
case BUFFER_FORMAT:
|
||||
++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;
|
||||
default:
|
||||
break;
|
||||
@ -569,6 +668,8 @@ destroy_display(struct display *display)
|
||||
if (display->egl.display != EGL_NO_DISPLAY)
|
||||
eglTerminate(display->egl.display);
|
||||
|
||||
free(display->modifiers);
|
||||
|
||||
if (display->dmabuf)
|
||||
zwp_linux_dmabuf_v1_destroy(display->dmabuf);
|
||||
|
||||
@ -663,6 +764,14 @@ display_set_up_egl(struct display *display)
|
||||
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 =
|
||||
(void *) eglGetProcAddress("eglCreateImageKHR");
|
||||
assert(display->egl.create_image);
|
||||
@ -681,6 +790,76 @@ error:
|
||||
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
|
||||
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);
|
||||
|
||||
if (!display->xrgb8888_format_found) {
|
||||
if (!display->modifiers_count) {
|
||||
fprintf(stderr, "format XRGB8888 is not available\n");
|
||||
goto error;
|
||||
}
|
||||
@ -737,6 +916,9 @@ create_display(char const *drm_render_node, int opts)
|
||||
if (!display_set_up_egl(display))
|
||||
goto error;
|
||||
|
||||
if (!display_update_supported_modifiers_for_egl(display))
|
||||
goto error;
|
||||
|
||||
if (!display_set_up_gbm(display, drm_render_node))
|
||||
goto error;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user