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.
296 lines
7.5 KiB
296 lines
7.5 KiB
import WebGPURenderPipeline from './WebGPURenderPipeline.js';
|
|
import WebGPUProgrammableStage from './WebGPUProgrammableStage.js';
|
|
|
|
class WebGPURenderPipelines {
|
|
|
|
constructor( device, nodes, utils ) {
|
|
|
|
this.device = device;
|
|
this.nodes = nodes;
|
|
this.utils = utils;
|
|
|
|
this.bindings = null;
|
|
|
|
this.pipelines = [];
|
|
this.objectCache = new WeakMap();
|
|
|
|
this.stages = {
|
|
vertex: new Map(),
|
|
fragment: new Map()
|
|
};
|
|
|
|
}
|
|
|
|
get( object ) {
|
|
|
|
const device = this.device;
|
|
|
|
const cache = this._getCache( object );
|
|
|
|
let currentPipeline;
|
|
|
|
if ( this._needsUpdate( object, cache ) ) {
|
|
|
|
const material = object.material;
|
|
|
|
// release previous cache
|
|
|
|
if ( cache.currentPipeline !== undefined ) {
|
|
|
|
this._releaseObject( object );
|
|
|
|
}
|
|
|
|
// 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, 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, 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 used times
|
|
|
|
currentPipeline.usedTimes ++;
|
|
stageVertex.usedTimes ++;
|
|
stageFragment.usedTimes ++;
|
|
|
|
// events
|
|
|
|
material.addEventListener( 'dispose', cache.dispose );
|
|
|
|
} 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.utils );
|
|
pipeline.init( cacheKey, stageVertex, stageFragment, object, nodeBuilder );
|
|
|
|
pipelines.push( pipeline );
|
|
|
|
}
|
|
|
|
return pipeline;
|
|
|
|
}
|
|
|
|
_computeCacheKey( stageVertex, stageFragment, object ) {
|
|
|
|
const material = object.material;
|
|
const utils = this.utils;
|
|
|
|
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,
|
|
utils.getSampleCount(),
|
|
utils.getCurrentEncoding(), utils.getCurrentColorFormat(), utils.getCurrentDepthStencilFormat(),
|
|
utils.getPrimitiveTopology( object )
|
|
];
|
|
|
|
return parameters.join();
|
|
|
|
}
|
|
|
|
_getCache( object ) {
|
|
|
|
let cache = this.objectCache.get( object );
|
|
|
|
if ( cache === undefined ) {
|
|
|
|
cache = {
|
|
|
|
dispose: () => {
|
|
|
|
this._releaseObject( object );
|
|
|
|
this.objectCache.delete( object );
|
|
|
|
object.material.removeEventListener( 'dispose', cache.dispose );
|
|
|
|
}
|
|
|
|
};
|
|
|
|
this.objectCache.set( object, cache );
|
|
|
|
}
|
|
|
|
return cache;
|
|
|
|
}
|
|
|
|
_releaseObject( object ) {
|
|
|
|
const cache = this.objectCache.get( object );
|
|
|
|
this._releasePipeline( cache.currentPipeline );
|
|
delete cache.currentPipeline;
|
|
|
|
this.nodes.remove( object );
|
|
this.bindings.remove( object );
|
|
|
|
}
|
|
|
|
_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 utils = this.utils;
|
|
|
|
const sampleCount = utils.getSampleCount();
|
|
const encoding = utils.getCurrentEncoding();
|
|
const colorFormat = utils.getCurrentColorFormat();
|
|
const depthStencilFormat = utils.getCurrentDepthStencilFormat();
|
|
|
|
if ( cache.sampleCount !== sampleCount || cache.encoding !== encoding ||
|
|
cache.colorFormat !== colorFormat || cache.depthStencilFormat !== depthStencilFormat ) {
|
|
|
|
cache.sampleCount = sampleCount;
|
|
cache.encoding = encoding;
|
|
cache.colorFormat = colorFormat;
|
|
cache.depthStencilFormat = depthStencilFormat;
|
|
|
|
needsUpdate = true;
|
|
|
|
}
|
|
|
|
return needsUpdate;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
export default WebGPURenderPipelines;
|
|
|