Move code around in prep for CacheKey -> Stream refactor

Moves code around to simplify viewing the diff for Change
If7594c4ff7117454c1ab3d0afaeee5653120add8

Bug: dawn:1480, dawn:1481
Change-Id: Iecfe4356b1a933a46741cec185008ca1d927c0a6
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/96903
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Loko Kung <lokokung@google.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Austin Eng <enga@chromium.org>
diff --git a/src/dawn/native/BUILD.gn b/src/dawn/native/BUILD.gn
index 07617ba..d8ddca2 100644
--- a/src/dawn/native/BUILD.gn
+++ b/src/dawn/native/BUILD.gn
@@ -334,6 +334,7 @@
     "VertexFormat.cpp",
     "VertexFormat.h",
     "dawn_platform.h",
+    "stream/Stream.h",
     "utils/WGPUHelpers.cpp",
     "utils/WGPUHelpers.h",
     "webgpu_absl_format.cpp",
@@ -604,7 +605,6 @@
       "vulkan/BufferVk.cpp",
       "vulkan/BufferVk.h",
       "vulkan/CacheKeyVk.cpp",
-      "vulkan/CacheKeyVk.h",
       "vulkan/CommandBufferVk.cpp",
       "vulkan/CommandBufferVk.h",
       "vulkan/CommandRecordingContext.h",
diff --git a/src/dawn/native/CMakeLists.txt b/src/dawn/native/CMakeLists.txt
index 7d88df1..9306178 100644
--- a/src/dawn/native/CMakeLists.txt
+++ b/src/dawn/native/CMakeLists.txt
@@ -193,6 +193,7 @@
     "dawn_platform.h"
     "webgpu_absl_format.cpp"
     "webgpu_absl_format.h"
+    "stream/Stream.h"
     "utils/WGPUHelpers.cpp"
     "utils/WGPUHelpers.h"
 )
@@ -486,7 +487,6 @@
         "vulkan/BufferVk.cpp"
         "vulkan/BufferVk.h"
         "vulkan/CacheKeyVk.cpp"
-        "vulkan/CacheKeyVk.h"
         "vulkan/CommandBufferVk.cpp"
         "vulkan/CommandBufferVk.h"
         "vulkan/CommandRecordingContext.h"
diff --git a/src/dawn/native/CacheKey.h b/src/dawn/native/CacheKey.h
index b45c4c2..786ae86 100644
--- a/src/dawn/native/CacheKey.h
+++ b/src/dawn/native/CacheKey.h
@@ -111,151 +111,10 @@
     return CacheKey::UnsafeUnkeyedValue<T>(std::forward<T>(value));
 }
 
