123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370 |
- 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 nodeBuilder = this.nodes.getForCompute( computeNode );
- // programmable stage
- let stageCompute = this.programs.compute.get( nodeBuilder.computeShader );
- if ( stageCompute === undefined ) {
- if ( previousPipeline && previousPipeline.computeProgram.usedTimes === 0 ) this._releaseProgram( previousPipeline.computeProgram );
- stageCompute = new ProgrammableStage( nodeBuilder.computeShader, 'compute' );
- this.programs.compute.set( nodeBuilder.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 ) {
- 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 nodeBuilder = this.nodes.getForRender( renderObject );
- // programmable stages
- let stageVertex = this.programs.vertex.get( nodeBuilder.vertexShader );
- if ( stageVertex === undefined ) {
- if ( previousPipeline && previousPipeline.vertexProgram.usedTimes === 0 ) this._releaseProgram( previousPipeline.vertexProgram );
- stageVertex = new ProgrammableStage( nodeBuilder.vertexShader, 'vertex' );
- this.programs.vertex.set( nodeBuilder.vertexShader, stageVertex );
- backend.createProgram( stageVertex );
- }
- let stageFragment = this.programs.fragment.get( nodeBuilder.fragmentShader );
- if ( stageFragment === undefined ) {
- if ( previousPipeline && previousPipeline.fragmentProgram.usedTimes === 0 ) this._releaseProgram( previousPipeline.fragmentProgram );
- stageFragment = new ProgrammableStage( nodeBuilder.fragmentShader, 'fragment' );
- this.programs.fragment.set( nodeBuilder.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 );
- } 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 ) {
- // 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 );
- }
- return pipeline;
- }
- _getComputeCacheKey( computeNode, stageCompute ) {
- return 'compute' + computeNode.id + stageCompute.id;
- }
- _getRenderCacheKey( renderObject, stageVertex, stageFragment ) {
- const { material } = renderObject;
- 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.backend.getCacheKey( renderObject )
- ];
- return parameters.join();
- }
- _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 );
- const material = renderObject.material;
- let needsUpdate = this.backend.needsUpdate( renderObject );
- // check material state
- if ( data.material !== material || data.materialVersion !== material.version ||
- data.transparent !== material.transparent || data.blending !== material.blending || data.premultipliedAlpha !== material.premultipliedAlpha ||
- data.blendSrc !== material.blendSrc || data.blendDst !== material.blendDst || data.blendEquation !== material.blendEquation ||
- data.blendSrcAlpha !== material.blendSrcAlpha || data.blendDstAlpha !== material.blendDstAlpha || data.blendEquationAlpha !== material.blendEquationAlpha ||
- data.colorWrite !== material.colorWrite ||
- data.depthWrite !== material.depthWrite || data.depthTest !== material.depthTest || data.depthFunc !== material.depthFunc ||
- data.stencilWrite !== material.stencilWrite || data.stencilFunc !== material.stencilFunc ||
- data.stencilFail !== material.stencilFail || data.stencilZFail !== material.stencilZFail || data.stencilZPass !== material.stencilZPass ||
- data.stencilFuncMask !== material.stencilFuncMask || data.stencilWriteMask !== material.stencilWriteMask ||
- data.side !== material.side || data.alphaToCoverage !== material.alphaToCoverage
- ) {
- data.material = material; data.materialVersion = material.version;
- data.transparent = material.transparent; data.blending = material.blending; data.premultipliedAlpha = material.premultipliedAlpha;
- data.blendSrc = material.blendSrc; data.blendDst = material.blendDst; data.blendEquation = material.blendEquation;
- data.blendSrcAlpha = material.blendSrcAlpha; data.blendDstAlpha = material.blendDstAlpha; data.blendEquationAlpha = material.blendEquationAlpha;
- data.colorWrite = material.colorWrite;
- data.depthWrite = material.depthWrite; data.depthTest = material.depthTest; data.depthFunc = material.depthFunc;
- data.stencilWrite = material.stencilWrite; data.stencilFunc = material.stencilFunc;
- data.stencilFail = material.stencilFail; data.stencilZFail = material.stencilZFail; data.stencilZPass = material.stencilZPass;
- data.stencilFuncMask = material.stencilFuncMask; data.stencilWriteMask = material.stencilWriteMask;
- data.side = material.side; data.alphaToCoverage = material.alphaToCoverage;
- needsUpdate = true;
- }
- return needsUpdate || data.pipeline === undefined;
- }
- }
- export default Pipelines;
|