323 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			323 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
|  | import DataMap from './DataMap.js'; | ||
|  | import RenderPipeline from './RenderPipeline.js'; | ||
|  | import ComputePipeline from './ComputePipeline.js'; | ||
|  | import ProgrammableStage from './ProgrammableStage.js'; | ||
|  | 
 | ||
|  | class Pipelines extends DataMap { | ||
|  | 
 | ||
|  | 	constructor( backend, nodes ) { | ||
|  | 
 | ||
|  | 		super(); | ||
|  | 
 | ||
|  | 		this.backend = backend; | ||
|  | 		this.nodes = nodes; | ||
|  | 
 | ||
|  | 		this.bindings = null; // set by the bindings
 | ||
|  | 
 | ||
|  | 		this.caches = new Map(); | ||
|  | 		this.programs = { | ||
|  | 			vertex: new Map(), | ||
|  | 			fragment: new Map(), | ||
|  | 			compute: new Map() | ||
|  | 		}; | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | 	getForCompute( computeNode, bindings ) { | ||
|  | 
 | ||
|  | 		const { backend } = this; | ||
|  | 
 | ||
|  | 		const data = this.get( computeNode ); | ||
|  | 
 | ||
|  | 		if ( this._needsComputeUpdate( computeNode ) ) { | ||
|  | 
 | ||
|  | 			const previousPipeline = data.pipeline; | ||
|  | 
 | ||
|  | 			if ( previousPipeline ) { | ||
|  | 
 | ||
|  | 				previousPipeline.usedTimes --; | ||
|  | 				previousPipeline.computeProgram.usedTimes --; | ||
|  | 
 | ||
|  | 			} | ||
|  | 
 | ||
|  | 			// get shader
 | ||
|  | 
 | ||
|  | 			const nodeBuilderState = this.nodes.getForCompute( computeNode ); | ||
|  | 
 | ||
|  | 			// programmable stage
 | ||
|  | 
 | ||
|  | 			let stageCompute = this.programs.compute.get( nodeBuilderState.computeShader ); | ||
|  | 
 | ||
|  | 			if ( stageCompute === undefined ) { | ||
|  | 
 | ||
|  | 				if ( previousPipeline && previousPipeline.computeProgram.usedTimes === 0 ) this._releaseProgram( previousPipeline.computeProgram ); | ||
|  | 
 | ||
|  | 				stageCompute = new ProgrammableStage( nodeBuilderState.computeShader, 'compute', nodeBuilderState.transforms, nodeBuilderState.nodeAttributes ); | ||
|  | 				this.programs.compute.set( nodeBuilderState.computeShader, stageCompute ); | ||
|  | 
 | ||
|  | 				backend.createProgram( stageCompute ); | ||
|  | 
 | ||
|  | 			} | ||
|  | 
 | ||
|  | 			// determine compute pipeline
 | ||
|  | 
 | ||
|  | 			const cacheKey = this._getComputeCacheKey( computeNode, stageCompute ); | ||
|  | 
 | ||
|  | 			let pipeline = this.caches.get( cacheKey ); | ||
|  | 
 | ||
|  | 			if ( pipeline === undefined ) { | ||
|  | 
 | ||
|  | 				if ( previousPipeline && previousPipeline.usedTimes === 0 ) this._releasePipeline( computeNode ); | ||
|  | 
 | ||
|  | 				pipeline = this._getComputePipeline( computeNode, stageCompute, cacheKey, bindings ); | ||
|  | 
 | ||
|  | 			} | ||
|  | 
 | ||
|  | 			// keep track of all used times
 | ||
|  | 
 | ||
|  | 			pipeline.usedTimes ++; | ||
|  | 			stageCompute.usedTimes ++; | ||
|  | 
 | ||
|  | 			//
 | ||
|  | 
 | ||
|  | 			data.version = computeNode.version; | ||
|  | 			data.pipeline = pipeline; | ||
|  | 
 | ||
|  | 		} | ||
|  | 
 | ||
|  | 		return data.pipeline; | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | 	getForRender( renderObject, promises = null ) { | ||
|  | 
 | ||
|  | 		const { backend } = this; | ||
|  | 
 | ||
|  | 		const data = this.get( renderObject ); | ||
|  | 
 | ||
|  | 		if ( this._needsRenderUpdate( renderObject ) ) { | ||
|  | 
 | ||
|  | 			const previousPipeline = data.pipeline; | ||
|  | 
 | ||
|  | 			if ( previousPipeline ) { | ||
|  | 
 | ||
|  | 				previousPipeline.usedTimes --; | ||
|  | 				previousPipeline.vertexProgram.usedTimes --; | ||
|  | 				previousPipeline.fragmentProgram.usedTimes --; | ||
|  | 
 | ||
|  | 			} | ||
|  | 
 | ||
|  | 			// get shader
 | ||
|  | 
 | ||
|  | 			const nodeBuilderState = renderObject.getNodeBuilderState(); | ||
|  | 
 | ||
|  | 			// programmable stages
 | ||
|  | 
 | ||
|  | 			let stageVertex = this.programs.vertex.get( nodeBuilderState.vertexShader ); | ||
|  | 
 | ||
|  | 			if ( stageVertex === undefined ) { | ||
|  | 
 | ||
|  | 				if ( previousPipeline && previousPipeline.vertexProgram.usedTimes === 0 ) this._releaseProgram( previousPipeline.vertexProgram ); | ||
|  | 
 | ||
|  | 				stageVertex = new ProgrammableStage( nodeBuilderState.vertexShader, 'vertex' ); | ||
|  | 				this.programs.vertex.set( nodeBuilderState.vertexShader, stageVertex ); | ||
|  | 
 | ||
|  | 				backend.createProgram( stageVertex ); | ||
|  | 
 | ||
|  | 			} | ||
|  | 
 | ||
|  | 			let stageFragment = this.programs.fragment.get( nodeBuilderState.fragmentShader ); | ||
|  | 
 | ||
|  | 			if ( stageFragment === undefined ) { | ||
|  | 
 | ||
|  | 				if ( previousPipeline && previousPipeline.fragmentProgram.usedTimes === 0 ) this._releaseProgram( previousPipeline.fragmentProgram ); | ||
|  | 
 | ||
|  | 				stageFragment = new ProgrammableStage( nodeBuilderState.fragmentShader, 'fragment' ); | ||
|  | 				this.programs.fragment.set( nodeBuilderState.fragmentShader, stageFragment ); | ||
|  | 
 | ||
|  | 				backend.createProgram( stageFragment ); | ||
|  | 
 | ||
|  | 			} | ||
|  | 
 | ||
|  | 			// determine render pipeline
 | ||
|  | 
 | ||
|  | 			const cacheKey = this._getRenderCacheKey( renderObject, stageVertex, stageFragment ); | ||
|  | 
 | ||
|  | 			let pipeline = this.caches.get( cacheKey ); | ||
|  | 
 | ||
|  | 			if ( pipeline === undefined ) { | ||
|  | 
 | ||
|  | 				if ( previousPipeline && previousPipeline.usedTimes === 0 ) this._releasePipeline( previousPipeline ); | ||
|  | 
 | ||
|  | 				pipeline = this._getRenderPipeline( renderObject, stageVertex, stageFragment, cacheKey, promises ); | ||
|  | 
 | ||
|  | 			} else { | ||
|  | 
 | ||
|  | 				renderObject.pipeline = pipeline; | ||
|  | 
 | ||
|  | 			} | ||
|  | 
 | ||
|  | 			// keep track of all used times
 | ||
|  | 
 | ||
|  | 			pipeline.usedTimes ++; | ||
|  | 			stageVertex.usedTimes ++; | ||
|  | 			stageFragment.usedTimes ++; | ||
|  | 
 | ||
|  | 			//
 | ||
|  | 
 | ||
|  | 			data.pipeline = pipeline; | ||
|  | 
 | ||
|  | 		} | ||
|  | 
 | ||
|  | 		return data.pipeline; | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | 	delete( object ) { | ||
|  | 
 | ||
|  | 		const pipeline = this.get( object ).pipeline; | ||
|  | 
 | ||
|  | 		if ( pipeline ) { | ||
|  | 
 | ||
|  | 			// pipeline
 | ||
|  | 
 | ||
|  | 			pipeline.usedTimes --; | ||
|  | 
 | ||
|  | 			if ( pipeline.usedTimes === 0 ) this._releasePipeline( pipeline ); | ||
|  | 
 | ||
|  | 			// programs
 | ||
|  | 
 | ||
|  | 			if ( pipeline.isComputePipeline ) { | ||
|  | 
 | ||
|  | 				pipeline.computeProgram.usedTimes --; | ||
|  | 
 | ||
|  | 				if ( pipeline.computeProgram.usedTimes === 0 ) this._releaseProgram( pipeline.computeProgram ); | ||
|  | 
 | ||
|  | 			} else { | ||
|  | 
 | ||
|  | 				pipeline.fragmentProgram.usedTimes --; | ||
|  | 				pipeline.vertexProgram.usedTimes --; | ||
|  | 
 | ||
|  | 				if ( pipeline.vertexProgram.usedTimes === 0 ) this._releaseProgram( pipeline.vertexProgram ); | ||
|  | 				if ( pipeline.fragmentProgram.usedTimes === 0 ) this._releaseProgram( pipeline.fragmentProgram ); | ||
|  | 
 | ||
|  | 			} | ||
|  | 
 | ||
|  | 		} | ||
|  | 
 | ||
|  | 		super.delete( object ); | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | 	dispose() { | ||
|  | 
 | ||
|  | 		super.dispose(); | ||
|  | 
 | ||
|  | 		this.caches = new Map(); | ||
|  | 		this.programs = { | ||
|  | 			vertex: new Map(), | ||
|  | 			fragment: new Map(), | ||
|  | 			compute: new Map() | ||
|  | 		}; | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | 	updateForRender( renderObject ) { | ||
|  | 
 | ||
|  | 		this.getForRender( renderObject ); | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | 	_getComputePipeline( computeNode, stageCompute, cacheKey, bindings ) { | ||
|  | 
 | ||
|  | 		// check for existing pipeline
 | ||
|  | 
 | ||
|  | 		cacheKey = cacheKey || this._getComputeCacheKey( computeNode, stageCompute ); | ||
|  | 
 | ||
|  | 		let pipeline = this.caches.get( cacheKey ); | ||
|  | 
 | ||
|  | 		if ( pipeline === undefined ) { | ||
|  | 
 | ||
|  | 			pipeline = new ComputePipeline( cacheKey, stageCompute ); | ||
|  | 
 | ||
|  | 			this.caches.set( cacheKey, pipeline ); | ||
|  | 
 | ||
|  | 			this.backend.createComputePipeline( pipeline, bindings ); | ||
|  | 
 | ||
|  | 		} | ||
|  | 
 | ||
|  | 		return pipeline; | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | 	_getRenderPipeline( renderObject, stageVertex, stageFragment, cacheKey, promises ) { | ||
|  | 
 | ||
|  | 		// check for existing pipeline
 | ||
|  | 
 | ||
|  | 		cacheKey = cacheKey || this._getRenderCacheKey( renderObject, stageVertex, stageFragment ); | ||
|  | 
 | ||
|  | 		let pipeline = this.caches.get( cacheKey ); | ||
|  | 
 | ||
|  | 		if ( pipeline === undefined ) { | ||
|  | 
 | ||
|  | 			pipeline = new RenderPipeline( cacheKey, stageVertex, stageFragment ); | ||
|  | 
 | ||
|  | 			this.caches.set( cacheKey, pipeline ); | ||
|  | 
 | ||
|  | 			renderObject.pipeline = pipeline; | ||
|  | 
 | ||
|  | 			this.backend.createRenderPipeline( renderObject, promises ); | ||
|  | 
 | ||
|  | 		} | ||
|  | 
 | ||
|  | 		return pipeline; | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | 	_getComputeCacheKey( computeNode, stageCompute ) { | ||
|  | 
 | ||
|  | 		return computeNode.id + ',' + stageCompute.id; | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | 	_getRenderCacheKey( renderObject, stageVertex, stageFragment ) { | ||
|  | 
 | ||
|  | 		return stageVertex.id + ',' + stageFragment.id + ',' + this.backend.getRenderCacheKey( renderObject ); | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | 	_releasePipeline( pipeline ) { | ||
|  | 
 | ||
|  | 		this.caches.delete( pipeline.cacheKey ); | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | 	_releaseProgram( program ) { | ||
|  | 
 | ||
|  | 		const code = program.code; | ||
|  | 		const stage = program.stage; | ||
|  | 
 | ||
|  | 		this.programs[ stage ].delete( code ); | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | 	_needsComputeUpdate( computeNode ) { | ||
|  | 
 | ||
|  | 		const data = this.get( computeNode ); | ||
|  | 
 | ||
|  | 		return data.pipeline === undefined || data.version !== computeNode.version; | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | 	_needsRenderUpdate( renderObject ) { | ||
|  | 
 | ||
|  | 		const data = this.get( renderObject ); | ||
|  | 
 | ||
|  | 		return data.pipeline === undefined || this.backend.needsRenderUpdate( renderObject ); | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | } | ||
|  | 
 | ||
|  | export default Pipelines; |