Sampler feedback with 32-bit atomics: one region, two words
D3D12 sampler feedback, which records which parts of a texture a shader sampled at which detail level, now works on an M1 Mac without 64-bit texture atomics.
What changed
Sampler feedback lets a shader record which parts of a texture it sampled, and at which mip (a level in the texture's chain of half-size copies). A game uses it to stream in only the detail it actually needs. The record is a "feedback map" with one entry per region of the texture, for example 16x16 texels. The program never reads the map directly. It asks the driver to decode it into one byte per region with ResolveSubresourceRegion. Metal has no such feature, so the layer now emulates it and reports tier 0.9.
I followed vkd3d-proton's design, which does the same on Vulkan. Each region gets 64 bits, 4 per mip: "this region was sampled", plus whether the footprint reached into the neighbour at +x, +y or +x+y. The shader ORs its bits in atomically, and a compute kernel decodes the map. The decode and encode kernels are ports of vkd3d-proton's shaders (LGPL-2.1).
The interesting part
vkd3d-proton writes the 64 bits with one 64-bit image atomic. The M1 Max has no 64-bit texture atomics, and SPIRV-Cross, which turns our SPIR-V into Metal source, emits none. So a region here is two 32-bit words side by side: mips 0–7 in the left word and 8–15 in the right. A patch to dxil-spirv (0004) splits the value and issues two 32-bit atomic ORs. The two halves are not written together. That is fine, because D3D12 requires a barrier between feedback writes and a decode.
Two things cost time. First, the atomics must be written inline. When they sit in a helper function that takes the image as a parameter, SPIRV-Cross can no longer see the writes. It then declares the texture read-only, and Metal refuses to compile the shader. Second, the first decode kernels hung the GPU queue: the compute argument table was created with room for one texture, and the kernels bind two. Nothing reported an error; the queue simply stopped.
The gallery scene shows what feedback means. On the left a textured floor turns under the camera, and every mip has its own colour. On the right is the decoded map: the finest mip each region was sampled at. Regions behind the camera still show a coarse mip. Pixels near the horizon sample coarse mips whose texels cover many regions, and the MIN_MIP map (the decoded "finest mip per region" format) counts such an access for every region those texels cover.
Numbers
- Full run: 795 pass, 26 fail, 92 skip of 913; 0 newly failing, 12 newly passing, all vkd3d-proton sampler feedback tests.
- The 13th, test_sampler_feedback_grad, runs 889 checks with 0 failures. It counts as a skip, because the test runs 29 of its checks only on AMD Windows.
- Previous run: 783 pass, 26 fail, 104 skip of 913.
- pass
- fail
- skip
suite · share of each row's total
- vkd3d-proton (+12)472 pass, 17 fail, 88 skip of 577
- vkd3d177 pass, 9 fail, 4 skip of 190
- apps77 pass, 0 fail, 0 skip of 77
- api65 pass, 0 fail, 0 skip of 65
- samples4 pass, 0 fail, 0 skip of 4