comparison render_sdl.c @ 1692:5dacaef602a7 segacd

Merge from default
author Michael Pavone <pavone@retrodev.com>
date Sat, 05 Jan 2019 00:58:08 -0800
parents 19331a21da3a
children b1d063466d03
comparison
equal deleted inserted replaced
1504:95b3a1a8b26c 1692:5dacaef602a7
6 #include <stdlib.h> 6 #include <stdlib.h>
7 #include <stdio.h> 7 #include <stdio.h>
8 #include <string.h> 8 #include <string.h>
9 #include <math.h> 9 #include <math.h>
10 #include "render.h" 10 #include "render.h"
11 #include "render_sdl.h"
11 #include "blastem.h" 12 #include "blastem.h"
12 #include "genesis.h" 13 #include "genesis.h"
13 #include "io.h" 14 #include "bindings.h"
14 #include "util.h" 15 #include "util.h"
15 #include "ppm.h" 16 #include "ppm.h"
16 17 #include "png.h"
17 #ifndef DISABLE_OPENGL 18 #include "config.h"
19 #include "controller_info.h"
20
21 #ifndef DISABLE_OPENGL
22 #ifdef USE_GLES
23 #include <SDL_opengles2.h>
24 #else
18 #include <GL/glew.h> 25 #include <GL/glew.h>
19 #endif 26 #endif
27 #endif
20 28
21 #define MAX_EVENT_POLL_PER_FRAME 2 29 #define MAX_EVENT_POLL_PER_FRAME 2
22 30
23 static SDL_Window *main_window; 31 static SDL_Window *main_window;
32 static SDL_Window **extra_windows;
24 static SDL_Renderer *main_renderer; 33 static SDL_Renderer *main_renderer;
34 static SDL_Renderer **extra_renderers;
25 static SDL_Texture **sdl_textures; 35 static SDL_Texture **sdl_textures;
36 static window_close_handler *close_handlers;
26 static uint8_t num_textures; 37 static uint8_t num_textures;
27 static SDL_Rect main_clip; 38 static SDL_Rect main_clip;
28 static SDL_GLContext *main_context; 39 static SDL_GLContext *main_context;
29 40
30 static int main_width, main_height, windowed_width, windowed_height, is_fullscreen; 41 static int main_width, main_height, windowed_width, windowed_height, is_fullscreen;
32 static uint8_t render_gl = 1; 43 static uint8_t render_gl = 1;
33 static uint8_t scanlines = 0; 44 static uint8_t scanlines = 0;
34 45
35 static uint32_t last_frame = 0; 46 static uint32_t last_frame = 0;
36 47
37 static int16_t * current_psg = NULL;
38 static int16_t * current_ym = NULL;
39
40 static uint32_t buffer_samples, sample_rate; 48 static uint32_t buffer_samples, sample_rate;
41 static uint32_t missing_count; 49 static uint32_t missing_count;
42 50
43 static SDL_mutex * audio_mutex; 51 static SDL_mutex * audio_mutex;
44 static SDL_cond * audio_ready; 52 static SDL_cond * audio_ready;
45 static SDL_cond * psg_cond;
46 static SDL_cond * ym_cond;
47 static uint8_t quitting = 0; 53 static uint8_t quitting = 0;
48 static uint8_t ym_enabled = 1; 54
55 struct audio_source {
56 SDL_cond *cond;
57 int16_t *front;
58 int16_t *back;
59 double dt;
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 };
72
73 static audio_source *audio_sources[8];
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;
79
80 typedef int32_t (*mix_func)(audio_source *audio, void *vstream, int len);
81
82 static int32_t mix_s16(audio_source *audio, void *vstream, int len)
83 {
84 int samples = len/(sizeof(int16_t)*2);
85 int16_t *stream = vstream;
86 int16_t *end = stream + 2*samples;
87 int16_t *src = audio->front;
88 uint32_t i = audio->read_start;
89 uint32_t i_end = audio->read_end;
90 int16_t *cur = stream;
91 if (audio->num_channels == 1) {
92 while (cur < end && i != i_end)
93 {
94 *(cur++) += src[i];
95 *(cur++) += src[i++];
96 i &= audio->mask;
97 }
98 } else {
99 while (cur < end && i != i_end)
100 {
101 *(cur++) += src[i++];
102 *(cur++) += src[i++];
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;
49 162
50 static void audio_callback(void * userdata, uint8_t *byte_stream, int len) 163 static void audio_callback(void * userdata, uint8_t *byte_stream, int len)
51 { 164 {
52 //puts("audio_callback"); 165 uint8_t num_populated;
53 int16_t * stream = (int16_t *)byte_stream; 166 memset(byte_stream, 0, len);
54 int samples = len/(sizeof(int16_t)*2);
55 int16_t * psg_buf, * ym_buf;
56 uint8_t local_quit;
57 SDL_LockMutex(audio_mutex); 167 SDL_LockMutex(audio_mutex);
58 psg_buf = NULL;
59 ym_buf = NULL;
60 do { 168 do {
61 if (!psg_buf) { 169 num_populated = 0;
62 psg_buf = current_psg; 170 for (uint8_t i = 0; i < num_audio_sources; i++)
63 current_psg = NULL; 171 {
64 SDL_CondSignal(psg_cond); 172 if (audio_sources[i]->front_populated) {
65 } 173 num_populated++;
66 if (ym_enabled && !ym_buf) { 174 }
67 ym_buf = current_ym; 175 }
68 current_ym = NULL; 176 if (!quitting && num_populated < num_audio_sources) {
69 SDL_CondSignal(ym_cond); 177 fflush(stdout);
70 }
71 if (!quitting && (!psg_buf || (ym_enabled && !ym_buf))) {
72 SDL_CondWait(audio_ready, audio_mutex); 178 SDL_CondWait(audio_ready, audio_mutex);
73 } 179 }
74 } while(!quitting && (!psg_buf || (ym_enabled && !ym_buf))); 180 } while(!quitting && num_populated < num_audio_sources);
75 181 if (!quitting) {
76 local_quit = quitting; 182 for (uint8_t i = 0; i < num_audio_sources; i++)
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 }
77 SDL_UnlockMutex(audio_mutex); 189 SDL_UnlockMutex(audio_mutex);
78 if (!local_quit) { 190 }
79 if (ym_enabled) { 191
80 for (int i = 0; i < samples; i++) 192 #define NO_LAST_BUFFERED -2000000000
81 { 193 static int32_t last_buffered = NO_LAST_BUFFERED;
82 *(stream++) = psg_buf[i] + *(ym_buf++); 194 static float average_change;
83 *(stream++) = psg_buf[i] + *(ym_buf++); 195 #define BUFFER_FRAMES_THRESHOLD 6
84 } 196 #define BASE_MAX_ADJUST 0.0125
85 } else { 197 static float max_adjust;
86 for (int i = 0; i < samples; i++) 198 static int32_t cur_min_buffered;
87 { 199 static uint32_t min_remaining_buffer;
88 *(stream++) = psg_buf[i]; 200 static void audio_callback_drc(void *userData, uint8_t *byte_stream, int len)
89 *(stream++) = psg_buf[i]; 201 {
90 } 202 memset(byte_stream, 0, len);
91 } 203 if (cur_min_buffered < 0) {
92 } 204 //underflow last frame, but main thread hasn't gotten a chance to call SDL_PauseAudio yet
93 } 205 return;
94 206 }
95 void render_disable_ym() 207 cur_min_buffered = 0x7FFFFFFF;
96 { 208 min_remaining_buffer = 0xFFFFFFFF;
97 ym_enabled = 0; 209 for (uint8_t i = 0; i < num_audio_sources; i++)
98 } 210 {
99 211
100 void render_enable_ym() 212 int32_t buffered = mix(audio_sources[i], byte_stream, len);
101 { 213 cur_min_buffered = buffered < cur_min_buffered ? buffered : cur_min_buffered;
102 ym_enabled = 1; 214 uint32_t remaining = (audio_sources[i]->mask + 1)/audio_sources[i]->num_channels - buffered;
215 min_remaining_buffer = remaining < min_remaining_buffer ? remaining : min_remaining_buffer;
216 }
217 }
218
219 static void lock_audio()
220 {
221 if (sync_to_audio) {
222 SDL_LockMutex(audio_mutex);
223 } else {
224 SDL_LockAudio();
225 }
226 }
227
228 static void unlock_audio()
229 {
230 if (sync_to_audio) {
231 SDL_UnlockMutex(audio_mutex);
232 } else {
233 SDL_UnlockAudio();
234 }
103 } 235 }
104 236
105 static void render_close_audio() 237 static void render_close_audio()
106 { 238 {
107 SDL_LockMutex(audio_mutex); 239 SDL_LockMutex(audio_mutex);
109 SDL_CondSignal(audio_ready); 241 SDL_CondSignal(audio_ready);
110 SDL_UnlockMutex(audio_mutex); 242 SDL_UnlockMutex(audio_mutex);
111 SDL_CloseAudio(); 243 SDL_CloseAudio();
112 } 244 }
113 245
246 #define BUFFER_INC_RES 0x40000000UL
247
248 void render_audio_adjust_clock(audio_source *src, uint64_t master_clock, uint64_t sample_divider)
249 {
250 src->buffer_inc = ((BUFFER_INC_RES * (uint64_t)sample_rate) / master_clock) * sample_divider;
251 }
252
253 audio_source *render_audio_source(uint64_t master_clock, uint64_t sample_divider, uint8_t channels)
254 {
255 audio_source *ret = NULL;
256 uint32_t alloc_size = sync_to_audio ? channels * buffer_samples : nearest_pow2(min_buffered * 4 * channels);
257 lock_audio();
258 if (num_audio_sources < 8) {
259 ret = malloc(sizeof(audio_source));
260 ret->back = malloc(alloc_size * sizeof(int16_t));
261 ret->front = sync_to_audio ? malloc(alloc_size * sizeof(int16_t)) : ret->back;
262 ret->front_populated = 0;
263 ret->cond = SDL_CreateCond();
264 ret->num_channels = channels;
265 audio_sources[num_audio_sources++] = ret;
266 }
267 unlock_audio();
268 if (!ret) {
269 fatal_error("Too many audio sources!");
270 } else {
271 render_audio_adjust_clock(ret, master_clock, sample_divider);
272 double lowpass_cutoff = get_lowpass_cutoff(config);
273 double rc = (1.0 / lowpass_cutoff) / (2.0 * M_PI);
274 ret->dt = 1.0 / ((double)master_clock / (double)(sample_divider));
275 double alpha = ret->dt / (ret->dt + rc);
276 ret->lowpass_alpha = (int32_t)(((double)0x10000) * alpha);
277 ret->buffer_pos = 0;
278 ret->buffer_fraction = 0;
279 ret->last_left = ret->last_right = 0;
280 ret->read_start = 0;
281 ret->read_end = sync_to_audio ? buffer_samples * channels : 0;
282 ret->mask = sync_to_audio ? 0xFFFFFFFF : alloc_size-1;
283 }
284 if (sync_to_audio && SDL_GetAudioStatus() == SDL_AUDIO_PAUSED) {
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);
310 }
311 inactive_audio_sources[num_inactive_audio_sources++] = src;
312 }
313
314 void render_resume_source(audio_source *src)
315 {
316 lock_audio();
317 if (num_audio_sources < 8) {
318 audio_sources[num_audio_sources++] = src;
319 }
320 unlock_audio();
321 for (uint8_t i = 0; i < num_inactive_audio_sources; i++)
322 {
323 if (inactive_audio_sources[i] == src) {
324 inactive_audio_sources[i] = inactive_audio_sources[--num_inactive_audio_sources];
325 }
326 }
327 if (sync_to_audio) {
328 SDL_PauseAudio(0);
329 }
330 }
331
332 void render_free_source(audio_source *src)
333 {
334 render_pause_source(src);
335
336 free(src->front);
337 if (sync_to_audio) {
338 free(src->back);
339 SDL_DestroyCond(src->cond);
340 }
341 free(src);
342 }
343 static uint32_t sync_samples;
344 static void do_audio_ready(audio_source *src)
345 {
346 if (sync_to_audio) {
347 SDL_LockMutex(audio_mutex);
348 while (src->front_populated) {
349 SDL_CondWait(src->cond, audio_mutex);
350 }
351 int16_t *tmp = src->front;
352 src->front = src->back;
353 src->back = tmp;
354 src->front_populated = 1;
355 src->buffer_pos = 0;
356 SDL_CondSignal(audio_ready);
357 SDL_UnlockMutex(audio_mutex);
358 } else {
359 uint32_t num_buffered;
360 SDL_LockAudio();
361 src->read_end = src->buffer_pos;
362 num_buffered = ((src->read_end - src->read_start) & src->mask) / src->num_channels;
363 SDL_UnlockAudio();
364 if (num_buffered >= min_buffered && SDL_GetAudioStatus() == SDL_AUDIO_PAUSED) {
365 SDL_PauseAudio(0);
366 }
367 }
368 }
369
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
114 static SDL_Joystick * joysticks[MAX_JOYSTICKS]; 424 static SDL_Joystick * joysticks[MAX_JOYSTICKS];
115 static int joystick_sdl_index[MAX_JOYSTICKS]; 425 static int joystick_sdl_index[MAX_JOYSTICKS];
116 426
117 int render_width() 427 int render_width()
118 { 428 {
129 return is_fullscreen; 439 return is_fullscreen;
130 } 440 }
131 441
132 uint32_t render_map_color(uint8_t r, uint8_t g, uint8_t b) 442 uint32_t render_map_color(uint8_t r, uint8_t g, uint8_t b)
133 { 443 {
444 #ifdef USE_GLES
445 return 255 << 24 | b << 16 | g << 8 | r;
446 #else
134 return 255 << 24 | r << 16 | g << 8 | b; 447 return 255 << 24 | r << 16 | g << 8 | b;
448 #endif
135 } 449 }
136 450
137 #ifndef DISABLE_OPENGL 451 #ifndef DISABLE_OPENGL
138 static GLuint textures[3], buffers[2], vshader, fshader, program, un_textures[2], un_width, un_height, at_pos; 452 static GLuint textures[3], buffers[2], vshader, fshader, program, un_textures[2], un_width, un_height, at_pos;
139 453
145 }; 459 };
146 460
147 static GLfloat vertex_data[8]; 461 static GLfloat vertex_data[8];
148 462
149 static const GLushort element_data[] = {0, 1, 2, 3}; 463 static const GLushort element_data[] = {0, 1, 2, 3};
464
465 static const GLchar shader_prefix[] =
466 #ifdef USE_GLES
467 "#version 100\n";
468 #else
469 "#version 110\n"
470 "#define lowp\n"
471 "#define mediump\n"
472 "#define highp\n";
473 #endif
150 474
151 static GLuint load_shader(char * fname, GLenum shader_type) 475 static GLuint load_shader(char * fname, GLenum shader_type)
152 { 476 {
153 char const * parts[] = {get_home_dir(), "/.config/blastem/shaders/", fname}; 477 char const * parts[] = {get_home_dir(), "/.config/blastem/shaders/", fname};
154 char * shader_path = alloc_concat_m(3, parts); 478 char * shader_path = alloc_concat_m(3, parts);
169 GLchar * text = malloc(fsize); 493 GLchar * text = malloc(fsize);
170 if (fread(text, 1, fsize, f) != fsize) { 494 if (fread(text, 1, fsize, f) != fsize) {
171 warning("Error reading from shader file %s\n", fname); 495 warning("Error reading from shader file %s\n", fname);
172 free(text); 496 free(text);
173 return 0; 497 return 0;
498 }
499 if (strncmp(text, "#version", strlen("#version"))) {
500 GLchar *tmp = text;
501 text = alloc_concat(shader_prefix, tmp);
502 free(tmp);
503 fsize += strlen(shader_prefix);
174 } 504 }
175 GLuint ret = glCreateShader(shader_type); 505 GLuint ret = glCreateShader(shader_type);
176 glShaderSource(ret, 1, (const GLchar **)&text, (const GLint *)&fsize); 506 glShaderSource(ret, 1, (const GLchar **)&text, (const GLint *)&fsize);
177 free(text); 507 free(text);
178 glCompileShader(ret); 508 glCompileShader(ret);
191 } 521 }
192 #endif 522 #endif
193 523
194 static uint32_t texture_buf[512 * 513]; 524 static uint32_t texture_buf[512 * 513];
195 #ifndef DISABLE_OPENGL 525 #ifndef DISABLE_OPENGL
526 #ifdef USE_GLES
527 #define INTERNAL_FORMAT GL_RGBA
528 #define SRC_FORMAT GL_RGBA
529 #define RENDER_FORMAT SDL_PIXELFORMAT_ABGR8888
530 #else
531 #define INTERNAL_FORMAT GL_RGBA8
532 #define SRC_FORMAT GL_BGRA
533 #define RENDER_FORMAT SDL_PIXELFORMAT_ARGB8888
534 #endif
196 static void gl_setup() 535 static void gl_setup()
197 { 536 {
198 tern_val def = {.ptrval = "linear"}; 537 tern_val def = {.ptrval = "linear"};
199 char *scaling = tern_find_path_default(config, "video\0scaling\0", def, TVAL_PTR).ptrval; 538 char *scaling = tern_find_path_default(config, "video\0scaling\0", def, TVAL_PTR).ptrval;
200 GLint filter = strcmp(scaling, "linear") ? GL_NEAREST : GL_LINEAR; 539 GLint filter = strcmp(scaling, "linear") ? GL_NEAREST : GL_LINEAR;
206 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); 545 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
207 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 546 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
208 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 547 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
209 if (i < 2) { 548 if (i < 2) {
210 //TODO: Fixme for PAL + invalid display mode 549 //TODO: Fixme for PAL + invalid display mode
211 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 512, 512, 0, GL_BGRA, GL_UNSIGNED_BYTE, texture_buf); 550 glTexImage2D(GL_TEXTURE_2D, 0, INTERNAL_FORMAT, 512, 512, 0, SRC_FORMAT, GL_UNSIGNED_BYTE, texture_buf);
212 } else { 551 } else {
213 uint32_t blank = 255 << 24; 552 uint32_t blank = 255 << 24;
214 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_BGRA, GL_UNSIGNED_BYTE, &blank); 553 glTexImage2D(GL_TEXTURE_2D, 0, INTERNAL_FORMAT, 1, 1, 0, SRC_FORMAT, GL_UNSIGNED_BYTE, &blank);
215 } 554 }
216 } 555 }
217 glGenBuffers(2, buffers); 556 glGenBuffers(2, buffers);
218 glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); 557 glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
219 glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data, GL_STATIC_DRAW); 558 glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data, GL_STATIC_DRAW);
237 un_textures[1] = glGetUniformLocation(program, "textures[1]"); 576 un_textures[1] = glGetUniformLocation(program, "textures[1]");
238 un_width = glGetUniformLocation(program, "width"); 577 un_width = glGetUniformLocation(program, "width");
239 un_height = glGetUniformLocation(program, "height"); 578 un_height = glGetUniformLocation(program, "height");
240 at_pos = glGetAttribLocation(program, "pos"); 579 at_pos = glGetAttribLocation(program, "pos");
241 } 580 }
242 #endif 581
243 582 static void gl_teardown()
583 {
584 glDeleteProgram(program);
585 glDeleteShader(vshader);
586 glDeleteShader(fshader);
587 glDeleteBuffers(2, buffers);
588 glDeleteTextures(3, textures);
589 }
590 #endif
591
592 static uint8_t texture_init;
244 static void render_alloc_surfaces() 593 static void render_alloc_surfaces()
245 { 594 {
246 static uint8_t texture_init;
247
248 if (texture_init) { 595 if (texture_init) {
249 return; 596 return;
250 } 597 }
251 sdl_textures= malloc(sizeof(SDL_Texture *) * 2); 598 sdl_textures= malloc(sizeof(SDL_Texture *) * 2);
252 num_textures = 2; 599 num_textures = 2;
259 #endif 606 #endif
260 tern_val def = {.ptrval = "linear"}; 607 tern_val def = {.ptrval = "linear"};
261 char *scaling = tern_find_path_default(config, "video\0scaling\0", def, TVAL_PTR).ptrval; 608 char *scaling = tern_find_path_default(config, "video\0scaling\0", def, TVAL_PTR).ptrval;
262 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, scaling); 609 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, scaling);
263 //TODO: Fixme for invalid display mode 610 //TODO: Fixme for invalid display mode
264 sdl_textures[0] = sdl_textures[1] = SDL_CreateTexture(main_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, LINEBUF_SIZE, 588); 611 sdl_textures[0] = sdl_textures[1] = SDL_CreateTexture(main_renderer, RENDER_FORMAT, SDL_TEXTUREACCESS_STREAMING, LINEBUF_SIZE, 588);
265 #ifndef DISABLE_OPENGL 612 #ifndef DISABLE_OPENGL
266 } 613 }
267 #endif 614 #endif
268 } 615 }
269 616
270 static char * caption = NULL; 617 static void free_surfaces(void)
271 static char * fps_caption = NULL; 618 {
272
273 static void render_quit()
274 {
275 render_close_audio();
276 for (int i = 0; i < num_textures; i++) 619 for (int i = 0; i < num_textures; i++)
277 { 620 {
278 if (sdl_textures[i]) { 621 if (sdl_textures[i]) {
279 SDL_DestroyTexture(sdl_textures[i]); 622 SDL_DestroyTexture(sdl_textures[i]);
280 } 623 }
281 } 624 }
625 free(sdl_textures);
626 sdl_textures = NULL;
627 texture_init = 0;
628 }
629
630 static char * caption = NULL;
631 static char * fps_caption = NULL;
632
633 static void render_quit()
634 {
635 render_close_audio();
636 free_surfaces();
637 #ifndef DISABLE_OPENGL
638 if (render_gl) {
639 gl_teardown();
640 SDL_GL_DeleteContext(main_context);
641 }
642 #endif
282 } 643 }
283 644
284 static float config_aspect() 645 static float config_aspect()
285 { 646 {
286 static float aspect = 0.0f; 647 static float aspect = 0.0f;
336 } 697 }
337 #endif 698 #endif
338 } 699 }
339 } 700 }
340 701
341 static uint32_t overscan_top[NUM_VID_STD] = {2, 21}; 702 static ui_render_fun on_context_destroyed, on_context_created;
342 static uint32_t overscan_bot[NUM_VID_STD] = {1, 17}; 703 void render_set_gl_context_handlers(ui_render_fun destroy, ui_render_fun create)
343 static uint32_t overscan_left[NUM_VID_STD] = {13, 13}; 704 {
344 static uint32_t overscan_right[NUM_VID_STD] = {14, 14}; 705 on_context_destroyed = destroy;
345 static vid_std video_standard = VID_NTSC; 706 on_context_created = create;
346 static char *vid_std_names[NUM_VID_STD] = {"ntsc", "pal"};
347 void render_init(int width, int height, char * title, uint8_t fullscreen)
348 {
349 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) < 0) {
350 fatal_error("Unable to init SDL: %s\n", SDL_GetError());
351 }
352 atexit(SDL_Quit);
353 if (height <= 0) {
354 float aspect = config_aspect() > 0.0f ? config_aspect() : 4.0f/3.0f;
355 height = ((float)width / aspect) + 0.5f;
356 }
357 printf("width: %d, height: %d\n", width, height);
358 windowed_width = width;
359 windowed_height = height;
360
361 uint32_t flags = SDL_WINDOW_RESIZABLE;
362
363 if (fullscreen) {
364 flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
365 SDL_DisplayMode mode;
366 //TODO: Multiple monitor support
367 SDL_GetCurrentDisplayMode(0, &mode);
368 //the SDL2 migration guide suggests setting width and height to 0 when using SDL_WINDOW_FULLSCREEN_DESKTOP
369 //but that doesn't seem to work right when using OpenGL, at least on Linux anyway
370 width = mode.w;
371 height = mode.h;
372 }
373 main_width = width;
374 main_height = height;
375 is_fullscreen = fullscreen;
376
377 render_gl = 0;
378 tern_val def = {.ptrval = "off"};
379 char *vsync = tern_find_path_default(config, "video\0vsync\0", def, TVAL_PTR).ptrval;
380
381 tern_node *video = tern_find_node(config, "video");
382 if (video)
383 {
384 for (int i = 0; i < NUM_VID_STD; i++)
385 {
386 tern_node *std_settings = tern_find_node(video, vid_std_names[i]);
387 if (std_settings) {
388 char *val = tern_find_path_default(std_settings, "overscan\0top\0", (tern_val){.ptrval = NULL}, TVAL_PTR).ptrval;
389 if (val) {
390 overscan_top[i] = atoi(val);
391 }
392 val = tern_find_path_default(std_settings, "overscan\0bottom\0", (tern_val){.ptrval = NULL}, TVAL_PTR).ptrval;
393 if (val) {
394 overscan_bot[i] = atoi(val);
395 }
396 val = tern_find_path_default(std_settings, "overscan\0left\0", (tern_val){.ptrval = NULL}, TVAL_PTR).ptrval;
397 if (val) {
398 overscan_left[i] = atoi(val);
399 }
400 val = tern_find_path_default(std_settings, "overscan\0right\0", (tern_val){.ptrval = NULL}, TVAL_PTR).ptrval;
401 if (val) {
402 overscan_right[i] = atoi(val);
403 }
404 }
405 }
406 }
407
408 #ifndef DISABLE_OPENGL
409 char *gl_enabled_str = tern_find_path_default(config, "video\0gl\0", def, TVAL_PTR).ptrval;
410 uint8_t gl_enabled = strcmp(gl_enabled_str, "off") != 0;
411 if (gl_enabled)
412 {
413 flags |= SDL_WINDOW_OPENGL;
414 SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
415 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
416 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
417 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0);
418 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
419 }
420 #endif
421 main_window = SDL_CreateWindow(title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, flags);
422 if (!main_window) {
423 fatal_error("Unable to create SDL window: %s\n", SDL_GetError());
424 }
425 #ifndef DISABLE_OPENGL
426 if (gl_enabled)
427 {
428 main_context = SDL_GL_CreateContext(main_window);
429 GLenum res = glewInit();
430 if (res != GLEW_OK) {
431 warning("Initialization of GLEW failed with code %d\n", res);
432 }
433
434 if (res == GLEW_OK && GLEW_VERSION_2_0) {
435 render_gl = 1;
436 if (!strcmp("tear", vsync)) {
437 if (SDL_GL_SetSwapInterval(-1) < 0) {
438 warning("late tear is not available (%s), using normal vsync\n", SDL_GetError());
439 vsync = "on";
440 } else {
441 vsync = NULL;
442 }
443 }
444 if (vsync) {
445 if (SDL_GL_SetSwapInterval(!strcmp("on", vsync)) < 0) {
446 warning("Failed to set vsync to %s: %s\n", vsync, SDL_GetError());
447 }
448 }
449 } else {
450 warning("OpenGL 2.0 is unavailable, falling back to SDL2 renderer\n");
451 }
452 }
453 if (!render_gl) {
454 #endif
455 flags = SDL_RENDERER_ACCELERATED;
456 if (!strcmp("on", vsync) || !strcmp("tear", vsync)) {
457 flags |= SDL_RENDERER_PRESENTVSYNC;
458 }
459 main_renderer = SDL_CreateRenderer(main_window, -1, flags);
460
461 if (!main_renderer) {
462 fatal_error("unable to create SDL renderer: %s\n", SDL_GetError());
463 }
464 main_clip.x = main_clip.y = 0;
465 main_clip.w = width;
466 main_clip.h = height;
467 #ifndef DISABLE_OPENGL
468 }
469 #endif
470
471 SDL_GetWindowSize(main_window, &main_width, &main_height);
472 printf("Window created with size: %d x %d\n", main_width, main_height);
473 update_aspect();
474 render_alloc_surfaces();
475 def.ptrval = "off";
476 scanlines = !strcmp(tern_find_path_default(config, "video\0scanlines\0", def, TVAL_PTR).ptrval, "on");
477
478 caption = title;
479
480 audio_mutex = SDL_CreateMutex();
481 psg_cond = SDL_CreateCond();
482 ym_cond = SDL_CreateCond();
483 audio_ready = SDL_CreateCond();
484
485 SDL_AudioSpec desired, actual;
486 char * rate_str = tern_find_path(config, "audio\0rate\0", TVAL_PTR).ptrval;
487 int rate = rate_str ? atoi(rate_str) : 0;
488 if (!rate) {
489 rate = 48000;
490 }
491 desired.freq = rate;
492 desired.format = AUDIO_S16SYS;
493 desired.channels = 2;
494 char * samples_str = tern_find_path(config, "audio\0buffer\0", TVAL_PTR).ptrval;
495 int samples = samples_str ? atoi(samples_str) : 0;
496 if (!samples) {
497 samples = 512;
498 }
499 printf("config says: %d\n", samples);
500 desired.samples = samples*2;
501 desired.callback = audio_callback;
502 desired.userdata = NULL;
503
504 if (SDL_OpenAudio(&desired, &actual) < 0) {
505 fatal_error("Unable to open SDL audio: %s\n", SDL_GetError());
506 }
507 buffer_samples = actual.samples;
508 sample_rate = actual.freq;
509 printf("Initialized audio at frequency %d with a %d sample buffer\n", actual.freq, actual.samples);
510 SDL_PauseAudio(0);
511
512 uint32_t db_size;
513 char *db_data = read_bundled_file("gamecontrollerdb.txt", &db_size);
514 if (db_data) {
515 int added = SDL_GameControllerAddMappingsFromRW(SDL_RWFromMem(db_data, db_size), 1);
516 free(db_data);
517 printf("Added %d game controller mappings from gamecontrollerdb.txt\n", added);
518 }
519
520 SDL_JoystickEventState(SDL_ENABLE);
521
522 atexit(render_quit);
523 }
524
525 void render_set_video_standard(vid_std std)
526 {
527 video_standard = std;
528 }
529
530 void render_update_caption(char *title)
531 {
532 caption = title;
533 free(fps_caption);
534 fps_caption = NULL;
535 }
536
537 static char *screenshot_path;
538 void render_save_screenshot(char *path)
539 {
540 if (screenshot_path) {
541 free(screenshot_path);
542 }
543 screenshot_path = path;
544 }
545
546 uint32_t *locked_pixels;
547 uint32_t locked_pitch;
548 uint32_t *render_get_framebuffer(uint8_t which, int *pitch)
549 {
550 #ifndef DISABLE_OPENGL
551 if (render_gl && which <= FRAMEBUFFER_EVEN) {
552 *pitch = LINEBUF_SIZE * sizeof(uint32_t);
553 return texture_buf;
554 } else {
555 #endif
556 if (which >= num_textures) {
557 warning("Request for invalid framebuffer number %d\n", which);
558 return NULL;
559 }
560 void *pixels;
561 if (SDL_LockTexture(sdl_textures[which], NULL, &pixels, pitch) < 0) {
562 warning("Failed to lock texture: %s\n", SDL_GetError());
563 return NULL;
564 }
565 static uint8_t last;
566 if (which <= FRAMEBUFFER_EVEN) {
567 locked_pixels = pixels;
568 if (which == FRAMEBUFFER_EVEN) {
569 pixels += *pitch;
570 }
571 locked_pitch = *pitch;
572 if (which != last) {
573 *pitch *= 2;
574 }
575 last = which;
576 }
577 return pixels;
578 #ifndef DISABLE_OPENGL
579 }
580 #endif
581 }
582
583 uint8_t events_processed;
584 #ifdef __ANDROID__
585 #define FPS_INTERVAL 10000
586 #else
587 #define FPS_INTERVAL 1000
588 #endif
589
590 static uint32_t last_width;
591 void render_framebuffer_updated(uint8_t which, int width)
592 {
593 static uint8_t last;
594 last_width = width;
595 uint32_t height = which <= FRAMEBUFFER_EVEN
596 ? (video_standard == VID_NTSC ? 243 : 294) - (overscan_top[video_standard] + overscan_bot[video_standard])
597 : 240;
598 FILE *screenshot_file = NULL;
599 uint32_t shot_height, shot_width;
600 if (screenshot_path && which == FRAMEBUFFER_ODD) {
601 screenshot_file = fopen(screenshot_path, "wb");
602 if (screenshot_file) {
603 info_message("Saving screenshot to %s\n", screenshot_path);
604 } else {
605 warning("Failed to open screenshot file %s for writing\n", screenshot_path);
606 }
607 free(screenshot_path);
608 screenshot_path = NULL;
609 shot_height = video_standard == VID_NTSC ? 243 : 294;
610 shot_width = width;
611 }
612 width -= overscan_left[video_standard] + overscan_right[video_standard];
613 #ifndef DISABLE_OPENGL
614 if (render_gl && which <= FRAMEBUFFER_EVEN) {
615 glBindTexture(GL_TEXTURE_2D, textures[which]);
616 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, LINEBUF_SIZE, height, GL_BGRA, GL_UNSIGNED_BYTE, texture_buf + overscan_left[video_standard] + LINEBUF_SIZE * overscan_top[video_standard]);
617
618 glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
619 glClear(GL_COLOR_BUFFER_BIT);
620
621 glUseProgram(program);
622 glActiveTexture(GL_TEXTURE0);
623 glBindTexture(GL_TEXTURE_2D, textures[0]);
624 glUniform1i(un_textures[0], 0);
625
626 glActiveTexture(GL_TEXTURE1);
627 glBindTexture(GL_TEXTURE_2D, textures[last != which ? 1 : scanlines ? 2 : 0]);
628 glUniform1i(un_textures[1], 1);
629
630 glUniform1f(un_width, width);
631 glUniform1f(un_height, height);
632
633 glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
634 glVertexAttribPointer(at_pos, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat[2]), (void *)0);
635 glEnableVertexAttribArray(at_pos);
636
637 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]);
638 glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, (void *)0);
639
640 glDisableVertexAttribArray(at_pos);
641
642 SDL_GL_SwapWindow(main_window);
643
644 if (screenshot_file) {
645 //properly supporting interlaced modes here is non-trivial, so only save the odd field for now
646 save_ppm(screenshot_file, texture_buf, shot_width, shot_height, LINEBUF_SIZE*sizeof(uint32_t));
647 }
648 } else {
649 #endif
650 if (which <= FRAMEBUFFER_EVEN && last != which) {
651 uint8_t *cur_dst = (uint8_t *)locked_pixels;
652 uint8_t *cur_saved = (uint8_t *)texture_buf;
653 uint32_t dst_off = which == FRAMEBUFFER_EVEN ? 0 : locked_pitch;
654 uint32_t src_off = which == FRAMEBUFFER_EVEN ? locked_pitch : 0;
655 for (int i = 0; i < height; ++i)
656 {
657 //copy saved line from other field
658 memcpy(cur_dst + dst_off, cur_saved, locked_pitch);
659 //save line from this field to buffer for next frame
660 memcpy(cur_saved, cur_dst + src_off, locked_pitch);
661 cur_dst += locked_pitch * 2;
662 cur_saved += locked_pitch;
663 }
664 height = 480;
665 }
666 if (screenshot_file) {
667 uint32_t shot_pitch = locked_pitch;
668 if (which == FRAMEBUFFER_EVEN) {
669 shot_height *= 2;
670 } else {
671 shot_pitch *= 2;
672 }
673 save_ppm(screenshot_file, locked_pixels, shot_width, shot_height, shot_pitch);
674 }
675 SDL_UnlockTexture(sdl_textures[which]);
676 SDL_Rect src_clip = {
677 .x = overscan_left[video_standard],
678 .y = overscan_top[video_standard],
679 .w = width,
680 .h = height
681 };
682 SDL_SetRenderDrawColor(main_renderer, 0, 0, 0, 255);
683 SDL_RenderClear(main_renderer);
684 SDL_RenderCopy(main_renderer, sdl_textures[which], &src_clip, &main_clip);
685 SDL_RenderPresent(main_renderer);
686 #ifndef DISABLE_OPENGL
687 }
688 #endif
689 if (screenshot_file) {
690 fclose(screenshot_file);
691 }
692 if (which <= FRAMEBUFFER_EVEN) {
693 last = which;
694 static uint32_t frame_counter, start;
695 frame_counter++;
696 last_frame= SDL_GetTicks();
697 if ((last_frame - start) > FPS_INTERVAL) {
698 if (start && (last_frame-start)) {
699 #ifdef __ANDROID__
700 info_message("%s - %.1f fps", caption, ((float)frame_counter) / (((float)(last_frame-start)) / 1000.0));
701 #else
702 if (!fps_caption) {
703 fps_caption = malloc(strlen(caption) + strlen(" - 100000000.1 fps") + 1);
704 }
705 sprintf(fps_caption, "%s - %.1f fps", caption, ((float)frame_counter) / (((float)(last_frame-start)) / 1000.0));
706 SDL_SetWindowTitle(main_window, fps_caption);
707 #endif
708 }
709 start = last_frame;
710 frame_counter = 0;
711 }
712 }
713 if (!events_processed) {
714 process_events();
715 }
716 events_processed = 0;
717 }
718
719 uint32_t render_emulated_width()
720 {
721 return last_width - overscan_left[video_standard] - overscan_right[video_standard];
722 }
723
724 uint32_t render_emulated_height()
725 {
726 return (video_standard == VID_NTSC ? 243 : 294) - overscan_top[video_standard] - overscan_bot[video_standard];
727 }
728
729 uint32_t render_overscan_left()
730 {
731 return overscan_left[video_standard];
732 }
733
734 uint32_t render_overscan_top()
735 {
736 return overscan_top[video_standard];
737 }
738
739 void render_wait_quit(vdp_context * context)
740 {
741 SDL_Event event;
742 while(SDL_WaitEvent(&event)) {
743 switch (event.type) {
744 case SDL_QUIT:
745 return;
746 }
747 }
748 }
749
750 static int find_joystick_index(SDL_JoystickID instanceID)
751 {
752 for (int i = 0; i < MAX_JOYSTICKS; i++) {
753 if (joysticks[i] && SDL_JoystickInstanceID(joysticks[i]) == instanceID) {
754 return i;
755 }
756 }
757 return -1;
758 }
759
760 static int lowest_unused_joystick_index()
761 {
762 for (int i = 0; i < MAX_JOYSTICKS; i++) {
763 if (!joysticks[i]) {
764 return i;
765 }
766 }
767 return -1;
768 }
769
770 int32_t render_translate_input_name(int32_t controller, char *name, uint8_t is_axis)
771 {
772 static tern_node *button_lookup, *axis_lookup;
773 if (controller > MAX_JOYSTICKS || !joysticks[controller]) {
774 return RENDER_NOT_PLUGGED_IN;
775 }
776
777 if (!SDL_IsGameController(joystick_sdl_index[controller])) {
778 return RENDER_NOT_MAPPED;
779 }
780 SDL_GameController *control = SDL_GameControllerOpen(joystick_sdl_index[controller]);
781 if (!control) {
782 warning("Failed to open game controller %d: %s\n", controller, SDL_GetError());
783 return RENDER_NOT_PLUGGED_IN;
784 }
785
786 SDL_GameControllerButtonBind cbind;
787 if (is_axis) {
788 if (!axis_lookup) {
789 for (int i = SDL_CONTROLLER_AXIS_LEFTX; i < SDL_CONTROLLER_AXIS_MAX; i++)
790 {
791 axis_lookup = tern_insert_int(axis_lookup, SDL_GameControllerGetStringForAxis(i), i);
792 }
793 //alternative Playstation-style names
794 axis_lookup = tern_insert_int(axis_lookup, "l2", SDL_CONTROLLER_AXIS_TRIGGERLEFT);
795 axis_lookup = tern_insert_int(axis_lookup, "r2", SDL_CONTROLLER_AXIS_TRIGGERRIGHT);
796 }
797 intptr_t sdl_axis = tern_find_int(axis_lookup, name, SDL_CONTROLLER_AXIS_INVALID);
798 if (sdl_axis == SDL_CONTROLLER_AXIS_INVALID) {
799 SDL_GameControllerClose(control);
800 return RENDER_INVALID_NAME;
801 }
802 cbind = SDL_GameControllerGetBindForAxis(control, sdl_axis);
803 } else {
804 if (!button_lookup) {
805 for (int i = SDL_CONTROLLER_BUTTON_A; i < SDL_CONTROLLER_BUTTON_MAX; i++)
806 {
807 button_lookup = tern_insert_int(button_lookup, SDL_GameControllerGetStringForButton(i), i);
808 }
809 //alternative Playstation-style names
810 button_lookup = tern_insert_int(button_lookup, "cross", SDL_CONTROLLER_BUTTON_A);
811 button_lookup = tern_insert_int(button_lookup, "circle", SDL_CONTROLLER_BUTTON_B);
812 button_lookup = tern_insert_int(button_lookup, "square", SDL_CONTROLLER_BUTTON_X);
813 button_lookup = tern_insert_int(button_lookup, "triangle", SDL_CONTROLLER_BUTTON_Y);
814 button_lookup = tern_insert_int(button_lookup, "share", SDL_CONTROLLER_BUTTON_BACK);
815 button_lookup = tern_insert_int(button_lookup, "select", SDL_CONTROLLER_BUTTON_BACK);
816 button_lookup = tern_insert_int(button_lookup, "options", SDL_CONTROLLER_BUTTON_START);
817 button_lookup = tern_insert_int(button_lookup, "l1", SDL_CONTROLLER_BUTTON_LEFTSHOULDER);
818 button_lookup = tern_insert_int(button_lookup, "r1", SDL_CONTROLLER_BUTTON_RIGHTSHOULDER);
819 button_lookup = tern_insert_int(button_lookup, "l3", SDL_CONTROLLER_BUTTON_LEFTSTICK);
820 button_lookup = tern_insert_int(button_lookup, "r3", SDL_CONTROLLER_BUTTON_RIGHTSTICK);
821 }
822 intptr_t sdl_button = tern_find_int(button_lookup, name, SDL_CONTROLLER_BUTTON_INVALID);
823 if (sdl_button == SDL_CONTROLLER_BUTTON_INVALID) {
824 SDL_GameControllerClose(control);
825 return RENDER_INVALID_NAME;
826 }
827 cbind = SDL_GameControllerGetBindForButton(control, sdl_button);
828 }
829 SDL_GameControllerClose(control);
830 switch (cbind.bindType)
831 {
832 case SDL_CONTROLLER_BINDTYPE_BUTTON:
833 return cbind.value.button;
834 case SDL_CONTROLLER_BINDTYPE_AXIS:
835 return RENDER_AXIS_BIT | cbind.value.axis;
836 case SDL_CONTROLLER_BINDTYPE_HAT:
837 return RENDER_DPAD_BIT | (cbind.value.hat.hat << 4) | cbind.value.hat.hat_mask;
838 }
839 return RENDER_NOT_MAPPED;
840 }
841
842 int32_t render_dpad_part(int32_t input)
843 {
844 return input >> 4 & 0xFFFFFF;
845 }
846
847 uint8_t render_direction_part(int32_t input)
848 {
849 return input & 0xF;
850 }
851
852 int32_t render_axis_part(int32_t input)
853 {
854 return input & 0xFFFFFFF;
855 } 707 }
856 708
857 static uint8_t scancode_map[SDL_NUM_SCANCODES] = { 709 static uint8_t scancode_map[SDL_NUM_SCANCODES] = {
858 [SDL_SCANCODE_A] = 0x1C, 710 [SDL_SCANCODE_A] = 0x1C,
859 [SDL_SCANCODE_B] = 0x32, 711 [SDL_SCANCODE_B] = 0x32,
962 void render_set_drag_drop_handler(drop_handler handler) 814 void render_set_drag_drop_handler(drop_handler handler)
963 { 815 {
964 drag_drop_handler = handler; 816 drag_drop_handler = handler;
965 } 817 }
966 818
819 static event_handler custom_event_handler;
820 void render_set_event_handler(event_handler handler)
821 {
822 custom_event_handler = handler;
823 }
824
825 static int find_joystick_index(SDL_JoystickID instanceID)
826 {
827 for (int i = 0; i < MAX_JOYSTICKS; i++) {
828 if (joysticks[i] && SDL_JoystickInstanceID(joysticks[i]) == instanceID) {
829 return i;
830 }
831 }
832 return -1;
833 }
834
835 static int lowest_unused_joystick_index()
836 {
837 for (int i = 0; i < MAX_JOYSTICKS; i++) {
838 if (!joysticks[i]) {
839 return i;
840 }
841 }
842 return -1;
843 }
844
845 SDL_Joystick *render_get_joystick(int index)
846 {
847 if (index >= MAX_JOYSTICKS) {
848 return NULL;
849 }
850 return joysticks[index];
851 }
852
853 char* render_joystick_type_id(int index)
854 {
855 SDL_Joystick *stick = render_get_joystick(index);
856 if (!stick) {
857 return NULL;
858 }
859 char *guid_string = malloc(33);
860 SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(stick), guid_string, 33);
861 return guid_string;
862 }
863
864 SDL_GameController *render_get_controller(int index)
865 {
866 if (index >= MAX_JOYSTICKS) {
867 return NULL;
868 }
869 return SDL_GameControllerOpen(joystick_sdl_index[index]);
870 }
871
872 static uint32_t overscan_top[NUM_VID_STD] = {2, 21};
873 static uint32_t overscan_bot[NUM_VID_STD] = {1, 17};
874 static uint32_t overscan_left[NUM_VID_STD] = {13, 13};
875 static uint32_t overscan_right[NUM_VID_STD] = {14, 14};
876 static vid_std video_standard = VID_NTSC;
877
967 static int32_t handle_event(SDL_Event *event) 878 static int32_t handle_event(SDL_Event *event)
968 { 879 {
880 if (custom_event_handler) {
881 custom_event_handler(event);
882 }
969 switch (event->type) { 883 switch (event->type) {
970 case SDL_KEYDOWN: 884 case SDL_KEYDOWN:
971 handle_keydown(event->key.keysym.sym, scancode_map[event->key.keysym.scancode]); 885 handle_keydown(event->key.keysym.sym, scancode_map[event->key.keysym.scancode]);
972 break; 886 break;
973 case SDL_KEYUP: 887 case SDL_KEYUP:
978 break; 892 break;
979 case SDL_JOYBUTTONUP: 893 case SDL_JOYBUTTONUP:
980 handle_joyup(find_joystick_index(event->jbutton.which), event->jbutton.button); 894 handle_joyup(find_joystick_index(event->jbutton.which), event->jbutton.button);
981 break; 895 break;
982 case SDL_JOYHATMOTION: 896 case SDL_JOYHATMOTION:
983 handle_joy_dpad(find_joystick_index(event->jbutton.which), event->jhat.hat, event->jhat.value); 897 handle_joy_dpad(find_joystick_index(event->jhat.which), event->jhat.hat, event->jhat.value);
984 break; 898 break;
985 case SDL_JOYAXISMOTION: 899 case SDL_JOYAXISMOTION:
986 handle_joy_axis(find_joystick_index(event->jaxis.which), event->jaxis.axis, event->jaxis.value); 900 handle_joy_axis(find_joystick_index(event->jaxis.which), event->jaxis.axis, event->jaxis.value);
987 break; 901 break;
988 case SDL_JOYDEVICEADDED: 902 case SDL_JOYDEVICEADDED:
1026 main_width = event->window.data1; 940 main_width = event->window.data1;
1027 main_height = event->window.data2; 941 main_height = event->window.data2;
1028 update_aspect(); 942 update_aspect();
1029 #ifndef DISABLE_OPENGL 943 #ifndef DISABLE_OPENGL
1030 if (render_gl) { 944 if (render_gl) {
945 if (on_context_destroyed) {
946 on_context_destroyed();
947 }
948 gl_teardown();
1031 SDL_GL_DeleteContext(main_context); 949 SDL_GL_DeleteContext(main_context);
1032 main_context = SDL_GL_CreateContext(main_window); 950 main_context = SDL_GL_CreateContext(main_window);
1033 gl_setup(); 951 gl_setup();
1034 } 952 if (on_context_created) {
1035 #endif 953 on_context_created();
954 }
955 }
956 #endif
957 break;
958 case SDL_WINDOWEVENT_CLOSE:
959 if (SDL_GetWindowID(main_window) == event->window.windowID) {
960 exit(0);
961 } else {
962 for (int i = 0; i < num_textures - FRAMEBUFFER_USER_START; i++)
963 {
964 if (SDL_GetWindowID(extra_windows[i]) == event->window.windowID) {
965 if (close_handlers[i]) {
966 close_handlers[i](i + FRAMEBUFFER_USER_START);
967 }
968 break;
969 }
970 }
971 }
1036 break; 972 break;
1037 } 973 }
1038 break; 974 break;
1039 case SDL_DROPFILE: 975 case SDL_DROPFILE:
1040 if (drag_drop_handler) { 976 if (drag_drop_handler) {
1056 { 992 {
1057 handle_event(&event); 993 handle_event(&event);
1058 } 994 }
1059 } 995 }
1060 996
997 static char *vid_std_names[NUM_VID_STD] = {"ntsc", "pal"};
998 static int display_hz;
999 static int source_hz;
1000 static int source_frame;
1001 static int source_frame_count;
1002 static int frame_repeat[60];
1003
1004 static void init_audio()
1005 {
1006 SDL_AudioSpec desired, actual;
1007 char * rate_str = tern_find_path(config, "audio\0rate\0", TVAL_PTR).ptrval;
1008 int rate = rate_str ? atoi(rate_str) : 0;
1009 if (!rate) {
1010 rate = 48000;
1011 }
1012 desired.freq = rate;
1013 desired.format = AUDIO_S16SYS;
1014 desired.channels = 2;
1015 char * samples_str = tern_find_path(config, "audio\0buffer\0", TVAL_PTR).ptrval;
1016 int samples = samples_str ? atoi(samples_str) : 0;
1017 if (!samples) {
1018 samples = 512;
1019 }
1020 printf("config says: %d\n", samples);
1021 desired.samples = samples*2;
1022 desired.callback = sync_to_audio ? audio_callback : audio_callback_drc;
1023 desired.userdata = NULL;
1024
1025 if (SDL_OpenAudio(&desired, &actual) < 0) {
1026 fatal_error("Unable to open SDL audio: %s\n", SDL_GetError());
1027 }
1028 buffer_samples = actual.samples;
1029 sample_rate = actual.freq;
1030 printf("Initialized audio at frequency %d with a %d sample buffer, ", actual.freq, actual.samples);
1031 if (actual.format == AUDIO_S16SYS) {
1032 puts("signed 16-bit int format");
1033 mix = mix_s16;
1034 } else if (actual.format == AUDIO_F32SYS) {
1035 puts("32-bit float format");
1036 mix = mix_f32;
1037 } else {
1038 printf("unsupported format %X\n", actual.format);
1039 warning("Unsupported audio sample format: %X\n", actual.format);
1040 mix = mix_null;
1041 }
1042 }
1043
1044 void window_setup(void)
1045 {
1046 uint32_t flags = SDL_WINDOW_RESIZABLE;
1047 if (is_fullscreen) {
1048 flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
1049 }
1050
1051 tern_val def = {.ptrval = "audio"};
1052 char *sync_src = tern_find_path_default(config, "system\0sync_source\0", def, TVAL_PTR).ptrval;
1053 sync_to_audio = !strcmp(sync_src, "audio");
1054
1055 const char *vsync;
1056 if (sync_to_audio) {
1057 def.ptrval = "off";
1058 vsync = tern_find_path_default(config, "video\0vsync\0", def, TVAL_PTR).ptrval;
1059 } else {
1060 vsync = "on";
1061 }
1062
1063 tern_node *video = tern_find_node(config, "video");
1064 if (video)
1065 {
1066 for (int i = 0; i < NUM_VID_STD; i++)
1067 {
1068 tern_node *std_settings = tern_find_node(video, vid_std_names[i]);
1069 if (std_settings) {
1070 char *val = tern_find_path_default(std_settings, "overscan\0top\0", (tern_val){.ptrval = NULL}, TVAL_PTR).ptrval;
1071 if (val) {
1072 overscan_top[i] = atoi(val);
1073 }
1074 val = tern_find_path_default(std_settings, "overscan\0bottom\0", (tern_val){.ptrval = NULL}, TVAL_PTR).ptrval;
1075 if (val) {
1076 overscan_bot[i] = atoi(val);
1077 }
1078 val = tern_find_path_default(std_settings, "overscan\0left\0", (tern_val){.ptrval = NULL}, TVAL_PTR).ptrval;
1079 if (val) {
1080 overscan_left[i] = atoi(val);
1081 }
1082 val = tern_find_path_default(std_settings, "overscan\0right\0", (tern_val){.ptrval = NULL}, TVAL_PTR).ptrval;
1083 if (val) {
1084 overscan_right[i] = atoi(val);
1085 }
1086 }
1087 }
1088 }
1089 render_gl = 0;
1090
1091 #ifndef DISABLE_OPENGL
1092 char *gl_enabled_str = tern_find_path_default(config, "video\0gl\0", def, TVAL_PTR).ptrval;
1093 uint8_t gl_enabled = strcmp(gl_enabled_str, "off") != 0;
1094 if (gl_enabled)
1095 {
1096 flags |= SDL_WINDOW_OPENGL;
1097 SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
1098 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
1099 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
1100 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0);
1101 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
1102 #ifdef USE_GLES
1103 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
1104 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
1105 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
1106 #endif
1107 }
1108 #endif
1109 main_window = SDL_CreateWindow(caption, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, main_width, main_height, flags);
1110 if (!main_window) {
1111 fatal_error("Unable to create SDL window: %s\n", SDL_GetError());
1112 }
1113 #ifndef DISABLE_OPENGL
1114 if (gl_enabled)
1115 {
1116 main_context = SDL_GL_CreateContext(main_window);
1117 #ifdef USE_GLES
1118 int major_version;
1119 if (SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &major_version) == 0 && major_version >= 2) {
1120 #else
1121 GLenum res = glewInit();
1122 if (res != GLEW_OK) {
1123 warning("Initialization of GLEW failed with code %d\n", res);
1124 }
1125
1126 if (res == GLEW_OK && GLEW_VERSION_2_0) {
1127 #endif
1128 render_gl = 1;
1129 SDL_GL_MakeCurrent(main_window, main_context);
1130 if (!strcmp("tear", vsync)) {
1131 if (SDL_GL_SetSwapInterval(-1) < 0) {
1132 warning("late tear is not available (%s), using normal vsync\n", SDL_GetError());
1133 vsync = "on";
1134 } else {
1135 vsync = NULL;
1136 }
1137 }
1138 if (vsync) {
1139 if (SDL_GL_SetSwapInterval(!strcmp("on", vsync)) < 0) {
1140 warning("Failed to set vsync to %s: %s\n", vsync, SDL_GetError());
1141 }
1142 }
1143 } else {
1144 warning("OpenGL 2.0 is unavailable, falling back to SDL2 renderer\n");
1145 }
1146 }
1147 if (!render_gl) {
1148 #endif
1149 flags = SDL_RENDERER_ACCELERATED;
1150 if (!strcmp("on", vsync) || !strcmp("tear", vsync)) {
1151 flags |= SDL_RENDERER_PRESENTVSYNC;
1152 }
1153 main_renderer = SDL_CreateRenderer(main_window, -1, flags);
1154
1155 if (!main_renderer) {
1156 fatal_error("unable to create SDL renderer: %s\n", SDL_GetError());
1157 }
1158 SDL_RendererInfo rinfo;
1159 SDL_GetRendererInfo(main_renderer, &rinfo);
1160 printf("SDL2 Render Driver: %s\n", rinfo.name);
1161 main_clip.x = main_clip.y = 0;
1162 main_clip.w = main_width;
1163 main_clip.h = main_height;
1164 #ifndef DISABLE_OPENGL
1165 }
1166 #endif
1167
1168 SDL_GetWindowSize(main_window, &main_width, &main_height);
1169 printf("Window created with size: %d x %d\n", main_width, main_height);
1170 update_aspect();
1171 render_alloc_surfaces();
1172 def.ptrval = "off";
1173 scanlines = !strcmp(tern_find_path_default(config, "video\0scanlines\0", def, TVAL_PTR).ptrval, "on");
1174 }
1175
1176 void render_init(int width, int height, char * title, uint8_t fullscreen)
1177 {
1178 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) < 0) {
1179 fatal_error("Unable to init SDL: %s\n", SDL_GetError());
1180 }
1181 atexit(SDL_Quit);
1182 if (height <= 0) {
1183 float aspect = config_aspect() > 0.0f ? config_aspect() : 4.0f/3.0f;
1184 height = ((float)width / aspect) + 0.5f;
1185 }
1186 printf("width: %d, height: %d\n", width, height);
1187 windowed_width = width;
1188 windowed_height = height;
1189
1190 SDL_DisplayMode mode;
1191 //TODO: Explicit multiple monitor support
1192 SDL_GetCurrentDisplayMode(0, &mode);
1193 display_hz = mode.refresh_rate;
1194
1195 if (fullscreen) {
1196 //the SDL2 migration guide suggests setting width and height to 0 when using SDL_WINDOW_FULLSCREEN_DESKTOP
1197 //but that doesn't seem to work right when using OpenGL, at least on Linux anyway
1198 width = mode.w;
1199 height = mode.h;
1200 }
1201 main_width = width;
1202 main_height = height;
1203 is_fullscreen = fullscreen;
1204
1205 caption = title;
1206
1207 window_setup();
1208
1209 audio_mutex = SDL_CreateMutex();
1210 audio_ready = SDL_CreateCond();
1211
1212 init_audio();
1213
1214 uint32_t db_size;
1215 char *db_data = read_bundled_file("gamecontrollerdb.txt", &db_size);
1216 if (db_data) {
1217 int added = SDL_GameControllerAddMappingsFromRW(SDL_RWFromMem(db_data, db_size), 1);
1218 free(db_data);
1219 printf("Added %d game controller mappings from gamecontrollerdb.txt\n", added);
1220 }
1221
1222 controller_add_mappings();
1223
1224 SDL_JoystickEventState(SDL_ENABLE);
1225
1226 render_set_video_standard(VID_NTSC);
1227
1228 atexit(render_quit);
1229 }
1230 #include<unistd.h>
1231 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
1253 void render_config_updated(void)
1254 {
1255 uint8_t old_sync_to_audio = sync_to_audio;
1256
1257 free_surfaces();
1258 #ifndef DISABLE_OPENGL
1259 if (render_gl) {
1260 if (on_context_destroyed) {
1261 on_context_destroyed();
1262 }
1263 gl_teardown();
1264 SDL_GL_DeleteContext(main_context);
1265 } else {
1266 #endif
1267 SDL_DestroyRenderer(main_renderer);
1268 #ifndef DISABLE_OPENGL
1269 }
1270 #endif
1271 in_toggle = 1;
1272 SDL_DestroyWindow(main_window);
1273 drain_events();
1274
1275 char *config_width = tern_find_path(config, "video\0width\0", TVAL_PTR).ptrval;
1276 if (config_width) {
1277 windowed_width = atoi(config_width);
1278 }
1279 char *config_height = tern_find_path(config, "video\0height\0", TVAL_PTR).ptrval;
1280 if (config_height) {
1281 windowed_height = atoi(config_height);
1282 } else {
1283 float aspect = config_aspect() > 0.0f ? config_aspect() : 4.0f/3.0f;
1284 windowed_height = ((float)windowed_width / aspect) + 0.5f;
1285 }
1286 char *config_fullscreen = tern_find_path(config, "video\0fullscreen\0", TVAL_PTR).ptrval;
1287 is_fullscreen = config_fullscreen && !strcmp("on", config_fullscreen);
1288 if (is_fullscreen) {
1289 SDL_DisplayMode mode;
1290 //TODO: Multiple monitor support
1291 SDL_GetCurrentDisplayMode(0, &mode);
1292 main_width = mode.w;
1293 main_height = mode.h;
1294 } else {
1295 main_width = windowed_width;
1296 main_height = windowed_height;
1297 }
1298
1299 window_setup();
1300 update_aspect();
1301 #ifndef DISABLE_OPENGL
1302 //need to check render_gl again after window_setup as render option could have changed
1303 if (render_gl && on_context_created) {
1304 on_context_created();
1305 }
1306 #endif
1307
1308 uint8_t was_paused = SDL_GetAudioStatus() == SDL_AUDIO_PAUSED;
1309 render_close_audio();
1310 quitting = 0;
1311 init_audio();
1312 render_set_video_standard(video_standard);
1313
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();
1327 in_toggle = 0;
1328 if (!was_paused) {
1329 SDL_PauseAudio(0);
1330 }
1331 }
1332
1333 SDL_Window *render_get_window(void)
1334 {
1335 return main_window;
1336 }
1337
1338 void render_set_video_standard(vid_std std)
1339 {
1340 video_standard = std;
1341 source_hz = std == VID_PAL ? 50 : 60;
1342 uint32_t max_repeat = 0;
1343 if (abs(source_hz - display_hz) < 2) {
1344 memset(frame_repeat, 0, sizeof(int)*display_hz);
1345 } else {
1346 int inc = display_hz * 100000 / source_hz;
1347 int accum = 0;
1348 int dst_frames = 0;
1349 for (int src_frame = 0; src_frame < source_hz; src_frame++)
1350 {
1351 frame_repeat[src_frame] = -1;
1352 accum += inc;
1353 while (accum > 100000)
1354 {
1355 accum -= 100000;
1356 frame_repeat[src_frame]++;
1357 max_repeat = frame_repeat[src_frame] > max_repeat ? frame_repeat[src_frame] : max_repeat;
1358 dst_frames++;
1359 }
1360 }
1361 if (dst_frames != display_hz) {
1362 frame_repeat[source_hz-1] += display_hz - dst_frames;
1363 }
1364 }
1365 source_frame = 0;
1366 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++;
1370 min_buffered = (((float)max_repeat * (float)sample_rate/(float)source_hz)/* / (float)buffer_samples*/);// + 0.9999;
1371 //min_buffered *= buffer_samples;
1372 printf("Min samples buffered before audio start: %d\n", min_buffered);
1373 max_adjust = BASE_MAX_ADJUST / source_hz;
1374 }
1375
1376 void render_update_caption(char *title)
1377 {
1378 caption = title;
1379 free(fps_caption);
1380 fps_caption = NULL;
1381 }
1382
1383 static char *screenshot_path;
1384 void render_save_screenshot(char *path)
1385 {
1386 if (screenshot_path) {
1387 free(screenshot_path);
1388 }
1389 screenshot_path = path;
1390 }
1391
1392 uint8_t render_create_window(char *caption, uint32_t width, uint32_t height, window_close_handler close_handler)
1393 {
1394 uint8_t win_idx = 0xFF;
1395 for (int i = 0; i < num_textures - FRAMEBUFFER_USER_START; i++)
1396 {
1397 if (!extra_windows[i]) {
1398 win_idx = i;
1399 break;
1400 }
1401 }
1402
1403 if (win_idx == 0xFF) {
1404 num_textures++;
1405 sdl_textures = realloc(sdl_textures, num_textures * sizeof(*sdl_textures));
1406 extra_windows = realloc(extra_windows, (num_textures - FRAMEBUFFER_USER_START) * sizeof(*extra_windows));
1407 extra_renderers = realloc(extra_renderers, (num_textures - FRAMEBUFFER_USER_START) * sizeof(*extra_renderers));
1408 close_handlers = realloc(close_handlers, (num_textures - FRAMEBUFFER_USER_START) * sizeof(*close_handlers));
1409 win_idx = num_textures - FRAMEBUFFER_USER_START - 1;
1410 }
1411 extra_windows[win_idx] = SDL_CreateWindow(caption, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, 0);
1412 if (!extra_windows[win_idx]) {
1413 goto fail_window;
1414 }
1415 extra_renderers[win_idx] = SDL_CreateRenderer(extra_windows[win_idx], -1, SDL_RENDERER_ACCELERATED);
1416 if (!extra_renderers[win_idx]) {
1417 goto fail_renderer;
1418 }
1419 uint8_t texture_idx = win_idx + FRAMEBUFFER_USER_START;
1420 sdl_textures[texture_idx] = SDL_CreateTexture(extra_renderers[win_idx], SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, width, height);
1421 if (!sdl_textures[texture_idx]) {
1422 goto fail_texture;
1423 }
1424 close_handlers[win_idx] = close_handler;
1425 return texture_idx;
1426
1427 fail_texture:
1428 SDL_DestroyRenderer(extra_renderers[win_idx]);
1429 fail_renderer:
1430 SDL_DestroyWindow(extra_windows[win_idx]);
1431 fail_window:
1432 num_textures--;
1433 return 0;
1434 }
1435
1436 void render_destroy_window(uint8_t which)
1437 {
1438 uint8_t win_idx = which - FRAMEBUFFER_USER_START;
1439 //Destroying the renderers also frees the textures
1440 SDL_DestroyRenderer(extra_renderers[win_idx]);
1441 SDL_DestroyWindow(extra_windows[win_idx]);
1442
1443 extra_renderers[win_idx] = NULL;
1444 extra_windows[win_idx] = NULL;
1445 }
1446
1447 uint32_t *locked_pixels;
1448 uint32_t locked_pitch;
1449 uint32_t *render_get_framebuffer(uint8_t which, int *pitch)
1450 {
1451 #ifndef DISABLE_OPENGL
1452 if (render_gl && which <= FRAMEBUFFER_EVEN) {
1453 *pitch = LINEBUF_SIZE * sizeof(uint32_t);
1454 return texture_buf;
1455 } else {
1456 #endif
1457 if (which >= num_textures) {
1458 warning("Request for invalid framebuffer number %d\n", which);
1459 return NULL;
1460 }
1461 void *pixels;
1462 if (SDL_LockTexture(sdl_textures[which], NULL, &pixels, pitch) < 0) {
1463 warning("Failed to lock texture: %s\n", SDL_GetError());
1464 return NULL;
1465 }
1466 static uint8_t last;
1467 if (which <= FRAMEBUFFER_EVEN) {
1468 locked_pixels = pixels;
1469 if (which == FRAMEBUFFER_EVEN) {
1470 pixels += *pitch;
1471 }
1472 locked_pitch = *pitch;
1473 if (which != last) {
1474 *pitch *= 2;
1475 }
1476 last = which;
1477 }
1478 return pixels;
1479 #ifndef DISABLE_OPENGL
1480 }
1481 #endif
1482 }
1483
1484 uint8_t events_processed;
1485 #ifdef __ANDROID__
1486 #define FPS_INTERVAL 10000
1487 #else
1488 #define FPS_INTERVAL 1000
1489 #endif
1490
1491 static uint32_t last_width, last_height;
1492 static uint8_t interlaced;
1493 void render_framebuffer_updated(uint8_t which, int width)
1494 {
1495 static uint8_t last;
1496 if (!sync_to_audio && which <= FRAMEBUFFER_EVEN && source_frame_count < 0) {
1497 source_frame++;
1498 if (source_frame >= source_hz) {
1499 source_frame = 0;
1500 }
1501 source_frame_count = frame_repeat[source_frame];
1502 //TODO: Figure out what to do about SDL Render API texture locking
1503 return;
1504 }
1505
1506 last_width = width;
1507 uint32_t height = which <= FRAMEBUFFER_EVEN
1508 ? (video_standard == VID_NTSC ? 243 : 294) - (overscan_top[video_standard] + overscan_bot[video_standard])
1509 : 240;
1510 FILE *screenshot_file = NULL;
1511 uint32_t shot_height, shot_width;
1512 char *ext;
1513 if (screenshot_path && which == FRAMEBUFFER_ODD) {
1514 screenshot_file = fopen(screenshot_path, "wb");
1515 if (screenshot_file) {
1516 #ifndef DISABLE_ZLIB
1517 ext = path_extension(screenshot_path);
1518 #endif
1519 info_message("Saving screenshot to %s\n", screenshot_path);
1520 } else {
1521 warning("Failed to open screenshot file %s for writing\n", screenshot_path);
1522 }
1523 free(screenshot_path);
1524 screenshot_path = NULL;
1525 shot_height = video_standard == VID_NTSC ? 243 : 294;
1526 shot_width = width;
1527 }
1528 interlaced = last != which;
1529 width -= overscan_left[video_standard] + overscan_right[video_standard];
1530 #ifndef DISABLE_OPENGL
1531 if (render_gl && which <= FRAMEBUFFER_EVEN) {
1532 SDL_GL_MakeCurrent(main_window, main_context);
1533 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]);
1535
1536 if (screenshot_file) {
1537 //properly supporting interlaced modes here is non-trivial, so only save the odd field for now
1538 #ifndef DISABLE_ZLIB
1539 if (!strcasecmp(ext, "png")) {
1540 free(ext);
1541 save_png(screenshot_file, texture_buf, shot_width, shot_height, LINEBUF_SIZE*sizeof(uint32_t));
1542 } else {
1543 free(ext);
1544 #endif
1545 save_ppm(screenshot_file, texture_buf, shot_width, shot_height, LINEBUF_SIZE*sizeof(uint32_t));
1546 #ifndef DISABLE_ZLIB
1547 }
1548 #endif
1549 }
1550 } else {
1551 #endif
1552 if (which <= FRAMEBUFFER_EVEN && last != which) {
1553 uint8_t *cur_dst = (uint8_t *)locked_pixels;
1554 uint8_t *cur_saved = (uint8_t *)texture_buf;
1555 uint32_t dst_off = which == FRAMEBUFFER_EVEN ? 0 : locked_pitch;
1556 uint32_t src_off = which == FRAMEBUFFER_EVEN ? locked_pitch : 0;
1557 for (int i = 0; i < height; ++i)
1558 {
1559 //copy saved line from other field
1560 memcpy(cur_dst + dst_off, cur_saved, locked_pitch);
1561 //save line from this field to buffer for next frame
1562 memcpy(cur_saved, cur_dst + src_off, locked_pitch);
1563 cur_dst += locked_pitch * 2;
1564 cur_saved += locked_pitch;
1565 }
1566 height = 480;
1567 }
1568 if (screenshot_file) {
1569 uint32_t shot_pitch = locked_pitch;
1570 if (which == FRAMEBUFFER_EVEN) {
1571 shot_height *= 2;
1572 } else {
1573 shot_pitch *= 2;
1574 }
1575 #ifndef DISABLE_ZLIB
1576 if (!strcasecmp(ext, "png")) {
1577 free(ext);
1578 save_png(screenshot_file, locked_pixels, shot_width, shot_height, shot_pitch);
1579 } else {
1580 free(ext);
1581 #endif
1582 save_ppm(screenshot_file, locked_pixels, shot_width, shot_height, shot_pitch);
1583 #ifndef DISABLE_ZLIB
1584 }
1585 #endif
1586 }
1587 SDL_UnlockTexture(sdl_textures[which]);
1588 #ifndef DISABLE_OPENGL
1589 }
1590 #endif
1591 last_height = height;
1592 if (which <= FRAMEBUFFER_EVEN) {
1593 render_update_display();
1594 } else {
1595 SDL_RenderCopy(extra_renderers[which - FRAMEBUFFER_USER_START], sdl_textures[which], NULL, NULL);
1596 SDL_RenderPresent(extra_renderers[which - FRAMEBUFFER_USER_START]);
1597 }
1598 if (screenshot_file) {
1599 fclose(screenshot_file);
1600 }
1601 if (which <= FRAMEBUFFER_EVEN) {
1602 last = which;
1603 static uint32_t frame_counter, start;
1604 frame_counter++;
1605 last_frame= SDL_GetTicks();
1606 if ((last_frame - start) > FPS_INTERVAL) {
1607 if (start && (last_frame-start)) {
1608 #ifdef __ANDROID__
1609 info_message("%s - %.1f fps", caption, ((float)frame_counter) / (((float)(last_frame-start)) / 1000.0));
1610 #else
1611 if (!fps_caption) {
1612 fps_caption = malloc(strlen(caption) + strlen(" - 100000000.1 fps") + 1);
1613 }
1614 sprintf(fps_caption, "%s - %.1f fps", caption, ((float)frame_counter) / (((float)(last_frame-start)) / 1000.0));
1615 SDL_SetWindowTitle(main_window, fps_caption);
1616 #endif
1617 }
1618 start = last_frame;
1619 frame_counter = 0;
1620 }
1621 }
1622 if (!sync_to_audio) {
1623 int32_t local_cur_min, local_min_remaining;
1624 SDL_LockAudio();
1625 if (last_buffered > NO_LAST_BUFFERED) {
1626 average_change *= 0.9f;
1627 average_change += (cur_min_buffered - last_buffered) * 0.1f;
1628 }
1629 local_cur_min = cur_min_buffered;
1630 local_min_remaining = min_remaining_buffer;
1631 last_buffered = cur_min_buffered;
1632 SDL_UnlockAudio();
1633 float frames_to_problem;
1634 if (average_change < 0) {
1635 frames_to_problem = (float)local_cur_min / -average_change;
1636 } else {
1637 frames_to_problem = (float)local_min_remaining / average_change;
1638 }
1639 float adjust_ratio = 0.0f;
1640 if (
1641 frames_to_problem < BUFFER_FRAMES_THRESHOLD
1642 || (average_change < 0 && local_cur_min < 3*min_buffered / 4)
1643 || (average_change >0 && local_cur_min > 5 * min_buffered / 4)
1644 || cur_min_buffered < 0
1645 ) {
1646
1647 if (cur_min_buffered < 0) {
1648 adjust_ratio = max_adjust;
1649 SDL_PauseAudio(1);
1650 last_buffered = NO_LAST_BUFFERED;
1651 cur_min_buffered = 0;
1652 } else {
1653 adjust_ratio = -1.0 * average_change / ((float)sample_rate / (float)source_hz);
1654 adjust_ratio /= 2.5 * source_hz;
1655 if (fabsf(adjust_ratio) > max_adjust) {
1656 adjust_ratio = adjust_ratio > 0 ? max_adjust : -max_adjust;
1657 }
1658 }
1659 } else if (local_cur_min < min_buffered / 2) {
1660 adjust_ratio = max_adjust;
1661 }
1662 if (adjust_ratio != 0.0f) {
1663 average_change = 0;
1664 for (uint8_t i = 0; i < num_audio_sources; i++)
1665 {
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 while (source_frame_count > 0)
1670 {
1671 render_update_display();
1672 source_frame_count--;
1673 }
1674 source_frame++;
1675 if (source_frame >= source_hz) {
1676 source_frame = 0;
1677 }
1678 source_frame_count = frame_repeat[source_frame];
1679 }
1680 }
1681
1682 static ui_render_fun render_ui;
1683 void render_set_ui_render_fun(ui_render_fun fun)
1684 {
1685 render_ui = fun;
1686 }
1687
1688 void render_update_display()
1689 {
1690 #ifndef DISABLE_OPENGL
1691 if (render_gl) {
1692 glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
1693 glClear(GL_COLOR_BUFFER_BIT);
1694
1695 glUseProgram(program);
1696 glActiveTexture(GL_TEXTURE0);
1697 glBindTexture(GL_TEXTURE_2D, textures[0]);
1698 glUniform1i(un_textures[0], 0);
1699
1700 glActiveTexture(GL_TEXTURE1);
1701 glBindTexture(GL_TEXTURE_2D, textures[interlaced ? 1 : scanlines ? 2 : 0]);
1702 glUniform1i(un_textures[1], 1);
1703
1704 glUniform1f(un_width, render_emulated_width());
1705 glUniform1f(un_height, last_height);
1706
1707 glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
1708 glVertexAttribPointer(at_pos, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat[2]), (void *)0);
1709 glEnableVertexAttribArray(at_pos);
1710
1711 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]);
1712 glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, (void *)0);
1713
1714 glDisableVertexAttribArray(at_pos);
1715
1716 if (render_ui) {
1717 render_ui();
1718 }
1719
1720 SDL_GL_SwapWindow(main_window);
1721 } else {
1722 #endif
1723 SDL_Rect src_clip = {
1724 .x = overscan_left[video_standard],
1725 .y = overscan_top[video_standard],
1726 .w = render_emulated_width(),
1727 .h = last_height
1728 };
1729 SDL_SetRenderDrawColor(main_renderer, 0, 0, 0, 255);
1730 SDL_RenderClear(main_renderer);
1731 SDL_RenderCopy(main_renderer, sdl_textures[FRAMEBUFFER_ODD], &src_clip, &main_clip);
1732 if (render_ui) {
1733 render_ui();
1734 }
1735 SDL_RenderPresent(main_renderer);
1736 #ifndef DISABLE_OPENGL
1737 }
1738 #endif
1739 if (!events_processed) {
1740 process_events();
1741 }
1742 events_processed = 0;
1743 }
1744
1745 uint32_t render_emulated_width()
1746 {
1747 return last_width - overscan_left[video_standard] - overscan_right[video_standard];
1748 }
1749
1750 uint32_t render_emulated_height()
1751 {
1752 return (video_standard == VID_NTSC ? 243 : 294) - overscan_top[video_standard] - overscan_bot[video_standard];
1753 }
1754
1755 uint32_t render_overscan_left()
1756 {
1757 return overscan_left[video_standard];
1758 }
1759
1760 uint32_t render_overscan_top()
1761 {
1762 return overscan_top[video_standard];
1763 }
1764
1765 void render_wait_quit(vdp_context * context)
1766 {
1767 SDL_Event event;
1768 while(SDL_WaitEvent(&event)) {
1769 switch (event.type) {
1770 case SDL_QUIT:
1771 return;
1772 }
1773 }
1774 }
1775
1776 int render_lookup_button(char *name)
1777 {
1778 static tern_node *button_lookup;
1779 if (!button_lookup) {
1780 for (int i = SDL_CONTROLLER_BUTTON_A; i < SDL_CONTROLLER_BUTTON_MAX; i++)
1781 {
1782 button_lookup = tern_insert_int(button_lookup, SDL_GameControllerGetStringForButton(i), i);
1783 }
1784 //alternative Playstation-style names
1785 button_lookup = tern_insert_int(button_lookup, "cross", SDL_CONTROLLER_BUTTON_A);
1786 button_lookup = tern_insert_int(button_lookup, "circle", SDL_CONTROLLER_BUTTON_B);
1787 button_lookup = tern_insert_int(button_lookup, "square", SDL_CONTROLLER_BUTTON_X);
1788 button_lookup = tern_insert_int(button_lookup, "triangle", SDL_CONTROLLER_BUTTON_Y);
1789 button_lookup = tern_insert_int(button_lookup, "share", SDL_CONTROLLER_BUTTON_BACK);
1790 button_lookup = tern_insert_int(button_lookup, "select", SDL_CONTROLLER_BUTTON_BACK);
1791 button_lookup = tern_insert_int(button_lookup, "options", SDL_CONTROLLER_BUTTON_START);
1792 button_lookup = tern_insert_int(button_lookup, "l1", SDL_CONTROLLER_BUTTON_LEFTSHOULDER);
1793 button_lookup = tern_insert_int(button_lookup, "r1", SDL_CONTROLLER_BUTTON_RIGHTSHOULDER);
1794 button_lookup = tern_insert_int(button_lookup, "l3", SDL_CONTROLLER_BUTTON_LEFTSTICK);
1795 button_lookup = tern_insert_int(button_lookup, "r3", SDL_CONTROLLER_BUTTON_RIGHTSTICK);
1796 }
1797 return (int)tern_find_int(button_lookup, name, SDL_CONTROLLER_BUTTON_INVALID);
1798 }
1799
1800 int render_lookup_axis(char *name)
1801 {
1802 static tern_node *axis_lookup;
1803 if (!axis_lookup) {
1804 for (int i = SDL_CONTROLLER_AXIS_LEFTX; i < SDL_CONTROLLER_AXIS_MAX; i++)
1805 {
1806 axis_lookup = tern_insert_int(axis_lookup, SDL_GameControllerGetStringForAxis(i), i);
1807 }
1808 //alternative Playstation-style names
1809 axis_lookup = tern_insert_int(axis_lookup, "l2", SDL_CONTROLLER_AXIS_TRIGGERLEFT);
1810 axis_lookup = tern_insert_int(axis_lookup, "r2", SDL_CONTROLLER_AXIS_TRIGGERRIGHT);
1811 }
1812 return (int)tern_find_int(axis_lookup, name, SDL_CONTROLLER_AXIS_INVALID);
1813 }
1814
1815 int32_t render_translate_input_name(int32_t controller, char *name, uint8_t is_axis)
1816 {
1817 tern_node *button_lookup, *axis_lookup;
1818 if (controller > MAX_JOYSTICKS || !joysticks[controller]) {
1819 return RENDER_NOT_PLUGGED_IN;
1820 }
1821
1822 if (!SDL_IsGameController(joystick_sdl_index[controller])) {
1823 return RENDER_NOT_MAPPED;
1824 }
1825 SDL_GameController *control = SDL_GameControllerOpen(joystick_sdl_index[controller]);
1826 if (!control) {
1827 warning("Failed to open game controller %d: %s\n", controller, SDL_GetError());
1828 return RENDER_NOT_PLUGGED_IN;
1829 }
1830
1831 SDL_GameControllerButtonBind cbind;
1832 if (is_axis) {
1833
1834 int sdl_axis = render_lookup_axis(name);
1835 if (sdl_axis == SDL_CONTROLLER_AXIS_INVALID) {
1836 SDL_GameControllerClose(control);
1837 return RENDER_INVALID_NAME;
1838 }
1839 cbind = SDL_GameControllerGetBindForAxis(control, sdl_axis);
1840 } else {
1841 int sdl_button = render_lookup_button(name);
1842 if (sdl_button == SDL_CONTROLLER_BUTTON_INVALID) {
1843 SDL_GameControllerClose(control);
1844 return RENDER_INVALID_NAME;
1845 }
1846 cbind = SDL_GameControllerGetBindForButton(control, sdl_button);
1847 }
1848 SDL_GameControllerClose(control);
1849 switch (cbind.bindType)
1850 {
1851 case SDL_CONTROLLER_BINDTYPE_BUTTON:
1852 return cbind.value.button;
1853 case SDL_CONTROLLER_BINDTYPE_AXIS:
1854 return RENDER_AXIS_BIT | cbind.value.axis;
1855 case SDL_CONTROLLER_BINDTYPE_HAT:
1856 return RENDER_DPAD_BIT | (cbind.value.hat.hat << 4) | cbind.value.hat.hat_mask;
1857 }
1858 return RENDER_NOT_MAPPED;
1859 }
1860
1861 int32_t render_dpad_part(int32_t input)
1862 {
1863 return input >> 4 & 0xFFFFFF;
1864 }
1865
1866 uint8_t render_direction_part(int32_t input)
1867 {
1868 return input & 0xF;
1869 }
1870
1871 int32_t render_axis_part(int32_t input)
1872 {
1873 return input & 0xFFFFFFF;
1874 }
1875
1061 void process_events() 1876 void process_events()
1062 { 1877 {
1063 if (events_processed > MAX_EVENT_POLL_PER_FRAME) { 1878 if (events_processed > MAX_EVENT_POLL_PER_FRAME) {
1064 return; 1879 return;
1065 } 1880 }
1068 } 1883 }
1069 1884
1070 #define TOGGLE_MIN_DELAY 250 1885 #define TOGGLE_MIN_DELAY 250
1071 void render_toggle_fullscreen() 1886 void render_toggle_fullscreen()
1072 { 1887 {
1073 static int in_toggle;
1074 //protect against event processing causing us to attempt to toggle while still toggling 1888 //protect against event processing causing us to attempt to toggle while still toggling
1075 if (in_toggle) { 1889 if (in_toggle) {
1076 return; 1890 return;
1077 } 1891 }
1078 in_toggle = 1; 1892 in_toggle = 1;
1108 SDL_SetWindowSize(main_window, windowed_width, windowed_height); 1922 SDL_SetWindowSize(main_window, windowed_width, windowed_height);
1109 drain_events(); 1923 drain_events();
1110 in_toggle = 0; 1924 in_toggle = 0;
1111 } 1925 }
1112 1926
1113 void render_wait_psg(psg_context * context)
1114 {
1115 SDL_LockMutex(audio_mutex);
1116 while (current_psg != NULL) {
1117 SDL_CondWait(psg_cond, audio_mutex);
1118 }
1119 current_psg = context->audio_buffer;
1120 SDL_CondSignal(audio_ready);
1121
1122 context->audio_buffer = context->back_buffer;
1123 context->back_buffer = current_psg;
1124 SDL_UnlockMutex(audio_mutex);
1125 context->buffer_pos = 0;
1126 }
1127
1128 void render_wait_ym(ym2612_context * context)
1129 {
1130 SDL_LockMutex(audio_mutex);
1131 while (current_ym != NULL) {
1132 SDL_CondWait(ym_cond, audio_mutex);
1133 }
1134 current_ym = context->audio_buffer;
1135 SDL_CondSignal(audio_ready);
1136
1137 context->audio_buffer = context->back_buffer;
1138 context->back_buffer = current_ym;
1139 SDL_UnlockMutex(audio_mutex);
1140 context->buffer_pos = 0;
1141 }
1142
1143 uint32_t render_audio_buffer() 1927 uint32_t render_audio_buffer()
1144 { 1928 {
1145 return buffer_samples; 1929 return buffer_samples;
1146 } 1930 }
1147 1931
1163 void render_infobox(char *title, char *message) 1947 void render_infobox(char *title, char *message)
1164 { 1948 {
1165 SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, title, message, NULL); 1949 SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, title, message, NULL);
1166 } 1950 }
1167 1951
1952 uint32_t render_elapsed_ms(void)
1953 {
1954 return SDL_GetTicks();
1955 }
1956
1957 void render_sleep_ms(uint32_t delay)
1958 {
1959 return SDL_Delay(delay);
1960 }
1961
1962 uint8_t render_has_gl(void)
1963 {
1964 return render_gl;
1965 }
1966
1967 uint8_t render_get_active_framebuffer(void)
1968 {
1969 if (SDL_GetWindowFlags(main_window) & SDL_WINDOW_INPUT_FOCUS) {
1970 return FRAMEBUFFER_ODD;
1971 }
1972 for (int i = 0; i < num_textures - 2; i++)
1973 {
1974 if (extra_windows[i] && (SDL_GetWindowFlags(extra_windows[i]) & SDL_WINDOW_INPUT_FOCUS)) {
1975 return FRAMEBUFFER_USER_START + i;
1976 }
1977 }
1978 return 0xFF;
1979 }