1 #include "rsx/rsx_intf.h"
2 
3 #include <math.h>
4 #include <stdint.h>
5 #include <stdlib.h>
6 #include <string.h>
7 
8 #include "boolean.h"
9 #include "libretro.h"
10 
11 #include "beetle_psx_globals.h"
12 #include "libretro_cbs.h"
13 #include "libretro_options.h"
14 #include "mednafen/mednafen.h"
15 #include "mednafen/psx/gpu.h"
16 #include "mednafen/settings.h"
17 
18 #ifdef RSX_DUMP
19 #include "rsx_dump.h"
20 #endif
21 
22 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES)
23 #include "rsx_lib_gl.h"
24 #endif
25 
26 #if defined(HAVE_VULKAN)
27 #include "rsx_lib_vulkan.h"
28 #endif
29 
30 extern bool fast_pal;
31 
32 static enum rsx_renderer_type rsx_type          = RSX_SOFTWARE;
33 
34 static bool gl_initialized                      = false;
35 static bool vk_initialized                      = false;
36 
37 // GPU reset defaults
38 static int rsx_width_mode = WIDTH_MODE_256;
39 static int rsx_height_mode = HEIGHT_MODE_240;
40 
rsx_soft_open(bool is_pal)41 static bool rsx_soft_open(bool is_pal)
42 {
43    content_is_pal = is_pal;
44    return true;
45 }
46 
rsx_intf_set_environment(retro_environment_t cb)47 void rsx_intf_set_environment(retro_environment_t cb)
48 {
49    switch (rsx_type)
50    {
51       case RSX_SOFTWARE:
52          break;
53       case RSX_OPENGL:
54          break;
55       case RSX_VULKAN:
56 #if defined(HAVE_VULKAN)
57          rsx_vulkan_set_environment(cb);
58 #endif
59          break;
60    }
61 }
62 
rsx_intf_set_video_refresh(retro_video_refresh_t cb)63 void rsx_intf_set_video_refresh(retro_video_refresh_t cb)
64 {
65    switch (rsx_type)
66    {
67       case RSX_SOFTWARE:
68          break;
69       case RSX_OPENGL:
70          break;
71       case RSX_VULKAN:
72 #if defined(HAVE_VULKAN)
73          rsx_vulkan_set_video_refresh(cb);
74 #endif
75          break;
76    }
77 }
78 
rsx_intf_get_system_av_info(struct retro_system_av_info * info)79 void rsx_intf_get_system_av_info(struct retro_system_av_info *info)
80 {
81    switch (rsx_type)
82    {
83       case RSX_SOFTWARE:
84          memset(info, 0, sizeof(*info));
85          info->timing.fps            = rsx_common_get_timing_fps();
86          info->timing.sample_rate    = SOUND_FREQUENCY;
87          info->geometry.base_width   = MEDNAFEN_CORE_GEOMETRY_BASE_W;
88          info->geometry.base_height  = MEDNAFEN_CORE_GEOMETRY_BASE_H;
89          info->geometry.max_width    = MEDNAFEN_CORE_GEOMETRY_MAX_W  << psx_gpu_upscale_shift;
90          info->geometry.max_height   = MEDNAFEN_CORE_GEOMETRY_MAX_H  << psx_gpu_upscale_shift;
91          info->geometry.aspect_ratio = rsx_common_get_aspect_ratio(content_is_pal, crop_overscan,
92                                           MDFN_GetSettingI(content_is_pal ? "psx.slstartp" : "psx.slstart"),
93                                           MDFN_GetSettingI(content_is_pal ? "psx.slendp" : "psx.slend"),
94                                           aspect_ratio_setting, false, widescreen_hack, widescreen_hack_aspect_ratio_setting);
95          break;
96       case RSX_OPENGL:
97 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES)
98          rsx_gl_get_system_av_info(info);
99 #endif
100          break;
101       case RSX_VULKAN:
102 #if defined(HAVE_VULKAN)
103          rsx_vulkan_get_system_av_info(info);
104 #endif
105          break;
106    }
107 }
108 
rsx_intf_is_type(void)109 enum rsx_renderer_type rsx_intf_is_type(void)
110 {
111    return rsx_type;
112 }
113 
rsx_intf_dump_init(void)114 inline void rsx_intf_dump_init(void)
115 {
116 #if defined(RSX_DUMP)
117    {
118       const char *env = getenv("RSX_DUMP");
119       if (env)
120          rsx_dump_init(env);
121    }
122 #endif
123 }
124 
rsx_intf_open(bool is_pal,bool force_software)125 bool rsx_intf_open(bool is_pal, bool force_software)
126 {
127    struct retro_variable var = {0};
128    bool software_selected    = false;
129    vk_initialized            = false;
130    gl_initialized            = false;
131 
132    enum force_renderer_type force_type = AUTO;
133 
134    var.key                   = BEETLE_OPT(renderer);
135 
136    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
137    {
138       if (!strcmp(var.value, "software") || force_software)
139          software_selected = true;
140       else if (!strcmp(var.value, "hardware_gl"))
141          force_type = FORCE_OPENGL;
142       else if (!strcmp(var.value, "hardware_vk"))
143          force_type = FORCE_VULKAN;
144    }
145    else
146    {
147       /* If 'BEETLE_OPT(renderer)' option is not found, then
148        * we are running in software mode */
149       software_selected = true;
150    }
151 
152    if (!software_selected)
153    {
154       /* Check for any hardware renderer forces before performing auto sequence */
155       if (force_type == FORCE_VULKAN)
156       {
157 #if defined (HAVE_VULKAN)
158          if (rsx_vulkan_open(is_pal))
159          {
160             rsx_type = RSX_VULKAN;
161             vk_initialized = true;
162             rsx_intf_dump_init();
163             return true;
164          }
165          else
166          {
167             MDFND_DispMessage(3, RETRO_LOG_ERROR,
168                   RETRO_MESSAGE_TARGET_ALL, RETRO_MESSAGE_TYPE_NOTIFICATION,
169                   "Could not force Vulkan renderer. Falling back to software renderer.");
170 
171             goto soft;
172          }
173 #else
174          MDFND_DispMessage(3, RETRO_LOG_ERROR,
175                RETRO_MESSAGE_TARGET_ALL, RETRO_MESSAGE_TYPE_NOTIFICATION,
176                "Attempted to force Vulkan renderer, but core was built without it. Falling back to software renderer.");
177 
178          goto soft;
179 #endif
180       }
181       else if (force_type == FORCE_OPENGL)
182       {
183 #if defined (HAVE_OPENGL) || defined(HAVE_OPENGLES)
184          if (rsx_gl_open(is_pal))
185          {
186             rsx_type = RSX_OPENGL;
187             gl_initialized = true;
188             rsx_intf_dump_init();
189             return true;
190          }
191          else
192          {
193             MDFND_DispMessage(3, RETRO_LOG_ERROR,
194                   RETRO_MESSAGE_TARGET_ALL, RETRO_MESSAGE_TYPE_NOTIFICATION,
195                   "Could not force OpenGL renderer. Falling back to software renderer.");
196 
197             goto soft;
198          }
199 #else
200          MDFND_DispMessage(3, RETRO_LOG_ERROR,
201                RETRO_MESSAGE_TARGET_ALL, RETRO_MESSAGE_TYPE_NOTIFICATION,
202                "Attempted to force OpenGL renderer, but core was built without it. Falling back to software renderer.");
203 
204          goto soft;
205 #endif
206       }
207       /* End forces section */
208 
209       unsigned preferred = 0; // This will be set to RETRO_HW_CONTEXT_DUMMY if GET_PREFERRED_HW_RENDER is not supported by frontend
210       if (!environ_cb(RETRO_ENVIRONMENT_GET_PREFERRED_HW_RENDER, &preferred))
211       {
212          preferred = RETRO_HW_CONTEXT_DUMMY;
213       }
214       /* If GET_PREFERRED_HW_RENDER is not supported by frontend, then we just go
215        * down the list attempting to open a hardware renderer until we get one */
216 
217 #if defined(HAVE_VULKAN)
218       if ((preferred == RETRO_HW_CONTEXT_DUMMY ||
219            preferred == RETRO_HW_CONTEXT_VULKAN)
220           && rsx_vulkan_open(is_pal))
221       {
222          rsx_type       = RSX_VULKAN;
223          vk_initialized = true;
224          rsx_intf_dump_init();
225          return true;
226       }
227 #endif
228 
229 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES)
230       if ((preferred == RETRO_HW_CONTEXT_DUMMY ||
231            preferred == RETRO_HW_CONTEXT_OPENGL ||
232            preferred == RETRO_HW_CONTEXT_OPENGL_CORE)
233           && rsx_gl_open(is_pal))
234       {
235          rsx_type       = RSX_OPENGL;
236          gl_initialized = true;
237          rsx_intf_dump_init();
238          return true;
239       }
240 #endif
241 
242       if (preferred == RETRO_HW_CONTEXT_DUMMY)
243          MDFND_DispMessage(3, RETRO_LOG_ERROR,
244                RETRO_MESSAGE_TARGET_ALL, RETRO_MESSAGE_TYPE_NOTIFICATION,
245                "No hardware renderers could be opened. Falling back to software renderer.");
246       else
247          MDFND_DispMessage(3, RETRO_LOG_ERROR,
248                RETRO_MESSAGE_TARGET_ALL, RETRO_MESSAGE_TYPE_NOTIFICATION,
249                "Unable to find or open hardware renderer for frontend preferred hardware context. Falling back to software renderer.");
250    }
251 
252 soft:
253    // rsx_soft_open(is_pal) always returns true
254    if (rsx_soft_open(is_pal))
255    {
256       rsx_type = RSX_SOFTWARE;
257       rsx_intf_dump_init();
258       return true;
259    }
260 
261    /* Note: fallback will result in the software renderer running at
262     * 1x internal resolution instead of the user configured setting
263     * because of the check_variables startup sequence in libretro.cpp;
264     * this is a good thing since emulation would likely slow to a crawl
265     * if the user had >2x IR for hardware rendering and that setting was
266     * retroactively applied that to software rendering fallback
267     */
268 
269    return false;
270 }
271 
rsx_intf_close(void)272 void rsx_intf_close(void)
273 {
274 #if defined(RSX_DUMP)
275    rsx_dump_deinit();
276 #endif
277 
278 #if defined(HAVE_VULKAN)
279    if (rsx_type != RSX_SOFTWARE && vk_initialized)
280       return;
281 #endif
282 
283 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES)
284    if (rsx_type != RSX_SOFTWARE && gl_initialized)
285    {
286       rsx_gl_close();
287       return;
288    }
289 #endif
290 }
291 
rsx_intf_refresh_variables(void)292 void rsx_intf_refresh_variables(void)
293 {
294    switch (rsx_type)
295    {
296       case RSX_SOFTWARE:
297          break;
298       case RSX_OPENGL:
299 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES)
300          rsx_gl_refresh_variables();
301 #endif
302          break;
303       case RSX_VULKAN:
304 #if defined(HAVE_VULKAN)
305          rsx_vulkan_refresh_variables();
306 #endif
307          break;
308    }
309 }
310 
rsx_intf_prepare_frame(void)311 void rsx_intf_prepare_frame(void)
312 {
313 #ifdef RSX_DUMP
314    rsx_dump_prepare_frame();
315 #endif
316 
317    switch (rsx_type)
318    {
319       case RSX_SOFTWARE:
320          break;
321       case RSX_OPENGL:
322 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES)
323          rsx_gl_prepare_frame();
324 #endif
325          break;
326       case RSX_VULKAN:
327 #if defined(HAVE_VULKAN)
328          rsx_vulkan_prepare_frame();
329 #endif
330          break;
331    }
332 }
333 
rsx_intf_finalize_frame(const void * fb,unsigned width,unsigned height,unsigned pitch)334 void rsx_intf_finalize_frame(const void *fb, unsigned width,
335                              unsigned height, unsigned pitch)
336 {
337 #ifdef RSX_DUMP
338    rsx_dump_finalize_frame();
339 #endif
340 
341    switch (rsx_type)
342    {
343       case RSX_SOFTWARE:
344          video_cb(fb, width, height, pitch);
345          break;
346       case RSX_OPENGL:
347 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES)
348          rsx_gl_finalize_frame(fb, width, height, pitch);
349 #endif
350          break;
351       case RSX_VULKAN:
352 #if defined(HAVE_VULKAN)
353          rsx_vulkan_finalize_frame(fb, width, height, pitch);
354 #endif
355          break;
356    }
357 }
358 
rsx_intf_set_tex_window(uint8_t tww,uint8_t twh,uint8_t twx,uint8_t twy)359 void rsx_intf_set_tex_window(uint8_t tww, uint8_t twh,
360                              uint8_t twx, uint8_t twy)
361 {
362 #ifdef RSX_DUMP
363    rsx_dump_set_tex_window(tww, twh, twx, twy);
364 #endif
365 
366    switch (rsx_type)
367    {
368       case RSX_OPENGL:
369 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES)
370          rsx_gl_set_tex_window(tww, twh, twx, twy);
371 #endif
372          break;
373       case RSX_VULKAN:
374 #if defined(HAVE_VULKAN)
375          rsx_vulkan_set_tex_window(tww, twh, twx, twy);
376 #endif
377          break;
378       default:
379          break;
380    }
381 }
382 
rsx_intf_set_mask_setting(uint32_t mask_set_or,uint32_t mask_eval_and)383 void rsx_intf_set_mask_setting(uint32_t mask_set_or, uint32_t mask_eval_and)
384 {
385    switch (rsx_type)
386    {
387       case RSX_SOFTWARE:
388          break;
389       case RSX_OPENGL:
390          break;
391       case RSX_VULKAN:
392          /* TODO/FIXME */
393          break;
394    }
395 }
396 
rsx_intf_set_draw_offset(int16_t x,int16_t y)397 void rsx_intf_set_draw_offset(int16_t x, int16_t y)
398 {
399 #ifdef RSX_DUMP
400    rsx_dump_set_draw_offset(x, y);
401 #endif
402 
403    switch (rsx_type)
404    {
405       case RSX_SOFTWARE:
406          break;
407       case RSX_OPENGL:
408 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES)
409          rsx_gl_set_draw_offset(x, y);
410 #endif
411          break;
412       case RSX_VULKAN:
413 #if defined(HAVE_VULKAN)
414          rsx_vulkan_set_draw_offset(x, y);
415 #endif
416          break;
417    }
418 }
419 
rsx_intf_set_draw_area(uint16_t x0,uint16_t y0,uint16_t x1,uint16_t y1)420 void rsx_intf_set_draw_area(uint16_t x0, uint16_t y0,
421                             uint16_t x1, uint16_t y1)
422 {
423 #ifdef RSX_DUMP
424    rsx_dump_set_draw_area(x0, y0, x1, y1);
425 #endif
426 
427    switch (rsx_type)
428    {
429       case RSX_SOFTWARE:
430          break;
431       case RSX_OPENGL:
432 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES)
433          rsx_gl_set_draw_area(x0, y0, x1, y1);
434 #endif
435          break;
436       case RSX_VULKAN:
437 #if defined(HAVE_VULKAN)
438          rsx_vulkan_set_draw_area(x0, y0, x1, y1);
439 #endif
440          break;
441    }
442 }
443 
rsx_intf_set_vram_framebuffer_coords(uint32_t xstart,uint32_t ystart)444 void rsx_intf_set_vram_framebuffer_coords(uint32_t xstart, uint32_t ystart)
445 {
446 #ifdef RSX_DUMP
447    rsx_dump_set_vram_framebuffer_coords(xstart, ystart);
448 #endif
449 
450    switch (rsx_type)
451    {
452       case RSX_SOFTWARE:
453          break;
454       case RSX_OPENGL:
455 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES)
456          rsx_gl_set_vram_framebuffer_coords(xstart, ystart);
457 #endif
458          break;
459       case RSX_VULKAN:
460 #if defined(HAVE_VULKAN)
461          rsx_vulkan_set_vram_framebuffer_coords(xstart, ystart);
462 #endif
463          break;
464    }
465 }
466 
rsx_intf_set_horizontal_display_range(uint16_t x1,uint16_t x2)467 void rsx_intf_set_horizontal_display_range(uint16_t x1, uint16_t x2)
468 {
469 #ifdef RSX_DUMP
470    rsx_dump_set_horizontal_display_range(x1, x2);
471 #endif
472 
473    switch (rsx_type)
474    {
475       case RSX_SOFTWARE:
476          break;
477       case RSX_OPENGL:
478 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES)
479          rsx_gl_set_horizontal_display_range(x1, x2);
480 #endif
481          break;
482       case RSX_VULKAN:
483 #if defined(HAVE_VULKAN)
484          rsx_vulkan_set_horizontal_display_range(x1, x2);
485 #endif
486          break;
487    }
488 }
489 
rsx_intf_set_vertical_display_range(uint16_t y1,uint16_t y2)490 void rsx_intf_set_vertical_display_range(uint16_t y1, uint16_t y2)
491 {
492 #ifdef RSX_DUMP
493    rsx_dump_set_vertical_display_range(y1, y2);
494 #endif
495 
496    switch (rsx_type)
497    {
498       case RSX_SOFTWARE:
499          break;
500       case RSX_OPENGL:
501 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES)
502          rsx_gl_set_vertical_display_range(y1, y2);
503 #endif
504          break;
505       case RSX_VULKAN:
506 #if defined(HAVE_VULKAN)
507          rsx_vulkan_set_vertical_display_range(y1, y2);
508 #endif
509          break;
510    }
511 }
512 
rsx_intf_set_display_mode(bool depth_24bpp,bool is_pal,bool is_480i,int width_mode)513 void rsx_intf_set_display_mode(bool depth_24bpp,
514                                bool is_pal,
515                                bool is_480i,
516                                int width_mode)
517 {
518 #ifdef RSX_DUMP
519    rsx_dump_set_display_mode(depth_24bpp, is_pal, is_480i, width_mode);
520 #endif
521 
522    // Is this check accurate for 240i timing? May need to be fixed later
523    if (currently_interlaced != is_480i)
524    {
525       currently_interlaced = is_480i;
526       interlace_setting_dirty = true;
527    }
528 
529    // Also verify if this is accurate for 240i
530    if ((rsx_width_mode != width_mode) || (rsx_height_mode != (int)is_480i))
531    {
532       rsx_width_mode = width_mode;
533       rsx_height_mode = (int)is_480i;
534       aspect_ratio_dirty = true;
535    }
536 
537    switch (rsx_type)
538    {
539       case RSX_SOFTWARE:
540          break;
541       case RSX_OPENGL:
542 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES)
543          rsx_gl_set_display_mode(depth_24bpp, is_pal, is_480i, width_mode);
544 #endif
545          break;
546       case RSX_VULKAN:
547 #if defined(HAVE_VULKAN)
548          rsx_vulkan_set_display_mode(depth_24bpp, is_pal, is_480i, width_mode);
549 #endif
550          break;
551    }
552 }
553 
rsx_intf_push_triangle(float p0x,float p0y,float p0w,float p1x,float p1y,float p1w,float p2x,float p2y,float p2w,uint32_t c0,uint32_t c1,uint32_t c2,uint16_t t0x,uint16_t t0y,uint16_t t1x,uint16_t t1y,uint16_t t2x,uint16_t t2y,uint16_t min_u,uint16_t min_v,uint16_t max_u,uint16_t max_v,uint16_t texpage_x,uint16_t texpage_y,uint16_t clut_x,uint16_t clut_y,uint8_t texture_blend_mode,uint8_t depth_shift,bool dither,int blend_mode,uint32_t mask_test,uint32_t set_mask)554 void rsx_intf_push_triangle(
555       float p0x, float p0y, float p0w,
556       float p1x, float p1y, float p1w,
557       float p2x, float p2y, float p2w,
558       uint32_t c0,
559       uint32_t c1,
560       uint32_t c2,
561       uint16_t t0x, uint16_t t0y,
562       uint16_t t1x, uint16_t t1y,
563       uint16_t t2x, uint16_t t2y,
564       uint16_t min_u, uint16_t min_v,
565       uint16_t max_u, uint16_t max_v,
566       uint16_t texpage_x, uint16_t texpage_y,
567       uint16_t clut_x, uint16_t clut_y,
568       uint8_t texture_blend_mode,
569       uint8_t depth_shift,
570       bool dither,
571       int blend_mode,
572       uint32_t mask_test,
573       uint32_t set_mask)
574 {
575 #ifdef RSX_DUMP
576    const rsx_dump_vertex vertices[3] = {
577       { p0x, p0y, p0w, c0, t0x, t0y },
578       { p1x, p1y, p1w, c1, t1x, t1y },
579       { p2x, p2y, p2w, c2, t2x, t2y },
580    };
581    const rsx_render_state state = {
582       texpage_x, texpage_y, clut_x, clut_y, texture_blend_mode, depth_shift, dither, blend_mode,
583       mask_test, set_mask,
584    };
585    rsx_dump_triangle(vertices, &state);
586 #endif
587 
588    switch (rsx_type)
589    {
590       case RSX_SOFTWARE:
591          break;
592       case RSX_OPENGL:
593 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES)
594          rsx_gl_push_triangle(p0x, p0y, p0w, p1x, p1y, p1w, p2x, p2y, p2w,
595                c0, c1, c2, t0x, t0y, t1x, t1y, t2x, t2y,
596                min_u, min_v, max_u, max_v,
597                texpage_x, texpage_y, clut_x, clut_y,
598                texture_blend_mode,
599                depth_shift,
600                dither,
601                blend_mode, mask_test != 0, set_mask != 0);
602 #endif
603          break;
604       case RSX_VULKAN:
605 #if defined(HAVE_VULKAN)
606          rsx_vulkan_push_triangle(p0x, p0y, p0w, p1x, p1y, p1w, p2x, p2y, p2w,
607                c0, c1, c2, t0x, t0y, t1x, t1y, t2x, t2y,
608                min_u, min_v, max_u, max_v,
609                texpage_x, texpage_y, clut_x, clut_y,
610                texture_blend_mode,
611                depth_shift,
612                dither,
613                blend_mode, mask_test != 0, set_mask != 0);
614 #endif
615          break;
616    }
617 }
618 
rsx_intf_push_quad(float p0x,float p0y,float p0w,float p1x,float p1y,float p1w,float p2x,float p2y,float p2w,float p3x,float p3y,float p3w,uint32_t c0,uint32_t c1,uint32_t c2,uint32_t c3,uint16_t t0x,uint16_t t0y,uint16_t t1x,uint16_t t1y,uint16_t t2x,uint16_t t2y,uint16_t t3x,uint16_t t3y,uint16_t min_u,uint16_t min_v,uint16_t max_u,uint16_t max_v,uint16_t texpage_x,uint16_t texpage_y,uint16_t clut_x,uint16_t clut_y,uint8_t texture_blend_mode,uint8_t depth_shift,bool dither,int blend_mode,uint32_t mask_test,uint32_t set_mask,bool is_sprite,bool may_be_2d)619 void rsx_intf_push_quad(
620    float p0x, float p0y, float p0w,
621    float p1x, float p1y, float p1w,
622    float p2x, float p2y, float p2w,
623    float p3x, float p3y, float p3w,
624    uint32_t c0, uint32_t c1, uint32_t c2, uint32_t c3,
625    uint16_t t0x, uint16_t t0y,
626    uint16_t t1x, uint16_t t1y,
627    uint16_t t2x, uint16_t t2y,
628    uint16_t t3x, uint16_t t3y,
629    uint16_t min_u, uint16_t min_v,
630    uint16_t max_u, uint16_t max_v,
631    uint16_t texpage_x, uint16_t texpage_y,
632    uint16_t clut_x, uint16_t clut_y,
633    uint8_t texture_blend_mode,
634    uint8_t depth_shift,
635    bool dither,
636    int blend_mode,
637    uint32_t mask_test,
638    uint32_t set_mask,
639    bool is_sprite,
640    bool may_be_2d)
641 {
642 #ifdef RSX_DUMP
643    const rsx_dump_vertex vertices[4] = {
644       { p0x, p0y, p0w, c0, t0x, t0y },
645       { p1x, p1y, p1w, c1, t1x, t1y },
646       { p2x, p2y, p2w, c2, t2x, t2y },
647       { p3x, p3y, p3w, c3, t3x, t3y },
648    };
649    const rsx_render_state state = {
650       texpage_x, texpage_y, clut_x, clut_y, texture_blend_mode, depth_shift, dither, blend_mode,
651       mask_test, set_mask,
652    };
653    rsx_dump_quad(vertices, &state);
654 #endif
655 
656    switch (rsx_type)
657    {
658       case RSX_SOFTWARE:
659          break;
660       case RSX_OPENGL:
661 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES)
662          rsx_gl_push_quad(p0x, p0y, p0w, p1x, p1y, p1w, p2x, p2y, p2w, p3x, p3y, p3w,
663                c0, c1, c2, c3,
664                t0x, t0y, t1x, t1y, t2x, t2y, t3x, t3y,
665                min_u, min_v, max_u, max_v,
666                texpage_x, texpage_y, clut_x, clut_y,
667                texture_blend_mode,
668                depth_shift,
669                dither,
670                blend_mode, mask_test != 0, set_mask != 0);
671 #endif
672          break;
673       case RSX_VULKAN:
674 #if defined(HAVE_VULKAN)
675          rsx_vulkan_push_quad(p0x, p0y, p0w, p1x, p1y, p1w, p2x, p2y, p2w, p3x, p3y, p3w,
676                c0, c1, c2, c3,
677                t0x, t0y, t1x, t1y, t2x, t2y, t3x, t3y,
678                min_u, min_v, max_u, max_v,
679                texpage_x, texpage_y, clut_x, clut_y,
680                texture_blend_mode,
681                depth_shift,
682                dither,
683                blend_mode, mask_test != 0, set_mask != 0, is_sprite, may_be_2d);
684 #endif
685          break;
686    }
687 }
688 
rsx_intf_push_line(int16_t p0x,int16_t p0y,int16_t p1x,int16_t p1y,uint32_t c0,uint32_t c1,bool dither,int blend_mode,uint32_t mask_test,uint32_t set_mask)689 void rsx_intf_push_line(int16_t p0x, int16_t p0y,
690       int16_t p1x, int16_t p1y,
691       uint32_t c0, uint32_t c1,
692       bool dither,
693       int blend_mode,
694       uint32_t mask_test,
695       uint32_t set_mask)
696 {
697 #ifdef RSX_DUMP
698    const rsx_dump_line_data line = {
699       p0x, p0y, p1x, p1y, c0, c1, dither, blend_mode,
700       mask_test != 0, set_mask != 0,
701    };
702    rsx_dump_line(&line);
703 #endif
704 
705    switch (rsx_type)
706    {
707       case RSX_SOFTWARE:
708          break;
709       case RSX_OPENGL:
710 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES)
711          rsx_gl_push_line(p0x, p0y, p1x, p1y, c0, c1, dither, blend_mode, mask_test != 0, set_mask != 0);
712 #endif
713          break;
714       case RSX_VULKAN:
715 #if defined(HAVE_VULKAN)
716          rsx_vulkan_push_line(p0x, p0y, p1x, p1y, c0, c1, dither, blend_mode, mask_test != 0, set_mask != 0);
717 #endif
718          break;
719    }
720 }
721 
rsx_intf_read_vram(uint16_t x,uint16_t y,uint16_t w,uint16_t h,uint16_t * vram)722 bool rsx_intf_read_vram(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *vram)
723 {
724    switch (rsx_type)
725    {
726       case RSX_VULKAN:
727 #if defined(HAVE_VULKAN)
728          return rsx_vulkan_read_vram(x, y, w, h, vram);
729 #endif
730          break;
731       default:
732          break;
733    }
734 
735    return false;
736 }
737 
rsx_intf_load_image(uint16_t x,uint16_t y,uint16_t w,uint16_t h,uint16_t * vram,uint32_t mask_test,uint32_t set_mask)738 void rsx_intf_load_image(uint16_t x, uint16_t y,
739       uint16_t w, uint16_t h,
740       uint16_t *vram, uint32_t mask_test, uint32_t set_mask)
741 {
742 #ifdef RSX_DUMP
743    rsx_dump_load_image(x, y, w, h, vram, mask_test != 0, set_mask != 0);
744 #endif
745 
746    switch (rsx_type)
747    {
748       case RSX_SOFTWARE:
749          break;
750       case RSX_OPENGL:
751 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES)
752          rsx_gl_load_image(x, y, w, h, vram, mask_test, set_mask);
753 #endif
754          break;
755       case RSX_VULKAN:
756 #if defined(HAVE_VULKAN)
757          rsx_vulkan_load_image(x, y, w, h, vram, mask_test, set_mask);
758 #endif
759          break;
760    }
761 }
762 
rsx_intf_fill_rect(uint32_t color,uint16_t x,uint16_t y,uint16_t w,uint16_t h)763 void rsx_intf_fill_rect(uint32_t color,
764       uint16_t x, uint16_t y,
765       uint16_t w, uint16_t h)
766 {
767 #ifdef RSX_DUMP
768    rsx_dump_fill_rect(color, x, y, w, h);
769 #endif
770 
771    switch (rsx_type)
772    {
773       case RSX_SOFTWARE:
774          break;
775       case RSX_OPENGL:
776 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES)
777          rsx_gl_fill_rect(color, x, y, w, h);
778 #endif
779          break;
780       case RSX_VULKAN:
781 #if defined(HAVE_VULKAN)
782          rsx_vulkan_fill_rect(color, x, y, w, h);
783 #endif
784          break;
785    }
786 }
787 
rsx_intf_copy_rect(uint16_t src_x,uint16_t src_y,uint16_t dst_x,uint16_t dst_y,uint16_t w,uint16_t h,uint32_t mask_test,uint32_t set_mask)788 void rsx_intf_copy_rect(uint16_t src_x, uint16_t src_y,
789       uint16_t dst_x, uint16_t dst_y,
790       uint16_t w, uint16_t h,
791       uint32_t mask_test, uint32_t set_mask)
792 {
793 #ifdef RSX_DUMP
794    rsx_dump_copy_rect(src_x, src_y, dst_x, dst_y, w, h, mask_test != 0, set_mask != 0);
795 #endif
796 
797    switch (rsx_type)
798    {
799       case RSX_SOFTWARE:
800          break;
801       case RSX_OPENGL:
802 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES)
803          rsx_gl_copy_rect(src_x, src_y, dst_x, dst_y,
804                w, h, mask_test, set_mask);
805 #endif
806          break;
807       case RSX_VULKAN:
808 #if defined(HAVE_VULKAN)
809          rsx_vulkan_copy_rect(src_x, src_y, dst_x, dst_y, w, h, mask_test, set_mask);
810 #endif
811          break;
812    }
813 }
814 
rsx_intf_has_software_renderer(void)815 bool rsx_intf_has_software_renderer(void)
816 {
817    switch (rsx_type)
818    {
819       case RSX_SOFTWARE:
820          return true;
821       case RSX_OPENGL:
822 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES)
823          return rsx_gl_has_software_renderer();
824 #endif
825          break;
826       case RSX_VULKAN:
827 #if defined(HAVE_VULKAN)
828          return rsx_vulkan_has_software_renderer();
829 #else
830          break;
831 #endif
832    }
833 
834    return false;
835 }
836 
rsx_intf_toggle_display(bool status)837 void rsx_intf_toggle_display(bool status)
838 {
839 #ifdef RSX_DUMP
840    rsx_dump_toggle_display(status);
841 #endif
842 
843     switch (rsx_type)
844     {
845     case RSX_SOFTWARE:
846         break;
847     case RSX_OPENGL:
848 #if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES)
849       rsx_gl_toggle_display(status);
850 #endif
851         break;
852     case RSX_VULKAN:
853 #if defined(HAVE_VULKAN)
854       rsx_vulkan_toggle_display(status);
855 #endif
856         break;
857     }
858 }
859 
rsx_common_get_timing_fps(void)860 double rsx_common_get_timing_fps(void)
861 {
862    bool pal_timings = content_is_pal && !fast_pal;
863 
864    if (core_timing_fps_mode == FORCE_PROGRESSIVE_TIMING)
865       return (pal_timings ? FPS_PAL_NONINTERLACED : FPS_NTSC_NONINTERLACED);
866 
867    else if (core_timing_fps_mode == FORCE_INTERLACED_TIMING)
868       return (pal_timings ? FPS_PAL_INTERLACED : FPS_NTSC_INTERLACED);
869 
870    //else AUTO_TOGGLE_TIMING
871    return (pal_timings ?
872                (currently_interlaced ? FPS_PAL_INTERLACED : FPS_PAL_NONINTERLACED) :
873                (currently_interlaced ? FPS_NTSC_INTERLACED : FPS_NTSC_NONINTERLACED));
874 }
875 
876 
rsx_common_get_aspect_ratio(bool pal_content,bool crop_overscan,int first_visible_scanline,int last_visible_scanline,int aspect_ratio_setting,bool vram_override,bool widescreen_override,int widescreen_hack_aspect_ratio_setting)877 float rsx_common_get_aspect_ratio(bool pal_content, bool crop_overscan,
878                                   int first_visible_scanline, int last_visible_scanline,
879                                   int aspect_ratio_setting, bool vram_override, bool widescreen_override,
880                                   int widescreen_hack_aspect_ratio_setting)
881 {
882    // Current assumptions
883    //    A fixed percentage of width is cropped when crop_overscan is true
884    //    aspect_ratio_setting is one of the following:
885    //          0 - Corrected
886    //          1 - Uncorrected (1:1 PAR)
887    //          2 - Force 4:3 (traditionally what Beetle PSX has done prior to adding in this setting)
888    //          3 - Force NTSC (get corrected NTSC aspect ratio even with PAL games)
889 
890    // Aspect ratio overrides - VRAM and widescreen take precedence
891 
892    if (vram_override)
893       return 2.0 / 1.0;
894 
895    if (widescreen_override)
896       switch(widescreen_hack_aspect_ratio_setting)
897       {
898          case 0:
899             return (16.0 / 10.0);
900          case 1:
901             return (16.0 / 9.0);
902          case 2:
903             return (/*21.0 / 9.0*/ 64.0 / 27.0);
904          case 3:
905             return (32.0 / 9.0);
906       }
907 
908    float ar = (4.0 / 3.0);
909 
910    if (aspect_ratio_setting == 0) // Corrected
911    {
912       // Calculate horizontal scaling in terms of gpu clock cycles
913       ar *= (crop_overscan ? (2560.0 / 2800.0) : 1.0);
914 
915       // Calculate vertical scaling in terms of visible scanline count
916       int num_vis_scanlines = last_visible_scanline - first_visible_scanline + 1;
917       ar *= (pal_content ? (288.0 / num_vis_scanlines) : (240.0 / num_vis_scanlines));
918 
919       return ar;
920    }
921    else if (aspect_ratio_setting == 1) // Uncorrected
922    {
923       int width_base = 0;
924       switch (rsx_width_mode)
925       {
926          case WIDTH_MODE_256:
927             width_base = crop_overscan ? 256 : 280;
928             break;
929          case WIDTH_MODE_320:
930             width_base = crop_overscan ? 320 : 350;
931             break;
932          case WIDTH_MODE_512:
933             width_base = crop_overscan ? 512 : 560;
934             break;
935          case WIDTH_MODE_640:
936             width_base = crop_overscan ? 640 : 700;
937             break;
938          case WIDTH_MODE_368:
939             // Probably slightly off because of rounding, see libretro.cpp comments
940             width_base = crop_overscan ? 366 : 400;
941             break;
942       }
943 
944       double height_base = (last_visible_scanline - first_visible_scanline + 1) *
945                            (rsx_height_mode == HEIGHT_MODE_480 ? 2.0 : 1.0);
946 
947       // Calculate aspect ratio as quotient of raw native framebuffer width and height
948       return width_base / height_base;
949    }
950    else if (aspect_ratio_setting == 3) // Force NTSC
951    {
952       ar *= (crop_overscan ? (2560.0 / 2800.0) : 1.0);
953 
954       int num_vis_scanlines = last_visible_scanline - first_visible_scanline + 1;
955       ar *= (240.0 / num_vis_scanlines);
956 
957       return ar;
958    }
959 
960    return ar; // 4:3
961 }
962