diff --git a/include/SFML/Graphics/RenderTarget.hpp b/include/SFML/Graphics/RenderTarget.hpp index a9a17d86ac..0d167460a3 100644 --- a/include/SFML/Graphics/RenderTarget.hpp +++ b/include/SFML/Graphics/RenderTarget.hpp @@ -296,7 +296,7 @@ class SFML_GRAPHICS_API RenderTarget : NonCopyable /// \return True if operation was successful, false otherwise /// //////////////////////////////////////////////////////////// - virtual bool setActive(bool active = true) = 0; + virtual bool setActive(bool active = true); //////////////////////////////////////////////////////////// /// \brief Save the current OpenGL render states and matrices @@ -458,6 +458,7 @@ class SFML_GRAPHICS_API RenderTarget : NonCopyable { enum {VertexCacheSize = 4}; + bool enable; ///< Is the cache enabled? bool glStatesSet; ///< Are our internal GL states set yet? bool viewChanged; ///< Has the current view changed since last draw? BlendMode lastBlendMode; ///< Cached blending mode @@ -473,6 +474,7 @@ class SFML_GRAPHICS_API RenderTarget : NonCopyable View m_defaultView; ///< Default view View m_view; ///< Current view StatesCache m_cache; ///< Render states cache + Uint64 m_id; ///< Unique number that identifies the RenderTarget }; } // namespace sf diff --git a/include/SFML/Window/Context.hpp b/include/SFML/Window/Context.hpp index 2f60db8a72..71e2c0a62b 100644 --- a/include/SFML/Window/Context.hpp +++ b/include/SFML/Window/Context.hpp @@ -112,11 +112,26 @@ class SFML_WINDOW_API Context : GlResource, NonCopyable //////////////////////////////////////////////////////////// /// \brief Get the currently active context /// + /// This function will only return sf::Context objects. + /// Contexts created e.g. by RenderTargets or for internal + /// use will not be returned by this function. + /// /// \return The currently active context or NULL if none is active /// //////////////////////////////////////////////////////////// static const Context* getActiveContext(); + //////////////////////////////////////////////////////////// + /// \brief Get the currently active context's ID + /// + /// The context ID is used to identify contexts when + /// managing unshareable OpenGL resources. + /// + /// \return The active context's ID or 0 if no context is currently active + /// + //////////////////////////////////////////////////////////// + static Uint64 getActiveContextId(); + //////////////////////////////////////////////////////////// /// \brief Construct a in-memory context /// diff --git a/include/SFML/Window/GlResource.hpp b/include/SFML/Window/GlResource.hpp index 5a69b66215..f7012426f7 100644 --- a/include/SFML/Window/GlResource.hpp +++ b/include/SFML/Window/GlResource.hpp @@ -37,6 +37,8 @@ namespace sf class Context; +typedef void(*ContextDestroyCallback)(void*); + //////////////////////////////////////////////////////////// /// \brief Base class for classes that require an OpenGL context /// @@ -57,6 +59,19 @@ class SFML_WINDOW_API GlResource //////////////////////////////////////////////////////////// ~GlResource(); + //////////////////////////////////////////////////////////// + /// \brief Register a function to be called when a context is destroyed + /// + /// This is used for internal purposes in order to properly + /// clean up OpenGL resources that cannot be shared between + /// contexts. + /// + /// \param callback Function to be called when a context is destroyed + /// \param arg Argument to pass when calling the function + /// + //////////////////////////////////////////////////////////// + static void registerContextDestroyCallback(ContextDestroyCallback callback, void* arg); + //////////////////////////////////////////////////////////// /// \brief RAII helper class to temporarily lock an available context for use /// diff --git a/src/SFML/Graphics/RenderTarget.cpp b/src/SFML/Graphics/RenderTarget.cpp index dae995859e..7eccf829cc 100644 --- a/src/SFML/Graphics/RenderTarget.cpp +++ b/src/SFML/Graphics/RenderTarget.cpp @@ -32,10 +32,14 @@ #include #include #include +#include +#include +#include #include #include #include #include +#include // GL_QUADS is unavailable on OpenGL ES, thus we need to define GL_QUADS ourselves @@ -48,6 +52,36 @@ namespace { + // Mutex to protect ID generation and our context-RenderTarget-map + sf::Mutex mutex; + + // Unique identifier, used for identifying RenderTargets when + // tracking the currently active RenderTarget within a given context + sf::Uint64 getUniqueId() + { + sf::Lock lock(mutex); + + static sf::Uint64 id = 1; // start at 1, zero is "no RenderTarget" + + return id++; + } + + // Map to help us detect whether a different RenderTarget + // has been activated within a single context + typedef std::map ContextRenderTargetMap; + ContextRenderTargetMap contextRenderTargetMap; + + // Check if a RenderTarget with the given ID is active in the current context + bool isActive(sf::Uint64 id) + { + ContextRenderTargetMap::iterator iter = contextRenderTargetMap.find(sf::Context::getActiveContextId()); + + if ((iter == contextRenderTargetMap.end()) || (iter->second != id)) + return false; + + return true; + } + // Convert an sf::BlendMode::Factor constant to the corresponding OpenGL constant. sf::Uint32 factorToGlConstant(sf::BlendMode::Factor blendFactor) { @@ -94,7 +128,8 @@ namespace sf RenderTarget::RenderTarget() : m_defaultView(), m_view (), -m_cache () +m_cache (), +m_id (getUniqueId()) { m_cache.glStatesSet = false; } @@ -109,7 +144,7 @@ RenderTarget::~RenderTarget() //////////////////////////////////////////////////////////// void RenderTarget::clear(const Color& color) { - if (setActive(true)) + if (isActive(m_id) || setActive(true)) { // Unbind texture to fix RenderTexture preventing clear applyTexture(NULL); @@ -224,7 +259,7 @@ void RenderTarget::draw(const Vertex* vertices, std::size_t vertexCount, } #endif - if (setActive(true)) + if (isActive(m_id) || setActive(true)) { // Check if the vertex count is low enough so that we can pre-transform them bool useVertexCache = (vertexCount <= StatesCache::VertexCacheSize); @@ -245,7 +280,7 @@ void RenderTarget::draw(const Vertex* vertices, std::size_t vertexCount, // Check if texture coordinates array is needed, and update client state accordingly bool enableTexCoordsArray = (states.texture || states.shader); - if (enableTexCoordsArray != m_cache.texCoordsArrayEnabled) + if (!m_cache.enable || (enableTexCoordsArray != m_cache.texCoordsArrayEnabled)) { if (enableTexCoordsArray) glCheck(glEnableClientState(GL_TEXTURE_COORD_ARRAY)); @@ -255,7 +290,7 @@ void RenderTarget::draw(const Vertex* vertices, std::size_t vertexCount, // If we switch between non-cache and cache mode or enable texture // coordinates we need to set up the pointers to the vertices' components - if (!useVertexCache || !m_cache.useVertexCache) + if (!m_cache.enable || !useVertexCache || !m_cache.useVertexCache) { const char* data = reinterpret_cast(vertices); @@ -324,7 +359,7 @@ void RenderTarget::draw(const VertexBuffer& vertexBuffer, std::size_t firstVerte } #endif - if (setActive(true)) + if (isActive(m_id) || setActive(true)) { setupDraw(false, states); @@ -332,7 +367,7 @@ void RenderTarget::draw(const VertexBuffer& vertexBuffer, std::size_t firstVerte VertexBuffer::bind(&vertexBuffer); // Always enable texture coordinates - if (!m_cache.texCoordsArrayEnabled) + if (!m_cache.enable || !m_cache.texCoordsArrayEnabled) glCheck(glEnableClientState(GL_TEXTURE_COORD_ARRAY)); glCheck(glVertexPointer(2, GL_FLOAT, sizeof(Vertex), reinterpret_cast(0))); @@ -353,10 +388,49 @@ void RenderTarget::draw(const VertexBuffer& vertexBuffer, std::size_t firstVerte } +//////////////////////////////////////////////////////////// +bool RenderTarget::setActive(bool active) +{ + // Mark this RenderTarget as active or no longer active in the tracking map + { + sf::Lock lock(mutex); + + Uint64 contextId = Context::getActiveContextId(); + + ContextRenderTargetMap::iterator iter = contextRenderTargetMap.find(contextId); + + if (active) + { + if (iter == contextRenderTargetMap.end()) + { + contextRenderTargetMap[contextId] = m_id; + + m_cache.enable = false; + } + else if (iter->second != m_id) + { + iter->second = m_id; + + m_cache.enable = false; + } + } + else + { + if (iter != contextRenderTargetMap.end()) + contextRenderTargetMap.erase(iter); + + m_cache.enable = false; + } + } + + return true; +} + + //////////////////////////////////////////////////////////// void RenderTarget::pushGLStates() { - if (setActive(true)) + if (isActive(m_id) || setActive(true)) { #ifdef SFML_DEBUG // make sure that the user didn't leave an unchecked OpenGL error @@ -388,7 +462,7 @@ void RenderTarget::pushGLStates() //////////////////////////////////////////////////////////// void RenderTarget::popGLStates() { - if (setActive(true)) + if (isActive(m_id) || setActive(true)) { glCheck(glMatrixMode(GL_PROJECTION)); glCheck(glPopMatrix()); @@ -417,7 +491,7 @@ void RenderTarget::resetGLStates() setActive(false); #endif - if (setActive(true)) + if (isActive(m_id) || setActive(true)) { // Make sure that extensions are initialized priv::ensureExtensionsInit(); @@ -458,6 +532,8 @@ void RenderTarget::resetGLStates() // Set the default view setView(getView()); + + m_cache.enable = true; } } @@ -579,7 +655,7 @@ void RenderTarget::setupDraw(bool useVertexCache, const RenderStates& states) if (useVertexCache) { // Since vertices are transformed, we must use an identity transform to render them - if (!m_cache.useVertexCache) + if (!m_cache.enable || !m_cache.useVertexCache) glCheck(glLoadIdentity()); } else @@ -588,17 +664,30 @@ void RenderTarget::setupDraw(bool useVertexCache, const RenderStates& states) } // Apply the view - if (m_cache.viewChanged) + if (!m_cache.enable || m_cache.viewChanged) applyCurrentView(); // Apply the blend mode - if (states.blendMode != m_cache.lastBlendMode) + if (!m_cache.enable || (states.blendMode != m_cache.lastBlendMode)) applyBlendMode(states.blendMode); // Apply the texture - Uint64 textureId = states.texture ? states.texture->m_cacheId : 0; - if (textureId != m_cache.lastTextureId) + if (!m_cache.enable || (states.texture && states.texture->m_fboAttachment)) + { + // If the texture is an FBO attachment, always rebind it + // in order to inform the OpenGL driver that we want changes + // made to it in other contexts to be visible here as well + // This saves us from having to call glFlush() in + // RenderTextureImplFBO which can be quite costly + // See: https://www.khronos.org/opengl/wiki/Memory_Model applyTexture(states.texture); + } + else + { + Uint64 textureId = states.texture ? states.texture->m_cacheId : 0; + if (textureId != m_cache.lastTextureId) + applyTexture(states.texture); + } // Apply the shader if (states.shader) @@ -630,6 +719,9 @@ void RenderTarget::cleanupDraw(const RenderStates& states) // This prevents a bug where some drivers do not clear RenderTextures properly. if (states.texture && states.texture->m_fboAttachment) applyTexture(NULL); + + // Re-enable the cache at the end of the draw if it was disabled + m_cache.enable = true; } } // namespace sf diff --git a/src/SFML/Graphics/RenderTexture.cpp b/src/SFML/Graphics/RenderTexture.cpp index a52e50639e..0805627b73 100644 --- a/src/SFML/Graphics/RenderTexture.cpp +++ b/src/SFML/Graphics/RenderTexture.cpp @@ -147,7 +147,13 @@ bool RenderTexture::generateMipmap() //////////////////////////////////////////////////////////// bool RenderTexture::setActive(bool active) { - return m_impl && m_impl->activate(active); + bool result = m_impl && m_impl->activate(active); + + // Update RenderTarget tracking + if (result) + RenderTarget::setActive(active); + + return result; } @@ -155,7 +161,7 @@ bool RenderTexture::setActive(bool active) void RenderTexture::display() { // Update the target texture - if (setActive(true)) + if (priv::RenderTextureImplFBO::isAvailable() || setActive(true)) { m_impl->updateTexture(m_texture.m_texture); m_texture.m_pixelsFlipped = true; diff --git a/src/SFML/Graphics/RenderTextureImplFBO.cpp b/src/SFML/Graphics/RenderTextureImplFBO.cpp index c424d33fe7..301aa04366 100644 --- a/src/SFML/Graphics/RenderTextureImplFBO.cpp +++ b/src/SFML/Graphics/RenderTextureImplFBO.cpp @@ -28,7 +28,67 @@ #include #include #include +#include +#include #include +#include +#include + + +namespace +{ + // Set to track all active FBO mappings + // This is used to free active FBOs while their owning + // RenderTextureImplFBO is still alive + std::set*> frameBuffers; + + // Set to track all stale FBOs + // This is used to free stale FBOs after their owning + // RenderTextureImplFBO has already been destroyed + // An FBO cannot be destroyed until it's containing context + // becomes active, so the destruction of the RenderTextureImplFBO + // has to be decoupled from the destruction of the FBOs themselves + std::set > staleFrameBuffers; + + // Mutex to protect both active and stale frame buffer sets + sf::Mutex mutex; + + // Callback that is called every time a context is destroyed + void contextDestroyCallback(void* arg) + { + sf::Lock lock(mutex); + + sf::Uint64 contextId = sf::Context::getActiveContextId(); + + // Destroy active frame buffer objects + for (std::set*>::iterator frameBuffersIter = frameBuffers.begin(); frameBuffersIter != frameBuffers.end(); ++frameBuffersIter) + { + for (std::map::iterator iter = (*frameBuffersIter)->begin(); iter != (*frameBuffersIter)->end(); ++iter) + { + if (iter->first == contextId) + { + GLuint frameBuffer = static_cast(iter->second); + glCheck(GLEXT_glDeleteFramebuffers(1, &frameBuffer)); + + // Erase the entry from the RenderTextureImplFBO's map + (*frameBuffersIter)->erase(iter); + + break; + } + } + } + + // Destroy stale frame buffer objects + for (std::set >::iterator iter = staleFrameBuffers.begin(); iter != staleFrameBuffers.end(); ++iter) + { + if (iter->first == contextId) + { + GLuint frameBuffer = static_cast(iter->second); + glCheck(GLEXT_glDeleteFramebuffers(1, &frameBuffer)); + } + } + } +} namespace sf @@ -37,22 +97,36 @@ namespace priv { //////////////////////////////////////////////////////////// RenderTextureImplFBO::RenderTextureImplFBO() : -m_context (NULL), -m_frameBuffer (0), -m_multisampleFrameBuffer(0), -m_depthStencilBuffer (0), -m_colorBuffer (0), -m_width (0), -m_height (0) +m_depthStencilBuffer(0), +m_colorBuffer (0), +m_width (0), +m_height (0), +m_context (NULL), +m_textureId (0), +m_multisample (false), +m_stencil (false) { + Lock lock(mutex); + + // Register the context destruction callback + registerContextDestroyCallback(contextDestroyCallback, 0); + // Insert the new frame buffer mapping into the set of all active mappings + frameBuffers.insert(&m_frameBuffers); + frameBuffers.insert(&m_multisampleFrameBuffers); } //////////////////////////////////////////////////////////// RenderTextureImplFBO::~RenderTextureImplFBO() { - m_context->setActive(true); + TransientContextLock contextLock; + + Lock lock(mutex); + + // Remove the frame buffer mapping from the set of all active mappings + frameBuffers.erase(&m_frameBuffers); + frameBuffers.erase(&m_multisampleFrameBuffers); // Destroy the color buffer if (m_colorBuffer) @@ -68,21 +142,17 @@ RenderTextureImplFBO::~RenderTextureImplFBO() glCheck(GLEXT_glDeleteRenderbuffers(1, &depthStencilBuffer)); } - // Destroy the multisample frame buffer - if (m_multisampleFrameBuffer) - { - GLuint multisampleFrameBuffer = static_cast(m_multisampleFrameBuffer); - glCheck(GLEXT_glDeleteFramebuffers(1, &multisampleFrameBuffer)); - } + // Move all frame buffer objects to stale set + for (std::map::iterator iter = m_frameBuffers.begin(); iter != m_frameBuffers.end(); ++iter) + staleFrameBuffers.insert(std::make_pair(iter->first, iter->second)); - // Destroy the frame buffer - if (m_frameBuffer) - { - GLuint frameBuffer = static_cast(m_frameBuffer); - glCheck(GLEXT_glDeleteFramebuffers(1, &frameBuffer)); - } + for (std::map::iterator iter = m_multisampleFrameBuffers.begin(); iter != m_multisampleFrameBuffers.end(); ++iter) + staleFrameBuffers.insert(std::make_pair(iter->first, iter->second)); + + // Clean up FBOs + contextDestroyCallback(0); - // Delete the context + // Delete the backup context if we had to create one delete m_context; } @@ -102,6 +172,8 @@ bool RenderTextureImplFBO::isAvailable() //////////////////////////////////////////////////////////// unsigned int RenderTextureImplFBO::getMaximumAntialiasingLevel() { + TransientContextLock lock; + GLint samples = 0; #ifndef SFML_OPENGL_ES @@ -114,6 +186,13 @@ unsigned int RenderTextureImplFBO::getMaximumAntialiasingLevel() } +//////////////////////////////////////////////////////////// +void RenderTextureImplFBO::unbind() +{ + glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, 0)); +} + + //////////////////////////////////////////////////////////// bool RenderTextureImplFBO::create(unsigned int width, unsigned int height, unsigned int textureId, const ContextSettings& settings) { @@ -121,191 +200,264 @@ bool RenderTextureImplFBO::create(unsigned int width, unsigned int height, unsig m_width = width; m_height = height; - // Disable creation of depth/stencil surfaces in the context - ContextSettings contextSettings(settings); - contextSettings.depthBits = 0; - contextSettings.stencilBits = 0; - contextSettings.antialiasingLevel = 0; - contextSettings.sRgbCapable = false; + { + TransientContextLock lock; - // Create the context - m_context = new Context(contextSettings, 1, 1); + // Make sure that extensions are initialized + priv::ensureExtensionsInit(); - if (settings.antialiasingLevel && !(GLEXT_framebuffer_multisample && GLEXT_framebuffer_blit)) - return false; + if (settings.antialiasingLevel && !(GLEXT_framebuffer_multisample && GLEXT_framebuffer_blit)) + return false; - if (settings.stencilBits && !GLEXT_packed_depth_stencil) - return false; + if (settings.stencilBits && !GLEXT_packed_depth_stencil) + return false; #ifndef SFML_OPENGL_ES - // Check if the requested anti-aliasing level is supported - if (settings.antialiasingLevel) - { - GLint samples = 0; - glCheck(glGetIntegerv(GLEXT_GL_MAX_SAMPLES, &samples)); - - if (settings.antialiasingLevel > static_cast(samples)) + // Check if the requested anti-aliasing level is supported + if (settings.antialiasingLevel) { - err() << "Impossible to create render texture (unsupported anti-aliasing level)"; - err() << " Requested: " << settings.antialiasingLevel << " Maximum supported: " << samples << std::endl; - return false; + GLint samples = 0; + glCheck(glGetIntegerv(GLEXT_GL_MAX_SAMPLES, &samples)); + + if (settings.antialiasingLevel > static_cast(samples)) + { + err() << "Impossible to create render texture (unsupported anti-aliasing level)"; + err() << " Requested: " << settings.antialiasingLevel << " Maximum supported: " << samples << std::endl; + return false; + } } - } #endif - if (!settings.antialiasingLevel) - { - // Create the framebuffer object - GLuint frameBuffer = 0; - glCheck(GLEXT_glGenFramebuffers(1, &frameBuffer)); - m_frameBuffer = static_cast(frameBuffer); - if (!m_frameBuffer) - { - err() << "Impossible to create render texture (failed to create the frame buffer object)" << std::endl; - return false; - } - glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, m_frameBuffer)); - // Create the depth/stencil buffer if requested - if (settings.stencilBits) + if (!settings.antialiasingLevel) { + // Create the depth/stencil buffer if requested + if (settings.stencilBits) + { #ifndef SFML_OPENGL_ES - GLuint depthStencil = 0; - glCheck(GLEXT_glGenRenderbuffers(1, &depthStencil)); - m_depthStencilBuffer = static_cast(depthStencil); - if (!m_depthStencilBuffer) - { - err() << "Impossible to create render texture (failed to create the attached depth/stencil buffer)" << std::endl; - return false; - } - glCheck(GLEXT_glBindRenderbuffer(GLEXT_GL_RENDERBUFFER, m_depthStencilBuffer)); - glCheck(GLEXT_glRenderbufferStorage(GLEXT_GL_RENDERBUFFER, GLEXT_GL_DEPTH24_STENCIL8, width, height)); - glCheck(GLEXT_glFramebufferRenderbuffer(GLEXT_GL_FRAMEBUFFER, GLEXT_GL_DEPTH_ATTACHMENT, GLEXT_GL_RENDERBUFFER, m_depthStencilBuffer)); - glCheck(GLEXT_glFramebufferRenderbuffer(GLEXT_GL_FRAMEBUFFER, GLEXT_GL_STENCIL_ATTACHMENT, GLEXT_GL_RENDERBUFFER, m_depthStencilBuffer)); + GLuint depthStencil = 0; + glCheck(GLEXT_glGenRenderbuffers(1, &depthStencil)); + m_depthStencilBuffer = static_cast(depthStencil); + if (!m_depthStencilBuffer) + { + err() << "Impossible to create render texture (failed to create the attached depth/stencil buffer)" << std::endl; + return false; + } + glCheck(GLEXT_glBindRenderbuffer(GLEXT_GL_RENDERBUFFER, m_depthStencilBuffer)); + glCheck(GLEXT_glRenderbufferStorage(GLEXT_GL_RENDERBUFFER, GLEXT_GL_DEPTH24_STENCIL8, width, height)); + #else - err() << "Impossible to create render texture (failed to create the attached depth/stencil buffer)" << std::endl; - return false; + err() << "Impossible to create render texture (failed to create the attached depth/stencil buffer)" << std::endl; + return false; #endif // SFML_OPENGL_ES + m_stencil = true; + + } + else if (settings.depthBits) + { + GLuint depthStencil = 0; + glCheck(GLEXT_glGenRenderbuffers(1, &depthStencil)); + m_depthStencilBuffer = static_cast(depthStencil); + if (!m_depthStencilBuffer) + { + err() << "Impossible to create render texture (failed to create the attached depth buffer)" << std::endl; + return false; + } + glCheck(GLEXT_glBindRenderbuffer(GLEXT_GL_RENDERBUFFER, m_depthStencilBuffer)); + glCheck(GLEXT_glRenderbufferStorage(GLEXT_GL_RENDERBUFFER, GLEXT_GL_DEPTH_COMPONENT, width, height)); + } } - else if (settings.depthBits) + else { - GLuint depthStencil = 0; - glCheck(GLEXT_glGenRenderbuffers(1, &depthStencil)); - m_depthStencilBuffer = static_cast(depthStencil); - if (!m_depthStencilBuffer) + +#ifndef SFML_OPENGL_ES + + // Create the multisample color buffer + GLuint color = 0; + glCheck(GLEXT_glGenRenderbuffers(1, &color)); + m_colorBuffer = static_cast(color); + if (!m_colorBuffer) { - err() << "Impossible to create render texture (failed to create the attached depth buffer)" << std::endl; + err() << "Impossible to create render texture (failed to create the attached multisample color buffer)" << std::endl; return false; } - glCheck(GLEXT_glBindRenderbuffer(GLEXT_GL_RENDERBUFFER, m_depthStencilBuffer)); - glCheck(GLEXT_glRenderbufferStorage(GLEXT_GL_RENDERBUFFER, GLEXT_GL_DEPTH_COMPONENT, width, height)); - glCheck(GLEXT_glFramebufferRenderbuffer(GLEXT_GL_FRAMEBUFFER, GLEXT_GL_DEPTH_ATTACHMENT, GLEXT_GL_RENDERBUFFER, m_depthStencilBuffer)); - } + glCheck(GLEXT_glBindRenderbuffer(GLEXT_GL_RENDERBUFFER, m_colorBuffer)); + glCheck(GLEXT_glRenderbufferStorageMultisample(GLEXT_GL_RENDERBUFFER, settings.antialiasingLevel, GL_RGBA, width, height)); + + // Create the multisample depth/stencil buffer if requested + if (settings.stencilBits) + { + GLuint depthStencil = 0; + glCheck(GLEXT_glGenRenderbuffers(1, &depthStencil)); + m_depthStencilBuffer = static_cast(depthStencil); + if (!m_depthStencilBuffer) + { + err() << "Impossible to create render texture (failed to create the attached multisample depth/stencil buffer)" << std::endl; + return false; + } + glCheck(GLEXT_glBindRenderbuffer(GLEXT_GL_RENDERBUFFER, m_depthStencilBuffer)); + glCheck(GLEXT_glRenderbufferStorageMultisample(GLEXT_GL_RENDERBUFFER, settings.antialiasingLevel, GLEXT_GL_DEPTH24_STENCIL8, width, height)); + + m_stencil = true; + } + else if (settings.depthBits) + { + GLuint depthStencil = 0; + glCheck(GLEXT_glGenRenderbuffers(1, &depthStencil)); + m_depthStencilBuffer = static_cast(depthStencil); + if (!m_depthStencilBuffer) + { + err() << "Impossible to create render texture (failed to create the attached multisample depth buffer)" << std::endl; + return false; + } + glCheck(GLEXT_glBindRenderbuffer(GLEXT_GL_RENDERBUFFER, m_depthStencilBuffer)); + glCheck(GLEXT_glRenderbufferStorageMultisample(GLEXT_GL_RENDERBUFFER, settings.antialiasingLevel, GLEXT_GL_DEPTH_COMPONENT, width, height)); + } - // Link the texture to the frame buffer - glCheck(GLEXT_glFramebufferTexture2D(GLEXT_GL_FRAMEBUFFER, GLEXT_GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureId, 0)); +#else - // A final check, just to be sure... - GLenum status; - glCheck(status = GLEXT_glCheckFramebufferStatus(GLEXT_GL_FRAMEBUFFER)); - if (status != GLEXT_GL_FRAMEBUFFER_COMPLETE) - { - glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, 0)); - err() << "Impossible to create render texture (frame buffer incomplete)" << std::endl; + err() << "Impossible to create render texture (failed to create the multisample render buffers)" << std::endl; return false; + +#endif // SFML_OPENGL_ES + + m_multisample = true; + } + } + + // Save our texture ID in order to be able to attach it to an FBO at any time + m_textureId = textureId; + + // We can't create an FBO now if there is no active context + if (!Context::getActiveContextId()) + return true; + +#ifndef SFML_OPENGL_ES + + // Save the current bindings so we can restore them after we are done + GLint readFramebuffer = 0; + GLint drawFramebuffer = 0; + + glCheck(glGetIntegerv(GLEXT_GL_READ_FRAMEBUFFER_BINDING, &readFramebuffer)); + glCheck(glGetIntegerv(GLEXT_GL_DRAW_FRAMEBUFFER_BINDING, &drawFramebuffer)); + + if (createFrameBuffer()) + { + // Restore previously bound framebuffers + glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_READ_FRAMEBUFFER, readFramebuffer)); + glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_DRAW_FRAMEBUFFER, drawFramebuffer)); + + return true; + } + +#else + + // Save the current binding so we can restore them after we are done + GLint frameBuffer = 0; + + glCheck(glGetIntegerv(GLEXT_GL_FRAMEBUFFER_BINDING, &frameBuffer)); + + if (createFrameBuffer()) + { + // Restore previously bound framebuffer + glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, frameBuffer)); return true; } - else + +#endif + + return false; +} + + +//////////////////////////////////////////////////////////// +bool RenderTextureImplFBO::createFrameBuffer() +{ + // Create the framebuffer object + GLuint frameBuffer = 0; + glCheck(GLEXT_glGenFramebuffers(1, &frameBuffer)); + + if (!frameBuffer) + { + err() << "Impossible to create render texture (failed to create the frame buffer object)" << std::endl; + return false; + } + glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, frameBuffer)); + + // Link the depth/stencil renderbuffer to the frame buffer + if (!m_multisample && m_depthStencilBuffer) { + glCheck(GLEXT_glFramebufferRenderbuffer(GLEXT_GL_FRAMEBUFFER, GLEXT_GL_DEPTH_ATTACHMENT, GLEXT_GL_RENDERBUFFER, m_depthStencilBuffer)); #ifndef SFML_OPENGL_ES - // Create the framebuffer object - GLuint frameBuffer = 0; - glCheck(GLEXT_glGenFramebuffers(1, &frameBuffer)); - m_frameBuffer = static_cast(frameBuffer); - if (!m_frameBuffer) + if (m_stencil) { - err() << "Impossible to create render texture (failed to create the frame buffer object)" << std::endl; - return false; + glCheck(GLEXT_glFramebufferRenderbuffer(GLEXT_GL_FRAMEBUFFER, GLEXT_GL_STENCIL_ATTACHMENT, GLEXT_GL_RENDERBUFFER, m_depthStencilBuffer)); } - glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, m_frameBuffer)); - // Link the texture to the frame buffer - glCheck(GLEXT_glFramebufferTexture2D(GLEXT_GL_FRAMEBUFFER, GLEXT_GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureId, 0)); +#endif - // A final check, just to be sure... - GLenum status; - glCheck(status = GLEXT_glCheckFramebufferStatus(GLEXT_GL_FRAMEBUFFER)); - if (status != GLEXT_GL_FRAMEBUFFER_COMPLETE) - { - glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, 0)); - err() << "Impossible to create render texture (frame buffer incomplete)" << std::endl; - return false; - } + } + + // Link the texture to the frame buffer + glCheck(GLEXT_glFramebufferTexture2D(GLEXT_GL_FRAMEBUFFER, GLEXT_GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_textureId, 0)); + + // A final check, just to be sure... + GLenum status; + glCheck(status = GLEXT_glCheckFramebufferStatus(GLEXT_GL_FRAMEBUFFER)); + if (status != GLEXT_GL_FRAMEBUFFER_COMPLETE) + { + glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, 0)); + glCheck(GLEXT_glDeleteFramebuffers(1, &frameBuffer)); + err() << "Impossible to create render texture (failed to link the target texture to the frame buffer)" << std::endl; + return false; + } + + { + Lock lock(mutex); + + // Insert the FBO into our map + m_frameBuffers.insert(std::make_pair(Context::getActiveContextId(), static_cast(frameBuffer))); + } +#ifndef SFML_OPENGL_ES + + if (m_multisample) + { // Create the multisample framebuffer object - frameBuffer = 0; - glCheck(GLEXT_glGenFramebuffers(1, &frameBuffer)); - m_multisampleFrameBuffer = static_cast(frameBuffer); - if (!m_multisampleFrameBuffer) + GLuint multisampleFrameBuffer = 0; + glCheck(GLEXT_glGenFramebuffers(1, &multisampleFrameBuffer)); + + if (!multisampleFrameBuffer) { err() << "Impossible to create render texture (failed to create the multisample frame buffer object)" << std::endl; return false; } - glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, m_multisampleFrameBuffer)); + glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, multisampleFrameBuffer)); - // Create the multisample color buffer - GLuint color = 0; - glCheck(GLEXT_glGenRenderbuffers(1, &color)); - m_colorBuffer = static_cast(color); - if (!m_colorBuffer) - { - err() << "Impossible to create render texture (failed to create the attached multisample color buffer)" << std::endl; - return false; - } + // Link the multisample color buffer to the frame buffer glCheck(GLEXT_glBindRenderbuffer(GLEXT_GL_RENDERBUFFER, m_colorBuffer)); - glCheck(GLEXT_glRenderbufferStorageMultisample(GLEXT_GL_RENDERBUFFER, settings.antialiasingLevel, GL_RGBA, width, height)); glCheck(GLEXT_glFramebufferRenderbuffer(GLEXT_GL_FRAMEBUFFER, GLEXT_GL_COLOR_ATTACHMENT0, GLEXT_GL_RENDERBUFFER, m_colorBuffer)); - // Create the multisample depth/stencil buffer if requested - if (settings.stencilBits) + // Link the depth/stencil renderbuffer to the frame buffer + if (m_depthStencilBuffer) { - GLuint depthStencil = 0; - glCheck(GLEXT_glGenRenderbuffers(1, &depthStencil)); - m_depthStencilBuffer = static_cast(depthStencil); - if (!m_depthStencilBuffer) - { - err() << "Impossible to create render texture (failed to create the attached multisample depth/stencil buffer)" << std::endl; - return false; - } - glCheck(GLEXT_glBindRenderbuffer(GLEXT_GL_RENDERBUFFER, m_depthStencilBuffer)); - glCheck(GLEXT_glRenderbufferStorageMultisample(GLEXT_GL_RENDERBUFFER, settings.antialiasingLevel, GLEXT_GL_DEPTH24_STENCIL8, width, height)); glCheck(GLEXT_glFramebufferRenderbuffer(GLEXT_GL_FRAMEBUFFER, GLEXT_GL_DEPTH_ATTACHMENT, GLEXT_GL_RENDERBUFFER, m_depthStencilBuffer)); - glCheck(GLEXT_glFramebufferRenderbuffer(GLEXT_GL_FRAMEBUFFER, GLEXT_GL_STENCIL_ATTACHMENT, GLEXT_GL_RENDERBUFFER, m_depthStencilBuffer)); - } - else if (settings.depthBits) - { - GLuint depthStencil = 0; - glCheck(GLEXT_glGenRenderbuffers(1, &depthStencil)); - m_depthStencilBuffer = static_cast(depthStencil); - if (!m_depthStencilBuffer) + + if (m_stencil) { - err() << "Impossible to create render texture (failed to create the attached multisample depth buffer)" << std::endl; - return false; + glCheck(GLEXT_glFramebufferRenderbuffer(GLEXT_GL_FRAMEBUFFER, GLEXT_GL_STENCIL_ATTACHMENT, GLEXT_GL_RENDERBUFFER, m_depthStencilBuffer)); } - glCheck(GLEXT_glBindRenderbuffer(GLEXT_GL_RENDERBUFFER, m_depthStencilBuffer)); - glCheck(GLEXT_glRenderbufferStorageMultisample(GLEXT_GL_RENDERBUFFER, settings.antialiasingLevel, GLEXT_GL_DEPTH_COMPONENT, width, height)); - glCheck(GLEXT_glFramebufferRenderbuffer(GLEXT_GL_FRAMEBUFFER, GLEXT_GL_DEPTH_ATTACHMENT, GLEXT_GL_RENDERBUFFER, m_depthStencilBuffer)); } // A final check, just to be sure... @@ -313,46 +465,123 @@ bool RenderTextureImplFBO::create(unsigned int width, unsigned int height, unsig if (status != GLEXT_GL_FRAMEBUFFER_COMPLETE) { glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, 0)); - err() << "Impossible to create render texture (multisample frame buffer incomplete)" << std::endl; + glCheck(GLEXT_glDeleteFramebuffers(1, &multisampleFrameBuffer)); + err() << "Impossible to create render texture (failed to link the render buffers to the multisample frame buffer)" << std::endl; return false; } - return true; - -#else + { + Lock lock(mutex); - err() << "Impossible to create render texture (failed to create the multisample frame buffer object)" << std::endl; - return false; + // Insert the FBO into our map + m_multisampleFrameBuffers.insert(std::make_pair(Context::getActiveContextId(), static_cast(multisampleFrameBuffer))); + } + } -#endif // SFML_OPENGL_ES +#endif - } + return true; } //////////////////////////////////////////////////////////// bool RenderTextureImplFBO::activate(bool active) { - return m_context->setActive(active); + // Unbind the FBO if requested + if (!active) + { + glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, 0)); + return true; + } + + Uint64 contextId = Context::getActiveContextId(); + + // In the odd case we have to activate and there is no active + // context yet, we have to create one + if (!contextId) + { + if (!m_context) + m_context = new Context; + + m_context->setActive(true); + + contextId = Context::getActiveContextId(); + + if (!contextId) + { + err() << "Impossible to activate render texture (failed to create backup context)" << std::endl; + + return false; + } + } + + // Lookup the FBO corresponding to the currently active context + // If none is found, there is no FBO corresponding to the + // currently active context so we will have to create a new FBO + { + Lock lock(mutex); + + std::map::iterator iter; + + if (m_multisample) + { + iter = m_multisampleFrameBuffers.find(contextId); + + if (iter != m_multisampleFrameBuffers.end()) + { + glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, iter->second)); + + return true; + } + } + else + { + iter = m_frameBuffers.find(contextId); + + if (iter != m_frameBuffers.end()) + { + glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, iter->second)); + + return true; + } + } + } + + return createFrameBuffer(); } //////////////////////////////////////////////////////////// void RenderTextureImplFBO::updateTexture(unsigned int) { + // If multisampling is enabled, we need to resolve by blitting + // from our FBO with multisample renderbuffer attachments + // to our FBO to which our target texture is attached #ifndef SFML_OPENGL_ES - if (m_multisampleFrameBuffer) + // In case of multisampling, make sure both FBOs + // are already available within the current context + if (m_multisample && m_width && m_height && activate(true)) { - glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_DRAW_FRAMEBUFFER, m_frameBuffer)); - glCheck(GLEXT_glBlitFramebuffer(0, 0, m_width, m_height, 0, 0, m_width, m_height, GL_COLOR_BUFFER_BIT, GL_NEAREST)); - glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_DRAW_FRAMEBUFFER, m_multisampleFrameBuffer)); + Uint64 contextId = Context::getActiveContextId(); + + Lock lock(mutex); + + std::map::iterator iter = m_frameBuffers.find(contextId); + std::map::iterator multisampleIter = m_multisampleFrameBuffers.find(contextId); + + if ((iter != m_frameBuffers.end()) && (multisampleIter != m_multisampleFrameBuffers.end())) + { + // Set up the blit target (draw framebuffer) and blit (from the read framebuffer, our multisample FBO) + glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_DRAW_FRAMEBUFFER, iter->second)); + glCheck(GLEXT_glBlitFramebuffer(0, 0, m_width, m_height, 0, 0, m_width, m_height, GL_COLOR_BUFFER_BIT, GL_NEAREST)); + glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_DRAW_FRAMEBUFFER, multisampleIter->second)); + } } #endif // SFML_OPENGL_ES - glCheck(glFlush()); } } // namespace priv diff --git a/src/SFML/Graphics/RenderTextureImplFBO.hpp b/src/SFML/Graphics/RenderTextureImplFBO.hpp index d2bb71c1f9..f83074ef36 100644 --- a/src/SFML/Graphics/RenderTextureImplFBO.hpp +++ b/src/SFML/Graphics/RenderTextureImplFBO.hpp @@ -31,6 +31,7 @@ #include #include #include +#include namespace sf @@ -74,6 +75,12 @@ class RenderTextureImplFBO : public RenderTextureImpl, GlResource //////////////////////////////////////////////////////////// static unsigned int getMaximumAntialiasingLevel(); + //////////////////////////////////////////////////////////// + /// \brief Unbind the currently bound FBO + /// + //////////////////////////////////////////////////////////// + static void unbind(); + private: //////////////////////////////////////////////////////////// @@ -89,6 +96,14 @@ class RenderTextureImplFBO : public RenderTextureImpl, GlResource //////////////////////////////////////////////////////////// virtual bool create(unsigned int width, unsigned int height, unsigned int textureId, const ContextSettings& settings); + //////////////////////////////////////////////////////////// + /// \brief Create an FBO in the current context + /// + /// \return True if creation has been successful + /// + //////////////////////////////////////////////////////////// + bool createFrameBuffer(); + //////////////////////////////////////////////////////////// /// \brief Activate or deactivate the render texture for rendering /// @@ -110,13 +125,16 @@ class RenderTextureImplFBO : public RenderTextureImpl, GlResource //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// - Context* m_context; ///< Needs a separate OpenGL context for not messing up the other ones - unsigned int m_frameBuffer; ///< OpenGL frame buffer object - unsigned int m_multisampleFrameBuffer; ///< Optional OpenGL frame buffer object with multisample attachments - unsigned int m_depthStencilBuffer; ///< Optional depth/stencil buffer attached to the frame buffer - unsigned int m_colorBuffer; ///< Optional multisample color buffer attached to the frame buffer - unsigned int m_width; ///< Width of the attachments - unsigned int m_height; ///< Height of the attachments + std::map m_frameBuffers; ///< OpenGL frame buffer objects per context + std::map m_multisampleFrameBuffers; ///< Optional per-context OpenGL frame buffer objects with multisample attachments + unsigned int m_depthStencilBuffer; ///< Optional depth/stencil buffer attached to the frame buffer + unsigned int m_colorBuffer; ///< Optional multisample color buffer attached to the frame buffer + unsigned int m_width; ///< Width of the attachments + unsigned int m_height; ///< Height of the attachments + Context* m_context; ///< Backup OpenGL context, used when none already exist + unsigned int m_textureId; ///< The ID of the texture to attach to the FBO + bool m_multisample; ///< Whether we have to create a multisample frame buffer as well + bool m_stencil; ///< Whether we have stencil attachment }; } // namespace priv diff --git a/src/SFML/Graphics/RenderWindow.cpp b/src/SFML/Graphics/RenderWindow.cpp index f174a3dbb0..906363b3fa 100644 --- a/src/SFML/Graphics/RenderWindow.cpp +++ b/src/SFML/Graphics/RenderWindow.cpp @@ -27,6 +27,8 @@ //////////////////////////////////////////////////////////// #include #include +#include +#include namespace sf @@ -71,7 +73,22 @@ Vector2u RenderWindow::getSize() const //////////////////////////////////////////////////////////// bool RenderWindow::setActive(bool active) { - return Window::setActive(active); + bool result = Window::setActive(active); + + // Update RenderTarget tracking + if (result) + RenderTarget::setActive(active); + + // If FBOs are available, make sure none are bound when we + // try to draw to the default framebuffer of the RenderWindow + if (result && priv::RenderTextureImplFBO::isAvailable()) + { + priv::RenderTextureImplFBO::unbind(); + + return true; + } + + return result; } diff --git a/src/SFML/Window/Context.cpp b/src/SFML/Window/Context.cpp index 46275e3e4f..4e0a4b8a4b 100644 --- a/src/SFML/Window/Context.cpp +++ b/src/SFML/Window/Context.cpp @@ -80,6 +80,13 @@ const Context* Context::getActiveContext() } +//////////////////////////////////////////////////////////// +Uint64 Context::getActiveContextId() +{ + return priv::GlContext::getActiveContextId(); +} + + //////////////////////////////////////////////////////////// bool Context::isExtensionAvailable(const char* name) { diff --git a/src/SFML/Window/EglContext.cpp b/src/SFML/Window/EglContext.cpp index 91d02f93bd..fb91cdaca8 100644 --- a/src/SFML/Window/EglContext.cpp +++ b/src/SFML/Window/EglContext.cpp @@ -150,6 +150,9 @@ m_config (NULL) //////////////////////////////////////////////////////////// EglContext::~EglContext() { + // Notify unshared OpenGL resources of context destruction + cleanupUnsharedResources(); + // Deactivate the current context EGLContext currentContext = eglCheck(eglGetCurrentContext()); diff --git a/src/SFML/Window/GlContext.cpp b/src/SFML/Window/GlContext.cpp index 07a90ad7a2..9b6c1f70b5 100644 --- a/src/SFML/Window/GlContext.cpp +++ b/src/SFML/Window/GlContext.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -146,6 +147,16 @@ namespace // The hidden, inactive context that will be shared with all other contexts ContextType* sharedContext = NULL; + // Unique identifier, used for identifying contexts when managing unshareable OpenGL resources + sf::Uint64 id = 1; // start at 1, zero is "no context" + + // Set containing callback functions to be called whenever a + // context is going to be destroyed + // Unshareable OpenGL resources rely on this to clean up properly + // whenever a context containing them is destroyed + typedef std::set > ContextDestroyCallbacks; + ContextDestroyCallbacks contextDestroyCallbacks; + // This structure contains all the state necessary to // track TransientContext usage struct TransientContext : private sf::NonCopyable @@ -325,6 +336,13 @@ void GlContext::cleanupResource() } +//////////////////////////////////////////////////////////// +void GlContext::registerContextDestroyCallback(ContextDestroyCallback callback, void* arg) +{ + contextDestroyCallbacks.insert(std::make_pair(callback, arg)); +} + + //////////////////////////////////////////////////////////// void GlContext::acquireTransientContext() { @@ -473,6 +491,13 @@ GlFunctionPointer GlContext::getFunction(const char* name) } +//////////////////////////////////////////////////////////// +Uint64 GlContext::getActiveContextId() +{ + return currentContext ? currentContext->m_id : 0; +} + + //////////////////////////////////////////////////////////// GlContext::~GlContext() { @@ -546,7 +571,8 @@ bool GlContext::setActive(bool active) //////////////////////////////////////////////////////////// -GlContext::GlContext() +GlContext::GlContext() : +m_id(id++) { // Nothing to do } @@ -581,6 +607,29 @@ int GlContext::evaluateFormat(unsigned int bitsPerPixel, const ContextSettings& } +//////////////////////////////////////////////////////////// +void GlContext::cleanupUnsharedResources() +{ + // Save the current context so we can restore it later + GlContext* contextToRestore = currentContext; + + // If this context is already active there is no need to save it + if (contextToRestore == this) + contextToRestore = NULL; + + // Make this context active so resources can be freed + setActive(true); + + // Call the registered destruction callbacks + for (ContextDestroyCallbacks::iterator iter = contextDestroyCallbacks.begin(); iter != contextDestroyCallbacks.end(); ++iter) + iter->first(iter->second); + + // Make the originally active context active again + if (contextToRestore) + contextToRestore->setActive(true); +} + + //////////////////////////////////////////////////////////// void GlContext::initialize(const ContextSettings& requestedSettings) { diff --git a/src/SFML/Window/GlContext.hpp b/src/SFML/Window/GlContext.hpp index cf28c61cbd..0b66965e47 100644 --- a/src/SFML/Window/GlContext.hpp +++ b/src/SFML/Window/GlContext.hpp @@ -31,6 +31,7 @@ #include #include #include +#include #include @@ -69,6 +70,19 @@ class GlContext : NonCopyable //////////////////////////////////////////////////////////// static void cleanupResource(); + //////////////////////////////////////////////////////////// + /// \brief Register a function to be called when a context is destroyed + /// + /// This is used for internal purposes in order to properly + /// clean up OpenGL resources that cannot be shared bwteen + /// contexts. + /// + /// \param callback Function to be called when a context is destroyed + /// \param arg Argument to pass when calling the function + /// + //////////////////////////////////////////////////////////// + static void registerContextDestroyCallback(ContextDestroyCallback callback, void* arg); + //////////////////////////////////////////////////////////// /// \brief Acquires a context for short-term use on the current thread /// @@ -143,6 +157,17 @@ class GlContext : NonCopyable //////////////////////////////////////////////////////////// static GlFunctionPointer getFunction(const char* name); + //////////////////////////////////////////////////////////// + /// \brief Get the currently active context's ID + /// + /// The context ID is used to identify contexts when + /// managing unshareable OpenGL resources. + /// + /// \return The active context's ID or 0 if no context is currently active + /// + //////////////////////////////////////////////////////////// + static Uint64 getActiveContextId(); + //////////////////////////////////////////////////////////// /// \brief Destructor /// @@ -217,6 +242,12 @@ class GlContext : NonCopyable //////////////////////////////////////////////////////////// virtual bool makeCurrent(bool current) = 0; + //////////////////////////////////////////////////////////// + /// \brief Notify unshared GlResources of context destruction + /// + //////////////////////////////////////////////////////////// + void cleanupUnsharedResources(); + //////////////////////////////////////////////////////////// /// \brief Evaluate a pixel format configuration /// @@ -259,6 +290,11 @@ class GlContext : NonCopyable /// //////////////////////////////////////////////////////////// void checkSettings(const ContextSettings& requestedSettings); + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + const Uint64 m_id; ///< Unique number that identifies the context }; } // namespace priv diff --git a/src/SFML/Window/GlResource.cpp b/src/SFML/Window/GlResource.cpp index 7af4a140c9..64fc88c52b 100644 --- a/src/SFML/Window/GlResource.cpp +++ b/src/SFML/Window/GlResource.cpp @@ -45,6 +45,13 @@ GlResource::~GlResource() } +//////////////////////////////////////////////////////////// +void GlResource::registerContextDestroyCallback(ContextDestroyCallback callback, void* arg) +{ + priv::GlContext::registerContextDestroyCallback(callback, arg); +} + + //////////////////////////////////////////////////////////// GlResource::TransientContextLock::TransientContextLock() { diff --git a/src/SFML/Window/OSX/SFContext.mm b/src/SFML/Window/OSX/SFContext.mm index 653756903d..cf74de91bb 100644 --- a/src/SFML/Window/OSX/SFContext.mm +++ b/src/SFML/Window/OSX/SFContext.mm @@ -103,6 +103,9 @@ //////////////////////////////////////////////////////////// SFContext::~SFContext() { + // Notify unshared OpenGL resources of context destruction + cleanupUnsharedResources(); + [m_context clearDrawable]; if (m_context == [NSOpenGLContext currentContext]) diff --git a/src/SFML/Window/Unix/GlxContext.cpp b/src/SFML/Window/Unix/GlxContext.cpp index d9f52fefea..cd0223643f 100644 --- a/src/SFML/Window/Unix/GlxContext.cpp +++ b/src/SFML/Window/Unix/GlxContext.cpp @@ -172,6 +172,9 @@ m_ownsWindow(false) //////////////////////////////////////////////////////////// GlxContext::~GlxContext() { + // Notify unshared OpenGL resources of context destruction + cleanupUnsharedResources(); + // Destroy the context if (m_context) { diff --git a/src/SFML/Window/Win32/WglContext.cpp b/src/SFML/Window/Win32/WglContext.cpp index cd5e9ab8fa..fd9f89e487 100644 --- a/src/SFML/Window/Win32/WglContext.cpp +++ b/src/SFML/Window/Win32/WglContext.cpp @@ -154,6 +154,9 @@ m_ownsWindow (false) //////////////////////////////////////////////////////////// WglContext::~WglContext() { + // Notify unshared OpenGL resources of context destruction + cleanupUnsharedResources(); + // Destroy the OpenGL context if (m_context) { diff --git a/src/SFML/Window/iOS/EaglContext.mm b/src/SFML/Window/iOS/EaglContext.mm index f04e1537bc..9fd6b836d5 100644 --- a/src/SFML/Window/iOS/EaglContext.mm +++ b/src/SFML/Window/iOS/EaglContext.mm @@ -91,6 +91,9 @@ //////////////////////////////////////////////////////////// EaglContext::~EaglContext() { + // Notify unshared OpenGL resources of context destruction + cleanupUnsharedResources(); + if (m_context) { // Activate the context, so that we can destroy the buffers