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
44typedef struct _GdkVulkanContextPrivate GdkVulkanContextPrivate;
45
46struct _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
67enum {
68 IMAGES_UPDATED,
69
70 LAST_SIGNAL
71};
72
73G_DEFINE_QUARK (gdk-vulkan-error-quark, gdk_vulkan_error)
74
75static guint signals[LAST_SIGNAL] = { 0 };
76
77static void gdk_vulkan_context_initable_init (GInitableIface *iface);
78
79G_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
85const char *
86gdk_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
227static void
228gdk_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
278static gboolean
279gdk_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
406static gboolean
407device_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
426static void
427gdk_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
450static void
451gdk_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 = &regions;
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
504static void
505gdk_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
518static void
519gdk_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
549static void
550gdk_vulkan_context_init (GdkVulkanContext *self)
551{
552}
553
554static gboolean
555gdk_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
629out_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
637static void
638gdk_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 */
651VkInstance
652gdk_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 */
667VkPhysicalDevice
668gdk_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 */
683VkDevice
684gdk_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 */
699VkQueue
700gdk_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 */
717uint32_t
718gdk_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 */
733VkFormat
734gdk_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 */
751uint32_t
752gdk_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 */
770VkImage
771gdk_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 */
793uint32_t
794gdk_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 */
816VkSemaphore
817gdk_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
827static gboolean
828gdk_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
993static VkBool32 VKAPI_CALL
994gdk_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
1017static gboolean
1018gdk_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
1159gboolean
1160gdk_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
1174void
1175gdk_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
1202static void
1203gdk_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
1215static void
1216gdk_vulkan_context_init (GdkVulkanContext *self)
1217{
1218}
1219
1220static void
1221gdk_vulkan_context_initable_init (GInitableIface *iface)
1222{
1223}
1224
1225#endif /* GDK_RENDERING_VULKAN */
1226

source code of gtk/gdk/gdkvulkancontext.c