Skip to content

fix: fire setDirty when pager fetch pipeline drains#316

Open
onderilkesever wants to merge 1 commit intosparkjsdev:mainfrom
onderilkesever:fix/pager-ondirty-on-fetch-complete
Open

fix: fire setDirty when pager fetch pipeline drains#316
onderilkesever wants to merge 1 commit intosparkjsdev:mainfrom
onderilkesever:fix/pager-ondirty-on-fetch-complete

Conversation

@onderilkesever
Copy link
Copy Markdown

@onderilkesever onderilkesever commented Apr 22, 2026

Problem

Fixes #298.

When SparkRenderer is used in an on-demand rendering architecture (render only when onDirty fires, no continuous loop) with a static camera, paged LOD splats can stop loading partway through. The onDirty chain has a gap: chunk fetches complete asynchronously, outside any render frame, and nothing calls setDirty() to signal that new data is ready.

Root cause

setDirty() is only called from:

  1. After sort completes (driveSort)
  2. When the LOD snapshot changes (updateInternal)
  3. After updateLodInstances when lodDirty = true (driveLod)

All three require a render frame to be in progress. If the camera is static and dirty = false, onBeforeRender never fires → driveLod never runs → consumeLodTreeUpdates is never called → downloaded chunks sit in lodTreeUpdates forever.

Fix

Add an optional onDirty callback to SplatPagerOptions and call it when processFetched() fully empties the fetched queue — i.e. after each chunk finishes decoding and is moved into the LOD pipeline. SparkRenderer passes () => this.setDirty() when constructing the pager.

This fires after every individual chunk lands, giving progressive rendering. The consumeLodTreeUpdates path is intentionally unchanged — SparkRenderer already calls setDirty() via the lodDirty flow when it processes those updates inside a render frame.

Changes

  • SplatPagerOptions: add optional onDirty?: () => void
  • SplatPager: store callback, call it when processFetched() drains fetched to empty
  • SparkRenderer: pass () => this.setDirty() when constructing the pager

Verified

Confirmed against an on-demand renderer that previously required a polling workaround to drive rendering during paged LOD loading. With this fix the workaround is no longer needed.

— Claude

@onderilkesever onderilkesever force-pushed the fix/pager-ondirty-on-fetch-complete branch from 044e0de to 4842c09 Compare April 22, 2026 15:30
…v#298)

When a static camera is combined with on-demand rendering, the
onDirty chain breaks during paged LOD loading because chunk fetches
complete asynchronously — outside any render frame — and nothing
signals the renderer that new data is ready to process.

Fix: call setDirty() via a new onDirty callback on SplatPager when
processFetched() fully empties the fetched queue. This fires after
every individual chunk lands, giving progressive rendering.

The consumeLodTreeUpdates path is intentionally unchanged; SparkRenderer
already calls setDirty() there via the existing lodDirty flow.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

onDirty chain breaks during paged LOD loading when camera is static

1 participant