|
|
Donner SVG 0.8.0-pre
SVG editor and embeddable C++20 engine.
|
Document-scoped suballocation slab for GPU residence. More...
#include "donner/svg/renderer/geode/GeodeResidentPathComponent.h"
Classes | |
| struct | Allocation |
| One sub-range of a slab chunk. More... | |
Public Member Functions | |
| const gpu::Buffer & | bufferForId (uint64_t bufferId) const |
The chunk buffer carrying bufferId, or a null handle when this slab does not own it. Uploads need the owning handle; Slot and Allocation only carry an identity view. | |
| uint64_t | chunkBytesForId (uint64_t bufferId) const |
Byte size of the chunk carrying bufferId, or 0 when this slab does not own it. | |
| GeodeResidentSlab (uint64_t deviceId, std::shared_ptr< GeodeDocumentGeometryBudget > budget=nullptr) | |
| Create an empty slab bound to deviceId (usually the current renderer's device). Chunks are allocated lazily on first use. | |
| GeodeResidentSlab (const GeodeResidentSlab &)=delete | |
| GeodeResidentSlab & | operator= (const GeodeResidentSlab &)=delete |
| uint64_t | owningDeviceId () const |
| Device this slab's chunks were created on. | |
| const std::shared_ptr< GeodeDocumentGeometryBudget > & | documentBudget () const |
| void | beginFrame (uint64_t frameIndex) |
| Merge the previous frame's freed ranges into the reusable free list. Gated on frameIndex (the renderer's frame counter) so the merge runs at most once per frame no matter how many times the renderer touches the slab: the accessor calls this on every slot lookup, which covers every draw entry point (full-document draws and multi-document tile/thumbnail frames alike) without trusting any single call site. | |
| bool | allocate (GeodeDevice &device, uint64_t size, uint64_t alignment, Allocation &out) |
| Bump- or free-list-allocate size bytes aligned to alignment. Creates a new chunk (and counts it) when nothing fits. Returns false when the device cannot create a buffer. | |
| void | free (const Allocation &alloc) |
| Return an allocation for reuse at the NEXT frame boundary. The range is not reusable within the current frame: see beginFrame(). | |
| void | retireBindGroup (gpu::BindGroup bindGroup) |
| Park a bind group a slot is dropping until the next frame boundary. | |
Document-scoped suballocation slab for GPU residence.
Per-slot residence previously gave every cached path its own combined GPU buffer, so a first frame allocated one buffer per resident slot (Lion: 133). The slab replaces those with sub-ranges of a small number of growable chunk buffers owned by the document's registry context, cutting first-frame bufferCreates to one chunk per growth step (1 to 2 for typical documents).
Allocations are bump-placed and returned through a first-fit free list, so a geometry edit that re-uploads a slot reuses the freed range. Each chunk's bytes are accounted in the device's live-resident-bytes gauge on creation and released when the slab is destroyed with its registry. Note the gauge therefore reports slab CAPACITY, not live payload bytes: it grows when a chunk is created and never shrinks on slot reset, which is the right shape for leak detection but over-reads against the previous per-slot accounting.
The slab is bound to one device (GeodeDevice::deviceId()): a document rendered by a second device gets a fresh slab, and the old chunks are released without touching the old device's objects (WebGPU retains any still-referenced buffers through submitted command buffers).
Not thread-safe: allocate/free/beginFrame mutate the free list and bump cursors without locking. The renderer serializes one frame per device at a time, so a document's slab is only touched from one thread.
| struct donner::geode::GeodeResidentSlab::Allocation |
One sub-range of a slab chunk.
| Class Members | ||
|---|---|---|
| BufferRef | buffer | |
| uint64_t | bufferId = 0 | Stable identity of buffer (see GeodeDevice::AllocateBufferId). Only allocation results carry it; free matches on the handle, which is unambiguous while the slab that owns the chunk is alive. |
| uint64_t | offset = 0 | |
| uint64_t | size = 0 | |
|
inline |
Merge the previous frame's freed ranges into the reusable free list. Gated on frameIndex (the renderer's frame counter) so the merge runs at most once per frame no matter how many times the renderer touches the slab: the accessor calls this on every slot lookup, which covers every draw entry point (full-document draws and multi-document tile/thumbnail frames alike) without trusting any single call site.
Freed ranges must NOT be reused within the frame that freed them: a re-upload that reuses its own just-freed range would overwrite the geometry before the already-recorded draws of the same frame read it (the resident slots hold one buffer range per slot, not one buffer per draw). Merging at the first touch of frame N is safe because ranges pending at that point were freed no later than frame N-1, whose command buffer was submitted at its endFrame; a second renderer on the same device has its own frame counter, and a stale-index skip only defers the merge (the safe direction). Frames on one device are serialized by contract, so no merge can run under another renderer's unsubmitted frame.
|
inline |
The chunk buffer carrying bufferId, or a null handle when this slab does not own it. Uploads need the owning handle; Slot and Allocation only carry an identity view.
| bufferId | Stable buffer id stamped on the slot or allocation. |
|
inline |
Byte size of the chunk carrying bufferId, or 0 when this slab does not own it.
| bufferId | Stable buffer id stamped on the slot or allocation. |
|
inline |
Park a bind group a slot is dropping until the next frame boundary.
A slot's cached bind group can be dropped mid-frame (geometry re-upload, component removal, device change) while draws recorded earlier in the frame still name it and have not reached the backend yet. Destroying it there fails those draws closed, so it is held here and released at the next beginFrame, by which point the frame that recorded them has been submitted.
| bindGroup | Bind group to release at the next frame boundary. |