file: free cached IO blocks to avoid them appearing as "leaks"

vfs: now always check filter in VDir (Not only in debug mode)
vfs_tree: free VFS nodes to avoid them appearing as "leaks". bugfix in
bucket allocator - wasn't coping with exactly filled buckets correctly
all Handle users: add to_string method that writes the interesting parts
of a resource's user data to string.
h_mgr: show this information when a handle is closed
ogl_tex: add ogl_tex_find
add 2 timer clients
mem: now record address of function that allocated memory
GameSetup: fix shutdown order
Renderer: allow freeing individual alpha map textures (we cache the
composite)

This was SVN commit r2981.
This commit is contained in:
janwas 2005-10-21 07:47:38 +00:00
parent 32cfc6d807
commit d43aa11d36
19 changed files with 194 additions and 75 deletions

View File

@ -1038,6 +1038,14 @@ skip_issue:
}
static void block_shutdown()
{
for(BlockIt it = block_cache.begin(); it != block_cache.end(); ++it)
mem_free(it->second);
}
// remove all blocks loaded from the file <fn>. used when reloading the file.
int file_invalidate_cache(const char* fn)
{
@ -1394,5 +1402,6 @@ int file_unmap(File* f)
int file_shutdown()
{
aiocb_pool_shutdown();
block_shutdown();
return 0;
}

View File

@ -98,12 +98,10 @@ struct VDir
uint it_valid : 1; // <it> will be closed iff == 1
// safety check
#ifndef NDEBUG
const char* filter;
// has filter been assigned? this flag is necessary because there are no
// "invalid" filter values we can use.
uint filter_latched : 1;
#endif
};
H_TYPE_DEFINE(VDir);
@ -138,11 +136,19 @@ static int VDir_reload(VDir* vd, const char* path, Handle UNUSED(hvd))
static int VDir_validate(const VDir* vd)
{
// note: <it> is opaque and cannot be validated.
#ifndef NDEBUG
if(vd->filter && !isprint(vd->filter[0]))
return -2;
#endif
UNUSED2(vd);
return 0;
}
static int VDir_to_string(const VDir* d, char* buf)
{
const char* filter = d->filter;
if(!d->filter_latched)
filter = "?";
if(!filter)
filter = "*";
snprintf(buf, H_STRING_LEN, "(%s)", filter);
return 0;
}
@ -190,7 +196,6 @@ int vfs_dir_next_ent(const Handle hd, DirEnt* ent, const char* filter)
// it is imaginable that someone will want to change it, but until
// there's a good reason, leave this check in. note: only comparing
// pointers isn't 100% certain, but it's safe enough and easy.
#ifndef NDEBUG
if(!vd->filter_latched)
{
vd->filter = filter;
@ -198,7 +203,6 @@ int vfs_dir_next_ent(const Handle hd, DirEnt* ent, const char* filter)
}
if(vd->filter != filter)
debug_warn("filter has changed for this directory. are you scanning it twice?");
#endif
bool want_dir = true;
if(filter)
@ -416,6 +420,12 @@ static int VFile_validate(const VFile* vf)
return 0;
}
static int VFile_to_string(const VFile* UNUSED(vf), char* buf)
{
strcpy(buf, ""); // safe
return 0;
}
// return the size of an already opened file, or a negative error code.
ssize_t vfs_size(Handle hf)
@ -596,7 +606,7 @@ debug_printf("vfs_load v_fn=%s\n", v_fn);
}
else
{
hm = mem_wrap(p, size, 0, 0, 0, 0, 0);
hm = mem_wrap(p, size, 0, 0, 0, 0, 0, vfs_load);
if(flags & FILE_CACHE)
vf->hm = hm;
@ -694,6 +704,13 @@ static int VIo_validate(const VIo* vio)
return x_io_validate(&vio->xio);
}
static int VIo_to_string(const VIo* vio, char* buf)
{
snprintf(buf, H_STRING_LEN, "buf=%p size=%d", vio->buf, vio->size);
return 0;
}
// begin transferring <size> bytes, starting at <ofs>. get result
// with vfs_io_wait; when no longer needed, free via vfs_io_discard.
@ -793,4 +810,5 @@ void vfs_shutdown()
{
file_listing_shutdown();
mount_shutdown();
tree_shutdown();
}

View File

