[Epic] Cache rewrite: segment store, compression, tiered texture caching, disk-aware sizing #715
Labels
No labels
amd
avatars
bug
builder
cannot-reproduce
crash
epic
feature
from-viewer
gl-removal
known-limitation
monitoring
parity
perf
platform
priority
critical
priority
high
priority
low
priority
medium
provenance
rendering
rt
status
in-progress
status
testing
status
up-next
triage
ui
upstream
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
sky_line/Slipstream#715
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Cache rewrite. Replace the file-per-item caches with one budgeted, compressed, crash-safe store and a policy for what is worth caching.
Parts
Related: #706 (texture RAM copies: boosted breakdown, face-less fix), #707 (RAM copies only while a feature holds a lease; depends on #705).
Principles
Order
Segment store + wear policy -> migration -> zstd -> texture tiers (#705) -> sizes -> HDD locality. Object cache zstd is independent.
Step 1 done: audit of today's disk caches + the interface the segment store must implement. (Code: main at
3df839bf30.)Today's caches
LLTextureCache(lltexturecache.cpp)texture.entries+texture.cache(first 600 B of each texture) +[0-f]/<uuid>.texturebodiesCacheSize16 GB), 1M entriesLLGpuTexCache(llgputexcache.cpp)gputex/<uuid>_<w>x<h>_e_l_v[_mask].gtxpoll()LLDiskCache/LLFileSystem(llfilesystem/)cache/sl_cache_<uuid>_0.asset, one flat folderLLSoundCachesounds/<uuid>.dsfLLCleanClusterCacheclusterlod/<uuid>_f<face>_b<ver>.clodLLVOCacheobjectcache/object.cache+objects_<x>_<y>.slc/_extras.slecGPU-ready and cluster LOD already have the store's client shape (in-RAM index, async verified reads, results merged on one thread). The hard consumers are
LLFileSystem(seek / append / read-write, ~70 call sites, the mesh repository patches a sparse file in place, downloads write a temp UUID then rename) andLLTextureCache(600-byte header split, a full-prefix rewrite per discard step, reads at an offset).Interface (sketch; full version in the local research note)
Ticketcan be polled (fetch workers that park) or completed on a WorkQueue the caller picks plus an optional wake callback. The store never chooses a thread.Mapping (phase 1 = adapters, call sites unchanged; phase 2 = call sites move)
LLGpuTexCachelookup/read/write;Info= record metaLLTextureCachereadRangeSync(offset,size); writes supersedeLLFileSystemREAD / exists / sizeLLFileSystemWRITE / APPEND / renamerename(new_id)commits one record, WRITE restarts,removeaborts, session end commitsLLDiskCache::purge/ purge threadLLCleanClusterCacheLLSoundCache,LLVOCacheCodec(#713)Findings worth knowing
Open question for the owner
The budget model. #708 says one store with one budget, #711 lists Textures / Assets / Sounds separately, and #705 makes GPU-ready a capped share inside Textures. Proposal: one store with budget groups TEXTURES {J2C, GPU-ready share}, ASSETS {assets, mesh} and CLUSTER, and sounds keep their own budget outside the store.
Recommended first implementation step
Build the store core (segments, record format, sharded index + snapshot, one writer thread, tail recovery, lock file / overflow, codec shim, stats) as a standalone library with #708's tests (crash at each write step, corrupt / truncated records, disk full, two instances). Then move GPU-ready onto it behind its current API. It has whole-record semantics, our code on both ends, the only compressing kind (so it proves the codec), and a failure there just falls back to J2C. Then J2C (adds range reads and supersede), then assets + the mesh parts (the 265k-file folder; it waits only so the core is proven before it carries asset delivery). Add the zstd 3p package in parallel.
Decided 2026-10-18: region-aware placement inside the segment store
The store stays one append-only segment store with one index (#708). On top of it, content is placed by region, so a revisit reads a few contiguous runs instead of thousands of scattered records, and the store can prefetch a region on arrival. The details are split across the tickets:
Interface additions (both optional; the drop-in adapters work without them, they just get no locality):
write(..., PlacementHint{region_id | SHARED})prefetchRegion(region_id, priority), andcancelPrefetch(region_id)Bail-out clause (owner): if testing shows placement is too complex to manage reliably, fall back towards plain size-budgeted caches. Placement must therefore stay removable: hints are advisory, a missing or wrong hint only costs locality and never correctness, and manifests are a separate record kind that can be dropped without touching the data records.
Decisions (owner, 2026-10-18)
Budget groups: one store with budget groups.
Sounds keep their own small cache outside the store. Preferences can still show separate sizes per group.
Cluster LOD page cache joins the store as its own budget group. It migrates after the GPU-ready textures.
Second running viewer: read-only for now. The second instance opens the store read-only and writes new records to a small private overflow (required, because assets reach their consumers through the cache). Separately, the current asset cache's purge must not run in a second instance; it runs today regardless of the read-only check.
First implementation step: the store core as a standalone library (
indra/llcachestore) with #708's tests, including the #709 wear policy, region-run placement and the zstd codec layer (LLCacheCodec, already merged). Viewer integration follows, GPU-ready textures first.Shipped in 1.4.0 Beta 1. Closing; reopen if it misbehaves.