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
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
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
|
||||
|
@ -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
|
||||
///
|
||||
|
@ -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
|
||||
///
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -80,6 +80,13 @@ const Context* Context::getActiveContext()
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
Uint64 Context::getActiveContextId()
|
||||
{
|
||||
return priv::GlContext::getActiveContextId();
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
bool Context::isExtensionAvailable(const char* name)
|
||||
{
|
||||
|
@ -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());
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -45,6 +45,13 @@ GlResource::~GlResource()
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
void GlResource::registerContextDestroyCallback(ContextDestroyCallback callback, void* arg)
|
||||
{
|
||||
priv::GlContext::registerContextDestroyCallback(callback, arg);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
GlResource::TransientContextLock::TransientContextLock()
|
||||
{
|
||||
|
@ -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])
|
||||
|
@ -172,6 +172,9 @@ m_ownsWindow(false)
|
||||
////////////////////////////////////////////////////////////
|
||||
GlxContext::~GlxContext()
|
||||
{
|
||||
// Notify unshared OpenGL resources of context destruction
|
||||
cleanupUnsharedResources();
|
||||
|
||||
// Destroy the context
|
||||
if (m_context)
|
||||
{
|
||||
|
@ -154,6 +154,9 @@ m_ownsWindow (false)
|
||||
////////////////////////////////////////////////////////////
|
||||
WglContext::~WglContext()
|
||||
{
|
||||
// Notify unshared OpenGL resources of context destruction
|
||||
cleanupUnsharedResources();
|
||||
|
||||
// Destroy the OpenGL context
|
||||
if (m_context)
|
||||
{
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user