diff --git a/src/z_zone.c b/src/z_zone.c index e28de13fe..2c7cf7c71 100644 --- a/src/z_zone.c +++ b/src/z_zone.c @@ -82,6 +82,59 @@ typedef struct memblock_s struct memblock_s *next, *prev; } ATTRPACK memblock_t; +// both the head and tail of the zone memory block list +static memblock_t head; + +// +// Function prototypes +// +static void Command_Memfree_f(void); +#ifdef ZDEBUG +static void Command_Memdump_f(void); +#endif + +// -------------------------- +// Zone memory initialisation +// -------------------------- + +/** Initialises zone memory. + * Used at game startup. + * + * \sa I_GetFreeMem, Command_Memfree_f, Command_Memdump_f + */ +void Z_Init(void) +{ + UINT32 total, memfree; + + memset(&head, 0x00, sizeof(head)); + + head.next = head.prev = &head; + + memfree = I_GetFreeMem(&total)>>20; + CONS_Printf("System memory: %uMB - Free: %uMB\n", total>>20, memfree); + + // Note: This allocates memory. Watch out. + COM_AddCommand("memfree", Command_Memfree_f); + +#ifdef ZDEBUG + COM_AddCommand("memdump", Command_Memdump_f); +#endif +} + + +// ---------------------- +// Zone memory allocation +// ---------------------- + +/** Returns the corresponding memblock_t for a given memory block. + * + * \param ptr A pointer to allocated memory, + * assumed to have been allocated with Z_Malloc/Z_Calloc. + * \param func A string containing the name of the function that called this, + * to be printed if the function I_Errors + * \return A pointer to the memblock_t for the given memory. + * \sa Z_Free, Z_ReallocAlign + */ #ifdef ZDEBUG #define Ptr2Memblock(s, f) Ptr2Memblock2(s, f, __FILE__, __LINE__) static memblock_t *Ptr2Memblock2(void *ptr, const char* func, const char *file, INT32 line) @@ -131,32 +184,12 @@ static memblock_t *Ptr2Memblock(void *ptr, const char* func) } -static memblock_t head; - -static void Command_Memfree_f(void); -#ifdef ZDEBUG -static void Command_Memdump_f(void); -#endif - -void Z_Init(void) -{ - UINT32 total, memfree; - - memset(&head, 0x00, sizeof(head)); - - head.next = head.prev = &head; - - memfree = I_GetFreeMem(&total)>>20; - CONS_Printf("System memory: %uMB - Free: %uMB\n", total>>20, memfree); - - // Note: This allocates memory. Watch out. - COM_AddCommand("memfree", Command_Memfree_f); - -#ifdef ZDEBUG - COM_AddCommand("memdump", Command_Memdump_f); -#endif -} - +/** Frees allocated memory. + * + * \param ptr A pointer to allocated memory, + * assumed to have been allocated with Z_Malloc/Z_Calloc. + * \sa Z_FreeTags + */ #ifdef ZDEBUG void Z_Free2(void *ptr, const char *file, INT32 line) #else @@ -206,7 +239,11 @@ void Z_Free(void *ptr) #endif } -// malloc() that doesn't accept failure. +/** malloc() that doesn't accept failure. + * + * \param size Amount of memory to be allocated, in bytes. + * \return A pointer to the allocated memory. + */ static void *xm(size_t size) { const size_t padedsize = size+sizeof (size_t); @@ -227,10 +264,18 @@ static void *xm(size_t size) return p; } -// Z_Malloc -// You can pass Z_Malloc() a NULL user if the tag is less than -// PU_PURGELEVEL. - +/** The Z_MallocAlign function. + * Allocates a block of memory, adds it to a linked list so we can keep track of it. + * + * \param size Amount of memory to be allocated, in bytes. + * \param tag Purge tag. + * \param user The address of a pointer to the memory to be allocated. + * When the memory is freed by Z_Free later, + * the pointer at this address will then be automatically set to NULL. + * \param alignbits The alignment of the memory to be allocated, in bits. Can be 0. + * \note You can pass Z_Malloc() a NULL user if the tag is less than PU_PURGELEVEL. + * \sa Z_CallocAlign, Z_ReallocAlign + */ #ifdef ZDEBUG void *Z_Malloc2(size_t size, INT32 tag, void *user, INT32 alignbits, const char *file, INT32 line) @@ -307,6 +352,19 @@ void *Z_MallocAlign(size_t size, INT32 tag, void *user, INT32 alignbits) return given; } +/** The Z_CallocAlign function. + * Allocates a block of memory, adds it to a linked list so we can keep track of it. + * Unlike Z_MallocAlign, this also initialises the bytes to zero. + * + * \param size Amount of memory to be allocated, in bytes. + * \param tag Purge tag. + * \param user The address of a pointer to the memory to be allocated. + * When the memory is freed by Z_Free later, + * the pointer at this address will then be automatically set to NULL. + * \param alignbits The alignment of the memory to be allocated, in bits. Can be 0. + * \note You can pass Z_Calloc() a NULL user if the tag is less than PU_PURGELEVEL. + * \sa Z_MallocAlign, Z_ReallocAlign + */ #ifdef ZDEBUG void *Z_Calloc2(size_t size, INT32 tag, void *user, INT32 alignbits, const char *file, INT32 line) #else @@ -323,10 +381,26 @@ void *Z_CallocAlign(size_t size, INT32 tag, void *user, INT32 alignbits) #endif } +/** The Z_ReallocAlign function. + * Reallocates a block of memory with a new size. + * + * \param ptr A pointer to allocated memory, + * assumed to have been allocated with Z_Malloc/Z_Calloc. + * If NULL, this function instead acts as a wrapper for Z_CallocAlign. + * \param size New size of memory block, in bytes. + * If zero, then the memory is freed and NULL is returned. + * \param tag New purge tag. + * \param user The address of a pointer to the memory to be reallocated. + * This can be a different user to the one originally assigned to the memory block. + * \param alignbits The alignment of the memory to be allocated, in bits. Can be 0. + * \return A pointer to the reallocated memory. Can be NULL if memory was freed. + * \note You can pass Z_Realloc() a NULL user if the tag is less than PU_PURGELEVEL. + * \sa Z_MallocAlign, Z_CallocAlign + */ #ifdef ZDEBUG void *Z_Realloc2(void *ptr, size_t size, INT32 tag, void *user, INT32 alignbits, const char *file, INT32 line) #else -void *Z_ReallocAlign(void *ptr, size_t size,INT32 tag, void *user, INT32 alignbits) +void *Z_ReallocAlign(void *ptr, size_t size, INT32 tag, void *user, INT32 alignbits) #endif { void *rez; @@ -393,6 +467,11 @@ void *Z_ReallocAlign(void *ptr, size_t size,INT32 tag, void *user, INT32 alignb return rez; } +/** Frees all memory for a given set of tags. + * + * \param lowtag The lowest tag to consider. + * \param hightag The highest tag to consider. + */ void Z_FreeTags(INT32 lowtag, INT32 hightag) { memblock_t *block, *next; @@ -407,22 +486,25 @@ void Z_FreeTags(INT32 lowtag, INT32 hightag) } } -// -// Z_CheckMemCleanup -// -// TODO: Currently blocks >= PU_PURGELEVEL are freed every -// CLEANUPCOUNT. It might be better to keep track of -// the total size of all purgable memory and free it when the -// size exceeds some value. -// -// This was in Z_Malloc, but was freeing data at -// unsafe times. Now it is only called when it is safe -// to cleanup memory. +// ----------------- +// Utility functions +// ----------------- +// starting value of nextcleanup #define CLEANUPCOUNT 2000 +// number of function calls left before next cleanup static INT32 nextcleanup = CLEANUPCOUNT; +/** This was in Z_Malloc, but was freeing data at + * unsafe times. Now it is only called when it is safe + * to cleanup memory. + * + * \todo Currently blocks >= PU_PURGELEVEL are freed every + * CLEANUPCOUNT. It might be better to keep track of + * the total size of all purgable memory and free it when the + * size exceeds some value. + */ void Z_CheckMemCleanup(void) { if (nextcleanup-- == 0) @@ -538,6 +620,17 @@ void Z_CheckHeap(INT32 i) } } +// ------------------------ +// Zone memory modification +// ------------------------ + +/** Changes a memory block's purge tag. + * + * \param ptr A pointer to allocated memory, + * assumed to have been allocated with Z_Malloc/Z_Calloc. + * \param tag The new tag. + * \sa Z_SetUser + */ #ifdef PARANOIA void Z_ChangeTag2(void *ptr, INT32 tag, const char *file, INT32 line) #else @@ -583,91 +676,13 @@ void Z_ChangeTag(void *ptr, INT32 tag) block->tag = tag; } -/** Calculates memory usage for a given set of tags. - * NOTE: Z_TagUsage is now just a macro of this function. +/** Changes a memory block's user. * - * \param lowtag The lowest tag to consider. - * \param hightag The highest tag to consider. - * \return Number of bytes currently allocated in the heap for the - * given tags. + * \param ptr A pointer to allocated memory, + * assumed to have been allocated with Z_Malloc/Z_Calloc. + * \param newuser The new user for the memory block. + * \sa Z_ChangeTag */ -size_t Z_TagsUsage(INT32 lowtag, INT32 hightag) -{ - size_t cnt = 0; - memblock_t *rover; - - for (rover = head.next; rover != &head; rover = rover->next) - { - if (rover->tag < lowtag || rover->tag > hightag) - continue; - cnt += rover->size + sizeof *rover; - } - - return cnt; -} - -static void Command_Memfree_f(void) -{ - UINT32 freebytes, totalbytes; - - Z_CheckHeap(-1); - CONS_Printf("\x82%s", M_GetText("Memory Info\n")); - CONS_Printf(M_GetText("Total heap used : %7s KB\n"), sizeu1(Z_TagsUsage(0, INT32_MAX)>>10)); - CONS_Printf(M_GetText("Static : %7s KB\n"), sizeu1(Z_TagUsage(PU_STATIC)>>10)); - CONS_Printf(M_GetText("Static (sound) : %7s KB\n"), sizeu1(Z_TagUsage(PU_SOUND)>>10)); - CONS_Printf(M_GetText("Static (music) : %7s KB\n"), sizeu1(Z_TagUsage(PU_MUSIC)>>10)); - CONS_Printf(M_GetText("Locked cache : %7s KB\n"), sizeu1(Z_TagUsage(PU_CACHE)>>10)); - CONS_Printf(M_GetText("Level : %7s KB\n"), sizeu1(Z_TagUsage(PU_LEVEL)>>10)); - CONS_Printf(M_GetText("Special thinker : %7s KB\n"), sizeu1(Z_TagUsage(PU_LEVSPEC)>>10)); - CONS_Printf(M_GetText("All purgable : %7s KB\n"), - sizeu1(Z_TagsUsage(PU_PURGELEVEL, INT32_MAX)>>10)); - -#ifdef HWRENDER - if (rendermode != render_soft && rendermode != render_none) - { - CONS_Printf(M_GetText("Patch info headers: %7s KB\n"), sizeu1(Z_TagUsage(PU_HWRPATCHINFO)>>10)); - CONS_Printf(M_GetText("Mipmap patches : %7s KB\n"), sizeu1(Z_TagUsage(PU_HWRPATCHCOLMIPMAP)>>10)); - CONS_Printf(M_GetText("HW Texture cache : %7s KB\n"), sizeu1(Z_TagUsage(PU_HWRCACHE)>>10)); - CONS_Printf(M_GetText("Plane polygons : %7s KB\n"), sizeu1(Z_TagUsage(PU_HWRPLANE)>>10)); - CONS_Printf(M_GetText("HW Texture used : %7d KB\n"), HWR_GetTextureUsed()>>10); - } -#endif - - CONS_Printf("\x82%s", M_GetText("System Memory Info\n")); - freebytes = I_GetFreeMem(&totalbytes); - CONS_Printf(M_GetText(" Total physical memory: %7u KB\n"), totalbytes>>10); - CONS_Printf(M_GetText("Available physical memory: %7u KB\n"), freebytes>>10); -} - -#ifdef ZDEBUG -static void Command_Memdump_f(void) -{ - memblock_t *block; - INT32 mintag = 0, maxtag = INT32_MAX; - INT32 i; - - if ((i = COM_CheckParm("-min"))) - mintag = atoi(COM_Argv(i + 1)); - - if ((i = COM_CheckParm("-max"))) - maxtag = atoi(COM_Argv(i + 1)); - - for (block = head.next; block != &head; block = block->next) - if (block->tag >= mintag && block->tag <= maxtag) - { - char *filename = strrchr(block->ownerfile, PATHSEP[0]); - CONS_Printf("[%3d] %s (%s) bytes @ %s:%d\n", block->tag, sizeu1(block->size), sizeu2(block->realsize), filename ? filename + 1 : block->ownerfile, block->ownerline); - } -} -#endif - -// Creates a copy of a string. -char *Z_StrDup(const char *s) -{ - return strcpy(ZZ_Alloc(strlen(s) + 1), s); -} - - #ifdef PARANOIA void Z_SetUser2(void *ptr, void **newuser, const char *file, INT32 line) #else @@ -703,3 +718,106 @@ void Z_SetUser(void *ptr, void **newuser) block->user = (void*)newuser; *newuser = ptr; } + +// ----------------- +// Zone memory usage +// ----------------- + +/** Calculates memory usage for a given set of tags. + * + * \param lowtag The lowest tag to consider. + * \param hightag The highest tag to consider. + * \return Number of bytes currently allocated in the heap for the + * given tags. + */ +size_t Z_TagsUsage(INT32 lowtag, INT32 hightag) +{ + size_t cnt = 0; + memblock_t *rover; + + for (rover = head.next; rover != &head; rover = rover->next) + { + if (rover->tag < lowtag || rover->tag > hightag) + continue; + cnt += rover->size + sizeof *rover; + } + + return cnt; +} + +// ----------------------- +// Miscellaneous functions +// ----------------------- + +/** The function called by the "memfree" console command. + * Prints the memory being used by each part of the game to the console. + */ +static void Command_Memfree_f(void) +{ + UINT32 freebytes, totalbytes; + + Z_CheckHeap(-1); + CONS_Printf("\x82%s", M_GetText("Memory Info\n")); + CONS_Printf(M_GetText("Total heap used : %7s KB\n"), sizeu1(Z_TagsUsage(0, INT32_MAX)>>10)); + CONS_Printf(M_GetText("Static : %7s KB\n"), sizeu1(Z_TagUsage(PU_STATIC)>>10)); + CONS_Printf(M_GetText("Static (sound) : %7s KB\n"), sizeu1(Z_TagUsage(PU_SOUND)>>10)); + CONS_Printf(M_GetText("Static (music) : %7s KB\n"), sizeu1(Z_TagUsage(PU_MUSIC)>>10)); + CONS_Printf(M_GetText("Locked cache : %7s KB\n"), sizeu1(Z_TagUsage(PU_CACHE)>>10)); + CONS_Printf(M_GetText("Level : %7s KB\n"), sizeu1(Z_TagUsage(PU_LEVEL)>>10)); + CONS_Printf(M_GetText("Special thinker : %7s KB\n"), sizeu1(Z_TagUsage(PU_LEVSPEC)>>10)); + CONS_Printf(M_GetText("All purgable : %7s KB\n"), + sizeu1(Z_TagsUsage(PU_PURGELEVEL, INT32_MAX)>>10)); + +#ifdef HWRENDER + if (rendermode != render_soft && rendermode != render_none) + { + CONS_Printf(M_GetText("Patch info headers: %7s KB\n"), sizeu1(Z_TagUsage(PU_HWRPATCHINFO)>>10)); + CONS_Printf(M_GetText("Mipmap patches : %7s KB\n"), sizeu1(Z_TagUsage(PU_HWRPATCHCOLMIPMAP)>>10)); + CONS_Printf(M_GetText("HW Texture cache : %7s KB\n"), sizeu1(Z_TagUsage(PU_HWRCACHE)>>10)); + CONS_Printf(M_GetText("Plane polygons : %7s KB\n"), sizeu1(Z_TagUsage(PU_HWRPLANE)>>10)); + CONS_Printf(M_GetText("HW Texture used : %7d KB\n"), HWR_GetTextureUsed()>>10); + } +#endif + + CONS_Printf("\x82%s", M_GetText("System Memory Info\n")); + freebytes = I_GetFreeMem(&totalbytes); + CONS_Printf(M_GetText(" Total physical memory: %7u KB\n"), totalbytes>>10); + CONS_Printf(M_GetText("Available physical memory: %7u KB\n"), freebytes>>10); +} + +#ifdef ZDEBUG +/** The function called by the "memdump" console command. + * Prints zone memory debugging information (i.e. tag, size, location in code allocated). + * Can be all memory allocated in game, or between a set of tags (if -min/-max args used). + * This command is available only if ZDEBUG is enabled. + */ +static void Command_Memdump_f(void) +{ + memblock_t *block; + INT32 mintag = 0, maxtag = INT32_MAX; + INT32 i; + + if ((i = COM_CheckParm("-min"))) + mintag = atoi(COM_Argv(i + 1)); + + if ((i = COM_CheckParm("-max"))) + maxtag = atoi(COM_Argv(i + 1)); + + for (block = head.next; block != &head; block = block->next) + if (block->tag >= mintag && block->tag <= maxtag) + { + char *filename = strrchr(block->ownerfile, PATHSEP[0]); + CONS_Printf("[%3d] %s (%s) bytes @ %s:%d\n", block->tag, sizeu1(block->size), sizeu2(block->realsize), filename ? filename + 1 : block->ownerfile, block->ownerline); + } +} +#endif + +/** Creates a copy of a string. + * + * \param s The string to be copied. + * \return A copy of the string, allocated in zone memory. + */ +char *Z_StrDup(const char *s) +{ + return strcpy(ZZ_Alloc(strlen(s) + 1), s); +} diff --git a/src/z_zone.h b/src/z_zone.h index b21d951d6..d74639be9 100644 --- a/src/z_zone.h +++ b/src/z_zone.h @@ -30,53 +30,53 @@ //#define ZDEBUG // -// ZONE MEMORY -// PU - purge tags. -// Tags < PU_LEVEL are not purged until freed explicitly. -#define PU_STATIC 1 // static entire execution time -#define PU_LUA 2 // static entire execution time -- used by lua so it doesn't get caught in loops forever - -#define PU_SOUND 11 // static while playing -#define PU_MUSIC 12 // static while playing -#define PU_HUDGFX 13 // static until WAD added - -#define PU_HWRPATCHINFO 21 // Hardware GLPatch_t struct for OpenGL texture cache -#define PU_HWRPATCHCOLMIPMAP 22 // Hardware GLMipmap_t struct colromap variation of patch - -#define PU_HWRCACHE 48 // static until unlocked -#define PU_CACHE 49 // static until unlocked - -// Tags s.t. PU_LEVEL <= tag < PU_PURGELEVEL are purged at level start -#define PU_LEVEL 50 // static until level exited -#define PU_LEVSPEC 51 // a special thinker in a level -#define PU_HWRPLANE 52 - -// Tags >= PU_PURGELEVEL are purgable whenever needed -#define PU_PURGELEVEL 100 -#define PU_CACHE_UNLOCKED 101 -#define PU_HWRCACHE_UNLOCKED 102 // 'second-level' cache for graphics - // stored in hardware format and downloaded as needed -#define PU_HWRPATCHINFO_UNLOCKED 103 - -void Z_Init(void); -#define Z_FreeTag(tagnum) Z_FreeTags(tagnum, tagnum) -void Z_FreeTags(INT32 lowtag, INT32 hightag); -void Z_CheckMemCleanup(void); -void Z_CheckHeap(INT32 i); - -#ifdef PARANOIA -// This is used to get the local FILE : LINE info from CPP -// prior to really call the function in question. +// Purge tags // -#define Z_ChangeTag(p,t) Z_ChangeTag2(p, t, __FILE__, __LINE__) -#define Z_SetUser(p,u) Z_SetUser2(p, u, __FILE__, __LINE__) -void Z_ChangeTag2(void *ptr, INT32 tag, const char *file, INT32 line); -void Z_SetUser2(void *ptr, void **newuser, const char *file, INT32 line); -#else -void Z_ChangeTag(void *ptr, INT32 tag); -void Z_SetUser(void *ptr, void **newuser); -#endif +// Now they are an enum! -- Monster Iestyn 15/02/18 +// +enum +{ + // Tags < PU_LEVEL are not purged until freed explicitly. + PU_STATIC = 1, // static entire execution time + PU_LUA = 2, // static entire execution time -- used by lua so it doesn't get caught in loops forever + PU_SOUND = 11, // static while playing + PU_MUSIC = 12, // static while playing + PU_HUDGFX = 13, // static until WAD added + + PU_HWRPATCHINFO = 21, // Hardware GLPatch_t struct for OpenGL texture cache + PU_HWRPATCHCOLMIPMAP = 22, // Hardware GLMipmap_t struct colormap variation of patch + + PU_HWRCACHE = 48, // static until unlocked + PU_CACHE = 49, // static until unlocked + + // Tags s.t. PU_LEVEL <= tag < PU_PURGELEVEL are purged at level start + PU_LEVEL = 50, // static until level exited + PU_LEVSPEC = 51, // a special thinker in a level + PU_HWRPLANE = 52, // if ZPLANALLOC is enabled in hw_bsp.c, this is used to alloc polygons for OpenGL + + // Tags >= PU_PURGELEVEL are purgable whenever needed + PU_PURGELEVEL = 100, // Note: this is never actually used as a tag + PU_CACHE_UNLOCKED = 101, // Note: unused + PU_HWRCACHE_UNLOCKED = 102, // 'unlocked' PU_HWRCACHE memory: + // 'second-level' cache for graphics + // stored in hardware format and downloaded as needed + PU_HWRPATCHINFO_UNLOCKED = 103, // 'unlocked' PU_HWRPATCHINFO memory +}; + +// +// Zone memory initialisation +// +void Z_Init(void); + +// +// Zone memory allocation +// +// enable ZDEBUG to get the file + line the functions were called from +// for ZZ_Alloc, see doomdef.h +// + +// Z_Free and alloc with alignment #ifdef ZDEBUG #define Z_Free(p) Z_Free2(p, __FILE__, __LINE__) #define Z_MallocAlign(s,t,u,a) Z_Malloc2(s, t, u, a, __FILE__, __LINE__) @@ -93,15 +93,51 @@ void *Z_CallocAlign(size_t size, INT32 tag, void *user, INT32 alignbits) FUNCALL void *Z_ReallocAlign(void *ptr, size_t size, INT32 tag, void *user, INT32 alignbits) FUNCALLOC(2); #endif +// Alloc with no alignment #define Z_Malloc(s,t,u) Z_MallocAlign(s, t, u, 0) #define Z_Calloc(s,t,u) Z_CallocAlign(s, t, u, 0) #define Z_Realloc(p,s,t,u) Z_ReallocAlign(p, s, t, u, 0) +// Free all memory by tag +// these don't give line numbers for ZDEBUG currently though +// (perhaps this should be changed in future?) +#define Z_FreeTag(tagnum) Z_FreeTags(tagnum, tagnum) +void Z_FreeTags(INT32 lowtag, INT32 hightag); + +// +// Utility functions +// +void Z_CheckMemCleanup(void); +void Z_CheckHeap(INT32 i); + +// +// Zone memory modification +// +// enable PARANOIA to get the file + line the functions were called from +// +#ifdef PARANOIA +#define Z_ChangeTag(p,t) Z_ChangeTag2(p, t, __FILE__, __LINE__) +#define Z_SetUser(p,u) Z_SetUser2(p, u, __FILE__, __LINE__) +void Z_ChangeTag2(void *ptr, INT32 tag, const char *file, INT32 line); +void Z_SetUser2(void *ptr, void **newuser, const char *file, INT32 line); +#else +void Z_ChangeTag(void *ptr, INT32 tag); +void Z_SetUser(void *ptr, void **newuser); +#endif + +// +// Zone memory usage +// +// Note: These give the memory used in bytes, +// shift down by 10 to convert to KB +// #define Z_TagUsage(tagnum) Z_TagsUsage(tagnum, tagnum) size_t Z_TagsUsage(INT32 lowtag, INT32 hightag); +// +// Miscellaneous functions +// char *Z_StrDup(const char *in); - -#define Z_Unlock(p) (void)p +#define Z_Unlock(p) (void)p // TODO: remove this now that NDS code has been removed #endif