-// Specialized overload for CacheKey::UnsafeIgnoredValue which does nothing.
-template <typename T>
-class CacheKeySerializer<CacheKey::UnsafeUnkeyedValue<T>> {
-  public:
-    constexpr static void Serialize(CacheKey* key, const CacheKey::UnsafeUnkeyedValue<T>&) {}
-};
-
-// Specialized overload for fundamental types.
-template <typename T>
-class CacheKeySerializer<T, std::enable_if_t<std::is_fundamental_v<T>>> {
-  public:
-    static void Serialize(CacheKey* key, const T t) {
-        const char* it = reinterpret_cast<const char*>(&t);
-        key->insert(key->end(), it, (it + sizeof(T)));
-    }
-};
-
-// Specialized overload for bitsets that are smaller than 64.
-template <size_t N>
-class CacheKeySerializer<std::bitset<N>, std::enable_if_t<(N <= 64)>> {
-  public:
-    static void Serialize(CacheKey* key, const std::bitset<N>& t) { key->Record(t.to_ullong()); }
-};
-
-// Specialized overload for bitsets since using the built-in to_ullong have a size limit.
-template <size_t N>
-class CacheKeySerializer<std::bitset<N>, std::enable_if_t<(N > 64)>> {
-  public:
-    static void Serialize(CacheKey* key, const std::bitset<N>& t) {
-        // Serializes the bitset into series of uint8_t, along with recording the size.
-        static_assert(N > 0);
-        key->Record(static_cast<size_t>(N));
-        uint8_t value = 0;
-        for (size_t i = 0; i < N; i++) {
-            value <<= 1;
-            // Explicitly convert to numeric since MSVC doesn't like mixing of bools.
-            value |= t[i] ? 1 : 0;
-            if (i % 8 == 7) {
-                // Whenever we fill an 8 bit value, record it and zero it out.
-                key->Record(value);
-                value = 0;
-            }
-        }
-        // Serialize the last value if we are not a multiple of 8.
-        if (N % 8 != 0) {
-            key->Record(value);
-        }
-    }
-};
-
-// Specialized overload for enums.
-template <typename T>
-class CacheKeySerializer<T, std::enable_if_t<std::is_enum_v<T>>> {
-  public:
-    static void Serialize(CacheKey* key, const T t) {
-        CacheKeySerializer<std::underlying_type_t<T>>::Serialize(
-            key, static_cast<std::underlying_type_t<T>>(t));
-    }
-};
-
-// Specialized overload for TypedInteger.
-template <typename Tag, typename Integer>
-class CacheKeySerializer<::detail::TypedIntegerImpl<Tag, Integer>> {
-  public:
-    static void Serialize(CacheKey* key, const ::detail::TypedIntegerImpl<Tag, Integer> t) {
-        CacheKeySerializer<Integer>::Serialize(key, static_cast<Integer>(t));
-    }
-};
-
-// Specialized overload for pointers. Since we are serializing for a cache key, we always
-// serialize via value, not by pointer. To handle nullptr scenarios, we always serialize whether
-// the pointer was nullptr followed by the contents if applicable.
-template <typename T>
-class CacheKeySerializer<T, std::enable_if_t<std::is_pointer_v<T>>> {
-  public:
-    static void Serialize(CacheKey* key, const T t) {
-        key->Record(t == nullptr);
-        if (t != nullptr) {
-            CacheKeySerializer<std::remove_cv_t<std::remove_pointer_t<T>>>::Serialize(key, *t);
-        }
-    }
-};
-
-// Specialized overload for fixed arrays of primitives.
-template <typename T, size_t N>
-class CacheKeySerializer<T[N], std::enable_if_t<std::is_fundamental_v<T>>> {
-  public:
-    static void Serialize(CacheKey* key, const T (&t)[N]) {
-        static_assert(N > 0);
-        key->Record(static_cast<size_t>(N));
-        const char* it = reinterpret_cast<const char*>(t);
-        key->insert(key->end(), it, it + sizeof(t));
-    }
-};
-
-// Specialized overload for fixed arrays of non-primitives.
-template <typename T, size_t N>
-class CacheKeySerializer<T[N], std::enable_if_t<!std::is_fundamental_v<T>>> {
-  public:
-    static void Serialize(CacheKey* key, const T (&t)[N]) {
-        static_assert(N > 0);
-        key->Record(static_cast<size_t>(N));
-        for (size_t i = 0; i < N; i++) {
-            key->Record(t[i]);
-        }
-    }
-};
-
-// Specialized overload for CachedObjects.
-template <typename T>
-class CacheKeySerializer<T, std::enable_if_t<std::is_base_of_v<CachedObject, T>>> {
-  public:
-    static void Serialize(CacheKey* key, const T& t) { key->Record(t.GetCacheKey()); }
-};
-
-// Specialized overload for std::vector.
-template <typename T>
-class CacheKeySerializer<std::vector<T>> {
-  public:
-    static void Serialize(CacheKey* key, const std::vector<T>& t) { key->RecordIterable(t); }
-};
-
-// Specialized overload for std::pair<A, B>
-template <typename A, typename B>
-class CacheKeySerializer<std::pair<A, B>> {
-  public:
-    static void Serialize(CacheKey* key, const std::pair<A, B>& p) {
-        key->Record(p.first, p.second);
-    }
-};
-
-// Specialized overload for std::unordered_map<K, V>
-template <typename K, typename V>
-class CacheKeySerializer<std::unordered_map<K, V>> {
-  public:
-    static void Serialize(CacheKey* key, const std::unordered_map<K, V>& m) {
-        std::vector<std::pair<K, V>> ordered(m.begin(), m.end());
-        std::sort(ordered.begin(), ordered.end(),
-                  [](const std::pair<K, V>& a, const std::pair<K, V>& b) {
-                      return std::less<K>{}(a.first, b.first);
-                  });
-        key->RecordIterable(ordered);
-    }
-};
-
 }  // namespace dawn::native
 
