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 a45b524c575..5caecd4d7b9 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 (*ScanoutTextureCleanup)(ScanoutTextureNative *native); + 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; + ScanoutTextureCleanup 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, + ScanoutTextureCleanup 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 b3e83046643..c01587bd25c 100644 --- a/hw/display/virtio-gpu-virgl.c +++ b/hw/display/virtio-gpu-virgl.c @@ -478,7 +478,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 9378afd53db..df70ccc5535 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); } +typedef struct ScanoutChange { + ScanoutTextureNative native; + ScanoutTextureCleanup cb_cleanup; +} ScanoutChange; + +#define SCANOUT_CHANGE_NONE ((ScanoutChange){ NO_NATIVE_TEXTURE }) + +static ScanoutChange dpy_change_scanout_kind(DisplayScanout *scanout, + enum display_scanout kind) +{ + ScanoutChange 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(ScanoutChange *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; + ScanoutChange 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; + ScanoutChange 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, + ScanoutTextureCleanup cb_cleanup) { DisplayState *s = con->ds; DisplayChangeListener *dcl; + ScanoutChange 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; + ScanoutChange 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, -- 2.50.1 (Apple Git-155)
