From f9b77b8d58aa8f7df0d05b0b12cec592eabb7b75 Mon Sep 17 00:00:00 2001 From: Karn Kaul Date: Fri, 28 Mar 2025 17:41:41 -0700 Subject: [PATCH 01/11] VMA --- ext/CMakeLists.txt | 24 ++++++++++++++ ext/src.zip | Bin 4304736 -> 4494751 bytes ext/vk_mem_alloc.cpp | 3 ++ guide/src/SUMMARY.md | 5 +++ guide/src/memory/README.md | 5 +++ guide/src/memory/vma.md | 65 +++++++++++++++++++++++++++++++++++++ src/app.cpp | 4 +++ src/app.hpp | 5 ++- src/vma.cpp | 29 +++++++++++++++++ src/vma.hpp | 16 +++++++++ 10 files changed, 155 insertions(+), 1 deletion(-) create mode 100644 ext/vk_mem_alloc.cpp create mode 100644 guide/src/memory/README.md create mode 100644 guide/src/memory/vma.md create mode 100644 src/vma.cpp create mode 100644 src/vma.hpp diff --git a/ext/CMakeLists.txt b/ext/CMakeLists.txt index 94c6e10..a4204b1 100644 --- a/ext/CMakeLists.txt +++ b/ext/CMakeLists.txt @@ -27,6 +27,10 @@ target_compile_definitions(glm PUBLIC message(STATUS "[Vulkan-Headers]") add_subdirectory(src/Vulkan-Headers) +# add VulkanMemoryAllocator to build tree +message(STATUS "[VulkanMemoryAllocator]") +add_subdirectory(src/VulkanMemoryAllocator) + # setup Dear ImGui library message(STATUS "[Dear ImGui]") add_library(imgui) @@ -55,6 +59,25 @@ target_sources(imgui PRIVATE src/imgui/backends/imgui_impl_vulkan.h ) +# setup vma library (source file with VMA interface) +message(STATUS "[vma]") +add_library(vma) +add_library(vma::vma ALIAS vma) +target_link_libraries(vma PUBLIC + Vulkan::Headers + GPUOpen::VulkanMemoryAllocator +) +target_compile_definitions(vma PUBLIC + VMA_STATIC_VULKAN_FUNCTIONS=0 + VMA_DYNAMIC_VULKAN_FUNCTIONS=1 +) +target_sources(vma PRIVATE + vk_mem_alloc.cpp +) + +# ignore compiler warnings +target_compile_options(vma PRIVATE -w) + # declare ext library target add_library(${PROJECT_NAME} INTERFACE) add_library(learn-vk::ext ALIAS ${PROJECT_NAME}) @@ -63,6 +86,7 @@ add_library(learn-vk::ext ALIAS ${PROJECT_NAME}) target_link_libraries(${PROJECT_NAME} INTERFACE glm::glm imgui::imgui + vma::vma ) # setup preprocessor defines diff --git a/ext/src.zip b/ext/src.zip index 032ad24b364fffb0fd72184530ffa3b60b94010d..a308392e08b43e1f84676794f736c1f78f0ea70b 100644 GIT binary patch delta 191634 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�K!OI*z?1R7`-SYA| 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 delta 175 zcmWN^Cl0~@06)*Ufg7Nz30FWPe!ydp8~{tgDezSUN5U!SbMJP)h@baK zJR%?>p~rv`6J{(}v0=x76Blkgc=6#UK#&k&B1DN1Cqa@FX)sZpmv zlNN0{bm`G&z>pC#Vi#Z!qdKKj|lZWkI()thD_B D0}?rm diff --git a/ext/vk_mem_alloc.cpp b/ext/vk_mem_alloc.cpp new file mode 100644 index 0000000..3fd28ba --- /dev/null +++ b/ext/vk_mem_alloc.cpp @@ -0,0 +1,3 @@ +#define VMA_IMPLEMENTATION + +#include diff --git a/guide/src/SUMMARY.md b/guide/src/SUMMARY.md index 6c9363b..bdbb798 100644 --- a/guide/src/SUMMARY.md +++ b/guide/src/SUMMARY.md @@ -33,3 +33,8 @@ - [GLSL to SPIR-V](shader_objects/glsl_to_spir_v.md) - [Drawing a Triangle](shader_objects/drawing_triangle.md) - [Graphics Pipelines](shader_objects/pipelines.md) + +# Shader Resources + +- [Memory Allocation](memory/README.md) + - [Vulkan Memory Allocator](memory/vma.md) diff --git a/guide/src/memory/README.md b/guide/src/memory/README.md new file mode 100644 index 0000000..b58383a --- /dev/null +++ b/guide/src/memory/README.md @@ -0,0 +1,5 @@ +# Memory Allocation + +Being an explicit API, [allocating memory](https://docs.vulkan.org/guide/latest/memory_allocation.html) in Vulkan that can be used by the device is the application's responsibility. The specifics can get quite complicated, but as recommended by the spec, we shall simply defer all that to a library: [Vulkan Memory Allocator (VMA)](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator). + +Vulkan exposes two kinds of objects that use such allocated memory: Buffers and Images, VMA offers transparent support for both: we just have to allocate/free buffers and images through VMA instead of the device directly. Unlike memory allocation / object construction on the CPU, there are many more parameters (than say alignment and size) to provide for the creation of buffers and images. As you might have guessed, we shall constrain ourselves to a subset that's relevant for shader resources: vertex buffers, uniform/storage buffers, and texture images. diff --git a/guide/src/memory/vma.md b/guide/src/memory/vma.md new file mode 100644 index 0000000..8226d70 --- /dev/null +++ b/guide/src/memory/vma.md @@ -0,0 +1,65 @@ +# Vulkan Memory Allocator + +VMA has full CMake support, but it is also a single-header library that requires users to "instantiate" it in a single translation unit. Isolating that into a wrapper library to minimize warning pollution etc, we create our own `vma::vma` target that compiles this source file: + +```cpp +// vk_mem_alloc.cpp +#define VMA_IMPLEMENTATION + +#include +``` + +Unlike VulkanHPP, VMA's interface is C only, thus we shall use our `Scoped` class template to wrap objects in RAII types. The first thing we need is a `VmaAllocator`, which is similar to a `vk::Device` or `GLFWwindow*`: + +```cpp +// vma.hpp +namespace lvk::vma { +struct Deleter { + void operator()(VmaAllocator allocator) const noexcept; +}; + +using Allocator = Scoped; + +[[nodiscard]] auto create_allocator(vk::Instance instance, + vk::PhysicalDevice physical_device, + vk::Device device) -> Allocator; +} // namespace lvk::vma + +// vma.cpp +void vma::Deleter::operator()(VmaAllocator allocator) const noexcept { + vmaDestroyAllocator(allocator); +} + +auto vma::create_allocator(vk::Instance const instance, + vk::PhysicalDevice const physical_device, + vk::Device const device) -> Allocator { + auto const& dispatcher = VULKAN_HPP_DEFAULT_DISPATCHER; + // need to zero initialize C structs, unlike VulkanHPP. + auto vma_vk_funcs = VmaVulkanFunctions{}; + vma_vk_funcs.vkGetInstanceProcAddr = dispatcher.vkGetInstanceProcAddr; + vma_vk_funcs.vkGetDeviceProcAddr = dispatcher.vkGetDeviceProcAddr; + + auto allocator_ci = VmaAllocatorCreateInfo{}; + allocator_ci.physicalDevice = physical_device; + allocator_ci.device = device; + allocator_ci.pVulkanFunctions = &vma_vk_funcs; + allocator_ci.instance = instance; + VmaAllocator ret{}; + auto const result = vmaCreateAllocator(&allocator_ci, &ret); + if (result == VK_SUCCESS) { return ret; } + + throw std::runtime_error{"Failed to create Vulkan Memory Allocator"}; +} +``` + +`App` stores and creates a `vma::Allocator` object: + +```cpp +// ... +vma::Allocator m_allocator{}; // anywhere between m_device and m_shader. + +// ... +void App::create_allocator() { + m_allocator = vma::create_allocator(*m_instance, m_gpu.device, *m_device); +} +``` diff --git a/src/app.cpp b/src/app.cpp index a9e63e5..a0f3b5f 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -228,6 +228,10 @@ void App::create_imgui() { m_imgui.emplace(imgui_ci); } +void App::create_allocator() { + m_allocator = vma::create_allocator(*m_instance, m_gpu.device, *m_device); +} + void App::create_shader() { auto const vertex_spirv = to_spir_v(asset_path("shader.vert")); auto const fragment_spirv = to_spir_v(asset_path("shader.frag")); diff --git a/src/app.hpp b/src/app.hpp index 7f5d47a..6532cbc 100644 --- a/src/app.hpp +++ b/src/app.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -35,6 +36,7 @@ class App { void create_swapchain(); void create_render_sync(); void create_imgui(); + void create_allocator(); void create_shader(); [[nodiscard]] auto asset_path(std::string_view uri) const -> fs::path; @@ -61,7 +63,8 @@ class App { vk::UniqueSurfaceKHR m_surface{}; Gpu m_gpu{}; // not an RAII member. vk::UniqueDevice m_device{}; - vk::Queue m_queue{}; // not an RAII member. + vk::Queue m_queue{}; // not an RAII member. + vma::Allocator m_allocator{}; // anywhere between m_device and m_shader. std::optional m_swapchain{}; // command pool for all render Command Buffers. diff --git a/src/vma.cpp b/src/vma.cpp new file mode 100644 index 0000000..36a67de --- /dev/null +++ b/src/vma.cpp @@ -0,0 +1,29 @@ +#include +#include + +namespace lvk { +void vma::Deleter::operator()(VmaAllocator allocator) const noexcept { + vmaDestroyAllocator(allocator); +} + +auto vma::create_allocator(vk::Instance const instance, + vk::PhysicalDevice const physical_device, + vk::Device const device) -> Allocator { + auto const& dispatcher = VULKAN_HPP_DEFAULT_DISPATCHER; + // need to zero initialize C structs, unlike VulkanHPP. + auto vma_vk_funcs = VmaVulkanFunctions{}; + vma_vk_funcs.vkGetInstanceProcAddr = dispatcher.vkGetInstanceProcAddr; + vma_vk_funcs.vkGetDeviceProcAddr = dispatcher.vkGetDeviceProcAddr; + + auto allocator_ci = VmaAllocatorCreateInfo{}; + allocator_ci.physicalDevice = physical_device; + allocator_ci.device = device; + allocator_ci.pVulkanFunctions = &vma_vk_funcs; + allocator_ci.instance = instance; + VmaAllocator ret{}; + auto const result = vmaCreateAllocator(&allocator_ci, &ret); + if (result == VK_SUCCESS) { return ret; } + + throw std::runtime_error{"Failed to create Vulkan Memory Allocator"}; +} +} // namespace lvk diff --git a/src/vma.hpp b/src/vma.hpp new file mode 100644 index 0000000..ada2568 --- /dev/null +++ b/src/vma.hpp @@ -0,0 +1,16 @@ +#pragma once +#include +#include +#include + +namespace lvk::vma { +struct Deleter { + void operator()(VmaAllocator allocator) const noexcept; +}; + +using Allocator = Scoped; + +[[nodiscard]] auto create_allocator(vk::Instance instance, + vk::PhysicalDevice physical_device, + vk::Device device) -> Allocator; +} // namespace lvk::vma From d87b2e66aed313a98b3e979de4d85a286adbcb4a Mon Sep 17 00:00:00 2001 From: Karn Kaul Date: Fri, 28 Mar 2025 21:41:45 -0700 Subject: [PATCH 02/11] WIP: Buffers --- guide/src/SUMMARY.md | 1 + guide/src/memory/buffers.md | 127 ++++++++++++++++++++++++++++++++++++ guide/src/memory/vma.md | 3 +- src/app.cpp | 1 + src/scoped.hpp | 3 +- src/vma.cpp | 73 ++++++++++++++++++++- src/vma.hpp | 52 +++++++++++++++ 7 files changed, 256 insertions(+), 4 deletions(-) create mode 100644 guide/src/memory/buffers.md diff --git a/guide/src/SUMMARY.md b/guide/src/SUMMARY.md index bdbb798..ff1f5f3 100644 --- a/guide/src/SUMMARY.md +++ b/guide/src/SUMMARY.md @@ -38,3 +38,4 @@ - [Memory Allocation](memory/README.md) - [Vulkan Memory Allocator](memory/vma.md) + - [Buffers](memory/buffers.md) diff --git a/guide/src/memory/buffers.md b/guide/src/memory/buffers.md new file mode 100644 index 0000000..3af220e --- /dev/null +++ b/guide/src/memory/buffers.md @@ -0,0 +1,127 @@ +# Buffers + +First add the RAII wrapper components for VMA buffers: + +```cpp +struct RawBuffer { + VmaAllocator allocator{}; + VmaAllocation allocation{}; + vk::Buffer buffer{}; + vk::DeviceSize capacity{}; + vk::DeviceSize size{}; + void* mapped{}; + + auto operator==(RawBuffer const& rhs) const -> bool = default; +}; + +struct BufferDeleter { + void operator()(RawBuffer const& raw_buffer) const noexcept; +}; + +// ... +void BufferDeleter::operator()(RawBuffer const& raw_buffer) const noexcept { + vmaDestroyBuffer(raw_buffer.allocator, raw_buffer.buffer, + raw_buffer.allocation); +} +``` + +Buffers can be backed by host (RAM) or device (VRAM) memory: the former is mappable and thus useful for data that changes every frame, latter is faster to access for the GPU but needs more complex methods to copy data from the CPU to it. Add the relevant subset of parameters and the RAII wrapper: + +```cpp +enum class BufferType : std::int8_t { Host, Device }; + +struct BufferCreateInfo { + vk::BufferUsageFlags usage{}; + vk::DeviceSize size{}; + BufferType type{BufferType::Host}; +}; + +class Buffer { + public: + using CreateInfo = BufferCreateInfo; + + explicit Buffer(VmaAllocator allocator, CreateInfo const& create_info); + + [[nodiscard]] auto get_type() const -> Type { + return m_buffer.get().mapped == nullptr ? Type::Device : Type::Host; + } + + [[nodiscard]] auto get_usage() const -> vk::BufferUsageFlags { + return m_usage; + } + + [[nodiscard]] auto get_raw() const -> RawBuffer const& { + return m_buffer.get(); + } + + auto resize(vk::DeviceSize size) -> bool; + + private: + auto create(VmaAllocator allocator, vk::DeviceSize size) -> bool; + + Scoped m_buffer{}; + vk::BufferUsageFlags m_usage{}; +}; +``` + +`resize()` and `create()` are separate because the former uses the existing `m_buffer`'s allocator. The implementation: + +```cpp +[[nodiscard]] constexpr auto positive_size(vk::DeviceSize const in) { + return in > 0 ? in : 1; +} + +// ... +Buffer::Buffer(VmaAllocator allocator, CreateInfo const& create_info) + : m_usage(create_info.usage) { + create(allocator, create_info.type, create_info.size); +} + +auto Buffer::resize(vk::DeviceSize const size) -> bool { + if (size <= m_buffer.get().capacity) { + m_buffer.get().size = size; + return true; + } + return create(m_buffer.get().allocator, get_type(), size); +} + +auto Buffer::create(VmaAllocator allocator, Type const type, + vk::DeviceSize size) -> bool { + // buffers cannot be zero sized. + size = positive_size(size); + auto allocation_ci = VmaAllocationCreateInfo{}; + allocation_ci.flags = + VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; + if (type == BufferType::Device) { + allocation_ci.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; + } else { + allocation_ci.usage = VMA_MEMORY_USAGE_AUTO_PREFER_HOST; + allocation_ci.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT; + } + + auto buffer_ci = vk::BufferCreateInfo{}; + buffer_ci.setSize(size).setUsage(m_usage); + auto vma_buffer_ci = static_cast(buffer_ci); + + VmaAllocation allocation{}; + VkBuffer buffer{}; + auto allocation_info = VmaAllocationInfo{}; + auto const result = + vmaCreateBuffer(allocator, &vma_buffer_ci, &allocation_ci, &buffer, + &allocation, &allocation_info); + if (result != VK_SUCCESS) { + std::println(stderr, "Failed to create VMA Buffer"); + return false; + } + + m_buffer = RawBuffer{ + .allocator = allocator, + .allocation = allocation, + .buffer = buffer, + .capacity = size, + .size = size, + .mapped = allocation_info.pMappedData, + }; + return true; +} +``` diff --git a/guide/src/memory/vma.md b/guide/src/memory/vma.md index 8226d70..25e90c7 100644 --- a/guide/src/memory/vma.md +++ b/guide/src/memory/vma.md @@ -26,10 +26,11 @@ using Allocator = Scoped; } // namespace lvk::vma // vma.cpp -void vma::Deleter::operator()(VmaAllocator allocator) const noexcept { +void Deleter::operator()(VmaAllocator allocator) const noexcept { vmaDestroyAllocator(allocator); } +// ... auto vma::create_allocator(vk::Instance const instance, vk::PhysicalDevice const physical_device, vk::Device const device) -> Allocator { diff --git a/src/app.cpp b/src/app.cpp index a0f3b5f..e311fe4 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -77,6 +77,7 @@ void App::run() { create_surface(); select_gpu(); create_device(); + create_allocator(); create_swapchain(); create_render_sync(); create_imgui(); diff --git a/src/scoped.hpp b/src/scoped.hpp index 462d6d9..73b6dec 100644 --- a/src/scoped.hpp +++ b/src/scoped.hpp @@ -4,8 +4,7 @@ namespace lvk { template -concept Scopeable = - std::equality_comparable && std::is_default_constructible_v; +concept Scopeable = std::equality_comparable; template class Scoped { diff --git a/src/vma.cpp b/src/vma.cpp index 36a67de..09aa9fa 100644 --- a/src/vma.cpp +++ b/src/vma.cpp @@ -1,11 +1,82 @@ #include +#include #include namespace lvk { -void vma::Deleter::operator()(VmaAllocator allocator) const noexcept { +namespace vma { +namespace { +[[nodiscard]] constexpr auto positive_size(vk::DeviceSize const in) { + return in > 0 ? in : 1; +} +} // namespace + +void Deleter::operator()(VmaAllocator allocator) const noexcept { vmaDestroyAllocator(allocator); } +void BufferDeleter::operator()(RawBuffer const& raw_buffer) const noexcept { + vmaDestroyBuffer(raw_buffer.allocator, raw_buffer.buffer, + raw_buffer.allocation); +} + +Buffer::Buffer(VmaAllocator allocator, CreateInfo const& create_info) + : m_usage(create_info.usage) { + if (create_info.type == Type::Device) { + // device buffers require a transfer operation to copy data. + m_usage |= vk::BufferUsageFlagBits::eTransferDst; + } + create(allocator, create_info.type, create_info.size); +} + +auto Buffer::resize(vk::DeviceSize const size) -> bool { + if (size <= m_buffer.get().capacity) { + m_buffer.get().size = size; + return true; + } + return create(m_buffer.get().allocator, get_type(), size); +} + +auto Buffer::create(VmaAllocator allocator, Type const type, + vk::DeviceSize size) -> bool { + // buffers cannot be zero sized. + size = positive_size(size); + auto allocation_ci = VmaAllocationCreateInfo{}; + allocation_ci.flags = + VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; + if (type == BufferType::Device) { + allocation_ci.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; + } else { + allocation_ci.usage = VMA_MEMORY_USAGE_AUTO_PREFER_HOST; + allocation_ci.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT; + } + + auto buffer_ci = vk::BufferCreateInfo{}; + buffer_ci.setSize(size).setUsage(m_usage); + auto vma_buffer_ci = static_cast(buffer_ci); + + VmaAllocation allocation{}; + VkBuffer buffer{}; + auto allocation_info = VmaAllocationInfo{}; + auto const result = + vmaCreateBuffer(allocator, &vma_buffer_ci, &allocation_ci, &buffer, + &allocation, &allocation_info); + if (result != VK_SUCCESS) { + std::println(stderr, "Failed to create VMA Buffer"); + return false; + } + + m_buffer = RawBuffer{ + .allocator = allocator, + .allocation = allocation, + .buffer = buffer, + .capacity = size, + .size = size, + .mapped = allocation_info.pMappedData, + }; + return true; +} +} // namespace vma + auto vma::create_allocator(vk::Instance const instance, vk::PhysicalDevice const physical_device, vk::Device const device) -> Allocator { diff --git a/src/vma.hpp b/src/vma.hpp index ada2568..ca72202 100644 --- a/src/vma.hpp +++ b/src/vma.hpp @@ -2,6 +2,7 @@ #include #include #include +#include namespace lvk::vma { struct Deleter { @@ -13,4 +14,55 @@ using Allocator = Scoped; [[nodiscard]] auto create_allocator(vk::Instance instance, vk::PhysicalDevice physical_device, vk::Device device) -> Allocator; + +struct RawBuffer { + VmaAllocator allocator{}; + VmaAllocation allocation{}; + vk::Buffer buffer{}; + vk::DeviceSize capacity{}; + vk::DeviceSize size{}; + void* mapped{}; + + auto operator==(RawBuffer const& rhs) const -> bool = default; +}; + +struct BufferDeleter { + void operator()(RawBuffer const& raw_buffer) const noexcept; +}; + +enum class BufferType : std::int8_t { Host, Device }; + +struct BufferCreateInfo { + vk::BufferUsageFlags usage{}; + vk::DeviceSize size{}; + BufferType type{BufferType::Host}; +}; + +class Buffer { + public: + using CreateInfo = BufferCreateInfo; + using Type = BufferType; + + explicit Buffer(VmaAllocator allocator, CreateInfo const& create_info); + + [[nodiscard]] auto get_type() const -> Type { + return m_buffer.get().mapped == nullptr ? Type::Device : Type::Host; + } + + [[nodiscard]] auto get_usage() const -> vk::BufferUsageFlags { + return m_usage; + } + + [[nodiscard]] auto get_raw() const -> RawBuffer const& { + return m_buffer.get(); + } + + auto resize(vk::DeviceSize size) -> bool; + + private: + auto create(VmaAllocator allocator, Type type, vk::DeviceSize size) -> bool; + + Scoped m_buffer{}; + vk::BufferUsageFlags m_usage{}; +}; } // namespace lvk::vma From 0f0ddd212b033c609efe84ef5599193f0e5eeee8 Mon Sep 17 00:00:00 2001 From: Karn Kaul Date: Fri, 28 Mar 2025 22:36:37 -0700 Subject: [PATCH 03/11] Vertex and Index Buffers --- assets/shader.vert | Bin 1572 -> 1156 bytes guide/src/SUMMARY.md | 1 + guide/src/memory/ibo_quad.png | Bin 0 -> 74756 bytes guide/src/memory/vertex_buffer.md | 148 ++++++++++++++++++++++++++++++ src/app.cpp | 70 +++++++++++++- src/app.hpp | 4 + src/glsl/shader.vert | 19 +--- src/vertex.hpp | 10 ++ 8 files changed, 235 insertions(+), 17 deletions(-) create mode 100644 guide/src/memory/ibo_quad.png create mode 100644 guide/src/memory/vertex_buffer.md create mode 100644 src/vertex.hpp diff --git a/assets/shader.vert b/assets/shader.vert index 1d47401067651e26d7355fbb9845e57b9f251913..ca41ab9d6f7b9492dd8230e282fa74e09a50c8ab 100644 GIT binary patch literal 1156 zcmYk5-Afx$5XG-aHhycZ^`qA6T74^s4_X9K5tZ#jK#S1VC2SVCuOCD>aQXHqzf%GW1>zR-P}Z->p1E6<6-$XOXJMuY234e zG%4+4if(F5XY(W*^~ME(6yt9OCUBkfi=i!T;rc*BFWp$IJR15cMrE87Sut>2%k|l< znLsnIiNDX{+jQ_d9hB)GL9rE2OwK>kHj_@zE*x+3^3ML#1l*}|%&y#JX8TumSa$Oy zeao>)a-%GJ%LUpOd*ej)3|kd+m>I?~dj``(q`UObn?E%#F^4^HJckwz?>X+k(Ua$h z;n?-Es-oGE0-ktPQCmKgtx2UHlf&df;VF4I3_K&xo8Xfg`#!ioFP?hTN54?V@aVzJ z@ADrNQ_ttY>;sN^%ktEt{}p|Hk`HB^?=@agjDAyiRUSRumLm4&Ymmd}Yh9im$kP|N z`mI;*YzT)k>hccwi;M&2jjs;97$Gtkz$O~8=&^-`}Fv&ngF+N(i7`Jb8i zSNX-n=eKu@RhLX>=9y<_W@neH*EXk&nJ}|v&g`3VEt;ATBdp`DE8>ew7-MY1k|T-RSs`iIDqX2wMAI66Fx+OOlJ-;amImn@Alo2PNt z4$`EspHq0#V?3KD*{C~C2*en#7ns1DQH_3YXbanOJp}5VK0apg`*cvGCvWp^dSb*3 z%q8I#xAP%WIfv12Z&budFY65|Z0kQ>!?c7c=TSN+)5=w)62~Uy+h~|6$7fCsw|Q}F zzfw{=${4dz>{Vv_2Xo~*^kft5oZS1oc?3j{BZ$n-vp?}2V*DF8#2j8rGxWwR7=Z1t_?1Mvpx1{NxIR5R*r(e#qEzNm& z6aEgHvL__;e@~h?*t#sOJ-r3q7(KAS ze+mht$;G?l`@lKyxf8wt-sg;4mH$j}YOPAR$LGohm$&dz8Oe270tN>@d9Fyn;ZqCW z=ejA|=b{dCCKt23BLRa0pSkkC3{EcI0=}>FNH+Q3X+Lv)ECFMAY)3X2cW|Wcn9;5T ZE-`=a^uQdSNSHPAr6xZ7U&^pA`3)_5bHo4u diff --git a/guide/src/SUMMARY.md b/guide/src/SUMMARY.md index ff1f5f3..852f5df 100644 --- a/guide/src/SUMMARY.md +++ b/guide/src/SUMMARY.md @@ -39,3 +39,4 @@ - [Memory Allocation](memory/README.md) - [Vulkan Memory Allocator](memory/vma.md) - [Buffers](memory/buffers.md) + - [Vertex Buffer](memory/vertex_buffer.md) diff --git a/guide/src/memory/ibo_quad.png b/guide/src/memory/ibo_quad.png new file mode 100644 index 0000000000000000000000000000000000000000..554785ba7c1c950d2c076d7482a9ae3f5a522634 GIT binary patch literal 74756 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 2x 32-bit floats. + vk::VertexInputAttributeDescription2EXT{0, 0, vk::Format::eR32G32Sfloat, + offsetof(Vertex, position)}, + // vec3 => 3x 32-bit floats + vk::VertexInputAttributeDescription2EXT{1, 0, vk::Format::eR32G32B32Sfloat, + offsetof(Vertex, color)}, +}; + +// one vertex binding at location 0. +constexpr auto vertex_bindings_v = std::array{ + // we are using interleaved data with a stride of sizeof(Vertex). + vk::VertexInputBindingDescription2EXT{0, sizeof(Vertex), + vk::VertexInputRate::eVertex, 1}, +}; + +constexpr auto vertex_input_v = ShaderVertexInput{ + .attributes = vertex_attributes_v, + .bindings = vertex_bindings_v, +}; +``` + +Add `vertex_input_v` to the Shader Create Info: + +```cpp +auto const shader_ci = ShaderProgram::CreateInfo{ + // ... + .vertex_input = vertex_input_v, + .set_layouts = {}, +}; +``` + +With the vertex input defined, we can update the vertex shader and recompile it: + +```glsl +#version 450 core + +layout (location = 0) in vec2 a_pos; +layout (location = 1) in vec3 a_color; + +layout (location = 0) out vec3 out_color; + +void main() { + const vec2 position = a_pos; + + out_color = a_color; + gl_Position = vec4(position, 0.0, 1.0); +} +``` + +Add a VBO (Vertex Buffer Object) member and create it: + +```cpp +void App::create_vertex_buffer() { + // we want to write 3x Vertex objects to a Host VertexBuffer. + auto const buffer_ci = vma::Buffer::CreateInfo{ + .usage = vk::BufferUsageFlagBits::eVertexBuffer, + .size = 3 * sizeof(Vertex), + .type = vma::BufferType::Host, + }; + m_vbo.emplace(m_allocator.get(), buffer_ci); + + // vertices that were previously hard-coded in the shader. + static constexpr auto vertices_v = std::array{ + Vertex{.position = {-0.5f, -0.5f}, .color = {1.0f, 0.0f, 0.0f}}, + Vertex{.position = {0.5f, -0.5f}, .color = {0.0f, 1.0f, 0.0f}}, + Vertex{.position = {0.0f, 0.5f}, .color = {0.0f, 0.0f, 1.0f}}, + }; + // host buffers have a memory-mapped pointer available to memcpy data to. + std::memcpy(m_vbo->get_raw().mapped, vertices_v.data(), sizeof(vertices_v)); +} +``` + +Bind the VBO before recording the draw call: + +```cpp +// single VBO at binding 0 at no offset. +command_buffer.bindVertexBuffers(0, m_vbo->get_raw().buffer, + vk::DeviceSize{}); +// m_vbo has 3 vertices. +command_buffer.draw(3, 1, 0, 0); +``` + +You should see the same triangle as before. But now we can use whatever set of vertices we like! To change it to a quad / rectangle, let's utilize an index buffer: this will reduce vertex duplication in general. + +```cpp +void App::create_vertex_buffer() { + // we want to write 4x Vertex objects to a Host VertexBuffer. + auto buffer_ci = vma::Buffer::CreateInfo{ + .usage = vk::BufferUsageFlagBits::eVertexBuffer, + .size = 4 * sizeof(Vertex), + .type = vma::BufferType::Host, + }; + m_vbo.emplace(m_allocator.get(), buffer_ci); + + // vertices that form a quad. + static constexpr auto vertices_v = std::array{ + Vertex{.position = {-0.5f, -0.5f}, .color = {1.0f, 0.0f, 0.0f}}, + Vertex{.position = {0.5f, -0.5f}, .color = {0.0f, 1.0f, 0.0f}}, + Vertex{.position = {0.5f, 0.5f}, .color = {0.0f, 0.0f, 1.0f}}, + Vertex{.position = {-0.5f, 0.5f}, .color = {1.0f, 1.0f, 0.0f}}, + }; + // host buffers have a memory-mapped pointer available to memcpy data to. + std::memcpy(m_vbo->get_raw().mapped, vertices_v.data(), sizeof(vertices_v)); + + // prepare to write 6x u32 indices to a Host IndexBuffer. + buffer_ci = { + .usage = vk::BufferUsageFlagBits::eIndexBuffer, + .size = 6 * sizeof(std::uint32_t), + .type = vma::BufferType::Host, + }; + m_ibo.emplace(m_allocator.get(), buffer_ci); + static constexpr auto indices_v = std::array{ + 0u, 1u, 2u, 2u, 3u, 0u, + }; + std::memcpy(m_ibo->get_raw().mapped, indices_v.data(), sizeof(indices_v)); +} +``` + +Bind the index buffer and use the `drawIndexed()` command: + +```cpp +// single VBO at binding 0 at no offset. +command_buffer.bindVertexBuffers(0, m_vbo->get_raw().buffer, + vk::DeviceSize{}); +// IBO with u32 indices at no offset. +command_buffer.bindIndexBuffer(m_ibo->get_raw().buffer, 0, + vk::IndexType::eUint32); +// m_ibo has 6 indices. +command_buffer.drawIndexed(6, 1, 0, 0, 0); +``` diff --git a/src/app.cpp b/src/app.cpp index e311fe4..73a7d33 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -11,6 +12,28 @@ namespace lvk { using namespace std::chrono_literals; namespace { +// two vertex attributes: position at 0, color at 1. +constexpr auto vertex_attributes_v = std::array{ + // the format matches the type and layout of data: vec2 => 2x 32-bit floats. + vk::VertexInputAttributeDescription2EXT{0, 0, vk::Format::eR32G32Sfloat, + offsetof(Vertex, position)}, + // vec3 => 3x 32-bit floats + vk::VertexInputAttributeDescription2EXT{1, 0, vk::Format::eR32G32B32Sfloat, + offsetof(Vertex, color)}, +}; + +// one vertex binding at location 0. +constexpr auto vertex_bindings_v = std::array{ + // we are using interleaved data with a stride of sizeof(Vertex). + vk::VertexInputBindingDescription2EXT{0, sizeof(Vertex), + vk::VertexInputRate::eVertex, 1}, +}; + +constexpr auto vertex_input_v = ShaderVertexInput{ + .attributes = vertex_attributes_v, + .bindings = vertex_bindings_v, +}; + [[nodiscard]] auto locate_assets_dir() -> fs::path { // look for '/assets/', starting from the working // directory and walking up the parent directory tree. @@ -83,6 +106,8 @@ void App::run() { create_imgui(); create_shader(); + create_vertex_buffer(); + main_loop(); } @@ -236,16 +261,49 @@ void App::create_allocator() { void App::create_shader() { auto const vertex_spirv = to_spir_v(asset_path("shader.vert")); auto const fragment_spirv = to_spir_v(asset_path("shader.frag")); + auto const shader_ci = ShaderProgram::CreateInfo{ .device = *m_device, .vertex_spirv = vertex_spirv, .fragment_spirv = fragment_spirv, - .vertex_input = {}, + .vertex_input = vertex_input_v, .set_layouts = {}, }; m_shader.emplace(shader_ci); } +void App::create_vertex_buffer() { + // we want to write 4x Vertex objects to a Host VertexBuffer. + auto buffer_ci = vma::Buffer::CreateInfo{ + .usage = vk::BufferUsageFlagBits::eVertexBuffer, + .size = 4 * sizeof(Vertex), + .type = vma::BufferType::Host, + }; + m_vbo.emplace(m_allocator.get(), buffer_ci); + + // vertices that form a quad. + static constexpr auto vertices_v = std::array{ + Vertex{.position = {-0.5f, -0.5f}, .color = {1.0f, 0.0f, 0.0f}}, + Vertex{.position = {0.5f, -0.5f}, .color = {0.0f, 1.0f, 0.0f}}, + Vertex{.position = {0.5f, 0.5f}, .color = {0.0f, 0.0f, 1.0f}}, + Vertex{.position = {-0.5f, 0.5f}, .color = {1.0f, 1.0f, 0.0f}}, + }; + // host buffers have a memory-mapped pointer available to memcpy data to. + std::memcpy(m_vbo->get_raw().mapped, vertices_v.data(), sizeof(vertices_v)); + + // prepare to write 6x u32 indices to a Host IndexBuffer. + buffer_ci = { + .usage = vk::BufferUsageFlagBits::eIndexBuffer, + .size = 6 * sizeof(std::uint32_t), + .type = vma::BufferType::Host, + }; + m_ibo.emplace(m_allocator.get(), buffer_ci); + static constexpr auto indices_v = std::array{ + 0u, 1u, 2u, 2u, 3u, 0u, + }; + std::memcpy(m_ibo->get_raw().mapped, indices_v.data(), sizeof(indices_v)); +} + auto App::asset_path(std::string_view const uri) const -> fs::path { return m_assets_dir / uri; } @@ -424,7 +482,13 @@ void App::inspect() { void App::draw(vk::CommandBuffer const command_buffer) const { m_shader->bind(command_buffer, m_framebuffer_size); - // current shader has hard-coded logic for 3 vertices. - command_buffer.draw(3, 1, 0, 0); + // single VBO at binding 0 at no offset. + command_buffer.bindVertexBuffers(0, m_vbo->get_raw().buffer, + vk::DeviceSize{}); + // IBO with u32 indices at no offset. + command_buffer.bindIndexBuffer(m_ibo->get_raw().buffer, 0, + vk::IndexType::eUint32); + // m_ibo has 6 indices. + command_buffer.drawIndexed(6, 1, 0, 0, 0); } } // namespace lvk diff --git a/src/app.hpp b/src/app.hpp index 6532cbc..c06f8fe 100644 --- a/src/app.hpp +++ b/src/app.hpp @@ -38,6 +38,7 @@ class App { void create_imgui(); void create_allocator(); void create_shader(); + void create_vertex_buffer(); [[nodiscard]] auto asset_path(std::string_view uri) const -> fs::path; @@ -78,6 +79,9 @@ class App { std::optional m_shader{}; + std::optional m_vbo{}; + std::optional m_ibo{}; + glm::ivec2 m_framebuffer_size{}; std::optional m_render_target{}; bool m_wireframe{}; diff --git a/src/glsl/shader.vert b/src/glsl/shader.vert index 8efde56..edebf18 100644 --- a/src/glsl/shader.vert +++ b/src/glsl/shader.vert @@ -1,22 +1,13 @@ #version 450 core +layout (location = 0) in vec2 a_pos; +layout (location = 1) in vec3 a_color; + layout (location = 0) out vec3 out_color; void main() { - const vec2 positions[] = { - vec2(-0.5, -0.5), - vec2(0.5, -0.5), - vec2(0.0, 0.5), - }; - - const vec3 colors[] = { - vec3(1.0, 0.0, 0.0), - vec3(0.0, 1.0, 0.0), - vec3(0.0, 0.0, 1.0), - }; - - const vec2 position = positions[gl_VertexIndex]; + const vec2 position = a_pos; - out_color = colors[gl_VertexIndex]; + out_color = a_color; gl_Position = vec4(position, 0.0, 1.0); } diff --git a/src/vertex.hpp b/src/vertex.hpp new file mode 100644 index 0000000..20987d5 --- /dev/null +++ b/src/vertex.hpp @@ -0,0 +1,10 @@ +#pragma once +#include +#include + +namespace lvk { +struct Vertex { + glm::vec2 position{}; + glm::vec3 color{}; +}; +} // namespace lvk From 6b1b15f91fb51536db48877793abc511a1c3fe42 Mon Sep 17 00:00:00 2001 From: Karn Kaul Date: Fri, 28 Mar 2025 22:44:52 -0700 Subject: [PATCH 04/11] Fix warnings --- ext/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ext/CMakeLists.txt b/ext/CMakeLists.txt index a4204b1..bd7eb32 100644 --- a/ext/CMakeLists.txt +++ b/ext/CMakeLists.txt @@ -67,6 +67,9 @@ target_link_libraries(vma PUBLIC Vulkan::Headers GPUOpen::VulkanMemoryAllocator ) +target_include_directories(vma SYSTEM PUBLIC + src/VulkanMemoryAllocator/include +) target_compile_definitions(vma PUBLIC VMA_STATIC_VULKAN_FUNCTIONS=0 VMA_DYNAMIC_VULKAN_FUNCTIONS=1 From 23f4b5040b523cd98cb586f0a99ba724c622dde7 Mon Sep 17 00:00:00 2001 From: Karn Kaul Date: Sat, 29 Mar 2025 15:43:59 -0700 Subject: [PATCH 05/11] Refactor `vma::Buffer` API --- guide/src/SUMMARY.md | 2 +- guide/src/memory/buffers.md | 110 ++++++------------ ...vertex_buffer.md => host_vertex_buffer.md} | 86 ++++---------- src/app.cpp | 68 +++-------- src/app.hpp | 3 +- src/vertex.hpp | 18 +++ src/vma.cpp | 79 +++++-------- src/vma.hpp | 48 ++------ 8 files changed, 133 insertions(+), 281 deletions(-) rename guide/src/memory/{vertex_buffer.md => host_vertex_buffer.md} (50%) diff --git a/guide/src/SUMMARY.md b/guide/src/SUMMARY.md index 852f5df..bd6a749 100644 --- a/guide/src/SUMMARY.md +++ b/guide/src/SUMMARY.md @@ -39,4 +39,4 @@ - [Memory Allocation](memory/README.md) - [Vulkan Memory Allocator](memory/vma.md) - [Buffers](memory/buffers.md) - - [Vertex Buffer](memory/vertex_buffer.md) + - [Host Vertex Buffer](memory/host_vertex_buffer.md) diff --git a/guide/src/memory/buffers.md b/guide/src/memory/buffers.md index 3af220e..149be94 100644 --- a/guide/src/memory/buffers.md +++ b/guide/src/memory/buffers.md @@ -4,14 +4,17 @@ First add the RAII wrapper components for VMA buffers: ```cpp struct RawBuffer { + [[nodiscard]] auto mapped_span() const -> std::span { + return std::span{static_cast(mapped), size}; + } + + auto operator==(RawBuffer const& rhs) const -> bool = default; + VmaAllocator allocator{}; VmaAllocation allocation{}; vk::Buffer buffer{}; - vk::DeviceSize capacity{}; vk::DeviceSize size{}; void* mapped{}; - - auto operator==(RawBuffer const& rhs) const -> bool = default; }; struct BufferDeleter { @@ -25,82 +28,30 @@ void BufferDeleter::operator()(RawBuffer const& raw_buffer) const noexcept { } ``` -Buffers can be backed by host (RAM) or device (VRAM) memory: the former is mappable and thus useful for data that changes every frame, latter is faster to access for the GPU but needs more complex methods to copy data from the CPU to it. Add the relevant subset of parameters and the RAII wrapper: +Buffers can be backed by host (RAM) or device (VRAM) memory: the former is mappable and thus useful for data that changes every frame, latter is faster to access for the GPU but needs more complex methods to copy data from the CPU to it. Leaving device buffers for later, add the `Buffer` alias and a create function: ```cpp -enum class BufferType : std::int8_t { Host, Device }; - -struct BufferCreateInfo { - vk::BufferUsageFlags usage{}; - vk::DeviceSize size{}; - BufferType type{BufferType::Host}; -}; - -class Buffer { - public: - using CreateInfo = BufferCreateInfo; - - explicit Buffer(VmaAllocator allocator, CreateInfo const& create_info); - - [[nodiscard]] auto get_type() const -> Type { - return m_buffer.get().mapped == nullptr ? Type::Device : Type::Host; - } - - [[nodiscard]] auto get_usage() const -> vk::BufferUsageFlags { - return m_usage; - } - - [[nodiscard]] auto get_raw() const -> RawBuffer const& { - return m_buffer.get(); - } - - auto resize(vk::DeviceSize size) -> bool; - - private: - auto create(VmaAllocator allocator, vk::DeviceSize size) -> bool; +using Buffer = Scoped; - Scoped m_buffer{}; - vk::BufferUsageFlags m_usage{}; -}; +[[nodiscard]] auto create_host_buffer(VmaAllocator allocator, + vk::BufferUsageFlags usage, + vk::DeviceSize size) -> Buffer; ``` -`resize()` and `create()` are separate because the former uses the existing `m_buffer`'s allocator. The implementation: +Add a helper function that can be reused for device buffers too later: ```cpp -[[nodiscard]] constexpr auto positive_size(vk::DeviceSize const in) { - return in > 0 ? in : 1; -} - -// ... -Buffer::Buffer(VmaAllocator allocator, CreateInfo const& create_info) - : m_usage(create_info.usage) { - create(allocator, create_info.type, create_info.size); -} - -auto Buffer::resize(vk::DeviceSize const size) -> bool { - if (size <= m_buffer.get().capacity) { - m_buffer.get().size = size; - return true; - } - return create(m_buffer.get().allocator, get_type(), size); -} - -auto Buffer::create(VmaAllocator allocator, Type const type, - vk::DeviceSize size) -> bool { - // buffers cannot be zero sized. - size = positive_size(size); - auto allocation_ci = VmaAllocationCreateInfo{}; - allocation_ci.flags = - VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; - if (type == BufferType::Device) { - allocation_ci.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; - } else { - allocation_ci.usage = VMA_MEMORY_USAGE_AUTO_PREFER_HOST; - allocation_ci.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT; +[[nodiscard]] auto create_buffer(VmaAllocator allocator, + VmaAllocationCreateInfo const& allocation_ci, + vk::BufferUsageFlags const usage, + vk::DeviceSize const size) -> Buffer { + if (size == 0) { + std::println(stderr, "Buffer cannot be 0-sized"); + return {}; } auto buffer_ci = vk::BufferCreateInfo{}; - buffer_ci.setSize(size).setUsage(m_usage); + buffer_ci.setSize(size).setUsage(usage); auto vma_buffer_ci = static_cast(buffer_ci); VmaAllocation allocation{}; @@ -111,17 +62,30 @@ auto Buffer::create(VmaAllocator allocator, Type const type, &allocation, &allocation_info); if (result != VK_SUCCESS) { std::println(stderr, "Failed to create VMA Buffer"); - return false; + return {}; } - m_buffer = RawBuffer{ + return RawBuffer{ .allocator = allocator, .allocation = allocation, .buffer = buffer, - .capacity = size, .size = size, .mapped = allocation_info.pMappedData, }; - return true; +} +``` + +Implement `create_host_buffer()`: + +```cpp +auto vma::create_host_buffer(VmaAllocator allocator, + vk::BufferUsageFlags const usage, + vk::DeviceSize const size) -> Buffer { + auto allocation_ci = VmaAllocationCreateInfo{}; + allocation_ci.usage = VMA_MEMORY_USAGE_AUTO_PREFER_HOST; + allocation_ci.flags = + VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | + VMA_ALLOCATION_CREATE_MAPPED_BIT; + return create_buffer(allocator, allocation_ci, usage, size); } ``` diff --git a/guide/src/memory/vertex_buffer.md b/guide/src/memory/host_vertex_buffer.md similarity index 50% rename from guide/src/memory/vertex_buffer.md rename to guide/src/memory/host_vertex_buffer.md index 8161828..4b0e1cf 100644 --- a/guide/src/memory/vertex_buffer.md +++ b/guide/src/memory/host_vertex_buffer.md @@ -1,6 +1,6 @@ -# Vertex Buffer +# Host Vertex Buffer -The goal here is to move the hard-coded vertices in the shader to application code. For the time being we will use an ad-hoc host `vma::Buffer` and focus more on the rest of the infrastructure like vertex attributes. +The goal here is to move the hard-coded vertices in the shader to application code. For the time being we will use an ad-hoc Host type `vma::Buffer` and focus more on the rest of the infrastructure like vertex attributes. First add a new header, `vertex.hpp`: @@ -9,11 +9,7 @@ struct Vertex { glm::vec2 position{}; glm::vec3 color{}; }; -``` - -In `app.cpp`, store the vertex input: -```cpp // two vertex attributes: position at 0, color at 1. constexpr auto vertex_attributes_v = std::array{ // the format matches the type and layout of data: vec2 => 2x 32-bit floats. @@ -30,21 +26,24 @@ constexpr auto vertex_bindings_v = std::array{ vk::VertexInputBindingDescription2EXT{0, sizeof(Vertex), vk::VertexInputRate::eVertex, 1}, }; - -constexpr auto vertex_input_v = ShaderVertexInput{ - .attributes = vertex_attributes_v, - .bindings = vertex_bindings_v, -}; ``` -Add `vertex_input_v` to the Shader Create Info: +Add the vertex attributes and bindings to the Shader Create Info: ```cpp +// ... +static constexpr auto vertex_input_v = ShaderVertexInput{ + .attributes = vertex_attributes_v, + .bindings = vertex_bindings_v, +}; auto const shader_ci = ShaderProgram::CreateInfo{ - // ... + .device = *m_device, + .vertex_spirv = vertex_spirv, + .fragment_spirv = fragment_spirv, .vertex_input = vertex_input_v, .set_layouts = {}, }; +// ... ``` With the vertex input defined, we can update the vertex shader and recompile it: @@ -69,22 +68,22 @@ Add a VBO (Vertex Buffer Object) member and create it: ```cpp void App::create_vertex_buffer() { - // we want to write 3x Vertex objects to a Host VertexBuffer. - auto const buffer_ci = vma::Buffer::CreateInfo{ + // we want to write 4x Vertex objects to a Host VertexBuffer. + auto const buffer_ci = vma::BufferCreateInfo{ .usage = vk::BufferUsageFlagBits::eVertexBuffer, - .size = 3 * sizeof(Vertex), + .size = 4 * sizeof(Vertex), .type = vma::BufferType::Host, }; - m_vbo.emplace(m_allocator.get(), buffer_ci); + m_vbo = vma::create_buffer(m_allocator.get(), buffer_ci); - // vertices that were previously hard-coded in the shader. + // vertices moved from the shader. static constexpr auto vertices_v = std::array{ Vertex{.position = {-0.5f, -0.5f}, .color = {1.0f, 0.0f, 0.0f}}, Vertex{.position = {0.5f, -0.5f}, .color = {0.0f, 1.0f, 0.0f}}, Vertex{.position = {0.0f, 0.5f}, .color = {0.0f, 0.0f, 1.0f}}, }; // host buffers have a memory-mapped pointer available to memcpy data to. - std::memcpy(m_vbo->get_raw().mapped, vertices_v.data(), sizeof(vertices_v)); + std::memcpy(m_vbo.get().mapped, vertices_v.data(), sizeof(vertices_v)); } ``` @@ -98,51 +97,6 @@ command_buffer.bindVertexBuffers(0, m_vbo->get_raw().buffer, command_buffer.draw(3, 1, 0, 0); ``` -You should see the same triangle as before. But now we can use whatever set of vertices we like! To change it to a quad / rectangle, let's utilize an index buffer: this will reduce vertex duplication in general. - -```cpp -void App::create_vertex_buffer() { - // we want to write 4x Vertex objects to a Host VertexBuffer. - auto buffer_ci = vma::Buffer::CreateInfo{ - .usage = vk::BufferUsageFlagBits::eVertexBuffer, - .size = 4 * sizeof(Vertex), - .type = vma::BufferType::Host, - }; - m_vbo.emplace(m_allocator.get(), buffer_ci); - - // vertices that form a quad. - static constexpr auto vertices_v = std::array{ - Vertex{.position = {-0.5f, -0.5f}, .color = {1.0f, 0.0f, 0.0f}}, - Vertex{.position = {0.5f, -0.5f}, .color = {0.0f, 1.0f, 0.0f}}, - Vertex{.position = {0.5f, 0.5f}, .color = {0.0f, 0.0f, 1.0f}}, - Vertex{.position = {-0.5f, 0.5f}, .color = {1.0f, 1.0f, 0.0f}}, - }; - // host buffers have a memory-mapped pointer available to memcpy data to. - std::memcpy(m_vbo->get_raw().mapped, vertices_v.data(), sizeof(vertices_v)); - - // prepare to write 6x u32 indices to a Host IndexBuffer. - buffer_ci = { - .usage = vk::BufferUsageFlagBits::eIndexBuffer, - .size = 6 * sizeof(std::uint32_t), - .type = vma::BufferType::Host, - }; - m_ibo.emplace(m_allocator.get(), buffer_ci); - static constexpr auto indices_v = std::array{ - 0u, 1u, 2u, 2u, 3u, 0u, - }; - std::memcpy(m_ibo->get_raw().mapped, indices_v.data(), sizeof(indices_v)); -} -``` - -Bind the index buffer and use the `drawIndexed()` command: +You should see the same triangle as before. But now we can use whatever set of vertices we like! The Primitive Topology is Triange List by default, so every three vertices in the array is drawn as a triangle, eg for 9 vertices: `[[0, 1, 2], [3, 4, 5], [6, 7, 8]]`, where each inner `[]` represents a triangle comprised of the vertices at those indices. -```cpp -// single VBO at binding 0 at no offset. -command_buffer.bindVertexBuffers(0, m_vbo->get_raw().buffer, - vk::DeviceSize{}); -// IBO with u32 indices at no offset. -command_buffer.bindIndexBuffer(m_ibo->get_raw().buffer, 0, - vk::IndexType::eUint32); -// m_ibo has 6 indices. -command_buffer.drawIndexed(6, 1, 0, 0, 0); -``` +Host Vertex Buffers are useful for primitives that are temporary and/or frequently changing, such as UI objects. A 2D framework can use such VBOs exclusively: a simple approach would be a pool of buffers per virtual frame where for each draw a buffer is obtained from the current virtual frame's pool and vertices are copied in. diff --git a/src/app.cpp b/src/app.cpp index 73a7d33..56312d2 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -12,28 +12,6 @@ namespace lvk { using namespace std::chrono_literals; namespace { -// two vertex attributes: position at 0, color at 1. -constexpr auto vertex_attributes_v = std::array{ - // the format matches the type and layout of data: vec2 => 2x 32-bit floats. - vk::VertexInputAttributeDescription2EXT{0, 0, vk::Format::eR32G32Sfloat, - offsetof(Vertex, position)}, - // vec3 => 3x 32-bit floats - vk::VertexInputAttributeDescription2EXT{1, 0, vk::Format::eR32G32B32Sfloat, - offsetof(Vertex, color)}, -}; - -// one vertex binding at location 0. -constexpr auto vertex_bindings_v = std::array{ - // we are using interleaved data with a stride of sizeof(Vertex). - vk::VertexInputBindingDescription2EXT{0, sizeof(Vertex), - vk::VertexInputRate::eVertex, 1}, -}; - -constexpr auto vertex_input_v = ShaderVertexInput{ - .attributes = vertex_attributes_v, - .bindings = vertex_bindings_v, -}; - [[nodiscard]] auto locate_assets_dir() -> fs::path { // look for '/assets/', starting from the working // directory and walking up the parent directory tree. @@ -262,6 +240,10 @@ void App::create_shader() { auto const vertex_spirv = to_spir_v(asset_path("shader.vert")); auto const fragment_spirv = to_spir_v(asset_path("shader.frag")); + static constexpr auto vertex_input_v = ShaderVertexInput{ + .attributes = vertex_attributes_v, + .bindings = vertex_bindings_v, + }; auto const shader_ci = ShaderProgram::CreateInfo{ .device = *m_device, .vertex_spirv = vertex_spirv, @@ -273,35 +255,19 @@ void App::create_shader() { } void App::create_vertex_buffer() { - // we want to write 4x Vertex objects to a Host VertexBuffer. - auto buffer_ci = vma::Buffer::CreateInfo{ - .usage = vk::BufferUsageFlagBits::eVertexBuffer, - .size = 4 * sizeof(Vertex), - .type = vma::BufferType::Host, - }; - m_vbo.emplace(m_allocator.get(), buffer_ci); - - // vertices that form a quad. + // vertices previously hard-coded in the vertex shader. static constexpr auto vertices_v = std::array{ Vertex{.position = {-0.5f, -0.5f}, .color = {1.0f, 0.0f, 0.0f}}, Vertex{.position = {0.5f, -0.5f}, .color = {0.0f, 1.0f, 0.0f}}, - Vertex{.position = {0.5f, 0.5f}, .color = {0.0f, 0.0f, 1.0f}}, - Vertex{.position = {-0.5f, 0.5f}, .color = {1.0f, 1.0f, 0.0f}}, + Vertex{.position = {0.0f, 0.5f}, .color = {0.0f, 0.0f, 1.0f}}, }; - // host buffers have a memory-mapped pointer available to memcpy data to. - std::memcpy(m_vbo->get_raw().mapped, vertices_v.data(), sizeof(vertices_v)); + // we want to write vertices_v to a Host VertexBuffer. + m_vbo = vma::create_host_buffer(m_allocator.get(), + vk::BufferUsageFlagBits::eVertexBuffer, + sizeof(vertices_v)); - // prepare to write 6x u32 indices to a Host IndexBuffer. - buffer_ci = { - .usage = vk::BufferUsageFlagBits::eIndexBuffer, - .size = 6 * sizeof(std::uint32_t), - .type = vma::BufferType::Host, - }; - m_ibo.emplace(m_allocator.get(), buffer_ci); - static constexpr auto indices_v = std::array{ - 0u, 1u, 2u, 2u, 3u, 0u, - }; - std::memcpy(m_ibo->get_raw().mapped, indices_v.data(), sizeof(indices_v)); + // host buffers have a memory-mapped pointer available to memcpy data to. + std::memcpy(m_vbo.get().mapped, vertices_v.data(), sizeof(vertices_v)); } auto App::asset_path(std::string_view const uri) const -> fs::path { @@ -483,12 +449,8 @@ void App::inspect() { void App::draw(vk::CommandBuffer const command_buffer) const { m_shader->bind(command_buffer, m_framebuffer_size); // single VBO at binding 0 at no offset. - command_buffer.bindVertexBuffers(0, m_vbo->get_raw().buffer, - vk::DeviceSize{}); - // IBO with u32 indices at no offset. - command_buffer.bindIndexBuffer(m_ibo->get_raw().buffer, 0, - vk::IndexType::eUint32); - // m_ibo has 6 indices. - command_buffer.drawIndexed(6, 1, 0, 0, 0); + command_buffer.bindVertexBuffers(0, m_vbo.get().buffer, vk::DeviceSize{}); + // m_vbo has 3 vertices. + command_buffer.draw(3, 1, 0, 0); } } // namespace lvk diff --git a/src/app.hpp b/src/app.hpp index c06f8fe..0426738 100644 --- a/src/app.hpp +++ b/src/app.hpp @@ -79,8 +79,7 @@ class App { std::optional m_shader{}; - std::optional m_vbo{}; - std::optional m_ibo{}; + vma::Buffer m_vbo{}; glm::ivec2 m_framebuffer_size{}; std::optional m_render_target{}; diff --git a/src/vertex.hpp b/src/vertex.hpp index 20987d5..6c0fabb 100644 --- a/src/vertex.hpp +++ b/src/vertex.hpp @@ -1,10 +1,28 @@ #pragma once #include #include +#include namespace lvk { struct Vertex { glm::vec2 position{}; glm::vec3 color{}; }; + +// two vertex attributes: position at 0, color at 1. +constexpr auto vertex_attributes_v = std::array{ + // the format matches the type and layout of data: vec2 => 2x 32-bit floats. + vk::VertexInputAttributeDescription2EXT{0, 0, vk::Format::eR32G32Sfloat, + offsetof(Vertex, position)}, + // vec3 => 3x 32-bit floats + vk::VertexInputAttributeDescription2EXT{1, 0, vk::Format::eR32G32B32Sfloat, + offsetof(Vertex, color)}, +}; + +// one vertex binding at location 0. +constexpr auto vertex_bindings_v = std::array{ + // we are using interleaved data with a stride of sizeof(Vertex). + vk::VertexInputBindingDescription2EXT{0, sizeof(Vertex), + vk::VertexInputRate::eVertex, 1}, +}; } // namespace lvk diff --git a/src/vma.cpp b/src/vma.cpp index 09aa9fa..8841957 100644 --- a/src/vma.cpp +++ b/src/vma.cpp @@ -5,53 +5,17 @@ namespace lvk { namespace vma { namespace { -[[nodiscard]] constexpr auto positive_size(vk::DeviceSize const in) { - return in > 0 ? in : 1; -} -} // namespace - -void Deleter::operator()(VmaAllocator allocator) const noexcept { - vmaDestroyAllocator(allocator); -} - -void BufferDeleter::operator()(RawBuffer const& raw_buffer) const noexcept { - vmaDestroyBuffer(raw_buffer.allocator, raw_buffer.buffer, - raw_buffer.allocation); -} - -Buffer::Buffer(VmaAllocator allocator, CreateInfo const& create_info) - : m_usage(create_info.usage) { - if (create_info.type == Type::Device) { - // device buffers require a transfer operation to copy data. - m_usage |= vk::BufferUsageFlagBits::eTransferDst; - } - create(allocator, create_info.type, create_info.size); -} - -auto Buffer::resize(vk::DeviceSize const size) -> bool { - if (size <= m_buffer.get().capacity) { - m_buffer.get().size = size; - return true; - } - return create(m_buffer.get().allocator, get_type(), size); -} - -auto Buffer::create(VmaAllocator allocator, Type const type, - vk::DeviceSize size) -> bool { - // buffers cannot be zero sized. - size = positive_size(size); - auto allocation_ci = VmaAllocationCreateInfo{}; - allocation_ci.flags = - VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; - if (type == BufferType::Device) { - allocation_ci.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; - } else { - allocation_ci.usage = VMA_MEMORY_USAGE_AUTO_PREFER_HOST; - allocation_ci.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT; +[[nodiscard]] auto create_buffer(VmaAllocator allocator, + VmaAllocationCreateInfo const& allocation_ci, + vk::BufferUsageFlags const usage, + vk::DeviceSize const size) -> Buffer { + if (size == 0) { + std::println(stderr, "Buffer cannot be 0-sized"); + return {}; } auto buffer_ci = vk::BufferCreateInfo{}; - buffer_ci.setSize(size).setUsage(m_usage); + buffer_ci.setSize(size).setUsage(usage); auto vma_buffer_ci = static_cast(buffer_ci); VmaAllocation allocation{}; @@ -62,18 +26,26 @@ auto Buffer::create(VmaAllocator allocator, Type const type, &allocation, &allocation_info); if (result != VK_SUCCESS) { std::println(stderr, "Failed to create VMA Buffer"); - return false; + return {}; } - m_buffer = RawBuffer{ + return RawBuffer{ .allocator = allocator, .allocation = allocation, .buffer = buffer, - .capacity = size, .size = size, .mapped = allocation_info.pMappedData, }; - return true; +} +} // namespace + +void Deleter::operator()(VmaAllocator allocator) const noexcept { + vmaDestroyAllocator(allocator); +} + +void BufferDeleter::operator()(RawBuffer const& raw_buffer) const noexcept { + vmaDestroyBuffer(raw_buffer.allocator, raw_buffer.buffer, + raw_buffer.allocation); } } // namespace vma @@ -97,4 +69,15 @@ auto vma::create_allocator(vk::Instance const instance, throw std::runtime_error{"Failed to create Vulkan Memory Allocator"}; } + +auto vma::create_host_buffer(VmaAllocator allocator, + vk::BufferUsageFlags const usage, + vk::DeviceSize const size) -> Buffer { + auto allocation_ci = VmaAllocationCreateInfo{}; + allocation_ci.usage = VMA_MEMORY_USAGE_AUTO_PREFER_HOST; + allocation_ci.flags = + VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | + VMA_ALLOCATION_CREATE_MAPPED_BIT; + return create_buffer(allocator, allocation_ci, usage, size); +} } // namespace lvk diff --git a/src/vma.hpp b/src/vma.hpp index ca72202..0a5b521 100644 --- a/src/vma.hpp +++ b/src/vma.hpp @@ -2,7 +2,6 @@ #include #include #include -#include namespace lvk::vma { struct Deleter { @@ -16,53 +15,26 @@ using Allocator = Scoped; vk::Device device) -> Allocator; struct RawBuffer { + [[nodiscard]] auto mapped_span() const -> std::span { + return std::span{static_cast(mapped), size}; + } + + auto operator==(RawBuffer const& rhs) const -> bool = default; + VmaAllocator allocator{}; VmaAllocation allocation{}; vk::Buffer buffer{}; - vk::DeviceSize capacity{}; vk::DeviceSize size{}; void* mapped{}; - - auto operator==(RawBuffer const& rhs) const -> bool = default; }; struct BufferDeleter { void operator()(RawBuffer const& raw_buffer) const noexcept; }; -enum class BufferType : std::int8_t { Host, Device }; - -struct BufferCreateInfo { - vk::BufferUsageFlags usage{}; - vk::DeviceSize size{}; - BufferType type{BufferType::Host}; -}; - -class Buffer { - public: - using CreateInfo = BufferCreateInfo; - using Type = BufferType; +using Buffer = Scoped; - explicit Buffer(VmaAllocator allocator, CreateInfo const& create_info); - - [[nodiscard]] auto get_type() const -> Type { - return m_buffer.get().mapped == nullptr ? Type::Device : Type::Host; - } - - [[nodiscard]] auto get_usage() const -> vk::BufferUsageFlags { - return m_usage; - } - - [[nodiscard]] auto get_raw() const -> RawBuffer const& { - return m_buffer.get(); - } - - auto resize(vk::DeviceSize size) -> bool; - - private: - auto create(VmaAllocator allocator, Type type, vk::DeviceSize size) -> bool; - - Scoped m_buffer{}; - vk::BufferUsageFlags m_usage{}; -}; +[[nodiscard]] auto create_host_buffer(VmaAllocator allocator, + vk::BufferUsageFlags usage, + vk::DeviceSize size) -> Buffer; } // namespace lvk::vma From ee82cf1cac269db3ecd556fe5d6b7cf51cc0d2ad Mon Sep 17 00:00:00 2001 From: Karn Kaul Date: Sat, 29 Mar 2025 21:21:42 -0700 Subject: [PATCH 06/11] Add `CommandBlock`, use device VBO --- guide/src/SUMMARY.md | 4 +- guide/src/memory/command_block.md | 84 +++++++++++ guide/src/memory/device_buffers.md | 139 ++++++++++++++++++ .../src/memory/{ibo_quad.png => vbo_quad.png} | Bin ...host_vertex_buffer.md => vertex_buffer.md} | 6 +- src/app.cpp | 55 +++++-- src/app.hpp | 5 + src/command_block.cpp | 50 +++++++ src/command_block.hpp | 21 +++ src/vma.cpp | 52 +++++++ src/vma.hpp | 10 ++ 11 files changed, 411 insertions(+), 15 deletions(-) create mode 100644 guide/src/memory/command_block.md create mode 100644 guide/src/memory/device_buffers.md rename guide/src/memory/{ibo_quad.png => vbo_quad.png} (100%) rename guide/src/memory/{host_vertex_buffer.md => vertex_buffer.md} (93%) create mode 100644 src/command_block.cpp create mode 100644 src/command_block.hpp diff --git a/guide/src/SUMMARY.md b/guide/src/SUMMARY.md index bd6a749..7f6f044 100644 --- a/guide/src/SUMMARY.md +++ b/guide/src/SUMMARY.md @@ -39,4 +39,6 @@ - [Memory Allocation](memory/README.md) - [Vulkan Memory Allocator](memory/vma.md) - [Buffers](memory/buffers.md) - - [Host Vertex Buffer](memory/host_vertex_buffer.md) + - [Vertex Buffer](memory/vertex_buffer.md) + - [Command Block](memory/command_block.md) + - [Device Buffers](memory/device_buffers.md) diff --git a/guide/src/memory/command_block.md b/guide/src/memory/command_block.md new file mode 100644 index 0000000..26cc25c --- /dev/null +++ b/guide/src/memory/command_block.md @@ -0,0 +1,84 @@ +# Command Block + +Long-lived vertex buffers perform better when backed by Device memory, especially for 3D meshes. Data is transferred to device buffers in two steps: + +1. Allocate a host buffer and copy the data to its mapped memory +1. Allocate a device buffer, record a Buffer Copy operation and submit it + +The second step requires a command buffer and queue submission (_and_ waiting for the submitted work to complete). Encapsulate this behavior into a class, it will also be used for creating images: + +```cpp +class CommandBlock { + public: + explicit CommandBlock(vk::Device device, vk::Queue queue, + vk::CommandPool command_pool); + + [[nodiscard]] auto command_buffer() const -> vk::CommandBuffer { + return *m_command_buffer; + } + + void submit_and_wait(); + + private: + vk::Device m_device{}; + vk::Queue m_queue{}; + vk::UniqueCommandBuffer m_command_buffer{}; +}; +``` + +The constructor takes an existing command pool created for such ad-hoc allocations, and the queue for submission later. This way it can be passed around after creation and used by other code. + +```cpp +CommandBlock::CommandBlock(vk::Device const device, vk::Queue const queue, + vk::CommandPool const command_pool) + : m_device(device), m_queue(queue) { + // allocate a UniqueCommandBuffer which will free the underlying command + // buffer from its owning pool on destruction. + auto allocate_info = vk::CommandBufferAllocateInfo{}; + allocate_info.setCommandPool(command_pool) + .setCommandBufferCount(1) + .setLevel(vk::CommandBufferLevel::ePrimary); + // all the current VulkanHPP functions for UniqueCommandBuffer allocation + // return vectors. + auto command_buffers = m_device.allocateCommandBuffersUnique(allocate_info); + m_command_buffer = std::move(command_buffers.front()); + + // start recording commands before returning. + auto begin_info = vk::CommandBufferBeginInfo{}; + begin_info.setFlags(vk::CommandBufferUsageFlagBits::eOneTimeSubmit); + m_command_buffer->begin(begin_info); +} +``` + +`submit_and_wait()` resets the unique command buffer at the end, to free it from its command pool: + +```cpp +void CommandBlock::submit_and_wait() { + if (!m_command_buffer) { return; } + + // end recording and submit. + m_command_buffer->end(); + auto submit_info = vk::SubmitInfo2KHR{}; + auto const command_buffer_info = + vk::CommandBufferSubmitInfo{*m_command_buffer}; + submit_info.setCommandBufferInfos(command_buffer_info); + auto fence = m_device.createFenceUnique({}); + m_queue.submit2(submit_info, *fence); + + // wait for submit fence to be signaled. + static constexpr auto timeout_v = + static_cast(std::chrono::nanoseconds(30s).count()); + auto const result = m_device.waitForFences(*fence, vk::True, timeout_v); + if (result != vk::Result::eSuccess) { + std::println(stderr, "Failed to submit Command Buffer"); + } + // free the command buffer. + m_command_buffer.reset(); +} +``` + +## Multithreading considerations + +Instead of blocking the main thread on every Command Block's `submit_and_wait()`, you might be wondering if command block usage could be multithreaded. The answer is yes! But with some extra work: each thread will require its own command pool - just using one owned (unique) pool per Command Block (with no need to free the buffer) is a good starting point. All queue operations need to be synchronized, ie a critical section protected by a mutex. This includes Swapchain acquire/present calls, and Queue submissions. A `class Queue` value type that stores a copy of the `vk::Queue` and a pointer/reference to its `std::mutex` - and wraps the submit call - can be passed to command blocks. Just this much will enable asynchronous asset loading etc, as each loading thread will use its own command pool, and queue submissions all around will be critical sections. `VmaAllocator` is internally synchronized (can be disabled at build time), so performing allocations through the same allocator on multiple threads is safe. + +For multi-threaded rendering, use a Secondary command buffer per thread to record rendering commands, accumulate and execute them in the main (Primary) command buffer currently in `RenderSync`. This is not particularly helpful unless you have thousands of expensive draw calls and dozens of render passes, as recording even a hundred draws will likely be faster on a single thread. diff --git a/guide/src/memory/device_buffers.md b/guide/src/memory/device_buffers.md new file mode 100644 index 0000000..0d248b4 --- /dev/null +++ b/guide/src/memory/device_buffers.md @@ -0,0 +1,139 @@ +# Device Buffers + +This guide will only use device buffers for vertex buffers, where both vertex and index data will be strung together in a single VBO. The create function can thus take the data and perform the buffer copy operation before returning. In essence this return value is a "GPU const" buffer. To enable utilizing separate spans for vertices and indices (instead of forcing allocation of a contiguous bytestream and copying the data), the create function takes a slightly awkward span of spans: + +```cpp +// disparate byte spans. +using ByteSpans = std::span const>; + +// returns a Device Buffer with each byte span sequentially written. +[[nodiscard]] auto create_device_buffer(VmaAllocator allocator, + vk::BufferUsageFlags usage, + CommandBlock command_block, + ByteSpans const& byte_spans) -> Buffer; +``` + +Implement `create_device_buffer()`: + +```cpp +auto vma::create_device_buffer(VmaAllocator allocator, + vk::BufferUsageFlags usage, + CommandBlock command_block, + ByteSpans const& byte_spans) -> Buffer { + auto const total_size = std::accumulate( + byte_spans.begin(), byte_spans.end(), 0uz, + [](std::size_t const n, std::span bytes) { + return n + bytes.size(); + }); + + // create staging Host Buffer with TransferSrc usage. + auto staging_buffer = create_host_buffer( + allocator, vk::BufferUsageFlagBits::eTransferSrc, total_size); + + // create the Device Buffer, ensuring TransferDst usage. + usage |= vk::BufferUsageFlagBits::eTransferDst; + auto allocation_ci = VmaAllocationCreateInfo{}; + allocation_ci.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; + allocation_ci.flags = + VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; + auto ret = create_buffer(allocator, allocation_ci, usage, total_size); + + // can't do anything if either buffer creation failed. + if (!staging_buffer.get().buffer || !ret.get().buffer) { return {}; } + + // copy byte spans into staging buffer. + auto dst = staging_buffer.get().mapped_span(); + for (auto const bytes : byte_spans) { + std::memcpy(dst.data(), bytes.data(), bytes.size()); + dst = dst.subspan(bytes.size()); + } + + // record buffer copy operation. + auto buffer_copy = vk::BufferCopy2{}; + buffer_copy.setSize(total_size); + auto copy_buffer_info = vk::CopyBufferInfo2{}; + copy_buffer_info.setSrcBuffer(staging_buffer.get().buffer) + .setDstBuffer(ret.get().buffer) + .setRegions(buffer_copy); + command_block.command_buffer().copyBuffer2(copy_buffer_info); + + // submit and wait. + // waiting here is necessary to keep the staging buffer alive while the GPU + // accesses it through the recorded commands. + // this is also why the function takes ownership of the passed CommandBlock + // instead of just referencing it / taking a vk::CommandBuffer. + command_block.submit_and_wait(); + + return ret; +} +``` + +Add a command block pool to `App`, and a helper function to create command blocks: + +```cpp +void App::create_cmd_block_pool() { + auto command_pool_ci = vk::CommandPoolCreateInfo{}; + command_pool_ci + .setQueueFamilyIndex(m_gpu.queue_family) + // this flag indicates that the allocated Command Buffers will be + // short-lived. + .setFlags(vk::CommandPoolCreateFlagBits::eTransient); + m_cmd_block_pool = m_device->createCommandPoolUnique(command_pool_ci); +} + +auto App::create_command_block() const -> CommandBlock { + return CommandBlock{*m_device, m_queue, *m_cmd_block_pool}; +} +``` + +Update `create_vertex_buffer()` to create a quad with indices: + +```cpp +template +[[nodiscard]] constexpr auto to_byte_array(T const& t) { + return std::bit_cast>(t); +} + +// ... +void App::create_vertex_buffer() { + // vertices of a quad. + static constexpr auto vertices_v = std::array{ + Vertex{.position = {-0.5f, -0.5f}, .color = {1.0f, 0.0f, 0.0f}}, + Vertex{.position = {0.5f, -0.5f}, .color = {0.0f, 1.0f, 0.0f}}, + Vertex{.position = {0.5f, 0.5f}, .color = {0.0f, 0.0f, 1.0f}}, + Vertex{.position = {-0.5f, 0.5f}, .color = {1.0f, 1.0f, 0.0f}}, + }; + static constexpr auto indices_v = std::array{ + 0u, 1u, 2u, 2u, 3u, 0u, + }; + static constexpr auto vertices_bytes_v = to_byte_array(vertices_v); + static constexpr auto indices_bytes_v = to_byte_array(indices_v); + static constexpr auto total_bytes_v = + std::array, 2>{ + vertices_bytes_v, + indices_bytes_v, + }; + // we want to write total_bytes_v to a Device VertexBuffer | IndexBuffer. + m_vbo = vma::create_device_buffer(m_allocator.get(), + vk::BufferUsageFlagBits::eVertexBuffer | + vk::BufferUsageFlagBits::eIndexBuffer, + create_command_block(), total_bytes_v); +} +``` + +Update `draw()`: + +```cpp +void App::draw(vk::CommandBuffer const command_buffer) const { + m_shader->bind(command_buffer, m_framebuffer_size); + // single VBO at binding 0 at no offset. + command_buffer.bindVertexBuffers(0, m_vbo.get().buffer, vk::DeviceSize{}); + // u32 indices after offset of 4 vertices. + command_buffer.bindIndexBuffer(m_vbo.get().buffer, 4 * sizeof(Vertex), + vk::IndexType::eUint32); + // m_vbo has 6 indices. + command_buffer.drawIndexed(6, 1, 0, 0, 0); +} +``` + +![VBO Quad](./vbo_quad.png) diff --git a/guide/src/memory/ibo_quad.png b/guide/src/memory/vbo_quad.png similarity index 100% rename from guide/src/memory/ibo_quad.png rename to guide/src/memory/vbo_quad.png diff --git a/guide/src/memory/host_vertex_buffer.md b/guide/src/memory/vertex_buffer.md similarity index 93% rename from guide/src/memory/host_vertex_buffer.md rename to guide/src/memory/vertex_buffer.md index 4b0e1cf..0efe3a2 100644 --- a/guide/src/memory/host_vertex_buffer.md +++ b/guide/src/memory/vertex_buffer.md @@ -1,6 +1,6 @@ -# Host Vertex Buffer +# Vertex Buffer -The goal here is to move the hard-coded vertices in the shader to application code. For the time being we will use an ad-hoc Host type `vma::Buffer` and focus more on the rest of the infrastructure like vertex attributes. +The goal here is to move the hard-coded vertices in the shader to application code. For the time being we will use an ad-hoc Host `vma::Buffer` and focus more on the rest of the infrastructure like vertex attributes. First add a new header, `vertex.hpp`: @@ -97,6 +97,6 @@ command_buffer.bindVertexBuffers(0, m_vbo->get_raw().buffer, command_buffer.draw(3, 1, 0, 0); ``` -You should see the same triangle as before. But now we can use whatever set of vertices we like! The Primitive Topology is Triange List by default, so every three vertices in the array is drawn as a triangle, eg for 9 vertices: `[[0, 1, 2], [3, 4, 5], [6, 7, 8]]`, where each inner `[]` represents a triangle comprised of the vertices at those indices. +You should see the same triangle as before. But now we can use whatever set of vertices we like! The Primitive Topology is Triange List by default, so every three vertices in the array is drawn as a triangle, eg for 9 vertices: `[[0, 1, 2], [3, 4, 5], [6, 7, 8]]`, where each inner `[]` represents a triangle comprised of the vertices at those indices. Try playing around with customized vertices and topologies, use Render Doc to debug unexpected outputs / bugs. Host Vertex Buffers are useful for primitives that are temporary and/or frequently changing, such as UI objects. A 2D framework can use such VBOs exclusively: a simple approach would be a pool of buffers per virtual frame where for each draw a buffer is obtained from the current virtual frame's pool and vertices are copied in. diff --git a/src/app.cpp b/src/app.cpp index 56312d2..64e6e4c 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -12,6 +13,11 @@ namespace lvk { using namespace std::chrono_literals; namespace { +template +[[nodiscard]] constexpr auto to_byte_array(T const& t) { + return std::bit_cast>(t); +} + [[nodiscard]] auto locate_assets_dir() -> fs::path { // look for '/assets/', starting from the working // directory and walking up the parent directory tree. @@ -83,6 +89,7 @@ void App::run() { create_render_sync(); create_imgui(); create_shader(); + create_cmd_block_pool(); create_vertex_buffer(); @@ -254,26 +261,49 @@ void App::create_shader() { m_shader.emplace(shader_ci); } +void App::create_cmd_block_pool() { + auto command_pool_ci = vk::CommandPoolCreateInfo{}; + command_pool_ci + .setQueueFamilyIndex(m_gpu.queue_family) + // this flag indicates that the allocated Command Buffers will be + // short-lived. + .setFlags(vk::CommandPoolCreateFlagBits::eTransient); + m_cmd_block_pool = m_device->createCommandPoolUnique(command_pool_ci); +} + void App::create_vertex_buffer() { - // vertices previously hard-coded in the vertex shader. + // vertices of a quad. static constexpr auto vertices_v = std::array{ Vertex{.position = {-0.5f, -0.5f}, .color = {1.0f, 0.0f, 0.0f}}, Vertex{.position = {0.5f, -0.5f}, .color = {0.0f, 1.0f, 0.0f}}, - Vertex{.position = {0.0f, 0.5f}, .color = {0.0f, 0.0f, 1.0f}}, + Vertex{.position = {0.5f, 0.5f}, .color = {0.0f, 0.0f, 1.0f}}, + Vertex{.position = {-0.5f, 0.5f}, .color = {1.0f, 1.0f, 0.0f}}, }; - // we want to write vertices_v to a Host VertexBuffer. - m_vbo = vma::create_host_buffer(m_allocator.get(), - vk::BufferUsageFlagBits::eVertexBuffer, - sizeof(vertices_v)); - - // host buffers have a memory-mapped pointer available to memcpy data to. - std::memcpy(m_vbo.get().mapped, vertices_v.data(), sizeof(vertices_v)); + static constexpr auto indices_v = std::array{ + 0u, 1u, 2u, 2u, 3u, 0u, + }; + static constexpr auto vertices_bytes_v = to_byte_array(vertices_v); + static constexpr auto indices_bytes_v = to_byte_array(indices_v); + static constexpr auto total_bytes_v = + std::array, 2>{ + vertices_bytes_v, + indices_bytes_v, + }; + // we want to write total_bytes_v to a Device VertexBuffer | IndexBuffer. + m_vbo = vma::create_device_buffer(m_allocator.get(), + vk::BufferUsageFlagBits::eVertexBuffer | + vk::BufferUsageFlagBits::eIndexBuffer, + create_command_block(), total_bytes_v); } auto App::asset_path(std::string_view const uri) const -> fs::path { return m_assets_dir / uri; } +auto App::create_command_block() const -> CommandBlock { + return CommandBlock{*m_device, m_queue, *m_cmd_block_pool}; +} + void App::main_loop() { while (glfwWindowShouldClose(m_window.get()) == GLFW_FALSE) { glfwPollEvents(); @@ -450,7 +480,10 @@ void App::draw(vk::CommandBuffer const command_buffer) const { m_shader->bind(command_buffer, m_framebuffer_size); // single VBO at binding 0 at no offset. command_buffer.bindVertexBuffers(0, m_vbo.get().buffer, vk::DeviceSize{}); - // m_vbo has 3 vertices. - command_buffer.draw(3, 1, 0, 0); + // u32 indices after offset of 4 vertices. + command_buffer.bindIndexBuffer(m_vbo.get().buffer, 4 * sizeof(Vertex), + vk::IndexType::eUint32); + // m_vbo has 6 indices. + command_buffer.drawIndexed(6, 1, 0, 0, 0); } } // namespace lvk diff --git a/src/app.hpp b/src/app.hpp index 0426738..5bd094d 100644 --- a/src/app.hpp +++ b/src/app.hpp @@ -1,4 +1,5 @@ #pragma once +#include #include #include #include @@ -38,9 +39,11 @@ class App { void create_imgui(); void create_allocator(); void create_shader(); + void create_cmd_block_pool(); void create_vertex_buffer(); [[nodiscard]] auto asset_path(std::string_view uri) const -> fs::path; + [[nodiscard]] auto create_command_block() const -> CommandBlock; void main_loop(); @@ -70,6 +73,8 @@ class App { std::optional m_swapchain{}; // command pool for all render Command Buffers. vk::UniqueCommandPool m_render_cmd_pool{}; + // command pool for all Command Blocks. + vk::UniqueCommandPool m_cmd_block_pool{}; // Sync and Command Buffer for virtual frames. Buffered m_render_sync{}; // Current virtual frame index. diff --git a/src/command_block.cpp b/src/command_block.cpp new file mode 100644 index 0000000..1a1258e --- /dev/null +++ b/src/command_block.cpp @@ -0,0 +1,50 @@ +#include +#include +#include + +namespace lvk { +using namespace std::chrono_literals; + +CommandBlock::CommandBlock(vk::Device const device, vk::Queue const queue, + vk::CommandPool const command_pool) + : m_device(device), m_queue(queue) { + // allocate a UniqueCommandBuffer which will free the underlying command + // buffer from its owning pool on destruction. + auto allocate_info = vk::CommandBufferAllocateInfo{}; + allocate_info.setCommandPool(command_pool) + .setCommandBufferCount(1) + .setLevel(vk::CommandBufferLevel::ePrimary); + // all the current VulkanHPP functions for UniqueCommandBuffer allocation + // return vectors. + auto command_buffers = m_device.allocateCommandBuffersUnique(allocate_info); + m_command_buffer = std::move(command_buffers.front()); + + // start recording commands before returning. + auto begin_info = vk::CommandBufferBeginInfo{}; + begin_info.setFlags(vk::CommandBufferUsageFlagBits::eOneTimeSubmit); + m_command_buffer->begin(begin_info); +} + +void CommandBlock::submit_and_wait() { + if (!m_command_buffer) { return; } + + // end recording and submit. + m_command_buffer->end(); + auto submit_info = vk::SubmitInfo2KHR{}; + auto const command_buffer_info = + vk::CommandBufferSubmitInfo{*m_command_buffer}; + submit_info.setCommandBufferInfos(command_buffer_info); + auto fence = m_device.createFenceUnique({}); + m_queue.submit2(submit_info, *fence); + + // wait for submit fence to be signaled. + static constexpr auto timeout_v = + static_cast(std::chrono::nanoseconds(30s).count()); + auto const result = m_device.waitForFences(*fence, vk::True, timeout_v); + if (result != vk::Result::eSuccess) { + std::println(stderr, "Failed to submit Command Buffer"); + } + // free the command buffer. + m_command_buffer.reset(); +} +} // namespace lvk diff --git a/src/command_block.hpp b/src/command_block.hpp new file mode 100644 index 0000000..a5719c8 --- /dev/null +++ b/src/command_block.hpp @@ -0,0 +1,21 @@ +#pragma once +#include + +namespace lvk { +class CommandBlock { + public: + explicit CommandBlock(vk::Device device, vk::Queue queue, + vk::CommandPool command_pool); + + [[nodiscard]] auto command_buffer() const -> vk::CommandBuffer { + return *m_command_buffer; + } + + void submit_and_wait(); + + private: + vk::Device m_device{}; + vk::Queue m_queue{}; + vk::UniqueCommandBuffer m_command_buffer{}; +}; +} // namespace lvk diff --git a/src/vma.cpp b/src/vma.cpp index 8841957..9a93ff8 100644 --- a/src/vma.cpp +++ b/src/vma.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -80,4 +81,55 @@ auto vma::create_host_buffer(VmaAllocator allocator, VMA_ALLOCATION_CREATE_MAPPED_BIT; return create_buffer(allocator, allocation_ci, usage, size); } + +auto vma::create_device_buffer(VmaAllocator allocator, + vk::BufferUsageFlags usage, + CommandBlock command_block, + ByteSpans const& byte_spans) -> Buffer { + auto const total_size = std::accumulate( + byte_spans.begin(), byte_spans.end(), 0uz, + [](std::size_t const n, std::span bytes) { + return n + bytes.size(); + }); + + // create staging Host Buffer with TransferSrc usage. + auto staging_buffer = create_host_buffer( + allocator, vk::BufferUsageFlagBits::eTransferSrc, total_size); + + // create the Device Buffer, ensuring TransferDst usage. + usage |= vk::BufferUsageFlagBits::eTransferDst; + auto allocation_ci = VmaAllocationCreateInfo{}; + allocation_ci.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; + allocation_ci.flags = + VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; + auto ret = create_buffer(allocator, allocation_ci, usage, total_size); + + // can't do anything if either buffer creation failed. + if (!staging_buffer.get().buffer || !ret.get().buffer) { return {}; } + + // copy byte spans into staging buffer. + auto dst = staging_buffer.get().mapped_span(); + for (auto const bytes : byte_spans) { + std::memcpy(dst.data(), bytes.data(), bytes.size()); + dst = dst.subspan(bytes.size()); + } + + // record buffer copy operation. + auto buffer_copy = vk::BufferCopy2{}; + buffer_copy.setSize(total_size); + auto copy_buffer_info = vk::CopyBufferInfo2{}; + copy_buffer_info.setSrcBuffer(staging_buffer.get().buffer) + .setDstBuffer(ret.get().buffer) + .setRegions(buffer_copy); + command_block.command_buffer().copyBuffer2(copy_buffer_info); + + // submit and wait. + // waiting here is necessary to keep the staging buffer alive while the GPU + // accesses it through the recorded commands. + // this is also why the function takes ownership of the passed CommandBlock + // instead of just referencing it / taking a vk::CommandBuffer. + command_block.submit_and_wait(); + + return ret; +} } // namespace lvk diff --git a/src/vma.hpp b/src/vma.hpp index 0a5b521..77b1c73 100644 --- a/src/vma.hpp +++ b/src/vma.hpp @@ -1,5 +1,6 @@ #pragma once #include +#include #include #include @@ -37,4 +38,13 @@ using Buffer = Scoped; [[nodiscard]] auto create_host_buffer(VmaAllocator allocator, vk::BufferUsageFlags usage, vk::DeviceSize size) -> Buffer; + +// disparate byte spans. +using ByteSpans = std::span const>; + +// returns a Device Buffer with each byte span sequentially written. +[[nodiscard]] auto create_device_buffer(VmaAllocator allocator, + vk::BufferUsageFlags usage, + CommandBlock command_block, + ByteSpans const& byte_spans) -> Buffer; } // namespace lvk::vma From 19ba8ac39c8eee4d30d2955d23f351adb425a7c4 Mon Sep 17 00:00:00 2001 From: Karn Kaul Date: Sun, 30 Mar 2025 00:43:47 -0700 Subject: [PATCH 07/11] Refactor Buffer API --- guide/src/memory/buffers.md | 65 +++++++------ guide/src/memory/device_buffers.md | 22 ++--- guide/src/memory/vertex_buffer.md | 18 ++-- src/app.cpp | 12 ++- src/vma.cpp | 145 ++++++++++++++++++----------- src/vma.hpp | 45 ++++++++- 6 files changed, 191 insertions(+), 116 deletions(-) diff --git a/guide/src/memory/buffers.md b/guide/src/memory/buffers.md index 149be94..4c74806 100644 --- a/guide/src/memory/buffers.md +++ b/guide/src/memory/buffers.md @@ -28,45 +28,63 @@ void BufferDeleter::operator()(RawBuffer const& raw_buffer) const noexcept { } ``` -Buffers can be backed by host (RAM) or device (VRAM) memory: the former is mappable and thus useful for data that changes every frame, latter is faster to access for the GPU but needs more complex methods to copy data from the CPU to it. Leaving device buffers for later, add the `Buffer` alias and a create function: +Buffers can be backed by host (RAM) or device (VRAM) memory: the former is mappable and thus useful for data that changes every frame, latter is faster to access for the GPU but needs more complex methods to copy data to. Add the related types and a create function: ```cpp -using Buffer = Scoped; +struct BufferCreateInfo { + VmaAllocator allocator; + vk::BufferUsageFlags usage; + std::uint32_t queue_family; +}; -[[nodiscard]] auto create_host_buffer(VmaAllocator allocator, - vk::BufferUsageFlags usage, - vk::DeviceSize size) -> Buffer; -``` +enum class BufferMemoryType : std::int8_t { Host, Device }; -Add a helper function that can be reused for device buffers too later: +[[nodiscard]] auto create_buffer(BufferCreateInfo const& create_info, + BufferMemoryType memory_type, + vk::DeviceSize size) -> Buffer; -```cpp -[[nodiscard]] auto create_buffer(VmaAllocator allocator, - VmaAllocationCreateInfo const& allocation_ci, - vk::BufferUsageFlags const usage, - vk::DeviceSize const size) -> Buffer { +// ... +auto vma::create_buffer(BufferCreateInfo const& create_info, + BufferMemoryType const memory_type, + vk::DeviceSize const size) -> Buffer { if (size == 0) { std::println(stderr, "Buffer cannot be 0-sized"); return {}; } + auto allocation_ci = VmaAllocationCreateInfo{}; + allocation_ci.flags = + VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; + auto usage = create_info.usage; + if (memory_type == BufferMemoryType::Device) { + allocation_ci.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; + // device buffers need to support TransferDst. + usage |= vk::BufferUsageFlagBits::eTransferDst; + } else { + allocation_ci.usage = VMA_MEMORY_USAGE_AUTO_PREFER_HOST; + // host buffers can provide mapped memory. + allocation_ci.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT; + } + auto buffer_ci = vk::BufferCreateInfo{}; - buffer_ci.setSize(size).setUsage(usage); + buffer_ci.setQueueFamilyIndices(create_info.queue_family) + .setSize(size) + .setUsage(usage); auto vma_buffer_ci = static_cast(buffer_ci); VmaAllocation allocation{}; VkBuffer buffer{}; auto allocation_info = VmaAllocationInfo{}; auto const result = - vmaCreateBuffer(allocator, &vma_buffer_ci, &allocation_ci, &buffer, - &allocation, &allocation_info); + vmaCreateBuffer(create_info.allocator, &vma_buffer_ci, &allocation_ci, + &buffer, &allocation, &allocation_info); if (result != VK_SUCCESS) { std::println(stderr, "Failed to create VMA Buffer"); return {}; } return RawBuffer{ - .allocator = allocator, + .allocator = create_info.allocator, .allocation = allocation, .buffer = buffer, .size = size, @@ -74,18 +92,3 @@ Add a helper function that can be reused for device buffers too later: }; } ``` - -Implement `create_host_buffer()`: - -```cpp -auto vma::create_host_buffer(VmaAllocator allocator, - vk::BufferUsageFlags const usage, - vk::DeviceSize const size) -> Buffer { - auto allocation_ci = VmaAllocationCreateInfo{}; - allocation_ci.usage = VMA_MEMORY_USAGE_AUTO_PREFER_HOST; - allocation_ci.flags = - VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | - VMA_ALLOCATION_CREATE_MAPPED_BIT; - return create_buffer(allocator, allocation_ci, usage, size); -} -``` diff --git a/guide/src/memory/device_buffers.md b/guide/src/memory/device_buffers.md index 0d248b4..1a72369 100644 --- a/guide/src/memory/device_buffers.md +++ b/guide/src/memory/device_buffers.md @@ -7,8 +7,7 @@ This guide will only use device buffers for vertex buffers, where both vertex an using ByteSpans = std::span const>; // returns a Device Buffer with each byte span sequentially written. -[[nodiscard]] auto create_device_buffer(VmaAllocator allocator, - vk::BufferUsageFlags usage, +[[nodiscard]] auto create_device_buffer(BufferCreateInfo const& create_info, CommandBlock command_block, ByteSpans const& byte_spans) -> Buffer; ``` @@ -16,8 +15,7 @@ using ByteSpans = std::span const>; Implement `create_device_buffer()`: ```cpp -auto vma::create_device_buffer(VmaAllocator allocator, - vk::BufferUsageFlags usage, +auto vma::create_device_buffer(BufferCreateInfo const& create_info, CommandBlock command_block, ByteSpans const& byte_spans) -> Buffer { auto const total_size = std::accumulate( @@ -26,18 +24,14 @@ auto vma::create_device_buffer(VmaAllocator allocator, return n + bytes.size(); }); - // create staging Host Buffer with TransferSrc usage. - auto staging_buffer = create_host_buffer( - allocator, vk::BufferUsageFlagBits::eTransferSrc, total_size); + auto staging_ci = create_info; + staging_ci.usage = vk::BufferUsageFlagBits::eTransferSrc; + // create staging Host Buffer with TransferSrc usage. + auto staging_buffer = + create_buffer(create_info, BufferMemoryType::Host, total_size); // create the Device Buffer, ensuring TransferDst usage. - usage |= vk::BufferUsageFlagBits::eTransferDst; - auto allocation_ci = VmaAllocationCreateInfo{}; - allocation_ci.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; - allocation_ci.flags = - VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; - auto ret = create_buffer(allocator, allocation_ci, usage, total_size); - + auto ret = create_buffer(create_info, BufferMemoryType::Device, total_size); // can't do anything if either buffer creation failed. if (!staging_buffer.get().buffer || !ret.get().buffer) { return {}; } diff --git a/guide/src/memory/vertex_buffer.md b/guide/src/memory/vertex_buffer.md index 0efe3a2..703258b 100644 --- a/guide/src/memory/vertex_buffer.md +++ b/guide/src/memory/vertex_buffer.md @@ -68,20 +68,22 @@ Add a VBO (Vertex Buffer Object) member and create it: ```cpp void App::create_vertex_buffer() { - // we want to write 4x Vertex objects to a Host VertexBuffer. - auto const buffer_ci = vma::BufferCreateInfo{ - .usage = vk::BufferUsageFlagBits::eVertexBuffer, - .size = 4 * sizeof(Vertex), - .type = vma::BufferType::Host, - }; - m_vbo = vma::create_buffer(m_allocator.get(), buffer_ci); - // vertices moved from the shader. static constexpr auto vertices_v = std::array{ Vertex{.position = {-0.5f, -0.5f}, .color = {1.0f, 0.0f, 0.0f}}, Vertex{.position = {0.5f, -0.5f}, .color = {0.0f, 1.0f, 0.0f}}, Vertex{.position = {0.0f, 0.5f}, .color = {0.0f, 0.0f, 1.0f}}, }; + + // we want to write vertices_v to a Host VertexBuffer. + auto const buffer_ci = vma::BufferCreateInfo{ + .allocator = m_allocator.get(), + .usage = vk::BufferUsageFlagBits::eVertexBuffer, + .queue_family = m_gpu.queue_family, + }; + m_vbo = vma::create_buffer(buffer_ci, vma::BufferMemoryType::Host, + sizeof(vertices_v)); + // host buffers have a memory-mapped pointer available to memcpy data to. std::memcpy(m_vbo.get().mapped, vertices_v.data(), sizeof(vertices_v)); } diff --git a/src/app.cpp b/src/app.cpp index 64e6e4c..3f5839e 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -290,10 +290,14 @@ void App::create_vertex_buffer() { indices_bytes_v, }; // we want to write total_bytes_v to a Device VertexBuffer | IndexBuffer. - m_vbo = vma::create_device_buffer(m_allocator.get(), - vk::BufferUsageFlagBits::eVertexBuffer | - vk::BufferUsageFlagBits::eIndexBuffer, - create_command_block(), total_bytes_v); + auto const buffer_ci = vma::BufferCreateInfo{ + .allocator = m_allocator.get(), + .usage = vk::BufferUsageFlagBits::eVertexBuffer | + vk::BufferUsageFlagBits::eIndexBuffer, + .queue_family = m_gpu.queue_family, + }; + m_vbo = vma::create_device_buffer(buffer_ci, create_command_block(), + total_bytes_v); } auto App::asset_path(std::string_view const uri) const -> fs::path { diff --git a/src/vma.cpp b/src/vma.cpp index 9a93ff8..42b6fb6 100644 --- a/src/vma.cpp +++ b/src/vma.cpp @@ -5,41 +5,6 @@ namespace lvk { namespace vma { -namespace { -[[nodiscard]] auto create_buffer(VmaAllocator allocator, - VmaAllocationCreateInfo const& allocation_ci, - vk::BufferUsageFlags const usage, - vk::DeviceSize const size) -> Buffer { - if (size == 0) { - std::println(stderr, "Buffer cannot be 0-sized"); - return {}; - } - - auto buffer_ci = vk::BufferCreateInfo{}; - buffer_ci.setSize(size).setUsage(usage); - auto vma_buffer_ci = static_cast(buffer_ci); - - VmaAllocation allocation{}; - VkBuffer buffer{}; - auto allocation_info = VmaAllocationInfo{}; - auto const result = - vmaCreateBuffer(allocator, &vma_buffer_ci, &allocation_ci, &buffer, - &allocation, &allocation_info); - if (result != VK_SUCCESS) { - std::println(stderr, "Failed to create VMA Buffer"); - return {}; - } - - return RawBuffer{ - .allocator = allocator, - .allocation = allocation, - .buffer = buffer, - .size = size, - .mapped = allocation_info.pMappedData, - }; -} -} // namespace - void Deleter::operator()(VmaAllocator allocator) const noexcept { vmaDestroyAllocator(allocator); } @@ -71,19 +36,55 @@ auto vma::create_allocator(vk::Instance const instance, throw std::runtime_error{"Failed to create Vulkan Memory Allocator"}; } -auto vma::create_host_buffer(VmaAllocator allocator, - vk::BufferUsageFlags const usage, - vk::DeviceSize const size) -> Buffer { +auto vma::create_buffer(BufferCreateInfo const& create_info, + BufferMemoryType const memory_type, + vk::DeviceSize const size) -> Buffer { + if (size == 0) { + std::println(stderr, "Buffer cannot be 0-sized"); + return {}; + } + auto allocation_ci = VmaAllocationCreateInfo{}; - allocation_ci.usage = VMA_MEMORY_USAGE_AUTO_PREFER_HOST; allocation_ci.flags = - VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | - VMA_ALLOCATION_CREATE_MAPPED_BIT; - return create_buffer(allocator, allocation_ci, usage, size); + VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; + auto usage = create_info.usage; + if (memory_type == BufferMemoryType::Device) { + allocation_ci.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; + // device buffers need to support TransferDst. + usage |= vk::BufferUsageFlagBits::eTransferDst; + } else { + allocation_ci.usage = VMA_MEMORY_USAGE_AUTO_PREFER_HOST; + // host buffers can provide mapped memory. + allocation_ci.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT; + } + + auto buffer_ci = vk::BufferCreateInfo{}; + buffer_ci.setQueueFamilyIndices(create_info.queue_family) + .setSize(size) + .setUsage(usage); + auto vma_buffer_ci = static_cast(buffer_ci); + + VmaAllocation allocation{}; + VkBuffer buffer{}; + auto allocation_info = VmaAllocationInfo{}; + auto const result = + vmaCreateBuffer(create_info.allocator, &vma_buffer_ci, &allocation_ci, + &buffer, &allocation, &allocation_info); + if (result != VK_SUCCESS) { + std::println(stderr, "Failed to create VMA Buffer"); + return {}; + } + + return RawBuffer{ + .allocator = create_info.allocator, + .allocation = allocation, + .buffer = buffer, + .size = size, + .mapped = allocation_info.pMappedData, + }; } -auto vma::create_device_buffer(VmaAllocator allocator, - vk::BufferUsageFlags usage, +auto vma::create_device_buffer(BufferCreateInfo const& create_info, CommandBlock command_block, ByteSpans const& byte_spans) -> Buffer { auto const total_size = std::accumulate( @@ -92,18 +93,14 @@ auto vma::create_device_buffer(VmaAllocator allocator, return n + bytes.size(); }); - // create staging Host Buffer with TransferSrc usage. - auto staging_buffer = create_host_buffer( - allocator, vk::BufferUsageFlagBits::eTransferSrc, total_size); + auto staging_ci = create_info; + staging_ci.usage = vk::BufferUsageFlagBits::eTransferSrc; + // create staging Host Buffer with TransferSrc usage. + auto staging_buffer = + create_buffer(create_info, BufferMemoryType::Host, total_size); // create the Device Buffer, ensuring TransferDst usage. - usage |= vk::BufferUsageFlagBits::eTransferDst; - auto allocation_ci = VmaAllocationCreateInfo{}; - allocation_ci.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; - allocation_ci.flags = - VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; - auto ret = create_buffer(allocator, allocation_ci, usage, total_size); - + auto ret = create_buffer(create_info, BufferMemoryType::Device, total_size); // can't do anything if either buffer creation failed. if (!staging_buffer.get().buffer || !ret.get().buffer) { return {}; } @@ -132,4 +129,44 @@ auto vma::create_device_buffer(VmaAllocator allocator, return ret; } + +auto vma::create_image(VmaAllocator allocator, + ImageCreateInfo const& create_info) -> Image { + if (create_info.extent.width == 0 || create_info.extent.height == 0) { + std::println(stderr, "Images cannot have 0 width or height"); + return {}; + } + auto image_ci = vk::ImageCreateInfo{}; + image_ci.setImageType(vk::ImageType::e2D) + .setExtent({create_info.extent.width, create_info.extent.height, 1}) + .setFormat(create_info.format) + .setUsage(create_info.usage) + .setArrayLayers(1) + .setMipLevels(create_info.levels) + .setSamples(vk::SampleCountFlagBits::e1) + .setTiling(vk::ImageTiling::eOptimal) + .setInitialLayout(vk::ImageLayout::eUndefined) + .setQueueFamilyIndices(create_info.queue_family); + auto const vk_image_ci = static_cast(image_ci); + + auto allocation_ci = VmaAllocationCreateInfo{}; + allocation_ci.usage = VMA_MEMORY_USAGE_AUTO; + VkImage image{}; + VmaAllocation allocation{}; + auto const result = vmaCreateImage(allocator, &vk_image_ci, &allocation_ci, + &image, &allocation, {}); + if (result != VK_SUCCESS) { + std::println(stderr, "Failed to create VMA Image"); + return {}; + } + + return RawImage{ + .allocator = allocator, + .allocation = allocation, + .image = image, + .extent = create_info.extent, + .format = create_info.format, + .levels = create_info.levels, + }; +} } // namespace lvk diff --git a/src/vma.hpp b/src/vma.hpp index 77b1c73..9060383 100644 --- a/src/vma.hpp +++ b/src/vma.hpp @@ -35,16 +35,51 @@ struct BufferDeleter { using Buffer = Scoped; -[[nodiscard]] auto create_host_buffer(VmaAllocator allocator, - vk::BufferUsageFlags usage, - vk::DeviceSize size) -> Buffer; +struct BufferCreateInfo { + VmaAllocator allocator; + vk::BufferUsageFlags usage; + std::uint32_t queue_family; +}; + +enum class BufferMemoryType : std::int8_t { Host, Device }; + +[[nodiscard]] auto create_buffer(BufferCreateInfo const& create_info, + BufferMemoryType memory_type, + vk::DeviceSize size) -> Buffer; // disparate byte spans. using ByteSpans = std::span const>; // returns a Device Buffer with each byte span sequentially written. -[[nodiscard]] auto create_device_buffer(VmaAllocator allocator, - vk::BufferUsageFlags usage, +[[nodiscard]] auto create_device_buffer(BufferCreateInfo const& create_info, CommandBlock command_block, ByteSpans const& byte_spans) -> Buffer; + +struct RawImage { + auto operator==(RawImage const& rhs) const -> bool = default; + + VmaAllocator allocator{}; + VmaAllocation allocation{}; + vk::Image image{}; + vk::Extent2D extent{}; + vk::Format format{}; + std::uint32_t levels{}; +}; + +struct ImageDeleter { + void operator()(RawImage const& raw_image) const noexcept; +}; + +using Image = Scoped; + +struct ImageCreateInfo { + std::uint32_t queue_family; + vk::ImageUsageFlags usage; + vk::Format format; + vk::Extent2D extent; + std::uint32_t levels; +}; + +[[nodiscard]] auto create_image(VmaAllocator allocator, + ImageCreateInfo const& create_info) -> Image; } // namespace lvk::vma From cbd31e5d63efbb1955f256eb2f864add6e5b687d Mon Sep 17 00:00:00 2001 From: Karn Kaul Date: Sun, 30 Mar 2025 09:12:42 -0700 Subject: [PATCH 08/11] WIP images --- guide/src/SUMMARY.md | 1 + guide/src/memory/images.md | 83 ++++++++++++++++++++++++++++++++++++++ src/vma.cpp | 32 +++++++++------ src/vma.hpp | 11 +++-- 4 files changed, 108 insertions(+), 19 deletions(-) create mode 100644 guide/src/memory/images.md diff --git a/guide/src/SUMMARY.md b/guide/src/SUMMARY.md index 7f6f044..a465269 100644 --- a/guide/src/SUMMARY.md +++ b/guide/src/SUMMARY.md @@ -42,3 +42,4 @@ - [Vertex Buffer](memory/vertex_buffer.md) - [Command Block](memory/command_block.md) - [Device Buffers](memory/device_buffers.md) + - [Images](memory/images.md) diff --git a/guide/src/memory/images.md b/guide/src/memory/images.md new file mode 100644 index 0000000..6db64f1 --- /dev/null +++ b/guide/src/memory/images.md @@ -0,0 +1,83 @@ +# Images + +Images have a lot more properties and creation parameters than buffers. We shall constrain ourselves to just two kinds: sampled images (textures) for shaders, and depth images for rendering. For now add the foundation types and functions: + +```cpp +struct RawImage { + auto operator==(RawImage const& rhs) const -> bool = default; + + VmaAllocator allocator{}; + VmaAllocation allocation{}; + vk::Image image{}; + vk::Extent2D extent{}; + vk::Format format{}; + std::uint32_t levels{}; +}; + +struct ImageDeleter { + void operator()(RawImage const& raw_image) const noexcept; +}; + +using Image = Scoped; + +struct ImageCreateInfo { + VmaAllocator allocator; + std::uint32_t queue_family; +}; + +[[nodiscard]] auto create_image(ImageCreateInfo const& create_info, + vk::ImageUsageFlags usage, std::uint32_t levels, + vk::Format format, vk::Extent2D extent) + -> Image; +``` + +Implementation: + +```cpp +void ImageDeleter::operator()(RawImage const& raw_image) const noexcept { + vmaDestroyImage(raw_image.allocator, raw_image.image, raw_image.allocation); +} + +// ... +auto vma::create_image(ImageCreateInfo const& create_info, + vk::ImageUsageFlags const usage, + std::uint32_t const levels, vk::Format const format, + vk::Extent2D const extent) -> Image { + if (extent.width == 0 || extent.height == 0) { + std::println(stderr, "Images cannot have 0 width or height"); + return {}; + } + auto image_ci = vk::ImageCreateInfo{}; + image_ci.setImageType(vk::ImageType::e2D) + .setExtent({extent.width, extent.height, 1}) + .setFormat(format) + .setUsage(usage) + .setArrayLayers(1) + .setMipLevels(levels) + .setSamples(vk::SampleCountFlagBits::e1) + .setTiling(vk::ImageTiling::eOptimal) + .setInitialLayout(vk::ImageLayout::eUndefined) + .setQueueFamilyIndices(create_info.queue_family); + auto const vk_image_ci = static_cast(image_ci); + + auto allocation_ci = VmaAllocationCreateInfo{}; + allocation_ci.usage = VMA_MEMORY_USAGE_AUTO; + VkImage image{}; + VmaAllocation allocation{}; + auto const result = vmaCreateImage(create_info.allocator, &vk_image_ci, + &allocation_ci, &image, &allocation, {}); + if (result != VK_SUCCESS) { + std::println(stderr, "Failed to create VMA Image"); + return {}; + } + + return RawImage{ + .allocator = create_info.allocator, + .allocation = allocation, + .image = image, + .extent = extent, + .format = format, + .levels = levels, + }; +} +``` diff --git a/src/vma.cpp b/src/vma.cpp index 42b6fb6..cc0016f 100644 --- a/src/vma.cpp +++ b/src/vma.cpp @@ -13,6 +13,10 @@ void BufferDeleter::operator()(RawBuffer const& raw_buffer) const noexcept { vmaDestroyBuffer(raw_buffer.allocator, raw_buffer.buffer, raw_buffer.allocation); } + +void ImageDeleter::operator()(RawImage const& raw_image) const noexcept { + vmaDestroyImage(raw_image.allocator, raw_image.image, raw_image.allocation); +} } // namespace vma auto vma::create_allocator(vk::Instance const instance, @@ -130,19 +134,21 @@ auto vma::create_device_buffer(BufferCreateInfo const& create_info, return ret; } -auto vma::create_image(VmaAllocator allocator, - ImageCreateInfo const& create_info) -> Image { - if (create_info.extent.width == 0 || create_info.extent.height == 0) { +auto vma::create_image(ImageCreateInfo const& create_info, + vk::ImageUsageFlags const usage, + std::uint32_t const levels, vk::Format const format, + vk::Extent2D const extent) -> Image { + if (extent.width == 0 || extent.height == 0) { std::println(stderr, "Images cannot have 0 width or height"); return {}; } auto image_ci = vk::ImageCreateInfo{}; image_ci.setImageType(vk::ImageType::e2D) - .setExtent({create_info.extent.width, create_info.extent.height, 1}) - .setFormat(create_info.format) - .setUsage(create_info.usage) + .setExtent({extent.width, extent.height, 1}) + .setFormat(format) + .setUsage(usage) .setArrayLayers(1) - .setMipLevels(create_info.levels) + .setMipLevels(levels) .setSamples(vk::SampleCountFlagBits::e1) .setTiling(vk::ImageTiling::eOptimal) .setInitialLayout(vk::ImageLayout::eUndefined) @@ -153,20 +159,20 @@ auto vma::create_image(VmaAllocator allocator, allocation_ci.usage = VMA_MEMORY_USAGE_AUTO; VkImage image{}; VmaAllocation allocation{}; - auto const result = vmaCreateImage(allocator, &vk_image_ci, &allocation_ci, - &image, &allocation, {}); + auto const result = vmaCreateImage(create_info.allocator, &vk_image_ci, + &allocation_ci, &image, &allocation, {}); if (result != VK_SUCCESS) { std::println(stderr, "Failed to create VMA Image"); return {}; } return RawImage{ - .allocator = allocator, + .allocator = create_info.allocator, .allocation = allocation, .image = image, - .extent = create_info.extent, - .format = create_info.format, - .levels = create_info.levels, + .extent = extent, + .format = format, + .levels = levels, }; } } // namespace lvk diff --git a/src/vma.hpp b/src/vma.hpp index 9060383..2a3e025 100644 --- a/src/vma.hpp +++ b/src/vma.hpp @@ -73,13 +73,12 @@ struct ImageDeleter { using Image = Scoped; struct ImageCreateInfo { + VmaAllocator allocator; std::uint32_t queue_family; - vk::ImageUsageFlags usage; - vk::Format format; - vk::Extent2D extent; - std::uint32_t levels; }; -[[nodiscard]] auto create_image(VmaAllocator allocator, - ImageCreateInfo const& create_info) -> Image; +[[nodiscard]] auto create_image(ImageCreateInfo const& create_info, + vk::ImageUsageFlags usage, std::uint32_t levels, + vk::Format format, vk::Extent2D extent) + -> Image; } // namespace lvk::vma From 4e2116f7be5d0106658f03d697e5516c7030ffcc Mon Sep 17 00:00:00 2001 From: Karn Kaul Date: Sun, 30 Mar 2025 09:39:56 -0700 Subject: [PATCH 09/11] Bugfixes etc --- guide/src/memory/device_buffers.md | 4 ++-- guide/src/memory/vertex_buffer.md | 2 +- src/vertex.hpp | 2 +- src/vma.cpp | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/guide/src/memory/device_buffers.md b/guide/src/memory/device_buffers.md index 1a72369..804c704 100644 --- a/guide/src/memory/device_buffers.md +++ b/guide/src/memory/device_buffers.md @@ -29,8 +29,8 @@ auto vma::create_device_buffer(BufferCreateInfo const& create_info, // create staging Host Buffer with TransferSrc usage. auto staging_buffer = - create_buffer(create_info, BufferMemoryType::Host, total_size); - // create the Device Buffer, ensuring TransferDst usage. + create_buffer(staging_ci, BufferMemoryType::Host, total_size); + // create the Device Buffer. auto ret = create_buffer(create_info, BufferMemoryType::Device, total_size); // can't do anything if either buffer creation failed. if (!staging_buffer.get().buffer || !ret.get().buffer) { return {}; } diff --git a/guide/src/memory/vertex_buffer.md b/guide/src/memory/vertex_buffer.md index 703258b..75e04d3 100644 --- a/guide/src/memory/vertex_buffer.md +++ b/guide/src/memory/vertex_buffer.md @@ -7,7 +7,7 @@ First add a new header, `vertex.hpp`: ```cpp struct Vertex { glm::vec2 position{}; - glm::vec3 color{}; + glm::vec3 color{1.0f}; }; // two vertex attributes: position at 0, color at 1. diff --git a/src/vertex.hpp b/src/vertex.hpp index 6c0fabb..a8c003c 100644 --- a/src/vertex.hpp +++ b/src/vertex.hpp @@ -6,7 +6,7 @@ namespace lvk { struct Vertex { glm::vec2 position{}; - glm::vec3 color{}; + glm::vec3 color{1.0f}; }; // two vertex attributes: position at 0, color at 1. diff --git a/src/vma.cpp b/src/vma.cpp index cc0016f..4456b21 100644 --- a/src/vma.cpp +++ b/src/vma.cpp @@ -102,8 +102,8 @@ auto vma::create_device_buffer(BufferCreateInfo const& create_info, // create staging Host Buffer with TransferSrc usage. auto staging_buffer = - create_buffer(create_info, BufferMemoryType::Host, total_size); - // create the Device Buffer, ensuring TransferDst usage. + create_buffer(staging_ci, BufferMemoryType::Host, total_size); + // create the Device Buffer. auto ret = create_buffer(create_info, BufferMemoryType::Device, total_size); // can't do anything if either buffer creation failed. if (!staging_buffer.get().buffer || !ret.get().buffer) { return {}; } From 88e6f64705dfeb69e632fe702fb8370803aff7c7 Mon Sep 17 00:00:00 2001 From: Karn Kaul Date: Sun, 30 Mar 2025 16:47:49 -0700 Subject: [PATCH 10/11] Sampled image --- guide/src/memory/images.md | 98 ++++++++++++++++++++++++++++++++++++++ src/bitmap.hpp | 11 +++++ src/vma.cpp | 80 +++++++++++++++++++++++++++++++ src/vma.hpp | 5 ++ 4 files changed, 194 insertions(+) create mode 100644 src/bitmap.hpp diff --git a/guide/src/memory/images.md b/guide/src/memory/images.md index 6db64f1..7ee9d11 100644 --- a/guide/src/memory/images.md +++ b/guide/src/memory/images.md @@ -81,3 +81,101 @@ auto vma::create_image(ImageCreateInfo const& create_info, }; } ``` + +For creating sampled images, we need both the image bytes and size (extent). Wrap that into a struct: + +```cpp +struct Bitmap { + std::span bytes{}; + glm::ivec2 size{}; +}; +``` + +The creation process is similar to device buffers: requiring a staging copy, but it also needs layout transitions. In short: + +1. Create the image and staging buffer +1. Transition the layout from Undefined to TransferDst +1. Record a buffer image copy operation +1. Transition the layout from TransferDst to ShaderReadOnlyOptimal + +```cpp +auto vma::create_sampled_image(ImageCreateInfo const& create_info, + CommandBlock command_block, Bitmap const& bitmap) + -> Image { + // create image. + // no mip-mapping right now: 1 level. + auto const mip_levels = 1u; + auto const usize = glm::uvec2{bitmap.size}; + auto const extent = vk::Extent2D{usize.x, usize.y}; + auto const usage = + vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled; + auto ret = create_image(create_info, usage, mip_levels, + vk::Format::eR8G8B8A8Srgb, extent); + + // create staging buffer. + auto const buffer_ci = BufferCreateInfo{ + .allocator = create_info.allocator, + .usage = vk::BufferUsageFlagBits::eTransferSrc, + .queue_family = create_info.queue_family, + }; + auto const staging_buffer = create_buffer(buffer_ci, BufferMemoryType::Host, + bitmap.bytes.size_bytes()); + + // can't do anything if either creation failed. + if (!ret.get().image || !staging_buffer.get().buffer) { return {}; } + + // copy bytes into staging buffer. + std::memcpy(staging_buffer.get().mapped, bitmap.bytes.data(), + bitmap.bytes.size_bytes()); + + // transition image for transfer. + auto dependency_info = vk::DependencyInfo{}; + auto subresource_range = vk::ImageSubresourceRange{}; + subresource_range.setAspectMask(vk::ImageAspectFlagBits::eColor) + .setLayerCount(1) + .setLevelCount(mip_levels); + auto barrier = vk::ImageMemoryBarrier2{}; + barrier.setImage(ret.get().image) + .setSrcQueueFamilyIndex(create_info.queue_family) + .setDstQueueFamilyIndex(create_info.queue_family) + .setOldLayout(vk::ImageLayout::eUndefined) + .setNewLayout(vk::ImageLayout::eTransferDstOptimal) + .setSubresourceRange(subresource_range) + .setSrcStageMask(vk::PipelineStageFlagBits2::eTopOfPipe) + .setSrcAccessMask(vk::AccessFlagBits2::eNone) + .setDstStageMask(vk::PipelineStageFlagBits2::eTransfer) + .setDstAccessMask(vk::AccessFlagBits2::eMemoryRead | + vk::AccessFlagBits2::eMemoryWrite); + dependency_info.setImageMemoryBarriers(barrier); + command_block.command_buffer().pipelineBarrier2(dependency_info); + + auto buffer_image_copy = vk::BufferImageCopy2{}; + auto subresource_layers = vk::ImageSubresourceLayers{}; + subresource_layers.setAspectMask(vk::ImageAspectFlagBits::eColor) + .setLayerCount(1) + .setLayerCount(mip_levels); + buffer_image_copy.setImageSubresource(subresource_layers) + .setImageExtent(vk::Extent3D{extent.width, extent.height, 1}); + auto copy_info = vk::CopyBufferToImageInfo2{}; + copy_info.setDstImage(ret.get().image) + .setDstImageLayout(vk::ImageLayout::eTransferDstOptimal) + .setSrcBuffer(staging_buffer.get().buffer) + .setRegions(buffer_image_copy); + command_block.command_buffer().copyBufferToImage2(copy_info); + + // transition image for sampling. + barrier.setOldLayout(barrier.newLayout) + .setNewLayout(vk::ImageLayout::eShaderReadOnlyOptimal) + .setSrcStageMask(barrier.dstStageMask) + .setSrcAccessMask(barrier.dstAccessMask) + .setDstStageMask(vk::PipelineStageFlagBits2::eAllGraphics) + .setDstAccessMask(vk::AccessFlagBits2::eMemoryRead | + vk::AccessFlagBits2::eMemoryWrite); + dependency_info.setImageMemoryBarriers(barrier); + command_block.command_buffer().pipelineBarrier2(dependency_info); + + command_block.submit_and_wait(); + + return ret; +} +``` diff --git a/src/bitmap.hpp b/src/bitmap.hpp new file mode 100644 index 0000000..41004a2 --- /dev/null +++ b/src/bitmap.hpp @@ -0,0 +1,11 @@ +#pragma once +#include +#include +#include + +namespace lvk { +struct Bitmap { + std::span bytes{}; + glm::ivec2 size{}; +}; +} // namespace lvk diff --git a/src/vma.cpp b/src/vma.cpp index 4456b21..2123787 100644 --- a/src/vma.cpp +++ b/src/vma.cpp @@ -175,4 +175,84 @@ auto vma::create_image(ImageCreateInfo const& create_info, .levels = levels, }; } + +auto vma::create_sampled_image(ImageCreateInfo const& create_info, + CommandBlock command_block, Bitmap const& bitmap) + -> Image { + // create image. + // no mip-mapping right now: 1 level. + auto const mip_levels = 1u; + auto const usize = glm::uvec2{bitmap.size}; + auto const extent = vk::Extent2D{usize.x, usize.y}; + auto const usage = + vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled; + auto ret = create_image(create_info, usage, mip_levels, + vk::Format::eR8G8B8A8Srgb, extent); + + // create staging buffer. + auto const buffer_ci = BufferCreateInfo{ + .allocator = create_info.allocator, + .usage = vk::BufferUsageFlagBits::eTransferSrc, + .queue_family = create_info.queue_family, + }; + auto const staging_buffer = create_buffer(buffer_ci, BufferMemoryType::Host, + bitmap.bytes.size_bytes()); + + // can't do anything if either creation failed. + if (!ret.get().image || !staging_buffer.get().buffer) { return {}; } + + // copy bytes into staging buffer. + std::memcpy(staging_buffer.get().mapped, bitmap.bytes.data(), + bitmap.bytes.size_bytes()); + + // transition image for transfer. + auto dependency_info = vk::DependencyInfo{}; + auto subresource_range = vk::ImageSubresourceRange{}; + subresource_range.setAspectMask(vk::ImageAspectFlagBits::eColor) + .setLayerCount(1) + .setLevelCount(mip_levels); + auto barrier = vk::ImageMemoryBarrier2{}; + barrier.setImage(ret.get().image) + .setSrcQueueFamilyIndex(create_info.queue_family) + .setDstQueueFamilyIndex(create_info.queue_family) + .setOldLayout(vk::ImageLayout::eUndefined) + .setNewLayout(vk::ImageLayout::eTransferDstOptimal) + .setSubresourceRange(subresource_range) + .setSrcStageMask(vk::PipelineStageFlagBits2::eTopOfPipe) + .setSrcAccessMask(vk::AccessFlagBits2::eNone) + .setDstStageMask(vk::PipelineStageFlagBits2::eTransfer) + .setDstAccessMask(vk::AccessFlagBits2::eMemoryRead | + vk::AccessFlagBits2::eMemoryWrite); + dependency_info.setImageMemoryBarriers(barrier); + command_block.command_buffer().pipelineBarrier2(dependency_info); + + auto buffer_image_copy = vk::BufferImageCopy2{}; + auto subresource_layers = vk::ImageSubresourceLayers{}; + subresource_layers.setAspectMask(vk::ImageAspectFlagBits::eColor) + .setLayerCount(1) + .setLayerCount(mip_levels); + buffer_image_copy.setImageSubresource(subresource_layers) + .setImageExtent(vk::Extent3D{extent.width, extent.height, 1}); + auto copy_info = vk::CopyBufferToImageInfo2{}; + copy_info.setDstImage(ret.get().image) + .setDstImageLayout(vk::ImageLayout::eTransferDstOptimal) + .setSrcBuffer(staging_buffer.get().buffer) + .setRegions(buffer_image_copy); + command_block.command_buffer().copyBufferToImage2(copy_info); + + // transition image for sampling. + barrier.setOldLayout(barrier.newLayout) + .setNewLayout(vk::ImageLayout::eShaderReadOnlyOptimal) + .setSrcStageMask(barrier.dstStageMask) + .setSrcAccessMask(barrier.dstAccessMask) + .setDstStageMask(vk::PipelineStageFlagBits2::eAllGraphics) + .setDstAccessMask(vk::AccessFlagBits2::eMemoryRead | + vk::AccessFlagBits2::eMemoryWrite); + dependency_info.setImageMemoryBarriers(barrier); + command_block.command_buffer().pipelineBarrier2(dependency_info); + + command_block.submit_and_wait(); + + return ret; +} } // namespace lvk diff --git a/src/vma.hpp b/src/vma.hpp index 2a3e025..2ea912e 100644 --- a/src/vma.hpp +++ b/src/vma.hpp @@ -1,5 +1,6 @@ #pragma once #include +#include #include #include #include @@ -81,4 +82,8 @@ struct ImageCreateInfo { vk::ImageUsageFlags usage, std::uint32_t levels, vk::Format format, vk::Extent2D extent) -> Image; + +[[nodiscard]] auto create_sampled_image(ImageCreateInfo const& create_info, + CommandBlock command_block, + Bitmap const& bitmap) -> Image; } // namespace lvk::vma From 1b1fffd4fc5e7e27d3547356814f36ebee4d9ae4 Mon Sep 17 00:00:00 2001 From: Karn Kaul Date: Sun, 30 Mar 2025 16:56:55 -0700 Subject: [PATCH 11/11] Cleanup --- guide/src/memory/command_block.md | 4 ++-- guide/src/memory/images.md | 3 +++ src/vma.cpp | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/guide/src/memory/command_block.md b/guide/src/memory/command_block.md index 26cc25c..6f5812b 100644 --- a/guide/src/memory/command_block.md +++ b/guide/src/memory/command_block.md @@ -9,7 +9,7 @@ The second step requires a command buffer and queue submission (_and_ waiting fo ```cpp class CommandBlock { - public: + public: explicit CommandBlock(vk::Device device, vk::Queue queue, vk::CommandPool command_pool); @@ -19,7 +19,7 @@ class CommandBlock { void submit_and_wait(); - private: + private: vk::Device m_device{}; vk::Queue m_queue{}; vk::UniqueCommandBuffer m_command_buffer{}; diff --git a/guide/src/memory/images.md b/guide/src/memory/images.md index 7ee9d11..583957c 100644 --- a/guide/src/memory/images.md +++ b/guide/src/memory/images.md @@ -149,6 +149,7 @@ auto vma::create_sampled_image(ImageCreateInfo const& create_info, dependency_info.setImageMemoryBarriers(barrier); command_block.command_buffer().pipelineBarrier2(dependency_info); + // record buffer image copy. auto buffer_image_copy = vk::BufferImageCopy2{}; auto subresource_layers = vk::ImageSubresourceLayers{}; subresource_layers.setAspectMask(vk::ImageAspectFlagBits::eColor) @@ -179,3 +180,5 @@ auto vma::create_sampled_image(ImageCreateInfo const& create_info, return ret; } ``` + +Before such images can be used as textures, we need to set up Descriptor Set infrastructure. diff --git a/src/vma.cpp b/src/vma.cpp index 2123787..94d9c87 100644 --- a/src/vma.cpp +++ b/src/vma.cpp @@ -226,6 +226,7 @@ auto vma::create_sampled_image(ImageCreateInfo const& create_info, dependency_info.setImageMemoryBarriers(barrier); command_block.command_buffer().pipelineBarrier2(dependency_info); + // record buffer image copy. auto buffer_image_copy = vk::BufferImageCopy2{}; auto subresource_layers = vk::ImageSubresourceLayers{}; subresource_layers.setAspectMask(vk::ImageAspectFlagBits::eColor)