Do calls to requestAnimationFrame stack? - javascript

I have the following functions
function render() {
init = timeStep(init)
if (steps <= yQuotient) {
requestAnimationFrame(render)
}
}
export function fire() {
setup();
render();
}
I then call fire() whenever I need to start/restart the application. My question is whether this will result in multiple requestAnimationLoop()'s active simulatenously as they build up like eventListeners do when called repeatedly, presumably affecting performance.
Do I need to amend my fire() function to something like this?
export function fire() {
cancelAnimationFrame(render)
setup();
render();
}
Or does requestAnimationLoop() just automatically know to end it's current loop when a new one is started?

Yes, calls to requestAnimationFrame will stack all the callbacks into the map of animation frame callbacks.
Each painting frame, the keys of this map are all gotten and looped over, allowing to schedule a new animation frame callback from such a callback, for firing at the next painting frame.
So yes, scheduling multiple times the same callback in the same event loop iteration will worsen the performances of your page, since it will have to do multiple times the same work.
It's quite unclear how all your system works, but a quite often used strategyfor games and interactive animations is to keep a single main animation loop running all the time and calling sub-tasks. External events only modifying states of objects that get visited from these sub-tasks.

Related

How to write Javascript code that doesn't block the ui

I'm working on a javascript application that performs 2 jobs.
The first job is more important and needs to run at 60fps. The other job is a "background" job that still needs to run but it's okay if it takes longer.
Normally the way I would do this is have the more important job's code in a RequestAnimationFrame loop, and put the background job on a web worker.
However the main job is already spawning 2 web workers, and I don't want to spawn a third for context switching and memory consumption reasons.
There is ~8 ms of processing time left over on the RequestAnimationFrame loop that I have to work with for the background job to run on, however it is a job that will take about 100 ms to complete.
My question is there a way to write a loop that will pause itself every time the ui is about to be blocked?
Basically run as much code as you can until the remaining 8ms of time are up for the frame, and then pause until there is free time again.
This is currently experimental technology which isn't well-supported yet, but: There's requestIdleCallback, which:
...queues a function to be called during a browser's idle periods. This enables developers to perform background and low priority work on the main event loop, without impacting latency-critical events such as animation and input response. Functions are generally called in first-in-first-out order; however, callbacks which have a timeout specified may be called out-of-order if necessary in order to run them before the timeout elapses.
One of the key things about rIC is that it receives an IdleDeadline object which
...lets you determine how much longer the user agent estimates it will remain idle and a property, didTimeout, which lets you determine if your callback is executing because its timeout duration expired.
So you could have your loop stop when the deadline.timeRemaining() method returns a small enough number of remaining milliseconds.
That said, I think I'd probably add the third worker and see what it looks like in aggressive testing before I tried other approaches. Yes, it's true that context-switching is costly and you don't want to overdo it. On the other hand, there's already plenty of other stuff going on on mobiles and architectures these days are quite fast at context switching. I can't speak to the memory demands of workers on mobiles (haven't measured them myself), but that's where I'd start.
I recommend requestIdleCallback() as the accepted answer does, but it is still experimental and I like coming up with stuff like this. You might even combine rIC with this answer to produce something more suited to your needs.
The first task is to split up your idle code into small runnable chunks so you can check how much time you have/spent between chunks.
One way is to create several functions in a queue that do the work needed, such as unprocessed.forEach(x=>workQueue.push(idleFunc.bind(null,x)));}, then have an executor that will at some point process the queue for a set amount of time.
If you have a loop that takes awhile to finish, you could use a generator function and yield at the end of each loop, then run it inside recursive calls to setTimeout() with your own deadline or requestIdleCallback().
You could also have a recursive function that when processed, would add itself back to the end of the queue, which could help when you want to give other work time to run or when creating a function per piece of work would be absurd (e.g., hundreds of array items bound to a function that together only take 1ms to process).
Anyway, here's something I whipped up out of curiosity.
class IdleWorkExecutor {
constructor() {
this.workQueue=[];
this.running=null;
}
addWork(func) {
this.workQueue.push(_=>func());
this.start();
}
//
addWorkPromise(func) {
return new Promise(r=>{
this.workQueue.push(_=>r(func()));
this.start();
});
//DRY alternative with more overhead:
//return new Promise(r=>this.addWork(_=>r(func())));
}
sleep(ms) {
return new Promise(r=>setTimeout(r,ms));
}
//Only run the work loop when there is work to be done
start() {
if (this.running) {return this.running;}
return this.running=(async _=>{
//Create local reference to the queue and sleep for negligible performance gain...
const {workQueue,sleep}=this;
//Declare deadline as 0 to pause execution as soon as the loop is entered.
let deadline=0;
while (workQueue.length!==0) {
if (performance.now()>deadline) {
await sleep(10);
deadline=performance.now()+1;
}
/*shift* off and execute a piece of work. *push and shift are used to
create a FIFO buffer, but a growable ring buffer would be better. This
was chosen over unshift and pop because expensive operations shouldn't
be performed outside the idle executor.*/
workQueue.shift()(deadline);
}
this.running=false;
})();
}
}
//Trying out the class.
let executor=new IdleWorkExecutor();
executor.addWork(_=>console.log('Hello World!'));
executor.addWorkPromise(_=>1+1).then(ans=>{
executor.addWork(_=>console.log('Answer: '+ans));
});
//A recursive busy loop function.
executor.addWork(function a(counter=20) {
const deadline=performance.now()+0.2;
let i=0;
while (performance.now()<deadline) {i++}
console.log(deadline,i);
if (counter>0) {
executor.addWork(a.bind(null,counter-1));
}
});
If you can use requestIdleCallback() in your code, adding it to IdleWorkExecutor is pretty simple:
function rICPromise(opt) {
return new Promise(r=>{
requestIdleCallback(r,opt);
});
}
if (!deadline||deadline.timeRemaining()>0) {
deadline=await rICPromise({timeout:5000});
}

