diff --git a/client/renderers/OpenGL/opengl.c b/client/renderers/OpenGL/opengl.c index 63aab03a..9cd898a6 100644 --- a/client/renderers/OpenGL/opengl.c +++ b/client/renderers/OpenGL/opengl.c @@ -100,6 +100,13 @@ struct OpenGL_Options bool amdPinnedMem; }; +struct OpenGL_FrameRelease +{ + LG_FrameReleaseFn fn; + void * opaque; + uint64_t handle; +}; + struct Inst { LG_Renderer base; @@ -127,6 +134,7 @@ struct Inst float scaleX, scaleY; const FrameBuffer * frame; LG_RendererFrameToken pendingFrameToken; + struct OpenGL_FrameRelease frameRelease; uint64_t drawStart; bool hasBuffers; @@ -179,6 +187,7 @@ enum ConfigStatus static void deconfigure(struct Inst * this); static enum ConfigStatus configure(struct Inst * this); +static void releasePendingFrame(struct Inst * this); static void updateMouseShape(struct Inst * this); static bool drawFrame(struct Inst * this, LG_RendererFrameToken frameTokenLimit, @@ -203,6 +212,33 @@ static bool swSurfaceEnsureBuffer(struct Inst * this, size_t size) return true; } +static struct OpenGL_FrameRelease takePendingFrameLocked(struct Inst * this) +{ + const struct OpenGL_FrameRelease release = this->frameRelease; + + this->frame = NULL; + this->pendingFrameToken = LG_RENDERER_FRAME_TOKEN_NONE; + this->frameRelease = (struct OpenGL_FrameRelease) {}; + atomic_store_explicit(&this->frameUpdate, false, memory_order_release); + return release; +} + +static void invokeFrameRelease(const struct OpenGL_FrameRelease release) +{ + if (release.fn) + release.fn(release.opaque, release.handle); +} + +static void releasePendingFrame(struct Inst * this) +{ + LG_LOCK(this->frameLock); + const struct OpenGL_FrameRelease release = + takePendingFrameLocked(this); + LG_UNLOCK(this->frameLock); + + invokeFrameRelease(release); +} + const char * opengl_getName(void) { return "OpenGL"; @@ -252,6 +288,8 @@ void opengl_deinitialize(LG_Renderer * renderer) { struct Inst * this = UPCAST(struct Inst, renderer); + releasePendingFrame(this); + if (this->renderStarted) { ImGui_ImplOpenGL2_Shutdown(); @@ -290,7 +328,8 @@ void opengl_deinitialize(LG_Renderer * renderer) void opengl_onRestart(LG_Renderer * renderer) { -// struct Inst * this = UPCAST(struct Inst, renderer); + struct Inst * this = UPCAST(struct Inst, renderer); + releasePendingFrame(this); } static void setupModelView(struct Inst * this) @@ -449,20 +488,25 @@ bool opengl_onFrame(LG_Renderer * renderer, const FrameBuffer * frame, int dmaFd { struct Inst * this = UPCAST(struct Inst, renderer); - /* The legacy renderer retains the shared framebuffer until render. It does - * not support asynchronous DMA ownership. */ - if (releaseFn) + /* The legacy renderer supports only deferred CPU reads. */ + if (!frame || dmaFd >= 0) return false; - (void)releaseOpaque; - (void)releaseHandle; - LG_LOCK(this->frameLock); + const struct OpenGL_FrameRelease oldRelease = + takePendingFrameLocked(this); this->frame = frame; this->pendingFrameToken = frameToken; + this->frameRelease = (struct OpenGL_FrameRelease) + { + .fn = releaseFn, + .opaque = releaseOpaque, + .handle = releaseHandle, + }; atomic_store_explicit(&this->frameUpdate, true, memory_order_release); LG_UNLOCK(this->frameLock); + invokeFrameRelease(oldRelease); return true; } @@ -1327,8 +1371,10 @@ static bool drawFrame(struct Inst * this, LG_UNLOCK(this->frameLock); return true; } - atomic_store_explicit(&this->frameUpdate, false, memory_order_release); + const FrameBuffer * frame = this->frame; const LG_RendererFrameToken pendingFrameToken = this->pendingFrameToken; + const struct OpenGL_FrameRelease release = + takePendingFrameLocked(this); LG_LOCK(this->formatLock); glBindTexture(GL_TEXTURE_2D, this->frames[this->texWIndex]); @@ -1340,7 +1386,7 @@ static bool drawFrame(struct Inst * this, this->texPos = 0; framebuffer_read_fn( - this->frame, + frame, this->format.dataHeight, this->format.dataWidth, bpp, @@ -1406,6 +1452,7 @@ static bool drawFrame(struct Inst * this, glFlush(); LG_UNLOCK(this->formatLock); + invokeFrameRelease(release); this->texReady = true; return true; } diff --git a/client/src/main.c b/client/src/main.c index 64ddfb4b..fd6d590d 100644 --- a/client/src/main.c +++ b/client/src/main.c @@ -1622,7 +1622,7 @@ int main_frameThread(void * unused) g_state.frameImportTime = 0; g_state.frameImportWaitTime = 0; - const bool rendererOwnsFrame = frame.dmaFD >= 0 && frame.releaseFn; + const bool rendererOwnsFrame = frame.releaseFn != NULL; if (!RENDERER(onFrame, frame.framebuffer, frame.dmaFD, frame.damageRects, damageCount, frameToken, rendererOwnsFrame ? frame.releaseFn : NULL, @@ -1638,7 +1638,7 @@ int main_frameThread(void * unused) primaryWorkerFailed(); break; } - /* A DMA snapshot can complete on the render thread immediately after it + /* A leased frame can complete on the render thread immediately after it * is signalled below, so sample producer timing while this lease is still * unambiguously owned by the frame thread. */ LG_TransportFrameTiming timing = {};