comparison render_sdl.c @ 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 42c12d141f6e
children 3701517d852c
comparison
equal deleted inserted replaced
1966:b3c2dcae7dfc 1967:bd70f1e15684
48 48
49 static SDL_mutex *audio_mutex, *frame_mutex, *free_buffer_mutex; 49 static SDL_mutex *audio_mutex, *frame_mutex, *free_buffer_mutex;
50 static SDL_cond *audio_ready, *frame_ready; 50 static SDL_cond *audio_ready, *frame_ready;
51 static uint8_t quitting = 0; 51 static uint8_t quitting = 0;
52 52
53 static uint8_t sync_to_audio, run_on_audio_thread; 53 enum {
54 SYNC_AUDIO,
55 SYNC_AUDIO_THREAD,
56 SYNC_VIDEO,
57 SYNC_EXTERNAL
58 };
59
60 static uint8_t sync_src;
54 static uint32_t min_buffered; 61 static uint32_t min_buffered;
55 62
56 uint32_t **frame_buffers; 63 uint32_t **frame_buffers;
57 uint32_t num_buffers; 64 uint32_t num_buffers;
58 uint32_t buffer_storage; 65 uint32_t buffer_storage;
62 return min_buffered; 69 return min_buffered;
63 } 70 }
64 71
65 uint8_t render_is_audio_sync(void) 72 uint8_t render_is_audio_sync(void)
66 { 73 {
67 return sync_to_audio || run_on_audio_thread; 74 return sync_src < SYNC_VIDEO;
68 } 75 }
69 76
70 uint8_t render_should_release_on_exit(void) 77 uint8_t render_should_release_on_exit(void)
71 { 78 {
72 return !run_on_audio_thread; 79 return sync_src != SYNC_AUDIO_THREAD;
73 } 80 }
74 81
75 void render_buffer_consumed(audio_source *src) 82 void render_buffer_consumed(audio_source *src)
76 { 83 {
77 SDL_CondSignal(src->opaque); 84 SDL_CondSignal(src->opaque);
118 mix_and_convert(byte_stream, len, NULL); 125 mix_and_convert(byte_stream, len, NULL);
119 } 126 }
120 127
121 void render_lock_audio() 128 void render_lock_audio()
122 { 129 {
123 if (sync_to_audio) { 130 if (sync_src == SYNC_AUDIO) {
124 SDL_LockMutex(audio_mutex); 131 SDL_LockMutex(audio_mutex);
125 } else { 132 } else {
126 SDL_LockAudio(); 133 SDL_LockAudio();
127 } 134 }
128 } 135 }
129 136
130 void render_unlock_audio() 137 void render_unlock_audio()
131 { 138 {
132 if (sync_to_audio) { 139 if (sync_src == SYNC_AUDIO) {
133 SDL_UnlockMutex(audio_mutex); 140 SDL_UnlockMutex(audio_mutex);
134 } else { 141 } else {
135 SDL_UnlockAudio(); 142 SDL_UnlockAudio();
136 } 143 }
137 } 144 }
162 SDL_DestroyCond(opaque); 169 SDL_DestroyCond(opaque);
163 } 170 }
164 171
165 void render_audio_created(audio_source *source) 172 void render_audio_created(audio_source *source)
166 { 173 {
167 if (sync_to_audio && SDL_GetAudioStatus() == SDL_AUDIO_PAUSED) { 174 if (sync_src == SYNC_AUDIO && SDL_GetAudioStatus() == SDL_AUDIO_PAUSED) {
168 SDL_PauseAudio(0); 175 SDL_PauseAudio(0);
169 } 176 }
170 if (current_system) { 177 if (current_system) {
171 current_system->request_exit(current_system); 178 current_system->request_exit(current_system);
172 } 179 }
173 } 180 }
174 181
175 void render_source_paused(audio_source *src, uint8_t remaining_sources) 182 void render_source_paused(audio_source *src, uint8_t remaining_sources)
176 { 183 {
177 if (sync_to_audio) { 184 if (sync_src == SYNC_AUDIO) {
178 SDL_CondSignal(audio_ready); 185 SDL_CondSignal(audio_ready);
179 } 186 }
180 if (!remaining_sources) { 187 if (!remaining_sources) {
181 SDL_PauseAudio(0); 188 SDL_PauseAudio(0);
182 } 189 }
183 } 190 }
184 191
185 void render_source_resumed(audio_source *src) 192 void render_source_resumed(audio_source *src)
186 { 193 {
187 if (sync_to_audio) { 194 if (sync_src == SYNC_AUDIO) {
188 SDL_PauseAudio(0); 195 SDL_PauseAudio(0);
189 } 196 }
190 } 197 }
191 198
192 void render_do_audio_ready(audio_source *src) 199 void render_do_audio_ready(audio_source *src)
193 { 200 {
194 if (run_on_audio_thread) { 201 if (sync_src == SYNC_AUDIO_THREAD) {
195 int16_t *tmp = src->front; 202 int16_t *tmp = src->front;
196 src->front = src->back; 203 src->front = src->back;
197 src->back = tmp; 204 src->back = tmp;
198 src->front_populated = 1; 205 src->front_populated = 1;
199 src->buffer_pos = 0; 206 src->buffer_pos = 0;
200 if (all_sources_ready()) { 207 if (all_sources_ready()) {
201 //we've emulated far enough to fill the current buffer 208 //we've emulated far enough to fill the current buffer
202 current_system->request_exit(current_system); 209 current_system->request_exit(current_system);
203 } 210 }
204 } else if (sync_to_audio) { 211 } else if (sync_src == SYNC_AUDIO) {
205 SDL_LockMutex(audio_mutex); 212 SDL_LockMutex(audio_mutex);
206 while (src->front_populated) { 213 while (src->front_populated) {
207 SDL_CondWait(src->opaque, audio_mutex); 214 SDL_CondWait(src->opaque, audio_mutex);
208 } 215 }
209 int16_t *tmp = src->front; 216 int16_t *tmp = src->front;
249 #ifdef USE_GLES 256 #ifdef USE_GLES
250 return 255 << 24 | b << 16 | g << 8 | r; 257 return 255 << 24 | b << 16 | g << 8 | r;
251 #else 258 #else
252 return 255 << 24 | r << 16 | g << 8 | b; 259 return 255 << 24 | r << 16 | g << 8 | b;
253 #endif 260 #endif
261 }
262
263 static uint8_t external_sync;
264 void render_set_external_sync(uint8_t ext_sync_on)
265 {
266 if (ext_sync_on != external_sync) {
267 external_sync = ext_sync_on;
268 render_config_updated();
269 }
254 } 270 }
255 271
256 #ifndef DISABLE_OPENGL 272 #ifndef DISABLE_OPENGL
257 static GLuint textures[3], buffers[2], vshader, fshader, program, un_textures[2], un_width, un_height, at_pos; 273 static GLuint textures[3], buffers[2], vshader, fshader, program, un_textures[2], un_width, un_height, at_pos;
258 274
911 if (!samples) { 927 if (!samples) {
912 samples = 512; 928 samples = 512;
913 } 929 }
914 debug_message("config says: %d\n", samples); 930 debug_message("config says: %d\n", samples);
915 desired.samples = samples*2; 931 desired.samples = samples*2;
916 desired.callback = sync_to_audio ? audio_callback : run_on_audio_thread ? audio_callback_run_on_audio : audio_callback_drc; 932 switch (sync_src)
933 {
934 case SYNC_AUDIO:
935 desired.callback = audio_callback;
936 break;
937 case SYNC_AUDIO_THREAD:
938 desired.callback = audio_callback_run_on_audio;
939 default:
940 desired.callback = audio_callback_drc;
941 }
917 desired.userdata = NULL; 942 desired.userdata = NULL;
918 943
919 if (SDL_OpenAudio(&desired, &actual) < 0) { 944 if (SDL_OpenAudio(&desired, &actual) < 0) {
920 fatal_error("Unable to open SDL audio: %s\n", SDL_GetError()); 945 fatal_error("Unable to open SDL audio: %s\n", SDL_GetError());
921 } 946 }
941 if (is_fullscreen) { 966 if (is_fullscreen) {
942 flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; 967 flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
943 } 968 }
944 969
945 tern_val def = {.ptrval = "audio"}; 970 tern_val def = {.ptrval = "audio"};
946 char *sync_src = tern_find_path_default(config, "system\0sync_source\0", def, TVAL_PTR).ptrval; 971 if (external_sync) {
947 sync_to_audio = !strcmp(sync_src, "audio"); 972 sync_src = SYNC_EXTERNAL;
948 run_on_audio_thread = !strcmp(sync_src, "audio_thread"); 973 } else {
974 char *sync_src_str = tern_find_path_default(config, "system\0sync_source\0", def, TVAL_PTR).ptrval;
975 if (!strcmp(sync_src_str, "audio")) {
976 sync_src = SYNC_AUDIO;
977 } else if (!strcmp(sync_src_str, "audio_thread")) {
978 sync_src = SYNC_AUDIO_THREAD;
979 } else {
980 sync_src = SYNC_VIDEO;
981 }
982 }
983
984 if (!num_buffers && (sync_src == SYNC_AUDIO_THREAD || sync_src == SYNC_EXTERNAL)) {
985 frame_mutex = SDL_CreateMutex();
986 free_buffer_mutex = SDL_CreateMutex();
987 frame_ready = SDL_CreateCond();
988 buffer_storage = 4;
989 frame_buffers = calloc(buffer_storage, sizeof(uint32_t*));
990 frame_buffers[0] = texture_buf;
991 num_buffers = 1;
992 }
949 993
950 const char *vsync; 994 const char *vsync;
951 if (sync_to_audio) { 995 if (sync_src == SYNC_AUDIO) {
952 def.ptrval = "off"; 996 def.ptrval = "off";
953 vsync = tern_find_path_default(config, "video\0vsync\0", def, TVAL_PTR).ptrval; 997 vsync = tern_find_path_default(config, "video\0vsync\0", def, TVAL_PTR).ptrval;
954 } else { 998 } else {
955 vsync = "on"; 999 vsync = "on";
956 } 1000 }
1106 window_setup(); 1150 window_setup();
1107 1151
1108 audio_mutex = SDL_CreateMutex(); 1152 audio_mutex = SDL_CreateMutex();
1109 audio_ready = SDL_CreateCond(); 1153 audio_ready = SDL_CreateCond();
1110 1154
1111 if (run_on_audio_thread) {
1112 frame_mutex = SDL_CreateMutex();
1113 free_buffer_mutex = SDL_CreateMutex();
1114 frame_ready = SDL_CreateCond();
1115 buffer_storage = 4;
1116 frame_buffers = calloc(buffer_storage, sizeof(uint32_t*));
1117 frame_buffers[0] = texture_buf;
1118 num_buffers = 1;
1119 }
1120
1121 init_audio(); 1155 init_audio();
1122 1156
1123 uint32_t db_size; 1157 uint32_t db_size;
1124 char *db_data = read_bundled_file("gamecontrollerdb.txt", &db_size); 1158 char *db_data = read_bundled_file("gamecontrollerdb.txt", &db_size);
1125 if (db_data) { 1159 if (db_data) {
1138 } 1172 }
1139 static int in_toggle; 1173 static int in_toggle;
1140 1174
1141 void render_config_updated(void) 1175 void render_config_updated(void)
1142 { 1176 {
1143 uint8_t old_sync_to_audio = sync_to_audio;
1144
1145 free_surfaces(); 1177 free_surfaces();
1146 #ifndef DISABLE_OPENGL 1178 #ifndef DISABLE_OPENGL
1147 if (render_gl) { 1179 if (render_gl) {
1148 if (on_context_destroyed) { 1180 if (on_context_destroyed) {
1149 on_context_destroyed(); 1181 on_context_destroyed();
1333 1365
1334 uint32_t *locked_pixels; 1366 uint32_t *locked_pixels;
1335 uint32_t locked_pitch; 1367 uint32_t locked_pitch;
1336 uint32_t *render_get_framebuffer(uint8_t which, int *pitch) 1368 uint32_t *render_get_framebuffer(uint8_t which, int *pitch)
1337 { 1369 {
1338 if (run_on_audio_thread) { 1370 if (sync_src == SYNC_AUDIO_THREAD || sync_src == SYNC_EXTERNAL) {
1339 *pitch = LINEBUF_SIZE * sizeof(uint32_t); 1371 *pitch = LINEBUF_SIZE * sizeof(uint32_t);
1340 uint32_t *buffer; 1372 uint32_t *buffer;
1341 SDL_LockMutex(free_buffer_mutex); 1373 SDL_LockMutex(free_buffer_mutex);
1342 if (num_buffers) { 1374 if (num_buffers) {
1343 buffer = frame_buffers[--num_buffers]; 1375 buffer = frame_buffers[--num_buffers];
1405 static uint32_t last_width, last_height; 1437 static uint32_t last_width, last_height;
1406 static uint8_t interlaced; 1438 static uint8_t interlaced;
1407 static void process_framebuffer(uint32_t *buffer, uint8_t which, int width) 1439 static void process_framebuffer(uint32_t *buffer, uint8_t which, int width)
1408 { 1440 {
1409 static uint8_t last; 1441 static uint8_t last;
1410 if (!render_is_audio_sync() && which <= FRAMEBUFFER_EVEN && source_frame_count < 0) { 1442 if (sync_src == SYNC_VIDEO && which <= FRAMEBUFFER_EVEN && source_frame_count < 0) {
1411 source_frame++; 1443 source_frame++;
1412 if (source_frame >= source_hz) { 1444 if (source_frame >= source_hz) {
1413 source_frame = 0; 1445 source_frame = 0;
1414 } 1446 }
1415 source_frame_count = frame_repeat[source_frame]; 1447 source_frame_count = frame_repeat[source_frame];
1461 } 1493 }
1462 #endif 1494 #endif
1463 } 1495 }
1464 } else { 1496 } else {
1465 #endif 1497 #endif
1466 //TODO: Support run_on_audio_thread for render API framebuffers 1498 //TODO: Support SYNC_AUDIO_THREAD/SYNC_EXTERNAL for render API framebuffers
1467 if (which <= FRAMEBUFFER_EVEN && last != which) { 1499 if (which <= FRAMEBUFFER_EVEN && last != which) {
1468 uint8_t *cur_dst = (uint8_t *)locked_pixels; 1500 uint8_t *cur_dst = (uint8_t *)locked_pixels;
1469 uint8_t *cur_saved = (uint8_t *)texture_buf; 1501 uint8_t *cur_saved = (uint8_t *)texture_buf;
1470 uint32_t dst_off = which == FRAMEBUFFER_EVEN ? 0 : locked_pitch; 1502 uint32_t dst_off = which == FRAMEBUFFER_EVEN ? 0 : locked_pitch;
1471 uint32_t src_off = which == FRAMEBUFFER_EVEN ? locked_pitch : 0; 1503 uint32_t src_off = which == FRAMEBUFFER_EVEN ? locked_pitch : 0;
1610 frame frame_queue[4]; 1642 frame frame_queue[4];
1611 int frame_queue_len, frame_queue_read, frame_queue_write; 1643 int frame_queue_len, frame_queue_read, frame_queue_write;
1612 1644
1613 void render_framebuffer_updated(uint8_t which, int width) 1645 void render_framebuffer_updated(uint8_t which, int width)
1614 { 1646 {
1615 if (run_on_audio_thread) { 1647 if (sync_src == SYNC_AUDIO_THREAD || sync_src == SYNC_EXTERNAL) {
1616 SDL_LockMutex(frame_mutex); 1648 SDL_LockMutex(frame_mutex);
1617 while (frame_queue_len == 4) { 1649 while (frame_queue_len == 4) {
1618 SDL_CondSignal(frame_ready); 1650 SDL_CondSignal(frame_ready);
1619 SDL_UnlockMutex(frame_mutex); 1651 SDL_UnlockMutex(frame_mutex);
1620 SDL_Delay(1); 1652 SDL_Delay(1);
1648 process_framebuffer(texture_buf, which, width); 1680 process_framebuffer(texture_buf, which, width);
1649 } 1681 }
1650 1682
1651 void render_video_loop(void) 1683 void render_video_loop(void)
1652 { 1684 {
1653 if (!run_on_audio_thread) { 1685 if (sync_src != SYNC_AUDIO_THREAD && sync_src != SYNC_EXTERNAL) {
1654 return; 1686 return;
1655 } 1687 }
1656 SDL_PauseAudio(0); 1688 SDL_PauseAudio(0);
1657 SDL_LockMutex(frame_mutex); 1689 SDL_LockMutex(frame_mutex);
1658 for(;;) 1690 for(;;)
1971 return FRAMEBUFFER_USER_START + i; 2003 return FRAMEBUFFER_USER_START + i;
1972 } 2004 }
1973 } 2005 }
1974 return 0xFF; 2006 return 0xFF;
1975 } 2007 }
2008
2009 uint8_t render_create_thread(render_thread *thread, const char *name, render_thread_fun fun, void *data)
2010 {
2011 *thread = SDL_CreateThread(fun, name, data);
2012 return *thread != 0;
2013 }