gpuav: Handle UntypedPointers in AccessPath
diff --git a/layers/gpuav/shaders/instrumentation/descriptor_heap.comp b/layers/gpuav/shaders/instrumentation/descriptor_heap.comp
index 5adb555..955fd5d 100644
--- a/layers/gpuav/shaders/instrumentation/descriptor_heap.comp
+++ b/layers/gpuav/shaders/instrumentation/descriptor_heap.comp
@@ -177,3 +177,29 @@
 bool inst_heap_mapping_indirect_address(const uint inst_offset, const uint push_offset, const uint address_offset) {
     return true; // Saving function offset space, but currently not used
 }
+
+// Untyped Pointers
+bool inst_heap_untyped(const uint inst_offset, const uint heap_offset, const uint address_stride, const uint desc_index, const bool is_sampler) {
+    uint error = 0;
+    uint param_0 = 0;
+
+    uint offset = heap_offset + (desc_index * address_stride);
+    BoundHeapInfo bound_heap_info = is_sampler ? gpuav.bound_sampler_heap : gpuav.bound_resource_heap;
+    if (offset >= bound_heap_info.heap_size) {
+        error = kErrorSubCode_DescriptorHeap_HeapOOB;
+        param_0 = offset;
+    }
+
+    if (error != 0) {
+        error_payload = ErrorPayload(
+                            inst_offset,
+                            SpecConstantLinkShaderId | (kErrorGroup_InstDescriptorHeap << kErrorGroup_Shift) | (error << kErrorSubCode_Shift),
+                            desc_index,
+                            param_0,
+                            0
+                        );
+
+        return false;
+    }
+    return true;
+}
diff --git a/layers/gpuav/spirv/debug_printf_pass.cpp b/layers/gpuav/spirv/debug_printf_pass.cpp
index c7ae4c7..cbc1415 100644
--- a/layers/gpuav/spirv/debug_printf_pass.cpp
+++ b/layers/gpuav/spirv/debug_printf_pass.cpp
@@ -823,17 +823,7 @@
         const ParamInfo& param = param_infos[i];
         const uint32_t argument_id = meta.target_instruction->Word(first_argument_offset + i);
 
