Implement depth24unorm-stencil8 and depth32float-stencil8 formats

- Add format implementation on D3D12, Metal and Vulkan
- Add more formats in depth/stencil copy, sampling and load op tests and
  refactor them to test with parameters.

BUG=dawn:690

Change-Id: I829d1eea3ce35ffb39417ea23fb8afba6d542769
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/73180
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Hao Li <hao.x.li@intel.com>
diff --git a/src/common/Math.h b/src/common/Math.h
index 69cab24..408c0a9 100644
--- a/src/common/Math.h
+++ b/src/common/Math.h
@@ -89,6 +89,11 @@
 float Float16ToFloat32(uint16_t fp16);
 bool IsFloat16NaN(uint16_t fp16);
 
+template <typename T>
+T FloatToUnorm(float value) {
+    return static_cast<T>(value * static_cast<float>(std::numeric_limits<T>::max()));
+}
+
 float SRGBToLinear(float srgb);
 
 template <typename T1,
diff --git a/src/dawn_native/d3d12/AdapterD3D12.cpp b/src/dawn_native/d3d12/AdapterD3D12.cpp
index 6e40c7a..df8ec50 100644
--- a/src/dawn_native/d3d12/AdapterD3D12.cpp
+++ b/src/dawn_native/d3d12/AdapterD3D12.cpp
@@ -135,6 +135,9 @@
         mSupportedFeatures.EnableFeature(Feature::TextureCompressionBC);
         mSupportedFeatures.EnableFeature(Feature::PipelineStatisticsQuery);
         mSupportedFeatures.EnableFeature(Feature::MultiPlanarFormats);
+        mSupportedFeatures.EnableFeature(Feature::Depth24UnormStencil8);
+        mSupportedFeatures.EnableFeature(Feature::Depth32FloatStencil8);
+
         return {};
     }
 
diff --git a/src/dawn_native/d3d12/TextureD3D12.cpp b/src/dawn_native/d3d12/TextureD3D12.cpp
index 7a913a2..4168134 100644
--- a/src/dawn_native/d3d12/TextureD3D12.cpp
+++ b/src/dawn_native/d3d12/TextureD3D12.cpp
@@ -178,7 +178,10 @@
                 case wgpu::TextureFormat::Depth24Plus:
                     return DXGI_FORMAT_R32_TYPELESS;
 
+                case wgpu::TextureFormat::Depth24UnormStencil8:
+                    return DXGI_FORMAT_R24G8_TYPELESS;
                 case wgpu::TextureFormat::Depth24PlusStencil8:
+                case wgpu::TextureFormat::Depth32FloatStencil8:
                     return DXGI_FORMAT_R32G8X24_TYPELESS;
 
                 case wgpu::TextureFormat::BC1RGBAUnorm:
@@ -252,10 +255,6 @@
                 case wgpu::TextureFormat::R8BG8Biplanar420Unorm:
                 // TODO(dawn:666): implement stencil8
                 case wgpu::TextureFormat::Stencil8:
-                // TODO(dawn:690): implement depth24unorm-stencil8
-                case wgpu::TextureFormat::Depth24UnormStencil8:
-                // TODO(dawn:690): implement depth32float-stencil8
-                case wgpu::TextureFormat::Depth32FloatStencil8:
                 case wgpu::TextureFormat::Undefined:
                     UNREACHABLE();
             }
@@ -342,14 +341,16 @@
             case wgpu::TextureFormat::RGBA32Float:
                 return DXGI_FORMAT_R32G32B32A32_FLOAT;
 
-            case wgpu::TextureFormat::Depth32Float:
-                return DXGI_FORMAT_D32_FLOAT;
-            case wgpu::TextureFormat::Depth24Plus:
-                return DXGI_FORMAT_D32_FLOAT;
-            case wgpu::TextureFormat::Depth24PlusStencil8:
-                return DXGI_FORMAT_D32_FLOAT_S8X24_UINT;
             case wgpu::TextureFormat::Depth16Unorm:
                 return DXGI_FORMAT_D16_UNORM;
+            case wgpu::TextureFormat::Depth32Float:
+            case wgpu::TextureFormat::Depth24Plus:
+                return DXGI_FORMAT_D32_FLOAT;
+            case wgpu::TextureFormat::Depth24UnormStencil8:
+                return DXGI_FORMAT_D24_UNORM_S8_UINT;
+            case wgpu::TextureFormat::Depth24PlusStencil8:
+            case wgpu::TextureFormat::Depth32FloatStencil8:
+                return DXGI_FORMAT_D32_FLOAT_S8X24_UINT;
 
             case wgpu::TextureFormat::BC1RGBAUnorm:
                 return DXGI_FORMAT_BC1_UNORM;
@@ -425,10 +426,6 @@
 
             // TODO(dawn:666): implement stencil8
             case wgpu::TextureFormat::Stencil8:
-            // TODO(dawn:690): implement depth24unorm-stencil8
-            case wgpu::TextureFormat::Depth24UnormStencil8:
-            // TODO(dawn:690): implement depth32float-stencil8
-            case wgpu::TextureFormat::Depth32FloatStencil8:
             case wgpu::TextureFormat::Undefined:
                 UNREACHABLE();
         }
