From 4c9bb5d8444b9f9c51cd6a8563ab1607b3d744f0 Mon Sep 17 00:00:00 2001 From: baldurk Date: Wed, 22 Jan 2020 18:18:18 +0000 Subject: [PATCH] Add special push/pop logic for WGL contexts * We can't easily restore the application's context if the DC that was current has been released. Instead we check to see if the last known window is still valid - if so, we get the DC handle from it. If not then we assume that rendering to the application's context setup would have failed so we bind our own backdoor window (but with the application's context still). --- renderdoc/driver/gl/gl_common.cpp | 6 +++-- renderdoc/driver/gl/gl_common.h | 14 +++++++++++ renderdoc/driver/gl/gl_initstate.cpp | 14 +++++++---- renderdoc/driver/gl/gl_manager.cpp | 10 ++++++-- renderdoc/driver/gl/wgl_platform.cpp | 35 ++++++++++++++++++++++++++++ 5 files changed, 70 insertions(+), 9 deletions(-) diff --git a/renderdoc/driver/gl/gl_common.cpp b/renderdoc/driver/gl/gl_common.cpp index f7c31257d..77b6207e4 100644 --- a/renderdoc/driver/gl/gl_common.cpp +++ b/renderdoc/driver/gl/gl_common.cpp @@ -711,8 +711,10 @@ void DoVendorChecks(GLPlatform &platform, GLWindowingData context) if(child.ctx) { + GLWindowingData saved; + // switch to child - platform.MakeContextCurrent(child); + platform.PushChildContext(context, child, &saved); // these shouldn't be visible VendorCheck[VendorCheck_EXT_fbo_shared] = (GL.glIsFramebuffer(fbo) != GL_FALSE); @@ -724,7 +726,7 @@ void DoVendorChecks(GLPlatform &platform, GLWindowingData context) RDCWARN("VAOs are shared on this implementation"); // switch back to context - platform.MakeContextCurrent(context); + platform.PopChildContext(context, child, saved); platform.DeleteClonedContext(child); } diff --git a/renderdoc/driver/gl/gl_common.h b/renderdoc/driver/gl/gl_common.h index 83f9a5b38..664218fc0 100644 --- a/renderdoc/driver/gl/gl_common.h +++ b/renderdoc/driver/gl/gl_common.h @@ -274,6 +274,20 @@ struct GLPlatform virtual GLWindowingData MakeOutputWindow(WindowingData window, bool depth, GLWindowingData share_context) = 0; + // for pushing and popping a child context. The default implementation just does + // MakeContextCurrent but platforms can override this if they need more complex state handling + virtual bool PushChildContext(GLWindowingData existing, GLWindowingData newChild, + GLWindowingData *saved) + { + bool success = MakeContextCurrent(newChild); + *saved = existing; + return success; + } + virtual void PopChildContext(GLWindowingData existing, GLWindowingData newChild, + GLWindowingData saved) + { + MakeContextCurrent(saved); + } // for 'backwards compatible' overlay rendering virtual void DrawQuads(float width, float height, const rdcarray &vertices) = 0; diff --git a/renderdoc/driver/gl/gl_initstate.cpp b/renderdoc/driver/gl/gl_initstate.cpp index bf00a74d9..1fac198a6 100644 --- a/renderdoc/driver/gl/gl_initstate.cpp +++ b/renderdoc/driver/gl/gl_initstate.cpp @@ -567,7 +567,9 @@ bool GLResourceManager::Prepare_InitialState(GLResource res) ContextShareGroup *shareGroup = (ContextShareGroup *)res.ContextShareGroup; - if(m_Driver->m_Platform.MakeContextCurrent(shareGroup->m_BackDoor)) + GLWindowingData savedContext; + + if(m_Driver->m_Platform.PushChildContext(oldContextData, shareGroup->m_BackDoor, &savedContext)) { m_Driver->m_ActiveContexts[Threading::GetCurrentID()] = shareGroup->m_BackDoor; @@ -575,7 +577,7 @@ bool GLResourceManager::Prepare_InitialState(GLResource res) // restore the context m_Driver->m_ActiveContexts[Threading::GetCurrentID()] = oldContextData; - m_Driver->m_Platform.MakeContextCurrent(oldContextData); + m_Driver->m_Platform.PopChildContext(oldContextData, shareGroup->m_BackDoor, savedContext); } } else @@ -1807,7 +1809,7 @@ bool GLResourceManager::Serialise_InitialState(WriteSerialiser &ser, ResourceId GLResourceRecord *record, const GLInitialContents *initial) { - GLResource res = GetCurrentResource(id); + GLResource res = record->Resource; if(IsResourceTrackedForPersistency(res)) { @@ -1815,7 +1817,9 @@ bool GLResourceManager::Serialise_InitialState(WriteSerialiser &ser, ResourceId GLWindowingData backdoor = ((ContextShareGroup *)res.ContextShareGroup)->m_BackDoor; - if(m_Driver->m_Platform.MakeContextCurrent(backdoor)) + GLWindowingData savedContext; + + if(m_Driver->m_Platform.PushChildContext(oldContextData, backdoor, &savedContext)) { m_Driver->m_ActiveContexts[Threading::GetCurrentID()] = backdoor; @@ -1824,7 +1828,7 @@ bool GLResourceManager::Serialise_InitialState(WriteSerialiser &ser, ResourceId // restore the context m_Driver->m_ActiveContexts[Threading::GetCurrentID()] = oldContextData; - m_Driver->m_Platform.MakeContextCurrent(oldContextData); + m_Driver->m_Platform.PopChildContext(oldContextData, backdoor, savedContext); return success; } diff --git a/renderdoc/driver/gl/gl_manager.cpp b/renderdoc/driver/gl/gl_manager.cpp index be6a025bb..ee95ab6bd 100644 --- a/renderdoc/driver/gl/gl_manager.cpp +++ b/renderdoc/driver/gl/gl_manager.cpp @@ -145,12 +145,18 @@ bool GLResourceManager::ResourceTypeRelease(GLResource res) { ContextShareGroup *contextShareGroup = (ContextShareGroup *)res.ContextShareGroup; - if(m_Driver->m_Platform.MakeContextCurrent(contextShareGroup->m_BackDoor)) + GLWindowingData oldContextData = m_Driver->m_ActiveContexts[Threading::GetCurrentID()]; + + GLWindowingData savedContext; + + if(m_Driver->m_Platform.PushChildContext(oldContextData, contextShareGroup->m_BackDoor, + &savedContext)) { m_Driver->ReleaseResource(res); // restore the context - m_Driver->m_Platform.MakeContextCurrent(m_Driver->m_ActiveContexts[Threading::GetCurrentID()]); + m_Driver->m_Platform.PopChildContext(oldContextData, contextShareGroup->m_BackDoor, + savedContext); } else { diff --git a/renderdoc/driver/gl/wgl_platform.cpp b/renderdoc/driver/gl/wgl_platform.cpp index 885f01411..2aa6e9cd1 100644 --- a/renderdoc/driver/gl/wgl_platform.cpp +++ b/renderdoc/driver/gl/wgl_platform.cpp @@ -37,6 +37,41 @@ class WGLPlatform : public GLPlatform return false; } + // pushing/popping contexts is complex on windows due to the really awful rules for DC lifetimes. + // Changing to the child context is easy, but getting back to where we started is hard. The + // 'current' DC may no longer be valid, as it may have been released in the meantime (while + // rendering is still A-OK). We check that the window behind the DC is valid, stored the 'wnd' + // member. If that window is valid then we use GetDC() to get a temporary DC and bind that, + // assuming all will be well. If the window isn't valid then we can't get a valid DC so we can't + // rebind the context exactly as it was, however we assume that rendering to that setup was broken + // (because the bound DC wasn't pointing to a valid window) so instead we just bind the old + // context but with our DC. + + virtual bool PushChildContext(GLWindowingData existing, GLWindowingData newChild, + GLWindowingData *saved) + { + bool success = MakeContextCurrent(newChild); + *saved = existing; + if(::IsWindow(existing.wnd)) + { + saved->DC = GetDC(existing.wnd); + } + else + { + saved->wnd = newChild.wnd; + saved->DC = newChild.DC; + } + + return success; + } + virtual void PopChildContext(GLWindowingData existing, GLWindowingData newChild, + GLWindowingData saved) + { + MakeContextCurrent(saved); + // release the DC now, if we didn't use our own because theirs was invalid + if(saved.DC != newChild.DC) + ::ReleaseDC(saved.wnd, saved.DC); + } GLWindowingData CloneTemporaryContext(GLWindowingData share) { GLWindowingData ret = share;