gdkvulkancontext.c 42.8 KB
Newer Older
Benjamin Otte's avatar
Benjamin Otte committed
1 2
/* GDK - The GIMP Drawing Kit
 *
3
 * gdkvulkancontext.c: Vulkan wrappers
Benjamin Otte's avatar
Benjamin Otte committed
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *
 * Copyright © 2016  Benjamin Otte
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
 */

#include "config.h"

23
#include "gdkvulkancontext.h"
Benjamin Otte's avatar
Benjamin Otte committed
24

25
#include "gdkvulkancontextprivate.h"
Benjamin Otte's avatar
Benjamin Otte committed
26 27 28 29 30

#include "gdkdisplayprivate.h"
#include "gdkinternals.h"
#include "gdkintl.h"

31 32 33
/**
 * SECTION:gdkvulkancontext
 * @Title: GdkVulkanContext
34
 * @Short_description: Vulkan draw context
35 36
 *
 * #GdkVulkanContext is an object representing the platform-specific
37
 * Vulkan draw context.
38
 *
39 40
 * #GdkVulkanContexts are created for a #GdkSurface using
 * gdk_surface_create_vulkan_context(), and the context will match the
41
 * the characteristics of the surface.
42
 *
43
 * Support for #GdkVulkanContext is platform-specific, context creation
44 45 46
 * can fail, returning %NULL context.
 */

47 48 49 50 51 52 53
/**
 * GdkVulkanContext:
 *
 * The GdkVulkanContext struct contains only private fields and should not
 * be accessed directly.
 */

Benjamin Otte's avatar
Benjamin Otte committed
54 55 56
typedef struct _GdkVulkanContextPrivate GdkVulkanContextPrivate;

struct _GdkVulkanContextPrivate {
57
#ifdef GDK_RENDERING_VULKAN
58
  VkSurfaceKHR surface;
59 60 61
  VkSurfaceFormatKHR image_format;

  VkSwapchainKHR swapchain;
62
  VkSemaphore draw_semaphore;
63 64 65

  guint n_images;
  VkImage *images;
66
  cairo_region_t **regions;
67
#endif
68

69
  guint32 draw_index;
70 71

  guint vulkan_ref: 1;
72 73 74 75 76 77
};

enum {
  IMAGES_UPDATED,

  LAST_SIGNAL
Benjamin Otte's avatar
Benjamin Otte committed
78 79 80 81
};

G_DEFINE_QUARK (gdk-vulkan-error-quark, gdk_vulkan_error)

82 83
static guint signals[LAST_SIGNAL] = { 0 };

84 85
static void gdk_vulkan_context_initable_init (GInitableIface *iface);

86
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GdkVulkanContext, gdk_vulkan_context, GDK_TYPE_DRAW_CONTEXT,
87 88
                                  G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, gdk_vulkan_context_initable_init)
                                  G_ADD_PRIVATE (GdkVulkanContext))
Benjamin Otte's avatar
Benjamin Otte committed
89

90
#ifdef GDK_RENDERING_VULKAN
91 92 93 94

const char *
gdk_vulkan_strerror (VkResult result)
{
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
  /* If your compiler brought you here with a warning about missing
   * enumeration values, you're running a newer Vulkan version than
   * the GTK developers )or you are a GTK developer) and have
   * encountered a newly added Vulkan error message.
   * You want to add it to this enum now.
   *
   * Becuse the Vulkan people don't make adding this too easy, here's
   * the process to manage it:
   * 1. go to
   *    https://github.com/KhronosGroup/Vulkan-LoaderAndValidationLayers/blame/master/include/vulkan/vulkan.h
   * 2. Find the line where this enum value was added.
   * 3. Click the commit that added this line.
   * 4. The commit you're looking at now should also change
   *    VK_HEADER_VERSION, find that number.
   * 5. Use that number in the #ifdef when adding the enum value to
   *    this enum.
   * 6. For the error message, look at the specification (the one
   *    that includes all extensions) at
   *    https://www.khronos.org/registry/vulkan/specs/1.0-extensions/html/vkspec.html#VkResult
   * 7. If this value has not been added to the specification yet,
   *    search for the error message in the text of specification.
   *    Often it will have a description that can be used as an error
   *    message.
   * 8. If that didn't lead to one (or you are lazy), just use the
   *    literal string of the enum value as the error message. A
   *    GTK developer will add the correct one once it's added to the
   *    specification.
   */
  switch (result)
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
  {
    case VK_SUCCESS:
      return "Command successfully completed.";
    case VK_NOT_READY:
      return "A fence or query has not yet completed.";
    case VK_TIMEOUT:
      return "A wait operation has not completed in the specified time.";
    case VK_EVENT_SET:
      return "An event is signaled.";
    case VK_EVENT_RESET:
      return "An event is unsignaled.";
    case VK_INCOMPLETE:
      return "A return array was too small for the result.";
    case VK_SUBOPTIMAL_KHR:
      return "A swapchain no longer matches the surface properties exactly, but can still be used to present to the surface successfully.";
    case VK_ERROR_OUT_OF_HOST_MEMORY:
      return "A host memory allocation has failed.";
    case VK_ERROR_OUT_OF_DEVICE_MEMORY:
      return "A device memory allocation has failed.";
    case VK_ERROR_INITIALIZATION_FAILED:
      return "Initialization of an object could not be completed for implementation-specific reasons.";
    case VK_ERROR_DEVICE_LOST:
      return "The logical or physical device has been lost.";
    case VK_ERROR_MEMORY_MAP_FAILED:
      return "Mapping of a memory object has failed.";
    case VK_ERROR_LAYER_NOT_PRESENT:
      return "A requested layer is not present or could not be loaded.";
    case VK_ERROR_EXTENSION_NOT_PRESENT:
      return "A requested extension is not supported.";
    case VK_ERROR_FEATURE_NOT_PRESENT:
      return "A requested feature is not supported.";
    case VK_ERROR_INCOMPATIBLE_DRIVER:
      return "The requested version of Vulkan is not supported by the driver or is otherwise incompatible for implementation-specific reasons.";
    case VK_ERROR_TOO_MANY_OBJECTS:
      return "Too many objects of the type have already been created.";
    case VK_ERROR_FORMAT_NOT_SUPPORTED:
      return "A requested format is not supported on this device.";
161
#if VK_HEADER_VERSION >= 24
162 163
    case VK_ERROR_FRAGMENTED_POOL:
      return "A requested pool allocation has failed due to fragmentation of the pool’s memory.";
164
#endif
165 166 167 168 169 170 171 172
    case VK_ERROR_SURFACE_LOST_KHR:
      return "A surface is no longer available.";
    case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR:
      return "The requested window is already in use by Vulkan or another API in a manner which prevents it from being used again.";
    case VK_ERROR_OUT_OF_DATE_KHR:
      return "A surface has changed in such a way that it is no longer compatible with the swapchain.";
    case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR:
      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.";
173 174 175 176
    case VK_ERROR_VALIDATION_FAILED_EXT:
      return "The application caused the validation layer to fail.";
    case VK_ERROR_INVALID_SHADER_NV:
      return "One or more shaders failed to compile or link.";
177
#if VK_HEADER_VERSION >= 39
178
    case VK_ERROR_OUT_OF_POOL_MEMORY_KHR:
179 180 181
      return "A pool memory allocation has failed.";
#endif
#if VK_HEADER_VERSION >= 54
182
    case VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR:
183 184 185 186 187
      return "An external handle is not a valid handle of the specified type.";
#endif
#if VK_HEADER_VERSION >= 64
    case VK_ERROR_NOT_PERMITTED_EXT:
      return "The caller does not have sufficient privileges.";
188 189 190 191
#endif
#if VK_HEADER_VERSION >= 72
    case VK_ERROR_FRAGMENTATION_EXT:
      return "A descriptor pool creation has failed due to fragmentation";
192
#endif
193 194 195 196 197
#if VK_HEADER_VERSION >= 89
    case VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT:
      return "Invalid DRM format modifier plane layout";
#endif

198 199
    case VK_RESULT_RANGE_SIZE:
    case VK_RESULT_MAX_ENUM:
200 201 202 203 204
    default:
      return "Unknown Vulkan error.";
  }
}

