Memory System: Part I
Note: These Game Engine blogs are a series of write-ups I did when I was in school for my master’s degree. I took a series of three courses run by Ed Keenan where we built a game engine from scratch in C++.
The next portion of the engine that we’re building is the memory system. This system is crucial to the engine. Our new memory system will allow developers to budget out their memory for different parts of a game and it’ll be much faster than the standard new and delete operators in C++. Consider that if we had the available memory be first-come first-served, whoever gets their first would take up all of the memory.
For example, if the environment artists get there first then maybe the sound artists would be left with no memory to work with. Games, especially on consoles, can only take up so much memory (excluding download patches). So we want our developers to be able to budget the memory and give out different increments of that memory in Heaps (think of them like large chunks of memory). Maybe they want to give out 100MB to the sound artists, 1GB to the environment artists, etc etc.. Now each small team knows their constraints and they’ll do their best to fit as much into their Heap as possible.
So how are we going to do this?
We’re going to overload the new and delete operators to do Windows API calls directly to the memory. Then we’ll tie that memory into our custom classes, which becomes a memory management system.
How these interact to form a Memory System
So we have a double linked list of Heaps. Each Heap has is its own bucket of memory (RawMemory) that we can do allocations from. The RawMemory portion is a bit more complicated than I’m showing for brevity purposes. It contains a linked list of TrackingBlocks that keep track of every allocation within that Heap.
On top of the Heaps sits a Memory System. The Memory System manages the heaps by creating and destroying them. This is the same way each Heap manages the TrackingBlocks and allocations.
MemorySystem has MemInfo which keeps track of how many bytes are currently used in the entire MemorySystem, how many Heaps there are, peak stats, and some other things that will be useful for debugging and optimization. Heap’s also have HeapInfo which keeps track of a lot of similar stats but only specific to that Heap.
With this system we’ll be able to create Heaps of all different sizes. Now when someone calls the new operator for a sound clip or character model, they’ll pass in the proper Heap from which they want to allocate from.
Take note some design patterns here. The MemorySystem is a Singleton meaning there can only ever be one of them. Heaps don’t manage themselves, they’re managed by the MemorySystem. This is the same way TrackingBlocks don’t manage themselves — the Heaps manage the TrackingBlocks.
Next week there will be a Memory System Part II where we deal with alignment and a few other issues.