Improving Html5 canvas with double buffering? - javascript

I'm writing a game in javascript canvas, and I experience lag sometimes. I think this is because I draw pretty much to the canvas.
My background for example isn't static, it moves from the right to the left, so each time, I have to clear the entire canvas. And I'm not clearing with just a color, I'm clearing with an image that moves every cycle of the gameloop.
I think this is an expensive operation and I was thinking if there is something to make this operation less expensive, like for example using double buffering for clearing the background (no idea if you can use double buffering just for clearing the background?).
Or should I use double buffering in the entire game?
(I don't think so because I've read that the browser already does that for you)

Since you experience your lag 'sometimes', and it's not a low frame rate issue, i would rather move my eyes towards the evil garbage collector : whenever it triggers, the application will freeze for up to a few milliseconds, and you'll get a frame miss.
To watch for this, you can use google profiling tools, in TimeLine / Memory / press the record button : you can see that a GC occurred when there's a sudden drop in the memory used.
Beware when using this tool, that it slows down a bit the app, and it creates garbage on its own (!)...
Also, since any function call for example creates a bit of garbage, so you can't have a full flat memory line.
Below i show the diagram of a simple game i made before i optimized memory use,
there's up to 5 GC per second :
And here the diagram of the very same game after memory optimisation : there's something like 1 GC per second. Because of the limitations i mentioned above, it is in fact the best we can get, and the game suffers no frame drop and feels more responsive.
To avoid creating garbage
- never create object, arrays or functions.
- never grow or reduce an array size.
- watch out for hidden object creation : Function.bind or Array.splice are two examples.
You can also :
- Pool (recycle) your objects
- use a particle engine to handle objects that are numerous / short lived.
I made a pooling lib (here) and a particle engine (here) you might use if you're interested.
But maybe first thing to do is to seek where you create objects, and have them created only once. Since js is single-threaded, you can in fact use static objects for quite a few things without any risk.
Just one small expl :
function complicatedComputation(parameters) {
// ...
var x=parameters.x, y = parameters.y, ... ...
}
// you can call creating an object each time :
var res = complicatedComputation ( { x : this.x, y : this.y, ... ... } );
//... or for instance define once a parameter object :
complicatedComputation.parameters = { x :0, y:0, ... ... };
// then when you want to call :
var params = complicatedComputation.parameters;
params.x = this.x ; params.y = this.y; ... ...
var res = complicatedComputation(params);
It has the drawback that previous calls parameters remains, so you don't get undefined if you don't
set them, but previous value, so you might have to change bit your function.
But on the other hand, if you call several times the function with similar parameters, it comes very handy.
Happy memory hunting !

Double buffering is a technique to reduce flickering: You draw to one buffer while another buffer is displayed, and then swap them out in a single operation, so the user doesn't see any of the drawing in a partial state.
However, it does not really help for performance problems at all, as there is still the same amount of drawing. I would not try to use double buffering if you don't have a problem with flickering, as it requires more memory and flickering may be implicitly prevented by the system by similar or other means.
If you think drawing the background is too expensive, there are several things that you could look into:
Do you scale the background image down while drawing? If so, create a downscaled version once and use this for clearing the background, reducing the drawing costs per iteration.
Remember the "dirty" areas and just draw the portions of the background that were obscured. This will add some management overhead but reduce the number of pixels that need to be touched significantly
Make the background the background image of the canvas DOM element and just clear the canvas to transparency in each iteration. If the canvas is very large, you could make this faster by remembering the "dirty" areas and just clearing them.
Are you sure painting the background is the main cause of lag? Do you still have lag when you only repaint the background and do not do much else?

Related

Best approach for collision detection with HTML5 and JavaScript?

