Cross-RenderList content-hash dedup for decoded asset bytes. More...
Detailed Description
Cross-RenderList content-hash dedup for decoded asset bytes.
Lives on GfxContext, shared across all RenderLists in the session. Keyed by content_hash (64-bit stable hash of the source bytes — the canonical primitive is ossia::hash_bytes from ossia/detail/hash.hpp, which dispatches to rapidhash; parsers and the preprocessor produce content_hash values through that helper).
Purpose: one decode per asset across the whole session. When two glTF files reference the same baseColor.jpg, we decode it once and reuse. Per-RenderList GpuResourceRegistries upload from the cached QImage independently: one decode, N uploads.
Not the GPU-resource owner — GpuResourceRegistry does that per QRhi. AssetTable only holds CPU-side bytes + format metadata during the window between decode and eviction.
Lifecycle
Three states per entry:
- hot (refcount > 0): actively held by at least one consumer. Never evicted.
- cool (refcount == 0, still referenced in the LRU list): eviction candidate.
acquire()resurrects it at zero cost. - evicted: dropped from the map. Next
acquire()misses; the caller re-decodes and restage()s.
Transitions:
stage()inserts a refcount==0 entry directly into the cold LRU pool (or no-op if already present), so a stage that is never acquired remains trimmable rather than leaking.acquire()bumps refcount and (if the entry is cold) splices it out of the LRU list.release()decrements; at 0 the entry moves to the LRU head.trim(max_bytes)pops from the LRU tail until under budget.maybeAutoTrim()called periodically: reads a supplied utilization ratio and trims when above a threshold.
Byte accounting is approximate — sizeInBytes(DecodedAsset) hits QImage::sizeInBytes and the raw bytes vector size. Good enough for budget bookkeeping without a full allocator hook.
Thread safety
All public methods take m_mutex. Fine for the access pattern (parser worker threads stage, render threads acquire/release, GUI tick trims) — the mutex is held for microseconds at a time.
Classes | |
| struct | DecodedAsset |
Public Member Functions | |
| AssetTable (const AssetTable &)=delete | |
| AssetTable & | operator= (const AssetTable &)=delete |
| void | stage (uint64_t content_hash, QImage image) |
| void | stage (uint64_t content_hash, std::shared_ptr< const std::vector< uint8_t > > bytes, std::string mime_type={}) |
| std::shared_ptr< const DecodedAsset > | acquire (uint64_t content_hash) |
| std::shared_ptr< const DecodedAsset > | peek (uint64_t content_hash) const |
| void | release (uint64_t content_hash) |
| std::size_t | trim (std::size_t max_bytes_budget) |
| void | maybeAutoTrim (float utilization, float high_watermark=0.80f, float target=0.60f) |
| std::size_t | size () const noexcept |
| Debug / inspector. | |
| std::size_t | totalBytes () const noexcept |
| Approx total bytes held in cold pool + hot pool. | |
| std::size_t | coldCount () const noexcept |
| Number of cold entries eligible for eviction. | |
Member Function Documentation
◆ acquire()
| std::shared_ptr< const AssetTable::DecodedAsset > Gfx::AssetTable::acquire | ( | uint64_t | content_hash | ) |
Return a shared pointer to the decoded asset, bumping its refcount. Null when not staged. O(1) average.
◆ maybeAutoTrim()
| void Gfx::AssetTable::maybeAutoTrim | ( | float | utilization, |
| float | high_watermark = 0.80f, |
||
| float | target = 0.60f |
||
| ) |
Called on a cadence (e.g. from the Gfx thread idle tick) to pressure-trim when the supplied utilization ratio exceeds high_watermark. Cost: O(n) in the LRU list when a trim fires; constant otherwise.
utilization in [0, 1]. Compute externally from QRhiStats::usedBytes / (usedBytes + unusedBytes), or from a hard OS-level memory query. high_watermark default 0.80. target default 0.60.
◆ peek()
| std::shared_ptr< const AssetTable::DecodedAsset > Gfx::AssetTable::peek | ( | uint64_t | content_hash | ) | const |
Read-through without refcount bump. The returned shared_ptr keeps the DecodedAsset alive on the caller's side even if the AssetTable evicts the entry — but does NOT prevent eviction. Suitable for the "upload once to GPU, then done" path where the consumer doesn't care if the CPU-side bytes live on.
◆ release()
| void Gfx::AssetTable::release | ( | uint64_t | content_hash | ) |
Decrement refcount. At 0 the entry moves to the LRU head and is eligible for eviction on the next trim.
◆ stage()
| void Gfx::AssetTable::stage | ( | uint64_t | content_hash, |
| QImage | image | ||
| ) |
Publish a decoded asset under its content hash. Idempotent — a second stage() with the same hash is a no-op (hash contract: same hash = same bytes).
◆ trim()
| std::size_t Gfx::AssetTable::trim | ( | std::size_t | max_bytes_budget | ) |
Force eviction until the cold-pool byte total is below max_bytes. Called explicitly by UI ("unload unused") or implicitly by maybeAutoTrim.
- Returns
- bytes evicted.
The documentation for this class was generated from the following files:
- AssetTable.hpp
- AssetTable.cpp