Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions Sources/Rendering/WebGPU/Actor/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import vtkProp from 'vtk.js/Sources/Rendering/Core/Prop';
import vtkViewNode from 'vtk.js/Sources/Rendering/SceneGraph/ViewNode';

import { registerOverride } from 'vtk.js/Sources/Rendering/WebGPU/ViewNodeFactory';
import { getWebGPUContext } from 'vtk.js/Sources/Rendering/WebGPU/Helpers/Context';

const { CoordinateSystem } = vtkProp;

Expand All @@ -19,11 +20,9 @@ function vtkWebGPUActor(publicAPI, model) {
// Builds myself.
publicAPI.buildPass = (prepass) => {
if (prepass) {
model.WebGPURenderer =
publicAPI.getFirstAncestorOfType('vtkWebGPURenderer');
model.WebGPURenderWindow = model.WebGPURenderer.getFirstAncestorOfType(
'vtkWebGPURenderWindow'
);
const { renderer, renderWindow } = getWebGPUContext(publicAPI);
model.WebGPURenderer = renderer;
model.WebGPURenderWindow = renderWindow;
if (model.propID === undefined) {
model.propID = model.WebGPURenderWindow.getUniquePropID();
}
Expand Down
9 changes: 4 additions & 5 deletions Sources/Rendering/WebGPU/Actor2D/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import vtkProp from 'vtk.js/Sources/Rendering/Core/Prop';
import vtkViewNode from 'vtk.js/Sources/Rendering/SceneGraph/ViewNode';

import { registerOverride } from 'vtk.js/Sources/Rendering/WebGPU/ViewNodeFactory';
import { getWebGPUContext } from 'vtk.js/Sources/Rendering/WebGPU/Helpers/Context';

const { CoordinateSystem } = vtkProp;

Expand All @@ -19,11 +20,9 @@ function vtkWebGPUActor2D(publicAPI, model) {
// Builds myself.
publicAPI.buildPass = (prepass) => {
if (prepass) {
model.WebGPURenderer =
publicAPI.getFirstAncestorOfType('vtkWebGPURenderer');
model.WebGPURenderWindow = model.WebGPURenderer.getFirstAncestorOfType(
'vtkWebGPURenderWindow'
);
const { renderer, renderWindow } = getWebGPUContext(publicAPI);
model.WebGPURenderer = renderer;
model.WebGPURenderWindow = renderWindow;
if (model.propID === undefined) {
model.propID = model.WebGPURenderWindow.getUniquePropID();
}
Expand Down
43 changes: 40 additions & 3 deletions Sources/Rendering/WebGPU/BufferManager/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,40 @@ function generateNormals(cellArray, pointArray) {
return packedVBO;
}

// Build a cache key from the request fields that determine buffer contents, so
// callers cannot forget a content affecting field (e.g. shift or scale, which
// _createBuffer bakes into the packed data). Returns null for usages whose
// contents are not fully described by these fields (RawVertex / UniformArray /
// Storage / Texture carry a raw nativeArray); those callers must supply
// req.hash themselves.
function _computeBufferHash(req) {
switch (req.usage) {
case BufferUsage.Index:
case BufferUsage.PointArray:
case BufferUsage.NormalsFromPoints:
break;
default:
return null;
}
const parts = [`u${req.usage}`];
if (req.format) parts.push(`f${req.format}`);
if (req.dataArray) parts.push(`d${req.dataArray.getMTime()}`);
if (req.cells) parts.push(`c${req.cells.getMTime()}`);
if (req.indexBuffer) parts.push(`i${req.indexBuffer.getMTime?.() ?? 0}`);
if (req.representation !== undefined) parts.push(`R${req.representation}`);
if (req.primitiveType !== undefined) parts.push(`P${req.primitiveType}`);
if (req.cellOffset !== undefined) parts.push(`O${req.cellOffset}`);
if (req.cellData) parts.push('cd');
if (req.packExtra) parts.push('pe');
if (req.shift !== undefined) {
parts.push(`s${Array.isArray(req.shift) ? req.shift.join() : req.shift}`);
}
if (req.scale !== undefined) {
parts.push(`S${Array.isArray(req.scale) ? req.scale.join() : req.scale}`);
}
return parts.join('');
}

// ----------------------------------------------------------------------------
// vtkWebGPUBufferManager methods
// ----------------------------------------------------------------------------
Expand Down Expand Up @@ -335,9 +369,12 @@ function vtkWebGPUBufferManager(publicAPI, model) {
publicAPI.hasBuffer = (hash) => model.device.hasCachedObject(hash);

publicAPI.getBuffer = (req) => {
// if we have a source the get/create/cache the buffer
if (req.hash) {
return model.device.getCachedObject(req.hash, _createBuffer, req);
// Prefer an explicit caller hash, otherwise derive one from the request
// fields that determine the buffer contents so shift/scale/etc. cannot be
// silently dropped from the cache key.
const hash = req.hash ?? _computeBufferHash(req);
if (hash) {
return model.device.getCachedObject(hash, _createBuffer, req);
}

return _createBuffer(req);
Expand Down
61 changes: 19 additions & 42 deletions Sources/Rendering/WebGPU/CellArrayMapper/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import vtkWebGPUBufferManager from 'vtk.js/Sources/Rendering/WebGPU/BufferManage
import vtkWebGPUShaderCache from 'vtk.js/Sources/Rendering/WebGPU/ShaderCache';
import vtkWebGPUUniformBuffer from 'vtk.js/Sources/Rendering/WebGPU/UniformBuffer';
import vtkWebGPUSimpleMapper from 'vtk.js/Sources/Rendering/WebGPU/SimpleMapper';
import { getWebGPUContext } from 'vtk.js/Sources/Rendering/WebGPU/Helpers/Context';
import vtkWebGPUTypes from 'vtk.js/Sources/Rendering/WebGPU/Types';
import {
addClipPlaneEntries,
Expand Down Expand Up @@ -391,11 +392,6 @@ fn main(

const tmp2Mat4 = new Float64Array(16);

function isEdges(hash) {
// edge pipelines have "edge" in them
return hash.indexOf('edge') >= 0;
}

// ----------------------------------------------------------------------------
// vtkWebGPUCellArrayMapper methods
// ----------------------------------------------------------------------------
Expand All @@ -406,22 +402,19 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {

publicAPI.buildPass = (prepass) => {
if (prepass) {
if (model.is2D) {
model.WebGPUActor =
publicAPI.getFirstAncestorOfType('vtkWebGPUActor2D');
model.forceZValue = true;
} else {
model.WebGPUActor = publicAPI.getFirstAncestorOfType('vtkWebGPUActor');
model.forceZValue = false;
}
model.forceZValue = model.is2D;
const { parent, renderer, renderWindow, device } = getWebGPUContext(
publicAPI,
model.is2D ? 'vtkWebGPUActor2D' : 'vtkWebGPUActor'
);
model.WebGPUActor = parent;
model.coordinateSystem =
model.WebGPUActor.getRenderable().getCoordinateSystem();
model.useRendererMatrix =
model.coordinateSystem !== CoordinateSystem.DISPLAY;
model.WebGPURenderer =
model.WebGPUActor.getFirstAncestorOfType('vtkWebGPURenderer');
model.WebGPURenderWindow = model.WebGPURenderer.getParent();
model.device = model.WebGPURenderWindow.getDevice();
model.WebGPURenderer = renderer;
model.WebGPURenderWindow = renderWindow;
model.device = device;
}
};

Expand Down Expand Up @@ -911,7 +904,7 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
// we only apply lighting when there is a "var normal" declaration in the
// fragment shader code. That is the lighting trigger.
publicAPI.replaceShaderLight = (hash, pipeline, vertexInput) => {
if (hash.includes('sel')) return;
if (model.selectionPass) return;
const vDesc = pipeline.getShaderDescription('vertex');
if (!vDesc.hasOutput('vertexVC')) vDesc.addOutput('vec4<f32>', 'vertexVC');

Expand All @@ -925,9 +918,9 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
const needLighting =
hasNormal &&
model.useRendererMatrix &&
!isEdges(hash) &&
!publicAPI.isEdgePrimitive() &&
!model.is2D &&
!hash.includes('sel');
!model.selectionPass;
if (needLighting) {
const lightingCode = [
// Vectors needed for light calculations
Expand Down Expand Up @@ -1040,7 +1033,7 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {

publicAPI.replaceShaderColor = (hash, pipeline, vertexInput) => {
// By default, set the colors to be flat
if (isEdges(hash)) {
if (publicAPI.isEdgePrimitive()) {
const fDesc = pipeline.getShaderDescription('fragment');
let code = fDesc.getCode();
code = vtkWebGPUShaderCache.substitute(code, '//VTK::Color::Impl', [
Expand Down Expand Up @@ -1231,7 +1224,7 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
);

publicAPI.replaceShaderSelect = (hash, pipeline, vertexInput) => {
if (hash.includes('sel')) {
if (model.selectionPass) {
const selectBuffer = vertexInput.getBuffer('selectId');
if (selectBuffer) {
const vDesc = pipeline.getShaderDescription('vertex');
Expand Down Expand Up @@ -1342,9 +1335,6 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
let indexBuffer = null;
if (cells) {
indexBuffer = device.getBufferManager().getBuffer({
hash: `R${representation}P${primType}O${
model.cellOffset
}${cells.getMTime()}`,
usage: BufferUsage.Index,
cells,
numberOfPoints: points.getNumberOfPoints(),
Expand All @@ -1357,23 +1347,15 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
vertexInput.setIndexBuffer(null);
}

// hash = all things that can change the values on the buffer
// since mtimes are unique we can use
// - indexBuffer mtime - because cells drive how we pack
// - relevant dataArray mtime - the source data
// - shift - not currently captured
// - scale - not currently captured
// - format
// - usage
// - packExtra - covered by format
// The buffer cache key is derived by BufferManager.getBuffer from the
// request fields (dataArray/indexBuffer mtimes, shift, scale, format, usage,
// packExtra, cellOffset), so callers pass inputs rather than a hand built
// hash and cannot silently drop a content affecting field.
// --- Points Buffer ---
if (points) {
const shift = model.WebGPUActor.getBufferShift(model.WebGPURenderer);
vertexInput.addBuffer(
device.getBufferManager().getBuffer({
hash: `${points.getMTime()}I${
indexBuffer?.getMTime?.() ?? 0
}${shift.join()}float32x4`,
usage: BufferUsage.PointArray,
format: 'float32x4',
dataArray: points,
Expand Down Expand Up @@ -1412,7 +1394,6 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
scale: 127,
};
if (normals) {
buffRequest.hash = `${normals.getMTime()}I${indexBuffer.getMTime()}snorm8x4`;
buffRequest.dataArray = normals;
buffRequest.usage = BufferUsage.PointArray;
vertexInput.addBuffer(
Expand All @@ -1421,7 +1402,6 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
);
} else if (primType === PrimitiveTypes.Triangles) {
model._usesCellNormals = true;
buffRequest.hash = `PFN${points.getMTime()}I${indexBuffer.getMTime()}snorm8x4`;
buffRequest.dataArray = points;
buffRequest.cells = cells;
buffRequest.usage = BufferUsage.NormalsFromPoints;
Expand Down Expand Up @@ -1455,9 +1435,6 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
device.getBufferManager().getBuffer({
usage: BufferUsage.PointArray,
format: 'unorm8x4',
hash: `${haveCellScalars}${c.getMTime()}I${indexBuffer.getMTime()}O${
model.cellOffset
}unorm8x4`,
dataArray: c,
indexBuffer,
cellData: haveCellScalars,
Expand Down
7 changes: 4 additions & 3 deletions Sources/Rendering/WebGPU/CubeAxesActor/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import vtkCubeAxesActor from 'vtk.js/Sources/Rendering/Core/CubeAxesActor';
import vtkViewNode from 'vtk.js/Sources/Rendering/SceneGraph/ViewNode';

import { registerOverride } from 'vtk.js/Sources/Rendering/WebGPU/ViewNodeFactory';
import { getWebGPUContext } from 'vtk.js/Sources/Rendering/WebGPU/Helpers/Context';

// ----------------------------------------------------------------------------
// vtkWebGPUCubeAxesActor methods
Expand All @@ -14,9 +15,9 @@ function vtkWebGPUCubeAxesActor(publicAPI, model) {
// Builds myself.
publicAPI.buildPass = (prepass) => {
if (prepass) {
model.WebGPURenderer =
publicAPI.getFirstAncestorOfType('vtkWebGPURenderer');
model.WebGPURenderWindow = model.WebGPURenderer.getParent();
const { renderer, renderWindow } = getWebGPUContext(publicAPI);
model.WebGPURenderer = renderer;
model.WebGPURenderWindow = renderWindow;

if (!model.CubeAxesActorHelper.getRenderable()) {
model.CubeAxesActorHelper.setRenderable(model.renderable);
Expand Down
9 changes: 4 additions & 5 deletions Sources/Rendering/WebGPU/Glyph3DMapper/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import vtkWebGPUPolyDataMapper from 'vtk.js/Sources/Rendering/WebGPU/PolyDataMap
import vtkWebGPUStorageBuffer from 'vtk.js/Sources/Rendering/WebGPU/StorageBuffer';
import vtkWebGPUShaderCache from 'vtk.js/Sources/Rendering/WebGPU/ShaderCache';
import { registerOverride } from 'vtk.js/Sources/Rendering/WebGPU/ViewNodeFactory';
import { getWebGPUContext } from 'vtk.js/Sources/Rendering/WebGPU/Helpers/Context';

function vtkWebGPUGlyph3DCellArrayMapper(publicAPI, model) {
// Set our className
Expand Down Expand Up @@ -91,7 +92,7 @@ function vtkWebGPUGlyph3DCellArrayMapper(publicAPI, model) {
);

publicAPI.replaceShaderSelect = (hash, pipeline, vertexInput) => {
if (hash.includes('sel')) {
if (model.selectionPass) {
const vDesc = pipeline.getShaderDescription('vertex');
vDesc.addOutput('u32', 'attributeID', 'flat');
vDesc.addOutput('u32', 'compositeID', 'flat');
Expand Down Expand Up @@ -187,10 +188,8 @@ function vtkWebGPUGlyph3DMapper(publicAPI, model) {
) {
// In Core class all arrays are rebuilt when this happens
// but these arrays can be shared between all primType
model.WebGPURenderWindow = publicAPI.getFirstAncestorOfType(
'vtkWebGPURenderWindow'
);
const device = model.WebGPURenderWindow.getDevice();
const { renderWindow, device } = getWebGPUContext(publicAPI);
model.WebGPURenderWindow = renderWindow;

const ssboInstances = Math.max(model.numInstances, 1);
model.SSBO.clearData();
Expand Down
24 changes: 24 additions & 0 deletions Sources/Rendering/WebGPU/Helpers/Context.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* Resolve the WebGPU rendering context (renderer, render window, device) for a
* view node, replacing the ancestor walk that was copy pasted across mappers,
* actors and passes.
* Pass parentType when the caller first needs an intermediate ancestor (the
* actor or image slice the node draws through); the walk to the renderer starts
* from that ancestor. Omit it for nodes (actors, volumes, passes) that sit
* directly under the renderer.
* @param {*} node
* @param {*} parentType
* @returns { parent, renderer, renderWindow, device }
*/
export function getWebGPUContext(node, parentType = null) {
const parent = parentType ? node.getFirstAncestorOfType(parentType) : null;
const from = parent ?? node;
const renderer = from?.getFirstAncestorOfType('vtkWebGPURenderer');
const renderWindow = renderer?.getFirstAncestorOfType(
'vtkWebGPURenderWindow'
);
const device = renderWindow?.getDevice();
return { parent, renderer, renderWindow, device };
}

export default { getWebGPUContext };
18 changes: 11 additions & 7 deletions Sources/Rendering/WebGPU/ImageCPRMapper/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
MAX_CLIPPING_PLANES,
} from 'vtk.js/Sources/Rendering/WebGPU/Helpers/ClippingPlanes';
import { computeFnToString } from 'vtk.js/Sources/Rendering/WebGPU/Helpers/ImageSampling';
import { getWebGPUContext } from 'vtk.js/Sources/Rendering/WebGPU/Helpers/Context';
import { registerOverride } from 'vtk.js/Sources/Rendering/WebGPU/ViewNodeFactory';

const { BufferUsage } = vtkWebGPUBufferManager;
Expand Down Expand Up @@ -300,13 +301,14 @@ function vtkWebGPUImageCPRMapper(publicAPI, model) {

publicAPI.buildPass = (prepass) => {
if (prepass) {
model.WebGPUImageSlice = publicAPI.getFirstAncestorOfType(
const { parent, renderer, renderWindow, device } = getWebGPUContext(
publicAPI,
'vtkWebGPUImageSlice'
);
model.WebGPURenderer =
model.WebGPUImageSlice.getFirstAncestorOfType('vtkWebGPURenderer');
model.WebGPURenderWindow = model.WebGPURenderer.getParent();
model.device = model.WebGPURenderWindow.getDevice();
model.WebGPUImageSlice = parent;
model.WebGPURenderer = renderer;
model.WebGPURenderWindow = renderWindow;
model.device = device;
publicAPI.setWebGPURenderer(model.WebGPURenderer);
}
};
Expand Down Expand Up @@ -340,7 +342,9 @@ function vtkWebGPUImageCPRMapper(publicAPI, model) {
model.currentImageDataInput = model.renderable.getInputData(0);
model.currentCenterlineInput = model.renderable.getOrientedCenterline();

publicAPI.prepareToDraw(model.WebGPURenderer.getRenderEncoder());
const renderEncoder = model.WebGPURenderer.getRenderEncoder();
model.selectionPass = renderEncoder?.getPipelineHash?.() === 'sel';
publicAPI.prepareToDraw(renderEncoder);
model.renderEncoder.registerDrawCallback(model.pipeline, publicAPI.draw);
};

Expand Down Expand Up @@ -890,7 +894,7 @@ function vtkWebGPUImageCPRMapper(publicAPI, model) {
);

publicAPI.replaceShaderRenderEncoder = (hash, pipeline) => {
if (hash.includes('sel')) {
if (model.selectionPass) {
const fDesc = pipeline.getShaderDescription('fragment');
fDesc.addOutput('vec4<u32>', 'outColor');
let code = fDesc.getCode();
Expand Down
Loading
Loading