Remove kMaxBindingsPerGroup limit

kMaxBindingsPerGroup is not a real WebGPU limit. Code in Dawn that
uses it now uses dynamically sized vectors, optimistic stack_vecs or
a different limit like kMaxDynamicBuffersPerPipelineLayout.

The CL introduces kMaxOptimalBindingsPerGroup=32 which is used to size
the static portion of stack_vecs, but the value is tentative.

Bug: dawn:443
Change-Id: I08e06bed6531bed8b4365f36cf2fc0579ac5f180
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/23502
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Stephen White <senorblanco@chromium.org>
diff --git a/src/dawn_native/BindGroup.cpp b/src/dawn_native/BindGroup.cpp
index 2da980d..71e8a28 100644
--- a/src/dawn_native/BindGroup.cpp
+++ b/src/dawn_native/BindGroup.cpp
@@ -178,8 +178,9 @@
         }
 
         const BindGroupLayoutBase::BindingMap& bindingMap = descriptor->layout->GetBindingMap();
+        ASSERT(bindingMap.size() <= kMaxBindingsPerPipelineLayout);
 
-        ityp::bitset<BindingIndex, kMaxBindingsPerGroup> bindingsSet;
+        ityp::bitset<BindingIndex, kMaxBindingsPerPipelineLayout> bindingsSet;
         for (uint32_t i = 0; i < descriptor->entryCount; ++i) {
             const BindGroupEntry& entry = descriptor->entries[i];
 
diff --git a/src/dawn_native/BindGroupAndStorageBarrierTracker.h b/src/dawn_native/BindGroupAndStorageBarrierTracker.h
index e17d263..e34a165 100644
--- a/src/dawn_native/BindGroupAndStorageBarrierTracker.h
+++ b/src/dawn_native/BindGroupAndStorageBarrierTracker.h
@@ -16,6 +16,7 @@
 #define DAWNNATIVE_BINDGROUPANDSTORAGEBARRIERTRACKER_H_
 
 #include "common/ityp_bitset.h"
+#include "common/ityp_stack_vec.h"
 #include "dawn_native/BindGroup.h"
 #include "dawn_native/BindGroupTracker.h"
 #include "dawn_native/Buffer.h"
@@ -39,11 +40,12 @@
             ASSERT(index < kMaxBindGroupsTyped);
 
             if (this->mBindGroups[index] != bindGroup) {
-                mBindings[index] = {};
-                mBindingsNeedingBarrier[index] = {};
-
                 const BindGroupLayoutBase* layout = bindGroup->GetLayout();
 
+                mBindings[index].resize(layout->GetBindingCount());
+                mBindingTypes[index].resize(layout->GetBindingCount());
+                mBindingsNeedingBarrier[index] = {};
+
                 for (BindingIndex bindingIndex{0}; bindingIndex < layout->GetBindingCount();
                      ++bindingIndex) {
                     const BindingInfo& bindingInfo = layout->GetBindingInfo(bindingIndex);
@@ -91,15 +93,16 @@
         }
 
       protected:
-        ityp::
-            array<BindGroupIndex, ityp::bitset<BindingIndex, kMaxBindingsPerGroup>, kMaxBindGroups>
-                mBindingsNeedingBarrier = {};
         ityp::array<BindGroupIndex,
-                    ityp::array<BindingIndex, wgpu::BindingType, kMaxBindingsPerGroup>,
+                    ityp::bitset<BindingIndex, kMaxBindingsPerPipelineLayout>,
+                    kMaxBindGroups>
+            mBindingsNeedingBarrier = {};
+        ityp::array<BindGroupIndex,
+                    ityp::stack_vec<BindingIndex, wgpu::BindingType, kMaxOptimalBindingsPerGroup>,
                     kMaxBindGroups>
             mBindingTypes = {};
         ityp::array<BindGroupIndex,
-                    ityp::array<BindingIndex, ObjectBase*, kMaxBindingsPerGroup>,
+                    ityp::stack_vec<BindingIndex, ObjectBase*, kMaxOptimalBindingsPerGroup>,
                     kMaxBindGroups>
             mBindings = {};
     };
diff --git a/src/dawn_native/BindGroupLayout.cpp b/src/dawn_native/BindGroupLayout.cpp
index 3476520..2df2483 100644
--- a/src/dawn_native/BindGroupLayout.cpp
+++ b/src/dawn_native/BindGroupLayout.cpp
@@ -258,10 +258,6 @@
             bindingsSet.insert(bindingNumber);
         }
 