@ -99,9 +99,10 @@ enum TNodeType
const size_t BUCKET_SIZE = 8*KiB;
static u8* bucket_pos;
static uint num_buckets;
TNode* node_alloc(size_t size)
static TNode* node_alloc(size_t size)
{
// would overflow a bucket
if(size > BUCKET_SIZE-sizeof(u8*))
@ -113,17 +114,22 @@ TNode* node_alloc(size_t size)
size = round_up(size, 8);
// ensure alignment, since size includes a string
const uintptr_t addr = (uintptr_t)bucket_pos;
const size_t bytes_used = addr % BUCKET_SIZE;
size_t bytes_used = addr % BUCKET_SIZE;
// a node fit exactly at the end of a bucket
if(!bytes_used)
bytes_used = BUCKET_SIZE;
// addr = 0 on first call (no bucket yet allocated)
// bytes_used == 0 if a node fit exactly into a bucket
if(addr == 0 || bytes_used == 0 || bytes_used+size > BUCKET_SIZE)
if(addr == 0 || bytes_used+size > BUCKET_SIZE)
{
u8* const prev_bucket = (u8*)addr - bytes_used;
u8* prev_bucket = (u8*)addr - bytes_used;
if(addr == 0)
prev_bucket = 0;
u8* bucket = (u8*)mem_alloc(BUCKET_SIZE, BUCKET_SIZE);
if(!bucket)
return 0;
*(u8**)bucket = prev_bucket;
bucket_pos = bucket+round_up(sizeof(u8*), 8);
num_buckets++;
}
TNode* node = (TNode*)bucket_pos;
@ -132,7 +138,7 @@ TNode* node_alloc(size_t size)
}
void node_free_all()
static void node_free_all()
{
const uintptr_t addr = (uintptr_t)bucket_pos;
u8* bucket = bucket_pos - (addr % BUCKET_SIZE);
@ -143,7 +149,10 @@ void node_free_all()
u8* prev_bucket = *(u8**)bucket;
mem_free(bucket);
bucket = prev_bucket;
num_buckets--;
}
debug_assert(num_buckets == 0);
}
@ -678,6 +687,12 @@ void tree_init()
}
void tree_shutdown()
{
node_free_all();
}
// write a representation of the VFS tree to stdout.
void vfs_display()
{

View File

@ -26,6 +26,7 @@ struct TFile;
struct TDir;
extern void tree_init();
extern void tree_shutdown();
// empties the tree and frees all resources. this is used when
// rebuilding VFS and on exit.

View File

@ -593,6 +593,12 @@ static int lookup_enum_files(LookupInfo* li, FileCB cb, uintptr_t user)
}
static uint lookup_get_num_files(const LookupInfo* li)
{
return li->num_files;
}
///////////////////////////////////////////////////////////////////////////////
//
// ZArchive_*: Handle-based container for archive info
@ -669,6 +675,13 @@ static int ZArchive_validate(const ZArchive* za)
return 0;
}
static int ZArchive_to_string(const ZArchive* za, char* buf)
{
snprintf(buf, H_STRING_LEN, "(%d files)", lookup_get_num_files(&za->li));
return 0;
}
// open and return a handle to the zip archive indicated by <fn>.
// somewhat slow - each file is added to an internal index.

View File

@ -145,6 +145,13 @@ static int Cursor_validate(const Cursor* c)
return 0;
}
static int Cursor_to_string(const Cursor* c, char* buf)
{
const char* type = c->sys_cursor? "sys" : "gl";
snprintf(buf, H_STRING_LEN, "(%s)", type);
return 0;
}
// note: these standard resource interface functions are not exposed to the
// caller. all we need here is storage for the sys_cursor / GLCursor and

View File

@ -168,6 +168,12 @@ static int Ogl_Shader_validate(const Ogl_Shader* UNUSED(shdr))
return 0;
}
static int Ogl_Shader_to_string(const Ogl_Shader* UNUSED(shdr), char* buf)
{
snprintf(buf, H_STRING_LEN, "");
return 0;
}
//----------------------------------------------------------------------------
// Public API
@ -385,6 +391,11 @@ static int Ogl_Program_validate(const Ogl_Program* UNUSED(p))
return 0;
}
static int Ogl_Program_to_string(const Ogl_Program* UNUSED(p), char* buf)
{
snprintf(buf, H_STRING_LEN, "");
return 0;
}
//----------------------------------------------------------------------------

View File

@ -460,6 +460,12 @@ static int OglTex_validate(const OglTex* ot)
return 0;
}
static int OglTex_to_string(const OglTex* ot, char* buf)
{
snprintf(buf, H_STRING_LEN, "id=%d", ot->id);
return 0;
}
// load and return a handle to the texture given in <fn>.
// for a list of supported formats, see tex.h's tex_load.
@ -470,6 +476,14 @@ Handle ogl_tex_load(const char* fn, uint flags)
}
// return Handle to an existing object, if it has been loaded and
// is still in memory; otherwise, a negative error code.
Handle ogl_tex_find(const char* fn)
{
return h_find(H_OglTex, (uintptr_t)fn);
}
// make the given Tex object ready for use as an OpenGL texture
// and return a handle to it. this will be as if its contents
// had been loaded by ogl_tex_load.

