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