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