On 2025/12/03 13:07, Joelle van Dyne wrote:
Before we introduce changes that allow for QemuConsole to take ownership
of a texture handle, we need scaffolding that will allow us to callback
into a cleanup function any time the ScanoutTexture becomes invalid, which
is whenever the `scanout.kind` or `scanout.texture` gets updated.
The ordering is important: we need to first update the DisplayScanout,
then we need to notify all the listeners, and once all the listeners have
had the chance to finish using the previous native texture, we are safe to
call the cleanup function. This means we need to hold on to the previous
scanout native handle locally until all listeners are notified.
Signed-off-by: Joelle van Dyne <[email protected]>
---
include/ui/console.h | 9 +++++-
hw/display/virtio-gpu-virgl.c | 2 +-
ui/console.c | 56 +++++++++++++++++++++++++++++++----
3 files changed, 59 insertions(+), 8 deletions(-)
diff --git a/include/ui/console.h b/include/ui/console.h
index a45b524c57..df9c083a16 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -149,6 +149,11 @@ typedef struct ScanoutTextureNative {
.type = SCANOUT_TEXTURE_NATIVE_TYPE_NONE \
})
+/**
+ * Cleanup callback function when ScanoutTexture is about to be destroyed
+ */
+typedef void (*dpy_cleanup_texture)(ScanoutTextureNative *native);
Please name this typedef CamelCase as like others.
+
typedef struct ScanoutTexture {
uint32_t backing_id;
bool backing_y_0_top;
@@ -159,6 +164,7 @@ typedef struct ScanoutTexture {
uint32_t width;
uint32_t height;
ScanoutTextureNative native;
+ dpy_cleanup_texture cb_cleanup;
} ScanoutTexture;
typedef struct QemuUIInfo {
@@ -347,7 +353,8 @@ void dpy_gl_scanout_texture(QemuConsole *con,
uint32_t backing_id, bool backing_y_0_top,
uint32_t backing_width, uint32_t backing_height,
uint32_t x, uint32_t y, uint32_t w, uint32_t h,
- ScanoutTextureNative native);
+ ScanoutTextureNative native,
+ dpy_cleanup_texture cb_cleanup);
void dpy_gl_scanout_dmabuf(QemuConsole *con,
QemuDmaBuf *dmabuf);
void dpy_gl_cursor_dmabuf(QemuConsole *con, QemuDmaBuf *dmabuf,
diff --git a/hw/display/virtio-gpu-virgl.c b/hw/display/virtio-gpu-virgl.c
index e091eb0c76..b7bc095676 100644
--- a/hw/display/virtio-gpu-virgl.c
+++ b/hw/display/virtio-gpu-virgl.c
@@ -492,7 +492,7 @@ static void virgl_cmd_set_scanout(VirtIOGPU *g,
info.flags & VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP,
info.width, info.height,
ss.r.x, ss.r.y, ss.r.width, ss.r.height,
- native);
+ native, NULL);
} else {
dpy_gfx_replace_surface(
g->parent_obj.scanout[ss.scanout_id].con, NULL);
diff --git a/ui/console.c b/ui/console.c
index 9378afd53d..8271c36586 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -808,6 +808,41 @@ void dpy_gfx_update_full(QemuConsole *con)
dpy_gfx_update(con, 0, 0, w, h);
}
+struct scanout_change {
+ ScanoutTextureNative native;
+ dpy_cleanup_texture cb_cleanup;
+};
Struct should have typedef according to: docs/devel/style.rst
Regards,
Akihiko Odaki
+
+#define SCANOUT_CHANGE_NONE ((struct scanout_change){ NO_NATIVE_TEXTURE })
+
+static struct scanout_change dpy_change_scanout_kind(DisplayScanout *scanout,
+ enum display_scanout kind)
+{
+ struct scanout_change change = SCANOUT_CHANGE_NONE;
+
+ /**
+ * We cannot cleanup until the resource is no longer in use, so we record
it
+ * You MUST call dpy_complete_scanout_change after all listeners are
updated
+ */
+ if (scanout->kind == SCANOUT_TEXTURE && scanout->texture.cb_cleanup) {
+ change.native = scanout->texture.native;
+ change.cb_cleanup = scanout->texture.cb_cleanup;
+ }
+ scanout->kind = kind;
+
+ return change;
+}
+
+static void dpy_complete_scanout_change(struct scanout_change *change)
+{
+ /**
+ * If we previously have a texture and cleanup is required, we call it now
+ */
+ if (change->native.type != SCANOUT_TEXTURE_NATIVE_TYPE_NONE &&
change->cb_cleanup) {
+ change->cb_cleanup(&change->native);
+ }
+}
+
void dpy_gfx_replace_surface(QemuConsole *con,
DisplaySurface *surface)
{
@@ -818,6 +853,7 @@ void dpy_gfx_replace_surface(QemuConsole *con,
DisplayChangeListener *dcl;
int width;
int height;
+ struct scanout_change change = SCANOUT_CHANGE_NONE;
if (!surface) {
if (old_surface) {
@@ -833,7 +869,7 @@ void dpy_gfx_replace_surface(QemuConsole *con,
assert(old_surface != new_surface);
- con->scanout.kind = SCANOUT_SURFACE;
+ change = dpy_change_scanout_kind(&con->scanout, SCANOUT_SURFACE);
con->surface = new_surface;
dpy_gfx_create_texture(con, new_surface);
QLIST_FOREACH(dcl, &s->listeners, next) {
@@ -844,6 +880,7 @@ void dpy_gfx_replace_surface(QemuConsole *con,
}
dpy_gfx_destroy_texture(con, old_surface);
qemu_free_displaysurface(old_surface);
+ dpy_complete_scanout_change(&change);
}
bool dpy_gfx_check_format(QemuConsole *con,
@@ -1002,9 +1039,10 @@ void dpy_gl_scanout_disable(QemuConsole *con)
{
DisplayState *s = con->ds;
DisplayChangeListener *dcl;
+ struct scanout_change change = SCANOUT_CHANGE_NONE;
if (con->scanout.kind != SCANOUT_SURFACE) {
- con->scanout.kind = SCANOUT_NONE;
+ change = dpy_change_scanout_kind(&con->scanout, SCANOUT_NONE);
}
QLIST_FOREACH(dcl, &s->listeners, next) {
if (con != dcl->con) {
@@ -1014,6 +1052,7 @@ void dpy_gl_scanout_disable(QemuConsole *con)
dcl->ops->dpy_gl_scanout_disable(dcl);
}
}
+ dpy_complete_scanout_change(&change);
}
void dpy_gl_scanout_texture(QemuConsole *con,
@@ -1023,15 +1062,17 @@ void dpy_gl_scanout_texture(QemuConsole *con,
uint32_t backing_height,
uint32_t x, uint32_t y,
uint32_t width, uint32_t height,
- ScanoutTextureNative native)
+ ScanoutTextureNative native,
+ dpy_cleanup_texture cb_cleanup)
{
DisplayState *s = con->ds;
DisplayChangeListener *dcl;
+ struct scanout_change change = SCANOUT_CHANGE_NONE;
- con->scanout.kind = SCANOUT_TEXTURE;
+ change = dpy_change_scanout_kind(&con->scanout, SCANOUT_TEXTURE);
con->scanout.texture = (ScanoutTexture) {
backing_id, backing_y_0_top, backing_width, backing_height,
- x, y, width, height, native,
+ x, y, width, height, native, cb_cleanup
};
QLIST_FOREACH(dcl, &s->listeners, next) {
if (con != dcl->con) {
@@ -1045,6 +1086,7 @@ void dpy_gl_scanout_texture(QemuConsole *con,
native);
}
}
+ dpy_complete_scanout_change(&change);
}
void dpy_gl_scanout_dmabuf(QemuConsole *con,
@@ -1052,8 +1094,9 @@ void dpy_gl_scanout_dmabuf(QemuConsole *con,
{
DisplayState *s = con->ds;
DisplayChangeListener *dcl;
+ struct scanout_change change = SCANOUT_CHANGE_NONE;
- con->scanout.kind = SCANOUT_DMABUF;
+ change = dpy_change_scanout_kind(&con->scanout, SCANOUT_DMABUF);
con->scanout.dmabuf = dmabuf;
QLIST_FOREACH(dcl, &s->listeners, next) {
if (con != dcl->con) {
@@ -1063,6 +1106,7 @@ void dpy_gl_scanout_dmabuf(QemuConsole *con,
dcl->ops->dpy_gl_scanout_dmabuf(dcl, dmabuf);
}
}
+ dpy_complete_scanout_change(&change);
}
void dpy_gl_cursor_dmabuf(QemuConsole *con, QemuDmaBuf *dmabuf,