webgpuCacheRenderPipeline.ts 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949
  1. import { Constants } from "../constants";
  2. import * as WebGPUConstants from './webgpuConstants';
  3. import { Effect } from "../../Materials/effect";
  4. import { InternalTexture } from "../../Materials/Textures/internalTexture";
  5. import { VertexBuffer } from "../../Meshes/buffer";
  6. import { DataBuffer } from "../../Meshes/dataBuffer";
  7. import { Nullable } from "../../types";
  8. import { WebGPUHardwareTexture } from "./webgpuHardwareTexture";
  9. import { WebGPUPipelineContext } from "./webgpuPipelineContext";
  10. enum StatePosition {
  11. ShaderStage = 0,
  12. RasterizationState = 1,
  13. //DepthBias = 2, // not used, so remove it to improve perf
  14. //DepthBiasClamp = 3, // not used, so remove it to improve perf
  15. DepthBiasSlopeScale = 2,
  16. MRTAttachments1 = 3,
  17. MRTAttachments2 = 4,
  18. ColorStates = 5,
  19. DepthStencilState = 6,
  20. StencilReadMask = 7,
  21. StencilWriteMask = 8,
  22. VertexState = 9, // vertex state will consume positions 9, 10, ... depending on the number of vertex inputs
  23. NumStates = 10
  24. }
  25. const textureFormatToIndex: { [name: string]: number } = {
  26. "" : 0,
  27. "r8unorm": 1,
  28. "r8snorm": 2,
  29. "r8uint": 3,
  30. "r8sint": 4,
  31. "r16uint": 5,
  32. "r16sint": 6,
  33. "r16float": 7,
  34. "rg8unorm": 8,
  35. "rg8snorm": 9,
  36. "rg8uint": 10,
  37. "rg8sint": 11,
  38. "r32uint": 12,
  39. "r32sint": 13,
  40. "r32float": 14,
  41. "rg16uint": 15,
  42. "rg16sint": 16,
  43. "rg16float": 17,
  44. "rgba8unorm": 18,
  45. "rgba8unorm-srgb": 19,
  46. "rgba8snorm": 20,
  47. "rgba8uint": 21,
  48. "rgba8sint": 22,
  49. "bgra8unorm": 23,
  50. "bgra8unorm-srgb": 24,
  51. "rgb9e5ufloat": 25,
  52. "rgb10a2unorm": 26,
  53. "rg11b10ufloat": 27,
  54. "rg32uint": 28,
  55. "rg32sint": 29,
  56. "rg32float": 30,
  57. "rgba16uint": 31,
  58. "rgba16sint": 32,
  59. "rgba16float": 33,
  60. "rgba32uint": 34,
  61. "rgba32sint": 35,
  62. "rgba32float": 36,
  63. "stencil8": 37,
  64. "depth16unorm": 38,
  65. "depth24plus": 39,
  66. "depth24plus-stencil8": 40,
  67. "depth32float": 41,
  68. "bc1-rgba-unorm": 42,
  69. "bc1-rgba-unorm-srgb": 43,
  70. "bc2-rgba-unorm": 44,
  71. "bc2-rgba-unorm-srgb": 45,
  72. "bc3-rgba-unorm": 46,
  73. "bc3-rgba-unorm-srgb": 47,
  74. "bc4-r-unorm": 48,
  75. "bc4-r-snorm": 49,
  76. "bc5-rg-unorm": 50,
  77. "bc5-rg-snorm": 51,
  78. "bc6h-rgb-ufloat": 52,
  79. "bc6h-rgb-float": 53,
  80. "bc7-rgba-unorm": 54,
  81. "bc7-rgba-unorm-srgb": 55,
  82. "depth24unorm-stencil8": 56,
  83. "depth32float-stencil8": 57,
  84. };
  85. const alphaBlendFactorToIndex: { [name: number]: number } = {
  86. 0: 1, // Zero
  87. 1: 2, // One
  88. 0x0300: 3, // SrcColor
  89. 0x0301: 4, // OneMinusSrcColor
  90. 0x0302: 5, // SrcAlpha
  91. 0x0303: 6, // OneMinusSrcAlpha
  92. 0x0304: 7, // DstAlpha
  93. 0x0305: 8, // OneMinusDstAlpha
  94. 0x0306: 9, // DstColor
  95. 0x0307: 10, // OneMinusDstColor
  96. 0x0308: 11, // SrcAlphaSaturated
  97. 0x8001: 12, // BlendColor
  98. 0x8002: 13, // OneMinusBlendColor
  99. 0x8003: 12, // BlendColor (alpha)
  100. 0x8004: 13, // OneMinusBlendColor (alpha)
  101. };
  102. const stencilOpToIndex: { [name: number]: number } = {
  103. 0x0000: 0, // ZERO
  104. 0x1E00: 1, // KEEP
  105. 0x1E01: 2, // REPLACE
  106. 0x1E02: 3, // INCR
  107. 0x1E03: 4, // DECR
  108. 0x150A: 5, // INVERT
  109. 0x8507: 6, // INCR_WRAP
  110. 0x8508: 7, // DECR_WRAP
  111. };
  112. /** @hidden */
  113. export class WebGPUCacheRenderPipeline {
  114. public static NumCacheHitWithoutHash = 0;
  115. public static NumCacheHitWithHash = 0;
  116. public static NumCacheMiss = 0;
  117. public static NumPipelineCreationLastFrame = 0;
  118. public disabled: boolean;
  119. private static _Cache: { [hash: string]: GPURenderPipeline } = {};
  120. private static _NumPipelineCreationCurrentFrame = 0;
  121. private _device: GPUDevice;
  122. private _states: string[];
  123. private _isDirty: boolean;
  124. private _currentRenderPipeline: GPURenderPipeline;
  125. private _emptyVertexBuffer: VertexBuffer;
  126. private _shaderId: number;
  127. private _alphaToCoverageEnabled: boolean;
  128. private _frontFace: number;
  129. private _cullEnabled: boolean;
  130. private _cullFace: number;
  131. private _clampDepth: boolean;
  132. private _rasterizationState: number;
  133. private _depthBias: number;
  134. private _depthBiasClamp: number;
  135. private _depthBiasSlopeScale: number;
  136. private _colorFormat: number;
  137. private _webgpuColorFormat: GPUTextureFormat;
  138. private _mrtAttachments1: number;
  139. private _mrtAttachments2: number;
  140. private _mrtFormats: GPUTextureFormat[];
  141. private _alphaBlendEnabled: boolean;
  142. private _alphaBlendFuncParams: Array<Nullable<number>>;
  143. private _alphaBlendEqParams: Array<Nullable<number>>;
  144. private _writeMask: number;
  145. private _colorStates: number;
  146. private _depthStencilFormat: number;
  147. private _webgpuDepthStencilFormat: GPUTextureFormat | undefined;
  148. private _depthTestEnabled: boolean;
  149. private _depthWriteEnabled: boolean;
  150. private _depthCompare: number;
  151. private _stencilEnabled: boolean;
  152. private _stencilFrontCompare: number;
  153. private _stencilFrontDepthFailOp: number;
  154. private _stencilFrontPassOp: number;
  155. private _stencilFrontFailOp: number;
  156. private _stencilReadMask: number;
  157. private _stencilWriteMask: number;
  158. private _depthStencilState: number;
  159. private _vertexBuffers: Nullable<{ [key: string]: Nullable<VertexBuffer> }>;
  160. private _indexBuffer: Nullable<DataBuffer>;
  161. constructor(device: GPUDevice, emptyVertexBuffer: VertexBuffer) {
  162. this._device = device;
  163. this._states = [];
  164. this._states.length = StatePosition.NumStates;
  165. this._emptyVertexBuffer = emptyVertexBuffer;
  166. this._mrtFormats = [];
  167. this.disabled = false;
  168. this.reset();
  169. }
  170. public reset(): void {
  171. this._isDirty = true;
  172. this.setAlphaToCoverage(false);
  173. this.resetDepthCullingState();
  174. this.setClampDepth(false);
  175. //this.setDepthBias(0);
  176. //this.setDepthBiasClamp(0);
  177. this.setColorFormat(WebGPUConstants.TextureFormat.BGRA8Unorm);
  178. this.setMRTAttachments([], []);
  179. this.setAlphaBlendEnabled(false);
  180. this.setAlphaBlendFactors([null, null, null, null], [null, null]);
  181. this.setWriteMask(0xF);
  182. this.setDepthStencilFormat(WebGPUConstants.TextureFormat.Depth24PlusStencil8);
  183. this.setStencilEnabled(false);
  184. this.resetStencilState();
  185. this.setBuffers(null, null);
  186. }
  187. public getRenderPipeline(fillMode: number, effect: Effect, sampleCount: number): GPURenderPipeline {
  188. if (this.disabled) {
  189. const topology = WebGPUCacheRenderPipeline._GetTopology(fillMode);
  190. this._currentRenderPipeline = this._createRenderPipeline(effect, topology, sampleCount);
  191. WebGPUCacheRenderPipeline.NumCacheMiss++;
  192. WebGPUCacheRenderPipeline._NumPipelineCreationCurrentFrame++;
  193. return this._currentRenderPipeline;
  194. }
  195. this._setShaderStage(effect.uniqueId);
  196. this._setRasterizationState(fillMode, sampleCount);
  197. this._setColorStates();
  198. this._setDepthStencilState();
  199. this._setVertexState(effect);
  200. if (!this._isDirty && this._currentRenderPipeline) {
  201. WebGPUCacheRenderPipeline.NumCacheHitWithoutHash++;
  202. return this._currentRenderPipeline;
  203. }
  204. this._isDirty = false;
  205. let hash = this._states.join();
  206. let pipeline = WebGPUCacheRenderPipeline._Cache[hash];
  207. if (pipeline) {
  208. this._currentRenderPipeline = pipeline;
  209. WebGPUCacheRenderPipeline.NumCacheHitWithHash++;
  210. return pipeline;
  211. }
  212. const topology = WebGPUCacheRenderPipeline._GetTopology(fillMode);
  213. this._currentRenderPipeline = this._createRenderPipeline(effect, topology, sampleCount);
  214. WebGPUCacheRenderPipeline._Cache[hash] = this._currentRenderPipeline;
  215. WebGPUCacheRenderPipeline.NumCacheMiss++;
  216. WebGPUCacheRenderPipeline._NumPipelineCreationCurrentFrame++;
  217. return this._currentRenderPipeline;
  218. }
  219. public endFrame(): void {
  220. WebGPUCacheRenderPipeline.NumPipelineCreationLastFrame = WebGPUCacheRenderPipeline._NumPipelineCreationCurrentFrame;
  221. WebGPUCacheRenderPipeline._NumPipelineCreationCurrentFrame = 0;
  222. }
  223. public setAlphaToCoverage(enabled: boolean): void {
  224. this._alphaToCoverageEnabled = enabled;
  225. }
  226. public setFrontFace(frontFace: number): void {
  227. this._frontFace = frontFace;
  228. }
  229. public setCullEnabled(enabled: boolean): void {
  230. this._cullEnabled = enabled;
  231. }
  232. public setCullFace(cullFace: number): void {
  233. this._cullFace = cullFace;
  234. }
  235. public setClampDepth(clampDepth: boolean): void {
  236. this._clampDepth = clampDepth;
  237. }
  238. public resetDepthCullingState(): void {
  239. this.setDepthCullingState(false, 2, 1, 0, true, true, Constants.ALWAYS);
  240. }
  241. public setDepthCullingState(cullEnabled: boolean, frontFace: number, cullFace: number, zOffset: number, depthTestEnabled: boolean, depthWriteEnabled: boolean, depthCompare: Nullable<number>): void {
  242. this._depthWriteEnabled = depthWriteEnabled;
  243. this._depthTestEnabled = depthTestEnabled;
  244. this._depthCompare = (depthCompare ?? Constants.ALWAYS) - 0x0200;
  245. this._cullFace = cullFace;
  246. this._cullEnabled = cullEnabled;
  247. this._frontFace = frontFace;
  248. this.setDepthBiasSlopeScale(zOffset);
  249. }
  250. /*public setDepthBias(depthBias: number): void {
  251. if (this._depthBias !== depthBias) {
  252. this._depthBias = depthBias;
  253. this._states[StatePosition.DepthBias] = depthBias.toString();
  254. this._isDirty = true;
  255. }
  256. }
  257. public setDepthBiasClamp(depthBiasClamp: number): void {
  258. if (this._depthBiasClamp !== depthBiasClamp) {
  259. this._depthBiasClamp = depthBiasClamp;
  260. this._states[StatePosition.DepthBiasClamp] = depthBiasClamp.toString();
  261. this._isDirty = true;
  262. }
  263. }*/
  264. public setDepthBiasSlopeScale(depthBiasSlopeScale: number): void {
  265. if (this._depthBiasSlopeScale !== depthBiasSlopeScale) {
  266. this._depthBiasSlopeScale = depthBiasSlopeScale;
  267. this._states[StatePosition.DepthBiasSlopeScale] = depthBiasSlopeScale.toString();
  268. this._isDirty = true;
  269. }
  270. }
  271. public setColorFormat(format: GPUTextureFormat): void {
  272. this._webgpuColorFormat = format;
  273. this._colorFormat = textureFormatToIndex[format];
  274. }
  275. public setMRTAttachments(attachments: number[], textureArray: InternalTexture[]): void {
  276. if (attachments.length > 10) {
  277. // If we want more than 10 attachments we need to change this method but 10 seems plenty
  278. // Note we can do better without changing this method if only dealing with texture formats that can be used as output attachments
  279. // It could allow to use 5 bits (or even less) to code a texture format. For the time being, we use 6 bits as we need 58 different values (2^6=64)
  280. // so we can encode 5 texture formats in 32 bits
  281. throw "Can't handle more than 10 attachments for a MRT in cache render pipeline!";
  282. }
  283. let bits: number[] = [0, 0], indexBits = 0, mask = 0;
  284. this._mrtFormats.length = attachments.length;
  285. for (let i = 0; i < attachments.length; ++i) {
  286. const index = attachments[i];
  287. const texture = textureArray[index - 1];
  288. const gpuWrapper = texture?._hardwareTexture as Nullable<WebGPUHardwareTexture>;
  289. this._mrtFormats[i] = gpuWrapper?.format ?? this._webgpuColorFormat;
  290. bits[indexBits] += textureFormatToIndex[this._mrtFormats[i]] << mask;
  291. mask += 6;
  292. if (mask >= 32) {
  293. mask = 0;
  294. indexBits++;
  295. }
  296. }
  297. if (this._mrtAttachments1 !== bits[0] || this._mrtAttachments2 !== bits[1]) {
  298. this._mrtAttachments1 = bits[0];
  299. this._mrtAttachments2 = bits[1];
  300. this._states[StatePosition.MRTAttachments1] = bits[0].toString();
  301. this._states[StatePosition.MRTAttachments2] = bits[1].toString();
  302. this._isDirty = true;
  303. }
  304. }
  305. public setAlphaBlendEnabled(enabled: boolean): void {
  306. this._alphaBlendEnabled = enabled;
  307. }
  308. public setAlphaBlendFactors(factors: Array<Nullable<number>>, operations: Array<Nullable<number>>): void {
  309. this._alphaBlendFuncParams = factors;
  310. this._alphaBlendEqParams = operations;
  311. }
  312. public setWriteMask(mask: number): void {
  313. this._writeMask = mask;
  314. }
  315. public setDepthStencilFormat(format: GPUTextureFormat | undefined): void {
  316. this._webgpuDepthStencilFormat = format;
  317. this._depthStencilFormat = format === undefined ? 0 : textureFormatToIndex[format];
  318. }
  319. public setDepthTestEnabled(enabled: boolean): void {
  320. this._depthTestEnabled = enabled;
  321. }
  322. public setDepthWriteEnabled(enabled: boolean): void {
  323. this._depthWriteEnabled = enabled;
  324. }
  325. public setDepthCompare(func: Nullable<number>): void {
  326. this._depthCompare = (func ?? Constants.ALWAYS) - 0x0200;
  327. }
  328. public setStencilEnabled(enabled: boolean): void {
  329. this._stencilEnabled = enabled;
  330. }
  331. public setStencilCompare(func: Nullable<number>): void {
  332. this._stencilFrontCompare = (func ?? Constants.ALWAYS) - 0x0200;
  333. }
  334. public setStencilDepthFailOp(op: Nullable<number>): void {
  335. this._stencilFrontDepthFailOp = op === null ? 1 /* KEEP */ : stencilOpToIndex[op];
  336. }
  337. public setStencilPassOp(op: Nullable<number>): void {
  338. this._stencilFrontPassOp = op === null ? 2 /* REPLACE */ : stencilOpToIndex[op];
  339. }
  340. public setStencilFailOp(op: Nullable<number>): void {
  341. this._stencilFrontFailOp = op === null ? 1 /* KEEP */ : stencilOpToIndex[op];
  342. }
  343. public setStencilReadMask(mask: number): void {
  344. if (this._stencilReadMask !== mask) {
  345. this._stencilReadMask = mask;
  346. this._states[StatePosition.StencilReadMask] = mask.toString();
  347. this._isDirty = true;
  348. }
  349. }
  350. public setStencilWriteMask(mask: number): void {
  351. if (this._stencilWriteMask !== mask) {
  352. this._stencilWriteMask = mask;
  353. this._states[StatePosition.StencilWriteMask] = mask.toString();
  354. this._isDirty = true;
  355. }
  356. }
  357. public resetStencilState(): void {
  358. this.setStencilState(false, Constants.ALWAYS, Constants.KEEP, Constants.REPLACE, Constants.KEEP, 0xFF, 0xFF);
  359. }
  360. public setStencilState(stencilEnabled: boolean, compare: Nullable<number>, depthFailOp: Nullable<number>, passOp: Nullable<number>, failOp: Nullable<number>, readMask: number, writeMask: number): void {
  361. this._stencilEnabled = stencilEnabled;
  362. this._stencilFrontCompare = (compare ?? Constants.ALWAYS) - 0x0200;
  363. this._stencilFrontDepthFailOp = depthFailOp === null ? 1 /* KEEP */ : stencilOpToIndex[depthFailOp];
  364. this._stencilFrontPassOp = passOp === null ? 2 /* REPLACE */ : stencilOpToIndex[passOp];
  365. this._stencilFrontFailOp = failOp === null ? 1 /* KEEP */ : stencilOpToIndex[failOp];
  366. this.setStencilReadMask(readMask);
  367. this.setStencilWriteMask(writeMask);
  368. }
  369. public setBuffers(vertexBuffers: Nullable<{ [key: string]: Nullable<VertexBuffer> }>, indexBuffer: Nullable<DataBuffer>): void {
  370. this._vertexBuffers = vertexBuffers;
  371. this._indexBuffer = indexBuffer;
  372. }
  373. private static _GetTopology(fillMode: number): GPUPrimitiveTopology {
  374. switch (fillMode) {
  375. // Triangle views
  376. case Constants.MATERIAL_TriangleFillMode:
  377. return WebGPUConstants.PrimitiveTopology.TriangleList;
  378. case Constants.MATERIAL_PointFillMode:
  379. return WebGPUConstants.PrimitiveTopology.PointList;
  380. case Constants.MATERIAL_WireFrameFillMode:
  381. return WebGPUConstants.PrimitiveTopology.LineList;
  382. // Draw modes
  383. case Constants.MATERIAL_PointListDrawMode:
  384. return WebGPUConstants.PrimitiveTopology.PointList;
  385. case Constants.MATERIAL_LineListDrawMode:
  386. return WebGPUConstants.PrimitiveTopology.LineList;
  387. case Constants.MATERIAL_LineLoopDrawMode:
  388. // return this._gl.LINE_LOOP;
  389. // TODO WEBGPU. Line Loop Mode Fallback at buffer load time.
  390. throw "LineLoop is an unsupported fillmode in WebGPU";
  391. case Constants.MATERIAL_LineStripDrawMode:
  392. return WebGPUConstants.PrimitiveTopology.LineStrip;
  393. case Constants.MATERIAL_TriangleStripDrawMode:
  394. return WebGPUConstants.PrimitiveTopology.TriangleStrip;
  395. case Constants.MATERIAL_TriangleFanDrawMode:
  396. // return this._gl.TRIANGLE_FAN;
  397. // TODO WEBGPU. Triangle Fan Mode Fallback at buffer load time.
  398. throw "TriangleFan is an unsupported fillmode in WebGPU";
  399. default:
  400. return WebGPUConstants.PrimitiveTopology.TriangleList;
  401. }
  402. }
  403. private static _GetAphaBlendOperation(operation: Nullable<number>): GPUBlendOperation {
  404. switch (operation) {
  405. case 0x8006:
  406. return WebGPUConstants.BlendOperation.Add;
  407. case 0x800A:
  408. return WebGPUConstants.BlendOperation.Subtract;
  409. case 0x800B:
  410. return WebGPUConstants.BlendOperation.ReverseSubtract;
  411. case 0x8007:
  412. return WebGPUConstants.BlendOperation.Min;
  413. case 0x8008:
  414. return WebGPUConstants.BlendOperation.Max;
  415. default:
  416. return WebGPUConstants.BlendOperation.Add;
  417. }
  418. }
  419. private static _GetAphaBlendFactor(factor: Nullable<number>): GPUBlendFactor {
  420. switch (factor) {
  421. case 0:
  422. return WebGPUConstants.BlendFactor.Zero;
  423. case 1:
  424. return WebGPUConstants.BlendFactor.One;
  425. case 0x0300:
  426. return WebGPUConstants.BlendFactor.SrcColor;
  427. case 0x0301:
  428. return WebGPUConstants.BlendFactor.OneMinusSrcColor;
  429. case 0x0302:
  430. return WebGPUConstants.BlendFactor.SrcAlpha;
  431. case 0x0303:
  432. return WebGPUConstants.BlendFactor.OneMinusSrcAlpha;
  433. case 0x0304:
  434. return WebGPUConstants.BlendFactor.DstAlpha;
  435. case 0x0305:
  436. return WebGPUConstants.BlendFactor.OneMinusDstAlpha;
  437. case 0x0306:
  438. return WebGPUConstants.BlendFactor.DstColor;
  439. case 0x0307:
  440. return WebGPUConstants.BlendFactor.OneMinusDstColor;
  441. case 0x0308:
  442. return WebGPUConstants.BlendFactor.SrcAlphaSaturated;
  443. case 0x8001:
  444. return WebGPUConstants.BlendFactor.BlendColor;
  445. case 0x8002:
  446. return WebGPUConstants.BlendFactor.OneMinusBlendColor;
  447. case 0x8003:
  448. return WebGPUConstants.BlendFactor.BlendColor;
  449. case 0x8004:
  450. return WebGPUConstants.BlendFactor.OneMinusBlendColor;
  451. default:
  452. return WebGPUConstants.BlendFactor.One;
  453. }
  454. }
  455. private static _GetCompareFunction(compareFunction: number): GPUCompareFunction {
  456. switch (compareFunction) {
  457. case 0: // NEVER
  458. return WebGPUConstants.CompareFunction.Never;
  459. case 1: // LESS
  460. return WebGPUConstants.CompareFunction.Less;
  461. case 2: // EQUAL
  462. return WebGPUConstants.CompareFunction.Equal;
  463. case 3: // LEQUAL
  464. return WebGPUConstants.CompareFunction.LessEqual;
  465. case 4: // GREATER
  466. return WebGPUConstants.CompareFunction.Greater;
  467. case 5: // NOTEQUAL
  468. return WebGPUConstants.CompareFunction.NotEqual;
  469. case 6: // GEQUAL
  470. return WebGPUConstants.CompareFunction.GreaterEqual;
  471. case 7: // ALWAYS
  472. return WebGPUConstants.CompareFunction.Always;
  473. }
  474. return WebGPUConstants.CompareFunction.Never;
  475. }
  476. private static _GetStencilOpFunction(operation: number): GPUStencilOperation {
  477. switch (operation) {
  478. case 0:
  479. return WebGPUConstants.StencilOperation.Zero;
  480. case 1:
  481. return WebGPUConstants.StencilOperation.Keep;
  482. case 2:
  483. return WebGPUConstants.StencilOperation.Replace;
  484. case 3:
  485. return WebGPUConstants.StencilOperation.IncrementClamp;
  486. case 4:
  487. return WebGPUConstants.StencilOperation.DecrementClamp;
  488. case 5:
  489. return WebGPUConstants.StencilOperation.Invert;
  490. case 6:
  491. return WebGPUConstants.StencilOperation.IncrementWrap;
  492. case 7:
  493. return WebGPUConstants.StencilOperation.DecrementWrap;
  494. }
  495. return WebGPUConstants.StencilOperation.Keep;
  496. }
  497. private static _GetVertexInputDescriptorFormat(vertexBuffer: VertexBuffer): GPUVertexFormat {
  498. const type = vertexBuffer.type;
  499. const normalized = vertexBuffer.normalized;
  500. const size = vertexBuffer.getSize();
  501. switch (type) {
  502. case VertexBuffer.BYTE:
  503. switch (size) {
  504. case 1:
  505. case 2:
  506. return normalized ? WebGPUConstants.VertexFormat.Char2Norm : WebGPUConstants.VertexFormat.Char2;
  507. case 3:
  508. case 4:
  509. return normalized ? WebGPUConstants.VertexFormat.Char4Norm : WebGPUConstants.VertexFormat.Char4;
  510. }
  511. break;
  512. case VertexBuffer.UNSIGNED_BYTE:
  513. switch (size) {
  514. case 1:
  515. case 2:
  516. return normalized ? WebGPUConstants.VertexFormat.Uchar2Norm : WebGPUConstants.VertexFormat.Uchar2;
  517. case 3:
  518. case 4:
  519. return normalized ? WebGPUConstants.VertexFormat.Uchar4Norm : WebGPUConstants.VertexFormat.Uchar4;
  520. }
  521. break;
  522. case VertexBuffer.SHORT:
  523. switch (size) {
  524. case 1:
  525. case 2:
  526. return normalized ? WebGPUConstants.VertexFormat.Short2Norm : WebGPUConstants.VertexFormat.Short2;
  527. case 3:
  528. case 4:
  529. return normalized ? WebGPUConstants.VertexFormat.Short4Norm : WebGPUConstants.VertexFormat.Short4;
  530. }
  531. break;
  532. case VertexBuffer.UNSIGNED_SHORT:
  533. switch (size) {
  534. case 1:
  535. case 2:
  536. return normalized ? WebGPUConstants.VertexFormat.Ushort2Norm : WebGPUConstants.VertexFormat.Ushort2;
  537. case 3:
  538. case 4:
  539. return normalized ? WebGPUConstants.VertexFormat.Ushort4Norm : WebGPUConstants.VertexFormat.Ushort4;
  540. }
  541. break;
  542. case VertexBuffer.INT:
  543. switch (size) {
  544. case 1:
  545. return WebGPUConstants.VertexFormat.Int;
  546. case 2:
  547. return WebGPUConstants.VertexFormat.Int2;
  548. case 3:
  549. return WebGPUConstants.VertexFormat.Int3;
  550. case 4:
  551. return WebGPUConstants.VertexFormat.Int4;
  552. }
  553. break;
  554. case VertexBuffer.UNSIGNED_INT:
  555. switch (size) {
  556. case 1:
  557. return WebGPUConstants.VertexFormat.Uint;
  558. case 2:
  559. return WebGPUConstants.VertexFormat.Uint2;
  560. case 3:
  561. return WebGPUConstants.VertexFormat.Uint3;
  562. case 4:
  563. return WebGPUConstants.VertexFormat.Uint4;
  564. }
  565. break;
  566. case VertexBuffer.FLOAT:
  567. switch (size) {
  568. case 1:
  569. return WebGPUConstants.VertexFormat.Float;
  570. case 2:
  571. return WebGPUConstants.VertexFormat.Float2;
  572. case 3:
  573. return WebGPUConstants.VertexFormat.Float3;
  574. case 4:
  575. return WebGPUConstants.VertexFormat.Float4;
  576. }
  577. break;
  578. }
  579. throw new Error(`Invalid Format '${vertexBuffer.getKind()}' - type=${type}, normalized=${normalized}, size=${size}`);
  580. }
  581. private _getAphaBlendState(): GPUBlendDescriptor {
  582. if (!this._alphaBlendEnabled) {
  583. return { };
  584. }
  585. return {
  586. srcFactor: WebGPUCacheRenderPipeline._GetAphaBlendFactor(this._alphaBlendFuncParams[2]),
  587. dstFactor: WebGPUCacheRenderPipeline._GetAphaBlendFactor(this._alphaBlendFuncParams[3]),
  588. operation: WebGPUCacheRenderPipeline._GetAphaBlendOperation(this._alphaBlendEqParams[1]),
  589. };
  590. }
  591. private _getColorBlendState(): GPUBlendDescriptor {
  592. if (!this._alphaBlendEnabled) {
  593. return { };
  594. }
  595. return {
  596. srcFactor: WebGPUCacheRenderPipeline._GetAphaBlendFactor(this._alphaBlendFuncParams[0]),
  597. dstFactor: WebGPUCacheRenderPipeline._GetAphaBlendFactor(this._alphaBlendFuncParams[1]),
  598. operation: WebGPUCacheRenderPipeline._GetAphaBlendOperation(this._alphaBlendEqParams[0]),
  599. };
  600. }
  601. private _setShaderStage(id: number): void {
  602. if (this._shaderId !== id) {
  603. this._shaderId = id;
  604. this._states[StatePosition.ShaderStage] = id.toString();
  605. this._isDirty = true;
  606. }
  607. }
  608. private _setRasterizationState(topology: number, sampleCount: number): void {
  609. const frontFace = this._frontFace;
  610. const cullMode = this._cullEnabled ? this._cullFace : 0;
  611. const clampDepth = this._clampDepth ? 1 : 0;
  612. const alphaToCoverage = this._alphaToCoverageEnabled ? 1 : 0;
  613. const rasterizationState =
  614. (frontFace - 1) +
  615. (cullMode << 1) +
  616. (clampDepth << 3) +
  617. (alphaToCoverage << 4) +
  618. (topology << 5) +
  619. (sampleCount << 8);
  620. if (this._rasterizationState !== rasterizationState) {
  621. this._rasterizationState = rasterizationState;
  622. this._states[StatePosition.RasterizationState] = this._rasterizationState.toString();
  623. this._isDirty = true;
  624. }
  625. }
  626. private _setColorStates(): void {
  627. let colorStates = ((this._writeMask ? 1 : 0) << 22) + (this._colorFormat << 23);
  628. if (this._alphaBlendEnabled) {
  629. colorStates +=
  630. ((this._alphaBlendFuncParams[0] === null ? 2 : alphaBlendFactorToIndex[this._alphaBlendFuncParams[0]]) << 0) +
  631. ((this._alphaBlendFuncParams[1] === null ? 2 : alphaBlendFactorToIndex[this._alphaBlendFuncParams[1]]) << 4) +
  632. ((this._alphaBlendFuncParams[2] === null ? 2 : alphaBlendFactorToIndex[this._alphaBlendFuncParams[2]]) << 8) +
  633. ((this._alphaBlendFuncParams[3] === null ? 2 : alphaBlendFactorToIndex[this._alphaBlendFuncParams[3]]) << 12) +
  634. ((this._alphaBlendEqParams[0] === null ? 1 : this._alphaBlendEqParams[0] - 0x8005) << 16) +
  635. ((this._alphaBlendEqParams[1] === null ? 1 : this._alphaBlendEqParams[1] - 0x8005) << 19);
  636. }
  637. if (colorStates !== this._colorStates) {
  638. this._colorStates = colorStates;
  639. this._states[StatePosition.ColorStates] = this._colorStates.toString();
  640. this._isDirty = true;
  641. }
  642. }
  643. private _setDepthStencilState(): void {
  644. let stencilState = !this._stencilEnabled ?
  645. 7 /* ALWAYS */ + (1 /* KEEP */ << 3) + (1 /* KEEP */ << 6) + (1 /* KEEP */ << 9) :
  646. this._stencilFrontCompare + (this._stencilFrontDepthFailOp << 3) + (this._stencilFrontPassOp << 6) + (this._stencilFrontFailOp << 9);
  647. const depthStencilState =
  648. this._depthStencilFormat +
  649. ((this._depthWriteEnabled ? 1 : 0) << 6) +
  650. ((this._depthTestEnabled ? this._depthCompare : 7 /* ALWAYS */) << 7) +
  651. (stencilState << 10); // stencil front - stencil back is the same
  652. if (this._depthStencilState !== depthStencilState) {
  653. this._depthStencilState = depthStencilState;
  654. this._states[StatePosition.DepthStencilState] = this._depthStencilState.toString();
  655. this._isDirty = true;
  656. }
  657. }
  658. private _setVertexState(effect: Effect): void {
  659. const currStateLen = this._states.length;
  660. let newNumStates = StatePosition.VertexState;
  661. const webgpuPipelineContext = effect._pipelineContext as WebGPUPipelineContext;
  662. const attributes = webgpuPipelineContext.shaderProcessingContext.attributeNamesFromEffect;
  663. const locations = webgpuPipelineContext.shaderProcessingContext.attributeLocationsFromEffect;
  664. for (var index = 0; index < attributes.length; index++) {
  665. const location = locations[index];
  666. let vertexBuffer = this._vertexBuffers![attributes[index]];
  667. if (!vertexBuffer) {
  668. // In WebGL it's valid to not bind a vertex buffer to an attribute, but it's not valid in WebGPU
  669. // So we must bind a dummy buffer when we are not given one for a specific attribute
  670. vertexBuffer = this._emptyVertexBuffer;
  671. }
  672. const type = vertexBuffer.type - 5120;
  673. const normalized = vertexBuffer.normalized ? 1 : 0;
  674. const size = vertexBuffer.getSize();
  675. const stepMode = vertexBuffer.getIsInstanced() ? 1 : 0;
  676. const stride = vertexBuffer.byteStride;
  677. const vid =
  678. ((type << 0) +
  679. (normalized << 3) +
  680. (size << 4) +
  681. (stepMode << 6) +
  682. (location << 7) +
  683. (stride << 12)).toString();
  684. this._isDirty = this._isDirty || this._states[newNumStates] !== vid;
  685. this._states[newNumStates++] = vid;
  686. }
  687. this._states.length = newNumStates;
  688. this._isDirty = this._isDirty || newNumStates !== currStateLen;
  689. }
  690. private _createPipelineLayout(webgpuPipelineContext: WebGPUPipelineContext): GPUPipelineLayout {
  691. const bindGroupLayouts: GPUBindGroupLayout[] = [];
  692. for (let i = 0; i < webgpuPipelineContext.shaderProcessingContext.orderedUBOsAndSamplers.length; i++) {
  693. const setDefinition = webgpuPipelineContext.shaderProcessingContext.orderedUBOsAndSamplers[i];
  694. if (setDefinition === undefined) {
  695. const entries: GPUBindGroupLayoutEntry[] = [];
  696. const uniformsBindGroupLayout = this._device.createBindGroupLayout({
  697. entries,
  698. });
  699. bindGroupLayouts[i] = uniformsBindGroupLayout;
  700. continue;
  701. }
  702. const entries: GPUBindGroupLayoutEntry[] = [];
  703. for (let j = 0; j < setDefinition.length; j++) {
  704. const bindingDefinition = webgpuPipelineContext.shaderProcessingContext.orderedUBOsAndSamplers[i][j];
  705. if (bindingDefinition === undefined) {
  706. continue;
  707. }
  708. let visibility = 0;
  709. if (bindingDefinition.usedInVertex) {
  710. visibility = visibility | WebGPUConstants.ShaderStage.Vertex;
  711. }
  712. if (bindingDefinition.usedInFragment) {
  713. visibility = visibility | WebGPUConstants.ShaderStage.Fragment;
  714. }
  715. if (bindingDefinition.isSampler) {
  716. entries.push({
  717. binding: j,
  718. visibility,
  719. type: bindingDefinition.isComparisonSampler ? WebGPUConstants.BindingType.ComparisonSampler : WebGPUConstants.BindingType.Sampler
  720. });
  721. } else if (bindingDefinition.isTexture) {
  722. entries.push({
  723. binding: j,
  724. visibility,
  725. type: WebGPUConstants.BindingType.SampledTexture,
  726. viewDimension: bindingDefinition.textureDimension,
  727. textureComponentType: bindingDefinition.componentType,
  728. // TODO WEBGPU.
  729. // hasDynamicOffset?: boolean;
  730. // storageTextureFormat?: GPUTextureFormat;
  731. // minBufferBindingSize?: number;
  732. });
  733. } else {
  734. entries.push({
  735. binding: j,
  736. visibility,
  737. type: WebGPUConstants.BindingType.UniformBuffer,
  738. });
  739. }
  740. }
  741. if (entries.length > 0) {
  742. const uniformsBindGroupLayout = this._device.createBindGroupLayout({
  743. entries,
  744. });
  745. bindGroupLayouts[i] = uniformsBindGroupLayout;
  746. }
  747. }
  748. webgpuPipelineContext.bindGroupLayouts = bindGroupLayouts;
  749. return this._device.createPipelineLayout({ bindGroupLayouts });
  750. }
  751. private _getVertexInputDescriptor(effect: Effect, topology: GPUPrimitiveTopology): GPUVertexStateDescriptor {
  752. const descriptors: GPUVertexBufferLayoutDescriptor[] = [];
  753. const webgpuPipelineContext = effect._pipelineContext as WebGPUPipelineContext;
  754. const attributes = webgpuPipelineContext.shaderProcessingContext.attributeNamesFromEffect;
  755. const locations = webgpuPipelineContext.shaderProcessingContext.attributeLocationsFromEffect;
  756. for (var index = 0; index < attributes.length; index++) {
  757. const location = locations[index];
  758. let vertexBuffer = this._vertexBuffers![attributes[index]];
  759. if (!vertexBuffer) {
  760. // In WebGL it's valid to not bind a vertex buffer to an attribute, but it's not valid in WebGPU
  761. // So we must bind a dummy buffer when we are not given one for a specific attribute
  762. vertexBuffer = this._emptyVertexBuffer;
  763. }
  764. const attributeDescriptor: GPUVertexAttributeDescriptor = {
  765. shaderLocation: location,
  766. offset: 0, // not available in WebGL
  767. format: WebGPUCacheRenderPipeline._GetVertexInputDescriptorFormat(vertexBuffer),
  768. };
  769. // TODO WEBGPU. Factorize the one with the same underlying buffer.
  770. const vertexBufferDescriptor: GPUVertexBufferLayoutDescriptor = {
  771. arrayStride: vertexBuffer.byteStride,
  772. stepMode: vertexBuffer.getIsInstanced() ? WebGPUConstants.InputStepMode.Instance : WebGPUConstants.InputStepMode.Vertex,
  773. attributes: [attributeDescriptor]
  774. };
  775. descriptors.push(vertexBufferDescriptor);
  776. }
  777. const inputStateDescriptor: GPUVertexStateDescriptor = {
  778. vertexBuffers: descriptors
  779. };
  780. if (topology === WebGPUConstants.PrimitiveTopology.LineStrip || topology === WebGPUConstants.PrimitiveTopology.TriangleStrip) {
  781. inputStateDescriptor.indexFormat = !this._indexBuffer || this._indexBuffer.is32Bits ? WebGPUConstants.IndexFormat.Uint32 : WebGPUConstants.IndexFormat.Uint16;
  782. }
  783. return inputStateDescriptor;
  784. }
  785. private _createRenderPipeline(effect: Effect, topology: GPUPrimitiveTopology, sampleCount: number, createLayout = true): GPURenderPipeline {
  786. const webgpuPipelineContext = effect._pipelineContext as WebGPUPipelineContext;
  787. const inputStateDescriptor = this._getVertexInputDescriptor(effect, topology);
  788. const pipelineLayout = createLayout ? this._createPipelineLayout(webgpuPipelineContext) : undefined;
  789. const colorStates: Array<GPUColorStateDescriptor> = [];
  790. const alphaBlend = this._getAphaBlendState();
  791. const colorBlend = this._getColorBlendState();
  792. if (this._mrtAttachments1 > 0) {
  793. for (let i = 0; i < this._mrtFormats.length; ++i) {
  794. colorStates.push({
  795. format: this._mrtFormats[i],
  796. alphaBlend,
  797. colorBlend,
  798. writeMask: this._writeMask,
  799. });
  800. }
  801. } else {
  802. colorStates.push({
  803. format: this._webgpuColorFormat,
  804. alphaBlend,
  805. colorBlend,
  806. writeMask: this._writeMask,
  807. });
  808. }
  809. const stencilFrontBack: GPUStencilStateFaceDescriptor = {
  810. compare: WebGPUCacheRenderPipeline._GetCompareFunction(this._stencilFrontCompare),
  811. depthFailOp: WebGPUCacheRenderPipeline._GetStencilOpFunction(this._stencilFrontDepthFailOp),
  812. failOp: WebGPUCacheRenderPipeline._GetStencilOpFunction(this._stencilFrontFailOp),
  813. passOp: WebGPUCacheRenderPipeline._GetStencilOpFunction(this._stencilFrontPassOp)
  814. };
  815. return this._device.createRenderPipeline({
  816. layout: pipelineLayout,
  817. ...webgpuPipelineContext.stages!,
  818. primitiveTopology: topology,
  819. rasterizationState: {
  820. frontFace: this._frontFace === 1 ? WebGPUConstants.FrontFace.CCW : WebGPUConstants.FrontFace.CW,
  821. cullMode: !this._cullEnabled ? WebGPUConstants.CullMode.None : this._cullFace === 2 ? WebGPUConstants.CullMode.Front : WebGPUConstants.CullMode.Back,
  822. depthBias: this._depthBias,
  823. depthBiasClamp: this._depthBiasClamp,
  824. depthBiasSlopeScale: this._depthBiasSlopeScale,
  825. },
  826. colorStates,
  827. sampleCount,
  828. depthStencilState: this._webgpuDepthStencilFormat === undefined ? undefined : {
  829. depthWriteEnabled: this._depthWriteEnabled,
  830. depthCompare: this._depthTestEnabled ? WebGPUCacheRenderPipeline._GetCompareFunction(this._depthCompare) : WebGPUConstants.CompareFunction.Always,
  831. format: this._webgpuDepthStencilFormat,
  832. stencilFront: stencilFrontBack,
  833. stencilBack: stencilFrontBack,
  834. stencilReadMask: this._stencilReadMask,
  835. stencilWriteMask: this._stencilWriteMask,
  836. },
  837. vertexState: inputStateDescriptor,
  838. });
  839. }
  840. }