Game memory management

Started by
13 comments, last by KulSeran 15 years, 4 months ago
Hi all, Recent games often have such immense worlds that not all 3D models can be stored in memory at any single time. Therefore, I assume, 3D models are only loaded from disk when actually needed and some cashing mechanism is used to remove old models from memory when no longer needed. Now I was wondering, when should I remove older models? Should I wait until allocating new memory failed? It seems to me this would be just to late for smooth loading of new models. An alternative might be to keep track of the amount of bytes occupied by each model and by all models in total. This, however, is very hard to do (every allocated piece of memory actually takes up an extra few compiler specific bytes) or very inaccurate. Just unloading models when they are not used for some specific time is much simpler, but might require to much memory for one system and have unnecessary cash misses for systems with much memory. Anyone suggestions for a general solution to this problem? thnx, Dietger
Advertisement
Quote:Original post by dietepiet
Hi all,

Recent games often have such immense worlds that not all 3D models can be stored in memory at any single time. Therefore, I assume, 3D models are only loaded from disk when actually needed and some cashing mechanism is used to remove old models from memory when no longer needed.

Now I was wondering, when should I remove older models? Should I wait until allocating new memory failed? It seems to me this would be just to late for smooth loading of new models.
An alternative might be to keep track of the amount of bytes occupied by each model and by all models in total. This, however, is very hard to do (every allocated piece of memory actually takes up an extra few compiler specific bytes) or very inaccurate.
Just unloading models when they are not used for some specific time is much simpler, but might require to much memory for one system and have unnecessary cash misses for systems with much memory.

Anyone suggestions for a general solution to this problem?

thnx,
Dietger
Usually you load all reasources required for a level at load time. In cases where you can't do that (Huge / endless levels, or masses of content), then you usually allocate a certain space for resources - which may well just be "until there's no more space" for loading resources into video memory particularly. When there memory is full and you need another resource, you unload the least recently used resource from the cache, and load the new one.
You can decide to load the new model before you actually need it, for instance if you know there's a bit of the level coming up which has new enemies in it. It's also possible to unload resources from the cache when all instances are removed - for instance when you've killed all of enemy X in the level, there's no need to keep them around in memory.
Thanx for your answer.

For video memory, your method works fine because there are only a few places in game you allocate video memory.
For system memory on the other hand, allocations (new operator in c++) happen all over the place. How can I easily check each such allocation for failure and call my model clean-up routine? especially when many such allocations happen somewhere deep in stl vector and list code.

thnx
Quote:Original post by dietepiet
Thanx for your answer.

For video memory, your method works fine because there are only a few places in game you allocate video memory.
For system memory on the other hand, allocations (new operator in c++) happen all over the place. How can I easily check each such allocation for failure and call my model clean-up routine? especially when many such allocations happen somewhere deep in stl vector and list code.

thnx
You won't be doing much, if any allocations on a per-frame basis, you'll only be allocating when you need to load new models into the cache. So long as you're careful not to leak memory or corrupt the heap, you're very unlikely to run out of memory, unless you're running on some fixed hardware like a console or other embedded system.
If you really want, you can just put a try {} catch block around your resource loading code to cache any allocation failures, and either exit the game or not display a model.

If you find you're doing a lot of allocations on a per-frame basis, you should seriously look into why you need to. vectors can be reserved, pools can be allocated ahead of time, and so on.
Yeah, don't wait for an allocation to fail. Instead, have some sort of quota of loaded resources. If you go over the quota, free something first. Often you just free the one you used the longest time ago.

The quota could simply be just a maximum quantity of models/textures/whatever. If you want to try measuring the actual size of a resource and placing a limit on the total size used, it's usually easy to write a simple routine that returns an approximate value, and that's all you need really.
Thanx Kylotan, i just now realized that allocating memory will succeed up until the point where the OS swap file reaches its maximum size. I obviously need to remove cashed models way before this point, preferable around the time the swap file is actually needed. so a more or less approximate resource limit will probably do just fine.
Quote:Original post by dietepiet
Thanx Kylotan, i just now realized that allocating memory will succeed up until the point where the OS swap file reaches its maximum size. I obviously need to remove cashed models way before this point, preferable around the time the swap file is actually needed. so a more or less approximate resource limit will probably do just fine.
Not quite - they'll continue until you run out of address space too. On a 32-bit OS, you've got about 1.5GB of space to play around in, since the upper 2GB is reserved for the OS, and all of the DLLs and the EXE itself could total up to around 500MB.
And of that 1.5GB, that's probably fragmented, not in one large chunk.

If you're on a 64-bit OS, running a 64-bit EXE then you're good for 8 TB of address space.

This is interesting reading if you'd like to read more about it
Reaching that point is hard enough for a 32 bit application. You've only got 2 GB address space to yourself and part of that will be taken by things like mapped video memory, loaded DLLs and so on.

Also keep in mind that these numbers might change while your game runs. A common approach is to just choose an arbitrary limit, say, 1 GB, that represents what you expect from your target audience. That leaves 500 MB for mapped memory and 500 MB for normal use. Never underestimate the amount of memory hogging crap people install and keep running on their computers.

- You can use functions like GlobalMemoryStatus() and DirectX's GetAvailableTextureMem() to fine-tune this limit, but be careful. Otherwise, your game might run differently if the user starts Microsoft Office, then your game, then Alt+Tabs out and closes Office :)

Microsoft recommends: IDirect3DDevice9::GetAvailableTextureMem() returns the total available memory, including AGP. Allocating resources based on an assumption of how much video memory you have is not a great idea. For example, what if the card is running under a Unified Memory Architecture (UMA) or is able to compress the textures? There might be more space available than you might have thought. You should create resources and check for 'out of memory' errors, then scale back on the textures. For example, you could remove the top mip-levels of your textures.

- If you want to be smart, keep an LRU (least recently used) list of resources for quick unloading. There have been some articles and even a dedicated data structure here on gamedev.net.

Resources can be assigned a memory estimate, but it's also possible to shoot one's own foot with this. If the game prefers to unload large resources first, you might end up with your memory full of smaller resources and performance trouble where it would count - when larger resources need to be loaded.

- To avoid little hickups when loading resources during gameplay, you should begin to precache resources before they will actually be required. This way, you can set yourself a fixed time budget per frame that can be spend on precaching resources. If the time budget is up, stop loading resources and draw another frame.

Reading data from the hard drive doesn't eat up any CPU time (the DMA controller shovels data directly into RAM), preparing resources can easily be done in a thread, but creating the actual graphics resources will hog the system bus and, depending on what API and settings you use, might have to be done in the rendering thread.
Professional C++ and .NET developer trying to break into indie game development.
Follow my progress: http://blog.nuclex-games.com/ or Twitter - Topics: Ogre3D, Blender, game architecture tips & code snippets.
Arrr! Steve beat me to the address space bit.
Professional C++ and .NET developer trying to break into indie game development.
Follow my progress: http://blog.nuclex-games.com/ or Twitter - Topics: Ogre3D, Blender, game architecture tips & code snippets.
Thanx a lot, this sounds like really useful tips. Especially this one:
Quote: - You can use functions like GlobalMemoryStatus() and DirectX's GetAvailableTextureMem() to fine-tune this limit

I did not know such functions existed. I will just inspect the memory usage each frame and start unloading if its to much relative to the available memory.
About loading, I already planned an asynchronous loading schema where objects are requested before they are needed, including some priority indicator. So a loading budget per frame fits right in.

This topic is closed to new replies.

Advertisement