From b4f923f502eee6fa6f5dbaf16d882ab5d8258112 Mon Sep 17 00:00:00 2001 From: Sascha Willems <webmaster@saschawillems.de> Date: Sat, 19 Oct 2024 14:14:21 +0200 Subject: [PATCH 01/11] Replace deprecated debug reports extension with debug utils Fix validation layers not being enabled in debug builds Refs #1168 --- samples/api/hello_triangle/hello_triangle.cpp | 63 ++++++++++--------- samples/api/hello_triangle/hello_triangle.h | 4 +- 2 files changed, 36 insertions(+), 31 deletions(-) diff --git a/samples/api/hello_triangle/hello_triangle.cpp b/samples/api/hello_triangle/hello_triangle.cpp index 33aec7d81..689124cdc 100644 --- a/samples/api/hello_triangle/hello_triangle.cpp +++ b/samples/api/hello_triangle/hello_triangle.cpp @@ -24,26 +24,28 @@ #include "platform/window.h" #if defined(VKB_DEBUG) || defined(VKB_VALIDATION_LAYERS) -/// @brief A debug callback called from Vulkan validation layers. -static VKAPI_ATTR VkBool32 VKAPI_CALL debug_callback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT type, - uint64_t object, size_t location, int32_t message_code, - const char *layer_prefix, const char *message, void *user_data) +/// @brief A debug callback used to report messages from the validation layers. See instance creation for details on how this is set up +static VKAPI_ATTR VkBool32 VKAPI_CALL debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity, VkDebugUtilsMessageTypeFlagsEXT message_type, + const VkDebugUtilsMessengerCallbackDataEXT *callback_data, + void *user_data) { - if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) + (void) user_data; + + if (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) { - LOGE("Validation Layer: Error: {}: {}", layer_prefix, message); + LOGE("{} Validation Layer: Error: {}: {}", callback_data->messageIdNumber, callback_data->pMessageIdName, callback_data->pMessage) } - else if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT) + else if (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) { - LOGE("Validation Layer: Warning: {}: {}", layer_prefix, message); + LOGE("{} Validation Layer: Warning: {}: {}", callback_data->messageIdNumber, callback_data->pMessageIdName, callback_data->pMessage) } - else if (flags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT) + else if (message_type & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT) { - LOGI("Validation Layer: Performance warning: {}: {}", layer_prefix, message); + LOGI("{} Validation Layer: Performance warning: {}: {}", callback_data->messageIdNumber, callback_data->pMessageIdName, callback_data->pMessage) } else { - LOGI("Validation Layer: Information: {}: {}", layer_prefix, message); + LOGI("{} Validation Layer: Information: {}: {}", callback_data->messageIdNumber, callback_data->pMessageIdName, callback_data->pMessage) } return VK_FALSE; } @@ -176,22 +178,22 @@ void HelloTriangle::init_instance(Context &context, std::vector<const char *> active_instance_extensions(required_instance_extensions); #if defined(VKB_DEBUG) || defined(VKB_VALIDATION_LAYERS) - bool has_debug_report = false; + // Validation layers help finding wrong api usage, we enable them when explicitly requested or in debug builds + // For this we use the debug utils extension if it is supported + bool has_debug_utils = false; for (const auto &ext : available_instance_extensions) { - if (strcmp(ext.extensionName, VK_EXT_DEBUG_REPORT_EXTENSION_NAME) == 0) + if (strcmp(ext.extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME) == 0) { - has_debug_report = true; + has_debug_utils = true; + active_instance_extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); break; } } - if (has_debug_report) + if (!has_debug_utils) { - active_instance_extensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); - } - else - { - LOGW("{} is not available; disabling debug reporting", VK_EXT_DEBUG_REPORT_EXTENSION_NAME); + LOGW("{} not supported or available", VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + LOGW("Make sure to compile the sample in debug mode and/or enable the validation layers"); } #endif @@ -238,7 +240,7 @@ void HelloTriangle::init_instance(Context &context, std::vector<const char *> requested_validation_layers(required_validation_layers); -#ifdef VKB_VALIDATION_LAYERS +#if defined(VKB_DEBUG) || defined(VKB_VALIDATION_LAYERS) // Determine the optimal validation layers to enable that are necessary for useful debugging std::vector<const char *> optimal_validation_layers = vkb::get_optimal_validation_layers(supported_validation_layers); requested_validation_layers.insert(requested_validation_layers.end(), optimal_validation_layers.begin(), optimal_validation_layers.end()); @@ -270,13 +272,16 @@ void HelloTriangle::init_instance(Context &context, instance_info.ppEnabledLayerNames = requested_validation_layers.data(); #if defined(VKB_DEBUG) || defined(VKB_VALIDATION_LAYERS) - VkDebugReportCallbackCreateInfoEXT debug_report_create_info = {VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT}; - if (has_debug_report) + // Validation layers help finding wrong api usage, we enable them when explicitly requested or in debug builds + // For this we use the debug utils extension if it is supported + VkDebugUtilsMessengerCreateInfoEXT debug_utils_create_info = {VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT}; + if (has_debug_utils) { - debug_report_create_info.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; - debug_report_create_info.pfnCallback = debug_callback; + debug_utils_create_info.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; + debug_utils_create_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT; + debug_utils_create_info.pfnUserCallback = debug_callback; - instance_info.pNext = &debug_report_create_info; + instance_info.pNext = &debug_utils_create_info; } #endif @@ -293,9 +298,9 @@ void HelloTriangle::init_instance(Context &context, volkLoadInstance(context.instance); #if defined(VKB_DEBUG) || defined(VKB_VALIDATION_LAYERS) - if (has_debug_report) + if (has_debug_utils) { - VK_CHECK(vkCreateDebugReportCallbackEXT(context.instance, &debug_report_create_info, nullptr, &context.debug_callback)); + VK_CHECK(vkCreateDebugUtilsMessengerEXT(context.instance, &debug_utils_create_info, nullptr, &context.debug_callback)); } #endif } @@ -1062,7 +1067,7 @@ void HelloTriangle::teardown(Context &context) if (context.debug_callback != VK_NULL_HANDLE) { - vkDestroyDebugReportCallbackEXT(context.instance, context.debug_callback, nullptr); + vkDestroyDebugUtilsMessengerEXT(context.instance, context.debug_callback, nullptr); context.debug_callback = VK_NULL_HANDLE; } diff --git a/samples/api/hello_triangle/hello_triangle.h b/samples/api/hello_triangle/hello_triangle.h index a093769f4..0f3c62614 100644 --- a/samples/api/hello_triangle/hello_triangle.h +++ b/samples/api/hello_triangle/hello_triangle.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2018-2023, Arm Limited and Contributors +/* Copyright (c) 2018-2024, Arm Limited and Contributors * * SPDX-License-Identifier: Apache-2.0 * @@ -110,7 +110,7 @@ class HelloTriangle : public vkb::Application VkPipelineLayout pipeline_layout = VK_NULL_HANDLE; /// The debug report callback. - VkDebugReportCallbackEXT debug_callback = VK_NULL_HANDLE; + VkDebugUtilsMessengerEXT debug_callback = VK_NULL_HANDLE; /// A set of semaphores that can be reused. std::vector<VkSemaphore> recycled_semaphores; From 687bee876e2d6e3c244a7cb7670fe5a7a708b812 Mon Sep 17 00:00:00 2001 From: Sascha Willems <webmaster@saschawillems.de> Date: Sat, 19 Oct 2024 17:14:12 +0200 Subject: [PATCH 02/11] Update comment --- samples/api/hello_triangle/hello_triangle.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/api/hello_triangle/hello_triangle.h b/samples/api/hello_triangle/hello_triangle.h index 0f3c62614..bef309cb6 100644 --- a/samples/api/hello_triangle/hello_triangle.h +++ b/samples/api/hello_triangle/hello_triangle.h @@ -109,7 +109,7 @@ class HelloTriangle : public vkb::Application */ VkPipelineLayout pipeline_layout = VK_NULL_HANDLE; - /// The debug report callback. + /// The debug utility callback. VkDebugUtilsMessengerEXT debug_callback = VK_NULL_HANDLE; /// A set of semaphores that can be reused. From 0bc8b33d8fca99ced98c0e6a0602da9761812dea Mon Sep 17 00:00:00 2001 From: Sascha Willems <webmaster@saschawillems.de> Date: Sat, 19 Oct 2024 17:25:09 +0200 Subject: [PATCH 03/11] Fix error messages --- samples/api/hello_triangle/hello_triangle.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/api/hello_triangle/hello_triangle.cpp b/samples/api/hello_triangle/hello_triangle.cpp index 689124cdc..38d2733fc 100644 --- a/samples/api/hello_triangle/hello_triangle.cpp +++ b/samples/api/hello_triangle/hello_triangle.cpp @@ -358,7 +358,7 @@ void HelloTriangle::init_device(Context &context, if (context.graphics_queue_index < 0) { - LOGE("Did not find suitable queue which supports graphics, compute and presentation."); + LOGE("Did not find suitable queue which supports graphics and presentation."); } uint32_t device_extension_count; @@ -368,7 +368,7 @@ void HelloTriangle::init_device(Context &context, if (!validate_extensions(required_device_extensions, device_extensions)) { - throw std::runtime_error("Required device extensions are missing, will try without."); + throw std::runtime_error("Required device extensions are missing."); } float queue_priority = 1.0f; From be7969005c25c6ffa949723c45577fb0714ba691 Mon Sep 17 00:00:00 2001 From: Sascha Willems <webmaster@saschawillems.de> Date: Sat, 19 Oct 2024 18:57:39 +0200 Subject: [PATCH 04/11] Remove unnecessary per-frame properties --- samples/api/hello_triangle/hello_triangle.cpp | 10 ++-------- samples/api/hello_triangle/hello_triangle.h | 18 +++++------------- 2 files changed, 7 insertions(+), 21 deletions(-) diff --git a/samples/api/hello_triangle/hello_triangle.cpp b/samples/api/hello_triangle/hello_triangle.cpp index 38d2733fc..d9226c228 100644 --- a/samples/api/hello_triangle/hello_triangle.cpp +++ b/samples/api/hello_triangle/hello_triangle.cpp @@ -412,9 +412,6 @@ void HelloTriangle::init_per_frame(Context &context, PerFrame &per_frame) cmd_buf_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; cmd_buf_info.commandBufferCount = 1; VK_CHECK(vkAllocateCommandBuffers(context.device, &cmd_buf_info, &per_frame.primary_command_buffer)); - - per_frame.device = context.device; - per_frame.queue_index = context.graphics_queue_index; } /** @@ -458,9 +455,6 @@ void HelloTriangle::teardown_per_frame(Context &context, PerFrame &per_frame) per_frame.swapchain_release_semaphore = VK_NULL_HANDLE; } - - per_frame.device = VK_NULL_HANDLE; - per_frame.queue_index = -1; } /** @@ -474,7 +468,7 @@ void HelloTriangle::init_swapchain(Context &context) VkSurfaceFormatKHR format = vkb::select_surface_format(context.gpu, context.surface); - VkExtent2D swapchain_size; + VkExtent2D swapchain_size{}; if (surface_properties.currentExtent.width == 0xFFFFFFFF) { swapchain_size.width = context.swapchain_dimensions.width; @@ -880,7 +874,7 @@ void HelloTriangle::render_triangle(Context &context, uint32_t swapchain_index) vkBeginCommandBuffer(cmd, &begin_info); // Set clear color values. - VkClearValue clear_value; + VkClearValue clear_value{}; clear_value.color = {{0.01f, 0.01f, 0.033f, 1.0f}}; // Begin the render pass. diff --git a/samples/api/hello_triangle/hello_triangle.h b/samples/api/hello_triangle/hello_triangle.h index bef309cb6..09b078af4 100644 --- a/samples/api/hello_triangle/hello_triangle.h +++ b/samples/api/hello_triangle/hello_triangle.h @@ -47,19 +47,11 @@ class HelloTriangle : public vkb::Application */ struct PerFrame { - VkDevice device = VK_NULL_HANDLE; - - VkFence queue_submit_fence = VK_NULL_HANDLE; - - VkCommandPool primary_command_pool = VK_NULL_HANDLE; - - VkCommandBuffer primary_command_buffer = VK_NULL_HANDLE; - - VkSemaphore swapchain_acquire_semaphore = VK_NULL_HANDLE; - - VkSemaphore swapchain_release_semaphore = VK_NULL_HANDLE; - - int32_t queue_index; + VkFence queue_submit_fence = VK_NULL_HANDLE; + VkCommandPool primary_command_pool = VK_NULL_HANDLE; + VkCommandBuffer primary_command_buffer = VK_NULL_HANDLE; + VkSemaphore swapchain_acquire_semaphore = VK_NULL_HANDLE; + VkSemaphore swapchain_release_semaphore = VK_NULL_HANDLE; }; /** From b4e46874b0473470b9594d251487bf0ecd2fa239 Mon Sep 17 00:00:00 2001 From: Sascha Willems <webmaster@saschawillems.de> Date: Sat, 19 Oct 2024 19:09:31 +0200 Subject: [PATCH 05/11] Clean up instance and device creation code Use extension name constants instead of strings Remove unnecessary arguments --- samples/api/hello_triangle/hello_triangle.cpp | 44 +++++++++---------- samples/api/hello_triangle/hello_triangle.h | 7 +-- 2 files changed, 23 insertions(+), 28 deletions(-) diff --git a/samples/api/hello_triangle/hello_triangle.cpp b/samples/api/hello_triangle/hello_triangle.cpp index d9226c228..a50688f39 100644 --- a/samples/api/hello_triangle/hello_triangle.cpp +++ b/samples/api/hello_triangle/hello_triangle.cpp @@ -158,9 +158,7 @@ VkShaderStageFlagBits HelloTriangle::find_shader_stage(const std::string &ext) * @param required_instance_extensions The required Vulkan instance extensions. * @param required_validation_layers The required Vulkan validation layers */ -void HelloTriangle::init_instance(Context &context, - const std::vector<const char *> &required_instance_extensions, - const std::vector<const char *> &required_validation_layers) +void HelloTriangle::init_instance(Context &context) { LOGI("Initializing vulkan instance."); @@ -175,7 +173,7 @@ void HelloTriangle::init_instance(Context &context, std::vector<VkExtensionProperties> available_instance_extensions(instance_extension_count); VK_CHECK(vkEnumerateInstanceExtensionProperties(nullptr, &instance_extension_count, available_instance_extensions.data())); - std::vector<const char *> active_instance_extensions(required_instance_extensions); + std::vector<const char *> required_instance_extensions{VK_KHR_SURFACE_EXTENSION_NAME}; #if defined(VKB_DEBUG) || defined(VKB_VALIDATION_LAYERS) // Validation layers help finding wrong api usage, we enable them when explicitly requested or in debug builds @@ -186,7 +184,7 @@ void HelloTriangle::init_instance(Context &context, if (strcmp(ext.extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME) == 0) { has_debug_utils = true; - active_instance_extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + required_instance_extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); break; } } @@ -198,36 +196,36 @@ void HelloTriangle::init_instance(Context &context, #endif #if (defined(VKB_ENABLE_PORTABILITY)) - active_instance_extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); + required_instance_extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); bool portability_enumeration_available = false; if (std::any_of(available_instance_extensions.begin(), available_instance_extensions.end(), [](VkExtensionProperties extension) { return strcmp(extension.extensionName, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME) == 0; })) { - active_instance_extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); + required_instance_extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); portability_enumeration_available = true; } #endif #if defined(VK_USE_PLATFORM_ANDROID_KHR) - active_instance_extensions.push_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME); + required_instance_extensions.push_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME); #elif defined(VK_USE_PLATFORM_WIN32_KHR) - active_instance_extensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); + required_instance_extensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); #elif defined(VK_USE_PLATFORM_METAL_EXT) - active_instance_extensions.push_back(VK_EXT_METAL_SURFACE_EXTENSION_NAME); + required_instance_extensions.push_back(VK_EXT_METAL_SURFACE_EXTENSION_NAME); #elif defined(VK_USE_PLATFORM_XCB_KHR) - active_instance_extensions.push_back(VK_KHR_XCB_SURFACE_EXTENSION_NAME); + required_instance_extensions.push_back(VK_KHR_XCB_SURFACE_EXTENSION_NAME); #elif defined(VK_USE_PLATFORM_XLIB_KHR) - active_instance_extensions.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME); + required_instance_extensions.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME); #elif defined(VK_USE_PLATFORM_WAYLAND_KHR) - active_instance_extensions.push_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME); + required_instance_extensions.push_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME); #elif defined(VK_USE_PLATFORM_DISPLAY_KHR) - active_instance_extensions.push_back(VK_KHR_DISPLAY_EXTENSION_NAME); + required_instance_extensions.push_back(VK_KHR_DISPLAY_EXTENSION_NAME); #else # pragma error Platform not supported #endif - if (!validate_extensions(active_instance_extensions, available_instance_extensions)) + if (!validate_extensions(required_instance_extensions, available_instance_extensions)) { throw std::runtime_error("Required instance extensions are missing."); } @@ -238,7 +236,7 @@ void HelloTriangle::init_instance(Context &context, std::vector<VkLayerProperties> supported_validation_layers(instance_layer_count); VK_CHECK(vkEnumerateInstanceLayerProperties(&instance_layer_count, supported_validation_layers.data())); - std::vector<const char *> requested_validation_layers(required_validation_layers); + std::vector<const char *> requested_validation_layers{}; #if defined(VKB_DEBUG) || defined(VKB_VALIDATION_LAYERS) // Determine the optimal validation layers to enable that are necessary for useful debugging @@ -266,8 +264,8 @@ void HelloTriangle::init_instance(Context &context, VkInstanceCreateInfo instance_info{VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO}; instance_info.pApplicationInfo = &app; - instance_info.enabledExtensionCount = vkb::to_u32(active_instance_extensions.size()); - instance_info.ppEnabledExtensionNames = active_instance_extensions.data(); + instance_info.enabledExtensionCount = vkb::to_u32(required_instance_extensions.size()); + instance_info.ppEnabledExtensionNames = required_instance_extensions.data(); instance_info.enabledLayerCount = vkb::to_u32(requested_validation_layers.size()); instance_info.ppEnabledLayerNames = requested_validation_layers.data(); @@ -309,10 +307,8 @@ void HelloTriangle::init_instance(Context &context, * @brief Initializes the Vulkan physical device and logical device. * * @param context A Vulkan context with an instance already set up. - * @param required_device_extensions The required Vulkan device extensions. */ -void HelloTriangle::init_device(Context &context, - const std::vector<const char *> &required_device_extensions) +void HelloTriangle::init_device(Context &context) { LOGI("Initializing vulkan device."); @@ -366,6 +362,8 @@ void HelloTriangle::init_device(Context &context, std::vector<VkExtensionProperties> device_extensions(device_extension_count); VK_CHECK(vkEnumerateDeviceExtensionProperties(context.gpu, nullptr, &device_extension_count, device_extensions.data())); + // Since this sample has visual output, the device needs to support the swapchain extension + std::vector<const char *> required_device_extensions{VK_KHR_SWAPCHAIN_EXTENSION_NAME}; if (!validate_extensions(required_device_extensions, device_extensions)) { throw std::runtime_error("Required device extensions are missing."); @@ -1081,7 +1079,7 @@ bool HelloTriangle::prepare(const vkb::ApplicationOptions &options) { assert(options.window != nullptr); - init_instance(context, {VK_KHR_SURFACE_EXTENSION_NAME}, {}); + init_instance(context); vk_instance = std::make_unique<vkb::Instance>(context.instance); @@ -1095,7 +1093,7 @@ bool HelloTriangle::prepare(const vkb::ApplicationOptions &options) throw std::runtime_error("Failed to create window surface."); } - init_device(context, {"VK_KHR_swapchain"}); + init_device(context); init_swapchain(context); diff --git a/samples/api/hello_triangle/hello_triangle.h b/samples/api/hello_triangle/hello_triangle.h index 09b078af4..a33df2553 100644 --- a/samples/api/hello_triangle/hello_triangle.h +++ b/samples/api/hello_triangle/hello_triangle.h @@ -130,12 +130,9 @@ class HelloTriangle : public vkb::Application VkShaderStageFlagBits find_shader_stage(const std::string &ext); - void init_instance(Context &context, - const std::vector<const char *> &required_instance_extensions, - const std::vector<const char *> &required_validation_layers); + void init_instance(Context &context); - void init_device(Context &context, - const std::vector<const char *> &required_device_extensions); + void init_device(Context &context); void init_per_frame(Context &context, PerFrame &per_frame); From 68a037fdb23f824d22f99fac8a2d53d6351264bd Mon Sep 17 00:00:00 2001 From: Sascha Willems <webmaster@saschawillems.de> Date: Sat, 19 Oct 2024 19:13:45 +0200 Subject: [PATCH 06/11] Remove unnecessary passing of the context --- samples/api/hello_triangle/hello_triangle.cpp | 68 +++++++++---------- samples/api/hello_triangle/hello_triangle.h | 28 ++++---- 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/samples/api/hello_triangle/hello_triangle.cpp b/samples/api/hello_triangle/hello_triangle.cpp index a50688f39..a215fe539 100644 --- a/samples/api/hello_triangle/hello_triangle.cpp +++ b/samples/api/hello_triangle/hello_triangle.cpp @@ -158,7 +158,7 @@ VkShaderStageFlagBits HelloTriangle::find_shader_stage(const std::string &ext) * @param required_instance_extensions The required Vulkan instance extensions. * @param required_validation_layers The required Vulkan validation layers */ -void HelloTriangle::init_instance(Context &context) +void HelloTriangle::init_instance() { LOGI("Initializing vulkan instance."); @@ -308,7 +308,7 @@ void HelloTriangle::init_instance(Context &context) * * @param context A Vulkan context with an instance already set up. */ -void HelloTriangle::init_device(Context &context) +void HelloTriangle::init_device() { LOGI("Initializing vulkan device."); @@ -394,7 +394,7 @@ void HelloTriangle::init_device(Context &context) * @param context A newly created Vulkan context. * @param per_frame The data of a frame. */ -void HelloTriangle::init_per_frame(Context &context, PerFrame &per_frame) +void HelloTriangle::init_per_frame(PerFrame &per_frame) { VkFenceCreateInfo info{VK_STRUCTURE_TYPE_FENCE_CREATE_INFO}; info.flags = VK_FENCE_CREATE_SIGNALED_BIT; @@ -417,7 +417,7 @@ void HelloTriangle::init_per_frame(Context &context, PerFrame &per_frame) * @param context The Vulkan context. * @param per_frame The data of a frame. */ -void HelloTriangle::teardown_per_frame(Context &context, PerFrame &per_frame) +void HelloTriangle::teardown_per_frame(PerFrame &per_frame) { if (per_frame.queue_submit_fence != VK_NULL_HANDLE) { @@ -459,7 +459,7 @@ void HelloTriangle::teardown_per_frame(Context &context, PerFrame &per_frame) * @brief Initializes the Vulkan swapchain. * @param context A Vulkan context with a physical device already set up. */ -void HelloTriangle::init_swapchain(Context &context) +void HelloTriangle::init_swapchain() { VkSurfaceCapabilitiesKHR surface_properties; VK_CHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(context.gpu, context.surface, &surface_properties)); @@ -552,7 +552,7 @@ void HelloTriangle::init_swapchain(Context &context) for (size_t i = 0; i < image_count; i++) { - teardown_per_frame(context, context.per_frame[i]); + teardown_per_frame(context.per_frame[i]); } context.swapchain_image_views.clear(); @@ -577,7 +577,7 @@ void HelloTriangle::init_swapchain(Context &context) for (size_t i = 0; i < image_count; i++) { - init_per_frame(context, context.per_frame[i]); + init_per_frame(context.per_frame[i]); } for (size_t i = 0; i < image_count; i++) @@ -606,7 +606,7 @@ void HelloTriangle::init_swapchain(Context &context) * @brief Initializes the Vulkan render pass. * @param context A Vulkan context with a device already set up. */ -void HelloTriangle::init_render_pass(Context &context) +void HelloTriangle::init_render_pass() { VkAttachmentDescription attachment = {0}; // Backbuffer format. @@ -673,7 +673,7 @@ void HelloTriangle::init_render_pass(Context &context) * @param path The path for the shader (relative to the assets directory). * @returns A VkShaderModule handle. Aborts execution if shader creation fails. */ -VkShaderModule HelloTriangle::load_shader_module(Context &context, const char *path) +VkShaderModule HelloTriangle::load_shader_module(const char *path) { vkb::GLSLCompiler glsl_compiler; @@ -708,7 +708,7 @@ VkShaderModule HelloTriangle::load_shader_module(Context &context, const char *p * @brief Initializes the Vulkan pipeline. * @param context A Vulkan context with a device and a render pass already set up. */ -void HelloTriangle::init_pipeline(Context &context) +void HelloTriangle::init_pipeline() { // Create a blank pipeline layout. // We are not binding any resources to the pipeline in this first sample. @@ -760,13 +760,13 @@ void HelloTriangle::init_pipeline(Context &context) // Vertex stage of the pipeline shader_stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; shader_stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; - shader_stages[0].module = load_shader_module(context, "triangle.vert"); + shader_stages[0].module = load_shader_module("triangle.vert"); shader_stages[0].pName = "main"; // Fragment stage of the pipeline shader_stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; shader_stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; - shader_stages[1].module = load_shader_module(context, "triangle.frag"); + shader_stages[1].module = load_shader_module("triangle.frag"); shader_stages[1].pName = "main"; VkGraphicsPipelineCreateInfo pipe{VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO}; @@ -798,7 +798,7 @@ void HelloTriangle::init_pipeline(Context &context) * @param[out] image The swapchain index for the acquired image. * @returns Vulkan result code */ -VkResult HelloTriangle::acquire_next_image(Context &context, uint32_t *image) +VkResult HelloTriangle::acquire_next_image(uint32_t *image) { VkSemaphore acquire_semaphore; if (context.recycled_semaphores.empty()) @@ -857,7 +857,7 @@ VkResult HelloTriangle::acquire_next_image(Context &context, uint32_t *image) * @param context A Vulkan context set up for rendering. * @param swapchain_index The swapchain index for the image being rendered. */ -void HelloTriangle::render_triangle(Context &context, uint32_t swapchain_index) +void HelloTriangle::render_triangle(uint32_t swapchain_index) { // Render to this framebuffer. VkFramebuffer framebuffer = context.swapchain_framebuffers[swapchain_index]; @@ -940,7 +940,7 @@ void HelloTriangle::render_triangle(Context &context, uint32_t swapchain_index) * @param index The swapchain index previously obtained from @ref acquire_next_image. * @returns Vulkan result code */ -VkResult HelloTriangle::present_image(Context &context, uint32_t index) +VkResult HelloTriangle::present_image(uint32_t index) { VkPresentInfoKHR present{VK_STRUCTURE_TYPE_PRESENT_INFO_KHR}; present.swapchainCount = 1; @@ -956,7 +956,7 @@ VkResult HelloTriangle::present_image(Context &context, uint32_t index) * @brief Initializes the Vulkan framebuffers. * @param context A Vulkan context with the render pass already set up. */ -void HelloTriangle::init_framebuffers(Context &context) +void HelloTriangle::init_framebuffers() { VkDevice device = context.device; @@ -983,7 +983,7 @@ void HelloTriangle::init_framebuffers(Context &context) * @brief Tears down the framebuffers. If our swapchain changes, we will call this, and create a new swapchain. * @param context The Vulkan context. */ -void HelloTriangle::teardown_framebuffers(Context &context) +void HelloTriangle::teardown_framebuffers() { // Wait until device is idle before teardown. vkQueueWaitIdle(context.queue); @@ -1000,16 +1000,16 @@ void HelloTriangle::teardown_framebuffers(Context &context) * @brief Tears down the Vulkan context. * @param context The Vulkan context. */ -void HelloTriangle::teardown(Context &context) +void HelloTriangle::teardown() { // Don't release anything until the GPU is completely idle. vkDeviceWaitIdle(context.device); - teardown_framebuffers(context); + teardown_framebuffers(); for (auto &per_frame : context.per_frame) { - teardown_per_frame(context, per_frame); + teardown_per_frame(per_frame); } context.per_frame.clear(); @@ -1072,14 +1072,14 @@ HelloTriangle::HelloTriangle() HelloTriangle::~HelloTriangle() { - teardown(context); + teardown(); } bool HelloTriangle::prepare(const vkb::ApplicationOptions &options) { assert(options.window != nullptr); - init_instance(context); + init_instance(); vk_instance = std::make_unique<vkb::Instance>(context.instance); @@ -1093,14 +1093,14 @@ bool HelloTriangle::prepare(const vkb::ApplicationOptions &options) throw std::runtime_error("Failed to create window surface."); } - init_device(context); + init_device(); - init_swapchain(context); + init_swapchain(); // Create the necessary objects for rendering. - init_render_pass(context); - init_pipeline(context); - init_framebuffers(context); + init_render_pass(); + init_pipeline(); + init_framebuffers(); return true; } @@ -1109,13 +1109,13 @@ void HelloTriangle::update(float delta_time) { uint32_t index; - auto res = acquire_next_image(context, &index); + auto res = acquire_next_image(&index); // Handle outdated error in acquire. if (res == VK_SUBOPTIMAL_KHR || res == VK_ERROR_OUT_OF_DATE_KHR) { resize(context.swapchain_dimensions.width, context.swapchain_dimensions.height); - res = acquire_next_image(context, &index); + res = acquire_next_image(&index); } if (res != VK_SUCCESS) @@ -1124,8 +1124,8 @@ void HelloTriangle::update(float delta_time) return; } - render_triangle(context, index); - res = present_image(context, index); + render_triangle(index); + res = present_image(index); // Handle Outdated error in present. if (res == VK_SUBOPTIMAL_KHR || res == VK_ERROR_OUT_OF_DATE_KHR) @@ -1156,10 +1156,10 @@ bool HelloTriangle::resize(const uint32_t, const uint32_t) } vkDeviceWaitIdle(context.device); - teardown_framebuffers(context); + teardown_framebuffers(); - init_swapchain(context); - init_framebuffers(context); + init_swapchain(); + init_framebuffers(); return true; } diff --git a/samples/api/hello_triangle/hello_triangle.h b/samples/api/hello_triangle/hello_triangle.h index a33df2553..c35f92ad4 100644 --- a/samples/api/hello_triangle/hello_triangle.h +++ b/samples/api/hello_triangle/hello_triangle.h @@ -130,33 +130,33 @@ class HelloTriangle : public vkb::Application VkShaderStageFlagBits find_shader_stage(const std::string &ext); - void init_instance(Context &context); + void init_instance(); - void init_device(Context &context); + void init_device(); - void init_per_frame(Context &context, PerFrame &per_frame); + void init_per_frame(PerFrame &per_frame); - void teardown_per_frame(Context &context, PerFrame &per_frame); + void teardown_per_frame(PerFrame &per_frame); - void init_swapchain(Context &context); + void init_swapchain(); - void init_render_pass(Context &context); + void init_render_pass(); - VkShaderModule load_shader_module(Context &context, const char *path); + VkShaderModule load_shader_module(const char *path); - void init_pipeline(Context &context); + void init_pipeline(); - VkResult acquire_next_image(Context &context, uint32_t *image); + VkResult acquire_next_image(uint32_t *image); - void render_triangle(Context &context, uint32_t swapchain_index); + void render_triangle(uint32_t swapchain_index); - VkResult present_image(Context &context, uint32_t index); + VkResult present_image(uint32_t index); - void init_framebuffers(Context &context); + void init_framebuffers(); - void teardown_framebuffers(Context &context); + void teardown_framebuffers(); - void teardown(Context &context); + void teardown(); private: Context context; From 34d6b35358d99755a0ba2cb373e0d0310c9b8e66 Mon Sep 17 00:00:00 2001 From: Sascha Willems <webmaster@saschawillems.de> Date: Sat, 19 Oct 2024 19:19:07 +0200 Subject: [PATCH 07/11] Move resource destruction into the sample class destrutor The teardown function was only used for that anyway and was unnecessary --- samples/api/hello_triangle/hello_triangle.cpp | 26 +++++-------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/samples/api/hello_triangle/hello_triangle.cpp b/samples/api/hello_triangle/hello_triangle.cpp index a215fe539..c8ffc09e9 100644 --- a/samples/api/hello_triangle/hello_triangle.cpp +++ b/samples/api/hello_triangle/hello_triangle.cpp @@ -996,13 +996,14 @@ void HelloTriangle::teardown_framebuffers() context.swapchain_framebuffers.clear(); } -/** - * @brief Tears down the Vulkan context. - * @param context The Vulkan context. - */ -void HelloTriangle::teardown() +HelloTriangle::HelloTriangle() +{ +} + +HelloTriangle::~HelloTriangle() { - // Don't release anything until the GPU is completely idle. + // When destroying the application, we need to make sure the GPU is no longer accessing any resources + // This is done by doing a device wait idle, which blocks until the GPU signals vkDeviceWaitIdle(context.device); teardown_framebuffers(); @@ -1042,39 +1043,26 @@ void HelloTriangle::teardown() if (context.swapchain != VK_NULL_HANDLE) { vkDestroySwapchainKHR(context.device, context.swapchain, nullptr); - context.swapchain = VK_NULL_HANDLE; } if (context.surface != VK_NULL_HANDLE) { vkDestroySurfaceKHR(context.instance, context.surface, nullptr); - context.surface = VK_NULL_HANDLE; } if (context.device != VK_NULL_HANDLE) { vkDestroyDevice(context.device, nullptr); - context.device = VK_NULL_HANDLE; } if (context.debug_callback != VK_NULL_HANDLE) { vkDestroyDebugUtilsMessengerEXT(context.instance, context.debug_callback, nullptr); - context.debug_callback = VK_NULL_HANDLE; } vk_instance.reset(); } -HelloTriangle::HelloTriangle() -{ -} - -HelloTriangle::~HelloTriangle() -{ - teardown(); -} - bool HelloTriangle::prepare(const vkb::ApplicationOptions &options) { assert(options.window != nullptr); From 9c252f8aaae8b855d5981342c051b7f0389b4c6c Mon Sep 17 00:00:00 2001 From: Sascha Willems <webmaster@saschawillems.de> Date: Sat, 19 Oct 2024 19:22:25 +0200 Subject: [PATCH 08/11] Remove context parameter from function doc --- samples/api/hello_triangle/hello_triangle.cpp | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/samples/api/hello_triangle/hello_triangle.cpp b/samples/api/hello_triangle/hello_triangle.cpp index c8ffc09e9..d8e165983 100644 --- a/samples/api/hello_triangle/hello_triangle.cpp +++ b/samples/api/hello_triangle/hello_triangle.cpp @@ -154,7 +154,6 @@ VkShaderStageFlagBits HelloTriangle::find_shader_stage(const std::string &ext) /** * @brief Initializes the Vulkan instance. * - * @param context A newly created Vulkan context. * @param required_instance_extensions The required Vulkan instance extensions. * @param required_validation_layers The required Vulkan validation layers */ @@ -305,8 +304,6 @@ void HelloTriangle::init_instance() /** * @brief Initializes the Vulkan physical device and logical device. - * - * @param context A Vulkan context with an instance already set up. */ void HelloTriangle::init_device() { @@ -391,7 +388,6 @@ void HelloTriangle::init_device() /** * @brief Initializes per frame data. - * @param context A newly created Vulkan context. * @param per_frame The data of a frame. */ void HelloTriangle::init_per_frame(PerFrame &per_frame) @@ -414,7 +410,6 @@ void HelloTriangle::init_per_frame(PerFrame &per_frame) /** * @brief Tears down the frame data. - * @param context The Vulkan context. * @param per_frame The data of a frame. */ void HelloTriangle::teardown_per_frame(PerFrame &per_frame) @@ -457,7 +452,6 @@ void HelloTriangle::teardown_per_frame(PerFrame &per_frame) /** * @brief Initializes the Vulkan swapchain. - * @param context A Vulkan context with a physical device already set up. */ void HelloTriangle::init_swapchain() { @@ -604,7 +598,6 @@ void HelloTriangle::init_swapchain() /** * @brief Initializes the Vulkan render pass. - * @param context A Vulkan context with a device already set up. */ void HelloTriangle::init_render_pass() { @@ -669,7 +662,6 @@ void HelloTriangle::init_render_pass() /** * @brief Helper function to load a shader module. - * @param context A Vulkan context with a device. * @param path The path for the shader (relative to the assets directory). * @returns A VkShaderModule handle. Aborts execution if shader creation fails. */ @@ -706,7 +698,6 @@ VkShaderModule HelloTriangle::load_shader_module(const char *path) /** * @brief Initializes the Vulkan pipeline. - * @param context A Vulkan context with a device and a render pass already set up. */ void HelloTriangle::init_pipeline() { @@ -794,7 +785,6 @@ void HelloTriangle::init_pipeline() /** * @brief Acquires an image from the swapchain. - * @param context A Vulkan context with a swapchain already set up. * @param[out] image The swapchain index for the acquired image. * @returns Vulkan result code */ @@ -854,7 +844,6 @@ VkResult HelloTriangle::acquire_next_image(uint32_t *image) /** * @brief Renders a triangle to the specified swapchain image. - * @param context A Vulkan context set up for rendering. * @param swapchain_index The swapchain index for the image being rendered. */ void HelloTriangle::render_triangle(uint32_t swapchain_index) @@ -936,7 +925,6 @@ void HelloTriangle::render_triangle(uint32_t swapchain_index) /** * @brief Presents an image to the swapchain. - * @param context The Vulkan context, with a swapchain and per-frame resources already set up. * @param index The swapchain index previously obtained from @ref acquire_next_image. * @returns Vulkan result code */ @@ -954,7 +942,6 @@ VkResult HelloTriangle::present_image(uint32_t index) /** * @brief Initializes the Vulkan framebuffers. - * @param context A Vulkan context with the render pass already set up. */ void HelloTriangle::init_framebuffers() { @@ -981,7 +968,6 @@ void HelloTriangle::init_framebuffers() /** * @brief Tears down the framebuffers. If our swapchain changes, we will call this, and create a new swapchain. - * @param context The Vulkan context. */ void HelloTriangle::teardown_framebuffers() { From d37645a4348a9ece86d0a1f3f840a565ee6b181d Mon Sep 17 00:00:00 2001 From: Sascha Willems <webmaster@saschawillems.de> Date: Sat, 19 Oct 2024 19:25:52 +0200 Subject: [PATCH 09/11] Doc cleanup --- samples/api/hello_triangle/hello_triangle.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/samples/api/hello_triangle/hello_triangle.cpp b/samples/api/hello_triangle/hello_triangle.cpp index d8e165983..0990d489f 100644 --- a/samples/api/hello_triangle/hello_triangle.cpp +++ b/samples/api/hello_triangle/hello_triangle.cpp @@ -153,9 +153,6 @@ VkShaderStageFlagBits HelloTriangle::find_shader_stage(const std::string &ext) /** * @brief Initializes the Vulkan instance. - * - * @param required_instance_extensions The required Vulkan instance extensions. - * @param required_validation_layers The required Vulkan validation layers */ void HelloTriangle::init_instance() { From 67fcc7e31e9996c3142a11d014bd3de16f6e812e Mon Sep 17 00:00:00 2001 From: Sascha Willems <webmaster@saschawillems.de> Date: Sat, 19 Oct 2024 21:48:10 +0200 Subject: [PATCH 10/11] Remove unnecessary function for frame buffer teardown, also remove duplicate queue idle wait Add comments, throw if no device is found Males code easier to follow --- samples/api/hello_triangle/hello_triangle.cpp | 39 +++++++------------ samples/api/hello_triangle/hello_triangle.h | 4 -- 2 files changed, 15 insertions(+), 28 deletions(-) diff --git a/samples/api/hello_triangle/hello_triangle.cpp b/samples/api/hello_triangle/hello_triangle.cpp index 0990d489f..247fe77f5 100644 --- a/samples/api/hello_triangle/hello_triangle.cpp +++ b/samples/api/hello_triangle/hello_triangle.cpp @@ -314,6 +314,7 @@ void HelloTriangle::init_device() throw std::runtime_error("No physical device found."); } + // For simplicity, the sample selects the first gpu that has a graphics and present queue std::vector<VkPhysicalDevice> gpus(gpu_count); VK_CHECK(vkEnumeratePhysicalDevices(context.instance, &gpu_count, gpus.data())); @@ -348,7 +349,7 @@ void HelloTriangle::init_device() if (context.graphics_queue_index < 0) { - LOGE("Did not find suitable queue which supports graphics and presentation."); + throw std::runtime_error("Did not find suitable device with a queue that supports graphics and presentation."); } uint32_t device_extension_count; @@ -363,9 +364,8 @@ void HelloTriangle::init_device() throw std::runtime_error("Required device extensions are missing."); } - float queue_priority = 1.0f; - - // Create one queue + // The sample uses a single graphics queue + const float queue_priority = 1.0f; VkDeviceQueueCreateInfo queue_info{VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO}; queue_info.queueFamilyIndex = context.graphics_queue_index; queue_info.queueCount = 1; @@ -942,7 +942,7 @@ VkResult HelloTriangle::present_image(uint32_t index) */ void HelloTriangle::init_framebuffers() { - VkDevice device = context.device; + context.swapchain_framebuffers.clear(); // Create framebuffer for each swapchain image view for (auto &image_view : context.swapchain_image_views) @@ -957,28 +957,12 @@ void HelloTriangle::init_framebuffers() fb_info.layers = 1; VkFramebuffer framebuffer; - VK_CHECK(vkCreateFramebuffer(device, &fb_info, nullptr, &framebuffer)); + VK_CHECK(vkCreateFramebuffer(context.device, &fb_info, nullptr, &framebuffer)); context.swapchain_framebuffers.push_back(framebuffer); } } -/** - * @brief Tears down the framebuffers. If our swapchain changes, we will call this, and create a new swapchain. - */ -void HelloTriangle::teardown_framebuffers() -{ - // Wait until device is idle before teardown. - vkQueueWaitIdle(context.queue); - - for (auto &framebuffer : context.swapchain_framebuffers) - { - vkDestroyFramebuffer(context.device, framebuffer, nullptr); - } - - context.swapchain_framebuffers.clear(); -} - HelloTriangle::HelloTriangle() { } @@ -989,7 +973,10 @@ HelloTriangle::~HelloTriangle() // This is done by doing a device wait idle, which blocks until the GPU signals vkDeviceWaitIdle(context.device); - teardown_framebuffers(); + for (auto &framebuffer : context.swapchain_framebuffers) + { + vkDestroyFramebuffer(context.device, framebuffer, nullptr); + } for (auto &per_frame : context.per_frame) { @@ -1127,7 +1114,11 @@ bool HelloTriangle::resize(const uint32_t, const uint32_t) } vkDeviceWaitIdle(context.device); - teardown_framebuffers(); + + for (auto &framebuffer : context.swapchain_framebuffers) + { + vkDestroyFramebuffer(context.device, framebuffer, nullptr); + } init_swapchain(); init_framebuffers(); diff --git a/samples/api/hello_triangle/hello_triangle.h b/samples/api/hello_triangle/hello_triangle.h index c35f92ad4..5a23153a2 100644 --- a/samples/api/hello_triangle/hello_triangle.h +++ b/samples/api/hello_triangle/hello_triangle.h @@ -154,10 +154,6 @@ class HelloTriangle : public vkb::Application void init_framebuffers(); - void teardown_framebuffers(); - - void teardown(); - private: Context context; From dab29d1295714c64e5b9c90dde1a19c1e752d8d6 Mon Sep 17 00:00:00 2001 From: Sascha Willems <webmaster@saschawillems.de> Date: Sat, 19 Oct 2024 21:50:23 +0200 Subject: [PATCH 11/11] Making clang format happy --- samples/api/hello_triangle/hello_triangle.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/samples/api/hello_triangle/hello_triangle.cpp b/samples/api/hello_triangle/hello_triangle.cpp index 247fe77f5..b4b378907 100644 --- a/samples/api/hello_triangle/hello_triangle.cpp +++ b/samples/api/hello_triangle/hello_triangle.cpp @@ -366,6 +366,7 @@ void HelloTriangle::init_device() // The sample uses a single graphics queue const float queue_priority = 1.0f; + VkDeviceQueueCreateInfo queue_info{VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO}; queue_info.queueFamilyIndex = context.graphics_queue_index; queue_info.queueCount = 1;