[client] reworked frame timing waits for better responsiveness

This commit is contained in:
Geoffrey McRae 2020-10-26 18:09:45 +11:00
parent e02ccd7c6f
commit a70858aea0
2 changed files with 53 additions and 89 deletions

View File

@ -71,8 +71,6 @@ static LGThread *t_cursor = NULL;
static LGThread *t_frame = NULL; static LGThread *t_frame = NULL;
static SDL_Cursor *cursor = NULL; static SDL_Cursor *cursor = NULL;
static atomic_uint a_framesPending = 0;
struct AppState state; struct AppState state;
// this structure is initialized in config.c // 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 */ /* signal to other threads that the renderer is ready */
lgSignalEvent(e_startup); lgSignalEvent(e_startup);
int resyncCheck = 0;
struct timespec time; struct timespec time;
clock_gettime(CLOCK_REALTIME, &time);
while(state.state != APP_STATE_SHUTDOWN) while(state.state != APP_STATE_SHUTDOWN)
{ {
if (state.frameTime > 0) if (state.frameTime > 0)
{ {
if (++resyncCheck == 100) lgWaitEventAbs(e_frame, &time);
{ clock_gettime(CLOCK_MONOTONIC, &time);
resyncCheck = 0;
clock_gettime(CLOCK_REALTIME, &time);
}
tsAdd(&time, state.frameTime); tsAdd(&time, state.frameTime);
} }
@ -198,11 +190,16 @@ static int renderThread(void * unused)
if (state.renderTime > 1e9) if (state.renderTime > 1e9)
{ {
const float avgUPS = 1000.0f / (((float)state.renderTime / atomic_load_explicit(&state.frameCount, memory_order_acquire)) / 1e6f); const float avgUPS = 1000.0f / (((float)state.renderTime /
const float avgFPS = 1000.0f / (((float)state.renderTime / state.renderCount) / 1e6f); 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); state.lgr->update_fps(state.lgrData, avgUPS, avgFPS);
atomic_store_explicit(&state.frameCount, 0, memory_order_release);
state.renderTime = 0; state.renderTime = 0;
state.renderCount = 0; state.renderCount = 0;
} }
@ -217,28 +214,6 @@ static int renderThread(void * unused)
); );
state.resizeDone = true; 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; state.state = APP_STATE_SHUTDOWN;
@ -517,7 +492,6 @@ static int frameThread(void * unused)
} }
atomic_fetch_add_explicit(&state.frameCount, 1, memory_order_relaxed); 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); lgmpClientMessageDone(queue);
@ -1425,8 +1399,8 @@ static int lg_run()
if (params.fpsMin == -1) if (params.fpsMin == -1)
{ {
// minimum 60fps to keep interactivity decent // default 30 fps
state.frameTime = 1000000000ULL / 60ULL; state.frameTime = 1000000000ULL / 30ULL;
} }
else else
{ {

View File

@ -32,7 +32,7 @@ struct LGEvent
{ {
pthread_mutex_t mutex; pthread_mutex_t mutex;
pthread_cond_t cond; pthread_cond_t cond;
uint32_t flag; atomic_uint count;
bool autoReset; bool autoReset;
}; };
@ -52,7 +52,18 @@ LGEvent * lgCreateEvent(bool autoReset, unsigned int msSpinTime)
return NULL; 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); pthread_mutex_destroy(&handle->mutex);
free(handle); free(handle);
@ -76,16 +87,17 @@ bool lgWaitEventAbs(LGEvent * handle, struct timespec * ts)
{ {
assert(handle); assert(handle);
bool ret = true;
int res;
while(ret && atomic_load(&handle->count) == 0)
{
if (pthread_mutex_lock(&handle->mutex) != 0) if (pthread_mutex_lock(&handle->mutex) != 0)
{ {
DEBUG_ERROR("Failed to lock the mutex"); DEBUG_ERROR("Failed to lock the mutex");
return false; return false;
} }
bool ret = true;
int res;
while(ret && !atomic_load(&handle->flag))
{
if (!ts) if (!ts)
{ {
if ((res = pthread_cond_wait(&handle->cond, &handle->mutex)) != 0) if ((res = pthread_cond_wait(&handle->cond, &handle->mutex)) != 0)
@ -111,16 +123,16 @@ bool lgWaitEventAbs(LGEvent * handle, struct timespec * ts)
break; break;
} }
} }
}
if (handle->autoReset)
atomic_store(&handle->flag, 0);
if (pthread_mutex_unlock(&handle->mutex) != 0) if (pthread_mutex_unlock(&handle->mutex) != 0)
{ {
DEBUG_ERROR("Failed to unlock the mutex"); DEBUG_ERROR("Failed to unlock the mutex");
return false; return false;
} }
}
if (ret && handle->autoReset)
atomic_fetch_sub(&handle->count, 1);
return ret; return ret;
} }
@ -131,11 +143,11 @@ bool lgWaitEventNS(LGEvent * handle, unsigned int timeout)
return lgWaitEventAbs(handle, NULL); return lgWaitEventAbs(handle, NULL);
struct timespec ts; struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts); clock_gettime(CLOCK_MONOTONIC, &ts);
uint64_t nsec = ts.tv_nsec + timeout; 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; ++ts.tv_sec;
} }
else else
@ -149,26 +161,18 @@ bool lgWaitEvent(LGEvent * handle, unsigned int timeout)
if (timeout == TIMEOUT_INFINITE) if (timeout == TIMEOUT_INFINITE)
return lgWaitEventAbs(handle, NULL); return lgWaitEventAbs(handle, NULL);
return lgWaitEventNS(handle, timeout * 1000000); return lgWaitEventNS(handle, timeout * 1000000U);
} }
bool lgSignalEvent(LGEvent * handle) bool lgSignalEvent(LGEvent * handle)
{ {
assert(handle); assert(handle);
if (pthread_mutex_lock(&handle->mutex) != 0) const bool signalled = atomic_fetch_add_explicit(&handle->count, 1,
{ memory_order_acquire) > 0;
DEBUG_ERROR("Failed to lock the mutex");
return false;
}
atomic_store(&handle->flag, 1); if (signalled)
return true;
if (pthread_mutex_unlock(&handle->mutex) != 0)
{
DEBUG_ERROR("Failed to unlock the mutex");
return false;
}
if (pthread_cond_broadcast(&handle->cond) != 0) if (pthread_cond_broadcast(&handle->cond) != 0)
{ {
@ -182,20 +186,6 @@ bool lgSignalEvent(LGEvent * handle)
bool lgResetEvent(LGEvent * handle) bool lgResetEvent(LGEvent * handle)
{ {
assert(handle); assert(handle);
atomic_store(&handle->count, 0);
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;
}
return true; return true;
} }