Skip to content

feat(WebGPU): add WebGPU support to VolumePassFSQ#3526

Draft
daker wants to merge 7 commits into
Kitware:masterfrom
daker:feat-webgpu-volumepassfsq
Draft

feat(WebGPU): add WebGPU support to VolumePassFSQ#3526
daker wants to merge 7 commits into
Kitware:masterfrom
daker:feat-webgpu-volumepassfsq

Conversation

@daker

@daker daker commented Jun 3, 2026

Copy link
Copy Markdown
Collaborator

Context

Results

Changes

  • Documentation and TypeScript definitions were updated to match those changes

PR and Code Checklist

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

Testing

  • This change adds or fixes unit tests
  • Tested environment:
    • vtk.js:
    • OS:
    • Browser:

@daker daker marked this pull request as draft June 3, 2026 10:03
@daker

daker commented Jun 3, 2026

Copy link
Copy Markdown
Collaborator Author

Draft to get some initial feedback

@daker daker force-pushed the feat-webgpu-volumepassfsq branch from 97852ee to cfbfc19 Compare June 3, 2026 12:10
@daker daker force-pushed the feat-webgpu-volumepassfsq branch from cfbfc19 to 2c55dfe Compare June 3, 2026 12:18
@daker

daker commented Jun 8, 2026

Copy link
Copy Markdown
Collaborator Author

@sankhesh mind doing an initial review ?

@daker

daker commented Jun 15, 2026

Copy link
Copy Markdown
Collaborator Author

@sankhesh any chance to get an initial review ?

@sankhesh sankhesh left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is a big change. Took me a while to go through it. I've left some questions but overall, needs more documentation and testing.

Comment on lines +39 to +53
let dims = textureDimensions(vTex, 0);
let maxCoord = vec3<i32>(dims) - vec3<i32>(1);
let nearestCoord = clamp(
vec3<i32>(floor(tpos.xyz * vec3<f32>(dims))),
vec3<i32>(0),
maxCoord
);
let nearestValue = textureLoad(vTex, nearestCoord, 0);

if ((forceNearestMask & 1) != 0) { value.x = nearestValue.x; }
if ((forceNearestMask & 2) != 0) { value.y = nearestValue.y; }
if ((forceNearestMask & 4) != 0) { value.z = nearestValue.z; }
if ((forceNearestMask & 8) != 0) { value.w = nearestValue.w; }

return value;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Why does getTextureValue need to perform filtering as well now?

Comment on lines +41 to +44
let nearestCoord = clamp(
vec3<i32>(floor(tpos.xyz * vec3<f32>(dims))),
vec3<i32>(0),
maxCoord

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This does not match the exact texel-center convention used by filter sampling, especially near boundaries.

{
if (numComp == 1u) { return sample.x; }
if (numComp == 2u) { return sample.y; }
if (numComp == 3u) { return length(sample.xyz); }

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Why is opacity the length(sample.xyz) here? Was this intentional?

Comment on lines +139 to +141
result.x = getComponentValue(vTex, tpos + vec4<f32>(tstep.x, 0.0, 0.0, 1.0), vNum, component) - scalar;
result.y = getComponentValue(vTex, tpos + vec4<f32>(0.0, tstep.y, 0.0, 1.0), vNum, component) - scalar;
result.z = getComponentValue(vTex, tpos + vec4<f32>(0.0, 0.0, tstep.z, 1.0), vNum, component) - scalar;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I know the webgl mapper uses forward differences only. Did you try central differences here? I think that would produce better results.

var visibility = 1.0;

var j: i32 = 0;
loop

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is a nested sampling loop that is known to have quite an overhead. Have you profiled this?

traverseVals[vNum] = getSimpleColor(sumVal, rowIdx, tfunRows);
}

fn traverseRadon(vTex: texture_3d<f32>, vNum: i32, rowIdx: i32, rayLengthSC: f32, minPosSC: vec4<f32>, rayStepSC: vec4<f32>)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This seems different than the opengl mapper. Could you please explain/document this function?

const tmp3Mat4 = new Float64Array(16);
const tmp4Mat4 = new Float64Array(16);

function transformPoint(mat, x, y, z) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

IMHO, there are math functions already that do this.

forceNearestMask |= 1 << component;
}
}
componentInfoArray[vidx * 4 + 3] = forceNearestMask;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I don't follow this. Could you please explain?

Comment on lines +1730 to +1744
const cRange = cfun.getRange();
colorScaleArray[vidx * 4 + component] =
sscale / (cRange[1] - cRange[0]);
colorShiftArray[vidx * 4 + component] =
-cRange[0] / (cRange[1] - cRange[0]);

const ofun = vprop.getScalarOpacity(
vprop.getIndependentComponents() ? component : 0
);
const oRange = ofun.getRange();
opacityScaleArray[vidx * 4 + component] =
sscale / (oRange[1] - oRange[0]);
opacityShiftArray[vidx * 4 + component] =
-oRange[0] / (oRange[1] - oRange[0]);
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Maybe add a check to ensure the ranges are valid before dividing by them

Comment on lines +1925 to +1927
for (let i = 0; i < jitterArray.length; i++) {
jitterArray[i] = Math.random();
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I thought we used a perlin noise texture in the opengl mapper. This works too, but this buffer will be computed for each volume in the window.

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