.. _program_listing_file_Src_GraphicsEngineVulkan_vulkan_base_VulkanDevice.cpp: Program Listing for File VulkanDevice.cpp ========================================= |exhale_lsh| :ref:`Return to documentation for file ` (``Src/GraphicsEngineVulkan/vulkan_base/VulkanDevice.cpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp #include "vulkan_base/VulkanDevice.hpp" #include #include "common/Utilities.hpp" #include "spdlog/spdlog.h" #include #include #include Kataglyphis::VulkanDevice::VulkanDevice(VulkanInstance *instance, VkSurfaceKHR *surface) { this->instance = instance; this->surface = surface; get_physical_device(); create_logical_device(); } Kataglyphis::VulkanRendererInternals::SwapChainDetails Kataglyphis::VulkanDevice::getSwapchainDetails() { return getSwapchainDetails(physical_device); } void Kataglyphis::VulkanDevice::cleanUp() { vkDestroyDevice(logical_device, nullptr); } Kataglyphis::VulkanDevice::~VulkanDevice() {} Kataglyphis::VulkanRendererInternals::QueueFamilyIndices Kataglyphis::VulkanDevice::getQueueFamilies() { Kataglyphis::VulkanRendererInternals::QueueFamilyIndices indices{}; uint32_t queue_family_count = 0; vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_count, nullptr); std::vector queue_family_list(queue_family_count); vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_count, queue_family_list.data()); // Go through each queue family and check if it has at least 1 of required // types we need to keep track th eindex by our own int index = 0; for (const auto &queue_family : queue_family_list) { // first check if queue family has at least 1 queue in that family // Queue can be multiple types defined through bitfield. Need to bitwise AND // with VK_QUE_*_BIT to check if has required type if (queue_family.queueCount > 0 && queue_family.queueFlags & VK_QUEUE_GRAPHICS_BIT) { indices.graphics_family = index;// if queue family valid, than get index } if (queue_family.queueCount > 0 && queue_family.queueFlags & VK_QUEUE_COMPUTE_BIT) { indices.compute_family = index; } // check if queue family suppports presentation VkBool32 presentation_support = false; vkGetPhysicalDeviceSurfaceSupportKHR(physical_device, index, *surface, &presentation_support); // check if queue is presentation type (can be both graphics and // presentation) if (queue_family.queueCount > 0 && presentation_support) { indices.presentation_family = index; } // check if queue family indices are in a valid state if (indices.is_valid()) { break; } index++; } return indices; } void Kataglyphis::VulkanDevice::get_physical_device() { // Enumerate physical devices the vkInstance can access uint32_t device_count = 0; vkEnumeratePhysicalDevices(instance->getVulkanInstance(), &device_count, nullptr); // if no devices available, then none support of Vulkan if (device_count == 0) { spdlog::error("Can not find GPU's that support Vulkan Instance!"); } // Get list of physical devices std::vector device_list(device_count); vkEnumeratePhysicalDevices(instance->getVulkanInstance(), &device_count, device_list.data()); for (const auto &device : device_list) { if (check_device_suitable(device)) { physical_device = device; break; } } // get properties of our new device vkGetPhysicalDeviceProperties(physical_device, &device_properties); } void Kataglyphis::VulkanDevice::create_logical_device() { // get the queue family indices for the chosen physical device Kataglyphis::VulkanRendererInternals::QueueFamilyIndices indices = getQueueFamilies(); // vector for queue creation information and set for family indices std::vector queue_create_infos; std::set queue_family_indices = { indices.graphics_family, indices.presentation_family, indices.compute_family }; // Queue the logical device needs to create and info to do so (only 1 for now, // will add more later!) for (int queue_family_index : queue_family_indices) { VkDeviceQueueCreateInfo queue_create_info{}; queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; queue_create_info.queueFamilyIndex = queue_family_index;// the index of the family to create a queue from queue_create_info.queueCount = 1;// number of queues to create float priority = 1.0f; queue_create_info.pQueuePriorities = &priority;// Vulkan needs to know how to handle multiple queues, so // decide priority (1 = highest) queue_create_infos.push_back(queue_create_info); } // -- ALL EXTENSION WE NEED VkPhysicalDeviceDescriptorIndexingFeatures indexing_features{}; indexing_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES; indexing_features.runtimeDescriptorArray = VK_TRUE; indexing_features.shaderSampledImageArrayNonUniformIndexing = VK_TRUE; indexing_features.pNext = nullptr; // -- NEEDED FOR QUERING THE DEVICE ADDRESS WHEN CREATING ACCELERATION // STRUCTURES VkPhysicalDeviceBufferDeviceAddressFeaturesEXT buffer_device_address_features{}; buffer_device_address_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES_EXT; buffer_device_address_features.pNext = &indexing_features; buffer_device_address_features.bufferDeviceAddress = VK_TRUE; buffer_device_address_features.bufferDeviceAddressCaptureReplay = VK_TRUE; buffer_device_address_features.bufferDeviceAddressMultiDevice = VK_FALSE; // --ENABLE RAY TRACING PIPELINE VkPhysicalDeviceRayTracingPipelineFeaturesKHR ray_tracing_pipeline_features{}; ray_tracing_pipeline_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR; ray_tracing_pipeline_features.pNext = &buffer_device_address_features; ray_tracing_pipeline_features.rayTracingPipeline = VK_TRUE; // -- ENABLE ACCELERATION STRUCTURES VkPhysicalDeviceAccelerationStructureFeaturesKHR acceleration_structure_features{}; acceleration_structure_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR; acceleration_structure_features.pNext = &ray_tracing_pipeline_features; acceleration_structure_features.accelerationStructure = VK_TRUE; acceleration_structure_features.accelerationStructureCaptureReplay = VK_TRUE; acceleration_structure_features.accelerationStructureIndirectBuild = VK_FALSE; acceleration_structure_features.accelerationStructureHostCommands = VK_FALSE; acceleration_structure_features.descriptorBindingAccelerationStructureUpdateAfterBind = VK_FALSE; VkPhysicalDeviceVulkan13Features features13{}; features13.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES; features13.maintenance4 = VK_TRUE; features13.robustImageAccess = VK_FALSE; features13.inlineUniformBlock = VK_FALSE; features13.descriptorBindingInlineUniformBlockUpdateAfterBind = VK_FALSE; features13.pipelineCreationCacheControl = VK_FALSE; features13.privateData = VK_FALSE; features13.shaderDemoteToHelperInvocation = VK_FALSE; features13.shaderTerminateInvocation = VK_FALSE; features13.subgroupSizeControl = VK_FALSE; features13.computeFullSubgroups = VK_FALSE; features13.synchronization2 = VK_FALSE; features13.textureCompressionASTC_HDR = VK_FALSE; features13.shaderZeroInitializeWorkgroupMemory = VK_FALSE; features13.dynamicRendering = VK_FALSE; features13.shaderIntegerDotProduct = VK_FALSE; features13.pNext = &acceleration_structure_features; VkPhysicalDeviceRayQueryFeaturesKHR rayQueryFeature{}; rayQueryFeature.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_QUERY_FEATURES_KHR; rayQueryFeature.pNext = &features13; rayQueryFeature.rayQuery = VK_TRUE; VkPhysicalDeviceFeatures2 features2{}; features2.pNext = &rayQueryFeature; features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; features2.features.samplerAnisotropy = VK_TRUE; features2.features.shaderInt64 = VK_TRUE; features2.features.geometryShader = VK_TRUE; features2.features.logicOp = VK_TRUE; // -- PREPARE FOR HAVING MORE EXTENSION BECAUSE WE NEED RAYTRACING // CAPABILITIES std::vector extensions(device_extensions); // Query available extensions for the physical device uint32_t extensionCount = 0; vkEnumerateDeviceExtensionProperties(physical_device, nullptr, &extensionCount, nullptr); std::vector availableExtensions(extensionCount); vkEnumerateDeviceExtensionProperties(physical_device, nullptr, &extensionCount, availableExtensions.data()); // Helper function to check if an extension is supported auto isExtensionSupported = [&availableExtensions](const char *extensionName) { for (const auto &ext : availableExtensions) { if (strcmp(ext.extensionName, extensionName) == 0) { return true; } } return false; }; for (const char *extensionName : device_extensions_for_raytracing) { if (!isExtensionSupported(extensionName)) { deviceSupportsHardwareAcceleratedRRT = false; spdlog::info("Required extension not supported: {}", extensionName); } } if (deviceSupportsHardwareAcceleratedRRT) { // COPY ALL NECESSARY EXTENSIONS FOR RAYTRACING TO THE EXTENSION extensions.insert( extensions.begin(), device_extensions_for_raytracing.begin(), device_extensions_for_raytracing.end()); } // information to create logical device (sometimes called "device") VkDeviceCreateInfo device_create_info{}; device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; device_create_info.queueCreateInfoCount = static_cast(queue_create_infos.size());// number of queue create infos device_create_info.pQueueCreateInfos = queue_create_infos.data();// list of queue create infos so device can // create required queues device_create_info.enabledExtensionCount = static_cast(extensions.size());// number of enabled logical device extensions device_create_info.ppEnabledExtensionNames = extensions.data();// list of enabled logical device extensions device_create_info.flags = 0; device_create_info.pEnabledFeatures = NULL; if (deviceSupportsHardwareAcceleratedRRT) { device_create_info.pNext = &features2; } // create logical device for the given physical device VkResult result = vkCreateDevice(physical_device, &device_create_info, nullptr, &logical_device); ASSERT_VULKAN(result, "Failed to create a logical device!"); // Queues are created at the same time as the device... // So we want handle to queues // From given logical device of given queue family, of given queue index (0 // since only one queue), place reference in given VkQueue vkGetDeviceQueue(logical_device, indices.graphics_family, 0, &graphics_queue); vkGetDeviceQueue(logical_device, indices.presentation_family, 0, &presentation_queue); vkGetDeviceQueue(logical_device, indices.compute_family, 0, &compute_queue); } Kataglyphis::VulkanRendererInternals::QueueFamilyIndices Kataglyphis::VulkanDevice::getQueueFamilies( VkPhysicalDevice physical_device) { Kataglyphis::VulkanRendererInternals::QueueFamilyIndices indices{}; uint32_t queue_family_count = 0; vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_count, nullptr); std::vector queue_family_list(queue_family_count); vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_count, queue_family_list.data()); // Go through each queue family and check if it has at least 1 of required // types we need to keep track th eindex by our own int index = 0; for (const auto &queue_family : queue_family_list) { // first check if queue family has at least 1 queue in that family // Queue can be multiple types defined through bitfield. Need to bitwise AND // with VK_QUE_*_BIT to check if has required type if (queue_family.queueCount > 0 && queue_family.queueFlags & VK_QUEUE_GRAPHICS_BIT) { indices.graphics_family = index;// if queue family valid, than get index } if (queue_family.queueCount > 0 && queue_family.queueFlags & VK_QUEUE_COMPUTE_BIT) { indices.compute_family = index; } // check if queue family suppports presentation VkBool32 presentation_support = false; vkGetPhysicalDeviceSurfaceSupportKHR(physical_device, index, *surface, &presentation_support); // check if queue is presentation type (can be both graphics and // presentation) if (queue_family.queueCount > 0 && presentation_support) { indices.presentation_family = index; } // check if queue family indices are in a valid state if (indices.is_valid()) { break; } index++; } return indices; } Kataglyphis::VulkanRendererInternals::SwapChainDetails Kataglyphis::VulkanDevice::getSwapchainDetails( VkPhysicalDevice device) { Kataglyphis::VulkanRendererInternals::SwapChainDetails swapchain_details{}; // get the surface capabilities for the given surface on the given physical // device vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, *surface, &swapchain_details.surface_capabilities); uint32_t format_count = 0; vkGetPhysicalDeviceSurfaceFormatsKHR(device, *surface, &format_count, nullptr); // if formats returned, get list of formats if (format_count != 0) { swapchain_details.formats.resize(format_count); vkGetPhysicalDeviceSurfaceFormatsKHR(device, *surface, &format_count, swapchain_details.formats.data()); } uint32_t presentation_count = 0; vkGetPhysicalDeviceSurfacePresentModesKHR(device, *surface, &presentation_count, nullptr); // if presentation modes returned, get list of presentation modes if (presentation_count > 0) { swapchain_details.presentation_mode.resize(presentation_count); vkGetPhysicalDeviceSurfacePresentModesKHR( device, *surface, &presentation_count, swapchain_details.presentation_mode.data()); } return swapchain_details; } bool Kataglyphis::VulkanDevice::check_device_suitable(VkPhysicalDevice device) { // Information about device itself (ID, name, type, vendor, etc) VkPhysicalDeviceProperties device_properties; vkGetPhysicalDeviceProperties(device, &device_properties); VkPhysicalDeviceFeatures device_features; vkGetPhysicalDeviceFeatures(device, &device_features); Kataglyphis::VulkanRendererInternals::QueueFamilyIndices indices = getQueueFamilies(device); bool extensions_supported = check_device_extension_support(device); bool swap_chain_valid = false; if (extensions_supported) { Kataglyphis::VulkanRendererInternals::SwapChainDetails swap_chain_details = getSwapchainDetails(device); swap_chain_valid = !swap_chain_details.presentation_mode.empty() && !swap_chain_details.formats.empty(); } return indices.is_valid() && extensions_supported && swap_chain_valid && device_features.samplerAnisotropy; } bool Kataglyphis::VulkanDevice::check_device_extension_support(VkPhysicalDevice device) { uint32_t extension_count = 0; vkEnumerateDeviceExtensionProperties(device, nullptr, &extension_count, nullptr); if (extension_count == 0) { return false; } // populate list of extensions std::vector extensions(extension_count); vkEnumerateDeviceExtensionProperties(device, nullptr, &extension_count, extensions.data()); for (const auto &device_extension : device_extensions) { bool has_extension = false; for (const auto &extension : extensions) { if (strcmp(device_extension, extension.extensionName) == 0) { has_extension = true; break; } } if (!has_extension) { return false; } } return true; }