abstractEngine.js 51 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354
  1. import { EngineStore } from "./engineStore.js";
  2. import { Logger } from "../Misc/logger.js";
  3. import { Effect } from "../Materials/effect.js";
  4. import { PerformanceConfigurator } from "./performanceConfigurator.js";
  5. import { PrecisionDate } from "../Misc/precisionDate.js";
  6. import { DepthCullingState } from "../States/depthCullingState.js";
  7. import { StencilStateComposer } from "../States/stencilStateComposer.js";
  8. import { StencilState } from "../States/stencilState.js";
  9. import { AlphaState } from "../States/alphaCullingState.js";
  10. import { _WarnImport } from "../Misc/devTools.js";
  11. import { InternalTexture, InternalTextureSource } from "../Materials/Textures/internalTexture.js";
  12. import { IsDocumentAvailable, IsNavigatorAvailable, IsWindowObjectExist } from "../Misc/domManagement.js";
  13. import { Observable } from "../Misc/observable.js";
  14. /**
  15. * Queue a new function into the requested animation frame pool (ie. this function will be executed by the browser (or the javascript engine) for the next frame)
  16. * @param func - the function to be called
  17. * @param requester - the object that will request the next frame. Falls back to window.
  18. * @returns frame number
  19. */
  20. export function QueueNewFrame(func, requester) {
  21. // Note that there is kind of a typing issue here, as `setTimeout` might return something else than a number (NodeJs returns a NodeJS.Timeout object).
  22. // Also if the global `requestAnimationFrame`'s returnType is number, `requester.requestPostAnimationFrame` and `requester.requestAnimationFrame` types
  23. // are `any`.
  24. if (!IsWindowObjectExist()) {
  25. if (typeof requestAnimationFrame === "function") {
  26. return requestAnimationFrame(func);
  27. }
  28. }
  29. else {
  30. const { requestAnimationFrame } = requester || window;
  31. if (typeof requestAnimationFrame === "function") {
  32. return requestAnimationFrame(func);
  33. }
  34. }
  35. // fallback to the global `setTimeout`.
  36. // In most cases (aka in the browser), `window` is the global object, so instead of calling `window.setTimeout` we could call the global `setTimeout`.
  37. return setTimeout(func, 16);
  38. }
  39. /**
  40. * The parent class for specialized engines (WebGL, WebGPU)
  41. */
  42. export class AbstractEngine {
  43. /**
  44. * Gets the current frame id
  45. */
  46. get frameId() {
  47. return this._frameId;
  48. }
  49. /**
  50. * Gets a boolean indicating if the engine runs in WebGPU or not.
  51. */
  52. get isWebGPU() {
  53. return this._isWebGPU;
  54. }
  55. /**
  56. * @internal
  57. */
  58. _getShaderProcessor(shaderLanguage) {
  59. return this._shaderProcessor;
  60. }
  61. /**
  62. * Gets the shader platform name used by the effects.
  63. */
  64. get shaderPlatformName() {
  65. return this._shaderPlatformName;
  66. }
  67. _clearEmptyResources() {
  68. this._emptyTexture = null;
  69. this._emptyCubeTexture = null;
  70. this._emptyTexture3D = null;
  71. this._emptyTexture2DArray = null;
  72. }
  73. /**
  74. * Gets or sets a boolean indicating if depth buffer should be reverse, going from far to near.
  75. * This can provide greater z depth for distant objects.
  76. */
  77. get useReverseDepthBuffer() {
  78. return this._useReverseDepthBuffer;
  79. }
  80. set useReverseDepthBuffer(useReverse) {
  81. if (useReverse === this._useReverseDepthBuffer) {
  82. return;
  83. }
  84. this._useReverseDepthBuffer = useReverse;
  85. if (useReverse) {
  86. this._depthCullingState.depthFunc = 518;
  87. }
  88. else {
  89. this._depthCullingState.depthFunc = 515;
  90. }
  91. }
  92. /**
  93. * Enable or disable color writing
  94. * @param enable defines the state to set
  95. */
  96. setColorWrite(enable) {
  97. if (enable !== this._colorWrite) {
  98. this._colorWriteChanged = true;
  99. this._colorWrite = enable;
  100. }
  101. }
  102. /**
  103. * Gets a boolean indicating if color writing is enabled
  104. * @returns the current color writing state
  105. */
  106. getColorWrite() {
  107. return this._colorWrite;
  108. }
  109. /**
  110. * Gets the depth culling state manager
  111. */
  112. get depthCullingState() {
  113. return this._depthCullingState;
  114. }
  115. /**
  116. * Gets the alpha state manager
  117. */
  118. get alphaState() {
  119. return this._alphaState;
  120. }
  121. /**
  122. * Gets the stencil state manager
  123. */
  124. get stencilState() {
  125. return this._stencilState;
  126. }
  127. /**
  128. * Gets the stencil state composer
  129. */
  130. get stencilStateComposer() {
  131. return this._stencilStateComposer;
  132. }
  133. /** @internal */
  134. _getGlobalDefines(defines) {
  135. if (defines) {
  136. if (this.isNDCHalfZRange) {
  137. defines["IS_NDC_HALF_ZRANGE"] = "";
  138. }
  139. else {
  140. delete defines["IS_NDC_HALF_ZRANGE"];
  141. }
  142. if (this.useReverseDepthBuffer) {
  143. defines["USE_REVERSE_DEPTHBUFFER"] = "";
  144. }
  145. else {
  146. delete defines["USE_REVERSE_DEPTHBUFFER"];
  147. }
  148. if (this.useExactSrgbConversions) {
  149. defines["USE_EXACT_SRGB_CONVERSIONS"] = "";
  150. }
  151. else {
  152. delete defines["USE_EXACT_SRGB_CONVERSIONS"];
  153. }
  154. return;
  155. }
  156. else {
  157. let s = "";
  158. if (this.isNDCHalfZRange) {
  159. s += "#define IS_NDC_HALF_ZRANGE";
  160. }
  161. if (this.useReverseDepthBuffer) {
  162. if (s) {
  163. s += "\n";
  164. }
  165. s += "#define USE_REVERSE_DEPTHBUFFER";
  166. }
  167. if (this.useExactSrgbConversions) {
  168. if (s) {
  169. s += "\n";
  170. }
  171. s += "#define USE_EXACT_SRGB_CONVERSIONS";
  172. }
  173. return s;
  174. }
  175. }
  176. _rebuildInternalTextures() {
  177. const currentState = this._internalTexturesCache.slice(); // Do a copy because the rebuild will add proxies
  178. for (const internalTexture of currentState) {
  179. internalTexture._rebuild();
  180. }
  181. }
  182. _rebuildRenderTargetWrappers() {
  183. const currentState = this._renderTargetWrapperCache.slice(); // Do a copy because the rebuild will add proxies
  184. for (const renderTargetWrapper of currentState) {
  185. renderTargetWrapper._rebuild();
  186. }
  187. }
  188. _rebuildEffects() {
  189. for (const key in this._compiledEffects) {
  190. const effect = this._compiledEffects[key];
  191. effect._pipelineContext = null; // because _prepareEffect will try to dispose this pipeline before recreating it and that would lead to webgl errors
  192. effect._prepareEffect();
  193. }
  194. Effect.ResetCache();
  195. }
  196. _rebuildGraphicsResources() {
  197. // Ensure webgl and engine states are matching
  198. this.wipeCaches(true);
  199. // Rebuild effects
  200. this._rebuildEffects();
  201. this._rebuildComputeEffects?.();
  202. // Note:
  203. // The call to _rebuildBuffers must be made before the call to _rebuildInternalTextures because in the process of _rebuildBuffers the buffers used by the post process managers will be rebuilt
  204. // and we may need to use the post process manager of the scene during _rebuildInternalTextures (in WebGL1, non-POT textures are rescaled using a post process + post process manager of the scene)
  205. // Rebuild buffers
  206. this._rebuildBuffers();
  207. // Rebuild textures
  208. this._rebuildInternalTextures();
  209. // Rebuild textures
  210. this._rebuildTextures();
  211. // Rebuild textures
  212. this._rebuildRenderTargetWrappers();
  213. // Reset engine states after all the buffer/textures/... have been rebuilt
  214. this.wipeCaches(true);
  215. }
  216. _flagContextRestored() {
  217. Logger.Warn(this.name + " context successfully restored.");
  218. this.onContextRestoredObservable.notifyObservers(this);
  219. this._contextWasLost = false;
  220. }
  221. _restoreEngineAfterContextLost(initEngine) {
  222. // Adding a timeout to avoid race condition at browser level
  223. setTimeout(async () => {
  224. this._clearEmptyResources();
  225. const depthTest = this._depthCullingState.depthTest; // backup those values because the call to initEngine / wipeCaches will reset them
  226. const depthFunc = this._depthCullingState.depthFunc;
  227. const depthMask = this._depthCullingState.depthMask;
  228. const stencilTest = this._stencilState.stencilTest;
  229. // Rebuild context
  230. await initEngine();
  231. this._rebuildGraphicsResources();
  232. this._depthCullingState.depthTest = depthTest;
  233. this._depthCullingState.depthFunc = depthFunc;
  234. this._depthCullingState.depthMask = depthMask;
  235. this._stencilState.stencilTest = stencilTest;
  236. this._flagContextRestored();
  237. }, 0);
  238. }
  239. /** Gets a boolean indicating if the engine was disposed */
  240. get isDisposed() {
  241. return this._isDisposed;
  242. }
  243. /**
  244. * Enables or disables the snapshot rendering mode
  245. * Note that the WebGL engine does not support snapshot rendering so setting the value won't have any effect for this engine
  246. */
  247. get snapshotRendering() {
  248. return false;
  249. }
  250. set snapshotRendering(activate) {
  251. // Do nothing
  252. }
  253. /**
  254. * Gets or sets the snapshot rendering mode
  255. */
  256. get snapshotRenderingMode() {
  257. return 0;
  258. }
  259. set snapshotRenderingMode(mode) { }
  260. /**
  261. * Returns the string "AbstractEngine"
  262. * @returns "AbstractEngine"
  263. */
  264. getClassName() {
  265. return "AbstractEngine";
  266. }
  267. /**
  268. * Gets the default empty texture
  269. */
  270. get emptyTexture() {
  271. if (!this._emptyTexture) {
  272. this._emptyTexture = this.createRawTexture(new Uint8Array(4), 1, 1, 5, false, false, 1);
  273. }
  274. return this._emptyTexture;
  275. }
  276. /**
  277. * Gets the default empty 3D texture
  278. */
  279. get emptyTexture3D() {
  280. if (!this._emptyTexture3D) {
  281. this._emptyTexture3D = this.createRawTexture3D(new Uint8Array(4), 1, 1, 1, 5, false, false, 1);
  282. }
  283. return this._emptyTexture3D;
  284. }
  285. /**
  286. * Gets the default empty 2D array texture
  287. */
  288. get emptyTexture2DArray() {
  289. if (!this._emptyTexture2DArray) {
  290. this._emptyTexture2DArray = this.createRawTexture2DArray(new Uint8Array(4), 1, 1, 1, 5, false, false, 1);
  291. }
  292. return this._emptyTexture2DArray;
  293. }
  294. /**
  295. * Gets the default empty cube texture
  296. */
  297. get emptyCubeTexture() {
  298. if (!this._emptyCubeTexture) {
  299. const faceData = new Uint8Array(4);
  300. const cubeData = [faceData, faceData, faceData, faceData, faceData, faceData];
  301. this._emptyCubeTexture = this.createRawCubeTexture(cubeData, 1, 5, 0, false, false, 1);
  302. }
  303. return this._emptyCubeTexture;
  304. }
  305. /**
  306. * Gets the list of current active render loop functions
  307. * @returns a read only array with the current render loop functions
  308. */
  309. get activeRenderLoops() {
  310. return this._activeRenderLoops;
  311. }
  312. /**
  313. * stop executing a render loop function and remove it from the execution array
  314. * @param renderFunction defines the function to be removed. If not provided all functions will be removed.
  315. */
  316. stopRenderLoop(renderFunction) {
  317. if (!renderFunction) {
  318. this._activeRenderLoops.length = 0;
  319. this._cancelFrame();
  320. return;
  321. }
  322. const index = this._activeRenderLoops.indexOf(renderFunction);
  323. if (index >= 0) {
  324. this._activeRenderLoops.splice(index, 1);
  325. if (this._activeRenderLoops.length == 0) {
  326. this._cancelFrame();
  327. }
  328. }
  329. }
  330. _cancelFrame() {
  331. if (this._frameHandler !== 0) {
  332. const handlerToCancel = this._frameHandler;
  333. this._frameHandler = 0;
  334. if (!IsWindowObjectExist()) {
  335. if (typeof cancelAnimationFrame === "function") {
  336. return cancelAnimationFrame(handlerToCancel);
  337. }
  338. }
  339. else {
  340. const { cancelAnimationFrame } = this.getHostWindow() || window;
  341. if (typeof cancelAnimationFrame === "function") {
  342. return cancelAnimationFrame(handlerToCancel);
  343. }
  344. }
  345. return clearTimeout(handlerToCancel);
  346. }
  347. }
  348. /**
  349. * Begin a new frame
  350. */
  351. beginFrame() {
  352. this.onBeginFrameObservable.notifyObservers(this);
  353. }
  354. /**
  355. * End the current frame
  356. */
  357. endFrame() {
  358. this._frameId++;
  359. this.onEndFrameObservable.notifyObservers(this);
  360. }
  361. /** @internal */
  362. _renderLoop() {
  363. this._frameHandler = 0;
  364. if (!this._contextWasLost) {
  365. let shouldRender = true;
  366. if (this._isDisposed || (!this.renderEvenInBackground && this._windowIsBackground)) {
  367. shouldRender = false;
  368. }
  369. if (shouldRender) {
  370. // Start new frame
  371. this.beginFrame();
  372. for (let index = 0; index < this._activeRenderLoops.length; index++) {
  373. const renderFunction = this._activeRenderLoops[index];
  374. renderFunction();
  375. }
  376. // Present
  377. this.endFrame();
  378. }
  379. }
  380. if (this._frameHandler === 0) {
  381. this._frameHandler = this._queueNewFrame(this._boundRenderFunction, this.getHostWindow());
  382. }
  383. }
  384. /**
  385. * Can be used to override the current requestAnimationFrame requester.
  386. * @internal
  387. */
  388. _queueNewFrame(bindedRenderFunction, requester) {
  389. return QueueNewFrame(bindedRenderFunction, requester);
  390. }
  391. /**
  392. * Register and execute a render loop. The engine can have more than one render function
  393. * @param renderFunction defines the function to continuously execute
  394. */
  395. runRenderLoop(renderFunction) {
  396. if (this._activeRenderLoops.indexOf(renderFunction) !== -1) {
  397. return;
  398. }
  399. this._activeRenderLoops.push(renderFunction);
  400. // On the first added function, start the render loop.
  401. if (this._activeRenderLoops.length === 1 && this._frameHandler === 0) {
  402. this._frameHandler = this._queueNewFrame(this._boundRenderFunction, this.getHostWindow());
  403. }
  404. }
  405. /**
  406. * Gets a boolean indicating if depth testing is enabled
  407. * @returns the current state
  408. */
  409. getDepthBuffer() {
  410. return this._depthCullingState.depthTest;
  411. }
  412. /**
  413. * Enable or disable depth buffering
  414. * @param enable defines the state to set
  415. */
  416. setDepthBuffer(enable) {
  417. this._depthCullingState.depthTest = enable;
  418. }
  419. /**
  420. * Set the z offset Factor to apply to current rendering
  421. * @param value defines the offset to apply
  422. */
  423. setZOffset(value) {
  424. this._depthCullingState.zOffset = this.useReverseDepthBuffer ? -value : value;
  425. }
  426. /**
  427. * Gets the current value of the zOffset Factor
  428. * @returns the current zOffset Factor state
  429. */
  430. getZOffset() {
  431. const zOffset = this._depthCullingState.zOffset;
  432. return this.useReverseDepthBuffer ? -zOffset : zOffset;
  433. }
  434. /**
  435. * Set the z offset Units to apply to current rendering
  436. * @param value defines the offset to apply
  437. */
  438. setZOffsetUnits(value) {
  439. this._depthCullingState.zOffsetUnits = this.useReverseDepthBuffer ? -value : value;
  440. }
  441. /**
  442. * Gets the current value of the zOffset Units
  443. * @returns the current zOffset Units state
  444. */
  445. getZOffsetUnits() {
  446. const zOffsetUnits = this._depthCullingState.zOffsetUnits;
  447. return this.useReverseDepthBuffer ? -zOffsetUnits : zOffsetUnits;
  448. }
  449. /**
  450. * Gets host window
  451. * @returns the host window object
  452. */
  453. getHostWindow() {
  454. if (!IsWindowObjectExist()) {
  455. return null;
  456. }
  457. if (this._renderingCanvas && this._renderingCanvas.ownerDocument && this._renderingCanvas.ownerDocument.defaultView) {
  458. return this._renderingCanvas.ownerDocument.defaultView;
  459. }
  460. return window;
  461. }
  462. /**
  463. * (WebGPU only) True (default) to be in compatibility mode, meaning rendering all existing scenes without artifacts (same rendering than WebGL).
  464. * Setting the property to false will improve performances but may not work in some scenes if some precautions are not taken.
  465. * See https://doc.babylonjs.com/setup/support/webGPU/webGPUOptimization/webGPUNonCompatibilityMode for more details
  466. */
  467. get compatibilityMode() {
  468. return this._compatibilityMode;
  469. }
  470. set compatibilityMode(mode) {
  471. // not supported in WebGL
  472. this._compatibilityMode = true;
  473. }
  474. _rebuildTextures() {
  475. for (const scene of this.scenes) {
  476. scene._rebuildTextures();
  477. }
  478. for (const scene of this._virtualScenes) {
  479. scene._rebuildTextures();
  480. }
  481. }
  482. /**
  483. * @internal
  484. */
  485. _releaseRenderTargetWrapper(rtWrapper) {
  486. const index = this._renderTargetWrapperCache.indexOf(rtWrapper);
  487. if (index !== -1) {
  488. this._renderTargetWrapperCache.splice(index, 1);
  489. }
  490. }
  491. /**
  492. * Gets the current viewport
  493. */
  494. get currentViewport() {
  495. return this._cachedViewport;
  496. }
  497. /**
  498. * Set the WebGL's viewport
  499. * @param viewport defines the viewport element to be used
  500. * @param requiredWidth defines the width required for rendering. If not provided the rendering canvas' width is used
  501. * @param requiredHeight defines the height required for rendering. If not provided the rendering canvas' height is used
  502. */
  503. setViewport(viewport, requiredWidth, requiredHeight) {
  504. const width = requiredWidth || this.getRenderWidth();
  505. const height = requiredHeight || this.getRenderHeight();
  506. const x = viewport.x || 0;
  507. const y = viewport.y || 0;
  508. this._cachedViewport = viewport;
  509. this._viewport(x * width, y * height, width * viewport.width, height * viewport.height);
  510. }
  511. /**
  512. * Create an image to use with canvas
  513. * @returns IImage interface
  514. */
  515. createCanvasImage() {
  516. return document.createElement("img");
  517. }
  518. /**
  519. * Returns a string describing the current engine
  520. */
  521. get description() {
  522. let description = this.name + this.version;
  523. if (this._caps.parallelShaderCompile) {
  524. description += " - Parallel shader compilation";
  525. }
  526. return description;
  527. }
  528. _createTextureBase(url, noMipmap, invertY, scene, samplingMode = 3, onLoad = null, onError = null, prepareTexture, prepareTextureProcessFunction, buffer = null, fallback = null, format = null, forcedExtension = null, mimeType, loaderOptions, useSRGBBuffer) {
  529. url = url || "";
  530. const fromData = url.substr(0, 5) === "data:";
  531. const fromBlob = url.substr(0, 5) === "blob:";
  532. const isBase64 = fromData && url.indexOf(";base64,") !== -1;
  533. const texture = fallback ? fallback : new InternalTexture(this, InternalTextureSource.Url);
  534. if (texture !== fallback) {
  535. texture.label = url.substring(0, 60); // default label, can be overriden by the caller
  536. }
  537. const originalUrl = url;
  538. if (this._transformTextureUrl && !isBase64 && !fallback && !buffer) {
  539. url = this._transformTextureUrl(url);
  540. }
  541. if (originalUrl !== url) {
  542. texture._originalUrl = originalUrl;
  543. }
  544. // establish the file extension, if possible
  545. const lastDot = url.lastIndexOf(".");
  546. let extension = forcedExtension ? forcedExtension : lastDot > -1 ? url.substring(lastDot).toLowerCase() : "";
  547. let loader = null;
  548. // Remove query string
  549. const queryStringIndex = extension.indexOf("?");
  550. if (queryStringIndex > -1) {
  551. extension = extension.split("?")[0];
  552. }
  553. for (const availableLoader of AbstractEngine._TextureLoaders) {
  554. if (availableLoader.canLoad(extension, mimeType)) {
  555. loader = availableLoader;
  556. break;
  557. }
  558. }
  559. if (scene) {
  560. scene.addPendingData(texture);
  561. }
  562. texture.url = url;
  563. texture.generateMipMaps = !noMipmap;
  564. texture.samplingMode = samplingMode;
  565. texture.invertY = invertY;
  566. texture._useSRGBBuffer = this._getUseSRGBBuffer(!!useSRGBBuffer, noMipmap);
  567. if (!this._doNotHandleContextLost) {
  568. // Keep a link to the buffer only if we plan to handle context lost
  569. texture._buffer = buffer;
  570. }
  571. let onLoadObserver = null;
  572. if (onLoad && !fallback) {
  573. onLoadObserver = texture.onLoadedObservable.add(onLoad);
  574. }
  575. if (!fallback) {
  576. this._internalTexturesCache.push(texture);
  577. }
  578. const onInternalError = (message, exception) => {
  579. if (scene) {
  580. scene.removePendingData(texture);
  581. }
  582. if (url === originalUrl) {
  583. if (onLoadObserver) {
  584. texture.onLoadedObservable.remove(onLoadObserver);
  585. }
  586. if (EngineStore.UseFallbackTexture && url !== EngineStore.FallbackTexture) {
  587. this._createTextureBase(EngineStore.FallbackTexture, noMipmap, texture.invertY, scene, samplingMode, null, onError, prepareTexture, prepareTextureProcessFunction, buffer, texture);
  588. }
  589. message = (message || "Unknown error") + (EngineStore.UseFallbackTexture ? " - Fallback texture was used" : "");
  590. texture.onErrorObservable.notifyObservers({ message, exception });
  591. if (onError) {
  592. onError(message, exception);
  593. }
  594. }
  595. else {
  596. // fall back to the original url if the transformed url fails to load
  597. Logger.Warn(`Failed to load ${url}, falling back to ${originalUrl}`);
  598. this._createTextureBase(originalUrl, noMipmap, texture.invertY, scene, samplingMode, onLoad, onError, prepareTexture, prepareTextureProcessFunction, buffer, texture, format, forcedExtension, mimeType, loaderOptions, useSRGBBuffer);
  599. }
  600. };
  601. // processing for non-image formats
  602. if (loader) {
  603. const callback = (data) => {
  604. loader.loadData(data, texture, (width, height, loadMipmap, isCompressed, done, loadFailed) => {
  605. if (loadFailed) {
  606. onInternalError("TextureLoader failed to load data");
  607. }
  608. else {
  609. prepareTexture(texture, extension, scene, { width, height }, texture.invertY, !loadMipmap, isCompressed, () => {
  610. done();
  611. return false;
  612. }, samplingMode);
  613. }
  614. }, loaderOptions);
  615. };
  616. if (!buffer) {
  617. this._loadFile(url, (data) => callback(new Uint8Array(data)), undefined, scene ? scene.offlineProvider : undefined, true, (request, exception) => {
  618. onInternalError("Unable to load " + (request ? request.responseURL : url, exception));
  619. });
  620. }
  621. else {
  622. if (buffer instanceof ArrayBuffer) {
  623. callback(new Uint8Array(buffer));
  624. }
  625. else if (ArrayBuffer.isView(buffer)) {
  626. callback(buffer);
  627. }
  628. else {
  629. if (onError) {
  630. onError("Unable to load: only ArrayBuffer or ArrayBufferView is supported", null);
  631. }
  632. }
  633. }
  634. }
  635. else {
  636. const onload = (img) => {
  637. if (fromBlob && !this._doNotHandleContextLost) {
  638. // We need to store the image if we need to rebuild the texture
  639. // in case of a webgl context lost
  640. texture._buffer = img;
  641. }
  642. prepareTexture(texture, extension, scene, img, texture.invertY, noMipmap, false, prepareTextureProcessFunction, samplingMode);
  643. };
  644. // According to the WebGL spec section 6.10, ImageBitmaps must be inverted on creation.
  645. // So, we pass imageOrientation to _FileToolsLoadImage() as it may create an ImageBitmap.
  646. if (!fromData || isBase64) {
  647. if (buffer && (typeof buffer.decoding === "string" || buffer.close)) {
  648. onload(buffer);
  649. }
  650. else {
  651. AbstractEngine._FileToolsLoadImage(url || "", onload, onInternalError, scene ? scene.offlineProvider : null, mimeType, texture.invertY && this._features.needsInvertingBitmap ? { imageOrientation: "flipY" } : undefined);
  652. }
  653. }
  654. else if (typeof buffer === "string" || buffer instanceof ArrayBuffer || ArrayBuffer.isView(buffer) || buffer instanceof Blob) {
  655. AbstractEngine._FileToolsLoadImage(buffer, onload, onInternalError, scene ? scene.offlineProvider : null, mimeType, texture.invertY && this._features.needsInvertingBitmap ? { imageOrientation: "flipY" } : undefined);
  656. }
  657. else if (buffer) {
  658. onload(buffer);
  659. }
  660. }
  661. return texture;
  662. }
  663. _rebuildBuffers() {
  664. // Uniforms
  665. for (const uniformBuffer of this._uniformBuffers) {
  666. uniformBuffer._rebuildAfterContextLost();
  667. }
  668. }
  669. /** @internal */
  670. get _shouldUseHighPrecisionShader() {
  671. return !!(this._caps.highPrecisionShaderSupported && this._highPrecisionShadersAllowed);
  672. }
  673. /**
  674. * Gets host document
  675. * @returns the host document object
  676. */
  677. getHostDocument() {
  678. if (this._renderingCanvas && this._renderingCanvas.ownerDocument) {
  679. return this._renderingCanvas.ownerDocument;
  680. }
  681. return IsDocumentAvailable() ? document : null;
  682. }
  683. /**
  684. * Gets the list of loaded textures
  685. * @returns an array containing all loaded textures
  686. */
  687. getLoadedTexturesCache() {
  688. return this._internalTexturesCache;
  689. }
  690. /**
  691. * Clears the list of texture accessible through engine.
  692. * This can help preventing texture load conflict due to name collision.
  693. */
  694. clearInternalTexturesCache() {
  695. this._internalTexturesCache.length = 0;
  696. }
  697. /**
  698. * Gets the object containing all engine capabilities
  699. * @returns the EngineCapabilities object
  700. */
  701. getCaps() {
  702. return this._caps;
  703. }
  704. /**
  705. * Reset the texture cache to empty state
  706. */
  707. resetTextureCache() {
  708. for (const key in this._boundTexturesCache) {
  709. if (!Object.prototype.hasOwnProperty.call(this._boundTexturesCache, key)) {
  710. continue;
  711. }
  712. this._boundTexturesCache[key] = null;
  713. }
  714. this._currentTextureChannel = -1;
  715. }
  716. /**
  717. * Gets or sets the name of the engine
  718. */
  719. get name() {
  720. return this._name;
  721. }
  722. set name(value) {
  723. this._name = value;
  724. }
  725. /**
  726. * Returns the current npm package of the sdk
  727. */
  728. // Not mixed with Version for tooling purpose.
  729. static get NpmPackage() {
  730. return "babylonjs@7.2.3";
  731. }
  732. /**
  733. * Returns the current version of the framework
  734. */
  735. static get Version() {
  736. return "7.2.3";
  737. }
  738. /**
  739. * Gets the HTML canvas attached with the current webGL context
  740. * @returns a HTML canvas
  741. */
  742. getRenderingCanvas() {
  743. return this._renderingCanvas;
  744. }
  745. /**
  746. * Gets the audio context specified in engine initialization options
  747. * @returns an Audio Context
  748. */
  749. getAudioContext() {
  750. return this._audioContext;
  751. }
  752. /**
  753. * Gets the audio destination specified in engine initialization options
  754. * @returns an audio destination node
  755. */
  756. getAudioDestination() {
  757. return this._audioDestination;
  758. }
  759. /**
  760. * Defines the hardware scaling level.
  761. * By default the hardware scaling level is computed from the window device ratio.
  762. * if level = 1 then the engine will render at the exact resolution of the canvas. If level = 0.5 then the engine will render at twice the size of the canvas.
  763. * @param level defines the level to use
  764. */
  765. setHardwareScalingLevel(level) {
  766. this._hardwareScalingLevel = level;
  767. this.resize();
  768. }
  769. /**
  770. * Gets the current hardware scaling level.
  771. * By default the hardware scaling level is computed from the window device ratio.
  772. * if level = 1 then the engine will render at the exact resolution of the canvas. If level = 0.5 then the engine will render at twice the size of the canvas.
  773. * @returns a number indicating the current hardware scaling level
  774. */
  775. getHardwareScalingLevel() {
  776. return this._hardwareScalingLevel;
  777. }
  778. /**
  779. * Gets or sets a boolean indicating if resources should be retained to be able to handle context lost events
  780. * @see https://doc.babylonjs.com/features/featuresDeepDive/scene/optimize_your_scene#handling-webgl-context-lost
  781. */
  782. get doNotHandleContextLost() {
  783. return this._doNotHandleContextLost;
  784. }
  785. set doNotHandleContextLost(value) {
  786. this._doNotHandleContextLost = value;
  787. }
  788. /**
  789. * Returns true if the stencil buffer has been enabled through the creation option of the context.
  790. */
  791. get isStencilEnable() {
  792. return this._isStencilEnable;
  793. }
  794. /**
  795. * Creates a new engine
  796. * @param antialias defines enable antialiasing (default: false)
  797. * @param options defines further options to be sent to the creation context
  798. * @param adaptToDeviceRatio defines whether to adapt to the device's viewport characteristics (default: false)
  799. */
  800. constructor(antialias, options, adaptToDeviceRatio) {
  801. // States
  802. /** @internal */
  803. this._colorWrite = true;
  804. /** @internal */
  805. this._colorWriteChanged = true;
  806. /** @internal */
  807. this._depthCullingState = new DepthCullingState();
  808. /** @internal */
  809. this._stencilStateComposer = new StencilStateComposer();
  810. /** @internal */
  811. this._stencilState = new StencilState();
  812. /** @internal */
  813. this._alphaState = new AlphaState();
  814. /** @internal */
  815. this._alphaMode = 1;
  816. /** @internal */
  817. this._alphaEquation = 0;
  818. /** @internal */
  819. this._badOS = false;
  820. /** @internal */
  821. this._badDesktopOS = false;
  822. this._compatibilityMode = true;
  823. /** @internal */
  824. this._internalTexturesCache = new Array();
  825. this._activeRequests = new Array();
  826. /** @internal */
  827. this._currentRenderTarget = null;
  828. /** @internal */
  829. this._boundTexturesCache = {};
  830. /** @internal */
  831. this._activeChannel = 0;
  832. /** @internal */
  833. this._currentTextureChannel = -1;
  834. /** @internal */
  835. this._viewportCached = { x: 0, y: 0, z: 0, w: 0 };
  836. /** @internal */
  837. this._isWebGPU = false;
  838. /**
  839. * Observable event triggered each time the canvas loses focus
  840. */
  841. this.onCanvasBlurObservable = new Observable();
  842. /**
  843. * Observable event triggered each time the canvas gains focus
  844. */
  845. this.onCanvasFocusObservable = new Observable();
  846. /**
  847. * Event raised when a new scene is created
  848. */
  849. this.onNewSceneAddedObservable = new Observable();
  850. /**
  851. * Observable event triggered each time the rendering canvas is resized
  852. */
  853. this.onResizeObservable = new Observable();
  854. /**
  855. * Observable event triggered each time the canvas receives pointerout event
  856. */
  857. this.onCanvasPointerOutObservable = new Observable();
  858. /**
  859. * Turn this value on if you want to pause FPS computation when in background
  860. */
  861. this.disablePerformanceMonitorInBackground = false;
  862. /**
  863. * Gets or sets a boolean indicating that vertex array object must be disabled even if they are supported
  864. */
  865. this.disableVertexArrayObjects = false;
  866. /** @internal */
  867. this._frameId = 0;
  868. /**
  869. * Gets information about the current host
  870. */
  871. this.hostInformation = {
  872. isMobile: false,
  873. };
  874. /**
  875. * Gets a boolean indicating if the engine is currently rendering in fullscreen mode
  876. */
  877. this.isFullscreen = false;
  878. /**
  879. * Gets or sets a boolean to enable/disable IndexedDB support and avoid XHR on .manifest
  880. **/
  881. this.enableOfflineSupport = false;
  882. /**
  883. * Gets or sets a boolean to enable/disable checking manifest if IndexedDB support is enabled (js will always consider the database is up to date)
  884. **/
  885. this.disableManifestCheck = false;
  886. /**
  887. * Gets or sets a boolean to enable/disable the context menu (right-click) from appearing on the main canvas
  888. */
  889. this.disableContextMenu = true;
  890. /**
  891. * Gets or sets the current render pass id
  892. */
  893. this.currentRenderPassId = 0;
  894. /**
  895. * Gets a boolean indicating if the pointer is currently locked
  896. */
  897. this.isPointerLock = false;
  898. /**
  899. * Gets the list of created postprocesses
  900. */
  901. this.postProcesses = [];
  902. /** Gets or sets the tab index to set to the rendering canvas. 1 is the minimum value to set to be able to capture keyboard events */
  903. this.canvasTabIndex = 1;
  904. /** @internal */
  905. this._contextWasLost = false;
  906. this._useReverseDepthBuffer = false;
  907. /**
  908. * Indicates if the z range in NDC space is 0..1 (value: true) or -1..1 (value: false)
  909. */
  910. this.isNDCHalfZRange = false;
  911. /**
  912. * Indicates that the origin of the texture/framebuffer space is the bottom left corner. If false, the origin is top left
  913. */
  914. this.hasOriginBottomLeft = true;
  915. /** @internal */
  916. this._renderTargetWrapperCache = new Array();
  917. /** @internal */
  918. this._compiledEffects = {};
  919. /** @internal */
  920. this._isDisposed = false;
  921. /**
  922. * Gets the list of created scenes
  923. */
  924. this.scenes = [];
  925. /** @internal */
  926. this._virtualScenes = new Array();
  927. /**
  928. * Observable event triggered before each texture is initialized
  929. */
  930. this.onBeforeTextureInitObservable = new Observable();
  931. /**
  932. * Gets or sets a boolean indicating if the engine must keep rendering even if the window is not in foreground
  933. */
  934. this.renderEvenInBackground = true;
  935. /**
  936. * Gets or sets a boolean indicating that cache can be kept between frames
  937. */
  938. this.preventCacheWipeBetweenFrames = false;
  939. /** @internal */
  940. this._frameHandler = 0;
  941. /** @internal */
  942. this._activeRenderLoops = new Array();
  943. /** @internal */
  944. this._windowIsBackground = false;
  945. /** @internal */
  946. this._boundRenderFunction = () => this._renderLoop();
  947. /**
  948. * Observable raised when the engine is about to compile a shader
  949. */
  950. this.onBeforeShaderCompilationObservable = new Observable();
  951. /**
  952. * Observable raised when the engine has just compiled a shader
  953. */
  954. this.onAfterShaderCompilationObservable = new Observable();
  955. /**
  956. * Observable raised when the engine begins a new frame
  957. */
  958. this.onBeginFrameObservable = new Observable();
  959. /**
  960. * Observable raised when the engine ends the current frame
  961. */
  962. this.onEndFrameObservable = new Observable();
  963. /** @internal */
  964. this._transformTextureUrl = null;
  965. /** @internal */
  966. this._uniformBuffers = new Array();
  967. /** @internal */
  968. this._storageBuffers = new Array();
  969. this._highPrecisionShadersAllowed = true;
  970. // Lost context
  971. /**
  972. * Observable signaled when a context lost event is raised
  973. */
  974. this.onContextLostObservable = new Observable();
  975. /**
  976. * Observable signaled when a context restored event is raised
  977. */
  978. this.onContextRestoredObservable = new Observable();
  979. /** @internal */
  980. this._name = "";
  981. /**
  982. * Defines whether the engine has been created with the premultipliedAlpha option on or not.
  983. */
  984. this.premultipliedAlpha = true;
  985. /**
  986. * If set to true zooming in and out in the browser will rescale the hardware-scaling correctly.
  987. */
  988. this.adaptToDeviceRatio = false;
  989. /** @internal */
  990. this._lastDevicePixelRatio = 1.0;
  991. /** @internal */
  992. this._doNotHandleContextLost = false;
  993. /**
  994. * Gets or sets a boolean indicating if back faces must be culled. If false, front faces are culled instead (true by default)
  995. * If non null, this takes precedence over the value from the material
  996. */
  997. this.cullBackFaces = null;
  998. /** @internal */
  999. this._renderPassNames = ["main"];
  1000. // FPS
  1001. this._fps = 60;
  1002. this._deltaTime = 0;
  1003. // Deterministic lockstepMaxSteps
  1004. /** @internal */
  1005. this._deterministicLockstep = false;
  1006. /** @internal */
  1007. this._lockstepMaxSteps = 4;
  1008. /** @internal */
  1009. this._timeStep = 1 / 60;
  1010. /**
  1011. * An event triggered when the engine is disposed.
  1012. */
  1013. this.onDisposeObservable = new Observable();
  1014. EngineStore.Instances.push(this);
  1015. this.startTime = PrecisionDate.Now;
  1016. this._stencilStateComposer.stencilGlobal = this._stencilState;
  1017. PerformanceConfigurator.SetMatrixPrecision(!!options.useHighPrecisionMatrix);
  1018. if (IsNavigatorAvailable() && navigator.userAgent) {
  1019. // Detect if we are running on a faulty buggy OS.
  1020. this._badOS = /iPad/i.test(navigator.userAgent) || /iPhone/i.test(navigator.userAgent);
  1021. // Detect if we are running on a faulty buggy desktop OS.
  1022. this._badDesktopOS = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
  1023. }
  1024. // Save this off for use in resize().
  1025. this.adaptToDeviceRatio = adaptToDeviceRatio ?? false;
  1026. options.antialias = antialias ?? options.antialias;
  1027. options.deterministicLockstep = options.deterministicLockstep ?? false;
  1028. options.lockstepMaxSteps = options.lockstepMaxSteps ?? 4;
  1029. options.timeStep = options.timeStep ?? 1 / 60;
  1030. options.audioEngine = options.audioEngine ?? true;
  1031. options.stencil = options.stencil ?? true;
  1032. this._audioContext = options.audioEngineOptions?.audioContext ?? null;
  1033. this._audioDestination = options.audioEngineOptions?.audioDestination ?? null;
  1034. this.premultipliedAlpha = options.premultipliedAlpha ?? true;
  1035. this._doNotHandleContextLost = !!options.doNotHandleContextLost;
  1036. this._isStencilEnable = options.stencil ? true : false;
  1037. this.useExactSrgbConversions = options.useExactSrgbConversions ?? false;
  1038. const devicePixelRatio = IsWindowObjectExist() ? window.devicePixelRatio || 1.0 : 1.0;
  1039. const limitDeviceRatio = options.limitDeviceRatio || devicePixelRatio;
  1040. // Viewport
  1041. adaptToDeviceRatio = adaptToDeviceRatio || options.adaptToDeviceRatio || false;
  1042. this._hardwareScalingLevel = adaptToDeviceRatio ? 1.0 / Math.min(limitDeviceRatio, devicePixelRatio) : 1.0;
  1043. this._lastDevicePixelRatio = devicePixelRatio;
  1044. }
  1045. /**
  1046. * Resize the view according to the canvas' size
  1047. * @param forceSetSize true to force setting the sizes of the underlying canvas
  1048. */
  1049. resize(forceSetSize = false) {
  1050. let width;
  1051. let height;
  1052. // Re-query hardware scaling level to handle zoomed-in resizing.
  1053. if (this.adaptToDeviceRatio) {
  1054. const devicePixelRatio = IsWindowObjectExist() ? window.devicePixelRatio || 1.0 : 1.0;
  1055. const changeRatio = this._lastDevicePixelRatio / devicePixelRatio;
  1056. this._lastDevicePixelRatio = devicePixelRatio;
  1057. this._hardwareScalingLevel *= changeRatio;
  1058. }
  1059. if (IsWindowObjectExist() && IsDocumentAvailable()) {
  1060. // make sure it is a Node object, and is a part of the document.
  1061. if (this._renderingCanvas) {
  1062. const boundingRect = this._renderingCanvas.getBoundingClientRect
  1063. ? this._renderingCanvas.getBoundingClientRect()
  1064. : {
  1065. // fallback to last solution in case the function doesn't exist
  1066. width: this._renderingCanvas.width * this._hardwareScalingLevel,
  1067. height: this._renderingCanvas.height * this._hardwareScalingLevel,
  1068. };
  1069. width = this._renderingCanvas.clientWidth || boundingRect.width || this._renderingCanvas.width || 100;
  1070. height = this._renderingCanvas.clientHeight || boundingRect.height || this._renderingCanvas.height || 100;
  1071. }
  1072. else {
  1073. width = window.innerWidth;
  1074. height = window.innerHeight;
  1075. }
  1076. }
  1077. else {
  1078. width = this._renderingCanvas ? this._renderingCanvas.width : 100;
  1079. height = this._renderingCanvas ? this._renderingCanvas.height : 100;
  1080. }
  1081. this.setSize(width / this._hardwareScalingLevel, height / this._hardwareScalingLevel, forceSetSize);
  1082. }
  1083. /**
  1084. * Force a specific size of the canvas
  1085. * @param width defines the new canvas' width
  1086. * @param height defines the new canvas' height
  1087. * @param forceSetSize true to force setting the sizes of the underlying canvas
  1088. * @returns true if the size was changed
  1089. */
  1090. setSize(width, height, forceSetSize = false) {
  1091. if (!this._renderingCanvas) {
  1092. return false;
  1093. }
  1094. width = width | 0;
  1095. height = height | 0;
  1096. if (!forceSetSize && this._renderingCanvas.width === width && this._renderingCanvas.height === height) {
  1097. return false;
  1098. }
  1099. this._renderingCanvas.width = width;
  1100. this._renderingCanvas.height = height;
  1101. return true;
  1102. }
  1103. /**
  1104. * Shared initialization across engines types.
  1105. * @param canvas The canvas associated with this instance of the engine.
  1106. */
  1107. _sharedInit(canvas) {
  1108. this._renderingCanvas = canvas;
  1109. }
  1110. _setupMobileChecks() {
  1111. if (!(navigator && navigator.userAgent)) {
  1112. return;
  1113. }
  1114. // Function to check if running on mobile device
  1115. this._checkForMobile = () => {
  1116. const currentUA = navigator.userAgent;
  1117. this.hostInformation.isMobile =
  1118. currentUA.indexOf("Mobile") !== -1 ||
  1119. // Needed for iOS 13+ detection on iPad (inspired by solution from https://stackoverflow.com/questions/9038625/detect-if-device-is-ios)
  1120. (currentUA.indexOf("Mac") !== -1 && IsDocumentAvailable() && "ontouchend" in document);
  1121. };
  1122. // Set initial isMobile value
  1123. this._checkForMobile();
  1124. // Set up event listener to check when window is resized (used to get emulator activation to work properly)
  1125. if (IsWindowObjectExist()) {
  1126. window.addEventListener("resize", this._checkForMobile);
  1127. }
  1128. }
  1129. /**
  1130. * creates and returns a new video element
  1131. * @param constraints video constraints
  1132. * @returns video element
  1133. */
  1134. // eslint-disable-next-line @typescript-eslint/no-unused-vars
  1135. createVideoElement(constraints) {
  1136. return document.createElement("video");
  1137. }
  1138. /**
  1139. * @internal
  1140. */
  1141. _reportDrawCall(numDrawCalls = 1) {
  1142. this._drawCalls?.addCount(numDrawCalls, false);
  1143. }
  1144. /**
  1145. * Gets the current framerate
  1146. * @returns a number representing the framerate
  1147. */
  1148. getFps() {
  1149. return this._fps;
  1150. }
  1151. /**
  1152. * Gets the time spent between current and previous frame
  1153. * @returns a number representing the delta time in ms
  1154. */
  1155. getDeltaTime() {
  1156. return this._deltaTime;
  1157. }
  1158. /**
  1159. * Gets a boolean indicating that the engine is running in deterministic lock step mode
  1160. * @see https://doc.babylonjs.com/features/featuresDeepDive/animation/advanced_animations#deterministic-lockstep
  1161. * @returns true if engine is in deterministic lock step mode
  1162. */
  1163. isDeterministicLockStep() {
  1164. return this._deterministicLockstep;
  1165. }
  1166. /**
  1167. * Gets the max steps when engine is running in deterministic lock step
  1168. * @see https://doc.babylonjs.com/features/featuresDeepDive/animation/advanced_animations#deterministic-lockstep
  1169. * @returns the max steps
  1170. */
  1171. getLockstepMaxSteps() {
  1172. return this._lockstepMaxSteps;
  1173. }
  1174. /**
  1175. * Returns the time in ms between steps when using deterministic lock step.
  1176. * @returns time step in (ms)
  1177. */
  1178. getTimeStep() {
  1179. return this._timeStep * 1000;
  1180. }
  1181. /**
  1182. * Engine abstraction for loading and creating an image bitmap from a given source string.
  1183. * @param imageSource source to load the image from.
  1184. * @param options An object that sets options for the image's extraction.
  1185. */
  1186. _createImageBitmapFromSource(imageSource, options) {
  1187. throw new Error("createImageBitmapFromSource is not implemented");
  1188. }
  1189. /**
  1190. * Engine abstraction for createImageBitmap
  1191. * @param image source for image
  1192. * @param options An object that sets options for the image's extraction.
  1193. * @returns ImageBitmap
  1194. */
  1195. createImageBitmap(image, options) {
  1196. return createImageBitmap(image, options);
  1197. }
  1198. /**
  1199. * Resize an image and returns the image data as an uint8array
  1200. * @param image image to resize
  1201. * @param bufferWidth destination buffer width
  1202. * @param bufferHeight destination buffer height
  1203. */
  1204. resizeImageBitmap(image, bufferWidth, bufferHeight) {
  1205. throw new Error("resizeImageBitmap is not implemented");
  1206. }
  1207. /**
  1208. * Get Font size information
  1209. * @param font font name
  1210. */
  1211. getFontOffset(font) {
  1212. throw new Error("getFontOffset is not implemented");
  1213. }
  1214. static _CreateCanvas(width, height) {
  1215. if (typeof document === "undefined") {
  1216. return new OffscreenCanvas(width, height);
  1217. }
  1218. const canvas = document.createElement("canvas");
  1219. canvas.width = width;
  1220. canvas.height = height;
  1221. return canvas;
  1222. }
  1223. /**
  1224. * Create a canvas. This method is overridden by other engines
  1225. * @param width width
  1226. * @param height height
  1227. * @returns ICanvas interface
  1228. */
  1229. createCanvas(width, height) {
  1230. return AbstractEngine._CreateCanvas(width, height);
  1231. }
  1232. /**
  1233. * Loads an image as an HTMLImageElement.
  1234. * @param input url string, ArrayBuffer, or Blob to load
  1235. * @param onLoad callback called when the image successfully loads
  1236. * @param onError callback called when the image fails to load
  1237. * @param offlineProvider offline provider for caching
  1238. * @param mimeType optional mime type
  1239. * @param imageBitmapOptions optional the options to use when creating an ImageBitmap
  1240. * @returns the HTMLImageElement of the loaded image
  1241. * @internal
  1242. */
  1243. static _FileToolsLoadImage(input, onLoad, onError, offlineProvider, mimeType, imageBitmapOptions) {
  1244. throw _WarnImport("FileTools");
  1245. }
  1246. /**
  1247. * Loads a file from a url
  1248. * @param url url to load
  1249. * @param onSuccess callback called when the file successfully loads
  1250. * @param onProgress callback called while file is loading (if the server supports this mode)
  1251. * @param offlineProvider defines the offline provider for caching
  1252. * @param useArrayBuffer defines a boolean indicating that date must be returned as ArrayBuffer
  1253. * @param onError callback called when the file fails to load
  1254. * @returns a file request object
  1255. * @internal
  1256. */
  1257. static _FileToolsLoadFile(url, onSuccess, onProgress, offlineProvider, useArrayBuffer, onError) {
  1258. throw _WarnImport("FileTools");
  1259. }
  1260. /**
  1261. * @internal
  1262. */
  1263. _loadFile(url, onSuccess, onProgress, offlineProvider, useArrayBuffer, onError) {
  1264. const request = AbstractEngine._FileToolsLoadFile(url, onSuccess, onProgress, offlineProvider, useArrayBuffer, onError);
  1265. this._activeRequests.push(request);
  1266. request.onCompleteObservable.add((request) => {
  1267. this._activeRequests.splice(this._activeRequests.indexOf(request), 1);
  1268. });
  1269. return request;
  1270. }
  1271. /**
  1272. * Dispose and release all associated resources
  1273. */
  1274. dispose() {
  1275. this.hideLoadingUI();
  1276. this._isDisposed = true;
  1277. this.stopRenderLoop();
  1278. // Empty texture
  1279. if (this._emptyTexture) {
  1280. this._releaseTexture(this._emptyTexture);
  1281. this._emptyTexture = null;
  1282. }
  1283. if (this._emptyCubeTexture) {
  1284. this._releaseTexture(this._emptyCubeTexture);
  1285. this._emptyCubeTexture = null;
  1286. }
  1287. this._renderingCanvas = null;
  1288. // Clear observables
  1289. if (this.onBeforeTextureInitObservable) {
  1290. this.onBeforeTextureInitObservable.clear();
  1291. }
  1292. // Release postProcesses
  1293. while (this.postProcesses.length) {
  1294. this.postProcesses[0].dispose();
  1295. }
  1296. // Release scenes
  1297. while (this.scenes.length) {
  1298. this.scenes[0].dispose();
  1299. }
  1300. while (this._virtualScenes.length) {
  1301. this._virtualScenes[0].dispose();
  1302. }
  1303. // Release effects
  1304. this.releaseComputeEffects?.();
  1305. Effect.ResetCache();
  1306. // Abort active requests
  1307. for (const request of this._activeRequests) {
  1308. request.abort();
  1309. }
  1310. this._boundRenderFunction = null;
  1311. this.onDisposeObservable.notifyObservers(this);
  1312. this.onDisposeObservable.clear();
  1313. this.onResizeObservable.clear();
  1314. this.onCanvasBlurObservable.clear();
  1315. this.onCanvasFocusObservable.clear();
  1316. this.onCanvasPointerOutObservable.clear();
  1317. this.onNewSceneAddedObservable.clear();
  1318. if (IsWindowObjectExist()) {
  1319. window.removeEventListener("resize", this._checkForMobile);
  1320. }
  1321. // Remove from Instances
  1322. const index = EngineStore.Instances.indexOf(this);
  1323. if (index >= 0) {
  1324. EngineStore.Instances.splice(index, 1);
  1325. }
  1326. // no more engines left in the engine store? Notify!
  1327. if (!EngineStore.Instances.length) {
  1328. EngineStore.OnEnginesDisposedObservable.notifyObservers(this);
  1329. }
  1330. // Observables
  1331. this.onBeginFrameObservable.clear();
  1332. this.onEndFrameObservable.clear();
  1333. }
  1334. /**
  1335. * Method called to create the default loading screen.
  1336. * This can be overridden in your own app.
  1337. * @param canvas The rendering canvas element
  1338. */
  1339. // eslint-disable-next-line @typescript-eslint/no-unused-vars
  1340. static DefaultLoadingScreenFactory(canvas) {
  1341. throw _WarnImport("LoadingScreen");
  1342. }
  1343. }
  1344. /** @internal */
  1345. AbstractEngine._TextureLoaders = [];
  1346. // eslint-disable-next-line @typescript-eslint/naming-convention
  1347. /** @internal */
  1348. AbstractEngine._RenderPassIdCounter = 0;
  1349. /**
  1350. * Method called to create the default rescale post process on each engine.
  1351. */
  1352. AbstractEngine._RescalePostProcessFactory = null;
  1353. //# sourceMappingURL=abstractEngine.js.map