-        const Type* argument_type = nullptr;
-        if (const Constant* constant = type_manager_.FindConstantById(argument_id)) {
-            argument_type = &constant->type_;
-        } else {
-            const Instruction* inst = current_function.FindInstruction(argument_id);
-            if (!inst) {
-                module_.InternalWarning(tag, "Unable to find OpExtInst ID inside function block");
-                return true;  // possibily our error, so leave a warning
-            }
-            argument_type = type_manager_.FindTypeById(inst->TypeId());
-        }
+        const Type* argument_type = type_manager_.FindTypeGlobal(current_function, argument_id);
         if (!argument_type) {
             module_.InternalWarning(tag, "Unable find OpExtInst ID type");
             return true;  // possibily our error, so leave a warning
diff --git a/layers/gpuav/spirv/descriptor_class_general_buffer_pass.cpp b/layers/gpuav/spirv/descriptor_class_general_buffer_pass.cpp
index 0973a0f..5797b15 100644
--- a/layers/gpuav/spirv/descriptor_class_general_buffer_pass.cpp
+++ b/layers/gpuav/spirv/descriptor_class_general_buffer_pass.cpp
@@ -18,6 +18,8 @@
 #include "generated/spirv_grammar_helper.h"
 #include "containers/container_utils.h"
 #include "module.h"
+#include <cassert>
+#include <cstdint>
 #include <spirv/unified1/spirv.hpp>
 #include <iostream>
 
@@ -54,14 +56,15 @@
 
 void DescriptorClassGeneralBufferPass::CreateFunctionCall(BasicBlock& block, InstructionIt* inst_it, const InstructionMeta& meta) {
     assert(!meta.access_path.ac_list.empty());
-    const Constant& desc_set_constant = type_manager_.GetConstantUInt32(meta.descriptor_set);
-    const uint32_t desc_index_id = CastToUint32(meta.descriptor_index_id, block, inst_it);  // might be int32
+    const DescriptorInterface& interface = meta.access_path.variable->interface_;
+    const Constant& desc_set_constant = type_manager_.GetConstantUInt32(interface.set);
+    const uint32_t desc_index_id = CastToUint32(meta.access_path.descriptor_index_id, block, inst_it);  // might be int32
 
     const uint32_t descriptor_offset_id =
-        GetLastByte(*meta.descriptor_type, meta.access_path.ac_list, meta.coop_mat_access, block, inst_it);
+        GetLastByte(*meta.access_path.pointer_type, meta.access_path.ac_list, meta.coop_mat_access, block, inst_it);
 
     const auto& layout_lut = module_.interface_.instrumentation_dsl.set_index_to_bindings_layout_lut;
-    BindingLayout binding_layout = layout_lut[meta.descriptor_set][meta.descriptor_binding];
+    BindingLayout binding_layout = layout_lut[interface.set][interface.binding];
     const Constant& binding_layout_offset = type_manager_.GetConstantUInt32(binding_layout.start);
 
     const uint32_t inst_position = meta.target_instruction->GetPositionOffset();
@@ -84,6 +87,7 @@
                                                                InstructionMeta& meta) {
     const uint32_t opcode = inst.Opcode();
 
+    // Only known way to access a UBO/SSBO
     if (!IsValueIn(spv::Op(opcode),
                    {spv::OpLoad, spv::OpStore, spv::OpCooperativeMatrixLoadKHR, spv::OpCooperativeMatrixStoreKHR}) &&
         !AtomicOperation(opcode)) {
@@ -99,51 +103,35 @@
     if (!meta.access_path.IsValid()) {
         return false;
     }
-    const Variable& access_variable = *meta.access_path.variable;
 
-    uint32_t storage_class = access_variable.StorageClass();
+    uint32_t storage_class = meta.access_path.variable->StorageClass();
+    // The idea is General Buffer will not include any UniformConstant descriptor type
     if (storage_class != spv::StorageClassUniform && storage_class != spv::StorageClassStorageBuffer) {
         return false;
     }
 
-    meta.descriptor_type = access_variable.PointerType(type_manager_);
-    if (!meta.descriptor_type || meta.descriptor_type->spv_type_ == SpvType::kRuntimeArray) {
+    if (meta.access_path.pointer_type->spv_type_ == SpvType::kRuntimeArray) {
         return false;  // TODO - Currently we mark these as "bindless"
     }
 
-    const bool is_descriptor_array = meta.descriptor_type->IsArray();
-    meta.descriptor_id = is_descriptor_array ? meta.descriptor_type->inst_.Operand(0) : meta.descriptor_type->Id();
+    const bool is_descriptor_array = meta.access_path.pointer_type->IsArray();
+    meta.descriptor_block_type_id =
+        is_descriptor_array ? meta.access_path.pointer_type->inst_.Operand(0) : meta.access_path.pointer_type->Id();
+    assert(type_manager_.FindTypeById(meta.descriptor_block_type_id)->spv_type_ == SpvType::kStruct && "unexpected block type");
 
     // Check for deprecated storage block form
     if (storage_class == spv::StorageClassUniform) {
-        assert(type_manager_.FindTypeById(meta.descriptor_id)->spv_type_ == SpvType::kStruct && "unexpected block type");
-
-        const bool block_found = GetDecoration(meta.descriptor_id, spv::DecorationBlock) != nullptr;
+        const bool block_found = GetDecoration(meta.descriptor_block_type_id, spv::DecorationBlock) != nullptr;
 
         // If block decoration not found, verify deprecated form of SSBO
         if (!block_found) {
-            assert(GetDecoration(meta.descriptor_id, spv::DecorationBufferBlock) != nullptr && "block decoration not found");
+            assert(GetDecoration(meta.descriptor_block_type_id, spv::DecorationBufferBlock) != nullptr &&
+                   "block decoration not found");
             storage_class = spv::StorageClassStorageBuffer;
         }
     }
 
-    // Grab front() as it will be the "final" type we access
-    const Type* value_type = type_manager_.FindValueTypeById(meta.access_path.FinalAccessedType());
-    if (!value_type) {
-        return false;
-    }
-
-    if (is_descriptor_array) {
-        // Because you can't have 2D array of descriptors, the first index of the last accessChain is the descriptor index
-        meta.descriptor_index_id = meta.access_path.DescriptorIndexId();
-    } else {
-        // There is no array of this descriptor, so we essentially have an array of 1
-        meta.descriptor_index_id = type_manager_.GetConstantZeroUint32().Id();
-    }
-
-    GetDescriptorSetAndBinding(access_variable.Id(), meta.descriptor_set, meta.descriptor_binding);
-
-    if (meta.descriptor_set >= glsl::kDebugInputBindlessMaxDescSets) {
+    if (meta.access_path.variable->interface_.set >= glsl::kDebugInputBindlessMaxDescSets) {
         module_.InternalWarning(Name(), "Tried to use a descriptor slot over the current max limit");
         return false;
     }
@@ -155,7 +143,7 @@
 
     if (!module_.settings_.safe_mode) {
         meta.access_offset =
-            FindOffsetInStruct(meta.descriptor_id, &meta.coop_mat_access, is_descriptor_array, meta.access_path.ac_list);
+            FindOffsetInStruct(meta.descriptor_block_type_id, &meta.coop_mat_access, is_descriptor_array, meta.access_path.ac_list);
     }
 
     // Save information to be used to make the Function
@@ -205,9 +193,9 @@
 
                     if (meta.access_offset != 0) {
                         // set offset for the first loop of the block
-                        auto map_it = block_highest_offset_map.find(meta.descriptor_id);
+                        auto map_it = block_highest_offset_map.find(meta.descriptor_block_type_id);
                         if (map_it == block_highest_offset_map.end()) {
-                            block_highest_offset_map[meta.descriptor_id] = meta.access_offset;
+                            block_highest_offset_map[meta.descriptor_block_type_id] = meta.access_offset;
                         } else {
                             map_it->second = std::max(map_it->second, meta.access_offset);
                         }
@@ -223,7 +211,7 @@
                 }
 
                 if (!module_.settings_.safe_mode && meta.access_offset != 0) {
-                    const uint32_t block_highest_offset = block_highest_offset_map[meta.descriptor_id];
+                    const uint32_t block_highest_offset = block_highest_offset_map[meta.descriptor_block_type_id];
                     if (meta.access_offset < block_highest_offset) {
                         continue;  // skipping because other instruction in block will be a higher offset
                     }
diff --git a/layers/gpuav/spirv/descriptor_class_general_buffer_pass.h b/layers/gpuav/spirv/descriptor_class_general_buffer_pass.h
index 180851a..c769b67 100644
--- a/layers/gpuav/spirv/descriptor_class_general_buffer_pass.h
+++ b/layers/gpuav/spirv/descriptor_class_general_buffer_pass.h
@@ -37,14 +37,8 @@
     struct InstructionMeta {
         const Instruction* target_instruction = nullptr;
 
-        uint32_t descriptor_set = 0;
-        uint32_t descriptor_binding = 0;
-        uint32_t descriptor_index_id = 0;  // index input the descriptor array
-
-        // The Type of the OpVariable that is being accessed
-        const Type* descriptor_type = nullptr;
-        // Id to the descriptor, will have array stripped if descriptor indexing
-        uint32_t descriptor_id = 0;
+        // The ID to the OpTypeStruct inside the SSBO/UBO
+        uint32_t descriptor_block_type_id = 0;
 
         AccessPath access_path;
 
diff --git a/layers/gpuav/spirv/descriptor_class_texel_buffer_pass.cpp b/layers/gpuav/spirv/descriptor_class_texel_buffer_pass.cpp
index f9ea440..2cb420d 100644
--- a/layers/gpuav/spirv/descriptor_class_texel_buffer_pass.cpp
+++ b/layers/gpuav/spirv/descriptor_class_texel_buffer_pass.cpp
@@ -14,6 +14,7 @@
  */
 
 #include "descriptor_class_texel_buffer_pass.h"
+#include "function_basic_block.h"
 #include "module.h"
 #include <spirv/unified1/spirv.hpp>
 #include <iostream>
@@ -38,8 +39,9 @@
 uint32_t DescriptorClassTexelBufferPass::GetLinkFunctionId() { return GetLinkFunction(link_function_id_, kOfflineFunction); }
 
 void DescriptorClassTexelBufferPass::CreateFunctionCall(BasicBlock& block, InstructionIt* inst_it, const InstructionMeta& meta) {
-    assert(meta.access_chain_inst && meta.var_inst);
-    const Constant& set_constant = type_manager_.GetConstantUInt32(meta.descriptor_set);
+    assert(meta.access_chain_inst && meta.descriptor_var);
+    const DescriptorInterface& interface = meta.descriptor_var->interface_;
+    const Constant& set_constant = type_manager_.GetConstantUInt32(interface.set);
     const uint32_t descriptor_index_id = CastToUint32(meta.descriptor_index_id, block, inst_it);  // might be int32
 
     const uint32_t opcode = meta.target_instruction->Opcode();
@@ -56,7 +58,7 @@
     const uint32_t descriptor_offset_id = CastToUint32(meta.target_instruction->Operand(1), block, inst_it);
 
     const auto& layout_lut = module_.interface_.instrumentation_dsl.set_index_to_bindings_layout_lut;
-    BindingLayout binding_layout = layout_lut[meta.descriptor_set][meta.descriptor_binding];
+    BindingLayout binding_layout = layout_lut[interface.set][interface.binding];
     const Constant& binding_layout_offset = type_manager_.GetConstantUInt32(binding_layout.start);
 
     const uint32_t inst_position = meta.target_instruction->GetPositionOffset();
@@ -74,6 +76,7 @@
     module_.need_log_error_ = true;
 }
 
+// TODO - use AccessPath
 bool DescriptorClassTexelBufferPass::RequiresInstrumentation(const Function& function, const Instruction& inst,
                                                              InstructionMeta& meta) {
     const uint32_t opcode = inst.Opcode();
@@ -110,41 +113,39 @@
         return false;  // TODO: Handle additional possibilities?
     }
 
-    meta.var_inst = function.FindInstruction(load_inst->Operand(0));
-    if (!meta.var_inst) {
+    const Instruction* var_inst = function.FindInstruction(load_inst->Operand(0));
+    if (!var_inst) {
         // can be a global variable
         const Variable* global_var = type_manager_.FindVariableById(load_inst->Operand(0));
-        meta.var_inst = global_var ? &global_var->inst_ : nullptr;
+        var_inst = global_var ? &global_var->inst_ : nullptr;
     }
-    if (!meta.var_inst || (!meta.var_inst->IsNonPtrAccessChain() && meta.var_inst->Opcode() != spv::OpVariable)) {
+    if (!var_inst || (!var_inst->IsNonPtrAccessChain() && var_inst->Opcode() != spv::OpVariable)) {
         return false;
     }
 
     // If OpVariable, access_chain_inst_ is never checked because it should be a direct image access
-    meta.access_chain_inst = meta.var_inst;
+    meta.access_chain_inst = var_inst;
 
-    if (meta.var_inst->IsNonPtrAccessChain()) {
-        meta.descriptor_index_id = meta.var_inst->Operand(1);
+    if (var_inst->IsNonPtrAccessChain()) {
+        meta.descriptor_index_id = var_inst->Operand(1);
 
-        if (meta.var_inst->Length() > 5) {
+        if (var_inst->Length() > 5) {
             module_.InternalError(Name(), "OpAccessChain has more than 1 indexes. 2D Texel Buffers not supported");
             return false;
         }
 
-        const Variable* variable = type_manager_.FindVariableById(meta.var_inst->Operand(0));
-        if (!variable) {
+        meta.descriptor_var = type_manager_.FindVariableById(var_inst->Operand(0));
+        if (!meta.descriptor_var) {
             module_.InternalError(Name(), "OpAccessChain base is not a variable");
             return false;
         }
-        meta.var_inst = &variable->inst_;
     } else {
         // There is no array of this descriptor, so we essentially have an array of 1
         meta.descriptor_index_id = type_manager_.GetConstantZeroUint32().Id();
+        meta.descriptor_var = type_manager_.FindVariableById(var_inst->ResultId());
     }
 
-    GetDescriptorSetAndBinding(meta.var_inst->ResultId(), meta.descriptor_set, meta.descriptor_binding);
-
-    if (meta.descriptor_set >= glsl::kDebugInputBindlessMaxDescSets) {
+    if (meta.descriptor_var->interface_.set >= glsl::kDebugInputBindlessMaxDescSets) {
         module_.InternalWarning(Name(), "Tried to use a descriptor slot over the current max limit");
         return false;
     }
diff --git a/layers/gpuav/spirv/descriptor_class_texel_buffer_pass.h b/layers/gpuav/spirv/descriptor_class_texel_buffer_pass.h
index dfbb1eb..b01b5e7 100644
--- a/layers/gpuav/spirv/descriptor_class_texel_buffer_pass.h
+++ b/layers/gpuav/spirv/descriptor_class_texel_buffer_pass.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2024-2025 LunarG, Inc.
+/* Copyright (c) 2024-2026 LunarG, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -35,12 +35,12 @@
     struct InstructionMeta {
         const Instruction* target_instruction = nullptr;
         const Instruction* access_chain_inst = nullptr;
-        const Instruction* var_inst = nullptr;
         const Instruction* image_inst = nullptr;
 
-        uint32_t descriptor_set = 0;
-        uint32_t descriptor_binding = 0;
-        uint32_t descriptor_index_id = 0;  // index input the descriptor array
+        const Variable* descriptor_var = nullptr;
+
+        // This is the ID of the uint that indexes in the array (or constant zero if no array)
+        uint32_t descriptor_index_id = 0;
     };
 
     bool RequiresInstrumentation(const Function& function, const Instruction& inst, InstructionMeta& meta);
diff --git a/layers/gpuav/spirv/descriptor_heap_pass.cpp b/layers/gpuav/spirv/descriptor_heap_pass.cpp
index b34117b..ebe9644 100644
--- a/layers/gpuav/spirv/descriptor_heap_pass.cpp
+++ b/layers/gpuav/spirv/descriptor_heap_pass.cpp
@@ -16,13 +16,16 @@
 #include "descriptor_heap_pass.h"
 #include <vulkan/vulkan_core.h>
 #include "containers/container_utils.h"
+#include "generated/spirv_grammar_helper.h"
 #include "link.h"
 #include "module.h"
+#include <cassert>
 #include <spirv/unified1/spirv.hpp>
 #include <iostream>
 
 #include "generated/gpuav_offline_spirv.h"
 #include "pass.h"
+#include "type_manager.h"
 
 namespace gpuav {
 namespace spirv {
@@ -39,6 +42,7 @@
     {"inst_heap_mapping_push_data", instrumentation_descriptor_heap_comp_function_5_offset},
     {"inst_heap_mapping_push_address", instrumentation_descriptor_heap_comp_function_6_offset},
     {"inst_heap_mapping_indirect_address", instrumentation_descriptor_heap_comp_function_7_offset},
+    {"inst_heap_untyped", instrumentation_descriptor_heap_comp_function_8_offset},
 };
 
 DescriptorHeapPass::DescriptorHeapPass(Module& module) : Pass(module, kOfflineModule) { module.use_bda_ = true; }
@@ -48,12 +52,15 @@
 }
 
 // TODO - if we find this slow, we could pre-sort all the mappings
-const VkDescriptorSetAndBindingMappingEXT* DescriptorHeapPass::GetMapping(uint32_t descriptor_set, uint32_t descriptor_binding) {
+const VkDescriptorSetAndBindingMappingEXT* DescriptorHeapPass::GetMapping(const DescriptorInterface& interface) const {
+    if (interface.IsHeap()) {
+        return nullptr;
+    }
     for (uint32_t map_i = 0; map_i < module_.interface_.mapping_info->mappingCount; map_i++) {
         const VkDescriptorSetAndBindingMappingEXT& mapping = module_.interface_.mapping_info->pMappings[map_i];
         const uint32_t last_binding = mapping.firstBinding + mapping.bindingCount;
-        if (mapping.descriptorSet == descriptor_set && descriptor_binding >= mapping.firstBinding &&
-            descriptor_binding < last_binding) {
+        if (mapping.descriptorSet == interface.set && interface.binding >= mapping.firstBinding &&
+            interface.binding < last_binding) {
             return &mapping;
         }
     }
@@ -61,20 +68,22 @@
 }
 
 uint32_t DescriptorHeapPass::CreateFunctionCall(BasicBlock& block, InstructionIt* inst_it, const InstructionMeta& meta,
-                                                bool is_sampler) {
-    const DescriptorMeta descriptor_meta = is_sampler ? meta.sampler : meta.resource;
-    const uint32_t descriptor_index_id = CastToUint32(descriptor_meta.descriptor_index_id, block, inst_it);  // might be int32
+                                                bool is_seperate_sampler) {
+    const Variable& descriptor_var = is_seperate_sampler ? *meta.access_path.sampler_variable : *meta.access_path.variable;
+    const uint32_t descriptor_index =
+        is_seperate_sampler ? meta.access_path.sampler_descriptor_index_id : meta.access_path.descriptor_index_id;
+    const uint32_t descriptor_index_id = CastToUint32(descriptor_index, block, inst_it);  // might be int32
 
     const uint32_t inst_position = meta.target_instruction->GetPositionOffset();
     const uint32_t inst_position_id = type_manager_.CreateConstantUInt32(inst_position).Id();
-    const uint32_t is_sampler_id = type_manager_.GetConstantBool(is_sampler).Id();
+    const uint32_t is_sampler_id = type_manager_.GetConstantBool(is_seperate_sampler).Id();
 
     uint32_t function_result = module_.TakeNextId();
     const uint32_t bool_type = type_manager_.GetTypeBool().Id();
 
     // TODO - This logic is not obvious and should be either fixed or moved to a common util
     // If dealing with a seperate sampler, only need to do it on the resource
-    if (meta.image_inst && !is_sampler) {
+    if (meta.access_path.image_load_inst && !is_seperate_sampler) {
         const uint32_t opcode = meta.target_instruction->Opcode();
         if (opcode != spv::OpImageRead && opcode != spv::OpImageFetch && opcode != spv::OpImageWrite) {
             // if not a direct read/write/fetch, will be a OpSampledImage
@@ -96,16 +105,45 @@
                 } else {
                     copy_object_map_.emplace(image_id, copy_id);
                     // slower, but need to guarantee it is placed after a OpSampledImage
-                    block.function_->CreateInstruction(spv::OpCopyObject, {type_id, copy_id, image_id}, image_id);
+                    block.function_->CreateInstruction(spv::OpCopyObject, {type_id, copy_id, image_id}, image_id, inst_it);
                 }
             }
         }
     }
 
-    const VkDescriptorSetAndBindingMappingEXT* mapping =
-        GetMapping(descriptor_meta.descriptor_set, descriptor_meta.descriptor_binding);
+    assert(descriptor_var.IsDescriptor());
+    const VkDescriptorSetAndBindingMappingEXT* mapping = GetMapping(descriptor_var.interface_);
     if (!mapping) {
-        // TODO - handle untyped
+        assert(descriptor_var.interface_.IsHeap());
+
+        uint32_t heap_offset_id = 0;
+        const Type* descriptor_array = nullptr;
+        if (meta.access_path.pointer_type->spv_type_ == SpvType::kStruct) {
+            const Instruction* offset_decoration = GetMemberDecoration(
+                meta.access_path.pointer_type->Id(), meta.access_path.heap_offset_member_index, spv::DecorationOffsetIdEXT);
+            assert(offset_decoration);
+            heap_offset_id = offset_decoration->Word(4);
+            heap_offset_id = CastToUint32(heap_offset_id, block, inst_it);
+
+            descriptor_array =
+                type_manager_.FindTypeById(meta.access_path.pointer_type->inst_.Operand(meta.access_path.heap_offset_member_index));
+            assert(descriptor_array->IsArray());
+        } else {
+            assert(meta.access_path.pointer_type->IsArray());
+            descriptor_array = meta.access_path.pointer_type;
+            heap_offset_id = type_manager_.GetConstantZeroUint32().Id();
+        }
+
+        const uint32_t array_stride_dec = GetDecoration(descriptor_array->Id(), spv::DecorationArrayStrideIdEXT)->Word(3);
+        const Constant* array_stride = type_manager_.FindConstantById(array_stride_dec);
+        const uint32_t array_stride_id = CastToUint32(array_stride->Id(), block, inst_it);  // might be int32
+
+        const uint32_t function_def = GetLinkFunctionId(UNTYPED);
+
+        block.CreateInstruction(spv::OpFunctionCall,
+                                {bool_type, function_result, function_def, inst_position_id, heap_offset_id, array_stride_id,
+                                 descriptor_index_id, is_sampler_id},
+                                inst_it);
     } else if (mapping->source == VK_DESCRIPTOR_MAPPING_SOURCE_HEAP_WITH_CONSTANT_OFFSET_EXT) {
         const VkDescriptorMappingSourceConstantOffsetEXT& map_data = mapping->sourceData.constantOffset;
 
@@ -206,10 +244,10 @@
     module_.need_log_error_ = true;
 
     // If there is a sampler, we have another descriptor at this spot we need to validate
-    if (!is_sampler && meta.HasSampler()) {
+    if (!is_seperate_sampler && meta.access_path.HasSampler()) {
         const uint32_t valid_image = function_result;
         uint32_t valid_sampler = 0;
-        if (meta.is_combined_image_sampler) {
+        if (meta.access_path.is_combined_image_sampler) {
             assert(mapping);  // not allowed with untyped pointers
             valid_sampler = CreateFunctionCallCombinedSampler(block, inst_it, meta, *mapping, descriptor_index_id);
         } else {
@@ -298,187 +336,9 @@
 }
 
 bool DescriptorHeapPass::RequiresInstrumentation(const Function& function, const Instruction& inst, InstructionMeta& meta) {
-    const spv::Op opcode = (spv::Op)inst.Opcode();
-
-    const Instruction* sampler_load_inst = nullptr;
-    if (opcode == spv::OpAtomicLoad || opcode == spv::OpAtomicStore || opcode == spv::OpAtomicExchange) {
-        // Image Atomics
-        const Instruction* image_texel_ptr_inst = function.FindInstruction(inst.Operand(0));
-        if (!image_texel_ptr_inst || image_texel_ptr_inst->Opcode() != spv::OpImageTexelPointer) {
-            return false;
-        }
-
-        const Variable* variable = nullptr;
-        const Instruction* access_chain_inst = function.FindInstruction(image_texel_ptr_inst->Operand(0));
-        if (access_chain_inst && access_chain_inst->IsNonPtrAccessChain()) {
-            variable = type_manager_.FindVariableById(access_chain_inst->Operand(0));
-        } else {
-            // if no array, will point right to a variable
-            variable = type_manager_.FindVariableById(image_texel_ptr_inst->Operand(0));
-        }
-
-        if (!variable) {
-            return false;
-        }
-        meta.resource.var_inst = &variable->inst_;
-
-        const Type* pointer_type = variable->PointerType(type_manager_);
-        if (!pointer_type) {
-            module_.InternalError(Name(), "Pointer type not found");
-            return false;
-        }
-
-        const bool non_empty_access_chain = access_chain_inst && access_chain_inst->Length() >= 5;
-        if (pointer_type->IsArray() && non_empty_access_chain) {
-            meta.resource.descriptor_index_id = access_chain_inst->Operand(1);
-        } else {
-            // There is no array of this descriptor, so we essentially have an array of 1
-            meta.resource.descriptor_index_id = type_manager_.GetConstantZeroUint32().Id();
-        }
-    } else if (IsValueIn(opcode, {spv::OpLoad, spv::OpStore, spv::OpCooperativeMatrixLoadKHR, spv::OpCooperativeMatrixStoreKHR}) ||
-               AtomicOperation(opcode)) {
-        // Buffer and Buffer Atomics and Storage Images
-
-        const AccessPath access_path = type_manager_.BuildAccessPath(function, inst);
-        if (!access_path.IsValid()) {
-            return false;
-        }
-        meta.resource.var_inst = &access_path.variable->inst_;
-
-        const uint32_t storage_class = access_path.variable->StorageClass();
-        if (storage_class == spv::StorageClassUniformConstant) {
-            // TODO - Need to add Storage Image support
-            return false;
-        }
-        if (storage_class != spv::StorageClassUniform && storage_class != spv::StorageClassStorageBuffer) {
-            return false;  // Prevents things like Push Constants
-        }
-
-        const Type* pointer_type = access_path.variable->PointerType(type_manager_);
-        if (!pointer_type) {
-            module_.InternalError(Name(), "Pointer type not found");
-            return false;
-        }
-
-        if (pointer_type->IsArray()) {
-            meta.resource.descriptor_index_id = access_path.DescriptorIndexId();
-        } else {
-            // There is no array of this descriptor, so we essentially have an array of 1
-            meta.resource.descriptor_index_id = type_manager_.GetConstantZeroUint32().Id();
-        }
-    } else {
-        // sampled image (non-atomic)
-
-        // Reference is not load or store, so if it isn't a image-based reference, move on
-        const uint32_t image_word = OpcodeImageAccessPosition(opcode);
-        if (image_word == 0) {
-            return false;
-        }
-
-        // Things that have an OpImage (in OpcodeImageAccessPosition) but we don't want to handle
-        if (opcode == spv::OpImageRead || opcode == spv::OpImageWrite) {
-            return false;  // Storage Images are handled at OpLoad
-        } else if (opcode == spv::OpImageTexelPointer) {
-            return false;  // atomics are handled separately
-        } else if (opcode == spv::OpImage) {
-            return false;  // Don't deal with the access directly
-        }
-
-        meta.image_inst = function.FindInstruction(inst.Word(image_word));
-        const Instruction* load_inst = meta.image_inst;
-        while (load_inst && (load_inst->Opcode() == spv::OpSampledImage || load_inst->Opcode() == spv::OpImage ||
-                             load_inst->Opcode() == spv::OpCopyObject)) {
-            const uint32_t load_operand = load_inst->Operand(0);
-            load_inst = function.FindInstruction(load_operand);
-
-            if (!load_inst) {
-                assert(type_manager_.IsUndef(load_operand));
-                return false;  //
-            } else if (load_inst->Opcode() == spv::OpSampledImage) {
-                sampler_load_inst = function.FindInstruction(load_inst->Operand(1));
-            }
-        }
-
-        if (!sampler_load_inst && meta.image_inst->Opcode() == spv::OpSampledImage) {
-            sampler_load_inst = function.FindInstruction(meta.image_inst->Operand(1));
-        }
-
-        // If we can't find a seperate sampler, and non sampled images are check elsewhere
-        // we know this is actually a combined image sampler
-        meta.is_combined_image_sampler = sampler_load_inst == nullptr;
-
-        if (!load_inst || load_inst->Opcode() != spv::OpLoad) {
-            return false;  // TODO: Handle additional possibilities?
-        }
-
-        meta.resource.var_inst = function.FindInstruction(load_inst->Operand(0));
-        if (!meta.resource.var_inst) {
-            // can be a global variable
-            const Variable* global_var = type_manager_.FindVariableById(load_inst->Operand(0));
-            meta.resource.var_inst = global_var ? &global_var->inst_ : nullptr;
-        }
-        if (!meta.resource.var_inst ||
-            (!meta.resource.var_inst->IsNonPtrAccessChain() && meta.resource.var_inst->Opcode() != spv::OpVariable)) {
-            return false;
-        }
-
-        if (meta.resource.var_inst->IsNonPtrAccessChain()) {
-            meta.resource.descriptor_index_id = meta.resource.var_inst->Operand(1);
-
-            if (meta.resource.var_inst->Length() > 5) {
-                module_.InternalError(Name(), "OpAccessChain has more than 1 indexes");
-                return false;
-            }
-
-            const Variable* variable = type_manager_.FindVariableById(meta.resource.var_inst->Operand(0));
-            if (!variable) {
-                module_.InternalError(Name(), "OpAccessChain base is not a variable");
-                return false;
-            }
-            meta.resource.var_inst = &variable->inst_;
-        } else {
-            meta.resource.descriptor_index_id = type_manager_.GetConstantZeroUint32().Id();
-        }
-    }
-
-    assert(meta.resource.var_inst);
-    uint32_t variable_id = meta.resource.var_inst->ResultId();
-    GetDescriptorSetAndBinding(variable_id, meta.resource.descriptor_set, meta.resource.descriptor_binding);
-
-    // When using a SAMPLED_IMAGE and SAMPLER, they are accessed together so we need check for 2 descriptors at the same time
-    // TODO - This is currently 95% the same logic as above, find a way to combine it
-    if (sampler_load_inst && sampler_load_inst->Opcode() == spv::OpLoad) {
-        meta.sampler.var_inst = function.FindInstruction(sampler_load_inst->Operand(0));
-        if (!meta.sampler.var_inst) {
-            // can be a global variable
-            const Variable* global_var = type_manager_.FindVariableById(sampler_load_inst->Operand(0));
-            meta.sampler.var_inst = global_var ? &global_var->inst_ : nullptr;
-        }
-        if (!meta.sampler.var_inst ||
-            (!meta.sampler.var_inst->IsNonPtrAccessChain() && meta.sampler.var_inst->Opcode() != spv::OpVariable)) {
-            return false;
-        }
-
-        if (meta.sampler.var_inst->IsNonPtrAccessChain()) {
-            meta.sampler.descriptor_index_id = meta.sampler.var_inst->Operand(1);
-
-            if (meta.sampler.var_inst->Length() > 5) {
-                module_.InternalError(Name(), "Sampler OpAccessChain has more than 1 indexes");
-                return false;
-            }
-
-            const Variable* variable = type_manager_.FindVariableById(meta.sampler.var_inst->Operand(0));
-            if (!variable) {
-                module_.InternalError(Name(), "Sampler OpAccessChain base is not a variable");
-                return false;
-            }
-            meta.sampler.var_inst = &variable->inst_;
-        } else {
-            meta.sampler.descriptor_index_id = type_manager_.GetConstantZeroUint32().Id();
-        }
-
-        variable_id = meta.sampler.var_inst->ResultId();
-        GetDescriptorSetAndBinding(variable_id, meta.sampler.descriptor_set, meta.sampler.descriptor_binding);
+    meta.access_path = type_manager_.BuildAccessPath(function, inst);
+    if (!meta.access_path.IsValid() || !meta.access_path.variable->IsDescriptor()) {
+        return false;
     }
 
     meta.target_instruction = &inst;
@@ -520,7 +380,7 @@
                 if (!module_.settings_.safe_mode) {
                     CreateFunctionCall(current_block, &inst_it, meta, false);
                     // This is just a dumb hack around iterators, for samplers we call a second function
-                    if (meta.HasSampler()) {
+                    if (meta.access_path.HasSampler()) {
                         inst_it++;
                     }
                 } else {
diff --git a/layers/gpuav/spirv/descriptor_heap_pass.h b/layers/gpuav/spirv/descriptor_heap_pass.h
index 897fb94..1227197 100644
--- a/layers/gpuav/spirv/descriptor_heap_pass.h
+++ b/layers/gpuav/spirv/descriptor_heap_pass.h
@@ -15,6 +15,7 @@
 #pragma once
 
 #include <stdint.h>
+#include "type_manager.h"
 #include "pass.h"
 
 struct VkDescriptorSetAndBindingMappingEXT;
@@ -22,6 +23,8 @@
 namespace gpuav {
 namespace spirv {
 
+struct DescriptorInterface;
+
 class DescriptorHeapPass : public Pass {
   public:
     DescriptorHeapPass(Module& module);
@@ -30,25 +33,10 @@
     void PrintDebugInfo() const final;
 
   private:
-    struct DescriptorMeta {
-        const Instruction* var_inst = nullptr;
-        uint32_t descriptor_set = 0;
-        uint32_t descriptor_binding = 0;
-        uint32_t descriptor_index_id = 0;
-    };
-
     struct InstructionMeta {
         const Instruction* target_instruction = nullptr;
 
-        DescriptorMeta resource;
-        DescriptorMeta sampler;
-
-        bool is_combined_image_sampler = false;
-
-        // Used to move OpSampledImage into block we access it
-        const Instruction* image_inst = nullptr;
-
-        bool HasSampler() const { return sampler.var_inst != nullptr || is_combined_image_sampler; }
+        AccessPath access_path;
     };
 
     bool RequiresInstrumentation(const Function& function, const Instruction& inst, InstructionMeta& meta);
@@ -57,7 +45,7 @@
                                                const VkDescriptorSetAndBindingMappingEXT& mapping,
                                                const uint32_t descriptor_index_id);
 
-    const VkDescriptorSetAndBindingMappingEXT* GetMapping(uint32_t descriptor_set, uint32_t descriptor_binding);
+    const VkDescriptorSetAndBindingMappingEXT* GetMapping(const DescriptorInterface& interface) const;
 
     // < original ID, new CopyObject ID >
     vvl::unordered_map<uint32_t, uint32_t> copy_object_map_;
@@ -72,7 +60,8 @@
         MAPPING_PUSH_DATA = 5,
         MAPPING_PUSH_ADDRESS = 6,
         MAPPING_INDIRECT_ADDRESS = 7,
-        FUNC_COUNT = 8,
+        UNTYPED = 8,
+        FUNC_COUNT = 9,
     };
     uint32_t link_function_id_[FUNC_COUNT]{};
     uint32_t GetLinkFunctionId(const FunctionNames func_name);
diff --git a/layers/gpuav/spirv/descriptor_indexing_oob_pass.cpp b/layers/gpuav/spirv/descriptor_indexing_oob_pass.cpp
index 1d215da..b94a45e 100644
--- a/layers/gpuav/spirv/descriptor_indexing_oob_pass.cpp
+++ b/layers/gpuav/spirv/descriptor_indexing_oob_pass.cpp
@@ -14,7 +14,6 @@
  */
 
 #include "descriptor_indexing_oob_pass.h"
-#include "containers/container_utils.h"
 #include "link.h"
 #include "module.h"
 #include <spirv/unified1/spirv.hpp>
@@ -52,11 +51,12 @@
 }
 
 uint32_t DescriptorIndexingOOBPass::CreateFunctionCall(BasicBlock& block, InstructionIt* inst_it, const InstructionMeta& meta) {
-    const Constant& set_constant = type_manager_.GetConstantUInt32(meta.descriptor_set);
-    const Constant& binding_constant = type_manager_.GetConstantUInt32(meta.descriptor_binding);
-    const uint32_t descriptor_index_id = CastToUint32(meta.descriptor_index_id, block, inst_it);  // might be int32
+    const DescriptorInterface& interface = meta.access_path.variable->interface_;
+    const Constant& set_constant = type_manager_.GetConstantUInt32(interface.set);
+    const Constant& binding_constant = type_manager_.GetConstantUInt32(interface.binding);
+    const uint32_t descriptor_index_id = CastToUint32(meta.access_path.descriptor_index_id, block, inst_it);  // might be int32
 
-    if (meta.image_inst) {
+    if (meta.access_path.image_load_inst) {
         const uint32_t opcode = meta.target_instruction->Opcode();
         if (opcode != spv::OpImageRead && opcode != spv::OpImageFetch && opcode != spv::OpImageWrite) {
             // if not a direct read/write/fetch, will be a OpSampledImage
@@ -78,14 +78,14 @@
                 } else {
                     copy_object_map_.emplace(image_id, copy_id);
                     // slower, but need to guarantee it is placed after a OpSampledImage
-                    block.function_->CreateInstruction(spv::OpCopyObject, {type_id, copy_id, image_id}, image_id);
+                    block.function_->CreateInstruction(spv::OpCopyObject, {type_id, copy_id, image_id}, image_id, inst_it);
                 }
             }
         }
     }
 
     const auto& layout_lut = module_.interface_.instrumentation_dsl.set_index_to_bindings_layout_lut;
-    BindingLayout binding_layout = layout_lut[meta.descriptor_set][meta.descriptor_binding];
+    BindingLayout binding_layout = layout_lut[interface.set][interface.binding];
     const Constant& binding_layout_size = type_manager_.GetConstantUInt32(binding_layout.count);
     const Constant& binding_layout_offset = type_manager_.GetConstantUInt32(binding_layout.start);
 
@@ -93,7 +93,7 @@
     const uint32_t inst_position_id = type_manager_.CreateConstantUInt32(inst_position).Id();
 
     uint32_t function_result = module_.TakeNextId();
-    const uint32_t function_def = GetLinkFunctionId(meta.is_combined_image_sampler);
+    const uint32_t function_def = GetLinkFunctionId(meta.access_path.is_combined_image_sampler);
     const uint32_t bool_type = type_manager_.GetTypeBool().Id();
 
     block.CreateInstruction(spv::OpFunctionCall,
@@ -107,16 +107,16 @@
     //     bool valid_image = inst_descriptor_indexing_oob(image);
     //     bool valid_sampler = inst_descriptor_indexing_oob(sampler);
     //     bool valid_both = image_valid && sampler_valid;
-    if (meta.sampler_var_inst) {
+    if (meta.access_path.sampler_variable) {
         const uint32_t valid_image = function_result;
         const uint32_t valid_sampler = module_.TakeNextId();
+        const DescriptorInterface& sampler_interface = meta.access_path.sampler_variable->interface_;
 
-        const Constant& sampler_set_constant = type_manager_.GetConstantUInt32(meta.sampler_descriptor_set);
-        const Constant& sampler_binding_constant = type_manager_.GetConstantUInt32(meta.sampler_descriptor_binding);
-        const uint32_t sampler_descriptor_index_id =
-            CastToUint32(meta.sampler_descriptor_index_id, block, inst_it);  // might be int32
+        const Constant& sampler_set_constant = type_manager_.GetConstantUInt32(sampler_interface.set);
+        const Constant& sampler_binding_constant = type_manager_.GetConstantUInt32(sampler_interface.binding);
+        const uint32_t sampler_descriptor_index_id = CastToUint32(meta.access_path.sampler_descriptor_index_id, block, inst_it);
 
-        BindingLayout sampler_binding_layout = layout_lut[meta.sampler_descriptor_set][meta.sampler_descriptor_binding];
+        BindingLayout sampler_binding_layout = layout_lut[sampler_interface.set][sampler_interface.binding];
         const Constant& sampler_binding_layout_size = type_manager_.GetConstantUInt32(sampler_binding_layout.count);
         const Constant& sampler_binding_layout_offset = type_manager_.GetConstantUInt32(sampler_binding_layout.start);
 
@@ -134,218 +134,42 @@
 }
 
 bool DescriptorIndexingOOBPass::RequiresInstrumentation(const Function& function, const Instruction& inst, InstructionMeta& meta) {
-    const spv::Op opcode = (spv::Op)inst.Opcode();
-
-    bool array_found = false;
-    const Instruction* sampler_load_inst = nullptr;
-    if (opcode == spv::OpAtomicLoad || opcode == spv::OpAtomicStore || opcode == spv::OpAtomicExchange) {
-        // Image Atomics
-        const Instruction* image_texel_ptr_inst = function.FindInstruction(inst.Operand(0));
-        if (!image_texel_ptr_inst || image_texel_ptr_inst->Opcode() != spv::OpImageTexelPointer) {
-            return false;
-        }
-
-        const Variable* variable = nullptr;
-        const Instruction* access_chain_inst = function.FindInstruction(image_texel_ptr_inst->Operand(0));
-        if (access_chain_inst && access_chain_inst->IsNonPtrAccessChain()) {
-            variable = type_manager_.FindVariableById(access_chain_inst->Operand(0));
-        } else {
-            // if no array, will point right to a variable
-            variable = type_manager_.FindVariableById(image_texel_ptr_inst->Operand(0));
-        }
-
-        if (!variable) {
-            return false;
-        }
-        meta.var_inst = &variable->inst_;
-
-        const Type* pointer_type = variable->PointerType(type_manager_);
-        if (!pointer_type) {
-            module_.InternalError(Name(), "Pointer type not found");
-            return false;
-        }
-
-        const bool non_empty_access_chain = access_chain_inst && access_chain_inst->Length() >= 5;
-        if (pointer_type->IsArray() && non_empty_access_chain) {
-            array_found = true;
-            meta.descriptor_index_id = access_chain_inst->Operand(1);
-        } else {
-            // There is no array of this descriptor, so we essentially have an array of 1
-            meta.descriptor_index_id = type_manager_.GetConstantZeroUint32().Id();
-        }
-    } else if (IsValueIn(opcode, {spv::OpLoad, spv::OpStore, spv::OpCooperativeMatrixLoadKHR, spv::OpCooperativeMatrixStoreKHR}) ||
-               AtomicOperation(opcode)) {
-        // Buffer and Buffer Atomics and Storage Images
-
-        const AccessPath access_path = type_manager_.BuildAccessPath(function, inst);
-        if (!access_path.IsValid()) {
-            return false;
-        }
-        meta.var_inst = &access_path.variable->inst_;
-
-        const uint32_t storage_class = access_path.variable->StorageClass();
-        if (storage_class == spv::StorageClassUniformConstant) {
-            // TODO - Need to add Storage Image support
-            return false;
-        }
-        if (storage_class != spv::StorageClassUniform && storage_class != spv::StorageClassStorageBuffer) {
-            return false;  // Prevents things like Push Constants
-        }
-
-        const Type* pointer_type = access_path.variable->PointerType(type_manager_);
-        if (!pointer_type) {
-            module_.InternalError(Name(), "Pointer type not found");
-            return false;
-        }
-
-        if (pointer_type->IsArray()) {
-            array_found = true;
-            meta.descriptor_index_id = access_path.DescriptorIndexId();
-        } else {
-            // There is no array of this descriptor, so we essentially have an array of 1
-            meta.descriptor_index_id = type_manager_.GetConstantZeroUint32().Id();
-        }
-    } else {
-        // sampled image (non-atomic)
-
-        // Reference is not load or store, so if it isn't a image-based reference, move on
-        const uint32_t image_word = OpcodeImageAccessPosition(opcode);
-        if (image_word == 0) {
-            return false;
-        }
-
-        // Things that have an OpImage (in OpcodeImageAccessPosition) but we don't want to handle
-        if (opcode == spv::OpImageRead || opcode == spv::OpImageWrite) {
-            return false;  // Storage Images are handled at OpLoad
-        } else if (opcode == spv::OpImageTexelPointer) {
-            return false;  // atomics are handled separately
-        } else if (opcode == spv::OpImage) {
-            return false;  // Don't deal with the access directly
-        }
-
-        meta.image_inst = function.FindInstruction(inst.Word(image_word));
-        const Instruction* load_inst = meta.image_inst;
-        while (load_inst && (load_inst->Opcode() == spv::OpSampledImage || load_inst->Opcode() == spv::OpImage ||
-                             load_inst->Opcode() == spv::OpCopyObject)) {
-            const uint32_t load_operand = load_inst->Operand(0);
-            load_inst = function.FindInstruction(load_operand);
-
-            if (!load_inst) {
-                assert(type_manager_.IsUndef(load_operand));
-                return false;  //
-            } else if (load_inst->Opcode() == spv::OpSampledImage) {
-                sampler_load_inst = function.FindInstruction(load_inst->Operand(1));
-            }
-        }
-
-        // If we can't find a seperate sampler, and non sampled images are check elsewhere, we know this is actually a combined
-        // image sampler
-        meta.is_combined_image_sampler = sampler_load_inst == nullptr;
-
-        if (!load_inst || load_inst->Opcode() != spv::OpLoad) {
-            return false;  // TODO: Handle additional possibilities?
-        }
-
-        meta.var_inst = function.FindInstruction(load_inst->Operand(0));
-        if (!meta.var_inst) {
-            // can be a global variable
-            const Variable* global_var = type_manager_.FindVariableById(load_inst->Operand(0));
-            meta.var_inst = global_var ? &global_var->inst_ : nullptr;
-        }
-        if (!meta.var_inst || (!meta.var_inst->IsNonPtrAccessChain() && meta.var_inst->Opcode() != spv::OpVariable)) {
-            return false;
-        }
-
-        if (meta.var_inst->IsNonPtrAccessChain()) {
-            array_found = true;
-            meta.descriptor_index_id = meta.var_inst->Operand(1);
-
-            if (meta.var_inst->Length() > 5) {
-                module_.InternalError(Name(), "OpAccessChain has more than 1 indexes");
-                return false;
-            }
-
-            const Variable* variable = type_manager_.FindVariableById(meta.var_inst->Operand(0));
-            if (!variable) {
-                module_.InternalError(Name(), "OpAccessChain base is not a variable");
-                return false;
-            }
-            meta.var_inst = &variable->inst_;
-        } else {
-            meta.descriptor_index_id = type_manager_.GetConstantZeroUint32().Id();
-        }
-    }
-
-    // guaranteed to be valid already, save compiler time optimizing the check out
-    if (!array_found && !module_.has_bindless_descriptors_) {
+    meta.access_path = type_manager_.BuildAccessPath(function, inst);
+    if (!meta.access_path.IsValid() || !meta.access_path.variable->IsDescriptor()) {
         return false;
     }
 
-    assert(meta.var_inst);
-    uint32_t variable_id = meta.var_inst->ResultId();
-    GetDescriptorSetAndBinding(variable_id, meta.descriptor_set, meta.descriptor_binding);
+    // guaranteed to be valid already, save compiler time optimizing the check out
+    if (!meta.access_path.pointer_type->IsArray() && !module_.has_bindless_descriptors_) {
+        return false;
+    }
 
-    if (meta.descriptor_set >= glsl::kDebugInputBindlessMaxDescSets) {
+    if (meta.access_path.variable->interface_.set >= glsl::kDebugInputBindlessMaxDescSets) {
         module_.InternalWarning(Name(), "Tried to use a descriptor slot over the current max limit");
         return false;
     }
 
     if (!module_.settings_.safe_mode) {
+        uint32_t variable_id = meta.access_path.variable->Id();
         auto variable_found_it = block_instrumented_table_.find(variable_id);
         if (variable_found_it == block_instrumented_table_.end()) {
-            block_instrumented_table_[variable_id] = {meta.descriptor_index_id};
+            block_instrumented_table_[variable_id] = {meta.access_path.descriptor_index_id};
         } else {
             vvl::unordered_set<uint32_t>& descriptor_index_set = variable_found_it->second;
-            if (descriptor_index_set.find(meta.descriptor_index_id) != descriptor_index_set.end()) {
+            if (descriptor_index_set.find(meta.access_path.descriptor_index_id) != descriptor_index_set.end()) {
                 return false;  // Already instrumented, can skip
             } else {
-                descriptor_index_set.emplace(meta.descriptor_index_id);
+                descriptor_index_set.emplace(meta.access_path.descriptor_index_id);
             }
         }
     }
 
-    // When using a SAMPLED_IMAGE and SAMPLER, they are accessed together so we need check for 2 descriptors at the same time
-    // TODO - This is currently 95% the same logic as above, find a way to combine it
-    if (sampler_load_inst && sampler_load_inst->Opcode() == spv::OpLoad) {
-        meta.sampler_var_inst = function.FindInstruction(sampler_load_inst->Operand(0));
-        if (!meta.sampler_var_inst) {
-            // can be a global variable
-            const Variable* global_var = type_manager_.FindVariableById(sampler_load_inst->Operand(0));
-            meta.sampler_var_inst = global_var ? &global_var->inst_ : nullptr;
-        }
-        if (!meta.sampler_var_inst ||
-            (!meta.sampler_var_inst->IsNonPtrAccessChain() && meta.sampler_var_inst->Opcode() != spv::OpVariable)) {
-            return false;
-        }
-
-        if (meta.sampler_var_inst->IsNonPtrAccessChain()) {
-            array_found = true;
-            meta.sampler_descriptor_index_id = meta.sampler_var_inst->Operand(1);
-
-            if (meta.sampler_var_inst->Length() > 5) {
-                module_.InternalError(Name(), "Sampler OpAccessChain has more than 1 indexes");
-                return false;
-            }
-
-            const Variable* variable = type_manager_.FindVariableById(meta.sampler_var_inst->Operand(0));
-            if (!variable) {
-                module_.InternalError(Name(), "Sampler OpAccessChain base is not a variable");
-                return false;
-            }
-            meta.sampler_var_inst = &variable->inst_;
-        } else {
-            meta.sampler_descriptor_index_id = type_manager_.GetConstantZeroUint32().Id();
-        }
-
-        variable_id = meta.sampler_var_inst->ResultId();
-        GetDescriptorSetAndBinding(variable_id, meta.sampler_descriptor_set, meta.sampler_descriptor_binding);
-
-        if (meta.sampler_descriptor_set >= glsl::kDebugInputBindlessMaxDescSets) {
-            module_.InternalWarning(Name(), "Sampler Tried to use a descriptor slot over the current max limit");
-            return false;
-        }
+    if (meta.access_path.sampler_variable &&
+        meta.access_path.sampler_variable->interface_.set >= glsl::kDebugInputBindlessMaxDescSets) {
+        module_.InternalWarning(Name(), "Sampler Tried to use a descriptor slot over the current max limit");
+        return false;
     }
-    // Save information to be used to make the Function
+
     meta.target_instruction = &inst;
 
     return true;
@@ -420,10 +244,11 @@
                 }
 
                 if (!module_.settings_.safe_mode) {
-                    const uint32_t hash_descriptor_index_id = pc_access.next_alias_id == meta.descriptor_index_id
+                    const uint32_t hash_descriptor_index_id = pc_access.next_alias_id == meta.access_path.descriptor_index_id
                                                                   ? pc_access.descriptor_index_id
-                                                                  : meta.descriptor_index_id;
-                    uint32_t hash_content[3] = {meta.descriptor_set, meta.descriptor_binding, hash_descriptor_index_id};
+                                                                  : meta.access_path.descriptor_index_id;
+                    uint32_t hash_content[3] = {meta.access_path.variable->interface_.set,
+                                                meta.access_path.variable->interface_.binding, hash_descriptor_index_id};
                     const uint32_t hash = hash_util::Hash32(hash_content, sizeof(uint32_t) * 3);
                     if (function_duplicate_tracker.FindAndUpdate(block_duplicate_tracker, hash)) {
                         continue;  // duplicate detected
diff --git a/layers/gpuav/spirv/descriptor_indexing_oob_pass.h b/layers/gpuav/spirv/descriptor_indexing_oob_pass.h
index 957c306..2c2a8b9 100644
--- a/layers/gpuav/spirv/descriptor_indexing_oob_pass.h
+++ b/layers/gpuav/spirv/descriptor_indexing_oob_pass.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2024-2025 LunarG, Inc.
+/* Copyright (c) 2024-2026 LunarG, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -15,6 +15,7 @@
 #pragma once
 
 #include <stdint.h>
+#include "type_manager.h"
 #include "pass.h"
 
 namespace gpuav {
@@ -34,18 +35,7 @@
     struct InstructionMeta {
         const Instruction* target_instruction = nullptr;
 
-        const Instruction* var_inst = nullptr;
-        const Instruction* image_inst = nullptr;
-        uint32_t descriptor_set = 0;
-        uint32_t descriptor_binding = 0;
-        uint32_t descriptor_index_id = 0;
-        bool is_combined_image_sampler = false;
-
-        // Duplicate values if dealing with SAMPLED_IMAGE and SAMPLER together
-        const Instruction* sampler_var_inst = nullptr;
-        uint32_t sampler_descriptor_set = 0;
-        uint32_t sampler_descriptor_binding = 0;
-        uint32_t sampler_descriptor_index_id = 0;
+        AccessPath access_path;
     };
 
     bool RequiresInstrumentation(const Function& function, const Instruction& inst, InstructionMeta& meta);
diff --git a/layers/gpuav/spirv/function_basic_block.cpp b/layers/gpuav/spirv/function_basic_block.cpp
index faec7e5..b08bb9f 100644
--- a/layers/gpuav/spirv/function_basic_block.cpp
+++ b/layers/gpuav/spirv/function_basic_block.cpp
@@ -129,12 +129,15 @@
     return (it == inst_map_.end()) ? nullptr : it->second;
 }
 
-void Function::CreateInstruction(spv::Op opcode, const std::vector<uint32_t>& words, uint32_t id) {
+void Function::CreateInstruction(spv::Op opcode, const std::vector<uint32_t>& words, uint32_t id, InstructionIt* out_inst_it) {
     for (auto& block : blocks_) {
         for (auto inst_it = block->instructions_.begin(); inst_it != block->instructions_.end(); ++inst_it) {
             if ((*inst_it)->ResultId() == id) {
                 inst_it++;  // insert after
                 block->CreateInstruction(opcode, words, &inst_it);
+                if (out_inst_it) {
+                    *out_inst_it = inst_it;
+                }
                 return;
             }
         }
diff --git a/layers/gpuav/spirv/function_basic_block.h b/layers/gpuav/spirv/function_basic_block.h
index e1d3a07..6c31432 100644
--- a/layers/gpuav/spirv/function_basic_block.h
+++ b/layers/gpuav/spirv/function_basic_block.h
@@ -120,7 +120,7 @@
     // A slower version of BasicBlock::CreateInstruction() that will search the entire function for |id| and then inject the
     // instruction after. Only to be used if you need to suddenly walk back to find an instruction, but normally instructions should
     // be added as you go forward only.
-    void CreateInstruction(spv::Op opcode, const std::vector<uint32_t>& words, uint32_t id);
+    void CreateInstruction(spv::Op opcode, const std::vector<uint32_t>& words, uint32_t id, InstructionIt* out_inst_it);
 
     // This is the uvec4 most consumers will need
     uint32_t stage_info_id_ = 0;
diff --git a/layers/gpuav/spirv/mesh_shading_pass.cpp b/layers/gpuav/spirv/mesh_shading_pass.cpp
index 6531130..968a1e0 100644
--- a/layers/gpuav/spirv/mesh_shading_pass.cpp
+++ b/layers/gpuav/spirv/mesh_shading_pass.cpp
@@ -72,7 +72,7 @@
         meta.function_id = SET_MESH_OUTPUT;
         return true;
     } else if (guard_all_task_payloads_ && (IsValueIn(opcode, {spv::OpLoad, spv::OpStore}) || AtomicOperation(opcode))) {
-        const AccessPath access_path = type_manager_.BuildAccessPath(function, inst);
+        const AccessPath access_path = type_manager_.BuildAccessPath(function, inst, true);
         if (!access_path.IsValid()) {
             return false;
         } else if (access_path.variable->StorageClass() != spv::StorageClassTaskPayloadWorkgroupEXT) {
diff --git a/layers/gpuav/spirv/module.cpp b/layers/gpuav/spirv/module.cpp
index c454134..dba2e50 100644
--- a/layers/gpuav/spirv/module.cpp
+++ b/layers/gpuav/spirv/module.cpp
@@ -16,6 +16,7 @@
 #include "module.h"
 #include <cassert>
 #include <spirv/unified1/spirv.hpp>
+#include "containers/container_utils.h"
 #include "containers/custom_containers.h"
 #include "containers/limits.h"
 #include "function_basic_block.h"
@@ -115,6 +116,7 @@
                 types_values_constants_.emplace_back(std::move(new_inst));
                 break;
             case spv::OpMemberDecorate:
+            case spv::OpMemberDecorateIdEXT:
             case spv::OpDecorationGroup:
             case spv::OpGroupDecorate:
             case spv::OpGroupMemberDecorate:
@@ -155,7 +157,8 @@
             case spv::OpSpecConstantComposite:
             case spv::OpConstant:
             case spv::OpConstantNull:
-            case spv::OpConstantComposite: {
+            case spv::OpConstantComposite:
+            case spv::OpConstantSizeOfEXT: {
                 const Type* type = type_manager_.FindTypeById(new_inst->TypeId());
                 if (opcode == spv::OpSpecConstant || opcode == spv::OpSpecConstantComposite) {
                     SetSpecConstantValue(new_inst.get(), *type, id_to_spec_id);
@@ -163,7 +166,8 @@
                 type_manager_.AddConstant(std::move(new_inst), *type);
                 break;
             }
-            case spv::OpVariable: {
+            case spv::OpVariable:
+            case spv::OpUntypedVariableKHR: {
                 const Type* type = type_manager_.FindTypeById(new_inst->TypeId());
                 const Variable& new_var = type_manager_.AddVariable(std::move(new_inst), *type);
 
@@ -171,8 +175,9 @@
                 spv::StorageClass storage_class = new_var.StorageClass();
                 // These are the only storage classes that interface with a descriptor
                 // see vkspec.html#interfaces-resources-descset
-                if (storage_class == spv::StorageClassUniform || storage_class == spv::StorageClassUniformConstant ||
-                    storage_class == spv::StorageClassStorageBuffer) {
+                if (opcode == spv::OpVariable &&
+                    IsValueIn(storage_class,
+                              {spv::StorageClassUniform, spv::StorageClassUniformConstant, spv::StorageClassStorageBuffer})) {
                     const Type* ptr_type = new_var.PointerType(type_manager_);
                     // The shader will also have OpCapability RuntimeDescriptorArray
                     if (ptr_type->spv_type_ == SpvType::kRuntimeArray) {
@@ -183,11 +188,6 @@
 
                 break;
             }
-            case spv::OpUntypedVariableKHR: {
-                type_manager_.AddUntypedVariable(new_inst->ResultId());
-                types_values_constants_.emplace_back(std::move(new_inst));
-                break;
-            }
             case spv::OpSpecConstantOp: {
                 const Type* type = type_manager_.FindTypeById(new_inst->TypeId());
                 // If folded, we drop the |new_inst| as we will add it inside the function
diff --git a/layers/gpuav/spirv/pass.cpp b/layers/gpuav/spirv/pass.cpp
index 8fc89de..c26d5fc 100644
--- a/layers/gpuav/spirv/pass.cpp
+++ b/layers/gpuav/spirv/pass.cpp
@@ -227,7 +227,8 @@
 
 const Instruction* Pass::GetDecoration(uint32_t id, spv::Decoration decoration) const {
     for (const auto& annotation : module_.annotations_) {
-        if (annotation->Opcode() == spv::OpDecorate && annotation->Word(1) == id &&
+        const uint32_t opcode = annotation->Opcode();
+        if ((opcode == spv::OpDecorate || opcode == spv::OpDecorateId) && annotation->Word(1) == id &&
             spv::Decoration(annotation->Word(2)) == decoration) {
             return annotation.get();
         }
@@ -237,26 +238,15 @@
 
 const Instruction* Pass::GetMemberDecoration(uint32_t id, uint32_t member_index, spv::Decoration decoration) const {
     for (const auto& annotation : module_.annotations_) {
-        if (annotation->Opcode() == spv::OpMemberDecorate && annotation->Word(1) == id && annotation->Word(2) == member_index &&
-            spv::Decoration(annotation->Word(3)) == decoration) {
+        const uint32_t opcode = annotation->Opcode();
+        if ((opcode == spv::OpMemberDecorate || opcode == spv::OpMemberDecorateIdEXT) && annotation->Word(1) == id &&
+            annotation->Word(2) == member_index && spv::Decoration(annotation->Word(3)) == decoration) {
             return annotation.get();
         }
     }
     return nullptr;
 }
 
-void Pass::GetDescriptorSetAndBinding(uint32_t variable_id, uint32_t& out_set, uint32_t& out_binding) const {
-    for (const auto& annotation : module_.annotations_) {
-        if (annotation->Opcode() == spv::OpDecorate && annotation->Word(1) == variable_id) {
-            if (annotation->Word(2) == spv::DecorationDescriptorSet) {
-                out_set = annotation->Word(3);
-            } else if (annotation->Word(2) == spv::DecorationBinding) {
-                out_binding = annotation->Word(3);
-            }
-        }
-    }
-}
-
 // In an ideal world, this would be baked into the Type class when we construct it. The core issue is OpTypeMatrix size can be
 // different depending where it is used. Because of this, we need to have a higher level view what is going on in order to correctly
 // figure out the size of a given type.
@@ -748,16 +738,7 @@
 // Generate code to convert integer id to 32bit, if needed.
 uint32_t Pass::ConvertTo32(uint32_t id, BasicBlock& block, InstructionIt* inst_it) const {
     // Find type doing the indexing into the access chain
-    const Type* type = nullptr;
-    const Constant* constant = type_manager_.FindConstantById(id);
-    if (constant) {
-        type = &constant->type_;
-    } else {
-        const Instruction* inst = block.function_->FindInstruction(id);
-        if (inst) {
-            type = type_manager_.FindTypeById(inst->TypeId());
-        }
-    }
+    const Type* type = type_manager_.FindTypeGlobal(*block.function_, id);
     if (!type) {
         return id;
     }
@@ -778,20 +759,12 @@
 }
 
 // Generate code to cast integer it to 32bit unsigned, if needed.
+// TODO - Have a fast path for int32 to uint32
 uint32_t Pass::CastToUint32(uint32_t id, BasicBlock& block, InstructionIt* inst_it) const {
     // Convert value to 32-bit if necessary
     uint32_t int32_id = ConvertTo32(id, block, inst_it);
 
-    const Type* type = nullptr;
-    const Constant* constant = type_manager_.FindConstantById(int32_id);
-    if (constant) {
-        type = &constant->type_;
-    } else {
-        const Instruction* inst = block.function_->FindInstruction(int32_id);
-        if (inst) {
-            type = type_manager_.FindTypeById(inst->TypeId());
-        }
-    }
+    const Type* type = type_manager_.FindTypeGlobal(*block.function_, int32_id);
     if (!type) {
         return int32_id;
     }
diff --git a/layers/gpuav/spirv/pass.h b/layers/gpuav/spirv/pass.h
index 448eb82..172757d 100644
--- a/layers/gpuav/spirv/pass.h
+++ b/layers/gpuav/spirv/pass.h
@@ -63,7 +63,6 @@
 
     const Instruction* GetDecoration(uint32_t id, spv::Decoration decoration) const;
     const Instruction* GetMemberDecoration(uint32_t id, uint32_t member_index, spv::Decoration decoration) const;
-    void GetDescriptorSetAndBinding(uint32_t variable_id, uint32_t& out_set, uint32_t& out_binding) const;
 
     uint32_t FindTypeByteSize(uint32_t type_id, uint32_t matrix_stride = 0, bool col_major = false, bool in_matrix = false) const;
     // Currently only used in the General Buffer OOB check, put here so it can be adapted for general use if needed
diff --git a/layers/gpuav/spirv/post_process_descriptor_indexing_pass.cpp b/layers/gpuav/spirv/post_process_descriptor_indexing_pass.cpp
index 2f3463b..97e64e2 100644
--- a/layers/gpuav/spirv/post_process_descriptor_indexing_pass.cpp
+++ b/layers/gpuav/spirv/post_process_descriptor_indexing_pass.cpp
@@ -15,10 +15,10 @@
 
 #include "post_process_descriptor_indexing_pass.h"
 
-#include "containers/container_utils.h"
 #include "module.h"
 #include "generated/gpuav_offline_spirv.h"
 #include "gpuav/shaders/gpuav_shaders_constants.h"
+#include "type_manager.h"
 #include "utils/hash_util.h"
 
 #include <iostream>
@@ -40,14 +40,15 @@
 uint32_t PostProcessDescriptorIndexingPass::GetLinkFunctionId() { return GetLinkFunction(link_function_id_, kOfflineFunction); }
 
 void PostProcessDescriptorIndexingPass::CreateFunctionCall(BasicBlock& block, InstructionIt* inst_it, const InstructionMeta& meta) {
-    const Constant& set_constant = type_manager_.GetConstantUInt32(meta.descriptor_set);
-    const Constant& binding_constant = type_manager_.GetConstantUInt32(meta.descriptor_binding);
-    const uint32_t descriptor_index_id = CastToUint32(meta.descriptor_index_id, block, inst_it);  // might be int32
+    const DescriptorInterface& interface = meta.access_path.variable->interface_;
+    const Constant& set_constant = type_manager_.GetConstantUInt32(interface.set);
+    const Constant& binding_constant = type_manager_.GetConstantUInt32(interface.binding);
+    const uint32_t descriptor_index_id = CastToUint32(meta.access_path.descriptor_index_id, block, inst_it);  // might be int32
 
     const auto& layout_lut = module_.interface_.instrumentation_dsl.set_index_to_bindings_layout_lut;
-    BindingLayout binding_layout = layout_lut[meta.descriptor_set][meta.descriptor_binding];
+    BindingLayout binding_layout = layout_lut[interface.set][interface.binding];
     const Constant& binding_layout_offset = type_manager_.GetConstantUInt32(binding_layout.start);
-    const Constant& variable_id_constant = type_manager_.GetConstantUInt32(meta.variable_id);
+    const Constant& variable_id_constant = type_manager_.GetConstantUInt32(meta.access_path.variable->Id());
 
     const uint32_t inst_position = meta.target_instruction->GetPositionOffset();
     const uint32_t inst_position_id = type_manager_.CreateConstantUInt32(inst_position).Id();
@@ -64,84 +65,12 @@
 
 bool PostProcessDescriptorIndexingPass::RequiresInstrumentation(const Function& function, const Instruction& inst,
                                                                 InstructionMeta& meta) {
-    const spv::Op opcode = (spv::Op)inst.Opcode();
-
-    const Instruction* var_inst = nullptr;
-    if (IsValueIn(opcode, {spv::OpLoad, spv::OpStore, spv::OpCooperativeMatrixLoadKHR, spv::OpCooperativeMatrixStoreKHR})) {
-        const AccessPath access_path = type_manager_.BuildAccessPath(function, inst);
-        if (!access_path.IsValid()) {
-            return false;
-        }
-
-        var_inst = &access_path.variable->inst_;
-
-        const uint32_t storage_class = access_path.variable->StorageClass();
-        if (storage_class != spv::StorageClassUniform && storage_class != spv::StorageClassStorageBuffer) {
-            return false;
-        }
-
-        const Type* pointer_type = access_path.variable->PointerType(type_manager_);
-        if (pointer_type->IsArray()) {
-            meta.descriptor_index_id = access_path.DescriptorIndexId();
-        } else {
-            // There is no array of this descriptor, so we essentially have an array of 1
-            meta.descriptor_index_id = type_manager_.GetConstantZeroUint32().Id();
-        }
-
-    } else {
-        // Reference is not load or store, so if it isn't a image-based reference, move on
-        const uint32_t image_word = OpcodeImageAccessPosition(opcode);
-        if (image_word == 0) {
-            return false;
-        }
-        if (opcode == spv::OpImageTexelPointer || opcode == spv::OpImage) {
-            return false;  // need to test if we can support these
-        }
-
-        const Instruction* load_inst = function.FindInstruction(inst.Word(image_word));
-        while (load_inst && (load_inst->Opcode() == spv::OpSampledImage || load_inst->Opcode() == spv::OpImage ||
-                             load_inst->Opcode() == spv::OpCopyObject)) {
-            load_inst = function.FindInstruction(load_inst->Operand(0));
-        }
-        if (!load_inst || load_inst->Opcode() != spv::OpLoad) {
-            return false;  // TODO: Handle additional possibilities?
-        }
-
-        var_inst = function.FindInstruction(load_inst->Operand(0));
-        if (!var_inst) {
-            // can be a global variable
-            const Variable* global_var = type_manager_.FindVariableById(load_inst->Operand(0));
-            var_inst = global_var ? &global_var->inst_ : nullptr;
-        }
-        if (!var_inst || (!var_inst->IsNonPtrAccessChain() && var_inst->Opcode() != spv::OpVariable)) {
-            return false;
-        }
-
-        if (var_inst->IsNonPtrAccessChain()) {
-            meta.descriptor_index_id = var_inst->Operand(1);
-
-            if (var_inst->Length() > 5) {
-                module_.InternalError(Name(), "OpAccessChain has more than 1 indexes");
-                return false;
-            }
-
-            const Variable* variable = type_manager_.FindVariableById(var_inst->Operand(0));
-            if (!variable) {
-                module_.InternalError(Name(), "OpAccessChain base is not a variable");
-                return false;
-            }
-            var_inst = &variable->inst_;
-        } else {
-            meta.descriptor_index_id = type_manager_.GetConstantZeroUint32().Id();
-        }
+    meta.access_path = type_manager_.BuildAccessPath(function, inst, true);
+    if (!meta.access_path.IsValid() || !meta.access_path.variable->IsDescriptor()) {
+        return false;
     }
 
-    assert(var_inst);
-    meta.variable_id = var_inst->ResultId();
-
-    GetDescriptorSetAndBinding(meta.variable_id, meta.descriptor_set, meta.descriptor_binding);
-
-    if (meta.descriptor_set >= glsl::kDebugInputBindlessMaxDescSets) {
+    if (meta.access_path.variable->interface_.set >= glsl::kDebugInputBindlessMaxDescSets) {
         module_.InternalWarning(Name(), "Tried to use a descriptor slot over the current max limit");
         return false;
     }
@@ -185,10 +114,12 @@
                     continue;
                 }
 
-                const uint32_t hash_descriptor_index_id =
-                    pc_access.next_alias_id == meta.descriptor_index_id ? pc_access.descriptor_index_id : meta.descriptor_index_id;
-                uint32_t hash_content[4] = {meta.descriptor_set, meta.descriptor_binding, hash_descriptor_index_id,
-                                            meta.variable_id};
+                const uint32_t hash_descriptor_index_id = pc_access.next_alias_id == meta.access_path.descriptor_index_id
+                                                              ? pc_access.descriptor_index_id
+                                                              : meta.access_path.descriptor_index_id;
+                uint32_t hash_content[4] = {meta.access_path.variable->interface_.set,
+                                            meta.access_path.variable->interface_.binding, hash_descriptor_index_id,
+                                            meta.access_path.variable->Id()};
                 const uint32_t hash = hash_util::Hash32(hash_content, sizeof(uint32_t) * 4);
                 if (function_duplicate_tracker.FindAndUpdate(block_duplicate_tracker, hash)) {
                     continue;  // duplicate detected
diff --git a/layers/gpuav/spirv/post_process_descriptor_indexing_pass.h b/layers/gpuav/spirv/post_process_descriptor_indexing_pass.h
index 3b7fdd5..fe68cc0 100644
--- a/layers/gpuav/spirv/post_process_descriptor_indexing_pass.h
+++ b/layers/gpuav/spirv/post_process_descriptor_indexing_pass.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2024-2025 LunarG, Inc.
+/* Copyright (c) 2024-2026 LunarG, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -15,6 +15,7 @@
 #pragma once
 
 #include <stdint.h>
+#include "type_manager.h"
 #include "pass.h"
 
 namespace gpuav {
@@ -35,10 +36,8 @@
     // This is metadata tied to a single instruction gathered during RequiresInstrumentation() to be used later
     struct InstructionMeta {
         const Instruction* target_instruction = nullptr;
-        uint32_t descriptor_set = 0;
-        uint32_t descriptor_binding = 0;
-        uint32_t descriptor_index_id = 0;
-        uint32_t variable_id = 0;
+
+        AccessPath access_path;
     };
 
     bool RequiresInstrumentation(const Function& function, const Instruction& inst, InstructionMeta& meta);
diff --git a/layers/gpuav/spirv/shared_memory_data_race_pass.cpp b/layers/gpuav/spirv/shared_memory_data_race_pass.cpp
index 388f1b7..872099a 100644
--- a/layers/gpuav/spirv/shared_memory_data_race_pass.cpp
+++ b/layers/gpuav/spirv/shared_memory_data_race_pass.cpp
@@ -45,21 +45,6 @@
     return GetLinkFunction(link_function_id_[meta.function_idx], kOfflineFunction[meta.function_idx]);
 }
 
-// The goal of Function::FindInstruction is you should know the instruction is in the Function.
-// For walking the indexes of an access chain in this pass, we want a global lookup
-const Instruction* SharedMemoryDataRacePass::FindInstructionGlobal(const Function& function, uint32_t id) const {
-    if (auto ret = function.FindInstruction(id)) {
-        return ret;
-    }
-    if (auto ret = module_.type_manager_.FindConstantById(id)) {
-        return &ret->inst_;
-    }
-    if (auto ret = module_.type_manager_.FindVariableById(id)) {
-        return &ret->inst_;
-    }
-    return nullptr;
-}
-
 void SharedMemoryDataRacePass::CreateFunctionCall(const Function& function, BasicBlock& block, InstructionIt* inst_it,
                                                   const InstructionMeta& meta) {
     const uint32_t function_def = GetLinkFunctionId(meta);
@@ -92,8 +77,7 @@
                 const Type* coopmat_type;
                 if (store) {
                     const uint32_t object_id = inst.Word(2);
-                    const Instruction* object_inst = FindInstructionGlobal(function, object_id);
-                    coopmat_type = type_manager_.FindTypeById(object_inst->TypeId());
+                    coopmat_type = type_manager_.FindTypeGlobal(function, object_id);
                 } else {
                     coopmat_type = type_manager_.FindTypeById(inst.TypeId());
                 }
@@ -106,8 +90,7 @@
                 const uint32_t ptr_id = inst.Operand(0);  // works with both store and loads
 
                 // get type of pointee
-                auto ptr_inst = FindInstructionGlobal(function, ptr_id);
-                const Type* ptr_type = type_manager_.FindTypeById(ptr_inst->TypeId());
+                const Type* ptr_type = type_manager_.FindTypeGlobal(function, ptr_id);
                 const Type* scalar_elem_type = type_manager_.FindChildType(*ptr_type, 0);
 
                 const Type& uint32_type = type_manager_.GetTypeInt(32, false);
@@ -192,7 +175,7 @@
         return true;
     }
 
-    const AccessPath access_path = type_manager_.BuildAccessPath(function, inst);
+    const AccessPath access_path = type_manager_.BuildAccessPath(function, inst, true);
     if (!access_path.IsValid()) {
         return false;
     } else if (access_path.variable->StorageClass() != spv::StorageClassWorkgroup) {
@@ -206,16 +189,14 @@
     // to the variable's pointee type in case of no access chains.
     const Type* ptr_elem_type = type_manager_.FindChildType(*type_manager_.FindTypeById(access_path.variable->inst_.Word(1)), 0);
     for (auto ac : access_path.ac_list) {
-        auto ptr = FindInstructionGlobal(function, ac->Word(3));
-        const Type* base_ptr_type = type_manager_.FindTypeById(ptr->Word(1));
+        const Type* base_ptr_type = type_manager_.FindTypeGlobal(function, ac->Word(3));
 
         // Get the base pointer pointee type.
         ptr_elem_type = type_manager_.FindChildType(*base_ptr_type, 0);
 
         for (uint32_t i = 4; i < ac->Length(); ++i) {
             uint32_t idx_id = ac->Word(i);
-            auto idx_inst = FindInstructionGlobal(function, idx_id);
-            auto idx_type = type_manager_.FindTypeById(idx_inst->Word(1));
+            auto idx_type = type_manager_.FindTypeGlobal(function, idx_id);
             assert(idx_type->inst_.Opcode() == spv::OpTypeInt);
             // convert to u32 if needed
             if (idx_type->inst_.Word(2) != 32) {
diff --git a/layers/gpuav/spirv/shared_memory_data_race_pass.h b/layers/gpuav/spirv/shared_memory_data_race_pass.h
index 1839599..c9f451e 100644
--- a/layers/gpuav/spirv/shared_memory_data_race_pass.h
+++ b/layers/gpuav/spirv/shared_memory_data_race_pass.h
@@ -53,8 +53,6 @@
 
     uint32_t GetLinkFunctionId(const InstructionMeta& meta);
 
-    const Instruction* FindInstructionGlobal(const Function& function, uint32_t id) const;
-
     // Function IDs to link in
     enum FunctionNames {
         INIT_SHADOW = 0,
diff --git a/layers/gpuav/spirv/trace_ray_pass.cpp b/layers/gpuav/spirv/trace_ray_pass.cpp
index f43b8e7..c50aa95 100644
--- a/layers/gpuav/spirv/trace_ray_pass.cpp
+++ b/layers/gpuav/spirv/trace_ray_pass.cpp
@@ -24,6 +24,7 @@
 #include <iostream>
 
 #include "generated/gpuav_offline_spirv.h"
+#include "type_manager.h"
 
 namespace gpuav {
 namespace spirv {
@@ -58,52 +59,26 @@
         return {};
     }
 
-    const AccessPath access_path = type_manager_.BuildAccessPath(function, *as_op_load_inst);
+    const AccessPath access_path = type_manager_.BuildAccessPath(function, *as_op_load_inst, true);
     if (!access_path.IsValid()) {
         return {};
     }
 
-    const Type* descriptor_type = access_path.variable->PointerType(type_manager_);
-    if (!descriptor_type || descriptor_type->spv_type_ == SpvType::kRuntimeArray) {
+    if (access_path.pointer_type->spv_type_ == SpvType::kRuntimeArray) {
         return {};  // TODO - Currently we mark these as "bindless"
     }
 
-    const bool is_descriptor_array = descriptor_type->IsArray();
-    if (is_descriptor_array && access_path.ac_list.empty()) {
-        return {};  // array descriptor without an access chain is invalid SPIR-V
-    }
-
-    uint32_t descriptor_index_id = 0;
-    if (is_descriptor_array) {
-        // Because you can't have 2D array of descriptors, the first index of the last accessChain is the descriptor index
-        descriptor_index_id = access_path.DescriptorIndexId();
-    } else {
-        // There is no array of this descriptor, so we essentially have an array of 1
-        descriptor_index_id = type_manager_.GetConstantZeroUint32().Id();
-    }
-
-    uint32_t descriptor_set = 0;
-    uint32_t descriptor_binding = 0;
-    for (const auto& annotation : module_.annotations_) {
-        if (annotation->Opcode() == spv::OpDecorate && annotation->Word(1) == access_path.variable->Id()) {
-            if (annotation->Word(2) == spv::DecorationDescriptorSet) {
-                descriptor_set = annotation->Word(3);
-            } else if (annotation->Word(2) == spv::DecorationBinding) {
-                descriptor_binding = annotation->Word(3);
-            }
-        }
-    }
-
-    if (descriptor_set >= glsl::kDebugInputBindlessMaxDescSets) {
+    const DescriptorInterface& interface = access_path.variable->interface_;
+    if (interface.set >= glsl::kDebugInputBindlessMaxDescSets) {
         module_.InternalWarning(Name(), "Tried to use a descriptor slot over the current max limit");
         return {};
     }
 
-    const Constant& desc_set_constant = type_manager_.GetConstantUInt32(descriptor_set);
-    const uint32_t desc_index_id = CastToUint32(descriptor_index_id, block, trace_ray_inst_it);  // might be int32
+    const Constant& desc_set_constant = type_manager_.GetConstantUInt32(interface.set);
+    const uint32_t desc_index_id = CastToUint32(access_path.descriptor_index_id, block, trace_ray_inst_it);  // might be int32
 
     const auto& layout_lut = module_.interface_.instrumentation_dsl.set_index_to_bindings_layout_lut;
-    BindingLayout binding_layout = layout_lut[descriptor_set][descriptor_binding];
+    BindingLayout binding_layout = layout_lut[interface.set][interface.binding];
     const Constant& binding_layout_offset = type_manager_.GetConstantUInt32(binding_layout.start);
 
     const uint32_t function_result = module_.TakeNextId();
diff --git a/layers/gpuav/spirv/type_manager.cpp b/layers/gpuav/spirv/type_manager.cpp
index 214de9d..4ef65a8 100644
--- a/layers/gpuav/spirv/type_manager.cpp
+++ b/layers/gpuav/spirv/type_manager.cpp
@@ -16,6 +16,7 @@
 #include "type_manager.h"
 #include <cstdint>
 #include <spirv/unified1/spirv.hpp>
+#include "containers/container_utils.h"
 #include "generated/spirv_grammar_helper.h"
 #include "module.h"
 
@@ -37,10 +38,41 @@
     return true;
 }
 
+DescriptorInterface Variable::FindDescriptorInterface(const Module& module, const Instruction& inst) {
+    DescriptorInterface descriptor;
+    // Only allowed descriptor storage classes
+    // https://docs.vulkan.org/spec/latest/chapters/interfaces.html#interfaces-resources-storage-class-correspondence
+    if (IsValueIn(inst.StorageClass(), {
+                                           spv::StorageClassUniform,
+                                           spv::StorageClassUniformConstant,
+                                           spv::StorageClassStorageBuffer,
+                                           spv::StorageClassTileAttachmentQCOM,
+                                       })) {
+        const uint32_t variable_id = inst.ResultId();
+        for (const auto& annotation : module.annotations_) {
+            if (annotation->Opcode() == spv::OpDecorate && annotation->Word(1) == variable_id) {
+                if (annotation->Word(2) == spv::DecorationDescriptorSet) {
+                    descriptor.set = annotation->Word(3);
+                } else if (annotation->Word(2) == spv::DecorationBinding) {
+                    descriptor.binding = annotation->Word(3);
+                } else if (annotation->Word(2) == spv::DecorationBuiltIn && annotation->Word(3) == spv::BuiltInResourceHeapEXT) {
+                    descriptor.is_resource_heap = true;
+                } else if (annotation->Word(2) == spv::DecorationBuiltIn && annotation->Word(3) == spv::BuiltInSamplerHeapEXT) {
+                    descriptor.is_sampler_heap = true;
+                }
+            }
+        }
+    }
+    return descriptor;
+}
+
 // return %A in:
 //   %B = OpTypePointer Input %A
 //   %C = OpVariable %B Input
-const Type* Variable::PointerType(TypeManager& type_manager_) const {
+const Type* Variable::PointerType(const TypeManager& type_manager_) const {
+    // If we are hitting kUntypedPointer, the logic need to get the type info either from the Base Type of the UntypedAccessChain or
+    // if it cares about the type, it can find it at the access type (example to help show how to getting the type
+    // https://godbolt.org/z/ejf1TGx8Y)
     assert(type_.spv_type_ == SpvType::kPointer || type_.spv_type_ == SpvType::kForwardPointer);
     uint32_t type_id = type_.inst_.Word(3);
     return type_manager_.FindTypeById(type_id);
@@ -161,26 +193,6 @@
     return (type == id_to_type_.end()) ? nullptr : type->second.get();
 }
 
-// It is common to have things like
-//
-// %uint = OpTypeInt 32 0
-// %ptr_uint = OpTypePointer StorageBuffer %uint
-// %ac = OpAccessChain %ptr_uint %var %int_1
-//
-// Where you have %ptr_uint and want to know it is OpTypeInt
-// This function is like FindTypeById() but it will bypass the OpTypePointer for you (if it is there)
-// There is also a matching Variable::PointerType()
-const Type* TypeManager::FindValueTypeById(uint32_t id) const {
-    const Type* pointer_type = FindTypeById(id);
-    if (!pointer_type) {
-        return nullptr;
-    } else if (pointer_type->spv_type_ != SpvType::kPointer && pointer_type->spv_type_ != SpvType::kForwardPointer) {
-        return pointer_type;
-    } else {
-        return FindTypeById(pointer_type->inst_.Word(3));
-    }
-}
-
 const Type* TypeManager::FindFunctionType(const Instruction& inst) const {
     const uint32_t inst_length = inst.Length();
     for (const auto& type : function_types_) {
@@ -202,6 +214,23 @@
     return nullptr;
 }
 
+// ONLY USE IF NEEDED!
+// The goal of Function::FindInstruction is you should know the instruction is in the Function.
+// For walking the indexes of an access chain in this pass, we want a global lookup
+// This manily happens because the value of the index could be both a OpConstant value or loaded in the function.
+const Type* TypeManager::FindTypeGlobal(const Function& function, uint32_t id) const {
+    if (auto ret = function.FindInstruction(id)) {
+        return FindTypeById(ret->TypeId());
+    }
+    if (auto ret = module_.type_manager_.FindConstantById(id)) {
+        return &ret->type_;
+    }
+    if (auto ret = module_.type_manager_.FindVariableById(id)) {
+        return &ret->type_;
+    }
+    return nullptr;
+}
+
 const Type& TypeManager::GetTypeVoid() {
     if (void_type) {
         return *void_type;
@@ -736,32 +765,208 @@
     return AddConstant(std::move(new_inst), type);
 }
 
-const AccessPath TypeManager::BuildAccessPath(const Function& function, const Instruction& inst) const {
+static bool IsAccessInstruction(const Instruction& inst) {
+    const spv::Op opcode = (spv::Op)inst.Opcode();
+    if (IsValueIn(opcode, {spv::OpLoad, spv::OpStore, spv::OpCooperativeMatrixLoadKHR, spv::OpCooperativeMatrixStoreKHR})) {
+        return true;
+    } else if (IsValueIn(opcode, {spv::OpImageTexelPointer, spv::OpImage})) {
+        // OpImageTexelPointer is for image atomics handled at the atomic instruction
+        // OpImage is describing an action, rather than the input to or the output from an action.
+        return false;
+    } else if (AtomicOperation(opcode)) {
+        return true;  // any atomic
+    } else if (OpcodeImageAccessPosition(opcode) != 0) {
+        return true;  // image access
+    }
+
+    return false;
+}
+
+// |ignore_image_sampler_skip| used for passes that don't care about safe_mode that want to ignore until we fix support
+const AccessPath TypeManager::BuildAccessPath(const Function& function, const Instruction& inst, bool ignore_image_sampler_skip) {
     AccessPath path;
 
-    // |Operand 0| works for both Store/Load
-    const uint32_t ptr_id = inst.Operand(0);
+    if (!IsAccessInstruction(inst)) {
+        return path;
+    }
+
+    const spv::Op opcode = (spv::Op)inst.Opcode();
+    path.access_type = FindTypeById(inst.TypeId());
+    // if it is a store, then we need to look a the type it loading
+    if (!path.access_type) {
+        if (opcode == spv::OpStore || opcode == spv::OpCooperativeMatrixStoreKHR) {
+            path.access_type = FindTypeGlobal(function, inst.Operand(1));  // object id
+        } else if (opcode == spv::OpImageWrite) {
+            path.access_type = FindTypeGlobal(function, inst.Operand(2));  // texel id
+        } else if (opcode == spv::OpAtomicStore) {
+            path.access_type = FindTypeGlobal(function, inst.Operand(3));  // value id
+        } else {
+            assert(false);  // not being handled
+        }
+    }
+    assert(path.access_type);
+
+    const bool image_sampler_access = path.access_type->spv_type_ == SpvType::kImage ||
+                                      path.access_type->spv_type_ == SpvType::kSampledImage ||
+                                      path.access_type->spv_type_ == SpvType::kSampler;
+
+    // This is just loading the image handle, this alone is the not the access.
+    // There will be an access (OpImageWrite, OpImageSampleImplicitLod, etc) later which has the real access information
+    if (opcode == spv::OpLoad && image_sampler_access) {
+        return path;
+    }
+
+    // TODO - Need to add Storage Image support (https://gitlab.khronos.org/vulkan/vulkan/-/issues/3977)
+    // TODO - we currently don't handle storage image/sampled/samplers in safe mode as we put a OpConstantNull in the OpPhi which
+    // doesn't make sense as it needs to be a OpTypeImage. The way around this will be having a null descriptor ready and point
+    // there in the case it is wrong
+    if (!ignore_image_sampler_skip && module_.settings_.safe_mode && image_sampler_access) {
+        return path;
+    }
+
+    const Instruction* sampler_load_inst = nullptr;
+    uint32_t ptr_id = OpcodeImageAccessPosition(opcode);
+    // Basically there are 2 flows, images and non-images
+    const bool image_access = ptr_id != 0;
+    if (image_access) {
+        path.image_load_inst = function.FindInstruction(inst.Word(ptr_id));
+        const Instruction* load_inst = path.image_load_inst;
+        uint32_t load_operand = 0;
+        while (load_inst && (load_inst->Opcode() == spv::OpSampledImage || load_inst->Opcode() == spv::OpImage ||
+                             load_inst->Opcode() == spv::OpCopyObject)) {
+            if (load_inst->Opcode() == spv::OpSampledImage) {
+                sampler_load_inst = function.FindInstruction(load_inst->Operand(1));
+            }
+
+            load_operand = load_inst->Operand(0);
+            load_inst = function.FindInstruction(load_operand);
+        }
+
+        // Note - we never "store" an image, we only load its handle and store the "texel" data
+        if (!load_inst || load_inst->Opcode() != spv::OpLoad) {
+            // TODO - should be able to remove this check, its invalid SPIR-V
+            // https://gitlab.khronos.org/vulkan/vulkan/-/merge_requests/7753
+            assert(IsUndef(load_operand));
+            return path;
+        }
+
+        path.is_combined_image_sampler = sampler_load_inst == nullptr && ImageSampleOperation(opcode);
+
+        // From here the load should look like a non-image access
+        ptr_id = load_inst->Operand(0);
+    } else {
+        // |Operand 0| works for both Store/Load
+        ptr_id = inst.Operand(0);
+    }
 
     // Buffer/Image Descriptor will always have an access chains, but some cases can have direct access.
     // TaskPayload can be a scalar that does a direct variable access
     // An non-array AccelerationStructure (which uses UniformConstant storage class)
     path.variable = FindVariableById(ptr_id);
-    if (path.variable) {
+    const Instruction* next_access_chain = nullptr;
+    if (!path.variable) {
+        next_access_chain = function.FindInstruction(ptr_id);
+
+        // We need to walk down possibly multiple chained OpAccessChains or OpCopyObject to get the variable
+        while (next_access_chain && next_access_chain->IsNonPtrAccessChain()) {
+            // inserting in front allows us to walk over the loop from the front
+            path.ac_list.insert(path.ac_list.begin(), next_access_chain);
+
+            const uint32_t base_operand = next_access_chain->IsUntypedAccessChain() ? 1 : 0;
+            const uint32_t access_chain_base_id = next_access_chain->Operand(base_operand);
+            path.variable = FindVariableById(access_chain_base_id);
+            if (path.variable) {
+                break;  // found
+            }
+            next_access_chain = function.FindInstruction(access_chain_base_id);
+        }
+
+        if (next_access_chain->Opcode() == spv::OpBufferPointerEXT) {
+            const uint32_t buffer_pointer_id = next_access_chain->Operand(0);
+            // For now assume this is a 1D array into the descriptor array
+            // https://gitlab.khronos.org/spirv/SPIR-V/-/issues/942
+            next_access_chain = function.FindInstruction(buffer_pointer_id);
+            assert(next_access_chain->Opcode() == spv::OpUntypedAccessChainKHR);
+            path.ac_list.insert(path.ac_list.begin(), next_access_chain);
+            const uint32_t untyped_variable_id = next_access_chain->Operand(1);
+            path.variable = FindVariableById(untyped_variable_id);
+        } else if (next_access_chain->Opcode() == spv::OpImageTexelPointer) {
+            // Storage Images (and image atomics)
+            const Instruction* access_chain_inst = function.FindInstruction(next_access_chain->Operand(0));
+            if (access_chain_inst && access_chain_inst->IsNonPtrAccessChain()) {
+                next_access_chain = access_chain_inst;
+                path.ac_list.insert(path.ac_list.begin(), next_access_chain);
+                path.variable = FindVariableById(access_chain_inst->Operand(0));
+            } else {
+                // if no array, will point right to a variable
+                path.variable = FindVariableById(next_access_chain->Operand(0));
+            }
+        }
+    }
+
+    if (!path.variable) {
+        // Two know spots this occur is Function Variables and PhysicalStorageBuffer access
+        assert((next_access_chain->Opcode() == spv::OpVariable && next_access_chain->StorageClass() == spv::StorageClassFunction) ||
+               FindTypeById(next_access_chain->TypeId())->spv_type_ == SpvType::kPointer);
+        return path;  // not a valid access path
+    }
+
+    // Welcome to SPV_KHR_untyped_pointers soldier!
+    // Untyped we get the pointer type from the last access chain
+    // But typed, the OpVariable had it
+    if (next_access_chain && next_access_chain->IsUntypedAccessChain()) {
+        const uint32_t pointer_type_id = next_access_chain->Operand(0);
+        path.pointer_type = FindTypeById(pointer_type_id);
+    } else {
+        path.pointer_type = path.variable->PointerType(*this);
+    }
+    assert(path.pointer_type);
+
+    // Everything else is just for descriptor variable access
+    if (!path.variable->IsDescriptor()) {
         return path;
     }
 
-    const Instruction* next_access_chain = function.FindInstruction(ptr_id);
-    // We need to walk down possibly multiple chained OpAccessChains or OpCopyObject to get the variable
-    while (next_access_chain && next_access_chain->IsNonPtrAccessChain()) {
-        // inserting in front allows us to walk over the loop from the front
-        path.ac_list.insert(path.ac_list.begin(), next_access_chain);
+    if (path.pointer_type->IsArray()) {
+        assert(next_access_chain);  // no way to have an array otherwise
+        const uint32_t index_0_operand = next_access_chain->IsUntypedAccessChain() ? 2 : 1;
+        path.descriptor_index_id = next_access_chain->Operand(index_0_operand);
+    } else {
+        // There is no array of this descriptor, so we essentially have an array of 1
+        path.descriptor_index_id = GetConstantZeroUint32().Id();
 
-        const uint32_t access_chain_base_id = next_access_chain->Operand(0);
-        path.variable = FindVariableById(access_chain_base_id);
-        if (path.variable) {
-            break;  // found
+        // Hack for Offset in Heaps until get better understanding
+        if (path.variable->interface_.IsHeap() && path.pointer_type->spv_type_ == SpvType::kStruct) {
+            assert(next_access_chain->IsUntypedAccessChain() && next_access_chain->Length() == 7);
+            // https://godbolt.org/z/hWz84zdTW - this is required to be a constant
+            const Constant* struct_member_index_constant = FindConstantById(next_access_chain->Operand(2));
+            assert(struct_member_index_constant);
+            path.heap_offset_member_index = struct_member_index_constant->GetValueUint32();
+            path.descriptor_index_id = next_access_chain->Operand(3);
         }
-        next_access_chain = function.FindInstruction(access_chain_base_id);
+    }
+
+    // When using a SAMPLED_IMAGE and SAMPLER, they are accessed together so we need check for 2 descriptors
+    if (sampler_load_inst) {
+        assert(sampler_load_inst->Opcode() == spv::OpLoad);
+
+        ptr_id = sampler_load_inst->Operand(0);
+        path.sampler_variable = FindVariableById(ptr_id);
+        if (path.sampler_variable) {
+            path.sampler_descriptor_index_id = GetConstantZeroUint32().Id();
+        } else {
+            // descriptor array
+            // this is a lazy way to assume the sampler is can only be 1D and a single access chain away
+            next_access_chain = function.FindInstruction(ptr_id);
+            assert(next_access_chain->IsNonPtrAccessChain());
+
+            const uint32_t base_operand = next_access_chain->IsUntypedAccessChain() ? 1 : 0;
+            const uint32_t access_chain_base_id = next_access_chain->Operand(base_operand);
+            path.sampler_variable = FindVariableById(access_chain_base_id);
+
+            const uint32_t index_0_operand = base_operand + 1;
+            path.sampler_descriptor_index_id = next_access_chain->Operand(index_0_operand);
+        }
     }
 
     return path;
@@ -770,7 +975,7 @@
 const Variable& TypeManager::AddVariable(std::unique_ptr<Instruction> new_inst, const Type& type) {
     const auto& inst = module_.types_values_constants_.emplace_back(std::move(new_inst));
 
-    id_to_variable_[inst->ResultId()] = std::make_unique<Variable>(type, *inst);
+    id_to_variable_[inst->ResultId()] = std::make_unique<Variable>(module_, type, *inst);
     const Variable* new_variable = id_to_variable_[inst->ResultId()].get();
 
     if (new_variable->StorageClass() == spv::StorageClassInput) {
@@ -788,6 +993,8 @@
     return *new_variable;
 }
 
+// Note - this does not include Function variables and will not find them
+// currently no need to track them for any GPU-AV checks
 const Variable* TypeManager::FindVariableById(uint32_t id) const {
     auto variable = id_to_variable_.find(id);
     return (variable == id_to_variable_.end()) ? nullptr : variable->second.get();
diff --git a/layers/gpuav/spirv/type_manager.h b/layers/gpuav/spirv/type_manager.h
index 23f943c..5e09a49 100644
--- a/layers/gpuav/spirv/type_manager.h
+++ b/layers/gpuav/spirv/type_manager.h
@@ -18,6 +18,7 @@
 #include <vector>
 #include <memory>
 #include "containers/custom_containers.h"
+#include "containers/limits.h"
 #include "state_tracker/shader_instruction.h"
 #include "generated/spirv_grammar_helper.h"
 
@@ -121,26 +122,59 @@
     const bool is_spec_constant_;
 };
 
+struct DescriptorInterface {
+    // Set/Binding for decorations
+    uint32_t set = vvl::kNoIndex32;
+    uint32_t binding = vvl::kNoIndex32;
+
+    // For SPV_EXT_descriptor_heap
+    bool is_resource_heap = false;
+    bool is_sampler_heap = false;
+    bool IsHeap() const { return is_resource_heap || is_sampler_heap; }
+};
+
 // Represents a global OpVariable found before the first function
 struct Variable {
-    Variable(const Type& type, const Instruction& inst) : type_(type), inst_(inst) {}
+    Variable(const Module& module, const Type& type, const Instruction& inst)
+        : type_(type), inst_(inst), interface_(FindDescriptorInterface(module, inst)) {}
 
     uint32_t Id() const { return inst_.ResultId(); }
-    spv::StorageClass StorageClass() const { return spv::StorageClass(inst_.Word(3)); }
-    const Type* PointerType(TypeManager& type_manager_) const;
+    spv::StorageClass StorageClass() const { return inst_.StorageClass(); }
+    const Type* PointerType(const TypeManager& type_manager_) const;
 
     const Type& type_;
     const Instruction& inst_;
+
+    const DescriptorInterface interface_;
+
+    // Help used to know if you have a PushConstant, Input/Output, etc instead
+    bool IsDescriptor() const {
+        return interface_.IsHeap() || (interface_.set != vvl::kNoIndex32 && interface_.binding != vvl::kNoIndex32);
+    }
+
+  protected:
+    static DescriptorInterface FindDescriptorInterface(const Module& module, const Instruction& inst);
 };
 
 // We often want to walk the SSA from an "access" (load, store, atomic, etc) to the Variable it is referencing. There can be a
 // single OpAccessChain or multiple, and this struct holds this information.
 // Background info: https://github.com/KhronosGroup/SPIRV-Guide/blob/main/chapters/access_chains.md
+//
+// Note - currently this is very heavily leaned towards use of descriptors, but will work for any variable type
 struct AccessPath {
+    // The type of the access itself (what type it will store or load)
+    const Type* access_type = nullptr;
+
+    // This the %ptr_type in
+    //   %ptr_type = OpTypeArray
+    //   %ptr = OpTypePointer StorageBuffer %ptr_type
+    //   %var = OpVariable %ptr StorageBuffer
+    const Type* pointer_type = nullptr;
+
     // The variable at the end of the access chain
     const Variable* variable = nullptr;
 
-    bool IsValid() const { return variable != nullptr; }
+    bool IsValid() const { return access_type != nullptr && pointer_type != nullptr && variable != nullptr; }
 
     // List of OpAccessChains from the variable to the "access"
     // - The front() will be closest to the OpVariable
@@ -150,10 +184,26 @@
     // Note: GLSL will try to always create a single large OpAccessChain
     std::vector<const Instruction*> ac_list;
 
-    // When dealing with an array of descriptors, the access closest to the variable will have it
-    uint32_t DescriptorIndexId() const { return ac_list.front()->Operand(1); }
+    //
+    // Descriptor variable access related info
+    //
 
-    uint32_t FinalAccessedType() const { return ac_list.back()->TypeId(); }
+    // Optional variable of seperate sampler descriptor (still null if combinedImageSampler)
+    const Variable* sampler_variable = nullptr;
+    bool is_combined_image_sampler = false;
+    bool HasSampler() const { return sampler_variable != nullptr || is_combined_image_sampler; }
+
+    // The OpLoad to access an image descriptor
+    const Instruction* image_load_inst = nullptr;
+
+    // Most access paths are used to get the descriptor variable.
+    // This is the ID of the uint that indexes in the array (or constant zero if no array)
+    uint32_t descriptor_index_id = 0;
+    // Optional index if there is a seperate sampler as well
+    uint32_t sampler_descriptor_index_id = 0;
+
+    // TODO - Need to handle OffsetIdEXT correctly, this is a dumb hack
+    uint32_t heap_offset_member_index = 0;
 };
 
 // In charge of tracking all Types, Constants, and Variable in the module.
@@ -170,8 +220,8 @@
     const Type& AddType(std::unique_ptr<Instruction> new_inst, SpvType spv_type);
 
     const Type* FindTypeById(uint32_t id) const;
-    const Type* FindValueTypeById(uint32_t id) const;
     const Type* FindFunctionType(const Instruction& inst) const;
+    const Type* FindTypeGlobal(const Function& function, uint32_t id) const;
     // There shouldn't be a case where we need to query for a specific type, but then not add it if not found.
     const Type& GetTypeVoid();
     const Type& GetTypeBool();
@@ -215,7 +265,7 @@
     const Constant& GetConstantZeroVector(const Type& vector_type);
     const Constant& GetConstantNull(const Type& type);
 
-    const AccessPath BuildAccessPath(const Function& function, const Instruction& inst) const;
+    const AccessPath BuildAccessPath(const Function& function, const Instruction& inst, bool ignore_image_sampler_skip = false);
 
     const Variable& AddVariable(std::unique_ptr<Instruction> new_inst, const Type& type);
     const Variable* FindVariableById(uint32_t id) const;
@@ -230,9 +280,6 @@
     void AddUndef(std::unique_ptr<Instruction> new_inst);
     bool IsUndef(uint32_t id) const;
 
-    void AddUntypedVariable(uint32_t id) { untyped_variable_set_.insert(id); };
-    bool IsUntypedVariable(uint32_t id) const { return untyped_variable_set_.find(id) != untyped_variable_set_.end(); }
-
   private:
     Module& module_;
 
@@ -242,10 +289,6 @@
     vvl::unordered_map<uint32_t, std::unique_ptr<Constant>> id_to_constant_;
     vvl::unordered_map<uint32_t, std::unique_ptr<Variable>> id_to_variable_;
 
-    // Currently don't fully support untyped pointers, but to allow things not to break, start tracking them so when searching for
-    // Variable, we can assert if not found, it is an OpUntypedVariableKHR
-    vvl::unordered_set<uint32_t> untyped_variable_set_;
-
     // Create faster lookups for specific types
     // some types are base types and only will be one
     const Type* void_type = nullptr;
diff --git a/layers/gpuav/spirv/vertex_attribute_fetch_oob_pass.cpp b/layers/gpuav/spirv/vertex_attribute_fetch_oob_pass.cpp
index d9f969b..7642771 100644
--- a/layers/gpuav/spirv/vertex_attribute_fetch_oob_pass.cpp
+++ b/layers/gpuav/spirv/vertex_attribute_fetch_oob_pass.cpp
@@ -54,8 +54,6 @@
         for (; word < total_words; word++) {
             const uint32_t interface_id = entry_point->Word(word);
             const Variable* variable = type_manager_.FindVariableById(interface_id);
-            // guaranteed by spirv-val to be a OpVariable/OpUntypedVariable
-            assert(variable || type_manager_.IsUntypedVariable(interface_id));
             if (variable && variable->StorageClass() == spv::StorageClassInput) {
                 found_input = true;
                 break;
diff --git a/layers/state_tracker/shader_instruction.cpp b/layers/state_tracker/shader_instruction.cpp
index ff95a6d..89e4146 100644
--- a/layers/state_tracker/shader_instruction.cpp
+++ b/layers/state_tracker/shader_instruction.cpp
@@ -196,13 +196,20 @@
 
 bool Instruction::IsNonPtrAccessChain() const {
     const uint32_t opcode = Opcode();
-    return opcode == spv::OpAccessChain || opcode == spv::OpInBoundsAccessChain;
+    return opcode == spv::OpAccessChain || opcode == spv::OpInBoundsAccessChain || opcode == spv::OpUntypedAccessChainKHR ||
+           opcode == spv::OpUntypedInBoundsAccessChainKHR;
 }
 
 bool Instruction::IsAccessChain() const {
     const uint32_t opcode = Opcode();
-    return opcode == spv::OpAccessChain || opcode == spv::OpPtrAccessChain || opcode == spv::OpInBoundsAccessChain ||
-           opcode == spv::OpInBoundsPtrAccessChain;
+    return IsNonPtrAccessChain() || opcode == spv::OpPtrAccessChain || opcode == spv::OpInBoundsPtrAccessChain ||
+           opcode == spv::OpUntypedPtrAccessChainKHR || opcode == spv::OpUntypedInBoundsPtrAccessChainKHR;
+}
+
+bool Instruction::IsUntypedAccessChain() const {
+    const uint32_t opcode = Opcode();
+    return opcode == spv::OpUntypedAccessChainKHR || opcode == spv::OpUntypedInBoundsAccessChainKHR ||
+           opcode == spv::OpUntypedPtrAccessChainKHR || opcode == spv::OpUntypedInBoundsPtrAccessChainKHR;
 }
 
 bool Instruction::IsTensor() const { return (Opcode() == spv::OpTypeTensorARM); }
@@ -577,6 +584,10 @@
         case spv::OpImageQueryLod:
         case spv::OpFragmentFetchAMD:
         case spv::OpFragmentMaskFetchAMD:
+        // Image atomics can only be accessed with these 3 atomics
+        case spv::OpAtomicLoad:
+        case spv::OpAtomicStore:
+        case spv::OpAtomicExchange:
             break;
 
         case spv::OpImageSparseTexelsResident:
diff --git a/layers/state_tracker/shader_instruction.h b/layers/state_tracker/shader_instruction.h
index bb21241..18cca3b 100644
--- a/layers/state_tracker/shader_instruction.h
+++ b/layers/state_tracker/shader_instruction.h
@@ -79,6 +79,7 @@
     bool IsVector() const;
     bool IsNonPtrAccessChain() const;
     bool IsAccessChain() const;
+    bool IsUntypedAccessChain() const;
     // Helpers for OpTypeImage
     bool IsTensor() const;
     bool IsConstant() const;
diff --git a/layers/vulkan/generated/gpuav_offline_spirv.cpp b/layers/vulkan/generated/gpuav_offline_spirv.cpp
index a0781fd..8222ca9 100644
--- a/layers/vulkan/generated/gpuav_offline_spirv.cpp
+++ b/layers/vulkan/generated/gpuav_offline_spirv.cpp
@@ -282,9 +282,9 @@
     0x000100fd, 0x00010038};
 [[maybe_unused]] const uint32_t instrumentation_descriptor_class_texel_buffer_comp_function_0_offset = 461;
 
-[[maybe_unused]] const uint32_t instrumentation_descriptor_heap_comp_size = 2297;
-[[maybe_unused]] const uint32_t instrumentation_descriptor_heap_comp[2297] = {
-    0x07230203, 0x00010300, 0x0008000b, 0x000001d4, 0x00000000, 0x00020011, 0x00000001, 0x00020011, 0x00000005, 0x00020011,
+[[maybe_unused]] const uint32_t instrumentation_descriptor_heap_comp_size = 2640;
+[[maybe_unused]] const uint32_t instrumentation_descriptor_heap_comp[2640] = {
+    0x07230203, 0x00010300, 0x0008000b, 0x00000216, 0x00000000, 0x00020011, 0x00000001, 0x00020011, 0x00000005, 0x00020011,
     0x0000000b, 0x00020011, 0x000014e3, 0x0009000a, 0x5f565053, 0x5f52484b, 0x73796870, 0x6c616369, 0x6f74735f, 0x65676172,
     0x6675625f, 0x00726566, 0x0006000b, 0x00000001, 0x4c534c47, 0x6474732e, 0x3035342e, 0x00000000, 0x0003000e, 0x000014e4,
     0x00000001, 0x00030003, 0x00000002, 0x000001c2, 0x00070004, 0x415f4c47, 0x675f4252, 0x735f7570, 0x65646168, 0x6e695f72,
@@ -323,205 +323,240 @@
     0x00746573, 0x00070005, 0x00000035, 0x68737570, 0x6464615f, 0x73736572, 0x66666f5f, 0x00746573, 0x000e0005, 0x0000003b,
     0x74736e69, 0x6165685f, 0x616d5f70, 0x6e697070, 0x6e695f67, 0x65726964, 0x615f7463, 0x65726464, 0x75287373, 0x31753b31,
     0x3b31753b, 0x00000000, 0x00050005, 0x00000038, 0x74736e69, 0x66666f5f, 0x00746573, 0x00050005, 0x00000039, 0x68737570,
-    0x66666f5f, 0x00746573, 0x00060005, 0x0000003a, 0x72646461, 0x5f737365, 0x7366666f, 0x00007465, 0x00040005, 0x0000003e,
-    0x6f727265, 0x00000072, 0x00040005, 0x00000040, 0x61726170, 0x00305f6d, 0x00060005, 0x00000044, 0x6e756f42, 0x61654864,
-    0x666e4970, 0x0000006f, 0x00060006, 0x00000044, 0x00000000, 0x70616568, 0x7a69735f, 0x00000065, 0x00070006, 0x00000044,
-    0x00000001, 0x65736572, 0x64657672, 0x6765625f, 0x00006e69, 0x00070006, 0x00000044, 0x00000002, 0x65736572, 0x64657672,
-    0x646e655f, 0x00000000, 0x00060005, 0x00000046, 0x6e756f62, 0x65685f64, 0x695f7061, 0x006f666e, 0x00060005, 0x0000004a,
-    0x6e756f42, 0x61654864, 0x666e4970, 0x0000006f, 0x00060006, 0x0000004a, 0x00000000, 0x70616568, 0x7a69735f, 0x00000065,
-    0x00070006, 0x0000004a, 0x00000001, 0x65736572, 0x64657672, 0x6765625f, 0x00006e69, 0x00070006, 0x0000004a, 0x00000002,
-    0x65736572, 0x64657672, 0x646e655f, 0x00000000, 0x00080005, 0x0000004c, 0x63736544, 0x74706972, 0x6548726f, 0x6e457061,
-    0x69646f63, 0x0000676e, 0x00080006, 0x0000004c, 0x00000000, 0x6e756f62, 0x65725f64, 0x72756f73, 0x685f6563, 0x00706165,
-    0x00080006, 0x0000004c, 0x00000001, 0x6e756f62, 0x61735f64, 0x656c706d, 0x65685f72, 0x00007061, 0x00060006, 0x0000004c,
-    0x00000002, 0x68737570, 0x7461645f, 0x00000061, 0x00040005, 0x0000004e, 0x61757067, 0x00000076, 0x00060005, 0x00000072,
-    0x6f727245, 0x79615072, 0x64616f6c, 0x00000000, 0x00060006, 0x00000072, 0x00000000, 0x74736e69, 0x66666f5f, 0x00746573,
-    0x00090006, 0x00000072, 0x00000001, 0x64616873, 0x655f7265, 0x726f7272, 0x636e655f, 0x6e69646f, 0x00000067, 0x00060006,
-    0x00000072, 0x00000002, 0x61726170, 0x6574656d, 0x00305f72, 0x00060006, 0x00000072, 0x00000003, 0x61726170, 0x6574656d,
-    0x00315f72, 0x00060006, 0x00000072, 0x00000004, 0x61726170, 0x6574656d, 0x00325f72, 0x00060005, 0x00000074, 0x6f727265,
-    0x61705f72, 0x616f6c79, 0x00000064, 0x00090005, 0x00000075, 0x63657053, 0x736e6f43, 0x746e6174, 0x6b6e694c, 0x64616853,
-    0x64497265, 0x00000000, 0x00040005, 0x00000084, 0x6f727265, 0x00000072, 0x00040005, 0x00000085, 0x61726170, 0x00305f6d,
-    0x00060005, 0x00000090, 0x6e756f62, 0x65685f64, 0x695f7061, 0x006f666e, 0x00040005, 0x000000ba, 0x6f727265, 0x00000072,
-    0x00040005, 0x000000bb, 0x61726170, 0x00305f6d, 0x00060005, 0x000000c9, 0x69646e49, 0x74636572, 0x66667542, 0x00007265,
-    0x00050006, 0x000000c9, 0x00000000, 0x61746164, 0x00000000, 0x00060005, 0x000000d9, 0x6e756f62, 0x65685f64, 0x695f7061,
-    0x006f666e, 0x00040005, 0x00000103, 0x6f727265, 0x00000072, 0x00040005, 0x00000104, 0x61726170, 0x00305f6d, 0x00060005,
-    0x0000011b, 0x6e756f62, 0x65685f64, 0x695f7061, 0x006f666e, 0x000d0047, 0x0000000a, 0x00000029, 0x74736e69, 0x6165685f,
-    0x616d5f70, 0x6e697070, 0x6f635f67, 0x6174736e, 0x6f5f746e, 0x65736666, 0x00000074, 0x00000000, 0x000c0047, 0x00000014,
-    0x00000029, 0x74736e69, 0x6165685f, 0x616d5f70, 0x6e697070, 0x75705f67, 0x695f6873, 0x7865646e, 0x00000000, 0x00000000,
-    0x000d0047, 0x0000001f, 0x00000029, 0x74736e69, 0x6165685f, 0x616d5f70, 0x6e697070, 0x6e695f67, 0x65726964, 0x695f7463,
-    0x7865646e, 0x00000000, 0x00000000, 0x000e0047, 0x00000028, 0x00000029, 0x74736e69, 0x6165685f, 0x616d5f70, 0x6e697070,
-    0x6e695f67, 0x65726964, 0x695f7463, 0x7865646e, 0x7272615f, 0x00007961, 0x00000000, 0x000e0047, 0x0000002e, 0x00000029,
-    0x74736e69, 0x6165685f, 0x616d5f70, 0x6e697070, 0x65725f67, 0x72756f73, 0x685f6563, 0x5f706165, 0x61746164, 0x00000000,
-    0x00000000, 0x000b0047, 0x00000031, 0x00000029, 0x74736e69, 0x6165685f, 0x616d5f70, 0x6e697070, 0x75705f67, 0x645f6873,
-    0x00617461, 0x00000000, 0x000c0047, 0x00000036, 0x00000029, 0x74736e69, 0x6165685f, 0x616d5f70, 0x6e697070, 0x75705f67,
-    0x615f6873, 0x65726464, 0x00007373, 0x00000000, 0x000d0047, 0x0000003b, 0x00000029, 0x74736e69, 0x6165685f, 0x616d5f70,
-    0x6e697070, 0x6e695f67, 0x65726964, 0x615f7463, 0x65726464, 0x00007373, 0x00000000, 0x00050048, 0x0000004a, 0x00000000,
-    0x00000023, 0x00000000, 0x00050048, 0x0000004a, 0x00000001, 0x00000023, 0x00000004, 0x00050048, 0x0000004a, 0x00000002,
-    0x00000023, 0x00000008, 0x00040047, 0x0000004b, 0x00000006, 0x00000004, 0x00030047, 0x0000004c, 0x00000002, 0x00050048,
-    0x0000004c, 0x00000000, 0x00000023, 0x00000000, 0x00050048, 0x0000004c, 0x00000001, 0x00000023, 0x0000000c, 0x00050048,
-    0x0000004c, 0x00000002, 0x00000023, 0x00000018, 0x00040047, 0x0000004e, 0x00000021, 0x00000004, 0x00040047, 0x0000004e,
-    0x00000022, 0x00000007, 0x00040047, 0x00000075, 0x00000001, 0x00000000, 0x00040047, 0x000000c8, 0x00000006, 0x00000004,
-    0x00030047, 0x000000c9, 0x00000002, 0x00050048, 0x000000c9, 0x00000000, 0x00000023, 0x00000000, 0x00040015, 0x00000002,
+    0x66666f5f, 0x00746573, 0x00060005, 0x0000003a, 0x72646461, 0x5f737365, 0x7366666f, 0x00007465, 0x000b0005, 0x00000042,
+    0x74736e69, 0x6165685f, 0x6e755f70, 0x65707974, 0x31752864, 0x3b31753b, 0x753b3175, 0x31623b31, 0x0000003b, 0x00050005,
+    0x0000003d, 0x74736e69, 0x66666f5f, 0x00746573, 0x00050005, 0x0000003e, 0x70616568, 0x66666f5f, 0x00746573, 0x00060005,
+    0x0000003f, 0x72646461, 0x5f737365, 0x69727473, 0x00006564, 0x00050005, 0x00000040, 0x63736564, 0x646e695f, 0x00007865,
+    0x00050005, 0x00000041, 0x735f7369, 0x6c706d61, 0x00007265, 0x00040005, 0x00000045, 0x6f727265, 0x00000072, 0x00040005,
+    0x00000047, 0x61726170, 0x00305f6d, 0x00060005, 0x0000004b, 0x6e756f42, 0x61654864, 0x666e4970, 0x0000006f, 0x00060006,
+    0x0000004b, 0x00000000, 0x70616568, 0x7a69735f, 0x00000065, 0x00070006, 0x0000004b, 0x00000001, 0x65736572, 0x64657672,
+    0x6765625f, 0x00006e69, 0x00070006, 0x0000004b, 0x00000002, 0x65736572, 0x64657672, 0x646e655f, 0x00000000, 0x00060005,
+    0x0000004d, 0x6e756f62, 0x65685f64, 0x695f7061, 0x006f666e, 0x00060005, 0x00000051, 0x6e756f42, 0x61654864, 0x666e4970,
+    0x0000006f, 0x00060006, 0x00000051, 0x00000000, 0x70616568, 0x7a69735f, 0x00000065, 0x00070006, 0x00000051, 0x00000001,
+    0x65736572, 0x64657672, 0x6765625f, 0x00006e69, 0x00070006, 0x00000051, 0x00000002, 0x65736572, 0x64657672, 0x646e655f,
+    0x00000000, 0x00080005, 0x00000053, 0x63736544, 0x74706972, 0x6548726f, 0x6e457061, 0x69646f63, 0x0000676e, 0x00080006,
+    0x00000053, 0x00000000, 0x6e756f62, 0x65725f64, 0x72756f73, 0x685f6563, 0x00706165, 0x00080006, 0x00000053, 0x00000001,
+    0x6e756f62, 0x61735f64, 0x656c706d, 0x65685f72, 0x00007061, 0x00060006, 0x00000053, 0x00000002, 0x68737570, 0x7461645f,
+    0x00000061, 0x00040005, 0x00000055, 0x61757067, 0x00000076, 0x00060005, 0x00000079, 0x6f727245, 0x79615072, 0x64616f6c,
+    0x00000000, 0x00060006, 0x00000079, 0x00000000, 0x74736e69, 0x66666f5f, 0x00746573, 0x00090006, 0x00000079, 0x00000001,
+    0x64616873, 0x655f7265, 0x726f7272, 0x636e655f, 0x6e69646f, 0x00000067, 0x00060006, 0x00000079, 0x00000002, 0x61726170,
+    0x6574656d, 0x00305f72, 0x00060006, 0x00000079, 0x00000003, 0x61726170, 0x6574656d, 0x00315f72, 0x00060006, 0x00000079,
+    0x00000004, 0x61726170, 0x6574656d, 0x00325f72, 0x00060005, 0x0000007b, 0x6f727265, 0x61705f72, 0x616f6c79, 0x00000064,
+    0x00090005, 0x0000007c, 0x63657053, 0x736e6f43, 0x746e6174, 0x6b6e694c, 0x64616853, 0x64497265, 0x00000000, 0x00040005,
+    0x0000008b, 0x6f727265, 0x00000072, 0x00040005, 0x0000008c, 0x61726170, 0x00305f6d, 0x00060005, 0x00000097, 0x6e756f62,
+    0x65685f64, 0x695f7061, 0x006f666e, 0x00040005, 0x000000c1, 0x6f727265, 0x00000072, 0x00040005, 0x000000c2, 0x61726170,
+    0x00305f6d, 0x00060005, 0x000000d0, 0x69646e49, 0x74636572, 0x66667542, 0x00007265, 0x00050006, 0x000000d0, 0x00000000,
+    0x61746164, 0x00000000, 0x00060005, 0x000000e0, 0x6e756f62, 0x65685f64, 0x695f7061, 0x006f666e, 0x00040005, 0x0000010a,
+    0x6f727265, 0x00000072, 0x00040005, 0x0000010b, 0x61726170, 0x00305f6d, 0x00060005, 0x00000122, 0x6e756f62, 0x65685f64,
+    0x695f7061, 0x006f666e, 0x00040005, 0x00000154, 0x6f727265, 0x00000072, 0x00040005, 0x00000155, 0x61726170, 0x00305f6d,
+    0x00060005, 0x00000159, 0x6e756f62, 0x65685f64, 0x695f7061, 0x006f666e, 0x000d0047, 0x0000000a, 0x00000029, 0x74736e69,
+    0x6165685f, 0x616d5f70, 0x6e697070, 0x6f635f67, 0x6174736e, 0x6f5f746e, 0x65736666, 0x00000074, 0x00000000, 0x000c0047,
+    0x00000014, 0x00000029, 0x74736e69, 0x6165685f, 0x616d5f70, 0x6e697070, 0x75705f67, 0x695f6873, 0x7865646e, 0x00000000,
+    0x00000000, 0x000d0047, 0x0000001f, 0x00000029, 0x74736e69, 0x6165685f, 0x616d5f70, 0x6e697070, 0x6e695f67, 0x65726964,
+    0x695f7463, 0x7865646e, 0x00000000, 0x00000000, 0x000e0047, 0x00000028, 0x00000029, 0x74736e69, 0x6165685f, 0x616d5f70,
+    0x6e697070, 0x6e695f67, 0x65726964, 0x695f7463, 0x7865646e, 0x7272615f, 0x00007961, 0x00000000, 0x000e0047, 0x0000002e,
+    0x00000029, 0x74736e69, 0x6165685f, 0x616d5f70, 0x6e697070, 0x65725f67, 0x72756f73, 0x685f6563, 0x5f706165, 0x61746164,
+    0x00000000, 0x00000000, 0x000b0047, 0x00000031, 0x00000029, 0x74736e69, 0x6165685f, 0x616d5f70, 0x6e697070, 0x75705f67,
+    0x645f6873, 0x00617461, 0x00000000, 0x000c0047, 0x00000036, 0x00000029, 0x74736e69, 0x6165685f, 0x616d5f70, 0x6e697070,
+    0x75705f67, 0x615f6873, 0x65726464, 0x00007373, 0x00000000, 0x000d0047, 0x0000003b, 0x00000029, 0x74736e69, 0x6165685f,
+    0x616d5f70, 0x6e697070, 0x6e695f67, 0x65726964, 0x615f7463, 0x65726464, 0x00007373, 0x00000000, 0x00090047, 0x00000042,
+    0x00000029, 0x74736e69, 0x6165685f, 0x6e755f70, 0x65707974, 0x00000064, 0x00000000, 0x00050048, 0x00000051, 0x00000000,
+    0x00000023, 0x00000000, 0x00050048, 0x00000051, 0x00000001, 0x00000023, 0x00000004, 0x00050048, 0x00000051, 0x00000002,
+    0x00000023, 0x00000008, 0x00040047, 0x00000052, 0x00000006, 0x00000004, 0x00030047, 0x00000053, 0x00000002, 0x00050048,
+    0x00000053, 0x00000000, 0x00000023, 0x00000000, 0x00050048, 0x00000053, 0x00000001, 0x00000023, 0x0000000c, 0x00050048,
+    0x00000053, 0x00000002, 0x00000023, 0x00000018, 0x00040047, 0x00000055, 0x00000021, 0x00000004, 0x00040047, 0x00000055,
+    0x00000022, 0x00000007, 0x00040047, 0x0000007c, 0x00000001, 0x00000000, 0x00040047, 0x000000cf, 0x00000006, 0x00000004,
+    0x00030047, 0x000000d0, 0x00000002, 0x00050048, 0x000000d0, 0x00000000, 0x00000023, 0x00000000, 0x00040015, 0x00000002,
     0x00000020, 0x00000000, 0x00020014, 0x00000003, 0x00080021, 0x00000004, 0x00000003, 0x00000002, 0x00000002, 0x00000002,
     0x00000002, 0x00000003, 0x000a0021, 0x0000000c, 0x00000003, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000002,
     0x00000002, 0x00000003, 0x000b0021, 0x00000016, 0x00000003, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000002,
     0x00000002, 0x00000002, 0x00000003, 0x00060021, 0x0000002a, 0x00000003, 0x00000002, 0x00000002, 0x00000002, 0x00030021,
-    0x00000030, 0x00000003, 0x00050021, 0x00000033, 0x00000003, 0x00000002, 0x00000002, 0x00040020, 0x0000003d, 0x00000007,
-    0x00000002, 0x0004002b, 0x00000002, 0x0000003f, 0x00000000, 0x0005001e, 0x00000044, 0x00000002, 0x00000002, 0x00000002,
-    0x00040020, 0x00000045, 0x00000007, 0x00000044, 0x0005001e, 0x0000004a, 0x00000002, 0x00000002, 0x00000002, 0x0003001d,
-    0x0000004b, 0x00000002, 0x0005001e, 0x0000004c, 0x0000004a, 0x0000004a, 0x0000004b, 0x00040020, 0x0000004d, 0x0000000c,
-    0x0000004c, 0x0004003b, 0x0000004d, 0x0000004e, 0x0000000c, 0x00040015, 0x0000004f, 0x00000020, 0x00000001, 0x0004002b,
-    0x0000004f, 0x00000050, 0x00000001, 0x00040020, 0x00000051, 0x0000000c, 0x0000004a, 0x0004002b, 0x0000004f, 0x00000055,
-    0x00000000, 0x0004002b, 0x0000004f, 0x0000005a, 0x00000002, 0x0004002b, 0x00000002, 0x0000006c, 0x00000001, 0x0007001e,
-    0x00000072, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00040020, 0x00000073, 0x00000006, 0x00000072,
-    0x0004003b, 0x00000073, 0x00000074, 0x00000006, 0x00040032, 0x00000002, 0x00000075, 0x0dead001, 0x0004002b, 0x00000002,
-    0x00000076, 0x11000000, 0x00060034, 0x00000002, 0x00000077, 0x000000c5, 0x00000075, 0x00000076, 0x0004002b, 0x0000004f,
-    0x00000079, 0x00000012, 0x0003002a, 0x00000003, 0x0000007e, 0x00030029, 0x00000003, 0x00000080, 0x00040020, 0x00000087,
-    0x0000000c, 0x00000002, 0x00060034, 0x00000002, 0x000000b1, 0x000000c5, 0x00000075, 0x00000076, 0x00040015, 0x000000bc,
-    0x00000040, 0x00000000, 0x00040017, 0x000000c4, 0x00000002, 0x00000002, 0x00030027, 0x000000c7, 0x000014e5, 0x0003001d,
-    0x000000c8, 0x00000002, 0x0003001e, 0x000000c9, 0x000000c8, 0x00040020, 0x000000c7, 0x000014e5, 0x000000c9, 0x00040020,
-    0x000000d0, 0x000014e5, 0x00000002, 0x00060034, 0x00000002, 0x000000fa, 0x000000c5, 0x00000075, 0x00000076, 0x00060034,
-    0x00000002, 0x0000013c, 0x000000c5, 0x00000075, 0x00000076, 0x0004002b, 0x00000002, 0x000001a7, 0x00000002, 0x00050036,
-    0x00000003, 0x0000000a, 0x00000000, 0x00000004, 0x00030037, 0x00000002, 0x00000005, 0x00030037, 0x00000002, 0x00000006,
-    0x00030037, 0x00000002, 0x00000007, 0x00030037, 0x00000002, 0x00000008, 0x00030037, 0x00000003, 0x00000009, 0x000200f8,
-    0x0000000b, 0x0004003b, 0x0000003d, 0x0000003e, 0x00000007, 0x0004003b, 0x0000003d, 0x00000040, 0x00000007, 0x0004003b,
-    0x00000045, 0x00000046, 0x00000007, 0x0004003b, 0x00000045, 0x00000047, 0x00000007, 0x0003003e, 0x0000003e, 0x0000003f,
-    0x0003003e, 0x00000040, 0x0000003f, 0x00050084, 0x00000002, 0x00000042, 0x00000008, 0x00000007, 0x00050080, 0x00000002,
-    0x00000043, 0x00000006, 0x00000042, 0x000300f7, 0x00000049, 0x00000000, 0x000400fa, 0x00000009, 0x00000048, 0x0000005c,
-    0x000200f8, 0x00000048, 0x00050041, 0x00000051, 0x00000052, 0x0000004e, 0x00000050, 0x00050041, 0x00000087, 0x000001aa,
-    0x00000052, 0x0000003f, 0x0004003d, 0x00000002, 0x000001ab, 0x000001aa, 0x00050041, 0x00000087, 0x000001ac, 0x00000052,
-    0x0000006c, 0x0004003d, 0x00000002, 0x000001ad, 0x000001ac, 0x00050041, 0x00000087, 0x000001ae, 0x00000052, 0x000001a7,
-    0x0004003d, 0x00000002, 0x000001af, 0x000001ae, 0x00050041, 0x0000003d, 0x00000056, 0x00000047, 0x00000055, 0x0003003e,
-    0x00000056, 0x000001ab, 0x00050041, 0x0000003d, 0x00000058, 0x00000047, 0x00000050, 0x0003003e, 0x00000058, 0x000001ad,
-    0x00050041, 0x0000003d, 0x0000005b, 0x00000047, 0x0000005a, 0x0003003e, 0x0000005b, 0x000001af, 0x000200f9, 0x00000049,
-    0x000200f8, 0x0000005c, 0x00050041, 0x00000051, 0x0000005d, 0x0000004e, 0x00000055, 0x00050041, 0x00000087, 0x000001a3,
-    0x0000005d, 0x0000003f, 0x0004003d, 0x00000002, 0x000001a4, 0x000001a3, 0x00050041, 0x00000087, 0x000001a5, 0x0000005d,
-    0x0000006c, 0x0004003d, 0x00000002, 0x000001a6, 0x000001a5, 0x00050041, 0x00000087, 0x000001a8, 0x0000005d, 0x000001a7,
-    0x0004003d, 0x00000002, 0x000001a9, 0x000001a8, 0x00050041, 0x0000003d, 0x00000060, 0x00000047, 0x00000055, 0x0003003e,
-    0x00000060, 0x000001a4, 0x00050041, 0x0000003d, 0x00000062, 0x00000047, 0x00000050, 0x0003003e, 0x00000062, 0x000001a6,
-    0x00050041, 0x0000003d, 0x00000064, 0x00000047, 0x0000005a, 0x0003003e, 0x00000064, 0x000001a9, 0x000200f9, 0x00000049,
-    0x000200f8, 0x00000049, 0x0004003d, 0x00000044, 0x00000065, 0x00000047, 0x0003003e, 0x00000046, 0x00000065, 0x00050041,
-    0x0000003d, 0x00000067, 0x00000046, 0x00000055, 0x0004003d, 0x00000002, 0x00000068, 0x00000067, 0x000500ae, 0x00000003,
-    0x00000069, 0x00000043, 0x00000068, 0x000300f7, 0x0000006b, 0x00000000, 0x000400fa, 0x00000069, 0x0000006a, 0x0000006b,
-    0x000200f8, 0x0000006a, 0x0003003e, 0x0000003e, 0x0000006c, 0x0003003e, 0x00000040, 0x00000043, 0x000200f9, 0x0000006b,
-    0x000200f8, 0x0000006b, 0x0004003d, 0x00000002, 0x0000006e, 0x0000003e, 0x000500ab, 0x00000003, 0x0000006f, 0x0000006e,
-    0x0000003f, 0x000300f7, 0x00000071, 0x00000000, 0x000400fa, 0x0000006f, 0x00000070, 0x00000071, 0x000200f8, 0x00000070,
-    0x0004003d, 0x00000002, 0x00000078, 0x0000003e, 0x000500c4, 0x00000002, 0x0000007a, 0x00000078, 0x00000079, 0x000500c5,
-    0x00000002, 0x0000007b, 0x00000077, 0x0000007a, 0x0004003d, 0x00000002, 0x0000007c, 0x00000040, 0x00080050, 0x00000072,
-    0x0000007d, 0x00000005, 0x0000007b, 0x00000008, 0x0000007c, 0x0000003f, 0x0003003e, 0x00000074, 0x0000007d, 0x000200fe,
-    0x0000007e, 0x000200f8, 0x00000071, 0x000200fe, 0x00000080, 0x00010038, 0x00050036, 0x00000003, 0x00000014, 0x00000000,
-    0x0000000c, 0x00030037, 0x00000002, 0x0000000d, 0x00030037, 0x00000002, 0x0000000e, 0x00030037, 0x00000002, 0x0000000f,
-    0x00030037, 0x00000002, 0x00000010, 0x00030037, 0x00000002, 0x00000011, 0x00030037, 0x00000002, 0x00000012, 0x00030037,
-    0x00000003, 0x00000013, 0x000200f8, 0x00000015, 0x0004003b, 0x0000003d, 0x00000084, 0x00000007, 0x0004003b, 0x0000003d,
-    0x00000085, 0x00000007, 0x0004003b, 0x00000045, 0x00000090, 0x00000007, 0x0004003b, 0x00000045, 0x00000091, 0x00000007,
-    0x0003003e, 0x00000084, 0x0000003f, 0x0003003e, 0x00000085, 0x0000003f, 0x00060041, 0x00000087, 0x00000088, 0x0000004e,
-    0x0000005a, 0x0000000f, 0x0004003d, 0x00000002, 0x00000089, 0x00000088, 0x00050084, 0x00000002, 0x0000008c, 0x00000089,
-    0x00000010, 0x00050080, 0x00000002, 0x0000008d, 0x0000000e, 0x0000008c, 0x00050084, 0x00000002, 0x0000008e, 0x00000012,
-    0x00000011, 0x00050080, 0x00000002, 0x0000008f, 0x0000008d, 0x0000008e, 0x000300f7, 0x00000093, 0x00000000, 0x000400fa,
-    0x00000013, 0x00000092, 0x0000009c, 0x000200f8, 0x00000092, 0x00050041, 0x00000051, 0x00000094, 0x0000004e, 0x00000050,
-    0x00050041, 0x00000087, 0x000001b6, 0x00000094, 0x0000003f, 0x0004003d, 0x00000002, 0x000001b7, 0x000001b6, 0x00050041,
-    0x00000087, 0x000001b8, 0x00000094, 0x0000006c, 0x0004003d, 0x00000002, 0x000001b9, 0x000001b8, 0x00050041, 0x00000087,
-    0x000001ba, 0x00000094, 0x000001a7, 0x0004003d, 0x00000002, 0x000001bb, 0x000001ba, 0x00050041, 0x0000003d, 0x00000097,
-    0x00000091, 0x00000055, 0x0003003e, 0x00000097, 0x000001b7, 0x00050041, 0x0000003d, 0x00000099, 0x00000091, 0x00000050,
-    0x0003003e, 0x00000099, 0x000001b9, 0x00050041, 0x0000003d, 0x0000009b, 0x00000091, 0x0000005a, 0x0003003e, 0x0000009b,
-    0x000001bb, 0x000200f9, 0x00000093, 0x000200f8, 0x0000009c, 0x00050041, 0x00000051, 0x0000009d, 0x0000004e, 0x00000055,
-    0x00050041, 0x00000087, 0x000001b0, 0x0000009d, 0x0000003f, 0x0004003d, 0x00000002, 0x000001b1, 0x000001b0, 0x00050041,
-    0x00000087, 0x000001b2, 0x0000009d, 0x0000006c, 0x0004003d, 0x00000002, 0x000001b3, 0x000001b2, 0x00050041, 0x00000087,
-    0x000001b4, 0x0000009d, 0x000001a7, 0x0004003d, 0x00000002, 0x000001b5, 0x000001b4, 0x00050041, 0x0000003d, 0x000000a0,
-    0x00000091, 0x00000055, 0x0003003e, 0x000000a0, 0x000001b1, 0x00050041, 0x0000003d, 0x000000a2, 0x00000091, 0x00000050,
-    0x0003003e, 0x000000a2, 0x000001b3, 0x00050041, 0x0000003d, 0x000000a4, 0x00000091, 0x0000005a, 0x0003003e, 0x000000a4,
-    0x000001b5, 0x000200f9, 0x00000093, 0x000200f8, 0x00000093, 0x0004003d, 0x00000044, 0x000000a5, 0x00000091, 0x0003003e,
-    0x00000090, 0x000000a5, 0x00050041, 0x0000003d, 0x000000a7, 0x00000090, 0x00000055, 0x0004003d, 0x00000002, 0x000000a8,
-    0x000000a7, 0x000500ae, 0x00000003, 0x000000a9, 0x0000008f, 0x000000a8, 0x000300f7, 0x000000ab, 0x00000000, 0x000400fa,
-    0x000000a9, 0x000000aa, 0x000000ab, 0x000200f8, 0x000000aa, 0x0003003e, 0x00000084, 0x0000006c, 0x0003003e, 0x00000085,
-    0x0000008f, 0x000200f9, 0x000000ab, 0x000200f8, 0x000000ab, 0x0004003d, 0x00000002, 0x000000ad, 0x00000084, 0x000500ab,
-    0x00000003, 0x000000ae, 0x000000ad, 0x0000003f, 0x000300f7, 0x000000b0, 0x00000000, 0x000400fa, 0x000000ae, 0x000000af,
-    0x000000b0, 0x000200f8, 0x000000af, 0x0004003d, 0x00000002, 0x000000b2, 0x00000084, 0x000500c4, 0x00000002, 0x000000b3,
-    0x000000b2, 0x00000079, 0x000500c5, 0x00000002, 0x000000b4, 0x000000b1, 0x000000b3, 0x0004003d, 0x00000002, 0x000000b5,
-    0x00000085, 0x00080050, 0x00000072, 0x000000b6, 0x0000000d, 0x000000b4, 0x00000012, 0x000000b5, 0x0000003f, 0x0003003e,
-    0x00000074, 0x000000b6, 0x000200fe, 0x0000007e, 0x000200f8, 0x000000b0, 0x000200fe, 0x00000080, 0x00010038, 0x00050036,
-    0x00000003, 0x0000001f, 0x00000000, 0x00000016, 0x00030037, 0x00000002, 0x00000017, 0x00030037, 0x00000002, 0x00000018,
-    0x00030037, 0x00000002, 0x00000019, 0x00030037, 0x00000002, 0x0000001a, 0x00030037, 0x00000002, 0x0000001b, 0x00030037,
-    0x00000002, 0x0000001c, 0x00030037, 0x00000002, 0x0000001d, 0x00030037, 0x00000003, 0x0000001e, 0x000200f8, 0x00000020,
-    0x0004003b, 0x0000003d, 0x000000ba, 0x00000007, 0x0004003b, 0x0000003d, 0x000000bb, 0x00000007, 0x0004003b, 0x00000045,
-    0x000000d9, 0x00000007, 0x0004003b, 0x00000045, 0x000000da, 0x00000007, 0x0003003e, 0x000000ba, 0x0000003f, 0x0003003e,
-    0x000000bb, 0x0000003f, 0x00060041, 0x00000087, 0x000000bf, 0x0000004e, 0x0000005a, 0x00000019, 0x0004003d, 0x00000002,
-    0x000000c0, 0x000000bf, 0x00050080, 0x00000002, 0x000000c1, 0x00000019, 0x0000006c, 0x00060041, 0x00000087, 0x000000c2,
-    0x0000004e, 0x0000005a, 0x000000c1, 0x0004003d, 0x00000002, 0x000000c3, 0x000000c2, 0x00050050, 0x000000c4, 0x000000c5,
-    0x000000c0, 0x000000c3, 0x0004007c, 0x000000bc, 0x000000c6, 0x000000c5, 0x00040078, 0x000000c7, 0x000000cd, 0x000000c6,
-    0x00060041, 0x000000d0, 0x000000d1, 0x000000cd, 0x00000055, 0x0000001a, 0x0006003d, 0x00000002, 0x000000d2, 0x000000d1,
-    0x00000002, 0x00000004, 0x00050084, 0x00000002, 0x000000d5, 0x000000d2, 0x0000001b, 0x00050080, 0x00000002, 0x000000d6,
-    0x00000018, 0x000000d5, 0x00050084, 0x00000002, 0x000000d7, 0x0000001d, 0x0000001c, 0x00050080, 0x00000002, 0x000000d8,
-    0x000000d6, 0x000000d7, 0x000300f7, 0x000000dc, 0x00000000, 0x000400fa, 0x0000001e, 0x000000db, 0x000000e5, 0x000200f8,
-    0x000000db, 0x00050041, 0x00000051, 0x000000dd, 0x0000004e, 0x00000050, 0x00050041, 0x00000087, 0x000001c2, 0x000000dd,
-    0x0000003f, 0x0004003d, 0x00000002, 0x000001c3, 0x000001c2, 0x00050041, 0x00000087, 0x000001c4, 0x000000dd, 0x0000006c,
-    0x0004003d, 0x00000002, 0x000001c5, 0x000001c4, 0x00050041, 0x00000087, 0x000001c6, 0x000000dd, 0x000001a7, 0x0004003d,
-    0x00000002, 0x000001c7, 0x000001c6, 0x00050041, 0x0000003d, 0x000000e0, 0x000000da, 0x00000055, 0x0003003e, 0x000000e0,
-    0x000001c3, 0x00050041, 0x0000003d, 0x000000e2, 0x000000da, 0x00000050, 0x0003003e, 0x000000e2, 0x000001c5, 0x00050041,
-    0x0000003d, 0x000000e4, 0x000000da, 0x0000005a, 0x0003003e, 0x000000e4, 0x000001c7, 0x000200f9, 0x000000dc, 0x000200f8,
-    0x000000e5, 0x00050041, 0x00000051, 0x000000e6, 0x0000004e, 0x00000055, 0x00050041, 0x00000087, 0x000001bc, 0x000000e6,
-    0x0000003f, 0x0004003d, 0x00000002, 0x000001bd, 0x000001bc, 0x00050041, 0x00000087, 0x000001be, 0x000000e6, 0x0000006c,
-    0x0004003d, 0x00000002, 0x000001bf, 0x000001be, 0x00050041, 0x00000087, 0x000001c0, 0x000000e6, 0x000001a7, 0x0004003d,
-    0x00000002, 0x000001c1, 0x000001c0, 0x00050041, 0x0000003d, 0x000000e9, 0x000000da, 0x00000055, 0x0003003e, 0x000000e9,
-    0x000001bd, 0x00050041, 0x0000003d, 0x000000eb, 0x000000da, 0x00000050, 0x0003003e, 0x000000eb, 0x000001bf, 0x00050041,
-    0x0000003d, 0x000000ed, 0x000000da, 0x0000005a, 0x0003003e, 0x000000ed, 0x000001c1, 0x000200f9, 0x000000dc, 0x000200f8,
-    0x000000dc, 0x0004003d, 0x00000044, 0x000000ee, 0x000000da, 0x0003003e, 0x000000d9, 0x000000ee, 0x00050041, 0x0000003d,
-    0x000000f0, 0x000000d9, 0x00000055, 0x0004003d, 0x00000002, 0x000000f1, 0x000000f0, 0x000500ae, 0x00000003, 0x000000f2,
-    0x000000d8, 0x000000f1, 0x000300f7, 0x000000f4, 0x00000000, 0x000400fa, 0x000000f2, 0x000000f3, 0x000000f4, 0x000200f8,
-    0x000000f3, 0x0003003e, 0x000000ba, 0x0000006c, 0x0003003e, 0x000000bb, 0x000000d8, 0x000200f9, 0x000000f4, 0x000200f8,
-    0x000000f4, 0x0004003d, 0x00000002, 0x000000f6, 0x000000ba, 0x000500ab, 0x00000003, 0x000000f7, 0x000000f6, 0x0000003f,
-    0x000300f7, 0x000000f9, 0x00000000, 0x000400fa, 0x000000f7, 0x000000f8, 0x000000f9, 0x000200f8, 0x000000f8, 0x0004003d,
-    0x00000002, 0x000000fb, 0x000000ba, 0x000500c4, 0x00000002, 0x000000fc, 0x000000fb, 0x00000079, 0x000500c5, 0x00000002,
-    0x000000fd, 0x000000fa, 0x000000fc, 0x0004003d, 0x00000002, 0x000000fe, 0x000000bb, 0x00080050, 0x00000072, 0x000000ff,
-    0x00000017, 0x000000fd, 0x0000001d, 0x000000fe, 0x0000003f, 0x0003003e, 0x00000074, 0x000000ff, 0x000200fe, 0x0000007e,
-    0x000200f8, 0x000000f9, 0x000200fe, 0x00000080, 0x00010038, 0x00050036, 0x00000003, 0x00000028, 0x00000000, 0x0000000c,
-    0x00030037, 0x00000002, 0x00000021, 0x00030037, 0x00000002, 0x00000022, 0x00030037, 0x00000002, 0x00000023, 0x00030037,
-    0x00000002, 0x00000024, 0x00030037, 0x00000002, 0x00000025, 0x00030037, 0x00000002, 0x00000026, 0x00030037, 0x00000003,
-    0x00000027, 0x000200f8, 0x00000029, 0x0004003b, 0x0000003d, 0x00000103, 0x00000007, 0x0004003b, 0x0000003d, 0x00000104,
-    0x00000007, 0x0004003b, 0x00000045, 0x0000011b, 0x00000007, 0x0004003b, 0x00000045, 0x0000011c, 0x00000007, 0x0003003e,
-    0x00000103, 0x0000003f, 0x0003003e, 0x00000104, 0x0000003f, 0x00060041, 0x00000087, 0x00000106, 0x0000004e, 0x0000005a,
-    0x00000023, 0x0004003d, 0x00000002, 0x00000107, 0x00000106, 0x00050080, 0x00000002, 0x00000108, 0x00000023, 0x0000006c,
-    0x00060041, 0x00000087, 0x00000109, 0x0000004e, 0x0000005a, 0x00000108, 0x0004003d, 0x00000002, 0x0000010a, 0x00000109,
-    0x00050050, 0x000000c4, 0x0000010b, 0x00000107, 0x0000010a, 0x0004007c, 0x000000bc, 0x0000010c, 0x0000010b, 0x00040078,
-    0x000000c7, 0x0000010f, 0x0000010c, 0x00050080, 0x00000002, 0x00000111, 0x00000024, 0x00000026, 0x00060041, 0x000000d0,
-    0x00000115, 0x0000010f, 0x00000055, 0x00000111, 0x0006003d, 0x00000002, 0x00000116, 0x00000115, 0x00000002, 0x00000004,
-    0x00050084, 0x00000002, 0x00000119, 0x00000116, 0x00000025, 0x00050080, 0x00000002, 0x0000011a, 0x00000022, 0x00000119,
-    0x000300f7, 0x0000011e, 0x00000000, 0x000400fa, 0x00000027, 0x0000011d, 0x00000127, 0x000200f8, 0x0000011d, 0x00050041,
-    0x00000051, 0x0000011f, 0x0000004e, 0x00000050, 0x00050041, 0x00000087, 0x000001ce, 0x0000011f, 0x0000003f, 0x0004003d,
-    0x00000002, 0x000001cf, 0x000001ce, 0x00050041, 0x00000087, 0x000001d0, 0x0000011f, 0x0000006c, 0x0004003d, 0x00000002,
-    0x000001d1, 0x000001d0, 0x00050041, 0x00000087, 0x000001d2, 0x0000011f, 0x000001a7, 0x0004003d, 0x00000002, 0x000001d3,
-    0x000001d2, 0x00050041, 0x0000003d, 0x00000122, 0x0000011c, 0x00000055, 0x0003003e, 0x00000122, 0x000001cf, 0x00050041,
-    0x0000003d, 0x00000124, 0x0000011c, 0x00000050, 0x0003003e, 0x00000124, 0x000001d1, 0x00050041, 0x0000003d, 0x00000126,
-    0x0000011c, 0x0000005a, 0x0003003e, 0x00000126, 0x000001d3, 0x000200f9, 0x0000011e, 0x000200f8, 0x00000127, 0x00050041,
-    0x00000051, 0x00000128, 0x0000004e, 0x00000055, 0x00050041, 0x00000087, 0x000001c8, 0x00000128, 0x0000003f, 0x0004003d,
-    0x00000002, 0x000001c9, 0x000001c8, 0x00050041, 0x00000087, 0x000001ca, 0x00000128, 0x0000006c, 0x0004003d, 0x00000002,
-    0x000001cb, 0x000001ca, 0x00050041, 0x00000087, 0x000001cc, 0x00000128, 0x000001a7, 0x0004003d, 0x00000002, 0x000001cd,
-    0x000001cc, 0x00050041, 0x0000003d, 0x0000012b, 0x0000011c, 0x00000055, 0x0003003e, 0x0000012b, 0x000001c9, 0x00050041,
-    0x0000003d, 0x0000012d, 0x0000011c, 0x00000050, 0x0003003e, 0x0000012d, 0x000001cb, 0x00050041, 0x0000003d, 0x0000012f,
-    0x0000011c, 0x0000005a, 0x0003003e, 0x0000012f, 0x000001cd, 0x000200f9, 0x0000011e, 0x000200f8, 0x0000011e, 0x0004003d,
-    0x00000044, 0x00000130, 0x0000011c, 0x0003003e, 0x0000011b, 0x00000130, 0x00050041, 0x0000003d, 0x00000132, 0x0000011b,
-    0x00000055, 0x0004003d, 0x00000002, 0x00000133, 0x00000132, 0x000500ae, 0x00000003, 0x00000134, 0x0000011a, 0x00000133,
-    0x000300f7, 0x00000136, 0x00000000, 0x000400fa, 0x00000134, 0x00000135, 0x00000136, 0x000200f8, 0x00000135, 0x0003003e,
-    0x00000103, 0x0000006c, 0x0003003e, 0x00000104, 0x0000011a, 0x000200f9, 0x00000136, 0x000200f8, 0x00000136, 0x0004003d,
-    0x00000002, 0x00000138, 0x00000103, 0x000500ab, 0x00000003, 0x00000139, 0x00000138, 0x0000003f, 0x000300f7, 0x0000013b,
-    0x00000000, 0x000400fa, 0x00000139, 0x0000013a, 0x0000013b, 0x000200f8, 0x0000013a, 0x0004003d, 0x00000002, 0x0000013d,
-    0x00000103, 0x000500c4, 0x00000002, 0x0000013e, 0x0000013d, 0x00000079, 0x000500c5, 0x00000002, 0x0000013f, 0x0000013c,
-    0x0000013e, 0x0004003d, 0x00000002, 0x00000140, 0x00000104, 0x00080050, 0x00000072, 0x00000141, 0x00000021, 0x0000013f,
-    0x00000026, 0x00000140, 0x0000003f, 0x0003003e, 0x00000074, 0x00000141, 0x000200fe, 0x0000007e, 0x000200f8, 0x0000013b,
-    0x000200fe, 0x00000080, 0x00010038, 0x00050036, 0x00000003, 0x0000002e, 0x00000000, 0x0000002a, 0x00030037, 0x00000002,
-    0x0000002b, 0x00030037, 0x00000002, 0x0000002c, 0x00030037, 0x00000002, 0x0000002d, 0x000200f8, 0x0000002f, 0x000200fe,
-    0x00000080, 0x00010038, 0x00050036, 0x00000003, 0x00000031, 0x00000000, 0x00000030, 0x000200f8, 0x00000032, 0x000200fe,
-    0x00000080, 0x00010038, 0x00050036, 0x00000003, 0x00000036, 0x00000000, 0x00000033, 0x00030037, 0x00000002, 0x00000034,
-    0x00030037, 0x00000002, 0x00000035, 0x000200f8, 0x00000037, 0x000200fe, 0x00000080, 0x00010038, 0x00050036, 0x00000003,
-    0x0000003b, 0x00000000, 0x0000002a, 0x00030037, 0x00000002, 0x00000038, 0x00030037, 0x00000002, 0x00000039, 0x00030037,
-    0x00000002, 0x0000003a, 0x000200f8, 0x0000003c, 0x000200fe, 0x00000080, 0x00010038};
-[[maybe_unused]] const uint32_t instrumentation_descriptor_heap_comp_function_0_offset = 969;
-[[maybe_unused]] const uint32_t instrumentation_descriptor_heap_comp_function_1_offset = 1246;
-[[maybe_unused]] const uint32_t instrumentation_descriptor_heap_comp_function_2_offset = 1549;
-[[maybe_unused]] const uint32_t instrumentation_descriptor_heap_comp_function_3_offset = 1895;
-[[maybe_unused]] const uint32_t instrumentation_descriptor_heap_comp_function_4_offset = 2233;
-[[maybe_unused]] const uint32_t instrumentation_descriptor_heap_comp_function_5_offset = 2252;
-[[maybe_unused]] const uint32_t instrumentation_descriptor_heap_comp_function_6_offset = 2262;
-[[maybe_unused]] const uint32_t instrumentation_descriptor_heap_comp_function_7_offset = 2278;
+    0x00000030, 0x00000003, 0x00050021, 0x00000033, 0x00000003, 0x00000002, 0x00000002, 0x00040020, 0x00000044, 0x00000007,
+    0x00000002, 0x0004002b, 0x00000002, 0x00000046, 0x00000000, 0x0005001e, 0x0000004b, 0x00000002, 0x00000002, 0x00000002,
+    0x00040020, 0x0000004c, 0x00000007, 0x0000004b, 0x0005001e, 0x00000051, 0x00000002, 0x00000002, 0x00000002, 0x0003001d,
+    0x00000052, 0x00000002, 0x0005001e, 0x00000053, 0x00000051, 0x00000051, 0x00000052, 0x00040020, 0x00000054, 0x0000000c,
+    0x00000053, 0x0004003b, 0x00000054, 0x00000055, 0x0000000c, 0x00040015, 0x00000056, 0x00000020, 0x00000001, 0x0004002b,
+    0x00000056, 0x00000057, 0x00000001, 0x00040020, 0x00000058, 0x0000000c, 0x00000051, 0x0004002b, 0x00000056, 0x0000005c,
+    0x00000000, 0x0004002b, 0x00000056, 0x00000061, 0x00000002, 0x0004002b, 0x00000002, 0x00000073, 0x00000001, 0x0007001e,
+    0x00000079, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00040020, 0x0000007a, 0x00000006, 0x00000079,
+    0x0004003b, 0x0000007a, 0x0000007b, 0x00000006, 0x00040032, 0x00000002, 0x0000007c, 0x0dead001, 0x0004002b, 0x00000002,
+    0x0000007d, 0x11000000, 0x00060034, 0x00000002, 0x0000007e, 0x000000c5, 0x0000007c, 0x0000007d, 0x0004002b, 0x00000056,
+    0x00000080, 0x00000012, 0x0003002a, 0x00000003, 0x00000085, 0x00030029, 0x00000003, 0x00000087, 0x00040020, 0x0000008e,
+    0x0000000c, 0x00000002, 0x00060034, 0x00000002, 0x000000b8, 0x000000c5, 0x0000007c, 0x0000007d, 0x00040015, 0x000000c3,
+    0x00000040, 0x00000000, 0x00040017, 0x000000cb, 0x00000002, 0x00000002, 0x00030027, 0x000000ce, 0x000014e5, 0x0003001d,
+    0x000000cf, 0x00000002, 0x0003001e, 0x000000d0, 0x000000cf, 0x00040020, 0x000000ce, 0x000014e5, 0x000000d0, 0x00040020,
+    0x000000d7, 0x000014e5, 0x00000002, 0x00060034, 0x00000002, 0x00000101, 0x000000c5, 0x0000007c, 0x0000007d, 0x00060034,
+    0x00000002, 0x00000143, 0x000000c5, 0x0000007c, 0x0000007d, 0x00060034, 0x00000002, 0x0000017a, 0x000000c5, 0x0000007c,
+    0x0000007d, 0x0004002b, 0x00000002, 0x000001dd, 0x00000002, 0x00050036, 0x00000003, 0x0000000a, 0x00000000, 0x00000004,
+    0x00030037, 0x00000002, 0x00000005, 0x00030037, 0x00000002, 0x00000006, 0x00030037, 0x00000002, 0x00000007, 0x00030037,
+    0x00000002, 0x00000008, 0x00030037, 0x00000003, 0x00000009, 0x000200f8, 0x0000000b, 0x0004003b, 0x00000044, 0x00000045,
+    0x00000007, 0x0004003b, 0x00000044, 0x00000047, 0x00000007, 0x0004003b, 0x0000004c, 0x0000004d, 0x00000007, 0x0004003b,
+    0x0000004c, 0x0000004e, 0x00000007, 0x0003003e, 0x00000045, 0x00000046, 0x0003003e, 0x00000047, 0x00000046, 0x00050084,
+    0x00000002, 0x00000049, 0x00000008, 0x00000007, 0x00050080, 0x00000002, 0x0000004a, 0x00000006, 0x00000049, 0x000300f7,
+    0x00000050, 0x00000000, 0x000400fa, 0x00000009, 0x0000004f, 0x00000063, 0x000200f8, 0x0000004f, 0x00050041, 0x00000058,
+    0x00000059, 0x00000055, 0x00000057, 0x00050041, 0x0000008e, 0x000001e0, 0x00000059, 0x00000046, 0x0004003d, 0x00000002,
+    0x000001e1, 0x000001e0, 0x00050041, 0x0000008e, 0x000001e2, 0x00000059, 0x00000073, 0x0004003d, 0x00000002, 0x000001e3,
+    0x000001e2, 0x00050041, 0x0000008e, 0x000001e4, 0x00000059, 0x000001dd, 0x0004003d, 0x00000002, 0x000001e5, 0x000001e4,
+    0x00050041, 0x00000044, 0x0000005d, 0x0000004e, 0x0000005c, 0x0003003e, 0x0000005d, 0x000001e1, 0x00050041, 0x00000044,
+    0x0000005f, 0x0000004e, 0x00000057, 0x0003003e, 0x0000005f, 0x000001e3, 0x00050041, 0x00000044, 0x00000062, 0x0000004e,
+    0x00000061, 0x0003003e, 0x00000062, 0x000001e5, 0x000200f9, 0x00000050, 0x000200f8, 0x00000063, 0x00050041, 0x00000058,
+    0x00000064, 0x00000055, 0x0000005c, 0x00050041, 0x0000008e, 0x000001d9, 0x00000064, 0x00000046, 0x0004003d, 0x00000002,
+    0x000001da, 0x000001d9, 0x00050041, 0x0000008e, 0x000001db, 0x00000064, 0x00000073, 0x0004003d, 0x00000002, 0x000001dc,
+    0x000001db, 0x00050041, 0x0000008e, 0x000001de, 0x00000064, 0x000001dd, 0x0004003d, 0x00000002, 0x000001df, 0x000001de,
+    0x00050041, 0x00000044, 0x00000067, 0x0000004e, 0x0000005c, 0x0003003e, 0x00000067, 0x000001da, 0x00050041, 0x00000044,
+    0x00000069, 0x0000004e, 0x00000057, 0x0003003e, 0x00000069, 0x000001dc, 0x00050041, 0x00000044, 0x0000006b, 0x0000004e,
+    0x00000061, 0x0003003e, 0x0000006b, 0x000001df, 0x000200f9, 0x00000050, 0x000200f8, 0x00000050, 0x0004003d, 0x0000004b,
+    0x0000006c, 0x0000004e, 0x0003003e, 0x0000004d, 0x0000006c, 0x00050041, 0x00000044, 0x0000006e, 0x0000004d, 0x0000005c,
+    0x0004003d, 0x00000002, 0x0000006f, 0x0000006e, 0x000500ae, 0x00000003, 0x00000070, 0x0000004a, 0x0000006f, 0x000300f7,
+    0x00000072, 0x00000000, 0x000400fa, 0x00000070, 0x00000071, 0x00000072, 0x000200f8, 0x00000071, 0x0003003e, 0x00000045,
+    0x00000073, 0x0003003e, 0x00000047, 0x0000004a, 0x000200f9, 0x00000072, 0x000200f8, 0x00000072, 0x0004003d, 0x00000002,
+    0x00000075, 0x00000045, 0x000500ab, 0x00000003, 0x00000076, 0x00000075, 0x00000046, 0x000300f7, 0x00000078, 0x00000000,
+    0x000400fa, 0x00000076, 0x00000077, 0x00000078, 0x000200f8, 0x00000077, 0x0004003d, 0x00000002, 0x0000007f, 0x00000045,
+    0x000500c4, 0x00000002, 0x00000081, 0x0000007f, 0x00000080, 0x000500c5, 0x00000002, 0x00000082, 0x0000007e, 0x00000081,
+    0x0004003d, 0x00000002, 0x00000083, 0x00000047, 0x00080050, 0x00000079, 0x00000084, 0x00000005, 0x00000082, 0x00000008,
+    0x00000083, 0x00000046, 0x0003003e, 0x0000007b, 0x00000084, 0x000200fe, 0x00000085, 0x000200f8, 0x00000078, 0x000200fe,
+    0x00000087, 0x00010038, 0x00050036, 0x00000003, 0x00000014, 0x00000000, 0x0000000c, 0x00030037, 0x00000002, 0x0000000d,
+    0x00030037, 0x00000002, 0x0000000e, 0x00030037, 0x00000002, 0x0000000f, 0x00030037, 0x00000002, 0x00000010, 0x00030037,
+    0x00000002, 0x00000011, 0x00030037, 0x00000002, 0x00000012, 0x00030037, 0x00000003, 0x00000013, 0x000200f8, 0x00000015,
+    0x0004003b, 0x00000044, 0x0000008b, 0x00000007, 0x0004003b, 0x00000044, 0x0000008c, 0x00000007, 0x0004003b, 0x0000004c,
+    0x00000097, 0x00000007, 0x0004003b, 0x0000004c, 0x00000098, 0x00000007, 0x0003003e, 0x0000008b, 0x00000046, 0x0003003e,
+    0x0000008c, 0x00000046, 0x00060041, 0x0000008e, 0x0000008f, 0x00000055, 0x00000061, 0x0000000f, 0x0004003d, 0x00000002,
+    0x00000090, 0x0000008f, 0x00050084, 0x00000002, 0x00000093, 0x00000090, 0x00000010, 0x00050080, 0x00000002, 0x00000094,
+    0x0000000e, 0x00000093, 0x00050084, 0x00000002, 0x00000095, 0x00000012, 0x00000011, 0x00050080, 0x00000002, 0x00000096,
+    0x00000094, 0x00000095, 0x000300f7, 0x0000009a, 0x00000000, 0x000400fa, 0x00000013, 0x00000099, 0x000000a3, 0x000200f8,
+    0x00000099, 0x00050041, 0x00000058, 0x0000009b, 0x00000055, 0x00000057, 0x00050041, 0x0000008e, 0x000001ec, 0x0000009b,
+    0x00000046, 0x0004003d, 0x00000002, 0x000001ed, 0x000001ec, 0x00050041, 0x0000008e, 0x000001ee, 0x0000009b, 0x00000073,
+    0x0004003d, 0x00000002, 0x000001ef, 0x000001ee, 0x00050041, 0x0000008e, 0x000001f0, 0x0000009b, 0x000001dd, 0x0004003d,
+    0x00000002, 0x000001f1, 0x000001f0, 0x00050041, 0x00000044, 0x0000009e, 0x00000098, 0x0000005c, 0x0003003e, 0x0000009e,
+    0x000001ed, 0x00050041, 0x00000044, 0x000000a0, 0x00000098, 0x00000057, 0x0003003e, 0x000000a0, 0x000001ef, 0x00050041,
+    0x00000044, 0x000000a2, 0x00000098, 0x00000061, 0x0003003e, 0x000000a2, 0x000001f1, 0x000200f9, 0x0000009a, 0x000200f8,
+    0x000000a3, 0x00050041, 0x00000058, 0x000000a4, 0x00000055, 0x0000005c, 0x00050041, 0x0000008e, 0x000001e6, 0x000000a4,
+    0x00000046, 0x0004003d, 0x00000002, 0x000001e7, 0x000001e6, 0x00050041, 0x0000008e, 0x000001e8, 0x000000a4, 0x00000073,
+    0x0004003d, 0x00000002, 0x000001e9, 0x000001e8, 0x00050041, 0x0000008e, 0x000001ea, 0x000000a4, 0x000001dd, 0x0004003d,
+    0x00000002, 0x000001eb, 0x000001ea, 0x00050041, 0x00000044, 0x000000a7, 0x00000098, 0x0000005c, 0x0003003e, 0x000000a7,
+    0x000001e7, 0x00050041, 0x00000044, 0x000000a9, 0x00000098, 0x00000057, 0x0003003e, 0x000000a9, 0x000001e9, 0x00050041,
+    0x00000044, 0x000000ab, 0x00000098, 0x00000061, 0x0003003e, 0x000000ab, 0x000001eb, 0x000200f9, 0x0000009a, 0x000200f8,
+    0x0000009a, 0x0004003d, 0x0000004b, 0x000000ac, 0x00000098, 0x0003003e, 0x00000097, 0x000000ac, 0x00050041, 0x00000044,
+    0x000000ae, 0x00000097, 0x0000005c, 0x0004003d, 0x00000002, 0x000000af, 0x000000ae, 0x000500ae, 0x00000003, 0x000000b0,
+    0x00000096, 0x000000af, 0x000300f7, 0x000000b2, 0x00000000, 0x000400fa, 0x000000b0, 0x000000b1, 0x000000b2, 0x000200f8,
+    0x000000b1, 0x0003003e, 0x0000008b, 0x00000073, 0x0003003e, 0x0000008c, 0x00000096, 0x000200f9, 0x000000b2, 0x000200f8,
+    0x000000b2, 0x0004003d, 0x00000002, 0x000000b4, 0x0000008b, 0x000500ab, 0x00000003, 0x000000b5, 0x000000b4, 0x00000046,
+    0x000300f7, 0x000000b7, 0x00000000, 0x000400fa, 0x000000b5, 0x000000b6, 0x000000b7, 0x000200f8, 0x000000b6, 0x0004003d,
+    0x00000002, 0x000000b9, 0x0000008b, 0x000500c4, 0x00000002, 0x000000ba, 0x000000b9, 0x00000080, 0x000500c5, 0x00000002,
+    0x000000bb, 0x000000b8, 0x000000ba, 0x0004003d, 0x00000002, 0x000000bc, 0x0000008c, 0x00080050, 0x00000079, 0x000000bd,
+    0x0000000d, 0x000000bb, 0x00000012, 0x000000bc, 0x00000046, 0x0003003e, 0x0000007b, 0x000000bd, 0x000200fe, 0x00000085,
+    0x000200f8, 0x000000b7, 0x000200fe, 0x00000087, 0x00010038, 0x00050036, 0x00000003, 0x0000001f, 0x00000000, 0x00000016,
+    0x00030037, 0x00000002, 0x00000017, 0x00030037, 0x00000002, 0x00000018, 0x00030037, 0x00000002, 0x00000019, 0x00030037,
+    0x00000002, 0x0000001a, 0x00030037, 0x00000002, 0x0000001b, 0x00030037, 0x00000002, 0x0000001c, 0x00030037, 0x00000002,
+    0x0000001d, 0x00030037, 0x00000003, 0x0000001e, 0x000200f8, 0x00000020, 0x0004003b, 0x00000044, 0x000000c1, 0x00000007,
+    0x0004003b, 0x00000044, 0x000000c2, 0x00000007, 0x0004003b, 0x0000004c, 0x000000e0, 0x00000007, 0x0004003b, 0x0000004c,
+    0x000000e1, 0x00000007, 0x0003003e, 0x000000c1, 0x00000046, 0x0003003e, 0x000000c2, 0x00000046, 0x00060041, 0x0000008e,
+    0x000000c6, 0x00000055, 0x00000061, 0x00000019, 0x0004003d, 0x00000002, 0x000000c7, 0x000000c6, 0x00050080, 0x00000002,
+    0x000000c8, 0x00000019, 0x00000073, 0x00060041, 0x0000008e, 0x000000c9, 0x00000055, 0x00000061, 0x000000c8, 0x0004003d,
+    0x00000002, 0x000000ca, 0x000000c9, 0x00050050, 0x000000cb, 0x000000cc, 0x000000c7, 0x000000ca, 0x0004007c, 0x000000c3,
+    0x000000cd, 0x000000cc, 0x00040078, 0x000000ce, 0x000000d4, 0x000000cd, 0x00060041, 0x000000d7, 0x000000d8, 0x000000d4,
+    0x0000005c, 0x0000001a, 0x0006003d, 0x00000002, 0x000000d9, 0x000000d8, 0x00000002, 0x00000004, 0x00050084, 0x00000002,
+    0x000000dc, 0x000000d9, 0x0000001b, 0x00050080, 0x00000002, 0x000000dd, 0x00000018, 0x000000dc, 0x00050084, 0x00000002,
+    0x000000de, 0x0000001d, 0x0000001c, 0x00050080, 0x00000002, 0x000000df, 0x000000dd, 0x000000de, 0x000300f7, 0x000000e3,
+    0x00000000, 0x000400fa, 0x0000001e, 0x000000e2, 0x000000ec, 0x000200f8, 0x000000e2, 0x00050041, 0x00000058, 0x000000e4,
+    0x00000055, 0x00000057, 0x00050041, 0x0000008e, 0x000001f8, 0x000000e4, 0x00000046, 0x0004003d, 0x00000002, 0x000001f9,
+    0x000001f8, 0x00050041, 0x0000008e, 0x000001fa, 0x000000e4, 0x00000073, 0x0004003d, 0x00000002, 0x000001fb, 0x000001fa,
+    0x00050041, 0x0000008e, 0x000001fc, 0x000000e4, 0x000001dd, 0x0004003d, 0x00000002, 0x000001fd, 0x000001fc, 0x00050041,
+    0x00000044, 0x000000e7, 0x000000e1, 0x0000005c, 0x0003003e, 0x000000e7, 0x000001f9, 0x00050041, 0x00000044, 0x000000e9,
+    0x000000e1, 0x00000057, 0x0003003e, 0x000000e9, 0x000001fb, 0x00050041, 0x00000044, 0x000000eb, 0x000000e1, 0x00000061,
+    0x0003003e, 0x000000eb, 0x000001fd, 0x000200f9, 0x000000e3, 0x000200f8, 0x000000ec, 0x00050041, 0x00000058, 0x000000ed,
+    0x00000055, 0x0000005c, 0x00050041, 0x0000008e, 0x000001f2, 0x000000ed, 0x00000046, 0x0004003d, 0x00000002, 0x000001f3,
+    0x000001f2, 0x00050041, 0x0000008e, 0x000001f4, 0x000000ed, 0x00000073, 0x0004003d, 0x00000002, 0x000001f5, 0x000001f4,
+    0x00050041, 0x0000008e, 0x000001f6, 0x000000ed, 0x000001dd, 0x0004003d, 0x00000002, 0x000001f7, 0x000001f6, 0x00050041,
+    0x00000044, 0x000000f0, 0x000000e1, 0x0000005c, 0x0003003e, 0x000000f0, 0x000001f3, 0x00050041, 0x00000044, 0x000000f2,
+    0x000000e1, 0x00000057, 0x0003003e, 0x000000f2, 0x000001f5, 0x00050041, 0x00000044, 0x000000f4, 0x000000e1, 0x00000061,
+    0x0003003e, 0x000000f4, 0x000001f7, 0x000200f9, 0x000000e3, 0x000200f8, 0x000000e3, 0x0004003d, 0x0000004b, 0x000000f5,
+    0x000000e1, 0x0003003e, 0x000000e0, 0x000000f5, 0x00050041, 0x00000044, 0x000000f7, 0x000000e0, 0x0000005c, 0x0004003d,
+    0x00000002, 0x000000f8, 0x000000f7, 0x000500ae, 0x00000003, 0x000000f9, 0x000000df, 0x000000f8, 0x000300f7, 0x000000fb,
+    0x00000000, 0x000400fa, 0x000000f9, 0x000000fa, 0x000000fb, 0x000200f8, 0x000000fa, 0x0003003e, 0x000000c1, 0x00000073,
+    0x0003003e, 0x000000c2, 0x000000df, 0x000200f9, 0x000000fb, 0x000200f8, 0x000000fb, 0x0004003d, 0x00000002, 0x000000fd,
+    0x000000c1, 0x000500ab, 0x00000003, 0x000000fe, 0x000000fd, 0x00000046, 0x000300f7, 0x00000100, 0x00000000, 0x000400fa,
+    0x000000fe, 0x000000ff, 0x00000100, 0x000200f8, 0x000000ff, 0x0004003d, 0x00000002, 0x00000102, 0x000000c1, 0x000500c4,
+    0x00000002, 0x00000103, 0x00000102, 0x00000080, 0x000500c5, 0x00000002, 0x00000104, 0x00000101, 0x00000103, 0x0004003d,
+    0x00000002, 0x00000105, 0x000000c2, 0x00080050, 0x00000079, 0x00000106, 0x00000017, 0x00000104, 0x0000001d, 0x00000105,
+    0x00000046, 0x0003003e, 0x0000007b, 0x00000106, 0x000200fe, 0x00000085, 0x000200f8, 0x00000100, 0x000200fe, 0x00000087,
+    0x00010038, 0x00050036, 0x00000003, 0x00000028, 0x00000000, 0x0000000c, 0x00030037, 0x00000002, 0x00000021, 0x00030037,
+    0x00000002, 0x00000022, 0x00030037, 0x00000002, 0x00000023, 0x00030037, 0x00000002, 0x00000024, 0x00030037, 0x00000002,
+    0x00000025, 0x00030037, 0x00000002, 0x00000026, 0x00030037, 0x00000003, 0x00000027, 0x000200f8, 0x00000029, 0x0004003b,
+    0x00000044, 0x0000010a, 0x00000007, 0x0004003b, 0x00000044, 0x0000010b, 0x00000007, 0x0004003b, 0x0000004c, 0x00000122,
+    0x00000007, 0x0004003b, 0x0000004c, 0x00000123, 0x00000007, 0x0003003e, 0x0000010a, 0x00000046, 0x0003003e, 0x0000010b,
+    0x00000046, 0x00060041, 0x0000008e, 0x0000010d, 0x00000055, 0x00000061, 0x00000023, 0x0004003d, 0x00000002, 0x0000010e,
+    0x0000010d, 0x00050080, 0x00000002, 0x0000010f, 0x00000023, 0x00000073, 0x00060041, 0x0000008e, 0x00000110, 0x00000055,
+    0x00000061, 0x0000010f, 0x0004003d, 0x00000002, 0x00000111, 0x00000110, 0x00050050, 0x000000cb, 0x00000112, 0x0000010e,
+    0x00000111, 0x0004007c, 0x000000c3, 0x00000113, 0x00000112, 0x00040078, 0x000000ce, 0x00000116, 0x00000113, 0x00050080,
+    0x00000002, 0x00000118, 0x00000024, 0x00000026, 0x00060041, 0x000000d7, 0x0000011c, 0x00000116, 0x0000005c, 0x00000118,
+    0x0006003d, 0x00000002, 0x0000011d, 0x0000011c, 0x00000002, 0x00000004, 0x00050084, 0x00000002, 0x00000120, 0x0000011d,
+    0x00000025, 0x00050080, 0x00000002, 0x00000121, 0x00000022, 0x00000120, 0x000300f7, 0x00000125, 0x00000000, 0x000400fa,
+    0x00000027, 0x00000124, 0x0000012e, 0x000200f8, 0x00000124, 0x00050041, 0x00000058, 0x00000126, 0x00000055, 0x00000057,
+    0x00050041, 0x0000008e, 0x00000204, 0x00000126, 0x00000046, 0x0004003d, 0x00000002, 0x00000205, 0x00000204, 0x00050041,
+    0x0000008e, 0x00000206, 0x00000126, 0x00000073, 0x0004003d, 0x00000002, 0x00000207, 0x00000206, 0x00050041, 0x0000008e,
+    0x00000208, 0x00000126, 0x000001dd, 0x0004003d, 0x00000002, 0x00000209, 0x00000208, 0x00050041, 0x00000044, 0x00000129,
+    0x00000123, 0x0000005c, 0x0003003e, 0x00000129, 0x00000205, 0x00050041, 0x00000044, 0x0000012b, 0x00000123, 0x00000057,
+    0x0003003e, 0x0000012b, 0x00000207, 0x00050041, 0x00000044, 0x0000012d, 0x00000123, 0x00000061, 0x0003003e, 0x0000012d,
+    0x00000209, 0x000200f9, 0x00000125, 0x000200f8, 0x0000012e, 0x00050041, 0x00000058, 0x0000012f, 0x00000055, 0x0000005c,
+    0x00050041, 0x0000008e, 0x000001fe, 0x0000012f, 0x00000046, 0x0004003d, 0x00000002, 0x000001ff, 0x000001fe, 0x00050041,
+    0x0000008e, 0x00000200, 0x0000012f, 0x00000073, 0x0004003d, 0x00000002, 0x00000201, 0x00000200, 0x00050041, 0x0000008e,
+    0x00000202, 0x0000012f, 0x000001dd, 0x0004003d, 0x00000002, 0x00000203, 0x00000202, 0x00050041, 0x00000044, 0x00000132,
+    0x00000123, 0x0000005c, 0x0003003e, 0x00000132, 0x000001ff, 0x00050041, 0x00000044, 0x00000134, 0x00000123, 0x00000057,
+    0x0003003e, 0x00000134, 0x00000201, 0x00050041, 0x00000044, 0x00000136, 0x00000123, 0x00000061, 0x0003003e, 0x00000136,
+    0x00000203, 0x000200f9, 0x00000125, 0x000200f8, 0x00000125, 0x0004003d, 0x0000004b, 0x00000137, 0x00000123, 0x0003003e,
+    0x00000122, 0x00000137, 0x00050041, 0x00000044, 0x00000139, 0x00000122, 0x0000005c, 0x0004003d, 0x00000002, 0x0000013a,
+    0x00000139, 0x000500ae, 0x00000003, 0x0000013b, 0x00000121, 0x0000013a, 0x000300f7, 0x0000013d, 0x00000000, 0x000400fa,
+    0x0000013b, 0x0000013c, 0x0000013d, 0x000200f8, 0x0000013c, 0x0003003e, 0x0000010a, 0x00000073, 0x0003003e, 0x0000010b,
+    0x00000121, 0x000200f9, 0x0000013d, 0x000200f8, 0x0000013d, 0x0004003d, 0x00000002, 0x0000013f, 0x0000010a, 0x000500ab,
+    0x00000003, 0x00000140, 0x0000013f, 0x00000046, 0x000300f7, 0x00000142, 0x00000000, 0x000400fa, 0x00000140, 0x00000141,
+    0x00000142, 0x000200f8, 0x00000141, 0x0004003d, 0x00000002, 0x00000144, 0x0000010a, 0x000500c4, 0x00000002, 0x00000145,
+    0x00000144, 0x00000080, 0x000500c5, 0x00000002, 0x00000146, 0x00000143, 0x00000145, 0x0004003d, 0x00000002, 0x00000147,
+    0x0000010b, 0x00080050, 0x00000079, 0x00000148, 0x00000021, 0x00000146, 0x00000026, 0x00000147, 0x00000046, 0x0003003e,
+    0x0000007b, 0x00000148, 0x000200fe, 0x00000085, 0x000200f8, 0x00000142, 0x000200fe, 0x00000087, 0x00010038, 0x00050036,
+    0x00000003, 0x0000002e, 0x00000000, 0x0000002a, 0x00030037, 0x00000002, 0x0000002b, 0x00030037, 0x00000002, 0x0000002c,
+    0x00030037, 0x00000002, 0x0000002d, 0x000200f8, 0x0000002f, 0x000200fe, 0x00000087, 0x00010038, 0x00050036, 0x00000003,
+    0x00000031, 0x00000000, 0x00000030, 0x000200f8, 0x00000032, 0x000200fe, 0x00000087, 0x00010038, 0x00050036, 0x00000003,
+    0x00000036, 0x00000000, 0x00000033, 0x00030037, 0x00000002, 0x00000034, 0x00030037, 0x00000002, 0x00000035, 0x000200f8,
+    0x00000037, 0x000200fe, 0x00000087, 0x00010038, 0x00050036, 0x00000003, 0x0000003b, 0x00000000, 0x0000002a, 0x00030037,
+    0x00000002, 0x00000038, 0x00030037, 0x00000002, 0x00000039, 0x00030037, 0x00000002, 0x0000003a, 0x000200f8, 0x0000003c,
+    0x000200fe, 0x00000087, 0x00010038, 0x00050036, 0x00000003, 0x00000042, 0x00000000, 0x00000004, 0x00030037, 0x00000002,
+    0x0000003d, 0x00030037, 0x00000002, 0x0000003e, 0x00030037, 0x00000002, 0x0000003f, 0x00030037, 0x00000002, 0x00000040,
+    0x00030037, 0x00000003, 0x00000041, 0x000200f8, 0x00000043, 0x0004003b, 0x00000044, 0x00000154, 0x00000007, 0x0004003b,
+    0x00000044, 0x00000155, 0x00000007, 0x0004003b, 0x0000004c, 0x00000159, 0x00000007, 0x0004003b, 0x0000004c, 0x0000015a,
+    0x00000007, 0x0003003e, 0x00000154, 0x00000046, 0x0003003e, 0x00000155, 0x00000046, 0x00050084, 0x00000002, 0x00000157,
+    0x00000040, 0x0000003f, 0x00050080, 0x00000002, 0x00000158, 0x0000003e, 0x00000157, 0x000300f7, 0x0000015c, 0x00000000,
+    0x000400fa, 0x00000041, 0x0000015b, 0x00000165, 0x000200f8, 0x0000015b, 0x00050041, 0x00000058, 0x0000015d, 0x00000055,
+    0x00000057, 0x00050041, 0x0000008e, 0x00000210, 0x0000015d, 0x00000046, 0x0004003d, 0x00000002, 0x00000211, 0x00000210,
+    0x00050041, 0x0000008e, 0x00000212, 0x0000015d, 0x00000073, 0x0004003d, 0x00000002, 0x00000213, 0x00000212, 0x00050041,
+    0x0000008e, 0x00000214, 0x0000015d, 0x000001dd, 0x0004003d, 0x00000002, 0x00000215, 0x00000214, 0x00050041, 0x00000044,
+    0x00000160, 0x0000015a, 0x0000005c, 0x0003003e, 0x00000160, 0x00000211, 0x00050041, 0x00000044, 0x00000162, 0x0000015a,
+    0x00000057, 0x0003003e, 0x00000162, 0x00000213, 0x00050041, 0x00000044, 0x00000164, 0x0000015a, 0x00000061, 0x0003003e,
+    0x00000164, 0x00000215, 0x000200f9, 0x0000015c, 0x000200f8, 0x00000165, 0x00050041, 0x00000058, 0x00000166, 0x00000055,
+    0x0000005c, 0x00050041, 0x0000008e, 0x0000020a, 0x00000166, 0x00000046, 0x0004003d, 0x00000002, 0x0000020b, 0x0000020a,
+    0x00050041, 0x0000008e, 0x0000020c, 0x00000166, 0x00000073, 0x0004003d, 0x00000002, 0x0000020d, 0x0000020c, 0x00050041,
+    0x0000008e, 0x0000020e, 0x00000166, 0x000001dd, 0x0004003d, 0x00000002, 0x0000020f, 0x0000020e, 0x00050041, 0x00000044,
+    0x00000169, 0x0000015a, 0x0000005c, 0x0003003e, 0x00000169, 0x0000020b, 0x00050041, 0x00000044, 0x0000016b, 0x0000015a,
+    0x00000057, 0x0003003e, 0x0000016b, 0x0000020d, 0x00050041, 0x00000044, 0x0000016d, 0x0000015a, 0x00000061, 0x0003003e,
+    0x0000016d, 0x0000020f, 0x000200f9, 0x0000015c, 0x000200f8, 0x0000015c, 0x0004003d, 0x0000004b, 0x0000016e, 0x0000015a,
+    0x0003003e, 0x00000159, 0x0000016e, 0x00050041, 0x00000044, 0x00000170, 0x00000159, 0x0000005c, 0x0004003d, 0x00000002,
+    0x00000171, 0x00000170, 0x000500ae, 0x00000003, 0x00000172, 0x00000158, 0x00000171, 0x000300f7, 0x00000174, 0x00000000,
+    0x000400fa, 0x00000172, 0x00000173, 0x00000174, 0x000200f8, 0x00000173, 0x0003003e, 0x00000154, 0x00000073, 0x0003003e,
+    0x00000155, 0x00000158, 0x000200f9, 0x00000174, 0x000200f8, 0x00000174, 0x0004003d, 0x00000002, 0x00000176, 0x00000154,
+    0x000500ab, 0x00000003, 0x00000177, 0x00000176, 0x00000046, 0x000300f7, 0x00000179, 0x00000000, 0x000400fa, 0x00000177,
+    0x00000178, 0x00000179, 0x000200f8, 0x00000178, 0x0004003d, 0x00000002, 0x0000017b, 0x00000154, 0x000500c4, 0x00000002,
+    0x0000017c, 0x0000017b, 0x00000080, 0x000500c5, 0x00000002, 0x0000017d, 0x0000017a, 0x0000017c, 0x0004003d, 0x00000002,
+    0x0000017e, 0x00000155, 0x00080050, 0x00000079, 0x0000017f, 0x0000003d, 0x0000017d, 0x00000040, 0x0000017e, 0x00000046,
+    0x0003003e, 0x0000007b, 0x0000017f, 0x000200fe, 0x00000085, 0x000200f8, 0x00000179, 0x000200fe, 0x00000087, 0x00010038};
+[[maybe_unused]] const uint32_t instrumentation_descriptor_heap_comp_function_0_offset = 1035;
+[[maybe_unused]] const uint32_t instrumentation_descriptor_heap_comp_function_1_offset = 1312;
+[[maybe_unused]] const uint32_t instrumentation_descriptor_heap_comp_function_2_offset = 1615;
+[[maybe_unused]] const uint32_t instrumentation_descriptor_heap_comp_function_3_offset = 1961;
+[[maybe_unused]] const uint32_t instrumentation_descriptor_heap_comp_function_4_offset = 2299;
+[[maybe_unused]] const uint32_t instrumentation_descriptor_heap_comp_function_5_offset = 2318;
+[[maybe_unused]] const uint32_t instrumentation_descriptor_heap_comp_function_6_offset = 2328;
+[[maybe_unused]] const uint32_t instrumentation_descriptor_heap_comp_function_7_offset = 2344;
+[[maybe_unused]] const uint32_t instrumentation_descriptor_heap_comp_function_8_offset = 2363;
 
 [[maybe_unused]] const uint32_t instrumentation_descriptor_indexing_oob_comp_size = 1599;
 [[maybe_unused]] const uint32_t instrumentation_descriptor_indexing_oob_comp[1599] = {
diff --git a/layers/vulkan/generated/gpuav_offline_spirv.h b/layers/vulkan/generated/gpuav_offline_spirv.h
index 57d2675..4e2b2f0 100644
--- a/layers/vulkan/generated/gpuav_offline_spirv.h
+++ b/layers/vulkan/generated/gpuav_offline_spirv.h
@@ -55,6 +55,7 @@
 extern const uint32_t instrumentation_descriptor_heap_comp_function_5_offset;
 extern const uint32_t instrumentation_descriptor_heap_comp_function_6_offset;
 extern const uint32_t instrumentation_descriptor_heap_comp_function_7_offset;
+extern const uint32_t instrumentation_descriptor_heap_comp_function_8_offset;
 
 extern const uint32_t instrumentation_descriptor_indexing_oob_comp_size;
 extern const uint32_t instrumentation_descriptor_indexing_oob_comp[];
diff --git a/tests/unit/gpu_av_descriptor_heap.cpp b/tests/unit/gpu_av_descriptor_heap.cpp
index 335fe36..45e8249 100644
--- a/tests/unit/gpu_av_descriptor_heap.cpp
+++ b/tests/unit/gpu_av_descriptor_heap.cpp
@@ -17,6 +17,7 @@
 #include "../framework/buffer_helper.h"
 #include "../utils/math_utils.h"
 #include "gpuav/shaders/gpuav_shaders_constants.h"
+#include "shader_helper.h"
 #include "shader_templates.h"
 
 void GpuAVDescriptorHeap::CreateResourceHeap(VkDeviceSize app_size, bool reserved_range_in_front) {
@@ -1523,4 +1524,415 @@
     m_errorMonitor->SetDesiredError("VUID-vkCmdDispatch-None-11309", 2);
     m_default_queue->SubmitAndWait(m_command_buffer);
     m_errorMonitor->VerifyFound();
+}
+
+TEST_F(NegativeGpuAVDescriptorHeap, ResourceOOBUntypedPointersBuffer) {
+    AddRequiredExtensions(VK_KHR_SHADER_UNTYPED_POINTERS_EXTENSION_NAME);
+    AddRequiredFeature(vkt::Feature::shaderUntypedPointers);
+    RETURN_IF_SKIP(InitGpuAVDescriptorHeap());
+    if ((heap_props.minResourceHeapReservedRange / heap_props.bufferDescriptorSize) >= 10000) {
+        GTEST_SKIP() << "reserved range is too large, access will not be OOB";
+    }
+    const VkDeviceSize resource_stride = heap_props.bufferDescriptorSize;
+    CreateResourceHeap(resource_stride);
+
+    const char* cs_source = R"glsl(
+        #version 450
+        #extension GL_EXT_descriptor_heap : require
+        layout (descriptor_heap) buffer SSBO_0 {
+            uint data;
+        } heapBuffer[];
+
+        void main() {
+            // Something large enough to get pass the reserved range
+            heapBuffer[10000].data = 0;
+        }
+    )glsl";
+
+    VkShaderObj cs_module = VkShaderObj(*m_device, cs_source, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_2);
+
+    VkPipelineCreateFlags2CreateInfoKHR pipeline_create_flags_2_create_info = vku::InitStructHelper();
+    pipeline_create_flags_2_create_info.flags = VK_PIPELINE_CREATE_2_DESCRIPTOR_HEAP_BIT_EXT;
+
+    CreateComputePipelineHelper pipe(*this, &pipeline_create_flags_2_create_info);
+    pipe.cp_ci_.stage = cs_module.GetStageCreateInfo();
+    pipe.cp_ci_.layout = VK_NULL_HANDLE;
+    pipe.CreateComputePipeline(false);
+
+    m_command_buffer.Begin();
+    BindResourceHeap();
+    vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe);
+    vk::CmdDispatch(m_command_buffer, 1, 1, 1);
+    m_command_buffer.End();
+
+    m_errorMonitor->SetDesiredError("VUID-vkCmdDispatch-None-11309");
+    m_default_queue->SubmitAndWait(m_command_buffer);
+    m_errorMonitor->VerifyFound();
+}
+
+TEST_F(NegativeGpuAVDescriptorHeap, ResourceOOBUntypedPointersBufferOldGlsl) {
+    AddRequiredExtensions(VK_KHR_SHADER_UNTYPED_POINTERS_EXTENSION_NAME);
+    AddRequiredFeature(vkt::Feature::shaderUntypedPointers);
+    RETURN_IF_SKIP(InitGpuAVDescriptorHeap());
+    if ((heap_props.minResourceHeapReservedRange / heap_props.bufferDescriptorSize) >= 10000) {
+        GTEST_SKIP() << "reserved range is too large, access will not be OOB";
+    }
+    const VkDeviceSize resource_stride = heap_props.bufferDescriptorSize;
+    CreateResourceHeap(resource_stride);
+
+    // Same shader above in ResourceOOBUntypedPointers, but using the old (still valid) GLSL output
+    const char* cs_source = R"asm(
+               OpCapability Shader
+               OpCapability UntypedPointersKHR
+               OpCapability DescriptorHeapEXT
+               OpExtension "SPV_EXT_descriptor_heap"
+               OpExtension "SPV_KHR_untyped_pointers"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main" %resource_heap
+               OpExecutionMode %main LocalSize 1 1 1
+               OpMemberName %SSBO_0 0 "data"
+               OpDecorate %resource_heap BuiltIn ResourceHeapEXT
+               OpDecorate %SSBO_0 Block
+               OpMemberDecorate %SSBO_0 0 Offset 0
+               OpDecorateId %_runtimearr_16 ArrayStrideIdEXT %17
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant
+%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant
+        %int = OpTypeInt 32 1
+  %int_10000 = OpConstant %int 10000
+       %uint = OpTypeInt 32 0
+     %SSBO_0 = OpTypeStruct %uint
+      %int_0 = OpConstant %int 0
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer = OpTypeUntypedPointerKHR StorageBuffer
+         %16 = OpTypeBufferEXT StorageBuffer
+         %17 = OpConstantSizeOfEXT %int %16
+%_runtimearr_16 = OpTypeRuntimeArray %16
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %15 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_16 %resource_heap %int_10000
+         %19 = OpBufferPointerEXT %_ptr_StorageBuffer %15
+         %20 = OpUntypedAccessChainKHR %_ptr_StorageBuffer %SSBO_0 %19 %int_0
+               OpStore %20 %uint_0
+               OpReturn
+               OpFunctionEnd
+    )asm";
+
+    VkShaderObj cs_module = VkShaderObj(*m_device, cs_source, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_2, SPV_SOURCE_ASM);
+
+    VkPipelineCreateFlags2CreateInfoKHR pipeline_create_flags_2_create_info = vku::InitStructHelper();
+    pipeline_create_flags_2_create_info.flags = VK_PIPELINE_CREATE_2_DESCRIPTOR_HEAP_BIT_EXT;
+
+    CreateComputePipelineHelper pipe(*this, &pipeline_create_flags_2_create_info);
+    pipe.cp_ci_.stage = cs_module.GetStageCreateInfo();
+    pipe.cp_ci_.layout = VK_NULL_HANDLE;
+    pipe.CreateComputePipeline(false);
+
+    m_command_buffer.Begin();
+    BindResourceHeap();
+    vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe);
+    vk::CmdDispatch(m_command_buffer, 1, 1, 1);
+    m_command_buffer.End();
+
+    m_errorMonitor->SetDesiredError("VUID-vkCmdDispatch-None-11309");
+    m_default_queue->SubmitAndWait(m_command_buffer);
+    m_errorMonitor->VerifyFound();
+}
+
+TEST_F(NegativeGpuAVDescriptorHeap, ResourceOOBUntypedPointersBufferAtomics) {
+    AddRequiredExtensions(VK_KHR_SHADER_UNTYPED_POINTERS_EXTENSION_NAME);
+    AddRequiredFeature(vkt::Feature::shaderUntypedPointers);
+    RETURN_IF_SKIP(InitGpuAVDescriptorHeap());
+    if ((heap_props.minResourceHeapReservedRange / heap_props.bufferDescriptorSize) >= 10000) {
+        GTEST_SKIP() << "reserved range is too large, access will not be OOB";
+    }
+    const VkDeviceSize resource_stride = heap_props.bufferDescriptorSize;
+    CreateResourceHeap(resource_stride);
+
+    const char* cs_source = R"glsl(
+        #version 450
+        #extension GL_EXT_descriptor_heap : enable
+        #extension GL_KHR_memory_scope_semantics : enable
+
+        layout (descriptor_heap) buffer heap {
+            uint a;
+        } heapBuffer[];
+
+        void main() {
+            atomicStore(heapBuffer[10000].a, 0u, gl_ScopeDevice, 0, 0);
+        }
+    )glsl";
+
+    VkShaderObj cs_module = VkShaderObj(*m_device, cs_source, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_2);
+
+    VkPipelineCreateFlags2CreateInfoKHR pipeline_create_flags_2_create_info = vku::InitStructHelper();
+    pipeline_create_flags_2_create_info.flags = VK_PIPELINE_CREATE_2_DESCRIPTOR_HEAP_BIT_EXT;
+
+    CreateComputePipelineHelper pipe(*this, &pipeline_create_flags_2_create_info);
+    pipe.cp_ci_.stage = cs_module.GetStageCreateInfo();
+    pipe.cp_ci_.layout = VK_NULL_HANDLE;
+    pipe.CreateComputePipeline(false);
+
+    m_command_buffer.Begin();
+    BindResourceHeap();
+    vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe);
+    vk::CmdDispatch(m_command_buffer, 1, 1, 1);
+    m_command_buffer.End();
+
+    m_errorMonitor->SetDesiredError("VUID-vkCmdDispatch-None-11309");
+    m_default_queue->SubmitAndWait(m_command_buffer);
+    m_errorMonitor->VerifyFound();
+}
+
+// TODO - See not in DescriptorHeapPass, need a way to return a "safe" null descriptor
+TEST_F(NegativeGpuAVDescriptorHeap, DISABLED_ResourceOOBUntypedPointersStorageImage) {
+    AddRequiredExtensions(VK_KHR_SHADER_UNTYPED_POINTERS_EXTENSION_NAME);
+    AddRequiredFeature(vkt::Feature::shaderUntypedPointers);
+    RETURN_IF_SKIP(InitGpuAVDescriptorHeap());
+    if ((heap_props.minResourceHeapReservedRange / heap_props.imageDescriptorSize) >= 10000) {
+        GTEST_SKIP() << "reserved range is too large, access will not be OOB";
+    }
+    const VkDeviceSize resource_stride = heap_props.bufferDescriptorSize;
+    CreateResourceHeap(resource_stride);
+
+    vkt::Buffer ssbo_buffer(*m_device, 256, VK_BUFFER_USAGE_2_STORAGE_BUFFER_BIT_KHR, vkt::device_address);
+    VkHostAddressRangeEXT descriptor_host = {resource_heap_data_, static_cast<size_t>(resource_stride)};
+    VkDeviceAddressRangeEXT device_range = ssbo_buffer.AddressRange();
+    VkResourceDescriptorInfoEXT descriptor_info = vku::InitStructHelper();
+    descriptor_info.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
+    descriptor_info.data.pAddressRange = &device_range;
+    vk::WriteResourceDescriptorsEXT(*m_device, 1u, &descriptor_info, &descriptor_host);
+
+    // Note - this looks the same in the "old" glslang code as well
+    const char* cs_source = R"glsl(
+        #version 450
+        #extension GL_EXT_descriptor_heap : require
+        layout(descriptor_heap, rgba8i) uniform iimage2D heapImages[];
+        layout(set = 0, binding = 0) buffer SSBO { ivec4 result; };
+        void main() {
+            ivec4 data = imageLoad(heapImages[10000], ivec2(0));
+            result = data;
+        }
+    )glsl";
+    VkShaderObj cs_module = VkShaderObj(*m_device, cs_source, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_2);
+
+    VkDescriptorSetAndBindingMappingEXT mapping = MakeSetAndBindingMapping(0, 0);
+    mapping.source = VK_DESCRIPTOR_MAPPING_SOURCE_HEAP_WITH_CONSTANT_OFFSET_EXT;
+    mapping.sourceData.constantOffset.heapOffset = 0;
+    VkShaderDescriptorSetAndBindingMappingInfoEXT mapping_info = vku::InitStructHelper();
+    mapping_info.mappingCount = 1u;
+    mapping_info.pMappings = &mapping;
+
+    VkPipelineCreateFlags2CreateInfoKHR pipeline_create_flags_2_create_info = vku::InitStructHelper();
+    pipeline_create_flags_2_create_info.flags = VK_PIPELINE_CREATE_2_DESCRIPTOR_HEAP_BIT_EXT;
+
+    CreateComputePipelineHelper pipe(*this, &pipeline_create_flags_2_create_info);
+    pipe.cp_ci_.stage = cs_module.GetStageCreateInfo(&mapping_info);
+    pipe.cp_ci_.layout = VK_NULL_HANDLE;
+    pipe.CreateComputePipeline(false);
+
+    m_command_buffer.Begin();
+    BindResourceHeap();
+    vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe);
+    vk::CmdDispatch(m_command_buffer, 1, 1, 1);
+    m_command_buffer.End();
+
+    m_errorMonitor->SetDesiredError("VUID-vkCmdDispatch-None-11309");
+    m_default_queue->SubmitAndWait(m_command_buffer);
+    m_errorMonitor->VerifyFound();
+}
+
+TEST_F(NegativeGpuAVDescriptorHeap, ResourceOOBUntypedPointersSampledImage) {
+    AddRequiredExtensions(VK_KHR_SHADER_UNTYPED_POINTERS_EXTENSION_NAME);
+    AddRequiredFeature(vkt::Feature::shaderUntypedPointers);
+    RETURN_IF_SKIP(InitGpuAVDescriptorHeap());
+    if ((heap_props.minResourceHeapReservedRange / heap_props.imageDescriptorSize) >= 10000) {
+        GTEST_SKIP() << "reserved range is too large, access will not be OOB";
+    }
+    const VkDeviceSize resource_stride = heap_props.bufferDescriptorSize;
+    const VkDeviceSize sampler_stride = heap_props.samplerDescriptorSize;
+    CreateResourceHeap(resource_stride);
+    CreateSamplerHeap(sampler_stride);
+
+    vkt::Buffer ssbo_buffer(*m_device, 256, VK_BUFFER_USAGE_2_STORAGE_BUFFER_BIT_KHR, vkt::device_address);
+    VkHostAddressRangeEXT descriptor_host = {resource_heap_data_, static_cast<size_t>(resource_stride)};
+    VkDeviceAddressRangeEXT device_range = ssbo_buffer.AddressRange();
+    VkResourceDescriptorInfoEXT descriptor_info = vku::InitStructHelper();
+    descriptor_info.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
+    descriptor_info.data.pAddressRange = &device_range;
+    vk::WriteResourceDescriptorsEXT(*m_device, 1u, &descriptor_info, &descriptor_host);
+
+    // Note - this looks the same in the "old" glslang code as well
+    const char* cs_source = R"glsl(
+        #version 450
+        #extension GL_EXT_descriptor_heap : require
+        layout(descriptor_heap) uniform texture2D heapTextures[];
+        layout(descriptor_heap) uniform sampler heapSamplers[];
+        layout(set = 0, binding = 0) buffer SSBO { vec4 result; };
+        void main() {
+            vec4 data = texture(sampler2D(heapTextures[10000], heapSamplers[0]), vec2(0.5f));
+            result = data;
+        }
+    )glsl";
+    VkShaderObj cs_module = VkShaderObj(*m_device, cs_source, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_2);
+
+    VkDescriptorSetAndBindingMappingEXT mapping = MakeSetAndBindingMapping(0, 0);
+    mapping.source = VK_DESCRIPTOR_MAPPING_SOURCE_HEAP_WITH_CONSTANT_OFFSET_EXT;
+    mapping.sourceData.constantOffset.heapOffset = 0;
+    VkShaderDescriptorSetAndBindingMappingInfoEXT mapping_info = vku::InitStructHelper();
+    mapping_info.mappingCount = 1u;
+    mapping_info.pMappings = &mapping;
+
+    VkPipelineCreateFlags2CreateInfoKHR pipeline_create_flags_2_create_info = vku::InitStructHelper();
+    pipeline_create_flags_2_create_info.flags = VK_PIPELINE_CREATE_2_DESCRIPTOR_HEAP_BIT_EXT;
+
+    CreateComputePipelineHelper pipe(*this, &pipeline_create_flags_2_create_info);
+    pipe.cp_ci_.stage = cs_module.GetStageCreateInfo(&mapping_info);
+    pipe.cp_ci_.layout = VK_NULL_HANDLE;
+    pipe.CreateComputePipeline(false);
+
+    m_command_buffer.Begin();
+    BindResourceHeap();
+    BindSamplerHeap();
+    vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe);
+    vk::CmdDispatch(m_command_buffer, 1, 1, 1);
+    m_command_buffer.End();
+
+    m_errorMonitor->SetDesiredError("VUID-vkCmdDispatch-None-11309");
+    m_default_queue->SubmitAndWait(m_command_buffer);
+    m_errorMonitor->VerifyFound();
+}
+
+TEST_F(NegativeGpuAVDescriptorHeap, SamplerOOBUntypedPointers) {
+    AddRequiredExtensions(VK_KHR_SHADER_UNTYPED_POINTERS_EXTENSION_NAME);
+    AddRequiredFeature(vkt::Feature::shaderUntypedPointers);
+    RETURN_IF_SKIP(InitGpuAVDescriptorHeap());
+    if ((heap_props.minSamplerHeapReservedRange / heap_props.samplerDescriptorSize) >= 10000) {
+        GTEST_SKIP() << "reserved range is too large, access will not be OOB";
+    }
+    const VkDeviceSize resource_stride = heap_props.imageDescriptorSize;
+    CreateResourceHeap(resource_stride);
+    CreateSamplerHeap(heap_props.samplerDescriptorSize);
+
+    // Note - this looks the same in the "old" glslang code as well
+    const char* cs_source = R"glsl(
+        #version 450
+        #extension GL_EXT_descriptor_heap : require
+        layout(descriptor_heap) uniform texture2D heapTextures[];
+        layout(descriptor_heap) uniform sampler heapSamplers[];
+        void main() {
+            vec4 data = texture(sampler2D(heapTextures[0], heapSamplers[10000]), vec2(0.5f));
+        }
+    )glsl";
+    VkShaderObj cs_module = VkShaderObj(*m_device, cs_source, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_2);
+
+    VkPipelineCreateFlags2CreateInfoKHR pipeline_create_flags_2_create_info = vku::InitStructHelper();
+    pipeline_create_flags_2_create_info.flags = VK_PIPELINE_CREATE_2_DESCRIPTOR_HEAP_BIT_EXT;
+
+    CreateComputePipelineHelper pipe(*this, &pipeline_create_flags_2_create_info);
+    pipe.cp_ci_.stage = cs_module.GetStageCreateInfo();
+    pipe.cp_ci_.layout = VK_NULL_HANDLE;
+    pipe.CreateComputePipeline(false);
+
+    m_command_buffer.Begin();
+    BindResourceHeap();
+    BindSamplerHeap();
+    vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe);
+    vk::CmdDispatch(m_command_buffer, 1, 1, 1);
+    m_command_buffer.End();
+
+    m_errorMonitor->SetDesiredError("VUID-vkCmdDispatch-None-11309");
+    m_default_queue->SubmitAndWait(m_command_buffer);
+    m_errorMonitor->VerifyFound();
+}
+
+TEST_F(NegativeGpuAVDescriptorHeap, ResourceOOBUntypedPointersOffsetId) {
+    AddRequiredExtensions(VK_KHR_SHADER_UNTYPED_POINTERS_EXTENSION_NAME);
+    AddRequiredFeature(vkt::Feature::shaderUntypedPointers);
+    RETURN_IF_SKIP(InitGpuAVDescriptorHeap());
+
+    const VkDeviceSize resource_stride = heap_props.bufferDescriptorSize;
+    CreateResourceHeap(resource_stride * 2);
+
+    vkt::Buffer buffer_0(*m_device, 64, VK_BUFFER_USAGE_2_STORAGE_BUFFER_BIT_KHR, vkt::device_address);
+
+    VkHostAddressRangeEXT descriptor_host = {resource_heap_data_ + resource_stride, static_cast<size_t>(resource_stride)};
+    VkDeviceAddressRangeEXT device_range = buffer_0.AddressRange();
+    VkResourceDescriptorInfoEXT descriptor_info = vku::InitStructHelper();
+    descriptor_info.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
+    descriptor_info.data.pAddressRange = &device_range;
+    vk::WriteResourceDescriptorsEXT(*m_device, 1u, &descriptor_info, &descriptor_host);
+
+    // layout(storage_buffer) SSBO {
+    //     uint a;
+    // };
+    // layout(offset = buffer_size * 10000) heap {
+    //     SSBO runtime_buffer[];
+    // } heap_layout;
+    //
+    // heap_layout.runtime_buffer[0].a = 42;
+    char const* cs_source = R"(
+               OpCapability Shader
+               OpCapability UntypedPointersKHR
+               OpCapability DescriptorHeapEXT
+               OpExtension "SPV_EXT_descriptor_heap"
+               OpExtension "SPV_KHR_untyped_pointers"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main" %resource_heap
+               OpExecutionMode %main LocalSize 1 1 1
+               OpDecorate %resource_heap BuiltIn ResourceHeapEXT
+               OpDecorate %SSBO Block
+               OpMemberDecorate %SSBO 0 Offset 0
+               OpDecorate %heap_layout Block
+               OpMemberDecorateIdEXT %heap_layout 0 OffsetIdEXT %crazy_offset
+               OpDecorateId %runtime_buffer ArrayStrideIdEXT %buf_size
+       %void = OpTypeVoid
+    %void_fn = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+       %uint = OpTypeInt 32 0
+      %int_0 = OpConstant %int 0
+    %uint_42 = OpConstant %uint 42
+    %int_10k = OpConstant %int 10000
+%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant
+%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant
+        %SSBO = OpTypeStruct %uint
+%_ptr_StorageBuffer = OpTypeUntypedPointerKHR StorageBuffer
+
+%type_buffer = OpTypeBufferEXT StorageBuffer
+   %buf_size = OpConstantSizeOfEXT %int %type_buffer
+%crazy_offset = OpSpecConstantOp %int IMul %buf_size %int_10k
+%runtime_buffer = OpTypeRuntimeArray %type_buffer
+
+ %heap_layout = OpTypeStruct %runtime_buffer
+
+       %main = OpFunction %void None %void_fn
+          %5 = OpLabel
+
+%heap_index_0 = OpUntypedAccessChainKHR %_ptr_UniformConstant %heap_layout %resource_heap %int_0 %int_0
+  %buf_ptr_0 = OpBufferPointerEXT %_ptr_StorageBuffer %heap_index_0
+   %member_0 = OpUntypedAccessChainKHR %_ptr_StorageBuffer %SSBO %buf_ptr_0 %int_0
+               OpStore %member_0 %uint_42
+
+               OpReturn
+               OpFunctionEnd
+    )";
+    VkShaderObj cs_module = VkShaderObj(*m_device, cs_source, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_2, SPV_SOURCE_ASM);
+
+    VkPipelineCreateFlags2CreateInfoKHR pipeline_create_flags_2_create_info = vku::InitStructHelper();
+    pipeline_create_flags_2_create_info.flags = VK_PIPELINE_CREATE_2_DESCRIPTOR_HEAP_BIT_EXT;
+    CreateComputePipelineHelper pipe(*this, &pipeline_create_flags_2_create_info);
+    pipe.cp_ci_.layout = VK_NULL_HANDLE;
+    pipe.cp_ci_.stage = cs_module.GetStageCreateInfo();
+    pipe.CreateComputePipeline(false);
+
+    m_command_buffer.Begin();
+    BindResourceHeap();
+    vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe);
+    vk::CmdDispatch(m_command_buffer, 1, 1, 1);
+    m_command_buffer.End();
+
+    m_errorMonitor->SetDesiredError("VUID-vkCmdDispatch-None-11309");
+    m_default_queue->SubmitAndWait(m_command_buffer);
+    m_errorMonitor->VerifyFound();
 }
\ No newline at end of file
diff --git a/tests/unit/gpu_av_descriptor_indexing.cpp b/tests/unit/gpu_av_descriptor_indexing.cpp
index f212b20..24182f1 100644
--- a/tests/unit/gpu_av_descriptor_indexing.cpp
+++ b/tests/unit/gpu_av_descriptor_indexing.cpp
@@ -12,6 +12,7 @@
  */
 
 #include <vulkan/vulkan_core.h>
+#include <cstdint>
 #include "../framework/layer_validation_tests.h"
 #include "../framework/pipeline_helper.h"
 #include "../framework/descriptor_helper.h"
@@ -3898,17 +3899,22 @@
         #extension GL_KHR_memory_scope_semantics : enable
 
         layout(set = 0, binding = 0, R32ui) uniform uimage2D atomic_image_array[];
-
+        // Also a UniformConstant
+        layout(push_constant) uniform PC {
+            uint x;
+        };
         void main() {
-            uint y = imageAtomicLoad(atomic_image_array[1], ivec2(0), gl_ScopeDevice, gl_StorageSemanticsImage, gl_SemanticsAcquire);
-            imageAtomicStore(atomic_image_array[1], ivec2(0), y, gl_ScopeDevice, gl_StorageSemanticsImage, gl_SemanticsRelease);
-            imageAtomicExchange(atomic_image_array[1], ivec2(0), y, gl_ScopeDevice, gl_StorageSemanticsImage, gl_SemanticsRelease);
+            ivec2 cord = ivec2(x, x);
+            uint y = imageAtomicLoad(atomic_image_array[1], cord, gl_ScopeDevice, gl_StorageSemanticsImage, gl_SemanticsAcquire);
+            imageAtomicStore(atomic_image_array[1], cord, y, gl_ScopeDevice, gl_StorageSemanticsImage, gl_SemanticsRelease);
+            imageAtomicExchange(atomic_image_array[1], cord, y, gl_ScopeDevice, gl_StorageSemanticsImage, gl_SemanticsRelease);
         }
     )glsl";
 
+    VkPushConstantRange pc_range{VK_SHADER_STAGE_COMPUTE_BIT, 0, 4};
     OneOffDescriptorIndexingSet descriptor_set(m_device, {{0, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 2, VK_SHADER_STAGE_ALL, nullptr,
                                                            VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT}});
-    vkt::PipelineLayout pipeline_layout(*m_device, {&descriptor_set.layout_});
+    vkt::PipelineLayout pipeline_layout(*m_device, {&descriptor_set.layout_}, {pc_range});
 
     descriptor_set.WriteDescriptorImageInfo(0, image_view, sampler, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_IMAGE_LAYOUT_GENERAL, 0);
     descriptor_set.UpdateDescriptorSets();
@@ -3919,6 +3925,8 @@
     pipe.CreateComputePipeline();
 
     m_command_buffer.Begin();
+    uint32_t push_data = 0;
+    vk::CmdPushConstants(m_command_buffer, pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, 4, &push_data);
     vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe);
     vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline_layout, 0, 1, &descriptor_set.set_, 0,
                               nullptr);
@@ -4001,6 +4009,47 @@
     m_errorMonitor->VerifyFound();
 }
 
+TEST_F(NegativeGpuAVDescriptorIndexing, AtomicBufferOOB) {
+    RETURN_IF_SKIP(InitGpuVUDescriptorIndexing());
+    InitRenderTarget();
+
+    const char* cs_source = R"glsl(
+        #version 450
+        #extension GL_EXT_nonuniform_qualifier : enable
+        #extension GL_KHR_memory_scope_semantics : enable
+        layout(set = 0, binding = 0) buffer ssbo { uint x; } atomic_buffers[];
+        void main() {
+            uint index = atomic_buffers[0].x;
+            atomicStore(atomic_buffers[index].x, 1u, gl_ScopeDevice, 0, 0);
+        }
+    )glsl";
+
+    OneOffDescriptorIndexingSet descriptor_set(m_device, {{0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 2, VK_SHADER_STAGE_ALL, nullptr,
+                                                           VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT}});
+    vkt::PipelineLayout pipeline_layout(*m_device, {&descriptor_set.layout_});
+
+    vkt::Buffer storage_buffer(*m_device, 32, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, kHostVisibleMemProps);
+    *((uint32_t*)storage_buffer.Memory().Map()) = 99;  // OOB!
+    descriptor_set.WriteDescriptorBufferInfo(0, storage_buffer, 0, VK_WHOLE_SIZE, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 0);
+    descriptor_set.UpdateDescriptorSets();
+
+    CreateComputePipelineHelper pipe(*this);
+    pipe.cp_ci_.layout = pipeline_layout;
+    pipe.cs_ = VkShaderObj(*m_device, cs_source, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_1);
+    pipe.CreateComputePipeline();
+
+    m_command_buffer.Begin();
+    vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe);
+    vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline_layout, 0, 1, &descriptor_set.set_, 0,
+                              nullptr);
+    vk::CmdDispatch(m_command_buffer, 1, 1, 1);
+    m_command_buffer.End();
+
+    m_errorMonitor->SetDesiredError("VUID-vkCmdDispatch-None-10068");
+    m_default_queue->SubmitAndWait(m_command_buffer);
+    m_errorMonitor->VerifyFound();
+}
+
 TEST_F(NegativeGpuAVDescriptorIndexing, StorageImagePartiallyBound) {
     RETURN_IF_SKIP(InitGpuVUDescriptorIndexing());
     InitRenderTarget();