WebGPURenderPipeline.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673
  1. import { GPUIndexFormat, GPUCompareFunction, GPUFrontFace, GPUCullMode, GPUBlendFactor, GPUBlendOperation, BlendColorFactor, OneMinusBlendColorFactor, GPUColorWriteFlags, GPUStencilOperation, GPUInputStepMode } from './constants.js';
  2. import {
  3. Float16BufferAttribute,
  4. FrontSide, BackSide, DoubleSide,
  5. NeverDepth, AlwaysDepth, LessDepth, LessEqualDepth, EqualDepth, GreaterEqualDepth, GreaterDepth, NotEqualDepth,
  6. NeverStencilFunc, AlwaysStencilFunc, LessStencilFunc, LessEqualStencilFunc, EqualStencilFunc, GreaterEqualStencilFunc, GreaterStencilFunc, NotEqualStencilFunc,
  7. KeepStencilOp, ZeroStencilOp, ReplaceStencilOp, InvertStencilOp, IncrementStencilOp, DecrementStencilOp, IncrementWrapStencilOp, DecrementWrapStencilOp,
  8. NoBlending, NormalBlending, AdditiveBlending, SubtractiveBlending, MultiplyBlending, CustomBlending,
  9. AddEquation, SubtractEquation, ReverseSubtractEquation, MinEquation, MaxEquation,
  10. ZeroFactor, OneFactor, SrcColorFactor, OneMinusSrcColorFactor, SrcAlphaFactor, OneMinusSrcAlphaFactor, DstAlphaFactor, OneMinusDstAlphaFactor, DstColorFactor, OneMinusDstColorFactor, SrcAlphaSaturateFactor
  11. } from 'three';
  12. const typedArraysToVertexFormatPrefix = new Map( [
  13. [ Int8Array, [ 'sint8', 'snorm8' ]],
  14. [ Uint8Array, [ 'uint8', 'unorm8' ]],
  15. [ Int16Array, [ 'sint16', 'snorm16' ]],
  16. [ Uint16Array, [ 'uint16', 'unorm16' ]],
  17. [ Int32Array, [ 'sint32', 'snorm32' ]],
  18. [ Uint32Array, [ 'uint32', 'unorm32' ]],
  19. [ Float32Array, [ 'float32', ]],
  20. ] );
  21. const typedAttributeToVertexFormatPrefix = new Map( [
  22. [ Float16BufferAttribute, [ 'float16', ]],
  23. ] );
  24. const typeArraysToVertexFormatPrefixForItemSize1 = new Map( [
  25. [ Int32Array, 'sint32' ],
  26. [ Uint32Array, 'uint32' ],
  27. [ Float32Array, 'float32' ]
  28. ] );
  29. class WebGPURenderPipeline {
  30. constructor( device, utils ) {
  31. this.cacheKey = null;
  32. this.shaderAttributes = null;
  33. this.stageVertex = null;
  34. this.stageFragment = null;
  35. this.usedTimes = 0;
  36. this._device = device;
  37. this._utils = utils;
  38. }
  39. init( cacheKey, stageVertex, stageFragment, renderObject, nodeBuilder ) {
  40. const { object, material, geometry } = renderObject;
  41. // determine shader attributes
  42. const shaderAttributes = this._getShaderAttributes( nodeBuilder, geometry );
  43. // vertex buffers
  44. const vertexBuffers = [];
  45. for ( const attribute of shaderAttributes ) {
  46. const name = attribute.name;
  47. const geometryAttribute = geometry.getAttribute( name );
  48. const stepMode = ( geometryAttribute !== undefined && geometryAttribute.isInstancedBufferAttribute ) ? GPUInputStepMode.Instance : GPUInputStepMode.Vertex;
  49. vertexBuffers.push( {
  50. arrayStride: attribute.arrayStride,
  51. attributes: [ { shaderLocation: attribute.slot, offset: attribute.offset, format: attribute.format } ],
  52. stepMode: stepMode
  53. } );
  54. }
  55. this.cacheKey = cacheKey;
  56. this.shaderAttributes = shaderAttributes;
  57. this.stageVertex = stageVertex;
  58. this.stageFragment = stageFragment;
  59. // blending
  60. let alphaBlend = {};
  61. let colorBlend = {};
  62. if ( material.transparent === true && material.blending !== NoBlending ) {
  63. alphaBlend = this._getAlphaBlend( material );
  64. colorBlend = this._getColorBlend( material );
  65. }
  66. // stencil
  67. let stencilFront = {};
  68. if ( material.stencilWrite === true ) {
  69. stencilFront = {
  70. compare: this._getStencilCompare( material ),
  71. failOp: this._getStencilOperation( material.stencilFail ),
  72. depthFailOp: this._getStencilOperation( material.stencilZFail ),
  73. passOp: this._getStencilOperation( material.stencilZPass )
  74. };
  75. }
  76. //
  77. const primitiveState = this._getPrimitiveState( object, geometry, material );
  78. const colorWriteMask = this._getColorWriteMask( material );
  79. const depthCompare = this._getDepthCompare( material );
  80. const colorFormat = this._utils.getCurrentColorFormat();
  81. const depthStencilFormat = this._utils.getCurrentDepthStencilFormat();
  82. const sampleCount = this._utils.getSampleCount();
  83. this.pipeline = this._device.createRenderPipeline( {
  84. vertex: Object.assign( {}, stageVertex.stage, { buffers: vertexBuffers } ),
  85. fragment: Object.assign( {}, stageFragment.stage, { targets: [ {
  86. format: colorFormat,
  87. blend: {
  88. alpha: alphaBlend,
  89. color: colorBlend
  90. },
  91. writeMask: colorWriteMask
  92. } ] } ),
  93. primitive: primitiveState,
  94. depthStencil: {
  95. format: depthStencilFormat,
  96. depthWriteEnabled: material.depthWrite,
  97. depthCompare: depthCompare,
  98. stencilFront: stencilFront,
  99. stencilBack: {}, // three.js does not provide an API to configure the back function (gl.stencilFuncSeparate() was never used)
  100. stencilReadMask: material.stencilFuncMask,
  101. stencilWriteMask: material.stencilWriteMask
  102. },
  103. multisample: {
  104. count: sampleCount
  105. },
  106. layout: 'auto'
  107. } );
  108. }
  109. _getAlphaBlend( material ) {
  110. const blending = material.blending;
  111. const premultipliedAlpha = material.premultipliedAlpha;
  112. let alphaBlend = undefined;
  113. switch ( blending ) {
  114. case NormalBlending:
  115. if ( premultipliedAlpha === false ) {
  116. alphaBlend = {
  117. srcFactor: GPUBlendFactor.One,
  118. dstFactor: GPUBlendFactor.OneMinusSrcAlpha,
  119. operation: GPUBlendOperation.Add
  120. };
  121. }
  122. break;
  123. case AdditiveBlending:
  124. alphaBlend = {
  125. srcFactor: GPUBlendFactor.Zero,
  126. dstFactor: GPUBlendFactor.One,
  127. operation: GPUBlendOperation.Add
  128. };
  129. break;
  130. case SubtractiveBlending:
  131. if ( premultipliedAlpha === true ) {
  132. alphaBlend = {
  133. srcFactor: GPUBlendFactor.OneMinusSrcColor,
  134. dstFactor: GPUBlendFactor.OneMinusSrcAlpha,
  135. operation: GPUBlendOperation.Add
  136. };
  137. }
  138. break;
  139. case MultiplyBlending:
  140. if ( premultipliedAlpha === true ) {
  141. alphaBlend = {
  142. srcFactor: GPUBlendFactor.Zero,
  143. dstFactor: GPUBlendFactor.SrcAlpha,
  144. operation: GPUBlendOperation.Add
  145. };
  146. }
  147. break;
  148. case CustomBlending:
  149. const blendSrcAlpha = material.blendSrcAlpha;
  150. const blendDstAlpha = material.blendDstAlpha;
  151. const blendEquationAlpha = material.blendEquationAlpha;
  152. if ( blendSrcAlpha !== null && blendDstAlpha !== null && blendEquationAlpha !== null ) {
  153. alphaBlend = {
  154. srcFactor: this._getBlendFactor( blendSrcAlpha ),
  155. dstFactor: this._getBlendFactor( blendDstAlpha ),
  156. operation: this._getBlendOperation( blendEquationAlpha )
  157. };
  158. }
  159. break;
  160. default:
  161. console.error( 'THREE.WebGPURenderer: Blending not supported.', blending );
  162. }
  163. return alphaBlend;
  164. }
  165. _getBlendFactor( blend ) {
  166. let blendFactor;
  167. switch ( blend ) {
  168. case ZeroFactor:
  169. blendFactor = GPUBlendFactor.Zero;
  170. break;
  171. case OneFactor:
  172. blendFactor = GPUBlendFactor.One;
  173. break;
  174. case SrcColorFactor:
  175. blendFactor = GPUBlendFactor.SrcColor;
  176. break;
  177. case OneMinusSrcColorFactor:
  178. blendFactor = GPUBlendFactor.OneMinusSrcColor;
  179. break;
  180. case SrcAlphaFactor:
  181. blendFactor = GPUBlendFactor.SrcAlpha;
  182. break;
  183. case OneMinusSrcAlphaFactor:
  184. blendFactor = GPUBlendFactor.OneMinusSrcAlpha;
  185. break;
  186. case DstColorFactor:
  187. blendFactor = GPUBlendFactor.DstColor;
  188. break;
  189. case OneMinusDstColorFactor:
  190. blendFactor = GPUBlendFactor.OneMinusDstColor;
  191. break;
  192. case DstAlphaFactor:
  193. blendFactor = GPUBlendFactor.DstAlpha;
  194. break;
  195. case OneMinusDstAlphaFactor:
  196. blendFactor = GPUBlendFactor.OneMinusDstAlpha;
  197. break;
  198. case SrcAlphaSaturateFactor:
  199. blendFactor = GPUBlendFactor.SrcAlphaSaturated;
  200. break;
  201. case BlendColorFactor:
  202. blendFactor = GPUBlendFactor.BlendColor;
  203. break;
  204. case OneMinusBlendColorFactor:
  205. blendFactor = GPUBlendFactor.OneMinusBlendColor;
  206. break;
  207. default:
  208. console.error( 'THREE.WebGPURenderer: Blend factor not supported.', blend );
  209. }
  210. return blendFactor;
  211. }
  212. _getBlendOperation( blendEquation ) {
  213. let blendOperation;
  214. switch ( blendEquation ) {
  215. case AddEquation:
  216. blendOperation = GPUBlendOperation.Add;
  217. break;
  218. case SubtractEquation:
  219. blendOperation = GPUBlendOperation.Subtract;
  220. break;
  221. case ReverseSubtractEquation:
  222. blendOperation = GPUBlendOperation.ReverseSubtract;
  223. break;
  224. case MinEquation:
  225. blendOperation = GPUBlendOperation.Min;
  226. break;
  227. case MaxEquation:
  228. blendOperation = GPUBlendOperation.Max;
  229. break;
  230. default:
  231. console.error( 'THREE.WebGPURenderer: Blend equation not supported.', blendEquation );
  232. }
  233. return blendOperation;
  234. }
  235. _getColorBlend( material ) {
  236. const blending = material.blending;
  237. const premultipliedAlpha = material.premultipliedAlpha;
  238. const colorBlend = {
  239. srcFactor: null,
  240. dstFactor: null,
  241. operation: null
  242. };
  243. switch ( blending ) {
  244. case NormalBlending:
  245. colorBlend.srcFactor = ( premultipliedAlpha === true ) ? GPUBlendFactor.One : GPUBlendFactor.SrcAlpha;
  246. colorBlend.dstFactor = GPUBlendFactor.OneMinusSrcAlpha;
  247. colorBlend.operation = GPUBlendOperation.Add;
  248. break;
  249. case AdditiveBlending:
  250. colorBlend.srcFactor = ( premultipliedAlpha === true ) ? GPUBlendFactor.One : GPUBlendFactor.SrcAlpha;
  251. colorBlend.dstFactor = GPUBlendFactor.One;
  252. colorBlend.operation = GPUBlendOperation.Add;
  253. break;
  254. case SubtractiveBlending:
  255. colorBlend.srcFactor = GPUBlendFactor.Zero;
  256. colorBlend.dstFactor = ( premultipliedAlpha === true ) ? GPUBlendFactor.Zero : GPUBlendFactor.OneMinusSrcColor;
  257. colorBlend.operation = GPUBlendOperation.Add;
  258. break;
  259. case MultiplyBlending:
  260. colorBlend.srcFactor = GPUBlendFactor.Zero;
  261. colorBlend.dstFactor = GPUBlendFactor.SrcColor;
  262. colorBlend.operation = GPUBlendOperation.Add;
  263. break;
  264. case CustomBlending:
  265. colorBlend.srcFactor = this._getBlendFactor( material.blendSrc );
  266. colorBlend.dstFactor = this._getBlendFactor( material.blendDst );
  267. colorBlend.operation = this._getBlendOperation( material.blendEquation );
  268. break;
  269. default:
  270. console.error( 'THREE.WebGPURenderer: Blending not supported.', blending );
  271. }
  272. return colorBlend;
  273. }
  274. _getColorWriteMask( material ) {
  275. return ( material.colorWrite === true ) ? GPUColorWriteFlags.All : GPUColorWriteFlags.None;
  276. }
  277. _getDepthCompare( material ) {
  278. let depthCompare;
  279. if ( material.depthTest === false ) {
  280. depthCompare = GPUCompareFunction.Always;
  281. } else {
  282. const depthFunc = material.depthFunc;
  283. switch ( depthFunc ) {
  284. case NeverDepth:
  285. depthCompare = GPUCompareFunction.Never;
  286. break;
  287. case AlwaysDepth:
  288. depthCompare = GPUCompareFunction.Always;
  289. break;
  290. case LessDepth:
  291. depthCompare = GPUCompareFunction.Less;
  292. break;
  293. case LessEqualDepth:
  294. depthCompare = GPUCompareFunction.LessEqual;
  295. break;
  296. case EqualDepth:
  297. depthCompare = GPUCompareFunction.Equal;
  298. break;
  299. case GreaterEqualDepth:
  300. depthCompare = GPUCompareFunction.GreaterEqual;
  301. break;
  302. case GreaterDepth:
  303. depthCompare = GPUCompareFunction.Greater;
  304. break;
  305. case NotEqualDepth:
  306. depthCompare = GPUCompareFunction.NotEqual;
  307. break;
  308. default:
  309. console.error( 'THREE.WebGPURenderer: Invalid depth function.', depthFunc );
  310. }
  311. }
  312. return depthCompare;
  313. }
  314. _getPrimitiveState( object, geometry, material ) {
  315. const descriptor = {};
  316. descriptor.topology = this._utils.getPrimitiveTopology( object, material );
  317. if ( object.isLine === true && object.isLineSegments !== true ) {
  318. const count = ( geometry.index ) ? geometry.index.count : geometry.attributes.position.count;
  319. descriptor.stripIndexFormat = ( count > 65535 ) ? GPUIndexFormat.Uint32 : GPUIndexFormat.Uint16; // define data type for primitive restart value
  320. }
  321. switch ( material.side ) {
  322. case FrontSide:
  323. descriptor.frontFace = GPUFrontFace.CW;
  324. descriptor.cullMode = GPUCullMode.Front;
  325. break;
  326. case BackSide:
  327. descriptor.frontFace = GPUFrontFace.CW;
  328. descriptor.cullMode = GPUCullMode.Back;
  329. break;
  330. case DoubleSide:
  331. descriptor.frontFace = GPUFrontFace.CW;
  332. descriptor.cullMode = GPUCullMode.None;
  333. break;
  334. default:
  335. console.error( 'THREE.WebGPURenderer: Unknown Material.side value.', material.side );
  336. break;
  337. }
  338. return descriptor;
  339. }
  340. _getStencilCompare( material ) {
  341. let stencilCompare;
  342. const stencilFunc = material.stencilFunc;
  343. switch ( stencilFunc ) {
  344. case NeverStencilFunc:
  345. stencilCompare = GPUCompareFunction.Never;
  346. break;
  347. case AlwaysStencilFunc:
  348. stencilCompare = GPUCompareFunction.Always;
  349. break;
  350. case LessStencilFunc:
  351. stencilCompare = GPUCompareFunction.Less;
  352. break;
  353. case LessEqualStencilFunc:
  354. stencilCompare = GPUCompareFunction.LessEqual;
  355. break;
  356. case EqualStencilFunc:
  357. stencilCompare = GPUCompareFunction.Equal;
  358. break;
  359. case GreaterEqualStencilFunc:
  360. stencilCompare = GPUCompareFunction.GreaterEqual;
  361. break;
  362. case GreaterStencilFunc:
  363. stencilCompare = GPUCompareFunction.Greater;
  364. break;
  365. case NotEqualStencilFunc:
  366. stencilCompare = GPUCompareFunction.NotEqual;
  367. break;
  368. default:
  369. console.error( 'THREE.WebGPURenderer: Invalid stencil function.', stencilFunc );
  370. }
  371. return stencilCompare;
  372. }
  373. _getStencilOperation( op ) {
  374. let stencilOperation;
  375. switch ( op ) {
  376. case KeepStencilOp:
  377. stencilOperation = GPUStencilOperation.Keep;
  378. break;
  379. case ZeroStencilOp:
  380. stencilOperation = GPUStencilOperation.Zero;
  381. break;
  382. case ReplaceStencilOp:
  383. stencilOperation = GPUStencilOperation.Replace;
  384. break;
  385. case InvertStencilOp:
  386. stencilOperation = GPUStencilOperation.Invert;
  387. break;
  388. case IncrementStencilOp:
  389. stencilOperation = GPUStencilOperation.IncrementClamp;
  390. break;
  391. case DecrementStencilOp:
  392. stencilOperation = GPUStencilOperation.DecrementClamp;
  393. break;
  394. case IncrementWrapStencilOp:
  395. stencilOperation = GPUStencilOperation.IncrementWrap;
  396. break;
  397. case DecrementWrapStencilOp:
  398. stencilOperation = GPUStencilOperation.DecrementWrap;
  399. break;
  400. default:
  401. console.error( 'THREE.WebGPURenderer: Invalid stencil operation.', stencilOperation );
  402. }
  403. return stencilOperation;
  404. }
  405. _getVertexFormat( geometryAttribute ) {
  406. const { itemSize, normalized } = geometryAttribute;
  407. const ArrayType = geometryAttribute.array.constructor;
  408. const AttributeType = geometryAttribute.constructor;
  409. let format;
  410. if ( itemSize == 1 ) {
  411. format = typeArraysToVertexFormatPrefixForItemSize1.get( ArrayType );
  412. } else {
  413. const prefixOptions = typedAttributeToVertexFormatPrefix.get( AttributeType ) || typedArraysToVertexFormatPrefix.get( ArrayType );
  414. const prefix = prefixOptions[ normalized ? 1 : 0 ];
  415. if ( prefix ) {
  416. const bytesPerUnit = ArrayType.BYTES_PER_ELEMENT * itemSize;
  417. const paddedBytesPerUnit = Math.floor( ( bytesPerUnit + 3 ) / 4 ) * 4;
  418. const paddedItemSize = paddedBytesPerUnit / ArrayType.BYTES_PER_ELEMENT;
  419. if ( paddedItemSize % 1 ) {
  420. throw new Error( 'THREE.WebGPURenderer: Bad vertex format item size.' );
  421. }
  422. format = `${prefix}x${paddedItemSize}`;
  423. }
  424. }
  425. if ( ! format ) {
  426. console.error( 'THREE.WebGPURenderer: Vertex format not supported yet.' );
  427. }
  428. return format;
  429. }
  430. _getShaderAttributes( nodeBuilder, geometry ) {
  431. const nodeAttributes = nodeBuilder.attributes;
  432. const attributes = [];
  433. for ( let slot = 0; slot < nodeAttributes.length; slot ++ ) {
  434. const nodeAttribute = nodeAttributes[ slot ];
  435. const name = nodeAttribute.name;
  436. const geometryAttribute = geometry.getAttribute( name );
  437. const bytesPerElement = geometryAttribute.array.BYTES_PER_ELEMENT;
  438. const format = this._getVertexFormat( geometryAttribute );
  439. let arrayStride = geometryAttribute.itemSize * bytesPerElement;
  440. let offset = 0;
  441. if ( geometryAttribute.isInterleavedBufferAttribute === true ) {
  442. // @TODO: It can be optimized for "vertexBuffers" on RenderPipeline
  443. arrayStride = geometryAttribute.data.stride * bytesPerElement;
  444. offset = geometryAttribute.offset * bytesPerElement;
  445. }
  446. attributes.push( {
  447. name,
  448. arrayStride,
  449. offset,
  450. format,
  451. slot
  452. } );
  453. }
  454. return attributes;
  455. }
  456. }
  457. export default WebGPURenderPipeline;