Update BindingInfo to reflect new BindGroupLayoutEntry structure

Changes the internal BindingInfo structure and any references to it. The
BindGroupLayoutEntry information is normalized when converting it into
the internal representation, but still accepted as either the old or
new layout. A "bindingType" member is added to the BindingInfo that's
not present in the BindGroupLayoutEntry itself to indicate which of
buffer, sampler, texture, or storageTexture is populated. This proves
useful for a myriad of switch statements in the various backends.

Bug: dawn:527
Change-Id: I6ae65adae61d0005fc50ed6d1bc2ec9b2a1295ad
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/35862
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
Auto-Submit: Brandon Jones <bajones@chromium.org>
diff --git a/src/dawn_native/BindGroup.cpp b/src/dawn_native/BindGroup.cpp
index 96c4921..c84d578 100644
--- a/src/dawn_native/BindGroup.cpp
+++ b/src/dawn_native/BindGroup.cpp
@@ -31,15 +31,31 @@
 
         MaybeError ValidateBufferBinding(const DeviceBase* device,
                                          const BindGroupEntry& entry,
-                                         wgpu::BufferUsage requiredUsage,
-                                         const BindingInfo& bindingInfo,
-                                         const uint64_t maxBindingSize) {
+                                         const BindingInfo& bindingInfo) {
             if (entry.buffer == nullptr || entry.sampler != nullptr ||
                 entry.textureView != nullptr) {
                 return DAWN_VALIDATION_ERROR("Expected buffer binding");
             }
             DAWN_TRY(device->ValidateObject(entry.buffer));
 
+            ASSERT(bindingInfo.bindingType == BindingInfoType::Buffer);
+
+            wgpu::BufferUsage requiredUsage;
+            uint64_t maxBindingSize;
+            switch (bindingInfo.buffer.type) {
+                case wgpu::BufferBindingType::Uniform:
+                    requiredUsage = wgpu::BufferUsage::Uniform;
+                    maxBindingSize = kMaxUniformBufferBindingSize;
+                    break;
+                case wgpu::BufferBindingType::Storage:
+                case wgpu::BufferBindingType::ReadOnlyStorage:
+                    requiredUsage = wgpu::BufferUsage::Storage;
+                    maxBindingSize = std::numeric_limits<uint64_t>::max();
+                    break;
+                case wgpu::BufferBindingType::Undefined:
+                    UNREACHABLE();
+            }
+
             uint64_t bufferSize = entry.buffer->GetSize();
 
             // Handle wgpu::WholeSize, avoiding overflows.
@@ -72,11 +88,11 @@
                 return DAWN_VALIDATION_ERROR("buffer binding usage mismatch");
             }
 
