MCP 3D Printer Server
by DMontgomery40
Verified
import { BlendColorFactor, OneMinusBlendColorFactor, } from '../../common/Constants.js';
import {
GPUFrontFace, GPUCullMode, GPUColorWriteFlags, GPUCompareFunction, GPUBlendFactor, GPUBlendOperation, GPUIndexFormat, GPUStencilOperation
} from './WebGPUConstants.js';
import {
FrontSide, BackSide, DoubleSide,
NeverDepth, AlwaysDepth, LessDepth, LessEqualDepth, EqualDepth, GreaterEqualDepth, GreaterDepth, NotEqualDepth,
NoBlending, NormalBlending, AdditiveBlending, SubtractiveBlending, MultiplyBlending, CustomBlending,
ZeroFactor, OneFactor, SrcColorFactor, OneMinusSrcColorFactor, SrcAlphaFactor, OneMinusSrcAlphaFactor, DstColorFactor,
OneMinusDstColorFactor, DstAlphaFactor, OneMinusDstAlphaFactor, SrcAlphaSaturateFactor,
AddEquation, SubtractEquation, ReverseSubtractEquation, MinEquation, MaxEquation,
KeepStencilOp, ZeroStencilOp, ReplaceStencilOp, InvertStencilOp, IncrementStencilOp, DecrementStencilOp, IncrementWrapStencilOp, DecrementWrapStencilOp,
NeverStencilFunc, AlwaysStencilFunc, LessStencilFunc, LessEqualStencilFunc, EqualStencilFunc, GreaterEqualStencilFunc, GreaterStencilFunc, NotEqualStencilFunc
} from '../../../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 !== 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;
}
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 === 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: GPUBlendOperation.Add
};
alpha = {
srcFactor: srcAlpha,
dstFactor: dstAlpha,
operation: GPUBlendOperation.Add
};
};
if ( premultipliedAlpha ) {
switch ( blending ) {
case NormalBlending:
setBlend( GPUBlendFactor.One, GPUBlendFactor.OneMinusSrcAlpha, GPUBlendFactor.One, GPUBlendFactor.OneMinusSrcAlpha );
break;
case AdditiveBlending:
setBlend( GPUBlendFactor.One, GPUBlendFactor.One, GPUBlendFactor.One, GPUBlendFactor.One );
break;
case SubtractiveBlending:
setBlend( GPUBlendFactor.Zero, GPUBlendFactor.OneMinusSrc, GPUBlendFactor.Zero, GPUBlendFactor.One );
break;
case MultiplyBlending:
setBlend( GPUBlendFactor.Zero, GPUBlendFactor.Src, GPUBlendFactor.Zero, GPUBlendFactor.SrcAlpha );
break;
}
} else {
switch ( blending ) {
case NormalBlending:
setBlend( GPUBlendFactor.SrcAlpha, GPUBlendFactor.OneMinusSrcAlpha, GPUBlendFactor.One, GPUBlendFactor.OneMinusSrcAlpha );
break;
case AdditiveBlending:
setBlend( GPUBlendFactor.SrcAlpha, GPUBlendFactor.One, GPUBlendFactor.SrcAlpha, GPUBlendFactor.One );
break;
case SubtractiveBlending:
setBlend( GPUBlendFactor.Zero, GPUBlendFactor.OneMinusSrc, GPUBlendFactor.Zero, GPUBlendFactor.One );
break;
case MultiplyBlending:
setBlend( GPUBlendFactor.Zero, GPUBlendFactor.Src, GPUBlendFactor.Zero, 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 ZeroFactor:
blendFactor = GPUBlendFactor.Zero;
break;
case OneFactor:
blendFactor = GPUBlendFactor.One;
break;
case SrcColorFactor:
blendFactor = GPUBlendFactor.Src;
break;
case OneMinusSrcColorFactor:
blendFactor = GPUBlendFactor.OneMinusSrc;
break;
case SrcAlphaFactor:
blendFactor = GPUBlendFactor.SrcAlpha;
break;
case OneMinusSrcAlphaFactor:
blendFactor = GPUBlendFactor.OneMinusSrcAlpha;
break;
case DstColorFactor:
blendFactor = GPUBlendFactor.Dst;
break;
case OneMinusDstColorFactor:
blendFactor = GPUBlendFactor.OneMinusDstColor;
break;
case DstAlphaFactor:
blendFactor = GPUBlendFactor.DstAlpha;
break;
case OneMinusDstAlphaFactor:
blendFactor = GPUBlendFactor.OneMinusDstAlpha;
break;
case SrcAlphaSaturateFactor:
blendFactor = GPUBlendFactor.SrcAlphaSaturated;
break;
case BlendColorFactor:
blendFactor = GPUBlendFactor.Constant;
break;
case OneMinusBlendColorFactor:
blendFactor = 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 NeverStencilFunc:
stencilCompare = GPUCompareFunction.Never;
break;
case AlwaysStencilFunc:
stencilCompare = GPUCompareFunction.Always;
break;
case LessStencilFunc:
stencilCompare = GPUCompareFunction.Less;
break;
case LessEqualStencilFunc:
stencilCompare = GPUCompareFunction.LessEqual;
break;
case EqualStencilFunc:
stencilCompare = GPUCompareFunction.Equal;
break;
case GreaterEqualStencilFunc:
stencilCompare = GPUCompareFunction.GreaterEqual;
break;
case GreaterStencilFunc:
stencilCompare = GPUCompareFunction.Greater;
break;
case NotEqualStencilFunc:
stencilCompare = 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 KeepStencilOp:
stencilOperation = GPUStencilOperation.Keep;
break;
case ZeroStencilOp:
stencilOperation = GPUStencilOperation.Zero;
break;
case ReplaceStencilOp:
stencilOperation = GPUStencilOperation.Replace;
break;
case InvertStencilOp:
stencilOperation = GPUStencilOperation.Invert;
break;
case IncrementStencilOp:
stencilOperation = GPUStencilOperation.IncrementClamp;
break;
case DecrementStencilOp:
stencilOperation = GPUStencilOperation.DecrementClamp;
break;
case IncrementWrapStencilOp:
stencilOperation = GPUStencilOperation.IncrementWrap;
break;
case DecrementWrapStencilOp:
stencilOperation = 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 AddEquation:
blendOperation = GPUBlendOperation.Add;
break;
case SubtractEquation:
blendOperation = GPUBlendOperation.Subtract;
break;
case ReverseSubtractEquation:
blendOperation = GPUBlendOperation.ReverseSubtract;
break;
case MinEquation:
blendOperation = GPUBlendOperation.Min;
break;
case MaxEquation:
blendOperation = 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 ) ? GPUIndexFormat.Uint16 : GPUIndexFormat.Uint32;
}
switch ( material.side ) {
case FrontSide:
descriptor.frontFace = GPUFrontFace.CCW;
descriptor.cullMode = GPUCullMode.Back;
break;
case BackSide:
descriptor.frontFace = GPUFrontFace.CCW;
descriptor.cullMode = GPUCullMode.Front;
break;
case DoubleSide:
descriptor.frontFace = GPUFrontFace.CCW;
descriptor.cullMode = 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 ) ? GPUColorWriteFlags.All : 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 = GPUCompareFunction.Always;
} else {
const depthFunc = material.depthFunc;
switch ( depthFunc ) {
case NeverDepth:
depthCompare = GPUCompareFunction.Never;
break;
case AlwaysDepth:
depthCompare = GPUCompareFunction.Always;
break;
case LessDepth:
depthCompare = GPUCompareFunction.Less;
break;
case LessEqualDepth:
depthCompare = GPUCompareFunction.LessEqual;
break;
case EqualDepth:
depthCompare = GPUCompareFunction.Equal;
break;
case GreaterEqualDepth:
depthCompare = GPUCompareFunction.GreaterEqual;
break;
case GreaterDepth:
depthCompare = GPUCompareFunction.Greater;
break;
case NotEqualDepth:
depthCompare = GPUCompareFunction.NotEqual;
break;
default:
console.error( 'THREE.WebGPUPipelineUtils: Invalid depth function.', depthFunc );
}
}
return depthCompare;
}
}
export default WebGPUPipelineUtils;