WebGPURenderPipelines.js 8.1 KB

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