public sealed class GrassDispatchThe host for GrassScatter.rvn: the cell records, the ring of device buffers, and the dispatch that fills them.
Remarks
[docs/plan/31 § T6]'s owed device half. GrassRenderer is the CPU reference — what a headless test and a machine with no compute queue use — and this is the dispatch that does the same arithmetic where a forest can afford it. Neither is the definition: Hash is, and both transliterate it.
⚠ One counter per cell, not one for the dispatch. A global head would serialise every invocation of every cell on one cache line, and it would make a cell's run depend on the order the workgroups happened to retire in — which is the one thing a scatter that has to be reproducible cannot afford.
⚠ The instance buffer is the ring, and its slots are assigned by the host. Each resident cell owns a fixed run, so an invocation never negotiates for space with another cell — only with the other candidates of its own. That is what makes the atomic local and the memory bounded.
⚠ The counts buffer is cleared before every dispatch and not after. A pass that cleared afterwards leaves the buffer holding last frame's numbers for anything that reads it in between — and what reads it is the indirect draw, which would then draw a cell's previous population out of its current bytes.
Fields and properties (12)
public const int GroupSizeHow many candidates a workgroup covers on each axis. `[ComputeShader(8, 8, 1)]`.
public static int DrawCommandBytesHow many bytes one indirect command is — DrawIndexedIndirect's stride.
public int CapacityHow many cells one dispatch may cover.
public DrawCommand MeshWhat one blade is drawn with: its index count, its first index and its vertex offset.
public int BladesPerCellHow many blades a cell's run holds.
public int CellCountHow many cells the last Prepare wrote records for.
public long CeilingHow many blades the last dispatch could produce at most.
public (int X, int Y, int Z) GroupsHow many workgroups the last Prepare would dispatch, on each axis.
public int RefusedHow many cells were refused because the dispatch was full.
public BufferHandle InstancesThe buffer the draw reads its instances from.
public BufferHandle CountsAnd the one holding how many each cell produced.
public BufferHandle CommandsAnd the indirect arguments the draw reads.
Methods (7)
public GrassDispatch(IGraphicsDevice device, ShaderHandle shader, ShaderHandle arguments, int capacity = 256, int bladesPerCell = 4096)Creates the pass and its buffers.
public int Prepare(in GrassType type, FoliageCellGrid grid, IReadOnlyList<GrassSlot> resident, in GrassTerrainSource terrain, float densityScale = 1)Writes the cell records and the constants for one grass type.
public int Record(ICommandList commands)Records the dispatch.
public int RecordDraws(ICommandList commandList)Issues one indirect draw per resident cell.
public long CommandOf(int index)Where a cell's indirect command is, for the draw that reads it.
public (long Offset, long Length) RunOf(int slot)Where a cell's blades are, for the draw that reads them.
public void Dispose()Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
Used by (3)
- GrassDispatchTestsVixen.Rendering.Terrain.Tests
- GrassDrawPassVixen.Rendering.Terrain
- GrassDrawPassTestsVixen.Rendering.Terrain.Tests