Skip to content

feat(webgpu): experimental WebGPU render backend for GenericViewport stacks and MPR - #2796

Open
sedghi wants to merge 11 commits into
mainfrom
cornerstoneWebGPUCornerstone
Open

feat(webgpu): experimental WebGPU render backend for GenericViewport stacks and MPR#2796
sedghi wants to merge 11 commits into
mainfrom
cornerstoneWebGPUCornerstone

Conversation

@sedghi

@sedghi sedghi commented Jul 10, 2026

Copy link
Copy Markdown
Member

Context

Groundwork for evaluating WebGPU as the rendering backend: an opt-in webgpu render backend for planar GenericViewports covering image stacks and volume-slice (MPR) rendering through the vtk.js WebGPU view API.

Depends on vtk.js fixes: Kitware/vtk-js#3547 (ImageMapper projection + imageData texture invalidation) and Kitware/vtk-js#3548 (translucent-pass depth for coplanar overlays, needed for labelmap segmentation). Both merged upstream and shipped in vtk.js v36.4.2; this branch now pins @kitware/vtk.js 36.4.2, so no linked build is required.

Changes & Results

  • registerWebGPURenderBackend() (opt-in, throws without navigator.gpu): registers the webgpu backend with self-rendering image (webgpuImage) and volume-slice (webgpuVolume) render paths; each viewport gets a ref-counted vtk.js WebGPU render window whose frames are blitted into the cpu surface canvas after the device work completes. Volume rendering materializes one shared, ref-counted mapper-input scalar array per volume (cornerstone volumes are image-backed and own no contiguous array).
  • Planar shared code and the labelmap display resolve render-mode kind/surface through the render-backend registry instead of hardcoded ActorRenderMode literals (image and volume kinds), so extension render modes participate in view resolution, view references, scrolling, labelmap styling, and the labelmap image-mapper plan. isImageRenderMode / isVolumeRenderMode are exported from core. The addImages overlay gate keys on the CanvasActor cpuVolume mode rather than the composited surface.
  • Perf: brush setValue resolves labelmap bindings once per stroke instead of per voxel; labelmap slice extraction walks flat voxel indices incrementally (original per-voxel IJK loop kept as fallback; obliques are unaffected — axis-aligned extraction only) and reuses slice buffers.
  • New examples: genericWebGPUStack (stack + tools + annotations + segmentation editing on a fake labelmap) and genericWebGPUOrtho (sagittal/coronal CT MPR + mock ellipsoid labelmap + tools), both with live webgpu/gpu/cpu switching, a ?renderBackend= URL parameter, and a debug panel reporting the effective backend, mounted render mode, and WebGPU adapter. New Generic Viewports section in the example index.

Verified on the webgpu backend: stack and MPR rendering with pixel parity to WebGL, wheel scroll with exact pixel round-trip, zoom/pan/VOI/invert/rotation, annotation tools, labelmap segmentation rendering and editing with segment colors matching the gpu and cpu backends, and live backend switching without errors. JS heap holds a single shared volume copy regardless of viewport count (verified via heap snapshots).

Testing

  • vtk.js 36.4.2 (pinned) contains the fixes above; a plain pnpm install is enough, no linked build needed.
  • Run the genericWebGPUStack and genericWebGPUOrtho examples in Chrome; the debug panels should show webgpuImage / webgpuVolume and the WebGPU adapter. Use the backend buttons (or ?renderBackend=gpu|cpu) to compare against gpu/cpu.

Checklist

PR

  • My Pull Request title is descriptive, accurate and follows the
    semantic-release format and guidelines.

Code

  • My code has been well-documented (function documentation, inline comments,
    etc.)

Public Documentation Updates

  • The documentation page has been updated as necessary for any public API
    additions or removals. (Experimental opt-in API; not documented on the docs site yet by design.)

Tested Environment

  • "OS: macOS 15"
  • "Node version: 22.22.2"
  • "Browser: Chrome (WebGPU, Apple Metal adapter)"

Summary by CodeRabbit

  • New Features
    • Added experimental WebGPU render paths for planar image stacks and WebGPU volume-slice (MPR) viewports, including per-viewport WebGPU window management and backend availability/debug controls.
    • Added interactive WebGPU demo examples (generic stack/orthographic MPR, PET MIP) with live render-backend switching and debug overlays/toolbars.
  • Performance
    • Reduced per-slice allocations via cached slice pixel buffers and faster voxel extraction.
    • Improved segmentation/labelmap interaction by caching per-stroke lookups.
  • Compatibility / Bug Fixes
    • Broadened image/volume render-mode detection across planar and labelmap/segmentation workflows.
    • Fixed CPU-surface sizing/guarding, improved view-reference rescaling on reorientation, and tightened 3D clipping behavior.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Claude Code Review

Claude Code Review is paused for this repository. To reconnect it, an admin of this repository's GitHub organization (or the account owner, for personal repositories) who can also manage your Claude organization's Code Review settings needs to re-link GitHub in Code Review settings. This is a one-time step.

Tip: disable this comment in your organization's Code Review settings.

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds WebGPU planar image-stack and volume-slice rendering with shared viewport windows, backend registration, registry-aware routing, segmentation integration, interactive examples, diagnostics, and Generic Viewports catalog entries.

Changes

WebGPU planar rendering

