don't click here

Memory Management Discussion

Discussion in 'Mobius Engine Project' started by Gen, Apr 15, 2012.

  1. Gen

    Gen

    This is halloween! This is halloween! Member
    309
    0
    0
    The Mobius Engine
    I have maybe a week before I'll be busy again with the general flow of work, so let's keep the ball rolling by starting a discussion regarding memory management.

    Now, before we can have a general resource manager, we need a means to manage memory. Smart pointers is a good start on this, however we may want to access things from a pool at times, or store different objects or even individual attributes in containers in memory for access later on for the purposes of caching different assets at runtime (like, say, virtual texturing where we would have many smaller tiles loaded into memory that are then pushed into VRAM when they're needed).

    So here's an initial proposal.

    As it stands, we now have smart pointers with reference counting. We should automatically delete most objects once their reference count reaches zero, since a lot of objects we don't want to keep in memory at all times. There can be exceptions though, for things like cached data.

    One idea would be to store all objects in unique pools for different purposes, using MEPointer as an interface to add and remove things from the pool. Each pool could be allocated at the global scope, either as a static member of a class or as a global variable, with MEPointer taking an optional additional parameter in its constructor for what memory pool the pointer should be allocated in. It could work something like this:

    Code (Text):
    1.  
    2. MEMemPool gBadnikPool;
    3. void MEBadnik::newBadnik(MEModel &badnikModel)
    4. {
    5.     MEPointer<MEBadnik> badnik = new MEPointer<MEBadnik>(gBadnikPool); // Allocate our new badnik in the global badnik memory pool
    6.     badnik->mModel = badnikModel;
    7. }
    8.  
    9. void MEBadnik::killBadnik(long poolLocation)
    10. {
    11.     gBadnikPool.destroyAt(poolLocation); // Assume gBadnikPool will call our deconstructor for MEBadnik, thus deallocating the badnik at this specific location in the pool, and dereferencing its model asset.
    12. }
    13.  
    There's several other means of managing memory here, and how this could potentially interface with the memory manager.

    I'm hereby opening the floor to discussion with regards to how the memory manager should be designed and implemented. Do note that the memory manager will mostly be used for caching different data, helping to instance assets that have already been loaded into memory.
     
  2. erbuka

    erbuka

    Member
    21
    8
    3
    Italy
    Do we really need smart pointers and reference count? I was thinking that the memory manager should only hold the information about all allocated resources:

    Code (Text):
    1.  
    2. class MEResource
    3. {
    4. public:
    5.     MEResource(void);
    6.     ~MEResource(void);
    7.    
    8.     virtual unsigned int GetUsedSystemMemory() = 0;
    9. };
    10.  
    11. MEResource::MEResource(void)
    12. {
    13.     MEMemoryManager::AddResource(this);
    14. }
    15.  
    16.  
    17. MEResource::~MEResource(void)
    18. {
    19.     MEMemoryManager::RemoveResource(this);
    20. }
    21.  
    All the resources inherits from the MEResource class. The default constructor/destructor tells the memory manager that something is being allocated. Subclasses should extend MEResource like this:

    Code (Text):
    1.  
    2. class ByteBuffer : public MEResource {
    3. private:
    4.     char * data;   
    5.     int size;
    6.     char staticData[16];
    7. public:
    8.     ByteBuffer(int size) {
    9.         this->data = new char[size];
    10.         this->size = size;
    11.         ZeroMemory(this->data, size);
    12.     }
    13.    
    14.     ~ByteBuffer() {
    15.         delete[] this->data;
    16.     }
    17.  
    18.     unsigned int GetUsedSystemMemory() {
    19.         return sizeof(*this) + size;
    20.     }
    21. };
    22.  
    And about the memory manager, it could be something like this:

    Code (Text):
    1.  
    2. class MEMemoryManager
    3. {
    4. private:
    5.  
    6.     static std::vector<MEResource*> resources;
    7.  
    8.     MEMemoryManager(void);
    9.  
    10.     static void AddResource(MEResource * resource);
    11.     static void RemoveResource(MEResource * resource);
    12. public:
    13.    
    14.     static unsigned int GetUsedSystemMemory();
    15.  
    16.     ~MEMemoryManager(void);
    17.  
    18.     friend class MEResource;
    19. };
    20.  
     
  3. Gen

    Gen

    This is halloween! This is halloween! Member
    309
    0
    0
    The Mobius Engine
    Smart pointers with reference counting tends to come in handy a lot of the time for mitigating memory leakage. Just because you know when to delete something in code, doesn't necessarily mean some other random coder will.

    Regarding memory manager, different memory management techniques have proven performance benefits with regards to execution time and reducing memory fragmentation. This is especially important when we'll likely be caching a lot of geometry and texture data in system memory at some point in time.
     
  4. Relick

    Relick

    Member
    197
    0
    16
    England
    C++/DX10 Engine (not sonic related)
    Bit of an old topic, but I thought I'd bring it up again as it is pretty important. What Gen posted in the first post, is the best option for this IMO.

    Also I think we should consider to start implementing it at the same time we implement model loading - so its easier to manage and then its also done ready for level loading/phsyics etc.