-            if (bindingSize < bindingInfo.minBufferBindingSize) {
+            if (bindingSize < bindingInfo.buffer.minBindingSize) {
                 return DAWN_VALIDATION_ERROR(
                     "Binding size smaller than minimum buffer size: binding " +
                     std::to_string(entry.binding) + " given " + std::to_string(bindingSize) +
-                    " bytes, required " + std::to_string(bindingInfo.minBufferBindingSize) +
+                    " bytes, required " + std::to_string(bindingInfo.buffer.minBindingSize) +
                     " bytes");
             }
 
@@ -93,8 +109,6 @@
 
         MaybeError ValidateTextureBinding(const DeviceBase* device,
                                           const BindGroupEntry& entry,
-                                          wgpu::TextureUsage requiredUsage,
-                                          bool multisampled,
                                           const BindingInfo& bindingInfo) {
             if (entry.textureView == nullptr || entry.sampler != nullptr ||
                 entry.buffer != nullptr) {
@@ -110,30 +124,44 @@
             }
 
             TextureBase* texture = view->GetTexture();
-            if (!(texture->GetUsage() & requiredUsage)) {
-                return DAWN_VALIDATION_ERROR("Texture binding usage mismatch");
-            }
-
-            if (texture->IsMultisampledTexture() != multisampled) {
-                return DAWN_VALIDATION_ERROR("Texture multisampling mismatch");
-            }
-
-            switch (requiredUsage) {
-                case wgpu::TextureUsage::Sampled: {
+            switch (bindingInfo.bindingType) {
+                case BindingInfoType::Texture: {
                     ComponentTypeBit supportedTypes =
                         texture->GetFormat().GetAspectInfo(aspect).supportedComponentTypes;
                     ComponentTypeBit requiredType =
-                        ToComponentTypeBit(bindingInfo.textureComponentType);
+                        SampleTypeToComponentTypeBit(bindingInfo.texture.sampleType);
+
+                    if (!(texture->GetUsage() & wgpu::TextureUsage::Sampled)) {
+                        return DAWN_VALIDATION_ERROR("Texture binding usage mismatch");
+                    }
+
+                    if (texture->IsMultisampledTexture() != bindingInfo.texture.multisampled) {
+                        return DAWN_VALIDATION_ERROR("Texture multisampling mismatch");
+                    }
 
                     if ((supportedTypes & requiredType) == 0) {
                         return DAWN_VALIDATION_ERROR("Texture component type usage mismatch");
                     }
+
+                    if (entry.textureView->GetDimension() != bindingInfo.texture.viewDimension) {
+                        return DAWN_VALIDATION_ERROR("Texture view dimension mismatch");
+                    }
                     break;
                 }
-                case wgpu::TextureUsage::Storage: {
-                    if (texture->GetFormat().format != bindingInfo.storageTextureFormat) {
+                case BindingInfoType::StorageTexture: {
+                    ASSERT(!texture->IsMultisampledTexture());
+
+                    if (!(texture->GetUsage() & wgpu::TextureUsage::Storage)) {
+                        return DAWN_VALIDATION_ERROR("Storage Texture binding usage mismatch");
+                    }
+
+                    if (texture->GetFormat().format != bindingInfo.storageTexture.format) {
                         return DAWN_VALIDATION_ERROR("Storage texture format mismatch");
                     }
+                    if (entry.textureView->GetDimension() !=
+                        bindingInfo.storageTexture.viewDimension) {
+                        return DAWN_VALIDATION_ERROR("Storage texture view dimension mismatch");
+                    }
                     break;
                 }
                 default:
@@ -141,29 +169,28 @@
                     break;
             }
 
-            if (entry.textureView->GetDimension() != bindingInfo.viewDimension) {
-                return DAWN_VALIDATION_ERROR("Texture view dimension mismatch");
-            }
-
             return {};
         }
 
         MaybeError ValidateSamplerBinding(const DeviceBase* device,
                                           const BindGroupEntry& entry,
-                                          wgpu::BindingType bindingType) {
+                                          const BindingInfo& bindingInfo) {
             if (entry.sampler == nullptr || entry.textureView != nullptr ||
                 entry.buffer != nullptr) {
                 return DAWN_VALIDATION_ERROR("Expected sampler binding");
             }
             DAWN_TRY(device->ValidateObject(entry.sampler));
 
-            switch (bindingType) {
-                case wgpu::BindingType::Sampler:
+            ASSERT(bindingInfo.bindingType == BindingInfoType::Sampler);
+
+            switch (bindingInfo.sampler.type) {
+                case wgpu::SamplerBindingType::Filtering:
+                case wgpu::SamplerBindingType::NonFiltering:
                     if (entry.sampler->HasCompareFunction()) {
                         return DAWN_VALIDATION_ERROR("Did not expect comparison sampler");
                     }
                     break;
-                case wgpu::BindingType::ComparisonSampler:
+                case wgpu::SamplerBindingType::Comparison:
                     if (!entry.sampler->HasCompareFunction()) {
                         return DAWN_VALIDATION_ERROR("Expected comparison sampler");
                     }
@@ -212,36 +239,16 @@
             const BindingInfo& bindingInfo = descriptor->layout->GetBindingInfo(bindingIndex);
 
             // Perform binding-type specific validation.
-            switch (bindingInfo.type) {
-                case wgpu::BindingType::UniformBuffer:
-                    DAWN_TRY(ValidateBufferBinding(device, entry, wgpu::BufferUsage::Uniform,
-                                                   bindingInfo, kMaxUniformBufferBindingSize));
+            switch (bindingInfo.bindingType) {
+                case BindingInfoType::Buffer:
+                    DAWN_TRY(ValidateBufferBinding(device, entry, bindingInfo));
                     break;
-                case wgpu::BindingType::StorageBuffer:
-                case wgpu::BindingType::ReadonlyStorageBuffer:
-                    DAWN_TRY(ValidateBufferBinding(device, entry, wgpu::BufferUsage::Storage,
-                                                   bindingInfo,
-                                                   std::numeric_limits<uint64_t>::max()));
+                case BindingInfoType::Texture:
+                case BindingInfoType::StorageTexture:
+                    DAWN_TRY(ValidateTextureBinding(device, entry, bindingInfo));
                     break;
-                case wgpu::BindingType::SampledTexture:
-                    DAWN_TRY(ValidateTextureBinding(device, entry, wgpu::TextureUsage::Sampled,
-                                                    false, bindingInfo));
-                    break;
-                case wgpu::BindingType::MultisampledTexture:
-                    DAWN_TRY(ValidateTextureBinding(device, entry, wgpu::TextureUsage::Sampled,
-                                                    true, bindingInfo));
-                    break;
-                case wgpu::BindingType::Sampler:
-                case wgpu::BindingType::ComparisonSampler:
-                    DAWN_TRY(ValidateSamplerBinding(device, entry, bindingInfo.type));
-                    break;
-                case wgpu::BindingType::ReadonlyStorageTexture:
-                case wgpu::BindingType::WriteonlyStorageTexture:
-                    DAWN_TRY(ValidateTextureBinding(device, entry, wgpu::TextureUsage::Storage,
-                                                    false, bindingInfo));
-                    break;
-                case wgpu::BindingType::Undefined:
-                    UNREACHABLE();
+                case BindingInfoType::Sampler:
+                    DAWN_TRY(ValidateSamplerBinding(device, entry, bindingInfo));
                     break;
             }
         }
@@ -307,7 +314,7 @@
         uint32_t packedIdx = 0;
         for (BindingIndex bindingIndex{0}; bindingIndex < descriptor->layout->GetBufferCount();
              ++bindingIndex) {
-            if (descriptor->layout->GetBindingInfo(bindingIndex).minBufferBindingSize == 0) {
+            if (descriptor->layout->GetBindingInfo(bindingIndex).buffer.minBindingSize == 0) {
                 mBindingData.unverifiedBufferSizes[packedIdx] =
                     mBindingData.bufferData[bindingIndex].size;
                 ++packedIdx;
@@ -359,10 +366,7 @@
     BufferBinding BindGroupBase::GetBindingAsBufferBinding(BindingIndex bindingIndex) {
         ASSERT(!IsError());
         ASSERT(bindingIndex < mLayout->GetBindingCount());
-        ASSERT(mLayout->GetBindingInfo(bindingIndex).type == wgpu::BindingType::UniformBuffer ||
-               mLayout->GetBindingInfo(bindingIndex).type == wgpu::BindingType::StorageBuffer ||
-               mLayout->GetBindingInfo(bindingIndex).type ==
-                   wgpu::BindingType::ReadonlyStorageBuffer);
+        ASSERT(mLayout->GetBindingInfo(bindingIndex).bindingType == BindingInfoType::Buffer);
         BufferBase* buffer = static_cast<BufferBase*>(mBindingData.bindings[bindingIndex].Get());
         return {buffer, mBindingData.bufferData[bindingIndex].offset,
                 mBindingData.bufferData[bindingIndex].size};
@@ -371,21 +375,16 @@
     SamplerBase* BindGroupBase::GetBindingAsSampler(BindingIndex bindingIndex) const {
         ASSERT(!IsError());
         ASSERT(bindingIndex < mLayout->GetBindingCount());
-        ASSERT(mLayout->GetBindingInfo(bindingIndex).type == wgpu::BindingType::Sampler ||
-               mLayout->GetBindingInfo(bindingIndex).type == wgpu::BindingType::ComparisonSampler);
+        ASSERT(mLayout->GetBindingInfo(bindingIndex).bindingType == BindingInfoType::Sampler);
         return static_cast<SamplerBase*>(mBindingData.bindings[bindingIndex].Get());
     }
 
     TextureViewBase* BindGroupBase::GetBindingAsTextureView(BindingIndex bindingIndex) {
         ASSERT(!IsError());
         ASSERT(bindingIndex < mLayout->GetBindingCount());
-        ASSERT(mLayout->GetBindingInfo(bindingIndex).type == wgpu::BindingType::SampledTexture ||
-               mLayout->GetBindingInfo(bindingIndex).type ==
-                   wgpu::BindingType::MultisampledTexture ||
-               mLayout->GetBindingInfo(bindingIndex).type ==
-                   wgpu::BindingType::ReadonlyStorageTexture ||
-               mLayout->GetBindingInfo(bindingIndex).type ==
-                   wgpu::BindingType::WriteonlyStorageTexture);
+        ASSERT(mLayout->GetBindingInfo(bindingIndex).bindingType == BindingInfoType::Texture ||
+               mLayout->GetBindingInfo(bindingIndex).bindingType ==
+                   BindingInfoType::StorageTexture);
         return static_cast<TextureViewBase*>(mBindingData.bindings[bindingIndex].Get());
     }
 
diff --git a/src/dawn_native/BindGroupLayout.cpp b/src/dawn_native/BindGroupLayout.cpp
index 4f34921..3d95e3b 100644
--- a/src/dawn_native/BindGroupLayout.cpp
+++ b/src/dawn_native/BindGroupLayout.cpp
@@ -248,17 +248,43 @@
 
 
         bool operator!=(const BindingInfo& a, const BindingInfo& b) {
-            return a.hasDynamicOffset != b.hasDynamicOffset ||          //
-                   a.visibility != b.visibility ||                      //
-                   a.type != b.type ||                                  //
-                   a.textureComponentType != b.textureComponentType ||  //
-                   a.viewDimension != b.viewDimension ||                //
-                   a.storageTextureFormat != b.storageTextureFormat ||  //
-                   a.minBufferBindingSize != b.minBufferBindingSize;
+            if (a.visibility != b.visibility || a.bindingType != b.bindingType) {
+                return true;
+            }
+
+            switch (a.bindingType) {
+                case BindingInfoType::Buffer:
+                    return a.buffer.type != b.buffer.type ||
+                           a.buffer.hasDynamicOffset != b.buffer.hasDynamicOffset ||
+                           a.buffer.minBindingSize != b.buffer.minBindingSize;
+                case BindingInfoType::Sampler:
+                    return a.sampler.type != b.sampler.type;
+                case BindingInfoType::Texture:
+                    return a.texture.sampleType != b.texture.sampleType ||
+                           a.texture.viewDimension != b.texture.viewDimension ||
+                           a.texture.multisampled != b.texture.multisampled;
+                case BindingInfoType::StorageTexture:
+                    return a.storageTexture.access != b.storageTexture.access ||
+                           a.storageTexture.viewDimension != b.storageTexture.viewDimension ||
+                           a.storageTexture.format != b.storageTexture.format;
+            }
         }
 
-        bool IsBufferBindingType(wgpu::BindingType type) {
-            switch (type) {
+        // TODO(dawn:527): Once the deprecated BindGroupLayoutEntry path has been removed, this can
+        // turn into a simple `binding.buffer.type != wgpu::BufferBindingType::Undefined` check.
+        bool IsBufferBinding(const BindGroupLayoutEntry& binding) {
+            if (binding.buffer.type != wgpu::BufferBindingType::Undefined) {
+                return true;
+            } else if (binding.sampler.type != wgpu::SamplerBindingType::Undefined) {
+                return false;
+            } else if (binding.texture.sampleType != wgpu::TextureSampleType::Undefined) {
+                return false;
+            } else if (binding.storageTexture.access != wgpu::StorageTextureAccess::Undefined) {
+                return false;
+            }
+
+            // Deprecated path
+            switch (binding.type) {
                 case wgpu::BindingType::UniformBuffer:
                 case wgpu::BindingType::StorageBuffer:
                 case wgpu::BindingType::ReadonlyStorageBuffer:
@@ -274,20 +300,6 @@
             }
         }
 
-        bool IsBufferBinding(const BindGroupLayoutEntry& binding) {
-            if (binding.buffer.type != wgpu::BufferBindingType::Undefined) {
-                return true;
-            } else if (binding.sampler.type != wgpu::SamplerBindingType::Undefined) {
-                return false;
-            } else if (binding.texture.sampleType != wgpu::TextureSampleType::Undefined) {
-                return false;
-            } else if (binding.storageTexture.access != wgpu::StorageTextureAccess::Undefined) {
-                return false;
-            }
-
-            return IsBufferBindingType(binding.type);
-        }
-
         bool BindingHasDynamicOffset(const BindGroupLayoutEntry& binding) {
             if (binding.buffer.type != wgpu::BufferBindingType::Undefined) {
                 return binding.buffer.hasDynamicOffset;
@@ -357,7 +369,7 @@
             BindingIndex lastBufferIndex{0};
             BindingIndex firstNonBufferIndex = std::numeric_limits<BindingIndex>::max();
             for (BindingIndex i{0}; i < bindings.size(); ++i) {
-                if (IsBufferBindingType(bindings[i].type)) {
+                if (bindings[i].bindingType == BindingInfoType::Buffer) {
                     lastBufferIndex = std::max(i, lastBufferIndex);
                 } else {
                     firstNonBufferIndex = std::min(i, firstNonBufferIndex);
@@ -369,6 +381,120 @@
             return firstNonBufferIndex >= lastBufferIndex;
         }
 
+        BindingInfo CreateBindGroupLayoutInfo(const BindGroupLayoutEntry& binding) {
+            BindingInfo bindingInfo;
+            bindingInfo.binding = BindingNumber(binding.binding);
+            bindingInfo.visibility = binding.visibility;
+
+            if (binding.buffer.type != wgpu::BufferBindingType::Undefined) {
+                bindingInfo.bindingType = BindingInfoType::Buffer;
+                bindingInfo.buffer = binding.buffer;
+            } else if (binding.sampler.type != wgpu::SamplerBindingType::Undefined) {
+                bindingInfo.bindingType = BindingInfoType::Sampler;
+                bindingInfo.sampler = binding.sampler;
+            } else if (binding.texture.sampleType != wgpu::TextureSampleType::Undefined) {
+                bindingInfo.bindingType = BindingInfoType::Texture;
+                bindingInfo.texture = binding.texture;
+
+                if (binding.texture.viewDimension == wgpu::TextureViewDimension::Undefined) {
+                    bindingInfo.texture.viewDimension = wgpu::TextureViewDimension::e2D;
+                }
+            } else if (binding.storageTexture.access != wgpu::StorageTextureAccess::Undefined) {
+                bindingInfo.bindingType = BindingInfoType::StorageTexture;
+                bindingInfo.storageTexture = binding.storageTexture;
+
+                if (binding.storageTexture.viewDimension == wgpu::TextureViewDimension::Undefined) {
+                    bindingInfo.storageTexture.viewDimension = wgpu::TextureViewDimension::e2D;
+                }
+            } else {
+                // Deprecated entry layout.
+                switch (binding.type) {
+                    case wgpu::BindingType::UniformBuffer:
+                        bindingInfo.bindingType = BindingInfoType::Buffer;
+                        bindingInfo.buffer.type = wgpu::BufferBindingType::Uniform;
+                        bindingInfo.buffer.hasDynamicOffset = binding.hasDynamicOffset;
+                        bindingInfo.buffer.minBindingSize = binding.minBufferBindingSize;
+                        break;
+                    case wgpu::BindingType::StorageBuffer:
+                        bindingInfo.bindingType = BindingInfoType::Buffer;
+                        bindingInfo.buffer.type = wgpu::BufferBindingType::Storage;
+                        bindingInfo.buffer.hasDynamicOffset = binding.hasDynamicOffset;
+                        bindingInfo.buffer.minBindingSize = binding.minBufferBindingSize;
+                        break;
+                    case wgpu::BindingType::ReadonlyStorageBuffer:
+                        bindingInfo.bindingType = BindingInfoType::Buffer;
+                        bindingInfo.buffer.type = wgpu::BufferBindingType::ReadOnlyStorage;
+                        bindingInfo.buffer.hasDynamicOffset = binding.hasDynamicOffset;
+                        bindingInfo.buffer.minBindingSize = binding.minBufferBindingSize;
+                        break;
+
+                    case wgpu::BindingType::Sampler:
+                        bindingInfo.bindingType = BindingInfoType::Sampler;
+                        bindingInfo.sampler.type = wgpu::SamplerBindingType::Filtering;
+                        break;
+                    case wgpu::BindingType::ComparisonSampler:
+                        bindingInfo.bindingType = BindingInfoType::Sampler;
+                        bindingInfo.sampler.type = wgpu::SamplerBindingType::Comparison;
+                        break;
+
+                    case wgpu::BindingType::MultisampledTexture:
+                        bindingInfo.texture.multisampled = true;
+                        DAWN_FALLTHROUGH;
+                    case wgpu::BindingType::SampledTexture:
+                        bindingInfo.bindingType = BindingInfoType::Texture;
+                        bindingInfo.texture.viewDimension = binding.viewDimension;
+                        if (binding.texture.viewDimension ==
+                            wgpu::TextureViewDimension::Undefined) {
+                            bindingInfo.texture.viewDimension = wgpu::TextureViewDimension::e2D;
+                        }
+
+                        switch (binding.textureComponentType) {
+                            case wgpu::TextureComponentType::Float:
+                                bindingInfo.texture.sampleType = wgpu::TextureSampleType::Float;
+                                break;
+                            case wgpu::TextureComponentType::Uint:
+                                bindingInfo.texture.sampleType = wgpu::TextureSampleType::Uint;
+                                break;
+                            case wgpu::TextureComponentType::Sint:
+                                bindingInfo.texture.sampleType = wgpu::TextureSampleType::Sint;
+                                break;
+                            case wgpu::TextureComponentType::DepthComparison:
+                                bindingInfo.texture.sampleType = wgpu::TextureSampleType::Depth;
+                                break;
+                        }
+                        break;
+
+                    case wgpu::BindingType::ReadonlyStorageTexture:
+                        bindingInfo.bindingType = BindingInfoType::StorageTexture;
+                        bindingInfo.storageTexture.access = wgpu::StorageTextureAccess::ReadOnly;
+                        bindingInfo.storageTexture.format = binding.storageTextureFormat;
+                        bindingInfo.storageTexture.viewDimension = binding.viewDimension;
+                        if (binding.storageTexture.viewDimension ==
+                            wgpu::TextureViewDimension::Undefined) {
+                            bindingInfo.storageTexture.viewDimension =
+                                wgpu::TextureViewDimension::e2D;
+                        }
+                        break;
+                    case wgpu::BindingType::WriteonlyStorageTexture:
+                        bindingInfo.bindingType = BindingInfoType::StorageTexture;
+                        bindingInfo.storageTexture.access = wgpu::StorageTextureAccess::WriteOnly;
+                        bindingInfo.storageTexture.format = binding.storageTextureFormat;
+                        bindingInfo.storageTexture.viewDimension = binding.viewDimension;
+                        if (binding.storageTexture.viewDimension ==
+                            wgpu::TextureViewDimension::Undefined) {
+                            bindingInfo.storageTexture.viewDimension =
+                                wgpu::TextureViewDimension::e2D;
+                        }
+                        break;
+
+                    case wgpu::BindingType::Undefined:
+                        UNREACHABLE();
+                }
+            }
+
+            return bindingInfo;
+        }
+
     }  // namespace
 
     // BindGroupLayoutBase
@@ -384,106 +510,7 @@
         for (BindingIndex i{0}; i < mBindingInfo.size(); ++i) {
             const BindGroupLayoutEntry& binding = sortedBindings[static_cast<uint32_t>(i)];
 
-            // TODO(dawn:527): This code currently converts the new-style BindGroupLayoutEntry
-            // definitions into the older-style BindingInfo. This is to allow for a staggered
-            // conversion, but it means that there's some combinations that the more expressive new
-            // style can handle that will be lost in the translation. The solution is to update
-            // BindingInfo to only reflect the new-style entry layout and convert the old-style
-            // entry layout into it instead.
-            mBindingInfo[i].binding = BindingNumber(binding.binding);
-            mBindingInfo[i].visibility = binding.visibility;
-
-            if (binding.buffer.type != wgpu::BufferBindingType::Undefined) {
-                switch (binding.buffer.type) {
-                    case wgpu::BufferBindingType::Uniform:
-                        mBindingInfo[i].type = wgpu::BindingType::UniformBuffer;
-                        break;
-                    case wgpu::BufferBindingType::Storage:
-                        mBindingInfo[i].type = wgpu::BindingType::StorageBuffer;
-                        break;
-                    case wgpu::BufferBindingType::ReadOnlyStorage:
-                        mBindingInfo[i].type = wgpu::BindingType::ReadonlyStorageBuffer;
-                        break;
-                    case wgpu::BufferBindingType::Undefined:
-                        UNREACHABLE();
-                }
-                mBindingInfo[i].minBufferBindingSize = binding.buffer.minBindingSize;
-                mBindingInfo[i].hasDynamicOffset = binding.buffer.hasDynamicOffset;
-            } else if (binding.sampler.type != wgpu::SamplerBindingType::Undefined) {
-                switch (binding.sampler.type) {
-                    case wgpu::SamplerBindingType::Filtering:
-                    case wgpu::SamplerBindingType::NonFiltering:
-                        mBindingInfo[i].type = wgpu::BindingType::Sampler;
-                        break;
-                    case wgpu::SamplerBindingType::Comparison:
-                        mBindingInfo[i].type = wgpu::BindingType::ComparisonSampler;
-                        break;
-                    case wgpu::SamplerBindingType::Undefined:
-                        UNREACHABLE();
-                }
-            } else if (binding.texture.sampleType != wgpu::TextureSampleType::Undefined) {
-                mBindingInfo[i].type = binding.texture.multisampled
-                                           ? wgpu::BindingType::MultisampledTexture
-                                           : wgpu::BindingType::SampledTexture;
-                switch (binding.texture.sampleType) {
-                    case wgpu::TextureSampleType::Float:
-                    case wgpu::TextureSampleType::UnfilterableFloat:
-                        mBindingInfo[i].textureComponentType = wgpu::TextureComponentType::Float;
-                        break;
-                    case wgpu::TextureSampleType::Sint:
-                        mBindingInfo[i].textureComponentType = wgpu::TextureComponentType::Sint;
-                        break;
-                    case wgpu::TextureSampleType::Uint:
-                        mBindingInfo[i].textureComponentType = wgpu::TextureComponentType::Uint;
-                        break;
-                    case wgpu::TextureSampleType::Depth:
-                        mBindingInfo[i].textureComponentType =
-                            wgpu::TextureComponentType::DepthComparison;
-                        break;
-                    case wgpu::TextureSampleType::Undefined:
-                        UNREACHABLE();
-                }
-
-                if (binding.texture.viewDimension == wgpu::TextureViewDimension::Undefined) {
-                    mBindingInfo[i].viewDimension = wgpu::TextureViewDimension::e2D;
-                } else {
-                    mBindingInfo[i].viewDimension = binding.texture.viewDimension;
-                }
-            } else if (binding.storageTexture.access != wgpu::StorageTextureAccess::Undefined) {
-                switch (binding.storageTexture.access) {
-                    case wgpu::StorageTextureAccess::ReadOnly:
-                        mBindingInfo[i].type = wgpu::BindingType::ReadonlyStorageTexture;
-                        break;
-                    case wgpu::StorageTextureAccess::WriteOnly:
-                        mBindingInfo[i].type = wgpu::BindingType::WriteonlyStorageTexture;
-                        break;
-                    case wgpu::StorageTextureAccess::Undefined:
-                        UNREACHABLE();
-                }
-
-                mBindingInfo[i].storageTextureFormat = binding.storageTexture.format;
-
-                if (binding.storageTexture.viewDimension == wgpu::TextureViewDimension::Undefined) {
-                    mBindingInfo[i].viewDimension = wgpu::TextureViewDimension::e2D;
-                } else {
-                    mBindingInfo[i].viewDimension = binding.storageTexture.viewDimension;
-                }
-            } else {
-                // Deprecated entry layout. As noted above, though, this is currently the only
-                // lossless path.
-                mBindingInfo[i].type = binding.type;
-                mBindingInfo[i].textureComponentType = binding.textureComponentType;
-                mBindingInfo[i].storageTextureFormat = binding.storageTextureFormat;
-                mBindingInfo[i].minBufferBindingSize = binding.minBufferBindingSize;
-
-                if (binding.viewDimension == wgpu::TextureViewDimension::Undefined) {
-                    mBindingInfo[i].viewDimension = wgpu::TextureViewDimension::e2D;
-                } else {
-                    mBindingInfo[i].viewDimension = binding.viewDimension;
-                }
-
-                mBindingInfo[i].hasDynamicOffset = binding.hasDynamicOffset;
-            }
+            mBindingInfo[i] = CreateBindGroupLayoutInfo(binding);
 
             if (IsBufferBinding(binding)) {
                 // Buffers must be contiguously packed at the start of the binding info.
@@ -534,9 +561,12 @@
             recorder.Record(it.first, it.second);
 
             const BindingInfo& info = mBindingInfo[it.second];
-            recorder.Record(info.hasDynamicOffset, info.visibility, info.type,
-                            info.textureComponentType, info.viewDimension,
-                            info.storageTextureFormat, info.minBufferBindingSize);
+
+            recorder.Record(info.buffer.hasDynamicOffset, info.visibility, info.bindingType,
+                            info.buffer.type, info.buffer.minBindingSize, info.sampler.type,
+                            info.texture.sampleType, info.texture.viewDimension,
+                            info.texture.multisampled, info.storageTexture.access,
+                            info.storageTexture.format, info.storageTexture.viewDimension);
         }
 
         return recorder.GetContentHash();
diff --git a/src/dawn_native/BindingInfo.h b/src/dawn_native/BindingInfo.h
index 5824781..ef3c7ac 100644
--- a/src/dawn_native/BindingInfo.h
+++ b/src/dawn_native/BindingInfo.h
@@ -48,15 +48,24 @@
     // TODO(enga): Figure out a good number for this.
     static constexpr uint32_t kMaxOptimalBindingsPerGroup = 32;
 
+    enum class BindingInfoType {
+        Buffer,
+        Sampler,
+        Texture,
+        StorageTexture,
+    };
+
     struct BindingInfo {
         BindingNumber binding;
         wgpu::ShaderStage visibility;
-        wgpu::BindingType type;
-        wgpu::TextureComponentType textureComponentType = wgpu::TextureComponentType::Float;
-        wgpu::TextureViewDimension viewDimension = wgpu::TextureViewDimension::Undefined;
-        wgpu::TextureFormat storageTextureFormat = wgpu::TextureFormat::Undefined;
-        bool hasDynamicOffset = false;
-        uint64_t minBufferBindingSize = 0;
+
+        BindingInfoType bindingType;
+
+        // TODO(dawn:527): These four values could be made into a union.
+        BufferBindingLayout buffer;
+        SamplerBindingLayout sampler;
+        TextureBindingLayout texture;
+        StorageTextureBindingLayout storageTexture;
     };
 
     struct PerStageBindingCounts {
diff --git a/src/dawn_native/Format.cpp b/src/dawn_native/Format.cpp
index f57b7b4..f1d7d16 100644
--- a/src/dawn_native/Format.cpp
+++ b/src/dawn_native/Format.cpp
@@ -31,6 +31,7 @@
 
     // Format
 
+    // TODO(dawn:527): Remove when unused.
     ComponentTypeBit ToComponentTypeBit(wgpu::TextureComponentType type) {
         switch (type) {
             case wgpu::TextureComponentType::Float:
@@ -64,6 +65,24 @@
         return static_cast<ComponentTypeBit>(1 << static_cast<uint32_t>(type));
     }
 
+    ComponentTypeBit SampleTypeToComponentTypeBit(wgpu::TextureSampleType sampleType) {
+        switch (sampleType) {
+            case wgpu::TextureSampleType::Float:
+            case wgpu::TextureSampleType::UnfilterableFloat:
+                return ComponentTypeBit::Float;
+            case wgpu::TextureSampleType::Sint:
+                return ComponentTypeBit::Sint;
+            case wgpu::TextureSampleType::Uint:
+                return ComponentTypeBit::Uint;
+            case wgpu::TextureSampleType::Depth:
+                return ComponentTypeBit::DepthComparison;
+            case wgpu::TextureSampleType::Undefined:
+                UNREACHABLE();
+        }
+
+        // TODO(dawn:527): Ideally we can get this path to use that static_cast method as well.
+    }
+
     bool Format::IsColor() const {
         return aspects == Aspect::Color;
     }
diff --git a/src/dawn_native/Format.h b/src/dawn_native/Format.h
index ec4d6b4..fe0c41f 100644
--- a/src/dawn_native/Format.h
+++ b/src/dawn_native/Format.h
@@ -39,6 +39,8 @@
 
     // Converts an wgpu::TextureComponentType to its bitmask representation.
     ComponentTypeBit ToComponentTypeBit(wgpu::TextureComponentType type);
+    // Converts an wgpu::TextureSampleType to its bitmask representation.
+    ComponentTypeBit SampleTypeToComponentTypeBit(wgpu::TextureSampleType sampleType);
 
     struct TexelBlockInfo {
         uint32_t byteSize;
diff --git a/src/dawn_native/PipelineLayout.cpp b/src/dawn_native/PipelineLayout.cpp
index 75839c7..b1317ae 100644
--- a/src/dawn_native/PipelineLayout.cpp
+++ b/src/dawn_native/PipelineLayout.cpp
@@ -83,14 +83,34 @@
         // Merges two entries at the same location, if they are allowed to be merged.
         auto MergeEntries = [](BindGroupLayoutEntry* modifiedEntry,
                                const BindGroupLayoutEntry& mergedEntry) -> MaybeError {
-            // Minimum buffer binding size excluded because we take the maximum seen across stages.
             // Visibility is excluded because we take the OR across stages.
             bool compatible =
-                modifiedEntry->binding == mergedEntry.binding &&                    //
-                modifiedEntry->type == mergedEntry.type &&                          //
-                modifiedEntry->hasDynamicOffset == mergedEntry.hasDynamicOffset &&  //
-                modifiedEntry->viewDimension == mergedEntry.viewDimension &&        //
-                modifiedEntry->textureComponentType == mergedEntry.textureComponentType;
+                modifiedEntry->binding == mergedEntry.binding &&
+                modifiedEntry->buffer.type == mergedEntry.buffer.type &&
+                modifiedEntry->sampler.type == mergedEntry.sampler.type &&
+                modifiedEntry->texture.sampleType == mergedEntry.texture.sampleType &&
+                modifiedEntry->storageTexture.access == mergedEntry.storageTexture.access;
+
+            // Minimum buffer binding size excluded because we take the maximum seen across stages.
+            if (modifiedEntry->buffer.type != wgpu::BufferBindingType::Undefined) {
+                compatible = compatible && modifiedEntry->buffer.hasDynamicOffset ==
+                                               mergedEntry.buffer.hasDynamicOffset;
+            }
+
+            if (modifiedEntry->texture.sampleType != wgpu::TextureSampleType::Undefined) {
+                compatible =
+                    compatible &&
+                    modifiedEntry->texture.viewDimension == mergedEntry.texture.viewDimension &&
+                    modifiedEntry->texture.multisampled == mergedEntry.texture.multisampled;
+            }
+
+            if (modifiedEntry->storageTexture.access != wgpu::StorageTextureAccess::Undefined) {
+                compatible =
+                    compatible &&
+                    modifiedEntry->storageTexture.format == mergedEntry.storageTexture.format &&
+                    modifiedEntry->storageTexture.viewDimension ==
+                        mergedEntry.storageTexture.viewDimension;
+            }
 
             // Check if any properties are incompatible with existing entry
             // If compatible, we will merge some properties
@@ -101,8 +121,8 @@
             }
 
             // Use the max |minBufferBindingSize| we find.
-            modifiedEntry->minBufferBindingSize =
-                std::max(modifiedEntry->minBufferBindingSize, mergedEntry.minBufferBindingSize);
+            modifiedEntry->buffer.minBindingSize =
+                std::max(modifiedEntry->buffer.minBindingSize, mergedEntry.buffer.minBindingSize);
 
             // Use the OR of all the stages at which we find this binding.
             modifiedEntry->visibility |= mergedEntry.visibility;
@@ -114,12 +134,20 @@
         auto ConvertMetadataToEntry =
             [](const EntryPointMetadata::ShaderBindingInfo& shaderBinding) -> BindGroupLayoutEntry {
             BindGroupLayoutEntry entry = {};
-            entry.type = shaderBinding.type;
-            entry.hasDynamicOffset = false;
-            entry.viewDimension = shaderBinding.viewDimension;
-            entry.textureComponentType = shaderBinding.textureComponentType;
-            entry.storageTextureFormat = shaderBinding.storageTextureFormat;
-            entry.minBufferBindingSize = shaderBinding.minBufferBindingSize;
+            switch (shaderBinding.bindingType) {
+                case BindingInfoType::Buffer:
+                    entry.buffer = shaderBinding.buffer;
+                    break;
+                case BindingInfoType::Sampler:
+                    entry.sampler = shaderBinding.sampler;
+                    break;
+                case BindingInfoType::Texture:
+                    entry.texture = shaderBinding.texture;
+                    break;
+                case BindingInfoType::StorageTexture:
+                    entry.storageTexture = shaderBinding.storageTexture;
+                    break;
+            }
             return entry;
         };
 
diff --git a/src/dawn_native/ProgrammablePassEncoder.cpp b/src/dawn_native/ProgrammablePassEncoder.cpp
index 9044ff1..2076296 100644
--- a/src/dawn_native/ProgrammablePassEncoder.cpp
+++ b/src/dawn_native/ProgrammablePassEncoder.cpp
@@ -32,52 +32,50 @@
                                          BindGroupBase* group) {
             for (BindingIndex bindingIndex{0}; bindingIndex < group->GetLayout()->GetBindingCount();
                  ++bindingIndex) {
-                wgpu::BindingType type = group->GetLayout()->GetBindingInfo(bindingIndex).type;
+                const BindingInfo& bindingInfo = group->GetLayout()->GetBindingInfo(bindingIndex);
 
-                switch (type) {
-                    case wgpu::BindingType::UniformBuffer: {
+                switch (bindingInfo.bindingType) {
+                    case BindingInfoType::Buffer: {
                         BufferBase* buffer = group->GetBindingAsBufferBinding(bindingIndex).buffer;
-                        usageTracker->BufferUsedAs(buffer, wgpu::BufferUsage::Uniform);
+                        switch (bindingInfo.buffer.type) {
+                            case wgpu::BufferBindingType::Uniform:
+                                usageTracker->BufferUsedAs(buffer, wgpu::BufferUsage::Uniform);
+                                break;
+                            case wgpu::BufferBindingType::Storage:
+                                usageTracker->BufferUsedAs(buffer, wgpu::BufferUsage::Storage);
+                                break;
+                            case wgpu::BufferBindingType::ReadOnlyStorage:
+                                usageTracker->BufferUsedAs(buffer, kReadOnlyStorageBuffer);
+                                break;
+                            case wgpu::BufferBindingType::Undefined:
+                                UNREACHABLE();
+                        }
                         break;
                     }
 
-                    case wgpu::BindingType::StorageBuffer: {
-                        BufferBase* buffer = group->GetBindingAsBufferBinding(bindingIndex).buffer;
-                        usageTracker->BufferUsedAs(buffer, wgpu::BufferUsage::Storage);
-                        break;
-                    }
-
-                    case wgpu::BindingType::SampledTexture:
-                    case wgpu::BindingType::MultisampledTexture: {
+                    case BindingInfoType::Texture: {
                         TextureViewBase* view = group->GetBindingAsTextureView(bindingIndex);
                         usageTracker->TextureViewUsedAs(view, wgpu::TextureUsage::Sampled);
                         break;
                     }
 
-                    case wgpu::BindingType::ReadonlyStorageBuffer: {
-                        BufferBase* buffer = group->GetBindingAsBufferBinding(bindingIndex).buffer;
-                        usageTracker->BufferUsedAs(buffer, kReadOnlyStorageBuffer);
-                        break;
-                    }
-
-                    case wgpu::BindingType::Sampler:
-                    case wgpu::BindingType::ComparisonSampler:
-                        break;
-
-                    case wgpu::BindingType::ReadonlyStorageTexture: {
+                    case BindingInfoType::StorageTexture: {
                         TextureViewBase* view = group->GetBindingAsTextureView(bindingIndex);
-                        usageTracker->TextureViewUsedAs(view, kReadonlyStorageTexture);
+                        switch (bindingInfo.storageTexture.access) {
+                            case wgpu::StorageTextureAccess::ReadOnly:
+                                usageTracker->TextureViewUsedAs(view, kReadonlyStorageTexture);
+                                break;
+                            case wgpu::StorageTextureAccess::WriteOnly:
+                                usageTracker->TextureViewUsedAs(view, wgpu::TextureUsage::Storage);
+                                break;
+                            case wgpu::StorageTextureAccess::Undefined:
+                                UNREACHABLE();
+                        }
                         break;
                     }
 
-                    case wgpu::BindingType::WriteonlyStorageTexture: {
-                        TextureViewBase* view = group->GetBindingAsTextureView(bindingIndex);
-                        usageTracker->TextureViewUsedAs(view, wgpu::TextureUsage::Storage);
+                    case BindingInfoType::Sampler:
                         break;
-                    }
-
-                    case wgpu::BindingType::Undefined:
-                        UNREACHABLE();
                 }
             }
         }
@@ -158,16 +156,8 @@
 
                     // BGL creation sorts bindings such that the dynamic buffer bindings are first.
                     // ASSERT that this true.
-                    ASSERT(bindingInfo.hasDynamicOffset);
-                    switch (bindingInfo.type) {
-                        case wgpu::BindingType::UniformBuffer:
-                        case wgpu::BindingType::StorageBuffer:
-                        case wgpu::BindingType::ReadonlyStorageBuffer:
-                            break;
-                        default:
-                            UNREACHABLE();
-                            break;
-                    }
+                    ASSERT(bindingInfo.bindingType == BindingInfoType::Buffer);
+                    ASSERT(bindingInfo.buffer.hasDynamicOffset);
 
                     if (dynamicOffsets[i] % kMinDynamicBufferOffsetAlignment != 0) {
                         return DAWN_VALIDATION_ERROR("Dynamic Buffer Offset need to be aligned");
diff --git a/src/dawn_native/ShaderModule.cpp b/src/dawn_native/ShaderModule.cpp
index ecd7454..df68e3c 100644
--- a/src/dawn_native/ShaderModule.cpp
+++ b/src/dawn_native/ShaderModule.cpp
@@ -261,7 +261,7 @@
             for (BindingIndex bindingIndex{0}; bindingIndex < layout->GetBufferCount();
                  ++bindingIndex) {
                 const BindingInfo& bindingInfo = layout->GetBindingInfo(bindingIndex);
-                if (bindingInfo.minBufferBindingSize != 0) {
+                if (bindingInfo.buffer.minBindingSize != 0) {
                     // Skip bindings that have minimum buffer size set in the layout
                     continue;
                 }
@@ -269,7 +269,7 @@
                 ASSERT(packedIdx < requiredBufferSizes.size());
                 const auto& shaderInfo = shaderBindings.find(bindingInfo.binding);
                 if (shaderInfo != shaderBindings.end()) {
-                    requiredBufferSizes[packedIdx] = shaderInfo->second.minBufferBindingSize;
+                    requiredBufferSizes[packedIdx] = shaderInfo->second.buffer.minBindingSize;
                 } else {
                     // We have to include buffers if they are included in the bind group's
                     // packed vector. We don't actually need to check these at draw time, so
@@ -340,30 +340,10 @@
                 BindingIndex bindingIndex(bindingIt->second);
                 const BindingInfo& layoutInfo = layout->GetBindingInfo(bindingIndex);
 
-                if (layoutInfo.type != shaderInfo.type) {
-                    // Binding mismatch between shader and bind group is invalid. For example, a
-                    // writable binding in the shader with a readonly storage buffer in the bind
-                    // group layout is invalid. However, a readonly binding in the shader with a
-                    // writable storage buffer in the bind group layout is valid.
-                    bool validBindingConversion =
-                        layoutInfo.type == wgpu::BindingType::StorageBuffer &&
-                        shaderInfo.type == wgpu::BindingType::ReadonlyStorageBuffer;
-
-                    // TODO(crbug.com/dawn/367): Temporarily allow using either a sampler or a
-                    // comparison sampler until we can perform the proper shader analysis of what
-                    // type is used in the shader module.
-                    validBindingConversion |=
-                        (layoutInfo.type == wgpu::BindingType::Sampler &&
-                         shaderInfo.type == wgpu::BindingType::ComparisonSampler);
-                    validBindingConversion |=
-                        (layoutInfo.type == wgpu::BindingType::ComparisonSampler &&
-                         shaderInfo.type == wgpu::BindingType::Sampler);
-
-                    if (!validBindingConversion) {
-                        return DAWN_VALIDATION_ERROR(
-                            "The binding type of the bind group layout entry conflicts " +
-                            GetShaderDeclarationString(group, bindingNumber));
-                    }
+                if (layoutInfo.bindingType != shaderInfo.bindingType) {
+                    return DAWN_VALIDATION_ERROR(
+                        "The binding type of the bind group layout entry conflicts " +
+                        GetShaderDeclarationString(group, bindingNumber));
                 }
 
                 if ((layoutInfo.visibility & StageBit(entryPoint.stage)) == 0) {
@@ -372,49 +352,77 @@
                                                  " is not visible for the shader stage");
                 }
 
-                switch (layoutInfo.type) {
-                    case wgpu::BindingType::SampledTexture:
-                    case wgpu::BindingType::MultisampledTexture: {
-                        if (layoutInfo.textureComponentType != shaderInfo.textureComponentType) {
+                switch (layoutInfo.bindingType) {
+                    case BindingInfoType::Texture: {
+                        if (layoutInfo.texture.multisampled != shaderInfo.texture.multisampled) {
                             return DAWN_VALIDATION_ERROR(
-                                "The textureComponentType of the bind group layout entry is "
+                                "The texture multisampled flag of the bind group layout entry is "
                                 "different from " +
                                 GetShaderDeclarationString(group, bindingNumber));
                         }
 
-                        if (layoutInfo.viewDimension != shaderInfo.viewDimension) {
+                        if (layoutInfo.texture.sampleType != shaderInfo.texture.sampleType) {
                             return DAWN_VALIDATION_ERROR(
-                                "The viewDimension of the bind group layout entry is different "
+                                "The texture sampleType of the bind group layout entry is "
+                                "different from " +
+                                GetShaderDeclarationString(group, bindingNumber));
+                        }
+
+                        if (layoutInfo.texture.viewDimension != shaderInfo.texture.viewDimension) {
+                            return DAWN_VALIDATION_ERROR(
+                                "The texture viewDimension of the bind group layout entry is "
+                                "different "
                                 "from " +
                                 GetShaderDeclarationString(group, bindingNumber));
                         }
                         break;
                     }
 
-                    case wgpu::BindingType::ReadonlyStorageTexture:
-                    case wgpu::BindingType::WriteonlyStorageTexture: {
-                        ASSERT(layoutInfo.storageTextureFormat != wgpu::TextureFormat::Undefined);
-                        ASSERT(shaderInfo.storageTextureFormat != wgpu::TextureFormat::Undefined);
-                        if (layoutInfo.storageTextureFormat != shaderInfo.storageTextureFormat) {
+                    case BindingInfoType::StorageTexture: {
+                        ASSERT(layoutInfo.storageTexture.format != wgpu::TextureFormat::Undefined);
+                        ASSERT(shaderInfo.storageTexture.format != wgpu::TextureFormat::Undefined);
+
+                        if (layoutInfo.storageTexture.access != shaderInfo.storageTexture.access) {
                             return DAWN_VALIDATION_ERROR(
-                                "The storageTextureFormat of the bind group layout entry is "
+                                "The storageTexture access mode of the bind group layout entry is "
                                 "different from " +
                                 GetShaderDeclarationString(group, bindingNumber));
                         }
-                        if (layoutInfo.viewDimension != shaderInfo.viewDimension) {
+
+                        if (layoutInfo.storageTexture.format != shaderInfo.storageTexture.format) {
                             return DAWN_VALIDATION_ERROR(
-                                "The viewDimension of the bind group layout entry is different "
-                                "from " +
+                                "The storageTexture format of the bind group layout entry is "
+                                "different from " +
+                                GetShaderDeclarationString(group, bindingNumber));
+                        }
+                        if (layoutInfo.storageTexture.viewDimension !=
+                            shaderInfo.storageTexture.viewDimension) {
+                            return DAWN_VALIDATION_ERROR(
+                                "The storageTexture viewDimension of the bind group layout entry "
+                                "is different from " +
                                 GetShaderDeclarationString(group, bindingNumber));
                         }
                         break;
                     }
 
-                    case wgpu::BindingType::UniformBuffer:
-                    case wgpu::BindingType::ReadonlyStorageBuffer:
-                    case wgpu::BindingType::StorageBuffer: {
-                        if (layoutInfo.minBufferBindingSize != 0 &&
-                            shaderInfo.minBufferBindingSize > layoutInfo.minBufferBindingSize) {
+                    case BindingInfoType::Buffer: {
+                        // Binding mismatch between shader and bind group is invalid. For example, a
+                        // writable binding in the shader with a readonly storage buffer in the bind
+                        // group layout is invalid. However, a readonly binding in the shader with a
+                        // writable storage buffer in the bind group layout is valid.
+                        bool validBindingConversion =
+                            layoutInfo.buffer.type == wgpu::BufferBindingType::Storage &&
+                            shaderInfo.buffer.type == wgpu::BufferBindingType::ReadOnlyStorage;
+
+                        if (layoutInfo.buffer.type != shaderInfo.buffer.type &&
+                            !validBindingConversion) {
+                            return DAWN_VALIDATION_ERROR(
+                                "The buffer type of the bind group layout entry conflicts " +
+                                GetShaderDeclarationString(group, bindingNumber));
+                        }
+
+                        if (layoutInfo.buffer.minBindingSize != 0 &&
+                            shaderInfo.buffer.minBindingSize > layoutInfo.buffer.minBindingSize) {
                             return DAWN_VALIDATION_ERROR(
                                 "The minimum buffer size of the bind group layout entry is smaller "
                                 "than " +
@@ -422,12 +430,12 @@
                         }
                         break;
                     }
-                    case wgpu::BindingType::Sampler:
-                    case wgpu::BindingType::ComparisonSampler:
-                        break;
 
-                    case wgpu::BindingType::Undefined:
-                        UNREACHABLE();
+                    case BindingInfoType::Sampler:
+                        // TODO(crbug.com/dawn/367): Temporarily allow using either a sampler or a
+                        // comparison sampler until we can perform the proper shader analysis of
+                        // what type is used in the shader module.
+                        break;
                 }
             }
 
@@ -458,8 +466,9 @@
             auto ExtractResourcesBinding =
                 [](const DeviceBase* device,
                    const spirv_cross::SmallVector<spirv_cross::Resource>& resources,
-                   const spirv_cross::Compiler& compiler, wgpu::BindingType bindingType,
-                   EntryPointMetadata::BindingInfo* metadataBindings) -> MaybeError {
+                   const spirv_cross::Compiler& compiler, BindingInfoType bindingType,
+                   EntryPointMetadata::BindingInfo* metadataBindings,
+                   bool isStorageBuffer = false) -> MaybeError {
                 for (const auto& resource : resources) {
                     if (!compiler.get_decoration_bitset(resource.id).get(spv::DecorationBinding)) {
                         return DAWN_VALIDATION_ERROR("No Binding decoration set for resource");
@@ -488,67 +497,64 @@
                     EntryPointMetadata::ShaderBindingInfo* info = &it.first->second;
                     info->id = resource.id;
                     info->base_type_id = resource.base_type_id;
-
-                    if (bindingType == wgpu::BindingType::UniformBuffer ||
-                        bindingType == wgpu::BindingType::StorageBuffer ||
-                        bindingType == wgpu::BindingType::ReadonlyStorageBuffer) {
-                        // Determine buffer size, with a minimum of 1 element in the runtime array
-                        spirv_cross::SPIRType type = compiler.get_type(info->base_type_id);
-                        info->minBufferBindingSize =
-                            compiler.get_declared_struct_size_runtime_array(type, 1);
-                    }
+                    info->bindingType = bindingType;
 
                     switch (bindingType) {
-                        case wgpu::BindingType::SampledTexture: {
+                        case BindingInfoType::Texture: {
                             spirv_cross::SPIRType::ImageType imageType =
                                 compiler.get_type(info->base_type_id).image;
                             spirv_cross::SPIRType::BaseType textureComponentType =
                                 compiler.get_type(imageType.type).basetype;
 
-                            info->viewDimension =
+                            info->texture.viewDimension =
                                 SpirvDimToTextureViewDimension(imageType.dim, imageType.arrayed);
-                            info->textureComponentType =
-                                SpirvBaseTypeToTextureComponentType(textureComponentType);
-
-                            if (imageType.ms) {
-                                info->type = wgpu::BindingType::MultisampledTexture;
-                            } else {
-                                info->type = wgpu::BindingType::SampledTexture;
-                            }
+                            info->texture.sampleType =
+                                SpirvBaseTypeToTextureSampleType(textureComponentType);
+                            info->texture.multisampled = imageType.ms;
 
                             if (imageType.depth) {
                                 if (imageType.ms) {
                                     return DAWN_VALIDATION_ERROR(
                                         "Multisampled depth textures aren't supported");
                                 }
-                                if (info->textureComponentType !=
-                                    wgpu::TextureComponentType::Float) {
+                                if (info->texture.sampleType != wgpu::TextureSampleType::Float) {
                                     return DAWN_VALIDATION_ERROR(
                                         "Depth textures must have a float type");
                                 }
-                                info->textureComponentType =
-                                    wgpu::TextureComponentType::DepthComparison;
+                                info->texture.sampleType = wgpu::TextureSampleType::Depth;
                             }
                             break;
                         }
-                        case wgpu::BindingType::StorageBuffer: {
+                        case BindingInfoType::Buffer: {
+                            // Determine buffer size, with a minimum of 1 element in the runtime
+                            // array
+                            spirv_cross::SPIRType type = compiler.get_type(info->base_type_id);
+                            info->buffer.minBindingSize =
+                                compiler.get_declared_struct_size_runtime_array(type, 1);
+
                             // Differentiate between readonly storage bindings and writable ones
-                            // based on the NonWritable decoration
-                            spirv_cross::Bitset flags =
-                                compiler.get_buffer_block_flags(resource.id);
-                            if (flags.get(spv::DecorationNonWritable)) {
-                                info->type = wgpu::BindingType::ReadonlyStorageBuffer;
+                            // based on the NonWritable decoration.
+                            // TODO(dawn:527): Could isStorageBuffer be determined by calling
+                            // compiler.get_storage_class(resource.id)?
+                            if (isStorageBuffer) {
+                                spirv_cross::Bitset flags =
+                                    compiler.get_buffer_block_flags(resource.id);
+                                if (flags.get(spv::DecorationNonWritable)) {
+                                    info->buffer.type = wgpu::BufferBindingType::ReadOnlyStorage;
+                                } else {
+                                    info->buffer.type = wgpu::BufferBindingType::Storage;
+                                }
                             } else {
-                                info->type = wgpu::BindingType::StorageBuffer;
+                                info->buffer.type = wgpu::BufferBindingType::Uniform;
                             }
                             break;
                         }
-                        case wgpu::BindingType::ReadonlyStorageTexture: {
+                        case BindingInfoType::StorageTexture: {
                             spirv_cross::Bitset flags = compiler.get_decoration_bitset(resource.id);
                             if (flags.get(spv::DecorationNonReadable)) {
-                                info->type = wgpu::BindingType::WriteonlyStorageTexture;
+                                info->storageTexture.access = wgpu::StorageTextureAccess::WriteOnly;
                             } else if (flags.get(spv::DecorationNonWritable)) {
-                                info->type = wgpu::BindingType::ReadonlyStorageTexture;
+                                info->storageTexture.access = wgpu::StorageTextureAccess::ReadOnly;
                             } else {
                                 return DAWN_VALIDATION_ERROR(
                                     "Read-write storage textures are not supported");
@@ -576,33 +582,30 @@
                                 return DAWN_VALIDATION_ERROR(
                                     "Depth storage textures aren't supported");
                             }
-                            info->storageTextureFormat = storageTextureFormat;
-                            info->viewDimension =
+                            info->storageTexture.format = storageTextureFormat;
+                            info->storageTexture.viewDimension =
                                 SpirvDimToTextureViewDimension(imageType.dim, imageType.arrayed);
                             break;
                         }
-                        default:
-                            info->type = bindingType;
+                        case BindingInfoType::Sampler: {
+                            info->sampler.type = wgpu::SamplerBindingType::Filtering;
+                        }
                     }
                 }
                 return {};
             };
 
             DAWN_TRY(ExtractResourcesBinding(device, resources.uniform_buffers, compiler,
-                                             wgpu::BindingType::UniformBuffer,
-                                             &metadata->bindings));
+                                             BindingInfoType::Buffer, &metadata->bindings));
             DAWN_TRY(ExtractResourcesBinding(device, resources.separate_images, compiler,
-                                             wgpu::BindingType::SampledTexture,
-                                             &metadata->bindings));
+                                             BindingInfoType::Texture, &metadata->bindings));
             DAWN_TRY(ExtractResourcesBinding(device, resources.separate_samplers, compiler,
-                                             wgpu::BindingType::Sampler, &metadata->bindings));
+                                             BindingInfoType::Sampler, &metadata->bindings));
             DAWN_TRY(ExtractResourcesBinding(device, resources.storage_buffers, compiler,
-                                             wgpu::BindingType::StorageBuffer,
-                                             &metadata->bindings));
+                                             BindingInfoType::Buffer, &metadata->bindings, true));
             // ReadonlyStorageTexture is used as a tag to do general storage texture handling.
             DAWN_TRY(ExtractResourcesBinding(device, resources.storage_images, compiler,
-                                             wgpu::BindingType::ReadonlyStorageTexture,
-                                             &metadata->bindings));
+                                             BindingInfoType::StorageTexture, &metadata->bindings));
 
             // Extract the vertex attributes
             if (stage == SingleShaderStage::Vertex) {
diff --git a/src/dawn_native/ShaderModule.h b/src/dawn_native/ShaderModule.h
index 2126dda..bedeb22 100644
--- a/src/dawn_native/ShaderModule.h
+++ b/src/dawn_native/ShaderModule.h
@@ -101,7 +101,6 @@
 
           private:
             // Disallow access to unused members.
-            using BindingInfo::hasDynamicOffset;
             using BindingInfo::visibility;
         };
 
diff --git a/src/dawn_native/SpirvUtils.cpp b/src/dawn_native/SpirvUtils.cpp
index 511bade..fafd6c2 100644
--- a/src/dawn_native/SpirvUtils.cpp
+++ b/src/dawn_native/SpirvUtils.cpp
@@ -148,4 +148,18 @@
         }
     }
 
+    wgpu::TextureSampleType SpirvBaseTypeToTextureSampleType(
+        spirv_cross::SPIRType::BaseType spirvBaseType) {
+        switch (spirvBaseType) {
+            case spirv_cross::SPIRType::Float:
+                return wgpu::TextureSampleType::Float;
+            case spirv_cross::SPIRType::Int:
+                return wgpu::TextureSampleType::Sint;
+            case spirv_cross::SPIRType::UInt:
+                return wgpu::TextureSampleType::Uint;
+            default:
+                UNREACHABLE();
+        }
+    }
+
 }  // namespace dawn_native
diff --git a/src/dawn_native/SpirvUtils.h b/src/dawn_native/SpirvUtils.h
index cd39c04..9ed94a5 100644
--- a/src/dawn_native/SpirvUtils.h
+++ b/src/dawn_native/SpirvUtils.h
@@ -39,6 +39,8 @@
     // Returns the format "component type" corresponding to the SPIRV base type.
     wgpu::TextureComponentType SpirvBaseTypeToTextureComponentType(
         spirv_cross::SPIRType::BaseType spirvBaseType);
+    wgpu::TextureSampleType SpirvBaseTypeToTextureSampleType(
+        spirv_cross::SPIRType::BaseType spirvBaseType);
 
 }  // namespace dawn_native
 
diff --git a/src/dawn_native/d3d12/BindGroupD3D12.cpp b/src/dawn_native/d3d12/BindGroupD3D12.cpp
index 9370dc3..a410807 100644
--- a/src/dawn_native/d3d12/BindGroupD3D12.cpp
+++ b/src/dawn_native/d3d12/BindGroupD3D12.cpp
@@ -53,71 +53,74 @@
 
             // Increment size does not need to be stored and is only used to get a handle
             // local to the allocation with OffsetFrom().
-            switch (bindingInfo.type) {
-                case wgpu::BindingType::UniformBuffer: {
+            switch (bindingInfo.bindingType) {
+                case BindingInfoType::Buffer: {
                     BufferBinding binding = GetBindingAsBufferBinding(bindingIndex);
 
-                    D3D12_CONSTANT_BUFFER_VIEW_DESC desc;
-                    // TODO(enga@google.com): investigate if this needs to be a constraint at
-                    // the API level
-                    desc.SizeInBytes = Align(binding.size, 256);
-                    desc.BufferLocation = ToBackend(binding.buffer)->GetVA() + binding.offset;
+                    switch (bindingInfo.buffer.type) {
+                        case wgpu::BufferBindingType::Uniform: {
+                            D3D12_CONSTANT_BUFFER_VIEW_DESC desc;
+                            // TODO(enga@google.com): investigate if this needs to be a constraint
+                            // at the API level
+                            desc.SizeInBytes = Align(binding.size, 256);
+                            desc.BufferLocation =
+                                ToBackend(binding.buffer)->GetVA() + binding.offset;
 
-                    d3d12Device->CreateConstantBufferView(
-                        &desc,
-                        viewAllocation.OffsetFrom(viewSizeIncrement, bindingOffsets[bindingIndex]));
-                    break;
-                }
-                case wgpu::BindingType::StorageBuffer: {
-                    BufferBinding binding = GetBindingAsBufferBinding(bindingIndex);
+                            d3d12Device->CreateConstantBufferView(
+                                &desc, viewAllocation.OffsetFrom(viewSizeIncrement,
+                                                                 bindingOffsets[bindingIndex]));
+                            break;
+                        }
+                        case wgpu::BufferBindingType::Storage: {
+                            // Since SPIRV-Cross outputs HLSL shaders with RWByteAddressBuffer,
+                            // we must use D3D12_BUFFER_UAV_FLAG_RAW when making the
+                            // UNORDERED_ACCESS_VIEW_DESC. Using D3D12_BUFFER_UAV_FLAG_RAW requires
+                            // that we use DXGI_FORMAT_R32_TYPELESS as the format of the view.
+                            // DXGI_FORMAT_R32_TYPELESS requires that the element size be 4
+                            // byte aligned. Since binding.size and binding.offset are in bytes,
+                            // we need to divide by 4 to obtain the element size.
+                            D3D12_UNORDERED_ACCESS_VIEW_DESC desc;
+                            desc.Buffer.NumElements = binding.size / 4;
+                            desc.Format = DXGI_FORMAT_R32_TYPELESS;
+                            desc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
+                            desc.Buffer.FirstElement = binding.offset / 4;
+                            desc.Buffer.StructureByteStride = 0;
+                            desc.Buffer.CounterOffsetInBytes = 0;
+                            desc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_RAW;
 
-                    // Since SPIRV-Cross outputs HLSL shaders with RWByteAddressBuffer,
-                    // we must use D3D12_BUFFER_UAV_FLAG_RAW when making the
-                    // UNORDERED_ACCESS_VIEW_DESC. Using D3D12_BUFFER_UAV_FLAG_RAW requires
-                    // that we use DXGI_FORMAT_R32_TYPELESS as the format of the view.
-                    // DXGI_FORMAT_R32_TYPELESS requires that the element size be 4
-                    // byte aligned. Since binding.size and binding.offset are in bytes,
-                    // we need to divide by 4 to obtain the element size.
-                    D3D12_UNORDERED_ACCESS_VIEW_DESC desc;
-                    desc.Buffer.NumElements = binding.size / 4;
-                    desc.Format = DXGI_FORMAT_R32_TYPELESS;
-                    desc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
-                    desc.Buffer.FirstElement = binding.offset / 4;
-                    desc.Buffer.StructureByteStride = 0;
-                    desc.Buffer.CounterOffsetInBytes = 0;
-                    desc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_RAW;
+                            d3d12Device->CreateUnorderedAccessView(
+                                ToBackend(binding.buffer)->GetD3D12Resource(), nullptr, &desc,
+                                viewAllocation.OffsetFrom(viewSizeIncrement,
+                                                          bindingOffsets[bindingIndex]));
+                            break;
+                        }
+                        case wgpu::BufferBindingType::ReadOnlyStorage: {
+                            // Like StorageBuffer, SPIRV-Cross outputs HLSL shaders for readonly
+                            // storage buffer with ByteAddressBuffer. So we must use
+                            // D3D12_BUFFER_SRV_FLAG_RAW when making the SRV descriptor. And it has
+                            // similar requirement for format, element size, etc.
+                            D3D12_SHADER_RESOURCE_VIEW_DESC desc;
+                            desc.Format = DXGI_FORMAT_R32_TYPELESS;
+                            desc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER;
+                            desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
+                            desc.Buffer.FirstElement = binding.offset / 4;
+                            desc.Buffer.NumElements = binding.size / 4;
+                            desc.Buffer.StructureByteStride = 0;
+                            desc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_RAW;
+                            d3d12Device->CreateShaderResourceView(
+                                ToBackend(binding.buffer)->GetD3D12Resource(), &desc,
+                                viewAllocation.OffsetFrom(viewSizeIncrement,
+                                                          bindingOffsets[bindingIndex]));
+                            break;
+                        }
+                        case wgpu::BufferBindingType::Undefined:
+                            UNREACHABLE();
+                    }
 
-                    d3d12Device->CreateUnorderedAccessView(
-                        ToBackend(binding.buffer)->GetD3D12Resource(), nullptr, &desc,
-                        viewAllocation.OffsetFrom(viewSizeIncrement, bindingOffsets[bindingIndex]));
-                    break;
-                }
-                case wgpu::BindingType::ReadonlyStorageBuffer: {
-                    BufferBinding binding = GetBindingAsBufferBinding(bindingIndex);
-
-                    // Like StorageBuffer, SPIRV-Cross outputs HLSL shaders for readonly storage
-                    // buffer with ByteAddressBuffer. So we must use D3D12_BUFFER_SRV_FLAG_RAW
-                    // when making the SRV descriptor. And it has similar requirement for
-                    // format, element size, etc.
-                    D3D12_SHADER_RESOURCE_VIEW_DESC desc;
-                    desc.Format = DXGI_FORMAT_R32_TYPELESS;
-                    desc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER;
-                    desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
-                    desc.Buffer.FirstElement = binding.offset / 4;
-                    desc.Buffer.NumElements = binding.size / 4;
-                    desc.Buffer.StructureByteStride = 0;
-                    desc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_RAW;
-                    d3d12Device->CreateShaderResourceView(
-                        ToBackend(binding.buffer)->GetD3D12Resource(), &desc,
-                        viewAllocation.OffsetFrom(viewSizeIncrement, bindingOffsets[bindingIndex]));
                     break;
                 }
 
-                case wgpu::BindingType::SampledTexture:
-                case wgpu::BindingType::MultisampledTexture:
-                // Readonly storage is implemented as SRV so it can be used at the same time as a
-                // sampled texture.
-                case wgpu::BindingType::ReadonlyStorageTexture: {
+                case BindingInfoType::Texture: {
                     auto* view = ToBackend(GetBindingAsTextureView(bindingIndex));
                     auto& srv = view->GetSRVDescriptor();
                     d3d12Device->CreateShaderResourceView(
@@ -125,23 +128,42 @@
                         viewAllocation.OffsetFrom(viewSizeIncrement, bindingOffsets[bindingIndex]));
                     break;
                 }
-                case wgpu::BindingType::Sampler:
-                case wgpu::BindingType::ComparisonSampler: {
+
+                case BindingInfoType::StorageTexture: {
+                    TextureView* view = ToBackend(GetBindingAsTextureView(bindingIndex));
+
+                    switch (bindingInfo.storageTexture.access) {
+                        case wgpu::StorageTextureAccess::ReadOnly: {
+                            // Readonly storage is implemented as SRV so it can be used at the same
+                            // time as a sampled texture.
+                            auto& srv = view->GetSRVDescriptor();
+                            d3d12Device->CreateShaderResourceView(
+                                ToBackend(view->GetTexture())->GetD3D12Resource(), &srv,
+                                viewAllocation.OffsetFrom(viewSizeIncrement,
+                                                          bindingOffsets[bindingIndex]));
+                            break;
+                        }
+
+                        case wgpu::StorageTextureAccess::WriteOnly: {
+                            D3D12_UNORDERED_ACCESS_VIEW_DESC uav = view->GetUAVDescriptor();
+                            d3d12Device->CreateUnorderedAccessView(
+                                ToBackend(view->GetTexture())->GetD3D12Resource(), nullptr, &uav,
+                                viewAllocation.OffsetFrom(viewSizeIncrement,
+                                                          bindingOffsets[bindingIndex]));
+                            break;
+                        }
+
+                        case wgpu::StorageTextureAccess::Undefined:
+                            UNREACHABLE();
+                    }
+
+                    break;
+                }
+
+                case BindingInfoType::Sampler: {
                     // No-op as samplers will be later initialized by CreateSamplers().
                     break;
                 }
-
-                case wgpu::BindingType::WriteonlyStorageTexture: {
-                    TextureView* view = ToBackend(GetBindingAsTextureView(bindingIndex));
-                    D3D12_UNORDERED_ACCESS_VIEW_DESC uav = view->GetUAVDescriptor();
-                    d3d12Device->CreateUnorderedAccessView(
-                        ToBackend(view->GetTexture())->GetD3D12Resource(), nullptr, &uav,
-                        viewAllocation.OffsetFrom(viewSizeIncrement, bindingOffsets[bindingIndex]));
-                    break;
-                }
-
-                case wgpu::BindingType::Undefined:
-                    UNREACHABLE();
             }
         }
     }
diff --git a/src/dawn_native/d3d12/BindGroupLayoutD3D12.cpp b/src/dawn_native/d3d12/BindGroupLayoutD3D12.cpp
index dae4786..449a07a 100644
--- a/src/dawn_native/d3d12/BindGroupLayoutD3D12.cpp
+++ b/src/dawn_native/d3d12/BindGroupLayoutD3D12.cpp
@@ -22,24 +22,36 @@
 
 namespace dawn_native { namespace d3d12 {
     namespace {
-        BindGroupLayout::DescriptorType WGPUBindingTypeToDescriptorType(
-            wgpu::BindingType bindingType) {
-            switch (bindingType) {
-                case wgpu::BindingType::UniformBuffer:
-                    return BindGroupLayout::DescriptorType::CBV;
-                case wgpu::BindingType::StorageBuffer:
-                case wgpu::BindingType::WriteonlyStorageTexture:
-                    return BindGroupLayout::DescriptorType::UAV;
-                case wgpu::BindingType::SampledTexture:
-                case wgpu::BindingType::MultisampledTexture:
-                case wgpu::BindingType::ReadonlyStorageBuffer:
-                case wgpu::BindingType::ReadonlyStorageTexture:
-                    return BindGroupLayout::DescriptorType::SRV;
-                case wgpu::BindingType::Sampler:
-                case wgpu::BindingType::ComparisonSampler:
+        BindGroupLayout::DescriptorType WGPUBindingInfoToDescriptorType(
+            const BindingInfo& bindingInfo) {
+            switch (bindingInfo.bindingType) {
+                case BindingInfoType::Buffer:
+                    switch (bindingInfo.buffer.type) {
+                        case wgpu::BufferBindingType::Uniform:
+                            return BindGroupLayout::DescriptorType::CBV;
+                        case wgpu::BufferBindingType::Storage:
+                            return BindGroupLayout::DescriptorType::UAV;
+                        case wgpu::BufferBindingType::ReadOnlyStorage:
+                            return BindGroupLayout::DescriptorType::SRV;
+                        case wgpu::BufferBindingType::Undefined:
+                            UNREACHABLE();
+                    }
+
+                case BindingInfoType::Sampler:
                     return BindGroupLayout::DescriptorType::Sampler;
-                case wgpu::BindingType::Undefined:
-                    UNREACHABLE();
+
+                case BindingInfoType::Texture:
+                    return BindGroupLayout::DescriptorType::SRV;
+
+                case BindingInfoType::StorageTexture:
+                    switch (bindingInfo.storageTexture.access) {
+                        case wgpu::StorageTextureAccess::ReadOnly:
+                            return BindGroupLayout::DescriptorType::SRV;
+                        case wgpu::StorageTextureAccess::WriteOnly:
+                            return BindGroupLayout::DescriptorType::UAV;
+                        case wgpu::StorageTextureAccess::Undefined:
+                            UNREACHABLE();
+                    }
             }
         }
     }  // anonymous namespace
@@ -57,9 +69,9 @@
             // 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);
+            ASSERT(!bindingInfo.buffer.hasDynamicOffset);
 
-            DescriptorType descriptorType = WGPUBindingTypeToDescriptorType(bindingInfo.type);
+            DescriptorType descriptorType = WGPUBindingInfoToDescriptorType(bindingInfo);
             mBindingOffsets[bindingIndex] = mDescriptorCounts[descriptorType]++;
         }
 
@@ -107,31 +119,17 @@
         for (BindingIndex bindingIndex{0}; bindingIndex < GetBindingCount(); ++bindingIndex) {
             const BindingInfo& bindingInfo = GetBindingInfo(bindingIndex);
 
-            if (bindingInfo.hasDynamicOffset) {
+            if (bindingInfo.bindingType == BindingInfoType::Buffer &&
+                bindingInfo.buffer.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 (bindingInfo.type) {
-                    case wgpu::BindingType::UniformBuffer:
-                    case wgpu::BindingType::StorageBuffer:
-                    case wgpu::BindingType::ReadonlyStorageBuffer:
-                        mBindingOffsets[bindingIndex] = baseRegister++;
-                        break;
-                    case wgpu::BindingType::SampledTexture:
-                    case wgpu::BindingType::MultisampledTexture:
-                    case wgpu::BindingType::Sampler:
-                    case wgpu::BindingType::ComparisonSampler:
-                    case wgpu::BindingType::ReadonlyStorageTexture:
-                    case wgpu::BindingType::WriteonlyStorageTexture:
-                    case wgpu::BindingType::Undefined:
-                        UNREACHABLE();
-                        break;
-                }
+                mBindingOffsets[bindingIndex] = baseRegister++;
                 continue;
             }
 
             // TODO(shaobo.yan@intel.com): Implement dynamic buffer offset.
-            DescriptorType descriptorType = WGPUBindingTypeToDescriptorType(bindingInfo.type);
+            DescriptorType descriptorType = WGPUBindingInfoToDescriptorType(bindingInfo);
             mBindingOffsets[bindingIndex] += descriptorOffsets[descriptorType];
         }
 
diff --git a/src/dawn_native/d3d12/CommandBufferD3D12.cpp b/src/dawn_native/d3d12/CommandBufferD3D12.cpp
index 02a2c18..2191665 100644
--- a/src/dawn_native/d3d12/CommandBufferD3D12.cpp
+++ b/src/dawn_native/d3d12/CommandBufferD3D12.cpp
@@ -223,51 +223,57 @@
                 for (BindGroupIndex index : IterateBitSet(mBindGroupLayoutsMask)) {
                     BindGroupLayoutBase* layout = mBindGroups[index]->GetLayout();
                     for (BindingIndex binding{0}; binding < layout->GetBindingCount(); ++binding) {
-                        switch (layout->GetBindingInfo(binding).type) {
-                            case wgpu::BindingType::StorageBuffer: {
+                        const BindingInfo& bindingInfo = layout->GetBindingInfo(binding);
+                        switch (bindingInfo.bindingType) {
+                            case BindingInfoType::Buffer: {
                                 D3D12_RESOURCE_BARRIER barrier;
+                                wgpu::BufferUsage usage;
+                                switch (bindingInfo.buffer.type) {
+                                    case wgpu::BufferBindingType::Uniform:
+                                        usage = wgpu::BufferUsage::Uniform;
+                                        break;
+                                    case wgpu::BufferBindingType::Storage:
+                                        usage = wgpu::BufferUsage::Storage;
+                                        break;
+                                    case wgpu::BufferBindingType::ReadOnlyStorage:
+                                        usage = kReadOnlyStorageBuffer;
+                                        break;
+                                    case wgpu::BufferBindingType::Undefined:
+                                        UNREACHABLE();
+                                }
                                 if (ToBackend(mBindGroups[index]
                                                   ->GetBindingAsBufferBinding(binding)
                                                   .buffer)
-                                        ->TrackUsageAndGetResourceBarrier(
-                                            commandContext, &barrier, wgpu::BufferUsage::Storage)) {
+                                        ->TrackUsageAndGetResourceBarrier(commandContext, &barrier,
+                                                                          usage)) {
                                     barriers.push_back(barrier);
                                 }
                                 break;
                             }
 
-                            case wgpu::BindingType::ReadonlyStorageTexture: {
+                            case BindingInfoType::StorageTexture: {
                                 TextureViewBase* view =
                                     mBindGroups[index]->GetBindingAsTextureView(binding);
-                                ToBackend(view->GetTexture())
-                                    ->TransitionUsageAndGetResourceBarrier(
-                                        commandContext, &barriers, kReadonlyStorageTexture,
-                                        view->GetSubresourceRange());
-                                break;
-                            }
-                            case wgpu::BindingType::WriteonlyStorageTexture: {
-                                TextureViewBase* view =
-                                    mBindGroups[index]->GetBindingAsTextureView(binding);
-                                ToBackend(view->GetTexture())
-                                    ->TransitionUsageAndGetResourceBarrier(
-                                        commandContext, &barriers, wgpu::TextureUsage::Storage,
-                                        view->GetSubresourceRange());
-                                break;
-                            }
-                            case wgpu::BindingType::ReadonlyStorageBuffer: {
-                                D3D12_RESOURCE_BARRIER barrier;
-                                if (ToBackend(mBindGroups[index]
-                                                  ->GetBindingAsBufferBinding(binding)
-                                                  .buffer)
-                                        ->TrackUsageAndGetResourceBarrier(commandContext, &barrier,
-                                                                          kReadOnlyStorageBuffer)) {
-                                    barriers.push_back(barrier);
+                                wgpu::TextureUsage usage;
+                                switch (bindingInfo.storageTexture.access) {
+                                    case wgpu::StorageTextureAccess::ReadOnly:
+                                        usage = kReadonlyStorageTexture;
+                                        break;
+                                    case wgpu::StorageTextureAccess::WriteOnly:
+                                        usage = wgpu::TextureUsage::Storage;
+                                        break;
+                                    case wgpu::StorageTextureAccess::Undefined:
+                                        UNREACHABLE();
                                 }
+                                ToBackend(view->GetTexture())
+                                    ->TransitionUsageAndGetResourceBarrier(
+                                        commandContext, &barriers, usage,
+                                        view->GetSubresourceRange());
                                 break;
                             }
-                            case wgpu::BindingType::SampledTexture:
-                            case wgpu::BindingType::MultisampledTexture: {
-                                    TextureViewBase* view =
+
+                            case BindingInfoType::Texture: {
+                                TextureViewBase* view =
                                     mBindGroups[index]->GetBindingAsTextureView(binding);
                                 ToBackend(view->GetTexture())
                                     ->TransitionUsageAndGetResourceBarrier(
@@ -275,25 +281,10 @@
                                         view->GetSubresourceRange());
                                 break;
                             }
-                            case wgpu::BindingType::UniformBuffer: {
-                                D3D12_RESOURCE_BARRIER barrier;
-                                if (ToBackend(mBindGroups[index]
-                                                  ->GetBindingAsBufferBinding(binding)
-                                                  .buffer)
-                                        ->TrackUsageAndGetResourceBarrier(
-                                            commandContext, &barrier, wgpu::BufferUsage::Uniform)) {
-                                    barriers.push_back(barrier);
-                                }
-                                break;
-                            }
 
-                            case wgpu::BindingType::Sampler:
-                            case wgpu::BindingType::ComparisonSampler:
+                            case BindingInfoType::Sampler:
                                 // Don't require barriers.
                                 break;
-
-                            case wgpu::BindingType::Undefined:
-                                UNREACHABLE();
                         }
                     }
                 }
@@ -353,8 +344,9 @@
                     D3D12_GPU_VIRTUAL_ADDRESS bufferLocation =
                         ToBackend(binding.buffer)->GetVA() + offset;
 
-                    switch (bindingInfo.type) {
-                        case wgpu::BindingType::UniformBuffer:
+                    ASSERT(bindingInfo.bindingType == BindingInfoType::Buffer);
+                    switch (bindingInfo.buffer.type) {
+                        case wgpu::BufferBindingType::Uniform:
                             if (mInCompute) {
                                 commandList->SetComputeRootConstantBufferView(parameterIndex,
                                                                               bufferLocation);
@@ -363,7 +355,7 @@
                                                                                bufferLocation);
                             }
                             break;
-                        case wgpu::BindingType::StorageBuffer:
+                        case wgpu::BufferBindingType::Storage:
                             if (mInCompute) {
                                 commandList->SetComputeRootUnorderedAccessView(parameterIndex,
                                                                                bufferLocation);
@@ -372,7 +364,7 @@
                                                                                 bufferLocation);
                             }
                             break;
-                        case wgpu::BindingType::ReadonlyStorageBuffer:
+                        case wgpu::BufferBindingType::ReadOnlyStorage:
                             if (mInCompute) {
                                 commandList->SetComputeRootShaderResourceView(parameterIndex,
                                                                               bufferLocation);
@@ -381,13 +373,7 @@
                                                                                bufferLocation);
                             }
                             break;
-                        case wgpu::BindingType::SampledTexture:
-                        case wgpu::BindingType::MultisampledTexture:
-                        case wgpu::BindingType::Sampler:
-                        case wgpu::BindingType::ComparisonSampler:
-                        case wgpu::BindingType::ReadonlyStorageTexture:
-                        case wgpu::BindingType::WriteonlyStorageTexture:
-                        case wgpu::BindingType::Undefined:
+                        case wgpu::BufferBindingType::Undefined:
                             UNREACHABLE();
                     }
                 }
diff --git a/src/dawn_native/d3d12/PipelineLayoutD3D12.cpp b/src/dawn_native/d3d12/PipelineLayoutD3D12.cpp
index bf5c039..f5e21db 100644
--- a/src/dawn_native/d3d12/PipelineLayoutD3D12.cpp
+++ b/src/dawn_native/d3d12/PipelineLayoutD3D12.cpp
@@ -40,21 +40,15 @@
             return D3D12_SHADER_VISIBILITY_ALL;
         }
 
-        D3D12_ROOT_PARAMETER_TYPE RootParameterType(wgpu::BindingType type) {
+        D3D12_ROOT_PARAMETER_TYPE RootParameterType(wgpu::BufferBindingType type) {
             switch (type) {
-                case wgpu::BindingType::UniformBuffer:
+                case wgpu::BufferBindingType::Uniform:
                     return D3D12_ROOT_PARAMETER_TYPE_CBV;
-                case wgpu::BindingType::StorageBuffer:
+                case wgpu::BufferBindingType::Storage:
                     return D3D12_ROOT_PARAMETER_TYPE_UAV;
-                case wgpu::BindingType::ReadonlyStorageBuffer:
+                case wgpu::BufferBindingType::ReadOnlyStorage:
                     return D3D12_ROOT_PARAMETER_TYPE_SRV;
-                case wgpu::BindingType::SampledTexture:
-                case wgpu::BindingType::MultisampledTexture:
-                case wgpu::BindingType::Sampler:
-                case wgpu::BindingType::ComparisonSampler:
-                case wgpu::BindingType::ReadonlyStorageTexture:
-                case wgpu::BindingType::WriteonlyStorageTexture:
-                case wgpu::BindingType::Undefined:
+                case wgpu::BufferBindingType::Undefined:
                     UNREACHABLE();
             }
         }
@@ -148,7 +142,7 @@
                 mDynamicRootParameterIndices[group][dynamicBindingIndex] = rootParameters.size();
 
                 // Set parameter types according to bind group layout descriptor.
-                rootParameter.ParameterType = RootParameterType(bindingInfo.type);
+                rootParameter.ParameterType = RootParameterType(bindingInfo.buffer.type);
 
                 // Set visibilities according to bind group layout descriptor.
                 rootParameter.ShaderVisibility = ShaderVisibilityType(bindingInfo.visibility);
@@ -196,7 +190,7 @@
                                                           BindingIndex bindingIndex) const {
         ASSERT(group < kMaxBindGroupsTyped);
         ASSERT(bindingIndex < kMaxDynamicBuffersPerPipelineLayoutTyped);
-        ASSERT(GetBindGroupLayout(group)->GetBindingInfo(bindingIndex).hasDynamicOffset);
+        ASSERT(GetBindGroupLayout(group)->GetBindingInfo(bindingIndex).buffer.hasDynamicOffset);
         ASSERT(GetBindGroupLayout(group)->GetBindingInfo(bindingIndex).visibility !=
                wgpu::ShaderStage::None);
         return mDynamicRootParameterIndices[group][bindingIndex];
diff --git a/src/dawn_native/d3d12/SamplerHeapCacheD3D12.cpp b/src/dawn_native/d3d12/SamplerHeapCacheD3D12.cpp
index 8ba3aab..d1d6f3f 100644
--- a/src/dawn_native/d3d12/SamplerHeapCacheD3D12.cpp
+++ b/src/dawn_native/d3d12/SamplerHeapCacheD3D12.cpp
@@ -103,8 +103,7 @@
         for (BindingIndex bindingIndex = bgl->GetDynamicBufferCount();
              bindingIndex < bgl->GetBindingCount(); ++bindingIndex) {
             const BindingInfo& bindingInfo = bgl->GetBindingInfo(bindingIndex);
-            if (bindingInfo.type == wgpu::BindingType::Sampler ||
-                bindingInfo.type == wgpu::BindingType::ComparisonSampler) {
+            if (bindingInfo.bindingType == BindingInfoType::Sampler) {
                 samplers.push_back(ToBackend(group->GetBindingAsSampler(bindingIndex)));
             }
         }
diff --git a/src/dawn_native/d3d12/ShaderModuleD3D12.cpp b/src/dawn_native/d3d12/ShaderModuleD3D12.cpp
index 8daf854..3c34203 100644
--- a/src/dawn_native/d3d12/ShaderModuleD3D12.cpp
+++ b/src/dawn_native/d3d12/ShaderModuleD3D12.cpp
@@ -277,8 +277,9 @@
                 // the BGL produces the wrong output. Force read-only storage buffer bindings to
                 // be treated as UAV instead of SRV.
                 const bool forceStorageBufferAsUAV =
-                    (bindingInfo.type == wgpu::BindingType::ReadonlyStorageBuffer &&
-                     bgl->GetBindingInfo(bindingIndex).type == wgpu::BindingType::StorageBuffer);
+                    (bindingInfo.buffer.type == wgpu::BufferBindingType::ReadOnlyStorage &&
+                     bgl->GetBindingInfo(bindingIndex).buffer.type ==
+                         wgpu::BufferBindingType::Storage);
 
                 uint32_t bindingOffset = bindingOffsets[bindingIndex];
                 compiler.set_decoration(bindingInfo.id, spv::DecorationBinding, bindingOffset);
diff --git a/src/dawn_native/metal/CommandBufferMTL.mm b/src/dawn_native/metal/CommandBufferMTL.mm
index 2e3c58b..aaf250e 100644
--- a/src/dawn_native/metal/CommandBufferMTL.mm
+++ b/src/dawn_native/metal/CommandBufferMTL.mm
@@ -379,10 +379,8 @@
                             SingleShaderStage::Compute)[index][bindingIndex];
                     }
 
-                    switch (bindingInfo.type) {
-                        case wgpu::BindingType::UniformBuffer:
-                        case wgpu::BindingType::StorageBuffer:
-                        case wgpu::BindingType::ReadonlyStorageBuffer: {
+                    switch (bindingInfo.bindingType) {
+                        case BindingInfoType::Buffer: {
                             const BufferBinding& binding =
                                 group->GetBindingAsBufferBinding(bindingIndex);
                             const id<MTLBuffer> buffer = ToBackend(binding.buffer)->GetMTLBuffer();
@@ -390,7 +388,7 @@
 
                             // TODO(shaobo.yan@intel.com): Record bound buffer status to use
                             // setBufferOffset to achieve better performance.
-                            if (bindingInfo.hasDynamicOffset) {
+                            if (bindingInfo.buffer.hasDynamicOffset) {
                                 offset += dynamicOffsets[currentDynamicBufferIndex];
                                 currentDynamicBufferIndex++;
                             }
@@ -423,8 +421,7 @@
                             break;
                         }
 
-                        case wgpu::BindingType::Sampler:
-                        case wgpu::BindingType::ComparisonSampler: {
+                        case BindingInfoType::Sampler: {
                             auto sampler = ToBackend(group->GetBindingAsSampler(bindingIndex));
                             if (hasVertStage) {
                                 [render setVertexSamplerState:sampler->GetMTLSamplerState()
@@ -441,10 +438,8 @@
                             break;
                         }
 
-                        case wgpu::BindingType::SampledTexture:
-                        case wgpu::BindingType::MultisampledTexture:
-                        case wgpu::BindingType::ReadonlyStorageTexture:
-                        case wgpu::BindingType::WriteonlyStorageTexture: {
+                        case BindingInfoType::Texture:
+                        case BindingInfoType::StorageTexture: {
                             auto textureView =
                                 ToBackend(group->GetBindingAsTextureView(bindingIndex));
                             if (hasVertStage) {
@@ -461,9 +456,6 @@
                             }
                             break;
                         }
-
-                        case wgpu::BindingType::Undefined:
-                            UNREACHABLE();
                     }
                 }
             }
diff --git a/src/dawn_native/metal/PipelineLayoutMTL.mm b/src/dawn_native/metal/PipelineLayoutMTL.mm
index f7efd8f7..3951ef5 100644
--- a/src/dawn_native/metal/PipelineLayoutMTL.mm
+++ b/src/dawn_native/metal/PipelineLayoutMTL.mm
@@ -39,27 +39,22 @@
                         continue;
                     }
 
-                    switch (bindingInfo.type) {
-                        case wgpu::BindingType::UniformBuffer:
-                        case wgpu::BindingType::StorageBuffer:
-                        case wgpu::BindingType::ReadonlyStorageBuffer:
+                    switch (bindingInfo.bindingType) {
+                        case BindingInfoType::Buffer:
                             mIndexInfo[stage][group][bindingIndex] = bufferIndex;
                             bufferIndex++;
                             break;
-                        case wgpu::BindingType::Sampler:
-                        case wgpu::BindingType::ComparisonSampler:
+
+                        case BindingInfoType::Sampler:
                             mIndexInfo[stage][group][bindingIndex] = samplerIndex;
                             samplerIndex++;
                             break;
-                        case wgpu::BindingType::SampledTexture:
-                        case wgpu::BindingType::MultisampledTexture:
-                        case wgpu::BindingType::ReadonlyStorageTexture:
-                        case wgpu::BindingType::WriteonlyStorageTexture:
+
+                        case BindingInfoType::Texture:
+                        case BindingInfoType::StorageTexture:
                             mIndexInfo[stage][group][bindingIndex] = textureIndex;
                             textureIndex++;
                             break;
-                        case wgpu::BindingType::Undefined:
-                            UNREACHABLE();
                     }
                 }
             }
diff --git a/src/dawn_native/opengl/BindGroupGL.cpp b/src/dawn_native/opengl/BindGroupGL.cpp
index 02c1af4..486eb2c 100644
--- a/src/dawn_native/opengl/BindGroupGL.cpp
+++ b/src/dawn_native/opengl/BindGroupGL.cpp
@@ -30,31 +30,15 @@
             ASSERT(bindingIndex < descriptor->layout->GetBindingCount());
 
             const BindingInfo& bindingInfo = descriptor->layout->GetBindingInfo(bindingIndex);
-            switch (bindingInfo.type) {
-                case wgpu::BindingType::ReadonlyStorageTexture:
-                case wgpu::BindingType::WriteonlyStorageTexture: {
-                    ASSERT(entry.textureView != nullptr);
-                    const uint32_t textureViewLayerCount = entry.textureView->GetLayerCount();
-                    if (textureViewLayerCount != 1 &&
-                        textureViewLayerCount !=
-                            entry.textureView->GetTexture()->GetArrayLayers()) {
-                        return DAWN_VALIDATION_ERROR(
-                            "Currently the OpenGL backend only supports either binding a layer or "
-                            "the entire texture as storage texture.");
-                    }
-                } break;
-
-                case wgpu::BindingType::UniformBuffer:
-                case wgpu::BindingType::StorageBuffer:
-                case wgpu::BindingType::ReadonlyStorageBuffer:
-                case wgpu::BindingType::SampledTexture:
-                case wgpu::BindingType::MultisampledTexture:
-                case wgpu::BindingType::Sampler:
-                case wgpu::BindingType::ComparisonSampler:
-                    break;
-
-                case wgpu::BindingType::Undefined:
-                    UNREACHABLE();
+            if (bindingInfo.bindingType == BindingInfoType::StorageTexture) {
+                ASSERT(entry.textureView != nullptr);
+                const uint32_t textureViewLayerCount = entry.textureView->GetLayerCount();
+                if (textureViewLayerCount != 1 &&
+                    textureViewLayerCount != entry.textureView->GetTexture()->GetArrayLayers()) {
+                    return DAWN_VALIDATION_ERROR(
+                        "Currently the OpenGL backend only supports either binding a layer or "
+                        "the entire texture as storage texture.");
+                }
             }
         }
 
diff --git a/src/dawn_native/opengl/CommandBufferGL.cpp b/src/dawn_native/opengl/CommandBufferGL.cpp
index fd5b00a..7d1a274 100644
--- a/src/dawn_native/opengl/CommandBufferGL.cpp
+++ b/src/dawn_native/opengl/CommandBufferGL.cpp
@@ -243,49 +243,43 @@
                     const BindingInfo& bindingInfo =
                         group->GetLayout()->GetBindingInfo(bindingIndex);
 
-                    switch (bindingInfo.type) {
-                        case wgpu::BindingType::UniformBuffer: {
+                    switch (bindingInfo.bindingType) {
+                        case BindingInfoType::Buffer: {
                             BufferBinding binding = group->GetBindingAsBufferBinding(bindingIndex);
                             GLuint buffer = ToBackend(binding.buffer)->GetHandle();
-                            GLuint uboIndex = indices[bindingIndex];
+                            GLuint index = indices[bindingIndex];
                             GLuint offset = binding.offset;
 
-                            if (bindingInfo.hasDynamicOffset) {
+                            if (bindingInfo.buffer.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;
+                            GLenum target;
+                            switch (bindingInfo.buffer.type) {
+                                case wgpu::BufferBindingType::Uniform:
+                                    target = GL_UNIFORM_BUFFER;
+                                    break;
+                                case wgpu::BufferBindingType::Storage:
+                                case wgpu::BufferBindingType::ReadOnlyStorage:
+                                    target = GL_SHADER_STORAGE_BUFFER;
+                                    break;
+                                case wgpu::BufferBindingType::Undefined:
+                                    UNREACHABLE();
                             }
 
-                            gl.BindBufferRange(GL_SHADER_STORAGE_BUFFER, ssboIndex, buffer, offset,
-                                               binding.size);
+                            gl.BindBufferRange(target, index, buffer, offset, binding.size);
                             break;
                         }
 
-                        case wgpu::BindingType::Sampler:
-                        case wgpu::BindingType::ComparisonSampler: {
+                        case BindingInfoType::Sampler: {
                             Sampler* sampler = ToBackend(group->GetBindingAsSampler(bindingIndex));
                             GLuint samplerIndex = indices[bindingIndex];
 
                             for (PipelineGL::SamplerUnit unit :
                                  mPipeline->GetTextureUnitsForSampler(samplerIndex)) {
-                                // Only use filtering for certain texture units, because int and
-                                // uint texture are only complete without filtering
+                                // Only use filtering for certain texture units, because int
+                                // and uint texture are only complete without filtering
                                 if (unit.shouldUseFiltering) {
                                     gl.BindSampler(unit.unit, sampler->GetFilteringHandle());
                                 } else {
@@ -295,8 +289,7 @@
                             break;
                         }
 
-                        case wgpu::BindingType::SampledTexture:
-                        case wgpu::BindingType::MultisampledTexture: {
+                        case BindingInfoType::Texture: {
                             TextureView* view =
                                 ToBackend(group->GetBindingAsTextureView(bindingIndex));
                             GLuint handle = view->GetHandle();
@@ -328,8 +321,7 @@
                             break;
                         }
 
-                        case wgpu::BindingType::ReadonlyStorageTexture:
-                        case wgpu::BindingType::WriteonlyStorageTexture: {
+                        case BindingInfoType::StorageTexture: {
                             TextureView* view =
                                 ToBackend(group->GetBindingAsTextureView(bindingIndex));
                             Texture* texture = ToBackend(view->GetTexture());
@@ -337,20 +329,19 @@
                             GLuint imageIndex = indices[bindingIndex];
 
                             GLenum access;
-                            switch (bindingInfo.type) {
-                                case wgpu::BindingType::ReadonlyStorageTexture:
+                            switch (bindingInfo.storageTexture.access) {
+                                case wgpu::StorageTextureAccess::ReadOnly:
                                     access = GL_READ_ONLY;
                                     break;
-                                case wgpu::BindingType::WriteonlyStorageTexture:
+                                case wgpu::StorageTextureAccess::WriteOnly:
                                     access = GL_WRITE_ONLY;
                                     break;
-
-                                default:
+                                case wgpu::StorageTextureAccess::Undefined:
                                     UNREACHABLE();
                             }
 
-                            // OpenGL ES only supports either binding a layer or the entire texture
-                            // in glBindImageTexture().
+                            // OpenGL ES only supports either binding a layer or the entire
+                            // texture in glBindImageTexture().
                             GLboolean isLayered;
                             if (view->GetLayerCount() == 1) {
                                 isLayered = GL_FALSE;
@@ -365,9 +356,6 @@
                                                 texture->GetGLFormat().internalFormat);
                             break;
                         }
-
-                        case wgpu::BindingType::Undefined:
-                            UNREACHABLE();
                     }
                 }
             }
diff --git a/src/dawn_native/opengl/PipelineGL.cpp b/src/dawn_native/opengl/PipelineGL.cpp
index 562f256..0039c1e 100644
--- a/src/dawn_native/opengl/PipelineGL.cpp
+++ b/src/dawn_native/opengl/PipelineGL.cpp
@@ -132,53 +132,53 @@
                 BindingIndex bindingIndex = it.second;
 
                 std::string name = GetBindingName(group, bindingNumber);
-                switch (bgl->GetBindingInfo(bindingIndex).type) {
-                    case wgpu::BindingType::UniformBuffer: {
-                        GLint location = gl.GetUniformBlockIndex(mProgram, name.c_str());
-                        if (location != -1) {
-                            gl.UniformBlockBinding(mProgram, location,
-                                                   indices[group][bindingIndex]);
-                        }
-                        break;
-                    }
-
-                    case wgpu::BindingType::StorageBuffer:
-                    case wgpu::BindingType::ReadonlyStorageBuffer: {
-                        // Since glShaderStorageBlockBinding doesn't exist in OpenGL ES, we skip
-                        // that call and handle it during shader translation by modifying the
-                        // location decoration.
-                        // Contrary to all other binding types, OpenGL ES's SSBO binding index in
-                        // the SSBO table is the value of the location= decoration in GLSL.
-                        if (gl.GetVersion().IsDesktop()) {
-                            GLuint location = gl.GetProgramResourceIndex(
-                                mProgram, GL_SHADER_STORAGE_BLOCK, name.c_str());
-                            if (location != GL_INVALID_INDEX) {
-                                gl.ShaderStorageBlockBinding(mProgram, location,
-                                                             indices[group][bindingIndex]);
+                const BindingInfo& bindingInfo = bgl->GetBindingInfo(bindingIndex);
+                switch (bindingInfo.bindingType) {
+                    case BindingInfoType::Buffer:
+                        switch (bindingInfo.buffer.type) {
+                            case wgpu::BufferBindingType::Uniform: {
+                                GLint location = gl.GetUniformBlockIndex(mProgram, name.c_str());
+                                if (location != -1) {
+                                    gl.UniformBlockBinding(mProgram, location,
+                                                           indices[group][bindingIndex]);
+                                }
+                                break;
                             }
+                            case wgpu::BufferBindingType::Storage:
+                            case wgpu::BufferBindingType::ReadOnlyStorage: {
+                                // Since glShaderStorageBlockBinding doesn't exist in OpenGL ES, we
+                                // skip that call and handle it during shader translation by
+                                // modifying the location decoration. Contrary to all other binding
+                                // types, OpenGL ES's SSBO binding index in the SSBO table is the
+                                // value of the location= decoration in GLSL.
+                                if (gl.GetVersion().IsDesktop()) {
+                                    GLuint location = gl.GetProgramResourceIndex(
+                                        mProgram, GL_SHADER_STORAGE_BLOCK, name.c_str());
+                                    if (location != GL_INVALID_INDEX) {
+                                        gl.ShaderStorageBlockBinding(mProgram, location,
+                                                                     indices[group][bindingIndex]);
+                                    }
+                                }
+                                break;
+                            }
+                            case wgpu::BufferBindingType::Undefined:
+                                UNREACHABLE();
                         }
                         break;
-                    }
 
-                    case wgpu::BindingType::Sampler:
-                    case wgpu::BindingType::ComparisonSampler:
-                    case wgpu::BindingType::SampledTexture:
-                    case wgpu::BindingType::MultisampledTexture:
+                    case BindingInfoType::Sampler:
+                    case BindingInfoType::Texture:
                         // These binding types are handled in the separate sampler and texture
                         // emulation
                         break;
 
-                    case wgpu::BindingType::ReadonlyStorageTexture:
-                    case wgpu::BindingType::WriteonlyStorageTexture: {
+                    case BindingInfoType::StorageTexture: {
                         GLint location = gl.GetUniformLocation(mProgram, name.c_str());
                         if (location != -1) {
                             gl.Uniform1i(location, indices[group][bindingIndex]);
                         }
                         break;
                     }
-
-                    case wgpu::BindingType::Undefined:
-                        UNREACHABLE();
                 }
             }
         }
@@ -216,8 +216,8 @@
                     GLuint textureIndex = indices[combined.textureLocation.group][bindingIndex];
                     mUnitsForTextures[textureIndex].push_back(textureUnit);
 
-                    shouldUseFiltering = bgl->GetBindingInfo(bindingIndex).textureComponentType ==
-                                         wgpu::TextureComponentType::Float;
+                    shouldUseFiltering = bgl->GetBindingInfo(bindingIndex).texture.sampleType ==
+                                         wgpu::TextureSampleType::Float;
                 }
                 {
                     if (combined.useDummySampler) {
diff --git a/src/dawn_native/opengl/PipelineLayoutGL.cpp b/src/dawn_native/opengl/PipelineLayoutGL.cpp
index 7161bfb..088eaf3 100644
--- a/src/dawn_native/opengl/PipelineLayoutGL.cpp
+++ b/src/dawn_native/opengl/PipelineLayoutGL.cpp
@@ -34,36 +34,38 @@
 
             for (BindingIndex bindingIndex{0}; bindingIndex < bgl->GetBindingCount();
                  ++bindingIndex) {
-                switch (bgl->GetBindingInfo(bindingIndex).type) {
-                    case wgpu::BindingType::UniformBuffer:
-                        mIndexInfo[group][bindingIndex] = uboIndex;
-                        uboIndex++;
+                const BindingInfo& bindingInfo = bgl->GetBindingInfo(bindingIndex);
+                switch (bindingInfo.bindingType) {
+                    case BindingInfoType::Buffer:
+                        switch (bindingInfo.buffer.type) {
+                            case wgpu::BufferBindingType::Uniform:
+                                mIndexInfo[group][bindingIndex] = uboIndex;
+                                uboIndex++;
+                                break;
+                            case wgpu::BufferBindingType::Storage:
+                            case wgpu::BufferBindingType::ReadOnlyStorage:
+                                mIndexInfo[group][bindingIndex] = ssboIndex;
+                                ssboIndex++;
+                                break;
+                            case wgpu::BufferBindingType::Undefined:
+                                UNREACHABLE();
+                        }
                         break;
-                    case wgpu::BindingType::Sampler:
-                    case wgpu::BindingType::ComparisonSampler:
+
+                    case BindingInfoType::Sampler:
                         mIndexInfo[group][bindingIndex] = samplerIndex;
                         samplerIndex++;
                         break;
-                    case wgpu::BindingType::SampledTexture:
-                    case wgpu::BindingType::MultisampledTexture:
+
+                    case BindingInfoType::Texture:
                         mIndexInfo[group][bindingIndex] = sampledTextureIndex;
                         sampledTextureIndex++;
                         break;
 
-                    case wgpu::BindingType::StorageBuffer:
-                    case wgpu::BindingType::ReadonlyStorageBuffer:
-                        mIndexInfo[group][bindingIndex] = ssboIndex;
-                        ssboIndex++;
-                        break;
-
-                    case wgpu::BindingType::ReadonlyStorageTexture:
-                    case wgpu::BindingType::WriteonlyStorageTexture:
+                    case BindingInfoType::StorageTexture:
                         mIndexInfo[group][bindingIndex] = storageTextureIndex;
                         storageTextureIndex++;
                         break;
-
-                    case wgpu::BindingType::Undefined:
-                        UNREACHABLE();
                 }
             }
         }
diff --git a/src/dawn_native/opengl/ShaderModuleGL.cpp b/src/dawn_native/opengl/ShaderModuleGL.cpp
index 647ce49..b3bcfb2 100644
--- a/src/dawn_native/opengl/ShaderModuleGL.cpp
+++ b/src/dawn_native/opengl/ShaderModuleGL.cpp
@@ -180,12 +180,10 @@
                 const auto& info = it.second;
 
                 uint32_t resourceId;
-                switch (info.type) {
+                switch (info.bindingType) {
                     // When the resource is a uniform or shader storage block, we should change the
                     // block name instead of the instance name.
-                    case wgpu::BindingType::ReadonlyStorageBuffer:
-                    case wgpu::BindingType::StorageBuffer:
-                    case wgpu::BindingType::UniformBuffer:
+                    case BindingInfoType::Buffer:
                         resourceId = info.base_type_id;
                         break;
                     default:
@@ -197,8 +195,9 @@
                 compiler.unset_decoration(info.id, spv::DecorationDescriptorSet);
                 // OpenGL ES has no glShaderStorageBlockBinding call, so we adjust the SSBO binding
                 // decoration here instead.
-                if (version.IsES() && (info.type == wgpu::BindingType::StorageBuffer ||
-                                       info.type == wgpu::BindingType::ReadonlyStorageBuffer)) {
+                if (version.IsES() && info.bindingType == BindingInfoType::Buffer &&
+                    (info.buffer.type == wgpu::BufferBindingType::Storage ||
+                     info.buffer.type == wgpu::BufferBindingType::ReadOnlyStorage)) {
                     const auto& indices = layout->GetBindingIndexInfo();
                     BindingIndex bindingIndex =
                         layout->GetBindGroupLayout(group)->GetBindingIndex(bindingNumber);
diff --git a/src/dawn_native/vulkan/BindGroupLayoutVk.cpp b/src/dawn_native/vulkan/BindGroupLayoutVk.cpp
index 24f49cf..9bb379b 100644
--- a/src/dawn_native/vulkan/BindGroupLayoutVk.cpp
+++ b/src/dawn_native/vulkan/BindGroupLayoutVk.cpp
@@ -46,30 +46,30 @@
 
     }  // anonymous namespace
 
-    VkDescriptorType VulkanDescriptorType(wgpu::BindingType type, bool isDynamic) {
-        switch (type) {
-            case wgpu::BindingType::UniformBuffer:
-                if (isDynamic) {
-                    return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
+    VkDescriptorType VulkanDescriptorType(const BindingInfo& bindingInfo) {
+        switch (bindingInfo.bindingType) {
+            case BindingInfoType::Buffer:
+                switch (bindingInfo.buffer.type) {
+                    case wgpu::BufferBindingType::Uniform:
+                        if (bindingInfo.buffer.hasDynamicOffset) {
+                            return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
+                        }
+                        return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+                    case wgpu::BufferBindingType::Storage:
+                    case wgpu::BufferBindingType::ReadOnlyStorage:
+                        if (bindingInfo.buffer.hasDynamicOffset) {
+                            return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC;
+                        }
+                        return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
+                    case wgpu::BufferBindingType::Undefined:
+                        UNREACHABLE();
                 }
-                return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
-            case wgpu::BindingType::Sampler:
-            case wgpu::BindingType::ComparisonSampler:
+            case BindingInfoType::Sampler:
                 return VK_DESCRIPTOR_TYPE_SAMPLER;
-            case wgpu::BindingType::SampledTexture:
-            case wgpu::BindingType::MultisampledTexture:
+            case BindingInfoType::Texture:
                 return VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
-            case wgpu::BindingType::StorageBuffer:
-            case wgpu::BindingType::ReadonlyStorageBuffer:
-                if (isDynamic) {
-                    return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC;
-                }
-                return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
-            case wgpu::BindingType::ReadonlyStorageTexture:
-            case wgpu::BindingType::WriteonlyStorageTexture:
+            case BindingInfoType::StorageTexture:
                 return VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
-            case wgpu::BindingType::Undefined:
-                UNREACHABLE();
         }
     }
 
@@ -96,8 +96,7 @@
 
             VkDescriptorSetLayoutBinding vkBinding;
             vkBinding.binding = static_cast<uint32_t>(bindingNumber);
-            vkBinding.descriptorType =
-                VulkanDescriptorType(bindingInfo.type, bindingInfo.hasDynamicOffset);
+            vkBinding.descriptorType = VulkanDescriptorType(bindingInfo);
             vkBinding.descriptorCount = 1;
             vkBinding.stageFlags = VulkanShaderStageFlags(bindingInfo.visibility);
             vkBinding.pImmutableSamplers = nullptr;
@@ -121,9 +120,7 @@
         std::map<VkDescriptorType, uint32_t> descriptorCountPerType;
 
         for (BindingIndex bindingIndex{0}; bindingIndex < GetBindingCount(); ++bindingIndex) {
-            const BindingInfo& bindingInfo = GetBindingInfo(bindingIndex);
-            VkDescriptorType vulkanType =
-                VulkanDescriptorType(bindingInfo.type, bindingInfo.hasDynamicOffset);
+            VkDescriptorType vulkanType = VulkanDescriptorType(GetBindingInfo(bindingIndex));
 
             // map::operator[] will return 0 if the key doesn't exist.
             descriptorCountPerType[vulkanType]++;
diff --git a/src/dawn_native/vulkan/BindGroupLayoutVk.h b/src/dawn_native/vulkan/BindGroupLayoutVk.h
index 33f3e66..394cfab 100644
--- a/src/dawn_native/vulkan/BindGroupLayoutVk.h
+++ b/src/dawn_native/vulkan/BindGroupLayoutVk.h
@@ -29,7 +29,7 @@
     class DescriptorSetAllocator;
     class Device;
 
-    VkDescriptorType VulkanDescriptorType(wgpu::BindingType type, bool isDynamic);
+    VkDescriptorType VulkanDescriptorType(const BindingInfo& bindingInfo);
 
     // In Vulkan descriptor pools have to be sized to an exact number of descriptors. This means
     // it's hard to have something where we can mix different types of descriptor sets because
diff --git a/src/dawn_native/vulkan/BindGroupVk.cpp b/src/dawn_native/vulkan/BindGroupVk.cpp
index 3fec844..b993090 100644
--- a/src/dawn_native/vulkan/BindGroupVk.cpp
+++ b/src/dawn_native/vulkan/BindGroupVk.cpp
@@ -60,13 +60,10 @@
             write.dstBinding = static_cast<uint32_t>(bindingNumber);
             write.dstArrayElement = 0;
             write.descriptorCount = 1;
-            write.descriptorType =
-                VulkanDescriptorType(bindingInfo.type, bindingInfo.hasDynamicOffset);
+            write.descriptorType = VulkanDescriptorType(bindingInfo);
 
-            switch (bindingInfo.type) {
-                case wgpu::BindingType::UniformBuffer:
-                case wgpu::BindingType::StorageBuffer:
-                case wgpu::BindingType::ReadonlyStorageBuffer: {
+            switch (bindingInfo.bindingType) {
+                case BindingInfoType::Buffer: {
                     BufferBinding binding = GetBindingAsBufferBinding(bindingIndex);
 
                     writeBufferInfo[numWrites].buffer = ToBackend(binding.buffer)->GetHandle();
@@ -76,16 +73,14 @@
                     break;
                 }
 
-                case wgpu::BindingType::Sampler:
-                case wgpu::BindingType::ComparisonSampler: {
+                case BindingInfoType::Sampler: {
                     Sampler* sampler = ToBackend(GetBindingAsSampler(bindingIndex));
                     writeImageInfo[numWrites].sampler = sampler->GetHandle();
                     write.pImageInfo = &writeImageInfo[numWrites];
                     break;
                 }
 
-                case wgpu::BindingType::SampledTexture:
-                case wgpu::BindingType::MultisampledTexture: {
+                case BindingInfoType::Texture: {
                     TextureView* view = ToBackend(GetBindingAsTextureView(bindingIndex));
 
                     writeImageInfo[numWrites].imageView = view->GetHandle();
@@ -98,8 +93,7 @@
                     break;
                 }
 
-                case wgpu::BindingType::ReadonlyStorageTexture:
-                case wgpu::BindingType::WriteonlyStorageTexture: {
+                case BindingInfoType::StorageTexture: {
                     TextureView* view = ToBackend(GetBindingAsTextureView(bindingIndex));
 
                     writeImageInfo[numWrites].imageView = view->GetHandle();
@@ -108,9 +102,6 @@
                     write.pImageInfo = &writeImageInfo[numWrites];
                     break;
                 }
-
-                case wgpu::BindingType::Undefined:
-                    UNREACHABLE();
             }
 
             numWrites++;
diff --git a/src/dawn_native/vulkan/CommandBufferVk.cpp b/src/dawn_native/vulkan/CommandBufferVk.cpp
index b2a90b8..6acab4d 100644
--- a/src/dawn_native/vulkan/CommandBufferVk.cpp
+++ b/src/dawn_native/vulkan/CommandBufferVk.cpp
@@ -155,23 +155,35 @@
                 for (BindGroupIndex index : IterateBitSet(mBindGroupLayoutsMask)) {
                     BindGroupLayoutBase* layout = mBindGroups[index]->GetLayout();
                     for (BindingIndex binding{0}; binding < layout->GetBindingCount(); ++binding) {
-                        switch (layout->GetBindingInfo(binding).type) {
-                            case wgpu::BindingType::StorageBuffer:
-                            case wgpu::BindingType::ReadonlyStorageBuffer: {
+                        const BindingInfo& bindingInfo = layout->GetBindingInfo(binding);
+
+                        switch (bindingInfo.bindingType) {
+                            case BindingInfoType::Buffer: {
+                                wgpu::BufferUsage usage;
+                                switch (bindingInfo.buffer.type) {
+                                    case wgpu::BufferBindingType::Uniform:
+                                        usage = wgpu::BufferUsage::Uniform;
+                                        break;
+                                    case wgpu::BufferBindingType::Storage:
+                                    case wgpu::BufferBindingType::ReadOnlyStorage:
+                                        usage = wgpu::BufferUsage::Storage;
+                                        break;
+                                    case wgpu::BufferBindingType::Undefined:
+                                        UNREACHABLE();
+                                }
+
                                 VkBufferMemoryBarrier bufferBarrier;
                                 if (ToBackend(mBindGroups[index]
                                                   ->GetBindingAsBufferBinding(binding)
                                                   .buffer)
                                         ->TransitionUsageAndGetResourceBarrier(
-                                            wgpu::BufferUsage::Storage, &bufferBarrier, &srcStages,
-                                            &dstStages)) {
+                                            usage, &bufferBarrier, &srcStages, &dstStages)) {
                                     bufferBarriers.push_back(bufferBarrier);
                                 }
                                 break;
                             }
 
-                            case wgpu::BindingType::ReadonlyStorageTexture:
-                            case wgpu::BindingType::WriteonlyStorageTexture: {
+                            case BindingInfoType::StorageTexture: {
                                 TextureViewBase* view =
                                     mBindGroups[index]->GetBindingAsTextureView(binding);
                                 ToBackend(view->GetTexture())
@@ -180,21 +192,8 @@
                                         &imageBarriers, &srcStages, &dstStages);
                                 break;
                             }
-                            case wgpu::BindingType::UniformBuffer: {
-                                VkBufferMemoryBarrier bufferBarrier;
-                                if (ToBackend(mBindGroups[index]
-                                                  ->GetBindingAsBufferBinding(binding)
-                                                  .buffer)
-                                        ->TransitionUsageAndGetResourceBarrier(
-                                            wgpu::BufferUsage::Uniform, &bufferBarrier, &srcStages,
-                                            &dstStages)) {
-                                    bufferBarriers.push_back(bufferBarrier);
-                                }
-                                break;
-                            }
 
-                            case wgpu::BindingType::SampledTexture:
-                            case wgpu::BindingType::MultisampledTexture: {
+                            case BindingInfoType::Texture: {
                                 TextureViewBase* view =
                                     mBindGroups[index]->GetBindingAsTextureView(binding);
                                 ToBackend(view->GetTexture())
@@ -204,13 +203,9 @@
                                 break;
                             }
 
-                            case wgpu::BindingType::Sampler:
-                            case wgpu::BindingType::ComparisonSampler:
+                            case BindingInfoType::Sampler:
                                 // Don't require barriers.
                                 break;
-
-                            case wgpu::BindingType::Undefined:
-                                UNREACHABLE();
                         }
                     }
                 }
diff --git a/src/tests/unittests/validation/StorageTextureValidationTests.cpp b/src/tests/unittests/validation/StorageTextureValidationTests.cpp
index 38897d4..c3433cf 100644
--- a/src/tests/unittests/validation/StorageTextureValidationTests.cpp
+++ b/src/tests/unittests/validation/StorageTextureValidationTests.cpp
@@ -414,13 +414,19 @@
 // render and compute pipeline, the binding type in the bind group layout must match the
 // declaration in the shader.
 TEST_F(StorageTextureValidationTests, BindGroupLayoutEntryTypeMatchesShaderDeclaration) {
-    constexpr std::array<wgpu::BindingType, 7> kSupportedBindingTypes = {
-        wgpu::BindingType::UniformBuffer,          wgpu::BindingType::StorageBuffer,
-        wgpu::BindingType::ReadonlyStorageBuffer,  wgpu::BindingType::Sampler,
-        wgpu::BindingType::SampledTexture,         wgpu::BindingType::ReadonlyStorageTexture,
-        wgpu::BindingType::WriteonlyStorageTexture};
     constexpr wgpu::TextureFormat kStorageTextureFormat = wgpu::TextureFormat::R32Float;
 
+    std::initializer_list<utils::BindingLayoutEntryInitializationHelper> kSupportedBindingTypes = {
+        {0, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::Uniform},
+        {0, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::Storage},
+        {0, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::ReadOnlyStorage},
+        {0, wgpu::ShaderStage::Compute, wgpu::SamplerBindingType::Filtering},
+        {0, wgpu::ShaderStage::Compute, wgpu::TextureSampleType::Float},
+        {0, wgpu::ShaderStage::Compute, wgpu::StorageTextureAccess::ReadOnly,
+         kStorageTextureFormat},
+        {0, wgpu::ShaderStage::Compute, wgpu::StorageTextureAccess::WriteOnly,
+         kStorageTextureFormat}};
+
     for (wgpu::StorageTextureAccess bindingTypeInShader : kSupportedStorageTextureAccess) {
         // Create the compute shader with the given binding type.
         std::string computeShader =
@@ -433,30 +439,20 @@
         defaultComputePipelineDescriptor.computeStage.module = csModule;
         defaultComputePipelineDescriptor.computeStage.entryPoint = "main";
 
-        // Set common fileds of bind group layout binding.
-        wgpu::BindGroupLayoutEntry defaultBindGroupLayoutEntry;
-        defaultBindGroupLayoutEntry.binding = 0;
-        defaultBindGroupLayoutEntry.visibility = wgpu::ShaderStage::Compute;
-        defaultBindGroupLayoutEntry.storageTextureFormat = kStorageTextureFormat;
-
-        for (wgpu::BindingType bindingTypeInBindgroupLayout : kSupportedBindingTypes) {
+        for (utils::BindingLayoutEntryInitializationHelper bindingLayoutEntry :
+             kSupportedBindingTypes) {
             wgpu::ComputePipelineDescriptor computePipelineDescriptor =
                 defaultComputePipelineDescriptor;
 
             // Create bind group layout with different binding types.
-            wgpu::BindGroupLayoutEntry bindGroupLayoutBinding = defaultBindGroupLayoutEntry;
-            bindGroupLayoutBinding.type = bindingTypeInBindgroupLayout;
             wgpu::BindGroupLayout bindGroupLayout =
-                utils::MakeBindGroupLayout(device, {bindGroupLayoutBinding});
+                utils::MakeBindGroupLayout(device, {bindingLayoutEntry});
             computePipelineDescriptor.layout =
                 utils::MakeBasicPipelineLayout(device, &bindGroupLayout);
 
             // The binding type in the bind group layout must the same as the related image object
             // declared in shader.
-            if ((bindingTypeInBindgroupLayout == wgpu::BindingType::ReadonlyStorageTexture &&
-                 bindingTypeInShader == wgpu::StorageTextureAccess::ReadOnly) ||
-                (bindingTypeInBindgroupLayout == wgpu::BindingType::WriteonlyStorageTexture &&
-                 bindingTypeInShader == wgpu::StorageTextureAccess::WriteOnly)) {
+            if (bindingLayoutEntry.storageTexture.access == bindingTypeInShader) {
                 device.CreateComputePipeline(&computePipelineDescriptor);
             } else {
                 ASSERT_DEVICE_ERROR(device.CreateComputePipeline(&computePipelineDescriptor));