Reserved resources on Metal 4, and tiles that remember what they were mapped to
D3D12 tiled resources now run on Metal 4's placement sparse API, with one workaround: an unmapped tile on Apple's GPU keeps showing the memory it was last mapped to.
What changed
D3D12 has reserved resources (also called tiled resources): buffers and textures with no memory of their own. The application maps their 64 KB tiles onto pieces of heaps, which are large memory blocks, and it can remap them at any time. Streaming engines use them for virtual texturing. macOS 26 added a matching Metal 4 feature, placement sparse resources. mtl12 now implements reserved resources on top of it: creation, GetResourceTiling, UpdateTileMappings, CopyTileMappings and CopyTiles.
Metal matches D3D12 closely. Tiles are 64 KB, and the 2D tile shapes are the ones D3D12 specifies. The gaps were found with small spike programs before the implementation. 3D textures get flat 2D tiles, so there are no 3D reserved textures. The documented tier queries crash with "unrecognized selector". Sparse buffers cannot be viewed as typed texture buffers.

The interesting part
In D3D12, a tile can be mapped to NULL. On the better hardware tiers, reads of a NULL tile then return zero. Metal has an unmap operation for this, and a tile that was never mapped does read zero. But on the M1 Max, a tile that had been mapped and was then unmapped mostly kept returning the data of the page it last pointed at. This happened when the GPU read it through a Metal 4 queue, and it did not matter whether I waited on the CPU or put explicit barriers in between. A shader on the older Metal queue read zero. The first unmap sometimes worked and later ones did not, which looks like timing. A small standalone program reproduces it.
So mtl12 never unmaps. Each device has one 64 KB tile of zeros (the kernel zero-fills fresh pages), and a NULL mapping points there. That gives the right reads. A write to a NULL tile would now change the shared zero tile, while D3D12's tier 2 requires such writes to be dropped. Tier 2 also needs shader feedback about whether a sample touched unmapped tiles, and SPIRV-Cross, which translates our shaders to Metal's language, cannot emit that yet. mtl12 therefore reports tier 1, where both of those are undefined.
Numbers
- Full run: 501 pass, 93 fail, 184 skip of 778, up from 495/93/190. Exactly six tests changed, all from skip to pass, all reserved resource tests. No test got worse.
- vkd3d-proton's test_get_resource_tiling: 1,298 checks, 0 failures, 8 skipped for tier-3/4 cases.
- Spike: 952 of the tiles checked read stale data in 100 remap iterations through the Metal 4 queue, 0 through the older queue.
how the tile was remapped and read · tiles read wrong
- separate unmap calls952
- one unmap call952
- Metal 4 queue shader read966
- older queue shader read0
- pass
- fail
- skip
suite · share of each row's total
- vkd3d-proton (+5)339 pass, 64 fail, 174 skip of 577
- vkd3d (+1)151 pass, 29 fail, 10 skip of 190
- api8 pass, 0 fail, 0 skip of 8
- samples3 pass, 0 fail, 0 skip of 3