Canvas performance - long time of clearRect execution - javascript

I have problem with canvas performance. At simplest operations I have 3-5 FPS and application is useless. It happens in every browser even when I have one object in canvas. Before last Chrome update application worked better but only on this browser.
On image you can see performance log from Google Chrome which is recorded when I move object by mouse and canvas has 4 objects but it happens with single object too. In step_2.onMouseMove is method which set object position based on pointer coords (very simple calculations)
I use fabric.js library in this project. What could be the reason? How to boost performance of this?­­­­­­­­­­­­­­­­­­­­

Related

WebGL VS Canvas 2D hardware acceleration

These days, I need to draw many images on a canvas. The canvas size is 800x600px, and I have many images of 256x256px(some is smaller) to draw on it, these small images will compose a complete image on the canvas. I have two ways to implement this.
First, if I use canvas 2D context, that is context = canvas.getContext('2d'), then I can just use context.drawimage() method to put every image on the proper location of the canvas.
Another way, I use WebGL to draw these images on the canvas. On this way, for every small image, I need to draw a rectangle. The size of the rectangle is the same as this small image. Besides, the rectangle is on the proper location of the canvas. Then I use the image as texture to fill it.
Then, I compare the performance of these two methods. Both of their fps will reach 60, and the animation(When I click or move by the mouse, canvas will redraw many times) looks very smooth. So I compare their CPU usage. I expect that when I use WebGL, the CPU will use less because GPU will assure many work of drawing. But the result is, the CPU usage looks almost the same. I try to optimize my WebGL code, and I think it's good enough. By google, I found that browser such as Chrome, Firefox will enable Hardware acceleration by default. So I try to close the hardware acceleration. Then the CPU usage of the first method becomes much higher. So, my question is, since canvas 2D use GPU to accelerate, is it necessary for me to use WebGL just for 2D rendering? What is different between canvas 2D GPU acceleration and WebGL? They both use GPU. Maybe there is any other method to lower the CPU usage of the second method? Any answer will be appreciate!
Canvas 2D is still supported more places than WebGL so if you don't need any other functionality then going with Canvas 2D would mean your page will work on those browsers that have canvas but not WebGL (like old android devices). Of course it will be slow on those devices and might fail for other reasons like running out of memory if you have a lot of images.
Theoretically WebGL can be faster because the default for canvas 2d is that the drawingbuffer is preserved whereas for WebGL it's not. That means if you turn anti-aliasing off on WebGL the browser has the option to double buffer. Something it can't do with canvas2d. Another optimization is in WebGL you can turn off alpha which means the browser has the option to turn off blending when compositing your WebGL with the page, again something it doesn't have the option to do with canvas 2d. (there are plans to be able to turn off alpha for canvas 2d but as of 2017/6 it's not widely supported)
But, by option I mean just that. It's up to the browser to decide whether or not to make those optimizations.
Otherwise if you don't pick those optimizations it's possible the 2 will be the same speed. I haven't personally found that to be the case. I've tried to do some drawImage only things with canvas 2d and didn't get a smooth framerate were as I did with WebGL. It made no sense to be but I assumed there was something going on inside the browser I was un-aware off.
I guess that brings up the final difference. WebGL is low-level and well known. There's not much the browser can do to mess it up. Or to put it another way you're 100% in control.
With Canvas2D on the other hand it's up to the browser what to do and which optimizations to make. They might changes on each release. I know for Chrome at one point any canvas under 256x256 was NOT hardware accelerated. Another example would be what the canvas does when drawing an image. In WebGL you make the texture. You decide how complicated your shader is. In Canvas you have no idea what it's doing. Maybe it's using a complicated shader that supports all the various canvas globalCompositeOperation, masking, and other features. Maybe for memory management it splits images into chucks and renders them in pieces. For each browser as well as each version of the same browser what it decides to do is up to that team, where as with WebGL it's pretty much 100% up to you. There's not much they can do in the middle to mess up WebGL.
FYI: Here's an article detailing how to write a WebGL version of the canvas2d drawImage function and it's followed by an article on how to implement the canvas2d matrix stack.

JavaScript canvas game development

