# HG changeset patch # User Michael Pavone # Date 1588922574 25200 # Node ID bd70f1e15684d66cd9b636457571a75858413b75 # Parent b3c2dcae7dfc93fe6551bd047e433cede7906ad2 Make netplay remote sync to network rather than audio or video so it doesn't drift out of sync with the host diff -r b3c2dcae7dfc -r bd70f1e15684 event_log.c --- a/event_log.c Mon May 04 23:58:37 2020 -0700 +++ b/event_log.c Fri May 08 00:22:54 2020 -0700 @@ -551,7 +551,6 @@ if (Z_OK != res && Z_BUF_ERROR != res) { fatal_error("inflate returned %d in init_event_reader_tcp\n", res); } - socket_blocking(reader->socket, 0); int flag = 1; setsockopt(reader->socket, IPPROTO_TCP, TCP_NODELAY, (const char *)&flag, sizeof(flag)); } @@ -608,19 +607,14 @@ void reader_ensure_data(event_reader *reader, size_t bytes) { if (reader->buffer.size - reader->buffer.cur_pos < bytes) { - if (reader->socket) { - read_from_socket(reader); - } if (reader->input_stream.avail_in) { inflate_flush(reader); } - if (reader->socket && reader->buffer.size - reader->buffer.cur_pos < bytes) { - socket_blocking(reader->socket, 1); + if (reader->socket) { while (reader->buffer.size - reader->buffer.cur_pos < bytes) { read_from_socket(reader); inflate_flush(reader); } - socket_blocking(reader->socket, 0); } } } diff -r b3c2dcae7dfc -r bd70f1e15684 gen_player.c --- a/gen_player.c Mon May 04 23:58:37 2020 -0700 +++ b/gen_player.c Fri May 08 00:22:54 2020 -0700 @@ -24,9 +24,8 @@ //printf("Target: %d, YM bufferpos: %d, PSG bufferpos: %d\n", target, gen->ym->buffer_pos, gen->psg->buffer_pos * 2); } -void start_context(system_header *sys, char *statefile) +static void run(gen_player *player) { - gen_player *player = (gen_player *)sys; while(player->reader.socket || player->reader.buffer.cur_pos < player->reader.buffer.size) { uint32_t cycle; @@ -92,7 +91,22 @@ reader_ensure_data(&player->reader, 1); } } - +} + +static int thread_main(void *player) +{ + run(player); + return 0; +} + +void start_context(system_header *sys, char *statefile) +{ + gen_player *player = (gen_player *)sys; + if (player->reader.socket) { + render_create_thread(&player->thread, "player", thread_main, player); + } else { + run(player); + } } static void gamepad_down(system_header *system, uint8_t gamepad_num, uint8_t button) @@ -151,6 +165,7 @@ gen_player *player = calloc(1, sizeof(gen_player)); player->reader = *reader; inflateCopy(&player->reader.input_stream, &reader->input_stream); + render_set_external_sync(1); config_common(player); return player; } diff -r b3c2dcae7dfc -r bd70f1e15684 gen_player.h --- a/gen_player.h Mon May 04 23:58:37 2020 -0700 +++ b/gen_player.h Fri May 08 00:22:54 2020 -0700 @@ -1,6 +1,7 @@ #ifndef GEN_PLAYER_H_ #define GEN_PLAYER_H_ +#include "render.h" #include "system.h" #include "vdp.h" #include "psg.h" @@ -13,6 +14,7 @@ vdp_context *vdp; ym2612_context *ym; psg_context *psg; + render_thread thread; event_reader reader; } gen_player; diff -r b3c2dcae7dfc -r bd70f1e15684 render.h --- a/render.h Mon May 04 23:58:37 2020 -0700 +++ b/render.h Fri May 08 00:22:54 2020 -0700 @@ -65,6 +65,7 @@ #define RENDER_DPAD_LEFT SDL_HAT_LEFT #define RENDER_DPAD_RIGHT SDL_HAT_RIGHT #define render_relative_mouse SDL_SetRelativeMouseMode +typedef SDL_Thread* render_thread; #endif #endif @@ -93,6 +94,7 @@ typedef void (*drop_handler)(const char *filename); typedef void (*window_close_handler)(uint8_t which); typedef void (*ui_render_fun)(void); +typedef int (*render_thread_fun)(void*); uint32_t render_map_color(uint8_t r, uint8_t g, uint8_t b); void render_save_screenshot(char *path); @@ -135,6 +137,8 @@ void render_set_ui_fb_resize_handler(ui_render_fun resize); void render_video_loop(void); uint8_t render_should_release_on_exit(void); +void render_set_external_sync(uint8_t ext_sync_on); +uint8_t render_create_thread(render_thread *thread, const char *name, render_thread_fun fun, void *data); #endif //RENDER_H_ diff -r b3c2dcae7dfc -r bd70f1e15684 render_sdl.c --- a/render_sdl.c Mon May 04 23:58:37 2020 -0700 +++ b/render_sdl.c Fri May 08 00:22:54 2020 -0700 @@ -50,7 +50,14 @@ static SDL_cond *audio_ready, *frame_ready; static uint8_t quitting = 0; -static uint8_t sync_to_audio, run_on_audio_thread; +enum { + SYNC_AUDIO, + SYNC_AUDIO_THREAD, + SYNC_VIDEO, + SYNC_EXTERNAL +}; + +static uint8_t sync_src; static uint32_t min_buffered; uint32_t **frame_buffers; @@ -64,12 +71,12 @@ uint8_t render_is_audio_sync(void) { - return sync_to_audio || run_on_audio_thread; + return sync_src < SYNC_VIDEO; } uint8_t render_should_release_on_exit(void) { - return !run_on_audio_thread; + return sync_src != SYNC_AUDIO_THREAD; } void render_buffer_consumed(audio_source *src) @@ -120,7 +127,7 @@ void render_lock_audio() { - if (sync_to_audio) { + if (sync_src == SYNC_AUDIO) { SDL_LockMutex(audio_mutex); } else { SDL_LockAudio(); @@ -129,7 +136,7 @@ void render_unlock_audio() { - if (sync_to_audio) { + if (sync_src == SYNC_AUDIO) { SDL_UnlockMutex(audio_mutex); } else { SDL_UnlockAudio(); @@ -164,7 +171,7 @@ void render_audio_created(audio_source *source) { - if (sync_to_audio && SDL_GetAudioStatus() == SDL_AUDIO_PAUSED) { + if (sync_src == SYNC_AUDIO && SDL_GetAudioStatus() == SDL_AUDIO_PAUSED) { SDL_PauseAudio(0); } if (current_system) { @@ -174,7 +181,7 @@ void render_source_paused(audio_source *src, uint8_t remaining_sources) { - if (sync_to_audio) { + if (sync_src == SYNC_AUDIO) { SDL_CondSignal(audio_ready); } if (!remaining_sources) { @@ -184,14 +191,14 @@ void render_source_resumed(audio_source *src) { - if (sync_to_audio) { + if (sync_src == SYNC_AUDIO) { SDL_PauseAudio(0); } } void render_do_audio_ready(audio_source *src) { - if (run_on_audio_thread) { + if (sync_src == SYNC_AUDIO_THREAD) { int16_t *tmp = src->front; src->front = src->back; src->back = tmp; @@ -201,7 +208,7 @@ //we've emulated far enough to fill the current buffer current_system->request_exit(current_system); } - } else if (sync_to_audio) { + } else if (sync_src == SYNC_AUDIO) { SDL_LockMutex(audio_mutex); while (src->front_populated) { SDL_CondWait(src->opaque, audio_mutex); @@ -253,6 +260,15 @@ #endif } +static uint8_t external_sync; +void render_set_external_sync(uint8_t ext_sync_on) +{ + if (ext_sync_on != external_sync) { + external_sync = ext_sync_on; + render_config_updated(); + } +} + #ifndef DISABLE_OPENGL static GLuint textures[3], buffers[2], vshader, fshader, program, un_textures[2], un_width, un_height, at_pos; @@ -913,7 +929,16 @@ } debug_message("config says: %d\n", samples); desired.samples = samples*2; - desired.callback = sync_to_audio ? audio_callback : run_on_audio_thread ? audio_callback_run_on_audio : audio_callback_drc; + switch (sync_src) + { + case SYNC_AUDIO: + desired.callback = audio_callback; + break; + case SYNC_AUDIO_THREAD: + desired.callback = audio_callback_run_on_audio; + default: + desired.callback = audio_callback_drc; + } desired.userdata = NULL; if (SDL_OpenAudio(&desired, &actual) < 0) { @@ -943,12 +968,31 @@ } tern_val def = {.ptrval = "audio"}; - char *sync_src = tern_find_path_default(config, "system\0sync_source\0", def, TVAL_PTR).ptrval; - sync_to_audio = !strcmp(sync_src, "audio"); - run_on_audio_thread = !strcmp(sync_src, "audio_thread"); + if (external_sync) { + sync_src = SYNC_EXTERNAL; + } else { + char *sync_src_str = tern_find_path_default(config, "system\0sync_source\0", def, TVAL_PTR).ptrval; + if (!strcmp(sync_src_str, "audio")) { + sync_src = SYNC_AUDIO; + } else if (!strcmp(sync_src_str, "audio_thread")) { + sync_src = SYNC_AUDIO_THREAD; + } else { + sync_src = SYNC_VIDEO; + } + } + + if (!num_buffers && (sync_src == SYNC_AUDIO_THREAD || sync_src == SYNC_EXTERNAL)) { + frame_mutex = SDL_CreateMutex(); + free_buffer_mutex = SDL_CreateMutex(); + frame_ready = SDL_CreateCond(); + buffer_storage = 4; + frame_buffers = calloc(buffer_storage, sizeof(uint32_t*)); + frame_buffers[0] = texture_buf; + num_buffers = 1; + } const char *vsync; - if (sync_to_audio) { + if (sync_src == SYNC_AUDIO) { def.ptrval = "off"; vsync = tern_find_path_default(config, "video\0vsync\0", def, TVAL_PTR).ptrval; } else { @@ -1108,16 +1152,6 @@ audio_mutex = SDL_CreateMutex(); audio_ready = SDL_CreateCond(); - if (run_on_audio_thread) { - frame_mutex = SDL_CreateMutex(); - free_buffer_mutex = SDL_CreateMutex(); - frame_ready = SDL_CreateCond(); - buffer_storage = 4; - frame_buffers = calloc(buffer_storage, sizeof(uint32_t*)); - frame_buffers[0] = texture_buf; - num_buffers = 1; - } - init_audio(); uint32_t db_size; @@ -1140,8 +1174,6 @@ void render_config_updated(void) { - uint8_t old_sync_to_audio = sync_to_audio; - free_surfaces(); #ifndef DISABLE_OPENGL if (render_gl) { @@ -1335,7 +1367,7 @@ uint32_t locked_pitch; uint32_t *render_get_framebuffer(uint8_t which, int *pitch) { - if (run_on_audio_thread) { + if (sync_src == SYNC_AUDIO_THREAD || sync_src == SYNC_EXTERNAL) { *pitch = LINEBUF_SIZE * sizeof(uint32_t); uint32_t *buffer; SDL_LockMutex(free_buffer_mutex); @@ -1407,7 +1439,7 @@ static void process_framebuffer(uint32_t *buffer, uint8_t which, int width) { static uint8_t last; - if (!render_is_audio_sync() && which <= FRAMEBUFFER_EVEN && source_frame_count < 0) { + if (sync_src == SYNC_VIDEO && which <= FRAMEBUFFER_EVEN && source_frame_count < 0) { source_frame++; if (source_frame >= source_hz) { source_frame = 0; @@ -1463,7 +1495,7 @@ } } else { #endif - //TODO: Support run_on_audio_thread for render API framebuffers + //TODO: Support SYNC_AUDIO_THREAD/SYNC_EXTERNAL for render API framebuffers if (which <= FRAMEBUFFER_EVEN && last != which) { uint8_t *cur_dst = (uint8_t *)locked_pixels; uint8_t *cur_saved = (uint8_t *)texture_buf; @@ -1612,7 +1644,7 @@ void render_framebuffer_updated(uint8_t which, int width) { - if (run_on_audio_thread) { + if (sync_src == SYNC_AUDIO_THREAD || sync_src == SYNC_EXTERNAL) { SDL_LockMutex(frame_mutex); while (frame_queue_len == 4) { SDL_CondSignal(frame_ready); @@ -1650,7 +1682,7 @@ void render_video_loop(void) { - if (!run_on_audio_thread) { + if (sync_src != SYNC_AUDIO_THREAD && sync_src != SYNC_EXTERNAL) { return; } SDL_PauseAudio(0); @@ -1973,3 +2005,9 @@ } return 0xFF; } + +uint8_t render_create_thread(render_thread *thread, const char *name, render_thread_fun fun, void *data) +{ + *thread = SDL_CreateThread(fun, name, data); + return *thread != 0; +}