Tobin Ehlis | 2d9deec | 2016-04-21 14:19:26 -0600 | [diff] [blame] | 1 | /* Copyright (c) 2015-2016 The Khronos Group Inc. |
| 2 | * Copyright (c) 2015-2016 Valve Corporation |
| 3 | * Copyright (c) 2015-2016 LunarG, Inc. |
| 4 | * Copyright (C) 2015-2016 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 | */ |
| 20 | #ifndef CORE_VALIDATION_DESCRIPTOR_SETS_H_ |
| 21 | #define CORE_VALIDATION_DESCRIPTOR_SETS_H_ |
| 22 | |
| 23 | // Check for noexcept support |
| 24 | #if defined(__clang__) |
| 25 | #if __has_feature(cxx_noexcept) |
| 26 | #define HAS_NOEXCEPT |
| 27 | #endif |
| 28 | #else |
| 29 | #if defined(__GXX_EXPERIMENTAL_CXX0X__) && __GNUC__ * 10 + __GNUC_MINOR__ >= 46 |
| 30 | #define HAS_NOEXCEPT |
| 31 | #else |
| 32 | #if defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023026 && defined(_HAS_EXCEPTIONS) && _HAS_EXCEPTIONS |
| 33 | #define HAS_NOEXCEPT |
| 34 | #endif |
| 35 | #endif |
| 36 | #endif |
| 37 | |
| 38 | #ifdef HAS_NOEXCEPT |
| 39 | #define NOEXCEPT noexcept |
| 40 | #else |
| 41 | #define NOEXCEPT |
| 42 | #endif |
| 43 | |
| 44 | #pragma once |
| 45 | #include "core_validation_error_enums.h" |
| 46 | #include "vk_layer_logging.h" |
| 47 | #include "vk_safe_struct.h" |
| 48 | #include "vulkan/vk_layer.h" |
| 49 | #include <unordered_map> |
| 50 | #include <vector> |
| 51 | |
| 52 | // Descriptor Data structures |
| 53 | |
| 54 | /* |
| 55 | * DescriptorSetLayout class |
| 56 | * |
| 57 | * Overview - This class encapsulates the Vulkan VkDescriptorSetLayout data (layout). |
| 58 | * A layout consists of some number of bindings, each of which has a binding#, a |
| 59 | * type, descriptor count, stage flags, and pImmutableSamplers. |
| 60 | * |
| 61 | * Index vs Binding - A layout is created with an array of VkDescriptorSetLayoutBinding |
| 62 | * where each array index will have a corresponding binding# that is defined in that struct. |
| 63 | * This class, therefore, provides utility functions where you can retrieve data for |
| 64 | * layout bindings based on either the original index into the pBindings array, or based |
| 65 | * on the binding#. |
| 66 | * Typically if you want to cover all of the bindings in a layout, you can do that by |
| 67 | * iterating over GetBindingCount() bindings and using the Get*FromIndex() functions. |
| 68 | * Otherwise, you can use the Get*FromBinding() functions to just grab binding info |
| 69 | * for a particular binding#. |
| 70 | * |
| 71 | * Global Index - The "Index" referenced above is the index into the original pBindings |
| 72 | * array. So there are as many indices as there are bindings. |
| 73 | * This class also has the concept of a Global Index. For the global index functions, |
| 74 | * there are as many global indices as there are descriptors in the layout. |
| 75 | * For the global index, consider all of the bindings to be a flat array where |
| 76 | * descriptor 0 of pBinding[0] is index 0 and each descriptor in the layout increments |
| 77 | * from there. So if pBinding[0] in this example had descriptorCount of 10, then |
| 78 | * the GlobalStartIndex of pBinding[1] will be 10 where 0-9 are the global indices |
| 79 | * for pBinding[0]. |
| 80 | */ |
| 81 | class DescriptorSetLayout { |
| 82 | public: |
| 83 | // Constructors and destructor |
| 84 | DescriptorSetLayout(); |
| 85 | DescriptorSetLayout(debug_report_data *report_data, const VkDescriptorSetLayoutCreateInfo *p_create_info, |
| 86 | const VkDescriptorSetLayout layout); |
| 87 | ~DescriptorSetLayout(); |
| 88 | // Straightforward Get functions |
| 89 | VkDescriptorSetLayout GetDescriptorSetLayout() { return layout_; }; |
| 90 | uint32_t GetTotalDescriptorCount() { return descriptor_count_; }; |
| 91 | uint32_t GetDynamicDescriptorCount() { return dynamic_descriptor_count_; }; |
| 92 | uint32_t GetBindingCount() { return binding_count_; }; |
| 93 | // Return true if given binding is present in this layout |
| 94 | bool HasBinding(const uint32_t binding) { return binding_to_index_map_.count(binding); }; |
| 95 | // Return true if this layout is compatible with passed in layout, |
| 96 | // else return false and update error_msg with description of incompatibility |
| 97 | bool IsCompatible(DescriptorSetLayout *, string *error_msg); |
| 98 | // Various Get functions that can either be passed a binding#, which will |
| 99 | // be automatically translated into the appropriate index from the original |
| 100 | // pBindings array, or the index# can be passed in directly |
| 101 | VkDescriptorSetLayoutBinding const *GetDescriptorSetLayoutBindingPtrFromBinding(const uint32_t); |
| 102 | VkDescriptorSetLayoutBinding const *GetDescriptorSetLayoutBindingPtrFromIndex(const uint32_t); |
| 103 | uint32_t GetDescriptorCountFromBinding(const uint32_t); |
| 104 | uint32_t GetDescriptorCountFromIndex(const uint32_t); |
| 105 | VkDescriptorType GetTypeFromBinding(const uint32_t); |
| 106 | VkDescriptorType GetTypeFromIndex(const uint32_t); |
Tobin Ehlis | 660aba4 | 2016-04-26 13:09:51 -0600 | [diff] [blame] | 107 | VkDescriptorType GetTypeFromGlobalIndex(const uint32_t); |
Tobin Ehlis | 2d9deec | 2016-04-21 14:19:26 -0600 | [diff] [blame] | 108 | VkShaderStageFlags GetStageFlagsFromBinding(const uint32_t); |
| 109 | VkSampler const *GetImmutableSamplerPtrFromBinding(const uint32_t); |
| 110 | // For a particular binding, get the global index |
| 111 | uint32_t GetGlobalStartIndexFromBinding(const uint32_t); |
| 112 | uint32_t GetGlobalEndIndexFromBinding(const uint32_t); |
| 113 | |
| 114 | private: |
| 115 | VkDescriptorSetLayout layout_; |
| 116 | unordered_map<uint32_t, uint32_t> binding_to_index_map_; |
| 117 | unordered_map<uint32_t, uint32_t> binding_to_global_start_index_map_; |
| 118 | unordered_map<uint32_t, uint32_t> binding_to_global_end_index_map_; |
Tobin Ehlis | 1bc258a | 2016-04-26 16:00:56 -0600 | [diff] [blame] | 119 | //VkDescriptorSetLayoutCreateFlags flags_; |
Tobin Ehlis | 2d9deec | 2016-04-21 14:19:26 -0600 | [diff] [blame] | 120 | uint32_t binding_count_; // # of bindings in this layout |
Tobin Ehlis | 546326f | 2016-04-26 11:06:05 -0600 | [diff] [blame] | 121 | vector<safe_VkDescriptorSetLayoutBinding *> bindings_; |
Tobin Ehlis | 2d9deec | 2016-04-21 14:19:26 -0600 | [diff] [blame] | 122 | uint32_t descriptor_count_; // total # descriptors in this layout |
| 123 | uint32_t dynamic_descriptor_count_; |
| 124 | }; |
| 125 | DescriptorSetLayout::DescriptorSetLayout() |
Tobin Ehlis | 1bc258a | 2016-04-26 16:00:56 -0600 | [diff] [blame] | 126 | : layout_(VK_NULL_HANDLE), /*flags_(0),*/ binding_count_(0), descriptor_count_(0), dynamic_descriptor_count_(0) {} |
Tobin Ehlis | 2d9deec | 2016-04-21 14:19:26 -0600 | [diff] [blame] | 127 | // Construct DescriptorSetLayout instance from given create info |
| 128 | DescriptorSetLayout::DescriptorSetLayout(debug_report_data *report_data, const VkDescriptorSetLayoutCreateInfo *p_create_info, |
| 129 | const VkDescriptorSetLayout layout) |
Tobin Ehlis | 1bc258a | 2016-04-26 16:00:56 -0600 | [diff] [blame] | 130 | : layout_(layout), /*flags_(p_create_info->flags),*/ binding_count_(p_create_info->bindingCount), descriptor_count_(0), |
Tobin Ehlis | 2d9deec | 2016-04-21 14:19:26 -0600 | [diff] [blame] | 131 | dynamic_descriptor_count_(0) { |
| 132 | uint32_t global_index = 0; |
| 133 | for (uint32_t i = 0; i < binding_count_; ++i) { |
| 134 | descriptor_count_ += p_create_info->pBindings[i].descriptorCount; |
| 135 | if (!binding_to_index_map_.emplace(p_create_info->pBindings[i].binding, i).second) { |
| 136 | log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT_EXT, |
| 137 | reinterpret_cast<uint64_t &>(layout_), __LINE__, DRAWSTATE_INVALID_LAYOUT, "DS", |
| 138 | "duplicated binding number in " |
| 139 | "VkDescriptorSetLayoutBinding"); |
| 140 | } |
| 141 | binding_to_global_start_index_map_[p_create_info->pBindings[i].binding] = global_index; |
| 142 | global_index += p_create_info->pBindings[i].descriptorCount ? p_create_info->pBindings[i].descriptorCount - 1 : 0; |
| 143 | binding_to_global_end_index_map_[p_create_info->pBindings[i].binding] = global_index; |
| 144 | global_index++; |
Tobin Ehlis | 546326f | 2016-04-26 11:06:05 -0600 | [diff] [blame] | 145 | bindings_.push_back(new safe_VkDescriptorSetLayoutBinding(&p_create_info->pBindings[i])); |
Tobin Ehlis | 2d9deec | 2016-04-21 14:19:26 -0600 | [diff] [blame] | 146 | // In cases where we should ignore pImmutableSamplers make sure it's NULL |
| 147 | if ((p_create_info->pBindings[i].pImmutableSamplers) && |
| 148 | ((p_create_info->pBindings[i].descriptorType != VK_DESCRIPTOR_TYPE_SAMPLER) && |
| 149 | (p_create_info->pBindings[i].descriptorType != VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER))) { |
Tobin Ehlis | 546326f | 2016-04-26 11:06:05 -0600 | [diff] [blame] | 150 | bindings_.back()->pImmutableSamplers = nullptr; |
Tobin Ehlis | 2d9deec | 2016-04-21 14:19:26 -0600 | [diff] [blame] | 151 | } |
| 152 | if (p_create_info->pBindings[i].descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC || |
| 153 | p_create_info->pBindings[i].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC) { |
| 154 | dynamic_descriptor_count_++; |
| 155 | } |
| 156 | } |
| 157 | } |
| 158 | DescriptorSetLayout::~DescriptorSetLayout() { |
Tobin Ehlis | 546326f | 2016-04-26 11:06:05 -0600 | [diff] [blame] | 159 | for (auto binding : bindings_) |
| 160 | delete binding; |
| 161 | }; |
| 162 | |
Tobin Ehlis | 2d9deec | 2016-04-21 14:19:26 -0600 | [diff] [blame] | 163 | VkDescriptorSetLayoutBinding const *DescriptorSetLayout::GetDescriptorSetLayoutBindingPtrFromBinding(const uint32_t binding) { |
| 164 | if (!binding_to_index_map_.count(binding)) |
| 165 | return nullptr; |
Chris Forbes | 6f6844a | 2016-04-27 14:00:44 +1200 | [diff] [blame^] | 166 | return bindings_[binding_to_index_map_[binding]]->ptr(); |
Tobin Ehlis | 2d9deec | 2016-04-21 14:19:26 -0600 | [diff] [blame] | 167 | } |
| 168 | VkDescriptorSetLayoutBinding const *DescriptorSetLayout::GetDescriptorSetLayoutBindingPtrFromIndex(const uint32_t index) { |
| 169 | if (index >= bindings_.size()) |
| 170 | return nullptr; |
Chris Forbes | 6f6844a | 2016-04-27 14:00:44 +1200 | [diff] [blame^] | 171 | return bindings_[index]->ptr(); |
Tobin Ehlis | 2d9deec | 2016-04-21 14:19:26 -0600 | [diff] [blame] | 172 | } |
| 173 | // Return descriptorCount for given binding, 0 if index is unavailable |
| 174 | uint32_t DescriptorSetLayout::GetDescriptorCountFromBinding(const uint32_t binding) { |
| 175 | if (!binding_to_index_map_.count(binding)) |
| 176 | return 0; |
Tobin Ehlis | 546326f | 2016-04-26 11:06:05 -0600 | [diff] [blame] | 177 | return bindings_[binding_to_index_map_[binding]]->descriptorCount; |
Tobin Ehlis | 2d9deec | 2016-04-21 14:19:26 -0600 | [diff] [blame] | 178 | } |
| 179 | // Return descriptorCount for given index, 0 if index is unavailable |
| 180 | uint32_t DescriptorSetLayout::GetDescriptorCountFromIndex(const uint32_t index) { |
| 181 | if (index >= bindings_.size()) |
| 182 | return 0; |
Tobin Ehlis | 546326f | 2016-04-26 11:06:05 -0600 | [diff] [blame] | 183 | return bindings_[index]->descriptorCount; |
Tobin Ehlis | 2d9deec | 2016-04-21 14:19:26 -0600 | [diff] [blame] | 184 | } |
| 185 | // For the given binding, return descriptorType |
| 186 | VkDescriptorType DescriptorSetLayout::GetTypeFromBinding(const uint32_t binding) { |
| 187 | assert(binding_to_index_map_.count(binding)); |
Tobin Ehlis | 546326f | 2016-04-26 11:06:05 -0600 | [diff] [blame] | 188 | return bindings_[binding_to_index_map_[binding]]->descriptorType; |
Tobin Ehlis | 2d9deec | 2016-04-21 14:19:26 -0600 | [diff] [blame] | 189 | } |
| 190 | // For the given index, return descriptorType |
| 191 | VkDescriptorType DescriptorSetLayout::GetTypeFromIndex(const uint32_t index) { |
| 192 | assert(index < bindings_.size()); |
Tobin Ehlis | 546326f | 2016-04-26 11:06:05 -0600 | [diff] [blame] | 193 | return bindings_[index]->descriptorType; |
Tobin Ehlis | 2d9deec | 2016-04-21 14:19:26 -0600 | [diff] [blame] | 194 | } |
Tobin Ehlis | 660aba4 | 2016-04-26 13:09:51 -0600 | [diff] [blame] | 195 | // For the given global index, return descriptorType |
| 196 | // Currently just counting up through bindings_, may improve this in future |
| 197 | VkDescriptorType DescriptorSetLayout::GetTypeFromGlobalIndex(const uint32_t index) { |
| 198 | auto global_offset = 0; |
| 199 | for (auto binding : bindings_) { |
| 200 | global_offset += binding->descriptorCount; |
| 201 | if (index < global_offset) |
| 202 | return binding->descriptorType; |
| 203 | } |
| 204 | assert(0); // requested global index is out of bounds |
Tobin Ehlis | 1bc258a | 2016-04-26 16:00:56 -0600 | [diff] [blame] | 205 | return VK_DESCRIPTOR_TYPE_MAX_ENUM; |
Tobin Ehlis | 660aba4 | 2016-04-26 13:09:51 -0600 | [diff] [blame] | 206 | } |
Tobin Ehlis | 2d9deec | 2016-04-21 14:19:26 -0600 | [diff] [blame] | 207 | // For the given binding, return stageFlags |
| 208 | VkShaderStageFlags DescriptorSetLayout::GetStageFlagsFromBinding(const uint32_t binding) { |
| 209 | assert(binding_to_index_map_.count(binding)); |
Tobin Ehlis | 546326f | 2016-04-26 11:06:05 -0600 | [diff] [blame] | 210 | return bindings_[binding_to_index_map_[binding]]->stageFlags; |
Tobin Ehlis | 2d9deec | 2016-04-21 14:19:26 -0600 | [diff] [blame] | 211 | } |
| 212 | // For the given binding, return start index |
| 213 | uint32_t DescriptorSetLayout::GetGlobalStartIndexFromBinding(const uint32_t binding) { |
| 214 | assert(binding_to_global_start_index_map_.count(binding)); |
| 215 | return binding_to_global_start_index_map_[binding]; |
| 216 | } |
| 217 | // For the given binding, return end index |
| 218 | uint32_t DescriptorSetLayout::GetGlobalEndIndexFromBinding(const uint32_t binding) { |
| 219 | assert(binding_to_global_end_index_map_.count(binding)); |
| 220 | return binding_to_global_end_index_map_[binding]; |
| 221 | } |
| 222 | // |
| 223 | VkSampler const *DescriptorSetLayout::GetImmutableSamplerPtrFromBinding(const uint32_t binding) { |
| 224 | assert(binding_to_index_map_.count(binding)); |
Tobin Ehlis | 546326f | 2016-04-26 11:06:05 -0600 | [diff] [blame] | 225 | return bindings_[binding_to_index_map_[binding]]->pImmutableSamplers; |
Tobin Ehlis | 2d9deec | 2016-04-21 14:19:26 -0600 | [diff] [blame] | 226 | } |
| 227 | // If our layout is compatible with rh_sd_layout, return true, |
| 228 | // else return false and fill in error_msg will description of what causes incompatibility |
| 229 | bool DescriptorSetLayout::IsCompatible(DescriptorSetLayout *rh_ds_layout, string *error_msg) { |
| 230 | // Trivial case |
| 231 | if (layout_ == rh_ds_layout->GetDescriptorSetLayout()) |
| 232 | return true; |
| 233 | if (descriptor_count_ != rh_ds_layout->descriptor_count_) { |
| 234 | stringstream error_str; |
| 235 | error_str << "DescriptorSetLayout " << layout_ << " has " << descriptor_count_ << " descriptors, but DescriptorSetLayout " |
| 236 | << rh_ds_layout->GetDescriptorSetLayout() << " has " << rh_ds_layout->descriptor_count_ << " descriptors."; |
| 237 | *error_msg = error_str.str(); |
| 238 | return false; // trivial fail case |
| 239 | } |
| 240 | // Descriptor counts match so need to go through bindings one-by-one |
| 241 | // and verify that type and stageFlags match |
| 242 | for (auto binding : bindings_) { |
| 243 | // TODO : Do we also need to check immutable samplers? |
| 244 | // VkDescriptorSetLayoutBinding *rh_binding; |
Tobin Ehlis | 546326f | 2016-04-26 11:06:05 -0600 | [diff] [blame] | 245 | // rh_ds_layout->FillDescriptorSetLayoutBindingStructFromBinding(binding->binding, rh_binding); |
| 246 | if (binding->descriptorCount != rh_ds_layout->GetTotalDescriptorCount()) { |
Tobin Ehlis | 2d9deec | 2016-04-21 14:19:26 -0600 | [diff] [blame] | 247 | stringstream error_str; |
Tobin Ehlis | 546326f | 2016-04-26 11:06:05 -0600 | [diff] [blame] | 248 | error_str << "Binding " << binding->binding << " for DescriptorSetLayout " << layout_ << " has a descriptorCount of " |
| 249 | << binding->descriptorCount << " but binding " << binding->binding << " for DescriptorSetLayout " |
Tobin Ehlis | 2d9deec | 2016-04-21 14:19:26 -0600 | [diff] [blame] | 250 | << rh_ds_layout->GetDescriptorSetLayout() << " has a descriptorCount of " |
| 251 | << rh_ds_layout->GetTotalDescriptorCount(); |
| 252 | *error_msg = error_str.str(); |
| 253 | return false; |
Tobin Ehlis | 546326f | 2016-04-26 11:06:05 -0600 | [diff] [blame] | 254 | } else if (binding->descriptorType != rh_ds_layout->GetTypeFromBinding(binding->binding)) { |
Tobin Ehlis | 2d9deec | 2016-04-21 14:19:26 -0600 | [diff] [blame] | 255 | stringstream error_str; |
Tobin Ehlis | 546326f | 2016-04-26 11:06:05 -0600 | [diff] [blame] | 256 | error_str << "Binding " << binding->binding << " for DescriptorSetLayout " << layout_ << " is type '" |
| 257 | << string_VkDescriptorType(binding->descriptorType) << "' but binding " << binding->binding |
Tobin Ehlis | 2d9deec | 2016-04-21 14:19:26 -0600 | [diff] [blame] | 258 | << " for DescriptorSetLayout " << rh_ds_layout->GetDescriptorSetLayout() << " is type '" |
Tobin Ehlis | 546326f | 2016-04-26 11:06:05 -0600 | [diff] [blame] | 259 | << string_VkDescriptorType(rh_ds_layout->GetTypeFromBinding(binding->binding)) << "'"; |
Tobin Ehlis | 2d9deec | 2016-04-21 14:19:26 -0600 | [diff] [blame] | 260 | *error_msg = error_str.str(); |
| 261 | return false; |
Tobin Ehlis | 546326f | 2016-04-26 11:06:05 -0600 | [diff] [blame] | 262 | } else if (binding->stageFlags != rh_ds_layout->GetStageFlagsFromBinding(binding->binding)) { |
Tobin Ehlis | 2d9deec | 2016-04-21 14:19:26 -0600 | [diff] [blame] | 263 | stringstream error_str; |
Tobin Ehlis | 546326f | 2016-04-26 11:06:05 -0600 | [diff] [blame] | 264 | error_str << "Binding " << binding->binding << " for DescriptorSetLayout " << layout_ << " has stageFlags " |
| 265 | << binding->stageFlags << " but binding " << binding->binding << " for DescriptorSetLayout " |
Tobin Ehlis | 2d9deec | 2016-04-21 14:19:26 -0600 | [diff] [blame] | 266 | << rh_ds_layout->GetDescriptorSetLayout() << " has stageFlags " |
Tobin Ehlis | 546326f | 2016-04-26 11:06:05 -0600 | [diff] [blame] | 267 | << rh_ds_layout->GetStageFlagsFromBinding(binding->binding); |
Tobin Ehlis | 2d9deec | 2016-04-21 14:19:26 -0600 | [diff] [blame] | 268 | *error_msg = error_str.str(); |
| 269 | return false; |
| 270 | } |
| 271 | } |
| 272 | return true; |
| 273 | } |
Chris Forbes | 6f6844a | 2016-04-27 14:00:44 +1200 | [diff] [blame^] | 274 | #endif // CORE_VALIDATION_DESCRIPTOR_SETS_H_ |