Mercurial > repos > blastem
comparison render_sdl.c @ 1202:a6ae693974e0
Allow toggling full screen mode at runtime. Allow resizing the window in windowed mode. Allow specifying the aspect ratio in the config file.
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Thu, 26 Jan 2017 00:55:02 -0800 |
parents | 8715174e9366 |
children | 3d3bad51183d |
comparison
equal
deleted
inserted
replaced
1201:aee2177a1630 | 1202:a6ae693974e0 |
---|---|
24 static SDL_Texture **sdl_textures; | 24 static SDL_Texture **sdl_textures; |
25 static uint8_t num_textures; | 25 static uint8_t num_textures; |
26 static SDL_Rect main_clip; | 26 static SDL_Rect main_clip; |
27 static SDL_GLContext *main_context; | 27 static SDL_GLContext *main_context; |
28 | 28 |
29 static int main_width, main_height, is_fullscreen; | 29 static int main_width, main_height, windowed_width, windowed_height, is_fullscreen; |
30 | 30 |
31 static uint8_t render_gl = 1; | 31 static uint8_t render_gl = 1; |
32 static uint8_t scanlines = 0; | 32 static uint8_t scanlines = 0; |
33 | 33 |
34 static uint32_t last_frame = 0; | 34 static uint32_t last_frame = 0; |
134 } | 134 } |
135 | 135 |
136 #ifndef DISABLE_OPENGL | 136 #ifndef DISABLE_OPENGL |
137 static GLuint textures[3], buffers[2], vshader, fshader, program, un_textures[2], un_width, un_height, at_pos; | 137 static GLuint textures[3], buffers[2], vshader, fshader, program, un_textures[2], un_width, un_height, at_pos; |
138 | 138 |
139 static GLfloat vertex_data[] = { | 139 static GLfloat vertex_data_default[] = { |
140 -1.0f, -1.0f, | 140 -1.0f, -1.0f, |
141 1.0f, -1.0f, | 141 1.0f, -1.0f, |
142 -1.0f, 1.0f, | 142 -1.0f, 1.0f, |
143 1.0f, 1.0f | 143 1.0f, 1.0f |
144 }; | 144 }; |
145 | |
146 static GLfloat vertex_data[8]; | |
145 | 147 |
146 static const GLushort element_data[] = {0, 1, 2, 3}; | 148 static const GLushort element_data[] = {0, 1, 2, 3}; |
147 | 149 |
148 static GLuint load_shader(char * fname, GLenum shader_type) | 150 static GLuint load_shader(char * fname, GLenum shader_type) |
149 { | 151 { |
187 return ret; | 189 return ret; |
188 } | 190 } |
189 #endif | 191 #endif |
190 | 192 |
191 static uint32_t texture_buf[512 * 513]; | 193 static uint32_t texture_buf[512 * 513]; |
194 #ifndef DISABLE_OPENGL | |
195 static void gl_setup() | |
196 { | |
197 glGenTextures(3, textures); | |
198 for (int i = 0; i < 3; i++) | |
199 { | |
200 glBindTexture(GL_TEXTURE_2D, textures[i]); | |
201 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
202 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |
203 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | |
204 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | |
205 if (i < 2) { | |
206 //TODO: Fixme for PAL + invalid display mode | |
207 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 512, 512, 0, GL_BGRA, GL_UNSIGNED_BYTE, texture_buf); | |
208 } else { | |
209 uint32_t blank = 255 << 24; | |
210 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_BGRA, GL_UNSIGNED_BYTE, &blank); | |
211 } | |
212 } | |
213 glGenBuffers(2, buffers); | |
214 glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); | |
215 glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data, GL_STATIC_DRAW); | |
216 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]); | |
217 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(element_data), element_data, GL_STATIC_DRAW); | |
218 tern_val def = {.ptrval = "default.v.glsl"}; | |
219 vshader = load_shader(tern_find_path_default(config, "video\0vertex_shader\0", def).ptrval, GL_VERTEX_SHADER); | |
220 def.ptrval = "default.f.glsl"; | |
221 fshader = load_shader(tern_find_path_default(config, "video\0fragment_shader\0", def).ptrval, GL_FRAGMENT_SHADER); | |
222 program = glCreateProgram(); | |
223 glAttachShader(program, vshader); | |
224 glAttachShader(program, fshader); | |
225 glLinkProgram(program); | |
226 GLint link_status; | |
227 glGetProgramiv(program, GL_LINK_STATUS, &link_status); | |
228 if (!link_status) { | |
229 fputs("Failed to link shader program\n", stderr); | |
230 exit(1); | |
231 } | |
232 un_textures[0] = glGetUniformLocation(program, "textures[0]"); | |
233 un_textures[1] = glGetUniformLocation(program, "textures[1]"); | |
234 un_width = glGetUniformLocation(program, "width"); | |
235 un_height = glGetUniformLocation(program, "height"); | |
236 at_pos = glGetAttribLocation(program, "pos"); | |
237 } | |
238 #endif | |
239 | |
192 static void render_alloc_surfaces() | 240 static void render_alloc_surfaces() |
193 { | 241 { |
194 static uint8_t texture_init; | 242 static uint8_t texture_init; |
195 | 243 |
196 if (texture_init) { | 244 if (texture_init) { |
200 num_textures = 2; | 248 num_textures = 2; |
201 texture_init = 1; | 249 texture_init = 1; |
202 #ifndef DISABLE_OPENGL | 250 #ifndef DISABLE_OPENGL |
203 if (render_gl) { | 251 if (render_gl) { |
204 sdl_textures[0] = sdl_textures[1] = NULL; | 252 sdl_textures[0] = sdl_textures[1] = NULL; |
205 glGenTextures(3, textures); | 253 gl_setup(); |
206 for (int i = 0; i < 3; i++) | |
207 { | |
208 glBindTexture(GL_TEXTURE_2D, textures[i]); | |
209 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
210 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |
211 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | |
212 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | |
213 if (i < 2) { | |
214 //TODO: Fixme for PAL + invalid display mode | |
215 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 512, 512, 0, GL_BGRA, GL_UNSIGNED_BYTE, texture_buf); | |
216 } else { | |
217 uint32_t blank = 255 << 24; | |
218 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_BGRA, GL_UNSIGNED_BYTE, &blank); | |
219 } | |
220 } | |
221 glGenBuffers(2, buffers); | |
222 glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); | |
223 glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data, GL_STATIC_DRAW); | |
224 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]); | |
225 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(element_data), element_data, GL_STATIC_DRAW); | |
226 tern_val def = {.ptrval = "default.v.glsl"}; | |
227 vshader = load_shader(tern_find_path_default(config, "video\0vertex_shader\0", def).ptrval, GL_VERTEX_SHADER); | |
228 def.ptrval = "default.f.glsl"; | |
229 fshader = load_shader(tern_find_path_default(config, "video\0fragment_shader\0", def).ptrval, GL_FRAGMENT_SHADER); | |
230 program = glCreateProgram(); | |
231 glAttachShader(program, vshader); | |
232 glAttachShader(program, fshader); | |
233 glLinkProgram(program); | |
234 GLint link_status; | |
235 glGetProgramiv(program, GL_LINK_STATUS, &link_status); | |
236 if (!link_status) { | |
237 fputs("Failed to link shader program\n", stderr); | |
238 exit(1); | |
239 } | |
240 un_textures[0] = glGetUniformLocation(program, "textures[0]"); | |
241 un_textures[1] = glGetUniformLocation(program, "textures[1]"); | |
242 un_width = glGetUniformLocation(program, "width"); | |
243 un_height = glGetUniformLocation(program, "height"); | |
244 at_pos = glGetAttribLocation(program, "pos"); | |
245 } else { | 254 } else { |
246 #endif | 255 #endif |
247 | 256 |
248 //TODO: Fixme for invalid display mode | 257 //TODO: Fixme for invalid display mode |
249 sdl_textures[0] = sdl_textures[1] = SDL_CreateTexture(main_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 320, 588); | 258 sdl_textures[0] = sdl_textures[1] = SDL_CreateTexture(main_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 320, 588); |
264 SDL_DestroyTexture(sdl_textures[i]); | 273 SDL_DestroyTexture(sdl_textures[i]); |
265 } | 274 } |
266 } | 275 } |
267 } | 276 } |
268 | 277 |
278 static void update_aspect() | |
279 { | |
280 //reset default values | |
281 memcpy(vertex_data, vertex_data_default, sizeof(vertex_data)); | |
282 main_clip.w = main_width; | |
283 main_clip.h = main_height; | |
284 main_clip.x = main_clip.y = 0; | |
285 //calculate configured aspect ratio | |
286 char *config_aspect = tern_find_path_default(config, "video\0aspect\0", (tern_val){.ptrval = "4:3"}).ptrval; | |
287 if (strcmp("stretch", config_aspect)) { | |
288 float src_aspect = 4.0f/3.0f; | |
289 char *end; | |
290 float aspect_numerator = strtof(config_aspect, &end); | |
291 if (aspect_numerator > 0.0f && *end == ':') { | |
292 float aspect_denominator = strtof(end+1, &end); | |
293 if (aspect_denominator > 0.0f && !*end) { | |
294 src_aspect = aspect_numerator / aspect_denominator; | |
295 } | |
296 } | |
297 float aspect = (float)main_width / main_height; | |
298 if (fabs(aspect - src_aspect) < 0.01f) { | |
299 //close enough for government work | |
300 return; | |
301 } | |
302 #ifndef DISABLE_OPENGL | |
303 if (render_gl) { | |
304 for (int i = 0; i < 4; i++) | |
305 { | |
306 if (aspect > src_aspect) { | |
307 vertex_data[i*2] *= src_aspect/aspect; | |
308 } else { | |
309 vertex_data[i*2+1] *= aspect/src_aspect; | |
310 } | |
311 } | |
312 } else { | |
313 #endif | |
314 main_clip.w = aspect > src_aspect ? src_aspect * (float)main_height : main_width; | |
315 main_clip.h = aspect > src_aspect ? main_height : main_width / src_aspect; | |
316 main_clip.x = (main_width - main_clip.w) / 2; | |
317 main_clip.y = (main_height - main_clip.h) / 2; | |
318 #ifndef DISABLE_OPENGL | |
319 } | |
320 #endif | |
321 } | |
322 } | |
323 | |
269 static uint32_t overscan_top[NUM_VID_STD] = {2, 21}; | 324 static uint32_t overscan_top[NUM_VID_STD] = {2, 21}; |
270 static uint32_t overscan_bot[NUM_VID_STD] = {1, 17}; | 325 static uint32_t overscan_bot[NUM_VID_STD] = {1, 17}; |
271 static vid_std video_standard = VID_NTSC; | 326 static vid_std video_standard = VID_NTSC; |
272 static char *vid_std_names[NUM_VID_STD] = {"ntsc", "pal"}; | 327 static char *vid_std_names[NUM_VID_STD] = {"ntsc", "pal"}; |
273 void render_init(int width, int height, char * title, uint8_t fullscreen) | 328 void render_init(int width, int height, char * title, uint8_t fullscreen) |
275 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) < 0) { | 330 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) < 0) { |
276 fatal_error("Unable to init SDL: %s\n", SDL_GetError()); | 331 fatal_error("Unable to init SDL: %s\n", SDL_GetError()); |
277 } | 332 } |
278 atexit(SDL_Quit); | 333 atexit(SDL_Quit); |
279 printf("width: %d, height: %d\n", width, height); | 334 printf("width: %d, height: %d\n", width, height); |
280 | 335 windowed_width = width; |
281 uint32_t flags = 0; | 336 windowed_height = height; |
337 | |
338 uint32_t flags = SDL_WINDOW_RESIZABLE; | |
282 | 339 |
283 if (fullscreen) { | 340 if (fullscreen) { |
284 flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; | 341 flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; |
285 SDL_DisplayMode mode; | 342 SDL_DisplayMode mode; |
286 //TODO: Multiple monitor support | 343 //TODO: Multiple monitor support |
372 main_clip.h = height; | 429 main_clip.h = height; |
373 #ifndef DISABLE_OPENGL | 430 #ifndef DISABLE_OPENGL |
374 } | 431 } |
375 #endif | 432 #endif |
376 | 433 |
377 SDL_GetWindowSize(main_window, &width, &height); | 434 SDL_GetWindowSize(main_window, &main_width, &main_height); |
378 printf("Window created with size: %d x %d\n", width, height); | 435 printf("Window created with size: %d x %d\n", main_width, main_height); |
379 float src_aspect = 4.0/3.0; | 436 update_aspect(); |
380 float aspect = (float)width / height; | |
381 def.ptrval = "normal"; | |
382 int stretch = fabs(aspect - src_aspect) > 0.01 && !strcmp(tern_find_path_default(config, "video\0aspect\0", def).ptrval, "stretch"); | |
383 | |
384 if (!stretch) { | |
385 #ifndef DISABLE_OPENGL | |
386 if (render_gl) { | |
387 for (int i = 0; i < 4; i++) | |
388 { | |
389 if (aspect > src_aspect) { | |
390 vertex_data[i*2] *= src_aspect/aspect; | |
391 } else { | |
392 vertex_data[i*2+1] *= aspect/src_aspect; | |
393 } | |
394 } | |
395 } else { | |
396 #endif | |
397 float scale_x = (float)width / 320.0; | |
398 float scale_y = (float)height / 240.0; | |
399 float scale = scale_x > scale_y ? scale_y : scale_x; | |
400 main_clip.w = 320.0 * scale; | |
401 main_clip.h = 240.0 * scale; | |
402 main_clip.x = (width - main_clip.w) / 2; | |
403 main_clip.y = (height - main_clip.h) / 2; | |
404 #ifndef DISABLE_OPENGL | |
405 } | |
406 #endif | |
407 } | |
408 render_alloc_surfaces(); | 437 render_alloc_surfaces(); |
409 def.ptrval = "off"; | 438 def.ptrval = "off"; |
410 scanlines = !strcmp(tern_find_path_default(config, "video\0scanlines\0", def).ptrval, "on"); | 439 scanlines = !strcmp(tern_find_path_default(config, "video\0scanlines\0", def).ptrval, "on"); |
411 | 440 |
412 caption = title; | 441 caption = title; |
841 handle_mousedown(event->button.which, event->button.button); | 870 handle_mousedown(event->button.which, event->button.button); |
842 break; | 871 break; |
843 case SDL_MOUSEBUTTONUP: | 872 case SDL_MOUSEBUTTONUP: |
844 handle_mouseup(event->button.which, event->button.button); | 873 handle_mouseup(event->button.which, event->button.button); |
845 break; | 874 break; |
875 case SDL_WINDOWEVENT: | |
876 switch (event->window.event) | |
877 { | |
878 case SDL_WINDOWEVENT_SIZE_CHANGED: | |
879 main_width = event->window.data1; | |
880 main_height = event->window.data2; | |
881 update_aspect(); | |
882 #ifndef DISABLE_OPENGL | |
883 if (render_gl) { | |
884 SDL_GL_DeleteContext(main_context); | |
885 main_context = SDL_GL_CreateContext(main_window); | |
886 gl_setup(); | |
887 } | |
888 #endif | |
889 break; | |
890 } | |
891 break; | |
846 case SDL_QUIT: | 892 case SDL_QUIT: |
847 puts(""); | 893 puts(""); |
848 exit(0); | 894 exit(0); |
849 } | 895 } |
850 return 0; | 896 return 0; |
851 } | 897 } |
852 | 898 |
899 static void drain_events() | |
900 { | |
901 SDL_Event event; | |
902 while(SDL_PollEvent(&event)) | |
903 { | |
904 handle_event(&event); | |
905 } | |
906 } | |
907 | |
853 void process_events() | 908 void process_events() |
854 { | 909 { |
855 if (events_processed > MAX_EVENT_POLL_PER_FRAME) { | 910 if (events_processed > MAX_EVENT_POLL_PER_FRAME) { |
856 return; | 911 return; |
857 } | 912 } |
858 SDL_Event event; | 913 drain_events(); |
859 while(SDL_PollEvent(&event)) { | |
860 handle_event(&event); | |
861 } | |
862 events_processed++; | 914 events_processed++; |
915 } | |
916 | |
917 #define TOGGLE_MIN_DELAY 250 | |
918 void render_toggle_fullscreen() | |
919 { | |
920 static int in_toggle; | |
921 //protect against event processing causing us to attempt to toggle while still toggling | |
922 if (in_toggle) { | |
923 return; | |
924 } | |
925 in_toggle = 1; | |
926 | |
927 //toggling too fast seems to cause a deadlock | |
928 static uint32_t last_toggle; | |
929 uint32_t cur = SDL_GetTicks(); | |
930 if (last_toggle && cur - last_toggle < TOGGLE_MIN_DELAY) { | |
931 in_toggle = 0; | |
932 return; | |
933 } | |
934 last_toggle = cur; | |
935 | |
936 drain_events(); | |
937 is_fullscreen = !is_fullscreen; | |
938 if (is_fullscreen) { | |
939 SDL_DisplayMode mode; | |
940 //TODO: Multiple monitor support | |
941 SDL_GetCurrentDisplayMode(0, &mode); | |
942 //In theory, the SDL2 docs suggest this is unnecessary | |
943 //but without it the OpenGL context remains the original size | |
944 //This needs to happen before the fullscreen transition to have any effect | |
945 //because SDL does not apply window size changes in fullscreen | |
946 SDL_SetWindowSize(main_window, mode.w, mode.h); | |
947 } | |
948 SDL_SetWindowFullscreen(main_window, is_fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); | |
949 //Since we change the window size on transition to full screen | |
950 //we need to set it back to normal so we can also go back to windowed mode | |
951 //normally you would think that this should only be done when actually transitioning | |
952 //but something is screwy in the guts of SDL (at least on Linux) and setting it each time | |
953 //is the only thing that seems to work reliably | |
954 //when we've just switched to fullscreen mode this should be harmless though | |
955 SDL_SetWindowSize(main_window, windowed_width, windowed_height); | |
956 drain_events(); | |
957 in_toggle = 0; | |
863 } | 958 } |
864 | 959 |
865 void render_wait_psg(psg_context * context) | 960 void render_wait_psg(psg_context * context) |
866 { | 961 { |
867 SDL_LockMutex(audio_mutex); | 962 SDL_LockMutex(audio_mutex); |