forked from 0ad/0ad
ogl_tex: centralize OpenGL caps detection; allow user override for broken card/driver combos
- enable floating point exceptions (helps catch bugs) file.cpp: improved error checking in dir_open/dir_next_ent code vfs.h: expand docs; add note on patching + SCM main: use spf (1/fps) to derive TimeSinceLastFrame (makes for smoother updates) timer: rename SUM_TIMER to TIMER_ACCRUE. interface change allows unlimited number of timers (instead of static limit as before). documented everything This was SVN commit r2958.
This commit is contained in:
parent
f2b662d12d
commit
b350f54162
@ -84,7 +84,7 @@ TIMER_ADD_CLIENT(tc_mipmap_basecolor);
|
|||||||
// in m_BaseColor member
|
// in m_BaseColor member
|
||||||
void CTextureEntry::BuildBaseColor()
|
void CTextureEntry::BuildBaseColor()
|
||||||
{
|
{
|
||||||
SUM_TIMER(tc_mipmap_basecolor);
|
TIMER_ACCRUE(tc_mipmap_basecolor);
|
||||||
|
|
||||||
if (m_pProperties && m_pProperties->HasBaseColor())
|
if (m_pProperties && m_pProperties->HasBaseColor())
|
||||||
{
|
{
|
||||||
|
@ -157,7 +157,10 @@ void cpu_init()
|
|||||||
// we need full precision when calculating the time.
|
// we need full precision when calculating the time.
|
||||||
// if there's a spot where we want to speed up divides|sqrts,
|
// if there's a spot where we want to speed up divides|sqrts,
|
||||||
// we can temporarily change precision there.
|
// we can temporarily change precision there.
|
||||||
// _control87(_PC_24, _MCW_PC);
|
//_control87(_PC_24, _MCW_PC);
|
||||||
|
|
||||||
|
// enable all floating-point exceptions (helps catch bugs)
|
||||||
|
_control87(_MCW_EM, _MCW_EM);
|
||||||
|
|
||||||
// detects CPU clock frequency and capabilities, which are prerequisites
|
// detects CPU clock frequency and capabilities, which are prerequisites
|
||||||
// for using the TSC as a timer (desirable due to its high resolution).
|
// for using the TSC as a timer (desirable due to its high resolution).
|
||||||
|
@ -347,19 +347,24 @@ int dir_open(const char* P_path, DirIterator* d_)
|
|||||||
d->os_dir = opendir(n_path);
|
d->os_dir = opendir(n_path);
|
||||||
if(!d->os_dir)
|
if(!d->os_dir)
|
||||||
{
|
{
|
||||||
int err = -1;
|
int err;
|
||||||
switch(errno)
|
switch(errno)
|
||||||
{
|
{
|
||||||
case ENOMEM:
|
case ENOMEM:
|
||||||
err = ERR_NO_MEM;
|
err = ERR_NO_MEM;
|
||||||
|
break;
|
||||||
case ENOENT:
|
case ENOENT:
|
||||||
err = ERR_PATH_NOT_FOUND;
|
err = ERR_PATH_NOT_FOUND;
|
||||||
// default: err already set
|
break;
|
||||||
|
default:
|
||||||
|
err = -1;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
CHECK_ERR(err);
|
CHECK_ERR(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
return pp_set_dir(&d->pp, n_path);
|
RETURN_ERR(pp_set_dir(&d->pp, n_path));
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -371,9 +376,14 @@ int dir_next_ent(DirIterator* d_, DirEnt* ent)
|
|||||||
DirIterator_* d = (DirIterator_*)d_;
|
DirIterator_* d = (DirIterator_*)d_;
|
||||||
|
|
||||||
get_another_entry:
|
get_another_entry:
|
||||||
|
errno = 0;
|
||||||
struct dirent* os_ent = readdir(d->os_dir);
|
struct dirent* os_ent = readdir(d->os_dir);
|
||||||
if(!os_ent)
|
if(!os_ent)
|
||||||
|
{
|
||||||
|
if(errno)
|
||||||
|
debug_warn("readdir failed");
|
||||||
return ERR_DIR_END;
|
return ERR_DIR_END;
|
||||||
|
}
|
||||||
|
|
||||||
// copy os_ent.name[]; we need it for stat() #if !OS_WIN and
|
// copy os_ent.name[]; we need it for stat() #if !OS_WIN and
|
||||||
// return it as ent.name (since os_ent.name[] is volatile).
|
// return it as ent.name (since os_ent.name[] is volatile).
|
||||||
|
@ -105,19 +105,24 @@ modder (since all files would first have to be copied somewhere).
|
|||||||
Allowing overriding individual files is much safer (since game data is
|
Allowing overriding individual files is much safer (since game data is
|
||||||
never touched) and easier (more fine-grained control for modders).
|
never touched) and easier (more fine-grained control for modders).
|
||||||
|
|
||||||
Alternatives to the patch archive approach would be to completely replace
|
|
||||||
the game data archive (infeasible due to size) or apply a binary patch
|
Patching
|
||||||
(complicated and brittle WRT versioning). We are therefore happy to
|
--------
|
||||||
use the already existing mod mechanism.
|
|
||||||
|
As mentioned above, patching is also done via mounting.
|
||||||
|
Alternatives would be to completely replace the game data archive
|
||||||
|
(infeasible due to size) or apply a binary patch (complicated and
|
||||||
|
brittle WRT versioning). We are therefore happy to use the
|
||||||
|
already existing mod mechanism.
|
||||||
|
|
||||||
Note however that multiple patches do impact performance (despite
|
Note however that multiple patches do impact performance (despite
|
||||||
constant-time VFS path -> file location lookup) simply due to locality;
|
constant-time VFS path -> file location lookup) simply due to locality;
|
||||||
files are no longer arranged in order of access. Fortunately there is an
|
files are no longer arranged in order of access. Fortunately there is an
|
||||||
easy way to avoid this: simply run the archive builder script; all
|
easy way to avoid this: simply run the archive builder script; all
|
||||||
patched files will be merged into the archive.
|
patched files will be merged into the archive. However, be warned that
|
||||||
|
reverting to previous versions (e.g. to watch old replays) would no longer
|
||||||
|
be possible! This is because their changes have been 'baked into' the
|
||||||
For more information, see 'Mount Details' below.
|
main archive, whereas previously the patch could simply be deleted.
|
||||||
|
|
||||||
|
|
||||||
Mount Details
|
Mount Details
|
||||||
@ -176,6 +181,8 @@ this is just an optimization.
|
|||||||
|
|
||||||
To ease development, files may additionally be stored in normal directories.
|
To ease development, files may additionally be stored in normal directories.
|
||||||
The VFS transparently provides access to the correct (newest) version.
|
The VFS transparently provides access to the correct (newest) version.
|
||||||
|
This is to allow keeping data files in SCM - developers can get the latest
|
||||||
|
version without always having to update archives afterwards.
|
||||||
|
|
||||||
One additional advantage of archives over loose files is that I/O throughput
|
One additional advantage of archives over loose files is that I/O throughput
|
||||||
is increased - since files are compressed, there is less to read from disk.
|
is increased - since files are compressed, there is less to read from disk.
|
||||||
|
@ -674,7 +674,7 @@ static int ZArchive_validate(const ZArchive* za)
|
|||||||
// somewhat slow - each file is added to an internal index.
|
// somewhat slow - each file is added to an internal index.
|
||||||
Handle zip_archive_open(const char* fn)
|
Handle zip_archive_open(const char* fn)
|
||||||
{
|
{
|
||||||
TIMER(zip_archive_open);
|
TIMER("zip_archive_open");
|
||||||
return h_alloc(H_ZArchive, fn);
|
return h_alloc(H_ZArchive, fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -816,12 +816,12 @@ double t0 = get_time();
|
|||||||
|
|
||||||
if(ctx->compressed)
|
if(ctx->compressed)
|
||||||
{
|
{
|
||||||
SUM_TIMER(tc_zip_inflate);
|
TIMER_ACCRUE(tc_zip_inflate);
|
||||||
err = inflate(zs, Z_SYNC_FLUSH);
|
err = inflate(zs, Z_SYNC_FLUSH);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SUM_TIMER(tc_zip_memcpy);
|
TIMER_ACCRUE(tc_zip_memcpy);
|
||||||
memcpy2(zs->next_out, zs->next_in, zs->avail_in);
|
memcpy2(zs->next_out, zs->next_in, zs->avail_in);
|
||||||
uInt size = MIN(zs->avail_in, zs->avail_out);
|
uInt size = MIN(zs->avail_in, zs->avail_out);
|
||||||
zs->avail_out -= size;
|
zs->avail_out -= size;
|
||||||
|
@ -506,6 +506,7 @@ int ogl_tex_free(Handle& ht)
|
|||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
// state setters (see "Texture Parameters" in docs)
|
// state setters (see "Texture Parameters" in docs)
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
|
||||||
// we require the below functions be called before uploading; this avoids
|
// we require the below functions be called before uploading; this avoids
|
||||||
// potentially redundant glTexParameter calls (we'd otherwise need to always
|
// potentially redundant glTexParameter calls (we'd otherwise need to always
|
||||||
@ -587,12 +588,84 @@ int ogl_tex_set_wrap(Handle ht, GLint wrap)
|
|||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
// upload
|
// upload
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// OpenGL has several features that are helpful for uploading but not
|
||||||
|
// available in all implementations. we check for their presence but
|
||||||
|
// provide for user override (in case they don't work on a card/driver
|
||||||
|
// combo we didn't test).
|
||||||
|
|
||||||
|
// tristate; -1 is undecided
|
||||||
|
static int have_auto_mipmap_gen = -1;
|
||||||
|
static int have_s3tc = -1;
|
||||||
|
|
||||||
|
// override the default decision and force/disallow use of the
|
||||||
|
// given feature. should be called from ah_override_gl_upload_caps.
|
||||||
|
void ogl_tex_override(OglTexOverrides what, OglTexAllow allow)
|
||||||
|
{
|
||||||
|
debug_assert(allow == OGL_TEX_ENABLE || allow == OGL_TEX_DISABLE);
|
||||||
|
const bool enable = (allow == OGL_TEX_ENABLE);
|
||||||
|
|
||||||
|
switch(what)
|
||||||
|
{
|
||||||
|
case OGL_TEX_S3TC:
|
||||||
|
have_s3tc = enable;
|
||||||
|
break;
|
||||||
|
case OGL_TEX_AUTO_MIPMAP_GEN:
|
||||||
|
have_auto_mipmap_gen = enable;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
debug_warn("ogl_tex_override: invalid <what>");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// detect caps (via OpenGL extension list) and give an app_hook the chance to
|
||||||
|
// override this (e.g. via list of card/driver combos on which S3TC breaks).
|
||||||
|
// called once from the first ogl_tex_upload.
|
||||||
|
static void detect_gl_upload_caps()
|
||||||
|
{
|
||||||
|
// detect features, but only change the variables if they were at
|
||||||
|
// "undecided" (if overrides were set before this, they must remain).
|
||||||
|
if(have_auto_mipmap_gen == -1)
|
||||||
|
{
|
||||||
|
have_auto_mipmap_gen = oglHaveExtension("GL_SGIS_generate_mipmap");
|
||||||
|
}
|
||||||
|
if(have_s3tc == -1)
|
||||||
|
{
|
||||||
|
// note: we don't bother checking for GL_S3_s3tc - it is incompatible
|
||||||
|
// and irrelevant (was never widespread).
|
||||||
|
have_s3tc = oglHaveExtensions(0, "GL_ARB_texture_compression", "GL_EXT_texture_compression_s3tc", 0) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//apphook - call back into override if app thinks anything should be disabled
|
||||||
|
// disable features if we're on a card/driver combo on which they
|
||||||
|
// are known to break.
|
||||||
|
if(gfx_card[0] == '\0')
|
||||||
|
debug_warn("ogl_tex requires get_gfx_info be called before ogl_tex_upload");
|
||||||
|
if(!strcmp(gfx_card, "S3 SuperSavage/IXC 1014"))
|
||||||
|
{
|
||||||
|
if(strstr(gfx_drv_ver, "ssicdnt.dll (2.60.115)"))
|
||||||
|
have_s3tc = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// warn if more-or-less essential features are missing
|
||||||
|
if(!have_s3tc)
|
||||||
|
DISPLAY_ERROR(L"Performance warning: your graphics card does not support compressed textures. The game will try to continue anyway, but may be slower than expected. Please try updating your graphics drivers; if that doesn't help, please try upgrading your hardware.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// take care of mipmaps. if they are called for by <filter>, either
|
||||||
|
// arrange for OpenGL to create them, or see to it that the Tex object
|
||||||
|
// contains them (if need be, creating them in software).
|
||||||
|
// sets *plevels_to_skip to influence upload behavior (depending on
|
||||||
|
// whether mipmaps are needed and the quality settings).
|
||||||
|
// returns 0 to indicate success; otherwise, caller must disable
|
||||||
|
// mipmapping by switching filter to e.g. GL_LINEAR.
|
||||||
static int get_mipmaps(Tex* t, GLint filter, uint q_flags, int* plevels_to_skip)
|
static int get_mipmaps(Tex* t, GLint filter, uint q_flags, int* plevels_to_skip)
|
||||||
{
|
{
|
||||||
// decisions:
|
// decisions:
|
||||||
// .. does this OpenGL implementation support auto mipmap generation?
|
|
||||||
static const bool have_auto_mipmap_gen = oglHaveVersion("1.4") || oglHaveExtension("GL_SGIS_generate_mipmap");
|
|
||||||
// .. does filter call for uploading mipmaps?
|
// .. does filter call for uploading mipmaps?
|
||||||
const bool need_mipmaps = filter_uses_mipmaps(filter);
|
const bool need_mipmaps = filter_uses_mipmaps(filter);
|
||||||
// .. does the image data include mipmaps? (stored as separate
|
// .. does the image data include mipmaps? (stored as separate
|
||||||
@ -652,6 +725,8 @@ static int get_mipmaps(Tex* t, GLint filter, uint q_flags, int* plevels_to_skip)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// tex_util_foreach_mipmap callbacks: upload the given level to OpenGL.
|
||||||
|
|
||||||
struct UploadParams
|
struct UploadParams
|
||||||
{
|
{
|
||||||
GLenum fmt;
|
GLenum fmt;
|
||||||
@ -693,26 +768,6 @@ static void upload_impl(Tex* t, GLenum fmt, GLint int_fmt, int levels_to_skip)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool detect_s3tc()
|
|
||||||
{
|
|
||||||
// 1. require extensions to be advertised.
|
|
||||||
// note: we don't bother checking for GL_S3_s3tc - it is incompatible
|
|
||||||
// and irrelevant (was never widespread).
|
|
||||||
bool have_s3tc = oglHaveExtensions(0, "GL_ARB_texture_compression", "GL_EXT_texture_compression_s3tc", 0) == 0;
|
|
||||||
|
|
||||||
// 2. exclude any card/driver combos on which it is known to break.
|
|
||||||
if(gfx_card[0] == '\0')
|
|
||||||
debug_warn("ogl_tex requires get_gfx_info be called before ogl_tex_upload");
|
|
||||||
if(!strcmp(gfx_card, "S3 SuperSavage/IXC 1014"))
|
|
||||||
{
|
|
||||||
if(strstr(gfx_drv_ver, "ssicdnt.dll (2.60.115)"))
|
|
||||||
have_s3tc = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return have_s3tc;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// upload the texture to OpenGL.
|
// upload the texture to OpenGL.
|
||||||
// if not 0, parameters override the following:
|
// if not 0, parameters override the following:
|
||||||
// fmt_ovr : OpenGL format (e.g. GL_RGB) decided from bpp / Tex flags;
|
// fmt_ovr : OpenGL format (e.g. GL_RGB) decided from bpp / Tex flags;
|
||||||
@ -724,6 +779,8 @@ static bool detect_s3tc()
|
|||||||
// - frees the texel data! see ogl_tex_get_data.
|
// - frees the texel data! see ogl_tex_get_data.
|
||||||
int ogl_tex_upload(const Handle ht, GLenum fmt_ovr, uint q_flags_ovr, GLint int_fmt_ovr)
|
int ogl_tex_upload(const Handle ht, GLenum fmt_ovr, uint q_flags_ovr, GLint int_fmt_ovr)
|
||||||
{
|
{
|
||||||
|
ONCE(detect_gl_upload_caps());
|
||||||
|
|
||||||
H_DEREF(ht, OglTex, ot);
|
H_DEREF(ht, OglTex, ot);
|
||||||
Tex* t = &ot->t;
|
Tex* t = &ot->t;
|
||||||
const char* fn = h_filename(ht);
|
const char* fn = h_filename(ht);
|
||||||
@ -738,12 +795,8 @@ int ogl_tex_upload(const Handle ht, GLenum fmt_ovr, uint q_flags_ovr, GLint int_
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// decompress S3TC if that's not supported by OpenGL.
|
// decompress S3TC if that's not supported by OpenGL.
|
||||||
static const bool have_s3tc = detect_s3tc();
|
|
||||||
if((t->flags & TEX_DXT) && !have_s3tc)
|
if((t->flags & TEX_DXT) && !have_s3tc)
|
||||||
{
|
|
||||||
ONCE(DISPLAY_ERROR(L"Performance warning: your graphics card does not support compressed textures. The game will try to continue anyway, but may be slower than expected. Please try updating your graphics drivers; if that doesn't help, please try upgrading your hardware."));
|
|
||||||
(void)tex_transform_to(t, t->flags & ~TEX_DXT);
|
(void)tex_transform_to(t, t->flags & ~TEX_DXT);
|
||||||
}
|
|
||||||
|
|
||||||
// determine fmt and int_fmt, allowing for user override.
|
// determine fmt and int_fmt, allowing for user override.
|
||||||
ot->fmt = choose_fmt(t->bpp, t->flags);
|
ot->fmt = choose_fmt(t->bpp, t->flags);
|
||||||
@ -788,6 +841,7 @@ int ogl_tex_upload(const Handle ht, GLenum fmt_ovr, uint q_flags_ovr, GLint int_
|
|||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
// getters
|
// getters
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
|
||||||
// retrieve texture dimensions and bits per pixel.
|
// retrieve texture dimensions and bits per pixel.
|
||||||
// all params are optional and filled if non-NULL.
|
// all params are optional and filled if non-NULL.
|
||||||
@ -842,6 +896,7 @@ int ogl_tex_get_data(Handle ht, void** p)
|
|||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
// misc API
|
// misc API
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
|
||||||
// bind the texture to the specified unit [number] in preparation for
|
// bind the texture to the specified unit [number] in preparation for
|
||||||
// using it in rendering. if <ht> is 0, texturing is disabled instead.
|
// using it in rendering. if <ht> is 0, texturing is disabled instead.
|
||||||
|
@ -237,6 +237,20 @@ extern int ogl_tex_set_wrap(Handle ht, GLint wrap);
|
|||||||
// upload
|
// upload
|
||||||
//
|
//
|
||||||
|
|
||||||
|
enum OglTexOverrides
|
||||||
|
{
|
||||||
|
OGL_TEX_S3TC,
|
||||||
|
OGL_TEX_AUTO_MIPMAP_GEN
|
||||||
|
};
|
||||||
|
|
||||||
|
enum OglTexAllow
|
||||||
|
{
|
||||||
|
OGL_TEX_DISABLE,
|
||||||
|
OGL_TEX_ENABLE
|
||||||
|
};
|
||||||
|
|
||||||
|
extern void ogl_tex_override(OglTexOverrides what, OglTexAllow allow);
|
||||||
|
|
||||||
// upload the texture to OpenGL.
|
// upload the texture to OpenGL.
|
||||||
// if not 0, parameters override the following:
|
// if not 0, parameters override the following:
|
||||||
// fmt_ovr : OpenGL format (e.g. GL_RGB) decided from bpp / Tex flags;
|
// fmt_ovr : OpenGL format (e.g. GL_RGB) decided from bpp / Tex flags;
|
||||||
|
@ -660,7 +660,7 @@ TIMER_ADD_CLIENT(tc_transform);
|
|||||||
// that are set in transforms.
|
// that are set in transforms.
|
||||||
int tex_transform(Tex* t, uint transforms)
|
int tex_transform(Tex* t, uint transforms)
|
||||||
{
|
{
|
||||||
SUM_TIMER(tc_transform);
|
TIMER_ACCRUE(tc_transform);
|
||||||
|
|
||||||
const uint target_flags = t->flags ^ transforms;
|
const uint target_flags = t->flags ^ transforms;
|
||||||
for(;;)
|
for(;;)
|
||||||
|
@ -40,10 +40,13 @@ extern u64 rdtsc(void);
|
|||||||
|
|
||||||
|
|
||||||
#ifndef _MCW_PC
|
#ifndef _MCW_PC
|
||||||
#define _MCW_PC 0x0300 // Precision Control
|
# define _MCW_PC 0x0300 // Precision Control
|
||||||
#endif
|
#endif
|
||||||
#ifndef _PC_24
|
#ifndef _PC_24
|
||||||
#define _PC_24 0x0000 // 24 bits
|
# define _PC_24 0x0000 // 24 bits
|
||||||
|
#endif
|
||||||
|
#ifndef _MCW_EM
|
||||||
|
# define _MCW_EM 0x003f // Exception Mask
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define _control87 ia32_control87
|
#define _control87 ia32_control87
|
||||||
|
@ -37,7 +37,6 @@ double get_time()
|
|||||||
double t;
|
double t;
|
||||||
|
|
||||||
#if HAVE_CLOCK_GETTIME
|
#if HAVE_CLOCK_GETTIME
|
||||||
|
|
||||||
static struct timespec start = {0};
|
static struct timespec start = {0};
|
||||||
struct timespec ts;
|
struct timespec ts;
|
||||||
|
|
||||||
@ -46,9 +45,7 @@ double get_time()
|
|||||||
|
|
||||||
(void)clock_gettime(CLOCK_REALTIME, &ts);
|
(void)clock_gettime(CLOCK_REALTIME, &ts);
|
||||||
t = (ts.tv_sec - start.tv_sec) + (ts.tv_nsec - start.tv_nsec)*1e-9;
|
t = (ts.tv_sec - start.tv_sec) + (ts.tv_nsec - start.tv_nsec)*1e-9;
|
||||||
|
|
||||||
#elif HAVE_GETTIMEOFDAY
|
#elif HAVE_GETTIMEOFDAY
|
||||||
|
|
||||||
static struct timeval start;
|
static struct timeval start;
|
||||||
struct timeval cur;
|
struct timeval cur;
|
||||||
|
|
||||||
@ -57,11 +54,8 @@ double get_time()
|
|||||||
|
|
||||||
gettimeofday(&cur, 0);
|
gettimeofday(&cur, 0);
|
||||||
t = (cur.tv_sec - start.tv_sec) + (cur.tv_usec - start.tv_usec)*1e-6;
|
t = (cur.tv_sec - start.tv_sec) + (cur.tv_usec - start.tv_usec)*1e-6;
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
#error "get_time: add timer implementation for this platform!"
|
#error "get_time: add timer implementation for this platform!"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// make sure time is monotonic (never goes backwards)
|
// make sure time is monotonic (never goes backwards)
|
||||||
@ -74,6 +68,8 @@ double get_time()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// return resolution (expressed in [s]) of the time source underlying
|
||||||
|
// get_time.
|
||||||
double timer_res()
|
double timer_res()
|
||||||
{
|
{
|
||||||
// may take a while to determine, so cache it
|
// may take a while to determine, so cache it
|
||||||
@ -110,6 +106,7 @@ double timer_res()
|
|||||||
// filter values are tuned for 100 FPS.
|
// filter values are tuned for 100 FPS.
|
||||||
|
|
||||||
int fps;
|
int fps;
|
||||||
|
float spf;
|
||||||
|
|
||||||
void calc_fps()
|
void calc_fps()
|
||||||
{
|
{
|
||||||
@ -225,6 +222,8 @@ void calc_fps()
|
|||||||
old = cur_fps*gain + old*(1.0-gain);
|
old = cur_fps*gain + old*(1.0-gain);
|
||||||
avg_fps = old;
|
avg_fps = old;
|
||||||
|
|
||||||
|
spf = 1.0 / avg_fps;
|
||||||
|
|
||||||
// update fps counter if it differs "enough"
|
// update fps counter if it differs "enough"
|
||||||
// currently, that means off by more than 5 FPS or 5%.
|
// currently, that means off by more than 5 FPS or 5%.
|
||||||
const double difference = fabs(avg_fps-fps);
|
const double difference = fabs(avg_fps-fps);
|
||||||
@ -242,15 +241,20 @@ void calc_fps()
|
|||||||
// this supplements in-game profiling by providing low-overhead,
|
// this supplements in-game profiling by providing low-overhead,
|
||||||
// high resolution time accounting.
|
// high resolution time accounting.
|
||||||
|
|
||||||
|
// intrusive linked-list of all clients. a fixed-size limit would be
|
||||||
// use static storage to ensure clients can be added at any time,
|
// acceptable (since timers are added manually), but the list is easy
|
||||||
// especially before the heap has been initialized.
|
// to implement and only has the drawback of exposing TimerClient to users.
|
||||||
|
//
|
||||||
|
// do not use std::list et al. for this! we must be callable at any time,
|
||||||
|
// especially before NLSO ctors run or before heap init.
|
||||||
static uint num_clients;
|
static uint num_clients;
|
||||||
static TimerClient* clients;
|
static TimerClient* clients;
|
||||||
|
|
||||||
|
|
||||||
// allocate a new TimerClient whose total (added to by timer_bill_client)
|
// make the given TimerClient (usually instantiated as static data)
|
||||||
// will be displayed by timer_display_client_totals.
|
// ready for use. returns its address for TIMER_ADD_CLIENT's convenience.
|
||||||
|
// this client's total (added to by timer_bill_client) will be
|
||||||
|
// displayed by timer_display_client_totals.
|
||||||
// notes:
|
// notes:
|
||||||
// - may be called at any time;
|
// - may be called at any time;
|
||||||
// - always succeeds (there's no fixed limit);
|
// - always succeeds (there's no fixed limit);
|
||||||
|
@ -30,13 +30,16 @@ extern "C" {
|
|||||||
// high resolution (> 1 us) timestamp [s], starting at or near 0 s.
|
// high resolution (> 1 us) timestamp [s], starting at or near 0 s.
|
||||||
extern double get_time(void);
|
extern double get_time(void);
|
||||||
|
|
||||||
|
// return resolution (expressed in [s]) of the time source underlying
|
||||||
|
// get_time.
|
||||||
extern double timer_res(void);
|
extern double timer_res(void);
|
||||||
|
|
||||||
// calculate fps (call once per frame)
|
// calculate fps (call once per frame)
|
||||||
// several smooth filters (tuned for ~100 FPS)
|
// several smooth filters (tuned for ~100 FPS)
|
||||||
// => less fluctuation, but rapid tracking
|
// => less fluctuation, but rapid tracking
|
||||||
|
|
||||||
extern int fps;
|
extern int fps; // for user display
|
||||||
|
extern float spf; // for time-since-last-frame use
|
||||||
|
|
||||||
extern void calc_fps(void);
|
extern void calc_fps(void);
|
||||||
|
|
||||||
@ -45,7 +48,10 @@ extern void calc_fps(void);
|
|||||||
// cumulative timer API
|
// cumulative timer API
|
||||||
//
|
//
|
||||||
|
|
||||||
// this is to be considered opaque - do not access its fields!
|
// this supplements in-game profiling by providing low-overhead,
|
||||||
|
// high resolution time accounting.
|
||||||
|
|
||||||
|
// opaque - do not access its fields!
|
||||||
// note: must be defined here because clients instantiate them;
|
// note: must be defined here because clients instantiate them;
|
||||||
// fields cannot be made private due to C compatibility requirement.
|
// fields cannot be made private due to C compatibility requirement.
|
||||||
struct TimerClient
|
struct TimerClient
|
||||||
@ -56,13 +62,13 @@ struct TimerClient
|
|||||||
const char* description;
|
const char* description;
|
||||||
|
|
||||||
TimerClient* next;
|
TimerClient* next;
|
||||||
|
|
||||||
int dummy;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// allocate a new TimerClient whose total (added to by timer_bill_client)
|
// make the given TimerClient (usually instantiated as static data)
|
||||||
// will be displayed by timer_display_client_totals.
|
// ready for use. returns its address for TIMER_ADD_CLIENT's convenience.
|
||||||
|
// this client's total (added to by timer_bill_client) will be
|
||||||
|
// displayed by timer_display_client_totals.
|
||||||
// notes:
|
// notes:
|
||||||
// - may be called at any time;
|
// - may be called at any time;
|
||||||
// - always succeeds (there's no fixed limit);
|
// - always succeeds (there's no fixed limit);
|
||||||
@ -70,10 +76,6 @@ struct TimerClient
|
|||||||
// - description must remain valid until exit; a string literal is safest.
|
// - description must remain valid until exit; a string literal is safest.
|
||||||
extern TimerClient* timer_add_client(TimerClient* tc, const char* description);
|
extern TimerClient* timer_add_client(TimerClient* tc, const char* description);
|
||||||
|
|
||||||
#define TIMER_ADD_CLIENT(id)\
|
|
||||||
static TimerClient UID__;\
|
|
||||||
TimerClient* id = timer_add_client(&UID__, #id);
|
|
||||||
|
|
||||||
// add <dt> [s] to the client's total.
|
// add <dt> [s] to the client's total.
|
||||||
extern void timer_bill_client(TimerClient* tc, double dt);
|
extern void timer_bill_client(TimerClient* tc, double dt);
|
||||||
|
|
||||||
@ -87,18 +89,21 @@ extern void timer_display_client_totals();
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ScopedTimer
|
|
||||||
|
// used via TIMER* macros below.
|
||||||
|
class ScopeTimer
|
||||||
{
|
{
|
||||||
double t0;
|
double t0;
|
||||||
const char* name;
|
const char* description;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ScopedTimer(const char* _name)
|
ScopeTimer(const char* _description)
|
||||||
{
|
{
|
||||||
t0 = get_time();
|
t0 = get_time();
|
||||||
name = _name;
|
description = _description;
|
||||||
}
|
}
|
||||||
~ScopedTimer()
|
|
||||||
|
~ScopeTimer()
|
||||||
{
|
{
|
||||||
double t1 = get_time();
|
double t1 = get_time();
|
||||||
double dt = t1-t0;
|
double dt = t1-t0;
|
||||||
@ -111,50 +116,70 @@ public:
|
|||||||
else if(dt > 1e-3)
|
else if(dt > 1e-3)
|
||||||
scale = 1e3, unit = "ms";
|
scale = 1e3, unit = "ms";
|
||||||
|
|
||||||
debug_printf("TIMER %s: %g %s\n", name, dt*scale, unit);
|
debug_printf("TIMER %s: %g %s\n", description, dt*scale, unit);
|
||||||
}
|
}
|
||||||
|
|
||||||
// disallow copying (makes no sense)
|
// disallow copying (makes no sense)
|
||||||
private:
|
private:
|
||||||
ScopedTimer& operator=(const ScopedTimer&);
|
ScopeTimer& operator=(const ScopeTimer&);
|
||||||
};
|
};
|
||||||
|
|
||||||
#define TIMER(name) ScopedTimer ST##name(#name)
|
|
||||||
// Cheat a bit to make things slightly easier on the user
|
|
||||||
#define TIMER_START(name) { ScopedTimer __timer( name )
|
|
||||||
#define TIMER_END(name) }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Measure the time taken to execute code up until end of the current scope;
|
||||||
|
display it via debug_printf. Can safely be nested.
|
||||||
|
Useful for measuring time spent in a function or basic block.
|
||||||
|
<description> must remain valid over the lifetime of this object;
|
||||||
|
a string literal is safest.
|
||||||
|
|
||||||
Example usage:
|
Example usage:
|
||||||
|
|
||||||
static TimerClient* client = timer_add_client("description");
|
|
||||||
|
|
||||||
void func()
|
void func()
|
||||||
{
|
{
|
||||||
SUM_TIMER(client);
|
TIMER("description");
|
||||||
(code to be measured)
|
// code to be measured
|
||||||
}
|
}
|
||||||
|
|
||||||
[at exit]
|
|
||||||
timer_display_client_totals();
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
#define TIMER(description) ScopeTimer UID__(description)
|
||||||
|
|
||||||
class ScopedSumTimer
|
/*
|
||||||
|
Measure the time taken to execute code between BEGIN and END markers;
|
||||||
|
display it via debug_printf. Can safely be nested.
|
||||||
|
Useful for measuring several pieces of code within the same function/block.
|
||||||
|
<description> must remain valid over the lifetime of this object;
|
||||||
|
a string literal is safest.
|
||||||
|
|
||||||
|
Caveats:
|
||||||
|
- this wraps the code to be measured in a basic block, so any
|
||||||
|
variables defined there are invisible to surrounding code.
|
||||||
|
- the description passed to END isn't inspected; you are responsible for
|
||||||
|
ensuring correct nesting!
|
||||||
|
|
||||||
|
Example usage:
|
||||||
|
void func2()
|
||||||
|
{
|
||||||
|
// uninteresting code
|
||||||
|
TIMER_BEGIN("description2");
|
||||||
|
// code to be measured
|
||||||
|
TIMER_END("description2");
|
||||||
|
// uninteresting code
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
#define TIMER_BEGIN(description) { ScopeTimer UID__(description)
|
||||||
|
#define TIMER_END(description) }
|
||||||
|
|
||||||
|
|
||||||
|
// used via TIMER_ACCRUE
|
||||||
|
class ScopeTimerAccrue
|
||||||
{
|
{
|
||||||
double t0;
|
double t0;
|
||||||
TimerClient* tc;
|
TimerClient* tc;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ScopedSumTimer(TimerClient* tc_)
|
ScopeTimerAccrue(TimerClient* tc_)
|
||||||
{
|
{
|
||||||
t0 = get_time();
|
t0 = get_time();
|
||||||
tc = tc_;
|
tc = tc_;
|
||||||
}
|
}
|
||||||
~ScopedSumTimer()
|
~ScopeTimerAccrue()
|
||||||
{
|
{
|
||||||
double t1 = get_time();
|
double t1 = get_time();
|
||||||
double dt = t1-t0;
|
double dt = t1-t0;
|
||||||
@ -163,12 +188,39 @@ public:
|
|||||||
|
|
||||||
// disallow copying (makes no sense)
|
// disallow copying (makes no sense)
|
||||||
private:
|
private:
|
||||||
ScopedSumTimer& operator=(const ScopedSumTimer&);
|
ScopeTimerAccrue& operator=(const ScopeTimerAccrue&);
|
||||||
};
|
};
|
||||||
|
|
||||||
// bills to <client> the time elapsed between execution reaching the
|
|
||||||
// point of macro invocation and end of the current basic block.
|
// "allocate" a new TimerClient that will keep track of the total time
|
||||||
// total times for all clients are displayed by timer_display_client_totals.
|
// billed to it, along with a description string. These are displayed when
|
||||||
#define SUM_TIMER(client) ScopedSumTimer UID__(client)
|
// timer_display_client_totals is called.
|
||||||
|
// Invoke this at file or function scope; a (static) TimerClient pointer of
|
||||||
|
// name <id> will be defined, which should be passed to TIMER_ACCRUE.
|
||||||
|
#define TIMER_ADD_CLIENT(id)\
|
||||||
|
static TimerClient UID__;\
|
||||||
|
static TimerClient* id = timer_add_client(&UID__, #id);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Measure the time taken to execute code up until end of the current scope;
|
||||||
|
bill it to the given TimerClient object. Can safely be nested.
|
||||||
|
Useful for measuring total time spent in a function or basic block over the
|
||||||
|
entire program.
|
||||||
|
<description> must remain valid over the lifetime of this object;
|
||||||
|
a string literal is safest.
|
||||||
|
|
||||||
|
Example usage:
|
||||||
|
TIMER_ADD_CLIENT(identifier)
|
||||||
|
|
||||||
|
void func()
|
||||||
|
{
|
||||||
|
TIMER_ACCRUE(name_of_pointer_to_client);
|
||||||
|
// code to be measured
|
||||||
|
}
|
||||||
|
|
||||||
|
[at exit]
|
||||||
|
timer_display_client_totals();
|
||||||
|
*/
|
||||||
|
#define TIMER_ACCRUE(client) ScopeTimerAccrue UID__(client)
|
||||||
|
|
||||||
#endif // #ifndef TIMER_H
|
#endif // #ifndef TIMER_H
|
||||||
|
@ -128,12 +128,19 @@ static void Frame()
|
|||||||
music_player.update();
|
music_player.update();
|
||||||
PROFILE_END( "update music" );
|
PROFILE_END( "update music" );
|
||||||
|
|
||||||
|
calc_fps();
|
||||||
|
// old method - "exact" but contains jumps
|
||||||
|
#if 0
|
||||||
static double last_time;
|
static double last_time;
|
||||||
const double time = get_time();
|
const double time = get_time();
|
||||||
const float TimeSinceLastFrame = (float)(time-last_time);
|
const float TimeSinceLastFrame = (float)(time-last_time);
|
||||||
last_time = time;
|
last_time = time;
|
||||||
ONCE(return);
|
ONCE(return); // first call: set last_time and return
|
||||||
// first call: set last_time and return
|
|
||||||
|
// new method - filtered and more smooth, but errors may accumulate
|
||||||
|
#else
|
||||||
|
const float TimeSinceLastFrame = spf;
|
||||||
|
#endif
|
||||||
debug_assert(TimeSinceLastFrame >= 0.0f);
|
debug_assert(TimeSinceLastFrame >= 0.0f);
|
||||||
|
|
||||||
PROFILE_START( "reload changed files" );
|
PROFILE_START( "reload changed files" );
|
||||||
@ -230,7 +237,6 @@ static void Frame()
|
|||||||
|
|
||||||
g_Profiler.Frame();
|
g_Profiler.Frame();
|
||||||
|
|
||||||
calc_fps();
|
|
||||||
if(g_FixedFrameTiming && frameCount==100)
|
if(g_FixedFrameTiming && frameCount==100)
|
||||||
kill_mainloop();
|
kill_mainloop();
|
||||||
}
|
}
|
||||||
|
@ -170,7 +170,7 @@ void CONFIG_Init(int argc, char* argv[])
|
|||||||
{
|
{
|
||||||
debug_printf("CFG_Init &argc=%p &argv=%p\n", &argc, &argv);
|
debug_printf("CFG_Init &argc=%p &argv=%p\n", &argc, &argv);
|
||||||
|
|
||||||
TIMER(CONFIG_Init);
|
TIMER("CONFIG_Init");
|
||||||
MICROLOG(L"init config");
|
MICROLOG(L"init config");
|
||||||
|
|
||||||
new CConfigDB;
|
new CConfigDB;
|
||||||
|
@ -131,14 +131,14 @@ static int SetVideoMode(int w, int h, int bpp, bool fullscreen)
|
|||||||
void GUI_Init()
|
void GUI_Init()
|
||||||
{
|
{
|
||||||
#ifndef NO_GUI
|
#ifndef NO_GUI
|
||||||
{TIMER(ps_gui_init);
|
{TIMER("ps_gui_init");
|
||||||
g_GUI.Initialize();}
|
g_GUI.Initialize();}
|
||||||
|
|
||||||
{TIMER(ps_gui_setup_xml);
|
{TIMER("ps_gui_setup_xml");
|
||||||
g_GUI.LoadXMLFile("gui/test/setup.xml");}
|
g_GUI.LoadXMLFile("gui/test/setup.xml");}
|
||||||
{TIMER(ps_gui_styles_xml);
|
{TIMER("ps_gui_styles_xml");
|
||||||
g_GUI.LoadXMLFile("gui/test/styles.xml");}
|
g_GUI.LoadXMLFile("gui/test/styles.xml");}
|
||||||
{TIMER(ps_gui_sprite1_xml);
|
{TIMER("ps_gui_sprite1_xml");
|
||||||
g_GUI.LoadXMLFile("gui/test/sprite1.xml");}
|
g_GUI.LoadXMLFile("gui/test/sprite1.xml");}
|
||||||
|
|
||||||
// Atlas is running, we won't need these GUI pages (for now!
|
// Atlas is running, we won't need these GUI pages (for now!
|
||||||
@ -147,23 +147,23 @@ void GUI_Init()
|
|||||||
// if(ATLAS_IsRunning())
|
// if(ATLAS_IsRunning())
|
||||||
// return;
|
// return;
|
||||||
|
|
||||||
{TIMER(ps_gui_1);
|
{TIMER("ps_gui_1");
|
||||||
g_GUI.LoadXMLFile("gui/test/1_init.xml");}
|
g_GUI.LoadXMLFile("gui/test/1_init.xml");}
|
||||||
{TIMER(ps_gui_2);
|
{TIMER("ps_gui_2");
|
||||||
g_GUI.LoadXMLFile("gui/test/2_mainmenu.xml");}
|
g_GUI.LoadXMLFile("gui/test/2_mainmenu.xml");}
|
||||||
{TIMER(ps_gui_3);
|
{TIMER("ps_gui_3");
|
||||||
g_GUI.LoadXMLFile("gui/test/3_loading.xml");}
|
g_GUI.LoadXMLFile("gui/test/3_loading.xml");}
|
||||||
{TIMER(ps_gui_4);
|
{TIMER("ps_gui_4");
|
||||||
g_GUI.LoadXMLFile("gui/test/4_session.xml");}
|
g_GUI.LoadXMLFile("gui/test/4_session.xml");}
|
||||||
{TIMER(ps_gui_6);
|
{TIMER("ps_gui_6");
|
||||||
g_GUI.LoadXMLFile("gui/test/6_subwindows.xml");}
|
g_GUI.LoadXMLFile("gui/test/6_subwindows.xml");}
|
||||||
{TIMER(ps_gui_6_1);
|
{TIMER("ps_gui_6_1");
|
||||||
g_GUI.LoadXMLFile("gui/test/6_1_manual.xml");}
|
g_GUI.LoadXMLFile("gui/test/6_1_manual.xml");}
|
||||||
{TIMER(ps_gui_6_2);
|
{TIMER("ps_gui_6_2");
|
||||||
g_GUI.LoadXMLFile("gui/test/6_2_jukebox.xml");}
|
g_GUI.LoadXMLFile("gui/test/6_2_jukebox.xml");}
|
||||||
{TIMER(ps_gui_7);
|
{TIMER("ps_gui_7");
|
||||||
g_GUI.LoadXMLFile("gui/test/7_atlas.xml");}
|
g_GUI.LoadXMLFile("gui/test/7_atlas.xml");}
|
||||||
{TIMER(ps_gui_9);
|
{TIMER("ps_gui_9");
|
||||||
g_GUI.LoadXMLFile("gui/test/9_global.xml");}
|
g_GUI.LoadXMLFile("gui/test/9_global.xml");}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -395,7 +395,7 @@ void Render()
|
|||||||
|
|
||||||
static void InitScripting()
|
static void InitScripting()
|
||||||
{
|
{
|
||||||
TIMER(InitScripting);
|
TIMER("InitScripting");
|
||||||
// Create the scripting host. This needs to be done before the GUI is created.
|
// Create the scripting host. This needs to be done before the GUI is created.
|
||||||
new ScriptingHost;
|
new ScriptingHost;
|
||||||
|
|
||||||
@ -449,7 +449,7 @@ static void InitScripting()
|
|||||||
|
|
||||||
static void InitVfs(const char* argv0)
|
static void InitVfs(const char* argv0)
|
||||||
{
|
{
|
||||||
TIMER(InitVfs);
|
TIMER("InitVfs");
|
||||||
// set root directory to "$game_dir/data". all relative file paths
|
// set root directory to "$game_dir/data". all relative file paths
|
||||||
// passed to file.cpp will be based from this dir.
|
// passed to file.cpp will be based from this dir.
|
||||||
// (we don't set current directory because other libraries may
|
// (we don't set current directory because other libraries may
|
||||||
@ -478,7 +478,7 @@ static void InitPs(bool setup_gui)
|
|||||||
{
|
{
|
||||||
// console
|
// console
|
||||||
{
|
{
|
||||||
TIMER(ps_console);
|
TIMER("ps_console");
|
||||||
|
|
||||||
g_Console->UpdateScreenSize(g_xres, g_yres);
|
g_Console->UpdateScreenSize(g_xres, g_yres);
|
||||||
|
|
||||||
@ -491,7 +491,7 @@ static void InitPs(bool setup_gui)
|
|||||||
|
|
||||||
// language and hotkeys
|
// language and hotkeys
|
||||||
{
|
{
|
||||||
TIMER(ps_lang_hotkeys);
|
TIMER("ps_lang_hotkeys");
|
||||||
|
|
||||||
std::string lang = "english";
|
std::string lang = "english";
|
||||||
CFG_GET_SYS_VAL("language", String, lang);
|
CFG_GET_SYS_VAL("language", String, lang);
|
||||||
@ -549,7 +549,7 @@ static void ShutdownPs()
|
|||||||
|
|
||||||
static void InitRenderer()
|
static void InitRenderer()
|
||||||
{
|
{
|
||||||
TIMER(InitRenderer);
|
TIMER("InitRenderer");
|
||||||
// create renderer
|
// create renderer
|
||||||
new CRenderer;
|
new CRenderer;
|
||||||
|
|
||||||
@ -695,28 +695,28 @@ void Shutdown()
|
|||||||
if (g_Game)
|
if (g_Game)
|
||||||
EndGame();
|
EndGame();
|
||||||
|
|
||||||
TIMER_START("shutdown Scheduler");
|
TIMER_BEGIN("shutdown Scheduler");
|
||||||
delete &g_Scheduler;
|
delete &g_Scheduler;
|
||||||
TIMER_END("shutdown Scheduler");
|
TIMER_END("shutdown Scheduler");
|
||||||
|
|
||||||
TIMER_START("shutdown SessionManager");
|
TIMER_BEGIN("shutdown SessionManager");
|
||||||
delete &g_SessionManager;
|
delete &g_SessionManager;
|
||||||
TIMER_END("shutdown SessionManager");
|
TIMER_END("shutdown SessionManager");
|
||||||
|
|
||||||
TIMER_START("shutdown mouse stuff");
|
TIMER_BEGIN("shutdown mouse stuff");
|
||||||
delete &g_Mouseover;
|
delete &g_Mouseover;
|
||||||
delete &g_Selection;
|
delete &g_Selection;
|
||||||
delete &g_BuildingPlacer;
|
delete &g_BuildingPlacer;
|
||||||
TIMER_END("shutdown mouse stuff");
|
TIMER_END("shutdown mouse stuff");
|
||||||
|
|
||||||
TIMER_START("shutdown Pathfinder");
|
TIMER_BEGIN("shutdown Pathfinder");
|
||||||
delete &g_Pathfinder;
|
delete &g_Pathfinder;
|
||||||
TIMER_END("shutdown Pathfinder");
|
TIMER_END("shutdown Pathfinder");
|
||||||
|
|
||||||
// Managed by CWorld
|
// Managed by CWorld
|
||||||
// delete &g_EntityManager;
|
// delete &g_EntityManager;
|
||||||
|
|
||||||
TIMER_START("shutdown game scripting stuff");
|
TIMER_BEGIN("shutdown game scripting stuff");
|
||||||
delete &g_GameAttributes;
|
delete &g_GameAttributes;
|
||||||
delete &g_JSGameEvents;
|
delete &g_JSGameEvents;
|
||||||
|
|
||||||
@ -724,7 +724,7 @@ void Shutdown()
|
|||||||
TIMER_END("shutdown game scripting stuff");
|
TIMER_END("shutdown game scripting stuff");
|
||||||
|
|
||||||
// destroy actor related stuff
|
// destroy actor related stuff
|
||||||
TIMER_START("shutdown actor stuff");
|
TIMER_BEGIN("shutdown actor stuff");
|
||||||
delete &g_UnitMan;
|
delete &g_UnitMan;
|
||||||
delete &g_ObjMan;
|
delete &g_ObjMan;
|
||||||
delete &g_SkelAnimMan;
|
delete &g_SkelAnimMan;
|
||||||
@ -734,38 +734,38 @@ void Shutdown()
|
|||||||
TIMER_END("shutdown actor stuff");
|
TIMER_END("shutdown actor stuff");
|
||||||
|
|
||||||
// destroy terrain related stuff
|
// destroy terrain related stuff
|
||||||
TIMER_START("shutdown TexMan");
|
TIMER_BEGIN("shutdown TexMan");
|
||||||
delete &g_TexMan;
|
delete &g_TexMan;
|
||||||
TIMER_END("shutdown TexMan");
|
TIMER_END("shutdown TexMan");
|
||||||
|
|
||||||
// destroy renderer
|
// destroy renderer
|
||||||
TIMER_START("shutdown Renderer");
|
TIMER_BEGIN("shutdown Renderer");
|
||||||
delete &g_Renderer;
|
delete &g_Renderer;
|
||||||
g_VBMan.Shutdown();
|
g_VBMan.Shutdown();
|
||||||
TIMER_END("shutdown Renderer");
|
TIMER_END("shutdown Renderer");
|
||||||
|
|
||||||
TIMER_START("shutdown ScriptingHost");
|
TIMER_BEGIN("shutdown ScriptingHost");
|
||||||
delete &g_ScriptingHost;
|
delete &g_ScriptingHost;
|
||||||
TIMER_END("shutdown ScriptingHost");
|
TIMER_END("shutdown ScriptingHost");
|
||||||
|
|
||||||
TIMER_START("shutdown ConfigDB");
|
TIMER_BEGIN("shutdown ConfigDB");
|
||||||
delete &g_ConfigDB;
|
delete &g_ConfigDB;
|
||||||
TIMER_END("shutdown ConfigDB");
|
TIMER_END("shutdown ConfigDB");
|
||||||
|
|
||||||
// Shut down the network loop
|
// Shut down the network loop
|
||||||
TIMER_START("shutdown CSocketBase");
|
TIMER_BEGIN("shutdown CSocketBase");
|
||||||
CSocketBase::Shutdown();
|
CSocketBase::Shutdown();
|
||||||
TIMER_END("shutdown CSocketBase");
|
TIMER_END("shutdown CSocketBase");
|
||||||
|
|
||||||
// Really shut down the i18n system. Any future calls
|
// Really shut down the i18n system. Any future calls
|
||||||
// to translate() will crash.
|
// to translate() will crash.
|
||||||
TIMER_START("shutdown I18N");
|
TIMER_BEGIN("shutdown I18N");
|
||||||
I18n::Shutdown();
|
I18n::Shutdown();
|
||||||
TIMER_END("shutdown I18N");
|
TIMER_END("shutdown I18N");
|
||||||
|
|
||||||
// resource
|
// resource
|
||||||
// first shut down all resource owners, and then the handle manager.
|
// first shut down all resource owners, and then the handle manager.
|
||||||
TIMER_START("resource modules");
|
TIMER_BEGIN("resource modules");
|
||||||
snd_shutdown();
|
snd_shutdown();
|
||||||
vfs_shutdown();
|
vfs_shutdown();
|
||||||
|
|
||||||
@ -778,7 +778,7 @@ void Shutdown()
|
|||||||
mem_shutdown();
|
mem_shutdown();
|
||||||
TIMER_END("resource modules");
|
TIMER_END("resource modules");
|
||||||
|
|
||||||
TIMER_START("shutdown misc");
|
TIMER_BEGIN("shutdown misc");
|
||||||
file_shutdown();
|
file_shutdown();
|
||||||
|
|
||||||
timer_display_client_totals();
|
timer_display_client_totals();
|
||||||
@ -924,7 +924,7 @@ void Init(int argc, char* argv[], bool setup_videomode, bool setup_gui)
|
|||||||
InitRenderer();
|
InitRenderer();
|
||||||
|
|
||||||
{
|
{
|
||||||
TIMER(Init_entitiessection);
|
TIMER("Init_entitiessection");
|
||||||
// This needs to be done after the renderer has loaded all its actors...
|
// This needs to be done after the renderer has loaded all its actors...
|
||||||
new CBaseEntityCollection;
|
new CBaseEntityCollection;
|
||||||
// CEntityManager is managed by CWorld
|
// CEntityManager is managed by CWorld
|
||||||
@ -934,7 +934,7 @@ void Init(int argc, char* argv[], bool setup_videomode, bool setup_gui)
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
TIMER(Init_miscgamesection);
|
TIMER("Init_miscgamesection");
|
||||||
new CPathfindEngine;
|
new CPathfindEngine;
|
||||||
new CBuildingPlacer;
|
new CBuildingPlacer;
|
||||||
new CSessionManager;
|
new CSessionManager;
|
||||||
@ -942,7 +942,7 @@ void Init(int argc, char* argv[], bool setup_videomode, bool setup_gui)
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
TIMER(Init_misc);
|
TIMER("Init_misc");
|
||||||
|
|
||||||
// Register a few Game/Network JS globals
|
// Register a few Game/Network JS globals
|
||||||
g_ScriptingHost.SetGlobal("g_GameAttributes", OBJECT_TO_JSVAL(g_GameAttributes.GetScript()));
|
g_ScriptingHost.SetGlobal("g_GameAttributes", OBJECT_TO_JSVAL(g_GameAttributes.GetScript()));
|
||||||
@ -959,13 +959,13 @@ void Init(int argc, char* argv[], bool setup_videomode, bool setup_gui)
|
|||||||
|
|
||||||
#ifndef NO_GUI
|
#ifndef NO_GUI
|
||||||
{
|
{
|
||||||
TIMER(Init_guiload);
|
TIMER("Init_guiload");
|
||||||
g_GUI.SendEventToAll("load");
|
g_GUI.SendEventToAll("load");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
{
|
{
|
||||||
TIMER(Init_renderblank);
|
TIMER("Init_renderblank");
|
||||||
MICROLOG(L"render blank");
|
MICROLOG(L"render blank");
|
||||||
// render everything to a blank frame to force renderer to load everything
|
// render everything to a blank frame to force renderer to load everything
|
||||||
RenderNoCull();
|
RenderNoCull();
|
||||||
|
@ -34,7 +34,7 @@ static std::string SplitExts(const char *exts)
|
|||||||
|
|
||||||
void WriteSystemInfo()
|
void WriteSystemInfo()
|
||||||
{
|
{
|
||||||
TIMER(write_sys_info);
|
TIMER("write_sys_info");
|
||||||
|
|
||||||
// get_cpu_info and get_gfx_info already called during init - see call site
|
// get_cpu_info and get_gfx_info already called during init - see call site
|
||||||
get_snd_info();
|
get_snd_info();
|
||||||
|
Loading…
Reference in New Issue
Block a user