From a70858aea0e7b4801a5e5a78ec3c260a0773dfb0 Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Mon, 26 Oct 2020 18:09:45 +1100 Subject: [PATCH] [client] reworked frame timing waits for better responsiveness --- client/src/main.c | 56 ++++++-------------- common/src/platform/linux/event.c | 86 ++++++++++++++----------------- 2 files changed, 53 insertions(+), 89 deletions(-) diff --git a/client/src/main.c b/client/src/main.c index 909c5070..b09516f4 100644 --- a/client/src/main.c +++ b/client/src/main.c @@ -71,8 +71,6 @@ static LGThread *t_cursor = NULL; static LGThread *t_frame = NULL; static SDL_Cursor *cursor = NULL; -static atomic_uint a_framesPending = 0; - struct AppState state; // this structure is initialized in config.c @@ -163,19 +161,13 @@ static int renderThread(void * unused) /* signal to other threads that the renderer is ready */ lgSignalEvent(e_startup); - int resyncCheck = 0; struct timespec time; - clock_gettime(CLOCK_REALTIME, &time); - while(state.state != APP_STATE_SHUTDOWN) { if (state.frameTime > 0) { - if (++resyncCheck == 100) - { - resyncCheck = 0; - clock_gettime(CLOCK_REALTIME, &time); - } + lgWaitEventAbs(e_frame, &time); + clock_gettime(CLOCK_MONOTONIC, &time); tsAdd(&time, state.frameTime); } @@ -198,11 +190,16 @@ static int renderThread(void * unused) if (state.renderTime > 1e9) { - const float avgUPS = 1000.0f / (((float)state.renderTime / atomic_load_explicit(&state.frameCount, memory_order_acquire)) / 1e6f); - const float avgFPS = 1000.0f / (((float)state.renderTime / state.renderCount) / 1e6f); + const float avgUPS = 1000.0f / (((float)state.renderTime / + atomic_exchange_explicit(&state.frameCount, 0, memory_order_acquire)) / + 1e6f); + + const float avgFPS = 1000.0f / (((float)state.renderTime / + state.renderCount) / + 1e6f); + state.lgr->update_fps(state.lgrData, avgUPS, avgFPS); - atomic_store_explicit(&state.frameCount, 0, memory_order_release); state.renderTime = 0; state.renderCount = 0; } @@ -217,28 +214,6 @@ static int renderThread(void * unused) ); state.resizeDone = true; } - - if (state.frameTime > 0) - { - /* if there are frames pending already, don't wait on the event */ - if (atomic_load_explicit(&a_framesPending, memory_order_acquire) > 0) - if (atomic_fetch_sub_explicit(&a_framesPending, 1, memory_order_release) > 1) - continue; - - if (lgWaitEventAbs(e_frame, &time) && state.frameTime > 0) - { - /* only resync the timer if we got an early frame */ - struct timespec now, diff; - clock_gettime(CLOCK_REALTIME, &now); - tsDiff(&diff, &time, &now); - if (diff.tv_sec == 0 && diff.tv_nsec < state.frameTime) - { - resyncCheck = 0; - memcpy(&time, &now, sizeof(struct timespec)); - tsAdd(&time, state.frameTime); - } - } - } } state.state = APP_STATE_SHUTDOWN; @@ -517,8 +492,7 @@ static int frameThread(void * unused) } atomic_fetch_add_explicit(&state.frameCount, 1, memory_order_relaxed); - if (atomic_fetch_add_explicit(&a_framesPending, 1, memory_order_relaxed) == 0) - lgSignalEvent(e_frame); + lgSignalEvent(e_frame); lgmpClientMessageDone(queue); } @@ -1425,13 +1399,13 @@ static int lg_run() if (params.fpsMin == -1) { - // minimum 60fps to keep interactivity decent - state.frameTime = 1000000000ULL / 60ULL; + // default 30 fps + state.frameTime = 1000000000ULL / 30ULL; } else { - DEBUG_INFO("Using the FPS minimum from args: %d", params.fpsMin); - state.frameTime = 1000000000ULL / (unsigned long long)params.fpsMin; + DEBUG_INFO("Using the FPS minimum from args: %d", params.fpsMin); + state.frameTime = 1000000000ULL / (unsigned long long)params.fpsMin; } register_key_binds(); diff --git a/common/src/platform/linux/event.c b/common/src/platform/linux/event.c index a54c2bfc..be3ae569 100644 --- a/common/src/platform/linux/event.c +++ b/common/src/platform/linux/event.c @@ -32,7 +32,7 @@ struct LGEvent { pthread_mutex_t mutex; pthread_cond_t cond; - uint32_t flag; + atomic_uint count; bool autoReset; }; @@ -52,7 +52,18 @@ LGEvent * lgCreateEvent(bool autoReset, unsigned int msSpinTime) return NULL; } - if (pthread_cond_init(&handle->cond, NULL) != 0) + pthread_condattr_t cattr; + pthread_condattr_init(&cattr); + + if (pthread_condattr_setclock(&cattr, CLOCK_MONOTONIC) != 0) + { + DEBUG_ERROR("Failed to set the condition clock to realtime"); + pthread_mutex_destroy(&handle->mutex); + free(handle); + return NULL; + } + + if (pthread_cond_init(&handle->cond, &cattr) != 0) { pthread_mutex_destroy(&handle->mutex); free(handle); @@ -76,16 +87,17 @@ bool lgWaitEventAbs(LGEvent * handle, struct timespec * ts) { assert(handle); - if (pthread_mutex_lock(&handle->mutex) != 0) - { - DEBUG_ERROR("Failed to lock the mutex"); - return false; - } - bool ret = true; int res; - while(ret && !atomic_load(&handle->flag)) + + while(ret && atomic_load(&handle->count) == 0) { + if (pthread_mutex_lock(&handle->mutex) != 0) + { + DEBUG_ERROR("Failed to lock the mutex"); + return false; + } + if (!ts) { if ((res = pthread_cond_wait(&handle->cond, &handle->mutex)) != 0) @@ -111,16 +123,16 @@ bool lgWaitEventAbs(LGEvent * handle, struct timespec * ts) break; } } + + if (pthread_mutex_unlock(&handle->mutex) != 0) + { + DEBUG_ERROR("Failed to unlock the mutex"); + return false; + } } - if (handle->autoReset) - atomic_store(&handle->flag, 0); - - if (pthread_mutex_unlock(&handle->mutex) != 0) - { - DEBUG_ERROR("Failed to unlock the mutex"); - return false; - } + if (ret && handle->autoReset) + atomic_fetch_sub(&handle->count, 1); return ret; } @@ -131,11 +143,11 @@ bool lgWaitEventNS(LGEvent * handle, unsigned int timeout) return lgWaitEventAbs(handle, NULL); struct timespec ts; - clock_gettime(CLOCK_REALTIME, &ts); + clock_gettime(CLOCK_MONOTONIC, &ts); uint64_t nsec = ts.tv_nsec + timeout; - if(nsec > 1e9) + if(nsec > 1000000000UL) { - ts.tv_nsec = nsec - 1e9; + ts.tv_nsec = nsec - 1000000000UL; ++ts.tv_sec; } else @@ -149,26 +161,18 @@ bool lgWaitEvent(LGEvent * handle, unsigned int timeout) if (timeout == TIMEOUT_INFINITE) return lgWaitEventAbs(handle, NULL); - return lgWaitEventNS(handle, timeout * 1000000); + return lgWaitEventNS(handle, timeout * 1000000U); } bool lgSignalEvent(LGEvent * handle) { assert(handle); - if (pthread_mutex_lock(&handle->mutex) != 0) - { - DEBUG_ERROR("Failed to lock the mutex"); - return false; - } + const bool signalled = atomic_fetch_add_explicit(&handle->count, 1, + memory_order_acquire) > 0; - atomic_store(&handle->flag, 1); - - if (pthread_mutex_unlock(&handle->mutex) != 0) - { - DEBUG_ERROR("Failed to unlock the mutex"); - return false; - } + if (signalled) + return true; if (pthread_cond_broadcast(&handle->cond) != 0) { @@ -182,20 +186,6 @@ bool lgSignalEvent(LGEvent * handle) bool lgResetEvent(LGEvent * handle) { assert(handle); - - if (pthread_mutex_lock(&handle->mutex) != 0) - { - DEBUG_ERROR("Failed to lock the mutex"); - return false; - } - - atomic_store(&handle->flag, 0); - - if (pthread_mutex_unlock(&handle->mutex) != 0) - { - DEBUG_ERROR("Failed to unlock the mutex"); - return false; - } - + atomic_store(&handle->count, 0); return true; }