Why is requestAnimation frame called multiple times for some frames, and not at all for others?

I have a redux app which uses requestAnimationFrame to render a new state on every TICK, however Google Chrome seems to be calling requestAnimationFrame up to 3 times(!!) for some ticks, and not at all for others, leading to janky animations.
In React development mode:
In React NODE_ENV=production mode:
It's rendering at 60fps already, so multiple unnecessary requestAnimationFrame callbacks per 16ms frame are highly wasteful of CPU resources. Frames that don't have a tick also cause laggy-ness in the animations. My tick function only takes 4ms to compute (well under the 16ms budget), and I only have one callback running on the page.
Here is the animation code that handles dispatching my redux call on every tick.
class AnimationHandler {
constructor(store) {
this.store = store
this.animating = false
store.subscribe(::this.handleStateChange)
}
handleStateChange() {
if (!this.animating) {
this.tick()
}
}
tick(timestamp) {
this.animating = true
this.store.dispatch({
type: 'TICK',
current_timestamp: (new Date).getTime(),
// reducer handles updating rendered state to this point in time
})
window.requestAnimationFrame(::this.tick)
}
}
window.animations = new AnimationHandler(window.store) // redux store
What could be causing Chrome to do this, and how can I make it so that there's one consistent tick() call per frame rendered, no more no less?
I had the same problem. It was a bug on my side and I suspect it always is.
In your code , calling tick() schedules an infinite loop of requestAnimationFrame since requestAnimationFrame() is always called as part of tick(). This is normal.
It's not a problem if tick() is called only once outside of the loop. It's not happening in your example, but it's likely that you initiated several loops in the real application.
Just in case, I would change your code to:
handleStateChange() {
if (!this.animating) {
this.animating = true // immediate
this.tick()
}
}
In my case, I was reusing the tick() method, adding layers of requestAnimationFrame().
Example: (WRONG!)
// init
window.requestAnimationFrame(tick)
// on some other events
tick() // WRONG! will create a new layer of requestAnimationFrame loop!

Javascript update functions

I'm getting into game developing online. I am trying to make an online FPS game, and I've only gotten to the point where I need to update my character. I am trying to keep my code simple, using only a draw and update function. When the html loads, I execute both: (Is this necessary?)
<body onload='DRAW(); UPDATE();'>
The draw function draws the player to the screen, and the update is supposed to check for a keypress to move the character. I am trying to make the script update using this:
function UPDATE()
{
update = setInterval(UPDATE, 60);
}
and to my knowledge, it is working fine because when I try and edit code in my online IDE (c9.io) which I use to test the site, it freezes when the site is running. I am also calling eventListeners in the draw function. (Is this proper if I want to test for a key down every frame?)
function DRAW()
{
window.addEventListener('keydown', function (e) {
keys.keys = (keys.keys || []);
keys.keys[e.keyCode] = true;
});
window.addEventListener('keyup', function (e){
keys.keys[e.keyCode] = false;
});
}
My questions are:
Is there an easier way to make a script update every frame?
Is there a JavaScript addon (like Three.js) I can use to make
developing this easier on myself?
Any knowledge is greatly appreciated.
This makes everything crash:
function UPDATE()
{
update = setInterval(UPDATE, 60);
}
You are recursively creating a new interval every 60ms; the first time you call UPDATE, you create an interval that creates a new interval every 60ms. All newly create intervals do the same. Don't really know what you actually want to do here.
I am also calling eventListeners in the draw function. (Is this proper
if I want to test for a key down every frame?)
It's fine to create eventlisteners in the draw function, provided you only call this function once. Which I guess you don't. Each time you call DRAW() a new set of eventlisteners will be added, and you really don't want that.
What you need is a form of game loop. Explaining how to create an FPS game is a bit more than I can do, but you can start by looking at this article Anatomy of a video game

