layers: Validate copying mutable descriptor set types
diff --git a/layers/descriptor_sets.cpp b/layers/descriptor_sets.cpp
index 29cbf59..13c66f6 100644
--- a/layers/descriptor_sets.cpp
+++ b/layers/descriptor_sets.cpp
@@ -234,6 +234,14 @@
     return false;
 }
 
+const std::vector<VkDescriptorType> &cvdescriptorset::DescriptorSetLayoutDef::GetMutableTypes(uint32_t binding) const {
+    if (binding >= mutable_types_.size()) {
+        static const std::vector<VkDescriptorType> empty = {};
+        return empty;
+    }
+    return mutable_types_[binding];
+}
+
 // If our layout is compatible with rh_ds_layout, return true.
 bool cvdescriptorset::DescriptorSetLayout::IsCompatible(DescriptorSetLayout const *rh_ds_layout) const {
     bool compatible = (this == rh_ds_layout) || (GetLayoutDef() == rh_ds_layout->GetLayoutDef());
@@ -784,7 +792,7 @@
                 break;
             case VK_DESCRIPTOR_TYPE_MUTABLE_VALVE:
                 for (uint32_t di = 0; di < layout_->GetDescriptorCountFromIndex(i); ++di) {
-                    descriptors_.emplace_back(new ((free_descriptor++)->InlineUniform()) MutableDescriptor());
+                    descriptors_.emplace_back(new ((free_descriptor++)->Mutable()) MutableDescriptor());
                     descriptors_.back()->AddParent(this);
                 }
                 break;
@@ -1641,6 +1649,7 @@
         uint32_t update_count = std::min(descriptors_remaining, current_binding.GetDescriptorCount() - offset);
         for (uint32_t di = 0; di < update_count; ++di, ++update_index) {
             descriptors_[global_idx + di]->WriteUpdate(this, state_data_, update, update_index);
+            descriptors_[global_idx + di]->SetDescriptorType(update->descriptorType);
         }
         // Roll over to next binding in case of consecutive update
         descriptors_remaining -= update_count;
@@ -1891,6 +1900,58 @@
         }
     }
 
+    if (dst_type == VK_DESCRIPTOR_TYPE_MUTABLE_VALVE) {
+        if (src_type != VK_DESCRIPTOR_TYPE_MUTABLE_VALVE) {
+            if (!dst_layout->IsTypeMutable(src_type, update->dstBinding)) {
+                *error_code = "VUID-VkCopyDescriptorSet-dstSet-04612";
+                std::stringstream error_str;
+                error_str << "Attempting copy update with dstBinding descriptor type VK_DESCRIPTOR_TYPE_MUTABLE_VALVE, but the new "
+                             "active descriptor type "
+                          << string_VkDescriptorType(src_type)
+                          << " is not in the corresponding pMutableDescriptorTypeLists list.";
+                *error_msg = error_str.str();
+                return false;
+            }
+        }
+    } else if (src_type == VK_DESCRIPTOR_TYPE_MUTABLE_VALVE) {
+        const auto *descriptor = src_set->GetDescriptorFromGlobalIndex(update->srcBinding);
+        if (descriptor->active_descriptor_type != dst_type) {
+            *error_code = "VUID-VkCopyDescriptorSet-srcSet-04613";
+            std::stringstream error_str;
+            error_str << "Attempting copy update with srcBinding descriptor type VK_DESCRIPTOR_TYPE_MUTABLE_VALVE, but the "
+                         "active descriptor type ("
+                      << string_VkDescriptorType(descriptor->active_descriptor_type)
+                      << ") does not match the dstBinding descriptor type " << string_VkDescriptorType(dst_type) << ".";
+            *error_msg = error_str.str();
+            return false;
+        }
+    }
+
+    if (dst_type == VK_DESCRIPTOR_TYPE_MUTABLE_VALVE) {
+        if (src_type == VK_DESCRIPTOR_TYPE_MUTABLE_VALVE) {
+            const auto &mutable_src_types = src_layout->GetMutableTypes(update->srcBinding);
+            const auto &mutable_dst_types = dst_layout->GetMutableTypes(update->dstBinding);
+            bool complete_match = mutable_src_types.size() == mutable_dst_types.size();
+            if (complete_match) {
+                for (const auto mutable_src_type : mutable_src_types) {
+                    if (std::find(mutable_dst_types.begin(), mutable_dst_types.end(), mutable_src_type) ==
+                        mutable_dst_types.end()) {
+                        complete_match = false;
+                        break;
+                    }
+                }
+            }
+            if (!complete_match) {
+                *error_code = "VUID-VkCopyDescriptorSet-dstSet-04614";
+                std::stringstream error_str;
+                error_str << "Attempting copy update with dstBinding and new active descriptor type being "
+                             "VK_DESCRIPTOR_TYPE_MUTABLE_VALVE, but their corresponding pMutableDescriptorTypeLists do not match.";
+                *error_msg = error_str.str();
+                return false;
+            }
+        }
+    }
+
     // Update parameters all look good and descriptor updated so verify update contents
     if (!VerifyCopyUpdateContents(update, src_set, src_type, src_start_idx, dst_set, dst_type, dst_start_idx, func_name, error_code,
                                   error_msg)) {
@@ -1916,6 +1977,7 @@
         } else {
             dst->updated = false;
         }
