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