Mercurial > repos > blastem
comparison render_sdl.c @ 1077:1a66d5165ea7
Cleanup the separation of render backend and VDP code in preparation for having extra debug windows. Make determination of H40/H32 based on number of lines in each mode.
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Mon, 22 Aug 2016 09:46:18 -0700 |
parents | 3a0f684891ae |
children | c15896605bf2 |
comparison
equal
deleted
inserted
replaced
1076:fa6fe03f218a | 1077:1a66d5165ea7 |
---|---|
14 | 14 |
15 #ifndef DISABLE_OPENGL | 15 #ifndef DISABLE_OPENGL |
16 #include <GL/glew.h> | 16 #include <GL/glew.h> |
17 #endif | 17 #endif |
18 | 18 |
19 #define MAX_EVENT_POLL_PER_FRAME 2 | |
20 | |
19 SDL_Window *main_window; | 21 SDL_Window *main_window; |
20 SDL_Renderer *main_renderer; | 22 SDL_Renderer *main_renderer; |
21 SDL_Texture *main_texture; | 23 SDL_Texture **sdl_textures; |
24 uint8_t num_textures; | |
22 SDL_Rect main_clip; | 25 SDL_Rect main_clip; |
23 SDL_GLContext *main_context; | 26 SDL_GLContext *main_context; |
24 | 27 |
25 int main_width, main_height, is_fullscreen; | 28 int main_width, main_height, is_fullscreen; |
26 | 29 |
166 } | 169 } |
167 return ret; | 170 return ret; |
168 } | 171 } |
169 #endif | 172 #endif |
170 | 173 |
171 void render_alloc_surfaces(vdp_context * context) | 174 uint32_t texture_buf[512 * 256]; |
175 void render_alloc_surfaces() | |
172 { | 176 { |
173 static uint8_t texture_init; | 177 static uint8_t texture_init; |
174 context->oddbuf = context->framebuf = malloc(512 * 256 * 4 * 2); | |
175 memset(context->oddbuf, 0, 512 * 256 * 4 * 2); | |
176 context->evenbuf = ((char *)context->oddbuf) + 512 * 256 * 4; | |
177 | 178 |
178 if (texture_init) { | 179 if (texture_init) { |
179 return; | 180 return; |
180 } | 181 } |
182 sdl_textures= malloc(sizeof(SDL_Texture *) * 2); | |
183 num_textures = 2; | |
181 texture_init = 1; | 184 texture_init = 1; |
182 #ifndef DISABLE_OPENGL | 185 #ifndef DISABLE_OPENGL |
183 if (render_gl) { | 186 if (render_gl) { |
187 sdl_textures[0] = sdl_textures[1] = NULL; | |
184 glGenTextures(3, textures); | 188 glGenTextures(3, textures); |
185 for (int i = 0; i < 3; i++) | 189 for (int i = 0; i < 3; i++) |
186 { | 190 { |
187 glBindTexture(GL_TEXTURE_2D, textures[i]); | 191 glBindTexture(GL_TEXTURE_2D, textures[i]); |
188 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | 192 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
189 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | 193 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
190 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | 194 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
191 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | 195 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
192 if (i < 2) { | 196 if (i < 2) { |
193 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 512, 256, 0, GL_BGRA, GL_UNSIGNED_BYTE, i ? context->evenbuf : context->oddbuf); | 197 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 512, 256, 0, GL_BGRA, GL_UNSIGNED_BYTE, texture_buf); |
194 } else { | 198 } else { |
195 uint32_t blank = 255 << 24; | 199 uint32_t blank = 255 << 24; |
196 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_BGRA, GL_UNSIGNED_BYTE, &blank); | 200 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_BGRA, GL_UNSIGNED_BYTE, &blank); |
197 } | 201 } |
198 } | 202 } |
219 un_textures[1] = glGetUniformLocation(program, "textures[1]"); | 223 un_textures[1] = glGetUniformLocation(program, "textures[1]"); |
220 un_width = glGetUniformLocation(program, "width"); | 224 un_width = glGetUniformLocation(program, "width"); |
221 at_pos = glGetAttribLocation(program, "pos"); | 225 at_pos = glGetAttribLocation(program, "pos"); |
222 } else { | 226 } else { |
223 #endif | 227 #endif |
224 /* height=480 to fit interlaced output */ | 228 |
225 main_texture = SDL_CreateTexture(main_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 320, 480); | 229 //height=480 to fit interlaced output |
226 #ifndef DISABLE_OPENGL | 230 sdl_textures[0] = sdl_textures[1] = SDL_CreateTexture(main_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 320, 480); |
227 } | 231 #ifndef DISABLE_OPENGL |
228 #endif | 232 } |
229 } | 233 #endif |
230 | |
231 void render_free_surfaces(vdp_context *context) | |
232 { | |
233 free(context->framebuf); | |
234 } | 234 } |
235 | 235 |
236 char * caption = NULL; | 236 char * caption = NULL; |
237 char * fps_caption = NULL; | 237 char * fps_caption = NULL; |
238 | 238 |
239 static void render_quit() | 239 static void render_quit() |
240 { | 240 { |
241 render_close_audio(); | 241 render_close_audio(); |
242 #ifdef DISABLE_OPENGL | 242 for (int i = 0; i < num_textures; i++) |
243 SDL_DestroyTexture(main_texture); | 243 { |
244 #endif | 244 if (sdl_textures[i]) { |
245 SDL_DestroyTexture(sdl_textures[i]); | |
246 } | |
247 } | |
245 } | 248 } |
246 | 249 |
247 void render_init(int width, int height, char * title, uint32_t fps, uint8_t fullscreen) | 250 void render_init(int width, int height, char * title, uint32_t fps, uint8_t fullscreen) |
248 { | 251 { |
249 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK) < 0) { | 252 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK) < 0) { |
354 main_clip.y = (height - main_clip.h) / 2; | 357 main_clip.y = (height - main_clip.h) / 2; |
355 #ifndef DISABLE_OPENGL | 358 #ifndef DISABLE_OPENGL |
356 } | 359 } |
357 #endif | 360 #endif |
358 } | 361 } |
362 render_alloc_surfaces(); | |
359 def.ptrval = "off"; | 363 def.ptrval = "off"; |
360 scanlines = !strcmp(tern_find_path_default(config, "video\0scanlines\0", def).ptrval, "on"); | 364 scanlines = !strcmp(tern_find_path_default(config, "video\0scanlines\0", def).ptrval, "on"); |
361 | 365 |
362 caption = title; | 366 caption = title; |
363 min_delay = 0; | 367 min_delay = 0; |
417 caption = title; | 421 caption = title; |
418 free(fps_caption); | 422 free(fps_caption); |
419 fps_caption = NULL; | 423 fps_caption = NULL; |
420 } | 424 } |
421 | 425 |
422 void render_context(vdp_context * context) | 426 uint32_t *locked_pixels; |
423 { | 427 uint32_t locked_pitch; |
424 int width = context->regs[REG_MODE_4] & BIT_H40 ? 320.0f : 256.0f; | 428 uint32_t *render_get_framebuffer(uint8_t which, int *pitch) |
425 int height = 240; | 429 { |
426 | 430 #ifndef DISABLE_OPENGL |
427 last_frame = SDL_GetTicks(); | 431 if (render_gl && which <= FRAMEBUFFER_EVEN) { |
428 #ifndef DISABLE_OPENGL | 432 *pitch = 320 * sizeof(uint32_t); //TODO: change this to LINEBUF_SIZE once border rendering is added |
429 if (render_gl) { | 433 return texture_buf; |
430 glBindTexture(GL_TEXTURE_2D, textures[context->framebuf == context->oddbuf ? 0 : 1]); | 434 } else { |
431 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 320, 240, GL_BGRA, GL_UNSIGNED_BYTE, context->framebuf);; | 435 #endif |
436 if (which >= num_textures) { | |
437 warning("Request for invalid framebuffer number %d\n", which); | |
438 return NULL; | |
439 } | |
440 void *pixels; | |
441 if (SDL_LockTexture(sdl_textures[which], NULL, &pixels, pitch) < 0) { | |
442 warning("Failed to lock texture: %s\n", SDL_GetError()); | |
443 return NULL; | |
444 } | |
445 static uint8_t last; | |
446 if (which <= FRAMEBUFFER_EVEN) { | |
447 locked_pixels = pixels; | |
448 if (which == FRAMEBUFFER_EVEN) { | |
449 pixels += *pitch; | |
450 } | |
451 locked_pitch = *pitch; | |
452 if (which != last) { | |
453 *pitch *= 2; | |
454 } | |
455 last = which; | |
456 } | |
457 return pixels; | |
458 #ifndef DISABLE_OPENGL | |
459 } | |
460 #endif | |
461 } | |
462 | |
463 uint8_t events_processed; | |
464 #ifdef __ANDROID__ | |
465 #define FPS_INTERVAL 10000 | |
466 #else | |
467 #define FPS_INTERVAL 1000 | |
468 #endif | |
469 | |
470 void render_framebuffer_updated(uint8_t which, int width) | |
471 { | |
472 static uint8_t last; | |
473 #ifndef DISABLE_OPENGL | |
474 if (render_gl && which <= FRAMEBUFFER_EVEN) { | |
475 glBindTexture(GL_TEXTURE_2D, textures[which]); | |
476 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 320, 240, GL_BGRA, GL_UNSIGNED_BYTE, texture_buf); | |
432 | 477 |
433 glClearColor(0.0f, 0.0f, 0.0f, 1.0f); | 478 glClearColor(0.0f, 0.0f, 0.0f, 1.0f); |
434 glClear(GL_COLOR_BUFFER_BIT); | 479 glClear(GL_COLOR_BUFFER_BIT); |
435 | 480 |
436 glUseProgram(program); | 481 glUseProgram(program); |
437 glActiveTexture(GL_TEXTURE0); | 482 glActiveTexture(GL_TEXTURE0); |
438 glBindTexture(GL_TEXTURE_2D, textures[0]); | 483 glBindTexture(GL_TEXTURE_2D, textures[0]); |
439 glUniform1i(un_textures[0], 0); | 484 glUniform1i(un_textures[0], 0); |
440 | 485 |
441 glActiveTexture(GL_TEXTURE1); | 486 glActiveTexture(GL_TEXTURE1); |
442 glBindTexture(GL_TEXTURE_2D, textures[(context->regs[REG_MODE_4] & BIT_INTERLACE) ? 1 : scanlines ? 2 : 0]); | 487 glBindTexture(GL_TEXTURE_2D, textures[last != which ? 1 : scanlines ? 2 : 0]); |
443 glUniform1i(un_textures[1], 1); | 488 glUniform1i(un_textures[1], 1); |
444 | 489 |
445 glUniform1f(un_width, width); | 490 glUniform1f(un_width, width); |
446 | 491 |
447 glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); | 492 glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); |
454 glDisableVertexAttribArray(at_pos); | 499 glDisableVertexAttribArray(at_pos); |
455 | 500 |
456 SDL_GL_SwapWindow(main_window); | 501 SDL_GL_SwapWindow(main_window); |
457 } else { | 502 } else { |
458 #endif | 503 #endif |
459 SDL_Rect area; | 504 uint32_t height = 240; |
460 | 505 if (which <= FRAMEBUFFER_EVEN && last != which) { |
461 area.x = area.y = 0; | 506 uint8_t *cur_dst = (uint8_t *)locked_pixels; |
462 area.w = width; | 507 uint8_t *cur_saved = (uint8_t *)texture_buf; |
463 area.h = height; | 508 uint32_t dst_off = which == FRAMEBUFFER_EVEN ? 0 : locked_pitch; |
464 | 509 uint32_t src_off = which == FRAMEBUFFER_EVEN ? locked_pitch : 0; |
465 if (context->regs[REG_MODE_4] & BIT_INTERLACE) { | 510 for (int i = 0; i < 240; ++i) |
466 unsigned skip; | 511 { |
467 uint32_t *src = (uint32_t*)context->framebuf; | 512 //copy saved line from other field |
468 uint8_t *dst; | 513 memcpy(cur_dst + dst_off, cur_saved, locked_pitch); |
469 int i; | 514 //save line from this field to buffer for next frame |
470 | 515 memcpy(cur_saved, cur_dst + src_off, locked_pitch); |
471 area.h *= 2; | 516 cur_dst += locked_pitch * 2; |
472 | 517 cur_saved += locked_pitch; |
473 SDL_LockTexture(main_texture, &area, (void**)&dst, &skip); | 518 } |
474 | 519 height = 480; |
475 if (context->framebuf == context->evenbuf) | 520 } |
476 dst += skip; | 521 SDL_UnlockTexture(sdl_textures[which]); |
477 | 522 SDL_Rect src_clip = { |
478 skip *= 2; | 523 .x = 0, |
479 | 524 .y = 0, |
480 for (i = 0; i < 240; ++i) { | 525 .w = width, |
481 memcpy(dst, src, width*sizeof(uint32_t)); | 526 .h = height |
482 src += 320; | 527 }; |
483 dst += skip; | 528 SDL_RenderCopy(main_renderer, sdl_textures[which], &src_clip, &main_clip); |
484 } | |
485 | |
486 SDL_UnlockTexture(main_texture); | |
487 } | |
488 else /* possibly faster path for non-interlaced output */ | |
489 SDL_UpdateTexture(main_texture, &area, context->framebuf, 320*sizeof(uint32_t)); | |
490 | |
491 SDL_RenderClear(main_renderer); | |
492 SDL_RenderCopy(main_renderer, main_texture, &area, &main_clip); | |
493 SDL_RenderPresent(main_renderer); | 529 SDL_RenderPresent(main_renderer); |
494 #ifndef DISABLE_OPENGL | 530 #ifndef DISABLE_OPENGL |
495 } | 531 } |
496 #endif | 532 #endif |
497 | 533 if (which <= FRAMEBUFFER_EVEN) { |
498 if (context->regs[REG_MODE_4] & BIT_INTERLACE) { | 534 last = which; |
499 context->framebuf = context->framebuf == context->oddbuf ? context->evenbuf : context->oddbuf; | 535 static uint32_t frame_counter, start; |
500 } | 536 frame_counter++; |
537 last_frame= SDL_GetTicks(); | |
538 if ((last_frame - start) > FPS_INTERVAL) { | |
539 if (start && (last_frame-start)) { | |
540 #ifdef __ANDROID__ | |
541 info_message("%s - %.1f fps", caption, ((float)frame_counter) / (((float)(last_frame-start)) / 1000.0)); | |
542 #else | |
543 if (!fps_caption) { | |
544 fps_caption = malloc(strlen(caption) + strlen(" - 100000000.1 fps") + 1); | |
545 } | |
546 sprintf(fps_caption, "%s - %.1f fps", caption, ((float)frame_counter) / (((float)(last_frame-start)) / 1000.0)); | |
547 SDL_SetWindowTitle(main_window, fps_caption); | |
548 #endif | |
549 } | |
550 start = last_frame; | |
551 frame_counter = 0; | |
552 } | |
553 } | |
554 if (!events_processed) { | |
555 process_events(); | |
556 } | |
557 events_processed = 0; | |
501 } | 558 } |
502 | 559 |
503 void render_wait_quit(vdp_context * context) | 560 void render_wait_quit(vdp_context * context) |
504 { | 561 { |
505 SDL_Event event; | 562 SDL_Event event; |
506 while(SDL_WaitEvent(&event)) { | 563 while(SDL_WaitEvent(&event)) { |
507 switch (event.type) { | 564 switch (event.type) { |
508 case SDL_KEYDOWN: | |
509 if (event.key.keysym.sym == SDLK_LEFTBRACKET) { | |
510 render_dbg++; | |
511 if (render_dbg == 4) { | |
512 render_dbg = 0; | |
513 } | |
514 render_context(context); | |
515 } else if(event.key.keysym.sym == SDLK_RIGHTBRACKET) { | |
516 debug_pal++; | |
517 if (debug_pal == 4) { | |
518 debug_pal = 0; | |
519 } | |
520 } | |
521 break; | |
522 case SDL_QUIT: | 565 case SDL_QUIT: |
523 return; | 566 return; |
524 } | 567 } |
525 } | 568 } |
526 } | 569 } |
718 exit(0); | 761 exit(0); |
719 } | 762 } |
720 return 0; | 763 return 0; |
721 } | 764 } |
722 | 765 |
723 uint32_t frame_counter = 0; | |
724 uint32_t start = 0; | |
725 #ifdef __ANDROID__ | |
726 #define FPS_INTERVAL 10000 | |
727 #else | |
728 #define FPS_INTERVAL 1000 | |
729 #endif | |
730 int wait_render_frame(vdp_context * context, int frame_limit) | |
731 { | |
732 SDL_Event event; | |
733 int ret = 0; | |
734 while(SDL_PollEvent(&event)) { | |
735 ret = handle_event(&event); | |
736 } | |
737 if (frame_limit) { | |
738 //TODO: Adjust frame delay so we actually get 60 FPS rather than 62.5 FPS | |
739 uint32_t current = SDL_GetTicks(); | |
740 uint32_t desired = last_frame + frame_delay; | |
741 if (current < desired) { | |
742 uint32_t delay = last_frame + frame_delay - current; | |
743 if (delay > min_delay) { | |
744 SDL_Delay((delay/min_delay)*min_delay); | |
745 } | |
746 while ((desired) >= SDL_GetTicks()) { | |
747 } | |
748 } | |
749 } | |
750 render_context(context); | |
751 | |
752 frame_counter++; | |
753 if ((last_frame - start) > FPS_INTERVAL) { | |
754 if (start && (last_frame-start)) { | |
755 #ifdef __ANDROID__ | |
756 info_message("%s - %.1f fps", caption, ((float)frame_counter) / (((float)(last_frame-start)) / 1000.0)); | |
757 #else | |
758 if (!fps_caption) { | |
759 fps_caption = malloc(strlen(caption) + strlen(" - 100000000.1 fps") + 1); | |
760 } | |
761 sprintf(fps_caption, "%s - %.1f fps", caption, ((float)frame_counter) / (((float)(last_frame-start)) / 1000.0)); | |
762 SDL_SetWindowTitle(main_window, fps_caption); | |
763 #endif | |
764 } | |
765 start = last_frame; | |
766 frame_counter = 0; | |
767 } | |
768 return ret; | |
769 } | |
770 | |
771 void process_events() | 766 void process_events() |
772 { | 767 { |
768 if (events_processed > MAX_EVENT_POLL_PER_FRAME) { | |
769 return; | |
770 } | |
773 SDL_Event event; | 771 SDL_Event event; |
774 while(SDL_PollEvent(&event)) { | 772 while(SDL_PollEvent(&event)) { |
775 handle_event(&event); | 773 handle_event(&event); |
776 } | 774 } |
775 events_processed++; | |
777 } | 776 } |
778 | 777 |
779 void render_wait_psg(psg_context * context) | 778 void render_wait_psg(psg_context * context) |
780 { | 779 { |
781 SDL_LockMutex(audio_mutex); | 780 SDL_LockMutex(audio_mutex); |