Post

Tried a few ways to resolve a visibility buffer as a nightly adventure a few days ago 😄 So far my favourite I could come up with is collecting lists of pixels per shader, internally sorted per draw, then a dispatch indirect per shader scalarizing on the draw index. Plus it can have cute bugs! 🙃 🧵

To collect the lists, I first count how many pixels each draw and shader want, then allocate space in a big buffer for each shader, then allocate space for each draw within the chunk allocated for their shader. Then repeat the same logic but writing the pixels to the buffer with the right offset.

Tried writing them once unsorted then sorting, but like I imagined, is just too expensive. I do believe you could make something like this work if you tiled the whole resolve and kept the working set of in local memory though. You need enough tiles/work in flight to feed a big GPU though.

0 reposts1 like

The resolve is dispatched per-shader, not per-draw, so it's still a very manageable amount of dispatch indirect calls. As in, a single one for all your variations of standard, anisotropic, whatever... so there isn't a ton of concerns about empty dispatch indirect arguments.

Because the list is not only sorted per-shader but also per-draw, most of the waves will just see one draw! To take advantage of this, I scalarize based on the draw index, so all of the draw-related data can be uniform and stay in scalar registers. With a noticeable win in occupancy too 😊

This can include a lot of data like per-draw transforms, tinting, material stuff, and notoriously also including the bindless texture indices. Nice not to need any of that NonUniformResourceIndex uglyness.

There's only a few waves per-frame that see two or more draws when resolving, which are the only cases where the waves aren't fully utilized. This is as good as it can be anyway, if those waves weren't going to shade another draw, they would have been inactive in a "wave-perfect" resolve anyway.