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;
 |