Completely new approach for threaded video.

Allows a good compromise between jitter and avoiding audio stutter.
This commit is contained in:
Themaister 2013-08-02 22:55:48 +02:00 committed by twinaphex
parent 8dc60fc3d5
commit 51b17039d4
4 changed files with 46 additions and 21 deletions

View File

@ -77,7 +77,7 @@ static void autosave_thread(void *data)
slock_lock(save->cond_lock);
if (!save->quit)
scond_wait_timeout(save->cond, save->cond_lock, save->interval * 1000);
scond_wait_timeout(save->cond, save->cond_lock, save->interval * 1000000LL);
slock_unlock(save->cond_lock);
}
}

View File

@ -30,7 +30,6 @@ enum thread_cmd
CMD_ALIVE, // Blocking alive check. Used when paused.
CMD_SET_ROTATION,
CMD_READ_VIEWPORT,
CMD_SET_NONBLOCK,
#ifdef HAVE_OVERLAY
CMD_OVERLAY_ENABLE,
@ -88,6 +87,12 @@ typedef struct thread_video
bool alive;
bool focus;
bool nonblock;
rarch_time_t last_time;
rarch_time_t target_frame_time;
unsigned hit_count;
unsigned miss_count;
enum thread_cmd send_cmd;
enum thread_cmd reply_cmd;
@ -191,11 +196,6 @@ static void thread_loop(void *data)
thread_reply(thr, CMD_FREE);
return;
case CMD_SET_NONBLOCK:
thr->driver->set_nonblock_state(thr->driver_data, thr->cmd_data.b);
thread_reply(thr, CMD_SET_NONBLOCK);
break;
case CMD_SET_ROTATION:
thr->driver->set_rotation(thr->driver_data, thr->cmd_data.i);
thread_reply(thr, CMD_SET_ROTATION);
@ -397,6 +397,24 @@ static bool thread_frame(void *data, const void *frame_,
uint8_t *dst = thr->frame.buffer;
slock_lock(thr->lock);
if (!thr->nonblock)
{
rarch_time_t target = thr->last_time + thr->target_frame_time;
// Ideally, use absolute time, but that is only a good idea on POSIX.
while (thr->frame.updated)
{
rarch_time_t current = rarch_get_time_usec();
rarch_time_t delta = target - current;
if (delta <= 0)
break;
if (!scond_wait_timeout(thr->cond_cmd, thr->lock, delta))
break;
}
}
// Drop frame if updated flag is still set, as thread is still working on last frame.
if (!thr->frame.updated)
{
@ -418,9 +436,6 @@ static bool thread_frame(void *data, const void *frame_,
scond_signal(thr->cond_thread);
// If we are going to render menu,
// we'll want to block to avoid stepping menu
// at crazy speeds.
#if defined(HAVE_RGUI) || defined(HAVE_RMENU)
if (thr->texture.enable)
{
@ -428,20 +443,23 @@ static bool thread_frame(void *data, const void *frame_,
scond_wait(thr->cond_cmd, thr->lock);
}
#endif
thr->hit_count++;
}
else
thr->miss_count++;
slock_unlock(thr->lock);
RARCH_PERFORMANCE_STOP(thread_frame);
thr->last_time = rarch_get_time_usec();
return true;
}
static void thread_set_nonblock_state(void *data, bool state)
{
thread_video_t *thr = (thread_video_t*)data;
thr->cmd_data.b = state;
thread_send_cmd(thr, CMD_SET_NONBLOCK);
thread_wait_reply(thr, CMD_SET_NONBLOCK);
thr->nonblock = state;
}
static bool thread_init(thread_video_t *thr, const video_info_t *info, const input_driver_t **input,
@ -466,6 +484,9 @@ static bool thread_init(thread_video_t *thr, const video_info_t *info, const inp
memset(thr->frame.buffer, 0x80, max_size);
thr->target_frame_time = (rarch_time_t)roundf(1000000LL / g_settings.video.refresh_rate);
thr->last_time = rarch_get_time_usec();
thr->thread = sthread_create(thread_loop, thr);
if (!thr->thread)
return false;
@ -534,6 +555,9 @@ static void thread_free(void *data)
scond_free(thr->cond_cmd);
scond_free(thr->cond_thread);
RARCH_LOG("Threaded video stats: Frames pushed: %u, Frames dropped: %u.\n",
thr->hit_count, thr->miss_count);
free(thr);
}

View File

@ -152,12 +152,12 @@ void scond_wait(scond_t *cond, slock_t *lock)
slock_lock(lock);
}
bool scond_wait_timeout(scond_t *cond, slock_t *lock, unsigned timeout_ms)
bool scond_wait_timeout(scond_t *cond, slock_t *lock, int64_t timeout_us)
{
WaitForSingleObject(cond->event, 0);
slock_unlock(lock);
DWORD res = WaitForSingleObject(cond->event, timeout_ms);
DWORD res = WaitForSingleObject(cond->event, timeout_us / 1000);
slock_lock(lock);
return res == WAIT_OBJECT_0;
@ -289,7 +289,7 @@ void scond_wait(scond_t *cond, slock_t *lock)
}
#ifndef RARCH_CONSOLE
bool scond_wait_timeout(scond_t *cond, slock_t *lock, unsigned timeout_ms)
bool scond_wait_timeout(scond_t *cond, slock_t *lock, int64_t timeout_us)
{
struct timespec now;
@ -305,11 +305,11 @@ bool scond_wait_timeout(scond_t *cond, slock_t *lock, unsigned timeout_ms)
clock_gettime(CLOCK_REALTIME, &now);
#endif
now.tv_sec += timeout_ms / 1000;
now.tv_nsec += timeout_ms * 1000000L;
now.tv_sec += timeout_us / 1000000LL;
now.tv_nsec += timeout_us * 1000LL;
now.tv_sec += now.tv_nsec / 1000000000L;
now.tv_nsec = now.tv_nsec % 1000000000L;
now.tv_sec += now.tv_nsec / 1000000000LL;
now.tv_nsec = now.tv_nsec % 1000000000LL;
int ret = pthread_cond_timedwait(&cond->cond, &lock->lock, &now);
return ret == 0;

View File

@ -17,6 +17,7 @@
#define THREAD_H__
#include "boolean.h"
#include <stdint.h>
// Implements the bare minimum needed for RetroArch. :)
@ -43,7 +44,7 @@ void scond_free(scond_t *cond);
void scond_wait(scond_t *cond, slock_t *lock);
#ifndef RARCH_CONSOLE
bool scond_wait_timeout(scond_t *cond, slock_t *lock, unsigned timeout_ms);
bool scond_wait_timeout(scond_t *cond, slock_t *lock, int64_t timeout_us);
#endif
void scond_signal(scond_t *cond);