Pipelines.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. import DataMap from './DataMap.js';
  2. import RenderPipeline from './RenderPipeline.js';
  3. import ComputePipeline from './ComputePipeline.js';
  4. import ProgrammableStage from './ProgrammableStage.js';
  5. class Pipelines extends DataMap {
  6. constructor( backend, nodes ) {
  7. super();
  8. this.backend = backend;
  9. this.nodes = nodes;
  10. this.bindings = null; // set by the bindings
  11. this.caches = new Map();
  12. this.programs = {
  13. vertex: new Map(),
  14. fragment: new Map(),
  15. compute: new Map()
  16. };
  17. }
  18. getForCompute( computeNode, bindings ) {
  19. const { backend } = this;
  20. const data = this.get( computeNode );
  21. if ( this._needsComputeUpdate( computeNode ) ) {
  22. const previousPipeline = data.pipeline;
  23. if ( previousPipeline ) {
  24. previousPipeline.usedTimes --;
  25. previousPipeline.computeProgram.usedTimes --;
  26. }
  27. // get shader
  28. const nodeBuilderState = this.nodes.getForCompute( computeNode );
  29. // programmable stage
  30. let stageCompute = this.programs.compute.get( nodeBuilderState.computeShader );
  31. if ( stageCompute === undefined ) {
  32. if ( previousPipeline && previousPipeline.computeProgram.usedTimes === 0 ) this._releaseProgram( previousPipeline.computeProgram );
  33. stageCompute = new ProgrammableStage( nodeBuilderState.computeShader, 'compute', nodeBuilderState.transforms, nodeBuilderState.nodeAttributes );
  34. this.programs.compute.set( nodeBuilderState.computeShader, stageCompute );
  35. backend.createProgram( stageCompute );
  36. }
  37. // determine compute pipeline
  38. const cacheKey = this._getComputeCacheKey( computeNode, stageCompute );
  39. let pipeline = this.caches.get( cacheKey );
  40. if ( pipeline === undefined ) {
  41. if ( previousPipeline && previousPipeline.usedTimes === 0 ) this._releasePipeline( computeNode );
  42. pipeline = this._getComputePipeline( computeNode, stageCompute, cacheKey, bindings );
  43. }
  44. // keep track of all used times
  45. pipeline.usedTimes ++;
  46. stageCompute.usedTimes ++;
  47. //
  48. data.version = computeNode.version;
  49. data.pipeline = pipeline;
  50. }
  51. return data.pipeline;
  52. }
  53. getForRender( renderObject, promises = null ) {
  54. const { backend } = this;
  55. const data = this.get( renderObject );
  56. if ( this._needsRenderUpdate( renderObject ) ) {
  57. const previousPipeline = data.pipeline;
  58. if ( previousPipeline ) {
  59. previousPipeline.usedTimes --;
  60. previousPipeline.vertexProgram.usedTimes --;
  61. previousPipeline.fragmentProgram.usedTimes --;
  62. }
  63. // get shader
  64. const nodeBuilderState = renderObject.getNodeBuilderState();
  65. // programmable stages
  66. let stageVertex = this.programs.vertex.get( nodeBuilderState.vertexShader );
  67. if ( stageVertex === undefined ) {
  68. if ( previousPipeline && previousPipeline.vertexProgram.usedTimes === 0 ) this._releaseProgram( previousPipeline.vertexProgram );
  69. stageVertex = new ProgrammableStage( nodeBuilderState.vertexShader, 'vertex' );
  70. this.programs.vertex.set( nodeBuilderState.vertexShader, stageVertex );
  71. backend.createProgram( stageVertex );
  72. }
  73. let stageFragment = this.programs.fragment.get( nodeBuilderState.fragmentShader );
  74. if ( stageFragment === undefined ) {
  75. if ( previousPipeline && previousPipeline.fragmentProgram.usedTimes === 0 ) this._releaseProgram( previousPipeline.fragmentProgram );
  76. stageFragment = new ProgrammableStage( nodeBuilderState.fragmentShader, 'fragment' );
  77. this.programs.fragment.set( nodeBuilderState.fragmentShader, stageFragment );
  78. backend.createProgram( stageFragment );
  79. }
  80. // determine render pipeline
  81. const cacheKey = this._getRenderCacheKey( renderObject, stageVertex, stageFragment );
  82. let pipeline = this.caches.get( cacheKey );
  83. if ( pipeline === undefined ) {
  84. if ( previousPipeline && previousPipeline.usedTimes === 0 ) this._releasePipeline( previousPipeline );
  85. pipeline = this._getRenderPipeline( renderObject, stageVertex, stageFragment, cacheKey, promises );
  86. } else {
  87. renderObject.pipeline = pipeline;
  88. }
  89. // keep track of all used times
  90. pipeline.usedTimes ++;
  91. stageVertex.usedTimes ++;
  92. stageFragment.usedTimes ++;
  93. //
  94. data.pipeline = pipeline;
  95. }
  96. return data.pipeline;
  97. }
  98. delete( object ) {
  99. const pipeline = this.get( object ).pipeline;
  100. if ( pipeline ) {
  101. // pipeline
  102. pipeline.usedTimes --;
  103. if ( pipeline.usedTimes === 0 ) this._releasePipeline( pipeline );
  104. // programs
  105. if ( pipeline.isComputePipeline ) {
  106. pipeline.computeProgram.usedTimes --;
  107. if ( pipeline.computeProgram.usedTimes === 0 ) this._releaseProgram( pipeline.computeProgram );
  108. } else {
  109. pipeline.fragmentProgram.usedTimes --;
  110. pipeline.vertexProgram.usedTimes --;
  111. if ( pipeline.vertexProgram.usedTimes === 0 ) this._releaseProgram( pipeline.vertexProgram );
  112. if ( pipeline.fragmentProgram.usedTimes === 0 ) this._releaseProgram( pipeline.fragmentProgram );
  113. }
  114. }
  115. super.delete( object );
  116. }
  117. dispose() {
  118. super.dispose();
  119. this.caches = new Map();
  120. this.programs = {
  121. vertex: new Map(),
  122. fragment: new Map(),
  123. compute: new Map()
  124. };
  125. }
  126. updateForRender( renderObject ) {
  127. this.getForRender( renderObject );
  128. }
  129. _getComputePipeline( computeNode, stageCompute, cacheKey, bindings ) {
  130. // check for existing pipeline
  131. cacheKey = cacheKey || this._getComputeCacheKey( computeNode, stageCompute );
  132. let pipeline = this.caches.get( cacheKey );
  133. if ( pipeline === undefined ) {
  134. pipeline = new ComputePipeline( cacheKey, stageCompute );
  135. this.caches.set( cacheKey, pipeline );
  136. this.backend.createComputePipeline( pipeline, bindings );
  137. }
  138. return pipeline;
  139. }
  140. _getRenderPipeline( renderObject, stageVertex, stageFragment, cacheKey, promises ) {
  141. // check for existing pipeline
  142. cacheKey = cacheKey || this._getRenderCacheKey( renderObject, stageVertex, stageFragment );
  143. let pipeline = this.caches.get( cacheKey );
  144. if ( pipeline === undefined ) {
  145. pipeline = new RenderPipeline( cacheKey, stageVertex, stageFragment );
  146. this.caches.set( cacheKey, pipeline );
  147. renderObject.pipeline = pipeline;
  148. this.backend.createRenderPipeline( renderObject, promises );
  149. }
  150. return pipeline;
  151. }
  152. _getComputeCacheKey( computeNode, stageCompute ) {
  153. return computeNode.id + ',' + stageCompute.id;
  154. }
  155. _getRenderCacheKey( renderObject, stageVertex, stageFragment ) {
  156. return stageVertex.id + ',' + stageFragment.id + ',' + this.backend.getRenderCacheKey( renderObject );
  157. }
  158. _releasePipeline( pipeline ) {
  159. this.caches.delete( pipeline.cacheKey );
  160. }
  161. _releaseProgram( program ) {
  162. const code = program.code;
  163. const stage = program.stage;
  164. this.programs[ stage ].delete( code );
  165. }
  166. _needsComputeUpdate( computeNode ) {
  167. const data = this.get( computeNode );
  168. return data.pipeline === undefined || data.version !== computeNode.version;
  169. }
  170. _needsRenderUpdate( renderObject ) {
  171. const data = this.get( renderObject );
  172. return data.pipeline === undefined || this.backend.needsRenderUpdate( renderObject );
  173. }
  174. }
  175. export default Pipelines;