disabled frame skipping

This commit is contained in:
Anthony Pesch 2017-05-26 20:47:21 -04:00
parent a2cedfdd6c
commit 7f0a8a7436
2 changed files with 41 additions and 50 deletions

View File

@ -35,12 +35,6 @@
DEFINE_AGGREGATE_COUNTER(frames);
enum {
FRAME_WAITING,
FRAME_RENDERING,
FRAME_RENDERED,
};
struct emu_texture {
struct tr_texture;
struct emu *emu;
@ -75,23 +69,19 @@ struct emu {
int multi_threaded;
thread_t video_thread;
/* latest raw context from the dreamcast, ready to be converted */
/* latest context from the dreamcast, ready to be presented */
mutex_t pending_mutex;
cond_t pending_cond;
unsigned pending_id;
struct tile_context *pending_ctx;
/* latest converted context ready to be presented locally */
volatile int video_state;
struct tr_context video_rc;
struct tr_context pending_rc;
/* offscreen framebuffer the video output is rendered to */
framebuffer_handle_t video_fb;
texture_handle_t video_tex;
sync_handle_t video_sync;
/* unique id for the current video frame, used to assert synchronization
between the video and main thread */
unsigned frame;
volatile unsigned video_id;
volatile unsigned last_video_id;
/* texture cache. the dreamcast interface calls into us when new contexts are
available to be rendered. parsing the contexts, uploading their textures to
@ -224,9 +214,9 @@ static void emu_register_texture_source(struct emu *emu, union tsp tsp,
entry->dirty = 1;
}
/* mark texture source valid for the current frame */
int first_registration_this_frame = entry->frame != emu->frame;
entry->frame = emu->frame;
/* mark texture source valid for the current pending frame */
int first_registration_this_frame = entry->frame != emu->pending_id;
entry->frame = emu->pending_id;
/* set texture address */
if (!entry->texture || !entry->palette) {
@ -337,8 +327,6 @@ static void emu_render_frame(struct emu *emu) {
prof_counter_add(COUNTER_frames, 1);
emu->video_state = FRAME_RENDERING;
/* resize the framebuffer at this time if the output size has changed */
if (emu->video_resized) {
r_destroy_framebuffer(r2, emu->video_fb);
@ -352,7 +340,7 @@ static void emu_render_frame(struct emu *emu) {
framebuffer_handle_t original = r_get_framebuffer(r2);
r_bind_framebuffer(r2, emu->video_fb);
r_viewport(emu->r, emu->video_width, emu->video_height);
tr_render_context(r2, &emu->video_rc);
tr_render_context(r2, &emu->pending_rc);
r_bind_framebuffer(r2, original);
/* insert fence for main thread to synchronize on in order to ensure that
@ -361,10 +349,11 @@ static void emu_render_frame(struct emu *emu) {
emu->video_sync = r_insert_sync(r2);
}
/* mark the currently available frame for emu_run_frame */
emu->video_id = emu->pending_id;
/* update frame-based profiler stats */
prof_flip();
emu->video_state = FRAME_RENDERED;
}
static void *emu_video_thread(void *data) {
@ -389,19 +378,23 @@ static void *emu_video_thread(void *data) {
/* convert the context, uploading its textures to the render backend */
tr_convert_context(emu->r2, emu, &emu_find_texture, emu->pending_ctx,
&emu->video_rc);
&emu->pending_rc);
emu->pending_ctx = NULL;
/* after the context has been parsed, release the mutex to let
emu_finish_render complete */
mutex_unlock(emu->pending_mutex);
/* note, during this window of time (after releasing the mutex, and before
emu_render_frame ends), a frame could be processed by emu_start_render /
emu_finish_rendered that is skipped */
/* render the parsed context to an offscreen framebuffer */
emu_render_frame(emu);
/* after the context has been rendered, release the mutex to unblock
emu_guest_finish_render
note, the main purpose of this mutex is to prevent the cpu from writing
to texture memory before it's been uploaded to the render backend. from
that perspective, this mutex could be released after tr_convert_context
and before emu_render_frame, enabling the frame to take more time to
render on the host than estimated by emu_guest_finish_render. however,
then multiple framebuffers would have to be managed and synchronized in
order to allow emu_render_frame and emu_paint to run in parallel */
mutex_unlock(emu->pending_mutex);
}
return NULL;
@ -523,6 +516,9 @@ static void emu_paint(struct emu *emu) {
mp_render(emu->mp);
nk_render(emu->nk);
/* mark the last rendered video id for emu_run_frame */
emu->last_video_id = emu->video_id;
}
/*
@ -544,28 +540,24 @@ static void emu_guest_finish_render(void *userdata) {
the yet-to-be-uploaded texture memory */
mutex_lock(emu->pending_mutex);
/* if pending_ctx is non-NULL here, a frame is being skipped */
emu->pending_ctx = NULL;
mutex_unlock(emu->pending_mutex);
}
/* texture entries are only valid between each start / finish render pair,
increment frame number again to invalidate */
emu->frame++;
}
static void emu_guest_start_render(void *userdata, struct tile_context *ctx) {
struct emu *emu = userdata;
if (emu->video_state != FRAME_WAITING) {
/* skip this frame if one is already rendering / rendered */
return;
}
/* note, while the video thread is guaranteed to not to be touching texture
memory from the previous frame at this point, it could still be actually
rendering the previous frame */
/* incement internal frame number. this frame number is assigned to the
context and each texture source it registers to assert synchronization
between the emulator and video thread is working as expected */
emu->frame++;
/* incement internal frame number. this frame number is assigned to the each
texture source registered to assert synchronization between the emulator
and video thread is working as expected */
emu->pending_id++;
/* now that the video thread is sure to not be accessing the texture data,
mark any textures dirty that were invalidated by a memory watch */
@ -591,7 +583,7 @@ static void emu_guest_start_render(void *userdata, struct tile_context *ctx) {
mutex_unlock(emu->pending_mutex);
} else {
/* convert the context and immediately render it */
tr_convert_context(emu->r, emu, &emu_find_texture, ctx, &emu->video_rc);
tr_convert_context(emu->r, emu, &emu_find_texture, ctx, &emu->pending_rc);
emu_render_frame(emu);
}
@ -723,9 +715,7 @@ static void emu_host_resized(void *userdata) {
void emu_run_frame(struct emu *emu) {
static const int64_t MACHINE_STEP = HZ_TO_NANO(1000);
emu->video_state = FRAME_WAITING;
while (emu->video_state != FRAME_RENDERED) {
while (emu->video_id <= emu->last_video_id) {
dc_tick(emu->dc, MACHINE_STEP);
}

View File

@ -124,7 +124,7 @@ static void audio_shutdown(struct sdl_host *host) {
if (host->audio_dev) {
SDL_CloseAudioDevice(host->audio_dev);
}
if (host->audio_frames) {
ringbuf_destroy(host->audio_frames);
}
@ -157,7 +157,8 @@ static int audio_init(struct sdl_host *host) {
/* resume device */
SDL_PauseAudioDevice(host->audio_dev, 0);
LOG_INFO("audio backend created, %d ms latency", AUDIO_FRAMES_TO_MS(host->audio_spec.samples));
LOG_INFO("audio backend created, %d ms latency",
AUDIO_FRAMES_TO_MS(host->audio_spec.samples));
return 1;
}