Texture disk tiers: one budget, GPU-ready cache as a reuse-admitted hot tier, caching strategy option, disk-space-based default sizes #705

Open
opened 2026-10-12 10:15:13 +00:00 by sky_line · 2 comments
Owner

Part of #715 (cache rewrite epic).

Today: two separate texture caches with separate budgets -- LL's J2C cache (texturecache, the server's format) and the GPU-ready cache (gputex, the GPU's block-compressed mip chains, #274). Measured on one install: J2C 11.0 GB / 42.9k textures, GPU-ready 8.2 GB / 29.8k textures (at its budget, evicting). Per texel, GPU-ready is ~3-5x larger than J2C (BC1 4 bpp, BC3/5/7 8 bpp + mips; ~10 bits/texel measured). It stays affordable only because it stores each texture at the size actually shown -- for the same textures the two caches are about equal on disk (8.19 vs 8.34 GB).

Design

  1. J2C stays the source of truth (smallest, any resolution, rebuilds GPU-ready). Never evicted to make room for GPU-ready.
  2. One user-facing texture budget; GPU-ready is a hot tier inside it (share capped, ~30% in Normal). Evict GPU-ready before J2C; rank by reuses x decode time saved / bytes, not plain LRU.
  3. Admission (Normal): a texture enters GPU-ready on reuse only:
    • found in the J2C cache with a last access from an earlier session within N days (setting, default 30) -- LL's cache entries already record last access, no new index;
    • or decoded a second time in one session (evicted from VRAM and needed again).
      A one-off visit costs no GPU-ready disk; a place revisited within the window gets it from the second visit. An entry older than the window counts as new again.
  4. Stored at the shown resolution (as today); an upgrade replaces the entry. Captures stay rate-limited (no burst after login / teleport).

Caching strategy (Preferences > Advanced, beside the cache sizes)

  • Normal (default): the reuse rule above.
  • Aggressive: cache everything seen in GPU-ready form on first sighting, at the largest resolution decoded, larger GPU-ready share of the budget. For large disks / people who prefer big caches.

Default sizes, explicit sizes and the disk-full guard: moved to #711.

Duplicates: a J2C entry whose texture the GPU-ready tier holds at full size (or the 2048 cap) is kept while space is free and is first in line for eviction when it isn't (measured: 6,090 such textures, 1.78 GB of J2C, on one install). Aggressive keeps both.

Part of #715 (cache rewrite epic). **Today:** two separate texture caches with separate budgets -- LL's J2C cache (`texturecache`, the server's format) and the GPU-ready cache (`gputex`, the GPU's block-compressed mip chains, #274). Measured on one install: J2C 11.0 GB / 42.9k textures, GPU-ready 8.2 GB / 29.8k textures (at its budget, evicting). Per texel, GPU-ready is ~3-5x larger than J2C (BC1 4 bpp, BC3/5/7 8 bpp + mips; ~10 bits/texel measured). It stays affordable only because it stores each texture at the size actually shown -- for the same textures the two caches are about equal on disk (8.19 vs 8.34 GB). **Design** 1. J2C stays the source of truth (smallest, any resolution, rebuilds GPU-ready). Never evicted to make room for GPU-ready. 2. One user-facing texture budget; GPU-ready is a hot tier inside it (share capped, ~30% in Normal). Evict GPU-ready before J2C; rank by reuses x decode time saved / bytes, not plain LRU. 3. **Admission (Normal):** a texture enters GPU-ready on reuse only: - found in the J2C cache with a last access from an earlier session within `N` days (setting, default 30) -- LL's cache entries already record last access, no new index; - or decoded a second time in one session (evicted from VRAM and needed again). A one-off visit costs no GPU-ready disk; a place revisited within the window gets it from the second visit. An entry older than the window counts as new again. 4. Stored at the shown resolution (as today); an upgrade replaces the entry. Captures stay rate-limited (no burst after login / teleport). **Caching strategy (Preferences > Advanced, beside the cache sizes)** - **Normal** (default): the reuse rule above. - **Aggressive:** cache everything seen in GPU-ready form on first sighting, at the largest resolution decoded, larger GPU-ready share of the budget. For large disks / people who prefer big caches. **Default sizes, explicit sizes and the disk-full guard:** moved to #711. **Duplicates:** a J2C entry whose texture the GPU-ready tier holds at full size (or the 2048 cap) is kept while space is free and is first in line for eviction when it isn't (measured: 6,090 such textures, 1.78 GB of J2C, on one install). Aggressive keeps both.
sky_line added this to the Slipstream project 2026-10-12 10:15:13 +00:00
Author
Owner

Interface audit (step 1 of #715): what the texture tiers sit on and which store hooks they need.

Touches today

Code Role
newview/lltexturecache.cpp:1080 initCache (budget split: 36% entry reserve, 1M entries cap), :1786 purgeTexturesLazy, :1876 purgeTextures J2C budget + plain LRU by Entry::mTime
newview/llappviewer.cpp:9047-9063, :9163-9167 J2C budget = CacheSize x (100 - DiskCachePercentOfTotal)%; GPU-ready budget = SlipstreamGpuTexCacheSizeMB (separate)
newview/llgputexcache.cpp:182 evict (LRU), :200 add_entry (a new capture replaces the old file) GPU-ready budget
newview/vkgfx/llcleanrenderervk.cpp:6241 (canWrite, capture rate limit), :12170 (write) where admission decides
newview/lltexturefetch.cpp:2106 J2C write (full size known from mFileSize, partial flagged as size + 1)

Slice of the interface

  • Budget group TEXTURES = {J2C, GPUTEX}, with a share cap for GPUTEX (Normal ~30%, Aggressive larger): Store::addBudgetGroup, setBudget.
  • EvictionPolicy for the group:
    • keepScore(info, now) = reuses x decode time saved / bytes;
    • isDuplicate(info) = J2C covered by a full-size (or 2048-capped) GPU-ready record. Duplicates go first.
    • GPUTEX is evicted before J2C, and J2C is never evicted to make room for GPUTEX.
  • Admission data comes from the index with no new files: RecordInfo.prev_session_access (last access in an earlier session, persisted in the index snapshot) and reuse_count. The rule "found in J2C with an earlier-session access within N days, or decoded twice this session" becomes a lookup(J2C key) before write(GPUTEX key).
  • J2C record meta: full image size + complete flag (today's mImageSize). GPUTEX meta: w, h, enc, enc_level, encoder version, full size (today's Info + header).
  • An upgrade replaces the entry: a new GPUTEX record under the same key kills the old one.

Drop-in plan

After the store carries J2C and GPU-ready (#708 steps 2-3). Policy-only work: the admission check goes in the renderer's capture decision (llcleanrenderervk.cpp ~6241), and the eviction policy is registered for the TEXTURES group. Separate budget settings merge into one texture budget (+ strategy setting); this needs the budget-group decision asked in #715.

**Interface audit (step 1 of #715): what the texture tiers sit on and which store hooks they need.** ### Touches today | Code | Role | |---|---| | newview/lltexturecache.cpp:1080 `initCache` (budget split: 36% entry reserve, 1M entries cap), :1786 `purgeTexturesLazy`, :1876 `purgeTextures` | J2C budget + plain LRU by `Entry::mTime` | | newview/llappviewer.cpp:9047-9063, :9163-9167 | J2C budget = `CacheSize` x (100 - `DiskCachePercentOfTotal`)%; GPU-ready budget = `SlipstreamGpuTexCacheSizeMB` (separate) | | newview/llgputexcache.cpp:182 `evict` (LRU), :200 `add_entry` (a new capture replaces the old file) | GPU-ready budget | | newview/vkgfx/llcleanrenderervk.cpp:6241 (`canWrite`, capture rate limit), :12170 (`write`) | where admission decides | | newview/lltexturefetch.cpp:2106 | J2C write (full size known from `mFileSize`, partial flagged as size + 1) | ### Slice of the interface - Budget group `TEXTURES` = {J2C, GPUTEX}, with a share cap for GPUTEX (Normal ~30%, Aggressive larger): `Store::addBudgetGroup`, `setBudget`. - `EvictionPolicy` for the group: - `keepScore(info, now)` = reuses x decode time saved / bytes; - `isDuplicate(info)` = J2C covered by a full-size (or 2048-capped) GPU-ready record. Duplicates go first. - GPUTEX is evicted before J2C, and J2C is never evicted to make room for GPUTEX. - Admission data comes from the index with no new files: `RecordInfo.prev_session_access` (last access in an earlier session, persisted in the index snapshot) and `reuse_count`. The rule "found in J2C with an earlier-session access within N days, or decoded twice this session" becomes a `lookup(J2C key)` before `write(GPUTEX key)`. - J2C record meta: full image size + complete flag (today's `mImageSize`). GPUTEX meta: w, h, enc, enc_level, encoder version, full size (today's `Info` + header). - An upgrade replaces the entry: a new GPUTEX record under the same key kills the old one. ### Drop-in plan After the store carries J2C and GPU-ready (#708 steps 2-3). Policy-only work: the admission check goes in the renderer's capture decision (llcleanrenderervk.cpp ~6241), and the eviction policy is registered for the TEXTURES group. Separate budget settings merge into one texture budget (+ strategy setting); this needs the budget-group decision asked in #715.
Author
Owner

Region-recency eviction (decided 2026-10-18, see #715 / #708 placement)

The eviction score of a record is reuse × decode time saved ÷ bytes, weighted by the recency of the regions that use it. A shared record takes the most recent region, or avatar sighting, that referenced it.

  • Regions age out: a region not visited for a long time stops contributing. Its own records sink to the bottom, and segments that end up mostly low-value are evicted whole (one delete, SSD-friendly; compaction rules per #709).
  • Shared survives: shared assets stay while any recently visited region or avatar still uses them.
  • Manifests: a region's manifest is dropped when the region ages out.
  • Tier budgets: the GPU-ready share cap and J2C-first-as-source rules in this ticket stay as specified. Region recency is an extra weight, not a replacement.
## Region-recency eviction (decided 2026-10-18, see #715 / #708 placement) The eviction score of a record is reuse × decode time saved ÷ bytes, weighted by **the recency of the regions that use it**. A shared record takes the most recent region, or avatar sighting, that referenced it. - **Regions age out:** a region not visited for a long time stops contributing. Its own records sink to the bottom, and segments that end up mostly low-value are evicted whole (one delete, SSD-friendly; compaction rules per #709). - **Shared survives:** shared assets stay while any recently visited region or avatar still uses them. - **Manifests:** a region's manifest is dropped when the region ages out. - **Tier budgets:** the GPU-ready share cap and J2C-first-as-source rules in this ticket stay as specified. Region recency is an extra weight, not a replacement.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
sky_line/Slipstream#705
No description provided.