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