Completely new approach for threaded video.
Allows a good compromise between jitter and avoiding audio stutter.
This commit is contained in:
parent
8dc60fc3d5
commit
51b17039d4
|
@ -77,7 +77,7 @@ static void autosave_thread(void *data)
|
||||||
|
|
||||||
slock_lock(save->cond_lock);
|
slock_lock(save->cond_lock);
|
||||||
if (!save->quit)
|
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);
|
slock_unlock(save->cond_lock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,6 @@ enum thread_cmd
|
||||||
CMD_ALIVE, // Blocking alive check. Used when paused.
|
CMD_ALIVE, // Blocking alive check. Used when paused.
|
||||||
CMD_SET_ROTATION,
|
CMD_SET_ROTATION,
|
||||||
CMD_READ_VIEWPORT,
|
CMD_READ_VIEWPORT,
|
||||||
CMD_SET_NONBLOCK,
|
|
||||||
|
|
||||||
#ifdef HAVE_OVERLAY
|
#ifdef HAVE_OVERLAY
|
||||||
CMD_OVERLAY_ENABLE,
|
CMD_OVERLAY_ENABLE,
|
||||||
|
@ -88,6 +87,12 @@ typedef struct thread_video
|
||||||
|
|
||||||
bool alive;
|
bool alive;
|
||||||
bool focus;
|
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 send_cmd;
|
||||||
enum thread_cmd reply_cmd;
|
enum thread_cmd reply_cmd;
|
||||||
|
@ -191,11 +196,6 @@ static void thread_loop(void *data)
|
||||||
thread_reply(thr, CMD_FREE);
|
thread_reply(thr, CMD_FREE);
|
||||||
return;
|
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:
|
case CMD_SET_ROTATION:
|
||||||
thr->driver->set_rotation(thr->driver_data, thr->cmd_data.i);
|
thr->driver->set_rotation(thr->driver_data, thr->cmd_data.i);
|
||||||
thread_reply(thr, CMD_SET_ROTATION);
|
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;
|
uint8_t *dst = thr->frame.buffer;
|
||||||
|
|
||||||
slock_lock(thr->lock);
|
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.
|
// Drop frame if updated flag is still set, as thread is still working on last frame.
|
||||||
if (!thr->frame.updated)
|
if (!thr->frame.updated)
|
||||||
{
|
{
|
||||||
|
@ -418,9 +436,6 @@ static bool thread_frame(void *data, const void *frame_,
|
||||||
|
|
||||||
scond_signal(thr->cond_thread);
|
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 defined(HAVE_RGUI) || defined(HAVE_RMENU)
|
||||||
if (thr->texture.enable)
|
if (thr->texture.enable)
|
||||||
{
|
{
|
||||||
|
@ -428,20 +443,23 @@ static bool thread_frame(void *data, const void *frame_,
|
||||||
scond_wait(thr->cond_cmd, thr->lock);
|
scond_wait(thr->cond_cmd, thr->lock);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
thr->hit_count++;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
thr->miss_count++;
|
||||||
|
|
||||||
slock_unlock(thr->lock);
|
slock_unlock(thr->lock);
|
||||||
|
|
||||||
RARCH_PERFORMANCE_STOP(thread_frame);
|
RARCH_PERFORMANCE_STOP(thread_frame);
|
||||||
|
|
||||||
|
thr->last_time = rarch_get_time_usec();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void thread_set_nonblock_state(void *data, bool state)
|
static void thread_set_nonblock_state(void *data, bool state)
|
||||||
{
|
{
|
||||||
thread_video_t *thr = (thread_video_t*)data;
|
thread_video_t *thr = (thread_video_t*)data;
|
||||||
thr->cmd_data.b = state;
|
thr->nonblock = state;
|
||||||
thread_send_cmd(thr, CMD_SET_NONBLOCK);
|
|
||||||
thread_wait_reply(thr, CMD_SET_NONBLOCK);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool thread_init(thread_video_t *thr, const video_info_t *info, const input_driver_t **input,
|
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);
|
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);
|
thr->thread = sthread_create(thread_loop, thr);
|
||||||
if (!thr->thread)
|
if (!thr->thread)
|
||||||
return false;
|
return false;
|
||||||
|
@ -534,6 +555,9 @@ static void thread_free(void *data)
|
||||||
scond_free(thr->cond_cmd);
|
scond_free(thr->cond_cmd);
|
||||||
scond_free(thr->cond_thread);
|
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);
|
free(thr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
14
thread.c
14
thread.c
|
@ -152,12 +152,12 @@ void scond_wait(scond_t *cond, slock_t *lock)
|
||||||
slock_lock(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);
|
WaitForSingleObject(cond->event, 0);
|
||||||
slock_unlock(lock);
|
slock_unlock(lock);
|
||||||
|
|
||||||
DWORD res = WaitForSingleObject(cond->event, timeout_ms);
|
DWORD res = WaitForSingleObject(cond->event, timeout_us / 1000);
|
||||||
|
|
||||||
slock_lock(lock);
|
slock_lock(lock);
|
||||||
return res == WAIT_OBJECT_0;
|
return res == WAIT_OBJECT_0;
|
||||||
|
@ -289,7 +289,7 @@ void scond_wait(scond_t *cond, slock_t *lock)
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef RARCH_CONSOLE
|
#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;
|
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);
|
clock_gettime(CLOCK_REALTIME, &now);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
now.tv_sec += timeout_ms / 1000;
|
now.tv_sec += timeout_us / 1000000LL;
|
||||||
now.tv_nsec += timeout_ms * 1000000L;
|
now.tv_nsec += timeout_us * 1000LL;
|
||||||
|
|
||||||
now.tv_sec += now.tv_nsec / 1000000000L;
|
now.tv_sec += now.tv_nsec / 1000000000LL;
|
||||||
now.tv_nsec = now.tv_nsec % 1000000000L;
|
now.tv_nsec = now.tv_nsec % 1000000000LL;
|
||||||
|
|
||||||
int ret = pthread_cond_timedwait(&cond->cond, &lock->lock, &now);
|
int ret = pthread_cond_timedwait(&cond->cond, &lock->lock, &now);
|
||||||
return ret == 0;
|
return ret == 0;
|
||||||
|
|
3
thread.h
3
thread.h
|
@ -17,6 +17,7 @@
|
||||||
#define THREAD_H__
|
#define THREAD_H__
|
||||||
|
|
||||||
#include "boolean.h"
|
#include "boolean.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
// Implements the bare minimum needed for RetroArch. :)
|
// 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);
|
void scond_wait(scond_t *cond, slock_t *lock);
|
||||||
#ifndef RARCH_CONSOLE
|
#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
|
#endif
|
||||||
void scond_signal(scond_t *cond);
|
void scond_signal(scond_t *cond);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue