Which is faster -- stacking canvases or pixel manipulation? - javascript

I'm making a little Tetris clone using some of the HTML 5 features, and I've come to a bit of a dilemma.
My tetrominos aren't composed of blocks -- they have shading and shadows that depend on the rotation and thus each is its own image. Which means I can't use the normal "redraw everything" every time the game state changes, because that would mean keeping track of all the piece drop history and "replaying" it whenever I want to clear a line.
So my options are:
1) Create two canvas elements stacked upon each other. The one on the back is the "board" image. The one on top is the pieces canvas, where I can slice and rearrange things easily. This feels very hackish though, as I wanted to keep everything in one canvas element.
2) Create a "drawing" context and a "buffer" context. In the buffer context, I have all of the pieces and can manipulate it freely, and when it's time to draw the buffer to the screen, I getImageData, loop through all of the pixels, and composite them onto the board image. I then write this into the "drawing" context with putImageData. This hassle occurs because I can't find a way to take an ImageData structure and composite it onto a canvas element (you can do that with ImageElements using drawImage, but drawImage won't take an ImageData structure).
Neither solution seems very elegant to me.
Thanks

There's nothing 'hackish' about layering canvases.
Similar strategies are performed at several level in many systems from old coin-operated consoles to modern GPUs, passing though 80's 8bit home computers, and that's without mentioning classic hand-drawn cartoons (which were drawn on transparent acetate to use similar techniques).
in fact, even if you do other kinds of manipulation, it's quite likely you should do layering for lots of reasons.

Related

What is a high performance way of splitting up Sprite Sheets/ Chip Sets in JS?

I'm working on a 2d world editor for html/js, and trying to find the best way to split up a chip set (as seen below) into multiple little squares (chips).
Currently, I'm using a method similar to the default css sprite sheets method, of using background position to move the background of many little <div> elements until each displays one square/chip of the chip set.
It is working fine, with no big performance issues, but it seems like an overall clunky way to do it.
Other ways I've thought of doing it would be to slice the chip set into many temporary images and make an <image> element for each, or using <canvas> instead of <div>'s or <image>'s
Anyways, I'm looking for advice on the subject:
What is a high performance way of splitting up Sprite Sheets/ Chip Sets in JS?
example of a chip set
The way you're doing it right now should be pretty fast. You could essentially reimplement that on the canvas level by storing just the one image then using drawImage to only draw sections from it. To the best of my knowledge, this is the most common way and is very fast. At least that was my experience when I wrote a game engine using canvas.
Using the canvas is most likely faster and more memory efficient since you don't have the additional DOM overhead but you'd have to measure it for your specific use case and verify that.
Creating a bunch of individual images, as you suggested, would be very slow since it'd require copying portions of the image and creating an additional DOM element for each image.
In short: using the canvas will always be faster (for a comparable implementation) since you're almost directly interfacing with the GPU. Drawing from one image rather than copying it into multiple sub images will always be faster since you won't have duplicate memory sitting around and as long as you're drawing from the same sheet, the GPU doesn't have to switch out the texture.

When is it faster to clear the whole canvas?

I'm in the process of building a canvas-based game that will involve a fair number of sprites overlapping each other while in motion. I'm targeting mobile devices, so performance is a concern.
In my case, there are basically two ways to go about animating the sprites on the canvas:
Erase the entire canvas on every frame on which at least one sprite has moved/changed and redraw everything. This sounds pretty standard, from what I've seen.
Erase only those sprites that have moved/changed and redraw only those sprites that have moved/changed and those sprites which overlap those sprites which have moved/changed. Erasing those sprites which have moved/changed will cut chunks out of those sprites which they overlap, so the overlapped sprites will need to be redrawn.
Naively, it sounds like the second option would yield better performance. Less draw operations equals less work that the machine has to do, right? However, whereas the first option would involve (potentially) one clearRect() invocation per frame, the second would involve many clearRect() invocations per frame (albeit each one for a significantly smaller rectangle.) And that's not to mention the overhead of determining what needs to be erased and what overlaps what was just erased.
So, after a bit of reflection, I'm not sure which would be faster. I'm certain there are situations where the second would be faster, and I'd imagine there are situations where the first option would be faster.
Are there ways to determine now which would be the faster option? Or is my best bet to implement it both ways and do some benchmarking?
I've dealt with this same issue on a smaller scale and had a head-slapping moment when I realized you can separate out the different drawn elements onto different canvases that overlap each other!
One canvas for background, one for character sprites, etc.etc.
Checkout the answer to this question for a full framework for handling this style of drawing.

Is it better to have one big canvas or up to 100 small dynamically generated canvases?

I am working on a mobile web dice simulator. The initial prototype is here: http://dicewalla.com
I currently have one large canvas where I draw all the dice. I am planning to re-write the code in way that is more MVC and easier for me to update. I think it would be easier for me to generate a small canvas for each die object than to draw all of the dice on the big canvas and keep updating that big canvas.
My question is if there is a bad performance hit in having the browser create lots of little canvases vs one big one. It's hard to test locally, I was hoping someone here might know what the best practice is.
Multiple canvases usually allow for better performance as you're able to selectively re-render.
If you have only one canvas and want to update one die, you'll typically have to redraw the entire canvas. On the other hand, multiple canvases allow you to update only the dice that need to be redrawn. That's an increase in efficiency.
Further, you shouldn't see any noticeable difference in loading 1 canvas versus 100.
In terms of performance, like was mentioned earlier, there should be little difference between 1-100 canvas elements if you're not updating the graphics on a regular basis. (ie: static graphics / no animation)
Most of the references around the net regarding multiple canvases tend to deal with cases where you have multiple layers and need to handle drawing on top of other things with transparency.
That being said, what you're doing with dicewalla doesn't look like it will gain anything from having multiple canvases.
Also you can selectively redraw the regions of a single canvas to get better performance if updating the entire canvas is a bottleneck. This gives you the performance benefits of having multiple canvases without having to deal with managing and creating those elements.

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 :]

