From 6185c081c1a6ec13b54eab6a12ff72814cf3addb Mon Sep 17 00:00:00 2001 From: Chuyan Zhang Date: Tue, 1 Oct 2024 17:08:41 -0700 Subject: Fix vulkan validation error --- .gitignore | 7 ++- CMakeLists.txt | 10 ++-- src/app.cpp | 10 ++-- src/vulkan_helper.cpp | 122 +++++++++++++++++++++++++---------------------- src/vulkan_helper.h | 26 ++++------ src/vulkan_swapchain.cpp | 44 +++++++++-------- src/vulkan_swapchain.h | 2 +- 7 files changed, 121 insertions(+), 100 deletions(-) diff --git a/.gitignore b/.gitignore index 01b5580..352a5fd 100644 --- a/.gitignore +++ b/.gitignore @@ -41,4 +41,9 @@ build/ # clangd config -.clangd \ No newline at end of file +.clangd + +# imgui config file +imgui.ini + +e.log diff --git a/CMakeLists.txt b/CMakeLists.txt index b2dc213..afe7626 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,9 +8,6 @@ set(CMAKE_CXX_STANDARD 20) set(PROJECT_ROOT ${CMAKE_SOURCE_DIR}) set(SRC_DIR ${PROJECT_ROOT}/src) set(EXT_DIR ${PROJECT_ROOT}/ext) -if (CMAKE_BUILD_TYPE STREQUAL "Debug") - add_compile_definitions(USE_VULKAN_VALIDATION_LAYERS) -endif() # Add external libraries include(${EXT_DIR}/imgui.cmake) @@ -32,6 +29,10 @@ file(GLOB_RECURSE SOURCES "${SRC_DIR}/*.cpp") # Add executable from your source files add_executable(iris_renderer ${SOURCES}) +if (CMAKE_BUILD_TYPE STREQUAL "Debug") + add_compile_definitions(USE_VULKAN_VALIDATION_LAYERS) +endif() + find_package(glfw3 REQUIRED) target_link_libraries(iris_renderer PRIVATE glfw) @@ -41,6 +42,9 @@ target_link_libraries(iris_renderer PRIVATE Vulkan::Vulkan) find_package(VulkanMemoryAllocator CONFIG REQUIRED) target_link_libraries(iris_renderer PRIVATE GPUOpen::VulkanMemoryAllocator) +find_package(spdlog REQUIRED) +target_link_libraries(iris_renderer PRIVATE spdlog::spdlog) + # Link external libraries to your project target_link_libraries(iris_renderer PRIVATE argparse imgui tinygltf tinyobjloader stb diff --git a/src/app.cpp b/src/app.cpp index f48edd9..11a219d 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -4,6 +4,8 @@ #include "imgui_impl_vulkan.h" #include "argparse/argparse.hpp" #include +#include +#include #include #include #include @@ -16,6 +18,7 @@ #include int main(int argc, char** argv) { + spdlog::cfg::load_env_levels(); argparse::ArgumentParser program("Iris Renderer"); program.add_argument("width") .help("display width of the window") @@ -39,9 +42,9 @@ int main(int argc, char** argv) { const char* description = nullptr; glfwGetError(&description); if (description != nullptr) { - std::cerr << "Error: " << description << std::endl; + spdlog::critical("Failed to initialize GLFW: {}", description); } else { - std::cerr << "Failed to initialize GLFW" << std::endl; + spdlog::critical("Failed to initialize GLFW"); } glfwTerminate(); @@ -54,7 +57,7 @@ int main(int argc, char** argv) { glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); auto window = glfwCreateWindow(window_width, window_height, "Iris Renderer", nullptr, nullptr); if (window == nullptr) { - std::cerr << "Failed to create GLFW window" << std::endl; + spdlog::critical("Failed to create GLFW window"); abort(); } @@ -103,6 +106,7 @@ int main(int argc, char** argv) { glfwDestroyWindow(swapchain.window); glfwTerminate(); + swapchain.release(); device.destroy(); return 0; diff --git a/src/vulkan_helper.cpp b/src/vulkan_helper.cpp index 69617c4..f39d867 100644 --- a/src/vulkan_helper.cpp +++ b/src/vulkan_helper.cpp @@ -1,12 +1,12 @@ #include "vulkan_helper.h" #include "vulkan/vulkan_core.h" #include +#include #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" #include #include #include -#include #include #define VMA_IMPLEMENTATION #include @@ -23,7 +23,7 @@ static VKAPI_ATTR VkBool32 VKAPI_CALL debug_report( void* pUserData) { (void)flags; (void)object; (void)location; (void)messageCode; (void)pUserData; (void)pLayerPrefix; // Unused arguments - fprintf(stderr, "[vulkan] Debug report from ObjectType: %i\nMessage: %s\n\n", objectType, pMessage); + spdlog::debug("[vulkan] Debug report from ObjectType: {}\nMessage: {}\n\n", (uint32_t)objectType, pMessage); return VK_FALSE; } @@ -36,7 +36,7 @@ VKAPI_ATTR VkBool32 VKAPI_CALL debug_callback( (void) messageSeverity; (void) messageType; (void) pUserData; - std::cerr << "Validation layer: " << pCallbackData->pMessage << std::endl; + spdlog::error("{}", pCallbackData->pMessage); return VK_FALSE; } #endif @@ -151,8 +151,7 @@ Device::Device(std::vector layers, std::vector instanc } if (!has_raytracing || !has_acceleration_structure) { - // TODO throw an exception - std::cerr << "Physical device does not support ray tracing extensions" << std::endl; + spdlog::critical("Physical device does not support ray tracing extensions"); abort(); } } @@ -225,6 +224,19 @@ Device::Device(std::vector layers, std::vector instanc // not handled by RAII, manually call at the end. void Device::destroy() { +#ifdef USE_VULKAN_VALIDATION_LAYERS + auto vkDestroyDebugReportCallback = (PFN_vkDestroyDebugReportCallbackEXT) + vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT"); + if (vkDestroyDebugReportCallback) { + vkDestroyDebugReportCallback(instance, debugReportCallback, nullptr); + } + auto vkDestroyDebugUtilsMessenger = (PFN_vkDestroyDebugUtilsMessengerEXT) + vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT"); + if (vkDestroyDebugUtilsMessenger) { + vkDestroyDebugUtilsMessenger(instance, debugUtilsMessenger, nullptr); + } +#endif + vmaDestroyAllocator(allocator); vkDestroyDevice(device, VK_NULL_HANDLE); vkDestroyInstance(instance, VK_NULL_HANDLE); @@ -265,6 +277,7 @@ CommandBuffer::CommandBuffer(VkDevice device, &fence_info, VK_NULL_HANDLE, &fence)); + spdlog::debug("Created command buffer: 0x{:x}", (uint64_t)buffer); } AsyncCommandBuffer::AsyncCommandBuffer(VkDevice device, @@ -293,11 +306,23 @@ AsyncCommandBuffer::AsyncCommandBuffer(VkDevice device, device, &buffer_info, &buffer)); + + VkFenceCreateInfo fence_info = { + .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, + }; + CHECK_VULKAN(vkCreateFence( + device, + &fence_info, + VK_NULL_HANDLE, + &fence)); + spdlog::debug("Created async command buffer: 0x{:x}", (uint64_t)buffer); } void AsyncCommandBuffer::destroy() { + vkDestroyFence(device, fence, VK_NULL_HANDLE); vkFreeCommandBuffers(device, pool, 1, &buffer); vkDestroyCommandPool(device, pool, VK_NULL_HANDLE); + spdlog::debug("Destroyed async command buffer: 0x{:x}", (uint64_t)buffer); } @@ -305,6 +330,7 @@ void CommandBuffer::destroy() { vkDestroyFence(device, fence, VK_NULL_HANDLE); vkFreeCommandBuffers(device, pool, 1, &buffer); vkDestroyCommandPool(device, pool, VK_NULL_HANDLE); + spdlog::debug("Destroyed command buffer: 0x{:x}", (uint64_t)buffer); } CommandBuffer Device::create_command_buffer() { @@ -366,55 +392,51 @@ void Buffer_t::unmap() { void Buffer_t::release() { if (buffer != VK_NULL_HANDLE) { - std::cerr << "Destroying buffer: 0x" << std::hex << (uint64_t)buffer << std::endl; + spdlog::debug("Destroying buffer: 0x{:x}", (uint64_t)buffer); + vkDeviceWaitIdle(device); vmaDestroyBuffer(allocator, buffer, allocation); buffer = VK_NULL_HANDLE; } } -Buffer_t Device::create_buffer_raw(VkDeviceSize size, - VkBufferUsageFlags usage, - VmaAllocationCreateInfo create_info) +Buffer Device::create_buffer(VkDeviceSize size, + VkBufferUsageFlags usage, + VmaAllocationCreateInfo create_info) { VkBufferCreateInfo buffer_info = { .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .size = size, .usage = usage, }; - Buffer_t buffer = { - .allocator = this->allocator, - .flags = usage, - .size = size, - }; + Buffer buffer = std::make_shared(); + buffer->device = this->device; + buffer->allocator = this->allocator; + buffer->flags = usage; + buffer->size = size; CHECK_VULKAN(vmaCreateBuffer( allocator, &buffer_info, &create_info, - &buffer.buffer, - &buffer.allocation, + &buffer->buffer, + &buffer->allocation, VK_NULL_HANDLE)); + spdlog::debug("Created buffer: 0x{:x}", (uint64_t)buffer->buffer); return buffer; } -Buffer Device::create_buffer(VkDeviceSize size, - VkBufferUsageFlags usage, - VmaAllocationCreateInfo create_info) -{ - return std::make_shared(create_buffer_raw(size, usage, create_info)); -} - void Texture2D_t::release() { if (image != VK_NULL_HANDLE) { - std::cerr << "Destroying image: 0x" << std::hex << (uint64_t)image << std::endl; + spdlog::debug("Destroying image: 0x{:x}", (uint64_t)image); + vkDeviceWaitIdle(device); vmaDestroyImage(allocator, image, allocation); image = VK_NULL_HANDLE; } } -Texture2D_t Device::create_texture_raw(VkExtent2D extent, - VkFormat format, - VkImageUsageFlags usage, - VmaAllocationCreateInfo create_info) +Texture2D Device::create_texture(VkExtent2D extent, + VkFormat format, + VkImageUsageFlags usage, + VmaAllocationCreateInfo create_info) { VkImageCreateInfo image_info = { .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, @@ -427,33 +449,25 @@ Texture2D_t Device::create_texture_raw(VkExtent2D extent, .tiling = VK_IMAGE_TILING_OPTIMAL, .usage = usage, }; - Texture2D_t texture = { - .allocator = this->allocator, - .layout = VK_IMAGE_LAYOUT_UNDEFINED, - .flags = usage, - .extent = extent, - }; + + Texture2D texture = std::make_shared(); + texture->device = this->device; + texture->allocator = this->allocator; + texture->layout = VK_IMAGE_LAYOUT_UNDEFINED; + texture->flags = usage; + texture->extent = extent; + CHECK_VULKAN(vmaCreateImage( allocator, &image_info, &create_info, - &texture.image, - &texture.allocation, + &texture->image, + &texture->allocation, VK_NULL_HANDLE)); - std::cerr << "Created image: 0x" << std::hex << (uint64_t)texture.image << std::endl; + spdlog::debug("Created image: 0x{:x}", (uint64_t)texture->image); return texture; } -Texture2D Device::create_texture(VkExtent2D extent, - VkFormat format, - VkImageUsageFlags usage, - VmaAllocationCreateInfo create_info) -{ - auto t = std::make_shared(create_texture_raw(extent, format, usage, create_info)); - std::cerr << "Before create texture terminates" << std::endl; - return t; -} - Texture2D Device::create_texture_from_image(const char* filename, VkFormat format, VkImageUsageFlags usage, @@ -461,13 +475,12 @@ Texture2D Device::create_texture_from_image(const char* filename, int width, height, channels; stbi_uc* pixels = stbi_load(filename, &width, &height, &channels, STBI_rgb_alpha); if (pixels == nullptr) { - // TODO throw an exception - std::cerr << "Failed to load image: " << filename << std::endl; + spdlog::critical("Failed to load image: {}", filename); abort(); } // destroy after use, don't need to wrap with shared_ptr - auto staging_buf = create_buffer_raw( + auto staging_buf = create_buffer( width * height * 4, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VmaAllocationCreateInfo { @@ -476,10 +489,10 @@ Texture2D Device::create_texture_from_image(const char* filename, .usage = VMA_MEMORY_USAGE_AUTO, }); std::memcpy( - staging_buf.map(), + staging_buf->map(), pixels, width * height * 4); - staging_buf.unmap(); + staging_buf->unmap(); stbi_image_free(pixels); VkExtent2D extent = {static_cast(width), static_cast(height)}; @@ -493,7 +506,6 @@ Texture2D Device::create_texture_from_image(const char* filename, .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, }; CHECK_VULKAN(vkBeginCommandBuffer(cmd_buf.buffer, &begin_info)); - std::cerr << "Image layout transition for 0x" << std::hex << (uint64_t)texture->image << std::endl; VkImageMemoryBarrier barrier = { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .srcAccessMask = VK_ACCESS_NONE, @@ -550,7 +562,7 @@ Texture2D Device::create_texture_from_image(const char* filename, }; vkCmdCopyBufferToImage( cmd_buf.buffer, - staging_buf.buffer, + staging_buf->buffer, texture->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, @@ -606,8 +618,6 @@ Texture2D Device::create_texture_from_image(const char* filename, CHECK_VULKAN(vkWaitForFences(cmd_buf.device, 1, &cmd_buf.fence, VK_TRUE, std::numeric_limits::max())); } texture->layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - std::cerr << "Image layout transition done" << std::endl; - staging_buf.release(); return texture; } diff --git a/src/vulkan_helper.h b/src/vulkan_helper.h index c2b1c8c..d8c9c32 100644 --- a/src/vulkan_helper.h +++ b/src/vulkan_helper.h @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -11,8 +12,7 @@ do { \ VkResult res = result; \ if (res != VK_SUCCESS) { \ - /* TODO: throw error instead of returning */ \ - std::cerr << "Vulkan error: " << string_VkResult(res) << std::endl; \ + spdlog::error("Vulkan error: {}", string_VkResult(res)); \ abort(); \ } \ } while (0) @@ -21,6 +21,7 @@ namespace iris { struct Buffer_t { VkBuffer buffer; + VkDevice device; VmaAllocator allocator; VmaAllocation allocation; VkBufferUsageFlags flags; @@ -30,12 +31,14 @@ struct Buffer_t { void* map(); void unmap(); void release(); + ~Buffer_t() { release(); } }; typedef std::shared_ptr Buffer; struct Texture2D_t { VkImage image; + VkDevice device; VmaAllocator allocator; VmaAllocation allocation; VkImageView image_view; @@ -45,6 +48,7 @@ struct Texture2D_t { VkExtent2D extent; void release(); + ~Texture2D_t() { release(); } }; typedef std::shared_ptr Texture2D; @@ -62,16 +66,19 @@ struct CommandBuffer { void destroy(); void begin(VkCommandBufferUsageFlags flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); void submit_sync(); + ~CommandBuffer() { destroy(); } }; struct AsyncCommandBuffer { VkDevice device; VkCommandPool pool; VkCommandBuffer buffer; + VkFence fence; VkQueue queue; AsyncCommandBuffer(VkDevice device, uint32_t queue_family_index, VkQueue queue); void destroy(); + ~AsyncCommandBuffer() { destroy(); } }; struct Device { @@ -92,21 +99,6 @@ struct Device { std::vector instance_extensions); void destroy(); - Buffer_t create_buffer_raw( - VkDeviceSize size, - VkBufferUsageFlags usage, - VmaAllocationCreateInfo create_info = { - .usage = VMA_MEMORY_USAGE_AUTO, - }); - - Texture2D_t create_texture_raw( - VkExtent2D extent, - VkFormat format, - VkImageUsageFlags usage, - VmaAllocationCreateInfo create_info = { - .usage = VMA_MEMORY_USAGE_AUTO, - }); - Buffer create_buffer( VkDeviceSize size, VkBufferUsageFlags usage, diff --git a/src/vulkan_swapchain.cpp b/src/vulkan_swapchain.cpp index 7916a3b..21948c1 100644 --- a/src/vulkan_swapchain.cpp +++ b/src/vulkan_swapchain.cpp @@ -8,8 +8,8 @@ #include #include +#include #include -#include #include #include @@ -77,8 +77,7 @@ void Swapchain::resize(uint32_t new_width, uint32_t new_height) { &image_count, nullptr)); if (image_count > SWAPCHAIN_IMAGE_COUNT) { - // TODO throw an exception - std::cerr << "Swapchain image count is greater than expected" << std::endl; + spdlog::critical("Swapchain image count is greater than expected"); abort(); } CHECK_VULKAN(vkGetSwapchainImagesKHR( @@ -134,6 +133,9 @@ void Swapchain::resize(uint32_t new_width, uint32_t new_height) { &framebuffers[i])); } + if (upload_texture) { + upload_texture->release(); + } upload_texture = device.create_texture( {width, height}, VK_FORMAT_B8G8R8A8_UNORM, @@ -155,8 +157,7 @@ Swapchain::Swapchain(GLFWwindow *window, , cmd_buf(device.create_async_command_buffer()) { if (!glfwVulkanSupported()) { - std::cerr << "GLFW failed to find Vulkan support" << std::endl; - // TODO throw an exception + spdlog::critical("GLFW failed to find Vulkan support"); abort(); } @@ -176,8 +177,7 @@ Swapchain::Swapchain(GLFWwindow *window, surface, &supported)); if (supported != VK_TRUE) { - // TODO throw an exception - std::cerr << "Surface does not support presentation" << std::endl; + spdlog::critical("Surface does not support presentation"); abort(); } @@ -188,8 +188,7 @@ Swapchain::Swapchain(GLFWwindow *window, &surface_capabilities)); if (surface_capabilities.maxImageCount < SWAPCHAIN_IMAGE_COUNT) { - // TODO throw an exception - std::cerr << "Surface does not support enough images" << std::endl; + spdlog::critical("Surface does not support enough images"); abort(); } } @@ -280,6 +279,7 @@ Swapchain::Swapchain(GLFWwindow *window, }; VkDescriptorPoolCreateInfo pool_info = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, .maxSets = 1, .poolSizeCount = 1, .pPoolSizes = &pool_size, @@ -317,11 +317,17 @@ Swapchain::Swapchain(GLFWwindow *window, } void Swapchain::start_frame() { + spdlog::trace("Starting frame {}", frame_index); VkCommandBufferBeginInfo begin_info = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, }; + + if (vkGetFenceStatus(device.device, cmd_buf.fence) == VK_NOT_READY) { + vkWaitForFences(device.device, 1, &cmd_buf.fence, VK_TRUE, 2e6); + } CHECK_VULKAN(vkBeginCommandBuffer(cmd_buf.buffer, &begin_info)); + vkResetFences(device.device, 1, &cmd_buf.fence); } void Swapchain::display(Texture2D& texture) { @@ -331,7 +337,7 @@ void Swapchain::display(Texture2D& texture) { UINT64_MAX, image_available_semaphores[semaphore_index], VK_NULL_HANDLE, - &frame_index); + &semaphore_index); switch (present_result) { case VK_ERROR_OUT_OF_DATE_KHR: case VK_SUBOPTIMAL_KHR: @@ -355,7 +361,7 @@ void Swapchain::display(Texture2D& texture) { .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = swapchain_images[frame_index], + .image = swapchain_images[semaphore_index], .subresourceRange = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .levelCount = 1, @@ -417,7 +423,7 @@ void Swapchain::display(Texture2D& texture) { cmd_buf.buffer, texture->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - swapchain_images[frame_index], + swapchain_images[semaphore_index], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, @@ -456,7 +462,7 @@ void Swapchain::display(Texture2D& texture) { VkRenderPassBeginInfo render_pass_begin_info = { .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, .renderPass = render_pass, - .framebuffer = framebuffers[frame_index], + .framebuffer = framebuffers[semaphore_index], .renderArea = { .offset = {0, 0}, .extent = {width, height}, @@ -488,7 +494,7 @@ void Swapchain::display(Texture2D& texture) { cmd_buf.queue, 1, &submit_info, - VK_NULL_HANDLE)); + cmd_buf.fence)); } // Present the image to the swapchain @@ -498,7 +504,7 @@ void Swapchain::display(Texture2D& texture) { .pWaitSemaphores = &render_finished_semaphores[semaphore_index], .swapchainCount = 1, .pSwapchains = &swapchain, - .pImageIndices = &frame_index, + .pImageIndices = &semaphore_index, }; present_result = vkQueuePresentKHR(device.graphics_queue, &present_info); @@ -514,10 +520,12 @@ void Swapchain::display(Texture2D& texture) { // Rotate the semaphore index semaphore_index = (semaphore_index + 1) % SWAPCHAIN_IMAGE_COUNT; + frame_index += 1; } -Swapchain::~Swapchain() { - ImGui_ImplVulkan_Shutdown(); +void Swapchain::release() { + upload_texture->release(); + cmd_buf.destroy(); for (uint32_t i = 0; i < SWAPCHAIN_IMAGE_COUNT; i++) { vkDestroyFramebuffer(device.device, framebuffers[i], nullptr); @@ -530,8 +538,6 @@ Swapchain::~Swapchain() { vkDestroySwapchainKHR(device.device, swapchain, nullptr); vkDestroyRenderPass(device.device, render_pass, nullptr); vkDestroySurfaceKHR(device.instance, surface, nullptr); - - upload_texture.reset(); } } // namespace iris \ No newline at end of file diff --git a/src/vulkan_swapchain.h b/src/vulkan_swapchain.h index 257840a..1cd270c 100644 --- a/src/vulkan_swapchain.h +++ b/src/vulkan_swapchain.h @@ -39,8 +39,8 @@ struct Swapchain { void resize(uint32_t new_width, uint32_t new_height); void start_frame(); void display(Texture2D &texture); + void release(); Swapchain(GLFWwindow *window, iris::Device device, uint32_t width, uint32_t height); - ~Swapchain(); }; } // namespace iris \ No newline at end of file -- cgit v1.2.3-70-g09d2