@@ -699,7 +696,9 @@
         ASSERT(GetFormat().aspects & aspect);
 
         switch (GetFormat().format) {
+            case wgpu::TextureFormat::Depth24UnormStencil8:
             case wgpu::TextureFormat::Depth24PlusStencil8:
+            case wgpu::TextureFormat::Depth32FloatStencil8:
                 switch (aspect) {
                     case Aspect::Depth:
                         return DXGI_FORMAT_R32_FLOAT;
@@ -1186,7 +1185,39 @@
                 case wgpu::TextureFormat::Depth16Unorm:
                     mSrvDesc.Format = DXGI_FORMAT_R16_UNORM;
                     break;
+                case wgpu::TextureFormat::Depth24UnormStencil8:
+                    switch (descriptor->aspect) {
+                        case wgpu::TextureAspect::DepthOnly:
+                            planeSlice = 0;
+                            mSrvDesc.Format = DXGI_FORMAT_R24_UNORM_X8_TYPELESS;
+                            break;
+                        case wgpu::TextureAspect::StencilOnly:
+                            planeSlice = 1;
+                            mSrvDesc.Format = DXGI_FORMAT_X24_TYPELESS_G8_UINT;
+                            // Stencil is accessed using the .g component in the shader.
+                            // Map it to the zeroth component to match other APIs.
+                            mSrvDesc.Shader4ComponentMapping =
+                                D3D12_ENCODE_SHADER_4_COMPONENT_MAPPING(
+                                    D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_1,
+                                    D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_0,
+                                    D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_0,
+                                    D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_1);
+                            break;
+                        case wgpu::TextureAspect::All:
+                            // A single aspect is not selected. The texture view must not be
+                            // sampled.
+                            mSrvDesc.Format = DXGI_FORMAT_UNKNOWN;
+                            break;
+
+                        // Depth formats cannot use plane aspects.
+                        case wgpu::TextureAspect::Plane0Only:
+                        case wgpu::TextureAspect::Plane1Only:
+                            UNREACHABLE();
+                            break;
+                    }
+                    break;
                 case wgpu::TextureFormat::Depth24PlusStencil8:
+                case wgpu::TextureFormat::Depth32FloatStencil8:
                     switch (descriptor->aspect) {
                         case wgpu::TextureAspect::DepthOnly:
                             planeSlice = 0;
diff --git a/src/dawn_native/metal/BackendMTL.mm b/src/dawn_native/metal/BackendMTL.mm
index 8b0194b..ee7e8e0 100644
--- a/src/dawn_native/metal/BackendMTL.mm
+++ b/src/dawn_native/metal/BackendMTL.mm
@@ -324,6 +324,17 @@
                 mSupportedFeatures.EnableFeature(Feature::DepthClamping);
             }
 
+            if (@available(macOS 10.11, iOS 9.0, *)) {
+                mSupportedFeatures.EnableFeature(Feature::Depth32FloatStencil8);
+            }
+
+#if defined(DAWN_PLATFORM_MACOS)
+            // MTLPixelFormatDepth24Unorm_Stencil8 is only available on macOS 10.11+
+            if ([*mDevice isDepth24Stencil8PixelFormatSupported]) {
+                mSupportedFeatures.EnableFeature(Feature::Depth24UnormStencil8);
+            }
+#endif
+
             return {};
         }
 
diff --git a/src/dawn_native/metal/TextureMTL.mm b/src/dawn_native/metal/TextureMTL.mm
index 37eb9bc..7a98886 100644
--- a/src/dawn_native/metal/TextureMTL.mm
+++ b/src/dawn_native/metal/TextureMTL.mm
@@ -222,6 +222,7 @@
             case wgpu::TextureFormat::Depth24Plus:
                 return MTLPixelFormatDepth32Float;
             case wgpu::TextureFormat::Depth24PlusStencil8:
+            case wgpu::TextureFormat::Depth32FloatStencil8:
                 return MTLPixelFormatDepth32Float_Stencil8;
             case wgpu::TextureFormat::Depth16Unorm:
                 if (@available(macOS 10.12, iOS 13.0, *)) {
@@ -232,6 +233,9 @@
                 }
 
 #if defined(DAWN_PLATFORM_MACOS)
+            case wgpu::TextureFormat::Depth24UnormStencil8:
+                return MTLPixelFormatDepth24Unorm_Stencil8;
+
             case wgpu::TextureFormat::BC1RGBAUnorm:
                 return MTLPixelFormatBC1_RGBA;
             case wgpu::TextureFormat::BC1RGBAUnormSrgb:
@@ -261,6 +265,8 @@
             case wgpu::TextureFormat::BC7RGBAUnormSrgb:
                 return MTLPixelFormatBC7_RGBAUnorm_sRGB;
 #else
+            case wgpu::TextureFormat::Depth24UnormStencil8:
+
             case wgpu::TextureFormat::BC1RGBAUnorm:
             case wgpu::TextureFormat::BC1RGBAUnormSrgb:
             case wgpu::TextureFormat::BC2RGBAUnorm:
@@ -321,10 +327,6 @@
 
             // TODO(dawn:666): implement stencil8
             case wgpu::TextureFormat::Stencil8:
-            // TODO(dawn:690): implement depth24unorm-stencil8
-            case wgpu::TextureFormat::Depth24UnormStencil8:
-            // TODO(dawn:690): implement depth32float-stencil8
-            case wgpu::TextureFormat::Depth32FloatStencil8:
             case wgpu::TextureFormat::Undefined:
                 UNREACHABLE();
         }
@@ -746,8 +748,17 @@
             MTLPixelFormat format = MetalPixelFormat(descriptor->format);
             if (descriptor->aspect == wgpu::TextureAspect::StencilOnly) {
                 if (@available(macOS 10.12, iOS 10.0, *)) {
-                    ASSERT(format == MTLPixelFormatDepth32Float_Stencil8);
-                    format = MTLPixelFormatX32_Stencil8;
+                    if (format == MTLPixelFormatDepth32Float_Stencil8) {
+                        format = MTLPixelFormatX32_Stencil8;
+                    }
+#if defined(DAWN_PLATFORM_MACOS)
+                    else if (format == MTLPixelFormatDepth24Unorm_Stencil8) {
+                        format = MTLPixelFormatX24_Stencil8;
+                    }
+#endif
+                    else {
+                        UNREACHABLE();
+                    }
                 } else {
                     // TODO(enga): Add a workaround to back combined depth/stencil textures
                     // with Sampled usage using two separate textures.
diff --git a/src/dawn_native/vulkan/AdapterVk.cpp b/src/dawn_native/vulkan/AdapterVk.cpp
index 4f8b1c9..4eea820 100644
--- a/src/dawn_native/vulkan/AdapterVk.cpp
+++ b/src/dawn_native/vulkan/AdapterVk.cpp
@@ -151,6 +151,14 @@
             mSupportedFeatures.EnableFeature(Feature::TimestampQuery);
         }
 
+        if (IsDepthStencilFormatSupported(VK_FORMAT_D24_UNORM_S8_UINT)) {
+            mSupportedFeatures.EnableFeature(Feature::Depth24UnormStencil8);
+        }
+
+        if (IsDepthStencilFormatSupported(VK_FORMAT_D32_SFLOAT_S8_UINT)) {
+            mSupportedFeatures.EnableFeature(Feature::Depth32FloatStencil8);
+        }
+
 #if defined(DAWN_USE_SYNC_FDS)
         // TODO(chromium:1258986): Precisely enable the feature by querying the device's format
         // features.
diff --git a/src/dawn_native/vulkan/TextureVk.cpp b/src/dawn_native/vulkan/TextureVk.cpp
index 1b4c7d3..ab89859 100644
--- a/src/dawn_native/vulkan/TextureVk.cpp
+++ b/src/dawn_native/vulkan/TextureVk.cpp
@@ -311,6 +311,10 @@
                 } else {
                     return VK_FORMAT_D24_UNORM_S8_UINT;
                 }
+            case wgpu::TextureFormat::Depth24UnormStencil8:
+                return VK_FORMAT_D24_UNORM_S8_UINT;
+            case wgpu::TextureFormat::Depth32FloatStencil8:
+                return VK_FORMAT_D32_SFLOAT_S8_UINT;
 
             case wgpu::TextureFormat::BC1RGBAUnorm:
                 return VK_FORMAT_BC1_RGBA_UNORM_BLOCK;
@@ -424,10 +428,6 @@
 
             // TODO(dawn:666): implement stencil8
             case wgpu::TextureFormat::Stencil8:
-            // TODO(dawn:690): implement depth24unorm-stencil8
-            case wgpu::TextureFormat::Depth24UnormStencil8:
-            // TODO(dawn:690): implement depth32float-stencil8
-            case wgpu::TextureFormat::Depth32FloatStencil8:
             case wgpu::TextureFormat::Undefined:
                 break;
         }
diff --git a/src/tests/end2end/DepthStencilCopyTests.cpp b/src/tests/end2end/DepthStencilCopyTests.cpp
index 8b875ce..2edd3ee 100644
--- a/src/tests/end2end/DepthStencilCopyTests.cpp
+++ b/src/tests/end2end/DepthStencilCopyTests.cpp
@@ -22,10 +22,23 @@
 #include "utils/TextureUtils.h"
 #include "utils/WGPUHelpers.h"
 
