Skip to content

BLOKCED BY VTK MERGE DO NOT MERGE feat(webgpu): experimental WebGPU render backend for GenericViewport stacks and MPR#2796

Open
sedghi wants to merge 9 commits into
mainfrom
cornerstoneWebGPUCornerstone
Open

BLOKCED BY VTK MERGE DO NOT MERGE feat(webgpu): experimental WebGPU render backend for GenericViewport stacks and MPR#2796
sedghi wants to merge 9 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 identifies the main WebGPU backend change, despite extra noisy prefix text.
Description check ✅ Passed It follows the required template and includes completed Context, Changes & Results, Testing, and Checklist sections.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 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
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.

2 participants