Add static sampler BindGroupLayout validation

This adds the following validation and tests.
1. YCbCr static samplers layout entries must have a sampled texture
   binding.
2. All static samplers layout entries with a sampled texture binding
   point to a valid texture binding layout entry.
3. No two static sampler layout entries have the same sampled texture
   binding.

Bug: 42241425
Change-Id: Ic15a15744269eec8a8f785967e7b4b125bc8e8cb
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/213414
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Kyle Charbonneau <kylechar@google.com>
diff --git a/src/dawn/native/BindGroup.cpp b/src/dawn/native/BindGroup.cpp
index af334cd..5c08f7d 100644
--- a/src/dawn/native/BindGroup.cpp
+++ b/src/dawn/native/BindGroup.cpp
@@ -174,6 +174,7 @@
 
     SampleTypeBit supportedTypes = texture->GetFormat().GetAspectInfo(aspect).supportedSampleTypes;
     if (supportedTypes == SampleTypeBit::External) {
+        DAWN_ASSERT(texture->GetSharedResourceMemoryContents());
         supportedTypes =
             static_cast<SharedTextureMemoryContents*>(texture->GetSharedResourceMemoryContents())
                 ->GetExternalFormatSupportedSampleTypes();
diff --git a/src/dawn/native/BindGroupLayoutInternal.cpp b/src/dawn/native/BindGroupLayoutInternal.cpp
index e9c03fd..475ae96 100644
--- a/src/dawn/native/BindGroupLayoutInternal.cpp
+++ b/src/dawn/native/BindGroupLayoutInternal.cpp
@@ -40,6 +40,7 @@
 #include "dawn/common/MatchVariant.h"
 #include "dawn/native/ChainUtils.h"
 #include "dawn/native/Device.h"
+#include "dawn/native/Error.h"
 #include "dawn/native/Instance.h"
 #include "dawn/native/ObjectBase.h"
 #include "dawn/native/ObjectContentHasher.h"
@@ -202,6 +203,11 @@
                         wgpu::FeatureName::StaticSamplers);
 
         DAWN_TRY(device->ValidateObject(staticSamplerBindingLayout->sampler));
+
+        if (staticSamplerBindingLayout->sampledTextureBinding == WGPU_LIMIT_U32_UNDEFINED) {
+            DAWN_INVALID_IF(staticSamplerBindingLayout->sampler->IsYCbCr(),
+                            "YCbCr static sampler requires a sampled texture binding");
+        }
     }
 
     if (entry.Get<ExternalTextureBindingLayout>()) {
@@ -219,6 +225,47 @@
     return {};
 }
 
