From b175eed3ca950204c9b52fdcebc897f337b7bf19 Mon Sep 17 00:00:00 2001 From: James Cowgill Date: Tue, 15 May 2018 13:58:15 +0100 Subject: New upstream version 2.5.0+dfsg --- src/SFML/Graphics/RenderTextureImplFBO.cpp | 508 +++++++++++++++++++++++++++-- 1 file changed, 474 insertions(+), 34 deletions(-) (limited to 'src/SFML/Graphics/RenderTextureImplFBO.cpp') diff --git a/src/SFML/Graphics/RenderTextureImplFBO.cpp b/src/SFML/Graphics/RenderTextureImplFBO.cpp index 58dcb46..301aa04 100644 --- a/src/SFML/Graphics/RenderTextureImplFBO.cpp +++ b/src/SFML/Graphics/RenderTextureImplFBO.cpp @@ -1,7 +1,7 @@ //////////////////////////////////////////////////////////// // // SFML - Simple and Fast Multimedia Library -// Copyright (C) 2007-2017 Laurent Gomila (laurent@sfml-dev.org) +// Copyright (C) 2007-2018 Laurent Gomila (laurent@sfml-dev.org) // // This software is provided 'as-is', without any express or implied warranty. // In no event will the authors be held liable for any damages arising from the use of this software. @@ -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,34 +97,62 @@ namespace priv { //////////////////////////////////////////////////////////// RenderTextureImplFBO::RenderTextureImplFBO() : -m_context (NULL), -m_frameBuffer(0), -m_depthBuffer(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); - // Destroy the depth buffer - if (m_depthBuffer) + // 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) { - GLuint depthBuffer = static_cast(m_depthBuffer); - glCheck(GLEXT_glDeleteRenderbuffers(1, &depthBuffer)); + GLuint colorBuffer = static_cast(m_colorBuffer); + glCheck(GLEXT_glDeleteRenderbuffers(1, &colorBuffer)); } - // Destroy the frame buffer - if (m_frameBuffer) + // Destroy the depth/stencil buffer + if (m_depthStencilBuffer) { - GLuint frameBuffer = static_cast(m_frameBuffer); - glCheck(GLEXT_glDeleteFramebuffers(1, &frameBuffer)); + GLuint depthStencilBuffer = static_cast(m_depthStencilBuffer); + glCheck(GLEXT_glDeleteRenderbuffers(1, &depthStencilBuffer)); } - // Delete the context + // 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)); + + 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 backup context if we had to create one delete m_context; } @@ -82,40 +170,247 @@ bool RenderTextureImplFBO::isAvailable() //////////////////////////////////////////////////////////// -bool RenderTextureImplFBO::create(unsigned int width, unsigned int height, unsigned int textureId, bool depthBuffer) +unsigned int RenderTextureImplFBO::getMaximumAntialiasingLevel() { - // Create the context - m_context = new Context; + TransientContextLock lock; + + GLint samples = 0; + +#ifndef SFML_OPENGL_ES + + glCheck(glGetIntegerv(GLEXT_GL_MAX_SAMPLES, &samples)); + +#endif + + return static_cast(samples); +} + + +//////////////////////////////////////////////////////////// +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) +{ + // Store the dimensions + m_width = width; + m_height = height; + + { + TransientContextLock lock; + // Make sure that extensions are initialized + priv::ensureExtensionsInit(); + + if (settings.antialiasingLevel && !(GLEXT_framebuffer_multisample && GLEXT_framebuffer_blit)) + 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)) + { + 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 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)); + +#else + + 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 + { + +#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 multisample color buffer)" << std::endl; + return false; + } + 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)); + } + +#else + + 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; + } + +#endif + + return false; +} + + +//////////////////////////////////////////////////////////// +bool RenderTextureImplFBO::createFrameBuffer() +{ // Create the framebuffer object GLuint frameBuffer = 0; glCheck(GLEXT_glGenFramebuffers(1, &frameBuffer)); - m_frameBuffer = static_cast(frameBuffer); - if (!m_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, m_frameBuffer)); + glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, frameBuffer)); - // Create the depth buffer if requested - if (depthBuffer) + // Link the depth/stencil renderbuffer to the frame buffer + if (!m_multisample && m_depthStencilBuffer) { - GLuint depth = 0; - glCheck(GLEXT_glGenRenderbuffers(1, &depth)); - m_depthBuffer = static_cast(depth); - if (!m_depthBuffer) + glCheck(GLEXT_glFramebufferRenderbuffer(GLEXT_GL_FRAMEBUFFER, GLEXT_GL_DEPTH_ATTACHMENT, GLEXT_GL_RENDERBUFFER, m_depthStencilBuffer)); + +#ifndef SFML_OPENGL_ES + + if (m_stencil) { - err() << "Impossible to create render texture (failed to create the attached 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_depthBuffer)); - 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_depthBuffer)); + +#endif + } // Link the texture to the frame buffer - glCheck(GLEXT_glFramebufferTexture2D(GLEXT_GL_FRAMEBUFFER, GLEXT_GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureId, 0)); + 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; @@ -123,10 +418,68 @@ bool RenderTextureImplFBO::create(unsigned int width, unsigned int height, unsig 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 + 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, multisampleFrameBuffer)); + + // Link the multisample color buffer to the frame buffer + glCheck(GLEXT_glBindRenderbuffer(GLEXT_GL_RENDERBUFFER, m_colorBuffer)); + glCheck(GLEXT_glFramebufferRenderbuffer(GLEXT_GL_FRAMEBUFFER, GLEXT_GL_COLOR_ATTACHMENT0, GLEXT_GL_RENDERBUFFER, m_colorBuffer)); + + // Link the depth/stencil renderbuffer to the frame buffer + if (m_depthStencilBuffer) + { + glCheck(GLEXT_glFramebufferRenderbuffer(GLEXT_GL_FRAMEBUFFER, GLEXT_GL_DEPTH_ATTACHMENT, GLEXT_GL_RENDERBUFFER, m_depthStencilBuffer)); + + if (m_stencil) + { + glCheck(GLEXT_glFramebufferRenderbuffer(GLEXT_GL_FRAMEBUFFER, GLEXT_GL_STENCIL_ATTACHMENT, GLEXT_GL_RENDERBUFFER, m_depthStencilBuffer)); + } + } + + // A final check, just to be sure... + glCheck(status = GLEXT_glCheckFramebufferStatus(GLEXT_GL_FRAMEBUFFER)); + if (status != GLEXT_GL_FRAMEBUFFER_COMPLETE) + { + glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, 0)); + 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; + } + + { + Lock lock(mutex); + + // Insert the FBO into our map + m_multisampleFrameBuffers.insert(std::make_pair(Context::getActiveContextId(), static_cast(multisampleFrameBuffer))); + } + } + +#endif + return true; } @@ -134,14 +487,101 @@ bool RenderTextureImplFBO::create(unsigned int width, unsigned int height, unsig //////////////////////////////////////////////////////////// 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) { - glCheck(glFlush()); + // 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 + + // In case of multisampling, make sure both FBOs + // are already available within the current context + if (m_multisample && m_width && m_height && activate(true)) + { + 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 + } } // namespace priv -- cgit v1.2.3