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 <tarceri@itsqueeze.com>
Reviewed-by: Pierre-Eric Pelloux-Prayer <pierre-eric.pelloux-prayer@amd.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/21039>
This commit is contained in:
Marek Olšák
2023-02-01 06:29:00 -05:00
committed by Marge Bot
parent 78c61140f1
commit 3ed141e9d8
4 changed files with 87 additions and 8 deletions

View File

@@ -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;

View File

@@ -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;
};

View File

@@ -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;
}

View File

@@ -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