Ive been having a really baffling slow down issue with the game I am working on probably because I am unsure how to handle graphics (most likely responsible for the slow down) in javascript without using a third party framework like Phaser/ImapactJS/EaselJS etc)*. The following is the low down on how I am approaching my graphics. I would be very thankful for some tips or methods on how to do this right.
My game is tile based - using tiles designed at 500x500 px because I want them to display decently on high definition devices.
I am using a spritesheet to load all (most of) my tiles before the main loop is run. This image is roughly 4000 x 4000 (keeping it below 4096 because the GPU cant handle texture sizes larger than that).
I then use the drawImage function to cycle through and draw each tile on a part of the canvas using information (w, h, x, y) stored in the tile array. I do this on every cycle of the main loop using my drawMap function.
The map is currently 6x6 tiles in size
A character spritesheet is also loaded and drawn on to the canvas after the map has been drawn. The character displays a different frame of the animation on every cycle of the main loop. There are sets of animations for the character each contained in the same spritesheet.
The character sprite sheet is roughly 4000x3500
The character is roughly 350x250 px
Other objects also use the same sprite sheet. Currently there is only one object.
Possibly helpful questions:
Am I using too many spritesheets or too few?
Should I only draw something if it's coordinates are in bounds of the screen?
How should I go about garbage collection? Do I need to set image objects to null when no longer in use?
Thanks in advance for input. I would just like to know if I am going about it the right way and pick your brains as how to speed it up enough.
*Note that I plan to port the JS game to cocoonJS which provides graphics acceleration for the canvas element on mobile.
** If interested please visit my Patreon page for fun!
You have asked lots of questions here, I'll address the ones I've run into.
I would like to start out by saying very clearly,
Use a profiler
Find out whether each thing you are advised to do, by anybody, is making an improvement. Unless we work on your code, we can only give you theories on how to optimise it.
How should I go about garbage collection? Do I need to set image objects to null when no longer in use?
If you are no longer using an object, setting its reference to null will probably mean it gets garbage collected. Having nulls around is not necessarily good but this is not within the scope of this question.
For high performance applications, you want to avoid too much allocation and therefore too much garbage collection activity. See what your profiler says - the chrome profiler can tell you how much CPU time the garbage collector is taking up. You might be OK at the moment.
I then use the drawImage function to cycle through and draw each tile on a part of the canvas using information (w, h, x, y) stored in the tile array. I do this on every cycle of the main loop using my drawMap function.
This is quite slow - instead consider drawing the current on screen tiles to a background canvas, and then only drawing areas which were previously obscured.
For example, if your player walks to the left, there is going to be a lot of tiles on the left hand side of the screen which have come into view; you will need to draw the background buffer onto the screen, offset to account for the movement, and then draw the missing tiles.
My game is tile based - using tiles designed at 500x500 px because I want them to display decently on high definition devices
If I interpret this right, your tiles are 500x500px in diameter, and you are drawing a small number of these on screen. and then for devices without such a high resolution, the canvas renderer is going to be scaling these down. You really want to be drawing pixels 1:1 on each device.
Would you be able, instead, to have a larger number of smaller tiles on screen - thereby avoiding the extra drawing at the edges? Its likely that the tiles around the edges will sometimes draw only a few pixels of one edge, and the rest of the image will be cropped anyway, so why not break them up further?
Should I only draw something if it's coordinates are in bounds of the screen?
Yes, this is a very common and good optimisation to take. You'll find it makes a big difference.
Am I using too many spritesheets or too few?
I have found that when I have a small number of sprite sheets, the big performance hit is when I frequently switch between them. If during one draw phase, you draw all your characters from character_sheet.png and then draw all the plants from plant_sheet.png you'll be ok. Switching between them can cause lots of trouble and you'll see a slow down. You will know this is happening if your profiler tells you that drawImage is taking a big proportion of your frame.

How can I increase map rendering performance in HTML Canvas?

We are developing a web-based game. The map has a fixed size and is procedually generated.
At the moment, all these polygons are stored in one array and checked whether they should be drawn or not. This requires a lot of performance. Which is the best rendering / buffering solution for big maps?
What I've tried:
Quadtrees. Problem: Performance still not as great because there are so many polygons.
Drawing sections of the map to offscreen-canvases. A test run: http://norizon.ch/repo/buffered-map-rendering/ Problem: The browser crashes when trying to buffer that much data and such big images (maybe 2000x2000) still seem to perform badly on a canvas.
(posting comments as an answer for convenience)
One idea could be, when the user is translating the map, to re-use the part that will still be in view, and to draw only the stripe(s) that are no longer corrects.
I believe (do you confirm ?) that the most costly operation is the drawing, not to find which polygon to draw.
If so, you should use your QuadTree to find the polygons that are within the strips. Notice that, given Javascript's overhead, a simple 2D bucket that contains the polygons that are within a given (x,y) tile might be faster to use (if the cost of the quadtree is too high).
Now i have a doubt about the precise way you should do that, i'm afraid you'll have to experiment / benchmark, and maybe choose a prefered browser.
Problems :
• Copying a canvas on itself can be very slow depending on devices/Browsers. (might require to do 2 copy, in fact)
• Using an offscreen canvas can be very slow depending on devices/Browsers. (might not use hardware acceleration when off-screen).
If you are drawing things on top of the map, you can either use a secondary canvas on top of the map canvas, or you'll be forced to use an off-screen canvas that you'll copy on each frame.
I have tried a lot of things and this solution turned out to be the best for us.
Because our map has a fixed size, it is calculated server-side.
One big image atlas with all the required tiles will be loaded at the beginning of the game. For each image on the atlas, a seperate canvas is created. The client loads the whole map data into one two-dimensional array. The values determine, which tile has to be loaded. Maybe it would be even better if the map was drawn on a seperate canvas, so that only the stripes have to be painted. But the performance is really good, so we won't change that.
Three conclusions:
Images are fast. GetImageData is not!
JavaScript has not yet great support for multi threading, so we don't calculate the map client-side in game-time.
Quadtrees are fast. Arrays are faster.

