One draw, four views: view instancing by replay
D3D12 view instancing, where one draw renders the same geometry for up to four cameras, now works on Metal by replaying each draw once per view.
What changed
D3D12 view instancing lets one draw run for up to four "views", for example the two eyes of a VR headset or the faces of a cube map. The shaders read an SV_ViewID, and the pipeline sends each view to its own layer of an array texture and its own viewport (a rectangle of the target). The layer now reports view instancing tier 1, the level at which the driver may implement all of this however it likes.
The way I chose is plain: every draw, indexed draw, mesh dispatch and ExecuteIndirect is recorded once per view. Each copy carries one extra 32-bit word after the root arguments (the small block of constants and pointers a D3D12 draw passes to its shaders), holding the view number and the view's layer. dxil-spirv, the DXIL-to-SPIR-V compiler this project uses, already knew how to read such a word for vkd3d-proton. A small patch lets it come from our root arguments rather than a separate buffer. Which viewport each view uses is known when the pipeline is created, so it becomes a literal in the Metal shader source.
The interesting part
The vkd3d-proton test for this feature also pointed at an older gap. One case has a vertex shader that writes the layer, feeding a geometry shader (a stage that can emit new triangles). Metal has no geometry shaders, so the project runs them as Metal object functions. The Metal compiler rejected the result: SPIRV-Cross had declared the geometry shader's layer inputs the way a pixel shader receives them. A two-line SPIRV-Cross patch (0033) now reads them from the input vertices, like positions. Before this, any geometry shader that read SV_RenderTargetArrayIndex would have failed to compile.
I also had to correct my own ADR draft. It said Metal's vertex amplification (Metal's own multi-view feature) goes up to 2 views on Apple GPUs. A four-line Swift probe showed the M1 Max accepts 1 to 8. Amplification is still not used, because only plain vertex pipelines could use it. Mesh pipelines, and the emulated geometry and tessellation stages, would still need the replay.
Numbers
- Full run: 781 pass, 27 fail, 104 skip of 912. Entry 041 ended at 778 of 911. 0 newly failing; 3 newly passing: the new api/view_instancing and vkd3d-proton's two view instancing tests.
- api/view_instancing: 672 checks over 10 cases (layers, viewports, masks, indexed and indirect draws, a shader-written layer).
- The two vkd3d-proton view instancing tests: 2,329 checks, up from 31 when they were skipped.
- pass
- fail
- skip
suite · share of each row's total
- vkd3d-proton (+2)459 pass, 18 fail, 100 skip of 577
- vkd3d177 pass, 9 fail, 4 skip of 190
- apps77 pass, 0 fail, 0 skip of 77
- api (+1)64 pass, 0 fail, 0 skip of 64
- samples4 pass, 0 fail, 0 skip of 4