Skip to content

fix(gsplat): re-render work buffer for non-streaming splats on device restore#8973

Merged
mvaligursky merged 1 commit into
mainfrom
mv-gsplat-restore-workbuffer
Jun 26, 2026
Merged

fix(gsplat): re-render work buffer for non-streaming splats on device restore#8973
mvaligursky merged 1 commit into
mainfrom
mv-gsplat-restore-workbuffer

Conversation

@mvaligursky

@mvaligursky mvaligursky commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Summary

A non-streaming SOG/PLY gsplat disappears after a WebGL context lose/restore (the ground/rest of the scene renders, but the splat is gone).

Cause: the unified-gsplat work buffer is a render target whose contents are GPU-rendered, not re-uploaded from CPU like normal textures. On restore it is recreated empty, and nothing re-triggers the rebuild. (The streaming/octree path already handles this — its own devicelost handler reloads resources from URL, which drives a full rebuild.)

Fix: GSplatManager subscribes to devicerestored and forces a full work-buffer rebuild + re-sort (mirroring the existing prepareRendererMode invalidation). It is gated on !world.hasOctreeInstances:

  • Pure non-streaming world — nothing else would rebuild → this handler does it.
  • World containing octree instances (pure or mixed) — the octree's own reload already drives a full rebuild that re-renders all active splats (including direct ones), and forcing a rebuild here would render from octree source textures that were just destroyed for async reload → debug asserts. So it is correctly skipped.

The subscription uses the EventHandle returned by on() and removes it via handle.off() in destroy().

Testing

Lose/restore via WEBGL_lose_context, counting "texture has been destroyed" asserts:

Scenario Result
Non-streaming skull (spherical-harmonics), CPU sort renders, 0 asserts
Non-streaming skull, GPU sort renders, 0 asserts
Streaming (lod-streaming) 0 asserts (gated out; matches clean baseline)
Mixed (world — octree + direct ply + SOG, ~4M splats) all visible, 0 asserts

… restore

After a graphics context restore the unified-gsplat work buffer render target
comes back blank: its contents are GPU-rendered, not re-uploaded from CPU like
source textures, and nothing re-triggered the rebuild. A non-streaming SOG/PLY
splat therefore disappeared after a context loss/restore.

Add a `devicerestored` handler on GSplatManager that forces a full work-buffer
rebuild and re-sort. It is skipped when the world has streaming octree instances:
those destroy and asynchronously reload their source resources from URL via their
own device-lost handling, so rebuilding here would render from textures that have
been destroyed (and not yet reloaded).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Build size report

This PR changes the size of the minified bundles.

Bundle Minified Gzip Brotli
playcanvas.min.js 2262.8 KB (+0.3 KB, +0.01%) 582.1 KB (+0.1 KB, +0.01%) 452.6 KB (−0.2 KB, −0.04%)
playcanvas.min.mjs 2260.3 KB (+0.3 KB, +0.01%) 581.2 KB (+0.1 KB, +0.01%) 452.1 KB (+0.0 KB, +0.00%)

@mvaligursky mvaligursky merged commit ad7f74b into main Jun 26, 2026
10 checks passed
@mvaligursky mvaligursky deleted the mv-gsplat-restore-workbuffer branch June 26, 2026 09:50
mvaligursky added a commit that referenced this pull request Jun 26, 2026
… restore (#8973)

After a graphics context restore the unified-gsplat work buffer render target
comes back blank: its contents are GPU-rendered, not re-uploaded from CPU like
source textures, and nothing re-triggered the rebuild. A non-streaming SOG/PLY
splat therefore disappeared after a context loss/restore.

Add a `devicerestored` handler on GSplatManager that forces a full work-buffer
rebuild and re-sort. It is skipped when the world has streaming octree instances:
those destroy and asynchronously reload their source resources from URL via their
own device-lost handling, so rebuilding here would render from textures that have
been destroyed (and not yet reloaded).

Co-authored-by: Martin Valigursky <mvaligursky@snapchat.com>
Co-authored-by: Claude Opus 4.8 <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.

1 participant