Benjamin Otte's avatar
Benjamin Otte committed
205 206 207 208 209
static void
gdk_vulkan_context_dispose (GObject *gobject)
{
  GdkVulkanContext *context = GDK_VULKAN_CONTEXT (gobject);
  GdkVulkanContextPrivate *priv = gdk_vulkan_context_get_instance_private (context);
210
  GdkDisplay *display;
211
  VkDevice device;
212
  guint i;
Benjamin Otte's avatar
Benjamin Otte committed
213

214 215 216 217 218
  for (i = 0; i < priv->n_images; i++)
    {
      cairo_region_destroy (priv->regions[i]);
    }
  g_clear_pointer (&priv->regions, g_free);
219 220 221
  g_clear_pointer (&priv->images, g_free);
  priv->n_images = 0;

222 223 224 225 226 227 228 229 230 231
  device = gdk_vulkan_context_get_device (context);

  if (priv->draw_semaphore != VK_NULL_HANDLE)
    {
      vkDestroySemaphore (device,
                           priv->draw_semaphore,
                           NULL);
      priv->draw_semaphore = VK_NULL_HANDLE;
    }

232 233
  if (priv->swapchain != VK_NULL_HANDLE)
    {
234
      vkDestroySwapchainKHR (device,
235 236 237 238 239
                             priv->swapchain,
                             NULL);
      priv->swapchain = VK_NULL_HANDLE;
    }

240
  if (priv->surface != VK_NULL_HANDLE)
Benjamin Otte's avatar
Benjamin Otte committed
241
    {
242 243 244 245
      vkDestroySurfaceKHR (gdk_vulkan_context_get_instance (context),
                           priv->surface,
                           NULL);
      priv->surface = VK_NULL_HANDLE;
Benjamin Otte's avatar
Benjamin Otte committed
246 247
    }

248 249
  /* display will be unset in gdk_draw_context_dispose() */
  display = gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context));
250
  if (display && priv->vulkan_ref)
251
    gdk_display_unref_vulkan (display);
Benjamin Otte's avatar
Benjamin Otte committed
252

253
  G_OBJECT_CLASS (gdk_vulkan_context_parent_class)->dispose (gobject);
Benjamin Otte's avatar
Benjamin Otte committed
254 255
}

256 257 258 259 260
static gboolean
gdk_vulkan_context_check_swapchain (GdkVulkanContext  *context,
                                    GError           **error)
{
  GdkVulkanContextPrivate *priv = gdk_vulkan_context_get_instance_private (context);
261
  GdkSurface *surface = gdk_draw_context_get_surface (GDK_DRAW_CONTEXT (context));
262 263 264 265
  VkSurfaceCapabilitiesKHR capabilities;
  VkCompositeAlphaFlagBitsKHR composite_alpha;
  VkSwapchainKHR new_swapchain;
  VkResult res;
266
  VkDevice device;
267
  guint i;
268

269 270
  device = gdk_vulkan_context_get_device (context);

271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
  res = GDK_VK_CHECK (vkGetPhysicalDeviceSurfaceCapabilitiesKHR, gdk_vulkan_context_get_physical_device (context),
                                                                 priv->surface,
                                                                 &capabilities);
  if (res != VK_SUCCESS)
    {
      g_set_error (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_NOT_AVAILABLE,
                   "Could not query surface capabilities: %s", gdk_vulkan_strerror (res));
      return FALSE;
    }

  if (capabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR)
    composite_alpha = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;
  else if (capabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR)
    {
      /* let's hope the backend knows what it's doing */
      composite_alpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;
    }
  else
    {
290 291
      GDK_DISPLAY_NOTE (gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context)),
                        VULKAN, g_warning ("Vulkan swapchain doesn't do transparency. Using opaque swapchain instead."));
292 293 294
      composite_alpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
    }

