1 | /* GDK - The GIMP Drawing Kit |
2 | * |
3 | * gdkvulkancontext.c: Vulkan wrappers |
4 | * |
5 | * Copyright © 2016 Benjamin Otte |
6 | * |
7 | * This library is free software; you can redistribute it and/or |
8 | * modify it under the terms of the GNU Library General Public |
9 | * License as published by the Free Software Foundation; either |
10 | * version 2 of the License, or (at your option) any later version. |
11 | * |
12 | * This library is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Library General Public License for more details. |
16 | * |
17 | * You should have received a copy of the GNU Library General Public |
18 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
19 | */ |
20 | |
21 | #include "config.h" |
22 | |
23 | #include "gdkvulkancontext.h" |
24 | |
25 | #include "gdkvulkancontextprivate.h" |
26 | |
27 | #include "gdkdisplayprivate.h" |
28 | #include "gdkintl.h" |
29 | |
30 | /** |
31 | * GdkVulkanContext: |
32 | * |
33 | * `GdkVulkanContext` is an object representing the platform-specific |
34 | * Vulkan draw context. |
35 | * |
36 | * `GdkVulkanContext`s are created for a surface using |
37 | * [method@Gdk.Surface.create_vulkan_context], and the context will match |
38 | * the characteristics of the surface. |
39 | * |
40 | * Support for `GdkVulkanContext` is platform-specific and context creation |
41 | * can fail, returning %NULL context. |
42 | */ |
43 | |
44 | typedef struct _GdkVulkanContextPrivate GdkVulkanContextPrivate; |
45 | |
46 | struct _GdkVulkanContextPrivate { |
47 | #ifdef GDK_RENDERING_VULKAN |
48 | VkSurfaceKHR surface; |
49 | VkSurfaceFormatKHR image_format; |
50 | |
51 | VkSwapchainKHR swapchain; |
52 | VkSemaphore draw_semaphore; |
53 | |
54 | guint n_images; |
55 | VkImage *images; |
56 | cairo_region_t **regions; |
57 | |
58 | gboolean has_present_region; |
59 | |
60 | #endif |
61 | |
62 | guint32 draw_index; |
63 | |
64 | guint vulkan_ref: 1; |
65 | }; |
66 | |
67 | enum { |
68 | IMAGES_UPDATED, |
69 | |
70 | LAST_SIGNAL |
71 | }; |
72 | |
73 | G_DEFINE_QUARK (gdk-vulkan-error-quark, gdk_vulkan_error) |
74 | |
75 | static guint signals[LAST_SIGNAL] = { 0 }; |
76 | |
77 | static void gdk_vulkan_context_initable_init (GInitableIface *iface); |
78 | |
79 | G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GdkVulkanContext, gdk_vulkan_context, GDK_TYPE_DRAW_CONTEXT, |
80 | G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, gdk_vulkan_context_initable_init) |
81 | G_ADD_PRIVATE (GdkVulkanContext)) |
82 | |
83 | #ifdef GDK_RENDERING_VULKAN |
84 | |
85 | const char * |
86 | gdk_vulkan_strerror (VkResult result) |
87 | { |
88 | /* If your compiler brought you here with a warning about missing |
89 | * enumeration values, you're running a newer Vulkan version than |
90 | * the GTK developers (or you are a GTK developer) and have |
91 | * encountered a newly added Vulkan error message. |
92 | * You want to add it to this enum now. |
93 | * |
94 | * Because the Vulkan people don't make adding this too easy, here's |
95 | * the process to manage it: |
96 | * 1. go to |
97 | * https://github.com/KhronosGroup/Vulkan-Headers/blob/master/include/vulkan/vulkan_core.h |
98 | * 2. Find the line where this enum value was added. |
99 | * 3. Click the commit that added this line. |
100 | * 4. The commit you're looking at now should also change |
101 | * VK_HEADER_VERSION, find that number. |
102 | * 5. Use that number in the #ifdef when adding the enum value to |
103 | * this enum. |
104 | * 6. For the error message, look at the specification (the one |
105 | * that includes all extensions) at |
106 | * https://www.khronos.org/registry/vulkan/specs/1.0-extensions/html/vkspec.html#VkResult |
107 | * 7. If this value has not been added to the specification yet, |
108 | * search for the error message in the text of specification. |
109 | * Often it will have a description that can be used as an error |
110 | * message. |
111 | * 8. If that didn't lead to one (or you are lazy), just use the |
112 | * literal string of the enum value as the error message. A |
113 | * GTK developer will add the correct one once it's added to the |
114 | * specification. |
115 | */ |
116 | switch (result) |
117 | { |
118 | case VK_SUCCESS: |
119 | return "Command successfully completed. (VK_SUCCESS)" ; |
120 | case VK_NOT_READY: |
121 | return "A fence or query has not yet completed. (VK_NOT_READY)" ; |
122 | case VK_TIMEOUT: |
123 | return "A wait operation has not completed in the specified time. (VK_TIMEOUT)" ; |
124 | case VK_EVENT_SET: |
125 | return "An event is signaled. (VK_EVENT_SET)" ; |
126 | case VK_EVENT_RESET: |
127 | return "An event is unsignaled. (VK_EVENT_RESET)" ; |
128 | case VK_INCOMPLETE: |
129 | return "A return array was too small for the result. (VK_INCOMPLETE)" ; |
130 | case VK_SUBOPTIMAL_KHR: |
131 | return "A swapchain no longer matches the surface properties exactly, but can still be used to present to the surface successfully. (VK_SUBOPTIMAL_KHR)" ; |
132 | case VK_ERROR_OUT_OF_HOST_MEMORY: |
133 | return "A host memory allocation has failed. (VK_ERROR_OUT_OF_HOST_MEMORY)" ; |
134 | case VK_ERROR_OUT_OF_DEVICE_MEMORY: |
135 | return "A device memory allocation has failed. (VK_ERROR_OUT_OF_DEVICE_MEMORY)" ; |
136 | case VK_ERROR_INITIALIZATION_FAILED: |
137 | return "Initialization of an object could not be completed for implementation-specific reasons. (VK_ERROR_INITIALIZATION_FAILED)" ; |
138 | case VK_ERROR_DEVICE_LOST: |
139 | return "The logical or physical device has been lost. (VK_ERROR_DEVICE_LOST)" ; |
140 | case VK_ERROR_MEMORY_MAP_FAILED: |
141 | return "Mapping of a memory object has failed. (VK_ERROR_MEMORY_MAP_FAILED)" ; |
142 | case VK_ERROR_LAYER_NOT_PRESENT: |
143 | return "A requested layer is not present or could not be loaded. (VK_ERROR_LAYER_NOT_PRESENT)" ; |
144 | case VK_ERROR_EXTENSION_NOT_PRESENT: |
145 | return "A requested extension is not supported. (VK_ERROR_EXTENSION_NOT_PRESENT)" ; |
146 | case VK_ERROR_FEATURE_NOT_PRESENT: |
147 | return "A requested feature is not supported. (VK_ERROR_FEATURE_NOT_PRESENT)" ; |
148 | case VK_ERROR_INCOMPATIBLE_DRIVER: |
149 | return "The requested version of Vulkan is not supported by the driver or is otherwise incompatible for implementation-specific reasons. (VK_ERROR_INCOMPATIBLE_DRIVER)" ; |
150 | case VK_ERROR_TOO_MANY_OBJECTS: |
151 | return "Too many objects of the type have already been created. (VK_ERROR_TOO_MANY_OBJECTS)" ; |
152 | case VK_ERROR_FORMAT_NOT_SUPPORTED: |
153 | return "A requested format is not supported on this device. (VK_ERROR_FORMAT_NOT_SUPPORTED)" ; |
154 | #if VK_HEADER_VERSION >= 24 |
155 | case VK_ERROR_FRAGMENTED_POOL: |
156 | return "A requested pool allocation has failed due to fragmentation of the pool’s memory. (VK_ERROR_FRAGMENTED_POOL)" ; |
157 | #endif |
158 | case VK_ERROR_SURFACE_LOST_KHR: |
159 | return "A surface is no longer available. (VK_ERROR_SURFACE_LOST_KHR)" ; |
160 | case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR: |
161 | return "The requested window is already in use by Vulkan or another API in a manner which prevents it from being used again. (VK_ERROR_NATIVE_WINDOW_IN_USE_KHR)" ; |
162 | case VK_ERROR_OUT_OF_DATE_KHR: |
163 | return "A surface has changed in such a way that it is no longer compatible with the swapchain. (VK_ERROR_OUT_OF_DATE_KHR)" ; |
164 | case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR: |
165 | return "The display used by a swapchain does not use the same presentable image layout, or is incompatible in a way that prevents sharing an image. (VK_ERROR_INCOMPATIBLE_DISPLAY_KHR)" ; |
166 | case VK_ERROR_VALIDATION_FAILED_EXT: |
167 | return "The application caused the validation layer to fail. (VK_ERROR_VALIDATION_FAILED_EXT)" ; |
168 | case VK_ERROR_INVALID_SHADER_NV: |
169 | return "One or more shaders failed to compile or link. (VK_ERROR_INVALID_SHADER_NV)" ; |
170 | #if VK_HEADER_VERSION >= 39 |
171 | case VK_ERROR_OUT_OF_POOL_MEMORY_KHR: |
172 | return "A pool memory allocation has failed. (VK_ERROR_OUT_OF_POOL_MEMORY_KHR)" ; |
173 | #endif |
174 | #if VK_HEADER_VERSION >= 54 |
175 | case VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR: |
176 | return "An external handle is not a valid handle of the specified type. (VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR)" ; |
177 | #endif |
178 | #if VK_HEADER_VERSION >= 64 |
179 | case VK_ERROR_NOT_PERMITTED_EXT: |
180 | return "The caller does not have sufficient privileges. (VK_ERROR_NOT_PERMITTED_EXT)" ; |
181 | #endif |
182 | #if VK_HEADER_VERSION >= 72 |
183 | case VK_ERROR_FRAGMENTATION_EXT: |
184 | return "A descriptor pool creation has failed due to fragmentation. (VK_ERROR_FRAGMENTATION_EXT)" ; |
185 | #endif |
186 | #if VK_HEADER_VERSION >= 89 |
187 | case VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT: |
188 | return "Invalid DRM format modifier plane layout (VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT)" ; |
189 | #endif |
190 | #if VK_HEADER_VERSION >= 97 |
191 | case VK_ERROR_INVALID_DEVICE_ADDRESS_EXT: |
192 | return "Invalid device address (VK_ERROR_INVALID_DEVICE_ADDRESS_EXT)" ; |
193 | #endif |
194 | #if VK_HEADER_VERSION >= 105 |
195 | case VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT: |
196 | return "An operation on a swapchain created with VK_FULL_SCREEN_EXCLUSIVE_APPLICATION_CONTROLLED_EXT failed as it did not have exclusive full-screen access. (VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT)" ; |
197 | #endif |
198 | #if VK_HEADER_VERSION >= 131 |
199 | case VK_ERROR_UNKNOWN: |
200 | return "An unknown error has occurred; either the application has provided invalid input, or an implementation failure has occurred. (VK_ERROR_UNKNOWN)" ; |
201 | #endif |
202 | #if VK_HEADER_VERSION >= 135 |
203 | #if VK_HEADER_VERSION < 162 |
204 | case VK_ERROR_INCOMPATIBLE_VERSION_KHR: |
205 | return "This error was removed by the Vulkan gods. (VK_ERROR_INCOMPATIBLE_VERSION_KHR)" ; |
206 | #endif |
207 | case VK_THREAD_IDLE_KHR: |
208 | return "A deferred operation is not complete but there is currently no work for this thread to do at the time of this call. (VK_THREAD_IDLE_KHR)" ; |
209 | case VK_THREAD_DONE_KHR: |
210 | return "A deferred operation is not complete but there is no work remaining to assign to additional threads. (VK_THREAD_DONE_KHR)" ; |
211 | case VK_OPERATION_DEFERRED_KHR: |
212 | return "A deferred operation was requested and at least some of the work was deferred. (VK_OPERATION_DEFERRED_KHR)" ; |
213 | case VK_OPERATION_NOT_DEFERRED_KHR: |
214 | return "A deferred operation was requested and no operations were deferred. (VK_OPERATION_NOT_DEFERRED_KHR)" ; |
215 | case VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT: |
216 | return "A requested pipeline creation would have required compilation, but the application requested compilation to not be performed. (VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT)" ; |
217 | #endif |
218 | #if VK_HEADER_VERSION < 140 |
219 | case VK_RESULT_RANGE_SIZE: |
220 | #endif |
221 | case VK_RESULT_MAX_ENUM: |
222 | default: |
223 | return "Unknown Vulkan error." ; |
224 | } |
225 | } |
226 | |
227 | static void |
228 | gdk_vulkan_context_dispose (GObject *gobject) |
229 | { |
230 | GdkVulkanContext *context = GDK_VULKAN_CONTEXT (gobject); |
231 | GdkVulkanContextPrivate *priv = gdk_vulkan_context_get_instance_private (context); |
232 | GdkDisplay *display; |
233 | VkDevice device; |
234 | guint i; |
235 | |
236 | for (i = 0; i < priv->n_images; i++) |
237 | { |
238 | cairo_region_destroy (priv->regions[i]); |
239 | } |
240 | g_clear_pointer (&priv->regions, g_free); |
241 | g_clear_pointer (&priv->images, g_free); |
242 | priv->n_images = 0; |
243 | |
244 | device = gdk_vulkan_context_get_device (context); |
245 | |
246 | if (priv->draw_semaphore != VK_NULL_HANDLE) |
247 | { |
248 | vkDestroySemaphore (device, |
249 | priv->draw_semaphore, |
250 | NULL); |
251 | priv->draw_semaphore = VK_NULL_HANDLE; |
252 | } |
253 | |
254 | if (priv->swapchain != VK_NULL_HANDLE) |
255 | { |
256 | vkDestroySwapchainKHR (device, |
257 | priv->swapchain, |
258 | NULL); |
259 | priv->swapchain = VK_NULL_HANDLE; |
260 | } |
261 | |
262 | if (priv->surface != VK_NULL_HANDLE) |
263 | { |
264 | vkDestroySurfaceKHR (gdk_vulkan_context_get_instance (context), |
265 | priv->surface, |
266 | NULL); |
267 | priv->surface = VK_NULL_HANDLE; |
268 | } |
269 | |
270 | /* display will be unset in gdk_draw_context_dispose() */ |
271 | display = gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context)); |
272 | if (display && priv->vulkan_ref) |
273 | gdk_display_unref_vulkan (display); |
274 | |
275 | G_OBJECT_CLASS (gdk_vulkan_context_parent_class)->dispose (gobject); |
276 | } |
277 | |
278 | static gboolean |
279 | gdk_vulkan_context_check_swapchain (GdkVulkanContext *context, |
280 | GError **error) |
281 | { |
282 | GdkVulkanContextPrivate *priv = gdk_vulkan_context_get_instance_private (context); |
283 | GdkSurface *surface = gdk_draw_context_get_surface (GDK_DRAW_CONTEXT (context)); |
284 | VkSurfaceCapabilitiesKHR capabilities; |
285 | VkCompositeAlphaFlagBitsKHR composite_alpha; |
286 | VkSwapchainKHR new_swapchain; |
287 | VkResult res; |
288 | VkDevice device; |
289 | guint i; |
290 | |
291 | device = gdk_vulkan_context_get_device (context); |
292 | |
293 | res = GDK_VK_CHECK (vkGetPhysicalDeviceSurfaceCapabilitiesKHR, gdk_vulkan_context_get_physical_device (context), |
294 | priv->surface, |
295 | &capabilities); |
296 | if (res != VK_SUCCESS) |
297 | { |
298 | g_set_error (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_NOT_AVAILABLE, |
299 | "Could not query surface capabilities: %s" , gdk_vulkan_strerror (res)); |
300 | return FALSE; |
301 | } |
302 | |
303 | if (capabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR) |
304 | composite_alpha = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR; |
305 | else if (capabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR) |
306 | { |
307 | /* let's hope the backend knows what it's doing */ |
308 | composite_alpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR; |
309 | } |
310 | else |
311 | { |
312 | GDK_DISPLAY_NOTE (gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context)), |
313 | VULKAN, g_warning ("Vulkan swapchain doesn't do transparency. Using opaque swapchain instead." )); |
314 | composite_alpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; |
315 | } |
316 | |
317 | /* |
318 | * Per https://www.khronos.org/registry/vulkan/specs/1.0-wsi_extensions/xhtml/vkspec.html#VkSurfaceCapabilitiesKHR |
319 | * the current extent may assume a special value, meaning that the extent should assume whatever |
320 | * value the surface has. |
321 | */ |
322 | if (capabilities.currentExtent.width == -1 || capabilities.currentExtent.height == -1) |
323 | { |
324 | capabilities.currentExtent.width = MAX (1, gdk_surface_get_width (surface) * gdk_surface_get_scale_factor (surface)); |
325 | capabilities.currentExtent.height = MAX (1, gdk_surface_get_height (surface) * gdk_surface_get_scale_factor (surface)); |
326 | } |
327 | |
328 | res = GDK_VK_CHECK (vkCreateSwapchainKHR, device, |
329 | &(VkSwapchainCreateInfoKHR) { |
330 | .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, |
331 | .pNext = NULL, |
332 | .flags = 0, |
333 | .surface = priv->surface, |
334 | .minImageCount = CLAMP (4, |
335 | capabilities.minImageCount, |
336 | capabilities.maxImageCount ? capabilities.maxImageCount : G_MAXUINT32), |
337 | .imageFormat = priv->image_format.format, |
338 | .imageColorSpace = priv->image_format.colorSpace, |
339 | .imageExtent = capabilities.currentExtent, |
340 | .imageArrayLayers = 1, |
341 | .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, |
342 | .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, |
343 | .queueFamilyIndexCount = 1, |
344 | .pQueueFamilyIndices = (uint32_t[1]) { |
345 | gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context))->vk_queue_family_index |
346 | }, |
347 | .preTransform = capabilities.currentTransform, |
348 | .compositeAlpha = composite_alpha, |
349 | .presentMode = VK_PRESENT_MODE_FIFO_KHR, |
350 | .clipped = VK_FALSE, |
351 | .oldSwapchain = priv->swapchain |
352 | }, |
353 | NULL, |
354 | &new_swapchain); |
355 | |
356 | if (priv->swapchain != VK_NULL_HANDLE) |
357 | { |
358 | vkDestroySwapchainKHR (device, |
359 | priv->swapchain, |
360 | NULL); |
361 | for (i = 0; i < priv->n_images; i++) |
362 | { |
363 | cairo_region_destroy (priv->regions[i]); |
364 | } |
365 | g_clear_pointer (&priv->regions, g_free); |
366 | g_clear_pointer (&priv->images, g_free); |
367 | priv->n_images = 0; |
368 | } |
369 | |
370 | if (res == VK_SUCCESS) |
371 | { |
372 | priv->swapchain = new_swapchain; |
373 | |
374 | GDK_VK_CHECK (vkGetSwapchainImagesKHR, device, |
375 | priv->swapchain, |
376 | &priv->n_images, |
377 | NULL); |
378 | priv->images = g_new (VkImage, priv->n_images); |
379 | GDK_VK_CHECK (vkGetSwapchainImagesKHR, device, |
380 | priv->swapchain, |
381 | &priv->n_images, |
382 | priv->images); |
383 | priv->regions = g_new (cairo_region_t *, priv->n_images); |
384 | for (i = 0; i < priv->n_images; i++) |
385 | { |
386 | priv->regions[i] = cairo_region_create_rectangle (&(cairo_rectangle_int_t) { |
387 | 0, 0, |
388 | gdk_surface_get_width (surface), |
389 | gdk_surface_get_height (surface), |
390 | }); |
391 | } |
392 | } |
393 | else |
394 | { |
395 | g_set_error (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_NOT_AVAILABLE, |
396 | "Could not create swapchain for this surface: %s" , gdk_vulkan_strerror (res)); |
397 | priv->swapchain = VK_NULL_HANDLE; |
398 | return FALSE; |
399 | } |
400 | |
401 | g_signal_emit (context, signals[IMAGES_UPDATED], 0); |
402 | |
403 | return res == VK_SUCCESS; |
404 | } |
405 | |
406 | static gboolean |
407 | device_supports_incremental_present (VkPhysicalDevice device) |
408 | { |
409 | VkExtensionProperties *extensions; |
410 | uint32_t n_device_extensions; |
411 | |
412 | vkEnumerateDeviceExtensionProperties (device, NULL, &n_device_extensions, NULL); |
413 | |
414 | extensions = g_newa (VkExtensionProperties, n_device_extensions); |
415 | vkEnumerateDeviceExtensionProperties (device, NULL, &n_device_extensions, extensions); |
416 | |
417 | for (uint32_t i = 0; i < n_device_extensions; i++) |
418 | { |
419 | if (g_str_equal (extensions[i].extensionName, VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME)) |
420 | return TRUE; |
421 | } |
422 | |
423 | return FALSE; |
424 | } |
425 | |
426 | static void |
427 | gdk_vulkan_context_begin_frame (GdkDrawContext *draw_context, |
428 | gboolean prefers_high_depth, |
429 | cairo_region_t *region) |
430 | { |
431 | GdkVulkanContext *context = GDK_VULKAN_CONTEXT (draw_context); |
432 | GdkVulkanContextPrivate *priv = gdk_vulkan_context_get_instance_private (context); |
433 | guint i; |
434 | |
435 | for (i = 0; i < priv->n_images; i++) |
436 | { |
437 | cairo_region_union (priv->regions[i], region); |
438 | } |
439 | |
440 | GDK_VK_CHECK (vkAcquireNextImageKHR, gdk_vulkan_context_get_device (context), |
441 | priv->swapchain, |
442 | UINT64_MAX, |
443 | priv->draw_semaphore, |
444 | VK_NULL_HANDLE, |
445 | &priv->draw_index); |
446 | |
447 | cairo_region_union (region, priv->regions[priv->draw_index]); |
448 | } |
449 | |
450 | static void |
451 | gdk_vulkan_context_end_frame (GdkDrawContext *draw_context, |
452 | cairo_region_t *painted) |
453 | { |
454 | GdkVulkanContext *context = GDK_VULKAN_CONTEXT (draw_context); |
455 | GdkVulkanContextPrivate *priv = gdk_vulkan_context_get_instance_private (context); |
456 | GdkSurface *surface = gdk_draw_context_get_surface (draw_context); |
457 | VkPresentRegionsKHR *regionsptr = VK_NULL_HANDLE; |
458 | VkPresentRegionsKHR regions; |
459 | cairo_rectangle_int_t extents; |
460 | int scale; |
461 | |
462 | cairo_region_get_extents (painted, &extents); |
463 | scale = gdk_surface_get_scale_factor (surface); |
464 | |
465 | regions = (VkPresentRegionsKHR) { |
466 | .sType = VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR, |
467 | .swapchainCount = 1, |
468 | .pRegions = &(VkPresentRegionKHR) { |
469 | .rectangleCount = 1, |
470 | .pRectangles = &(VkRectLayerKHR) { |
471 | .layer = 0, |
472 | .offset.x = extents.x * scale, |
473 | .offset.y = extents.y * scale, |
474 | .extent.width = extents.width * scale, |
475 | .extent.height = extents.height * scale, |
476 | } |
477 | }, |
478 | }; |
479 | |
480 | if (priv->has_present_region) |
481 | regionsptr = ®ions; |
482 | |
483 | GDK_VK_CHECK (vkQueuePresentKHR, gdk_vulkan_context_get_queue (context), |
484 | &(VkPresentInfoKHR) { |
485 | .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, |
486 | .waitSemaphoreCount = 1, |
487 | .pWaitSemaphores = (VkSemaphore[]) { |
488 | priv->draw_semaphore |
489 | }, |
490 | .swapchainCount = 1, |
491 | .pSwapchains = (VkSwapchainKHR[]) { |
492 | priv->swapchain |
493 | }, |
494 | .pImageIndices = (uint32_t[]) { |
495 | priv->draw_index |
496 | }, |
497 | .pNext = regionsptr, |
498 | }); |
499 | |
500 | cairo_region_destroy (priv->regions[priv->draw_index]); |
501 | priv->regions[priv->draw_index] = cairo_region_create (); |
502 | } |
503 | |
504 | static void |
505 | gdk_vulkan_context_surface_resized (GdkDrawContext *draw_context) |
506 | { |
507 | GdkVulkanContext *context = GDK_VULKAN_CONTEXT (draw_context); |
508 | GError *error = NULL; |
509 | |
510 | if (!gdk_vulkan_context_check_swapchain (context, &error)) |
511 | { |
512 | g_warning ("%s" , error->message); |
513 | g_error_free (error); |
514 | return; |
515 | } |
516 | } |
517 | |
518 | static void |
519 | gdk_vulkan_context_class_init (GdkVulkanContextClass *klass) |
520 | { |
521 | GObjectClass *gobject_class = G_OBJECT_CLASS (klass); |
522 | GdkDrawContextClass *draw_context_class = GDK_DRAW_CONTEXT_CLASS (klass); |
523 | |
524 | gobject_class->dispose = gdk_vulkan_context_dispose; |
525 | |
526 | draw_context_class->begin_frame = gdk_vulkan_context_begin_frame; |
527 | draw_context_class->end_frame = gdk_vulkan_context_end_frame; |
528 | draw_context_class->surface_resized = gdk_vulkan_context_surface_resized; |
529 | |
530 | /** |
531 | * GdkVulkanContext::images-updated: |
532 | * @context: the object on which the signal is emitted |
533 | * |
534 | * Emitted when the images managed by this context have changed. |
535 | * |
536 | * Usually this means that the swapchain had to be recreated, |
537 | * for example in response to a change of the surface size. |
538 | */ |
539 | signals[IMAGES_UPDATED] = |
540 | g_signal_new (g_intern_static_string ("images-updated" ), |
541 | G_OBJECT_CLASS_TYPE (gobject_class), |
542 | G_SIGNAL_RUN_LAST, |
543 | 0, |
544 | NULL, NULL, |
545 | NULL, |
546 | G_TYPE_NONE, 0); |
547 | } |
548 | |
549 | static void |
550 | gdk_vulkan_context_init (GdkVulkanContext *self) |
551 | { |
552 | } |
553 | |
554 | static gboolean |
555 | gdk_vulkan_context_real_init (GInitable *initable, |
556 | GCancellable *cancellable, |
557 | GError **error) |
558 | { |
559 | GdkVulkanContext *context = GDK_VULKAN_CONTEXT (initable); |
560 | GdkVulkanContextPrivate *priv = gdk_vulkan_context_get_instance_private (context); |
561 | GdkDisplay *display = gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context)); |
562 | VkResult res; |
563 | VkBool32 supported; |
564 | uint32_t i; |
565 | |
566 | priv->vulkan_ref = gdk_display_ref_vulkan (display, error); |
567 | if (!priv->vulkan_ref) |
568 | return FALSE; |
569 | |
570 | res = GDK_VULKAN_CONTEXT_GET_CLASS (context)->create_surface (context, &priv->surface); |
571 | if (res != VK_SUCCESS) |
572 | { |
573 | g_set_error (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_NOT_AVAILABLE, |
574 | "Could not create surface for this surface: %s" , gdk_vulkan_strerror (res)); |
575 | return FALSE; |
576 | } |
577 | |
578 | res = GDK_VK_CHECK (vkGetPhysicalDeviceSurfaceSupportKHR, gdk_vulkan_context_get_physical_device (context), |
579 | gdk_vulkan_context_get_queue_family_index (context), |
580 | priv->surface, |
581 | &supported); |
582 | if (res != VK_SUCCESS) |
583 | { |
584 | g_set_error (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_NOT_AVAILABLE, |
585 | "Could not check if queue family supports this surface: %s" , gdk_vulkan_strerror (res)); |
586 | } |
587 | else if (!supported) |
588 | { |
589 | g_set_error (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_NOT_AVAILABLE, |
590 | "FIXME: Queue family does not support surface. Write code to try different queue family." ); |
591 | } |
592 | else |
593 | { |
594 | uint32_t n_formats; |
595 | GDK_VK_CHECK (vkGetPhysicalDeviceSurfaceFormatsKHR, gdk_vulkan_context_get_physical_device (context), |
596 | priv->surface, |
597 | &n_formats, NULL); |
598 | VkSurfaceFormatKHR *formats = g_newa (VkSurfaceFormatKHR, n_formats); |
599 | GDK_VK_CHECK (vkGetPhysicalDeviceSurfaceFormatsKHR, gdk_vulkan_context_get_physical_device (context), |
600 | priv->surface, |
601 | &n_formats, formats); |
602 | for (i = 0; i < n_formats; i++) |
603 | { |
604 | if (formats[i].format == VK_FORMAT_B8G8R8A8_UNORM) |
605 | break; |
606 | } |
607 | if (i == n_formats) |
608 | { |
609 | g_set_error_literal (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_NOT_AVAILABLE, |
610 | "No supported image format found." ); |
611 | goto out_surface; |
612 | } |
613 | priv->image_format = formats[i]; |
614 | priv->has_present_region = device_supports_incremental_present (display->vk_physical_device); |
615 | |
616 | if (!gdk_vulkan_context_check_swapchain (context, error)) |
617 | goto out_surface; |
618 | |
619 | GDK_VK_CHECK (vkCreateSemaphore, gdk_vulkan_context_get_device (context), |
620 | &(VkSemaphoreCreateInfo) { |
621 | .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, |
622 | }, |
623 | NULL, |
624 | &priv->draw_semaphore); |
625 | |
626 | return TRUE; |
627 | } |
628 | |
629 | out_surface: |
630 | vkDestroySurfaceKHR (gdk_vulkan_context_get_instance (context), |
631 | priv->surface, |
632 | NULL); |
633 | priv->surface = VK_NULL_HANDLE; |
634 | return FALSE; |
635 | } |
636 | |
637 | static void |
638 | gdk_vulkan_context_initable_init (GInitableIface *iface) |
639 | { |
640 | iface->init = gdk_vulkan_context_real_init; |
641 | } |
642 | |
643 | /** |
644 | * gdk_vulkan_context_get_instance: |
645 | * @context: a `GdkVulkanContext` |
646 | * |
647 | * Gets the Vulkan instance that is associated with @context. |
648 | * |
649 | * Returns: (transfer none): the VkInstance |
650 | */ |
651 | VkInstance |
652 | gdk_vulkan_context_get_instance (GdkVulkanContext *context) |
653 | { |
654 | g_return_val_if_fail (GDK_IS_VULKAN_CONTEXT (context), NULL); |
655 | |
656 | return gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context))->vk_instance; |
657 | } |
658 | |
659 | /** |
660 | * gdk_vulkan_context_get_physical_device: |
661 | * @context: a `GdkVulkanContext` |
662 | * |
663 | * Gets the Vulkan physical device that this context is using. |
664 | * |
665 | * Returns: (transfer none): the VkPhysicalDevice |
666 | */ |
667 | VkPhysicalDevice |
668 | gdk_vulkan_context_get_physical_device (GdkVulkanContext *context) |
669 | { |
670 | g_return_val_if_fail (GDK_IS_VULKAN_CONTEXT (context), NULL); |
671 | |
672 | return gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context))->vk_physical_device; |
673 | } |
674 | |
675 | /** |
676 | * gdk_vulkan_context_get_device: |
677 | * @context: a `GdkVulkanContext` |
678 | * |
679 | * Gets the Vulkan device that this context is using. |
680 | * |
681 | * Returns: (transfer none): the VkDevice |
682 | */ |
683 | VkDevice |
684 | gdk_vulkan_context_get_device (GdkVulkanContext *context) |
685 | { |
686 | g_return_val_if_fail (GDK_IS_VULKAN_CONTEXT (context), NULL); |
687 | |
688 | return gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context))->vk_device; |
689 | } |
690 | |
691 | /** |
692 | * gdk_vulkan_context_get_queue: |
693 | * @context: a `GdkVulkanContext` |
694 | * |
695 | * Gets the Vulkan queue that this context is using. |
696 | * |
697 | * Returns: (transfer none): the VkQueue |
698 | */ |
699 | VkQueue |
700 | gdk_vulkan_context_get_queue (GdkVulkanContext *context) |
701 | { |
702 | g_return_val_if_fail (GDK_IS_VULKAN_CONTEXT (context), NULL); |
703 | |
704 | return gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context))->vk_queue; |
705 | } |
706 | |
707 | /** |
708 | * gdk_vulkan_context_get_queue_family_index: |
709 | * @context: a `GdkVulkanContext` |
710 | * |
711 | * Gets the family index for the queue that this context is using. |
712 | * |
713 | * See vkGetPhysicalDeviceQueueFamilyProperties(). |
714 | * |
715 | * Returns: the index |
716 | */ |
717 | uint32_t |
718 | gdk_vulkan_context_get_queue_family_index (GdkVulkanContext *context) |
719 | { |
720 | g_return_val_if_fail (GDK_IS_VULKAN_CONTEXT (context), 0); |
721 | |
722 | return gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context))->vk_queue_family_index; |
723 | } |
724 | |
725 | /** |
726 | * gdk_vulkan_context_get_image_format: |
727 | * @context: a `GdkVulkanContext` |
728 | * |
729 | * Gets the image format that this context is using. |
730 | * |
731 | * Returns: (transfer none): the VkFormat |
732 | */ |
733 | VkFormat |
734 | gdk_vulkan_context_get_image_format (GdkVulkanContext *context) |
735 | { |
736 | GdkVulkanContextPrivate *priv = gdk_vulkan_context_get_instance_private (context); |
737 | |
738 | g_return_val_if_fail (GDK_IS_VULKAN_CONTEXT (context), VK_FORMAT_UNDEFINED); |
739 | |
740 | return priv->image_format.format; |
741 | } |
742 | |
743 | /** |
744 | * gdk_vulkan_context_get_n_images: |
745 | * @context: a `GdkVulkanContext` |
746 | * |
747 | * Gets the number of images that this context is using in its swap chain. |
748 | * |
749 | * Returns: the number of images |
750 | */ |
751 | uint32_t |
752 | gdk_vulkan_context_get_n_images (GdkVulkanContext *context) |
753 | { |
754 | GdkVulkanContextPrivate *priv = gdk_vulkan_context_get_instance_private (context); |
755 | |
756 | g_return_val_if_fail (GDK_IS_VULKAN_CONTEXT (context), 0); |
757 | |
758 | return priv->n_images; |
759 | } |
760 | |
761 | /** |
762 | * gdk_vulkan_context_get_image: |
763 | * @context: a `GdkVulkanContext` |
764 | * @id: the index of the image to return |
765 | * |
766 | * Gets the image with index @id that this context is using. |
767 | * |
768 | * Returns: (transfer none): the VkImage |
769 | */ |
770 | VkImage |
771 | gdk_vulkan_context_get_image (GdkVulkanContext *context, |
772 | guint id) |
773 | { |
774 | GdkVulkanContextPrivate *priv = gdk_vulkan_context_get_instance_private (context); |
775 | |
776 | g_return_val_if_fail (GDK_IS_VULKAN_CONTEXT (context), VK_NULL_HANDLE); |
777 | g_return_val_if_fail (id < priv->n_images, VK_NULL_HANDLE); |
778 | |
779 | return priv->images[id]; |
780 | } |
781 | |
782 | /** |
783 | * gdk_vulkan_context_get_draw_index: |
784 | * @context: a `GdkVulkanContext` |
785 | * |
786 | * Gets the index of the image that is currently being drawn. |
787 | * |
788 | * This function can only be used between [method@Gdk.DrawContext.begin_frame] |
789 | * and [method@Gdk.DrawContext.end_frame] calls. |
790 | * |
791 | * Returns: the index of the images that is being drawn |
792 | */ |
793 | uint32_t |
794 | gdk_vulkan_context_get_draw_index (GdkVulkanContext *context) |
795 | { |
796 | GdkVulkanContextPrivate *priv = gdk_vulkan_context_get_instance_private (context); |
797 | |
798 | g_return_val_if_fail (GDK_IS_VULKAN_CONTEXT (context), 0); |
799 | g_return_val_if_fail (gdk_draw_context_is_in_frame (GDK_DRAW_CONTEXT (context)), 0); |
800 | |
801 | return priv->draw_index; |
802 | } |
803 | |
804 | /** |
805 | * gdk_vulkan_context_get_draw_semaphore: |
806 | * @context: a `GdkVulkanContext` |
807 | * |
808 | * Gets the Vulkan semaphore that protects access to the image that is |
809 | * currently being drawn. |
810 | * |
811 | * This function can only be used between [method@Gdk.DrawContext.begin_frame] |
812 | * and [method@Gdk.DrawContext.end_frame] calls. |
813 | * |
814 | * Returns: (transfer none): the VkSemaphore |
815 | */ |
816 | VkSemaphore |
817 | gdk_vulkan_context_get_draw_semaphore (GdkVulkanContext *context) |
818 | { |
819 | GdkVulkanContextPrivate *priv = gdk_vulkan_context_get_instance_private (context); |
820 | |
821 | g_return_val_if_fail (GDK_IS_VULKAN_CONTEXT (context), VK_NULL_HANDLE); |
822 | g_return_val_if_fail (gdk_draw_context_is_in_frame (GDK_DRAW_CONTEXT (context)), VK_NULL_HANDLE); |
823 | |
824 | return priv->draw_semaphore; |
825 | } |
826 | |
827 | static gboolean |
828 | gdk_display_create_vulkan_device (GdkDisplay *display, |
829 | GError **error) |
830 | { |
831 | uint32_t i, j, k; |
832 | const char *override; |
833 | gboolean list_devices; |
834 | int first, last; |
835 | |
836 | uint32_t n_devices = 0; |
837 | GDK_VK_CHECK(vkEnumeratePhysicalDevices, display->vk_instance, &n_devices, NULL); |
838 | VkPhysicalDevice *devices; |
839 | |
840 | if (n_devices == 0) |
841 | { |
842 | /* Give a different error for 0 devices so people know their drivers suck. */ |
843 | g_set_error_literal (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_NOT_AVAILABLE, |
844 | "No Vulkan devices available." ); |
845 | return FALSE; |
846 | } |
847 | |
848 | devices = g_newa (VkPhysicalDevice, n_devices); |
849 | GDK_VK_CHECK(vkEnumeratePhysicalDevices, display->vk_instance, &n_devices, devices); |
850 | |
851 | first = 0; |
852 | last = n_devices; |
853 | |
854 | override = g_getenv ("GDK_VULKAN_DEVICE" ); |
855 | list_devices = FALSE; |
856 | if (override) |
857 | { |
858 | if (g_strcmp0 (override, "list" ) == 0) |
859 | list_devices = TRUE; |
860 | else |
861 | { |
862 | gint64 device_idx; |
863 | GError *error2 = NULL; |
864 | |
865 | if (!g_ascii_string_to_signed (override, 10, 0, G_MAXINT, &device_idx, &error2)) |
866 | { |
867 | g_warning ("Failed to parse %s: %s" , "GDK_VULKAN_DEVICE" , error2->message); |
868 | g_error_free (error2); |
869 | device_idx = -1; |
870 | } |
871 | |
872 | if (device_idx < 0 || device_idx >= n_devices) |
873 | g_warning ("%s value out of range, ignoring" , "GDK_VULKAN_DEVICE" ); |
874 | else |
875 | { |
876 | first = device_idx; |
877 | last = first + 1; |
878 | } |
879 | } |
880 | } |
881 | |
882 | if (list_devices || GDK_DISPLAY_DEBUG_CHECK (display, VULKAN)) |
883 | { |
884 | for (i = 0; i < n_devices; i++) |
885 | { |
886 | VkPhysicalDeviceProperties props; |
887 | VkQueueFamilyProperties *queue_props; |
888 | uint32_t n_queue_props; |
889 | const char *device_type[] = { |
890 | "Other" , "Integrated GPU" , "Discrete GPU" , "Virtual GPU" , "CPU" |
891 | }; |
892 | struct { |
893 | int bit; |
894 | const char *name; |
895 | } queue_caps[] = { |
896 | { VK_QUEUE_GRAPHICS_BIT, "graphics" }, |
897 | { VK_QUEUE_COMPUTE_BIT, "compute" }, |
898 | { VK_QUEUE_TRANSFER_BIT, "transfer" }, |
899 | { VK_QUEUE_SPARSE_BINDING_BIT, "sparse binding" } |
900 | }; |
901 | |
902 | vkGetPhysicalDeviceProperties (devices[i], &props); |
903 | vkGetPhysicalDeviceQueueFamilyProperties (devices[i], &n_queue_props, NULL); |
904 | queue_props = g_newa (VkQueueFamilyProperties, n_queue_props); |
905 | vkGetPhysicalDeviceQueueFamilyProperties (devices[i], &n_queue_props, queue_props); |
906 | |
907 | g_print ("Vulkan Device %u:\n" , i); |
908 | g_print (" %s (%s)\n" , props.deviceName, device_type[props.deviceType]); |
909 | g_print (" Vendor ID: 0x%Xu\n" , props.vendorID); |
910 | g_print (" Device ID: 0x%Xu\n" , props.deviceID); |
911 | g_print (" API version %u.%u.%u\n" , |
912 | VK_VERSION_MAJOR (props.apiVersion), |
913 | VK_VERSION_MINOR (props.apiVersion), |
914 | VK_VERSION_PATCH (props.apiVersion)); |
915 | for (j = 0; j < n_queue_props; j++) |
916 | { |
917 | const char *sep = "" ; |
918 | |
919 | g_print (" Queue %d: " , j); |
920 | for (k = 0; k < G_N_ELEMENTS (queue_caps); k++) |
921 | { |
922 | if (queue_props[j].queueFlags & queue_caps[k].bit) |
923 | { |
924 | g_print ("%s%s" , sep, queue_caps[k].name); |
925 | sep = "/" ; |
926 | } |
927 | } |
928 | g_print ("\n" ); |
929 | } |
930 | } |
931 | } |
932 | |
933 | for (i = first; i < last; i++) |
934 | { |
935 | uint32_t n_queue_props; |
936 | vkGetPhysicalDeviceQueueFamilyProperties (devices[i], &n_queue_props, NULL); |
937 | VkQueueFamilyProperties *queue_props = g_newa (VkQueueFamilyProperties, n_queue_props); |
938 | vkGetPhysicalDeviceQueueFamilyProperties (devices[i], &n_queue_props, queue_props); |
939 | for (j = 0; j < n_queue_props; j++) |
940 | { |
941 | if (queue_props[j].queueFlags & VK_QUEUE_GRAPHICS_BIT) |
942 | { |
943 | GPtrArray *device_extensions; |
944 | gboolean has_incremental_present; |
945 | |
946 | has_incremental_present = device_supports_incremental_present (devices[i]); |
947 | |
948 | device_extensions = g_ptr_array_new (); |
949 | g_ptr_array_add (device_extensions, (gpointer) VK_KHR_SWAPCHAIN_EXTENSION_NAME); |
950 | if (has_incremental_present) |
951 | g_ptr_array_add (device_extensions, (gpointer) VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME); |
952 | |
953 | GDK_DISPLAY_NOTE (display, VULKAN, g_print ("Using Vulkan device %u, queue %u\n" , i, j)); |
954 | if (GDK_VK_CHECK (vkCreateDevice, devices[i], |
955 | &(VkDeviceCreateInfo) { |
956 | VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, |
957 | NULL, |
958 | 0, |
959 | 1, |
960 | &(VkDeviceQueueCreateInfo) { |
961 | .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, |
962 | .queueFamilyIndex = j, |
963 | .queueCount = 1, |
964 | .pQueuePriorities = (float []) { 1.0f }, |
965 | }, |
966 | 0, |
967 | NULL, |
968 | device_extensions->len, |
969 | (const char * const *) device_extensions->pdata |
970 | }, |
971 | NULL, |
972 | &display->vk_device) != VK_SUCCESS) |
973 | { |
974 | g_ptr_array_unref (device_extensions); |
975 | continue; |
976 | } |
977 | |
978 | g_ptr_array_unref (device_extensions); |
979 | |
980 | display->vk_physical_device = devices[i]; |
981 | vkGetDeviceQueue(display->vk_device, j, 0, &display->vk_queue); |
982 | display->vk_queue_family_index = j; |
983 | return TRUE; |
984 | } |
985 | } |
986 | } |
987 | |
988 | g_set_error_literal (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_NOT_AVAILABLE, |
989 | "Could not find a Vulkan device with the required features." ); |
990 | return FALSE; |
991 | } |
992 | |
993 | static VkBool32 VKAPI_CALL |
994 | gdk_vulkan_debug_report (VkDebugReportFlagsEXT flags, |
995 | VkDebugReportObjectTypeEXT objectType, |
996 | uint64_t object, |
997 | size_t location, |
998 | int32_t messageCode, |
999 | const char* pLayerPrefix, |
1000 | const char* pMessage, |
1001 | void* pUserData) |
1002 | { |
1003 | if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) |
1004 | g_critical ("Vulkan: %s: %s" , pLayerPrefix, pMessage); |
1005 | else if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT) |
1006 | g_critical ("Vulkan: %s: %s" , pLayerPrefix, pMessage); |
1007 | else if (flags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT) |
1008 | g_warning ("Vulkan: %s: %s" , pLayerPrefix, pMessage); |
1009 | else if (flags & VK_DEBUG_REPORT_DEBUG_BIT_EXT) |
1010 | g_debug ("Vulkan: %s: %s" , pLayerPrefix, pMessage); |
1011 | else |
1012 | g_info ("Vulkan: %s: %s" , pLayerPrefix, pMessage); |
1013 | |
1014 | return VK_FALSE; |
1015 | } |
1016 | |
1017 | static gboolean |
1018 | gdk_display_create_vulkan_instance (GdkDisplay *display, |
1019 | GError **error) |
1020 | { |
1021 | uint32_t i; |
1022 | GPtrArray *used_extensions; |
1023 | GPtrArray *used_layers; |
1024 | gboolean validate = FALSE, have_debug_report = FALSE; |
1025 | VkResult res; |
1026 | |
1027 | if (GDK_DISPLAY_GET_CLASS (display)->vk_extension_name == NULL) |
1028 | { |
1029 | g_set_error (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_UNSUPPORTED, |
1030 | "The %s backend has no Vulkan support." , G_OBJECT_TYPE_NAME (display)); |
1031 | return FALSE; |
1032 | } |
1033 | |
1034 | uint32_t n_extensions; |
1035 | GDK_VK_CHECK (vkEnumerateInstanceExtensionProperties, NULL, &n_extensions, NULL); |
1036 | VkExtensionProperties *extensions = g_newa (VkExtensionProperties, n_extensions); |
1037 | GDK_VK_CHECK (vkEnumerateInstanceExtensionProperties, NULL, &n_extensions, extensions); |
1038 | |
1039 | used_extensions = g_ptr_array_new (); |
1040 | g_ptr_array_add (used_extensions, (gpointer) VK_KHR_SURFACE_EXTENSION_NAME); |
1041 | g_ptr_array_add (used_extensions, (gpointer) GDK_DISPLAY_GET_CLASS (display)->vk_extension_name); |
1042 | |
1043 | for (i = 0; i < n_extensions; i++) |
1044 | { |
1045 | if (GDK_DISPLAY_DEBUG_CHECK (display, VULKAN)) |
1046 | g_print ("Extension available: %s v%u.%u.%u\n" , |
1047 | extensions[i].extensionName, |
1048 | VK_VERSION_MAJOR (extensions[i].specVersion), |
1049 | VK_VERSION_MINOR (extensions[i].specVersion), |
1050 | VK_VERSION_PATCH (extensions[i].specVersion)); |
1051 | |
1052 | if (g_str_equal (extensions[i].extensionName, VK_EXT_DEBUG_REPORT_EXTENSION_NAME)) |
1053 | { |
1054 | g_ptr_array_add (used_extensions, (gpointer) VK_EXT_DEBUG_REPORT_EXTENSION_NAME); |
1055 | have_debug_report = TRUE; |
1056 | } |
1057 | } |
1058 | |
1059 | uint32_t n_layers; |
1060 | GDK_VK_CHECK (vkEnumerateInstanceLayerProperties, &n_layers, NULL); |
1061 | VkLayerProperties *layers = g_newa (VkLayerProperties, n_layers); |
1062 | GDK_VK_CHECK (vkEnumerateInstanceLayerProperties, &n_layers, layers); |
1063 | |
1064 | used_layers = g_ptr_array_new (); |
1065 | |
1066 | for (i = 0; i < n_layers; i++) |
1067 | { |
1068 | if (GDK_DISPLAY_DEBUG_CHECK (display, VULKAN)) |
1069 | g_print ("Layer available: %s v%u.%u.%u (%s)\n" , |
1070 | layers[i].layerName, |
1071 | VK_VERSION_MAJOR (layers[i].specVersion), |
1072 | VK_VERSION_MINOR (layers[i].specVersion), |
1073 | VK_VERSION_PATCH (layers[i].specVersion), |
1074 | layers[i].description); |
1075 | if (GDK_DISPLAY_DEBUG_CHECK (display, VULKAN_VALIDATE) && |
1076 | g_str_equal (layers[i].layerName, "VK_LAYER_LUNARG_standard_validation" )) |
1077 | { |
1078 | g_ptr_array_add (used_layers, (gpointer) "VK_LAYER_LUNARG_standard_validation" ); |
1079 | validate = TRUE; |
1080 | } |
1081 | } |
1082 | |
1083 | if (GDK_DISPLAY_DEBUG_CHECK (display, VULKAN_VALIDATE) && !validate) |
1084 | { |
1085 | g_warning ("Vulkan validation layers were requested, but not found. Running without." ); |
1086 | } |
1087 | |
1088 | res = GDK_VK_CHECK (vkCreateInstance, &(VkInstanceCreateInfo) { |
1089 | .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, |
1090 | .pNext = NULL, |
1091 | .flags = 0, |
1092 | .pApplicationInfo = &(VkApplicationInfo) { |
1093 | .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, |
1094 | .pNext = NULL, |
1095 | .pApplicationName = g_get_application_name (), |
1096 | .applicationVersion = 0, |
1097 | .pEngineName = "GTK" , |
1098 | .engineVersion = VK_MAKE_VERSION (GDK_MAJOR_VERSION, GDK_MINOR_VERSION, GDK_MICRO_VERSION), |
1099 | .apiVersion = VK_API_VERSION_1_0 |
1100 | }, |
1101 | .enabledLayerCount = used_layers->len, |
1102 | .ppEnabledLayerNames = (const char * const *) used_layers->pdata, |
1103 | .enabledExtensionCount = used_extensions->len, |
1104 | .ppEnabledExtensionNames = (const char * const *) used_extensions->pdata |
1105 | }, |
1106 | NULL, |
1107 | &display->vk_instance); |
1108 | g_ptr_array_free (used_layers, TRUE); |
1109 | g_ptr_array_free (used_extensions, TRUE); |
1110 | |
1111 | if (res != VK_SUCCESS) |
1112 | { |
1113 | g_set_error (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_UNSUPPORTED, |
1114 | "Could not create a Vulkan instance: %s" , gdk_vulkan_strerror (res)); |
1115 | return FALSE; |
1116 | } |
1117 | |
1118 | if (have_debug_report) |
1119 | { |
1120 | PFN_vkCreateDebugReportCallbackEXT vkCreateDebugReportCallbackEXT; |
1121 | |
1122 | vkCreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT) vkGetInstanceProcAddr (display->vk_instance, "vkCreateDebugReportCallbackEXT" ); |
1123 | GDK_VK_CHECK (vkCreateDebugReportCallbackEXT, display->vk_instance, |
1124 | &(VkDebugReportCallbackCreateInfoEXT) { |
1125 | .sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT, |
1126 | .pNext = NULL, |
1127 | .flags = VK_DEBUG_REPORT_INFORMATION_BIT_EXT |
1128 | | VK_DEBUG_REPORT_WARNING_BIT_EXT |
1129 | | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT |
1130 | | VK_DEBUG_REPORT_ERROR_BIT_EXT |
1131 | | VK_DEBUG_REPORT_DEBUG_BIT_EXT, |
1132 | .pfnCallback = gdk_vulkan_debug_report, |
1133 | .pUserData = NULL |
1134 | }, |
1135 | NULL, |
1136 | &display->vk_debug_callback); |
1137 | } |
1138 | |
1139 | if (!gdk_display_create_vulkan_device (display, error)) |
1140 | { |
1141 | if (display->vk_debug_callback != VK_NULL_HANDLE) |
1142 | { |
1143 | PFN_vkDestroyDebugReportCallbackEXT vkDestroyDebugReportCallbackEXT; |
1144 | |
1145 | vkDestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT) vkGetInstanceProcAddr (display->vk_instance, "vkDestroyDebugReportCallbackEXT" ); |
1146 | vkDestroyDebugReportCallbackEXT (display->vk_instance, |
1147 | display->vk_debug_callback, |
1148 | NULL); |
1149 | display->vk_debug_callback = VK_NULL_HANDLE; |
1150 | } |
1151 | vkDestroyInstance (display->vk_instance, NULL); |
1152 | display->vk_instance = VK_NULL_HANDLE; |
1153 | return FALSE; |
1154 | } |
1155 | |
1156 | return TRUE; |
1157 | } |
1158 | |
1159 | gboolean |
1160 | gdk_display_ref_vulkan (GdkDisplay *display, |
1161 | GError **error) |
1162 | { |
1163 | if (display->vulkan_refcount == 0) |
1164 | { |
1165 | if (!gdk_display_create_vulkan_instance (display, error)) |
1166 | return FALSE; |
1167 | } |
1168 | |
1169 | display->vulkan_refcount++; |
1170 | |
1171 | return TRUE; |
1172 | } |
1173 | |
1174 | void |
1175 | gdk_display_unref_vulkan (GdkDisplay *display) |
1176 | { |
1177 | g_return_if_fail (GDK_IS_DISPLAY (display)); |
1178 | g_return_if_fail (display->vulkan_refcount > 0); |
1179 | |
1180 | display->vulkan_refcount--; |
1181 | if (display->vulkan_refcount > 0) |
1182 | return; |
1183 | |
1184 | vkDestroyDevice (display->vk_device, NULL); |
1185 | display->vk_device = VK_NULL_HANDLE; |
1186 | if (display->vk_debug_callback != VK_NULL_HANDLE) |
1187 | { |
1188 | PFN_vkDestroyDebugReportCallbackEXT vkDestroyDebugReportCallbackEXT; |
1189 | |
1190 | vkDestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT) vkGetInstanceProcAddr (display->vk_instance, "vkDestroyDebugReportCallbackEXT" ); |
1191 | vkDestroyDebugReportCallbackEXT (display->vk_instance, |
1192 | display->vk_debug_callback, |
1193 | NULL); |
1194 | display->vk_debug_callback = VK_NULL_HANDLE; |
1195 | } |
1196 | vkDestroyInstance (display->vk_instance, NULL); |
1197 | display->vk_instance = VK_NULL_HANDLE; |
1198 | } |
1199 | |
1200 | #else /* GDK_RENDERING_VULKAN */ |
1201 | |
1202 | static void |
1203 | gdk_vulkan_context_class_init (GdkVulkanContextClass *klass) |
1204 | { |
1205 | signals[IMAGES_UPDATED] = |
1206 | g_signal_new (signal_name: g_intern_static_string (string: "images-updated" ), |
1207 | G_OBJECT_CLASS_TYPE (klass), |
1208 | signal_flags: G_SIGNAL_RUN_LAST, |
1209 | class_offset: 0, |
1210 | NULL, NULL, |
1211 | NULL, |
1212 | G_TYPE_NONE, n_params: 0); |
1213 | } |
1214 | |
1215 | static void |
1216 | gdk_vulkan_context_init (GdkVulkanContext *self) |
1217 | { |
1218 | } |
1219 | |
1220 | static void |
1221 | gdk_vulkan_context_initable_init (GInitableIface *iface) |
1222 | { |
1223 | } |
1224 | |
1225 | #endif /* GDK_RENDERING_VULKAN */ |
1226 | |