From 40717cf8e41a827c3e9d6ba7c6f59f1449400c1d Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Tue, 25 Nov 2025 13:45:08 -0500 Subject: [PATCH] Updated to the next chapter of vkguide https://vkguide.dev/docs/new_chapter_2/vulkan_shader_drawing/ --- main.cpp | 9 +++-- meson.build | 9 ++++- vk_engine.cpp | 89 +++++++++++++++++++++++++++++++++++++++++---- vk_engine.h | 29 ++++++++++++++- vk_images.cpp | 41 +++++++++++++++++++++ vk_images.h | 2 + vk_initializers.cpp | 37 +++++++++++++++++++ vk_initializers.h | 4 ++ vk_types.h | 10 +++++ 9 files changed, 216 insertions(+), 14 deletions(-) diff --git a/main.cpp b/main.cpp index f958ea2..1e56674 100644 --- a/main.cpp +++ b/main.cpp @@ -1,14 +1,15 @@ #include +#include int main() { - VulkanEngine engine; + auto engine = std::make_unique(); - engine.init(); + engine->init(); - engine.run(); + engine->run(); - engine.cleanup(); + engine->cleanup(); return 0; } diff --git a/meson.build b/meson.build index b75c944..c602331 100644 --- a/meson.build +++ b/meson.build @@ -10,7 +10,14 @@ project('hellovulk', 'cpp', cmake = import('cmake') # use this for common options only for our executables -cpp_args=['-Wno-sign-conversion'] +cpp_args=[ + '-Wno-sign-conversion', + '-Wno-unused-parameter', + '-Wno-unused-function', + '-Wno-unused-variable', + '-Wno-conversion', + '-Wno-missing-field-initializers' + ] link_args=[] # these are passed as override_defaults exe_defaults = [ 'warning_level=2' ] diff --git a/vk_engine.cpp b/vk_engine.cpp index cca6502..821ecb4 100644 --- a/vk_engine.cpp +++ b/vk_engine.cpp @@ -12,6 +12,9 @@ #include #include +#define VMA_IMPLEMENTATION +#include "vk_mem_alloc.h" + constexpr bool bUseValidationLayers = false; VulkanEngine* loadedEngine = nullptr; @@ -59,8 +62,12 @@ void VulkanEngine::cleanup() vkDestroyFence(_device, _frames[i]._renderFence, nullptr); vkDestroySemaphore(_device, _frames[i]._renderSemaphore, nullptr); vkDestroySemaphore(_device ,_frames[i]._swapchainSemaphore, nullptr); + + _frames[i]._deletionQueue.flush(); } + _mainDeletionQueue.flush(); + destroy_swapchain(); vkDestroySurfaceKHR(_instance, _surface, nullptr); @@ -77,6 +84,8 @@ void VulkanEngine::draw() { // wait until the gpu has finished rendering the last frame. Timeout of 1 second VK_CHECK(vkWaitForFences(_device, 1, &get_current_frame()._renderFence, true, 1000000000)); + get_current_frame()._deletionQueue.flush(); + VK_CHECK(vkResetFences(_device, 1, &get_current_frame()._renderFence)); uint32_t swapchainImageIndex = 0; @@ -87,19 +96,28 @@ void VulkanEngine::draw() VkCommandBufferBeginInfo cmdBeginInfo = vkinit::command_buffer_begin_info(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); + _drawExtent.width = _drawImage.imageExtent.width; + _drawExtent.height = _drawImage.imageExtent.height; + VK_CHECK(vkBeginCommandBuffer(cmd, &cmdBeginInfo)); - vkutil::transition_image(cmd, _swapchainImages[swapchainImageIndex], VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL); + // transition our main draw image into general layout so we can write into it + // we will overwrite it all so we dont care about what was the older layout - VkClearColorValue clearValue; - float flash = std::abs(std::sin(float(_frameNumber) / 120.0f)); - clearValue = { { 0.0f, 0.0f, flash, 1.0f} }; + vkutil::transition_image(cmd, _drawImage.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL); - VkImageSubresourceRange clearRange = vkinit::image_subresource_range(VK_IMAGE_ASPECT_COLOR_BIT); + draw_background(cmd); - vkCmdClearColorImage(cmd, _swapchainImages[swapchainImageIndex], VK_IMAGE_LAYOUT_GENERAL, &clearValue, 1, &clearRange); + vkutil::transition_image(cmd, _drawImage.image, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + + vkutil::transition_image(cmd, _swapchainImages[swapchainImageIndex], VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + + vkutil::copy_image_to_image(cmd, _drawImage.image, + _swapchainImages[swapchainImageIndex], + _drawExtent, _swapchainExtent); + + vkutil::transition_image(cmd, _swapchainImages[swapchainImageIndex], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); - vkutil::transition_image(cmd, _swapchainImages[swapchainImageIndex], VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); VK_CHECK(vkEndCommandBuffer(cmd)); //prepare the submission to the queue. @@ -226,6 +244,18 @@ void VulkanEngine::init_vulkan() { _graphicsQueue = vkbDevice.get_queue(vkb::QueueType::graphics).value(); _graphicsQueueFamily = vkbDevice.get_queue_index(vkb::QueueType::graphics).value(); + + // initialize the memory allocator + VmaAllocatorCreateInfo allocatorInfo{}; + allocatorInfo.physicalDevice = _chosenGPU; + allocatorInfo.device = _device; + allocatorInfo.instance = _instance; + allocatorInfo.flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT; + vmaCreateAllocator(&allocatorInfo, &_allocator); + + _mainDeletionQueue.push_function([&]() { + vmaDestroyAllocator(_allocator); + }); } void VulkanEngine::create_swapchain(uint32_t width, uint32_t height) { @@ -261,6 +291,41 @@ void VulkanEngine::destroy_swapchain() { void VulkanEngine::init_swapchain() { create_swapchain(_windowExtent.width, _windowExtent.height); + + VkExtent3D drawImageExtent = { + _windowExtent.width, + _windowExtent.height, + 1 + }; + + _drawImage.imageFormat = VK_FORMAT_R16G16B16A16_SFLOAT; + _drawImage.imageExtent = drawImageExtent; + + VkImageUsageFlags drawImageUsages{}; + drawImageUsages |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + drawImageUsages |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; + drawImageUsages |= VK_IMAGE_USAGE_STORAGE_BIT; + drawImageUsages |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + + VkImageCreateInfo rimg_info = vkinit::image_create_info(_drawImage.imageFormat, drawImageUsages, drawImageExtent); + + VmaAllocationCreateInfo rimg_allocinfo{}; + rimg_allocinfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; + rimg_allocinfo.requiredFlags = VkMemoryPropertyFlags(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + + //allocate and create the image + vmaCreateImage(_allocator, &rimg_info, &rimg_allocinfo, &_drawImage.image, &_drawImage.allocation, nullptr); + + //build a image-view for the draw image to use for rendering + VkImageViewCreateInfo rview_info = vkinit::imageview_create_info(_drawImage.imageFormat, _drawImage.image, VK_IMAGE_ASPECT_COLOR_BIT); + + VK_CHECK(vkCreateImageView(_device, &rview_info, nullptr, &_drawImage.imageView)); + + //add to deletion queues + _mainDeletionQueue.push_function([=, this]() { + vkDestroyImageView(_device, _drawImage.imageView, nullptr); + vmaDestroyImage(_allocator, _drawImage.image, _drawImage.allocation); + }); } void VulkanEngine::init_commands() { @@ -291,3 +356,13 @@ void VulkanEngine::init_sync_structures() { } } +void VulkanEngine::draw_background(VkCommandBuffer cmd) +{ + VkClearColorValue clearValue; + float flash = std::abs(std::sin(float(_frameNumber) / 120.0f)); + clearValue = { { 0.0f, 0.0f, flash, 1.0f} }; + + VkImageSubresourceRange clearRange = vkinit::image_subresource_range(VK_IMAGE_ASPECT_COLOR_BIT); + + vkCmdClearColorImage(cmd, _drawImage.image, VK_IMAGE_LAYOUT_GENERAL, &clearValue, 1, &clearRange); +} diff --git a/vk_engine.h b/vk_engine.h index 3f02ddf..c9b120e 100644 --- a/vk_engine.h +++ b/vk_engine.h @@ -5,12 +5,30 @@ #include +struct DeletionQueue { + std::deque> deletors; + + void push_function(std::function&& function) { + deletors.push_back(function); + } + + void flush() { + // reverse itererate the deletion queue + for(auto it = deletors.rbegin(); it != deletors.rend(); it++) { + (*it)(); + } + + deletors.clear(); + } +}; + struct FrameData { VkCommandPool _commandPool; VkCommandBuffer _mainCommandBuffer; VkSemaphore _swapchainSemaphore; VkSemaphore _renderSemaphore; VkFence _renderFence; + DeletionQueue _deletionQueue; }; constexpr unsigned int FRAME_OVERLAP=2; @@ -32,6 +50,9 @@ public: std::vector _swapchainImageViews; VkExtent2D _swapchainExtent; + // VMA stuff + VmaAllocator _allocator; + // frames/command buffer unsigned int _frameNumber = 0; FrameData _frames[FRAME_OVERLAP]; @@ -43,12 +64,15 @@ public: VkQueue _graphicsQueue; uint32_t _graphicsQueueFamily; + // draw resources + AllocatedImage _drawImage; + VkExtent2D _drawExtent; + // internal data bool _isInitialized{ false }; - VkExtent2D _windowExtent{ 1700 , 900 }; - struct SDL_Window* _window{ nullptr }; + DeletionQueue _mainDeletionQueue; static VulkanEngine& Get(); @@ -72,4 +96,5 @@ private: void create_swapchain(uint32_t width, uint32_t height); void destroy_swapchain(); + void draw_background(VkCommandBuffer cmd); }; diff --git a/vk_images.cpp b/vk_images.cpp index f23eae7..a457d61 100644 --- a/vk_images.cpp +++ b/vk_images.cpp @@ -28,3 +28,44 @@ void vkutil::transition_image(VkCommandBuffer cmd, VkImage image, VkImageLayout vkCmdPipelineBarrier2(cmd, &depInfo); } + +void vkutil::copy_image_to_image(VkCommandBuffer cmd, VkImage source, VkImage destination, VkExtent2D srcSize, VkExtent2D dstSize) +{ + VkImageBlit2 blitRegion{ + .sType = VK_STRUCTURE_TYPE_IMAGE_BLIT_2, + .pNext = nullptr + }; + + blitRegion.srcOffsets[1].x = srcSize.width; + blitRegion.srcOffsets[1].y = srcSize.height; + blitRegion.srcOffsets[1].z = 1; + + blitRegion.dstOffsets[1].x = dstSize.width; + blitRegion.dstOffsets[1].y = dstSize.height; + blitRegion.dstOffsets[1].z = 1; + + blitRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + blitRegion.srcSubresource.baseArrayLayer = 0; + blitRegion.srcSubresource.layerCount = 1; + blitRegion.srcSubresource.mipLevel = 0; + + blitRegion.dstSubresource.aspectMask=VK_IMAGE_ASPECT_COLOR_BIT; + blitRegion.dstSubresource.baseArrayLayer=0; + blitRegion.dstSubresource.layerCount=1; + blitRegion.dstSubresource.mipLevel=0; + + VkBlitImageInfo2 blitInfo{ + .sType = VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2, .pNext = nullptr + }; + + blitInfo.dstImage = destination; + blitInfo.dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + blitInfo.srcImage = source; + blitInfo.srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + blitInfo.filter = VK_FILTER_LINEAR; + blitInfo.regionCount = 1; + blitInfo.pRegions = &blitRegion; + + vkCmdBlitImage2(cmd, &blitInfo); +} + diff --git a/vk_images.h b/vk_images.h index a1de765..8c5a53e 100644 --- a/vk_images.h +++ b/vk_images.h @@ -4,4 +4,6 @@ namespace vkutil { void transition_image(VkCommandBuffer cmd, VkImage image, VkImageLayout currentLayout, VkImageLayout newLayout); + + void copy_image_to_image(VkCommandBuffer cmd, VkImage source, VkImage destination, VkExtent2D srcSize, VkExtent2D dstSize); } diff --git a/vk_initializers.cpp b/vk_initializers.cpp index bfa9806..8c30ace 100644 --- a/vk_initializers.cpp +++ b/vk_initializers.cpp @@ -108,3 +108,40 @@ VkSubmitInfo2 vkinit::submit_info(VkCommandBufferSubmitInfo* cmd, VkSemaphoreSub return info; } + +VkImageCreateInfo vkinit::image_create_info(VkFormat format, VkImageUsageFlags usageFlags, VkExtent3D extent) +{ + VkImageCreateInfo info{}; + info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + + info.format = format; + info.extent = extent; + + info.mipLevels = 1; + info.arrayLayers = 1; + + info.samples = VK_SAMPLE_COUNT_1_BIT; + + info.tiling = VK_IMAGE_TILING_OPTIMAL; + info.usage = usageFlags; + + return info; +} + +VkImageViewCreateInfo vkinit::imageview_create_info(VkFormat format, VkImage image, VkImageAspectFlags aspectFlags) +{ + VkImageViewCreateInfo info{}; + info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + info.pNext = nullptr; + + info.viewType = VK_IMAGE_VIEW_TYPE_2D; + info.image = image; + info.format = format; + info.subresourceRange.baseMipLevel = 0; + info.subresourceRange.levelCount = 1; + info.subresourceRange.baseArrayLayer = 0; + info.subresourceRange.layerCount = 1; + info.subresourceRange.aspectMask = aspectFlags; + + return info; +} diff --git a/vk_initializers.h b/vk_initializers.h index bf36032..3fd01ed 100644 --- a/vk_initializers.h +++ b/vk_initializers.h @@ -26,5 +26,9 @@ namespace vkinit { VkCommandBufferSubmitInfo command_buffer_submit_info(VkCommandBuffer cmd); VkSubmitInfo2 submit_info(VkCommandBufferSubmitInfo* cmd, VkSemaphoreSubmitInfo* signalSemaphoreInfo, VkSemaphoreSubmitInfo* waitSemaphoreInfo); + + VkImageViewCreateInfo imageview_create_info(VkFormat format, VkImage image, VkImageAspectFlags aspectFlags); + + VkImageCreateInfo image_create_info(VkFormat format, VkImageUsageFlags usageFlags, VkExtent3D extent); } diff --git a/vk_types.h b/vk_types.h index d4dfc65..0098606 100644 --- a/vk_types.h +++ b/vk_types.h @@ -29,3 +29,13 @@ std::println("Detected Vulkan error: {}", int(err));\ }\ } while (0) + + +struct AllocatedImage { + VkImage image; + VkImageView imageView; + VmaAllocation allocation; + VkExtent3D imageExtent; + VkFormat imageFormat; +}; +