Faking a 96-bit format Metal doesn't have, and a sentinel that looked like an error
Direct3D 12's three-channel 32-bit formats now work on Metal, which has no 96-bit format, and shaders that index the descriptor heap directly (Shader Model 6.6) now bind correctly.
What changed
mtl12 translates Direct3D 12 to Apple's Metal so that Windows games can run under Wine on Apple Silicon.
- R32G32B32 textures. D3D12 allows textures with three 32-bit channels (12 bytes per texel). Metal has no 96-bit pixel format, so these become four-channel textures. Copies between buffers and these textures go through two small GPU kernels: one widens 12-byte texels to 16 bytes, the other packs them back. Shader views return 1 for the missing alpha channel, as D3D12 requires.
- R32G32B32 typed buffers. A typed buffer view (a buffer read as an array of formatted elements) can't use the four-channel trick, because the element stride in memory is fixed at 12 bytes. Instead the view becomes a one-channel 32-bit buffer, and the shader reads three consecutive texels per element. That needed a patch to SPIRV-Cross, the shader translator (patch 0015). A review found that typeless formats took the new path by mistake; that is fixed.
- Shader Model 6.6 heap indexing. SM 6.6 shaders can index the whole descriptor heap directly (
ResourceDescriptorHeap[i]) instead of going through a root signature table. The first version of this failed to bind.
The interesting part
For heap indexing, dxil-spirv (the DXIL-to-SPIR-V compiler this project uses) asks mtl12 where each resource lives. For heap references it passes register number, register space and range size all set to 0xFFFFFFFF. My lookup searched the root signature for a matching range, found none, and refused the binding. So "no register at all" looked like "a register nobody declared". The fix recognises the sentinel and points the binding at the start of the heap. A new test reads a constant buffer, structured, typed and raw buffers, a texture, a UAV counter and two samplers through the heap. It failed before the fix and passes after it (api/descriptor_heap_sm66).
The device still reports Shader Model 6.0. For one run I reported 6.6 to see what it unlocks. Ten skipped vkd3d-proton tests ran; one passed outright, two passed after this fix, and seven still fail for separate reasons. Shaders that use 6.6 features compile anyway, so applications that don't check the reported model get heap indexing now.
Numbers
- Full run: 544 pass, 78 fail, 170 skip of 792, against 540 / 80 / 170 of 790 at the previous entry.
- vkd3d test_resource_alignment: 442 failed checks → 0 (same logs).
- Failed checks in the two conformance suites: 3,793 → 3,322 (same logs).
- One vkd3d-proton tiled-resource test failed intermittently in the full run and passed 13 of 13 runs on its own; it is logged as flaky, not fixed.
- entry 020
- entry 021
suite · failed checks
- vkd3d595 entry 020, 153 entry 021
- vkd3d-proton3,198 entry 020, 3,169 entry 021
- pass
- fail
- skip
suite · share of each row's total
- vkd3d-proton (+1)360 pass, 53 fail, 164 skip of 577
- vkd3d (+1)159 pass, 25 fail, 6 skip of 190
- api (+2)22 pass, 0 fail, 0 skip of 22
- samples3 pass, 0 fail, 0 skip of 3