public sealed class GrassResidencyWhich cells are close enough to hold a scattered buffer, and which slot each of them is in.
Remarks
[docs/plan/31 § T6]'s ring, and the reason grass costs a fixed amount of memory. A cell entering range is given one of a fixed number of pooled allocations and scattered into it; a cell leaving hands its slot back. Nothing about it is persisted, so eviction is free and re-entry is a re-scatter that produces the identical blades — Hash is what makes that true.
⚠ Eviction happens further out than creation, and the gap is not decoration. A camera standing on the boundary of a cell that is created and evicted at the same distance re-scatters it every frame, which is the whole cost of the feature paid for nothing. The hysteresis makes the two ranges different, so standing still is stable.
⚠ Distance is measured to the nearest point of the cell, not to its centre. A 32 m cell whose near edge is under the camera has its centre 22 m away, so a centre test with a 20 m range would leave the ground the camera is standing on bare.
⚠ A ring too small for its range drops the far cells. The wanted set is filled nearest first, so what a capacity that ran out loses is the horizon rather than a hole under the camera — and Refused says it happened, because the alternative is a level that quietly stops having grass at a distance nobody chose.
⚠ Not Vixen.Rendering's PageResidency, and what separates them is the policy rather than the storage. That service is an LRU under a byte budget: a request that needs room evicts whatever was used longest ago, and it always finds room, because only a pinned page refuses. This is a distance ring, where a cell further out than everything resident has to lose — Reclaim takes a slot only from a cell further away than the candidate, and Refused is what happens when there is none. Touching cells in distance order does not recover that: nine slots under a range that wants forty-one cells re-place every slot every frame while nobody moves, in either request order, because each of the thirty-two that does not fit still evicts one that did. ARingThatRanOutStandsStill is that property held down. The layering says the same thing more cheaply — this assembly references no device and that one is the render system — but the policy is why they are two services and not one.
Fields and properties (6)
public FoliageCellGrid GridThe grid its cells are of.
public int CapacityHow many cells may be resident at once.
public int CountHow many are.
public float HysteresisHow far past the creation range a cell survives before it is evicted, as a fraction.
public int RefusedHow many cells the last update wanted and had no slot for.
public IEnumerable<GrassSlot> ResidentEvery resident cell and its slot.
Methods (5)
public GrassResidency(FoliageCellGrid grid, int capacity)Creates an empty ring.
public bool TryGetSlot(FoliageCellKey cell, out int slot)Which slot a cell is in, if it is resident.
public GrassResidencyChange Update(Vector3 viewPosition, float range)Brings the ring up to date with where the view is.
public void Clear()Drops every cell, returning every slot.
public float DistanceTo(FoliageCellKey cell, Vector2 position)How far a cell's nearest point is from a horizontal position.
Used by (7)
- GrassFieldVixen.Rendering.Terrain
- GrassRendererVixen.Rendering.Terrain
- GrassRendererTestsVixen.Rendering.Terrain.Tests
- GrassResidencyTestsVixen.Foliage.Tests
- TerrainSceneRendererVixen.Rendering.Terrain
- GrassFieldVixen.Editor.App
- VegetationPresenterVixen.Editor.App