I'm trying to make a little platform game with pure HTML5 and JavaScript. No frameworks.
So in order to make my character jump on top of enemies and floors/walls etc., it needs some proper collision detection algorithms.
Since I'm not usually into doing this. I really have no clue on how to approach the problem.
Should I do a re-check in every frame (it runs in 30 FPS) for all obstacles in the Canvas and see if it collides with my player, or is there a better and faster way to do so?
I even thought of making dynamic maps. So the width, height, x- and y coordinates of the obstacle are stored in an object. Would that make it faster to check if it's colliding with the player?
1. Should I re-check in every frame (it runs on 30 FPS)?
Who says it runs in 30 FPS? I found no such thing in the HTML5 specification. Closest you'll get to have anything to say about the framerate at all is to programmatically call setInterval or the newish, more preferred, requestAnimationFrame function.
However, back to the story. You should always look for collisions as much as you can. Usually, writing games on other platforms where one have a greater ability to measure CPU load, this could be one of those things you might find favorable to scale back some if the CPU has a hard time to follow suit. In JavaScript though, you're out of luck trying to implement advanced solutions like this one.
I don't think there's a shortcut here. The computer has no way of knowing what collided, how, when- and where, if you don't make that computation yourself. And yes, this is usually, if not at all times even, done just before each new frame is painted.
2. A dynamic map?
If by "map" you mean an array-like object or multidimensional array that maps coordinates to objects, then the short answer has to be no. But please do have an array of all objects on the scene. The width, height and coordinates of the object should be stored in variables in the object. Leaking these things would quickly become a burden; rendering the code complex and introduce bugs (please see separation of concerns and cohesion).
Do note that I just said "array of all objects on the scene" =) There is a subtle but most important point in this quote:
Whenever you walk through objects to determine their position and whether they have collided with someone or not. Also have a look at your viewport boundaries and determine whether the object are still "on the scene" or not. For instance, if you have a space craft simulator of some kind and a star just passed the player's viewport from one side to the other and then of the screen, and there is no way for the star to return and become visible again, then there is no reason for the star to be left behind in the system any more. He should be deleted and removed. He should definitely not be stored in an array and become part of a future collision detection with the player's avatar! Such things could dramatically slow down your game.
Bonus: Collision quick tips
Divide the screen into parts. There is no reason for you to look for a collision between two objects if one of them are on left side of the screen, and the other one is on the right side. You could split up the screen into more logical units than just left and right too.
Always strive to have a cheap computation made first. We kind of already did that in the last tip. But even if you now know that two objects just might be in collision with each other, draw two logical squares around your objects. For instance, say you have two 2D airplanes, then there is no reason for you to first look if some part of their wings collide. Draw a square around each airplane, effectively capturing their largest width and their largest height. If these two squares do not overlap, then just like in the last tip, you know they cannot be in collision with each other. But, if your first-phase cheap computation hinted that they might be in collision, pass those two airplanes to another more expensive computation to really look into the matter a bit more.
I am still working on something i wanted to make lots of divs and make them act on physics. I will share somethings that weren't obvious to me at first.
Detect collisions in data first. I was reading the x and y of boxes on screen then checking against other divs. After a week it occurred to me how stupid this was. I mean first i would assign a new value to div, then read it from div. Accessing divs is expensive. Think dom as a rendering stage.
Use webworkers if possible easy.
Use canvas if possible.
And if possible make elements carry a list of elements they should be checked against for collision.(this would be helpful in only certain cases).
I learned that interactive collisions are way more expensive. Because you have to check for changes in environment while in normal interaction you simulate what is going to happen in future, and therefore your animation would be more fluid and more cpu available.
i made something very very early stage just for fun: http://www.lastnoob.com/

What's the fastest way to draw to an HTML 5 canvas?