+// CacheKeySerializer implementation temporarily moved to stream/Stream.h to
+// simplify the diff in the refactor to stream::Stream.
+#include "dawn/native/stream/Stream.h"
+
 #endif  // SRC_DAWN_NATIVE_CACHEKEY_H_
diff --git a/src/dawn/native/stream/Stream.h b/src/dawn/native/stream/Stream.h
new file mode 100644
index 0000000..1797102
--- /dev/null
+++ b/src/dawn/native/stream/Stream.h
@@ -0,0 +1,181 @@
+// Copyright 2022 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_DAWN_NATIVE_STREAM_STREAM_H_
+#define SRC_DAWN_NATIVE_STREAM_STREAM_H_
+
+#include <algorithm>
+#include <bitset>
+#include <functional>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include "dawn/common/Platform.h"
+#include "dawn/common/TypedInteger.h"
+#include "dawn/native/CacheKey.h"
+#include "dawn/native/Error.h"
+
+namespace dawn::native {
+
+class CacheKey;
+
+// Specialized overload for CacheKey::UnsafeIgnoredValue which does nothing.
+template <typename T>
+class CacheKeySerializer<CacheKey::UnsafeUnkeyedValue<T>> {
+  public:
+    constexpr static void Serialize(CacheKey* key, const CacheKey::UnsafeUnkeyedValue<T>&) {}
+};
+
+// Specialized overload for fundamental types.
+template <typename T>
+class CacheKeySerializer<T, std::enable_if_t<std::is_fundamental_v<T>>> {
+  public:
+    static void Serialize(CacheKey* key, const T t) {
+        const char* it = reinterpret_cast<const char*>(&t);
+        key->insert(key->end(), it, (it + sizeof(T)));
+    }
+};
+
+// Specialized overload for bitsets that are smaller than 64.
+template <size_t N>
+class CacheKeySerializer<std::bitset<N>, std::enable_if_t<(N <= 64)>> {
+  public:
+    static void Serialize(CacheKey* key, const std::bitset<N>& t) { key->Record(t.to_ullong()); }
+};
+
+// Specialized overload for bitsets since using the built-in to_ullong have a size limit.
+template <size_t N>
+class CacheKeySerializer<std::bitset<N>, std::enable_if_t<(N > 64)>> {
+  public:
+    static void Serialize(CacheKey* key, const std::bitset<N>& t) {
+        // Serializes the bitset into series of uint8_t, along with recording the size.
+        static_assert(N > 0);
+        key->Record(static_cast<size_t>(N));
+        uint8_t value = 0;
+        for (size_t i = 0; i < N; i++) {
+            value <<= 1;
+            // Explicitly convert to numeric since MSVC doesn't like mixing of bools.
+            value |= t[i] ? 1 : 0;
+            if (i % 8 == 7) {
+                // Whenever we fill an 8 bit value, record it and zero it out.
+                key->Record(value);
+                value = 0;
+            }
+        }
+        // Serialize the last value if we are not a multiple of 8.
+        if (N % 8 != 0) {
+            key->Record(value);
+        }
+    }
+};
+
+// Specialized overload for enums.
+template <typename T>
+class CacheKeySerializer<T, std::enable_if_t<std::is_enum_v<T>>> {
+  public:
+    static void Serialize(CacheKey* key, const T t) {
+        CacheKeySerializer<std::underlying_type_t<T>>::Serialize(
+            key, static_cast<std::underlying_type_t<T>>(t));
+    }
+};
+
+// Specialized overload for TypedInteger.
+template <typename Tag, typename Integer>
+class CacheKeySerializer<::detail::TypedIntegerImpl<Tag, Integer>> {
+  public:
+    static void Serialize(CacheKey* key, const ::detail::TypedIntegerImpl<Tag, Integer> t) {
+        CacheKeySerializer<Integer>::Serialize(key, static_cast<Integer>(t));
+    }
+};
+
+// Specialized overload for pointers. Since we are serializing for a cache key, we always
+// serialize via value, not by pointer. To handle nullptr scenarios, we always serialize whether
+// the pointer was nullptr followed by the contents if applicable.
+template <typename T>
+class CacheKeySerializer<T, std::enable_if_t<std::is_pointer_v<T>>> {
+  public:
+    static void Serialize(CacheKey* key, const T t) {
+        key->Record(t == nullptr);
+        if (t != nullptr) {
+            CacheKeySerializer<std::remove_cv_t<std::remove_pointer_t<T>>>::Serialize(key, *t);
+        }
+    }
+};
+
+// Specialized overload for fixed arrays of primitives.
+template <typename T, size_t N>
+class CacheKeySerializer<T[N], std::enable_if_t<std::is_fundamental_v<T>>> {
+  public:
+    static void Serialize(CacheKey* key, const T (&t)[N]) {
+        static_assert(N > 0);
+        key->Record(static_cast<size_t>(N));
+        const char* it = reinterpret_cast<const char*>(t);
+        key->insert(key->end(), it, it + sizeof(t));
+    }
+};
+
+// Specialized overload for fixed arrays of non-primitives.
+template <typename T, size_t N>
+class CacheKeySerializer<T[N], std::enable_if_t<!std::is_fundamental_v<T>>> {
+  public:
+    static void Serialize(CacheKey* key, const T (&t)[N]) {
+        static_assert(N > 0);
+        key->Record(static_cast<size_t>(N));
+        for (size_t i = 0; i < N; i++) {
+            key->Record(t[i]);
+        }
+    }
+};
+
+// Specialized overload for CachedObjects.
+template <typename T>
+class CacheKeySerializer<T, std::enable_if_t<std::is_base_of_v<CachedObject, T>>> {
+  public:
+    static void Serialize(CacheKey* key, const T& t) { key->Record(t.GetCacheKey()); }
+};
+
+// Specialized overload for std::vector.
+template <typename T>
+class CacheKeySerializer<std::vector<T>> {
+  public:
+    static void Serialize(CacheKey* key, const std::vector<T>& t) { key->RecordIterable(t); }
+};
+
+// Specialized overload for std::pair<A, B>
+template <typename A, typename B>
+class CacheKeySerializer<std::pair<A, B>> {
+  public:
+    static void Serialize(CacheKey* key, const std::pair<A, B>& p) {
+        key->Record(p.first, p.second);
+    }
+};
+
+// Specialized overload for std::unordered_map<K, V>
+template <typename K, typename V>
+class CacheKeySerializer<std::unordered_map<K, V>> {
+  public:
+    static void Serialize(CacheKey* key, const std::unordered_map<K, V>& m) {
+        std::vector<std::pair<K, V>> ordered(m.begin(), m.end());
+        std::sort(ordered.begin(), ordered.end(),
+                  [](const std::pair<K, V>& a, const std::pair<K, V>& b) {
+                      return std::less<K>{}(a.first, b.first);
+                  });
+        key->RecordIterable(ordered);
+    }
+};
+
+}  // namespace dawn::native
+
+#endif  // SRC_DAWN_NATIVE_STREAM_STREAM_H_
diff --git a/src/dawn/native/vulkan/CacheKeyVk.cpp b/src/dawn/native/vulkan/CacheKeyVk.cpp
index 3a82667..2b6d51b 100644
--- a/src/dawn/native/vulkan/CacheKeyVk.cpp
+++ b/src/dawn/native/vulkan/CacheKeyVk.cpp
@@ -14,11 +14,89 @@
 
 #include <cstring>
 
