Skip to content

Commit f9b77b8

Browse files
committed
VMA
1 parent a8d6b8d commit f9b77b8

File tree

10 files changed

+155
-1
lines changed

10 files changed

+155
-1
lines changed

ext/CMakeLists.txt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ target_compile_definitions(glm PUBLIC
2727
message(STATUS "[Vulkan-Headers]")
2828
add_subdirectory(src/Vulkan-Headers)
2929

30+
# add VulkanMemoryAllocator to build tree
31+
message(STATUS "[VulkanMemoryAllocator]")
32+
add_subdirectory(src/VulkanMemoryAllocator)
33+
3034
# setup Dear ImGui library
3135
message(STATUS "[Dear ImGui]")
3236
add_library(imgui)
@@ -55,6 +59,25 @@ target_sources(imgui PRIVATE
5559
src/imgui/backends/imgui_impl_vulkan.h
5660
)
5761

62+
# setup vma library (source file with VMA interface)
63+
message(STATUS "[vma]")
64+
add_library(vma)
65+
add_library(vma::vma ALIAS vma)
66+
target_link_libraries(vma PUBLIC
67+
Vulkan::Headers
68+
GPUOpen::VulkanMemoryAllocator
69+
)
70+
target_compile_definitions(vma PUBLIC
71+
VMA_STATIC_VULKAN_FUNCTIONS=0
72+
VMA_DYNAMIC_VULKAN_FUNCTIONS=1
73+
)
74+
target_sources(vma PRIVATE
75+
vk_mem_alloc.cpp
76+
)
77+
78+
# ignore compiler warnings
79+
target_compile_options(vma PRIVATE -w)
80+
5881
# declare ext library target
5982
add_library(${PROJECT_NAME} INTERFACE)
6083
add_library(learn-vk::ext ALIAS ${PROJECT_NAME})
@@ -63,6 +86,7 @@ add_library(learn-vk::ext ALIAS ${PROJECT_NAME})
6386
target_link_libraries(${PROJECT_NAME} INTERFACE
6487
glm::glm
6588
imgui::imgui
89+
vma::vma
6690
)
6791

6892
# setup preprocessor defines

ext/src.zip

186 KB
Binary file not shown.

ext/vk_mem_alloc.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#define VMA_IMPLEMENTATION
2+
3+
#include <vk_mem_alloc.h>

guide/src/SUMMARY.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,8 @@
3333
- [GLSL to SPIR-V](shader_objects/glsl_to_spir_v.md)
3434
- [Drawing a Triangle](shader_objects/drawing_triangle.md)
3535
- [Graphics Pipelines](shader_objects/pipelines.md)
36+
37+
# Shader Resources
38+
39+
- [Memory Allocation](memory/README.md)
40+
- [Vulkan Memory Allocator](memory/vma.md)

guide/src/memory/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Memory Allocation
2+
3+
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).
4+
5+
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.

