Optimized RenderTexture performance when using the FBO implementation by removing unnecessary context switches and flushing.

This commit is contained in:
binary1248 2018-02-28 19:54:02 +01:00 committed by Lukas Dürrenberger
parent c706f11f29
commit 0adde249ec
17 changed files with 755 additions and 247 deletions

View File

@ -296,7 +296,7 @@ public:
/// \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 @@ private:
{
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 @@ private:
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

View File

@ -112,11 +112,26 @@ public:
////////////////////////////////////////////////////////////
/// \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
///

View File

@ -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 @@ protected:
////////////////////////////////////////////////////////////
~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
///

View File

@ -32,10 +32,14 @@
#include <SFML/Graphics/VertexArray.hpp>
#include <SFML/Graphics/VertexBuffer.hpp>
#include <SFML/Graphics/GLCheck.hpp>
#include <SFML/Window/Context.hpp>
#include <SFML/System/Mutex.hpp>
#include <SFML/System/Lock.hpp>
#include <SFML/System/Err.hpp>
#include <cassert>
#include <iostream>
#include <algorithm>
#include <map>
// 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<sf::Uint64, sf::Uint64> 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<const char*>(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<const void*>(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

View File

@ -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;

View File

@ -28,7 +28,67 @@
#include <SFML/Graphics/RenderTextureImplFBO.hpp>
#include <SFML/Graphics/Texture.hpp>
#include <SFML/Graphics/GLCheck.hpp>
#include <SFML/System/Mutex.hpp>
#include <SFML/System/Lock.hpp>
#include <SFML/System/Err.hpp>
#include <utility>
#include <set>
namespace
{
// Set to track all active FBO mappings
// This is used to free active FBOs while their owning
// RenderTextureImplFBO is still alive
std::set<std::map<sf::Uint64, unsigned int>*> 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<std::pair<sf::Uint64, unsigned int> > 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<std::map<sf::Uint64, unsigned int>*>::iterator frameBuffersIter = frameBuffers.begin(); frameBuffersIter != frameBuffers.end(); ++frameBuffersIter)
{
for (std::map<sf::Uint64, unsigned int>::iterator iter = (*frameBuffersIter)->begin(); iter != (*frameBuffersIter)->end(); ++iter)
{
if (iter->first == contextId)
{
GLuint frameBuffer = static_cast<GLuint>(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<std::pair<sf::Uint64, unsigned int> >::iterator iter = staleFrameBuffers.begin(); iter != staleFrameBuffers.end(); ++iter)
{
if (iter->first == contextId)
{
GLuint frameBuffer = static_cast<GLuint>(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<GLuint>(m_multisampleFrameBuffer);
glCheck(GLEXT_glDeleteFramebuffers(1, &multisampleFrameBuffer));
}
// Move all frame buffer objects to stale set
for (std::map<Uint64, unsigned int>::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<GLuint>(m_frameBuffer);
glCheck(GLEXT_glDeleteFramebuffers(1, &frameBuffer));
}
for (std::map<Uint64, unsigned int>::iterator iter = m_multisampleFrameBuffers.begin(); iter != m_multisampleFrameBuffers.end(); ++iter)
staleFrameBuffers.insert(std::make_pair(iter->first, iter->second));
// Delete the context
// Clean up FBOs
contextDestroyCallback(0);
// 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,238 +200,388 @@ 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<unsigned int>(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;
GLint samples = 0;
glCheck(glGetIntegerv(GLEXT_GL_MAX_SAMPLES, &samples));
if (settings.antialiasingLevel > static_cast<unsigned int>(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<unsigned int>(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<unsigned int>(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<unsigned int>(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<unsigned int>(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<unsigned int>(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));
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
if (m_stencil)
{
glCheck(GLEXT_glFramebufferRenderbuffer(GLEXT_GL_FRAMEBUFFER, GLEXT_GL_STENCIL_ATTACHMENT, GLEXT_GL_RENDERBUFFER, m_depthStencilBuffer));
}
#endif
}
// 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<unsigned int>(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<unsigned int>(multisampleFrameBuffer)));
}
}
#endif
if (!settings.antialiasingLevel)
{
// Create the framebuffer object
GLuint frameBuffer = 0;
glCheck(GLEXT_glGenFramebuffers(1, &frameBuffer));
m_frameBuffer = static_cast<unsigned int>(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)
{
#ifndef SFML_OPENGL_ES
GLuint depthStencil = 0;
glCheck(GLEXT_glGenRenderbuffers(1, &depthStencil));
m_depthStencilBuffer = static_cast<unsigned int>(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));
#else
err() << "Impossible to create render texture (failed to create the attached depth/stencil buffer)" << std::endl;
return false;
#endif // SFML_OPENGL_ES
}
else if (settings.depthBits)
{
GLuint depthStencil = 0;
glCheck(GLEXT_glGenRenderbuffers(1, &depthStencil));
m_depthStencilBuffer = static_cast<unsigned int>(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));
glCheck(GLEXT_glFramebufferRenderbuffer(GLEXT_GL_FRAMEBUFFER, GLEXT_GL_DEPTH_ATTACHMENT, GLEXT_GL_RENDERBUFFER, m_depthStencilBuffer));
}
// Link the texture to the frame buffer
glCheck(GLEXT_glFramebufferTexture2D(GLEXT_GL_FRAMEBUFFER, GLEXT_GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 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));
err() << "Impossible to create render texture (frame buffer incomplete)" << std::endl;
return false;
}
return true;
}
else
{
#ifndef SFML_OPENGL_ES
// Create the framebuffer object
GLuint frameBuffer = 0;
glCheck(GLEXT_glGenFramebuffers(1, &frameBuffer));
m_frameBuffer = static_cast<unsigned int>(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));
// Link the texture to the frame buffer
glCheck(GLEXT_glFramebufferTexture2D(GLEXT_GL_FRAMEBUFFER, GLEXT_GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 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));
err() << "Impossible to create render texture (frame buffer incomplete)" << std::endl;
return false;
}
// Create the multisample framebuffer object
frameBuffer = 0;
glCheck(GLEXT_glGenFramebuffers(1, &frameBuffer));
m_multisampleFrameBuffer = static_cast<unsigned int>(frameBuffer);
if (!m_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));
// Create the multisample color buffer
GLuint color = 0;
glCheck(GLEXT_glGenRenderbuffers(1, &color));
m_colorBuffer = static_cast<unsigned int>(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));
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)
{
GLuint depthStencil = 0;
glCheck(GLEXT_glGenRenderbuffers(1, &depthStencil));
m_depthStencilBuffer = static_cast<unsigned int>(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<unsigned int>(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));
glCheck(GLEXT_glFramebufferRenderbuffer(GLEXT_GL_FRAMEBUFFER, GLEXT_GL_DEPTH_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));
err() << "Impossible to create render texture (multisample frame buffer incomplete)" << std::endl;
return false;
}
return true;
#else
err() << "Impossible to create render texture (failed to create the multisample frame buffer object)" << std::endl;
return false;
#endif // SFML_OPENGL_ES
}
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<Uint64, unsigned int>::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<Uint64, unsigned int>::iterator iter = m_frameBuffers.find(contextId);
std::map<Uint64, unsigned int>::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

