1 /*  RetroArch - A frontend for libretro.
2  *  Copyright (C) 2018      - misson20000
3  *  Copyright (C) 2018      - m4xw
4  *
5  *  RetroArch is free software: you can redistribute it and/or modify it under the terms
6  *  of the GNU General Public License as published by the Free Software Found-
7  *  ation, either version 3 of the License, or (at your option) any later version.
8  *
9  *  RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
10  *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
11  *  PURPOSE.  See the GNU General Public License for more details.
12  *
13  *  You should have received a copy of the GNU General Public License along with RetroArch.
14  *  If not, see <http://www.gnu.org/licenses/>.
15  */
16 
17 #include <stdio.h>
18 #include <string.h>
19 #include <malloc.h>
20 
21 #include <retro_inline.h>
22 #include <retro_math.h>
23 #include <formats/image.h>
24 
25 #include <gfx/scaler/scaler.h>
26 #include <gfx/scaler/pixconv.h>
27 
28 #include <libtransistor/nx.h>
29 
30 #ifdef HAVE_CONFIG_H
31 #include "../../config.h"
32 #endif
33 
34 #ifdef HAVE_MENU
35 #include "../../menu/menu_driver.h"
36 #endif
37 
38 #include "../font_driver.h"
39 
40 #include "../../configuration.h"
41 #include "../../command.h"
42 #include "../../driver.h"
43 
44 #include "../../retroarch.h"
45 #include "../../verbosity.h"
46 
47 #ifndef HAVE_THREADS
48 #include "../../tasks/tasks_internal.h"
49 #endif
50 
51 typedef struct
52 {
53    bool vsync;
54    bool rgb32;
55    unsigned width, height;
56    unsigned rotation;
57 	uint32_t last_width;
58 	uint32_t last_height;
59 
60    struct video_viewport vp;
61 
62 	struct
63    {
64       bool enable;
65       bool fullscreen;
66 
67       uint32_t *pixels;
68 
69       unsigned width;
70       unsigned height;
71 
72       unsigned tgtw;
73       unsigned tgth;
74 
75       struct scaler_ctx scaler;
76    } menu_texture;
77 	surface_t surface;
78 	revent_h vsync_h;
79 	uint32_t image[1280*720];
80 	struct scaler_ctx scaler;
81 } switch_video_t;
82 
switch_init(const video_info_t * video,input_driver_t ** input,void ** input_data)83 static void *switch_init(const video_info_t *video,
84       input_driver_t **input, void **input_data)
85 {
86    unsigned x, y;
87    switch_video_t *sw = (switch_video_t*)calloc(1, sizeof(*sw));
88    if (!sw)
89       return NULL;
90 
91    RARCH_LOG("loading switch gfx driver, width: %d, height: %d\n", video->width, video->height);
92 
93    result_t r = display_init();
94    if (r != RESULT_OK)
95       goto error;
96    r = display_open_layer(&sw->surface);
97 
98    if (r != RESULT_OK)
99    {
100       display_finalize();
101       goto error;
102    }
103    r = display_get_vsync_event(&sw->vsync_h);
104 
105    if (r != RESULT_OK)
106    {
107 	   display_close_layer(&sw->surface);
108       display_finalize();
109       goto error;
110    }
111 
112    sw->vp.x           = 0;
113    sw->vp.y           = 0;
114    sw->vp.width       = 1280;
115    sw->vp.height      = 720;
116    sw->vp.full_width  = 1280;
117    sw->vp.full_height = 720;
118    video_driver_set_size(sw->vp.width, sw->vp.height);
119 
120    sw->vsync   = video->vsync;
121    sw->rgb32   = video->rgb32;
122 
123    *input      = NULL;
124    *input_data = NULL;
125 
126    return sw;
127 
128 error:
129    free(sw);
130    return NULL;
131 }
132 
switch_wait_vsync(switch_video_t * sw)133 static void switch_wait_vsync(switch_video_t *sw)
134 {
135 	uint32_t handle_idx;
136 	svcWaitSynchronization(&handle_idx, &sw->vsync_h, 1, 33333333);
137 	svcResetSignal(sw->vsync_h);
138 }
139 
switch_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)140 static bool switch_frame(void *data, const void *frame,
141       unsigned width, unsigned height,
142       uint64_t frame_count, unsigned pitch,
143       const char *msg, video_frame_info_t *video_info)
144 {
145    static uint64_t last_frame = 0;
146 
147    unsigned x, y;
148    result_t r;
149    int tgtw, tgth, centerx, centery;
150    uint32_t *out_buffer   = NULL;
151    switch_video_t *sw     = data;
152    int xsf                = 1280 / width;
153    int ysf                = 720  / height;
154    int sf                 = xsf;
155 #ifdef HAVE_MENU
156    bool menu_is_alive     = video_info->menu_is_alive;
157 #endif
158    bool statistics_show   = video_info->statistics_show;
159    struct font_params
160       *osd_params         = (struct font_params*)
161       &video_info->osd_stat_params;
162 
163 
164    if (ysf < sf)
165       sf = ysf;
166 
167    tgtw                   = width * sf;
168    tgth                   = height * sf;
169    centerx                = (1280-tgtw)/2;
170    centery                = (720-tgth)/2;
171 
172    /* clear image to black */
173    for (y = 0; y < 720; y++)
174       for (x = 0; x < 1280; x++)
175          sw->image[y*1280+x] = 0xFF000000;
176 
177    if(width > 0 && height > 0)
178    {
179       if(sw->last_width != width ||
180             sw->last_height != height)
181       {
182          scaler_ctx_gen_reset(&sw->scaler);
183 
184          sw->scaler.in_width    = width;
185          sw->scaler.in_height   = height;
186          sw->scaler.in_stride   = pitch;
187          sw->scaler.in_fmt      = sw->rgb32 ? SCALER_FMT_ARGB8888 : SCALER_FMT_RGB565;
188 
189          sw->scaler.out_width   = tgtw;
190          sw->scaler.out_height  = tgth;
191          sw->scaler.out_stride  = 1280 * sizeof(uint32_t);
192          sw->scaler.out_fmt     = SCALER_FMT_ABGR8888;
193 
194          sw->scaler.scaler_type = SCALER_TYPE_POINT;
195 
196          if(!scaler_ctx_gen_filter(&sw->scaler))
197          {
198             RARCH_ERR("failed to generate scaler for main image\n");
199             return false;
200          }
201 
202          sw->last_width         = width;
203          sw->last_height        = height;
204       }
205 
206       scaler_ctx_scale(&sw->scaler, sw->image + (centery * 1280) + centerx, frame);
207    }
208 
209 #if defined(HAVE_MENU)
210    if (sw->menu_texture.enable)
211    {
212       menu_driver_frame(menu_is_alive, video_info);
213 
214       if (sw->menu_texture.pixels)
215       {
216 #if 0
217          if (sw->menu_texture.fullscreen)
218          {
219 #endif
220             scaler_ctx_scale(&sw->menu_texture.scaler, sw->image +
221                   ((720-sw->menu_texture.tgth)/2)*1280 +
222                   ((1280-sw->menu_texture.tgtw)/2), sw->menu_texture.pixels);
223 #if 0
224          }
225          else
226          {
227          }
228 #endif
229       }
230    }
231    else if (statistics_show)
232    {
233       if (osd_params)
234          font_driver_render_msg(sw, video_info->stat_text,
235                osd_params, NULL);
236    }
237 #endif
238 
239 #if 0
240    if (frame_count > 6000)
241    {
242       display_finalize();
243       exit(0);
244    }
245 #endif
246 
247    r = surface_dequeue_buffer(&sw->surface, &out_buffer);
248    if(r != RESULT_OK)
249       return true; /* just skip the frame */
250 
251    r = surface_wait_buffer(&sw->surface);
252    if(r != RESULT_OK)
253       return true;
254    gfx_slow_swizzling_blit(out_buffer, sw->image, 1280, 720, 0, 0);
255 
256    r = surface_queue_buffer(&sw->surface);
257 
258    if (r != RESULT_OK)
259       return false;
260 
261    last_frame = svcGetSystemTick();
262    return true;
263 }
264 
switch_set_nonblock_state(void * data,bool toggle,bool c,unsigned d)265 static void switch_set_nonblock_state(void *data, bool toggle, bool c, unsigned d)
266 {
267    switch_video_t *sw = data;
268    sw->vsync          = !toggle;
269 }
270 
switch_alive(void * data)271 static bool switch_alive(void *data) { return true; }
switch_focus(void * data)272 static bool switch_focus(void *data) { return true; }
switch_suppress_screensaver(void * data,bool enable)273 static bool switch_suppress_screensaver(void *data, bool enable) { return false; }
switch_has_windowed(void * data)274 static bool switch_has_windowed(void *data) { return false; }
275 
switch_free(void * data)276 static void switch_free(void *data)
277 {
278 	switch_video_t *sw = data;
279 	svcCloseHandle(sw->vsync_h);
280 	display_close_layer(&sw->surface);
281 	display_finalize();
282 	free(sw);
283 }
284 
switch_set_shader(void * data,enum rarch_shader_type type,const char * path)285 static bool switch_set_shader(void *data,
286       enum rarch_shader_type type, const char *path)
287 {
288    (void) data;
289    (void) type;
290    (void) path;
291 
292    return false;
293 }
294 
switch_set_rotation(void * data,unsigned rotation)295 static void switch_set_rotation(void *data, unsigned rotation)
296 {
297    switch_video_t *sw = data;
298    if (!sw)
299       return;
300    sw->rotation = rotation;
301 }
302 
switch_viewport_info(void * data,struct video_viewport * vp)303 static void switch_viewport_info(void *data, struct video_viewport *vp)
304 {
305    switch_video_t *sw = data;
306    *vp = sw->vp;
307 }
308 
switch_set_texture_frame(void * data,const void * frame,bool rgb32,unsigned width,unsigned height,float alpha)309 static void switch_set_texture_frame(
310       void *data, const void *frame, bool rgb32,
311       unsigned width, unsigned height, float alpha)
312 {
313    switch_video_t *sw = data;
314 
315    if (  !sw->menu_texture.pixels         ||
316          sw->menu_texture.width  != width ||
317          sw->menu_texture.height != height)
318    {
319       struct scaler_ctx *sctx;
320       int xsf, ysf, sf;
321       if (sw->menu_texture.pixels)
322          free(sw->menu_texture.pixels);
323 
324       sw->menu_texture.pixels = malloc(width * height * (rgb32 ? 4 : 2));
325       if (!sw->menu_texture.pixels)
326       {
327          RARCH_ERR("failed to allocate buffer for menu texture\n");
328          return;
329       }
330 
331       xsf                     = 1280 / width;
332       ysf                     = 720  / height;
333       sf                      = xsf;
334 
335       if (ysf < sf)
336 	      sf = ysf;
337 
338       sw->menu_texture.width  = width;
339       sw->menu_texture.height = height;
340       sw->menu_texture.tgtw   = width * sf;
341       sw->menu_texture.tgth   = height * sf;
342 
343       sctx                    = &sw->menu_texture.scaler;
344       scaler_ctx_gen_reset(sctx);
345 
346       sctx->in_width          = width;
347       sctx->in_height         = height;
348       sctx->in_stride         = width * (rgb32 ? 4 : 2);
349       sctx->in_fmt            = rgb32 ? SCALER_FMT_ARGB8888 : SCALER_FMT_RGB565;
350 
351       sctx->out_width         = sw->menu_texture.tgtw;
352       sctx->out_height        = sw->menu_texture.tgth;
353       sctx->out_stride        = 1280 * 4;
354       sctx->out_fmt           = SCALER_FMT_ABGR8888;
355 
356       sctx->scaler_type       = SCALER_TYPE_POINT;
357 
358       if (!scaler_ctx_gen_filter(sctx))
359       {
360          RARCH_ERR("failed to generate scaler for menu texture\n");
361          return;
362       }
363    }
364 
365    memcpy(sw->menu_texture.pixels, frame, width * height * (rgb32 ? 4 : 2));
366 }
367 
switch_set_texture_enable(void * data,bool enable,bool full_screen)368 static void switch_set_texture_enable(void *data, bool enable, bool full_screen)
369 {
370 	switch_video_t *sw = data;
371    if (!sw)
372       return;
373 
374 	sw->menu_texture.enable = enable;
375 	sw->menu_texture.fullscreen = full_screen;
376 }
377 
378 static const video_poke_interface_t switch_poke_interface = {
379    NULL, /* get_flags */
380 	NULL, /* load_texture */
381 	NULL, /* unload_texture */
382 	NULL, /* set_video_mode */
383 	NULL, /* get_refresh_rate */
384 	NULL, /* set_filtering */
385 	NULL, /* get_video_output_size */
386 	NULL, /* get_video_output_prev */
387 	NULL, /* get_video_output_next */
388 	NULL, /* get_current_framebuffer */
389 	NULL, /* get_proc_address */
390 	NULL, /* set_aspect_ratio */
391 	NULL, /* apply_state_changes */
392 	switch_set_texture_frame,
393 	switch_set_texture_enable,
394 	NULL, /* set_osd_msg */
395 	NULL, /* show_mouse */
396 	NULL, /* grab_mouse_toggle */
397 	NULL, /* get_current_shader */
398 	NULL, /* get_current_software_framebuffer */
399 	NULL, /* get_hw_render_interface */
400 };
401 
switch_get_poke_interface(void * data,const video_poke_interface_t ** iface)402 static void switch_get_poke_interface(void *data,
403       const video_poke_interface_t **iface)
404 {
405    (void) data;
406    *iface = &switch_poke_interface;
407 }
408 
409 video_driver_t video_switch = {
410 	switch_init,
411 	switch_frame,
412 	switch_set_nonblock_state,
413 	switch_alive,
414 	switch_focus,
415 	switch_suppress_screensaver,
416 	switch_has_windowed,
417 	switch_set_shader,
418 	switch_free,
419 	"switch",
420 	NULL, /* set_viewport */
421 	switch_set_rotation,
422 	switch_viewport_info,
423 	NULL, /* read_viewport  */
424 	NULL, /* read_frame_raw */
425 #ifdef HAVE_OVERLAY
426 	NULL, /* overlay_interface */
427 #endif
428 #ifdef HAVE_VIDEO_LAYOUT
429   NULL,
430 #endif
431 	switch_get_poke_interface,
432 };
433 
434 /* vim: set ts=3 sw=3 */
435