From e31f38eadc0259d78d45e27c423d7f29059813c0 Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Thu, 21 May 2020 13:41:59 +1000 Subject: [PATCH] [client] allow frame updates to be triggered by a timed event This is a major change to how the LG client performs it's updates. In the past LG would operate a fixed FPS regardless of incoming update speed and/or frequency. This change allows LG to dynamically increase it's FPS in order to better sync with the guest as it's rate changes. --- VERSION | 2 +- client/renderers/EGL/texture.c | 2 +- client/src/main.c | 45 ++++++++++++++++------------ common/include/common/event.h | 7 ++++- common/src/platform/linux/event.c | 49 ++++++++++++++++++++++++------- 5 files changed, 72 insertions(+), 33 deletions(-) diff --git a/VERSION b/VERSION index 097c6efc..d537cbe4 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -B1-205-g01bfd2e090+1 \ No newline at end of file +B1-206-g756b57400b+1 \ No newline at end of file diff --git a/client/renderers/EGL/texture.c b/client/renderers/EGL/texture.c index 151a353b..ff4cbfec 100644 --- a/client/renderers/EGL/texture.c +++ b/client/renderers/EGL/texture.c @@ -421,7 +421,7 @@ enum EGL_TexStatus egl_texture_bind(EGL_Texture * texture) if (texture->tex[s.s].sync != 0) { - switch(glClientWaitSync(texture->tex[s.s].sync, 0, 0)) + switch(glClientWaitSync(texture->tex[s.s].sync, 0, 20000000)) { case GL_ALREADY_SIGNALED: case GL_CONDITION_SATISFIED: diff --git a/client/src/main.c b/client/src/main.c index 349d5a7b..60d1a46f 100644 --- a/client/src/main.c +++ b/client/src/main.c @@ -62,6 +62,7 @@ static int renderThread(void * unused); static int frameThread (void * unused); static LGEvent *e_startup = NULL; +static LGEvent *e_frame = NULL; static LGThread *t_spice = NULL; static LGThread *t_render = NULL; static LGThread *t_cursor = NULL; @@ -149,7 +150,7 @@ static int renderThread(void * unused) unsigned int resyncCheck = 0; struct timespec time; - clock_gettime(CLOCK_MONOTONIC, &time); + clock_gettime(CLOCK_REALTIME, &time); while(state.running) { @@ -161,7 +162,7 @@ static int renderThread(void * unused) resyncCheck = 0; struct timespec tmp; - clock_gettime(CLOCK_MONOTONIC, &tmp); + clock_gettime(CLOCK_REALTIME, &tmp); if (tmp.tv_nsec - time.tv_nsec < 0) { tmp.tv_sec -= time.tv_sec - 1; @@ -172,12 +173,6 @@ static int renderThread(void * unused) tmp.tv_sec -= time.tv_sec; tmp.tv_nsec -= time.tv_nsec; } - const unsigned long diff = tmp.tv_sec * 1000000000 + tmp.tv_nsec; - if (diff > state.frameTime) - { - DEBUG_INFO("Timer drift detected, %lu is > %lu", diff, state.frameTime); - clock_gettime(CLOCK_MONOTONIC, &time); - } } if (state.lgr->render_begin && !state.lgr->render_begin(state.lgrData, @@ -227,19 +222,16 @@ static int renderThread(void * unused) state.resizeDone = true; } - if (state.frameTime > 0) + uint64_t nsec = time.tv_nsec + state.frameTime; + if(nsec > 1e9) { - uint64_t nsec = time.tv_nsec + state.frameTime; - if (nsec > 1e9) - { - time.tv_nsec = nsec - 1e9; - ++time.tv_sec; - } - else - time.tv_nsec = nsec; - - clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &time, NULL); + time.tv_nsec = nsec - 1e9; + ++time.tv_sec; } + else + time.tv_nsec = nsec; + + lgWaitEventAbs(e_frame, &time); } state.running = false; @@ -474,6 +466,7 @@ static int frameThread(void * unused) } lgmpClientMessageDone(queue); ++state.frameCount; + lgSignalEvent(e_frame); } lgmpClientUnsubscribe(&queue); @@ -1443,6 +1436,13 @@ static int lg_run() return -1; } + // setup the new frame event + if (!(e_frame = lgCreateEvent(true, 0))) + { + DEBUG_ERROR("failed to create the frame event"); + return -1; + } + // start the renderThread so we don't just display junk if (!lgCreateThread("renderThread", renderThread, NULL, &t_render)) { @@ -1555,11 +1555,18 @@ static void lg_shutdown() if (t_render) { lgSignalEvent(e_startup); + lgSignalEvent(e_frame); lgJoinThread(t_render, NULL); } lgmpClientFree(&state.lgmp); + if (e_frame) + { + lgFreeEvent(e_frame); + e_frame = NULL; + } + if (e_startup) { lgFreeEvent(e_startup); diff --git a/common/include/common/event.h b/common/include/common/event.h index 072a24c5..2d74476f 100644 --- a/common/include/common/event.h +++ b/common/include/common/event.h @@ -20,6 +20,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA #pragma once #include +#include #define TIMEOUT_INFINITE ((unsigned int)~0) @@ -34,4 +35,8 @@ bool lgResetEvent (LGEvent * handle); // os specific method to wrap/convert a native event into a LGEvent // for windows this is an event HANDLE -LGEvent * lgWrapEvent(void * handle); \ No newline at end of file +LGEvent * lgWrapEvent(void * handle); + +// Posix specific, not implmented/possible in windows +bool lgWaitEventAbs(LGEvent * handle, struct timespec * ts); +bool lgWaitEventNS (LGEvent * handle, unsigned int timeout); diff --git a/common/src/platform/linux/event.c b/common/src/platform/linux/event.c index 44e3ee59..4495bf4a 100644 --- a/common/src/platform/linux/event.c +++ b/common/src/platform/linux/event.c @@ -72,7 +72,7 @@ void lgFreeEvent(LGEvent * handle) free(handle); } -bool lgWaitEvent(LGEvent * handle, unsigned int timeout) +bool lgWaitEventAbs(LGEvent * handle, struct timespec * ts) { assert(handle); @@ -82,29 +82,32 @@ bool lgWaitEvent(LGEvent * handle, unsigned int timeout) return false; } - while(!atomic_load(&handle->flag)) + bool ret = true; + while(ret && !atomic_load(&handle->flag)) { - if (timeout == TIMEOUT_INFINITE) + if (!ts) { if (pthread_cond_wait(&handle->cond, &handle->mutex) != 0) { DEBUG_ERROR("Wait to wait on the condition"); - return false; + ret = false; } } else { - struct timespec ts; - ts.tv_sec = timeout / 1000; - ts.tv_nsec = (timeout % 1000) * 1000000; - switch(pthread_cond_timedwait(&handle->cond, &handle->mutex, &ts)) + switch(pthread_cond_timedwait(&handle->cond, &handle->mutex, ts)) { + case 0: + break; + case ETIMEDOUT: - return false; + ret = false; + break; default: + ret = false; DEBUG_ERROR("Timed wait failed"); - return false; + break; } } } @@ -118,7 +121,31 @@ bool lgWaitEvent(LGEvent * handle, unsigned int timeout) return false; } - return true; + return ret; +} + +bool lgWaitEventNS(LGEvent * handle, unsigned int timeout) +{ + if (timeout == TIMEOUT_INFINITE) + return lgWaitEventAbs(handle, NULL); + + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + uint64_t nsec = ts.tv_nsec + timeout; + if(nsec > 1e9) + { + ts.tv_nsec = nsec - 1e9; + ++ts.tv_sec; + } + else + ts.tv_nsec = nsec; + + return lgWaitEventAbs(handle, &ts); +} + +bool lgWaitEvent(LGEvent * handle, unsigned int timeout) +{ + return lgWaitEventNS(handle, timeout * 1000000); } bool lgSignalEvent(LGEvent * handle)