Fix webgpu vtktexture cache invalidation - #3557
Merged
finetjul merged 3 commits intoAug 10, 2026
Merged
Conversation
PaulHax
force-pushed
the
webgpu-vtktexture-cache-invalidation
branch
from
August 8, 2026 16:15
a458cfa to
73a0026
Compare
finetjul
approved these changes
Aug 10, 2026
finetjul
left a comment
Member
There was a problem hiding this comment.
LGTM
FYI You can avoid toJSON()/getState() circular device-texture graph by using "protected" model member variables. To do so, you need to prefix their names with '_' or use macros.makeProtected().
createWebGPUTestDevice now registers an onTestFinished callback that destroys the device handle whether the test passes or fails, so callers no longer need try/finally cleanup. Destroying the device also destroys every resource created from it, covering the per-texture destroy calls.
…geData mtimes getTextureForVTKTexture seeds the cache hash with the vtkTexture mtime, but for imageData-backed textures _fillRequest overwrites it with the scalar array mtime alone, so updating scalars in place and signaling through imageData.modified() or srcTexture.modified() kept returning the stale texture. Hash on the newest of the texture, imageData, and scalar array mtimes, mirroring the getTextureForImageData fix (e072af2).
PaulHax
force-pushed
the
webgpu-vtktexture-cache-invalidation
branch
from
August 10, 2026 11:32
73a0026 to
e756623
Compare
model.device is a back reference to the device that owns the texture, so serializing a texture walked device -> textureManager -> device until the stack overflowed: getState() and JSON.stringify() both threw RangeError. Prefix it with '_' so getState() skips it, matching how vtkOpenGLTexture holds _openGLRenderWindow. getDevice()/setDevice() keep their names because _capitalize() strips the leading underscore, so nothing outside the class changes.
PaulHax
force-pushed
the
webgpu-vtktexture-cache-invalidation
branch
from
August 10, 2026 11:55
e756623 to
2162b84
Compare
Collaborator
Author
Thanks, vtkWebGPUTexture holds _device now, so getState() skips it and the cycle never forms; getDevice()/setDevice() are unchanged. Rewrote the branch to drop my boolean-comparison workaround, so the tests are back to plain toBe(). |
|
🎉 This PR is included in version 36.6.2 🎉 The release is available on: Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Context
PR #3547 (e072af2) fixed the stale-texture cache for
getTextureForImageData: the shared_fillRequesthelper derives the cache hash from the scalar array mtime alone, so writing scalars in place and signaling withimageData.modified()kept returning the stale GPU texture.The sibling entry point
getTextureForVTKTexturehas the identical clobber: it seeds the hash with thevtkTexturemtime, but for imageData-backed textures_fillRequestoverwrites it. Streaming new pixels into a texture draped over geometry (actor.addTexture(...)), a background, or a skybox silently keeps rendering the old image unlessscalars.modified()is called directly — the OpenGL backend refreshes in all of these cases.Results
vtkTextures refresh when scalars are updated in place and the change is signaled throughimageData.modified()ortexture.modified()alone (before: the WebGPU view kept the stale texture untilscalars.modified(); WebGL updated).expect(...).toBe(vtkTexture)made the reporter serialize the circular device–texture graph throughtoJSON()/getState()untilRangeError: Maximum call stack size exceededmasked the actual failure.try/finallycleanup:createWebGPUTestDevicedestroys its device when the test finishes, pass or fail.Changes
WebGPU/TextureManager:getTextureForVTKTexturehashes cache entries on the newest of the texture, imageData, and scalar array mtimes, mirroring e072af2.New regression test
TextureManager/test/testVTKTextureCacheInvalidation.js.TextureManager/test/testImageDataTextureCacheInvalidation.js: compare cached texture identities as booleans so assertion failures stay readable.Testing/testUtils.js:createWebGPUTestDeviceregistersonTestFinished(() => handle.destroy()); removed per-test device cleanup from the device-level tests (destroying a device destroys every resource created from it).No public API changes.
Documentation and TypeScript definitions were updated to match those changes
PR and Code Checklist
npm run reformatto have correctly formatted codeTesting
The new regression test fails without the fix (
imageData.modified() alone must invalidate the cached texture: expected true to be false). Note the WebGPU tests are skipped unlessWEBGPU=1is set, so CI does not exercise them yet.