Removed internal OpenGL contexts, reduced the number of temporary contexts that get created during runtime.

This commit is contained in:
binary1248 2015-10-04 17:03:43 +02:00 committed by Lukas Dürrenberger
parent 95828a85a2
commit 39208efb55
21 changed files with 477 additions and 289 deletions

View File

@ -29,10 +29,14 @@
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Window/Export.hpp>
#include <SFML/System/NonCopyable.hpp>
namespace sf
{
class Context;
////////////////////////////////////////////////////////////
/// \brief Base class for classes that require an OpenGL context
///
@ -54,10 +58,27 @@ protected:
~GlResource();
////////////////////////////////////////////////////////////
/// \brief Make sure that a valid OpenGL context exists in the current thread
/// \brief RAII helper class to temporarily lock an available context for use
///
////////////////////////////////////////////////////////////
static void ensureGlContext();
class SFML_WINDOW_API TransientContextLock : NonCopyable
{
public:
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
////////////////////////////////////////////////////////////
TransientContextLock();
////////////////////////////////////////////////////////////
/// \brief Destructor
///
////////////////////////////////////////////////////////////
~TransientContextLock();
private:
Context* m_context; ///< Temporary context, in case we needed to create one
};
};
} // namespace sf

View File

@ -645,10 +645,6 @@ Glyph Font::loadGlyph(Uint32 codePoint, unsigned int characterSize, bool bold, f
// Delete the FT glyph
FT_Done_Glyph(glyphDesc);
// Force an OpenGL flush, so that the font's texture will appear updated
// in all contexts immediately (solves problems in multi-threaded apps)
glCheck(glFlush());
// Done :)
return glyph;
}

View File

@ -29,6 +29,14 @@
#include <SFML/Window/Context.hpp>
#include <SFML/System/Err.hpp>
#if !defined(GL_MAJOR_VERSION)
#define GL_MAJOR_VERSION 0x821B
#endif
#if !defined(GL_MINOR_VERSION)
#define GL_MINOR_VERSION 0x821C
#endif
namespace sf
{
@ -41,22 +49,41 @@ void ensureExtensionsInit()
static bool initialized = false;
if (!initialized)
{
const Context* context = Context::getActiveContext();
if (!context)
return;
initialized = true;
sfogl_LoadFunctions();
ContextSettings settings = context->getSettings();
// Retrieve the context version number
int majorVersion = 0;
int minorVersion = 0;
if ((settings.majorVersion < 1) || ((settings.majorVersion == 1) && (settings.minorVersion < 1)))
// Try the new way first
glGetIntegerv(GL_MAJOR_VERSION, &majorVersion);
glGetIntegerv(GL_MINOR_VERSION, &minorVersion);
if (glGetError() == GL_INVALID_ENUM)
{
// Try the old way
const GLubyte* version = glGetString(GL_VERSION);
if (version)
{
// The beginning of the returned string is "major.minor" (this is standard)
majorVersion = version[0] - '0';
minorVersion = version[2] - '0';
}
else
{
// Can't get the version number, assume 1.1
majorVersion = 1;
minorVersion = 1;
}
}
if ((majorVersion < 1) || ((majorVersion == 1) && (minorVersion < 1)))
{
err() << "sfml-graphics requires support for OpenGL 1.1 or greater" << std::endl;
err() << "Ensure that hardware acceleration is enabled if available" << std::endl;
}
initialized = true;
}
#endif
}

View File

