From 3ed141e9d80bc2174e34afafb13b5bf07c802ef0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Ol=C5=A1=C3=A1k?= Date: Wed, 1 Feb 2023 06:29:00 -0500 Subject: [PATCH] glthread: add a heuristic to stop locking global mutexes with multiple contexts Closes: https://gitlab.freedesktop.org/mesa/mesa/-/issues/4516 Closes: https://gitlab.freedesktop.org/mesa/mesa/-/issues/8035 Acked-by: Timothy Arceri Reviewed-by: Pierre-Eric Pelloux-Prayer Part-of: --- src/mesa/main/glthread.c | 75 +++++++++++++++++++++++++++++++++++----- src/mesa/main/mtypes.h | 16 +++++++++ src/mesa/main/shared.c | 2 ++ src/util/os_time.h | 2 ++ 4 files changed, 87 insertions(+), 8 deletions(-) diff --git a/src/mesa/main/glthread.c b/src/mesa/main/glthread.c index 680fc1a154c..c72a1fa0a0e 100644 --- a/src/mesa/main/glthread.c +++ b/src/mesa/main/glthread.c @@ -50,13 +50,70 @@ glthread_unmarshal_batch(void *job, void *gdata, int thread_index) unsigned pos = 0; unsigned used = batch->used; uint64_t *buffer = batch->buffer; + struct gl_shared_state *shared = ctx->Shared; + /* Determine if we should lock the global mutexes. */ + simple_mtx_lock(&shared->Mutex); + int64_t current_time = os_time_get_nano(); + + /* We can only lock the mutexes after NoLockDuration nanoseconds have + * passed since multiple contexts were active. + */ + bool lock_mutexes = shared->GLThread.LastContextSwitchTime + + shared->GLThread.NoLockDuration < current_time; + + /* Check if multiple contexts are active (the last executing context is + * different). + */ + if (ctx != shared->GLThread.LastExecutingCtx) { + if (lock_mutexes) { + /* If we get here, we've been locking the global mutexes for a while + * and now we are switching contexts. */ + if (shared->GLThread.LastContextSwitchTime + + 120 * ONE_SECOND_IN_NS < current_time) { + /* If it's been more than 2 minutes of only one active context, + * indicating that there was no other active context for a long + * time, reset the no-lock time to its initial state of only 1 + * second. This is most likely an infrequent situation of + * multi-context loading of game content and shaders. + * (this is a heuristic) + */ + shared->GLThread.NoLockDuration = ONE_SECOND_IN_NS; + } else if (shared->GLThread.NoLockDuration < 32 * ONE_SECOND_IN_NS) { + /* Double the no-lock duration if we are transitioning from only + * one active context to multiple active contexts after a short + * time, up to a maximum of 32 seconds, indicating that multiple + * contexts are frequently executing. (this is a heuristic) + */ + shared->GLThread.NoLockDuration *= 2; + } + + lock_mutexes = false; + } + + /* There are multiple active contexts. Update the last executing context + * and the last context switch time. We only start locking global mutexes + * after LastContextSwitchTime + NoLockDuration passes, so this + * effectively resets the non-locking stopwatch to 0, so that multiple + * contexts can execute simultaneously as long as they are not idle. + */ + shared->GLThread.LastExecutingCtx = ctx; + shared->GLThread.LastContextSwitchTime = current_time; + } + simple_mtx_unlock(&shared->Mutex); + + /* Execute the GL calls. */ _glapi_set_dispatch(ctx->CurrentServerDispatch); - _mesa_HashLockMutex(ctx->Shared->BufferObjects); - ctx->BufferObjectsLocked = true; - simple_mtx_lock(&ctx->Shared->TexMutex); - ctx->TexturesLocked = true; + /* Here we lock the mutexes once globally if possible. If not, we just + * fallback to the individual API calls doing it. + */ + if (lock_mutexes) { + _mesa_HashLockMutex(shared->BufferObjects); + ctx->BufferObjectsLocked = true; + simple_mtx_lock(&shared->TexMutex); + ctx->TexturesLocked = true; + } while (pos < used) { const struct marshal_cmd_base *cmd = @@ -65,10 +122,12 @@ glthread_unmarshal_batch(void *job, void *gdata, int thread_index) pos += _mesa_unmarshal_dispatch[cmd->cmd_id](ctx, cmd); } - ctx->TexturesLocked = false; - simple_mtx_unlock(&ctx->Shared->TexMutex); - ctx->BufferObjectsLocked = false; - _mesa_HashUnlockMutex(ctx->Shared->BufferObjects); + if (lock_mutexes) { + ctx->TexturesLocked = false; + simple_mtx_unlock(&shared->TexMutex); + ctx->BufferObjectsLocked = false; + _mesa_HashUnlockMutex(shared->BufferObjects); + } assert(pos == used); batch->used = 0; diff --git a/src/mesa/main/mtypes.h b/src/mesa/main/mtypes.h index 29e0fde135c..f6e818f691f 100644 --- a/src/mesa/main/mtypes.h +++ b/src/mesa/main/mtypes.h @@ -2521,6 +2521,22 @@ struct gl_shared_state struct util_idalloc free_idx; unsigned size; } small_dlist_store; + + /* Global GLThread state. */ + struct { + /* The last context that locked global mutexes. */ + struct gl_context *LastExecutingCtx; + + /* The last time LastExecutingCtx started executing after a different + * context (the time of multiple active contexts). + */ + int64_t LastContextSwitchTime; + + /* The time for which no context can lock global mutexes since + * LastContextSwitchTime. + */ + int64_t NoLockDuration; + } GLThread; }; diff --git a/src/mesa/main/shared.c b/src/mesa/main/shared.c index c4904f6eea6..186bab8c69e 100644 --- a/src/mesa/main/shared.c +++ b/src/mesa/main/shared.c @@ -141,6 +141,8 @@ _mesa_alloc_shared_state(struct gl_context *ctx) shared->MemoryObjects = _mesa_NewHashTable(); shared->SemaphoreObjects = _mesa_NewHashTable(); + shared->GLThread.NoLockDuration = ONE_SECOND_IN_NS; + return shared; } diff --git a/src/util/os_time.h b/src/util/os_time.h index 27efdb7e564..5b30bb92bb6 100644 --- a/src/util/os_time.h +++ b/src/util/os_time.h @@ -43,6 +43,8 @@ extern "C" { #endif +#define ONE_SECOND_IN_NS INT64_C(1000000000) + /* must be equal to PIPE_TIMEOUT_INFINITE */ #define OS_TIMEOUT_INFINITE 0xffffffffffffffffull