Commit 069fdd5f authored by Louis-Francis Ratté-Boulianne's avatar Louis-Francis Ratté-Boulianne Committed by Daniel Stone
Browse files

egl/x11: Support DRI3 v1.1



Add support for DRI3 v1.1, which allows pixmaps to be backed by
multi-planar buffers, or those with format modifiers. This is both
for allocating render buffers, as well as EGLImage imports from a
native pixmap (EGL_NATIVE_PIXMAP_KHR).
Signed-off-by: Louis-Francis Ratté-Boulianne's avatarLouis-Francis Ratté-Boulianne <lfrb@collabora.com>
Reviewed-by: Eric Engestrom's avatarEric Engestrom <eric.engestrom@imgtec.com>
Reviewed-by: Emil Velikov's avatarEmil Velikov <emil.velikov@collabora.com>
Reviewed-by: Daniel Stone's avatarDaniel Stone <daniels@collabora.com>
parent 61309c2a
......@@ -881,6 +881,13 @@ dri2_setup_extensions(_EGLDisplay *disp)
if (!dri2_bind_extensions(dri2_dpy, mandatory_core_extensions, extensions, false))
return EGL_FALSE;
#ifdef HAVE_DRI3
dri2_dpy->multibuffers_available =
(dri2_dpy->dri3_major_version > 1 || (dri2_dpy->dri3_major_version == 1 &&
dri2_dpy->dri3_minor_version >= 2)) &&
(dri2_dpy->image && dri2_dpy->image->base.version >= 15);
#endif
dri2_bind_extensions(dri2_dpy, optional_core_extensions, extensions, true);
return EGL_TRUE;
}
......
......@@ -199,6 +199,9 @@ struct dri2_egl_display
xcb_screen_t *screen;
bool swap_available;
#ifdef HAVE_DRI3
bool multibuffers_available;
int dri3_major_version;
int dri3_minor_version;
struct loader_dri3_extensions loader_dri3_ext;
#endif
#endif
......
......@@ -39,6 +39,23 @@
#include "loader.h"
#include "loader_dri3_helper.h"
static uint32_t
dri3_format_for_depth(uint32_t depth)
{
switch (depth) {
case 16:
return __DRI_IMAGE_FORMAT_RGB565;
case 24:
return __DRI_IMAGE_FORMAT_XRGB8888;
case 30:
return __DRI_IMAGE_FORMAT_XRGB2101010;
case 32:
return __DRI_IMAGE_FORMAT_ARGB8888;
default:
return __DRI_IMAGE_FORMAT_NONE;
}
}
static struct dri3_egl_surface *
loader_drawable_to_egl_surface(struct loader_dri3_drawable *draw) {
size_t offset = offsetof(struct dri3_egl_surface, loader_drawable);
......@@ -168,7 +185,9 @@ dri3_create_surface(_EGLDriver *drv, _EGLDisplay *disp, EGLint type,
if (loader_dri3_drawable_init(dri2_dpy->conn, drawable,
dri2_dpy->dri_screen,
dri2_dpy->is_different_gpu, dri_config,
dri2_dpy->is_different_gpu,
dri2_dpy->multibuffers_available,
dri_config,
&dri2_dpy->loader_dri3_ext,
&egl_dri3_vtable,
&dri3_surf->loader_drawable)) {
......@@ -274,20 +293,8 @@ dri3_create_image_khr_pixmap(_EGLDisplay *disp, _EGLContext *ctx,
return NULL;
}
switch (bp_reply->depth) {
case 16:
format = __DRI_IMAGE_FORMAT_RGB565;
break;
case 24:
format = __DRI_IMAGE_FORMAT_XRGB8888;
break;
case 30:
format = __DRI_IMAGE_FORMAT_XRGB2101010;
break;
case 32:
format = __DRI_IMAGE_FORMAT_ARGB8888;
break;
default:
format = dri3_format_for_depth(bp_reply->depth);
if (format == __DRI_IMAGE_FORMAT_NONE) {
_eglError(EGL_BAD_PARAMETER,
"dri3_create_image_khr: unsupported pixmap depth");
free(bp_reply);
......@@ -315,13 +322,74 @@ dri3_create_image_khr_pixmap(_EGLDisplay *disp, _EGLContext *ctx,
return &dri2_img->base;
}
static _EGLImage *
dri3_create_image_khr_pixmap_from_buffers(_EGLDisplay *disp, _EGLContext *ctx,
EGLClientBuffer buffer,
const EGLint *attr_list)
{
struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
struct dri2_egl_image *dri2_img;
xcb_dri3_buffers_from_pixmap_cookie_t bp_cookie;
xcb_dri3_buffers_from_pixmap_reply_t *bp_reply;
xcb_drawable_t drawable;
unsigned int format;
drawable = (xcb_drawable_t) (uintptr_t) buffer;
bp_cookie = xcb_dri3_buffers_from_pixmap(dri2_dpy->conn, drawable);
bp_reply = xcb_dri3_buffers_from_pixmap_reply(dri2_dpy->conn,
bp_cookie, NULL);
if (!bp_reply) {
_eglError(EGL_BAD_ATTRIBUTE, "dri3_create_image_khr");
return EGL_NO_IMAGE_KHR;
}
format = dri3_format_for_depth(bp_reply->depth);
if (format == __DRI_IMAGE_FORMAT_NONE) {
_eglError(EGL_BAD_PARAMETER,
"dri3_create_image_khr: unsupported pixmap depth");
free(bp_reply);
return EGL_NO_IMAGE_KHR;
}
dri2_img = malloc(sizeof *dri2_img);
if (!dri2_img) {
_eglError(EGL_BAD_ALLOC, "dri3_create_image_khr");
free(bp_reply);
return EGL_NO_IMAGE_KHR;
}
_eglInitImage(&dri2_img->base, disp);
dri2_img->dri_image = loader_dri3_create_image_from_buffers(dri2_dpy->conn,
bp_reply,
format,
dri2_dpy->dri_screen,
dri2_dpy->image,
dri2_img);
free(bp_reply);
if (!dri2_img->dri_image) {
_eglError(EGL_BAD_ATTRIBUTE, "dri3_create_image_khr");
free(dri2_img);
return EGL_NO_IMAGE_KHR;
}
return &dri2_img->base;
}
static _EGLImage *
dri3_create_image_khr(_EGLDriver *drv, _EGLDisplay *disp,
_EGLContext *ctx, EGLenum target,
EGLClientBuffer buffer, const EGLint *attr_list)
{
struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
switch (target) {
case EGL_NATIVE_PIXMAP_KHR:
if (dri2_dpy->multibuffers_available)
return dri3_create_image_khr_pixmap_from_buffers(disp, ctx, buffer,
attr_list);
return dri3_create_image_khr_pixmap(disp, ctx, buffer, attr_list);
default:
return dri2_create_image_khr(drv, disp, ctx, target, buffer, attr_list);
......@@ -483,6 +551,9 @@ dri3_x11_connect(struct dri2_egl_display *dri2_dpy)
free(error);
return EGL_FALSE;
}
dri2_dpy->dri3_major_version = dri3_query->major_version;
dri2_dpy->dri3_minor_version = dri3_query->minor_version;
free(dri3_query);
present_query =
......
......@@ -357,7 +357,10 @@ dri3_create_drawable(struct glx_screen *base, XID xDrawable,
{
struct dri3_drawable *pdraw;
struct dri3_screen *psc = (struct dri3_screen *) base;
const struct dri3_display *const pdp = (struct dri3_display *)
base->display->dri3Display;
__GLXDRIconfigPrivate *config = (__GLXDRIconfigPrivate *) config_base;
bool has_multibuffer = false;
pdraw = calloc(1, sizeof(*pdraw));
if (!pdraw)
......@@ -368,11 +371,16 @@ dri3_create_drawable(struct glx_screen *base, XID xDrawable,
pdraw->base.drawable = drawable;
pdraw->base.psc = &psc->base;
if ((psc->image && psc->image->base.version >= 15) &&
(pdp->dri3Major > 1 || (pdp->dri3Major == 1 && pdp->dri3Minor >= 2)))
has_multibuffer = true;
(void) __glXInitialize(psc->base.dpy);
if (loader_dri3_drawable_init(XGetXCBConnection(base->dpy),
xDrawable, psc->driScreen,
psc->is_different_gpu, config->driConfig,
psc->is_different_gpu, has_multibuffer,
config->driConfig,
&psc->loader_dri3_ext, &glx_dri3_vtable,
&pdraw->loader_drawable)) {
free(pdraw);
......
......@@ -24,6 +24,7 @@
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <X11/xshmfence.h>
#include <xcb/xcb.h>
......@@ -34,6 +35,7 @@
#include "loader_dri3_helper.h"
#include "util/macros.h"
#include "drm_fourcc.h"
/* From xmlpool/options.h, user exposed so should be stable */
#define DRI_CONF_VBLANK_NEVER 0
......@@ -257,6 +259,7 @@ loader_dri3_drawable_init(xcb_connection_t *conn,
xcb_drawable_t drawable,
__DRIscreen *dri_screen,
bool is_different_gpu,
bool multiplanes_available,
const __DRIconfig *dri_config,
struct loader_dri3_extensions *ext,
const struct loader_dri3_vtable *vtable,
......@@ -274,6 +277,7 @@ loader_dri3_drawable_init(xcb_connection_t *conn,
draw->drawable = drawable;
draw->dri_screen = dri_screen;
draw->is_different_gpu = is_different_gpu;
draw->multiplanes_available = multiplanes_available;
draw->have_back = 0;
draw->have_fake_front = 0;
......@@ -1023,6 +1027,41 @@ image_format_to_fourcc(int format)
return 0;
}
static bool
has_supported_modifier(struct loader_dri3_drawable *draw, unsigned int format,
uint64_t *modifiers, uint32_t count)
{
uint64_t *supported_modifiers;
int32_t supported_modifiers_count;
bool found = false;
int i, j;
if (!draw->ext->image->queryDmaBufModifiers(draw->dri_screen,
format, 0, NULL, NULL,
&supported_modifiers_count) ||
supported_modifiers_count == 0)
return false;
supported_modifiers = malloc(supported_modifiers_count * sizeof(uint64_t));
if (!supported_modifiers)
return false;
draw->ext->image->queryDmaBufModifiers(draw->dri_screen, format,
supported_modifiers_count,
supported_modifiers, NULL,
&supported_modifiers_count);
for (i = 0; !found && i < supported_modifiers_count; i++) {
for (j = 0; !found && j < count; j++) {
if (supported_modifiers[i] == modifiers[j])
found = true;
}
}
free(supported_modifiers);
return found;
}
/** loader_dri3_alloc_render_buffer
*
* Use the driver createImage function to construct a __DRIimage, then
......@@ -1039,8 +1078,10 @@ dri3_alloc_render_buffer(struct loader_dri3_drawable *draw, unsigned int format,
xcb_pixmap_t pixmap;
xcb_sync_fence_t sync_fence;
struct xshmfence *shm_fence;
int buffer_fd, fence_fd;
int stride;
int buffer_fds[4], fence_fd;
int num_planes = 0;
int i, mod;
int ret;
/* Create an xshmfence object and
* prepare to send that to the X server
......@@ -1065,13 +1106,79 @@ dri3_alloc_render_buffer(struct loader_dri3_drawable *draw, unsigned int format,
goto no_image;
if (!draw->is_different_gpu) {
buffer->image = draw->ext->image->createImage(draw->dri_screen,
width, height,
format,
__DRI_IMAGE_USE_SHARE |
__DRI_IMAGE_USE_SCANOUT |
__DRI_IMAGE_USE_BACKBUFFER,
buffer);
if (draw->multiplanes_available &&
draw->ext->image->base.version >= 15 &&
draw->ext->image->queryDmaBufModifiers &&
draw->ext->image->createImageWithModifiers) {
xcb_dri3_get_supported_modifiers_cookie_t mod_cookie;
xcb_dri3_get_supported_modifiers_reply_t *mod_reply;
xcb_generic_error_t *error = NULL;
uint64_t *modifiers = NULL;
uint32_t count = 0;
mod_cookie = xcb_dri3_get_supported_modifiers(draw->conn,
draw->drawable,
depth, buffer->cpp * 8);
mod_reply = xcb_dri3_get_supported_modifiers_reply(draw->conn,
mod_cookie,
&error);
if (!mod_reply)
goto no_image;
if (mod_reply->num_window_modifiers) {
count = mod_reply->num_window_modifiers;
modifiers = malloc(count * sizeof(uint64_t));
if (!modifiers) {
free(mod_reply);
goto no_image;
}
memcpy(modifiers,
xcb_dri3_get_supported_modifiers_window_modifiers(mod_reply),
count * sizeof(uint64_t));
if (!has_supported_modifier(draw, image_format_to_fourcc(format),
modifiers, count)) {
free(modifiers);
count = 0;
modifiers = NULL;
}
}
if (mod_reply->num_screen_modifiers && modifiers == NULL) {
count = mod_reply->num_screen_modifiers;
modifiers = malloc(count * sizeof(uint64_t));
if (!modifiers) {
free(modifiers);
free(mod_reply);
goto no_image;
}
memcpy(modifiers,
xcb_dri3_get_supported_modifiers_screen_modifiers(mod_reply),
count * sizeof(uint64_t));
}
free(mod_reply);
buffer->image = draw->ext->image->createImageWithModifiers(draw->dri_screen,
width, height,
format,
modifiers,
count,
buffer);
free(modifiers);
}
if (!buffer->image)
buffer->image = draw->ext->image->createImage(draw->dri_screen,
width, height,
format,
__DRI_IMAGE_USE_SHARE |
__DRI_IMAGE_USE_SCANOUT |
__DRI_IMAGE_USE_BACKBUFFER,
buffer);
pixmap_buffer = buffer->image;
if (!buffer->image)
......@@ -1099,25 +1206,67 @@ dri3_alloc_render_buffer(struct loader_dri3_drawable *draw, unsigned int format,
goto no_linear_buffer;
}
/* X wants the stride, so ask the image for it
/* X want some information about the planes, so ask the image for it
*/
if (!draw->ext->image->queryImage(pixmap_buffer, __DRI_IMAGE_ATTRIB_STRIDE,
&stride))
goto no_buffer_attrib;
if (!draw->ext->image->queryImage(pixmap_buffer, __DRI_IMAGE_ATTRIB_NUM_PLANES,
&num_planes))
num_planes = 1;
for (i = 0; i < num_planes; i++) {
__DRIimage *image = draw->ext->image->fromPlanar(pixmap_buffer, i, NULL);
buffer->pitch = stride;
if (!image) {
assert(i == 0);
image = pixmap_buffer;
}
if (!draw->ext->image->queryImage(pixmap_buffer, __DRI_IMAGE_ATTRIB_FD,
&buffer_fd))
goto no_buffer_attrib;
ret = draw->ext->image->queryImage(image, __DRI_IMAGE_ATTRIB_FD,
&buffer_fds[i]);
ret &= draw->ext->image->queryImage(image, __DRI_IMAGE_ATTRIB_STRIDE,
&buffer->strides[i]);
ret &= draw->ext->image->queryImage(image, __DRI_IMAGE_ATTRIB_OFFSET,
&buffer->offsets[i]);
if (image != pixmap_buffer)
draw->ext->image->destroyImage(image);
if (!ret)
goto no_buffer_attrib;
}
xcb_dri3_pixmap_from_buffer(draw->conn,
(pixmap = xcb_generate_id(draw->conn)),
draw->drawable,
buffer->size,
width, height, buffer->pitch,
depth, buffer->cpp * 8,
buffer_fd);
ret = draw->ext->image->queryImage(pixmap_buffer,
__DRI_IMAGE_ATTRIB_MODIFIER_UPPER, &mod);
buffer->modifier = (uint64_t) mod << 32;
ret &= draw->ext->image->queryImage(pixmap_buffer,
__DRI_IMAGE_ATTRIB_MODIFIER_LOWER, &mod);
buffer->modifier |= (uint64_t)(mod & 0xffffffff);
if (!ret)
buffer->modifier = DRM_FORMAT_MOD_INVALID;
pixmap = xcb_generate_id(draw->conn);
if (draw->multiplanes_available &&
buffer->modifier != DRM_FORMAT_MOD_INVALID) {
xcb_dri3_pixmap_from_buffers(draw->conn,
pixmap,
draw->drawable,
num_planes,
width, height,
buffer->strides[0], buffer->offsets[0],
buffer->strides[1], buffer->offsets[1],
buffer->strides[2], buffer->offsets[2],
buffer->strides[3], buffer->offsets[3],
depth, buffer->cpp * 8,
buffer->modifier,
buffer_fds);
} else {
xcb_dri3_pixmap_from_buffer(draw->conn,
pixmap,
draw->drawable,
buffer->size,
width, height, buffer->strides[0],
depth, buffer->cpp * 8,
buffer_fds[0]);
}
xcb_dri3_fence_from_fd(draw->conn,
pixmap,
......@@ -1139,6 +1288,9 @@ dri3_alloc_render_buffer(struct loader_dri3_drawable *draw, unsigned int format,
return buffer;
no_buffer_attrib:
do {
close(buffer_fds[i]);
} while (--i >= 0);
draw->ext->image->destroyImage(pixmap_buffer);
no_linear_buffer:
if (draw->is_different_gpu)
......@@ -1295,6 +1447,48 @@ loader_dri3_create_image(xcb_connection_t *c,
return ret;
}
__DRIimage *
loader_dri3_create_image_from_buffers(xcb_connection_t *c,
xcb_dri3_buffers_from_pixmap_reply_t *bp_reply,
unsigned int format,
__DRIscreen *dri_screen,
const __DRIimageExtension *image,
void *loaderPrivate)
{
__DRIimage *ret;
int *fds;
uint32_t *strides_in, *offsets_in;
int strides[4], offsets[4];
unsigned error;
int i;
if (bp_reply->nfd > 4)
return NULL;
fds = xcb_dri3_buffers_from_pixmap_reply_fds(c, bp_reply);
strides_in = xcb_dri3_buffers_from_pixmap_strides(bp_reply);
offsets_in = xcb_dri3_buffers_from_pixmap_offsets(bp_reply);
for (i = 0; i < bp_reply->nfd; i++) {
strides[i] = strides_in[i];
offsets[i] = offsets_in[i];
}
ret = image->createImageFromDmaBufs2(dri_screen,
bp_reply->width,
bp_reply->height,
image_format_to_fourcc(format),
bp_reply->modifier,
fds, bp_reply->nfd,
strides, offsets,
0, 0, 0, 0, /* UNDEFINED */
&error, loaderPrivate);
for (i = 0; i < bp_reply->nfd; i++)
close(fds[i]);
return ret;
}
/** dri3_get_pixmap_buffer
*
* Get the DRM object for a pixmap from the X server and
......@@ -1308,10 +1502,10 @@ dri3_get_pixmap_buffer(__DRIdrawable *driDrawable, unsigned int format,
int buf_id = loader_dri3_pixmap_buf_id(buffer_type);
struct loader_dri3_buffer *buffer = draw->buffers[buf_id];
xcb_drawable_t pixmap;
xcb_dri3_buffer_from_pixmap_cookie_t bp_cookie;
xcb_dri3_buffer_from_pixmap_reply_t *bp_reply;
xcb_sync_fence_t sync_fence;
struct xshmfence *shm_fence;
int width;
int height;
int fence_fd;
__DRIscreen *cur_screen;
......@@ -1333,17 +1527,6 @@ dri3_get_pixmap_buffer(__DRIdrawable *driDrawable, unsigned int format,
goto no_fence;
}
xcb_dri3_fence_from_fd(draw->conn,
pixmap,
(sync_fence = xcb_generate_id(draw->conn)),
false,
fence_fd);
bp_cookie = xcb_dri3_buffer_from_pixmap(draw->conn, pixmap);
bp_reply = xcb_dri3_buffer_from_pixmap_reply(draw->conn, bp_cookie, NULL);
if (!bp_reply)
goto no_image;
/* Get the currently-bound screen or revert to using the drawable's screen if
* no contexts are currently bound. The latter case is at least necessary for
* obs-studio, when using Window Capture (Xcomposite) as a Source.
......@@ -1353,27 +1536,62 @@ dri3_get_pixmap_buffer(__DRIdrawable *driDrawable, unsigned int format,
cur_screen = draw->dri_screen;
}
buffer->image = loader_dri3_create_image(draw->conn, bp_reply, format,
cur_screen, draw->ext->image,
buffer);
xcb_dri3_fence_from_fd(draw->conn,
pixmap,
(sync_fence = xcb_generate_id(draw->conn)),
false,
fence_fd);
if (draw->multiplanes_available &&
draw->ext->image->base.version >= 15 &&
draw->ext->image->createImageFromDmaBufs2) {
xcb_dri3_buffers_from_pixmap_cookie_t bps_cookie;
xcb_dri3_buffers_from_pixmap_reply_t *bps_reply;
bps_cookie = xcb_dri3_buffers_from_pixmap(draw->conn, pixmap);
bps_reply = xcb_dri3_buffers_from_pixmap_reply(draw->conn, bps_cookie,
NULL);
if (!bps_reply)
goto no_image;
buffer->image =
loader_dri3_create_image_from_buffers(draw->conn, bps_reply, format,
cur_screen, draw->ext->image,
buffer);
width = bps_reply->width;
height = bps_reply->height;
free(bps_reply);
} else {
xcb_dri3_buffer_from_pixmap_cookie_t bp_cookie;
xcb_dri3_buffer_from_pixmap_reply_t *bp_reply;
bp_cookie = xcb_dri3_buffer_from_pixmap(draw->conn, pixmap);
bp_reply = xcb_dri3_buffer_from_pixmap_reply(draw->conn, bp_cookie, NULL);
if (!bp_reply)
goto no_image;
buffer->image = loader_dri3_create_image(draw->conn, bp_reply, format,
cur_screen, draw->ext->image,
buffer);
width = bp_reply->width;
height = bp_reply->height;
free(bp_reply);
}
if (!buffer->image)
goto no_image;
buffer->pixmap = pixmap;
buffer->own_pixmap = false;
buffer->width = bp_reply->width;
buffer->height = bp_reply->height;
buffer->width = width;
buffer->height = height;
buffer->shm_fence = shm_fence;
buffer->sync_fence = sync_fence;
draw->buffers[buf_id] = buffer;
free(bp_reply);
return buffer;
no_image: