Library for tracking and iteratively improving framerate over time inspired by the Babylon.SceneOptimizer.
The terms "increase work" and "decrease work" are synonymous with "improve quality" and "improve performance" respectively.
import { Optimizer } from '.../Optimizer.js';
// wait for x milliseconds
function wait(ms) {
const start = window.performance.now();
while (window.performance.now < start + ms) {}
}
// optimize the work time until we reach the target framerate
const optimizer = new Optimizer();
optimizer.addOptimization(delta => {
workTime += delta < 0 ? -1 : 1;
return true;
});
// work loop
let workTime = 500;
(function loop(){
optimizer.update();
wait(workTime);
requestAnimationFrame(loop);
})();
The optimizer tracks the amount of time spent between frames or between calls to begin
and end
and calculates the difference between the target amount of time to spend and the actual time spent on the last frame. After the specified amount of time has passed the average time spent is calculated and the framerate the amount of work is either increased or decreased depending on whether or not the time spent was above or below the target. The amount of work is adjusted by iteratively calling prioritized optimizations and sampling framerate until the target work time is met.
enabled = true : Boolean
Getter and setter for enabling or disabling the optimizer. Elapsed time is reset on reenable.
completed : Boolean
Whether or not the optimizer has stopped iterating and sampling the framerate.
constructor( options : Object )
All options can be accessed and modified on Optimizer.options
.
The target milliseconds to hit in the enclosed code block. This cannot be less than 16.66...
because browser caps the framerate to 60 frames per second. If this is less than that the optimizer will continually decrease quality to try to get performance up.
Defaults to 16.66...
.
The target framerate to hit. This overrides targetMillis
if it is set.
Defaults to null
.
How often to perform a check sample the average framerate to try to optimize in milliseconds.
Defaults to 500
or half a second.
At most how many frames can run before an optimization should occur. This is useful when code may not run consistently but runs as needed, meaning that checking after a fixed amount of time might mean that an inconsistent or no actual iterations has occurred. Interval
should probably be set to Infinity
in this case.
Defaults to Infinity
.
The amount of time to wait between sampling frames for a new optimization. This is useful when an optimization may cause the framerate to drop for a frame or two -- such as with allocating a lot of new memory or new webgl render targets.
Defaults to 0
;
At most how many frames to wait for.
Defaults to Infinity
;
How far outside of the target framerate must be in order for an optimization to occurs.
Defaults to 0.05
or 5%.
By default the optimizer will stop trying to optimize and optimization the page once the target framerate is hit or no more optimizations can run and will never try to improve quality of the page again after the first iteration.
This option allows the optimizer to ping pong between improving performance and quality continually to keep a steady framerate. Note that this may not be a good option when using "expensive" optimizations that may stall the frame for a moment, like compiling shaders.
Defaults to false
.
Whether the optimizer should ever try to increase the amount of work done at the cost of framerate. After the quality improvements are made the optimizer tries to improve the framerate until the target framerate is met.
Defaults to false
.
dispose() : void
Removes window events that the optimizer listens for, including window "blur"
and "focus"
. It is expected that the optimizer is no longer used after this.
addOptimization( optimization : Optimization, priority = 0 : Number ) : void
Takes an instance of a Optimization
(described below) and a priority by which to call the optimization. Optimizations are optimized iteratively until each priority level has been completely optimized then it will move to the next one. When improving quality the lowest priority level is started with to improve quality. The highest is started with when improving performance.
Expensive but unnecessary work should be optimized at a high priority level. Critical functionality should be optimized at a low priority level.
begin() : void
end() : void
begin
an end
are used to encapsulate the code block to be optimized. Any optimizations being used should affect the performance of the code within that code block.
optimizer.begin();
// ... code to optimize ...
optimizer.end();
update() : void
update
takes the place of both begin()
and end()
and should be called once per frame to optimize the full frame time.
function loop() {
optimizer.update();
// ... code to optimize
requesAnimationFrame(loop);
}
addSample( time : Number ) : void
Call instead of begin
, end
, or update
if you'd like to use a separate time-measuring mechanism that cannot be achieved with the aforementioned function, such as when measuring GPU work time with the WebGL disjoint_timer_query.
restart() : void
Restarts optimization from the beginning.
This should be called if something on the page changed icantly which might give more room for improvement, such as significantly resizing the browser window with a WebGL app.
An optimization base class for creating a reusable optimization to add to the Optimizer
.
optimize( delta : Number, optimizer : Optimizer )
The optimize function is called whenever an optimization should take place, either to improve performance or quality. The delta
argument is the amount of milliseconds difference between the target framerate and the current framerate. A negative value means that less work should be done to hit the target.
The optimize function must return true
if the setting was optimized and 'false' if no optimization could occur, ie the setting being optimized could not be turned down any further or is at the lowest acceptable setting.
The second argument optimizer
is the optimizer that is running the optimization.
A extension of Optimization
that abstracts some of the basic functionality needed to implement it.
canIncreaseWork() : Boolean
canDecreaseWork() : Boolean
Functions that should return true if the optimizer can increase or decrease work. If they return true the associated optimization function will be called.
increaesWork( delta : Number ) : void
decreaseWork( delta : Number ) : void
Functions to increase or decrease work and optimize the frame if needed. These are called if the associated canIncreaseWork
or canDecreaseWork
return true.