Running multiple jquery functions together

I know this question has been asked a lot of times, and I have seen the solutions to them on SO as well as other forums. Most of the times the solution suggested is to use Web Workers.
A game I'm developing requires me to run multiple functions at the same time. One of them is an on click function and other is a setInterval.
My approach at doing this can be seen here in this JSFiddle. (keep clicking in gray area to make player jump).
The whole idea is to continuously spawn those blue obstacles after an interval of 1000ms.
In my earlier approach the obstacles would spawn only when I click to make player jump, otherwise they wouldn't as expected.
How can I run such two functions side by side in order to achieve
the aim of spawning obstacles while also making player jump.
Secondly, what would be the best approach to carry out this process
in view of game development i.e attaining a certain level of
efficiency so that the animations are not affected.
Here is the HTML and Javascript code I've been working on:
<div class="container">
<div class="player"></div>
<div class="obstacle-container">
<div class="obstacle"></div>
</div>
</div>
$.fn.animator = function () {
var hit_list, done = false;
$(".container").click(function () {
if (!done) {
$(".obstacle").stop().animate({
left: "-=105%"
}, 10000, "linear");
$(".player").stop().animate({
bottom: "+=100px"
}, {
duration: 300,
complete: function () {
$(".player").animate({
bottom: "0"
}, 800);
},
step: function () {
//Test for collision
hit_list = $(".player").collision(".obstacle");
if (hit_list.length !== 0) {
$(function () {
if (!done) {
$(".container").append("Game Over!");
return false;
}
});
done = true;
}
}
});
}
});
};
$(function () {
$('.container').animator();
});
var interval = null;
$(".obstacle-container").obstacle_generator();
$.fn.obstacle_generator = function () {
interval = setInterval(function () {
$(".obstacle-container").append('<div class="obstacle"></div>');
}, 1000);
};
The generic concept you want to investigate is known as a game loop.
Almost every game will be built using some variant of this system:
Initialise game
Loop:
Check for user input
Update any actors
Draw the scene
Wait until it's time to repeat
A game running at 60 frames per second would perform this loop 60 times per second, or about once every 16ms.
Compared to your original question, you wouldn't need to be running multiple execution threads (running multiple functions together) to achieve this.
You are, in a way, already using a similar loop. jQuery maintains its own loop for updating animations. Where you are checking for collisions as part of your animation step, this is the sort of thing you would do in a hypothetical Player.update() method. You want to move this code out of jQuery, and in to a loop that you control.
Since you're running in a browser, the generic game loop becomes a bit more simple:
Check for user input - this can still be handled by event handlers, jQuery or not. Rather than directly changing properties like CSS position, though, they should act upon the state of the game object. For example, by changing the velocity of a Player object.
Update any actors - the important part of your loop. You should check how many milliseconds have passed since you last looped, since the browser doesn't guarantee that your code will be run exactly, or at least, 60 times per second. You should then loop through all of your game objects and update them all. In your Player.update() method, you would want to move it according to its velocity and the time passed, for example.
Draw the scene - if you're using DOM elements, then the browser handles drawing for you, of course. If you were using a <canvas> element, then you would do drawing yourself as part of the loop here.
Wait until it's time to repeat - this will be up to the browser to do for you, as part of normal setInterval/setTimeout behavior.
A simple game loop in JavaScript can look like this:
var gameObjects = [];
// Initialise game, create player objects etc, add them to the array
var gameLoop = function() {
// Loop through gameObjects, and call their respective update methods
};
setInterval(gameLoop, 16); // Try to run the loop 60 times per second.
In a complex game, you wouldn't have just a basic array to hold all game objects, this is just an basic example.

Game loop crashing my browser

