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 // Headers
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
#include <SFML/Window/Export.hpp> #include <SFML/Window/Export.hpp>
#include <SFML/System/NonCopyable.hpp>
namespace sf namespace sf
{ {
class Context;
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Base class for classes that require an OpenGL context /// \brief Base class for classes that require an OpenGL context
/// ///
@ -54,10 +58,27 @@ protected:
~GlResource(); ~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 } // namespace sf

View File

@ -645,10 +645,6 @@ Glyph Font::loadGlyph(Uint32 codePoint, unsigned int characterSize, bool bold, f
// Delete the FT glyph // Delete the FT glyph
FT_Done_Glyph(glyphDesc); 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 :) // Done :)
return glyph; return glyph;
} }

View File

@ -29,6 +29,14 @@
#include <SFML/Window/Context.hpp> #include <SFML/Window/Context.hpp>
#include <SFML/System/Err.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 namespace sf
{ {
@ -41,22 +49,41 @@ void ensureExtensionsInit()
static bool initialized = false; static bool initialized = false;
if (!initialized) if (!initialized)
{ {
const Context* context = Context::getActiveContext(); initialized = true;
if (!context)
return;
sfogl_LoadFunctions(); 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() << "sfml-graphics requires support for OpenGL 1.1 or greater" << std::endl;
err() << "Ensure that hardware acceleration is enabled if available" << std::endl; err() << "Ensure that hardware acceleration is enabled if available" << std::endl;
} }
initialized = true;
} }
#endif #endif
} }

View File

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

View File

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

View File

