Moved most variables in sfml-graphics, GlResource and GlContext out of namespace scope.
This commit is contained in:
parent
6a3e44bc4d
commit
b39be46db0
@ -29,6 +29,8 @@
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
#include <SFML/Window/Export.hpp>
|
#include <SFML/Window/Export.hpp>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
|
||||||
namespace sf
|
namespace sf
|
||||||
{
|
{
|
||||||
@ -48,23 +50,24 @@ protected:
|
|||||||
GlResource();
|
GlResource();
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
/// \brief Destructor
|
/// \brief Register an OpenGL object to be destroyed when its containing context is destroyed
|
||||||
///
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
~GlResource();
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
/// \brief Register a function to be called when a context is destroyed
|
|
||||||
///
|
///
|
||||||
/// This is used for internal purposes in order to properly
|
/// This is used for internal purposes in order to properly
|
||||||
/// clean up OpenGL resources that cannot be shared between
|
/// clean up OpenGL resources that cannot be shared between
|
||||||
/// contexts.
|
/// contexts.
|
||||||
///
|
///
|
||||||
/// \param callback Function to be called when a context is destroyed
|
/// \param object Object to be destroyed when its containing context is destroyed
|
||||||
/// \param arg Argument to pass when calling the function
|
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
static void registerContextDestroyCallback(ContextDestroyCallback callback, void* arg);
|
static void registerUnsharedGlObject(std::shared_ptr<void> object);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
/// \brief Unregister an OpenGL object from its containing context
|
||||||
|
///
|
||||||
|
/// \param object Object to be unregistered
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
static void unregisterUnsharedGlObject(std::shared_ptr<void> object);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
/// \brief RAII helper class to temporarily lock an available context for use
|
/// \brief RAII helper class to temporarily lock an available context for use
|
||||||
@ -97,6 +100,12 @@ protected:
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
TransientContextLock& operator=(const TransientContextLock&) = delete;
|
TransientContextLock& operator=(const TransientContextLock&) = delete;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
// Member data
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
std::shared_ptr<void> m_sharedContext; //!< Shared context used to link all contexts together for resource sharing
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace sf
|
} // namespace sf
|
||||||
|
@ -54,30 +54,35 @@ namespace
|
|||||||
namespace RenderTargetImpl
|
namespace RenderTargetImpl
|
||||||
{
|
{
|
||||||
// Mutex to protect ID generation and our context-RenderTarget-map
|
// Mutex to protect ID generation and our context-RenderTarget-map
|
||||||
std::recursive_mutex mutex;
|
std::recursive_mutex& getMutex()
|
||||||
|
{
|
||||||
|
static std::recursive_mutex mutex;
|
||||||
|
return mutex;
|
||||||
|
}
|
||||||
|
|
||||||
// Unique identifier, used for identifying RenderTargets when
|
// Unique identifier, used for identifying RenderTargets when
|
||||||
// tracking the currently active RenderTarget within a given context
|
// tracking the currently active RenderTarget within a given context
|
||||||
std::uint64_t getUniqueId()
|
std::uint64_t getUniqueId()
|
||||||
{
|
{
|
||||||
const std::lock_guard lock(mutex);
|
const std::lock_guard lock(getMutex());
|
||||||
|
|
||||||
static std::uint64_t id = 1; // start at 1, zero is "no RenderTarget"
|
static std::uint64_t id = 1; // start at 1, zero is "no RenderTarget"
|
||||||
|
|
||||||
return id++;
|
return id++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map to help us detect whether a different RenderTarget
|
// Map to help us detect whether a different RenderTarget
|
||||||
// has been activated within a single context
|
// has been activated within a single context
|
||||||
using ContextRenderTargetMap = std::unordered_map<std::uint64_t, std::uint64_t>;
|
using ContextRenderTargetMap = std::unordered_map<std::uint64_t, std::uint64_t>;
|
||||||
ContextRenderTargetMap contextRenderTargetMap;
|
ContextRenderTargetMap& getContextRenderTargetMap()
|
||||||
|
{
|
||||||
|
static ContextRenderTargetMap contextRenderTargetMap;
|
||||||
|
return contextRenderTargetMap;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if a RenderTarget with the given ID is active in the current context
|
// Check if a RenderTarget with the given ID is active in the current context
|
||||||
bool isActive(std::uint64_t id)
|
bool isActive(std::uint64_t id)
|
||||||
{
|
{
|
||||||
const auto it = contextRenderTargetMap.find(sf::Context::getActiveContextId());
|
const auto it = getContextRenderTargetMap().find(sf::Context::getActiveContextId());
|
||||||
|
return (it != getContextRenderTargetMap().end()) && (it->second == id);
|
||||||
return (it != contextRenderTargetMap.end()) && (it->second == id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert an sf::BlendMode::Factor constant to the corresponding OpenGL constant.
|
// Convert an sf::BlendMode::Factor constant to the corresponding OpenGL constant.
|
||||||
@ -393,12 +398,13 @@ bool RenderTarget::setActive(bool active)
|
|||||||
{
|
{
|
||||||
// Mark this RenderTarget as active or no longer active in the tracking map
|
// Mark this RenderTarget as active or no longer active in the tracking map
|
||||||
{
|
{
|
||||||
const std::lock_guard lock(RenderTargetImpl::mutex);
|
const std::lock_guard lock(RenderTargetImpl::getMutex());
|
||||||
|
|
||||||
const std::uint64_t contextId = Context::getActiveContextId();
|
const std::uint64_t contextId = Context::getActiveContextId();
|
||||||
|
|
||||||
using RenderTargetImpl::contextRenderTargetMap;
|
using RenderTargetImpl::getContextRenderTargetMap;
|
||||||
const auto it = contextRenderTargetMap.find(contextId);
|
auto& contextRenderTargetMap = getContextRenderTargetMap();
|
||||||
|
auto it = contextRenderTargetMap.find(contextId);
|
||||||
|
|
||||||
if (active)
|
if (active)
|
||||||
{
|
{
|
||||||
|
@ -36,111 +36,38 @@
|
|||||||
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <set>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <unordered_set>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
// Set to track all active FBO mappings
|
|
||||||
// This is used to free active FBOs while their owning
|
|
||||||
// RenderTextureImplFBO is still alive
|
|
||||||
std::unordered_set<std::unordered_map<std::uint64_t, unsigned int>*> frameBuffers;
|
|
||||||
|
|
||||||
// Set to track all stale FBOs
|
|
||||||
// This is used to free stale FBOs after their owning
|
|
||||||
// RenderTextureImplFBO has already been destroyed
|
|
||||||
// An FBO cannot be destroyed until it's containing context
|
|
||||||
// becomes active, so the destruction of the RenderTextureImplFBO
|
|
||||||
// has to be decoupled from the destruction of the FBOs themselves
|
|
||||||
std::set<std::pair<std::uint64_t, unsigned int>> staleFrameBuffers;
|
|
||||||
|
|
||||||
// Mutex to protect both active and stale frame buffer sets
|
|
||||||
std::recursive_mutex mutex;
|
|
||||||
|
|
||||||
// This function is called either when a RenderTextureImplFBO is
|
|
||||||
// destroyed or via contextDestroyCallback when context destruction
|
|
||||||
// might trigger deletion of its contained stale FBOs
|
|
||||||
void destroyStaleFBOs()
|
|
||||||
{
|
|
||||||
const std::uint64_t contextId = sf::Context::getActiveContextId();
|
|
||||||
|
|
||||||
for (auto it = staleFrameBuffers.begin(); it != staleFrameBuffers.end();)
|
|
||||||
{
|
|
||||||
if (it->first == contextId)
|
|
||||||
{
|
|
||||||
const auto frameBuffer = static_cast<GLuint>(it->second);
|
|
||||||
glCheck(GLEXT_glDeleteFramebuffers(1, &frameBuffer));
|
|
||||||
|
|
||||||
staleFrameBuffers.erase(it++);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Callback that is called every time a context is destroyed
|
|
||||||
void contextDestroyCallback(void* /*arg*/)
|
|
||||||
{
|
|
||||||
const std::lock_guard lock(mutex);
|
|
||||||
|
|
||||||
const std::uint64_t contextId = sf::Context::getActiveContextId();
|
|
||||||
|
|
||||||
// Destroy active frame buffer objects
|
|
||||||
for (auto* frameBuffer : frameBuffers)
|
|
||||||
{
|
|
||||||
for (auto it = frameBuffer->begin(); it != frameBuffer->end(); ++it)
|
|
||||||
{
|
|
||||||
if (it->first == contextId)
|
|
||||||
{
|
|
||||||
const GLuint frameBufferId = it->second;
|
|
||||||
glCheck(GLEXT_glDeleteFramebuffers(1, &frameBufferId));
|
|
||||||
|
|
||||||
// Erase the entry from the RenderTextureImplFBO's map
|
|
||||||
frameBuffer->erase(it);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Destroy stale frame buffer objects
|
|
||||||
destroyStaleFBOs();
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
|
|
||||||
namespace sf::priv
|
namespace sf::priv
|
||||||
{
|
{
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
RenderTextureImplFBO::RenderTextureImplFBO()
|
struct RenderTextureImplFBO::FrameBufferObject
|
||||||
{
|
{
|
||||||
const std::lock_guard lock(mutex);
|
FrameBufferObject()
|
||||||
|
{
|
||||||
// Register the context destruction callback
|
// Create the framebuffer object
|
||||||
registerContextDestroyCallback(contextDestroyCallback, nullptr);
|
glCheck(GLEXT_glGenFramebuffers(1, &object));
|
||||||
|
|
||||||
// Insert the new frame buffer mapping into the set of all active mappings
|
|
||||||
frameBuffers.insert(&m_frameBuffers);
|
|
||||||
frameBuffers.insert(&m_multisampleFrameBuffers);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~FrameBufferObject()
|
||||||
|
{
|
||||||
|
if (object)
|
||||||
|
glCheck(GLEXT_glDeleteFramebuffers(1, &object));
|
||||||
|
}
|
||||||
|
|
||||||
|
GLuint object{};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
RenderTextureImplFBO::RenderTextureImplFBO() = default;
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
RenderTextureImplFBO::~RenderTextureImplFBO()
|
RenderTextureImplFBO::~RenderTextureImplFBO()
|
||||||
{
|
{
|
||||||
const TransientContextLock contextLock;
|
const TransientContextLock contextLock;
|
||||||
|
|
||||||
const std::lock_guard lock(mutex);
|
|
||||||
|
|
||||||
// Remove the frame buffer mapping from the set of all active mappings
|
|
||||||
frameBuffers.erase(&m_frameBuffers);
|
|
||||||
frameBuffers.erase(&m_multisampleFrameBuffers);
|
|
||||||
|
|
||||||
// Destroy the color buffer
|
// Destroy the color buffer
|
||||||
if (m_colorBuffer)
|
if (m_colorBuffer)
|
||||||
{
|
{
|
||||||
@ -155,15 +82,22 @@ RenderTextureImplFBO::~RenderTextureImplFBO()
|
|||||||
glCheck(GLEXT_glDeleteRenderbuffers(1, &depthStencilBuffer));
|
glCheck(GLEXT_glDeleteRenderbuffers(1, &depthStencilBuffer));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move all frame buffer objects to stale set
|
// Unregister FBOs with the contexts if they haven't already been destroyed
|
||||||
for (auto& [contextId, frameBufferId] : m_frameBuffers)
|
for (auto& entry : m_frameBuffers)
|
||||||
staleFrameBuffers.emplace(contextId, frameBufferId);
|
{
|
||||||
|
auto frameBuffer = entry.second.lock();
|
||||||
|
|
||||||
for (auto& [contextId, multisampleFrameBufferId] : m_multisampleFrameBuffers)
|
if (frameBuffer)
|
||||||
staleFrameBuffers.emplace(contextId, multisampleFrameBufferId);
|
unregisterUnsharedGlObject(std::move(frameBuffer));
|
||||||
|
}
|
||||||
|
|
||||||
// Clean up FBOs
|
for (auto& entry : m_multisampleFrameBuffers)
|
||||||
destroyStaleFBOs();
|
{
|
||||||
|
auto frameBuffer = entry.second.lock();
|
||||||
|
|
||||||
|
if (frameBuffer)
|
||||||
|
unregisterUnsharedGlObject(std::move(frameBuffer));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -422,15 +356,14 @@ bool RenderTextureImplFBO::create(const Vector2u& size, unsigned int textureId,
|
|||||||
bool RenderTextureImplFBO::createFrameBuffer()
|
bool RenderTextureImplFBO::createFrameBuffer()
|
||||||
{
|
{
|
||||||
// Create the framebuffer object
|
// Create the framebuffer object
|
||||||
GLuint frameBuffer = 0;
|
auto frameBuffer = std::make_shared<FrameBufferObject>();
|
||||||
glCheck(GLEXT_glGenFramebuffers(1, &frameBuffer));
|
|
||||||
|
|
||||||
if (!frameBuffer)
|
if (!frameBuffer->object)
|
||||||
{
|
{
|
||||||
err() << "Impossible to create render texture (failed to create the frame buffer object)" << std::endl;
|
err() << "Impossible to create render texture (failed to create the frame buffer object)" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, frameBuffer));
|
glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, frameBuffer->object));
|
||||||
|
|
||||||
// Link the depth/stencil renderbuffer to the frame buffer
|
// Link the depth/stencil renderbuffer to the frame buffer
|
||||||
if (!m_multisample && m_depthStencilBuffer)
|
if (!m_multisample && m_depthStencilBuffer)
|
||||||
@ -462,33 +395,30 @@ bool RenderTextureImplFBO::createFrameBuffer()
|
|||||||
if (status != GLEXT_GL_FRAMEBUFFER_COMPLETE)
|
if (status != GLEXT_GL_FRAMEBUFFER_COMPLETE)
|
||||||
{
|
{
|
||||||
glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, 0));
|
glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, 0));
|
||||||
glCheck(GLEXT_glDeleteFramebuffers(1, &frameBuffer));
|
|
||||||
err() << "Impossible to create render texture (failed to link the target texture to the frame buffer)" << std::endl;
|
err() << "Impossible to create render texture (failed to link the target texture to the frame buffer)" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
|
||||||
const std::lock_guard lock(mutex);
|
|
||||||
|
|
||||||
// Insert the FBO into our map
|
// Insert the FBO into our map
|
||||||
m_frameBuffers.emplace(Context::getActiveContextId(), frameBuffer);
|
m_frameBuffers.emplace(Context::getActiveContextId(), frameBuffer);
|
||||||
}
|
|
||||||
|
// Register the object with the current context so it is automatically destroyed
|
||||||
|
registerUnsharedGlObject(std::move(frameBuffer));
|
||||||
|
|
||||||
#ifndef SFML_OPENGL_ES
|
#ifndef SFML_OPENGL_ES
|
||||||
|
|
||||||
if (m_multisample)
|
if (m_multisample)
|
||||||
{
|
{
|
||||||
// Create the multisample framebuffer object
|
// Create the multisample framebuffer object
|
||||||
GLuint multisampleFrameBuffer = 0;
|
auto multisampleFrameBuffer = std::make_shared<FrameBufferObject>();
|
||||||
glCheck(GLEXT_glGenFramebuffers(1, &multisampleFrameBuffer));
|
|
||||||
|
|
||||||
if (!multisampleFrameBuffer)
|
if (!multisampleFrameBuffer->object)
|
||||||
{
|
{
|
||||||
err() << "Impossible to create render texture (failed to create the multisample frame buffer object)"
|
err() << "Impossible to create render texture (failed to create the multisample frame buffer object)"
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, multisampleFrameBuffer));
|
glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, multisampleFrameBuffer->object));
|
||||||
|
|
||||||
// Link the multisample color buffer to the frame buffer
|
// Link the multisample color buffer to the frame buffer
|
||||||
glCheck(GLEXT_glBindRenderbuffer(GLEXT_GL_RENDERBUFFER, m_colorBuffer));
|
glCheck(GLEXT_glBindRenderbuffer(GLEXT_GL_RENDERBUFFER, m_colorBuffer));
|
||||||
@ -517,19 +447,17 @@ bool RenderTextureImplFBO::createFrameBuffer()
|
|||||||
if (status != GLEXT_GL_FRAMEBUFFER_COMPLETE)
|
if (status != GLEXT_GL_FRAMEBUFFER_COMPLETE)
|
||||||
{
|
{
|
||||||
glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, 0));
|
glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, 0));
|
||||||
glCheck(GLEXT_glDeleteFramebuffers(1, &multisampleFrameBuffer));
|
|
||||||
err() << "Impossible to create render texture (failed to link the render buffers to the multisample frame "
|
err() << "Impossible to create render texture (failed to link the render buffers to the multisample frame "
|
||||||
"buffer)"
|
"buffer)"
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
|
||||||
const std::lock_guard lock(mutex);
|
|
||||||
|
|
||||||
// Insert the FBO into our map
|
// Insert the FBO into our map
|
||||||
m_multisampleFrameBuffers.emplace(Context::getActiveContextId(), multisampleFrameBuffer);
|
m_multisampleFrameBuffers.emplace(Context::getActiveContextId(), multisampleFrameBuffer);
|
||||||
}
|
|
||||||
|
// Register the object with the current context so it is automatically destroyed
|
||||||
|
registerUnsharedGlObject(std::move(multisampleFrameBuffer));
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@ -575,29 +503,33 @@ bool RenderTextureImplFBO::activate(bool active)
|
|||||||
// Lookup the FBO corresponding to the currently active context
|
// Lookup the FBO corresponding to the currently active context
|
||||||
// If none is found, there is no FBO corresponding to the
|
// If none is found, there is no FBO corresponding to the
|
||||||
// currently active context so we will have to create a new FBO
|
// currently active context so we will have to create a new FBO
|
||||||
{
|
|
||||||
const std::lock_guard lock(mutex);
|
|
||||||
|
|
||||||
std::unordered_map<std::uint64_t, unsigned int>::iterator it;
|
|
||||||
|
|
||||||
if (m_multisample)
|
if (m_multisample)
|
||||||
{
|
{
|
||||||
it = m_multisampleFrameBuffers.find(contextId);
|
const auto it = m_multisampleFrameBuffers.find(contextId);
|
||||||
|
|
||||||
if (it != m_multisampleFrameBuffers.end())
|
if (it != m_multisampleFrameBuffers.end())
|
||||||
{
|
{
|
||||||
glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, it->second));
|
auto frameBuffer = it->second.lock();
|
||||||
|
|
||||||
|
if (frameBuffer)
|
||||||
|
{
|
||||||
|
glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, frameBuffer->object));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
it = m_frameBuffers.find(contextId);
|
const auto it = m_frameBuffers.find(contextId);
|
||||||
|
|
||||||
if (it != m_frameBuffers.end())
|
if (it != m_frameBuffers.end())
|
||||||
{
|
{
|
||||||
glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, it->second));
|
auto frameBuffer = it->second.lock();
|
||||||
|
|
||||||
|
if (frameBuffer)
|
||||||
|
{
|
||||||
|
glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, frameBuffer->object));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -630,15 +562,18 @@ void RenderTextureImplFBO::updateTexture(unsigned int)
|
|||||||
{
|
{
|
||||||
const std::uint64_t contextId = Context::getActiveContextId();
|
const std::uint64_t contextId = Context::getActiveContextId();
|
||||||
|
|
||||||
const std::lock_guard lock(mutex);
|
|
||||||
|
|
||||||
const auto frameBufferIt = m_frameBuffers.find(contextId);
|
const auto frameBufferIt = m_frameBuffers.find(contextId);
|
||||||
const auto multisampleIt = m_multisampleFrameBuffers.find(contextId);
|
const auto multisampleIt = m_multisampleFrameBuffers.find(contextId);
|
||||||
|
|
||||||
if ((frameBufferIt != m_frameBuffers.end()) && (multisampleIt != m_multisampleFrameBuffers.end()))
|
if ((frameBufferIt != m_frameBuffers.end()) && (multisampleIt != m_multisampleFrameBuffers.end()))
|
||||||
|
{
|
||||||
|
auto frameBuffer = frameBufferIt->second.lock();
|
||||||
|
auto multiSampleFrameBuffer = multisampleIt->second.lock();
|
||||||
|
|
||||||
|
if (frameBuffer && multiSampleFrameBuffer)
|
||||||
{
|
{
|
||||||
// Set up the blit target (draw framebuffer) and blit (from the read framebuffer, our multisample FBO)
|
// Set up the blit target (draw framebuffer) and blit (from the read framebuffer, our multisample FBO)
|
||||||
glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_DRAW_FRAMEBUFFER, frameBufferIt->second));
|
glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_DRAW_FRAMEBUFFER, frameBuffer->object));
|
||||||
glCheck(GLEXT_glBlitFramebuffer(0,
|
glCheck(GLEXT_glBlitFramebuffer(0,
|
||||||
0,
|
0,
|
||||||
static_cast<GLint>(m_size.x),
|
static_cast<GLint>(m_size.x),
|
||||||
@ -649,7 +584,8 @@ void RenderTextureImplFBO::updateTexture(unsigned int)
|
|||||||
static_cast<GLint>(m_size.y),
|
static_cast<GLint>(m_size.y),
|
||||||
GL_COLOR_BUFFER_BIT,
|
GL_COLOR_BUFFER_BIT,
|
||||||
GL_NEAREST));
|
GL_NEAREST));
|
||||||
glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_DRAW_FRAMEBUFFER, multisampleIt->second));
|
glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_DRAW_FRAMEBUFFER, multiSampleFrameBuffer->object));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,8 +137,12 @@ private:
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
// Member data
|
// Member data
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
std::unordered_map<std::uint64_t, unsigned int> m_frameBuffers; //!< OpenGL frame buffer objects per context
|
struct FrameBufferObject;
|
||||||
std::unordered_map<std::uint64_t, unsigned int> m_multisampleFrameBuffers; //!< Optional per-context OpenGL frame buffer objects with multisample attachments
|
|
||||||
|
using FrameBufferObjectMap = std::unordered_map<std::uint64_t, std::weak_ptr<FrameBufferObject>>;
|
||||||
|
|
||||||
|
FrameBufferObjectMap m_frameBuffers; //!< OpenGL frame buffer objects per context
|
||||||
|
FrameBufferObjectMap m_multisampleFrameBuffers; //!< Optional per-context OpenGL frame buffer objects with multisample attachments
|
||||||
unsigned int m_depthStencilBuffer{}; //!< Optional depth/stencil buffer attached to the frame buffer
|
unsigned int m_depthStencilBuffer{}; //!< Optional depth/stencil buffer attached to the frame buffer
|
||||||
unsigned int m_colorBuffer{}; //!< Optional multisample color buffer attached to the frame buffer
|
unsigned int m_colorBuffer{}; //!< Optional multisample color buffer attached to the frame buffer
|
||||||
Vector2u m_size; //!< Width and height of the attachments
|
Vector2u m_size; //!< Width and height of the attachments
|
||||||
|
@ -40,7 +40,6 @@
|
|||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <mutex>
|
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -61,20 +60,17 @@
|
|||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
std::recursive_mutex isAvailableMutex;
|
|
||||||
|
|
||||||
GLint checkMaxTextureUnits()
|
|
||||||
{
|
|
||||||
GLint maxUnits = 0;
|
|
||||||
glCheck(glGetIntegerv(GLEXT_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxUnits));
|
|
||||||
|
|
||||||
return maxUnits;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve the maximum number of texture units available
|
// Retrieve the maximum number of texture units available
|
||||||
std::size_t getMaxTextureUnits()
|
std::size_t getMaxTextureUnits()
|
||||||
{
|
{
|
||||||
static const GLint maxUnits = checkMaxTextureUnits();
|
static const GLint maxUnits = []()
|
||||||
|
{
|
||||||
|
GLint value = 0;
|
||||||
|
glCheck(glGetIntegerv(GLEXT_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &value));
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}();
|
||||||
|
|
||||||
return static_cast<std::size_t>(maxUnits);
|
return static_cast<std::size_t>(maxUnits);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -747,23 +743,16 @@ void Shader::bind(const Shader* shader)
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
bool Shader::isAvailable()
|
bool Shader::isAvailable()
|
||||||
{
|
{
|
||||||
const std::lock_guard lock(isAvailableMutex);
|
static const bool available = []()
|
||||||
|
|
||||||
static bool checked = false;
|
|
||||||
static bool available = false;
|
|
||||||
|
|
||||||
if (!checked)
|
|
||||||
{
|
{
|
||||||
checked = true;
|
|
||||||
|
|
||||||
const TransientContextLock contextLock;
|
const TransientContextLock contextLock;
|
||||||
|
|
||||||
// Make sure that extensions are initialized
|
// Make sure that extensions are initialized
|
||||||
sf::priv::ensureExtensionsInit();
|
sf::priv::ensureExtensionsInit();
|
||||||
|
|
||||||
available = GLEXT_multitexture && GLEXT_shading_language_100 && GLEXT_shader_objects && GLEXT_vertex_shader &&
|
return GLEXT_multitexture && GLEXT_shading_language_100 && GLEXT_shader_objects && GLEXT_vertex_shader &&
|
||||||
GLEXT_fragment_shader;
|
GLEXT_fragment_shader;
|
||||||
}
|
}();
|
||||||
|
|
||||||
return available;
|
return available;
|
||||||
}
|
}
|
||||||
@ -772,22 +761,15 @@ bool Shader::isAvailable()
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
bool Shader::isGeometryAvailable()
|
bool Shader::isGeometryAvailable()
|
||||||
{
|
{
|
||||||
const std::lock_guard lock(isAvailableMutex);
|
static const bool available = []()
|
||||||
|
|
||||||
static bool checked = false;
|
|
||||||
static bool available = false;
|
|
||||||
|
|
||||||
if (!checked)
|
|
||||||
{
|
{
|
||||||
checked = true;
|
|
||||||
|
|
||||||
const TransientContextLock contextLock;
|
const TransientContextLock contextLock;
|
||||||
|
|
||||||
// Make sure that extensions are initialized
|
// Make sure that extensions are initialized
|
||||||
sf::priv::ensureExtensionsInit();
|
sf::priv::ensureExtensionsInit();
|
||||||
|
|
||||||
available = isAvailable() && (GLEXT_geometry_shader4 || GLEXT_GL_VERSION_3_2);
|
return isAvailable() && (GLEXT_geometry_shader4 || GLEXT_GL_VERSION_3_2);
|
||||||
}
|
}();
|
||||||
|
|
||||||
return available;
|
return available;
|
||||||
}
|
}
|
||||||
|
@ -71,6 +71,7 @@ Texture::Texture() : m_cacheId(TextureImpl::getUniqueId())
|
|||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
Texture::Texture(const Texture& copy) :
|
Texture::Texture(const Texture& copy) :
|
||||||
|
GlResource(copy),
|
||||||
m_isSmooth(copy.m_isSmooth),
|
m_isSmooth(copy.m_isSmooth),
|
||||||
m_sRgb(copy.m_sRgb),
|
m_sRgb(copy.m_sRgb),
|
||||||
m_isRepeated(copy.m_isRepeated),
|
m_isRepeated(copy.m_isRepeated),
|
||||||
|
@ -84,7 +84,10 @@ VertexBuffer::VertexBuffer(PrimitiveType type, Usage usage) : m_primitiveType(ty
|
|||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
VertexBuffer::VertexBuffer(const VertexBuffer& copy) : m_primitiveType(copy.m_primitiveType), m_usage(copy.m_usage)
|
VertexBuffer::VertexBuffer(const VertexBuffer& copy) :
|
||||||
|
GlResource(copy),
|
||||||
|
m_primitiveType(copy.m_primitiveType),
|
||||||
|
m_usage(copy.m_usage)
|
||||||
{
|
{
|
||||||
if (copy.m_buffer && copy.m_size)
|
if (copy.m_buffer && copy.m_size)
|
||||||
{
|
{
|
||||||
|
@ -167,17 +167,6 @@ namespace
|
|||||||
// A nested named namespace is used here to allow unity builds of SFML.
|
// A nested named namespace is used here to allow unity builds of SFML.
|
||||||
namespace GlContextImpl
|
namespace GlContextImpl
|
||||||
{
|
{
|
||||||
// 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 and for managing
|
|
||||||
// the resource count
|
|
||||||
std::recursive_mutex mutex;
|
|
||||||
|
|
||||||
// OpenGL resources counter
|
|
||||||
unsigned int resourceCount = 0;
|
|
||||||
|
|
||||||
// This structure contains all the state necessary to
|
// This structure contains all the state necessary to
|
||||||
// track current context information for each thread
|
// track current context information for each thread
|
||||||
struct CurrentContext
|
struct CurrentContext
|
||||||
@ -185,101 +174,87 @@ struct CurrentContext
|
|||||||
std::uint64_t id{};
|
std::uint64_t id{};
|
||||||
sf::priv::GlContext* ptr{};
|
sf::priv::GlContext* ptr{};
|
||||||
unsigned int transientCount{};
|
unsigned int transientCount{};
|
||||||
};
|
|
||||||
|
|
||||||
// This per-thread variable holds the current context information for each thread
|
// This per-thread variable holds the current context information for each thread
|
||||||
|
static CurrentContext& get()
|
||||||
|
{
|
||||||
thread_local CurrentContext currentContext;
|
thread_local CurrentContext currentContext;
|
||||||
|
return currentContext;
|
||||||
|
}
|
||||||
|
|
||||||
// The hidden, inactive context that will be shared with all other contexts
|
private:
|
||||||
std::unique_ptr<ContextType> sharedContext;
|
// Private constructor to prevent CurrentContext from being constructed outside of get()
|
||||||
|
CurrentContext() = default;
|
||||||
|
};
|
||||||
|
} // namespace GlContextImpl
|
||||||
|
} // namespace
|
||||||
|
|
||||||
// Unique identifier, used for identifying contexts when managing unshareable OpenGL resources
|
|
||||||
std::atomic<std::uint64_t> nextContextId(1); // start at 1, zero is "no context"
|
|
||||||
|
|
||||||
// Set containing callback functions to be called whenever a
|
|
||||||
// context is going to be destroyed
|
|
||||||
// Unshareable OpenGL resources rely on this to clean up properly
|
|
||||||
// whenever a context containing them is destroyed
|
|
||||||
using ContextDestroyCallbacks = std::unordered_map<sf::ContextDestroyCallback, void*>;
|
|
||||||
ContextDestroyCallbacks contextDestroyCallbacks;
|
|
||||||
|
|
||||||
|
namespace sf::priv
|
||||||
|
{
|
||||||
// This structure contains all the state necessary to
|
// This structure contains all the state necessary to
|
||||||
// track TransientContext usage
|
// track SharedContext usage
|
||||||
struct TransientContext
|
struct GlContext::SharedContext
|
||||||
{
|
{
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
/// \brief Constructor
|
/// \brief Constructor
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
TransientContext()
|
SharedContext()
|
||||||
{
|
{
|
||||||
// TransientContext should never be created if there is
|
const std::lock_guard lock(mutex);
|
||||||
// already a context active on the current thread
|
|
||||||
assert(!currentContext.id);
|
|
||||||
|
|
||||||
std::unique_lock lock(mutex);
|
context.emplace(nullptr);
|
||||||
|
context->initialize(sf::ContextSettings());
|
||||||
|
|
||||||
if (resourceCount == 0)
|
loadExtensions();
|
||||||
{
|
|
||||||
// No GlResources, no shared context yet
|
|
||||||
assert(!sharedContext);
|
|
||||||
|
|
||||||
// Create a Context object for temporary use
|
context->setActive(false);
|
||||||
context.emplace();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// GlResources exist, currentContextId not yet set
|
|
||||||
assert(sharedContext);
|
|
||||||
|
|
||||||
// Lock the shared context for temporary use
|
|
||||||
sharedContextLock = std::move(lock);
|
|
||||||
sharedContext->setActive(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
/// \brief Destructor
|
/// \brief Get weak_ptr
|
||||||
|
///
|
||||||
|
/// \return weak_ptr to the SharedContext
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
~TransientContext()
|
static std::weak_ptr<SharedContext>& getWeakPtr()
|
||||||
{
|
{
|
||||||
if (sharedContextLock)
|
static std::weak_ptr<SharedContext> weakSharedContext;
|
||||||
sharedContext->setActive(false);
|
return weakSharedContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
/// \brief Deleted copy constructor
|
/// \brief Get shared_ptr to the shared context
|
||||||
|
///
|
||||||
|
/// Create new object if one doesn't already exist.
|
||||||
|
///
|
||||||
|
/// \return shared_ptr to the shared context
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
TransientContext(const TransientContext&) = delete;
|
static std::shared_ptr<SharedContext> get()
|
||||||
|
{
|
||||||
|
auto& weakSharedContext = getWeakPtr();
|
||||||
|
auto sharedContext = weakSharedContext.lock();
|
||||||
|
|
||||||
|
if (!sharedContext)
|
||||||
|
{
|
||||||
|
sharedContext = std::make_shared<GlContext::SharedContext>();
|
||||||
|
weakSharedContext = sharedContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sharedContext;
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
/// \brief Deleted copy assignment
|
/// \brief Load our extensions vector with the supported extensions
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
TransientContext& operator=(const TransientContext&) = delete;
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////
|
|
||||||
// Member data
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
std::optional<sf::Context> context;
|
|
||||||
std::unique_lock<std::recursive_mutex> sharedContextLock;
|
|
||||||
};
|
|
||||||
|
|
||||||
// This per-thread variable tracks if and how a transient
|
|
||||||
// context is currently being used on the current thread
|
|
||||||
thread_local std::optional<TransientContext> transientContext;
|
|
||||||
|
|
||||||
// Supported OpenGL extensions
|
|
||||||
std::vector<std::string> extensions;
|
|
||||||
|
|
||||||
// Load our extensions vector with the supported extensions
|
|
||||||
void loadExtensions()
|
void loadExtensions()
|
||||||
{
|
{
|
||||||
auto glGetErrorFunc = reinterpret_cast<glGetErrorFuncType>(sf::priv::GlContext::getFunction("glGetError"));
|
auto glGetErrorFunc = reinterpret_cast<glGetErrorFuncType>(getFunction("glGetError"));
|
||||||
auto glGetIntegervFunc = reinterpret_cast<glGetIntegervFuncType>(sf::priv::GlContext::getFunction("glGetIntegerv"));
|
auto glGetIntegervFunc = reinterpret_cast<glGetIntegervFuncType>(getFunction("glGetIntegerv"));
|
||||||
auto glGetStringFunc = reinterpret_cast<glGetStringFuncType>(sf::priv::GlContext::getFunction("glGetString"));
|
auto glGetStringFunc = reinterpret_cast<glGetStringFuncType>(getFunction("glGetString"));
|
||||||
|
|
||||||
if (!glGetErrorFunc || !glGetIntegervFunc || !glGetStringFunc)
|
if (!glGetErrorFunc || !glGetIntegervFunc || !glGetStringFunc)
|
||||||
return;
|
return;
|
||||||
@ -288,7 +263,7 @@ void loadExtensions()
|
|||||||
int majorVersion = 0;
|
int majorVersion = 0;
|
||||||
glGetIntegervFunc(GL_MAJOR_VERSION, &majorVersion);
|
glGetIntegervFunc(GL_MAJOR_VERSION, &majorVersion);
|
||||||
|
|
||||||
auto glGetStringiFunc = reinterpret_cast<glGetStringiFuncType>(sf::priv::GlContext::getFunction("glGetStringi"));
|
auto glGetStringiFunc = reinterpret_cast<glGetStringiFuncType>(getFunction("glGetStringi"));
|
||||||
|
|
||||||
if (glGetErrorFunc() == GL_INVALID_ENUM || !majorVersion || !glGetStringiFunc)
|
if (glGetErrorFunc() == GL_INVALID_ENUM || !majorVersion || !glGetStringiFunc)
|
||||||
{
|
{
|
||||||
@ -331,105 +306,211 @@ void loadExtensions()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper to parse OpenGL version strings
|
// AMD drivers have issues with internal synchronization
|
||||||
bool parseVersionString(const char* version, const char* prefix, unsigned int& major, unsigned int& minor)
|
// We need to make sure that no operating system context
|
||||||
{
|
// or pixel format operations are performed simultaneously
|
||||||
const std::size_t prefixLength = std::strlen(prefix);
|
// This mutex is also used to protect the shared context
|
||||||
|
// from being locked on multiple threads
|
||||||
|
std::recursive_mutex mutex;
|
||||||
|
|
||||||
if ((std::strlen(version) >= (prefixLength + 3)) && (std::strncmp(version, prefix, prefixLength) == 0) &&
|
// Supported OpenGL extensions
|
||||||
std::isdigit(version[prefixLength]) && (version[prefixLength + 1] == '.') &&
|
std::vector<std::string> extensions;
|
||||||
std::isdigit(version[prefixLength + 2]))
|
|
||||||
{
|
|
||||||
major = static_cast<unsigned int>(version[prefixLength] - '0');
|
|
||||||
minor = static_cast<unsigned int>(version[prefixLength + 2] - '0');
|
|
||||||
|
|
||||||
return true;
|
// The hidden, inactive context that will be shared with all other contexts
|
||||||
}
|
std::optional<ContextType> context;
|
||||||
|
};
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} // namespace GlContextImpl
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
|
|
||||||
namespace sf::priv
|
// This structure contains all the state necessary to
|
||||||
|
// track TransientContext usage
|
||||||
|
struct GlContext::TransientContext
|
||||||
{
|
{
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
void GlContext::initResource()
|
/// \brief Constructor
|
||||||
{
|
///
|
||||||
using GlContextImpl::loadExtensions;
|
|
||||||
using GlContextImpl::mutex;
|
|
||||||
using GlContextImpl::resourceCount;
|
|
||||||
using GlContextImpl::sharedContext;
|
|
||||||
|
|
||||||
// Protect from concurrent access
|
|
||||||
const std::lock_guard lock(mutex);
|
|
||||||
|
|
||||||
// If this is the very first resource, trigger the global context initialization
|
|
||||||
if (resourceCount == 0)
|
|
||||||
{
|
|
||||||
if (sharedContext)
|
|
||||||
{
|
|
||||||
// Increment the resources counter
|
|
||||||
++resourceCount;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the shared context
|
|
||||||
sharedContext = std::make_unique<ContextType>(nullptr);
|
|
||||||
sharedContext->initialize(ContextSettings());
|
|
||||||
|
|
||||||
// Load our extensions vector
|
|
||||||
loadExtensions();
|
|
||||||
|
|
||||||
// Deactivate the shared context so that others can activate it when necessary
|
|
||||||
sharedContext->setActive(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Increment the resources counter
|
|
||||||
++resourceCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
void GlContext::cleanupResource()
|
TransientContext()
|
||||||
{
|
{
|
||||||
using GlContextImpl::mutex;
|
// TransientContext should never be created if there is
|
||||||
using GlContextImpl::resourceCount;
|
// already a context active on the current thread
|
||||||
using GlContextImpl::sharedContext;
|
assert(!GlContextImpl::CurrentContext::get().id);
|
||||||
|
|
||||||
// Protect from concurrent access
|
// Lock ourselves so we don't create a new object if one doesn't already exist
|
||||||
const std::lock_guard lock(mutex);
|
sharedContext = SharedContext::getWeakPtr().lock();
|
||||||
|
|
||||||
// Decrement the resources counter
|
|
||||||
--resourceCount;
|
|
||||||
|
|
||||||
// If there's no more resource alive, we can trigger the global context cleanup
|
|
||||||
if (resourceCount == 0)
|
|
||||||
{
|
|
||||||
if (!sharedContext)
|
if (!sharedContext)
|
||||||
return;
|
{
|
||||||
|
// Create a Context object for temporary use
|
||||||
// Destroy the shared context
|
context.emplace();
|
||||||
sharedContext.reset();
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// GlResources exist, currentContextId not yet set
|
||||||
|
assert(sharedContext);
|
||||||
|
|
||||||
|
// Lock the shared context for temporary use
|
||||||
|
sharedContextLock = std::unique_lock(sharedContext->mutex);
|
||||||
|
sharedContext->context->setActive(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
/// \brief Destructor
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
~TransientContext()
|
||||||
|
{
|
||||||
|
if (sharedContextLock)
|
||||||
|
sharedContext->context->setActive(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
/// \brief Deleted copy constructor
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
TransientContext(const TransientContext&) = delete;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
/// \brief Deleted copy assignment
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
TransientContext& operator=(const TransientContext&) = delete;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
/// \brief Get the thread local TransientContext
|
||||||
|
///
|
||||||
|
/// This per-thread variable tracks if and how a transient
|
||||||
|
/// context is currently being used on the current thread
|
||||||
|
///
|
||||||
|
/// \return The thread local TransientContext
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
static std::optional<TransientContext>& get()
|
||||||
|
{
|
||||||
|
thread_local std::optional<TransientContext> transientContext;
|
||||||
|
return transientContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////
|
||||||
|
// Member data
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
std::optional<sf::Context> context;
|
||||||
|
std::unique_lock<std::recursive_mutex> sharedContextLock;
|
||||||
|
std::shared_ptr<SharedContext> sharedContext;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// This structure contains all the implementation data we
|
||||||
|
// don't want to expose through the visible interface
|
||||||
|
struct GlContext::Impl
|
||||||
|
{
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
/// \brief Constructor
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
Impl() :
|
||||||
|
m_id(
|
||||||
|
[]()
|
||||||
|
{
|
||||||
|
static std::atomic<std::uint64_t> id(1); // start at 1, zero is "no context"
|
||||||
|
return id.fetch_add(1);
|
||||||
|
}())
|
||||||
|
{
|
||||||
|
auto& weakUnsharedGlObjects = getWeakUnsharedGlObjects();
|
||||||
|
unsharedGlObjects = weakUnsharedGlObjects.lock();
|
||||||
|
|
||||||
|
if (!unsharedGlObjects)
|
||||||
|
{
|
||||||
|
unsharedGlObjects = std::make_shared<UnsharedGlObjects>();
|
||||||
|
|
||||||
|
weakUnsharedGlObjects = unsharedGlObjects;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Structure to track which unshared object belongs to which context
|
||||||
|
struct UnsharedGlObject
|
||||||
|
{
|
||||||
|
std::uint64_t contextId;
|
||||||
|
std::shared_ptr<void> object;
|
||||||
|
};
|
||||||
|
|
||||||
|
using UnsharedGlObjects = std::vector<UnsharedGlObject>;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
/// \brief Get weak_ptr to unshared objects
|
||||||
|
///
|
||||||
|
/// \return weak_ptr to unshared objects
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
static std::weak_ptr<UnsharedGlObjects>& getWeakUnsharedGlObjects()
|
||||||
|
{
|
||||||
|
static std::weak_ptr<UnsharedGlObjects> weakUnsharedGlObjects;
|
||||||
|
|
||||||
|
return weakUnsharedGlObjects;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
/// \brief Get mutex protecting unshared objects
|
||||||
|
///
|
||||||
|
/// \return Mutex protecting unshared objects
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
static std::mutex& getUnsharedGlObjectsMutex()
|
||||||
|
{
|
||||||
|
static std::mutex mutex;
|
||||||
|
return mutex;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////
|
||||||
|
// Member data
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
std::shared_ptr<UnsharedGlObjects> unsharedGlObjects; //!< The current object's handle to unshared objects
|
||||||
|
const std::uint64_t m_id; //!< Unique identifier, used for identifying contexts when managing unshareable OpenGL resources
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
std::shared_ptr<void> GlContext::getSharedContext()
|
||||||
|
{
|
||||||
|
return GlContext::SharedContext::get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
void GlContext::registerContextDestroyCallback(ContextDestroyCallback callback, void* arg)
|
void GlContext::registerUnsharedGlObject(std::shared_ptr<void> object)
|
||||||
{
|
{
|
||||||
GlContextImpl::contextDestroyCallbacks.emplace(callback, arg);
|
if (const std::lock_guard lock(Impl::getUnsharedGlObjectsMutex());
|
||||||
|
auto unsharedGlObjects = Impl::getWeakUnsharedGlObjects().lock())
|
||||||
|
unsharedGlObjects->emplace_back(Impl::UnsharedGlObject{GlContextImpl::CurrentContext::get().id, std::move(object)});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
void GlContext::unregisterUnsharedGlObject(std::shared_ptr<void> object)
|
||||||
|
{
|
||||||
|
if (const std::lock_guard lock(Impl::getUnsharedGlObjectsMutex());
|
||||||
|
auto unsharedGlObjects = Impl::getWeakUnsharedGlObjects().lock())
|
||||||
|
{
|
||||||
|
// Find the object in unshared objects and remove it if its associated context is currently active
|
||||||
|
// This will trigger the destructor of the object since shared_ptr
|
||||||
|
// in unshared objects should be the only one existing
|
||||||
|
auto iter = std::find_if(unsharedGlObjects->begin(),
|
||||||
|
unsharedGlObjects->end(),
|
||||||
|
[&](const Impl::UnsharedGlObject& obj) {
|
||||||
|
return (obj.object == object) &&
|
||||||
|
(obj.contextId == GlContextImpl::CurrentContext::get().id);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (iter != unsharedGlObjects->end())
|
||||||
|
unsharedGlObjects->erase(iter);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
void GlContext::acquireTransientContext()
|
void GlContext::acquireTransientContext()
|
||||||
{
|
{
|
||||||
using GlContextImpl::currentContext;
|
auto& currentContext = GlContextImpl::CurrentContext::get();
|
||||||
using GlContextImpl::TransientContext;
|
|
||||||
using GlContextImpl::transientContext;
|
|
||||||
|
|
||||||
// Fast path if we already have a context active on this thread
|
// Fast path if we already have a context active on this thread
|
||||||
if (currentContext.id)
|
if (currentContext.id)
|
||||||
@ -443,7 +524,7 @@ void GlContext::acquireTransientContext()
|
|||||||
|
|
||||||
// If currentContextId is not set, this must be the first
|
// If currentContextId is not set, this must be the first
|
||||||
// TransientContextLock on this thread, construct the state object
|
// TransientContextLock on this thread, construct the state object
|
||||||
transientContext.emplace();
|
TransientContext::get().emplace();
|
||||||
|
|
||||||
// Make sure a context is active at this point
|
// Make sure a context is active at this point
|
||||||
assert(currentContext.id);
|
assert(currentContext.id);
|
||||||
@ -453,8 +534,7 @@ void GlContext::acquireTransientContext()
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
void GlContext::releaseTransientContext()
|
void GlContext::releaseTransientContext()
|
||||||
{
|
{
|
||||||
using GlContextImpl::currentContext;
|
auto& currentContext = GlContextImpl::CurrentContext::get();
|
||||||
using GlContextImpl::transientContext;
|
|
||||||
|
|
||||||
// Make sure a context was left active after acquireTransientContext() was called
|
// Make sure a context was left active after acquireTransientContext() was called
|
||||||
assert(currentContext.id);
|
assert(currentContext.id);
|
||||||
@ -468,34 +548,29 @@ void GlContext::releaseTransientContext()
|
|||||||
|
|
||||||
// If currentContextId is set and currentContextTransientCount is 0,
|
// If currentContextId is set and currentContextTransientCount is 0,
|
||||||
// this is the last TransientContextLock that is released, destroy the state object
|
// this is the last TransientContextLock that is released, destroy the state object
|
||||||
transientContext.reset();
|
TransientContext::get().reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
std::unique_ptr<GlContext> GlContext::create()
|
std::unique_ptr<GlContext> GlContext::create()
|
||||||
{
|
{
|
||||||
using GlContextImpl::mutex;
|
|
||||||
using GlContextImpl::sharedContext;
|
|
||||||
|
|
||||||
// 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)
|
||||||
assert(sharedContext != nullptr);
|
auto sharedContext = SharedContext::get();
|
||||||
|
|
||||||
const std::lock_guard lock(mutex);
|
const std::lock_guard lock(sharedContext->mutex);
|
||||||
|
|
||||||
std::unique_ptr<GlContext> context;
|
std::unique_ptr<GlContext> context;
|
||||||
|
|
||||||
// We don't use acquireTransientContext here since we have
|
// We don't use acquireTransientContext here since we have
|
||||||
// to ensure we have exclusive access to the shared context
|
// to ensure we have exclusive access to the shared context
|
||||||
// in order to make sure it is not active during context creation
|
// in order to make sure it is not active during context creation
|
||||||
{
|
sharedContext->context->setActive(true);
|
||||||
sharedContext->setActive(true);
|
|
||||||
|
|
||||||
// Create the context
|
// Create the context
|
||||||
context = std::make_unique<ContextType>(sharedContext.get());
|
context = std::make_unique<ContextType>(&sharedContext->context.value());
|
||||||
|
|
||||||
sharedContext->setActive(false);
|
sharedContext->context->setActive(false);
|
||||||
}
|
|
||||||
|
|
||||||
context->initialize(ContextSettings());
|
context->initialize(ContextSettings());
|
||||||
|
|
||||||
@ -506,31 +581,26 @@ std::unique_ptr<GlContext> GlContext::create()
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
std::unique_ptr<GlContext> GlContext::create(const ContextSettings& settings, const WindowImpl& owner, unsigned int bitsPerPixel)
|
std::unique_ptr<GlContext> GlContext::create(const ContextSettings& settings, const WindowImpl& owner, unsigned int bitsPerPixel)
|
||||||
{
|
{
|
||||||
using GlContextImpl::loadExtensions;
|
|
||||||
using GlContextImpl::mutex;
|
|
||||||
using GlContextImpl::resourceCount;
|
|
||||||
using GlContextImpl::sharedContext;
|
|
||||||
|
|
||||||
// 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)
|
||||||
assert(sharedContext != nullptr);
|
auto sharedContext = SharedContext::get();
|
||||||
|
|
||||||
const std::lock_guard lock(mutex);
|
const std::lock_guard lock(sharedContext->mutex);
|
||||||
|
|
||||||
// If resourceCount is 1 we know that we are inside sf::Context or sf::Window
|
// If use_count is 2 (GlResource + sharedContext) we know that we are inside sf::Context or sf::Window
|
||||||
// Only in this situation we allow the user to indirectly re-create the shared context as a core context
|
// Only in this situation we allow the user to indirectly re-create the shared context as a core context
|
||||||
|
|
||||||
// Check if we need to convert our shared context into a core context
|
// Check if we need to convert our shared context into a core context
|
||||||
if ((resourceCount == 1) && (settings.attributeFlags & ContextSettings::Core) &&
|
if ((sharedContext.use_count() == 2) && (settings.attributeFlags & ContextSettings::Core) &&
|
||||||
!(sharedContext->m_settings.attributeFlags & ContextSettings::Core))
|
!(sharedContext->context->m_settings.attributeFlags & ContextSettings::Core))
|
||||||
{
|
{
|
||||||
// Re-create our shared context as a core context
|
// Re-create our shared context as a core context
|
||||||
const ContextSettings sharedSettings(0, 0, 0, settings.majorVersion, settings.minorVersion, settings.attributeFlags);
|
const ContextSettings sharedSettings(0, 0, 0, settings.majorVersion, settings.minorVersion, settings.attributeFlags);
|
||||||
|
|
||||||
sharedContext = std::make_unique<ContextType>(nullptr, sharedSettings, Vector2u(1, 1));
|
sharedContext->context.emplace(nullptr, sharedSettings, Vector2u(1, 1));
|
||||||
sharedContext->initialize(sharedSettings);
|
sharedContext->context->initialize(sharedSettings);
|
||||||
|
|
||||||
// Reload our extensions vector
|
// Reload our extensions vector
|
||||||
loadExtensions();
|
sharedContext->loadExtensions();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<GlContext> context;
|
std::unique_ptr<GlContext> context;
|
||||||
@ -538,14 +608,12 @@ std::unique_ptr<GlContext> GlContext::create(const ContextSettings& settings, co
|
|||||||
// We don't use acquireTransientContext here since we have
|
// We don't use acquireTransientContext here since we have
|
||||||
// to ensure we have exclusive access to the shared context
|
// to ensure we have exclusive access to the shared context
|
||||||
// in order to make sure it is not active during context creation
|
// in order to make sure it is not active during context creation
|
||||||
{
|
sharedContext->context->setActive(true);
|
||||||
sharedContext->setActive(true);
|
|
||||||
|
|
||||||
// Create the context
|
// Create the context
|
||||||
context = std::make_unique<ContextType>(sharedContext.get(), settings, owner, bitsPerPixel);
|
context = std::make_unique<ContextType>(&sharedContext->context.value(), settings, owner, bitsPerPixel);
|
||||||
|
|
||||||
sharedContext->setActive(false);
|
sharedContext->context->setActive(false);
|
||||||
}
|
|
||||||
|
|
||||||
context->initialize(settings);
|
context->initialize(settings);
|
||||||
context->checkSettings(settings);
|
context->checkSettings(settings);
|
||||||
@ -557,31 +625,26 @@ std::unique_ptr<GlContext> GlContext::create(const ContextSettings& settings, co
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
std::unique_ptr<GlContext> GlContext::create(const ContextSettings& settings, const Vector2u& size)
|
std::unique_ptr<GlContext> GlContext::create(const ContextSettings& settings, const Vector2u& size)
|
||||||
{
|
{
|
||||||
using GlContextImpl::loadExtensions;
|
|
||||||
using GlContextImpl::mutex;
|
|
||||||
using GlContextImpl::resourceCount;
|
|
||||||
using GlContextImpl::sharedContext;
|
|
||||||
|
|
||||||
// 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)
|
||||||
assert(sharedContext != nullptr);
|
auto sharedContext = SharedContext::get();
|
||||||
|
|
||||||
const std::lock_guard lock(mutex);
|
const std::lock_guard lock(sharedContext->mutex);
|
||||||
|
|
||||||
// If resourceCount is 1 we know that we are inside sf::Context or sf::Window
|
// If use_count is 2 (GlResource + sharedContext) we know that we are inside sf::Context or sf::Window
|
||||||
// Only in this situation we allow the user to indirectly re-create the shared context as a core context
|
// Only in this situation we allow the user to indirectly re-create the shared context as a core context
|
||||||
|
|
||||||
// Check if we need to convert our shared context into a core context
|
// Check if we need to convert our shared context into a core context
|
||||||
if ((resourceCount == 1) && (settings.attributeFlags & ContextSettings::Core) &&
|
if ((sharedContext.use_count() == 2) && (settings.attributeFlags & ContextSettings::Core) &&
|
||||||
!(sharedContext->m_settings.attributeFlags & ContextSettings::Core))
|
!(sharedContext->context->m_settings.attributeFlags & ContextSettings::Core))
|
||||||
{
|
{
|
||||||
// Re-create our shared context as a core context
|
// Re-create our shared context as a core context
|
||||||
const ContextSettings sharedSettings(0, 0, 0, settings.majorVersion, settings.minorVersion, settings.attributeFlags);
|
const ContextSettings sharedSettings(0, 0, 0, settings.majorVersion, settings.minorVersion, settings.attributeFlags);
|
||||||
|
|
||||||
sharedContext = std::make_unique<ContextType>(nullptr, sharedSettings, Vector2u(1, 1));
|
sharedContext->context.emplace(nullptr, sharedSettings, Vector2u(1, 1));
|
||||||
sharedContext->initialize(sharedSettings);
|
sharedContext->context->initialize(sharedSettings);
|
||||||
|
|
||||||
// Reload our extensions vector
|
// Reload our extensions vector
|
||||||
loadExtensions();
|
sharedContext->loadExtensions();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<GlContext> context;
|
std::unique_ptr<GlContext> context;
|
||||||
@ -589,14 +652,12 @@ std::unique_ptr<GlContext> GlContext::create(const ContextSettings& settings, co
|
|||||||
// We don't use acquireTransientContext here since we have
|
// We don't use acquireTransientContext here since we have
|
||||||
// to ensure we have exclusive access to the shared context
|
// to ensure we have exclusive access to the shared context
|
||||||
// in order to make sure it is not active during context creation
|
// in order to make sure it is not active during context creation
|
||||||
{
|
sharedContext->context->setActive(true);
|
||||||
sharedContext->setActive(true);
|
|
||||||
|
|
||||||
// Create the context
|
// Create the context
|
||||||
context = std::make_unique<ContextType>(sharedContext.get(), settings, size);
|
context = std::make_unique<ContextType>(&sharedContext->context.value(), settings, size);
|
||||||
|
|
||||||
sharedContext->setActive(false);
|
sharedContext->context->setActive(false);
|
||||||
}
|
|
||||||
|
|
||||||
context->initialize(settings);
|
context->initialize(settings);
|
||||||
context->checkSettings(settings);
|
context->checkSettings(settings);
|
||||||
@ -608,15 +669,27 @@ std::unique_ptr<GlContext> GlContext::create(const ContextSettings& settings, co
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
bool GlContext::isExtensionAvailable(const char* name)
|
bool GlContext::isExtensionAvailable(const char* name)
|
||||||
{
|
{
|
||||||
using GlContextImpl::extensions;
|
// If this function is called before any context is available,
|
||||||
return std::find(extensions.begin(), extensions.end(), name) != extensions.end();
|
// the shared context will be created for the duration of this call
|
||||||
|
const auto sharedContext = SharedContext::get();
|
||||||
|
|
||||||
|
return std::find(sharedContext->extensions.begin(), sharedContext->extensions.end(), name) !=
|
||||||
|
sharedContext->extensions.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
GlFunctionPointer GlContext::getFunction(const char* name)
|
GlFunctionPointer GlContext::getFunction(const char* name)
|
||||||
{
|
{
|
||||||
const std::lock_guard lock(GlContextImpl::mutex);
|
// Make sure we don't try to create the shared context here since
|
||||||
|
// setActive can be called during construction and lead to infinite recursion
|
||||||
|
auto* sharedContext = SharedContext::getWeakPtr().lock().get();
|
||||||
|
|
||||||
|
// We can't and don't need to lock when we are currently creating the shared context
|
||||||
|
std::unique_lock<std::recursive_mutex> lock;
|
||||||
|
|
||||||
|
if (sharedContext)
|
||||||
|
lock = std::unique_lock(sharedContext->mutex);
|
||||||
|
|
||||||
return ContextType::getFunction(name);
|
return ContextType::getFunction(name);
|
||||||
}
|
}
|
||||||
@ -625,27 +698,23 @@ GlFunctionPointer GlContext::getFunction(const char* name)
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
const GlContext* GlContext::getActiveContext()
|
const GlContext* GlContext::getActiveContext()
|
||||||
{
|
{
|
||||||
using GlContextImpl::currentContext;
|
return GlContextImpl::CurrentContext::get().ptr;
|
||||||
|
|
||||||
return currentContext.ptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
std::uint64_t GlContext::getActiveContextId()
|
std::uint64_t GlContext::getActiveContextId()
|
||||||
{
|
{
|
||||||
using GlContextImpl::currentContext;
|
return GlContextImpl::CurrentContext::get().id;
|
||||||
|
|
||||||
return currentContext.id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
GlContext::~GlContext()
|
GlContext::~GlContext()
|
||||||
{
|
{
|
||||||
using GlContextImpl::currentContext;
|
auto& currentContext = GlContextImpl::CurrentContext::get();
|
||||||
|
|
||||||
if (m_id == currentContext.id)
|
if (m_impl->m_id == currentContext.id)
|
||||||
{
|
{
|
||||||
currentContext.id = 0;
|
currentContext.id = 0;
|
||||||
currentContext.ptr = nullptr;
|
currentContext.ptr = nullptr;
|
||||||
@ -663,20 +732,27 @@ const ContextSettings& GlContext::getSettings() const
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
bool GlContext::setActive(bool active)
|
bool GlContext::setActive(bool active)
|
||||||
{
|
{
|
||||||
using GlContextImpl::currentContext;
|
auto& currentContext = GlContextImpl::CurrentContext::get();
|
||||||
using GlContextImpl::mutex;
|
|
||||||
|
// Make sure we don't try to create the shared context here since
|
||||||
|
// setActive can be called during construction and lead to infinite recursion
|
||||||
|
auto* sharedContext = SharedContext::getWeakPtr().lock().get();
|
||||||
|
|
||||||
if (active)
|
if (active)
|
||||||
{
|
{
|
||||||
if (m_id != currentContext.id)
|
if (m_impl->m_id != currentContext.id)
|
||||||
{
|
{
|
||||||
const std::lock_guard lock(mutex);
|
// We can't and don't need to lock when we are currently creating the shared context
|
||||||
|
std::unique_lock<std::recursive_mutex> lock;
|
||||||
|
|
||||||
|
if (sharedContext)
|
||||||
|
lock = std::unique_lock(sharedContext->mutex);
|
||||||
|
|
||||||
// Activate the context
|
// Activate the context
|
||||||
if (makeCurrent(true))
|
if (makeCurrent(true))
|
||||||
{
|
{
|
||||||
// Set it as the new current context for this thread
|
// Set it as the new current context for this thread
|
||||||
currentContext.id = m_id;
|
currentContext.id = m_impl->m_id;
|
||||||
currentContext.ptr = this;
|
currentContext.ptr = this;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -693,9 +769,13 @@ bool GlContext::setActive(bool active)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (m_id == currentContext.id)
|
if (m_impl->m_id == currentContext.id)
|
||||||
{
|
{
|
||||||
const std::lock_guard lock(mutex);
|
// We can't and don't need to lock when we are currently creating the shared context
|
||||||
|
std::unique_lock<std::recursive_mutex> lock;
|
||||||
|
|
||||||
|
if (sharedContext)
|
||||||
|
lock = std::unique_lock(sharedContext->mutex);
|
||||||
|
|
||||||
// Deactivate the context
|
// Deactivate the context
|
||||||
if (makeCurrent(false))
|
if (makeCurrent(false))
|
||||||
@ -719,7 +799,7 @@ bool GlContext::setActive(bool active)
|
|||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
GlContext::GlContext() : m_id(GlContextImpl::nextContextId.fetch_add(1))
|
GlContext::GlContext() : m_impl(std::make_unique<Impl>())
|
||||||
{
|
{
|
||||||
// Nothing to do
|
// Nothing to do
|
||||||
}
|
}
|
||||||
@ -765,23 +845,34 @@ int GlContext::evaluateFormat(
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
void GlContext::cleanupUnsharedResources()
|
void GlContext::cleanupUnsharedResources()
|
||||||
{
|
{
|
||||||
using GlContextImpl::ContextDestroyCallbacks;
|
const auto& currentContext = GlContextImpl::CurrentContext::get();
|
||||||
using GlContextImpl::contextDestroyCallbacks;
|
|
||||||
using GlContextImpl::currentContext;
|
|
||||||
|
|
||||||
// Save the current context so we can restore it later
|
// Save the current context so we can restore it later
|
||||||
GlContext* contextToRestore = currentContext.ptr;
|
GlContext* contextToRestore = currentContext.ptr;
|
||||||
|
|
||||||
// If this context is already active there is no need to save it
|
// If this context is already active there is no need to save it
|
||||||
if (m_id == currentContext.id)
|
if (m_impl->m_id == currentContext.id)
|
||||||
contextToRestore = nullptr;
|
contextToRestore = nullptr;
|
||||||
|
|
||||||
// Make this context active so resources can be freed
|
// Make this context active so resources can be freed
|
||||||
setActive(true);
|
setActive(true);
|
||||||
|
|
||||||
// Call the registered destruction callbacks
|
{
|
||||||
for (auto& [callback, ptr] : contextDestroyCallbacks)
|
const std::lock_guard lock(Impl::getUnsharedGlObjectsMutex());
|
||||||
callback(ptr);
|
|
||||||
|
// Destroy the unshared objects contained in this context
|
||||||
|
for (auto iter = m_impl->unsharedGlObjects->begin(); iter != m_impl->unsharedGlObjects->end();)
|
||||||
|
{
|
||||||
|
if (iter->contextId == m_impl->m_id)
|
||||||
|
{
|
||||||
|
iter = m_impl->unsharedGlObjects->erase(iter);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
++iter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Make the originally active context active again
|
// Make the originally active context active again
|
||||||
if (contextToRestore)
|
if (contextToRestore)
|
||||||
@ -815,7 +906,7 @@ void GlContext::initialize(const ContextSettings& requestedSettings)
|
|||||||
glGetIntegervFunc(GL_MAJOR_VERSION, &majorVersion);
|
glGetIntegervFunc(GL_MAJOR_VERSION, &majorVersion);
|
||||||
glGetIntegervFunc(GL_MINOR_VERSION, &minorVersion);
|
glGetIntegervFunc(GL_MINOR_VERSION, &minorVersion);
|
||||||
|
|
||||||
if ((glGetErrorFunc() != GL_INVALID_ENUM) && (majorVersion != 0))
|
if (glGetErrorFunc() != GL_INVALID_ENUM)
|
||||||
{
|
{
|
||||||
m_settings.majorVersion = static_cast<unsigned int>(majorVersion);
|
m_settings.majorVersion = static_cast<unsigned int>(majorVersion);
|
||||||
m_settings.minorVersion = static_cast<unsigned int>(minorVersion);
|
m_settings.minorVersion = static_cast<unsigned int>(minorVersion);
|
||||||
@ -836,7 +927,24 @@ void GlContext::initialize(const ContextSettings& requestedSettings)
|
|||||||
// OpenGL ES Full profile: The beginning of the returned string is "OpenGL ES major.minor"
|
// OpenGL ES Full profile: The beginning of the returned string is "OpenGL ES major.minor"
|
||||||
// Desktop OpenGL: The beginning of the returned string is "major.minor"
|
// Desktop OpenGL: The beginning of the returned string is "major.minor"
|
||||||
|
|
||||||
using GlContextImpl::parseVersionString;
|
// Helper to parse OpenGL version strings
|
||||||
|
static const auto parseVersionString =
|
||||||
|
[](const char* versionString, const char* prefix, unsigned int& major, unsigned int& minor)
|
||||||
|
{
|
||||||
|
const std::size_t prefixLength = std::strlen(prefix);
|
||||||
|
|
||||||
|
if ((std::strlen(versionString) >= (prefixLength + 3)) &&
|
||||||
|
(std::strncmp(versionString, prefix, prefixLength) == 0) && std::isdigit(versionString[prefixLength]) &&
|
||||||
|
(versionString[prefixLength + 1] == '.') && std::isdigit(versionString[prefixLength + 2]))
|
||||||
|
{
|
||||||
|
major = static_cast<unsigned int>(versionString[prefixLength] - '0');
|
||||||
|
minor = static_cast<unsigned int>(versionString[prefixLength + 2] - '0');
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
if (!parseVersionString(version, "OpenGL ES-CL ", m_settings.majorVersion, m_settings.minorVersion) &&
|
if (!parseVersionString(version, "OpenGL ES-CL ", m_settings.majorVersion, m_settings.minorVersion) &&
|
||||||
!parseVersionString(version, "OpenGL ES-CM ", m_settings.majorVersion, m_settings.minorVersion) &&
|
!parseVersionString(version, "OpenGL ES-CM ", m_settings.majorVersion, m_settings.minorVersion) &&
|
||||||
|
@ -50,38 +50,32 @@ class GlContext
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
/// \brief Perform resource initialization
|
/// \brief Get a shared_ptr to the shared context
|
||||||
///
|
///
|
||||||
/// This function is called every time an OpenGL resource is
|
/// \return shared_ptr to the shared context
|
||||||
/// created. When the first resource is initialized, it makes
|
|
||||||
/// sure that everything is ready for contexts to work properly.
|
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
static void initResource();
|
static std::shared_ptr<void> getSharedContext();
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
/// \brief Perform resource cleanup
|
/// \brief Register an OpenGL object to be destroyed when its containing context is destroyed
|
||||||
///
|
|
||||||
/// This function is called every time an OpenGL resource is
|
|
||||||
/// destroyed. When the last resource is destroyed, it makes
|
|
||||||
/// sure that everything that was created by initResource()
|
|
||||||
/// is properly released.
|
|
||||||
///
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
static void cleanupResource();
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
/// \brief Register a function to be called when a context is destroyed
|
|
||||||
///
|
///
|
||||||
/// This is used for internal purposes in order to properly
|
/// This is used for internal purposes in order to properly
|
||||||
/// clean up OpenGL resources that cannot be shared bwteen
|
/// clean up OpenGL resources that cannot be shared bwteen
|
||||||
/// contexts.
|
/// contexts.
|
||||||
///
|
///
|
||||||
/// \param callback Function to be called when a context is destroyed
|
/// \param object Object to be destroyed when its containing context is destroyed
|
||||||
/// \param arg Argument to pass when calling the function
|
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
static void registerContextDestroyCallback(ContextDestroyCallback callback, void* arg);
|
static void registerUnsharedGlObject(std::shared_ptr<void> object);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
/// \brief Unregister an OpenGL object from its containing context
|
||||||
|
///
|
||||||
|
/// \param object Object to be unregister
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
static void unregisterUnsharedGlObject(std::shared_ptr<void> object);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
/// \brief Acquires a context for short-term use on the current thread
|
/// \brief Acquires a context for short-term use on the current thread
|
||||||
@ -300,6 +294,10 @@ protected:
|
|||||||
ContextSettings m_settings; //!< Creation settings of the context
|
ContextSettings m_settings; //!< Creation settings of the context
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct TransientContext;
|
||||||
|
struct SharedContext;
|
||||||
|
struct Impl;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
/// \brief Perform various initializations after the context construction
|
/// \brief Perform various initializations after the context construction
|
||||||
/// \param requestedSettings Requested settings during context creation
|
/// \param requestedSettings Requested settings during context creation
|
||||||
@ -317,7 +315,7 @@ private:
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
// Member data
|
// Member data
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
const std::uint64_t m_id; //!< Unique number that identifies the context
|
const std::unique_ptr<Impl> m_impl; //!< Implementation details
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace sf::priv
|
} // namespace sf::priv
|
||||||
|
@ -32,23 +32,22 @@
|
|||||||
namespace sf
|
namespace sf
|
||||||
{
|
{
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
GlResource::GlResource()
|
GlResource::GlResource() : m_sharedContext(priv::GlContext::getSharedContext())
|
||||||
{
|
{
|
||||||
priv::GlContext::initResource();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
GlResource::~GlResource()
|
void GlResource::registerUnsharedGlObject(std::shared_ptr<void> object)
|
||||||
{
|
{
|
||||||
priv::GlContext::cleanupResource();
|
priv::GlContext::registerUnsharedGlObject(std::move(object));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
void GlResource::registerContextDestroyCallback(ContextDestroyCallback callback, void* arg)
|
void GlResource::unregisterUnsharedGlObject(std::shared_ptr<void> object)
|
||||||
{
|
{
|
||||||
priv::GlContext::registerContextDestroyCallback(callback, arg);
|
priv::GlContext::unregisterUnsharedGlObject(std::move(object));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
static_assert(!std::is_constructible_v<sf::GlResource>);
|
static_assert(!std::is_constructible_v<sf::GlResource>);
|
||||||
static_assert(!std::is_copy_constructible_v<sf::GlResource>);
|
static_assert(std::is_copy_constructible_v<sf::GlResource>);
|
||||||
static_assert(std::is_copy_assignable_v<sf::GlResource>);
|
static_assert(std::is_copy_assignable_v<sf::GlResource>);
|
||||||
static_assert(!std::is_nothrow_move_constructible_v<sf::GlResource>);
|
static_assert(std::is_nothrow_move_constructible_v<sf::GlResource>);
|
||||||
static_assert(std::is_nothrow_move_assignable_v<sf::GlResource>);
|
static_assert(std::is_nothrow_move_assignable_v<sf::GlResource>);
|
||||||
|
Loading…
Reference in New Issue
Block a user