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