Blender V5.0
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
8
9#include "vk_buffer.hh"
10#include "vk_backend.hh"
11#include "vk_context.hh"
12#include <vulkan/vulkan_core.h>
13
14#include "CLG_log.h"
15
16static CLG_LogRef LOG = {"gpu.vulkan"};
17
18namespace blender::gpu {
19
21{
22 if (is_allocated()) {
23 free();
24 }
25}
26
28 VkBufferUsageFlags buffer_usage,
29 VmaMemoryUsage vma_memory_usage,
30 VmaAllocationCreateFlags allocation_flags,
31 float priority,
32 bool export_memory)
33{
35 BLI_assert(vk_buffer_ == VK_NULL_HANDLE);
36 BLI_assert(mapped_memory_ == nullptr);
37 if (allocation_failed_) {
38 return false;
39 }
40
41 size_in_bytes_ = size_in_bytes;
42 /*
43 * Vulkan doesn't allow empty buffers but some areas (DrawManager Instance data, PyGPU) create
44 * them.
45 */
46 alloc_size_in_bytes_ = ceil_to_multiple_ul(max_ulul(size_in_bytes_, 16), 16);
47 VKDevice &device = VKBackend::get().device;
48
49 /* Precheck max buffer size. */
50 if (device.extensions_get().maintenance4 &&
51 alloc_size_in_bytes_ > device.physical_device_maintenance4_properties_get().maxBufferSize)
52 {
54 &LOG,
55 "Couldn't allocate buffer, requested allocation exceeds the maxBufferSize of the device.");
56 allocation_failed_ = true;
57 size_in_bytes_ = 0;
58 alloc_size_in_bytes_ = 0;
59 return false;
60 }
61
62 VmaAllocator allocator = device.mem_allocator_get();
63 VkBufferCreateInfo create_info = {};
64 create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
65 create_info.flags = 0;
66 create_info.size = alloc_size_in_bytes_;
67 create_info.usage = buffer_usage;
68 /* We use the same command queue for the compute and graphics pipeline, so it is safe to use
69 * exclusive resource handling. */
70 create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
71 create_info.queueFamilyIndexCount = 1;
72 const uint32_t queue_family_indices[1] = {device.queue_family_get()};
73 create_info.pQueueFamilyIndices = queue_family_indices;
74
75 VkExternalMemoryBufferCreateInfo external_memory_create_info = {
76 VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO, nullptr, 0};
77
78 VmaAllocationCreateInfo vma_create_info = {};
79 vma_create_info.flags = allocation_flags;
80 vma_create_info.priority = priority;
81 vma_create_info.usage = vma_memory_usage;
82
83 if (export_memory) {
84 create_info.pNext = &external_memory_create_info;
85 external_memory_create_info.handleTypes = vk_external_memory_handle_type();
86
87 /* Dedicated allocation for zero offset. */
88 vma_create_info.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
89 vma_create_info.pool = device.vma_pools.external_memory_pixel_buffer.pool;
90 }
91
92 const bool use_descriptor_buffer = device.extensions_get().descriptor_buffer;
93 if (use_descriptor_buffer) {
94 create_info.usage |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
95 }
96
97 VkResult result = vmaCreateBuffer(
98 allocator, &create_info, &vma_create_info, &vk_buffer_, &allocation_, nullptr);
99 if (result != VK_SUCCESS) {
100 allocation_failed_ = true;
101 size_in_bytes_ = 0;
102 alloc_size_in_bytes_ = 0;
103 return false;
104 }
105
106 device.resources.add_buffer(vk_buffer_);
107
108 if (use_descriptor_buffer) {
109 VkBufferDeviceAddressInfo vk_buffer_device_address_info = {
110 VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, nullptr, vk_buffer_};
111 vk_device_address = vkGetBufferDeviceAddress(device.vk_handle(),
112 &vk_buffer_device_address_info);
113 }
114
115 vmaGetAllocationMemoryProperties(allocator, allocation_, &vk_memory_property_flags_);
116 if (vk_memory_property_flags_ & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
117 return map();
118 }
119
120 return true;
121}
122
123void VKBuffer::update_immediately(const void *data) const
124{
125 update_sub_immediately(0, size_in_bytes_, data);
126}
127
128void VKBuffer::update_sub_immediately(size_t start_offset,
129 size_t data_size,
130 const void *data) const
131{
132 BLI_assert_msg(is_mapped(), "Cannot update a non-mapped buffer.");
133 memcpy(static_cast<uint8_t *>(mapped_memory_) + start_offset, data, data_size);
134}
135
137{
138 BLI_assert(size_in_bytes_ <= 65536 && size_in_bytes_ % 4 == 0);
140 update_buffer.dst_buffer = vk_buffer_;
141 update_buffer.data_size = size_in_bytes_;
142 update_buffer.data = data;
143 context.render_graph().add_node(update_buffer);
144}
145
146void VKBuffer::flush() const
147{
148 const VKDevice &device = VKBackend::get().device;
149 VmaAllocator allocator = device.mem_allocator_get();
150 vmaFlushAllocation(allocator, allocation_, 0, max_ulul(size_in_bytes(), 1));
151}
152
153void VKBuffer::clear(VKContext &context, uint32_t clear_value)
154{
156 fill_buffer.vk_buffer = vk_buffer_;
157 fill_buffer.data = clear_value;
158 fill_buffer.size = alloc_size_in_bytes_;
159 context.render_graph().add_node(fill_buffer);
160}
161
163{
164 BLI_assert(async_timeline_ == 0);
165 context.rendering_end();
166 async_timeline_ = context.flush_render_graph(RenderGraphFlushFlags::SUBMIT |
168}
169
171{
172 BLI_assert_msg(is_mapped(), "Cannot read a non-mapped buffer.");
173 if (async_timeline_ == 0) {
174 async_flush_to_host(context);
175 }
176 VKDevice &device = VKBackend::get().device;
177 device.wait_for_timeline(async_timeline_);
178 async_timeline_ = 0;
179 memcpy(data, mapped_memory_, size_in_bytes_);
180}
181
182void VKBuffer::read(VKContext &context, void *data) const
183{
184
185 BLI_assert_msg(is_mapped(), "Cannot read a non-mapped buffer.");
186 BLI_assert(async_timeline_ == 0);
187 context.rendering_end();
188 context.flush_render_graph(RenderGraphFlushFlags::SUBMIT |
191 memcpy(data, mapped_memory_, size_in_bytes_);
192}
193
194bool VKBuffer::map()
195{
197 const VKDevice &device = VKBackend::get().device;
198 VmaAllocator allocator = device.mem_allocator_get();
199 VkResult result = vmaMapMemory(allocator, allocation_, &mapped_memory_);
200 return result == VK_SUCCESS;
201}
202
203void VKBuffer::unmap()
204{
206 const VKDevice &device = VKBackend::get().device;
207 VmaAllocator allocator = device.mem_allocator_get();
208 vmaUnmapMemory(allocator, allocation_);
209 mapped_memory_ = nullptr;
210}
211
212VkDeviceMemory VKBuffer::export_memory_get(size_t &memory_size)
213{
214 const VKDevice &device = VKBackend::get().device;
215 VmaAllocator allocator = device.mem_allocator_get();
216
217 VmaAllocationInfo info = {};
218 vmaGetAllocationInfo(allocator, allocation_, &info);
219
220 /* VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT should ensure this. */
221 if (info.offset != 0) {
222 BLI_assert(!"Failed to get zero offset export memory for Vulkan buffer");
223 return nullptr;
224 }
225
226 memory_size = info.size;
227 return info.deviceMemory;
228}
229
231{
232 if (is_mapped()) {
233 unmap();
234 }
235
236 VKDiscardPool::discard_pool_get().discard_buffer(vk_buffer_, allocation_);
237
238 allocation_ = VK_NULL_HANDLE;
239 vk_buffer_ = VK_NULL_HANDLE;
240
241 return true;
242}
243
245{
246 BLI_assert(vk_buffer_ != VK_NULL_HANDLE);
247 BLI_assert(allocation_ != VK_NULL_HANDLE);
248 if (is_mapped()) {
249 unmap();
250 }
251 device.resources.remove_buffer(vk_buffer_);
252 vmaDestroyBuffer(device.mem_allocator_get(), vk_buffer_, allocation_);
253 allocation_ = VK_NULL_HANDLE;
254 vk_buffer_ = VK_NULL_HANDLE;
255}
256
257} // namespace blender::gpu
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
MINLINE uint64_t ceil_to_multiple_ul(uint64_t a, uint64_t b)
#define CLOG_WARN(clg_ref,...)
Definition CLG_log.h:189
BMesh const char void * data
static VKBackend & get()
Definition vk_backend.hh:91
void update_immediately(const void *data) const
Definition vk_buffer.cc:123
bool is_allocated() const
Definition vk_buffer.hh:145
bool is_mapped() const
Definition vk_buffer.hh:140
void read(VKContext &context, void *data) const
Definition vk_buffer.cc:182
void free_immediately(VKDevice &device)
Definition vk_buffer.cc:244
void read_async(VKContext &context, void *data)
Definition vk_buffer.cc:170
VkDeviceMemory export_memory_get(size_t &memory_size)
Definition vk_buffer.cc:212
void clear(VKContext &context, uint32_t clear_value)
Definition vk_buffer.cc:153
void update_sub_immediately(size_t start_offset, size_t data_size, const void *data) const
Definition vk_buffer.cc:128
void update_render_graph(VKContext &context, void *data) const
Definition vk_buffer.cc:136
void async_flush_to_host(VKContext &context)
Definition vk_buffer.cc:162
bool create(size_t size, VkBufferUsageFlags buffer_usage, VmaMemoryUsage vma_memory_usage, VmaAllocationCreateFlags vma_allocation_flags, float priority, bool export_memory=false)
Definition vk_buffer.cc:27
int64_t size_in_bytes() const
Definition vk_buffer.hh:96
uint32_t queue_family_get() const
Definition vk_device.hh:316
render_graph::VKResourceStateTracker resources
Definition vk_device.hh:217
VmaAllocator mem_allocator_get() const
Definition vk_device.hh:321
VkDevice vk_handle() const
Definition vk_device.hh:311
VKMemoryPools vma_pools
Definition vk_device.hh:257
const VKExtensions & extensions_get() const
Definition vk_device.hh:371
const VkPhysicalDeviceMaintenance4Properties & physical_device_maintenance4_properties_get() const
Definition vk_device.hh:275
void wait_for_timeline(TimelineValue timeline)
static VKDiscardPool & discard_pool_get()
void discard_buffer(VkBuffer vk_buffer, VmaAllocation vma_allocation)
void add_buffer(VkBuffer vk_buffer, const char *name=nullptr)
#define LOG(level)
Definition log.h:97
MINLINE unsigned long long max_ulul(unsigned long long a, unsigned long long b)
constexpr VkExternalMemoryHandleTypeFlags vk_external_memory_handle_type()
static CLG_LogRef LOG
VKMemoryPool external_memory_pixel_buffer