From a461df2b583f11beb5b67f246b0eef623faa4726 Mon Sep 17 00:00:00 2001 From: baldurk Date: Fri, 4 May 2018 16:37:20 +0100 Subject: [PATCH] GL non-sharing context support --- docs/behind_scenes/opengl_support.rst | 5 +- renderdoc/driver/gl/gl_common.h | 2 + renderdoc/driver/gl/gl_driver.cpp | 250 ++++++++++++++---- renderdoc/driver/gl/gl_driver.h | 28 +- renderdoc/driver/gl/gl_initstate.cpp | 61 +++-- renderdoc/driver/gl/gl_manager.cpp | 20 +- renderdoc/driver/gl/gl_manager.h | 6 +- renderdoc/driver/gl/gl_outputwindow.cpp | 3 + renderdoc/driver/gl/gl_overlay.cpp | 4 +- renderdoc/driver/gl/gl_postvs.cpp | 8 +- renderdoc/driver/gl/gl_renderstate.cpp | 50 ++-- renderdoc/driver/gl/gl_rendertext.cpp | 2 +- renderdoc/driver/gl/gl_replay.cpp | 6 +- renderdoc/driver/gl/gl_replay_egl.cpp | 3 +- renderdoc/driver/gl/gl_replay_linux.cpp | 5 +- renderdoc/driver/gl/gl_replay_win32.cpp | 5 +- renderdoc/driver/gl/gl_resources.h | 77 +++--- renderdoc/driver/gl/gl_stringise.cpp | 1 + .../driver/gl/wrappers/gl_buffer_funcs.cpp | 45 ---- .../driver/gl/wrappers/gl_draw_funcs.cpp | 12 - .../gl/wrappers/gl_framebuffer_funcs.cpp | 69 +---- 21 files changed, 372 insertions(+), 290 deletions(-) diff --git a/docs/behind_scenes/opengl_support.rst b/docs/behind_scenes/opengl_support.rst index 75328b547..763d42113 100644 --- a/docs/behind_scenes/opengl_support.rst +++ b/docs/behind_scenes/opengl_support.rst @@ -12,7 +12,7 @@ RenderDoc only supports the core profile of OpenGL - from 3.2 up to 4.5 inclusiv .. note:: - that to be more compatible with applications, RenderDoc will still attempt to capture on a compatibility context, but it will not replay successfully unless the given subset of functionality is used. + To be more compatible with applications, RenderDoc will still attempt to capture on a compatibility context, but it will not replay successfully unless the given subset of functionality is used. On OpenGL ES, any context version 2.0 and above is supported. @@ -35,7 +35,7 @@ On OpenGL ES, you must be able to create a GLES 3 context to replay. Multiple contexts & multithreading ---------------------------------- -RenderDoc assumes that all GL commands (with the exception of perhaps a SwapBuffers call) for frames will come from a single thread, and that all contexts are set up to share objects with each other. This means that e.g. if commands come from a second thread during loading, or some time during initialisation, this will be supported only if the second context shares with the primary context. During frame capture all commands are serialised as if they come from a single thread. +RenderDoc assumes that all GL commands (with the exception of perhaps a SwapBuffers call) for frames will come from a single thread. This means that e.g. if commands come from a second thread during loading, or some time during initialisation, this will be supported. However during frame capture all commands are serialised as if they come from a single thread, so interleaved rendering commands from multiple threads will not work. Extension support ----------------- @@ -47,7 +47,6 @@ OpenGL remaining work There are a couple of places where OpenGL is not yet at feature parity with other APIs. -* Full & complete support for multiple threads feeding GL simultaneously, or multiple contexts that don't share with each other (or only share within defined groups). * Shader debugging is not supported on any shader stage. * Pixel history is not implemented. diff --git a/renderdoc/driver/gl/gl_common.h b/renderdoc/driver/gl/gl_common.h index e932ac698..cba70a571 100644 --- a/renderdoc/driver/gl/gl_common.h +++ b/renderdoc/driver/gl/gl_common.h @@ -1758,6 +1758,8 @@ enum class GLChunk : uint32_t glIndirectSubCommand, + glContextInit, + Max, }; diff --git a/renderdoc/driver/gl/gl_driver.cpp b/renderdoc/driver/gl/gl_driver.cpp index bd0712b5f..ca9fd7a14 100644 --- a/renderdoc/driver/gl/gl_driver.cpp +++ b/renderdoc/driver/gl/gl_driver.cpp @@ -519,6 +519,8 @@ WrappedOpenGL::WrappedOpenGL(const GLHookSet &funcs, GLPlatform &platform) m_DrawcallStack.push_back(&m_ParentDrawcall); + m_ShareGroupID = UINTPTR_MAX - 2000; + m_CurEventID = 0; m_CurDrawcallID = 0; m_FirstEventID = 0; @@ -566,15 +568,9 @@ WrappedOpenGL::WrappedOpenGL(const GLHookSet &funcs, GLPlatform &platform) m_ContextRecord->Length = 0; m_ContextRecord->SpecialResource = true; - // register VAO 0 as a special VAO, so that it can be tracked if the app uses it - // we immediately mark it dirty since the vertex array tracking functions expect a proper VAO - m_FakeVAOID = GetResourceManager()->RegisterResource(VertexArrayRes(NULL, 0)); - GetResourceManager()->AddResourceRecord(m_FakeVAOID); - GetResourceManager()->MarkDirtyResource(m_FakeVAOID); - - // also register an ID for the backbuffer, so we can identify it properly. - m_FBO0_ID = GetResourceManager()->RegisterResource(FramebufferRes(NULL, 0)); - GetResourceManager()->AddResourceRecord(m_FBO0_ID); + // we register an ID for the backbuffer, this will be tied to the fake-created backbuffer on + // replay, and every context's FBO 0 will be pointed to it with ReplaceResource + m_FBO0_ID = ResourceIDGen::GetNewUniqueID(); } else { @@ -589,11 +585,11 @@ WrappedOpenGL::WrappedOpenGL(const GLHookSet &funcs, GLPlatform &platform) m_FakeBB_FBO = 0; m_FakeBB_Color = 0; m_FakeBB_DepthStencil = 0; - m_FakeVAO = 0; - m_FakeIdxSize = 0; m_CurChunkOffset = 0; m_AddedDrawcall = false; + + m_CurCtxPairTLS = Threading::AllocateTLSSlot(); } void WrappedOpenGL::Initialise(GLInitParams ¶ms, uint64_t sectionVersion) @@ -604,12 +600,6 @@ void WrappedOpenGL::Initialise(GLInitParams ¶ms, uint64_t sectionVersion) m_InitParams = params; m_SectionVersion = sectionVersion; - // as a concession to compatibility, generate a 'fake' VBO to act as VBO 0. - // consider making it an error/warning for programs to use this? - gl.glGenVertexArrays(1, &m_FakeVAO); - gl.glBindVertexArray(m_FakeVAO); - gl.glBindVertexArray(0); - gl.glGenFramebuffers(1, &m_FakeBB_FBO); gl.glBindFramebuffer(eGL_FRAMEBUFFER, m_FakeBB_FBO); @@ -749,8 +739,6 @@ std::string WrappedOpenGL::GetChunkName(uint32_t idx) WrappedOpenGL::~WrappedOpenGL() { - if(m_FakeVAO) - m_Real.glDeleteVertexArrays(1, &m_FakeVAO); if(m_FakeBB_FBO) m_Real.glDeleteFramebuffers(1, &m_FakeBB_FBO); if(m_FakeBB_Color) @@ -785,14 +773,17 @@ WrappedOpenGL::~WrappedOpenGL() RenderDoc::Inst().GetCrashHandler()->UnregisterMemoryRegion(this); } -void *WrappedOpenGL::GetCtx() +ContextPair &WrappedOpenGL::GetCtx() { - return (void *)m_ActiveContexts[Threading::GetCurrentID()].ctx; + ContextPair *ret = (ContextPair *)Threading::GetTLSValue(m_CurCtxPairTLS); + if(ret) + return *ret; + return m_EmptyPair; } WrappedOpenGL::ContextData &WrappedOpenGL::GetCtxData() { - return m_ContextData[GetCtx()]; + return m_ContextData[GetCtx().ctx]; } // defined in gl__hooks.cpp @@ -808,8 +799,26 @@ void WrappedOpenGL::DeleteContext(void *contextHandle) RenderDoc::Inst().RemoveDeviceFrameCapturer(ctxdata.ctx); + // delete the context GetResourceManager()->DeleteContext(contextHandle); + bool lastInGroup = true; + for(auto it = m_ContextData.begin(); it != m_ContextData.end(); ++it) + { + // if we find another context that's not this one, but is in the same share group, we're note + // the last + if(it->second.shareGroup == ctxdata.shareGroup && it->second.ctx && + it->second.ctx != contextHandle) + { + lastInGroup = false; + break; + } + } + + // if this is the last context in the share group, delete the group. + if(lastInGroup) + GetResourceManager()->DeleteContext(ctxdata.shareGroup); + if(ctxdata.built && ctxdata.ready) { if(ctxdata.Program) @@ -879,16 +888,46 @@ void WrappedOpenGL::CreateContext(GLWindowingData winData, void *shareContext, ctxdata.isCore = core; ctxdata.attribsCreate = attribsCreate; + if(shareContext == NULL) + { + // no sharing, allocate a new group ID + ctxdata.shareGroup = (void *)m_ShareGroupID; + + // we're counting down from UINTPTR_MAX when allocating IDs + m_ShareGroupID--; + } + else + { + // use the same shareGroup ID as the share context. + ctxdata.shareGroup = ShareCtx(shareContext); + } + RenderDoc::Inst().AddDeviceFrameCapturer(ctxdata.ctx, this); } -void WrappedOpenGL::RegisterContext(GLWindowingData winData, void *shareContext, bool core, - bool attribsCreate) +void WrappedOpenGL::RegisterReplayContext(GLWindowingData winData, void *shareContext, bool core, + bool attribsCreate) { ContextData &ctxdata = m_ContextData[winData.ctx]; ctxdata.ctx = winData.ctx; ctxdata.isCore = core; ctxdata.attribsCreate = attribsCreate; + + if(shareContext == NULL) + { + // no sharing, allocate a new group ID + ctxdata.shareGroup = (void *)m_ShareGroupID; + + // we're counting down from UINTPTR_MAX when allocating IDs + m_ShareGroupID--; + } + else + { + // use the same shareGroup ID as the share context. + ctxdata.shareGroup = ShareCtx(shareContext); + } + + ActivateContext(winData); } void WrappedOpenGL::ActivateContext(GLWindowingData winData) @@ -911,6 +950,23 @@ void WrappedOpenGL::ActivateContext(GLWindowingData winData) m_LastContexts.erase(m_LastContexts.begin()); } + // update thread-local context pair + { + ContextPair *ctx = (ContextPair *)Threading::GetTLSValue(m_CurCtxPairTLS); + + if(ctx) + { + *ctx = {winData.ctx, ShareCtx(winData.ctx)}; + } + else + { + ctx = new ContextPair({winData.ctx, ShareCtx(winData.ctx)}); + m_CtxPairs.push_back(ctx); + + Threading::SetTLSValue(m_CurCtxPairTLS, ctx); + } + } + // TODO: support multiple GL contexts more explicitly Keyboard::AddInputWindow((void *)winData.wnd); @@ -920,24 +976,30 @@ void WrappedOpenGL::ActivateContext(GLWindowingData winData) if(IsActiveCapturing(m_State)) { // fetch any initial states needed. Note this is insufficient, and doesn't handle the case - // where - // we might just suddenly start getting commands on a thread that already has a context - // active. - // For now we assume we'll only get GL commands from a single thread - QueuedInitialStateFetch fetch; - fetch.res.Context = winData.ctx; - size_t before = m_QueuedInitialFetches.size(); - auto it = std::lower_bound(m_QueuedInitialFetches.begin(), m_QueuedInitialFetches.end(), fetch); - for(; it != m_QueuedInitialFetches.end() && it->res.Context == winData.ctx;) + // where we might just suddenly start getting commands on a thread that already has a context + // active. For now we assume we'll only get GL commands from a single thread + // + // First we process any queued fetches from the context itself (i.e. non-shared resources), + // then from the context's share group. + for(void *ctx : {(void *)winData.ctx, ShareCtx(winData.ctx)}) { - GetResourceManager()->ContextPrepare_InitialState(it->res); - it = m_QueuedInitialFetches.erase(it); - } - size_t after = m_QueuedInitialFetches.size(); + QueuedInitialStateFetch fetch; + fetch.res.ContextShareGroup = ctx; + size_t before = m_QueuedInitialFetches.size(); + auto it = + std::lower_bound(m_QueuedInitialFetches.begin(), m_QueuedInitialFetches.end(), fetch); + for(; it != m_QueuedInitialFetches.end() && it->res.ContextShareGroup == ctx;) + { + GetResourceManager()->ContextPrepare_InitialState(it->res); + it = m_QueuedInitialFetches.erase(it); + } + size_t after = m_QueuedInitialFetches.size(); - (void)before; - (void)after; - RDCDEBUG("Prepared %zu resources on context %p, %zu left", before - after, winData.ctx, after); + (void)before; + (void)after; + RDCDEBUG("Prepared %zu resources on context/sharegroup %p, %zu left", before - after, ctx, + after); + } USE_SCRATCH_SERIALISER(); SCOPED_SERIALISE_CHUNK(GLChunk::MakeContextCurrent); @@ -1057,6 +1119,59 @@ void WrappedOpenGL::ActivateContext(GLWindowingData winData) gl_CurChunk = GLChunk::glGenBuffers; glGenBuffers(1, &ctxdata.m_ClientMemoryIBO); } + + if(IsCaptureMode(m_State)) + { + // check if we already have VAO 0 registered for this context. This could be possible if + // VAOs are shared and a previous context in the share group created it. + GLResource vao0 = VertexArrayRes(GetCtx(), 0); + + if(!GetResourceManager()->HasCurrentResource(vao0)) + { + ResourceId id = GetResourceManager()->RegisterResource(vao0); + + GLResourceRecord *record = GetResourceManager()->AddResourceRecord(id); + RDCASSERT(record); + + { + USE_SCRATCH_SERIALISER(); + SCOPED_SERIALISE_CHUNK(GLChunk::glGenVertexArrays); + GLuint zero = 0; + Serialise_glGenVertexArrays(ser, 1, &zero); + + record->AddChunk(scope.Get()); + } + + // give it a name + { + USE_SCRATCH_SERIALISER(); + SCOPED_SERIALISE_CHUNK(GLChunk::glObjectLabel); + Serialise_glObjectLabel(ser, eGL_VERTEX_ARRAY, 0, -1, "Default VAO"); + + record->AddChunk(scope.Get()); + } + + // we immediately mark it dirty since the vertex array tracking functions expect a proper + // VAO + GetResourceManager()->MarkDirtyResource(id); + } + + // we also do the same for FBO 0, but this is treated 'specially' as we actually only create + // one backbuffer and re-point all FBOs at it, however there may be several IDs + // corresponding to the default backbuffer. We rename them all. + GLResource fbo0 = FramebufferRes(GetCtx(), 0); + + if(!GetResourceManager()->HasCurrentResource(fbo0)) + { + ResourceId id = GetResourceManager()->RegisterResource(fbo0); + + USE_SCRATCH_SERIALISER(); + SCOPED_SERIALISE_CHUNK(GLChunk::glContextInit); + Serialise_ContextInit(ser); + + m_DeviceRecord->AddChunk(scope.Get()); + } + } } // this is hack but GL context creation is an *utter mess*. For first-frame captures, only @@ -1312,7 +1427,7 @@ void WrappedOpenGL::SwapBuffers(void *windowHandle) RenderDoc::Inst().Tick(); // don't do anything if no context is active. - if(GetCtx() == NULL) + if(m_ActiveContexts[Threading::GetCurrentID()].ctx == NULL) { m_NoCtxFrames++; if(m_NoCtxFrames == 100) @@ -1581,15 +1696,6 @@ void WrappedOpenGL::StartFrameCapture(void *dev, void *wnd) GetResourceManager()->MarkResourceFrameReferenced(m_DeviceResourceID, eFrameRef_Write); - GLuint prevVAO = 0; - m_Real.glGetIntegerv(eGL_VERTEX_ARRAY_BINDING, (GLint *)&prevVAO); - - m_Real.glBindVertexArray(m_FakeVAO); - - GetResourceManager()->MarkVAOReferenced(VertexArrayRes(NULL, m_FakeVAO), eFrameRef_Write, true); - - m_Real.glBindVertexArray(prevVAO); - GetResourceManager()->PrepareInitialContents(); FreeCaptureData(); @@ -1695,7 +1801,10 @@ bool WrappedOpenGL::EndFrameCapture(void *dev, void *wnd) // remember to update this estimated chunk length if you add more parameters SCOPED_SERIALISE_CHUNK(GLChunk::DeviceInitialisation, 32); - SERIALISE_ELEMENT(m_FakeVAOID); + // legacy behaviour where we had a single global VAO 0. Ignore, but preserve for easier + // compatibility with old captures + ResourceId vao; + SERIALISE_ELEMENT(vao); SERIALISE_ELEMENT(m_FBO0_ID); } @@ -2014,6 +2123,21 @@ bool WrappedOpenGL::Serialise_CaptureScope(SerialiserType &ser) return true; } +template +bool WrappedOpenGL::Serialise_ContextInit(SerialiserType &ser) +{ + SERIALISE_ELEMENT_LOCAL(FBO0_ID, GetResourceManager()->GetID(FramebufferRes(GetCtx(), 0))); + + SERIALISE_CHECK_READ_ERRORS(); + + if(IsReplayingAndReading()) + { + GetResourceManager()->ReplaceResource(FBO0_ID, m_FBO0_ID); + } + + return true; +} + void WrappedOpenGL::ContextEndFrame() { USE_SCRATCH_SERIALISER(); @@ -2118,6 +2242,18 @@ void WrappedOpenGL::BeginCaptureFrame() Serialise_BeginCaptureFrame(ser); m_ContextRecord->AddChunk(scope.Get(), 1); + + // mark VAO 0 on this context as referenced + { + GLuint prevVAO = 0; + m_Real.glGetIntegerv(eGL_VERTEX_ARRAY_BINDING, (GLint *)&prevVAO); + + m_Real.glBindVertexArray(0); + + GetResourceManager()->MarkVAOReferenced(VertexArrayRes(GetCtx(), 0), eFrameRef_Write, true); + + m_Real.glBindVertexArray(prevVAO); + } } void WrappedOpenGL::FinishCapture() @@ -2524,16 +2660,13 @@ bool WrappedOpenGL::ProcessChunk(ReadSerialiser &ser, GLChunk chunk) { case GLChunk::DeviceInitialisation: { - SERIALISE_ELEMENT(m_FakeVAOID).Named("VAO 0 ID"); + // legacy behaviour where we had a single global VAO 0. Ignore + ResourceId vao; + SERIALISE_ELEMENT(vao).Hidden(); SERIALISE_ELEMENT(m_FBO0_ID).Named("FBO 0 ID"); SERIALISE_CHECK_READ_ERRORS(); - GetResourceManager()->AddLiveResource(m_FakeVAOID, VertexArrayRes(NULL, 0)); - - AddResource(m_FakeVAOID, ResourceType::StateObject, ""); - GetReplay()->GetResourceDesc(m_FakeVAOID).SetCustomName("Special VAO 0"); - GetResourceManager()->AddLiveResource(m_FBO0_ID, FramebufferRes(GetCtx(), m_FakeBB_FBO)); AddResource(m_FBO0_ID, ResourceType::SwapchainImage, ""); @@ -2557,6 +2690,7 @@ bool WrappedOpenGL::ProcessChunk(ReadSerialiser &ser, GLChunk chunk) return true; } + case GLChunk::glContextInit: return Serialise_ContextInit(ser); case GLChunk::glGenBuffersARB: case GLChunk::glGenBuffers: return Serialise_glGenBuffers(ser, 0, 0); @@ -4139,7 +4273,7 @@ void WrappedOpenGL::AddUsage(const DrawcallDescription &d) GLResourceManager *rm = GetResourceManager(); - void *ctx = GetCtx(); + ContextPair &ctx = GetCtx(); uint32_t e = d.eventId; diff --git a/renderdoc/driver/gl/gl_driver.h b/renderdoc/driver/gl/gl_driver.h index 4270ec669..50330fb36 100644 --- a/renderdoc/driver/gl/gl_driver.h +++ b/renderdoc/driver/gl/gl_driver.h @@ -130,6 +130,12 @@ private: static std::map m_ActiveContexts; + ContextPair m_EmptyPair; + uint64_t m_CurCtxPairTLS; + std::vector m_CtxPairs; + + uintptr_t m_ShareGroupID; + std::vector m_LastContexts; public: @@ -346,10 +352,6 @@ private: GLuint m_FakeBB_FBO; GLuint m_FakeBB_Color; GLuint m_FakeBB_DepthStencil; - GLuint m_FakeVAO; - GLsizeiptr m_FakeIdxSize; - - ResourceId m_FakeVAOID; ResourceId m_FBO0_ID; uint32_t m_InitChunkIndex = 0; @@ -365,6 +367,9 @@ private: template bool Serialise_CaptureScope(SerialiserType &ser); + template + bool Serialise_ContextInit(SerialiserType &ser); + bool HasSuccessfulCapture(CaptureFailReason &reason) { reason = m_FailureReason; @@ -385,6 +390,7 @@ private: ContextData() { ctx = NULL; + shareGroup = NULL; built = ready = false; attribsCreate = false; @@ -406,6 +412,8 @@ private: void *ctx; + void *shareGroup; + bool built; bool ready; @@ -496,7 +504,10 @@ private: { GLResource res; - bool operator<(const QueuedInitialStateFetch &o) const { return res.Context < o.res.Context; } + bool operator<(const QueuedInitialStateFetch &o) const + { + return res.ContextShareGroup < o.res.ContextShareGroup; + } }; vector m_QueuedInitialFetches; @@ -555,7 +566,8 @@ public: bool isGLESMode() { return m_DriverType == RDCDriver::OpenGLES; } RDCDriver GetDriverType() { return m_DriverType; } GLInitParams &GetInitParams() { return m_InitParams; } - static void *GetCtx(); + ContextPair &GetCtx(); + void *ShareCtx(void *ctx) { return ctx ? m_ContextData[ctx].shareGroup : NULL; } void SetStructuredExport(uint64_t sectionVersion) { m_SectionVersion = sectionVersion; @@ -581,7 +593,6 @@ public: ReplayStatus ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers); GLuint GetFakeBBFBO() { return m_FakeBB_FBO; } - GLuint GetFakeVAO() { return m_FakeVAO; } FrameRecord &GetFrameRecord() { return m_FrameRecord; } const APIEvent &GetEvent(uint32_t eventId); @@ -592,7 +603,8 @@ public: vector GetUsage(ResourceId id) { return m_ResourceUses[id]; } void CreateContext(GLWindowingData winData, void *shareContext, GLInitParams initParams, bool core, bool attribsCreate); - void RegisterContext(GLWindowingData winData, void *shareContext, bool core, bool attribsCreate); + void RegisterReplayContext(GLWindowingData winData, void *shareContext, bool core, + bool attribsCreate); void DeleteContext(void *contextHandle); void ActivateContext(GLWindowingData winData); void WindowSize(void *windowHandle, uint32_t w, uint32_t h); diff --git a/renderdoc/driver/gl/gl_initstate.cpp b/renderdoc/driver/gl/gl_initstate.cpp index 37fdc858f..18d6c68e7 100644 --- a/renderdoc/driver/gl/gl_initstate.cpp +++ b/renderdoc/driver/gl/gl_initstate.cpp @@ -160,6 +160,8 @@ void GLResourceManager::ContextPrepare_InitialState(GLResource res) { FramebufferInitialData &data = initContents.fbo; + ContextPair &ctx = m_GL->GetCtx(); + RDCASSERT(!data.valid); data.valid = true; @@ -201,8 +203,7 @@ void GLResourceManager::ContextPrepare_InitialState(GLResource res) } a.layered = (layered != 0); - a.obj = (type == eGL_RENDERBUFFER) ? RenderbufferRes(res.Context, object) - : TextureRes(res.Context, object); + a.obj = (type == eGL_RENDERBUFFER) ? RenderbufferRes(ctx, object) : TextureRes(ctx, object); if(type != eGL_RENDERBUFFER) { @@ -234,13 +235,14 @@ void GLResourceManager::ContextPrepare_InitialState(GLResource res) RDCASSERT(!data.valid); data.valid = true; -#if ENABLED(RDOC_DEVEL) - if(ProgramRes(m_GL->GetCtx(), 0).Context != NULL) - RDCFATAL("Program resources are context-specific - we assume they aren't"); -#endif + // programs are shared + void *shareGroup = m_GL->GetCtx().shareGroup; for(GLuint i = 0; i < (GLuint)ARRAY_COUNT(data.programs); i++) + { data.programs[i].Namespace = eResProgram; + data.programs[i].ContextShareGroup = shareGroup; + } gl.glGetProgramPipelineiv(res.name, eGL_VERTEX_SHADER, (GLint *)&data.programs[0].name); gl.glGetProgramPipelineiv(res.name, eGL_FRAGMENT_SHADER, (GLint *)&data.programs[4].name); @@ -256,6 +258,8 @@ void GLResourceManager::ContextPrepare_InitialState(GLResource res) RDCASSERT(!data.valid); data.valid = true; + ContextPair &ctx = m_GL->GetCtx(); + GLuint prevfeedback = 0; gl.glGetIntegerv(eGL_TRANSFORM_FEEDBACK_BINDING, (GLint *)&prevfeedback); @@ -268,7 +272,7 @@ void GLResourceManager::ContextPrepare_InitialState(GLResource res) { GLuint buffer = 0; gl.glGetIntegeri_v(eGL_TRANSFORM_FEEDBACK_BUFFER_BINDING, i, (GLint *)&buffer); - data.Buffer[i] = BufferRes(res.Context, buffer); + data.Buffer[i] = BufferRes(ctx, buffer); gl.glGetInteger64i_v(eGL_TRANSFORM_FEEDBACK_BUFFER_START, i, (GLint64 *)&data.Offset[i]); gl.glGetInteger64i_v(eGL_TRANSFORM_FEEDBACK_BUFFER_SIZE, i, (GLint64 *)&data.Size[i]); } @@ -282,13 +286,12 @@ void GLResourceManager::ContextPrepare_InitialState(GLResource res) RDCASSERT(!data.valid); data.valid = true; + ContextPair &ctx = m_GL->GetCtx(); + GLuint prevVAO = 0; gl.glGetIntegerv(eGL_VERTEX_ARRAY_BINDING, (GLint *)&prevVAO); - if(res.name == 0) - gl.glBindVertexArray(m_GL->GetFakeVAO()); - else - gl.glBindVertexArray(res.name); + gl.glBindVertexArray(res.name); for(GLuint i = 0; i < 16; i++) { @@ -306,7 +309,7 @@ void GLResourceManager::ContextPrepare_InitialState(GLResource res) GLuint buffer = GetBoundVertexBuffer(gl, i); - data.VertexBuffers[i].Buffer = BufferRes(res.Context, buffer); + data.VertexBuffers[i].Buffer = BufferRes(ctx, buffer); gl.glGetIntegeri_v(eGL_VERTEX_BINDING_STRIDE, i, (GLint *)&data.VertexBuffers[i].Stride); gl.glGetIntegeri_v(eGL_VERTEX_BINDING_OFFSET, i, (GLint *)&data.VertexBuffers[i].Offset); @@ -315,7 +318,7 @@ void GLResourceManager::ContextPrepare_InitialState(GLResource res) GLuint buffer = 0; gl.glGetIntegerv(eGL_ELEMENT_ARRAY_BUFFER_BINDING, (GLint *)&buffer); - data.ElementArrayBuffer = BufferRes(res.Context, buffer); + data.ElementArrayBuffer = BufferRes(ctx, buffer); gl.glBindVertexArray(prevVAO); } @@ -359,7 +362,8 @@ bool GLResourceManager::Prepare_InitialState(GLResource res) gl.glBindBuffer(eGL_COPY_READ_BUFFER, oldbuf1); gl.glBindBuffer(eGL_COPY_WRITE_BUFFER, oldbuf2); - SetInitialContents(Id, GLInitialContents(BufferRes(res.Context, buf), length)); + SetInitialContents( + Id, GLInitialContents(GLResource(res.ContextShareGroup, eResBuffer, buf), length)); } else if(res.Namespace == eResProgram) { @@ -383,9 +387,11 @@ bool GLResourceManager::Prepare_InitialState(GLResource res) } else if(res.Namespace == eResFramebuffer) { - // if FBOs aren't shared we need to fetch the data for this FBO on the right context. It's - // not safe for us to go changing contexts ourselves (the context could be active on another - // thread), so instead we'll queue this up to fetch when we are on the correct context. + // We need to fetch the data for this FBO on the right context. + // It's not safe for us to go changing contexts ourselves (the context could be active on + // another thread), so instead we'll queue this up to fetch when we are on a correct context. + // The correct context depends on whether the object is shared or not - if it's shared, any + // context in the same share group will do, otherwise it must be precisely the right context // // Because we've already allocated and set the blob above, it can be filled in any time // before serialising (end of the frame, and if the context is never used before the end of @@ -395,7 +401,8 @@ bool GLResourceManager::Prepare_InitialState(GLResource res) // and we just start getting commands there, but that case already isn't supported as we don't // detect it and insert state-change chunks, we assume all commands will come from a single // thread. - if(!VendorCheck[VendorCheck_EXT_fbo_shared] && res.Context && m_GL->GetCtx() != res.Context) + ContextPair &ctx = m_GL->GetCtx(); + if(res.ContextShareGroup != ctx.ctx && res.ContextShareGroup != ctx.shareGroup) { m_GL->QueuePrepareInitialState(res); } @@ -411,7 +418,8 @@ bool GLResourceManager::Prepare_InitialState(GLResource res) { // queue initial state fetching if we're not on the right context, see above in FBOs for more // explanation of this. - if(res.Context && m_GL->GetCtx() != res.Context) + ContextPair &ctx = m_GL->GetCtx(); + if(res.ContextShareGroup != ctx.ctx && res.ContextShareGroup != ctx.shareGroup) { m_GL->QueuePrepareInitialState(res); } @@ -424,7 +432,8 @@ bool GLResourceManager::Prepare_InitialState(GLResource res) { // queue initial state fetching if we're not on the right context, see above in FBOs for more // explanation of this. - if(res.Context && m_GL->GetCtx() != res.Context) + ContextPair &ctx = m_GL->GetCtx(); + if(res.ContextShareGroup != ctx.ctx && res.ContextShareGroup != ctx.shareGroup) { m_GL->QueuePrepareInitialState(res); } @@ -437,7 +446,8 @@ bool GLResourceManager::Prepare_InitialState(GLResource res) { // queue initial state fetching if we're not on the right context, see above in FBOs for more // explanation of this. - if(res.Context && m_GL->GetCtx() != res.Context) + ContextPair &ctx = m_GL->GetCtx(); + if(res.ContextShareGroup != ctx.ctx && res.ContextShareGroup != ctx.shareGroup) { m_GL->QueuePrepareInitialState(res); } @@ -826,7 +836,7 @@ void GLResourceManager::PrepareTextureInitialContents(ResourceId liveid, Resourc (GLint *)&state.maxLevel); } - initContents.resource = TextureRes(res.Context, tex); + initContents.resource = GLResource(res.ContextShareGroup, eResTexture, tex); } else { @@ -835,7 +845,7 @@ void GLResourceManager::PrepareTextureInitialContents(ResourceId liveid, Resourc GLuint bufName = 0; gl.glGetTextureLevelParameterivEXT(res.name, details.curType, 0, eGL_TEXTURE_BUFFER_DATA_STORE_BINDING, (GLint *)&bufName); - state.texBuffer = BufferRes(res.Context, bufName); + state.texBuffer = GLResource(res.ContextShareGroup, eResBuffer, bufName); gl.glGetTextureLevelParameterivEXT(res.name, details.curType, 0, eGL_TEXTURE_BUFFER_OFFSET, (GLint *)&state.texBufOffs); @@ -2079,10 +2089,7 @@ void GLResourceManager::Apply_InitialState(GLResource live, GLInitialContents in GLuint VAO = 0; gl.glGetIntegerv(eGL_VERTEX_ARRAY_BINDING, (GLint *)&VAO); - if(live.name == 0) - gl.glBindVertexArray(m_GL->GetFakeVAO()); - else - gl.glBindVertexArray(live.name); + gl.glBindVertexArray(live.name); for(GLuint i = 0; i < 16; i++) { diff --git a/renderdoc/driver/gl/gl_manager.cpp b/renderdoc/driver/gl/gl_manager.cpp index eb792a636..9b535f46e 100644 --- a/renderdoc/driver/gl/gl_manager.cpp +++ b/renderdoc/driver/gl/gl_manager.cpp @@ -38,6 +38,8 @@ void GLResourceManager::MarkVAOReferenced(GLResource res, FrameRefType ref, bool if(res.name || allowFake0) { + ContextPair &ctx = m_GL->GetCtx(); + MarkResourceFrameReferenced(res, ref == eFrameRef_Unknown ? eFrameRef_Unknown : eFrameRef_Read); GLint numVBufferBindings = 16; @@ -47,12 +49,12 @@ void GLResourceManager::MarkVAOReferenced(GLResource res, FrameRefType ref, bool { GLuint buffer = GetBoundVertexBuffer(gl, i); - MarkResourceFrameReferenced(BufferRes(res.Context, buffer), ref); + MarkResourceFrameReferenced(BufferRes(ctx, buffer), ref); } GLuint ibuffer = 0; gl.glGetIntegerv(eGL_ELEMENT_ARRAY_BUFFER_BINDING, (GLint *)&ibuffer); - MarkResourceFrameReferenced(BufferRes(res.Context, ibuffer), ref); + MarkResourceFrameReferenced(BufferRes(ctx, ibuffer), ref); } } @@ -65,6 +67,8 @@ void GLResourceManager::MarkFBOReferenced(GLResource res, FrameRefType ref) const GLHookSet &gl = m_GL->GetHookset(); + ContextPair &ctx = m_GL->GetCtx(); + GLint numCols = 8; gl.glGetIntegerv(eGL_MAX_COLOR_ATTACHMENTS, &numCols); @@ -81,9 +85,9 @@ void GLResourceManager::MarkFBOReferenced(GLResource res, FrameRefType ref) (GLint *)&type); if(type == eGL_RENDERBUFFER) - MarkResourceFrameReferenced(RenderbufferRes(res.Context, name), ref); + MarkResourceFrameReferenced(RenderbufferRes(ctx, name), ref); else - MarkResourceFrameReferenced(TextureRes(res.Context, name), ref); + MarkResourceFrameReferenced(TextureRes(ctx, name), ref); } gl.glGetNamedFramebufferAttachmentParameterivEXT( @@ -94,9 +98,9 @@ void GLResourceManager::MarkFBOReferenced(GLResource res, FrameRefType ref) if(name) { if(type == eGL_RENDERBUFFER) - MarkResourceFrameReferenced(RenderbufferRes(res.Context, name), ref); + MarkResourceFrameReferenced(RenderbufferRes(ctx, name), ref); else - MarkResourceFrameReferenced(TextureRes(res.Context, name), ref); + MarkResourceFrameReferenced(TextureRes(ctx, name), ref); } gl.glGetNamedFramebufferAttachmentParameterivEXT( @@ -107,9 +111,9 @@ void GLResourceManager::MarkFBOReferenced(GLResource res, FrameRefType ref) if(name) { if(type == eGL_RENDERBUFFER) - MarkResourceFrameReferenced(RenderbufferRes(res.Context, name), ref); + MarkResourceFrameReferenced(RenderbufferRes(ctx, name), ref); else - MarkResourceFrameReferenced(TextureRes(res.Context, name), ref); + MarkResourceFrameReferenced(TextureRes(ctx, name), ref); } } diff --git a/renderdoc/driver/gl/gl_manager.h b/renderdoc/driver/gl/gl_manager.h index 2ca83c133..4e812cae3 100644 --- a/renderdoc/driver/gl/gl_manager.h +++ b/renderdoc/driver/gl/gl_manager.h @@ -106,7 +106,7 @@ public: size_t count = 0; for(auto it = m_CurrentResourceIds.begin(); it != m_CurrentResourceIds.end(); it++) { - if(it->first.Context == context) + if(it->first.ContextShareGroup == context) { ++count; ResourceId res = it->second; @@ -117,7 +117,7 @@ public: it = m_CurrentResourceIds.erase(it); } } - RDCDEBUG("Removed %zu/%zu resources belonging to context %p", count, + RDCDEBUG("Removed %zu/%zu resources belonging to context/sharegroup %p", count, m_CurrentResourceIds.size(), context); } @@ -213,7 +213,7 @@ public: using ResourceManager::MarkCleanResource; void MarkCleanResource(GLResource res) { return ResourceManager::MarkCleanResource(GetID(res)); } - void RegisterSync(void *ctx, GLsync sync, GLuint &name, ResourceId &id) + void RegisterSync(ContextPair &ctx, GLsync sync, GLuint &name, ResourceId &id) { name = (GLuint)Atomic::Inc64(&m_SyncName); id = RegisterResource(SyncRes(ctx, name)); diff --git a/renderdoc/driver/gl/gl_outputwindow.cpp b/renderdoc/driver/gl/gl_outputwindow.cpp index 353a17af5..20e85d1f3 100644 --- a/renderdoc/driver/gl/gl_outputwindow.cpp +++ b/renderdoc/driver/gl/gl_outputwindow.cpp @@ -210,6 +210,9 @@ uint64_t GLReplay::MakeOutputWindow(WindowingData window, bool depth) m_pDriver->m_Platform.GetOutputWindowDimensions(win, win.width, win.height); MakeCurrentReplayContext(&win); + + m_pDriver->RegisterReplayContext(win, m_ReplayCtx.ctx, true, true); + InitOutputWindow(win); CreateOutputWindowBackbuffer(win, depth); diff --git a/renderdoc/driver/gl/gl_overlay.cpp b/renderdoc/driver/gl/gl_overlay.cpp index 543d5786a..0e5fd9ee3 100644 --- a/renderdoc/driver/gl/gl_overlay.cpp +++ b/renderdoc/driver/gl/gl_overlay.cpp @@ -41,7 +41,7 @@ void GLReplay::CreateOverlayProgram(GLuint Program, GLuint Pipeline, GLuint frag { WrappedOpenGL &gl = *m_pDriver; - void *ctx = m_ReplayCtx.ctx; + ContextPair &ctx = gl.GetCtx(); // delete the old program if it exists if(DebugData.overlayProg != 0) @@ -151,7 +151,7 @@ ResourceId GLReplay::RenderOverlay(ResourceId texid, CompType typeHint, DebugOve GLMarkerRegion renderoverlay(StringFormat::Fmt("RenderOverlay %d", overlay)); - void *ctx = m_ReplayCtx.ctx; + ContextPair &ctx = gl.GetCtx(); GLRenderState rs(&gl.GetHookset()); rs.FetchState(&gl); diff --git a/renderdoc/driver/gl/gl_postvs.cpp b/renderdoc/driver/gl/gl_postvs.cpp index 2fbf1abbe..f4cee64da 100644 --- a/renderdoc/driver/gl/gl_postvs.cpp +++ b/renderdoc/driver/gl/gl_postvs.cpp @@ -414,7 +414,7 @@ void GLReplay::InitPostVSBuffers(uint32_t eventId) } else // drawcall is indexed { - ResourceId idxId = rm->GetID(BufferRes(NULL, elArrayBuffer)); + ResourceId idxId = rm->GetID(BufferRes(gl.GetCtx(), elArrayBuffer)); bytebuf idxdata; GetBufferData(idxId, drawcall->indexOffset * drawcall->indexByteWidth, @@ -1422,6 +1422,8 @@ MeshFormat GLReplay::GetPostVSBuffers(uint32_t eventId, uint32_t instID, MeshDat GLPostVSData postvs; RDCEraseEl(postvs); + ContextPair ctx = {m_ReplayCtx.ctx, m_pDriver->ShareCtx(m_ReplayCtx.ctx)}; + if(m_PostVSData.find(eventId) != m_PostVSData.end()) postvs = m_PostVSData[eventId]; @@ -1430,7 +1432,7 @@ MeshFormat GLReplay::GetPostVSBuffers(uint32_t eventId, uint32_t instID, MeshDat MeshFormat ret; if(s.useIndices && s.idxBuf) - ret.indexResourceId = m_pDriver->GetResourceManager()->GetID(BufferRes(NULL, s.idxBuf)); + ret.indexResourceId = m_pDriver->GetResourceManager()->GetID(BufferRes(ctx, s.idxBuf)); else ret.indexResourceId = ResourceId(); ret.indexByteOffset = 0; @@ -1438,7 +1440,7 @@ MeshFormat GLReplay::GetPostVSBuffers(uint32_t eventId, uint32_t instID, MeshDat ret.baseVertex = 0; if(s.buf) - ret.vertexResourceId = m_pDriver->GetResourceManager()->GetID(BufferRes(NULL, s.buf)); + ret.vertexResourceId = m_pDriver->GetResourceManager()->GetID(BufferRes(ctx, s.buf)); else ret.vertexResourceId = ResourceId(); diff --git a/renderdoc/driver/gl/gl_renderstate.cpp b/renderdoc/driver/gl/gl_renderstate.cpp index f9c3903da..9a717bc56 100644 --- a/renderdoc/driver/gl/gl_renderstate.cpp +++ b/renderdoc/driver/gl/gl_renderstate.cpp @@ -525,7 +525,7 @@ void GLRenderState::MarkDirty(WrappedOpenGL *gl) { GLResourceManager *manager = gl->GetResourceManager(); - void *ctx = gl->GetCtx(); + ContextPair &ctx = gl->GetCtx(); GLint maxCount = 0; GLuint name = 0; @@ -711,9 +711,9 @@ bool GLRenderState::CheckEnableDisableParam(GLenum pname) void GLRenderState::FetchState(WrappedOpenGL *gl) { - void *ctx = WrappedOpenGL::GetCtx(); + ContextPair &ctx = gl->GetCtx(); - if(ctx == NULL) + if(ctx.ctx == NULL) { ContextPresent = false; return; @@ -744,19 +744,24 @@ void GLRenderState::FetchState(WrappedOpenGL *gl) sizeof(Tex2DMSArray) == sizeof(Samplers), "All texture arrays should be identically sized"); -#if ENABLED(RDOC_DEVEL) - if(TextureRes(ctx, 0).Context != NULL) - RDCFATAL("Texture resources are context-specific - we assume they aren't"); - if(BufferRes(ctx, 0).Context != NULL) - RDCFATAL("Buffer resources are context-specific - we assume they aren't"); - if(SamplerRes(ctx, 0).Context != NULL) - RDCFATAL("Sampler resources are context-specific - we assume they aren't"); -#endif - for(GLuint i = 0; i < RDCMIN(maxTextures, (GLuint)ARRAY_COUNT(Tex2D)); i++) { m_Real->glActiveTexture(GLenum(eGL_TEXTURE0 + i)); + // textures are always shared + Tex1D[i].ContextShareGroup = ctx.shareGroup; + Tex2D[i].ContextShareGroup = ctx.shareGroup; + Tex3D[i].ContextShareGroup = ctx.shareGroup; + Tex1DArray[i].ContextShareGroup = ctx.shareGroup; + Tex2DArray[i].ContextShareGroup = ctx.shareGroup; + TexCube[i].ContextShareGroup = ctx.shareGroup; + TexRect[i].ContextShareGroup = ctx.shareGroup; + TexBuffer[i].ContextShareGroup = ctx.shareGroup; + Tex2DMS[i].ContextShareGroup = ctx.shareGroup; + Tex2DMSArray[i].ContextShareGroup = ctx.shareGroup; + TexCubeArray[i].ContextShareGroup = ctx.shareGroup; + Samplers[i].ContextShareGroup = ctx.shareGroup; + if(!IsGLES) m_Real->glGetIntegerv(eGL_TEXTURE_BINDING_1D, (GLint *)&Tex1D[i].name); else @@ -806,6 +811,9 @@ void GLRenderState::FetchState(WrappedOpenGL *gl) { GLboolean layered = GL_FALSE; + // textures are always shared + Images[i].res.ContextShareGroup = ctx.shareGroup; + m_Real->glGetIntegeri_v(eGL_IMAGE_BINDING_NAME, i, (GLint *)&Images[i].res.name); m_Real->glGetIntegeri_v(eGL_IMAGE_BINDING_LEVEL, i, (GLint *)&Images[i].level); m_Real->glGetIntegeri_v(eGL_IMAGE_BINDING_ACCESS, i, (GLint *)&Images[i].access); @@ -930,6 +938,10 @@ void GLRenderState::FetchState(WrappedOpenGL *gl) RDCEraseEl(Subroutines); } + // buffers are always shared + for(size_t i = 0; i < ARRAY_COUNT(BufferBindings); i++) + BufferBindings[i].ContextShareGroup = ctx.shareGroup; + m_Real->glGetIntegerv(eGL_ARRAY_BUFFER_BINDING, (GLint *)&BufferBindings[eBufIdx_Array].name); m_Real->glGetIntegerv(eGL_COPY_READ_BUFFER_BINDING, (GLint *)&BufferBindings[eBufIdx_Copy_Read].name); @@ -1000,6 +1012,9 @@ void GLRenderState::FetchState(WrappedOpenGL *gl) m_Real->glGetIntegerv(idxBufs[b].maxcount, &maxCount); for(int i = 0; i < idxBufs[b].count && i < maxCount; i++) { + // buffers are always shared + idxBufs[b].bufs[i].res.ContextShareGroup = ctx.shareGroup; + m_Real->glGetIntegeri_v(idxBufs[b].binding, i, (GLint *)&idxBufs[b].bufs[i].res.name); m_Real->glGetInteger64i_v(idxBufs[b].start, i, (GLint64 *)&idxBufs[b].bufs[i].start); m_Real->glGetInteger64i_v(idxBufs[b].size, i, (GLint64 *)&idxBufs[b].bufs[i].size); @@ -1260,9 +1275,9 @@ void GLRenderState::FetchState(WrappedOpenGL *gl) void GLRenderState::ApplyState(WrappedOpenGL *gl) { - void *ctx = WrappedOpenGL::GetCtx(); + ContextPair &ctx = gl->GetCtx(); - if(!ContextPresent || ctx == NULL) + if(!ContextPresent || ctx.ctx == NULL) return; for(GLuint i = 0; i < eEnabled_Count; i++) @@ -1322,10 +1337,7 @@ void GLRenderState::ApplyState(WrappedOpenGL *gl) m_Real->glActiveTexture(ActiveTexture); - if(VAO.name) - m_Real->glBindVertexArray(VAO.name); - else - m_Real->glBindVertexArray(gl->GetFakeVAO()); + m_Real->glBindVertexArray(VAO.name); if(HasExt[ARB_transform_feedback2]) m_Real->glBindTransformFeedback(eGL_TRANSFORM_FEEDBACK, FeedbackObj.name); @@ -1548,7 +1560,7 @@ void GLRenderState::ApplyState(WrappedOpenGL *gl) // this will always return true during capture, but on replay we only do // this work if we're on the replay context - if(gl->GetReplay()->IsReplayContext(ctx)) + if(gl->GetReplay()->IsReplayContext(ctx.ctx)) { // apply drawbuffers/readbuffer to default framebuffer m_Real->glBindFramebuffer(eGL_READ_FRAMEBUFFER, gl->GetFakeBBFBO()); diff --git a/renderdoc/driver/gl/gl_rendertext.cpp b/renderdoc/driver/gl/gl_rendertext.cpp index 7e2054a27..8672522f5 100644 --- a/renderdoc/driver/gl/gl_rendertext.cpp +++ b/renderdoc/driver/gl/gl_rendertext.cpp @@ -540,7 +540,7 @@ void WrappedOpenGL::RenderOverlayStr(float x, float y, const char *text) RDCASSERT(strlen(text) < (size_t)FONT_MAX_CHARS); - ContextData &ctxdata = m_ContextData[GetCtx()]; + ContextData &ctxdata = GetCtxData(); if(!ctxdata.built || !ctxdata.ready) return; diff --git a/renderdoc/driver/gl/gl_replay.cpp b/renderdoc/driver/gl/gl_replay.cpp index 73421195f..d5632e4b4 100644 --- a/renderdoc/driver/gl/gl_replay.cpp +++ b/renderdoc/driver/gl/gl_replay.cpp @@ -210,7 +210,7 @@ void GLReplay::SetReplayData(GLWindowingData data) { m_ReplayCtx = data; if(m_pDriver != NULL) - m_pDriver->RegisterContext(m_ReplayCtx, NULL, true, true); + m_pDriver->RegisterReplayContext(m_ReplayCtx, NULL, true, true); InitDebugData(); @@ -662,7 +662,7 @@ void GLReplay::SavePipelineState() // Index buffer - void *ctx = m_ReplayCtx.ctx; + ContextPair &ctx = gl.GetCtx(); GLuint ibuffer = 0; gl.glGetIntegerv(eGL_ELEMENT_ARRAY_BUFFER_BINDING, (GLint *)&ibuffer); @@ -1976,7 +1976,7 @@ void GLReplay::FillCBufferVariables(ResourceId shader, string entryPoint, uint32 else { ResourceId id = - m_pDriver->GetResourceManager()->GetID(ProgramPipeRes(m_ReplayCtx.ctx, curProg)); + m_pDriver->GetResourceManager()->GetID(ProgramPipeRes(m_pDriver->GetCtx(), curProg)); auto &pipeDetails = m_pDriver->m_Pipelines[id]; size_t s = ShaderIdx(shaderDetails.type); diff --git a/renderdoc/driver/gl/gl_replay_egl.cpp b/renderdoc/driver/gl/gl_replay_egl.cpp index efdab1d6f..e72950efc 100644 --- a/renderdoc/driver/gl/gl_replay_egl.cpp +++ b/renderdoc/driver/gl/gl_replay_egl.cpp @@ -156,13 +156,14 @@ ReplayStatus GLES_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver) WrappedOpenGL *gl = new WrappedOpenGL(real, GetGLPlatformEGL()); gl->SetDriverType(RDCDriver::OpenGLES); - gl->Initialise(initParams, ver); RDCLOG("Created OPEN GL ES replay device."); GLReplay *replay = gl->GetReplay(); replay->SetProxy(rdc == NULL); replay->SetReplayData(data); + gl->Initialise(initParams, ver); + *driver = (IReplayDriver *)replay; return ReplayStatus::Succeeded; } diff --git a/renderdoc/driver/gl/gl_replay_linux.cpp b/renderdoc/driver/gl/gl_replay_linux.cpp index bded233b6..68761619a 100644 --- a/renderdoc/driver/gl/gl_replay_linux.cpp +++ b/renderdoc/driver/gl/gl_replay_linux.cpp @@ -290,9 +290,8 @@ ReplayStatus GL_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver) } WrappedOpenGL *gl = new WrappedOpenGL(real, GetGLPlatform()); - gl->Initialise(initParams, ver); - RDCLOG("Created device."); + GLReplay *replay = gl->GetReplay(); replay->SetProxy(rdc == NULL); GLWindowingData data; @@ -301,6 +300,8 @@ ReplayStatus GL_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver) data.wnd = pbuffer; replay->SetReplayData(data); + gl->Initialise(initParams, ver); + *driver = (IReplayDriver *)replay; return ReplayStatus::Succeeded; } diff --git a/renderdoc/driver/gl/gl_replay_win32.cpp b/renderdoc/driver/gl/gl_replay_win32.cpp index 8e6086aeb..660a01ce2 100644 --- a/renderdoc/driver/gl/gl_replay_win32.cpp +++ b/renderdoc/driver/gl/gl_replay_win32.cpp @@ -318,9 +318,8 @@ ReplayStatus GL_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver) } WrappedOpenGL *gl = new WrappedOpenGL(real, GetGLPlatform()); - gl->Initialise(initParams, ver); - RDCLOG("Created device."); + GLReplay *replay = gl->GetReplay(); replay->SetProxy(rdc == NULL); GLWindowingData data; @@ -329,6 +328,8 @@ ReplayStatus GL_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver) data.wnd = w; replay->SetReplayData(data); + gl->Initialise(initParams, ver); + *driver = (IReplayDriver *)replay; return ReplayStatus::Succeeded; } diff --git a/renderdoc/driver/gl/gl_resources.h b/renderdoc/driver/gl/gl_resources.h index 1ea6fbc2d..05a0e8d36 100644 --- a/renderdoc/driver/gl/gl_resources.h +++ b/renderdoc/driver/gl/gl_resources.h @@ -102,37 +102,37 @@ struct GLResource { GLResource() { + ContextShareGroup = NULL; Namespace = eResUnknown; - Context = NULL; name = 0; } GLResource(NullInitialiser) { + ContextShareGroup = NULL; Namespace = eResUnknown; - Context = NULL; name = 0; } GLResource(void *ctx, GLNamespace n, GLuint i) { - Context = ctx; + ContextShareGroup = ctx; Namespace = n; name = i; } - void *Context; + void *ContextShareGroup; GLNamespace Namespace; GLuint name; bool operator==(const GLResource &o) const { - return Context == o.Context && Namespace == o.Namespace && name == o.name; + return ContextShareGroup == o.ContextShareGroup && Namespace == o.Namespace && name == o.name; } bool operator!=(const GLResource &o) const { return !(*this == o); } bool operator<(const GLResource &o) const { - if(Context != o.Context) - return Context < o.Context; + if(ContextShareGroup != o.ContextShareGroup) + return ContextShareGroup < o.ContextShareGroup; if(Namespace != o.Namespace) return Namespace < o.Namespace; return name < o.name; @@ -141,64 +141,65 @@ struct GLResource DECLARE_REFLECTION_STRUCT(GLResource); +struct ContextPair +{ + void *ctx; + void *shareGroup; +}; + // Shared objects currently ignore the context parameter. // For correctness we'd need to check if the context is shared and if so move up to a 'parent' // so the context value ends up being identical for objects being shared, but can be different // for objects in non-shared contexts -inline GLResource TextureRes(void *ctx, GLuint i) +inline GLResource TextureRes(const ContextPair &c, GLuint i) { - (void)ctx; - return GLResource(NULL, eResTexture, i); + return GLResource(c.shareGroup, eResTexture, i); } -inline GLResource SamplerRes(void *ctx, GLuint i) +inline GLResource SamplerRes(const ContextPair &c, GLuint i) { - (void)ctx; - return GLResource(NULL, eResSampler, i); + return GLResource(c.shareGroup, eResSampler, i); } -inline GLResource FramebufferRes(void *ctx, GLuint i) +inline GLResource FramebufferRes(const ContextPair &c, GLuint i) { - return GLResource(VendorCheck[VendorCheck_EXT_fbo_shared] ? NULL : ctx, eResFramebuffer, i); + return GLResource(VendorCheck[VendorCheck_EXT_fbo_shared] ? c.shareGroup : c.ctx, eResFramebuffer, + i); } -inline GLResource RenderbufferRes(void *ctx, GLuint i) +inline GLResource RenderbufferRes(const ContextPair &c, GLuint i) { - (void)ctx; - return GLResource(NULL, eResRenderbuffer, i); + return GLResource(c.shareGroup, eResRenderbuffer, i); } -inline GLResource BufferRes(void *ctx, GLuint i) +inline GLResource BufferRes(const ContextPair &c, GLuint i) { - (void)ctx; - return GLResource(NULL, eResBuffer, i); + return GLResource(c.shareGroup, eResBuffer, i); } -inline GLResource VertexArrayRes(void *ctx, GLuint i) +inline GLResource VertexArrayRes(const ContextPair &c, GLuint i) { - return GLResource(VendorCheck[VendorCheck_EXT_vao_shared] ? NULL : ctx, eResVertexArray, i); + return GLResource(VendorCheck[VendorCheck_EXT_vao_shared] ? c.shareGroup : c.ctx, eResVertexArray, + i); } -inline GLResource ShaderRes(void *ctx, GLuint i) +inline GLResource ShaderRes(const ContextPair &c, GLuint i) { - (void)ctx; - return GLResource(NULL, eResShader, i); + return GLResource(c.shareGroup, eResShader, i); } -inline GLResource ProgramRes(void *ctx, GLuint i) +inline GLResource ProgramRes(const ContextPair &c, GLuint i) { - (void)ctx; - return GLResource(NULL, eResProgram, i); + return GLResource(c.shareGroup, eResProgram, i); } -inline GLResource ProgramPipeRes(void *ctx, GLuint i) +inline GLResource ProgramPipeRes(const ContextPair &c, GLuint i) { - return GLResource(ctx, eResProgramPipe, i); + return GLResource(c.ctx, eResProgramPipe, i); } -inline GLResource FeedbackRes(void *ctx, GLuint i) +inline GLResource FeedbackRes(const ContextPair &c, GLuint i) { - return GLResource(ctx, eResFeedback, i); + return GLResource(c.ctx, eResFeedback, i); } -inline GLResource QueryRes(void *ctx, GLuint i) +inline GLResource QueryRes(const ContextPair &c, GLuint i) { - return GLResource(ctx, eResQuery, i); + return GLResource(c.ctx, eResQuery, i); } -inline GLResource SyncRes(void *ctx, GLuint i) +inline GLResource SyncRes(const ContextPair &c, GLuint i) { - (void)ctx; - return GLResource(NULL, eResSync, i); + return GLResource(c.shareGroup, eResSync, i); } struct GLResourceRecord : public ResourceRecord diff --git a/renderdoc/driver/gl/gl_stringise.cpp b/renderdoc/driver/gl/gl_stringise.cpp index 4ff3ad356..2545c9569 100644 --- a/renderdoc/driver/gl/gl_stringise.cpp +++ b/renderdoc/driver/gl/gl_stringise.cpp @@ -36,6 +36,7 @@ std::string DoStringise(const GLChunk &el) STRINGISE_ENUM_CLASS_NAMED(MakeContextCurrent, "MakeContextCurrent"); STRINGISE_ENUM_CLASS_NAMED(glIndirectSubCommand, "Indirect sub-command"); + STRINGISE_ENUM_CLASS_NAMED(glContextInit, "Context Initialisation"); STRINGISE_ENUM_CLASS(vrapi_CreateTextureSwapChain); STRINGISE_ENUM_CLASS(vrapi_CreateTextureSwapChain2); diff --git a/renderdoc/driver/gl/wrappers/gl_buffer_funcs.cpp b/renderdoc/driver/gl/wrappers/gl_buffer_funcs.cpp index 62023e419..e8f13c53c 100644 --- a/renderdoc/driver/gl/wrappers/gl_buffer_funcs.cpp +++ b/renderdoc/driver/gl/wrappers/gl_buffer_funcs.cpp @@ -3042,9 +3042,6 @@ bool WrappedOpenGL::Serialise_glVertexArrayVertexAttribOffsetEXT( if(IsReplayingAndReading()) { - if(vaobj.name == 0) - vaobj.name = m_FakeVAO; - // some intel drivers don't properly update query states (like GL_VERTEX_ATTRIB_ARRAY_SIZE) // unless the VAO is also bound when performing EXT_dsa functions :( GLuint prevVAO = 0; @@ -3168,9 +3165,6 @@ bool WrappedOpenGL::Serialise_glVertexArrayVertexAttribIOffsetEXT(SerialiserType if(IsReplayingAndReading()) { - if(vaobj.name == 0) - vaobj.name = m_FakeVAO; - // some intel drivers don't properly update query states (like GL_VERTEX_ATTRIB_ARRAY_SIZE) // unless the VAO is also bound when performing EXT_dsa functions :( GLuint prevVAO = 0; @@ -3294,9 +3288,6 @@ bool WrappedOpenGL::Serialise_glVertexArrayVertexAttribLOffsetEXT(SerialiserType if(IsReplayingAndReading()) { - if(vaobj.name == 0) - vaobj.name = m_FakeVAO; - // some intel drivers don't properly update query states (like GL_VERTEX_ATTRIB_ARRAY_SIZE) // unless the VAO is also bound when performing EXT_dsa functions :( GLuint prevVAO = 0; @@ -3415,9 +3406,6 @@ bool WrappedOpenGL::Serialise_glVertexArrayVertexAttribBindingEXT(SerialiserType if(IsReplayingAndReading()) { - if(vaobj.name == 0) - vaobj.name = m_FakeVAO; - m_Real.glVertexArrayVertexAttribBindingEXT(vaobj.name, attribindex, bindingindex); } return true; @@ -3500,9 +3488,6 @@ bool WrappedOpenGL::Serialise_glVertexArrayVertexAttribFormatEXT(SerialiserType if(IsReplayingAndReading()) { - if(vaobj.name == 0) - vaobj.name = m_FakeVAO; - m_Real.glVertexArrayVertexAttribFormatEXT(vaobj.name, attribindex, size, type, normalized, relativeoffset); } @@ -3591,9 +3576,6 @@ bool WrappedOpenGL::Serialise_glVertexArrayVertexAttribIFormatEXT(SerialiserType if(IsReplayingAndReading()) { - if(vaobj.name == 0) - vaobj.name = m_FakeVAO; - m_Real.glVertexArrayVertexAttribIFormatEXT(vaobj.name, attribindex, size, type, relativeoffset); } @@ -3678,9 +3660,6 @@ bool WrappedOpenGL::Serialise_glVertexArrayVertexAttribLFormatEXT(SerialiserType if(IsReplayingAndReading()) { - if(vaobj.name == 0) - vaobj.name = m_FakeVAO; - m_Real.glVertexArrayVertexAttribLFormatEXT(vaobj.name, attribindex, size, type, relativeoffset); } @@ -3762,9 +3741,6 @@ bool WrappedOpenGL::Serialise_glVertexArrayVertexAttribDivisorEXT(SerialiserType if(IsReplayingAndReading()) { - if(vaobj.name == 0) - vaobj.name = m_FakeVAO; - // at the time of writing, AMD driver seems to not have this entry point if(m_Real.glVertexArrayVertexAttribDivisorEXT) { @@ -3852,9 +3828,6 @@ bool WrappedOpenGL::Serialise_glEnableVertexArrayAttribEXT(SerialiserType &ser, if(IsReplayingAndReading()) { - if(vaobj.name == 0) - vaobj.name = m_FakeVAO; - GLint prevVAO = 0; m_Real.glGetIntegerv(eGL_VERTEX_ARRAY_BINDING, &prevVAO); @@ -3935,9 +3908,6 @@ bool WrappedOpenGL::Serialise_glDisableVertexArrayAttribEXT(SerialiserType &ser, if(IsReplayingAndReading()) { - if(vaobj.name == 0) - vaobj.name = m_FakeVAO; - GLint prevVAO = 0; m_Real.glGetIntegerv(eGL_VERTEX_ARRAY_BINDING, &prevVAO); @@ -4134,9 +4104,6 @@ bool WrappedOpenGL::Serialise_glBindVertexArray(SerialiserType &ser, GLuint vaob if(IsReplayingAndReading()) { - if(vaobj.name == 0) - vaobj.name = m_FakeVAO; - m_Real.glBindVertexArray(vaobj.name); } @@ -4185,9 +4152,6 @@ bool WrappedOpenGL::Serialise_glVertexArrayElementBuffer(SerialiserType &ser, GL if(IsReplayingAndReading()) { - if(vaobj.name == 0) - vaobj.name = m_FakeVAO; - // might not have the live resource if this is a pre-capture chunk, and the buffer was never // referenced at all in the actual frame if(buffer.name) @@ -4256,9 +4220,6 @@ bool WrappedOpenGL::Serialise_glVertexArrayBindVertexBufferEXT(SerialiserType &s if(IsReplayingAndReading()) { - if(vaobj.name == 0) - vaobj.name = m_FakeVAO; - if(buffer.name) { m_Buffers[GetResourceManager()->GetID(buffer)].curType = eGL_ARRAY_BUFFER; @@ -4393,9 +4354,6 @@ bool WrappedOpenGL::Serialise_glVertexArrayVertexBuffers(SerialiserType &ser, GL offs.push_back((GLintptr)offsets[i]); } - if(vaobj.name == 0) - vaobj.name = m_FakeVAO; - // use ARB_direct_state_access functions here as we use EXT_direct_state_access elsewhere. If // we are running without ARB_dsa support, these functions are emulated in the obvious way. This // is necessary since these functions can be serialised even if ARB_dsa was not used originally, @@ -4522,9 +4480,6 @@ bool WrappedOpenGL::Serialise_glVertexArrayVertexBindingDivisorEXT(SerialiserTyp if(IsReplayingAndReading()) { - if(vaobj.name == 0) - vaobj.name = m_FakeVAO; - m_Real.glVertexArrayVertexBindingDivisorEXT(vaobj.name, bindingindex, divisor); } diff --git a/renderdoc/driver/gl/wrappers/gl_draw_funcs.cpp b/renderdoc/driver/gl/wrappers/gl_draw_funcs.cpp index 2d1f31a38..3de5f0e0c 100644 --- a/renderdoc/driver/gl/wrappers/gl_draw_funcs.cpp +++ b/renderdoc/driver/gl/wrappers/gl_draw_funcs.cpp @@ -3139,9 +3139,6 @@ bool WrappedOpenGL::Serialise_glClearNamedFramebufferfv(SerialiserType &ser, if(IsReplayingAndReading()) { - if(framebuffer.name == 0) - framebuffer.name = m_FakeBB_FBO; - // use ARB_direct_state_access functions here as we use EXT_direct_state_access elsewhere. If // we are running without ARB_dsa support, these functions are emulated in the obvious way. This // is necessary since these functions can be serialised even if ARB_dsa was not used originally, @@ -3265,9 +3262,6 @@ bool WrappedOpenGL::Serialise_glClearNamedFramebufferiv(SerialiserType &ser, if(IsReplayingAndReading()) { - if(framebuffer.name == 0) - framebuffer.name = m_FakeBB_FBO; - // use ARB_direct_state_access functions here as we use EXT_direct_state_access elsewhere. If // we are running without ARB_dsa support, these functions are emulated in the obvious way. This // is necessary since these functions can be serialised even if ARB_dsa was not used originally, @@ -3382,9 +3376,6 @@ bool WrappedOpenGL::Serialise_glClearNamedFramebufferuiv(SerialiserType &ser, if(IsReplayingAndReading()) { - if(framebuffer.name == 0) - framebuffer.name = m_FakeBB_FBO; - // use ARB_direct_state_access functions here as we use EXT_direct_state_access elsewhere. If // we are running without ARB_dsa support, these functions are emulated in the obvious way. This // is necessary since these functions can be serialised even if ARB_dsa was not used originally, @@ -3488,9 +3479,6 @@ bool WrappedOpenGL::Serialise_glClearNamedFramebufferfi(SerialiserType &ser, GLu if(IsReplayingAndReading()) { - if(framebuffer.name == 0) - framebuffer.name = m_FakeBB_FBO; - // use ARB_direct_state_access functions here as we use EXT_direct_state_access elsewhere. If // we are running without ARB_dsa support, these functions are emulated in the obvious way. This // is necessary since these functions can be serialised even if ARB_dsa was not used originally, diff --git a/renderdoc/driver/gl/wrappers/gl_framebuffer_funcs.cpp b/renderdoc/driver/gl/wrappers/gl_framebuffer_funcs.cpp index 1201ce300..6c37ed81b 100644 --- a/renderdoc/driver/gl/wrappers/gl_framebuffer_funcs.cpp +++ b/renderdoc/driver/gl/wrappers/gl_framebuffer_funcs.cpp @@ -163,9 +163,6 @@ bool WrappedOpenGL::Serialise_glNamedFramebufferTextureEXT(SerialiserType &ser, if(IsReplayingAndReading()) { - if(framebuffer.name == 0) - framebuffer.name = m_FakeBB_FBO; - m_Real.glNamedFramebufferTextureEXT(framebuffer.name, attachment, texture.name, level); if(IsLoading(m_State) && texture.name) @@ -304,9 +301,6 @@ bool WrappedOpenGL::Serialise_glNamedFramebufferTexture1DEXT(SerialiserType &ser if(IsReplayingAndReading()) { - if(framebuffer.name == 0) - framebuffer.name = m_FakeBB_FBO; - m_Real.glNamedFramebufferTexture1DEXT(framebuffer.name, attachment, textarget, texture.name, level); @@ -449,9 +443,6 @@ bool WrappedOpenGL::Serialise_glNamedFramebufferTexture2DEXT(SerialiserType &ser if(IsReplayingAndReading()) { - if(framebuffer.name == 0) - framebuffer.name = m_FakeBB_FBO; - m_Real.glNamedFramebufferTexture2DEXT(framebuffer.name, attachment, textarget, texture.name, level); @@ -700,9 +691,6 @@ bool WrappedOpenGL::Serialise_glNamedFramebufferTexture3DEXT(SerialiserType &ser if(IsReplayingAndReading()) { - if(framebuffer.name == 0) - framebuffer.name = m_FakeBB_FBO; - m_Real.glNamedFramebufferTexture3DEXT(framebuffer.name, attachment, textarget, texture.name, level, zoffset); @@ -848,9 +836,6 @@ bool WrappedOpenGL::Serialise_glNamedFramebufferRenderbufferEXT(SerialiserType & if(IsReplayingAndReading()) { - if(framebuffer.name == 0) - framebuffer.name = m_FakeBB_FBO; - m_Real.glNamedFramebufferRenderbufferEXT(framebuffer.name, attachment, renderbuffertarget, renderbuffer.name); @@ -977,9 +962,6 @@ bool WrappedOpenGL::Serialise_glNamedFramebufferTextureLayerEXT(SerialiserType & if(IsReplayingAndReading()) { - if(framebuffer.name == 0) - framebuffer.name = m_FakeBB_FBO; - m_Real.glNamedFramebufferTextureLayerEXT(framebuffer.name, attachment, texture.name, level, layer); @@ -1383,20 +1365,13 @@ bool WrappedOpenGL::Serialise_glFramebufferReadBufferEXT(SerialiserType &ser, if(IsReplayingAndReading()) { - if(framebuffer.name == 0) - { - // since we are faking the default framebuffer with our own - // to see the results, replace back/front/left/right with color attachment 0 - if(mode == eGL_BACK_LEFT || mode == eGL_BACK_RIGHT || mode == eGL_BACK || - mode == eGL_FRONT_LEFT || mode == eGL_FRONT_RIGHT || mode == eGL_FRONT) - mode = eGL_COLOR_ATTACHMENT0; + // since we are faking the default framebuffer with our own + // to see the results, replace back/front/left/right with color attachment 0 + if(mode == eGL_BACK_LEFT || mode == eGL_BACK_RIGHT || mode == eGL_BACK || + mode == eGL_FRONT_LEFT || mode == eGL_FRONT_RIGHT || mode == eGL_FRONT) + mode = eGL_COLOR_ATTACHMENT0; - m_Real.glFramebufferReadBufferEXT(m_FakeBB_FBO, mode); - } - else - { - m_Real.glFramebufferReadBufferEXT(framebuffer.name, mode); - } + m_Real.glFramebufferReadBufferEXT(framebuffer.name, mode); } return true; @@ -1464,7 +1439,7 @@ bool WrappedOpenGL::Serialise_glBindFramebuffer(SerialiserType &ser, GLenum targ if(IsReplayingAndReading()) { - m_Real.glBindFramebuffer(target, framebuffer.name ? framebuffer.name : m_FakeBB_FBO); + m_Real.glBindFramebuffer(target, framebuffer.name); } return true; @@ -1472,9 +1447,6 @@ bool WrappedOpenGL::Serialise_glBindFramebuffer(SerialiserType &ser, GLenum targ void WrappedOpenGL::glBindFramebuffer(GLenum target, GLuint framebuffer) { - if(framebuffer == 0 && IsReplayMode(m_State)) - framebuffer = m_FakeBB_FBO; - SERIALISE_TIME_CALL(m_Real.glBindFramebuffer(target, framebuffer)); if(IsActiveCapturing(m_State)) @@ -1508,20 +1480,13 @@ bool WrappedOpenGL::Serialise_glFramebufferDrawBufferEXT(SerialiserType &ser, if(IsReplayingAndReading()) { - if(framebuffer.name == 0) - { - // since we are faking the default framebuffer with our own - // to see the results, replace back/front/left/right with color attachment 0 - if(buf == eGL_BACK_LEFT || buf == eGL_BACK_RIGHT || buf == eGL_BACK || - buf == eGL_FRONT_LEFT || buf == eGL_FRONT_RIGHT || buf == eGL_FRONT) - buf = eGL_COLOR_ATTACHMENT0; + // since we are faking the default framebuffer with our own + // to see the results, replace back/front/left/right with color attachment 0 + if(buf == eGL_BACK_LEFT || buf == eGL_BACK_RIGHT || buf == eGL_BACK || buf == eGL_FRONT_LEFT || + buf == eGL_FRONT_RIGHT || buf == eGL_FRONT) + buf = eGL_COLOR_ATTACHMENT0; - m_Real.glFramebufferDrawBufferEXT(m_FakeBB_FBO, buf); - } - else - { - m_Real.glFramebufferDrawBufferEXT(framebuffer.name, buf); - } + m_Real.glFramebufferDrawBufferEXT(framebuffer.name, buf); } return true; @@ -1602,7 +1567,7 @@ bool WrappedOpenGL::Serialise_glFramebufferDrawBuffersEXT(SerialiserType &ser, buffers[i] = eGL_COLOR_ATTACHMENT0; } - m_Real.glFramebufferDrawBuffersEXT(framebuffer.name ? framebuffer.name : m_FakeBB_FBO, n, bufs); + m_Real.glFramebufferDrawBuffersEXT(framebuffer.name, n, bufs); } return true; @@ -1803,12 +1768,6 @@ bool WrappedOpenGL::Serialise_glBlitNamedFramebuffer(SerialiserType &ser, if(IsReplayingAndReading()) { - if(readFramebuffer.name == 0) - readFramebuffer.name = m_FakeBB_FBO; - - if(drawFramebuffer.name == 0) - drawFramebuffer.name = m_FakeBB_FBO; - // use ARB_direct_state_access functions here as we use EXT_direct_state_access elsewhere. If // we are running without ARB_dsa support, these functions are emulated in the obvious way. This // is necessary since these functions can be serialised even if ARB_dsa was not used originally,