changeset 2093:46ee354f29bd

Hack fix for audio deadlock issue
author Michael Pavone <pavone@retrodev.com>
date Sun, 06 Feb 2022 23:25:23 -0800
parents 8665d8da0e1c
children ca6fc8c8dc60
files render_audio.c render_sdl.c
diffstat 2 files changed, 74 insertions(+), 33 deletions(-) [+]
line wrap: on
line diff
--- a/render_audio.c	Sun Feb 06 22:52:24 2022 -0800
+++ b/render_audio.c	Sun Feb 06 23:25:23 2022 -0800
@@ -135,6 +135,40 @@
 	return num_populated == num_audio_sources;
 }
 
+uint8_t audio_deadlock_hack(void)
+{
+	uint32_t min_buffer_pos = 0xFFFFFFFFU;
+	for (uint8_t i = 0; i < num_audio_sources; i++)
+	{
+		if (audio_sources[i]->front_populated) {
+			uint32_t buffer_pos = audio_sources[i]->buffer_pos;
+			if (audio_sources[i]->num_channels == 1) {
+				buffer_pos *= 2;
+			}
+			if (buffer_pos < min_buffer_pos) {
+				min_buffer_pos = buffer_pos;
+			}
+		}
+	}
+	uint8_t do_signal = 0;
+	for (uint8_t i = 0; i < num_audio_sources; i++)
+	{
+		if (!audio_sources[i]->front_populated) {
+			audio_sources[i]->front_populated = 1;
+			int16_t *tmp = audio_sources[i]->front;
+			audio_sources[i]->front = audio_sources[i]->back;
+			audio_sources[i]->back = tmp;
+			if (audio_sources[i]->num_channels == 2) {
+				audio_sources[i]->buffer_pos = min_buffer_pos;
+			} else {
+				audio_sources[i]->buffer_pos = min_buffer_pos / 2;
+			}
+			do_signal = 1;
+		}
+	}
+	return do_signal;
+}
+
 #define BUFFER_INC_RES 0x40000000UL
 
 void render_audio_adjust_clock(audio_source *src, uint64_t master_clock, uint64_t sample_divider)
--- a/render_sdl.c	Sun Feb 06 22:52:24 2022 -0800
+++ b/render_sdl.c	Sun Feb 06 23:25:23 2022 -0800
@@ -214,6 +214,8 @@
 	}
 }
 
+uint8_t audio_deadlock_hack(void);
+
 void render_do_audio_ready(audio_source *src)
 {
 	if (sync_src == SYNC_AUDIO_THREAD) {
@@ -228,6 +230,11 @@
 		}
 	} else if (sync_src == SYNC_AUDIO) {
 		SDL_LockMutex(audio_mutex);
+			if (src->front_populated) {
+				if (audio_deadlock_hack()) {
+					SDL_CondSignal(audio_ready);
+				}
+			}
 			while (src->front_populated) {
 				SDL_CondWait(src->opaque, audio_mutex);
 			}
@@ -349,7 +356,7 @@
 	}
 #endif
 	text[fsize] = 0;
-	
+
 	if (strncmp(text, "#version", strlen("#version"))) {
 		GLchar *tmp = text;
 		text = alloc_concat(shader_prefix, tmp);
@@ -999,7 +1006,7 @@
 	if (is_fullscreen) {
 		flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
 	}
-	
+
 	tern_val def = {.ptrval = "audio"};
 	if (external_sync) {
 		sync_src = SYNC_EXTERNAL;
@@ -1013,7 +1020,7 @@
 			sync_src = SYNC_VIDEO;
 		}
 	}
-	
+
 	if (!num_buffers && (sync_src == SYNC_AUDIO_THREAD || sync_src == SYNC_EXTERNAL)) {
 		frame_mutex = SDL_CreateMutex();
 		free_buffer_mutex = SDL_CreateMutex();
@@ -1023,7 +1030,7 @@
 		frame_buffers[0] = texture_buf;
 		num_buffers = 1;
 	}
-	
+
 	const char *vsync;
 	if (sync_src == SYNC_AUDIO) {
 		def.ptrval = "off";
@@ -1031,7 +1038,7 @@
 	} else {
 		vsync = "on";
 	}
-	
+
 	tern_node *video = tern_find_node(config, "video");
 	if (video)
 	{
@@ -1059,7 +1066,7 @@
 		}
 	}
 	render_gl = 0;
-	
+
 #ifndef DISABLE_OPENGL
 	char *gl_enabled_str = tern_find_path_default(config, "video\0gl\0", def, TVAL_PTR).ptrval;
 	uint8_t gl_enabled = strcmp(gl_enabled_str, "off") != 0;
@@ -1162,7 +1169,7 @@
 	debug_message("width: %d, height: %d\n", width, height);
 	windowed_width = width;
 	windowed_height = height;
-	
+
 	SDL_DisplayMode mode;
 	//TODO: Explicit multiple monitor support
 	SDL_GetCurrentDisplayMode(0, &mode);
@@ -1177,16 +1184,16 @@
 	main_width = width;
 	main_height = height;
 	is_fullscreen = fullscreen;
-	
+
 	caption = title;
-	
+
 	window_setup();
 
 	audio_mutex = SDL_CreateMutex();
 	audio_ready = SDL_CreateCond();
-	
+
 	init_audio();
-	
+
 	uint32_t db_size;
 	char *db_data = read_bundled_file("gamecontrollerdb.txt", &db_size);
 	if (db_data) {
@@ -1194,11 +1201,11 @@
 		free(db_data);
 		debug_message("Added %d game controller mappings from gamecontrollerdb.txt\n", added);
 	}
