Mercurial > repos > blastem
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 } |