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; extern struct gbm_device *qemu_egl_rn_gbm_dev;
int egl_rendernode_init(const char *rendernode, DisplayGLMode mode); int egl_rendernode_init(const char *rendernode, DisplayGLMode mode);
int egl_get_fd_for_texture(uint32_t tex_id, EGLint *stride, EGLint *fourcc, bool egl_dmabuf_export_texture(uint32_t tex_id, int *fd, EGLint *offset,
EGLuint64KHR *modifier); EGLint *stride, EGLint *fourcc, int *num_planes,
EGLuint64KHR *modifier);
void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf); void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf);
void egl_dmabuf_release_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); backing_width, backing_height, x, y, w, h);
#ifdef CONFIG_GBM #ifdef CONFIG_GBM
g_autoptr(QemuDmaBuf) dmabuf = NULL; g_autoptr(QemuDmaBuf) dmabuf = NULL;
int fd; int fd[DMABUF_MAX_PLANES], num_planes;
uint32_t offset = 0, stride, fourcc; uint32_t offset[DMABUF_MAX_PLANES], stride[DMABUF_MAX_PLANES], fourcc;
uint64_t modifier; uint64_t modifier;
assert(tex_id); assert(tex_id);
fd = egl_get_fd_for_texture(tex_id, (EGLint *)&stride, (EGLint *)&fourcc, if (!egl_dmabuf_export_texture(tex_id, fd, (EGLint *)offset, (EGLint *)stride,
&modifier); (EGLint *)&fourcc, &num_planes, &modifier)) {
if (fd < 0) { error_report("%s: failed to export dmabuf for texture", __func__);
error_report("%s: failed to get fd for texture", __func__);
return; return;
} }
dmabuf = qemu_dmabuf_new(w, h, &offset, &stride, x, y, backing_width, if (num_planes > 1) {
backing_height, fourcc, modifier, &fd, 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); false, backing_y_0_top);
dbus_scanout_dmabuf(dcl, dmabuf); dbus_scanout_dmabuf(dcl, dmabuf);

View file

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

View file

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