Skip to content
Merged
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
112 changes: 97 additions & 15 deletions Sources/Rendering/WebGPU/ForwardPass/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,36 @@
import macro from 'vtk.js/Sources/macros';
import vtkWebGPUFullScreenQuad from 'vtk.js/Sources/Rendering/WebGPU/FullScreenQuad';
import vtkWebGPUOpaquePass from 'vtk.js/Sources/Rendering/WebGPU/OpaquePass';
import vtkWebGPUOrderIndepenentTranslucentPass from 'vtk.js/Sources/Rendering/WebGPU/OrderIndependentTranslucentPass';
import vtkWebGPURenderEncoder from 'vtk.js/Sources/Rendering/WebGPU/RenderEncoder';
import vtkWebGPUVolumePass from 'vtk.js/Sources/Rendering/WebGPU/VolumePass';
import vtkRenderPass from 'vtk.js/Sources/Rendering/SceneGraph/RenderPass';
import vtkWebGPUSampler from 'vtk.js/Sources/Rendering/WebGPU/Sampler';
import vtkWebGPUTextureView from 'vtk.js/Sources/Rendering/WebGPU/TextureView';

const finalBlitFragTemplate = `
//VTK::Mapper::Dec

//VTK::TCoord::Dec

//VTK::RenderEncoder::Dec

//VTK::IOStructs::Dec

@fragment
fn main(
//VTK::IOStructs::Input
)
//VTK::IOStructs::Output
{
var output: fragmentOutput;

var computedColor: vec4<f32> = clamp(textureSampleLevel(opaquePassColorTexture, finalPassSampler, input.tcoordVS, 0),vec4<f32>(0.0),vec4<f32>(1.0));

//VTK::RenderEncoder::Impl
return output;
}
`;

// ----------------------------------------------------------------------------

Expand Down Expand Up @@ -83,26 +111,80 @@ function vtkForwardPass(publicAPI, model) {
model.volumePass.setVolumes(model.volumes);
model.volumePass.traverse(renNode, viewNode);
}

// blit the result into the swap chain
publicAPI.finalPass(viewNode, renNode);
}
}
}
};

