1 /* Copyright (c) 2015-2020 The Khronos Group Inc.
2 * Copyright (c) 2015-2020 Valve Corporation
3 * Copyright (c) 2015-2020 LunarG, Inc.
4 * Copyright (C) 2015-2020 Google Inc.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 * Author: Tobin Ehlis <tobine@google.com>
19 * John Zulauf <jzulauf@lunarg.com>
20 */
21 #ifndef CORE_VALIDATION_DESCRIPTOR_SETS_H_
22 #define CORE_VALIDATION_DESCRIPTOR_SETS_H_
23
24 #include "hash_vk_types.h"
25 #include "vk_layer_logging.h"
26 #include "vk_layer_utils.h"
27 #include "vk_safe_struct.h"
28 #include "vulkan/vk_layer.h"
29 #include "vk_object_types.h"
30 #include <map>
31 #include <memory>
32 #include <set>
33 #include <unordered_map>
34 #include <unordered_set>
35 #include <vector>
36
37 class CoreChecks;
38 class ValidationStateTracker;
39
40 // Descriptor Data structures
41 namespace cvdescriptorset {
42
43 // Utility structs/classes/types
44 // Index range for global indices below, end is exclusive, i.e. [start,end)
45 struct IndexRange {
IndexRangeIndexRange46 IndexRange(uint32_t start_in, uint32_t end_in) : start(start_in), end(end_in) {}
47 IndexRange() = default;
48 uint32_t start;
49 uint32_t end;
50 };
51
52 /*
53 * DescriptorSetLayoutDef/DescriptorSetLayout classes
54 *
55 * Overview - These two classes encapsulate the Vulkan VkDescriptorSetLayout data (layout).
56 * A layout consists of some number of bindings, each of which has a binding#, a
57 * type, descriptor count, stage flags, and pImmutableSamplers.
58
59 * The DescriptorSetLayoutDef represents a canonicalization of the input data and contains
60 * neither per handle or per device state. It is possible for different handles on
61 * different devices to share a common def. This is used and useful for quick compatibiltiy
62 * validation. The DescriptorSetLayout refers to a DescriptorSetLayoutDef and contains
63 * all per handle state.
64 *
65 * Index vs Binding - A layout is created with an array of VkDescriptorSetLayoutBinding
66 * where each array index will have a corresponding binding# that is defined in that struct.
67 * The binding#, then, is decoupled from VkDescriptorSetLayoutBinding index, which allows
68 * bindings to be defined out-of-order. This DescriptorSetLayout class, however, stores
69 * the bindings internally in-order. This is useful for operations which may "roll over"
70 * from a single binding to the next consecutive binding.
71 *
72 * Note that although the bindings are stored in-order, there still may be "gaps" in the
73 * binding#. For example, if the binding creation order is 8, 7, 10, 3, 4, then the
74 * internal binding array will have five entries stored in binding order 3, 4, 7, 8, 10.
75 * To process all of the bindings in a layout you can iterate from 0 to GetBindingCount()
76 * and use the Get*FromIndex() functions for each index. To just process a single binding,
77 * use the Get*FromBinding() functions.
78 *
79 * Global Index - The binding vector index has as many indices as there are bindings.
80 * This class also has the concept of a Global Index. For the global index functions,
81 * there are as many global indices as there are descriptors in the layout.
82 * For the global index, consider all of the bindings to be a flat array where
83 * descriptor 0 of of the lowest binding# is index 0 and each descriptor in the layout
84 * increments from there. So if the lowest binding# in this example had descriptorCount of
85 * 10, then the GlobalStartIndex of the 2nd lowest binding# will be 10 where 0-9 are the
86 * global indices for the lowest binding#.
87 */
88 class DescriptorSetLayoutDef {
89 public:
90 // Constructors and destructor
91 DescriptorSetLayoutDef(const VkDescriptorSetLayoutCreateInfo *p_create_info);
92 size_t hash() const;
93
GetTotalDescriptorCount()94 uint32_t GetTotalDescriptorCount() const { return descriptor_count_; };
GetDynamicDescriptorCount()95 uint32_t GetDynamicDescriptorCount() const { return dynamic_descriptor_count_; };
GetCreateFlags()96 VkDescriptorSetLayoutCreateFlags GetCreateFlags() const { return flags_; }
97 // For a given binding, return the number of descriptors in that binding and all successive bindings
GetBindingCount()98 uint32_t GetBindingCount() const { return binding_count_; };
99 // Non-empty binding numbers in order
GetSortedBindingSet()100 const std::set<uint32_t> &GetSortedBindingSet() const { return non_empty_bindings_; }
101 // Return true if given binding is present in this layout
HasBinding(const uint32_t binding)102 bool HasBinding(const uint32_t binding) const { return binding_to_index_map_.count(binding) > 0; };
103 // Return true if binding 1 beyond given exists and has same type, stageFlags & immutable sampler use
104 bool IsNextBindingConsistent(const uint32_t) const;
105 uint32_t GetIndexFromBinding(uint32_t binding) const;
106 // Various Get functions that can either be passed a binding#, which will
107 // be automatically translated into the appropriate index, or the index# can be passed in directly
GetMaxBinding()108 uint32_t GetMaxBinding() const { return bindings_[bindings_.size() - 1].binding; }
109 VkDescriptorSetLayoutBinding const *GetDescriptorSetLayoutBindingPtrFromIndex(const uint32_t) const;
GetDescriptorSetLayoutBindingPtrFromBinding(uint32_t binding)110 VkDescriptorSetLayoutBinding const *GetDescriptorSetLayoutBindingPtrFromBinding(uint32_t binding) const {
111 return GetDescriptorSetLayoutBindingPtrFromIndex(GetIndexFromBinding(binding));
112 }
GetBindings()113 const std::vector<safe_VkDescriptorSetLayoutBinding> &GetBindings() const { return bindings_; }
GetBindingInfoFromIndex(const uint32_t index)114 const VkDescriptorSetLayoutBinding *GetBindingInfoFromIndex(const uint32_t index) const { return bindings_[index].ptr(); }
GetBindingInfoFromBinding(const uint32_t binding)115 const VkDescriptorSetLayoutBinding *GetBindingInfoFromBinding(const uint32_t binding) const {
116 return GetBindingInfoFromIndex(GetIndexFromBinding(binding));
117 }
GetBindingFlags()118 const std::vector<VkDescriptorBindingFlagsEXT> &GetBindingFlags() const { return binding_flags_; }
119 uint32_t GetDescriptorCountFromIndex(const uint32_t) const;
GetDescriptorCountFromBinding(const uint32_t binding)120 uint32_t GetDescriptorCountFromBinding(const uint32_t binding) const {
121 return GetDescriptorCountFromIndex(GetIndexFromBinding(binding));
122 }
123 VkDescriptorType GetTypeFromIndex(const uint32_t) const;
GetTypeFromBinding(const uint32_t binding)124 VkDescriptorType GetTypeFromBinding(const uint32_t binding) const { return GetTypeFromIndex(GetIndexFromBinding(binding)); }
125 VkShaderStageFlags GetStageFlagsFromIndex(const uint32_t) const;
GetStageFlagsFromBinding(const uint32_t binding)126 VkShaderStageFlags GetStageFlagsFromBinding(const uint32_t binding) const {
127 return GetStageFlagsFromIndex(GetIndexFromBinding(binding));
128 }
129 VkDescriptorBindingFlagsEXT GetDescriptorBindingFlagsFromIndex(const uint32_t) const;
GetDescriptorBindingFlagsFromBinding(const uint32_t binding)130 VkDescriptorBindingFlagsEXT GetDescriptorBindingFlagsFromBinding(const uint32_t binding) const {
131 return GetDescriptorBindingFlagsFromIndex(GetIndexFromBinding(binding));
132 }
133 VkSampler const *GetImmutableSamplerPtrFromBinding(const uint32_t) const;
134 VkSampler const *GetImmutableSamplerPtrFromIndex(const uint32_t) const;
135 // For a given binding and array index, return the corresponding index into the dynamic offset array
GetDynamicOffsetIndexFromBinding(uint32_t binding)136 int32_t GetDynamicOffsetIndexFromBinding(uint32_t binding) const {
137 auto dyn_off = binding_to_dynamic_array_idx_map_.find(binding);
138 if (dyn_off == binding_to_dynamic_array_idx_map_.end()) {
139 assert(0); // Requesting dyn offset for invalid binding/array idx pair
140 return -1;
141 }
142 return dyn_off->second;
143 }
144 // For a particular binding, get the global index range
145 // This call should be guarded by a call to "HasBinding(binding)" to verify that the given binding exists
146 const IndexRange &GetGlobalIndexRangeFromBinding(const uint32_t) const;
147 const cvdescriptorset::IndexRange &GetGlobalIndexRangeFromIndex(uint32_t index) const;
148
149 // Helper function to get the next valid binding for a descriptor
150 uint32_t GetNextValidBinding(const uint32_t) const;
IsPushDescriptor()151 bool IsPushDescriptor() const { return GetCreateFlags() & VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR; };
152
153 struct BindingTypeStats {
154 uint32_t dynamic_buffer_count;
155 uint32_t non_dynamic_buffer_count;
156 uint32_t image_sampler_count;
157 };
GetBindingTypeStats()158 const BindingTypeStats &GetBindingTypeStats() const { return binding_type_stats_; }
159
160 private:
161 // Only the first three data members are used for hash and equality checks, the other members are derived from them, and are
162 // used to speed up the various lookups/queries/validations
163 VkDescriptorSetLayoutCreateFlags flags_;
164 std::vector<safe_VkDescriptorSetLayoutBinding> bindings_;
165 std::vector<VkDescriptorBindingFlagsEXT> binding_flags_;
166
167 // Convenience data structures for rapid lookup of various descriptor set layout properties
168 std::set<uint32_t> non_empty_bindings_; // Containing non-emtpy bindings in numerical order
169 std::unordered_map<uint32_t, uint32_t> binding_to_index_map_;
170 // The following map allows an non-iterative lookup of a binding from a global index...
171 std::vector<IndexRange> global_index_range_; // range is exclusive of .end
172 // For a given binding map to associated index in the dynamic offset array
173 std::unordered_map<uint32_t, uint32_t> binding_to_dynamic_array_idx_map_;
174
175 uint32_t binding_count_; // # of bindings in this layout
176 uint32_t descriptor_count_; // total # descriptors in this layout
177 uint32_t dynamic_descriptor_count_;
178 BindingTypeStats binding_type_stats_;
179 };
180
181 static inline bool operator==(const DescriptorSetLayoutDef &lhs, const DescriptorSetLayoutDef &rhs) {
182 bool result = (lhs.GetCreateFlags() == rhs.GetCreateFlags()) && (lhs.GetBindings() == rhs.GetBindings()) &&
183 (lhs.GetBindingFlags() == rhs.GetBindingFlags());
184 return result;
185 }
186
187 // Canonical dictionary of DSL definitions -- independent of device or handle
188 using DescriptorSetLayoutDict = hash_util::Dictionary<DescriptorSetLayoutDef, hash_util::HasHashMember<DescriptorSetLayoutDef>>;
189 using DescriptorSetLayoutId = DescriptorSetLayoutDict::Id;
190
191 class DescriptorSetLayout : public BASE_NODE {
192 public:
193 // Constructors and destructor
194 DescriptorSetLayout(const VkDescriptorSetLayoutCreateInfo *p_create_info, const VkDescriptorSetLayout layout);
HasBinding(const uint32_t binding)195 bool HasBinding(const uint32_t binding) const { return layout_id_->HasBinding(binding); }
196 // Return true if this layout is compatible with passed in layout from a pipelineLayout,
197 // else return false and update error_msg with description of incompatibility
198 // Return true if this layout is compatible with passed in layout
199 bool IsCompatible(DescriptorSetLayout const *rh_ds_layout) const;
200 // Straightforward Get functions
GetDescriptorSetLayout()201 VkDescriptorSetLayout GetDescriptorSetLayout() const { return layout_; };
GetLayoutDef()202 const DescriptorSetLayoutDef *GetLayoutDef() const { return layout_id_.get(); }
GetLayoutId()203 DescriptorSetLayoutId GetLayoutId() const { return layout_id_; }
GetTotalDescriptorCount()204 uint32_t GetTotalDescriptorCount() const { return layout_id_->GetTotalDescriptorCount(); };
GetDynamicDescriptorCount()205 uint32_t GetDynamicDescriptorCount() const { return layout_id_->GetDynamicDescriptorCount(); };
GetBindingCount()206 uint32_t GetBindingCount() const { return layout_id_->GetBindingCount(); };
GetCreateFlags()207 VkDescriptorSetLayoutCreateFlags GetCreateFlags() const { return layout_id_->GetCreateFlags(); }
208 bool IsNextBindingConsistent(const uint32_t) const;
GetIndexFromBinding(uint32_t binding)209 uint32_t GetIndexFromBinding(uint32_t binding) const { return layout_id_->GetIndexFromBinding(binding); }
210 // Various Get functions that can either be passed a binding#, which will
211 // be automatically translated into the appropriate index, or the index# can be passed in directly
GetMaxBinding()212 uint32_t GetMaxBinding() const { return layout_id_->GetMaxBinding(); }
GetDescriptorSetLayoutBindingPtrFromIndex(const uint32_t index)213 VkDescriptorSetLayoutBinding const *GetDescriptorSetLayoutBindingPtrFromIndex(const uint32_t index) const {
214 return layout_id_->GetDescriptorSetLayoutBindingPtrFromIndex(index);
215 }
GetDescriptorSetLayoutBindingPtrFromBinding(uint32_t binding)216 VkDescriptorSetLayoutBinding const *GetDescriptorSetLayoutBindingPtrFromBinding(uint32_t binding) const {
217 return layout_id_->GetDescriptorSetLayoutBindingPtrFromBinding(binding);
218 }
GetBindings()219 const std::vector<safe_VkDescriptorSetLayoutBinding> &GetBindings() const { return layout_id_->GetBindings(); }
GetSortedBindingSet()220 const std::set<uint32_t> &GetSortedBindingSet() const { return layout_id_->GetSortedBindingSet(); }
GetDescriptorCountFromIndex(const uint32_t index)221 uint32_t GetDescriptorCountFromIndex(const uint32_t index) const { return layout_id_->GetDescriptorCountFromIndex(index); }
GetDescriptorCountFromBinding(const uint32_t binding)222 uint32_t GetDescriptorCountFromBinding(const uint32_t binding) const {
223 return layout_id_->GetDescriptorCountFromBinding(binding);
224 }
GetTypeFromIndex(const uint32_t index)225 VkDescriptorType GetTypeFromIndex(const uint32_t index) const { return layout_id_->GetTypeFromIndex(index); }
GetTypeFromBinding(const uint32_t binding)226 VkDescriptorType GetTypeFromBinding(const uint32_t binding) const { return layout_id_->GetTypeFromBinding(binding); }
GetStageFlagsFromIndex(const uint32_t index)227 VkShaderStageFlags GetStageFlagsFromIndex(const uint32_t index) const { return layout_id_->GetStageFlagsFromIndex(index); }
GetStageFlagsFromBinding(const uint32_t binding)228 VkShaderStageFlags GetStageFlagsFromBinding(const uint32_t binding) const {
229 return layout_id_->GetStageFlagsFromBinding(binding);
230 }
GetDescriptorBindingFlagsFromIndex(const uint32_t index)231 VkDescriptorBindingFlagsEXT GetDescriptorBindingFlagsFromIndex(const uint32_t index) const {
232 return layout_id_->GetDescriptorBindingFlagsFromIndex(index);
233 }
GetDescriptorBindingFlagsFromBinding(const uint32_t binding)234 VkDescriptorBindingFlagsEXT GetDescriptorBindingFlagsFromBinding(const uint32_t binding) const {
235 return layout_id_->GetDescriptorBindingFlagsFromBinding(binding);
236 }
GetImmutableSamplerPtrFromBinding(const uint32_t binding)237 VkSampler const *GetImmutableSamplerPtrFromBinding(const uint32_t binding) const {
238 return layout_id_->GetImmutableSamplerPtrFromBinding(binding);
239 }
GetImmutableSamplerPtrFromIndex(const uint32_t index)240 VkSampler const *GetImmutableSamplerPtrFromIndex(const uint32_t index) const {
241 return layout_id_->GetImmutableSamplerPtrFromIndex(index);
242 }
243 // For a given binding and array index, return the corresponding index into the dynamic offset array
GetDynamicOffsetIndexFromBinding(uint32_t binding)244 int32_t GetDynamicOffsetIndexFromBinding(uint32_t binding) const {
245 return layout_id_->GetDynamicOffsetIndexFromBinding(binding);
246 }
247 // For a particular binding, get the global index range
248 // This call should be guarded by a call to "HasBinding(binding)" to verify that the given binding exists
GetGlobalIndexRangeFromBinding(const uint32_t binding)249 const IndexRange &GetGlobalIndexRangeFromBinding(const uint32_t binding) const {
250 return layout_id_->GetGlobalIndexRangeFromBinding(binding);
251 }
GetGlobalIndexRangeFromIndex(uint32_t index)252 const IndexRange &GetGlobalIndexRangeFromIndex(uint32_t index) const { return layout_id_->GetGlobalIndexRangeFromIndex(index); }
253
254 // Helper function to get the next valid binding for a descriptor
GetNextValidBinding(const uint32_t binding)255 uint32_t GetNextValidBinding(const uint32_t binding) const { return layout_id_->GetNextValidBinding(binding); }
IsPushDescriptor()256 bool IsPushDescriptor() const { return layout_id_->IsPushDescriptor(); }
IsVariableDescriptorCountFromIndex(uint32_t index)257 bool IsVariableDescriptorCountFromIndex(uint32_t index) const {
258 return !!(GetDescriptorBindingFlagsFromIndex(index) & VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT_EXT);
259 }
IsVariableDescriptorCount(uint32_t binding)260 bool IsVariableDescriptorCount(uint32_t binding) const {
261 return IsVariableDescriptorCountFromIndex(GetIndexFromBinding(binding));
262 }
263
264 using BindingTypeStats = DescriptorSetLayoutDef::BindingTypeStats;
GetBindingTypeStats()265 const BindingTypeStats &GetBindingTypeStats() const { return layout_id_->GetBindingTypeStats(); }
266
267 // Binding Iterator
268 class ConstBindingIterator {
269 public:
270 ConstBindingIterator() = delete;
271 ConstBindingIterator(const ConstBindingIterator &other) = default;
272 ConstBindingIterator &operator=(const ConstBindingIterator &rhs) = default;
273
ConstBindingIterator(const DescriptorSetLayout * layout)274 ConstBindingIterator(const DescriptorSetLayout *layout) : layout_(layout), index_(0) { assert(layout); }
ConstBindingIterator(const DescriptorSetLayout * layout,uint32_t binding)275 ConstBindingIterator(const DescriptorSetLayout *layout, uint32_t binding) : ConstBindingIterator(layout) {
276 index_ = layout->GetIndexFromBinding(binding);
277 }
278
GetDescriptorSetLayoutBindingPtr()279 VkDescriptorSetLayoutBinding const *GetDescriptorSetLayoutBindingPtr() const {
280 return layout_->GetDescriptorSetLayoutBindingPtrFromIndex(index_);
281 }
GetDescriptorCount()282 uint32_t GetDescriptorCount() const { return layout_->GetDescriptorCountFromIndex(index_); }
GetType()283 VkDescriptorType GetType() const { return layout_->GetTypeFromIndex(index_); }
GetStageFlags()284 VkShaderStageFlags GetStageFlags() const { return layout_->GetStageFlagsFromIndex(index_); }
285
GetDescriptorBindingFlags()286 VkDescriptorBindingFlagsEXT GetDescriptorBindingFlags() const {
287 return layout_->GetDescriptorBindingFlagsFromIndex(index_);
288 }
289
IsVariableDescriptorCount()290 bool IsVariableDescriptorCount() const { return layout_->IsVariableDescriptorCountFromIndex(index_); }
291
GetImmutableSamplerPtr()292 VkSampler const *GetImmutableSamplerPtr() const { return layout_->GetImmutableSamplerPtrFromIndex(index_); }
GetGlobalIndexRange()293 const IndexRange &GetGlobalIndexRange() const { return layout_->GetGlobalIndexRangeFromIndex(index_); }
GetIndex()294 uint32_t GetIndex() const { return index_; }
AtEnd()295 bool AtEnd() const { return index_ == layout_->GetBindingCount(); }
296
297 // Return index into dynamic offset array for given binding
GetDynamicOffsetIndex()298 int32_t GetDynamicOffsetIndex() const {
299 return layout_->GetDynamicOffsetIndexFromBinding(Binding()); // There is only binding mapped access in layout_
300 }
301
302 bool operator==(const ConstBindingIterator &rhs) { return (index_ = rhs.index_) && (layout_ == rhs.layout_); }
303
304 ConstBindingIterator &operator++() {
305 if (!AtEnd()) {
306 index_++;
307 }
308 return *this;
309 }
310
IsConsistent(const ConstBindingIterator & other)311 bool IsConsistent(const ConstBindingIterator &other) const {
312 if (AtEnd() || other.AtEnd()) {
313 return false;
314 }
315 const auto *binding_ci = GetDescriptorSetLayoutBindingPtr();
316 const auto *other_binding_ci = other.GetDescriptorSetLayoutBindingPtr();
317 assert((binding_ci != nullptr) && (other_binding_ci != nullptr));
318
319 if ((binding_ci->descriptorType != other_binding_ci->descriptorType) ||
320 (binding_ci->stageFlags != other_binding_ci->stageFlags) ||
321 (!hash_util::similar_for_nullity(binding_ci->pImmutableSamplers, other_binding_ci->pImmutableSamplers)) ||
322 (GetDescriptorBindingFlags() != other.GetDescriptorBindingFlags())) {
323 return false;
324 }
325 return true;
326 }
327
Layout()328 const DescriptorSetLayout *Layout() const { return layout_; }
Binding()329 uint32_t Binding() const { return layout_->GetBindings()[index_].binding; }
Next()330 ConstBindingIterator Next() {
331 ConstBindingIterator next(*this);
332 ++next;
333 return next;
334 }
335
336 private:
337 const DescriptorSetLayout *layout_;
338 uint32_t index_;
339 };
end()340 ConstBindingIterator end() const { return ConstBindingIterator(this, GetBindingCount()); }
341
342 private:
343 VkDescriptorSetLayout layout_;
344 DescriptorSetLayoutId layout_id_;
345 };
346
347 /*
348 * Descriptor classes
349 * Descriptor is an abstract base class from which 5 separate descriptor types are derived.
350 * This allows the WriteUpdate() and CopyUpdate() operations to be specialized per
351 * descriptor type, but all descriptors in a set can be accessed via the common Descriptor*.
352 */
353
354 // Slightly broader than type, each c++ "class" will has a corresponding "DescriptorClass"
355 enum DescriptorClass { PlainSampler, ImageSampler, Image, TexelBuffer, GeneralBuffer, InlineUniform, AccelerationStructure };
356
357 class Descriptor {
358 public:
~Descriptor()359 virtual ~Descriptor(){};
360 virtual void WriteUpdate(const ValidationStateTracker *dev_data, const VkWriteDescriptorSet *, const uint32_t) = 0;
361 virtual void CopyUpdate(const ValidationStateTracker *dev_data, const Descriptor *) = 0;
362 // Create binding between resources of this descriptor and given cb_node
363 virtual void UpdateDrawState(ValidationStateTracker *, CMD_BUFFER_STATE *) = 0;
GetClass()364 virtual DescriptorClass GetClass() const { return descriptor_class; };
365 // Special fast-path check for SamplerDescriptors that are immutable
IsImmutableSampler()366 virtual bool IsImmutableSampler() const { return false; };
367 // Check for dynamic descriptor type
IsDynamic()368 virtual bool IsDynamic() const { return false; };
369 // Check for storage descriptor type
IsStorage()370 virtual bool IsStorage() const { return false; };
371 bool updated; // Has descriptor been updated?
372 DescriptorClass descriptor_class;
373 };
374
375 // Return true if this layout is compatible with passed in layout from a pipelineLayout,
376 // else return false and update error_msg with description of incompatibility
377 bool VerifySetLayoutCompatibility(const debug_report_data *report_data, DescriptorSetLayout const *lh_ds_layout,
378 DescriptorSetLayout const *rh_ds_layout, std::string *error_msg);
379 bool ValidateDescriptorSetLayoutCreateInfo(const ValidationObject *val_obj, const VkDescriptorSetLayoutCreateInfo *create_info,
380 const bool push_descriptor_ext, const uint32_t max_push_descriptors,
381 const bool descriptor_indexing_ext,
382 const VkPhysicalDeviceVulkan12Features *core12_features,
383 const VkPhysicalDeviceInlineUniformBlockFeaturesEXT *inline_uniform_block_features,
384 const VkPhysicalDeviceInlineUniformBlockPropertiesEXT *inline_uniform_block_props,
385 const DeviceExtensions *device_extensions);
386
387 class SamplerDescriptor : public Descriptor {
388 public:
389 SamplerDescriptor(const ValidationStateTracker *dev_data, const VkSampler *);
390 void WriteUpdate(const ValidationStateTracker *dev_data, const VkWriteDescriptorSet *, const uint32_t) override;
391 void CopyUpdate(const ValidationStateTracker *dev_data, const Descriptor *) override;
392 void UpdateDrawState(ValidationStateTracker *, CMD_BUFFER_STATE *) override;
IsImmutableSampler()393 virtual bool IsImmutableSampler() const override { return immutable_; };
GetSampler()394 VkSampler GetSampler() const { return sampler_; }
GetSamplerState()395 const SAMPLER_STATE *GetSamplerState() const { return sampler_state_.get(); }
GetSamplerState()396 SAMPLER_STATE *GetSamplerState() { return sampler_state_.get(); }
397
398 private:
399 VkSampler sampler_;
400 bool immutable_;
401 std::shared_ptr<SAMPLER_STATE> sampler_state_;
402 };
403
404 class ImageSamplerDescriptor : public Descriptor {
405 public:
406 ImageSamplerDescriptor(const ValidationStateTracker *dev_data, const VkSampler *);
407 void WriteUpdate(const ValidationStateTracker *dev_data, const VkWriteDescriptorSet *, const uint32_t) override;
408 void CopyUpdate(const ValidationStateTracker *dev_data, const Descriptor *) override;
409 void UpdateDrawState(ValidationStateTracker *, CMD_BUFFER_STATE *) override;
IsImmutableSampler()410 virtual bool IsImmutableSampler() const override { return immutable_; };
GetSampler()411 VkSampler GetSampler() const { return sampler_; }
GetImageView()412 VkImageView GetImageView() const { return image_view_; }
GetImageViewState()413 const IMAGE_VIEW_STATE *GetImageViewState() const { return image_view_state_.get(); }
GetImageViewState()414 IMAGE_VIEW_STATE *GetImageViewState() { return image_view_state_.get(); }
GetImageLayout()415 VkImageLayout GetImageLayout() const { return image_layout_; }
GetSamplerState()416 const SAMPLER_STATE *GetSamplerState() const { return sampler_state_.get(); }
GetSamplerState()417 SAMPLER_STATE *GetSamplerState() { return sampler_state_.get(); }
418
419 private:
420 std::shared_ptr<SAMPLER_STATE> sampler_state_;
421 VkSampler sampler_;
422 bool immutable_;
423 std::shared_ptr<IMAGE_VIEW_STATE> image_view_state_;
424 VkImageView image_view_;
425 VkImageLayout image_layout_;
426 };
427
428 class ImageDescriptor : public Descriptor {
429 public:
430 ImageDescriptor(const VkDescriptorType);
431 void WriteUpdate(const ValidationStateTracker *dev_data, const VkWriteDescriptorSet *, const uint32_t) override;
432 void CopyUpdate(const ValidationStateTracker *dev_data, const Descriptor *) override;
433 void UpdateDrawState(ValidationStateTracker *, CMD_BUFFER_STATE *) override;
IsStorage()434 virtual bool IsStorage() const override { return storage_; }
GetImageView()435 VkImageView GetImageView() const { return image_view_; }
GetImageViewState()436 const IMAGE_VIEW_STATE *GetImageViewState() const { return image_view_state_.get(); }
GetImageViewState()437 IMAGE_VIEW_STATE *GetImageViewState() { return image_view_state_.get(); }
GetImageLayout()438 VkImageLayout GetImageLayout() const { return image_layout_; }
439
440 private:
441 bool storage_;
442 std::shared_ptr<IMAGE_VIEW_STATE> image_view_state_;
443 VkImageView image_view_;
444 VkImageLayout image_layout_;
445 };
446
447 class TexelDescriptor : public Descriptor {
448 public:
449 TexelDescriptor(const VkDescriptorType);
450 void WriteUpdate(const ValidationStateTracker *dev_data, const VkWriteDescriptorSet *, const uint32_t) override;
451 void CopyUpdate(const ValidationStateTracker *dev_data, const Descriptor *) override;
452 void UpdateDrawState(ValidationStateTracker *, CMD_BUFFER_STATE *) override;
IsStorage()453 virtual bool IsStorage() const override { return storage_; }
GetBufferView()454 VkBufferView GetBufferView() const { return buffer_view_; }
GetBufferViewState()455 const BUFFER_VIEW_STATE *GetBufferViewState() const { return buffer_view_state_.get(); }
GetBufferViewState()456 BUFFER_VIEW_STATE *GetBufferViewState() { return buffer_view_state_.get(); }
457
458 private:
459 VkBufferView buffer_view_;
460 bool storage_;
461 std::shared_ptr<BUFFER_VIEW_STATE> buffer_view_state_;
462 };
463
464 class BufferDescriptor : public Descriptor {
465 public:
466 BufferDescriptor(const VkDescriptorType);
467 void WriteUpdate(const ValidationStateTracker *dev_data, const VkWriteDescriptorSet *, const uint32_t) override;
468 void CopyUpdate(const ValidationStateTracker *dev_data, const Descriptor *) override;
469 void UpdateDrawState(ValidationStateTracker *, CMD_BUFFER_STATE *) override;
IsDynamic()470 virtual bool IsDynamic() const override { return dynamic_; }
IsStorage()471 virtual bool IsStorage() const override { return storage_; }
GetBuffer()472 VkBuffer GetBuffer() const { return buffer_; }
GetBufferState()473 const BUFFER_STATE *GetBufferState() const { return buffer_state_.get(); }
GetBufferState()474 BUFFER_STATE *GetBufferState() { return buffer_state_.get(); }
GetOffset()475 VkDeviceSize GetOffset() const { return offset_; }
GetRange()476 VkDeviceSize GetRange() const { return range_; }
477
478 private:
479 bool storage_;
480 bool dynamic_;
481 VkBuffer buffer_;
482 VkDeviceSize offset_;
483 VkDeviceSize range_;
484 std::shared_ptr<BUFFER_STATE> buffer_state_;
485 };
486
487 class InlineUniformDescriptor : public Descriptor {
488 public:
InlineUniformDescriptor(const VkDescriptorType)489 InlineUniformDescriptor(const VkDescriptorType) {
490 updated = false;
491 descriptor_class = InlineUniform;
492 }
WriteUpdate(const ValidationStateTracker * dev_data,const VkWriteDescriptorSet *,const uint32_t)493 void WriteUpdate(const ValidationStateTracker *dev_data, const VkWriteDescriptorSet *, const uint32_t) override {
494 updated = true;
495 }
CopyUpdate(const ValidationStateTracker * dev_data,const Descriptor *)496 void CopyUpdate(const ValidationStateTracker *dev_data, const Descriptor *) override { updated = true; }
UpdateDrawState(ValidationStateTracker *,CMD_BUFFER_STATE *)497 void UpdateDrawState(ValidationStateTracker *, CMD_BUFFER_STATE *) override {}
498 };
499
500 class AccelerationStructureDescriptor : public Descriptor {
501 public:
502 AccelerationStructureDescriptor(const VkDescriptorType);
503 void WriteUpdate(const ValidationStateTracker *dev_data, const VkWriteDescriptorSet *, const uint32_t) override;
GetAccelerationStructure()504 VkAccelerationStructureKHR GetAccelerationStructure() const { return acc_; }
GetAccelerationStructureState()505 const ACCELERATION_STRUCTURE_STATE *GetAccelerationStructureState() const { return acc_state_.get(); }
GetAccelerationStructureState()506 ACCELERATION_STRUCTURE_STATE *GetAccelerationStructureState() { return acc_state_.get(); }
507 void CopyUpdate(const ValidationStateTracker *dev_data, const Descriptor *) override;
508 void UpdateDrawState(ValidationStateTracker *, CMD_BUFFER_STATE *) override;
509
510 private:
511 VkAccelerationStructureKHR acc_;
512 std::shared_ptr<ACCELERATION_STRUCTURE_STATE> acc_state_;
513 };
514
515 union AnyDescriptor {
516 SamplerDescriptor sampler;
517 ImageSamplerDescriptor image_sampler;
518 ImageDescriptor image;
519 TexelDescriptor texel;
520 BufferDescriptor buffer;
521 InlineUniformDescriptor inline_uniform;
522 AccelerationStructureDescriptor accelerator_structure;
523 ~AnyDescriptor() = delete;
524 };
525
alignas(alignof (AnyDescriptor))526 struct alignas(alignof(AnyDescriptor)) DescriptorBackingStore {
527 uint8_t data[sizeof(AnyDescriptor)];
528
529 SamplerDescriptor *Sampler() { return &(reinterpret_cast<AnyDescriptor *>(this)->sampler); }
530 ImageSamplerDescriptor *ImageSampler() { return &(reinterpret_cast<AnyDescriptor *>(this)->image_sampler); }
531 ImageDescriptor *Image() { return &(reinterpret_cast<AnyDescriptor *>(this)->image); }
532 TexelDescriptor *Texel() { return &(reinterpret_cast<AnyDescriptor *>(this)->texel); }
533 BufferDescriptor *Buffer() { return &(reinterpret_cast<AnyDescriptor *>(this)->buffer); }
534 InlineUniformDescriptor *InlineUniform() { return &(reinterpret_cast<AnyDescriptor *>(this)->inline_uniform); }
535 AccelerationStructureDescriptor *AccelerationStructure() {
536 return &(reinterpret_cast<AnyDescriptor *>(this)->accelerator_structure);
537 }
538 };
539
540 // Structs to contain common elements that need to be shared between Validate* and Perform* calls below
541 struct AllocateDescriptorSetsData {
542 std::map<uint32_t, uint32_t> required_descriptors_by_type;
543 std::vector<std::shared_ptr<DescriptorSetLayout const>> layout_nodes;
544 void Init(uint32_t);
AllocateDescriptorSetsDataAllocateDescriptorSetsData545 AllocateDescriptorSetsData(){};
546 };
547 // Helper functions for descriptor set functions that cross multiple sets
548 // "Validate" will make sure an update is ok without actually performing it
549 bool ValidateUpdateDescriptorSets(const debug_report_data *, const CoreChecks *, uint32_t, const VkWriteDescriptorSet *, uint32_t,
550 const VkCopyDescriptorSet *, const char *func_name);
551 // "Perform" does the update with the assumption that ValidateUpdateDescriptorSets() has passed for the given update
552 void PerformUpdateDescriptorSets(ValidationStateTracker *, uint32_t, const VkWriteDescriptorSet *, uint32_t,
553 const VkCopyDescriptorSet *);
554
555 // Core Validation specific validation checks using DescriptorSet and DescriptorSetLayoutAccessors
556 // TODO: migrate out of descriptor_set.cpp/h
557 // For a particular binding starting at offset and having update_count descriptors
558 // updated, verify that for any binding boundaries crossed, the update is consistent
559 bool VerifyUpdateConsistency(debug_report_data *report_data, DescriptorSetLayout::ConstBindingIterator current_binding,
560 uint32_t offset, uint32_t update_count, const char *type, const VkDescriptorSet set,
561 std::string *error_msg);
562
563 // Validate buffer descriptor update info
564 bool ValidateBufferUsage(debug_report_data *report_data, BUFFER_STATE const *buffer_node, VkDescriptorType type,
565 std::string *error_code, std::string *error_msg);
566
567 // Helper class to encapsulate the descriptor update template decoding logic
568 struct DecodedTemplateUpdate {
569 std::vector<VkWriteDescriptorSet> desc_writes;
570 std::vector<VkWriteDescriptorSetInlineUniformBlockEXT> inline_infos;
571 DecodedTemplateUpdate(const ValidationStateTracker *device_data, VkDescriptorSet descriptorSet,
572 const TEMPLATE_STATE *template_state, const void *pData,
573 VkDescriptorSetLayout push_layout = VK_NULL_HANDLE);
574 };
575
576 /*
577 * DescriptorSet class
578 *
579 * Overview - This class encapsulates the Vulkan VkDescriptorSet data (set).
580 * A set has an underlying layout which defines the bindings in the set and the
581 * types and numbers of descriptors in each descriptor slot. Most of the layout
582 * interfaces are exposed through identically-named functions in the set class.
583 * Please refer to the DescriptorSetLayout comment above for a description of
584 * index, binding, and global index.
585 *
586 * At construction a vector of Descriptor* is created with types corresponding to the
587 * layout. The primary operation performed on the descriptors is to update them
588 * via write or copy updates, and validate that the update contents are correct.
589 * In order to validate update contents, the DescriptorSet stores a bunch of ptrs
590 * to data maps where various Vulkan objects can be looked up. The management of
591 * those maps is performed externally. The set class relies on their contents to
592 * be correct at the time of update.
593 */
594 class DescriptorSet : public BASE_NODE {
595 public:
596 using StateTracker = ValidationStateTracker;
597 DescriptorSet(const VkDescriptorSet, DESCRIPTOR_POOL_STATE *, const std::shared_ptr<DescriptorSetLayout const> &,
598 uint32_t variable_count, const StateTracker *state_data_const);
599 ~DescriptorSet();
600 // A number of common Get* functions that return data based on layout from which this set was created
GetTotalDescriptorCount()601 uint32_t GetTotalDescriptorCount() const { return p_layout_->GetTotalDescriptorCount(); };
GetDynamicDescriptorCount()602 uint32_t GetDynamicDescriptorCount() const { return p_layout_->GetDynamicDescriptorCount(); };
GetBindingCount()603 uint32_t GetBindingCount() const { return p_layout_->GetBindingCount(); };
GetTypeFromIndex(const uint32_t index)604 VkDescriptorType GetTypeFromIndex(const uint32_t index) const { return p_layout_->GetTypeFromIndex(index); };
GetTypeFromBinding(const uint32_t binding)605 VkDescriptorType GetTypeFromBinding(const uint32_t binding) const { return p_layout_->GetTypeFromBinding(binding); };
GetDescriptorCountFromIndex(const uint32_t index)606 uint32_t GetDescriptorCountFromIndex(const uint32_t index) const { return p_layout_->GetDescriptorCountFromIndex(index); };
GetDescriptorCountFromBinding(const uint32_t binding)607 uint32_t GetDescriptorCountFromBinding(const uint32_t binding) const {
608 return p_layout_->GetDescriptorCountFromBinding(binding);
609 };
610 // Return index into dynamic offset array for given binding
GetDynamicOffsetIndexFromBinding(uint32_t binding)611 int32_t GetDynamicOffsetIndexFromBinding(uint32_t binding) const {
612 return p_layout_->GetDynamicOffsetIndexFromBinding(binding);
613 }
614 // Return true if given binding is present in this set
HasBinding(const uint32_t binding)615 bool HasBinding(const uint32_t binding) const { return p_layout_->HasBinding(binding); };
616
617 std::string StringifySetAndLayout() const;
618
619 // Perform a push update whose contents were just validated using ValidatePushDescriptorsUpdate
620 void PerformPushDescriptorsUpdate(ValidationStateTracker *dev_data, uint32_t write_count, const VkWriteDescriptorSet *p_wds);
621 // Perform a WriteUpdate whose contents were just validated using ValidateWriteUpdate
622 void PerformWriteUpdate(ValidationStateTracker *dev_data, const VkWriteDescriptorSet *);
623 // Perform a CopyUpdate whose contents were just validated using ValidateCopyUpdate
624 void PerformCopyUpdate(ValidationStateTracker *dev_data, const VkCopyDescriptorSet *, const DescriptorSet *);
625
GetLayout()626 const std::shared_ptr<DescriptorSetLayout const> &GetLayout() const { return p_layout_; };
GetDescriptorSetLayout()627 VkDescriptorSetLayout GetDescriptorSetLayout() const { return p_layout_->GetDescriptorSetLayout(); }
GetSet()628 VkDescriptorSet GetSet() const { return set_; };
629 // Bind given cmd_buffer to this descriptor set and
630 // update CB image layout map with image/imagesampler descriptor image layouts
631 void UpdateDrawState(ValidationStateTracker *, CMD_BUFFER_STATE *, CMD_TYPE cmd_type, const PIPELINE_STATE *,
632 const std::map<uint32_t, DescriptorReqirement> &, const char *function);
633
634 // Track work that has been bound or validated to avoid duplicate work, important when large descriptor arrays
635 // are present
636 typedef std::unordered_set<uint32_t> TrackedBindings;
637 static void FilterOneBindingReq(const BindingReqMap::value_type &binding_req_pair, BindingReqMap *out_req,
638 const TrackedBindings &set, uint32_t limit);
639 void FilterBindingReqs(const CMD_BUFFER_STATE &, const PIPELINE_STATE &, const BindingReqMap &in_req,
640 BindingReqMap *out_req) const;
641 void UpdateValidationCache(const CMD_BUFFER_STATE &cb_state, const PIPELINE_STATE &pipeline,
642 const BindingReqMap &updated_bindings);
ClearCachedDynamicDescriptorValidation(CMD_BUFFER_STATE * cb_state)643 void ClearCachedDynamicDescriptorValidation(CMD_BUFFER_STATE *cb_state) {
644 cached_validation_[cb_state].dynamic_buffers.clear();
645 }
ClearCachedValidation(CMD_BUFFER_STATE * cb_state)646 void ClearCachedValidation(CMD_BUFFER_STATE *cb_state) { cached_validation_.erase(cb_state); }
GetImmutableSamplerPtrFromBinding(const uint32_t index)647 VkSampler const *GetImmutableSamplerPtrFromBinding(const uint32_t index) const {
648 return p_layout_->GetImmutableSamplerPtrFromBinding(index);
649 };
650 // For a particular binding, get the global index
651 const IndexRange GetGlobalIndexRangeFromBinding(const uint32_t binding, bool actual_length = false) const {
652 if (actual_length && binding == p_layout_->GetMaxBinding() && IsVariableDescriptorCount(binding)) {
653 IndexRange range = p_layout_->GetGlobalIndexRangeFromBinding(binding);
654 auto diff = GetDescriptorCountFromBinding(binding) - GetVariableDescriptorCount();
655 range.end -= diff;
656 return range;
657 }
658 return p_layout_->GetGlobalIndexRangeFromBinding(binding);
659 };
660 // Return true if any part of set has ever been updated
IsUpdated()661 bool IsUpdated() const { return some_update_; };
IsPushDescriptor()662 bool IsPushDescriptor() const { return p_layout_->IsPushDescriptor(); };
IsVariableDescriptorCount(uint32_t binding)663 bool IsVariableDescriptorCount(uint32_t binding) const { return p_layout_->IsVariableDescriptorCount(binding); }
IsUpdateAfterBind(uint32_t binding)664 bool IsUpdateAfterBind(uint32_t binding) const {
665 return !!(p_layout_->GetDescriptorBindingFlagsFromBinding(binding) & VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT_EXT);
666 }
GetVariableDescriptorCount()667 uint32_t GetVariableDescriptorCount() const { return variable_count_; }
GetPoolState()668 DESCRIPTOR_POOL_STATE *GetPoolState() const { return pool_state_; }
GetDescriptorFromGlobalIndex(const uint32_t index)669 const Descriptor *GetDescriptorFromGlobalIndex(const uint32_t index) const { return descriptors_[index].get(); }
670 const Descriptor *GetDescriptorFromBinding(const uint32_t binding, const uint32_t index = 0) const {
671 const auto range = GetGlobalIndexRangeFromBinding(binding);
672 if ((range.start + index) > range.end) {
673 return nullptr;
674 }
675 return descriptors_[range.start + index].get();
676 }
GetChangeCount()677 uint64_t GetChangeCount() const { return change_count_; }
678
GetWrites()679 const std::vector<safe_VkWriteDescriptorSet> &GetWrites() const { return push_descriptor_set_writes; }
680
681 // Given that we are providing placement new allocation for descriptors, the deleter needs to *only* call the destructor
682 struct DescriptorDeleter {
operatorDescriptorDeleter683 void operator()(Descriptor *desc) { desc->~Descriptor(); }
684 };
685
686 private:
687 // Private helper to set all bound cmd buffers to INVALID state
688 void InvalidateBoundCmdBuffers(ValidationStateTracker *state_data);
689 bool some_update_; // has any part of the set ever been updated?
690 VkDescriptorSet set_;
691 DESCRIPTOR_POOL_STATE *pool_state_;
692 const std::shared_ptr<DescriptorSetLayout const> p_layout_;
693 // NOTE: the the backing store for the descriptors must be declared *before* it so it will be destructed *after* it
694 // "Destructors for nonstatic member objects are called in the reverse order in which they appear in the class declaration."
695 std::vector<DescriptorBackingStore> descriptor_store_;
696 std::vector<std::unique_ptr<Descriptor, DescriptorDeleter>> descriptors_;
697 const StateTracker *state_data_;
698 uint32_t variable_count_;
699 uint64_t change_count_;
700
701 // If this descriptor set is a push descriptor set, the descriptor
702 // set writes that were last pushed.
703 std::vector<safe_VkWriteDescriptorSet> push_descriptor_set_writes;
704
705 // Cached binding and validation support:
706 //
707 // For the lifespan of a given command buffer recording, do lazy evaluation, caching, and dirtying of
708 // expensive validation operation (typically per-draw)
709 typedef std::unordered_map<CMD_BUFFER_STATE *, TrackedBindings> TrackedBindingMap;
710 // Track the validation caching of bindings vs. the command buffer and draw state
711 typedef std::unordered_map<uint32_t, CMD_BUFFER_STATE::ImageLayoutUpdateCount> VersionedBindings;
712 struct CachedValidation {
713 TrackedBindings command_binding_and_usage; // Persistent for the life of the recording
714 TrackedBindings non_dynamic_buffers; // Persistent for the life of the recording
715 TrackedBindings dynamic_buffers; // Dirtied (flushed) each BindDescriptorSet
716 std::unordered_map<const PIPELINE_STATE *, VersionedBindings> image_samplers; // Tested vs. changes to CB's ImageLayout
717 };
718 typedef std::unordered_map<const CMD_BUFFER_STATE *, CachedValidation> CachedValidationMap;
719 // Image and ImageView bindings are validated per pipeline and not invalidate by repeated binding
720 CachedValidationMap cached_validation_;
721 };
722 // For the "bindless" style resource usage with many descriptors, need to optimize binding and validation
723 class PrefilterBindRequestMap {
724 public:
725 static const uint32_t kManyDescriptors_ = 64; // TODO base this number on measured data
726 std::unique_ptr<BindingReqMap> filtered_map_;
727 const BindingReqMap &orig_map_;
728 const DescriptorSet &descriptor_set_;
729
PrefilterBindRequestMap(const DescriptorSet & ds,const BindingReqMap & in_map)730 PrefilterBindRequestMap(const DescriptorSet &ds, const BindingReqMap &in_map)
731 : filtered_map_(), orig_map_(in_map), descriptor_set_(ds) {}
732 const BindingReqMap &FilteredMap(const CMD_BUFFER_STATE &cb_state, const PIPELINE_STATE &);
IsManyDescriptors()733 bool IsManyDescriptors() const { return descriptor_set_.GetTotalDescriptorCount() > kManyDescriptors_; }
734 };
735 } // namespace cvdescriptorset
736 #endif // CORE_VALIDATION_DESCRIPTOR_SETS_H_
737