public sealed class PersistentUploadBuffer<T> where T : unmanagedA run of unmanaged records that lives across frames, and is rewritten only where it changed.
No guide page documents this yet — the page shows what the code says about itself.
Remarks
The sibling of UploadBuffer`1, and the difference between them is the whole reason this exists. That one is refilled from scratch every frame, which is right for a skeleton's matrices or a frame's light list — data the host recomputes anyway, so retaining it would buy nothing. This one is for data that is mostly the same as last frame's: a hundred thousand object bounds, of which a frame typically moves a handful. Uploading all of them costs three megabytes a frame to say that 99.99% of them did not move.
Dirtiness is decided by comparison, not by cooperation. Set compares the record it is given against the one it already holds and marks the slot only when the bytes differ. That is more work than being told — a linear pass over records the caller had to produce anyway — and it is what makes the tracking sound: there is no way for a caller to change something and forget to say so, which is the failure mode a dirty flag has and the one that shows up as geometry culled against last week's bounds. The alternative needs every writer of the source data to cooperate, and a writer that does not is silently wrong rather than slow.
A dirty set per frame in flight, not one. The ring is UploadBuffer`1's, and for the same reason: writing bytes the device may still be reading is a race the API cannot report. But a persistent buffer cannot simply rewrite this frame's region — the region is Slots frames stale, and what it is missing is every change since it was last written. So a change marks the record dirty in every slot, and each slot flushes its own set when its turn comes. One moved object costs one record written per frame for Slots frames, rather than the whole buffer once.
Runs are coalesced, because a driver call costs more than the bytes do. Two dirty records with three clean ones between them are one write of five, not two writes of one — see MergeGap. What that trades is bytes for calls, in the direction every measurement of this has ever pointed.
Fields and properties (12)
public const int MergeGapHow many clean records two dirty ones may be separated by and still be written together.
public IGraphicsDevice? DeviceThe device the buffer lives on. Set before the first upload.
public BufferHandle BufferThe buffer, valid once something has been uploaded.
public int CountHow many records are live this frame.
public int CapacityHow many the buffer has room for, per frame in flight.
public long OffsetWhere this frame's region starts, in bytes. Bind the buffer at this offset.
public long StrideHow many bytes one frame's region occupies, including its alignment padding.
public int SlotsHow many frames the ring is deep.
public int AlignmentWhat a buffer binding's offset must be a multiple of.
public long LastUploadBytesHow many bytes the last Upload handed to the device.
public int LastUploadRegionsHow many separate writes the last Upload made.
public ReadOnlySpan<T> ItemsWhat the host holds, for a test or an inspector.
Methods (5)
public PersistentUploadBuffer(string name, BufferUsage usage = Storage)A run of unmanaged records that lives across frames, and is rewritten only where it changed.
public void Begin(int required)Starts a frame: moves to the next region and states how many records are live.
public void Set(int index, in T value)Records one entry, marking it for upload if — and only if — it differs from what the device already has.
public void Upload()Writes the regions this frame's slot is missing, and nothing else.
public void Dispose()Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
Used by (3)
- GpuClusterVisibilityVixen.Rendering
- GpuVisibilityGroupVixen.Rendering
- PersistentUploadBufferTestsVixen.Rendering.Tests