295 296 297
  /*
   * Per https://www.khronos.org/registry/vulkan/specs/1.0-wsi_extensions/xhtml/vkspec.html#VkSurfaceCapabilitiesKHR
   * the current extent may assume a special value, meaning that the extend should assume whatever
298
   * value the surface has.
299 300 301
   */
  if (capabilities.currentExtent.width == -1 || capabilities.currentExtent.height == -1)
    {
302 303
      capabilities.currentExtent.width = gdk_surface_get_width (surface) * gdk_surface_get_scale_factor (surface);
      capabilities.currentExtent.height = gdk_surface_get_height (surface) * gdk_surface_get_scale_factor (surface);
304 305
    }

306
  res = GDK_VK_CHECK (vkCreateSwapchainKHR, device,
307 308 309 310 311
                                            &(VkSwapchainCreateInfoKHR) {
                                                .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
                                                .pNext = NULL,
                                                .flags = 0,
                                                .surface = priv->surface,
312
                                                .minImageCount = CLAMP (4,
313 314
                                                                        capabilities.minImageCount,
                                                                        capabilities.maxImageCount ? capabilities.maxImageCount : G_MAXUINT32),
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
                                                .imageFormat = priv->image_format.format,
                                                .imageColorSpace = priv->image_format.colorSpace,
                                                .imageExtent = capabilities.currentExtent,
                                                .imageArrayLayers = 1,
                                                .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
                                                .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
                                                .queueFamilyIndexCount = 1,
                                                .pQueueFamilyIndices = (uint32_t[1]) {
                                                    gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context))->vk_queue_family_index
                                                },
                                                .preTransform = capabilities.currentTransform,
                                                .compositeAlpha = composite_alpha,
                                                .presentMode = VK_PRESENT_MODE_FIFO_KHR,
                                                .clipped = VK_FALSE,
                                                .oldSwapchain = priv->swapchain
                                            },
                                            NULL,
                                            &new_swapchain);

  if (priv->swapchain != VK_NULL_HANDLE)
335 336 337 338
    {
      vkDestroySwapchainKHR (device,
                             priv->swapchain,
                             NULL);
339 340 341 342 343
      for (i = 0; i < priv->n_images; i++)
        {
          cairo_region_destroy (priv->regions[i]);
        }
      g_clear_pointer (&priv->regions, g_free);
344 345 346
      g_clear_pointer (&priv->images, g_free);
      priv->n_images = 0;
    }
347 348 349 350

  if (res == VK_SUCCESS)
    {
      priv->swapchain = new_swapchain;
351 352 353 354 355 356 357 358 359 360

      GDK_VK_CHECK (vkGetSwapchainImagesKHR, device,
                                             priv->swapchain,
                                             &priv->n_images,
                                             NULL);
      priv->images = g_new (VkImage, priv->n_images);
      GDK_VK_CHECK (vkGetSwapchainImagesKHR, device,
                                             priv->swapchain,
                                             &priv->n_images,
                                             priv->images);
361 362 363 364 365
      priv->regions = g_new (cairo_region_t *, priv->n_images);
      for (i = 0; i < priv->n_images; i++)
        {
          priv->regions[i] = cairo_region_create_rectangle (&(cairo_rectangle_int_t) {
                                                                0, 0,
366 367
                                                                gdk_surface_get_width (surface),
                                                                gdk_surface_get_height (surface),
368 369
                                                            });
        }
370 371 372 373
    }
  else
    {
      g_set_error (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_NOT_AVAILABLE,
374
                   "Could not create swapchain for this surface: %s", gdk_vulkan_strerror (res));
375 376 377 378
      priv->swapchain = VK_NULL_HANDLE;
      return FALSE;
    }

379 380 381
  g_signal_emit (context, signals[IMAGES_UPDATED], 0);

  return res == VK_SUCCESS;
382 383
}

384 385 386 387 388 389
static void
gdk_vulkan_context_begin_frame (GdkDrawContext *draw_context,
                                cairo_region_t *region)
{
  GdkVulkanContext *context = GDK_VULKAN_CONTEXT (draw_context);
  GdkVulkanContextPrivate *priv = gdk_vulkan_context_get_instance_private (context);
390
  guint i;
391

392 393 394 395 396
  for (i = 0; i < priv->n_images; i++)
    {
      cairo_region_union (priv->regions[i], region);
    }

397 398 399 400 401 402
  GDK_VK_CHECK (vkAcquireNextImageKHR, gdk_vulkan_context_get_device (context),
                                       priv->swapchain,
                                       UINT64_MAX,
                                       priv->draw_semaphore,
                                       VK_NULL_HANDLE,
                                       &priv->draw_index);
403 404

  cairo_region_union (region, priv->regions[priv->draw_index]);
405 406 407 408
}

static void
gdk_vulkan_context_end_frame (GdkDrawContext *draw_context,
409
                              cairo_region_t *painted)
410 411 412 413 414 415 416
{
  GdkVulkanContext *context = GDK_VULKAN_CONTEXT (draw_context);
  GdkVulkanContextPrivate *priv = gdk_vulkan_context_get_instance_private (context);

  GDK_VK_CHECK (vkQueuePresentKHR, gdk_vulkan_context_get_queue (context),
                                   &(VkPresentInfoKHR) {
                                       .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
417 418 419 420
                                       .waitSemaphoreCount = 1,
                                       .pWaitSemaphores = (VkSemaphore[]) {
                                           priv->draw_semaphore
                                       },
421 422 423 424 425 426 427 428
                                       .swapchainCount = 1,
                                       .pSwapchains = (VkSwapchainKHR[]) { 
                                           priv->swapchain
                                       },
                                       .pImageIndices = (uint32_t[]) {
                                           priv->draw_index
                                       },
                                   });
429 430 431

  cairo_region_destroy (priv->regions[priv->draw_index]);
  priv->regions[priv->draw_index] = cairo_region_create ();
432 433
}

