three 基础库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

315 lines
8.2 KiB

2 years ago
import WebGPURenderPipeline from './WebGPURenderPipeline.js';
import WebGPUProgrammableStage from './WebGPUProgrammableStage.js';
class WebGPURenderPipelines {
constructor( renderer, properties, device, glslang, sampleCount, nodes ) {
this.renderer = renderer;
this.properties = properties;
this.device = device;
this.glslang = glslang;
this.sampleCount = sampleCount;
this.nodes = nodes;
this.pipelines = [];
this.objectCache = new WeakMap();
this.stages = {
vertex: new Map(),
fragment: new Map()
};
}
get( object ) {
const device = this.device;
const glslang = this.glslang;
const properties = this.properties;
const material = object.material;
const materialProperties = properties.get( material );
const cache = this._getCache( object );
let currentPipeline;
if ( this._needsUpdate( object, cache ) ) {
// get shader
const nodeBuilder = this.nodes.get( object );
// programmable stages
let stageVertex = this.stages.vertex.get( nodeBuilder.vertexShader );
if ( stageVertex === undefined ) {
stageVertex = new WebGPUProgrammableStage( device, glslang, nodeBuilder.vertexShader, 'vertex' );
this.stages.vertex.set( nodeBuilder.vertexShader, stageVertex );
}
let stageFragment = this.stages.fragment.get( nodeBuilder.fragmentShader );
if ( stageFragment === undefined ) {
stageFragment = new WebGPUProgrammableStage( device, glslang, nodeBuilder.fragmentShader, 'fragment' );
this.stages.fragment.set( nodeBuilder.fragmentShader, stageFragment );
}
// determine render pipeline
currentPipeline = this._acquirePipeline( stageVertex, stageFragment, object, nodeBuilder );
cache.currentPipeline = currentPipeline;
// keep track of all pipelines which are used by a material
let materialPipelines = materialProperties.pipelines;
if ( materialPipelines === undefined ) {
materialPipelines = new Set();
materialProperties.pipelines = materialPipelines;
}
if ( materialPipelines.has( currentPipeline ) === false ) {
materialPipelines.add( currentPipeline );
currentPipeline.usedTimes ++;
stageVertex.usedTimes ++;
stageFragment.usedTimes ++;
}
// dispose
if ( materialProperties.disposeCallback === undefined ) {
const disposeCallback = onMaterialDispose.bind( this );
materialProperties.disposeCallback = disposeCallback;
material.addEventListener( 'dispose', disposeCallback );
}
} else {
currentPipeline = cache.currentPipeline;
}
return currentPipeline;
}
dispose() {
this.pipelines = [];
this.objectCache = new WeakMap();
this.shaderModules = {
vertex: new Map(),
fragment: new Map()
};
}
_acquirePipeline( stageVertex, stageFragment, object, nodeBuilder ) {
let pipeline;
const pipelines = this.pipelines;
// check for existing pipeline
const cacheKey = this._computeCacheKey( stageVertex, stageFragment, object );
for ( let i = 0, il = pipelines.length; i < il; i ++ ) {
const preexistingPipeline = pipelines[ i ];
if ( preexistingPipeline.cacheKey === cacheKey ) {
pipeline = preexistingPipeline;
break;
}
}
if ( pipeline === undefined ) {
pipeline = new WebGPURenderPipeline( this.device, this.renderer, this.sampleCount );
pipeline.init( cacheKey, stageVertex, stageFragment, object, nodeBuilder );
pipelines.push( pipeline );
}
return pipeline;
}
_computeCacheKey( stageVertex, stageFragment, object ) {
const material = object.material;
const renderer = this.renderer;
const parameters = [
stageVertex.id, stageFragment.id,
material.transparent, material.blending, material.premultipliedAlpha,
material.blendSrc, material.blendDst, material.blendEquation,
material.blendSrcAlpha, material.blendDstAlpha, material.blendEquationAlpha,
material.colorWrite,
material.depthWrite, material.depthTest, material.depthFunc,
material.stencilWrite, material.stencilFunc,
material.stencilFail, material.stencilZFail, material.stencilZPass,
material.stencilFuncMask, material.stencilWriteMask,
material.side,
this.sampleCount,
renderer.getCurrentEncoding(), renderer.getCurrentColorFormat(), renderer.getCurrentDepthStencilFormat()
];
return parameters.join();
}
_getCache( object ) {
let cache = this.objectCache.get( object );
if ( cache === undefined ) {
cache = {};
this.objectCache.set( object, cache );
}
return cache;
}
_releasePipeline( pipeline ) {
if ( -- pipeline.usedTimes === 0 ) {
const pipelines = this.pipelines;
const i = pipelines.indexOf( pipeline );
pipelines[ i ] = pipelines[ pipelines.length - 1 ];
pipelines.pop();
this._releaseStage( pipeline.stageVertex );
this._releaseStage( pipeline.stageFragment );
}
}
_releaseStage( stage ) {
if ( -- stage.usedTimes === 0 ) {
const code = stage.code;
const type = stage.type;
this.stages[ type ].delete( code );
}
}
_needsUpdate( object, cache ) {
const material = object.material;
let needsUpdate = false;
// check material state
if ( cache.material !== material || cache.materialVersion !== material.version ||
cache.transparent !== material.transparent || cache.blending !== material.blending || cache.premultipliedAlpha !== material.premultipliedAlpha ||
cache.blendSrc !== material.blendSrc || cache.blendDst !== material.blendDst || cache.blendEquation !== material.blendEquation ||
cache.blendSrcAlpha !== material.blendSrcAlpha || cache.blendDstAlpha !== material.blendDstAlpha || cache.blendEquationAlpha !== material.blendEquationAlpha ||
cache.colorWrite !== material.colorWrite ||
cache.depthWrite !== material.depthWrite || cache.depthTest !== material.depthTest || cache.depthFunc !== material.depthFunc ||
cache.stencilWrite !== material.stencilWrite || cache.stencilFunc !== material.stencilFunc ||
cache.stencilFail !== material.stencilFail || cache.stencilZFail !== material.stencilZFail || cache.stencilZPass !== material.stencilZPass ||
cache.stencilFuncMask !== material.stencilFuncMask || cache.stencilWriteMask !== material.stencilWriteMask ||
cache.side !== material.side
) {
cache.material = material; cache.materialVersion = material.version;
cache.transparent = material.transparent; cache.blending = material.blending; cache.premultipliedAlpha = material.premultipliedAlpha;
cache.blendSrc = material.blendSrc; cache.blendDst = material.blendDst; cache.blendEquation = material.blendEquation;
cache.blendSrcAlpha = material.blendSrcAlpha; cache.blendDstAlpha = material.blendDstAlpha; cache.blendEquationAlpha = material.blendEquationAlpha;
cache.colorWrite = material.colorWrite;
cache.depthWrite = material.depthWrite; cache.depthTest = material.depthTest; cache.depthFunc = material.depthFunc;
cache.stencilWrite = material.stencilWrite; cache.stencilFunc = material.stencilFunc;
cache.stencilFail = material.stencilFail; cache.stencilZFail = material.stencilZFail; cache.stencilZPass = material.stencilZPass;
cache.stencilFuncMask = material.stencilFuncMask; cache.stencilWriteMask = material.stencilWriteMask;
cache.side = material.side;
needsUpdate = true;
}
// check renderer state
const renderer = this.renderer;
const encoding = renderer.getCurrentEncoding();
const colorFormat = renderer.getCurrentColorFormat();
const depthStencilFormat = renderer.getCurrentDepthStencilFormat();
if ( cache.sampleCount !== this.sampleCount || cache.encoding !== encoding ||
cache.colorFormat !== colorFormat || cache.depthStencilFormat !== depthStencilFormat ) {
cache.sampleCount = this.sampleCount;
cache.encoding = encoding;
cache.colorFormat = colorFormat;
cache.depthStencilFormat = depthStencilFormat;
needsUpdate = true;
}
return needsUpdate;
}
}
function onMaterialDispose( event ) {
const properties = this.properties;
const material = event.target;
const materialProperties = properties.get( material );
material.removeEventListener( 'dispose', materialProperties.disposeCallback );
properties.remove( material );
// remove references to pipelines
const pipelines = materialProperties.pipelines;
if ( pipelines !== undefined ) {
for ( const pipeline of pipelines ) {
this._releasePipeline( pipeline );
}
}
}
export default WebGPURenderPipelines;