sceneOptimizer.js 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734
  1. import { EngineStore } from "../Engines/engineStore.js";
  2. import { Mesh } from "../Meshes/mesh.js";
  3. import { Observable } from "./observable.js";
  4. /**
  5. * Defines the root class used to create scene optimization to use with SceneOptimizer
  6. * @description More details at https://doc.babylonjs.com/features/featuresDeepDive/scene/sceneOptimizer
  7. */
  8. export class SceneOptimization {
  9. /**
  10. * Gets a string describing the action executed by the current optimization
  11. * @returns description string
  12. */
  13. getDescription() {
  14. return "";
  15. }
  16. /**
  17. * This function will be called by the SceneOptimizer when its priority is reached in order to apply the change required by the current optimization
  18. * @param scene defines the current scene where to apply this optimization
  19. * @param optimizer defines the current optimizer
  20. * @returns true if everything that can be done was applied
  21. */
  22. apply(scene, optimizer) {
  23. return true;
  24. }
  25. /**
  26. * Creates the SceneOptimization object
  27. * @param priority defines the priority of this optimization (0 by default which means first in the list)
  28. */
  29. constructor(
  30. /**
  31. * Defines the priority of this optimization (0 by default which means first in the list)
  32. */
  33. priority = 0) {
  34. this.priority = priority;
  35. }
  36. }
  37. /**
  38. * Defines an optimization used to reduce the size of render target textures
  39. * @description More details at https://doc.babylonjs.com/features/featuresDeepDive/scene/sceneOptimizer
  40. */
  41. export class TextureOptimization extends SceneOptimization {
  42. /**
  43. * Gets a string describing the action executed by the current optimization
  44. * @returns description string
  45. */
  46. getDescription() {
  47. return "Reducing render target texture size to " + this.maximumSize;
  48. }
  49. /**
  50. * Creates the TextureOptimization object
  51. * @param priority defines the priority of this optimization (0 by default which means first in the list)
  52. * @param maximumSize defines the maximum sized allowed for textures (1024 is the default value). If a texture is bigger, it will be scaled down using a factor defined by the step parameter
  53. * @param step defines the factor (0.5 by default) used to scale down textures bigger than maximum sized allowed.
  54. */
  55. constructor(
  56. /**
  57. * Defines the priority of this optimization (0 by default which means first in the list)
  58. */
  59. priority = 0,
  60. /**
  61. * Defines the maximum sized allowed for textures (1024 is the default value). If a texture is bigger, it will be scaled down using a factor defined by the step parameter
  62. */
  63. maximumSize = 1024,
  64. /**
  65. * Defines the factor (0.5 by default) used to scale down textures bigger than maximum sized allowed.
  66. */
  67. step = 0.5) {
  68. super(priority);
  69. this.priority = priority;
  70. this.maximumSize = maximumSize;
  71. this.step = step;
  72. }
  73. /**
  74. * This function will be called by the SceneOptimizer when its priority is reached in order to apply the change required by the current optimization
  75. * @param scene defines the current scene where to apply this optimization
  76. * @param optimizer defines the current optimizer
  77. * @returns true if everything that can be done was applied
  78. */
  79. apply(scene, optimizer) {
  80. let allDone = true;
  81. for (let index = 0; index < scene.textures.length; index++) {
  82. const texture = scene.textures[index];
  83. if (!texture.canRescale || texture.getContext) {
  84. continue;
  85. }
  86. const currentSize = texture.getSize();
  87. const maxDimension = Math.max(currentSize.width, currentSize.height);
  88. if (maxDimension > this.maximumSize) {
  89. texture.scale(this.step);
  90. allDone = false;
  91. }
  92. }
  93. return allDone;
  94. }
  95. }
  96. /**
  97. * Defines an optimization used to increase or decrease the rendering resolution
  98. * @description More details at https://doc.babylonjs.com/features/featuresDeepDive/scene/sceneOptimizer
  99. */
  100. export class HardwareScalingOptimization extends SceneOptimization {
  101. /**
  102. * Gets a string describing the action executed by the current optimization
  103. * @returns description string
  104. */
  105. getDescription() {
  106. return "Setting hardware scaling level to " + this._currentScale;
  107. }
  108. /**
  109. * Creates the HardwareScalingOptimization object
  110. * @param priority defines the priority of this optimization (0 by default which means first in the list)
  111. * @param maximumScale defines the maximum scale to use (2 by default)
  112. * @param step defines the step to use between two passes (0.5 by default)
  113. */
  114. constructor(
  115. /**
  116. * Defines the priority of this optimization (0 by default which means first in the list)
  117. */
  118. priority = 0,
  119. /**
  120. * Defines the maximum scale to use (2 by default)
  121. */
  122. maximumScale = 2,
  123. /**
  124. * Defines the step to use between two passes (0.5 by default)
  125. */
  126. step = 0.25) {
  127. super(priority);
  128. this.priority = priority;
  129. this.maximumScale = maximumScale;
  130. this.step = step;
  131. this._currentScale = -1;
  132. this._directionOffset = 1;
  133. }
  134. /**
  135. * This function will be called by the SceneOptimizer when its priority is reached in order to apply the change required by the current optimization
  136. * @param scene defines the current scene where to apply this optimization
  137. * @param optimizer defines the current optimizer
  138. * @returns true if everything that can be done was applied
  139. */
  140. apply(scene, optimizer) {
  141. if (this._currentScale === -1) {
  142. this._currentScale = scene.getEngine().getHardwareScalingLevel();
  143. if (this._currentScale > this.maximumScale) {
  144. this._directionOffset = -1;
  145. }
  146. }
  147. this._currentScale += this._directionOffset * this.step;
  148. scene.getEngine().setHardwareScalingLevel(this._currentScale);
  149. return this._directionOffset === 1 ? this._currentScale >= this.maximumScale : this._currentScale <= this.maximumScale;
  150. }
  151. }
  152. /**
  153. * Defines an optimization used to remove shadows
  154. * @description More details at https://doc.babylonjs.com/features/featuresDeepDive/scene/sceneOptimizer
  155. */
  156. export class ShadowsOptimization extends SceneOptimization {
  157. /**
  158. * Gets a string describing the action executed by the current optimization
  159. * @returns description string
  160. */
  161. getDescription() {
  162. return "Turning shadows on/off";
  163. }
  164. /**
  165. * This function will be called by the SceneOptimizer when its priority is reached in order to apply the change required by the current optimization
  166. * @param scene defines the current scene where to apply this optimization
  167. * @param optimizer defines the current optimizer
  168. * @returns true if everything that can be done was applied
  169. */
  170. apply(scene, optimizer) {
  171. scene.shadowsEnabled = optimizer.isInImprovementMode;
  172. return true;
  173. }
  174. }
  175. /**
  176. * Defines an optimization used to turn post-processes off
  177. * @description More details at https://doc.babylonjs.com/features/featuresDeepDive/scene/sceneOptimizer
  178. */
  179. export class PostProcessesOptimization extends SceneOptimization {
  180. /**
  181. * Gets a string describing the action executed by the current optimization
  182. * @returns description string
  183. */
  184. getDescription() {
  185. return "Turning post-processes on/off";
  186. }
  187. /**
  188. * This function will be called by the SceneOptimizer when its priority is reached in order to apply the change required by the current optimization
  189. * @param scene defines the current scene where to apply this optimization
  190. * @param optimizer defines the current optimizer
  191. * @returns true if everything that can be done was applied
  192. */
  193. apply(scene, optimizer) {
  194. scene.postProcessesEnabled = optimizer.isInImprovementMode;
  195. return true;
  196. }
  197. }
  198. /**
  199. * Defines an optimization used to turn lens flares off
  200. * @description More details at https://doc.babylonjs.com/features/featuresDeepDive/scene/sceneOptimizer
  201. */
  202. export class LensFlaresOptimization extends SceneOptimization {
  203. /**
  204. * Gets a string describing the action executed by the current optimization
  205. * @returns description string
  206. */
  207. getDescription() {
  208. return "Turning lens flares on/off";
  209. }
  210. /**
  211. * This function will be called by the SceneOptimizer when its priority is reached in order to apply the change required by the current optimization
  212. * @param scene defines the current scene where to apply this optimization
  213. * @param optimizer defines the current optimizer
  214. * @returns true if everything that can be done was applied
  215. */
  216. apply(scene, optimizer) {
  217. scene.lensFlaresEnabled = optimizer.isInImprovementMode;
  218. return true;
  219. }
  220. }
  221. /**
  222. * Defines an optimization based on user defined callback.
  223. * @description More details at https://doc.babylonjs.com/features/featuresDeepDive/scene/sceneOptimizer
  224. */
  225. export class CustomOptimization extends SceneOptimization {
  226. /**
  227. * Gets a string describing the action executed by the current optimization
  228. * @returns description string
  229. */
  230. getDescription() {
  231. if (this.onGetDescription) {
  232. return this.onGetDescription();
  233. }
  234. return "Running user defined callback";
  235. }
  236. /**
  237. * This function will be called by the SceneOptimizer when its priority is reached in order to apply the change required by the current optimization
  238. * @param scene defines the current scene where to apply this optimization
  239. * @param optimizer defines the current optimizer
  240. * @returns true if everything that can be done was applied
  241. */
  242. apply(scene, optimizer) {
  243. if (this.onApply) {
  244. return this.onApply(scene, optimizer);
  245. }
  246. return true;
  247. }
  248. }
  249. /**
  250. * Defines an optimization used to turn particles off
  251. * @description More details at https://doc.babylonjs.com/features/featuresDeepDive/scene/sceneOptimizer
  252. */
  253. export class ParticlesOptimization extends SceneOptimization {
  254. /**
  255. * Gets a string describing the action executed by the current optimization
  256. * @returns description string
  257. */
  258. getDescription() {
  259. return "Turning particles on/off";
  260. }
  261. /**
  262. * This function will be called by the SceneOptimizer when its priority is reached in order to apply the change required by the current optimization
  263. * @param scene defines the current scene where to apply this optimization
  264. * @param optimizer defines the current optimizer
  265. * @returns true if everything that can be done was applied
  266. */
  267. apply(scene, optimizer) {
  268. scene.particlesEnabled = optimizer.isInImprovementMode;
  269. return true;
  270. }
  271. }
  272. /**
  273. * Defines an optimization used to turn render targets off
  274. * @description More details at https://doc.babylonjs.com/features/featuresDeepDive/scene/sceneOptimizer
  275. */
  276. export class RenderTargetsOptimization extends SceneOptimization {
  277. /**
  278. * Gets a string describing the action executed by the current optimization
  279. * @returns description string
  280. */
  281. getDescription() {
  282. return "Turning render targets off";
  283. }
  284. /**
  285. * This function will be called by the SceneOptimizer when its priority is reached in order to apply the change required by the current optimization
  286. * @param scene defines the current scene where to apply this optimization
  287. * @param optimizer defines the current optimizer
  288. * @returns true if everything that can be done was applied
  289. */
  290. apply(scene, optimizer) {
  291. scene.renderTargetsEnabled = optimizer.isInImprovementMode;
  292. return true;
  293. }
  294. }
  295. /**
  296. * Defines an optimization used to merge meshes with compatible materials
  297. * @description More details at https://doc.babylonjs.com/features/featuresDeepDive/scene/sceneOptimizer
  298. */
  299. export class MergeMeshesOptimization extends SceneOptimization {
  300. constructor() {
  301. super(...arguments);
  302. this._canBeMerged = (abstractMesh) => {
  303. if (!(abstractMesh instanceof Mesh)) {
  304. return false;
  305. }
  306. const mesh = abstractMesh;
  307. if (mesh.isDisposed()) {
  308. return false;
  309. }
  310. if (!mesh.isVisible || !mesh.isEnabled()) {
  311. return false;
  312. }
  313. if (mesh.instances.length > 0) {
  314. return false;
  315. }
  316. if (mesh.skeleton || mesh.hasLODLevels) {
  317. return false;
  318. }
  319. if (mesh.getTotalVertices() === 0) {
  320. return false;
  321. }
  322. return true;
  323. };
  324. }
  325. /**
  326. * Gets or sets a boolean which defines if optimization octree has to be updated
  327. */
  328. static get UpdateSelectionTree() {
  329. return MergeMeshesOptimization._UpdateSelectionTree;
  330. }
  331. /**
  332. * Gets or sets a boolean which defines if optimization octree has to be updated
  333. */
  334. static set UpdateSelectionTree(value) {
  335. MergeMeshesOptimization._UpdateSelectionTree = value;
  336. }
  337. /**
  338. * Gets a string describing the action executed by the current optimization
  339. * @returns description string
  340. */
  341. getDescription() {
  342. return "Merging similar meshes together";
  343. }
  344. /**
  345. * This function will be called by the SceneOptimizer when its priority is reached in order to apply the change required by the current optimization
  346. * @param scene defines the current scene where to apply this optimization
  347. * @param optimizer defines the current optimizer
  348. * @param updateSelectionTree defines that the selection octree has to be updated (false by default)
  349. * @returns true if everything that can be done was applied
  350. */
  351. apply(scene, optimizer, updateSelectionTree) {
  352. const globalPool = scene.meshes.slice(0);
  353. let globalLength = globalPool.length;
  354. for (let index = 0; index < globalLength; index++) {
  355. const currentPool = [];
  356. const current = globalPool[index];
  357. // Checks
  358. if (!this._canBeMerged(current)) {
  359. continue;
  360. }
  361. currentPool.push(current);
  362. // Find compatible meshes
  363. for (let subIndex = index + 1; subIndex < globalLength; subIndex++) {
  364. const otherMesh = globalPool[subIndex];
  365. if (!this._canBeMerged(otherMesh)) {
  366. continue;
  367. }
  368. if (otherMesh.material !== current.material) {
  369. continue;
  370. }
  371. if (otherMesh.checkCollisions !== current.checkCollisions) {
  372. continue;
  373. }
  374. currentPool.push(otherMesh);
  375. globalLength--;
  376. globalPool.splice(subIndex, 1);
  377. subIndex--;
  378. }
  379. if (currentPool.length < 2) {
  380. continue;
  381. }
  382. // Merge meshes
  383. Mesh.MergeMeshes(currentPool, undefined, true);
  384. }
  385. // Call the octree system optimization if it is defined.
  386. const sceneAsAny = scene;
  387. if (sceneAsAny.createOrUpdateSelectionOctree) {
  388. if (updateSelectionTree != undefined) {
  389. if (updateSelectionTree) {
  390. sceneAsAny.createOrUpdateSelectionOctree();
  391. }
  392. }
  393. else if (MergeMeshesOptimization.UpdateSelectionTree) {
  394. sceneAsAny.createOrUpdateSelectionOctree();
  395. }
  396. }
  397. return true;
  398. }
  399. }
  400. MergeMeshesOptimization._UpdateSelectionTree = false;
  401. /**
  402. * Defines a list of options used by SceneOptimizer
  403. * @description More details at https://doc.babylonjs.com/features/featuresDeepDive/scene/sceneOptimizer
  404. */
  405. export class SceneOptimizerOptions {
  406. /**
  407. * Creates a new list of options used by SceneOptimizer
  408. * @param targetFrameRate defines the target frame rate to reach (60 by default)
  409. * @param trackerDuration defines the interval between two checks (2000ms by default)
  410. */
  411. constructor(
  412. /**
  413. * Defines the target frame rate to reach (60 by default)
  414. */
  415. targetFrameRate = 60,
  416. /**
  417. * Defines the interval between two checks (2000ms by default)
  418. */
  419. trackerDuration = 2000) {
  420. this.targetFrameRate = targetFrameRate;
  421. this.trackerDuration = trackerDuration;
  422. /**
  423. * Gets the list of optimizations to apply
  424. */
  425. this.optimizations = [];
  426. }
  427. /**
  428. * Add a new optimization
  429. * @param optimization defines the SceneOptimization to add to the list of active optimizations
  430. * @returns the current SceneOptimizerOptions
  431. */
  432. addOptimization(optimization) {
  433. this.optimizations.push(optimization);
  434. return this;
  435. }
  436. /**
  437. * Add a new custom optimization
  438. * @param onApply defines the callback called to apply the custom optimization (true if everything that can be done was applied)
  439. * @param onGetDescription defines the callback called to get the description attached with the optimization.
  440. * @param priority defines the priority of this optimization (0 by default which means first in the list)
  441. * @returns the current SceneOptimizerOptions
  442. */
  443. addCustomOptimization(onApply, onGetDescription, priority = 0) {
  444. const optimization = new CustomOptimization(priority);
  445. optimization.onApply = onApply;
  446. optimization.onGetDescription = onGetDescription;
  447. this.optimizations.push(optimization);
  448. return this;
  449. }
  450. /**
  451. * Creates a list of pre-defined optimizations aimed to reduce the visual impact on the scene
  452. * @param targetFrameRate defines the target frame rate (60 by default)
  453. * @returns a SceneOptimizerOptions object
  454. */
  455. static LowDegradationAllowed(targetFrameRate) {
  456. const result = new SceneOptimizerOptions(targetFrameRate);
  457. let priority = 0;
  458. result.addOptimization(new MergeMeshesOptimization(priority));
  459. result.addOptimization(new ShadowsOptimization(priority));
  460. result.addOptimization(new LensFlaresOptimization(priority));
  461. // Next priority
  462. priority++;
  463. result.addOptimization(new PostProcessesOptimization(priority));
  464. result.addOptimization(new ParticlesOptimization(priority));
  465. // Next priority
  466. priority++;
  467. result.addOptimization(new TextureOptimization(priority, 1024));
  468. return result;
  469. }
  470. /**
  471. * Creates a list of pre-defined optimizations aimed to have a moderate impact on the scene visual
  472. * @param targetFrameRate defines the target frame rate (60 by default)
  473. * @returns a SceneOptimizerOptions object
  474. */
  475. static ModerateDegradationAllowed(targetFrameRate) {
  476. const result = new SceneOptimizerOptions(targetFrameRate);
  477. let priority = 0;
  478. result.addOptimization(new MergeMeshesOptimization(priority));
  479. result.addOptimization(new ShadowsOptimization(priority));
  480. result.addOptimization(new LensFlaresOptimization(priority));
  481. // Next priority
  482. priority++;
  483. result.addOptimization(new PostProcessesOptimization(priority));
  484. result.addOptimization(new ParticlesOptimization(priority));
  485. // Next priority
  486. priority++;
  487. result.addOptimization(new TextureOptimization(priority, 512));
  488. // Next priority
  489. priority++;
  490. result.addOptimization(new RenderTargetsOptimization(priority));
  491. // Next priority
  492. priority++;
  493. result.addOptimization(new HardwareScalingOptimization(priority, 2));
  494. return result;
  495. }
  496. /**
  497. * Creates a list of pre-defined optimizations aimed to have a big impact on the scene visual
  498. * @param targetFrameRate defines the target frame rate (60 by default)
  499. * @returns a SceneOptimizerOptions object
  500. */
  501. static HighDegradationAllowed(targetFrameRate) {
  502. const result = new SceneOptimizerOptions(targetFrameRate);
  503. let priority = 0;
  504. result.addOptimization(new MergeMeshesOptimization(priority));
  505. result.addOptimization(new ShadowsOptimization(priority));
  506. result.addOptimization(new LensFlaresOptimization(priority));
  507. // Next priority
  508. priority++;
  509. result.addOptimization(new PostProcessesOptimization(priority));
  510. result.addOptimization(new ParticlesOptimization(priority));
  511. // Next priority
  512. priority++;
  513. result.addOptimization(new TextureOptimization(priority, 256));
  514. // Next priority
  515. priority++;
  516. result.addOptimization(new RenderTargetsOptimization(priority));
  517. // Next priority
  518. priority++;
  519. result.addOptimization(new HardwareScalingOptimization(priority, 4));
  520. return result;
  521. }
  522. }
  523. /**
  524. * Class used to run optimizations in order to reach a target frame rate
  525. * @description More details at https://doc.babylonjs.com/features/featuresDeepDive/scene/sceneOptimizer
  526. */
  527. export class SceneOptimizer {
  528. /**
  529. * Gets or sets a boolean indicating if the optimizer is in improvement mode
  530. */
  531. get isInImprovementMode() {
  532. return this._improvementMode;
  533. }
  534. set isInImprovementMode(value) {
  535. this._improvementMode = value;
  536. }
  537. /**
  538. * Gets the current priority level (0 at start)
  539. */
  540. get currentPriorityLevel() {
  541. return this._currentPriorityLevel;
  542. }
  543. /**
  544. * Gets the current frame rate checked by the SceneOptimizer
  545. */
  546. get currentFrameRate() {
  547. return this._currentFrameRate;
  548. }
  549. /**
  550. * Gets or sets the current target frame rate (60 by default)
  551. */
  552. get targetFrameRate() {
  553. return this._targetFrameRate;
  554. }
  555. /**
  556. * Gets or sets the current target frame rate (60 by default)
  557. */
  558. set targetFrameRate(value) {
  559. this._targetFrameRate = value;
  560. }
  561. /**
  562. * Gets or sets the current interval between two checks (every 2000ms by default)
  563. */
  564. get trackerDuration() {
  565. return this._trackerDuration;
  566. }
  567. /**
  568. * Gets or sets the current interval between two checks (every 2000ms by default)
  569. */
  570. set trackerDuration(value) {
  571. this._trackerDuration = value;
  572. }
  573. /**
  574. * Gets the list of active optimizations
  575. */
  576. get optimizations() {
  577. return this._options.optimizations;
  578. }
  579. /**
  580. * Creates a new SceneOptimizer
  581. * @param scene defines the scene to work on
  582. * @param options defines the options to use with the SceneOptimizer
  583. * @param autoGeneratePriorities defines if priorities must be generated and not read from SceneOptimization property (true by default)
  584. * @param improvementMode defines if the scene optimizer must run the maximum optimization while staying over a target frame instead of trying to reach the target framerate (false by default)
  585. */
  586. constructor(scene, options, autoGeneratePriorities = true, improvementMode = false) {
  587. this._isRunning = false;
  588. this._currentPriorityLevel = 0;
  589. this._targetFrameRate = 60;
  590. this._trackerDuration = 2000;
  591. this._currentFrameRate = 0;
  592. this._improvementMode = false;
  593. /**
  594. * Defines an observable called when the optimizer reaches the target frame rate
  595. */
  596. this.onSuccessObservable = new Observable();
  597. /**
  598. * Defines an observable called when the optimizer enables an optimization
  599. */
  600. this.onNewOptimizationAppliedObservable = new Observable();
  601. /**
  602. * Defines an observable called when the optimizer is not able to reach the target frame rate
  603. */
  604. this.onFailureObservable = new Observable();
  605. if (!options) {
  606. this._options = new SceneOptimizerOptions();
  607. }
  608. else {
  609. this._options = options;
  610. }
  611. if (this._options.targetFrameRate) {
  612. this._targetFrameRate = this._options.targetFrameRate;
  613. }
  614. if (this._options.trackerDuration) {
  615. this._trackerDuration = this._options.trackerDuration;
  616. }
  617. if (autoGeneratePriorities) {
  618. let priority = 0;
  619. for (const optim of this._options.optimizations) {
  620. optim.priority = priority++;
  621. }
  622. }
  623. this._improvementMode = improvementMode;
  624. this._scene = scene || EngineStore.LastCreatedScene;
  625. this._sceneDisposeObserver = this._scene.onDisposeObservable.add(() => {
  626. this._sceneDisposeObserver = null;
  627. this.dispose();
  628. });
  629. }
  630. /**
  631. * Stops the current optimizer
  632. */
  633. stop() {
  634. this._isRunning = false;
  635. }
  636. /**
  637. * Reset the optimizer to initial step (current priority level = 0)
  638. */
  639. reset() {
  640. this._currentPriorityLevel = 0;
  641. }
  642. /**
  643. * Start the optimizer. By default it will try to reach a specific framerate
  644. * but if the optimizer is set with improvementMode === true then it will run all optimization while frame rate is above the target frame rate
  645. */
  646. start() {
  647. if (this._isRunning) {
  648. return;
  649. }
  650. this._isRunning = true;
  651. // Let's wait for the scene to be ready before running our check
  652. this._scene.executeWhenReady(() => {
  653. setTimeout(() => {
  654. this._checkCurrentState();
  655. }, this._trackerDuration);
  656. });
  657. }
  658. _checkCurrentState() {
  659. if (!this._isRunning) {
  660. return;
  661. }
  662. const scene = this._scene;
  663. const options = this._options;
  664. this._currentFrameRate = Math.round(scene.getEngine().getFps());
  665. if ((this._improvementMode && this._currentFrameRate <= this._targetFrameRate) || (!this._improvementMode && this._currentFrameRate >= this._targetFrameRate)) {
  666. this._isRunning = false;
  667. this.onSuccessObservable.notifyObservers(this);
  668. return;
  669. }
  670. // Apply current level of optimizations
  671. let allDone = true;
  672. let noOptimizationApplied = true;
  673. for (let index = 0; index < options.optimizations.length; index++) {
  674. const optimization = options.optimizations[index];
  675. if (optimization.priority === this._currentPriorityLevel) {
  676. noOptimizationApplied = false;
  677. allDone = allDone && optimization.apply(scene, this);
  678. this.onNewOptimizationAppliedObservable.notifyObservers(optimization);
  679. }
  680. }
  681. // If no optimization was applied, this is a failure :(
  682. if (noOptimizationApplied) {
  683. this._isRunning = false;
  684. this.onFailureObservable.notifyObservers(this);
  685. return;
  686. }
  687. // If all optimizations were done, move to next level
  688. if (allDone) {
  689. this._currentPriorityLevel++;
  690. }
  691. // Let's the system running for a specific amount of time before checking FPS
  692. scene.executeWhenReady(() => {
  693. setTimeout(() => {
  694. this._checkCurrentState();
  695. }, this._trackerDuration);
  696. });
  697. }
  698. /**
  699. * Release all resources
  700. */
  701. dispose() {
  702. this.stop();
  703. this.onSuccessObservable.clear();
  704. this.onFailureObservable.clear();
  705. this.onNewOptimizationAppliedObservable.clear();
  706. if (this._sceneDisposeObserver) {
  707. this._scene.onDisposeObservable.remove(this._sceneDisposeObserver);
  708. }
  709. }
  710. /**
  711. * Helper function to create a SceneOptimizer with one single line of code
  712. * @param scene defines the scene to work on
  713. * @param options defines the options to use with the SceneOptimizer
  714. * @param onSuccess defines a callback to call on success
  715. * @param onFailure defines a callback to call on failure
  716. * @returns the new SceneOptimizer object
  717. */
  718. static OptimizeAsync(scene, options, onSuccess, onFailure) {
  719. const optimizer = new SceneOptimizer(scene, options || SceneOptimizerOptions.ModerateDegradationAllowed(), false);
  720. if (onSuccess) {
  721. optimizer.onSuccessObservable.add(() => {
  722. onSuccess();
  723. });
  724. }
  725. if (onFailure) {
  726. optimizer.onFailureObservable.add(() => {
  727. onFailure();
  728. });
  729. }
  730. optimizer.start();
  731. return optimizer;
  732. }
  733. }
  734. //# sourceMappingURL=sceneOptimizer.js.map