434 435 436 437 438 439 440 441 442 443 444 445 446 447
static void
gdk_vulkan_context_surface_resized (GdkDrawContext *draw_context)
{
  GdkVulkanContext *context = GDK_VULKAN_CONTEXT (draw_context);
  GError *error = NULL;

  if (!gdk_vulkan_context_check_swapchain (context, &error))
    {
      g_warning ("%s", error->message);
      g_error_free (error);
      return;
    }
}

448 449 450 451 452 453 454 455 456 457
static void
gdk_vulkan_context_class_init (GdkVulkanContextClass *klass)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
  GdkDrawContextClass *draw_context_class = GDK_DRAW_CONTEXT_CLASS (klass);

  gobject_class->dispose = gdk_vulkan_context_dispose;

  draw_context_class->begin_frame = gdk_vulkan_context_begin_frame;
  draw_context_class->end_frame = gdk_vulkan_context_end_frame;
458
  draw_context_class->surface_resized = gdk_vulkan_context_surface_resized;
459 460 461 462 463 464 465

  /**
   * GdkVulkanContext::images-updated:
   * @context: the object on which the signal is emitted
   *
   * This signal is emitted when the images managed by this context have
   * changed. Usually this means that the swapchain had to be recreated,
466
   * for example in response to a change of the surface size.
467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482
   */
  signals[IMAGES_UPDATED] =
    g_signal_new (g_intern_static_string ("images-updated"),
		  G_OBJECT_CLASS_TYPE (gobject_class),
                  G_SIGNAL_RUN_LAST,
                  0,
                  NULL, NULL,
                  g_cclosure_marshal_VOID__VOID,
                  G_TYPE_NONE, 0);
}

static void
gdk_vulkan_context_init (GdkVulkanContext *self)
{
}

483 484 485 486 487 488 489
static gboolean
gdk_vulkan_context_real_init (GInitable     *initable,
                              GCancellable  *cancellable,
                              GError       **error)
{
  GdkVulkanContext *context = GDK_VULKAN_CONTEXT (initable);
  GdkVulkanContextPrivate *priv = gdk_vulkan_context_get_instance_private (context);
490 491 492
  VkResult res;
  VkBool32 supported;
  uint32_t i;
493

494 495
  priv->vulkan_ref = gdk_display_ref_vulkan (gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context)), error);
  if (!priv->vulkan_ref)
496 497
    return FALSE;

498 499
  res = GDK_VULKAN_CONTEXT_GET_CLASS (context)->create_surface (context, &priv->surface);
  if (res != VK_SUCCESS)
500
    {
501
      g_set_error (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_NOT_AVAILABLE,
502
                   "Could not create surface for this surface: %s", gdk_vulkan_strerror (res));
503 504 505
      return FALSE;
    }

506 507 508 509 510 511 512
  res = GDK_VK_CHECK (vkGetPhysicalDeviceSurfaceSupportKHR, gdk_vulkan_context_get_physical_device (context),
                                                            gdk_vulkan_context_get_queue_family_index (context),
                                                            priv->surface,
                                                            &supported);
  if (res != VK_SUCCESS)
    {
      g_set_error (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_NOT_AVAILABLE,
513
                   "Could not check if queue family supports this surface: %s", gdk_vulkan_strerror (res));
514 515 516 517 518 519 520 521 522 523 524 525
    }
  else if (!supported)
    {
      g_set_error (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_NOT_AVAILABLE,
                   "FIXME: Queue family does not support surface. Write code to try different queue family.");
    }
  else
    {
      uint32_t n_formats;
      GDK_VK_CHECK (vkGetPhysicalDeviceSurfaceFormatsKHR, gdk_vulkan_context_get_physical_device (context),
                                                          priv->surface,
                                                          &n_formats, NULL);
526
      VkSurfaceFormatKHR *formats = g_newa (VkSurfaceFormatKHR, n_formats);
527 528 529 530 531
      GDK_VK_CHECK (vkGetPhysicalDeviceSurfaceFormatsKHR, gdk_vulkan_context_get_physical_device (context),
                                                          priv->surface,
                                                          &n_formats, formats);
      for (i = 0; i < n_formats; i++)
        {
532
          if (formats[i].format == VK_FORMAT_B8G8R8A8_UNORM)
533 534 535 536 537 538 539 540 541 542 543 544 545
            break;
        }
      if (i == n_formats)
        {
          g_set_error_literal (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_NOT_AVAILABLE,
                               "No supported image format found.");
          goto out_surface;  
        }
      priv->image_format = formats[i];

      if (!gdk_vulkan_context_check_swapchain (context, error))
        goto out_surface;

546 547 548 549 550 551 552
      GDK_VK_CHECK (vkCreateSemaphore, gdk_vulkan_context_get_device (context),
                                       &(VkSemaphoreCreateInfo) {
                                           .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
                                       },
                                       NULL,
                                       &priv->draw_semaphore);

553 554 555 556 557 558 559 560 561
      return TRUE;
    }

out_surface:
  vkDestroySurfaceKHR (gdk_vulkan_context_get_instance (context),
                       priv->surface,
                       NULL);
  priv->surface = VK_NULL_HANDLE;
  return FALSE;
562 563 564 565 566 567 568 569
}

static void
gdk_vulkan_context_initable_init (GInitableIface *iface)
{
  iface->init = gdk_vulkan_context_real_init;
}

570 571 572 573 574 575 576 577
/**
 * gdk_vulkan_context_get_instance:
 * @context: a #GdkVulkanContext
 *
 * Gets the Vulkan instance that is associated with @context.
 *
 * Returns: (transfer none): the VkInstance
 */
578 579 580 581 582
VkInstance
gdk_vulkan_context_get_instance (GdkVulkanContext *context)
{
  g_return_val_if_fail (GDK_IS_VULKAN_CONTEXT (context), NULL);

583
  return gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context))->vk_instance;
584 585
}

586 587 588 589 590 591 592 593
/**
 * gdk_vulkan_context_get_physical_device:
 * @context: a #GdkVulkanContext
 *
 * Gets the Vulkan physical device that this context is using.
 *
 * Returns: (transfer none): the VkPhysicalDevice
 */