I'm investigating the possibility of producing a game using only HTML's canvas as the display media. To take an example task I need to do, I need to construct the game environment from a number of isometric tiles. Of course, working in 2D means they by necessity come in rectangular packages so there's a large overlap between tiles.
I'm old enough that the natural solution to this problem is to call BitBltMasked. Oh wait, no, an HTML canvas doesn't have something as simple and as pleasing as BitBlt. It seems that the only way to dump pixel data in to a canvas is either with drawImage() which has no useful drawing modes that ignore the alpha channel or to use ImageData objects that have the image data in an array.. to which every. access. is. bounds. checked. and. therefore. dog. slow.
OK, that's more of a rant than a question (things the W3C like tend to provoke that from me), but what I really want to know is how to draw fast to a canvas? I'm finding it very difficult to ditch the feeling that doing 100s of drawImages() a second where every draw respects the alpha channel is inherently sinful and likely to make my application perform like arse in many browsers. On the other hand, the only way to implement BitBlt proper relies heavily on a browser using a hotspot-like execution technique to make it run fast.
Is there any way to draw fast across every possible implementation, or do I just have to forget about performance?
This is a really interesting problem, and there's a few interesting things you can do to solve it.
First, you should know that drawImage can accept a Canvas, not just an image. The "sub-Canvas"es don't even need to be in the DOM. This means that you can do some compositing on one canvas, then draw it to another. This opens a whole world of optimization opportunities, especially in the context of isometric tiles.
Let's say you have an area that's 50 tiles long by 50 tiles wide (I'll say meters for the sake of my own sanity). You might divide the area into 10x10m chunks. Each chunk is represented by its own Canvas. To draw the full scene, you'd simply draw each of the chunks' Canvas objects to the main canvas that's shown to the user. If only four chunks (a 20x20m area), you would only perform four drawImage operations.
Of course, each of those individual chunks will need to render its own Canvas. On game ticks where nothing happens in the chunk, you simply don't do anything: the Canvas will remain unchanged and will be drawn as you'd expect. When something does change, you can do one of a few things depending on your game:
If your tiles extend into the third dimension (i.e.: you have a Z-axis), you can draw each "layer" of the chunk into its own Canvas and only update the layers that need to be updated. For example, if each chunk contains ten layers of depth, you'd have ten Canvas objects. If something on layer 6 was updated, you would only need to re-paint layer 6's Canvas (probably one drawImage per square meter, which would be 100), then perform one drawImage operation per layer in the chunk (ten) to re-draw the chunk's Canvas. Decreasing or increasing the chunk size may increase or decrease performance depending on the number of update you make to the environment in your game. Further optimizations can be made to eliminate drawImage calls for obscured tiles and the like.
If you don't have a third dimension, you can simply perform one drawImage per square meter of a chunk. If two chunks are updated, that's only 200 drawImage calls per tick (plus one call per chunk visible on the screen). If your game involves very few updates, decreasing the chunk size will decrease the number of calls even further.
You can perform updates to the chunks in their own game loop. If you're using requestAnimationFrame (as you should be), you only need to paint the chunk Canvas objects to the screen. Independently, you can perform game logic in a setTimeout loop or the like. Then, each chunk could be updated in its own tick between frames without affecting performance. This can also be done in a web worker using getImageData and putImageData to send the rendered chunk back to the main thread whenever it needs to be updated, though making this work seamlessly will take a good deal of effort.
The other option that you have is to use a library like pixi.js to render the scene using WebGL. Even for 2D, it will increase performance by decreasing the amount of work that the CPU needs to do and shifting that over to the GPU. I'd highly recommend checking it out.
I know that GameJS has blit operations, and I certainly assume any other html5 game libraries do as well (gameQuery, LimeJS, etc etc). I don't know if these packages have addressed the specific array-bounds-checking concern that you had, but in practice their samples seem to work plenty fast on all platforms.
You should not make assumptions about what speedups make sense. For example, the GameJS developer reports that he was going to implement dirty rectangle tracking but it turned out that modern browsers do this automatically---link.
For this reason and others, I suggest to get something working before thinking about the speed. Also, make use of drawing libraries, as the authors have presumably spent some time optimizing performance.
I have no personal knowledge about this, but you can look into the appMobi "direct canvas" HTML element which is allegedly a much faster version of normal canvas, link. I'm confused about whether this works in all browsers or just webkit browsers or just appMobi's own special browser.
Again, you should not make assumptions about what speedups make sense without a very deep knowledge of web browser internal processes. That webpage about "direct canvas" mentions a bunch of things that slow down canvas-drawing: "Reflowing text, mapping hot spots, creating indexes for reference links, on and on." Alpha-blending and array-bounds-checking are not mentioned as prominent causes of slowness!
Unfortunately, there's no way around the alpha composition overhead. Clipping may be one solution, but I doubt there would be much, if any, performance gain. Not to mention how complicated such a route would be to implement on irregular shapes.
When you have to draw the entire display, you're going to have to deal with the performance hit. Although afterwards, you have a whole screen's worth of pre-calculated alpha imagery and you can draw this image data at an offset in one drawImage call. Then, you would only have to individually draw the new tiles that are scrolled into view.
But still, the browser is having to redraw each pixel at a different location in the canvas. Which is quite expensive. It would be nice if there was a method for just scrolling pixels, but no luck there either.
One idea that comes to mind is that you could implement multiple canvases, translating each individual canvas instead of redrawing the pixels. This would allow the browser to decide how to redraw those pixels, in a more native way, at least in theory anyway. Then you could render the newly visible tiles on a new, or used/cached, canvas element. Positioning it to match up with the last screen render.
But that's just my two blits... I mean bits... duh, I mean cents :]

What is the most processing efficient way to store mouse movement data in JavaScript?

I'm trying to record exactly where the mouse moves on a web page (to the pixel). I have the following code, but there are gaps in the resulting data.
var mouse = new Array();
$("html").mousemove(function(e){
mouse.push(e.pageX + "," + e.pageY);
});
But, when I look at the data that is recorded, this is an example of what I see.
76,2 //start x,y
78,5 //moved right two pixels, down three pixels
78,8 //moved down three pixels
This would preferably look more like:
76,2 //start x,y
77,3 //missing
78,4 //missing
78,5 //moved right two pixels, down three pixels
78,6 //missing
78,7 //missing
78,8 //moved down three pixels
Is there a better way to store pixel by pixel mouse movement data? Are my goals too unrealistic for a web page?
You can only save that information as fast as it's given to you. The mousemove events fires at a rate that is determined by the browser, usually topping at 60hz. Since you can certainly move your pointer faster than 60px/second, you won't be able to fill in the blanks unless you do some kind of interpolation.
That sounds like a good idea to me, imagine the hassle (and performance drag) of having 1920 mousemove events firing at once when you jump the mouse to the other side of the screen - and I don't even think the mouse itself polls fast enough, gaming mice don't go further than 1000hz.
See a live framerate test here: http://jsbin.com/ucevar/
On the interpolation, see this question that implements the Bresenham's line algorithm which you can use to find the missing points. This is a hard problem, the PenUltimate app for the iPad implements some amazing interpolation that makes line drawings look completely natural and fluid, but there is nothing about it on the web.
As for storing the data, just push an array of [x,y] instead of a string. A slow event handler function will also slow down the refresh rate, since events will be dropped when left behind.
The mouse doesn't exist at every pixel when you move it. During the update cycle, it actually jumps from point to point in a smooth manner, so to the eye it looks like it hits every point in between, when in fact it just skips around willy-nilly.
I'd recommend just storing the points where the mouse move event was registered. Each interval between two points creates a line, which can be used for whatever it is you need it for.
And, as far as processing efficiency goes...
Processing efficiency is going to depend on a number of factors. What browser is being used, how much memory the computer has, how well the code is optimized for the data-structure, etc.
Rather than prematurely optimize, write the program and then benchmark the slow parts to find out where your bottlenecks are.
I'd probably create a custom Point object with a bunch of functions on the prototype and see how it performs
if that bogs down too much, I'd switch to using object literals with x and y set.
If that bogs down, I'd switch to using two arrays, one for x and one for y and make sure to always set x and y values together.
If that bogs down, I'd try something new.
goto 4
Is there a better way to store pixel by pixel mouse movement data?
What are your criteria for "better"?
Are my goals too unrealistic for a web page?
If your goal is to store a new point each time the cursor enters a new pixel, yes. Also note that browser pixels don't necessarily map 1:1 to screen pixels, especially in the case of CRT monitors where they almost certainly don't.

javascript memory leak with HTML5 getImageData

I've been working on trying to make some visual effects for a javascript game I'm creating, and I noticed a piece of code that I'm using to modulate the color of my sprites makes my browsers memory usage go up and up, seemingly without limit.
You can see the code and the memory leak here: http://timzook.tk/javascript/test.html
This memory leak only happens in my updateimage() function when I call getImageData from my canvas context every frame (via setInterval) in order to make a new ImageData object to recolor. I would have thought that javascript's garbage collector would be destroying the old one, but if not I have no idea how to destroy it manually. Any help figuring out why it does this or how to fix it would be appreciated.
My question is very similar to this one: What is leaking memory with this use of getImageData, javascript, HTML5 canvas However, I NEED my code to run every frame in the functioned called by setInterval, his solution of moving it outside the setInterval function isn't an option for me, and I can't leave a comment asking if he found some other method of solving it.
Note to people testing it out: Since this example uses getImageData, it can't be tested out locally just by throwing it in a .html file, a web server is required. Also it obviously uses HTML5 elements so some browsers won't work with it.
Edit: *SOLVED* Thanks, the solution below fixed it. I didn't realize that you could use a canvas element the same way as you could use an image in drawImage(), I restructured my code so it now uses significantly less memory. I uploaded this changed code to the page linked above, if anyone wants to see it.
You aren't getting a memory leak from your calls to getImageData(). The source of your problem is this line:
TempImg.src = ImgCanvas.toDataURL("image/png");
Effectively, every time that line of code is executed the browser "downloads" another image and stores it in memory. So, what you actually end up with is a cache that is growing very quickly. You can easily verify this by opening the site in Chrome and checking the resources tab of the developer tools (ctrl+shift+i).
You can work around this by making a TempImgCanvas and storing your image data on that canvas instead of keeping an image object updated after every call to your updateimage() loop.
I have to step away for a while, but I can work up an example in a few hours when I get back, if you would like.
Edit: I restructured things a bit and eliminated your caching issue. You just have to make two changes. The first is replacing your updateimage() function with this one:
function updateimage() {
var TempImgData = ImgContext.getImageData(0, 0, ImgCanvas.width, ImgCanvas.height);
var NewData = TempImgData.data;
var OrigData = ImgData.data;
//Change image color
var len = 4*ImgData.width*ImgData.height-1;
for(var i=0;i<=len;i+=4) {
NewData[i+0] = OrigData[i+0] * color.r;
NewData[i+1] = OrigData[i+1] * color.g;
NewData[i+2] = OrigData[i+2] * color.b;
NewData[i+3] = OrigData[i+3];
}
//Put changed image onto the canvas
ImgContext.putImageData(TempImgData, 0, 0);
}
The second is updating the last line in draw() to read as follows:
drawImg(ImgCanvas, Positions[i].x, Positions[i].y, Positions[i].x+Positions[i].y);
Using this updated code, we simply refer to the original base (white) image data and calculate new values based on that every time we go through the updateimage() function. When you call getImageData() you receive a copy of the image data on the canvas, so if you edit the canvas or the data, the other one remains untouched. You were already grabbing the original base image data to begin with, so it made sense to just use that instead of having to re-acquire it every time we updated.
Also, you'll notice that I modified your loop that changes the image color slightly. By obtaining a handle to the actual data array that you want to access/modify, you save yourself having to resolve the array location from the parent object every time you go through the loop. This technique actually results in a pretty nice performance boost. Your performance was fine to start with, but it can't hurt to be more efficient since it's pretty straight-forward.

Optimizing html5 canvas game

Right now I've got two game loops in a game I'm making. A draw loop that loops through an array of objects on screen and a logic loop that does game logic. I have the logic loop running about 10 more frames than the draw loop. I have this set up like this because doing game logic may take longer and I don't want it to interfere with the draw loop.
I have the logic loop set up like this:
vs.logicloop = function(){
vs.Gameloop();
//do the updating of object scripts
if(vs.windowActive){
var l = vs.scenegraph.length;
var i = 0;
while(i < l){
vs.scenegraph[i].logicScript();
i++;
}
}
//restart loop
setTimeout(vs.logicloop, 1000/(vs.fps+10));
};
and the draw loop like this:
vs.drawloop = function(){
//clear the screen
vsd.clr();
//goes through everything in the scene
//graph and draws it and runs each object's
//personal draw code
if(vs.windowActive){
var l = vs.scenegraph.length;
var i = 0;
while(i < l){
vs.ctx.save();
vs.scenegraph[i].update();
vs.scenegraph[i].draw();
vs.scenegraph[i].drawScript();
vs.ctx.restore();
i++;
}
}
//restart loop
setTimeout(vs.drawloop, 1000/vs.fps);
};
I'm using setTimeout because I heard that setInterval will cause loops to overlap if one isn't finished yet. Are there any optimizations I can do to really get some speed? Especially optimizing the game loops.
I've heard of some javascript engines getting thousands of objects on screen at once. I can't imagine how they do that, at most mine can get up to 100 objects on screen on a very old computer and about 700 on a reasonably stocked computer. And that's without a lot of game code running in the background and before I've worked out how to do pixel perfect collision detection and physics.
My process is to draw a background color fillRect over the canvas every draw loop, then looping through all the objects and drawing their draw code. Also it doesn't try to draw objects out of view.
This is my first paying job, and I really want to impress. Plus I can keep ownership of the engine once I'm done with the game.
Thanks A Lot
if you use floating values for sprite coordinates, try converting them to integers. that will cost you losing subpixel rendering but you will gain a lot of speed.
don't use images with odd widths. always use widths as powers of 2.
this one is hard to implement in some cases but if your game is suitable, don't clear screen and redraw everything each frame. instead, draw changed parts.
if you have to clear the canvas, don't draw a blank rectangle. try setting the canvas width/height again with the same size. that should reset the pixels faster than rectangle drawing.
rest of the methods i can suggest are not HTML5 canvas dependent but general subjects like using bit shiftings, inverse loops, and operator instead of modulo when possible, precalculations etc.
Oh geez, I could probably write you a full sonnet here if you posted all your code. Here's a quick rundown:
Read Emir's answer. All of those are good except the last one which is very dependent on the situation. Setting canvas.width = canvas.width to clear the canvas can be faster on some browsers but it also clobbers all canvas state (ie the last set fill and font) which can slow things down because setting those properties is actually painfully slow.
Read my articles on Canvas performance: http://simonsarris.com/blog/tag/performance
I have a lot of other tips saved up in a private document that I'm writing a small ebook out of. If you want early access to it I can probably allow that.
Pick up High performance JavaScript by Zakas and read it.
Don't use save() and restore() like you are in the quoted code unless you must. They are just slowing things down.
For the timer see http://paulirish.com/2011/requestanimationframe-for-smart-animating/
Multiple canvases for foreground-background-middleground can definitely help. Caching things on in-memory canvases can definitely help. It all depends on what you're drawing.
A lot of performance problems are due to death by a thousand tiny cuts. For instance:
vs.scenegraph[i].update();
vs.scenegraph[i].draw();
vs.scenegraph[i].drawScript();
To
var scene = vs.scenegraph[i];
scene.update();
scene.draw();
scene.drawScript();
Will help a minute amount. How many opportunities for minute-amount stuff you have I don't know - we'd need to see a lot more code.
I heard that setInterval will cause loops to overlap if one isn't finished yet.
This is false, JavaScript is single threaded. This means that if your first interval is still running when it hits the next step, the next step will be delayed until the first step has finished computing. This also means that you can't rely on setInterval to be accurate if you start doing a lot of computations.
I'd echo much of all has been said by other people. Integer values, use request animation frame, etc. If drawing text be careful how often you set fonts. Also you might find using an object pool can help if you are creating lots of temporary objects per frame.
As a general read on game loops I'd recommend this: http://www.koonsolo.com/news/dewitters-gameloop/

Categories