| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312 | import WebGPURenderPipeline from './WebGPURenderPipeline.js';import WebGPUProgrammableStage from './WebGPUProgrammableStage.js';class WebGPURenderPipelines {	constructor( renderer, properties, device, sampleCount, nodes ) {		this.renderer = renderer;		this.properties = properties;		this.device = device;		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 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, 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 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;
 |