594 595 596 597 598 599 600 601
VkPhysicalDevice
gdk_vulkan_context_get_physical_device (GdkVulkanContext *context)
{
  g_return_val_if_fail (GDK_IS_VULKAN_CONTEXT (context), NULL);

  return gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context))->vk_physical_device;
}

602 603 604 605 606 607 608 609
/**
 * gdk_vulkan_context_get_device:
 * @context: a #GdkVulkanContext
 *
 * Gets the Vulkan device that this context is using.
 *
 * Returns: (transfer none): the VkDevice
 */
610 611 612 613 614 615 616 617
VkDevice
gdk_vulkan_context_get_device (GdkVulkanContext *context)
{
  g_return_val_if_fail (GDK_IS_VULKAN_CONTEXT (context), NULL);

  return gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context))->vk_device;
}

618 619 620 621 622 623 624 625
/**
 * gdk_vulkan_context_get_queue:
 * @context: a #GdkVulkanContext
 *
 * Gets the Vulkan queue that this context is using.
 *
 * Returns: (transfer none): the VkQueue
 */
626 627 628 629 630 631 632 633
VkQueue
gdk_vulkan_context_get_queue (GdkVulkanContext *context)
{
  g_return_val_if_fail (GDK_IS_VULKAN_CONTEXT (context), NULL);

  return gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context))->vk_queue;
}

634 635 636 637 638 639 640 641 642
/**
 * gdk_vulkan_context_get_queue_family_index:
 * @context: a #GdkVulkanContext
 *
 * Gets the family index for the queue that this context is using.
 * See vkGetPhysicalDeviceQueueFamilyProperties().
 *
 * Returns: the index
 */
643 644 645 646 647 648 649 650
uint32_t
gdk_vulkan_context_get_queue_family_index (GdkVulkanContext *context)
{
  g_return_val_if_fail (GDK_IS_VULKAN_CONTEXT (context), 0);

  return gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context))->vk_queue_family_index;
}

651 652 653 654 655 656 657 658
/**
 * gdk_vulkan_context_get_image_format:
 * @context: a #GdkVulkanContext
 *
 * Gets the image format that this context is using.
 *
 * Returns: (transfer none): the VkFormat
 */
659 660 661 662 663 664 665 666 667 668
VkFormat
gdk_vulkan_context_get_image_format (GdkVulkanContext *context)
{
  GdkVulkanContextPrivate *priv = gdk_vulkan_context_get_instance_private (context);

  g_return_val_if_fail (GDK_IS_VULKAN_CONTEXT (context), VK_FORMAT_UNDEFINED);

  return priv->image_format.format;
}

669 670 671 672 673 674 675 676
/**
 * gdk_vulkan_context_get_n_images:
 * @context: a #GdkVulkanContext
 *
 * Gets the number of images that this context is using in its swap chain.
 *
 * Returns: the number of images
 */
677
uint32_t
678 679 680 681 682 683 684 685 686
gdk_vulkan_context_get_n_images (GdkVulkanContext *context)
{
  GdkVulkanContextPrivate *priv = gdk_vulkan_context_get_instance_private (context);

  g_return_val_if_fail (GDK_IS_VULKAN_CONTEXT (context), 0);

  return priv->n_images;
}

687 688 689 690 691 692 693 694 695
/**
 * gdk_vulkan_context_get_image:
 * @context: a #GdkVulkanContext
 * @id: the index of the image to return
 *
 * Gets the image with index @id that this context is using.
 *
 * Returns: (transfer none): the VkImage
 */
696 697 698 699 700 701 702 703 704 705 706 707
VkImage
gdk_vulkan_context_get_image (GdkVulkanContext *context,
                              guint             id)
{
  GdkVulkanContextPrivate *priv = gdk_vulkan_context_get_instance_private (context);

  g_return_val_if_fail (GDK_IS_VULKAN_CONTEXT (context), VK_NULL_HANDLE);
  g_return_val_if_fail (id < priv->n_images, VK_NULL_HANDLE);

  return priv->images[id];
}

708 709 710 711 712 713
/**
 * gdk_vulkan_context_get_draw_index:
 * @context: a #GdkVulkanContext
 *
 * Gets the index of the image that is currently being drawn.
 *
714
 * This function can only be used between gdk_draw_context_begin_frame() and
715
 * gdk_draw_context_end_frame() calls.
716 717 718
 *
 * Returns: the index of the images that is being drawn
 */
719 720 721 722 723 724
uint32_t
gdk_vulkan_context_get_draw_index (GdkVulkanContext *context)
{
  GdkVulkanContextPrivate *priv = gdk_vulkan_context_get_instance_private (context);

  g_return_val_if_fail (GDK_IS_VULKAN_CONTEXT (context), 0);
725
  g_return_val_if_fail (gdk_draw_context_is_in_frame (GDK_DRAW_CONTEXT (context)), 0);
726 727 728 729

  return priv->draw_index;
}

730 731 732 733 734 735 736
/**
 * gdk_vulkan_context_get_draw_semaphore:
 * @context: a #GdkVulkanContext
 *
 * Gets the Vulkan semaphore that protects access to the image that is
 * currently being drawn.
 *
737
 * This function can only be used between gdk_draw_context_begin_frame() and
738
 * gdk_draw_context_end_frame() calls.
739 740 741
 *
 * Returns: (transfer none): the VkSemaphore
 */
742 743 744 745 746 747
VkSemaphore
gdk_vulkan_context_get_draw_semaphore (GdkVulkanContext *context)
{
  GdkVulkanContextPrivate *priv = gdk_vulkan_context_get_instance_private (context);

  g_return_val_if_fail (GDK_IS_VULKAN_CONTEXT (context), VK_NULL_HANDLE);
748
  g_return_val_if_fail (gdk_draw_context_is_in_frame (GDK_DRAW_CONTEXT (context)), VK_NULL_HANDLE);
749 750 751 752

  return priv->draw_semaphore;
}