Layer / File(s) Summary
WebGPU viewport window lifecycle
packages/core/src/RenderingEngine/GenericViewport/Planar/webgpuViewportRenderWindow.ts, packages/core/src/RenderingEngine/GenericViewport/Planar/vtkWebGPUModules.d.ts
Adds WebGPU module declarations, shared per-viewport windows, asynchronous rendering, canvas blitting, teardown, background updates, and diagnostics.
WebGPU image and volume render paths
packages/core/src/RenderingEngine/GenericViewport/Planar/WebGPU*RenderPath.ts
Adds WebGPU image-stack and volume-slice paths with VTK actors, image/voxel updates, projection synchronization, presentation handling, shared data caching, and cleanup.
Backend registration and planar routing
packages/core/src/RenderingEngine/helpers/registerWebGPURenderBackend.ts, packages/core/src/types/RenderBackendRegistry.ts, packages/core/src/RenderingEngine/GenericViewport/Planar/*, packages/core/src/index.ts
Registers WebGPU image and volume paths, exports backend helpers, adds WebGPU registry types, and replaces fixed actor-mode checks with image and volume predicates.
Segmentation integration and extraction
packages/tools/src/stateManagement/segmentation/helpers/*, packages/tools/src/tools/displayTools/Labelmap/*, packages/tools/src/tools/segmentation/strategies/compositions/setValue.ts
Updates labelmap compatibility for registered volume modes, reuses slice buffers, adds indexed voxel extraction, and caches segmentation layer resolution during strokes.
Interactive examples and catalog
packages/core/examples/genericWebGPUStack/index.ts, packages/core/examples/genericWebGPUOrtho/index.ts, packages/core/examples/genericWebGPUPetMIP/index.ts, utils/ExampleRunner/example-info.json
Adds WebGPU stack, orthographic, and PET MIP examples with backend switching, tools, segmentation overlays, debug panels, and Generic Viewports catalog entries.
3D volume camera adjustment
packages/core/src/RenderingEngine/GenericViewport/Volume3D/VtkVolume3DRenderPath.ts
Resets the camera clipping range after applying the initial range.

Estimated code review effort: 4 (Complex) | ~75 minutes

Possibly related issues

Possibly related PRs

Suggested reviewers: wayfarer3130

Sequence Diagram(s)

sequenceDiagram
  participant Example
  participant RenderingEngine
  participant WebGPURenderPath
  participant WebGPUViewportWindow
  participant Canvas
  Example->>RenderingEngine: register and select WebGPU backend
  RenderingEngine->>WebGPURenderPath: create image or volume path
  WebGPURenderPath->>WebGPUViewportWindow: acquire window and render
  WebGPUViewportWindow->>Canvas: blit completed WebGPU output
  WebGPURenderPath->>RenderingEngine: emit image-rendered state
Loading
🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (3 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning This PR adds a WebGPU backend and examples, but it does not implement the vtk.js fixes requested in #3547. Add the ImageMapper projection and TextureManager invalidation fixes, plus the headless WebGPU test config, or unlink #3547 from this PR.
Out of Scope Changes check ⚠️ Warning Most of the PR is broader Cornerstone WebGPU backend and example work that is unrelated to the single linked vtk.js fix issue. Split the unrelated backend, example, and API changes into separate PRs or link matching issues, and keep this PR focused on #3547.
Docstring Coverage ⚠️ Warning Docstring coverage is 39.53% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: an experimental WebGPU render backend for GenericViewport stacks and MPR. It uses the required semantic-release format and is concise and specific.
Description check ✅ Passed The description includes all required sections: context, changes and results, testing, checklist, and tested environment. It provides implementation details, dependencies, verification results, and te…
Full details: Description check

Explanation

The description includes all required sections: context, changes and results, testing, checklist, and tested environment. It provides implementation details, dependencies, verification results, and test instructions. The documentation checkbox includes an explicit explanation for the experimental API status.

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cornerstoneWebGPUCornerstone

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
packages/core/src/RenderingEngine/GenericViewport/Planar/PlanarResolvedView.ts (1)

426-432: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add currentImage to the shared image-rendering shape

PlanarRendering is a closed union, but this branch also handles extension image modes like webgpuImage, and those states already carry currentImage. Move that field onto the shared image-rendering type instead of casting here so the branch stays type-safe.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/core/src/RenderingEngine/GenericViewport/Planar/PlanarResolvedView.ts`
around lines 426 - 432, Update the shared image-rendering type used by the
closed PlanarRendering union to include the optional currentImage field, then
simplify the image selection in the isImageRenderMode branch of
PlanarResolvedView by accessing rendering.currentImage directly and removing the
local cast. Ensure all image-mode variants, including extension modes, satisfy
the updated type.
packages/core/src/RenderingEngine/GenericViewport/Planar/PlanarViewReferenceController.ts (1)

618-621: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider using isVolumeRenderMode for consistency with the registry-aware approach.

resolveVolumeReferenceImageIdIndex still checks rendering.renderMode !== ActorRenderMode.CPU_VOLUME && rendering.renderMode !== ActorRenderMode.VTK_VOLUME_SLICE explicitly. While correct for current core volume modes, this won't recognize future registered volume backends (e.g., a hypothetical WebGPU volume mode). getViewportLabelmapRenderMode.ts in this same PR already uses isVolumeRenderMode for this purpose — aligning here would keep the pattern consistent.

This is deferred-friendly since the WebGPU backend is image-only in this PR.

♻️ Optional refactor for consistency
   if (
-    rendering.renderMode !== ActorRenderMode.CPU_VOLUME &&
-    rendering.renderMode !== ActorRenderMode.VTK_VOLUME_SLICE
+    !isVolumeRenderMode(rendering.renderMode)
   ) {
     return;
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/core/src/RenderingEngine/GenericViewport/Planar/PlanarViewReferenceController.ts`
around lines 618 - 621, Replace the explicit CPU_VOLUME and VTK_VOLUME_SLICE
checks in resolveVolumeReferenceImageIdIndex with the registry-aware
isVolumeRenderMode helper, matching getViewportLabelmapRenderMode.ts. Preserve
the existing non-volume control flow while allowing future registered volume
render modes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@packages/core/src/RenderingEngine/GenericViewport/Planar/WebGPUImageMapperRenderPath.ts`:
- Around line 124-168: Ensure addData() releases the acquired WebGPU window when
setup fails. Wrap the setup from acquireWebGPUViewportWindow through
actor/rendering initialization in try/catch, call
releaseWebGPUViewportWindow(ctx.viewportId) in the catch before rethrowing, and
preserve normal ownership for successful setup and removeData().

---

Nitpick comments:
In
`@packages/core/src/RenderingEngine/GenericViewport/Planar/PlanarResolvedView.ts`:
- Around line 426-432: Update the shared image-rendering type used by the closed
PlanarRendering union to include the optional currentImage field, then simplify
the image selection in the isImageRenderMode branch of PlanarResolvedView by
accessing rendering.currentImage directly and removing the local cast. Ensure
all image-mode variants, including extension modes, satisfy the updated type.

In
`@packages/core/src/RenderingEngine/GenericViewport/Planar/PlanarViewReferenceController.ts`:
- Around line 618-621: Replace the explicit CPU_VOLUME and VTK_VOLUME_SLICE
checks in resolveVolumeReferenceImageIdIndex with the registry-aware
isVolumeRenderMode helper, matching getViewportLabelmapRenderMode.ts. Preserve
the existing non-volume control flow while allowing future registered volume
render modes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 95647821-8d48-4f8a-a177-32ac42f64f66

📥 Commits

Reviewing files that changed from the base of the PR and between 0b67d01 and 3049a2d.

📒 Files selected for processing (14)
  • packages/core/examples/genericWebGPUStack/index.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/PlanarResolvedView.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/PlanarViewReferenceController.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/WebGPUImageMapperRenderPath.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/planarRenderPathProjection.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/planarViewReference.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/vtkWebGPUModules.d.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/webgpuViewportRenderWindow.ts
  • packages/core/src/RenderingEngine/helpers/registerWebGPURenderBackend.ts
  • packages/core/src/index.ts
  • packages/core/src/types/RenderBackendRegistry.ts
  • packages/tools/src/stateManagement/segmentation/helpers/getViewportLabelmapRenderMode.ts
  • packages/tools/src/tools/displayTools/Labelmap/labelmapActorStyle.ts
  • utils/ExampleRunner/example-info.json

Comment on lines +124 to +168
const window = acquireWebGPUViewportWindow(ctx.viewportId);
this.window = window;

// 16-bit handling: the vtk.js WebGPU texture manager uploads non-8-bit
// scalar types (Int16/Uint16/Float) as r16float (half-float). There is no
// norm16 equivalent in WebGPU, so precision matches the WebGL
// no-EXT_texture_norm16 fallback path.
const mapper = vtkImageMapper.newInstance();
const actor = vtkImageSlice.newInstance();
const imageData =
payload.imageData ?? createVTKImageDataFromImage(payload.image);

ctx.display.activateRenderMode(WEBGPU_IMAGE_RENDER_MODE);
mapper.setInputData(imageData);
actor.setMapper(mapper);
// Multi-component images (e.g. RGB ultrasound) must render as direct
// color; see VtkImageMapperRenderPath.
if (imageData.getPointData().getScalars().getNumberOfComponents() > 1) {
actor.getProperty().setIndependentComponents(false);
}
window.renderer.addActor(actor);

const rendering: PlanarWebGPUImageRendering = {
renderMode: WEBGPU_IMAGE_RENDER_MODE,
actorEntryUID: uuidv4(),
actor,
currentImage: payload.image,
mapper,
imageData,
useWorldCoordinateImageData: payload.useWorldCoordinateImageData,
currentImageIdIndex: payload.initialImageIdIndex ?? 0,
defaultVOIRange: getDefaultImageVOIRange(payload.image),
dataPresentation: undefined,
loadRequestId: 0,
};

// Only the source binding drives the slice scrollbar; see
// VtkImageMapperRenderPath.
if (options.role !== 'overlay') {
triggerPlanarNewImage(ctx, {
image: payload.image,
imageIdIndex: payload.initialImageIdIndex ?? 0,
});
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- outline target file ---'
ast-grep outline packages/core/src/RenderingEngine/GenericViewport/Planar/WebGPUImageMapperRenderPath.ts --view expanded || true

echo '--- relevant slice ---'
sed -n '1,260p' packages/core/src/RenderingEngine/GenericViewport/Planar/WebGPUImageMapperRenderPath.ts

echo '--- search acquire/release helpers ---'
rg -n "acquireWebGPUViewportWindow|releaseWebGPUViewportWindow" packages/core/src -S

echo '--- search for try/catch or cleanup in webgpu image mapper path ---'
rg -n "removeData|releaseWebGPUViewportWindow|acquireWebGPUViewportWindow|WebGPUImageMapperRenderPath" packages/core/src/RenderingEngine/GenericViewport/Planar -S

Repository: cornerstonejs/cornerstone3D

Length of output: 15614


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- webgpuViewportRenderWindow.ts ---'
sed -n '1,220p' packages/core/src/RenderingEngine/GenericViewport/Planar/webgpuViewportRenderWindow.ts

echo '--- WebGPUImageMapperRenderPath removeData slice ---'
sed -n '410,460p' packages/core/src/RenderingEngine/GenericViewport/Planar/WebGPUImageMapperRenderPath.ts

echo '--- inspect createVTKImageDataFromImage references for throw-prone code ---'
rg -n "function createVTKImageDataFromImage|createVTKImageDataFromImage\\(" packages/core/src/RenderingEngine -S

Repository: cornerstonejs/cornerstone3D

Length of output: 9349


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- addData call sites in rendering engine ---'
rg -n "\.addData\(" packages/core/src/RenderingEngine -S

echo '--- RenderPathAttachment / mount flow ---'
sed -n '1,260p' packages/core/src/RenderingEngine/GenericViewport/Planar/PlanarMountedData.ts

echo '--- viewport / render path orchestration around binding add ---'
sed -n '1,260p' packages/core/src/RenderingEngine/GenericViewport/Planar/PlanarViewport.ts

echo '--- search for try/catch around addData consumers ---'
rg -n "try \\{|catch \\(|finally \\{|addData\\(" packages/core/src/RenderingEngine/GenericViewport -S

Repository: cornerstonejs/cornerstone3D

Length of output: 18883


Release the WebGPU window on setup failure.
addData() acquires the per-viewport WebGPU window before scene setup completes. If createVTKImageDataFromImage, scalar access, or actor wiring throws, removeData() is never returned, so the refcounted window is left alive. Wrap this block in try/catch and call releaseWebGPUViewportWindow(ctx.viewportId) on error.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/core/src/RenderingEngine/GenericViewport/Planar/WebGPUImageMapperRenderPath.ts`
around lines 124 - 168, Ensure addData() releases the acquired WebGPU window
when setup fails. Wrap the setup from acquireWebGPUViewportWindow through
actor/rendering initialization in try/catch, call
releaseWebGPUViewportWindow(ctx.viewportId) in the catch before rethrowing, and
preserve normal ownership for successful setup and removeData().

@daker

daker commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Nice 👍🏼

@sedghi sedghi changed the title feat(webgpu): experimental WebGPU render backend for GenericViewport image stacks feat(webgpu): experimental WebGPU render backend for GenericViewport stacks and MPR Jul 10, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/core/src/RenderingEngine/GenericViewport/Planar/planarViewReference.ts (1)

265-286: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use isVolumeRenderMode here so webgpuVolume gets a volume view-reference ID
getPlanarViewReferenceId still hard-codes ActorRenderMode.CPU_VOLUME / VTK_VOLUME_SLICE. webgpuVolume is a volume mode elsewhere in this path, so it falls through to the imageId: fallback instead of getVolumeViewReferenceId(...).

🐛 Proposed fix
-  if (
-    rendering.renderMode === ActorRenderMode.CPU_VOLUME ||
-    rendering.renderMode === ActorRenderMode.VTK_VOLUME_SLICE
-  ) {
+  if (isVolumeRenderMode(rendering.renderMode)) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/core/src/RenderingEngine/GenericViewport/Planar/planarViewReference.ts`
around lines 265 - 286, Update getPlanarViewReferenceId to use the existing
isVolumeRenderMode helper instead of checking only ActorRenderMode.CPU_VOLUME
and ActorRenderMode.VTK_VOLUME_SLICE, ensuring webgpuVolume and all supported
volume modes enter the getVolumeViewReferenceId path.
🧹 Nitpick comments (1)
packages/core/examples/genericWebGPUOrtho/index.ts (1)

335-347: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Consider adding error handling for the discarded scroll promise.

void viewport.scroll(delta).then(...) silently swallows rejections. In an example this is low-impact, but adding a .catch() would surface errors during debugging.

♻️ Optional improvement
-    void viewport.scroll(delta).then(() => viewport.render());
+    viewport.scroll(delta).then(() => viewport.render()).catch(console.error);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/examples/genericWebGPUOrtho/index.ts` around lines 335 - 347,
Add rejection handling to the promise chain in scrollViewports so failures from
viewport.scroll(delta) or viewport.render() are surfaced during debugging;
append a catch handler that logs the error with clear context while preserving
the existing scroll-then-render behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In
`@packages/core/src/RenderingEngine/GenericViewport/Planar/planarViewReference.ts`:
- Around line 265-286: Update getPlanarViewReferenceId to use the existing
isVolumeRenderMode helper instead of checking only ActorRenderMode.CPU_VOLUME
and ActorRenderMode.VTK_VOLUME_SLICE, ensuring webgpuVolume and all supported
volume modes enter the getVolumeViewReferenceId path.

---

Nitpick comments:
In `@packages/core/examples/genericWebGPUOrtho/index.ts`:
- Around line 335-347: Add rejection handling to the promise chain in
scrollViewports so failures from viewport.scroll(delta) or viewport.render() are
surfaced during debugging; append a catch handler that logs the error with clear
context while preserving the existing scroll-then-render behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5565ce39-5251-4e74-ac70-4bfb54f44b5f

📥 Commits

Reviewing files that changed from the base of the PR and between 3049a2d and 69ee478.

📒 Files selected for processing (19)
  • packages/core/examples/genericWebGPUOrtho/index.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/DefaultPlanarDataProvider.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/PlanarLegacyCompatibilityController.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/PlanarResolvedView.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/PlanarViewReferenceController.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/PlanarViewport.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/PlanarViewportLegacyAdapter.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/WebGPUImageMapperRenderPath.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/WebGPUVolumeSliceRenderPath.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/planarViewReference.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/webgpuViewportRenderWindow.ts
  • packages/core/src/RenderingEngine/helpers/registerWebGPURenderBackend.ts
  • packages/core/src/index.ts
  • packages/tools/src/stateManagement/segmentation/helpers/labelmapImageMapperSupport.ts
  • packages/tools/src/tools/displayTools/Labelmap/labelmapRenderPlan/planarGenericVolumeLabelmap.ts
  • packages/tools/src/tools/displayTools/Labelmap/labelmapRenderPlan/volumeSliceImageMapperPlan.ts
  • packages/tools/src/tools/displayTools/Labelmap/volumeLabelmapSliceData.ts
  • packages/tools/src/tools/segmentation/strategies/compositions/setValue.ts
  • utils/ExampleRunner/example-info.json
✅ Files skipped from review due to trivial changes (1)
  • utils/ExampleRunner/example-info.json
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/core/src/index.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/WebGPUImageMapperRenderPath.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/webgpuViewportRenderWindow.ts

@sedghi sedghi changed the title feat(webgpu): experimental WebGPU render backend for GenericViewport stacks and MPR BLOKCED BY VTK MERGE DO NOT MERGE feat(webgpu): experimental WebGPU render backend for GenericViewport stacks and MPR Jul 10, 2026
sedghi added 8 commits July 20, 2026 12:40
…image stacks

Adds an opt-in 'webgpu' render backend for planar GenericViewports,
rendering image stacks through the vtk.js WebGPU view API via a
self-rendering render path with a per-viewport WebGPU render window
blitted into the cpu surface canvas.

Shared planar code and the labelmap display now resolve render-mode
kind/surface through the render backend registry instead of hardcoded
ActorRenderMode literals, so extension render modes participate in view
resolution, view references, slice scrolling and labelmap styling.
isImageRenderMode/isVolumeRenderMode are exported from core.

Includes the genericWebGPUStack example: live backend switching
(webgpu/gpu/cpu), manipulation and annotation tools, segmentation
editing tools on a fake labelmap, and a debug panel reporting the
effective backend and WebGPU adapter.
Lists all GenericViewport (next architecture) examples, including the
new genericWebGPUStack WebGPU render backend example.
Adds the 'webgpuVolume' render mode: a volume-slice render path using the
stock vtkImageResliceMapper through the per-viewport WebGPU window, with a
shared reference-counted mapper-input imageData per volume (cornerstone
volumes are image-backed and own no contiguous scalar array; materializing
one is a full copy that must not be duplicated per viewport).

Shared planar code and the labelmap display now resolve volume-kind render
modes through the render backend registry (isVolumeRenderMode / an
isGpuVolumeSliceRenderMode helper) instead of hardcoded ActorRenderMode
literals, the addImages overlay gate keys on the CanvasActor cpuVolume mode
rather than the composited surface, the resolved camera is mirrored onto
the engine's vtk renderer for camera consumers, and the WebGPU window
honors the viewport background (with a setWebGPUViewportBackground helper).

Includes the genericWebGPUOrtho example: sagittal/coronal CT MPR with live
webgpu/gpu/cpu switching, manipulation and annotation tools, segmentation
editing on a mock labelmap, and a renderBackend URL parameter.
setValue runs per voxel; resolving the segmentation and labelmap layer
through ensureLabelmapState there re-normalized the entire binding state
for every voxel of a stroke. Cache the resolved layer per operation data
(created fresh per stroke, so bindings cannot change mid-stroke) and map
labelValue to segmentIndex through the layer's existing table.
… reorients the plane

The planar fit parallel scale is recomputed from the reoriented plane's
projected extent, which varies with obliquity, while the relative zoom was
carried over unchanged -- so rotating a viewport through setViewReference
pulsed in and out as the plane swept the volume. Resolve the hypothetical
post-patch view and rescale the relative zoom so the absolute parallel
scale is preserved, matching the legacy setViewReference semantics.
…kends

genericWebGPUPetMIP: an inverted full-volume PET slab projection
(maximum/minimum/average blend with a thickness slider) on the WebGPU
volume-slice render mode, wheel-rotated around the patient axis, beside a
drag-to-rotate CT-Bone 3D volume viewport. Live webgpu/gpu switching with
a backend-colored background cue and a debug panel reporting each
viewport's actual render mode (the 3D viewport has no WebGPU mode yet and
stays on WebGL).
… visible bounds

Legacy VolumeViewport3D follows the wide clipping-range default with
renderer.resetCameraClippingRange() on every camera change, pulling the
near/far planes tight around the actor bounds. The generic vtkVolume3d
path only applied the wide +/-1e6 default, and volume ray casting derives
its sampling from the clipping range, so renders showed banding and
streaking that the legacy viewport does not. Apply the same
wide-then-reset sequence on mount and on every camera update.
…MIP example

Mirror the legacy TrackballRotateTool interaction trick: double the volume
mapper's sample distance on drag start and restore it on release with a
final full-resolution render.
@sedghi
sedghi force-pushed the cornerstoneWebGPUCornerstone branch from b94fc89 to a1abe84 Compare July 20, 2026 16:42

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/tools/src/stateManagement/segmentation/helpers/labelmapImageMapperSupport.ts (1)

438-449: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove redundant render-mode check.

Since isNextPlanarViewport is defined using isPlanarGpuVolumeSliceViewport(viewport) at line 362, it already enforces that isGpuVolumeSliceRenderMode(...) is true (via line 101). Therefore, if isNextPlanarViewport is true, the condition !isGpuVolumeSliceRenderMode(...) will never evaluate to true.

Unsupported render modes for Next planar viewports are already caught at line 364, where the function returns unsupported:viewport. You can safely remove this entire block.

♻️ Proposed refactor
-  if (
-    isNextPlanarViewport &&
-    !isGpuVolumeSliceRenderMode(
-      getPlanarPrimaryRenderMode(compatibilityViewport)
-    )
-  ) {
-    return {
-      key: `unsupported:renderMode:${orientationKey}`,
-      sliceIndex: sliceIndex ?? NaN,
-      supported: false,
-    };
-  }
-
   return {
     // Use the discrete slice index instead of focal-point-derived world position.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/tools/src/stateManagement/segmentation/helpers/labelmapImageMapperSupport.ts`
around lines 438 - 449, Remove the unreachable render-mode validation block
guarded by isNextPlanarViewport and !isGpuVolumeSliceRenderMode in the
label-mapping helper. Keep the existing unsupported:viewport handling for Next
planar viewports unchanged, along with all subsequent mapping logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/core/examples/genericWebGPUStack/index.ts`:
- Around line 330-343: Guard the “Use webgpu backend” toolbar registration in
the generic WebGPU example with isWebGPURenderingAvailable(), or disable its
handler when unavailable, so it cannot invoke switchBackend('webgpu') unless
WebGPU was successfully registered. Leave the GPU and CPU toolbar actions
unchanged.

---

Nitpick comments:
In
`@packages/tools/src/stateManagement/segmentation/helpers/labelmapImageMapperSupport.ts`:
- Around line 438-449: Remove the unreachable render-mode validation block
guarded by isNextPlanarViewport and !isGpuVolumeSliceRenderMode in the
label-mapping helper. Keep the existing unsupported:viewport handling for Next
planar viewports unchanged, along with all subsequent mapping logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9a5a94cf-de9b-4232-a374-ec11997438d0

📥 Commits

Reviewing files that changed from the base of the PR and between b94fc89 and a1abe84.

📒 Files selected for processing (27)
  • packages/core/examples/genericWebGPUOrtho/index.ts
  • packages/core/examples/genericWebGPUPetMIP/index.ts
  • packages/core/examples/genericWebGPUStack/index.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/DefaultPlanarDataProvider.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/PlanarLegacyCompatibilityController.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/PlanarResolvedView.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/PlanarViewReferenceController.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/PlanarViewport.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/PlanarViewportLegacyAdapter.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/WebGPUImageMapperRenderPath.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/WebGPUVolumeSliceRenderPath.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/planarRenderPathProjection.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/planarViewReference.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/vtkWebGPUModules.d.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/webgpuViewportRenderWindow.ts
  • packages/core/src/RenderingEngine/GenericViewport/Volume3D/VtkVolume3DRenderPath.ts
  • packages/core/src/RenderingEngine/helpers/registerWebGPURenderBackend.ts
  • packages/core/src/index.ts
  • packages/core/src/types/RenderBackendRegistry.ts
  • packages/tools/src/stateManagement/segmentation/helpers/getViewportLabelmapRenderMode.ts
  • packages/tools/src/stateManagement/segmentation/helpers/labelmapImageMapperSupport.ts
  • packages/tools/src/tools/displayTools/Labelmap/labelmapActorStyle.ts
  • packages/tools/src/tools/displayTools/Labelmap/labelmapRenderPlan/planarGenericVolumeLabelmap.ts
  • packages/tools/src/tools/displayTools/Labelmap/labelmapRenderPlan/volumeSliceImageMapperPlan.ts
  • packages/tools/src/tools/displayTools/Labelmap/volumeLabelmapSliceData.ts
  • packages/tools/src/tools/segmentation/strategies/compositions/setValue.ts
  • utils/ExampleRunner/example-info.json
🚧 Files skipped from review as they are similar to previous changes (24)
  • packages/core/src/RenderingEngine/GenericViewport/Planar/vtkWebGPUModules.d.ts
  • utils/ExampleRunner/example-info.json
  • packages/tools/src/tools/displayTools/Labelmap/labelmapActorStyle.ts
  • packages/tools/src/tools/displayTools/Labelmap/labelmapRenderPlan/planarGenericVolumeLabelmap.ts
  • packages/core/src/RenderingEngine/GenericViewport/Volume3D/VtkVolume3DRenderPath.ts
  • packages/core/src/types/RenderBackendRegistry.ts
  • packages/tools/src/tools/segmentation/strategies/compositions/setValue.ts
  • packages/tools/src/tools/displayTools/Labelmap/volumeLabelmapSliceData.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/DefaultPlanarDataProvider.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/PlanarLegacyCompatibilityController.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/PlanarViewport.ts
  • packages/core/src/RenderingEngine/helpers/registerWebGPURenderBackend.ts
  • packages/tools/src/tools/displayTools/Labelmap/labelmapRenderPlan/volumeSliceImageMapperPlan.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/PlanarViewportLegacyAdapter.ts
  • packages/core/src/index.ts
  • packages/tools/src/stateManagement/segmentation/helpers/getViewportLabelmapRenderMode.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/PlanarViewReferenceController.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/PlanarResolvedView.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/WebGPUImageMapperRenderPath.ts
  • packages/core/examples/genericWebGPUOrtho/index.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/planarViewReference.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/webgpuViewportRenderWindow.ts
  • packages/core/src/RenderingEngine/GenericViewport/Planar/WebGPUVolumeSliceRenderPath.ts
  • packages/core/examples/genericWebGPUPetMIP/index.ts

Comment thread packages/core/examples/genericWebGPUStack/index.ts
@sedghi sedghi changed the title BLOKCED BY VTK MERGE DO NOT MERGE feat(webgpu): experimental WebGPU render backend for GenericViewport stacks and MPR feat(webgpu): experimental WebGPU render backend for GenericViewport stacks and MPR Jul 21, 2026

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Claude Code Review

Claude Code Review is paused for this repository. To reconnect it, an admin of this repository's GitHub organization (or the account owner, for personal repositories) who can also manage your Claude organization's Code Review settings needs to re-link GitHub in Code Review settings. This is a one-time step.

Tip: disable this comment in your organization's Code Review settings.

@james-hanks james-hanks changed the title feat(webgpu): experimental WebGPU render backend for GenericViewport stacks and MPR [REJECTED] feat(webgpu): experimental WebGPU render backend for GenericViewport stacks and MPR Sep 2, 2026
@wayfarer3130

Copy link
Copy Markdown
Collaborator

Superseded by #2894 — a request for a vtk-wasm render backend, delivered as a separable rendering module covering planar, MPR and 3D.

The rejection here is about direction, not quality. vtk.js's WebGPU work is not going to be completed; the supported route to WebGPU is vtk-wasm, with vtk.js expected to be deprecated in its favour. Extending the vtk.js WebGPU view API therefore builds on a path that will not be maintained.

Most of what this branch established carries forward into #2894 and is credited there:

  • the registry-driven render-mode resolution — replacing hardcoded ActorRenderMode literals with isImageRenderMode / isVolumeRenderMode / getRenderSurfaceForRenderMode across planar view resolution, view references, scrolling and labelmap styling. That refactor is what makes any extension backend possible, and it is good independently of which backend consumes it;
  • the examples with live backend switching, ?renderBackend=, and the debug panel;
  • the shape of the extension seam, and the specific places it turned out to be incomplete.

Several changes on this branch are not WebGPU-specific and should land on their own rather than being lost with it:

  • iOS Safari half-float volume prescalinggetFilterableFloatTexturePrecision(), setUseHalfFloatForFloatData() on vtkStreamingOpenGLTexture, and the two mapper call sites. This fixes R16F-filterable / R32F-non-filterable devices, where modality prescaling is currently disabled outright. Independent of WebGPU.
  • 3D volume clipping-range banding — the resetCameraClippingRange() call in VtkVolume3DRenderPath; without it the ray sampling is quantized by the ±1e6 fallback range and the render shows visible banding and streaks. That is a live defect in the WebGL 3D path.
  • Reorientation scale preservationgetReorientationScale() in PlanarViewReferenceController, stopping a rotating view reference from pulsing in and out as the fit parallel scale is recomputed per plane.
  • Performance — per-stroke brush binding resolution in setValue, incremental flat-index labelmap slice extraction, and slice pixel buffer reuse.

Could these be split out as their own PRs?

@wayfarer3130

Copy link
Copy Markdown
Collaborator

Revised position — not rejected, if it moves out of core

@sedghi I've gone back through the full diff and I'm withdrawing the rejection. I want to take a different route than dropping this.

The offer: package the WebGPU backend as its own module — @cornerstonejs/rendering-webgpu-vtkjs or similar — marked @deprecated from day one, not installed by default, not in the core dependency graph. On those terms I'm happy to carry it. A module nobody installs costs nothing to keep, and it stops being a maintenance liability the moment vtk-wasm (#2894) lands, without anyone having to delete working code.

What changed my mind: of 43 changed files, only four core/tools edits are actually WebGPU-specific. Everything else is either the generic registry refactor — ActorRenderMode literals giving way to isImageRenderMode / isVolumeRenderMode / getRenderSurfaceForRenderMode — or the four independent fixes in my previous comment. The branch is far less invasive than its line count suggests, and the registry refactor is work we want regardless of which backend consumes it.

But "entirely outside core" is not achievable, and I don't want to pretend otherwise. Three things have to come from core, and I'd rather state them as the terms up front than have you discover them mid-rework:

1. Core has to export a render-path SDK

The two WebGPU paths import nine internal modules. None of them are exported from packages/core/src/index.ts today — I checked all nine:

  • GenericViewport/Planar/buildPlanarActorEntry
  • RenderingEngine/helpers/planarImageRenderingapplyPlanarImagePresentation, createVTKImageDataFromImage, getDefaultImageVOIRange, updateVTKImageDataGeometryFromImage
  • Planar/planarRenderCameraapplyPlanarICameraToActor, applyPlanarICameraToRenderer
  • Planar/planarRenderPathProjectionresolvePlanarRenderPathProjection, resolvePlanarRenderPathCurrentImageIdIndex, getPlanarRenderPathActiveSourceICamera
  • Planar/planarImageEventstriggerPlanarNewImage, triggerPlanarVolumeNewImage
  • Planar/planarVolumePresentationapplyPlanarVolumePresentation
  • Planar/CpuImageSliceRenderPathbuildPlanarImageData
  • RenderingEngine/helpers/setDefaultVolumeVOI
  • RenderingEngine/renderingEngineCache

Plus the type contracts: ViewportArchitectureTypes (RenderPath, RenderPathDefinition, RenderPathAttachment, LoadedData, DataAddOptions), PlanarViewportTypes, planarRuntimeTypes. And isImageRenderMode / isVolumeRenderMode, which this branch already adds to index.ts and which @cornerstonejs/tools needs too.

Roughly 15 named exports and a types entry point. This is the reason I'd rather do it this way than drop the branch: that surface is exactly the seam the vtk-wasm work in #2894 needs. Defining it against a real second backend that already exists is a far better test of it than designing it in the abstract and discovering the gaps later.

2. Backend-owned surfaces, so the two workarounds can go

Two of the four WebGPU-specific edits exist only because RenderSurface is a closed 'vtk' | 'cpu' union. The backend can only declare surface: 'cpu' and blit, yet it still hosts vtk actors — so both PlanarViewport.supportsImageOverlays and the tools-side isGpuVolumeSliceRenderMode had to special-case renderMode !== ActorRenderMode.CPU_VOLUME rather than ask the surface. The branch's own comment says it plainly:

this must not key on the composited surface — extension backends such as webgpu blit into the cpu surface canvas while still hosting vtk actors

A registry capability for "hosts vtk actors" (or backend-declared surfaces, which is the fuller fix and is already on the #2894 list) makes both special cases disappear. I don't want either literal landing in core.

3. The registry augmentation belongs in the plugin

This branch adds webgpu to core's RenderBackendRegistry and RenderBackendConstants interfaces. That contradicts the documented design of that file, which has extensions augment those interfaces from their own .d.ts. Move it to the plugin package, along with the four registerWebGPURenderBackend / isWebGPURenderingAvailable / getWebGPUViewportDebugInfo / setWebGPUViewportBackground exports in index.ts — those become the plugin's public entry point.

One packaging detail

Core pins "@kitware/vtk.js": "36.4.1" exactly, and this branch needs 36.4.2 for the two upstream fixes (Kitware/vtk-js#3547, #3548). A single build can't resolve two versions, so core's pin takes the patch bump regardless of where the plugin lives. That's fine — it's a patch release containing fixes we want.

Suggested split

  1. The four non-WebGPU fixes as their own PRs (previous comment) — iOS half-float, 3D clipping banding, reorientation scale, the brush/slice performance work. These shouldn't be gated on any of the rest.
  2. Registry completion in core: the SDK exports, the actor-hosting capability, and the surface question.
  3. The registry refactor (ActorRenderMode literals → registry queries) in core and tools — generic, wanted either way.
  4. This branch reduced to @cornerstonejs/rendering-webgpu-vtkjs, @deprecated, opt-in install, registered through the add-on pattern we already use for polySeg.

Happy to discuss the ordering, and to take the core-side pieces (2 and 3) if that's easier than reworking the whole branch yourself. Removing the rejection marker from the title now.

@wayfarer3130 wayfarer3130 changed the title [REJECTED] feat(webgpu): experimental WebGPU render backend for GenericViewport stacks and MPR feat(webgpu): experimental WebGPU render backend for GenericViewport stacks and MPR Sep 2, 2026
@daker

daker commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

@wayfarer3130 Could you clarify how VTK.js is expected to be deprecated in favor of VTK.wasm? Is there an official statement from Kitware confirming this direction? as of today it's still maintained and WebGPU support is in parity with WebGL Kitware/vtk-js#3473

I also have some concerns about VTK.wasm as the primary path. While exposing VTK C++ has benefits, it makes debugging, patching, and contributing fixes more difficult(debug build with DWARF). We will have to deal with all their complex CPP tooling and build systems, even small fixes may need to go through the VTK development and release cycle. With VTK.js, we can debug directly in the browser, land fixes quickly or do postinstall patchs. VTK.wasm is a 22Mb(78Mb uncompressed) file that you have to load before doing anything.

Do we gain any performance improvement compared to VTK.wasm compared to VTK.js WebGL or even WebGPU version ?

Some NPM stats:

VTK.js ESM : 100K downloads/week
VTK.js UMD: 5K downloads/week
VTK.wasm: 1.6K downloads/week

@sedghi

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.

3 participants