• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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