Skip to content

Fix webgpu vtktexture cache invalidation - #3557

Merged
finetjul merged 3 commits into
Kitware:masterfrom
PaulHax:webgpu-vtktexture-cache-invalidation
Aug 10, 2026
Merged

Fix webgpu vtktexture cache invalidation#3557
finetjul merged 3 commits into
Kitware:masterfrom
PaulHax:webgpu-vtktexture-cache-invalidation

Conversation

@PaulHax

@PaulHax PaulHax commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

Context

PR #3547 (e072af2) fixed the stale-texture cache for getTextureForImageData: the shared _fillRequest helper derives the cache hash from the scalar array mtime alone, so writing scalars in place and signaling with imageData.modified() kept returning the stale GPU texture.

The sibling entry point getTextureForVTKTexture has the identical clobber: it seeds the hash with the vtkTexture mtime, but for imageData-backed textures _fillRequest overwrites it. Streaming new pixels into a texture draped over geometry (actor.addTexture(...)), a background, or a skybox silently keeps rendering the old image unless scalars.modified() is called directly — the OpenGL backend refreshes in all of these cases.

Results

  • imageData-backed vtkTextures refresh when scalars are updated in place and the change is signaled through imageData.modified() or texture.modified() alone (before: the WebGPU view kept the stale texture until scalars.modified(); WebGL updated).
  • Non-imageData texture sources (image, jsImageData, imageBitmap, canvas) are unaffected — their hash already used the texture mtime.
  • Failing texture-cache assertions now report their message instead of a stack overflow: expect(...).toBe(vtkTexture) made the reporter serialize the circular device–texture graph through toJSON()/getState() until RangeError: Maximum call stack size exceeded masked the actual failure.
  • Device-level WebGPU tests no longer need try/finally cleanup: createWebGPUTestDevice destroys its device when the test finishes, pass or fail.

Changes

  • WebGPU/TextureManager: getTextureForVTKTexture hashes 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: createWebGPUTestDevice registers onTestFinished(() => 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

  • semantic-release commit messages
  • Run npm run reformat to have correctly formatted code

Testing

WEBGPU=1 npx vitest run \
  Sources/Rendering/WebGPU/TextureManager \
  Sources/Rendering/WebGPU/Texture/test/testWriteSubImageData.js

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 unless WEBGPU=1 is set, so CI does not exercise them yet.

  • This change adds or fixes unit tests
  • Tested environment:
    • vtk.js: master (ca498ee)
    • OS: Linux (WSL2 Ubuntu 24.04)
    • Browser: Chromium (Playwright, headless)

@PaulHax PaulHax changed the title webgpu vtktexture cache invalidation Fix webgpu vtktexture cache invalidation Jul 16, 2026
@PaulHax
PaulHax force-pushed the webgpu-vtktexture-cache-invalidation branch from a458cfa to 73a0026 Compare August 8, 2026 16:15

@finetjul finetjul left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
PaulHax force-pushed the webgpu-vtktexture-cache-invalidation branch from 73a0026 to e756623 Compare August 10, 2026 11:32

@finetjul finetjul left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

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
PaulHax force-pushed the webgpu-vtktexture-cache-invalidation branch from e756623 to 2162b84 Compare August 10, 2026 11:55
@PaulHax

PaulHax commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator Author

FYI You can avoid toJSON()/getState() circular device-texture graph by using "protected" model member variables.

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().

@finetjul
finetjul added this pull request to the merge queue Aug 10, 2026
Merged via the queue into Kitware:master with commit 753459e Aug 10, 2026
3 checks passed
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 36.6.2 🎉

The release is available on:

Your semantic-release bot 📦🚀

@github-actions github-actions Bot added the released Automated label label Aug 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

released Automated label

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants