Blender V4.3
vk_buffer.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include "vk_buffer.hh"
10#include "vk_backend.hh"
11#include "vk_context.hh"
12
13namespace blender::gpu {
14
16{
17 if (is_allocated()) {
18 free();
19 }
20}
21
23{
24 return allocation_ != VK_NULL_HANDLE;
25}
26
27static VmaAllocationCreateFlags vma_allocation_flags(GPUUsageType usage)
28{
29 switch (usage) {
32 return 0;
35 return VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
37 break;
38 }
39 BLI_assert_msg(false, "Unimplemented GPUUsageType");
40 return VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
41}
42
43static VkMemoryPropertyFlags vma_preferred_flags()
44{
45 return VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
46}
47
48static VkMemoryPropertyFlags vma_required_flags(const bool is_host_visible)
49{
50 return is_host_visible ? VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT : 0;
51}
52
53bool VKBuffer::create(size_t size_in_bytes,
54 GPUUsageType usage,
55 VkBufferUsageFlags buffer_usage,
56 const bool is_host_visible)
57{
58 /*
59 * TODO: Check which memory is selected and adjust the creation flag to add mapping. This way the
60 * staging buffer can be skipped, or in case of a vertex buffer an intermediate buffer can be
61 * removed.
62 */
63
65 BLI_assert(vk_buffer_ == VK_NULL_HANDLE);
66 BLI_assert(mapped_memory_ == nullptr);
67
68 size_in_bytes_ = size_in_bytes;
69 VKDevice &device = VKBackend::get().device;
70
71 VmaAllocator allocator = device.mem_allocator_get();
72 VkBufferCreateInfo create_info = {};
73 create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
74 create_info.flags = 0;
75 /*
76 * Vulkan doesn't allow empty buffers but some areas (DrawManager Instance data, PyGPU) create
77 * them.
78 */
79 create_info.size = max_ulul(size_in_bytes, 1);
80 create_info.usage = buffer_usage;
81 /* We use the same command queue for the compute and graphics pipeline, so it is safe to use
82 * exclusive resource handling. */
83 create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
84 create_info.queueFamilyIndexCount = 1;
85 const uint32_t queue_family_indices[1] = {device.queue_family_get()};
86 create_info.pQueueFamilyIndices = queue_family_indices;
87
88 VmaAllocationCreateInfo vma_create_info = {};
89 vma_create_info.flags = vma_allocation_flags(usage);
90 vma_create_info.priority = 1.0f;
91 vma_create_info.requiredFlags = vma_required_flags(is_host_visible);
92 vma_create_info.preferredFlags = vma_preferred_flags();
93 vma_create_info.usage = VMA_MEMORY_USAGE_AUTO;
94
95 VkResult result = vmaCreateBuffer(
96 allocator, &create_info, &vma_create_info, &vk_buffer_, &allocation_, nullptr);
97 if (result != VK_SUCCESS) {
98 return false;
99 }
100
101 device.resources.add_buffer(vk_buffer_);
102
103 if (is_host_visible) {
104 return map();
105 }
106 return true;
107}
108
109void VKBuffer::update_immediately(const void *data) const
110{
111 BLI_assert_msg(is_mapped(), "Cannot update a non-mapped buffer.");
112 memcpy(mapped_memory_, data, size_in_bytes_);
113 flush();
114}
115
116void VKBuffer::update_render_graph(VKContext &context, void *data) const
117{
118 BLI_assert(size_in_bytes_ <= 65536 && size_in_bytes_ % 4 == 0);
120 update_buffer.dst_buffer = vk_buffer_;
121 update_buffer.data_size = size_in_bytes_;
122 update_buffer.data = data;
123 context.render_graph.add_node(update_buffer);
124}
125
126void VKBuffer::flush() const
127{
128 const VKDevice &device = VKBackend::get().device;
129 VmaAllocator allocator = device.mem_allocator_get();
130 vmaFlushAllocation(allocator, allocation_, 0, max_ulul(size_in_bytes(), 1));
131}
132
133void VKBuffer::clear(VKContext &context, uint32_t clear_value)
134{
136 fill_buffer.vk_buffer = vk_buffer_;
137 fill_buffer.data = clear_value;
138 fill_buffer.size = size_in_bytes_;
139 context.render_graph.add_node(fill_buffer);
140}
141
142void VKBuffer::read(VKContext &context, void *data) const
143{
144 BLI_assert_msg(is_mapped(), "Cannot read a non-mapped buffer.");
145 context.rendering_end();
146 context.descriptor_set_get().upload_descriptor_sets();
147 context.render_graph.submit_buffer_for_read(vk_buffer_);
148 memcpy(data, mapped_memory_, size_in_bytes_);
149}
150
152{
153 BLI_assert_msg(is_mapped(), "Cannot access a non-mapped buffer.");
154 return mapped_memory_;
155}
156
158{
159 return mapped_memory_ != nullptr;
160}
161
162bool VKBuffer::map()
163{
165 const VKDevice &device = VKBackend::get().device;
166 VmaAllocator allocator = device.mem_allocator_get();
167 VkResult result = vmaMapMemory(allocator, allocation_, &mapped_memory_);
168 return result == VK_SUCCESS;
169}
170
171void VKBuffer::unmap()
172{
174 const VKDevice &device = VKBackend::get().device;
175 VmaAllocator allocator = device.mem_allocator_get();
176 vmaUnmapMemory(allocator, allocation_);
177 mapped_memory_ = nullptr;
178}
179
181{
182 if (is_mapped()) {
183 unmap();
184 }
185
186 VKDevice &device = VKBackend::get().device;
187 device.discard_pool_for_current_thread().discard_buffer(vk_buffer_, allocation_);
188
189 allocation_ = VK_NULL_HANDLE;
190 vk_buffer_ = VK_NULL_HANDLE;
191
192 return true;
193}
194
196{
197 BLI_assert(vk_buffer_ != VK_NULL_HANDLE);
198 BLI_assert(allocation_ != VK_NULL_HANDLE);
199 if (is_mapped()) {
200 unmap();
201 }
202 device.resources.remove_buffer(vk_buffer_);
203 vmaDestroyBuffer(device.mem_allocator_get(), vk_buffer_, allocation_);
204 allocation_ = VK_NULL_HANDLE;
205 vk_buffer_ = VK_NULL_HANDLE;
206}
207
208} // namespace blender::gpu
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:57
@ GPU_USAGE_STATIC
@ GPU_USAGE_STREAM
@ GPU_USAGE_DYNAMIC
@ GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY
@ GPU_USAGE_DEVICE_ONLY
static VKBackend & get()
Definition vk_backend.hh:92
void update_immediately(const void *data) const
Definition vk_buffer.cc:109
bool is_allocated() const
Definition vk_buffer.cc:22
bool create(size_t size, GPUUsageType usage, VkBufferUsageFlags buffer_usage, bool is_host_visible=true)
Definition vk_buffer.cc:53
bool is_mapped() const
Definition vk_buffer.cc:157
void read(VKContext &context, void *data) const
Definition vk_buffer.cc:142
void free_immediately(VKDevice &device)
Definition vk_buffer.cc:195
void clear(VKContext &context, uint32_t clear_value)
Definition vk_buffer.cc:133
void update_render_graph(VKContext &context, void *data) const
Definition vk_buffer.cc:116
void * mapped_memory_get() const
Definition vk_buffer.cc:151
int64_t size_in_bytes() const
Definition vk_buffer.hh:64
render_graph::VKResourceStateTracker resources
Definition vk_device.hh:172
VmaAllocator mem_allocator_get() const
Definition vk_device.hh:243
const uint32_t queue_family_get() const
Definition vk_device.hh:238
VKDiscardPool & discard_pool_for_current_thread()
Definition vk_device.cc:399
void discard_buffer(VkBuffer vk_buffer, VmaAllocation vma_allocation)
void add_buffer(VkBuffer vk_buffer, const char *name=nullptr)
MINLINE unsigned long long max_ulul(unsigned long long a, unsigned long long b)
static VmaAllocationCreateFlags vma_allocation_flags(GPUUsageType usage)
Definition vk_buffer.cc:27
static VkMemoryPropertyFlags vma_required_flags(const bool is_host_visible)
Definition vk_buffer.cc:48
static VkMemoryPropertyFlags vma_preferred_flags()
Definition vk_buffer.cc:43
unsigned int uint32_t
Definition stdint.h:80