[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.
This commit is contained in:
Geoffrey McRae 2020-05-21 13:41:59 +10:00
parent 756b57400b
commit e31f38eadc
5 changed files with 72 additions and 33 deletions

View File

@ -1 +1 @@
B1-205-g01bfd2e090+1 B1-206-g756b57400b+1

View File

@ -421,7 +421,7 @@ enum EGL_TexStatus egl_texture_bind(EGL_Texture * texture)
if (texture->tex[s.s].sync != 0) 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_ALREADY_SIGNALED:
case GL_CONDITION_SATISFIED: case GL_CONDITION_SATISFIED:

View File

@ -62,6 +62,7 @@ static int renderThread(void * unused);
static int frameThread (void * unused); static int frameThread (void * unused);
static LGEvent *e_startup = NULL; static LGEvent *e_startup = NULL;
static LGEvent *e_frame = NULL;
static LGThread *t_spice = NULL; static LGThread *t_spice = NULL;
static LGThread *t_render = NULL; static LGThread *t_render = NULL;
static LGThread *t_cursor = NULL; static LGThread *t_cursor = NULL;
@ -149,7 +150,7 @@ static int renderThread(void * unused)
unsigned int resyncCheck = 0; unsigned int resyncCheck = 0;
struct timespec time; struct timespec time;
clock_gettime(CLOCK_MONOTONIC, &time); clock_gettime(CLOCK_REALTIME, &time);
while(state.running) while(state.running)
{ {
@ -161,7 +162,7 @@ static int renderThread(void * unused)
resyncCheck = 0; resyncCheck = 0;
struct timespec tmp; struct timespec tmp;
clock_gettime(CLOCK_MONOTONIC, &tmp); clock_gettime(CLOCK_REALTIME, &tmp);
if (tmp.tv_nsec - time.tv_nsec < 0) if (tmp.tv_nsec - time.tv_nsec < 0)
{ {
tmp.tv_sec -= time.tv_sec - 1; tmp.tv_sec -= time.tv_sec - 1;
@ -172,12 +173,6 @@ static int renderThread(void * unused)
tmp.tv_sec -= time.tv_sec; tmp.tv_sec -= time.tv_sec;
tmp.tv_nsec -= time.tv_nsec; 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, if (state.lgr->render_begin && !state.lgr->render_begin(state.lgrData,
@ -227,19 +222,16 @@ static int renderThread(void * unused)
state.resizeDone = true; 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; time.tv_nsec = nsec - 1e9;
if (nsec > 1e9) ++time.tv_sec;
{
time.tv_nsec = nsec - 1e9;
++time.tv_sec;
}
else
time.tv_nsec = nsec;
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &time, NULL);
} }
else
time.tv_nsec = nsec;
lgWaitEventAbs(e_frame, &time);
} }
state.running = false; state.running = false;
@ -474,6 +466,7 @@ static int frameThread(void * unused)
} }
lgmpClientMessageDone(queue); lgmpClientMessageDone(queue);
++state.frameCount; ++state.frameCount;
lgSignalEvent(e_frame);
} }
lgmpClientUnsubscribe(&queue); lgmpClientUnsubscribe(&queue);
@ -1443,6 +1436,13 @@ static int lg_run()
return -1; 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 // start the renderThread so we don't just display junk
if (!lgCreateThread("renderThread", renderThread, NULL, &t_render)) if (!lgCreateThread("renderThread", renderThread, NULL, &t_render))
{ {
@ -1555,11 +1555,18 @@ static void lg_shutdown()
if (t_render) if (t_render)
{ {
lgSignalEvent(e_startup); lgSignalEvent(e_startup);
lgSignalEvent(e_frame);
lgJoinThread(t_render, NULL); lgJoinThread(t_render, NULL);
} }
lgmpClientFree(&state.lgmp); lgmpClientFree(&state.lgmp);
if (e_frame)
{
lgFreeEvent(e_frame);
e_frame = NULL;
}
if (e_startup) if (e_startup)
{ {
lgFreeEvent(e_startup); lgFreeEvent(e_startup);

View File

@ -20,6 +20,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#pragma once #pragma once
#include <stdbool.h> #include <stdbool.h>
#include <time.h>
#define TIMEOUT_INFINITE ((unsigned int)~0) #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 // os specific method to wrap/convert a native event into a LGEvent
// for windows this is an event HANDLE // for windows this is an event HANDLE
LGEvent * lgWrapEvent(void * handle); 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);

View File

@ -72,7 +72,7 @@ void lgFreeEvent(LGEvent * handle)
free(handle); free(handle);
} }
bool lgWaitEvent(LGEvent * handle, unsigned int timeout) bool lgWaitEventAbs(LGEvent * handle, struct timespec * ts)
{ {
assert(handle); assert(handle);
@ -82,29 +82,32 @@ bool lgWaitEvent(LGEvent * handle, unsigned int timeout)
return false; 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) if (pthread_cond_wait(&handle->cond, &handle->mutex) != 0)
{ {
DEBUG_ERROR("Wait to wait on the condition"); DEBUG_ERROR("Wait to wait on the condition");
return false; ret = false;
} }
} }
else else
{ {
struct timespec ts; switch(pthread_cond_timedwait(&handle->cond, &handle->mutex, ts))
ts.tv_sec = timeout / 1000;
ts.tv_nsec = (timeout % 1000) * 1000000;
switch(pthread_cond_timedwait(&handle->cond, &handle->mutex, &ts))
{ {
case 0:
break;
case ETIMEDOUT: case ETIMEDOUT:
return false; ret = false;
break;
default: default:
ret = false;
DEBUG_ERROR("Timed wait failed"); DEBUG_ERROR("Timed wait failed");
return false; break;
} }
} }
} }
@ -118,7 +121,31 @@ bool lgWaitEvent(LGEvent * handle, unsigned int timeout)
return false; 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) bool lgSignalEvent(LGEvent * handle)