-#include "dawn/native/vulkan/CacheKeyVk.h"
+#include "dawn/common/Assert.h"
+#include "dawn/common/vulkan_platform.h"
+#include "dawn/native/CacheKey.h"
 #include "dawn/native/vulkan/RenderPassCache.h"
 
+#include "icd/generated/vk_typemap_helper.h"
+
 namespace dawn::native {
 
+namespace {
+
+namespace detail {
+
+template <typename... VK_STRUCT_TYPES>
+void ValidatePnextImpl(const VkBaseOutStructure* root) {
+    const VkBaseOutStructure* next = reinterpret_cast<const VkBaseOutStructure*>(root->pNext);
+    while (next != nullptr) {
+        // Assert that the type of each pNext struct is exactly one of the specified
+        // templates.
+        ASSERT(((LvlTypeMap<VK_STRUCT_TYPES>::kSType == next->sType ? 1 : 0) + ... + 0) == 1);
+        next = reinterpret_cast<const VkBaseOutStructure*>(next->pNext);
+    }
+}
+
+template <typename VK_STRUCT_TYPE>
+void SerializePnextImpl(CacheKey* key, const VkBaseOutStructure* root) {
+    const VkBaseOutStructure* next = reinterpret_cast<const VkBaseOutStructure*>(root->pNext);
+    const VK_STRUCT_TYPE* found = nullptr;
+    while (next != nullptr) {
+        if (LvlTypeMap<VK_STRUCT_TYPE>::kSType == next->sType) {
+            if (found == nullptr) {
+                found = reinterpret_cast<const VK_STRUCT_TYPE*>(next);
+            } else {
+                // Fail an assert here since that means that the chain had more than one of
+                // the same typed chained object.
+                ASSERT(false);
+            }
+        }
+        next = reinterpret_cast<const VkBaseOutStructure*>(next->pNext);
+    }
+    if (found != nullptr) {
+        key->Record(found);
+    }
+}
+
+template <typename VK_STRUCT_TYPE,
+          typename... VK_STRUCT_TYPES,
+          typename = std::enable_if_t<(sizeof...(VK_STRUCT_TYPES) > 0)>>
+void SerializePnextImpl(CacheKey* key, const VkBaseOutStructure* root) {
+    SerializePnextImpl<VK_STRUCT_TYPE>(key, root);
+    SerializePnextImpl<VK_STRUCT_TYPES...>(key, root);
+}
+
+template <typename VK_STRUCT_TYPE>
+const VkBaseOutStructure* ToVkBaseOutStructure(const VK_STRUCT_TYPE* t) {
+    // Checks to ensure proper type safety.
+    static_assert(offsetof(VK_STRUCT_TYPE, sType) == offsetof(VkBaseOutStructure, sType) &&
+                      offsetof(VK_STRUCT_TYPE, pNext) == offsetof(VkBaseOutStructure, pNext),
+                  "Argument type is not a proper Vulkan structure type");
+    return reinterpret_cast<const VkBaseOutStructure*>(t);
+}
+
+}  // namespace detail
+
+template <typename... VK_STRUCT_TYPES,
+          typename VK_STRUCT_TYPE,
+          typename = std::enable_if_t<(sizeof...(VK_STRUCT_TYPES) > 0)>>
+void SerializePnext(CacheKey* key, const VK_STRUCT_TYPE* t) {
+    const VkBaseOutStructure* root = detail::ToVkBaseOutStructure(t);
+    detail::ValidatePnextImpl<VK_STRUCT_TYPES...>(root);
+    detail::SerializePnextImpl<VK_STRUCT_TYPES...>(key, root);
+}
+
+// Empty template specialization so that we can put this in to ensure failures occur if new
+// extensions are added without updating serialization.
+template <typename VK_STRUCT_TYPE>
+void SerializePnext(CacheKey* key, const VK_STRUCT_TYPE* t) {
+    const VkBaseOutStructure* root = detail::ToVkBaseOutStructure(t);
+    detail::ValidatePnextImpl<>(root);
+}
+
+}  // namespace
+
 template <>
 void CacheKeySerializer<VkDescriptorSetLayoutBinding>::Serialize(
     CacheKey* key,
@@ -31,7 +109,7 @@
     CacheKey* key,
     const VkDescriptorSetLayoutCreateInfo& t) {
     key->Record(t.flags).RecordIterable(t.pBindings, t.bindingCount);
-    vulkan::SerializePnext<>(key, &t);
+    SerializePnext(key, &t);
 }
 
 template <>
@@ -47,7 +125,7 @@
     // The set layouts are not serialized here because they are pointers to backend objects.
     // They need to be cross-referenced with the frontend objects and serialized from there.
     key->Record(t.flags).RecordIterable(t.pPushConstantRanges, t.pushConstantRangeCount);
-    vulkan::SerializePnext<>(key, &t);
+    SerializePnext(key, &t);
 }
 
 template <>