Benjamin Otte's avatar
Benjamin Otte committed
753
static gboolean
754 755
gdk_display_create_vulkan_device (GdkDisplay  *display,
                                  GError     **error)
Benjamin Otte's avatar
Benjamin Otte committed
756
{
757 758 759 760
  uint32_t i, j, k;
  const char *override;
  gboolean list_devices;
  int first, last;
Benjamin Otte's avatar
Benjamin Otte committed
761

762
  uint32_t n_devices = 0;
Benjamin Otte's avatar
Benjamin Otte committed
763
  GDK_VK_CHECK(vkEnumeratePhysicalDevices, display->vk_instance, &n_devices, NULL);
764
  VkPhysicalDevice *devices;
Benjamin Otte's avatar
Benjamin Otte committed
765

766 767 768 769 770 771 772 773
  if (n_devices == 0)
    {
      /* Give a different error for 0 devices so people know their drivers suck. */
      g_set_error_literal (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_NOT_AVAILABLE,
                           "No Vulkan devices available.");
      return FALSE;
    }

774 775 776
  devices = g_newa (VkPhysicalDevice, n_devices);
  GDK_VK_CHECK(vkEnumeratePhysicalDevices, display->vk_instance, &n_devices, devices);

777 778 779 780 781 782 783 784 785 786 787 788
  first = 0;
  last = n_devices;

  override = g_getenv ("GDK_VULKAN_DEVICE");
  list_devices = FALSE;
  if (override)
    {
      if (g_strcmp0 (override, "list") == 0)
        list_devices = TRUE;
      else
        {
          gint64 device_idx;
789
          GError *error2 = NULL;
790

791
          if (!g_ascii_string_to_signed (override, 10, 0, G_MAXINT, &device_idx, &error2))
792
            {
793 794
              g_warning ("Failed to parse %s: %s", "GDK_VULKAN_DEVICE", error2->message);
              g_error_free (error2);
795 796 797 798 799 800 801 802 803 804 805 806 807
              device_idx = -1;
            }

          if (device_idx < 0 || device_idx >= n_devices)
            g_warning ("%s value out of range, ignoring", "GDK_VULKAN_DEVICE");
          else
            {
              first = device_idx;
              last = first + 1;
            }
        }
    }

808
  if (list_devices || GDK_DISPLAY_DEBUG_CHECK (display, VULKAN))
Benjamin Otte's avatar
Benjamin Otte committed
809
    {
810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857
      for (i = 0; i < n_devices; i++)
        {
          VkPhysicalDeviceProperties props;
          VkQueueFamilyProperties *queue_props;
          uint32_t n_queue_props;
          const char *device_type[] = {
            "Other", "Integrated GPU", "Discrete GPU", "Virtual GPU", "CPU"
          };
          struct {
            int bit;
            const char *name;
          } queue_caps[] = {
            { VK_QUEUE_GRAPHICS_BIT, "graphics" },
            { VK_QUEUE_COMPUTE_BIT, "compute" },
            { VK_QUEUE_TRANSFER_BIT, "transfer" },
            { VK_QUEUE_SPARSE_BINDING_BIT, "sparse binding" }
          };

          vkGetPhysicalDeviceProperties (devices[i], &props);
          vkGetPhysicalDeviceQueueFamilyProperties (devices[i], &n_queue_props, NULL);
          queue_props = g_newa (VkQueueFamilyProperties, n_queue_props);
          vkGetPhysicalDeviceQueueFamilyProperties (devices[i], &n_queue_props, queue_props);

          g_print ("Vulkan Device %u:\n", i);
          g_print ("    %s (%s)\n", props.deviceName, device_type[props.deviceType]);
          g_print ("    Vendor ID: 0x%Xu\n", props.vendorID);
          g_print ("    Device ID: 0x%Xu\n", props.deviceID);
          g_print ("    API version %u.%u.%u\n",
                   VK_VERSION_MAJOR (props.apiVersion),
                   VK_VERSION_MINOR (props.apiVersion),
                   VK_VERSION_PATCH (props.apiVersion));
          for (j = 0; j < n_queue_props; j++)
            {
              const char *sep = "";

              g_print ("    Queue %d: ", j);
              for (k = 0; k < G_N_ELEMENTS (queue_caps); k++)
                {
                  if (queue_props[j].queueFlags & queue_caps[k].bit)
                    {
                      g_print ("%s%s", sep, queue_caps[k].name);
                      sep = "/";
                    }
                }
              g_print ("\n");
            }
        }
    }
Benjamin Otte's avatar
Benjamin Otte committed
858

859 860
  for (i = first; i < last; i++)
    {
Benjamin Otte's avatar
Benjamin Otte committed
861 862
      uint32_t n_queue_props;
      vkGetPhysicalDeviceQueueFamilyProperties (devices[i], &n_queue_props, NULL);
863
      VkQueueFamilyProperties *queue_props = g_newa (VkQueueFamilyProperties, n_queue_props);
Benjamin Otte's avatar
Benjamin Otte committed
864 865 866 867 868 869
      vkGetPhysicalDeviceQueueFamilyProperties (devices[i], &n_queue_props, queue_props);

      for (j = 0; j < n_queue_props; j++)
        {
          if (queue_props[j].queueFlags & VK_QUEUE_GRAPHICS_BIT)
            {
870
              GDK_DISPLAY_NOTE (display, VULKAN, g_print ("Using Vulkan device %u, queue %u\n", i, j));
Benjamin Otte's avatar
Benjamin Otte committed
871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895
              if (GDK_VK_CHECK (vkCreateDevice, devices[i],
                                                &(VkDeviceCreateInfo) {
                                                    VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
                                                    NULL,
                                                    0,
                                                    1,
                                                    &(VkDeviceQueueCreateInfo) {
                                                        .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
                                                        .queueFamilyIndex = j,
                                                        .queueCount = 1,
                                                        .pQueuePriorities = (float []) { 1.0f },
                                                    },
                                                    0,
                                                    NULL,
                                                    1,
                                                    (const char * const []) {
                                                       VK_KHR_SWAPCHAIN_EXTENSION_NAME,
                                                    },
                                                },
                                                NULL,
                                                &display->vk_device) != VK_SUCCESS)
                continue;

              display->vk_physical_device = devices[i];
              vkGetDeviceQueue(display->vk_device, j, 0, &display->vk_queue);
896
              display->vk_queue_family_index = j;
Benjamin Otte's avatar
Benjamin Otte committed
897 898 899 900 901
              return TRUE;
            }
        }
    }

902 903
  g_set_error_literal (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_NOT_AVAILABLE,
                       "Could not find a Vulkan device with the required features.");
Benjamin Otte's avatar
Benjamin Otte committed
904 905 906
  return FALSE;
}

