WebGPURenderPipelines.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. import WebGPURenderPipeline from './WebGPURenderPipeline.js';
  2. import WebGPUProgrammableStage from './WebGPUProgrammableStage.js';
  3. class WebGPURenderPipelines {
  4. constructor( device, nodes, utils ) {
  5. this.device = device;
  6. this.nodes = nodes;
  7. this.utils = utils;
  8. this.bindings = null;
  9. this.pipelines = [];
  10. this.cache = new WeakMap();
  11. this.stages = {
  12. vertex: new Map(),
  13. fragment: new Map()
  14. };
  15. }
  16. get( renderObject ) {
  17. const device = this.device;
  18. const cache = this._getCache( renderObject );
  19. let currentPipeline = cache.currentPipeline;
  20. if ( this._needsUpdate( renderObject ) ) {
  21. // release previous cache
  22. this._releasePipeline( renderObject );
  23. // get shader
  24. const nodeBuilder = this.nodes.get( renderObject );
  25. // programmable stages
  26. let stageVertex = this.stages.vertex.get( nodeBuilder.vertexShader );
  27. if ( stageVertex === undefined ) {
  28. stageVertex = new WebGPUProgrammableStage( device, nodeBuilder.vertexShader, 'vertex' );
  29. this.stages.vertex.set( nodeBuilder.vertexShader, stageVertex );
  30. }
  31. let stageFragment = this.stages.fragment.get( nodeBuilder.fragmentShader );
  32. if ( stageFragment === undefined ) {
  33. stageFragment = new WebGPUProgrammableStage( device, nodeBuilder.fragmentShader, 'fragment' );
  34. this.stages.fragment.set( nodeBuilder.fragmentShader, stageFragment );
  35. }
  36. // determine render pipeline
  37. currentPipeline = this._acquirePipeline( stageVertex, stageFragment, renderObject );
  38. cache.currentPipeline = currentPipeline;
  39. // keep track of all used times
  40. currentPipeline.usedTimes ++;
  41. stageVertex.usedTimes ++;
  42. stageFragment.usedTimes ++;
  43. }
  44. return currentPipeline;
  45. }
  46. remove( renderObject ) {
  47. this._releasePipeline( renderObject );
  48. }
  49. dispose() {
  50. this.pipelines = [];
  51. this.cache = new WeakMap();
  52. this.shaderModules = {
  53. vertex: new Map(),
  54. fragment: new Map()
  55. };
  56. }
  57. _acquirePipeline( stageVertex, stageFragment, renderObject ) {
  58. let pipeline;
  59. const pipelines = this.pipelines;
  60. // check for existing pipeline
  61. const cacheKey = this._computeCacheKey( stageVertex, stageFragment, renderObject );
  62. for ( let i = 0, il = pipelines.length; i < il; i ++ ) {
  63. const preexistingPipeline = pipelines[ i ];
  64. if ( preexistingPipeline.cacheKey === cacheKey ) {
  65. pipeline = preexistingPipeline;
  66. break;
  67. }
  68. }
  69. if ( pipeline === undefined ) {
  70. pipeline = new WebGPURenderPipeline( this.device, this.utils );
  71. pipeline.init( cacheKey, stageVertex, stageFragment, renderObject, this.nodes.get( renderObject ) );
  72. pipelines.push( pipeline );
  73. }
  74. return pipeline;
  75. }
  76. _computeCacheKey( stageVertex, stageFragment, renderObject ) {
  77. const { object, material } = renderObject;
  78. const utils = this.utils;
  79. const parameters = [
  80. stageVertex.id, stageFragment.id,
  81. material.transparent, material.blending, material.premultipliedAlpha,
  82. material.blendSrc, material.blendDst, material.blendEquation,
  83. material.blendSrcAlpha, material.blendDstAlpha, material.blendEquationAlpha,
  84. material.colorWrite,
  85. material.depthWrite, material.depthTest, material.depthFunc,
  86. material.stencilWrite, material.stencilFunc,
  87. material.stencilFail, material.stencilZFail, material.stencilZPass,
  88. material.stencilFuncMask, material.stencilWriteMask,
  89. material.side,
  90. utils.getSampleCount(),
  91. utils.getCurrentColorSpace(), utils.getCurrentColorFormat(), utils.getCurrentDepthStencilFormat(),
  92. utils.getPrimitiveTopology( object, material )
  93. ];
  94. return parameters.join();
  95. }
  96. _getCache( renderObject ) {
  97. let cache = this.cache.get( renderObject );
  98. if ( cache === undefined ) {
  99. cache = {};
  100. this.cache.set( renderObject, cache );
  101. }
  102. return cache;
  103. }
  104. _releasePipeline( renderObject ) {
  105. const cache = this._getCache( renderObject );
  106. const pipeline = cache.currentPipeline;
  107. delete cache.currentPipeline;
  108. this.bindings.remove( renderObject );
  109. if ( pipeline && -- pipeline.usedTimes === 0 ) {
  110. const pipelines = this.pipelines;
  111. const i = pipelines.indexOf( pipeline );
  112. pipelines[ i ] = pipelines[ pipelines.length - 1 ];
  113. pipelines.pop();
  114. this._releaseStage( pipeline.stageVertex );
  115. this._releaseStage( pipeline.stageFragment );
  116. }
  117. }
  118. _releaseStage( stage ) {
  119. if ( -- stage.usedTimes === 0 ) {
  120. const code = stage.code;
  121. const type = stage.type;
  122. this.stages[ type ].delete( code );
  123. }
  124. }
  125. _needsUpdate( renderObject ) {
  126. const cache = this._getCache( renderObject );
  127. const material = renderObject.material;
  128. let needsUpdate = false;
  129. // check pipeline state
  130. if ( cache.currentPipeline === undefined ) needsUpdate = true;
  131. // check material state
  132. if ( cache.material !== material || cache.materialVersion !== material.version ||
  133. cache.transparent !== material.transparent || cache.blending !== material.blending || cache.premultipliedAlpha !== material.premultipliedAlpha ||
  134. cache.blendSrc !== material.blendSrc || cache.blendDst !== material.blendDst || cache.blendEquation !== material.blendEquation ||
  135. cache.blendSrcAlpha !== material.blendSrcAlpha || cache.blendDstAlpha !== material.blendDstAlpha || cache.blendEquationAlpha !== material.blendEquationAlpha ||
  136. cache.colorWrite !== material.colorWrite ||
  137. cache.depthWrite !== material.depthWrite || cache.depthTest !== material.depthTest || cache.depthFunc !== material.depthFunc ||
  138. cache.stencilWrite !== material.stencilWrite || cache.stencilFunc !== material.stencilFunc ||
  139. cache.stencilFail !== material.stencilFail || cache.stencilZFail !== material.stencilZFail || cache.stencilZPass !== material.stencilZPass ||
  140. cache.stencilFuncMask !== material.stencilFuncMask || cache.stencilWriteMask !== material.stencilWriteMask ||
  141. cache.side !== material.side
  142. ) {
  143. cache.material = material; cache.materialVersion = material.version;
  144. cache.transparent = material.transparent; cache.blending = material.blending; cache.premultipliedAlpha = material.premultipliedAlpha;
  145. cache.blendSrc = material.blendSrc; cache.blendDst = material.blendDst; cache.blendEquation = material.blendEquation;
  146. cache.blendSrcAlpha = material.blendSrcAlpha; cache.blendDstAlpha = material.blendDstAlpha; cache.blendEquationAlpha = material.blendEquationAlpha;
  147. cache.colorWrite = material.colorWrite;
  148. cache.depthWrite = material.depthWrite; cache.depthTest = material.depthTest; cache.depthFunc = material.depthFunc;
  149. cache.stencilWrite = material.stencilWrite; cache.stencilFunc = material.stencilFunc;
  150. cache.stencilFail = material.stencilFail; cache.stencilZFail = material.stencilZFail; cache.stencilZPass = material.stencilZPass;
  151. cache.stencilFuncMask = material.stencilFuncMask; cache.stencilWriteMask = material.stencilWriteMask;
  152. cache.side = material.side;
  153. needsUpdate = true;
  154. }
  155. // check renderer state
  156. const utils = this.utils;
  157. const sampleCount = utils.getSampleCount();
  158. const colorSpace = utils.getCurrentColorSpace();
  159. const colorFormat = utils.getCurrentColorFormat();
  160. const depthStencilFormat = utils.getCurrentDepthStencilFormat();
  161. if ( cache.sampleCount !== sampleCount || cache.colorSpace !== colorSpace ||
  162. cache.colorFormat !== colorFormat || cache.depthStencilFormat !== depthStencilFormat ) {
  163. cache.sampleCount = sampleCount;
  164. cache.colorSpace = colorSpace;
  165. cache.colorFormat = colorFormat;
  166. cache.depthStencilFormat = depthStencilFormat;
  167. needsUpdate = true;
  168. }
  169. return needsUpdate;
  170. }
  171. }
  172. export default WebGPURenderPipelines;