forked from 0ad/0ad
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:
parent
32cfc6d807
commit
d43aa11d36
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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");
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
//
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user