Reputation: 479
In my game using HTML5 Canvas, there's a lot of objects.
When I draw ~2000 images and more using drawImage()
I got a very poor performance and a big cpu usage (25%)
My game resolution is 1920x1080
; I'm using requestAnimationFrame
and only the drawImage()
function
In this pixiJS
benchmark (http://www.goodboydigital.com/pixijs/bunnymark/), I still have 60 fps and 15% of cpu usage with 30k (30 000) bunnies
How are they doing? How can I lower my CPU usage and keep a high FPS (60-40 fps is good) Please note that I can't use the "virtual multiple canvas" method because it's an isometric game, and depending on the user position, some objects should be draw after or before the user.
edit:
Profiling:
My container class (typescript) which sort the objects and then draw them (as seen on the profiling)
export default class Container {
private Objects: Array<any> = [];
private Context: CanvasRenderingContext2D;
private Texts: Array<any> = [];
private Points: Array<any> = [];
constructor(Context: CanvasRenderingContext2D) {
this.Context = Context;
}
public drawImage(index: number, z: number, img: HTMLImageElement, sx?: number, sy?: number, swidth?: number, sheight?: number, x?: number, y?: number, width?: number, height?: number) {
this.Objects.push({
index: index,
z: z,
img: img,
sx: sx,
sy: sy,
swidth: swidth,
sheight: sheight,
x: x,
y: y,
width: width,
height: height
});
return this.Objects[this.Objects.length - 1];
}
public fillText(text: string, color: string, x: number, y: number) {
this.Texts.push({
text: text,
x: x,
color: color,
y: y
});
}
public fillPoint(color: string, size: number, x: number, y: number) {
this.Points.push({
color: color,
width: size,
height: size,
x: x,
y: y
});
}
public Draw() {
this.Objects.sort((a, b) => {
return (a.index - b.index) == 0 ? (a.z - b.z == 0 ? (a.LayerId ? (a.LayerId < b.LayerId ? -1 : (a.LayerId > b.LayerId ? 1 : 0)) : 0) : a.z - b.z) : (a.index - b.index);
});
for(var id in this.Objects) {
var object = this.Objects[id];
if(object.swidth == undefined) {
this.Context.drawImage(object["img"], object["sx"], object["sy"]);
} else if(object.x == undefined) {
this.Context.drawImage(object["img"], object["sx"], object["sy"], object["swidth"], object["sheight"]);
} else {
this.Context.drawImage(object["img"], object["sx"], object["sy"], object["swidth"], object["sheight"], object["x"], object["y"], object["width"], object["height"]);
}
}
this.Objects = [];
}
}
Upvotes: 1
Views: 1245
Reputation: 54026
Bunnies don't count.
Wow 30,0000 sprites is a lot... wow..., but wait, they don't rotate, scale, or even have basic FX like alpha fade. One tiny sprite (26 by 37) on a tiny part of the screen all doing the very same thing. Come now a modern GPU can do much better than that with pixel rates of 40,000Mpps (Mega pixels per second) common. The demo is just at 2000Mpps, for such simple sprites you should be seeing a quarter of a million tiny bunnies madly bouncing about.
The bottleneck for the bunnies is the CPU and javascript. You say 15% CPU usage. Open up the task manager and look in the performance tab (turn on logical processors) now start the demo and you will see one core's use start to climb. When that core hits 100% (maybe 15% of overall system on 8 Core machine) the bunny frame rate starts to drop. This is the evidence that the Javascript execution can not keep up. If however the demo begins to slow down before a core reaches 100% then the GPU is old and slow.
Look at the javascript that moves the bunnies, it's a flat loop that updates each bunny in turn and then updates the vertex buffers binds them issue a draw and does it all again, its as simple as it can get and yet it is still the performance bottleneck.
So yes 30,000 bunnies is Wow a lot, but imagine now you wanted to add a bunny with a gun, and all the support code to test for bullets, bunny fur flying death FX. dead bunnies :( ohhh the rodenity... just a simple shoot-em up. You can try it by downloading the demo and changing the code yourself. 30000 bunnies, One bunny with a gun, the 60fps will very quickly start to drop for every bullet the gun toten bunny pops off.
For a Javascript 2D Context API canvas game you should be budgeting no more than 1000 maybe 1200,1300 at max sprites per frame. Unlike the bunnies these are proper sprites that can be rotated, scaled, have independent alpha and depending on the hardware some limited composite operation. eg lighten for explosions.
If you think you can get more sprites by using webGL you are not going to get much more than that budget. Proper sprites (Rotated, scaled, Fx,..) need a lot of support code. The 2D canvas API does it all for you using native interfaces, if you go down the WebGL path that just means you have to take on the extra burden of low level GPU management on top of your javascript's already flat out job.
There are opportunities for worthwhile performance increase (by off loading game logic to the GPU) but you had better be a slick A grade experienced coder to be able to pull that off for every device under the sun.
So stick to the 2D API as it`s apparent slow performance masks an awesome high performance, easy to use, very robust, well tested, extremely widely supported hardware accelerated graphics rendering interface.
Oh and maybe use javascript rather than typescript which for games is just more CPU cycle chewing bloat you real do not need.
Note : My criticism of the bunnie demo is only on that demo's use of gradiouse performance stats. PixiJS is a very well written library, among the very best available for javascript game development. IMO
Upvotes: 1
Reputation: 296
HTML5 canvas is never performing well when using it with many ( 1000+ ) images. I think javascript will perform better in that situation.
But if you still want to use HTML5, maybe this will help: https://www.html5rocks.com/en/tutorials/canvas/performance/
Upvotes: 0