Shared WebGL context render window#3453
Conversation
93f4a3a to
5e23b91
Compare
|
@sedghi do you have an opnion on this ? |
ca02f1f to
8ed472d
Compare
There was a problem hiding this comment.
Pull request overview
This PR adds a shared-WebGL2-context rendering path to vtk.js, enabling vtk.js to render into an externally owned WebGL2RenderingContext (e.g., MapLibre custom layers) rather than always creating/managing its own canvas/context.
Changes:
- Added
manageCanvas(defaulttrue) tovtkOpenGLRenderWindowto allow opting out of canvas resizing/styling when the canvas is externally owned. - Introduced
vtkSharedRenderWindow.createFromContext(canvas, gl)plusvtkSharedRendererto support shared-context rendering while keeping overrides local to the shared render-window instance. - Added SharedRenderWindow unit tests (GL state reset, host framebuffer rendering, host survival) and a MapLibre shared-context example.
Reviewed changes
Copilot reviewed 9 out of 13 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| Sources/Rendering/SceneGraph/ViewNodeFactory/index.js | Switches override lookup to support prototype-inherited mappings (in), enabling per-instance override chains. |
| Sources/Rendering/OpenGL/ViewNodeFactory/index.js | Makes OpenGL view-node mappings inheritable per factory instance via Object.create(CLASS_MAPPING) and adds instance-local registerOverride. |
| Sources/Rendering/OpenGL/RenderWindow/index.js | Adds manageCanvas gating for canvas size/display updates and blocks resize-based screenshot capture when canvas management is disabled. |
| Sources/Rendering/OpenGL/RenderWindow/index.d.ts | Exposes manageCanvas in initial values and adds getManageCanvas / setManageCanvas. |
| Sources/Rendering/OpenGL/SharedRenderWindow/index.js | New shared-context render window with GL-state reset, size sync from drawing buffer, and render-callback integration. |
| Sources/Rendering/OpenGL/SharedRenderWindow/index.d.ts | Type definitions for vtkSharedRenderWindow API (renderShared, callbacks, autoClear controls, createFromContext). |
| Sources/Rendering/OpenGL/SharedRenderer/index.js | New renderer variant that respects shared-window autoClear behavior and uses tiled viewport/scissor. |
| Sources/Rendering/OpenGL/SharedRenderer/index.d.ts | Type definitions for vtkSharedRenderer. |
| Sources/Rendering/OpenGL/SharedRenderWindow/test/testSharedRenderWindow.js | Baseline image test + validation that shared renderer override remains local + WebGL1 rejection + manageCanvas behavior. |
| Sources/Rendering/OpenGL/SharedRenderWindow/test/testSharedRenderWindowGLState.js | Tests that renderShared() resets GL state and renders into a currently bound host framebuffer. |
| Sources/Rendering/OpenGL/SharedRenderWindow/test/testSharedRenderWindowHostSurvival.js | Tests host rendering can resume after renderShared() when host state is rebound. |
| Examples/Rendering/SharedContext/index.js | MapLibre terrain example demonstrating shared-context depth-tested interleaving. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
6fd51df to
c19dc9c
Compare
Move registerOverride from extend() into the main function body for both ViewNodeFactory and SharedRenderWindow.
c19dc9c to
5d1f87a
Compare
There was a problem hiding this comment.
Shouldn't this be done only iff manageCanvas is true?
There was a problem hiding this comment.
Good catch. Added the manageCanvas guard to match the one at line 164.
|
|
||
| function getDefaultDrawBuffers(gl) { | ||
| const framebuffer = gl.getParameter(gl.FRAMEBUFFER_BINDING); | ||
| return framebuffer ? [gl.COLOR_ATTACHMENT0] : [gl.BACK]; |
There was a problem hiding this comment.
This is an assumption here. If the external application has bound multiple FBO attachments, assuming attachment 0 might be wrong.
There was a problem hiding this comment.
Updated to query DRAW_BUFFER0 to gl.MAX_DRAW_BUFFERS and respect the host's actual draw-buffer state, with [COLOR_ATTACHMENT0] as a fallback when every slot is NONE.
| gl.enable(gl.SCISSOR_TEST); | ||
| gl.scissor(ts.lowerLeftU, ts.lowerLeftV, ts.usize, ts.vsize); | ||
| gl.viewport(ts.lowerLeftU, ts.lowerLeftV, ts.usize, ts.vsize); | ||
| gl.enable(gl.DEPTH_TEST); |
There was a problem hiding this comment.
Shouldn't the state be saved and reset after VTK is done rendering?
There was a problem hiding this comment.
SharedRenderer is assuming a contract where host is responsible for rebinding its GL state after renderShared(). This is the same contract used by three.js (renderer.resetState()), Babylon (engine.wipeCaches()), and deck.gl/luma.gl. A full state save/restore would add a lot of getParameter calls per render and create a heavier contract.
There was a problem hiding this comment.
Not really. You only need to query and cache the state parameters that are being force-changed by vtk. This is what we do, for example in vtkExternalOpenGLRenderWindow that follows the same idea.
| model.classHierarchy.push('vtkSharedRenderer'); | ||
|
|
||
| publicAPI.clear = () => { | ||
| const gl = model.context; |
There was a problem hiding this comment.
Is it guaranteed that model.context will be valid always when we get here?
There was a problem hiding this comment.
Its guaranteed. Same assuption as parent vtkOpenGLRenderer.clear()
Gate the screenshot-path canvas display toggle on manageCanvas, matching the existing guard at line 164 so externally owned canvases are never restyled. In getDefaultDrawBuffers, query the host's actual DRAW_BUFFER0..N state when an FBO is bound rather than assuming COLOR_ATTACHMENT0. Falls back to [COLOR_ATTACHMENT0] when all slots are NONE so callers that left the default state untouched still get a valid render target.
Are folks intersted in putting this in the vtk.js repo? Its an uncommon use case. We could get away with simply doing something to keep
vtkOpenGLRenderWindowfrom resizing the canvas and put SharedRenderWindow in another repo/package.Context
This PR adds an OpenGL shared-context rendering path for vtk.js so it can render into a
WebGL2RenderingContextowned by another library, instead of always creating and managing its own canvas/context.The immediate use case is interleaving vtk.js content with host renderers such as MapLibre custom layers, including depth-tested rendering into the host scene. vtk.js also needs an explicit way to opt out of canvas DOM management when the canvas is externally owned.
Results
Before this change:
vtkOpenGLRenderWindowassumed ownership of the canvas and could resize/restyle it.After this change:
WebGL2context throughvtkSharedRenderWindow.createFromContext(canvas, gl).Changes
Added
manageCanvastovtkOpenGLRenderWindow(defaulttrue) so externally owned canvases can opt out of vtk.js canvas sizing/styling.Added
vtkSharedRenderWindowfor rendering into an externally providedWebGL2RenderingContext.Added
vtkSharedRendererfor the shared-context rendering path.Scoped the vtkSharedRenderer override to the shared render-window instance rather than changing the global OpenGL view-node factory. This avoids leaking shared-context behavior into unrelated normal OpenGL render windows.
Added a MapLibre example showing shared-context rendering on terrain.
Documentation and TypeScript definitions were updated to match those changes
PR and Code Checklist
npm run reformatto have correctly formatted codeTesting
npm run example -- SharedContext