Repository: KhronosGroup/Vulkan-ValidationLayers
Pull request #9982: layers: Make desc_set_pipeline_layout shared pointer
Author: login: spencer-lunarg
Created: 2025-04-23T00:38:49Z | Merged: 2025-04-23T12:30:10Z

going to need for future https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/9870 fix

everywhere we were setting `desc_set_pipeline_layout` already had to do a `Get<vvl::PipelineLayout>` prior so seemed wasteful to not just keep as a shared pointer


Diff:
diff --git a/layers/core_checks/cc_drawdispatch.cpp b/layers/core_checks/cc_drawdispatch.cpp
index 16be583eee1..1940c50234b 100644
--- a/layers/core_checks/cc_drawdispatch.cpp
+++ b/layers/core_checks/cc_drawdispatch.cpp
@@ -1392,15 +1392,21 @@ bool CoreChecks::ValidateActionStateDescriptorsPipeline(const LastBound &last_bo
         } else {
             pipe_layouts_log << FormatHandle(*layouts.front());
         }
-        objlist.add(last_bound_state.desc_set_pipeline_layout);
+        std::string pipeline_layout_handle;
+        if (last_bound_state.desc_set_pipeline_layout) {
+            pipeline_layout_handle = FormatHandle(last_bound_state.desc_set_pipeline_layout->Handle());
+            objlist.add(last_bound_state.desc_set_pipeline_layout->Handle());
+        } else {
+            pipeline_layout_handle = "Pipeline Layout never bound";  // < can happen when dealing with multiview
+        }
+
         std::string range =
             pipeline.max_active_slot == 0 ? "set 0 is" : "all sets 0 to " + std::to_string(pipeline.max_active_slot) + " are";
         skip |= LogError(vuid.compatible_pipeline_08600, objlist, vuid.loc(),
                          "The %s (created with %s) statically uses descriptor set %" PRIu32
                          ", but %s not compatible with the pipeline layout bound with %s (%s)\n%s",
                          FormatHandle(pipeline).c_str(), pipe_layouts_log.str().c_str(), pipeline.max_active_slot, range.c_str(),
-                         String(last_bound_state.desc_set_bound_command),
-                         FormatHandle(last_bound_state.desc_set_pipeline_layout).c_str(),
+                         String(last_bound_state.desc_set_bound_command), pipeline_layout_handle.c_str(),
                          last_bound_state.DescribeNonCompatibleSet(pipeline.max_active_slot, *pipeline_layout).c_str());
     } else {
         // if the bound set is not compatible, the rest will just be extra redundant errors
diff --git a/layers/gpuav/instrumentation/gpuav_instrumentation.cpp b/layers/gpuav/instrumentation/gpuav_instrumentation.cpp
index f82db1ea721..2cf251b0187 100644
--- a/layers/gpuav/instrumentation/gpuav_instrumentation.cpp
+++ b/layers/gpuav/instrumentation/gpuav_instrumentation.cpp
@@ -134,11 +134,7 @@ static VkPipelineLayout CreateInstrumentationPipelineLayout(Validator &gpuav, Vk
         PushConstantRangesId push_constants_layouts = main_bound_shader->push_constant_ranges;
 
         if (last_bound.desc_set_pipeline_layout) {
-            std::shared_ptr<const vvl::PipelineLayout> last_bound_desc_set_pipe_layout =
-                gpuav.Get<vvl::PipelineLayout>(last_bound.desc_set_pipeline_layout);
-            if (last_bound_desc_set_pipe_layout) {
-                pipe_layout_ci.flags = last_bound_desc_set_pipe_layout->CreateFlags();
-            }
+            pipe_layout_ci.flags = last_bound.desc_set_pipeline_layout->CreateFlags();
         }
         std::vector<VkDescriptorSetLayout> set_layout_handles;
         if (set_layouts) {
@@ -544,11 +540,11 @@ void PreCallSetupShaderInstrumentationResources(Validator &gpuav, CommandBufferS
         // One exception when using GPL is we need to look out for INDEPENDENT_SETS_BIT which will have null sets inside them.
         // We have a fake merged_graphics_layout to mimic the complete layout, but the app must bind it to descriptor set
         if (inst_binding_pipe_layout_state->IsIndependentSets()) {
-            inst_binding_pipe_layout_state = gpuav.Get<vvl::PipelineLayout>(last_bound.desc_set_pipeline_layout);
+            inst_binding_pipe_layout_state = last_bound.desc_set_pipeline_layout;
             inst_binding_pipe_layout_src = PipelineLayoutSource::LastBoundDescriptorSet;
         }
     } else if (last_bound.desc_set_pipeline_layout) {
-        inst_binding_pipe_layout_state = gpuav.Get<vvl::PipelineLayout>(last_bound.desc_set_pipeline_layout);
+        inst_binding_pipe_layout_state = last_bound.desc_set_pipeline_layout;
         inst_binding_pipe_layout_src = PipelineLayoutSource::LastBoundDescriptorSet;
     } else if (cb_state.base.push_constant_latest_used_layout[lv_bind_point] != VK_NULL_HANDLE) {
         inst_binding_pipe_layout_state =
@@ -666,40 +662,34 @@ void PostCallSetupShaderInstrumentationResources(Validator &gpuav, CommandBuffer
 
     // Only need to rebind application desc sets if they have been disturbed by GPU-AV binding its instrumentation desc set.
     // - Can happen if the pipeline layout used to bind instrumentation descriptor set is not compatible with the one used by the
-    // app to bind the last/all the last desc set. This pipeline layout is referred to as "last_bound_desc_set_pipe_layout_state"
-    // hereinafter.
+    // app to bind the last/all the last desc set.
     // => We create this incompatibility when we add our empty descriptor set.
     // See PositiveGpuAVDescriptorIndexing.SharedPipelineLayoutSubsetGraphics for instance
     if (last_bound.desc_set_pipeline_layout) {
-        std::shared_ptr<const vvl::PipelineLayout> last_bound_desc_set_pipe_layout_state =
-            gpuav.Get<vvl::PipelineLayout>(last_bound.desc_set_pipeline_layout);
-        if (last_bound_desc_set_pipe_layout_state) {
-            const uint32_t desc_set_bindings_counts_from_last_pipeline =
-                LastBoundPipelineOrShaderDescSetBindingsCount(bind_point, last_bound);
-
-            const bool any_disturbed_desc_sets_bindings =
-                desc_set_bindings_counts_from_last_pipeline <
-                static_cast<uint32_t>(last_bound_desc_set_pipe_layout_state->set_layouts.size());
-
-            if (any_disturbed_desc_sets_bindings) {
-                const uint32_t disturbed_bindings_count = static_cast<uint32_t>(
-                    last_bound_desc_set_pipe_layout_state->set_layouts.size() - desc_set_bindings_counts_from_last_pipeline);
-                const uint32_t first_disturbed_set = desc_set_bindings_counts_from_last_pipeline;
-
-                for (uint32_t set_i = 0; set_i < disturbed_bindings_count; ++set_i) {
-                    const uint32_t last_bound_set_i = set_i + first_disturbed_set;
-                    const auto &last_bound_set_state = last_bound.ds_slots[last_bound_set_i].ds_state;
-                    // last_bound.ds_slot is a LUT, and descriptor sets before the last one could be unbound.
-                    if (!last_bound_set_state) {
-                        continue;
-                    }
-                    VkDescriptorSet last_bound_set = last_bound_set_state->VkHandle();
-                    const std::vector<uint32_t> &dynamic_offset = last_bound.ds_slots[last_bound_set_i].dynamic_offsets;
-                    const uint32_t dynamic_offset_count = static_cast<uint32_t>(dynamic_offset.size());
-                    DispatchCmdBindDescriptorSets(cb_state.VkHandle(), bind_point,
-                                                  last_bound_desc_set_pipe_layout_state->VkHandle(), last_bound_set_i, 1,
-                                                  &last_bound_set, dynamic_offset_count, dynamic_offset.data());
+        const uint32_t desc_set_bindings_counts_from_last_pipeline =
+            LastBoundPipelineOrShaderDescSetBindingsCount(bind_point, last_bound);
+
+        const bool any_disturbed_desc_sets_bindings =
+            desc_set_bindings_counts_from_last_pipeline <
+            static_cast<uint32_t>(last_bound.desc_set_pipeline_layout->set_layouts.size());
+
+        if (any_disturbed_desc_sets_bindings) {
+            const uint32_t disturbed_bindings_count = static_cast<uint32_t>(
+                last_bound.desc_set_pipeline_layout->set_layouts.size() - desc_set_bindings_counts_from_last_pipeline);
+            const uint32_t first_disturbed_set = desc_set_bindings_counts_from_last_pipeline;
+
+            for (uint32_t set_i = 0; set_i < disturbed_bindings_count; ++set_i) {
+                const uint32_t last_bound_set_i = set_i + first_disturbed_set;
+                const auto &last_bound_set_state = last_bound.ds_slots[last_bound_set_i].ds_state;
+                // last_bound.ds_slot is a LUT, and descriptor sets before the last one could be unbound.
+                if (!last_bound_set_state) {
+                    continue;
                 }
+                VkDescriptorSet last_bound_set = last_bound_set_state->VkHandle();
+                const std::vector<uint32_t> &dynamic_offset = last_bound.ds_slots[last_bound_set_i].dynamic_offsets;
+                const uint32_t dynamic_offset_count = static_cast<uint32_t>(dynamic_offset.size());
+                DispatchCmdBindDescriptorSets(cb_state.VkHandle(), bind_point, last_bound.desc_set_pipeline_layout->VkHandle(),
+                                              last_bound_set_i, 1, &last_bound_set, dynamic_offset_count, dynamic_offset.data());
             }
         }
     }
diff --git a/layers/gpuav/validation_cmd/gpuav_validation_cmd_common.cpp b/layers/gpuav/validation_cmd/gpuav_validation_cmd_common.cpp
index 35b0e09b35d..1b1dbce7c5b 100644
--- a/layers/gpuav/validation_cmd/gpuav_validation_cmd_common.cpp
+++ b/layers/gpuav/validation_cmd/gpuav_validation_cmd_common.cpp
@@ -16,6 +16,7 @@
  */
 
 #include "gpuav/validation_cmd/gpuav_validation_cmd_common.h"
+#include <vulkan/vulkan_core.h>
 
 #include "gpuav/core/gpuav_constants.h"
 #include "gpuav/resources/gpuav_state_trackers.h"
@@ -86,7 +87,8 @@ void RestorablePipelineState::Create(CommandBufferSubState &cb_state, VkPipeline
         }
     }
 
-    desc_set_pipeline_layout_ = last_bound.desc_set_pipeline_layout;
+    desc_set_pipeline_layout_ =
+        last_bound.desc_set_pipeline_layout ? last_bound.desc_set_pipeline_layout->VkHandle() : VK_NULL_HANDLE;
 
     push_constants_data_ = cb_state.base.push_constant_data_chunks;
 
diff --git a/layers/state_tracker/cmd_buffer_state.cpp b/layers/state_tracker/cmd_buffer_state.cpp
index 26ff47f77c1..2b33c85df9e 100644
--- a/layers/state_tracker/cmd_buffer_state.cpp
+++ b/layers/state_tracker/cmd_buffer_state.cpp
@@ -1184,28 +1184,28 @@ void CommandBuffer::ExecuteCommands(vvl::span<const VkCommandBuffer> secondary_c
     }
 }
 
-void CommandBuffer::PushDescriptorSetState(VkPipelineBindPoint pipelineBindPoint, const vvl::PipelineLayout &pipeline_layout,
-                                           vvl::Func bound_command, uint32_t set, uint32_t descriptorWriteCount,
+void CommandBuffer::PushDescriptorSetState(VkPipelineBindPoint pipelineBindPoint,
+                                           std::shared_ptr<const vvl::PipelineLayout> pipeline_layout, vvl::Func bound_command,
+                                           uint32_t set, uint32_t descriptorWriteCount,
                                            const VkWriteDescriptorSet *pDescriptorWrites) {
     // Short circuit invalid updates
-    if ((set >= pipeline_layout.set_layouts.size()) || !pipeline_layout.set_layouts[set] ||
-        !pipeline_layout.set_layouts[set]->IsPushDescriptor()) {
+    if ((set >= pipeline_layout->set_layouts.size()) || !pipeline_layout->set_layouts[set] ||
+        !pipeline_layout->set_layouts[set]->IsPushDescriptor()) {
         return;
     }
 
     // We need a descriptor set to update the bindings with, compatible with the passed layout
-    const auto &dsl = pipeline_layout.set_layouts[set];
+    const auto &dsl = pipeline_layout->set_layouts[set];
     const auto lv_bind_point = ConvertToLvlBindPoint(pipelineBindPoint);
     auto &last_bound = lastBound[lv_bind_point];
     auto &push_descriptor_set = last_bound.push_descriptor_set;
     // If we are disturbing the current push_desriptor_set clear it
-    if (!push_descriptor_set || !last_bound.IsBoundSetCompatible(set, pipeline_layout)) {
+    if (!push_descriptor_set || !last_bound.IsBoundSetCompatible(set, *pipeline_layout)) {
         last_bound.UnbindAndResetPushDescriptorSet(dev_data.CreatePushDescriptorSet(dsl));
     }
 
     UpdateLastBoundDescriptorSets(pipelineBindPoint, pipeline_layout, bound_command, set, 1, nullptr, push_descriptor_set, 0,
                                   nullptr);
-    last_bound.desc_set_pipeline_layout = pipeline_layout.VkHandle();
 
     // Now that we have either the new or extant push_descriptor set ... do the write updates against it
     push_descriptor_set->PerformPushDescriptorsUpdate(descriptorWriteCount, pDescriptorWrites);
@@ -1260,7 +1260,7 @@ void CommandBuffer::UpdatePipelineState(Func command, const VkPipelineBindPoint
         SetActiveSubpassRasterizationSampleCount(dynamic_state_value.rasterization_samples);
     }
 
-    if (last_bound.desc_set_pipeline_layout != VK_NULL_HANDLE) {
+    if (last_bound.desc_set_pipeline_layout) {
         for (const auto &[set_index, binding_req_map] : pipe->active_slots) {
             if (set_index >= last_bound.ds_slots.size()) {
                 continue;
@@ -1315,22 +1315,23 @@ static bool PushDescriptorCleanup(LastBound &last_bound, uint32_t set_idx) {
 // One of pDescriptorSets or push_descriptor_set should be nullptr, indicating whether this
 // is called for CmdBindDescriptorSets or CmdPushDescriptorSet.
 void CommandBuffer::UpdateLastBoundDescriptorSets(VkPipelineBindPoint pipeline_bind_point,
-                                                  const vvl::PipelineLayout &pipeline_layout, vvl::Func bound_command,
-                                                  uint32_t first_set, uint32_t set_count, const VkDescriptorSet *pDescriptorSets,
+                                                  std::shared_ptr<const vvl::PipelineLayout> pipeline_layout,
+                                                  vvl::Func bound_command, uint32_t first_set, uint32_t set_count,
+                                                  const VkDescriptorSet *pDescriptorSets,
                                                   std::shared_ptr<vvl::DescriptorSet> &push_descriptor_set,
                                                   uint32_t dynamic_offset_count, const uint32_t *p_dynamic_offsets) {
     ASSERT_AND_RETURN((pDescriptorSets == nullptr) ^ (push_descriptor_set == nullptr));
 
     uint32_t required_size = first_set + set_count;
     const uint32_t last_binding_index = required_size - 1;
-    assert(last_binding_index < pipeline_layout.set_compat_ids.size());
+    assert(last_binding_index < pipeline_layout->set_compat_ids.size());
 
     // Some useful shorthand
     const auto lv_bind_point = ConvertToLvlBindPoint(pipeline_bind_point);
     auto &last_bound = lastBound[lv_bind_point];
-    last_bound.desc_set_pipeline_layout = pipeline_layout.VkHandle();
+    last_bound.desc_set_pipeline_layout = pipeline_layout;
     last_bound.desc_set_bound_command = bound_command;
-    auto &pipe_compat_ids = pipeline_layout.set_compat_ids;
+    auto &pipe_compat_ids = pipeline_layout->set_compat_ids;
     // Resize binding arrays
     if (last_binding_index >= last_bound.ds_slots.size()) {
         last_bound.ds_slots.resize(required_size);
@@ -1400,18 +1401,18 @@ void CommandBuffer::UpdateLastBoundDescriptorSets(VkPipelineBindPoint pipeline_b
 }
 
 void CommandBuffer::UpdateLastBoundDescriptorBuffers(VkPipelineBindPoint pipeline_bind_point,
-                                                     const vvl::PipelineLayout &pipeline_layout, uint32_t first_set,
+                                                     std::shared_ptr<const vvl::PipelineLayout> pipeline_layout, uint32_t first_set,
                                                      uint32_t set_count, const uint32_t *buffer_indicies,
                                                      const VkDeviceSize *buffer_offsets) {
     uint32_t required_size = first_set + set_count;
     const uint32_t last_binding_index = required_size - 1;
-    assert(last_binding_index < pipeline_layout.set_compat_ids.size());
+    assert(last_binding_index < pipeline_layout->set_compat_ids.size());
 
     // Some useful shorthand
     const auto lv_bind_point = ConvertToLvlBindPoint(pipeline_bind_point);
     auto &last_bound = lastBound[lv_bind_point];
-    last_bound.desc_set_pipeline_layout = pipeline_layout.VkHandle();
-    auto &pipe_compat_ids = pipeline_layout.set_compat_ids;
+    last_bound.desc_set_pipeline_layout = pipeline_layout;
+    auto &pipe_compat_ids = pipeline_layout->set_compat_ids;
     // Resize binding arrays
     if (last_binding_index >= last_bound.ds_slots.size()) {
         last_bound.ds_slots.resize(required_size);
diff --git a/layers/state_tracker/cmd_buffer_state.h b/layers/state_tracker/cmd_buffer_state.h
index e54bfdd240a..7e61bb1cf16 100644
--- a/layers/state_tracker/cmd_buffer_state.h
+++ b/layers/state_tracker/cmd_buffer_state.h
@@ -628,17 +628,17 @@ class CommandBuffer : public RefcountedStateObject, public SubStateManager<Comma
 
     void ExecuteCommands(vvl::span<const VkCommandBuffer> secondary_command_buffers);
 
-    void UpdateLastBoundDescriptorSets(VkPipelineBindPoint pipeline_bind_point, const vvl::PipelineLayout &pipeline_layout,
-                                       vvl::Func bound_command, uint32_t first_set, uint32_t set_count,
-                                       const VkDescriptorSet *pDescriptorSets,
+    void UpdateLastBoundDescriptorSets(VkPipelineBindPoint pipeline_bind_point,
+                                       std::shared_ptr<const vvl::PipelineLayout> pipeline_layout, vvl::Func bound_command,
+                                       uint32_t first_set, uint32_t set_count, const VkDescriptorSet *pDescriptorSets,
                                        std::shared_ptr<vvl::DescriptorSet> &push_descriptor_set, uint32_t dynamic_offset_count,
                                        const uint32_t *p_dynamic_offsets);
 
-    void UpdateLastBoundDescriptorBuffers(VkPipelineBindPoint pipeline_bind_point, const vvl::PipelineLayout &pipeline_layout,
-                                          uint32_t first_set, uint32_t set_count, const uint32_t *buffer_indicies,
-                                          const VkDeviceSize *buffer_offsets);
+    void UpdateLastBoundDescriptorBuffers(VkPipelineBindPoint pipeline_bind_point,
+                                          std::shared_ptr<const vvl::PipelineLayout> pipeline_layout, uint32_t first_set,
+                                          uint32_t set_count, const uint32_t *buffer_indicies, const VkDeviceSize *buffer_offsets);
 
-    void PushDescriptorSetState(VkPipelineBindPoint pipelineBindPoint, const vvl::PipelineLayout &pipeline_layout,
+    void PushDescriptorSetState(VkPipelineBindPoint pipelineBindPoint, std::shared_ptr<const vvl::PipelineLayout> pipeline_layout,
                                 vvl::Func bound_command, uint32_t set, uint32_t descriptorWriteCount,
                                 const VkWriteDescriptorSet *pDescriptorWrites);
 
diff --git a/layers/state_tracker/last_bound_state.cpp b/layers/state_tracker/last_bound_state.cpp
index 09b7af7697c..a2562c160ca 100644
--- a/layers/state_tracker/last_bound_state.cpp
+++ b/layers/state_tracker/last_bound_state.cpp
@@ -42,7 +42,7 @@ bool LastBound::IsDynamic(const CBDynamicState state) const { return !pipeline_s
 
 void LastBound::Reset() {
     pipeline_state = nullptr;
-    desc_set_pipeline_layout = VK_NULL_HANDLE;
+    desc_set_pipeline_layout.reset();
     if (push_descriptor_set) {
         cb_state.RemoveChild(push_descriptor_set);
         push_descriptor_set->Destroy();
diff --git a/layers/state_tracker/last_bound_state.h b/layers/state_tracker/last_bound_state.h
index 592c11db4cf..8556fd2bfbe 100644
--- a/layers/state_tracker/last_bound_state.h
+++ b/layers/state_tracker/last_bound_state.h
@@ -48,7 +48,7 @@ struct LastBound {
     bool shader_object_bound[kShaderObjectStageCount]{false};
     vvl::ShaderObject *shader_object_states[kShaderObjectStageCount]{nullptr};
     // The compatible layout used binding descriptor sets (track location to provide better error message)
-    VkPipelineLayout desc_set_pipeline_layout = VK_NULL_HANDLE;
+    std::shared_ptr<const vvl::PipelineLayout> desc_set_pipeline_layout;
     vvl::Func desc_set_bound_command = vvl::Func::Empty;  // will be something like vkCmdBindDescriptorSets
     std::shared_ptr<vvl::DescriptorSet> push_descriptor_set;
 
diff --git a/layers/state_tracker/state_tracker.cpp b/layers/state_tracker/state_tracker.cpp
index 1fbea66a895..d58b72115cb 100644
--- a/layers/state_tracker/state_tracker.cpp
+++ b/layers/state_tracker/state_tracker.cpp
@@ -2600,7 +2600,7 @@ void DeviceState::PreCallRecordCmdBindDescriptorSets(VkCommandBuffer commandBuff
 
     std::shared_ptr<DescriptorSet> no_push_desc;
 
-    cb_state->UpdateLastBoundDescriptorSets(pipelineBindPoint, *pipeline_layout, record_obj.location.function, firstSet, setCount,
+    cb_state->UpdateLastBoundDescriptorSets(pipelineBindPoint, pipeline_layout, record_obj.location.function, firstSet, setCount,
                                             pDescriptorSets, no_push_desc, dynamicOffsetCount, pDynamicOffsets);
 }
 
@@ -2617,19 +2617,19 @@ void DeviceState::PreCallRecordCmdBindDescriptorSets2(VkCommandBuffer commandBuf
 
     if (IsStageInPipelineBindPoint(pBindDescriptorSetsInfo->stageFlags, VK_PIPELINE_BIND_POINT_GRAPHICS)) {
         cb_state->UpdateLastBoundDescriptorSets(
-            VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline_layout, record_obj.location.function, pBindDescriptorSetsInfo->firstSet,
+            VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, record_obj.location.function, pBindDescriptorSetsInfo->firstSet,
             pBindDescriptorSetsInfo->descriptorSetCount, pBindDescriptorSetsInfo->pDescriptorSets, no_push_desc,
             pBindDescriptorSetsInfo->dynamicOffsetCount, pBindDescriptorSetsInfo->pDynamicOffsets);
     }
     if (IsStageInPipelineBindPoint(pBindDescriptorSetsInfo->stageFlags, VK_PIPELINE_BIND_POINT_COMPUTE)) {
         cb_state->UpdateLastBoundDescriptorSets(
-            VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline_layout, record_obj.location.function, pBindDescriptorSetsInfo->firstSet,
+            VK_PIPELINE_BIND_POINT_COMPUTE, pipeline_layout, record_obj.location.function, pBindDescriptorSetsInfo->firstSet,
             pBindDescriptorSetsInfo->descriptorSetCount, pBindDescriptorSetsInfo->pDescriptorSets, no_push_desc,
             pBindDescriptorSetsInfo->dynamicOffsetCount, pBindDescriptorSetsInfo->pDynamicOffsets);
     }
     if (IsStageInPipelineBindPoint(pBindDescriptorSetsInfo->stageFlags, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR)) {
         cb_state->UpdateLastBoundDescriptorSets(
-            VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, *pipeline_layout, record_obj.location.function,
+            VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, pipeline_layout, record_obj.location.function,
             pBindDescriptorSetsInfo->firstSet, pBindDescriptorSetsInfo->descriptorSetCount,
             pBindDescriptorSetsInfo->pDescriptorSets, no_push_desc, pBindDescriptorSetsInfo->dynamicOffsetCount,
             pBindDescriptorSetsInfo->pDynamicOffsets);
@@ -2648,7 +2648,7 @@ void DeviceState::PreCallRecordCmdPushDescriptorSet(VkCommandBuffer commandBuffe
     auto cb_state = GetWrite<CommandBuffer>(commandBuffer);
     auto pipeline_layout = Get<PipelineLayout>(layout);
     ASSERT_AND_RETURN(pipeline_layout);
-    cb_state->PushDescriptorSetState(pipelineBindPoint, *pipeline_layout, record_obj.location.function, set, descriptorWriteCount,
+    cb_state->PushDescriptorSetState(pipelineBindPoint, pipeline_layout, record_obj.location.function, set, descriptorWriteCount,
                                      pDescriptorWrites);
 }
 
@@ -2667,17 +2667,17 @@ void DeviceState::PreCallRecordCmdPushDescriptorSet2(VkCommandBuffer commandBuff
     auto pipeline_layout = Get<PipelineLayout>(pPushDescriptorSetInfo->layout);
     ASSERT_AND_RETURN(pipeline_layout);
     if (IsStageInPipelineBindPoint(pPushDescriptorSetInfo->stageFlags, VK_PIPELINE_BIND_POINT_GRAPHICS)) {
-        cb_state->PushDescriptorSetState(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline_layout, record_obj.location.function,
+        cb_state->PushDescriptorSetState(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, record_obj.location.function,
                                          pPushDescriptorSetInfo->set, pPushDescriptorSetInfo->descriptorWriteCount,
                                          pPushDescriptorSetInfo->pDescriptorWrites);
     }
     if (IsStageInPipelineBindPoint(pPushDescriptorSetInfo->stageFlags, VK_PIPELINE_BIND_POINT_COMPUTE)) {
-        cb_state->PushDescriptorSetState(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline_layout, record_obj.location.function,
+        cb_state->PushDescriptorSetState(VK_PIPELINE_BIND_POINT_COMPUTE, pipeline_layout, record_obj.location.function,
                                          pPushDescriptorSetInfo->set, pPushDescriptorSetInfo->descriptorWriteCount,
                                          pPushDescriptorSetInfo->pDescriptorWrites);
     }
     if (IsStageInPipelineBindPoint(pPushDescriptorSetInfo->stageFlags, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR)) {
-        cb_state->PushDescriptorSetState(VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, *pipeline_layout, record_obj.location.function,
+        cb_state->PushDescriptorSetState(VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, pipeline_layout, record_obj.location.function,
                                          pPushDescriptorSetInfo->set, pPushDescriptorSetInfo->descriptorWriteCount,
                                          pPushDescriptorSetInfo->pDescriptorWrites);
     }
@@ -2708,7 +2708,7 @@ void DeviceState::PreCallRecordCmdSetDescriptorBufferOffsetsEXT(VkCommandBuffer
     auto pipeline_layout = Get<PipelineLayout>(layout);
     ASSERT_AND_RETURN(pipeline_layout);
 
-    cb_state->UpdateLastBoundDescriptorBuffers(pipelineBindPoint, *pipeline_layout, firstSet, setCount, pBufferIndices, pOffsets);
+    cb_state->UpdateLastBoundDescriptorBuffers(pipelineBindPoint, pipeline_layout, firstSet, setCount, pBufferIndices, pOffsets);
 }
 
 void DeviceState::PreCallRecordCmdSetDescriptorBufferOffsets2EXT(
@@ -2720,19 +2720,19 @@ void DeviceState::PreCallRecordCmdSetDescriptorBufferOffsets2EXT(
 
     if (IsStageInPipelineBindPoint(pSetDescriptorBufferOffsetsInfo->stageFlags, VK_PIPELINE_BIND_POINT_GRAPHICS)) {
         cb_state->UpdateLastBoundDescriptorBuffers(
-            VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline_layout, pSetDescriptorBufferOffsetsInfo->firstSet,
+            VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, pSetDescriptorBufferOffsetsInfo->firstSet,
             pSetDescriptorBufferOffsetsInfo->setCount, pSetDescriptorBufferOffsetsInfo->pBufferIndices,
             pSetDescriptorBufferOffsetsInfo->pOffsets);
     }
     if (IsStageInPipelineBindPoint(pSetDescriptorBufferOffsetsInfo->stageFlags, VK_PIPELINE_BIND_POINT_COMPUTE)) {
         cb_state->UpdateLastBoundDescriptorBuffers(
-            VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline_layout, pSetDescriptorBufferOffsetsInfo->firstSet,
+            VK_PIPELINE_BIND_POINT_COMPUTE, pipeline_layout, pSetDescriptorBufferOffsetsInfo->firstSet,
             pSetDescriptorBufferOffsetsInfo->setCount, pSetDescriptorBufferOffsetsInfo->pBufferIndices,
             pSetDescriptorBufferOffsetsInfo->pOffsets);
     }
     if (IsStageInPipelineBindPoint(pSetDescriptorBufferOffsetsInfo->stageFlags, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR)) {
         cb_state->UpdateLastBoundDescriptorBuffers(
-            VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, *pipeline_layout, pSetDescriptorBufferOffsetsInfo->firstSet,
+            VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, pipeline_layout, pSetDescriptorBufferOffsetsInfo->firstSet,
             pSetDescriptorBufferOffsetsInfo->setCount, pSetDescriptorBufferOffsetsInfo->pBufferIndices,
             pSetDescriptorBufferOffsetsInfo->pOffsets);
     }
@@ -4395,7 +4395,7 @@ void DeviceState::PreCallRecordCmdPushDescriptorSetWithTemplate(VkCommandBuffer
     const auto &template_ci = template_state->create_info;
     // Decode the template into a set of write updates
     DecodedTemplateUpdate decoded_template(*this, VK_NULL_HANDLE, template_state.get(), pData, dsl->VkHandle());
-    cb_state->PushDescriptorSetState(template_ci.pipelineBindPoint, *pipeline_layout, record_obj.location.function, set,
+    cb_state->PushDescriptorSetState(template_ci.pipelineBindPoint, pipeline_layout, record_obj.location.function, set,
                                      static_cast<uint32_t>(decoded_template.desc_writes.size()),
                                      decoded_template.desc_writes.data());
 }
@@ -4424,7 +4424,7 @@ void DeviceState::PreCallRecordCmdPushDescriptorSetWithTemplate2(
     DecodedTemplateUpdate decoded_template(*this, VK_NULL_HANDLE, template_state.get(), pPushDescriptorSetWithTemplateInfo->pData,
                                            dsl->VkHandle());
     cb_state->PushDescriptorSetState(
-        template_ci.pipelineBindPoint, *pipeline_layout, record_obj.location.function, pPushDescriptorSetWithTemplateInfo->set,
+        template_ci.pipelineBindPoint, pipeline_layout, record_obj.location.function, pPushDescriptorSetWithTemplateInfo->set,
         static_cast<uint32_t>(decoded_template.desc_writes.size()), decoded_template.desc_writes.data());
 }
 


Reviews:
user: login: spencer-lunarg
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T00:39:37Z
user: login: spencer-lunarg
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T00:39:57Z
user: login: arno-lunarg
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T12:21:51Z
user: login: spencer-lunarg
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T12:26:19Z
user: login: spencer-lunarg
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T12:29:09Z
user: login: spencer-lunarg
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T12:29:32Z
user: login: spencer-lunarg
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T12:30:03Z

Review comments:
diff_hunk: @@ -666,40 +662,34 @@ void PostCallSetupShaderInstrumentationResources(Validator &gpuav, CommandBuffer
 
     // Only need to rebind application desc sets if they have been disturbed by GPU-AV binding its instrumentation desc set.
     // - Can happen if the pipeline layout used to bind instrumentation descriptor set is not compatible with the one used by the
-    // app to bind the last/all the last desc set. This pipeline layout is referred to as "last_bound_desc_set_pipe_layout_state"
-    // hereinafter.
+    // app to bind the last/all the last desc set.
     // => We create this incompatibility when we add our empty descriptor set.
     // See PositiveGpuAVDescriptorIndexing.SharedPipelineLayoutSubsetGraphics for instance
     if (last_bound.desc_set_pipeline_layout) {
-        std::shared_ptr<const vvl::PipelineLayout> last_bound_desc_set_pipe_layout_state =
path: layers/gpuav/instrumentation/gpuav_instrumentation.cpp
user: login: spencer-lunarg
body: removed this nesting, because now we just save it as a shared_ptr instead of going back out from a handle
created_at: 2025-04-23T00:39:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1184,28 +1184,28 @@ void CommandBuffer::ExecuteCommands(vvl::span<const VkCommandBuffer> secondary_c
     }
 }
 
-void CommandBuffer::PushDescriptorSetState(VkPipelineBindPoint pipelineBindPoint, const vvl::PipelineLayout &pipeline_layout,
-                                           vvl::Func bound_command, uint32_t set, uint32_t descriptorWriteCount,
+void CommandBuffer::PushDescriptorSetState(VkPipelineBindPoint pipelineBindPoint,
+                                           std::shared_ptr<const vvl::PipelineLayout> pipeline_layout, vvl::Func bound_command,
+                                           uint32_t set, uint32_t descriptorWriteCount,
                                            const VkWriteDescriptorSet *pDescriptorWrites) {
     // Short circuit invalid updates
-    if ((set >= pipeline_layout.set_layouts.size()) || !pipeline_layout.set_layouts[set] ||
-        !pipeline_layout.set_layouts[set]->IsPushDescriptor()) {
+    if ((set >= pipeline_layout->set_layouts.size()) || !pipeline_layout->set_layouts[set] ||
+        !pipeline_layout->set_layouts[set]->IsPushDescriptor()) {
         return;
     }
 
     // We need a descriptor set to update the bindings with, compatible with the passed layout
-    const auto &dsl = pipeline_layout.set_layouts[set];
+    const auto &dsl = pipeline_layout->set_layouts[set];
     const auto lv_bind_point = ConvertToLvlBindPoint(pipelineBindPoint);
     auto &last_bound = lastBound[lv_bind_point];
     auto &push_descriptor_set = last_bound.push_descriptor_set;
     // If we are disturbing the current push_desriptor_set clear it
-    if (!push_descriptor_set || !last_bound.IsBoundSetCompatible(set, pipeline_layout)) {
+    if (!push_descriptor_set || !last_bound.IsBoundSetCompatible(set, *pipeline_layout)) {
         last_bound.UnbindAndResetPushDescriptorSet(dev_data.CreatePushDescriptorSet(dsl));
     }
 
     UpdateLastBoundDescriptorSets(pipelineBindPoint, pipeline_layout, bound_command, set, 1, nullptr, push_descriptor_set, 0,
                                   nullptr);
-    last_bound.desc_set_pipeline_layout = pipeline_layout.VkHandle();
path: layers/state_tracker/cmd_buffer_state.cpp
user: login: spencer-lunarg
body: `UpdateLastBoundDescriptorSets` above it sets it, so no need to reset here
created_at: 2025-04-23T00:39:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1392,15 +1392,21 @@ bool CoreChecks::ValidateActionStateDescriptorsPipeline(const LastBound &last_bo
         } else {
             pipe_layouts_log << FormatHandle(*layouts.front());
         }
-        objlist.add(last_bound_state.desc_set_pipeline_layout);
+        std::string pipeline_layout_handle;
path: layers/core_checks/cc_drawdispatch.cpp
user: login: arno-lunarg
body: Add _str to the name?
created_at: 2025-04-23T12:06:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1400,18 +1401,18 @@ void CommandBuffer::UpdateLastBoundDescriptorSets(VkPipelineBindPoint pipeline_b
 }
 
 void CommandBuffer::UpdateLastBoundDescriptorBuffers(VkPipelineBindPoint pipeline_bind_point,
-                                                     const vvl::PipelineLayout &pipeline_layout, uint32_t first_set,
+                                                     std::shared_ptr<const vvl::PipelineLayout> pipeline_layout, uint32_t first_set,
path: layers/state_tracker/cmd_buffer_state.cpp
user: login: arno-lunarg
body: Is the shared_ptr copy necessary?
created_at: 2025-04-23T12:20:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1400,18 +1401,18 @@ void CommandBuffer::UpdateLastBoundDescriptorSets(VkPipelineBindPoint pipeline_b
 }
 
 void CommandBuffer::UpdateLastBoundDescriptorBuffers(VkPipelineBindPoint pipeline_bind_point,
-                                                     const vvl::PipelineLayout &pipeline_layout, uint32_t first_set,
+                                                     std::shared_ptr<const vvl::PipelineLayout> pipeline_layout, uint32_t first_set,
path: layers/state_tracker/cmd_buffer_state.cpp
user: login: spencer-lunarg
body: without it, things were breaking :shrug: ... let me look again now
created_at: 2025-04-23T12:26:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1392,15 +1392,21 @@ bool CoreChecks::ValidateActionStateDescriptorsPipeline(const LastBound &last_bo
         } else {
             pipe_layouts_log << FormatHandle(*layouts.front());
         }
-        objlist.add(last_bound_state.desc_set_pipeline_layout);
+        std::string pipeline_layout_handle;
path: layers/core_checks/cc_drawdispatch.cpp
user: login: spencer-lunarg
body: probably, but its scope is so small (and honestly was planning to just clean this up, this VU is such a mess :puke:)
created_at: 2025-04-23T12:29:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1400,18 +1401,18 @@ void CommandBuffer::UpdateLastBoundDescriptorSets(VkPipelineBindPoint pipeline_b
 }
 
 void CommandBuffer::UpdateLastBoundDescriptorBuffers(VkPipelineBindPoint pipeline_bind_point,
-                                                     const vvl::PipelineLayout &pipeline_layout, uint32_t first_set,
+                                                     std::shared_ptr<const vvl::PipelineLayout> pipeline_layout, uint32_t first_set,
path: layers/state_tracker/cmd_buffer_state.cpp
user: login: spencer-lunarg
body: ya, still breaking
created_at: 2025-04-23T12:29:32Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1392,15 +1392,21 @@ bool CoreChecks::ValidateActionStateDescriptorsPipeline(const LastBound &last_bo
         } else {
             pipe_layouts_log << FormatHandle(*layouts.front());
         }
-        objlist.add(last_bound_state.desc_set_pipeline_layout);
+        std::string pipeline_layout_handle;
path: layers/core_checks/cc_drawdispatch.cpp
user: login: spencer-lunarg
body: (aka, the GPL fix is going to force us to redo this area... because that is just the power of GPL)
created_at: 2025-04-23T12:30:03Z
author_association: CONTRIBUTOR

PR comments:
user: login: ci-tester-lunarg
created_at: 2025-04-23T00:38:52Z
body: CI Vulkan-ValidationLayers build queued with queue ID 428020.
author_association: COLLABORATOR
user: login: ci-tester-lunarg
created_at: 2025-04-23T00:40:02Z
body: CI Vulkan-ValidationLayers build # 19919 running.
author_association: COLLABORATOR
user: login: ci-tester-lunarg
created_at: 2025-04-23T01:27:30Z
body: CI Vulkan-ValidationLayers build # 19919 passed.
author_association: COLLABORATOR