UNPKG

@openhps/core

Version:

Open Hybrid Positioning System - Core component

565 lines (543 loc) 20.8 kB
"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;