comparison render_sdl.c @ 1932:b387f1c5a1d0

WIP new sync mode that runs emulation on audio thread
author Michael Pavone <pavone@retrodev.com>
date Sat, 18 Apr 2020 22:36:13 -0700
parents 33c0c4579c1f
children 16a795cababd
comparison
equal deleted inserted replaced
1930:0f135b214927 1932:b387f1c5a1d0
44 static uint8_t render_gl = 1; 44 static uint8_t render_gl = 1;
45 static uint8_t scanlines = 0; 45 static uint8_t scanlines = 0;
46 46
47 static uint32_t last_frame = 0; 47 static uint32_t last_frame = 0;
48 48
49 static SDL_mutex * audio_mutex; 49 static SDL_mutex *audio_mutex, *frame_mutex, *free_buffer_mutex;
50 static SDL_cond * audio_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; 53 static uint8_t sync_to_audio, run_on_audio_thread;
54 static uint32_t min_buffered; 54 static uint32_t min_buffered;
55 55
56 uint32_t **frame_buffers;
57 uint32_t num_buffers;
58 uint32_t buffer_storage;
59
56 uint32_t render_min_buffered(void) 60 uint32_t render_min_buffered(void)
57 { 61 {
58 return min_buffered; 62 return min_buffered;
59 } 63 }
60 64
61 uint8_t render_is_audio_sync(void) 65 uint8_t render_is_audio_sync(void)
62 { 66 {
63 return sync_to_audio; 67 return sync_to_audio || run_on_audio_thread;
68 }
69
70 uint8_t render_should_release_on_exit(void)
71 {
72 return !run_on_audio_thread;
64 } 73 }
65 74
66 void render_buffer_consumed(audio_source *src) 75 void render_buffer_consumed(audio_source *src)
67 { 76 {
68 SDL_CondSignal(src->opaque); 77 SDL_CondSignal(src->opaque);
97 if (cur_min_buffered < 0) { 106 if (cur_min_buffered < 0) {
98 //underflow last frame, but main thread hasn't gotten a chance to call SDL_PauseAudio yet 107 //underflow last frame, but main thread hasn't gotten a chance to call SDL_PauseAudio yet
99 return; 108 return;
100 } 109 }
101 cur_min_buffered = mix_and_convert(byte_stream, len, &min_remaining_buffer); 110 cur_min_buffered = mix_and_convert(byte_stream, len, &min_remaining_buffer);
111 }
112
113 static void audio_callback_run_on_audio(void *user_data, uint8_t *byte_stream, int len)
114 {
115 if (current_system) {
116 current_system->resume_context(current_system);
117 }
118 mix_and_convert(byte_stream, len, NULL);
102 } 119 }
103 120
104 void render_lock_audio() 121 void render_lock_audio()
105 { 122 {
106 if (sync_to_audio) { 123 if (sync_to_audio) {
148 void render_audio_created(audio_source *source) 165 void render_audio_created(audio_source *source)
149 { 166 {
150 if (sync_to_audio && SDL_GetAudioStatus() == SDL_AUDIO_PAUSED) { 167 if (sync_to_audio && SDL_GetAudioStatus() == SDL_AUDIO_PAUSED) {
151 SDL_PauseAudio(0); 168 SDL_PauseAudio(0);
152 } 169 }
170 if (current_system) {
171 current_system->request_exit(current_system);
172 }
153 } 173 }
154 174
155 void render_source_paused(audio_source *src, uint8_t remaining_sources) 175 void render_source_paused(audio_source *src, uint8_t remaining_sources)
156 { 176 {
157 if (sync_to_audio) { 177 if (sync_to_audio) {
169 } 189 }
170 } 190 }
171 191
172 void render_do_audio_ready(audio_source *src) 192 void render_do_audio_ready(audio_source *src)
173 { 193 {
174 if (sync_to_audio) { 194 if (run_on_audio_thread) {
195 int16_t *tmp = src->front;
196 src->front = src->back;
197 src->back = tmp;
198 src->front_populated = 1;
199 src->buffer_pos = 0;
200 if (all_sources_ready()) {
201 //we've emulated far enough to fill the current buffer
202 current_system->request_exit(current_system);
203 }
204 } else if (sync_to_audio) {
175 SDL_LockMutex(audio_mutex); 205 SDL_LockMutex(audio_mutex);
176 while (src->front_populated) { 206 while (src->front_populated) {
177 SDL_CondWait(src->opaque, audio_mutex); 207 SDL_CondWait(src->opaque, audio_mutex);
178 } 208 }
179 int16_t *tmp = src->front; 209 int16_t *tmp = src->front;
880 if (!samples) { 910 if (!samples) {
881 samples = 512; 911 samples = 512;
882 } 912 }
883 debug_message("config says: %d\n", samples); 913 debug_message("config says: %d\n", samples);
884 desired.samples = samples*2; 914 desired.samples = samples*2;
885 desired.callback = sync_to_audio ? audio_callback : audio_callback_drc; 915 desired.callback = sync_to_audio ? audio_callback : run_on_audio_thread ? audio_callback_run_on_audio : audio_callback_drc;
886 desired.userdata = NULL; 916 desired.userdata = NULL;
887 917
888 if (SDL_OpenAudio(&desired, &actual) < 0) { 918 if (SDL_OpenAudio(&desired, &actual) < 0) {
889 fatal_error("Unable to open SDL audio: %s\n", SDL_GetError()); 919 fatal_error("Unable to open SDL audio: %s\n", SDL_GetError());
890 } 920 }
912 } 942 }
913 943
914 tern_val def = {.ptrval = "audio"}; 944 tern_val def = {.ptrval = "audio"};
915 char *sync_src = tern_find_path_default(config, "system\0sync_source\0", def, TVAL_PTR).ptrval; 945 char *sync_src = tern_find_path_default(config, "system\0sync_source\0", def, TVAL_PTR).ptrval;
916 sync_to_audio = !strcmp(sync_src, "audio"); 946 sync_to_audio = !strcmp(sync_src, "audio");
947 run_on_audio_thread = !strcmp(sync_src, "audio_thread");
917 948
918 const char *vsync; 949 const char *vsync;
919 if (sync_to_audio) { 950 if (sync_to_audio) {
920 def.ptrval = "off"; 951 def.ptrval = "off";
921 vsync = tern_find_path_default(config, "video\0vsync\0", def, TVAL_PTR).ptrval; 952 vsync = tern_find_path_default(config, "video\0vsync\0", def, TVAL_PTR).ptrval;
1074 window_setup(); 1105 window_setup();
1075 1106
1076 audio_mutex = SDL_CreateMutex(); 1107 audio_mutex = SDL_CreateMutex();
1077 audio_ready = SDL_CreateCond(); 1108 audio_ready = SDL_CreateCond();
1078 1109
1110 if (run_on_audio_thread) {
1111 frame_mutex = SDL_CreateMutex();
1112 free_buffer_mutex = SDL_CreateMutex();
1113 frame_ready = SDL_CreateCond();
1114 buffer_storage = 4;
1115 frame_buffers = calloc(buffer_storage, sizeof(uint32_t*));
1116 frame_buffers[0] = texture_buf;
1117 num_buffers = 1;
1118 }
1119
1079 init_audio(); 1120 init_audio();
1080 1121
1081 uint32_t db_size; 1122 uint32_t db_size;
1082 char *db_data = read_bundled_file("gamecontrollerdb.txt", &db_size); 1123 char *db_data = read_bundled_file("gamecontrollerdb.txt", &db_size);
1083 if (db_data) { 1124 if (db_data) {
1175 } 1216 }
1176 1217
1177 uint32_t render_audio_syncs_per_sec(void) 1218 uint32_t render_audio_syncs_per_sec(void)
1178 { 1219 {
1179 //sync samples with audio thread approximately every 8 lines when doing sync to video 1220 //sync samples with audio thread approximately every 8 lines when doing sync to video
1180 return sync_to_audio ? 0 : source_hz * (video_standard == VID_PAL ? 313 : 262) / 8; 1221 return render_is_audio_sync() ? 0 : source_hz * (video_standard == VID_PAL ? 313 : 262) / 8;
1181 } 1222 }
1182 1223
1183 void render_set_video_standard(vid_std std) 1224 void render_set_video_standard(vid_std std)
1184 { 1225 {
1185 video_standard = std; 1226 video_standard = std;
1227 if (render_is_audio_sync()) {
1228 return;
1229 }
1186 source_hz = std == VID_PAL ? 50 : 60; 1230 source_hz = std == VID_PAL ? 50 : 60;
1187 uint32_t max_repeat = 0; 1231 uint32_t max_repeat = 0;
1188 if (abs(source_hz - display_hz) < 2) { 1232 if (abs(source_hz - display_hz) < 2) {
1189 memset(frame_repeat, 0, sizeof(int)*display_hz); 1233 memset(frame_repeat, 0, sizeof(int)*display_hz);
1190 } else { 1234 } else {
1289 1333
1290 uint32_t *locked_pixels; 1334 uint32_t *locked_pixels;
1291 uint32_t locked_pitch; 1335 uint32_t locked_pitch;
1292 uint32_t *render_get_framebuffer(uint8_t which, int *pitch) 1336 uint32_t *render_get_framebuffer(uint8_t which, int *pitch)
1293 { 1337 {
1338 if (run_on_audio_thread) {
1339 *pitch = LINEBUF_SIZE * sizeof(uint32_t);
1340 uint32_t *buffer;
1341 SDL_LockMutex(free_buffer_mutex);
1342 if (num_buffers) {
1343 buffer = frame_buffers[--num_buffers];
1344 } else {
1345 buffer = calloc(512*512, sizeof(uint32_t));
1346 }
1347 SDL_UnlockMutex(free_buffer_mutex);
1348 locked_pixels = buffer;
1349 return buffer;
1350 }
1294 #ifndef DISABLE_OPENGL 1351 #ifndef DISABLE_OPENGL
1295 if (render_gl && which <= FRAMEBUFFER_EVEN) { 1352 if (render_gl && which <= FRAMEBUFFER_EVEN) {
1296 *pitch = LINEBUF_SIZE * sizeof(uint32_t); 1353 *pitch = LINEBUF_SIZE * sizeof(uint32_t);
1297 return texture_buf; 1354 return texture_buf;
1298 } else { 1355 } else {
1325 #ifndef DISABLE_OPENGL 1382 #ifndef DISABLE_OPENGL
1326 } 1383 }
1327 #endif 1384 #endif
1328 } 1385 }
1329 1386
1387 static void release_buffer(uint32_t *buffer)
1388 {
1389 SDL_LockMutex(free_buffer_mutex);
1390 if (num_buffers == buffer_storage) {
1391 buffer_storage *= 2;
1392 frame_buffers = realloc(frame_buffers, sizeof(uint32_t*)*buffer_storage);
1393 }
1394 frame_buffers[num_buffers++] = buffer;
1395 SDL_UnlockMutex(free_buffer_mutex);
1396 }
1397
1330 uint8_t events_processed; 1398 uint8_t events_processed;
1331 #ifdef __ANDROID__ 1399 #ifdef __ANDROID__
1332 #define FPS_INTERVAL 10000 1400 #define FPS_INTERVAL 10000
1333 #else 1401 #else
1334 #define FPS_INTERVAL 1000 1402 #define FPS_INTERVAL 1000
1335 #endif 1403 #endif
1336 1404
1337 static uint32_t last_width, last_height; 1405 static uint32_t last_width, last_height;
1338 static uint8_t interlaced; 1406 static uint8_t interlaced;
1339 void render_framebuffer_updated(uint8_t which, int width) 1407 static void process_framebuffer(uint32_t *buffer, uint8_t which, int width)
1340 { 1408 {
1341 static uint8_t last; 1409 static uint8_t last;
1342 if (!sync_to_audio && which <= FRAMEBUFFER_EVEN && source_frame_count < 0) { 1410 if (!render_is_audio_sync() && which <= FRAMEBUFFER_EVEN && source_frame_count < 0) {
1343 source_frame++; 1411 source_frame++;
1344 if (source_frame >= source_hz) { 1412 if (source_frame >= source_hz) {
1345 source_frame = 0; 1413 source_frame = 0;
1346 } 1414 }
1347 source_frame_count = frame_repeat[source_frame]; 1415 source_frame_count = frame_repeat[source_frame];
1375 width -= overscan_left[video_standard] + overscan_right[video_standard]; 1443 width -= overscan_left[video_standard] + overscan_right[video_standard];
1376 #ifndef DISABLE_OPENGL 1444 #ifndef DISABLE_OPENGL
1377 if (render_gl && which <= FRAMEBUFFER_EVEN) { 1445 if (render_gl && which <= FRAMEBUFFER_EVEN) {
1378 SDL_GL_MakeCurrent(main_window, main_context); 1446 SDL_GL_MakeCurrent(main_window, main_context);
1379 glBindTexture(GL_TEXTURE_2D, textures[which]); 1447 glBindTexture(GL_TEXTURE_2D, textures[which]);
1380 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, LINEBUF_SIZE, height, SRC_FORMAT, GL_UNSIGNED_BYTE, texture_buf + overscan_left[video_standard] + LINEBUF_SIZE * overscan_top[video_standard]); 1448 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, LINEBUF_SIZE, height, SRC_FORMAT, GL_UNSIGNED_BYTE, buffer + overscan_left[video_standard] + LINEBUF_SIZE * overscan_top[video_standard]);
1381 1449
1382 if (screenshot_file) { 1450 if (screenshot_file) {
1383 //properly supporting interlaced modes here is non-trivial, so only save the odd field for now 1451 //properly supporting interlaced modes here is non-trivial, so only save the odd field for now
1384 #ifndef DISABLE_ZLIB 1452 #ifndef DISABLE_ZLIB
1385 if (!strcasecmp(ext, "png")) { 1453 if (!strcasecmp(ext, "png")) {
1386 free(ext); 1454 free(ext);
1387 save_png(screenshot_file, texture_buf, shot_width, shot_height, LINEBUF_SIZE*sizeof(uint32_t)); 1455 save_png(screenshot_file, buffer, shot_width, shot_height, LINEBUF_SIZE*sizeof(uint32_t));
1388 } else { 1456 } else {
1389 free(ext); 1457 free(ext);
1390 #endif 1458 #endif
1391 save_ppm(screenshot_file, texture_buf, shot_width, shot_height, LINEBUF_SIZE*sizeof(uint32_t)); 1459 save_ppm(screenshot_file, buffer, shot_width, shot_height, LINEBUF_SIZE*sizeof(uint32_t));
1392 #ifndef DISABLE_ZLIB 1460 #ifndef DISABLE_ZLIB
1393 } 1461 }
1394 #endif 1462 #endif
1395 } 1463 }
1396 } else { 1464 } else {
1397 #endif 1465 #endif
1466 //TODO: Support run_on_audio_thread for render API framebuffers
1398 if (which <= FRAMEBUFFER_EVEN && last != which) { 1467 if (which <= FRAMEBUFFER_EVEN && last != which) {
1399 uint8_t *cur_dst = (uint8_t *)locked_pixels; 1468 uint8_t *cur_dst = (uint8_t *)locked_pixels;
1400 uint8_t *cur_saved = (uint8_t *)texture_buf; 1469 uint8_t *cur_saved = (uint8_t *)texture_buf;
1401 uint32_t dst_off = which == FRAMEBUFFER_EVEN ? 0 : locked_pitch; 1470 uint32_t dst_off = which == FRAMEBUFFER_EVEN ? 0 : locked_pitch;
1402 uint32_t src_off = which == FRAMEBUFFER_EVEN ? locked_pitch : 0; 1471 uint32_t src_off = which == FRAMEBUFFER_EVEN ? locked_pitch : 0;
1473 } 1542 }
1474 start = last_frame; 1543 start = last_frame;
1475 frame_counter = 0; 1544 frame_counter = 0;
1476 } 1545 }
1477 } 1546 }
1478 if (!sync_to_audio) { 1547 if (!render_is_audio_sync()) {
1479 int32_t local_cur_min, local_min_remaining; 1548 int32_t local_cur_min, local_min_remaining;
1480 SDL_LockAudio(); 1549 SDL_LockAudio();
1481 if (last_buffered > NO_LAST_BUFFERED) { 1550 if (last_buffered > NO_LAST_BUFFERED) {
1482 average_change *= 0.9f; 1551 average_change *= 0.9f;
1483 average_change += (cur_min_buffered - last_buffered) * 0.1f; 1552 average_change += (cur_min_buffered - last_buffered) * 0.1f;
1531 } 1600 }
1532 source_frame_count = frame_repeat[source_frame]; 1601 source_frame_count = frame_repeat[source_frame];
1533 } 1602 }
1534 } 1603 }
1535 1604
1605 typedef struct {
1606 uint32_t *buffer;
1607 int width;
1608 uint8_t which;
1609 } frame;
1610 frame frame_queue[4];
1611 int frame_queue_len, frame_queue_read, frame_queue_write;
1612
1613 void render_framebuffer_updated(uint8_t which, int width)
1614 {
1615 if (run_on_audio_thread) {
1616 SDL_LockMutex(frame_mutex);
1617 while (frame_queue_len == 4) {
1618 SDL_CondSignal(frame_ready);
1619 SDL_UnlockMutex(frame_mutex);
1620 SDL_Delay(1);
1621 SDL_LockMutex(frame_mutex);
1622 }
1623 for (int cur = frame_queue_read, i = 0; i < frame_queue_len; i++) {
1624 if (frame_queue[cur].which == which) {
1625 int last = (frame_queue_write - 1) & 3;
1626 frame_queue_len--;
1627 release_buffer(frame_queue[cur].buffer);
1628 if (last != cur) {
1629 frame_queue[cur] = frame_queue[last];
1630 }
1631 frame_queue_write = last;
1632 break;
1633 }
1634 cur = (cur + 1) & 3;
1635 }
1636 frame_queue[frame_queue_write++] = (frame){
1637 .buffer = locked_pixels,
1638 .width = width,
1639 .which = which
1640 };
1641 frame_queue_write &= 0x3;
1642 frame_queue_len++;
1643 SDL_CondSignal(frame_ready);
1644 SDL_UnlockMutex(frame_mutex);
1645 return;
1646 }
1647 //TODO: Maybe fixme for render API
1648 process_framebuffer(texture_buf, which, width);
1649 }
1650
1651 void render_video_loop(void)
1652 {
1653 if (!run_on_audio_thread) {
1654 return;
1655 }
1656 SDL_PauseAudio(0);
1657 SDL_LockMutex(frame_mutex);
1658 for(;;)
1659 {
1660 while (!frame_queue_len)
1661 {
1662 SDL_CondWait(frame_ready, frame_mutex);
1663 }
1664 for (int i = 0; i < frame_queue_len; i++)
1665 {
1666 frame f = frame_queue[frame_queue_read++];
1667 frame_queue_read &= 0x3;
1668 process_framebuffer(f.buffer, f.which, f.width);
1669 release_buffer(f.buffer);
1670 }
1671 frame_queue_len = 0;
1672 }
1673
1674 SDL_UnlockMutex(frame_mutex);
1675 }
1676
1536 static ui_render_fun render_ui; 1677 static ui_render_fun render_ui;
1537 void render_set_ui_render_fun(ui_render_fun fun) 1678 void render_set_ui_render_fun(ui_render_fun fun)
1538 { 1679 {
1539 render_ui = fun; 1680 render_ui = fun;
1540 } 1681 }