[DX][VK] Implement SV_Depth pixel-shader output and Depth32 SRV mapping (#1046)#1229
[DX][VK] Implement SV_Depth pixel-shader output and Depth32 SRV mapping (#1046)#1229alsepkow wants to merge 18 commits into
Conversation
Adds the missing case for DataFormat::Depth32 in DX getDXFormat() so that SRV/UAV reads of textures declared with a depth-compatible format succeed. The depth-stencil view path (createTexture) uses getDXGIFormat() which already maps to D32_FLOAT. Adds Texture2D.Load.Depth32.test.yaml exercising the new path with a compute Load() of a 2x2 single-channel depth-format texture. Issue: llvm#1046 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds end-to-end coverage for SV_Depth by introducing a new
`Bindings.DepthBuffer` schema key that lets a test bind a
CPU-readable depth attachment, draws four single-pixel triangles each
writing a distinct exact-binary-fraction depth via SV_Depth, copies the
depth target back to a host buffer, and compares element-by-element
against the expected values on D3D12, WARP, and Vulkan.
Schema/API plumbing
-------------------
* `IOBindings` gains `DepthBuffer` (name) + `DepthBufferPtr`
(resolved CPUBuffer*), mirroring the existing RenderTarget pattern,
with YAML parsing and name resolution in `lib/Support/Pipeline.cpp`.
* New cross-backend helper
`createDepthBufferFromCPUBuffer` in `lib/API/Device.cpp` builds a
D32_FLOAT DepthStencil texture from a Depth32/1-channel CPUBuffer and
validates the texture description against the buffer.
* `isFloatingPointFormat` now accepts `DataFormat::Depth32` so the
existing `BufferFloatULP` comparator can be used for depth
read-back, matching the existing `testBufferFloatULP` case mapping
Depth32 to a float compare.
DirectX backend (`lib/API/DX/Device.cpp`)
-------------------------------------------
* `createDepthStencil` now branches on `Bindings.DepthBufferPtr`:
when set it builds the depth target from the helper and allocates a
paired `DSReadback` buffer; otherwise it falls back to the existing
default depth target (no read back).
* After the draw, the depth target is transitioned from DEPTH_WRITE to
COPY_SOURCE and copied to `DSReadback` using a placed footprint
whose format matches the resource's actual DXGI format (D32_FLOAT),
not the SRV-cast (R32_FLOAT) used for shader reads.
* `readBack` maps `DSReadback`, gets copyable footprints, and
copies into the user's `DepthBufferPtr` via `copyFromTexture`.
* PSO DSVFormat is now derived from the actual depth target
(`State.DepthStencil->getDesc().Fmt`) instead of being hard-coded
to D32_FLOAT_S8X24_UINT, so depth-only formats are valid here.
Vulkan backend (`lib/API/VK/Device.cpp`)
------------------------------------------
* InvocationState gains the same `DSReadback` buffer.
* `createDepthStencil` mirrors the DX branch.
* After `endEncoding`, `copyTextureToReadback` is reused for the
depth attachment with the depth aspect/layout/stage masks
(`LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL`,
`ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT`,
`LATE_FRAGMENT_TESTS_BIT`).
* `readBackData` maps and copies depth bytes into the
`DepthBufferPtr` using its image row stride.
Metal backend (`lib/API/MTL/MTLDevice.cpp`)
---------------------------------------------
* `createDepthStencil` now fail-fasts with `not_supported` if a
test binds `Bindings.DepthBuffer` so the next contributor knows to
wire it up instead of getting silently undefined behavior.
Test
----
* New `test/Feature/Semantics/SVDepth.test` runs on D3D12, WARP, and
VK and verifies four per-pixel SV_Depth writes (0.125, 0.25, 0.375,
0.5) against a Depth32 read-back buffer with `BufferFloatULP` ULP
0. `XFAIL: Clang` because DXC's HLSL -> DXIL lowering of SV_Depth
is not yet implemented in clang.
* Fix a YAML-schema regression in the previously-committed
`Texture2D.Load.Depth32.test.yaml`: `DispatchSize` -> top-level
`DispatchParameters: { DispatchGroupCount: ... }` to track the
upstream schema rename.
SV_StencilRef is intentionally out of scope until a stencil-bearing
`DataFormat` (e.g. D24S8) is added; `createDepthBufferFromCPUBuffer`
deliberately uses `Format::D32Float`.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Apply clang-format 19.1.6 to changed regions per pr-code-format CI.
The Metal offload backend cannot bind a Depth32 texture as an SRV through this path (rejects with Metal does not support buffer robustness). Mark the test UNSUPPORTED on Metal until llvm#1046 grows a Metal story; D3D12 and Vulkan continue to PASS. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Mac MTL CI reports `Bindings.DepthBuffer is not yet supported on the Metal backend.` for `Feature/Semantics/SVDepth.test`. The Metal device backend has no handling for the `DepthBuffer` binding introduced alongside this test, so mark it `UNSUPPORTED: Metal` until the MTL implementation lands. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Picks up the DriverVer fix (upstream llvm#1228) so clang-tidy stops failing on Device.cpp. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fixes misc-const-correctness warnings-as-errors from clang-tidy in CI. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
| auto TexFmtOrErr = toFormat(Buf.Format, Buf.Channels); | ||
| if (!TexFmtOrErr) | ||
| return TexFmtOrErr.takeError(); | ||
| if (*TexFmtOrErr != Format::D32Float) | ||
| return llvm::createStringError( | ||
| std::errc::invalid_argument, | ||
| "Depth buffer binding requires DataFormat::Depth32 with 1 channel."); |
There was a problem hiding this comment.
Why do we parse a format from the .yaml file if we only accept one format?
Also, we should consider using the Format enum in the .yaml so we don't have to do this conversion in the first place. We can then just check isDepth instead, which will also allow us to support other depth formats like those that include a stencil buffer.
There was a problem hiding this comment.
Followed your suggestion.
# Conflicts: # lib/API/DX/Device.cpp # lib/Support/Pipeline.cpp
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Address review feedback on the SV_Depth path: the YAML depth-buffer
binding parsed its format from a CPUBuffer's DataFormat+Channels and
then errored out unless that resolved to Format::D32Float, which was a
needless round-trip through the transitional toFormat() bridge and
hard-coded a single accepted format. Take the format directly from the
YAML instead.
Bindings.DepthBuffer is now a struct rather than a bare buffer name:
DepthBuffer:
Name: DepthTarget
Format: D32Float # optional; defaults to D32Float
createDepthBufferFromCPUBuffer takes the Format as an argument, drops
the toFormat() call, and validates with isDepthFormat() so depth-stencil
formats like D32FloatS8Uint can be plumbed through once a backend wires
them up. The CPUBuffer remains the readback destination and is no
longer used as the format source; a new validateTextureDimsMatchCPUBuffer
helper checks width/height/size without re-deriving Format via
toFormat().
DX, VK, and MTL backends, the IOBindings YAML mapping, and the SVDepth
lit test are updated for the new binding shape. SVDepth.test still
passes on D3D12 and Vulkan.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
# Conflicts: # include/API/FormatConversion.h
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fixes crash on depth-only formats like D32Float.
| # RUN: %dxc_target -T cs_6_0 -Fo %t.o %t/source.hlsl | ||
| # RUN: %offloader %t/pipeline.yaml %t.o | ||
|
|
||
| # Metal's offload backend cannot bind a depth-format texture as an SRV |
There was a problem hiding this comment.
nit: comment not needed
|
Moving this back to draft. Some NFC changes were factored out into a separate PR. #1327 |
| if (IS.DSReadback) { | ||
| void *DSMapped = nullptr; | ||
| auto &DSReadback = llvm::cast<DXBuffer>(*IS.DSReadback); | ||
| if (auto Err = HR::toError(DSReadback.Buffer->Map(0, nullptr, &DSMapped), | ||
| "Failed to map depth buffer readback")) | ||
| return Err; | ||
|
|
||
| auto &DS = llvm::cast<DXTexture>(*IS.DepthStencil); | ||
| const D3D12_RESOURCE_DESC DSDesc = DS.Resource->GetDesc(); | ||
| D3D12_PLACED_SUBRESOURCE_FOOTPRINT DSPlaced = {}; | ||
| uint32_t DSNumRows = 0; | ||
| uint64_t DSRowSizeInBytes = 0; | ||
| uint64_t DSTotalBytes = 0; | ||
| Device->GetCopyableFootprints(&DSDesc, 0u, 1u, 0u, &DSPlaced, &DSNumRows, | ||
| &DSRowSizeInBytes, &DSTotalBytes); | ||
|
|
||
| P.Bindings.DepthBuffer.Ptr->copyFromTexture(DSMapped, | ||
| DSPlaced.Footprint.RowPitch); | ||
| DSReadback.Buffer->Unmap(0, nullptr); | ||
| } |
There was a problem hiding this comment.
Let's use the copyTextureToBuffer function on the ComputeEncoder instead ^.^
This got recently added to main for the DX12 backend and the VK and MTL backends will use that path soon too.
Add SV_Depth pixel-shader output and Depth32-as-SRV support to the D3D12 and Vulkan backends.
Introduces a DepthBuffer YAML binding with an explicit Format field so tests can declare a CPU-readable depth attachment. The D3D12 backend creates a DSV, transitions to COPY_SOURCE for readback, and maps Depth32 to R32_FLOAT for SRV reads. Vulkan has a symmetric implementation; Metal returns an unsupported error for now.
Tests: SVDepth.test (pixel-shader depth output) and Texture2D.Load.Depth32.test.yaml (compute SRV read).
Resolves #1046
Assisted by Claude Opus 4.7