Skip to content

fix(OpenGLHelper): restore empty program cleared on shader failure - #3677

Open
cpruijsen wants to merge 3 commits into
Kitware:masterfrom
cpruijsen:fix/issue-3652-bbeed927
Open

cpruijsen wants to merge 3 commits into
Kitware:masterfrom
cpruijsen:fix/issue-3652-bbeed927

Conversation

@cpruijsen

Copy link
Copy Markdown

Context

Fixes #3652. vtkOpenGLHelper.setOpenGLRenderWindow now restores an empty vtkShaderProgram when a mapper has cleared the program, so a failed volume shader no longer crashes the next render pass.

When a volume shader fails to compile or bind, vtkOpenGLVolumeMapper.updateShaders calls cellBO.setProgram(null) and skips the draw. On the next pass, volumePass calls model.tris.setOpenGLRenderWindow(...), which dereferences the null program:

TypeError: Cannot read properties of null (reading 'setContext')
    at Object.setOpenGLRenderWindow
    at t.volumePass
    at Object.traverse
    at t.traverseVolumePass
    at Object.traverseAllPasses

Results

Before: after a shader failure, the next render pass throws and the viewport stays blank.

After: the empty program has handle 0, so the cellBO.getProgram()?.getHandle() === 0 check in getNeedToRebuildShaders triggers a rebuild on the next pass. If the shader compiles, the cached program replaces the empty one. If it fails again, the mapper skips the draw as before, without the exception. A program that was not cleared is kept.

I put the guard in the helper, as the issue suggests. A null check (model.program?.setContext) would leave the program null, and since undefined === 0 is false, the mapper would never rebuild. Setting an empty program at the two clear sites in VolumeMapper would fix only that mapper. The helper guard also covers the image mappers and Helper.updateShaders, which can store a null program after a failed compile.

The empty program does not go into ShaderCache.shaderPrograms and gets no GL handle (cleanup() does nothing when the handle is 0), so cache bookkeeping does not change.

Changes

  • Sources/Rendering/OpenGL/Helper/index.js: setOpenGLRenderWindow creates an empty vtkShaderProgram when model.program is null, before it calls setContext. No API change.
  • Sources/Rendering/OpenGL/Helper/test/testNullProgramRecovery.js: new regression test. One case replays the clear-then-next-pass sequence and asserts no throw, and that the empty program has handle 0, is not compiled and has the context. The other asserts that an existing program is not replaced.

Out of scope: Helper.updateShaders can still use a null program in the same frame after a failed compile. That is a separate crash path for the other mappers, and the issue's notes exclude it.

PR Checklist

  • GitHub Actions CI passed: semantic-release commit messages, lint, and tests
  • Test coverage added
  • Documentation and TypeScript definitions are updated to match these changes (no public API change)

@PaulHax

PaulHax commented Oct 17, 2026

Copy link
Copy Markdown
Collaborator

Good stuff! The helper-level guard looks appropriate for #3652.

Would you be interested in going a little further to clean up failed program links before retrying?

ShaderProgram.link() currently clears the program handle without deleting the failed WebGL program or resetting its recorded shader attachment handles.

The next retry creates another program and tries to detach those old shaders from it.

I reproduced INVALID_OPERATION during this retry using actual shader link failures. Deleting the failed program and resetting vertexShaderHandle and fragmentShaderHandle removed that error.

I’ve been looking at this area recently, and that cleanup is implemented in b4516233, part of an unmerged draft of mine. It could serve as a starting point if useful.

A failed link cleared the program handle without deleting the program or
resetting the recorded shader attachment handles. The shader cache keeps
the failed program, so the next build creates a new program and detaches
the old shaders from it, which raises INVALID_OPERATION.

link now deletes the failed program, which detaches its shaders, and
clears the attachment handles so a retry starts clean.

Co-authored-by: Paul Elliott <paul@vizworkshop.com>
@cpruijsen

Copy link
Copy Markdown
Author

I ported the link-failure cleanup from b451623 into this PR, with you as co-author. link() now deletes the failed program and resets the vertex and fragment shader handles. I added a mocked-GL test that fails without the cleanup, because without it the retry detaches the old shaders from the new program.

@PaulHax PaulHax left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

👍 Hoping this fixes some active errors i'm seeing in VolView.

The CI falure is unrelated to this work and is addressed here #3686

This branch has not been deployed

No deployments
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.

vtkOpenGLHelper.setOpenGLRenderWindow throws on a null program after VolumeMapper clears it on shader failure

2 participants