+MaybeError ValidateStaticSamplersWithTextureBindings(
+    DeviceBase* device,
+    const BindGroupLayoutDescriptor* descriptor,
+    const std::map<BindingNumber, uint32_t>& bindingNumberToIndexMap) {
+    // Map of texture binding number to static sampler binding number.
+    std::map<BindingNumber, BindingNumber> textureToStaticSamplerBindingMap;
+
+    for (uint32_t i = 0; i < descriptor->entryCount; ++i) {
+        UnpackedPtr<BindGroupLayoutEntry> entry = Unpack(&descriptor->entries[i]);
+        auto* staticSamplerLayout = entry.Get<StaticSamplerBindingLayout>();
+        if (!staticSamplerLayout ||
+            staticSamplerLayout->sampledTextureBinding == WGPU_LIMIT_U32_UNDEFINED) {
+            continue;
+        }
+
+        BindingNumber samplerBinding(entry->binding);
+        BindingNumber sampledTextureBinding(staticSamplerLayout->sampledTextureBinding);
+
+        bool inserted =
+            textureToStaticSamplerBindingMap.insert({sampledTextureBinding, samplerBinding}).second;
+        DAWN_INVALID_IF(!inserted,
+                        "For static sampler binding (%u) the sampled texture binding (%u) is "
+                        "already bound to a static sampler at binding (%u).",
+                        samplerBinding, sampledTextureBinding,
+                        textureToStaticSamplerBindingMap[sampledTextureBinding]);
+
+        DAWN_INVALID_IF(!bindingNumberToIndexMap.count(sampledTextureBinding),
+                        "For static sampler binding (%u) the sampled texture binding (%u) is not a "
+                        "valid binding number.",
+                        samplerBinding, sampledTextureBinding);
+
+        auto& textureEntry = descriptor->entries[bindingNumberToIndexMap.at(sampledTextureBinding)];
+        DAWN_INVALID_IF(textureEntry.texture.sampleType == wgpu::TextureSampleType::Undefined,
+                        "For static sampler binding (%u) the sampled texture binding (%u) is not a "
+                        "texture binding.",
+                        samplerBinding, sampledTextureBinding);
+    }
+
+    return {};
+}
+
 BindGroupLayoutEntry CreateSampledTextureBindingForExternalTexture(uint32_t binding,
                                                                    wgpu::ShaderStage visibility) {
     BindGroupLayoutEntry entry;
@@ -317,7 +364,8 @@
                                              bool allowInternalBinding) {
     DAWN_INVALID_IF(descriptor->nextInChain != nullptr, "nextInChain must be nullptr");
 
-    std::set<BindingNumber> bindingsSet;
+    // Map of binding number to entry index.
+    std::map<BindingNumber, uint32_t> bindingMap;
     BindingCounts bindingCounts = {};
 
     for (uint32_t i = 0; i < descriptor->entryCount; ++i) {
@@ -328,7 +376,7 @@
         DAWN_INVALID_IF(bindingNumber >= kMaxBindingsPerBindGroupTyped,
                         "Binding number (%u) exceeds the maxBindingsPerBindGroup limit (%u).",
                         uint32_t(bindingNumber), kMaxBindingsPerBindGroup);
-        DAWN_INVALID_IF(bindingsSet.count(bindingNumber) != 0,
+        DAWN_INVALID_IF(bindingMap.count(bindingNumber) != 0,
                         "On entries[%u]: binding index (%u) was specified by a previous entry.", i,
                         entry->binding);
 
@@ -337,9 +385,13 @@
 
         IncrementBindingCounts(&bindingCounts, entry);
 
-        bindingsSet.insert(bindingNumber);
+        bindingMap.insert({bindingNumber, i});
     }
 
+    // Perform a second validation pass for static samplers. This is done after initial validation
+    // as static samplers can have associated texture entries that need to be validated first.
+    DAWN_TRY(ValidateStaticSamplersWithTextureBindings(device, descriptor, bindingMap));
+
     DAWN_TRY_CONTEXT(ValidateBindingCounts(device->GetLimits(), bindingCounts),
                      "validating binding counts");
 
diff --git a/src/dawn/tests/end2end/YCbCrInfoTests.cpp b/src/dawn/tests/end2end/YCbCrInfoTests.cpp
index 38972ac..43aa101 100644
--- a/src/dawn/tests/end2end/YCbCrInfoTests.cpp
+++ b/src/dawn/tests/end2end/YCbCrInfoTests.cpp
@@ -29,6 +29,7 @@
 
 #include "dawn/native/VulkanBackend.h"
 #include "dawn/tests/DawnTest.h"
+#include "dawn/utils/WGPUHelpers.h"
 
 #if DAWN_PLATFORM_IS(ANDROID)
 #include <android/hardware_buffer.h>
@@ -41,8 +42,7 @@
 constexpr uint32_t kDefaultLayerCount = 1u;
 constexpr wgpu::TextureFormat kDefaultTextureFormat = wgpu::TextureFormat::External;
 
-wgpu::Texture Create2DTexture(wgpu::Device& device,
-                              wgpu::TextureFormat format = kDefaultTextureFormat) {
+wgpu::Texture Create2DTexture(wgpu::Device& device) {
 #if DAWN_PLATFORM_IS(ANDROID)
     constexpr uint32_t kWidth = 32u;
     constexpr uint32_t kHeight = 32u;
@@ -99,6 +99,22 @@
     return descriptor;
 }
 
+wgpu::TextureView Create2DTextureView(wgpu::Texture texture,
+                                      const wgpu::YCbCrVkDescriptor* yCbCrDesc) {
+    wgpu::TextureViewDescriptor textureDesc =
+        CreateDefaultViewDescriptor(wgpu::TextureViewDimension::e2D);
+    textureDesc.arrayLayerCount = 1;
+    textureDesc.nextInChain = yCbCrDesc;
+
+    return texture.CreateView(&textureDesc);
+}
+
+wgpu::Sampler CreateYCbCrSampler(wgpu::Device device, const wgpu::YCbCrVkDescriptor* yCbCrDesc) {
+    wgpu::SamplerDescriptor samplerDesc = {};
+    samplerDesc.nextInChain = yCbCrDesc;
+    return device.CreateSampler(&samplerDesc);
+}
+
 class YCbCrInfoTest : public DawnTest {
   protected:
     void SetUp() override {
@@ -115,6 +131,7 @@
             SupportsFeatures({wgpu::FeatureName::YCbCrVulkanSamplers}) &&
             SupportsFeatures({wgpu::FeatureName::SharedTextureMemoryAHardwareBuffer})) {
             requiredFeatures.push_back(wgpu::FeatureName::YCbCrVulkanSamplers);
+            requiredFeatures.push_back(wgpu::FeatureName::StaticSamplers);
             requiredFeatures.push_back(wgpu::FeatureName::SharedTextureMemoryAHardwareBuffer);
         }
         return requiredFeatures;
@@ -266,7 +283,237 @@
     ASSERT_DEVICE_ERROR(texture.CreateView(&descriptor));
 }
 
+// Tests that creating a bind group layout with a valid static sampler succeeds if the
+// required feature is enabled.
+TEST_P(YCbCrInfoTest, CreateBindGroupWithYCbCrSamplerSupported) {
+    std::vector<wgpu::BindGroupLayoutEntry> entries;
+
+    wgpu::YCbCrVkDescriptor yCbCrDesc = {};
+    yCbCrDesc.vkFormat = VK_FORMAT_R8G8B8A8_UNORM;
+
+    wgpu::BindGroupLayoutEntry& binding0 = entries.emplace_back();
+    binding0.binding = 0;
+    wgpu::StaticSamplerBindingLayout staticSamplerBinding = {};
+    staticSamplerBinding.sampler = CreateYCbCrSampler(device, &yCbCrDesc);
+    staticSamplerBinding.sampledTextureBinding = 1;
+    binding0.nextInChain = &staticSamplerBinding;
+
+    wgpu::BindGroupLayoutEntry& binding1 = entries.emplace_back();
+    binding1.binding = 1;
+    binding1.texture.sampleType = wgpu::TextureSampleType::Float;
+    binding1.texture.viewDimension = wgpu::TextureViewDimension::e2D;
+    binding1.texture.multisampled = false;
+
+    wgpu::BindGroupLayoutDescriptor layoutDesc = {};
+    layoutDesc.entryCount = entries.size();
+    layoutDesc.entries = entries.data();
+
+    wgpu::BindGroupLayout layout = device.CreateBindGroupLayout(&layoutDesc);
+
+    wgpu::Texture texture = Create2DTexture(device);
+    wgpu::TextureView textureView = Create2DTextureView(texture, &yCbCrDesc);
+
+    utils::MakeBindGroup(device, layout, {{1, textureView}});
+}
+
+// Verify that creating a bind group layout fails with a static sampler binding that has no texture
+// binding.
+TEST_P(YCbCrInfoTest, CreateBindGroupLayoutWithYCbCrSamplerNoTextureBinding) {
+    wgpu::YCbCrVkDescriptor yCbCrDesc = {};
+    yCbCrDesc.vkFormat = VK_FORMAT_R8G8B8A8_UNORM;
+
+    wgpu::BindGroupLayoutEntry binding = {};
+    binding.binding = 0;
+    wgpu::StaticSamplerBindingLayout staticSamplerBinding = {};
+    staticSamplerBinding.sampler = CreateYCbCrSampler(device, &yCbCrDesc);
+    staticSamplerBinding.sampledTextureBinding = 1;
+    binding.nextInChain = &staticSamplerBinding;
+
+    wgpu::BindGroupLayoutDescriptor desc = {};
+    desc.entryCount = 1;
+    desc.entries = &binding;
+
+    ASSERT_DEVICE_ERROR(device.CreateBindGroupLayout(&desc));
+}
+
+// Verifies that creating a bind group layout fails with a static sampler binding that refers to an
+// invalid binding index for texture binding.
+TEST_P(YCbCrInfoTest, CreateBindGroupLayoutWithYCbCrSamplerInvalidTextureBinding) {
+    wgpu::YCbCrVkDescriptor yCbCrDesc = {};
+    yCbCrDesc.vkFormat = VK_FORMAT_R8G8B8A8_UNORM;
+
+    wgpu::BindGroupLayoutEntry binding = {};
+    binding.binding = 0;
+    wgpu::StaticSamplerBindingLayout staticSamplerBinding = {};
+    staticSamplerBinding.sampler = CreateYCbCrSampler(device, &yCbCrDesc);
+    staticSamplerBinding.sampledTextureBinding = 1;
+    binding.nextInChain = &staticSamplerBinding;
+
+    wgpu::BindGroupLayoutDescriptor desc = {};
+    desc.entryCount = 1;
+    desc.entries = &binding;
+
+    ASSERT_DEVICE_ERROR(device.CreateBindGroupLayout(&desc));
+}
+
+// Verifies that creating a bind group layout fails with a static sampler binding that refers to a
+// binding that is not a texture binding.
+TEST_P(YCbCrInfoTest, CreateBindGroupLayoutWithYCbCrSamplerInvalidTextureBindingType) {
+    std::vector<wgpu::BindGroupLayoutEntry> entries;
+    wgpu::YCbCrVkDescriptor yCbCrDesc = {};
+    yCbCrDesc.vkFormat = VK_FORMAT_R8G8B8A8_UNORM;
+
+    wgpu::BindGroupLayoutEntry& binding0 = entries.emplace_back();
+    binding0.binding = 0;
+    wgpu::StaticSamplerBindingLayout staticSamplerBinding = {};
+    staticSamplerBinding.sampler = CreateYCbCrSampler(device, &yCbCrDesc);
+    staticSamplerBinding.sampledTextureBinding = 1;
+    binding0.nextInChain = &staticSamplerBinding;
+
+    // This should be a texture binding but it's not.
+    wgpu::BindGroupLayoutEntry& binding1 = entries.emplace_back();
+    binding1.binding = 1;
+    binding1.sampler.type = wgpu::SamplerBindingType::Filtering;
+
+    wgpu::BindGroupLayoutDescriptor layoutDesc = {};
+    layoutDesc.entryCount = entries.size();
+    layoutDesc.entries = entries.data();
+
+    ASSERT_DEVICE_ERROR(device.CreateBindGroupLayout(&layoutDesc));
+}
+
+// Verify that creating a bind group layout fails when two static samplers have the same
+// sampled texture binding.
+TEST_P(YCbCrInfoTest, CreateBindGroupLayoutWithYCbCrSamplerDuplicateSampledTextures) {
+    std::vector<wgpu::BindGroupLayoutEntry> entries;
+
+    wgpu::YCbCrVkDescriptor yCbCrDesc = {};
+    yCbCrDesc.vkFormat = VK_FORMAT_R8G8B8A8_UNORM;
+
+    auto& binding0 = entries.emplace_back();
+    binding0.binding = 0;
+    wgpu::StaticSamplerBindingLayout staticSamplerBinding = {};
+    staticSamplerBinding.sampler = CreateYCbCrSampler(device, &yCbCrDesc);
+    staticSamplerBinding.sampledTextureBinding = 1;
+    binding0.nextInChain = &staticSamplerBinding;
+
+    auto& binding1 = entries.emplace_back();
+    binding1.binding = 1;
+    binding1.texture.sampleType = wgpu::TextureSampleType::Float;
+    binding1.texture.viewDimension = wgpu::TextureViewDimension::e2D;
+    binding1.texture.multisampled = false;
+
+    // This static sampler has the same `sampledTextureBinding` as binding 0.
+    auto& binding2 = entries.emplace_back();
+    binding2.binding = 2;
+    binding2.nextInChain = &staticSamplerBinding;
+
+    wgpu::BindGroupLayoutDescriptor layoutDesc = {};
+    layoutDesc.entryCount = entries.size();
+    layoutDesc.entries = entries.data();
+
+    ASSERT_DEVICE_ERROR(device.CreateBindGroupLayout(&layoutDesc));
+}
+
+// Verifies that creation of a correctly-specified bind group for a layout that
+// has a sampler and a static sampler succeeds.
+TEST_P(YCbCrInfoTest, CreateBindGroupWithSamplerAndStaticSamplerSupported) {
+    std::vector<wgpu::BindGroupLayoutEntry> entries;
+
+    wgpu::BindGroupLayoutEntry& binding0 = entries.emplace_back();
+    binding0.binding = 0;
+    binding0.sampler.type = wgpu::SamplerBindingType::Filtering;
+
+    wgpu::BindGroupLayoutEntry& binding1 = entries.emplace_back();
+    binding1.binding = 1;
+    wgpu::StaticSamplerBindingLayout staticSamplerBinding = {};
+
+    wgpu::SamplerDescriptor samplerDesc = {};
+    wgpu::YCbCrVkDescriptor yCbCrDesc = {};
+    yCbCrDesc.vkFormat = VK_FORMAT_R8G8B8A8_UNORM;
+    samplerDesc.nextInChain = &yCbCrDesc;
+
+    staticSamplerBinding.sampler = device.CreateSampler(&samplerDesc);
+    staticSamplerBinding.sampledTextureBinding = 2;
+    binding1.nextInChain = &staticSamplerBinding;
+
+    wgpu::BindGroupLayoutEntry& binding2 = entries.emplace_back();
+    binding2.binding = 2;
+    binding2.texture.sampleType = wgpu::TextureSampleType::Float;
+    binding2.texture.viewDimension = wgpu::TextureViewDimension::e2D;
+    binding2.texture.multisampled = false;
+
+    wgpu::BindGroupLayoutDescriptor desc = {};
+    desc.entryCount = entries.size();
+    desc.entries = entries.data();
+
+    wgpu::BindGroupLayout layout = device.CreateBindGroupLayout(&desc);
+
+    wgpu::SamplerDescriptor dynamicSamplerDesc;
+    dynamicSamplerDesc.minFilter = wgpu::FilterMode::Linear;
+
+    wgpu::Texture texture = Create2DTexture(device);
+    wgpu::TextureView textureView = Create2DTextureView(texture, &yCbCrDesc);
+
+    utils::MakeBindGroup(device, layout,
+                         {{0, device.CreateSampler(&dynamicSamplerDesc)}, {2, textureView}});
+}
+
+// Verifies that creation of a bind group with the correct number of entries for a layout that has a
+// sampler and a static sampler raises an error if the entry is specified at the index of the static
+// sampler rather than that of the sampler.
+TEST_P(YCbCrInfoTest, BindGroupCreationForSamplerBindingTypeCausesError) {
+    std::vector<wgpu::BindGroupLayoutEntry> entries;
+
+    wgpu::BindGroupLayoutEntry& binding0 = entries.emplace_back();
+    binding0.binding = 0;
+    binding0.sampler.type = wgpu::SamplerBindingType::Filtering;
+
+    wgpu::BindGroupLayoutEntry& binding1 = entries.emplace_back();
+    binding1.binding = 1;
+    wgpu::StaticSamplerBindingLayout staticSamplerBinding = {};
+
+    wgpu::SamplerDescriptor samplerDesc = {};
+    wgpu::YCbCrVkDescriptor yCbCrDesc = {};
+    yCbCrDesc.vkFormat = VK_FORMAT_R8G8B8A8_UNORM;
+    samplerDesc.nextInChain = &yCbCrDesc;
+
+    staticSamplerBinding.sampler = device.CreateSampler(&samplerDesc);
+    staticSamplerBinding.sampledTextureBinding = 2;
+    binding1.nextInChain = &staticSamplerBinding;
+
+    wgpu::BindGroupLayoutEntry& binding2 = entries.emplace_back();
+    binding2.binding = 2;
+    binding2.texture.sampleType = wgpu::TextureSampleType::Float;
+    binding2.texture.viewDimension = wgpu::TextureViewDimension::e2D;
+    binding2.texture.multisampled = false;
+
+    wgpu::BindGroupLayoutDescriptor desc = {};
+    desc.entryCount = entries.size();
+    desc.entries = entries.data();
+
+    wgpu::BindGroupLayout layout = device.CreateBindGroupLayout(&desc);
+
+    wgpu::SamplerDescriptor samplerDesc0;
+    samplerDesc0.minFilter = wgpu::FilterMode::Linear;
+
+    wgpu::Texture texture = Create2DTexture(device);
+    wgpu::TextureView textureView = Create2DTextureView(texture, &yCbCrDesc);
+
+    ASSERT_DEVICE_ERROR(utils::MakeBindGroup(
+        device, layout, {{1, device.CreateSampler(&samplerDesc0)}, {2, textureView}}));
+}
+
 DAWN_INSTANTIATE_TEST(YCbCrInfoTest, VulkanBackend());
 
+// TODO(crbug.com/dawn/2476): Add test validating binding fails if texture view ycbcr info is
+// different from that on sampler
+// TODO(crbug.com/dawn/2476): Add test validating binding passes if texture view ycbcr info is same
+// as that on sampler
+// TODO(crbug.com/dawn/2476): Add validation that mipLevel, arrayLayers are always 1 along with 2D
+// view dimension (see
+// https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkImageCreateInfo.html) with
+// YCbCr and tests for it.
+
 }  // anonymous namespace
 }  // namespace dawn
diff --git a/src/dawn/tests/unittests/validation/YCbCrInfoValidationTests.cpp b/src/dawn/tests/unittests/validation/YCbCrInfoValidationTests.cpp
index 46b6a22..524e1b2 100644
--- a/src/dawn/tests/unittests/validation/YCbCrInfoValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/YCbCrInfoValidationTests.cpp
@@ -127,101 +127,6 @@
     device.CreateSampler(&samplerDesc);
 }
 
