Vulkan Schnee 0.0.1
High-performance rendering engine
Loading...
Searching...
No Matches
ApplicationContext.cpp
Go to the documentation of this file.
4
8#include <GLFW/glfw3.h>
10#include <openxr/openxr.h>
11#include <plog/Log.h>
13
14#ifdef ENABLE_TRACY
15#include <tracy/TracyVulkan.hpp>
16#endif
17
18
19namespace EngineCore
20{
21
22constexpr XrViewConfigurationType xrViewType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO;
23constexpr XrEnvironmentBlendMode environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE;
24
26{
28
29#if !defined(HEADLESS) && !defined(COMPUTE_DEBUG)
31 {
33 {
35 break;
36 }
38 {
39 createOpenXrInstance("C:/Program Files/Oculus/Support/oculus-runtime/oculus_openxr_64.json");
40 break;
41 }
42 }
43
45#ifdef IS_IN_DEBUG
46 createOpenXrDebugMessenger();
47#endif
51#else
52 PLOGI << "Running in HEADLESS/COMPUTE_DEBUG mode - skipping OpenXR initialization";
54#endif
55
57
59}
61{
62 PLOGI << "Destroying Application Context";
63
64 // Destroy VMA allocator before device
65 if (vmaAllocator_ != VK_NULL_HANDLE)
66 {
67 vmaDestroyAllocator(vmaAllocator_);
68 vmaAllocator_ = VK_NULL_HANDLE;
69 PLOGI << "VMA allocator destroyed";
70 }
71
72 vkDestroyDevice(vkDevice, nullptr);
73 vkDestroyInstance(vkInstance, nullptr);
74
75#if !defined(HEADLESS) && !defined(COMPUTE_DEBUG)
76 xrSystemId = XR_NULL_SYSTEM_ID;
77
78#ifdef IS_IN_DEBUG
79 xrDestroyDebugUtilsMessengerEXT(xrDebugMessenger);
80#endif
81
82 XrResult destroyXrInstanceResult = xrDestroyInstance(xrInstance);
83 if(destroyXrInstanceResult != XR_SUCCESS)
84 {
85 PLOGE_XR << "Failed to destroy xr instance!";
86 }
87#endif
88}
93
94void ApplicationContext::createDevice(VkSurfaceKHR mirrorSurface)
95{
97 // pick Vulkan Graphics Device
98 {
99 XrVulkanGraphicsDeviceGetInfoKHR xrVulkanGraphicsDeviceGetInfo{
100 .type = XR_TYPE_VULKAN_GRAPHICS_DEVICE_GET_INFO_KHR,
101 .next = nullptr,
102 .systemId = xrSystemId,
103 .vulkanInstance = vkInstance,
104 };
105 PLOG_FN_XR(xrGetVulkanGraphicsDevice2KHR(xrInstance, &xrVulkanGraphicsDeviceGetInfo, &vkPhysicalDevice));
106
107 PLOGI << "Picked vulkan graphics device";
108 }
109
110 pickQueueFamilies(vkPhysicalDevice, mirrorSurface, queueFamily.graphics.index, queueFamily.present.index, queueFamily.transfer.index);
111
112 // getSupportedVulkanDeviceExtensions
113 {
114 uint32_t deviceExtensionCount;
115 PLOG_FN_VK(vkEnumerateDeviceExtensionProperties(vkPhysicalDevice, nullptr, &deviceExtensionCount, nullptr));
116
117 supportedVulkanDeviceExtensions.resize(deviceExtensionCount);
118 PLOG_FN_VK(vkEnumerateDeviceExtensionProperties(vkPhysicalDevice, nullptr, &deviceExtensionCount, supportedVulkanDeviceExtensions.data()));
119
120 for (const VkExtensionProperties& extension : supportedVulkanDeviceExtensions)
121 {
122 PLOGV << "Device Extension: " << extension.extensionName;
123 }
124 }
125
126 // getRequiredVulkanDeviceExtensions
127 {
128 vulkanDeviceExtensions.push_back( VK_KHR_SWAPCHAIN_EXTENSION_NAME );
129#if !defined(HEADLESS) && !defined(COMPUTE_DEBUG)
130 vulkanDeviceExtensions.push_back( VK_EXT_MESH_SHADER_EXTENSION_NAME );
131 vulkanDeviceExtensions.push_back( VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME );
132#endif
133
134 // VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME is not needed - promoted to Vulkan 1.3 core
135 vulkanDeviceExtensions.push_back( VK_EXT_CONDITIONAL_RENDERING_EXTENSION_NAME );
136#ifdef ENABLE_TRACY
137 // Tracy profiling extensions (optional - will be checked for support)
138 vulkanDeviceExtensions.push_back( VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME );
139 vulkanDeviceExtensions.push_back( VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME );
140#endif
141 }
142
143 // checkVulkanDeviceExtensionSupport
144 {
145 TRACY_ZONE_SCOPED_NAMED("Check vulkan device extension support");
146 for ( const char * extensionToEnable : vulkanDeviceExtensions )
147 {
148 bool isExtensionSupported = false;
149 for ( const VkExtensionProperties & supportedExtension : supportedVulkanDeviceExtensions )
150 {
151 if ( strcmp( extensionToEnable, supportedExtension.extensionName ) == 0 )
152 {
153 isExtensionSupported = true;
154 break;
155 }
156 }
157
158 if ( !isExtensionSupported )
159 {
160 // Tracy extensions are optional - only warn, don't fail
161 if ( strcmp( extensionToEnable, VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME ) == 0 ||
162 strcmp( extensionToEnable, VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME ) == 0 )
163 {
164 PLOGW << "Optional Tracy extension " << extensionToEnable
165 << " not supported - Tracy will use standard profiling";
166 }
167 else
168 {
169 std::stringstream error;
170 error << "Required Vulkan device extension " << extensionToEnable << " is not supported!";
171 PLOGE << error.str();
172 throw std::runtime_error( error.str() );
173 }
174 }
175 }
176 }
177
178 // pickVulkanDevice
179 {
180 VkPhysicalDeviceProperties physicalDeviceProperties;
181 vkGetPhysicalDeviceProperties(vkPhysicalDevice, &physicalDeviceProperties);
182 uniformBufferOffsetAlignment = physicalDeviceProperties.limits.minUniformBufferOffsetAlignment;
183
184 // Query push constants limit for optimal sizing
185 maxPushConstantsSize = physicalDeviceProperties.limits.maxPushConstantsSize;
186 PLOGI << "Max push constants size: " << maxPushConstantsSize << " bytes";
187
188 const VkSampleCountFlags sampleCountFlags = physicalDeviceProperties.limits.framebufferColorSampleCounts & physicalDeviceProperties.limits.framebufferDepthSampleCounts;
189
190 if (sampleCountFlags & VK_SAMPLE_COUNT_4_BIT)
191 {
192 multisampleCount = VK_SAMPLE_COUNT_4_BIT;
193 }
194 else if (sampleCountFlags & VK_SAMPLE_COUNT_2_BIT)
195 {
196 multisampleCount = VK_SAMPLE_COUNT_2_BIT;
197 }
198
199 // Verify that the required phyiscal device features are supported.
200 VkPhysicalDeviceFeatures physicalDeviceFeatures;
201 vkGetPhysicalDeviceFeatures(vkPhysicalDevice, &physicalDeviceFeatures);
202
203 VkPhysicalDeviceFeatures2 physicalDeviceFeatures2{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2};
204
205#if !defined(HEADLESS) && !defined(COMPUTE_DEBUG)
206 VkPhysicalDeviceFragmentShadingRateFeaturesKHR fragmentShadingRateFeatures{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_FEATURES_KHR};
207 VkPhysicalDeviceMeshShaderFeaturesEXT meshShaderFeatures{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_FEATURES_EXT};
208#endif
209 VkPhysicalDeviceVulkan11Features vulkan11Features {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES};
210 VkPhysicalDeviceVulkan12Features vulkan12Features {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES};
211 VkPhysicalDeviceVulkan13Features vulkan13Features {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES};
212 VkPhysicalDeviceVulkan14Features vulkan14Features {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_4_FEATURES};
213 VkPhysicalDeviceDescriptorIndexingFeatures descriptorIndexingFeatures = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT};
214 VkPhysicalDeviceBufferDeviceAddressFeatures bufferDeviceAddressFeatures {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES};
215
216 Vulkan::ChainBuilder builder{};
217 builder
218 .add(physicalDeviceFeatures2)
219#if !defined(HEADLESS) && !defined(COMPUTE_DEBUG)
220 .add(meshShaderFeatures)
221 .add(fragmentShadingRateFeatures)
222#endif
223 .add(vulkan11Features)
224 .add(vulkan12Features)
225 .add(vulkan13Features)
226 .add(vulkan14Features)
227 .add(descriptorIndexingFeatures)
228 .add(bufferDeviceAddressFeatures);
229
230 vkGetPhysicalDeviceFeatures2(vkPhysicalDevice, &physicalDeviceFeatures2);
231
232#define VALIDATE_FEATURE(feature, is_not_supported_error) \
233 if (!feature) THROW_ERROR(is_not_supported_error); \
234 feature = VK_TRUE;
235
236 VALIDATE_FEATURE(vulkan11Features.multiview, "Multiview not supported")
237 VALIDATE_FEATURE(vulkan11Features.shaderDrawParameters, "Shader draw parameter not supported")
238
239 VALIDATE_FEATURE(vulkan12Features.descriptorIndexing, "Descriptor indexing not supported")
240 VALIDATE_FEATURE(vulkan12Features.descriptorBindingPartiallyBound, "Descriptor binding partially bound not supported")
241 VALIDATE_FEATURE(vulkan12Features.descriptorBindingVariableDescriptorCount, "Descriptor variable descriptor count not supported")
242 VALIDATE_FEATURE(vulkan12Features.timelineSemaphore, "timelineSemaphore is not supported!")
243
244 VALIDATE_FEATURE(vulkan13Features.maintenance4, "maintenance4 is not supported")
245 VALIDATE_FEATURE(vulkan14Features.maintenance5, "maintenance5 is not supported")
246 VALIDATE_FEATURE(vulkan14Features.maintenance6, "maintenance6 is not supported")
247 VALIDATE_FEATURE(vulkan13Features.synchronization2, "Synchronization2 not supported")
248
249 VALIDATE_FEATURE( descriptorIndexingFeatures.runtimeDescriptorArray, "Descriptor indexing not supported" )
250 VALIDATE_FEATURE( descriptorIndexingFeatures.shaderStorageBufferArrayNonUniformIndexing, "Shader storage buffer array non uniform indexing not supported" )
251
252 VALIDATE_FEATURE( bufferDeviceAddressFeatures.bufferDeviceAddress, "Buffer device addresses are not supported" )
253
254#if !defined(HEADLESS) && !defined(COMPUTE_DEBUG)
255 VALIDATE_FEATURE(physicalDeviceFeatures2.features.shaderStorageImageMultisample, "Shader storage image multisample is not supported")
256
257 VALIDATE_FEATURE(fragmentShadingRateFeatures.primitiveFragmentShadingRate, "Primitive shading rate not supported")
258
259 VALIDATE_FEATURE(meshShaderFeatures.meshShader, "meshShader is not supported")
260 VALIDATE_FEATURE(meshShaderFeatures.taskShader, "taskShader is not supported")
261 VALIDATE_FEATURE(meshShaderFeatures.primitiveFragmentShadingRateMeshShader, "primitiveFragmentShadingRateMeshShader is not supported")
262 VALIDATE_FEATURE(meshShaderFeatures.multiviewMeshShader, "multiviewMeshShader is not supported")
263#endif
264
265#undef VALIDATE_FEATURE
266
267 constexpr float queuePriority = 1.0f;
268
269 std::vector<VkDeviceQueueCreateInfo> deviceQueueCreateInfos;
270
271 VkDeviceQueueCreateInfo graphicsAndPresentDeviceQueueCreateInfo{
272 .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
273 .pNext = nullptr,
274 .flags = 0,
275 .queueFamilyIndex = queueFamily.graphics.index,
276 .queueCount = 1u,
277 .pQueuePriorities = &queuePriority,
278 };
279 deviceQueueCreateInfos.push_back(graphicsAndPresentDeviceQueueCreateInfo);
280
281 if (queueFamily.graphics.index != queueFamily.present.index)
282 {
283 graphicsAndPresentDeviceQueueCreateInfo.queueFamilyIndex = queueFamily.present.index;
284 deviceQueueCreateInfos.push_back(graphicsAndPresentDeviceQueueCreateInfo);
285 }
286
287 VkDeviceQueueCreateInfo transferQueueDeviceQueueCreateInfo{
288 .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
289 .pNext = nullptr,
290 .flags = 0u,
291 .queueFamilyIndex = queueFamily.transfer.index,
292 .queueCount = 1u,
293 .pQueuePriorities = &queuePriority,
294 };
295 deviceQueueCreateInfos.push_back(transferQueueDeviceQueueCreateInfo);
296
297 VkDeviceCreateInfo deviceCreateInfo{
298 .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
299 .pNext = &physicalDeviceFeatures2,
300 .flags = 0,
301 .queueCreateInfoCount = static_cast<uint32_t>(deviceQueueCreateInfos.size()),
302 .pQueueCreateInfos = deviceQueueCreateInfos.data(),
303 .enabledLayerCount = 0u,
304 .ppEnabledLayerNames = nullptr,
305 .enabledExtensionCount = static_cast<uint32_t>(vulkanDeviceExtensions.size()),
306 .ppEnabledExtensionNames = vulkanDeviceExtensions.data(),
307 .pEnabledFeatures = nullptr
308 };
309
310 XrVulkanDeviceCreateInfoKHR vkDeviceCreateInfo{
311 .type = XR_TYPE_VULKAN_DEVICE_CREATE_INFO_KHR,
312 .next = nullptr,
313 .systemId = xrSystemId,
314 .createFlags = 0u,
315 .pfnGetInstanceProcAddr = vkGetInstanceProcAddr,
316 .vulkanPhysicalDevice = vkPhysicalDevice,
317 .vulkanCreateInfo = &deviceCreateInfo,
318 .vulkanAllocator = nullptr
319 };
320
321 // load the function dynamcially
322 PFN_xrCreateVulkanDeviceKHR xrCreateVulkanDeviceKHR = nullptr;
324
325 VkResult deviceCreationResult{};
326 PLOG_FN_XR(xrCreateVulkanDeviceKHR(xrInstance, &vkDeviceCreateInfo, &vkDevice, &deviceCreationResult));
327 if (deviceCreationResult != VK_SUCCESS)
328 {
329 THROW_ERROR("Failed to create a vulkan device");
330 }
331
332 PLOGI << "Device Created!";
333 }
334
335 {
336 VkPhysicalDeviceProperties2 deviceProps2 {
337 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
338 };
339
340#if !defined(HEADLESS) && !defined(COMPUTE_DEBUG)
341 VkPhysicalDeviceMeshShaderPropertiesEXT meshShaderProps {
342 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_PROPERTIES_EXT
343 };
344#endif
345
346 Vulkan::ChainBuilder builder{};
347 builder.add(deviceProps2)
348#if !defined(HEADLESS) && !defined(COMPUTE_DEBUG)
349 .add(meshShaderProps)
350#endif
351 ;
352
353 // Query the device properties.
354 vkGetPhysicalDeviceProperties2(vkPhysicalDevice, &deviceProps2);
355
356#if !defined(HEADLESS) && !defined(COMPUTE_DEBUG)
357 // Print out key mesh shader limits.
358
359 PLOGV << "=== Mesh Shader Properties ===";
360 PLOGV << "Max Task WorkGroup Total Count: " << meshShaderProps.maxTaskWorkGroupTotalCount;
361 PLOGV << "Max Task WorkGroup Count: ("
362 << meshShaderProps.maxTaskWorkGroupCount[0] << ", "
363 << meshShaderProps.maxTaskWorkGroupCount[1] << ", "
364 << meshShaderProps.maxTaskWorkGroupCount[2] << ")";
365 PLOGV << "Max Task WorkGroup Invocations: " << meshShaderProps.maxTaskWorkGroupInvocations;
366 PLOGV << "Max Task WorkGroup Size: ("
367 << meshShaderProps.maxTaskWorkGroupSize[0] << ", "
368 << meshShaderProps.maxTaskWorkGroupSize[1] << ", "
369 << meshShaderProps.maxTaskWorkGroupSize[2] << ")";
370 PLOGV << "Max Task Payload Size: " << meshShaderProps.maxTaskPayloadSize;
371 PLOGV << "Max Task Shared Memory Size: " << meshShaderProps.maxTaskSharedMemorySize;
372 PLOGV << "Max Task Payload and Shared Memory Size: " << meshShaderProps.maxTaskPayloadAndSharedMemorySize;
373
374 PLOGV;
375
376 PLOGV << "Max Mesh WorkGroup Total Count: " << meshShaderProps.maxMeshWorkGroupTotalCount;
377 PLOGV << "Max Mesh WorkGroup Count: ("
378 << meshShaderProps.maxMeshWorkGroupCount[0] << ", "
379 << meshShaderProps.maxMeshWorkGroupCount[1] << ", "
380 << meshShaderProps.maxMeshWorkGroupCount[2] << ")";
381 PLOGV << "Max Mesh WorkGroup Invocations: " << meshShaderProps.maxMeshWorkGroupInvocations;
382 PLOGV << "Max Mesh WorkGroup Size: ("
383 << meshShaderProps.maxMeshWorkGroupSize[0] << ", "
384 << meshShaderProps.maxMeshWorkGroupSize[1] << ", "
385 << meshShaderProps.maxMeshWorkGroupSize[2] << ")";
386 PLOGV << "Max Mesh Shared Memory Size: " << meshShaderProps.maxMeshSharedMemorySize;
387 PLOGV << "Max Mesh Payload and Output Memory Size: " << meshShaderProps.maxMeshPayloadAndOutputMemorySize;
388
389 PLOGV;
390
391 PLOGV << "Max Mesh Output Memory Size: " << meshShaderProps.maxMeshOutputMemorySize;
392 PLOGV << "Max Mesh Output Components: " << meshShaderProps.maxMeshOutputComponents;
393 PLOGV << "Max Mesh Output Vertices: " << meshShaderProps.maxMeshOutputVertices;
394 PLOGV << "Max Mesh Output Primitives: " << meshShaderProps.maxMeshOutputPrimitives;
395 PLOGV << "Max Mesh Output Layers: " << meshShaderProps.maxMeshOutputLayers;
396 PLOGV << "Max Mesh Multiview View Count: " << meshShaderProps.maxMeshMultiviewViewCount;
397
398 PLOGV;
399 PLOGV << "Mesh Output Per Vertex Granularity: " << meshShaderProps.meshOutputPerVertexGranularity;
400 PLOGV << "Mesh Output Per Primitive Granularity: " << meshShaderProps.meshOutputPerPrimitiveGranularity;
401
402 PLOGV;
403 PLOGV << "Max Preferred Task WorkGroup Invocations: " << meshShaderProps.maxPreferredTaskWorkGroupInvocations;
404 PLOGV << "Max Preferred Mesh WorkGroup Invocations: " << meshShaderProps.maxPreferredMeshWorkGroupInvocations;
405 PLOGV << "Prefers Local Invocation Vertex Output: " << (meshShaderProps.prefersLocalInvocationVertexOutput ? "Yes" : "No");
406 PLOGV << "Prefers Local Invocation Primitive Output: " << (meshShaderProps.prefersLocalInvocationPrimitiveOutput ? "Yes" : "No");
407 PLOGV << "Prefers Compact Vertex Output: " << (meshShaderProps.prefersCompactVertexOutput ? "Yes" : "No");
408 PLOGV << "Prefers Compact Primitive Output: " << (meshShaderProps.prefersCompactPrimitiveOutput ? "Yes" : "No");
409#endif
410 }
411
412 // retrieveQueues
413 {
414 VkDeviceQueueInfo2 drawQueueInfo = {
415 .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2,
416 .pNext = nullptr,
417 .flags = 0u,
418 .queueFamilyIndex = queueFamily.graphics.index,
419 .queueIndex = 0u
420 };
421 vkGetDeviceQueue2(vkDevice, &drawQueueInfo, &queueFamily.graphics.queue);
422 VulkanHelper::setObjectName(vkDevice, queueFamily.graphics.queue, "Draw Queue");
423
424 PLOGI << "Retrieved draw queue";
425
426 VkDeviceQueueInfo2 presentQueueInfo {
427 .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2,
428 .pNext = nullptr,
429 .flags = 0u,
430 .queueFamilyIndex = queueFamily.present.index,
431 .queueIndex = 0u
432 };
433 vkGetDeviceQueue2(vkDevice, &presentQueueInfo, &queueFamily.present.queue);
434 VulkanHelper::setObjectName(vkDevice, queueFamily.present.queue, "Present Queue");
435
436 PLOGI << "Retrieved present queue";
437
438 VkDeviceQueueInfo2 transferQueueInfo {
439 .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2,
440 .pNext = nullptr,
441 .flags = 0u,
442 .queueFamilyIndex = queueFamily.transfer.index,
443 .queueIndex = 0u
444 };
445 vkGetDeviceQueue2(vkDevice, &transferQueueInfo, &queueFamily.transfer.queue);
446 VulkanHelper::setObjectName(vkDevice, queueFamily.transfer.queue, "Transfer Queue");
447
448 PLOGI << "Retrieved transfer queue";
449 }
450
451 // Create VMA allocator
452 {
453 TRACY_ZONE_SCOPED_NAMED("VMA Allocator Creation");
454 VmaAllocatorCreateInfo allocatorCI{};
455 allocatorCI.physicalDevice = vkPhysicalDevice;
456 allocatorCI.device = vkDevice;
457 allocatorCI.instance = vkInstance;
458 allocatorCI.vulkanApiVersion = VK_API_VERSION_1_3;
459
460 if (vmaCreateAllocator(&allocatorCI, &vmaAllocator_) != VK_SUCCESS)
461 {
462 throw std::runtime_error("Failed to create VMA allocator");
463 }
464 PLOGI << "VMA allocator created";
465 }
466}
467
468void ApplicationContext::pickQueueFamilies(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface,
469 uint32_t& outGraphicsIndex,
470 uint32_t& outPresentIndex,
471 uint32_t& outTransferIndex)
472{
473 std::optional<uint32_t> graphicsIndex;
474 std::optional<uint32_t> presentIndex;
475 std::optional<uint32_t> dedicatedTransferIndex; // Priority 1 for transfer
476 std::optional<uint32_t> fallbackTransferIndex; // Priority 2 for transfer
477
478 uint32_t queueFamilyCount = 0;
479 vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr);
480 if (queueFamilyCount == 0) {
481 throw std::runtime_error("Physical device has no queue families!");
482 }
483
484 std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
485 vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilies.data());
486
487 PLOGI << "Found " << queueFamilyCount << " queue families.";
488
489 for (uint32_t i = 0; i < queueFamilyCount; ++i) {
490 const auto& props = queueFamilies[i];
491 PLOGI << "Family " << i << ": count=" << props.queueCount << ", flags=" << std::hex << props.queueFlags;
492
493 if (props.queueCount == 0) {
494 continue; // Skip families with no actual queues
495 }
496
497 // 1. Check for Graphics Support
498 if (props.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
499 if (!graphicsIndex.has_value()) { // Take the first one we find
500 graphicsIndex = i;
501 PLOGI << " Found potential Graphics Queue Family: " << i;
502 }
503 // Also consider this as a fallback transfer queue if it supports transfer
504 if (props.queueFlags & VK_QUEUE_TRANSFER_BIT) {
505 if (!fallbackTransferIndex.has_value()) {
506 fallbackTransferIndex = i;
507 PLOGI << " Marking as Fallback Transfer Queue Family: " << i;
508 }
509 }
510 }
511
512 // 2. Check for Transfer Support (prioritize dedicated)
513 if (props.queueFlags & VK_QUEUE_TRANSFER_BIT) {
514 // Is it dedicated (Transfer ON, Graphics OFF)?
515 if (!(props.queueFlags & VK_QUEUE_GRAPHICS_BIT)) {
516 if (!dedicatedTransferIndex.has_value()) {
517 dedicatedTransferIndex = i;
518 PLOGI << " Found potential *Dedicated* Transfer Queue Family: " << i;
519 }
520 }
521 // If it's not dedicated, we already potentially marked it as a fallbackTransferIndex above
522 // if it also supported graphics. If it supports *only* transfer and compute (but not graphics)
523 // it would also be caught by the dedicated check above.
524 }
525
526
527 // 3. Check for Presentation Support
528 VkBool32 presentSupport = VK_FALSE;
529 VkResult presentCheckResult = vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, i, surface, &presentSupport);
530 if (presentCheckResult == VK_SUCCESS && presentSupport) {
531 if (!presentIndex.has_value()) { // Take the first one
532 presentIndex = i;
533 PLOGI << " Found potential Present Queue Family: " << i;
534 }
535 } else if (presentCheckResult != VK_SUCCESS) {
536 PLOGW << " vkGetPhysicalDeviceSurfaceSupportKHR failed for family " << i << " with error: " << presentCheckResult;
537 // Potentially handle specific errors here if needed
538 }
539 }
540
541 // --- Final Selection and Validation ---
542
543 if (!graphicsIndex.has_value()) {
544 throw std::runtime_error("Failed to find a suitable Graphics queue family!");
545 }
546 outGraphicsIndex = graphicsIndex.value();
547 PLOGI << "Selected Graphics Queue Family Index: " << outGraphicsIndex;
548
549
550 if (!presentIndex.has_value()) {
551 // As a last resort, sometimes the graphics queue might implicitly support present
552 // Check if the selected graphics queue supports present if no dedicated present queue was found.
553 VkBool32 presentSupport = VK_FALSE;
554 vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, outGraphicsIndex, surface, &presentSupport);
555 if(presentSupport) {
556 presentIndex = outGraphicsIndex;
557 PLOGI << " No dedicated Present Queue found, using Graphics Queue Family " << outGraphicsIndex << " for presentation.";
558 } else {
559 throw std::runtime_error("Failed to find a suitable Present queue family!");
560 }
561 }
562 outPresentIndex = presentIndex.value();
563 PLOGI << "Selected Present Queue Family Index: " << outPresentIndex;
564
565
566 // Prioritize dedicated transfer, then fallback (which might be the graphics queue), then finally ensure graphics queue can do it
567 if (dedicatedTransferIndex.has_value()) {
568 outTransferIndex = dedicatedTransferIndex.value();
569 PLOGI << "Selected *Dedicated* Transfer Queue Family Index: " << outTransferIndex;
570 } else if (fallbackTransferIndex.has_value()) {
571 outTransferIndex = fallbackTransferIndex.value();
572 PLOGI << "Selected *Fallback* Transfer Queue Family Index (might be same as Graphics): " << outTransferIndex;
573 } else {
574 // This case should be rare, implies no queue explicitly had TRANSFER_BIT other than maybe dedicated compute?
575 // Double check if our chosen graphics queue *definitely* has the transfer bit
576 if (queueFamilies[outGraphicsIndex].queueFlags & VK_QUEUE_TRANSFER_BIT) {
577 outTransferIndex = outGraphicsIndex;
578 PLOGW << "No dedicated or distinct fallback Transfer Queue found. Using Graphics Queue Family " << outGraphicsIndex << " which supports transfer.";
579 } else {
580 throw std::runtime_error("Failed to find any queue family supporting Transfer operations, including the Graphics queue!");
581 }
582 }
583
584 PLOGI << "Final Queue Selection: Graphics=" << outGraphicsIndex << ", Present=" << outPresentIndex << ", Transfer=" << outTransferIndex;
585
586 // Optional: Log if queues are shared
587 if (outGraphicsIndex == outPresentIndex) PLOGI << "Note: Graphics and Present queues share the same family.";
588 if (outGraphicsIndex == outTransferIndex) PLOGI << "Note: Graphics and Transfer queues share the same family.";
589 if (outPresentIndex == outTransferIndex) PLOGI << "Note: Present and Transfer queues share the same family.";
590}
591
593{
594 return xrInstance;
595}
596
598{
599 return xrSystemId;
600}
601
603{
604 return vkInstance;
605}
606
608{
609 return vkDevice;
610}
611
613{
614 return queueFamily.present.queue;
615}
616
618{
619 return queueFamily.graphics.queue;
620}
621
623{
624 return queueFamily.transfer.queue;
625}
626
627VkSampleCountFlagBits ApplicationContext::getMultisampleCount() const
628{
629 return multisampleCount;
630}
631
632const VkPhysicalDevice &ApplicationContext::getVkPhysicalDevice() const
633{
634 return vkPhysicalDevice;
635}
636
638{
639 return queueFamily.graphics.index;
640}
641
643{
644 return queueFamily.transfer.index;
645}
646
651
656
657const XrViewConfigurationType ApplicationContext::getXrViewType() const
658{
659 return xrViewType;
660}
661
663{
664 return vmaAllocator_;
665}
666
667void ApplicationContext::createOpenXrInstance(std::optional<std::filesystem::path> customOpenXrRuntimePath)
668{
669#ifdef ENABLE_TRACY
671#endif
672
673 // Log the XR_RUNTIME_JSON environment variable that OpenXR will try to load
674 const char* existingRuntime = std::getenv("XR_RUNTIME_JSON");
675 if (existingRuntime)
676 {
677 PLOGI << "[OpenXR Debug] XR_RUNTIME_JSON already set to: " << existingRuntime;
678 }
679 else
680 {
681 PLOGI << "[OpenXR Debug] XR_RUNTIME_JSON not set - OpenXR will search default locations";
682 }
683
684 if (customOpenXrRuntimePath.has_value())
685 {
686 PLOGI << "[OpenXR Debug] Using custom runtime path: " << customOpenXrRuntimePath.value().string();
687#ifdef WIN32
688 std::cout << "Set openxr runtime environment varibale to " << customOpenXrRuntimePath.value().string().c_str();
689 _putenv_s("XR_RUNTIME_JSON", customOpenXrRuntimePath.value().string().c_str());
690#else
691 setenv("XR_RUNTIME_JSON", customOpenXrRuntimePath.value().string().c_str(), 1);
692#endif
693 }
694
695 // get supported open xr instance extensions
696 {
697 uint32_t instanceExtensionCount;
698 PLOG_FN_XR(xrEnumerateInstanceExtensionProperties(nullptr, 0u, &instanceExtensionCount, nullptr));
699
700 supportedOpenXRInstanceExtensions.resize(instanceExtensionCount);
701 for (XrExtensionProperties& property : supportedOpenXRInstanceExtensions)
702 {
703 property.type = XR_TYPE_EXTENSION_PROPERTIES;
704 property.next = nullptr;
705 }
706
707 PLOG_FN_XR(xrEnumerateInstanceExtensionProperties(nullptr, instanceExtensionCount, &instanceExtensionCount, supportedOpenXRInstanceExtensions.data()));
708
709 PLOGI << "Found " << instanceExtensionCount << " XrInstance extensions";
710 }
711
712 // get supported open xr api layers
713 {
714 uint32_t apiLayerCount = 0;
715 PLOG_FN_XR(xrEnumerateApiLayerProperties(0, &apiLayerCount, nullptr));
716 supportedOpenXRApiLayers.resize(apiLayerCount, {XR_TYPE_API_LAYER_PROPERTIES});
717 PLOG_FN_XR(xrEnumerateApiLayerProperties(apiLayerCount, &apiLayerCount, supportedOpenXRApiLayers.data()));
718 }
719
720 PLOGI << "Creating OpenXR instance";
721 XrApplicationInfo applicationInfo{};
722 applicationInfo.apiVersion = XR_CURRENT_API_VERSION;
723 applicationInfo.applicationVersion = static_cast<uint32_t>(XR_MAKE_VERSION(0, 1, 0));
724 applicationInfo.engineVersion = static_cast<uint32_t>(XR_MAKE_VERSION(0, 1, 0));
725
726 std::string engineName = "betonmischer";
727 std::string applicationName = "VulkanSchnee";
728
729 memcpy(applicationInfo.engineName, engineName.data(), engineName.length() + 1u);
730 memcpy(applicationInfo.applicationName, applicationName.data(), applicationName.length() + 1u);
731
732 std::vector<const char*> extensions = {XR_KHR_VULKAN_ENABLE2_EXTENSION_NAME};
733
734#if IS_IN_DEBUG
735 // if we are in debug mode then add openxr debug instance extension
736 extensions.push_back(XR_EXT_DEBUG_UTILS_EXTENSION_NAME);
737#endif
738 //extensions.push_back(XR_FB_DISPLAY_REFRESH_RATE_EXTENSION_NAME);
739 //extensions.push_back(XR_EXT_PERFORMANCE_SETTINGS_EXTENSION_NAME);
740 //extensions.push_back(XR_FB_COMPOSITION_LAYER_SETTINGS_EXTENSION_NAME);
741
742 for (const XrExtensionProperties& supportedExtension : supportedOpenXRInstanceExtensions)
743 {
744 PLOGV << supportedExtension.extensionName;
745 }
746
747 // check for extension support
748 for (const char* extension : extensions)
749 {
750 bool isInstanceExtensionSupported = false;
751 for (const XrExtensionProperties& supportedExtension : supportedOpenXRInstanceExtensions)
752 {
753 if (strcmp(extension, supportedExtension.extensionName) == 0)
754 {
755 isInstanceExtensionSupported = true;
756 break;
757 }
758 }
759
760 if (!isInstanceExtensionSupported)
761 {
762 std::stringstream error;
763 error << "OpenXr Instance Extension " << extension << " is not supported!";
764 PLOGE << "[OpenXR Debug] " << error.str();
765 PLOGE << "[OpenXR Debug] This usually means no OpenXR runtime is installed or running.";
766 PLOGE << "[OpenXR Debug] For SteamVR: ensure SteamVR is running";
767 PLOGE << "[OpenXR Debug] For Monado: check ~/.config/openxr/1/active_runtime.json exists";
768 PLOGE << "[OpenXR Debug] Supported extensions count: " << supportedOpenXRInstanceExtensions.size();
769 throw VrHeadsetConnectionException(error.str());
770 }
771 }
772
773 // API layers
774 std::vector<std::string> requrestedApiLayers = {"XR_APILAYER_LUNARG_core_validation"};
775 std::vector<const char*> enabledLayers = {};
776 // Check the requested API layers against the ones from the OpenXR. If found add it to the Active API Layers.
777 for (auto& requestedLayer : requrestedApiLayers)
778 {
779 for (auto& layerProperty : supportedOpenXRApiLayers)
780 {
781 // strcmp returns 0 if the strings match.
782 if (strcmp(requestedLayer.c_str(), layerProperty.layerName) != 0)
783 {
784 continue;
785 }
786 else
787 {
788 enabledLayers.push_back(layerProperty.layerName);
789 PLOGI << "Enabled: " << layerProperty.layerName;
790 break;
791 }
792 }
793 }
794
795 XrInstanceCreateInfo instanceCreateInfo{
796 .type = XR_TYPE_INSTANCE_CREATE_INFO,
797 .next = nullptr,
798 .createFlags = 0u,
799 .applicationInfo = applicationInfo,
800 .enabledApiLayerCount = static_cast<uint32_t>(enabledLayers.size()),
801 .enabledApiLayerNames = enabledLayers.data(),
802 .enabledExtensionCount = static_cast<uint32_t>(extensions.size()),
803 .enabledExtensionNames = extensions.data()
804 };
805 XrResult instanceCreationResult = xrCreateInstance(&instanceCreateInfo, &xrInstance);
806 if (instanceCreationResult != XR_SUCCESS)
807 {
808 if (instanceCreationResult == XR_ERROR_RUNTIME_FAILURE)
809 {
810 throw EnvironmentException("Your steamvr is probably not running!");
811 }
812 THROW_XR_ERROR(instanceCreationResult);
813
814 if (instanceCreationResult == XR_ERROR_RUNTIME_UNAVAILABLE)
815 {
816 throw MissingOpenXrRuntimeException("Missing runtime");
817 }
818 }
819
820 PLOGI << "OpenXR instance created successfully.";
821}
822
824{
827 OpenXrHelper::LoadXrFunction(xrInstance, "xrGetVulkanGraphicsRequirements2KHR", xrGetVulkanGraphicsRequirements2KHR);
828
831#ifdef IS_IN_DEBUG
832 OpenXrHelper::LoadXrFunction(xrInstance, "xrCreateDebugUtilsMessengerEXT", xrCreateDebugUtilsMessengerEXT);
833 OpenXrHelper::LoadXrFunction(xrInstance, "xrDestroyDebugUtilsMessengerEXT", xrDestroyDebugUtilsMessengerEXT);
834#endif
835 PLOGI << "Finished loading OpenXR extension functions";
836}
837
838#ifdef IS_IN_DEBUG
839void ApplicationContext::createOpenXrDebugMessenger()
840{
841#ifdef ENABLE_TRACY
843#endif
844 constexpr XrDebugUtilsMessageTypeFlagsEXT typeFlags = XR_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | XR_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
845 XR_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT | XR_DEBUG_UTILS_MESSAGE_TYPE_CONFORMANCE_BIT_EXT;
846
847 constexpr XrDebugUtilsMessageSeverityFlagsEXT severityFlags = XR_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | XR_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT |
848 XR_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | XR_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
849
850 // Define the callback function
851 auto debugCallback = [](XrDebugUtilsMessageSeverityFlagsEXT severity, XrDebugUtilsMessageTypeFlagsEXT type, const XrDebugUtilsMessengerCallbackDataEXT* callbackData, void* userData) -> XrBool32
852 {
853 switch (severity)
854 {
855 case XR_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT:
856 {
857 DPLOGI << "OpenXR Debug Message: " << callbackData->message << std::endl;
858 break;
859 }
860 case XR_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT:
861 PLOGW << "[OpenXR] " << callbackData->message << std::endl;
862 break;
863 case XR_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:
864 PLOGE << "[OpenXR] " << callbackData->message << std::endl;
865 break;
866 case XR_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT:
867 PLOGI << "[OpenXR] " << callbackData->message << std::endl;
868 break;
869 }
870 return XR_FALSE;
871 };
872
873 XrDebugUtilsMessengerCreateInfoEXT createInfo{
874 .type = XR_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
875 .next = nullptr,
876 .messageSeverities = severityFlags,
877 .messageTypes = typeFlags,
878 .userCallback = debugCallback,
879 .userData = nullptr
880 };
881 PLOG_FN_XR(xrCreateDebugUtilsMessengerEXT(xrInstance, &createInfo, &xrDebugMessenger));
882
883 DPLOGI << "Debug Utils Messenger created successfully.";
884}
885#endif
886
888{
890 XrSystemGetInfo systemInfo{XR_TYPE_SYSTEM_GET_INFO};
891 systemInfo.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY;
892
893 const XrResult systemRetrievalResult = xrGetSystem(xrInstance, &systemInfo, &xrSystemId);
894 if (systemRetrievalResult != XR_SUCCESS)
895 {
896 std::stringstream errorMessage;
897 errorMessage << "Failed to retrieve the xr system. Check if ";
899 {
901 {
902 errorMessage << "your OpenXR application is running (SteamVR, Oculus)";
903 break;
904 }
906 {
907 errorMessage << "the Oculus app is running and your headset is connected.";
908 break;
909 }
910 }
911 throw VrHeadsetConnectionException(errorMessage.str());
912 }
913
914 DPLOGI << "Selected VR system";
915 DPLOGI << "VR System ID: " << xrSystemId;
916}
917
919{
921 uint32_t environmentBlendModeCount;
922 PLOG_FN_XR(xrEnumerateEnvironmentBlendModes(xrInstance, xrSystemId, xrViewType, 0u, &environmentBlendModeCount, nullptr));
923
924 std::vector<XrEnvironmentBlendMode> supportedEnvironmentBlendModes(environmentBlendModeCount);
925 PLOG_FN_XR(xrEnumerateEnvironmentBlendModes(xrInstance, xrSystemId, xrViewType, environmentBlendModeCount, &environmentBlendModeCount, supportedEnvironmentBlendModes.data()));
926
927 bool blendModeFound = false;
928 for (const XrEnvironmentBlendMode& mode : supportedEnvironmentBlendModes)
929 {
930 if (mode == environmentBlendMode)
931 {
932 blendModeFound = true;
933 break;
934 }
935 }
936
937 if (!blendModeFound)
938 {
939 PLOGE << "Failed to find a suitable blend mode!";
940 throw std::runtime_error("Failed to find suitable blend mode");
941 }
942}
943
945{
947 {
948 uint32_t instanceExtensionCount = 0;
949 PLOG_FN_VK(vkEnumerateInstanceExtensionProperties(nullptr, &instanceExtensionCount, nullptr));
950 supportedVulkanInstanceExtensions.resize(instanceExtensionCount);
951 PLOG_FN_VK(vkEnumerateInstanceExtensionProperties(nullptr, &instanceExtensionCount, supportedVulkanInstanceExtensions.data()));
952
953 for(int i = 0; i < instanceExtensionCount; i++)
954 {
955 PLOGV << "Supported vk extension: " << supportedVulkanInstanceExtensions[i].extensionName;
956 }
957 }
958
959 // get glfw instance extension requirements
960 {
961 uint32_t requiredExtensionCount;
962 const char** buffer = glfwGetRequiredInstanceExtensions(&requiredExtensionCount);
963 if (!buffer)
964 {
965 PLOGE << "Failed to get vulkan instance glfw extensions";
966 throw std::runtime_error("Failed to get vulkan instance glfw extensions");
967 }
968
969 for (uint32_t extensionIndex = 0u; extensionIndex < requiredExtensionCount; extensionIndex++)
970 {
971 vulkanInstanceExtensions.push_back(buffer[extensionIndex]);
972 }
973 }
974
975 vulkanInstanceExtensions.push_back(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME);
976#ifdef IS_IN_DEBUG
977 vulkanInstanceExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
978 //vulkanInstanceExtensions.push_back(VK_KHR_CALIBRATED_TIMESTAMPS_EXTENSION_NAME);
979 //vulkanInstanceExtensions.push_back(VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME);
980 //vulkanInstanceExtensions.push_back(VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME);
981#endif // IS_IN_DEBUG
982
983 // check if the hardware supports the required extensions
984 {
985 for (const char* extension : vulkanInstanceExtensions)
986 {
987 bool isExtensionSupported = false;
988
989 PLOGI << "Checking for extension support: " << extension;
990
991 for (const VkExtensionProperties& supportedExtension : supportedVulkanInstanceExtensions)
992 {
993 if (strcmp(extension, supportedExtension.extensionName) == 0)
994 {
995 isExtensionSupported = true;
996 PLOGI << "Extension " << extension << " is supported";
997 break;
998 }
999 }
1000
1001 if (!isExtensionSupported)
1002 {
1003 PLOGE << "Vulkan instance extension " << extension << " is not supported!";
1004 throw std::runtime_error("Vulkan instance extension not supported!");
1005 }
1006 }
1007 }
1008}
1009
1011{
1012 XrGraphicsRequirementsVulkan2KHR vulkanRequirements{XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN2_KHR};
1014 PLOGI << "Vulkan min api version: " << vulkanRequirements.minApiVersionSupported;
1015 PLOGI << "Vulkan max api version: " << vulkanRequirements.maxApiVersionSupported;
1016}
1017
1019{
1021#if !defined(HEADLESS) && !defined(COMPUTE_DEBUG)
1023#endif
1024
1025 VkApplicationInfo appInfo = {
1026 .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
1027 .pNext = nullptr,
1028 .pApplicationName = "vulkan_schnee",
1029 .applicationVersion = VK_MAKE_VERSION(0, 1, 0),
1030 .pEngineName = "No Engine",
1031 .engineVersion = VK_MAKE_VERSION(0, 1, 0),
1032 .apiVersion = VK_API_VERSION_1_4,
1033 };
1034
1035 std::vector<VkValidationFeatureEnableEXT> enabledValidationFeatures;
1036 std::vector<VkValidationFeatureDisableEXT> disabledValidationFeatures;
1037
1038 switch (USE_GPU_PRINTF)
1039 {
1041 {
1042 PLOGI << "Starting with GPU debuggin mode None";
1043 // Explicitly disable debug printf when USE_GPU_PRINTF is NONE
1044 disabledValidationFeatures.push_back(VK_VALIDATION_FEATURE_DISABLE_ALL_EXT);
1045 break;
1046 }
1048 {
1049 PLOGI << "Starting GPU debugging mode GPU printf";
1050 enabledValidationFeatures = {
1051 VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT
1052 };
1053 break;
1054 }
1056 {
1057 PLOGI << "Starting GPU debugging mode GPU validation";
1058 enabledValidationFeatures = {
1059 VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT,
1060 VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_RESERVE_BINDING_SLOT_EXT,
1061 VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT,
1062 VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT
1063 };
1064 break;
1065 }
1066 }
1067
1068 VkValidationFeaturesEXT validationFeatures {
1069 .sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT,
1070 .pNext = nullptr,
1071 .enabledValidationFeatureCount = static_cast<uint32_t>(enabledValidationFeatures.size()),
1072 .pEnabledValidationFeatures = enabledValidationFeatures.data(),
1073 .disabledValidationFeatureCount = static_cast<uint32_t>(disabledValidationFeatures.size()),
1074 .pDisabledValidationFeatures = disabledValidationFeatures.data()
1075 };
1076
1077 VkInstanceCreateInfo createInfo = {
1078 .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
1079 .pNext = &validationFeatures,
1080 .flags = 0u,
1081 .pApplicationInfo = &appInfo,
1082 .enabledLayerCount = 0u,
1083 .ppEnabledLayerNames = nullptr,
1084 .enabledExtensionCount = static_cast<uint32_t>(vulkanInstanceExtensions.size()),
1085 .ppEnabledExtensionNames = vulkanInstanceExtensions.data(),
1086 };
1087
1088#ifdef IS_IN_DEBUG
1090 {
1091 constexpr std::array layers = {
1092 "VK_LAYER_KHRONOS_validation",
1093 //"VK_LAYER_NV_nomad_release_public_2025_2_0",
1094 //"VK_LAYER_NV_GPU_Trace_release_public_2025_2_0",
1095 //"VK_LAYER_NV_ngfx_capture_release_public_2025_2_0",
1096 //"VK_LAYER_NV_shader_debugger_release_public_2025_2_0"
1097 };
1098
1099 std::vector<VkLayerProperties> supportedInstanceLayers;
1100 uint32_t instanceLayerCount;
1101 PLOG_FN_VK(vkEnumerateInstanceLayerProperties(&instanceLayerCount, nullptr));
1102 supportedInstanceLayers.resize(instanceLayerCount);
1103 PLOG_FN_VK(vkEnumerateInstanceLayerProperties(&instanceLayerCount, supportedInstanceLayers.data()));
1104
1105 //for (auto& layer : supportedInstanceLayers)
1106 //{
1107 // PLOGI << layer.layerName;
1108 //}
1109
1110 for (const char* layer : layers)
1111 {
1112 bool isLayerSupported = false;
1113 for (const VkLayerProperties& supportedLayer : supportedInstanceLayers)
1114 {
1115 PLOGI << supportedLayer.layerName << " " << supportedLayer.description;
1116 if (strcmp(layer, supportedLayer.layerName) == 0)
1117 {
1118 PLOGI << "Layer is supported!";
1119 isLayerSupported = true;
1120 break;
1121 }
1122 }
1123
1124 if (!isLayerSupported)
1125 {
1126 std::stringstream error;
1127 error << "Vulkan instance layer " << layer << " is not supported!";
1128 PLOGE << error.str();
1129 throw std::runtime_error(error.str());
1130 }
1131 }
1132
1133 createInfo.enabledLayerCount = static_cast<uint32_t>(layers.size());
1134 createInfo.ppEnabledLayerNames = layers.data();
1135 }
1136#endif
1137
1138 XrVulkanInstanceCreateInfoKHR createInfoXr = {};
1139 createInfoXr.type = XR_TYPE_VULKAN_INSTANCE_CREATE_INFO_KHR;
1140 createInfoXr.next = nullptr;
1141 createInfoXr.vulkanCreateInfo = &createInfo;
1142 createInfoXr.vulkanAllocator = nullptr;
1143 createInfoXr.systemId = xrSystemId;
1144 createInfoXr.createFlags = 0;
1145 createInfoXr.pfnGetInstanceProcAddr = vkGetInstanceProcAddr;
1146
1147 if (xrCreateVulkanInstanceKHR != nullptr)
1148 {
1149 VkResult result;
1150 XrResult xrResult = xrCreateVulkanInstanceKHR(xrInstance, &createInfoXr, &vkInstance, &result);
1151 if (xrResult != XR_SUCCESS)
1152 {
1153 throw std::runtime_error("XrFunctionCall: Failed to create Vulkan instance with error code: " + std::to_string(xrResult));
1154 }
1155 if (result != VK_SUCCESS)
1156 {
1157 throw std::runtime_error("VulkanResult: Failed to create Vulkan instance with error code: " + std::to_string(result));
1158 }
1159 }
1160
1161 PLOGI << "Created Vulkan Instance";
1162}
1163} // namespace EngineCore
#define VALIDATE_FEATURE(feature, is_not_supported_error)
constexpr OPENXR_LAUNCH_MODE DEFAULT_OPENXR_LAUNCH_MODE
Engine execution modes (mutually exclusive)
Definition Settings.h:53
constexpr bool ARE_VALIDATION_LAYERS_ENABLED
Definition Settings.h:29
constexpr GPU_DEBUGGING_MODE USE_GPU_PRINTF
Definition Settings.h:54
#define TRACY_ZONE_SCOPED_FUNCTION
#define TRACY_ZONE_SCOPED_NAMED(name)
const VkQueue & getPresentQueue() const
Gets the present queue.
void logVulkanRequirements()
Logs vulkan requirements.
uint32_t getVkTransferQueueFamilyIndex() const
gets the transfer queue family index
uint32_t getMaxPushConstantsSize() const
Gets max push constants size in bytes.
void createOpenXrInstance(std::optional< std::filesystem::path > customOpenXrRuntimePath=std::optional< std::filesystem::path >())
Queries supported openxr instance extensions, openxr api layers and creates the XrInstance.
PFN_xrCreateVulkanInstanceKHR xrCreateVulkanInstanceKHR
VmaAllocator vmaAllocator_
VMA allocator for efficient GPU memory management.
XrSystemId xrSystemId
Identifier for the xr device. So the identifier for the headset.
uint32_t getVkGraphicsQueueFamilyIndex() const
Gets the zero-based index of the vulkan draw queue family.
struct EngineCore::ApplicationContext::QueueFamily queueFamily
bool usesDedicatedTransferQueue() const
Check if the transfer queue index is its own, dedicated queue.
std::vector< XrExtensionProperties > supportedOpenXRInstanceExtensions
The supported open xr instance extensions.
uint32_t maxPushConstantsSize
Maximum push constants size in bytes.
PFN_xrGetVulkanGraphicsRequirements2KHR xrGetVulkanGraphicsRequirements2KHR
VkPhysicalDevice vkPhysicalDevice
The vulkan physical device.
XrInstance xrInstance
The xr instance.
std::vector< const char * > vulkanDeviceExtensions
(Immutable) the vulkan device extensions
const VkQueue & getTransferQueue() const
Gets the transfer queue.
void checkRequiredEnvironmentBlendModeAvailability()
Gets the environment blend mode.
void retrieveXrSystemId()
Gets the XrSystemId.
VkSampleCountFlagBits multisampleCount
Number of multisamples.
VkInstance vkInstance
The vulkan instance.
std::vector< VkExtensionProperties > supportedVulkanInstanceExtensions
The supported vulkan instance extensions.
std::vector< VkExtensionProperties > supportedVulkanDeviceExtensions
The supported vulkan device extensions.
XrSystemId getXrSystemId() const
Gets xr system identifier.
VkDeviceSize uniformBufferOffsetAlignment
The uniform buffer offset alignment.
const VkDevice getVkDevice() const
Gets the vulkan device.
void pickQueueFamilies(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t &outGraphicsIndex, uint32_t &outPresentIndex, uint32_t &outTransferIndex)
gets the device queues needed
const VkPhysicalDevice & getVkPhysicalDevice() const
Gets vulkan physical device.
void getVulkanInstanceExtensions()
Gets vulkan instance extensions.
PFN_xrCreateVulkanDeviceKHR xrCreateVulkanDeviceKHR
VkInstance getVkInstance() const
Gets vulkan instance.
void cleanup()
performs the cleanup by removing all vulkan and openxr pointers
PFN_xrGetVulkanGraphicsDevice2KHR xrGetVulkanGraphicsDevice2KHR
void createVulkanInstance()
Creates a vulkan instance.
const XrViewConfigurationType getXrViewType() const
Gets xr view type which should be stereo for any typical VR application.
void createDevice(VkSurfaceKHR mirrorSurface)
Creates the vulkan device and all necessary components for it.
std::vector< const char * > vulkanInstanceExtensions
(Immutable) the vulkan instance extensions which should get enabled
VmaAllocator getVmaAllocator() const
Gets the VMA allocator for memory management.
std::vector< XrApiLayerProperties > supportedOpenXRApiLayers
The supported open xr API layers.
VkSampleCountFlagBits getMultisampleCount() const
Gets multisample count.
const VkQueue & getGraphicsQueue() const
Gets draw queue.
void createResources()
creates all openxr and vulkan resources
VkDeviceSize getUniformBufferOffsetAlignment() const
Gets uniform buffer offset alignment.
void LoadOpenXrExtensionFunctions()
Loads all dynamically loaded xr functions.
XrInstance getXrInstance() const
Gets xr instance.
Is thrown when the environment for the game is not setup properly. So for example if we cant find an ...
When the open xr runtime is not installed this will be thrown.
Definition Exceptions.h:55
static void LoadXrFunction(XrInstance xrInstance, const std::string &functionName, T &functionPointer)
Loads a function from openxr which is not loaded at compile time. Retrieves function pointer.
static void initializeDebugFunctions(VkInstance instance)
static void setObjectName(VkDevice device, VulkanObjectType objectHandle, const std::string &name)
ChainBuilder & add(VulkanStruct &object)
Log category system implementation.
constexpr XrEnvironmentBlendMode environmentBlendMode
constexpr XrViewConfigurationType xrViewType