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 <memory>
|
||||
|
||||
|
||||
namespace sf
|
||||
{
|
||||
@ -48,23 +50,24 @@ protected:
|
||||
GlResource();
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Destructor
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
~GlResource();
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Register a function to be called when a context is destroyed
|
||||
/// \brief Register an OpenGL object to be destroyed when its containing context is destroyed
|
||||
///
|
||||
/// This is used for internal purposes in order to properly
|
||||
/// clean up OpenGL resources that cannot be shared between
|
||||
/// contexts.
|
||||
///
|
||||
/// \param callback Function to be called when a context is destroyed
|
||||
/// \param arg Argument to pass when calling the function
|
||||
/// \param object Object to be destroyed when its containing context is destroyed
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
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
|
||||
@ -97,6 +100,12 @@ protected:
|
||||
////////////////////////////////////////////////////////////
|
||||
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
|
||||
|
@ -54,30 +54,35 @@ namespace
|
||||
namespace RenderTargetImpl
|
||||
{
|
||||
// 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
|
||||
// tracking the currently active RenderTarget within a given context
|
||||
std::uint64_t getUniqueId()
|
||||
{
|
||||
const std::lock_guard lock(mutex);
|
||||
|
||||
static std::uint64_t id = 1; // start at 1, zero is "no RenderTarget"
|
||||
|
||||
const std::lock_guard lock(getMutex());
|
||||
static std::uint64_t id = 1; // start at 1, zero is "no RenderTarget"
|
||||
return id++;
|
||||
}
|
||||
|
||||
// Map to help us detect whether a different RenderTarget
|
||||
// has been activated within a single context
|
||||
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
|
||||
bool isActive(std::uint64_t id)
|
||||
{
|
||||
const auto it = contextRenderTargetMap.find(sf::Context::getActiveContextId());
|
||||
|
||||
return (it != contextRenderTargetMap.end()) && (it->second == id);
|
||||
const auto it = getContextRenderTargetMap().find(sf::Context::getActiveContextId());
|
||||
return (it != getContextRenderTargetMap().end()) && (it->second == id);
|
||||
}
|
||||
|
||||
// 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
|
||||
{
|
||||
const std::lock_guard lock(RenderTargetImpl::mutex);
|
||||
const std::lock_guard lock(RenderTargetImpl::getMutex());
|
||||
|
||||
const std::uint64_t contextId = Context::getActiveContextId();
|
||||
|
||||
using RenderTargetImpl::contextRenderTargetMap;
|
||||
const auto it = contextRenderTargetMap.find(contextId);
|
||||
using RenderTargetImpl::getContextRenderTargetMap;
|
||||
auto& contextRenderTargetMap = getContextRenderTargetMap();
|
||||
auto it = contextRenderTargetMap.find(contextId);
|
||||
|
||||
if (active)
|
||||
{
|
||||
|
@ -36,98 +36,31 @@
|
||||
|
||||
#include <mutex>
|
||||
#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
|
||||
{
|
||||
////////////////////////////////////////////////////////////
|
||||
RenderTextureImplFBO::RenderTextureImplFBO()
|
||||
struct RenderTextureImplFBO::FrameBufferObject
|
||||
{
|
||||
const std::lock_guard lock(mutex);
|
||||
FrameBufferObject()
|
||||
{
|
||||
// Create the framebuffer object
|
||||
glCheck(GLEXT_glGenFramebuffers(1, &object));
|
||||
}
|
||||
|
||||
// Register the context destruction callback
|
||||
registerContextDestroyCallback(contextDestroyCallback, nullptr);
|
||||
~FrameBufferObject()
|
||||
{
|
||||
if (object)
|
||||
glCheck(GLEXT_glDeleteFramebuffers(1, &object));
|
||||
}
|
||||
|
||||
// Insert the new frame buffer mapping into the set of all active mappings
|
||||
frameBuffers.insert(&m_frameBuffers);
|
||||
frameBuffers.insert(&m_multisampleFrameBuffers);
|
||||
}
|
||||
GLuint object{};
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
RenderTextureImplFBO::RenderTextureImplFBO() = default;
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
@ -135,12 +68,6 @@ RenderTextureImplFBO::~RenderTextureImplFBO()
|
||||
{
|
||||
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
|
||||
if (m_colorBuffer)
|
||||
{
|
||||
@ -155,15 +82,22 @@ RenderTextureImplFBO::~RenderTextureImplFBO()
|
||||
glCheck(GLEXT_glDeleteRenderbuffers(1, &depthStencilBuffer));
|
||||
}
|
||||
|
||||
// Move all frame buffer objects to stale set
|
||||
for (auto& [contextId, frameBufferId] : m_frameBuffers)
|
||||
staleFrameBuffers.emplace(contextId, frameBufferId);
|
||||
// Unregister FBOs with the contexts if they haven't already been destroyed
|
||||
for (auto& entry : m_frameBuffers)
|
||||
{
|
||||
auto frameBuffer = entry.second.lock();
|
||||
|
||||
for (auto& [contextId, multisampleFrameBufferId] : m_multisampleFrameBuffers)
|
||||
staleFrameBuffers.emplace(contextId, multisampleFrameBufferId);
|
||||
if (frameBuffer)
|
||||
unregisterUnsharedGlObject(std::move(frameBuffer));
|
||||
}
|
||||
|
||||
// Clean up FBOs
|
||||
destroyStaleFBOs();
|
||||
for (auto& entry : m_multisampleFrameBuffers)
|
||||
{
|
||||
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()
|
||||
{
|
||||
// Create the framebuffer object
|
||||
GLuint frameBuffer = 0;
|
||||
glCheck(GLEXT_glGenFramebuffers(1, &frameBuffer));
|
||||
auto frameBuffer = std::make_shared<FrameBufferObject>();
|
||||
|
||||
if (!frameBuffer)
|
||||
if (!frameBuffer->object)
|
||||
{
|
||||
err() << "Impossible to create render texture (failed to create the frame buffer object)" << std::endl;
|
||||
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
|
||||
if (!m_multisample && m_depthStencilBuffer)
|
||||
@ -462,33 +395,30 @@ bool RenderTextureImplFBO::createFrameBuffer()
|
||||
if (status != GLEXT_GL_FRAMEBUFFER_COMPLETE)
|
||||
{
|
||||
glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, 0));
|
||||
glCheck(GLEXT_glDeleteFramebuffers(1, &frameBuffer));
|
||||
err() << "Impossible to create render texture (failed to link the target texture to the frame buffer)" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
const std::lock_guard lock(mutex);
|
||||
// Insert the FBO into our map
|
||||
m_frameBuffers.emplace(Context::getActiveContextId(), frameBuffer);
|
||||
|
||||
// Insert the FBO into our map
|
||||
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
|
||||
|
||||
if (m_multisample)
|
||||
{
|
||||
// Create the multisample framebuffer object
|
||||
GLuint multisampleFrameBuffer = 0;
|
||||
glCheck(GLEXT_glGenFramebuffers(1, &multisampleFrameBuffer));
|
||||
auto multisampleFrameBuffer = std::make_shared<FrameBufferObject>();
|
||||
|
||||
if (!multisampleFrameBuffer)
|
||||
if (!multisampleFrameBuffer->object)
|
||||
{
|
||||
err() << "Impossible to create render texture (failed to create the multisample frame buffer object)"
|
||||
<< std::endl;
|
||||
return false;
|
||||
}
|
||||
glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, multisampleFrameBuffer));
|
||||
glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, multisampleFrameBuffer->object));
|
||||
|
||||
// Link the multisample color buffer to the frame buffer
|
||||
glCheck(GLEXT_glBindRenderbuffer(GLEXT_GL_RENDERBUFFER, m_colorBuffer));
|
||||
@ -517,19 +447,17 @@ bool RenderTextureImplFBO::createFrameBuffer()
|
||||
if (status != GLEXT_GL_FRAMEBUFFER_COMPLETE)
|
||||
{
|
||||
glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, 0));
|
||||
glCheck(GLEXT_glDeleteFramebuffers(1, &multisampleFrameBuffer));
|
||||
err() << "Impossible to create render texture (failed to link the render buffers to the multisample frame "
|
||||
"buffer)"
|
||||
<< std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
const std::lock_guard lock(mutex);
|
||||
// Insert the FBO into our map
|
||||
m_multisampleFrameBuffers.emplace(Context::getActiveContextId(), multisampleFrameBuffer);
|
||||
|
||||
// Insert the FBO into our map
|
||||
m_multisampleFrameBuffers.emplace(Context::getActiveContextId(), multisampleFrameBuffer);
|
||||
}
|
||||
// Register the object with the current context so it is automatically destroyed
|
||||
registerUnsharedGlObject(std::move(multisampleFrameBuffer));
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -575,29 +503,33 @@ bool RenderTextureImplFBO::activate(bool active)
|
||||
// Lookup the FBO corresponding to the currently active context
|
||||
// If none is found, there is no FBO corresponding to the
|
||||
// currently active context so we will have to create a new FBO
|
||||
if (m_multisample)
|
||||
{
|
||||
const std::lock_guard lock(mutex);
|
||||
const auto it = m_multisampleFrameBuffers.find(contextId);
|
||||
|
||||
std::unordered_map<std::uint64_t, unsigned int>::iterator it;
|
||||
|
||||
if (m_multisample)
|
||||
if (it != m_multisampleFrameBuffers.end())
|
||||
{
|
||||
it = m_multisampleFrameBuffers.find(contextId);
|
||||
auto frameBuffer = it->second.lock();
|
||||
|
||||
if (it != m_multisampleFrameBuffers.end())
|
||||
if (frameBuffer)
|
||||
{
|
||||
glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, it->second));
|
||||
glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, frameBuffer->object));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
it = m_frameBuffers.find(contextId);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto it = m_frameBuffers.find(contextId);
|
||||
|
||||
if (it != m_frameBuffers.end())
|
||||
if (it != m_frameBuffers.end())
|
||||
{
|
||||
auto frameBuffer = it->second.lock();
|
||||
|
||||
if (frameBuffer)
|
||||
{
|
||||
glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, it->second));
|
||||
glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, frameBuffer->object));
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -630,26 +562,30 @@ void RenderTextureImplFBO::updateTexture(unsigned int)
|
||||
{
|
||||
const std::uint64_t contextId = Context::getActiveContextId();
|
||||
|
||||
const std::lock_guard lock(mutex);
|
||||
|
||||
const auto frameBufferIt = m_frameBuffers.find(contextId);
|
||||
const auto multisampleIt = m_multisampleFrameBuffers.find(contextId);
|
||||
|
||||
if ((frameBufferIt != m_frameBuffers.end()) && (multisampleIt != m_multisampleFrameBuffers.end()))
|
||||
{
|
||||
// Set up the blit target (draw framebuffer) and blit (from the read framebuffer, our multisample FBO)
|
||||
glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_DRAW_FRAMEBUFFER, frameBufferIt->second));
|
||||
glCheck(GLEXT_glBlitFramebuffer(0,
|
||||
0,
|
||||
static_cast<GLint>(m_size.x),
|
||||
static_cast<GLint>(m_size.y),
|
||||
0,
|
||||
0,
|
||||
static_cast<GLint>(m_size.x),
|
||||
static_cast<GLint>(m_size.y),
|
||||
GL_COLOR_BUFFER_BIT,
|
||||
GL_NEAREST));
|
||||
glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_DRAW_FRAMEBUFFER, multisampleIt->second));
|
||||
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)
|
||||
glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_DRAW_FRAMEBUFFER, frameBuffer->object));
|
||||
glCheck(GLEXT_glBlitFramebuffer(0,
|
||||
0,
|
||||
static_cast<GLint>(m_size.x),
|
||||
static_cast<GLint>(m_size.y),
|
||||
0,
|
||||
0,
|
||||
static_cast<GLint>(m_size.x),
|
||||
static_cast<GLint>(m_size.y),
|
||||
GL_COLOR_BUFFER_BIT,
|
||||
GL_NEAREST));
|
||||
glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_DRAW_FRAMEBUFFER, multiSampleFrameBuffer->object));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -137,8 +137,12 @@ private:
|
||||
////////////////////////////////////////////////////////////
|
||||
// Member data
|
||||
////////////////////////////////////////////////////////////
|
||||
std::unordered_map<std::uint64_t, unsigned int> m_frameBuffers; //!< OpenGL frame buffer objects per context
|
||||
std::unordered_map<std::uint64_t, unsigned int> m_multisampleFrameBuffers; //!< Optional per-context OpenGL frame buffer objects with multisample attachments
|
||||
struct FrameBufferObject;
|
||||
|
||||
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_colorBuffer{}; //!< Optional multisample color buffer attached to the frame buffer
|
||||
Vector2u m_size; //!< Width and height of the attachments
|
||||
|
@ -40,7 +40,6 @@
|
||||
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <mutex>
|
||||
#include <ostream>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@ -61,20 +60,17 @@
|
||||
|
||||
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
|
||||
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);
|
||||
}
|
||||
|
||||
@ -747,23 +743,16 @@ void Shader::bind(const Shader* shader)
|
||||
////////////////////////////////////////////////////////////
|
||||
bool Shader::isAvailable()
|
||||
{
|
||||
const std::lock_guard lock(isAvailableMutex);
|
||||
|
||||
static bool checked = false;
|
||||
static bool available = false;
|
||||
|
||||
if (!checked)
|
||||
static const bool available = []()
|
||||
{
|
||||
checked = true;
|
||||
|
||||
const 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 GLEXT_multitexture && GLEXT_shading_language_100 && GLEXT_shader_objects && GLEXT_vertex_shader &&
|
||||
GLEXT_fragment_shader;
|
||||
}();
|
||||
|
||||
return available;
|
||||
}
|
||||
@ -772,22 +761,15 @@ bool Shader::isAvailable()
|
||||
////////////////////////////////////////////////////////////
|
||||
bool Shader::isGeometryAvailable()
|
||||
{
|
||||
const std::lock_guard lock(isAvailableMutex);
|
||||
|
||||
static bool checked = false;
|
||||
static bool available = false;
|
||||
|
||||
if (!checked)
|
||||
static const bool available = []()
|
||||
{
|
||||
checked = true;
|
||||
|
||||
const TransientContextLock contextLock;
|
||||
|
||||
// Make sure that extensions are initialized
|
||||
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;
|
||||
}
|
||||
|
@ -71,6 +71,7 @@ Texture::Texture() : m_cacheId(TextureImpl::getUniqueId())
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
Texture::Texture(const Texture& copy) :
|
||||
GlResource(copy),
|
||||
m_isSmooth(copy.m_isSmooth),
|
||||
m_sRgb(copy.m_sRgb),
|
||||
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)
|
||||
{
|
||||
|
@ -167,17 +167,6 @@ namespace
|
||||
// A nested named namespace is used here to allow unity builds of SFML.
|
||||
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
|
||||
// track current context information for each thread
|
||||
struct CurrentContext
|
||||
@ -185,27 +174,156 @@ struct CurrentContext
|
||||
std::uint64_t id{};
|
||||
sf::priv::GlContext* ptr{};
|
||||
unsigned int transientCount{};
|
||||
|
||||
// This per-thread variable holds the current context information for each thread
|
||||
static CurrentContext& get()
|
||||
{
|
||||
thread_local CurrentContext currentContext;
|
||||
return currentContext;
|
||||
}
|
||||
|
||||
private:
|
||||
// Private constructor to prevent CurrentContext from being constructed outside of get()
|
||||
CurrentContext() = default;
|
||||
};
|
||||
} // namespace GlContextImpl
|
||||
} // namespace
|
||||
|
||||
|
||||
namespace sf::priv
|
||||
{
|
||||
// This structure contains all the state necessary to
|
||||
// track SharedContext usage
|
||||
struct GlContext::SharedContext
|
||||
{
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Constructor
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
SharedContext()
|
||||
{
|
||||
const std::lock_guard lock(mutex);
|
||||
|
||||
context.emplace(nullptr);
|
||||
context->initialize(sf::ContextSettings());
|
||||
|
||||
loadExtensions();
|
||||
|
||||
context->setActive(false);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Get weak_ptr
|
||||
///
|
||||
/// \return weak_ptr to the SharedContext
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
static std::weak_ptr<SharedContext>& getWeakPtr()
|
||||
{
|
||||
static std::weak_ptr<SharedContext> weakSharedContext;
|
||||
return weakSharedContext;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Get shared_ptr to the shared context
|
||||
///
|
||||
/// Create new object if one doesn't already exist.
|
||||
///
|
||||
/// \return shared_ptr to the shared context
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
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 Load our extensions vector with the supported extensions
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
void loadExtensions()
|
||||
{
|
||||
auto glGetErrorFunc = reinterpret_cast<glGetErrorFuncType>(getFunction("glGetError"));
|
||||
auto glGetIntegervFunc = reinterpret_cast<glGetIntegervFuncType>(getFunction("glGetIntegerv"));
|
||||
auto glGetStringFunc = reinterpret_cast<glGetStringFuncType>(getFunction("glGetString"));
|
||||
|
||||
if (!glGetErrorFunc || !glGetIntegervFunc || !glGetStringFunc)
|
||||
return;
|
||||
|
||||
// Check whether a >= 3.0 context is available
|
||||
int majorVersion = 0;
|
||||
glGetIntegervFunc(GL_MAJOR_VERSION, &majorVersion);
|
||||
|
||||
auto glGetStringiFunc = reinterpret_cast<glGetStringiFuncType>(getFunction("glGetStringi"));
|
||||
|
||||
if (glGetErrorFunc() == GL_INVALID_ENUM || !majorVersion || !glGetStringiFunc)
|
||||
{
|
||||
// Try to load the < 3.0 way
|
||||
const char* extensionString = reinterpret_cast<const char*>(glGetStringFunc(GL_EXTENSIONS));
|
||||
|
||||
if (extensionString)
|
||||
{
|
||||
extensions.clear();
|
||||
|
||||
do
|
||||
{
|
||||
const char* extension = extensionString;
|
||||
|
||||
while (*extensionString && (*extensionString != ' '))
|
||||
++extensionString;
|
||||
|
||||
extensions.emplace_back(extension, extensionString);
|
||||
} while (*extensionString++);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try to load the >= 3.0 way
|
||||
int numExtensions = 0;
|
||||
glGetIntegervFunc(GL_NUM_EXTENSIONS, &numExtensions);
|
||||
|
||||
if (numExtensions)
|
||||
{
|
||||
extensions.clear();
|
||||
|
||||
for (unsigned int i = 0; i < static_cast<unsigned int>(numExtensions); ++i)
|
||||
{
|
||||
const char* extensionString = reinterpret_cast<const char*>(glGetStringiFunc(GL_EXTENSIONS, i));
|
||||
|
||||
if (extensionString)
|
||||
extensions.emplace_back(extensionString);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
std::recursive_mutex mutex;
|
||||
|
||||
// Supported OpenGL extensions
|
||||
std::vector<std::string> extensions;
|
||||
|
||||
// The hidden, inactive context that will be shared with all other contexts
|
||||
std::optional<ContextType> context;
|
||||
};
|
||||
|
||||
// This per-thread variable holds the current context information for each thread
|
||||
thread_local CurrentContext currentContext;
|
||||
|
||||
// The hidden, inactive context that will be shared with all other contexts
|
||||
std::unique_ptr<ContextType> sharedContext;
|
||||
|
||||
// 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;
|
||||
|
||||
// This structure contains all the state necessary to
|
||||
// track TransientContext usage
|
||||
struct TransientContext
|
||||
struct GlContext::TransientContext
|
||||
{
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Constructor
|
||||
@ -215,15 +333,13 @@ struct TransientContext
|
||||
{
|
||||
// TransientContext should never be created if there is
|
||||
// already a context active on the current thread
|
||||
assert(!currentContext.id);
|
||||
assert(!GlContextImpl::CurrentContext::get().id);
|
||||
|
||||
std::unique_lock lock(mutex);
|
||||
// Lock ourselves so we don't create a new object if one doesn't already exist
|
||||
sharedContext = SharedContext::getWeakPtr().lock();
|
||||
|
||||
if (resourceCount == 0)
|
||||
if (!sharedContext)
|
||||
{
|
||||
// No GlResources, no shared context yet
|
||||
assert(!sharedContext);
|
||||
|
||||
// Create a Context object for temporary use
|
||||
context.emplace();
|
||||
}
|
||||
@ -233,8 +349,8 @@ struct TransientContext
|
||||
assert(sharedContext);
|
||||
|
||||
// Lock the shared context for temporary use
|
||||
sharedContextLock = std::move(lock);
|
||||
sharedContext->setActive(true);
|
||||
sharedContextLock = std::unique_lock(sharedContext->mutex);
|
||||
sharedContext->context->setActive(true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -245,7 +361,7 @@ struct TransientContext
|
||||
~TransientContext()
|
||||
{
|
||||
if (sharedContextLock)
|
||||
sharedContext->setActive(false);
|
||||
sharedContext->context->setActive(false);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
@ -260,176 +376,141 @@ struct TransientContext
|
||||
////////////////////////////////////////////////////////////
|
||||
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 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()
|
||||
// This structure contains all the implementation data we
|
||||
// don't want to expose through the visible interface
|
||||
struct GlContext::Impl
|
||||
{
|
||||
auto glGetErrorFunc = reinterpret_cast<glGetErrorFuncType>(sf::priv::GlContext::getFunction("glGetError"));
|
||||
auto glGetIntegervFunc = reinterpret_cast<glGetIntegervFuncType>(sf::priv::GlContext::getFunction("glGetIntegerv"));
|
||||
auto glGetStringFunc = reinterpret_cast<glGetStringFuncType>(sf::priv::GlContext::getFunction("glGetString"));
|
||||
|
||||
if (!glGetErrorFunc || !glGetIntegervFunc || !glGetStringFunc)
|
||||
return;
|
||||
|
||||
// Check whether a >= 3.0 context is available
|
||||
int majorVersion = 0;
|
||||
glGetIntegervFunc(GL_MAJOR_VERSION, &majorVersion);
|
||||
|
||||
auto glGetStringiFunc = reinterpret_cast<glGetStringiFuncType>(sf::priv::GlContext::getFunction("glGetStringi"));
|
||||
|
||||
if (glGetErrorFunc() == GL_INVALID_ENUM || !majorVersion || !glGetStringiFunc)
|
||||
{
|
||||
// Try to load the < 3.0 way
|
||||
const char* extensionString = reinterpret_cast<const char*>(glGetStringFunc(GL_EXTENSIONS));
|
||||
|
||||
if (extensionString)
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Constructor
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
Impl() :
|
||||
m_id(
|
||||
[]()
|
||||
{
|
||||
extensions.clear();
|
||||
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();
|
||||
|
||||
do
|
||||
{
|
||||
const char* extension = extensionString;
|
||||
if (!unsharedGlObjects)
|
||||
{
|
||||
unsharedGlObjects = std::make_shared<UnsharedGlObjects>();
|
||||
|
||||
while (*extensionString && (*extensionString != ' '))
|
||||
++extensionString;
|
||||
|
||||
extensions.emplace_back(extension, extensionString);
|
||||
} while (*extensionString++);
|
||||
weakUnsharedGlObjects = unsharedGlObjects;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
// Structure to track which unshared object belongs to which context
|
||||
struct UnsharedGlObject
|
||||
{
|
||||
// Try to load the >= 3.0 way
|
||||
int numExtensions = 0;
|
||||
glGetIntegervFunc(GL_NUM_EXTENSIONS, &numExtensions);
|
||||
std::uint64_t contextId;
|
||||
std::shared_ptr<void> object;
|
||||
};
|
||||
|
||||
if (numExtensions)
|
||||
{
|
||||
extensions.clear();
|
||||
using UnsharedGlObjects = std::vector<UnsharedGlObject>;
|
||||
|
||||
for (unsigned int i = 0; i < static_cast<unsigned int>(numExtensions); ++i)
|
||||
{
|
||||
const char* extensionString = reinterpret_cast<const char*>(glGetStringiFunc(GL_EXTENSIONS, i));
|
||||
|
||||
if (extensionString)
|
||||
extensions.emplace_back(extensionString);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to parse OpenGL version strings
|
||||
bool parseVersionString(const char* version, const char* prefix, unsigned int& major, unsigned int& minor)
|
||||
{
|
||||
const std::size_t prefixLength = std::strlen(prefix);
|
||||
|
||||
if ((std::strlen(version) >= (prefixLength + 3)) && (std::strncmp(version, prefix, prefixLength) == 0) &&
|
||||
std::isdigit(version[prefixLength]) && (version[prefixLength + 1] == '.') &&
|
||||
std::isdigit(version[prefixLength + 2]))
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Get weak_ptr to unshared objects
|
||||
///
|
||||
/// \return weak_ptr to unshared objects
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
static std::weak_ptr<UnsharedGlObjects>& getWeakUnsharedGlObjects()
|
||||
{
|
||||
major = static_cast<unsigned int>(version[prefixLength] - '0');
|
||||
minor = static_cast<unsigned int>(version[prefixLength + 2] - '0');
|
||||
static std::weak_ptr<UnsharedGlObjects> weakUnsharedGlObjects;
|
||||
|
||||
return true;
|
||||
return weakUnsharedGlObjects;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
} // namespace GlContextImpl
|
||||
} // namespace
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \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
|
||||
};
|
||||
|
||||
|
||||
namespace sf::priv
|
||||
{
|
||||
////////////////////////////////////////////////////////////
|
||||
void GlContext::initResource()
|
||||
std::shared_ptr<void> GlContext::getSharedContext()
|
||||
{
|
||||
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;
|
||||
return GlContext::SharedContext::get();
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
void GlContext::cleanupResource()
|
||||
void GlContext::registerUnsharedGlObject(std::shared_ptr<void> object)
|
||||
{
|
||||
using GlContextImpl::mutex;
|
||||
using GlContextImpl::resourceCount;
|
||||
using GlContextImpl::sharedContext;
|
||||
|
||||
// Protect from concurrent access
|
||||
const std::lock_guard lock(mutex);
|
||||
|
||||
// Decrement the resources counter
|
||||
--resourceCount;
|
||||
|
||||
// If there's no more resource alive, we can trigger the global context cleanup
|
||||
if (resourceCount == 0)
|
||||
{
|
||||
if (!sharedContext)
|
||||
return;
|
||||
|
||||
// Destroy the shared context
|
||||
sharedContext.reset();
|
||||
}
|
||||
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::registerContextDestroyCallback(ContextDestroyCallback callback, void* arg)
|
||||
void GlContext::unregisterUnsharedGlObject(std::shared_ptr<void> object)
|
||||
{
|
||||
GlContextImpl::contextDestroyCallbacks.emplace(callback, arg);
|
||||
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()
|
||||
{
|
||||
using GlContextImpl::currentContext;
|
||||
using GlContextImpl::TransientContext;
|
||||
using GlContextImpl::transientContext;
|
||||
auto& currentContext = GlContextImpl::CurrentContext::get();
|
||||
|
||||
// Fast path if we already have a context active on this thread
|
||||
if (currentContext.id)
|
||||
@ -443,7 +524,7 @@ void GlContext::acquireTransientContext()
|
||||
|
||||
// If currentContextId is not set, this must be the first
|
||||
// TransientContextLock on this thread, construct the state object
|
||||
transientContext.emplace();
|
||||
TransientContext::get().emplace();
|
||||
|
||||
// Make sure a context is active at this point
|
||||
assert(currentContext.id);
|
||||
@ -453,8 +534,7 @@ void GlContext::acquireTransientContext()
|
||||
////////////////////////////////////////////////////////////
|
||||
void GlContext::releaseTransientContext()
|
||||
{
|
||||
using GlContextImpl::currentContext;
|
||||
using GlContextImpl::transientContext;
|
||||
auto& currentContext = GlContextImpl::CurrentContext::get();
|
||||
|
||||
// Make sure a context was left active after acquireTransientContext() was called
|
||||
assert(currentContext.id);
|
||||
@ -468,34 +548,29 @@ void GlContext::releaseTransientContext()
|
||||
|
||||
// If currentContextId is set and currentContextTransientCount is 0,
|
||||
// this is the last TransientContextLock that is released, destroy the state object
|
||||
transientContext.reset();
|
||||
TransientContext::get().reset();
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
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)
|
||||
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;
|
||||
|
||||
// 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);
|
||||
sharedContext->context->setActive(true);
|
||||
|
||||
// Create the context
|
||||
context = std::make_unique<ContextType>(sharedContext.get());
|
||||
// Create the context
|
||||
context = std::make_unique<ContextType>(&sharedContext->context.value());
|
||||
|
||||
sharedContext->setActive(false);
|
||||
}
|
||||
sharedContext->context->setActive(false);
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
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
|
||||
|
||||
// Check if we need to convert our shared context into a core context
|
||||
if ((resourceCount == 1) && (settings.attributeFlags & ContextSettings::Core) &&
|
||||
!(sharedContext->m_settings.attributeFlags & ContextSettings::Core))
|
||||
if ((sharedContext.use_count() == 2) && (settings.attributeFlags & ContextSettings::Core) &&
|
||||
!(sharedContext->context->m_settings.attributeFlags & ContextSettings::Core))
|
||||
{
|
||||
// Re-create our shared context as a core context
|
||||
const ContextSettings sharedSettings(0, 0, 0, settings.majorVersion, settings.minorVersion, settings.attributeFlags);
|
||||
|
||||
sharedContext = std::make_unique<ContextType>(nullptr, sharedSettings, Vector2u(1, 1));
|
||||
sharedContext->initialize(sharedSettings);
|
||||
sharedContext->context.emplace(nullptr, sharedSettings, Vector2u(1, 1));
|
||||
sharedContext->context->initialize(sharedSettings);
|
||||
|
||||
// Reload our extensions vector
|
||||
loadExtensions();
|
||||
sharedContext->loadExtensions();
|
||||
}
|
||||
|
||||
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
|
||||
// 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);
|
||||
sharedContext->context->setActive(true);
|
||||
|
||||
// Create the context
|
||||
context = std::make_unique<ContextType>(sharedContext.get(), settings, owner, bitsPerPixel);
|
||||
// Create the context
|
||||
context = std::make_unique<ContextType>(&sharedContext->context.value(), settings, owner, bitsPerPixel);
|
||||
|
||||
sharedContext->setActive(false);
|
||||
}
|
||||
sharedContext->context->setActive(false);
|
||||
|
||||
context->initialize(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)
|
||||
{
|
||||
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)
|
||||
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
|
||||
|
||||
// Check if we need to convert our shared context into a core context
|
||||
if ((resourceCount == 1) && (settings.attributeFlags & ContextSettings::Core) &&
|
||||
!(sharedContext->m_settings.attributeFlags & ContextSettings::Core))
|
||||
if ((sharedContext.use_count() == 2) && (settings.attributeFlags & ContextSettings::Core) &&
|
||||
!(sharedContext->context->m_settings.attributeFlags & ContextSettings::Core))
|
||||
{
|
||||
// Re-create our shared context as a core context
|
||||
const ContextSettings sharedSettings(0, 0, 0, settings.majorVersion, settings.minorVersion, settings.attributeFlags);
|
||||
|
||||
sharedContext = std::make_unique<ContextType>(nullptr, sharedSettings, Vector2u(1, 1));
|
||||
sharedContext->initialize(sharedSettings);
|
||||
sharedContext->context.emplace(nullptr, sharedSettings, Vector2u(1, 1));
|
||||
sharedContext->context->initialize(sharedSettings);
|
||||
|
||||
// Reload our extensions vector
|
||||
loadExtensions();
|
||||
sharedContext->loadExtensions();
|
||||
}
|
||||
|
||||
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
|
||||
// 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);
|
||||
sharedContext->context->setActive(true);
|
||||
|
||||
// Create the context
|
||||
context = std::make_unique<ContextType>(sharedContext.get(), settings, size);
|
||||
// Create the context
|
||||
context = std::make_unique<ContextType>(&sharedContext->context.value(), settings, size);
|
||||
|
||||
sharedContext->setActive(false);
|
||||
}
|
||||
sharedContext->context->setActive(false);
|
||||
|
||||
context->initialize(settings);
|
||||
context->checkSettings(settings);
|
||||
@ -608,15 +669,27 @@ std::unique_ptr<GlContext> GlContext::create(const ContextSettings& settings, co
|
||||
////////////////////////////////////////////////////////////
|
||||
bool GlContext::isExtensionAvailable(const char* name)
|
||||
{
|
||||
using GlContextImpl::extensions;
|
||||
return std::find(extensions.begin(), extensions.end(), name) != extensions.end();
|
||||
// If this function is called before any context is available,
|
||||
// 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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
@ -625,27 +698,23 @@ GlFunctionPointer GlContext::getFunction(const char* name)
|
||||
////////////////////////////////////////////////////////////
|
||||
const GlContext* GlContext::getActiveContext()
|
||||
{
|
||||
using GlContextImpl::currentContext;
|
||||
|
||||
return currentContext.ptr;
|
||||
return GlContextImpl::CurrentContext::get().ptr;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
std::uint64_t GlContext::getActiveContextId()
|
||||
{
|
||||
using GlContextImpl::currentContext;
|
||||
|
||||
return currentContext.id;
|
||||
return GlContextImpl::CurrentContext::get().id;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
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.ptr = nullptr;
|
||||
@ -663,20 +732,27 @@ const ContextSettings& GlContext::getSettings() const
|
||||
////////////////////////////////////////////////////////////
|
||||
bool GlContext::setActive(bool active)
|
||||
{
|
||||
using GlContextImpl::currentContext;
|
||||
using GlContextImpl::mutex;
|
||||
auto& currentContext = GlContextImpl::CurrentContext::get();
|
||||
|
||||
// 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 (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
|
||||
if (makeCurrent(true))
|
||||
{
|
||||
// Set it as the new current context for this thread
|
||||
currentContext.id = m_id;
|
||||
currentContext.id = m_impl->m_id;
|
||||
currentContext.ptr = this;
|
||||
return true;
|
||||
}
|
||||
@ -693,9 +769,13 @@ bool GlContext::setActive(bool active)
|
||||
}
|
||||
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
|
||||
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
|
||||
}
|
||||
@ -765,23 +845,34 @@ int GlContext::evaluateFormat(
|
||||
////////////////////////////////////////////////////////////
|
||||
void GlContext::cleanupUnsharedResources()
|
||||
{
|
||||
using GlContextImpl::ContextDestroyCallbacks;
|
||||
using GlContextImpl::contextDestroyCallbacks;
|
||||
using GlContextImpl::currentContext;
|
||||
const auto& currentContext = GlContextImpl::CurrentContext::get();
|
||||
|
||||
// Save the current context so we can restore it later
|
||||
GlContext* contextToRestore = currentContext.ptr;
|
||||
|
||||
// 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;
|
||||
|
||||
// Make this context active so resources can be freed
|
||||
setActive(true);
|
||||
|
||||
// Call the registered destruction callbacks
|
||||
for (auto& [callback, ptr] : contextDestroyCallbacks)
|
||||
callback(ptr);
|
||||
{
|
||||
const std::lock_guard lock(Impl::getUnsharedGlObjectsMutex());
|
||||
|
||||
// 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
|
||||
if (contextToRestore)
|
||||
@ -815,7 +906,7 @@ void GlContext::initialize(const ContextSettings& requestedSettings)
|
||||
glGetIntegervFunc(GL_MAJOR_VERSION, &majorVersion);
|
||||
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.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"
|
||||
// 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) &&
|
||||
!parseVersionString(version, "OpenGL ES-CM ", m_settings.majorVersion, m_settings.minorVersion) &&
|
||||
|
@ -50,38 +50,32 @@ class GlContext
|
||||
{
|
||||
public:
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Perform resource initialization
|
||||
/// \brief Get a shared_ptr to the shared context
|
||||
///
|
||||
/// This function is called every time an OpenGL resource is
|
||||
/// created. When the first resource is initialized, it makes
|
||||
/// sure that everything is ready for contexts to work properly.
|
||||
/// \return shared_ptr to the shared context
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
static void initResource();
|
||||
static std::shared_ptr<void> getSharedContext();
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Perform resource cleanup
|
||||
///
|
||||
/// 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
|
||||
/// \brief Register an OpenGL object to be destroyed when its containing context is destroyed
|
||||
///
|
||||
/// This is used for internal purposes in order to properly
|
||||
/// clean up OpenGL resources that cannot be shared bwteen
|
||||
/// contexts.
|
||||
///
|
||||
/// \param callback Function to be called when a context is destroyed
|
||||
/// \param arg Argument to pass when calling the function
|
||||
/// \param object Object to be destroyed when its containing context is destroyed
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
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
|
||||
@ -300,6 +294,10 @@ protected:
|
||||
ContextSettings m_settings; //!< Creation settings of the context
|
||||
|
||||
private:
|
||||
struct TransientContext;
|
||||
struct SharedContext;
|
||||
struct Impl;
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Perform various initializations after the context construction
|
||||
/// \param requestedSettings Requested settings during context creation
|
||||
@ -317,7 +315,7 @@ private:
|
||||
////////////////////////////////////////////////////////////
|
||||
// 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
|
||||
|
@ -32,23 +32,22 @@
|
||||
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>
|
||||
|
||||
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_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>);
|
||||
|
Loading…
Reference in New Issue
Block a user