-// Tests that creating a bind group layout with a valid static sampler succeeds if the
-// required feature is enabled.
-TEST_F(YCbCrInfoWithFeatureValidationTest, CreateBindGroupWithYCbCrSamplerSupported) {
-    wgpu::BindGroupLayoutEntry binding = {};
-    binding.binding = 0;
-    wgpu::StaticSamplerBindingLayout staticSamplerBinding = {};
-
-    wgpu::SamplerDescriptor samplerDesc = {};
-    wgpu::YCbCrVkDescriptor yCbCrDesc = {};
-    samplerDesc.nextInChain = &yCbCrDesc;
-
-    staticSamplerBinding.sampler = device.CreateSampler(&samplerDesc);
-    binding.nextInChain = &staticSamplerBinding;
-
-    wgpu::BindGroupLayoutDescriptor desc = {};
-    desc.entryCount = 1;
-    desc.entries = &binding;
-
-    wgpu::BindGroupLayout layout = device.CreateBindGroupLayout(&desc);
-
-    wgpu::BindGroupDescriptor descriptor;
-    descriptor.layout = layout;
-    descriptor.entryCount = 0;
-
-    device.CreateBindGroup(&descriptor);
-}
-
-// Verifies that creation of a correctly-specified bind group for a layout that
-// has a sampler and a static sampler succeeds.
-TEST_F(YCbCrInfoWithFeatureValidationTest, CreateBindGroupWithSamplerAndStaticSamplerSupported) {
-    std::vector<wgpu::BindGroupLayoutEntry> entries;
-
-    wgpu::BindGroupLayoutEntry binding0 = {};
-    binding0.binding = 0;
-    binding0.sampler.type = wgpu::SamplerBindingType::Filtering;
-    entries.push_back(binding0);
-
-    wgpu::BindGroupLayoutEntry binding1 = {};
-    binding1.binding = 1;
-    wgpu::StaticSamplerBindingLayout staticSamplerBinding = {};
-
-    wgpu::SamplerDescriptor samplerDesc = {};
-    wgpu::YCbCrVkDescriptor yCbCrDesc = {};
-    samplerDesc.nextInChain = &yCbCrDesc;
-
-    staticSamplerBinding.sampler = device.CreateSampler(&samplerDesc);
-    binding1.nextInChain = &staticSamplerBinding;
-    entries.push_back(binding1);
-
-    wgpu::BindGroupLayoutDescriptor desc = {};
-    desc.entryCount = 2;
-    desc.entries = entries.data();
-
-    wgpu::BindGroupLayout layout = device.CreateBindGroupLayout(&desc);
-
-    wgpu::SamplerDescriptor samplerDesc0;
-    samplerDesc0.minFilter = wgpu::FilterMode::Linear;
-    utils::MakeBindGroup(device, layout, {{0, device.CreateSampler(&samplerDesc0)}});
-}
-
-// Verifies that creation of a bind group with the correct number of entries for a layout that has a
-// sampler and a static sampler raises an error if the entry is specified at the
-// index of the static sampler rather than that of the sampler.
-TEST_F(YCbCrInfoWithFeatureValidationTest, BindGroupCreationForSamplerBindingTypeCausesError) {
-    std::vector<wgpu::BindGroupLayoutEntry> entries;
-
-    wgpu::BindGroupLayoutEntry binding0 = {};
-    binding0.binding = 0;
-    binding0.sampler.type = wgpu::SamplerBindingType::Filtering;
-    entries.push_back(binding0);
-
-    wgpu::BindGroupLayoutEntry binding1 = {};
-    binding1.binding = 1;
-    wgpu::StaticSamplerBindingLayout staticSamplerBinding = {};
-
-    wgpu::SamplerDescriptor samplerDesc = {};
-    wgpu::YCbCrVkDescriptor yCbCrDesc = {};
-    samplerDesc.nextInChain = &yCbCrDesc;
-
-    staticSamplerBinding.sampler = device.CreateSampler(&samplerDesc);
-    binding1.nextInChain = &staticSamplerBinding;
-    entries.push_back(binding1);
-
-    wgpu::BindGroupLayoutDescriptor desc = {};
-    desc.entryCount = 2;
-    desc.entries = entries.data();
-
-    wgpu::BindGroupLayout layout = device.CreateBindGroupLayout(&desc);
-
-    wgpu::SamplerDescriptor samplerDesc0;
-    samplerDesc0.minFilter = wgpu::FilterMode::Linear;
-    ASSERT_DEVICE_ERROR(
-        utils::MakeBindGroup(device, layout, {{1, device.CreateSampler(&samplerDesc0)}}));
-}
-
 // Tests that creating a texture view with a valid ycbcr vulkan descriptor succeeds if the
 // required feature is enabled.
 TEST_F(YCbCrInfoWithFeatureValidationTest, YCbCrTextureViewSupported) {
@@ -236,18 +141,6 @@
     texture.CreateView(&descriptor);
 }
 
-// TODO(crbug.com/dawn/2476): Add test validating binding fails if sampler or texture view is not
-// YCbCr
-// TODO(crbug.com/dawn/2476): Add test validating binding passes if sampler and texture view is
-// YCbCr
-// TODO(crbug.com/dawn/2476): Add test validating binding fails if texture view ycbcr info is
-// different from that on sampler
-// TODO(crbug.com/dawn/2476): Add test validating binding passes if texture view ycbcr info is same
-// as that on sampler
-// TODO(crbug.com/dawn/2476): Add validation that mipLevel, arrayLayers are always 1 along with 2D
-// view dimension (see
-// https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkImageCreateInfo.html) with
-// YCbCr and tests for it.
 
 }  // anonymous namespace
 }  // namespace dawn