Mercurial > repos > blastem
comparison render_sdl.c @ 2053:3414a4423de1 segacd
Merge from default
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Sat, 15 Jan 2022 13:15:21 -0800 |
parents | 0757da8ee702 |
children | 46ee354f29bd |
comparison
equal
deleted
inserted
replaced
1692:5dacaef602a7 | 2053:3414a4423de1 |
---|---|
11 #include "render_sdl.h" | 11 #include "render_sdl.h" |
12 #include "blastem.h" | 12 #include "blastem.h" |
13 #include "genesis.h" | 13 #include "genesis.h" |
14 #include "bindings.h" | 14 #include "bindings.h" |
15 #include "util.h" | 15 #include "util.h" |
16 #include "paths.h" | |
16 #include "ppm.h" | 17 #include "ppm.h" |
17 #include "png.h" | 18 #include "png.h" |
18 #include "config.h" | 19 #include "config.h" |
19 #include "controller_info.h" | 20 #include "controller_info.h" |
20 | 21 |
43 static uint8_t render_gl = 1; | 44 static uint8_t render_gl = 1; |
44 static uint8_t scanlines = 0; | 45 static uint8_t scanlines = 0; |
45 | 46 |
46 static uint32_t last_frame = 0; | 47 static uint32_t last_frame = 0; |
47 | 48 |
48 static uint32_t buffer_samples, sample_rate; | 49 static SDL_mutex *audio_mutex, *frame_mutex, *free_buffer_mutex; |
49 static uint32_t missing_count; | 50 static SDL_cond *audio_ready, *frame_ready; |
50 | |
51 static SDL_mutex * audio_mutex; | |
52 static SDL_cond * audio_ready; | |
53 static uint8_t quitting = 0; | 51 static uint8_t quitting = 0; |
54 | 52 |
55 struct audio_source { | 53 enum { |
56 SDL_cond *cond; | 54 SYNC_AUDIO, |
57 int16_t *front; | 55 SYNC_AUDIO_THREAD, |
58 int16_t *back; | 56 SYNC_VIDEO, |
59 double dt; | 57 SYNC_EXTERNAL |
60 uint64_t buffer_fraction; | |
61 uint64_t buffer_inc; | |
62 uint32_t buffer_pos; | |
63 uint32_t read_start; | |
64 uint32_t read_end; | |
65 uint32_t lowpass_alpha; | |
66 uint32_t mask; | |
67 int16_t last_left; | |
68 int16_t last_right; | |
69 uint8_t num_channels; | |
70 uint8_t front_populated; | |
71 }; | 58 }; |
72 | 59 |
73 static audio_source *audio_sources[8]; | 60 static uint8_t sync_src; |
74 static audio_source *inactive_audio_sources[8]; | |
75 static uint8_t num_audio_sources; | |
76 static uint8_t num_inactive_audio_sources; | |
77 static uint8_t sync_to_audio; | |
78 static uint32_t min_buffered; | 61 static uint32_t min_buffered; |
79 | 62 |
80 typedef int32_t (*mix_func)(audio_source *audio, void *vstream, int len); | 63 uint32_t **frame_buffers; |
81 | 64 uint32_t num_buffers; |
82 static int32_t mix_s16(audio_source *audio, void *vstream, int len) | 65 uint32_t buffer_storage; |
83 { | 66 |
84 int samples = len/(sizeof(int16_t)*2); | 67 uint32_t render_min_buffered(void) |
85 int16_t *stream = vstream; | 68 { |
86 int16_t *end = stream + 2*samples; | 69 return min_buffered; |
87 int16_t *src = audio->front; | 70 } |
88 uint32_t i = audio->read_start; | 71 |
89 uint32_t i_end = audio->read_end; | 72 uint8_t render_is_audio_sync(void) |
90 int16_t *cur = stream; | 73 { |
91 if (audio->num_channels == 1) { | 74 return sync_src < SYNC_VIDEO; |
92 while (cur < end && i != i_end) | 75 } |
93 { | 76 |
94 *(cur++) += src[i]; | 77 uint8_t render_should_release_on_exit(void) |
95 *(cur++) += src[i++]; | 78 { |
96 i &= audio->mask; | 79 return sync_src != SYNC_AUDIO_THREAD; |
97 } | 80 } |
98 } else { | 81 |
99 while (cur < end && i != i_end) | 82 void render_buffer_consumed(audio_source *src) |
100 { | 83 { |
101 *(cur++) += src[i++]; | 84 SDL_CondSignal(src->opaque); |
102 *(cur++) += src[i++]; | 85 } |
103 i &= audio->mask; | |
104 } | |
105 } | |
106 | |
107 if (cur != end) { | |
108 printf("Underflow of %d samples, read_start: %d, read_end: %d, mask: %X\n", (int)(end-cur)/2, audio->read_start, audio->read_end, audio->mask); | |
109 } | |
110 if (!sync_to_audio) { | |
111 audio->read_start = i; | |
112 } | |
113 if (cur != end) { | |
114 //printf("Underflow of %d samples, read_start: %d, read_end: %d, mask: %X\n", (int)(end-cur)/2, audio->read_start, audio->read_end, audio->mask); | |
115 return (cur-end)/2; | |
116 } else { | |
117 return ((i_end - i) & audio->mask) / audio->num_channels; | |
118 } | |
119 } | |
120 | |
121 static int32_t mix_f32(audio_source *audio, void *vstream, int len) | |
122 { | |
123 int samples = len/(sizeof(float)*2); | |
124 float *stream = vstream; | |
125 float *end = stream + 2*samples; | |
126 int16_t *src = audio->front; | |
127 uint32_t i = audio->read_start; | |
128 uint32_t i_end = audio->read_end; | |
129 float *cur = stream; | |
130 if (audio->num_channels == 1) { | |
131 while (cur < end && i != i_end) | |
132 { | |
133 *(cur++) += ((float)src[i]) / 0x7FFF; | |
134 *(cur++) += ((float)src[i++]) / 0x7FFF; | |
135 i &= audio->mask; | |
136 } | |
137 } else { | |
138 while(cur < end && i != i_end) | |
139 { | |
140 *(cur++) += ((float)src[i++]) / 0x7FFF; | |
141 *(cur++) += ((float)src[i++]) / 0x7FFF; | |
142 i &= audio->mask; | |
143 } | |
144 } | |
145 if (!sync_to_audio) { | |
146 audio->read_start = i; | |
147 } | |
148 if (cur != end) { | |
149 printf("Underflow of %d samples, read_start: %d, read_end: %d, mask: %X\n", (int)(end-cur)/2, audio->read_start, audio->read_end, audio->mask); | |
150 return (cur-end)/2; | |
151 } else { | |
152 return ((i_end - i) & audio->mask) / audio->num_channels; | |
153 } | |
154 } | |
155 | |
156 static int32_t mix_null(audio_source *audio, void *vstream, int len) | |
157 { | |
158 return 0; | |
159 } | |
160 | |
161 static mix_func mix; | |
162 | 86 |
163 static void audio_callback(void * userdata, uint8_t *byte_stream, int len) | 87 static void audio_callback(void * userdata, uint8_t *byte_stream, int len) |
164 { | 88 { |
165 uint8_t num_populated; | |
166 memset(byte_stream, 0, len); | |
167 SDL_LockMutex(audio_mutex); | 89 SDL_LockMutex(audio_mutex); |
90 uint8_t all_ready; | |
168 do { | 91 do { |
169 num_populated = 0; | 92 all_ready = all_sources_ready(); |
170 for (uint8_t i = 0; i < num_audio_sources; i++) | 93 if (!quitting && !all_ready) { |
171 { | |
172 if (audio_sources[i]->front_populated) { | |
173 num_populated++; | |
174 } | |
175 } | |
176 if (!quitting && num_populated < num_audio_sources) { | |
177 fflush(stdout); | |
178 SDL_CondWait(audio_ready, audio_mutex); | 94 SDL_CondWait(audio_ready, audio_mutex); |
179 } | 95 } |
180 } while(!quitting && num_populated < num_audio_sources); | 96 } while(!quitting && !all_ready); |
181 if (!quitting) { | 97 if (!quitting) { |
182 for (uint8_t i = 0; i < num_audio_sources; i++) | 98 mix_and_convert(byte_stream, len, NULL); |
183 { | |
184 mix(audio_sources[i], byte_stream, len); | |
185 audio_sources[i]->front_populated = 0; | |
186 SDL_CondSignal(audio_sources[i]->cond); | |
187 } | |
188 } | 99 } |
189 SDL_UnlockMutex(audio_mutex); | 100 SDL_UnlockMutex(audio_mutex); |
190 } | 101 } |
191 | 102 |
192 #define NO_LAST_BUFFERED -2000000000 | 103 #define NO_LAST_BUFFERED -2000000000 |
197 static float max_adjust; | 108 static float max_adjust; |
198 static int32_t cur_min_buffered; | 109 static int32_t cur_min_buffered; |
199 static uint32_t min_remaining_buffer; | 110 static uint32_t min_remaining_buffer; |
200 static void audio_callback_drc(void *userData, uint8_t *byte_stream, int len) | 111 static void audio_callback_drc(void *userData, uint8_t *byte_stream, int len) |
201 { | 112 { |
202 memset(byte_stream, 0, len); | |
203 if (cur_min_buffered < 0) { | 113 if (cur_min_buffered < 0) { |
204 //underflow last frame, but main thread hasn't gotten a chance to call SDL_PauseAudio yet | 114 //underflow last frame, but main thread hasn't gotten a chance to call SDL_PauseAudio yet |
205 return; | 115 return; |
206 } | 116 } |
207 cur_min_buffered = 0x7FFFFFFF; | 117 cur_min_buffered = mix_and_convert(byte_stream, len, &min_remaining_buffer); |
208 min_remaining_buffer = 0xFFFFFFFF; | 118 } |
209 for (uint8_t i = 0; i < num_audio_sources; i++) | 119 |
210 { | 120 static void audio_callback_run_on_audio(void *user_data, uint8_t *byte_stream, int len) |
211 | 121 { |
212 int32_t buffered = mix(audio_sources[i], byte_stream, len); | 122 if (current_system) { |
213 cur_min_buffered = buffered < cur_min_buffered ? buffered : cur_min_buffered; | 123 current_system->resume_context(current_system); |
214 uint32_t remaining = (audio_sources[i]->mask + 1)/audio_sources[i]->num_channels - buffered; | 124 } |
215 min_remaining_buffer = remaining < min_remaining_buffer ? remaining : min_remaining_buffer; | 125 mix_and_convert(byte_stream, len, NULL); |
216 } | 126 } |
217 } | 127 |
218 | 128 void render_lock_audio() |
219 static void lock_audio() | 129 { |
220 { | 130 if (sync_src == SYNC_AUDIO) { |
221 if (sync_to_audio) { | |
222 SDL_LockMutex(audio_mutex); | 131 SDL_LockMutex(audio_mutex); |
223 } else { | 132 } else { |
224 SDL_LockAudio(); | 133 SDL_LockAudio(); |
225 } | 134 } |
226 } | 135 } |
227 | 136 |
228 static void unlock_audio() | 137 void render_unlock_audio() |
229 { | 138 { |
230 if (sync_to_audio) { | 139 if (sync_src == SYNC_AUDIO) { |
231 SDL_UnlockMutex(audio_mutex); | 140 SDL_UnlockMutex(audio_mutex); |
232 } else { | 141 } else { |
233 SDL_UnlockAudio(); | 142 SDL_UnlockAudio(); |
234 } | 143 } |
235 } | 144 } |
239 SDL_LockMutex(audio_mutex); | 148 SDL_LockMutex(audio_mutex); |
240 quitting = 1; | 149 quitting = 1; |
241 SDL_CondSignal(audio_ready); | 150 SDL_CondSignal(audio_ready); |
242 SDL_UnlockMutex(audio_mutex); | 151 SDL_UnlockMutex(audio_mutex); |
243 SDL_CloseAudio(); | 152 SDL_CloseAudio(); |
244 } | 153 /* |
245 | 154 FIXME: move this to render_audio.c |
246 #define BUFFER_INC_RES 0x40000000UL | 155 if (mix_buf) { |
247 | 156 free(mix_buf); |
248 void render_audio_adjust_clock(audio_source *src, uint64_t master_clock, uint64_t sample_divider) | 157 mix_buf = NULL; |
249 { | 158 } |
250 src->buffer_inc = ((BUFFER_INC_RES * (uint64_t)sample_rate) / master_clock) * sample_divider; | 159 */ |
251 } | 160 } |
252 | 161 |
253 audio_source *render_audio_source(uint64_t master_clock, uint64_t sample_divider, uint8_t channels) | 162 void *render_new_audio_opaque(void) |
254 { | 163 { |
255 audio_source *ret = NULL; | 164 return SDL_CreateCond(); |
256 uint32_t alloc_size = sync_to_audio ? channels * buffer_samples : nearest_pow2(min_buffered * 4 * channels); | 165 } |
257 lock_audio(); | 166 |
258 if (num_audio_sources < 8) { | 167 void render_free_audio_opaque(void *opaque) |
259 ret = malloc(sizeof(audio_source)); | 168 { |
260 ret->back = malloc(alloc_size * sizeof(int16_t)); | 169 SDL_DestroyCond(opaque); |
261 ret->front = sync_to_audio ? malloc(alloc_size * sizeof(int16_t)) : ret->back; | 170 } |
262 ret->front_populated = 0; | 171 |
263 ret->cond = SDL_CreateCond(); | 172 void render_audio_created(audio_source *source) |
264 ret->num_channels = channels; | 173 { |
265 audio_sources[num_audio_sources++] = ret; | 174 if (sync_src == SYNC_AUDIO) { |
266 } | 175 //SDL_PauseAudio acquires the audio device lock, which is held while the callback runs |
267 unlock_audio(); | 176 //since our callback can itself be stuck waiting on the audio_ready condition variable |
268 if (!ret) { | 177 //calling SDL_PauseAudio(0) again for audio sources after the first can deadlock |
269 fatal_error("Too many audio sources!"); | 178 //fortunately SDL_GetAudioStatus does not acquire the lock so is safe to call here |
270 } else { | 179 if (SDL_GetAudioStatus() == SDL_AUDIO_PAUSED) { |
271 render_audio_adjust_clock(ret, master_clock, sample_divider); | 180 SDL_PauseAudio(0); |
272 double lowpass_cutoff = get_lowpass_cutoff(config); | 181 } |
273 double rc = (1.0 / lowpass_cutoff) / (2.0 * M_PI); | 182 } |
274 ret->dt = 1.0 / ((double)master_clock / (double)(sample_divider)); | 183 if (current_system && sync_src == SYNC_AUDIO_THREAD) { |
275 double alpha = ret->dt / (ret->dt + rc); | 184 system_request_exit(current_system, 0); |
276 ret->lowpass_alpha = (int32_t)(((double)0x10000) * alpha); | 185 } |
277 ret->buffer_pos = 0; | 186 } |
278 ret->buffer_fraction = 0; | 187 |
279 ret->last_left = ret->last_right = 0; | 188 void render_source_paused(audio_source *src, uint8_t remaining_sources) |
280 ret->read_start = 0; | 189 { |
281 ret->read_end = sync_to_audio ? buffer_samples * channels : 0; | 190 if (sync_src == SYNC_AUDIO) { |
282 ret->mask = sync_to_audio ? 0xFFFFFFFF : alloc_size-1; | 191 SDL_CondSignal(audio_ready); |
283 } | 192 } |
284 if (sync_to_audio && SDL_GetAudioStatus() == SDL_AUDIO_PAUSED) { | 193 if (!remaining_sources && render_is_audio_sync()) { |
285 SDL_PauseAudio(0); | |
286 } | |
287 return ret; | |
288 } | |
289 | |
290 void render_pause_source(audio_source *src) | |
291 { | |
292 uint8_t need_pause = 0; | |
293 lock_audio(); | |
294 for (uint8_t i = 0; i < num_audio_sources; i++) | |
295 { | |
296 if (audio_sources[i] == src) { | |
297 audio_sources[i] = audio_sources[--num_audio_sources]; | |
298 if (sync_to_audio) { | |
299 SDL_CondSignal(audio_ready); | |
300 } | |
301 break; | |
302 } | |
303 } | |
304 if (!num_audio_sources) { | |
305 need_pause = 1; | |
306 } | |
307 unlock_audio(); | |
308 if (need_pause) { | |
309 SDL_PauseAudio(1); | 194 SDL_PauseAudio(1); |
310 } | 195 if (sync_src == SYNC_AUDIO_THREAD) { |
311 inactive_audio_sources[num_inactive_audio_sources++] = src; | 196 SDL_CondSignal(frame_ready); |
312 } | 197 } |
313 | 198 } |
314 void render_resume_source(audio_source *src) | 199 } |
315 { | 200 |
316 lock_audio(); | 201 void render_source_resumed(audio_source *src) |
317 if (num_audio_sources < 8) { | 202 { |
318 audio_sources[num_audio_sources++] = src; | 203 if (sync_src == SYNC_AUDIO) { |
319 } | 204 //SDL_PauseAudio acquires the audio device lock, which is held while the callback runs |
320 unlock_audio(); | 205 //since our callback can itself be stuck waiting on the audio_ready condition variable |
321 for (uint8_t i = 0; i < num_inactive_audio_sources; i++) | 206 //calling SDL_PauseAudio(0) again for audio sources after the first can deadlock |
322 { | 207 //fortunately SDL_GetAudioStatus does not acquire the lock so is safe to call here |
323 if (inactive_audio_sources[i] == src) { | 208 if (SDL_GetAudioStatus() == SDL_AUDIO_PAUSED) { |
324 inactive_audio_sources[i] = inactive_audio_sources[--num_inactive_audio_sources]; | 209 SDL_PauseAudio(0); |
325 } | 210 } |
326 } | 211 } |
327 if (sync_to_audio) { | 212 if (current_system && sync_src == SYNC_AUDIO_THREAD) { |
328 SDL_PauseAudio(0); | 213 system_request_exit(current_system, 0); |
329 } | 214 } |
330 } | 215 } |
331 | 216 |
332 void render_free_source(audio_source *src) | 217 void render_do_audio_ready(audio_source *src) |
333 { | 218 { |
334 render_pause_source(src); | 219 if (sync_src == SYNC_AUDIO_THREAD) { |
335 | 220 int16_t *tmp = src->front; |
336 free(src->front); | 221 src->front = src->back; |
337 if (sync_to_audio) { | 222 src->back = tmp; |
338 free(src->back); | 223 src->front_populated = 1; |
339 SDL_DestroyCond(src->cond); | 224 src->buffer_pos = 0; |
340 } | 225 if (all_sources_ready()) { |
341 free(src); | 226 //we've emulated far enough to fill the current buffer |
342 } | 227 system_request_exit(current_system, 0); |
343 static uint32_t sync_samples; | 228 } |
344 static void do_audio_ready(audio_source *src) | 229 } else if (sync_src == SYNC_AUDIO) { |
345 { | |
346 if (sync_to_audio) { | |
347 SDL_LockMutex(audio_mutex); | 230 SDL_LockMutex(audio_mutex); |
348 while (src->front_populated) { | 231 while (src->front_populated) { |
349 SDL_CondWait(src->cond, audio_mutex); | 232 SDL_CondWait(src->opaque, audio_mutex); |
350 } | 233 } |
351 int16_t *tmp = src->front; | 234 int16_t *tmp = src->front; |
352 src->front = src->back; | 235 src->front = src->back; |
353 src->back = tmp; | 236 src->back = tmp; |
354 src->front_populated = 1; | 237 src->front_populated = 1; |
365 SDL_PauseAudio(0); | 248 SDL_PauseAudio(0); |
366 } | 249 } |
367 } | 250 } |
368 } | 251 } |
369 | 252 |
370 static int16_t lowpass_sample(audio_source *src, int16_t last, int16_t current) | |
371 { | |
372 int32_t tmp = current * src->lowpass_alpha + last * (0x10000 - src->lowpass_alpha); | |
373 current = tmp >> 16; | |
374 return current; | |
375 } | |
376 | |
377 static void interp_sample(audio_source *src, int16_t last, int16_t current) | |
378 { | |
379 int64_t tmp = last * ((src->buffer_fraction << 16) / src->buffer_inc); | |
380 tmp += current * (0x10000 - ((src->buffer_fraction << 16) / src->buffer_inc)); | |
381 src->back[src->buffer_pos++] = tmp >> 16; | |
382 } | |
383 | |
384 void render_put_mono_sample(audio_source *src, int16_t value) | |
385 { | |
386 value = lowpass_sample(src, src->last_left, value); | |
387 src->buffer_fraction += src->buffer_inc; | |
388 uint32_t base = sync_to_audio ? 0 : src->read_end; | |
389 while (src->buffer_fraction > BUFFER_INC_RES) | |
390 { | |
391 src->buffer_fraction -= BUFFER_INC_RES; | |
392 interp_sample(src, src->last_left, value); | |
393 | |
394 if (((src->buffer_pos - base) & src->mask) >= sync_samples) { | |
395 do_audio_ready(src); | |
396 } | |
397 src->buffer_pos &= src->mask; | |
398 } | |
399 src->last_left = value; | |
400 } | |
401 | |
402 void render_put_stereo_sample(audio_source *src, int16_t left, int16_t right) | |
403 { | |
404 left = lowpass_sample(src, src->last_left, left); | |
405 right = lowpass_sample(src, src->last_right, right); | |
406 src->buffer_fraction += src->buffer_inc; | |
407 uint32_t base = sync_to_audio ? 0 : src->read_end; | |
408 while (src->buffer_fraction > BUFFER_INC_RES) | |
409 { | |
410 src->buffer_fraction -= BUFFER_INC_RES; | |
411 | |
412 interp_sample(src, src->last_left, left); | |
413 interp_sample(src, src->last_right, right); | |
414 | |
415 if (((src->buffer_pos - base) & src->mask)/2 >= sync_samples) { | |
416 do_audio_ready(src); | |
417 } | |
418 src->buffer_pos &= src->mask; | |
419 } | |
420 src->last_left = left; | |
421 src->last_right = right; | |
422 } | |
423 | |
424 static SDL_Joystick * joysticks[MAX_JOYSTICKS]; | 253 static SDL_Joystick * joysticks[MAX_JOYSTICKS]; |
425 static int joystick_sdl_index[MAX_JOYSTICKS]; | 254 static int joystick_sdl_index[MAX_JOYSTICKS]; |
255 static uint8_t joystick_index_locked[MAX_JOYSTICKS]; | |
426 | 256 |
427 int render_width() | 257 int render_width() |
428 { | 258 { |
429 return main_width; | 259 return main_width; |
430 } | 260 } |
446 #else | 276 #else |
447 return 255 << 24 | r << 16 | g << 8 | b; | 277 return 255 << 24 | r << 16 | g << 8 | b; |
448 #endif | 278 #endif |
449 } | 279 } |
450 | 280 |
281 static uint8_t external_sync; | |
282 void render_set_external_sync(uint8_t ext_sync_on) | |
283 { | |
284 if (ext_sync_on != external_sync) { | |
285 external_sync = ext_sync_on; | |
286 if (windowed_width) { | |
287 //only do this if render_init has already been called | |
288 render_config_updated(); | |
289 } | |
290 } | |
291 } | |
292 | |
451 #ifndef DISABLE_OPENGL | 293 #ifndef DISABLE_OPENGL |
452 static GLuint textures[3], buffers[2], vshader, fshader, program, un_textures[2], un_width, un_height, at_pos; | 294 static GLuint textures[3], buffers[2], vshader, fshader, program, un_textures[2], un_width, un_height, un_texsize, at_pos; |
295 static int tex_width, tex_height; | |
453 | 296 |
454 static GLfloat vertex_data_default[] = { | 297 static GLfloat vertex_data_default[] = { |
455 -1.0f, -1.0f, | 298 -1.0f, -1.0f, |
456 1.0f, -1.0f, | 299 1.0f, -1.0f, |
457 -1.0f, 1.0f, | 300 -1.0f, 1.0f, |
472 "#define highp\n"; | 315 "#define highp\n"; |
473 #endif | 316 #endif |
474 | 317 |
475 static GLuint load_shader(char * fname, GLenum shader_type) | 318 static GLuint load_shader(char * fname, GLenum shader_type) |
476 { | 319 { |
320 char * shader_path; | |
321 FILE *f; | |
322 GLchar *text; | |
323 long fsize; | |
324 #ifndef __ANDROID__ | |
477 char const * parts[] = {get_home_dir(), "/.config/blastem/shaders/", fname}; | 325 char const * parts[] = {get_home_dir(), "/.config/blastem/shaders/", fname}; |
478 char * shader_path = alloc_concat_m(3, parts); | 326 shader_path = alloc_concat_m(3, parts); |
479 FILE * f = fopen(shader_path, "rb"); | 327 f = fopen(shader_path, "rb"); |
480 free(shader_path); | 328 free(shader_path); |
481 if (!f) { | 329 if (f) { |
482 parts[0] = get_exe_dir(); | 330 fsize = file_size(f); |
483 parts[1] = "/shaders/"; | 331 text = malloc(fsize); |
484 shader_path = alloc_concat_m(3, parts); | 332 if (fread(text, 1, fsize, f) != fsize) { |
485 f = fopen(shader_path, "rb"); | 333 warning("Error reading from shader file %s\n", fname); |
334 free(text); | |
335 return 0; | |
336 } | |
337 } else { | |
338 #endif | |
339 shader_path = path_append("shaders", fname); | |
340 uint32_t fsize32; | |
341 text = read_bundled_file(shader_path, &fsize32); | |
486 free(shader_path); | 342 free(shader_path); |
487 if (!f) { | 343 if (!text) { |
488 warning("Failed to open shader file %s for reading\n", fname); | 344 warning("Failed to open shader file %s for reading\n", fname); |
489 return 0; | 345 return 0; |
490 } | 346 } |
491 } | 347 fsize = fsize32; |
492 long fsize = file_size(f); | 348 #ifndef __ANDROID__ |
493 GLchar * text = malloc(fsize); | 349 } |
494 if (fread(text, 1, fsize, f) != fsize) { | 350 #endif |
495 warning("Error reading from shader file %s\n", fname); | 351 text[fsize] = 0; |
496 free(text); | 352 |
497 return 0; | |
498 } | |
499 if (strncmp(text, "#version", strlen("#version"))) { | 353 if (strncmp(text, "#version", strlen("#version"))) { |
500 GLchar *tmp = text; | 354 GLchar *tmp = text; |
501 text = alloc_concat(shader_prefix, tmp); | 355 text = alloc_concat(shader_prefix, tmp); |
502 free(tmp); | 356 free(tmp); |
503 fsize += strlen(shader_prefix); | 357 fsize += strlen(shader_prefix); |
504 } | 358 } |
505 GLuint ret = glCreateShader(shader_type); | 359 GLuint ret = glCreateShader(shader_type); |
360 if (!ret) { | |
361 warning("glCreateShader failed with error %d\n", glGetError()); | |
362 return 0; | |
363 } | |
506 glShaderSource(ret, 1, (const GLchar **)&text, (const GLint *)&fsize); | 364 glShaderSource(ret, 1, (const GLchar **)&text, (const GLint *)&fsize); |
507 free(text); | 365 free(text); |
508 glCompileShader(ret); | 366 glCompileShader(ret); |
509 GLint compile_status, loglen; | 367 GLint compile_status, loglen; |
510 glGetShaderiv(ret, GL_COMPILE_STATUS, &compile_status); | 368 glGetShaderiv(ret, GL_COMPILE_STATUS, &compile_status); |
520 return ret; | 378 return ret; |
521 } | 379 } |
522 #endif | 380 #endif |
523 | 381 |
524 static uint32_t texture_buf[512 * 513]; | 382 static uint32_t texture_buf[512 * 513]; |
525 #ifndef DISABLE_OPENGL | 383 #ifdef DISABLE_OPENGL |
384 #define RENDER_FORMAT SDL_PIXELFORMAT_ARGB8888 | |
385 #else | |
526 #ifdef USE_GLES | 386 #ifdef USE_GLES |
527 #define INTERNAL_FORMAT GL_RGBA | 387 #define INTERNAL_FORMAT GL_RGBA |
528 #define SRC_FORMAT GL_RGBA | 388 #define SRC_FORMAT GL_RGBA |
529 #define RENDER_FORMAT SDL_PIXELFORMAT_ABGR8888 | 389 #define RENDER_FORMAT SDL_PIXELFORMAT_ABGR8888 |
530 #else | 390 #else |
536 { | 396 { |
537 tern_val def = {.ptrval = "linear"}; | 397 tern_val def = {.ptrval = "linear"}; |
538 char *scaling = tern_find_path_default(config, "video\0scaling\0", def, TVAL_PTR).ptrval; | 398 char *scaling = tern_find_path_default(config, "video\0scaling\0", def, TVAL_PTR).ptrval; |
539 GLint filter = strcmp(scaling, "linear") ? GL_NEAREST : GL_LINEAR; | 399 GLint filter = strcmp(scaling, "linear") ? GL_NEAREST : GL_LINEAR; |
540 glGenTextures(3, textures); | 400 glGenTextures(3, textures); |
401 def.ptrval = "off"; | |
402 char *npot_textures = tern_find_path_default(config, "video\0npot_textures\0", def, TVAL_PTR).ptrval; | |
403 if (!strcmp(npot_textures, "on")) { | |
404 tex_width = LINEBUF_SIZE; | |
405 tex_height = 294; //PAL height with full borders | |
406 } else { | |
407 tex_width = tex_height = 512; | |
408 } | |
409 debug_message("Using %dx%d textures\n", tex_width, tex_height); | |
541 for (int i = 0; i < 3; i++) | 410 for (int i = 0; i < 3; i++) |
542 { | 411 { |
543 glBindTexture(GL_TEXTURE_2D, textures[i]); | 412 glBindTexture(GL_TEXTURE_2D, textures[i]); |
544 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); | 413 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); |
545 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); | 414 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); |
546 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | 415 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
547 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | 416 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
548 if (i < 2) { | 417 if (i < 2) { |
549 //TODO: Fixme for PAL + invalid display mode | 418 //TODO: Fixme for PAL + invalid display mode |
550 glTexImage2D(GL_TEXTURE_2D, 0, INTERNAL_FORMAT, 512, 512, 0, SRC_FORMAT, GL_UNSIGNED_BYTE, texture_buf); | 419 glTexImage2D(GL_TEXTURE_2D, 0, INTERNAL_FORMAT, tex_width, tex_height, 0, SRC_FORMAT, GL_UNSIGNED_BYTE, texture_buf); |
551 } else { | 420 } else { |
552 uint32_t blank = 255 << 24; | 421 uint32_t blank = 255 << 24; |
553 glTexImage2D(GL_TEXTURE_2D, 0, INTERNAL_FORMAT, 1, 1, 0, SRC_FORMAT, GL_UNSIGNED_BYTE, &blank); | 422 glTexImage2D(GL_TEXTURE_2D, 0, INTERNAL_FORMAT, 1, 1, 0, SRC_FORMAT, GL_UNSIGNED_BYTE, &blank); |
554 } | 423 } |
555 } | 424 } |
574 } | 443 } |
575 un_textures[0] = glGetUniformLocation(program, "textures[0]"); | 444 un_textures[0] = glGetUniformLocation(program, "textures[0]"); |
576 un_textures[1] = glGetUniformLocation(program, "textures[1]"); | 445 un_textures[1] = glGetUniformLocation(program, "textures[1]"); |
577 un_width = glGetUniformLocation(program, "width"); | 446 un_width = glGetUniformLocation(program, "width"); |
578 un_height = glGetUniformLocation(program, "height"); | 447 un_height = glGetUniformLocation(program, "height"); |
448 un_texsize = glGetUniformLocation(program, "texsize"); | |
579 at_pos = glGetAttribLocation(program, "pos"); | 449 at_pos = glGetAttribLocation(program, "pos"); |
580 } | 450 } |
581 | 451 |
582 static void gl_teardown() | 452 static void gl_teardown() |
583 { | 453 { |
593 static void render_alloc_surfaces() | 463 static void render_alloc_surfaces() |
594 { | 464 { |
595 if (texture_init) { | 465 if (texture_init) { |
596 return; | 466 return; |
597 } | 467 } |
598 sdl_textures= malloc(sizeof(SDL_Texture *) * 2); | 468 sdl_textures= calloc(sizeof(SDL_Texture *), 3); |
599 num_textures = 2; | 469 num_textures = 3; |
600 texture_init = 1; | 470 texture_init = 1; |
601 #ifndef DISABLE_OPENGL | 471 #ifndef DISABLE_OPENGL |
602 if (render_gl) { | 472 if (render_gl) { |
603 sdl_textures[0] = sdl_textures[1] = NULL; | |
604 gl_setup(); | 473 gl_setup(); |
605 } else { | 474 } else { |
606 #endif | 475 #endif |
607 tern_val def = {.ptrval = "linear"}; | 476 tern_val def = {.ptrval = "linear"}; |
608 char *scaling = tern_find_path_default(config, "video\0scaling\0", def, TVAL_PTR).ptrval; | 477 char *scaling = tern_find_path_default(config, "video\0scaling\0", def, TVAL_PTR).ptrval; |
665 } | 534 } |
666 | 535 |
667 static void update_aspect() | 536 static void update_aspect() |
668 { | 537 { |
669 //reset default values | 538 //reset default values |
539 #ifndef DISABLE_OPENGL | |
670 memcpy(vertex_data, vertex_data_default, sizeof(vertex_data)); | 540 memcpy(vertex_data, vertex_data_default, sizeof(vertex_data)); |
541 #endif | |
671 main_clip.w = main_width; | 542 main_clip.w = main_width; |
672 main_clip.h = main_height; | 543 main_clip.h = main_height; |
673 main_clip.x = main_clip.y = 0; | 544 main_clip.x = main_clip.y = 0; |
674 if (config_aspect() > 0.0f) { | 545 if (config_aspect() > 0.0f) { |
675 float aspect = (float)main_width / main_height; | 546 float aspect = (float)main_width / main_height; |
697 } | 568 } |
698 #endif | 569 #endif |
699 } | 570 } |
700 } | 571 } |
701 | 572 |
702 static ui_render_fun on_context_destroyed, on_context_created; | 573 static ui_render_fun on_context_destroyed, on_context_created, on_ui_fb_resized; |
703 void render_set_gl_context_handlers(ui_render_fun destroy, ui_render_fun create) | 574 void render_set_gl_context_handlers(ui_render_fun destroy, ui_render_fun create) |
704 { | 575 { |
705 on_context_destroyed = destroy; | 576 on_context_destroyed = destroy; |
706 on_context_created = create; | 577 on_context_created = create; |
578 } | |
579 | |
580 void render_set_ui_fb_resize_handler(ui_render_fun resize) | |
581 { | |
582 on_ui_fb_resized = resize; | |
707 } | 583 } |
708 | 584 |
709 static uint8_t scancode_map[SDL_NUM_SCANCODES] = { | 585 static uint8_t scancode_map[SDL_NUM_SCANCODES] = { |
710 [SDL_SCANCODE_A] = 0x1C, | 586 [SDL_SCANCODE_A] = 0x1C, |
711 [SDL_SCANCODE_B] = 0x32, | 587 [SDL_SCANCODE_B] = 0x32, |
840 } | 716 } |
841 } | 717 } |
842 return -1; | 718 return -1; |
843 } | 719 } |
844 | 720 |
721 static int lowest_unlocked_joystick_index(void) | |
722 { | |
723 for (int i = 0; i < MAX_JOYSTICKS; i++) { | |
724 if (!joystick_index_locked[i]) { | |
725 return i; | |
726 } | |
727 } | |
728 return -1; | |
729 } | |
730 | |
845 SDL_Joystick *render_get_joystick(int index) | 731 SDL_Joystick *render_get_joystick(int index) |
846 { | 732 { |
847 if (index >= MAX_JOYSTICKS) { | 733 if (index >= MAX_JOYSTICKS) { |
848 return NULL; | 734 return NULL; |
849 } | 735 } |
861 return guid_string; | 747 return guid_string; |
862 } | 748 } |
863 | 749 |
864 SDL_GameController *render_get_controller(int index) | 750 SDL_GameController *render_get_controller(int index) |
865 { | 751 { |
866 if (index >= MAX_JOYSTICKS) { | 752 if (index >= MAX_JOYSTICKS || !joysticks[index]) { |
867 return NULL; | 753 return NULL; |
868 } | 754 } |
869 return SDL_GameControllerOpen(joystick_sdl_index[index]); | 755 return SDL_GameControllerOpen(joystick_sdl_index[index]); |
756 } | |
757 | |
758 static uint8_t gc_events_enabled; | |
759 static SDL_GameController *controllers[MAX_JOYSTICKS]; | |
760 void render_enable_gamepad_events(uint8_t enabled) | |
761 { | |
762 if (enabled != gc_events_enabled) { | |
763 gc_events_enabled = enabled; | |
764 for (int i = 0; i < MAX_JOYSTICKS; i++) { | |
765 if (enabled) { | |
766 controllers[i] = render_get_controller(i); | |
767 } else if (controllers[i]) { | |
768 SDL_GameControllerClose(controllers[i]); | |
769 controllers[i] = NULL; | |
770 } | |
771 } | |
772 } | |
870 } | 773 } |
871 | 774 |
872 static uint32_t overscan_top[NUM_VID_STD] = {2, 21}; | 775 static uint32_t overscan_top[NUM_VID_STD] = {2, 21}; |
873 static uint32_t overscan_bot[NUM_VID_STD] = {1, 17}; | 776 static uint32_t overscan_bot[NUM_VID_STD] = {1, 17}; |
874 static uint32_t overscan_left[NUM_VID_STD] = {13, 13}; | 777 static uint32_t overscan_left[NUM_VID_STD] = {13, 13}; |
875 static uint32_t overscan_right[NUM_VID_STD] = {14, 14}; | 778 static uint32_t overscan_right[NUM_VID_STD] = {14, 14}; |
876 static vid_std video_standard = VID_NTSC; | 779 static vid_std video_standard = VID_NTSC; |
780 static uint8_t need_ui_fb_resize; | |
781 | |
782 int lock_joystick_index(int joystick, int desired_index) | |
783 { | |
784 if (desired_index < 0) { | |
785 desired_index = lowest_unlocked_joystick_index(); | |
786 if (desired_index < 0 || desired_index >= joystick) { | |
787 return joystick; | |
788 } | |
789 } | |
790 SDL_Joystick *tmp_joy = joysticks[joystick]; | |
791 int tmp_index = joystick_sdl_index[joystick]; | |
792 joysticks[joystick] = joysticks[desired_index]; | |
793 joystick_sdl_index[joystick] = joystick_sdl_index[desired_index]; | |
794 joystick_index_locked[joystick] = joystick_sdl_index[desired_index]; | |
795 joysticks[desired_index] = tmp_joy; | |
796 joystick_sdl_index[desired_index] = tmp_index; | |
797 joystick_index_locked[desired_index] = 1; | |
798 //update bindings as the controllers being swapped may have different mappings | |
799 handle_joy_added(desired_index); | |
800 if (joysticks[joystick]) { | |
801 handle_joy_added(joystick); | |
802 } | |
803 return desired_index; | |
804 } | |
877 | 805 |
878 static int32_t handle_event(SDL_Event *event) | 806 static int32_t handle_event(SDL_Event *event) |
879 { | 807 { |
880 if (custom_event_handler) { | 808 if (custom_event_handler) { |
881 custom_event_handler(event); | 809 custom_event_handler(event); |
889 break; | 817 break; |
890 case SDL_JOYBUTTONDOWN: | 818 case SDL_JOYBUTTONDOWN: |
891 handle_joydown(find_joystick_index(event->jbutton.which), event->jbutton.button); | 819 handle_joydown(find_joystick_index(event->jbutton.which), event->jbutton.button); |
892 break; | 820 break; |
893 case SDL_JOYBUTTONUP: | 821 case SDL_JOYBUTTONUP: |
894 handle_joyup(find_joystick_index(event->jbutton.which), event->jbutton.button); | 822 handle_joyup(lock_joystick_index(find_joystick_index(event->jbutton.which), -1), event->jbutton.button); |
895 break; | 823 break; |
896 case SDL_JOYHATMOTION: | 824 case SDL_JOYHATMOTION: |
897 handle_joy_dpad(find_joystick_index(event->jhat.which), event->jhat.hat, event->jhat.value); | 825 handle_joy_dpad(lock_joystick_index(find_joystick_index(event->jhat.which), -1), event->jhat.hat, event->jhat.value); |
898 break; | 826 break; |
899 case SDL_JOYAXISMOTION: | 827 case SDL_JOYAXISMOTION: |
900 handle_joy_axis(find_joystick_index(event->jaxis.which), event->jaxis.axis, event->jaxis.value); | 828 handle_joy_axis(lock_joystick_index(find_joystick_index(event->jaxis.which), -1), event->jaxis.axis, event->jaxis.value); |
901 break; | 829 break; |
902 case SDL_JOYDEVICEADDED: | 830 case SDL_JOYDEVICEADDED: |
903 if (event->jdevice.which < MAX_JOYSTICKS) { | 831 if (event->jdevice.which < MAX_JOYSTICKS) { |
904 int index = lowest_unused_joystick_index(); | 832 int index = lowest_unused_joystick_index(); |
905 if (index >= 0) { | 833 if (index >= 0) { |
906 SDL_Joystick * joy = joysticks[index] = SDL_JoystickOpen(event->jdevice.which); | 834 SDL_Joystick * joy = joysticks[index] = SDL_JoystickOpen(event->jdevice.which); |
907 joystick_sdl_index[index] = event->jdevice.which; | 835 joystick_sdl_index[index] = event->jdevice.which; |
836 joystick_index_locked[index] = 0; | |
837 if (gc_events_enabled) { | |
838 controllers[index] = SDL_GameControllerOpen(event->jdevice.which); | |
839 } | |
908 if (joy) { | 840 if (joy) { |
909 printf("Joystick %d added: %s\n", index, SDL_JoystickName(joy)); | 841 debug_message("Joystick %d added: %s\n", index, SDL_JoystickName(joy)); |
910 printf("\tNum Axes: %d\n\tNum Buttons: %d\n\tNum Hats: %d\n", SDL_JoystickNumAxes(joy), SDL_JoystickNumButtons(joy), SDL_JoystickNumHats(joy)); | 842 debug_message("\tNum Axes: %d\n\tNum Buttons: %d\n\tNum Hats: %d\n", SDL_JoystickNumAxes(joy), SDL_JoystickNumButtons(joy), SDL_JoystickNumHats(joy)); |
911 handle_joy_added(index); | 843 handle_joy_added(index); |
912 } | 844 } |
913 } | 845 } |
914 } | 846 } |
915 break; | 847 break; |
916 case SDL_JOYDEVICEREMOVED: { | 848 case SDL_JOYDEVICEREMOVED: { |
917 int index = find_joystick_index(event->jdevice.which); | 849 int index = find_joystick_index(event->jdevice.which); |
918 if (index >= 0) { | 850 if (index >= 0) { |
919 SDL_JoystickClose(joysticks[index]); | 851 SDL_JoystickClose(joysticks[index]); |
920 joysticks[index] = NULL; | 852 joysticks[index] = NULL; |
921 printf("Joystick %d removed\n", index); | 853 if (controllers[index]) { |
854 SDL_GameControllerClose(controllers[index]); | |
855 controllers[index] = NULL; | |
856 } | |
857 debug_message("Joystick %d removed\n", index); | |
922 } else { | 858 } else { |
923 printf("Failed to find removed joystick with instance ID: %d\n", index); | 859 debug_message("Failed to find removed joystick with instance ID: %d\n", index); |
924 } | 860 } |
925 break; | 861 break; |
926 } | 862 } |
927 case SDL_MOUSEMOTION: | 863 case SDL_MOUSEMOTION: |
928 handle_mouse_moved(event->motion.which, event->motion.x, event->motion.y + overscan_top[video_standard], event->motion.xrel, event->motion.yrel); | 864 handle_mouse_moved(event->motion.which, event->motion.x, event->motion.y + overscan_top[video_standard], event->motion.xrel, event->motion.yrel); |
935 break; | 871 break; |
936 case SDL_WINDOWEVENT: | 872 case SDL_WINDOWEVENT: |
937 switch (event->window.event) | 873 switch (event->window.event) |
938 { | 874 { |
939 case SDL_WINDOWEVENT_SIZE_CHANGED: | 875 case SDL_WINDOWEVENT_SIZE_CHANGED: |
876 if (!main_window) { | |
877 break; | |
878 } | |
940 main_width = event->window.data1; | 879 main_width = event->window.data1; |
941 main_height = event->window.data2; | 880 main_height = event->window.data2; |
881 need_ui_fb_resize = 1; | |
942 update_aspect(); | 882 update_aspect(); |
943 #ifndef DISABLE_OPENGL | 883 #ifndef DISABLE_OPENGL |
944 if (render_gl) { | 884 if (render_gl) { |
945 if (on_context_destroyed) { | 885 if (on_context_destroyed) { |
946 on_context_destroyed(); | 886 on_context_destroyed(); |
954 } | 894 } |
955 } | 895 } |
956 #endif | 896 #endif |
957 break; | 897 break; |
958 case SDL_WINDOWEVENT_CLOSE: | 898 case SDL_WINDOWEVENT_CLOSE: |
959 if (SDL_GetWindowID(main_window) == event->window.windowID) { | 899 if (main_window && SDL_GetWindowID(main_window) == event->window.windowID) { |
960 exit(0); | 900 exit(0); |
961 } else { | 901 } else { |
962 for (int i = 0; i < num_textures - FRAMEBUFFER_USER_START; i++) | 902 for (int i = 0; i < num_textures - FRAMEBUFFER_USER_START; i++) |
963 { | 903 { |
964 if (SDL_GetWindowID(extra_windows[i]) == event->window.windowID) { | 904 if (SDL_GetWindowID(extra_windows[i]) == event->window.windowID) { |
999 static int source_hz; | 939 static int source_hz; |
1000 static int source_frame; | 940 static int source_frame; |
1001 static int source_frame_count; | 941 static int source_frame_count; |
1002 static int frame_repeat[60]; | 942 static int frame_repeat[60]; |
1003 | 943 |
944 static uint32_t sample_rate; | |
1004 static void init_audio() | 945 static void init_audio() |
1005 { | 946 { |
1006 SDL_AudioSpec desired, actual; | 947 SDL_AudioSpec desired, actual; |
1007 char * rate_str = tern_find_path(config, "audio\0rate\0", TVAL_PTR).ptrval; | 948 char * rate_str = tern_find_path(config, "audio\0rate\0", TVAL_PTR).ptrval; |
1008 int rate = rate_str ? atoi(rate_str) : 0; | 949 int rate = rate_str ? atoi(rate_str) : 0; |
1009 if (!rate) { | 950 if (!rate) { |
1010 rate = 48000; | 951 rate = 48000; |
1011 } | 952 } |
1012 desired.freq = rate; | 953 desired.freq = rate; |
1013 desired.format = AUDIO_S16SYS; | 954 char *config_format = tern_find_path_default(config, "audio\0format\0", (tern_val){.ptrval="f32"}, TVAL_PTR).ptrval; |
955 desired.format = !strcmp(config_format, "s16") ? AUDIO_S16SYS : AUDIO_F32SYS; | |
1014 desired.channels = 2; | 956 desired.channels = 2; |
1015 char * samples_str = tern_find_path(config, "audio\0buffer\0", TVAL_PTR).ptrval; | 957 char * samples_str = tern_find_path(config, "audio\0buffer\0", TVAL_PTR).ptrval; |
1016 int samples = samples_str ? atoi(samples_str) : 0; | 958 int samples = samples_str ? atoi(samples_str) : 0; |
1017 if (!samples) { | 959 if (!samples) { |
1018 samples = 512; | 960 samples = 512; |
1019 } | 961 } |
1020 printf("config says: %d\n", samples); | 962 debug_message("config says: %d\n", samples); |
1021 desired.samples = samples*2; | 963 desired.samples = samples*2; |
1022 desired.callback = sync_to_audio ? audio_callback : audio_callback_drc; | 964 switch (sync_src) |
965 { | |
966 case SYNC_AUDIO: | |
967 desired.callback = audio_callback; | |
968 break; | |
969 case SYNC_AUDIO_THREAD: | |
970 desired.callback = audio_callback_run_on_audio; | |
971 break; | |
972 default: | |
973 desired.callback = audio_callback_drc; | |
974 } | |
1023 desired.userdata = NULL; | 975 desired.userdata = NULL; |
1024 | 976 |
1025 if (SDL_OpenAudio(&desired, &actual) < 0) { | 977 if (SDL_OpenAudio(&desired, &actual) < 0) { |
1026 fatal_error("Unable to open SDL audio: %s\n", SDL_GetError()); | 978 fatal_error("Unable to open SDL audio: %s\n", SDL_GetError()); |
1027 } | 979 } |
1028 buffer_samples = actual.samples; | |
1029 sample_rate = actual.freq; | 980 sample_rate = actual.freq; |
1030 printf("Initialized audio at frequency %d with a %d sample buffer, ", actual.freq, actual.samples); | 981 debug_message("Initialized audio at frequency %d with a %d sample buffer, ", actual.freq, actual.samples); |
982 render_audio_format format = RENDER_AUDIO_UNKNOWN; | |
1031 if (actual.format == AUDIO_S16SYS) { | 983 if (actual.format == AUDIO_S16SYS) { |
1032 puts("signed 16-bit int format"); | 984 debug_message("signed 16-bit int format\n"); |
1033 mix = mix_s16; | 985 format = RENDER_AUDIO_S16; |
1034 } else if (actual.format == AUDIO_F32SYS) { | 986 } else if (actual.format == AUDIO_F32SYS) { |
1035 puts("32-bit float format"); | 987 debug_message("32-bit float format\n"); |
1036 mix = mix_f32; | 988 format = RENDER_AUDIO_FLOAT; |
1037 } else { | 989 } else { |
1038 printf("unsupported format %X\n", actual.format); | 990 debug_message("unsupported format %X\n", actual.format); |
1039 warning("Unsupported audio sample format: %X\n", actual.format); | 991 warning("Unsupported audio sample format: %X\n", actual.format); |
1040 mix = mix_null; | 992 } |
1041 } | 993 render_audio_initialized(format, actual.freq, actual.channels, actual.samples, SDL_AUDIO_BITSIZE(actual.format) / 8); |
1042 } | 994 } |
1043 | 995 |
1044 void window_setup(void) | 996 void window_setup(void) |
1045 { | 997 { |
1046 uint32_t flags = SDL_WINDOW_RESIZABLE; | 998 uint32_t flags = SDL_WINDOW_RESIZABLE; |
1047 if (is_fullscreen) { | 999 if (is_fullscreen) { |
1048 flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; | 1000 flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; |
1049 } | 1001 } |
1050 | 1002 |
1051 tern_val def = {.ptrval = "audio"}; | 1003 tern_val def = {.ptrval = "audio"}; |
1052 char *sync_src = tern_find_path_default(config, "system\0sync_source\0", def, TVAL_PTR).ptrval; | 1004 if (external_sync) { |
1053 sync_to_audio = !strcmp(sync_src, "audio"); | 1005 sync_src = SYNC_EXTERNAL; |
1006 } else { | |
1007 char *sync_src_str = tern_find_path_default(config, "system\0sync_source\0", def, TVAL_PTR).ptrval; | |
1008 if (!strcmp(sync_src_str, "audio")) { | |
1009 sync_src = SYNC_AUDIO; | |
1010 } else if (!strcmp(sync_src_str, "audio_thread")) { | |
1011 sync_src = SYNC_AUDIO_THREAD; | |
1012 } else { | |
1013 sync_src = SYNC_VIDEO; | |
1014 } | |
1015 } | |
1016 | |
1017 if (!num_buffers && (sync_src == SYNC_AUDIO_THREAD || sync_src == SYNC_EXTERNAL)) { | |
1018 frame_mutex = SDL_CreateMutex(); | |
1019 free_buffer_mutex = SDL_CreateMutex(); | |
1020 frame_ready = SDL_CreateCond(); | |
1021 buffer_storage = 4; | |
1022 frame_buffers = calloc(buffer_storage, sizeof(uint32_t*)); | |
1023 frame_buffers[0] = texture_buf; | |
1024 num_buffers = 1; | |
1025 } | |
1054 | 1026 |
1055 const char *vsync; | 1027 const char *vsync; |
1056 if (sync_to_audio) { | 1028 if (sync_src == SYNC_AUDIO) { |
1057 def.ptrval = "off"; | 1029 def.ptrval = "off"; |
1058 vsync = tern_find_path_default(config, "video\0vsync\0", def, TVAL_PTR).ptrval; | 1030 vsync = tern_find_path_default(config, "video\0vsync\0", def, TVAL_PTR).ptrval; |
1059 } else { | 1031 } else { |
1060 vsync = "on"; | 1032 vsync = "on"; |
1061 } | 1033 } |
1135 vsync = NULL; | 1107 vsync = NULL; |
1136 } | 1108 } |
1137 } | 1109 } |
1138 if (vsync) { | 1110 if (vsync) { |
1139 if (SDL_GL_SetSwapInterval(!strcmp("on", vsync)) < 0) { | 1111 if (SDL_GL_SetSwapInterval(!strcmp("on", vsync)) < 0) { |
1112 #ifdef __ANDROID__ | |
1113 debug_message("Failed to set vsync to %s: %s\n", vsync, SDL_GetError()); | |
1114 #else | |
1140 warning("Failed to set vsync to %s: %s\n", vsync, SDL_GetError()); | 1115 warning("Failed to set vsync to %s: %s\n", vsync, SDL_GetError()); |
1116 #endif | |
1141 } | 1117 } |
1142 } | 1118 } |
1143 } else { | 1119 } else { |
1144 warning("OpenGL 2.0 is unavailable, falling back to SDL2 renderer\n"); | 1120 warning("OpenGL 2.0 is unavailable, falling back to SDL2 renderer\n"); |
1145 } | 1121 } |
1155 if (!main_renderer) { | 1131 if (!main_renderer) { |
1156 fatal_error("unable to create SDL renderer: %s\n", SDL_GetError()); | 1132 fatal_error("unable to create SDL renderer: %s\n", SDL_GetError()); |
1157 } | 1133 } |
1158 SDL_RendererInfo rinfo; | 1134 SDL_RendererInfo rinfo; |
1159 SDL_GetRendererInfo(main_renderer, &rinfo); | 1135 SDL_GetRendererInfo(main_renderer, &rinfo); |
1160 printf("SDL2 Render Driver: %s\n", rinfo.name); | 1136 debug_message("SDL2 Render Driver: %s\n", rinfo.name); |
1161 main_clip.x = main_clip.y = 0; | 1137 main_clip.x = main_clip.y = 0; |
1162 main_clip.w = main_width; | 1138 main_clip.w = main_width; |
1163 main_clip.h = main_height; | 1139 main_clip.h = main_height; |
1164 #ifndef DISABLE_OPENGL | 1140 #ifndef DISABLE_OPENGL |
1165 } | 1141 } |
1166 #endif | 1142 #endif |
1167 | 1143 |
1168 SDL_GetWindowSize(main_window, &main_width, &main_height); | 1144 SDL_GetWindowSize(main_window, &main_width, &main_height); |
1169 printf("Window created with size: %d x %d\n", main_width, main_height); | 1145 debug_message("Window created with size: %d x %d\n", main_width, main_height); |
1170 update_aspect(); | 1146 update_aspect(); |
1171 render_alloc_surfaces(); | 1147 render_alloc_surfaces(); |
1172 def.ptrval = "off"; | 1148 def.ptrval = "off"; |
1173 scanlines = !strcmp(tern_find_path_default(config, "video\0scanlines\0", def, TVAL_PTR).ptrval, "on"); | 1149 scanlines = !strcmp(tern_find_path_default(config, "video\0scanlines\0", def, TVAL_PTR).ptrval, "on"); |
1174 } | 1150 } |
1181 atexit(SDL_Quit); | 1157 atexit(SDL_Quit); |
1182 if (height <= 0) { | 1158 if (height <= 0) { |
1183 float aspect = config_aspect() > 0.0f ? config_aspect() : 4.0f/3.0f; | 1159 float aspect = config_aspect() > 0.0f ? config_aspect() : 4.0f/3.0f; |
1184 height = ((float)width / aspect) + 0.5f; | 1160 height = ((float)width / aspect) + 0.5f; |
1185 } | 1161 } |
1186 printf("width: %d, height: %d\n", width, height); | 1162 debug_message("width: %d, height: %d\n", width, height); |
1187 windowed_width = width; | 1163 windowed_width = width; |
1188 windowed_height = height; | 1164 windowed_height = height; |
1189 | 1165 |
1190 SDL_DisplayMode mode; | 1166 SDL_DisplayMode mode; |
1191 //TODO: Explicit multiple monitor support | 1167 //TODO: Explicit multiple monitor support |
1214 uint32_t db_size; | 1190 uint32_t db_size; |
1215 char *db_data = read_bundled_file("gamecontrollerdb.txt", &db_size); | 1191 char *db_data = read_bundled_file("gamecontrollerdb.txt", &db_size); |
1216 if (db_data) { | 1192 if (db_data) { |
1217 int added = SDL_GameControllerAddMappingsFromRW(SDL_RWFromMem(db_data, db_size), 1); | 1193 int added = SDL_GameControllerAddMappingsFromRW(SDL_RWFromMem(db_data, db_size), 1); |
1218 free(db_data); | 1194 free(db_data); |
1219 printf("Added %d game controller mappings from gamecontrollerdb.txt\n", added); | 1195 debug_message("Added %d game controller mappings from gamecontrollerdb.txt\n", added); |
1220 } | 1196 } |
1221 | 1197 |
1222 controller_add_mappings(); | 1198 controller_add_mappings(); |
1223 | 1199 |
1224 SDL_JoystickEventState(SDL_ENABLE); | 1200 SDL_JoystickEventState(SDL_ENABLE); |
1225 | 1201 |
1226 render_set_video_standard(VID_NTSC); | 1202 render_set_video_standard(VID_NTSC); |
1227 | 1203 |
1228 atexit(render_quit); | 1204 atexit(render_quit); |
1229 } | 1205 } |
1230 #include<unistd.h> | 1206 |
1207 void render_reset_mappings(void) | |
1208 { | |
1209 SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER); | |
1210 SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER); | |
1211 uint32_t db_size; | |
1212 char *db_data = read_bundled_file("gamecontrollerdb.txt", &db_size); | |
1213 if (db_data) { | |
1214 int added = SDL_GameControllerAddMappingsFromRW(SDL_RWFromMem(db_data, db_size), 1); | |
1215 free(db_data); | |
1216 debug_message("Added %d game controller mappings from gamecontrollerdb.txt\n", added); | |
1217 } | |
1218 } | |
1231 static int in_toggle; | 1219 static int in_toggle; |
1232 static void update_source(audio_source *src, double rc, uint8_t sync_changed) | |
1233 { | |
1234 double alpha = src->dt / (src->dt + rc); | |
1235 int32_t lowpass_alpha = (int32_t)(((double)0x10000) * alpha); | |
1236 src->lowpass_alpha = lowpass_alpha; | |
1237 if (sync_changed) { | |
1238 uint32_t alloc_size = sync_to_audio ? src->num_channels * buffer_samples : nearest_pow2(min_buffered * 4 * src->num_channels); | |
1239 src->back = realloc(src->back, alloc_size * sizeof(int16_t)); | |
1240 if (sync_to_audio) { | |
1241 src->front = malloc(alloc_size * sizeof(int16_t)); | |
1242 } else { | |
1243 free(src->front); | |
1244 src->front = src->back; | |
1245 } | |
1246 src->mask = sync_to_audio ? 0xFFFFFFFF : alloc_size-1; | |
1247 src->read_start = 0; | |
1248 src->read_end = sync_to_audio ? buffer_samples * src->num_channels : 0; | |
1249 src->buffer_pos = 0; | |
1250 } | |
1251 } | |
1252 | 1220 |
1253 void render_config_updated(void) | 1221 void render_config_updated(void) |
1254 { | 1222 { |
1255 uint8_t old_sync_to_audio = sync_to_audio; | |
1256 | |
1257 free_surfaces(); | 1223 free_surfaces(); |
1258 #ifndef DISABLE_OPENGL | 1224 #ifndef DISABLE_OPENGL |
1259 if (render_gl) { | 1225 if (render_gl) { |
1260 if (on_context_destroyed) { | 1226 if (on_context_destroyed) { |
1261 on_context_destroyed(); | 1227 on_context_destroyed(); |
1268 #ifndef DISABLE_OPENGL | 1234 #ifndef DISABLE_OPENGL |
1269 } | 1235 } |
1270 #endif | 1236 #endif |
1271 in_toggle = 1; | 1237 in_toggle = 1; |
1272 SDL_DestroyWindow(main_window); | 1238 SDL_DestroyWindow(main_window); |
1239 main_window = NULL; | |
1273 drain_events(); | 1240 drain_events(); |
1274 | 1241 |
1275 char *config_width = tern_find_path(config, "video\0width\0", TVAL_PTR).ptrval; | 1242 char *config_width = tern_find_path(config, "video\0width\0", TVAL_PTR).ptrval; |
1276 if (config_width) { | 1243 if (config_width) { |
1277 windowed_width = atoi(config_width); | 1244 windowed_width = atoi(config_width); |
1293 main_height = mode.h; | 1260 main_height = mode.h; |
1294 } else { | 1261 } else { |
1295 main_width = windowed_width; | 1262 main_width = windowed_width; |
1296 main_height = windowed_height; | 1263 main_height = windowed_height; |
1297 } | 1264 } |
1265 if (on_ui_fb_resized) { | |
1266 on_ui_fb_resized(); | |
1267 } | |
1298 | 1268 |
1299 window_setup(); | 1269 window_setup(); |
1300 update_aspect(); | 1270 update_aspect(); |
1301 #ifndef DISABLE_OPENGL | 1271 #ifndef DISABLE_OPENGL |
1302 //need to check render_gl again after window_setup as render option could have changed | 1272 //need to check render_gl again after window_setup as render option could have changed |
1309 render_close_audio(); | 1279 render_close_audio(); |
1310 quitting = 0; | 1280 quitting = 0; |
1311 init_audio(); | 1281 init_audio(); |
1312 render_set_video_standard(video_standard); | 1282 render_set_video_standard(video_standard); |
1313 | 1283 |
1314 double lowpass_cutoff = get_lowpass_cutoff(config); | |
1315 double rc = (1.0 / lowpass_cutoff) / (2.0 * M_PI); | |
1316 lock_audio(); | |
1317 for (uint8_t i = 0; i < num_audio_sources; i++) | |
1318 { | |
1319 update_source(audio_sources[i], rc, old_sync_to_audio != sync_to_audio); | |
1320 } | |
1321 unlock_audio(); | |
1322 for (uint8_t i = 0; i < num_inactive_audio_sources; i++) | |
1323 { | |
1324 update_source(inactive_audio_sources[i], rc, old_sync_to_audio != sync_to_audio); | |
1325 } | |
1326 drain_events(); | 1284 drain_events(); |
1327 in_toggle = 0; | 1285 in_toggle = 0; |
1328 if (!was_paused) { | 1286 if (!was_paused) { |
1329 SDL_PauseAudio(0); | 1287 SDL_PauseAudio(0); |
1330 } | 1288 } |
1333 SDL_Window *render_get_window(void) | 1291 SDL_Window *render_get_window(void) |
1334 { | 1292 { |
1335 return main_window; | 1293 return main_window; |
1336 } | 1294 } |
1337 | 1295 |
1296 uint32_t render_audio_syncs_per_sec(void) | |
1297 { | |
1298 //sync samples with audio thread approximately every 8 lines when doing sync to video | |
1299 return render_is_audio_sync() ? 0 : source_hz * (video_standard == VID_PAL ? 313 : 262) / 8; | |
1300 } | |
1301 | |
1338 void render_set_video_standard(vid_std std) | 1302 void render_set_video_standard(vid_std std) |
1339 { | 1303 { |
1340 video_standard = std; | 1304 video_standard = std; |
1305 if (render_is_audio_sync()) { | |
1306 return; | |
1307 } | |
1341 source_hz = std == VID_PAL ? 50 : 60; | 1308 source_hz = std == VID_PAL ? 50 : 60; |
1342 uint32_t max_repeat = 0; | 1309 uint32_t max_repeat = 0; |
1343 if (abs(source_hz - display_hz) < 2) { | 1310 if (abs(source_hz - display_hz) < 2) { |
1344 memset(frame_repeat, 0, sizeof(int)*display_hz); | 1311 memset(frame_repeat, 0, sizeof(int)*display_hz); |
1345 } else { | 1312 } else { |
1362 frame_repeat[source_hz-1] += display_hz - dst_frames; | 1329 frame_repeat[source_hz-1] += display_hz - dst_frames; |
1363 } | 1330 } |
1364 } | 1331 } |
1365 source_frame = 0; | 1332 source_frame = 0; |
1366 source_frame_count = frame_repeat[0]; | 1333 source_frame_count = frame_repeat[0]; |
1367 //sync samples with audio thread approximately every 8 lines | |
1368 sync_samples = sync_to_audio ? buffer_samples : 8 * sample_rate / (source_hz * (VID_PAL ? 313 : 262)); | |
1369 max_repeat++; | 1334 max_repeat++; |
1370 min_buffered = (((float)max_repeat * (float)sample_rate/(float)source_hz)/* / (float)buffer_samples*/);// + 0.9999; | 1335 min_buffered = (((float)max_repeat * (float)sample_rate/(float)source_hz)/* / (float)buffer_samples*/);// + 0.9999; |
1371 //min_buffered *= buffer_samples; | 1336 //min_buffered *= buffer_samples; |
1372 printf("Min samples buffered before audio start: %d\n", min_buffered); | 1337 debug_message("Min samples buffered before audio start: %d\n", min_buffered); |
1373 max_adjust = BASE_MAX_ADJUST / source_hz; | 1338 max_adjust = BASE_MAX_ADJUST / source_hz; |
1374 } | 1339 } |
1375 | 1340 |
1376 void render_update_caption(char *title) | 1341 void render_update_caption(char *title) |
1377 { | 1342 { |
1446 | 1411 |
1447 uint32_t *locked_pixels; | 1412 uint32_t *locked_pixels; |
1448 uint32_t locked_pitch; | 1413 uint32_t locked_pitch; |
1449 uint32_t *render_get_framebuffer(uint8_t which, int *pitch) | 1414 uint32_t *render_get_framebuffer(uint8_t which, int *pitch) |
1450 { | 1415 { |
1416 if (sync_src == SYNC_AUDIO_THREAD || sync_src == SYNC_EXTERNAL) { | |
1417 *pitch = LINEBUF_SIZE * sizeof(uint32_t); | |
1418 uint32_t *buffer; | |
1419 SDL_LockMutex(free_buffer_mutex); | |
1420 if (num_buffers) { | |
1421 buffer = frame_buffers[--num_buffers]; | |
1422 } else { | |
1423 buffer = calloc(tex_width*(tex_height + 1), sizeof(uint32_t)); | |
1424 } | |
1425 SDL_UnlockMutex(free_buffer_mutex); | |
1426 locked_pixels = buffer; | |
1427 return buffer; | |
1428 } | |
1451 #ifndef DISABLE_OPENGL | 1429 #ifndef DISABLE_OPENGL |
1452 if (render_gl && which <= FRAMEBUFFER_EVEN) { | 1430 if (render_gl && which <= FRAMEBUFFER_EVEN) { |
1453 *pitch = LINEBUF_SIZE * sizeof(uint32_t); | 1431 *pitch = LINEBUF_SIZE * sizeof(uint32_t); |
1454 return texture_buf; | 1432 return texture_buf; |
1455 } else { | 1433 } else { |
1456 #endif | 1434 #endif |
1435 if (which == FRAMEBUFFER_UI && !sdl_textures[which]) { | |
1436 sdl_textures[which] = SDL_CreateTexture(main_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, main_width, main_height); | |
1437 } | |
1457 if (which >= num_textures) { | 1438 if (which >= num_textures) { |
1458 warning("Request for invalid framebuffer number %d\n", which); | 1439 warning("Request for invalid framebuffer number %d\n", which); |
1459 return NULL; | 1440 return NULL; |
1460 } | 1441 } |
1461 void *pixels; | 1442 uint8_t *pixels; |
1462 if (SDL_LockTexture(sdl_textures[which], NULL, &pixels, pitch) < 0) { | 1443 if (SDL_LockTexture(sdl_textures[which], NULL, (void **)&pixels, pitch) < 0) { |
1463 warning("Failed to lock texture: %s\n", SDL_GetError()); | 1444 warning("Failed to lock texture: %s\n", SDL_GetError()); |
1464 return NULL; | 1445 return NULL; |
1465 } | 1446 } |
1466 static uint8_t last; | 1447 static uint8_t last; |
1467 if (which <= FRAMEBUFFER_EVEN) { | 1448 if (which <= FRAMEBUFFER_EVEN) { |
1468 locked_pixels = pixels; | 1449 locked_pixels = (uint32_t *)pixels; |
1469 if (which == FRAMEBUFFER_EVEN) { | 1450 if (which == FRAMEBUFFER_EVEN) { |
1470 pixels += *pitch; | 1451 pixels += *pitch; |
1471 } | 1452 } |
1472 locked_pitch = *pitch; | 1453 locked_pitch = *pitch; |
1473 if (which != last) { | 1454 if (which != last) { |
1474 *pitch *= 2; | 1455 *pitch *= 2; |
1475 } | 1456 } |
1476 last = which; | 1457 last = which; |
1477 } | 1458 } |
1478 return pixels; | 1459 return (uint32_t *)pixels; |
1479 #ifndef DISABLE_OPENGL | 1460 #ifndef DISABLE_OPENGL |
1480 } | 1461 } |
1481 #endif | 1462 #endif |
1463 } | |
1464 | |
1465 static void release_buffer(uint32_t *buffer) | |
1466 { | |
1467 SDL_LockMutex(free_buffer_mutex); | |
1468 if (num_buffers == buffer_storage) { | |
1469 buffer_storage *= 2; | |
1470 frame_buffers = realloc(frame_buffers, sizeof(uint32_t*)*buffer_storage); | |
1471 } | |
1472 frame_buffers[num_buffers++] = buffer; | |
1473 SDL_UnlockMutex(free_buffer_mutex); | |
1482 } | 1474 } |
1483 | 1475 |
1484 uint8_t events_processed; | 1476 uint8_t events_processed; |
1485 #ifdef __ANDROID__ | 1477 #ifdef __ANDROID__ |
1486 #define FPS_INTERVAL 10000 | 1478 #define FPS_INTERVAL 10000 |
1488 #define FPS_INTERVAL 1000 | 1480 #define FPS_INTERVAL 1000 |
1489 #endif | 1481 #endif |
1490 | 1482 |
1491 static uint32_t last_width, last_height; | 1483 static uint32_t last_width, last_height; |
1492 static uint8_t interlaced; | 1484 static uint8_t interlaced; |
1493 void render_framebuffer_updated(uint8_t which, int width) | 1485 static void process_framebuffer(uint32_t *buffer, uint8_t which, int width) |
1494 { | 1486 { |
1495 static uint8_t last; | 1487 static uint8_t last; |
1496 if (!sync_to_audio && which <= FRAMEBUFFER_EVEN && source_frame_count < 0) { | 1488 if (sync_src == SYNC_VIDEO && which <= FRAMEBUFFER_EVEN && source_frame_count < 0) { |
1497 source_frame++; | 1489 source_frame++; |
1498 if (source_frame >= source_hz) { | 1490 if (source_frame >= source_hz) { |
1499 source_frame = 0; | 1491 source_frame = 0; |
1500 } | 1492 } |
1501 source_frame_count = frame_repeat[source_frame]; | 1493 source_frame_count = frame_repeat[source_frame]; |
1514 screenshot_file = fopen(screenshot_path, "wb"); | 1506 screenshot_file = fopen(screenshot_path, "wb"); |
1515 if (screenshot_file) { | 1507 if (screenshot_file) { |
1516 #ifndef DISABLE_ZLIB | 1508 #ifndef DISABLE_ZLIB |
1517 ext = path_extension(screenshot_path); | 1509 ext = path_extension(screenshot_path); |
1518 #endif | 1510 #endif |
1519 info_message("Saving screenshot to %s\n", screenshot_path); | 1511 debug_message("Saving screenshot to %s\n", screenshot_path); |
1520 } else { | 1512 } else { |
1521 warning("Failed to open screenshot file %s for writing\n", screenshot_path); | 1513 warning("Failed to open screenshot file %s for writing\n", screenshot_path); |
1522 } | 1514 } |
1523 free(screenshot_path); | 1515 free(screenshot_path); |
1524 screenshot_path = NULL; | 1516 screenshot_path = NULL; |
1529 width -= overscan_left[video_standard] + overscan_right[video_standard]; | 1521 width -= overscan_left[video_standard] + overscan_right[video_standard]; |
1530 #ifndef DISABLE_OPENGL | 1522 #ifndef DISABLE_OPENGL |
1531 if (render_gl && which <= FRAMEBUFFER_EVEN) { | 1523 if (render_gl && which <= FRAMEBUFFER_EVEN) { |
1532 SDL_GL_MakeCurrent(main_window, main_context); | 1524 SDL_GL_MakeCurrent(main_window, main_context); |
1533 glBindTexture(GL_TEXTURE_2D, textures[which]); | 1525 glBindTexture(GL_TEXTURE_2D, textures[which]); |
1534 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]); | 1526 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]); |
1535 | 1527 |
1536 if (screenshot_file) { | 1528 if (screenshot_file) { |
1537 //properly supporting interlaced modes here is non-trivial, so only save the odd field for now | 1529 //properly supporting interlaced modes here is non-trivial, so only save the odd field for now |
1538 #ifndef DISABLE_ZLIB | 1530 #ifndef DISABLE_ZLIB |
1539 if (!strcasecmp(ext, "png")) { | 1531 if (!strcasecmp(ext, "png")) { |
1540 free(ext); | 1532 free(ext); |
1541 save_png(screenshot_file, texture_buf, shot_width, shot_height, LINEBUF_SIZE*sizeof(uint32_t)); | 1533 save_png(screenshot_file, buffer, shot_width, shot_height, LINEBUF_SIZE*sizeof(uint32_t)); |
1542 } else { | 1534 } else { |
1543 free(ext); | 1535 free(ext); |
1544 #endif | 1536 #endif |
1545 save_ppm(screenshot_file, texture_buf, shot_width, shot_height, LINEBUF_SIZE*sizeof(uint32_t)); | 1537 save_ppm(screenshot_file, buffer, shot_width, shot_height, LINEBUF_SIZE*sizeof(uint32_t)); |
1546 #ifndef DISABLE_ZLIB | 1538 #ifndef DISABLE_ZLIB |
1547 } | 1539 } |
1548 #endif | 1540 #endif |
1549 } | 1541 } |
1550 } else { | 1542 } else { |
1551 #endif | 1543 #endif |
1544 //TODO: Support SYNC_AUDIO_THREAD/SYNC_EXTERNAL for render API framebuffers | |
1552 if (which <= FRAMEBUFFER_EVEN && last != which) { | 1545 if (which <= FRAMEBUFFER_EVEN && last != which) { |
1553 uint8_t *cur_dst = (uint8_t *)locked_pixels; | 1546 uint8_t *cur_dst = (uint8_t *)locked_pixels; |
1554 uint8_t *cur_saved = (uint8_t *)texture_buf; | 1547 uint8_t *cur_saved = (uint8_t *)texture_buf; |
1555 uint32_t dst_off = which == FRAMEBUFFER_EVEN ? 0 : locked_pitch; | 1548 uint32_t dst_off = which == FRAMEBUFFER_EVEN ? 0 : locked_pitch; |
1556 uint32_t src_off = which == FRAMEBUFFER_EVEN ? locked_pitch : 0; | 1549 uint32_t src_off = which == FRAMEBUFFER_EVEN ? locked_pitch : 0; |
1589 } | 1582 } |
1590 #endif | 1583 #endif |
1591 last_height = height; | 1584 last_height = height; |
1592 if (which <= FRAMEBUFFER_EVEN) { | 1585 if (which <= FRAMEBUFFER_EVEN) { |
1593 render_update_display(); | 1586 render_update_display(); |
1587 } else if (which == FRAMEBUFFER_UI) { | |
1588 SDL_RenderCopy(main_renderer, sdl_textures[which], NULL, NULL); | |
1589 if (need_ui_fb_resize) { | |
1590 SDL_DestroyTexture(sdl_textures[which]); | |
1591 sdl_textures[which] = NULL; | |
1592 if (on_ui_fb_resized) { | |
1593 on_ui_fb_resized(); | |
1594 } | |
1595 need_ui_fb_resize = 0; | |
1596 } | |
1594 } else { | 1597 } else { |
1595 SDL_RenderCopy(extra_renderers[which - FRAMEBUFFER_USER_START], sdl_textures[which], NULL, NULL); | 1598 SDL_RenderCopy(extra_renderers[which - FRAMEBUFFER_USER_START], sdl_textures[which], NULL, NULL); |
1596 SDL_RenderPresent(extra_renderers[which - FRAMEBUFFER_USER_START]); | 1599 SDL_RenderPresent(extra_renderers[which - FRAMEBUFFER_USER_START]); |
1597 } | 1600 } |
1598 if (screenshot_file) { | 1601 if (screenshot_file) { |
1604 frame_counter++; | 1607 frame_counter++; |
1605 last_frame= SDL_GetTicks(); | 1608 last_frame= SDL_GetTicks(); |
1606 if ((last_frame - start) > FPS_INTERVAL) { | 1609 if ((last_frame - start) > FPS_INTERVAL) { |
1607 if (start && (last_frame-start)) { | 1610 if (start && (last_frame-start)) { |
1608 #ifdef __ANDROID__ | 1611 #ifdef __ANDROID__ |
1609 info_message("%s - %.1f fps", caption, ((float)frame_counter) / (((float)(last_frame-start)) / 1000.0)); | 1612 debug_message("%s - %.1f fps", caption, ((float)frame_counter) / (((float)(last_frame-start)) / 1000.0)); |
1610 #else | 1613 #else |
1611 if (!fps_caption) { | 1614 if (!fps_caption) { |
1612 fps_caption = malloc(strlen(caption) + strlen(" - 100000000.1 fps") + 1); | 1615 fps_caption = malloc(strlen(caption) + strlen(" - 100000000.1 fps") + 1); |
1613 } | 1616 } |
1614 sprintf(fps_caption, "%s - %.1f fps", caption, ((float)frame_counter) / (((float)(last_frame-start)) / 1000.0)); | 1617 sprintf(fps_caption, "%s - %.1f fps", caption, ((float)frame_counter) / (((float)(last_frame-start)) / 1000.0)); |
1617 } | 1620 } |
1618 start = last_frame; | 1621 start = last_frame; |
1619 frame_counter = 0; | 1622 frame_counter = 0; |
1620 } | 1623 } |
1621 } | 1624 } |
1622 if (!sync_to_audio) { | 1625 if (!render_is_audio_sync()) { |
1623 int32_t local_cur_min, local_min_remaining; | 1626 int32_t local_cur_min, local_min_remaining; |
1624 SDL_LockAudio(); | 1627 SDL_LockAudio(); |
1625 if (last_buffered > NO_LAST_BUFFERED) { | 1628 if (last_buffered > NO_LAST_BUFFERED) { |
1626 average_change *= 0.9f; | 1629 average_change *= 0.9f; |
1627 average_change += (cur_min_buffered - last_buffered) * 0.1f; | 1630 average_change += (cur_min_buffered - last_buffered) * 0.1f; |
1659 } else if (local_cur_min < min_buffered / 2) { | 1662 } else if (local_cur_min < min_buffered / 2) { |
1660 adjust_ratio = max_adjust; | 1663 adjust_ratio = max_adjust; |
1661 } | 1664 } |
1662 if (adjust_ratio != 0.0f) { | 1665 if (adjust_ratio != 0.0f) { |
1663 average_change = 0; | 1666 average_change = 0; |
1664 for (uint8_t i = 0; i < num_audio_sources; i++) | 1667 render_audio_adjust_speed(adjust_ratio); |
1665 { | 1668 |
1666 audio_sources[i]->buffer_inc = ((double)audio_sources[i]->buffer_inc) + ((double)audio_sources[i]->buffer_inc) * adjust_ratio + 0.5; | |
1667 } | |
1668 } | 1669 } |
1669 while (source_frame_count > 0) | 1670 while (source_frame_count > 0) |
1670 { | 1671 { |
1671 render_update_display(); | 1672 render_update_display(); |
1672 source_frame_count--; | 1673 source_frame_count--; |
1677 } | 1678 } |
1678 source_frame_count = frame_repeat[source_frame]; | 1679 source_frame_count = frame_repeat[source_frame]; |
1679 } | 1680 } |
1680 } | 1681 } |
1681 | 1682 |
1683 typedef struct { | |
1684 uint32_t *buffer; | |
1685 int width; | |
1686 uint8_t which; | |
1687 } frame; | |
1688 frame frame_queue[4]; | |
1689 int frame_queue_len, frame_queue_read, frame_queue_write; | |
1690 | |
1691 void render_framebuffer_updated(uint8_t which, int width) | |
1692 { | |
1693 if (sync_src == SYNC_AUDIO_THREAD || sync_src == SYNC_EXTERNAL) { | |
1694 SDL_LockMutex(frame_mutex); | |
1695 while (frame_queue_len == 4) { | |
1696 SDL_CondSignal(frame_ready); | |
1697 SDL_UnlockMutex(frame_mutex); | |
1698 SDL_Delay(1); | |
1699 SDL_LockMutex(frame_mutex); | |
1700 } | |
1701 for (int cur = frame_queue_read, i = 0; i < frame_queue_len; i++) { | |
1702 if (frame_queue[cur].which == which) { | |
1703 int last = (frame_queue_write - 1) & 3; | |
1704 frame_queue_len--; | |
1705 release_buffer(frame_queue[cur].buffer); | |
1706 if (last != cur) { | |
1707 frame_queue[cur] = frame_queue[last]; | |
1708 } | |
1709 frame_queue_write = last; | |
1710 break; | |
1711 } | |
1712 cur = (cur + 1) & 3; | |
1713 } | |
1714 frame_queue[frame_queue_write++] = (frame){ | |
1715 .buffer = locked_pixels, | |
1716 .width = width, | |
1717 .which = which | |
1718 }; | |
1719 frame_queue_write &= 0x3; | |
1720 frame_queue_len++; | |
1721 SDL_CondSignal(frame_ready); | |
1722 SDL_UnlockMutex(frame_mutex); | |
1723 return; | |
1724 } | |
1725 //TODO: Maybe fixme for render API | |
1726 process_framebuffer(texture_buf, which, width); | |
1727 } | |
1728 | |
1729 void render_video_loop(void) | |
1730 { | |
1731 if (sync_src != SYNC_AUDIO_THREAD && sync_src != SYNC_EXTERNAL) { | |
1732 return; | |
1733 } | |
1734 SDL_PauseAudio(0); | |
1735 SDL_LockMutex(frame_mutex); | |
1736 for(;;) | |
1737 { | |
1738 while (!frame_queue_len && SDL_GetAudioStatus() == SDL_AUDIO_PLAYING) | |
1739 { | |
1740 SDL_CondWait(frame_ready, frame_mutex); | |
1741 } | |
1742 while (frame_queue_len) | |
1743 { | |
1744 frame f = frame_queue[frame_queue_read++]; | |
1745 frame_queue_read &= 0x3; | |
1746 frame_queue_len--; | |
1747 SDL_UnlockMutex(frame_mutex); | |
1748 process_framebuffer(f.buffer, f.which, f.width); | |
1749 release_buffer(f.buffer); | |
1750 SDL_LockMutex(frame_mutex); | |
1751 } | |
1752 if (SDL_GetAudioStatus() != SDL_AUDIO_PLAYING) { | |
1753 break; | |
1754 } | |
1755 } | |
1756 | |
1757 SDL_UnlockMutex(frame_mutex); | |
1758 } | |
1759 | |
1682 static ui_render_fun render_ui; | 1760 static ui_render_fun render_ui; |
1683 void render_set_ui_render_fun(ui_render_fun fun) | 1761 void render_set_ui_render_fun(ui_render_fun fun) |
1684 { | 1762 { |
1685 render_ui = fun; | 1763 render_ui = fun; |
1686 } | 1764 } |
1701 glBindTexture(GL_TEXTURE_2D, textures[interlaced ? 1 : scanlines ? 2 : 0]); | 1779 glBindTexture(GL_TEXTURE_2D, textures[interlaced ? 1 : scanlines ? 2 : 0]); |
1702 glUniform1i(un_textures[1], 1); | 1780 glUniform1i(un_textures[1], 1); |
1703 | 1781 |
1704 glUniform1f(un_width, render_emulated_width()); | 1782 glUniform1f(un_width, render_emulated_width()); |
1705 glUniform1f(un_height, last_height); | 1783 glUniform1f(un_height, last_height); |
1784 glUniform2f(un_texsize, tex_width, tex_height); | |
1706 | 1785 |
1707 glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); | 1786 glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); |
1708 glVertexAttribPointer(at_pos, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat[2]), (void *)0); | 1787 glVertexAttribPointer(at_pos, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat[2]), (void *)0); |
1709 glEnableVertexAttribArray(at_pos); | 1788 glEnableVertexAttribArray(at_pos); |
1710 | 1789 |
1760 uint32_t render_overscan_top() | 1839 uint32_t render_overscan_top() |
1761 { | 1840 { |
1762 return overscan_top[video_standard]; | 1841 return overscan_top[video_standard]; |
1763 } | 1842 } |
1764 | 1843 |
1765 void render_wait_quit(vdp_context * context) | 1844 uint32_t render_overscan_bot() |
1845 { | |
1846 return overscan_bot[video_standard]; | |
1847 } | |
1848 | |
1849 void render_wait_quit(void) | |
1766 { | 1850 { |
1767 SDL_Event event; | 1851 SDL_Event event; |
1768 while(SDL_WaitEvent(&event)) { | 1852 while(SDL_WaitEvent(&event)) { |
1769 switch (event.type) { | 1853 switch (event.type) { |
1770 case SDL_QUIT: | 1854 case SDL_QUIT: |
1827 warning("Failed to open game controller %d: %s\n", controller, SDL_GetError()); | 1911 warning("Failed to open game controller %d: %s\n", controller, SDL_GetError()); |
1828 return RENDER_NOT_PLUGGED_IN; | 1912 return RENDER_NOT_PLUGGED_IN; |
1829 } | 1913 } |
1830 | 1914 |
1831 SDL_GameControllerButtonBind cbind; | 1915 SDL_GameControllerButtonBind cbind; |
1916 int32_t is_positive = RENDER_AXIS_POS; | |
1832 if (is_axis) { | 1917 if (is_axis) { |
1833 | 1918 |
1834 int sdl_axis = render_lookup_axis(name); | 1919 int sdl_axis = render_lookup_axis(name); |
1835 if (sdl_axis == SDL_CONTROLLER_AXIS_INVALID) { | 1920 if (sdl_axis == SDL_CONTROLLER_AXIS_INVALID) { |
1836 SDL_GameControllerClose(control); | 1921 SDL_GameControllerClose(control); |
1841 int sdl_button = render_lookup_button(name); | 1926 int sdl_button = render_lookup_button(name); |
1842 if (sdl_button == SDL_CONTROLLER_BUTTON_INVALID) { | 1927 if (sdl_button == SDL_CONTROLLER_BUTTON_INVALID) { |
1843 SDL_GameControllerClose(control); | 1928 SDL_GameControllerClose(control); |
1844 return RENDER_INVALID_NAME; | 1929 return RENDER_INVALID_NAME; |
1845 } | 1930 } |
1931 if (sdl_button == SDL_CONTROLLER_BUTTON_DPAD_UP || sdl_button == SDL_CONTROLLER_BUTTON_DPAD_LEFT) { | |
1932 //assume these will be negative if they are an axis | |
1933 is_positive = 0; | |
1934 } | |
1846 cbind = SDL_GameControllerGetBindForButton(control, sdl_button); | 1935 cbind = SDL_GameControllerGetBindForButton(control, sdl_button); |
1847 } | 1936 } |
1848 SDL_GameControllerClose(control); | 1937 SDL_GameControllerClose(control); |
1849 switch (cbind.bindType) | 1938 switch (cbind.bindType) |
1850 { | 1939 { |
1851 case SDL_CONTROLLER_BINDTYPE_BUTTON: | 1940 case SDL_CONTROLLER_BINDTYPE_BUTTON: |
1852 return cbind.value.button; | 1941 return cbind.value.button; |
1853 case SDL_CONTROLLER_BINDTYPE_AXIS: | 1942 case SDL_CONTROLLER_BINDTYPE_AXIS: |
1854 return RENDER_AXIS_BIT | cbind.value.axis; | 1943 return RENDER_AXIS_BIT | cbind.value.axis | is_positive; |
1855 case SDL_CONTROLLER_BINDTYPE_HAT: | 1944 case SDL_CONTROLLER_BINDTYPE_HAT: |
1856 return RENDER_DPAD_BIT | (cbind.value.hat.hat << 4) | cbind.value.hat.hat_mask; | 1945 return RENDER_DPAD_BIT | (cbind.value.hat.hat << 4) | cbind.value.hat.hat_mask; |
1857 } | 1946 } |
1858 return RENDER_NOT_MAPPED; | 1947 return RENDER_NOT_MAPPED; |
1859 } | 1948 } |
1920 //is the only thing that seems to work reliably | 2009 //is the only thing that seems to work reliably |
1921 //when we've just switched to fullscreen mode this should be harmless though | 2010 //when we've just switched to fullscreen mode this should be harmless though |
1922 SDL_SetWindowSize(main_window, windowed_width, windowed_height); | 2011 SDL_SetWindowSize(main_window, windowed_width, windowed_height); |
1923 drain_events(); | 2012 drain_events(); |
1924 in_toggle = 0; | 2013 in_toggle = 0; |
1925 } | 2014 need_ui_fb_resize = 1; |
1926 | |
1927 uint32_t render_audio_buffer() | |
1928 { | |
1929 return buffer_samples; | |
1930 } | |
1931 | |
1932 uint32_t render_sample_rate() | |
1933 { | |
1934 return sample_rate; | |
1935 } | 2015 } |
1936 | 2016 |
1937 void render_errorbox(char *title, char *message) | 2017 void render_errorbox(char *title, char *message) |
1938 { | 2018 { |
1939 SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, title, message, NULL); | 2019 SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, title, message, NULL); |
1975 return FRAMEBUFFER_USER_START + i; | 2055 return FRAMEBUFFER_USER_START + i; |
1976 } | 2056 } |
1977 } | 2057 } |
1978 return 0xFF; | 2058 return 0xFF; |
1979 } | 2059 } |
2060 | |
2061 uint8_t render_create_thread(render_thread *thread, const char *name, render_thread_fun fun, void *data) | |
2062 { | |
2063 *thread = SDL_CreateThread(fun, name, data); | |
2064 return *thread != 0; | |
2065 } |