@ -40,39 +40,19 @@
namespace namespace
{ {
sf::Mutex mutex; sf::Mutex idMutex;
sf::Mutex maximumSizeMutex;
// Thread-safe unique identifier generator, // Thread-safe unique identifier generator,
// is used for states cache (see RenderTarget) // is used for states cache (see RenderTarget)
sf::Uint64 getUniqueId() sf::Uint64 getUniqueId()
{ {
sf::Lock lock(mutex); sf::Lock lock(idMutex);
static sf::Uint64 id = 1; // start at 1, zero is "no texture" static sf::Uint64 id = 1; // start at 1, zero is "no texture"
return id++; 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 // Destroy the OpenGL texture
if (m_texture) if (m_texture)
{ {
ensureGlContext(); TransientContextLock lock;
GLuint texture = static_cast<GLuint>(m_texture); GLuint texture = static_cast<GLuint>(m_texture);
glCheck(glDeleteTextures(1, &texture)); glCheck(glDeleteTextures(1, &texture));
@ -157,7 +137,7 @@ bool Texture::create(unsigned int width, unsigned int height)
m_pixelsFlipped = false; m_pixelsFlipped = false;
m_fboAttachment = false; m_fboAttachment = false;
ensureGlContext(); TransientContextLock lock;
// Create the OpenGL texture if it doesn't exist yet // Create the OpenGL texture if it doesn't exist yet
if (!m_texture) if (!m_texture)
@ -265,10 +245,6 @@ bool Texture::loadFromImage(const Image& image, const IntRect& area)
{ {
update(image); 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; return true;
} }
else else
@ -290,6 +266,8 @@ bool Texture::loadFromImage(const Image& image, const IntRect& area)
// Create the texture and upload the pixels // Create the texture and upload the pixels
if (create(rectangle.width, rectangle.height)) if (create(rectangle.width, rectangle.height))
{ {
TransientContextLock lock;
// Make sure that the current texture binding will be preserved // Make sure that the current texture binding will be preserved
priv::TextureSaver save; priv::TextureSaver save;
@ -333,7 +311,7 @@ Image Texture::copyToImage() const
if (!m_texture) if (!m_texture)
return Image(); return Image();
ensureGlContext(); TransientContextLock lock;
// Make sure that the current texture binding will be preserved // Make sure that the current texture binding will be preserved
priv::TextureSaver save; priv::TextureSaver save;
@ -424,7 +402,7 @@ void Texture::update(const Uint8* pixels, unsigned int width, unsigned int heigh
if (pixels && m_texture) if (pixels && m_texture)
{ {
ensureGlContext(); TransientContextLock lock;
// Make sure that the current texture binding will be preserved // Make sure that the current texture binding will be preserved
priv::TextureSaver save; priv::TextureSaver save;
@ -436,6 +414,10 @@ void Texture::update(const Uint8* pixels, unsigned int width, unsigned int heigh
m_hasMipmap = false; m_hasMipmap = false;
m_pixelsFlipped = false; m_pixelsFlipped = false;
m_cacheId = getUniqueId(); 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)) if (m_texture && window.setActive(true))
{ {
TransientContextLock lock;
// Make sure that the current texture binding will be preserved // Make sure that the current texture binding will be preserved
priv::TextureSaver save; priv::TextureSaver save;
@ -480,6 +464,10 @@ void Texture::update(const Window& window, unsigned int x, unsigned int y)
m_hasMipmap = false; m_hasMipmap = false;
m_pixelsFlipped = true; m_pixelsFlipped = true;
m_cacheId = getUniqueId(); 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) if (m_texture)
{ {
ensureGlContext(); TransientContextLock lock;
// Make sure that the current texture binding will be preserved // Make sure that the current texture binding will be preserved
priv::TextureSaver save; priv::TextureSaver save;
@ -544,7 +532,7 @@ void Texture::setRepeated(bool repeated)
if (m_texture) if (m_texture)
{ {
ensureGlContext(); TransientContextLock lock;
// Make sure that the current texture binding will be preserved // Make sure that the current texture binding will be preserved
priv::TextureSaver save; priv::TextureSaver save;
@ -586,7 +574,7 @@ bool Texture::generateMipmap()
if (!m_texture) if (!m_texture)
return false; return false;
ensureGlContext(); TransientContextLock lock;
// Make sure that extensions are initialized // Make sure that extensions are initialized
priv::ensureExtensionsInit(); priv::ensureExtensionsInit();
@ -613,7 +601,7 @@ void Texture::invalidateMipmap()
if (!m_hasMipmap) if (!m_hasMipmap)
return; return;
ensureGlContext(); TransientContextLock lock;
// Make sure that the current texture binding will be preserved // Make sure that the current texture binding will be preserved
priv::TextureSaver save; priv::TextureSaver save;
@ -628,7 +616,7 @@ void Texture::invalidateMipmap()
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void Texture::bind(const Texture* texture, CoordinateType coordinateType) void Texture::bind(const Texture* texture, CoordinateType coordinateType)
{ {
ensureGlContext(); TransientContextLock lock;
if (texture && texture->m_texture) if (texture && texture->m_texture)
{ {
@ -684,12 +672,21 @@ void Texture::bind(const Texture* texture, CoordinateType coordinateType)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
unsigned int Texture::getMaximumSize() unsigned int Texture::getMaximumSize()
{ {
// TODO: Remove this lock when it becomes unnecessary in C++11 Lock lock(maximumSizeMutex);
Lock lock(mutex);
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) unsigned int Texture::getValidSize(unsigned int size)
{ {
ensureGlContext(); TransientContextLock lock;
// Make sure that extensions are initialized // Make sure that extensions are initialized
priv::ensureExtensionsInit(); priv::ensureExtensionsInit();

View File

@ -28,24 +28,6 @@
#include <SFML/Window/Context.hpp> #include <SFML/Window/Context.hpp>
#include <SFML/Window/GlContext.hpp> #include <SFML/Window/GlContext.hpp>
#include <SFML/System/ThreadLocalPtr.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 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; return priv::GlContext::getFunction(name);
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();
} }

View File

@ -173,9 +173,12 @@ EglContext::~EglContext()
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
bool EglContext::makeCurrent() bool EglContext::makeCurrent(bool current)
{ {
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, 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 else
toShared = EGL_NO_CONTEXT; toShared = EGL_NO_CONTEXT;
if (toShared != EGL_NO_CONTEXT)
eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
// Create EGL context // Create EGL context
m_context = eglCheck(eglCreateContext(m_display, m_config, toShared, contextVersion)); 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 /// \brief Activate the context as the current target
/// for rendering /// for rendering
/// ///
/// \param current Whether to make the context current or no longer current
///
/// \return True on success, false if any error happened /// \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 /// \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/Lock.hpp>
#include <SFML/System/Err.hpp> #include <SFML/System/Err.hpp>
#include <SFML/OpenGL.hpp> #include <SFML/OpenGL.hpp>
#include <algorithm>
#include <vector>
#include <string>
#include <set> #include <set>
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include <cassert>
#if !defined(SFML_OPENGL_ES) #if !defined(SFML_OPENGL_ES)
@ -126,6 +130,8 @@ namespace
// AMD drivers have issues with internal synchronization // AMD drivers have issues with internal synchronization
// We need to make sure that no operating system context // We need to make sure that no operating system context
// or pixel format operations are performed simultaneously // 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; sf::Mutex mutex;
// This per-thread variable holds the current context for each thread // 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 // The hidden, inactive context that will be shared with all other contexts
ContextType* sharedContext = NULL; ContextType* sharedContext = NULL;
// Internal contexts // This per-thread variable is set to point to the shared context
sf::ThreadLocalPtr<sf::Context> internalContext(NULL); // if we had to acquire it when a TransientContextLock was required
std::set<sf::Context*> internalContexts; sf::ThreadLocalPtr<sf::priv::GlContext> currentSharedContext(NULL);
sf::Mutex internalContextsMutex;
// Check if the internal context of the current thread is valid // Supported OpenGL extensions
bool hasInternalContext() std::vector<std::string> extensions;
{
// 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;
}
} }
@ -182,9 +165,53 @@ void GlContext::globalInit()
sharedContext = new ContextType(NULL); sharedContext = new ContextType(NULL);
sharedContext->initialize(ContextSettings()); sharedContext->initialize(ContextSettings());
// This call makes sure that: // Load our extensions vector
// - the shared context is inactive (it must never be) extensions.clear();
// - another valid context is activated in the current thread
// 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); sharedContext->setActive(false);
} }
@ -200,31 +227,59 @@ void GlContext::globalCleanup()
// Destroy the shared context // Destroy the shared context
delete sharedContext; delete sharedContext;
sharedContext = NULL; 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 a capable context is already active on this thread
if (!currentContext) // there is no need to use the shared context for the operation
getInternalContext()->setActive(true); 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() 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); Lock lock(mutex);
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 // Create the context
GlContext* context = new ContextType(sharedContext); context = new ContextType(sharedContext);
sharedContext->setActive(false);
}
context->initialize(ContextSettings()); context->initialize(ContextSettings());
return context; return context;
@ -235,12 +290,24 @@ GlContext* GlContext::create()
GlContext* GlContext::create(const ContextSettings& settings, const WindowImpl* owner, unsigned int bitsPerPixel) 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) // 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); Lock lock(mutex);
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 // Create the context
GlContext* context = new ContextType(sharedContext, settings, owner, bitsPerPixel); context = new ContextType(sharedContext, settings, owner, bitsPerPixel);
sharedContext->setActive(false);
}
context->initialize(settings); context->initialize(settings);
context->checkSettings(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) 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) // 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); Lock lock(mutex);
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 // Create the context
GlContext* context = new ContextType(sharedContext, settings, width, height); context = new ContextType(sharedContext, settings, width, height);
sharedContext->setActive(false);
}
context->initialize(settings); context->initialize(settings);
context->checkSettings(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) GlFunctionPointer GlContext::getFunction(const char* name)
{ {
@ -287,7 +373,10 @@ GlContext::~GlContext()
{ {
// Deactivate the context before killing it, unless we're inside Cleanup() // Deactivate the context before killing it, unless we're inside Cleanup()
if (sharedContext) if (sharedContext)
setActive(false); {
if (this == currentContext)
currentContext = NULL;
}
} }
@ -308,7 +397,7 @@ bool GlContext::setActive(bool active)
Lock lock(mutex); Lock lock(mutex);
// Activate the context // Activate the context
if (makeCurrent()) if (makeCurrent(true))
{ {
// Set it as the new current context for this thread // Set it as the new current context for this thread
currentContext = this; currentContext = this;
@ -329,9 +418,18 @@ bool GlContext::setActive(bool active)
{ {
if (this == currentContext) if (this == currentContext)
{ {
// To deactivate the context, we actually activate another one so that we make Lock lock(mutex);
// sure that there is always an active context for subsequent graphics operations
return getInternalContext()->setActive(true); // Deactivate the context
if (makeCurrent(false))
{
currentContext = NULL;
return true;
}
else
{
return false;
}
} }
else else
{ {

View File

@ -73,10 +73,16 @@ public:
static void globalCleanup(); 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 /// \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); static GlContext* create(const ContextSettings& settings, unsigned int width, unsigned int height);
public: 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 /// \brief Get the address of an OpenGL function
/// ///
@ -197,10 +213,12 @@ protected:
/// \brief Activate the context as the current target /// \brief Activate the context as the current target
/// for rendering /// for rendering
/// ///
/// \param current Whether to make the context current or no longer current
///
/// \return True on success, false if any error happened /// \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 /// \brief Evaluate a pixel format configuration

View File

@ -27,6 +27,7 @@
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
#include <SFML/Window/GlResource.hpp> #include <SFML/Window/GlResource.hpp>
#include <SFML/Window/GlContext.hpp> #include <SFML/Window/GlContext.hpp>
#include <SFML/Window/Context.hpp>
#include <SFML/System/Mutex.hpp> #include <SFML/System/Mutex.hpp>
#include <SFML/System/Lock.hpp> #include <SFML/System/Lock.hpp>
@ -44,7 +45,6 @@ namespace sf
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
GlResource::GlResource() GlResource::GlResource()
{ {
{
// Protect from concurrent access // Protect from concurrent access
Lock lock(mutex); Lock lock(mutex);
@ -54,10 +54,6 @@ GlResource::GlResource()
// Increment the resources counter // Increment the resources counter
count++; count++;
}
// Now make sure that there is an active OpenGL context in the current thread
priv::GlContext::ensureContext();
} }
@ -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 } // namespace sf

View File

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

View File

@ -104,6 +104,10 @@ m_window(0)
SFContext::~SFContext() SFContext::~SFContext()
{ {
[m_context clearDrawable]; [m_context clearDrawable];
if (m_context == [NSOpenGLContext currentContext])
[NSOpenGLContext clearCurrentContext];
[m_context release]; [m_context release];
[m_view release]; // Might be nil but we don't care. [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)
{ {
if (current)
{
[m_context makeCurrentContext]; [m_context makeCurrentContext];
return m_context == [NSOpenGLContext currentContext]; // Should be true. 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. // Use the shared context if one is given.
NSOpenGLContext* sharedContext = shared != NULL ? shared->m_context : nil; 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. // Create the context.
m_context = [[NSOpenGLContext alloc] initWithFormat:pixFmt m_context = [[NSOpenGLContext alloc] initWithFormat:pixFmt
shareContext:sharedContext]; shareContext:sharedContext];

View File

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

View File

@ -211,7 +211,7 @@ GlFunctionPointer GlxContext::getFunction(const char* name)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
bool GlxContext::makeCurrent() bool GlxContext::makeCurrent(bool current)
{ {
if (!m_context) if (!m_context)
return false; return false;
@ -222,6 +222,8 @@ bool GlxContext::makeCurrent()
bool result = false; bool result = false;
if (current)
{
if (m_pbuffer) if (m_pbuffer)
{ {
result = glXMakeContextCurrent(m_display, m_pbuffer, m_pbuffer, m_context); result = glXMakeContextCurrent(m_display, m_pbuffer, m_pbuffer, m_context);
@ -230,6 +232,11 @@ bool GlxContext::makeCurrent()
{ {
result = glXMakeCurrent(m_display, m_window, m_context); result = glXMakeCurrent(m_display, m_window, m_context);
} }
}
else
{
result = glXMakeCurrent(m_display, None, NULL);
}
#if defined(GLX_DEBUGGING) #if defined(GLX_DEBUGGING)
if (glxErrorOccurred) if (glxErrorOccurred)
@ -686,6 +693,15 @@ void GlxContext::createContext(GlxContext* shared)
// On an error, glXCreateContextAttribsARB will return 0 anyway // On an error, glXCreateContextAttribsARB will return 0 anyway
GlxErrorHandler handler(m_display); 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 // Create the context
m_context = glXCreateContextAttribsARB(m_display, *config, toShare, true, &attributes[0]); m_context = glXCreateContextAttribsARB(m_display, *config, toShare, true, &attributes[0]);
@ -732,6 +748,15 @@ void GlxContext::createContext(GlxContext* shared)
GlxErrorHandler handler(m_display); GlxErrorHandler handler(m_display);
#endif #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 // Create the context, using the target window's visual
m_context = glXCreateContext(m_display, visualInfo, toShare, true); 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 /// \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 /// \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 /// \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);
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 // Create the context
m_context = wglCreateContextAttribsARB(m_deviceContext, sharedContext, &attributes[0]); m_context = wglCreateContextAttribsARB(m_deviceContext, sharedContext, &attributes[0]);
} }
@ -657,6 +669,12 @@ void WglContext::createContext(WglContext* shared)
static Mutex mutex; static Mutex mutex;
Lock lock(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)) if (!wglShareLists(sharedContext, m_context))
err() << "Failed to share the OpenGL context: " << getErrorString(GetLastError()).toAnsiString() << std::endl; 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 /// \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 /// \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 /// \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 /// \brief Activate the context as the current target
/// for rendering /// for rendering
/// ///
/// \param current Whether to make the context current or no longer current
///
/// \return True on success, false if any error happened /// \return True on success, false if any error happened
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
virtual bool makeCurrent(); virtual bool makeCurrent(bool current);
private: private:

View File

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