Skip to content

niivue: planner and decoded tier still price unlit chunked volumes at 8 bytes per voxel #203

Description

@cdrake

Follow-up from the review of #167 (issue #154, tracking #160). Line numbers are from main at 2a88a618.

Problem

#167 made the residency budget honest: an unlit chunked volume now holds a 1x1x1 placeholder gradient, and residentBytesForChunkDesc (packages/niivue/src/volume/chunkBudget.ts:101) prices a chunk at 4 bytes per voxel when hasGradient is false. Two other places still assume 8 bytes per voxel and are now out of step with it:

  1. The planner. bytesOf in packages/niivue/src/volume/chunking.ts:1225-1229 costs every brick at 8 bytes per voxel when deciding whether a candidate plan fits budget. For an unlit volume (the default: gradientAmount, gradientOpacity and silhouette all 0) the real residency is half that, so the planner picks a plan with about half the bricks the GPU budget could hold, and the coarser-than-necessary result is exactly what niivue: unlit chunked volumes allocate a full-size dummy gradient #154 set out to avoid. The renderer's working-set cap sees the true bytes, but it can only evict, not refine.

  2. The decoded CPU tier. GPU_BYTES_PER_VOXEL = 8 in packages/niivue/src/volume/decodedChunkCache.ts:65 sizes the decoded tier as 1.5x the shadow of the resident set. With unlit volumes now holding twice as many voxels on the GPU for the same budget, that tier is 0.75x of the real shadow. The module's own note (decodedChunkCache.ts:86-88) explains that a tier smaller than the shadow holds no evicted chunk at all. Concretely, a uint8 volume with a 256 MiB GPU budget: resident voxels go from 32M to 64M, the tier stays at 48 MiB, which is now smaller than the resident set. The tier is sized once when the entry is created (packages/niivue/src/gl/render.ts:926, packages/niivue/src/wgpu/render.ts:1073), so it cannot follow a lighting toggle either way.

Proposal

  • Give the planner a bytesPerVoxel input (4 when the caller knows the volume is unlit, 8 otherwise) and thread needsGradient() through from both renderers' plan sites, the same way fix(niivue): use a 1x1x1 placeholder gradient for unlit chunked volumes #167 threaded it into the working-set cap.
  • Size the decoded tier at 4 bytes per voxel resident. That is the larger and therefore conservative shadow, it is still capped at 384 MiB, and it keeps the tier effective for the common unlit case without any dynamic resizing.
  • Unit tests: the planner fits twice the bricks at 4 than at 8 for the same budget, and decodedTierBudgetBytes for a uint8 volume at 256 MiB exceeds the resident voxel count.

🤖 Generated with Claude Code

https://claude.ai/code/session_01RxnaB17kqcirGHNyAjPbgG

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions