comparison render_sdl.c @ 719:019d27995e32

Upgrade to SDL 2.0 and drop support for the non-OpenGL render path
author Michael Pavone <pavone@retrodev.com>
date Wed, 20 May 2015 19:05:11 -0700
parents 38006d43f5a3
children fc68992cf18d
comparison
equal deleted inserted replaced
718:eaba6789f316 719:019d27995e32
9 #include "render.h" 9 #include "render.h"
10 #include "blastem.h" 10 #include "blastem.h"
11 #include "io.h" 11 #include "io.h"
12 #include "util.h" 12 #include "util.h"
13 13
14 #ifndef DISABLE_OPENGL
15 #include <GL/glew.h> 14 #include <GL/glew.h>
16 #endif 15
17 16 SDL_Window *main_window;
18 SDL_Surface *screen; 17 SDL_GLContext *main_context;
19 uint8_t render_dbg = 0; 18 uint8_t render_dbg = 0;
20 uint8_t debug_pal = 0; 19 uint8_t debug_pal = 0;
21 uint8_t render_gl = 1;
22 20
23 uint32_t last_frame = 0; 21 uint32_t last_frame = 0;
24 22
25 uint32_t min_delay; 23 uint32_t min_delay;
26 uint32_t frame_delay = 1000/60; 24 uint32_t frame_delay = 1000/60;
90 return num_joysticks; 88 return num_joysticks;
91 } 89 }
92 90
93 uint32_t render_map_color(uint8_t r, uint8_t g, uint8_t b) 91 uint32_t render_map_color(uint8_t r, uint8_t g, uint8_t b)
94 { 92 {
95 if (render_gl) { 93 return 255 << 24 | r << 16 | g << 8 | b;
96 return 255 << 24 | r << 16 | g << 8 | b; 94 }
97 } else { 95
98 return SDL_MapRGB(screen->format, r, g, b);
99 }
100 }
101
102 #ifndef DISABLE_OPENGL
103 GLuint textures[3], buffers[2], vshader, fshader, program, un_textures[2], un_width, at_pos; 96 GLuint textures[3], buffers[2], vshader, fshader, program, un_textures[2], un_width, at_pos;
104 97
105 GLfloat vertex_data[] = { 98 GLfloat vertex_data[] = {
106 -1.0f, -1.0f, 99 -1.0f, -1.0f,
107 1.0f, -1.0f, 100 1.0f, -1.0f,
151 glDeleteShader(ret); 144 glDeleteShader(ret);
152 return 0; 145 return 0;
153 } 146 }
154 return ret; 147 return ret;
155 } 148 }
156 #endif
157 149
158 void render_alloc_surfaces(vdp_context * context) 150 void render_alloc_surfaces(vdp_context * context)
159 { 151 {
160 #ifndef DISABLE_OPENGL 152 context->oddbuf = context->framebuf = malloc(512 * 256 * 4 * 2);
161 if (render_gl) { 153 memset(context->oddbuf, 0, 512 * 256 * 4 * 2);
162 context->oddbuf = context->framebuf = malloc(512 * 256 * 4 * 2); 154 context->evenbuf = ((char *)context->oddbuf) + 512 * 256 * 4;
163 memset(context->oddbuf, 0, 512 * 256 * 4 * 2); 155 glGenTextures(3, textures);
164 context->evenbuf = ((char *)context->oddbuf) + 512 * 256 * 4; 156 for (int i = 0; i < 3; i++)
165 glGenTextures(3, textures); 157 {
166 for (int i = 0; i < 3; i++) 158 glBindTexture(GL_TEXTURE_2D, textures[i]);
167 { 159 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
168 glBindTexture(GL_TEXTURE_2D, textures[i]); 160 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
169 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 161 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
170 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 162 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
171 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 163 if (i < 2) {
172 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 164 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 512, 256, 0, GL_BGRA, GL_UNSIGNED_BYTE, i ? context->evenbuf : context->oddbuf);
173 if (i < 2) { 165 } else {
174 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 512, 256, 0, GL_BGRA, GL_UNSIGNED_BYTE, i ? context->evenbuf : context->oddbuf); 166 uint32_t blank = 255 << 24;
175 } else { 167 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_BGRA, GL_UNSIGNED_BYTE, &blank);
176 uint32_t blank = 255 << 24; 168 }
177 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_BGRA, GL_UNSIGNED_BYTE, &blank); 169 }
178 } 170 glGenBuffers(2, buffers);
179 } 171 glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
180 glGenBuffers(2, buffers); 172 glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data, GL_STATIC_DRAW);
181 glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); 173 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]);
182 glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data, GL_STATIC_DRAW); 174 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(element_data), element_data, GL_STATIC_DRAW);
183 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]); 175 vshader = load_shader(tern_find_ptr_default(config, "videovertex_shader", "default.v.glsl"), GL_VERTEX_SHADER);
184 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(element_data), element_data, GL_STATIC_DRAW); 176 fshader = load_shader(tern_find_ptr_default(config, "videofragment_shader", "default.f.glsl"), GL_FRAGMENT_SHADER);
185 vshader = load_shader(tern_find_ptr_default(config, "videovertex_shader", "default.v.glsl"), GL_VERTEX_SHADER); 177 program = glCreateProgram();
186 fshader = load_shader(tern_find_ptr_default(config, "videofragment_shader", "default.f.glsl"), GL_FRAGMENT_SHADER); 178 glAttachShader(program, vshader);
187 program = glCreateProgram(); 179 glAttachShader(program, fshader);
188 glAttachShader(program, vshader); 180 glLinkProgram(program);
189 glAttachShader(program, fshader); 181 GLint link_status;
190 glLinkProgram(program); 182 glGetProgramiv(program, GL_LINK_STATUS, &link_status);
191 GLint link_status; 183 if (!link_status) {
192 glGetProgramiv(program, GL_LINK_STATUS, &link_status); 184 fputs("Failed to link shader program\n", stderr);
193 if (!link_status) { 185 exit(1);
194 fputs("Failed to link shader program\n", stderr); 186 }
195 exit(1); 187 un_textures[0] = glGetUniformLocation(program, "textures[0]");
196 } 188 un_textures[1] = glGetUniformLocation(program, "textures[1]");
197 un_textures[0] = glGetUniformLocation(program, "textures[0]"); 189 un_width = glGetUniformLocation(program, "width");
198 un_textures[1] = glGetUniformLocation(program, "textures[1]"); 190 at_pos = glGetAttribLocation(program, "pos");
199 un_width = glGetUniformLocation(program, "width");
200 at_pos = glGetAttribLocation(program, "pos");
201 } else {
202 #endif
203 context->oddbuf = context->framebuf = malloc(320 * 240 * screen->format->BytesPerPixel * 2);
204 context->evenbuf = ((char *)context->oddbuf) + 320 * 240 * screen->format->BytesPerPixel;
205 #ifndef DISABLE_OPENGL
206 }
207 #endif
208 }
209
210 uint8_t render_depth()
211 {
212 return screen->format->BytesPerPixel * 8;
213 } 191 }
214 192
215 char * caption = NULL; 193 char * caption = NULL;
216 194
217 void render_init(int width, int height, char * title, uint32_t fps, uint8_t fullscreen, uint8_t use_gl) 195 void render_init(int width, int height, char * title, uint32_t fps, uint8_t fullscreen)
218 { 196 {
219 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK) < 0) { 197 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK) < 0) {
220 fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError()); 198 fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError());
221 exit(1); 199 exit(1);
222 } 200 }
223 printf("width: %d, height: %d\n", width, height); 201 printf("width: %d, height: %d\n", width, height);
224 uint32_t flags = SDL_ANYFORMAT; 202 uint32_t flags = SDL_WINDOW_OPENGL;
225 203
226 #ifndef DISABLE_OPENGL 204
227 if (use_gl) 205 SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
228 { 206 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
229 SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5); 207 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
230 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5); 208 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0);
231 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5); 209 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
232 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0); 210 if (fullscreen) {
233 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); 211 flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
234 flags = SDL_OPENGL; 212 SDL_DisplayMode mode;
235 if (fullscreen) { 213 //TODO: Multiple monitor support
236 flags |= SDL_FULLSCREEN; 214 SDL_GetCurrentDisplayMode(0, &mode);
237 } 215 //the SDL2 migration guide suggests setting width and height to 0 when using SDL_WINDOW_FULLSCREEN_DESKTOP
238 } else { 216 //but that doesn't seem to work right when using OpenGL, at least on Linux anyway
239 #else 217 width = mode.w;
240 { 218 height = mode.h;
241 #endif 219 }
242 if (fullscreen) { 220 main_window = SDL_CreateWindow(title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, flags);
243 flags |= SDL_FULLSCREEN | SDL_HWSURFACE | SDL_DOUBLEBUF; 221 if (!main_window) {
244 } else { 222 fprintf(stderr, "Unable to create SDL window: %s\n", SDL_GetError());
245 flags |= SDL_SWSURFACE;
246 }
247 }
248 screen = SDL_SetVideoMode(width, height, 32, flags);
249 if (!screen) {
250 fprintf(stderr, "Unable to get SDL surface: %s\n", SDL_GetError());
251 SDL_Quit(); 223 SDL_Quit();
252 exit(1); 224 exit(1);
253 } 225 }
254 if (!use_gl && screen->format->BytesPerPixel != 2 && screen->format->BytesPerPixel != 4) { 226 SDL_GetWindowSize(main_window, &width, &height);
255 fprintf(stderr, "BlastEm requires a 16-bit or 32-bit surface, SDL returned a %d-bit surface\n", screen->format->BytesPerPixel * 8); 227 printf("Window created with size: %d x %d\n", width, height);
228 main_context = SDL_GL_CreateContext(main_window);
229 GLenum res = glewInit();
230 if (res != GLEW_OK) {
231 fprintf(stderr, "Initialization of GLEW failed with code %d\n", res);
256 SDL_Quit(); 232 SDL_Quit();
257 exit(1); 233 exit(1);
258 } 234 }
259 #ifndef DISABLE_OPENGL 235 if (!GLEW_VERSION_2_0) {
260 //TODO: fallback on standard rendering if OpenGL 2.0 is unavailable or if init fails 236 fputs("BlastEm requires at least OpenGL 2.0, but it is unavailable\n", stderr);
261 if (use_gl) 237 SDL_Quit();
262 { 238 exit(1);
263 GLenum res = glewInit(); 239 }
264 if (res != GLEW_OK) { 240 float aspect = (float)width / height;
265 fprintf(stderr, "Initialization of GLEW failed with code %d\n", res); 241 if (fabs(aspect - 4.0/3.0) > 0.01 && strcmp(tern_find_ptr_default(config, "videoaspect", "normal"), "stretch")) {
266 SDL_Quit(); 242 for (int i = 0; i < 4; i++)
267 exit(1); 243 {
268 } 244 if (aspect > 4.0/3.0) {
269 if (!GLEW_VERSION_2_0) { 245 vertex_data[i*2] *= (4.0/3.0)/aspect;
270 fputs("OpenGL 2.0 is unable, falling back to standard SDL rendering\n", stderr); 246 } else {
271 SDL_Quit(); 247 vertex_data[i*2+1] *= aspect/(4.0/3.0);
272 exit(1); 248 }
273 } 249 }
274 float aspect = (float)width / height; 250 }
275 if (fabs(aspect - 4.0/3.0) > 0.01 && strcmp(tern_find_ptr_default(config, "videoaspect", "normal"), "stretch")) {
276 for (int i = 0; i < 4; i++)
277 {
278 if (aspect > 4.0/3.0) {
279 vertex_data[i*2] *= (4.0/3.0)/aspect;
280 } else {
281 vertex_data[i*2+1] *= aspect/(4.0/3.0);
282 }
283 }
284 }
285 }
286 render_gl = use_gl;
287 #endif
288 SDL_WM_SetCaption(title, title);
289 caption = title; 251 caption = title;
290 min_delay = 0; 252 min_delay = 0;
291 for (int i = 0; i < 100; i++) { 253 for (int i = 0; i < 100; i++) {
292 uint32_t start = SDL_GetTicks(); 254 uint32_t start = SDL_GetTicks();
293 SDL_Delay(1); 255 SDL_Delay(1);
339 num_joysticks = SDL_NumJoysticks(); 301 num_joysticks = SDL_NumJoysticks();
340 if (num_joysticks > MAX_JOYSTICKS) { 302 if (num_joysticks > MAX_JOYSTICKS) {
341 num_joysticks = MAX_JOYSTICKS; 303 num_joysticks = MAX_JOYSTICKS;
342 } 304 }
343 for (int i = 0; i < num_joysticks; i++) { 305 for (int i = 0; i < num_joysticks; i++) {
344 printf("Joystick %d: %s\n", i, SDL_JoystickName(i));
345 SDL_Joystick * joy = joysticks[i] = SDL_JoystickOpen(i); 306 SDL_Joystick * joy = joysticks[i] = SDL_JoystickOpen(i);
307 printf("Joystick %d: %s\n", i, SDL_JoystickName(joy));
346 if (joy) { 308 if (joy) {
347 printf("\tNum Axes: %d\n\tNum Buttons: %d\n\tNum Hats: %d\n", SDL_JoystickNumAxes(joy), SDL_JoystickNumButtons(joy), SDL_JoystickNumHats(joy)); 309 printf("\tNum Axes: %d\n\tNum Buttons: %d\n\tNum Hats: %d\n", SDL_JoystickNumAxes(joy), SDL_JoystickNumButtons(joy), SDL_JoystickNumHats(joy));
348 } 310 }
349 } 311 }
350 SDL_JoystickEventState(SDL_ENABLE); 312 SDL_JoystickEventState(SDL_ENABLE);
351 313
352 atexit(SDL_Quit); 314 atexit(SDL_Quit);
353 atexit(render_close_audio); 315 atexit(render_close_audio);
354 } 316 }
355 #ifndef DISABLE_OPENGL 317
356 void render_context_gl(vdp_context * context) 318 void render_context(vdp_context * context)
357 { 319 {
320 last_frame = SDL_GetTicks();
321
358 glBindTexture(GL_TEXTURE_2D, textures[context->framebuf == context->oddbuf ? 0 : 1]); 322 glBindTexture(GL_TEXTURE_2D, textures[context->framebuf == context->oddbuf ? 0 : 1]);
359 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 320, 240, GL_BGRA, GL_UNSIGNED_BYTE, context->framebuf);; 323 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 320, 240, GL_BGRA, GL_UNSIGNED_BYTE, context->framebuf);;
360 324
361 glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 325 glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
362 glClear(GL_COLOR_BUFFER_BIT); 326 glClear(GL_COLOR_BUFFER_BIT);
379 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]); 343 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]);
380 glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, (void *)0); 344 glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, (void *)0);
381 345
382 glDisableVertexAttribArray(at_pos); 346 glDisableVertexAttribArray(at_pos);
383 347
384 SDL_GL_SwapBuffers(); 348 SDL_GL_SwapWindow(main_window);
385 if (context->regs[REG_MODE_4] & BIT_INTERLACE)
386 {
387 context->framebuf = context->framebuf == context->oddbuf ? context->evenbuf : context->oddbuf;
388 }
389 }
390 #endif
391
392 uint32_t blankbuf[320*240];
393
394 void render_context(vdp_context * context)
395 {
396 uint16_t *buf_16;
397 uint32_t *buf_32;
398 uint8_t b,g,r;
399 last_frame = SDL_GetTicks();
400 #ifndef DISABLE_OPENGL
401 if (render_gl)
402 {
403 render_context_gl(context);
404 return;
405 }
406 #endif
407 if (SDL_MUSTLOCK(screen)) {
408 if (SDL_LockSurface(screen) < 0) {
409 return;
410 }
411 }
412 uint16_t repeat_x = screen->clip_rect.w / 320;
413 uint16_t repeat_y = screen->clip_rect.h / 240;
414 if (repeat_x > repeat_y) {
415 repeat_x = repeat_y;
416 } else {
417 repeat_y = repeat_x;
418 }
419 int othermask = repeat_y >> 1;
420
421 if (screen->format->BytesPerPixel == 2) {
422 uint16_t *otherbuf = (context->regs[REG_MODE_4] & BIT_INTERLACE) ? context->evenbuf : (uint16_t *)blankbuf;
423 uint16_t * oddbuf = context->oddbuf;
424 buf_16 = (uint16_t *)screen->pixels;
425 for (int y = 0; y < 240; y++) {
426 for (int i = 0; i < repeat_y; i++,buf_16 += screen->pitch/2) {
427 uint16_t *line = buf_16;
428 uint16_t *src_line = (i & othermask ? otherbuf : oddbuf) + y * 320;
429 for (int x = 0; x < 320; x++) {
430 uint16_t color = *(src_line++);
431 for (int j = 0; j < repeat_x; j++) {
432 *(line++) = color;
433 }
434 }
435 }
436 }
437 } else {
438 uint32_t *otherbuf = (context->regs[REG_MODE_4] & BIT_INTERLACE) ? context->evenbuf : (uint32_t *)blankbuf;
439 uint32_t * oddbuf = context->oddbuf;
440 buf_32 = (uint32_t *)screen->pixels;
441 for (int y = 0; y < 240; y++) {
442 for (int i = 0; i < repeat_y; i++,buf_32 += screen->pitch/4) {
443 uint32_t *line = buf_32;
444 uint32_t *src_line = (i & othermask ? otherbuf : oddbuf) + y * 320;
445 for (int x = 0; x < 320; x++) {
446 uint32_t color = *(src_line++);
447 for (int j = 0; j < repeat_x; j++) {
448 *(line++) = color;
449 }
450 }
451 }
452 }
453 }
454 if ( SDL_MUSTLOCK(screen) ) {
455 SDL_UnlockSurface(screen);
456 }
457 //SDL_UpdateRect(screen, 0, 0, screen->clip_rect.w, screen->clip_rect.h);
458 SDL_Flip(screen);
459 if (context->regs[REG_MODE_4] & BIT_INTERLACE) 349 if (context->regs[REG_MODE_4] & BIT_INTERLACE)
460 { 350 {
461 context->framebuf = context->framebuf == context->oddbuf ? context->evenbuf : context->oddbuf; 351 context->framebuf = context->framebuf == context->oddbuf ? context->evenbuf : context->oddbuf;
462 } 352 }
463 } 353 }
566 } 456 }
567 } 457 }
568 } 458 }
569 render_context(context); 459 render_context(context);
570 460
571
572 //TODO: Figure out why this causes segfaults
573 frame_counter++; 461 frame_counter++;
574 if ((last_frame - start) > 1000) { 462 if ((last_frame - start) > 1000) {
575 if (start && (last_frame-start)) { 463 if (start && (last_frame-start)) {
576 if (!fps_caption) { 464 if (!fps_caption) {
577 fps_caption = malloc(strlen(caption) + strlen(" - 1000.1 fps") + 1); 465 fps_caption = malloc(strlen(caption) + strlen(" - 1000.1 fps") + 1);
578 } 466 }
579 sprintf(fps_caption, "%s - %.1f fps", caption, ((float)frame_counter) / (((float)(last_frame-start)) / 1000.0)); 467 sprintf(fps_caption, "%s - %.1f fps", caption, ((float)frame_counter) / (((float)(last_frame-start)) / 1000.0));
580 SDL_WM_SetCaption(fps_caption, caption); 468 SDL_SetWindowTitle(main_window, fps_caption);
581 fflush(stdout);
582 } 469 }
583 start = last_frame; 470 start = last_frame;
584 frame_counter = 0; 471 frame_counter = 0;
585 } 472 }
586 return ret; 473 return ret;