diff --git a/include/SFML/Window/GlResource.hpp b/include/SFML/Window/GlResource.hpp index 9deb35a52..876f177cd 100644 --- a/include/SFML/Window/GlResource.hpp +++ b/include/SFML/Window/GlResource.hpp @@ -29,10 +29,14 @@ // Headers //////////////////////////////////////////////////////////// #include +#include 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 diff --git a/src/SFML/Graphics/Font.cpp b/src/SFML/Graphics/Font.cpp index f58454aab..b786b9d4a 100644 --- a/src/SFML/Graphics/Font.cpp +++ b/src/SFML/Graphics/Font.cpp @@ -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; } diff --git a/src/SFML/Graphics/GLExtensions.cpp b/src/SFML/Graphics/GLExtensions.cpp index 21f718b80..865fa38bd 100644 --- a/src/SFML/Graphics/GLExtensions.cpp +++ b/src/SFML/Graphics/GLExtensions.cpp @@ -29,6 +29,14 @@ #include #include +#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 } diff --git a/src/SFML/Graphics/RenderTextureImplFBO.cpp b/src/SFML/Graphics/RenderTextureImplFBO.cpp index 3ce7006f9..33adfb071 100644 --- a/src/SFML/Graphics/RenderTextureImplFBO.cpp +++ b/src/SFML/Graphics/RenderTextureImplFBO.cpp @@ -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(); diff --git a/src/SFML/Graphics/Shader.cpp b/src/SFML/Graphics/Shader.cpp index d8258aee1..14a300c69 100644 --- a/src/SFML/Graphics/Shader.cpp +++ b/src/SFML/Graphics/Shader.cpp @@ -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 std::vector flatten(const sf::Vector2* 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()) diff --git a/src/SFML/Graphics/Texture.cpp b/src/SFML/Graphics/Texture.cpp index a3e813f23..75e1313cc 100644 --- a/src/SFML/Graphics/Texture.cpp +++ b/src/SFML/Graphics/Texture.cpp @@ -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(size); - } - - GLint size; - glCheck(glGetIntegerv(GL_MAX_TEXTURE_SIZE, &size)); - - return static_cast(size); - } } @@ -118,7 +98,7 @@ Texture::~Texture() // Destroy the OpenGL texture if (m_texture) { - ensureGlContext(); + TransientContextLock lock; GLuint texture = static_cast(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(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(); diff --git a/src/SFML/Window/Context.cpp b/src/SFML/Window/Context.cpp index 2d51bbc23..7617e1af9 100644 --- a/src/SFML/Window/Context.cpp +++ b/src/SFML/Window/Context.cpp @@ -28,24 +28,6 @@ #include #include #include -#include -#include -#include -#include - -#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 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(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(getFunction("glGetStringi")); - - if (glGetStringiFunc) - { - int numExtensions = 0; - glGetIntegerv(GL_NUM_EXTENSIONS, &numExtensions); - - if (numExtensions) - { - for (unsigned int i = 0; i < static_cast(numExtensions); ++i) - { - extensionString = reinterpret_cast(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); } diff --git a/src/SFML/Window/EglContext.cpp b/src/SFML/Window/EglContext.cpp index f6686f179..03c419798 100644 --- a/src/SFML/Window/EglContext.cpp +++ b/src/SFML/Window/EglContext.cpp @@ -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)); } diff --git a/src/SFML/Window/EglContext.hpp b/src/SFML/Window/EglContext.hpp index 6df6a536e..a889c3ac7 100644 --- a/src/SFML/Window/EglContext.hpp +++ b/src/SFML/Window/EglContext.hpp @@ -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 diff --git a/src/SFML/Window/GlContext.cpp b/src/SFML/Window/GlContext.cpp index b74725e7c..8ae4b3abc 100644 --- a/src/SFML/Window/GlContext.cpp +++ b/src/SFML/Window/GlContext.cpp @@ -31,9 +31,13 @@ #include #include #include +#include +#include +#include #include #include #include +#include #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 internalContext(NULL); - std::set 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 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 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(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(getFunction("glGetStringi")); + + if (glGetStringiFunc) + { + int numExtensions = 0; + glGetIntegerv(GL_NUM_EXTENSIONS, &numExtensions); + + if (numExtensions) + { + for (unsigned int i = 0; i < static_cast(numExtensions); ++i) + { + const char* extensionString = reinterpret_cast(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::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 { diff --git a/src/SFML/Window/GlContext.hpp b/src/SFML/Window/GlContext.hpp index 8c4ce0192..55b6c1f10 100644 --- a/src/SFML/Window/GlContext.hpp +++ b/src/SFML/Window/GlContext.hpp @@ -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 diff --git a/src/SFML/Window/GlResource.cpp b/src/SFML/Window/GlResource.cpp index dfcbe7a9b..a3cdddf6d 100644 --- a/src/SFML/Window/GlResource.cpp +++ b/src/SFML/Window/GlResource.cpp @@ -27,6 +27,7 @@ //////////////////////////////////////////////////////////// #include #include +#include #include #include @@ -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 diff --git a/src/SFML/Window/OSX/SFContext.hpp b/src/SFML/Window/OSX/SFContext.hpp index 3e2a979db..75e679428 100644 --- a/src/SFML/Window/OSX/SFContext.hpp +++ b/src/SFML/Window/OSX/SFContext.hpp @@ -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: //////////////////////////////////////////////////////////// diff --git a/src/SFML/Window/OSX/SFContext.mm b/src/SFML/Window/OSX/SFContext.mm index 097000777..d458a6e1c 100644 --- a/src/SFML/Window/OSX/SFContext.mm +++ b/src/SFML/Window/OSX/SFContext.mm @@ -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]; diff --git a/src/SFML/Window/Unix/Display.cpp b/src/SFML/Window/Unix/Display.cpp index fce86015d..7445508fa 100644 --- a/src/SFML/Window/Unix/Display.cpp +++ b/src/SFML/Window/Unix/Display.cpp @@ -26,6 +26,8 @@ // Headers //////////////////////////////////////////////////////////// #include +#include +#include #include #include #include @@ -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 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--; diff --git a/src/SFML/Window/Unix/GlxContext.cpp b/src/SFML/Window/Unix/GlxContext.cpp index 8b4711d4f..4efee9f02 100644 --- a/src/SFML/Window/Unix/GlxContext.cpp +++ b/src/SFML/Window/Unix/GlxContext.cpp @@ -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); diff --git a/src/SFML/Window/Unix/GlxContext.hpp b/src/SFML/Window/Unix/GlxContext.hpp index 959c3299b..360906b6f 100644 --- a/src/SFML/Window/Unix/GlxContext.hpp +++ b/src/SFML/Window/Unix/GlxContext.hpp @@ -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 diff --git a/src/SFML/Window/Win32/WglContext.cpp b/src/SFML/Window/Win32/WglContext.cpp index 486946fe5..a44c0c5ec 100644 --- a/src/SFML/Window/Win32/WglContext.cpp +++ b/src/SFML/Window/Win32/WglContext.cpp @@ -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; } diff --git a/src/SFML/Window/Win32/WglContext.hpp b/src/SFML/Window/Win32/WglContext.hpp index ceecc2dfa..9017c9383 100644 --- a/src/SFML/Window/Win32/WglContext.hpp +++ b/src/SFML/Window/Win32/WglContext.hpp @@ -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 diff --git a/src/SFML/Window/iOS/EaglContext.hpp b/src/SFML/Window/iOS/EaglContext.hpp index a3c48eac4..2bcdc36ef 100644 --- a/src/SFML/Window/iOS/EaglContext.hpp +++ b/src/SFML/Window/iOS/EaglContext.hpp @@ -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: diff --git a/src/SFML/Window/iOS/EaglContext.mm b/src/SFML/Window/iOS/EaglContext.mm index e03371883..37ab4c3cc 100644 --- a/src/SFML/Window/iOS/EaglContext.mm +++ b/src/SFML/Window/iOS/EaglContext.mm @@ -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