| /*************************************************************************/ |
| /* */ |
| /* Copyright (c) 1994 Stanford University */ |
| /* */ |
| /* All rights reserved. */ |
| /* */ |
| /* Permission is given to use, copy, and modify this software for any */ |
| /* non-commercial purpose as long as this copyright notice is not */ |
| /* removed. All other uses, including redistribution in whole or in */ |
| /* part, are forbidden without prior written permission. */ |
| /* */ |
| /* This software is provided with absolutely no warranty and no */ |
| /* support. */ |
| /* */ |
| /*************************************************************************/ |
| |
| |
| /* |
| * NAME |
| * memory.c |
| * |
| * DESCRIPTION |
| * This file contains routines that handle all local and global memory |
| * allocation and deallocation for the ray tracer. |
| * |
| * Various statistics are maintained and printed by these routines as |
| * well. |
| * |
| */ |
| |
| |
| #include <stdio.h> |
| #include <math.h> |
| #include "rt.h" |
| |
| |
| |
| #define CKSM 0x55AA55AA |
| #define PAGESIZE (4*1024) |
| #define THRESHOLD (sizeof(NODE) + 16) |
| #define ROUND_UP(x) ((((x) + 7) >> 3) << 3) |
| #define ROUND_DN(x) (x & 0xFFFFFFF8) |
| #define NODE_ADD(x, y) (NODE huge *)((U8 huge *)(x) + (y)) |
| |
| |
| |
| /* |
| * Define variables local to this module. |
| */ |
| |
| UINT nodesize; /* Size of a node structure. */ |
| NODE huge *begmem; /* Ptr to first byte in heap. */ |
| NODE huge *endmem; /* Prt to last byte in heap. */ |
| |
| |
| |
| /* |
| * Allocate storage for memory usage statistics counters. |
| */ |
| |
| INT mem_grid; |
| INT maxmem_grid; |
| |
| INT mem_voxel; |
| INT maxmem_voxel; |
| |
| INT mem_hashtable; |
| INT maxmem_hashtable; |
| |
| INT mem_emptycells; |
| INT maxmem_emptycells; |
| |
| INT mem_bintree; |
| INT maxmem_bintree; |
| |
| INT mem_pepArray; |
| INT maxmem_pepArray; |
| |
| |
| |
| /* |
| * NAME |
| * LocalMalloc - allocate local memory and check validity |
| * |
| * SYNOPSIS |
| * VOID *LocalMalloc(n, msg) |
| * UINT n; // Number of bytes to allocate. |
| * CHAR *msg; // String to print if it fails. |
| * |
| * DESCRIPTION |
| * Malloc is simply provided as a single point of control for local |
| * memory allocation. Because checks for the validity of the allocation |
| * are made in this routine, they need not be performed by the caller. |
| * If the call fails, the caller can identify the context by including |
| * an appropriate identification string pointed to by msg. This string |
| * will become a part of the RIP message. |
| * |
| * RETURNS |
| * A pointer to the new memory if successful, otherwise the routine |
| * generates an error exit. |
| */ |
| |
| VOID *LocalMalloc(n, msg) |
| UINT n; |
| CHAR *msg; |
| { |
| VOID *p; |
| |
| p = (VOID *) malloc(n); |
| if (!p) |
| { |
| printf("%s: %s cannot allocate local memory.\n", ProgName, msg); |
| exit(-1); |
| } |
| |
| return (p); |
| } |
| |
| |
| |
| /* |
| * NAME |
| * LocalFree - deallocate local memory |
| * |
| * SYNOPSIS |
| * VOID LocalFree(n) |
| * VOID *p; |
| * |
| * DESCRIPTION |
| * As currently implemented, LocalFree() simply calls the standard |
| * library function free(), and thus is not strictly needed. However, it |
| * does provide single point control for local memory deallocation such |
| * that future changes to this routine may make it more useful. |
| * |
| * RETURNS |
| * Nothing. |
| */ |
| |
| VOID LocalFree(p) |
| VOID *p; |
| { |
| free(p); |
| |
| } |
| |
| |
| |
| /* |
| * NAME |
| * GlobalHeapWalk - walk the global heap and print info |
| * |
| * SYNOPSIS |
| * VOID GlobalHeapWalk() |
| * |
| * DESCRIPTION |
| * |
| * RETURNS |
| * Nothing. |
| * |
| */ |
| |
| VOID GlobalHeapWalk() |
| { |
| NODE huge *curr; |
| |
| LOCK(gm->memlock) |
| curr = begmem; |
| |
| printf("freelist ->\t0x%08lX\n\n", (U32)gm->freelist); |
| |
| printf("node addr \tnode->next\tnode->size\tnode->free\tnode->cksm\n"); |
| printf("==========\t==========\t==========\t==========\t==========\n"); |
| |
| while (curr < endmem) |
| { |
| printf("0x%08lX\t0x%08lX\t%10ld\t%s\t\t0x%08lX\n", |
| (U32)curr, (U32)curr->next, curr->size, |
| (curr->free ? "FREE" : " "), curr->cksm); |
| |
| if (curr->cksm != CKSM) |
| { |
| fprintf(stderr, "GlobalHeapWalk: Invalid checksum in node.\n"); |
| exit(1); |
| } |
| |
| curr = NODE_ADD(curr, curr->size + nodesize); |
| } |
| |
| UNLOCK(gm->memlock) |
| } |
| |
| |
| |
| /* |
| * NAME |
| * GlobalHeapInit - initialize the global memory management system |
| * |
| * SYNOPSIS |
| * BOOL GlobalHeapInit(size) |
| * UINT size; // Size in bytes of global memory. |
| * |
| * DESCRIPTION |
| * GlobalHeapInit initializes the global memory management system by |
| * setting up the heap. It MUST be called by one (and only one) |
| * processor before any of the other memory management functions are |
| * called. Typically, this is done by the main() routine just after the |
| * MAIN INITENV call is made. |
| * |
| * GlobalHeapInit attempts to allocate size bytes of global shared memory |
| * from the multiprocessor OS. If the request is satisfied, the memory |
| * block becomes the first node on the free list. |
| * |
| * RETURNS |
| * TRUE if the global memory heap of the requested size could be allocated; |
| * FALSE otherwise. |
| */ |
| |
| BOOL GlobalHeapInit(size) |
| UINT size; |
| { |
| INT i; |
| U8 *ptr; |
| |
| size = ROUND_UP(size); |
| gm->freelist = (NODE huge *)G_MALLOC(size); |
| |
| if (!gm->freelist) |
| return (FALSE); |
| |
| nodesize = sizeof(NODE); |
| begmem = gm->freelist; |
| endmem = NODE_ADD(gm->freelist, size); |
| |
| gm->freelist->size = size - nodesize; |
| gm->freelist->next = NULL; |
| gm->freelist->free = TRUE; |
| gm->freelist->cksm = CKSM; |
| |
| /* NOTE TO USERS: Here's where one can allocate the memory segment from |
| begmem to endmem round-robin among memories or however one desires */ |
| |
| return (TRUE); |
| } |
| |
| |
| |
| /* |
| * NAME |
| * GlobalMalloc - allocate a node of given size from global memory |
| * |
| * SYNOPSIS |
| * VOID *GlobalMalloc(size, msg) |
| * UINT size; // Size of block in bytes. |
| * CHAR *msg; // Size of block in bytes. |
| * |
| * DESCRIPTION |
| * The GlobalMalloc() function allocates space for an object of size |
| * bytes and returns a pointer to the space. This function does not |
| * modify the memory it allocates. The storage space pointed to by the |
| * return value is guaranteed to be suitably aligned for storage of any |
| * type of object. |
| * |
| * NOTES |
| * Because checks for the validity of the allocation are made in this |
| * routine, they need not be performed by the caller. If the call fails, |
| * the caller can identify the context by including an appropriate |
| * identification string pointed to by msg. This string will become a |
| * part of the RIP message. |
| * |
| * RETURNS |
| * A pointer to the allocated space if successful. NULL if size is 0. |
| * Generates error exit if there is insufficient memory available. |
| */ |
| |
| VOID *GlobalMalloc(size, msg) |
| UINT size; |
| CHAR *msg; |
| { |
| NODE huge *prev; |
| NODE huge *curr; |
| NODE huge *next; |
| |
| if (!size) |
| return (NULL); |
| |
| LOCK(gm->memlock) |
| |
| prev = NULL; |
| curr = gm->freelist; |
| size = ROUND_UP(size); |
| |
| /* |
| * Scan through list for large enough node (first fit). |
| */ |
| |
| while (curr && curr->size < size) |
| { |
| if (curr->cksm != CKSM) |
| { |
| fprintf(stderr, "GlobalMalloc: Invalid checksum in node.\n"); |
| exit(1); |
| } |
| |
| if (curr->free != TRUE) |
| { |
| fprintf(stderr, "GlobalMalloc: Node in free list not marked as free.\n"); |
| exit(1); |
| } |
| |
| prev = curr; |
| curr = curr->next; |
| } |
| |
| |
| if (!curr) |
| { |
| fprintf(stderr, "%s: %s cannot allocate global memory.\n", ProgName, msg); |
| exit(-1); |
| } |
| |
| |
| /* |
| * If node is larger than needed, free extra space at end |
| * by inserting remaining space into free list. |
| */ |
| |
| if (curr->size - size > THRESHOLD) |
| { |
| next = NODE_ADD(curr, nodesize + size); |
| next->size = curr->size - nodesize - size; |
| next->next = curr->next; |
| next->free = TRUE; |
| next->cksm = CKSM; |
| curr->size = size; |
| } |
| else |
| next = curr->next; |
| |
| |
| if (!prev) |
| gm->freelist = next; |
| else |
| prev->next = next; |
| |
| |
| UNLOCK(gm->memlock) |
| curr->next = NULL; |
| curr->free = FALSE; |
| curr = NODE_ADD(curr, nodesize); |
| |
| return ((VOID *)curr); |
| } |
| |
| |
| |
| /* |
| * NAME |
| * GlobalCalloc - allocate n elements of given size and initialize to 0 |
| * |
| * SYNOPSIS |
| * VOID *GlobalCalloc(n, size) |
| * UINT n; // Number of elements to allocate. |
| * UINT size; // Size of each element in bytes. |
| * |
| * DESCRIPTION |
| * The GlobalCalloc() function allocates space for n elements of size |
| * bytes each. It initializes each element to 0, and returns a pointer |
| * to the allocated space. The storage space pointed to by the return |
| * value is guaranteed to be suitably aligned for storage of any type of |
| * object. |
| * |
| * RETURNS |
| * A pointer to the allocated space if successful. NULL if n or size are |
| * 0. Generates error exit if there is insufficient memory available. |
| * |
| */ |
| |
| VOID *GlobalCalloc(n, size) |
| UINT n; |
| UINT size; |
| { |
| UINT nbytes; |
| UINT huge *p; |
| VOID huge *q; |
| |
| nbytes = ROUND_UP(n*size); |
| |
| p = q = GlobalMalloc(nbytes, "GlobalCalloc"); |
| |
| nbytes >>= 2; /* Need size in words. */ |
| while (nbytes--) |
| *p++ = 0; |
| |
| return (q); |
| } |
| |
| |
| |
| /* |
| * NAME |
| * GlobalRealloc - reallocate a node to a new size |
| * |
| * SYNOPSIS |
| * VOID *GlobalRealloc(p, size) |
| * VOID *p; // Ptr to object to change. |
| * UINT size; // New size in bytes of node. |
| * |
| * DESCRIPTION |
| * The GlobalRealloc() function changes the size of the allocated memory |
| * pointed to by p to the number of bytes specified by size. The |
| * contents of the memory space (up to the lesser of the old and new |
| * sizes) is not changed. |
| * |
| * There are three main cases to consider: |
| * |
| * Allocating a new block: |
| * |
| * If p is NULL, then GlobalRealloc() behaves like |
| * GlobalMalloc(). In other words, it attempts to allocate the |
| * requested number of bytes. |
| * |
| * Shrinking an old block: |
| * |
| * Shrinking a block is always possible (assuming valid |
| * arguments). If the new size is less than the old size, the |
| * memory block is truncated at the new size and the remaining |
| * memory is freed. |
| * |
| * If size = 0 and p is not NULL, then the entire memory block is |
| * freed and NULL is returned. |
| * |
| * Expanding an old block: |
| * |
| * If the new size is larger than the old size, this function |
| * first attempts to append more contiguous memory on the end of |
| * the old block. If successful, the return value is the same as |
| * p. |
| * |
| * If there is no free space following the old block, this |
| * function then tries allocate space of the new size elsewhere |
| * in memory. If successful, the old block will be moved |
| * (copied) to the new block. The old block will be freed, and a |
| * pointer to the new block returned. |
| * |
| * If the new space cannot be allocated, the original memory |
| * space is not changed and the return value is NULL. |
| * |
| * |
| * If p points to an unallocated space, an error exit occurs. |
| * |
| * RETURNS |
| * As stated above. |
| */ |
| |
| VOID *GlobalRealloc(p, size) |
| VOID *p; |
| UINT size; |
| { |
| UINT oldsize; |
| UINT newsize; |
| UINT totsize; |
| VOID huge *q; |
| UINT huge *r; |
| UINT huge *s; |
| NODE huge *pn; |
| NODE huge *prev; |
| NODE huge *curr; |
| NODE huge *next; |
| NODE huge *node; |
| |
| if (!size) |
| { |
| GlobalFree(p); |
| return (NULL); |
| } |
| |
| if (!p) |
| return (GlobalMalloc(size, "GlobalRealloc")); |
| |
| |
| pn = NODE_ADD(p, -nodesize); /* Adjust ptr back to arena. */ |
| |
| if (pn->cksm != CKSM) |
| { |
| fprintf(stderr, "GlobalRealloc: Attempted to realloc node with invalid checksum.\n"); |
| exit(1); |
| } |
| |
| if (pn->free) |
| { |
| fprintf(stderr, "GlobalRealloc: Attempted to realloc an unallocated node.\n"); |
| exit(1); |
| } |
| |
| |
| newsize = ROUND_UP(size); |
| oldsize = pn->size; |
| |
| |
| /* |
| * If new size is less than current node size, truncate the node |
| * and return end to free list. |
| */ |
| |
| if (newsize <= oldsize) |
| { |
| if (oldsize - newsize < THRESHOLD) |
| return (p); |
| |
| pn->size = newsize; |
| |
| next = NODE_ADD(p, newsize); |
| next->size = oldsize - nodesize - newsize; |
| next->next = NULL; |
| next->free = FALSE; |
| next->cksm = CKSM; |
| next = NODE_ADD(next, nodesize); |
| |
| GlobalFree(next); |
| return (p); |
| } |
| |
| |
| /* |
| * New size is bigger than current node. Try to expand next node |
| * in list. |
| */ |
| |
| next = NODE_ADD(p, oldsize); |
| totsize = oldsize + nodesize + next->size; |
| |
| LOCK(gm->memlock) |
| if (next < endmem && next->free && totsize >= newsize) |
| { |
| /* Find next in free list. */ |
| |
| prev = NULL; |
| curr = gm->freelist; |
| |
| while (curr && curr < next && curr < endmem) |
| { |
| prev = curr; |
| curr = curr->next; |
| } |
| |
| if (curr != next) |
| { |
| fprintf(stderr, "GlobalRealloc: Could not find next node in free list.\n"); |
| exit(1); |
| } |
| |
| if (totsize - newsize < THRESHOLD) |
| { |
| /* Just remove next from free list. */ |
| |
| if (!prev) |
| gm->freelist = next->next; |
| else |
| prev->next = next->next; |
| |
| next->next = NULL; |
| next->free = FALSE; |
| pn->size = totsize; |
| |
| UNLOCK(gm->memlock) |
| return (p); |
| } |
| else |
| { |
| /* Remove next from free list while adding node. */ |
| |
| node = NODE_ADD(p, newsize); |
| node->next = next->next; |
| node->size = totsize - nodesize - newsize; |
| node->free = TRUE; |
| node->cksm = CKSM; |
| |
| if (!prev) |
| gm->freelist = node; |
| else |
| prev->next = node; |
| |
| next->next = NULL; |
| next->free = FALSE; |
| pn->size = newsize; |
| |
| UNLOCK(gm->memlock) |
| return (p); |
| } |
| } |
| |
| |
| /* |
| * New size is bigger than current node, but next node in list |
| * could not be expanded. Try to allocate new node and move data |
| * to new location. |
| */ |
| |
| UNLOCK(gm->memlock) |
| |
| s = q = GlobalMalloc(newsize, "GlobalRealloc"); |
| if (!q) |
| return (NULL); |
| |
| r = (UINT huge *)p; |
| oldsize >>= 2; |
| |
| while (oldsize--) |
| *s++ = *r++; |
| |
| GlobalFree(p); |
| return (q); |
| } |
| |
| |
| |
| /* |
| * NAME |
| * GlobalFree - return a node to the free list |
| * |
| * SYNOPSIS |
| * VOID GlobalFree(p) |
| * VOID *p; |
| * |
| * DESCRIPTION |
| * The GlobalFree() function deallocates memory space (pointed to by p) |
| * that was previously allocated by a call to GlobalMalloc(). This makes |
| * the memory space available again. An attempt to free unallocated |
| * memory generates an error exit. |
| * |
| * RETURNS |
| * Nothing. |
| */ |
| |
| VOID GlobalFree(p) |
| VOID *p; |
| { |
| BOOL pcom; /* TRUE if prev can combine. */ |
| BOOL ncom; /* TRUE if next can combine. */ |
| NODE huge *pn; |
| NODE huge *prev; /* Pointer to previous node. */ |
| NODE huge *curr; /* Pointer to this node. */ |
| NODE huge *next; /* Pointer to next node. */ |
| |
| if (!begmem) |
| return; |
| |
| |
| pn = NODE_ADD(p, -nodesize); /* Adjust ptr back to arena. */ |
| |
| if (pn->cksm != CKSM) |
| { |
| fprintf(stderr, "GlobalFree: Attempted to free node with invalid checksum.\n"); |
| exit(1); |
| } |
| |
| if (pn->free) |
| { |
| fprintf(stderr, "GlobalFree: Attempted to free unallocated node.\n"); |
| exit(1); |
| } |
| |
| |
| pcom = FALSE; |
| prev = NULL; |
| |
| LOCK(gm->memlock) |
| if (gm->freelist) |
| { |
| /* |
| * Search the memory arena blocks for previous free neighbor. |
| */ |
| |
| curr = gm->freelist; |
| |
| while (curr < pn && curr < endmem) |
| { |
| if (curr->cksm != CKSM) |
| { |
| fprintf(stderr, "GlobalFree: Invalid checksum in previous node.\n"); |
| exit(1); |
| } |
| |
| if (curr->free) |
| { |
| prev = curr; |
| pcom = TRUE; |
| } |
| else |
| pcom = FALSE; |
| |
| curr = NODE_ADD(curr, curr->size + nodesize); |
| } |
| |
| |
| /* |
| * Make sure we found the original node. |
| */ |
| |
| if (curr >= endmem) |
| { |
| fprintf(stdout, "freelist=0x%08lX, curr=0x%08lX, size=0x%08lX, pn=0x%08lX, endmem=0x%08lX\n", gm->freelist, curr, curr->size, pn, endmem); |
| fprintf(stderr, "GlobalFree: Search for previous block fell off end of memory.\n"); |
| exit(1); |
| } |
| } |
| |
| |
| /* |
| * Search the memory arena blocks for next free neighbor. |
| */ |
| |
| ncom = TRUE; |
| next = NULL; |
| curr = NODE_ADD(pn, pn->size + nodesize); |
| |
| while (!next && curr < endmem) |
| { |
| if (curr->cksm != CKSM) |
| { |
| fprintf(stderr, "GlobalFree: Invalid checksum in next node.\n"); |
| exit(1); |
| } |
| |
| if (curr->free) |
| next = curr; |
| else |
| ncom = FALSE; |
| |
| curr = NODE_ADD(curr, curr->size + nodesize); |
| } |
| |
| |
| if (!next) /* Loop may have fallen thru.*/ |
| ncom = FALSE; |
| |
| curr = pn; |
| curr->free = TRUE; /* Mark NODE as free. */ |
| |
| |
| /* |
| * Attempt to combine the three nodes (prev, current, next). |
| * There are 9 cases to consider (16 total, but 7 are degenerate). |
| */ |
| |
| |
| if (next && !ncom && pcom) |
| { |
| prev->next = next; |
| prev->size += curr->size + nodesize; |
| } |
| else |
| if (next && !ncom && prev && !pcom) |
| { |
| prev->next = curr; |
| curr->next = next; |
| } |
| else |
| if (next && !ncom && !prev) |
| { |
| gm->freelist = curr; |
| curr->next = next; |
| } |
| else |
| if (ncom && pcom) |
| { |
| prev->next = next->next; |
| prev->size += curr->size + next->size + 2*nodesize; |
| } |
| else |
| if (ncom && prev && !pcom) |
| { |
| prev->next = curr; |
| curr->next = next->next; |
| curr->size += next->size + nodesize; |
| } |
| else |
| if (ncom && !prev) |
| { |
| gm->freelist = curr; |
| curr->next = next->next; |
| curr->size += next->size + nodesize; |
| } |
| else |
| if (!next && pcom) |
| { |
| prev->next = NULL; |
| prev->size += curr->size + nodesize; |
| } |
| else |
| if (!next && prev && !pcom) |
| { |
| prev->next = curr; |
| curr->next = NULL; |
| } |
| else |
| if (!next && !prev) |
| { |
| gm->freelist = curr; |
| curr->next = NULL; |
| } |
| |
| UNLOCK(gm->memlock) |
| return; |
| } |
| |
| |
| |
| /* |
| * NAME |
| * GlobalMemAvl - return total memory that remains available for allocation |
| * |
| * SYNOPSIS |
| * UINT GlobalMemAvl() |
| * |
| * DESCRIPTION |
| * This function walks the free list and returns the approximate size in |
| * bytes of the memory available for dynamic memory allocation. |
| * |
| * RETURNS |
| * As stated above. |
| */ |
| |
| UINT GlobalMemAvl() |
| { |
| UINT total; |
| NODE huge *curr; |
| |
| LOCK(gm->memlock) |
| total = 0; |
| curr = gm->freelist; |
| |
| while (curr) |
| { |
| total += curr->size; |
| curr = curr->next; |
| } |
| |
| total = ROUND_DN(total); |
| |
| UNLOCK(gm->memlock) |
| return (total); |
| } |
| |
| |
| |
| /* |
| * NAME |
| * GlobalMemMax - return size of largest block that can be allocated |
| * |
| * SYNOPSIS |
| * UINT GlobalMemMax() |
| * |
| * DESCRIPTION |
| * This function walks the free list and returns the size in bytes of the |
| * largest contiguous block of memory that can be allocated from the |
| * heap. |
| * |
| * RETURNS |
| * As stated above. |
| */ |
| |
| UINT GlobalMemMax() |
| { |
| UINT max; |
| NODE huge *curr; |
| |
| LOCK(gm->memlock) |
| max = 0; |
| curr = gm->freelist; |
| |
| while (curr) |
| { |
| max = (curr->size > max ? curr->size : max); |
| curr = curr->next; |
| } |
| |
| max = ROUND_DN(max); |
| |
| UNLOCK(gm->memlock) |
| return (max); |
| } |
| |
| |
| |
| /* |
| * NAME |
| * ObjectMalloc - allocate various global memory objects |
| * |
| * SYNOPSIS |
| * VOID *ObjectMalloc(ObjectType, count) |
| * INT ObjectType; // Type of object to allocate. |
| * INT count; // Number of objects to allocate. |
| * |
| * DESCRIPTION |
| * ObjectMalloc provides a way to allocate various ray tracer objects |
| * from global memory. It computes the size in bytes required for the |
| * objects and also maintains memory usage statistics for each object |
| * type. |
| * |
| * RETURNS |
| * A pointer to the new object if successful, otherwise the routine |
| * generates an error exit. |
| */ |
| |
| VOID *ObjectMalloc(ObjectType, count) |
| INT ObjectType; |
| INT count; |
| { |
| INT n; |
| VOID *p; |
| |
| switch (ObjectType) |
| { |
| case OT_GRID: |
| n = count*sizeof(GRID); |
| p = GlobalMalloc(n, "GRID"); |
| |
| mem_grid += n; |
| maxmem_grid = Max(mem_grid, maxmem_grid); |
| break; |
| |
| case OT_VOXEL: |
| n = count*sizeof(VOXEL); |
| p = GlobalMalloc(n, "VOXEL"); |
| |
| mem_voxel += n; |
| maxmem_voxel = Max(mem_voxel, maxmem_voxel); |
| break; |
| |
| case OT_HASHTABLE: |
| { |
| INT i; |
| VOXEL **x; |
| |
| n = count*sizeof(VOXEL *); |
| p = GlobalMalloc(n, "HASHTABLE"); |
| x = p; |
| |
| for (i = 0; i < count; i++) |
| x[i] = NULL; |
| |
| mem_hashtable += n; |
| maxmem_hashtable = Max(mem_hashtable, maxmem_hashtable); |
| } |
| break; |
| |
| case OT_EMPTYCELLS: |
| { |
| INT i, w; |
| UINT *x; |
| |
| w = 1 + count/(8*sizeof(UINT)); |
| n = w*sizeof(UINT); |
| p = GlobalMalloc(n, "EMPTYCELLS"); |
| x = p; |
| |
| for (i = 0; i < w; i++) |
| x[i] = ~0; /* 1 => empty */ |
| |
| mem_emptycells += n; |
| maxmem_emptycells = Max(mem_emptycells, maxmem_emptycells); |
| } |
| break; |
| |
| case OT_BINTREE: |
| n = count*sizeof(BTNODE); |
| p = GlobalMalloc(n, "BINTREE"); |
| |
| mem_bintree += n; |
| maxmem_bintree = Max(mem_bintree, maxmem_bintree); |
| break; |
| |
| case OT_PEPARRAY: |
| n = count*sizeof(ELEMENT *); |
| p = GlobalMalloc(n, "PEPARRAY"); |
| |
| mem_pepArray += n; |
| maxmem_pepArray = Max(mem_pepArray, maxmem_pepArray); |
| break; |
| |
| default: |
| printf("ObjectMalloc: Unknown object type: %d\n", ObjectType); |
| exit(-1); |
| } |
| |
| return (p); |
| } |
| |
| |
| |
| /* |
| * NAME |
| * ObjectFree - deallocate various global memory objects |
| * |
| * SYNOPSIS |
| * VOID *ObjectFree(ObjectType, count, p) |
| * INT ObjectType; // Type of object to deallocate. |
| * INT count; // Number of objects to deallocate. |
| * VOID *p; // The object pointer to deallocate. |
| * |
| * DESCRIPTION |
| * ObjectFree simply deallocates objects which were obtained by |
| * ObjectMalloc(), updating usage statistics appropriately. |
| * |
| * RETURNS |
| * Nothing. |
| */ |
| |
| VOID ObjectFree(ObjectType, count, p) |
| INT ObjectType; |
| INT count; |
| VOID *p; |
| { |
| INT n; |
| |
| GlobalFree(p); |
| |
| switch (ObjectType) |
| { |
| case OT_GRID: |
| n = count*sizeof(GRID); |
| mem_grid -= n; |
| break; |
| |
| case OT_VOXEL: |
| n = count*sizeof(VOXEL); |
| mem_voxel -= n; |
| break; |
| |
| case OT_HASHTABLE: |
| n = count*sizeof(VOXEL *); |
| mem_hashtable -= n; |
| break; |
| |
| case OT_EMPTYCELLS: |
| n = 1 + count/(8*sizeof(UINT)); |
| n = n*sizeof(UINT); |
| mem_emptycells -= n; |
| break; |
| |
| case OT_BINTREE: |
| n = count*sizeof(BTNODE); |
| mem_bintree -= n; |
| break; |
| |
| case OT_PEPARRAY: |
| n = count*sizeof(ELEMENT *); |
| mem_pepArray -= n; |
| break; |
| |
| default: |
| printf("ObjectFree: Unknown object type: %d\n", ObjectType); |
| exit(-1); |
| } |
| } |
| |
| |
| |
| |
| RAYINFO *ma_rayinfo(r) |
| RAY *r; |
| { |
| RAYINFO *p; |
| |
| if (r->ri_indx + 1 > MAX_RAYINFO + 1) |
| { |
| fprintf(stderr, "error ma_rayinfo \n"); |
| exit(-1); |
| } |
| |
| p = (RAYINFO *)(&(r->rinfo[r->ri_indx])); |
| |
| /* |
| * It is assumed that rayinfos are allocated and released in a |
| * stack fashion, i.e. the one released is the one most recently |
| * allocated. |
| */ |
| |
| r->ri_indx += 1; |
| |
| return (p); |
| } |
| |
| |
| |
| VOID free_rayinfo(r) |
| RAY *r; |
| { |
| r->ri_indx -= 1; |
| } |
| |
| |
| |
| VOID reset_rayinfo(r) |
| RAY *r; |
| { |
| r->ri_indx = 0; |
| } |
| |
| |
| |
| VOID ma_print() |
| { |
| INT i; |
| INT mem_total; |
| INT maxmem_total; |
| |
| mem_total = mem_grid + mem_hashtable + mem_emptycells; |
| mem_total += mem_voxel + mem_bintree; |
| |
| maxmem_total = maxmem_grid + maxmem_hashtable + maxmem_emptycells; |
| maxmem_total += maxmem_voxel + maxmem_bintree; |
| |
| fprintf(stdout, "\n****** Hierarchial uniform grid memory allocation summary ******* \n\n"); |
| fprintf(stdout, " < struct >: < current > < maximum > < sizeof > \n"); |
| fprintf(stdout, " < bytes >: < bytes > < bytes > < bytes > \n\n"); |
| fprintf(stdout, " grid: %11ld %11ld %11d \n", mem_grid, maxmem_grid, sizeof(GRID) ); |
| fprintf(stdout, " hashtable entries: %11ld %11ld %11d \n", mem_hashtable, maxmem_hashtable, sizeof(VOXEL**)); |
| fprintf(stdout, " emptycell entries: %11ld %11ld %11d \n", mem_emptycells, maxmem_emptycells, sizeof(UINT) ); |
| fprintf(stdout, " voxel: %11ld %11ld %11d \n", mem_voxel, maxmem_voxel, sizeof(VOXEL) ); |
| fprintf(stdout, " bintree_node: %11ld %11ld %11d \n", mem_bintree, maxmem_bintree, sizeof(BTNODE) ); |
| |
| fprintf(stdout, "\n"); |
| fprintf(stdout, " Totals: %11ld %11ld \n\n", mem_total, maxmem_total); |
| } |
| |