mirror of
https://github.com/gnif/LookingGlass.git
synced 2024-11-10 08:38:20 +00:00
[client] reworked frame timing waits for better responsiveness
This commit is contained in:
parent
e02ccd7c6f
commit
a70858aea0
@ -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
|
||||||
{
|
{
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user