webgpuEngine.js 141 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060
  1. /* eslint-disable babylonjs/available */
  2. import { Logger } from "../Misc/logger.js";
  3. import { Color4 } from "../Maths/math.js";
  4. import { Engine } from "../Engines/engine.js";
  5. import { InternalTexture, InternalTextureSource } from "../Materials/Textures/internalTexture.js";
  6. import { Effect } from "../Materials/effect.js";
  7. // eslint-disable-next-line @typescript-eslint/naming-convention
  8. import * as WebGPUConstants from "./WebGPU/webgpuConstants.js";
  9. import { VertexBuffer } from "../Buffers/buffer.js";
  10. import { WebGPUPipelineContext } from "./WebGPU/webgpuPipelineContext.js";
  11. import { WebGPUShaderProcessorGLSL } from "./WebGPU/webgpuShaderProcessorsGLSL.js";
  12. import { WebGPUShaderProcessorWGSL } from "./WebGPU/webgpuShaderProcessorsWGSL.js";
  13. import { WebGPUShaderProcessingContext } from "./WebGPU/webgpuShaderProcessingContext.js";
  14. import { Tools } from "../Misc/tools.js";
  15. import { WebGPUTextureHelper } from "./WebGPU/webgpuTextureHelper.js";
  16. import { WebGPUTextureManager } from "./WebGPU/webgpuTextureManager.js";
  17. import { AbstractEngine } from "./abstractEngine.js";
  18. import { WebGPUBufferManager } from "./WebGPU/webgpuBufferManager.js";
  19. import { WebGPUHardwareTexture } from "./WebGPU/webgpuHardwareTexture.js";
  20. import { UniformBuffer } from "../Materials/uniformBuffer.js";
  21. import { WebGPUCacheSampler } from "./WebGPU/webgpuCacheSampler.js";
  22. import { WebGPUCacheRenderPipelineTree } from "./WebGPU/webgpuCacheRenderPipelineTree.js";
  23. import { WebGPUStencilStateComposer } from "./WebGPU/webgpuStencilStateComposer.js";
  24. import { WebGPUDepthCullingState } from "./WebGPU/webgpuDepthCullingState.js";
  25. import { WebGPUMaterialContext } from "./WebGPU/webgpuMaterialContext.js";
  26. import { WebGPUDrawContext } from "./WebGPU/webgpuDrawContext.js";
  27. import { WebGPUCacheBindGroups } from "./WebGPU/webgpuCacheBindGroups.js";
  28. import { WebGPUClearQuad } from "./WebGPU/webgpuClearQuad.js";
  29. import { WebGPURenderItemBlendColor, WebGPURenderItemScissor, WebGPURenderItemStencilRef, WebGPURenderItemViewport, WebGPUBundleList } from "./WebGPU/webgpuBundleList.js";
  30. import { WebGPUTimestampQuery } from "./WebGPU/webgpuTimestampQuery.js";
  31. import { WebGPUOcclusionQuery } from "./WebGPU/webgpuOcclusionQuery.js";
  32. import { ShaderCodeInliner } from "./Processors/shaderCodeInliner.js";
  33. import { WebGPUTintWASM } from "./WebGPU/webgpuTintWASM.js";
  34. import { WebGPUShaderProcessor } from "./WebGPU/webgpuShaderProcessor.js";
  35. import { ShaderLanguage } from "../Materials/shaderLanguage.js";
  36. import { WebGPUSnapshotRendering } from "./WebGPU/webgpuSnapshotRendering.js";
  37. import "../Buffers/buffer.align.js";
  38. import "../ShadersWGSL/postprocess.vertex.js";
  39. import { WebGPUPerfCounter } from "./WebGPU/webgpuPerfCounter.js";
  40. import { SphericalPolynomial } from "../Maths/sphericalPolynomial.js";
  41. import { PerformanceMonitor } from "../Misc/performanceMonitor.js";
  42. import { CreateImageBitmapFromSource, ExitFullscreen, ExitPointerlock, GetFontOffset, RequestFullscreen, RequestPointerlock, ResizeImageBitmap, _CommonDispose, _CommonInit, } from "./engine.common.js";
  43. import { IsWrapper } from "../Materials/drawWrapper.functions.js";
  44. import { PerfCounter } from "../Misc/perfCounter.js";
  45. import "./AbstractEngine/abstractEngine.loadingScreen.js";
  46. import "./AbstractEngine/abstractEngine.dom.js";
  47. import "./AbstractEngine/abstractEngine.states.js";
  48. import "./AbstractEngine/abstractEngine.renderPass.js";
  49. import "../Audio/audioEngine.js";
  50. const viewDescriptorSwapChainAntialiasing = {
  51. label: `TextureView_SwapChain_ResolveTarget`,
  52. dimension: WebGPUConstants.TextureDimension.E2d,
  53. format: undefined,
  54. mipLevelCount: 1,
  55. arrayLayerCount: 1,
  56. };
  57. const viewDescriptorSwapChain = {
  58. label: `TextureView_SwapChain`,
  59. dimension: WebGPUConstants.TextureDimension.E2d,
  60. format: undefined,
  61. mipLevelCount: 1,
  62. arrayLayerCount: 1,
  63. };
  64. const disableUniformityAnalysisMarker = "/* disable_uniformity_analysis */";
  65. const tempColor4 = new Color4();
  66. /**
  67. * The web GPU engine class provides support for WebGPU version of babylon.js.
  68. * @since 5.0.0
  69. */
  70. export class WebGPUEngine extends AbstractEngine {
  71. /**
  72. * Gets or sets the snapshot rendering mode
  73. */
  74. get snapshotRenderingMode() {
  75. return this._snapshotRendering.mode;
  76. }
  77. set snapshotRenderingMode(mode) {
  78. this._snapshotRendering.mode = mode;
  79. }
  80. /**
  81. * Creates a new snapshot at the next frame using the current snapshotRenderingMode
  82. */
  83. snapshotRenderingReset() {
  84. this._snapshotRendering.reset();
  85. }
  86. /**
  87. * Enables or disables the snapshot rendering mode
  88. * Note that the WebGL engine does not support snapshot rendering so setting the value won't have any effect for this engine
  89. */
  90. get snapshotRendering() {
  91. return this._snapshotRendering.enabled;
  92. }
  93. set snapshotRendering(activate) {
  94. this._snapshotRendering.enabled = activate;
  95. }
  96. /**
  97. * Sets this to true to disable the cache for the samplers. You should do it only for testing purpose!
  98. */
  99. get disableCacheSamplers() {
  100. return this._cacheSampler ? this._cacheSampler.disabled : false;
  101. }
  102. set disableCacheSamplers(disable) {
  103. if (this._cacheSampler) {
  104. this._cacheSampler.disabled = disable;
  105. }
  106. }
  107. /**
  108. * Sets this to true to disable the cache for the render pipelines. You should do it only for testing purpose!
  109. */
  110. get disableCacheRenderPipelines() {
  111. return this._cacheRenderPipeline ? this._cacheRenderPipeline.disabled : false;
  112. }
  113. set disableCacheRenderPipelines(disable) {
  114. if (this._cacheRenderPipeline) {
  115. this._cacheRenderPipeline.disabled = disable;
  116. }
  117. }
  118. /**
  119. * Sets this to true to disable the cache for the bind groups. You should do it only for testing purpose!
  120. */
  121. get disableCacheBindGroups() {
  122. return this._cacheBindGroups ? this._cacheBindGroups.disabled : false;
  123. }
  124. set disableCacheBindGroups(disable) {
  125. if (this._cacheBindGroups) {
  126. this._cacheBindGroups.disabled = disable;
  127. }
  128. }
  129. /**
  130. * Gets a boolean indicating if all created effects are ready
  131. * @returns true if all effects are ready
  132. */
  133. areAllEffectsReady() {
  134. return true;
  135. }
  136. /**
  137. * Get Font size information
  138. * @param font font name
  139. * @returns an object containing ascent, height and descent
  140. */
  141. getFontOffset(font) {
  142. return GetFontOffset(font);
  143. }
  144. /**
  145. * Gets a Promise<boolean> indicating if the engine can be instantiated (ie. if a WebGPU context can be found)
  146. */
  147. static get IsSupportedAsync() {
  148. return !navigator.gpu
  149. ? Promise.resolve(false)
  150. : navigator.gpu
  151. .requestAdapter()
  152. .then((adapter) => !!adapter, () => false)
  153. .catch(() => false);
  154. }
  155. /**
  156. * Not supported by WebGPU, you should call IsSupportedAsync instead!
  157. */
  158. static get IsSupported() {
  159. Logger.Warn("You must call IsSupportedAsync for WebGPU!");
  160. return false;
  161. }
  162. /**
  163. * Gets a boolean indicating that the engine supports uniform buffers
  164. */
  165. get supportsUniformBuffers() {
  166. return true;
  167. }
  168. /** Gets the supported extensions by the WebGPU adapter */
  169. get supportedExtensions() {
  170. return this._adapterSupportedExtensions;
  171. }
  172. /** Gets the currently enabled extensions on the WebGPU device */
  173. get enabledExtensions() {
  174. return this._deviceEnabledExtensions;
  175. }
  176. /** Gets the supported limits by the WebGPU adapter */
  177. get supportedLimits() {
  178. return this._adapterSupportedLimits;
  179. }
  180. /** Gets the current limits of the WebGPU device */
  181. get currentLimits() {
  182. return this._deviceLimits;
  183. }
  184. /**
  185. * Returns a string describing the current engine
  186. */
  187. get description() {
  188. const description = this.name + this.version;
  189. return description;
  190. }
  191. /**
  192. * Returns the version of the engine
  193. */
  194. get version() {
  195. return 1;
  196. }
  197. /**
  198. * Gets an object containing information about the current engine context
  199. * @returns an object containing the vendor, the renderer and the version of the current engine context
  200. */
  201. getInfo() {
  202. return {
  203. vendor: this._adapterInfo.vendor || "unknown vendor",
  204. renderer: this._adapterInfo.architecture || "unknown renderer",
  205. version: this._adapterInfo.description || "unknown version",
  206. };
  207. }
  208. /**
  209. * (WebGPU only) True (default) to be in compatibility mode, meaning rendering all existing scenes without artifacts (same rendering than WebGL).
  210. * Setting the property to false will improve performances but may not work in some scenes if some precautions are not taken.
  211. * See https://doc.babylonjs.com/setup/support/webGPU/webGPUOptimization/webGPUNonCompatibilityMode for more details
  212. */
  213. get compatibilityMode() {
  214. return this._compatibilityMode;
  215. }
  216. set compatibilityMode(mode) {
  217. this._compatibilityMode = mode;
  218. }
  219. /**
  220. * Enables or disables GPU timing measurements.
  221. * Note that this is only supported if the "timestamp-query" extension is enabled in the options.
  222. */
  223. get enableGPUTimingMeasurements() {
  224. return this._timestampQuery.enable;
  225. }
  226. set enableGPUTimingMeasurements(enable) {
  227. if (this._timestampQuery.enable === enable) {
  228. return;
  229. }
  230. this.gpuTimeInFrameForMainPass = enable ? new WebGPUPerfCounter() : undefined;
  231. this._timestampQuery.enable = enable;
  232. }
  233. /** @internal */
  234. get currentSampleCount() {
  235. return this._currentRenderTarget ? this._currentRenderTarget.samples : this._mainPassSampleCount;
  236. }
  237. /**
  238. * Create a new instance of the gpu engine asynchronously
  239. * @param canvas Defines the canvas to use to display the result
  240. * @param options Defines the options passed to the engine to create the GPU context dependencies
  241. * @returns a promise that resolves with the created engine
  242. */
  243. static CreateAsync(canvas, options = {}) {
  244. const engine = new WebGPUEngine(canvas, options);
  245. return new Promise((resolve) => {
  246. engine.initAsync(options.glslangOptions, options.twgslOptions).then(() => resolve(engine));
  247. });
  248. }
  249. /**
  250. * Create a new instance of the gpu engine.
  251. * @param canvas Defines the canvas to use to display the result
  252. * @param options Defines the options passed to the engine to create the GPU context dependencies
  253. */
  254. constructor(canvas, options = {}) {
  255. super(options.antialias ?? true, options);
  256. /** A unique id to identify this instance */
  257. this.uniqueId = -1;
  258. // Page Life cycle and constants
  259. this._uploadEncoderDescriptor = { label: "upload" };
  260. this._renderEncoderDescriptor = { label: "render" };
  261. /** @internal */
  262. this._clearDepthValue = 1;
  263. /** @internal */
  264. this._clearReverseDepthValue = 0;
  265. /** @internal */
  266. this._clearStencilValue = 0;
  267. this._defaultSampleCount = 4; // Only supported value for now.
  268. this._glslang = null;
  269. this._tintWASM = null;
  270. this._adapterInfo = {
  271. vendor: "",
  272. architecture: "",
  273. device: "",
  274. description: "",
  275. };
  276. /** @internal */
  277. this._timestampIndex = 0;
  278. /** @internal */
  279. this._compiledComputeEffects = {};
  280. /** @internal */
  281. this._counters = {
  282. numEnableEffects: 0,
  283. numEnableDrawWrapper: 0,
  284. numBundleCreationNonCompatMode: 0,
  285. numBundleReuseNonCompatMode: 0,
  286. };
  287. /**
  288. * Counters from last frame
  289. */
  290. this.countersLastFrame = {
  291. numEnableEffects: 0,
  292. numEnableDrawWrapper: 0,
  293. numBundleCreationNonCompatMode: 0,
  294. numBundleReuseNonCompatMode: 0,
  295. };
  296. /**
  297. * Max number of uncaptured error messages to log
  298. */
  299. this.numMaxUncapturedErrors = 20;
  300. /**
  301. * Gets the list of created scenes
  302. */
  303. this.scenes = [];
  304. /** @internal */
  305. this._virtualScenes = new Array();
  306. this._commandBuffers = [null, null];
  307. // Frame Buffer Life Cycle (recreated for each render target pass)
  308. /** @internal */
  309. this._currentRenderPass = null;
  310. this._mainRenderPassWrapper = {
  311. renderPassDescriptor: null,
  312. colorAttachmentViewDescriptor: null,
  313. depthAttachmentViewDescriptor: null,
  314. colorAttachmentGPUTextures: [],
  315. depthTextureFormat: undefined,
  316. };
  317. this._rttRenderPassWrapper = {
  318. renderPassDescriptor: null,
  319. colorAttachmentViewDescriptor: null,
  320. depthAttachmentViewDescriptor: null,
  321. colorAttachmentGPUTextures: [],
  322. depthTextureFormat: undefined,
  323. };
  324. /** @internal */
  325. this._pendingDebugCommands = [];
  326. this._currentOverrideVertexBuffers = null;
  327. this._currentIndexBuffer = null;
  328. this._colorWriteLocal = true;
  329. this._forceEnableEffect = false;
  330. // TODO WEBGPU remove those variables when code stabilized
  331. /** @internal */
  332. this.dbgShowShaderCode = false;
  333. /** @internal */
  334. this.dbgSanityChecks = true;
  335. /** @internal */
  336. this.dbgVerboseLogsForFirstFrames = false;
  337. /** @internal */
  338. this.dbgVerboseLogsNumFrames = 10;
  339. /** @internal */
  340. this.dbgLogIfNotDrawWrapper = true;
  341. /** @internal */
  342. this.dbgShowEmptyEnableEffectCalls = true;
  343. this._snapshotRenderingMode = 0;
  344. /**
  345. * Indicates if the z range in NDC space is 0..1 (value: true) or -1..1 (value: false)
  346. */
  347. this.isNDCHalfZRange = true;
  348. /**
  349. * Indicates that the origin of the texture/framebuffer space is the bottom left corner. If false, the origin is top left
  350. */
  351. this.hasOriginBottomLeft = false;
  352. //------------------------------------------------------------------------------
  353. // Dynamic WebGPU States
  354. //------------------------------------------------------------------------------
  355. // index 0 is for main render pass, 1 for RTT render pass
  356. this._viewportsCurrent = { x: 0, y: 0, w: 0, h: 0 };
  357. this._scissorsCurrent = { x: 0, y: 0, w: 0, h: 0 };
  358. this._scissorCached = { x: 0, y: 0, z: 0, w: 0 };
  359. this._stencilRefsCurrent = -1;
  360. this._blendColorsCurrent = [null, null, null, null];
  361. this._performanceMonitor = new PerformanceMonitor();
  362. this._name = "WebGPU";
  363. this._drawCalls = new PerfCounter();
  364. this._creationOptions = options;
  365. options.deviceDescriptor = options.deviceDescriptor || {};
  366. options.enableGPUDebugMarkers = options.enableGPUDebugMarkers ?? false;
  367. Logger.Log(`Babylon.js v${Engine.Version} - ${this.description} engine`);
  368. if (!navigator.gpu) {
  369. Logger.Error("WebGPU is not supported by your browser.");
  370. return;
  371. }
  372. options.swapChainFormat = options.swapChainFormat || navigator.gpu.getPreferredCanvasFormat();
  373. this._isWebGPU = true;
  374. this._shaderPlatformName = "WEBGPU";
  375. this._renderingCanvas = canvas;
  376. this._options = options;
  377. this._mainPassSampleCount = options.antialias ? this._defaultSampleCount : 1;
  378. if (navigator && navigator.userAgent) {
  379. this._setupMobileChecks();
  380. }
  381. this._sharedInit(this._renderingCanvas);
  382. this._shaderProcessor = new WebGPUShaderProcessorGLSL();
  383. this._shaderProcessorWGSL = new WebGPUShaderProcessorWGSL();
  384. }
  385. //------------------------------------------------------------------------------
  386. // Initialization
  387. //------------------------------------------------------------------------------
  388. /**
  389. * Initializes the WebGPU context and dependencies.
  390. * @param glslangOptions Defines the GLSLang compiler options if necessary
  391. * @param twgslOptions Defines the Twgsl compiler options if necessary
  392. * @returns a promise notifying the readiness of the engine.
  393. */
  394. initAsync(glslangOptions, twgslOptions) {
  395. this.uniqueId = WebGPUEngine._InstanceId++;
  396. this._glslangOptions = glslangOptions;
  397. this._twgslOptions = twgslOptions;
  398. return this._initGlslang(glslangOptions ?? this._options?.glslangOptions)
  399. .then((glslang) => {
  400. this._glslang = glslang;
  401. this._tintWASM = WebGPUEngine.UseTWGSL ? new WebGPUTintWASM() : null;
  402. return this._tintWASM
  403. ? this._tintWASM.initTwgsl(twgslOptions ?? this._options?.twgslOptions).then(() => {
  404. return navigator.gpu.requestAdapter(this._options);
  405. })
  406. : navigator.gpu.requestAdapter(this._options);
  407. })
  408. .then((adapter) => {
  409. if (!adapter) {
  410. // eslint-disable-next-line no-throw-literal
  411. throw "Could not retrieve a WebGPU adapter (adapter is null).";
  412. }
  413. else {
  414. this._adapter = adapter;
  415. this._adapterSupportedExtensions = [];
  416. this._adapter.features?.forEach((feature) => this._adapterSupportedExtensions.push(feature));
  417. this._adapterSupportedLimits = this._adapter.limits;
  418. this._adapter.requestAdapterInfo().then((adapterInfo) => {
  419. this._adapterInfo = adapterInfo;
  420. });
  421. const deviceDescriptor = this._options.deviceDescriptor ?? {};
  422. const requiredFeatures = deviceDescriptor?.requiredFeatures ?? (this._options.enableAllFeatures ? this._adapterSupportedExtensions : undefined);
  423. if (requiredFeatures) {
  424. const requestedExtensions = requiredFeatures;
  425. const validExtensions = [];
  426. for (const extension of requestedExtensions) {
  427. if (this._adapterSupportedExtensions.indexOf(extension) !== -1) {
  428. validExtensions.push(extension);
  429. }
  430. }
  431. deviceDescriptor.requiredFeatures = validExtensions;
  432. }
  433. if (this._options.setMaximumLimits && !deviceDescriptor.requiredLimits) {
  434. deviceDescriptor.requiredLimits = {};
  435. for (const name in this._adapterSupportedLimits) {
  436. if (name === "minSubgroupSize" || name === "maxSubgroupSize") {
  437. // Chrome exposes these limits in "webgpu developer" mode, but these can't be set on the device.
  438. continue;
  439. }
  440. deviceDescriptor.requiredLimits[name] = this._adapterSupportedLimits[name];
  441. }
  442. }
  443. deviceDescriptor.label = `BabylonWebGPUDevice${this.uniqueId}`;
  444. return this._adapter.requestDevice(deviceDescriptor);
  445. }
  446. })
  447. .then((device) => {
  448. this._device = device;
  449. this._deviceEnabledExtensions = [];
  450. this._device.features?.forEach((feature) => this._deviceEnabledExtensions.push(feature));
  451. this._deviceLimits = device.limits;
  452. let numUncapturedErrors = -1;
  453. this._device.addEventListener("uncapturederror", (event) => {
  454. if (++numUncapturedErrors < this.numMaxUncapturedErrors) {
  455. Logger.Warn(`WebGPU uncaptured error (${numUncapturedErrors + 1}): ${event.error} - ${event.error.message}`);
  456. }
  457. else if (numUncapturedErrors++ === this.numMaxUncapturedErrors) {
  458. Logger.Warn(`WebGPU uncaptured error: too many warnings (${this.numMaxUncapturedErrors}), no more warnings will be reported to the console for this engine.`);
  459. }
  460. });
  461. if (!this._doNotHandleContextLost) {
  462. this._device.lost?.then((info) => {
  463. if (this._isDisposed) {
  464. return;
  465. }
  466. this._contextWasLost = true;
  467. Logger.Warn("WebGPU context lost. " + info);
  468. this.onContextLostObservable.notifyObservers(this);
  469. this._restoreEngineAfterContextLost(async () => {
  470. const snapshotRenderingMode = this.snapshotRenderingMode;
  471. const snapshotRendering = this.snapshotRendering;
  472. const disableCacheSamplers = this.disableCacheSamplers;
  473. const disableCacheRenderPipelines = this.disableCacheRenderPipelines;
  474. const disableCacheBindGroups = this.disableCacheBindGroups;
  475. const enableGPUTimingMeasurements = this.enableGPUTimingMeasurements;
  476. await this.initAsync(this._glslangOptions ?? this._options?.glslangOptions, this._twgslOptions ?? this._options?.twgslOptions);
  477. this.snapshotRenderingMode = snapshotRenderingMode;
  478. this.snapshotRendering = snapshotRendering;
  479. this.disableCacheSamplers = disableCacheSamplers;
  480. this.disableCacheRenderPipelines = disableCacheRenderPipelines;
  481. this.disableCacheBindGroups = disableCacheBindGroups;
  482. this.enableGPUTimingMeasurements = enableGPUTimingMeasurements;
  483. this._currentRenderPass = null;
  484. });
  485. });
  486. }
  487. })
  488. .then(() => {
  489. this._bufferManager = new WebGPUBufferManager(this, this._device);
  490. this._textureHelper = new WebGPUTextureManager(this, this._device, this._glslang, this._tintWASM, this._bufferManager, this._deviceEnabledExtensions);
  491. this._cacheSampler = new WebGPUCacheSampler(this._device);
  492. this._cacheBindGroups = new WebGPUCacheBindGroups(this._device, this._cacheSampler, this);
  493. this._timestampQuery = new WebGPUTimestampQuery(this, this._device, this._bufferManager);
  494. this._occlusionQuery = this._device.createQuerySet ? new WebGPUOcclusionQuery(this, this._device, this._bufferManager) : undefined;
  495. this._bundleList = new WebGPUBundleList(this._device);
  496. this._snapshotRendering = new WebGPUSnapshotRendering(this, this._snapshotRenderingMode, this._bundleList);
  497. this._ubInvertY = this._bufferManager.createBuffer(new Float32Array([-1, 0]), WebGPUConstants.BufferUsage.Uniform | WebGPUConstants.BufferUsage.CopyDst, "UBInvertY");
  498. this._ubDontInvertY = this._bufferManager.createBuffer(new Float32Array([1, 0]), WebGPUConstants.BufferUsage.Uniform | WebGPUConstants.BufferUsage.CopyDst, "UBDontInvertY");
  499. if (this.dbgVerboseLogsForFirstFrames) {
  500. if (this._count === undefined) {
  501. this._count = 0;
  502. Logger.Log(["%c frame #" + this._count + " - begin", "background: #ffff00"]);
  503. }
  504. }
  505. this._uploadEncoder = this._device.createCommandEncoder(this._uploadEncoderDescriptor);
  506. this._renderEncoder = this._device.createCommandEncoder(this._renderEncoderDescriptor);
  507. this._initializeLimits();
  508. this._emptyVertexBuffer = new VertexBuffer(this, [0], "", {
  509. stride: 1,
  510. offset: 0,
  511. size: 1,
  512. label: "EmptyVertexBuffer",
  513. });
  514. this._cacheRenderPipeline = new WebGPUCacheRenderPipelineTree(this._device, this._emptyVertexBuffer);
  515. this._depthCullingState = new WebGPUDepthCullingState(this._cacheRenderPipeline);
  516. this._stencilStateComposer = new WebGPUStencilStateComposer(this._cacheRenderPipeline);
  517. this._stencilStateComposer.stencilGlobal = this._stencilState;
  518. this._depthCullingState.depthTest = true;
  519. this._depthCullingState.depthFunc = 515;
  520. this._depthCullingState.depthMask = true;
  521. this._textureHelper.setCommandEncoder(this._uploadEncoder);
  522. this._clearQuad = new WebGPUClearQuad(this._device, this, this._emptyVertexBuffer);
  523. this._defaultDrawContext = this.createDrawContext();
  524. this._currentDrawContext = this._defaultDrawContext;
  525. this._defaultMaterialContext = this.createMaterialContext();
  526. this._currentMaterialContext = this._defaultMaterialContext;
  527. this._initializeContextAndSwapChain();
  528. this._initializeMainAttachments();
  529. this.resize();
  530. })
  531. .catch((e) => {
  532. Logger.Error("A fatal error occurred during WebGPU creation/initialization.");
  533. throw e;
  534. });
  535. }
  536. _initGlslang(glslangOptions) {
  537. glslangOptions = glslangOptions || {};
  538. glslangOptions = {
  539. ...WebGPUEngine._GLSLslangDefaultOptions,
  540. ...glslangOptions,
  541. };
  542. if (glslangOptions.glslang) {
  543. return Promise.resolve(glslangOptions.glslang);
  544. }
  545. if (self.glslang) {
  546. return self.glslang(glslangOptions.wasmPath);
  547. }
  548. if (glslangOptions.jsPath && glslangOptions.wasmPath) {
  549. return Tools.LoadBabylonScriptAsync(glslangOptions.jsPath).then(() => {
  550. return self.glslang(Tools.GetBabylonScriptURL(glslangOptions.wasmPath));
  551. });
  552. }
  553. return Promise.reject("gslang is not available.");
  554. }
  555. _initializeLimits() {
  556. // Init caps
  557. // TODO WEBGPU Real Capability check once limits will be working.
  558. this._caps = {
  559. maxTexturesImageUnits: this._deviceLimits.maxSampledTexturesPerShaderStage,
  560. maxVertexTextureImageUnits: this._deviceLimits.maxSampledTexturesPerShaderStage,
  561. maxCombinedTexturesImageUnits: this._deviceLimits.maxSampledTexturesPerShaderStage * 2,
  562. maxTextureSize: this._deviceLimits.maxTextureDimension2D,
  563. maxCubemapTextureSize: this._deviceLimits.maxTextureDimension2D,
  564. maxRenderTextureSize: this._deviceLimits.maxTextureDimension2D,
  565. maxVertexAttribs: this._deviceLimits.maxVertexAttributes,
  566. maxVaryingVectors: this._deviceLimits.maxInterStageShaderVariables,
  567. maxFragmentUniformVectors: Math.floor(this._deviceLimits.maxUniformBufferBindingSize / 4),
  568. maxVertexUniformVectors: Math.floor(this._deviceLimits.maxUniformBufferBindingSize / 4),
  569. standardDerivatives: true,
  570. astc: (this._deviceEnabledExtensions.indexOf(WebGPUConstants.FeatureName.TextureCompressionASTC) >= 0 ? true : undefined),
  571. s3tc: (this._deviceEnabledExtensions.indexOf(WebGPUConstants.FeatureName.TextureCompressionBC) >= 0 ? true : undefined),
  572. pvrtc: null,
  573. etc1: null,
  574. etc2: (this._deviceEnabledExtensions.indexOf(WebGPUConstants.FeatureName.TextureCompressionETC2) >= 0 ? true : undefined),
  575. bptc: this._deviceEnabledExtensions.indexOf(WebGPUConstants.FeatureName.TextureCompressionBC) >= 0 ? true : undefined,
  576. maxAnisotropy: 16,
  577. uintIndices: true,
  578. fragmentDepthSupported: true,
  579. highPrecisionShaderSupported: true,
  580. colorBufferFloat: true,
  581. supportFloatTexturesResolve: false,
  582. rg11b10ufColorRenderable: this._deviceEnabledExtensions.indexOf(WebGPUConstants.FeatureName.RG11B10UFloatRenderable) >= 0,
  583. textureFloat: true,
  584. textureFloatLinearFiltering: this._deviceEnabledExtensions.indexOf(WebGPUConstants.FeatureName.Float32Filterable) >= 0,
  585. textureFloatRender: true,
  586. textureHalfFloat: true,
  587. textureHalfFloatLinearFiltering: true,
  588. textureHalfFloatRender: true,
  589. textureLOD: true,
  590. texelFetch: true,
  591. drawBuffersExtension: true,
  592. depthTextureExtension: true,
  593. vertexArrayObject: false,
  594. instancedArrays: true,
  595. timerQuery: typeof BigUint64Array !== "undefined" && this._deviceEnabledExtensions.indexOf(WebGPUConstants.FeatureName.TimestampQuery) !== -1 ? true : undefined,
  596. supportOcclusionQuery: typeof BigUint64Array !== "undefined",
  597. canUseTimestampForTimerQuery: true,
  598. multiview: false,
  599. oculusMultiview: false,
  600. parallelShaderCompile: undefined,
  601. blendMinMax: true,
  602. maxMSAASamples: 4,
  603. canUseGLInstanceID: true,
  604. canUseGLVertexID: true,
  605. supportComputeShaders: true,
  606. supportSRGBBuffers: true,
  607. supportTransformFeedbacks: false,
  608. textureMaxLevel: true,
  609. texture2DArrayMaxLayerCount: this._deviceLimits.maxTextureArrayLayers,
  610. disableMorphTargetTexture: false,
  611. };
  612. this._features = {
  613. forceBitmapOverHTMLImageElement: true,
  614. supportRenderAndCopyToLodForFloatTextures: true,
  615. supportDepthStencilTexture: true,
  616. supportShadowSamplers: true,
  617. uniformBufferHardCheckMatrix: false,
  618. allowTexturePrefiltering: true,
  619. trackUbosInFrame: true,
  620. checkUbosContentBeforeUpload: true,
  621. supportCSM: true,
  622. basisNeedsPOT: false,
  623. support3DTextures: true,
  624. needTypeSuffixInShaderConstants: true,
  625. supportMSAA: true,
  626. supportSSAO2: true,
  627. supportExtendedTextureFormats: true,
  628. supportSwitchCaseInShader: true,
  629. supportSyncTextureRead: false,
  630. needsInvertingBitmap: false,
  631. useUBOBindingCache: false,
  632. needShaderCodeInlining: true,
  633. needToAlwaysBindUniformBuffers: true,
  634. supportRenderPasses: true,
  635. supportSpriteInstancing: true,
  636. forceVertexBufferStrideAndOffsetMultiple4Bytes: true,
  637. _collectUbosUpdatedInFrame: false,
  638. };
  639. }
  640. _initializeContextAndSwapChain() {
  641. if (!this._renderingCanvas) {
  642. // eslint-disable-next-line no-throw-literal
  643. throw "The rendering canvas has not been set!";
  644. }
  645. this._context = this._renderingCanvas.getContext("webgpu");
  646. this._configureContext();
  647. this._colorFormat = this._options.swapChainFormat;
  648. this._mainRenderPassWrapper.colorAttachmentGPUTextures = [new WebGPUHardwareTexture()];
  649. this._mainRenderPassWrapper.colorAttachmentGPUTextures[0].format = this._colorFormat;
  650. this._setColorFormat(this._mainRenderPassWrapper);
  651. }
  652. // Set default values as WebGL with depth and stencil attachment for the broadest Compat.
  653. _initializeMainAttachments() {
  654. if (!this._bufferManager) {
  655. return;
  656. }
  657. this.flushFramebuffer();
  658. this._mainTextureExtends = {
  659. width: this.getRenderWidth(true),
  660. height: this.getRenderHeight(true),
  661. depthOrArrayLayers: 1,
  662. };
  663. const bufferDataUpdate = new Float32Array([this.getRenderHeight(true)]);
  664. this._bufferManager.setSubData(this._ubInvertY, 4, bufferDataUpdate);
  665. this._bufferManager.setSubData(this._ubDontInvertY, 4, bufferDataUpdate);
  666. let mainColorAttachments;
  667. if (this._options.antialias) {
  668. const mainTextureDescriptor = {
  669. label: `Texture_MainColor_${this._mainTextureExtends.width}x${this._mainTextureExtends.height}_antialiasing`,
  670. size: this._mainTextureExtends,
  671. mipLevelCount: 1,
  672. sampleCount: this._mainPassSampleCount,
  673. dimension: WebGPUConstants.TextureDimension.E2d,
  674. format: this._options.swapChainFormat,
  675. usage: WebGPUConstants.TextureUsage.RenderAttachment,
  676. };
  677. if (this._mainTexture) {
  678. this._textureHelper.releaseTexture(this._mainTexture);
  679. }
  680. this._mainTexture = this._device.createTexture(mainTextureDescriptor);
  681. mainColorAttachments = [
  682. {
  683. view: this._mainTexture.createView({
  684. label: "TextureView_MainColor_antialiasing",
  685. dimension: WebGPUConstants.TextureDimension.E2d,
  686. format: this._options.swapChainFormat,
  687. mipLevelCount: 1,
  688. arrayLayerCount: 1,
  689. }),
  690. clearValue: new Color4(0, 0, 0, 1),
  691. loadOp: WebGPUConstants.LoadOp.Clear,
  692. storeOp: WebGPUConstants.StoreOp.Store, // don't use StoreOp.Discard, else using several cameras with different viewports or using scissors will fail because we call beginRenderPass / endPass several times for the same color attachment!
  693. },
  694. ];
  695. }
  696. else {
  697. mainColorAttachments = [
  698. {
  699. view: undefined,
  700. clearValue: new Color4(0, 0, 0, 1),
  701. loadOp: WebGPUConstants.LoadOp.Clear,
  702. storeOp: WebGPUConstants.StoreOp.Store,
  703. },
  704. ];
  705. }
  706. this._mainRenderPassWrapper.depthTextureFormat = this.isStencilEnable ? WebGPUConstants.TextureFormat.Depth24PlusStencil8 : WebGPUConstants.TextureFormat.Depth32Float;
  707. this._setDepthTextureFormat(this._mainRenderPassWrapper);
  708. this._setColorFormat(this._mainRenderPassWrapper);
  709. const depthTextureDescriptor = {
  710. label: `Texture_MainDepthStencil_${this._mainTextureExtends.width}x${this._mainTextureExtends.height}`,
  711. size: this._mainTextureExtends,
  712. mipLevelCount: 1,
  713. sampleCount: this._mainPassSampleCount,
  714. dimension: WebGPUConstants.TextureDimension.E2d,
  715. format: this._mainRenderPassWrapper.depthTextureFormat,
  716. usage: WebGPUConstants.TextureUsage.RenderAttachment,
  717. };
  718. if (this._depthTexture) {
  719. this._textureHelper.releaseTexture(this._depthTexture);
  720. }
  721. this._depthTexture = this._device.createTexture(depthTextureDescriptor);
  722. const mainDepthAttachment = {
  723. view: this._depthTexture.createView({
  724. label: `TextureView_MainDepthStencil_${this._mainTextureExtends.width}x${this._mainTextureExtends.height}`,
  725. dimension: WebGPUConstants.TextureDimension.E2d,
  726. format: this._depthTexture.format,
  727. mipLevelCount: 1,
  728. arrayLayerCount: 1,
  729. }),
  730. depthClearValue: this._clearDepthValue,
  731. depthLoadOp: WebGPUConstants.LoadOp.Clear,
  732. depthStoreOp: WebGPUConstants.StoreOp.Store,
  733. stencilClearValue: this._clearStencilValue,
  734. stencilLoadOp: !this.isStencilEnable ? undefined : WebGPUConstants.LoadOp.Clear,
  735. stencilStoreOp: !this.isStencilEnable ? undefined : WebGPUConstants.StoreOp.Store,
  736. };
  737. this._mainRenderPassWrapper.renderPassDescriptor = {
  738. label: "MainRenderPass",
  739. colorAttachments: mainColorAttachments,
  740. depthStencilAttachment: mainDepthAttachment,
  741. };
  742. }
  743. /**
  744. * Shared initialization across engines types.
  745. * @param canvas The canvas associated with this instance of the engine.
  746. */
  747. _sharedInit(canvas) {
  748. super._sharedInit(canvas);
  749. _CommonInit(this, canvas, this._creationOptions);
  750. }
  751. _configureContext() {
  752. this._context.configure({
  753. device: this._device,
  754. format: this._options.swapChainFormat,
  755. usage: WebGPUConstants.TextureUsage.RenderAttachment | WebGPUConstants.TextureUsage.CopySrc,
  756. alphaMode: this.premultipliedAlpha ? WebGPUConstants.CanvasAlphaMode.Premultiplied : WebGPUConstants.CanvasAlphaMode.Opaque,
  757. });
  758. }
  759. /**
  760. * Resize an image and returns the image data as an uint8array
  761. * @param image image to resize
  762. * @param bufferWidth destination buffer width
  763. * @param bufferHeight destination buffer height
  764. * @returns an uint8array containing RGBA values of bufferWidth * bufferHeight size
  765. */
  766. resizeImageBitmap(image, bufferWidth, bufferHeight) {
  767. return ResizeImageBitmap(this, image, bufferWidth, bufferHeight);
  768. }
  769. /**
  770. * Engine abstraction for loading and creating an image bitmap from a given source string.
  771. * @param imageSource source to load the image from.
  772. * @param options An object that sets options for the image's extraction.
  773. * @returns ImageBitmap
  774. */
  775. _createImageBitmapFromSource(imageSource, options) {
  776. return CreateImageBitmapFromSource(this, imageSource, options);
  777. }
  778. /**
  779. * Toggle full screen mode
  780. * @param requestPointerLock defines if a pointer lock should be requested from the user
  781. */
  782. switchFullscreen(requestPointerLock) {
  783. if (this.isFullscreen) {
  784. this.exitFullscreen();
  785. }
  786. else {
  787. this.enterFullscreen(requestPointerLock);
  788. }
  789. }
  790. /**
  791. * Enters full screen mode
  792. * @param requestPointerLock defines if a pointer lock should be requested from the user
  793. */
  794. enterFullscreen(requestPointerLock) {
  795. if (!this.isFullscreen) {
  796. this._pointerLockRequested = requestPointerLock;
  797. if (this._renderingCanvas) {
  798. RequestFullscreen(this._renderingCanvas);
  799. }
  800. }
  801. }
  802. /**
  803. * Exits full screen mode
  804. */
  805. exitFullscreen() {
  806. if (this.isFullscreen) {
  807. ExitFullscreen();
  808. }
  809. }
  810. /**
  811. * Enters Pointerlock mode
  812. */
  813. enterPointerlock() {
  814. if (this._renderingCanvas) {
  815. RequestPointerlock(this._renderingCanvas);
  816. }
  817. }
  818. /**
  819. * Exits Pointerlock mode
  820. */
  821. exitPointerlock() {
  822. ExitPointerlock();
  823. }
  824. _rebuildBuffers() {
  825. super._rebuildBuffers();
  826. for (const storageBuffer of this._storageBuffers) {
  827. // The buffer can already be rebuilt by the call to _rebuildGeometries(), which recreates the storage buffers for the ComputeShaderParticleSystem
  828. if (storageBuffer.getBuffer().engineId !== this.uniqueId) {
  829. storageBuffer._rebuild();
  830. }
  831. }
  832. }
  833. _restoreEngineAfterContextLost(initEngine) {
  834. WebGPUCacheRenderPipelineTree.ResetCache();
  835. WebGPUCacheBindGroups.ResetCache();
  836. // Clear the draw wrappers and material contexts
  837. const cleanScenes = (scenes) => {
  838. for (const scene of scenes) {
  839. for (const mesh of scene.meshes) {
  840. const subMeshes = mesh.subMeshes;
  841. if (!subMeshes) {
  842. continue;
  843. }
  844. for (const subMesh of subMeshes) {
  845. subMesh._drawWrappers = [];
  846. }
  847. }
  848. for (const material of scene.materials) {
  849. material._materialContext?.reset();
  850. }
  851. }
  852. };
  853. cleanScenes(this.scenes);
  854. cleanScenes(this._virtualScenes);
  855. // The leftOver uniform buffers are removed from the list because they will be recreated when we rebuild the effects
  856. const uboList = [];
  857. for (const uniformBuffer of this._uniformBuffers) {
  858. if (uniformBuffer.name.indexOf("leftOver") < 0) {
  859. uboList.push(uniformBuffer);
  860. }
  861. }
  862. this._uniformBuffers = uboList;
  863. super._restoreEngineAfterContextLost(initEngine);
  864. }
  865. /**
  866. * Sets a depth stencil texture from a render target to the according uniform.
  867. * @param channel The texture channel
  868. * @param uniform The uniform to set
  869. * @param texture The render target texture containing the depth stencil texture to apply
  870. * @param name The texture name
  871. */
  872. setDepthStencilTexture(channel, uniform, texture, name) {
  873. if (channel === undefined) {
  874. return;
  875. }
  876. if (!texture || !texture.depthStencilTexture) {
  877. this._setTexture(channel, null, undefined, undefined, name);
  878. }
  879. else {
  880. this._setTexture(channel, texture, false, true, name);
  881. }
  882. }
  883. /**
  884. * Sets a texture to the context from a postprocess
  885. * @param channel defines the channel to use
  886. * @param postProcess defines the source postprocess
  887. * @param name name of the channel
  888. */
  889. setTextureFromPostProcess(channel, postProcess, name) {
  890. let postProcessInput = null;
  891. if (postProcess) {
  892. if (postProcess._forcedOutputTexture) {
  893. postProcessInput = postProcess._forcedOutputTexture;
  894. }
  895. else if (postProcess._textures.data[postProcess._currentRenderTextureInd]) {
  896. postProcessInput = postProcess._textures.data[postProcess._currentRenderTextureInd];
  897. }
  898. }
  899. this._bindTexture(channel, postProcessInput?.texture ?? null, name);
  900. }
  901. /**
  902. * Binds the output of the passed in post process to the texture channel specified
  903. * @param channel The channel the texture should be bound to
  904. * @param postProcess The post process which's output should be bound
  905. * @param name name of the channel
  906. */
  907. setTextureFromPostProcessOutput(channel, postProcess, name) {
  908. this._bindTexture(channel, postProcess?._outputTexture?.texture ?? null, name);
  909. }
  910. /**
  911. * Force a specific size of the canvas
  912. * @param width defines the new canvas' width
  913. * @param height defines the new canvas' height
  914. * @param forceSetSize true to force setting the sizes of the underlying canvas
  915. * @returns true if the size was changed
  916. */
  917. setSize(width, height, forceSetSize = false) {
  918. if (!super.setSize(width, height, forceSetSize)) {
  919. return false;
  920. }
  921. if (this.dbgVerboseLogsForFirstFrames) {
  922. if (this._count === undefined) {
  923. this._count = 0;
  924. }
  925. if (!this._count || this._count < this.dbgVerboseLogsNumFrames) {
  926. Logger.Log(["frame #" + this._count + " - setSize -", width, height]);
  927. }
  928. }
  929. this._initializeMainAttachments();
  930. if (this.snapshotRendering) {
  931. // reset snapshot rendering so that the next frame will record a new list of bundles
  932. this.snapshotRenderingReset();
  933. }
  934. return true;
  935. }
  936. /**
  937. * @internal
  938. */
  939. _getShaderProcessor(shaderLanguage) {
  940. if (shaderLanguage === ShaderLanguage.WGSL) {
  941. return this._shaderProcessorWGSL;
  942. }
  943. return this._shaderProcessor;
  944. }
  945. /**
  946. * @internal
  947. */
  948. _getShaderProcessingContext(shaderLanguage) {
  949. return new WebGPUShaderProcessingContext(shaderLanguage);
  950. }
  951. _currentPassIsMainPass() {
  952. return this._currentRenderTarget === null;
  953. }
  954. _getCurrentRenderPass() {
  955. if (this._currentRenderTarget && !this._currentRenderPass) {
  956. // delayed creation of the render target pass, but we now need to create it as we are requested the render pass
  957. this._startRenderTargetRenderPass(this._currentRenderTarget, false, null, false, false);
  958. }
  959. else if (!this._currentRenderPass) {
  960. this._startMainRenderPass(false);
  961. }
  962. return this._currentRenderPass;
  963. }
  964. /** @internal */
  965. _getCurrentRenderPassWrapper() {
  966. return this._currentRenderTarget ? this._rttRenderPassWrapper : this._mainRenderPassWrapper;
  967. }
  968. //------------------------------------------------------------------------------
  969. // Static Pipeline WebGPU States
  970. //------------------------------------------------------------------------------
  971. /** @internal */
  972. applyStates() {
  973. this._stencilStateComposer.apply();
  974. this._cacheRenderPipeline.setAlphaBlendEnabled(this._alphaState.alphaBlend);
  975. }
  976. /**
  977. * Force the entire cache to be cleared
  978. * You should not have to use this function unless your engine needs to share the WebGPU context with another engine
  979. * @param bruteForce defines a boolean to force clearing ALL caches (including stencil, detoh and alpha states)
  980. */
  981. wipeCaches(bruteForce) {
  982. if (this.preventCacheWipeBetweenFrames && !bruteForce) {
  983. return;
  984. }
  985. //this._currentEffect = null; // can't reset _currentEffect, else some crashes can occur (for eg in ProceduralTexture which calls bindFrameBuffer (which calls wipeCaches) after having called enableEffect and before drawing into the texture)
  986. // _forceEnableEffect = true assumes the role of _currentEffect = null
  987. this._forceEnableEffect = true;
  988. this._currentIndexBuffer = null;
  989. this._currentOverrideVertexBuffers = null;
  990. this._cacheRenderPipeline.setBuffers(null, null, null);
  991. if (bruteForce) {
  992. this._stencilStateComposer.reset();
  993. this._depthCullingState.reset();
  994. this._depthCullingState.depthFunc = 515;
  995. this._alphaState.reset();
  996. this._alphaMode = 1;
  997. this._alphaEquation = 0;
  998. this._cacheRenderPipeline.setAlphaBlendFactors(this._alphaState._blendFunctionParameters, this._alphaState._blendEquationParameters);
  999. this._cacheRenderPipeline.setAlphaBlendEnabled(false);
  1000. this.setColorWrite(true);
  1001. }
  1002. this._cachedVertexBuffers = null;
  1003. this._cachedIndexBuffer = null;
  1004. this._cachedEffectForVertexBuffers = null;
  1005. }
  1006. /**
  1007. * Enable or disable color writing
  1008. * @param enable defines the state to set
  1009. */
  1010. setColorWrite(enable) {
  1011. this._colorWriteLocal = enable;
  1012. this._cacheRenderPipeline.setWriteMask(enable ? 0xf : 0);
  1013. }
  1014. /**
  1015. * Gets a boolean indicating if color writing is enabled
  1016. * @returns the current color writing state
  1017. */
  1018. getColorWrite() {
  1019. return this._colorWriteLocal;
  1020. }
  1021. _mustUpdateViewport() {
  1022. const x = this._viewportCached.x, y = this._viewportCached.y, w = this._viewportCached.z, h = this._viewportCached.w;
  1023. const update = this._viewportsCurrent.x !== x || this._viewportsCurrent.y !== y || this._viewportsCurrent.w !== w || this._viewportsCurrent.h !== h;
  1024. if (update) {
  1025. this._viewportsCurrent.x = this._viewportCached.x;
  1026. this._viewportsCurrent.y = this._viewportCached.y;
  1027. this._viewportsCurrent.w = this._viewportCached.z;
  1028. this._viewportsCurrent.h = this._viewportCached.w;
  1029. }
  1030. return update;
  1031. }
  1032. _applyViewport(bundleList) {
  1033. const x = Math.floor(this._viewportCached.x);
  1034. const w = Math.floor(this._viewportCached.z);
  1035. const h = Math.floor(this._viewportCached.w);
  1036. let y = Math.floor(this._viewportCached.y);
  1037. if (!this._currentRenderTarget) {
  1038. y = this.getRenderHeight(true) - y - h;
  1039. }
  1040. if (bundleList) {
  1041. bundleList.addItem(new WebGPURenderItemViewport(x, y, w, h));
  1042. }
  1043. else {
  1044. this._getCurrentRenderPass().setViewport(x, y, w, h, 0, 1);
  1045. }
  1046. if (this.dbgVerboseLogsForFirstFrames) {
  1047. if (this._count === undefined) {
  1048. this._count = 0;
  1049. }
  1050. if (!this._count || this._count < this.dbgVerboseLogsNumFrames) {
  1051. Logger.Log([
  1052. "frame #" + this._count + " - viewport applied - (",
  1053. this._viewportCached.x,
  1054. this._viewportCached.y,
  1055. this._viewportCached.z,
  1056. this._viewportCached.w,
  1057. ") current pass is main pass=" + this._currentPassIsMainPass(),
  1058. ]);
  1059. }
  1060. }
  1061. }
  1062. /**
  1063. * @internal
  1064. */
  1065. _viewport(x, y, width, height) {
  1066. this._viewportCached.x = x;
  1067. this._viewportCached.y = y;
  1068. this._viewportCached.z = width;
  1069. this._viewportCached.w = height;
  1070. }
  1071. _mustUpdateScissor() {
  1072. const x = this._scissorCached.x, y = this._scissorCached.y, w = this._scissorCached.z, h = this._scissorCached.w;
  1073. const update = this._scissorsCurrent.x !== x || this._scissorsCurrent.y !== y || this._scissorsCurrent.w !== w || this._scissorsCurrent.h !== h;
  1074. if (update) {
  1075. this._scissorsCurrent.x = this._scissorCached.x;
  1076. this._scissorsCurrent.y = this._scissorCached.y;
  1077. this._scissorsCurrent.w = this._scissorCached.z;
  1078. this._scissorsCurrent.h = this._scissorCached.w;
  1079. }
  1080. return update;
  1081. }
  1082. _applyScissor(bundleList) {
  1083. const y = this._currentRenderTarget ? this._scissorCached.y : this.getRenderHeight() - this._scissorCached.w - this._scissorCached.y;
  1084. if (bundleList) {
  1085. bundleList.addItem(new WebGPURenderItemScissor(this._scissorCached.x, y, this._scissorCached.z, this._scissorCached.w));
  1086. }
  1087. else {
  1088. this._getCurrentRenderPass().setScissorRect(this._scissorCached.x, y, this._scissorCached.z, this._scissorCached.w);
  1089. }
  1090. if (this.dbgVerboseLogsForFirstFrames) {
  1091. if (this._count === undefined) {
  1092. this._count = 0;
  1093. }
  1094. if (!this._count || this._count < this.dbgVerboseLogsNumFrames) {
  1095. Logger.Log([
  1096. "frame #" + this._count + " - scissor applied - (",
  1097. this._scissorCached.x,
  1098. this._scissorCached.y,
  1099. this._scissorCached.z,
  1100. this._scissorCached.w,
  1101. ") current pass is main pass=" + this._currentPassIsMainPass(),
  1102. ]);
  1103. }
  1104. }
  1105. }
  1106. _scissorIsActive() {
  1107. return this._scissorCached.x !== 0 || this._scissorCached.y !== 0 || this._scissorCached.z !== 0 || this._scissorCached.w !== 0;
  1108. }
  1109. enableScissor(x, y, width, height) {
  1110. this._scissorCached.x = x;
  1111. this._scissorCached.y = y;
  1112. this._scissorCached.z = width;
  1113. this._scissorCached.w = height;
  1114. }
  1115. disableScissor() {
  1116. this._scissorCached.x = this._scissorCached.y = this._scissorCached.z = this._scissorCached.w = 0;
  1117. this._scissorsCurrent.x = this._scissorsCurrent.y = this._scissorsCurrent.w = this._scissorsCurrent.h = 0;
  1118. }
  1119. _mustUpdateStencilRef() {
  1120. const update = this._stencilStateComposer.funcRef !== this._stencilRefsCurrent;
  1121. if (update) {
  1122. this._stencilRefsCurrent = this._stencilStateComposer.funcRef;
  1123. }
  1124. return update;
  1125. }
  1126. _applyStencilRef(bundleList) {
  1127. if (bundleList) {
  1128. bundleList.addItem(new WebGPURenderItemStencilRef(this._stencilStateComposer.funcRef ?? 0));
  1129. }
  1130. else {
  1131. this._getCurrentRenderPass().setStencilReference(this._stencilStateComposer.funcRef ?? 0);
  1132. }
  1133. }
  1134. _mustUpdateBlendColor() {
  1135. const colorBlend = this._alphaState._blendConstants;
  1136. const update = colorBlend[0] !== this._blendColorsCurrent[0] ||
  1137. colorBlend[1] !== this._blendColorsCurrent[1] ||
  1138. colorBlend[2] !== this._blendColorsCurrent[2] ||
  1139. colorBlend[3] !== this._blendColorsCurrent[3];
  1140. if (update) {
  1141. this._blendColorsCurrent[0] = colorBlend[0];
  1142. this._blendColorsCurrent[1] = colorBlend[1];
  1143. this._blendColorsCurrent[2] = colorBlend[2];
  1144. this._blendColorsCurrent[3] = colorBlend[3];
  1145. }
  1146. return update;
  1147. }
  1148. _applyBlendColor(bundleList) {
  1149. if (bundleList) {
  1150. bundleList.addItem(new WebGPURenderItemBlendColor(this._alphaState._blendConstants.slice()));
  1151. }
  1152. else {
  1153. this._getCurrentRenderPass().setBlendConstant(this._alphaState._blendConstants);
  1154. }
  1155. }
  1156. _resetRenderPassStates() {
  1157. this._viewportsCurrent.x = this._viewportsCurrent.y = this._viewportsCurrent.w = this._viewportsCurrent.h = 0;
  1158. this._scissorsCurrent.x = this._scissorsCurrent.y = this._scissorsCurrent.w = this._scissorsCurrent.h = 0;
  1159. this._stencilRefsCurrent = -1;
  1160. this._blendColorsCurrent[0] = this._blendColorsCurrent[1] = this._blendColorsCurrent[2] = this._blendColorsCurrent[3] = null;
  1161. }
  1162. /**
  1163. * Clear the current render buffer or the current render target (if any is set up)
  1164. * @param color defines the color to use
  1165. * @param backBuffer defines if the back buffer must be cleared
  1166. * @param depth defines if the depth buffer must be cleared
  1167. * @param stencil defines if the stencil buffer must be cleared
  1168. */
  1169. clear(color, backBuffer, depth, stencil = false) {
  1170. // Some PGs are using color3...
  1171. if (color && color.a === undefined) {
  1172. color.a = 1;
  1173. }
  1174. const hasScissor = this._scissorIsActive();
  1175. if (this.dbgVerboseLogsForFirstFrames) {
  1176. if (this._count === undefined) {
  1177. this._count = 0;
  1178. }
  1179. if (!this._count || this._count < this.dbgVerboseLogsNumFrames) {
  1180. Logger.Log(["frame #" + this._count + " - clear - backBuffer=", backBuffer, " depth=", depth, " stencil=", stencil, " scissor is active=", hasScissor]);
  1181. }
  1182. }
  1183. // We need to recreate the render pass so that the new parameters for clear color / depth / stencil are taken into account
  1184. if (this._currentRenderTarget) {
  1185. if (hasScissor) {
  1186. if (!this._currentRenderPass) {
  1187. this._startRenderTargetRenderPass(this._currentRenderTarget, false, backBuffer ? color : null, depth, stencil);
  1188. }
  1189. this._applyScissor(!this.compatibilityMode ? this._bundleList : null);
  1190. this._clearFullQuad(backBuffer ? color : null, depth, stencil);
  1191. }
  1192. else {
  1193. if (this._currentRenderPass) {
  1194. this._endCurrentRenderPass();
  1195. }
  1196. this._startRenderTargetRenderPass(this._currentRenderTarget, true, backBuffer ? color : null, depth, stencil);
  1197. }
  1198. }
  1199. else {
  1200. if (!this._currentRenderPass || !hasScissor) {
  1201. this._startMainRenderPass(!hasScissor, backBuffer ? color : null, depth, stencil);
  1202. }
  1203. if (hasScissor) {
  1204. this._applyScissor(!this.compatibilityMode ? this._bundleList : null);
  1205. this._clearFullQuad(backBuffer ? color : null, depth, stencil);
  1206. }
  1207. }
  1208. }
  1209. _clearFullQuad(clearColor, clearDepth, clearStencil) {
  1210. const renderPass = !this.compatibilityMode ? null : this._getCurrentRenderPass();
  1211. this._clearQuad.setColorFormat(this._colorFormat);
  1212. this._clearQuad.setDepthStencilFormat(this._depthTextureFormat);
  1213. this._clearQuad.setMRTAttachments(this._cacheRenderPipeline.mrtAttachments ?? [], this._cacheRenderPipeline.mrtTextureArray ?? [], this._cacheRenderPipeline.mrtTextureCount);
  1214. if (!this.compatibilityMode) {
  1215. this._bundleList.addItem(new WebGPURenderItemStencilRef(this._clearStencilValue));
  1216. }
  1217. else {
  1218. renderPass.setStencilReference(this._clearStencilValue);
  1219. }
  1220. const bundle = this._clearQuad.clear(renderPass, clearColor, clearDepth, clearStencil, this.currentSampleCount);
  1221. if (!this.compatibilityMode) {
  1222. this._bundleList.addBundle(bundle);
  1223. this._applyStencilRef(this._bundleList);
  1224. this._reportDrawCall();
  1225. }
  1226. else {
  1227. this._applyStencilRef(null);
  1228. }
  1229. }
  1230. //------------------------------------------------------------------------------
  1231. // Vertex/Index/Storage Buffers
  1232. //------------------------------------------------------------------------------
  1233. /**
  1234. * Creates a vertex buffer
  1235. * @param data the data or the size for the vertex buffer
  1236. * @param _updatable whether the buffer should be created as updatable
  1237. * @param label defines the label of the buffer (for debug purpose)
  1238. * @returns the new buffer
  1239. */
  1240. createVertexBuffer(data, _updatable, label) {
  1241. let view;
  1242. if (data instanceof Array) {
  1243. view = new Float32Array(data);
  1244. }
  1245. else if (data instanceof ArrayBuffer) {
  1246. view = new Uint8Array(data);
  1247. }
  1248. else {
  1249. view = data;
  1250. }
  1251. const dataBuffer = this._bufferManager.createBuffer(view, WebGPUConstants.BufferUsage.Vertex | WebGPUConstants.BufferUsage.CopyDst, label);
  1252. return dataBuffer;
  1253. }
  1254. /**
  1255. * Creates a vertex buffer
  1256. * @param data the data for the dynamic vertex buffer
  1257. * @param label defines the label of the buffer (for debug purpose)
  1258. * @returns the new buffer
  1259. */
  1260. createDynamicVertexBuffer(data, label) {
  1261. return this.createVertexBuffer(data, undefined, label);
  1262. }
  1263. /**
  1264. * Creates a new index buffer
  1265. * @param indices defines the content of the index buffer
  1266. * @param _updatable defines if the index buffer must be updatable
  1267. * @param label defines the label of the buffer (for debug purpose)
  1268. * @returns a new buffer
  1269. */
  1270. createIndexBuffer(indices, _updatable, label) {
  1271. let is32Bits = true;
  1272. let view;
  1273. if (indices instanceof Uint32Array || indices instanceof Int32Array) {
  1274. view = indices;
  1275. }
  1276. else if (indices instanceof Uint16Array) {
  1277. view = indices;
  1278. is32Bits = false;
  1279. }
  1280. else {
  1281. if (indices.length > 65535) {
  1282. view = new Uint32Array(indices);
  1283. }
  1284. else {
  1285. view = new Uint16Array(indices);
  1286. is32Bits = false;
  1287. }
  1288. }
  1289. const dataBuffer = this._bufferManager.createBuffer(view, WebGPUConstants.BufferUsage.Index | WebGPUConstants.BufferUsage.CopyDst, label);
  1290. dataBuffer.is32Bits = is32Bits;
  1291. return dataBuffer;
  1292. }
  1293. /**
  1294. * Update a dynamic index buffer
  1295. * @param indexBuffer defines the target index buffer
  1296. * @param indices defines the data to update
  1297. * @param offset defines the offset in the target index buffer where update should start
  1298. */
  1299. updateDynamicIndexBuffer(indexBuffer, indices, offset = 0) {
  1300. const gpuBuffer = indexBuffer;
  1301. let view;
  1302. if (indexBuffer.is32Bits) {
  1303. view = indices instanceof Uint32Array ? indices : new Uint32Array(indices);
  1304. }
  1305. else {
  1306. view = indices instanceof Uint16Array ? indices : new Uint16Array(indices);
  1307. }
  1308. this._bufferManager.setSubData(gpuBuffer, offset, view);
  1309. }
  1310. /**
  1311. * Updates a dynamic vertex buffer.
  1312. * @param vertexBuffer the vertex buffer to update
  1313. * @param data the data used to update the vertex buffer
  1314. * @param byteOffset the byte offset of the data
  1315. * @param byteLength the byte length of the data
  1316. */
  1317. updateDynamicVertexBuffer(vertexBuffer, data, byteOffset, byteLength) {
  1318. const dataBuffer = vertexBuffer;
  1319. if (byteOffset === undefined) {
  1320. byteOffset = 0;
  1321. }
  1322. let view;
  1323. if (byteLength === undefined) {
  1324. if (data instanceof Array) {
  1325. view = new Float32Array(data);
  1326. }
  1327. else if (data instanceof ArrayBuffer) {
  1328. view = new Uint8Array(data);
  1329. }
  1330. else {
  1331. view = data;
  1332. }
  1333. byteLength = view.byteLength;
  1334. }
  1335. else {
  1336. if (data instanceof Array) {
  1337. view = new Float32Array(data);
  1338. }
  1339. else if (data instanceof ArrayBuffer) {
  1340. view = new Uint8Array(data);
  1341. }
  1342. else {
  1343. view = data;
  1344. }
  1345. }
  1346. this._bufferManager.setSubData(dataBuffer, byteOffset, view, 0, byteLength);
  1347. }
  1348. /**
  1349. * @internal
  1350. */
  1351. _createBuffer(data, creationFlags, label) {
  1352. let view;
  1353. if (data instanceof Array) {
  1354. view = new Float32Array(data);
  1355. }
  1356. else if (data instanceof ArrayBuffer) {
  1357. view = new Uint8Array(data);
  1358. }
  1359. else {
  1360. view = data;
  1361. }
  1362. let flags = 0;
  1363. if (creationFlags & 1) {
  1364. flags |= WebGPUConstants.BufferUsage.CopySrc;
  1365. }
  1366. if (creationFlags & 2) {
  1367. flags |= WebGPUConstants.BufferUsage.CopyDst;
  1368. }
  1369. if (creationFlags & 4) {
  1370. flags |= WebGPUConstants.BufferUsage.Uniform;
  1371. }
  1372. if (creationFlags & 8) {
  1373. flags |= WebGPUConstants.BufferUsage.Vertex;
  1374. }
  1375. if (creationFlags & 16) {
  1376. flags |= WebGPUConstants.BufferUsage.Index;
  1377. }
  1378. if (creationFlags & 32) {
  1379. flags |= WebGPUConstants.BufferUsage.Storage;
  1380. }
  1381. return this._bufferManager.createBuffer(view, flags, label);
  1382. }
  1383. /**
  1384. * @internal
  1385. */
  1386. bindBuffersDirectly() {
  1387. // eslint-disable-next-line no-throw-literal
  1388. throw "Not implemented on WebGPU";
  1389. }
  1390. /**
  1391. * @internal
  1392. */
  1393. updateAndBindInstancesBuffer() {
  1394. // eslint-disable-next-line no-throw-literal
  1395. throw "Not implemented on WebGPU";
  1396. }
  1397. /**
  1398. * Unbind all instance attributes
  1399. */
  1400. unbindInstanceAttributes() {
  1401. // Does nothing
  1402. }
  1403. /**
  1404. * Bind a list of vertex buffers with the engine
  1405. * @param vertexBuffers defines the list of vertex buffers to bind
  1406. * @param indexBuffer defines the index buffer to bind
  1407. * @param effect defines the effect associated with the vertex buffers
  1408. * @param overrideVertexBuffers defines optional list of avertex buffers that overrides the entries in vertexBuffers
  1409. */
  1410. bindBuffers(vertexBuffers, indexBuffer, effect, overrideVertexBuffers) {
  1411. this._currentIndexBuffer = indexBuffer;
  1412. this._currentOverrideVertexBuffers = overrideVertexBuffers ?? null;
  1413. this._cacheRenderPipeline.setBuffers(vertexBuffers, indexBuffer, this._currentOverrideVertexBuffers);
  1414. }
  1415. /**
  1416. * @internal
  1417. */
  1418. _releaseBuffer(buffer) {
  1419. return this._bufferManager.releaseBuffer(buffer);
  1420. }
  1421. //------------------------------------------------------------------------------
  1422. // Uniform Buffers
  1423. //------------------------------------------------------------------------------
  1424. /**
  1425. * Create an uniform buffer
  1426. * @see https://doc.babylonjs.com/setup/support/webGL2#uniform-buffer-objets
  1427. * @param elements defines the content of the uniform buffer
  1428. * @param label defines a name for the buffer (for debugging purpose)
  1429. * @returns the webGL uniform buffer
  1430. */
  1431. createUniformBuffer(elements, label) {
  1432. let view;
  1433. if (elements instanceof Array) {
  1434. view = new Float32Array(elements);
  1435. }
  1436. else {
  1437. view = elements;
  1438. }
  1439. const dataBuffer = this._bufferManager.createBuffer(view, WebGPUConstants.BufferUsage.Uniform | WebGPUConstants.BufferUsage.CopyDst, label);
  1440. return dataBuffer;
  1441. }
  1442. /**
  1443. * Create a dynamic uniform buffer (no different from a non dynamic uniform buffer in WebGPU)
  1444. * @see https://doc.babylonjs.com/setup/support/webGL2#uniform-buffer-objets
  1445. * @param elements defines the content of the uniform buffer
  1446. * @param label defines a name for the buffer (for debugging purpose)
  1447. * @returns the webGL uniform buffer
  1448. */
  1449. createDynamicUniformBuffer(elements, label) {
  1450. return this.createUniformBuffer(elements, label);
  1451. }
  1452. /**
  1453. * Update an existing uniform buffer
  1454. * @see https://doc.babylonjs.com/setup/support/webGL2#uniform-buffer-objets
  1455. * @param uniformBuffer defines the target uniform buffer
  1456. * @param elements defines the content to update
  1457. * @param offset defines the offset in the uniform buffer where update should start
  1458. * @param count defines the size of the data to update
  1459. */
  1460. updateUniformBuffer(uniformBuffer, elements, offset, count) {
  1461. if (offset === undefined) {
  1462. offset = 0;
  1463. }
  1464. const dataBuffer = uniformBuffer;
  1465. let view;
  1466. if (count === undefined) {
  1467. if (elements instanceof Float32Array) {
  1468. view = elements;
  1469. }
  1470. else {
  1471. view = new Float32Array(elements);
  1472. }
  1473. count = view.byteLength;
  1474. }
  1475. else {
  1476. if (elements instanceof Float32Array) {
  1477. view = elements;
  1478. }
  1479. else {
  1480. view = new Float32Array(elements);
  1481. }
  1482. }
  1483. this._bufferManager.setSubData(dataBuffer, offset, view, 0, count);
  1484. }
  1485. /**
  1486. * Bind a buffer to the current draw context
  1487. * @param buffer defines the buffer to bind
  1488. * @param _location not used in WebGPU
  1489. * @param name Name of the uniform variable to bind
  1490. */
  1491. bindUniformBufferBase(buffer, _location, name) {
  1492. this._currentDrawContext.setBuffer(name, buffer);
  1493. }
  1494. /**
  1495. * Unused in WebGPU
  1496. */
  1497. bindUniformBlock() { }
  1498. //------------------------------------------------------------------------------
  1499. // Effects
  1500. //------------------------------------------------------------------------------
  1501. /**
  1502. * Create a new effect (used to store vertex/fragment shaders)
  1503. * @param baseName defines the base name of the effect (The name of file without .fragment.fx or .vertex.fx)
  1504. * @param attributesNamesOrOptions defines either a list of attribute names or an IEffectCreationOptions object
  1505. * @param uniformsNamesOrEngine defines either a list of uniform names or the engine to use
  1506. * @param samplers defines an array of string used to represent textures
  1507. * @param defines defines the string containing the defines to use to compile the shaders
  1508. * @param fallbacks defines the list of potential fallbacks to use if shader compilation fails
  1509. * @param onCompiled defines a function to call when the effect creation is successful
  1510. * @param onError defines a function to call when the effect creation has failed
  1511. * @param indexParameters defines an object containing the index values to use to compile shaders (like the maximum number of simultaneous lights)
  1512. * @param shaderLanguage the language the shader is written in (default: GLSL)
  1513. * @returns the new Effect
  1514. */
  1515. createEffect(baseName, attributesNamesOrOptions, uniformsNamesOrEngine, samplers, defines, fallbacks, onCompiled, onError, indexParameters, shaderLanguage = ShaderLanguage.GLSL) {
  1516. const vertex = typeof baseName === "string" ? baseName : baseName.vertexToken || baseName.vertexSource || baseName.vertexElement || baseName.vertex;
  1517. const fragment = typeof baseName === "string" ? baseName : baseName.fragmentToken || baseName.fragmentSource || baseName.fragmentElement || baseName.fragment;
  1518. const globalDefines = this._getGlobalDefines();
  1519. let fullDefines = defines ?? attributesNamesOrOptions.defines ?? "";
  1520. if (globalDefines) {
  1521. fullDefines += "\n" + globalDefines;
  1522. }
  1523. const name = vertex + "+" + fragment + "@" + fullDefines;
  1524. if (this._compiledEffects[name]) {
  1525. const compiledEffect = this._compiledEffects[name];
  1526. if (onCompiled && compiledEffect.isReady()) {
  1527. onCompiled(compiledEffect);
  1528. }
  1529. return compiledEffect;
  1530. }
  1531. const effect = new Effect(baseName, attributesNamesOrOptions, uniformsNamesOrEngine, samplers, this, defines, fallbacks, onCompiled, onError, indexParameters, name, shaderLanguage);
  1532. this._compiledEffects[name] = effect;
  1533. return effect;
  1534. }
  1535. _compileRawShaderToSpirV(source, type) {
  1536. return this._glslang.compileGLSL(source, type);
  1537. }
  1538. _compileShaderToSpirV(source, type, defines, shaderVersion) {
  1539. return this._compileRawShaderToSpirV(shaderVersion + (defines ? defines + "\n" : "") + source, type);
  1540. }
  1541. _getWGSLShader(source, type, defines) {
  1542. if (defines) {
  1543. defines = "//" + defines.split("\n").join("\n//") + "\n";
  1544. }
  1545. else {
  1546. defines = "";
  1547. }
  1548. return defines + source;
  1549. }
  1550. _createPipelineStageDescriptor(vertexShader, fragmentShader, shaderLanguage, disableUniformityAnalysisInVertex, disableUniformityAnalysisInFragment) {
  1551. if (this._tintWASM && shaderLanguage === ShaderLanguage.GLSL) {
  1552. vertexShader = this._tintWASM.convertSpirV2WGSL(vertexShader, disableUniformityAnalysisInVertex);
  1553. fragmentShader = this._tintWASM.convertSpirV2WGSL(fragmentShader, disableUniformityAnalysisInFragment);
  1554. }
  1555. return {
  1556. vertexStage: {
  1557. module: this._device.createShaderModule({
  1558. code: vertexShader,
  1559. }),
  1560. entryPoint: "main",
  1561. },
  1562. fragmentStage: {
  1563. module: this._device.createShaderModule({
  1564. code: fragmentShader,
  1565. }),
  1566. entryPoint: "main",
  1567. },
  1568. };
  1569. }
  1570. _compileRawPipelineStageDescriptor(vertexCode, fragmentCode, shaderLanguage) {
  1571. const disableUniformityAnalysisInVertex = vertexCode.indexOf(disableUniformityAnalysisMarker) >= 0;
  1572. const disableUniformityAnalysisInFragment = fragmentCode.indexOf(disableUniformityAnalysisMarker) >= 0;
  1573. const vertexShader = shaderLanguage === ShaderLanguage.GLSL ? this._compileRawShaderToSpirV(vertexCode, "vertex") : vertexCode;
  1574. const fragmentShader = shaderLanguage === ShaderLanguage.GLSL ? this._compileRawShaderToSpirV(fragmentCode, "fragment") : fragmentCode;
  1575. return this._createPipelineStageDescriptor(vertexShader, fragmentShader, shaderLanguage, disableUniformityAnalysisInVertex, disableUniformityAnalysisInFragment);
  1576. }
  1577. _compilePipelineStageDescriptor(vertexCode, fragmentCode, defines, shaderLanguage) {
  1578. this.onBeforeShaderCompilationObservable.notifyObservers(this);
  1579. const disableUniformityAnalysisInVertex = vertexCode.indexOf(disableUniformityAnalysisMarker) >= 0;
  1580. const disableUniformityAnalysisInFragment = fragmentCode.indexOf(disableUniformityAnalysisMarker) >= 0;
  1581. const shaderVersion = "#version 450\n";
  1582. const vertexShader = shaderLanguage === ShaderLanguage.GLSL ? this._compileShaderToSpirV(vertexCode, "vertex", defines, shaderVersion) : this._getWGSLShader(vertexCode, "vertex", defines);
  1583. const fragmentShader = shaderLanguage === ShaderLanguage.GLSL
  1584. ? this._compileShaderToSpirV(fragmentCode, "fragment", defines, shaderVersion)
  1585. : this._getWGSLShader(fragmentCode, "fragment", defines);
  1586. const program = this._createPipelineStageDescriptor(vertexShader, fragmentShader, shaderLanguage, disableUniformityAnalysisInVertex, disableUniformityAnalysisInFragment);
  1587. this.onAfterShaderCompilationObservable.notifyObservers(this);
  1588. return program;
  1589. }
  1590. /**
  1591. * @internal
  1592. */
  1593. createRawShaderProgram() {
  1594. // eslint-disable-next-line no-throw-literal
  1595. throw "Not available on WebGPU";
  1596. }
  1597. /**
  1598. * @internal
  1599. */
  1600. createShaderProgram() {
  1601. // eslint-disable-next-line no-throw-literal
  1602. throw "Not available on WebGPU";
  1603. }
  1604. /**
  1605. * Inline functions in shader code that are marked to be inlined
  1606. * @param code code to inline
  1607. * @returns inlined code
  1608. */
  1609. inlineShaderCode(code) {
  1610. const sci = new ShaderCodeInliner(code);
  1611. sci.debug = false;
  1612. sci.processCode();
  1613. return sci.code;
  1614. }
  1615. /**
  1616. * Creates a new pipeline context
  1617. * @param shaderProcessingContext defines the shader processing context used during the processing if available
  1618. * @returns the new pipeline
  1619. */
  1620. createPipelineContext(shaderProcessingContext) {
  1621. return new WebGPUPipelineContext(shaderProcessingContext, this);
  1622. }
  1623. /**
  1624. * Creates a new material context
  1625. * @returns the new context
  1626. */
  1627. createMaterialContext() {
  1628. return new WebGPUMaterialContext();
  1629. }
  1630. /**
  1631. * Creates a new draw context
  1632. * @returns the new context
  1633. */
  1634. createDrawContext() {
  1635. return new WebGPUDrawContext(this._bufferManager);
  1636. }
  1637. /**
  1638. * @internal
  1639. */
  1640. _preparePipelineContext(pipelineContext, vertexSourceCode, fragmentSourceCode, createAsRaw, rawVertexSourceCode, rawFragmentSourceCode, rebuildRebind, defines) {
  1641. const webGpuContext = pipelineContext;
  1642. const shaderLanguage = webGpuContext.shaderProcessingContext.shaderLanguage;
  1643. if (this.dbgShowShaderCode) {
  1644. Logger.Log(["defines", defines]);
  1645. Logger.Log(vertexSourceCode);
  1646. Logger.Log(fragmentSourceCode);
  1647. Logger.Log("***********************************************");
  1648. }
  1649. webGpuContext.sources = {
  1650. fragment: fragmentSourceCode,
  1651. vertex: vertexSourceCode,
  1652. rawVertex: rawVertexSourceCode,
  1653. rawFragment: rawFragmentSourceCode,
  1654. };
  1655. if (createAsRaw) {
  1656. webGpuContext.stages = this._compileRawPipelineStageDescriptor(vertexSourceCode, fragmentSourceCode, shaderLanguage);
  1657. }
  1658. else {
  1659. webGpuContext.stages = this._compilePipelineStageDescriptor(vertexSourceCode, fragmentSourceCode, defines, shaderLanguage);
  1660. }
  1661. }
  1662. /**
  1663. * Gets the list of active attributes for a given WebGPU program
  1664. * @param pipelineContext defines the pipeline context to use
  1665. * @param attributesNames defines the list of attribute names to get
  1666. * @returns an array of indices indicating the offset of each attribute
  1667. */
  1668. getAttributes(pipelineContext, attributesNames) {
  1669. const results = new Array(attributesNames.length);
  1670. const gpuPipelineContext = pipelineContext;
  1671. for (let i = 0; i < attributesNames.length; i++) {
  1672. const attributeName = attributesNames[i];
  1673. const attributeLocation = gpuPipelineContext.shaderProcessingContext.availableAttributes[attributeName];
  1674. if (attributeLocation === undefined) {
  1675. continue;
  1676. }
  1677. results[i] = attributeLocation;
  1678. }
  1679. return results;
  1680. }
  1681. /**
  1682. * Activates an effect, making it the current one (ie. the one used for rendering)
  1683. * @param effect defines the effect to activate
  1684. */
  1685. enableEffect(effect) {
  1686. if (!effect) {
  1687. return;
  1688. }
  1689. if (!IsWrapper(effect)) {
  1690. this._currentEffect = effect;
  1691. this._currentMaterialContext = this._defaultMaterialContext;
  1692. this._currentDrawContext = this._defaultDrawContext;
  1693. this._counters.numEnableEffects++;
  1694. if (this.dbgLogIfNotDrawWrapper) {
  1695. Logger.Warn(`enableEffect has been called with an Effect and not a Wrapper! effect.uniqueId=${effect.uniqueId}, effect.name=${effect.name}, effect.name.vertex=${typeof effect.name === "string" ? "" : effect.name.vertex}, effect.name.fragment=${typeof effect.name === "string" ? "" : effect.name.fragment}`, 10);
  1696. }
  1697. }
  1698. else if (!effect.effect ||
  1699. (effect.effect === this._currentEffect &&
  1700. effect.materialContext === this._currentMaterialContext &&
  1701. effect.drawContext === this._currentDrawContext &&
  1702. !this._forceEnableEffect)) {
  1703. if (!effect.effect && this.dbgShowEmptyEnableEffectCalls) {
  1704. Logger.Log(["drawWrapper=", effect]);
  1705. // eslint-disable-next-line no-throw-literal
  1706. throw "Invalid call to enableEffect: the effect property is empty!";
  1707. }
  1708. return;
  1709. }
  1710. else {
  1711. this._currentEffect = effect.effect;
  1712. this._currentMaterialContext = effect.materialContext;
  1713. this._currentDrawContext = effect.drawContext;
  1714. this._counters.numEnableDrawWrapper++;
  1715. if (!this._currentMaterialContext) {
  1716. Logger.Log(["drawWrapper=", effect]);
  1717. // eslint-disable-next-line no-throw-literal
  1718. throw `Invalid call to enableEffect: the materialContext property is empty!`;
  1719. }
  1720. }
  1721. this._stencilStateComposer.stencilMaterial = undefined;
  1722. this._forceEnableEffect = false;
  1723. if (this._currentEffect.onBind) {
  1724. this._currentEffect.onBind(this._currentEffect);
  1725. }
  1726. if (this._currentEffect._onBindObservable) {
  1727. this._currentEffect._onBindObservable.notifyObservers(this._currentEffect);
  1728. }
  1729. }
  1730. /**
  1731. * @internal
  1732. */
  1733. _releaseEffect(effect) {
  1734. if (this._compiledEffects[effect._key]) {
  1735. delete this._compiledEffects[effect._key];
  1736. this._deletePipelineContext(effect.getPipelineContext());
  1737. }
  1738. }
  1739. /**
  1740. * Force the engine to release all cached effects. This means that next effect compilation will have to be done completely even if a similar effect was already compiled
  1741. */
  1742. releaseEffects() {
  1743. for (const name in this._compiledEffects) {
  1744. const webGPUPipelineContext = this._compiledEffects[name].getPipelineContext();
  1745. this._deletePipelineContext(webGPUPipelineContext);
  1746. }
  1747. this._compiledEffects = {};
  1748. }
  1749. _deletePipelineContext(pipelineContext) {
  1750. const webgpuPipelineContext = pipelineContext;
  1751. if (webgpuPipelineContext) {
  1752. pipelineContext.dispose();
  1753. }
  1754. }
  1755. //------------------------------------------------------------------------------
  1756. // Textures
  1757. //------------------------------------------------------------------------------
  1758. /**
  1759. * Gets a boolean indicating that only power of 2 textures are supported
  1760. * Please note that you can still use non power of 2 textures but in this case the engine will forcefully convert them
  1761. */
  1762. get needPOTTextures() {
  1763. return false;
  1764. }
  1765. /** @internal */
  1766. _createHardwareTexture() {
  1767. return new WebGPUHardwareTexture();
  1768. }
  1769. /**
  1770. * @internal
  1771. */
  1772. _releaseTexture(texture) {
  1773. const index = this._internalTexturesCache.indexOf(texture);
  1774. if (index !== -1) {
  1775. this._internalTexturesCache.splice(index, 1);
  1776. }
  1777. this._textureHelper.releaseTexture(texture);
  1778. }
  1779. /**
  1780. * @internal
  1781. */
  1782. _getRGBABufferInternalSizedFormat() {
  1783. return 5;
  1784. }
  1785. updateTextureComparisonFunction(texture, comparisonFunction) {
  1786. texture._comparisonFunction = comparisonFunction;
  1787. }
  1788. /**
  1789. * Creates an internal texture without binding it to a framebuffer
  1790. * @internal
  1791. * @param size defines the size of the texture
  1792. * @param options defines the options used to create the texture
  1793. * @param delayGPUTextureCreation true to delay the texture creation the first time it is really needed. false to create it right away
  1794. * @param source source type of the texture
  1795. * @returns a new internal texture
  1796. */
  1797. _createInternalTexture(size, options, delayGPUTextureCreation = true, source = InternalTextureSource.Unknown) {
  1798. const fullOptions = {};
  1799. if (options !== undefined && typeof options === "object") {
  1800. fullOptions.generateMipMaps = options.generateMipMaps;
  1801. fullOptions.type = options.type === undefined ? 0 : options.type;
  1802. fullOptions.samplingMode = options.samplingMode === undefined ? 3 : options.samplingMode;
  1803. fullOptions.format = options.format === undefined ? 5 : options.format;
  1804. fullOptions.samples = options.samples ?? 1;
  1805. fullOptions.creationFlags = options.creationFlags ?? 0;
  1806. fullOptions.useSRGBBuffer = options.useSRGBBuffer ?? false;
  1807. fullOptions.label = options.label;
  1808. }
  1809. else {
  1810. fullOptions.generateMipMaps = options;
  1811. fullOptions.type = 0;
  1812. fullOptions.samplingMode = 3;
  1813. fullOptions.format = 5;
  1814. fullOptions.samples = 1;
  1815. fullOptions.creationFlags = 0;
  1816. fullOptions.useSRGBBuffer = false;
  1817. }
  1818. if (fullOptions.type === 1 && !this._caps.textureFloatLinearFiltering) {
  1819. fullOptions.samplingMode = 1;
  1820. }
  1821. else if (fullOptions.type === 2 && !this._caps.textureHalfFloatLinearFiltering) {
  1822. fullOptions.samplingMode = 1;
  1823. }
  1824. if (fullOptions.type === 1 && !this._caps.textureFloat) {
  1825. fullOptions.type = 0;
  1826. Logger.Warn("Float textures are not supported. Type forced to TEXTURETYPE_UNSIGNED_BYTE");
  1827. }
  1828. const texture = new InternalTexture(this, source);
  1829. const width = size.width || size;
  1830. const height = size.height || size;
  1831. const depth = size.depth || 0;
  1832. const layers = size.layers || 0;
  1833. texture.baseWidth = width;
  1834. texture.baseHeight = height;
  1835. texture.width = width;
  1836. texture.height = height;
  1837. texture.depth = depth || layers;
  1838. texture.isReady = true;
  1839. texture.samples = fullOptions.samples;
  1840. texture.generateMipMaps = fullOptions.generateMipMaps ? true : false;
  1841. texture.samplingMode = fullOptions.samplingMode;
  1842. texture.type = fullOptions.type;
  1843. texture.format = fullOptions.format;
  1844. texture.is2DArray = layers > 0;
  1845. texture.is3D = depth > 0;
  1846. texture._cachedWrapU = 0;
  1847. texture._cachedWrapV = 0;
  1848. texture._useSRGBBuffer = fullOptions.useSRGBBuffer;
  1849. texture.label = fullOptions.label;
  1850. this._internalTexturesCache.push(texture);
  1851. if (!delayGPUTextureCreation) {
  1852. this._textureHelper.createGPUTextureForInternalTexture(texture, width, height, layers || 1, fullOptions.creationFlags);
  1853. }
  1854. return texture;
  1855. }
  1856. /**
  1857. * Usually called from Texture.ts.
  1858. * Passed information to create a hardware texture
  1859. * @param url defines a value which contains one of the following:
  1860. * * A conventional http URL, e.g. 'http://...' or 'file://...'
  1861. * * A base64 string of in-line texture data, e.g. 'data:image/jpg;base64,/...'
  1862. * * An indicator that data being passed using the buffer parameter, e.g. 'data:mytexture.jpg'
  1863. * @param noMipmap defines a boolean indicating that no mipmaps shall be generated. Ignored for compressed textures. They must be in the file
  1864. * @param invertY when true, image is flipped when loaded. You probably want true. Certain compressed textures may invert this if their default is inverted (eg. ktx)
  1865. * @param scene needed for loading to the correct scene
  1866. * @param samplingMode mode with should be used sample / access the texture (Default: Texture.TRILINEAR_SAMPLINGMODE)
  1867. * @param onLoad optional callback to be called upon successful completion
  1868. * @param onError optional callback to be called upon failure
  1869. * @param buffer a source of a file previously fetched as either a base64 string, an ArrayBuffer (compressed or image format), HTMLImageElement (image format), or a Blob
  1870. * @param fallback an internal argument in case the function must be called again, due to etc1 not having alpha capabilities
  1871. * @param format internal format. Default: RGB when extension is '.jpg' else RGBA. Ignored for compressed textures
  1872. * @param forcedExtension defines the extension to use to pick the right loader
  1873. * @param mimeType defines an optional mime type
  1874. * @param loaderOptions options to be passed to the loader
  1875. * @param creationFlags specific flags to use when creating the texture (1 for storage textures, for eg)
  1876. * @param useSRGBBuffer defines if the texture must be loaded in a sRGB GPU buffer (if supported by the GPU).
  1877. * @returns a InternalTexture for assignment back into BABYLON.Texture
  1878. */
  1879. createTexture(url, noMipmap, invertY, scene, samplingMode = 3, onLoad = null, onError = null, buffer = null, fallback = null, format = null, forcedExtension = null, mimeType, loaderOptions, creationFlags, useSRGBBuffer) {
  1880. return this._createTextureBase(url, noMipmap, invertY, scene, samplingMode, onLoad, onError, (texture, extension, scene, img, invertY, noMipmap, isCompressed, processFunction) => {
  1881. const imageBitmap = img; // we will never get an HTMLImageElement in WebGPU
  1882. texture.baseWidth = imageBitmap.width;
  1883. texture.baseHeight = imageBitmap.height;
  1884. texture.width = imageBitmap.width;
  1885. texture.height = imageBitmap.height;
  1886. texture.format = texture.format !== -1 ? texture.format : format ?? 5;
  1887. texture.type = texture.type !== -1 ? texture.type : 0;
  1888. texture._creationFlags = creationFlags ?? 0;
  1889. processFunction(texture.width, texture.height, imageBitmap, extension, texture, () => { });
  1890. if (!texture._hardwareTexture?.underlyingResource) {
  1891. // the texture could have been created before reaching this point so don't recreate it if already existing
  1892. const gpuTextureWrapper = this._textureHelper.createGPUTextureForInternalTexture(texture, imageBitmap.width, imageBitmap.height, undefined, creationFlags);
  1893. if (WebGPUTextureHelper.IsImageBitmap(imageBitmap)) {
  1894. this._textureHelper.updateTexture(imageBitmap, texture, imageBitmap.width, imageBitmap.height, texture.depth, gpuTextureWrapper.format, 0, 0, invertY, false, 0, 0);
  1895. if (!noMipmap && !isCompressed) {
  1896. this._generateMipmaps(texture, this._uploadEncoder);
  1897. }
  1898. }
  1899. }
  1900. else if (!noMipmap && !isCompressed) {
  1901. this._generateMipmaps(texture, this._uploadEncoder);
  1902. }
  1903. if (scene) {
  1904. scene.removePendingData(texture);
  1905. }
  1906. texture.isReady = true;
  1907. texture.onLoadedObservable.notifyObservers(texture);
  1908. texture.onLoadedObservable.clear();
  1909. }, () => false, buffer, fallback, format, forcedExtension, mimeType, loaderOptions, useSRGBBuffer);
  1910. }
  1911. /**
  1912. * Wraps an external web gpu texture in a Babylon texture.
  1913. * @param texture defines the external texture
  1914. * @returns the babylon internal texture
  1915. */
  1916. wrapWebGPUTexture(texture) {
  1917. const hardwareTexture = new WebGPUHardwareTexture(texture);
  1918. const internalTexture = new InternalTexture(this, InternalTextureSource.Unknown, true);
  1919. internalTexture._hardwareTexture = hardwareTexture;
  1920. internalTexture.isReady = true;
  1921. return internalTexture;
  1922. }
  1923. // eslint-disable-next-line jsdoc/require-returns-check
  1924. /**
  1925. * Wraps an external web gl texture in a Babylon texture.
  1926. * @returns the babylon internal texture
  1927. */
  1928. wrapWebGLTexture() {
  1929. throw new Error("wrapWebGLTexture is not supported, use wrapWebGPUTexture instead.");
  1930. }
  1931. generateMipMapsForCubemap(texture) {
  1932. if (texture.generateMipMaps) {
  1933. const gpuTexture = texture._hardwareTexture?.underlyingResource;
  1934. if (!gpuTexture) {
  1935. this._textureHelper.createGPUTextureForInternalTexture(texture);
  1936. }
  1937. this._generateMipmaps(texture);
  1938. }
  1939. }
  1940. /**
  1941. * @internal
  1942. */
  1943. _getUseSRGBBuffer(useSRGBBuffer, noMipmap) {
  1944. return useSRGBBuffer && this._caps.supportSRGBBuffers;
  1945. }
  1946. /**
  1947. * Update the sampling mode of a given texture
  1948. * @param samplingMode defines the required sampling mode
  1949. * @param texture defines the texture to update
  1950. * @param generateMipMaps defines whether to generate mipmaps for the texture
  1951. */
  1952. updateTextureSamplingMode(samplingMode, texture, generateMipMaps = false) {
  1953. if (generateMipMaps) {
  1954. texture.generateMipMaps = true;
  1955. this._generateMipmaps(texture);
  1956. }
  1957. texture.samplingMode = samplingMode;
  1958. }
  1959. /**
  1960. * Update the sampling mode of a given texture
  1961. * @param texture defines the texture to update
  1962. * @param wrapU defines the texture wrap mode of the u coordinates
  1963. * @param wrapV defines the texture wrap mode of the v coordinates
  1964. * @param wrapR defines the texture wrap mode of the r coordinates
  1965. */
  1966. updateTextureWrappingMode(texture, wrapU, wrapV = null, wrapR = null) {
  1967. if (wrapU !== null) {
  1968. texture._cachedWrapU = wrapU;
  1969. }
  1970. if (wrapV !== null) {
  1971. texture._cachedWrapV = wrapV;
  1972. }
  1973. if ((texture.is2DArray || texture.is3D) && wrapR !== null) {
  1974. texture._cachedWrapR = wrapR;
  1975. }
  1976. }
  1977. /**
  1978. * Update the dimensions of a texture
  1979. * @param texture texture to update
  1980. * @param width new width of the texture
  1981. * @param height new height of the texture
  1982. * @param depth new depth of the texture
  1983. */
  1984. updateTextureDimensions(texture, width, height, depth = 1) {
  1985. if (!texture._hardwareTexture) {
  1986. // the gpu texture is not created yet, so when it is it will be created with the right dimensions
  1987. return;
  1988. }
  1989. if (texture.width === width && texture.height === height && texture.depth === depth) {
  1990. return;
  1991. }
  1992. const additionalUsages = texture._hardwareTexture.textureAdditionalUsages;
  1993. texture._hardwareTexture.release(); // don't defer the releasing! Else we will release at the end of this frame the gpu texture we are about to create in the next line...
  1994. this._textureHelper.createGPUTextureForInternalTexture(texture, width, height, depth, additionalUsages);
  1995. }
  1996. /**
  1997. * @internal
  1998. */
  1999. _setInternalTexture(name, texture, baseName) {
  2000. baseName = baseName ?? name;
  2001. if (this._currentEffect) {
  2002. const webgpuPipelineContext = this._currentEffect._pipelineContext;
  2003. const availableTexture = webgpuPipelineContext.shaderProcessingContext.availableTextures[baseName];
  2004. this._currentMaterialContext.setTexture(name, texture);
  2005. if (availableTexture && availableTexture.autoBindSampler) {
  2006. const samplerName = baseName + WebGPUShaderProcessor.AutoSamplerSuffix;
  2007. this._currentMaterialContext.setSampler(samplerName, texture); // we can safely cast to InternalTexture because ExternalTexture always has autoBindSampler = false
  2008. }
  2009. }
  2010. }
  2011. /**
  2012. * Create a cube texture from prefiltered data (ie. the mipmaps contain ready to use data for PBR reflection)
  2013. * @param rootUrl defines the url where the file to load is located
  2014. * @param scene defines the current scene
  2015. * @param lodScale defines scale to apply to the mip map selection
  2016. * @param lodOffset defines offset to apply to the mip map selection
  2017. * @param onLoad defines an optional callback raised when the texture is loaded
  2018. * @param onError defines an optional callback raised if there is an issue to load the texture
  2019. * @param format defines the format of the data
  2020. * @param forcedExtension defines the extension to use to pick the right loader
  2021. * @param createPolynomials defines wheter or not to create polynomails harmonics for the texture
  2022. * @returns the cube texture as an InternalTexture
  2023. */
  2024. createPrefilteredCubeTexture(rootUrl, scene, lodScale, lodOffset, onLoad = null, onError = null, format, forcedExtension = null, createPolynomials = true) {
  2025. const callback = (loadData) => {
  2026. if (!loadData) {
  2027. if (onLoad) {
  2028. onLoad(null);
  2029. }
  2030. return;
  2031. }
  2032. const texture = loadData.texture;
  2033. if (!createPolynomials) {
  2034. texture._sphericalPolynomial = new SphericalPolynomial();
  2035. }
  2036. else if (loadData.info.sphericalPolynomial) {
  2037. texture._sphericalPolynomial = loadData.info.sphericalPolynomial;
  2038. }
  2039. texture._source = InternalTextureSource.CubePrefiltered;
  2040. if (onLoad) {
  2041. onLoad(texture);
  2042. }
  2043. };
  2044. return this.createCubeTexture(rootUrl, scene, null, false, callback, onError, format, forcedExtension, createPolynomials, lodScale, lodOffset);
  2045. }
  2046. /**
  2047. * Sets a texture to the according uniform.
  2048. * @param channel The texture channel
  2049. * @param unused unused parameter
  2050. * @param texture The texture to apply
  2051. * @param name The name of the uniform in the effect
  2052. */
  2053. setTexture(channel, unused, texture, name) {
  2054. this._setTexture(channel, texture, false, false, name, name);
  2055. }
  2056. /**
  2057. * Sets an array of texture to the WebGPU context
  2058. * @param channel defines the channel where the texture array must be set
  2059. * @param unused unused parameter
  2060. * @param textures defines the array of textures to bind
  2061. * @param name name of the channel
  2062. */
  2063. setTextureArray(channel, unused, textures, name) {
  2064. for (let index = 0; index < textures.length; index++) {
  2065. this._setTexture(-1, textures[index], true, false, name + index.toString(), name);
  2066. }
  2067. }
  2068. _setTexture(channel, texture,
  2069. // eslint-disable-next-line @typescript-eslint/no-unused-vars
  2070. isPartOfTextureArray = false, depthStencilTexture = false, name = "", baseName) {
  2071. // name == baseName for a texture that is not part of a texture array
  2072. // Else, name is something like 'myTexture0' / 'myTexture1' / ... and baseName is 'myTexture'
  2073. // baseName is used to look up the texture in the shaderProcessingContext.availableTextures map
  2074. // name is used to look up the texture in the _currentMaterialContext.textures map
  2075. baseName = baseName ?? name;
  2076. if (this._currentEffect) {
  2077. if (!texture) {
  2078. this._currentMaterialContext.setTexture(name, null);
  2079. return false;
  2080. }
  2081. // Video
  2082. if (texture.video) {
  2083. texture.update();
  2084. }
  2085. else if (texture.delayLoadState === 4) {
  2086. // Delay loading
  2087. texture.delayLoad();
  2088. return false;
  2089. }
  2090. let internalTexture = null;
  2091. if (depthStencilTexture) {
  2092. internalTexture = texture.depthStencilTexture;
  2093. }
  2094. else if (texture.isReady()) {
  2095. internalTexture = texture.getInternalTexture();
  2096. }
  2097. else if (texture.isCube) {
  2098. internalTexture = this.emptyCubeTexture;
  2099. }
  2100. else if (texture.is3D) {
  2101. internalTexture = this.emptyTexture3D;
  2102. }
  2103. else if (texture.is2DArray) {
  2104. internalTexture = this.emptyTexture2DArray;
  2105. }
  2106. else {
  2107. internalTexture = this.emptyTexture;
  2108. }
  2109. if (internalTexture && !internalTexture.isMultiview) {
  2110. // CUBIC_MODE and SKYBOX_MODE both require CLAMP_TO_EDGE. All other modes use REPEAT.
  2111. if (internalTexture.isCube && internalTexture._cachedCoordinatesMode !== texture.coordinatesMode) {
  2112. internalTexture._cachedCoordinatesMode = texture.coordinatesMode;
  2113. const textureWrapMode = texture.coordinatesMode !== 3 && texture.coordinatesMode !== 5
  2114. ? 1
  2115. : 0;
  2116. texture.wrapU = textureWrapMode;
  2117. texture.wrapV = textureWrapMode;
  2118. }
  2119. internalTexture._cachedWrapU = texture.wrapU;
  2120. internalTexture._cachedWrapV = texture.wrapV;
  2121. if (internalTexture.is3D) {
  2122. internalTexture._cachedWrapR = texture.wrapR;
  2123. }
  2124. this._setAnisotropicLevel(0, internalTexture, texture.anisotropicFilteringLevel);
  2125. }
  2126. this._setInternalTexture(name, internalTexture, baseName);
  2127. }
  2128. else {
  2129. if (this.dbgVerboseLogsForFirstFrames) {
  2130. if (this._count === undefined) {
  2131. this._count = 0;
  2132. }
  2133. if (!this._count || this._count < this.dbgVerboseLogsNumFrames) {
  2134. Logger.Log(["frame #" + this._count + " - _setTexture called with a null _currentEffect! texture=", texture]);
  2135. }
  2136. }
  2137. }
  2138. return true;
  2139. }
  2140. /**
  2141. * @internal
  2142. */
  2143. _setAnisotropicLevel(target, internalTexture, anisotropicFilteringLevel) {
  2144. if (internalTexture._cachedAnisotropicFilteringLevel !== anisotropicFilteringLevel) {
  2145. internalTexture._cachedAnisotropicFilteringLevel = Math.min(anisotropicFilteringLevel, this._caps.maxAnisotropy);
  2146. }
  2147. }
  2148. /**
  2149. * @internal
  2150. */
  2151. _bindTexture(channel, texture, name) {
  2152. if (channel === undefined) {
  2153. return;
  2154. }
  2155. this._setInternalTexture(name, texture);
  2156. }
  2157. /**
  2158. * Generates the mipmaps for a texture
  2159. * @param texture texture to generate the mipmaps for
  2160. */
  2161. generateMipmaps(texture) {
  2162. this._generateMipmaps(texture);
  2163. }
  2164. /**
  2165. * @internal
  2166. */
  2167. _generateMipmaps(texture, commandEncoder) {
  2168. commandEncoder = commandEncoder ?? this._renderEncoder;
  2169. const gpuHardwareTexture = texture._hardwareTexture;
  2170. if (!gpuHardwareTexture) {
  2171. return;
  2172. }
  2173. if (commandEncoder === this._renderEncoder) {
  2174. // We must close the current pass (if any) because we are going to use the render encoder to generate the mipmaps (so, we are going to create a new render pass)
  2175. this._endCurrentRenderPass();
  2176. }
  2177. const format = texture._hardwareTexture.format;
  2178. const mipmapCount = WebGPUTextureHelper.ComputeNumMipmapLevels(texture.width, texture.height);
  2179. if (this.dbgVerboseLogsForFirstFrames) {
  2180. if (this._count === undefined) {
  2181. this._count = 0;
  2182. }
  2183. if (!this._count || this._count < this.dbgVerboseLogsNumFrames) {
  2184. Logger.Log("frame #" +
  2185. this._count +
  2186. " - generate mipmaps - width=" +
  2187. texture.width +
  2188. ", height=" +
  2189. texture.height +
  2190. ", isCube=" +
  2191. texture.isCube +
  2192. ", command encoder=" +
  2193. (commandEncoder === this._renderEncoder ? "render" : "copy"));
  2194. }
  2195. }
  2196. if (texture.isCube) {
  2197. this._textureHelper.generateCubeMipmaps(gpuHardwareTexture, format, mipmapCount, commandEncoder);
  2198. }
  2199. else {
  2200. this._textureHelper.generateMipmaps(gpuHardwareTexture, format, mipmapCount, 0, texture.is3D, commandEncoder);
  2201. }
  2202. }
  2203. /**
  2204. * Update a portion of an internal texture
  2205. * @param texture defines the texture to update
  2206. * @param imageData defines the data to store into the texture
  2207. * @param xOffset defines the x coordinates of the update rectangle
  2208. * @param yOffset defines the y coordinates of the update rectangle
  2209. * @param width defines the width of the update rectangle
  2210. * @param height defines the height of the update rectangle
  2211. * @param faceIndex defines the face index if texture is a cube (0 by default)
  2212. * @param lod defines the lod level to update (0 by default)
  2213. * @param generateMipMaps defines whether to generate mipmaps or not
  2214. */
  2215. updateTextureData(texture, imageData, xOffset, yOffset, width, height, faceIndex = 0, lod = 0, generateMipMaps = false) {
  2216. let gpuTextureWrapper = texture._hardwareTexture;
  2217. if (!texture._hardwareTexture?.underlyingResource) {
  2218. gpuTextureWrapper = this._textureHelper.createGPUTextureForInternalTexture(texture);
  2219. }
  2220. const data = new Uint8Array(imageData.buffer, imageData.byteOffset, imageData.byteLength);
  2221. this._textureHelper.updateTexture(data, texture, width, height, texture.depth, gpuTextureWrapper.format, faceIndex, lod, texture.invertY, false, xOffset, yOffset);
  2222. if (generateMipMaps) {
  2223. this._generateMipmaps(texture);
  2224. }
  2225. }
  2226. /**
  2227. * @internal
  2228. */
  2229. _uploadCompressedDataToTextureDirectly(texture, internalFormat, width, height, imageData, faceIndex = 0, lod = 0) {
  2230. let gpuTextureWrapper = texture._hardwareTexture;
  2231. if (!texture._hardwareTexture?.underlyingResource) {
  2232. texture.format = internalFormat;
  2233. gpuTextureWrapper = this._textureHelper.createGPUTextureForInternalTexture(texture, width, height);
  2234. }
  2235. const data = new Uint8Array(imageData.buffer, imageData.byteOffset, imageData.byteLength);
  2236. this._textureHelper.updateTexture(data, texture, width, height, texture.depth, gpuTextureWrapper.format, faceIndex, lod, false, false, 0, 0);
  2237. }
  2238. /**
  2239. * @internal
  2240. */
  2241. _uploadDataToTextureDirectly(texture, imageData, faceIndex = 0, lod = 0, babylonInternalFormat, useTextureWidthAndHeight = false) {
  2242. const lodMaxWidth = Math.round(Math.log(texture.width) * Math.LOG2E);
  2243. const lodMaxHeight = Math.round(Math.log(texture.height) * Math.LOG2E);
  2244. const width = useTextureWidthAndHeight ? texture.width : Math.pow(2, Math.max(lodMaxWidth - lod, 0));
  2245. const height = useTextureWidthAndHeight ? texture.height : Math.pow(2, Math.max(lodMaxHeight - lod, 0));
  2246. let gpuTextureWrapper = texture._hardwareTexture;
  2247. if (!texture._hardwareTexture?.underlyingResource) {
  2248. gpuTextureWrapper = this._textureHelper.createGPUTextureForInternalTexture(texture, width, height);
  2249. }
  2250. const data = new Uint8Array(imageData.buffer, imageData.byteOffset, imageData.byteLength);
  2251. this._textureHelper.updateTexture(data, texture, width, height, texture.depth, gpuTextureWrapper.format, faceIndex, lod, texture.invertY, false, 0, 0);
  2252. }
  2253. /**
  2254. * @internal
  2255. */
  2256. _uploadArrayBufferViewToTexture(texture, imageData, faceIndex = 0, lod = 0) {
  2257. this._uploadDataToTextureDirectly(texture, imageData, faceIndex, lod);
  2258. }
  2259. /**
  2260. * @internal
  2261. */
  2262. _uploadImageToTexture(texture, image, faceIndex = 0, lod = 0) {
  2263. let gpuTextureWrapper = texture._hardwareTexture;
  2264. if (!texture._hardwareTexture?.underlyingResource) {
  2265. gpuTextureWrapper = this._textureHelper.createGPUTextureForInternalTexture(texture);
  2266. }
  2267. if (image instanceof HTMLImageElement) {
  2268. // eslint-disable-next-line no-throw-literal
  2269. throw "WebGPU engine: HTMLImageElement not supported in _uploadImageToTexture!";
  2270. }
  2271. const bitmap = image; // in WebGPU we will always get an ImageBitmap, not an HTMLImageElement
  2272. const width = Math.ceil(texture.width / (1 << lod));
  2273. const height = Math.ceil(texture.height / (1 << lod));
  2274. this._textureHelper.updateTexture(bitmap, texture, width, height, texture.depth, gpuTextureWrapper.format, faceIndex, lod, texture.invertY, false, 0, 0);
  2275. }
  2276. /**
  2277. * Reads pixels from the current frame buffer. Please note that this function can be slow
  2278. * @param x defines the x coordinate of the rectangle where pixels must be read
  2279. * @param y defines the y coordinate of the rectangle where pixels must be read
  2280. * @param width defines the width of the rectangle where pixels must be read
  2281. * @param height defines the height of the rectangle where pixels must be read
  2282. * @param hasAlpha defines whether the output should have alpha or not (defaults to true)
  2283. * @param flushRenderer true to flush the renderer from the pending commands before reading the pixels
  2284. * @returns a ArrayBufferView promise (Uint8Array) containing RGBA colors
  2285. */
  2286. // eslint-disable-next-line @typescript-eslint/no-unused-vars
  2287. readPixels(x, y, width, height, hasAlpha = true, flushRenderer = true) {
  2288. const renderPassWrapper = this._getCurrentRenderPassWrapper();
  2289. const hardwareTexture = renderPassWrapper.colorAttachmentGPUTextures[0];
  2290. if (!hardwareTexture) {
  2291. // we are calling readPixels for a render pass with no color texture bound
  2292. return Promise.resolve(new Uint8Array(0));
  2293. }
  2294. const gpuTexture = hardwareTexture.underlyingResource;
  2295. const gpuTextureFormat = hardwareTexture.format;
  2296. if (!gpuTexture) {
  2297. // we are calling readPixels before startMainRenderPass has been called and no RTT is bound, so swapChainTexture is not setup yet!
  2298. return Promise.resolve(new Uint8Array(0));
  2299. }
  2300. if (flushRenderer) {
  2301. this.flushFramebuffer();
  2302. }
  2303. return this._textureHelper.readPixels(gpuTexture, x, y, width, height, gpuTextureFormat);
  2304. }
  2305. //------------------------------------------------------------------------------
  2306. // Frame management
  2307. //------------------------------------------------------------------------------
  2308. _measureFps() {
  2309. this._performanceMonitor.sampleFrame();
  2310. this._fps = this._performanceMonitor.averageFPS;
  2311. this._deltaTime = this._performanceMonitor.instantaneousFrameTime || 0;
  2312. }
  2313. /**
  2314. * Gets the performance monitor attached to this engine
  2315. * @see https://doc.babylonjs.com/features/featuresDeepDive/scene/optimize_your_scene#engineinstrumentation
  2316. */
  2317. get performanceMonitor() {
  2318. return this._performanceMonitor;
  2319. }
  2320. /**
  2321. * Begin a new frame
  2322. */
  2323. beginFrame() {
  2324. this._measureFps();
  2325. super.beginFrame();
  2326. }
  2327. /**
  2328. * End the current frame
  2329. */
  2330. endFrame() {
  2331. this._endCurrentRenderPass();
  2332. this._snapshotRendering.endFrame();
  2333. this._timestampQuery.endFrame(this._renderEncoder);
  2334. this._timestampIndex = 0;
  2335. this.flushFramebuffer();
  2336. this._textureHelper.destroyDeferredTextures();
  2337. this._bufferManager.destroyDeferredBuffers();
  2338. if (this._features._collectUbosUpdatedInFrame) {
  2339. if (this.dbgVerboseLogsForFirstFrames) {
  2340. if (this._count === undefined) {
  2341. this._count = 0;
  2342. }
  2343. if (!this._count || this._count < this.dbgVerboseLogsNumFrames) {
  2344. const list = [];
  2345. for (const name in UniformBuffer._UpdatedUbosInFrame) {
  2346. list.push(name + ":" + UniformBuffer._UpdatedUbosInFrame[name]);
  2347. }
  2348. Logger.Log(["frame #" + this._count + " - updated ubos -", list.join(", ")]);
  2349. }
  2350. }
  2351. UniformBuffer._UpdatedUbosInFrame = {};
  2352. }
  2353. this.countersLastFrame.numEnableEffects = this._counters.numEnableEffects;
  2354. this.countersLastFrame.numEnableDrawWrapper = this._counters.numEnableDrawWrapper;
  2355. this.countersLastFrame.numBundleCreationNonCompatMode = this._counters.numBundleCreationNonCompatMode;
  2356. this.countersLastFrame.numBundleReuseNonCompatMode = this._counters.numBundleReuseNonCompatMode;
  2357. this._counters.numEnableEffects = 0;
  2358. this._counters.numEnableDrawWrapper = 0;
  2359. this._counters.numBundleCreationNonCompatMode = 0;
  2360. this._counters.numBundleReuseNonCompatMode = 0;
  2361. this._cacheRenderPipeline.endFrame();
  2362. this._cacheBindGroups.endFrame();
  2363. this._pendingDebugCommands.length = 0;
  2364. if (this.dbgVerboseLogsForFirstFrames) {
  2365. if (this._count === undefined) {
  2366. this._count = 0;
  2367. }
  2368. if (this._count < this.dbgVerboseLogsNumFrames) {
  2369. Logger.Log(["%c frame #" + this._count + " - end", "background: #ffff00"]);
  2370. }
  2371. if (this._count < this.dbgVerboseLogsNumFrames) {
  2372. this._count++;
  2373. if (this._count !== this.dbgVerboseLogsNumFrames) {
  2374. Logger.Log(["%c frame #" + this._count + " - begin", "background: #ffff00"]);
  2375. }
  2376. }
  2377. }
  2378. super.endFrame();
  2379. }
  2380. /**Gets driver info if available */
  2381. extractDriverInfo() {
  2382. return "";
  2383. }
  2384. /**
  2385. * Force a WebGPU flush (ie. a flush of all waiting commands)
  2386. */
  2387. flushFramebuffer() {
  2388. // we need to end the current render pass (main or rtt) if any as we are not allowed to submit the command buffers when being in a pass
  2389. this._endCurrentRenderPass();
  2390. this._commandBuffers[0] = this._uploadEncoder.finish();
  2391. this._commandBuffers[1] = this._renderEncoder.finish();
  2392. this._device.queue.submit(this._commandBuffers);
  2393. this._uploadEncoder = this._device.createCommandEncoder(this._uploadEncoderDescriptor);
  2394. this._renderEncoder = this._device.createCommandEncoder(this._renderEncoderDescriptor);
  2395. this._timestampQuery.startFrame(this._uploadEncoder);
  2396. this._textureHelper.setCommandEncoder(this._uploadEncoder);
  2397. this._bundleList.reset();
  2398. }
  2399. /** @internal */
  2400. _currentFrameBufferIsDefaultFrameBuffer() {
  2401. return this._currentPassIsMainPass();
  2402. }
  2403. //------------------------------------------------------------------------------
  2404. // Render Pass
  2405. //------------------------------------------------------------------------------
  2406. _startRenderTargetRenderPass(renderTargetWrapper, setClearStates, clearColor, clearDepth, clearStencil) {
  2407. this._endCurrentRenderPass();
  2408. const rtWrapper = renderTargetWrapper;
  2409. const depthStencilTexture = rtWrapper._depthStencilTexture;
  2410. const gpuDepthStencilWrapper = depthStencilTexture?._hardwareTexture;
  2411. const gpuDepthStencilTexture = gpuDepthStencilWrapper?.underlyingResource;
  2412. const gpuDepthStencilMSAATexture = gpuDepthStencilWrapper?.getMSAATexture();
  2413. const depthTextureView = gpuDepthStencilTexture?.createView(this._rttRenderPassWrapper.depthAttachmentViewDescriptor);
  2414. const depthMSAATextureView = gpuDepthStencilMSAATexture?.createView(this._rttRenderPassWrapper.depthAttachmentViewDescriptor);
  2415. const depthTextureHasStencil = gpuDepthStencilWrapper ? WebGPUTextureHelper.HasStencilAspect(gpuDepthStencilWrapper.format) : false;
  2416. const colorAttachments = [];
  2417. if (this.useReverseDepthBuffer) {
  2418. this.setDepthFunctionToGreaterOrEqual();
  2419. }
  2420. const clearColorForIntegerRT = tempColor4;
  2421. if (clearColor) {
  2422. clearColorForIntegerRT.r = clearColor.r * 255;
  2423. clearColorForIntegerRT.g = clearColor.g * 255;
  2424. clearColorForIntegerRT.b = clearColor.b * 255;
  2425. clearColorForIntegerRT.a = clearColor.a * 255;
  2426. }
  2427. const mustClearColor = setClearStates && clearColor;
  2428. const mustClearDepth = setClearStates && clearDepth;
  2429. const mustClearStencil = setClearStates && clearStencil;
  2430. if (rtWrapper._attachments && rtWrapper.isMulti) {
  2431. // multi render targets
  2432. if (!this._mrtAttachments || this._mrtAttachments.length === 0) {
  2433. this._mrtAttachments = rtWrapper._defaultAttachments;
  2434. }
  2435. for (let i = 0; i < this._mrtAttachments.length; ++i) {
  2436. const index = this._mrtAttachments[i]; // if index == 0 it means the texture should not be written to => at render pass creation time, it means we should not clear it
  2437. const mrtTexture = rtWrapper.textures[i];
  2438. const gpuMRTWrapper = mrtTexture?._hardwareTexture;
  2439. const gpuMRTTexture = gpuMRTWrapper?.underlyingResource;
  2440. if (gpuMRTWrapper && gpuMRTTexture) {
  2441. const gpuMSAATexture = gpuMRTWrapper.getMSAATexture(i);
  2442. const layerIndex = rtWrapper.layerIndices?.[i] ?? 0;
  2443. const faceIndex = rtWrapper.faceIndices?.[i] ?? 0;
  2444. const viewDescriptor = {
  2445. ...this._rttRenderPassWrapper.colorAttachmentViewDescriptor,
  2446. dimension: mrtTexture.is3D ? WebGPUConstants.TextureViewDimension.E3d : WebGPUConstants.TextureViewDimension.E2d,
  2447. format: gpuMRTWrapper.format,
  2448. baseArrayLayer: mrtTexture.isCube ? layerIndex * 6 + faceIndex : mrtTexture.is3D ? 0 : layerIndex,
  2449. };
  2450. const msaaViewDescriptor = {
  2451. ...this._rttRenderPassWrapper.colorAttachmentViewDescriptor,
  2452. dimension: mrtTexture.is3D ? WebGPUConstants.TextureViewDimension.E3d : WebGPUConstants.TextureViewDimension.E2d,
  2453. format: gpuMRTWrapper.format,
  2454. baseArrayLayer: 0,
  2455. };
  2456. const isRTInteger = mrtTexture.type === 7 || mrtTexture.type === 5;
  2457. const colorTextureView = gpuMRTTexture.createView(viewDescriptor);
  2458. const colorMSAATextureView = gpuMSAATexture?.createView(msaaViewDescriptor);
  2459. colorAttachments.push({
  2460. view: colorMSAATextureView ? colorMSAATextureView : colorTextureView,
  2461. resolveTarget: gpuMSAATexture ? colorTextureView : undefined,
  2462. depthSlice: mrtTexture.is3D ? layerIndex : undefined,
  2463. clearValue: index !== 0 && mustClearColor ? (isRTInteger ? clearColorForIntegerRT : clearColor) : undefined,
  2464. loadOp: index !== 0 && mustClearColor ? WebGPUConstants.LoadOp.Clear : WebGPUConstants.LoadOp.Load,
  2465. storeOp: WebGPUConstants.StoreOp.Store,
  2466. });
  2467. }
  2468. }
  2469. this._cacheRenderPipeline.setMRT(rtWrapper.textures, this._mrtAttachments.length);
  2470. this._cacheRenderPipeline.setMRTAttachments(this._mrtAttachments);
  2471. }
  2472. else {
  2473. // single render target
  2474. const internalTexture = rtWrapper.texture;
  2475. if (internalTexture) {
  2476. const gpuWrapper = internalTexture._hardwareTexture;
  2477. const gpuTexture = gpuWrapper.underlyingResource;
  2478. let depthSlice = undefined;
  2479. if (rtWrapper.is3D) {
  2480. depthSlice = this._rttRenderPassWrapper.colorAttachmentViewDescriptor.baseArrayLayer;
  2481. this._rttRenderPassWrapper.colorAttachmentViewDescriptor.baseArrayLayer = 0;
  2482. }
  2483. const gpuMSAATexture = gpuWrapper.getMSAATexture();
  2484. const colorTextureView = gpuTexture.createView(this._rttRenderPassWrapper.colorAttachmentViewDescriptor);
  2485. const colorMSAATextureView = gpuMSAATexture?.createView(this._rttRenderPassWrapper.colorAttachmentViewDescriptor);
  2486. const isRTInteger = internalTexture.type === 7 || internalTexture.type === 5;
  2487. colorAttachments.push({
  2488. view: colorMSAATextureView ? colorMSAATextureView : colorTextureView,
  2489. resolveTarget: gpuMSAATexture ? colorTextureView : undefined,
  2490. depthSlice,
  2491. clearValue: mustClearColor ? (isRTInteger ? clearColorForIntegerRT : clearColor) : undefined,
  2492. loadOp: mustClearColor ? WebGPUConstants.LoadOp.Clear : WebGPUConstants.LoadOp.Load,
  2493. storeOp: WebGPUConstants.StoreOp.Store,
  2494. });
  2495. }
  2496. else {
  2497. colorAttachments.push(null);
  2498. }
  2499. }
  2500. this._debugPushGroup?.("render target pass" + (renderTargetWrapper.label ? " (" + renderTargetWrapper.label + ")" : ""), 1);
  2501. this._rttRenderPassWrapper.renderPassDescriptor = {
  2502. label: (renderTargetWrapper.label ?? "RTT") + "RenderPass",
  2503. colorAttachments,
  2504. depthStencilAttachment: depthStencilTexture && gpuDepthStencilTexture
  2505. ? {
  2506. view: depthMSAATextureView ? depthMSAATextureView : depthTextureView,
  2507. depthClearValue: mustClearDepth ? (this.useReverseDepthBuffer ? this._clearReverseDepthValue : this._clearDepthValue) : undefined,
  2508. depthLoadOp: mustClearDepth ? WebGPUConstants.LoadOp.Clear : WebGPUConstants.LoadOp.Load,
  2509. depthStoreOp: WebGPUConstants.StoreOp.Store,
  2510. stencilClearValue: rtWrapper._depthStencilTextureWithStencil && mustClearStencil ? this._clearStencilValue : undefined,
  2511. stencilLoadOp: !depthTextureHasStencil
  2512. ? undefined
  2513. : rtWrapper._depthStencilTextureWithStencil && mustClearStencil
  2514. ? WebGPUConstants.LoadOp.Clear
  2515. : WebGPUConstants.LoadOp.Load,
  2516. stencilStoreOp: !depthTextureHasStencil ? undefined : WebGPUConstants.StoreOp.Store,
  2517. }
  2518. : undefined,
  2519. occlusionQuerySet: this._occlusionQuery?.hasQueries ? this._occlusionQuery.querySet : undefined,
  2520. };
  2521. this._timestampQuery.startPass(this._rttRenderPassWrapper.renderPassDescriptor, this._timestampIndex);
  2522. this._currentRenderPass = this._renderEncoder.beginRenderPass(this._rttRenderPassWrapper.renderPassDescriptor);
  2523. if (this.dbgVerboseLogsForFirstFrames) {
  2524. if (this._count === undefined) {
  2525. this._count = 0;
  2526. }
  2527. if (!this._count || this._count < this.dbgVerboseLogsNumFrames) {
  2528. const internalTexture = rtWrapper.texture;
  2529. Logger.Log([
  2530. "frame #" +
  2531. this._count +
  2532. " - render target begin pass - rtt name=" +
  2533. renderTargetWrapper.label +
  2534. ", internalTexture.uniqueId=" +
  2535. internalTexture.uniqueId +
  2536. ", width=" +
  2537. internalTexture.width +
  2538. ", height=" +
  2539. internalTexture.height +
  2540. ", setClearStates=" +
  2541. setClearStates,
  2542. "renderPassDescriptor=",
  2543. this._rttRenderPassWrapper.renderPassDescriptor,
  2544. ]);
  2545. }
  2546. }
  2547. this._debugFlushPendingCommands?.();
  2548. this._resetRenderPassStates();
  2549. if (!gpuDepthStencilWrapper || !WebGPUTextureHelper.HasStencilAspect(gpuDepthStencilWrapper.format)) {
  2550. this._stencilStateComposer.enabled = false;
  2551. }
  2552. }
  2553. _startMainRenderPass(setClearStates, clearColor, clearDepth, clearStencil) {
  2554. this._endCurrentRenderPass();
  2555. if (this.useReverseDepthBuffer) {
  2556. this.setDepthFunctionToGreaterOrEqual();
  2557. }
  2558. const mustClearColor = setClearStates && clearColor;
  2559. const mustClearDepth = setClearStates && clearDepth;
  2560. const mustClearStencil = setClearStates && clearStencil;
  2561. this._mainRenderPassWrapper.renderPassDescriptor.colorAttachments[0].clearValue = mustClearColor ? clearColor : undefined;
  2562. this._mainRenderPassWrapper.renderPassDescriptor.colorAttachments[0].loadOp = mustClearColor ? WebGPUConstants.LoadOp.Clear : WebGPUConstants.LoadOp.Load;
  2563. this._mainRenderPassWrapper.renderPassDescriptor.depthStencilAttachment.depthClearValue = mustClearDepth
  2564. ? this.useReverseDepthBuffer
  2565. ? this._clearReverseDepthValue
  2566. : this._clearDepthValue
  2567. : undefined;
  2568. this._mainRenderPassWrapper.renderPassDescriptor.depthStencilAttachment.depthLoadOp = mustClearDepth ? WebGPUConstants.LoadOp.Clear : WebGPUConstants.LoadOp.Load;
  2569. this._mainRenderPassWrapper.renderPassDescriptor.depthStencilAttachment.stencilClearValue = mustClearStencil ? this._clearStencilValue : undefined;
  2570. this._mainRenderPassWrapper.renderPassDescriptor.depthStencilAttachment.stencilLoadOp = !this.isStencilEnable
  2571. ? undefined
  2572. : mustClearStencil
  2573. ? WebGPUConstants.LoadOp.Clear
  2574. : WebGPUConstants.LoadOp.Load;
  2575. this._mainRenderPassWrapper.renderPassDescriptor.occlusionQuerySet = this._occlusionQuery?.hasQueries ? this._occlusionQuery.querySet : undefined;
  2576. const swapChainTexture = this._context.getCurrentTexture();
  2577. this._mainRenderPassWrapper.colorAttachmentGPUTextures[0].set(swapChainTexture);
  2578. // Resolve in case of MSAA
  2579. if (this._options.antialias) {
  2580. viewDescriptorSwapChainAntialiasing.format = swapChainTexture.format;
  2581. this._mainRenderPassWrapper.renderPassDescriptor.colorAttachments[0].resolveTarget = swapChainTexture.createView(viewDescriptorSwapChainAntialiasing);
  2582. }
  2583. else {
  2584. viewDescriptorSwapChain.format = swapChainTexture.format;
  2585. this._mainRenderPassWrapper.renderPassDescriptor.colorAttachments[0].view = swapChainTexture.createView(viewDescriptorSwapChain);
  2586. }
  2587. if (this.dbgVerboseLogsForFirstFrames) {
  2588. if (this._count === undefined) {
  2589. this._count = 0;
  2590. }
  2591. if (!this._count || this._count < this.dbgVerboseLogsNumFrames) {
  2592. Logger.Log([
  2593. "frame #" + this._count + " - main begin pass - texture width=" + this._mainTextureExtends.width,
  2594. " height=" + this._mainTextureExtends.height + ", setClearStates=" + setClearStates,
  2595. "renderPassDescriptor=",
  2596. this._mainRenderPassWrapper.renderPassDescriptor,
  2597. ]);
  2598. }
  2599. }
  2600. this._debugPushGroup?.("main pass", 0);
  2601. this._timestampQuery.startPass(this._mainRenderPassWrapper.renderPassDescriptor, this._timestampIndex);
  2602. this._currentRenderPass = this._renderEncoder.beginRenderPass(this._mainRenderPassWrapper.renderPassDescriptor);
  2603. this._setDepthTextureFormat(this._mainRenderPassWrapper);
  2604. this._setColorFormat(this._mainRenderPassWrapper);
  2605. this._debugFlushPendingCommands?.();
  2606. this._resetRenderPassStates();
  2607. if (!this._isStencilEnable) {
  2608. this._stencilStateComposer.enabled = false;
  2609. }
  2610. }
  2611. /** @internal */
  2612. _endCurrentRenderPass() {
  2613. if (!this._currentRenderPass) {
  2614. return 0;
  2615. }
  2616. const currentPassIndex = this._currentPassIsMainPass() ? 2 : 1;
  2617. if (!this._snapshotRendering.endRenderPass(this._currentRenderPass) && !this.compatibilityMode) {
  2618. this._bundleList.run(this._currentRenderPass);
  2619. this._bundleList.reset();
  2620. }
  2621. this._currentRenderPass.end();
  2622. this._timestampQuery.endPass(this._timestampIndex, (this._currentRenderTarget && this._currentRenderTarget.gpuTimeInFrame
  2623. ? this._currentRenderTarget.gpuTimeInFrame
  2624. : this.gpuTimeInFrameForMainPass));
  2625. this._timestampIndex += 2;
  2626. if (this.dbgVerboseLogsForFirstFrames) {
  2627. if (this._count === undefined) {
  2628. this._count = 0;
  2629. }
  2630. if (!this._count || this._count < this.dbgVerboseLogsNumFrames) {
  2631. Logger.Log("frame #" +
  2632. this._count +
  2633. " - " +
  2634. (currentPassIndex === 2 ? "main" : "render target") +
  2635. " end pass" +
  2636. (currentPassIndex === 1 ? " - internalTexture.uniqueId=" + this._currentRenderTarget?.texture?.uniqueId : ""));
  2637. }
  2638. }
  2639. this._debugPopGroup?.(0);
  2640. this._currentRenderPass = null;
  2641. return currentPassIndex;
  2642. }
  2643. /**
  2644. * Binds the frame buffer to the specified texture.
  2645. * @param texture The render target wrapper to render to
  2646. * @param faceIndex The face of the texture to render to in case of cube texture
  2647. * @param requiredWidth The width of the target to render to
  2648. * @param requiredHeight The height of the target to render to
  2649. * @param forceFullscreenViewport Forces the viewport to be the entire texture/screen if true
  2650. * @param lodLevel defines the lod level to bind to the frame buffer
  2651. * @param layer defines the 2d array index to bind to frame buffer to
  2652. */
  2653. bindFramebuffer(texture, faceIndex = 0, requiredWidth, requiredHeight, forceFullscreenViewport, lodLevel = 0, layer = 0) {
  2654. const hardwareTexture = texture.texture?._hardwareTexture;
  2655. if (this._currentRenderTarget) {
  2656. this.unBindFramebuffer(this._currentRenderTarget);
  2657. }
  2658. else {
  2659. this._endCurrentRenderPass();
  2660. }
  2661. this._currentRenderTarget = texture;
  2662. const depthStencilTexture = this._currentRenderTarget._depthStencilTexture;
  2663. this._rttRenderPassWrapper.colorAttachmentGPUTextures[0] = hardwareTexture;
  2664. this._rttRenderPassWrapper.depthTextureFormat = depthStencilTexture ? WebGPUTextureHelper.GetWebGPUTextureFormat(-1, depthStencilTexture.format) : undefined;
  2665. this._setDepthTextureFormat(this._rttRenderPassWrapper);
  2666. this._setColorFormat(this._rttRenderPassWrapper);
  2667. this._rttRenderPassWrapper.colorAttachmentViewDescriptor = {
  2668. format: this._colorFormat,
  2669. dimension: texture.is3D ? WebGPUConstants.TextureViewDimension.E3d : WebGPUConstants.TextureViewDimension.E2d,
  2670. mipLevelCount: 1,
  2671. baseArrayLayer: texture.isCube ? layer * 6 + faceIndex : layer,
  2672. baseMipLevel: lodLevel,
  2673. arrayLayerCount: 1,
  2674. aspect: WebGPUConstants.TextureAspect.All,
  2675. };
  2676. this._rttRenderPassWrapper.depthAttachmentViewDescriptor = {
  2677. format: this._depthTextureFormat,
  2678. dimension: depthStencilTexture && depthStencilTexture.is3D ? WebGPUConstants.TextureViewDimension.E3d : WebGPUConstants.TextureViewDimension.E2d,
  2679. mipLevelCount: 1,
  2680. baseArrayLayer: depthStencilTexture ? (depthStencilTexture.isCube ? layer * 6 + faceIndex : layer) : 0,
  2681. baseMipLevel: 0,
  2682. arrayLayerCount: 1,
  2683. aspect: WebGPUConstants.TextureAspect.All,
  2684. };
  2685. if (this.dbgVerboseLogsForFirstFrames) {
  2686. if (this._count === undefined) {
  2687. this._count = 0;
  2688. }
  2689. if (!this._count || this._count < this.dbgVerboseLogsNumFrames) {
  2690. Logger.Log([
  2691. "frame #" +
  2692. this._count +
  2693. " - bindFramebuffer - rtt name=" +
  2694. texture.label +
  2695. ", internalTexture.uniqueId=" +
  2696. texture.texture?.uniqueId +
  2697. ", face=" +
  2698. faceIndex +
  2699. ", lodLevel=" +
  2700. lodLevel +
  2701. ", layer=" +
  2702. layer,
  2703. "colorAttachmentViewDescriptor=",
  2704. this._rttRenderPassWrapper.colorAttachmentViewDescriptor,
  2705. "depthAttachmentViewDescriptor=",
  2706. this._rttRenderPassWrapper.depthAttachmentViewDescriptor,
  2707. ]);
  2708. }
  2709. }
  2710. // We don't create the render pass just now, we do a lazy creation of the render pass, hoping the render pass will be created by a call to clear()...
  2711. if (this._cachedViewport && !forceFullscreenViewport) {
  2712. this.setViewport(this._cachedViewport, requiredWidth, requiredHeight);
  2713. }
  2714. else {
  2715. if (!requiredWidth) {
  2716. requiredWidth = texture.width;
  2717. if (lodLevel) {
  2718. requiredWidth = requiredWidth / Math.pow(2, lodLevel);
  2719. }
  2720. }
  2721. if (!requiredHeight) {
  2722. requiredHeight = texture.height;
  2723. if (lodLevel) {
  2724. requiredHeight = requiredHeight / Math.pow(2, lodLevel);
  2725. }
  2726. }
  2727. this._viewport(0, 0, requiredWidth, requiredHeight);
  2728. }
  2729. this.wipeCaches();
  2730. }
  2731. /**
  2732. * Unbind the current render target texture from the WebGPU context
  2733. * @param texture defines the render target wrapper to unbind
  2734. * @param disableGenerateMipMaps defines a boolean indicating that mipmaps must not be generated
  2735. * @param onBeforeUnbind defines a function which will be called before the effective unbind
  2736. */
  2737. unBindFramebuffer(texture, disableGenerateMipMaps = false, onBeforeUnbind) {
  2738. const saveCRT = this._currentRenderTarget;
  2739. this._currentRenderTarget = null; // to be iso with abstractEngine, this._currentRenderTarget must be null when onBeforeUnbind is called
  2740. if (onBeforeUnbind) {
  2741. onBeforeUnbind();
  2742. }
  2743. this._currentRenderTarget = saveCRT;
  2744. this._endCurrentRenderPass();
  2745. if (texture.texture?.generateMipMaps && !disableGenerateMipMaps && !texture.isCube) {
  2746. this._generateMipmaps(texture.texture);
  2747. }
  2748. this._currentRenderTarget = null;
  2749. if (this.dbgVerboseLogsForFirstFrames) {
  2750. if (this._count === undefined) {
  2751. this._count = 0;
  2752. }
  2753. if (!this._count || this._count < this.dbgVerboseLogsNumFrames) {
  2754. Logger.Log("frame #" + this._count + " - unBindFramebuffer - rtt name=" + texture.label + ", internalTexture.uniqueId=", texture.texture?.uniqueId);
  2755. }
  2756. }
  2757. this._mrtAttachments = [];
  2758. this._cacheRenderPipeline.setMRT([]);
  2759. this._cacheRenderPipeline.setMRTAttachments(this._mrtAttachments);
  2760. }
  2761. /**
  2762. * Unbind the current render target and bind the default framebuffer
  2763. */
  2764. restoreDefaultFramebuffer() {
  2765. if (this._currentRenderTarget) {
  2766. this.unBindFramebuffer(this._currentRenderTarget);
  2767. }
  2768. else if (!this._currentRenderPass) {
  2769. this._startMainRenderPass(false);
  2770. }
  2771. if (this._cachedViewport) {
  2772. this.setViewport(this._cachedViewport);
  2773. }
  2774. this.wipeCaches();
  2775. }
  2776. //------------------------------------------------------------------------------
  2777. // Render
  2778. //------------------------------------------------------------------------------
  2779. /**
  2780. * @internal
  2781. */
  2782. _setColorFormat(wrapper) {
  2783. const format = wrapper.colorAttachmentGPUTextures[0]?.format ?? null;
  2784. this._cacheRenderPipeline.setColorFormat(format);
  2785. if (this._colorFormat === format) {
  2786. return;
  2787. }
  2788. this._colorFormat = format;
  2789. }
  2790. /**
  2791. * @internal
  2792. */
  2793. _setDepthTextureFormat(wrapper) {
  2794. this._cacheRenderPipeline.setDepthStencilFormat(wrapper.depthTextureFormat);
  2795. if (this._depthTextureFormat === wrapper.depthTextureFormat) {
  2796. return;
  2797. }
  2798. this._depthTextureFormat = wrapper.depthTextureFormat;
  2799. }
  2800. setDitheringState() {
  2801. // Does not exist in WebGPU
  2802. }
  2803. setRasterizerState() {
  2804. // Does not exist in WebGPU
  2805. }
  2806. /**
  2807. * @internal
  2808. */
  2809. _executeWhenRenderingStateIsCompiled(pipelineContext, action) {
  2810. // No parallel shader compilation.
  2811. // No Async, so direct launch
  2812. action();
  2813. }
  2814. /**
  2815. * @internal
  2816. */
  2817. bindSamplers() { }
  2818. /** @internal */
  2819. _getUnpackAlignement() {
  2820. return 1;
  2821. }
  2822. /**
  2823. * @internal
  2824. */
  2825. _bindTextureDirectly() {
  2826. return false;
  2827. }
  2828. /**
  2829. * Set various states to the webGL context
  2830. * @param culling defines culling state: true to enable culling, false to disable it
  2831. * @param zOffset defines the value to apply to zOffset (0 by default)
  2832. * @param force defines if states must be applied even if cache is up to date
  2833. * @param reverseSide defines if culling must be reversed (CCW if false, CW if true)
  2834. * @param cullBackFaces true to cull back faces, false to cull front faces (if culling is enabled)
  2835. * @param stencil stencil states to set
  2836. * @param zOffsetUnits defines the value to apply to zOffsetUnits (0 by default)
  2837. */
  2838. setState(culling, zOffset = 0, force, reverseSide = false, cullBackFaces, stencil, zOffsetUnits = 0) {
  2839. // Culling
  2840. if (this._depthCullingState.cull !== culling || force) {
  2841. this._depthCullingState.cull = culling;
  2842. }
  2843. // Cull face
  2844. const cullFace = this.cullBackFaces ?? cullBackFaces ?? true ? 1 : 2;
  2845. if (this._depthCullingState.cullFace !== cullFace || force) {
  2846. this._depthCullingState.cullFace = cullFace;
  2847. }
  2848. // Z offset
  2849. this.setZOffset(zOffset);
  2850. this.setZOffsetUnits(zOffsetUnits);
  2851. // Front face
  2852. const frontFace = reverseSide ? (this._currentRenderTarget ? 1 : 2) : this._currentRenderTarget ? 2 : 1;
  2853. if (this._depthCullingState.frontFace !== frontFace || force) {
  2854. this._depthCullingState.frontFace = frontFace;
  2855. }
  2856. this._stencilStateComposer.stencilMaterial = stencil;
  2857. }
  2858. _applyRenderPassChanges(bundleList) {
  2859. const mustUpdateStencilRef = !this._stencilStateComposer.enabled ? false : this._mustUpdateStencilRef();
  2860. const mustUpdateBlendColor = !this._alphaState.alphaBlend ? false : this._mustUpdateBlendColor();
  2861. if (this._mustUpdateViewport()) {
  2862. this._applyViewport(bundleList);
  2863. }
  2864. if (this._mustUpdateScissor()) {
  2865. this._applyScissor(bundleList);
  2866. }
  2867. if (mustUpdateStencilRef) {
  2868. this._applyStencilRef(bundleList);
  2869. }
  2870. if (mustUpdateBlendColor) {
  2871. this._applyBlendColor(bundleList);
  2872. }
  2873. }
  2874. _draw(drawType, fillMode, start, count, instancesCount) {
  2875. const renderPass = this._getCurrentRenderPass();
  2876. const bundleList = this._bundleList;
  2877. this.applyStates();
  2878. const webgpuPipelineContext = this._currentEffect._pipelineContext;
  2879. this.bindUniformBufferBase(this._currentRenderTarget ? this._ubInvertY : this._ubDontInvertY, 0, WebGPUShaderProcessor.InternalsUBOName);
  2880. if (webgpuPipelineContext.uniformBuffer) {
  2881. webgpuPipelineContext.uniformBuffer.update();
  2882. this.bindUniformBufferBase(webgpuPipelineContext.uniformBuffer.getBuffer(), 0, WebGPUShaderProcessor.LeftOvertUBOName);
  2883. }
  2884. if (this._snapshotRendering.play) {
  2885. this._reportDrawCall();
  2886. return;
  2887. }
  2888. if (!this.compatibilityMode &&
  2889. (this._currentDrawContext.isDirty(this._currentMaterialContext.updateId) || this._currentMaterialContext.isDirty || this._currentMaterialContext.forceBindGroupCreation)) {
  2890. this._currentDrawContext.fastBundle = undefined;
  2891. }
  2892. const useFastPath = !this.compatibilityMode && this._currentDrawContext.fastBundle;
  2893. let renderPass2 = renderPass;
  2894. if (useFastPath || this._snapshotRendering.record) {
  2895. this._applyRenderPassChanges(bundleList);
  2896. if (!this._snapshotRendering.record) {
  2897. this._counters.numBundleReuseNonCompatMode++;
  2898. if (this._currentDrawContext.indirectDrawBuffer) {
  2899. this._currentDrawContext.setIndirectData(count, instancesCount || 1, start);
  2900. }
  2901. bundleList.addBundle(this._currentDrawContext.fastBundle);
  2902. this._reportDrawCall();
  2903. return;
  2904. }
  2905. renderPass2 = bundleList.getBundleEncoder(this._cacheRenderPipeline.colorFormats, this._depthTextureFormat, this.currentSampleCount); // for snapshot recording mode
  2906. bundleList.numDrawCalls++;
  2907. }
  2908. let textureState = 0;
  2909. if (this._currentMaterialContext.hasFloatOrDepthTextures) {
  2910. let bitVal = 1;
  2911. for (let i = 0; i < webgpuPipelineContext.shaderProcessingContext.textureNames.length; ++i) {
  2912. const textureName = webgpuPipelineContext.shaderProcessingContext.textureNames[i];
  2913. const texture = this._currentMaterialContext.textures[textureName]?.texture;
  2914. const textureIsDepth = texture && texture.format >= 13 && texture.format <= 18;
  2915. if ((texture?.type === 1 && !this._caps.textureFloatLinearFiltering) || textureIsDepth) {
  2916. textureState |= bitVal;
  2917. }
  2918. bitVal = bitVal << 1;
  2919. }
  2920. }
  2921. this._currentMaterialContext.textureState = textureState;
  2922. const pipeline = this._cacheRenderPipeline.getRenderPipeline(fillMode, this._currentEffect, this.currentSampleCount, textureState);
  2923. const bindGroups = this._cacheBindGroups.getBindGroups(webgpuPipelineContext, this._currentDrawContext, this._currentMaterialContext);
  2924. if (!this._snapshotRendering.record) {
  2925. this._applyRenderPassChanges(!this.compatibilityMode ? bundleList : null);
  2926. if (!this.compatibilityMode) {
  2927. this._counters.numBundleCreationNonCompatMode++;
  2928. renderPass2 = this._device.createRenderBundleEncoder({
  2929. colorFormats: this._cacheRenderPipeline.colorFormats,
  2930. depthStencilFormat: this._depthTextureFormat,
  2931. sampleCount: WebGPUTextureHelper.GetSample(this.currentSampleCount),
  2932. });
  2933. }
  2934. }
  2935. // bind pipeline
  2936. renderPass2.setPipeline(pipeline);
  2937. // bind index/vertex buffers
  2938. if (this._currentIndexBuffer) {
  2939. renderPass2.setIndexBuffer(this._currentIndexBuffer.underlyingResource, this._currentIndexBuffer.is32Bits ? WebGPUConstants.IndexFormat.Uint32 : WebGPUConstants.IndexFormat.Uint16, 0);
  2940. }
  2941. const vertexBuffers = this._cacheRenderPipeline.vertexBuffers;
  2942. for (let index = 0; index < vertexBuffers.length; index++) {
  2943. const vertexBuffer = vertexBuffers[index];
  2944. const buffer = vertexBuffer.effectiveBuffer;
  2945. if (buffer) {
  2946. renderPass2.setVertexBuffer(index, buffer.underlyingResource, vertexBuffer._validOffsetRange ? 0 : vertexBuffer.byteOffset);
  2947. }
  2948. }
  2949. // bind bind groups
  2950. for (let i = 0; i < bindGroups.length; i++) {
  2951. renderPass2.setBindGroup(i, bindGroups[i]);
  2952. }
  2953. // draw
  2954. const nonCompatMode = !this.compatibilityMode && !this._snapshotRendering.record;
  2955. if (nonCompatMode && this._currentDrawContext.indirectDrawBuffer) {
  2956. this._currentDrawContext.setIndirectData(count, instancesCount || 1, start);
  2957. if (drawType === 0) {
  2958. renderPass2.drawIndexedIndirect(this._currentDrawContext.indirectDrawBuffer, 0);
  2959. }
  2960. else {
  2961. renderPass2.drawIndirect(this._currentDrawContext.indirectDrawBuffer, 0);
  2962. }
  2963. }
  2964. else if (drawType === 0) {
  2965. renderPass2.drawIndexed(count, instancesCount || 1, start, 0, 0);
  2966. }
  2967. else {
  2968. renderPass2.draw(count, instancesCount || 1, start, 0);
  2969. }
  2970. if (nonCompatMode) {
  2971. this._currentDrawContext.fastBundle = renderPass2.finish();
  2972. bundleList.addBundle(this._currentDrawContext.fastBundle);
  2973. }
  2974. this._reportDrawCall();
  2975. }
  2976. /**
  2977. * Draw a list of indexed primitives
  2978. * @param fillMode defines the primitive to use
  2979. * @param indexStart defines the starting index
  2980. * @param indexCount defines the number of index to draw
  2981. * @param instancesCount defines the number of instances to draw (if instantiation is enabled)
  2982. */
  2983. drawElementsType(fillMode, indexStart, indexCount, instancesCount = 1) {
  2984. this._draw(0, fillMode, indexStart, indexCount, instancesCount);
  2985. }
  2986. /**
  2987. * Draw a list of unindexed primitives
  2988. * @param fillMode defines the primitive to use
  2989. * @param verticesStart defines the index of first vertex to draw
  2990. * @param verticesCount defines the count of vertices to draw
  2991. * @param instancesCount defines the number of instances to draw (if instantiation is enabled)
  2992. */
  2993. drawArraysType(fillMode, verticesStart, verticesCount, instancesCount = 1) {
  2994. this._currentIndexBuffer = null;
  2995. this._draw(1, fillMode, verticesStart, verticesCount, instancesCount);
  2996. }
  2997. //------------------------------------------------------------------------------
  2998. // Dispose
  2999. //------------------------------------------------------------------------------
  3000. /**
  3001. * Dispose and release all associated resources
  3002. */
  3003. dispose() {
  3004. this._isDisposed = true;
  3005. this._timestampQuery.dispose();
  3006. this._mainTexture?.destroy();
  3007. this._depthTexture?.destroy();
  3008. this._textureHelper.destroyDeferredTextures();
  3009. this._bufferManager.destroyDeferredBuffers();
  3010. this._device.destroy();
  3011. _CommonDispose(this, this._renderingCanvas);
  3012. super.dispose();
  3013. }
  3014. //------------------------------------------------------------------------------
  3015. // Misc
  3016. //------------------------------------------------------------------------------
  3017. /**
  3018. * Gets the current render width
  3019. * @param useScreen defines if screen size must be used (or the current render target if any)
  3020. * @returns a number defining the current render width
  3021. */
  3022. getRenderWidth(useScreen = false) {
  3023. if (!useScreen && this._currentRenderTarget) {
  3024. return this._currentRenderTarget.width;
  3025. }
  3026. return this._renderingCanvas?.width ?? 0;
  3027. }
  3028. /**
  3029. * Gets the current render height
  3030. * @param useScreen defines if screen size must be used (or the current render target if any)
  3031. * @returns a number defining the current render height
  3032. */
  3033. getRenderHeight(useScreen = false) {
  3034. if (!useScreen && this._currentRenderTarget) {
  3035. return this._currentRenderTarget.height;
  3036. }
  3037. return this._renderingCanvas?.height ?? 0;
  3038. }
  3039. //------------------------------------------------------------------------------
  3040. // Errors
  3041. //------------------------------------------------------------------------------
  3042. /**
  3043. * Get the current error code of the WebGPU context
  3044. * @returns the error code
  3045. */
  3046. getError() {
  3047. // TODO WEBGPU. from the webgpu errors.
  3048. return 0;
  3049. }
  3050. }
  3051. // Default glslang options.
  3052. WebGPUEngine._GLSLslangDefaultOptions = {
  3053. jsPath: `${Tools._DefaultCdnUrl}/glslang/glslang.js`,
  3054. wasmPath: `${Tools._DefaultCdnUrl}/glslang/glslang.wasm`,
  3055. };
  3056. WebGPUEngine._InstanceId = 0;
  3057. /** true to enable using TintWASM to convert Spir-V to WGSL */
  3058. WebGPUEngine.UseTWGSL = true;
  3059. //# sourceMappingURL=webgpuEngine.js.map