1 /*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 * All rights reserved.
4 *
5 * This source code is licensed under the BSD-style license found in the
6 * LICENSE file in the root directory of this source tree.
7 */
8
9 #include <executorch/backends/vulkan/runtime/vk_api/memory/Buffer.h>
10
11 namespace vkcompute {
12 namespace vkapi {
13
14 //
15 // VulkanBuffer
16 //
17
VulkanBuffer()18 VulkanBuffer::VulkanBuffer()
19 : buffer_properties_{},
20 allocator_(VK_NULL_HANDLE),
21 memory_{},
22 owns_memory_(false),
23 is_copy_(false),
24 handle_(VK_NULL_HANDLE) {}
25
VulkanBuffer(VmaAllocator vma_allocator,const VkDeviceSize size,const VmaAllocationCreateInfo & allocation_create_info,const VkBufferUsageFlags usage,const bool allocate_memory)26 VulkanBuffer::VulkanBuffer(
27 VmaAllocator vma_allocator,
28 const VkDeviceSize size,
29 const VmaAllocationCreateInfo& allocation_create_info,
30 const VkBufferUsageFlags usage,
31 const bool allocate_memory)
32 : buffer_properties_({
33 size,
34 0u,
35 size,
36 usage,
37 }),
38 allocator_(vma_allocator),
39 memory_{},
40 owns_memory_(allocate_memory),
41 is_copy_(false),
42 handle_(VK_NULL_HANDLE) {
43 // If the buffer size is 0, allocate a buffer with a size of 1 byte. This is
44 // to ensure that there will be some resource that can be bound to a shader.
45 if (size == 0) {
46 buffer_properties_.size = 1u;
47 buffer_properties_.mem_range = 1u;
48 }
49
50 const VkBufferCreateInfo buffer_create_info{
51 VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // sType
52 nullptr, // pNext
53 0u, // flags
54 buffer_properties_.size, // size
55 buffer_properties_.buffer_usage, // usage
56 VK_SHARING_MODE_EXCLUSIVE, // sharingMode
57 0u, // queueFamilyIndexCount
58 nullptr, // pQueueFamilyIndices
59 };
60
61 if (allocate_memory) {
62 VK_CHECK(vmaCreateBuffer(
63 allocator_,
64 &buffer_create_info,
65 &allocation_create_info,
66 &handle_,
67 &(memory_.allocation),
68 nullptr));
69 } else {
70 VmaAllocatorInfo allocator_info{};
71 vmaGetAllocatorInfo(allocator_, &allocator_info);
72 VK_CHECK(vkCreateBuffer(
73 allocator_info.device, &buffer_create_info, nullptr, &handle_));
74 }
75 }
76
VulkanBuffer(const VulkanBuffer & other,const VkDeviceSize offset,const VkDeviceSize range)77 VulkanBuffer::VulkanBuffer(
78 const VulkanBuffer& other,
79 const VkDeviceSize offset,
80 const VkDeviceSize range) noexcept
81 : buffer_properties_(other.buffer_properties_),
82 allocator_(other.allocator_),
83 memory_(other.memory_),
84 owns_memory_(false),
85 is_copy_(true),
86 handle_(other.handle_) {
87 // TODO: set the offset and range appropriately
88 buffer_properties_.mem_offset = other.buffer_properties_.mem_offset + offset;
89 if (range != VK_WHOLE_SIZE) {
90 buffer_properties_.mem_range = range;
91 }
92 }
93
VulkanBuffer(VulkanBuffer && other)94 VulkanBuffer::VulkanBuffer(VulkanBuffer&& other) noexcept
95 : buffer_properties_(other.buffer_properties_),
96 allocator_(other.allocator_),
97 memory_(std::move(other.memory_)),
98 owns_memory_(other.owns_memory_),
99 is_copy_(other.is_copy_),
100 handle_(other.handle_) {
101 other.handle_ = VK_NULL_HANDLE;
102 }
103
operator =(VulkanBuffer && other)104 VulkanBuffer& VulkanBuffer::operator=(VulkanBuffer&& other) noexcept {
105 VkBuffer tmp_buffer = handle_;
106 bool tmp_owns_memory = owns_memory_;
107
108 buffer_properties_ = other.buffer_properties_;
109 allocator_ = other.allocator_;
110 memory_ = std::move(other.memory_);
111 owns_memory_ = other.owns_memory_;
112 is_copy_ = other.is_copy_;
113 handle_ = other.handle_;
114
115 other.handle_ = tmp_buffer;
116 other.owns_memory_ = tmp_owns_memory;
117
118 return *this;
119 }
120
~VulkanBuffer()121 VulkanBuffer::~VulkanBuffer() {
122 // Do not destroy the VkBuffer if this class instance is a copy of another
123 // class instance, since this means that this class instance does not have
124 // ownership of the underlying resource.
125 if (handle_ != VK_NULL_HANDLE && !is_copy_) {
126 if (owns_memory_) {
127 vmaDestroyBuffer(allocator_, handle_, memory_.allocation);
128 } else {
129 vkDestroyBuffer(this->device(), handle_, nullptr);
130 }
131 // Prevent the underlying memory allocation from being freed; it was either
132 // freed by vmaDestroyBuffer, or this resource does not own the underlying
133 // memory
134 memory_.allocation = VK_NULL_HANDLE;
135 }
136 }
137
allocation_info() const138 VmaAllocationInfo VulkanBuffer::allocation_info() const {
139 VmaAllocationInfo info;
140 vmaGetAllocationInfo(allocator_, memory_.allocation, &info);
141 return info;
142 }
143
get_memory_requirements() const144 VkMemoryRequirements VulkanBuffer::get_memory_requirements() const {
145 VkMemoryRequirements memory_requirements;
146 vkGetBufferMemoryRequirements(this->device(), handle_, &memory_requirements);
147 return memory_requirements;
148 }
149
150 //
151 // MemoryMap
152 //
153
MemoryMap(const VulkanBuffer & buffer,const uint8_t access)154 MemoryMap::MemoryMap(const VulkanBuffer& buffer, const uint8_t access)
155 : access_(access),
156 allocator_(buffer.vma_allocator()),
157 allocation_(buffer.allocation()),
158 data_(nullptr),
159 data_len_{buffer.mem_size()} {
160 if (allocation_) {
161 VK_CHECK(vmaMapMemory(allocator_, allocation_, &data_));
162 }
163 }
164
MemoryMap(MemoryMap && other)165 MemoryMap::MemoryMap(MemoryMap&& other) noexcept
166 : access_(other.access_),
167 allocator_(other.allocator_),
168 allocation_(other.allocation_),
169 data_(other.data_),
170 data_len_{other.data_len_} {
171 other.allocation_ = VK_NULL_HANDLE;
172 other.data_ = nullptr;
173 }
174
~MemoryMap()175 MemoryMap::~MemoryMap() {
176 if (!data_) {
177 return;
178 }
179
180 if (allocation_) {
181 if (access_ & MemoryAccessType::WRITE) {
182 // Call will be ignored by implementation if the memory type this
183 // allocation belongs to is not HOST_VISIBLE or is HOST_COHERENT, which is
184 // the behavior we want. Don't check the result here as the destructor
185 // cannot throw.
186 vmaFlushAllocation(allocator_, allocation_, 0u, VK_WHOLE_SIZE);
187 }
188
189 vmaUnmapMemory(allocator_, allocation_);
190 }
191 }
192
invalidate()193 void MemoryMap::invalidate() {
194 if (access_ & MemoryAccessType::READ && allocation_) {
195 // Call will be ignored by implementation if the memory type this allocation
196 // belongs to is not HOST_VISIBLE or is HOST_COHERENT, which is the behavior
197 // we want.
198 VK_CHECK(
199 vmaInvalidateAllocation(allocator_, allocation_, 0u, VK_WHOLE_SIZE));
200 }
201 }
202
203 //
204 // BufferMemoryBarrier
205 //
206
BufferMemoryBarrier(const VkAccessFlags src_access_flags,const VkAccessFlags dst_access_flags,const VulkanBuffer & buffer)207 BufferMemoryBarrier::BufferMemoryBarrier(
208 const VkAccessFlags src_access_flags,
209 const VkAccessFlags dst_access_flags,
210 const VulkanBuffer& buffer)
211 : handle{
212 VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, // sType
213 nullptr, // pNext
214 src_access_flags, // srcAccessMask
215 dst_access_flags, // dstAccessMask
216 VK_QUEUE_FAMILY_IGNORED, // srcQueueFamilyIndex
217 VK_QUEUE_FAMILY_IGNORED, // dstQueueFamilyIndex
218 buffer.handle_, // buffer
219 buffer.buffer_properties_.mem_offset, // offset
220 buffer.buffer_properties_.mem_range, // size
221 } {}
222
223 } // namespace vkapi
224 } // namespace vkcompute
225