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);