Permalink
Browse files

Completely new approach for threaded video.

Allows a good compromise between jitter and avoiding audio stutter.
  • Loading branch information...
1 parent 8dc60fc commit 51b17039d40d2c8ad5ed3e5cfceb8435c714db79 @Themaister committed with twinaphex Aug 2, 2013
Showing with 46 additions and 21 deletions.
  1. +1 −1 autosave.c
  2. +36 −12 gfx/thread_wrapper.c
  3. +7 −7 thread.c
  4. +2 −1 thread.h
View
2 autosave.c
@@ -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
48 gfx/thread_wrapper.c
@@ -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,30 +436,30 @@ 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)
{
while (thr->frame.updated)
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
14 thread.c
@@ -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
3 thread.h
@@ -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);

0 comments on commit 51b1703

Please sign in to comment.