From 2ead02037dc89e987fbc0a021fe470e29d226cfd Mon Sep 17 00:00:00 2001 From: Chuyan Zhang Date: Sat, 7 Sep 2024 00:40:38 -0700 Subject: Add more encapsulation, use VMA for allocation --- .gitmodules | 3 + CMakeLists.txt | 14 ++-- ext/VulkanMemoryAllocator | 1 + src/app.cpp | 32 ++++--- src/vulkan_helper.cpp | 91 +++++++++++++++++++- src/vulkan_helper.h | 45 +++++++++- src/vulkan_swapchain.cpp | 208 +++++++++++++++++++++++++--------------------- src/vulkan_swapchain.h | 7 +- 8 files changed, 281 insertions(+), 120 deletions(-) create mode 160000 ext/VulkanMemoryAllocator diff --git a/.gitmodules b/.gitmodules index 3c3f029..6ea0b14 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "ext/tinygltf.git"] path = ext/tinygltf.git url = https://github.com/syoyo/tinygltf.git +[submodule "ext/VulkanMemoryAllocator"] + path = ext/VulkanMemoryAllocator + url = https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator diff --git a/CMakeLists.txt b/CMakeLists.txt index 0cedde2..41401be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,22 +21,24 @@ target_include_directories(tinygltf INTERFACE ${EXT_DIR}/tinygltf) add_library(tinyobjloader INTERFACE) target_include_directories(tinyobjloader INTERFACE ${EXT_DIR}/tinyobjloader) -# Add your project's source files file(GLOB_RECURSE SOURCES "${SRC_DIR}/*.cpp") # Add executable from your source files add_executable(IrisRenderer ${SOURCES}) -# find_package(glfw3 REQUIRED) -# target_link_libraries(IrisRenderer PRIVATE glfw) +find_package(glfw3 REQUIRED) +target_link_libraries(IrisRenderer PRIVATE glfw) -# find_package(Vulkan REQUIRED) -# target_link_libraries(IrisRenderer PRIVATE Vulkan::Vulkan) +find_package(Vulkan REQUIRED) +target_link_libraries(IrisRenderer PRIVATE Vulkan::Vulkan) + +find_package(VulkanMemoryAllocator REQUIRED) +target_link_libraries(IrisRenderer PRIVATE GPUOpen::VulkanMemoryAllocator) # Link external libraries to your project target_link_libraries(IrisRenderer PRIVATE argparse imgui tinygltf tinyobjloader - glfw vulkan dl pthread X11 Xxf86vm Xrandr Xi) + dl pthread X11 Xxf86vm Xrandr Xi) # Optional: Include additional compiler options or flags target_compile_options(IrisRenderer PRIVATE -Wall -Wextra -Wno-missing-field-initializers) diff --git a/ext/VulkanMemoryAllocator b/ext/VulkanMemoryAllocator new file mode 160000 index 0000000..1c35ba9 --- /dev/null +++ b/ext/VulkanMemoryAllocator @@ -0,0 +1 @@ +Subproject commit 1c35ba99ce775f8342d87a83a3f0f696f99c2a39 diff --git a/src/app.cpp b/src/app.cpp index b3df2a5..a5b3247 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -1,9 +1,9 @@ #include "vulkan_swapchain.h" -#include "imgui.h" -#include "imgui_impl_glfw.h" #include "imgui_impl_vulkan.h" #include "argparse/argparse.hpp" +#include +#include #define GLFW_INCLUDE_VULKAN #include @@ -15,12 +15,6 @@ std::unique_ptr start_up(int width, int height) { auto glfw_extensions = get_glfw_instance_extensions(); - // std::vector glfw_extensions = { - // "VK_KHR_surface", - // }; - // for (const auto& extension : glfw_extensions) { - // std::cerr << "GLFW extension: " << extension << std::endl; - // } iris::Device device({}, glfw_extensions); auto window = glfwCreateWindow(width, height, "IrisRenderer", nullptr, nullptr); @@ -31,10 +25,6 @@ std::unique_ptr start_up(int width, int height) { return std::make_unique(window, device); } -void main_loop(GLFWwindow *window) { - (void) window; -} - void shut_down(std::unique_ptr& swapchain) { ImGui_ImplVulkan_Shutdown(); @@ -77,7 +67,23 @@ int main(int argc, char** argv) { auto swapchain = start_up(window_width, window_height); while (!glfwWindowShouldClose(swapchain->window)) { - main_loop(swapchain->window); + glfwPollEvents(); + + int display_w, display_h; + glfwGetFramebufferSize(swapchain->window, &display_w, &display_h); + if (display_w == 0 || display_h == 0) { + break; + } + + swapchain->needs_recreate |= + (uint32_t) display_w != swapchain->width || + (uint32_t) display_h != swapchain->height; + if (swapchain->needs_recreate) { + swapchain->resize(display_w, display_h); + swapchain->needs_recreate = false; + } + + ImGui_ImplVulkan_NewFrame(); } shut_down(swapchain); diff --git a/src/vulkan_helper.cpp b/src/vulkan_helper.cpp index 84a2b6d..e533d2d 100644 --- a/src/vulkan_helper.cpp +++ b/src/vulkan_helper.cpp @@ -3,17 +3,19 @@ #include #include #include +#include +#include namespace iris { -Device::Device(std::vector layers, std::vector instance_extensions) { +Device::Device(std::vector layers, std::vector instance_extensions) { VkApplicationInfo app_info = { .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .pApplicationName = "IrisRenderer", .applicationVersion = VK_MAKE_VERSION(1, 0, 0), .pEngineName = "No Engine", .engineVersion = VK_MAKE_VERSION(1, 0, 0), - .apiVersion = VK_API_VERSION_1_0, + .apiVersion = VK_API_VERSION_1_3, }; // Create the Vulkan instance @@ -140,6 +142,91 @@ Device::Device(std::vector layers, std::vector instan main_queue_family_index, 0, &graphics_queue); + + // Create the memory allocator + VmaAllocatorCreateInfo allocator_info = { + .physicalDevice = physical_device, + .device = device, + .instance = instance, + }; + CHECK_VULKAN(vmaCreateAllocator( + &allocator_info, + &allocator)); +} + +Device::~Device() { + vmaDestroyAllocator(allocator); + vkDestroyDevice(device, VK_NULL_HANDLE); + vkDestroyInstance(instance, VK_NULL_HANDLE); +} + + +Buffer_t::~Buffer_t() { + vmaDestroyBuffer(allocator, buffer, allocation); +} + +Buffer Device::create_buffer(VkDeviceSize size, + VkBufferUsageFlags usage, + VmaMemoryUsage memory_usage) { + VkBufferCreateInfo buffer_info = { + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .size = size, + .usage = usage, + }; + VmaAllocationCreateInfo allocation_info = { + .usage = memory_usage, + }; + Buffer_t buffer = { + .allocator = this->allocator, + .flags = usage, + .size = size, + }; + CHECK_VULKAN(vmaCreateBuffer( + allocator, + &buffer_info, + &allocation_info, + &buffer.buffer, + &buffer.allocation, + VK_NULL_HANDLE)); + return std::make_shared(buffer); +} + +Texture2D_t::~Texture2D_t() { + vmaDestroyImage(allocator, image, allocation); + // TODO: optionally destroy image view, if created +} + +Texture2D Device::create_texture(VkExtent2D extent, + VkFormat format, + VkImageUsageFlags usage, + VmaMemoryUsage memory_usage) { + VkImageCreateInfo image_info = { + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .imageType = VK_IMAGE_TYPE_2D, + .format = format, + .extent = {extent.width, extent.height, 1}, + .mipLevels = 1, + .arrayLayers = 1, + .samples = VK_SAMPLE_COUNT_1_BIT, + .tiling = VK_IMAGE_TILING_OPTIMAL, + .usage = usage, + }; + VmaAllocationCreateInfo allocation_info = { + .usage = memory_usage, + }; + Texture2D_t texture = { + .allocator = this->allocator, + .flags = usage, + .extent = extent, + }; + CHECK_VULKAN(vmaCreateImage( + allocator, + &image_info, + &allocation_info, + &texture.image, + &texture.allocation, + VK_NULL_HANDLE)); + return std::make_shared(texture); } } // namespace iris \ No newline at end of file diff --git a/src/vulkan_helper.h b/src/vulkan_helper.h index 089a09f..b6f270a 100644 --- a/src/vulkan_helper.h +++ b/src/vulkan_helper.h @@ -1,5 +1,8 @@ -#include +#include #include +#include + +#include #include #include @@ -15,16 +18,56 @@ namespace iris { +struct Buffer_t { + VkBuffer buffer; + VmaAllocator allocator; + VmaAllocation allocation; + + VkBufferUsageFlags flags; + VkDeviceSize size; + + ~Buffer_t(); +}; + +typedef std::shared_ptr Buffer; + +struct Texture2D_t { + VkImage image; + VmaAllocator allocator; + VmaAllocation allocation; + VkImageView image_view; + + VkImageUsageFlags flags; + VkExtent2D extent; + + ~Texture2D_t(); +}; + +typedef std::shared_ptr Texture2D; + struct Device { VkInstance instance; VkPhysicalDevice physical_device; VkDevice device; uint32_t main_queue_family_index; VkQueue graphics_queue; + VmaAllocator allocator; Device( std::vector layers, std::vector instance_extensions); + ~Device(); + + Buffer create_buffer( + VkDeviceSize size, + VkBufferUsageFlags usage, + VmaMemoryUsage memory_usage = VMA_MEMORY_USAGE_AUTO); + + Texture2D create_texture( + VkExtent2D extent, + VkFormat format, + VkImageUsageFlags usage, + VmaMemoryUsage memory_usage = VMA_MEMORY_USAGE_AUTO); }; } // namespace iris \ No newline at end of file diff --git a/src/vulkan_swapchain.cpp b/src/vulkan_swapchain.cpp index 651b558..fde4388 100644 --- a/src/vulkan_swapchain.cpp +++ b/src/vulkan_swapchain.cpp @@ -10,6 +10,7 @@ #include #include #include +#include std::vector get_glfw_instance_extensions() { uint32_t extension_count = 0; @@ -25,6 +26,116 @@ std::vector get_glfw_instance_extensions() { namespace iris { +void Swapchain::resize(uint32_t new_width, uint32_t new_height) { + width = new_width; + height = new_height; + + // wait for the device to finish + CHECK_VULKAN(vkDeviceWaitIdle(device.device)); + + // destroy old resources + if (swapchain != VK_NULL_HANDLE) { + for (uint32_t i = 0; i < SWAPCHAIN_IMAGE_COUNT; i++) { + vkDestroyFramebuffer(device.device, framebuffers[i], nullptr); + vkDestroyImageView(device.device, swapchain_image_views[i], nullptr); + } + vkDestroySwapchainKHR(device.device, swapchain, nullptr); + } + + VkSwapchainCreateInfoKHR swapchain_create_info = { + .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, + .surface = surface, + .minImageCount = SWAPCHAIN_IMAGE_COUNT, + .imageFormat = VK_FORMAT_B8G8R8A8_UNORM, + .imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, + .imageExtent = {width, height}, + .imageArrayLayers = 1, + .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, + .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, + .preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR, + .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, + .presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR, + .clipped = VK_TRUE, + .oldSwapchain = VK_NULL_HANDLE, + }; + CHECK_VULKAN(vkCreateSwapchainKHR( + device.device, + &swapchain_create_info, + nullptr, + &swapchain)); + + // images + uint32_t image_count = 0; + CHECK_VULKAN(vkGetSwapchainImagesKHR( + device.device, + swapchain, + &image_count, + nullptr)); + if (image_count > SWAPCHAIN_IMAGE_COUNT) { + // TODO throw an exception + std::cerr << "Swapchain image count is greater than expected" << std::endl; + abort(); + } + CHECK_VULKAN(vkGetSwapchainImagesKHR( + device.device, + swapchain, + &image_count, + swapchain_images)); + + // image views + for (uint32_t i = 0; i < image_count; i++) { + VkImageViewCreateInfo image_view_create_info = { + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .image = swapchain_images[i], + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = VK_FORMAT_B8G8R8A8_UNORM, + .components = { + .r = VK_COMPONENT_SWIZZLE_IDENTITY, + .g = VK_COMPONENT_SWIZZLE_IDENTITY, + .b = VK_COMPONENT_SWIZZLE_IDENTITY, + .a = VK_COMPONENT_SWIZZLE_IDENTITY, + }, + .subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + }; + + CHECK_VULKAN(vkCreateImageView( + device.device, + &image_view_create_info, + nullptr, + &swapchain_image_views[i])); + } + + // framebuffers + for (uint32_t i = 0; i < image_count; i++) { + VkFramebufferCreateInfo frame_buffer_create_info = { + .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + .renderPass = render_pass, + .attachmentCount = 1, + .pAttachments = &swapchain_image_views[i], + .width = width, + .height = height, + .layers = 1, + }; + CHECK_VULKAN(vkCreateFramebuffer( + device.device, + &frame_buffer_create_info, + nullptr, + &framebuffers[i])); + } + + upload_texture = device.create_texture( + {width, height}, + VK_FORMAT_B8G8R8A8_UNORM, + VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, + VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE); +} + Swapchain::Swapchain( GLFWwindow *window, iris::Device device) : device(device), window(window) @@ -129,102 +240,7 @@ Swapchain::Swapchain( &render_pass)); // Create swapchain and image/imageview/framebuffers - // TODO: move this into `resize` to support resizing - { - // swapchain - int i_width, i_height; - glfwGetFramebufferSize(window, &i_width, &i_height); - width = (uint32_t) i_width; - height = (uint32_t) i_height; - - VkSwapchainCreateInfoKHR swapchain_create_info = { - .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, - .surface = surface, - .minImageCount = SWAPCHAIN_IMAGE_COUNT, - .imageFormat = VK_FORMAT_B8G8R8A8_UNORM, - .imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, - .imageExtent = {width, height}, - .imageArrayLayers = 1, - .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, - .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, - .preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR, - .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, - .presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR, - .clipped = VK_TRUE, - .oldSwapchain = VK_NULL_HANDLE, - }; - CHECK_VULKAN(vkCreateSwapchainKHR( - device.device, - &swapchain_create_info, - nullptr, - &swapchain)); - - // images - uint32_t image_count = 0; - CHECK_VULKAN(vkGetSwapchainImagesKHR( - device.device, - swapchain, - &image_count, - nullptr)); - if (image_count > SWAPCHAIN_IMAGE_COUNT) { - // TODO throw an exception - std::cerr << "Swapchain image count is greater than expected" << std::endl; - abort(); - } - CHECK_VULKAN(vkGetSwapchainImagesKHR( - device.device, - swapchain, - &image_count, - swapchain_images)); - - // image views - for (uint32_t i = 0; i < image_count; i++) { - VkImageViewCreateInfo image_view_create_info = { - .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, - .image = swapchain_images[i], - .viewType = VK_IMAGE_VIEW_TYPE_2D, - .format = VK_FORMAT_B8G8R8A8_UNORM, - .components = { - .r = VK_COMPONENT_SWIZZLE_IDENTITY, - .g = VK_COMPONENT_SWIZZLE_IDENTITY, - .b = VK_COMPONENT_SWIZZLE_IDENTITY, - .a = VK_COMPONENT_SWIZZLE_IDENTITY, - }, - .subresourceRange = { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1, - }, - }; - - CHECK_VULKAN(vkCreateImageView( - device.device, - &image_view_create_info, - nullptr, - &swapchain_image_views[i])); - } - - // framebuffers - for (uint32_t i = 0; i < image_count; i++) { - VkFramebufferCreateInfo frame_buffer_create_info = { - .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, - .renderPass = render_pass, - .attachmentCount = 1, - .pAttachments = &swapchain_image_views[i], - .width = width, - .height = height, - .layers = 1, - }; - CHECK_VULKAN(vkCreateFramebuffer( - device.device, - &frame_buffer_create_info, - nullptr, - &framebuffers[i])); - } - } - + this->resize(width, height); // Create descriptor pool VkDescriptorPoolSize pool_size = { diff --git a/src/vulkan_swapchain.h b/src/vulkan_swapchain.h index 99405b6..509753f 100644 --- a/src/vulkan_swapchain.h +++ b/src/vulkan_swapchain.h @@ -1,5 +1,4 @@ #include "imgui_impl_glfw.h" -#include "imgui_impl_vulkan.h" #include "vulkan_helper.h" #include "vulkan/vulkan_core.h" #include @@ -12,7 +11,7 @@ std::vector get_glfw_instance_extensions(); namespace iris { struct Swapchain { - iris::Device device; + Device device; VkSurfaceKHR surface = VK_NULL_HANDLE; VkSwapchainKHR swapchain = VK_NULL_HANDLE; @@ -21,12 +20,16 @@ struct Swapchain { GLFWwindow *window = nullptr; uint32_t width = -1; uint32_t height = -1; + bool needs_recreate = false; static constexpr uint32_t SWAPCHAIN_IMAGE_COUNT = 3; VkImage swapchain_images[SWAPCHAIN_IMAGE_COUNT]; VkImageView swapchain_image_views[SWAPCHAIN_IMAGE_COUNT]; VkFramebuffer framebuffers[SWAPCHAIN_IMAGE_COUNT]; + Texture2D upload_texture; + + void resize(uint32_t new_width, uint32_t new_height); Swapchain(GLFWwindow *window, iris::Device device); ~Swapchain(); }; -- cgit v1.2.3-70-g09d2