Add YCbCr sampler validation

VUID-VkSamplerCreateInfo-addressModeU-01646 enforces constraints on
address mode and anisotropy being disabled for YCbCr samplers. Enforce
those constraints in dawn validation and when creating VkSampler.

Bug: 513006636
Change-Id: I608a29e7e468bc06baae3fed45192bc5ad9144b0
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/309015
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Brandon Jones <bajones@chromium.org>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/dawn/native/Sampler.cpp b/src/dawn/native/Sampler.cpp
index e9509e4..706ef42 100644
--- a/src/dawn/native/Sampler.cpp
+++ b/src/dawn/native/Sampler.cpp
@@ -77,6 +77,17 @@
 
         DAWN_INVALID_IF(ycbcr->externalFormat == 0 && ycbcr->vkFormat == 0,
                         "Both VkFormat and VkExternalFormatANDROID are undefined.");
+
+        DAWN_INVALID_IF(descriptor->addressModeU != wgpu::AddressMode::ClampToEdge,
+                        "addressModeU must be ClampToEdge for YCbCr samplers.");
+        DAWN_INVALID_IF(descriptor->addressModeV != wgpu::AddressMode::ClampToEdge,
+                        "addressModeV must be ClampToEdge for YCbCr samplers.");
+        DAWN_INVALID_IF(descriptor->addressModeW != wgpu::AddressMode::ClampToEdge,
+                        "addressModeW must be ClampToEdge for YCbCr samplers.");
+
+        DAWN_INVALID_IF(descriptor->maxAnisotropy > 1,
+                        "maxAnisotropy (%d) must be 1 for YCbCr samplers.",
+                        descriptor->maxAnisotropy);
     }
 
     return {};
diff --git a/src/dawn/native/vulkan/SamplerVk.cpp b/src/dawn/native/vulkan/SamplerVk.cpp
index 11a97d4..439a41f 100644
--- a/src/dawn/native/vulkan/SamplerVk.cpp
+++ b/src/dawn/native/vulkan/SamplerVk.cpp
@@ -144,6 +144,13 @@
         samplerYCbCrInfo.conversion = mSamplerYCbCrConversion;
 
         createInfo.pNext = &samplerYCbCrInfo;
+
+        // VUID-VkSamplerCreateInfo-addressModeU-01646 requires CLAMP_TO_EDGE on every axis and
+        // anisotropy disabled when VkSamplerYcbcrConversionInfo is provided.
+        createInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
+        createInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
+        createInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
+        createInfo.anisotropyEnable = VK_FALSE;
     }
 
     DAWN_TRY(CheckVkSuccess(
diff --git a/src/dawn/tests/unittests/validation/YCbCrValidationTests.cpp b/src/dawn/tests/unittests/validation/YCbCrValidationTests.cpp
index ab47124..58075fe 100644
--- a/src/dawn/tests/unittests/validation/YCbCrValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/YCbCrValidationTests.cpp
@@ -127,6 +127,41 @@
     device.CreateSampler(&samplerDesc);
 }
 
+// Test that creating a YCbCr sampler with address mode or max anisotropy set to something invalid
+// fails.
+TEST_F(YCbCrVulkanSamplersTest, YCbCrSamplerRequiredParams) {
+    wgpu::SamplerDescriptor samplerDesc = {};
+    wgpu::YCbCrVkDescriptor yCbCrDesc = {};
+    yCbCrDesc.vkFormat = VK_FORMAT_R8G8B8A8_UNORM;
+    samplerDesc.nextInChain = &yCbCrDesc;
+
+    // This should work.
+    device.CreateSampler(&samplerDesc);
+
+    {
+        wgpu::SamplerDescriptor invalidSamplerDesc = samplerDesc;
+        invalidSamplerDesc.addressModeU = wgpu::AddressMode::Repeat;
+        ASSERT_DEVICE_ERROR(device.CreateSampler(&invalidSamplerDesc));
+    }
+
+    {
+        wgpu::SamplerDescriptor invalidSamplerDesc = samplerDesc;
+        invalidSamplerDesc.addressModeV = wgpu::AddressMode::Repeat;
+        ASSERT_DEVICE_ERROR(device.CreateSampler(&invalidSamplerDesc));
+    }
+
+    {
+        wgpu::SamplerDescriptor invalidSamplerDesc = samplerDesc;
+        invalidSamplerDesc.addressModeW = wgpu::AddressMode::Repeat;
+        ASSERT_DEVICE_ERROR(device.CreateSampler(&invalidSamplerDesc));
+    }
+    {
+        wgpu::SamplerDescriptor invalidSamplerDesc = samplerDesc;
+        invalidSamplerDesc.maxAnisotropy = 2;
+        ASSERT_DEVICE_ERROR(device.CreateSampler(&invalidSamplerDesc));
+    }
+}
+
 // Test that only OpaqueYCbCrAndroid textures can be used to create YCbCr views.
 TEST_F(YCbCrVulkanSamplersTest, YCbCrTextureViewRequiresOpaqueYCbCrAndroid) {
     wgpu::TextureViewDescriptor viewDesc{};