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 = &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 
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