-        if (bindingsSet.size() > kMaxBindingsPerGroup) {
-            return DAWN_VALIDATION_ERROR("The number of bindings exceeds kMaxBindingsPerGroup.");
-        }
-
         DAWN_TRY(ValidateBindingCounts(bindingCounts));
 
         return {};
@@ -358,8 +354,6 @@
         // This is a utility function to help ASSERT that the BGL-binding comparator places buffers
         // first.
         bool CheckBufferBindingsFirst(ityp::span<BindingIndex, const BindingInfo> bindings) {
-            ASSERT(bindings.size() <= BindingIndex(kMaxBindingsPerGroup));
-
             BindingIndex lastBufferIndex{0};
             BindingIndex firstNonBufferIndex = std::numeric_limits<BindingIndex>::max();
             for (BindingIndex i{0}; i < bindings.size(); ++i) {
@@ -381,13 +375,12 @@
 
     BindGroupLayoutBase::BindGroupLayoutBase(DeviceBase* device,
                                              const BindGroupLayoutDescriptor* descriptor)
-        : CachedObject(device) {
+        : CachedObject(device), mBindingInfo(BindingIndex(descriptor->entryCount)) {
         std::vector<BindGroupLayoutEntry> sortedBindings(
             descriptor->entries, descriptor->entries + descriptor->entryCount);
-
         std::sort(sortedBindings.begin(), sortedBindings.end(), SortBindingsCompare);
 
-        for (BindingIndex i{0}; i < BindingIndex(descriptor->entryCount); ++i) {
+        for (BindingIndex i{0}; i < mBindingInfo.size(); ++i) {
             const BindGroupLayoutEntry& binding = sortedBindings[static_cast<uint32_t>(i)];
             mBindingInfo[i].binding = BindingNumber(binding.binding);
             mBindingInfo[i].type = binding.type;
@@ -416,6 +409,7 @@
             ASSERT(it.second);
         }
         ASSERT(CheckBufferBindingsFirst({mBindingInfo.data(), GetBindingCount()}));
+        ASSERT(mBindingInfo.size() <= kMaxBindingsPerPipelineLayoutTyped);
     }
 
     BindGroupLayoutBase::BindGroupLayoutBase(DeviceBase* device, ObjectBase::ErrorTag tag)
@@ -471,7 +465,7 @@
     }
 
     BindingIndex BindGroupLayoutBase::GetBindingCount() const {
-        return BindingIndex(mBindingCounts.totalCount);
+        return mBindingInfo.size();
     }
 
     BindingIndex BindGroupLayoutBase::GetBufferCount() const {
diff --git a/src/dawn_native/BindGroupLayout.h b/src/dawn_native/BindGroupLayout.h
index 5e50e75..958abf6 100644
--- a/src/dawn_native/BindGroupLayout.h
+++ b/src/dawn_native/BindGroupLayout.h
@@ -18,8 +18,8 @@
 #include "common/Constants.h"
 #include "common/Math.h"
 #include "common/SlabAllocator.h"
-#include "common/ityp_array.h"
 #include "common/ityp_span.h"
+#include "common/ityp_vector.h"
 #include "dawn_native/BindingInfo.h"
 #include "dawn_native/CachedObject.h"
 #include "dawn_native/Error.h"
@@ -64,7 +64,7 @@
 
         const BindingInfo& GetBindingInfo(BindingIndex bindingIndex) const {
             ASSERT(!IsError());
-            ASSERT(bindingIndex < BindingIndex(kMaxBindingsPerGroup));
+            ASSERT(bindingIndex < mBindingInfo.size());
             return mBindingInfo[bindingIndex];
         }
         const BindingMap& GetBindingMap() const;
@@ -124,7 +124,7 @@
         BindGroupLayoutBase(DeviceBase* device, ObjectBase::ErrorTag tag);
 
         BindingCounts mBindingCounts = {};
-        ityp::array<BindingIndex, BindingInfo, kMaxBindingsPerGroup> mBindingInfo;
+        ityp::vector<BindingIndex, BindingInfo> mBindingInfo;
 
         // Map from BindGroupLayoutEntry.binding to packed indices.
         BindingMap mBindingMap;
diff --git a/src/dawn_native/BindGroupTracker.h b/src/dawn_native/BindGroupTracker.h
index 8d03ebf..a3addb2 100644
--- a/src/dawn_native/BindGroupTracker.h
+++ b/src/dawn_native/BindGroupTracker.h
@@ -103,7 +103,9 @@
         BindGroupLayoutMask mBindGroupLayoutsMask = 0;
         ityp::array<BindGroupIndex, BindGroupBase*, kMaxBindGroups> mBindGroups = {};
         ityp::array<BindGroupIndex, uint32_t, kMaxBindGroups> mDynamicOffsetCounts = {};
-        ityp::array<BindGroupIndex, std::array<DynamicOffset, kMaxBindingsPerGroup>, kMaxBindGroups>
+        ityp::array<BindGroupIndex,
+                    std::array<DynamicOffset, kMaxDynamicBuffersPerPipelineLayout>,
+                    kMaxBindGroups>
             mDynamicOffsets = {};
 
         // |mPipelineLayout| is the current pipeline layout set on the command buffer.
diff --git a/src/dawn_native/BindingInfo.h b/src/dawn_native/BindingInfo.h
index f518ed8..b6cfad1 100644
--- a/src/dawn_native/BindingInfo.h
+++ b/src/dawn_native/BindingInfo.h
@@ -36,9 +36,28 @@
 
     using BindGroupIndex = TypedInteger<struct BindGroupIndexT, uint32_t>;
 
-    static constexpr BindingIndex kMaxBindingsPerGroupTyped = BindingIndex(kMaxBindingsPerGroup);
     static constexpr BindGroupIndex kMaxBindGroupsTyped = BindGroupIndex(kMaxBindGroups);
 
+    // Not a real WebGPU limit, but the sum of the two limits is useful for internal optimizations.
+    static constexpr uint32_t kMaxDynamicBuffersPerPipelineLayout =
+        kMaxDynamicUniformBuffersPerPipelineLayout + kMaxDynamicStorageBuffersPerPipelineLayout;
+
+    static constexpr BindingIndex kMaxDynamicBuffersPerPipelineLayoutTyped =
+        BindingIndex(kMaxDynamicBuffersPerPipelineLayout);
+
+    // Not a real WebGPU limit, but used to optimize parts of Dawn which expect valid usage of the
+    // API. There should never be more bindings than the max per stage, for each stage.
+    static constexpr uint32_t kMaxBindingsPerPipelineLayout =
+        3 * (kMaxSampledTexturesPerShaderStage + kMaxSamplersPerShaderStage +
+             kMaxStorageBuffersPerShaderStage + kMaxStorageTexturesPerShaderStage +
+             kMaxUniformBuffersPerShaderStage);
+
+    static constexpr BindingIndex kMaxBindingsPerPipelineLayoutTyped =
+        BindingIndex(kMaxBindingsPerPipelineLayout);
+
+    // TODO(enga): Figure out a good number for this.
+    static constexpr uint32_t kMaxOptimalBindingsPerGroup = 32;
+
     struct BindingInfo {
         BindingNumber binding;
         wgpu::ShaderStage visibility;
diff --git a/src/dawn_native/PipelineLayout.cpp b/src/dawn_native/PipelineLayout.cpp
index 5b47d9d..f34003a 100644
--- a/src/dawn_native/PipelineLayout.cpp
+++ b/src/dawn_native/PipelineLayout.cpp
@@ -17,6 +17,7 @@
 #include "common/Assert.h"
 #include "common/BitSetIterator.h"
 #include "common/HashUtils.h"
+#include "common/ityp_stack_vec.h"
 #include "dawn_native/BindGroupLayout.h"
 #include "dawn_native/Device.h"
 #include "dawn_native/ShaderModule.h"
@@ -118,9 +119,10 @@
         ASSERT(count > 0);
 
         // Data which BindGroupLayoutDescriptor will point to for creation
-        ityp::array<BindGroupIndex,
-                    ityp::array<BindingIndex, BindGroupLayoutEntry, kMaxBindingsPerGroup>,
-                    kMaxBindGroups>
+        ityp::array<
+            BindGroupIndex,
+            ityp::stack_vec<BindingIndex, BindGroupLayoutEntry, kMaxOptimalBindingsPerGroup>,
+            kMaxBindGroups>
             entryData = {};
 
         // A map of bindings to the index in |entryData|
@@ -194,6 +196,7 @@
 
                     IncrementBindingCounts(&bindingCounts, bindingSlot);
                     BindingIndex currentBindingCount = entryCounts[group];
+                    entryData[group].resize(currentBindingCount + BindingIndex(1));
                     entryData[group][currentBindingCount] = bindingSlot;
 
                     usedBindingsMap[group][bindingNumber] = currentBindingCount;
diff --git a/src/dawn_native/d3d12/BindGroupLayoutD3D12.cpp b/src/dawn_native/d3d12/BindGroupLayoutD3D12.cpp
index 9280f8c..62722f6 100644
--- a/src/dawn_native/d3d12/BindGroupLayoutD3D12.cpp
+++ b/src/dawn_native/d3d12/BindGroupLayoutD3D12.cpp
@@ -46,6 +46,7 @@
 
     BindGroupLayout::BindGroupLayout(Device* device, const BindGroupLayoutDescriptor* descriptor)
         : BindGroupLayoutBase(device, descriptor),
+          mBindingOffsets(GetBindingCount()),
           mDescriptorCounts{},
           mBindGroupAllocator(MakeFrontendBindGroupAllocator<BindGroup>(4096)) {
         for (BindingIndex bindingIndex = GetDynamicBufferCount(); bindingIndex < GetBindingCount();
@@ -170,9 +171,8 @@
         mBindGroupAllocator.Deallocate(bindGroup);
     }
 
-    const ityp::array<BindingIndex, uint32_t, kMaxBindingsPerGroup>&
-    BindGroupLayout::GetBindingOffsets() const {
-        return mBindingOffsets;
+    ityp::span<BindingIndex, const uint32_t> BindGroupLayout::GetBindingOffsets() const {
+        return {mBindingOffsets.data(), mBindingOffsets.size()};
     }
 
     uint32_t BindGroupLayout::GetCbvUavSrvDescriptorTableSize() const {
diff --git a/src/dawn_native/d3d12/BindGroupLayoutD3D12.h b/src/dawn_native/d3d12/BindGroupLayoutD3D12.h
index 5a5ba8b..c5f32f5 100644
--- a/src/dawn_native/d3d12/BindGroupLayoutD3D12.h
+++ b/src/dawn_native/d3d12/BindGroupLayoutD3D12.h
@@ -18,6 +18,7 @@
 #include "dawn_native/BindGroupLayout.h"
 
 #include "common/SlabAllocator.h"
+#include "common/ityp_stack_vec.h"
 #include "dawn_native/d3d12/d3d12_platform.h"
 
 namespace dawn_native { namespace d3d12 {
@@ -44,7 +45,7 @@
             Count,
         };
 
-        const ityp::array<BindingIndex, uint32_t, kMaxBindingsPerGroup>& GetBindingOffsets() const;
+        ityp::span<BindingIndex, const uint32_t> GetBindingOffsets() const;
         uint32_t GetCbvUavSrvDescriptorTableSize() const;
         uint32_t GetSamplerDescriptorTableSize() const;
         uint32_t GetCbvUavSrvDescriptorCount() const;
@@ -54,7 +55,7 @@
 
       private:
         ~BindGroupLayout() override = default;
-        ityp::array<BindingIndex, uint32_t, kMaxBindingsPerGroup> mBindingOffsets;
+        ityp::stack_vec<BindingIndex, uint32_t, kMaxOptimalBindingsPerGroup> mBindingOffsets;
         std::array<uint32_t, DescriptorType::Count> mDescriptorCounts;
         D3D12_DESCRIPTOR_RANGE mRanges[DescriptorType::Count];
 
diff --git a/src/dawn_native/d3d12/DeviceD3D12.cpp b/src/dawn_native/d3d12/DeviceD3D12.cpp
index 01e7ead..e3a4c3c 100644
--- a/src/dawn_native/d3d12/DeviceD3D12.cpp
+++ b/src/dawn_native/d3d12/DeviceD3D12.cpp
@@ -90,12 +90,14 @@
         mCommandAllocatorManager = std::make_unique<CommandAllocatorManager>(this);
 
         // Zero sized allocator is never requested and does not need to exist.
-        for (uint32_t countIndex = 1; countIndex < kNumOfStagingDescriptorAllocators;
-             countIndex++) {
+        for (uint32_t countIndex = 1; countIndex <= kMaxViewDescriptorsPerBindGroup; countIndex++) {
             mViewAllocators[countIndex] = std::make_unique<StagingDescriptorAllocator>(
                 this, countIndex, kShaderVisibleDescriptorHeapSize,
                 D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+        }
 
+        for (uint32_t countIndex = 1; countIndex <= kMaxSamplerDescriptorsPerBindGroup;
+             countIndex++) {
             mSamplerAllocators[countIndex] = std::make_unique<StagingDescriptorAllocator>(
                 this, countIndex, kShaderVisibleDescriptorHeapSize,
                 D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
@@ -556,13 +558,13 @@
 
     StagingDescriptorAllocator* Device::GetViewStagingDescriptorAllocator(
         uint32_t descriptorCount) const {
-        ASSERT(descriptorCount < kNumOfStagingDescriptorAllocators);
+        ASSERT(descriptorCount <= kMaxViewDescriptorsPerBindGroup);
         return mViewAllocators[descriptorCount].get();
     }
 
     StagingDescriptorAllocator* Device::GetSamplerStagingDescriptorAllocator(
         uint32_t descriptorCount) const {
-        ASSERT(descriptorCount < kNumOfStagingDescriptorAllocators);
+        ASSERT(descriptorCount <= kMaxSamplerDescriptorsPerBindGroup);
         return mSamplerAllocators[descriptorCount].get();
     }
 
diff --git a/src/dawn_native/d3d12/DeviceD3D12.h b/src/dawn_native/d3d12/DeviceD3D12.h
index 410e84e..efb9fd2 100644
--- a/src/dawn_native/d3d12/DeviceD3D12.h
+++ b/src/dawn_native/d3d12/DeviceD3D12.h
@@ -19,6 +19,7 @@
 
 #include "common/Constants.h"
 #include "common/SerialQueue.h"
+#include "dawn_native/BindingInfo.h"
 #include "dawn_native/Device.h"
 #include "dawn_native/d3d12/CommandRecordingContext.h"
 #include "dawn_native/d3d12/D3D12Info.h"
@@ -192,13 +193,21 @@
         std::unique_ptr<ResourceAllocatorManager> mResourceAllocatorManager;
         std::unique_ptr<ResidencyManager> mResidencyManager;
 
-        // Index corresponds to the descriptor count in the range [0, kMaxBindingsPerGroup].
-        static constexpr uint32_t kNumOfStagingDescriptorAllocators = kMaxBindingsPerGroup + 1;
+        // TODO(enga): Consider bucketing these if the count is too many.
+        static constexpr uint32_t kMaxSamplerDescriptorsPerBindGroup =
+            3 * kMaxSamplersPerShaderStage;
+        static constexpr uint32_t kMaxViewDescriptorsPerBindGroup =
+            kMaxBindingsPerPipelineLayout - kMaxSamplerDescriptorsPerBindGroup;
 
-        std::array<std::unique_ptr<StagingDescriptorAllocator>, kNumOfStagingDescriptorAllocators>
+        // Index corresponds to the descriptor count in the range [0,
+        // kMaxSamplerDescriptorsPerBindGroup].
+        std::array<std::unique_ptr<StagingDescriptorAllocator>,
+                   kMaxSamplerDescriptorsPerBindGroup + 1>
             mViewAllocators;
 
-        std::array<std::unique_ptr<StagingDescriptorAllocator>, kNumOfStagingDescriptorAllocators>
+        // Index corresponds to the descriptor count in the range [0,
+        // kMaxViewDescriptorsPerBindGroup].
+        std::array<std::unique_ptr<StagingDescriptorAllocator>, kMaxViewDescriptorsPerBindGroup + 1>
             mSamplerAllocators;
 
         std::unique_ptr<StagingDescriptorAllocator> mRenderTargetViewAllocator;
diff --git a/src/dawn_native/d3d12/PipelineLayoutD3D12.cpp b/src/dawn_native/d3d12/PipelineLayoutD3D12.cpp
index 5e3460b..8eed07e 100644
--- a/src/dawn_native/d3d12/PipelineLayoutD3D12.cpp
+++ b/src/dawn_native/d3d12/PipelineLayoutD3D12.cpp
@@ -194,7 +194,7 @@
     uint32_t PipelineLayout::GetDynamicRootParameterIndex(BindGroupIndex group,
                                                           BindingIndex bindingIndex) const {
         ASSERT(group < kMaxBindGroupsTyped);
-        ASSERT(bindingIndex < kMaxBindingsPerGroupTyped);
+        ASSERT(bindingIndex < kMaxDynamicBuffersPerPipelineLayoutTyped);
         ASSERT(GetBindGroupLayout(group)->GetBindingInfo(bindingIndex).hasDynamicOffset);
         ASSERT(GetBindGroupLayout(group)->GetBindingInfo(bindingIndex).visibility !=
                wgpu::ShaderStage::None);
diff --git a/src/dawn_native/d3d12/PipelineLayoutD3D12.h b/src/dawn_native/d3d12/PipelineLayoutD3D12.h
index 8de41b0..6116cd8 100644
--- a/src/dawn_native/d3d12/PipelineLayoutD3D12.h
+++ b/src/dawn_native/d3d12/PipelineLayoutD3D12.h
@@ -45,7 +45,7 @@
         ityp::array<BindGroupIndex, uint32_t, kMaxBindGroups> mCbvUavSrvRootParameterInfo;
         ityp::array<BindGroupIndex, uint32_t, kMaxBindGroups> mSamplerRootParameterInfo;
         ityp::array<BindGroupIndex,
-                    ityp::array<BindingIndex, uint32_t, kMaxBindingsPerGroup>,
+                    ityp::array<BindingIndex, uint32_t, kMaxDynamicBuffersPerPipelineLayout>,
                     kMaxBindGroups>
             mDynamicRootParameterIndices;
         ComPtr<ID3D12RootSignature> mRootSignature;
diff --git a/src/dawn_native/metal/PipelineLayoutMTL.h b/src/dawn_native/metal/PipelineLayoutMTL.h
index 7a3ad80..b492e3b 100644
--- a/src/dawn_native/metal/PipelineLayoutMTL.h
+++ b/src/dawn_native/metal/PipelineLayoutMTL.h
@@ -15,7 +15,7 @@
 #ifndef DAWNNATIVE_METAL_PIPELINELAYOUTMTL_H_
 #define DAWNNATIVE_METAL_PIPELINELAYOUTMTL_H_
 
-#include "common/ityp_array.h"
+#include "common/ityp_stack_vec.h"
 #include "dawn_native/BindingInfo.h"
 #include "dawn_native/PipelineLayout.h"
 
@@ -44,7 +44,7 @@
 
         using BindingIndexInfo =
             ityp::array<BindGroupIndex,
-                        ityp::array<BindingIndex, uint32_t, kMaxBindingsPerGroup>,
+                        ityp::stack_vec<BindingIndex, uint32_t, kMaxOptimalBindingsPerGroup>,
                         kMaxBindGroups>;
         const BindingIndexInfo& GetBindingIndexInfo(SingleShaderStage stage) const;
 
diff --git a/src/dawn_native/metal/PipelineLayoutMTL.mm b/src/dawn_native/metal/PipelineLayoutMTL.mm
index 3ee7d92..fa5d926 100644
--- a/src/dawn_native/metal/PipelineLayoutMTL.mm
+++ b/src/dawn_native/metal/PipelineLayoutMTL.mm
@@ -29,6 +29,8 @@
             uint32_t textureIndex = 0;
 
             for (BindGroupIndex group : IterateBitSet(GetBindGroupLayoutsMask())) {
+                mIndexInfo[stage][group].resize(GetBindGroupLayout(group)->GetBindingCount());
+
                 for (BindingIndex bindingIndex{0};
                      bindingIndex < GetBindGroupLayout(group)->GetBindingCount(); ++bindingIndex) {
                     const BindingInfo& bindingInfo =
diff --git a/src/dawn_native/opengl/PipelineGL.h b/src/dawn_native/opengl/PipelineGL.h
index 7f681f9..a79909a 100644
--- a/src/dawn_native/opengl/PipelineGL.h
+++ b/src/dawn_native/opengl/PipelineGL.h
@@ -36,10 +36,6 @@
                         const PipelineLayout* layout,
                         const PerStage<const ShaderModule*>& modules);
 
-        using BindingLocations = ityp::array<BindGroupIndex,
-                                             ityp::array<BindingIndex, GLint, kMaxBindingsPerGroup>,
-                                             kMaxBindGroups>;
-
         // For each unit a sampler is bound to we need to know if we should use filtering or not
         // because int and uint texture are only complete without filtering.
         struct SamplerUnit {
diff --git a/src/dawn_native/opengl/PipelineLayoutGL.cpp b/src/dawn_native/opengl/PipelineLayoutGL.cpp
index 0e98c61..8faab78 100644
--- a/src/dawn_native/opengl/PipelineLayoutGL.cpp
+++ b/src/dawn_native/opengl/PipelineLayoutGL.cpp
@@ -30,6 +30,7 @@
 
         for (BindGroupIndex group : IterateBitSet(GetBindGroupLayoutsMask())) {
             const BindGroupLayoutBase* bgl = GetBindGroupLayout(group);
+            mIndexInfo[group].resize(bgl->GetBindingCount());
 
             for (BindingIndex bindingIndex{0}; bindingIndex < bgl->GetBindingCount();
                  ++bindingIndex) {
diff --git a/src/dawn_native/opengl/PipelineLayoutGL.h b/src/dawn_native/opengl/PipelineLayoutGL.h
index 3d511d6..eeff718 100644
--- a/src/dawn_native/opengl/PipelineLayoutGL.h
+++ b/src/dawn_native/opengl/PipelineLayoutGL.h
@@ -18,6 +18,7 @@
 #include "dawn_native/PipelineLayout.h"
 
 #include "common/ityp_array.h"
+#include "common/ityp_vector.h"
 #include "dawn_native/BindingInfo.h"
 #include "dawn_native/opengl/opengl_platform.h"
 
@@ -30,9 +31,7 @@
         PipelineLayout(Device* device, const PipelineLayoutDescriptor* descriptor);
 
         using BindingIndexInfo =
-            ityp::array<BindGroupIndex,
-                        ityp::array<BindingIndex, GLuint, kMaxBindingsPerGroup>,
-                        kMaxBindGroups>;
+            ityp::array<BindGroupIndex, ityp::vector<BindingIndex, GLuint>, kMaxBindGroups>;
         const BindingIndexInfo& GetBindingIndexInfo() const;
 
         GLuint GetTextureUnitsUsed() const;
diff --git a/src/dawn_native/vulkan/BindGroupLayoutVk.cpp b/src/dawn_native/vulkan/BindGroupLayoutVk.cpp
index 1b325be..54c3dcf 100644
--- a/src/dawn_native/vulkan/BindGroupLayoutVk.cpp
+++ b/src/dawn_native/vulkan/BindGroupLayoutVk.cpp
@@ -15,6 +15,7 @@
 #include "dawn_native/vulkan/BindGroupLayoutVk.h"
 
 #include "common/BitSetIterator.h"
+#include "common/ityp_vector.h"
 #include "dawn_native/vulkan/BindGroupVk.h"
 #include "dawn_native/vulkan/DescriptorSetAllocator.h"
 #include "dawn_native/vulkan/DeviceVk.h"
@@ -85,29 +86,30 @@
         // Compute the bindings that will be chained in the DescriptorSetLayout create info. We add
         // one entry per binding set. This might be optimized by computing continuous ranges of
         // bindings of the same type.
-        uint32_t numBindings = 0;
-        std::array<VkDescriptorSetLayoutBinding, kMaxBindingsPerGroup> bindings;
+        ityp::vector<BindingIndex, VkDescriptorSetLayoutBinding> bindings;
+        bindings.reserve(GetBindingCount());
+
         for (const auto& it : GetBindingMap()) {
             BindingNumber bindingNumber = it.first;
             BindingIndex bindingIndex = it.second;
             const BindingInfo& bindingInfo = GetBindingInfo(bindingIndex);
 
-            VkDescriptorSetLayoutBinding* vkBinding = &bindings[numBindings];
-            vkBinding->binding = static_cast<uint32_t>(bindingNumber);
-            vkBinding->descriptorType =
+            VkDescriptorSetLayoutBinding vkBinding;
+            vkBinding.binding = static_cast<uint32_t>(bindingNumber);
+            vkBinding.descriptorType =
                 VulkanDescriptorType(bindingInfo.type, bindingInfo.hasDynamicOffset);
-            vkBinding->descriptorCount = 1;
-            vkBinding->stageFlags = VulkanShaderStageFlags(bindingInfo.visibility);
-            vkBinding->pImmutableSamplers = nullptr;
+            vkBinding.descriptorCount = 1;
+            vkBinding.stageFlags = VulkanShaderStageFlags(bindingInfo.visibility);
+            vkBinding.pImmutableSamplers = nullptr;
 
-            numBindings++;
+            bindings.emplace_back(vkBinding);
         }
 
         VkDescriptorSetLayoutCreateInfo createInfo;
         createInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
         createInfo.pNext = nullptr;
         createInfo.flags = 0;
-        createInfo.bindingCount = numBindings;
+        createInfo.bindingCount = static_cast<uint32_t>(bindings.size());
         createInfo.pBindings = bindings.data();
 
         Device* device = ToBackend(GetDevice());
diff --git a/src/dawn_native/vulkan/BindGroupVk.cpp b/src/dawn_native/vulkan/BindGroupVk.cpp
index eb31182..1124e15 100644
--- a/src/dawn_native/vulkan/BindGroupVk.cpp
+++ b/src/dawn_native/vulkan/BindGroupVk.cpp
@@ -15,6 +15,7 @@
 #include "dawn_native/vulkan/BindGroupVk.h"
 
 #include "common/BitSetIterator.h"
+#include "common/ityp_stack_vec.h"
 #include "dawn_native/vulkan/BindGroupLayoutVk.h"
 #include "dawn_native/vulkan/BufferVk.h"
 #include "dawn_native/vulkan/DeviceVk.h"
@@ -38,11 +39,15 @@
           mDescriptorSetAllocation(descriptorSetAllocation) {
         // Now do a write of a single descriptor set with all possible chained data allocated on the
         // stack.
-        uint32_t numWrites = 0;
-        std::array<VkWriteDescriptorSet, kMaxBindingsPerGroup> writes;
-        std::array<VkDescriptorBufferInfo, kMaxBindingsPerGroup> writeBufferInfo;
-        std::array<VkDescriptorImageInfo, kMaxBindingsPerGroup> writeImageInfo;
+        const uint32_t bindingCount = static_cast<uint32_t>((GetLayout()->GetBindingCount()));
+        ityp::stack_vec<uint32_t, VkWriteDescriptorSet, kMaxOptimalBindingsPerGroup> writes(
+            bindingCount);
+        ityp::stack_vec<uint32_t, VkDescriptorBufferInfo, kMaxOptimalBindingsPerGroup>
+            writeBufferInfo(bindingCount);
+        ityp::stack_vec<uint32_t, VkDescriptorImageInfo, kMaxOptimalBindingsPerGroup>
+            writeImageInfo(bindingCount);
 
+        uint32_t numWrites = 0;
         for (const auto& it : GetLayout()->GetBindingMap()) {
             BindingNumber bindingNumber = it.first;
             BindingIndex bindingIndex = it.second;
diff --git a/src/dawn_native/vulkan/CommandBufferVk.cpp b/src/dawn_native/vulkan/CommandBufferVk.cpp
index dcbbf0d..1159a3e 100644
--- a/src/dawn_native/vulkan/CommandBufferVk.cpp
+++ b/src/dawn_native/vulkan/CommandBufferVk.cpp
@@ -104,7 +104,7 @@
             const ityp::array<BindGroupIndex, BindGroupBase*, kMaxBindGroups>& bindGroups,
             const ityp::array<BindGroupIndex, uint32_t, kMaxBindGroups>& dynamicOffsetCounts,
             const ityp::array<BindGroupIndex,
-                              std::array<uint32_t, kMaxBindingsPerGroup>,
+                              std::array<uint32_t, kMaxDynamicBuffersPerPipelineLayout>,
                               kMaxBindGroups>& dynamicOffsets) {
             for (BindGroupIndex dirtyIndex : IterateBitSet(bindGroupsToApply)) {
                 VkDescriptorSet set = ToBackend(bindGroups[dirtyIndex])->GetHandle();
diff --git a/src/dawn_native/vulkan/DescriptorSetAllocator.cpp b/src/dawn_native/vulkan/DescriptorSetAllocator.cpp
index 9f1999c..a7b794f 100644
--- a/src/dawn_native/vulkan/DescriptorSetAllocator.cpp
+++ b/src/dawn_native/vulkan/DescriptorSetAllocator.cpp
@@ -38,7 +38,6 @@
             totalDescriptorCount += it.second;
             mPoolSizes.push_back(VkDescriptorPoolSize{it.first, it.second});
         }
-        ASSERT(totalDescriptorCount <= kMaxBindingsPerGroup);
 
         if (totalDescriptorCount == 0) {
             // Vulkan requires that valid usage of vkCreateDescriptorPool must have a non-zero
@@ -49,6 +48,9 @@
             mPoolSizes.push_back(VkDescriptorPoolSize{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1});
             mMaxSets = kMaxDescriptorsPerPool;
         } else {
+            ASSERT(totalDescriptorCount <= kMaxBindingsPerPipelineLayout);
+            static_assert(kMaxBindingsPerPipelineLayout <= kMaxDescriptorsPerPool, "");
+
             // Compute the total number of descriptors sets that fits given the max.
             mMaxSets = kMaxDescriptorsPerPool / totalDescriptorCount;
             ASSERT(mMaxSets > 0);