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 "gdkinternals.h"
29 #include "gdkintl.h"
30
31 /**
32 * GdkVulkanContext:
33 *
34 * `GdkVulkanContext` is an object representing the platform-specific
35 * Vulkan draw context.
36 *
37 * `GdkVulkanContext`s are created for a surface using
38 * [method@Gdk.Surface.create_vulkan_context], and the context will match
39 * the the characteristics of the surface.
40 *
41 * Support for `GdkVulkanContext` is platform-specific and context creation
42 * can fail, returning %NULL context.
43 */
44
45 typedef struct _GdkVulkanContextPrivate GdkVulkanContextPrivate;
46
47 struct _GdkVulkanContextPrivate {
48 #ifdef GDK_RENDERING_VULKAN
49 VkSurfaceKHR surface;
50 VkSurfaceFormatKHR image_format;
51
52 VkSwapchainKHR swapchain;
53 VkSemaphore draw_semaphore;
54
55 guint n_images;
56 VkImage *images;
57 cairo_region_t **regions;
58
59 gboolean has_present_region;
60
61 #endif
62
63 guint32 draw_index;
64
65 guint vulkan_ref: 1;
66 };
67
68 enum {
69 IMAGES_UPDATED,
70
71 LAST_SIGNAL
72 };
73
74 G_DEFINE_QUARK (gdk-vulkan-error-quark, gdk_vulkan_error)
75
76 static guint signals[LAST_SIGNAL] = { 0 };
77
78 static void gdk_vulkan_context_initable_init (GInitableIface *iface);
79
G_DEFINE_ABSTRACT_TYPE_WITH_CODE(GdkVulkanContext,gdk_vulkan_context,GDK_TYPE_DRAW_CONTEXT,G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,gdk_vulkan_context_initable_init)G_ADD_PRIVATE (GdkVulkanContext))80 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GdkVulkanContext, gdk_vulkan_context, GDK_TYPE_DRAW_CONTEXT,
81 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, gdk_vulkan_context_initable_init)
82 G_ADD_PRIVATE (GdkVulkanContext))
83
84 #ifdef GDK_RENDERING_VULKAN
85
86 const char *
87 gdk_vulkan_strerror (VkResult result)
88 {
89 /* If your compiler brought you here with a warning about missing
90 * enumeration values, you're running a newer Vulkan version than
91 * the GTK developers (or you are a GTK developer) and have
92 * encountered a newly added Vulkan error message.
93 * You want to add it to this enum now.
94 *
95 * Because the Vulkan people don't make adding this too easy, here's
96 * the process to manage it:
97 * 1. go to
98 * https://github.com/KhronosGroup/Vulkan-Headers/blob/master/include/vulkan/vulkan_core.h
99 * 2. Find the line where this enum value was added.
100 * 3. Click the commit that added this line.
101 * 4. The commit you're looking at now should also change
102 * VK_HEADER_VERSION, find that number.
103 * 5. Use that number in the #ifdef when adding the enum value to
104 * this enum.
105 * 6. For the error message, look at the specification (the one
106 * that includes all extensions) at
107 * https://www.khronos.org/registry/vulkan/specs/1.0-extensions/html/vkspec.html#VkResult
108 * 7. If this value has not been added to the specification yet,
109 * search for the error message in the text of specification.
110 * Often it will have a description that can be used as an error
111 * message.
112 * 8. If that didn't lead to one (or you are lazy), just use the
113 * literal string of the enum value as the error message. A
114 * GTK developer will add the correct one once it's added to the
115 * specification.
116 */
117 switch (result)
118 {
119 case VK_SUCCESS:
120 return "Command successfully completed. (VK_SUCCESS)";
121 case VK_NOT_READY:
122 return "A fence or query has not yet completed. (VK_NOT_READY)";
123 case VK_TIMEOUT:
124 return "A wait operation has not completed in the specified time. (VK_TIMEOUT)";
125 case VK_EVENT_SET:
126 return "An event is signaled. (VK_EVENT_SET)";
127 case VK_EVENT_RESET:
128 return "An event is unsignaled. (VK_EVENT_RESET)";
129 case VK_INCOMPLETE:
130 return "A return array was too small for the result. (VK_INCOMPLETE)";
131 case VK_SUBOPTIMAL_KHR:
132 return "A swapchain no longer matches the surface properties exactly, but can still be used to present to the surface successfully. (VK_SUBOPTIMAL_KHR)";
133 case VK_ERROR_OUT_OF_HOST_MEMORY:
134 return "A host memory allocation has failed. (VK_ERROR_OUT_OF_HOST_MEMORY)";
135 case VK_ERROR_OUT_OF_DEVICE_MEMORY:
136 return "A device memory allocation has failed. (VK_ERROR_OUT_OF_DEVICE_MEMORY)";
137 case VK_ERROR_INITIALIZATION_FAILED:
138 return "Initialization of an object could not be completed for implementation-specific reasons. (VK_ERROR_INITIALIZATION_FAILED)";
139 case VK_ERROR_DEVICE_LOST:
140 return "The logical or physical device has been lost. (VK_ERROR_DEVICE_LOST)";
141 case VK_ERROR_MEMORY_MAP_FAILED:
142 return "Mapping of a memory object has failed. (VK_ERROR_MEMORY_MAP_FAILED)";
143 case VK_ERROR_LAYER_NOT_PRESENT:
144 return "A requested layer is not present or could not be loaded. (VK_ERROR_LAYER_NOT_PRESENT)";
145 case VK_ERROR_EXTENSION_NOT_PRESENT:
146 return "A requested extension is not supported. (VK_ERROR_EXTENSION_NOT_PRESENT)";
147 case VK_ERROR_FEATURE_NOT_PRESENT:
148 return "A requested feature is not supported. (VK_ERROR_FEATURE_NOT_PRESENT)";
149 case VK_ERROR_INCOMPATIBLE_DRIVER:
150 return "The requested version of Vulkan is not supported by the driver or is otherwise incompatible for implementation-specific reasons. (VK_ERROR_INCOMPATIBLE_DRIVER)";
151 case VK_ERROR_TOO_MANY_OBJECTS:
152 return "Too many objects of the type have already been created. (VK_ERROR_TOO_MANY_OBJECTS)";
153 case VK_ERROR_FORMAT_NOT_SUPPORTED:
154 return "A requested format is not supported on this device. (VK_ERROR_FORMAT_NOT_SUPPORTED)";
155 #if VK_HEADER_VERSION >= 24
156 case VK_ERROR_FRAGMENTED_POOL:
157 return "A requested pool allocation has failed due to fragmentation of the pool’s memory. (VK_ERROR_FRAGMENTED_POOL)";
158 #endif
159 case VK_ERROR_SURFACE_LOST_KHR:
160 return "A surface is no longer available. (VK_ERROR_SURFACE_LOST_KHR)";
161 case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR:
162 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)";
163 case VK_ERROR_OUT_OF_DATE_KHR:
164 return "A surface has changed in such a way that it is no longer compatible with the swapchain. (VK_ERROR_OUT_OF_DATE_KHR)";
165 case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR:
166 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)";
167 case VK_ERROR_VALIDATION_FAILED_EXT:
168 return "The application caused the validation layer to fail. (VK_ERROR_VALIDATION_FAILED_EXT)";
169 case VK_ERROR_INVALID_SHADER_NV:
170 return "One or more shaders failed to compile or link. (VK_ERROR_INVALID_SHADER_NV)";
171 #if VK_HEADER_VERSION >= 39
172 case VK_ERROR_OUT_OF_POOL_MEMORY_KHR:
173 return "A pool memory allocation has failed. (VK_ERROR_OUT_OF_POOL_MEMORY_KHR)";
174 #endif
175 #if VK_HEADER_VERSION >= 54
176 case VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR:
177 return "An external handle is not a valid handle of the specified type. (VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR)";
178 #endif
179 #if VK_HEADER_VERSION >= 64
180 case VK_ERROR_NOT_PERMITTED_EXT:
181 return "The caller does not have sufficient privileges. (VK_ERROR_NOT_PERMITTED_EXT)";
182 #endif
183 #if VK_HEADER_VERSION >= 72
184 case VK_ERROR_FRAGMENTATION_EXT:
185 return "A descriptor pool creation has failed due to fragmentation. (VK_ERROR_FRAGMENTATION_EXT)";
186 #endif
187 #if VK_HEADER_VERSION >= 89
188 case VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT:
189 return "Invalid DRM format modifier plane layout (VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT)";
190 #endif
191 #if VK_HEADER_VERSION >= 97
192 case VK_ERROR_INVALID_DEVICE_ADDRESS_EXT:
193 return "Invalid device address (VK_ERROR_INVALID_DEVICE_ADDRESS_EXT)";
194 #endif
195 #if VK_HEADER_VERSION >= 105
196 case VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT:
197 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)";
198 #endif
199 #if VK_HEADER_VERSION >= 131
200 case VK_ERROR_UNKNOWN:
201 return "An unknown error has occurred; either the application has provided invalid input, or an implementation failure has occurred. (VK_ERROR_UNKNOWN)";
202 #endif
203 #if VK_HEADER_VERSION >= 135
204 #if VK_HEADER_VERSION < 162
205 case VK_ERROR_INCOMPATIBLE_VERSION_KHR:
206 return "This error was removed by the Vulkan gods. (VK_ERROR_INCOMPATIBLE_VERSION_KHR)";
207 #endif
208 case VK_THREAD_IDLE_KHR:
209 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)";
210 case VK_THREAD_DONE_KHR:
211 return "A deferred operation is not complete but there is no work remaining to assign to additional threads. (VK_THREAD_DONE_KHR)";
212 case VK_OPERATION_DEFERRED_KHR:
213 return "A deferred operation was requested and at least some of the work was deferred. (VK_OPERATION_DEFERRED_KHR)";
214 case VK_OPERATION_NOT_DEFERRED_KHR:
215 return "A deferred operation was requested and no operations were deferred. (VK_OPERATION_NOT_DEFERRED_KHR)";
216 case VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT:
217 return "A requested pipeline creation would have required compilation, but the application requested compilation to not be performed. (VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT)";
218 #endif
219 #if VK_HEADER_VERSION < 140
220 case VK_RESULT_RANGE_SIZE:
221 #endif
222 case VK_RESULT_MAX_ENUM:
223 default:
224 return "Unknown Vulkan error.";
225 }
226 }
227
228 static void
gdk_vulkan_context_dispose(GObject * gobject)229 gdk_vulkan_context_dispose (GObject *gobject)
230 {
231 GdkVulkanContext *context = GDK_VULKAN_CONTEXT (gobject);
232 GdkVulkanContextPrivate *priv = gdk_vulkan_context_get_instance_private (context);
233 GdkDisplay *display;
234 VkDevice device;
235 guint i;
236
237 for (i = 0; i < priv->n_images; i++)
238 {
239 cairo_region_destroy (priv->regions[i]);
240 }
241 g_clear_pointer (&priv->regions, g_free);
242 g_clear_pointer (&priv->images, g_free);
243 priv->n_images = 0;
244
245 device = gdk_vulkan_context_get_device (context);
246
247 if (priv->draw_semaphore != VK_NULL_HANDLE)
248 {
249 vkDestroySemaphore (device,
250 priv->draw_semaphore,
251 NULL);
252 priv->draw_semaphore = VK_NULL_HANDLE;
253 }
254
255 if (priv->swapchain != VK_NULL_HANDLE)
256 {
257 vkDestroySwapchainKHR (device,
258 priv->swapchain,
259 NULL);
260 priv->swapchain = VK_NULL_HANDLE;
261 }
262
263 if (priv->surface != VK_NULL_HANDLE)
264 {
265 vkDestroySurfaceKHR (gdk_vulkan_context_get_instance (context),
266 priv->surface,
267 NULL);
268 priv->surface = VK_NULL_HANDLE;
269 }
270
271 /* display will be unset in gdk_draw_context_dispose() */
272 display = gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context));
273 if (display && priv->vulkan_ref)
274 gdk_display_unref_vulkan (display);
275
276 G_OBJECT_CLASS (gdk_vulkan_context_parent_class)->dispose (gobject);
277 }
278
279 static gboolean
gdk_vulkan_context_check_swapchain(GdkVulkanContext * context,GError ** error)280 gdk_vulkan_context_check_swapchain (GdkVulkanContext *context,
281 GError **error)
282 {
283 GdkVulkanContextPrivate *priv = gdk_vulkan_context_get_instance_private (context);
284 GdkSurface *surface = gdk_draw_context_get_surface (GDK_DRAW_CONTEXT (context));
285 VkSurfaceCapabilitiesKHR capabilities;
286 VkCompositeAlphaFlagBitsKHR composite_alpha;
287 VkSwapchainKHR new_swapchain;
288 VkResult res;
289 VkDevice device;
290 guint i;
291
292 device = gdk_vulkan_context_get_device (context);
293
294 res = GDK_VK_CHECK (vkGetPhysicalDeviceSurfaceCapabilitiesKHR, gdk_vulkan_context_get_physical_device (context),
295 priv->surface,
296 &capabilities);
297 if (res != VK_SUCCESS)
298 {
299 g_set_error (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_NOT_AVAILABLE,
300 "Could not query surface capabilities: %s", gdk_vulkan_strerror (res));
301 return FALSE;
302 }
303
304 if (capabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR)
305 composite_alpha = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;
306 else if (capabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR)
307 {
308 /* let's hope the backend knows what it's doing */
309 composite_alpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;
310 }
311 else
312 {
313 GDK_DISPLAY_NOTE (gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context)),
314 VULKAN, g_warning ("Vulkan swapchain doesn't do transparency. Using opaque swapchain instead."));
315 composite_alpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
316 }
317
318 /*
319 * Per https://www.khronos.org/registry/vulkan/specs/1.0-wsi_extensions/xhtml/vkspec.html#VkSurfaceCapabilitiesKHR
320 * the current extent may assume a special value, meaning that the extent should assume whatever
321 * value the surface has.
322 */
323 if (capabilities.currentExtent.width == -1 || capabilities.currentExtent.height == -1)
324 {
325 capabilities.currentExtent.width = MAX (1, gdk_surface_get_width (surface) * gdk_surface_get_scale_factor (surface));
326 capabilities.currentExtent.height = MAX (1, gdk_surface_get_height (surface) * gdk_surface_get_scale_factor (surface));
327 }
328
329 res = GDK_VK_CHECK (vkCreateSwapchainKHR, device,
330 &(VkSwapchainCreateInfoKHR) {
331 .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
332 .pNext = NULL,
333 .flags = 0,
334 .surface = priv->surface,
335 .minImageCount = CLAMP (4,
336 capabilities.minImageCount,
337 capabilities.maxImageCount ? capabilities.maxImageCount : G_MAXUINT32),
338 .imageFormat = priv->image_format.format,
339 .imageColorSpace = priv->image_format.colorSpace,
340 .imageExtent = capabilities.currentExtent,
341 .imageArrayLayers = 1,
342 .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
343 .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
344 .queueFamilyIndexCount = 1,
345 .pQueueFamilyIndices = (uint32_t[1]) {
346 gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context))->vk_queue_family_index
347 },
348 .preTransform = capabilities.currentTransform,
349 .compositeAlpha = composite_alpha,
350 .presentMode = VK_PRESENT_MODE_FIFO_KHR,
351 .clipped = VK_FALSE,
352 .oldSwapchain = priv->swapchain
353 },
354 NULL,
355 &new_swapchain);
356
357 if (priv->swapchain != VK_NULL_HANDLE)
358 {
359 vkDestroySwapchainKHR (device,
360 priv->swapchain,
361 NULL);
362 for (i = 0; i < priv->n_images; i++)
363 {
364 cairo_region_destroy (priv->regions[i]);
365 }
366 g_clear_pointer (&priv->regions, g_free);
367 g_clear_pointer (&priv->images, g_free);
368 priv->n_images = 0;
369 }
370
371 if (res == VK_SUCCESS)
372 {
373 priv->swapchain = new_swapchain;
374
375 GDK_VK_CHECK (vkGetSwapchainImagesKHR, device,
376 priv->swapchain,
377 &priv->n_images,
378 NULL);
379 priv->images = g_new (VkImage, priv->n_images);
380 GDK_VK_CHECK (vkGetSwapchainImagesKHR, device,
381 priv->swapchain,
382 &priv->n_images,
383 priv->images);
384 priv->regions = g_new (cairo_region_t *, priv->n_images);
385 for (i = 0; i < priv->n_images; i++)
386 {
387 priv->regions[i] = cairo_region_create_rectangle (&(cairo_rectangle_int_t) {
388 0, 0,
389 gdk_surface_get_width (surface),
390 gdk_surface_get_height (surface),
391 });
392 }
393 }
394 else
395 {
396 g_set_error (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_NOT_AVAILABLE,
397 "Could not create swapchain for this surface: %s", gdk_vulkan_strerror (res));
398 priv->swapchain = VK_NULL_HANDLE;
399 return FALSE;
400 }
401
402 g_signal_emit (context, signals[IMAGES_UPDATED], 0);
403
404 return res == VK_SUCCESS;
405 }
406
407 static gboolean
device_supports_incremental_present(VkPhysicalDevice device)408 device_supports_incremental_present (VkPhysicalDevice device)
409 {
410 VkExtensionProperties *extensions;
411 uint32_t n_device_extensions;
412
413 vkEnumerateDeviceExtensionProperties (device, NULL, &n_device_extensions, NULL);
414
415 extensions = g_newa (VkExtensionProperties, n_device_extensions);
416 vkEnumerateDeviceExtensionProperties (device, NULL, &n_device_extensions, extensions);
417
418 for (uint32_t i = 0; i < n_device_extensions; i++)
419 {
420 if (g_str_equal (extensions[i].extensionName, VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME))
421 return TRUE;
422 }
423
424 return FALSE;
425 }
426
427 static void
gdk_vulkan_context_begin_frame(GdkDrawContext * draw_context,cairo_region_t * region)428 gdk_vulkan_context_begin_frame (GdkDrawContext *draw_context,
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
gdk_vulkan_context_end_frame(GdkDrawContext * draw_context,cairo_region_t * painted)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
gdk_vulkan_context_surface_resized(GdkDrawContext * draw_context)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
gdk_vulkan_context_class_init(GdkVulkanContextClass * klass)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
gdk_vulkan_context_init(GdkVulkanContext * self)550 gdk_vulkan_context_init (GdkVulkanContext *self)
551 {
552 }
553
554 static gboolean
gdk_vulkan_context_real_init(GInitable * initable,GCancellable * cancellable,GError ** error)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
gdk_vulkan_context_initable_init(GInitableIface * iface)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
gdk_vulkan_context_get_instance(GdkVulkanContext * context)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
gdk_vulkan_context_get_physical_device(GdkVulkanContext * context)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
gdk_vulkan_context_get_device(GdkVulkanContext * context)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
gdk_vulkan_context_get_queue(GdkVulkanContext * context)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
gdk_vulkan_context_get_queue_family_index(GdkVulkanContext * context)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
gdk_vulkan_context_get_image_format(GdkVulkanContext * context)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
gdk_vulkan_context_get_n_images(GdkVulkanContext * context)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
gdk_vulkan_context_get_image(GdkVulkanContext * context,guint id)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
gdk_vulkan_context_get_draw_index(GdkVulkanContext * context)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
gdk_vulkan_context_get_draw_semaphore(GdkVulkanContext * context)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
gdk_display_create_vulkan_device(GdkDisplay * display,GError ** error)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
gdk_vulkan_debug_report(VkDebugReportFlagsEXT flags,VkDebugReportObjectTypeEXT objectType,uint64_t object,size_t location,int32_t messageCode,const char * pLayerPrefix,const char * pMessage,void * pUserData)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
gdk_display_create_vulkan_instance(GdkDisplay * display,GError ** error)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
gdk_display_ref_vulkan(GdkDisplay * display,GError ** error)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
gdk_display_unref_vulkan(GdkDisplay * display)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 (g_intern_static_string ("images-updated"),
1207 G_OBJECT_CLASS_TYPE (klass),
1208 G_SIGNAL_RUN_LAST,
1209 0,
1210 NULL, NULL,
1211 NULL,
1212 G_TYPE_NONE, 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