The KISS Game Engine.

Main
Parallelism
Runtime Types
Memory Management
Graphics (in process)

Memory Management

Memory Management Policy

This is probably one of the things that varies most from game to game. Going back a few years it was uncommon for console games to do any dynamic memory allocation, and I've written many games where there was no "free" function, all you could do was return to a previously known good state on the heap (or in this case stack). There are still many developers who build games this way. Frankly it's hard, it requires a lot of up front planning and what you end up doing is building mini-heaps inside the linearly allocated heaps for dynamic objects anyway.

So why the paranoia and the avoiding of dynamic allocations? It's basically about two things

  • fragmentation - general allocators generally have problems in constrained memory spaces, where lifetime and size of allocations vary dramatically
  • performance -  malloc and free are expensive operations.

You can build games with dynamic allocations, avoid the fragmentation  problems and make the allocator fast enough for the common case, but it requires constant monitoring of heap usage and a good understanding of how the allocator works and how to solve problems when they arrise.

What we'll do here is accept the fact that malloc and free are SLOW operations, however we won't completely disallow dynamic allocation, instead we'll assume that we need a strong justification to allocate memory. Since we are allowing dynamic allocations, and we want people to be able to use pieces of our technology independantly we'll provide an abstract allocation interface, that the application can pass into systems that have dynamic memory requirements.

 

An Allocator interface

The IAllocator interface is relatively simple, although there are a couple of things worth discussing.

We only support Alloc and Free, it maybe worth adding Realloc later, but we'll omit it until we have a real need for it.

For the most part we've just mirrored the malloc and free interfaces, the only real departure is the inclusion of a flags field on the Alloc interface. The intention for this is that a client implementation can use it to pass hints to it's allocator, we will  later reserve some bits in this field for things like the distinction between physical and virtual memory. For now we define a default FLAG value of 0 if the application has not provided one.

We also provide a helper macro "KISS_DEFAULT_ALLOCATOR" which defines and declares an allocator that thunks through to the system malloc/free. This is purely a convenience to minimize the lines of code that are needed in a basic client app.


#ifndef KISS_IALLOCATOR_H
#define KISS_IALLOCATOR_H

#ifndef KISS_DEFAULT_MEMORY_FLAGS
#define KISS_DEFAULT_MEMORY_FLAGS 0
#endif

namespace KISS {

class IAllocator
{
public:
    virtual void *Alloc(size_t size, uint32_t flags) = 0;
    virtual void Free(void *address) = 0;
};
}

#define KISS_DEFAULT_ALLOCATOR \
class DefaultAllocator : public KISS::IAllocator { \
    public: \
    void *Alloc(size_t size, uint32_t = KISS_DEFAULT_MEMORY_FLAGS) {return malloc(size);} \
    void Free(void *address) {free(address);} }; \
    DefaultAllocator gDefaultAllocator

#endif


 

Contact me