Optimized RenderTexture performance when using the FBO implementation by removing unnecessary context switches and flushing.
This commit is contained in:
parent
c706f11f29
commit
0adde249ec
@ -296,7 +296,7 @@ public:
|
|||||||
/// \return True if operation was successful, false otherwise
|
/// \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
|
/// \brief Save the current OpenGL render states and matrices
|
||||||
@ -458,6 +458,7 @@ private:
|
|||||||
{
|
{
|
||||||
enum {VertexCacheSize = 4};
|
enum {VertexCacheSize = 4};
|
||||||
|
|
||||||
|
bool enable; ///< Is the cache enabled?
|
||||||
bool glStatesSet; ///< Are our internal GL states set yet?
|
bool glStatesSet; ///< Are our internal GL states set yet?
|
||||||
bool viewChanged; ///< Has the current view changed since last draw?
|
bool viewChanged; ///< Has the current view changed since last draw?
|
||||||
BlendMode lastBlendMode; ///< Cached blending mode
|
BlendMode lastBlendMode; ///< Cached blending mode
|
||||||
@ -473,6 +474,7 @@ private:
|
|||||||
View m_defaultView; ///< Default view
|
View m_defaultView; ///< Default view
|
||||||
View m_view; ///< Current view
|
View m_view; ///< Current view
|
||||||
StatesCache m_cache; ///< Render states cache
|
StatesCache m_cache; ///< Render states cache
|
||||||
|
Uint64 m_id; ///< Unique number that identifies the RenderTarget
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace sf
|
} // namespace sf
|
||||||
|
@ -112,11 +112,26 @@ public:
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
/// \brief Get the currently active context
|
/// \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
|
/// \return The currently active context or NULL if none is active
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
static const Context* getActiveContext();
|
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
|
/// \brief Construct a in-memory context
|
||||||
///
|
///
|
||||||
|
@ -37,6 +37,8 @@ namespace sf
|
|||||||
|
|
||||||
class Context;
|
class Context;
|
||||||
|
|
||||||
|
typedef void(*ContextDestroyCallback)(void*);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
/// \brief Base class for classes that require an OpenGL context
|
/// \brief Base class for classes that require an OpenGL context
|
||||||
///
|
///
|
||||||
@ -57,6 +59,19 @@ protected:
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
~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
|
/// \brief RAII helper class to temporarily lock an available context for use
|
||||||
///
|
///
|
||||||
|
@ -32,10 +32,14 @@
|
|||||||
#include <SFML/Graphics/VertexArray.hpp>
|
#include <SFML/Graphics/VertexArray.hpp>
|
||||||
#include <SFML/Graphics/VertexBuffer.hpp>
|
#include <SFML/Graphics/VertexBuffer.hpp>
|
||||||
#include <SFML/Graphics/GLCheck.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 <SFML/System/Err.hpp>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
|
||||||
// GL_QUADS is unavailable on OpenGL ES, thus we need to define GL_QUADS ourselves
|
// GL_QUADS is unavailable on OpenGL ES, thus we need to define GL_QUADS ourselves
|
||||||
@ -48,6 +52,36 @@
|
|||||||
|
|
||||||
namespace
|
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.
|
// Convert an sf::BlendMode::Factor constant to the corresponding OpenGL constant.
|
||||||
sf::Uint32 factorToGlConstant(sf::BlendMode::Factor blendFactor)
|
sf::Uint32 factorToGlConstant(sf::BlendMode::Factor blendFactor)
|
||||||
{
|
{
|
||||||
@ -94,7 +128,8 @@ namespace sf
|
|||||||
RenderTarget::RenderTarget() :
|
RenderTarget::RenderTarget() :
|
||||||
m_defaultView(),
|
m_defaultView(),
|
||||||
m_view (),
|
m_view (),
|
||||||
m_cache ()
|
m_cache (),
|
||||||
|
m_id (getUniqueId())
|
||||||
{
|
{
|
||||||
m_cache.glStatesSet = false;
|
m_cache.glStatesSet = false;
|
||||||
}
|
}
|
||||||
@ -109,7 +144,7 @@ RenderTarget::~RenderTarget()
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
void RenderTarget::clear(const Color& color)
|
void RenderTarget::clear(const Color& color)
|
||||||
{
|
{
|
||||||
if (setActive(true))
|
if (isActive(m_id) || setActive(true))
|
||||||
{
|
{
|
||||||
// Unbind texture to fix RenderTexture preventing clear
|
// Unbind texture to fix RenderTexture preventing clear
|
||||||
applyTexture(NULL);
|
applyTexture(NULL);
|
||||||
@ -224,7 +259,7 @@ void RenderTarget::draw(const Vertex* vertices, std::size_t vertexCount,
|
|||||||
}
|
}
|
||||||
#endif
|
#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
|
// Check if the vertex count is low enough so that we can pre-transform them
|
||||||
bool useVertexCache = (vertexCount <= StatesCache::VertexCacheSize);
|
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
|
// Check if texture coordinates array is needed, and update client state accordingly
|
||||||
bool enableTexCoordsArray = (states.texture || states.shader);
|
bool enableTexCoordsArray = (states.texture || states.shader);
|
||||||
if (enableTexCoordsArray != m_cache.texCoordsArrayEnabled)
|
if (!m_cache.enable || (enableTexCoordsArray != m_cache.texCoordsArrayEnabled))
|
||||||
{
|
{
|
||||||
if (enableTexCoordsArray)
|
if (enableTexCoordsArray)
|
||||||
glCheck(glEnableClientState(GL_TEXTURE_COORD_ARRAY));
|
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
|
// If we switch between non-cache and cache mode or enable texture
|
||||||
// coordinates we need to set up the pointers to the vertices' components
|
// 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);
|
const char* data = reinterpret_cast<const char*>(vertices);
|
||||||
|
|
||||||
@ -324,7 +359,7 @@ void RenderTarget::draw(const VertexBuffer& vertexBuffer, std::size_t firstVerte
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (setActive(true))
|
if (isActive(m_id) || setActive(true))
|
||||||
{
|
{
|
||||||
setupDraw(false, states);
|
setupDraw(false, states);
|
||||||
|
|
||||||
@ -332,7 +367,7 @@ void RenderTarget::draw(const VertexBuffer& vertexBuffer, std::size_t firstVerte
|
|||||||
VertexBuffer::bind(&vertexBuffer);
|
VertexBuffer::bind(&vertexBuffer);
|
||||||
|
|
||||||
// Always enable texture coordinates
|
// Always enable texture coordinates
|
||||||
if (!m_cache.texCoordsArrayEnabled)
|
if (!m_cache.enable || !m_cache.texCoordsArrayEnabled)
|
||||||
glCheck(glEnableClientState(GL_TEXTURE_COORD_ARRAY));
|
glCheck(glEnableClientState(GL_TEXTURE_COORD_ARRAY));
|
||||||
|
|
||||||
glCheck(glVertexPointer(2, GL_FLOAT, sizeof(Vertex), reinterpret_cast<const void*>(0)));
|
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()
|
void RenderTarget::pushGLStates()
|
||||||
{
|
{
|
||||||
if (setActive(true))
|
if (isActive(m_id) || setActive(true))
|
||||||
{
|
{
|
||||||
#ifdef SFML_DEBUG
|
#ifdef SFML_DEBUG
|
||||||
// make sure that the user didn't leave an unchecked OpenGL error
|
// make sure that the user didn't leave an unchecked OpenGL error
|
||||||
@ -388,7 +462,7 @@ void RenderTarget::pushGLStates()
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
void RenderTarget::popGLStates()
|
void RenderTarget::popGLStates()
|
||||||
{
|
{
|
||||||
if (setActive(true))
|
if (isActive(m_id) || setActive(true))
|
||||||
{
|
{
|
||||||
glCheck(glMatrixMode(GL_PROJECTION));
|
glCheck(glMatrixMode(GL_PROJECTION));
|
||||||
glCheck(glPopMatrix());
|
glCheck(glPopMatrix());
|
||||||
@ -417,7 +491,7 @@ void RenderTarget::resetGLStates()
|
|||||||
setActive(false);
|
setActive(false);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (setActive(true))
|
if (isActive(m_id) || setActive(true))
|
||||||
{
|
{
|
||||||
// Make sure that extensions are initialized
|
// Make sure that extensions are initialized
|
||||||
priv::ensureExtensionsInit();
|
priv::ensureExtensionsInit();
|
||||||
@ -458,6 +532,8 @@ void RenderTarget::resetGLStates()
|
|||||||
|
|
||||||
// Set the default view
|
// Set the default view
|
||||||
setView(getView());
|
setView(getView());
|
||||||
|
|
||||||
|
m_cache.enable = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -579,7 +655,7 @@ void RenderTarget::setupDraw(bool useVertexCache, const RenderStates& states)
|
|||||||
if (useVertexCache)
|
if (useVertexCache)
|
||||||
{
|
{
|
||||||
// Since vertices are transformed, we must use an identity transform to render them
|
// 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());
|
glCheck(glLoadIdentity());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -588,17 +664,30 @@ void RenderTarget::setupDraw(bool useVertexCache, const RenderStates& states)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Apply the view
|
// Apply the view
|
||||||
if (m_cache.viewChanged)
|
if (!m_cache.enable || m_cache.viewChanged)
|
||||||
applyCurrentView();
|
applyCurrentView();
|
||||||
|
|
||||||
// Apply the blend mode
|
// Apply the blend mode
|
||||||
if (states.blendMode != m_cache.lastBlendMode)
|
if (!m_cache.enable || (states.blendMode != m_cache.lastBlendMode))
|
||||||
applyBlendMode(states.blendMode);
|
applyBlendMode(states.blendMode);
|
||||||
|
|
||||||
// Apply the texture
|
// Apply the texture
|
||||||
Uint64 textureId = states.texture ? states.texture->m_cacheId : 0;
|
if (!m_cache.enable || (states.texture && states.texture->m_fboAttachment))
|
||||||
if (textureId != m_cache.lastTextureId)
|
{
|
||||||
|
// 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);
|
applyTexture(states.texture);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Uint64 textureId = states.texture ? states.texture->m_cacheId : 0;
|
||||||
|
if (textureId != m_cache.lastTextureId)
|
||||||
|
applyTexture(states.texture);
|
||||||
|
}
|
||||||
|
|
||||||
// Apply the shader
|
// Apply the shader
|
||||||
if (states.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.
|
// This prevents a bug where some drivers do not clear RenderTextures properly.
|
||||||
if (states.texture && states.texture->m_fboAttachment)
|
if (states.texture && states.texture->m_fboAttachment)
|
||||||
applyTexture(NULL);
|
applyTexture(NULL);
|
||||||
|
|
||||||
|
// Re-enable the cache at the end of the draw if it was disabled
|
||||||
|
m_cache.enable = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace sf
|
} // namespace sf
|
||||||
|
@ -147,7 +147,13 @@ bool RenderTexture::generateMipmap()
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
bool RenderTexture::setActive(bool active)
|
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()
|
void RenderTexture::display()
|
||||||
{
|
{
|
||||||
// Update the target texture
|
// Update the target texture
|
||||||
if (setActive(true))
|
if (priv::RenderTextureImplFBO::isAvailable() || setActive(true))
|
||||||
{
|
{
|
||||||
m_impl->updateTexture(m_texture.m_texture);
|
m_impl->updateTexture(m_texture.m_texture);
|
||||||
m_texture.m_pixelsFlipped = true;
|
m_texture.m_pixelsFlipped = true;
|
||||||
|
@ -28,7 +28,67 @@
|
|||||||
#include <SFML/Graphics/RenderTextureImplFBO.hpp>
|
#include <SFML/Graphics/RenderTextureImplFBO.hpp>
|
||||||
#include <SFML/Graphics/Texture.hpp>
|
#include <SFML/Graphics/Texture.hpp>
|
||||||
#include <SFML/Graphics/GLCheck.hpp>
|
#include <SFML/Graphics/GLCheck.hpp>
|
||||||
|
#include <SFML/System/Mutex.hpp>
|
||||||
|
#include <SFML/System/Lock.hpp>
|
||||||
#include <SFML/System/Err.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
|
namespace sf
|
||||||
@ -37,22 +97,36 @@ namespace priv
|
|||||||
{
|
{
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
RenderTextureImplFBO::RenderTextureImplFBO() :
|
RenderTextureImplFBO::RenderTextureImplFBO() :
|
||||||
m_context (NULL),
|
m_depthStencilBuffer(0),
|
||||||
m_frameBuffer (0),
|
m_colorBuffer (0),
|
||||||
m_multisampleFrameBuffer(0),
|
m_width (0),
|
||||||
m_depthStencilBuffer (0),
|
m_height (0),
|
||||||
m_colorBuffer (0),
|
m_context (NULL),
|
||||||
m_width (0),
|
m_textureId (0),
|
||||||
m_height (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()
|
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
|
// Destroy the color buffer
|
||||||
if (m_colorBuffer)
|
if (m_colorBuffer)
|
||||||
@ -68,21 +142,17 @@ RenderTextureImplFBO::~RenderTextureImplFBO()
|
|||||||
glCheck(GLEXT_glDeleteRenderbuffers(1, &depthStencilBuffer));
|
glCheck(GLEXT_glDeleteRenderbuffers(1, &depthStencilBuffer));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destroy the multisample frame buffer
|
// Move all frame buffer objects to stale set
|
||||||
if (m_multisampleFrameBuffer)
|
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));
|
||||||
GLuint multisampleFrameBuffer = static_cast<GLuint>(m_multisampleFrameBuffer);
|
|
||||||
glCheck(GLEXT_glDeleteFramebuffers(1, &multisampleFrameBuffer));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Destroy the frame buffer
|
for (std::map<Uint64, unsigned int>::iterator iter = m_multisampleFrameBuffers.begin(); iter != m_multisampleFrameBuffers.end(); ++iter)
|
||||||
if (m_frameBuffer)
|
staleFrameBuffers.insert(std::make_pair(iter->first, iter->second));
|
||||||
{
|
|
||||||
GLuint frameBuffer = static_cast<GLuint>(m_frameBuffer);
|
|
||||||
glCheck(GLEXT_glDeleteFramebuffers(1, &frameBuffer));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete the context
|
// Clean up FBOs
|
||||||
|
contextDestroyCallback(0);
|
||||||
|
|
||||||
|
// Delete the backup context if we had to create one
|
||||||
delete m_context;
|
delete m_context;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,6 +172,8 @@ bool RenderTextureImplFBO::isAvailable()
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
unsigned int RenderTextureImplFBO::getMaximumAntialiasingLevel()
|
unsigned int RenderTextureImplFBO::getMaximumAntialiasingLevel()
|
||||||
{
|
{
|
||||||
|
TransientContextLock lock;
|
||||||
|
|
||||||
GLint samples = 0;
|
GLint samples = 0;
|
||||||
|
|
||||||
#ifndef SFML_OPENGL_ES
|
#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)
|
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_width = width;
|
||||||
m_height = height;
|
m_height = height;
|
||||||
|
|
||||||
// Disable creation of depth/stencil surfaces in the context
|
{
|
||||||
ContextSettings contextSettings(settings);
|
TransientContextLock lock;
|
||||||
contextSettings.depthBits = 0;
|
|
||||||
contextSettings.stencilBits = 0;
|
|
||||||
contextSettings.antialiasingLevel = 0;
|
|
||||||
contextSettings.sRgbCapable = false;
|
|
||||||
|
|
||||||
// Create the context
|
// Make sure that extensions are initialized
|
||||||
m_context = new Context(contextSettings, 1, 1);
|
priv::ensureExtensionsInit();
|
||||||
|
|
||||||
if (settings.antialiasingLevel && !(GLEXT_framebuffer_multisample && GLEXT_framebuffer_blit))
|
if (settings.antialiasingLevel && !(GLEXT_framebuffer_multisample && GLEXT_framebuffer_blit))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (settings.stencilBits && !GLEXT_packed_depth_stencil)
|
if (settings.stencilBits && !GLEXT_packed_depth_stencil)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
#ifndef SFML_OPENGL_ES
|
#ifndef SFML_OPENGL_ES
|
||||||
|
|
||||||
// Check if the requested anti-aliasing level is supported
|
// Check if the requested anti-aliasing level is supported
|
||||||
if (settings.antialiasingLevel)
|
if (settings.antialiasingLevel)
|
||||||
{
|
|
||||||
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)";
|
GLint samples = 0;
|
||||||
err() << " Requested: " << settings.antialiasingLevel << " Maximum supported: " << samples << std::endl;
|
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;
|
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
|
#endif
|
||||||
|
|
||||||
if (!settings.antialiasingLevel)
|
return true;
|
||||||
{
|
|
||||||
// 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
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
bool RenderTextureImplFBO::activate(bool active)
|
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)
|
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
|
#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));
|
Uint64 contextId = Context::getActiveContextId();
|
||||||
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));
|
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
|
#endif // SFML_OPENGL_ES
|
||||||
|
|
||||||
glCheck(glFlush());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace priv
|
} // namespace priv
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
#include <SFML/Graphics/RenderTextureImpl.hpp>
|
#include <SFML/Graphics/RenderTextureImpl.hpp>
|
||||||
#include <SFML/Window/Context.hpp>
|
#include <SFML/Window/Context.hpp>
|
||||||
#include <SFML/Window/GlResource.hpp>
|
#include <SFML/Window/GlResource.hpp>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
|
||||||
namespace sf
|
namespace sf
|
||||||
@ -74,6 +75,12 @@ public:
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
static unsigned int getMaximumAntialiasingLevel();
|
static unsigned int getMaximumAntialiasingLevel();
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
/// \brief Unbind the currently bound FBO
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
static void unbind();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
@ -89,6 +96,14 @@ private:
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
virtual bool create(unsigned int width, unsigned int height, unsigned int textureId, const ContextSettings& settings);
|
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
|
/// \brief Activate or deactivate the render texture for rendering
|
||||||
///
|
///
|
||||||
@ -110,13 +125,16 @@ private:
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
// Member data
|
// Member data
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
Context* m_context; ///< Needs a separate OpenGL context for not messing up the other ones
|
std::map<Uint64, unsigned int> m_frameBuffers; ///< OpenGL frame buffer objects per context
|
||||||
unsigned int m_frameBuffer; ///< OpenGL frame buffer object
|
std::map<Uint64, unsigned int> m_multisampleFrameBuffers; ///< Optional per-context OpenGL frame buffer objects with multisample attachments
|
||||||
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_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_colorBuffer; ///< Optional multisample color buffer attached to the frame buffer
|
unsigned int m_width; ///< Width of the attachments
|
||||||
unsigned int m_width; ///< Width of the attachments
|
unsigned int m_height; ///< Height 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
|
} // namespace priv
|
||||||
|
@ -27,6 +27,8 @@
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
#include <SFML/Graphics/RenderWindow.hpp>
|
#include <SFML/Graphics/RenderWindow.hpp>
|
||||||
#include <SFML/Graphics/Texture.hpp>
|
#include <SFML/Graphics/Texture.hpp>
|
||||||
|
#include <SFML/Graphics/GLCheck.hpp>
|
||||||
|
#include <SFML/Graphics/RenderTextureImplFBO.hpp>
|
||||||
|
|
||||||
|
|
||||||
namespace sf
|
namespace sf
|
||||||
@ -71,7 +73,22 @@ Vector2u RenderWindow::getSize() const
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
bool RenderWindow::setActive(bool active)
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -80,6 +80,13 @@ const Context* Context::getActiveContext()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
Uint64 Context::getActiveContextId()
|
||||||
|
{
|
||||||
|
return priv::GlContext::getActiveContextId();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
bool Context::isExtensionAvailable(const char* name)
|
bool Context::isExtensionAvailable(const char* name)
|
||||||
{
|
{
|
||||||
|
@ -150,6 +150,9 @@ m_config (NULL)
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
EglContext::~EglContext()
|
EglContext::~EglContext()
|
||||||
{
|
{
|
||||||
|
// Notify unshared OpenGL resources of context destruction
|
||||||
|
cleanupUnsharedResources();
|
||||||
|
|
||||||
// Deactivate the current context
|
// Deactivate the current context
|
||||||
EGLContext currentContext = eglCheck(eglGetCurrentContext());
|
EGLContext currentContext = eglCheck(eglGetCurrentContext());
|
||||||
|
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <utility>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
@ -146,6 +147,16 @@ namespace
|
|||||||
// The hidden, inactive context that will be shared with all other contexts
|
// The hidden, inactive context that will be shared with all other contexts
|
||||||
ContextType* sharedContext = NULL;
|
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
|
// This structure contains all the state necessary to
|
||||||
// track TransientContext usage
|
// track TransientContext usage
|
||||||
struct TransientContext : private sf::NonCopyable
|
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()
|
void GlContext::acquireTransientContext()
|
||||||
{
|
{
|
||||||
@ -473,6 +491,13 @@ GlFunctionPointer GlContext::getFunction(const char* name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
Uint64 GlContext::getActiveContextId()
|
||||||
|
{
|
||||||
|
return currentContext ? currentContext->m_id : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
GlContext::~GlContext()
|
GlContext::~GlContext()
|
||||||
{
|
{
|
||||||
@ -546,7 +571,8 @@ bool GlContext::setActive(bool active)
|
|||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
GlContext::GlContext()
|
GlContext::GlContext() :
|
||||||
|
m_id(id++)
|
||||||
{
|
{
|
||||||
// Nothing to do
|
// 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)
|
void GlContext::initialize(const ContextSettings& requestedSettings)
|
||||||
{
|
{
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
#include <SFML/Config.hpp>
|
#include <SFML/Config.hpp>
|
||||||
#include <SFML/Window/Context.hpp>
|
#include <SFML/Window/Context.hpp>
|
||||||
#include <SFML/Window/ContextSettings.hpp>
|
#include <SFML/Window/ContextSettings.hpp>
|
||||||
|
#include <SFML/Window/GlResource.hpp>
|
||||||
#include <SFML/System/NonCopyable.hpp>
|
#include <SFML/System/NonCopyable.hpp>
|
||||||
|
|
||||||
|
|
||||||
@ -69,6 +70,19 @@ public:
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
static void cleanupResource();
|
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
|
/// \brief Acquires a context for short-term use on the current thread
|
||||||
///
|
///
|
||||||
@ -143,6 +157,17 @@ public:
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
static GlFunctionPointer getFunction(const char* name);
|
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
|
/// \brief Destructor
|
||||||
///
|
///
|
||||||
@ -217,6 +242,12 @@ protected:
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
virtual bool makeCurrent(bool current) = 0;
|
virtual bool makeCurrent(bool current) = 0;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
/// \brief Notify unshared GlResources of context destruction
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
void cleanupUnsharedResources();
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
/// \brief Evaluate a pixel format configuration
|
/// \brief Evaluate a pixel format configuration
|
||||||
///
|
///
|
||||||
@ -259,6 +290,11 @@ private:
|
|||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
void checkSettings(const ContextSettings& requestedSettings);
|
void checkSettings(const ContextSettings& requestedSettings);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
// Member data
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
const Uint64 m_id; ///< Unique number that identifies the context
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace priv
|
} // namespace priv
|
||||||
|
@ -45,6 +45,13 @@ GlResource::~GlResource()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
void GlResource::registerContextDestroyCallback(ContextDestroyCallback callback, void* arg)
|
||||||
|
{
|
||||||
|
priv::GlContext::registerContextDestroyCallback(callback, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
GlResource::TransientContextLock::TransientContextLock()
|
GlResource::TransientContextLock::TransientContextLock()
|
||||||
{
|
{
|
||||||
|
@ -103,6 +103,9 @@ m_window(0)
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
SFContext::~SFContext()
|
SFContext::~SFContext()
|
||||||
{
|
{
|
||||||
|
// Notify unshared OpenGL resources of context destruction
|
||||||
|
cleanupUnsharedResources();
|
||||||
|
|
||||||
[m_context clearDrawable];
|
[m_context clearDrawable];
|
||||||
|
|
||||||
if (m_context == [NSOpenGLContext currentContext])
|
if (m_context == [NSOpenGLContext currentContext])
|
||||||
|
@ -172,6 +172,9 @@ m_ownsWindow(false)
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
GlxContext::~GlxContext()
|
GlxContext::~GlxContext()
|
||||||
{
|
{
|
||||||
|
// Notify unshared OpenGL resources of context destruction
|
||||||
|
cleanupUnsharedResources();
|
||||||
|
|
||||||
// Destroy the context
|
// Destroy the context
|
||||||
if (m_context)
|
if (m_context)
|
||||||
{
|
{
|
||||||
|
@ -154,6 +154,9 @@ m_ownsWindow (false)
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
WglContext::~WglContext()
|
WglContext::~WglContext()
|
||||||
{
|
{
|
||||||
|
// Notify unshared OpenGL resources of context destruction
|
||||||
|
cleanupUnsharedResources();
|
||||||
|
|
||||||
// Destroy the OpenGL context
|
// Destroy the OpenGL context
|
||||||
if (m_context)
|
if (m_context)
|
||||||
{
|
{
|
||||||
|
@ -91,6 +91,9 @@ m_clock ()
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
EaglContext::~EaglContext()
|
EaglContext::~EaglContext()
|
||||||
{
|
{
|
||||||
|
// Notify unshared OpenGL resources of context destruction
|
||||||
|
cleanupUnsharedResources();
|
||||||
|
|
||||||
if (m_context)
|
if (m_context)
|
||||||
{
|
{
|
||||||
// Activate the context, so that we can destroy the buffers
|
// Activate the context, so that we can destroy the buffers
|
||||||
|
Loading…
Reference in New Issue
Block a user