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