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
|
||||
void CTextureEntry::BuildBaseColor()
|
||||
{
|
||||
SUM_TIMER(tc_mipmap_basecolor);
|
||||
TIMER_ACCRUE(tc_mipmap_basecolor);
|
||||
|
||||
if (m_pProperties && m_pProperties->HasBaseColor())
|
||||
{
|
||||
|
@ -157,7 +157,10 @@ void cpu_init()
|
||||
// we need full precision when calculating the time.
|
||||
// if there's a spot where we want to speed up divides|sqrts,
|
||||
// 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
|
||||
// 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);
|
||||
if(!d->os_dir)
|
||||
{
|
||||
int err = -1;
|
||||
int err;
|
||||
switch(errno)
|
||||
{
|
||||
case ENOMEM:
|
||||
err = ERR_NO_MEM;
|
||||
break;
|
||||
case ENOENT:
|
||||
err = ERR_PATH_NOT_FOUND;
|
||||
// default: err already set
|
||||
break;
|
||||
default:
|
||||
err = -1;
|
||||
break;
|
||||
}
|
||||
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_;
|
||||
|
||||
get_another_entry:
|
||||
errno = 0;
|
||||
struct dirent* os_ent = readdir(d->os_dir);
|
||||
if(!os_ent)
|
||||
{
|
||||
if(errno)
|
||||
debug_warn("readdir failed");
|
||||
return ERR_DIR_END;
|
||||
}
|
||||
|
||||
// copy os_ent.name[]; we need it for stat() #if !OS_WIN and
|
||||
// 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
|
||||
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
|
||||
(complicated and brittle WRT versioning). We are therefore happy to
|
||||
use the already existing mod mechanism.
|
||||
|
||||
Patching
|
||||
--------
|
||||
|
||||
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
|
||||
constant-time VFS path -> file location lookup) simply due to locality;
|
||||
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
|
||||
patched files will be merged into the archive.
|
||||
|
||||
|
||||
For more information, see 'Mount Details' below.
|
||||
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
|
||||
main archive, whereas previously the patch could simply be deleted.
|
||||
|
||||
|
||||
Mount Details
|
||||
@ -176,6 +181,8 @@ this is just an optimization.
|
||||
|
||||
To ease development, files may additionally be stored in normal directories.
|
||||
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
|
||||
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.
|
||||
Handle zip_archive_open(const char* fn)
|
||||
{
|
||||
TIMER(zip_archive_open);
|
||||
TIMER("zip_archive_open");
|
||||
return h_alloc(H_ZArchive, fn);
|
||||
}
|
||||
|
||||
@ -816,12 +816,12 @@ double t0 = get_time();
|
||||
|
||||
if(ctx->compressed)
|
||||
{
|
||||
SUM_TIMER(tc_zip_inflate);
|
||||
TIMER_ACCRUE(tc_zip_inflate);
|
||||
err = inflate(zs, Z_SYNC_FLUSH);
|
||||
}
|
||||
else
|
||||
{
|
||||
SUM_TIMER(tc_zip_memcpy);
|
||||
TIMER_ACCRUE(tc_zip_memcpy);
|
||||
memcpy2(zs->next_out, zs->next_in, zs->avail_in);
|
||||
uInt size = MIN(zs->avail_in, zs->avail_out);
|
||||
zs->avail_out -= size;
|
||||
|
@ -506,6 +506,7 @@ int ogl_tex_free(Handle& ht)
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// state setters (see "Texture Parameters" in docs)
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
// we require the below functions be called before uploading; this avoids
|
||||
// potentially redundant glTexParameter calls (we'd otherwise need to always
|
||||
@ -587,12 +588,84 @@ int ogl_tex_set_wrap(Handle ht, GLint wrap)
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// 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)
|
||||
{
|
||||
// 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?
|
||||
const bool need_mipmaps = filter_uses_mipmaps(filter);
|
||||
// .. 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
|
||||
{
|
||||
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.
|
||||
// if not 0, parameters override the following:
|
||||
// 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.
|
||||
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);
|
||||
Tex* t = &ot->t;
|
||||
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;
|
||||
|
||||
// decompress S3TC if that's not supported by OpenGL.
|
||||
static const bool have_s3tc = detect_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);
|
||||
}
|
||||
|
||||
// determine fmt and int_fmt, allowing for user override.
|
||||
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
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
// retrieve texture dimensions and bits per pixel.
|
||||
// all params are optional and filled if non-NULL.
|
||||
@ -842,6 +896,7 @@ int ogl_tex_get_data(Handle ht, void** p)
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// misc API
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
// bind the texture to the specified unit [number] in preparation for
|
||||
// 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
|
||||
//
|
||||
|
||||
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.
|
||||
// if not 0, parameters override the following:
|
||||
// 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.
|
||||
int tex_transform(Tex* t, uint transforms)
|
||||
{
|
||||
SUM_TIMER(tc_transform);
|
||||
TIMER_ACCRUE(tc_transform);
|
||||
|
||||
const uint target_flags = t->flags ^ transforms;
|
||||
for(;;)
|
||||
|
@ -40,10 +40,13 @@ extern u64 rdtsc(void);
|
||||
|
||||
|
||||
#ifndef _MCW_PC
|
||||
#define _MCW_PC 0x0300 // Precision Control
|
||||
# define _MCW_PC 0x0300 // Precision Control
|
||||
#endif
|
||||
#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
|
||||
|
||||
#define _control87 ia32_control87
|
||||
|
@ -37,7 +37,6 @@ double get_time()
|
||||
double t;
|
||||
|
||||
#if HAVE_CLOCK_GETTIME
|
||||
|
||||
static struct timespec start = {0};
|
||||
struct timespec ts;
|
||||
|
||||
@ -46,9 +45,7 @@ double get_time()
|
||||
|
||||
(void)clock_gettime(CLOCK_REALTIME, &ts);
|
||||
t = (ts.tv_sec - start.tv_sec) + (ts.tv_nsec - start.tv_nsec)*1e-9;
|
||||
|
||||
#elif HAVE_GETTIMEOFDAY
|
||||
|
||||
static struct timeval start;
|
||||
struct timeval cur;
|
||||
|
||||
@ -57,11 +54,8 @@ double get_time()
|
||||
|
||||
gettimeofday(&cur, 0);
|
||||
t = (cur.tv_sec - start.tv_sec) + (cur.tv_usec - start.tv_usec)*1e-6;
|
||||
|
||||
#else
|
||||
|
||||
#error "get_time: add timer implementation for this platform!"
|
||||
|
||||
#endif
|
||||
|
||||
// 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()
|
||||
{
|
||||
// may take a while to determine, so cache it
|
||||
@ -110,6 +106,7 @@ double timer_res()
|
||||
// filter values are tuned for 100 FPS.
|
||||
|
||||
int fps;
|
||||
float spf;
|
||||
|
||||
void calc_fps()
|
||||
{
|
||||
@ -225,6 +222,8 @@ void calc_fps()
|
||||
old = cur_fps*gain + old*(1.0-gain);
|
||||
avg_fps = old;
|
||||
|
||||
spf = 1.0 / avg_fps;
|
||||
|
||||
// update fps counter if it differs "enough"
|
||||
// currently, that means off by more than 5 FPS or 5%.
|
||||
const double difference = fabs(avg_fps-fps);
|
||||
@ -242,15 +241,20 @@ void calc_fps()
|
||||
// this supplements in-game profiling by providing low-overhead,
|
||||
// high resolution time accounting.
|
||||
|
||||
|
||||
// use static storage to ensure clients can be added at any time,
|
||||
// especially before the heap has been initialized.
|
||||
// intrusive linked-list of all clients. a fixed-size limit would be
|
||||
// acceptable (since timers are added manually), but the list is easy
|
||||
// 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 TimerClient* clients;
|
||||
|
||||
|
||||
// allocate a new TimerClient whose total (added to by timer_bill_client)
|
||||
// will be displayed by timer_display_client_totals.
|
||||
// make the given TimerClient (usually instantiated as static data)
|
||||
// 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:
|
||||
// - may be called at any time;
|
||||
// - 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.
|
||||
extern double get_time(void);
|
||||
|
||||
// return resolution (expressed in [s]) of the time source underlying
|
||||
// get_time.
|
||||
extern double timer_res(void);
|
||||
|
||||
// calculate fps (call once per frame)
|
||||
// several smooth filters (tuned for ~100 FPS)
|
||||
// => 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);
|
||||
|
||||
@ -45,7 +48,10 @@ extern void calc_fps(void);
|
||||
// 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;
|
||||
// fields cannot be made private due to C compatibility requirement.
|
||||
struct TimerClient
|
||||
@ -56,13 +62,13 @@ struct TimerClient
|
||||
const char* description;
|
||||
|
||||
TimerClient* next;
|
||||
|
||||
int dummy;
|
||||
};
|
||||
|
||||
|
||||
// allocate a new TimerClient whose total (added to by timer_bill_client)
|
||||
// will be displayed by timer_display_client_totals.
|
||||
// make the given TimerClient (usually instantiated as static data)
|
||||
// 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:
|
||||
// - may be called at any time;
|
||||
// - always succeeds (there's no fixed limit);
|
||||
@ -70,10 +76,6 @@ struct TimerClient
|
||||
// - description must remain valid until exit; a string literal is safest.
|
||||
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.
|
||||
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;
|
||||
const char* name;
|
||||
const char* description;
|
||||
|
||||
public:
|
||||
ScopedTimer(const char* _name)
|
||||
ScopeTimer(const char* _description)
|
||||
{
|
||||
t0 = get_time();
|
||||
name = _name;
|
||||
description = _description;
|
||||
}
|
||||
~ScopedTimer()
|
||||
|
||||
~ScopeTimer()
|
||||
{
|
||||
double t1 = get_time();
|
||||
double dt = t1-t0;
|
||||
@ -111,50 +116,70 @@ public:
|
||||
else if(dt > 1e-3)
|
||||
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)
|
||||
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:
|
||||
|
||||
static TimerClient* client = timer_add_client("description");
|
||||
|
||||
void func()
|
||||
{
|
||||
SUM_TIMER(client);
|
||||
(code to be measured)
|
||||
TIMER("description");
|
||||
// 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;
|
||||
TimerClient* tc;
|
||||
|
||||
public:
|
||||
ScopedSumTimer(TimerClient* tc_)
|
||||
ScopeTimerAccrue(TimerClient* tc_)
|
||||
{
|
||||
t0 = get_time();
|
||||
tc = tc_;
|
||||
}
|
||||
~ScopedSumTimer()
|
||||
~ScopeTimerAccrue()
|
||||
{
|
||||
double t1 = get_time();
|
||||
double dt = t1-t0;
|
||||
@ -163,12 +188,39 @@ public:
|
||||
|
||||
// disallow copying (makes no sense)
|
||||
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.
|
||||
// total times for all clients are displayed by timer_display_client_totals.
|
||||
#define SUM_TIMER(client) ScopedSumTimer UID__(client)
|
||||
|
||||
// "allocate" a new TimerClient that will keep track of the total time
|
||||
// billed to it, along with a description string. These are displayed when
|
||||
// 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
|
||||
|
@ -128,12 +128,19 @@ static void Frame()
|
||||
music_player.update();
|
||||
PROFILE_END( "update music" );
|
||||
|
||||
calc_fps();
|
||||
// old method - "exact" but contains jumps
|
||||
#if 0
|
||||
static double last_time;
|
||||
const double time = get_time();
|
||||
const float TimeSinceLastFrame = (float)(time-last_time);
|
||||
last_time = time;
|
||||
ONCE(return);
|
||||
// first call: set last_time and return
|
||||
ONCE(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);
|
||||
|
||||
PROFILE_START( "reload changed files" );
|
||||
@ -230,7 +237,6 @@ static void Frame()
|
||||
|
||||
g_Profiler.Frame();
|
||||
|
||||
calc_fps();
|
||||
if(g_FixedFrameTiming && frameCount==100)
|
||||
kill_mainloop();
|
||||
}
|
||||
|
@ -170,7 +170,7 @@ void CONFIG_Init(int argc, char* argv[])
|
||||
{
|
||||
debug_printf("CFG_Init &argc=%p &argv=%p\n", &argc, &argv);
|
||||
|
||||
TIMER(CONFIG_Init);
|
||||
TIMER("CONFIG_Init");
|
||||
MICROLOG(L"init config");
|
||||
|
||||
new CConfigDB;
|
||||
|
@ -131,14 +131,14 @@ static int SetVideoMode(int w, int h, int bpp, bool fullscreen)
|
||||
void GUI_Init()
|
||||
{
|
||||
#ifndef NO_GUI
|
||||
{TIMER(ps_gui_init);
|
||||
{TIMER("ps_gui_init");
|
||||
g_GUI.Initialize();}
|
||||
|
||||
{TIMER(ps_gui_setup_xml);
|
||||
{TIMER("ps_gui_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");}
|
||||
{TIMER(ps_gui_sprite1_xml);
|
||||
{TIMER("ps_gui_sprite1_xml");
|
||||
g_GUI.LoadXMLFile("gui/test/sprite1.xml");}
|
||||
|
||||
// Atlas is running, we won't need these GUI pages (for now!
|
||||
@ -147,23 +147,23 @@ void GUI_Init()
|
||||
// if(ATLAS_IsRunning())
|
||||
// return;
|
||||
|
||||
{TIMER(ps_gui_1);
|
||||
{TIMER("ps_gui_1");
|
||||
g_GUI.LoadXMLFile("gui/test/1_init.xml");}
|
||||
{TIMER(ps_gui_2);
|
||||
{TIMER("ps_gui_2");
|
||||
g_GUI.LoadXMLFile("gui/test/2_mainmenu.xml");}
|
||||
{TIMER(ps_gui_3);
|
||||
{TIMER("ps_gui_3");
|
||||
g_GUI.LoadXMLFile("gui/test/3_loading.xml");}
|
||||
{TIMER(ps_gui_4);
|
||||
{TIMER("ps_gui_4");
|
||||
g_GUI.LoadXMLFile("gui/test/4_session.xml");}
|
||||
{TIMER(ps_gui_6);
|
||||
{TIMER("ps_gui_6");
|
||||
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");}
|
||||
{TIMER(ps_gui_6_2);
|
||||
{TIMER("ps_gui_6_2");
|
||||
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");}
|
||||
{TIMER(ps_gui_9);
|
||||
{TIMER("ps_gui_9");
|
||||
g_GUI.LoadXMLFile("gui/test/9_global.xml");}
|
||||
#endif
|
||||
}
|
||||
@ -395,7 +395,7 @@ void Render()
|
||||
|
||||
static void InitScripting()
|
||||
{
|
||||
TIMER(InitScripting);
|
||||
TIMER("InitScripting");
|
||||
// Create the scripting host. This needs to be done before the GUI is created.
|
||||
new ScriptingHost;
|
||||
|
||||
@ -449,7 +449,7 @@ static void InitScripting()
|
||||
|
||||
static void InitVfs(const char* argv0)
|
||||
{
|
||||
TIMER(InitVfs);
|
||||
TIMER("InitVfs");
|
||||
// set root directory to "$game_dir/data". all relative file paths
|
||||
// passed to file.cpp will be based from this dir.
|
||||
// (we don't set current directory because other libraries may
|
||||
@ -478,7 +478,7 @@ static void InitPs(bool setup_gui)
|
||||
{
|
||||
// console
|
||||
{
|
||||
TIMER(ps_console);
|
||||
TIMER("ps_console");
|
||||
|
||||
g_Console->UpdateScreenSize(g_xres, g_yres);
|
||||
|
||||
@ -491,7 +491,7 @@ static void InitPs(bool setup_gui)
|
||||
|
||||
// language and hotkeys
|
||||
{
|
||||
TIMER(ps_lang_hotkeys);
|
||||
TIMER("ps_lang_hotkeys");
|
||||
|
||||
std::string lang = "english";
|
||||
CFG_GET_SYS_VAL("language", String, lang);
|
||||
@ -549,7 +549,7 @@ static void ShutdownPs()
|
||||
|
||||
static void InitRenderer()
|
||||
{
|
||||
TIMER(InitRenderer);
|
||||
TIMER("InitRenderer");
|
||||
// create renderer
|
||||
new CRenderer;
|
||||
|
||||
@ -695,28 +695,28 @@ void Shutdown()
|
||||
if (g_Game)
|
||||
EndGame();
|
||||
|
||||
TIMER_START("shutdown Scheduler");
|
||||
TIMER_BEGIN("shutdown Scheduler");
|
||||
delete &g_Scheduler;
|
||||
TIMER_END("shutdown Scheduler");
|
||||
|
||||
TIMER_START("shutdown SessionManager");
|
||||
TIMER_BEGIN("shutdown SessionManager");
|
||||
delete &g_SessionManager;
|
||||
TIMER_END("shutdown SessionManager");
|
||||
|
||||
TIMER_START("shutdown mouse stuff");
|
||||
TIMER_BEGIN("shutdown mouse stuff");
|
||||
delete &g_Mouseover;
|
||||
delete &g_Selection;
|
||||
delete &g_BuildingPlacer;
|
||||
TIMER_END("shutdown mouse stuff");
|
||||
|
||||
TIMER_START("shutdown Pathfinder");
|
||||
TIMER_BEGIN("shutdown Pathfinder");
|
||||
delete &g_Pathfinder;
|
||||
TIMER_END("shutdown Pathfinder");
|
||||
|
||||
// Managed by CWorld
|
||||
// delete &g_EntityManager;
|
||||
|
||||
TIMER_START("shutdown game scripting stuff");
|
||||
TIMER_BEGIN("shutdown game scripting stuff");
|
||||
delete &g_GameAttributes;
|
||||
delete &g_JSGameEvents;
|
||||
|
||||
@ -724,7 +724,7 @@ void Shutdown()
|
||||
TIMER_END("shutdown game scripting stuff");
|
||||
|
||||
// destroy actor related stuff
|
||||
TIMER_START("shutdown actor stuff");
|
||||
TIMER_BEGIN("shutdown actor stuff");
|
||||
delete &g_UnitMan;
|
||||
delete &g_ObjMan;
|
||||
delete &g_SkelAnimMan;
|
||||
@ -734,38 +734,38 @@ void Shutdown()
|
||||
TIMER_END("shutdown actor stuff");
|
||||
|
||||
// destroy terrain related stuff
|
||||
TIMER_START("shutdown TexMan");
|
||||
TIMER_BEGIN("shutdown TexMan");
|
||||
delete &g_TexMan;
|
||||
TIMER_END("shutdown TexMan");
|
||||
|
||||
// destroy renderer
|
||||
TIMER_START("shutdown Renderer");
|
||||
TIMER_BEGIN("shutdown Renderer");
|
||||
delete &g_Renderer;
|
||||
g_VBMan.Shutdown();
|
||||
TIMER_END("shutdown Renderer");
|
||||
|
||||
TIMER_START("shutdown ScriptingHost");
|
||||
TIMER_BEGIN("shutdown ScriptingHost");
|
||||
delete &g_ScriptingHost;
|
||||
TIMER_END("shutdown ScriptingHost");
|
||||
|
||||
TIMER_START("shutdown ConfigDB");
|
||||
TIMER_BEGIN("shutdown ConfigDB");
|
||||
delete &g_ConfigDB;
|
||||
TIMER_END("shutdown ConfigDB");
|
||||
|
||||
// Shut down the network loop
|
||||
TIMER_START("shutdown CSocketBase");
|
||||
TIMER_BEGIN("shutdown CSocketBase");
|
||||
CSocketBase::Shutdown();
|
||||
TIMER_END("shutdown CSocketBase");
|
||||
|
||||
// Really shut down the i18n system. Any future calls
|
||||
// to translate() will crash.
|
||||
TIMER_START("shutdown I18N");
|
||||
TIMER_BEGIN("shutdown I18N");
|
||||
I18n::Shutdown();
|
||||
TIMER_END("shutdown I18N");
|
||||
|
||||
// resource
|
||||
// first shut down all resource owners, and then the handle manager.
|
||||
TIMER_START("resource modules");
|
||||
TIMER_BEGIN("resource modules");
|
||||
snd_shutdown();
|
||||
vfs_shutdown();
|
||||
|
||||
@ -778,7 +778,7 @@ void Shutdown()
|
||||
mem_shutdown();
|
||||
TIMER_END("resource modules");
|
||||
|
||||
TIMER_START("shutdown misc");
|
||||
TIMER_BEGIN("shutdown misc");
|
||||
file_shutdown();
|
||||
|
||||
timer_display_client_totals();
|
||||
@ -924,7 +924,7 @@ void Init(int argc, char* argv[], bool setup_videomode, bool setup_gui)
|
||||
InitRenderer();
|
||||
|
||||
{
|
||||
TIMER(Init_entitiessection);
|
||||
TIMER("Init_entitiessection");
|
||||
// This needs to be done after the renderer has loaded all its actors...
|
||||
new CBaseEntityCollection;
|
||||
// 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 CBuildingPlacer;
|
||||
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
|
||||
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
|
||||
{
|
||||
TIMER(Init_guiload);
|
||||
TIMER("Init_guiload");
|
||||
g_GUI.SendEventToAll("load");
|
||||
}
|
||||
#endif
|
||||
|
||||
{
|
||||
TIMER(Init_renderblank);
|
||||
TIMER("Init_renderblank");
|
||||
MICROLOG(L"render blank");
|
||||
// render everything to a blank frame to force renderer to load everything
|
||||
RenderNoCull();
|
||||
|
@ -34,7 +34,7 @@ static std::string SplitExts(const char *exts)
|
||||
|
||||
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_snd_info();
|
||||
|
Loading…
Reference in New Issue
Block a user