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