[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)
{
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:

View file

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

View file

@ -20,6 +20,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#pragma once
#include <stdbool.h>
#include <time.h>
#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);
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);
}
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)