Mercurial > repos > blastem
comparison render_sdl.c @ 1936:2c1c88cd1a3f mame_interp
Merge from default
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Sun, 19 Apr 2020 00:59:09 -0700 |
parents | 16a795cababd |
children | 5a76a7373823 |
comparison
equal
deleted
inserted
replaced
1931:374a5ae694e8 | 1936:2c1c88cd1a3f |
---|---|
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; |
871 int rate = rate_str ? atoi(rate_str) : 0; | 901 int rate = rate_str ? atoi(rate_str) : 0; |
872 if (!rate) { | 902 if (!rate) { |
873 rate = 48000; | 903 rate = 48000; |
874 } | 904 } |
875 desired.freq = rate; | 905 desired.freq = rate; |
876 desired.format = AUDIO_F32SYS; | 906 char *config_format = tern_find_path_default(config, "audio\0format\0", (tern_val){.ptrval="f32"}, TVAL_PTR).ptrval; |
907 desired.format = !strcmp(config_format, "s16") ? AUDIO_S16SYS : AUDIO_F32SYS; | |
877 desired.channels = 2; | 908 desired.channels = 2; |
878 char * samples_str = tern_find_path(config, "audio\0buffer\0", TVAL_PTR).ptrval; | 909 char * samples_str = tern_find_path(config, "audio\0buffer\0", TVAL_PTR).ptrval; |
879 int samples = samples_str ? atoi(samples_str) : 0; | 910 int samples = samples_str ? atoi(samples_str) : 0; |
880 if (!samples) { | 911 if (!samples) { |
881 samples = 512; | 912 samples = 512; |
882 } | 913 } |
883 debug_message("config says: %d\n", samples); | 914 debug_message("config says: %d\n", samples); |
884 desired.samples = samples*2; | 915 desired.samples = samples*2; |
885 desired.callback = sync_to_audio ? audio_callback : audio_callback_drc; | 916 desired.callback = sync_to_audio ? audio_callback : run_on_audio_thread ? audio_callback_run_on_audio : audio_callback_drc; |
886 desired.userdata = NULL; | 917 desired.userdata = NULL; |
887 | 918 |
888 if (SDL_OpenAudio(&desired, &actual) < 0) { | 919 if (SDL_OpenAudio(&desired, &actual) < 0) { |
889 fatal_error("Unable to open SDL audio: %s\n", SDL_GetError()); | 920 fatal_error("Unable to open SDL audio: %s\n", SDL_GetError()); |
890 } | 921 } |
912 } | 943 } |
913 | 944 |
914 tern_val def = {.ptrval = "audio"}; | 945 tern_val def = {.ptrval = "audio"}; |
915 char *sync_src = tern_find_path_default(config, "system\0sync_source\0", def, TVAL_PTR).ptrval; | 946 char *sync_src = tern_find_path_default(config, "system\0sync_source\0", def, TVAL_PTR).ptrval; |
916 sync_to_audio = !strcmp(sync_src, "audio"); | 947 sync_to_audio = !strcmp(sync_src, "audio"); |
948 run_on_audio_thread = !strcmp(sync_src, "audio_thread"); | |
917 | 949 |
918 const char *vsync; | 950 const char *vsync; |
919 if (sync_to_audio) { | 951 if (sync_to_audio) { |
920 def.ptrval = "off"; | 952 def.ptrval = "off"; |
921 vsync = tern_find_path_default(config, "video\0vsync\0", def, TVAL_PTR).ptrval; | 953 vsync = tern_find_path_default(config, "video\0vsync\0", def, TVAL_PTR).ptrval; |
1074 window_setup(); | 1106 window_setup(); |
1075 | 1107 |
1076 audio_mutex = SDL_CreateMutex(); | 1108 audio_mutex = SDL_CreateMutex(); |
1077 audio_ready = SDL_CreateCond(); | 1109 audio_ready = SDL_CreateCond(); |
1078 | 1110 |
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 | |
1079 init_audio(); | 1121 init_audio(); |
1080 | 1122 |
1081 uint32_t db_size; | 1123 uint32_t db_size; |
1082 char *db_data = read_bundled_file("gamecontrollerdb.txt", &db_size); | 1124 char *db_data = read_bundled_file("gamecontrollerdb.txt", &db_size); |
1083 if (db_data) { | 1125 if (db_data) { |
1175 } | 1217 } |
1176 | 1218 |
1177 uint32_t render_audio_syncs_per_sec(void) | 1219 uint32_t render_audio_syncs_per_sec(void) |
1178 { | 1220 { |
1179 //sync samples with audio thread approximately every 8 lines when doing sync to video | 1221 //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; | 1222 return render_is_audio_sync() ? 0 : source_hz * (video_standard == VID_PAL ? 313 : 262) / 8; |
1181 } | 1223 } |
1182 | 1224 |
1183 void render_set_video_standard(vid_std std) | 1225 void render_set_video_standard(vid_std std) |
1184 { | 1226 { |
1185 video_standard = std; | 1227 video_standard = std; |
1228 if (render_is_audio_sync()) { | |
1229 return; | |
1230 } | |
1186 source_hz = std == VID_PAL ? 50 : 60; | 1231 source_hz = std == VID_PAL ? 50 : 60; |
1187 uint32_t max_repeat = 0; | 1232 uint32_t max_repeat = 0; |
1188 if (abs(source_hz - display_hz) < 2) { | 1233 if (abs(source_hz - display_hz) < 2) { |
1189 memset(frame_repeat, 0, sizeof(int)*display_hz); | 1234 memset(frame_repeat, 0, sizeof(int)*display_hz); |
1190 } else { | 1235 } else { |
1289 | 1334 |
1290 uint32_t *locked_pixels; | 1335 uint32_t *locked_pixels; |
1291 uint32_t locked_pitch; | 1336 uint32_t locked_pitch; |
1292 uint32_t *render_get_framebuffer(uint8_t which, int *pitch) | 1337 uint32_t *render_get_framebuffer(uint8_t which, int *pitch) |
1293 { | 1338 { |
1339 if (run_on_audio_thread) { | |
1340 *pitch = LINEBUF_SIZE * sizeof(uint32_t); | |
1341 uint32_t *buffer; | |
1342 SDL_LockMutex(free_buffer_mutex); | |
1343 if (num_buffers) { | |
1344 buffer = frame_buffers[--num_buffers]; | |
1345 } else { | |
1346 buffer = calloc(512*512, sizeof(uint32_t)); | |
1347 } | |
1348 SDL_UnlockMutex(free_buffer_mutex); | |
1349 locked_pixels = buffer; | |
1350 return buffer; | |
1351 } | |
1294 #ifndef DISABLE_OPENGL | 1352 #ifndef DISABLE_OPENGL |
1295 if (render_gl && which <= FRAMEBUFFER_EVEN) { | 1353 if (render_gl && which <= FRAMEBUFFER_EVEN) { |
1296 *pitch = LINEBUF_SIZE * sizeof(uint32_t); | 1354 *pitch = LINEBUF_SIZE * sizeof(uint32_t); |
1297 return texture_buf; | 1355 return texture_buf; |
1298 } else { | 1356 } else { |
1325 #ifndef DISABLE_OPENGL | 1383 #ifndef DISABLE_OPENGL |
1326 } | 1384 } |
1327 #endif | 1385 #endif |
1328 } | 1386 } |
1329 | 1387 |
1388 static void release_buffer(uint32_t *buffer) | |
1389 { | |
1390 SDL_LockMutex(free_buffer_mutex); | |
1391 if (num_buffers == buffer_storage) { | |
1392 buffer_storage *= 2; | |
1393 frame_buffers = realloc(frame_buffers, sizeof(uint32_t*)*buffer_storage); | |
1394 } | |
1395 frame_buffers[num_buffers++] = buffer; | |
1396 SDL_UnlockMutex(free_buffer_mutex); | |
1397 } | |
1398 | |
1330 uint8_t events_processed; | 1399 uint8_t events_processed; |
1331 #ifdef __ANDROID__ | 1400 #ifdef __ANDROID__ |
1332 #define FPS_INTERVAL 10000 | 1401 #define FPS_INTERVAL 10000 |
1333 #else | 1402 #else |
1334 #define FPS_INTERVAL 1000 | 1403 #define FPS_INTERVAL 1000 |
1335 #endif | 1404 #endif |
1336 | 1405 |
1337 static uint32_t last_width, last_height; | 1406 static uint32_t last_width, last_height; |
1338 static uint8_t interlaced; | 1407 static uint8_t interlaced; |
1339 void render_framebuffer_updated(uint8_t which, int width) | 1408 static void process_framebuffer(uint32_t *buffer, uint8_t which, int width) |
1340 { | 1409 { |
1341 static uint8_t last; | 1410 static uint8_t last; |
1342 if (!sync_to_audio && which <= FRAMEBUFFER_EVEN && source_frame_count < 0) { | 1411 if (!render_is_audio_sync() && which <= FRAMEBUFFER_EVEN && source_frame_count < 0) { |
1343 source_frame++; | 1412 source_frame++; |
1344 if (source_frame >= source_hz) { | 1413 if (source_frame >= source_hz) { |
1345 source_frame = 0; | 1414 source_frame = 0; |
1346 } | 1415 } |
1347 source_frame_count = frame_repeat[source_frame]; | 1416 source_frame_count = frame_repeat[source_frame]; |
1375 width -= overscan_left[video_standard] + overscan_right[video_standard]; | 1444 width -= overscan_left[video_standard] + overscan_right[video_standard]; |
1376 #ifndef DISABLE_OPENGL | 1445 #ifndef DISABLE_OPENGL |
1377 if (render_gl && which <= FRAMEBUFFER_EVEN) { | 1446 if (render_gl && which <= FRAMEBUFFER_EVEN) { |
1378 SDL_GL_MakeCurrent(main_window, main_context); | 1447 SDL_GL_MakeCurrent(main_window, main_context); |
1379 glBindTexture(GL_TEXTURE_2D, textures[which]); | 1448 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]); | 1449 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 | 1450 |
1382 if (screenshot_file) { | 1451 if (screenshot_file) { |
1383 //properly supporting interlaced modes here is non-trivial, so only save the odd field for now | 1452 //properly supporting interlaced modes here is non-trivial, so only save the odd field for now |
1384 #ifndef DISABLE_ZLIB | 1453 #ifndef DISABLE_ZLIB |
1385 if (!strcasecmp(ext, "png")) { | 1454 if (!strcasecmp(ext, "png")) { |
1386 free(ext); | 1455 free(ext); |
1387 save_png(screenshot_file, texture_buf, shot_width, shot_height, LINEBUF_SIZE*sizeof(uint32_t)); | 1456 save_png(screenshot_file, buffer, shot_width, shot_height, LINEBUF_SIZE*sizeof(uint32_t)); |
1388 } else { | 1457 } else { |
1389 free(ext); | 1458 free(ext); |
1390 #endif | 1459 #endif |
1391 save_ppm(screenshot_file, texture_buf, shot_width, shot_height, LINEBUF_SIZE*sizeof(uint32_t)); | 1460 save_ppm(screenshot_file, buffer, shot_width, shot_height, LINEBUF_SIZE*sizeof(uint32_t)); |
1392 #ifndef DISABLE_ZLIB | 1461 #ifndef DISABLE_ZLIB |
1393 } | 1462 } |
1394 #endif | 1463 #endif |
1395 } | 1464 } |
1396 } else { | 1465 } else { |
1397 #endif | 1466 #endif |
1467 //TODO: Support run_on_audio_thread for render API framebuffers | |
1398 if (which <= FRAMEBUFFER_EVEN && last != which) { | 1468 if (which <= FRAMEBUFFER_EVEN && last != which) { |
1399 uint8_t *cur_dst = (uint8_t *)locked_pixels; | 1469 uint8_t *cur_dst = (uint8_t *)locked_pixels; |
1400 uint8_t *cur_saved = (uint8_t *)texture_buf; | 1470 uint8_t *cur_saved = (uint8_t *)texture_buf; |
1401 uint32_t dst_off = which == FRAMEBUFFER_EVEN ? 0 : locked_pitch; | 1471 uint32_t dst_off = which == FRAMEBUFFER_EVEN ? 0 : locked_pitch; |
1402 uint32_t src_off = which == FRAMEBUFFER_EVEN ? locked_pitch : 0; | 1472 uint32_t src_off = which == FRAMEBUFFER_EVEN ? locked_pitch : 0; |
1473 } | 1543 } |
1474 start = last_frame; | 1544 start = last_frame; |
1475 frame_counter = 0; | 1545 frame_counter = 0; |
1476 } | 1546 } |
1477 } | 1547 } |
1478 if (!sync_to_audio) { | 1548 if (!render_is_audio_sync()) { |
1479 int32_t local_cur_min, local_min_remaining; | 1549 int32_t local_cur_min, local_min_remaining; |
1480 SDL_LockAudio(); | 1550 SDL_LockAudio(); |
1481 if (last_buffered > NO_LAST_BUFFERED) { | 1551 if (last_buffered > NO_LAST_BUFFERED) { |
1482 average_change *= 0.9f; | 1552 average_change *= 0.9f; |
1483 average_change += (cur_min_buffered - last_buffered) * 0.1f; | 1553 average_change += (cur_min_buffered - last_buffered) * 0.1f; |
1531 } | 1601 } |
1532 source_frame_count = frame_repeat[source_frame]; | 1602 source_frame_count = frame_repeat[source_frame]; |
1533 } | 1603 } |
1534 } | 1604 } |
1535 | 1605 |
1606 typedef struct { | |
1607 uint32_t *buffer; | |
1608 int width; | |
1609 uint8_t which; | |
1610 } frame; | |
1611 frame frame_queue[4]; | |
1612 int frame_queue_len, frame_queue_read, frame_queue_write; | |
1613 | |
1614 void render_framebuffer_updated(uint8_t which, int width) | |
1615 { | |
1616 if (run_on_audio_thread) { | |
1617 SDL_LockMutex(frame_mutex); | |
1618 while (frame_queue_len == 4) { | |
1619 SDL_CondSignal(frame_ready); | |
1620 SDL_UnlockMutex(frame_mutex); | |
1621 SDL_Delay(1); | |
1622 SDL_LockMutex(frame_mutex); | |
1623 } | |
1624 for (int cur = frame_queue_read, i = 0; i < frame_queue_len; i++) { | |
1625 if (frame_queue[cur].which == which) { | |
1626 int last = (frame_queue_write - 1) & 3; | |
1627 frame_queue_len--; | |
1628 release_buffer(frame_queue[cur].buffer); | |
1629 if (last != cur) { | |
1630 frame_queue[cur] = frame_queue[last]; | |
1631 } | |
1632 frame_queue_write = last; | |
1633 break; | |
1634 } | |
1635 cur = (cur + 1) & 3; | |
1636 } | |
1637 frame_queue[frame_queue_write++] = (frame){ | |
1638 .buffer = locked_pixels, | |
1639 .width = width, | |
1640 .which = which | |
1641 }; | |
1642 frame_queue_write &= 0x3; | |
1643 frame_queue_len++; | |
1644 SDL_CondSignal(frame_ready); | |
1645 SDL_UnlockMutex(frame_mutex); | |
1646 return; | |
1647 } | |
1648 //TODO: Maybe fixme for render API | |
1649 process_framebuffer(texture_buf, which, width); | |
1650 } | |
1651 | |
1652 void render_video_loop(void) | |
1653 { | |
1654 if (!run_on_audio_thread) { | |
1655 return; | |
1656 } | |
1657 SDL_PauseAudio(0); | |
1658 SDL_LockMutex(frame_mutex); | |
1659 for(;;) | |
1660 { | |
1661 while (!frame_queue_len) | |
1662 { | |
1663 SDL_CondWait(frame_ready, frame_mutex); | |
1664 } | |
1665 for (int i = 0; i < frame_queue_len; i++) | |
1666 { | |
1667 frame f = frame_queue[frame_queue_read++]; | |
1668 frame_queue_read &= 0x3; | |
1669 process_framebuffer(f.buffer, f.which, f.width); | |
1670 release_buffer(f.buffer); | |
1671 } | |
1672 frame_queue_len = 0; | |
1673 } | |
1674 | |
1675 SDL_UnlockMutex(frame_mutex); | |
1676 } | |
1677 | |
1536 static ui_render_fun render_ui; | 1678 static ui_render_fun render_ui; |
1537 void render_set_ui_render_fun(ui_render_fun fun) | 1679 void render_set_ui_render_fun(ui_render_fun fun) |
1538 { | 1680 { |
1539 render_ui = fun; | 1681 render_ui = fun; |
1540 } | 1682 } |