View File

@ -194,6 +194,10 @@ extern void ogl_tex_set_defaults(uint q_flags, GLint filter);
// for a list of supported formats, see tex.h's tex_load.
extern Handle ogl_tex_load(const char* fn, uint flags = 0);
// return Handle to an existing object, if it has been loaded and
// is still in memory; otherwise, a negative error code.
extern Handle ogl_tex_find(const char* fn);
// make the given Tex object ready for use as an OpenGL texture
// and return a handle to it. this will be as if its contents
// had been loaded by ogl_tex_load.

View File

@ -169,6 +169,8 @@ static void create_level(uint level, uint level_w, uint level_h,
}
TIMER_ADD_CLIENT(tc_plain_transform);
// handles BGR and row flipping in "plain" format (see below).
//
// called by codecs after they get their format-specific transforms out of
@ -178,6 +180,8 @@ static void create_level(uint level, uint level_w, uint level_h,
// somewhat optimized (loops are hoisted, cache associativity accounted for)
static int plain_transform(Tex* t, uint transforms)
{
TIMER_ACCRUE(tc_plain_transform);
// (this is also called directly instead of through ogl_tex, so
// we need to validate)
CHECK_ERR(tex_validate(t));
@ -472,7 +476,7 @@ int tex_wrap(uint w, uint h, uint bpp, uint flags, void* img, Tex* t)
// note: we can't use tex_img_size because that requires all
// Tex fields to be valid, but this calculation must be done first.
const size_t img_size = w*h*bpp/8;
t->hm = mem_wrap(img, img_size, 0, 0, 0, 0, 0);
t->hm = mem_wrap(img, img_size, 0, 0, 0, 0, 0, tex_wrap);
RETURN_ERR(t->hm);
// the exact value of img is lost, since the handle references the

View File

@ -12,6 +12,7 @@
#include "lib/byte_order.h"
#include "lib/res/res.h"
#include "tex_codec.h"
#include "lib/timer.h"
#if MSC_VERSION
@ -191,9 +192,13 @@ static size_t png_hdr_size(const u8* UNUSED(file))
}
TIMER_ADD_CLIENT(tc_png_decode);
// limitation: palette images aren't supported
static int png_decode(DynArray* restrict da, Tex* restrict t)
{
TIMER_ACCRUE(tc_png_decode);
int err = -1;
// freed when ret is reached:
png_structp png_ptr = 0;

View File

@ -188,6 +188,12 @@ static int UniFont_validate(const UniFont* f)
return 0;
}
static int UniFont_to_string(const UniFont* UNUSED(f), char* buf)
{
snprintf(buf, H_STRING_LEN, "");
return 0;
}
Handle unifont_load(const char* fn, int scope)
{

View File

@ -714,7 +714,11 @@ static int h_free_idx(i32 idx, HDATA* hd)
const char* slash = strrchr(hd->fn, '/');
fn = slash? slash+1 : hd->fn;
}
debug_printf("H_FREE %s %s accesses=%d\n", hd->type->name, fn, hd->num_derefs);
char buf[H_STRING_LEN];
if(vtbl->to_string(hd->user, buf) < 0)
strcpy(buf, "(error)"); // safe
debug_printf("H_FREE %s %s accesses=%d %s\n", hd->type->name, fn, hd->num_derefs, buf);
fn_free(hd);

View File

@ -312,6 +312,7 @@ struct H_VTbl
int(*reload)(void* user, const char* fn, Handle);
void(*dtor)(void* user);
int(*validate)(const void* user);
int(*to_string)(const void* user, char* buf);
size_t user_size;
const char* name;
};
@ -324,12 +325,14 @@ typedef H_VTbl* H_Type;
static int type##_reload(type*, const char*, Handle);\
static void type##_dtor(type*);\
static int type##_validate(const type*);\
static int type##_to_string(const type*, char* buf);\
static H_VTbl V_##type =\
{\
(void(*)(void*, va_list))type##_init,\
(int(*)(void*, const char*, Handle))type##_reload,\
(void(*)(void*))type##_dtor,\
(int(*)(const void*))type##_validate,\
(int(*)(const void*, char*))type##_to_string,\
sizeof(type), /* control block size */\
#type /* name */\
};\
@ -394,6 +397,8 @@ enum
RES_DISALLOW_RELOAD = 0x20
};
const size_t H_STRING_LEN = 256;
// allocate a new handle.