-	
+
 	controller_add_mappings();
-	
+
 	SDL_JoystickEventState(SDL_ENABLE);
-	
+
 	render_set_video_standard(VID_NTSC);
 
 	atexit(render_quit);
@@ -1238,7 +1245,7 @@
 	SDL_DestroyWindow(main_window);
 	main_window = NULL;
 	drain_events();
-	
+
 	char *config_width = tern_find_path(config, "video\0width\0", TVAL_PTR).ptrval;
 	if (config_width) {
 		windowed_width = atoi(config_width);
@@ -1265,7 +1272,7 @@
 	if (on_ui_fb_resized) {
 		on_ui_fb_resized();
 	}
-	
+
 	window_setup();
 	update_aspect();
 #ifndef DISABLE_OPENGL
@@ -1280,7 +1287,7 @@
 	quitting = 0;
 	init_audio();
 	render_set_video_standard(video_standard);
-	
+
 	drain_events();
 	in_toggle = 0;
 	if (!was_paused) {
@@ -1364,7 +1371,7 @@
 			break;
 		}
 	}
-	
+
 	if (win_idx == 0xFF) {
 		num_textures++;
 		sdl_textures = realloc(sdl_textures, num_textures * sizeof(*sdl_textures));
@@ -1388,7 +1395,7 @@
 	}
 	close_handlers[win_idx] = close_handler;
 	return texture_idx;
-	
+
 fail_texture:
 	SDL_DestroyRenderer(extra_renderers[win_idx]);
 fail_renderer:
@@ -1404,7 +1411,7 @@
 	//Destroying the renderers also frees the textures
 	SDL_DestroyRenderer(extra_renderers[win_idx]);
 	SDL_DestroyWindow(extra_windows[win_idx]);
-	
+
 	extra_renderers[win_idx] = NULL;
 	extra_windows[win_idx] = NULL;
 }
@@ -1494,9 +1501,9 @@
 		//TODO: Figure out what to do about SDL Render API texture locking
 		return;
 	}
-	
+
 	last_width = width;
-	uint32_t height = which <= FRAMEBUFFER_EVEN 
+	uint32_t height = which <= FRAMEBUFFER_EVEN
 		? (video_standard == VID_NTSC ? 243 : 294) - (overscan_top[video_standard] + overscan_bot[video_standard])
 		: 240;
 	FILE *screenshot_file = NULL;
@@ -1524,7 +1531,7 @@
 		SDL_GL_MakeCurrent(main_window, main_context);
 		glBindTexture(GL_TEXTURE_2D, textures[which]);
 		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]);
-		
+
 		if (screenshot_file) {
 			//properly supporting interlaced modes here is non-trivial, so only save the odd field for now
 #ifndef DISABLE_ZLIB
@@ -1646,7 +1653,7 @@
 			|| (average_change >0 && local_cur_min > 5 * min_buffered / 4)
 			|| cur_min_buffered < 0
 		) {
-			
+
 			if (cur_min_buffered < 0) {
 				adjust_ratio = max_adjust;
 				SDL_PauseAudio(1);
@@ -1665,7 +1672,7 @@
 		if (adjust_ratio != 0.0f) {
 			average_change = 0;
 			render_audio_adjust_speed(adjust_ratio);
-			
+
 		}
 		while (source_frame_count > 0)
 		{
@@ -1753,7 +1760,7 @@
 				break;
 			}
 		}
-	
+
 	SDL_UnlockMutex(frame_mutex);
 }
 
@@ -1791,7 +1798,7 @@
 		glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, (void *)0);
 
 		glDisableVertexAttribArray(at_pos);
-		
+
 		if (render_ui) {
 			render_ui();
 		}
@@ -1902,7 +1909,7 @@
 	if (controller > MAX_JOYSTICKS || !joysticks[controller]) {
 		return RENDER_NOT_PLUGGED_IN;
 	}
-	
+
 	if (!SDL_IsGameController(joystick_sdl_index[controller])) {
 		return RENDER_NOT_MAPPED;
 	}
@@ -1911,11 +1918,11 @@
 		warning("Failed to open game controller %d: %s\n", controller, SDL_GetError());
 		return RENDER_NOT_PLUGGED_IN;
 	}
-	
+
 	SDL_GameControllerButtonBind cbind;
 	int32_t is_positive = RENDER_AXIS_POS;
 	if (is_axis) {
-		
+
 		int sdl_axis = render_lookup_axis(name);
 		if (sdl_axis == SDL_CONTROLLER_AXIS_INVALID) {
 			SDL_GameControllerClose(control);
@@ -1979,7 +1986,7 @@
 		return;
 	}
 	in_toggle = 1;
-	
+
 	//toggling too fast seems to cause a deadlock
 	static uint32_t last_toggle;
 	uint32_t cur = SDL_GetTicks();
@@ -1988,7 +1995,7 @@
 		return;
 	}
 	last_toggle = cur;
-	
+
 	drain_events();
 	is_fullscreen = !is_fullscreen;
 	if (is_fullscreen) {
@@ -2052,7 +2059,7 @@
 	for (int i = 0; i < num_textures - 2; i++)
 	{
 		if (extra_windows[i] && (SDL_GetWindowFlags(extra_windows[i]) & SDL_WINDOW_INPUT_FOCUS)) {
-			return FRAMEBUFFER_USER_START + i; 
+			return FRAMEBUFFER_USER_START + i;
 		}
 	}
 	return 0xFF;