babylon.sceneOptimizer.ts 33 KB

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