907
static VkBool32 VKAPI_CALL
908 909 910 911 912 913 914 915 916 917
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)
{
  if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT)
918
    g_critical ("Vulkan: %s: %s", pLayerPrefix, pMessage);
919
  else if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT)
920
    g_critical ("Vulkan: %s: %s", pLayerPrefix, pMessage);
921
  else if (flags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT)
922
    g_warning ("Vulkan: %s: %s", pLayerPrefix, pMessage);
923
  else if (flags & VK_DEBUG_REPORT_DEBUG_BIT_EXT)
924
    g_debug ("Vulkan: %s: %s", pLayerPrefix, pMessage);
925
  else
926 927
    g_info ("Vulkan: %s: %s", pLayerPrefix, pMessage);

928
  return VK_FALSE;
929 930
}

Benjamin Otte's avatar
Benjamin Otte committed
931
static gboolean
932 933
gdk_display_create_vulkan_instance (GdkDisplay  *display,
                                    GError     **error)
Benjamin Otte's avatar
Benjamin Otte committed
934 935
{
  uint32_t i;
936
  GPtrArray *used_extensions;
937
  GPtrArray *used_layers;
938
  gboolean validate = FALSE, have_debug_report = FALSE;
939
  VkResult res;
Benjamin Otte's avatar
Benjamin Otte committed
940

941 942 943 944 945 946 947
  if (GDK_DISPLAY_GET_CLASS (display)->vk_extension_name == NULL)
    {
      g_set_error (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_UNSUPPORTED,
                   "The %s backend has no Vulkan support.", G_OBJECT_TYPE_NAME (display));
      return FALSE;
    }

Benjamin Otte's avatar
Benjamin Otte committed
948 949
  uint32_t n_extensions;
  GDK_VK_CHECK (vkEnumerateInstanceExtensionProperties, NULL, &n_extensions, NULL);
950
  VkExtensionProperties *extensions = g_newa (VkExtensionProperties, n_extensions);
Benjamin Otte's avatar
Benjamin Otte committed
951 952
  GDK_VK_CHECK (vkEnumerateInstanceExtensionProperties, NULL, &n_extensions, extensions);

953 954 955 956
  used_extensions = g_ptr_array_new ();
  g_ptr_array_add (used_extensions, (gpointer) VK_KHR_SURFACE_EXTENSION_NAME); 
  g_ptr_array_add (used_extensions, (gpointer) GDK_DISPLAY_GET_CLASS (display)->vk_extension_name); 

Benjamin Otte's avatar
Benjamin Otte committed
957 958
  for (i = 0; i < n_extensions; i++)
    {
959 960
      if (GDK_DISPLAY_DEBUG_CHECK (display, VULKAN))
        g_print ("Extension available: %s v%u.%u.%u\n",
Benjamin Otte's avatar
Benjamin Otte committed
961 962 963
                                 extensions[i].extensionName,
                                 VK_VERSION_MAJOR (extensions[i].specVersion),
                                 VK_VERSION_MINOR (extensions[i].specVersion),
964
                                 VK_VERSION_PATCH (extensions[i].specVersion));
965 966 967 968 969 970

      if (g_str_equal (extensions[i].extensionName, VK_EXT_DEBUG_REPORT_EXTENSION_NAME))
        {
          g_ptr_array_add (used_extensions, (gpointer) VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
          have_debug_report = TRUE;
        }
Benjamin Otte's avatar
Benjamin Otte committed
971 972 973 974
    }

  uint32_t n_layers;
  GDK_VK_CHECK (vkEnumerateInstanceLayerProperties, &n_layers, NULL);
975
  VkLayerProperties *layers = g_newa (VkLayerProperties, n_layers);
Benjamin Otte's avatar
Benjamin Otte committed
976 977
  GDK_VK_CHECK (vkEnumerateInstanceLayerProperties, &n_layers, layers);

978 979
  used_layers = g_ptr_array_new ();

Benjamin Otte's avatar
Benjamin Otte committed
980 981
  for (i = 0; i < n_layers; i++)
    {
982 983
      if (GDK_DISPLAY_DEBUG_CHECK (display, VULKAN))
        g_print ("Layer available: %s v%u.%u.%u (%s)\n",
Benjamin Otte's avatar
Benjamin Otte committed
984 985 986 987
                                 layers[i].layerName,
                                 VK_VERSION_MAJOR (layers[i].specVersion),
                                 VK_VERSION_MINOR (layers[i].specVersion),
                                 VK_VERSION_PATCH (layers[i].specVersion),
988 989
                                 layers[i].description);
      if (GDK_DISPLAY_DEBUG_CHECK (display, VULKAN_VALIDATE) &&
990 991 992 993 994
          g_str_equal (layers[i].layerName, "VK_LAYER_LUNARG_standard_validation"))
        {
          g_ptr_array_add (used_layers, (gpointer) "VK_LAYER_LUNARG_standard_validation");
          validate = TRUE;
        }
Benjamin Otte's avatar
Benjamin Otte committed
995 996
    }

997
  if (GDK_DISPLAY_DEBUG_CHECK (display, VULKAN_VALIDATE) && !validate)
998
    {
999 1000 1001 1002
      g_warning ("Vulkan validation layers were requested, but not found. Running without.");
    }

  res = GDK_VK_CHECK (vkCreateInstance, &(VkInstanceCreateInfo) {
1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013
                                             .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
                                             .pNext = NULL,
                                             .flags = 0,
                                             .pApplicationInfo = &(VkApplicationInfo) {
                                                 .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
                                                 .pNext = NULL,
                                                 .pApplicationName = g_get_application_name (),
                                                 .applicationVersion = 0,
                                                 .pEngineName = "GTK+",
                                                 .engineVersion = VK_MAKE_VERSION (GDK_MAJOR_VERSION, GDK_MINOR_VERSION, GDK_MICRO_VERSION),
                                                 .apiVersion = VK_API_VERSION_1_0
1014
                                             },
1015 1016 1017 1018
                                             .enabledLayerCount = used_layers->len,
                                             .ppEnabledLayerNames = (const char * const *) used_layers->pdata,
                                             .enabledExtensionCount = used_extensions->len,
                                             .ppEnabledExtensionNames = (const char * const *) used_extensions->pdata
1019 1020 1021 1022
                                         },
                                         NULL,
                                         &display->vk_instance);
  g_ptr_array_free (used_layers, TRUE);
1023
  g_ptr_array_free (used_extensions, TRUE);
1024 1025 1026 1027 1028

  if (res != VK_SUCCESS)
    {
      g_set_error (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_UNSUPPORTED,
                   "Could not create a Vulkan instance: %s", gdk_vulkan_strerror (res));
1029 1030
      return FALSE;
    }
Benjamin Otte's avatar
Benjamin Otte committed
1031

1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052
  if (have_debug_report)
    {
      PFN_vkCreateDebugReportCallbackEXT vkCreateDebugReportCallbackEXT;
      
      vkCreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT) vkGetInstanceProcAddr (display->vk_instance, "vkCreateDebugReportCallbackEXT" );
      GDK_VK_CHECK (vkCreateDebugReportCallbackEXT, display->vk_instance,
                                                    &(VkDebugReportCallbackCreateInfoEXT) {
                                                        .sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT,
                                                        .pNext = NULL,
                                                        .flags = VK_DEBUG_REPORT_INFORMATION_BIT_EXT
                                                               | VK_DEBUG_REPORT_WARNING_BIT_EXT
                                                               | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT
                                                               | VK_DEBUG_REPORT_ERROR_BIT_EXT
                                                               | VK_DEBUG_REPORT_DEBUG_BIT_EXT,
                                                        .pfnCallback = gdk_vulkan_debug_report,
                                                        .pUserData = NULL
                                                    },
                                                    NULL,
                                                    &display->vk_debug_callback);
    }

1053 1054
  if (!gdk_display_create_vulkan_device (display, error))
    {
1055 1056 1057 1058 1059 1060 1061 1062 1063 1064
      if (display->vk_debug_callback != VK_NULL_HANDLE)
        {
          PFN_vkDestroyDebugReportCallbackEXT vkDestroyDebugReportCallbackEXT;

          vkDestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT) vkGetInstanceProcAddr (display->vk_instance, "vkDestroyDebugReportCallbackEXT");
          vkDestroyDebugReportCallbackEXT (display->vk_instance,
                                           display->vk_debug_callback,
                                           NULL);
          display->vk_debug_callback = VK_NULL_HANDLE;
        }
1065 1066 1067 1068 1069 1070
      vkDestroyInstance (display->vk_instance, NULL);
      display->vk_instance = VK_NULL_HANDLE;
      return FALSE;
    }

  return TRUE;
