Blender V4.3
GHOST_ContextVK.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2022-2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include "GHOST_ContextVK.hh"
10
11#ifdef _WIN32
12# include <vulkan/vulkan_win32.h>
13#elif defined(__APPLE__)
14# include <MoltenVK/vk_mvk_moltenvk.h>
15#else /* X11/WAYLAND. */
16# ifdef WITH_GHOST_X11
17# include <vulkan/vulkan_xlib.h>
18# endif
19# ifdef WITH_GHOST_WAYLAND
20# include <vulkan/vulkan_wayland.h>
21# endif
22#endif
23
24#include <vector>
25
26#include <cassert>
27#include <cstdio>
28#include <cstring>
29#include <iostream>
30#include <mutex>
31#include <optional>
32#include <sstream>
33
34#include <sys/stat.h>
35
36/*
37 * Should we only select surfaces that are known to be compatible. Or should we in case no
38 * compatible surfaces have been found select the first one.
39 *
40 * Currently we also select incompatible surfaces as Vulkan is still experimental. Assuming we get
41 * reports of color differences between OpenGL and Vulkan to narrow down if there are other
42 * configurations we need to support.
43 */
44#define SELECT_COMPATIBLE_SURFACES_ONLY false
45
46using namespace std;
47
48uint32_t GHOST_ContextVK::s_currentImage = 0;
49
50static const char *vulkan_error_as_string(VkResult result)
51{
52#define FORMAT_ERROR(X) \
53 case X: { \
54 return "" #X; \
55 }
56
57 switch (result) {
58 FORMAT_ERROR(VK_NOT_READY);
59 FORMAT_ERROR(VK_TIMEOUT);
60 FORMAT_ERROR(VK_EVENT_SET);
61 FORMAT_ERROR(VK_EVENT_RESET);
62 FORMAT_ERROR(VK_INCOMPLETE);
63 FORMAT_ERROR(VK_ERROR_OUT_OF_HOST_MEMORY);
64 FORMAT_ERROR(VK_ERROR_OUT_OF_DEVICE_MEMORY);
65 FORMAT_ERROR(VK_ERROR_INITIALIZATION_FAILED);
66 FORMAT_ERROR(VK_ERROR_DEVICE_LOST);
67 FORMAT_ERROR(VK_ERROR_MEMORY_MAP_FAILED);
68 FORMAT_ERROR(VK_ERROR_LAYER_NOT_PRESENT);
69 FORMAT_ERROR(VK_ERROR_EXTENSION_NOT_PRESENT);
70 FORMAT_ERROR(VK_ERROR_FEATURE_NOT_PRESENT);
71 FORMAT_ERROR(VK_ERROR_INCOMPATIBLE_DRIVER);
72 FORMAT_ERROR(VK_ERROR_TOO_MANY_OBJECTS);
73 FORMAT_ERROR(VK_ERROR_FORMAT_NOT_SUPPORTED);
74 FORMAT_ERROR(VK_ERROR_FRAGMENTED_POOL);
75 FORMAT_ERROR(VK_ERROR_UNKNOWN);
76 FORMAT_ERROR(VK_ERROR_OUT_OF_POOL_MEMORY);
77 FORMAT_ERROR(VK_ERROR_INVALID_EXTERNAL_HANDLE);
78 FORMAT_ERROR(VK_ERROR_FRAGMENTATION);
79 FORMAT_ERROR(VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS);
80 FORMAT_ERROR(VK_ERROR_SURFACE_LOST_KHR);
81 FORMAT_ERROR(VK_ERROR_NATIVE_WINDOW_IN_USE_KHR);
82 FORMAT_ERROR(VK_SUBOPTIMAL_KHR);
83 FORMAT_ERROR(VK_ERROR_OUT_OF_DATE_KHR);
84 FORMAT_ERROR(VK_ERROR_INCOMPATIBLE_DISPLAY_KHR);
85 FORMAT_ERROR(VK_ERROR_VALIDATION_FAILED_EXT);
86 FORMAT_ERROR(VK_ERROR_INVALID_SHADER_NV);
87 FORMAT_ERROR(VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT);
88 FORMAT_ERROR(VK_ERROR_NOT_PERMITTED_EXT);
89 FORMAT_ERROR(VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT);
90 FORMAT_ERROR(VK_THREAD_IDLE_KHR);
91 FORMAT_ERROR(VK_THREAD_DONE_KHR);
92 FORMAT_ERROR(VK_OPERATION_DEFERRED_KHR);
93 FORMAT_ERROR(VK_OPERATION_NOT_DEFERRED_KHR);
94 FORMAT_ERROR(VK_PIPELINE_COMPILE_REQUIRED_EXT);
95 default:
96 return "Unknown Error";
97 }
98}
99
100#define __STR(A) "" #A
101#define VK_CHECK(__expression) \
102 do { \
103 VkResult r = (__expression); \
104 if (r != VK_SUCCESS) { \
105 fprintf(stderr, \
106 "Vulkan Error : %s:%d : %s failled with %s\n", \
107 __FILE__, \
108 __LINE__, \
109 __STR(__expression), \
110 vulkan_error_as_string(r)); \
111 return GHOST_kFailure; \
112 } \
113 } while (0)
114
115#define DEBUG_PRINTF(...) \
116 if (m_debug) { \
117 printf(__VA_ARGS__); \
118 }
119
120/* -------------------------------------------------------------------- */
125 public:
126 VkInstance instance = VK_NULL_HANDLE;
127 VkPhysicalDevice physical_device = VK_NULL_HANDLE;
128
129 VkDevice device = VK_NULL_HANDLE;
130
132
133 VkPhysicalDeviceProperties properties = {};
134 VkPhysicalDeviceFeatures2 features = {};
135 VkPhysicalDeviceVulkan11Features features_11 = {};
136 VkPhysicalDeviceVulkan12Features features_12 = {};
137
138 int users = 0;
139
141 std::mutex queue_mutex;
142
143 public:
144 GHOST_DeviceVK(VkInstance vk_instance, VkPhysicalDevice vk_physical_device)
145 : instance(vk_instance), physical_device(vk_physical_device)
146 {
147 vkGetPhysicalDeviceProperties(physical_device, &properties);
148
149 features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
150 features_11.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES;
151 features_12.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;
152 features.pNext = &features_11;
153 features_11.pNext = &features_12;
154
155 vkGetPhysicalDeviceFeatures2(physical_device, &features);
156 }
158 {
159 if (device != VK_NULL_HANDLE) {
160 vkDestroyDevice(device, nullptr);
161 }
162 }
163
165 {
166 if (device) {
167 vkDeviceWaitIdle(device);
168 }
169 }
170
171 bool has_extensions(const vector<const char *> &required_extensions)
172 {
173 uint32_t ext_count;
174 vkEnumerateDeviceExtensionProperties(physical_device, nullptr, &ext_count, nullptr);
175
176 vector<VkExtensionProperties> available_exts(ext_count);
177 vkEnumerateDeviceExtensionProperties(
178 physical_device, nullptr, &ext_count, available_exts.data());
179
180 for (const auto &extension_needed : required_extensions) {
181 bool found = false;
182 for (const auto &extension : available_exts) {
183 if (strcmp(extension_needed, extension.extensionName) == 0) {
184 found = true;
185 break;
186 }
187 }
188 if (!found) {
189 return false;
190 }
191 }
192 return true;
193 }
194
195 void ensure_device(vector<const char *> &required_extensions,
196 vector<const char *> &optional_extensions)
197 {
198 if (device != VK_NULL_HANDLE) {
199 return;
200 }
202
203 vector<VkDeviceQueueCreateInfo> queue_create_infos;
204 vector<const char *> device_extensions(required_extensions);
205 for (const char *optional_extension : optional_extensions) {
206 if (has_extensions({optional_extension})) {
207 device_extensions.push_back(optional_extension);
208 }
209 }
210
211 float queue_priorities[] = {1.0f};
212 VkDeviceQueueCreateInfo graphic_queue_create_info = {};
213 graphic_queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
214 graphic_queue_create_info.queueFamilyIndex = generic_queue_family;
215 graphic_queue_create_info.queueCount = 1;
216 graphic_queue_create_info.pQueuePriorities = queue_priorities;
217 queue_create_infos.push_back(graphic_queue_create_info);
218
219 VkPhysicalDeviceFeatures device_features = {};
220#ifndef __APPLE__
221 device_features.geometryShader = VK_TRUE;
222 /* MoltenVK supports logicOp, needs to be build with MVK_USE_METAL_PRIVATE_API. */
223 device_features.logicOp = VK_TRUE;
224#endif
225 device_features.dualSrcBlend = VK_TRUE;
226 device_features.imageCubeArray = VK_TRUE;
227 device_features.multiDrawIndirect = VK_TRUE;
228 device_features.multiViewport = VK_TRUE;
229 device_features.shaderClipDistance = VK_TRUE;
230 device_features.drawIndirectFirstInstance = VK_TRUE;
231 device_features.fragmentStoresAndAtomics = VK_TRUE;
232 device_features.samplerAnisotropy = features.features.samplerAnisotropy;
233
234 VkDeviceCreateInfo device_create_info = {};
235 device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
236 device_create_info.queueCreateInfoCount = uint32_t(queue_create_infos.size());
237 device_create_info.pQueueCreateInfos = queue_create_infos.data();
238 device_create_info.enabledExtensionCount = uint32_t(device_extensions.size());
239 device_create_info.ppEnabledExtensionNames = device_extensions.data();
240 device_create_info.pEnabledFeatures = &device_features;
241
242 void *device_create_info_p_next = nullptr;
243
244 /* Enable optional vulkan 12 features when supported on physical device. */
245 VkPhysicalDeviceVulkan12Features vulkan_12_features = {};
246 vulkan_12_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;
247 vulkan_12_features.shaderOutputLayer = features_12.shaderOutputLayer;
248 vulkan_12_features.shaderOutputViewportIndex = features_12.shaderOutputViewportIndex;
249 vulkan_12_features.pNext = device_create_info_p_next;
250 device_create_info_p_next = &vulkan_12_features;
251
252 /* Enable shader draw parameters on logical device when supported on physical device. */
253 VkPhysicalDeviceShaderDrawParametersFeatures shader_draw_parameters = {};
254 shader_draw_parameters.sType =
255 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES;
256 shader_draw_parameters.shaderDrawParameters = features_11.shaderDrawParameters;
257 shader_draw_parameters.pNext = device_create_info_p_next;
258 device_create_info_p_next = &shader_draw_parameters;
259
260 /* Enable dynamic rendering. */
261 VkPhysicalDeviceDynamicRenderingFeatures dynamic_rendering = {};
262 dynamic_rendering.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES;
263 dynamic_rendering.dynamicRendering = true;
264 dynamic_rendering.pNext = device_create_info_p_next;
265 device_create_info_p_next = &dynamic_rendering;
266
267 VkPhysicalDeviceDynamicRenderingUnusedAttachmentsFeaturesEXT
268 dynamic_rendering_unused_attachments = {};
269 dynamic_rendering_unused_attachments.sType =
270 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_UNUSED_ATTACHMENTS_FEATURES_EXT;
271 dynamic_rendering_unused_attachments.dynamicRenderingUnusedAttachments = VK_TRUE;
272 if (has_extensions({VK_EXT_DYNAMIC_RENDERING_UNUSED_ATTACHMENTS_EXTENSION_NAME})) {
273 dynamic_rendering_unused_attachments.pNext = device_create_info_p_next;
274 device_create_info_p_next = &dynamic_rendering_unused_attachments;
275 }
276
277 /* Query for Mainenance4 (core in Vulkan 1.3). */
278 VkPhysicalDeviceMaintenance4FeaturesKHR maintenance_4 = {};
279 maintenance_4.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_4_FEATURES_KHR;
280 maintenance_4.maintenance4 = VK_TRUE;
281 if (has_extensions({VK_KHR_MAINTENANCE_4_EXTENSION_NAME})) {
282 maintenance_4.pNext = device_create_info_p_next;
283 device_create_info_p_next = &maintenance_4;
284 }
285
286 /* Query and enable Fragment Shader Barycentrics. */
287 VkPhysicalDeviceFragmentShaderBarycentricFeaturesKHR fragment_shader_barycentric = {};
288 fragment_shader_barycentric.sType =
289 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_BARYCENTRIC_FEATURES_KHR;
290 fragment_shader_barycentric.fragmentShaderBarycentric = VK_TRUE;
291 if (has_extensions({VK_KHR_FRAGMENT_SHADER_BARYCENTRIC_EXTENSION_NAME})) {
292 fragment_shader_barycentric.pNext = device_create_info_p_next;
293 device_create_info_p_next = &fragment_shader_barycentric;
294 }
295
296 device_create_info.pNext = device_create_info_p_next;
297 vkCreateDevice(physical_device, &device_create_info, nullptr, &device);
298 }
299
301 {
302 uint32_t queue_family_count = 0;
303 vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_count, nullptr);
304
305 vector<VkQueueFamilyProperties> queue_families(queue_family_count);
306 vkGetPhysicalDeviceQueueFamilyProperties(
307 physical_device, &queue_family_count, queue_families.data());
308
310 for (const auto &queue_family : queue_families) {
311 /* Every VULKAN implementation by spec must have one queue family that support both graphics
312 * and compute pipelines. We select this one; compute only queue family hints at asynchronous
313 * compute implementations. */
314 if ((queue_family.queueFlags & VK_QUEUE_GRAPHICS_BIT) &&
315 (queue_family.queueFlags & VK_QUEUE_COMPUTE_BIT))
316 {
317 return;
318 }
320 }
321
322 fprintf(stderr, "Couldn't find any Graphic queue family on selected device\n");
323 }
324};
325
333static std::optional<GHOST_DeviceVK> vulkan_device;
334
335static GHOST_TSuccess ensure_vulkan_device(VkInstance vk_instance,
336 VkSurfaceKHR vk_surface,
337 const GHOST_GPUDevice &preferred_device,
338 const vector<const char *> &required_extensions)
339{
340 if (vulkan_device.has_value()) {
341 return GHOST_kSuccess;
342 }
343
344 VkPhysicalDevice best_physical_device = VK_NULL_HANDLE;
345
346 uint32_t device_count = 0;
347 vkEnumeratePhysicalDevices(vk_instance, &device_count, nullptr);
348
349 vector<VkPhysicalDevice> physical_devices(device_count);
350 vkEnumeratePhysicalDevices(vk_instance, &device_count, physical_devices.data());
351
352 int best_device_score = -1;
353 int device_index = -1;
354 for (const auto &physical_device : physical_devices) {
355 GHOST_DeviceVK device_vk(vk_instance, physical_device);
356 device_index++;
357
358 if (!device_vk.has_extensions(required_extensions)) {
359 continue;
360 }
361
362 if (vk_surface != VK_NULL_HANDLE) {
363 uint32_t format_count;
364 vkGetPhysicalDeviceSurfaceFormatsKHR(
365 device_vk.physical_device, vk_surface, &format_count, nullptr);
366
367 uint32_t present_count;
368 vkGetPhysicalDeviceSurfacePresentModesKHR(
369 device_vk.physical_device, vk_surface, &present_count, nullptr);
370
371 /* For now anything will do. */
372 if (format_count == 0 || present_count == 0) {
373 continue;
374 }
375 }
376
377#ifdef __APPLE__
378 if (!device_vk.features.features.dualSrcBlend || !device_vk.features.features.imageCubeArray) {
379 continue;
380 }
381#else
382 if (!device_vk.features.features.geometryShader || !device_vk.features.features.dualSrcBlend ||
383 !device_vk.features.features.logicOp || !device_vk.features.features.imageCubeArray)
384 {
385 continue;
386 }
387#endif
388
389 int device_score = 0;
390 switch (device_vk.properties.deviceType) {
391 case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
392 device_score = 400;
393 break;
394 case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
395 device_score = 300;
396 break;
397 case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
398 device_score = 200;
399 break;
400 case VK_PHYSICAL_DEVICE_TYPE_CPU:
401 device_score = 100;
402 break;
403 default:
404 break;
405 }
406 /* User has configured a preferred device. Add bonus score when vendor and device match. Driver
407 * id isn't considered as drivers update more frequently and can break the device selection. */
408 if (device_vk.properties.deviceID == preferred_device.device_id &&
409 device_vk.properties.vendorID == preferred_device.vendor_id)
410 {
411 device_score += 500;
412 if (preferred_device.index == device_index) {
413 device_score += 10;
414 }
415 }
416 if (device_score > best_device_score) {
417 best_physical_device = physical_device;
418 best_device_score = device_score;
419 }
420 }
421
422 if (best_physical_device == VK_NULL_HANDLE) {
423 fprintf(stderr, "Error: No suitable Vulkan Device found!\n");
424 return GHOST_kFailure;
425 }
426
427 vulkan_device.emplace(vk_instance, best_physical_device);
428
429 return GHOST_kSuccess;
430}
431
435#ifdef _WIN32
436 HWND hwnd,
437#elif defined(__APPLE__)
438 CAMetalLayer *metal_layer,
439#else
441 /* X11 */
442 Window window,
443 Display *display,
444 /* Wayland */
445 wl_surface *wayland_surface,
446 wl_display *wayland_display,
447 const GHOST_ContextVK_WindowInfo *wayland_window_info,
448#endif
449 int contextMajorVersion,
450 int contextMinorVersion,
451 int debug,
452 const GHOST_GPUDevice &preferred_device)
453 : GHOST_Context(stereoVisual),
454#ifdef _WIN32
455 m_hwnd(hwnd),
456#elif defined(__APPLE__)
457 m_metal_layer(metal_layer),
458#else
459 m_platform(platform),
460 /* X11 */
461 m_display(display),
462 m_window(window),
463 /* Wayland */
464 m_wayland_surface(wayland_surface),
465 m_wayland_display(wayland_display),
466 m_wayland_window_info(wayland_window_info),
467#endif
468 m_context_major_version(contextMajorVersion),
469 m_context_minor_version(contextMinorVersion),
470 m_debug(debug),
471 m_preferred_device(preferred_device),
472 m_command_pool(VK_NULL_HANDLE),
473 m_command_buffer(VK_NULL_HANDLE),
474 m_surface(VK_NULL_HANDLE),
475 m_swapchain(VK_NULL_HANDLE),
476 m_fence(VK_NULL_HANDLE)
477{
478}
479
481{
482 if (vulkan_device.has_value()) {
483 GHOST_DeviceVK &device_vk = *vulkan_device;
484 device_vk.wait_idle();
485
486 destroySwapchain();
487
488 if (m_command_buffer != VK_NULL_HANDLE) {
489 vkFreeCommandBuffers(device_vk.device, m_command_pool, 1, &m_command_buffer);
490 m_command_buffer = VK_NULL_HANDLE;
491 }
492 if (m_command_pool != VK_NULL_HANDLE) {
493 vkDestroyCommandPool(device_vk.device, m_command_pool, nullptr);
494 }
495 if (m_surface != VK_NULL_HANDLE) {
496 vkDestroySurfaceKHR(device_vk.instance, m_surface, nullptr);
497 }
498
499 device_vk.users--;
500 if (device_vk.users == 0) {
501 vulkan_device.reset();
502 }
503 }
504}
505
506GHOST_TSuccess GHOST_ContextVK::destroySwapchain()
507{
508 assert(vulkan_device.has_value() && vulkan_device->device != VK_NULL_HANDLE);
509 VkDevice device = vulkan_device->device;
510
511 if (m_swapchain != VK_NULL_HANDLE) {
512 vkDestroySwapchainKHR(device, m_swapchain, nullptr);
513 }
514 if (m_fence != VK_NULL_HANDLE) {
515 vkDestroyFence(device, m_fence, nullptr);
516 m_fence = VK_NULL_HANDLE;
517 }
518 return GHOST_kSuccess;
519}
520
522{
523 if (m_swapchain == VK_NULL_HANDLE) {
524 return GHOST_kFailure;
525 }
526
527#ifdef WITH_GHOST_WAYLAND
528 /* Wayland doesn't provide a WSI with windowing capabilities, therefore cannot detect whether the
529 * swap-chain needs to be recreated. But as a side effect we can recreate the swap chain before
530 * presenting. */
531 if (m_wayland_window_info) {
532 const bool recreate_swapchain =
533 ((m_wayland_window_info->size[0] !=
534 std::max(m_render_extent.width, m_render_extent_min.width)) ||
535 (m_wayland_window_info->size[1] !=
536 std::max(m_render_extent.height, m_render_extent_min.height)));
537
538 if (recreate_swapchain) {
539 /* Swap-chain is out of date. Recreate swap-chain. */
540 destroySwapchain();
541 createSwapchain();
542 }
543 }
544#endif
545
546 assert(vulkan_device.has_value() && vulkan_device->device != VK_NULL_HANDLE);
547 VkDevice device = vulkan_device->device;
548 vkAcquireNextImageKHR(device, m_swapchain, UINT64_MAX, VK_NULL_HANDLE, m_fence, &s_currentImage);
549 VK_CHECK(vkWaitForFences(device, 1, &m_fence, VK_TRUE, UINT64_MAX));
550 VK_CHECK(vkResetFences(device, 1, &m_fence));
551
552 GHOST_VulkanSwapChainData swap_chain_data;
553 swap_chain_data.swap_chain_index = s_currentImage;
554 swap_chain_data.image = m_swapchain_images[s_currentImage];
555 swap_chain_data.format = m_surface_format.format;
556 swap_chain_data.extent = m_render_extent;
557
558 if (swap_buffers_pre_callback_) {
559 swap_buffers_pre_callback_(&swap_chain_data);
560 }
561
562 VkPresentInfoKHR present_info = {};
563 present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
564 present_info.waitSemaphoreCount = 0;
565 present_info.pWaitSemaphores = nullptr;
566 present_info.swapchainCount = 1;
567 present_info.pSwapchains = &m_swapchain;
568 present_info.pImageIndices = &s_currentImage;
569 present_info.pResults = nullptr;
570
571 VkResult result = VK_SUCCESS;
572 {
573 std::scoped_lock lock(vulkan_device->queue_mutex);
574 result = vkQueuePresentKHR(m_present_queue, &present_info);
575 }
576 if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) {
577 /* Swap-chain is out of date. Recreate swap-chain and skip this frame. */
578 destroySwapchain();
579 createSwapchain();
580 if (swap_buffers_post_callback_) {
581 swap_buffers_post_callback_();
582 }
583 return GHOST_kSuccess;
584 }
585 else if (result != VK_SUCCESS) {
586 fprintf(stderr,
587 "Error: Failed to present swap chain image : %s\n",
588 vulkan_error_as_string(result));
589 if (swap_buffers_post_callback_) {
590 swap_buffers_post_callback_();
591 }
592 return GHOST_kFailure;
593 }
594
595 s_currentImage = (s_currentImage + 1) % m_swapchain_images.size();
596
597 if (swap_buffers_post_callback_) {
598 swap_buffers_post_callback_();
599 }
600
601 return GHOST_kSuccess;
602}
603
605 GHOST_VulkanSwapChainData *r_swap_chain_data)
606{
607 r_swap_chain_data->swap_chain_index = s_currentImage;
608 r_swap_chain_data->image = VK_NULL_HANDLE;
609 r_swap_chain_data->format = m_surface_format.format;
610 r_swap_chain_data->extent = m_render_extent;
611
612 return GHOST_kSuccess;
613}
614
616 void *r_physical_device,
617 void *r_device,
618 uint32_t *r_graphic_queue_family,
619 void *r_queue,
620 void **r_queue_mutex)
621{
622 *((VkInstance *)r_instance) = VK_NULL_HANDLE;
623 *((VkPhysicalDevice *)r_physical_device) = VK_NULL_HANDLE;
624 *((VkDevice *)r_device) = VK_NULL_HANDLE;
625
626 if (vulkan_device.has_value()) {
627 *((VkInstance *)r_instance) = vulkan_device->instance;
628 *((VkPhysicalDevice *)r_physical_device) = vulkan_device->physical_device;
629 *((VkDevice *)r_device) = vulkan_device->device;
630 *r_graphic_queue_family = vulkan_device->generic_queue_family;
631 std::mutex **queue_mutex = (std::mutex **)r_queue_mutex;
632 *queue_mutex = &vulkan_device->queue_mutex;
633 }
634
635 *((VkQueue *)r_queue) = m_graphic_queue;
636
637 return GHOST_kSuccess;
638}
639
641 std::function<void(const GHOST_VulkanSwapChainData *)> swap_buffers_pre_callback,
642 std::function<void(void)> swap_buffers_post_callback)
643{
644 swap_buffers_pre_callback_ = swap_buffers_pre_callback;
645 swap_buffers_post_callback_ = swap_buffers_post_callback;
646 return GHOST_kSuccess;
647}
648
653
658
660{
661 uint32_t extension_count = 0;
662 vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, nullptr);
663
664 vector<VkExtensionProperties> extensions(extension_count);
665 vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, extensions.data());
666
667 return extensions;
668}
669
670static bool checkExtensionSupport(const vector<VkExtensionProperties> &extensions_available,
671 const char *extension_name)
672{
673 for (const auto &extension : extensions_available) {
674 if (strcmp(extension_name, extension.extensionName) == 0) {
675 return true;
676 }
677 }
678 return false;
679}
680
681static void requireExtension(const vector<VkExtensionProperties> &extensions_available,
682 vector<const char *> &extensions_enabled,
683 const char *extension_name)
684{
685 if (checkExtensionSupport(extensions_available, extension_name)) {
686 extensions_enabled.push_back(extension_name);
687 }
688 else {
689 fprintf(stderr, "Error: %s not found.\n", extension_name);
690 }
691}
692
693static GHOST_TSuccess selectPresentMode(VkPhysicalDevice device,
694 VkSurfaceKHR surface,
695 VkPresentModeKHR *r_presentMode)
696{
697 // TODO cleanup: we are not going to use MAILBOX as it isn't supported by renderdoc.
698 uint32_t present_count;
699 vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &present_count, nullptr);
700 vector<VkPresentModeKHR> presents(present_count);
701 vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &present_count, presents.data());
702 /* MAILBOX is the lowest latency V-Sync enabled mode so use it if available */
703 for (auto present_mode : presents) {
704 if (present_mode == VK_PRESENT_MODE_FIFO_KHR) {
705 *r_presentMode = present_mode;
706 return GHOST_kSuccess;
707 }
708 }
709 /* FIFO present mode is always available. */
710 for (auto present_mode : presents) {
711 if (present_mode == VK_PRESENT_MODE_MAILBOX_KHR) {
712 *r_presentMode = present_mode;
713 return GHOST_kSuccess;
714 }
715 }
716
717 fprintf(stderr, "Error: FIFO present mode is not supported by the swap chain!\n");
718
719 return GHOST_kFailure;
720}
721
722GHOST_TSuccess GHOST_ContextVK::createCommandPools()
723{
724 assert(vulkan_device.has_value() && vulkan_device->device != VK_NULL_HANDLE);
725 VkCommandPoolCreateInfo poolInfo = {};
726 poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
727 poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
728 poolInfo.queueFamilyIndex = vulkan_device->generic_queue_family;
729
730 VK_CHECK(vkCreateCommandPool(vulkan_device->device, &poolInfo, nullptr, &m_command_pool));
731 return GHOST_kSuccess;
732}
733
734GHOST_TSuccess GHOST_ContextVK::createGraphicsCommandBuffer()
735{
736 assert(vulkan_device.has_value() && vulkan_device->device != VK_NULL_HANDLE);
737 assert(m_command_pool != VK_NULL_HANDLE);
738 assert(m_command_buffer == VK_NULL_HANDLE);
739 VkCommandBufferAllocateInfo alloc_info = {};
740 alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
741 alloc_info.commandPool = m_command_pool;
742 alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
743 alloc_info.commandBufferCount = 1;
744
745 VK_CHECK(vkAllocateCommandBuffers(vulkan_device->device, &alloc_info, &m_command_buffer));
746 return GHOST_kSuccess;
747}
748
749static bool surfaceFormatSupported(const VkSurfaceFormatKHR &surface_format)
750{
751 if (surface_format.colorSpace != VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
752 return false;
753 }
754
755 if (surface_format.format == VK_FORMAT_R8G8B8A8_UNORM ||
756 surface_format.format == VK_FORMAT_B8G8R8A8_UNORM)
757 {
758 return true;
759 }
760
761 return false;
762}
763
769static bool selectSurfaceFormat(const VkPhysicalDevice physical_device,
770 const VkSurfaceKHR surface,
771 VkSurfaceFormatKHR &r_surfaceFormat)
772{
773 uint32_t format_count;
774 vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &format_count, nullptr);
775 vector<VkSurfaceFormatKHR> formats(format_count);
776 vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &format_count, formats.data());
777
778 for (const VkSurfaceFormatKHR &format : formats) {
780 r_surfaceFormat = format;
781 return true;
782 }
783 }
784
785#if !SELECT_COMPATIBLE_SURFACES_ONLY
786 r_surfaceFormat = formats[0];
787#endif
788
789 return false;
790}
791
792GHOST_TSuccess GHOST_ContextVK::createSwapchain()
793{
794 assert(vulkan_device.has_value() && vulkan_device->device != VK_NULL_HANDLE);
795
796 VkPhysicalDevice physical_device = vulkan_device->physical_device;
797
798 m_surface_format = {};
799#if SELECT_COMPATIBLE_SURFACES_ONLY
800 if (!selectSurfaceFormat(physical_device, m_surface, m_surface_format)) {
801 return GHOST_kFailure;
802 }
803#else
804 selectSurfaceFormat(physical_device, m_surface, m_surface_format);
805#endif
806
807 VkPresentModeKHR present_mode;
808 if (!selectPresentMode(physical_device, m_surface, &present_mode)) {
809 return GHOST_kFailure;
810 }
811
812 VkSurfaceCapabilitiesKHR capabilities;
813 vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, m_surface, &capabilities);
814
815 m_render_extent = capabilities.currentExtent;
816 m_render_extent_min = capabilities.minImageExtent;
817 if (m_render_extent.width == UINT32_MAX) {
818 /* Window Manager is going to set the surface size based on the given size.
819 * Choose something between minImageExtent and maxImageExtent. */
820 int width = 0;
821 int height = 0;
822
823#ifdef WITH_GHOST_WAYLAND
824 /* Wayland doesn't provide a windowing API via WSI. */
825 if (m_wayland_window_info) {
826 width = m_wayland_window_info->size[0];
827 height = m_wayland_window_info->size[1];
828 }
829#endif
830
831 if (width == 0 || height == 0) {
832 width = 1280;
833 height = 720;
834 }
835
836 m_render_extent.width = width;
837 m_render_extent.height = height;
838
839 if (capabilities.minImageExtent.width > m_render_extent.width) {
840 m_render_extent.width = capabilities.minImageExtent.width;
841 }
842 if (capabilities.minImageExtent.height > m_render_extent.height) {
843 m_render_extent.height = capabilities.minImageExtent.height;
844 }
845 }
846
847 /* Driver can stall if only using minimal image count. */
848 uint32_t image_count = capabilities.minImageCount + 1;
849 /* NOTE: maxImageCount == 0 means no limit. */
850 if (image_count > capabilities.maxImageCount && capabilities.maxImageCount > 0) {
851 image_count = capabilities.maxImageCount;
852 }
853 if (capabilities.minImageCount <= 3 && image_count > 3) {
854 image_count = 3;
855 }
856
857 VkSwapchainCreateInfoKHR create_info = {};
858 create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
859 create_info.surface = m_surface;
860 create_info.minImageCount = image_count;
861 create_info.imageFormat = m_surface_format.format;
862 create_info.imageColorSpace = m_surface_format.colorSpace;
863 create_info.imageExtent = m_render_extent;
864 create_info.imageArrayLayers = 1;
865 create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
866 create_info.preTransform = capabilities.currentTransform;
867 create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
868 create_info.presentMode = present_mode;
869 create_info.clipped = VK_TRUE;
870 create_info.oldSwapchain = VK_NULL_HANDLE; /* TODO Window resize */
871 create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
872 create_info.queueFamilyIndexCount = 0;
873 create_info.pQueueFamilyIndices = nullptr;
874
875 VkDevice device = vulkan_device->device;
876 VK_CHECK(vkCreateSwapchainKHR(device, &create_info, nullptr, &m_swapchain));
877
878 /* image_count may not be what we requested! Getter for final value. */
879 vkGetSwapchainImagesKHR(device, m_swapchain, &image_count, nullptr);
880 m_swapchain_images.resize(image_count);
881 vkGetSwapchainImagesKHR(device, m_swapchain, &image_count, m_swapchain_images.data());
882
883 VkFenceCreateInfo fence_info = {};
884 fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
885 VK_CHECK(vkCreateFence(device, &fence_info, nullptr, &m_fence));
886
887 /* Change image layout from VK_IMAGE_LAYOUT_UNDEFINED to VK_IMAGE_LAYOUT_PRESENT_SRC_KHR. */
888 VkCommandBufferBeginInfo begin_info = {};
889 begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
890 VK_CHECK(vkBeginCommandBuffer(m_command_buffer, &begin_info));
891 VkImageMemoryBarrier *barriers = new VkImageMemoryBarrier[image_count];
892 for (int i = 0; i < image_count; i++) {
893 VkImageMemoryBarrier &barrier = barriers[i];
894 barrier = {};
895
896 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
897 barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
898 barrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
899 barrier.image = m_swapchain_images[i];
900 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
901 barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
902 barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
903 }
904 vkCmdPipelineBarrier(m_command_buffer,
905 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
906 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
907 VK_DEPENDENCY_BY_REGION_BIT,
908 0,
909 nullptr,
910 0,
911 nullptr,
912 image_count,
913 barriers);
914 VK_CHECK(vkEndCommandBuffer(m_command_buffer));
915
916 VkPipelineStageFlags wait_stages[] = {VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT};
917 VkSubmitInfo submit_info = {};
918 submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
919 submit_info.pWaitDstStageMask = wait_stages;
920 submit_info.commandBufferCount = 1;
921 submit_info.pCommandBuffers = &m_command_buffer;
922 submit_info.signalSemaphoreCount = 0;
923 submit_info.pSignalSemaphores = nullptr;
924 VK_CHECK(vkQueueSubmit(m_graphic_queue, 1, &submit_info, nullptr));
925 VK_CHECK(vkQueueWaitIdle(m_graphic_queue));
926
927 delete[] barriers;
928
929 return GHOST_kSuccess;
930}
931
932const char *GHOST_ContextVK::getPlatformSpecificSurfaceExtension() const
933{
934#ifdef _WIN32
935 return VK_KHR_WIN32_SURFACE_EXTENSION_NAME;
936#elif defined(__APPLE__)
937 return VK_EXT_METAL_SURFACE_EXTENSION_NAME;
938#else /* UNIX/Linux */
939 switch (m_platform) {
940# ifdef WITH_GHOST_X11
941 case GHOST_kVulkanPlatformX11:
942 return VK_KHR_XLIB_SURFACE_EXTENSION_NAME;
943 break;
944# endif
945# ifdef WITH_GHOST_WAYLAND
946 case GHOST_kVulkanPlatformWayland:
947 return VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME;
948 break;
949# endif
950 }
951#endif
952 return nullptr;
953}
954
956{
957#ifdef _WIN32
958 const bool use_window_surface = (m_hwnd != nullptr);
959#elif defined(__APPLE__)
960 const bool use_window_surface = (m_metal_layer != nullptr);
961#else /* UNIX/Linux */
962 bool use_window_surface = false;
963 switch (m_platform) {
964# ifdef WITH_GHOST_X11
965 case GHOST_kVulkanPlatformX11:
966 use_window_surface = (m_display != nullptr) && (m_window != (Window) nullptr);
967 break;
968# endif
969# ifdef WITH_GHOST_WAYLAND
970 case GHOST_kVulkanPlatformWayland:
971 use_window_surface = (m_wayland_display != nullptr) && (m_wayland_surface != nullptr);
972 break;
973# endif
974 }
975#endif
976
977 std::vector<VkExtensionProperties> extensions_available = getExtensionsAvailable();
978 vector<const char *> required_device_extensions;
979 vector<const char *> optional_device_extensions;
980 vector<const char *> extensions_enabled;
981
982 if (m_debug) {
983 requireExtension(extensions_available, extensions_enabled, VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
984 }
985
986 if (use_window_surface) {
987 const char *native_surface_extension_name = getPlatformSpecificSurfaceExtension();
988 requireExtension(extensions_available, extensions_enabled, VK_KHR_SURFACE_EXTENSION_NAME);
989 requireExtension(extensions_available, extensions_enabled, native_surface_extension_name);
990
991 required_device_extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
992 }
993 required_device_extensions.push_back(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME);
994 /* NOTE: marking this as an optional extension, but is actually required. RenderDoc doesn't
995 * create a device with this extension, but seems to work when not requesting the extension.
996 */
997 optional_device_extensions.push_back(VK_EXT_DYNAMIC_RENDERING_UNUSED_ATTACHMENTS_EXTENSION_NAME);
998 optional_device_extensions.push_back(VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME);
999 optional_device_extensions.push_back(VK_KHR_MAINTENANCE_4_EXTENSION_NAME);
1000 optional_device_extensions.push_back(VK_KHR_FRAGMENT_SHADER_BARYCENTRIC_EXTENSION_NAME);
1001
1002 /* Enable MoltenVK required instance extensions. */
1003#ifdef __APPLE__
1005 extensions_available, extensions_enabled, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
1006#endif
1007
1008 VkInstance instance = VK_NULL_HANDLE;
1009 if (!vulkan_device.has_value()) {
1010 VkApplicationInfo app_info = {};
1011 app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
1012 app_info.pApplicationName = "Blender";
1013 app_info.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
1014 app_info.pEngineName = "Blender";
1015 app_info.engineVersion = VK_MAKE_VERSION(1, 0, 0);
1016 app_info.apiVersion = VK_MAKE_VERSION(m_context_major_version, m_context_minor_version, 0);
1017
1018 /* Create Instance */
1019 VkInstanceCreateInfo create_info = {};
1020 create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
1021 create_info.pApplicationInfo = &app_info;
1022 create_info.enabledExtensionCount = uint32_t(extensions_enabled.size());
1023 create_info.ppEnabledExtensionNames = extensions_enabled.data();
1024
1025 /* VkValidationFeaturesEXT */
1026 VkValidationFeaturesEXT validationFeatures = {};
1027 validationFeatures.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT;
1028 validationFeatures.enabledValidationFeatureCount = 1;
1029
1030 VkValidationFeatureEnableEXT enabledValidationFeatures[1] = {
1031 VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT};
1032 validationFeatures.pEnabledValidationFeatures = enabledValidationFeatures;
1033 if (m_debug) {
1034 create_info.pNext = &validationFeatures;
1035 }
1036
1037#ifdef __APPLE__
1038 create_info.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
1039#endif
1040
1041 VK_CHECK(vkCreateInstance(&create_info, nullptr, &instance));
1042 }
1043 else {
1044 instance = vulkan_device->instance;
1045 }
1046
1047 if (use_window_surface) {
1048#ifdef _WIN32
1049 VkWin32SurfaceCreateInfoKHR surface_create_info = {};
1050 surface_create_info.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
1051 surface_create_info.hinstance = GetModuleHandle(nullptr);
1052 surface_create_info.hwnd = m_hwnd;
1053 VK_CHECK(vkCreateWin32SurfaceKHR(instance, &surface_create_info, nullptr, &m_surface));
1054#elif defined(__APPLE__)
1055 VkMetalSurfaceCreateInfoEXT info = {};
1056 info.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT;
1057 info.pNext = nullptr;
1058 info.flags = 0;
1059 info.pLayer = m_metal_layer;
1060 VK_CHECK(vkCreateMetalSurfaceEXT(instance, &info, nullptr, &m_surface));
1061#else
1062 switch (m_platform) {
1063# ifdef WITH_GHOST_X11
1064 case GHOST_kVulkanPlatformX11: {
1065 VkXlibSurfaceCreateInfoKHR surface_create_info = {};
1066 surface_create_info.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
1067 surface_create_info.dpy = m_display;
1068 surface_create_info.window = m_window;
1069 VK_CHECK(vkCreateXlibSurfaceKHR(instance, &surface_create_info, nullptr, &m_surface));
1070 break;
1071 }
1072# endif
1073# ifdef WITH_GHOST_WAYLAND
1074 case GHOST_kVulkanPlatformWayland: {
1075 VkWaylandSurfaceCreateInfoKHR surface_create_info = {};
1076 surface_create_info.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR;
1077 surface_create_info.display = m_wayland_display;
1078 surface_create_info.surface = m_wayland_surface;
1079 VK_CHECK(vkCreateWaylandSurfaceKHR(instance, &surface_create_info, nullptr, &m_surface));
1080 break;
1081 }
1082# endif
1083 }
1084
1085#endif
1086 }
1087
1088 if (!ensure_vulkan_device(instance, m_surface, m_preferred_device, required_device_extensions)) {
1089 return GHOST_kFailure;
1090 }
1091
1092 vulkan_device->users++;
1093 vulkan_device->ensure_device(required_device_extensions, optional_device_extensions);
1094
1095 vkGetDeviceQueue(
1096 vulkan_device->device, vulkan_device->generic_queue_family, 0, &m_graphic_queue);
1097
1098 createCommandPools();
1099 createGraphicsCommandBuffer();
1100 if (use_window_surface) {
1101 vkGetDeviceQueue(
1102 vulkan_device->device, vulkan_device->generic_queue_family, 0, &m_present_queue);
1103 createSwapchain();
1104 }
1105
1106 return GHOST_kSuccess;
1107}
1108
#define VK_CHECK(__expression)
static bool surfaceFormatSupported(const VkSurfaceFormatKHR &surface_format)
static bool checkExtensionSupport(const vector< VkExtensionProperties > &extensions_available, const char *extension_name)
static const char * vulkan_error_as_string(VkResult result)
static void requireExtension(const vector< VkExtensionProperties > &extensions_available, vector< const char * > &extensions_enabled, const char *extension_name)
#define FORMAT_ERROR(X)
static GHOST_TSuccess ensure_vulkan_device(VkInstance vk_instance, VkSurfaceKHR vk_surface, const GHOST_GPUDevice &preferred_device, const vector< const char * > &required_extensions)
static bool selectSurfaceFormat(const VkPhysicalDevice physical_device, const VkSurfaceKHR surface, VkSurfaceFormatKHR &r_surfaceFormat)
static vector< VkExtensionProperties > getExtensionsAvailable()
static std::optional< GHOST_DeviceVK > vulkan_device
static GHOST_TSuccess selectPresentMode(VkPhysicalDevice device, VkSurfaceKHR surface, VkPresentModeKHR *r_presentMode)
GHOST_TVulkanPlatformType
#define wl_display
#define wl_surface
GHOST_TSuccess
Definition GHOST_Types.h:87
@ GHOST_kFailure
Definition GHOST_Types.h:87
@ GHOST_kSuccess
Definition GHOST_Types.h:87
volatile int lock
GHOST_TSuccess activateDrawingContext() override
GHOST_TSuccess swapBuffers() override
GHOST_TSuccess getVulkanSwapChainFormat(GHOST_VulkanSwapChainData *r_swap_chain_data) override
GHOST_TSuccess getVulkanHandles(void *r_instance, void *r_physical_device, void *r_device, uint32_t *r_graphic_queue_family, void *r_queue, void **r_queue_mutex) override
GHOST_TSuccess initializeDrawingContext() override
GHOST_TSuccess releaseDrawingContext() override
GHOST_TSuccess releaseNativeHandles() override
GHOST_TSuccess setVulkanSwapBuffersCallbacks(std::function< void(const GHOST_VulkanSwapChainData *)> swap_buffers_pre_callback, std::function< void(void)> swap_buffers_post_callback) override
GHOST_ContextVK(bool stereoVisual, GHOST_TVulkanPlatformType platform, Window window, Display *display, wl_surface *wayland_surface, wl_display *wayland_display, const GHOST_ContextVK_WindowInfo *wayland_window_info, int contextMajorVersion, int contextMinorVersion, int debug, const GHOST_GPUDevice &preferred_device)
bool has_extensions(const vector< const char * > &required_extensions)
GHOST_DeviceVK(VkInstance vk_instance, VkPhysicalDevice vk_physical_device)
void init_generic_queue_family()
VkPhysicalDeviceFeatures2 features
VkPhysicalDevice physical_device
uint32_t generic_queue_family
VkPhysicalDeviceVulkan12Features features_12
VkPhysicalDeviceProperties properties
void ensure_device(vector< const char * > &required_extensions, vector< const char * > &optional_extensions)
std::mutex queue_mutex
VkPhysicalDeviceVulkan11Features features_11
format
#define UINT64_MAX
Definition stdint.h:143
unsigned int uint32_t
Definition stdint.h:80
#define UINT32_MAX
Definition stdint.h:142