ui/egl: support multi-plane dmabuf when egl export/import

v2:
  * use new dmabuf API and check length

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Qiang Yu <yuq825@gmail.com>
[ Fix style ]
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Message-ID: <20250327025848.46962-5-yuq825@gmail.com>
This commit is contained in:
Qiang Yu 2025-03-27 10:58:46 +08:00 committed by Marc-André Lureau
parent ac70568902
commit 0e15d0b927
4 changed files with 124 additions and 52 deletions

View file

@ -46,8 +46,9 @@ extern int qemu_egl_rn_fd;
extern struct gbm_device *qemu_egl_rn_gbm_dev;
int egl_rendernode_init(const char *rendernode, DisplayGLMode mode);
int egl_get_fd_for_texture(uint32_t tex_id, EGLint *stride, EGLint *fourcc,
EGLuint64KHR *modifier);
bool egl_dmabuf_export_texture(uint32_t tex_id, int *fd, EGLint *offset,
EGLint *stride, EGLint *fourcc, int *num_planes,
EGLuint64KHR *modifier);
void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf);
void egl_dmabuf_release_texture(QemuDmaBuf *dmabuf);

View file

@ -504,19 +504,22 @@ static void dbus_scanout_texture(DisplayChangeListener *dcl,
backing_width, backing_height, x, y, w, h);
#ifdef CONFIG_GBM
g_autoptr(QemuDmaBuf) dmabuf = NULL;
int fd;
uint32_t offset = 0, stride, fourcc;
int fd[DMABUF_MAX_PLANES], num_planes;
uint32_t offset[DMABUF_MAX_PLANES], stride[DMABUF_MAX_PLANES], fourcc;
uint64_t modifier;
assert(tex_id);
fd = egl_get_fd_for_texture(tex_id, (EGLint *)&stride, (EGLint *)&fourcc,
&modifier);
if (fd < 0) {
error_report("%s: failed to get fd for texture", __func__);
if (!egl_dmabuf_export_texture(tex_id, fd, (EGLint *)offset, (EGLint *)stride,
(EGLint *)&fourcc, &num_planes, &modifier)) {
error_report("%s: failed to export dmabuf for texture", __func__);
return;
}
dmabuf = qemu_dmabuf_new(w, h, &offset, &stride, x, y, backing_width,
backing_height, fourcc, modifier, &fd, 1,
if (num_planes > 1) {
error_report("%s: does not support multi-plane dmabuf", __func__);
return;
}
dmabuf = qemu_dmabuf_new(w, h, offset, stride, x, y, backing_width,
backing_height, fourcc, modifier, fd, num_planes,
false, backing_y_0_top);
dbus_scanout_dmabuf(dcl, dmabuf);

View file

@ -283,44 +283,86 @@ err:
return -1;
}
int egl_get_fd_for_texture(uint32_t tex_id, EGLint *stride, EGLint *fourcc,
EGLuint64KHR *modifier)
bool egl_dmabuf_export_texture(uint32_t tex_id, int *fd, EGLint *offset,
EGLint *stride, EGLint *fourcc, int *num_planes,
EGLuint64KHR *modifier)
{
EGLImageKHR image;
EGLint num_planes, fd;
EGLuint64KHR modifiers[DMABUF_MAX_PLANES];
image = eglCreateImageKHR(qemu_egl_display, eglGetCurrentContext(),
EGL_GL_TEXTURE_2D_KHR,
(EGLClientBuffer)(unsigned long)tex_id,
NULL);
if (!image) {
return -1;
return false;
}
eglExportDMABUFImageQueryMESA(qemu_egl_display, image, fourcc,
&num_planes, modifier);
if (num_planes != 1) {
eglDestroyImageKHR(qemu_egl_display, image);
return -1;
}
eglExportDMABUFImageMESA(qemu_egl_display, image, &fd, stride, NULL);
num_planes, modifiers);
eglExportDMABUFImageMESA(qemu_egl_display, image, fd, stride, offset);
eglDestroyImageKHR(qemu_egl_display, image);
return fd;
/* Only first modifier matters. */
if (modifier) {
*modifier = modifiers[0];
}
return true;
}
void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf)
{
EGLImageKHR image = EGL_NO_IMAGE_KHR;
EGLint attrs[64];
int i = 0;
int i = 0, j;
uint64_t modifier = qemu_dmabuf_get_modifier(dmabuf);
uint32_t texture = qemu_dmabuf_get_texture(dmabuf);
int nfds, noffsets, nstrides;
const int *fds = qemu_dmabuf_get_fds(dmabuf, &nfds);
const uint32_t *offsets = qemu_dmabuf_get_offsets(dmabuf, &noffsets);
const uint32_t *strides = qemu_dmabuf_get_strides(dmabuf, &nstrides);
uint32_t num_planes = qemu_dmabuf_get_num_planes(dmabuf);
EGLint fd_attrs[] = {
EGL_DMA_BUF_PLANE0_FD_EXT,
EGL_DMA_BUF_PLANE1_FD_EXT,
EGL_DMA_BUF_PLANE2_FD_EXT,
EGL_DMA_BUF_PLANE3_FD_EXT,
};
EGLint offset_attrs[] = {
EGL_DMA_BUF_PLANE0_OFFSET_EXT,
EGL_DMA_BUF_PLANE1_OFFSET_EXT,
EGL_DMA_BUF_PLANE2_OFFSET_EXT,
EGL_DMA_BUF_PLANE3_OFFSET_EXT,
};
EGLint stride_attrs[] = {
EGL_DMA_BUF_PLANE0_PITCH_EXT,
EGL_DMA_BUF_PLANE1_PITCH_EXT,
EGL_DMA_BUF_PLANE2_PITCH_EXT,
EGL_DMA_BUF_PLANE3_PITCH_EXT,
};
EGLint modifier_lo_attrs[] = {
EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT,
EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT,
EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT,
EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT,
};
EGLint modifier_hi_attrs[] = {
EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT,
EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT,
EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT,
EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT,
};
if (texture != 0) {
return;
}
assert(nfds >= num_planes);
assert(noffsets >= num_planes);
assert(nstrides >= num_planes);
attrs[i++] = EGL_WIDTH;
attrs[i++] = qemu_dmabuf_get_backing_width(dmabuf);
attrs[i++] = EGL_HEIGHT;
@ -328,18 +370,22 @@ void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf)
attrs[i++] = EGL_LINUX_DRM_FOURCC_EXT;
attrs[i++] = qemu_dmabuf_get_fourcc(dmabuf);
attrs[i++] = EGL_DMA_BUF_PLANE0_FD_EXT;
attrs[i++] = qemu_dmabuf_get_fds(dmabuf, NULL)[0];
attrs[i++] = EGL_DMA_BUF_PLANE0_PITCH_EXT;
attrs[i++] = qemu_dmabuf_get_strides(dmabuf, NULL)[0];
attrs[i++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT;
attrs[i++] = 0;
if (modifier != DRM_FORMAT_MOD_INVALID) {
attrs[i++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT;
attrs[i++] = (modifier >> 0) & 0xffffffff;
attrs[i++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT;
attrs[i++] = (modifier >> 32) & 0xffffffff;
for (j = 0; j < num_planes; j++) {
attrs[i++] = fd_attrs[j];
/* fd[1-3] may be -1 if using a joint buffer for all planes */
attrs[i++] = fds[j] >= 0 ? fds[j] : fds[0];
attrs[i++] = stride_attrs[j];
attrs[i++] = strides[j];
attrs[i++] = offset_attrs[j];
attrs[i++] = offsets[j];
if (modifier != DRM_FORMAT_MOD_INVALID) {
attrs[i++] = modifier_lo_attrs[j];
attrs[i++] = (modifier >> 0) & 0xffffffff;
attrs[i++] = modifier_hi_attrs[j];
attrs[i++] = (modifier >> 32) & 0xffffffff;
}
}
attrs[i++] = EGL_NONE;
image = eglCreateImageKHR(qemu_egl_display,

View file

@ -876,19 +876,24 @@ static void spice_gl_switch(DisplayChangeListener *dcl,
struct DisplaySurface *new_surface)
{
SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
EGLint stride, fourcc;
int fd;
if (ssd->ds) {
surface_gl_destroy_texture(ssd->gls, ssd->ds);
}
ssd->ds = new_surface;
if (ssd->ds) {
uint32_t offset[DMABUF_MAX_PLANES], stride[DMABUF_MAX_PLANES];
int fd[DMABUF_MAX_PLANES], num_planes, fourcc;
surface_gl_create_texture(ssd->gls, ssd->ds);
fd = egl_get_fd_for_texture(ssd->ds->texture,
&stride, &fourcc,
NULL);
if (fd < 0) {
if (!egl_dmabuf_export_texture(ssd->ds->texture, fd, (EGLint *)offset,
(EGLint *)stride, &fourcc, &num_planes, NULL)) {
surface_gl_destroy_texture(ssd->gls, ssd->ds);
return;
}
if (num_planes > 1) {
fprintf(stderr, "%s: does not support multi-plane texture\n", __func__);
surface_gl_destroy_texture(ssd->gls, ssd->ds);
return;
}
@ -899,10 +904,10 @@ static void spice_gl_switch(DisplayChangeListener *dcl,
fourcc);
/* note: spice server will close the fd */
spice_qxl_gl_scanout(&ssd->qxl, fd,
spice_qxl_gl_scanout(&ssd->qxl, fd[0],
surface_width(ssd->ds),
surface_height(ssd->ds),
stride, fourcc, false);
stride[0], fourcc, false);
ssd->have_surface = true;
ssd->have_scanout = false;
@ -941,20 +946,24 @@ static void qemu_spice_gl_scanout_texture(DisplayChangeListener *dcl,
void *d3d_tex2d)
{
SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
EGLint stride = 0, fourcc = 0;
int fd = -1;
EGLint offset[DMABUF_MAX_PLANES], stride[DMABUF_MAX_PLANES], fourcc = 0;
int fd[DMABUF_MAX_PLANES], num_planes;
assert(tex_id);
fd = egl_get_fd_for_texture(tex_id, &stride, &fourcc, NULL);
if (fd < 0) {
fprintf(stderr, "%s: failed to get fd for texture\n", __func__);
if (!egl_dmabuf_export_texture(tex_id, fd, offset, stride, &fourcc,
&num_planes, NULL)) {
fprintf(stderr, "%s: failed to export dmabuf for texture\n", __func__);
return;
}
if (num_planes > 1) {
fprintf(stderr, "%s: does not support multi-plane dmabuf\n", __func__);
return;
}
trace_qemu_spice_gl_scanout_texture(ssd->qxl.id, w, h, fourcc);
/* note: spice server will close the fd */
spice_qxl_gl_scanout(&ssd->qxl, fd, backing_width, backing_height,
stride, fourcc, y_0_top);
spice_qxl_gl_scanout(&ssd->qxl, fd[0], backing_width, backing_height,
stride[0], fourcc, y_0_top);
qemu_spice_gl_monitor_config(ssd, x, y, w, h);
ssd->have_surface = false;
ssd->have_scanout = true;
@ -1064,15 +1073,28 @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl,
/* dest framebuffer */
if (ssd->blit_fb.width != width ||
ssd->blit_fb.height != height) {
int fds[DMABUF_MAX_PLANES], num_planes;
uint32_t offsets[DMABUF_MAX_PLANES], strides[DMABUF_MAX_PLANES];
trace_qemu_spice_gl_render_dmabuf(ssd->qxl.id, width,
height);
egl_fb_destroy(&ssd->blit_fb);
egl_fb_setup_new_tex(&ssd->blit_fb,
width, height);
fd = egl_get_fd_for_texture(ssd->blit_fb.texture,
&stride, &fourcc, NULL);
spice_qxl_gl_scanout(&ssd->qxl, fd, width, height,
stride, fourcc, false);
if (!egl_dmabuf_export_texture(ssd->blit_fb.texture, fds,
(EGLint *)offsets, (EGLint *)strides,
&fourcc, &num_planes, NULL)) {
fprintf(stderr,
"%s: failed to export dmabuf for texture\n", __func__);
return;
}
if (num_planes > 1) {
fprintf(stderr,
"%s: does not support multi-plane dmabuf\n", __func__);
return;
}
spice_qxl_gl_scanout(&ssd->qxl, fds[0], width, height,
strides[0], fourcc, false);
}
} else {
stride = qemu_dmabuf_get_strides(dmabuf, NULL)[0];