@@ -85,7 +163,7 @@
     key->Record(t.flags, t.stage)
         .RecordIterable(t.pName, strlen(t.pName))
         .Record(t.pSpecializationInfo);
-    vulkan::SerializePnext<VkPipelineShaderStageRequiredSubgroupSizeCreateInfoEXT>(key, &t);
+    SerializePnext<VkPipelineShaderStageRequiredSubgroupSizeCreateInfoEXT>(key, &t);
 }
 
 template <>
@@ -121,7 +199,7 @@
     key->Record(t.flags)
         .RecordIterable(t.pVertexBindingDescriptions, t.vertexBindingDescriptionCount)
         .RecordIterable(t.pVertexAttributeDescriptions, t.vertexAttributeDescriptionCount);
-    vulkan::SerializePnext<>(key, &t);
+    SerializePnext(key, &t);
 }
 
 template <>
@@ -129,7 +207,7 @@
     CacheKey* key,
     const VkPipelineInputAssemblyStateCreateInfo& t) {
     key->Record(t.flags, t.topology, t.primitiveRestartEnable);
-    vulkan::SerializePnext<>(key, &t);
+    SerializePnext(key, &t);
 }
 
 template <>
@@ -137,7 +215,7 @@
     CacheKey* key,
     const VkPipelineTessellationStateCreateInfo& t) {
     key->Record(t.flags, t.patchControlPoints);
-    vulkan::SerializePnext<>(key, &t);
+    SerializePnext(key, &t);
 }
 
 template <>
