Convert LayoutBindingInfo to an array of BindingInfos

This makes accessing per-index data simpler, and now that dynamic
buffer bindings are packed at the front, the old IterateBitset on
the dynamic buffer binding mask can be replaced with a simple loop
over the beginning bindings.

Bug: dawn:354
Change-Id: I1adf371c3228690758f90ab1f0de88ad8d0f950d
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/17681
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Austin Eng <enga@chromium.org>
diff --git a/src/dawn_native/BindGroup.cpp b/src/dawn_native/BindGroup.cpp
index b8e87f0..c21ae23 100644
--- a/src/dawn_native/BindGroup.cpp
+++ b/src/dawn_native/BindGroup.cpp
@@ -64,10 +64,7 @@
         MaybeError ValidateTextureBinding(const DeviceBase* device,
                                           const BindGroupBinding& binding,
                                           wgpu::TextureUsage requiredUsage,
-                                          bool multisampledBinding,
-                                          wgpu::TextureComponentType requiredComponentType,
-                                          wgpu::TextureViewDimension requiredDimension,
-                                          wgpu::TextureFormat requiredStorageTextureFormat) {
+                                          const BindGroupLayoutBase::BindingInfo& bindingInfo) {
             if (binding.textureView == nullptr || binding.sampler != nullptr ||
                 binding.buffer != nullptr) {
                 return DAWN_VALIDATION_ERROR("expected texture binding");
@@ -80,18 +77,18 @@
                 return DAWN_VALIDATION_ERROR("texture binding usage mismatch");
             }
 
-            if (texture->IsMultisampledTexture() != multisampledBinding) {
+            if (texture->IsMultisampledTexture() != bindingInfo.multisampled) {
                 return DAWN_VALIDATION_ERROR("texture multisampling mismatch");
             }
 
             switch (requiredUsage) {
                 case wgpu::TextureUsage::Sampled: {
-                    if (!texture->GetFormat().HasComponentType(requiredComponentType)) {
+                    if (!texture->GetFormat().HasComponentType(bindingInfo.textureComponentType)) {
                         return DAWN_VALIDATION_ERROR("texture component type usage mismatch");
                     }
                 } break;
                 case wgpu::TextureUsage::Storage: {
-                    if (texture->GetFormat().format != requiredStorageTextureFormat) {
+                    if (texture->GetFormat().format != bindingInfo.storageTextureFormat) {
                         return DAWN_VALIDATION_ERROR("storage texture format mismatch");
                     }
                 } break;
@@ -100,7 +97,7 @@
                     break;
             }
 
-            if (binding.textureView->GetDimension() != requiredDimension) {
+            if (binding.textureView->GetDimension() != bindingInfo.textureDimension) {
                 return DAWN_VALIDATION_ERROR("texture view dimension mismatch");
             }
 
@@ -127,16 +124,12 @@
         }
 
         DAWN_TRY(device->ValidateObject(descriptor->layout));
-
-        const BindGroupLayoutBase::LayoutBindingInfo& layoutInfo =
-            descriptor->layout->GetBindingInfo();
-
-        const BindGroupLayoutBase::BindingMap& bindingMap = descriptor->layout->GetBindingMap();
-
-        if (descriptor->bindingCount != bindingMap.size()) {
+        if (descriptor->bindingCount != descriptor->layout->GetBindingCount()) {
             return DAWN_VALIDATION_ERROR("numBindings mismatch");
         }
 
+        const BindGroupLayoutBase::BindingMap& bindingMap = descriptor->layout->GetBindingMap();
+
         std::bitset<kMaxBindingsPerGroup> bindingsSet;
         for (uint32_t i = 0; i < descriptor->bindingCount; ++i) {
             const BindGroupBinding& binding = descriptor->bindings[i];
@@ -146,15 +139,18 @@
                 return DAWN_VALIDATION_ERROR("setting non-existent binding");
             }
             BindingIndex bindingIndex = it->second;
-            ASSERT(bindingIndex < layoutInfo.bindingCount);
+            ASSERT(bindingIndex < descriptor->layout->GetBindingCount());
 
             if (bindingsSet[bindingIndex]) {
                 return DAWN_VALIDATION_ERROR("binding set twice");
             }
             bindingsSet.set(bindingIndex);
 
+            const BindGroupLayoutBase::BindingInfo& bindingInfo =
+                descriptor->layout->GetBindingInfo(bindingIndex);
+
             // Perform binding-type specific validation.
-            switch (layoutInfo.types[bindingIndex]) {
+            switch (bindingInfo.type) {
                 case wgpu::BindingType::UniformBuffer:
                     DAWN_TRY(ValidateBufferBinding(device, binding, wgpu::BufferUsage::Uniform));
                     break;
@@ -163,12 +159,8 @@
                     DAWN_TRY(ValidateBufferBinding(device, binding, wgpu::BufferUsage::Storage));
                     break;
                 case wgpu::BindingType::SampledTexture:
-                    DAWN_TRY(
-                        ValidateTextureBinding(device, binding, wgpu::TextureUsage::Sampled,
-                                               layoutInfo.multisampled[bindingIndex],
-                                               layoutInfo.textureComponentTypes[bindingIndex],
-                                               layoutInfo.textureDimensions[bindingIndex],
-                                               layoutInfo.storageTextureFormats[bindingIndex]));
+                    DAWN_TRY(ValidateTextureBinding(device, binding, wgpu::TextureUsage::Sampled,
+                                                    bindingInfo));
                     break;
                 case wgpu::BindingType::Sampler:
                     DAWN_TRY(ValidateSamplerBinding(device, binding));
@@ -177,12 +169,8 @@
                 // write-only storage textures.
                 case wgpu::BindingType::ReadonlyStorageTexture:
                 case wgpu::BindingType::WriteonlyStorageTexture:
-                    DAWN_TRY(
-                        ValidateTextureBinding(device, binding, wgpu::TextureUsage::Storage,
-                                               layoutInfo.multisampled[bindingIndex],
-                                               layoutInfo.textureComponentTypes[bindingIndex],
-                                               layoutInfo.textureDimensions[bindingIndex],
-                                               layoutInfo.storageTextureFormats[bindingIndex]));
+                    DAWN_TRY(ValidateTextureBinding(device, binding, wgpu::TextureUsage::Storage,
+                                                    bindingInfo));
                     break;
                 case wgpu::BindingType::StorageTexture:
                     UNREACHABLE();
@@ -274,9 +262,9 @@
     BufferBinding BindGroupBase::GetBindingAsBufferBinding(BindingIndex bindingIndex) {
         ASSERT(!IsError());
         ASSERT(bindingIndex < mLayout->GetBindingCount());
-        ASSERT(mLayout->GetBindingInfo().types[bindingIndex] == wgpu::BindingType::UniformBuffer ||
-               mLayout->GetBindingInfo().types[bindingIndex] == wgpu::BindingType::StorageBuffer ||
-               mLayout->GetBindingInfo().types[bindingIndex] ==
+        ASSERT(mLayout->GetBindingInfo(bindingIndex).type == wgpu::BindingType::UniformBuffer ||
+               mLayout->GetBindingInfo(bindingIndex).type == wgpu::BindingType::StorageBuffer ||
+               mLayout->GetBindingInfo(bindingIndex).type ==
                    wgpu::BindingType::ReadonlyStorageBuffer);
         BufferBase* buffer = static_cast<BufferBase*>(mBindingData.bindings[bindingIndex].Get());
         return {buffer, mBindingData.bufferData[bindingIndex].offset,
@@ -286,14 +274,14 @@
     SamplerBase* BindGroupBase::GetBindingAsSampler(BindingIndex bindingIndex) {
         ASSERT(!IsError());
         ASSERT(bindingIndex < mLayout->GetBindingCount());
-        ASSERT(mLayout->GetBindingInfo().types[bindingIndex] == wgpu::BindingType::Sampler);
+        ASSERT(mLayout->GetBindingInfo(bindingIndex).type == wgpu::BindingType::Sampler);
         return static_cast<SamplerBase*>(mBindingData.bindings[bindingIndex].Get());
     }
 
     TextureViewBase* BindGroupBase::GetBindingAsTextureView(BindingIndex bindingIndex) {
         ASSERT(!IsError());
         ASSERT(bindingIndex < mLayout->GetBindingCount());
-        ASSERT(mLayout->GetBindingInfo().types[bindingIndex] == wgpu::BindingType::SampledTexture);
+        ASSERT(mLayout->GetBindingInfo(bindingIndex).type == wgpu::BindingType::SampledTexture);
         return static_cast<TextureViewBase*>(mBindingData.bindings[bindingIndex].Get());
     }
 
diff --git a/src/dawn_native/BindGroupAndStorageBarrierTracker.h b/src/dawn_native/BindGroupAndStorageBarrierTracker.h
index 975e96f..34bbe2e 100644
--- a/src/dawn_native/BindGroupAndStorageBarrierTracker.h
+++ b/src/dawn_native/BindGroupAndStorageBarrierTracker.h
@@ -38,16 +38,18 @@
                 mBuffersNeedingBarrier[index] = {};
 
                 const BindGroupLayoutBase* layout = bindGroup->GetLayout();
-                const BindGroupLayoutBase::LayoutBindingInfo& info = layout->GetBindingInfo();
 
-                for (BindingIndex bindingIndex = 0; bindingIndex < info.bindingCount;
+                for (BindingIndex bindingIndex = 0; bindingIndex < layout->GetBindingCount();
                      ++bindingIndex) {
-                    if ((info.visibilities[bindingIndex] & wgpu::ShaderStage::Compute) == 0) {
+                    const BindGroupLayoutBase::BindingInfo& bindingInfo =
+                        layout->GetBindingInfo(bindingIndex);
+
+                    if ((bindingInfo.visibility & wgpu::ShaderStage::Compute) == 0) {
                         continue;
                     }
 
-                    mBindingTypes[index][bindingIndex] = info.types[bindingIndex];
-                    switch (info.types[bindingIndex]) {
+                    mBindingTypes[index][bindingIndex] = bindingInfo.type;
+                    switch (bindingInfo.type) {
                         case wgpu::BindingType::UniformBuffer:
                         case wgpu::BindingType::ReadonlyStorageBuffer:
                         case wgpu::BindingType::Sampler:
diff --git a/src/dawn_native/BindGroupLayout.cpp b/src/dawn_native/BindGroupLayout.cpp
index 7556724..f6cc49b 100644
--- a/src/dawn_native/BindGroupLayout.cpp
+++ b/src/dawn_native/BindGroupLayout.cpp
@@ -167,39 +167,25 @@
         }
 
         return {};
-    }  // namespace dawn_native
+    }
 
     namespace {
-        size_t HashBindingInfo(const BindGroupLayoutBase::LayoutBindingInfo& info) {
-            size_t hash = 0;
-            HashCombine(&hash, info.hasDynamicOffset, info.multisampled);
 
-            for (BindingIndex i = 0; i < info.bindingCount; ++i) {
-                HashCombine(&hash, info.visibilities[i], info.types[i],
-                            info.textureComponentTypes[i], info.textureDimensions[i],
-                            info.storageTextureFormats[i]);
-            }
-
-            return hash;
+        void HashCombineBindingInfo(size_t* hash, const BindGroupLayoutBase::BindingInfo& info) {
+            HashCombine(hash, info.hasDynamicOffset, info.multisampled, info.visibility, info.type,
+                        info.textureComponentType, info.textureDimension,
+                        info.storageTextureFormat);
         }
 
-        bool operator==(const BindGroupLayoutBase::LayoutBindingInfo& a,
-                        const BindGroupLayoutBase::LayoutBindingInfo& b) {
-            if (a.bindingCount != b.bindingCount || a.hasDynamicOffset != b.hasDynamicOffset ||
-                a.multisampled != b.multisampled) {
-                return false;
-            }
-
-            for (BindingIndex i = 0; i < a.bindingCount; ++i) {
-                if ((a.visibilities[i] != b.visibilities[i]) || (a.types[i] != b.types[i]) ||
-                    (a.textureComponentTypes[i] != b.textureComponentTypes[i]) ||
-                    (a.textureDimensions[i] != b.textureDimensions[i]) ||
-                    (a.storageTextureFormats[i] != b.storageTextureFormats[i])) {
-                    return false;
-                }
-            }
-
-            return true;
+        bool operator!=(const BindGroupLayoutBase::BindingInfo& a,
+                        const BindGroupLayoutBase::BindingInfo& b) {
+            return a.hasDynamicOffset != b.hasDynamicOffset ||          //
+                   a.multisampled != b.multisampled ||                  //
+                   a.visibility != b.visibility ||                      //
+                   a.type != b.type ||                                  //
+                   a.textureComponentType != b.textureComponentType ||  //
+                   a.textureDimension != b.textureDimension ||          //
+                   a.storageTextureFormat != b.storageTextureFormat;
         }
 
         bool SortBindingsCompare(const BindGroupLayoutBinding& a, const BindGroupLayoutBinding& b) {
@@ -233,7 +219,8 @@
 
         // This is a utility function to help ASSERT that the BGL-binding comparator places buffers
         // first.
-        bool CheckBufferBindingsFirst(const BindGroupLayoutBinding* bindings, size_t count) {
+        bool CheckBufferBindingsFirst(const BindGroupLayoutBase::BindingInfo* bindings,
+                                      BindingIndex count) {
             ASSERT(count <= kMaxBindingsPerGroup);
 
             BindingIndex lastBufferIndex = 0;
@@ -269,21 +256,18 @@
 
     BindGroupLayoutBase::BindGroupLayoutBase(DeviceBase* device,
                                              const BindGroupLayoutDescriptor* descriptor)
-        : CachedObject(device) {
-        mBindingInfo.bindingCount = descriptor->bindingCount;
-
+        : CachedObject(device), mBindingCount(descriptor->bindingCount) {
         std::vector<BindGroupLayoutBinding> sortedBindings(
             descriptor->bindings, descriptor->bindings + descriptor->bindingCount);
 
         std::sort(sortedBindings.begin(), sortedBindings.end(), SortBindingsCompare);
-        ASSERT(CheckBufferBindingsFirst(sortedBindings.data(), sortedBindings.size()));
 
-        for (BindingIndex i = 0; i < mBindingInfo.bindingCount; ++i) {
+        for (BindingIndex i = 0; i < mBindingCount; ++i) {
             const BindGroupLayoutBinding& binding = sortedBindings[i];
-            mBindingInfo.types[i] = binding.type;
-            mBindingInfo.visibilities[i] = binding.visibility;
-            mBindingInfo.textureComponentTypes[i] = binding.textureComponentType;
-            mBindingInfo.storageTextureFormats[i] = binding.storageTextureFormat;
+            mBindingInfo[i].type = binding.type;
+            mBindingInfo[i].visibility = binding.visibility;
+            mBindingInfo[i].textureComponentType = binding.textureComponentType;
+            mBindingInfo[i].storageTextureFormat = binding.storageTextureFormat;
 
             switch (binding.type) {
                 case wgpu::BindingType::UniformBuffer:
@@ -298,13 +282,14 @@
             }
 
             if (binding.textureDimension == wgpu::TextureViewDimension::Undefined) {
-                mBindingInfo.textureDimensions[i] = wgpu::TextureViewDimension::e2D;
+                mBindingInfo[i].textureDimension = wgpu::TextureViewDimension::e2D;
             } else {
-                mBindingInfo.textureDimensions[i] = binding.textureDimension;
+                mBindingInfo[i].textureDimension = binding.textureDimension;
             }
 
+            mBindingInfo[i].multisampled = binding.multisampled;
+            mBindingInfo[i].hasDynamicOffset = binding.hasDynamicOffset;
             if (binding.hasDynamicOffset) {
-                mBindingInfo.hasDynamicOffset.set(i);
                 switch (binding.type) {
                     case wgpu::BindingType::UniformBuffer:
                         ++mDynamicUniformBufferCount;
@@ -322,11 +307,11 @@
                         break;
                 }
             }
-            mBindingInfo.multisampled.set(i, binding.multisampled);
 
             const auto& it = mBindingMap.emplace(BindingNumber(binding.binding), i);
             ASSERT(it.second);
         }
+        ASSERT(CheckBufferBindingsFirst(mBindingInfo.data(), mBindingCount));
     }
 
     BindGroupLayoutBase::BindGroupLayoutBase(DeviceBase* device, ObjectBase::ErrorTag tag)
@@ -345,11 +330,6 @@
         return new BindGroupLayoutBase(device, ObjectBase::kError);
     }
 
-    const BindGroupLayoutBase::LayoutBindingInfo& BindGroupLayoutBase::GetBindingInfo() const {
-        ASSERT(!IsError());
-        return mBindingInfo;
-    }
-
     const BindGroupLayoutBase::BindingMap& BindGroupLayoutBase::GetBindingMap() const {
         ASSERT(!IsError());
         return mBindingMap;
@@ -363,22 +343,31 @@
     }
 
     size_t BindGroupLayoutBase::HashFunc::operator()(const BindGroupLayoutBase* bgl) const {
-        size_t hash = HashBindingInfo(bgl->mBindingInfo);
+        size_t hash = 0;
         // std::map is sorted by key, so two BGLs constructed in different orders
         // will still hash the same.
         for (const auto& it : bgl->mBindingMap) {
             HashCombine(&hash, it.first, it.second);
+            HashCombineBindingInfo(&hash, bgl->mBindingInfo[it.second]);
         }
         return hash;
     }
 
     bool BindGroupLayoutBase::EqualityFunc::operator()(const BindGroupLayoutBase* a,
                                                        const BindGroupLayoutBase* b) const {
-        return a->mBindingInfo == b->mBindingInfo && a->mBindingMap == b->mBindingMap;
+        if (a->GetBindingCount() != b->GetBindingCount()) {
+            return false;
+        }
+        for (BindingIndex i = 0; i < a->GetBindingCount(); ++i) {
+            if (a->mBindingInfo[i] != b->mBindingInfo[i]) {
+                return false;
+            }
+        }
+        return a->mBindingMap == b->mBindingMap;
     }
 
     BindingIndex BindGroupLayoutBase::GetBindingCount() const {
-        return mBindingInfo.bindingCount;
+        return mBindingCount;
     }
 
     BindingIndex BindGroupLayoutBase::GetDynamicBufferCount() const {
@@ -398,7 +387,7 @@
         // | --- offsets + sizes -------------| --------------- Ref<ObjectBase> ----------|
         size_t objectPointerStart = mBufferCount * sizeof(BufferBindingData);
         ASSERT(IsAligned(objectPointerStart, alignof(Ref<ObjectBase>)));
-        return objectPointerStart + mBindingInfo.bindingCount * sizeof(Ref<ObjectBase>);
+        return objectPointerStart + mBindingCount * sizeof(Ref<ObjectBase>);
     }
 
     BindGroupLayoutBase::BindingDataPointers BindGroupLayoutBase::ComputeBindingDataPointers(
diff --git a/src/dawn_native/BindGroupLayout.h b/src/dawn_native/BindGroupLayout.h
index c110f6a..abe50f4 100644
--- a/src/dawn_native/BindGroupLayout.h
+++ b/src/dawn_native/BindGroupLayout.h
@@ -51,22 +51,24 @@
         ~BindGroupLayoutBase() override;
 
         static BindGroupLayoutBase* MakeError(DeviceBase* device);
-
-        struct LayoutBindingInfo {
-            std::array<wgpu::ShaderStage, kMaxBindingsPerGroup> visibilities;
-            std::array<wgpu::BindingType, kMaxBindingsPerGroup> types;
-            std::array<wgpu::TextureComponentType, kMaxBindingsPerGroup> textureComponentTypes;
-            std::array<wgpu::TextureViewDimension, kMaxBindingsPerGroup> textureDimensions;
-            std::array<wgpu::TextureFormat, kMaxBindingsPerGroup> storageTextureFormats;
-            std::bitset<kMaxBindingsPerGroup> hasDynamicOffset;
-            std::bitset<kMaxBindingsPerGroup> multisampled;
-            BindingIndex bindingCount;
+        struct BindingInfo {
+            wgpu::ShaderStage visibility;
+            wgpu::BindingType type;
+            wgpu::TextureComponentType textureComponentType;
+            wgpu::TextureViewDimension textureDimension;
+            wgpu::TextureFormat storageTextureFormat;
+            bool hasDynamicOffset;
+            bool multisampled;
         };
 
         // A map from the BindingNumber to its packed BindingIndex.
         using BindingMap = std::map<BindingNumber, BindingIndex>;
 
-        const LayoutBindingInfo& GetBindingInfo() const;
+        const BindingInfo& GetBindingInfo(BindingIndex bindingIndex) const {
+            ASSERT(!IsError());
+            ASSERT(bindingIndex < kMaxBindingsPerGroup);
+            return mBindingInfo[bindingIndex];
+        }
         const BindingMap& GetBindingMap() const;
         BindingIndex GetBindingIndex(BindingNumber bindingNumber) const;
 
@@ -117,10 +119,12 @@
       private:
         BindGroupLayoutBase(DeviceBase* device, ObjectBase::ErrorTag tag);
 
+        BindingIndex mBindingCount;
         BindingIndex mBufferCount = 0;  // |BindingIndex| because buffers are packed at the front.
         uint32_t mDynamicUniformBufferCount = 0;
         uint32_t mDynamicStorageBufferCount = 0;
-        LayoutBindingInfo mBindingInfo;
+
+        std::array<BindingInfo, kMaxBindingsPerGroup> mBindingInfo;
 
         // Map from BindGroupLayoutEntry.binding to packed indices.
         BindingMap mBindingMap;
diff --git a/src/dawn_native/ProgrammablePassEncoder.cpp b/src/dawn_native/ProgrammablePassEncoder.cpp
index fd03d70..cc17a72 100644
--- a/src/dawn_native/ProgrammablePassEncoder.cpp
+++ b/src/dawn_native/ProgrammablePassEncoder.cpp
@@ -29,11 +29,9 @@
     namespace {
         void TrackBindGroupResourceUsage(PassResourceUsageTracker* usageTracker,
                                          BindGroupBase* group) {
-            const BindGroupLayoutBase::LayoutBindingInfo& layoutInfo =
-                group->GetLayout()->GetBindingInfo();
-            for (BindingIndex bindingIndex = 0; bindingIndex < layoutInfo.bindingCount;
-                 ++bindingIndex) {
-                wgpu::BindingType type = layoutInfo.types[bindingIndex];
+            for (BindingIndex bindingIndex = 0;
+                 bindingIndex < group->GetLayout()->GetBindingCount(); ++bindingIndex) {
+                wgpu::BindingType type = group->GetLayout()->GetBindingInfo(bindingIndex).type;
 
                 switch (type) {
                     case wgpu::BindingType::UniformBuffer: {
@@ -133,12 +131,13 @@
                     return DAWN_VALIDATION_ERROR("dynamicOffset count mismatch");
                 }
 
-                const BindGroupLayoutBase::LayoutBindingInfo& layoutInfo = layout->GetBindingInfo();
                 for (BindingIndex i = 0; i < dynamicOffsetCount; ++i) {
+                    const BindGroupLayoutBase::BindingInfo& bindingInfo = layout->GetBindingInfo(i);
+
                     // BGL creation sorts bindings such that the dynamic buffer bindings are first.
                     // ASSERT that this true.
-                    ASSERT(layoutInfo.hasDynamicOffset[i]);
-                    switch (layoutInfo.types[i]) {
+                    ASSERT(bindingInfo.hasDynamicOffset);
+                    switch (bindingInfo.type) {
                         case wgpu::BindingType::UniformBuffer:
                         case wgpu::BindingType::StorageBuffer:
                         case wgpu::BindingType::ReadonlyStorageBuffer:
diff --git a/src/dawn_native/ShaderModule.cpp b/src/dawn_native/ShaderModule.cpp
index 1a7c6b5..39324b8 100644
--- a/src/dawn_native/ShaderModule.cpp
+++ b/src/dawn_native/ShaderModule.cpp
@@ -741,7 +741,6 @@
         const BindGroupLayoutBase* layout) const {
         ASSERT(!IsError());
 
-        const BindGroupLayoutBase::LayoutBindingInfo& layoutInfo = layout->GetBindingInfo();
         const BindGroupLayoutBase::BindingMap& bindingMap = layout->GetBindingMap();
 
         // Iterate over all bindings used by this group in the shader, and find the
@@ -756,7 +755,9 @@
             }
             BindingIndex bindingIndex(bindingIt->second);
 
-            const auto& layoutBindingType = layoutInfo.types[bindingIndex];
+            const BindGroupLayoutBase::BindingInfo& bindingInfo =
+                layout->GetBindingInfo(bindingIndex);
+            const auto& layoutBindingType = bindingInfo.type;
 
             if (layoutBindingType != moduleInfo.type) {
                 // Binding mismatch between shader and bind group is invalid. For example, a
@@ -771,34 +772,31 @@
                 }
             }
 
-            if ((layoutInfo.visibilities[bindingIndex] & StageBit(mExecutionModel)) == 0) {
+            if ((bindingInfo.visibility & StageBit(mExecutionModel)) == 0) {
                 return false;
             }
 
             switch (layoutBindingType) {
                 case wgpu::BindingType::SampledTexture: {
                     Format::Type layoutTextureComponentType =
-                        Format::TextureComponentTypeToFormatType(
-                            layoutInfo.textureComponentTypes[bindingIndex]);
+                        Format::TextureComponentTypeToFormatType(bindingInfo.textureComponentType);
                     if (layoutTextureComponentType != moduleInfo.textureComponentType) {
                         return false;
                     }
 
-                    if (layoutInfo.textureDimensions[bindingIndex] != moduleInfo.textureDimension) {
+                    if (bindingInfo.textureDimension != moduleInfo.textureDimension) {
                         return false;
                     }
                 } break;
 
                 case wgpu::BindingType::ReadonlyStorageTexture:
                 case wgpu::BindingType::WriteonlyStorageTexture: {
-                    ASSERT(layoutInfo.storageTextureFormats[bindingIndex] !=
-                           wgpu::TextureFormat::Undefined);
+                    ASSERT(bindingInfo.storageTextureFormat != wgpu::TextureFormat::Undefined);
                     ASSERT(moduleInfo.storageTextureFormat != wgpu::TextureFormat::Undefined);
-                    if (layoutInfo.storageTextureFormats[bindingIndex] !=
-                        moduleInfo.storageTextureFormat) {
+                    if (bindingInfo.storageTextureFormat != moduleInfo.storageTextureFormat) {
                         return false;
                     }
-                    if (layoutInfo.textureDimensions[bindingIndex] != moduleInfo.textureDimension) {
+                    if (bindingInfo.textureDimension != moduleInfo.textureDimension) {
                         return false;
                     }
                 } break;
diff --git a/src/dawn_native/d3d12/BindGroupD3D12.cpp b/src/dawn_native/d3d12/BindGroupD3D12.cpp
index 6ef4bc2..92a1445 100644
--- a/src/dawn_native/d3d12/BindGroupD3D12.cpp
+++ b/src/dawn_native/d3d12/BindGroupD3D12.cpp
@@ -81,21 +81,21 @@
         mLastUsageSerial = pendingSerial;
         mHeapSerial = allocator->GetShaderVisibleHeapsSerial();
 
-        const BindGroupLayoutBase::LayoutBindingInfo& layout = bgl->GetBindingInfo();
-
         const auto& bindingOffsets = bgl->GetBindingOffsets();
 
         ID3D12Device* d3d12Device = device->GetD3D12Device().Get();
 
-        for (BindingIndex bindingIndex = 0; bindingIndex < layout.bindingCount; ++bindingIndex) {
+        for (BindingIndex bindingIndex = 0; bindingIndex < bgl->GetBindingCount(); ++bindingIndex) {
+            const BindGroupLayoutBase::BindingInfo& bindingInfo = bgl->GetBindingInfo(bindingIndex);
+
             // It's not necessary to create descriptors in descriptor heap for dynamic
             // resources. So skip allocating descriptors in descriptor heaps for dynamic
             // buffers.
-            if (layout.hasDynamicOffset[bindingIndex]) {
+            if (bindingInfo.hasDynamicOffset) {
                 continue;
             }
 
-            switch (layout.types[bindingIndex]) {
+            switch (bindingInfo.type) {
                 case wgpu::BindingType::UniformBuffer: {
                     BufferBinding binding = GetBindingAsBufferBinding(bindingIndex);
 
diff --git a/src/dawn_native/d3d12/BindGroupLayoutD3D12.cpp b/src/dawn_native/d3d12/BindGroupLayoutD3D12.cpp
index 50a4b03..7d07611 100644
--- a/src/dawn_native/d3d12/BindGroupLayoutD3D12.cpp
+++ b/src/dawn_native/d3d12/BindGroupLayoutD3D12.cpp
@@ -45,18 +45,17 @@
         : BindGroupLayoutBase(device, descriptor),
           mDescriptorCounts{},
           mBindGroupAllocator(MakeFrontendBindGroupAllocator<BindGroup>(4096)) {
-        const BindGroupLayoutBase::LayoutBindingInfo& groupInfo = GetBindingInfo();
+        for (BindingIndex bindingIndex = GetDynamicBufferCount(); bindingIndex < GetBindingCount();
+             ++bindingIndex) {
+            const BindGroupLayoutBase::BindingInfo& bindingInfo = GetBindingInfo(bindingIndex);
 
-        for (BindingIndex bindingIndex = 0; bindingIndex < groupInfo.bindingCount; ++bindingIndex) {
             // For dynamic resources, Dawn uses root descriptor in D3D12 backend.
-            // So there is no need to allocate the descriptor from descriptor heap. Skip counting
-            // dynamic resources for calculating size of descriptor heap.
-            if (groupInfo.hasDynamicOffset[bindingIndex]) {
-                continue;
-            }
+            // So there is no need to allocate the descriptor from descriptor heap.
+            // This loop starts after the dynamic buffer indices to skip counting
+            // dynamic resources in calculating the size of the descriptor heap.
+            ASSERT(!bindingInfo.hasDynamicOffset);
 
-            DescriptorType descriptorType =
-                WGPUBindingTypeToDescriptorType(groupInfo.types[bindingIndex]);
+            DescriptorType descriptorType = WGPUBindingTypeToDescriptorType(bindingInfo.type);
             mBindingOffsets[bindingIndex] = mDescriptorCounts[descriptorType]++;
         }
 
@@ -101,12 +100,14 @@
                            D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER);
         descriptorOffsets[Sampler] = 0;
 
-        for (BindingIndex bindingIndex = 0; bindingIndex < groupInfo.bindingCount; ++bindingIndex) {
-            if (groupInfo.hasDynamicOffset[bindingIndex]) {
+        for (BindingIndex bindingIndex = 0; bindingIndex < GetBindingCount(); ++bindingIndex) {
+            const BindGroupLayoutBase::BindingInfo& bindingInfo = GetBindingInfo(bindingIndex);
+
+            if (bindingInfo.hasDynamicOffset) {
                 // Dawn is using values in mBindingOffsets to decide register number in HLSL.
                 // Root descriptor needs to set this value to set correct register number in
                 // generated HLSL shader.
-                switch (groupInfo.types[bindingIndex]) {
+                switch (bindingInfo.type) {
                     case wgpu::BindingType::UniformBuffer:
                     case wgpu::BindingType::StorageBuffer:
                     case wgpu::BindingType::ReadonlyStorageBuffer:
@@ -124,8 +125,7 @@
             }
 
             // TODO(shaobo.yan@intel.com): Implement dynamic buffer offset.
-            DescriptorType descriptorType =
-                WGPUBindingTypeToDescriptorType(groupInfo.types[bindingIndex]);
+            DescriptorType descriptorType = WGPUBindingTypeToDescriptorType(bindingInfo.type);
             mBindingOffsets[bindingIndex] += descriptorOffsets[descriptorType];
         }
     }
diff --git a/src/dawn_native/d3d12/CommandBufferD3D12.cpp b/src/dawn_native/d3d12/CommandBufferD3D12.cpp
index 385cbda..646a96e 100644
--- a/src/dawn_native/d3d12/CommandBufferD3D12.cpp
+++ b/src/dawn_native/d3d12/CommandBufferD3D12.cpp
@@ -196,28 +196,27 @@
                             BindGroup* group,
                             uint32_t dynamicOffsetCount,
                             const uint64_t* dynamicOffsets) {
+            ASSERT(dynamicOffsetCount == group->GetLayout()->GetDynamicBufferCount());
+
             // Usually, the application won't set the same offsets many times,
             // so always try to apply dynamic offsets even if the offsets stay the same
-            if (dynamicOffsetCount) {
-                // Update dynamic offsets
-                const BindGroupLayout::LayoutBindingInfo& layout =
-                    group->GetLayout()->GetBindingInfo();
-                uint32_t currentDynamicBufferIndex = 0;
-
-                for (uint32_t bindingIndex : IterateBitSet(layout.hasDynamicOffset)) {
-                    ASSERT(dynamicOffsetCount > 0);
+            if (dynamicOffsetCount != 0) {
+                // Update dynamic offsets.
+                // Dynamic buffer bindings are packed at the beginning of the layout.
+                for (BindingIndex bindingIndex = 0; bindingIndex < dynamicOffsetCount;
+                     ++bindingIndex) {
                     uint32_t parameterIndex =
                         pipelineLayout->GetDynamicRootParameterIndex(index, bindingIndex);
                     BufferBinding binding = group->GetBindingAsBufferBinding(bindingIndex);
 
                     // Calculate buffer locations that root descriptors links to. The location
                     // is (base buffer location + initial offset + dynamic offset)
-                    uint64_t dynamicOffset = dynamicOffsets[currentDynamicBufferIndex];
+                    uint64_t dynamicOffset = dynamicOffsets[bindingIndex];
                     uint64_t offset = binding.offset + dynamicOffset;
                     D3D12_GPU_VIRTUAL_ADDRESS bufferLocation =
                         ToBackend(binding.buffer)->GetVA() + offset;
 
-                    switch (layout.types[bindingIndex]) {
+                    switch (group->GetLayout()->GetBindingInfo(bindingIndex).type) {
                         case wgpu::BindingType::UniformBuffer:
                             if (mInCompute) {
                                 commandList->SetComputeRootConstantBufferView(parameterIndex,
@@ -253,8 +252,6 @@
                             UNREACHABLE();
                             break;
                     }
-
-                    ++currentDynamicBufferIndex;
                 }
             }
 
diff --git a/src/dawn_native/d3d12/PipelineLayoutD3D12.cpp b/src/dawn_native/d3d12/PipelineLayoutD3D12.cpp
index 7afadc7..14e71f5 100644
--- a/src/dawn_native/d3d12/PipelineLayoutD3D12.cpp
+++ b/src/dawn_native/d3d12/PipelineLayoutD3D12.cpp
@@ -89,7 +89,6 @@
 
         for (uint32_t group : IterateBitSet(GetBindGroupLayoutsMask())) {
             const BindGroupLayout* bindGroupLayout = ToBackend(GetBindGroupLayout(group));
-            const BindGroupLayout::LayoutBindingInfo& groupInfo = bindGroupLayout->GetBindingInfo();
 
             // Set the root descriptor table parameter and copy ranges. Ranges are offset by the
             // bind group index Returns whether or not the parameter was set. A root parameter is
@@ -129,25 +128,30 @@
             // Get calculated shader register for root descriptors
             const auto& shaderRegisters = bindGroupLayout->GetBindingOffsets();
 
-            // Init root descriptors in root signatures.
-            for (uint32_t dynamicBinding : IterateBitSet(groupInfo.hasDynamicOffset)) {
+            // Init root descriptors in root signatures for dynamic buffer bindings.
+            // These are packed at the beginning of the layout binding info.
+            for (BindingIndex dynamicBindingIndex = 0;
+                 dynamicBindingIndex < bindGroupLayout->GetDynamicBufferCount();
+                 ++dynamicBindingIndex) {
+                const BindGroupLayoutBase::BindingInfo& bindingInfo =
+                    bindGroupLayout->GetBindingInfo(dynamicBindingIndex);
+
                 D3D12_ROOT_PARAMETER* rootParameter = &rootParameters[parameterIndex];
 
                 // Setup root descriptor.
                 D3D12_ROOT_DESCRIPTOR rootDescriptor;
-                rootDescriptor.ShaderRegister = shaderRegisters[dynamicBinding];
+                rootDescriptor.ShaderRegister = shaderRegisters[dynamicBindingIndex];
                 rootDescriptor.RegisterSpace = group;
 
                 // Set root descriptors in root signatures.
                 rootParameter->Descriptor = rootDescriptor;
-                mDynamicRootParameterIndices[group][dynamicBinding] = parameterIndex++;
+                mDynamicRootParameterIndices[group][dynamicBindingIndex] = parameterIndex++;
 
                 // Set parameter types according to bind group layout descriptor.
-                rootParameter->ParameterType = RootParameterType(groupInfo.types[dynamicBinding]);
+                rootParameter->ParameterType = RootParameterType(bindingInfo.type);
 
                 // Set visibilities according to bind group layout descriptor.
-                rootParameter->ShaderVisibility =
-                    ShaderVisibilityType(groupInfo.visibilities[dynamicBinding]);
+                rootParameter->ShaderVisibility = ShaderVisibilityType(bindingInfo.visibility);
             }
         }
 
@@ -186,10 +190,11 @@
         return mRootSignature;
     }
 
-    uint32_t PipelineLayout::GetDynamicRootParameterIndex(uint32_t group, uint32_t binding) const {
+    uint32_t PipelineLayout::GetDynamicRootParameterIndex(uint32_t group,
+                                                          BindingIndex bindingIndex) const {
         ASSERT(group < kMaxBindGroups);
-        ASSERT(binding < kMaxBindingsPerGroup);
-        ASSERT(GetBindGroupLayout(group)->GetBindingInfo().hasDynamicOffset[binding]);
-        return mDynamicRootParameterIndices[group][binding];
+        ASSERT(bindingIndex < kMaxBindingsPerGroup);
+        ASSERT(GetBindGroupLayout(group)->GetBindingInfo(bindingIndex).hasDynamicOffset);
+        return mDynamicRootParameterIndices[group][bindingIndex];
     }
 }}  // namespace dawn_native::d3d12
diff --git a/src/dawn_native/d3d12/PipelineLayoutD3D12.h b/src/dawn_native/d3d12/PipelineLayoutD3D12.h
index 1d6c7e5..bbe3659 100644
--- a/src/dawn_native/d3d12/PipelineLayoutD3D12.h
+++ b/src/dawn_native/d3d12/PipelineLayoutD3D12.h
@@ -15,8 +15,8 @@
 #ifndef DAWNNATIVE_D3D12_PIPELINELAYOUTD3D12_H_
 #define DAWNNATIVE_D3D12_PIPELINELAYOUTD3D12_H_
 
+#include "dawn_native/IntegerTypes.h"
 #include "dawn_native/PipelineLayout.h"
-
 #include "dawn_native/d3d12/d3d12_platform.h"
 
 namespace dawn_native { namespace d3d12 {
@@ -32,7 +32,7 @@
         uint32_t GetSamplerRootParameterIndex(uint32_t group) const;
 
         // Returns the index of the root parameter reserved for a dynamic buffer binding
-        uint32_t GetDynamicRootParameterIndex(uint32_t group, uint32_t binding) const;
+        uint32_t GetDynamicRootParameterIndex(uint32_t group, BindingIndex bindingIndex) const;
 
         ComPtr<ID3D12RootSignature> GetRootSignature() const;
 
diff --git a/src/dawn_native/metal/CommandBufferMTL.mm b/src/dawn_native/metal/CommandBufferMTL.mm
index 5d325c7..1f5b84f 100644
--- a/src/dawn_native/metal/CommandBufferMTL.mm
+++ b/src/dawn_native/metal/CommandBufferMTL.mm
@@ -493,19 +493,22 @@
                                     uint32_t dynamicOffsetCount,
                                     uint64_t* dynamicOffsets,
                                     PipelineLayout* pipelineLayout) {
-                const BindGroupLayoutBase::LayoutBindingInfo& layout =
-                    group->GetLayout()->GetBindingInfo();
                 uint32_t currentDynamicBufferIndex = 0;
 
                 // TODO(kainino@chromium.org): Maintain buffers and offsets arrays in BindGroup
                 // so that we only have to do one setVertexBuffers and one setFragmentBuffers
                 // call here.
-                for (BindingIndex bindingIndex = 0; bindingIndex < layout.bindingCount;
-                     ++bindingIndex) {
-                    auto stage = layout.visibilities[bindingIndex];
-                    bool hasVertStage = stage & wgpu::ShaderStage::Vertex && render != nil;
-                    bool hasFragStage = stage & wgpu::ShaderStage::Fragment && render != nil;
-                    bool hasComputeStage = stage & wgpu::ShaderStage::Compute && compute != nil;
+                for (BindingIndex bindingIndex = 0;
+                     bindingIndex < group->GetLayout()->GetBindingCount(); ++bindingIndex) {
+                    const BindGroupLayoutBase::BindingInfo& bindingInfo =
+                        group->GetLayout()->GetBindingInfo(bindingIndex);
+
+                    bool hasVertStage =
+                        bindingInfo.visibility & wgpu::ShaderStage::Vertex && render != nil;
+                    bool hasFragStage =
+                        bindingInfo.visibility & wgpu::ShaderStage::Fragment && render != nil;
+                    bool hasComputeStage =
+                        bindingInfo.visibility & wgpu::ShaderStage::Compute && compute != nil;
 
                     uint32_t vertIndex = 0;
                     uint32_t fragIndex = 0;
@@ -524,7 +527,7 @@
                             SingleShaderStage::Compute)[index][bindingIndex];
                     }
 
-                    switch (layout.types[bindingIndex]) {
+                    switch (bindingInfo.type) {
                         case wgpu::BindingType::UniformBuffer:
                         case wgpu::BindingType::StorageBuffer:
                         case wgpu::BindingType::ReadonlyStorageBuffer: {
@@ -535,7 +538,7 @@
 
                             // TODO(shaobo.yan@intel.com): Record bound buffer status to use
                             // setBufferOffset to achieve better performance.
-                            if (layout.hasDynamicOffset[bindingIndex]) {
+                            if (bindingInfo.hasDynamicOffset) {
                                 offset += dynamicOffsets[currentDynamicBufferIndex];
                                 currentDynamicBufferIndex++;
                             }
diff --git a/src/dawn_native/metal/PipelineLayoutMTL.mm b/src/dawn_native/metal/PipelineLayoutMTL.mm
index 4e41ef6..0dc1b52 100644
--- a/src/dawn_native/metal/PipelineLayoutMTL.mm
+++ b/src/dawn_native/metal/PipelineLayoutMTL.mm
@@ -29,15 +29,15 @@
             uint32_t textureIndex = 0;
 
             for (uint32_t group : IterateBitSet(GetBindGroupLayoutsMask())) {
-                const BindGroupLayoutBase::LayoutBindingInfo& groupInfo =
-                    GetBindGroupLayout(group)->GetBindingInfo();
-                for (BindingIndex bindingIndex = 0; bindingIndex < groupInfo.bindingCount;
-                     ++bindingIndex) {
-                    if (!(groupInfo.visibilities[bindingIndex] & StageBit(stage))) {
+                for (BindingIndex bindingIndex = 0;
+                     bindingIndex < GetBindGroupLayout(group)->GetBindingCount(); ++bindingIndex) {
+                    const BindGroupLayoutBase::BindingInfo& bindingInfo =
+                        GetBindGroupLayout(group)->GetBindingInfo(bindingIndex);
+                    if (!(bindingInfo.visibility & StageBit(stage))) {
                         continue;
                     }
 
-                    switch (groupInfo.types[bindingIndex]) {
+                    switch (bindingInfo.type) {
                         case wgpu::BindingType::UniformBuffer:
                         case wgpu::BindingType::StorageBuffer:
                         case wgpu::BindingType::ReadonlyStorageBuffer:
diff --git a/src/dawn_native/metal/ShaderModuleMTL.mm b/src/dawn_native/metal/ShaderModuleMTL.mm
index 15f4cc2..aa0ca02 100644
--- a/src/dawn_native/metal/ShaderModuleMTL.mm
+++ b/src/dawn_native/metal/ShaderModuleMTL.mm
@@ -131,22 +131,23 @@
 
         // Create one resource binding entry per stage per binding.
         for (uint32_t group : IterateBitSet(layout->GetBindGroupLayoutsMask())) {
-            const BindGroupLayoutBase::LayoutBindingInfo& bgInfo =
-                layout->GetBindGroupLayout(group)->GetBindingInfo();
             const BindGroupLayoutBase::BindingMap& bindingMap =
                 layout->GetBindGroupLayout(group)->GetBindingMap();
 
             for (const auto& it : bindingMap) {
-                uint32_t binding = it.first;
+                BindingNumber bindingNumber = it.first;
                 BindingIndex bindingIndex = it.second;
 
-                for (auto stage : IterateStages(bgInfo.visibilities[bindingIndex])) {
+                const BindGroupLayoutBase::BindingInfo& bindingInfo =
+                    layout->GetBindGroupLayout(group)->GetBindingInfo(bindingIndex);
+
+                for (auto stage : IterateStages(bindingInfo.visibility)) {
                     uint32_t shaderIndex = layout->GetBindingIndexInfo(stage)[group][bindingIndex];
                     if (GetDevice()->IsToggleEnabled(Toggle::UseSpvc)) {
                         shaderc_spvc_msl_resource_binding mslBinding;
                         mslBinding.stage = ToSpvcExecutionModel(stage);
                         mslBinding.desc_set = group;
-                        mslBinding.binding = binding;
+                        mslBinding.binding = bindingNumber;
                         mslBinding.msl_buffer = mslBinding.msl_texture = mslBinding.msl_sampler =
                             shaderIndex;
                         DAWN_TRY(CheckSpvcSuccess(mSpvcContext.AddMSLResourceBinding(mslBinding),
@@ -155,7 +156,7 @@
                         spirv_cross::MSLResourceBinding mslBinding;
                         mslBinding.stage = SpirvExecutionModelForStage(stage);
                         mslBinding.desc_set = group;
-                        mslBinding.binding = binding;
+                        mslBinding.binding = bindingNumber;
                         mslBinding.msl_buffer = mslBinding.msl_texture = mslBinding.msl_sampler =
                             shaderIndex;
 
diff --git a/src/dawn_native/opengl/CommandBufferGL.cpp b/src/dawn_native/opengl/CommandBufferGL.cpp
index f772162..a43281d 100644
--- a/src/dawn_native/opengl/CommandBufferGL.cpp
+++ b/src/dawn_native/opengl/CommandBufferGL.cpp
@@ -239,28 +239,45 @@
                                 uint32_t dynamicOffsetCount,
                                 uint64_t* dynamicOffsets) {
                 const auto& indices = ToBackend(mPipelineLayout)->GetBindingIndexInfo()[index];
-                const BindGroupLayoutBase::LayoutBindingInfo& layout =
-                    group->GetLayout()->GetBindingInfo();
-                uint32_t currentDynamicIndex = 0;
+                uint32_t currentDynamicOffsetIndex = 0;
 
-                for (BindingIndex bindingIndex = 0; bindingIndex < layout.bindingCount;
-                     ++bindingIndex) {
-                    switch (layout.types[bindingIndex]) {
+                for (BindingIndex bindingIndex = 0;
+                     bindingIndex < group->GetLayout()->GetBindingCount(); ++bindingIndex) {
+                    const BindGroupLayoutBase::BindingInfo& bindingInfo =
+                        group->GetLayout()->GetBindingInfo(bindingIndex);
+
+                    switch (bindingInfo.type) {
                         case wgpu::BindingType::UniformBuffer: {
                             BufferBinding binding = group->GetBindingAsBufferBinding(bindingIndex);
                             GLuint buffer = ToBackend(binding.buffer)->GetHandle();
                             GLuint uboIndex = indices[bindingIndex];
                             GLuint offset = binding.offset;
 
-                            if (layout.hasDynamicOffset[bindingIndex]) {
-                                offset += dynamicOffsets[currentDynamicIndex];
-                                ++currentDynamicIndex;
+                            if (bindingInfo.hasDynamicOffset) {
+                                offset += dynamicOffsets[currentDynamicOffsetIndex];
+                                ++currentDynamicOffsetIndex;
                             }
 
                             gl.BindBufferRange(GL_UNIFORM_BUFFER, uboIndex, buffer, offset,
                                                binding.size);
                         } break;
 
+                        case wgpu::BindingType::StorageBuffer:
+                        case wgpu::BindingType::ReadonlyStorageBuffer: {
+                            BufferBinding binding = group->GetBindingAsBufferBinding(bindingIndex);
+                            GLuint buffer = ToBackend(binding.buffer)->GetHandle();
+                            GLuint ssboIndex = indices[bindingIndex];
+                            GLuint offset = binding.offset;
+
+                            if (bindingInfo.hasDynamicOffset) {
+                                offset += dynamicOffsets[currentDynamicOffsetIndex];
+                                ++currentDynamicOffsetIndex;
+                            }
+
+                            gl.BindBufferRange(GL_SHADER_STORAGE_BUFFER, ssboIndex, buffer, offset,
+                                               binding.size);
+                        } break;
+
                         case wgpu::BindingType::Sampler: {
                             Sampler* sampler = ToBackend(group->GetBindingAsSampler(bindingIndex));
                             GLuint samplerIndex = indices[bindingIndex];
@@ -290,22 +307,6 @@
                             }
                         } break;
 
-                        case wgpu::BindingType::StorageBuffer:
-                        case wgpu::BindingType::ReadonlyStorageBuffer: {
-                            BufferBinding binding = group->GetBindingAsBufferBinding(bindingIndex);
-                            GLuint buffer = ToBackend(binding.buffer)->GetHandle();
-                            GLuint ssboIndex = indices[bindingIndex];
-                            GLuint offset = binding.offset;
-
-                            if (layout.hasDynamicOffset[bindingIndex]) {
-                                offset += dynamicOffsets[currentDynamicIndex];
-                                ++currentDynamicIndex;
-                            }
-
-                            gl.BindBufferRange(GL_SHADER_STORAGE_BUFFER, ssboIndex, buffer, offset,
-                                               binding.size);
-                        } break;
-
                         case wgpu::BindingType::StorageTexture:
                         case wgpu::BindingType::ReadonlyStorageTexture:
                         case wgpu::BindingType::WriteonlyStorageTexture:
diff --git a/src/dawn_native/opengl/PipelineGL.cpp b/src/dawn_native/opengl/PipelineGL.cpp
index eeda875..6539dec 100644
--- a/src/dawn_native/opengl/PipelineGL.cpp
+++ b/src/dawn_native/opengl/PipelineGL.cpp
@@ -107,15 +107,14 @@
         const auto& indices = layout->GetBindingIndexInfo();
 
         for (uint32_t group : IterateBitSet(layout->GetBindGroupLayoutsMask())) {
-            const BindGroupLayoutBase::LayoutBindingInfo& groupInfo =
-                layout->GetBindGroupLayout(group)->GetBindingInfo();
+            const BindGroupLayoutBase* bgl = layout->GetBindGroupLayout(group);
 
-            for (const auto& it : layout->GetBindGroupLayout(group)->GetBindingMap()) {
+            for (const auto& it : bgl->GetBindingMap()) {
                 BindingNumber bindingNumber = it.first;
                 BindingIndex bindingIndex = it.second;
 
                 std::string name = GetBindingName(group, bindingNumber);
-                switch (groupInfo.types[bindingIndex]) {
+                switch (bgl->GetBindingInfo(bindingIndex).type) {
                     case wgpu::BindingType::UniformBuffer: {
                         GLint location = gl.GetUniformBlockIndex(mProgram, name.c_str());
                         if (location != -1) {
@@ -178,10 +177,11 @@
                     indices[combined.textureLocation.group][combined.textureLocation.binding];
                 mUnitsForTextures[textureIndex].push_back(textureUnit);
 
+                const BindGroupLayoutBase* bgl =
+                    layout->GetBindGroupLayout(combined.textureLocation.group);
                 wgpu::TextureComponentType componentType =
-                    layout->GetBindGroupLayout(combined.textureLocation.group)
-                        ->GetBindingInfo()
-                        .textureComponentTypes[combined.textureLocation.binding];
+                    bgl->GetBindingInfo(bgl->GetBindingIndex(combined.textureLocation.binding))
+                        .textureComponentType;
                 bool shouldUseFiltering = componentType == wgpu::TextureComponentType::Float;
 
                 GLuint samplerIndex =
diff --git a/src/dawn_native/opengl/PipelineLayoutGL.cpp b/src/dawn_native/opengl/PipelineLayoutGL.cpp
index 7048d5e..383a079 100644
--- a/src/dawn_native/opengl/PipelineLayoutGL.cpp
+++ b/src/dawn_native/opengl/PipelineLayoutGL.cpp
@@ -28,12 +28,11 @@
         GLuint ssboIndex = 0;
 
         for (uint32_t group : IterateBitSet(GetBindGroupLayoutsMask())) {
-            const BindGroupLayoutBase::LayoutBindingInfo& groupInfo =
-                GetBindGroupLayout(group)->GetBindingInfo();
+            const BindGroupLayoutBase* bgl = GetBindGroupLayout(group);
 
-            for (BindingIndex bindingIndex = 0; bindingIndex < groupInfo.bindingCount;
+            for (BindingIndex bindingIndex = 0; bindingIndex < bgl->GetBindingCount();
                  ++bindingIndex) {
-                switch (groupInfo.types[bindingIndex]) {
+                switch (bgl->GetBindingInfo(bindingIndex).type) {
                     case wgpu::BindingType::UniformBuffer:
                         mIndexInfo[group][bindingIndex] = uboIndex;
                         uboIndex++;
diff --git a/src/dawn_native/vulkan/BindGroupLayoutVk.cpp b/src/dawn_native/vulkan/BindGroupLayoutVk.cpp
index d26eebb..d694492 100644
--- a/src/dawn_native/vulkan/BindGroupLayoutVk.cpp
+++ b/src/dawn_native/vulkan/BindGroupLayoutVk.cpp
@@ -82,8 +82,6 @@
     }
 
     MaybeError BindGroupLayout::Initialize() {
-        const LayoutBindingInfo& info = GetBindingInfo();
-
         // 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.
@@ -92,13 +90,14 @@
         for (const auto& it : GetBindingMap()) {
             BindingNumber bindingNumber = it.first;
             BindingIndex bindingIndex = it.second;
+            const BindingInfo& bindingInfo = GetBindingInfo(bindingIndex);
 
             VkDescriptorSetLayoutBinding* vkBinding = &bindings[numBindings];
             vkBinding->binding = bindingNumber;
             vkBinding->descriptorType =
-                VulkanDescriptorType(info.types[bindingIndex], info.hasDynamicOffset[bindingIndex]);
+                VulkanDescriptorType(bindingInfo.type, bindingInfo.hasDynamicOffset);
             vkBinding->descriptorCount = 1;
-            vkBinding->stageFlags = VulkanShaderStageFlags(info.visibilities[bindingIndex]);
+            vkBinding->stageFlags = VulkanShaderStageFlags(bindingInfo.visibility);
             vkBinding->pImmutableSamplers = nullptr;
 
             numBindings++;
@@ -119,9 +118,10 @@
         // Compute the size of descriptor pools used for this layout.
         std::map<VkDescriptorType, uint32_t> descriptorCountPerType;
 
-        for (BindingIndex bindingIndex = 0; bindingIndex < info.bindingCount; ++bindingIndex) {
+        for (BindingIndex bindingIndex = 0; bindingIndex < GetBindingCount(); ++bindingIndex) {
+            const BindingInfo& bindingInfo = GetBindingInfo(bindingIndex);
             VkDescriptorType vulkanType =
-                VulkanDescriptorType(info.types[bindingIndex], info.hasDynamicOffset[bindingIndex]);
+                VulkanDescriptorType(bindingInfo.type, bindingInfo.hasDynamicOffset);
 
             // map::operator[] will return 0 if the key doesn't exist.
             descriptorCountPerType[vulkanType]++;
diff --git a/src/dawn_native/vulkan/BindGroupVk.cpp b/src/dawn_native/vulkan/BindGroupVk.cpp
index 43bee2a..718dd24 100644
--- a/src/dawn_native/vulkan/BindGroupVk.cpp
+++ b/src/dawn_native/vulkan/BindGroupVk.cpp
@@ -43,10 +43,11 @@
         std::array<VkDescriptorBufferInfo, kMaxBindingsPerGroup> writeBufferInfo;
         std::array<VkDescriptorImageInfo, kMaxBindingsPerGroup> writeImageInfo;
 
-        const BindGroupLayoutBase::LayoutBindingInfo& layoutInfo = GetLayout()->GetBindingInfo();
         for (const auto& it : GetLayout()->GetBindingMap()) {
             BindingNumber bindingNumber = it.first;
             BindingIndex bindingIndex = it.second;
+            const BindGroupLayoutBase::BindingInfo& bindingInfo =
+                GetLayout()->GetBindingInfo(bindingIndex);
 
             auto& write = writes[numWrites];
             write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
@@ -55,10 +56,10 @@
             write.dstBinding = bindingNumber;
             write.dstArrayElement = 0;
             write.descriptorCount = 1;
-            write.descriptorType = VulkanDescriptorType(layoutInfo.types[bindingIndex],
-                                                        layoutInfo.hasDynamicOffset[bindingIndex]);
+            write.descriptorType =
+                VulkanDescriptorType(bindingInfo.type, bindingInfo.hasDynamicOffset);
 
-            switch (layoutInfo.types[bindingIndex]) {
+            switch (bindingInfo.type) {
                 case wgpu::BindingType::UniformBuffer:
                 case wgpu::BindingType::StorageBuffer:
                 case wgpu::BindingType::ReadonlyStorageBuffer: {