-class DepthStencilCopyTests : public DawnTest {
+namespace {
+    using TextureFormat = wgpu::TextureFormat;
+    DAWN_TEST_PARAM_STRUCT(DepthStencilCopyTestParams, TextureFormat);
+
+    constexpr std::array<wgpu::TextureFormat, 3> kValidDepthCopyTextureFormats = {
+        wgpu::TextureFormat::Depth16Unorm,
+        wgpu::TextureFormat::Depth32Float,
+        wgpu::TextureFormat::Depth32FloatStencil8,
+    };
+}  // namespace
+
+class DepthStencilCopyTests : public DawnTestWithParams<DepthStencilCopyTestParams> {
   protected:
     void SetUp() override {
-        DawnTest::SetUp();
+        DawnTestWithParams<DepthStencilCopyTestParams>::SetUp();
+
+        DAWN_TEST_UNSUPPORTED_IF(!mIsFormatSupported);
 
         // Draw a square in the bottom left quarter of the screen.
         mVertexModule = utils::CreateShaderModule(device, R"(
@@ -42,13 +55,56 @@
             })");
     }
 
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        switch (GetParam().mTextureFormat) {
+            case wgpu::TextureFormat::Depth24UnormStencil8:
+                if (SupportsFeatures({wgpu::FeatureName::Depth24UnormStencil8})) {
+                    mIsFormatSupported = true;
+                    return {wgpu::FeatureName::Depth24UnormStencil8};
+                }
+                return {};
+            case wgpu::TextureFormat::Depth32FloatStencil8:
+                if (SupportsFeatures({wgpu::FeatureName::Depth32FloatStencil8})) {
+                    mIsFormatSupported = true;
+                    return {wgpu::FeatureName::Depth32FloatStencil8};
+                }
+                return {};
+            default:
+                mIsFormatSupported = true;
+                return {};
+        }
+    }
+
+    bool IsValidDepthCopyTextureFormat() {
+        switch (GetParam().mTextureFormat) {
+            case wgpu::TextureFormat::Depth16Unorm:
+            case wgpu::TextureFormat::Depth32Float:
+            case wgpu::TextureFormat::Depth32FloatStencil8:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    wgpu::Texture CreateTexture(uint32_t width,
+                                uint32_t height,
+                                wgpu::TextureUsage usage,
+                                uint32_t mipLevelCount = 1) {
+        wgpu::TextureDescriptor texDescriptor = {};
+        texDescriptor.size = {width, height, 1};
+        texDescriptor.format = GetParam().mTextureFormat;
+        texDescriptor.usage = usage;
+        texDescriptor.mipLevelCount = mipLevelCount;
+        return device.CreateTexture(&texDescriptor);
+    }
+
     wgpu::Texture CreateDepthStencilTexture(uint32_t width,
                                             uint32_t height,
                                             wgpu::TextureUsage usage,
                                             uint32_t mipLevelCount = 1) {
         wgpu::TextureDescriptor texDescriptor = {};
         texDescriptor.size = {width, height, 1};
-        texDescriptor.format = wgpu::TextureFormat::Depth24PlusStencil8;
+        texDescriptor.format = GetParam().mTextureFormat;
         texDescriptor.usage = usage;
         texDescriptor.mipLevelCount = mipLevelCount;
         return device.CreateTexture(&texDescriptor);
@@ -60,7 +116,7 @@
                                      uint32_t mipLevelCount = 1) {
         wgpu::TextureDescriptor texDescriptor = {};
         texDescriptor.size = {width, height, 1};
-        texDescriptor.format = wgpu::TextureFormat::Depth32Float;
+        texDescriptor.format = GetParam().mTextureFormat;
         texDescriptor.usage = usage;
         texDescriptor.mipLevelCount = mipLevelCount;
         return device.CreateTexture(&texDescriptor);
@@ -97,7 +153,7 @@
         renderPassDesc.cDepthStencilAttachmentInfo.clearDepth = clearDepth;
 
         utils::ComboRenderPipelineDescriptor renderPipelineDesc;
-        PopulatePipelineDescriptorWriteDepth(&renderPipelineDesc, wgpu::TextureFormat::Depth32Float,
+        PopulatePipelineDescriptorWriteDepth(&renderPipelineDesc, GetParam().mTextureFormat,
                                              regionDepth);
 
         wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&renderPipelineDesc);
@@ -129,8 +185,8 @@
         renderPassDesc.cDepthStencilAttachmentInfo.clearStencil = clearStencil;
 
         utils::ComboRenderPipelineDescriptor renderPipelineDesc;
-        PopulatePipelineDescriptorWriteDepth(&renderPipelineDesc,
-                                             wgpu::TextureFormat::Depth24PlusStencil8, regionDepth);
+        PopulatePipelineDescriptorWriteDepth(&renderPipelineDesc, GetParam().mTextureFormat,
+                                             regionDepth);
         renderPipelineDesc.cDepthStencil.stencilFront.passOp = wgpu::StencilOperation::Replace;
 
         wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&renderPipelineDesc);
@@ -183,97 +239,11 @@
     }
 
     wgpu::ShaderModule mVertexModule;
+
+  private:
+    bool mIsFormatSupported = false;
 };
 