@@ -167,7 +245,7 @@
     key->Record(t.flags)
         .RecordIterable(t.pViewports, t.viewportCount)
         .RecordIterable(t.pScissors, t.scissorCount);
-    vulkan::SerializePnext<>(key, &t);
+    SerializePnext(key, &t);
 }
 
 template <>
@@ -177,7 +255,7 @@
     key->Record(t.flags, t.depthClampEnable, t.rasterizerDiscardEnable, t.polygonMode, t.cullMode,
                 t.frontFace, t.depthBiasEnable, t.depthBiasConstantFactor, t.depthBiasClamp,
                 t.depthBiasSlopeFactor, t.lineWidth);
-    vulkan::SerializePnext<VkPipelineRasterizationDepthClipStateCreateInfoEXT>(key, &t);
+    SerializePnext<VkPipelineRasterizationDepthClipStateCreateInfoEXT>(key, &t);
 }
 
 template <>
@@ -186,7 +264,7 @@
     const VkPipelineMultisampleStateCreateInfo& t) {
     key->Record(t.flags, t.rasterizationSamples, t.sampleShadingEnable, t.minSampleShading,
                 t.pSampleMask, t.alphaToCoverageEnable, t.alphaToOneEnable);
-    vulkan::SerializePnext<>(key, &t);
+    SerializePnext(key, &t);
 }
 
 template <>
@@ -202,7 +280,7 @@
     key->Record(t.flags, t.depthTestEnable, t.depthWriteEnable, t.depthCompareOp,
                 t.depthBoundsTestEnable, t.stencilTestEnable, t.front, t.back, t.minDepthBounds,
                 t.maxDepthBounds);
-    vulkan::SerializePnext<>(key, &t);
+    SerializePnext(key, &t);
 }
 
 template <>
@@ -220,7 +298,7 @@
     key->Record(t.flags, t.logicOpEnable, t.logicOp)
         .RecordIterable(t.pAttachments, t.attachmentCount)
         .Record(t.blendConstants);
-    vulkan::SerializePnext<>(key, &t);
+    SerializePnext(key, &t);
 }
 
 template <>
@@ -228,7 +306,7 @@
     CacheKey* key,
     const VkPipelineDynamicStateCreateInfo& t) {
     key->Record(t.flags).RecordIterable(t.pDynamicStates, t.dynamicStateCount);
-    vulkan::SerializePnext<>(key, &t);
+    SerializePnext(key, &t);
 }
 
 template <>
@@ -266,7 +344,7 @@
         .Record(t.pVertexInputState, t.pInputAssemblyState, t.pTessellationState, t.pViewportState,
                 t.pRasterizationState, t.pMultisampleState, t.pDepthStencilState,
                 t.pColorBlendState, t.pDynamicState, t.subpass);
-    vulkan::SerializePnext<>(key, &t);
+    SerializePnext(key, &t);
 }
 
 }  // namespace dawn::native