+        dst->active_descriptor_type = src->active_descriptor_type;
     }
 
     if (!(layout_->GetDescriptorBindingFlagsFromBinding(update->dstBinding) &
diff --git a/layers/descriptor_sets.h b/layers/descriptor_sets.h
index 5aa1b03..a8e9272 100644
--- a/layers/descriptor_sets.h
+++ b/layers/descriptor_sets.h
@@ -176,6 +176,7 @@
     VkSampler const *GetImmutableSamplerPtrFromBinding(const uint32_t) const;
     VkSampler const *GetImmutableSamplerPtrFromIndex(const uint32_t) const;
     bool IsTypeMutable(const VkDescriptorType type, uint32_t binding) const;
+    const std::vector<VkDescriptorType> &GetMutableTypes(uint32_t binding) const;
     // For a particular binding, get the global index range
     //  This call should be guarded by a call to "HasBinding(binding)" to verify that the given binding exists
     const IndexRange &GetGlobalIndexRangeFromBinding(const uint32_t) const;
@@ -276,6 +277,7 @@
         return layout_id_->GetImmutableSamplerPtrFromIndex(index);
     }
     bool IsTypeMutable(const VkDescriptorType type, uint32_t binding) const { return layout_id_->IsTypeMutable(type, binding); }
+    const std::vector<VkDescriptorType> &GetMutableTypes(uint32_t binding) const { return layout_id_->GetMutableTypes(binding); }
     // For a particular binding, get the global index range
     //  This call should be guarded by a call to "HasBinding(binding)" to verify that the given binding exists
     const IndexRange &GetGlobalIndexRangeFromBinding(const uint32_t binding) const {
@@ -386,7 +388,7 @@
 
 class Descriptor {
   public:
-    Descriptor(DescriptorClass class_) : updated(false), descriptor_class(class_) {}
+    Descriptor(DescriptorClass class_) : updated(false), descriptor_class(class_), active_descriptor_type(VK_DESCRIPTOR_TYPE_MUTABLE_VALVE) {}
     virtual ~Descriptor(){};
     virtual void WriteUpdate(DescriptorSet *set_state, const ValidationStateTracker *dev_data, const VkWriteDescriptorSet *, const uint32_t) = 0;
     virtual void CopyUpdate(DescriptorSet *set_state, const ValidationStateTracker *dev_data, const Descriptor *) = 0;
@@ -396,9 +398,11 @@
     virtual bool IsImmutableSampler() const { return false; };
     virtual bool AddParent(BASE_NODE *base_node) { return false; }
     virtual void RemoveParent(BASE_NODE *base_node) {}
+    void SetDescriptorType(VkDescriptorType type) { active_descriptor_type = type; }
 
     bool updated;  // Has descriptor been updated?
     DescriptorClass descriptor_class;
+    VkDescriptorType active_descriptor_type;
 };
 
 // Return true if this layout is compatible with passed in layout from a pipelineLayout,
@@ -652,6 +656,7 @@
     AccelerationStructureDescriptor *AccelerationStructure() {
         return &(reinterpret_cast<AnyDescriptor *>(this)->accelerator_structure);
     }
+    MutableDescriptor *Mutable() { return &(reinterpret_cast<AnyDescriptor *>(this)->mutable_descriptor); }
 };
 
 // Structs to contain common elements that need to be shared between Validate* and Perform* calls below