32
\$\begingroup\$

I'm creating a game in jQuery, where I use about 10000 32x32 tiles. Until now, I have been using them all separately (no sprite sheet). An average map uses about 2000 tiles (sometimes re-used PNG's but all separate divs) and the performance ranges from stable (Chrome) to a bit laggy (Firefox). Each of these divs are positioned absolutely using CSS. They do not need to be updated every tick, just when a new map is loaded.

Would it be better for performance to use spritesheet methods for the divs using CSS background-positioning, like gameQuery does?

Thank you in advance!

\$\endgroup\$
3
  • 3
    \$\begingroup\$ Where the hell you need 10 000 tiles for map? I suggest that you dont upload/draw all the map on user side. Show just a portion, that is visible. When user moves, load next section. The way every 3D graphics works. \$\endgroup\$
    – Deele
    Commented Nov 2, 2011 at 23:44
  • \$\begingroup\$ An average map uses 2000 different tiles, or just 2000 total tiles? How big are the maps you are creating? \$\endgroup\$ Commented Nov 3, 2011 at 3:32
  • \$\begingroup\$ Have you considered using one or a few larger images as a background and performing polygonal collision detection for boundaries? \$\endgroup\$ Commented Dec 19, 2012 at 15:15

2 Answers 2

49
\$\begingroup\$

My suggestion

Too many small PNGs will add a lot of network overhead (because of the size of the HTTP requests, but also the PNG header, and, probably even more importantly, the inability to compress efficently). On the other hand, one very large PNG has the drawbacks that it takes some time to load, and needs to stay permanently in memory (40 megabytes for 10,000 tiles) in a continuous chunk of memory.

I recommend the middle ground: several reasonably sized PNGs, for instance 1024 tiles of size 32×32. Maybe grouped by theme (for instance, a PNG with forest tiles, one with mountain tiles, another with city tiles -- I don't know the theme of your game, but you get the idea).

Note about cache efficiency

Because of memory access efficiency, you should never make your spritesheets too wide. Blitting tiles from a 128×8192 image will always be faster than blitting from a 8192×128 image.

Imagine you want to blit the first tile in a 8192×128 image. For the sake of simplicity, assume 1 pixel is 1 byte. The first two lines of pixels are laid out this way (cells contain their byte number in memory):

┌────┬────┬───┬────┬──────────┬─────┐
│  0 │  1 │...│ 31 │   ....   │ 8191│ 1st line of pixels: bytes 0 to 8191
├────┼────┼───┼────┼──────────┼─────┤
│8192│8193│...│8223│   ....   │16383│ 2nd line of pixels: bytes 8192 to 16383
├────┼────┼───┼────┼──────────┼─────┤
│ .. │ .. │...│ .. │   ....   │ ... │

So in order to blit the first line of the first title, the browser engine will retrieve bytes 0 to 31. To blit the second line, it will retrieve bytes 8192 to 8223, and so on until the 32nd line where bytes 253952 to 253983 are retrieved.

The total number of processed bytes will be 32×32. However, the total memory range is more than 253984 bytes. On a modern CPU, this means 32 or 33 cache stalls. In contrast, if the image was 128×8192, the memory range would be only 4000 bytes, which means no more than two cache stalls.

Because today's CPUs are very fast, cache stalls are very expensive and hang computations. So using a 128×8192 image instead of a 8192×128 image is potentially 8 times as fast, in theory at least. In practice this will depend how the blitting is implemented: it is possible that the underlying engine itself splits images into tiles to reduce the problem.

This isn't easy to explain correctly and I didn't expect to elaborate much. I hope it makes sense!

\$\endgroup\$
6
  • 4
    \$\begingroup\$ "Note also that because of memory access efficiency, you should never make your spritesheets too wide. Blitting tiles from a 128×8192 image will always be faster than blitting from a 8192×128 image." - nice curiosity, would you elaborate please? : ) \$\endgroup\$
    – Grimshaw
    Commented Nov 2, 2011 at 18:34
  • \$\begingroup\$ @DevilWithin: I have tried to explain the problem; let me know if I am being clear enough! \$\endgroup\$ Commented Nov 2, 2011 at 20:12
  • \$\begingroup\$ The cache efficiency stuff is fascinating. Do you have any numbers on the difference this makes with regard to framerate? \$\endgroup\$ Commented Nov 2, 2011 at 23:27
  • \$\begingroup\$ Whether it is a problem, looks like implementation specific, but if API like OpenGL act on textures that way, it sure is important to take that into consideration, thanks :) \$\endgroup\$
    – Grimshaw
    Commented Nov 2, 2011 at 23:36
  • 2
    \$\begingroup\$ @DevilWithin: what I wrote is only valid for a CPU -- a GPU, being massively parallel, has very different cache settings and will store textures using its own internal format, optimised for random access. This is the reason why square, power-of-two textures are recommended in OpenGL, and I would follow that rule for tilesets, too. \$\endgroup\$ Commented Nov 3, 2011 at 0:00
5
\$\begingroup\$

One huge spritesheet is (likely) going to give you better performance, simply because one of the biggest causes of lag is the round trip from browser request to server to browser. 10,000 HTTP calls is going to take much, much longer than 1 HTTP call that returns a file that is 10,000 bigger.

There might be other things you can use to decrease lag, depending on the structure of the game and the HTML you're creating.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .