Counting, flipping and packing: three small Direct3D 12 options on Metal
mtl12 now reports three more optional Direct3D 12 features: indirect draws that know their own index, upside-down viewports, and resources packed tighter than 64 KB.
What changed
Direct3D 12 has many small optional features. A game asks for them with CheckFeatureSupport and gets a slower path, or none, if the driver says no. This session turned on three of them.
- ExecuteIndirect tier 1.1. ExecuteIndirect runs a list of draws whose arguments live in a GPU buffer. Tier 1.1 adds an "incrementing constant": a root constant (a small value passed straight to the shaders) that holds the command's own index in the list, 0, 1, 2 and so on, without the buffer storing it. mtl12 already rewrites each D3D12 command into a Metal record with a small compute kernel. An unused word in each copy instruction became a flag that says "write the command's index here instead of copying".
- Negative viewport heights. A viewport with a negative height draws the picture upside down. Metal's viewport does the same, so the feature only had to be reported. One bug showed up: a pass with no render targets sized its area from
TopLeftY + Height. That is the top edge when the height is negative, so the whole pass was clipped away. The area now runs to the lower of the two edges. - Tight resource alignment, tier 1. D3D12 normally places every buffer and texture in a heap at 64 KB boundaries, so a 4-byte buffer costs 64 KB. With
USE_TIGHT_ALIGNMENT, the driver may report a smaller alignment.
The interesting part
Tight alignment looked like "report Metal's alignment and you're done". On the M1 Max, Metal needs 256 bytes for heap buffers and 128 for the textures I measured, and it refuses a buffer at offset 64. The first version reported exactly that, and vkd3d-proton's test_tight_resource_alignment passed.
Then the older vkd3d test test_resource_alignment went from skipping its tight cases to 2,976 failed checks. It encodes the alignments Windows drivers accept: a committed buffer (one with its own memory) may ask for up to 4 KB, a placed one for up to 256 bytes, a texture for up to the default, and the 64 KB-swizzled layouts not at all. None of that follows from Metal. It is API behaviour that a game could depend on. After following that table, the test passes 69,651 checks with 0 failures. Before this session it ran 27,215 and skipped the tight part.
The explicit Alignment a game passes doesn't change the alignment the driver reports, but it still limits where a placed resource may go. Both rules came from the tests, not from Metal.
Numbers
- Full run: 865 pass, 27 fail, 69 skip of 961, against 858/27/73 of 958 at the end of the last session. 7 newly passing, 0 newly failing. The baseline rerun gave the same numbers.
- api/execute_indirect_incrementing: 170 checks, 31 failed before the change. api/negative_viewport: 18 checks, 5 failed without the area fix. api/tight_alignment: 38 checks. It packs five resources, including a 4-byte buffer and two small textures, into one heap under 64 KB and reads them back; 7 checks failed before.
- pass
- fail
- skip
suite · share of each row's total
- vkd3d-proton (+3)497 pass, 14 fail, 66 skip of 577
- vkd3d (+1)178 pass, 9 fail, 3 skip of 190
- api (+3)90 pass, 0 fail, 0 skip of 90
- apps80 pass, 0 fail, 0 skip of 80
- wine-dxgi16 pass, 4 fail, 0 skip of 20
- samples4 pass, 0 fail, 0 skip of 4