diff options
| author | John Zulauf <jzulauf@lunarg.com> | 2017-12-22 17:14:54 -0700 |
|---|---|---|
| committer | jzulauf-lunarg <32470354+jzulauf-lunarg@users.noreply.github.com> | 2018-01-04 17:08:05 -0700 |
| commit | 8467279d2b5cd6278c26b94fbe87d8499bb0e6b5 (patch) | |
| tree | 046ade3da1926ca2bf9cd7d004c051f20243ff18 /layers | |
| parent | 70fe5b4ab8cdf00aa4fee86b4c661a3a330961ed (diff) | |
| download | usermoji-8467279d2b5cd6278c26b94fbe87d8499bb0e6b5.tar.xz | |
layers: Add validation caching for draw/dispatch
Validation of descriptors at draw or dispatch time is now cached, s.t.
without changes that affect validity, validation checks are not
repeated.
Change-Id: I713662d00813989bf4441921456afca431d730d7
Diffstat (limited to 'layers')
| -rw-r--r-- | layers/buffer_validation.cpp | 2 | ||||
| -rw-r--r-- | layers/core_validation.cpp | 45 | ||||
| -rw-r--r-- | layers/core_validation_types.h | 3 | ||||
| -rw-r--r-- | layers/descriptor_sets.cpp | 107 | ||||
| -rw-r--r-- | layers/descriptor_sets.h | 64 |
5 files changed, 200 insertions, 21 deletions
diff --git a/layers/buffer_validation.cpp b/layers/buffer_validation.cpp index 05f2f405..112972f0 100644 --- a/layers/buffer_validation.cpp +++ b/layers/buffer_validation.cpp @@ -228,6 +228,7 @@ void SetLayout(layer_data *device_data, GLOBAL_CB_NODE *pCB, ImageSubresourcePai void SetImageLayout(layer_data *device_data, GLOBAL_CB_NODE *cb_node, const IMAGE_STATE *image_state, VkImageSubresourceRange image_subresource_range, const VkImageLayout &layout) { assert(image_state); + cb_node->image_layout_change_count++; // Change the version of this data to force revalidation for (uint32_t level_index = 0; level_index < image_subresource_range.levelCount; ++level_index) { uint32_t level = image_subresource_range.baseMipLevel + level_index; for (uint32_t layer_index = 0; layer_index < image_subresource_range.layerCount; layer_index++) { @@ -385,6 +386,7 @@ void TransitionImageAspectLayout(layer_data *device_data, GLOBAL_CB_NODE *pCB, c VkImageSubresource sub = {aspect, level, layer}; IMAGE_CMD_BUF_LAYOUT_NODE node; if (!FindCmdBufLayout(device_data, pCB, mem_barrier->image, sub, node)) { + pCB->image_layout_change_count++; // Change the version of this data to force revalidation SetLayout(device_data, pCB, mem_barrier->image, sub, IMAGE_CMD_BUF_LAYOUT_NODE(mem_barrier->oldLayout, mem_barrier->newLayout)); return; diff --git a/layers/core_validation.cpp b/layers/core_validation.cpp index 123db135..d55ceded 100644 --- a/layers/core_validation.cpp +++ b/layers/core_validation.cpp @@ -1184,15 +1184,24 @@ static bool ValidateDrawState(layer_data *dev_data, GLOBAL_CB_NODE *cb_node, CMD cvdescriptorset::DescriptorSet *descriptor_set = state.boundDescriptorSets[setIndex]; // Validate the draw-time state for this descriptor set std::string err_str; - if (!descriptor_set->IsPushDescriptor() && - !descriptor_set->ValidateDrawState(set_binding_pair.second, state.dynamicOffsets[setIndex], cb_node, function, - &err_str)) { - auto set = descriptor_set->GetSet(); - result |= log_msg(dev_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, - VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_EXT, HandleToUint64(set), __LINE__, - DRAWSTATE_DESCRIPTOR_SET_NOT_UPDATED, "DS", - "Descriptor set 0x%" PRIx64 " encountered the following validation error at %s time: %s", - HandleToUint64(set), function, err_str.c_str()); + if (!descriptor_set->IsPushDescriptor()) { + // For the "bindless" style resource usage with many descriptors, need to optimize command <-> descriptor + // binding validation. Take the requested binding set and prefilter it to eliminate redundant validation checks. + // Here, the currently bound pipeline determines whether an image validation check is redundant... + // for images are the "req" portion of the binding_req is indirectly (but tightly) coupled to the pipeline. + const cvdescriptorset::PrefilterBindRequestMap reduced_map(*descriptor_set, set_binding_pair.second, cb_node, + pPipe); + const auto &binding_req_map = reduced_map.Map(); + + if (!descriptor_set->ValidateDrawState(binding_req_map, state.dynamicOffsets[setIndex], cb_node, function, + &err_str)) { + auto set = descriptor_set->GetSet(); + result |= log_msg(dev_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, + VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_EXT, HandleToUint64(set), __LINE__, + DRAWSTATE_DESCRIPTOR_SET_NOT_UPDATED, "DS", + "Descriptor set 0x%" PRIx64 " encountered the following validation error at %s time: %s", + HandleToUint64(set), function, err_str.c_str()); + } } } } @@ -1214,10 +1223,14 @@ static void UpdateDrawState(layer_data *dev_data, GLOBAL_CB_NODE *cb_state, cons // Pull the set node cvdescriptorset::DescriptorSet *descriptor_set = state.boundDescriptorSets[setIndex]; if (!descriptor_set->IsPushDescriptor()) { + // For the "bindless" style resource usage with many descriptors, need to optimize command <-> descriptor binding + const cvdescriptorset::PrefilterBindRequestMap reduced_map(*descriptor_set, set_binding_pair.second, cb_state); + const auto &binding_req_map = reduced_map.Map(); + // Bind this set and its active descriptor resources to the command buffer - descriptor_set->BindCommandBuffer(cb_state, set_binding_pair.second); + descriptor_set->BindCommandBuffer(cb_state, binding_req_map); // For given active slots record updated images & buffers - descriptor_set->GetStorageUpdates(set_binding_pair.second, &cb_state->updateBuffers, &cb_state->updateImages); + descriptor_set->GetStorageUpdates(binding_req_map, &cb_state->updateBuffers, &cb_state->updateImages); } } } @@ -1762,6 +1775,7 @@ static void ResetCommandBufferState(layer_data *dev_data, const VkCommandBuffer pCB->hasDrawCmd = false; pCB->state = CB_NEW; pCB->submitCount = 0; + pCB->image_layout_change_count = 1; // Start at 1. 0 is insert value for validation cache versions, s.t. new == dirty pCB->status = 0; pCB->static_status = 0; pCB->viewportMask = 0; @@ -5369,6 +5383,13 @@ VKAPI_ATTR VkResult VKAPI_CALL BeginCommandBuffer(VkCommandBuffer commandBuffer, return result; } +static void PostCallRecordEndCommandBuffer(layer_data *dev_data, GLOBAL_CB_NODE *cb_state) { + // Cached validation is specific to a specific recording of a specific command buffer. + for (auto descriptor_set : cb_state->validated_descriptor_sets) { + descriptor_set->ClearCachedValidation(cb_state); + } + cb_state->validated_descriptor_sets.clear(); +} VKAPI_ATTR VkResult VKAPI_CALL EndCommandBuffer(VkCommandBuffer commandBuffer) { bool skip = false; @@ -5394,6 +5415,7 @@ VKAPI_ATTR VkResult VKAPI_CALL EndCommandBuffer(VkCommandBuffer commandBuffer) { lock.unlock(); auto result = dev_data->dispatch_table.EndCommandBuffer(commandBuffer); lock.lock(); + PostCallRecordEndCommandBuffer(dev_data, pCB); if (VK_SUCCESS == result) { pCB->state = CB_RECORDED; } @@ -5709,6 +5731,7 @@ static void PreCallRecordCmdBindDescriptorSets(layer_data *device_data, GLOBAL_C pDynamicOffsets + total_dynamic_descriptors + set_dynamic_descriptor_count); total_dynamic_descriptors += set_dynamic_descriptor_count; } + cb_state->validated_descriptor_sets.insert(descriptor_set); } // For any previously bound sets, need to set them to "invalid" if they were disturbed by this update if (firstSet > 0) { diff --git a/layers/core_validation_types.h b/layers/core_validation_types.h index fd48faf6..6f1610b0 100644 --- a/layers/core_validation_types.h +++ b/layers/core_validation_types.h @@ -649,6 +649,8 @@ struct GLOBAL_CB_NODE : public BASE_NODE { bool hasDrawCmd; CB_STATE state; // Track cmd buffer update state uint64_t submitCount; // Number of times CB has been submitted + typedef uint64_t ImageLayoutUpdateCount; + ImageLayoutUpdateCount image_layout_change_count; // The sequence number for changes to image layout (for cached validation) CBStatusFlags status; // Track status of various bindings on cmd buffer CBStatusFlags static_status; // All state bits provided by current graphics pipeline // rather than dynamic state @@ -697,6 +699,7 @@ struct GLOBAL_CB_NODE : public BASE_NODE { std::unordered_set<VkDeviceMemory> memObjs; std::vector<std::function<bool(VkQueue)>> eventUpdates; std::vector<std::function<bool(VkQueue)>> queryUpdates; + std::unordered_set<cvdescriptorset::DescriptorSet *> validated_descriptor_sets; }; struct SEMAPHORE_WAIT { diff --git a/layers/descriptor_sets.cpp b/layers/descriptor_sets.cpp index ed03840d..b217d913 100644 --- a/layers/descriptor_sets.cpp +++ b/layers/descriptor_sets.cpp @@ -30,6 +30,7 @@ #include <algorithm> // Construct DescriptorSetLayout instance from given create info +// Proactively reserve and resize as possible, as the reallocation was visible in profiling cvdescriptorset::DescriptorSetLayout::DescriptorSetLayout(const VkDescriptorSetLayoutCreateInfo *p_create_info, const VkDescriptorSetLayout layout) : layout_(layout), @@ -37,7 +38,7 @@ cvdescriptorset::DescriptorSetLayout::DescriptorSetLayout(const VkDescriptorSetL binding_count_(p_create_info->bindingCount), descriptor_count_(0), dynamic_descriptor_count_(0) { - // Proactively reserve and resize as possible, as the reallocation was visible in profiling + binding_type_stats_ = {0, 0, 0}; std::set<uint32_t> sorted_bindings; // Create the sorted set and unsorted map of bindings and indices for (uint32_t i = 0; i < binding_count_; i++) { @@ -65,6 +66,12 @@ cvdescriptorset::DescriptorSetLayout::DescriptorSetLayout(const VkDescriptorSetL binding_info.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC) { binding_to_dyn_count[binding_num] = binding_info.descriptorCount; dynamic_descriptor_count_ += binding_info.descriptorCount; + binding_type_stats_.dynamic_buffer_count++; + } else if ((binding_info.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER) || + (binding_info.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)) { + binding_type_stats_.non_dynamic_buffer_count++; + } else { + binding_type_stats_.image_sampler_count++; } } assert(bindings_.size() == binding_count_); @@ -296,7 +303,7 @@ cvdescriptorset::AllocateDescriptorSetsData::AllocateDescriptorSetsData(uint32_t : required_descriptors_by_type{}, layout_nodes(count, nullptr) {} cvdescriptorset::DescriptorSet::DescriptorSet(const VkDescriptorSet set, const VkDescriptorPool pool, - const std::shared_ptr<DescriptorSetLayout const> &layout, const layer_data *dev_data) + const std::shared_ptr<DescriptorSetLayout const> &layout, layer_data *dev_data) : some_update_(false), set_(set), pool_state_(nullptr), @@ -383,7 +390,7 @@ bool cvdescriptorset::DescriptorSet::IsCompatible(DescriptorSetLayout const *con // that any update buffers are valid, and that any dynamic offsets are within the bounds of their buffers. // Return true if state is acceptable, or false and write an error message into error string bool cvdescriptorset::DescriptorSet::ValidateDrawState(const std::map<uint32_t, descriptor_req> &bindings, - const std::vector<uint32_t> &dynamic_offsets, const GLOBAL_CB_NODE *cb_node, + const std::vector<uint32_t> &dynamic_offsets, GLOBAL_CB_NODE *cb_node, const char *caller, std::string *error) const { for (auto binding_pair : bindings) { auto binding = binding_pair.first; @@ -415,7 +422,7 @@ bool cvdescriptorset::DescriptorSet::ValidateDrawState(const std::map<uint32_t, << " references invalid buffer " << buffer << "."; *error = error_str.str(); return false; - } else { + } else if (!buffer_node->sparse) { for (auto mem_binding : buffer_node->GetBoundMemory()) { if (!GetMemObjInfo(device_data_, mem_binding)) { std::stringstream error_str; @@ -425,6 +432,13 @@ bool cvdescriptorset::DescriptorSet::ValidateDrawState(const std::map<uint32_t, return false; } } + } else { + // Enqueue sparse resource validation, as these can only be validated at submit time + auto device_data_copy = device_data_; // Cannot capture members by value, so make capturable copy. + std::function<bool(void)> function = [device_data_copy, caller, buffer_node]() { + return core_validation::ValidateBufferMemoryIsValid(device_data_copy, buffer_node, caller); + }; + cb_node->queue_submit_functions.push_back(function); } if (descriptors_[i]->IsDynamic()) { // Validate that dynamic offsets are within the buffer @@ -723,6 +737,72 @@ void cvdescriptorset::DescriptorSet::BindCommandBuffer(GLOBAL_CB_NODE *cb_node, } } } +void cvdescriptorset::DescriptorSet::FilterAndTrackOneBindingReq(const BindingReqMap::value_type &binding_req_pair, + const BindingReqMap &in_req, BindingReqMap *out_req, + TrackedBindings *bindings) { + assert(out_req); + assert(bindings); + const auto binding = binding_req_pair.first; + // Use insert and look at the boolean ("was inserted") in the returned pair to see if this is a new set member. + // Saves one hash lookup vs. find ... compare w/ end ... insert. + const auto it_bool_pair = bindings->insert(binding); + if (it_bool_pair.second) { + out_req->emplace(binding_req_pair); + } +} +void cvdescriptorset::DescriptorSet::FilterAndTrackOneBindingReq(const BindingReqMap::value_type &binding_req_pair, + const BindingReqMap &in_req, BindingReqMap *out_req, + TrackedBindings *bindings, uint32_t limit) { + if (bindings->size() < limit) FilterAndTrackOneBindingReq(binding_req_pair, in_req, out_req, bindings); +} + +void cvdescriptorset::DescriptorSet::FilterAndTrackBindingReqs(GLOBAL_CB_NODE *cb_state, const BindingReqMap &in_req, + BindingReqMap *out_req) { + TrackedBindings &bound = cached_validation_[cb_state].command_binding_and_usage; + if (bound.size() == GetBindingCount()) { + return; // All bindings are bound, out req is empty + } + for (const auto &binding_req_pair : in_req) { + const auto binding = binding_req_pair.first; + // If a binding doesn't exist, or has already been bound, skip it + if (p_layout_->HasBinding(binding)) { + FilterAndTrackOneBindingReq(binding_req_pair, in_req, out_req, &bound); + } + } +} + +void cvdescriptorset::DescriptorSet::FilterAndTrackBindingReqs(GLOBAL_CB_NODE *cb_state, PIPELINE_STATE *pipeline, + const BindingReqMap &in_req, BindingReqMap *out_req) { + auto &validated = cached_validation_[cb_state]; + auto &image_sample_val = validated.image_samplers[pipeline]; + auto *const dynamic_buffers = &validated.dynamic_buffers; + auto *const non_dynamic_buffers = &validated.non_dynamic_buffers; + const auto &stats = p_layout_->GetBindingTypeStats(); + for (const auto &binding_req_pair : in_req) { + auto binding = binding_req_pair.first; + VkDescriptorSetLayoutBinding const *layout_binding = p_layout_->GetDescriptorSetLayoutBindingPtrFromBinding(binding); + if (!layout_binding) { + continue; + } + // Caching criteria differs per type. + // If image_layout have changed , the image descriptors need to be validated against them. + if ((layout_binding->descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC) || + (layout_binding->descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC)) { + FilterAndTrackOneBindingReq(binding_req_pair, in_req, out_req, dynamic_buffers, stats.dynamic_buffer_count); + } else if ((layout_binding->descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER) || + (layout_binding->descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)) { + FilterAndTrackOneBindingReq(binding_req_pair, in_req, out_req, non_dynamic_buffers, stats.non_dynamic_buffer_count); + } else { + // This is rather crude, as the changed layouts may not impact the bound descriptors, + // but the simple "versioning" is a simple "dirt" test. + auto &version = image_sample_val[binding]; // Take advantage of default construtor zero initialzing new entries + if (version != cb_state->image_layout_change_count) { + version = cb_state->image_layout_change_count; + out_req->emplace(binding_req_pair); + } + } + } +} cvdescriptorset::SamplerDescriptor::SamplerDescriptor(const VkSampler *immut) : sampler_(VK_NULL_HANDLE), immutable_(false) { updated = false; @@ -1706,7 +1786,7 @@ void cvdescriptorset::PerformAllocateDescriptorSets(const VkDescriptorSetAllocat const AllocateDescriptorSetsData *ds_data, std::unordered_map<VkDescriptorPool, DESCRIPTOR_POOL_STATE *> *pool_map, std::unordered_map<VkDescriptorSet, cvdescriptorset::DescriptorSet *> *set_map, - const layer_data *dev_data) { + layer_data *dev_data) { auto pool_state = (*pool_map)[p_alloc_info->descriptorPool]; // Account for sets and individual descriptors allocated from pool pool_state->availableSets -= p_alloc_info->descriptorSetCount; @@ -1723,3 +1803,20 @@ void cvdescriptorset::PerformAllocateDescriptorSets(const VkDescriptorSetAllocat (*set_map)[descriptor_sets[i]] = new_ds; } } + +cvdescriptorset::PrefilterBindRequestMap::PrefilterBindRequestMap(cvdescriptorset::DescriptorSet &ds, const BindingReqMap &in_map, + GLOBAL_CB_NODE *cb_state) + : filtered_map_(), orig_map_(in_map) { + if (ds.GetTotalDescriptorCount() > kManyDescriptors_) { + filtered_map_.reset(new std::map<uint32_t, descriptor_req>()); + ds.FilterAndTrackBindingReqs(cb_state, orig_map_, filtered_map_.get()); + } +} +cvdescriptorset::PrefilterBindRequestMap::PrefilterBindRequestMap(cvdescriptorset::DescriptorSet &ds, const BindingReqMap &in_map, + GLOBAL_CB_NODE *cb_state, PIPELINE_STATE *pipeline) + : filtered_map_(), orig_map_(in_map) { + if (ds.GetTotalDescriptorCount() > kManyDescriptors_) { + filtered_map_.reset(new std::map<uint32_t, descriptor_req>()); + ds.FilterAndTrackBindingReqs(cb_state, pipeline, orig_map_, filtered_map_.get()); + } +}
\ No newline at end of file diff --git a/layers/descriptor_sets.h b/layers/descriptor_sets.h index 1ac2d0aa..cbc4ad19 100644 --- a/layers/descriptor_sets.h +++ b/layers/descriptor_sets.h @@ -41,6 +41,7 @@ using core_validation::layer_data; // Descriptor Data structures namespace cvdescriptorset { +// Utility structs/classes/types // Index range for global indices below, end is exclusive, i.e. [start,end) struct IndexRange { IndexRange(uint32_t start_in, uint32_t end_in) : start(start_in), end(end_in) {} @@ -48,6 +49,7 @@ struct IndexRange { uint32_t start; uint32_t end; }; +typedef std::map<uint32_t, descriptor_req> BindingReqMap; /* * DescriptorSetLayout class @@ -145,6 +147,13 @@ class DescriptorSetLayout { bool VerifyUpdateConsistency(uint32_t, uint32_t, uint32_t, const char *, const VkDescriptorSet, std::string *) const; bool IsPushDescriptor() const { return GetCreateFlags() & VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR; }; + struct BindingTypeStats { + uint32_t dynamic_buffer_count; + uint32_t non_dynamic_buffer_count; + uint32_t image_sampler_count; + }; + const BindingTypeStats &GetBindingTypeStats() const { return binding_type_stats_; } + private: VkDescriptorSetLayout layout_; std::set<uint32_t> non_empty_bindings_; // Containing non-emtpy bindings in numerical order @@ -159,6 +168,7 @@ class DescriptorSetLayout { std::vector<safe_VkDescriptorSetLayoutBinding> bindings_; uint32_t descriptor_count_; // total # descriptors in this layout uint32_t dynamic_descriptor_count_; + BindingTypeStats binding_type_stats_; }; /* @@ -302,7 +312,7 @@ bool ValidateAllocateDescriptorSets(const core_validation::layer_data *, const V void PerformAllocateDescriptorSets(const VkDescriptorSetAllocateInfo *, const VkDescriptorSet *, const AllocateDescriptorSetsData *, std::unordered_map<VkDescriptorPool, DESCRIPTOR_POOL_STATE *> *, std::unordered_map<VkDescriptorSet, cvdescriptorset::DescriptorSet *> *, - const core_validation::layer_data *); + core_validation::layer_data *); /* * DescriptorSet class @@ -325,7 +335,7 @@ void PerformAllocateDescriptorSets(const VkDescriptorSetAllocateInfo *, const Vk class DescriptorSet : public BASE_NODE { public: DescriptorSet(const VkDescriptorSet, const VkDescriptorPool, const std::shared_ptr<DescriptorSetLayout const> &, - const core_validation::layer_data *); + core_validation::layer_data *); ~DescriptorSet(); // A number of common Get* functions that return data based on layout from which this set was created uint32_t GetTotalDescriptorCount() const { return p_layout_->GetTotalDescriptorCount(); }; @@ -347,7 +357,7 @@ class DescriptorSet : public BASE_NODE { // Is this set compatible with the given layout? bool IsCompatible(DescriptorSetLayout const *const, std::string *) const; // For given bindings validate state at time of draw is correct, returning false on error and writing error details into string* - bool ValidateDrawState(const std::map<uint32_t, descriptor_req> &, const std::vector<uint32_t> &, const GLOBAL_CB_NODE *, + bool ValidateDrawState(const std::map<uint32_t, descriptor_req> &, const std::vector<uint32_t> &, GLOBAL_CB_NODE *, const char *caller, std::string *) const; // For given set of bindings, add any buffers and images that will be updated to their respective unordered_sets & return number // of objects inserted @@ -372,8 +382,23 @@ class DescriptorSet : public BASE_NODE { std::unordered_set<GLOBAL_CB_NODE *> GetBoundCmdBuffers() const { return cb_bindings; } // Bind given cmd_buffer to this descriptor set void BindCommandBuffer(GLOBAL_CB_NODE *, const std::map<uint32_t, descriptor_req> &); + + // Track work that has been bound or validated to avoid duplicate work, important when large descriptor arrays + // are present + typedef std::unordered_set<uint32_t> TrackedBindings; + static void FilterAndTrackOneBindingReq(const BindingReqMap::value_type &binding_req_pair, const BindingReqMap &in_req, + BindingReqMap *out_req, TrackedBindings *set); + static void FilterAndTrackOneBindingReq(const BindingReqMap::value_type &binding_req_pair, const BindingReqMap &in_req, + BindingReqMap *out_req, TrackedBindings *set, uint32_t limit); + void FilterAndTrackBindingReqs(GLOBAL_CB_NODE *, const BindingReqMap &in_req, BindingReqMap *out_req); + void FilterAndTrackBindingReqs(GLOBAL_CB_NODE *, PIPELINE_STATE *, const BindingReqMap &in_req, BindingReqMap *out_req); + void ClearCachedDynamicDescriptorValidation(GLOBAL_CB_NODE *cb_state) { cached_validation_[cb_state].dynamic_buffers.clear(); } + void ClearCachedValidation(GLOBAL_CB_NODE *cb_state) { cached_validation_.erase(cb_state); } // If given cmd_buffer is in the cb_bindings set, remove it - void RemoveBoundCommandBuffer(GLOBAL_CB_NODE *cb_node) { cb_bindings.erase(cb_node); } + void RemoveBoundCommandBuffer(GLOBAL_CB_NODE *cb_node) { + cb_bindings.erase(cb_node); + ClearCachedValidation(cb_node); + } VkSampler const *GetImmutableSamplerPtrFromBinding(const uint32_t index) const { return p_layout_->GetImmutableSamplerPtrFromBinding(index); }; @@ -401,8 +426,37 @@ class DescriptorSet : public BASE_NODE { const std::shared_ptr<DescriptorSetLayout const> p_layout_; std::vector<std::unique_ptr<Descriptor>> descriptors_; // Ptr to device data used for various data look-ups - const core_validation::layer_data *device_data_; + core_validation::layer_data *const device_data_; const VkPhysicalDeviceLimits limits_; + + // Cached binding and validation support: + // + // For the lifespan of a given command buffer recording, do lazy evaluation, caching, and dirtying of + // expensive validation operation (typically per-draw) + typedef std::unordered_map<GLOBAL_CB_NODE *, TrackedBindings> TrackedBindingMap; + typedef std::unordered_map<PIPELINE_STATE *, TrackedBindingMap> ValidatedBindings; + // Track the validation caching of bindings vs. the command buffer and draw state + typedef std::unordered_map<uint32_t, GLOBAL_CB_NODE::ImageLayoutUpdateCount> VersionedBindings; + struct CachedValidation { + TrackedBindings command_binding_and_usage; // Persistent for the life of the recording + TrackedBindings non_dynamic_buffers; // Persistent for the life of the recording + TrackedBindings dynamic_buffers; // Dirtied (flushed) each BindDescriptorSet + std::unordered_map<PIPELINE_STATE *, VersionedBindings> image_samplers; // Tested vs. changes to CB's ImageLayout + }; + typedef std::unordered_map<GLOBAL_CB_NODE *, CachedValidation> CachedValidationMap; + // Image and ImageView bindings are validated per pipeline and not invalidate by repeated binding + CachedValidationMap cached_validation_; +}; +// For the "bindless" style resource usage with many descriptors, need to optimize binding and validation +class PrefilterBindRequestMap { + public: + static const uint32_t kManyDescriptors_ = 64; // TODO base this number on measured data + std::unique_ptr<BindingReqMap> filtered_map_; + const BindingReqMap &orig_map_; + + PrefilterBindRequestMap(DescriptorSet &ds, const BindingReqMap &in_map, GLOBAL_CB_NODE *cb_state); + PrefilterBindRequestMap(DescriptorSet &ds, const BindingReqMap &in_map, GLOBAL_CB_NODE *cb_state, PIPELINE_STATE *); + const BindingReqMap &Map() const { return (filtered_map_) ? *filtered_map_ : orig_map_; } }; } #endif // CORE_VALIDATION_DESCRIPTOR_SETS_H_ |
