rich Posted August 7, 2013 Share Posted August 7, 2013 Caveat: I've not had time to test this. I don't think it would take long to write a test, I'm just mental busy and am hoping someone else here has covered this already I'm wondering which of these approaches will render fastest (especially on mobile browser): Assume you've got a "Stage" (i.e. your large single canvas which is displayed to the user), a Camera and a load of Sprites. Method 1 1. Transform the canvas (via setTransform, so maybe some scaling and/or rotation)2. Now draw a stack of images to it (maybe several hundred), each of which may or may not have their own transform3. Reset the canvas back to normal This approach draws directly to the Stage. Method 2 1. Using a hidden canvas (that belongs to the Camera), draw all the images to it (each of which may or may not have their own transform). This canvas has no transforms or anything, just a straight normal hidden canvas.2. Now draw the hidden camera canvas to the Stage and apply a transform before doing so, thus rotating/scaling the display. Both methods should give the exact same visual result. In Method 1 the Stage canvas is left in a transformed state and then all the images are drawn to it, so they all take on the effect of that transform (i.e. scaling them, or rotating them). In Method 2 they are all drawn 'flat' with no base transform and at the end the final camera canvas is drawn to the Stage canvas. This has the overhead of doing that final draw to the Stage, so really I'm wondering which saves the most time - transforming and drawing all of those images, or doing effectively two draws. I guess I could probably have written a test case in the time it took to write this Quote Link to comment Share on other sites More sharing options...
rich Posted August 7, 2013 Author Share Posted August 7, 2013 Just to add that the vast majority of the sprites won't typically have their own transform. Quote Link to comment Share on other sites More sharing options...
remvst Posted August 7, 2013 Share Posted August 7, 2013 On my own library, I used offscreen canvases to optimize drawing.You can see an example of how it was used here: In this example, there a huge offscreen canvas which stores the level, instead of drawing every tile at every frame. I still use this technique because, the thing is, almost everytime you have something static, storing it in an offscreen canvas will be faster than drawing every small sprite.I almost always choose the solution that will use the least Javascript instructions. Quote Link to comment Share on other sites More sharing options...
rich Posted August 7, 2013 Author Share Posted August 7, 2013 "I almost always choose the solution that will use the least Javascript instructions." Solid gold advice Quote Link to comment Share on other sites More sharing options...
FLAG Posted August 7, 2013 Share Posted August 7, 2013 I use code to determine what tiles are visible and only draw those tiles. Singular canvas style. FLAG 1 Quote Link to comment Share on other sites More sharing options...
mrspeaker Posted August 7, 2013 Share Posted August 7, 2013 I have been using the offscreen canvas approach for all my games. Just the other day I realised I had no idea if it was more efficient or not... In my brain it seeeems like it should be faster - in the olden days a back buffer was essential - but just like the "less javascript instructions == better" philosophy... there's no science behind it Time to do some benchmarkin'! Quote Link to comment Share on other sites More sharing options...
rich Posted August 7, 2013 Author Share Posted August 7, 2013 Aye, but my original question isn't really about tiles at all. It's about the cost of transforms Quote Link to comment Share on other sites More sharing options...
FLAG Posted August 7, 2013 Share Posted August 7, 2013 I use a singular canvas, but I have also not benched mark any comparison between methods Quote Link to comment Share on other sites More sharing options...
remvst Posted August 7, 2013 Share Posted August 7, 2013 Aye, but my original question isn't really about tiles at all. It's about the cost of transforms I don't think transforms cost that much. They are only simple matrix multiplications (4x4 per 4x1, not so big). Drawing AFTER a transform has been made must cost a lot (because of pixel interpolation and stuff). Though, I'm only taking a guess. I didn't benchmark this. What I did benchmark (well, at least I saw a major difference) was on the screenshot above : drawing each part at each frame, or storing the whole thing on an offscreen canvas. And of course, offscreen canvas was waaaaaaaaaaaay faster (tried on really, really bigger levels).I'm also using it on Infiltration (dunno if you guys tried it). Levels are smaller, but it's still faster. I can see only one reason why it could be slower: when the offscreen canvas contains a lot of empty spaces. And I don't think the difference is that important. Quote Link to comment Share on other sites More sharing options...
rich Posted August 7, 2013 Author Share Posted August 7, 2013 Are hidden canvases still bound by the same memory limits as images? (i.e. you can't create bigger than 2048x2048 on most mobiles). Quote Link to comment Share on other sites More sharing options...
FLAG Posted August 7, 2013 Share Posted August 7, 2013 Why would you draw anything bigger than your screen? Quote Link to comment Share on other sites More sharing options...
rich Posted August 7, 2013 Author Share Posted August 7, 2013 So you could render a whole game level to a hidden canvas, then just copy the 'screen sized' section of it over. Otherwise you need to refresh the tiles every time you move. Quote Link to comment Share on other sites More sharing options...
FLAG Posted August 7, 2013 Share Posted August 7, 2013 What if you tiles are animated, or you have paralax layers of tiles, or isometric tiles that characters get depth sorted depending on what tile they are on. Point being, not all tiled environments are static once they are drawn once. Quote Link to comment Share on other sites More sharing options...
rich Posted August 7, 2013 Author Share Posted August 7, 2013 This is still unrelated to my original question and no-one said pre-rendering a level was a solution that worked for every game type. But it obviously worked well for remvst. Quote Link to comment Share on other sites More sharing options...
remvst Posted August 7, 2013 Share Posted August 7, 2013 This is still unrelated to my original question and no-one said pre-rendering a level was a solution that worked for every game type. But it obviously worked well for remvst.Actually, I think there might be some issues on mobile. Sometimes, on Infiltration, with the Android browser, I see the level disappearing and then the whole browser crash. I'm not sure if this is related specifically to my method though, since the game uses several complex algorithms which might consume too much memory. If you want to try it on desktop, on a bigger scale, here is Haunted Gardens: http://haunted.remvst.com/ Here is the kind of levels that can be stored (which was clearly faster than redrawing everything at each frame): Of course, it works because the levels are static. In the console, you can see it took 96ms to create the offscreen canvas. Then it is much faster to redraw it. What you can do is use an intelligent way of storing it: once the camera reaches the limits of your offscreen canvas, you can create a new one, which enables you to store only one smaller canvas at the same time. I haven't had the opportunity to try it, but it's certainly possible. Quote Link to comment Share on other sites More sharing options...
FLAG Posted August 7, 2013 Share Posted August 7, 2013 Wouldnt you be holding essentially 3 versions of all your images in cache with every draw? The original image files, the offscreen canvas image and the onscreen canvas image. Quote Link to comment Share on other sites More sharing options...
remvst Posted August 7, 2013 Share Posted August 7, 2013 Wouldnt you be holding essentially 3 versions of all your images in cache with every draw? The original image files, the offscreen canvas image and the onscreen canvas image.Yep, but that's the whole idea of cache: you use more memory to consume less CPU cycles. Quote Link to comment Share on other sites More sharing options...
dreta Posted August 7, 2013 Share Posted August 7, 2013 My guess is that drawing operations are always transformed, just that the default transformation matrix is an identity matrix. Since canvas drawing is hardware accelerated, it just doesn't make sense that the browser would do some sort of check whether the current transformation matrix is an identity matrix or not, especially that applying a transformation on the GPU costs nothing and it's only done per vertex. IMHO the first approach is the way to go, the second approach potentially has to draw content that isn't going to be displayed on the final canvas, not to mention the copying operation and the memory footprint. Quote Link to comment Share on other sites More sharing options...
rich Posted August 7, 2013 Author Share Posted August 7, 2013 Wouldnt you be holding essentially 3 versions of all your images in cache with every draw? The original image files, the offscreen canvas image and the onscreen canvas image. You've got the original image data, which you need regardless of approach so that's a must anyway. Plus the on-screen game sized canvas, which again you need regardless. So the only extra one is a single hidden canvas to which you'd render whatever you need cached, and then copy that one canvas to the game canvas. I'm not using this for tilemaps just for cameras, so each camera has its own hidden canvas and at the end I composite them all back onto the main game canvas. The only time I don't do this is when there aren't any custom cameras, in which case I'm drawing direct to the game canvas. The thought behind it was that say I've got 2 cameras side by side, each viewing a different part of the game world. Maybe for some reason the camera can rotate a bit (perhaps a shake effect happens during play), or it scales down/up in size a bit. I'm testing out rendering all the sprites that the camera can see to a hidden canvas, repeat for the 2nd camera, and then finally applying the transform (rotate/scale/skew) before rendering the 2 hidden canvases to the game. Alternatively I could not use a hidden canvas and instead set-up a global transform, then render all the sprites, then repeat for the other camera. This avoids using any hidden canvases but comes with 2 drawbacks: 1) You have to use context.clip() to stop sprites bleeding out over the edge of the camera, which is expensive on mobile (2) I'm not yet sure if the cost of drawing all of this sprites with the camera transform applied is more or less expensive than drawing them all 'flat' and then just doing one single transform during the camera render phase (so it only has to interpolate all those pixels once). As it happens I'm experimenting and benchmarking this at the moment, so should find out very soon. Quote Link to comment Share on other sites More sharing options...
Pixels Commander Posted August 7, 2013 Share Posted August 7, 2013 Are hidden canvases still bound by the same memory limits as images? (i.e. you can't create bigger than 2048x2048 on most mobiles). Yep, that is true at the moment. Here is nice tool to determine which size is ok for particular device http://www.williammalone.com/articles/html5-javascript-ios-maximum-image-size/ In order to have larger canvas you can use few of them joined together, but this can multiply available surface in 4 times and no more because of global memory limitations. Funny that iOS browser have no limit on this and browser just crashes after 6 x 2000 x 2000 canvases were created and used. Sorry for getting out off topic. Quote Link to comment Share on other sites More sharing options...
rich Posted August 7, 2013 Author Share Posted August 7, 2013 Ok experiments over and I'm going to run with using a hidden canvas per Camera. The benefits far outweigh the cost (in my case at least) and there was another 'gotcha' I forgot about until now - you can only have one active global composite operation per canvas at once. So if I want to apply a mask to my camera (with maybe xor) it needs to be a canvas anyway! and I often need that. Quote Link to comment Share on other sites More sharing options...
Paul-Andre Posted August 7, 2013 Share Posted August 7, 2013 Purely thinking in OpenGL terms, method 1 would be a bit faster, since you will draw one image less. Applying transformations on top of transformations doesn't have any additional cost. Quote Link to comment Share on other sites More sharing options...
rich Posted August 7, 2013 Author Share Posted August 7, 2013 It doesn't have GPU cost, but it has JavaScript cost (as it performs the matrix math for every sprite, every frame) and I'm not then sure which rendering process is faster - rendering a load of rotated sprites, or rendering them "normally" and then just doing one final rotation at the end on the whole scene. I've kind of answered my own question anyway as I need to use a canvas for the effects I require, but I'm still curious Quote Link to comment Share on other sites More sharing options...
Paul-Andre Posted August 8, 2013 Share Posted August 8, 2013 I'm sure that the cost of matrix math if the same, whether you perform a transform on the original matrix or on the transformed matrix. And you will have roughly the same number of transforms. Quote Link to comment Share on other sites More sharing options...
rich Posted August 8, 2013 Author Share Posted August 8, 2013 True, yes, I see what you mean. I believe the real cost will be in rendering, but it's ok - I'm happy with the approach I've got and will file this away under "test it out another day" Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.