View File

@ -24,6 +24,8 @@ struct Mem
uintptr_t ctx;
MEM_DTOR dtor; // this allows user-specified dtors.
void* owner;
};
H_TYPE_DEFINE(Mem);
@ -144,6 +146,7 @@ static void Mem_init(Mem* m, va_list args)
m->raw_size = va_arg(args, size_t);
m->dtor = va_arg(args, MEM_DTOR);
m->ctx = va_arg(args, uintptr_t);
m->owner = va_arg(args, void*);
}
static void Mem_dtor(Mem* m)
@ -177,6 +180,21 @@ static int Mem_validate(const Mem* m)
return 0;
}
static int Mem_to_string(const Mem* m, char* buf)
{
char owner_sym[DBG_SYMBOL_LEN];
if(debug_resolve_symbol(m->owner, owner_sym, 0, 0) < 0)
{
if(m->owner)
snprintf(owner_sym, ARRAY_SIZE(owner_sym), "(%p)", m->owner);
else
strcpy_s(owner_sym, ARRAY_SIZE(owner_sym), "(?)");
}
snprintf(buf, H_STRING_LEN, "p=%p size=%d owner=%s", m->p, m->size, owner_sym);
return 0;
}
//////////////////////////////////////////////////////////////////////////////
@ -203,54 +221,6 @@ static void* heap_alloc(size_t raw_size, uintptr_t& ctx)
}
//////////////////////////////////////////////////////////////////////////////
/*
static u8* pool;
static size_t pool_pos;
static const size_t POOL_CAP = 8*MiB; // TODO: user editable
static void pool_free(void* UNUSED(raw_p), size_t raw_size, uintptr_t ctx)
{
size_t ofs = (size_t)ctx;
// at or beyond current next-alloc position: invalid
if(ofs >= pool_pos)
debug_warn("pool_free: invalid ctx, beyond end of pool");
// at end of pool; 'free' it by moving pos back
else if(ofs + raw_size == pool_pos)
pool_pos = ofs;
else
; // TODO: warn about lost memory in pool;
// suggest using a different allocator
}
static void* pool_alloc(const size_t raw_size, uintptr_t& ctx)
{
ctx = ~0U; // make sure it's invalid if we fail
if(!pool)
{
pool = (u8*)mem_alloc(POOL_CAP, 64*KiB, RES_STATIC);
if(!pool)
return 0;
}
if(pool_pos + raw_size > POOL_CAP)
{
debug_warn("pool_alloc: not enough memory in pool");
return 0;
}
ctx = (uintptr_t)pool_pos;
pool_pos += raw_size;
void* raw_p = (u8*)pool + ctx;
return raw_p;
}
*/
//////////////////////////////////////////////////////////////////////////////
@ -279,7 +249,7 @@ int mem_free_p(void*& p)
// create a H_MEM handle of type MEM_USER,
// and assign it the specified memory range.
// if dtor is non-NULL, it is called (passing ctx) when the handle is freed.
Handle mem_wrap(void* p, size_t size, uint flags, void* raw_p, size_t raw_size, MEM_DTOR dtor, uintptr_t ctx)
Handle mem_wrap(void* p, size_t size, uint flags, void* raw_p, size_t raw_size, MEM_DTOR dtor, uintptr_t ctx, void* owner)
{
if(!p || !size)
CHECK_ERR(ERR_INVALID_PARAM);
@ -298,7 +268,7 @@ Handle mem_wrap(void* p, size_t size, uint flags, void* raw_p, size_t raw_size,
raw_size = size;
hm = h_alloc(H_Mem, (const char*)p, flags|RES_KEY|RES_NO_CACHE,
p, size, raw_p, raw_size, dtor, ctx);
p, size, raw_p, raw_size, dtor, ctx, owner);
return hm;
}
@ -330,6 +300,12 @@ void* mem_alloc(size_t size, const size_t align, uint flags, Handle* phm)
if(phm)
*phm = ERR_NO_MEM;
#ifdef NDEBUG
void* owner = 0;
#else
void* owner = debug_get_nth_caller(1, 0);
#endif
// note: this is legitimate. vfs_load on 0-length files must return
// a valid and unique pointer to an (at least) 0-length buffer.
if(size == 0)
@ -357,7 +333,7 @@ void* mem_alloc(size_t size, const size_t align, uint flags, Handle* phm)
void* p = (void*)round_up((uintptr_t)raw_p, align);
Handle hm = mem_wrap(p, size, flags, raw_p, raw_size, dtor, ctx);
Handle hm = mem_wrap(p, size, flags, raw_p, raw_size, dtor, ctx, owner);
if(!hm) // failed to allocate a handle
{
debug_warn("mem_wrap failed");

View File

@ -31,7 +31,7 @@ extern void* mem_get_ptr(Handle h, size_t* size = 0);
extern int mem_get(Handle hm, u8** pp, size_t* psize);
extern Handle mem_wrap(void* p, size_t size, uint flags, void* raw_p, size_t raw_size, MEM_DTOR dtor, uintptr_t ctx);
extern Handle mem_wrap(void* p, size_t size, uint flags, void* raw_p, size_t raw_size, MEM_DTOR dtor, uintptr_t ctx, void* owner);
// exception to normal resource shutdown: must not be called before
// h_mgr_shutdown! (this is because h_mgr calls us to free memory, which

View File

@ -1016,6 +1016,13 @@ static int SndData_validate(const SndData* sd)
return 0;
}
static int SndData_to_string(const SndData* sd, char* buf)
{
const char* type = sd->is_stream? "stream" : "clip";
snprintf(buf, H_STRING_LEN, "%s; al_buf=%d", type, sd->al_buf);
return 0;
}
// open and return a handle to a sound file's data.
static Handle snd_data_load(const char* fn, bool is_stream)
@ -1315,6 +1322,12 @@ static int VSrc_validate(const VSrc* vs)
return 0;
}
static int VSrc_to_string(const VSrc* vs, char* buf)
{
snprintf(buf, H_STRING_LEN, "al_src = %d", vs->al_src);
return 0;
}
// open and return a handle to a sound instance.
//

