1 /* RetroArch - A frontend for libretro.
2 * Copyright (C) 2020 Google
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 <stdio.h>
17 #include <string.h>
18 #include <malloc.h>
19
20 #include <retro_inline.h>
21 #include <retro_math.h>
22 #include <formats/image.h>
23
24 #ifdef HAVE_CONFIG_H
25 #include "../../config.h"
26 #endif
27
28 #ifdef HAVE_MENU
29 #include "../../menu/menu_driver.h"
30 #endif
31
32 #include "../font_driver.h"
33
34 #include "../../configuration.h"
35 #include "../../command.h"
36 #include "../../driver.h"
37
38 #include "../../retroarch.h"
39 #include "../../verbosity.h"
40
41 #ifndef HAVE_THREADS
42 #include "../../tasks/tasks_internal.h"
43 #endif
44
45 #include <defines/ps3_defines.h>
46
47 #include <rsx/rsx.h>
48 #include <rsx/nv40.h>
49 #include <ppu-types.h>
50
51 #define CB_SIZE 0x100000
52 #define HOST_SIZE (32*1024*1024)
53
54 #include <ppu-lv2.h>
55 #include <stdio.h>
56 #include <malloc.h>
57 #include <string.h>
58 #include <unistd.h>
59 #include <sysutil/video.h>
60 #include <rsx/gcm_sys.h>
61 #include <rsx/rsx.h>
62 #include <io/pad.h>
63 #include <time.h>
64 #include <math.h>
65
66 #define MAX_BUFFERS 2
67
68 #define rsx_context_bind_hw_render(rsx, enable) \
69 if (rsx->shared_context_use) \
70 rsx->ctx_driver->bind_hw_render(rsx->ctx_data, enable)
71
72 typedef struct
73 {
74 int height;
75 int width;
76 int id;
77 uint32_t *ptr;
78 /* Internal stuff */
79 uint32_t offset;
80 } rsxBuffer;
81
82 typedef struct
83 {
84 float v;
85 float u;
86 float y;
87 float x;
88 } rsx_scale_vector_t;
89
90 typedef struct
91 {
92 s16 x0, y0, x1, y1;
93 s16 u0, v0, u1, v1;
94 } rsx_vertex_t;
95
96 typedef struct {
97 video_viewport_t vp;
98 rsxBuffer buffers[MAX_BUFFERS];
99 rsxBuffer menuBuffers[MAX_BUFFERS];
100 int currentBuffer, menuBuffer;
101 gcmContextData* context;
102 u16 width;
103 u16 height;
104 bool menu_frame_enable;
105 bool rgb32;
106 bool vsync;
107 u32 depth_pitch;
108 u32 depth_offset;
109 u32* depth_buffer;
110
111 bool smooth;
112 unsigned rotation;
113 bool keep_aspect;
114 bool should_resize;
115 bool msg_rendering_enabled;
116
117 const shader_backend_t* shader;
118 void* shader_data;
119 void* renderchain_data;
120 void* ctx_data;
121 const gfx_ctx_driver_t* ctx_driver;
122 bool shared_context_use;
123
124 video_info_t video_info;
125 struct video_tex_info tex_info; /* unsigned int alignment */
126 struct video_tex_info prev_info[GFX_MAX_TEXTURES]; /* unsigned alignment */
127 struct video_fbo_rect fbo_rect[GFX_MAX_SHADERS]; /* unsigned alignment */
128 } rsx_t;
129
rsx_get_context(rsx_t * rsx)130 static const gfx_ctx_driver_t* rsx_get_context(rsx_t* rsx)
131 {
132 const gfx_ctx_driver_t* gfx_ctx = NULL;
133 void* ctx_data = NULL;
134 settings_t* settings = config_get_ptr();
135 struct retro_hw_render_callback* hwr = video_driver_get_hw_context();
136
137 bool video_shared_context = settings->bools.video_shared_context;
138 enum gfx_ctx_api api = GFX_CTX_RSX_API;
139
140 rsx->shared_context_use = video_shared_context && hwr->context_type != RETRO_HW_CONTEXT_NONE;
141
142 if ((libretro_get_shared_context())
143 && (hwr->context_type != RETRO_HW_CONTEXT_NONE))
144 rsx->shared_context_use = true;
145
146 gfx_ctx = video_context_driver_init_first(rsx,
147 settings->arrays.video_context_driver,
148 api, 1, 0, rsx->shared_context_use, &ctx_data);
149
150 if (ctx_data)
151 rsx->ctx_data = ctx_data;
152
153 return gfx_ctx;
154 }
155
156 #ifndef HAVE_THREADS
rsx_tasks_finder(retro_task_t * task,void * userdata)157 static bool rsx_tasks_finder(retro_task_t *task,void *userdata)
158 {
159 return task;
160 }
161 task_finder_data_t rsx_tasks_finder_data = {rsx_tasks_finder, NULL};
162 #endif
163
rsx_make_buffer(rsxBuffer * buffer,u16 width,u16 height,int id)164 static int rsx_make_buffer(rsxBuffer * buffer, u16 width, u16 height, int id)
165 {
166 int depth = sizeof(u32);
167 int pitch = depth * width;
168 int size = depth * width * height;
169
170 buffer->ptr = (uint32_t*)rsxMemalign (64, size);
171 if (!buffer->ptr)
172 goto error;
173
174 if (rsxAddressToOffset (buffer->ptr, &buffer->offset) != 0)
175 goto error;
176
177 /* Register the display buffer with the RSX */
178 if (gcmSetDisplayBuffer (id, buffer->offset, pitch, width, height) != 0)
179 goto error;
180
181 buffer->width = width;
182 buffer->height = height;
183 buffer->id = id;
184
185 return TRUE;
186
187 error:
188 if (buffer->ptr)
189 rsxFree (buffer->ptr);
190
191 return FALSE;
192 }
193
rsx_flip(gcmContextData * context,s32 buffer)194 static int rsx_flip(gcmContextData *context, s32 buffer)
195 {
196 if (gcmSetFlip(context, buffer) == 0)
197 {
198 rsxFlushBuffer (context);
199 /* Prevent the RSX from continuing until the flip has finished. */
200 gcmSetWaitFlip (context);
201
202 return TRUE;
203 }
204 return FALSE;
205 }
206
207 #define GCM_LABEL_INDEX 255
208
209 static void rsx_wait_rsx_idle(gcmContextData *context);
210
rsx_wait_flip(void)211 static void rsx_wait_flip(void)
212 {
213 while (gcmGetFlipStatus() != 0)
214 usleep (200); /* Sleep, to not stress the cpu. */
215 gcmResetFlipStatus();
216 }
217
rsx_init_screen(rsx_t * gcm)218 static gcmContextData *rsx_init_screen(rsx_t* gcm)
219 {
220 /* Context to keep track of the RSX buffer. */
221 gcmContextData *context = NULL;
222 static gcmContextData *saved_context = NULL;
223 videoState state;
224 videoConfiguration vconfig;
225 videoResolution res; /* Screen Resolution */
226
227 if (!saved_context)
228 {
229 /* Allocate a 1Mb buffer, alligned to a 1Mb boundary
230 * to be our shared IO memory with the RSX. */
231 void *host_addr = memalign (1024*1024, HOST_SIZE);
232
233 if (!host_addr)
234 goto error;
235
236 /* Initialise Reality, which sets up the
237 * command buffer and shared I/O memory */
238 #ifdef NV40TCL_RENDER_ENABLE
239 /* There was an api breakage on 2020-07-10, let's
240 * workaround this by using one of the new defines */
241 rsxInit (&context, CB_SIZE, HOST_SIZE, host_addr);
242 #else
243 context = rsxInit (CB_SIZE, HOST_SIZE, host_addr);
244 #endif
245 if (!context)
246 goto error;
247 saved_context = context;
248 }
249 else
250 context = saved_context;
251
252 /* Get the state of the display */
253 if (videoGetState (0, 0, &state) != 0)
254 goto error;
255
256 /* Make sure display is enabled */
257 if (state.state != 0)
258 goto error;
259
260 /* Get the current resolution */
261 if (videoGetResolution (state.displayMode.resolution, &res) != 0)
262 goto error;
263
264 /* Configure the buffer format to xRGB */
265 memset (&vconfig, 0, sizeof(videoConfiguration));
266 vconfig.resolution = state.displayMode.resolution;
267 vconfig.format = VIDEO_BUFFER_FORMAT_XRGB;
268 vconfig.pitch = res.width * sizeof(u32);
269 vconfig.aspect = state.displayMode.aspect;
270
271 gcm->width = res.width;
272 gcm->height = res.height;
273
274 rsx_wait_rsx_idle(context);
275
276 if (videoConfigure (0, &vconfig, NULL, 0) != 0)
277 goto error;
278
279 if (videoGetState (0, 0, &state) != 0)
280 goto error;
281
282 gcmSetFlipMode (GCM_FLIP_VSYNC); /* Wait for VSYNC to flip */
283
284 gcm->depth_pitch = res.width * sizeof(u32);
285 gcm->depth_buffer = (u32 *) rsxMemalign (64, (res.height * gcm->depth_pitch)); //Beware, if was (res.height * gcm->depth_pitch)*2
286
287 rsxAddressToOffset (gcm->depth_buffer, &gcm->depth_offset);
288
289 gcmResetFlipStatus();
290
291 return context;
292
293 error:
294 #if 0
295 if (context)
296 rsxFinish (context, 0);
297
298 if (gcm->host_addr)
299 free (gcm->host_addr);
300 #endif
301
302 return NULL;
303 }
304
305
waitFinish(gcmContextData * context,u32 sLabelVal)306 static void waitFinish(gcmContextData *context, u32 sLabelVal)
307 {
308 rsxSetWriteBackendLabel (context, GCM_LABEL_INDEX, sLabelVal);
309
310 rsxFlushBuffer (context);
311
312 while (*(vu32 *) gcmGetLabelAddress (GCM_LABEL_INDEX) != sLabelVal)
313 usleep(30);
314
315 sLabelVal++;
316 }
317
rsx_wait_rsx_idle(gcmContextData * context)318 static void rsx_wait_rsx_idle(gcmContextData *context)
319 {
320 u32 sLabelVal = 1;
321
322 rsxSetWriteBackendLabel (context, GCM_LABEL_INDEX, sLabelVal);
323 rsxSetWaitLabel (context, GCM_LABEL_INDEX, sLabelVal);
324
325 sLabelVal++;
326
327 waitFinish(context, sLabelVal);
328 }
329
rsx_init(const video_info_t * video,input_driver_t ** input,void ** input_data)330 static void* rsx_init(const video_info_t* video,
331 input_driver_t** input, void** input_data)
332 {
333 int i;
334 rsx_t* rsx = malloc(sizeof(rsx_t));
335
336 if (!rsx)
337 return NULL;
338
339 memset(rsx, 0, sizeof(rsx_t));
340
341 rsx->context = rsx_init_screen(rsx);
342 const gfx_ctx_driver_t* ctx_driver = rsx_get_context(rsx);
343
344 if (!ctx_driver)
345 return NULL;
346
347 video_context_driver_set((const gfx_ctx_driver_t*)ctx_driver);
348 rsx->ctx_driver = ctx_driver;
349 rsx->video_info = *video;
350
351 for (i = 0; i < MAX_BUFFERS; i++)
352 rsx_make_buffer(&rsx->buffers[i], rsx->width, rsx->height, i);
353
354 for (i = 0; i < MAX_BUFFERS; i++)
355 rsx_make_buffer(&rsx->menuBuffers[i], rsx->width, rsx->height, i + MAX_BUFFERS);
356
357 rsx_flip(rsx->context, MAX_BUFFERS - 1);
358
359 rsx->vp.x = 0;
360 rsx->vp.y = 0;
361 rsx->vp.width = rsx->width;
362 rsx->vp.height = rsx->height;
363 rsx->vp.full_width = rsx->width;
364 rsx->vp.full_height = rsx->height;
365 rsx->rgb32 = video->rgb32;
366 video_driver_set_size(rsx->vp.width, rsx->vp.height);
367
368 if (input && input_data)
369 {
370 void *ps3input = input_driver_init_wrap(&input_ps3, ps3_joypad.ident);
371 *input = ps3input ? &input_ps3 : NULL;
372 *input_data = ps3input;
373 }
374
375 rsx_context_bind_hw_render(rsx, true);
376
377 return rsx;
378 }
379
rsx_fill_black(uint32_t * dst,uint32_t * dst_end,size_t sz)380 static void rsx_fill_black(uint32_t *dst, uint32_t *dst_end, size_t sz)
381 {
382 if (sz > dst_end - dst)
383 sz = dst_end - dst;
384 memset (dst, 0, sz * 4);
385 }
386
rsx_blit_buffer(rsxBuffer * buffer,const void * frame,unsigned width,unsigned height,unsigned pitch,int rgb32,bool do_scaling)387 static void rsx_blit_buffer(
388 rsxBuffer *buffer, const void *frame, unsigned width,
389 unsigned height, unsigned pitch, int rgb32, bool do_scaling)
390 {
391 int i;
392 uint32_t *dst;
393 uint32_t *dst_end;
394 int pre_clean;
395 int scale = 1, xofs = 0, yofs = 0;
396
397 if (width > buffer->width)
398 width = buffer->width;
399 if (height > buffer->height)
400 height = buffer->height;
401
402 if (do_scaling)
403 {
404 scale = buffer->width / width;
405 if (scale > buffer->height / height)
406 scale = buffer->height / height;
407 if (scale >= 10)
408 scale = 10;
409 if (scale >= 1)
410 {
411 xofs = (buffer->width - width * scale) / 2;
412 yofs = (buffer->height - height * scale) / 2;
413 }
414 else
415 scale = 1;
416 }
417
418 /* TODO/FIXME: let RSX do the copy */
419 pre_clean = xofs + buffer->width * yofs;
420 dst = buffer->ptr;
421 dst_end = buffer->ptr + buffer->width * buffer->height;
422
423 memset(dst, 0, pre_clean * 4);
424 dst += pre_clean;
425
426 if (scale == 1)
427 {
428 if (rgb32)
429 {
430 const uint8_t *src = frame;
431 for (i = 0; i < height; i++)
432 {
433 memcpy(dst, src, width * 4);
434 rsx_fill_black(dst + width, dst_end, buffer->width - width);
435 dst += buffer->width;
436 src += pitch;
437 }
438 }
439 else
440 {
441 const uint16_t *src = frame;
442 for (i = 0; i < height; i++)
443 {
444 for (int j = 0; j < width; j++, src++, dst++)
445 {
446 u16 rgb565 = *src;
447 u8 r = ((rgb565 >> 8) & 0xf8);
448 u8 g = ((rgb565 >> 3) & 0xfc);
449 u8 b = ((rgb565 << 3) & 0xfc);
450 *dst = (r<<16) | (g<<8) | b;
451 }
452 rsx_fill_black(dst, dst_end, buffer->width - width);
453
454 dst += buffer->width - width;
455 src += pitch / 2 - width;
456 }
457 }
458 }
459 else
460 {
461 if (rgb32)
462 {
463 const uint32_t *src = frame;
464 for (i = 0; i < height; i++)
465 {
466 for (int j = 0; j < width; j++, src++)
467 {
468 u32 c = *src;
469 for (int k = 0; k < scale; k++, dst++)
470 for (int l = 0; l < scale; l++)
471 dst[l * buffer->width] = c;
472 }
473 for (int l = 0; l < scale; l++)
474 rsx_fill_black(dst + l * buffer->width, dst_end, buffer->width - width * scale);
475
476 dst += buffer->width * scale - width * scale;
477 src += pitch / 4 - width;
478 }
479 } else {
480 const uint16_t *src = frame;
481 for (i = 0; i < height; i++)
482 {
483 for (int j = 0; j < width; j++, src++)
484 {
485 u16 rgb565 = *src;
486 u8 r = ((rgb565 >> 8) & 0xf8);
487 u8 g = ((rgb565 >> 3) & 0xfc);
488 u8 b = ((rgb565 << 3) & 0xfc);
489 u32 c = (r<<16) | (g<<8) | b;
490 for (int k = 0; k < scale; k++, dst++)
491 for (int l = 0; l < scale; l++)
492 dst[l * buffer->width] = c;
493 }
494 for (int l = 0; l < scale; l++)
495 rsx_fill_black(dst + l * buffer->width, dst_end, buffer->width - width * scale);
496
497 dst += buffer->width * scale - width * scale;
498 src += pitch / 2 - width;
499 }
500 }
501 }
502
503 if (dst < dst_end)
504 memset(dst, 0, 4 * (dst_end - dst));
505 }
506
rsx_update_screen(rsx_t * gcm)507 static void rsx_update_screen(rsx_t* gcm)
508 {
509 rsxBuffer *buffer = gcm->menu_frame_enable
510 ? &gcm->menuBuffers[gcm->menuBuffer]
511 : &gcm->buffers[gcm->currentBuffer];
512 rsx_flip(gcm->context, buffer->id);
513 if (gcm->vsync)
514 rsx_wait_flip();
515 #ifdef HAVE_SYSUTILS
516 cellSysutilCheckCallback();
517 #endif
518 }
519
rsx_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)520 static bool rsx_frame(void* data, const void* frame,
521 unsigned width, unsigned height,
522 uint64_t frame_count,
523 unsigned pitch, const char* msg, video_frame_info_t *video_info)
524 {
525 rsx_t* gcm = (rsx_t*)data;
526 #ifdef HAVE_MENU
527 bool statistics_show = video_info->statistics_show;
528 struct font_params *osd_params = (struct font_params*)
529 &video_info->osd_stat_params;
530 #endif
531
532 if(frame && width && height)
533 {
534 gcm->currentBuffer++;
535 if (gcm->currentBuffer >= MAX_BUFFERS)
536 gcm->currentBuffer = 0;
537 rsx_blit_buffer(
538 &gcm->buffers[gcm->currentBuffer], frame, width, height, pitch,
539 gcm->rgb32, true);
540 }
541
542 /* TODO: translucid menu */
543 rsx_update_screen(gcm);
544
545 return true;
546
547 #ifdef HAVE_MENU
548 if (statistics_show)
549 if (osd_params)
550 font_driver_render_msg(gcm,
551 video_info->stat_text,
552 osd_params, NULL);
553 #endif
554
555 if (msg)
556 font_driver_render_msg(gcm, msg, NULL, NULL);
557 return true;
558 }
559
rsx_set_nonblock_state(void * data,bool toggle,bool a,unsigned b)560 static void rsx_set_nonblock_state(void* data, bool toggle,
561 bool a, unsigned b)
562 {
563 rsx_t* gcm = (rsx_t*)data;
564
565 if (gcm)
566 gcm->vsync = !toggle;
567 }
568
rsx_alive(void * data)569 static bool rsx_alive(void* data)
570 {
571 (void)data;
572 return true;
573 }
574
rsx_focus(void * data)575 static bool rsx_focus(void* data)
576 {
577 (void)data;
578 return true;
579 }
580
rsx_suppress_screensaver(void * data,bool enable)581 static bool rsx_suppress_screensaver(void* data, bool enable)
582 {
583 (void)data;
584 (void)enable;
585 return false;
586 }
587
rsx_free(void * data)588 static void rsx_free(void* data)
589 {
590 int i;
591 rsx_t* gcm = (rsx_t*)data;
592
593 if (!gcm)
594 return;
595
596 gcmSetWaitFlip(gcm->context);
597 for (i = 0; i < MAX_BUFFERS; i++)
598 rsxFree(gcm->buffers[i].ptr);
599 for (i = 0; i < MAX_BUFFERS; i++)
600 rsxFree(gcm->menuBuffers[i].ptr);
601
602 #if 0
603 rsxFinish(gcm->context, 1);
604 free(gcm->host_addr);
605 #endif
606 free (gcm);
607 }
608
rsx_set_texture_frame(void * data,const void * frame,bool rgb32,unsigned width,unsigned height,float alpha)609 static void rsx_set_texture_frame(void* data, const void* frame, bool rgb32,
610 unsigned width, unsigned height, float alpha)
611 {
612 rsx_t* gcm = (rsx_t*)data;
613 int newBuffer = gcm->menuBuffer + 1;
614
615 if (newBuffer >= MAX_BUFFERS)
616 newBuffer = 0;
617
618 /* TODO: respect alpha */
619 rsx_blit_buffer(&gcm->menuBuffers[newBuffer], frame, width, height,
620 width * (rgb32 ? 4 : 2), rgb32, true);
621 gcm->menuBuffer = newBuffer;
622
623 rsx_update_screen(gcm);
624 }
625
rsx_set_texture_enable(void * data,bool state,bool full_screen)626 static void rsx_set_texture_enable(void* data, bool state, bool full_screen)
627 {
628 rsx_t* gcm = (rsx_t*)data;
629
630 if (!gcm)
631 return;
632
633 gcm->menu_frame_enable = state;
634
635 rsx_update_screen(gcm);
636 }
637
rsx_set_rotation(void * data,unsigned rotation)638 static void rsx_set_rotation(void* data, unsigned rotation)
639 {
640 rsx_t* gcm = (rsx_t*)data;
641
642 if (!gcm)
643 return;
644
645 gcm->rotation = rotation;
646 gcm->should_resize = true;
647 }
rsx_set_filtering(void * data,unsigned index,bool smooth)648 static void rsx_set_filtering(void* data, unsigned index, bool smooth)
649 {
650 rsx_t* gcm = (rsx_t*)data;
651
652 if (gcm)
653 gcm->smooth = smooth;
654 }
655
rsx_set_aspect_ratio(void * data,unsigned aspect_ratio_idx)656 static void rsx_set_aspect_ratio(void* data, unsigned aspect_ratio_idx)
657 {
658 rsx_t* gcm = (rsx_t*)data;
659
660 if(!gcm)
661 return;
662
663 gcm->keep_aspect = true;
664 gcm->should_resize = true;
665 }
666
rsx_apply_state_changes(void * data)667 static void rsx_apply_state_changes(void* data)
668 {
669 rsx_t* gcm = (rsx_t*)data;
670
671 if (gcm)
672 gcm->should_resize = true;
673
674 }
675
rsx_viewport_info(void * data,struct video_viewport * vp)676 static void rsx_viewport_info(void* data, struct video_viewport* vp)
677 {
678 rsx_t* gcm = (rsx_t*)data;
679
680 if (gcm)
681 *vp = gcm->vp;
682 }
683
rsx_set_osd_msg(void * data,video_frame_info_t * video_info,const char * msg,const void * params,void * font)684 static void rsx_set_osd_msg(void *data,
685 video_frame_info_t *video_info,
686 const char *msg,
687 const void *params, void *font)
688 {
689 rsx_t* gcm = (rsx_t*)data;
690
691 if (gcm && gcm->msg_rendering_enabled)
692 font_driver_render_msg(data, msg, params, font);
693 }
694
rsx_get_flags(void * data)695 static uint32_t rsx_get_flags(void *data)
696 {
697 uint32_t flags = 0;
698
699 return flags;
700 }
701
702 static const video_poke_interface_t rsx_poke_interface = {
703 rsx_get_flags,
704 NULL, /* load_texture */
705 NULL, /* unload_texture */
706 NULL,
707 NULL,
708 rsx_set_filtering,
709 NULL, /* get_video_output_size */
710 NULL, /* get_video_output_prev */
711 NULL, /* get_video_output_next */
712 NULL, /* get_current_framebuffer */
713 NULL,
714 rsx_set_aspect_ratio,
715 rsx_apply_state_changes,
716 rsx_set_texture_frame,
717 rsx_set_texture_enable,
718 rsx_set_osd_msg,
719 NULL, /* show_mouse */
720 NULL, /* grab_mouse_toggle */
721 NULL, /* get_current_shader */
722 NULL, /* get_current_software_framebuffer */
723 NULL /* get_hw_render_interface */
724 };
725
rsx_get_poke_interface(void * data,const video_poke_interface_t ** iface)726 static void rsx_get_poke_interface(void* data,
727 const video_poke_interface_t** iface)
728 {
729 (void)data;
730 *iface = &rsx_poke_interface;
731 }
732
rsx_set_shader(void * data,enum rarch_shader_type type,const char * path)733 static bool rsx_set_shader(void* data,
734 enum rarch_shader_type type, const char* path)
735 {
736 (void)data;
737 (void)type;
738 (void)path;
739
740 return false;
741 }
742
743 video_driver_t video_gcm =
744 {
745 rsx_init,
746 rsx_frame,
747 rsx_set_nonblock_state,
748 rsx_alive,
749 rsx_focus,
750 rsx_suppress_screensaver,
751 NULL, /* has_windowed */
752 rsx_set_shader,
753 rsx_free,
754 "rsx",
755 NULL, /* set_viewport */
756 rsx_set_rotation,
757 rsx_viewport_info,
758 NULL, /* read_viewport */
759 NULL, /* read_frame_raw */
760 #ifdef HAVE_OVERLAY
761 NULL,
762 #endif
763 #ifdef HAVE_VIDEO_LAYOUT
764 NULL,
765 #endif
766 rsx_get_poke_interface
767 };
768