d3d: Map Depth24PlusStencil8 to DXGI_FORMAT_D24_UNORM_S8_UINT

WebGPU spec says that 24 bit depth can be mapped to a depth 24 unorm
format[1]. When depth/stencil formats were initially implemented, all
platforms defaulted to using D32_FLOAT_S8X24_UINT formats presumably due
to their universal availability, but D3D has supported D24_UNORM_S8_UINT
format since feature level 10.0[2] which covers all clients which we
would like Dawn to support. Still put it behind a toggle since there's
a difference in behavior with respect to clamping of the compare ref
value when sampling from unorm vs float depth components with D3D.

[1] https://www.w3.org/TR/webgpu/#24-bit-depth
[2] https://learn.microsoft.com/en-us/windows/win32/direct3ddxgi/format-support-for-direct3d-feature-level-10-0-hardware#dxgi_format_d24_unorm_s8_uintfcs-45

Bug: 337980338, 341254292
Change-Id: I9a64de8afaffe820f24c61bf925445d3e23bc22e
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/188640
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Sunny Sachanandani <sunnyps@chromium.org>
Auto-Submit: Sunny Sachanandani <sunnyps@chromium.org>
diff --git a/src/dawn/native/Toggles.cpp b/src/dawn/native/Toggles.cpp
index ae42d83..2b373e7 100644
--- a/src/dawn/native/Toggles.cpp
+++ b/src/dawn/native/Toggles.cpp
@@ -542,6 +542,16 @@
       "Only use shader model 6.5 or less for D3D12 backend, to workaround issues on some Intel "
       "devices.",
       "https://crbug.com/dawn/2470", ToggleStage::Adapter}},