Non-blocking "intersects" in KineticJs

I am in the middle of process of making a HTML5 game. Now I have a problem related to performance.
I have some irregular Shapes. So detecting if {x,y} is in the Shape or not using a custom function is near impossible(At least as I think).
There are two big Shapes, so I need to call Shape.intersects({x,y}) twice for multiple times per second to detect if current {x,y} is in the shape or not.
{x,y} are variable and are not touch/mouse events. So I can not use onmousemove,etc events.
Each twice call of Shape.intersects({x,y}) on Nexus 5 has a ~45ms overhead. ~45ms each 100ms! This make some hiccups in game experience.
The most straight solution is to make Shape.intersects({x,y}) non-blocking. But how? or do anyone have any idea around this problem?
I am using Kinetic v5.0.1
RESULT:
.intersects will re-generate the Shape in memory, this is why it is very costly. According the to the #MarkE answer below, using native canvas function context.isPointInPath(x, y) would be very efficient. But:
Note that this will only work for the last path (after using beginPath()). If you need to iterate several paths (ie. shapes) you need to re-construct the paths (no need to stroke or fill though). This is perhaps a reason why some don't use it.
Source: https://stackoverflow.com/a/17268669/172163
In my shapes, I had solid colors. Also there are multiple shapes dynamically generated. so I ended up with context.getImageData(x, y, 1, 1) to get the color of specific pixel, and if it is the color of my Shapes are not. It is more efficient than .intersects(). It costs only ~3ms.
Why intersects gives you a performance hit (pun intended!)
Internally, KineticJS uses context.getImageData to do hit-tests in the intersects method.
getImageData provides pixel-perfect hit testing but it's slow on mobile devices (as you've discovered).
To answer your question:
No, you cannot run KineticJS's internal methods on a background worker thread.
But...
If you really need to squeeze performance out of hit-testing, you can write your own custom hit-test.
One possibility for such a custom hit test would be to maintain 2 offscreen html5 canvas elements that mirror your 2 irregular shapes.
That way you can use canvas's context.isPointInPath(x,y) to do your hit-testing.
The importance is that isPointInPath uses GPU acceleration and is therefore quite fast.
If you feel ambitious you could even create an asynchronous background worker thread to execute isPointInPath while your UI thread continues.
You mention the Nexus5. You can spawn a background worker thread with Android's AsyncTask.
Your background task can return the results of your hit-test using it's onPostExecute(Result) callback which invokes on the UI thread.
http://developer.android.com/reference/android/os/AsyncTask.html
Good luck with your project!

HTML5 <canvas>: Performance implications of drawImage()

I was wondering if there were any performance differences in using drawImage() on a dynamically created canvas (i.e. document.createElement("canvas")) versus a canvas already created in the DOM (i.e. tags on a HTML page).
My thinking is that the "slow" part of drawImage() occurs when it actually has to display visuals to the user, though I could be wrong (can't find too much information on this).
Would it be substantially more expensive to draw a group of objects to a canvas in memory followed by a final draw to the "main" canvas than just drawing straight to the latter? I feel like it'd be better to have multiple canvases, at least for organizational purposes.
Related: Does the size of the canvas impact performance if you're only drawing to a subsection of it?
Talking about Chrome and Firefox I could not find any difference between static and dynamic canvas elements. Mainly the amount of pixels drawImage() handles makes it slow + the current globalCompositeOperation (copy, source-over are fastest). However, the browser has to render the full page, so it is a bad idea to place a stretched (background) image below the canvas.
There is a difference between the canvas attributes width/height and its style width/height attributes. You may have a 300*200 pixels canvas with a style size set to 100%. Then internal drawing speed is same what ever the browsers window size is. Of course display quality is a concern.
You may want to separate drawing (lines, boxes, arcs etc) from blitting (drawImage) and find out what consumes more time in your application. As long there is no need for multiple canvas (image processing, blending videos, etc.) try to avoid drawImage(). Your code - not elements - should help you to deal with 'organizational purposes'.
A fullscreen drawImage() on a 1 GHZ Netbook with 1024x600 pixels takes about 10msec. Doing it twice means there is no way to achieve a display rate of 50Hz. Things getting worse if you target iPhone or Android smartphones.
There is no need to do good-old-double-buffering with canvas, it's already implemented. You are free to update only relevant (dirty) subparts of your canvas element whenever you want and gain essential msecs.
Instead of using multiple canvas there is an option to do all invisible operations on a huge one in different sections - drawImage() with target and source the same. It is easier then to see what is happening while debugging.

Categories

Resources