diff --git a/src/dawn/native/vulkan/CacheKeyVk.h b/src/dawn/native/vulkan/CacheKeyVk.h
deleted file mode 100644
index 80b04db..0000000
--- a/src/dawn/native/vulkan/CacheKeyVk.h
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright 2022 The Dawn Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef SRC_DAWN_NATIVE_VULKAN_CACHEKEYVK_H_
-#define SRC_DAWN_NATIVE_VULKAN_CACHEKEYVK_H_
-
-#include <map>
-
-#include "dawn/common/Assert.h"
-#include "dawn/common/vulkan_platform.h"
-#include "dawn/native/CacheKey.h"
-
-#include "icd/generated/vk_typemap_helper.h"
-
-namespace dawn::native::vulkan {
-
-namespace detail {
-
-template <typename... VK_STRUCT_TYPES>
-void ValidatePnextImpl(const VkBaseOutStructure* root) {
-    const VkBaseOutStructure* next = reinterpret_cast<const VkBaseOutStructure*>(root->pNext);
-    while (next != nullptr) {
-        // Assert that the type of each pNext struct is exactly one of the specified
-        // templates.
-        ASSERT(((LvlTypeMap<VK_STRUCT_TYPES>::kSType == next->sType ? 1 : 0) + ... + 0) == 1);
-        next = reinterpret_cast<const VkBaseOutStructure*>(next->pNext);
-    }
-}
-
-template <typename VK_STRUCT_TYPE>
-void SerializePnextImpl(CacheKey* key, const VkBaseOutStructure* root) {
-    const VkBaseOutStructure* next = reinterpret_cast<const VkBaseOutStructure*>(root->pNext);
-    const VK_STRUCT_TYPE* found = nullptr;
-    while (next != nullptr) {
-        if (LvlTypeMap<VK_STRUCT_TYPE>::kSType == next->sType) {
-            if (found == nullptr) {
-                found = reinterpret_cast<const VK_STRUCT_TYPE*>(next);
-            } else {
-                // Fail an assert here since that means that the chain had more than one of
-                // the same typed chained object.
-                ASSERT(false);
-            }
-        }
-        next = reinterpret_cast<const VkBaseOutStructure*>(next->pNext);
-    }
-    if (found != nullptr) {
-        key->Record(found);
-    }
-}
-
-template <typename VK_STRUCT_TYPE,
-          typename... VK_STRUCT_TYPES,
-          typename = std::enable_if_t<(sizeof...(VK_STRUCT_TYPES) > 0)>>
-void SerializePnextImpl(CacheKey* key, const VkBaseOutStructure* root) {
-    SerializePnextImpl<VK_STRUCT_TYPE>(key, root);
-    SerializePnextImpl<VK_STRUCT_TYPES...>(key, root);
-}
-
-template <typename VK_STRUCT_TYPE>
-const VkBaseOutStructure* ToVkBaseOutStructure(const VK_STRUCT_TYPE* t) {
-    // Checks to ensure proper type safety.
-    static_assert(offsetof(VK_STRUCT_TYPE, sType) == offsetof(VkBaseOutStructure, sType) &&
-                      offsetof(VK_STRUCT_TYPE, pNext) == offsetof(VkBaseOutStructure, pNext),
-                  "Argument type is not a proper Vulkan structure type");
-    return reinterpret_cast<const VkBaseOutStructure*>(t);
-}
-
-}  // namespace detail
-
-template <typename... VK_STRUCT_TYPES,
-          typename VK_STRUCT_TYPE,
-          typename = std::enable_if_t<(sizeof...(VK_STRUCT_TYPES) > 0)>>
-void SerializePnext(CacheKey* key, const VK_STRUCT_TYPE* t) {
-    const VkBaseOutStructure* root = detail::ToVkBaseOutStructure(t);
-    detail::ValidatePnextImpl<VK_STRUCT_TYPES...>(root);
-    detail::SerializePnextImpl<VK_STRUCT_TYPES...>(key, root);
-}
-
-// Empty template specialization so that we can put this in to ensure failures occur if new
-// extensions are added without updating serialization.
-template <typename VK_STRUCT_TYPE>
-void SerializePnext(CacheKey* key, const VK_STRUCT_TYPE* t) {
-    const VkBaseOutStructure* root = detail::ToVkBaseOutStructure(t);
-    detail::ValidatePnextImpl<>(root);
-}
-
-}  // namespace dawn::native::vulkan
-
-#endif  // SRC_DAWN_NATIVE_VULKAN_CACHEKEYVK_H_