webgpuCacheRenderPipeline.js 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903
  1. /* eslint-disable @typescript-eslint/naming-convention */
  2. /* eslint-disable babylonjs/available */
  3. /* eslint-disable jsdoc/require-jsdoc */
  4. import * as WebGPUConstants from "./webgpuConstants.js";
  5. import { VertexBuffer } from "../../Buffers/buffer.js";
  6. import { WebGPUShaderProcessor } from "./webgpuShaderProcessor.js";
  7. import { WebGPUTextureHelper } from "./webgpuTextureHelper.js";
  8. import { renderableTextureFormatToIndex } from "./webgpuTextureManager.js";
  9. var StatePosition;
  10. (function (StatePosition) {
  11. StatePosition[StatePosition["StencilReadMask"] = 0] = "StencilReadMask";
  12. StatePosition[StatePosition["StencilWriteMask"] = 1] = "StencilWriteMask";
  13. //DepthBiasClamp = 1, // not used, so remove it to improve perf
  14. StatePosition[StatePosition["DepthBias"] = 2] = "DepthBias";
  15. StatePosition[StatePosition["DepthBiasSlopeScale"] = 3] = "DepthBiasSlopeScale";
  16. StatePosition[StatePosition["DepthStencilState"] = 4] = "DepthStencilState";
  17. StatePosition[StatePosition["MRTAttachments1"] = 5] = "MRTAttachments1";
  18. StatePosition[StatePosition["MRTAttachments2"] = 6] = "MRTAttachments2";
  19. StatePosition[StatePosition["RasterizationState"] = 7] = "RasterizationState";
  20. StatePosition[StatePosition["ColorStates"] = 8] = "ColorStates";
  21. StatePosition[StatePosition["ShaderStage"] = 9] = "ShaderStage";
  22. StatePosition[StatePosition["TextureStage"] = 10] = "TextureStage";
  23. StatePosition[StatePosition["VertexState"] = 11] = "VertexState";
  24. StatePosition[StatePosition["NumStates"] = 12] = "NumStates";
  25. })(StatePosition || (StatePosition = {}));
  26. const alphaBlendFactorToIndex = {
  27. 0: 1,
  28. 1: 2,
  29. 0x0300: 3,
  30. 0x0301: 4,
  31. 0x0302: 5,
  32. 0x0303: 6,
  33. 0x0304: 7,
  34. 0x0305: 8,
  35. 0x0306: 9,
  36. 0x0307: 10,
  37. 0x0308: 11,
  38. 0x8001: 12,
  39. 0x8002: 13,
  40. 0x8003: 12,
  41. 0x8004: 13, // OneMinusBlendColor (alpha)
  42. };
  43. const stencilOpToIndex = {
  44. 0x0000: 0,
  45. 0x1e00: 1,
  46. 0x1e01: 2,
  47. 0x1e02: 3,
  48. 0x1e03: 4,
  49. 0x150a: 5,
  50. 0x8507: 6,
  51. 0x8508: 7, // DECR_WRAP
  52. };
  53. const vertexBufferKindForNonFloatProcessing = {
  54. [VertexBuffer.PositionKind]: true,
  55. [VertexBuffer.NormalKind]: true,
  56. [VertexBuffer.TangentKind]: true,
  57. [VertexBuffer.UVKind]: true,
  58. [VertexBuffer.UV2Kind]: true,
  59. [VertexBuffer.UV3Kind]: true,
  60. [VertexBuffer.UV4Kind]: true,
  61. [VertexBuffer.UV5Kind]: true,
  62. [VertexBuffer.UV6Kind]: true,
  63. [VertexBuffer.ColorKind]: true,
  64. [VertexBuffer.ColorInstanceKind]: true,
  65. [VertexBuffer.MatricesIndicesKind]: true,
  66. [VertexBuffer.MatricesWeightsKind]: true,
  67. [VertexBuffer.MatricesIndicesExtraKind]: true,
  68. [VertexBuffer.MatricesWeightsExtraKind]: true,
  69. };
  70. /** @internal */
  71. export class WebGPUCacheRenderPipeline {
  72. static _IsSignedType(type) {
  73. switch (type) {
  74. case VertexBuffer.BYTE:
  75. case VertexBuffer.SHORT:
  76. case VertexBuffer.INT:
  77. case VertexBuffer.FLOAT:
  78. return true;
  79. case VertexBuffer.UNSIGNED_BYTE:
  80. case VertexBuffer.UNSIGNED_SHORT:
  81. case VertexBuffer.UNSIGNED_INT:
  82. return false;
  83. default:
  84. throw new Error(`Invalid type '${type}'`);
  85. }
  86. }
  87. constructor(device, emptyVertexBuffer) {
  88. this.mrtTextureCount = 0;
  89. this._device = device;
  90. this._useTextureStage = true; // we force usage because we must handle depth textures with "float" filtering, which can't be fixed by a caps (like "textureFloatLinearFiltering" can for float textures)
  91. this._states = new Array(30); // pre-allocate enough room so that no new allocation will take place afterwards
  92. this._statesLength = 0;
  93. this._stateDirtyLowestIndex = 0;
  94. this._emptyVertexBuffer = emptyVertexBuffer;
  95. this._mrtFormats = [];
  96. this._parameter = { token: undefined, pipeline: null };
  97. this.disabled = false;
  98. this.vertexBuffers = [];
  99. this._kMaxVertexBufferStride = device.limits.maxVertexBufferArrayStride || 2048;
  100. this.reset();
  101. }
  102. reset() {
  103. this._isDirty = true;
  104. this.vertexBuffers.length = 0;
  105. this.setAlphaToCoverage(false);
  106. this.resetDepthCullingState();
  107. this.setClampDepth(false);
  108. this.setDepthBias(0);
  109. //this.setDepthBiasClamp(0);
  110. this._webgpuColorFormat = [WebGPUConstants.TextureFormat.BGRA8Unorm];
  111. this.setColorFormat(WebGPUConstants.TextureFormat.BGRA8Unorm);
  112. this.setMRT([]);
  113. this.setAlphaBlendEnabled(false);
  114. this.setAlphaBlendFactors([null, null, null, null], [null, null]);
  115. this.setWriteMask(0xf);
  116. this.setDepthStencilFormat(WebGPUConstants.TextureFormat.Depth24PlusStencil8);
  117. this.setStencilEnabled(false);
  118. this.resetStencilState();
  119. this.setBuffers(null, null, null);
  120. this._setTextureState(0);
  121. }
  122. get colorFormats() {
  123. return this._mrtAttachments1 > 0 ? this._mrtFormats : this._webgpuColorFormat;
  124. }
  125. getRenderPipeline(fillMode, effect, sampleCount, textureState = 0) {
  126. sampleCount = WebGPUTextureHelper.GetSample(sampleCount);
  127. if (this.disabled) {
  128. const topology = WebGPUCacheRenderPipeline._GetTopology(fillMode);
  129. this._setVertexState(effect); // to fill this.vertexBuffers with correct data
  130. this._setTextureState(textureState);
  131. this._parameter.pipeline = this._createRenderPipeline(effect, topology, sampleCount);
  132. WebGPUCacheRenderPipeline.NumCacheMiss++;
  133. WebGPUCacheRenderPipeline._NumPipelineCreationCurrentFrame++;
  134. return this._parameter.pipeline;
  135. }
  136. this._setShaderStage(effect.uniqueId);
  137. this._setRasterizationState(fillMode, sampleCount);
  138. this._setColorStates();
  139. this._setDepthStencilState();
  140. this._setVertexState(effect);
  141. this._setTextureState(textureState);
  142. this.lastStateDirtyLowestIndex = this._stateDirtyLowestIndex;
  143. if (!this._isDirty && this._parameter.pipeline) {
  144. this._stateDirtyLowestIndex = this._statesLength;
  145. WebGPUCacheRenderPipeline.NumCacheHitWithoutHash++;
  146. return this._parameter.pipeline;
  147. }
  148. this._getRenderPipeline(this._parameter);
  149. this._isDirty = false;
  150. this._stateDirtyLowestIndex = this._statesLength;
  151. if (this._parameter.pipeline) {
  152. WebGPUCacheRenderPipeline.NumCacheHitWithHash++;
  153. return this._parameter.pipeline;
  154. }
  155. const topology = WebGPUCacheRenderPipeline._GetTopology(fillMode);
  156. this._parameter.pipeline = this._createRenderPipeline(effect, topology, sampleCount);
  157. this._setRenderPipeline(this._parameter);
  158. WebGPUCacheRenderPipeline.NumCacheMiss++;
  159. WebGPUCacheRenderPipeline._NumPipelineCreationCurrentFrame++;
  160. return this._parameter.pipeline;
  161. }
  162. endFrame() {
  163. WebGPUCacheRenderPipeline.NumPipelineCreationLastFrame = WebGPUCacheRenderPipeline._NumPipelineCreationCurrentFrame;
  164. WebGPUCacheRenderPipeline._NumPipelineCreationCurrentFrame = 0;
  165. }
  166. setAlphaToCoverage(enabled) {
  167. this._alphaToCoverageEnabled = enabled;
  168. }
  169. setFrontFace(frontFace) {
  170. this._frontFace = frontFace;
  171. }
  172. setCullEnabled(enabled) {
  173. this._cullEnabled = enabled;
  174. }
  175. setCullFace(cullFace) {
  176. this._cullFace = cullFace;
  177. }
  178. setClampDepth(clampDepth) {
  179. this._clampDepth = clampDepth;
  180. }
  181. resetDepthCullingState() {
  182. this.setDepthCullingState(false, 2, 1, 0, 0, true, true, 519);
  183. }
  184. setDepthCullingState(cullEnabled, frontFace, cullFace, zOffset, zOffsetUnits, depthTestEnabled, depthWriteEnabled, depthCompare) {
  185. this._depthWriteEnabled = depthWriteEnabled;
  186. this._depthTestEnabled = depthTestEnabled;
  187. this._depthCompare = (depthCompare ?? 519) - 0x0200;
  188. this._cullFace = cullFace;
  189. this._cullEnabled = cullEnabled;
  190. this._frontFace = frontFace;
  191. this.setDepthBiasSlopeScale(zOffset);
  192. this.setDepthBias(zOffsetUnits);
  193. }
  194. setDepthBias(depthBias) {
  195. if (this._depthBias !== depthBias) {
  196. this._depthBias = depthBias;
  197. this._states[StatePosition.DepthBias] = depthBias;
  198. this._isDirty = true;
  199. this._stateDirtyLowestIndex = Math.min(this._stateDirtyLowestIndex, StatePosition.DepthBias);
  200. }
  201. }
  202. /*public setDepthBiasClamp(depthBiasClamp: number): void {
  203. if (this._depthBiasClamp !== depthBiasClamp) {
  204. this._depthBiasClamp = depthBiasClamp;
  205. this._states[StatePosition.DepthBiasClamp] = depthBiasClamp.toString();
  206. this._isDirty = true;
  207. }
  208. }*/
  209. setDepthBiasSlopeScale(depthBiasSlopeScale) {
  210. if (this._depthBiasSlopeScale !== depthBiasSlopeScale) {
  211. this._depthBiasSlopeScale = depthBiasSlopeScale;
  212. this._states[StatePosition.DepthBiasSlopeScale] = depthBiasSlopeScale;
  213. this._isDirty = true;
  214. this._stateDirtyLowestIndex = Math.min(this._stateDirtyLowestIndex, StatePosition.DepthBiasSlopeScale);
  215. }
  216. }
  217. setColorFormat(format) {
  218. this._webgpuColorFormat[0] = format;
  219. this._colorFormat = renderableTextureFormatToIndex[format ?? ""];
  220. }
  221. setMRTAttachments(attachments) {
  222. this.mrtAttachments = attachments;
  223. let mask = 0;
  224. for (let i = 0; i < attachments.length; ++i) {
  225. if (attachments[i] !== 0) {
  226. mask += 1 << i;
  227. }
  228. }
  229. if (this._mrtEnabledMask !== mask) {
  230. this._mrtEnabledMask = mask;
  231. this._isDirty = true;
  232. this._stateDirtyLowestIndex = Math.min(this._stateDirtyLowestIndex, StatePosition.MRTAttachments1);
  233. }
  234. }
  235. setMRT(textureArray, textureCount) {
  236. textureCount = textureCount ?? textureArray.length;
  237. if (textureCount > 10) {
  238. // If we want more than 10 attachments we need to change this method (and the StatePosition enum) but 10 seems plenty: note that WebGPU only supports 8 at the time (2021/12/13)!
  239. // As we need ~39 different values we are using 6 bits to encode a texture format, meaning we can encode 5 texture formats in 32 bits
  240. // We are using 2x32 bit values to handle 10 textures
  241. // eslint-disable-next-line no-throw-literal
  242. throw "Can't handle more than 10 attachments for a MRT in cache render pipeline!";
  243. }
  244. this.mrtTextureArray = textureArray;
  245. this.mrtTextureCount = textureCount;
  246. this._mrtEnabledMask = 0xffff; // all textures are enabled at start (meaning we can write to them). Calls to setMRTAttachments may disable some
  247. const bits = [0, 0];
  248. let indexBits = 0, mask = 0, numRT = 0;
  249. for (let i = 0; i < textureCount; ++i) {
  250. const texture = textureArray[i];
  251. const gpuWrapper = texture?._hardwareTexture;
  252. this._mrtFormats[numRT] = gpuWrapper?.format ?? this._webgpuColorFormat[0];
  253. bits[indexBits] += renderableTextureFormatToIndex[this._mrtFormats[numRT] ?? ""] << mask;
  254. mask += 6;
  255. numRT++;
  256. if (mask >= 32) {
  257. mask = 0;
  258. indexBits++;
  259. }
  260. }
  261. this._mrtFormats.length = numRT;
  262. if (this._mrtAttachments1 !== bits[0] || this._mrtAttachments2 !== bits[1]) {
  263. this._mrtAttachments1 = bits[0];
  264. this._mrtAttachments2 = bits[1];
  265. this._states[StatePosition.MRTAttachments1] = bits[0];
  266. this._states[StatePosition.MRTAttachments2] = bits[1];
  267. this._isDirty = true;
  268. this._stateDirtyLowestIndex = Math.min(this._stateDirtyLowestIndex, StatePosition.MRTAttachments1);
  269. }
  270. }
  271. setAlphaBlendEnabled(enabled) {
  272. this._alphaBlendEnabled = enabled;
  273. }
  274. setAlphaBlendFactors(factors, operations) {
  275. this._alphaBlendFuncParams = factors;
  276. this._alphaBlendEqParams = operations;
  277. }
  278. setWriteMask(mask) {
  279. this._writeMask = mask;
  280. }
  281. setDepthStencilFormat(format) {
  282. this._webgpuDepthStencilFormat = format;
  283. this._depthStencilFormat = format === undefined ? 0 : renderableTextureFormatToIndex[format];
  284. }
  285. setDepthTestEnabled(enabled) {
  286. this._depthTestEnabled = enabled;
  287. }
  288. setDepthWriteEnabled(enabled) {
  289. this._depthWriteEnabled = enabled;
  290. }
  291. setDepthCompare(func) {
  292. this._depthCompare = (func ?? 519) - 0x0200;
  293. }
  294. setStencilEnabled(enabled) {
  295. this._stencilEnabled = enabled;
  296. }
  297. setStencilCompare(func) {
  298. this._stencilFrontCompare = (func ?? 519) - 0x0200;
  299. }
  300. setStencilDepthFailOp(op) {
  301. this._stencilFrontDepthFailOp = op === null ? 1 /* KEEP */ : stencilOpToIndex[op];
  302. }
  303. setStencilPassOp(op) {
  304. this._stencilFrontPassOp = op === null ? 2 /* REPLACE */ : stencilOpToIndex[op];
  305. }
  306. setStencilFailOp(op) {
  307. this._stencilFrontFailOp = op === null ? 1 /* KEEP */ : stencilOpToIndex[op];
  308. }
  309. setStencilReadMask(mask) {
  310. if (this._stencilReadMask !== mask) {
  311. this._stencilReadMask = mask;
  312. this._states[StatePosition.StencilReadMask] = mask;
  313. this._isDirty = true;
  314. this._stateDirtyLowestIndex = Math.min(this._stateDirtyLowestIndex, StatePosition.StencilReadMask);
  315. }
  316. }
  317. setStencilWriteMask(mask) {
  318. if (this._stencilWriteMask !== mask) {
  319. this._stencilWriteMask = mask;
  320. this._states[StatePosition.StencilWriteMask] = mask;
  321. this._isDirty = true;
  322. this._stateDirtyLowestIndex = Math.min(this._stateDirtyLowestIndex, StatePosition.StencilWriteMask);
  323. }
  324. }
  325. resetStencilState() {
  326. this.setStencilState(false, 519, 7680, 7681, 7680, 0xff, 0xff);
  327. }
  328. setStencilState(stencilEnabled, compare, depthFailOp, passOp, failOp, readMask, writeMask) {
  329. this._stencilEnabled = stencilEnabled;
  330. this._stencilFrontCompare = (compare ?? 519) - 0x0200;
  331. this._stencilFrontDepthFailOp = depthFailOp === null ? 1 /* KEEP */ : stencilOpToIndex[depthFailOp];
  332. this._stencilFrontPassOp = passOp === null ? 2 /* REPLACE */ : stencilOpToIndex[passOp];
  333. this._stencilFrontFailOp = failOp === null ? 1 /* KEEP */ : stencilOpToIndex[failOp];
  334. this.setStencilReadMask(readMask);
  335. this.setStencilWriteMask(writeMask);
  336. }
  337. setBuffers(vertexBuffers, indexBuffer, overrideVertexBuffers) {
  338. this._vertexBuffers = vertexBuffers;
  339. this._overrideVertexBuffers = overrideVertexBuffers;
  340. this._indexBuffer = indexBuffer;
  341. }
  342. static _GetTopology(fillMode) {
  343. switch (fillMode) {
  344. // Triangle views
  345. case 0:
  346. return WebGPUConstants.PrimitiveTopology.TriangleList;
  347. case 2:
  348. return WebGPUConstants.PrimitiveTopology.PointList;
  349. case 1:
  350. return WebGPUConstants.PrimitiveTopology.LineList;
  351. // Draw modes
  352. case 3:
  353. return WebGPUConstants.PrimitiveTopology.PointList;
  354. case 4:
  355. return WebGPUConstants.PrimitiveTopology.LineList;
  356. case 5:
  357. // return this._gl.LINE_LOOP;
  358. // TODO WEBGPU. Line Loop Mode Fallback at buffer load time.
  359. // eslint-disable-next-line no-throw-literal
  360. throw "LineLoop is an unsupported fillmode in WebGPU";
  361. case 6:
  362. return WebGPUConstants.PrimitiveTopology.LineStrip;
  363. case 7:
  364. return WebGPUConstants.PrimitiveTopology.TriangleStrip;
  365. case 8:
  366. // return this._gl.TRIANGLE_FAN;
  367. // TODO WEBGPU. Triangle Fan Mode Fallback at buffer load time.
  368. // eslint-disable-next-line no-throw-literal
  369. throw "TriangleFan is an unsupported fillmode in WebGPU";
  370. default:
  371. return WebGPUConstants.PrimitiveTopology.TriangleList;
  372. }
  373. }
  374. static _GetAphaBlendOperation(operation) {
  375. switch (operation) {
  376. case 32774:
  377. return WebGPUConstants.BlendOperation.Add;
  378. case 32778:
  379. return WebGPUConstants.BlendOperation.Subtract;
  380. case 32779:
  381. return WebGPUConstants.BlendOperation.ReverseSubtract;
  382. case 32775:
  383. return WebGPUConstants.BlendOperation.Min;
  384. case 32776:
  385. return WebGPUConstants.BlendOperation.Max;
  386. default:
  387. return WebGPUConstants.BlendOperation.Add;
  388. }
  389. }
  390. static _GetAphaBlendFactor(factor) {
  391. switch (factor) {
  392. case 0:
  393. return WebGPUConstants.BlendFactor.Zero;
  394. case 1:
  395. return WebGPUConstants.BlendFactor.One;
  396. case 768:
  397. return WebGPUConstants.BlendFactor.Src;
  398. case 769:
  399. return WebGPUConstants.BlendFactor.OneMinusSrc;
  400. case 770:
  401. return WebGPUConstants.BlendFactor.SrcAlpha;
  402. case 771:
  403. return WebGPUConstants.BlendFactor.OneMinusSrcAlpha;
  404. case 772:
  405. return WebGPUConstants.BlendFactor.DstAlpha;
  406. case 773:
  407. return WebGPUConstants.BlendFactor.OneMinusDstAlpha;
  408. case 774:
  409. return WebGPUConstants.BlendFactor.Dst;
  410. case 775:
  411. return WebGPUConstants.BlendFactor.OneMinusDst;
  412. case 776:
  413. return WebGPUConstants.BlendFactor.SrcAlphaSaturated;
  414. case 32769:
  415. return WebGPUConstants.BlendFactor.Constant;
  416. case 32770:
  417. return WebGPUConstants.BlendFactor.OneMinusConstant;
  418. case 32771:
  419. return WebGPUConstants.BlendFactor.Constant;
  420. case 32772:
  421. return WebGPUConstants.BlendFactor.OneMinusConstant;
  422. default:
  423. return WebGPUConstants.BlendFactor.One;
  424. }
  425. }
  426. static _GetCompareFunction(compareFunction) {
  427. switch (compareFunction) {
  428. case 0: // NEVER
  429. return WebGPUConstants.CompareFunction.Never;
  430. case 1: // LESS
  431. return WebGPUConstants.CompareFunction.Less;
  432. case 2: // EQUAL
  433. return WebGPUConstants.CompareFunction.Equal;
  434. case 3: // LEQUAL
  435. return WebGPUConstants.CompareFunction.LessEqual;
  436. case 4: // GREATER
  437. return WebGPUConstants.CompareFunction.Greater;
  438. case 5: // NOTEQUAL
  439. return WebGPUConstants.CompareFunction.NotEqual;
  440. case 6: // GEQUAL
  441. return WebGPUConstants.CompareFunction.GreaterEqual;
  442. case 7: // ALWAYS
  443. return WebGPUConstants.CompareFunction.Always;
  444. }
  445. return WebGPUConstants.CompareFunction.Never;
  446. }
  447. static _GetStencilOpFunction(operation) {
  448. switch (operation) {
  449. case 0:
  450. return WebGPUConstants.StencilOperation.Zero;
  451. case 1:
  452. return WebGPUConstants.StencilOperation.Keep;
  453. case 2:
  454. return WebGPUConstants.StencilOperation.Replace;
  455. case 3:
  456. return WebGPUConstants.StencilOperation.IncrementClamp;
  457. case 4:
  458. return WebGPUConstants.StencilOperation.DecrementClamp;
  459. case 5:
  460. return WebGPUConstants.StencilOperation.Invert;
  461. case 6:
  462. return WebGPUConstants.StencilOperation.IncrementWrap;
  463. case 7:
  464. return WebGPUConstants.StencilOperation.DecrementWrap;
  465. }
  466. return WebGPUConstants.StencilOperation.Keep;
  467. }
  468. static _GetVertexInputDescriptorFormat(vertexBuffer) {
  469. const type = vertexBuffer.type;
  470. const normalized = vertexBuffer.normalized;
  471. const size = vertexBuffer.getSize();
  472. switch (type) {
  473. case VertexBuffer.BYTE:
  474. switch (size) {
  475. case 1:
  476. case 2:
  477. return normalized ? WebGPUConstants.VertexFormat.Snorm8x2 : WebGPUConstants.VertexFormat.Sint8x2;
  478. case 3:
  479. case 4:
  480. return normalized ? WebGPUConstants.VertexFormat.Snorm8x4 : WebGPUConstants.VertexFormat.Sint8x4;
  481. }
  482. break;
  483. case VertexBuffer.UNSIGNED_BYTE:
  484. switch (size) {
  485. case 1:
  486. case 2:
  487. return normalized ? WebGPUConstants.VertexFormat.Unorm8x2 : WebGPUConstants.VertexFormat.Uint8x2;
  488. case 3:
  489. case 4:
  490. return normalized ? WebGPUConstants.VertexFormat.Unorm8x4 : WebGPUConstants.VertexFormat.Uint8x4;
  491. }
  492. break;
  493. case VertexBuffer.SHORT:
  494. switch (size) {
  495. case 1:
  496. case 2:
  497. return normalized ? WebGPUConstants.VertexFormat.Snorm16x2 : WebGPUConstants.VertexFormat.Sint16x2;
  498. case 3:
  499. case 4:
  500. return normalized ? WebGPUConstants.VertexFormat.Snorm16x4 : WebGPUConstants.VertexFormat.Sint16x4;
  501. }
  502. break;
  503. case VertexBuffer.UNSIGNED_SHORT:
  504. switch (size) {
  505. case 1:
  506. case 2:
  507. return normalized ? WebGPUConstants.VertexFormat.Unorm16x2 : WebGPUConstants.VertexFormat.Uint16x2;
  508. case 3:
  509. case 4:
  510. return normalized ? WebGPUConstants.VertexFormat.Unorm16x4 : WebGPUConstants.VertexFormat.Uint16x4;
  511. }
  512. break;
  513. case VertexBuffer.INT:
  514. switch (size) {
  515. case 1:
  516. return WebGPUConstants.VertexFormat.Sint32;
  517. case 2:
  518. return WebGPUConstants.VertexFormat.Sint32x2;
  519. case 3:
  520. return WebGPUConstants.VertexFormat.Sint32x3;
  521. case 4:
  522. return WebGPUConstants.VertexFormat.Sint32x4;
  523. }
  524. break;
  525. case VertexBuffer.UNSIGNED_INT:
  526. switch (size) {
  527. case 1:
  528. return WebGPUConstants.VertexFormat.Uint32;
  529. case 2:
  530. return WebGPUConstants.VertexFormat.Uint32x2;
  531. case 3:
  532. return WebGPUConstants.VertexFormat.Uint32x3;
  533. case 4:
  534. return WebGPUConstants.VertexFormat.Uint32x4;
  535. }
  536. break;
  537. case VertexBuffer.FLOAT:
  538. switch (size) {
  539. case 1:
  540. return WebGPUConstants.VertexFormat.Float32;
  541. case 2:
  542. return WebGPUConstants.VertexFormat.Float32x2;
  543. case 3:
  544. return WebGPUConstants.VertexFormat.Float32x3;
  545. case 4:
  546. return WebGPUConstants.VertexFormat.Float32x4;
  547. }
  548. break;
  549. }
  550. throw new Error(`Invalid Format '${vertexBuffer.getKind()}' - type=${type}, normalized=${normalized}, size=${size}`);
  551. }
  552. _getAphaBlendState() {
  553. if (!this._alphaBlendEnabled) {
  554. return null;
  555. }
  556. return {
  557. srcFactor: WebGPUCacheRenderPipeline._GetAphaBlendFactor(this._alphaBlendFuncParams[2]),
  558. dstFactor: WebGPUCacheRenderPipeline._GetAphaBlendFactor(this._alphaBlendFuncParams[3]),
  559. operation: WebGPUCacheRenderPipeline._GetAphaBlendOperation(this._alphaBlendEqParams[1]),
  560. };
  561. }
  562. _getColorBlendState() {
  563. if (!this._alphaBlendEnabled) {
  564. return null;
  565. }
  566. return {
  567. srcFactor: WebGPUCacheRenderPipeline._GetAphaBlendFactor(this._alphaBlendFuncParams[0]),
  568. dstFactor: WebGPUCacheRenderPipeline._GetAphaBlendFactor(this._alphaBlendFuncParams[1]),
  569. operation: WebGPUCacheRenderPipeline._GetAphaBlendOperation(this._alphaBlendEqParams[0]),
  570. };
  571. }
  572. _setShaderStage(id) {
  573. if (this._shaderId !== id) {
  574. this._shaderId = id;
  575. this._states[StatePosition.ShaderStage] = id;
  576. this._isDirty = true;
  577. this._stateDirtyLowestIndex = Math.min(this._stateDirtyLowestIndex, StatePosition.ShaderStage);
  578. }
  579. }
  580. _setRasterizationState(topology, sampleCount) {
  581. const frontFace = this._frontFace;
  582. const cullMode = this._cullEnabled ? this._cullFace : 0;
  583. const clampDepth = this._clampDepth ? 1 : 0;
  584. const alphaToCoverage = this._alphaToCoverageEnabled ? 1 : 0;
  585. const rasterizationState = frontFace - 1 + (cullMode << 1) + (clampDepth << 3) + (alphaToCoverage << 4) + (topology << 5) + (sampleCount << 8);
  586. if (this._rasterizationState !== rasterizationState) {
  587. this._rasterizationState = rasterizationState;
  588. this._states[StatePosition.RasterizationState] = this._rasterizationState;
  589. this._isDirty = true;
  590. this._stateDirtyLowestIndex = Math.min(this._stateDirtyLowestIndex, StatePosition.RasterizationState);
  591. }
  592. }
  593. _setColorStates() {
  594. let colorStates = ((this._writeMask ? 1 : 0) << 22) + (this._colorFormat << 23) + ((this._depthWriteEnabled ? 1 : 0) << 29); // this state has been moved from depthStencilState here because alpha and depth are related (generally when alpha is on, depth write is off and the other way around)
  595. if (this._alphaBlendEnabled) {
  596. colorStates +=
  597. ((this._alphaBlendFuncParams[0] === null ? 2 : alphaBlendFactorToIndex[this._alphaBlendFuncParams[0]]) << 0) +
  598. ((this._alphaBlendFuncParams[1] === null ? 2 : alphaBlendFactorToIndex[this._alphaBlendFuncParams[1]]) << 4) +
  599. ((this._alphaBlendFuncParams[2] === null ? 2 : alphaBlendFactorToIndex[this._alphaBlendFuncParams[2]]) << 8) +
  600. ((this._alphaBlendFuncParams[3] === null ? 2 : alphaBlendFactorToIndex[this._alphaBlendFuncParams[3]]) << 12) +
  601. ((this._alphaBlendEqParams[0] === null ? 1 : this._alphaBlendEqParams[0] - 0x8005) << 16) +
  602. ((this._alphaBlendEqParams[1] === null ? 1 : this._alphaBlendEqParams[1] - 0x8005) << 19);
  603. }
  604. if (colorStates !== this._colorStates) {
  605. this._colorStates = colorStates;
  606. this._states[StatePosition.ColorStates] = this._colorStates;
  607. this._isDirty = true;
  608. this._stateDirtyLowestIndex = Math.min(this._stateDirtyLowestIndex, StatePosition.ColorStates);
  609. }
  610. }
  611. _setDepthStencilState() {
  612. const stencilState = !this._stencilEnabled
  613. ? 7 /* ALWAYS */ + (1 /* KEEP */ << 3) + (1 /* KEEP */ << 6) + (1 /* KEEP */ << 9)
  614. : this._stencilFrontCompare + (this._stencilFrontDepthFailOp << 3) + (this._stencilFrontPassOp << 6) + (this._stencilFrontFailOp << 9);
  615. const depthStencilState = this._depthStencilFormat + ((this._depthTestEnabled ? this._depthCompare : 7) /* ALWAYS */ << 6) + (stencilState << 10); // stencil front - stencil back is the same
  616. if (this._depthStencilState !== depthStencilState) {
  617. this._depthStencilState = depthStencilState;
  618. this._states[StatePosition.DepthStencilState] = this._depthStencilState;
  619. this._isDirty = true;
  620. this._stateDirtyLowestIndex = Math.min(this._stateDirtyLowestIndex, StatePosition.DepthStencilState);
  621. }
  622. }
  623. _setVertexState(effect) {
  624. const currStateLen = this._statesLength;
  625. let newNumStates = StatePosition.VertexState;
  626. const webgpuPipelineContext = effect._pipelineContext;
  627. const attributes = webgpuPipelineContext.shaderProcessingContext.attributeNamesFromEffect;
  628. const locations = webgpuPipelineContext.shaderProcessingContext.attributeLocationsFromEffect;
  629. let currentGPUBuffer;
  630. let numVertexBuffers = 0;
  631. for (let index = 0; index < attributes.length; index++) {
  632. const location = locations[index];
  633. let vertexBuffer = (this._overrideVertexBuffers && this._overrideVertexBuffers[attributes[index]]) ?? this._vertexBuffers[attributes[index]];
  634. if (!vertexBuffer) {
  635. // In WebGL it's valid to not bind a vertex buffer to an attribute, but it's not valid in WebGPU
  636. // So we must bind a dummy buffer when we are not given one for a specific attribute
  637. vertexBuffer = this._emptyVertexBuffer;
  638. }
  639. const buffer = vertexBuffer.effectiveBuffer?.underlyingResource;
  640. // We optimize usage of GPUVertexBufferLayout: we will create a single GPUVertexBufferLayout for all the attributes which follow each other and which use the same GPU buffer
  641. // However, there are some constraints in the attribute.offset value range, so we must check for them before being able to reuse the same GPUVertexBufferLayout
  642. // See _getVertexInputDescriptor() below
  643. if (vertexBuffer._validOffsetRange === undefined) {
  644. const offset = vertexBuffer.effectiveByteOffset;
  645. const formatSize = vertexBuffer.getSize(true);
  646. const byteStride = vertexBuffer.effectiveByteStride;
  647. vertexBuffer._validOffsetRange =
  648. (offset + formatSize <= this._kMaxVertexBufferStride && byteStride === 0) || (byteStride !== 0 && offset + formatSize <= byteStride);
  649. }
  650. if (!(currentGPUBuffer && currentGPUBuffer === buffer && vertexBuffer._validOffsetRange)) {
  651. // we can't combine the previous vertexBuffer with the current one
  652. this.vertexBuffers[numVertexBuffers++] = vertexBuffer;
  653. currentGPUBuffer = vertexBuffer._validOffsetRange ? buffer : null;
  654. }
  655. const vid = vertexBuffer.hashCode + (location << 7);
  656. this._isDirty = this._isDirty || this._states[newNumStates] !== vid;
  657. this._states[newNumStates++] = vid;
  658. }
  659. this.vertexBuffers.length = numVertexBuffers;
  660. this._statesLength = newNumStates;
  661. this._isDirty = this._isDirty || newNumStates !== currStateLen;
  662. if (this._isDirty) {
  663. this._stateDirtyLowestIndex = Math.min(this._stateDirtyLowestIndex, StatePosition.VertexState);
  664. }
  665. }
  666. _setTextureState(textureState) {
  667. if (this._textureState !== textureState) {
  668. this._textureState = textureState;
  669. this._states[StatePosition.TextureStage] = this._textureState;
  670. this._isDirty = true;
  671. this._stateDirtyLowestIndex = Math.min(this._stateDirtyLowestIndex, StatePosition.TextureStage);
  672. }
  673. }
  674. _createPipelineLayout(webgpuPipelineContext) {
  675. if (this._useTextureStage) {
  676. return this._createPipelineLayoutWithTextureStage(webgpuPipelineContext);
  677. }
  678. const bindGroupLayouts = [];
  679. const bindGroupLayoutEntries = webgpuPipelineContext.shaderProcessingContext.bindGroupLayoutEntries;
  680. for (let i = 0; i < bindGroupLayoutEntries.length; i++) {
  681. const setDefinition = bindGroupLayoutEntries[i];
  682. bindGroupLayouts[i] = this._device.createBindGroupLayout({
  683. entries: setDefinition,
  684. });
  685. }
  686. webgpuPipelineContext.bindGroupLayouts[0] = bindGroupLayouts;
  687. return this._device.createPipelineLayout({ bindGroupLayouts });
  688. }
  689. _createPipelineLayoutWithTextureStage(webgpuPipelineContext) {
  690. const shaderProcessingContext = webgpuPipelineContext.shaderProcessingContext;
  691. const bindGroupLayoutEntries = shaderProcessingContext.bindGroupLayoutEntries;
  692. let bitVal = 1;
  693. for (let i = 0; i < bindGroupLayoutEntries.length; i++) {
  694. const setDefinition = bindGroupLayoutEntries[i];
  695. for (let j = 0; j < setDefinition.length; j++) {
  696. const entry = bindGroupLayoutEntries[i][j];
  697. if (entry.texture) {
  698. const name = shaderProcessingContext.bindGroupLayoutEntryInfo[i][entry.binding].name;
  699. const textureInfo = shaderProcessingContext.availableTextures[name];
  700. const samplerInfo = textureInfo.autoBindSampler ? shaderProcessingContext.availableSamplers[name + WebGPUShaderProcessor.AutoSamplerSuffix] : null;
  701. let sampleType = textureInfo.sampleType;
  702. let samplerType = samplerInfo?.type ?? WebGPUConstants.SamplerBindingType.Filtering;
  703. if (this._textureState & bitVal && sampleType !== WebGPUConstants.TextureSampleType.Depth) {
  704. // The texture is a 32 bits float texture but the system does not support linear filtering for them OR the texture is a depth texture with "float" filtering:
  705. // we set the sampler to "non-filtering" and the texture sample type to "unfilterable-float"
  706. if (textureInfo.autoBindSampler) {
  707. samplerType = WebGPUConstants.SamplerBindingType.NonFiltering;
  708. }
  709. sampleType = WebGPUConstants.TextureSampleType.UnfilterableFloat;
  710. }
  711. entry.texture.sampleType = sampleType;
  712. if (samplerInfo) {
  713. const binding = shaderProcessingContext.bindGroupLayoutEntryInfo[samplerInfo.binding.groupIndex][samplerInfo.binding.bindingIndex].index;
  714. bindGroupLayoutEntries[samplerInfo.binding.groupIndex][binding].sampler.type = samplerType;
  715. }
  716. bitVal = bitVal << 1;
  717. }
  718. }
  719. }
  720. const bindGroupLayouts = [];
  721. for (let i = 0; i < bindGroupLayoutEntries.length; ++i) {
  722. bindGroupLayouts[i] = this._device.createBindGroupLayout({
  723. entries: bindGroupLayoutEntries[i],
  724. });
  725. }
  726. webgpuPipelineContext.bindGroupLayouts[this._textureState] = bindGroupLayouts;
  727. return this._device.createPipelineLayout({ bindGroupLayouts });
  728. }
  729. _getVertexInputDescriptor(effect) {
  730. const descriptors = [];
  731. const webgpuPipelineContext = effect._pipelineContext;
  732. const attributes = webgpuPipelineContext.shaderProcessingContext.attributeNamesFromEffect;
  733. const locations = webgpuPipelineContext.shaderProcessingContext.attributeLocationsFromEffect;
  734. let currentGPUBuffer;
  735. let currentGPUAttributes;
  736. for (let index = 0; index < attributes.length; index++) {
  737. const location = locations[index];
  738. let vertexBuffer = (this._overrideVertexBuffers && this._overrideVertexBuffers[attributes[index]]) ?? this._vertexBuffers[attributes[index]];
  739. if (!vertexBuffer) {
  740. // In WebGL it's valid to not bind a vertex buffer to an attribute, but it's not valid in WebGPU
  741. // So we must bind a dummy buffer when we are not given one for a specific attribute
  742. vertexBuffer = this._emptyVertexBuffer;
  743. }
  744. let buffer = vertexBuffer.effectiveBuffer?.underlyingResource;
  745. // We reuse the same GPUVertexBufferLayout for all attributes that use the same underlying GPU buffer (and for attributes that follow each other in the attributes array)
  746. let offset = vertexBuffer.effectiveByteOffset;
  747. const invalidOffsetRange = !vertexBuffer._validOffsetRange;
  748. if (!(currentGPUBuffer && currentGPUAttributes && currentGPUBuffer === buffer) || invalidOffsetRange) {
  749. const vertexBufferDescriptor = {
  750. arrayStride: vertexBuffer.effectiveByteStride,
  751. stepMode: vertexBuffer.getIsInstanced() ? WebGPUConstants.VertexStepMode.Instance : WebGPUConstants.VertexStepMode.Vertex,
  752. attributes: [],
  753. };
  754. descriptors.push(vertexBufferDescriptor);
  755. currentGPUAttributes = vertexBufferDescriptor.attributes;
  756. if (invalidOffsetRange) {
  757. offset = 0; // the offset will be set directly in the setVertexBuffer call
  758. buffer = null; // buffer can't be reused
  759. }
  760. }
  761. currentGPUAttributes.push({
  762. shaderLocation: location,
  763. offset,
  764. format: WebGPUCacheRenderPipeline._GetVertexInputDescriptorFormat(vertexBuffer),
  765. });
  766. currentGPUBuffer = buffer;
  767. }
  768. return descriptors;
  769. }
  770. _processNonFloatVertexBuffers(webgpuPipelineContext, effect) {
  771. const webgpuShaderProcessor = webgpuPipelineContext.engine._getShaderProcessor(webgpuPipelineContext.shaderProcessingContext.shaderLanguage);
  772. let reprocessShaders = false;
  773. for (const kind in this._vertexBuffers) {
  774. const currentVertexBuffer = this._vertexBuffers[kind];
  775. if (!currentVertexBuffer || !vertexBufferKindForNonFloatProcessing[kind]) {
  776. continue;
  777. }
  778. const currentVertexBufferType = currentVertexBuffer.normalized ? VertexBuffer.FLOAT : currentVertexBuffer.type;
  779. const vertexBufferType = webgpuPipelineContext.vertexBufferKindToType[kind];
  780. if ((currentVertexBufferType !== VertexBuffer.FLOAT && vertexBufferType === undefined) ||
  781. (vertexBufferType !== undefined && vertexBufferType !== currentVertexBufferType)) {
  782. reprocessShaders = true;
  783. webgpuPipelineContext.vertexBufferKindToType[kind] = currentVertexBufferType;
  784. if (currentVertexBufferType !== VertexBuffer.FLOAT) {
  785. webgpuShaderProcessor.vertexBufferKindToNumberOfComponents[kind] = VertexBuffer.DeduceStride(kind);
  786. if (WebGPUCacheRenderPipeline._IsSignedType(currentVertexBufferType)) {
  787. webgpuShaderProcessor.vertexBufferKindToNumberOfComponents[kind] *= -1;
  788. }
  789. }
  790. }
  791. }
  792. if (reprocessShaders) {
  793. effect._processShaderCode(webgpuShaderProcessor, true);
  794. }
  795. }
  796. _createRenderPipeline(effect, topology, sampleCount) {
  797. const webgpuPipelineContext = effect._pipelineContext;
  798. const inputStateDescriptor = this._getVertexInputDescriptor(effect);
  799. const pipelineLayout = this._createPipelineLayout(webgpuPipelineContext);
  800. const colorStates = [];
  801. const alphaBlend = this._getAphaBlendState();
  802. const colorBlend = this._getColorBlendState();
  803. this._processNonFloatVertexBuffers(webgpuPipelineContext, effect);
  804. if (this._mrtAttachments1 > 0) {
  805. for (let i = 0; i < this._mrtFormats.length; ++i) {
  806. const format = this._mrtFormats[i];
  807. if (format) {
  808. const descr = {
  809. format,
  810. writeMask: (this._mrtEnabledMask & (1 << i)) !== 0 ? this._writeMask : 0,
  811. };
  812. if (alphaBlend && colorBlend) {
  813. descr.blend = {
  814. alpha: alphaBlend,
  815. color: colorBlend,
  816. };
  817. }
  818. colorStates.push(descr);
  819. }
  820. else {
  821. colorStates.push(null);
  822. }
  823. }
  824. }
  825. else {
  826. if (this._webgpuColorFormat[0]) {
  827. const descr = {
  828. format: this._webgpuColorFormat[0],
  829. writeMask: this._writeMask,
  830. };
  831. if (alphaBlend && colorBlend) {
  832. descr.blend = {
  833. alpha: alphaBlend,
  834. color: colorBlend,
  835. };
  836. }
  837. colorStates.push(descr);
  838. }
  839. else {
  840. colorStates.push(null);
  841. }
  842. }
  843. const stencilFrontBack = {
  844. compare: WebGPUCacheRenderPipeline._GetCompareFunction(this._stencilEnabled ? this._stencilFrontCompare : 7 /* ALWAYS */),
  845. depthFailOp: WebGPUCacheRenderPipeline._GetStencilOpFunction(this._stencilEnabled ? this._stencilFrontDepthFailOp : 1 /* KEEP */),
  846. failOp: WebGPUCacheRenderPipeline._GetStencilOpFunction(this._stencilEnabled ? this._stencilFrontFailOp : 1 /* KEEP */),
  847. passOp: WebGPUCacheRenderPipeline._GetStencilOpFunction(this._stencilEnabled ? this._stencilFrontPassOp : 1 /* KEEP */),
  848. };
  849. let stripIndexFormat = undefined;
  850. if (topology === WebGPUConstants.PrimitiveTopology.LineStrip || topology === WebGPUConstants.PrimitiveTopology.TriangleStrip) {
  851. stripIndexFormat = !this._indexBuffer || this._indexBuffer.is32Bits ? WebGPUConstants.IndexFormat.Uint32 : WebGPUConstants.IndexFormat.Uint16;
  852. }
  853. const depthStencilFormatHasStencil = this._webgpuDepthStencilFormat ? WebGPUTextureHelper.HasStencilAspect(this._webgpuDepthStencilFormat) : false;
  854. return this._device.createRenderPipeline({
  855. label: `RenderPipeline_${colorStates[0]?.format ?? "nooutput"}_${this._webgpuDepthStencilFormat ?? "nodepth"}_samples${sampleCount}_textureState${this._textureState}`,
  856. layout: pipelineLayout,
  857. vertex: {
  858. module: webgpuPipelineContext.stages.vertexStage.module,
  859. entryPoint: webgpuPipelineContext.stages.vertexStage.entryPoint,
  860. buffers: inputStateDescriptor,
  861. },
  862. primitive: {
  863. topology,
  864. stripIndexFormat,
  865. frontFace: this._frontFace === 1 ? WebGPUConstants.FrontFace.CCW : WebGPUConstants.FrontFace.CW,
  866. cullMode: !this._cullEnabled ? WebGPUConstants.CullMode.None : this._cullFace === 2 ? WebGPUConstants.CullMode.Front : WebGPUConstants.CullMode.Back,
  867. },
  868. fragment: !webgpuPipelineContext.stages.fragmentStage
  869. ? undefined
  870. : {
  871. module: webgpuPipelineContext.stages.fragmentStage.module,
  872. entryPoint: webgpuPipelineContext.stages.fragmentStage.entryPoint,
  873. targets: colorStates,
  874. },
  875. multisample: {
  876. count: sampleCount,
  877. /*mask,
  878. alphaToCoverageEnabled,*/
  879. },
  880. depthStencil: this._webgpuDepthStencilFormat === undefined
  881. ? undefined
  882. : {
  883. depthWriteEnabled: this._depthWriteEnabled,
  884. depthCompare: this._depthTestEnabled ? WebGPUCacheRenderPipeline._GetCompareFunction(this._depthCompare) : WebGPUConstants.CompareFunction.Always,
  885. format: this._webgpuDepthStencilFormat,
  886. stencilFront: this._stencilEnabled && depthStencilFormatHasStencil ? stencilFrontBack : undefined,
  887. stencilBack: this._stencilEnabled && depthStencilFormatHasStencil ? stencilFrontBack : undefined,
  888. stencilReadMask: this._stencilEnabled && depthStencilFormatHasStencil ? this._stencilReadMask : undefined,
  889. stencilWriteMask: this._stencilEnabled && depthStencilFormatHasStencil ? this._stencilWriteMask : undefined,
  890. depthBias: this._depthBias,
  891. depthBiasClamp: this._depthBiasClamp,
  892. depthBiasSlopeScale: this._depthBiasSlopeScale,
  893. },
  894. });
  895. }
  896. }
  897. WebGPUCacheRenderPipeline.NumCacheHitWithoutHash = 0;
  898. WebGPUCacheRenderPipeline.NumCacheHitWithHash = 0;
  899. WebGPUCacheRenderPipeline.NumCacheMiss = 0;
  900. WebGPUCacheRenderPipeline.NumPipelineCreationLastFrame = 0;
  901. WebGPUCacheRenderPipeline._NumPipelineCreationCurrentFrame = 0;
  902. //# sourceMappingURL=webgpuCacheRenderPipeline.js.map