1 /*  RetroArch - A frontend for libretro.
2  *  Copyright (C) 2016-2017 - Hans-Kristian Arntzen
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 <compat/strl.h>
17 #include <string/stdstring.h>
18 #include <retro_timers.h>
19 
20 #ifdef HAVE_CONFIG_H
21 #include "../../config.h"
22 #endif
23 
24 #include "../../frontend/frontend_driver.h"
25 #include "../common/vulkan_common.h"
26 #include "../../verbosity.h"
27 #include "../../configuration.h"
28 
29 typedef struct
30 {
31    gfx_ctx_vulkan_data_t vk;
32    int swap_interval;
33    unsigned width;
34    unsigned height;
35 } khr_display_ctx_data_t;
36 
gfx_ctx_khr_display_destroy(void * data)37 static void gfx_ctx_khr_display_destroy(void *data)
38 {
39    khr_display_ctx_data_t *khr = (khr_display_ctx_data_t*)data;
40    if (!khr)
41       return;
42 
43    vulkan_context_destroy(&khr->vk, true);
44 #ifdef HAVE_THREADS
45    if (khr->vk.context.queue_lock)
46       slock_free(khr->vk.context.queue_lock);
47 #endif
48    free(khr);
49 }
50 
gfx_ctx_khr_display_get_video_size(void * data,unsigned * width,unsigned * height)51 static void gfx_ctx_khr_display_get_video_size(void *data,
52       unsigned *width, unsigned *height)
53 {
54    khr_display_ctx_data_t *khr = (khr_display_ctx_data_t*)data;
55 
56    *width  = khr->width;
57    *height = khr->height;
58 }
59 
gfx_ctx_khr_display_init(void * video_driver)60 static void *gfx_ctx_khr_display_init(void *video_driver)
61 {
62    khr_display_ctx_data_t *khr = (khr_display_ctx_data_t*)
63       calloc(1, sizeof(*khr));
64    if (!khr)
65        return NULL;
66 
67    if (!vulkan_context_init(&khr->vk, VULKAN_WSI_DISPLAY))
68    {
69       RARCH_ERR("[Vulkan]: Failed to create Vulkan context.\n");
70       goto error;
71    }
72 
73    frontend_driver_install_signal_handler();
74 
75    return khr;
76 
77 error:
78    gfx_ctx_khr_display_destroy(khr);
79    return NULL;
80 }
81 
gfx_ctx_khr_display_check_window(void * data,bool * quit,bool * resize,unsigned * width,unsigned * height)82 static void gfx_ctx_khr_display_check_window(void *data, bool *quit,
83       bool *resize, unsigned *width, unsigned *height)
84 {
85    khr_display_ctx_data_t *khr = (khr_display_ctx_data_t*)data;
86 
87    *resize = khr->vk.need_new_swapchain;
88 
89    if (khr->width != *width || khr->height != *height)
90    {
91       *width  = khr->width;
92       *height = khr->height;
93       *resize = true;
94    }
95 
96    if ((bool)frontend_driver_get_signal_handler_state())
97       *quit = true;
98 }
99 
gfx_ctx_khr_display_set_resize(void * data,unsigned width,unsigned height)100 static bool gfx_ctx_khr_display_set_resize(void *data,
101       unsigned width, unsigned height)
102 {
103    khr_display_ctx_data_t *khr = (khr_display_ctx_data_t*)data;
104 
105    khr->width                  = width;
106    khr->height                 = height;
107 
108    if (!vulkan_create_swapchain(&khr->vk, khr->width, khr->height,
109             khr->swap_interval))
110    {
111       RARCH_ERR("[Vulkan]: Failed to update swapchain.\n");
112       return false;
113    }
114 
115    if (khr->vk.created_new_swapchain)
116       vulkan_acquire_next_image(&khr->vk);
117 
118    khr->vk.context.invalid_swapchain = true;
119    khr->vk.need_new_swapchain        = false;
120    return true;
121 }
122 
gfx_ctx_khr_display_set_video_mode(void * data,unsigned width,unsigned height,bool fullscreen)123 static bool gfx_ctx_khr_display_set_video_mode(void *data,
124       unsigned width, unsigned height,
125       bool fullscreen)
126 {
127    struct vulkan_display_surface_info info;
128    khr_display_ctx_data_t *khr    = (khr_display_ctx_data_t*)data;
129    settings_t *settings           = config_get_ptr();
130    unsigned video_monitor_index   = settings->uints.video_monitor_index;
131 
132    if (!fullscreen)
133    {
134       width                       = 0;
135       height                      = 0;
136    }
137 
138    info.width                     = width;
139    info.height                    = height;
140    info.monitor_index             = video_monitor_index;
141 
142    if (!vulkan_surface_create(&khr->vk, VULKAN_WSI_DISPLAY, &info, NULL,
143             0, 0, khr->swap_interval))
144    {
145       RARCH_ERR("[Vulkan]: Failed to create KHR_display surface.\n");
146       goto error;
147    }
148 
149    khr->width                     = khr->vk.context.swapchain_width;
150    khr->height                    = khr->vk.context.swapchain_height;
151 
152    return true;
153 
154 error:
155    gfx_ctx_khr_display_destroy(data);
156    return false;
157 }
158 
gfx_ctx_khr_display_input_driver(void * data,const char * joypad_name,input_driver_t ** input,void ** input_data)159 static void gfx_ctx_khr_display_input_driver(void *data,
160       const char *joypad_name,
161       input_driver_t **input, void **input_data)
162 {
163 #ifdef HAVE_X11
164    settings_t *settings = config_get_ptr();
165 
166    /* We cannot use the X11 input driver for DRM/KMS */
167    if (string_is_equal(settings->arrays.input_driver, "x"))
168    {
169 #ifdef HAVE_UDEV
170       {
171          /* Try to set it to udev instead */
172          void *udev = input_driver_init_wrap(&input_udev, joypad_name);
173          if (udev)
174          {
175             *input       = &input_udev;
176             *input_data  = udev;
177             return;
178          }
179       }
180 #endif
181 #if defined(__linux__) && !defined(ANDROID)
182       {
183          /* Try to set it to linuxraw instead */
184          void *linuxraw = input_driver_init_wrap(&input_linuxraw, joypad_name);
185          if (linuxraw)
186          {
187             *input       = &input_linuxraw;
188             *input_data  = linuxraw;
189             return;
190          }
191       }
192 #endif
193    }
194 #endif
195 
196    *input      = NULL;
197    *input_data = NULL;
198 }
199 
gfx_ctx_khr_display_get_api(void * data)200 static enum gfx_ctx_api gfx_ctx_khr_display_get_api(void *data)
201 {
202    return GFX_CTX_VULKAN_API;
203 }
204 
gfx_ctx_khr_display_bind_api(void * data,enum gfx_ctx_api api,unsigned major,unsigned minor)205 static bool gfx_ctx_khr_display_bind_api(void *data,
206       enum gfx_ctx_api api, unsigned major, unsigned minor)
207 {
208    if (api == GFX_CTX_VULKAN_API)
209       return true;
210    return false;
211 }
212 
gfx_ctx_khr_display_set_flags(void * data,uint32_t flags)213 static void gfx_ctx_khr_display_set_flags(void *data, uint32_t flags) { }
gfx_ctx_khr_display_has_focus(void * data)214 static bool gfx_ctx_khr_display_has_focus(void *data) { return true; }
gfx_ctx_khr_display_suppress_screensaver(void * data,bool enable)215 static bool gfx_ctx_khr_display_suppress_screensaver(void *data, bool enable) { return false; }
gfx_ctx_khr_display_get_proc_address(const char * symbol)216 static gfx_ctx_proc_t gfx_ctx_khr_display_get_proc_address(const char *symbol) { return NULL; }
217 
gfx_ctx_khr_display_set_swap_interval(void * data,int swap_interval)218 static void gfx_ctx_khr_display_set_swap_interval(void *data,
219       int swap_interval)
220 {
221    khr_display_ctx_data_t *khr = (khr_display_ctx_data_t*)data;
222 
223    if (khr->swap_interval != swap_interval)
224    {
225       khr->swap_interval = swap_interval;
226       if (khr->vk.swapchain)
227          khr->vk.need_new_swapchain = true;
228    }
229 }
230 
gfx_ctx_khr_display_swap_buffers(void * data)231 static void gfx_ctx_khr_display_swap_buffers(void *data)
232 {
233    khr_display_ctx_data_t *khr = (khr_display_ctx_data_t*)data;
234    if (khr->vk.context.has_acquired_swapchain)
235    {
236       khr->vk.context.has_acquired_swapchain = false;
237       if (khr->vk.swapchain == VK_NULL_HANDLE)
238       {
239          retro_sleep(10);
240       }
241       else
242          vulkan_present(&khr->vk, khr->vk.context.current_swapchain_index);
243    }
244    vulkan_acquire_next_image(&khr->vk);
245 }
246 
gfx_ctx_khr_display_get_flags(void * data)247 static uint32_t gfx_ctx_khr_display_get_flags(void *data)
248 {
249    uint32_t flags = 0;
250 
251 #if defined(HAVE_SLANG) && defined(HAVE_SPIRV_CROSS)
252    BIT32_SET(flags, GFX_CTX_FLAGS_SHADERS_SLANG);
253 #endif
254 
255    return flags;
256 }
257 
gfx_ctx_khr_display_get_context_data(void * data)258 static void *gfx_ctx_khr_display_get_context_data(void *data)
259 {
260    khr_display_ctx_data_t *khr = (khr_display_ctx_data_t*)data;
261    return &khr->vk.context;
262 }
263 
264 const gfx_ctx_driver_t gfx_ctx_khr_display = {
265    gfx_ctx_khr_display_init,
266    gfx_ctx_khr_display_destroy,
267    gfx_ctx_khr_display_get_api,
268    gfx_ctx_khr_display_bind_api,
269    gfx_ctx_khr_display_set_swap_interval,
270    gfx_ctx_khr_display_set_video_mode,
271    gfx_ctx_khr_display_get_video_size,
272    NULL,                                        /* get_refresh_rate */
273    NULL,                                        /* get_video_output_size */
274    NULL,                                        /* get_video_output_prev */
275    NULL,                                        /* get_video_output_next */
276    NULL,                                        /* get_metrics */
277    NULL,
278    NULL,                                        /* update_title */
279    gfx_ctx_khr_display_check_window,
280    gfx_ctx_khr_display_set_resize,
281    gfx_ctx_khr_display_has_focus,
282    gfx_ctx_khr_display_suppress_screensaver,
283    false,                                       /* has_windowed */
284    gfx_ctx_khr_display_swap_buffers,
285    gfx_ctx_khr_display_input_driver,
286    gfx_ctx_khr_display_get_proc_address,
287    NULL,
288    NULL,
289    NULL,
290    "khr_display",
291    gfx_ctx_khr_display_get_flags,
292    gfx_ctx_khr_display_set_flags,
293    NULL,
294    gfx_ctx_khr_display_get_context_data,
295    NULL                                         /* make_current */
296 };
297