@ -48,7 +48,7 @@ m_depthBuffer(0)
////////////////////////////////////////////////////////////
RenderTextureImplFBO::~RenderTextureImplFBO()
{
ensureGlContext();
m_context->setActive(true);
// Destroy the depth buffer
if (m_depthBuffer)
@ -72,7 +72,7 @@ RenderTextureImplFBO::~RenderTextureImplFBO()
////////////////////////////////////////////////////////////
bool RenderTextureImplFBO::isAvailable()
{
ensureGlContext();
TransientContextLock lock;
// Make sure that extensions are initialized
priv::ensureExtensionsInit();

View File

@ -56,7 +56,8 @@
namespace
{
sf::Mutex mutex;
sf::Mutex maxTextureUnitsMutex;
sf::Mutex isAvailableMutex;
GLint checkMaxTextureUnits()
{
@ -70,7 +71,7 @@ namespace
GLint getMaxTextureUnits()
{
// TODO: Remove this lock when it becomes unnecessary in C++11
sf::Lock lock(mutex);
sf::Lock lock(maxTextureUnitsMutex);
static GLint maxUnits = checkMaxTextureUnits();
@ -116,53 +117,6 @@ namespace
return success;
}
bool checkShadersAvailable()
{
// Create a temporary context in case the user checks
// before a GlResource is created, thus initializing
// the shared context
if (!sf::Context::getActiveContext())
{
sf::Context context;
// Make sure that extensions are initialized
sf::priv::ensureExtensionsInit();
bool available = GLEXT_multitexture &&
GLEXT_shading_language_100 &&
GLEXT_shader_objects &&
GLEXT_vertex_shader &&
GLEXT_fragment_shader;
return available;
}
// Make sure that extensions are initialized
sf::priv::ensureExtensionsInit();
bool available = GLEXT_multitexture &&
GLEXT_shading_language_100 &&
GLEXT_shader_objects &&
GLEXT_vertex_shader &&
GLEXT_fragment_shader;
return available;
}
bool checkGeometryShadersAvailable()
{
// Create a temporary context in case the user checks
// before a GlResource is created, thus initializing
// the shared context
sf::Context context;
// Make sure that extensions are initialized
sf::priv::ensureExtensionsInit();
bool available = checkShadersAvailable() && GLEXT_geometry_shader4;
return available;
}
// Transforms an array of 2D vectors into a contiguous array of scalars
template <typename T>
std::vector<T> flatten(const sf::Vector2<T>* vectorArray, std::size_t length)
@ -236,8 +190,6 @@ struct Shader::UniformBinder : private NonCopyable
{
if (currentProgram)
{
ensureGlContext();
// Enable program object
glCheck(savedProgram = GLEXT_glGetHandle(GLEXT_GL_PROGRAM_OBJECT));
if (currentProgram != savedProgram)
@ -259,9 +211,10 @@ struct Shader::UniformBinder : private NonCopyable
glCheck(GLEXT_glUseProgramObject(savedProgram));
}
GLEXT_GLhandle savedProgram; ///< Handle to the previously active program object
GLEXT_GLhandle currentProgram; ///< Handle to the program object of the modified sf::Shader instance
GLint location; ///< Uniform location, used by the surrounding sf::Shader code
TransientContextLock lock; ///< Lock to keep context active while uniform is bound
GLEXT_GLhandle savedProgram; ///< Handle to the previously active program object
GLEXT_GLhandle currentProgram; ///< Handle to the program object of the modified sf::Shader instance
GLint location; ///< Uniform location, used by the surrounding sf::Shader code
};
@ -278,7 +231,7 @@ m_uniforms ()
////////////////////////////////////////////////////////////
Shader::~Shader()
{
ensureGlContext();
TransientContextLock lock;
// Destroy effect program
if (m_shaderProgram)
@ -592,7 +545,7 @@ void Shader::setUniform(const std::string& name, const Texture& texture)
{
if (m_shaderProgram)
{
ensureGlContext();
TransientContextLock lock;
// Find the location of the variable in the shader
int location = getUniformLocation(name);
@ -627,7 +580,7 @@ void Shader::setUniform(const std::string& name, CurrentTextureType)
{
if (m_shaderProgram)
{
ensureGlContext();
TransientContextLock lock;
// Find the location of the variable in the shader
m_currentTexture = getUniformLocation(name);
@ -787,7 +740,7 @@ unsigned int Shader::getNativeHandle() const
////////////////////////////////////////////////////////////
void Shader::bind(const Shader* shader)
{
ensureGlContext();
TransientContextLock lock;
// Make sure that we can use shaders
if (!isAvailable())
@ -820,10 +773,26 @@ void Shader::bind(const Shader* shader)
////////////////////////////////////////////////////////////
bool Shader::isAvailable()
{
// TODO: Remove this lock when it becomes unnecessary in C++11
Lock lock(mutex);
Lock lock(isAvailableMutex);
static bool available = checkShadersAvailable();
static bool checked = false;
static bool available = false;
if (!checked)
{
checked = true;
TransientContextLock contextLock;
// Make sure that extensions are initialized
sf::priv::ensureExtensionsInit();
available = GLEXT_multitexture &&
GLEXT_shading_language_100 &&
GLEXT_shader_objects &&
GLEXT_vertex_shader &&
GLEXT_fragment_shader;
}
return available;
}
@ -832,10 +801,22 @@ bool Shader::isAvailable()
////////////////////////////////////////////////////////////
bool Shader::isGeometryAvailable()
{
// TODO: Remove this lock when it becomes unnecessary in C++11
Lock lock(mutex);
Lock lock(isAvailableMutex);
static bool available = checkGeometryShadersAvailable();
static bool checked = false;
static bool available = false;
if (!checked)
{
checked = true;
TransientContextLock contextLock;
// Make sure that extensions are initialized
sf::priv::ensureExtensionsInit();
available = isAvailable() && GLEXT_geometry_shader4;
}
return available;
}
@ -844,7 +825,7 @@ bool Shader::isGeometryAvailable()
////////////////////////////////////////////////////////////
bool Shader::compile(const char* vertexShaderCode, const char* geometryShaderCode, const char* fragmentShaderCode)
{
ensureGlContext();
TransientContextLock lock;
// First make sure that we can use shaders
if (!isAvailable())

View File

@ -40,39 +40,19 @@
namespace
{
sf::Mutex mutex;
sf::Mutex idMutex;
sf::Mutex maximumSizeMutex;
// Thread-safe unique identifier generator,
// is used for states cache (see RenderTarget)
sf::Uint64 getUniqueId()
{
sf::Lock lock(mutex);
sf::Lock lock(idMutex);
static sf::Uint64 id = 1; // start at 1, zero is "no texture"
return id++;
}
unsigned int checkMaximumTextureSize()
{
// Create a temporary context in case the user queries
// the size before a GlResource is created, thus
// initializing the shared context
if (!sf::Context::getActiveContext())
{
sf::Context context;
GLint size;
glCheck(glGetIntegerv(GL_MAX_TEXTURE_SIZE, &size));
return static_cast<unsigned int>(size);
}
GLint size;
glCheck(glGetIntegerv(GL_MAX_TEXTURE_SIZE, &size));
return static_cast<unsigned int>(size);
}
}
@ -118,7 +98,7 @@ Texture::~Texture()
// Destroy the OpenGL texture
if (m_texture)
{
ensureGlContext();
TransientContextLock lock;
GLuint texture = static_cast<GLuint>(m_texture);
glCheck(glDeleteTextures(1, &texture));
@ -157,7 +137,7 @@ bool Texture::create(unsigned int width, unsigned int height)
m_pixelsFlipped = false;
m_fboAttachment = false;
ensureGlContext();
TransientContextLock lock;
// Create the OpenGL texture if it doesn't exist yet
if (!m_texture)
@ -265,10 +245,6 @@ bool Texture::loadFromImage(const Image& image, const IntRect& area)
{
update(image);
// Force an OpenGL flush, so that the texture will appear updated
// in all contexts immediately (solves problems in multi-threaded apps)
glCheck(glFlush());
return true;
}
else
@ -290,6 +266,8 @@ bool Texture::loadFromImage(const Image& image, const IntRect& area)
// Create the texture and upload the pixels
if (create(rectangle.width, rectangle.height))
{
TransientContextLock lock;
// Make sure that the current texture binding will be preserved
priv::TextureSaver save;
@ -333,7 +311,7 @@ Image Texture::copyToImage() const
if (!m_texture)
return Image();
ensureGlContext();
TransientContextLock lock;
// Make sure that the current texture binding will be preserved
priv::TextureSaver save;
@ -424,7 +402,7 @@ void Texture::update(const Uint8* pixels, unsigned int width, unsigned int heigh
if (pixels && m_texture)
{
ensureGlContext();
TransientContextLock lock;
// Make sure that the current texture binding will be preserved
priv::TextureSaver save;
@ -436,6 +414,10 @@ void Texture::update(const Uint8* pixels, unsigned int width, unsigned int heigh
m_hasMipmap = false;
m_pixelsFlipped = false;
m_cacheId = getUniqueId();
// Force an OpenGL flush, so that the texture data will appear updated
// in all contexts immediately (solves problems in multi-threaded apps)
glCheck(glFlush());
}
}
@ -470,6 +452,8 @@ void Texture::update(const Window& window, unsigned int x, unsigned int y)
if (m_texture && window.setActive(true))
{
TransientContextLock lock;
// Make sure that the current texture binding will be preserved
priv::TextureSaver save;
@ -480,6 +464,10 @@ void Texture::update(const Window& window, unsigned int x, unsigned int y)
m_hasMipmap = false;
m_pixelsFlipped = true;
m_cacheId = getUniqueId();
// Force an OpenGL flush, so that the texture will appear updated
// in all contexts immediately (solves problems in multi-threaded apps)
glCheck(glFlush());
}
}
@ -493,7 +481,7 @@ void Texture::setSmooth(bool smooth)
if (m_texture)
{
ensureGlContext();
TransientContextLock lock;
// Make sure that the current texture binding will be preserved
priv::TextureSaver save;
@ -544,7 +532,7 @@ void Texture::setRepeated(bool repeated)
if (m_texture)
{
ensureGlContext();
TransientContextLock lock;
// Make sure that the current texture binding will be preserved
priv::TextureSaver save;
@ -586,7 +574,7 @@ bool Texture::generateMipmap()
if (!m_texture)
return false;
ensureGlContext();
TransientContextLock lock;
// Make sure that extensions are initialized
priv::ensureExtensionsInit();
@ -613,7 +601,7 @@ void Texture::invalidateMipmap()
if (!m_hasMipmap)
return;
ensureGlContext();
TransientContextLock lock;
// Make sure that the current texture binding will be preserved
priv::TextureSaver save;
@ -628,7 +616,7 @@ void Texture::invalidateMipmap()
////////////////////////////////////////////////////////////
void Texture::bind(const Texture* texture, CoordinateType coordinateType)
{
ensureGlContext();
TransientContextLock lock;
if (texture && texture->m_texture)
{
@ -684,12 +672,21 @@ void Texture::bind(const Texture* texture, CoordinateType coordinateType)
////////////////////////////////////////////////////////////
unsigned int Texture::getMaximumSize()
{
// TODO: Remove this lock when it becomes unnecessary in C++11
Lock lock(mutex);
Lock lock(maximumSizeMutex);
static unsigned int size = checkMaximumTextureSize();
static bool checked = false;
static GLint size = 0;
return size;
if (!checked)
{
checked = true;
TransientContextLock lock;
glCheck(glGetIntegerv(GL_MAX_TEXTURE_SIZE, &size));
}
return static_cast<unsigned int>(size);
}
@ -722,7 +719,7 @@ unsigned int Texture::getNativeHandle() const
////////////////////////////////////////////////////////////
unsigned int Texture::getValidSize(unsigned int size)
{
ensureGlContext();
TransientContextLock lock;
// Make sure that extensions are initialized
priv::ensureExtensionsInit();

View File

@ -28,24 +28,6 @@
#include <SFML/Window/Context.hpp>
#include <SFML/Window/GlContext.hpp>
#include <SFML/System/ThreadLocalPtr.hpp>
#include <SFML/OpenGL.hpp>
#include <algorithm>
#include <vector>
#include <string>
#if defined(SFML_SYSTEM_WINDOWS)
typedef const GLubyte* (APIENTRY *glGetStringiFuncType)(GLenum, GLuint);
#else
typedef const GLubyte* (*glGetStringiFuncType)(GLenum, GLuint);
#endif
#if !defined(GL_NUM_EXTENSIONS)
#define GL_NUM_EXTENSIONS 0x821D
#endif
namespace
@ -99,70 +81,16 @@ const Context* Context::getActiveContext()
////////////////////////////////////////////////////////////
GlFunctionPointer Context::getFunction(const char* name)
bool Context::isExtensionAvailable(const char* name)
{
return priv::GlContext::getFunction(name);
return priv::GlContext::isExtensionAvailable(name);
}
////////////////////////////////////////////////////////////
bool Context::isExtensionAvailable(const char* name)
GlFunctionPointer Context::getFunction(const char* name)
{
static std::vector<std::string> extensions;
static bool loaded = false;
if (!loaded)
{
const Context* context = getActiveContext();
if (!context)
return false;
const char* extensionString = NULL;
if(context->getSettings().majorVersion < 3)
{
// Try to load the < 3.0 way
extensionString = reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS));
do
{
const char* extension = extensionString;
while(*extensionString && (*extensionString != ' '))
extensionString++;
extensions.push_back(std::string(extension, extensionString));
}
while (*extensionString++);
}
else
{
// Try to load the >= 3.0 way
glGetStringiFuncType glGetStringiFunc = NULL;
glGetStringiFunc = reinterpret_cast<glGetStringiFuncType>(getFunction("glGetStringi"));
if (glGetStringiFunc)
{
int numExtensions = 0;
glGetIntegerv(GL_NUM_EXTENSIONS, &numExtensions);
if (numExtensions)
{
for (unsigned int i = 0; i < static_cast<unsigned int>(numExtensions); ++i)
{
extensionString = reinterpret_cast<const char*>(glGetStringiFunc(GL_EXTENSIONS, i));
extensions.push_back(extensionString);
}
}
}
}
loaded = true;
}
return std::find(extensions.begin(), extensions.end(), name) != extensions.end();
return priv::GlContext::getFunction(name);
}

View File

@ -173,9 +173,12 @@ EglContext::~EglContext()
////////////////////////////////////////////////////////////
bool EglContext::makeCurrent()
bool EglContext::makeCurrent(bool current)
{
return m_surface != EGL_NO_SURFACE && eglCheck(eglMakeCurrent(m_display, m_surface, m_surface, m_context));
if (current)
return m_surface != EGL_NO_SURFACE && eglCheck(eglMakeCurrent(m_display, m_surface, m_surface, m_context));
return m_surface != EGL_NO_SURFACE && eglCheck(eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
}
@ -209,6 +212,9 @@ void EglContext::createContext(EglContext* shared)
else
toShared = EGL_NO_CONTEXT;
if (toShared != EGL_NO_CONTEXT)
eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
// Create EGL context
m_context = eglCheck(eglCreateContext(m_display, m_config, toShared, contextVersion));
}

View File

@ -83,10 +83,12 @@ public:
/// \brief Activate the context as the current target
/// for rendering
///
/// \param current Whether to make the context current or no longer current
///
/// \return True on success, false if any error happened
///
////////////////////////////////////////////////////////////
virtual bool makeCurrent();
virtual bool makeCurrent(bool current);
////////////////////////////////////////////////////////////
/// \brief Display what has been rendered to the context so far

View File

@ -31,9 +31,13 @@
#include <SFML/System/Lock.hpp>
#include <SFML/System/Err.hpp>
#include <SFML/OpenGL.hpp>
#include <algorithm>
#include <vector>
#include <string>
#include <set>
#include <cstdlib>
#include <cstring>
#include <cassert>
#if !defined(SFML_OPENGL_ES)
@ -126,6 +130,8 @@ namespace
// AMD drivers have issues with internal synchronization
// We need to make sure that no operating system context
// or pixel format operations are performed simultaneously
// This mutex is also used to protect the shared context
// from being locked on multiple threads
sf::Mutex mutex;
// This per-thread variable holds the current context for each thread
@ -134,35 +140,12 @@ namespace
// The hidden, inactive context that will be shared with all other contexts
ContextType* sharedContext = NULL;
// Internal contexts
sf::ThreadLocalPtr<sf::Context> internalContext(NULL);
std::set<sf::Context*> internalContexts;
sf::Mutex internalContextsMutex;
// This per-thread variable is set to point to the shared context
// if we had to acquire it when a TransientContextLock was required
sf::ThreadLocalPtr<sf::priv::GlContext> currentSharedContext(NULL);
// Check if the internal context of the current thread is valid
bool hasInternalContext()
{
// The internal context can be null...
if (!internalContext)
return false;
// ... or non-null but deleted from the list of internal contexts
sf::Lock lock(internalContextsMutex);
return internalContexts.find(internalContext) != internalContexts.end();
}
// Retrieve the internal context for the current thread
sf::Context* getInternalContext()
{
if (!hasInternalContext())
{
internalContext = new sf::Context;
sf::Lock lock(internalContextsMutex);
internalContexts.insert(internalContext);
}
return internalContext;
}
// Supported OpenGL extensions
std::vector<std::string> extensions;
}
@ -182,9 +165,53 @@ void GlContext::globalInit()
sharedContext = new ContextType(NULL);
sharedContext->initialize(ContextSettings());
// This call makes sure that:
// - the shared context is inactive (it must never be)
// - another valid context is activated in the current thread
// Load our extensions vector
extensions.clear();
// Check whether a >= 3.0 context is available
int majorVersion = 0;
glGetIntegerv(GL_MAJOR_VERSION, &majorVersion);
if (glGetError() == GL_INVALID_ENUM)
{
// Try to load the < 3.0 way
const char* extensionString = reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS));
do
{
const char* extension = extensionString;
while(*extensionString && (*extensionString != ' '))
extensionString++;
extensions.push_back(std::string(extension, extensionString));
}
while (*extensionString++);
}
else
{
// Try to load the >= 3.0 way
glGetStringiFuncType glGetStringiFunc = NULL;
glGetStringiFunc = reinterpret_cast<glGetStringiFuncType>(getFunction("glGetStringi"));
if (glGetStringiFunc)
{
int numExtensions = 0;
glGetIntegerv(GL_NUM_EXTENSIONS, &numExtensions);
if (numExtensions)
{
for (unsigned int i = 0; i < static_cast<unsigned int>(numExtensions); ++i)
{
const char* extensionString = reinterpret_cast<const char*>(glGetStringiFunc(GL_EXTENSIONS, i));
extensions.push_back(extensionString);
}
}
}
}
// Deactivate the shared context so that others can activate it when necessary
sharedContext->setActive(false);
}
@ -200,31 +227,59 @@ void GlContext::globalCleanup()
// Destroy the shared context
delete sharedContext;
sharedContext = NULL;
// Destroy the internal contexts
Lock internalContextsLock(internalContextsMutex);
for (std::set<Context*>::iterator it = internalContexts.begin(); it != internalContexts.end(); ++it)
delete *it;
internalContexts.clear();
}
////////////////////////////////////////////////////////////
void GlContext::ensureContext()
void GlContext::acquireTransientContext()
{
// If there's no active context on the current thread, activate an internal one
if (!currentContext)
getInternalContext()->setActive(true);
// If a capable context is already active on this thread
// there is no need to use the shared context for the operation
if (currentContext)
{
currentSharedContext = NULL;
return;
}
mutex.lock();
currentSharedContext = sharedContext;
sharedContext->setActive(true);
}
////////////////////////////////////////////////////////////
void GlContext::releaseTransientContext()
{
if (!currentSharedContext)
return;
sharedContext->setActive(false);
mutex.unlock();
}
////////////////////////////////////////////////////////////
GlContext* GlContext::create()
{
// Make sure that there's an active context (context creation may need extensions, and thus a valid context)
assert(sharedContext != NULL);
Lock lock(mutex);
// Create the context
GlContext* context = new ContextType(sharedContext);
GlContext* context = NULL;
// We don't use acquireTransientContext here since we have
// to ensure we have exclusive access to the shared context
// in order to make sure it is not active during context creation
{
sharedContext->setActive(true);
// Create the context
context = new ContextType(sharedContext);
sharedContext->setActive(false);
}
context->initialize(ContextSettings());
return context;
@ -235,12 +290,24 @@ GlContext* GlContext::create()
GlContext* GlContext::create(const ContextSettings& settings, const WindowImpl* owner, unsigned int bitsPerPixel)
{
// Make sure that there's an active context (context creation may need extensions, and thus a valid context)
ensureContext();
assert(sharedContext != NULL);
Lock lock(mutex);
// Create the context
GlContext* context = new ContextType(sharedContext, settings, owner, bitsPerPixel);
GlContext* context = NULL;
// We don't use acquireTransientContext here since we have
// to ensure we have exclusive access to the shared context
// in order to make sure it is not active during context creation
{
sharedContext->setActive(true);
// Create the context
context = new ContextType(sharedContext, settings, owner, bitsPerPixel);
sharedContext->setActive(false);
}
context->initialize(settings);
context->checkSettings(settings);
@ -252,12 +319,24 @@ GlContext* GlContext::create(const ContextSettings& settings, const WindowImpl*
GlContext* GlContext::create(const ContextSettings& settings, unsigned int width, unsigned int height)
{
// Make sure that there's an active context (context creation may need extensions, and thus a valid context)
ensureContext();
assert(sharedContext != NULL);
Lock lock(mutex);
// Create the context
GlContext* context = new ContextType(sharedContext, settings, width, height);
GlContext* context = NULL;
// We don't use acquireTransientContext here since we have
// to ensure we have exclusive access to the shared context
// in order to make sure it is not active during context creation
{
sharedContext->setActive(true);
// Create the context
context = new ContextType(sharedContext, settings, width, height);
sharedContext->setActive(false);
}
context->initialize(settings);
context->checkSettings(settings);
@ -265,6 +344,13 @@ GlContext* GlContext::create(const ContextSettings& settings, unsigned int width
}
////////////////////////////////////////////////////////////
bool GlContext::isExtensionAvailable(const char* name)
{
return std::find(extensions.begin(), extensions.end(), name) != extensions.end();
}
////////////////////////////////////////////////////////////
GlFunctionPointer GlContext::getFunction(const char* name)
{
@ -287,7 +373,10 @@ GlContext::~GlContext()
{
// Deactivate the context before killing it, unless we're inside Cleanup()
if (sharedContext)
setActive(false);
{
if (this == currentContext)
currentContext = NULL;
}
}
@ -308,7 +397,7 @@ bool GlContext::setActive(bool active)
Lock lock(mutex);
// Activate the context
if (makeCurrent())
if (makeCurrent(true))
{
// Set it as the new current context for this thread
currentContext = this;
@ -329,9 +418,18 @@ bool GlContext::setActive(bool active)
{
if (this == currentContext)
{
// To deactivate the context, we actually activate another one so that we make
// sure that there is always an active context for subsequent graphics operations
return getInternalContext()->setActive(true);
Lock lock(mutex);
// Deactivate the context
if (makeCurrent(false))
{
currentContext = NULL;
return true;
}
else
{
return false;
}
}
else
{

View File

@ -73,10 +73,16 @@ public:
static void globalCleanup();
////////////////////////////////////////////////////////////
/// \brief Ensures that an OpenGL context is active in the current thread
/// \brief Acquires a context for short-term use on the current thread
///
////////////////////////////////////////////////////////////
static void ensureContext();
static void acquireTransientContext();
////////////////////////////////////////////////////////////
/// \brief Releases a context after short-term use on the current thread
///
////////////////////////////////////////////////////////////
static void releaseTransientContext();
////////////////////////////////////////////////////////////
/// \brief Create a new context, not associated to a window
@ -120,6 +126,16 @@ public:
static GlContext* create(const ContextSettings& settings, unsigned int width, unsigned int height);
public:
////////////////////////////////////////////////////////////
/// \brief Check whether a given OpenGL extension is available
///
/// \param name Name of the extension to check for
///
/// \return True if available, false if unavailable
///
////////////////////////////////////////////////////////////
static bool isExtensionAvailable(const char* name);
////////////////////////////////////////////////////////////
/// \brief Get the address of an OpenGL function
///
@ -197,10 +213,12 @@ protected:
/// \brief Activate the context as the current target
/// for rendering
///
/// \param current Whether to make the context current or no longer current
///
/// \return True on success, false if any error happened
///
////////////////////////////////////////////////////////////
virtual bool makeCurrent() = 0;
virtual bool makeCurrent(bool current) = 0;
////////////////////////////////////////////////////////////
/// \brief Evaluate a pixel format configuration

View File

@ -27,6 +27,7 @@
////////////////////////////////////////////////////////////
#include <SFML/Window/GlResource.hpp>
#include <SFML/Window/GlContext.hpp>
#include <SFML/Window/Context.hpp>
#include <SFML/System/Mutex.hpp>
#include <SFML/System/Lock.hpp>
@ -44,20 +45,15 @@ namespace sf
////////////////////////////////////////////////////////////
GlResource::GlResource()
{
{
// Protect from concurrent access
Lock lock(mutex);
// Protect from concurrent access
Lock lock(mutex);
// If this is the very first resource, trigger the global context initialization
if (count == 0)
priv::GlContext::globalInit();
// If this is the very first resource, trigger the global context initialization
if (count == 0)
priv::GlContext::globalInit();
// Increment the resources counter
count++;
}
// Now make sure that there is an active OpenGL context in the current thread
priv::GlContext::ensureContext();
// Increment the resources counter
count++;
}
@ -77,9 +73,31 @@ GlResource::~GlResource()
////////////////////////////////////////////////////////////
void GlResource::ensureGlContext()
GlResource::TransientContextLock::TransientContextLock() :
m_context(0)
{
priv::GlContext::ensureContext();
Lock lock(mutex);
if (count == 0)
{
m_context = new Context;
return;
}
priv::GlContext::acquireTransientContext();
}
////////////////////////////////////////////////////////////
GlResource::TransientContextLock::~TransientContextLock()
{
if (m_context)
{
delete m_context;
return;
}
priv::GlContext::releaseTransientContext();
}
} // namespace sf

View File

@ -137,10 +137,12 @@ protected:
/// \brief Activate the context as the current target
/// for rendering
///
/// \param current Whether to make the context current or no longer current
///
/// \return True on success, false if any error happened
///
////////////////////////////////////////////////////////////
virtual bool makeCurrent();
virtual bool makeCurrent(bool current);
private:
////////////////////////////////////////////////////////////

View File

@ -104,6 +104,10 @@ m_window(0)
SFContext::~SFContext()
{
[m_context clearDrawable];
if (m_context == [NSOpenGLContext currentContext])
[NSOpenGLContext clearCurrentContext];
[m_context release];
[m_view release]; // Might be nil but we don't care.
@ -124,10 +128,18 @@ GlFunctionPointer SFContext::getFunction(const char* name)
////////////////////////////////////////////////////////////
bool SFContext::makeCurrent()
bool SFContext::makeCurrent(bool current)
{
[m_context makeCurrentContext];
return m_context == [NSOpenGLContext currentContext]; // Should be true.
if (current)
{
[m_context makeCurrentContext];
return m_context == [NSOpenGLContext currentContext]; // Should be true.
}
else
{
[NSOpenGLContext clearCurrentContext];
return m_context != [NSOpenGLContext currentContext]; // Should be true.
}
}
@ -257,6 +269,17 @@ void SFContext::createContext(SFContext* shared,
// Use the shared context if one is given.
NSOpenGLContext* sharedContext = shared != NULL ? shared->m_context : nil;
if (sharedContext != nil)
{
[NSOpenGLContext clearCurrentContext];
if (sharedContext == [NSOpenGLContext currentContext])
{
sf::err() << "Failed to deactivate shared context before sharing" << std::endl;
return;
}
}
// Create the context.
m_context = [[NSOpenGLContext alloc] initWithFormat:pixFmt
shareContext:sharedContext];

View File

@ -26,6 +26,8 @@
// Headers
////////////////////////////////////////////////////////////
#include <SFML/System/Err.hpp>
#include <SFML/System/Mutex.hpp>
#include <SFML/System/Lock.hpp>
#include <SFML/Window/Unix/Display.hpp>
#include <X11/keysym.h>
#include <cassert>
@ -38,6 +40,7 @@ namespace
// The shared display and its reference counter
Display* sharedDisplay = NULL;
unsigned int referenceCount = 0;
sf::Mutex mutex;
typedef std::map<std::string, Atom> AtomMap;
AtomMap atoms;
@ -50,6 +53,8 @@ namespace priv
////////////////////////////////////////////////////////////
Display* OpenDisplay()
{
Lock lock(mutex);
if (referenceCount == 0)
{
sharedDisplay = XOpenDisplay(NULL);
@ -71,6 +76,8 @@ Display* OpenDisplay()
////////////////////////////////////////////////////////////
void CloseDisplay(Display* display)
{
Lock lock(mutex);
assert(display == sharedDisplay);
referenceCount--;

View File

@ -211,7 +211,7 @@ GlFunctionPointer GlxContext::getFunction(const char* name)
////////////////////////////////////////////////////////////
bool GlxContext::makeCurrent()
bool GlxContext::makeCurrent(bool current)
{
if (!m_context)
return false;
@ -222,13 +222,20 @@ bool GlxContext::makeCurrent()
bool result = false;
if (m_pbuffer)
if (current)
{
result = glXMakeContextCurrent(m_display, m_pbuffer, m_pbuffer, m_context);
if (m_pbuffer)
{
result = glXMakeContextCurrent(m_display, m_pbuffer, m_pbuffer, m_context);
}
else if (m_window)
{
result = glXMakeCurrent(m_display, m_window, m_context);
}
}
else if (m_window)
else
{
result = glXMakeCurrent(m_display, m_window, m_context);
result = glXMakeCurrent(m_display, None, NULL);
}
#if defined(GLX_DEBUGGING)
@ -686,6 +693,15 @@ void GlxContext::createContext(GlxContext* shared)
// On an error, glXCreateContextAttribsARB will return 0 anyway
GlxErrorHandler handler(m_display);
if (toShare)
{
if (!glXMakeCurrent(m_display, None, NULL))
{
err() << "Failed to deactivate shared context before sharing" << std::endl;
return;
}
}
// Create the context
m_context = glXCreateContextAttribsARB(m_display, *config, toShare, true, &attributes[0]);
@ -732,6 +748,15 @@ void GlxContext::createContext(GlxContext* shared)
GlxErrorHandler handler(m_display);
#endif
if (toShare)
{
if (!glXMakeCurrent(m_display, None, NULL))
{
err() << "Failed to deactivate shared context before sharing" << std::endl;
return;
}
}
// Create the context, using the target window's visual
m_context = glXCreateContext(m_display, visualInfo, toShare, true);

View File

@ -94,10 +94,12 @@ public:
////////////////////////////////////////////////////////////
/// \brief Activate the context as the current target for rendering
///
/// \param current Whether to make the context current or no longer current
///
/// \return True on success, false if any error happened
///
////////////////////////////////////////////////////////////
virtual bool makeCurrent();
virtual bool makeCurrent(bool current);
////////////////////////////////////////////////////////////
/// \brief Display what has been rendered to the context so far

View File

@ -200,9 +200,9 @@ GlFunctionPointer WglContext::getFunction(const char* name)
////////////////////////////////////////////////////////////
bool WglContext::makeCurrent()
bool WglContext::makeCurrent(bool current)
{
return m_deviceContext && m_context && wglMakeCurrent(m_deviceContext, m_context);
return m_deviceContext && m_context && wglMakeCurrent(current ? m_deviceContext : NULL, current ? m_context : NULL);
}
@ -599,6 +599,18 @@ void WglContext::createContext(WglContext* shared)
attributes.push_back(0);
attributes.push_back(0);
if (sharedContext)
{
static Mutex mutex;
Lock lock(mutex);
if (!wglMakeCurrent(NULL, NULL))
{
err() << "Failed to deactivate shared context before sharing: " << getErrorString(GetLastError()).toAnsiString() << std::endl;
return;
}
}
// Create the context
m_context = wglCreateContextAttribsARB(m_deviceContext, sharedContext, &attributes[0]);
}
@ -657,6 +669,12 @@ void WglContext::createContext(WglContext* shared)
static Mutex mutex;
Lock lock(mutex);
if (!wglMakeCurrent(NULL, NULL))
{
err() << "Failed to deactivate shared context before sharing: " << getErrorString(GetLastError()).toAnsiString() << std::endl;
return;
}
if (!wglShareLists(sharedContext, m_context))
err() << "Failed to share the OpenGL context: " << getErrorString(GetLastError()).toAnsiString() << std::endl;
}

View File

@ -93,10 +93,12 @@ public:
////////////////////////////////////////////////////////////
/// \brief Activate the context as the current target for rendering
///
/// \param current Whether to make the context current or no longer current
///
/// \return True on success, false if any error happened
///
////////////////////////////////////////////////////////////
virtual bool makeCurrent();
virtual bool makeCurrent(bool current);
////////////////////////////////////////////////////////////
/// \brief Display what has been rendered to the context so far

View File

@ -126,10 +126,12 @@ protected:
/// \brief Activate the context as the current target
/// for rendering
///
/// \param current Whether to make the context current or no longer current
///
/// \return True on success, false if any error happened
///
////////////////////////////////////////////////////////////
virtual bool makeCurrent();
virtual bool makeCurrent(bool current);
private:

View File

@ -107,6 +107,9 @@ EaglContext::~EaglContext()
// Restore the previous context
[EAGLContext setCurrentContext:previousContext];
if (m_context == [EAGLContext currentContext])
[EAGLContext setCurrentContext:nil];
}
}
@ -167,9 +170,12 @@ void EaglContext::recreateRenderBuffers(SFView* glView)
////////////////////////////////////////////////////////////
bool EaglContext::makeCurrent()
bool EaglContext::makeCurrent(bool current)
{
return [EAGLContext setCurrentContext:m_context];
if (current)
return [EAGLContext setCurrentContext:m_context];
return [EAGLContext setCurrentContext:nil];
}
@ -215,12 +221,18 @@ void EaglContext::createContext(EaglContext* shared,
// Create the context
if (shared)
{
[EAGLContext setCurrentContext:nil];
m_context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1 sharegroup:[shared->m_context sharegroup]];
}
else
{
m_context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
}
// Activate it
makeCurrent();
makeCurrent(true);
// Create the framebuffer (this is the only allowed drawable on iOS)
glGenFramebuffersOES(1, &m_framebuffer);
@ -230,6 +242,9 @@ void EaglContext::createContext(EaglContext* shared,
// Attach the context to the GL view for future updates
window->getGlView().context = this;
// Deactivate it
makeCurrent(false);
}
} // namespace priv