4

The problem is as such:

In a js and asm.js based multiplayer game I've got two loops.

One handles the actual game ticks, like unit position, velocity and combat.

The other handles rendering of this world onto the canvas for the user to see.

What I'd like to happen is when the processor/GPU(they made those the same thing on some machines now, can't say I'm happy about that) gets encumbered too much the rendering loop should skip and thus stop changing the canvas. I.e. freezing the game screen in a lag pike.

Meanwhile the little processing power left is used to successfully complete the actual game tick preventing de-synchronisation with other game clients.

(It's an RTS-like game when it comes to load so the user input instead of positions of all objects are sent over the net).

Failing this the client would have to be kicked by the other clients or all clients would have to pause for him to reconnect and resync. i.e. bad bad bad!

A sloppy makeshift way to do this would probably be by using timestamps and terminate the graphic loop if it won't be complete by a certain time. One would presumably do this by determining max execution time for the packet types on the stack of the loop and immediately terminate the loop if the "time to execute value" of all packets together is too great to be dealt with within the resource capacity the timestamps are indicating by slowdown measurement. Hell, maybe that's radical but perhaps even skip-terminating the graphic loop when any slowdown is detected just to be sure to avoid desync.

So priorotizing one loop over another(both handling ticks) and making the second one skip if a shortage in resource is detected to ensure the first one always completes it's tick within each timeframe(10 ticks per second here).

Any possibilities or best practice methods you guys can inform me on?

EDIT: Please focus on the ability to measure availability of cpu resources and the skipping/termination for one tick of the graphic loop if these resources would not available enough to finish both loops (i.e. if the loops won't finish in the 100ms timeframe after which the next loop tick should already be firing, don't start/terminate the graphics loop).

Cookie
  • 73
  • 1
  • 5
  • Don't you thought about have two `function` running through two `setInterval()`? – Claudio Santos Oct 24 '13 at 22:47
  • 1
    @ClaudioSantos Eh... I'm not seeing how that would ensure that the first loop would be able to complete it's tick every time when resources are too low to complete both... You might've not fully understood the question I'm afraid. – Cookie Oct 25 '13 at 07:30
  • I understand that you need two process running in background. The second thing, that's harder, is to monitoring the process, but I try first to create the two process in background. – Claudio Santos Oct 25 '13 at 10:58
  • Might I add for the eyes of anyone else theorising on what to use for these loops. I've read that `setInterval()` behaves as such: "The function will reliably be triggered, regardless of tab focus, at intervals of ***at least*** ***value*** ms, but usually slightly more due to overheads and delays." – Cookie Oct 25 '13 at 15:20

2 Answers2

1

One solution would be to use a web worker to do your world update loop and then the normal javascript loop to do the render. You would need to hand the state back and forthright o /from the web worker but the render loop would only draw on the updated data.

An advantage is that you could still have reactionary display code on the main ui loop.

This also have the advantage of the fact that the web worker could be using a different core and with multiple web workers you could use multiple extra cores

0

Fo the logical loop, i would take setInterval, and for the paint - requestAnimationFrame. And even more - the callback at requestAnimationFrame also receives a timestamp, so you can track timestamps and skip single frame if some lack appear.


the processor is able to handle other tasks while also rendering the animation

This statement is wrong - processor can handle only one task, and requestAnimationFrame is not actually the Rendering, it is your callback - generic javascript. You can think about it like a setTimeout. The only difference is, that it tries to run the callback on next free framerate's frame. That's why it is much better than setTimeout. So for the animations you must use the requestAnimationFrame. Other good part about it is, when the webpage is in background(other tab opened). Then the callback wont be called until it comes to the foreground. This saves processor time, as nothing is calculated in that callback.

Going back to your question: You now but, that only one callback can be processed in a time, so if the processor is in a particular time busy with the logical function, then the callback of the animation loop won't be fired. In that case it calls 'lag'. But as I understood, it is actually the desired behavior - to give the logical callback function more time. But there is other side. What if your animation function is busy, when the time for logical function came to be fired? In this case it will be fired only when animation function ends. There is nothing to do about it. If your animation function is 'heavy', you could only try to split it for 2 frames. One frame - prepare everything for render, the second one - render.

But anyway, you never become millisecond-perfect interval or timeout in javascript. As it want be called until event-loop is not free. To get the idea:

var seconds = 0;
setInterval(function(){ seconds++; var x = 10e8; while(--x); }, 1000);

Depends on you CPU, but after 10 seconds time, variable 'seconds' will be much less then 10.

And one more thing, if you really rely on time, then it is safer to use Date.now() to synchronize next logical tick:

var setLogicalLoop = (function(){

    var _startedAt,
        _stop,
        _ms;


    function frame(){
        if (_stop === true) 
            return;

        // calculations

        var dt = Date.now() - _startedAt,
            diff = dt % _ms;

        setTimeout(frame, ms - diff);
    };

    return function (callback, ms){

        _startedAt = Date.now();
        _stop = false;

        setTimeout(frame, ms);


        return function(){
            _stop = true;
        };
    };
});


// -> start
var stopLoop = setLogicalLoop(myFunction, ms);
// -> stop
stopLoop();
tenbits
  • 7,568
  • 5
  • 34
  • 53
  • Yes I've read good things about requestAnimationFrame and was considering it as the main option for the graphic loop rendering. However there are still things I'm researching about it, for example I've heard that with requestAnimationFrame: `Rather than being overloaded with rendering tasks, the processor is able to handle other tasks while also rendering the animation. In fact, it is able to determine a frame rate that works with the other tasks it is handling.` I need more time to research the mechs behind this and how it might be used to achieve full priority to the first loop on each tick. – Cookie Oct 25 '13 at 13:44
  • Another question would be why the text speaks about processor and not GPU, I was under the impression one could accelerate graphics by preforming css tasks(which is accelerated by GPU in the browser I've read). **The real question however remains** how to ensure the graphics loop skips if the first loop would otherwise be in danger of lacking resources to complete itself in time and face desync with other clients. – Cookie Oct 25 '13 at 13:53