diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/engine.cpp | 542 |
1 files changed, 371 insertions, 171 deletions
diff --git a/src/engine.cpp b/src/engine.cpp index f548e75..204b2e9 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -15,6 +15,9 @@ #include <numbers> #include <optional> #include <algorithm> +#include <tuple> +#include <limits> +#include <array> #define GLFW_INCLUDE_VULKAN #include <GLFW/glfw3.h> @@ -312,10 +315,15 @@ const bool enable_validation_layers = true; static bool check_validation_layer_support() { uint32_t layer_count; - vkEnumerateInstanceLayerProperties(&layer_count, nullptr); - + if (VkResult res = vkEnumerateInstanceLayerProperties(&layer_count, nullptr); res != VK_SUCCESS) { + std::cerr << "failed to enumerate instance layer properties, error code: " << string_VkResult(res) << std::endl; + std::exit(EXIT_FAILURE); + } std::vector<VkLayerProperties> available_layers(layer_count); - vkEnumerateInstanceLayerProperties(&layer_count, available_layers.data()); + if (VkResult res = vkEnumerateInstanceLayerProperties(&layer_count, available_layers.data()); res != VK_SUCCESS) { + std::cerr << "failed to enumerate instance layer properties, error code: " << string_VkResult(res) << std::endl; + std::exit(EXIT_FAILURE); + } for (const char* layer_name : validation_layers) { bool layer_found = false; @@ -392,11 +400,28 @@ static void populate_msger_create_info(VkDebugUtilsMessengerCreateInfoEXT& msger msger_create_info.pUserData = nullptr; } -static uint32_t find_queue_family_index(const std::vector<VkQueueFamilyProperties2>& queue_family_properties) { - auto it = std::ranges::find_if(queue_family_properties, [](const auto& prop) { - return (prop.queueFamilyProperties.queueFlags & VK_QUEUE_GRAPHICS_BIT) != static_cast<VkQueueFlags>(0); - }); - return static_cast<uint32_t>(std::distance(queue_family_properties.begin(), it)); +static std::tuple<std::optional<uint32_t>, std::optional<uint32_t>> find_queue_family_indices( + VkPhysicalDevice physical_device, const std::vector<VkQueueFamilyProperties2>& queue_family_properties, VkSurfaceKHR surface) { + std::optional<uint32_t> graphics_queue_family_index, presentation_queue_family_index; + for (uint32_t i = 0; i < queue_family_properties.size(); i++) { + const auto& prop = queue_family_properties[i]; + bool is_graphics_queue = (prop.queueFamilyProperties.queueFlags & VK_QUEUE_GRAPHICS_BIT) != static_cast<VkQueueFlags>(0); + VkBool32 is_presentation_queue; + if (VkResult res = vkGetPhysicalDeviceSurfaceSupportKHR(physical_device, i, surface, &is_presentation_queue); res != VK_SUCCESS) { + std::cerr << "failed to check if queue family supports surface, error code: " << string_VkResult(res) << std::endl; + std::exit(EXIT_FAILURE); + } + if (is_graphics_queue && is_presentation_queue) { + graphics_queue_family_index = i; + presentation_queue_family_index = i; + break; + } else if (is_graphics_queue && !graphics_queue_family_index) { + graphics_queue_family_index = i; + } else if (is_presentation_queue && !presentation_queue_family_index) { + presentation_queue_family_index = i; + } + } + return { graphics_queue_family_index, presentation_queue_family_index }; } static int main_graphical() { @@ -441,9 +466,15 @@ static int main_graphical() { } uint32_t extension_count; - vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, nullptr); + if (VkResult res = vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, nullptr); res != VK_SUCCESS) { + std::cerr << "failed to enumerate instance extension properties, error code: " << string_VkResult(res) << std::endl; + std::exit(EXIT_FAILURE); + } std::vector<VkExtensionProperties> available_extensions(extension_count); - vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, available_extensions.data()); + if (VkResult res = vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, available_extensions.data()); res != VK_SUCCESS) { + std::cerr << "failed to enumerate instance extension properties, error code: " << string_VkResult(res) << std::endl; + std::exit(EXIT_FAILURE); + } std::cout << "vulkan extensions:\n"; for (const auto& extension : available_extensions) { @@ -497,175 +528,208 @@ static int main_graphical() { create_debug_messenger(instance, &msger_create_info, nullptr, &debug_messenger); } - // select physical device - uint32_t physical_devices_count; - vkEnumeratePhysicalDevices(instance, &physical_devices_count, nullptr); - std::vector<VkPhysicalDevice> physical_devices(physical_devices_count); - vkEnumeratePhysicalDevices(instance, &physical_devices_count, physical_devices.data()); - - if (physical_devices.empty()) { - std::cerr << "failed to find physical devices with Vulkan support" << std::endl; + // create window surface + VkSurfaceKHR surface; + if (VkResult res = glfwCreateWindowSurface(instance, window, nullptr, &surface); res != VK_SUCCESS) { + std::cerr << "failed to create window surface, error code: " << string_VkResult(res) << std::endl; std::exit(EXIT_FAILURE); } - VkPhysicalDevice device; - bool found = false; - for (const auto& physical_device : physical_devices) { - VkPhysicalDeviceProperties2 properties{}; - properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; - vkGetPhysicalDeviceProperties2(physical_device, &properties); - - VkPhysicalDeviceFeatures2 features{}; - features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; - vkGetPhysicalDeviceFeatures2(physical_device, &features); - - // TODO: found a better name, too confusing with device_exts - uint32_t ext_props_count; - vkEnumerateDeviceExtensionProperties(physical_device, nullptr, &ext_props_count, nullptr); - std::vector<VkExtensionProperties> ext_props(ext_props_count); - vkEnumerateDeviceExtensionProperties(physical_device, nullptr, &ext_props_count, ext_props.data()); - - uint32_t queue_family_properties_count; - vkGetPhysicalDeviceQueueFamilyProperties2(physical_device, &queue_family_properties_count, nullptr); - std::vector<VkQueueFamilyProperties2> queue_family_properties(queue_family_properties_count); - for (auto& elt : queue_family_properties) - elt.sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2; - vkGetPhysicalDeviceQueueFamilyProperties2(physical_device, &queue_family_properties_count, queue_family_properties.data()); - - std::cout << "structure type: " << string_VkStructureType(properties.sType) << std::endl; - std::cout << "pNext: " << properties.pNext << std::endl; - std::cout << "apiVersion: (variant " << VK_API_VERSION_VARIANT(properties.properties.apiVersion) << ") " - << VK_API_VERSION_MAJOR(properties.properties.apiVersion) - << "." << VK_API_VERSION_MINOR(properties.properties.apiVersion) - << "." << VK_API_VERSION_PATCH(properties.properties.apiVersion) - << std::endl; - std::cout << "driverVersion: " << properties.properties.driverVersion << std::endl; - std::cout << "vendorID: " << properties.properties.vendorID << std::endl; - std::cout << "deviceID: " << properties.properties.deviceID << std::endl; - std::cout << "deviceType: " << string_VkPhysicalDeviceType(properties.properties.deviceType) << std::endl; - std::cout << "deviceName: " << properties.properties.deviceName << std::endl; - // pipelineCacheUUID[VK_UUID_SIZE] - // limits - std::cout << "sparseProperties:" << std::endl; - std::cout << std::boolalpha; - std::cout << " residencyStandard2DBlockShape: " << static_cast<bool>(properties.properties.sparseProperties.residencyStandard2DBlockShape) << std::endl; - std::cout << " residencyStandard2DMultisampleBlockShape: " - << static_cast<bool>(properties.properties.sparseProperties.residencyStandard2DMultisampleBlockShape) << std::endl; - std::cout << " residencyStandard3DBlockShape: " << static_cast<bool>(properties.properties.sparseProperties.residencyStandard3DBlockShape) << std::endl; - std::cout << " residencyAlignedMipSize: " << static_cast<bool>(properties.properties.sparseProperties.residencyAlignedMipSize) << std::endl; - std::cout << " residencyNonResidentStrict: " << static_cast<bool>(properties.properties.sparseProperties.residencyNonResidentStrict) << std::endl; - std::cout << std::noboolalpha; - std::cout << "features:" << std::endl; - std::cout << std::boolalpha; - std::cout << " robustBufferAccess: " << static_cast<bool>(features.features.robustBufferAccess) << std::endl; - std::cout << " fullDrawIndexUint32: " << static_cast<bool>(features.features.fullDrawIndexUint32) << std::endl; - std::cout << " imageCubeArray: " << static_cast<bool>(features.features.imageCubeArray) << std::endl; - std::cout << " independentBlend: " << static_cast<bool>(features.features.independentBlend) << std::endl; - std::cout << " geometryShader: " << static_cast<bool>(features.features.geometryShader) << std::endl; - std::cout << " tessellationShader: " << static_cast<bool>(features.features.tessellationShader) << std::endl; - std::cout << " sampleRateShading: " << static_cast<bool>(features.features.sampleRateShading) << std::endl; - std::cout << " dualSrcBlend: " << static_cast<bool>(features.features.dualSrcBlend) << std::endl; - std::cout << " logicOp: " << static_cast<bool>(features.features.logicOp) << std::endl; - std::cout << " multiDrawIndirect: " << static_cast<bool>(features.features.multiDrawIndirect) << std::endl; - std::cout << " drawIndirectFirstInstance: " << static_cast<bool>(features.features.drawIndirectFirstInstance) << std::endl; - std::cout << " depthClamp: " << static_cast<bool>(features.features.depthClamp) << std::endl; - std::cout << " depthBiasClamp: " << static_cast<bool>(features.features.depthBiasClamp) << std::endl; - std::cout << " fillModeNonSolid: " << static_cast<bool>(features.features.fillModeNonSolid) << std::endl; - std::cout << " depthBounds: " << static_cast<bool>(features.features.depthBounds) << std::endl; - std::cout << " wideLines: " << static_cast<bool>(features.features.wideLines) << std::endl; - std::cout << " largePoints: " << static_cast<bool>(features.features.largePoints) << std::endl; - std::cout << " alphaToOne: " << static_cast<bool>(features.features.alphaToOne) << std::endl; - std::cout << " multiViewport: " << static_cast<bool>(features.features.multiViewport) << std::endl; - std::cout << " samplerAnisotropy: " << static_cast<bool>(features.features.samplerAnisotropy) << std::endl; - std::cout << " textureCompressionETC2: " << static_cast<bool>(features.features.textureCompressionETC2) << std::endl; - std::cout << " textureCompressionASTC_LDR: " << static_cast<bool>(features.features.textureCompressionASTC_LDR) << std::endl; - std::cout << " textureCompressionBC: " << static_cast<bool>(features.features.textureCompressionBC) << std::endl; - std::cout << " occlusionQueryPrecise: " << static_cast<bool>(features.features.occlusionQueryPrecise) << std::endl; - std::cout << " pipelineStatisticsQuery: " << static_cast<bool>(features.features.pipelineStatisticsQuery) << std::endl; - std::cout << " vertexPipelineStoresAndAtomics: " << static_cast<bool>(features.features.vertexPipelineStoresAndAtomics) << std::endl; - std::cout << " fragmentStoresAndAtomics: " << static_cast<bool>(features.features.fragmentStoresAndAtomics) << std::endl; - std::cout << " shaderTessellationAndGeometryPointSize: " << static_cast<bool>(features.features.shaderTessellationAndGeometryPointSize) << std::endl; - std::cout << " shaderImageGatherExtended: " << static_cast<bool>(features.features.shaderImageGatherExtended) << std::endl; - std::cout << " shaderStorageImageExtendedFormats: " << static_cast<bool>(features.features.shaderStorageImageExtendedFormats) << std::endl; - std::cout << " shaderStorageImageMultisample: " << static_cast<bool>(features.features.shaderStorageImageMultisample) << std::endl; - std::cout << " shaderStorageImageReadWithoutFormat: " << static_cast<bool>(features.features.shaderStorageImageReadWithoutFormat) << std::endl; - std::cout << " shaderStorageImageWriteWithoutFormat: " << static_cast<bool>(features.features.shaderStorageImageWriteWithoutFormat) << std::endl; - std::cout << " shaderUniformBufferArrayDynamicIndexing: " << static_cast<bool>(features.features.shaderUniformBufferArrayDynamicIndexing) << std::endl; - std::cout << " shaderSampledImageArrayDynamicIndexing: " << static_cast<bool>(features.features.shaderSampledImageArrayDynamicIndexing) << std::endl; - std::cout << " shaderStorageBufferArrayDynamicIndexing: " << static_cast<bool>(features.features.shaderStorageBufferArrayDynamicIndexing) << std::endl; - std::cout << " shaderStorageImageArrayDynamicIndexing: " << static_cast<bool>(features.features.shaderStorageImageArrayDynamicIndexing) << std::endl; - std::cout << " shaderClipDistance: " << static_cast<bool>(features.features.shaderClipDistance) << std::endl; - std::cout << " shaderCullDistance: " << static_cast<bool>(features.features.shaderCullDistance) << std::endl; - std::cout << " shaderFloat64: " << static_cast<bool>(features.features.shaderFloat64) << std::endl; - std::cout << " shaderInt64: " << static_cast<bool>(features.features.shaderInt64) << std::endl; - std::cout << " shaderInt16: " << static_cast<bool>(features.features.shaderInt16) << std::endl; - std::cout << " shaderResourceResidency: " << static_cast<bool>(features.features.shaderResourceResidency) << std::endl; - std::cout << " shaderResourceMinLod: " << static_cast<bool>(features.features.shaderResourceMinLod) << std::endl; - std::cout << " sparseBinding: " << static_cast<bool>(features.features.sparseBinding) << std::endl; - std::cout << " sparseResidencyBuffer: " << static_cast<bool>(features.features.sparseResidencyBuffer) << std::endl; - std::cout << " sparseResidencyImage2D: " << static_cast<bool>(features.features.sparseResidencyImage2D) << std::endl; - std::cout << " sparseResidencyImage3D: " << static_cast<bool>(features.features.sparseResidencyImage3D) << std::endl; - std::cout << " sparseResidency2Samples: " << static_cast<bool>(features.features.sparseResidency2Samples) << std::endl; - std::cout << " sparseResidency4Samples: " << static_cast<bool>(features.features.sparseResidency4Samples) << std::endl; - std::cout << " sparseResidency8Samples: " << static_cast<bool>(features.features.sparseResidency8Samples) << std::endl; - std::cout << " sparseResidency16Samples: " << static_cast<bool>(features.features.sparseResidency16Samples) << std::endl; - std::cout << " sparseResidencyAliased: " << static_cast<bool>(features.features.sparseResidencyAliased) << std::endl; - std::cout << " variableMultisampleRate: " << static_cast<bool>(features.features.variableMultisampleRate) << std::endl; - std::cout << " inheritedQueries: " << static_cast<bool>(features.features.inheritedQueries) << std::endl; - std::cout << std::noboolalpha; - std::cout << "extensions:" << std::endl; - for (const auto& ext : ext_props) { - std::cout - << (std::ranges::find_if(device_exts, [ext_name=ext.extensionName](const auto& device_ext) { return strcmp(ext_name, device_ext) == 0; }) - != device_exts.end() ? "*" : " "); - std::cout << " " << ext.extensionName << std::endl; + // select physical device and queues + auto [device, graphics_queue_family_index, presentation_queue_family_index, device_features] = [instance, surface]() { + std::optional<VkPhysicalDevice> device; + uint32_t res_graphics_queue_family_index, res_presentation_queue_family_index; + VkPhysicalDeviceFeatures2 res_features; + + uint32_t physical_devices_count; + if (VkResult res = vkEnumeratePhysicalDevices(instance, &physical_devices_count, nullptr); res != VK_SUCCESS) { + std::cerr << "failed to enumerate physical devices, error code: " << string_VkResult(res) << std::endl; + std::exit(EXIT_FAILURE); + } + std::vector<VkPhysicalDevice> physical_devices(physical_devices_count); + if (VkResult res = vkEnumeratePhysicalDevices(instance, &physical_devices_count, physical_devices.data()); res != VK_SUCCESS) { + std::cerr << "failed to enumerate physical devices, error code: " << string_VkResult(res) << std::endl; + std::exit(EXIT_FAILURE); } - bool is_suitable = [&properties, &queue_family_properties, &ext_props]() { - if (VK_API_VERSION_VARIANT(properties.properties.apiVersion) != 0 - || properties.properties.apiVersion < VK_API_VERSION_1_4) - return false; - if (find_queue_family_index(queue_family_properties) == queue_family_properties.size()) - return false; - if (std::ranges::find_if_not(device_exts, [&ext_props](const auto& device_ext) { - return std::ranges::find_if(ext_props, - [device_ext](const auto& ext_prop) { return strcmp(ext_prop.extensionName, device_ext) == 0; }) - != ext_props.end(); - }) != device_exts.end()) - return false; - return true; - }(); - std::cout << "is_suitable: " << std::boolalpha << is_suitable << std::noboolalpha; - if (!found && is_suitable) { - found = true; - std::cout << " (picking this one)"; - device = physical_device; + if (physical_devices.empty()) { + std::cerr << "failed to find physical devices with Vulkan support" << std::endl; + std::exit(EXIT_FAILURE); } - std::cout << std::endl; - } - if (!found) { - std::cerr << "no suitable physical device found" << std::endl; - std::exit(EXIT_FAILURE); - } + for (const auto& physical_device : physical_devices) { + VkPhysicalDeviceProperties2 properties{}; + properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + vkGetPhysicalDeviceProperties2(physical_device, &properties); - // TODO: shouldn't fetch twice (already fetched while selecting physical device) - uint32_t queue_family_index = [device]() { - uint32_t queue_family_properties_count; - vkGetPhysicalDeviceQueueFamilyProperties2(device, &queue_family_properties_count, nullptr); - std::vector<VkQueueFamilyProperties2> queue_family_properties(queue_family_properties_count); - for (auto& elt : queue_family_properties) - elt.sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2; - vkGetPhysicalDeviceQueueFamilyProperties2(device, &queue_family_properties_count, queue_family_properties.data()); - - // don't need to check if it's found, we already checked that in device seleciton - return find_queue_family_index(queue_family_properties); + VkPhysicalDeviceFeatures2 features{}; + features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + vkGetPhysicalDeviceFeatures2(physical_device, &features); + + // TODO: found a better name, too confusing with device_exts + uint32_t ext_props_count; + if (VkResult res = vkEnumerateDeviceExtensionProperties(physical_device, nullptr, &ext_props_count, nullptr); res != VK_SUCCESS) { + std::cerr << "failed to enumerate device extension properties, error code: " << string_VkResult(res) << std::endl; + std::exit(EXIT_FAILURE); + } + std::vector<VkExtensionProperties> ext_props(ext_props_count); + if (VkResult res = vkEnumerateDeviceExtensionProperties(physical_device, nullptr, &ext_props_count, ext_props.data()); res != VK_SUCCESS) { + std::cerr << "failed to enumerate device extension properties, error code: " << string_VkResult(res) << std::endl; + std::exit(EXIT_FAILURE); + } + + uint32_t queue_family_properties_count; + vkGetPhysicalDeviceQueueFamilyProperties2(physical_device, &queue_family_properties_count, nullptr); + std::vector<VkQueueFamilyProperties2> queue_family_properties(queue_family_properties_count); + for (auto& elt : queue_family_properties) + elt.sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2; + vkGetPhysicalDeviceQueueFamilyProperties2(physical_device, &queue_family_properties_count, queue_family_properties.data()); + + std::cout << "structure type: " << string_VkStructureType(properties.sType) << std::endl; + std::cout << "pNext: " << properties.pNext << std::endl; + std::cout << "apiVersion: (variant " << VK_API_VERSION_VARIANT(properties.properties.apiVersion) << ") " + << VK_API_VERSION_MAJOR(properties.properties.apiVersion) + << "." << VK_API_VERSION_MINOR(properties.properties.apiVersion) + << "." << VK_API_VERSION_PATCH(properties.properties.apiVersion) + << std::endl; + std::cout << "driverVersion: " << properties.properties.driverVersion << std::endl; + std::cout << "vendorID: " << properties.properties.vendorID << std::endl; + std::cout << "deviceID: " << properties.properties.deviceID << std::endl; + std::cout << "deviceType: " << string_VkPhysicalDeviceType(properties.properties.deviceType) << std::endl; + std::cout << "deviceName: " << properties.properties.deviceName << std::endl; + // pipelineCacheUUID[VK_UUID_SIZE] + // limits + std::cout << "sparseProperties:" << std::endl; + std::cout << std::boolalpha; + std::cout << " residencyStandard2DBlockShape: " << static_cast<bool>(properties.properties.sparseProperties.residencyStandard2DBlockShape) << std::endl; + std::cout << " residencyStandard2DMultisampleBlockShape: " + << static_cast<bool>(properties.properties.sparseProperties.residencyStandard2DMultisampleBlockShape) << std::endl; + std::cout << " residencyStandard3DBlockShape: " << static_cast<bool>(properties.properties.sparseProperties.residencyStandard3DBlockShape) << std::endl; + std::cout << " residencyAlignedMipSize: " << static_cast<bool>(properties.properties.sparseProperties.residencyAlignedMipSize) << std::endl; + std::cout << " residencyNonResidentStrict: " << static_cast<bool>(properties.properties.sparseProperties.residencyNonResidentStrict) << std::endl; + std::cout << std::noboolalpha; + std::cout << "features:" << std::endl; + std::cout << std::boolalpha; + std::cout << " robustBufferAccess: " << static_cast<bool>(features.features.robustBufferAccess) << std::endl; + std::cout << " fullDrawIndexUint32: " << static_cast<bool>(features.features.fullDrawIndexUint32) << std::endl; + std::cout << " imageCubeArray: " << static_cast<bool>(features.features.imageCubeArray) << std::endl; + std::cout << " independentBlend: " << static_cast<bool>(features.features.independentBlend) << std::endl; + std::cout << " geometryShader: " << static_cast<bool>(features.features.geometryShader) << std::endl; + std::cout << " tessellationShader: " << static_cast<bool>(features.features.tessellationShader) << std::endl; + std::cout << " sampleRateShading: " << static_cast<bool>(features.features.sampleRateShading) << std::endl; + std::cout << " dualSrcBlend: " << static_cast<bool>(features.features.dualSrcBlend) << std::endl; + std::cout << " logicOp: " << static_cast<bool>(features.features.logicOp) << std::endl; + std::cout << " multiDrawIndirect: " << static_cast<bool>(features.features.multiDrawIndirect) << std::endl; + std::cout << " drawIndirectFirstInstance: " << static_cast<bool>(features.features.drawIndirectFirstInstance) << std::endl; + std::cout << " depthClamp: " << static_cast<bool>(features.features.depthClamp) << std::endl; + std::cout << " depthBiasClamp: " << static_cast<bool>(features.features.depthBiasClamp) << std::endl; + std::cout << " fillModeNonSolid: " << static_cast<bool>(features.features.fillModeNonSolid) << std::endl; + std::cout << " depthBounds: " << static_cast<bool>(features.features.depthBounds) << std::endl; + std::cout << " wideLines: " << static_cast<bool>(features.features.wideLines) << std::endl; + std::cout << " largePoints: " << static_cast<bool>(features.features.largePoints) << std::endl; + std::cout << " alphaToOne: " << static_cast<bool>(features.features.alphaToOne) << std::endl; + std::cout << " multiViewport: " << static_cast<bool>(features.features.multiViewport) << std::endl; + std::cout << " samplerAnisotropy: " << static_cast<bool>(features.features.samplerAnisotropy) << std::endl; + std::cout << " textureCompressionETC2: " << static_cast<bool>(features.features.textureCompressionETC2) << std::endl; + std::cout << " textureCompressionASTC_LDR: " << static_cast<bool>(features.features.textureCompressionASTC_LDR) << std::endl; + std::cout << " textureCompressionBC: " << static_cast<bool>(features.features.textureCompressionBC) << std::endl; + std::cout << " occlusionQueryPrecise: " << static_cast<bool>(features.features.occlusionQueryPrecise) << std::endl; + std::cout << " pipelineStatisticsQuery: " << static_cast<bool>(features.features.pipelineStatisticsQuery) << std::endl; + std::cout << " vertexPipelineStoresAndAtomics: " << static_cast<bool>(features.features.vertexPipelineStoresAndAtomics) << std::endl; + std::cout << " fragmentStoresAndAtomics: " << static_cast<bool>(features.features.fragmentStoresAndAtomics) << std::endl; + std::cout << " shaderTessellationAndGeometryPointSize: " << static_cast<bool>(features.features.shaderTessellationAndGeometryPointSize) << std::endl; + std::cout << " shaderImageGatherExtended: " << static_cast<bool>(features.features.shaderImageGatherExtended) << std::endl; + std::cout << " shaderStorageImageExtendedFormats: " << static_cast<bool>(features.features.shaderStorageImageExtendedFormats) << std::endl; + std::cout << " shaderStorageImageMultisample: " << static_cast<bool>(features.features.shaderStorageImageMultisample) << std::endl; + std::cout << " shaderStorageImageReadWithoutFormat: " << static_cast<bool>(features.features.shaderStorageImageReadWithoutFormat) << std::endl; + std::cout << " shaderStorageImageWriteWithoutFormat: " << static_cast<bool>(features.features.shaderStorageImageWriteWithoutFormat) << std::endl; + std::cout << " shaderUniformBufferArrayDynamicIndexing: " << static_cast<bool>(features.features.shaderUniformBufferArrayDynamicIndexing) << std::endl; + std::cout << " shaderSampledImageArrayDynamicIndexing: " << static_cast<bool>(features.features.shaderSampledImageArrayDynamicIndexing) << std::endl; + std::cout << " shaderStorageBufferArrayDynamicIndexing: " << static_cast<bool>(features.features.shaderStorageBufferArrayDynamicIndexing) << std::endl; + std::cout << " shaderStorageImageArrayDynamicIndexing: " << static_cast<bool>(features.features.shaderStorageImageArrayDynamicIndexing) << std::endl; + std::cout << " shaderClipDistance: " << static_cast<bool>(features.features.shaderClipDistance) << std::endl; + std::cout << " shaderCullDistance: " << static_cast<bool>(features.features.shaderCullDistance) << std::endl; + std::cout << " shaderFloat64: " << static_cast<bool>(features.features.shaderFloat64) << std::endl; + std::cout << " shaderInt64: " << static_cast<bool>(features.features.shaderInt64) << std::endl; + std::cout << " shaderInt16: " << static_cast<bool>(features.features.shaderInt16) << std::endl; + std::cout << " shaderResourceResidency: " << static_cast<bool>(features.features.shaderResourceResidency) << std::endl; + std::cout << " shaderResourceMinLod: " << static_cast<bool>(features.features.shaderResourceMinLod) << std::endl; + std::cout << " sparseBinding: " << static_cast<bool>(features.features.sparseBinding) << std::endl; + std::cout << " sparseResidencyBuffer: " << static_cast<bool>(features.features.sparseResidencyBuffer) << std::endl; + std::cout << " sparseResidencyImage2D: " << static_cast<bool>(features.features.sparseResidencyImage2D) << std::endl; + std::cout << " sparseResidencyImage3D: " << static_cast<bool>(features.features.sparseResidencyImage3D) << std::endl; + std::cout << " sparseResidency2Samples: " << static_cast<bool>(features.features.sparseResidency2Samples) << std::endl; + std::cout << " sparseResidency4Samples: " << static_cast<bool>(features.features.sparseResidency4Samples) << std::endl; + std::cout << " sparseResidency8Samples: " << static_cast<bool>(features.features.sparseResidency8Samples) << std::endl; + std::cout << " sparseResidency16Samples: " << static_cast<bool>(features.features.sparseResidency16Samples) << std::endl; + std::cout << " sparseResidencyAliased: " << static_cast<bool>(features.features.sparseResidencyAliased) << std::endl; + std::cout << " variableMultisampleRate: " << static_cast<bool>(features.features.variableMultisampleRate) << std::endl; + std::cout << " inheritedQueries: " << static_cast<bool>(features.features.inheritedQueries) << std::endl; + std::cout << std::noboolalpha; + std::cout << "extensions:" << std::endl; + for (const auto& ext : ext_props) { + std::cout + << (std::ranges::find_if(device_exts, [ext_name=ext.extensionName](const auto& device_ext) { return strcmp(ext_name, device_ext) == 0; }) + != device_exts.end() ? "*" : " "); + std::cout << " " << ext.extensionName << std::endl; + } + + auto [graphics_queue_family_index, presentation_queue_family_index] = find_queue_family_indices(physical_device, queue_family_properties, surface); + std::cout << "graphics queue family index: "; + if (graphics_queue_family_index) + std::cout << *graphics_queue_family_index; + else + std::cout << "none"; + std::cout << std::endl; + std::cout << "presentation queue family index: "; + if (presentation_queue_family_index) + std::cout << *presentation_queue_family_index; + else + std::cout << "none"; + std::cout << std::endl; + + bool is_suitable = [&properties, &queue_family_properties, &ext_props, &graphics_queue_family_index, &presentation_queue_family_index]() { + if (VK_API_VERSION_VARIANT(properties.properties.apiVersion) != 0 + || properties.properties.apiVersion < VK_API_VERSION_1_4) + return false; + if (!graphics_queue_family_index || !presentation_queue_family_index) + return false; + if (std::ranges::find_if_not(device_exts, [&ext_props](const auto& device_ext) { + return std::ranges::find_if(ext_props, + [device_ext](const auto& ext_prop) { return strcmp(ext_prop.extensionName, device_ext) == 0; }) + != ext_props.end(); + }) != device_exts.end()) + return false; + return true; + }(); + std::cout << "is_suitable: " << std::boolalpha << is_suitable << std::noboolalpha; + if (!device && is_suitable) { + std::cout << " (picking this one)"; + device = physical_device; + res_graphics_queue_family_index = *graphics_queue_family_index; + res_presentation_queue_family_index = *presentation_queue_family_index; + res_features = features; + } + std::cout << std::endl; + } + + if (!device) { + std::cerr << "no suitable physical device found" << std::endl; + std::exit(EXIT_FAILURE); + } + + return std::tuple<VkPhysicalDevice, uint32_t, uint32_t, VkPhysicalDeviceFeatures2> + { *device, res_graphics_queue_family_index, res_presentation_queue_family_index, res_features }; }(); + // TODO: We only create the graphics queue, I don't know if we have to also create the + // presentation queue. They don't create it in the official Vulkan tutorial, but it might cause + // a bug on some rare platforms where there is no queue that's both graphics and presentation. + // IDK. float queue_priority = .5f; VkDeviceQueueCreateInfo deviceQueueCreateInfo{}; deviceQueueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - deviceQueueCreateInfo.queueFamilyIndex = queue_family_index; + deviceQueueCreateInfo.queueFamilyIndex = graphics_queue_family_index; deviceQueueCreateInfo.queueCount = 1; deviceQueueCreateInfo.pQueuePriorities = &queue_priority; @@ -675,10 +739,7 @@ static int main_graphical() { device_create_info.pQueueCreateInfos = &deviceQueueCreateInfo; device_create_info.enabledExtensionCount = static_cast<uint32_t>(device_exts.size()); device_create_info.ppEnabledExtensionNames = device_exts.data(); - - // set everything to false - VkPhysicalDeviceFeatures2 device_features{}; - device_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + // we get device features while selection the physical device device_create_info.pNext = &device_features; VkPhysicalDeviceVulkan13Features device_vk13_features{}; @@ -699,7 +760,142 @@ static int main_graphical() { } VkQueue graphics_queue; - vkGetDeviceQueue(logical_device, queue_family_index, 0, &graphics_queue); + vkGetDeviceQueue(logical_device, graphics_queue_family_index, 0, &graphics_queue); + + VkQueue presentation_queue; + vkGetDeviceQueue(logical_device, presentation_queue_family_index, 0, &presentation_queue); + + // create swap chain + // TODO: should probably use version 2 of theses functions, but glfwCreateWindowSurface return + // version 1, so for now we will use version 1 + VkSurfaceCapabilitiesKHR surface_capabilities; + if (VkResult res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &surface_capabilities); res != VK_SUCCESS) { + std::cerr << "failed to get physical device surface capabilities, error code: " << string_VkResult(res) << std::endl; + std::exit(EXIT_FAILURE); + } + + VkExtent2D swapchain_extent = [surface_capabilities, window]() { + if (surface_capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max()) + return surface_capabilities.currentExtent; + int width, height; + glfwGetFramebufferSize(window, &width, &height); + return VkExtent2D{ + .width = std::clamp(static_cast<uint32_t>(width), surface_capabilities.minImageExtent.width, surface_capabilities.maxImageExtent.width), + .height = std::clamp(static_cast<uint32_t>(height), surface_capabilities.minImageExtent.height, surface_capabilities.maxImageExtent.height), + }; + }(); + + VkSurfaceFormatKHR surface_format = [device, surface]() { + uint32_t surface_formats_count; + if (VkResult res = vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &surface_formats_count, nullptr); res != VK_SUCCESS) { + std::cerr << "failed to get physical device surface formats, error code: " << string_VkResult(res) << std::endl; + std::exit(EXIT_FAILURE); + } + std::vector<VkSurfaceFormatKHR> surface_formats(surface_formats_count); + if (VkResult res = vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &surface_formats_count, surface_formats.data()); res != VK_SUCCESS) { + std::cerr << "failed to get physical device surface formats, error code: " << string_VkResult(res) << std::endl; + std::exit(EXIT_FAILURE); + } + + for (const auto& surface_format : surface_formats) { + if (surface_format.format == VK_FORMAT_B8G8R8A8_SRGB + && surface_format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) + return surface_format; + } + + // not found + std::cerr << "surface format not found (VK_FORMAT_B8G8R8_SRGB and VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)" << std::endl; + std::exit(EXIT_FAILURE); + }(); + + VkPresentModeKHR present_mode = [device, surface]() { + uint32_t present_modes_count; + if (VkResult res = vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &present_modes_count, nullptr); res != VK_SUCCESS) { + std::cerr << "failed to get physical device present modes, error code: " << string_VkResult(res) << std::endl; + std::exit(EXIT_FAILURE); + } + std::vector<VkPresentModeKHR> present_modes(present_modes_count); + if (VkResult res = vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &present_modes_count, present_modes.data()); res != VK_SUCCESS) { + std::cerr << "failed to get physical device present modes, error code: " << string_VkResult(res) << std::endl; + std::exit(EXIT_FAILURE); + } + + for (const auto& present_mode : present_modes) + if (present_mode == VK_PRESENT_MODE_MAILBOX_KHR) + return present_mode; + + return VK_PRESENT_MODE_FIFO_KHR; + }(); + + // TODO: remove unnecessary static_cast<uint32_t>, but at this moment I'm not sure where they + // are necessary + uint32_t min_image_count = std::max(static_cast<uint32_t>(3), surface_capabilities.minImageCount + static_cast<uint32_t>(1)); + if (surface_capabilities.maxImageCount > 0 && surface_capabilities.maxImageCount < min_image_count) + min_image_count = surface_capabilities.maxImageCount; + + // might not be used, but if we do, we have to keep it in memory until the call to vkCreateSwapchainKHR() + std::array<uint32_t, 2> queue_family_indices; + VkSwapchainCreateInfoKHR swapchain_create_info{}; + swapchain_create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + swapchain_create_info.flags = VkSwapchainCreateFlagsKHR{}; + swapchain_create_info.surface = surface; + swapchain_create_info.minImageCount = min_image_count; + swapchain_create_info.imageFormat = surface_format.format; + swapchain_create_info.imageColorSpace = surface_format.colorSpace; + swapchain_create_info.imageExtent = swapchain_extent; + swapchain_create_info.imageArrayLayers = 1; + swapchain_create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + swapchain_create_info.preTransform = surface_capabilities.currentTransform; + swapchain_create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + swapchain_create_info.presentMode = present_mode; + swapchain_create_info.clipped = VK_TRUE; + if (graphics_queue_family_index == presentation_queue_family_index) { + swapchain_create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + } else { + swapchain_create_info.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + queue_family_indices[0] = graphics_queue_family_index; + queue_family_indices[1] = presentation_queue_family_index; + swapchain_create_info.queueFamilyIndexCount = 2; + swapchain_create_info.pQueueFamilyIndices = queue_family_indices.data(); + } + + VkSwapchainKHR swapchain; + if (VkResult res = vkCreateSwapchainKHR(logical_device, &swapchain_create_info, nullptr, &swapchain); res != VK_SUCCESS) { + std::cerr << "failed create swapchain, error code: " << string_VkResult(res) << std::endl; + std::exit(EXIT_FAILURE); + } + + uint32_t swapchain_imgs_count; + if (VkResult res = vkGetSwapchainImagesKHR(logical_device, swapchain, &swapchain_imgs_count, nullptr); res != VK_SUCCESS) { + std::cerr << "failed to get swapchain images, error code: " << string_VkResult(res) << std::endl; + std::exit(EXIT_FAILURE); + } + std::vector<VkImage> swapchain_imgs(swapchain_imgs_count); + if (VkResult res = vkGetSwapchainImagesKHR(logical_device, swapchain, &swapchain_imgs_count, swapchain_imgs.data()); res != VK_SUCCESS) { + std::cerr << "failed to get swapchain images, error code: " << string_VkResult(res) << std::endl; + std::exit(EXIT_FAILURE); + } + + std::vector<VkImageView> swapchain_img_views(swapchain_imgs.size()); + for (uint32_t i = 0; i < swapchain_imgs.size(); i++) { + VkImageViewCreateInfo img_view_create_info { + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .image = swapchain_imgs[i], + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = surface_format.format, + .subresourceRange = { // VkImageSubresourceRange + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + }; + if (VkResult res = vkCreateImageView(logical_device, &img_view_create_info, nullptr, &swapchain_img_views[i]); res != VK_SUCCESS) { + std::cerr << "failed to create image view, error code: " << string_VkResult(res) << std::endl; + std::exit(EXIT_FAILURE); + } + } // main loop while (!glfwWindowShouldClose(window)) { @@ -707,6 +903,10 @@ static int main_graphical() { } // cleanup + for (const auto img_view : swapchain_img_views) + vkDestroyImageView(logical_device, img_view, nullptr); + vkDestroySwapchainKHR(logical_device, swapchain, nullptr); + vkDestroySurfaceKHR(instance, surface, nullptr); vkDestroyDevice(logical_device, nullptr); if (enable_validation_layers) { |
