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