diff --git a/include/Nazara/Renderer/OpenGL.hpp b/include/Nazara/Renderer/OpenGL.hpp index 33f5b0cf2..cdfe0b075 100644 --- a/include/Nazara/Renderer/OpenGL.hpp +++ b/include/Nazara/Renderer/OpenGL.hpp @@ -86,9 +86,11 @@ class NAZARA_API NzOpenGL static void BindViewport(const NzRecti& viewport); static void DeleteBuffer(nzBufferType type, GLuint id); + static void DeleteFrameBuffer(const NzContext* context, GLuint id); static void DeleteProgram(GLuint id); static void DeleteSampler(GLuint id); static void DeleteTexture(GLuint id); + static void DeleteVertexArray(const NzContext* context, GLuint id); static GLuint GetCurrentBuffer(nzBufferType type); static GLuint GetCurrentProgram(); @@ -149,7 +151,7 @@ class NAZARA_API NzOpenGL static GLenum TextureTargetProxy[nzImageType_Max+1]; private: - static void OnContextChange(const NzContext* newContext); + static void OnContextChanged(const NzContext* newContext); static void OnContextDestruction(const NzContext* context); }; diff --git a/src/Nazara/Renderer/Context.cpp b/src/Nazara/Renderer/Context.cpp index 21b7672fd..0e49be56f 100644 --- a/src/Nazara/Renderer/Context.cpp +++ b/src/Nazara/Renderer/Context.cpp @@ -248,7 +248,7 @@ bool NzContext::SetActive(bool active) const currentContext = nullptr; } - NzOpenGL::OnContextChange(currentContext); + NzOpenGL::OnContextChanged(currentContext); return true; } diff --git a/src/Nazara/Renderer/OpenGL.cpp b/src/Nazara/Renderer/OpenGL.cpp index 5a3bb8a7a..082899014 100644 --- a/src/Nazara/Renderer/OpenGL.cpp +++ b/src/Nazara/Renderer/OpenGL.cpp @@ -63,8 +63,15 @@ namespace #endif } + enum GarbageResourceType + { + GarbageResourceType_FrameBuffer, + GarbageResourceType_VertexArray + }; + struct ContextStates { + std::vector> garbage; // Les ressources à supprimer dès que possible GLuint buffersBinding[nzBufferType_Max+1] = {0}; GLuint currentProgram = 0; GLuint samplers[32] = {0}; // 32 est pour l'instant la plus haute limite (GL_TEXTURE31) @@ -496,6 +503,15 @@ void NzOpenGL::DeleteBuffer(nzBufferType type, GLuint id) s_contextStates->buffersBinding[type] = 0; } +void NzOpenGL::DeleteFrameBuffer(const NzContext* context, GLuint id) +{ + // Si le contexte est actif, ne nous privons pas + if (NzContext::GetCurrent() == context) + glDeleteFramebuffers(1, &id); + else + s_contexts[context].garbage.emplace_back(GarbageResourceType_FrameBuffer, id); +} + void NzOpenGL::DeleteProgram(GLuint id) { #ifdef NAZARA_DEBUG @@ -555,6 +571,15 @@ void NzOpenGL::DeleteTexture(GLuint id) } } +void NzOpenGL::DeleteVertexArray(const NzContext* context, GLuint id) +{ + // Si le contexte est actif, ne nous privons pas + if (NzContext::GetCurrent() == context) + glDeleteFramebuffers(1, &id); + else + s_contexts[context].garbage.emplace_back(GarbageResourceType_VertexArray, id); +} + GLuint NzOpenGL::GetCurrentBuffer(nzBufferType type) { #ifdef NAZARA_DEBUG @@ -1778,9 +1803,27 @@ void NzOpenGL::Uninitialize() } } -void NzOpenGL::OnContextChange(const NzContext* newContext) +void NzOpenGL::OnContextChanged(const NzContext* newContext) { s_contextStates = (newContext) ? &s_contexts[newContext] : nullptr; + if (s_contextStates) + { + // On supprime les éventuelles ressources mortes-vivantes (Qui ne peuvent être libérées que dans notre contexte) + for (std::pair& pair : s_contextStates->garbage) + { + switch (pair.first) + { + case GarbageResourceType_FrameBuffer: + glDeleteFramebuffers(1, &pair.second); + break; + + case GarbageResourceType_VertexArray: + glDeleteVertexArrays(1, &pair.second); + break; + } + } + s_contextStates->garbage.clear(); + } } void NzOpenGL::OnContextDestruction(const NzContext* context) diff --git a/src/Nazara/Renderer/RenderTexture.cpp b/src/Nazara/Renderer/RenderTexture.cpp index 01b5cb92d..5c5ae4cdf 100644 --- a/src/Nazara/Renderer/RenderTexture.cpp +++ b/src/Nazara/Renderer/RenderTexture.cpp @@ -3,6 +3,7 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include +#include #include #include #include @@ -325,31 +326,36 @@ bool NzRenderTexture::Create(bool lock) } #endif - NzRenderTextureImpl* impl = new NzRenderTextureImpl; - - impl->context = NzContext::GetCurrent(); - impl->context->AddResourceListener(this); + std::unique_ptr impl(new NzRenderTextureImpl); impl->fbo = 0; glGenFramebuffers(1, &impl->fbo); if (!impl->fbo) { - delete impl; - NazaraError("Failed to create framebuffer"); return false; } - m_impl = impl; + m_impl = impl.release(); + m_impl->context = NzContext::GetCurrent(); + m_impl->context->AddResourceListener(this); - if (lock && !Lock()) + if (lock) { - delete impl; - m_impl = nullptr; + // En cas d'exception, la ressource sera quand même libérée + NzCallOnExit onExit([this] () + { + Destroy(); + }); - NazaraError("Failed to lock render texture"); - return false; + if (!Lock()) + { + NazaraError("Failed to lock render texture"); + return false; + } + + onExit.Reset(); } NotifyParametersChange(); @@ -365,15 +371,6 @@ void NzRenderTexture::Destroy() if (IsActive()) NzRenderer::SetTarget(nullptr); - bool canFreeFBO = true; - #if NAZARA_RENDERER_SAFE - if (NzContext::GetCurrent() != m_impl->context) - { - NazaraWarning("RenderTexture should be destroyed by it's creation context, this will cause leaks"); - canFreeFBO = false; - } - #endif - m_impl->context->RemoveResourceListener(this); for (const Attachment& attachment : m_impl->attachments) @@ -387,10 +384,11 @@ void NzRenderTexture::Destroy() } } - if (canFreeFBO) - glDeleteFramebuffers(1, &m_impl->fbo); + // Le FBO devant être supprimé dans son contexte d'origine, nous déléguons sa suppression à la classe OpenGL + // Celle-ci va libérer le FBO dès que possible (la prochaine fois que son contexte d'origine sera actif) + NzOpenGL::DeleteFrameBuffer(m_impl->context, m_impl->fbo); - delete m_impl; + delete m_impl; // Enlève également une références sur les Texture/RenderBuffer m_impl = nullptr; } }