Break the scene to several canvases or keep redrawing one?

Intent on creating a canvas-based game in javascript I stand before a choice:
Should I performance-wise keep all the stuff happening in the screen in one canvas (all the moving characters, sprites) and redraw it at constant rate of, say, 60 FPS or should I break the scene into several smaller canvases thus removing the need of redundant redrawing of the scene? I could even create separate canvas elements for the characters' limbs and then do most of the animation by simply manipulating the CSS of the given canvas element (rotation, positioning, opacity).
To me the latter sounds way more plausible and easier to implement, but is it also faster? Also, shouldn't I perhaps use SVG, keep the characters and sprites as elements inside of it and manipulate their XML and CSS properties directly?
So what do you think is the most fitting solution to a scene with severals sprites and characters:
One canvas object redrawn manually (and wastefully) at FPS rate
Several canvas elements, redrawn manually in a more reasonable fashion
Structured vector graphics document like SVG / VML manipulated via DOM
I am mainly concerned about the performance differences, but the legibility of the logical code behind is also of interest (I, having already worked with canvas before, am for example fairly sure that the redrawing function for the entire canvas would be one hard-to-maintain beast of a script).
DOM manipulations are slow in comparison to GPU-accelerated canvas operations, so I would stay away from SVG and VML.
As far as structuring your canvas code goes, it certainly doesn't make sense (especially for performance reasons) to clear and re-draw the entire canvas because the player moved or performed an action. Based on your description here, I'm guessing that your game will be 2D. These types of games lend themselves extremely well to layering unless you're doing something rather complex like Paper Mario. You should be looking at the issue from an object-oriented viewpoint and encapsulating your drawing procedures and objects together as appropriate.
For instance, create a player object that maintains a small canvas representing the character. All the logic needed to maintain the character's state is kept within the object and any changes made to it need not worry about other components of the game's visual representation. Likewise, do the same for the background, user interface, and anything else you can abstract into a layer (within reason). For example, if you're doing a parallax game, you might have a foreground, background, character, and user interface layer.
Ultimately you will need to maintain the state of the different components in your game individually. Player animations, background clouds moving, trees swaying, etc. will all be managed by appropriate objects. Since you will already have that type of code structure setup, it makes sense to just maintain major components on separate canvas elements and composite them together as needed for better performance. If the character moves in the bottom left corner of a scene with a static background, you don't need to re-draw the top right corner (or 95% of the scene, for that matter). If you're considering full-screen capabilities, this is definitely a performance concern.
There's a rule in programming, that you should never try and optimize something before you have a speed problem with it. You'll spend more time trying to figure out how to code something in the best way than to actually code, and will never finish anything.
Draw them all on your canvas at a fixed rate. That's how it's done. If you start creating a canvas for each limb and element and manipulate them using CSS, you're wasting the potential of canvas. Might as well just use images. That's how they did it before canvas. That's the problem canvas was made to solve.
If you ever encounter speed issues, then you can start hammering at them. Check out these slides for some tips (related video). This guy's blog also has some handy tips on canvas performance.

Categories