View File

@ -31,6 +31,7 @@
#include <SFML/Graphics/RenderTextureImpl.hpp>
#include <SFML/Window/Context.hpp>
#include <SFML/Window/GlResource.hpp>
#include <map>
namespace sf
@ -74,6 +75,12 @@ public:
////////////////////////////////////////////////////////////
static unsigned int getMaximumAntialiasingLevel();
////////////////////////////////////////////////////////////
/// \brief Unbind the currently bound FBO
///
////////////////////////////////////////////////////////////
static void unbind();
private:
////////////////////////////////////////////////////////////
@ -89,6 +96,14 @@ private:
////////////////////////////////////////////////////////////
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 @@ private:
////////////////////////////////////////////////////////////
// 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<Uint64, unsigned int> m_frameBuffers; ///< OpenGL frame buffer objects per context
std::map<Uint64, unsigned int> 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

View File

@ -27,6 +27,8 @@
////////////////////////////////////////////////////////////
#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Graphics/Texture.hpp>
#include <SFML/Graphics/GLCheck.hpp>
#include <SFML/Graphics/RenderTextureImplFBO.hpp>
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;
}

View File

@ -80,6 +80,13 @@ const Context* Context::getActiveContext()
}
////////////////////////////////////////////////////////////
Uint64 Context::getActiveContextId()
{
return priv::GlContext::getActiveContextId();
}
////////////////////////////////////////////////////////////
bool Context::isExtensionAvailable(const char* name)
{

View File

@ -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());

View File

@ -36,6 +36,7 @@
#include <vector>
#include <string>
#include <set>
#include <utility>
#include <cstdlib>
#include <cstring>
#include <cctype>
@ -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<std::pair<sf::ContextDestroyCallback, void*> > 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)
{

View File

@ -31,6 +31,7 @@
#include <SFML/Config.hpp>
#include <SFML/Window/Context.hpp>
#include <SFML/Window/ContextSettings.hpp>
#include <SFML/Window/GlResource.hpp>
#include <SFML/System/NonCopyable.hpp>
@ -69,6 +70,19 @@ public:
////////////////////////////////////////////////////////////
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 @@ public:
////////////////////////////////////////////////////////////
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 @@ protected:
////////////////////////////////////////////////////////////
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 @@ private:
///
////////////////////////////////////////////////////////////
void checkSettings(const ContextSettings& requestedSettings);
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
const Uint64 m_id; ///< Unique number that identifies the context
};
} // namespace priv

View File

@ -45,6 +45,13 @@ GlResource::~GlResource()
}
////////////////////////////////////////////////////////////
void GlResource::registerContextDestroyCallback(ContextDestroyCallback callback, void* arg)
{
priv::GlContext::registerContextDestroyCallback(callback, arg);
}
////////////////////////////////////////////////////////////
GlResource::TransientContextLock::TransientContextLock()
{

View File

@ -103,6 +103,9 @@ m_window(0)
////////////////////////////////////////////////////////////
SFContext::~SFContext()
{
// Notify unshared OpenGL resources of context destruction
cleanupUnsharedResources();
[m_context clearDrawable];
if (m_context == [NSOpenGLContext currentContext])

View File

@ -172,6 +172,9 @@ m_ownsWindow(false)
////////////////////////////////////////////////////////////
GlxContext::~GlxContext()
{
// Notify unshared OpenGL resources of context destruction
cleanupUnsharedResources();
// Destroy the context
if (m_context)
{

View File

@ -154,6 +154,9 @@ m_ownsWindow (false)
////////////////////////////////////////////////////////////
WglContext::~WglContext()
{
// Notify unshared OpenGL resources of context destruction
cleanupUnsharedResources();
// Destroy the OpenGL context
if (m_context)
{

View File

@ -91,6 +91,9 @@ m_clock ()
////////////////////////////////////////////////////////////
EaglContext::~EaglContext()
{
// Notify unshared OpenGL resources of context destruction
cleanupUnsharedResources();
if (m_context)
{
// Activate the context, so that we can destroy the buffers