Benjamin Otte's avatar
Benjamin Otte committed
1071 1072 1073 1074
}

gboolean
gdk_display_ref_vulkan (GdkDisplay *display,
1075
                        GError     **error)
Benjamin Otte's avatar
Benjamin Otte committed
1076 1077 1078
{
  if (display->vulkan_refcount == 0)
    {
1079
      if (!gdk_display_create_vulkan_instance (display, error))
Benjamin Otte's avatar
Benjamin Otte committed
1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098
        return FALSE;
    }

  display->vulkan_refcount++;
    
  return TRUE;
}

void
gdk_display_unref_vulkan (GdkDisplay *display)
{
  g_return_if_fail (GDK_IS_DISPLAY (display));
  g_return_if_fail (display->vulkan_refcount > 0);

  display->vulkan_refcount--;
  if (display->vulkan_refcount > 0)
    return;
  
  vkDestroyDevice (display->vk_device, NULL);
1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109
  display->vk_device = VK_NULL_HANDLE;
  if (display->vk_debug_callback != VK_NULL_HANDLE)
    {
      PFN_vkDestroyDebugReportCallbackEXT vkDestroyDebugReportCallbackEXT;

      vkDestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT) vkGetInstanceProcAddr (display->vk_instance, "vkDestroyDebugReportCallbackEXT");
      vkDestroyDebugReportCallbackEXT (display->vk_instance,
                                       display->vk_debug_callback,
                                       NULL);
      display->vk_debug_callback = VK_NULL_HANDLE;
    }
Benjamin Otte's avatar
Benjamin Otte committed
1110
  vkDestroyInstance (display->vk_instance, NULL);
1111
  display->vk_instance = VK_NULL_HANDLE;
Benjamin Otte's avatar
Benjamin Otte committed
1112 1113
}

1114
#else /* GDK_RENDERING_VULKAN */
1115 1116 1117 1118

static void
gdk_vulkan_context_class_init (GdkVulkanContextClass *klass)
{
1119 1120 1121 1122 1123 1124 1125 1126
  signals[IMAGES_UPDATED] =
    g_signal_new (g_intern_static_string ("images-updated"),
		  G_OBJECT_CLASS_TYPE (klass),
                  G_SIGNAL_RUN_LAST,
                  0,
                  NULL, NULL,
                  g_cclosure_marshal_VOID__VOID,
                  G_TYPE_NONE, 0);
1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138
}

static void
gdk_vulkan_context_init (GdkVulkanContext *self)
{
}

static void
gdk_vulkan_context_initable_init (GInitableIface *iface)
{
}

1139
#endif /* GDK_RENDERING_VULKAN */