aboutsummaryrefslogtreecommitdiff
path: root/src/engine.cpp
diff options
context:
space:
mode:
authorvimene <vincent.menegaux@gmail.com>2025-12-17 14:04:39 +0100
committervimene <vincent.menegaux@gmail.com>2025-12-17 14:04:39 +0100
commitd727209c071f13c4761e3d3e4d395f11d134738c (patch)
tree25066cc3f8cc1babea7a36b4dcba67a06fa08c4b /src/engine.cpp
parent979334a2f9a99fbe1222a49ee1c2302214bc2dfc (diff)
downloadengine-d727209c071f13c4761e3d3e4d395f11d134738c.tar.gz
started working on presentation
- fixed missing error code checks - use physical device features instead of setting everything to false - prefer a single queue if it can do graphics and presentation, instead of just picking the first graphics queue and the first presentation queue
Diffstat (limited to 'src/engine.cpp')
-rw-r--r--src/engine.cpp542
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) {