diff options
author | Dave Chapman <dave@dchapman.com> | 2006-03-28 15:44:01 +0000 |
---|---|---|
committer | Dave Chapman <dave@dchapman.com> | 2006-03-28 15:44:01 +0000 |
commit | 47f4a458d636a889e955e68f896708f1276febc0 (patch) | |
tree | 99f770c02ef606f0abbdcd332ac39e69830d8007 /apps/plugins/doom/z_zone.c | |
parent | fff7d6157d56f233cad5c2003475e47a5ff809a7 (diff) |
Patch #2969 - Doom! Currently only working on the H300.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@9312 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins/doom/z_zone.c')
-rw-r--r-- | apps/plugins/doom/z_zone.c | 666 |
1 files changed, 666 insertions, 0 deletions
diff --git a/apps/plugins/doom/z_zone.c b/apps/plugins/doom/z_zone.c new file mode 100644 index 0000000000..036c995743 --- /dev/null +++ b/apps/plugins/doom/z_zone.c @@ -0,0 +1,666 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Zone Memory Allocation. Neat. + * + * Neat enough to be rewritten by Lee Killough... + * + * Must not have been real neat :) + * + * Made faster and more general, and added wrappers for all of Doom's + * memory allocation functions, including malloc() and similar functions. + * Added line and file numbers, in case of error. Added performance + * statistics and tunables. + *----------------------------------------------------------------------------- + */ + +#include "z_zone.h" +#include "z_bmalloc.h" +#include "doomdef.h" +#include "i_system.h" +#include "rockmacros.h" +#include "m_argv.h" + +// Tunables + +// Alignment of zone memory (benefit may be negated by HEADER_SIZE, CHUNK_SIZE) +#define CACHE_ALIGN 32 + +// Minimum chunk size at which blocks are allocated +#define CHUNK_SIZE 32 + +// Minimum size a block must be to become part of a split +#define MIN_BLOCK_SPLIT (1024) + +// Minimum RAM machine is assumed to have +/* cph - Select zone size. 6megs is usable, but with the SDL version + * storing sounds in the zone, 8 is more sensible */ +#define MIN_RAM (8*1024*1024) + +// Amount to subtract when retrying failed attempts to allocate initial pool +#define RETRY_AMOUNT (256*1024) + +// signature for block header +#define ZONEID 0x931d4a11 + +// Number of mallocs & frees kept in history buffer (must be a power of 2) +#define ZONE_HISTORY 4 + +// End Tunables + +typedef struct memblock { + +#ifdef ZONEIDCHECK + unsigned id; +#endif + + struct memblock *next,*prev; + size_t size; + void **user; + unsigned char tag,vm; + +#ifdef INSTRUMENTED + unsigned short extra; + const char *file; + int line; +#endif + +} memblock_t; + +/* size of block header + * cph - base on sizeof(memblock_t), which can be larger than CHUNK_SIZE on + * 64bit architectures */ +static const size_t HEADER_SIZE IDATA_ATTR= (sizeof(memblock_t)+CHUNK_SIZE-1) & ~(CHUNK_SIZE-1); + +static memblock_t *rover IBSS_ATTR; // roving pointer to memory blocks +static memblock_t *zone IBSS_ATTR; // pointer to first block +static memblock_t *zonebase IBSS_ATTR; // pointer to entire zone memory +static size_t zonebase_size IBSS_ATTR; // zone memory allocated size + +#ifdef INSTRUMENTED + +// statistics for evaluating performance +static size_t free_memory; +static size_t active_memory; +static size_t purgable_memory; +static size_t inactive_memory; +static size_t virtual_memory; + +static void Z_PrintStats(void) // Print allocation statistics +{ + unsigned long total_memory = free_memory + active_memory + + purgable_memory + inactive_memory + + virtual_memory; + double s = 100.0 / total_memory; + + doom_printf("%-5u\t%6.01f%%\tstatic\n" + "%-5u\t%6.01f%%\tpurgable\n" + "%-5u\t%6.01f%%\tfree\n" + "%-5u\t%6.01f%%\tfragmentary\n" + "%-5u\t%6.01f%%\tvirtual\n" + "%-5lu\t\ttotal\n", + active_memory, + active_memory*s, + purgable_memory, + purgable_memory*s, + free_memory, + free_memory*s, + inactive_memory, + inactive_memory*s, + virtual_memory, + virtual_memory*s, + total_memory + ); +} + +#ifdef HEAPDUMP +void W_PrintLump(FILE* fp, void* p); + +void Z_DumpMemory(void) +{ + static int dump; + memblock_t* block = zone; + char buf[80]; + FILE* fp; + size_t total_cache = 0, total_free = 0, total_malloc = 0; + + sprintf(buf, "memdump.%d", dump++); + fp = fopen(buf, "w"); + do { + switch (block->tag) { + case PU_FREE: + fprintf(fp, "free %d\n", block->size); + total_free += block->size; + break; + case PU_CACHE: + fprintf(fp, "cache %s:%d:%d\n", block->file, block->line, block->size); + total_cache += block->size; + break; + case PU_LEVEL: + fprintf(fp, "level %s:%d:%d\n", block->file, block->line, block->size); + total_malloc += block->size; + break; + default: + fprintf(fp, "malloc %s:%d:%d", block->file, block->line, block->size); + total_malloc += block->size; + if (!strcmp(block->file,"w_wad.c")) W_PrintLump(fp, (char*)block + HEADER_SIZE); + fputc('\n', fp); + break; + } + block=block->next; + } while (block != zone); + fprintf(fp, "malloc %d, cache %d, free %d, total %d\n", + total_malloc, total_cache, total_free, + total_malloc + total_cache + total_free); + fclose(fp); +} +#endif +#endif + +#ifdef INSTRUMENTED + +// killough 4/26/98: Add history information + +enum {malloc_history, free_history, NUM_HISTORY_TYPES}; + +static const char *file_history[NUM_HISTORY_TYPES][ZONE_HISTORY]; +static int line_history[NUM_HISTORY_TYPES][ZONE_HISTORY]; +static int history_index[NUM_HISTORY_TYPES]; +static const char *const desc[NUM_HISTORY_TYPES] = {"malloc()'s", "free()'s"}; + +void Z_DumpHistory(char *buf) +{ + int i,j; + char s[1024]; + strcat(buf,"\n"); + for (i=0;i<NUM_HISTORY_TYPES;i++) + { + sprintf(s,"\nLast several %s:\n\n", desc[i]); + strcat(buf,s); + for (j=0; j<ZONE_HISTORY; j++) + { + int k = (history_index[i]-j-1) & (ZONE_HISTORY-1); + if (file_history[i][k]) + { + sprintf(s, "File: %s, Line: %d\n", file_history[i][k], + line_history[i][k]); + strcat(buf,s); + } + } + } +} +#else + +void Z_DumpHistory(char *buf) +{ + (void)buf; +} + +#endif + +void Z_Close(void) +{ +// (free)(zonebase); + zone = rover = zonebase = NULL; +} + +void Z_Init(void) +{ + unsigned int size; +#ifdef INSTRUMENTED + if (!(HEADER_SIZE >= sizeof(memblock_t) && MIN_RAM > LEAVE_ASIDE)) + I_Error("Z_Init: Sanity check failed"); +#endif + +// atexit(Z_Close); // exit handler + + // Allocate the memory + + zonebase=rb->plugin_get_audio_buffer(&size); + size-=2*(HEADER_SIZE + CACHE_ALIGN); // Leave space for header and CACHE_ALIGN + size = (size+CHUNK_SIZE-1) & ~(CHUNK_SIZE-1); // round to chunk size + size += HEADER_SIZE + CACHE_ALIGN; + + zonebase_size=size; + + printf("Z_Init: Allocated %dKb zone memory\n", (long unsigned)size >> 10); + + // Align on cache boundary + + zone = (memblock_t *) ((char *) zonebase + CACHE_ALIGN - + ((unsigned) zonebase & (CACHE_ALIGN-1))); + + rover = zone; // Rover points to base of zone mem + zone->next = zone->prev = zone; // Single node + zone->size = size; // All memory in one block + zone->tag = PU_FREE; // A free block + zone->vm = 0; + +#ifdef ZONEIDCHECK + zone->id = 0; +#endif + +#ifdef INSTRUMENTED + free_memory = size; + inactive_memory = zonebase_size - size; + active_memory = purgable_memory = virtual_memory = 0; +#endif +} + +/* Z_Malloc + * You can pass a NULL user if the tag is < PU_PURGELEVEL. + * + * cph - the algorithm here was a very simple first-fit round-robin + * one - just keep looping around, freeing everything we can until + * we get a large enough space + * + * This has been changed now; we still do the round-robin first-fit, + * but we only free the blocks we actually end up using; we don't + * free all the stuff we just pass on the way. + */ + +void *(Z_Malloc)(size_t size, int tag, void **user +#ifdef INSTRUMENTED + , const char *file, int line +#endif + ) +{ + register memblock_t *block; + memblock_t *start, *first_of_free; + register size_t contig_free; + +#ifdef INSTRUMENTED + size_t size_orig = size; +#ifdef CHECKHEAP + Z_CheckHeap(); +#endif + + file_history[malloc_history][history_index[malloc_history]] = file; + line_history[malloc_history][history_index[malloc_history]++] = line; + history_index[malloc_history] &= ZONE_HISTORY-1; +#endif + +#ifdef ZONEIDCHECK + if (tag >= PU_PURGELEVEL && !user) + I_Error ("Z_Malloc: An owner is required for purgable blocks" +#ifdef INSTRUMENTED + "Source: %s:%d", file, line +#endif + ); +#endif + + if (!size) + return user ? *user = NULL : NULL; // malloc(0) returns NULL + + size = (size+CHUNK_SIZE-1) & ~(CHUNK_SIZE-1); // round to chunk size + + block = rover; + + if (block->prev->tag == PU_FREE) + block = block->prev; + + start = block; + first_of_free = NULL; contig_free = 0; + + do { + /* If we just wrapped, we're not contiguous with the previous block */ + if (block == zone) contig_free = 0; + + if (block->tag < PU_PURGELEVEL && block->tag != PU_FREE) { + /* Not free(able), so no free space here */ + contig_free = 0; + } else { + /* Add to contiguous chunk of free space */ + if (!contig_free) first_of_free = block; + contig_free += block->size; + + /* First fit */ + if (contig_free >= size) + break; + } + } + while ((block = block->next) != start); // detect cycles as failure + + if (contig_free >= size) { + /* We have a block of free(able) memory on the heap which will suffice */ + block = first_of_free; + + /* If the previous block is adjacent and free, step back and include it */ + if (block != zone && block->prev->tag == PU_FREE) + block = block->prev; + + /* Free current block if needed */ + if (block->tag != PU_FREE) Z_Free((char *) block + HEADER_SIZE); + + /* Note: guaranteed that block->prev is either + * not free or not contiguous + * + * At every step, block->next must be not free, else it would + * have been merged with our block + * No range check needed because we know it works by the previous loop */ + while (block->size < size) + Z_Free((char *)(block->next) + HEADER_SIZE); + + /* Now, carve up the block */ + { + size_t extra = block->size - size; + if (extra >= MIN_BLOCK_SPLIT + HEADER_SIZE) { + memblock_t *newb = (memblock_t *)((char *) block + + HEADER_SIZE + size); + + (newb->next = block->next)->prev = newb; + (newb->prev = block)->next = newb; // Split up block + block->size = size; + newb->size = extra - HEADER_SIZE; + newb->tag = PU_FREE; + newb->vm = 0; + +#ifdef INSTRUMENTED + inactive_memory += HEADER_SIZE; + free_memory -= HEADER_SIZE; +#endif + } + + rover = block->next; // set roving pointer for next search + +#ifdef INSTRUMENTED + inactive_memory += block->extra = block->size - size_orig; + if (tag >= PU_PURGELEVEL) + purgable_memory += size_orig; + else + active_memory += size_orig; + free_memory -= block->size; +#endif + } + } else { // We don't have enough contiguous free blocks + I_Error ("Z_Malloc: Failure trying to allocate %d bytes",(unsigned long) size); + rb->sleep(300); + } + +#ifdef INSTRUMENTED + block->file = file; + block->line = line; +#endif + +#ifdef ZONEIDCHECK + block->id = ZONEID; // signature required in block header +#endif + block->tag = tag; // tag + block->user = user; // user + block = (memblock_t *)((char *) block + HEADER_SIZE); + if (user) // if there is a user + *user = block; // set user to point to new block + +#ifdef INSTRUMENTED + Z_PrintStats(); // print memory allocation stats + // scramble memory -- weed out any bugs + memset(block, gametic & 0xff, size); +#endif + return block; +} + +void (Z_Free)(void *p +#ifdef INSTRUMENTED + , const char *file, int line +#endif + ) +{ +#ifdef INSTRUMENTED +#ifdef CHECKHEAP + Z_CheckHeap(); +#endif + file_history[free_history][history_index[free_history]] = file; + line_history[free_history][history_index[free_history]++] = line; + history_index[free_history] &= ZONE_HISTORY-1; +#endif + + if (p) + { + memblock_t *other, *block = (memblock_t *)((char *) p - HEADER_SIZE); + +#ifdef ZONEIDCHECK + if (block->id != ZONEID) + I_Error("Z_Free: freed a pointer without ZONEID" +#ifdef INSTRUMENTED + "\nSource: %s:%d" + "\nSource of malloc: %s:%d" + , file, line, block->file, block->line +#endif + ); + block->id = 0; // Nullify id so another free fails +#endif + +#ifdef INSTRUMENTED + /* scramble memory -- weed out any bugs */ + memset(p, gametic & 0xff, block->size); +#endif + + if (block->user) // Nullify user if one exists + *block->user = NULL; + + { + +#ifdef INSTRUMENTED + free_memory += block->size; + inactive_memory -= block->extra; + if (block->tag >= PU_PURGELEVEL) + purgable_memory -= block->size - block->extra; + else + active_memory -= block->size - block->extra; +#endif + + block->tag = PU_FREE; // Mark block freed + + if (block != zone) + { + other = block->prev; // Possibly merge with previous block + if (other->tag == PU_FREE) + { + if (rover == block) // Move back rover if it points at block + rover = other; + (other->next = block->next)->prev = other; + other->size += block->size + HEADER_SIZE; + block = other; + +#ifdef INSTRUMENTED + inactive_memory -= HEADER_SIZE; + free_memory += HEADER_SIZE; +#endif + } + } + + other = block->next; // Possibly merge with next block + if (other->tag == PU_FREE && other != zone) + { + if (rover == other) // Move back rover if it points at next block + rover = block; + (block->next = other->next)->prev = block; + block->size += other->size + HEADER_SIZE; + +#ifdef INSTRUMENTED + inactive_memory -= HEADER_SIZE; + free_memory += HEADER_SIZE; +#endif + } + } + +#ifdef INSTRUMENTED + Z_PrintStats(); // print memory allocation stats +#endif + } +} + +void (Z_FreeTags)(int lowtag, int hightag +#ifdef INSTRUMENTED + , const char *file, int line +#endif + ) +{ + /* cph - move rover to start of zone; we like to encourage static + * data to stay in one place, at the start of the heap + */ + memblock_t *block = rover = zone; + +#ifdef HEAPDUMP + Z_DumpMemory(); +#endif + + if (lowtag <= PU_FREE) + lowtag = PU_FREE+1; + + do // Scan through list, searching for tags in range + if (block->tag >= lowtag && block->tag <= hightag) + { + memblock_t *prev = block->prev, *cur = block; +#ifdef INSTRUMENTED + (Z_Free)((char *) block + HEADER_SIZE, file, line); +#else + (Z_Free)((char *) block + HEADER_SIZE); +#endif + /* cph - be more careful here, we were skipping blocks! + * If the current block was not merged with the previous, + * cur is still a valid pointer, prev->next == cur, and cur is + * already free so skip to the next. + * If the current block was merged with the previous, + * the next block to analyse is prev->next. + * Note that the while() below does the actual step forward + */ + block = (prev->next == cur) ? cur : prev; + } + while ((block=block->next) != zone); +} + +void (Z_ChangeTag)(void *ptr, int tag +#ifdef INSTRUMENTED + , const char *file, int line +#endif + ) +{ + memblock_t *block = (memblock_t *)((char *) ptr - HEADER_SIZE); + +#ifdef INSTRUMENTED +#ifdef CHECKHEAP + Z_CheckHeap(); +#endif +#endif + +#ifdef ZONEIDCHECK + if (block->id != ZONEID) + I_Error ("Z_ChangeTag: freed a pointer without ZONEID" +#ifdef INSTRUMENTED + "\nSource: %s:%d" + "\nSource of malloc: %s:%d" + , file, line, block->file, block->line +#endif + ); + + if (tag >= PU_PURGELEVEL && !block->user) + I_Error ("Z_ChangeTag: an owner is required for purgable blocks\n" +#ifdef INSTRUMENTED + "Source: %s:%d" + "\nSource of malloc: %s:%d" + , file, line, block->file, block->line +#endif + ); + +#endif // ZONEIDCHECK + + { +#ifdef INSTRUMENTED + if (block->tag < PU_PURGELEVEL && tag >= PU_PURGELEVEL) + { + active_memory -= block->size - block->extra; + purgable_memory += block->size - block->extra; + } + else + if (block->tag >= PU_PURGELEVEL && tag < PU_PURGELEVEL) + { + active_memory += block->size - block->extra; + purgable_memory -= block->size - block->extra; + } +#endif + } + block->tag = tag; +} + +void *(Z_Realloc)(void *ptr, size_t n, int tag, void **user +#ifdef INSTRUMENTED + , const char *file, int line +#endif + ) +{ + void *p = (Z_Malloc)(n, tag, user DA(file, line)); + if (ptr) + { + memblock_t *block = (memblock_t *)((char *) ptr - HEADER_SIZE); + memcpy(p, ptr, n <= block->size ? n : block->size); + (Z_Free)(ptr DA(file, line)); + if (user) // in case Z_Free nullified same user + *user=p; + } + return p; +} + +void *(Z_Calloc)(size_t n1, size_t n2, int tag, void **user +#ifdef INSTRUMENTED + , const char *file, int line +#endif + ) +{ + return + (n1*=n2) ? memset((Z_Malloc)(n1, tag, user DA(file, line)), 0, n1) : NULL; +} + +char *(Z_Strdup)(const char *s, int tag, void **user +#ifdef INSTRUMENTED + , const char *file, int line +#endif + ) +{ + return strcpy((Z_Malloc)(strlen(s)+1, tag, user DA(file, line)), s); +} + +void (Z_CheckHeap)( +#ifdef INSTRUMENTED + const char *file, int line +#endif +) +{ + memblock_t *block = zone; // Start at base of zone mem + do // Consistency check (last node treated special) + if ((block->next != zone && + (memblock_t *)((char *) block+HEADER_SIZE+block->size) != block->next) + || block->next->prev != block || block->prev->next != block) + I_Error("Z_ChkHp: B size %d touch %d\n", block+HEADER_SIZE+block->size, block->next +#ifdef INSTRUMENTED + "Source: %s:%d" + "\nSource of offending block: %s:%d" + , file, line, block->file, block->line +#endif + ); + while ((block=block->next) != zone); +} |