Texture memory problem

Started by
11 comments, last by TaxCollector 13 years, 8 months ago
So I'm developing a 2D tile game.
To avoid having to make a huge amount of draw calls every frame, I render my tiles to textures of about 512x512 when loading the map.

In the game loop I then only have to draw a few of these textures on screen every frame.

I was quite happy with this method as my render method went from 15ms to 3ms.
But when I tried this approach on larger maps, my render method sometimes took over 20ms. (not constantly, it was more like: 3,3,4,3,4,28,3,3,3,3,4,3,3,3,4,34,3,3)

When I analyzed my application with visual studio I got the warning that I was pageing an extremely high amount of active memory, sometimes over 5000 pages/sec.

So the problem is that I can't just load the entire map into small textures as I can't store all these textures in the memory at once.

As I see it, the only way I can solve the pageing problem is by going back to my old strategy of drawing all tiles seperately on screen every frame. But I don't want to lose performance again... Does anyone know of an alternative or maybe a way to combine both approaches that I have tried.
Advertisement
Is each tile unique? I.e., can you share the same texture among several tiles?

Is every tile in view all the time? I.e., can you cull the tiles that aren't in view?

Do you need 512x512 resolution? Changing to 256x256 would cut memory required by a factor of 4.

As you probably realize, 512x512 is over a 1Meg (not including mips). Do you generate mipmaps? Do you need them?

Just for info, how many tiles are you talking about?

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

Are the textures used as an atlas for single-tile primitives (scalable to large levels) or to assemble the tiles into large pictures (not scalable)?
You can afford many primitives if you use buffers instead of separate calls for each tile; regardless of level size you only need to draw one screen's worth of tiles and to keep in memory one copy of each distinct tile.
For example, 16x16 tiles on a 1024x768 screen require only 3072 quads, certainly affordable on any hardware; 256 different tiles would fit in a 256x256 texture atlas.

Omae Wa Mou Shindeiru

Thanks for the replies. Let me describe my render method in greater detail to clear things up a bit.

tile size: width 64, height 32 (diamond shaped, stored in a png of 64*64)

small map: 32 tiles * 128 tiles = 4096 tiles
normal map: 64 tiles * 256 tiles = 16384 tiles
large map: 128 tiles * 512 tiles = 65536 tiles

Now since my tiles are diamond shaped I draw a tile every 64 pixels on the x axis and every 16 pixels on the y axis.

small map: (32 tiles * 64 pixels/tile) * (128 tiles * 16 pixels/tile)
-> map size = 2048px * 2048px
normal map: (64 tiles * 64 pixels/tile) * (256 tiles * 16 pixels/tile)
-> map size = 4096px * 4096px
large map: (128 tiles * 64 pixels/tile) * (512 tiles * 16 pixels/tile)
-> map size = 8192px * 8192px

As you can see even the smallest map has a size larger than most screen resolutions so not all tiles have to be drawn on the screen.

When loading a map at the start of the application, I:
1) divide the map in chunks of 512x512
2) create a render target texture of size 512*512 for every chunk (I'll call it a supertile)
3) draw the right tiles on the right supertile

In the main game loop I then draw the supertiles that are in view on screen. If a supertile is only partly in the view, I draw just that part (using a rect).

Performance is very good since for a screen resolution of 1024*768, at most 9 supertiles will be in the view, so only 9 draw calls are made per frame.
But as I said performance for larger maps is bad due to memory bounds. Loading a map of 8192px * 8192px in memory is just too much.


@buckeye:
- not all tiles are unique, I only use 64 different terrain sprites.
- tiles that aren't in view aren't drawn
- I don't need 512*512, but since I'm loading the entire map in the memory (8192*8192) it won't make a difference if I do it in pieces of 256*256 or 512*512. If I take a smaller supertile size I will just have to use more supertiles so in the end I will use the same amount of memory.
- supertiles have 1 mipmap level

@lorenzogatti
-the textures/supertiles are used to assemble into large pictures
-a buffer to replace seperate calls sounds like an interesting idea. Any links to some info on that topic will be appreciated.
Yeah, 8192x8192 ~ 270Meg.. that rates way up there as a texture requirement.

If you only have 64 separate textures, have you considered a textured mesh, or several meshes to enhance offscreen culling?

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

You must not needlessly keep the whole map in memory, especially not in premium memory like OpenGL textures. 64 unique tiles sized 64x64 fit a 512x512 texture, maybe less if you pack them tighter, and you don't need more memory than that texture: draw the tiles directly (about 768 quads) using vertex arrays or buffers and without pointless "supertiles".



Omae Wa Mou Shindeiru

I will do that, thanks guys. I Should probably do some more research before I start programming next time. It took me quite some time to get those supertiles working before realizing that it wasn't very realistic.
Quote:Original post by TaxCollector
But when I tried this approach on larger maps, my render method sometimes took over 20ms. (not constantly, it was more like: 3,3,4,3,4,28,3,3,3,3,4,3,3,3,4,34,3,3)

When I analyzed my application with visual studio I got the warning that I was pageing an extremely high amount of active memory, sometimes over 5000 pages/sec.


I've been strugling with exactly the same problem that every X frame takes so much longer to render.

How do you create your textures? I use D3DXCreateTextureFromMemoryEx(). Initially I used the D3DPOOL_DEFAULT pool for the textures, since it looked like it uses much less system memory. I changed it to D3DPOOL_MANAGED, and the problem of swapping memory in and out was gone and my framerate increased by 3x what it previously was. I does use more system memory now, but that I can later sort out with 64-bit programming to use more mem ;) hehe

I would like to know if this fixed your problem.
Well since I first render my tiles to texture, the textures are D3DUSAGE_RENDERTARGET and that flag is only compatible with D3DPOOL_DEFAULT. So that may contribute to the memory problems.
I'm now drawing my on screen tiles with 1 DrawIndexedPrimitive call using a vertex and index buffer and 1 SetTexture call using a terrain atlas (512*512).

All in all I'm quite happy with the performance:

pc 1: ATI radeon HD 4850 @ 1920x1200

rendertime: 2ms-3ms

pc 2: NVIDIA geforce 6600 @ 1024x768

rendertime: 4ms

laptop: NVIDIA geforce go 7300 @ 1280x800

rendertime: 18ms

What bothers me is that my laptop can't even get 60 fps. With a resolution of 1280x800, 1 DrawIndexedPrimitive call that draws about 2000 triangle primitives takes 18 ms. If I turn on vsync fps drops to 30. Am I expecting too much? 2D tile games have been in existence for years and even PC's back then could manage..

This topic is closed to new replies.

Advertisement