diff --git a/Graphics/GraphicsEngineOpenGL/include/DeviceContextGLImpl.hpp b/Graphics/GraphicsEngineOpenGL/include/DeviceContextGLImpl.hpp index 8a9886f01..49603cd61 100644 --- a/Graphics/GraphicsEngineOpenGL/include/DeviceContextGLImpl.hpp +++ b/Graphics/GraphicsEngineOpenGL/include/DeviceContextGLImpl.hpp @@ -293,7 +293,7 @@ class DeviceContextGLImpl final : public DeviceContextBase virtual void DILIGENT_CALL_TYPE BindSparseResourceMemory(const BindSparseResourceMemoryAttribs& Attribs) override final; /// Implementation of IDeviceContextGL::UpdateCurrentGLContext(). - virtual bool DILIGENT_CALL_TYPE UpdateCurrentGLContext() override final; + virtual bool DILIGENT_CALL_TYPE UpdateCurrentGLContext(bool PurgeCaches) override final; GLContextState& GetContextState() { return m_ContextState; } diff --git a/Graphics/GraphicsEngineOpenGL/include/RenderDeviceGLImpl.hpp b/Graphics/GraphicsEngineOpenGL/include/RenderDeviceGLImpl.hpp index 086df473c..ebd4ee797 100644 --- a/Graphics/GraphicsEngineOpenGL/include/RenderDeviceGLImpl.hpp +++ b/Graphics/GraphicsEngineOpenGL/include/RenderDeviceGLImpl.hpp @@ -197,6 +197,8 @@ class RenderDeviceGLImpl : public RenderDeviceBase void OnDestroyPSO(PipelineStateGLImpl& PSO); void OnDestroyBuffer(BufferGLImpl& Buffer); + void PurgeContextCaches(GLContext::NativeGLContextType Context); + GLProgramCache& GetProgramCache() { return m_ProgramCache; } size_t GetCommandQueueCount() const { return 1; } diff --git a/Graphics/GraphicsEngineOpenGL/interface/DeviceContextGL.h b/Graphics/GraphicsEngineOpenGL/interface/DeviceContextGL.h index e64206e2a..1c091feb7 100644 --- a/Graphics/GraphicsEngineOpenGL/interface/DeviceContextGL.h +++ b/Graphics/GraphicsEngineOpenGL/interface/DeviceContextGL.h @@ -1,5 +1,5 @@ /* - * Copyright 2019-2023 Diligent Graphics LLC + * Copyright 2019-2024 Diligent Graphics LLC * Copyright 2015-2019 Egor Yusov * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -56,10 +56,16 @@ DILIGENT_BEGIN_INTERFACE(IDeviceContextGL, IDeviceContext) /// If an application uses multiple GL contexts, this method must be called before any /// other command to let the engine update active context every time when control flow - /// is passed over from the main application + /// is passed over from the main application. /// - /// \return false if there is no active GL context, and true otherwise - VIRTUAL bool METHOD(UpdateCurrentGLContext)(THIS) PURE; + /// \param[in] PurgeCaches - Whether to purge context caches (e.g. VAO, FBO) before + /// updating the active context. An application should set this + /// flag to true if the last active context will not be used anymore + /// (e.g. it was destroyed) to avoid memory leaks. + /// + /// \return false if there is no active GL context, and true otherwise. + VIRTUAL bool METHOD(UpdateCurrentGLContext)(THIS_ + bool PurgeCaches DEFAULT_INITIALIZER(false)) PURE; /// Sets the swap in the device context. The swap chain is used by the device context /// to obtain the default FBO handle. @@ -74,8 +80,8 @@ DILIGENT_END_INTERFACE // clang-format off -# define IDeviceContextGL_UpdateCurrentGLContext(This) CALL_IFACE_METHOD(DeviceContextGL, UpdateCurrentGLContext, This) -# define IDeviceContextGL_SetSwapChain(This, ...) CALL_IFACE_METHOD(DeviceContextGL, SetSwapChain, This, __VA_ARGS__) +# define IDeviceContextGL_UpdateCurrentGLContext(This, ...) CALL_IFACE_METHOD(DeviceContextGL, UpdateCurrentGLContext, This, __VA_ARGS__) +# define IDeviceContextGL_SetSwapChain(This, ...) CALL_IFACE_METHOD(DeviceContextGL, SetSwapChain, This, __VA_ARGS__) // clang-format on diff --git a/Graphics/GraphicsEngineOpenGL/src/DeviceContextGLImpl.cpp b/Graphics/GraphicsEngineOpenGL/src/DeviceContextGLImpl.cpp index 19a39b84e..a6996d57c 100644 --- a/Graphics/GraphicsEngineOpenGL/src/DeviceContextGLImpl.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/DeviceContextGLImpl.cpp @@ -408,9 +408,8 @@ void DeviceContextGLImpl::CommitRenderTargets() "Depth buffer of the default framebuffer can only be bound with the default framebuffer's color buffer " "and cannot be combined with any other render target in OpenGL backend."); - auto CurrentNativeGLContext = m_ContextState.GetCurrentGLContext(); - auto& FBOCache = m_pDevice->GetFBOCache(CurrentNativeGLContext); - const auto& FBO = FBOCache.GetFBO(NumRenderTargets, pBoundRTVs, m_pBoundDepthStencil, m_ContextState); + auto& FBOCache = m_pDevice->GetFBOCache(m_ContextState.GetCurrentGLContext()); + const auto& FBO = FBOCache.GetFBO(NumRenderTargets, pBoundRTVs, m_pBoundDepthStencil, m_ContextState); // Even though the write mask only applies to writes to a framebuffer, the mask state is NOT // Framebuffer state. So it is NOT part of a Framebuffer Object or the Default Framebuffer. // Binding a new framebuffer will NOT affect the mask. @@ -771,11 +770,10 @@ void DeviceContextGLImpl::PrepareForDraw(DRAW_FLAGS Flags, bool IsIndexed, GLenu DvpValidateCommittedShaderResources(); #endif - const auto CurrNativeGLContext = m_pDevice->m_GLContext.GetCurrentNativeGLContext(); - const auto& PipelineDesc = m_pPipelineState->GetGraphicsPipelineDesc(); + const auto& PipelineDesc = m_pPipelineState->GetGraphicsPipelineDesc(); if (!m_ContextState.IsValidVAOBound()) { - auto& VaoCache = m_pDevice->GetVAOCache(CurrNativeGLContext); + auto& VaoCache = m_pDevice->GetVAOCache(m_ContextState.GetCurrentGLContext()); auto* pIndexBuffer = IsIndexed ? m_pIndexBuffer.RawPtr() : nullptr; if (PipelineDesc.InputLayout.NumElements > 0 || pIndexBuffer != nullptr) { @@ -1655,8 +1653,13 @@ void DeviceContextGLImpl::EndQuery(IQuery* pQuery) } } -bool DeviceContextGLImpl::UpdateCurrentGLContext() +bool DeviceContextGLImpl::UpdateCurrentGLContext(bool PurgeCaches) { + if (PurgeCaches) + { + m_pDevice->PurgeContextCaches(m_ContextState.GetCurrentGLContext()); + } + auto NativeGLContext = m_pDevice->m_GLContext.GetCurrentNativeGLContext(); if (NativeGLContext == NULL) return false; @@ -1905,8 +1908,7 @@ void DeviceContextGLImpl::ResolveTextureSubresource(ITexture* const auto& SrcTexDesc = pSrcTexGl->GetDesc(); //const auto& DstTexDesc = pDstTexGl->GetDesc(); - auto CurrentNativeGLContext = m_ContextState.GetCurrentGLContext(); - auto& FBOCache = m_pDevice->GetFBOCache(CurrentNativeGLContext); + auto& FBOCache = m_pDevice->GetFBOCache(m_ContextState.GetCurrentGLContext()); GLuint SrcFBOHandle = 0; { diff --git a/Graphics/GraphicsEngineOpenGL/src/RenderDeviceGLImpl.cpp b/Graphics/GraphicsEngineOpenGL/src/RenderDeviceGLImpl.cpp index 406c8f28e..ab147404a 100644 --- a/Graphics/GraphicsEngineOpenGL/src/RenderDeviceGLImpl.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/RenderDeviceGLImpl.cpp @@ -1682,6 +1682,19 @@ void RenderDeviceGLImpl::OnDestroyBuffer(BufferGLImpl& Buffer) VAOCacheIt.second.OnDestroyBuffer(Buffer); } + +void RenderDeviceGLImpl::PurgeContextCaches(GLContext::NativeGLContextType Context) +{ + { + Threading::SpinLockGuard FBOCacheGuard{m_FBOCacheLock}; + m_FBOCache.erase(Context); + } + { + Threading::SpinLockGuard VAOCacheGuard{m_VAOCacheLock}; + m_VAOCache.erase(Context); + } +} + void RenderDeviceGLImpl::IdleGPU() { glFinish();