View File

@ -768,18 +768,20 @@ void Shutdown()
snd_shutdown();
vfs_shutdown();
// must come before h_mgr_shutdown - it frees IO buffers,
// which we don't want showing up as leaks.
file_shutdown();
// this forcibly frees all open handles (thus preventing real leaks),
// and makes further access to h_mgr impossible.
h_mgr_shutdown();
// must come after h_mgr_shutdown - it causes memory to be freed,
// which requires this module to still be active.
// must come after h_mgr_shutdown - it causes memory
// to be freed, which requires this module to still be active.
mem_shutdown();
TIMER_END("resource modules");
TIMER_BEGIN("shutdown misc");
file_shutdown();
timer_display_client_totals();
// should be last, since the above use them

View File

@ -40,10 +40,10 @@
#include "ModelDef.h"
#include "ogl.h"
#include "lib/res/res.h"
#include "lib/res/file/file.h"
#include "lib/res/graphics/tex.h"
#include "lib/res/graphics/ogl_tex.h"
#include "lib/res/file/vfs.h"
#include "ps/Loader.h"
#include "renderer/RenderPathVertexShader.h"
@ -1341,6 +1341,16 @@ inline void CopyTriple(unsigned char* dst,const unsigned char* src)
// calculate the coordinate of each alphamap within this packed texture
int CRenderer::LoadAlphaMaps()
{
const char* const key = "(alpha map composite)";
Handle ht = ogl_tex_find(key);
// alpha map texture had already been created and is still in memory:
// reuse it, do not load again.
if(ht > 0)
{
m_hCompositeAlphaMap = ht;
return 0;
}
//
// load all textures and store Handle in array
//
@ -1370,7 +1380,9 @@ int CRenderer::LoadAlphaMaps()
for(uint i=0;i<NumAlphaMaps;i++)
{
(void)pp_append_file(&pp, fnames[i]);
textures[i] = ogl_tex_load(pp.path);
// note: these individual textures can be discarded afterwards;
// we cache the composite.
textures[i] = ogl_tex_load(pp.path, RES_NO_CACHE);
RETURN_ERR(textures[i]);
// get its size and make sure they are all equal.
@ -1443,7 +1455,7 @@ int CRenderer::LoadAlphaMaps()
// upload the composite texture
Tex t;
(void)tex_wrap(total_w, total_h, 24, 0, data, &t);
m_hCompositeAlphaMap = ogl_tex_wrap(&t, "(alpha map composite)");
m_hCompositeAlphaMap = ogl_tex_wrap(&t, key);
(void)ogl_tex_set_filter(m_hCompositeAlphaMap, GL_LINEAR);
(void)ogl_tex_set_wrap (m_hCompositeAlphaMap, GL_CLAMP_TO_EDGE);
int ret = ogl_tex_upload(m_hCompositeAlphaMap, 0, 0, GL_INTENSITY);