xref: /aosp_15_r20/external/executorch/backends/vulkan/runtime/vk_api/memory/Image.h (revision 523fa7a60841cd1ecfb9cc4201f1ca8b03ed023a)
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 #pragma once
10 
11 // @lint-ignore-every CLANGTIDY facebook-hte-BadMemberName
12 
13 #include <executorch/backends/vulkan/runtime/vk_api/vk_api.h>
14 
15 #include <executorch/backends/vulkan/runtime/utils/VecUtils.h>
16 
17 #include <executorch/backends/vulkan/runtime/vk_api/memory/vma_api.h>
18 
19 #include <executorch/backends/vulkan/runtime/vk_api/memory/Allocation.h>
20 
21 #include <mutex>
22 #include <unordered_map>
23 
24 namespace vkcompute {
25 
26 // Forward declare vTensor classes such that they can be set as friend classes
27 namespace api {
28 class vTensorStorage;
29 } // namespace api
30 
31 namespace vkapi {
32 
33 class ImageSampler final {
34  public:
35   struct Properties final {
36     VkFilter filter;
37     VkSamplerMipmapMode mipmap_mode;
38     VkSamplerAddressMode address_mode;
39     VkBorderColor border_color;
40   };
41 
42   explicit ImageSampler(VkDevice, const Properties&);
43 
44   ImageSampler(const ImageSampler&) = delete;
45   ImageSampler& operator=(const ImageSampler&) = delete;
46 
47   ImageSampler(ImageSampler&&) noexcept;
48   ImageSampler& operator=(ImageSampler&&) = delete;
49 
50   ~ImageSampler();
51 
52  private:
53   VkDevice device_;
54   VkSampler handle_;
55 
56  public:
handle()57   VkSampler handle() const {
58     return handle_;
59   }
60 
61   struct Hasher {
62     size_t operator()(const Properties&) const;
63   };
64 
65   // We need to define a custom swap function since this class
66   // does not allow for move assignment. The swap function will
67   // be used in the hash map.
68   friend void swap(ImageSampler& lhs, ImageSampler& rhs) noexcept;
69 };
70 
71 class VulkanImage final {
72  public:
73   struct ImageProperties final {
74     VkImageType image_type;
75     VkFormat image_format;
76     VkExtent3D image_extents;
77     VkImageTiling image_tiling;
78     VkImageUsageFlags image_usage;
79   };
80 
81   struct ViewProperties final {
82     VkImageViewType view_type;
83     VkFormat view_format;
84   };
85 
86   using SamplerProperties = ImageSampler::Properties;
87 
88   struct Handles final {
89     VkImage image;
90     VkImageView image_view;
91     VkSampler sampler;
92   };
93 
94   explicit VulkanImage();
95 
96   explicit VulkanImage(
97       VkDevice,
98       const VmaAllocator,
99       const VmaAllocationCreateInfo&,
100       const ImageProperties&,
101       const ViewProperties&,
102       const SamplerProperties&,
103       VkSampler,
104       const VkImageLayout,
105       const bool allocate_memory = true);
106 
107   explicit VulkanImage(
108       VkDevice,
109       const ImageProperties&,
110       VkImage,
111       VkImageView,
112       VkSampler,
113       const VkImageLayout);
114 
115  protected:
116   /*
117    * The Copy constructor allows for creation of a class instance that are
118    * "aliases" of another class instance. The resulting class instance will not
119    * have ownership of the underlying VkImage.
120    *
121    * This behaviour is analogous to creating a copy of a pointer, thus it is
122    * unsafe, as the original class instance may be destroyed before the copy.
123    * These constructors are therefore marked protected so that they may be used
124    * only in situations where the lifetime of the original class instance is
125    * guaranteed to exceed, or at least be the same as, the lifetime of the
126    * copied class instance.
127    */
128   VulkanImage(const VulkanImage& other) noexcept;
129 
130  public:
131   // To discourage creating copies, the assignment operator is still deleted.
132   VulkanImage& operator=(const VulkanImage&) = delete;
133 
134   VulkanImage(VulkanImage&&) noexcept;
135   VulkanImage& operator=(VulkanImage&&) noexcept;
136 
137   ~VulkanImage();
138 
139   struct Package final {
140     VkImage handle;
141     VkImageLayout image_layout;
142     VkImageView image_view;
143     VkSampler image_sampler;
144   };
145 
146   friend struct ImageMemoryBarrier;
147 
148  private:
149   VkDevice device_;
150   ImageProperties image_properties_;
151   ViewProperties view_properties_;
152   SamplerProperties sampler_properties_;
153   // The allocator object this was allocated from
154   VmaAllocator allocator_;
155   // Handles to the allocated memory
156   Allocation memory_;
157   // Indicates whether the underlying memory is owned by this resource
158   bool owns_memory_;
159   // In some cases, a VulkanImage may be a copy of another VulkanImage but still
160   // own a unique view of the VkImage.
161   bool owns_view_;
162   // Indicates whether this VulkanImage was copied from another VulkanImage,
163   // thus it does not have ownership of the underlying VKBuffer
164   bool is_copy_;
165   Handles handles_;
166   // Layout
167   VkImageLayout layout_;
168 
169  public:
170   void create_image_view();
171 
device()172   inline VkDevice device() const {
173     return device_;
174   }
175 
vma_allocator()176   inline VmaAllocator vma_allocator() const {
177     return allocator_;
178   }
179 
allocation()180   inline VmaAllocation allocation() const {
181     return memory_.allocation;
182   }
183 
type()184   inline VkImageType type() const {
185     return image_properties_.image_type;
186   }
187 
format()188   inline VkFormat format() const {
189     return image_properties_.image_format;
190   }
191 
extents()192   inline VkExtent3D extents() const {
193     return image_properties_.image_extents;
194   }
195 
handle()196   inline VkImage handle() const {
197     return handles_.image;
198   }
199 
image_view()200   inline VkImageView image_view() const {
201     return handles_.image_view;
202   }
203 
sampler()204   inline VkSampler sampler() const {
205     return handles_.sampler;
206   }
207 
package()208   Package package() const {
209     return {
210         handles_.image,
211         layout_,
212         handles_.image_view,
213         handles_.sampler,
214     };
215   }
216 
layout()217   inline VkImageLayout layout() const {
218     return layout_;
219   }
220 
set_layout(const VkImageLayout layout)221   inline void set_layout(const VkImageLayout layout) {
222     layout_ = layout;
223   }
224 
has_memory()225   inline bool has_memory() const {
226     return (memory_.allocation != VK_NULL_HANDLE);
227   }
228 
owns_memory()229   inline bool owns_memory() const {
230     return owns_memory_;
231   }
232 
is_copy()233   inline bool is_copy() const {
234     return is_copy_;
235   }
236 
237   inline operator bool() const {
238     return (handles_.image != VK_NULL_HANDLE);
239   }
240 
is_copy_of(const VulkanImage & other)241   inline bool is_copy_of(const VulkanImage& other) const {
242     return (handles_.image == other.handles_.image) && is_copy_;
243   }
244 
bind_allocation(const Allocation & memory)245   inline void bind_allocation(const Allocation& memory) {
246     VK_CHECK_COND(!memory_, "Cannot bind an already bound allocation!");
247     // To prevent multiple instances of binding the same VkImage to a memory
248     // block, do not actually bind memory if this VulkanImage is a copy. Assume
249     // that the original VulkanImage is responsible for binding the image.
250     if (!is_copy_) {
251       VK_CHECK(
252           vmaBindImageMemory(allocator_, memory.allocation, handles_.image));
253     }
254     memory_.allocation = memory.allocation;
255 
256     // Only create the image view if the image has been bound to memory
257     owns_view_ = true;
258     create_image_view();
259   }
260 
261   VkMemoryRequirements get_memory_requirements() const;
262 
263   friend class api::vTensorStorage;
264 };
265 
266 struct ImageMemoryBarrier final {
267   VkImageMemoryBarrier handle;
268 
269   ImageMemoryBarrier(
270       const VkAccessFlags src_access_flags,
271       const VkAccessFlags dst_access_flags,
272       const VkImageLayout src_layout_flags,
273       const VkImageLayout dst_layout_flags,
274       const VulkanImage& image);
275 };
276 
277 class SamplerCache final {
278  public:
279   explicit SamplerCache(VkDevice device);
280 
281   SamplerCache(const SamplerCache&) = delete;
282   SamplerCache& operator=(const SamplerCache&) = delete;
283 
284   SamplerCache(SamplerCache&&) noexcept;
285   SamplerCache& operator=(SamplerCache&&) = delete;
286 
287   ~SamplerCache();
288 
289   using Key = ImageSampler::Properties;
290   using Value = ImageSampler;
291   using Hasher = ImageSampler::Hasher;
292 
293  private:
294   // Multiple threads could potentially be adding entries into the cache, so use
295   // a mutex to manage access
296   std::mutex cache_mutex_;
297 
298   VkDevice device_;
299   std::unordered_map<Key, Value, Hasher> cache_;
300 
301  public:
302   VkSampler retrieve(const Key&);
303   void purge();
304 };
305 
306 } // namespace vkapi
307 } // namespace vkcompute
308