Pipelines.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  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 ) {
  19. const { backend } = this;
  20. const data = this.get( computeNode );
  21. if ( data.pipeline === undefined ) {
  22. // release previous cache
  23. const previousPipeline = this._releasePipeline( computeNode );
  24. // get shader
  25. const nodeBuilder = this.nodes.getForCompute( computeNode );
  26. // programmable stage
  27. let stageCompute = this.programs.compute.get( nodeBuilder.computeShader );
  28. if ( stageCompute === undefined ) {
  29. if ( previousPipeline ) this._releaseProgram( previousPipeline.computeShader );
  30. stageCompute = new ProgrammableStage( nodeBuilder.computeShader, 'compute' );
  31. this.programs.compute.set( nodeBuilder.computeShader, stageCompute );
  32. backend.createProgram( stageCompute );
  33. }
  34. // determine compute pipeline
  35. const pipeline = this._getComputePipeline( stageCompute );
  36. // keep track of all used times
  37. pipeline.usedTimes ++;
  38. stageCompute.usedTimes ++;
  39. //
  40. data.pipeline = pipeline;
  41. }
  42. return data.pipeline;
  43. }
  44. getForRender( renderObject ) {
  45. const { backend } = this;
  46. const data = this.get( renderObject );
  47. if ( this._needsUpdate( renderObject ) ) {
  48. // release previous cache
  49. const previousPipeline = this._releasePipeline( renderObject );
  50. // get shader
  51. const nodeBuilder = this.nodes.getForRender( renderObject );
  52. // programmable stages
  53. let stageVertex = this.programs.vertex.get( nodeBuilder.vertexShader );
  54. if ( stageVertex === undefined ) {
  55. if ( previousPipeline ) this._releaseProgram( previousPipeline.vertexProgram );
  56. stageVertex = new ProgrammableStage( nodeBuilder.vertexShader, 'vertex' );
  57. this.programs.vertex.set( nodeBuilder.vertexShader, stageVertex );
  58. backend.createProgram( stageVertex );
  59. }
  60. let stageFragment = this.programs.fragment.get( nodeBuilder.fragmentShader );
  61. if ( stageFragment === undefined ) {
  62. if ( previousPipeline ) this._releaseProgram( previousPipeline.fragmentShader );
  63. stageFragment = new ProgrammableStage( nodeBuilder.fragmentShader, 'fragment' );
  64. this.programs.fragment.set( nodeBuilder.fragmentShader, stageFragment );
  65. backend.createProgram( stageFragment );
  66. }
  67. // determine render pipeline
  68. const pipeline = this._getRenderPipeline( renderObject, stageVertex, stageFragment );
  69. // keep track of all used times
  70. pipeline.usedTimes ++;
  71. stageVertex.usedTimes ++;
  72. stageFragment.usedTimes ++;
  73. //
  74. data.pipeline = pipeline;
  75. }
  76. return data.pipeline;
  77. }
  78. delete( object ) {
  79. const pipeline = this._releasePipeline( object );
  80. if ( pipeline && pipeline.usedTimes === 0 ) {
  81. if ( pipeline.isComputePipeline ) {
  82. this._releaseProgram( pipeline.computeProgram );
  83. } else {
  84. this._releaseProgram( pipeline.vertexProgram );
  85. this._releaseProgram( pipeline.fragmentProgram );
  86. }
  87. }
  88. super.delete( object );
  89. }
  90. dispose() {
  91. super.dispose();
  92. this.caches = new Map();
  93. this.programs = {
  94. vertex: new Map(),
  95. fragment: new Map(),
  96. compute: new Map()
  97. };
  98. }
  99. _getComputePipeline( stageCompute ) {
  100. // check for existing pipeline
  101. const cacheKey = 'compute:' + stageCompute.id;
  102. let pipeline = this.caches.get( cacheKey );
  103. if ( pipeline === undefined ) {
  104. pipeline = new ComputePipeline( cacheKey, stageCompute );
  105. this.caches.set( cacheKey, pipeline );
  106. this.backend.createComputePipeline( pipeline );
  107. }
  108. return pipeline;
  109. }
  110. _getRenderPipeline( renderObject, stageVertex, stageFragment ) {
  111. // check for existing pipeline
  112. const cacheKey = this._getRenderCacheKey( renderObject, stageVertex, stageFragment );
  113. let pipeline = this.caches.get( cacheKey );
  114. if ( pipeline === undefined ) {
  115. pipeline = new RenderPipeline( cacheKey, stageVertex, stageFragment );
  116. this.caches.set( cacheKey, pipeline );
  117. renderObject.pipeline = pipeline;
  118. this.backend.createRenderPipeline( renderObject );
  119. } else {
  120. // assign a shared pipeline to renderObject
  121. renderObject.pipeline = pipeline;
  122. }
  123. return pipeline;
  124. }
  125. _getRenderCacheKey( renderObject, stageVertex, stageFragment ) {
  126. const { material } = renderObject;
  127. const parameters = [
  128. stageVertex.id, stageFragment.id,
  129. material.transparent, material.blending, material.premultipliedAlpha,
  130. material.blendSrc, material.blendDst, material.blendEquation,
  131. material.blendSrcAlpha, material.blendDstAlpha, material.blendEquationAlpha,
  132. material.colorWrite,
  133. material.depthWrite, material.depthTest, material.depthFunc,
  134. material.stencilWrite, material.stencilFunc,
  135. material.stencilFail, material.stencilZFail, material.stencilZPass,
  136. material.stencilFuncMask, material.stencilWriteMask,
  137. material.side,
  138. this.backend.getCacheKey( renderObject )
  139. ];
  140. return parameters.join();
  141. }
  142. _releasePipeline( object ) {
  143. const pipeline = this.get( object ).pipeline;
  144. //this.bindings.delete( object );
  145. if ( pipeline && -- pipeline.usedTimes === 0 ) {
  146. this.caches.delete( pipeline.cacheKey );
  147. }
  148. return pipeline;
  149. }
  150. _releaseProgram( program ) {
  151. if ( -- program.usedTimes === 0 ) {
  152. const code = program.code;
  153. const stage = program.stage;
  154. this.programs[ stage ].delete( code );
  155. }
  156. }
  157. _needsUpdate( renderObject ) {
  158. const data = this.get( renderObject );
  159. const material = renderObject.material;
  160. let needsUpdate = this.backend.needsUpdate( renderObject );
  161. // check material state
  162. if ( data.material !== material || data.materialVersion !== material.version ||
  163. data.transparent !== material.transparent || data.blending !== material.blending || data.premultipliedAlpha !== material.premultipliedAlpha ||
  164. data.blendSrc !== material.blendSrc || data.blendDst !== material.blendDst || data.blendEquation !== material.blendEquation ||
  165. data.blendSrcAlpha !== material.blendSrcAlpha || data.blendDstAlpha !== material.blendDstAlpha || data.blendEquationAlpha !== material.blendEquationAlpha ||
  166. data.colorWrite !== material.colorWrite ||
  167. data.depthWrite !== material.depthWrite || data.depthTest !== material.depthTest || data.depthFunc !== material.depthFunc ||
  168. data.stencilWrite !== material.stencilWrite || data.stencilFunc !== material.stencilFunc ||
  169. data.stencilFail !== material.stencilFail || data.stencilZFail !== material.stencilZFail || data.stencilZPass !== material.stencilZPass ||
  170. data.stencilFuncMask !== material.stencilFuncMask || data.stencilWriteMask !== material.stencilWriteMask ||
  171. data.side !== material.side
  172. ) {
  173. data.material = material; data.materialVersion = material.version;
  174. data.transparent = material.transparent; data.blending = material.blending; data.premultipliedAlpha = material.premultipliedAlpha;
  175. data.blendSrc = material.blendSrc; data.blendDst = material.blendDst; data.blendEquation = material.blendEquation;
  176. data.blendSrcAlpha = material.blendSrcAlpha; data.blendDstAlpha = material.blendDstAlpha; data.blendEquationAlpha = material.blendEquationAlpha;
  177. data.colorWrite = material.colorWrite;
  178. data.depthWrite = material.depthWrite; data.depthTest = material.depthTest; data.depthFunc = material.depthFunc;
  179. data.stencilWrite = material.stencilWrite; data.stencilFunc = material.stencilFunc;
  180. data.stencilFail = material.stencilFail; data.stencilZFail = material.stencilZFail; data.stencilZPass = material.stencilZPass;
  181. data.stencilFuncMask = material.stencilFuncMask; data.stencilWriteMask = material.stencilWriteMask;
  182. data.side = material.side;
  183. needsUpdate = true;
  184. }
  185. return needsUpdate || data.pipeline !== undefined;
  186. }
  187. }
  188. export default Pipelines;