changeset 1967:bd70f1e15684

Make netplay remote sync to network rather than audio or video so it doesn't drift out of sync with the host
author Michael Pavone <pavone@retrodev.com>
date Fri, 08 May 2020 00:22:54 -0700
parents b3c2dcae7dfc
children c16dabdb0aad
files event_log.c gen_player.c gen_player.h render.h render_sdl.c
diffstat 5 files changed, 95 insertions(+), 42 deletions(-) [+]
line wrap: on
line diff
--- 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);
 		}
 	}
 }
--- 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;
 }
--- 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;
 
--- 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_
 
--- 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;
+}