@kitware/vtk.js
Version:
Visualization Toolkit for the Web
302 lines (250 loc) • 10.5 kB
JavaScript
import macro from '../../macros.js';
import HalfFloat from '../../Common/Core/HalfFloat.js';
import vtkWebGPUBufferManager from './BufferManager.js';
import vtkWebGPUTextureView from './TextureView.js';
import vtkWebGPUTypes from './Types.js';
import vtkTexture from '../Core/Texture.js';
var BufferUsage = vtkWebGPUBufferManager.BufferUsage; // ----------------------------------------------------------------------------
// Global methods
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// vtkWebGPUTexture methods
// ----------------------------------------------------------------------------
function vtkWebGPUTexture(publicAPI, model) {
// Set our className
model.classHierarchy.push('vtkWebGPUTexture');
publicAPI.create = function (device, options) {
model.device = device;
model.width = options.width;
model.height = options.height;
model.depth = options.depth ? options.depth : 1;
var dimension = model.depth === 1 ? '2d' : '3d';
model.format = options.format ? options.format : 'rgba8unorm';
model.mipLevel = options.mipLevel ? options.mipLevel : 0;
/* eslint-disable no-undef */
/* eslint-disable no-bitwise */
model.usage = options.usage ? options.usage : GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST;
/* eslint-enable no-undef */
/* eslint-enable no-bitwise */
model.handle = model.device.getHandle().createTexture({
size: [model.width, model.height, model.depth],
format: model.format,
// 'rgba8unorm',
usage: model.usage,
label: model.label,
dimension: dimension,
mipLevelCount: model.mipLevel + 1
});
};
publicAPI.assignFromHandle = function (device, handle, options) {
model.device = device;
model.handle = handle;
model.width = options.width;
model.height = options.height;
model.depth = options.depth ? options.depth : 1;
model.format = options.format ? options.format : 'rgba8unorm';
/* eslint-disable no-undef */
/* eslint-disable no-bitwise */
model.usage = options.usage ? options.usage : GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST;
/* eslint-enable no-undef */
/* eslint-enable no-bitwise */
};
publicAPI.writeImageData = function (req) {
var nativeArray = [];
if (req.canvas) {
model.device.getHandle().queue.copyExternalImageToTexture({
source: req.canvas,
flipY: req.flip
}, {
texture: model.handle,
premultipliedAlpha: true
}, [model.width, model.height, model.depth]);
model.ready = true;
return;
}
if (req.jsImageData && !req.nativeArray) {
req.width = req.jsImageData.width;
req.height = req.jsImageData.height;
req.depth = 1;
req.format = 'rgba8unorm';
req.flip = true;
req.nativeArray = req.jsImageData.data;
}
var tDetails = vtkWebGPUTypes.getDetailsFromTextureFormat(model.format);
var bufferBytesPerRow = model.width * tDetails.stride;
var fixAll = function fixAll(arr, height, depth) {
// bytesPerRow must be a multiple of 256 so we might need to rebuild
// the data here before passing to the buffer. e.g. if it is unorm8x4 then
// we need to have width be a multiple of 64
var inWidthInBytes = arr.length / (height * depth) * arr.BYTES_PER_ELEMENT; // is this a half float texture?
var halfFloat = tDetails.elementSize === 2 && tDetails.sampleType === 'float'; // if we need to copy the data
if (halfFloat || inWidthInBytes % 256) {
var inArray = arr;
var inWidth = inWidthInBytes / inArray.BYTES_PER_ELEMENT;
var outBytesPerElement = tDetails.elementSize;
var outWidthInBytes = 256 * Math.floor((inWidth * outBytesPerElement + 255) / 256);
var outWidth = outWidthInBytes / outBytesPerElement;
var outArray = macro.newTypedArray(halfFloat ? 'Uint16Array' : inArray.constructor.name, outWidth * height * depth);
for (var v = 0; v < height * depth; v++) {
if (halfFloat) {
for (var i = 0; i < inWidth; i++) {
outArray[v * outWidth + i] = HalfFloat.toHalf(inArray[v * inWidth + i]);
}
} else {
outArray.set(inArray.subarray(v * inWidth, (v + 1) * inWidth), v * outWidth);
}
}
return [outArray, outWidthInBytes];
}
return [arr, inWidthInBytes];
};
if (req.nativeArray) {
nativeArray = req.nativeArray;
}
if (req.image) {
var canvas = document.createElement('canvas');
canvas.width = req.image.width;
canvas.height = req.image.height;
var ctx = canvas.getContext('2d');
ctx.translate(0, canvas.height);
ctx.scale(1, -1);
ctx.drawImage(req.image, 0, 0, req.image.width, req.image.height, 0, 0, canvas.width, canvas.height);
var imageData = ctx.getImageData(0, 0, req.image.width, req.image.height);
nativeArray = imageData.data;
}
var cmdEnc = model.device.createCommandEncoder();
if (publicAPI.getDimensionality() !== 3) {
// Non-3D, supports mipmaps
var mips = vtkTexture.generateMipmaps(nativeArray, model.width, model.height, model.mipLevel);
var currentWidth = model.width;
var currentHeight = model.height;
for (var m = 0; m <= model.mipLevel; m++) {
var fix = fixAll(mips[m], currentHeight, 1);
bufferBytesPerRow = fix[1];
var buffRequest = {
dataArray: req.dataArray ? req.dataArray : null,
nativeArray: fix[0],
/* eslint-disable no-undef */
usage: BufferUsage.Texture
/* eslint-enable no-undef */
};
var buff = model.device.getBufferManager().getBuffer(buffRequest);
cmdEnc.copyBufferToTexture({
buffer: buff.getHandle(),
offset: 0,
bytesPerRow: bufferBytesPerRow,
rowsPerImage: currentHeight
}, {
texture: model.handle,
mipLevel: m
}, [currentWidth, currentHeight, 1]);
currentWidth /= 2;
currentHeight /= 2;
}
model.device.submitCommandEncoder(cmdEnc);
model.ready = true;
} else {
// 3D, no mipmaps
var _fix = fixAll(nativeArray, model.height, model.depth);
bufferBytesPerRow = _fix[1];
var _buffRequest = {
dataArray: req.dataArray ? req.dataArray : null,
/* eslint-disable no-undef */
usage: BufferUsage.Texture
/* eslint-enable no-undef */
};
_buffRequest.nativeArray = _fix[0];
var _buff = model.device.getBufferManager().getBuffer(_buffRequest);
cmdEnc.copyBufferToTexture({
buffer: _buff.getHandle(),
offset: 0,
bytesPerRow: bufferBytesPerRow,
rowsPerImage: model.height
}, {
texture: model.handle
}, [model.width, model.height, model.depth]);
model.device.submitCommandEncoder(cmdEnc);
model.ready = true;
}
}; // when data is pulled out of this texture what scale must be applied to
// get back to the original source data. For formats such as r8unorm we
// have to multiply by 255.0, for formats such as r16float it is 1.0
publicAPI.getScale = function () {
var tDetails = vtkWebGPUTypes.getDetailsFromTextureFormat(model.format);
var halfFloat = tDetails.elementSize === 2 && tDetails.sampleType === 'float';
return halfFloat ? 1.0 : 255.0;
};
publicAPI.getNumberOfComponents = function () {
var tDetails = vtkWebGPUTypes.getDetailsFromTextureFormat(model.format);
return tDetails.numComponents;
};
publicAPI.getDimensionality = function () {
var dims = 0;
if (model.width > 1) dims++;
if (model.height > 1) dims++;
if (model.depth > 1) dims++;
return dims;
};
publicAPI.resizeToMatch = function (tex) {
if (tex.getWidth() !== model.width || tex.getHeight() !== model.height || tex.getDepth() !== model.depth) {
model.width = tex.getWidth();
model.height = tex.getHeight();
model.depth = tex.getDepth();
model.handle = model.device.getHandle().createTexture({
size: [model.width, model.height, model.depth],
format: model.format,
usage: model.usage,
label: model.label
});
}
};
publicAPI.resize = function (width, height) {
var depth = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
if (width !== model.width || height !== model.height || depth !== model.depth) {
model.width = width;
model.height = height;
model.depth = depth;
model.handle = model.device.getHandle().createTexture({
size: [model.width, model.height, model.depth],
format: model.format,
usage: model.usage,
label: model.label
});
}
};
publicAPI.createView = function (label) {
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
// if options is missing values try to add them in
if (!options.dimension) {
options.dimension = model.depth === 1 ? '2d' : '3d';
}
var view = vtkWebGPUTextureView.newInstance({
label: label
});
view.create(publicAPI, options);
return view;
};
} // ----------------------------------------------------------------------------
// Object factory
// ----------------------------------------------------------------------------
var DEFAULT_VALUES = {
device: null,
handle: null,
buffer: null,
ready: false,
label: null
}; // ----------------------------------------------------------------------------
function extend(publicAPI, model) {
var initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
Object.assign(model, DEFAULT_VALUES, initialValues); // Object methods
macro.obj(publicAPI, model);
macro.get(publicAPI, model, ['handle', 'ready', 'width', 'height', 'depth', 'format', 'usage']);
macro.setGet(publicAPI, model, ['device', 'label']);
vtkWebGPUTexture(publicAPI, model);
} // ----------------------------------------------------------------------------
var newInstance = macro.newInstance(extend); // ----------------------------------------------------------------------------
var vtkWebGPUTexture$1 = {
newInstance: newInstance,
extend: extend
};
export { vtkWebGPUTexture$1 as default, extend, newInstance };