// blit the result into the swap chain
const sctex = viewNode.getCurrentTexture();
const optex = model.opaquePass.getColorTexture();
const cmdEnc = viewNode.getCommandEncoder();
cmdEnc.copyTextureToTexture(
{
texture: optex.getHandle(),
},
{
texture: sctex,
publicAPI.finalPass = (viewNode, renNode) => {
if (!model._finalBlitEncoder) {
publicAPI.createFinalBlitEncoder(viewNode);
}
model._finalBlitOutputTextureView.createFromTextureHandle(
viewNode.getCurrentTexture(),
{ depth: 1, format: viewNode.getPresentationFormat() }
);

model._finalBlitEncoder.attachTextureViews();
model._finalBlitEncoder.begin(viewNode.getCommandEncoder());
renNode.scissorAndViewport(model._finalBlitEncoder);
model._fullScreenQuad.prepareAndDraw(model._finalBlitEncoder);
model._finalBlitEncoder.end();
};

publicAPI.createFinalBlitEncoder = (viewNode) => {
model._finalBlitEncoder = vtkWebGPURenderEncoder.newInstance({
label: 'forwardPassBlit',
});
model._finalBlitEncoder.setDescription({
colorAttachments: [
{
view: null,
loadOp: 'load',
storeOp: 'store',
},
],
});
model._finalBlitEncoder.setPipelineHash('fpf');
model._finalBlitEncoder.setPipelineSettings({
primitive: { cullMode: 'none' },
fragment: {
targets: [
{
format: viewNode.getPresentationFormat(),
blend: {
color: {
srcFactor: 'src-alpha',
dstFactor: 'one-minus-src-alpha',
},
alpha: { srcfactor: 'one', dstFactor: 'one-minus-src-alpha' },
},
},
],
},
{
width: optex.getWidth(),
height: optex.getHeight(),
depthOrArrayLayers: 1,
}
});
model._fsqSampler = vtkWebGPUSampler.newInstance({
label: 'finalPassSampler',
});
model._fsqSampler.create(viewNode.getDevice(), {
minFilter: 'linear',
magFilter: 'linear',
});
model._fullScreenQuad = vtkWebGPUFullScreenQuad.newInstance();
model._fullScreenQuad.setDevice(viewNode.getDevice());
model._fullScreenQuad.setPipelineHash('fpfsq');
model._fullScreenQuad.setTextureViews([
model.opaquePass.getColorTextureView(),
]);
model._fullScreenQuad.setAdditionalBindables([model._fsqSampler]);
model._fullScreenQuad.setFragmentShaderTemplate(finalBlitFragTemplate);
model._finalBlitOutputTextureView = vtkWebGPUTextureView.newInstance();
model._finalBlitEncoder.setColorTextureView(
0,
model._finalBlitOutputTextureView
);
};

Expand Down
2 changes: 1 addition & 1 deletion Sources/Rendering/WebGPU/OpaquePass/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ function vtkWebGPUOpaquePass(publicAPI, model) {
model.colorTexture.create(device, {
width: viewNode.getCanvas().width,
height: viewNode.getCanvas().height,
format: 'bgra8unorm',
format: 'rgba16float',
/* eslint-disable no-undef */
/* eslint-disable no-bitwise */
usage:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ function vtkWebGPUOrderIndependentTranslucentPass(publicAPI, model) {
fragment: {
targets: [
{
format: 'bgra8unorm',
format: 'rgba16float',
blend: {
color: {
srcFactor: 'src-alpha',
Expand Down
10 changes: 5 additions & 5 deletions Sources/Rendering/WebGPU/RenderEncoder/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ function vtkWebGPURenderEncoder(publicAPI, model) {
console.trace();
} else {
for (let i = 0; i < model.colorTextureViews.length; i++) {
const fmt = model.colorTextureViews[i].getTexture().getFormat();
if (fmt !== pd.fragment.targets[i].format) {
const fmt = model.colorTextureViews[i].getTexture()?.getFormat();
if (fmt && fmt !== pd.fragment.targets[i].format) {
console.log(
`mismatched attachments for attachment ${i} on pipeline ${pd.fragment.targets[i].format} while encoder has ${fmt}`
);
Expand All @@ -74,8 +74,8 @@ function vtkWebGPURenderEncoder(publicAPI, model) {
console.log('mismatched depth attachments');
console.trace();
} else if (model.depthTextureView) {
const dfmt = model.depthTextureView.getTexture().getFormat();
if (dfmt !== pd.depthStencil.format) {
const dfmt = model.depthTextureView.getTexture()?.getFormat();
if (dfmt && dfmt !== pd.depthStencil.format) {
console.log(
`mismatched depth attachments on pipeline ${pd.depthStencil.format} while encoder has ${dfmt}`
);
Expand Down Expand Up @@ -211,7 +211,7 @@ export function extend(publicAPI, model, initialValues = {}) {
fragment: {
targets: [
{
format: 'bgra8unorm',
format: 'rgba16float',
blend: {
color: {
srcFactor: 'src-alpha',
Expand Down
32 changes: 18 additions & 14 deletions Sources/Rendering/WebGPU/RenderWindow/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import vtkWebGPUHardwareSelector from 'vtk.js/Sources/Rendering/WebGPU/HardwareS
import vtkWebGPUViewNodeFactory from 'vtk.js/Sources/Rendering/WebGPU/ViewNodeFactory';
import vtkRenderPass from 'vtk.js/Sources/Rendering/SceneGraph/RenderPass';
import vtkRenderWindowViewNode from 'vtk.js/Sources/Rendering/SceneGraph/RenderWindowViewNode';
// import { VtkDataTypes } from 'vtk.js/Sources/Common/Core/DataArray/Constants';
import HalfFloat from 'vtk.js/Sources/Common/Core/HalfFloat';

const { vtkErrorMacro } = macro;
// const IS_CHROME = navigator.userAgent.indexOf('Chrome') !== -1;
Expand Down Expand Up @@ -68,15 +68,15 @@ function vtkWebGPURenderWindow(publicAPI, model) {
publicAPI.recreateSwapChain = () => {
if (model.context) {
model.context.unconfigure();
const presentationFormat = navigator.gpu.getPreferredCanvasFormat(
model.presentationFormat = navigator.gpu.getPreferredCanvasFormat(
model.adapter
);

/* eslint-disable no-undef */
/* eslint-disable no-bitwise */
model.context.configure({
device: model.device.getHandle(),
format: presentationFormat,
format: model.presentationFormat,
alphaMode: 'premultiplied',
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_DST,
width: model.size[0],
Expand Down Expand Up @@ -478,9 +478,9 @@ function vtkWebGPURenderWindow(publicAPI, model) {
height: texture.getHeight(),
};

// must be a multiple of 256 bytes, so 64 texels with rgba8
result.colorBufferWidth = 64 * Math.floor((result.width + 63) / 64);
result.colorBufferSizeInBytes = result.colorBufferWidth * result.height * 4;
// must be a multiple of 256 bytes, so 32 texels with rgba16
result.colorBufferWidth = 32 * Math.floor((result.width + 31) / 32);
result.colorBufferSizeInBytes = result.colorBufferWidth * result.height * 8;
const colorBuffer = vtkWebGPUBuffer.newInstance();
colorBuffer.setDevice(device);
/* eslint-disable no-bitwise */
Expand All @@ -499,7 +499,7 @@ function vtkWebGPURenderWindow(publicAPI, model) {
},
{
buffer: colorBuffer.getHandle(),
bytesPerRow: 4 * result.colorBufferWidth,
bytesPerRow: 8 * result.colorBufferWidth,
rowsPerImage: result.height,
},
{
Expand All @@ -515,20 +515,22 @@ function vtkWebGPURenderWindow(publicAPI, model) {
await cLoad;
/* eslint-enable no-undef */

result.colorValues = new Uint8ClampedArray(
colorBuffer.getMappedRange().slice()
);
result.colorValues = new Uint16Array(colorBuffer.getMappedRange().slice());
colorBuffer.unmap();
// repack the array
const tmparray = new Uint8ClampedArray(result.height * result.width * 4);
for (let y = 0; y < result.height; y++) {
for (let x = 0; x < result.width; x++) {
const doffset = (y * result.width + x) * 4;
const soffset = (y * result.colorBufferWidth + x) * 4;
tmparray[doffset] = result.colorValues[soffset + 2];
tmparray[doffset + 1] = result.colorValues[soffset + 1];
tmparray[doffset + 2] = result.colorValues[soffset];
tmparray[doffset + 3] = result.colorValues[soffset + 3];
tmparray[doffset] =
255.0 * HalfFloat.fromHalf(result.colorValues[soffset]);
tmparray[doffset + 1] =
255.0 * HalfFloat.fromHalf(result.colorValues[soffset + 1]);
tmparray[doffset + 2] =
255.0 * HalfFloat.fromHalf(result.colorValues[soffset + 2]);
tmparray[doffset + 3] =
255.0 * HalfFloat.fromHalf(result.colorValues[soffset + 3]);
}
}
result.colorValues = tmparray;
Expand Down Expand Up @@ -564,6 +566,7 @@ const DEFAULT_VALUES = {
useBackgroundImage: false,
nextPropID: 1,
xrSupported: false,
presentationFormat: null,
};

// ----------------------------------------------------------------------------
Expand Down Expand Up @@ -607,6 +610,7 @@ export function extend(publicAPI, model, initialValues = {}) {
macro.get(publicAPI, model, [
'commandEncoder',
'device',
'presentationFormat',
'useBackgroundImage',
'xrSupported',
]);
Expand Down
17 changes: 15 additions & 2 deletions Sources/Rendering/WebGPU/TextureView/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,19 @@ function vtkWebGPUTextureView(publicAPI, model) {
model.bindGroupLayoutEntry.texture.sampleType = tDetails.sampleType;
};

publicAPI.createFromTextureHandle = (textureHandle, options) => {
model.texture = null;
model.options = options;
model.options.dimension = model.options.dimension || '2d';
model.options.label = model.label;
model.textureHandle = textureHandle;
model.handle = model.textureHandle.createView(model.options);
model.bindGroupLayoutEntry.texture.viewDimension = model.options.dimension;
const tDetails = vtkWebGPUTypes.getDetailsFromTextureFormat(options.format);
model.bindGroupLayoutEntry.texture.sampleType = tDetails.sampleType;
model.bindGroupTime.modified();
};

publicAPI.getBindGroupEntry = () => {
const foo = {
resource: publicAPI.getHandle(),
Expand Down Expand Up @@ -57,7 +70,7 @@ function vtkWebGPUTextureView(publicAPI, model) {

publicAPI.getBindGroupTime = () => {
// check if the handle changed
if (model.texture.getHandle() !== model.textureHandle) {
if (model.texture && model.texture.getHandle() !== model.textureHandle) {
model.textureHandle = model.texture.getHandle();
model.handle = model.textureHandle.createView(model.options);
model.bindGroupTime.modified();
Expand All @@ -67,7 +80,7 @@ function vtkWebGPUTextureView(publicAPI, model) {

// if the texture has changed then get a new view
publicAPI.getHandle = () => {
if (model.texture.getHandle() !== model.textureHandle) {
if (model.texture && model.texture.getHandle() !== model.textureHandle) {
model.textureHandle = model.texture.getHandle();
model.handle = model.textureHandle.createView(model.options);
model.bindGroupTime.modified();
Expand Down
2 changes: 1 addition & 1 deletion Sources/Rendering/WebGPU/VolumePass/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -629,7 +629,7 @@ function vtkWebGPUVolumePass(publicAPI, model) {
fragment: {
targets: [
{
format: 'bgra8unorm',
format: 'rgba16float',
blend: {
color: {
srcFactor: 'one',
Expand Down