I'm trying to write my first html5 game. However, the game loop causes my browser to become unresponsive (eventually being shut down by the browser). I created a state machine:
while(state != State.EXIT){
switch(state){
case State.SPLASH:
break;
case State.HOW_TO:
break;
case State.PLAY:
oldTime=Date.now();
state=gameLoop();
break;
case State.GAME_OVER:
break;
default:
state=State.EXIT;
}
}
That seems to be working okay. So, then, here's the game loop:
function gameLoop(){
var newTime=Date.now();
var delta=newTime-oldTime;
update(delta/1000);
render();
oldTime=newTime;
return state;
}
This is where the crash happens. If I take out the return statement, it returns null or whatever javascript returns. And, that's fine. It runs once and exits. However, if I leave it in there, this is where the browser seizes up. The update function gives my character the ability to move and the render function draws one image to the screen. Very simple stuff.
NOTE: This is being written in the canvas element if that matters.
SOLUTION! I created a stateSelector() function which contains the switch statement above(without the while). However, rather than state=gameLoop, I used interval=setInterval(gameLoop, 1). Then, I use clearInterval(interval) when I want to stop, followed immediately by stateSelector(). Obviously, if I want to change the state, I do that before calling the stateSelector function. I could probably have it take in a parameter containing the state I want to go into, but that a small change that I could evaluate later. I just wanted to announce my solution in case anyone else runs into this.
JavaScript is single-threaded and runs (in effect) in the GUI thread in all common browser environments. When you're JavaScript, the UI of the browser is not updated until the JavaScript finishes running.
You're using a while loop that will never finish, and so the UI will never get updated. To fix this, you need to restructure a little: render a frame and then tell the browser you want to render another frame soon; it can update the UI and do other browsery things and then it can get back to you to render another frame.
Implementation
There's an experimental new function called requestAnimationFrame that can do this. Since it's still experimental, to use it, you need to check for browser-specific versions of it, or if it's not available at all, provide a fallback. Here are some of the names of the browser-specific versions:
mozRequestAnimationFrame for Gecko (Firefox)
webkitRequestAnimationFrame for WebKit (Chrome and Safari)
msRequestAnimationFrame for Trident (Internet Explorer)
So if an unprefixed requestAnimationFrame is available, use that. If that's not available but a prefixed one is, use that. If none of those work, you can use a fallback:
function fallbackRequestAnimationFrame(func) {
setTimeout(func, 10); // Schedule func to be run in 10 milliseconds.
}
Here's a slightly-modified version of the code found on MDN:
var myRequestAnimationFrame =
window.requestAnimationFrame
|| window.mozRequestAnimationFrame
|| window.webkitRequestAnimationFrame
|| window.msRequestAnimationFrame
|| fallbackRequestAnimationFrame;
Once you've figured out which requestAnimationFrame function you can use, you can change your game loop (which seems to be not the gameLoop function, which has no loops, but rather the while loop) to look like this:
function runFrame() {
switch(state) {
// state handling code
}
if(state != State.EXIT) {
myRequestAnimationFrame(runFrame);
}
}
Then start it off:
runFrame();
I think you may need some sort of a pause, if you're looping with no pause it will consume all of the CPU processing the loop over and over, preventing the page from rendering.
JavaScript runs on the same thread the browser uses to render the page, so if you write an infinite loop the browser never gets control back to refresh the page. (Modern browsers detect "long running" loops and offer the user a chance to abort them, but that doesn't help you with your game.)
You need to use either setTimeout() or setInterval() with some variation on the following:
function gameLoop() {
// do calculations
// render
// etc
if (!gameOver)
setTimeout(gameLoop, 30);
}
gameLoop();
(Note: your original gameLoop() function doesn't actually loop - your loop is controlled outside the function - whereas what I've just showed does loop.)
The setTimeout() function queues up a function to be run later and then immediately continues with the next line of code. When the current code finishes executing the browser then gets control back to update the display, etc. Then after (approximately) the specified interval (in milliseconds) the queued function is executed.
The effect above is similar to a recursive call where a function calls itself directly, except using setTimeout() yields control back to the browser in the meantime.
Outside the gameLoop() function you can then define event handlers for key and/or mouse events, and have those update variables that gameLoop() will use to decide how to, e.g., move the player's character, something like I said in this answer to another question.
Generally, programmers make loops play nice by adding sleep(s) or yield() calls, but since javascript's event driven model lacks these, you would instead replace your loop with a setInterval() which could call a function containing something like your loop body every specified interval, say, every 33 milliseconds, for a 30 fps experience.

Categories

Resources