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 }