Reduce TransientContextLock overhead if there is already a context active on the current thread.

This commit is contained in:
binary1248 2023-03-17 03:04:11 +01:00 committed by Lukas Dürrenberger
parent 05d9f2046a
commit ccda8a9db0

View File

@ -177,6 +177,7 @@ unsigned int resourceCount = 0;
// This per-thread variable holds the current context for each thread // This per-thread variable holds the current context for each thread
thread_local sf::priv::GlContext* currentContext(nullptr); thread_local sf::priv::GlContext* currentContext(nullptr);
thread_local unsigned int currentContextTransientCount(0);
// The hidden, inactive context that will be shared with all other contexts // The hidden, inactive context that will be shared with all other contexts
std::unique_ptr<ContextType> sharedContext; std::unique_ptr<ContextType> sharedContext;
@ -201,14 +202,27 @@ struct TransientContext
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
TransientContext() TransientContext()
{ {
// TransientContext should never be created if there is
// already a context active on the current thread
assert(!currentContext);
std::unique_lock lock(mutex);
if (resourceCount == 0) if (resourceCount == 0)
{ {
// No GlResources, no shared context yet
assert(!sharedContext);
// Create a Context object for temporary use
context.emplace(); context.emplace();
} }
else if (!currentContext) else
{ {
sharedContextLock.emplace(mutex); // GlResources exist, currentContext not yet set
useSharedContext = true; assert(sharedContext);
// Lock the shared context for temporary use
sharedContextLock = std::move(lock);
sharedContext->setActive(true); sharedContext->setActive(true);
} }
} }
@ -219,7 +233,7 @@ struct TransientContext
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
~TransientContext() ~TransientContext()
{ {
if (useSharedContext) if (sharedContextLock)
sharedContext->setActive(false); sharedContext->setActive(false);
} }
@ -238,10 +252,8 @@ struct TransientContext
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
// Member data // Member data
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
unsigned int referenceCount{};
std::optional<sf::Context> context; std::optional<sf::Context> context;
std::optional<std::lock_guard<std::recursive_mutex>> sharedContextLock; std::unique_lock<std::recursive_mutex> sharedContextLock;
bool useSharedContext{};
}; };
// This per-thread variable tracks if and how a transient // This per-thread variable tracks if and how a transient
@ -328,7 +340,6 @@ namespace sf::priv
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void GlContext::initResource() void GlContext::initResource()
{ {
using GlContextImpl::currentContext;
using GlContextImpl::loadExtensions; using GlContextImpl::loadExtensions;
using GlContextImpl::mutex; using GlContextImpl::mutex;
using GlContextImpl::resourceCount; using GlContextImpl::resourceCount;
@ -399,44 +410,50 @@ void GlContext::registerContextDestroyCallback(ContextDestroyCallback callback,
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void GlContext::acquireTransientContext() void GlContext::acquireTransientContext()
{ {
using GlContextImpl::mutex; using GlContextImpl::currentContext;
using GlContextImpl::currentContextTransientCount;
using GlContextImpl::TransientContext; using GlContextImpl::TransientContext;
using GlContextImpl::transientContext; using GlContextImpl::transientContext;
// Protect from concurrent access // Fast path if we already have a context active on this thread
std::lock_guard lock(mutex); if (currentContext)
{
++currentContextTransientCount;
return;
}
// If this is the first TransientContextLock on this thread // If we don't already have a context active on this thread the count should be 0
// construct the state object assert(!currentContextTransientCount);
if (!transientContext.has_value())
// If currentContext is not set, this must be the first
// TransientContextLock on this thread, construct the state object
transientContext.emplace(); transientContext.emplace();
// Increase the reference count // Make sure a context is active at this point
++transientContext->referenceCount; assert(currentContext);
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void GlContext::releaseTransientContext() void GlContext::releaseTransientContext()
{ {
using GlContextImpl::mutex; using GlContextImpl::currentContext;
using GlContextImpl::currentContextTransientCount;
using GlContextImpl::transientContext; using GlContextImpl::transientContext;
// Protect from concurrent access // Make sure a context was left active after acquireTransientContext() was called
std::lock_guard lock(mutex); assert(currentContext);
// Make sure a matching acquireTransientContext() was called // Fast path if we already had a context active on this thread before acquireTransientContext() was called
assert(transientContext.has_value()); if (currentContextTransientCount)
// Decrease the reference count
--transientContext->referenceCount;
// If this is the last TransientContextLock that is released
// destroy the state object
if (transientContext->referenceCount == 0)
{ {
transientContext.reset(); --currentContextTransientCount;
return;
} }
// If currentContext is set and currentContextTransientCount is 0,
// this is the last TransientContextLock that is released, destroy the state object
transientContext.reset();
} }