+    {Toggle::UsePackedDepth24UnormStencil8Format,
+     {"use_packed_depth24_unorm_stencil8_format",
+      "Use a packed depth24_unorm_stencil8 format like DXGI_FORMAT_D24_UNORM_STENCIL8_UINT on D3D "
+      "for wgpu::TextureFormat::Depth24PlusStencil8.",
+      "https://crbug.com/341254292", ToggleStage::Device}},
+    {Toggle::D3D12ForceStencilComponentReplicateSwizzle,
+     {"d3d12_force_stencil_component_replicate_swizzle",
+      "Force a replicate swizzle for the stencil component i.e. (ssss) instead of (s001) to "
+      "workaround issues on certain Nvidia drivers on D3D12 with depth24_unorm_stencil8 format.",
+      "https://crbug.com/341254292", ToggleStage::Device}},
     {Toggle::NoWorkaroundSampleMaskBecomesZeroForAllButLastColorTarget,
      {"no_workaround_sample_mask_becomes_zero_for_all_but_last_color_target",
       "MacOS 12.0+ Intel has a bug where the sample mask is only applied for the last color "
diff --git a/src/dawn/native/Toggles.h b/src/dawn/native/Toggles.h
index 2c1909d..b5f35e6 100644
--- a/src/dawn/native/Toggles.h
+++ b/src/dawn/native/Toggles.h
@@ -132,6 +132,8 @@
     EnableImmediateErrorHandling,
     VulkanUseStorageInputOutput16,
     D3D12DontUseShaderModel66OrHigher,
+    UsePackedDepth24UnormStencil8Format,
+    D3D12ForceStencilComponentReplicateSwizzle,
 
     // Unresolved issues.
     NoWorkaroundSampleMaskBecomesZeroForAllButLastColorTarget,
diff --git a/src/dawn/native/d3d/SwapChainD3D.cpp b/src/dawn/native/d3d/SwapChainD3D.cpp
index 83ccc28..43a575c 100644
--- a/src/dawn/native/d3d/SwapChainD3D.cpp
+++ b/src/dawn/native/d3d/SwapChainD3D.cpp
@@ -109,7 +109,7 @@
 
     // Precompute the configuration parameters we want for the DXGI swapchain.
     mConfig.bufferCount = PresentModeToBufferCount(GetPresentMode());
-    mConfig.format = d3d::DXGITextureFormat(GetFormat());
+    mConfig.format = d3d::DXGITextureFormat(GetDevice(), GetFormat());
     mConfig.swapChainFlags = PresentModeToSwapChainFlags(GetPresentMode());
     mConfig.usage = ToDXGIUsage(GetUsage());
 
diff --git a/src/dawn/native/d3d/UtilsD3D.cpp b/src/dawn/native/d3d/UtilsD3D.cpp
index 9d0123a..2575637 100644
--- a/src/dawn/native/d3d/UtilsD3D.cpp
+++ b/src/dawn/native/d3d/UtilsD3D.cpp
@@ -29,6 +29,8 @@
 
 #include <utility>
 
+#include "dawn/native/Device.h"
+
 namespace dawn::native::d3d {
 
 ResultOrError<std::wstring> ConvertStringToWstring(std::string_view s) {
@@ -97,7 +99,7 @@
     return (majorVersion << 32) + minorVersion;
 }
 
-DXGI_FORMAT DXGITypelessTextureFormat(wgpu::TextureFormat format) {
+DXGI_FORMAT DXGITypelessTextureFormat(const DeviceBase* device, wgpu::TextureFormat format) {
     switch (format) {
         case wgpu::TextureFormat::R8Unorm:
         case wgpu::TextureFormat::R8Snorm:
@@ -176,7 +178,12 @@
         // for which the typeless equivalent is DXGI_FORMAT_R24G8_TYPELESS.
         case wgpu::TextureFormat::Stencil8:
             return DXGI_FORMAT_R24G8_TYPELESS;
+
         case wgpu::TextureFormat::Depth24PlusStencil8:
+            return device->IsToggleEnabled(Toggle::UsePackedDepth24UnormStencil8Format)
+                       ? DXGI_FORMAT_R24G8_TYPELESS
+                       : DXGI_FORMAT_R32G8X24_TYPELESS;
+
         case wgpu::TextureFormat::Depth32FloatStencil8:
             return DXGI_FORMAT_R32G8X24_TYPELESS;
 
@@ -313,7 +320,7 @@
     X(wgpu::TextureFormat::R8BG8Biplanar420Unorm, DXGI_FORMAT_NV12)         \
     X(wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm, DXGI_FORMAT_P010)
 
-DXGI_FORMAT DXGITextureFormat(wgpu::TextureFormat format) {
+DXGI_FORMAT DXGITextureFormat(const DeviceBase* device, wgpu::TextureFormat format) {
     switch (format) {
 #define X(wgpuFormat, dxgiFormat) \
     case wgpuFormat:              \
@@ -330,6 +337,9 @@
         case wgpu::TextureFormat::Stencil8:
             return DXGI_FORMAT_D24_UNORM_S8_UINT;
         case wgpu::TextureFormat::Depth24PlusStencil8:
+            return device->IsToggleEnabled(Toggle::UsePackedDepth24UnormStencil8Format)
+                       ? DXGI_FORMAT_D24_UNORM_S8_UINT
+                       : DXGI_FORMAT_D32_FLOAT_S8X24_UINT;
         case wgpu::TextureFormat::Depth32FloatStencil8:
             return DXGI_FORMAT_D32_FLOAT_S8X24_UINT;
 
@@ -497,4 +507,61 @@
     }
 }
 
+DXGI_FORMAT D3DShaderResourceViewFormat(const DeviceBase* device,
+                                        const Format& textureFormat,
+                                        const Format& viewFormat,
+                                        Aspect aspects) {
+    DAWN_ASSERT(aspects != Aspect::None);
+    if (!HasZeroOrOneBits(aspects)) {
+        // A single aspect is not selected. The texture view must not be sampled.
+        return DXGI_FORMAT_UNKNOWN;
+    }
+    // Note that this will configure the SRV descriptor to reinterpret the texture allocated as
+    // TYPELESS as a single-plane shader-accessible view.
+    DXGI_FORMAT srvFormat = DXGITextureFormat(device, viewFormat.format);
+    if (textureFormat.HasDepthOrStencil()) {
+        // Depth-stencil formats must be mapped to compatible shader-accessible view format.
+        switch (DXGITextureFormat(device, textureFormat.format)) {
+            case DXGI_FORMAT_D32_FLOAT:
+                srvFormat = DXGI_FORMAT_R32_FLOAT;
+                break;
+            case DXGI_FORMAT_D16_UNORM:
+                srvFormat = DXGI_FORMAT_R16_UNORM;
+                break;
+            case DXGI_FORMAT_D24_UNORM_S8_UINT: {
+                switch (aspects) {
+                    case Aspect::Depth:
+                        srvFormat = DXGI_FORMAT_R24_UNORM_X8_TYPELESS;
+                        break;
+                    case Aspect::Stencil:
+                        srvFormat = DXGI_FORMAT_X24_TYPELESS_G8_UINT;
+                        break;
+                    default:
+                        DAWN_UNREACHABLE();
+                        break;
+                }
+                break;
+            }
+            case DXGI_FORMAT_D32_FLOAT_S8X24_UINT: {
+                switch (aspects) {
+                    case Aspect::Depth:
+                        srvFormat = DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS;
+                        break;
+                    case Aspect::Stencil:
+                        srvFormat = DXGI_FORMAT_X32_TYPELESS_G8X24_UINT;
+                        break;
+                    default:
+                        DAWN_UNREACHABLE();
+                        break;
+                }
+                break;
+            }
+            default:
+                DAWN_UNREACHABLE();
+                break;
+        }
+    }
+    return srvFormat;
+}
+
 }  // namespace dawn::native::d3d
diff --git a/src/dawn/native/d3d/UtilsD3D.h b/src/dawn/native/d3d/UtilsD3D.h
index e78ed7b..3980640 100644
--- a/src/dawn/native/d3d/UtilsD3D.h
+++ b/src/dawn/native/d3d/UtilsD3D.h
@@ -43,13 +43,20 @@
 
 uint64_t MakeDXCVersion(uint64_t majorVersion, uint64_t minorVersion);
 
-DXGI_FORMAT DXGITypelessTextureFormat(wgpu::TextureFormat format);
+DXGI_FORMAT DXGITypelessTextureFormat(const DeviceBase* device, wgpu::TextureFormat format);
+DXGI_FORMAT DXGITextureFormat(const DeviceBase* device, wgpu::TextureFormat format);
 
-DXGI_FORMAT DXGITextureFormat(wgpu::TextureFormat format);
 ResultOrError<wgpu::TextureFormat> FromUncompressedColorDXGITextureFormat(DXGI_FORMAT format);
 
 DXGI_FORMAT DXGIVertexFormat(wgpu::VertexFormat format);
 
+// Returns the DXGI format to use as the SRV format for a texture view. Returns
+// DXGI_FORMAT_UNKNOWN if a single aspect is not selected.
+DXGI_FORMAT D3DShaderResourceViewFormat(const DeviceBase* device,
+                                        const Format& textureFormat,
+                                        const Format& viewFormat,
+                                        Aspect aspects);
+
 }  // namespace dawn::native::d3d
 
 #endif  // SRC_DAWN_NATIVE_D3D_UTILSD3D_H_
diff --git a/src/dawn/native/d3d11/CommandBufferD3D11.cpp b/src/dawn/native/d3d11/CommandBufferD3D11.cpp
index 392f314..f5710d0 100644
--- a/src/dawn/native/d3d11/CommandBufferD3D11.cpp
+++ b/src/dawn/native/d3d11/CommandBufferD3D11.cpp
@@ -807,7 +807,8 @@
                     d3d11DeviceContext->ResolveSubresource(
                         resolveTexture->GetD3D11Resource(), dstSubresource,
                         colorTexture->GetD3D11Resource(), srcSubresource,
-                        d3d::DXGITextureFormat(attachment.resolveTarget->GetFormat().format));
+                        d3d::DXGITextureFormat(GetDevice(),
+                                               attachment.resolveTarget->GetFormat().format));
                 }
 
                 return {};
diff --git a/src/dawn/native/d3d11/TextureD3D11.cpp b/src/dawn/native/d3d11/TextureD3D11.cpp
index 45cab9e..f31c305 100644
--- a/src/dawn/native/d3d11/TextureD3D11.cpp
+++ b/src/dawn/native/d3d11/TextureD3D11.cpp
@@ -236,9 +236,11 @@
     // We need to use the typeless format if it's a staging texture for writting to depth-stencil
     // textures.
     needsTypelessFormat |=
-        d3d::IsDepthStencil(d3d::DXGITextureFormat(GetFormat().format)) && mKind == Kind::Staging;
-    desc.Format = needsTypelessFormat ? d3d::DXGITypelessTextureFormat(GetFormat().format)
-                                      : d3d::DXGITextureFormat(GetFormat().format);
+        d3d::IsDepthStencil(d3d::DXGITextureFormat(GetDevice(), GetFormat().format)) &&
+        mKind == Kind::Staging;
+    desc.Format = needsTypelessFormat
+                      ? d3d::DXGITypelessTextureFormat(GetDevice(), GetFormat().format)
+                      : d3d::DXGITextureFormat(GetDevice(), GetFormat().format);
     desc.Usage = mKind == Kind::Staging ? D3D11_USAGE_STAGING : D3D11_USAGE_DEFAULT;
     desc.BindFlags = D3D11TextureBindFlags(GetInternalUsage(), GetFormat());
     constexpr UINT kCPUReadWriteFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
@@ -348,7 +350,7 @@
     uint32_t sliceCount,
     uint32_t planeSlice) const {
     D3D11_RENDER_TARGET_VIEW_DESC1 rtvDesc;
-    rtvDesc.Format = d3d::DXGITextureFormat(format);
+    rtvDesc.Format = d3d::DXGITextureFormat(GetDevice(), format);
     if (IsMultisampledTexture()) {
         DAWN_ASSERT(GetDimension() == wgpu::TextureDimension::e2D);
         DAWN_ASSERT(GetNumMipLevels() == 1);
@@ -402,7 +404,7 @@
     bool stencilReadOnly) const {
     D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc;
     DAWN_ASSERT(singleLevelRange.levelCount == 1);
-    dsvDesc.Format = d3d::DXGITextureFormat(GetFormat().format);
+    dsvDesc.Format = d3d::DXGITextureFormat(GetDevice(), GetFormat().format);
     dsvDesc.Flags = 0;
     if (depthReadOnly && singleLevelRange.aspects & Aspect::Depth) {
         dsvDesc.Flags |= D3D11_DSV_READ_ONLY_DEPTH;
@@ -716,7 +718,7 @@
     DAWN_ASSERT(size.width != 0 && size.height != 0 && size.depthOrArrayLayers != 0);
     DAWN_ASSERT(subresources.levelCount == 1);
 
-    if (d3d::IsDepthStencil(d3d::DXGITextureFormat(GetFormat().format))) {
+    if (d3d::IsDepthStencil(d3d::DXGITextureFormat(GetDevice(), GetFormat().format))) {
         DAWN_TRY(WriteDepthStencilInternal(commandContext, subresources, origin, size, data,
                                            bytesPerRow, rowsPerImage));
         return {};
@@ -807,8 +809,8 @@
         DAWN_TRY(Texture::CopyInternal(commandContext, &copyCmd));
     }
 
-    const auto aspectLayout =
-        DepthStencilAspectLayout(d3d::DXGITextureFormat(GetFormat().format), subresources.aspects);
+    const auto aspectLayout = DepthStencilAspectLayout(
+        d3d::DXGITextureFormat(GetDevice(), GetFormat().format), subresources.aspects);
 
     // Map and write to the staging texture.
     D3D11_MAPPED_SUBRESOURCE mappedResource;
@@ -891,7 +893,7 @@
                 // We need to read texel by texel for depth-stencil formats.
                 std::vector<uint8_t> depthOrStencilData(size.width * blockInfo.byteSize);
                 const auto aspectLayout = DepthStencilAspectLayout(
-                    d3d::DXGITextureFormat(GetFormat().format), subresources.aspects);
+                    d3d::DXGITextureFormat(GetDevice(), GetFormat().format), subresources.aspects);
                 DAWN_ASSERT(blockInfo.byteSize == aspectLayout.componentSize);
                 for (uint32_t y = 0; y < rowsPerImage; ++y) {
                     // Filter the depth/stencil data out.
@@ -1189,71 +1191,8 @@
 
     Device* device = ToBackend(GetDevice());
     D3D11_SHADER_RESOURCE_VIEW_DESC1 srvDesc;
-    srvDesc.Format = d3d::DXGITextureFormat(GetFormat().format);
-
-    const Format& textureFormat = GetTexture()->GetFormat();
-    // TODO(dawn:1705): share below code with D3D12?
-    if (textureFormat.HasDepthOrStencil()) {
-        // Configure the SRV descriptor to reinterpret the texture allocated as
-        // TYPELESS as a single-plane shader-accessible view.
-        switch (textureFormat.format) {
-            case wgpu::TextureFormat::Depth32Float:
-            case wgpu::TextureFormat::Depth24Plus:
-                srvDesc.Format = DXGI_FORMAT_R32_FLOAT;
-                break;
-            case wgpu::TextureFormat::Depth16Unorm:
-                srvDesc.Format = DXGI_FORMAT_R16_UNORM;
-                break;
-            case wgpu::TextureFormat::Stencil8: {
-                Aspect aspects = GetAspects();
-                DAWN_ASSERT(aspects != Aspect::None);
-                if (!HasZeroOrOneBits(aspects)) {
-                    // A single aspect is not selected. The texture view must not be
-                    // sampled.
-                    srvDesc.Format = DXGI_FORMAT_UNKNOWN;
-                    break;
-                }
-                switch (aspects) {
-                    case Aspect::Depth:
-                        srvDesc.Format = DXGI_FORMAT_R24_UNORM_X8_TYPELESS;
-                        break;
-                    case Aspect::Stencil:
-                        srvDesc.Format = DXGI_FORMAT_X24_TYPELESS_G8_UINT;
-                        break;
-                    default:
-                        DAWN_UNREACHABLE();
-                        break;
-                }
-                break;
-            }
-            case wgpu::TextureFormat::Depth24PlusStencil8:
-            case wgpu::TextureFormat::Depth32FloatStencil8: {
-                Aspect aspects = GetAspects();
-                DAWN_ASSERT(aspects != Aspect::None);
-                if (!HasZeroOrOneBits(aspects)) {
-                    // A single aspect is not selected. The texture view must not be
-                    // sampled.
-                    srvDesc.Format = DXGI_FORMAT_UNKNOWN;
-                    break;
-                }
-                switch (aspects) {
-                    case Aspect::Depth:
-                        srvDesc.Format = DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS;
-                        break;
-                    case Aspect::Stencil:
-                        srvDesc.Format = DXGI_FORMAT_X32_TYPELESS_G8X24_UINT;
-                        break;
-                    default:
-                        DAWN_UNREACHABLE();
-                        break;
-                }
-                break;
-            }
-            default:
-                DAWN_UNREACHABLE();
-                break;
-        }
-    }
+    srvDesc.Format = d3d::D3DShaderResourceViewFormat(GetDevice(), GetTexture()->GetFormat(),
+                                                      GetFormat(), GetAspects());
 
     // Currently we always use D3D11_TEX2D_ARRAY_SRV because we cannot specify base array
     // layer and layer count in D3D11_TEX2D_SRV. For 2D texture views, we treat them as
@@ -1371,7 +1310,7 @@
     }
 
     D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc;
-    uavDesc.Format = d3d::DXGITextureFormat(GetFormat().format);
+    uavDesc.Format = d3d::DXGITextureFormat(GetDevice(), GetFormat().format);
 
     DAWN_ASSERT(!GetTexture()->IsMultisampledTexture());
     switch (GetDimension()) {
diff --git a/src/dawn/native/d3d12/PhysicalDeviceD3D12.cpp b/src/dawn/native/d3d12/PhysicalDeviceD3D12.cpp
index 3fe92f1..95d0499 100644
--- a/src/dawn/native/d3d12/PhysicalDeviceD3D12.cpp
+++ b/src/dawn/native/d3d12/PhysicalDeviceD3D12.cpp
@@ -768,6 +768,10 @@
             deviceToggles->Default(Toggle::DisableResourceSuballocation, true);
         }
     }
+
+    if (gpu_info::IsNvidia(vendorId)) {
+        deviceToggles->Default(Toggle::D3D12ForceStencilComponentReplicateSwizzle, true);
+    }
 }
 
 ResultOrError<Ref<DeviceBase>> PhysicalDevice::CreateDeviceImpl(
diff --git a/src/dawn/native/d3d12/RenderPipelineD3D12.cpp b/src/dawn/native/d3d12/RenderPipelineD3D12.cpp
index 9c71063..0784d27 100644
--- a/src/dawn/native/d3d12/RenderPipelineD3D12.cpp
+++ b/src/dawn/native/d3d12/RenderPipelineD3D12.cpp
@@ -401,7 +401,7 @@
     descriptorD3D12.RasterizerState.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
 
     if (HasDepthStencilAttachment()) {
-        descriptorD3D12.DSVFormat = d3d::DXGITextureFormat(GetDepthStencilFormat());
+        descriptorD3D12.DSVFormat = d3d::DXGITextureFormat(device, GetDepthStencilFormat());
     }
 
     static_assert(kMaxColorAttachments == 8);
@@ -412,7 +412,7 @@
     auto highestColorAttachmentIndexPlusOne = GetHighestBitIndexPlusOne(GetColorAttachmentsMask());
     for (auto i : IterateBitSet(GetColorAttachmentsMask())) {
         descriptorD3D12.RTVFormats[static_cast<uint8_t>(i)] =
-            d3d::DXGITextureFormat(GetColorAttachmentFormat(i));
+            d3d::DXGITextureFormat(device, GetColorAttachmentFormat(i));
         descriptorD3D12.BlendState.RenderTarget[static_cast<uint8_t>(i)] =
             ComputeColorDesc(device, GetColorTargetState(i));
     }
diff --git a/src/dawn/native/d3d12/TextureD3D12.cpp b/src/dawn/native/d3d12/TextureD3D12.cpp
index 17b386e..d8635b5 100644
--- a/src/dawn/native/d3d12/TextureD3D12.cpp
+++ b/src/dawn/native/d3d12/TextureD3D12.cpp
@@ -103,16 +103,18 @@
 D3D12_RESOURCE_FLAGS D3D12ResourceFlags(wgpu::TextureUsage usage, const Format& format) {
     D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE;
 
-    if (usage & wgpu::TextureUsage::StorageBinding) {
-        flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
-    }
-
-    if (usage & wgpu::TextureUsage::RenderAttachment) {
-        if (format.HasDepthOrStencil()) {
-            flags |= D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
-        } else {
+    // Always set the depth stencil flags on depth stencil format textures since some formats like
+    // D24_UNORM_S8_UINT don't support copying for lazy clear. Also, setting depth stencil flags
+    // precludes setting other resource flags like render target or unordered access.
+    if (format.HasDepthOrStencil()) {
+        flags |= D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
+    } else {
+        if (usage & wgpu::TextureUsage::RenderAttachment) {
             flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
         }
+        if (usage & wgpu::TextureUsage::StorageBinding) {
+            flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
+        }
     }
 
     DAWN_ASSERT(!(flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) ||
@@ -246,8 +248,8 @@
          (GetInternalUsage() & wgpu::TextureUsage::TextureBinding) != 0);
 
     DXGI_FORMAT dxgiFormat = needsTypelessFormat
-                                 ? d3d::DXGITypelessTextureFormat(GetFormat().format)
-                                 : d3d::DXGITextureFormat(GetFormat().format);
+                                 ? d3d::DXGITypelessTextureFormat(device, GetFormat().format)
+                                 : d3d::DXGITextureFormat(device, GetFormat().format);
 
     resourceDescriptor.MipLevels = static_cast<UINT16>(GetNumMipLevels());
     resourceDescriptor.Format = dxgiFormat;
@@ -343,7 +345,7 @@
 }
 
 DXGI_FORMAT Texture::GetD3D12Format() const {
-    return d3d::DXGITextureFormat(GetFormat().format);
+    return d3d::DXGITextureFormat(GetDevice(), GetFormat().format);
 }
 
 ID3D12Resource* Texture::GetD3D12Resource() const {
@@ -357,12 +359,17 @@
 DXGI_FORMAT Texture::GetD3D12CopyableSubresourceFormat(Aspect aspect) const {
     DAWN_ASSERT(GetFormat().aspects & aspect);
 
-    switch (GetFormat().format) {
+    wgpu::TextureFormat format = GetFormat().format;
+    switch (format) {
         case wgpu::TextureFormat::Depth24PlusStencil8:
         case wgpu::TextureFormat::Depth32FloatStencil8:
         case wgpu::TextureFormat::Stencil8:
             switch (aspect) {
                 case Aspect::Depth:
+                    // The depth24 part of a D24_UNORM_S8_UINT texture cannot be copied with D3D and
+                    // is also not supported by WebGPU.
+                    // See https://gpuweb.github.io/gpuweb/#depth-formats
+                    DAWN_ASSERT(format == wgpu::TextureFormat::Depth32FloatStencil8);
                     return DXGI_FORMAT_R32_FLOAT;
                 case Aspect::Stencil:
                     return DXGI_FORMAT_R8_UINT;
@@ -645,7 +652,7 @@
                                                         uint32_t sliceCount,
                                                         uint32_t planeSlice) const {
     D3D12_RENDER_TARGET_VIEW_DESC rtvDesc;
-    rtvDesc.Format = d3d::DXGITextureFormat(format.format);
+    rtvDesc.Format = d3d::DXGITextureFormat(GetDevice(), format.format);
     if (IsMultisampledTexture()) {
         DAWN_ASSERT(GetDimension() == wgpu::TextureDimension::e2D);
         DAWN_ASSERT(GetNumMipLevels() == 1);
@@ -905,167 +912,104 @@
 
 TextureView::TextureView(TextureBase* texture, const UnpackedPtr<TextureViewDescriptor>& descriptor)
     : TextureViewBase(texture, descriptor) {
-    mSrvDesc.Format = d3d::DXGITextureFormat(descriptor->format);
-    mSrvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
-
-    UINT planeSlice = 0;
+    const Aspect aspects = GetAspects();
     const Format& textureFormat = texture->GetFormat();
-    if (textureFormat.HasDepthOrStencil()) {
-        // Configure the SRV descriptor to reinterpret the texture allocated as
-        // TYPELESS as a single-plane shader-accessible view.
-        switch (textureFormat.format) {
-            case wgpu::TextureFormat::Depth32Float:
-            case wgpu::TextureFormat::Depth24Plus:
-                mSrvDesc.Format = DXGI_FORMAT_R32_FLOAT;
-                break;
-            case wgpu::TextureFormat::Depth16Unorm:
-                mSrvDesc.Format = DXGI_FORMAT_R16_UNORM;
-                break;
-            case wgpu::TextureFormat::Stencil8: {
-                Aspect aspects = SelectFormatAspects(textureFormat, descriptor->aspect);
-                DAWN_ASSERT(aspects != Aspect::None);
-                if (!HasZeroOrOneBits(aspects)) {
-                    // A single aspect is not selected. The texture view must not be
-                    // sampled.
-                    mSrvDesc.Format = DXGI_FORMAT_UNKNOWN;
-                    break;
-                }
-                switch (aspects) {
-                    case Aspect::Depth:
-                        planeSlice = 0;
-                        mSrvDesc.Format = DXGI_FORMAT_R24_UNORM_X8_TYPELESS;
-                        break;
-                    case Aspect::Stencil:
-                        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;
-                    default:
-                        DAWN_UNREACHABLE();
-                        break;
-                }
-                break;
+
+    mSrvDesc.Format =
+        d3d::D3DShaderResourceViewFormat(GetDevice(), textureFormat, GetFormat(), aspects);
+    if (mSrvDesc.Format != DXGI_FORMAT_UNKNOWN) {
+        mSrvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
+
+        // Stencil is accessed using the .g component in the shader. Map it to the zeroth component
+        // to match other APIs.
+        DXGI_FORMAT textureDxgiFormat = d3d::DXGITextureFormat(GetDevice(), textureFormat.format);
+        if (d3d::IsDepthStencil(textureDxgiFormat) && aspects == Aspect::Stencil) {
+            if (GetDevice()->IsToggleEnabled(Toggle::D3D12ForceStencilComponentReplicateSwizzle) &&
+                textureDxgiFormat == DXGI_FORMAT_D24_UNORM_S8_UINT) {
+                // Swizzle (ssss)
+                mSrvDesc.Shader4ComponentMapping = D3D12_ENCODE_SHADER_4_COMPONENT_MAPPING(
+                    D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_1,
+                    D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_1,
+                    D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_1,
+                    D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_1);
+            } else {
+                // Swizzle (s001)
+                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);
             }
-            case wgpu::TextureFormat::Depth24PlusStencil8:
-            case wgpu::TextureFormat::Depth32FloatStencil8: {
-                Aspect aspects = SelectFormatAspects(textureFormat, descriptor->aspect);
-                DAWN_ASSERT(aspects != Aspect::None);
-                if (!HasZeroOrOneBits(aspects)) {
-                    // A single aspect is not selected. The texture view must not be
-                    // sampled.
-                    mSrvDesc.Format = DXGI_FORMAT_UNKNOWN;
+        }
+
+        // Currently we always use D3D12_TEX2D_ARRAY_SRV because we cannot specify base array layer
+        // and layer count in D3D12_TEX2D_SRV. For 2D texture views, we treat them as 1-layer 2D
+        // array textures.
+        // Multisampled textures may only be one array layer, so we use
+        // D3D12_SRV_DIMENSION_TEXTURE2DMS.
+        // https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_tex2d_srv
+        // https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_tex2d_array_srv
+        if (GetTexture()->IsMultisampledTexture()) {
+            switch (descriptor->dimension) {
+                case wgpu::TextureViewDimension::e2DArray:
+                    DAWN_ASSERT(texture->GetArrayLayers() == 1);
+                    [[fallthrough]];
+                case wgpu::TextureViewDimension::e2D:
+                    DAWN_ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D);
+                    mSrvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DMS;
                     break;
-                }
-                switch (aspects) {
-                    case Aspect::Depth:
-                        planeSlice = 0;
-                        mSrvDesc.Format = DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS;
-                        break;
-                    case Aspect::Stencil:
-                        planeSlice = 1;
-                        mSrvDesc.Format = DXGI_FORMAT_X32_TYPELESS_G8X24_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;
-                    default:
-                        DAWN_UNREACHABLE();
-                        break;
-                }
-                break;
+
+                default:
+                    DAWN_UNREACHABLE();
             }
-            default:
-                DAWN_UNREACHABLE();
-                break;
-        }
-    }
+        } else {
+            switch (descriptor->dimension) {
+                case wgpu::TextureViewDimension::e1D:
+                    mSrvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE1D;
+                    mSrvDesc.Texture1D.MipLevels = descriptor->mipLevelCount;
+                    mSrvDesc.Texture1D.MostDetailedMip = descriptor->baseMipLevel;
+                    mSrvDesc.Texture1D.ResourceMinLODClamp = 0;
+                    break;
 
-    // Per plane view formats must have the plane slice number be the index of the plane in the
-    // array of textures.
-    if (texture->GetFormat().IsMultiPlanar()) {
-        const Aspect planeAspect = ConvertViewAspect(GetFormat(), descriptor->aspect);
-        planeSlice = GetAspectIndex(planeAspect);
-        mSrvDesc.Format =
-            d3d::DXGITextureFormat(texture->GetFormat().GetAspectInfo(planeAspect).format);
-    }
+                case wgpu::TextureViewDimension::e2D:
+                case wgpu::TextureViewDimension::e2DArray:
+                    DAWN_ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D);
+                    mSrvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY;
+                    mSrvDesc.Texture2DArray.ArraySize = descriptor->arrayLayerCount;
+                    mSrvDesc.Texture2DArray.FirstArraySlice = descriptor->baseArrayLayer;
+                    mSrvDesc.Texture2DArray.MipLevels = descriptor->mipLevelCount;
+                    mSrvDesc.Texture2DArray.MostDetailedMip = descriptor->baseMipLevel;
+                    mSrvDesc.Texture2DArray.PlaneSlice = GetAspectIndex(aspects);
+                    mSrvDesc.Texture2DArray.ResourceMinLODClamp = 0;
+                    break;
+                case wgpu::TextureViewDimension::Cube:
+                case wgpu::TextureViewDimension::CubeArray:
+                    DAWN_ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D);
+                    DAWN_ASSERT(descriptor->arrayLayerCount % 6 == 0);
+                    mSrvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBEARRAY;
+                    mSrvDesc.TextureCubeArray.First2DArrayFace = descriptor->baseArrayLayer;
+                    mSrvDesc.TextureCubeArray.NumCubes = descriptor->arrayLayerCount / 6;
+                    mSrvDesc.TextureCubeArray.MostDetailedMip = descriptor->baseMipLevel;
+                    mSrvDesc.TextureCubeArray.MipLevels = descriptor->mipLevelCount;
+                    mSrvDesc.TextureCubeArray.ResourceMinLODClamp = 0;
+                    break;
+                case wgpu::TextureViewDimension::e3D:
+                    DAWN_ASSERT(texture->GetDimension() == wgpu::TextureDimension::e3D);
+                    mSrvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE3D;
+                    mSrvDesc.Texture3D.MostDetailedMip = descriptor->baseMipLevel;
+                    mSrvDesc.Texture3D.MipLevels = descriptor->mipLevelCount;
+                    mSrvDesc.Texture3D.ResourceMinLODClamp = 0;
+                    break;
 
-    // Currently we always use D3D12_TEX2D_ARRAY_SRV because we cannot specify base array layer
-    // and layer count in D3D12_TEX2D_SRV. For 2D texture views, we treat them as 1-layer 2D
-    // array textures.
-    // Multisampled textures may only be one array layer, so we use
-    // D3D12_SRV_DIMENSION_TEXTURE2DMS.
-    // https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_tex2d_srv
-    // https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_tex2d_array_srv
-    if (GetTexture()->IsMultisampledTexture()) {
-        switch (descriptor->dimension) {
-            case wgpu::TextureViewDimension::e2DArray:
-                DAWN_ASSERT(texture->GetArrayLayers() == 1);
-                [[fallthrough]];
-            case wgpu::TextureViewDimension::e2D:
-                DAWN_ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D);
-                mSrvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DMS;
-                break;
-
-            default:
-                DAWN_UNREACHABLE();
-        }
-    } else {
-        switch (descriptor->dimension) {
-            case wgpu::TextureViewDimension::e1D:
-                mSrvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE1D;
-                mSrvDesc.Texture1D.MipLevels = descriptor->mipLevelCount;
-                mSrvDesc.Texture1D.MostDetailedMip = descriptor->baseMipLevel;
-                mSrvDesc.Texture1D.ResourceMinLODClamp = 0;
-                break;
-
-            case wgpu::TextureViewDimension::e2D:
-            case wgpu::TextureViewDimension::e2DArray:
-                DAWN_ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D);
-                mSrvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY;
-                mSrvDesc.Texture2DArray.ArraySize = descriptor->arrayLayerCount;
-                mSrvDesc.Texture2DArray.FirstArraySlice = descriptor->baseArrayLayer;
-                mSrvDesc.Texture2DArray.MipLevels = descriptor->mipLevelCount;
-                mSrvDesc.Texture2DArray.MostDetailedMip = descriptor->baseMipLevel;
-                mSrvDesc.Texture2DArray.PlaneSlice = planeSlice;
-                mSrvDesc.Texture2DArray.ResourceMinLODClamp = 0;
-                break;
-            case wgpu::TextureViewDimension::Cube:
-            case wgpu::TextureViewDimension::CubeArray:
-                DAWN_ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D);
-                DAWN_ASSERT(descriptor->arrayLayerCount % 6 == 0);
-                mSrvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBEARRAY;
-                mSrvDesc.TextureCubeArray.First2DArrayFace = descriptor->baseArrayLayer;
-                mSrvDesc.TextureCubeArray.NumCubes = descriptor->arrayLayerCount / 6;
-                mSrvDesc.TextureCubeArray.MostDetailedMip = descriptor->baseMipLevel;
-                mSrvDesc.TextureCubeArray.MipLevels = descriptor->mipLevelCount;
-                mSrvDesc.TextureCubeArray.ResourceMinLODClamp = 0;
-                break;
-            case wgpu::TextureViewDimension::e3D:
-                DAWN_ASSERT(texture->GetDimension() == wgpu::TextureDimension::e3D);
-                mSrvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE3D;
-                mSrvDesc.Texture3D.MostDetailedMip = descriptor->baseMipLevel;
-                mSrvDesc.Texture3D.MipLevels = descriptor->mipLevelCount;
-                mSrvDesc.Texture3D.ResourceMinLODClamp = 0;
-                break;
-
-            case wgpu::TextureViewDimension::Undefined:
-                DAWN_UNREACHABLE();
+                case wgpu::TextureViewDimension::Undefined:
+                    DAWN_UNREACHABLE();
+            }
         }
     }
 }
 
 DXGI_FORMAT TextureView::GetD3D12Format() const {
-    return d3d::DXGITextureFormat(GetFormat().format);
+    return d3d::DXGITextureFormat(GetDevice(), GetFormat().format);
 }
 
 const D3D12_SHADER_RESOURCE_VIEW_DESC& TextureView::GetSRVDescriptor() const {
diff --git a/src/dawn/tests/end2end/DepthBiasTests.cpp b/src/dawn/tests/end2end/DepthBiasTests.cpp
index e8a73e4..68b4ea3 100644
--- a/src/dawn/tests/end2end/DepthBiasTests.cpp
+++ b/src/dawn/tests/end2end/DepthBiasTests.cpp
@@ -50,6 +50,11 @@
                           int32_t bias,
                           float biasSlopeScale,
                           float biasClamp) {
+        // Skip formats other than Depth24PlusStencil8 if we're specifically testing with the packed
+        // depth24_unorm_stencil8 toggle.
+        DAWN_TEST_UNSUPPORTED_IF(HasToggleEnabled("use_packed_depth24_unorm_stencil8_format") &&
+                                 depthFormat != wgpu::TextureFormat::Depth24PlusStencil8);
+
         const char* vertexSource = nullptr;
         switch (quadAngle) {
             case QuadAngle::Flat:
diff --git a/src/dawn/tests/end2end/DepthStencilCopyTests.cpp b/src/dawn/tests/end2end/DepthStencilCopyTests.cpp
index 86d1080..7eb9960 100644
--- a/src/dawn/tests/end2end/DepthStencilCopyTests.cpp
+++ b/src/dawn/tests/end2end/DepthStencilCopyTests.cpp
@@ -129,6 +129,12 @@
 
         DAWN_TEST_UNSUPPORTED_IF(!mIsFormatSupported);
 
+        // Skip formats other than Depth24PlusStencil8 if we're specifically testing with the packed
+        // depth24_unorm_stencil8 toggle.
+        DAWN_TEST_UNSUPPORTED_IF(HasToggleEnabled("use_packed_depth24_unorm_stencil8_format") &&
+                                 GetParam().mTextureFormat !=
+                                     wgpu::TextureFormat::Depth24PlusStencil8);
+
         // Draw a square in the bottom left quarter of the screen.
         mVertexModule = utils::CreateShaderModule(device, R"(
             @vertex
@@ -330,6 +336,11 @@
 
 // Test copying both aspects in a T2T copy, then copying only stencil.
 TEST_P(DepthStencilCopyTests, T2TBothAspectsThenCopyStencil) {
+    // TODO(crbug.com/344949343): Test fails on Intel D3D11 with packed depth24_unorm_stencil8.
+    DAWN_SUPPRESS_TEST_IF(IsIntel() && IsD3D11() &&
+                          GetParam().mTextureFormat == wgpu::TextureFormat::Depth24PlusStencil8 &&
+                          HasToggleEnabled("use_packed_depth24_unorm_stencil8_format"));
+
     constexpr uint32_t kWidth = 4;
     constexpr uint32_t kHeight = 4;
 
@@ -351,6 +362,11 @@
 // Test that part of a non-renderable stencil aspect can be copied. Notably,
 // this test has different behavior on some platforms than T2TBothAspectsThenCopyStencil.
 TEST_P(DepthStencilCopyTests, T2TBothAspectsThenCopyNonRenderableStencil) {
+    // Test fails on D3D11 with Intel when using packed depth24_unorm_stencil8 format.
+    DAWN_SUPPRESS_TEST_IF(IsIntel() && IsD3D11() &&
+                          GetParam().mTextureFormat == wgpu::TextureFormat::Depth24PlusStencil8 &&
+                          HasToggleEnabled("use_packed_depth24_unorm_stencil8_format"));
+
     constexpr uint32_t kWidth = 4;
     constexpr uint32_t kHeight = 4;
 
@@ -371,6 +387,11 @@
 // Test that part of a non-renderable, non-zero mip stencil aspect can be copied. Notably,
 // this test has different behavior on some platforms than T2TBothAspectsThenCopyStencil.
 TEST_P(DepthStencilCopyTests, T2TBothAspectsThenCopyNonRenderableNonZeroMipStencil) {
+    // Test fails on D3D11 with Intel when using packed depth24_unorm_stencil8 format.
+    DAWN_SUPPRESS_TEST_IF(IsIntel() && IsD3D11() &&
+                          GetParam().mTextureFormat == wgpu::TextureFormat::Depth24PlusStencil8 &&
+                          HasToggleEnabled("use_packed_depth24_unorm_stencil8_format"));
+
     wgpu::Texture texture = CreateInitializeDepthStencilTextureAndCopyT2T(
         0.1f, 0.3f, 1u, 3u, 9, 9, wgpu::TextureUsage::CopySrc, 1);
 
@@ -1561,7 +1582,8 @@
 
 DAWN_INSTANTIATE_TEST_P(
     DepthStencilCopyTests,
-    {D3D11Backend(), D3D12Backend(),
+    {D3D11Backend(), D3D11Backend({"use_packed_depth24_unorm_stencil8_format"}), D3D12Backend(),
+     D3D12Backend({"use_packed_depth24_unorm_stencil8_format"}),
      D3D12Backend({"use_blit_for_depth_texture_to_texture_copy_to_nonzero_subresource"}),
      MetalBackend(),
      MetalBackend({"use_blit_for_depth_texture_to_texture_copy_to_nonzero_subresource"}),
@@ -1597,7 +1619,8 @@
 
 DAWN_INSTANTIATE_TEST_P(
     StencilCopyTests,
-    {D3D11Backend(), D3D12Backend(),
+    {D3D11Backend(), D3D11Backend({"use_packed_depth24_unorm_stencil8_format"}), D3D12Backend(),
+     D3D12Backend({"use_packed_depth24_unorm_stencil8_format"}),
      D3D12Backend({"d3d12_use_temp_buffer_in_depth_stencil_texture_and_buffer_"
                    "copy_with_non_zero_buffer_offset"}),
      MetalBackend(), MetalBackend({"metal_use_combined_depth_stencil_format_for_stencil8"}),
diff --git a/src/dawn/tests/end2end/DepthStencilLoadOpTests.cpp b/src/dawn/tests/end2end/DepthStencilLoadOpTests.cpp
index 29c36b5..8c202e0 100644
--- a/src/dawn/tests/end2end/DepthStencilLoadOpTests.cpp
+++ b/src/dawn/tests/end2end/DepthStencilLoadOpTests.cpp
@@ -84,6 +84,11 @@
         // Also depends on glTextureView which is not supported on ES.
         DAWN_SUPPRESS_TEST_IF(IsOpenGL() || IsOpenGLES());
 
+        // Skip formats other than Depth24PlusStencil8 if we're specifically testing with the packed
+        // depth24_unorm_stencil8 toggle.
+        DAWN_TEST_UNSUPPORTED_IF(HasToggleEnabled("use_packed_depth24_unorm_stencil8_format") &&
+                                 GetParam().mFormat != wgpu::TextureFormat::Depth24PlusStencil8);
+
         wgpu::TextureDescriptor descriptor;
         descriptor.size = {kRTSize, kRTSize};
         descriptor.format = GetParam().mFormat;
@@ -436,8 +441,9 @@
 }
 
 DAWN_INSTANTIATE_TEST_P(DepthTextureClearTwiceTest,
-                        {D3D11Backend(), D3D12Backend(), MetalBackend(), OpenGLBackend(),
-                         OpenGLESBackend(), VulkanBackend()},
+                        {D3D11Backend(), D3D11Backend({"use_packed_depth24_unorm_stencil8_format"}),
+                         D3D12Backend(), D3D12Backend({"use_packed_depth24_unorm_stencil8_format"}),
+                         MetalBackend(), OpenGLBackend(), OpenGLESBackend(), VulkanBackend()},
                         {wgpu::TextureFormat::Depth16Unorm, wgpu::TextureFormat::Depth24Plus,
                          wgpu::TextureFormat::Depth32Float,
                          wgpu::TextureFormat::Depth32FloatStencil8,
diff --git a/src/dawn/tests/end2end/DepthStencilSamplingTests.cpp b/src/dawn/tests/end2end/DepthStencilSamplingTests.cpp
index 8a75ed2..285003f 100644
--- a/src/dawn/tests/end2end/DepthStencilSamplingTests.cpp
+++ b/src/dawn/tests/end2end/DepthStencilSamplingTests.cpp
@@ -28,6 +28,7 @@
 #include <utility>
 #include <vector>
 
+#include "absl/strings/str_format.h"
 #include "dawn/common/Assert.h"
 #include "dawn/tests/DawnTest.h"
 #include "dawn/utils/ComboRenderPipelineDescriptor.h"
@@ -68,6 +69,12 @@
 
         DAWN_TEST_UNSUPPORTED_IF(!mIsFormatSupported);
 
+        // Skip formats other than Depth24PlusStencil8 if we're specifically testing with the packed
+        // depth24_unorm_stencil8 toggle.
+        DAWN_TEST_UNSUPPORTED_IF(HasToggleEnabled("use_packed_depth24_unorm_stencil8_format") &&
+                                 GetParam().mTextureFormat !=
+                                     wgpu::TextureFormat::Depth24PlusStencil8);
+
         wgpu::BufferDescriptor uniformBufferDesc;
         uniformBufferDesc.usage = wgpu::BufferUsage::Uniform | wgpu::BufferUsage::CopyDst;
         uniformBufferDesc.size = sizeof(float);
@@ -568,7 +575,9 @@
             queue.Submit(1, &commands);
 
             EXPECT_TEXTURE_EQ(CompareFunctionPasses(compareRef, compare, textureValue) ? 1.f : 0.f,
-                              outputTexture, {0, 0});
+                              outputTexture, {0, 0})
+                << "compareRef=" << compareRef << " " << compare
+                << " textureValue=" << textureValue;
         }
     }
 
@@ -618,7 +627,9 @@
             float* expected =
                 CompareFunctionPasses(compareRef, compare, textureValue) ? &float1 : &float0;
 
-            EXPECT_BUFFER_U32_EQ(*reinterpret_cast<uint32_t*>(expected), outputBuffer, 0);
+            EXPECT_BUFFER_U32_EQ(*reinterpret_cast<uint32_t*>(expected), outputBuffer, 0)
+                << "compareRef=" << compareRef << " " << compare
+                << " textureValue=" << textureValue;
         }
     }
 
@@ -761,6 +772,10 @@
 
     wgpu::TextureFormat format = GetParam().mTextureFormat;
 
+    // Depth24PlusStencil8 could be mapped to a depth24unorm format and ULP for 24-bit unorm is
+    // 1 / (2 ^ 24 - 1) whereas ULP for 32-bit float varies and can be smaller.
+    const float tolerance = format == wgpu::TextureFormat::Depth24PlusStencil8 ? 1e-6f : 0.0f;
+
     wgpu::SamplerDescriptor samplerDesc;
     wgpu::Sampler sampler = device.CreateSampler(&samplerDesc);
 
@@ -820,7 +835,7 @@
         memcpy(&expectedDepth, &passDescriptor.cDepthStencilAttachmentInfo.depthClearValue,
                sizeof(float));
         EXPECT_BUFFER(depthOutput, 0, sizeof(float),
-                      new ::dawn::detail::ExpectEq<float>(expectedDepth));
+                      new ::dawn::detail::ExpectEq<float>(expectedDepth, tolerance));
 
         uint8_t expectedStencil = 0;
         memcpy(&expectedStencil, &passDescriptor.cDepthStencilAttachmentInfo.stencilClearValue,
@@ -870,7 +885,7 @@
         memcpy(&expectedDepth, &passDescriptor.cDepthStencilAttachmentInfo.depthClearValue,
                sizeof(float));
         EXPECT_BUFFER(depthOutput, 0, sizeof(float),
-                      new ::dawn::detail::ExpectEq<float>(expectedDepth));
+                      new ::dawn::detail::ExpectEq<float>(expectedDepth, tolerance));
 
         uint8_t expectedStencil = 0;
         memcpy(&expectedStencil, &passDescriptor.cDepthStencilAttachmentInfo.stencilClearValue,
@@ -890,7 +905,14 @@
     DAWN_SUPPRESS_TEST_IF(IsWindows() && IsIntelGen12() && IsANGLED3D11());
 
     wgpu::TextureFormat format = GetParam().mTextureFormat;
-    float tolerance = format == wgpu::TextureFormat::Depth16Unorm ? 0.001f : 0.0f;
+
+    // ULP for Depth16Unorm is 1 / (2 ^ 16 - 1). Similarly Depth24PlusStencil8 could be mapped to a
+    // depth24unorm format and ULP for 24-bit unorm is 1 / (2 ^ 24 - 1). But ULP for 32-bit float
+    // varies and can be smaller so set a tolerance (any value greater than unorm ULP is fine).
+    const float tolerance = (format == wgpu::TextureFormat::Depth16Unorm ||
+                             format == wgpu::TextureFormat::Depth24PlusStencil8)
+                                ? 1e-4f
+                                : 0.0f;
 
     // Test 0, between [0, 1], and 1.
     DoSamplingTest(TestAspectAndSamplerType::DepthAsDepth,
@@ -917,13 +939,17 @@
     DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsQualcomm());
 
     wgpu::TextureFormat format = GetParam().mTextureFormat;
-    // Test does not account for precision issues when comparison testing Depth16Unorm.
-    DAWN_TEST_UNSUPPORTED_IF(format == wgpu::TextureFormat::Depth16Unorm);
-
     wgpu::RenderPipeline pipeline = CreateComparisonRenderPipeline();
 
+    bool isDepthUnorm = format == wgpu::TextureFormat::Depth16Unorm ||
+                        (format == wgpu::TextureFormat::Depth24PlusStencil8 &&
+                         HasToggleEnabled("use_packed_depth24_unorm_stencil8_format"));
+
     // Test a "normal" ref value between 0 and 1; as well as negative and > 1 refs.
     for (float compareRef : kCompareRefs) {
+        if (isDepthUnorm) {
+            compareRef = std::clamp(compareRef, 0.0f, 1.0f);
+        }
         // Test 0, below the ref, equal to, above the ref, and 1.
         for (wgpu::CompareFunction f : kCompareFunctions) {
             DoDepthCompareRefTest(pipeline, format, compareRef, f, kNormalizedTextureValues);
@@ -953,19 +979,21 @@
 }
 
 DAWN_INSTANTIATE_TEST_P(DepthStencilSamplingTest,
-                        {D3D11Backend(), D3D12Backend(), MetalBackend(), OpenGLBackend(),
-                         OpenGLESBackend(), VulkanBackend()},
+                        {D3D11Backend(), D3D11Backend({"use_packed_depth24_unorm_stencil8_format"}),
+                         D3D12Backend(), D3D12Backend({"use_packed_depth24_unorm_stencil8_format"}),
+                         MetalBackend(), OpenGLBackend(), OpenGLESBackend(), VulkanBackend()},
                         std::vector<wgpu::TextureFormat>(utils::kDepthAndStencilFormats.begin(),
                                                          utils::kDepthAndStencilFormats.end()));
 
 DAWN_INSTANTIATE_TEST_P(DepthSamplingTest,
-                        {D3D11Backend(), D3D12Backend(), MetalBackend(), OpenGLBackend(),
-                         OpenGLESBackend(), VulkanBackend()},
+                        {D3D11Backend(), D3D11Backend({"use_packed_depth24_unorm_stencil8_format"}),
+                         MetalBackend(), OpenGLBackend(), OpenGLESBackend(), VulkanBackend()},
                         std::vector<wgpu::TextureFormat>(utils::kDepthFormats.begin(),
                                                          utils::kDepthFormats.end()));
 
 DAWN_INSTANTIATE_TEST_P(StencilSamplingTest,
-                        {D3D11Backend(), D3D12Backend(), MetalBackend(),
+                        {D3D12Backend(), D3D12Backend({"use_packed_depth24_unorm_stencil8_format"}),
+                         MetalBackend(),
                          MetalBackend({"metal_use_combined_depth_stencil_format_for_stencil8"}),
                          OpenGLBackend(), OpenGLESBackend(), VulkanBackend()},
                         std::vector<wgpu::TextureFormat>(utils::kStencilFormats.begin(),