guide/src/memory/vma.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# Vulkan Memory Allocator
2+
3+
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:
4+
5+
```cpp
6+
// vk_mem_alloc.cpp
7+
#define VMA_IMPLEMENTATION
8+
9+
#include <vk_mem_alloc.h>
10+
```
11+
12+
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*`:
13+
14+
```cpp
15+
// vma.hpp
16+
namespace lvk::vma {
17+
struct Deleter {
18+
void operator()(VmaAllocator allocator) const noexcept;
19+
};
20+
21+
using Allocator = Scoped<VmaAllocator, Deleter>;
22+
23+
[[nodiscard]] auto create_allocator(vk::Instance instance,
24+
vk::PhysicalDevice physical_device,
25+
vk::Device device) -> Allocator;
26+
} // namespace lvk::vma
27+
28+
// vma.cpp
29+
void vma::Deleter::operator()(VmaAllocator allocator) const noexcept {
30+
vmaDestroyAllocator(allocator);
31+
}
32+
33+
auto vma::create_allocator(vk::Instance const instance,
34+
vk::PhysicalDevice const physical_device,
35+
vk::Device const device) -> Allocator {
36+
auto const& dispatcher = VULKAN_HPP_DEFAULT_DISPATCHER;
37+
// need to zero initialize C structs, unlike VulkanHPP.
38+
auto vma_vk_funcs = VmaVulkanFunctions{};
39+
vma_vk_funcs.vkGetInstanceProcAddr = dispatcher.vkGetInstanceProcAddr;
40+
vma_vk_funcs.vkGetDeviceProcAddr = dispatcher.vkGetDeviceProcAddr;
41+
42+
auto allocator_ci = VmaAllocatorCreateInfo{};
43+
allocator_ci.physicalDevice = physical_device;
44+
allocator_ci.device = device;
45+
allocator_ci.pVulkanFunctions = &vma_vk_funcs;
46+
allocator_ci.instance = instance;
47+
VmaAllocator ret{};
48+
auto const result = vmaCreateAllocator(&allocator_ci, &ret);
49+
if (result == VK_SUCCESS) { return ret; }
50+
51+
throw std::runtime_error{"Failed to create Vulkan Memory Allocator"};
52+
}
53+
```
54+
55+
`App` stores and creates a `vma::Allocator` object:
56+
57+
```cpp
58+
// ...
59+
vma::Allocator m_allocator{}; // anywhere between m_device and m_shader.
60+
61+
// ...
62+
void App::create_allocator() {
63+
m_allocator = vma::create_allocator(*m_instance, m_gpu.device, *m_device);
64+
}
65+
```

src/app.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,10 @@ void App::create_imgui() {
228228
m_imgui.emplace(imgui_ci);
229229
}
230230

231+
void App::create_allocator() {
232+
m_allocator = vma::create_allocator(*m_instance, m_gpu.device, *m_device);
233+
}
234+
231235
void App::create_shader() {
232236
auto const vertex_spirv = to_spir_v(asset_path("shader.vert"));
233237
auto const fragment_spirv = to_spir_v(asset_path("shader.frag"));

src/app.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <scoped_waiter.hpp>
66
#include <shader_program.hpp>
77
#include <swapchain.hpp>
8+
#include <vma.hpp>
89
#include <window.hpp>
910
#include <filesystem>
1011

@@ -35,6 +36,7 @@ class App {
3536
void create_swapchain();
3637
void create_render_sync();
3738
void create_imgui();
39+
void create_allocator();
3840
void create_shader();
3941

4042
[[nodiscard]] auto asset_path(std::string_view uri) const -> fs::path;
@@ -61,7 +63,8 @@ class App {
6163
vk::UniqueSurfaceKHR m_surface{};
6264
Gpu m_gpu{}; // not an RAII member.
6365
vk::UniqueDevice m_device{};
64-
vk::Queue m_queue{}; // not an RAII member.
66+
vk::Queue m_queue{}; // not an RAII member.
67+
vma::Allocator m_allocator{}; // anywhere between m_device and m_shader.
6568

6669
std::optional<Swapchain> m_swapchain{};
6770
// command pool for all render Command Buffers.

src/vma.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#include <vma.hpp>
2+
#include <stdexcept>
3+
4+
namespace lvk {
5+
void vma::Deleter::operator()(VmaAllocator allocator) const noexcept {
6+
vmaDestroyAllocator(allocator);
7+
}
8+
9+
auto vma::create_allocator(vk::Instance const instance,
10+
vk::PhysicalDevice const physical_device,
11+
vk::Device const device) -> Allocator {
12+
auto const& dispatcher = VULKAN_HPP_DEFAULT_DISPATCHER;
13+
// need to zero initialize C structs, unlike VulkanHPP.
14+
auto vma_vk_funcs = VmaVulkanFunctions{};
15+
vma_vk_funcs.vkGetInstanceProcAddr = dispatcher.vkGetInstanceProcAddr;
16+
vma_vk_funcs.vkGetDeviceProcAddr = dispatcher.vkGetDeviceProcAddr;
17+
18+
auto allocator_ci = VmaAllocatorCreateInfo{};
19+
allocator_ci.physicalDevice = physical_device;
20+
allocator_ci.device = device;
21+
allocator_ci.pVulkanFunctions = &vma_vk_funcs;
22+
allocator_ci.instance = instance;
23+
VmaAllocator ret{};
24+
auto const result = vmaCreateAllocator(&allocator_ci, &ret);
25+
if (result == VK_SUCCESS) { return ret; }
26+
27+
throw std::runtime_error{"Failed to create Vulkan Memory Allocator"};
28+
}
29+
} // namespace lvk

src/vma.hpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#pragma once
2+
#include <vk_mem_alloc.h>
3+
#include <scoped.hpp>
4+
#include <vulkan/vulkan.hpp>
5+
6+
namespace lvk::vma {
7+
struct Deleter {
8+
void operator()(VmaAllocator allocator) const noexcept;
9+
};
10+
11+
using Allocator = Scoped<VmaAllocator, Deleter>;
12+
13+
[[nodiscard]] auto create_allocator(vk::Instance instance,
14+
vk::PhysicalDevice physical_device,
15+
vk::Device device) -> Allocator;
16+
} // namespace lvk::vma

0 commit comments

Comments
 (0)