blob: 663267410b92f12aa9c8515e5cc2ed18699f4d87 [file] [log] [blame]
Jeremy Gebben6c771d22022-01-01 12:30:15 -07001/* Copyright (c) 2015-2022 The Khronos Group Inc.
2 * Copyright (c) 2015-2022 Valve Corporation
3 * Copyright (c) 2015-2022 LunarG, Inc.
4 * Copyright (C) 2015-2022 Google Inc.
Jeremy Gebben1dfbd172021-05-19 14:00:58 -06005 * Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved.
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 * Author: Courtney Goeltzenleuchter <courtneygo@google.com>
20 * Author: Tobin Ehlis <tobine@google.com>
21 * Author: Chris Forbes <chrisf@ijw.co.nz>
22 * Author: Mark Lobodzinski <mark@lunarg.com>
23 * Author: Dave Houlton <daveh@lunarg.com>
24 * Author: John Zulauf <jzulauf@lunarg.com>
25 * Author: Tobias Hector <tobias.hector@amd.com>
26 * Author: Jeremy Gebben <jeremyg@lunarg.com>
27 */
28#include "image_state.h"
Jeremy Gebben5d970742021-05-31 16:04:14 -060029#include "pipeline_state.h"
Jeremy Gebben1dfbd172021-05-19 14:00:58 -060030#include "descriptor_sets.h"
Jeremy Gebben62c3bf42021-07-21 15:38:24 -060031#include "state_tracker.h"
Jeremy Gebben4295bdf2021-09-09 12:34:07 -060032#include <limits>
Jeremy Gebben1dfbd172021-05-19 14:00:58 -060033
34static VkImageSubresourceRange MakeImageFullRange(const VkImageCreateInfo &create_info) {
35 const auto format = create_info.format;
36 VkImageSubresourceRange init_range{0, 0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS};
37
38#ifdef VK_USE_PLATFORM_ANDROID_KHR
39 const VkExternalFormatANDROID *external_format_android = LvlFindInChain<VkExternalFormatANDROID>(&create_info);
40 bool is_external_format_conversion = (external_format_android != nullptr && external_format_android->externalFormat != 0);
41#else
42 bool is_external_format_conversion = false;
43#endif
44
45 if (FormatIsColor(format) || FormatIsMultiplane(format) || is_external_format_conversion) {
46 init_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; // Normalization will expand this for multiplane
47 } else {
48 init_range.aspectMask =
49 (FormatHasDepth(format) ? VK_IMAGE_ASPECT_DEPTH_BIT : 0) | (FormatHasStencil(format) ? VK_IMAGE_ASPECT_STENCIL_BIT : 0);
50 }
51 return NormalizeSubresourceRange(create_info, init_range);
52}
53
Jeremy Gebben11a68a32021-07-29 11:59:22 -060054static uint32_t ResolveRemainingLevels(const VkImageSubresourceRange *range, uint32_t mip_levels) {
Jeremy Gebben1dfbd172021-05-19 14:00:58 -060055 // Return correct number of mip levels taking into account VK_REMAINING_MIP_LEVELS
56 uint32_t mip_level_count = range->levelCount;
57 if (range->levelCount == VK_REMAINING_MIP_LEVELS) {
58 mip_level_count = mip_levels - range->baseMipLevel;
59 }
60 return mip_level_count;
61}
62
Jeremy Gebben11a68a32021-07-29 11:59:22 -060063static uint32_t ResolveRemainingLayers(const VkImageSubresourceRange *range, uint32_t layers) {
Jeremy Gebben1dfbd172021-05-19 14:00:58 -060064 // Return correct number of layers taking into account VK_REMAINING_ARRAY_LAYERS
65 uint32_t array_layer_count = range->layerCount;
66 if (range->layerCount == VK_REMAINING_ARRAY_LAYERS) {
67 array_layer_count = layers - range->baseArrayLayer;
68 }
69 return array_layer_count;
70}
71
72VkImageSubresourceRange NormalizeSubresourceRange(const VkImageCreateInfo &image_create_info,
73 const VkImageSubresourceRange &range) {
74 VkImageSubresourceRange norm = range;
75 norm.levelCount = ResolveRemainingLevels(&range, image_create_info.mipLevels);
Jeremy Gebben11a68a32021-07-29 11:59:22 -060076 norm.layerCount = ResolveRemainingLayers(&range, image_create_info.arrayLayers);
Jeremy Gebben1dfbd172021-05-19 14:00:58 -060077
78 // For multiplanar formats, IMAGE_ASPECT_COLOR is equivalent to adding the aspect of the individual planes
Jeremy Gebben1dfbd172021-05-19 14:00:58 -060079 if (FormatIsMultiplane(image_create_info.format)) {
Jeremy Gebben11a68a32021-07-29 11:59:22 -060080 if (norm.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) {
81 norm.aspectMask &= ~VK_IMAGE_ASPECT_COLOR_BIT;
82 norm.aspectMask |= (VK_IMAGE_ASPECT_PLANE_0_BIT | VK_IMAGE_ASPECT_PLANE_1_BIT);
Jeremy Gebben1dfbd172021-05-19 14:00:58 -060083 if (FormatPlaneCount(image_create_info.format) > 2) {
Jeremy Gebben11a68a32021-07-29 11:59:22 -060084 norm.aspectMask |= VK_IMAGE_ASPECT_PLANE_2_BIT;
Jeremy Gebben1dfbd172021-05-19 14:00:58 -060085 }
86 }
87 }
88 return norm;
89}
90
Jeremy Gebben11a68a32021-07-29 11:59:22 -060091static bool IsDepthSliced(const VkImageCreateInfo &image_create_info, const VkImageViewCreateInfo &create_info) {
Jeremy Gebben7fad0db2022-08-02 13:52:09 -060092 auto kDepthSlicedFlags = VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT | VK_IMAGE_CREATE_2D_VIEW_COMPATIBLE_BIT_EXT;
93 return ((image_create_info.flags & kDepthSlicedFlags) != 0) &&
Jeremy Gebben11a68a32021-07-29 11:59:22 -060094 (create_info.viewType == VK_IMAGE_VIEW_TYPE_2D || create_info.viewType == VK_IMAGE_VIEW_TYPE_2D_ARRAY);
95}
96
97VkImageSubresourceRange NormalizeSubresourceRange(const VkImageCreateInfo &image_create_info,
98 const VkImageViewCreateInfo &create_info) {
99 auto subres_range = create_info.subresourceRange;
100
101 // if we're mapping a 3D image to a 2d image view, convert the view's subresource range to be compatible with the
102 // image's understanding of the world. From the VkImageSubresourceRange section of the Vulkan spec:
103 //
104 // When the VkImageSubresourceRange structure is used to select a subset of the slices of a 3D image’s mip level in
105 // order to create a 2D or 2D array image view of a 3D image created with VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT,
106 // baseArrayLayer and layerCount specify the first slice index and the number of slices to include in the created
107 // image view. Such an image view can be used as a framebuffer attachment that refers only to the specified range
108 // of slices of the selected mip level. However, any layout transitions performed on such an attachment view during
109 // a render pass instance still apply to the entire subresource referenced which includes all the slices of the
110 // selected mip level.
111 //
112 if (IsDepthSliced(image_create_info, create_info)) {
113 subres_range.baseArrayLayer = 0;
114 subres_range.layerCount = 1;
115 }
116 return NormalizeSubresourceRange(image_create_info, subres_range);
117}
118
Jeremy Gebben6fbf8242021-06-21 09:14:46 -0600119static VkExternalMemoryHandleTypeFlags GetExternalHandleType(const VkImageCreateInfo *pCreateInfo) {
Jeremy Gebbenb4d17012021-07-08 13:18:15 -0600120 const auto *external_memory_info = LvlFindInChain<VkExternalMemoryImageCreateInfo>(pCreateInfo->pNext);
Jeremy Gebben6fbf8242021-06-21 09:14:46 -0600121 return external_memory_info ? external_memory_info->handleTypes : 0;
122}
123
Jeremy Gebbenb4d17012021-07-08 13:18:15 -0600124static VkSwapchainKHR GetSwapchain(const VkImageCreateInfo *pCreateInfo) {
125 const auto *swapchain_info = LvlFindInChain<VkImageSwapchainCreateInfoKHR>(pCreateInfo->pNext);
126 return swapchain_info ? swapchain_info->swapchain : VK_NULL_HANDLE;
127}
128
129#ifdef VK_USE_PLATFORM_ANDROID_KHR
130static uint64_t GetExternalFormat(const VkImageCreateInfo *info) {
131 const VkExternalFormatANDROID *ext_format_android = LvlFindInChain<VkExternalFormatANDROID>(info->pNext);
132 return ext_format_android ? ext_format_android->externalFormat : 0;
133}
134#else
135static uint64_t GetExternalFormat(const VkImageCreateInfo *info) { return 0; }
136#endif // VK_USE_PLATFORM_ANDROID_KHR
137
Jeremy Gebbenbc9aacf2021-09-23 11:57:37 -0600138static IMAGE_STATE::MemoryReqs GetMemoryRequirements(const ValidationStateTracker *dev_data, VkImage img,
139 const VkImageCreateInfo *create_info, bool disjoint, bool is_external_ahb) {
140 IMAGE_STATE::MemoryReqs result{};
141 // Record the memory requirements in case they won't be queried
142 // External AHB memory can't be queried until after memory is bound
143 if (!is_external_ahb) {
144 if (disjoint == false) {
145 DispatchGetImageMemoryRequirements(dev_data->device, img, &result[0]);
146 } else {
147 uint32_t plane_count = FormatPlaneCount(create_info->format);
148 static const std::array<VkImageAspectFlagBits, 3> aspects{VK_IMAGE_ASPECT_PLANE_0_BIT, VK_IMAGE_ASPECT_PLANE_1_BIT,
149 VK_IMAGE_ASPECT_PLANE_2_BIT};
150 assert(plane_count <= aspects.size());
151 auto image_plane_req = lvl_init_struct<VkImagePlaneMemoryRequirementsInfo>();
152 auto mem_req_info2 = lvl_init_struct<VkImageMemoryRequirementsInfo2>(&image_plane_req);
153 mem_req_info2.image = img;
154
155 for (uint32_t i = 0; i < plane_count; i++) {
156 auto mem_reqs2 = lvl_init_struct<VkMemoryRequirements2>();
157
158 image_plane_req.planeAspect = aspects[i];
159 switch (dev_data->device_extensions.vk_khr_get_memory_requirements2) {
160 case kEnabledByApiLevel:
161 DispatchGetImageMemoryRequirements2(dev_data->device, &mem_req_info2, &mem_reqs2);
162 break;
163 case kEnabledByCreateinfo:
164 DispatchGetImageMemoryRequirements2KHR(dev_data->device, &mem_req_info2, &mem_reqs2);
165 break;
166 default:
167 // The VK_KHR_sampler_ycbcr_conversion extension requires VK_KHR_get_memory_requirements2,
168 // so validation of this vkCreateImage call should have already failed.
169 assert(false);
170 }
171 result[i] = mem_reqs2.memoryRequirements;
172 }
173 }
174 }
175 return result;
176}
177
178static IMAGE_STATE::SparseReqs GetSparseRequirements(const ValidationStateTracker *dev_data, VkImage img,
179 const VkImageCreateInfo *create_info) {
180 IMAGE_STATE::SparseReqs result;
181 if (create_info->flags & VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT) {
182 uint32_t count = 0;
183 DispatchGetImageSparseMemoryRequirements(dev_data->device, img, &count, nullptr);
184 result.resize(count);
185 DispatchGetImageSparseMemoryRequirements(dev_data->device, img, &count, result.data());
186 }
187 return result;
188}
189
190static bool SparseMetaDataRequired(const IMAGE_STATE::SparseReqs &sparse_reqs) {
191 bool result = false;
192 for (const auto &req : sparse_reqs) {
193 if (req.formatProperties.aspectMask & VK_IMAGE_ASPECT_METADATA_BIT) {
194 result = true;
195 break;
196 }
197 }
198 return result;
199}
Tony-LunarG285dbdc2022-07-15 09:42:54 -0600200#ifdef VK_USE_PLATFORM_METAL_EXT
201static bool GetMetalExport(const VkImageCreateInfo *info, VkExportMetalObjectTypeFlagBitsEXT object_type_required) {
202 bool retval = false;
203 auto export_metal_object_info = LvlFindInChain<VkExportMetalObjectCreateInfoEXT>(info->pNext);
204 while (export_metal_object_info) {
205 if (export_metal_object_info->exportObjectType == object_type_required) {
206 retval = true;
207 break;
208 }
209 export_metal_object_info = LvlFindInChain<VkExportMetalObjectCreateInfoEXT>(export_metal_object_info->pNext);
210 }
211 return retval;
212}
213#endif // VK_USE_PLATFORM_METAL_EXT
Jeremy Gebbenbc9aacf2021-09-23 11:57:37 -0600214
215IMAGE_STATE::IMAGE_STATE(const ValidationStateTracker *dev_data, VkImage img, const VkImageCreateInfo *pCreateInfo,
Lionel Landwerlin21719f62021-12-09 11:40:09 +0200216 VkFormatFeatureFlags2KHR ff)
Jeremy Gebben6fbf8242021-06-21 09:14:46 -0600217 : BINDABLE(img, kVulkanObjectTypeImage, (pCreateInfo->flags & VK_IMAGE_CREATE_SPARSE_BINDING_BIT) != 0,
218 (pCreateInfo->flags & VK_IMAGE_CREATE_PROTECTED_BIT) == 0, GetExternalHandleType(pCreateInfo)),
Jeremy Gebben1dfbd172021-05-19 14:00:58 -0600219 safe_create_info(pCreateInfo),
220 createInfo(*safe_create_info.ptr()),
Jeremy Gebben1dfbd172021-05-19 14:00:58 -0600221 shared_presentable(false),
222 layout_locked(false),
Jeremy Gebbenb4d17012021-07-08 13:18:15 -0600223 ahb_format(GetExternalFormat(pCreateInfo)),
224 full_range{MakeImageFullRange(*pCreateInfo)},
225 create_from_swapchain(GetSwapchain(pCreateInfo)),
paul-lunarg4820fd12022-07-12 11:08:01 -0600226 owned_by_swapchain(false),
Jeremy Gebben62c3bf42021-07-21 15:38:24 -0600227 swapchain_image_index(0),
Jeremy Gebbenb4d17012021-07-08 13:18:15 -0600228 format_features(ff),
229 disjoint((pCreateInfo->flags & VK_IMAGE_CREATE_DISJOINT_BIT) != 0),
Jeremy Gebbenbc9aacf2021-09-23 11:57:37 -0600230 requirements(GetMemoryRequirements(dev_data, img, pCreateInfo, disjoint, IsExternalAHB())),
Jeremy Gebben6fbf8242021-06-21 09:14:46 -0600231 memory_requirements_checked{{false, false, false}},
Jeremy Gebbenbc9aacf2021-09-23 11:57:37 -0600232 sparse_requirements(GetSparseRequirements(dev_data, img, pCreateInfo)),
233 sparse_metadata_required(SparseMetaDataRequired(sparse_requirements)),
234 get_sparse_reqs_called(false),
235 sparse_metadata_bound(false),
Tony-LunarG285dbdc2022-07-15 09:42:54 -0600236#ifdef VK_USE_PLATFORM_METAL_EXT
237 metal_image_export(GetMetalExport(pCreateInfo, VK_EXPORT_METAL_OBJECT_TYPE_METAL_TEXTURE_BIT_EXT)),
238 metal_io_surface_export(GetMetalExport(pCreateInfo, VK_EXPORT_METAL_OBJECT_TYPE_METAL_IOSURFACE_BIT_EXT)),
239#endif // VK_USE_PLATFORM_METAL_EXT
Jeremy Gebben1dfbd172021-05-19 14:00:58 -0600240 subresource_encoder(full_range),
241 fragment_encoder(nullptr),
Jeremy Gebbenbc9aacf2021-09-23 11:57:37 -0600242 store_device_as_workaround(dev_data->device) {} // TODO REMOVE WHEN encoder can be const
Jeremy Gebbenb4d17012021-07-08 13:18:15 -0600243
Lionel Landwerlin21719f62021-12-09 11:40:09 +0200244IMAGE_STATE::IMAGE_STATE(const ValidationStateTracker *dev_data, VkImage img, const VkImageCreateInfo *pCreateInfo,
245 VkSwapchainKHR swapchain, uint32_t swapchain_index, VkFormatFeatureFlags2KHR ff)
Jeremy Gebbenb4d17012021-07-08 13:18:15 -0600246 : BINDABLE(img, kVulkanObjectTypeImage, (pCreateInfo->flags & VK_IMAGE_CREATE_SPARSE_BINDING_BIT) != 0,
247 (pCreateInfo->flags & VK_IMAGE_CREATE_PROTECTED_BIT) == 0, GetExternalHandleType(pCreateInfo)),
248 safe_create_info(pCreateInfo),
249 createInfo(*safe_create_info.ptr()),
Jeremy Gebbenb4d17012021-07-08 13:18:15 -0600250 shared_presentable(false),
251 layout_locked(false),
Jeremy Gebbenb4d17012021-07-08 13:18:15 -0600252 ahb_format(GetExternalFormat(pCreateInfo)),
253 full_range{MakeImageFullRange(*pCreateInfo)},
254 create_from_swapchain(swapchain),
paul-lunarg4820fd12022-07-12 11:08:01 -0600255 owned_by_swapchain(true),
Jeremy Gebben62c3bf42021-07-21 15:38:24 -0600256 swapchain_image_index(swapchain_index),
Jeremy Gebbenb4d17012021-07-08 13:18:15 -0600257 format_features(ff),
258 disjoint((pCreateInfo->flags & VK_IMAGE_CREATE_DISJOINT_BIT) != 0),
Jeremy Gebbenbc9aacf2021-09-23 11:57:37 -0600259 requirements{},
Jeremy Gebbenb4d17012021-07-08 13:18:15 -0600260 memory_requirements_checked{false, false, false},
Jeremy Gebbenbc9aacf2021-09-23 11:57:37 -0600261 sparse_requirements{},
262 sparse_metadata_required(false),
263 get_sparse_reqs_called(false),
264 sparse_metadata_bound(false),
Tony-LunarG285dbdc2022-07-15 09:42:54 -0600265#ifdef VK_USE_PLATFORM_METAL_EXT
266 metal_image_export(GetMetalExport(pCreateInfo, VK_EXPORT_METAL_OBJECT_TYPE_METAL_TEXTURE_BIT_EXT)),
267 metal_io_surface_export(GetMetalExport(pCreateInfo, VK_EXPORT_METAL_OBJECT_TYPE_METAL_IOSURFACE_BIT_EXT)),
268#endif // VK_USE_PLATFORM_METAL_EXT
Jeremy Gebbenb4d17012021-07-08 13:18:15 -0600269 subresource_encoder(full_range),
270 fragment_encoder(nullptr),
Jeremy Gebbenbc9aacf2021-09-23 11:57:37 -0600271 store_device_as_workaround(dev_data->device) { // TODO REMOVE WHEN encoder can be const
Jeremy Gebbenb4d17012021-07-08 13:18:15 -0600272 fragment_encoder =
273 std::unique_ptr<const subresource_adapter::ImageRangeEncoder>(new subresource_adapter::ImageRangeEncoder(*this));
Jeremy Gebben6fbf8242021-06-21 09:14:46 -0600274}
Jeremy Gebben1dfbd172021-05-19 14:00:58 -0600275
Jeremy Gebben941e7fa2021-07-26 10:21:00 -0600276void IMAGE_STATE::Destroy() {
Jeremy Gebben4a8881f2022-01-10 17:04:30 -0700277 // NOTE: due to corner cases in aliased images, the layout_range_map MUST not be cleaned up here.
278 // If it is, bad local entries could be created by CMD_BUFFER_STATE::GetImageSubresourceLayoutMap()
279 // If an aliasing image was being destroyed (and layout_range_map was reset()), a nullptr keyed
280 // entry could get put into CMD_BUFFER_STATE::aliased_image_layout_map.
Jeremy Gebben23cd4f22022-01-02 11:03:21 -0700281 if (bind_swapchain) {
282 bind_swapchain->RemoveParent(this);
283 bind_swapchain = nullptr;
284 }
Jeremy Gebben6fbf8242021-06-21 09:14:46 -0600285 BINDABLE::Destroy();
286}
Jeremy Gebben1dfbd172021-05-19 14:00:58 -0600287
Jeremy Gebbenc9a24032021-11-02 11:36:08 -0600288void IMAGE_STATE::NotifyInvalidate(const BASE_NODE::NodeList &invalid_nodes, bool unlink) {
289 BINDABLE::NotifyInvalidate(invalid_nodes, unlink);
Jeremy Gebben6fbf8242021-06-21 09:14:46 -0600290 if (unlink) {
Jeremy Gebben23cd4f22022-01-02 11:03:21 -0700291 bind_swapchain = nullptr;
Jeremy Gebben1dfbd172021-05-19 14:00:58 -0600292 }
293}
294
295bool IMAGE_STATE::IsCreateInfoEqual(const VkImageCreateInfo &other_createInfo) const {
296 bool is_equal = (createInfo.sType == other_createInfo.sType) && (createInfo.flags == other_createInfo.flags);
297 is_equal = is_equal && IsImageTypeEqual(other_createInfo) && IsFormatEqual(other_createInfo);
298 is_equal = is_equal && IsMipLevelsEqual(other_createInfo) && IsArrayLayersEqual(other_createInfo);
299 is_equal = is_equal && IsUsageEqual(other_createInfo) && IsInitialLayoutEqual(other_createInfo);
300 is_equal = is_equal && IsExtentEqual(other_createInfo) && IsTilingEqual(other_createInfo);
301 is_equal = is_equal && IsSamplesEqual(other_createInfo) && IsSharingModeEqual(other_createInfo);
302 return is_equal &&
303 ((createInfo.sharingMode == VK_SHARING_MODE_CONCURRENT) ? IsQueueFamilyIndicesEqual(other_createInfo) : true);
304}
305
306// Check image compatibility rules for VK_NV_dedicated_allocation_image_aliasing
307bool IMAGE_STATE::IsCreateInfoDedicatedAllocationImageAliasingCompatible(const VkImageCreateInfo &other_createInfo) const {
308 bool is_compatible = (createInfo.sType == other_createInfo.sType) && (createInfo.flags == other_createInfo.flags);
309 is_compatible = is_compatible && IsImageTypeEqual(other_createInfo) && IsFormatEqual(other_createInfo);
310 is_compatible = is_compatible && IsMipLevelsEqual(other_createInfo);
311 is_compatible = is_compatible && IsUsageEqual(other_createInfo) && IsInitialLayoutEqual(other_createInfo);
312 is_compatible = is_compatible && IsSamplesEqual(other_createInfo) && IsSharingModeEqual(other_createInfo);
313 is_compatible = is_compatible &&
314 ((createInfo.sharingMode == VK_SHARING_MODE_CONCURRENT) ? IsQueueFamilyIndicesEqual(other_createInfo) : true);
315 is_compatible = is_compatible && IsTilingEqual(other_createInfo);
316
317 is_compatible = is_compatible && createInfo.extent.width <= other_createInfo.extent.width &&
318 createInfo.extent.height <= other_createInfo.extent.height &&
319 createInfo.extent.depth <= other_createInfo.extent.depth &&
320 createInfo.arrayLayers <= other_createInfo.arrayLayers;
321 return is_compatible;
322}
323
324bool IMAGE_STATE::IsCompatibleAliasing(IMAGE_STATE *other_image_state) const {
Jeremy Gebben82e11d52021-07-26 09:19:37 -0600325 if (!IsSwapchainImage() && !other_image_state->IsSwapchainImage() &&
Jeremy Gebben1dfbd172021-05-19 14:00:58 -0600326 !(createInfo.flags & other_image_state->createInfo.flags & VK_IMAGE_CREATE_ALIAS_BIT)) {
327 return false;
328 }
Jeremy Gebben6fbf8242021-06-21 09:14:46 -0600329 const auto binding = Binding();
330 const auto other_binding = other_image_state->Binding();
Aitor Camacho3294edd2022-05-16 22:34:19 +0200331 if ((create_from_swapchain == VK_NULL_HANDLE) && binding && other_binding &&
332 (binding->memory_state == other_binding->memory_state) && (binding->memory_offset == other_binding->memory_offset) &&
333 IsCreateInfoEqual(other_image_state->createInfo)) {
Jeremy Gebben1dfbd172021-05-19 14:00:58 -0600334 return true;
335 }
Jeremy Gebben4a8881f2022-01-10 17:04:30 -0700336 if (bind_swapchain && (bind_swapchain == other_image_state->bind_swapchain) &&
337 (swapchain_image_index == other_image_state->swapchain_image_index)) {
Jeremy Gebben1dfbd172021-05-19 14:00:58 -0600338 return true;
339 }
340 return false;
341}
342
Jeremy Gebben4a8881f2022-01-10 17:04:30 -0700343void IMAGE_STATE::SetInitialLayoutMap() {
344 if (layout_range_map) {
345 return;
Jeremy Gebben1dfbd172021-05-19 14:00:58 -0600346 }
Jeremy Gebben62c3bf42021-07-21 15:38:24 -0600347 if ((createInfo.flags & VK_IMAGE_CREATE_ALIAS_BIT) != 0) {
Jeremy Gebben4a8881f2022-01-10 17:04:30 -0700348 // Look for another aliasing image and point at its layout state.
349 // ObjectBindings() is thread safe since returns by value, and once
350 // the weak_ptr is successfully locked, the other image state won't
351 // be freed out from under us.
Aitor Camachoa5976602022-06-03 21:00:39 +0200352 for (auto const &memory_state : GetBoundMemoryStates()) {
353 for (auto &entry : memory_state->ObjectBindings()) {
354 if (entry.first.type == kVulkanObjectTypeImage) {
355 auto base_node = entry.second.lock();
356 if (base_node) {
357 auto other_image = static_cast<IMAGE_STATE *>(base_node.get());
358 if (other_image != this && other_image->IsCompatibleAliasing(this)) {
359 layout_range_map = other_image->layout_range_map;
360 break;
361 }
Jeremy Gebben4a8881f2022-01-10 17:04:30 -0700362 }
363 }
364 }
365 }
366 } else if (bind_swapchain) {
367 // Swapchains can also alias if multiple images are bound (or retrieved
368 // with vkGetSwapchainImages()) for a (single swapchain, index) pair.
369 // ObjectBindings() is thread safe since returns by value, and once
370 // the weak_ptr is successfully locked, the other image state won't
371 // be freed out from under us.
372 for (auto &entry : bind_swapchain->ObjectBindings()) {
373 if (entry.first.type == kVulkanObjectTypeImage) {
374 auto base_node = entry.second.lock();
375 if (base_node) {
376 auto other_image = static_cast<IMAGE_STATE *>(base_node.get());
377 if (other_image != this && other_image->IsCompatibleAliasing(this)) {
378 layout_range_map = other_image->layout_range_map;
379 break;
380 }
Jeremy Gebben610d3a62022-01-01 12:53:17 -0700381 }
Jeremy Gebben62c3bf42021-07-21 15:38:24 -0600382 }
383 }
384 }
Jeremy Gebben4a8881f2022-01-10 17:04:30 -0700385 // ... otherwise set up the new map.
386 if (!layout_range_map) {
387 // set up the new map completely before making it available
388 auto new_map = std::make_shared<GlobalImageLayoutRangeMap>(subresource_encoder.SubresourceCount());
389 auto range_gen = subresource_adapter::RangeGenerator(subresource_encoder);
390 for (; range_gen->non_empty(); ++range_gen) {
391 new_map->insert(new_map->end(), std::make_pair(*range_gen, createInfo.initialLayout));
392 }
393 layout_range_map = std::move(new_map);
394 }
Jeremy Gebben62c3bf42021-07-21 15:38:24 -0600395}
396
397void IMAGE_STATE::SetSwapchain(std::shared_ptr<SWAPCHAIN_NODE> &swapchain, uint32_t swapchain_index) {
Jeremy Gebben82e11d52021-07-26 09:19:37 -0600398 assert(IsSwapchainImage());
Jeremy Gebben62c3bf42021-07-21 15:38:24 -0600399 bind_swapchain = swapchain;
400 swapchain_image_index = swapchain_index;
401 bind_swapchain->AddParent(this);
Jeremy Gebben62c3bf42021-07-21 15:38:24 -0600402}
403
404VkDeviceSize IMAGE_STATE::GetFakeBaseAddress() const {
Jeremy Gebben82e11d52021-07-26 09:19:37 -0600405 if (!IsSwapchainImage()) {
Jeremy Gebben62c3bf42021-07-21 15:38:24 -0600406 return BINDABLE::GetFakeBaseAddress();
407 }
408 if (!bind_swapchain) {
409 return 0;
410 }
411 return bind_swapchain->images[swapchain_image_index].fake_base_address;
412}
413
Aitor Camacho089e1252022-05-24 21:57:47 +0200414VkExtent3D IMAGE_STATE::GetSubresourceExtent(VkImageAspectFlags aspect_mask, uint32_t mip_level) const {
Jeremy Gebben1dfbd172021-05-19 14:00:58 -0600415 // Return zero extent if mip level doesn't exist
Aitor Camacho089e1252022-05-24 21:57:47 +0200416 if (mip_level >= createInfo.mipLevels) {
Jeremy Gebben1dfbd172021-05-19 14:00:58 -0600417 return VkExtent3D{0, 0, 0};
418 }
419
420 // Don't allow mip adjustment to create 0 dim, but pass along a 0 if that's what subresource specified
421 VkExtent3D extent = createInfo.extent;
422
423 // If multi-plane, adjust per-plane extent
424 if (FormatIsMultiplane(createInfo.format)) {
Aitor Camacho089e1252022-05-24 21:57:47 +0200425 VkExtent2D divisors = FindMultiplaneExtentDivisors(createInfo.format, aspect_mask);
Jeremy Gebben1dfbd172021-05-19 14:00:58 -0600426 extent.width /= divisors.width;
427 extent.height /= divisors.height;
428 }
429
430 if (createInfo.flags & VK_IMAGE_CREATE_CORNER_SAMPLED_BIT_NV) {
Aitor Camacho089e1252022-05-24 21:57:47 +0200431 extent.width = (0 == extent.width ? 0 : std::max(2U, 1 + ((extent.width - 1) >> mip_level)));
432 extent.height = (0 == extent.height ? 0 : std::max(2U, 1 + ((extent.height - 1) >> mip_level)));
433 extent.depth = (0 == extent.depth ? 0 : std::max(2U, 1 + ((extent.depth - 1) >> mip_level)));
Jeremy Gebben1dfbd172021-05-19 14:00:58 -0600434 } else {
Aitor Camacho089e1252022-05-24 21:57:47 +0200435 extent.width = (0 == extent.width ? 0 : std::max(1U, extent.width >> mip_level));
436 extent.height = (0 == extent.height ? 0 : std::max(1U, extent.height >> mip_level));
437 extent.depth = (0 == extent.depth ? 0 : std::max(1U, extent.depth >> mip_level));
Jeremy Gebben1dfbd172021-05-19 14:00:58 -0600438 }
439
440 // Image arrays have an effective z extent that isn't diminished by mip level
441 if (VK_IMAGE_TYPE_3D != createInfo.imageType) {
442 extent.depth = createInfo.arrayLayers;
443 }
444
445 return extent;
446}
447
Aitor Camacho089e1252022-05-24 21:57:47 +0200448// Returns the effective extent of an image subresource, adjusted for mip level and array depth.
449VkExtent3D IMAGE_STATE::GetSubresourceExtent(const VkImageSubresourceLayers &subresource) const {
450 return GetSubresourceExtent(subresource.aspectMask, subresource.mipLevel);
451}
452
Jeremy Gebbenb4d17012021-07-08 13:18:15 -0600453static VkSamplerYcbcrConversion GetSamplerConversion(const VkImageViewCreateInfo *ci) {
454 auto *conversion_info = LvlFindInChain<VkSamplerYcbcrConversionInfo>(ci->pNext);
455 return conversion_info ? conversion_info->conversion : VK_NULL_HANDLE;
456}
457
458static VkImageUsageFlags GetInheritedUsage(const VkImageViewCreateInfo *ci, const IMAGE_STATE &image_state) {
459 auto usage_create_info = LvlFindInChain<VkImageViewUsageCreateInfo>(ci->pNext);
460 return (usage_create_info) ? usage_create_info->usage : image_state.createInfo.usage;
461}
462
Tony-LunarG69604c42021-11-22 16:00:12 -0700463static float GetImageViewMinLod(const VkImageViewCreateInfo* ci) {
464 auto image_view_min_lod = LvlFindInChain<VkImageViewMinLodCreateInfoEXT>(ci->pNext);
465 return (image_view_min_lod) ? image_view_min_lod->minLod : 0.0f;
466}
467
Tony-LunarGffb5b522022-06-15 15:49:27 -0600468#ifdef VK_USE_PLATFORM_METAL_EXT
469static bool GetMetalExport(const VkImageViewCreateInfo *info) {
470 bool retval = false;
471 auto export_metal_object_info = LvlFindInChain<VkExportMetalObjectCreateInfoEXT>(info->pNext);
472 while (export_metal_object_info) {
473 if (export_metal_object_info->exportObjectType == VK_EXPORT_METAL_OBJECT_TYPE_METAL_TEXTURE_BIT_EXT) {
474 retval = true;
475 break;
476 }
477 export_metal_object_info = LvlFindInChain<VkExportMetalObjectCreateInfoEXT>(export_metal_object_info->pNext);
478 }
479 return retval;
480}
481#endif // VK_USE_PLATFORM_METAL_EXT
482
Jeremy Gebbenb4d17012021-07-08 13:18:15 -0600483IMAGE_VIEW_STATE::IMAGE_VIEW_STATE(const std::shared_ptr<IMAGE_STATE> &im, VkImageView iv, const VkImageViewCreateInfo *ci,
Lionel Landwerlin21719f62021-12-09 11:40:09 +0200484 VkFormatFeatureFlags2KHR ff, const VkFilterCubicImageViewImageFormatPropertiesEXT &cubic_props)
Jeremy Gebben1dfbd172021-05-19 14:00:58 -0600485 : BASE_NODE(iv, kVulkanObjectTypeImageView),
ziga-lunarg857a6512022-04-08 01:08:18 +0200486 safe_create_info(ci),
487 create_info(*safe_create_info.ptr()),
Jeremy Gebben11a68a32021-07-29 11:59:22 -0600488 normalized_subresource_range(::NormalizeSubresourceRange(im->createInfo, *ci)),
Jeremy Gebben1dfbd172021-05-19 14:00:58 -0600489 range_generator(im->subresource_encoder, normalized_subresource_range),
Jeremy Gebbenb4d17012021-07-08 13:18:15 -0600490 samples(im->createInfo.samples),
491 // When the image has a external format the views format must be VK_FORMAT_UNDEFINED and it is required to use a sampler
492 // Ycbcr conversion. Thus we can't extract any meaningful information from the format parameter. As a Sampler Ycbcr
493 // conversion must be used the shader type is always float.
494 descriptor_format_bits(im->HasAHBFormat() ? static_cast<unsigned>(DESCRIPTOR_REQ_COMPONENT_TYPE_FLOAT)
495 : DescriptorRequirementsBitsFromFormat(ci->format)),
496 samplerConversion(GetSamplerConversion(ci)),
497 filter_cubic_props(cubic_props),
Tony-LunarG69604c42021-11-22 16:00:12 -0700498 min_lod(GetImageViewMinLod(ci)),
Jeremy Gebbenb4d17012021-07-08 13:18:15 -0600499 format_features(ff),
500 inherited_usage(GetInheritedUsage(ci, *im)),
Tony-LunarGffb5b522022-06-15 15:49:27 -0600501#ifdef VK_USE_PLATFORM_METAL_EXT
502 metal_imageview_export(GetMetalExport(ci)),
503#endif
Jeremy Gebben610d3a62022-01-01 12:53:17 -0700504 image_state(im) {}
Jeremy Gebbenb4d17012021-07-08 13:18:15 -0600505
506void IMAGE_VIEW_STATE::Destroy() {
Jeremy Gebben1dfbd172021-05-19 14:00:58 -0600507 if (image_state) {
Jeremy Gebbenb4d17012021-07-08 13:18:15 -0600508 image_state->RemoveParent(this);
509 image_state = nullptr;
510 }
511 BASE_NODE::Destroy();
512}
Jeremy Gebben1dfbd172021-05-19 14:00:58 -0600513
Jeremy Gebben11a68a32021-07-29 11:59:22 -0600514bool IMAGE_VIEW_STATE::IsDepthSliced() const { return ::IsDepthSliced(image_state->createInfo, create_info); }
515
516VkOffset3D IMAGE_VIEW_STATE::GetOffset() const {
517 VkOffset3D result = {0, 0, 0};
518 if (IsDepthSliced()) {
519 result.z = create_info.subresourceRange.baseArrayLayer;
520 }
521 return result;
522}
523
524VkExtent3D IMAGE_VIEW_STATE::GetExtent() const {
525 VkExtent3D result = image_state->createInfo.extent;
526 if (IsDepthSliced()) {
527 result.depth = create_info.subresourceRange.layerCount;
528 }
529 return result;
530}
531
ziga-lunargff6485c2021-10-08 18:18:32 +0200532uint32_t IMAGE_VIEW_STATE::GetAttachmentLayerCount() const {
533 if (create_info.subresourceRange.layerCount == VK_REMAINING_ARRAY_LAYERS && !IsDepthSliced()) {
534 return image_state->createInfo.arrayLayers;
535 }
536 return create_info.subresourceRange.layerCount;
537}
538
Jeremy Gebbenbcba6d32021-07-16 11:41:41 -0600539static safe_VkImageCreateInfo GetImageCreateInfo(const VkSwapchainCreateInfoKHR *pCreateInfo) {
Jeremy Gebbenb4d17012021-07-08 13:18:15 -0600540 auto image_ci = LvlInitStruct<VkImageCreateInfo>();
Jeremy Gebbenbcba6d32021-07-16 11:41:41 -0600541 // Pull out the format list only. This stack variable will get copied onto the heap
542 // by the 'safe' constructor used to build the return value below.
543 VkImageFormatListCreateInfo fmt_info;
544 auto chain_fmt_info = LvlFindInChain<VkImageFormatListCreateInfo>(pCreateInfo->pNext);
545 if (chain_fmt_info) {
546 fmt_info = *chain_fmt_info;
547 fmt_info.pNext = nullptr;
548 image_ci.pNext = &fmt_info;
549 } else {
550 image_ci.pNext = nullptr;
551 }
Jeremy Gebbenb4d17012021-07-08 13:18:15 -0600552 image_ci.flags = 0; // to be updated below
553 image_ci.imageType = VK_IMAGE_TYPE_2D;
554 image_ci.format = pCreateInfo->imageFormat;
555 image_ci.extent.width = pCreateInfo->imageExtent.width;
556 image_ci.extent.height = pCreateInfo->imageExtent.height;
557 image_ci.extent.depth = 1;
558 image_ci.mipLevels = 1;
559 image_ci.arrayLayers = pCreateInfo->imageArrayLayers;
560 image_ci.samples = VK_SAMPLE_COUNT_1_BIT;
561 image_ci.tiling = VK_IMAGE_TILING_OPTIMAL;
562 image_ci.usage = pCreateInfo->imageUsage;
563 image_ci.sharingMode = pCreateInfo->imageSharingMode;
564 image_ci.queueFamilyIndexCount = pCreateInfo->queueFamilyIndexCount;
565 image_ci.pQueueFamilyIndices = pCreateInfo->pQueueFamilyIndices;
566 image_ci.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
Jeremy Gebben1dfbd172021-05-19 14:00:58 -0600567
Jeremy Gebbenb4d17012021-07-08 13:18:15 -0600568 if (pCreateInfo->flags & VK_SWAPCHAIN_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT_KHR) {
569 image_ci.flags |= VK_IMAGE_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT;
570 }
571 if (pCreateInfo->flags & VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR) {
572 image_ci.flags |= VK_IMAGE_CREATE_PROTECTED_BIT;
573 }
574 if (pCreateInfo->flags & VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR) {
575 image_ci.flags |= (VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT | VK_IMAGE_CREATE_EXTENDED_USAGE_BIT);
576 }
Jeremy Gebbenbcba6d32021-07-16 11:41:41 -0600577 return safe_VkImageCreateInfo(&image_ci);
Jeremy Gebbenb4d17012021-07-08 13:18:15 -0600578}
579
Jeremy Gebben62c3bf42021-07-21 15:38:24 -0600580SWAPCHAIN_NODE::SWAPCHAIN_NODE(ValidationStateTracker *dev_data_, const VkSwapchainCreateInfoKHR *pCreateInfo,
581 VkSwapchainKHR swapchain)
Jeremy Gebbenb4d17012021-07-08 13:18:15 -0600582 : BASE_NODE(swapchain, kVulkanObjectTypeSwapchainKHR),
583 createInfo(pCreateInfo),
Jeremy Gebbenb4d17012021-07-08 13:18:15 -0600584 shared_presentable(VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR == pCreateInfo->presentMode ||
585 VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR == pCreateInfo->presentMode),
Jeremy Gebben62c3bf42021-07-21 15:38:24 -0600586 image_create_info(GetImageCreateInfo(pCreateInfo)),
Jeremy Gebben87b2e6b2021-10-20 11:21:40 -0600587 dev_data(dev_data_) {}
Jeremy Gebbenb4d17012021-07-08 13:18:15 -0600588
589void SWAPCHAIN_NODE::PresentImage(uint32_t image_index) {
590 if (image_index >= images.size()) return;
Jeremy Gebben4295bdf2021-09-09 12:34:07 -0600591 assert(acquired_images > 0);
592 acquired_images--;
ziga-lunarg69aa72f2022-03-29 15:24:35 +0200593 images[image_index].acquired = false;
Jeremy Gebben4295bdf2021-09-09 12:34:07 -0600594 if (shared_presentable) {
595 IMAGE_STATE *image_state = images[image_index].image_state;
596 if (image_state) {
Jeremy Gebbenb4d17012021-07-08 13:18:15 -0600597 image_state->layout_locked = true;
Jeremy Gebben1dfbd172021-05-19 14:00:58 -0600598 }
Jeremy Gebbenb4d17012021-07-08 13:18:15 -0600599 }
600}
601
602void SWAPCHAIN_NODE::AcquireImage(uint32_t image_index) {
603 if (image_index >= images.size()) return;
604
Jeremy Gebben4295bdf2021-09-09 12:34:07 -0600605 assert(acquired_images < std::numeric_limits<uint32_t>::max());
ziga-lunarg69aa72f2022-03-29 15:24:35 +0200606 acquired_images++;
607 images[image_index].acquired = true;
Jeremy Gebben4295bdf2021-09-09 12:34:07 -0600608 if (shared_presentable) {
609 IMAGE_STATE *image_state = images[image_index].image_state;
610 if (image_state) {
611 image_state->shared_presentable = shared_presentable;
612 }
Jeremy Gebbenb4d17012021-07-08 13:18:15 -0600613 }
614}
615
616void SWAPCHAIN_NODE::Destroy() {
617 for (auto &swapchain_image : images) {
Jeremy Gebben62c3bf42021-07-21 15:38:24 -0600618 if (swapchain_image.image_state) {
Jeremy Gebben8e599652021-10-20 12:10:33 -0600619 RemoveParent(swapchain_image.image_state);
Jeremy Gebben082a9832021-10-28 13:40:11 -0600620 dev_data->Destroy<IMAGE_STATE>(swapchain_image.image_state->image());
Jeremy Gebbenb4d17012021-07-08 13:18:15 -0600621 }
Jeremy Gebben62c3bf42021-07-21 15:38:24 -0600622 // NOTE: We don't have access to dev_data->fake_memory.Free() here, but it is currently a no-op
Jeremy Gebbenb4d17012021-07-08 13:18:15 -0600623 }
Jeremy Gebben5a542f52021-07-21 15:25:52 -0600624 images.clear();
625 if (surface) {
626 surface->RemoveParent(this);
627 surface = nullptr;
628 }
Jeremy Gebbenb4d17012021-07-08 13:18:15 -0600629 BASE_NODE::Destroy();
630}
631
Jeremy Gebbenc9a24032021-11-02 11:36:08 -0600632void SWAPCHAIN_NODE::NotifyInvalidate(const BASE_NODE::NodeList &invalid_nodes, bool unlink) {
633 BASE_NODE::NotifyInvalidate(invalid_nodes, unlink);
Jeremy Gebbenb4d17012021-07-08 13:18:15 -0600634 if (unlink) {
Jeremy Gebben5a542f52021-07-21 15:25:52 -0600635 surface = nullptr;
Jeremy Gebben1dfbd172021-05-19 14:00:58 -0600636 }
637}
Jeremy Gebben5a542f52021-07-21 15:25:52 -0600638
639void SURFACE_STATE::Destroy() {
640 if (swapchain) {
641 swapchain = nullptr;
642 }
643 BASE_NODE::Destroy();
644}
645
646void SURFACE_STATE::RemoveParent(BASE_NODE *parent_node) {
647 if (swapchain == parent_node) {
648 swapchain = nullptr;
649 }
650 BASE_NODE::RemoveParent(parent_node);
651}
Jeremy Gebben87b2e6b2021-10-20 11:21:40 -0600652
653void SURFACE_STATE::SetQueueSupport(VkPhysicalDevice phys_dev, uint32_t qfi, bool supported) {
Jeremy Gebben6c771d22022-01-01 12:30:15 -0700654 auto guard = Lock();
Jeremy Gebben87b2e6b2021-10-20 11:21:40 -0600655 assert(phys_dev);
656 GpuQueue key{phys_dev, qfi};
657 gpu_queue_support_[key] = supported;
658}
659
660bool SURFACE_STATE::GetQueueSupport(VkPhysicalDevice phys_dev, uint32_t qfi) const {
Jeremy Gebben6c771d22022-01-01 12:30:15 -0700661 auto guard = Lock();
Jeremy Gebben87b2e6b2021-10-20 11:21:40 -0600662 assert(phys_dev);
663 GpuQueue key{phys_dev, qfi};
664 auto iter = gpu_queue_support_.find(key);
665 if (iter != gpu_queue_support_.end()) {
666 return iter->second;
667 }
668 VkBool32 supported = VK_FALSE;
669 DispatchGetPhysicalDeviceSurfaceSupportKHR(phys_dev, qfi, surface(), &supported);
670 gpu_queue_support_[key] = (supported == VK_TRUE);
671 return supported == VK_TRUE;
672}
673
674void SURFACE_STATE::SetPresentModes(VkPhysicalDevice phys_dev, std::vector<VkPresentModeKHR> &&modes) {
Jeremy Gebben6c771d22022-01-01 12:30:15 -0700675 auto guard = Lock();
Jeremy Gebben87b2e6b2021-10-20 11:21:40 -0600676 assert(phys_dev);
677 present_modes_[phys_dev] = std::move(modes);
678}
679
680std::vector<VkPresentModeKHR> SURFACE_STATE::GetPresentModes(VkPhysicalDevice phys_dev) const {
Jeremy Gebben6c771d22022-01-01 12:30:15 -0700681 auto guard = Lock();
Jeremy Gebben87b2e6b2021-10-20 11:21:40 -0600682 assert(phys_dev);
683 auto iter = present_modes_.find(phys_dev);
684 if (iter != present_modes_.end()) {
685 return iter->second;
686 }
687 std::vector<VkPresentModeKHR> result;
688 uint32_t count = 0;
689 DispatchGetPhysicalDeviceSurfacePresentModesKHR(phys_dev, surface(), &count, nullptr);
690 result.resize(count);
691 DispatchGetPhysicalDeviceSurfacePresentModesKHR(phys_dev, surface(), &count, result.data());
692 return result;
693}
694
695void SURFACE_STATE::SetFormats(VkPhysicalDevice phys_dev, std::vector<VkSurfaceFormatKHR> &&fmts) {
Jeremy Gebben6c771d22022-01-01 12:30:15 -0700696 auto guard = Lock();
Jeremy Gebben87b2e6b2021-10-20 11:21:40 -0600697 assert(phys_dev);
698 formats_[phys_dev] = std::move(fmts);
699}
700
701std::vector<VkSurfaceFormatKHR> SURFACE_STATE::GetFormats(VkPhysicalDevice phys_dev) const {
Jeremy Gebben6c771d22022-01-01 12:30:15 -0700702 auto guard = Lock();
Jeremy Gebben87b2e6b2021-10-20 11:21:40 -0600703 assert(phys_dev);
704 auto iter = formats_.find(phys_dev);
705 if (iter != formats_.end()) {
706 return iter->second;
707 }
708 std::vector<VkSurfaceFormatKHR> result;
709 uint32_t count = 0;
710 DispatchGetPhysicalDeviceSurfaceFormatsKHR(phys_dev, surface(), &count, nullptr);
711 result.resize(count);
712 DispatchGetPhysicalDeviceSurfaceFormatsKHR(phys_dev, surface(), &count, result.data());
713 formats_[phys_dev] = result;
714 return result;
715}
716
717void SURFACE_STATE::SetCapabilities(VkPhysicalDevice phys_dev, const VkSurfaceCapabilitiesKHR &caps) {
Jeremy Gebben6c771d22022-01-01 12:30:15 -0700718 auto guard = Lock();
Jeremy Gebben87b2e6b2021-10-20 11:21:40 -0600719 assert(phys_dev);
720 capabilities_[phys_dev] = caps;
721}
722
723VkSurfaceCapabilitiesKHR SURFACE_STATE::GetCapabilities(VkPhysicalDevice phys_dev) const {
Jeremy Gebben6c771d22022-01-01 12:30:15 -0700724 auto guard = Lock();
Jeremy Gebben87b2e6b2021-10-20 11:21:40 -0600725 assert(phys_dev);
726 auto iter = capabilities_.find(phys_dev);
727 if (iter != capabilities_.end()) {
728 return iter->second;
729 }
730 VkSurfaceCapabilitiesKHR result{};
731 DispatchGetPhysicalDeviceSurfaceCapabilitiesKHR(phys_dev, surface(), &result);
732 capabilities_[phys_dev] = result;
733 return result;
734}