WebGPUPipelineUtils.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  1. import { BlendColorFactor, OneMinusBlendColorFactor, } from '../../common/Constants.js';
  2. import {
  3. GPUFrontFace, GPUCullMode, GPUColorWriteFlags, GPUCompareFunction, GPUBlendFactor, GPUBlendOperation, GPUIndexFormat, GPUStencilOperation
  4. } from './WebGPUConstants.js';
  5. import {
  6. FrontSide, BackSide, DoubleSide,
  7. NeverDepth, AlwaysDepth, LessDepth, LessEqualDepth, EqualDepth, GreaterEqualDepth, GreaterDepth, NotEqualDepth,
  8. NoBlending, NormalBlending, AdditiveBlending, SubtractiveBlending, MultiplyBlending, CustomBlending,
  9. ZeroFactor, OneFactor, SrcColorFactor, OneMinusSrcColorFactor, SrcAlphaFactor, OneMinusSrcAlphaFactor, DstColorFactor,
  10. OneMinusDstColorFactor, DstAlphaFactor, OneMinusDstAlphaFactor, SrcAlphaSaturateFactor,
  11. AddEquation, SubtractEquation, ReverseSubtractEquation, MinEquation, MaxEquation,
  12. KeepStencilOp, ZeroStencilOp, ReplaceStencilOp, InvertStencilOp, IncrementStencilOp, DecrementStencilOp, IncrementWrapStencilOp, DecrementWrapStencilOp,
  13. NeverStencilFunc, AlwaysStencilFunc, LessStencilFunc, LessEqualStencilFunc, EqualStencilFunc, GreaterEqualStencilFunc, GreaterStencilFunc, NotEqualStencilFunc
  14. } from 'three';
  15. class WebGPUPipelineUtils {
  16. constructor( backend ) {
  17. this.backend = backend;
  18. }
  19. createRenderPipeline( renderObject ) {
  20. const { object, material, geometry, pipeline } = renderObject;
  21. const { vertexProgram, fragmentProgram } = pipeline;
  22. const backend = this.backend;
  23. const device = backend.device;
  24. const utils = backend.utils;
  25. const pipelineData = backend.get( pipeline );
  26. // vertex buffers
  27. const vertexBuffers = backend.attributeUtils.createShaderVertexBuffers( renderObject );
  28. // blending
  29. let blending;
  30. if ( material.transparent === true && material.blending !== NoBlending ) {
  31. blending = {
  32. alpha: this._getAlphaBlend( material ),
  33. color: this._getColorBlend( material )
  34. };
  35. }
  36. // stencil
  37. let stencilFront = {};
  38. if ( material.stencilWrite === true ) {
  39. stencilFront = {
  40. compare: this._getStencilCompare( material ),
  41. failOp: this._getStencilOperation( material.stencilFail ),
  42. depthFailOp: this._getStencilOperation( material.stencilZFail ),
  43. passOp: this._getStencilOperation( material.stencilZPass )
  44. };
  45. }
  46. //
  47. const vertexModule = backend.get( vertexProgram ).module;
  48. const fragmentModule = backend.get( fragmentProgram ).module;
  49. const primitiveState = this._getPrimitiveState( object, geometry, material );
  50. const colorWriteMask = this._getColorWriteMask( material );
  51. const depthCompare = this._getDepthCompare( material );
  52. const colorFormat = utils.getCurrentColorFormat( renderObject.context );
  53. const depthStencilFormat = utils.getCurrentDepthStencilFormat( renderObject.context );
  54. const sampleCount = utils.getSampleCount( renderObject.context );
  55. pipelineData.pipeline = device.createRenderPipeline( {
  56. vertex: Object.assign( {}, vertexModule, { buffers: vertexBuffers } ),
  57. fragment: Object.assign( {}, fragmentModule, { targets: [ {
  58. format: colorFormat,
  59. blend: blending,
  60. writeMask: colorWriteMask
  61. } ] } ),
  62. primitive: primitiveState,
  63. depthStencil: {
  64. format: depthStencilFormat,
  65. depthWriteEnabled: material.depthWrite,
  66. depthCompare: depthCompare,
  67. stencilFront: stencilFront,
  68. stencilBack: {}, // three.js does not provide an API to configure the back function (gl.stencilFuncSeparate() was never used)
  69. stencilReadMask: material.stencilFuncMask,
  70. stencilWriteMask: material.stencilWriteMask
  71. },
  72. multisample: {
  73. count: sampleCount
  74. },
  75. layout: 'auto'
  76. } );
  77. }
  78. createComputePipeline( pipeline ) {
  79. const backend = this.backend;
  80. const device = backend.device;
  81. const computeProgram = backend.get( pipeline.computeProgram ).module;
  82. const pipelineGPU = backend.get( pipeline );
  83. pipelineGPU.pipeline = device.createComputePipeline( {
  84. compute: computeProgram,
  85. layout: 'auto'
  86. } );
  87. }
  88. _getAlphaBlend( material ) {
  89. const blending = material.blending;
  90. const premultipliedAlpha = material.premultipliedAlpha;
  91. let alphaBlend = undefined;
  92. switch ( blending ) {
  93. case NormalBlending:
  94. if ( premultipliedAlpha === false ) {
  95. alphaBlend = {
  96. srcFactor: GPUBlendFactor.One,
  97. dstFactor: GPUBlendFactor.OneMinusSrcAlpha,
  98. operation: GPUBlendOperation.Add
  99. };
  100. }
  101. break;
  102. case AdditiveBlending:
  103. alphaBlend = {
  104. srcFactor: GPUBlendFactor.Zero,
  105. dstFactor: GPUBlendFactor.One,
  106. operation: GPUBlendOperation.Add
  107. };
  108. break;
  109. case SubtractiveBlending:
  110. if ( premultipliedAlpha === true ) {
  111. alphaBlend = {
  112. srcFactor: GPUBlendFactor.OneMinusSrcColor,
  113. dstFactor: GPUBlendFactor.OneMinusSrcAlpha,
  114. operation: GPUBlendOperation.Add
  115. };
  116. }
  117. break;
  118. case MultiplyBlending:
  119. if ( premultipliedAlpha === true ) {
  120. alphaBlend = {
  121. srcFactor: GPUBlendFactor.Zero,
  122. dstFactor: GPUBlendFactor.SrcAlpha,
  123. operation: GPUBlendOperation.Add
  124. };
  125. }
  126. break;
  127. case CustomBlending:
  128. const blendSrcAlpha = material.blendSrcAlpha;
  129. const blendDstAlpha = material.blendDstAlpha;
  130. const blendEquationAlpha = material.blendEquationAlpha;
  131. if ( blendSrcAlpha !== null && blendDstAlpha !== null && blendEquationAlpha !== null ) {
  132. alphaBlend = {
  133. srcFactor: this._getBlendFactor( blendSrcAlpha ),
  134. dstFactor: this._getBlendFactor( blendDstAlpha ),
  135. operation: this._getBlendOperation( blendEquationAlpha )
  136. };
  137. }
  138. break;
  139. default:
  140. console.error( 'THREE.WebGPURenderer: Blending not supported.', blending );
  141. }
  142. return alphaBlend;
  143. }
  144. _getBlendFactor( blend ) {
  145. let blendFactor;
  146. switch ( blend ) {
  147. case ZeroFactor:
  148. blendFactor = GPUBlendFactor.Zero;
  149. break;
  150. case OneFactor:
  151. blendFactor = GPUBlendFactor.One;
  152. break;
  153. case SrcColorFactor:
  154. blendFactor = GPUBlendFactor.SrcColor;
  155. break;
  156. case OneMinusSrcColorFactor:
  157. blendFactor = GPUBlendFactor.OneMinusSrcColor;
  158. break;
  159. case SrcAlphaFactor:
  160. blendFactor = GPUBlendFactor.SrcAlpha;
  161. break;
  162. case OneMinusSrcAlphaFactor:
  163. blendFactor = GPUBlendFactor.OneMinusSrcAlpha;
  164. break;
  165. case DstColorFactor:
  166. blendFactor = GPUBlendFactor.DstColor;
  167. break;
  168. case OneMinusDstColorFactor:
  169. blendFactor = GPUBlendFactor.OneMinusDstColor;
  170. break;
  171. case DstAlphaFactor:
  172. blendFactor = GPUBlendFactor.DstAlpha;
  173. break;
  174. case OneMinusDstAlphaFactor:
  175. blendFactor = GPUBlendFactor.OneMinusDstAlpha;
  176. break;
  177. case SrcAlphaSaturateFactor:
  178. blendFactor = GPUBlendFactor.SrcAlphaSaturated;
  179. break;
  180. case BlendColorFactor:
  181. blendFactor = GPUBlendFactor.BlendColor;
  182. break;
  183. case OneMinusBlendColorFactor:
  184. blendFactor = GPUBlendFactor.OneMinusBlendColor;
  185. break;
  186. default:
  187. console.error( 'THREE.WebGPURenderer: Blend factor not supported.', blend );
  188. }
  189. return blendFactor;
  190. }
  191. _getColorBlend( material ) {
  192. const blending = material.blending;
  193. const premultipliedAlpha = material.premultipliedAlpha;
  194. const colorBlend = {
  195. srcFactor: null,
  196. dstFactor: null,
  197. operation: null
  198. };
  199. switch ( blending ) {
  200. case NormalBlending:
  201. colorBlend.srcFactor = ( premultipliedAlpha === true ) ? GPUBlendFactor.One : GPUBlendFactor.SrcAlpha;
  202. colorBlend.dstFactor = GPUBlendFactor.OneMinusSrcAlpha;
  203. colorBlend.operation = GPUBlendOperation.Add;
  204. break;
  205. case AdditiveBlending:
  206. colorBlend.srcFactor = ( premultipliedAlpha === true ) ? GPUBlendFactor.One : GPUBlendFactor.SrcAlpha;
  207. colorBlend.dstFactor = GPUBlendFactor.One;
  208. colorBlend.operation = GPUBlendOperation.Add;
  209. break;
  210. case SubtractiveBlending:
  211. colorBlend.srcFactor = GPUBlendFactor.Zero;
  212. colorBlend.dstFactor = ( premultipliedAlpha === true ) ? GPUBlendFactor.Zero : GPUBlendFactor.OneMinusSrcColor;
  213. colorBlend.operation = GPUBlendOperation.Add;
  214. break;
  215. case MultiplyBlending:
  216. colorBlend.srcFactor = GPUBlendFactor.Zero;
  217. colorBlend.dstFactor = GPUBlendFactor.SrcColor;
  218. colorBlend.operation = GPUBlendOperation.Add;
  219. break;
  220. case CustomBlending:
  221. colorBlend.srcFactor = this._getBlendFactor( material.blendSrc );
  222. colorBlend.dstFactor = this._getBlendFactor( material.blendDst );
  223. colorBlend.operation = this._getBlendOperation( material.blendEquation );
  224. break;
  225. default:
  226. console.error( 'THREE.WebGPURenderer: Blending not supported.', blending );
  227. }
  228. return colorBlend;
  229. }
  230. _getStencilCompare( material ) {
  231. let stencilCompare;
  232. const stencilFunc = material.stencilFunc;
  233. switch ( stencilFunc ) {
  234. case NeverStencilFunc:
  235. stencilCompare = GPUCompareFunction.Never;
  236. break;
  237. case AlwaysStencilFunc:
  238. stencilCompare = GPUCompareFunction.Always;
  239. break;
  240. case LessStencilFunc:
  241. stencilCompare = GPUCompareFunction.Less;
  242. break;
  243. case LessEqualStencilFunc:
  244. stencilCompare = GPUCompareFunction.LessEqual;
  245. break;
  246. case EqualStencilFunc:
  247. stencilCompare = GPUCompareFunction.Equal;
  248. break;
  249. case GreaterEqualStencilFunc:
  250. stencilCompare = GPUCompareFunction.GreaterEqual;
  251. break;
  252. case GreaterStencilFunc:
  253. stencilCompare = GPUCompareFunction.Greater;
  254. break;
  255. case NotEqualStencilFunc:
  256. stencilCompare = GPUCompareFunction.NotEqual;
  257. break;
  258. default:
  259. console.error( 'THREE.WebGPURenderer: Invalid stencil function.', stencilFunc );
  260. }
  261. return stencilCompare;
  262. }
  263. _getStencilOperation( op ) {
  264. let stencilOperation;
  265. switch ( op ) {
  266. case KeepStencilOp:
  267. stencilOperation = GPUStencilOperation.Keep;
  268. break;
  269. case ZeroStencilOp:
  270. stencilOperation = GPUStencilOperation.Zero;
  271. break;
  272. case ReplaceStencilOp:
  273. stencilOperation = GPUStencilOperation.Replace;
  274. break;
  275. case InvertStencilOp:
  276. stencilOperation = GPUStencilOperation.Invert;
  277. break;
  278. case IncrementStencilOp:
  279. stencilOperation = GPUStencilOperation.IncrementClamp;
  280. break;
  281. case DecrementStencilOp:
  282. stencilOperation = GPUStencilOperation.DecrementClamp;
  283. break;
  284. case IncrementWrapStencilOp:
  285. stencilOperation = GPUStencilOperation.IncrementWrap;
  286. break;
  287. case DecrementWrapStencilOp:
  288. stencilOperation = GPUStencilOperation.DecrementWrap;
  289. break;
  290. default:
  291. console.error( 'THREE.WebGPURenderer: Invalid stencil operation.', stencilOperation );
  292. }
  293. return stencilOperation;
  294. }
  295. _getBlendOperation( blendEquation ) {
  296. let blendOperation;
  297. switch ( blendEquation ) {
  298. case AddEquation:
  299. blendOperation = GPUBlendOperation.Add;
  300. break;
  301. case SubtractEquation:
  302. blendOperation = GPUBlendOperation.Subtract;
  303. break;
  304. case ReverseSubtractEquation:
  305. blendOperation = GPUBlendOperation.ReverseSubtract;
  306. break;
  307. case MinEquation:
  308. blendOperation = GPUBlendOperation.Min;
  309. break;
  310. case MaxEquation:
  311. blendOperation = GPUBlendOperation.Max;
  312. break;
  313. default:
  314. console.error( 'THREE.WebGPUPipelineUtils: Blend equation not supported.', blendEquation );
  315. }
  316. return blendOperation;
  317. }
  318. _getPrimitiveState( object, geometry, material ) {
  319. const descriptor = {};
  320. const utils = this.backend.utils;
  321. descriptor.topology = utils.getPrimitiveTopology( object, material );
  322. if ( object.isLine === true && object.isLineSegments !== true ) {
  323. const count = ( geometry.index ) ? geometry.index.count : geometry.attributes.position.count;
  324. descriptor.stripIndexFormat = ( count > 65535 ) ? GPUIndexFormat.Uint32 : GPUIndexFormat.Uint16; // define data type for primitive restart value
  325. }
  326. switch ( material.side ) {
  327. case FrontSide:
  328. descriptor.frontFace = GPUFrontFace.CW;
  329. descriptor.cullMode = GPUCullMode.Front;
  330. break;
  331. case BackSide:
  332. descriptor.frontFace = GPUFrontFace.CW;
  333. descriptor.cullMode = GPUCullMode.Back;
  334. break;
  335. case DoubleSide:
  336. descriptor.frontFace = GPUFrontFace.CW;
  337. descriptor.cullMode = GPUCullMode.None;
  338. break;
  339. default:
  340. console.error( 'THREE.WebGPUPipelineUtils: Unknown material.side value.', material.side );
  341. break;
  342. }
  343. return descriptor;
  344. }
  345. _getColorWriteMask( material ) {
  346. return ( material.colorWrite === true ) ? GPUColorWriteFlags.All : GPUColorWriteFlags.None;
  347. }
  348. _getDepthCompare( material ) {
  349. let depthCompare;
  350. if ( material.depthTest === false ) {
  351. depthCompare = GPUCompareFunction.Always;
  352. } else {
  353. const depthFunc = material.depthFunc;
  354. switch ( depthFunc ) {
  355. case NeverDepth:
  356. depthCompare = GPUCompareFunction.Never;
  357. break;
  358. case AlwaysDepth:
  359. depthCompare = GPUCompareFunction.Always;
  360. break;
  361. case LessDepth:
  362. depthCompare = GPUCompareFunction.Less;
  363. break;
  364. case LessEqualDepth:
  365. depthCompare = GPUCompareFunction.LessEqual;
  366. break;
  367. case EqualDepth:
  368. depthCompare = GPUCompareFunction.Equal;
  369. break;
  370. case GreaterEqualDepth:
  371. depthCompare = GPUCompareFunction.GreaterEqual;
  372. break;
  373. case GreaterDepth:
  374. depthCompare = GPUCompareFunction.Greater;
  375. break;
  376. case NotEqualDepth:
  377. depthCompare = GPUCompareFunction.NotEqual;
  378. break;
  379. default:
  380. console.error( 'THREE.WebGPUPipelineUtils: Invalid depth function.', depthFunc );
  381. }
  382. }
  383. return depthCompare;
  384. }
  385. }
  386. export default WebGPUPipelineUtils;