Here’s the code I’m using for video memory tracking.
Can anyone think of any improvements besides a free-list cache?
-Sam Lantinga (slouken at devolution.com)
Lead Programmer, Loki Entertainment Software–
“Any sufficiently advanced bug is indistinguishable from a feature”
– Rich Kulawiec
-------------- next part --------------
#include <stdio.h>
/* This is the structure we use to keep track of video memory */
typedef struct vidmem_bucket {
struct vidmem_bucket *prev;
int used;
int base;
int size;
struct vidmem_bucket *next;
} vidmem_bucket;
vidmem_bucket surfaces;
int surface_memtotal;
int surface_memleft;
void dump_surfaces()
{
vidmem_bucket *bucket;
printf("Memory left: %d (%d total)\n", surface_memleft, surface_memtotal);
printf("\n");
printf(" Base Size\n");
for ( bucket=&surfaces; bucket; bucket=bucket->next ) {
printf("Bucket: %4.4d, %4.4d (%s)\n", bucket->base, bucket->size, bucket->used ? "used" : "free");
if ( bucket->prev ) {
if ( bucket->base != bucket->prev->base+bucket->prev->size ) {
printf("Warning, corrupt bucket list! (prev)\n");
}
} else {
if ( bucket != &surfaces ) {
printf("Warning, corrupt bucket list! (!prev)\n");
}
}
if ( bucket->next ) {
if ( bucket->next->base != bucket->base+bucket->size ) {
printf("Warning, corrupt bucket list! (next)\n");
}
}
}
printf("\n");
}
void init_surfaces(int base, int size)
{
surfaces.prev = NULL;
surfaces.used = 0;
surfaces.base = base;
surfaces.size = size;
surfaces.next = NULL;
surface_memtotal = size;
surface_memleft = size;
}
void free_surfaces()
{
vidmem_bucket *bucket, *freeable;
bucket = surfaces.next;
while ( bucket ) {
freeable = bucket;
bucket = bucket->next;
free(freeable);
}
}
int alloc_surface(int size)
{
vidmem_bucket *bucket;
int extra;
printf(“Allocating bucket of %d bytes\n”, size);
/* Quick check for available mem */
if ( size > surface_memleft ) {
printf("Not enough video memory\n");
return(NULL);
}
/* Search for an empty bucket big enough */
for ( bucket=&surfaces; bucket; bucket=bucket->next ) {
if ( ! bucket->used && (size <= bucket->size) ) {
break;
}
}
if ( bucket == NULL ) {
printf("Video memory too fragmented\n");
return(NULL);
}
/* Create a new bucket for left-over memory */
extra = (bucket->size - size);
if ( extra ) {
vidmem_bucket *newbucket;
printf(“Adding new free bucket of %d bytes\n”, extra);
newbucket = (vidmem_bucket *)malloc(sizeof(*newbucket));
if ( newbucket == NULL ) {
printf(“Out of memory\n”);
return(NULL);
}
newbucket->prev = bucket;
newbucket->used = 0;
newbucket->base = bucket->base+size;
newbucket->size = extra;
newbucket->next = bucket->next;
if ( bucket->next ) {
bucket->next->prev = newbucket;
}
bucket->next = newbucket;
}
/* Set the current bucket values and return it! */
printf(“Allocated %d bytes at %d\n”, bucket->size, bucket->base);
bucket->used = 1;
bucket->size = size;
surface_memleft -= size;
return(bucket->base);
}
int free_surface(int base)
{
vidmem_bucket *bucket, *freeable;
/* Look for the bucket in the current list */
for ( bucket=&surfaces; bucket; bucket=bucket->next ) {
if ( bucket->base == base ) {
break;
}
}
if ( (bucket == NULL) || ! bucket->used ) {
printf("Bucket %d not found!\n", base);
return(-1);
}
/* Add the memory back to the total */
printf(“Freeing bucket of %d bytes\n”, bucket->size);
surface_memleft += bucket->size;
/* Can we merge the space with surrounding buckets? */
freeable = NULL;
bucket->used = 0;
if ( bucket->next && ! bucket->next->used ) {
printf(“Merging with next bucket, for %d total bytes\n”, bucket->size+bucket->next->size);
freeable = bucket->next;
bucket->size += bucket->next->size;
bucket->next = bucket->next->next;
if ( bucket->next ) {
bucket->next->prev = bucket;
}
memset(freeable, 0, (sizeof *freeable));
free(freeable);
}
if ( bucket->prev && ! bucket->prev->used ) {
printf(“Merging with previous bucket, for %d total bytes\n”, bucket->prev->size+bucket->size);
freeable = bucket;
bucket->prev->size += bucket->size;
bucket->prev->next = bucket->next;
if ( bucket->next ) {
bucket->next->prev = bucket->prev;
}
memset(freeable, 0, (sizeof *freeable));
free(freeable);
}
return(0);
}
main()
{
char line[1024];
int base, size;
init_surfaces(0, 4096);
do {
dump_surfaces();
fgets(line, sizeof(line), stdin);
switch(line[0]) {
case 'a':
sscanf(&line[1], "%d", &size);
alloc_surface(size);
break;
case 'f':
sscanf(&line[1], "%d", &base);
free_surface(base);
break;
default:
break;
}
} while ( line[0] != 'q' );
free_surfaces();
exit(0);
}