-// Test copying the depth-only aspect into a buffer.
-TEST_P(DepthStencilCopyTests, FromDepthAspect) {
-    constexpr uint32_t kWidth = 4;
-    constexpr uint32_t kHeight = 4;
-
-    wgpu::Texture depthTexture = CreateDepthTexture(
-        kWidth, kHeight, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc);
-
-    InitializeDepthTextureRegion(depthTexture, 0.f, 0.3f);
-
-    // This expectation is the test as it performs the CopyTextureToBuffer.
-    std::vector<float> expectedData = {
-        0.0, 0.0, 0.0, 0.0,  //
-        0.0, 0.0, 0.0, 0.0,  //
-        0.3, 0.3, 0.0, 0.0,  //
-        0.3, 0.3, 0.0, 0.0,  //
-    };
-    EXPECT_TEXTURE_EQ(expectedData.data(), depthTexture, {0, 0}, {kWidth, kHeight}, 0,
-                      wgpu::TextureAspect::DepthOnly);
-}
-
-// Test copying the stencil-only aspect into a buffer.
-TEST_P(DepthStencilCopyTests, FromStencilAspect) {
-    // TODO(crbug.com/dawn/667): Work around the fact that some platforms are unable to read
-    // stencil.
-    DAWN_TEST_UNSUPPORTED_IF(HasToggleEnabled("disable_depth_stencil_read"));
-
-    constexpr uint32_t kWidth = 4;
-    constexpr uint32_t kHeight = 4;
-
-    wgpu::Texture depthStencilTexture = CreateDepthStencilTexture(
-        kWidth, kHeight, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc);
-
-    InitializeDepthStencilTextureRegion(depthStencilTexture, 0.f, 0.3f, 0u, 1u);
-
-    // This expectation is the test as it performs the CopyTextureToBuffer.
-    std::vector<uint8_t> expectedData = {
-        0u, 0u, 0u, 0u,  //
-        0u, 0u, 0u, 0u,  //
-        1u, 1u, 0u, 0u,  //
-        1u, 1u, 0u, 0u,  //
-    };
-    EXPECT_TEXTURE_EQ(expectedData.data(), depthStencilTexture, {0, 0}, {kWidth, kHeight}, 0,
-                      wgpu::TextureAspect::StencilOnly);
-}
-
-// Test copying the non-zero mip, stencil-only aspect into a buffer.
-TEST_P(DepthStencilCopyTests, FromNonZeroMipStencilAspect) {
-    // TODO(crbug.com/dawn/704): Readback after clear via stencil copy does not work
-    // on some Intel drivers.
-    DAWN_SUPPRESS_TEST_IF(IsMetal() && IsIntel());
-
-    // TODO(crbug.com/dawn/667): Work around some platforms' inability to read back stencil.
-    DAWN_TEST_UNSUPPORTED_IF(HasToggleEnabled("disable_depth_stencil_read"));
-
-    wgpu::Texture depthStencilTexture = CreateDepthStencilTexture(
-        9, 9, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc, 2);
-
-    InitializeDepthStencilTextureRegion(depthStencilTexture, 0.f, 0.3f, 0u, 1u, 1u);
-
-    // This expectation is the test as it performs the CopyTextureToBuffer.
-    std::vector<uint8_t> expectedData = {
-        0u, 0u, 0u, 0u,  //
-        0u, 0u, 0u, 0u,  //
-        1u, 1u, 0u, 0u,  //
-        1u, 1u, 0u, 0u,  //
-    };
-    EXPECT_TEXTURE_EQ(expectedData.data(), depthStencilTexture, {0, 0}, {4, 4}, 1,
-                      wgpu::TextureAspect::StencilOnly);
-}
-
-// Test copying the non-zero mip, depth-only aspect into a buffer.
-TEST_P(DepthStencilCopyTests, FromNonZeroMipDepthAspect) {
-    wgpu::Texture depthTexture = CreateDepthTexture(
-        9, 9, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc, 2);
-
-    InitializeDepthTextureRegion(depthTexture, 0.f, 0.4f, 1);
-
-    // This expectation is the test as it performs the CopyTextureToBuffer.
-    std::vector<float> expectedData = {
-        0.0, 0.0, 0.0, 0.0,  //
-        0.0, 0.0, 0.0, 0.0,  //
-        0.4, 0.4, 0.0, 0.0,  //
-        0.4, 0.4, 0.0, 0.0,  //
-    };
-    EXPECT_TEXTURE_EQ(expectedData.data(), depthTexture, {0, 0}, {4, 4}, 1,
-                      wgpu::TextureAspect::DepthOnly);
-}
-
 // Test copying both aspects in a T2T copy, then copying only stencil.
 TEST_P(DepthStencilCopyTests, T2TBothAspectsThenCopyStencil) {
     // TODO(crbug.com/dawn/704): Readback after clear via stencil copy does not work
@@ -354,6 +324,8 @@
 
 // Test copying both aspects in a T2T copy, then copying only depth.
 TEST_P(DepthStencilCopyTests, T2TBothAspectsThenCopyDepth) {
+    DAWN_TEST_UNSUPPORTED_IF(!IsValidDepthCopyTextureFormat());
+
     constexpr uint32_t kWidth = 4;
     constexpr uint32_t kHeight = 4;
 
@@ -361,8 +333,7 @@
         0.1f, 0.3f, 1u, 3u, kWidth, kHeight, wgpu::TextureUsage::RenderAttachment);
 
     // Check the depth
-    ExpectAttachmentDepthTestData(texture, wgpu::TextureFormat::Depth24PlusStencil8, kWidth,
-                                  kHeight, 0, 0,
+    ExpectAttachmentDepthTestData(texture, GetParam().mTextureFormat, kWidth, kHeight, 0, 0,
                                   {
                                       0.1, 0.1, 0.1, 0.1,  //
                                       0.1, 0.1, 0.1, 0.1,  //
@@ -373,11 +344,13 @@
 
 // Test copying both aspects in a T2T copy, then copying only depth at a nonzero mip.
 TEST_P(DepthStencilCopyTests, T2TBothAspectsThenCopyNonZeroMipDepth) {
+    DAWN_TEST_UNSUPPORTED_IF(!IsValidDepthCopyTextureFormat());
+
     wgpu::Texture texture = CreateInitializeDepthStencilTextureAndCopyT2T(
         0.1f, 0.3f, 1u, 3u, 8, 8, wgpu::TextureUsage::RenderAttachment, 1);
 
     // Check the depth
-    ExpectAttachmentDepthTestData(texture, wgpu::TextureFormat::Depth24PlusStencil8, 4, 4, 0, 1,
+    ExpectAttachmentDepthTestData(texture, GetParam().mTextureFormat, 4, 4, 0, 1,
                                   {
                                       0.1, 0.1, 0.1, 0.1,  //
                                       0.1, 0.1, 0.1, 0.1,  //
@@ -388,6 +361,8 @@
 
 // Test copying both aspects in a T2T copy, then copying stencil, then copying depth
 TEST_P(DepthStencilCopyTests, T2TBothAspectsThenCopyStencilThenDepth) {
+    DAWN_TEST_UNSUPPORTED_IF(!IsValidDepthCopyTextureFormat());
+
     // TODO(crbug.com/dawn/667): Work around some platforms' inability to read back stencil.
     DAWN_TEST_UNSUPPORTED_IF(HasToggleEnabled("disable_depth_stencil_read"));
 
@@ -409,8 +384,7 @@
                       wgpu::TextureAspect::StencilOnly);
 
     // Check the depth
-    ExpectAttachmentDepthTestData(texture, wgpu::TextureFormat::Depth24PlusStencil8, kWidth,
-                                  kHeight, 0, 0,
+    ExpectAttachmentDepthTestData(texture, GetParam().mTextureFormat, kWidth, kHeight, 0, 0,
                                   {
                                       0.1, 0.1, 0.1, 0.1,  //
                                       0.1, 0.1, 0.1, 0.1,  //
@@ -421,6 +395,8 @@
 
 // Test copying both aspects in a T2T copy, then copying depth, then copying stencil
 TEST_P(DepthStencilCopyTests, T2TBothAspectsThenCopyDepthThenStencil) {
+    DAWN_TEST_UNSUPPORTED_IF(!IsValidDepthCopyTextureFormat());
+
     // TODO(crbug.com/dawn/704): Readback after clear via stencil copy does not work
     // on some Intel drivers.
     // It seems like the depth readback copy mutates the stencil because the previous
@@ -440,8 +416,7 @@
         wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::RenderAttachment);
 
     // Check the depth
-    ExpectAttachmentDepthTestData(texture, wgpu::TextureFormat::Depth24PlusStencil8, kWidth,
-                                  kHeight, 0, 0,
+    ExpectAttachmentDepthTestData(texture, GetParam().mTextureFormat, kWidth, kHeight, 0, 0,
                                   {
                                       0.1, 0.1, 0.1, 0.1,  //
                                       0.1, 0.1, 0.1, 0.1,  //
@@ -460,8 +435,137 @@
                       wgpu::TextureAspect::StencilOnly);
 }
 
+class DepthCopyTests : public DepthStencilCopyTests {};
+
+// Test copying the depth-only aspect into a buffer.
+TEST_P(DepthCopyTests, FromDepthAspect) {
+    // TODO(crbug.com/dawn/1237): Depth16Unorm test failed on OpenGL and OpenGLES which says
+    // Invalid format and type combination in glReadPixels
+    DAWN_TEST_UNSUPPORTED_IF(GetParam().mTextureFormat == wgpu::TextureFormat::Depth16Unorm &&
+                             (IsOpenGL() || IsOpenGLES()));
+
+    constexpr uint32_t kWidth = 4;
+    constexpr uint32_t kHeight = 4;
+
+    wgpu::Texture texture = CreateTexture(
+        kWidth, kHeight, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc);
+
+    constexpr float kInitDepth = 0.2f;
+    InitializeDepthTextureRegion(texture, 0.f, kInitDepth);
+
+    // This expectation is the test as it performs the CopyTextureToBuffer.
+    if (GetParam().mTextureFormat == wgpu::TextureFormat::Depth16Unorm) {
+        uint16_t expected = FloatToUnorm<uint16_t>(kInitDepth);
+        std::vector<uint16_t> expectedData = {
+            0,        0,        0, 0,  //
+            0,        0,        0, 0,  //
+            expected, expected, 0, 0,  //
+            expected, expected, 0, 0,  //
+        };
+        EXPECT_TEXTURE_EQ(expectedData.data(), texture, {0, 0}, {kWidth, kHeight}, 0,
+                          wgpu::TextureAspect::DepthOnly);
+    } else {
+        std::vector<float> expectedData = {
+            0.0,        0.0,        0.0, 0.0,  //
+            0.0,        0.0,        0.0, 0.0,  //
+            kInitDepth, kInitDepth, 0.0, 0.0,  //
+            kInitDepth, kInitDepth, 0.0, 0.0,  //
+        };
+        EXPECT_TEXTURE_EQ(expectedData.data(), texture, {0, 0}, {kWidth, kHeight}, 0,
+                          wgpu::TextureAspect::DepthOnly);
+    }
+}
+
+// Test copying the non-zero mip, depth-only aspect into a buffer.
+TEST_P(DepthCopyTests, FromNonZeroMipDepthAspect) {
+    // TODO(crbug.com/dawn/1237): Depth16Unorm test failed on OpenGL and OpenGLES which says
+    // Invalid format and type combination in glReadPixels
+    DAWN_TEST_UNSUPPORTED_IF(GetParam().mTextureFormat == wgpu::TextureFormat::Depth16Unorm &&
+                             (IsOpenGL() || IsOpenGLES()));
+
+    wgpu::Texture depthTexture = CreateDepthTexture(
+        9, 9, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc, 2);
+
+    constexpr float kInitDepth = 0.4f;
+    InitializeDepthTextureRegion(depthTexture, 0.f, kInitDepth, 1);
+
+    // This expectation is the test as it performs the CopyTextureToBuffer.
+    if (GetParam().mTextureFormat == wgpu::TextureFormat::Depth16Unorm) {
+        uint16_t expected = FloatToUnorm<uint16_t>(kInitDepth);
+        std::vector<uint16_t> expectedData = {
+            0,        0,        0, 0,  //
+            0,        0,        0, 0,  //
+            expected, expected, 0, 0,  //
+            expected, expected, 0, 0,  //
+        };
+        EXPECT_TEXTURE_EQ(expectedData.data(), depthTexture, {0, 0}, {4, 4}, 1,
+                          wgpu::TextureAspect::DepthOnly);
+    } else {
+        std::vector<float> expectedData = {
+            0.0,        0.0,        0.0, 0.0,  //
+            0.0,        0.0,        0.0, 0.0,  //
+            kInitDepth, kInitDepth, 0.0, 0.0,  //
+            kInitDepth, kInitDepth, 0.0, 0.0,  //
+        };
+        EXPECT_TEXTURE_EQ(expectedData.data(), depthTexture, {0, 0}, {4, 4}, 1,
+                          wgpu::TextureAspect::DepthOnly);
+    }
+}
+
+class StencilCopyTests : public DepthStencilCopyTests {};
+
+// Test copying the stencil-only aspect into a buffer.
+TEST_P(StencilCopyTests, FromStencilAspect) {
+    // TODO(crbug.com/dawn/667): Work around the fact that some platforms are unable to read
+    // stencil.
+    DAWN_TEST_UNSUPPORTED_IF(HasToggleEnabled("disable_depth_stencil_read"));
+
+    constexpr uint32_t kWidth = 4;
+    constexpr uint32_t kHeight = 4;
+
+    wgpu::Texture depthStencilTexture = CreateDepthStencilTexture(
+        kWidth, kHeight, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc);
+
+    InitializeDepthStencilTextureRegion(depthStencilTexture, 0.f, 0.3f, 0u, 1u);
+
+    // This expectation is the test as it performs the CopyTextureToBuffer.
+    std::vector<uint8_t> expectedData = {
+        0u, 0u, 0u, 0u,  //
+        0u, 0u, 0u, 0u,  //
+        1u, 1u, 0u, 0u,  //
+        1u, 1u, 0u, 0u,  //
+    };
+    EXPECT_TEXTURE_EQ(expectedData.data(), depthStencilTexture, {0, 0}, {kWidth, kHeight}, 0,
+                      wgpu::TextureAspect::StencilOnly);
+}
+
+// Test copying the non-zero mip, stencil-only aspect into a buffer.
+TEST_P(StencilCopyTests, FromNonZeroMipStencilAspect) {
+    // TODO(crbug.com/dawn/704): Readback after clear via stencil copy does not work
+    // on some Intel drivers.
+    DAWN_SUPPRESS_TEST_IF(IsMetal() && IsIntel());
+
+    // TODO(crbug.com/dawn/667): Work around some platforms' inability to read back stencil.
+    DAWN_TEST_UNSUPPORTED_IF(HasToggleEnabled("disable_depth_stencil_read"));
+
+    wgpu::Texture depthStencilTexture = CreateDepthStencilTexture(
+        9, 9, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc, 2);
+
+    InitializeDepthStencilTextureRegion(depthStencilTexture, 0.f, 0.3f, 0u, 1u, 1u);
+
+    // This expectation is the test as it performs the CopyTextureToBuffer.
+    std::vector<uint8_t> expectedData = {
+        0u, 0u, 0u, 0u,  //
+        0u, 0u, 0u, 0u,  //
+        1u, 1u, 0u, 0u,  //
+        1u, 1u, 0u, 0u,  //
+    };
+    EXPECT_TEXTURE_EQ(expectedData.data(), depthStencilTexture, {0, 0}, {4, 4}, 1,
+                      wgpu::TextureAspect::StencilOnly);
+}
+
 // Test copying to the stencil-aspect of a buffer
-TEST_P(DepthStencilCopyTests, ToStencilAspect) {
+TEST_P(StencilCopyTests, ToStencilAspect) {
     // Copies to a single aspect are unsupported on OpenGL.
     DAWN_TEST_UNSUPPORTED_IF(IsOpenGL());
     DAWN_TEST_UNSUPPORTED_IF(IsOpenGLES());
@@ -531,7 +635,7 @@
             [[stage(fragment)]] fn main() {
             })");
         wgpu::DepthStencilState* depthStencil =
-            renderPipelineDesc.EnableDepthStencil(wgpu::TextureFormat::Depth24PlusStencil8);
+            renderPipelineDesc.EnableDepthStencil(GetParam().mTextureFormat);
         depthStencil->stencilFront.passOp = wgpu::StencilOperation::DecrementClamp;
         renderPipelineDesc.cFragment.targetCount = 0;
 
@@ -557,8 +661,8 @@
     EXPECT_TEXTURE_EQ(expectedStencilData.data(), depthStencilTexture, {0, 0}, {kWidth, kHeight}, 0,
                       wgpu::TextureAspect::StencilOnly);
 
-    ExpectAttachmentDepthTestData(depthStencilTexture, wgpu::TextureFormat::Depth24PlusStencil8,
-                                  kWidth, kHeight, 0, 0,
+    ExpectAttachmentDepthTestData(depthStencilTexture, GetParam().mTextureFormat, kWidth, kHeight,
+                                  0, 0,
                                   {
                                       0.7, 0.7, 0.7, 0.7,  //
                                       0.7, 0.7, 0.7, 0.7,  //
@@ -567,9 +671,20 @@
                                   });
 }
 
-DAWN_INSTANTIATE_TEST(DepthStencilCopyTests,
-                      D3D12Backend(),
-                      MetalBackend(),
-                      OpenGLBackend(),
-                      OpenGLESBackend(),
-                      VulkanBackend());
+DAWN_INSTANTIATE_TEST_P(DepthStencilCopyTests,
+                        {D3D12Backend(), MetalBackend(), OpenGLBackend(), OpenGLESBackend(),
+                         VulkanBackend()},
+                        std::vector<wgpu::TextureFormat>(utils::kDepthAndStencilFormats.begin(),
+                                                         utils::kDepthAndStencilFormats.end()));
+
+DAWN_INSTANTIATE_TEST_P(DepthCopyTests,
+                        {D3D12Backend(), MetalBackend(), OpenGLBackend(), OpenGLESBackend(),
+                         VulkanBackend()},
+                        std::vector<wgpu::TextureFormat>(kValidDepthCopyTextureFormats.begin(),
+                                                         kValidDepthCopyTextureFormats.end()));
+
+DAWN_INSTANTIATE_TEST_P(StencilCopyTests,
+                        {D3D12Backend(), MetalBackend(), OpenGLBackend(), OpenGLESBackend(),
+                         VulkanBackend()},
+                        std::vector<wgpu::TextureFormat>(utils::kStencilFormats.begin(),
+                                                         utils::kStencilFormats.end()));
diff --git a/src/tests/end2end/DepthStencilLoadOpTests.cpp b/src/tests/end2end/DepthStencilLoadOpTests.cpp
index 8990408..e9b0796 100644
--- a/src/tests/end2end/DepthStencilLoadOpTests.cpp
+++ b/src/tests/end2end/DepthStencilLoadOpTests.cpp
@@ -62,6 +62,8 @@
         void SetUp() override {
             DawnTestWithParams<DepthStencilLoadOpTestParams>::SetUp();
 
+            DAWN_TEST_UNSUPPORTED_IF(!mIsFormatSupported);
+
             // Readback of Depth/Stencil textures not fully supported on GL right now.
             // Also depends on glTextureView which is not supported on ES.
             DAWN_SUPPRESS_TEST_IF(IsOpenGL() || IsOpenGLES());
@@ -91,6 +93,26 @@
             }
         }
 
+        std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+            switch (GetParam().mFormat) {
+                case wgpu::TextureFormat::Depth24UnormStencil8:
+                    if (SupportsFeatures({wgpu::FeatureName::Depth24UnormStencil8})) {
+                        mIsFormatSupported = true;
+                        return {wgpu::FeatureName::Depth24UnormStencil8};
+                    }
+                    return {};
+                case wgpu::TextureFormat::Depth32FloatStencil8:
+                    if (SupportsFeatures({wgpu::FeatureName::Depth32FloatStencil8})) {
+                        mIsFormatSupported = true;
+                        return {wgpu::FeatureName::Depth32FloatStencil8};
+                    }
+                    return {};
+                default:
+                    mIsFormatSupported = true;
+                    return {};
+            }
+        }
+
         void CheckMipLevel(uint32_t mipLevel) {
             uint32_t mipSize = std::max(kRTSize >> mipLevel, 1u);
 
@@ -151,6 +173,9 @@
         std::array<wgpu::TextureView, kMipLevelCount> textureViews;
         // Vector instead of array because there is no default constructor.
         std::vector<utils::ComboRenderPassDescriptor> renderPassDescriptors;
+
+      private:
+        bool mIsFormatSupported = false;
     };
 
 }  // anonymous namespace
@@ -240,7 +265,8 @@
         auto params2 = MakeParamGenerator<DepthStencilLoadOpTestParams>(
             {D3D12Backend(), D3D12Backend({}, {"use_d3d12_render_pass"}), MetalBackend(),
              OpenGLBackend(), OpenGLESBackend(), VulkanBackend()},
-            {wgpu::TextureFormat::Depth24PlusStencil8},
+            {wgpu::TextureFormat::Depth24PlusStencil8, wgpu::TextureFormat::Depth24UnormStencil8,
+             wgpu::TextureFormat::Depth32FloatStencil8},
             {Check::CopyStencil, Check::StencilTest, Check::DepthTest, Check::SampleDepth});
 
         std::vector<DepthStencilLoadOpTestParams> allParams;
diff --git a/src/tests/end2end/DepthStencilSamplingTests.cpp b/src/tests/end2end/DepthStencilSamplingTests.cpp
index 7ac870a..872e6d7 100644
--- a/src/tests/end2end/DepthStencilSamplingTests.cpp
+++ b/src/tests/end2end/DepthStencilSamplingTests.cpp
@@ -18,17 +18,8 @@
 #include "utils/WGPUHelpers.h"
 
 namespace {
-
-    constexpr wgpu::TextureFormat kDepthFormats[] = {
-        wgpu::TextureFormat::Depth32Float,
-        wgpu::TextureFormat::Depth24Plus,
-        wgpu::TextureFormat::Depth24PlusStencil8,
-        wgpu::TextureFormat::Depth16Unorm,
-    };
-
-    constexpr wgpu::TextureFormat kStencilFormats[] = {
-        wgpu::TextureFormat::Depth24PlusStencil8,
-    };
+    using TextureFormat = wgpu::TextureFormat;
+    DAWN_TEST_PARAM_STRUCT(DepthStencilSamplingTestParams, TextureFormat);
 
     constexpr wgpu::CompareFunction kCompareFunctions[] = {
         wgpu::CompareFunction::Never,        wgpu::CompareFunction::Less,
@@ -48,7 +39,7 @@
 
 }  // anonymous namespace
 
-class DepthStencilSamplingTest : public DawnTest {
+class DepthStencilSamplingTest : public DawnTestWithParams<DepthStencilSamplingTestParams> {
   protected:
     enum class TestAspect {
         Depth,
@@ -56,7 +47,9 @@
     };
 
     void SetUp() override {
-        DawnTest::SetUp();
+        DawnTestWithParams<DepthStencilSamplingTestParams>::SetUp();
+
+        DAWN_TEST_UNSUPPORTED_IF(!mIsFormatSupported);
 
         wgpu::BufferDescriptor uniformBufferDesc;
         uniformBufferDesc.usage = wgpu::BufferUsage::Uniform | wgpu::BufferUsage::CopyDst;
@@ -64,6 +57,26 @@
         mUniformBuffer = device.CreateBuffer(&uniformBufferDesc);
     }
 
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        switch (GetParam().mTextureFormat) {
+            case wgpu::TextureFormat::Depth24UnormStencil8:
+                if (SupportsFeatures({wgpu::FeatureName::Depth24UnormStencil8})) {
+                    mIsFormatSupported = true;
+                    return {wgpu::FeatureName::Depth24UnormStencil8};
+                }
+                return {};
+            case wgpu::TextureFormat::Depth32FloatStencil8:
+                if (SupportsFeatures({wgpu::FeatureName::Depth32FloatStencil8})) {
+                    mIsFormatSupported = true;
+                    return {wgpu::FeatureName::Depth32FloatStencil8};
+                }
+                return {};
+            default:
+                mIsFormatSupported = true;
+                return {};
+        }
+    }
+
     void GenerateSamplingShader(const std::vector<TestAspect>& aspects,
                                 const std::vector<uint32_t> components,
                                 std::ostringstream& shaderSource,
@@ -581,58 +594,39 @@
 
   private:
     wgpu::Buffer mUniformBuffer;
+    bool mIsFormatSupported = false;
 };
 
-// Test that sampling a depth texture with a render/compute pipeline works
-TEST_P(DepthStencilSamplingTest, SampleDepth) {
-    for (wgpu::TextureFormat format : kDepthFormats) {
-        float tolerance = 0.0f;
-        if (format == wgpu::TextureFormat::Depth16Unorm) {
-            tolerance = 0.001f;
-        }
-        // Test 0, between [0, 1], and 1.
-        DoSamplingTest(TestAspect::Depth, CreateSamplingRenderPipeline({TestAspect::Depth}, 0),
-                       format, kNormalizedTextureValues, tolerance);
-
-        DoSamplingTest(TestAspect::Depth, CreateSamplingComputePipeline({TestAspect::Depth}, 0),
-                       format, kNormalizedTextureValues, tolerance);
-    }
-}
-
-// Test that sampling a stencil texture with a render/compute pipeline works
-TEST_P(DepthStencilSamplingTest, SampleStencil) {
-    // TODO(crbug.com/dawn/593): This test requires glTextureView, which is unsupported on GLES.
-    DAWN_TEST_UNSUPPORTED_IF(IsOpenGLES());
-    for (wgpu::TextureFormat format : kStencilFormats) {
-        DoSamplingTest(TestAspect::Stencil, CreateSamplingRenderPipeline({TestAspect::Stencil}, 0),
-                       format, kStencilValues);
-
-        DoSamplingTest(TestAspect::Stencil, CreateSamplingComputePipeline({TestAspect::Stencil}, 0),
-                       format, kStencilValues);
-    }
-}
-
 // Test that sampling a depth/stencil texture at components 1, 2, and 3 yield 0, 0, and 1
 // respectively
 TEST_P(DepthStencilSamplingTest, SampleExtraComponents) {
     // TODO(crbug.com/dawn/593): This test requires glTextureView, which is unsupported on GLES.
     DAWN_TEST_UNSUPPORTED_IF(IsOpenGLES());
 
-    DoSamplingExtraStencilComponentsRenderTest(
-        TestAspect::Stencil, wgpu::TextureFormat::Depth24PlusStencil8, {uint8_t(42), uint8_t(37)});
+    wgpu::TextureFormat format = GetParam().mTextureFormat;
 
-    DoSamplingExtraStencilComponentsComputeTest(
-        TestAspect::Stencil, wgpu::TextureFormat::Depth24PlusStencil8, {uint8_t(42), uint8_t(37)});
+    // TODO(crbug.com/dawn/1239): depth24unorm-stencil8 fails on D3D12 Nvidia old driver version.
+    DAWN_SUPPRESS_TEST_IF(format == wgpu::TextureFormat::Depth24UnormStencil8 && IsD3D12() &&
+                          IsNvidia());
+
+    DoSamplingExtraStencilComponentsRenderTest(TestAspect::Stencil, format,
+                                               {uint8_t(42), uint8_t(37)});
+
+    DoSamplingExtraStencilComponentsComputeTest(TestAspect::Stencil, format,
+                                                {uint8_t(42), uint8_t(37)});
 }
 
 // Test sampling both depth and stencil with a render/compute pipeline works.
 TEST_P(DepthStencilSamplingTest, SampleDepthAndStencilRender) {
     // TODO(crbug.com/dawn/593): This test requires glTextureView, which is unsupported on GLES.
     DAWN_TEST_UNSUPPORTED_IF(IsOpenGLES());
+
+    wgpu::TextureFormat format = GetParam().mTextureFormat;
+
     wgpu::SamplerDescriptor samplerDesc;
     wgpu::Sampler sampler = device.CreateSampler(&samplerDesc);
 
-    wgpu::Texture inputTexture = CreateInputTexture(wgpu::TextureFormat::Depth24PlusStencil8);
+    wgpu::Texture inputTexture = CreateInputTexture(format);
 
     wgpu::TextureViewDescriptor depthViewDesc = {};
     depthViewDesc.aspect = wgpu::TextureAspect::DepthOnly;
@@ -640,6 +634,8 @@
     wgpu::TextureViewDescriptor stencilViewDesc = {};
     stencilViewDesc.aspect = wgpu::TextureAspect::StencilOnly;
 
+    float tolerance = format == wgpu::TextureFormat::Depth24UnormStencil8 ? 0.001f : 0.0f;
+
     // With render pipeline
     {
         wgpu::RenderPipeline pipeline =
@@ -682,15 +678,16 @@
         wgpu::CommandBuffer commands = commandEncoder.Finish();
         queue.Submit(1, &commands);
 
-        uint32_t expectedValueU32 = 0;
-        memcpy(&expectedValueU32, &passDescriptor.cDepthStencilAttachmentInfo.clearDepth,
+        float expectedDepth = 0.0f;
+        memcpy(&expectedDepth, &passDescriptor.cDepthStencilAttachmentInfo.clearDepth,
                sizeof(float));
-        EXPECT_BUFFER_U32_EQ(expectedValueU32, depthOutput, 0);
+        EXPECT_BUFFER(depthOutput, 0, sizeof(float),
+                      new ::detail::ExpectEq<float>(expectedDepth, tolerance));
 
-        expectedValueU32 = 0;
-        memcpy(&expectedValueU32, &passDescriptor.cDepthStencilAttachmentInfo.clearStencil,
+        uint8_t expectedStencil = 0;
+        memcpy(&expectedStencil, &passDescriptor.cDepthStencilAttachmentInfo.clearStencil,
                sizeof(uint8_t));
-        EXPECT_BUFFER_U32_EQ(expectedValueU32, stencilOutput, 0);
+        EXPECT_BUFFER_U32_EQ(expectedStencil, stencilOutput, 0);
     }
 
     // With compute pipeline
@@ -729,44 +726,89 @@
         wgpu::CommandBuffer commands = commandEncoder.Finish();
         queue.Submit(1, &commands);
 
-        uint32_t expectedValueU32 = 0;
-        memcpy(&expectedValueU32, &passDescriptor.cDepthStencilAttachmentInfo.clearDepth,
+        float expectedDepth = 0.0f;
+        memcpy(&expectedDepth, &passDescriptor.cDepthStencilAttachmentInfo.clearDepth,
                sizeof(float));
-        EXPECT_BUFFER_U32_EQ(expectedValueU32, depthOutput, 0);
+        EXPECT_BUFFER(depthOutput, 0, sizeof(float),
+                      new ::detail::ExpectEq<float>(expectedDepth, tolerance));
 
-        expectedValueU32 = 0;
-        memcpy(&expectedValueU32, &passDescriptor.cDepthStencilAttachmentInfo.clearStencil,
+        uint8_t expectedStencil = 0;
+        memcpy(&expectedStencil, &passDescriptor.cDepthStencilAttachmentInfo.clearStencil,
                sizeof(uint8_t));
-        EXPECT_BUFFER_U32_EQ(expectedValueU32, stencilOutput, 0);
+        EXPECT_BUFFER_U32_EQ(expectedStencil, stencilOutput, 0);
     }
 }
 
+class DepthSamplingTest : public DepthStencilSamplingTest {};
+
+// Test that sampling a depth texture with a render/compute pipeline works
+TEST_P(DepthSamplingTest, SampleDepthOnly) {
+    wgpu::TextureFormat format = GetParam().mTextureFormat;
+    float tolerance = format == wgpu::TextureFormat::Depth16Unorm ||
+                              format == wgpu::TextureFormat::Depth24UnormStencil8
+                          ? 0.001f
+                          : 0.0f;
+
+    // Test 0, between [0, 1], and 1.
+    DoSamplingTest(TestAspect::Depth, CreateSamplingRenderPipeline({TestAspect::Depth}, 0), format,
+                   kNormalizedTextureValues, tolerance);
+
+    DoSamplingTest(TestAspect::Depth, CreateSamplingComputePipeline({TestAspect::Depth}, 0), format,
+                   kNormalizedTextureValues, tolerance);
+}
+
 // Test that sampling in a render pipeline with all of the compare functions works.
-TEST_P(DepthStencilSamplingTest, CompareFunctionsRender) {
+TEST_P(DepthSamplingTest, CompareFunctionsRender) {
     // Initialization via renderPass loadOp doesn't work on Mac Intel.
     DAWN_SUPPRESS_TEST_IF(IsMetal() && IsIntel());
 
+    wgpu::TextureFormat format = GetParam().mTextureFormat;
+    // Test does not account for precision issues when comparison testing Depth16Unorm and
+    // Depth24UnormStencil8.
+    DAWN_TEST_UNSUPPORTED_IF(format == wgpu::TextureFormat::Depth16Unorm ||
+                             format == wgpu::TextureFormat::Depth24UnormStencil8);
+
     wgpu::RenderPipeline pipeline = CreateComparisonRenderPipeline();
 
-    for (wgpu::TextureFormat format : kDepthFormats) {
-        // Test does not account for precision issues when comparison testing Depth16Unorm.
-        if (format == wgpu::TextureFormat::Depth16Unorm) {
-            continue;
-        }
-
-        // Test a "normal" ref value between 0 and 1; as well as negative and > 1 refs.
-        for (float compareRef : kCompareRefs) {
-            // Test 0, below the ref, equal to, above the ref, and 1.
-            for (wgpu::CompareFunction f : kCompareFunctions) {
-                DoDepthCompareRefTest(pipeline, format, compareRef, f, kNormalizedTextureValues);
-            }
+    // Test a "normal" ref value between 0 and 1; as well as negative and > 1 refs.
+    for (float compareRef : kCompareRefs) {
+        // Test 0, below the ref, equal to, above the ref, and 1.
+        for (wgpu::CompareFunction f : kCompareFunctions) {
+            DoDepthCompareRefTest(pipeline, format, compareRef, f, kNormalizedTextureValues);
         }
     }
 }
 
-DAWN_INSTANTIATE_TEST(DepthStencilSamplingTest,
-                      D3D12Backend(),
-                      MetalBackend(),
-                      OpenGLBackend(),
-                      OpenGLESBackend(),
-                      VulkanBackend());
+class StencilSamplingTest : public DepthStencilSamplingTest {};
+
+// Test that sampling a stencil texture with a render/compute pipeline works
+TEST_P(StencilSamplingTest, SampleStencilOnly) {
+    // TODO(crbug.com/dawn/593): This test requires glTextureView, which is unsupported on GLES.
+    DAWN_TEST_UNSUPPORTED_IF(IsOpenGLES());
+
+    wgpu::TextureFormat format = GetParam().mTextureFormat;
+
+    DoSamplingTest(TestAspect::Stencil, CreateSamplingRenderPipeline({TestAspect::Stencil}, 0),
+                   format, kStencilValues);
+
+    DoSamplingTest(TestAspect::Stencil, CreateSamplingComputePipeline({TestAspect::Stencil}, 0),
+                   format, kStencilValues);
+}
+
+DAWN_INSTANTIATE_TEST_P(DepthStencilSamplingTest,
+                        {D3D12Backend(), MetalBackend(), OpenGLBackend(), OpenGLESBackend(),
+                         VulkanBackend()},
+                        std::vector<wgpu::TextureFormat>(utils::kDepthAndStencilFormats.begin(),
+                                                         utils::kDepthAndStencilFormats.end()));
+
+DAWN_INSTANTIATE_TEST_P(DepthSamplingTest,
+                        {D3D12Backend(), MetalBackend(), OpenGLBackend(), OpenGLESBackend(),
+                         VulkanBackend()},
+                        std::vector<wgpu::TextureFormat>(utils::kDepthFormats.begin(),
+                                                         utils::kDepthFormats.end()));
+
+DAWN_INSTANTIATE_TEST_P(StencilSamplingTest,
+                        {D3D12Backend(), MetalBackend(), OpenGLBackend(), OpenGLESBackend(),
+                         VulkanBackend()},
+                        std::vector<wgpu::TextureFormat>(utils::kStencilFormats.begin(),
+                                                         utils::kStencilFormats.end()));
diff --git a/src/tests/unittests/MathTests.cpp b/src/tests/unittests/MathTests.cpp
index 031c716..6517d06 100644
--- a/src/tests/unittests/MathTests.cpp
+++ b/src/tests/unittests/MathTests.cpp
@@ -249,6 +249,19 @@
     ASSERT_TRUE(IsFloat16NaN(0xFFFF));
 }
 
+// Tests for FloatToUnorm
+TEST(Math, FloatToUnorm) {
+    std::vector<float> kTestFloatValues = {0.0f, 0.4f, 0.5f, 1.0f};
+    std::vector<unsigned char> kExpectedCharValues = {0, 102, 127, 255};
+    std::vector<uint8_t> kExpectedUint8Values = {0, 102, 127, 255};
+    std::vector<uint16_t> kExpectedUint16Values = {0, 26214, 32767, 65535};
+    for (size_t i = 0; i < kTestFloatValues.size(); i++) {
+        ASSERT_EQ(FloatToUnorm<unsigned char>(kTestFloatValues[i]), kExpectedCharValues[i]);
+        ASSERT_EQ(FloatToUnorm<uint8_t>(kTestFloatValues[i]), kExpectedUint8Values[i]);
+        ASSERT_EQ(FloatToUnorm<uint16_t>(kTestFloatValues[i]), kExpectedUint16Values[i]);
+    }
+}
+
 // Tests for SRGBToLinear
 TEST(Math, SRGBToLinear) {
     ASSERT_EQ(SRGBToLinear(0.0f), 0.0f);