1 /*  RetroArch - A frontend for libretro.
2  *  Copyright (C) 2018 - Francisco Javier Trujillo Mata - fjtrujy
3  *
4  *  RetroArch is free software: you can redistribute it and/or modify it under the terms
5  *  of the GNU General Public License as published by the Free Software Found-
6  *  ation, either version 3 of the License, or (at your option) any later version.
7  *
8  *  RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9  *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
10  *  PURPOSE.  See the GNU General Public License for more details.
11  *
12  *  You should have received a copy of the GNU General Public License along with RetroArch.
13  *  If not, see <http://www.gnu.org/licenses/>.
14  */
15 
16 #include <kernel.h>
17 #include <gsKit.h>
18 #include <gsInline.h>
19 
20 #include "../../driver.h"
21 #include "../../retroarch.h"
22 #include "../../verbosity.h"
23 
24 #include "../../libretro-common/include/libretro_gskit_ps2.h"
25 
26 /* turn white GS Screen */
27 #define GS_TEXT GS_SETREG_RGBAQ(0x80,0x80,0x80,0x80,0x00)
28 /* turn white GS Screen */
29 #define GS_BLACK GS_SETREG_RGBAQ(0x00,0x00,0x00,0x00,0x00)
30 
31 #define NTSC_WIDTH  640
32 #define NTSC_HEIGHT 448
33 
34 typedef struct ps2_video
35 {
36    /* I need to create this additional field
37     * to be used in the font driver*/
38    bool clearVRAM_font;
39    bool menuVisible;
40    bool fullscreen;
41    bool vsync;
42    int vsync_callback_id;
43    bool force_aspect;
44 
45    int PSM;
46    int menu_filter;
47    int core_filter;
48 
49    video_viewport_t vp;
50 
51    /* Palette in the cores */
52    struct retro_hw_render_interface_gskit_ps2 iface;
53 
54    GSGLOBAL *gsGlobal;
55    GSTEXTURE *menuTexture;
56    GSTEXTURE *coreTexture;
57 } ps2_video_t;
58 
59 static int vsync_sema_id;
60 
61 /* PRIVATE METHODS */
vsync_handler()62 static int vsync_handler()
63 {
64    iSignalSema(vsync_sema_id);
65 
66    ExitHandler();
67    return 0;
68 }
69 
init_GSGlobal(void)70 static GSGLOBAL *init_GSGlobal(void)
71 {
72    ee_sema_t sema;
73    sema.init_count = 0;
74    sema.max_count = 1;
75    sema.option = 0;
76    vsync_sema_id = CreateSema(&sema);
77 
78    GSGLOBAL *gsGlobal        = gsKit_init_global();
79 
80    gsGlobal->Mode            = GS_MODE_NTSC;
81    gsGlobal->Interlace       = GS_INTERLACED;
82    gsGlobal->Field           = GS_FIELD;
83    gsGlobal->Width           = NTSC_WIDTH;
84    gsGlobal->Height          = NTSC_HEIGHT;
85 
86    gsGlobal->PSM             = GS_PSM_CT16;
87    gsGlobal->PSMZ            = GS_PSMZ_16;
88    gsGlobal->DoubleBuffering = GS_SETTING_OFF;
89    gsGlobal->ZBuffering      = GS_SETTING_OFF;
90    gsGlobal->PrimAlphaEnable = GS_SETTING_OFF;
91 
92    dmaKit_init(D_CTRL_RELE_OFF,D_CTRL_MFD_OFF, D_CTRL_STS_UNSPEC,
93                D_CTRL_STD_OFF, D_CTRL_RCYC_8, 1 << DMA_CHANNEL_GIF);
94 
95    /* Initialize the DMAC */
96    dmaKit_chan_init(DMA_CHANNEL_GIF);
97 
98    gsKit_init_screen(gsGlobal);
99    gsKit_mode_switch(gsGlobal, GS_ONESHOT);
100    gsKit_clear(gsGlobal, GS_BLACK);
101 
102    return gsGlobal;
103 }
104 
deinit_GSGlobal(GSGLOBAL * gsGlobal)105 static void deinit_GSGlobal(GSGLOBAL *gsGlobal)
106 {
107    gsKit_clear(gsGlobal, GS_BLACK);
108    gsKit_vram_clear(gsGlobal);
109    gsKit_deinit_global(gsGlobal);
110 }
111 
112 /* Copy of gsKit_sync_flip, but without the 'flip' */
gsKit_sync(GSGLOBAL * gsGlobal)113 static void gsKit_sync(GSGLOBAL *gsGlobal)
114 {
115    if (!gsGlobal->FirstFrame)
116       WaitSema(vsync_sema_id);
117 
118    while (PollSema(vsync_sema_id) >= 0);
119 }
120 
121 /* Copy of gsKit_sync_flip, but without the 'sync' */
gsKit_flip(GSGLOBAL * gsGlobal)122 static void gsKit_flip(GSGLOBAL *gsGlobal)
123 {
124    if (!gsGlobal->FirstFrame)
125    {
126       if (gsGlobal->DoubleBuffering == GS_SETTING_ON)
127       {
128          GS_SET_DISPFB2( gsGlobal->ScreenBuffer[
129                gsGlobal->ActiveBuffer & 1] / 8192,
130                gsGlobal->Width / 64, gsGlobal->PSM, 0, 0 );
131 
132          gsGlobal->ActiveBuffer ^= 1;
133       }
134 
135    }
136 
137    gsKit_setactive(gsGlobal);
138 }
139 
prepare_new_texture(void)140 static GSTEXTURE *prepare_new_texture(void)
141 {
142    GSTEXTURE *texture = (GSTEXTURE*)calloc(1, sizeof(*texture));
143    return texture;
144 }
145 
init_ps2_video(ps2_video_t * ps2)146 static void init_ps2_video(ps2_video_t *ps2)
147 {
148    ps2->gsGlobal    = init_GSGlobal();
149    gsKit_TexManager_init(ps2->gsGlobal);
150 
151    ps2->vp.x                = 0;
152    ps2->vp.y                = 0;
153    ps2->vp.width            = ps2->gsGlobal->Width;
154    ps2->vp.height           = ps2->gsGlobal->Height;
155    ps2->vp.full_width       = ps2->gsGlobal->Width;
156    ps2->vp.full_height      = ps2->gsGlobal->Height;
157 
158    ps2->vsync_callback_id = gsKit_add_vsync_handler(vsync_handler);
159    ps2->menuTexture = prepare_new_texture();
160    ps2->coreTexture = prepare_new_texture();
161 
162    /* Used for cores that supports palette */
163    ps2->iface.interface_type    = RETRO_HW_RENDER_INTERFACE_GSKIT_PS2;
164    ps2->iface.interface_version = RETRO_HW_RENDER_INTERFACE_GSKIT_PS2_VERSION;
165    ps2->iface.coreTexture       = ps2->coreTexture;
166 }
167 
ps2_gfx_deinit_texture(GSTEXTURE * texture)168 static void ps2_gfx_deinit_texture(GSTEXTURE *texture)
169 {
170    texture->Mem  = NULL;
171    texture->Clut = NULL;
172 }
173 
set_texture(GSTEXTURE * texture,const void * frame,int width,int height,int PSM,int filter)174 static void set_texture(GSTEXTURE *texture, const void *frame,
175       int width, int height, int PSM, int filter)
176 {
177    texture->Width  = width;
178    texture->Height = height;
179    texture->PSM    = PSM;
180    texture->Filter = filter;
181    texture->Mem    = (void *)frame;
182 }
183 
prim_texture(GSGLOBAL * gsGlobal,GSTEXTURE * texture,int zPosition,bool force_aspect,struct retro_hw_ps2_insets padding)184 static void prim_texture(GSGLOBAL *gsGlobal, GSTEXTURE *texture, int zPosition, bool force_aspect, struct retro_hw_ps2_insets padding)
185 {
186    float x1, y1, x2, y2;
187    float visible_width  =  texture->Width - padding.left - padding.right;
188    float visible_height =  texture->Height - padding.top - padding.bottom;
189 
190    if (force_aspect)
191    {
192       float width_proportion  = (float)gsGlobal->Width / (float)visible_width;
193       float height_proportion = (float)gsGlobal->Height / (float)visible_height;
194       float delta             = MIN(width_proportion, height_proportion);
195       float newWidth          = visible_width * delta;
196       float newHeight         = visible_height * delta;
197 
198       x1 = (gsGlobal->Width - newWidth) / 2.0f;
199       y1 = (gsGlobal->Height - newHeight) / 2.0f;
200       x2 = newWidth + x1;
201       y2 = newHeight + y1;
202    }
203    else
204    {
205       x1 = 0.0f;
206       y1 = 0.0f;
207       x2 = gsGlobal->Width;
208       y2 = gsGlobal->Height;
209    }
210 
211    gsKit_prim_sprite_texture( gsGlobal, texture,
212          x1,            /* X1 */
213          y1,            /* Y1 */
214          padding.left,  /* U1 */
215          padding.top,   /* V1 */
216          x2,            /* X2 */
217          y2,            /* Y2 */
218          texture->Width - padding.right,   /* U2 */
219          texture->Height - padding.bottom, /* V2 */
220          zPosition,
221          GS_TEXT);
222 }
223 
refreshScreen(ps2_video_t * ps2)224 static void refreshScreen(ps2_video_t *ps2)
225 {
226    if (ps2->vsync)
227    {
228       gsKit_sync(ps2->gsGlobal);
229       gsKit_flip(ps2->gsGlobal);
230    }
231    gsKit_queue_exec(ps2->gsGlobal);
232    gsKit_TexManager_nextFrame(ps2->gsGlobal);
233 }
234 
ps2_gfx_init(const video_info_t * video,input_driver_t ** input,void ** input_data)235 static void *ps2_gfx_init(const video_info_t *video,
236       input_driver_t **input, void **input_data)
237 {
238    void *ps2input   = NULL;
239    ps2_video_t *ps2 = (ps2_video_t*)calloc(1, sizeof(ps2_video_t));
240 
241    *input_data      = NULL;
242 
243    if (!ps2)
244       return NULL;
245 
246    init_ps2_video(ps2);
247    if (video->font_enable)
248       font_driver_init_osd(ps2,
249             video,
250             false,
251             video->is_threaded,
252             FONT_DRIVER_RENDER_PS2);
253 
254    ps2->PSM          = (video->rgb32 ? GS_PSM_CT32 : GS_PSM_CT16);
255    ps2->fullscreen   = video->fullscreen;
256    ps2->core_filter  = video->smooth ? GS_FILTER_LINEAR : GS_FILTER_NEAREST;
257    ps2->force_aspect = video->force_aspect;
258    ps2->vsync        = video->vsync;
259 
260    if (input && input_data)
261    {
262       settings_t *settings = config_get_ptr();
263       ps2input             = input_driver_init_wrap(&input_ps2,
264             settings->arrays.input_joypad_driver);
265       *input               = ps2input ? &input_ps2 : NULL;
266       *input_data          = ps2input;
267    }
268 
269    return ps2;
270 }
271 
ps2_gfx_frame(void * data,const void * frame,unsigned width,unsigned height,uint64_t frame_count,unsigned pitch,const char * msg,video_frame_info_t * video_info)272 static bool ps2_gfx_frame(void *data, const void *frame,
273       unsigned width, unsigned height, uint64_t frame_count,
274       unsigned pitch, const char *msg, video_frame_info_t *video_info)
275 {
276    ps2_video_t               *ps2 = (ps2_video_t*)data;
277    struct font_params *osd_params = (struct font_params*)
278       &video_info->osd_stat_params;
279    bool statistics_show           = video_info->statistics_show;
280 
281    if (!width || !height)
282       return false;
283 
284 #if defined(DEBUG)
285    if (frame_count % 180 == 0)
286       printf("ps2_gfx_frame %llu\n", frame_count);
287 #endif
288 
289    if (frame)
290    {
291       struct retro_hw_ps2_insets padding = empty_ps2_insets;
292       /* Checking if the transfer is done in the core */
293       if (frame != RETRO_HW_FRAME_BUFFER_VALID)
294       {
295          /* calculate proper width based in the pitch */
296          int shifh_per_bytes = (ps2->PSM == GS_PSM_CT32) ? 2 : 1;
297          int real_width      = pitch >> shifh_per_bytes;
298          set_texture(ps2->coreTexture, frame, real_width, height, ps2->PSM, ps2->core_filter);
299 
300          padding.right       = real_width - width;
301       }
302       else
303       {
304          padding = ps2->iface.padding;
305       }
306 
307       gsKit_TexManager_invalidate(ps2->gsGlobal, ps2->coreTexture);
308       gsKit_TexManager_bind(ps2->gsGlobal, ps2->coreTexture);
309       prim_texture(ps2->gsGlobal, ps2->coreTexture, 1, ps2->force_aspect, padding);
310    }
311 
312    if (ps2->menuVisible)
313    {
314       bool texture_empty = !ps2->menuTexture->Width || !ps2->menuTexture->Height;
315       if (!texture_empty)
316       {
317          prim_texture(ps2->gsGlobal, ps2->menuTexture, 2, ps2->fullscreen, empty_ps2_insets);
318       }
319    }
320    else if (statistics_show)
321    {
322       if (osd_params)
323          font_driver_render_msg(ps2, video_info->stat_text,
324                osd_params, NULL);
325    }
326 
327    if (!string_is_empty(msg))
328       font_driver_render_msg(ps2, msg, NULL, NULL);
329 
330    refreshScreen(ps2);
331 
332    return true;
333 }
334 
ps2_gfx_set_nonblock_state(void * data,bool toggle,bool adaptive_vsync_enabled,unsigned swap_interval)335 static void ps2_gfx_set_nonblock_state(void *data, bool toggle,
336       bool adaptive_vsync_enabled, unsigned swap_interval)
337 {
338    ps2_video_t *ps2 = (ps2_video_t*)data;
339 
340    if (ps2)
341       ps2->vsync = !toggle;
342 }
343 
ps2_gfx_alive(void * data)344 static bool ps2_gfx_alive(void *data) { return true; }
ps2_gfx_focus(void * data)345 static bool ps2_gfx_focus(void *data) { return true; }
ps2_gfx_suppress_screensaver(void * data,bool enable)346 static bool ps2_gfx_suppress_screensaver(void *data, bool enable) { return false; }
ps2_gfx_has_windowed(void * data)347 static bool ps2_gfx_has_windowed(void *data) { return false; }
348 
ps2_gfx_free(void * data)349 static void ps2_gfx_free(void *data)
350 {
351    ps2_video_t *ps2 = (ps2_video_t*)data;
352 
353    gsKit_clear(ps2->gsGlobal, GS_BLACK);
354    gsKit_vram_clear(ps2->gsGlobal);
355 
356    font_driver_free_osd();
357 
358    ps2_gfx_deinit_texture(ps2->menuTexture);
359    ps2_gfx_deinit_texture(ps2->coreTexture);
360 
361    free(ps2->menuTexture);
362    free(ps2->coreTexture);
363 
364    gsKit_remove_vsync_handler(ps2->vsync_callback_id);
365    deinit_GSGlobal(ps2->gsGlobal);
366 
367    if (vsync_sema_id >= 0)
368       DeleteSema(vsync_sema_id);
369 
370    free(data);
371 }
372 
ps2_gfx_set_shader(void * data,enum rarch_shader_type type,const char * path)373 static bool ps2_gfx_set_shader(void *data,
374       enum rarch_shader_type type, const char *path) { return false; }
375 
ps2_set_filtering(void * data,unsigned index,bool smooth,bool ctx_scaling)376 static void ps2_set_filtering(void *data, unsigned index, bool smooth, bool ctx_scaling)
377 {
378    ps2_video_t *ps2 = (ps2_video_t*)data;
379 
380    ps2->menu_filter = smooth ? GS_FILTER_LINEAR : GS_FILTER_NEAREST;
381 }
382 
ps2_set_texture_frame(void * data,const void * frame,bool rgb32,unsigned width,unsigned height,float alpha)383 static void ps2_set_texture_frame(void *data, const void *frame, bool rgb32,
384       unsigned width, unsigned height, float alpha)
385 {
386    ps2_video_t *ps2      = (ps2_video_t*)data;
387 
388    int PSM               = (rgb32 ? GS_PSM_CT32 : GS_PSM_CT16);
389 
390    set_texture(ps2->menuTexture, frame, width, height, PSM, ps2->menu_filter);
391    gsKit_TexManager_invalidate(ps2->gsGlobal, ps2->menuTexture);
392    gsKit_TexManager_bind(ps2->gsGlobal, ps2->menuTexture);
393 }
394 
ps2_set_texture_enable(void * data,bool enable,bool fullscreen)395 static void ps2_set_texture_enable(void *data, bool enable, bool fullscreen)
396 {
397    ps2_video_t *ps2 = (ps2_video_t*)data;
398 
399    if (ps2->menuVisible != enable)
400    {
401       /* If Menu change status, CLEAR SCREEN */
402       gsKit_clear(ps2->gsGlobal, GS_BLACK);
403    }
404    ps2->menuVisible = enable;
405    ps2->fullscreen  = fullscreen;
406 }
407 
ps2_get_hw_render_interface(void * data,const struct retro_hw_render_interface ** iface)408 static bool ps2_get_hw_render_interface(void* data,
409       const struct retro_hw_render_interface** iface)
410 {
411    ps2_video_t          *ps2 = (ps2_video_t*)data;
412    ps2->iface.padding        = empty_ps2_insets;
413    *iface                    =
414       (const struct retro_hw_render_interface*)&ps2->iface;
415    return true;
416 }
417 
418 static const video_poke_interface_t ps2_poke_interface = {
419    NULL,          /* get_flags  */
420    NULL,
421    NULL,
422    NULL,
423    NULL, /* get_refresh_rate */
424    ps2_set_filtering,
425    NULL, /* get_video_output_size */
426    NULL, /* get_video_output_prev */
427    NULL, /* get_video_output_next */
428    NULL, /* get_current_framebuffer */
429    NULL, /* get_proc_address */
430    NULL, /* set_aspect_ratio */
431    NULL, /* apply_state_changes */
432    ps2_set_texture_frame,
433    ps2_set_texture_enable,
434    font_driver_render_msg,             /* set_osd_msg */
435    NULL,                        /* show_mouse  */
436    NULL,                        /* grab_mouse_toggle */
437    NULL,                        /* get_current_shader */
438    NULL,                        /* get_current_software_framebuffer */
439    ps2_get_hw_render_interface  /* get_hw_render_interface */
440 };
441 
ps2_gfx_get_poke_interface(void * data,const video_poke_interface_t ** iface)442 static void ps2_gfx_get_poke_interface(void *data,
443       const video_poke_interface_t **iface)
444 {
445    (void)data;
446    *iface = &ps2_poke_interface;
447 }
448 
449 video_driver_t video_ps2 = {
450    ps2_gfx_init,
451    ps2_gfx_frame,
452    ps2_gfx_set_nonblock_state,
453    ps2_gfx_alive,
454    ps2_gfx_focus,
455    ps2_gfx_suppress_screensaver,
456    ps2_gfx_has_windowed,
457    ps2_gfx_set_shader,
458    ps2_gfx_free,
459    "ps2",
460    NULL, /* set_viewport */
461    NULL, /* set_rotation */
462    NULL, /* viewport_info */
463    NULL, /* read_viewport  */
464    NULL, /* read_frame_raw */
465 
466 #ifdef HAVE_OVERLAY
467   NULL, /* overlay_interface */
468 #endif
469 #ifdef HAVE_VIDEO_LAYOUT
470   NULL,
471 #endif
472   ps2_gfx_get_poke_interface,
473 };
474