From 7f0a8a7436a893618a68de4b2ee7a71b94453cc2 Mon Sep 17 00:00:00 2001 From: Anthony Pesch Date: Fri, 26 May 2017 20:47:21 -0400 Subject: [PATCH] disabled frame skipping --- src/emulator.c | 86 ++++++++++++++++++++++---------------------------- src/sdl_host.c | 5 +-- 2 files changed, 41 insertions(+), 50 deletions(-) diff --git a/src/emulator.c b/src/emulator.c index 3522732b..a8ecfcd5 100644 --- a/src/emulator.c +++ b/src/emulator.c @@ -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); } diff --git a/src/sdl_host.c b/src/sdl_host.c index 0824e701..e760430d 100644 --- a/src/sdl_host.c +++ b/src/sdl_host.c @@ -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; }