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/Image.h>
10
11 namespace vkcompute {
12 namespace vkapi {
13
14 //
15 // ImageSampler
16 //
17
operator ==(const ImageSampler::Properties & _1,const ImageSampler::Properties & _2)18 bool operator==(
19 const ImageSampler::Properties& _1,
20 const ImageSampler::Properties& _2) {
21 return (
22 _1.filter == _2.filter && _1.mipmap_mode == _2.mipmap_mode &&
23 _1.address_mode == _2.address_mode && _1.border_color == _2.border_color);
24 }
25
ImageSampler(VkDevice device,const ImageSampler::Properties & props)26 ImageSampler::ImageSampler(
27 VkDevice device,
28 const ImageSampler::Properties& props)
29 : device_(device), handle_(VK_NULL_HANDLE) {
30 const VkSamplerCreateInfo sampler_create_info{
31 VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, // sType
32 nullptr, // pNext
33 0u, // flags
34 props.filter, // magFilter
35 props.filter, // minFilter
36 props.mipmap_mode, // mipmapMode
37 props.address_mode, // addressModeU
38 props.address_mode, // addressModeV
39 props.address_mode, // addressModeW
40 0.0f, // mipLodBias
41 VK_FALSE, // anisotropyEnable
42 1.0f, // maxAnisotropy,
43 VK_FALSE, // compareEnable
44 VK_COMPARE_OP_NEVER, // compareOp
45 0.0f, // minLod
46 VK_LOD_CLAMP_NONE, // maxLod
47 props.border_color, // borderColor
48 VK_FALSE, // unnormalizedCoordinates
49 };
50
51 VK_CHECK(vkCreateSampler(device_, &sampler_create_info, nullptr, &handle_));
52 }
53
ImageSampler(ImageSampler && other)54 ImageSampler::ImageSampler(ImageSampler&& other) noexcept
55 : device_(other.device_), handle_(other.handle_) {
56 other.handle_ = VK_NULL_HANDLE;
57 }
58
~ImageSampler()59 ImageSampler::~ImageSampler() {
60 if (handle_ == VK_NULL_HANDLE) {
61 return;
62 }
63 vkDestroySampler(device_, handle_, nullptr);
64 }
65
operator ()(const ImageSampler::Properties & props) const66 size_t ImageSampler::Hasher::operator()(
67 const ImageSampler::Properties& props) const {
68 size_t seed = 0;
69 seed = utils::hash_combine(seed, std::hash<VkFilter>()(props.filter));
70 seed = utils::hash_combine(
71 seed, std::hash<VkSamplerMipmapMode>()(props.mipmap_mode));
72 seed = utils::hash_combine(
73 seed, std::hash<VkSamplerAddressMode>()(props.address_mode));
74 seed =
75 utils::hash_combine(seed, std::hash<VkBorderColor>()(props.border_color));
76 return seed;
77 }
78
swap(ImageSampler & lhs,ImageSampler & rhs)79 void swap(ImageSampler& lhs, ImageSampler& rhs) noexcept {
80 VkDevice tmp_device = lhs.device_;
81 VkSampler tmp_handle = lhs.handle_;
82
83 lhs.device_ = rhs.device_;
84 lhs.handle_ = rhs.handle_;
85
86 rhs.device_ = tmp_device;
87 rhs.handle_ = tmp_handle;
88 }
89
90 //
91 // VulkanImage
92 //
93
VulkanImage()94 VulkanImage::VulkanImage()
95 : device_{VK_NULL_HANDLE},
96 image_properties_{},
97 view_properties_{},
98 sampler_properties_{},
99 allocator_(VK_NULL_HANDLE),
100 memory_{},
101 owns_memory_(false),
102 owns_view_(false),
103 is_copy_(false),
104 handles_{
105 VK_NULL_HANDLE,
106 VK_NULL_HANDLE,
107 VK_NULL_HANDLE,
108 },
109 layout_{} {}
110
VulkanImage(VkDevice device,VmaAllocator vma_allocator,const VmaAllocationCreateInfo & allocation_create_info,const ImageProperties & image_props,const ViewProperties & view_props,const SamplerProperties & sampler_props,VkSampler sampler,const VkImageLayout layout,const bool allocate_memory)111 VulkanImage::VulkanImage(
112 VkDevice device,
113 VmaAllocator vma_allocator,
114 const VmaAllocationCreateInfo& allocation_create_info,
115 const ImageProperties& image_props,
116 const ViewProperties& view_props,
117 const SamplerProperties& sampler_props,
118 VkSampler sampler,
119 const VkImageLayout layout,
120 const bool allocate_memory)
121 : device_{device},
122 image_properties_(image_props),
123 view_properties_(view_props),
124 sampler_properties_(sampler_props),
125 allocator_(vma_allocator),
126 memory_{},
127 owns_memory_{allocate_memory},
128 owns_view_(false),
129 is_copy_(false),
130 handles_{
131 VK_NULL_HANDLE,
132 VK_NULL_HANDLE,
133 sampler,
134 },
135 layout_(layout) {
136 VmaAllocatorInfo allocator_info{};
137 vmaGetAllocatorInfo(allocator_, &allocator_info);
138
139 // If any dims are zero, then allocate a 1x1x1 image texture. This is to
140 // ensure that there will be some resource that can be bound to a shader.
141 if (image_props.image_extents.width == 0 ||
142 image_props.image_extents.height == 0 ||
143 image_props.image_extents.depth == 0) {
144 image_properties_.image_extents.width = 1u;
145 image_properties_.image_extents.height = 1u;
146 image_properties_.image_extents.depth = 1u;
147 }
148
149 const VkImageCreateInfo image_create_info{
150 VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // sType
151 nullptr, // pNext
152 0u, // flags
153 image_properties_.image_type, // imageType
154 image_properties_.image_format, // format
155 image_properties_.image_extents, // extents
156 1u, // mipLevels
157 1u, // arrayLayers
158 VK_SAMPLE_COUNT_1_BIT, // samples
159 image_properties_.image_tiling, // tiling
160 image_properties_.image_usage, // usage
161 VK_SHARING_MODE_EXCLUSIVE, // sharingMode
162 0u, // queueFamilyIndexCount
163 nullptr, // pQueueFamilyIndices
164 layout_, // initialLayout
165 };
166
167 if (allocate_memory) {
168 VK_CHECK(vmaCreateImage(
169 allocator_,
170 &image_create_info,
171 &allocation_create_info,
172 &(handles_.image),
173 &(memory_.allocation),
174 nullptr));
175 // Only create the image view if the image has been bound to memory
176 owns_view_ = true;
177 create_image_view();
178 } else {
179 VK_CHECK(vkCreateImage(
180 allocator_info.device, &image_create_info, nullptr, &(handles_.image)));
181 }
182 }
183
VulkanImage(VkDevice device,const ImageProperties & image_props,VkImage image,VkImageView image_view,VkSampler sampler,const VkImageLayout layout)184 VulkanImage::VulkanImage(
185 VkDevice device,
186 const ImageProperties& image_props,
187 VkImage image,
188 VkImageView image_view,
189 VkSampler sampler,
190 const VkImageLayout layout)
191 : device_{device},
192 image_properties_{image_props},
193 view_properties_{},
194 sampler_properties_{},
195 allocator_(VK_NULL_HANDLE),
196 memory_{},
197 owns_memory_(false),
198 is_copy_(false),
199 handles_{
200 image,
201 image_view,
202 sampler,
203 },
204 layout_{layout} {}
205
VulkanImage(const VulkanImage & other)206 VulkanImage::VulkanImage(const VulkanImage& other) noexcept
207 : device_(other.device_),
208 image_properties_(other.image_properties_),
209 view_properties_(other.view_properties_),
210 sampler_properties_(other.sampler_properties_),
211 allocator_(other.allocator_),
212 memory_(other.memory_),
213 owns_memory_{false},
214 owns_view_{false},
215 is_copy_(true),
216 handles_(other.handles_),
217 layout_(other.layout_) {}
218
VulkanImage(VulkanImage && other)219 VulkanImage::VulkanImage(VulkanImage&& other) noexcept
220 : device_(other.device_),
221 image_properties_(other.image_properties_),
222 view_properties_(other.view_properties_),
223 sampler_properties_(other.sampler_properties_),
224 allocator_(other.allocator_),
225 memory_(std::move(other.memory_)),
226 owns_memory_(other.owns_memory_),
227 owns_view_(other.owns_view_),
228 is_copy_(other.is_copy_),
229 handles_(other.handles_),
230 layout_(other.layout_) {
231 other.handles_.image = VK_NULL_HANDLE;
232 other.handles_.image_view = VK_NULL_HANDLE;
233 other.handles_.sampler = VK_NULL_HANDLE;
234 other.owns_memory_ = false;
235 }
236
operator =(VulkanImage && other)237 VulkanImage& VulkanImage::operator=(VulkanImage&& other) noexcept {
238 VkImage tmp_image = handles_.image;
239 VkImageView tmp_image_view = handles_.image_view;
240 bool tmp_owns_memory = owns_memory_;
241
242 device_ = other.device_;
243 image_properties_ = other.image_properties_;
244 view_properties_ = other.view_properties_;
245 sampler_properties_ = other.sampler_properties_;
246 allocator_ = other.allocator_;
247 memory_ = std::move(other.memory_);
248 owns_memory_ = other.owns_memory_;
249 is_copy_ = other.is_copy_;
250 handles_ = other.handles_;
251 layout_ = other.layout_;
252
253 other.handles_.image = tmp_image;
254 other.handles_.image_view = tmp_image_view;
255 other.owns_memory_ = tmp_owns_memory;
256
257 return *this;
258 }
259
~VulkanImage()260 VulkanImage::~VulkanImage() {
261 if (owns_view_ && handles_.image_view != VK_NULL_HANDLE) {
262 vkDestroyImageView(this->device(), handles_.image_view, nullptr);
263 }
264
265 // Do not destroy any resources if this class instance is a copy of another
266 // class instance, since this means that this class instance does not have
267 // ownership of the underlying resource.
268 if (is_copy_) {
269 return;
270 }
271
272 if (handles_.image != VK_NULL_HANDLE) {
273 if (owns_memory_) {
274 vmaDestroyImage(allocator_, handles_.image, memory_.allocation);
275 } else {
276 vkDestroyImage(this->device(), handles_.image, nullptr);
277 }
278 // Prevent the underlying memory allocation from being freed; it was either
279 // freed by vmaDestroyImage, or this resource does not own the underlying
280 // memory
281 memory_.allocation = VK_NULL_HANDLE;
282 }
283 }
284
create_image_view()285 void VulkanImage::create_image_view() {
286 VmaAllocatorInfo allocator_info{};
287 vmaGetAllocatorInfo(allocator_, &allocator_info);
288
289 const VkComponentMapping component_mapping{
290 VK_COMPONENT_SWIZZLE_IDENTITY, // r
291 VK_COMPONENT_SWIZZLE_IDENTITY, // g
292 VK_COMPONENT_SWIZZLE_IDENTITY, // b
293 VK_COMPONENT_SWIZZLE_IDENTITY, // a
294 };
295
296 const VkImageSubresourceRange subresource_range{
297 VK_IMAGE_ASPECT_COLOR_BIT, // aspectMask
298 0u, // baseMipLevel
299 VK_REMAINING_MIP_LEVELS, // levelCount
300 0u, // baseArrayLayer
301 VK_REMAINING_ARRAY_LAYERS, // layerCount
302 };
303
304 const VkImageViewCreateInfo image_view_create_info{
305 VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, // sType
306 nullptr, // pNext
307 0u, // flags
308 handles_.image, // image
309 view_properties_.view_type, // viewType
310 view_properties_.view_format, // format
311 component_mapping, // components
312 subresource_range, // subresourceRange
313 };
314
315 VK_CHECK(vkCreateImageView(
316 allocator_info.device,
317 &(image_view_create_info),
318 nullptr,
319 &(handles_.image_view)));
320 }
321
get_memory_requirements() const322 VkMemoryRequirements VulkanImage::get_memory_requirements() const {
323 VkMemoryRequirements memory_requirements;
324 vkGetImageMemoryRequirements(
325 this->device(), handles_.image, &memory_requirements);
326 return memory_requirements;
327 }
328
329 //
330 // ImageMemoryBarrier
331 //
332
ImageMemoryBarrier(const VkAccessFlags src_access_flags,const VkAccessFlags dst_access_flags,const VkImageLayout src_layout_flags,const VkImageLayout dst_layout_flags,const VulkanImage & image)333 ImageMemoryBarrier::ImageMemoryBarrier(
334 const VkAccessFlags src_access_flags,
335 const VkAccessFlags dst_access_flags,
336 const VkImageLayout src_layout_flags,
337 const VkImageLayout dst_layout_flags,
338 const VulkanImage& image)
339 : handle{
340 VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // sType
341 nullptr, // pNext
342 src_access_flags, // srcAccessMask
343 dst_access_flags, // dstAccessMask
344 src_layout_flags, // oldLayout
345 dst_layout_flags, // newLayout
346 VK_QUEUE_FAMILY_IGNORED, // srcQueueFamilyIndex
347 VK_QUEUE_FAMILY_IGNORED, // dstQueueFamilyIndex
348 image.handles_.image, // image
349 {
350 // subresourceRange
351 VK_IMAGE_ASPECT_COLOR_BIT, // aspectMask
352 0u, // baseMipLevel
353 VK_REMAINING_MIP_LEVELS, // levelCount
354 0u, // baseArrayLayer
355 VK_REMAINING_ARRAY_LAYERS, // layerCount
356 },
357 } {}
358
359 //
360 // SamplerCache
361 //
362
SamplerCache(VkDevice device)363 SamplerCache::SamplerCache(VkDevice device)
364 : cache_mutex_{}, device_(device), cache_{} {}
365
SamplerCache(SamplerCache && other)366 SamplerCache::SamplerCache(SamplerCache&& other) noexcept
367 : cache_mutex_{}, device_(other.device_), cache_(std::move(other.cache_)) {
368 std::lock_guard<std::mutex> lock(other.cache_mutex_);
369 }
370
~SamplerCache()371 SamplerCache::~SamplerCache() {
372 purge();
373 }
374
retrieve(const SamplerCache::Key & key)375 VkSampler SamplerCache::retrieve(const SamplerCache::Key& key) {
376 std::lock_guard<std::mutex> lock(cache_mutex_);
377
378 auto it = cache_.find(key);
379 if (cache_.cend() == it) {
380 it = cache_.insert({key, SamplerCache::Value(device_, key)}).first;
381 }
382
383 return it->second.handle();
384 }
385
purge()386 void SamplerCache::purge() {
387 std::lock_guard<std::mutex> lock(cache_mutex_);
388 cache_.clear();
389 }
390
391 } // namespace vkapi
392 } // namespace vkcompute
393