leileilol wrote:The earliest part of engoo development was actually trying to get Quake to run in 4-6mb - not much luck.
What is special about 4-6 MB?
And why are you thinking it should be able to run in such a small amount of memory?
The reason I am curious is that I recently did some memory experiments continuing a thought-experiment and I've developed a rather bizarre memory management system in just the last 48 hours that is laughably efficient and hilarious and simple. And niche in the sense that modern programmers don't care about such things (I sort of don't either, but I want minimum footprint and tracking of all allocations to check for memory leaks or accidental code oversights).
It is so efficient, if I had the time I'd consider replacing 100% of fixed memory allocations (like static whatever whatever) if I had the time.
It is untested in a performance based environment where speed matters (but I don't see problems), and although I don't know of the consequences of unaligned memory blocks and such and don't have the kind of experience MH and Spike do, it might solve your issue. (And there is a macro in there to make it aligned --- well --- it isn't a macro but a function but it can be turned into a macro easy enough, but not having MH/Spike experience I don't know other than maybe aligning it to 16 byte blocks how to maximize the performance for speed.)
Except I don't know globals would fit into it and software is all about globals.
Either way ... here is code ... I thought I'd have more free time in coming days, but my free time has been zero'ed out for at least a month ...
- Code:
/// Header file
#ifndef _MEMORY_H
#define _MEMORY_H
///////////////////////////////////////////////////////////////////////////////
// Bare minimum to use this file:
#include <stdio.h> // size_t
typedef struct memlink_s
{
void* addy;
size_t allocSize;
struct memlink_s* allocPrev;
struct memlink_s* allocNext;
} memlink_t;
///////////////////////////////////////////////////////////////////////////////
//
// Memory Functions
//
///////////////////////////////////////////////////////////////////////////////
size_t Boundary_Add_And_Pad_To_Next (const size_t originalSize, const size_t addBlockSize);
void Memory_free (const void *ptr);
void* Memory_malloc (const size_t size, const char *Reason); // memory allocation space is "undefined"
void* Memory_calloc (const size_t n, const size_t size, const char *Reason); // zero-filled memory allocation
void* Memory_realloc ( void *in_ptr, const size_t size, const char *Reason);
char* Memory_strdup (const char* text);
///////////////////////////////////////////////////////////////////////////////
//
// Memory stratification support: Create memory chains
//
///////////////////////////////////////////////////////////////////////////////
size_t MemoryChain_MemoryUsed (memlink_t* headnode);
// Allocations
void* MemoryChain_malloc (const size_t allocSize, const char* Reason, memlink_t* headnode);
void* MemoryChain_calloc (const size_t allocCount, const size_t allocSize, const char* Reason, memlink_t* headnode);
void* MemoryChain_strdup (const char* myString, memlink_t* headnode);
// Reallocation: Does realloc and refreshes node
void* MemoryChain_realloc (void *in_ptr, const size_t size, const char* Reason);
// Freeing them
void MemoryChain_freeAlloc (void* pointer, memlink_t* headnode);
void MemoryChain_freeAllocChain (memlink_t* headnode);
#endif // !_MEMORY_H
// HEADER ^^^^ CODE BELOW
/***********************************************************************************************************\
*****
**
** memory.c: Memory functions
**
*****
\***********************************************************************************************************/
#include "memory.h"
///////////////////////////////////////////////////////////////////////////////
// Bare minimum to use this file:
#include <string.h> // String funcs
#include <stdlib.h> // Malloc and friends
#include "eX_environment.h" // Host_FatalError
size_t Boundary_Add_And_Pad_To_Next (const size_t originalSize, const size_t addBlockSize)
{
const size_t boundaryCrossAmount = originalSize % addBlockSize; // 0 = even, anything else isn't
const size_t padToBoundaryAmount = addBlockSize - boundaryCrossAmount;
const size_t newSize = originalSize + addBlockSize + padToBoundaryAmount;
return newSize;
}
///////////////////////////////////////////////////////////////////////////////
// Simple memory
///////////////////////////////////////////////////////////////////////////////
void Memory_free (const void *ptr)
{
if (ptr == NULL) Host_FatalError ("Memory_free failed: \"%s\"", "Attempted to free NULL pointer");
free ((void *)ptr);
ptr = NULL;
}
void* Memory_malloc (const size_t size, const char *Reason)
{
const void *ptr = malloc (size);
if (ptr == NULL) Host_FatalError ("Memory_malloc failed: \"%s\" on %i bytes", Reason, size);
return (void *)ptr;
}
void* Memory_calloc (const size_t n, const size_t size, const char *Reason)
{
const void *ptr = calloc (n, size);
const int allocsize = (int)(n * size);
if (ptr == NULL) Host_FatalError ("Memory_calloc: \"%s\" on %i bytes", Reason, allocsize);
return (void *)ptr;
}
char* Memory_strdup (const char* text)
{
return strdup (text);
}
void* Memory_realloc (void *in_ptr, const size_t size, const char *Reason)
{
const void *ptr = realloc((void *)in_ptr, size);
if (ptr == NULL) Host_FatalError ("Memory_realloc: \"%s\" on %i bytes", Reason, size);
return (void *)ptr;
}
typedef enum // Tis private
{
MEMORYOP_MALLOC,
MEMORYOP_CALLOC,
MEMORYOP_REALLOC,
MEMORYOP_STRDUP,
} memoryop_t;
///////////////////////////////////////////////////////////////////////////////
// Internal function prototypes for links
///////////////////////////////////////////////////////////////////////////////
void* sMemoryChain_Register_OP (const memoryop_t allocType, const size_t allocCount, const size_t allocSize, const char* Reason, memlink_t* headnode);
void sMemoryChain_RemoveLink (memlink_t* myNode, memlink_t* headnode);
memlink_t* sMemoryChain_FindAllocation (const void* pointer, memlink_t* headnode);
///////////////////////////////////////////////////////////////////////////////
// Public memory link prototypes
///////////////////////////////////////////////////////////////////////////////
// Removes link
void MemoryChain_freeAlloc (void* pointer, memlink_t* headnode)
{
// Run through list. Dealloc pointer. Reassign previous and next links. Free link too.
memlink_t* currentNode = sMemoryChain_FindAllocation (pointer, headnode);
// Remove it
sMemoryChain_RemoveLink(currentNode, headnode);
Memory_free (pointer); // Goodbye pointer
Memory_free (currentNode); // Goodbye node.
}
// Removes all links
void MemoryChain_freeAllocChain (memlink_t* headnode)
{
while (headnode)
MemoryChain_freeAlloc (headnode->addy, headnode);
}
size_t MemoryChain_MemoryUsed (memlink_t* headnode)
{
size_t totalBytesAllocated = 0;
for (memlink_t* currentNode = headnode; currentNode; currentNode = currentNode->allocNext)
{
totalBytesAllocated += currentNode->allocSize;
totalBytesAllocated += sizeof(memlink_t);
}
return totalBytesAllocated;
}
void* MemoryChain_malloc (const size_t allocSize, const char* Reason, memlink_t* headnode)
{
return sMemoryChain_Register_OP (MEMORYOP_MALLOC, 1, allocSize, Reason, headnode);
}
void* MemoryChain_calloc (const size_t allocCount, const size_t allocSize, const char* Reason, memlink_t* headnode)
{
return sMemoryChain_Register_OP (MEMORYOP_CALLOC, allocCount, allocSize, Reason, headnode);
}
void* MemoryChain_strdup (const char* myString, memlink_t* headnode)
{
const size_t allocSize = strlen (myString) + 1;
void* pointer = sMemoryChain_Register_OP (MEMORYOP_STRDUP, 1, allocSize, "String dup", headnode);
// Must copy the string into there ...
strcpy (pointer, myString);
return pointer;
}
///////////////////////////////////////////////////////////////////////////////
// Internal memory link functions
///////////////////////////////////////////////////////////////////////////////
// The big boss ...
void* sMemoryChain_Register_OP (const memoryop_t allocType, const size_t allocCount, const size_t allocSize, const char* Reason, memlink_t* headnode)
{
memlink_t* newNode = Memory_calloc (1, sizeof(memlink_t), "Child node");
if (headnode == NULL)
{
headnode = newNode; // No need to specify next/previous since chain was empty
}
else
{
memlink_t* previousNode = headnode;
// Run through list until hits end which is NULL
for (memlink_t* currentNode = headnode->allocNext; currentNode != NULL; previousNode = currentNode, currentNode = currentNode->allocNext);
// Previous is final node
previousNode->allocNext = newNode;
newNode->allocPrev = previousNode;
}
// Determine alloc size
newNode->addy = allocType == MEMORYOP_CALLOC ? Memory_calloc (allocCount, allocSize, Reason): Memory_malloc (allocSize, Reason);
newNode->allocSize = allocType == MEMORYOP_CALLOC ? allocCount * allocSize : allocSize; // If strdup, this must be provided to us!
return newNode->addy;
}
memlink_t* sMemoryChain_FindAllocation (const void* pointer, memlink_t* headnode)
{
memlink_t* currentNode = headnode;
for (; ¤tNode->addy != pointer; currentNode = currentNode->allocNext);
return currentNode;
}
void sMemoryChain_RemoveLink (memlink_t* myNode, memlink_t* headnode)
{
do
{
// What if headnode (which has no previous, but requires us to rewrite top)
if (myNode == headnode) { headnode = myNode->allocNext; break; } // Next could be NULL which is ok
// We are not headnode, therefore we do have a previous
myNode->allocPrev->allocNext = myNode->allocNext;
if (myNode->allocNext)
myNode->allocNext->allocPrev = myNode->allocPrev;
} while (0);
}
You could just make a global memlink_t* mainMemoryChain and use that in the args. You call MemoryChain_malloc instead of malloc, MemoryChain_calloc instead of calloc, MemoryChain_free instead of free, MemoryChain_strdup instead of strdup and would kill everything Hunk_Alloc with MemoryChain_calloc.
You could create independent temporarily memory chains and use MemoryChain_freeAllocChain to deallocate every single allocation in that temp chain (think Hunk_FreeToLowMark)--- like model loading or whatever needs a ton of temp allocations that normally don't clear until the next level.
Not tested as much as I'd like and for the next month --- unfortunately it appears I'm going to be far too busy to do Quakey stuffs.
(No doubt this isn't a new idea in the universe of C. But I'm rather picky about memory allocations and wanted a system to measure, track with minimal overhead. The function MemoryChain_MemoryUsed can be used to get an aggregate byte count of all memory used by a specific chain. Like if you make a memory chain called memlink_t* modelLoadingTemps, calling that function and feeding it that pointer would say total amount of memory used and calling MemoryChain_freeAllocChain would free it all with a single line.)
NOTE: Didn't have time to write MemoryChain_realloc (or did I? Well it might be in there. Just checked ... nope. I don't believe that Quake uses that anywhere anyway).