@openhps/core
Version:
Open Hybrid Positioning System - Core component
565 lines (543 loc) • 20.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _Constants = require("../../common/Constants.js");
var _WebGPUConstants = require("./WebGPUConstants.js");
var _constants = require("../../../constants.js");
/**
* A WebGPU backend utility module for managing pipelines.
*
* @private
*/
class WebGPUPipelineUtils {
/**
* Constructs a new utility object.
*
* @param {WebGPUBackend} backend - The WebGPU backend.
*/
constructor(backend) {
/**
* A reference to the WebGPU backend.
*
* @type {WebGPUBackend}
*/
this.backend = backend;
}
/**
* Returns the sample count derived from the given render context.
*
* @private
* @param {RenderContext} renderContext - The render context.
* @return {number} The sample count.
*/
_getSampleCount(renderContext) {
return this.backend.utils.getSampleCountRenderContext(renderContext);
}
/**
* Creates a render pipeline for the given render object.
*
* @param {RenderObject} renderObject - The render object.
* @param {Array<Promise>} promises - An array of compilation promises which are used in `compileAsync()`.
*/
createRenderPipeline(renderObject, promises) {
const {
object,
material,
geometry,
pipeline
} = renderObject;
const {
vertexProgram,
fragmentProgram
} = pipeline;
const backend = this.backend;
const device = backend.device;
const utils = backend.utils;
const pipelineData = backend.get(pipeline);
// bind group layouts
const bindGroupLayouts = [];
for (const bindGroup of renderObject.getBindings()) {
const bindingsData = backend.get(bindGroup);
bindGroupLayouts.push(bindingsData.layout);
}
// vertex buffers
const vertexBuffers = backend.attributeUtils.createShaderVertexBuffers(renderObject);
// blending
let blending;
if (material.transparent === true && material.blending !== _constants.NoBlending) {
blending = this._getBlending(material);
}
// stencil
let stencilFront = {};
if (material.stencilWrite === true) {
stencilFront = {
compare: this._getStencilCompare(material),
failOp: this._getStencilOperation(material.stencilFail),
depthFailOp: this._getStencilOperation(material.stencilZFail),
passOp: this._getStencilOperation(material.stencilZPass)
};
}
const colorWriteMask = this._getColorWriteMask(material);
const targets = [];
if (renderObject.context.textures !== null) {
const textures = renderObject.context.textures;
for (let i = 0; i < textures.length; i++) {
const colorFormat = utils.getTextureFormatGPU(textures[i]);
targets.push({
format: colorFormat,
blend: blending,
writeMask: colorWriteMask
});
}
} else {
const colorFormat = utils.getCurrentColorFormat(renderObject.context);
targets.push({
format: colorFormat,
blend: blending,
writeMask: colorWriteMask
});
}
const vertexModule = backend.get(vertexProgram).module;
const fragmentModule = backend.get(fragmentProgram).module;
const primitiveState = this._getPrimitiveState(object, geometry, material);
const depthCompare = this._getDepthCompare(material);
const depthStencilFormat = utils.getCurrentDepthStencilFormat(renderObject.context);
const sampleCount = this._getSampleCount(renderObject.context);
const pipelineDescriptor = {
label: `renderPipeline_${material.name || material.type}_${material.id}`,
vertex: Object.assign({}, vertexModule, {
buffers: vertexBuffers
}),
fragment: Object.assign({}, fragmentModule, {
targets
}),
primitive: primitiveState,
multisample: {
count: sampleCount,
alphaToCoverageEnabled: material.alphaToCoverage && sampleCount > 1
},
layout: device.createPipelineLayout({
bindGroupLayouts
})
};
const depthStencil = {};
const renderDepth = renderObject.context.depth;
const renderStencil = renderObject.context.stencil;
if (renderDepth === true || renderStencil === true) {
if (renderDepth === true) {
depthStencil.format = depthStencilFormat;
depthStencil.depthWriteEnabled = material.depthWrite;
depthStencil.depthCompare = depthCompare;
}
if (renderStencil === true) {
depthStencil.stencilFront = stencilFront;
depthStencil.stencilBack = {}; // three.js does not provide an API to configure the back function (gl.stencilFuncSeparate() was never used)
depthStencil.stencilReadMask = material.stencilFuncMask;
depthStencil.stencilWriteMask = material.stencilWriteMask;
}
if (material.polygonOffset === true) {
depthStencil.depthBias = material.polygonOffsetUnits;
depthStencil.depthBiasSlopeScale = material.polygonOffsetFactor;
depthStencil.depthBiasClamp = 0; // three.js does not provide an API to configure this value
}
pipelineDescriptor.depthStencil = depthStencil;
}
if (promises === null) {
pipelineData.pipeline = device.createRenderPipeline(pipelineDescriptor);
} else {
const p = new Promise((resolve /*, reject*/) => {
device.createRenderPipelineAsync(pipelineDescriptor).then(pipeline => {
pipelineData.pipeline = pipeline;
resolve();
});
});
promises.push(p);
}
}
/**
* Creates GPU render bundle encoder for the given render context.
*
* @param {RenderContext} renderContext - The render context.
* @return {GPURenderBundleEncoder} The GPU render bundle encoder.
*/
createBundleEncoder(renderContext) {
const backend = this.backend;
const {
utils,
device
} = backend;
const depthStencilFormat = utils.getCurrentDepthStencilFormat(renderContext);
const colorFormat = utils.getCurrentColorFormat(renderContext);
const sampleCount = this._getSampleCount(renderContext);
const descriptor = {
label: 'renderBundleEncoder',
colorFormats: [colorFormat],
depthStencilFormat,
sampleCount
};
return device.createRenderBundleEncoder(descriptor);
}
/**
* Creates a compute pipeline for the given compute node.
*
* @param {ComputePipeline} pipeline - The compute pipeline.
* @param {Array<BindGroup>} bindings - The bindings.
*/
createComputePipeline(pipeline, bindings) {
const backend = this.backend;
const device = backend.device;
const computeProgram = backend.get(pipeline.computeProgram).module;
const pipelineGPU = backend.get(pipeline);
// bind group layouts
const bindGroupLayouts = [];
for (const bindingsGroup of bindings) {
const bindingsData = backend.get(bindingsGroup);
bindGroupLayouts.push(bindingsData.layout);
}
pipelineGPU.pipeline = device.createComputePipeline({
compute: computeProgram,
layout: device.createPipelineLayout({
bindGroupLayouts
})
});
}
/**
* Returns the blending state as a descriptor object required
* for the pipeline creation.
*
* @private
* @param {Material} material - The material.
* @return {Object} The blending state.
*/
_getBlending(material) {
let color, alpha;
const blending = material.blending;
const blendSrc = material.blendSrc;
const blendDst = material.blendDst;
const blendEquation = material.blendEquation;
if (blending === _constants.CustomBlending) {
const blendSrcAlpha = material.blendSrcAlpha !== null ? material.blendSrcAlpha : blendSrc;
const blendDstAlpha = material.blendDstAlpha !== null ? material.blendDstAlpha : blendDst;
const blendEquationAlpha = material.blendEquationAlpha !== null ? material.blendEquationAlpha : blendEquation;
color = {
srcFactor: this._getBlendFactor(blendSrc),
dstFactor: this._getBlendFactor(blendDst),
operation: this._getBlendOperation(blendEquation)
};
alpha = {
srcFactor: this._getBlendFactor(blendSrcAlpha),
dstFactor: this._getBlendFactor(blendDstAlpha),
operation: this._getBlendOperation(blendEquationAlpha)
};
} else {
const premultipliedAlpha = material.premultipliedAlpha;
const setBlend = (srcRGB, dstRGB, srcAlpha, dstAlpha) => {
color = {
srcFactor: srcRGB,
dstFactor: dstRGB,
operation: _WebGPUConstants.GPUBlendOperation.Add
};
alpha = {
srcFactor: srcAlpha,
dstFactor: dstAlpha,
operation: _WebGPUConstants.GPUBlendOperation.Add
};
};
if (premultipliedAlpha) {
switch (blending) {
case _constants.NormalBlending:
setBlend(_WebGPUConstants.GPUBlendFactor.One, _WebGPUConstants.GPUBlendFactor.OneMinusSrcAlpha, _WebGPUConstants.GPUBlendFactor.One, _WebGPUConstants.GPUBlendFactor.OneMinusSrcAlpha);
break;
case _constants.AdditiveBlending:
setBlend(_WebGPUConstants.GPUBlendFactor.One, _WebGPUConstants.GPUBlendFactor.One, _WebGPUConstants.GPUBlendFactor.One, _WebGPUConstants.GPUBlendFactor.One);
break;
case _constants.SubtractiveBlending:
setBlend(_WebGPUConstants.GPUBlendFactor.Zero, _WebGPUConstants.GPUBlendFactor.OneMinusSrc, _WebGPUConstants.GPUBlendFactor.Zero, _WebGPUConstants.GPUBlendFactor.One);
break;
case _constants.MultiplyBlending:
setBlend(_WebGPUConstants.GPUBlendFactor.Zero, _WebGPUConstants.GPUBlendFactor.Src, _WebGPUConstants.GPUBlendFactor.Zero, _WebGPUConstants.GPUBlendFactor.SrcAlpha);
break;
}
} else {
switch (blending) {
case _constants.NormalBlending:
setBlend(_WebGPUConstants.GPUBlendFactor.SrcAlpha, _WebGPUConstants.GPUBlendFactor.OneMinusSrcAlpha, _WebGPUConstants.GPUBlendFactor.One, _WebGPUConstants.GPUBlendFactor.OneMinusSrcAlpha);
break;
case _constants.AdditiveBlending:
setBlend(_WebGPUConstants.GPUBlendFactor.SrcAlpha, _WebGPUConstants.GPUBlendFactor.One, _WebGPUConstants.GPUBlendFactor.SrcAlpha, _WebGPUConstants.GPUBlendFactor.One);
break;
case _constants.SubtractiveBlending:
setBlend(_WebGPUConstants.GPUBlendFactor.Zero, _WebGPUConstants.GPUBlendFactor.OneMinusSrc, _WebGPUConstants.GPUBlendFactor.Zero, _WebGPUConstants.GPUBlendFactor.One);
break;
case _constants.MultiplyBlending:
setBlend(_WebGPUConstants.GPUBlendFactor.Zero, _WebGPUConstants.GPUBlendFactor.Src, _WebGPUConstants.GPUBlendFactor.Zero, _WebGPUConstants.GPUBlendFactor.Src);
break;
}
}
}
if (color !== undefined && alpha !== undefined) {
return {
color,
alpha
};
} else {
console.error('THREE.WebGPURenderer: Invalid blending: ', blending);
}
}
/**
* Returns the GPU blend factor which is required for the pipeline creation.
*
* @private
* @param {number} blend - The blend factor as a three.js constant.
* @return {string} The GPU blend factor.
*/
_getBlendFactor(blend) {
let blendFactor;
switch (blend) {
case _constants.ZeroFactor:
blendFactor = _WebGPUConstants.GPUBlendFactor.Zero;
break;
case _constants.OneFactor:
blendFactor = _WebGPUConstants.GPUBlendFactor.One;
break;
case _constants.SrcColorFactor:
blendFactor = _WebGPUConstants.GPUBlendFactor.Src;
break;
case _constants.OneMinusSrcColorFactor:
blendFactor = _WebGPUConstants.GPUBlendFactor.OneMinusSrc;
break;
case _constants.SrcAlphaFactor:
blendFactor = _WebGPUConstants.GPUBlendFactor.SrcAlpha;
break;
case _constants.OneMinusSrcAlphaFactor:
blendFactor = _WebGPUConstants.GPUBlendFactor.OneMinusSrcAlpha;
break;
case _constants.DstColorFactor:
blendFactor = _WebGPUConstants.GPUBlendFactor.Dst;
break;
case _constants.OneMinusDstColorFactor:
blendFactor = _WebGPUConstants.GPUBlendFactor.OneMinusDstColor;
break;
case _constants.DstAlphaFactor:
blendFactor = _WebGPUConstants.GPUBlendFactor.DstAlpha;
break;
case _constants.OneMinusDstAlphaFactor:
blendFactor = _WebGPUConstants.GPUBlendFactor.OneMinusDstAlpha;
break;
case _constants.SrcAlphaSaturateFactor:
blendFactor = _WebGPUConstants.GPUBlendFactor.SrcAlphaSaturated;
break;
case _Constants.BlendColorFactor:
blendFactor = _WebGPUConstants.GPUBlendFactor.Constant;
break;
case _Constants.OneMinusBlendColorFactor:
blendFactor = _WebGPUConstants.GPUBlendFactor.OneMinusConstant;
break;
default:
console.error('THREE.WebGPURenderer: Blend factor not supported.', blend);
}
return blendFactor;
}
/**
* Returns the GPU stencil compare function which is required for the pipeline creation.
*
* @private
* @param {Material} material - The material.
* @return {string} The GPU stencil compare function.
*/
_getStencilCompare(material) {
let stencilCompare;
const stencilFunc = material.stencilFunc;
switch (stencilFunc) {
case _constants.NeverStencilFunc:
stencilCompare = _WebGPUConstants.GPUCompareFunction.Never;
break;
case _constants.AlwaysStencilFunc:
stencilCompare = _WebGPUConstants.GPUCompareFunction.Always;
break;
case _constants.LessStencilFunc:
stencilCompare = _WebGPUConstants.GPUCompareFunction.Less;
break;
case _constants.LessEqualStencilFunc:
stencilCompare = _WebGPUConstants.GPUCompareFunction.LessEqual;
break;
case _constants.EqualStencilFunc:
stencilCompare = _WebGPUConstants.GPUCompareFunction.Equal;
break;
case _constants.GreaterEqualStencilFunc:
stencilCompare = _WebGPUConstants.GPUCompareFunction.GreaterEqual;
break;
case _constants.GreaterStencilFunc:
stencilCompare = _WebGPUConstants.GPUCompareFunction.Greater;
break;
case _constants.NotEqualStencilFunc:
stencilCompare = _WebGPUConstants.GPUCompareFunction.NotEqual;
break;
default:
console.error('THREE.WebGPURenderer: Invalid stencil function.', stencilFunc);
}
return stencilCompare;
}
/**
* Returns the GPU stencil operation which is required for the pipeline creation.
*
* @private
* @param {number} op - A three.js constant defining the stencil operation.
* @return {string} The GPU stencil operation.
*/
_getStencilOperation(op) {
let stencilOperation;
switch (op) {
case _constants.KeepStencilOp:
stencilOperation = _WebGPUConstants.GPUStencilOperation.Keep;
break;
case _constants.ZeroStencilOp:
stencilOperation = _WebGPUConstants.GPUStencilOperation.Zero;
break;
case _constants.ReplaceStencilOp:
stencilOperation = _WebGPUConstants.GPUStencilOperation.Replace;
break;
case _constants.InvertStencilOp:
stencilOperation = _WebGPUConstants.GPUStencilOperation.Invert;
break;
case _constants.IncrementStencilOp:
stencilOperation = _WebGPUConstants.GPUStencilOperation.IncrementClamp;
break;
case _constants.DecrementStencilOp:
stencilOperation = _WebGPUConstants.GPUStencilOperation.DecrementClamp;
break;
case _constants.IncrementWrapStencilOp:
stencilOperation = _WebGPUConstants.GPUStencilOperation.IncrementWrap;
break;
case _constants.DecrementWrapStencilOp:
stencilOperation = _WebGPUConstants.GPUStencilOperation.DecrementWrap;
break;
default:
console.error('THREE.WebGPURenderer: Invalid stencil operation.', stencilOperation);
}
return stencilOperation;
}
/**
* Returns the GPU blend operation which is required for the pipeline creation.
*
* @private
* @param {number} blendEquation - A three.js constant defining the blend equation.
* @return {string} The GPU blend operation.
*/
_getBlendOperation(blendEquation) {
let blendOperation;
switch (blendEquation) {
case _constants.AddEquation:
blendOperation = _WebGPUConstants.GPUBlendOperation.Add;
break;
case _constants.SubtractEquation:
blendOperation = _WebGPUConstants.GPUBlendOperation.Subtract;
break;
case _constants.ReverseSubtractEquation:
blendOperation = _WebGPUConstants.GPUBlendOperation.ReverseSubtract;
break;
case _constants.MinEquation:
blendOperation = _WebGPUConstants.GPUBlendOperation.Min;
break;
case _constants.MaxEquation:
blendOperation = _WebGPUConstants.GPUBlendOperation.Max;
break;
default:
console.error('THREE.WebGPUPipelineUtils: Blend equation not supported.', blendEquation);
}
return blendOperation;
}
/**
* Returns the primitive state as a descriptor object required
* for the pipeline creation.
*
* @private
* @param {Object3D} object - The 3D object.
* @param {BufferGeometry} geometry - The geometry.
* @param {Material} material - The material.
* @return {Object} The primitive state.
*/
_getPrimitiveState(object, geometry, material) {
const descriptor = {};
const utils = this.backend.utils;
descriptor.topology = utils.getPrimitiveTopology(object, material);
if (geometry.index !== null && object.isLine === true && object.isLineSegments !== true) {
descriptor.stripIndexFormat = geometry.index.array instanceof Uint16Array ? _WebGPUConstants.GPUIndexFormat.Uint16 : _WebGPUConstants.GPUIndexFormat.Uint32;
}
switch (material.side) {
case _constants.FrontSide:
descriptor.frontFace = _WebGPUConstants.GPUFrontFace.CCW;
descriptor.cullMode = _WebGPUConstants.GPUCullMode.Back;
break;
case _constants.BackSide:
descriptor.frontFace = _WebGPUConstants.GPUFrontFace.CCW;
descriptor.cullMode = _WebGPUConstants.GPUCullMode.Front;
break;
case _constants.DoubleSide:
descriptor.frontFace = _WebGPUConstants.GPUFrontFace.CCW;
descriptor.cullMode = _WebGPUConstants.GPUCullMode.None;
break;
default:
console.error('THREE.WebGPUPipelineUtils: Unknown material.side value.', material.side);
break;
}
return descriptor;
}
/**
* Returns the GPU color write mask which is required for the pipeline creation.
*
* @private
* @param {Material} material - The material.
* @return {string} The GPU color write mask.
*/
_getColorWriteMask(material) {
return material.colorWrite === true ? _WebGPUConstants.GPUColorWriteFlags.All : _WebGPUConstants.GPUColorWriteFlags.None;
}
/**
* Returns the GPU depth compare function which is required for the pipeline creation.
*
* @private
* @param {Material} material - The material.
* @return {string} The GPU depth compare function.
*/
_getDepthCompare(material) {
let depthCompare;
if (material.depthTest === false) {
depthCompare = _WebGPUConstants.GPUCompareFunction.Always;
} else {
const depthFunc = material.depthFunc;
switch (depthFunc) {
case _constants.NeverDepth:
depthCompare = _WebGPUConstants.GPUCompareFunction.Never;
break;
case _constants.AlwaysDepth:
depthCompare = _WebGPUConstants.GPUCompareFunction.Always;
break;
case _constants.LessDepth:
depthCompare = _WebGPUConstants.GPUCompareFunction.Less;
break;
case _constants.LessEqualDepth:
depthCompare = _WebGPUConstants.GPUCompareFunction.LessEqual;
break;
case _constants.EqualDepth:
depthCompare = _WebGPUConstants.GPUCompareFunction.Equal;
break;
case _constants.GreaterEqualDepth:
depthCompare = _WebGPUConstants.GPUCompareFunction.GreaterEqual;
break;
case _constants.GreaterDepth:
depthCompare = _WebGPUConstants.GPUCompareFunction.Greater;
break;
case _constants.NotEqualDepth:
depthCompare = _WebGPUConstants.GPUCompareFunction.NotEqual;
break;
default:
console.error('THREE.WebGPUPipelineUtils: Invalid depth function.', depthFunc);
}
}
return depthCompare;
}
}
var _default = exports.default = WebGPUPipelineUtils;