1
1
forked from 0ad/0ad

improved update frequency and accuracy of progress bar (by splitting up more init functions).

also add thunk mechanism to ease binding to member functions;
finally, added instrumentation to measure how long functions really take

This was SVN commit r2038.
This commit is contained in:
janwas 2005-03-22 21:00:56 +00:00
parent 9feaba2f36
commit f19d8dafee
12 changed files with 166 additions and 149 deletions

View File

@ -17,6 +17,7 @@
#include "Hotkey.h"
#include "ConfigDB.h"
#include "Loader.h"
#include "LoaderThunks.h"
#include "Quaternion.h"
#include "Unit.h"
@ -94,33 +95,24 @@ void CGameView::Initialize(CGameAttributes *pAttribs)
if( ( m_ViewSnapSmoothness < 0.0f ) || ( m_ViewSnapSmoothness > 1.0f ) ) m_ViewSnapSmoothness = 0.02f;
#undef getViewParameter
InitResources();
}
struct ThunkParams
{
CGameView* const this_;
CGameAttributes* const pAttribs;
ThunkParams(CGameView* this__, CGameAttributes* pAttribs_)
: this_(this__), pAttribs(pAttribs_) {}
};
static int LoadThunk(void* param, double time_left)
{
const ThunkParams* p = (const ThunkParams*)param;
CGameView* const this_ = p->this_;
CGameAttributes* const pAttribs = p->pAttribs;
this_->Initialize(pAttribs);
delete p;
return 0;
}
void CGameView::RegisterInit(CGameAttributes *pAttribs)
{
void* param = new ThunkParams(this, pAttribs);
THROW_ERR(LDR_Register(LoadThunk, param, L"CGameView", 1000));
// CGameView init
RegMemFun1(this, &CGameView::Initialize, pAttribs, L"CGameView init", 1);
// previously done by CGameView::InitResources
RegMemFun(g_TexMan.GetSingletonPtr(), &CTextureManager::LoadTerrainTextures, L"LoadTerrainTextures", 17);
RegMemFun(g_ObjMan.GetSingletonPtr(), &CObjectManager::LoadObjects, L"LoadObjects", 1063);
RegMemFun(g_Renderer.GetSingletonPtr(), &CRenderer::LoadAlphaMaps, L"LoadAlphaMaps", 36);
}
@ -192,15 +184,6 @@ void CGameView::RenderNoCull()
}
}
void CGameView::InitResources()
{
TIMER(CGameView__InitResources);
g_TexMan.LoadTerrainTextures();
g_ObjMan.LoadObjects();
g_Renderer.LoadAlphaMaps();
}
void CGameView::UnloadResources()
{

View File

@ -15,22 +15,25 @@
#include "TextureManager.h"
#include "timer.h"
#include "Loader.h"
#include "LoaderThunks.h"
// CMapReader constructor: nothing to do at the minute
CMapReader::CMapReader()
{
}
// LoadMap: try to load the map from given file; reinitialise the scene to new data if successful
void CMapReader::LoadMap(const char* filename, CTerrain *pTerrain, CUnitManager *pUnitMan, CLightEnv *pLightEnv)
{
TIMER(__CMapReader__LoadMap);
CFileUnpacker unpacker;
{
TIMER(____CMapReader__LoadMap__read);
unpacker.Read(filename,"PSMP");
}
// LoadMap: try to load the map from given file; reinitialise the scene to new data if successful
void CMapReader::LoadMap(const char* filename, CTerrain *pTerrain_, CUnitManager *pUnitMan_, CLightEnv *pLightEnv_)
{
// latch parameters (held until DelayedLoadFinished)
pTerrain = pTerrain_;
pUnitMan = pUnitMan_;
pLightEnv = pLightEnv_;
// [25ms]
unpacker.Read(filename,"PSMP");
// check version
if (unpacker.GetVersion()<FILE_READ_VERSION) {
@ -38,34 +41,34 @@ void CMapReader::LoadMap(const char* filename, CTerrain *pTerrain, CUnitManager
}
// unpack the data
UnpackMap(unpacker);
RegMemFun(this, &CMapReader::UnpackMap, L"CMapReader::UnpackMap", 691);
// apply data to the world
ApplyData(unpacker, pTerrain, pUnitMan, pLightEnv);
RegMemFun(this, &CMapReader::ApplyData, L"CMapReader::ApplyData", 415);
if (unpacker.GetVersion()>=3) {
// read the corresponding XML file
CStr filename_xml (filename);
filename_xml = filename;
filename_xml = filename_xml.Left(filename_xml.Length()-4) + ".xml";
ReadXML(filename_xml);
RegMemFun(this, &CMapReader::ReadXML, L"CMapReader::ReadXML", 1320);
}
RegMemFun(this, &CMapReader::DelayLoadFinished, L"CMapReader::DelayLoadFinished", 3);
}
// UnpackMap: unpack the given data from the raw data stream into local variables
void CMapReader::UnpackMap(CFileUnpacker& unpacker)
void CMapReader::UnpackMap()
{
TIMER(____CMapReader__UnpackMap);
// now unpack everything into local data
UnpackTerrain(unpacker);
UnpackObjects(unpacker);
UnpackTerrain();
UnpackObjects();
if (unpacker.GetVersion()>=2) {
UnpackLightEnv(unpacker);
UnpackLightEnv();
}
}
// UnpackLightEnv: unpack lighting parameters from input stream
void CMapReader::UnpackLightEnv(CFileUnpacker& unpacker)
void CMapReader::UnpackLightEnv()
{
unpacker.UnpackRaw(&m_LightEnv.m_SunColor,sizeof(m_LightEnv.m_SunColor));
unpacker.UnpackRaw(&m_LightEnv.m_Elevation,sizeof(m_LightEnv.m_Elevation));
@ -76,7 +79,7 @@ void CMapReader::UnpackLightEnv(CFileUnpacker& unpacker)
}
// UnpackObjects: unpack world objects from input stream
void CMapReader::UnpackObjects(CFileUnpacker& unpacker)
void CMapReader::UnpackObjects()
{
// unpack object types
u32 numObjTypes;
@ -95,7 +98,7 @@ void CMapReader::UnpackObjects(CFileUnpacker& unpacker)
// UnpackTerrain: unpack the terrain from the end of the input data stream
// - data: map size, heightmap, list of textures used by map, texture tile assignments
void CMapReader::UnpackTerrain(CFileUnpacker& unpacker)
void CMapReader::UnpackTerrain()
{
// unpack map size
unpacker.UnpackRaw(&m_MapSize,sizeof(m_MapSize));
@ -132,10 +135,8 @@ void CMapReader::UnpackTerrain(CFileUnpacker& unpacker)
}
// ApplyData: take all the input data, and rebuild the scene from it
void CMapReader::ApplyData(CFileUnpacker& unpacker, CTerrain *pTerrain, CUnitManager *pUnitMan, CLightEnv *pLightEnv)
void CMapReader::ApplyData()
{
TIMER(____CMapReader__ApplyData);
// initialise the terrain
pTerrain->Initialize(m_MapSize,&m_Heightmap[0]);
@ -200,21 +201,19 @@ TIMER(____CMapReader__ApplyData);
}
void CMapReader::ReadXML(const char* filename)
void CMapReader::ReadXML()
{
TIMER(____CMapReader__ReadXML);
#ifdef SCED
// HACK: ScEd uses absolute filenames, not VFS paths. I can't be bothered
// to make Xeromyces work with non-VFS, so just cheat:
CStr filename_vfs (filename);
CStr filename_vfs (filename_xml);
filename_vfs = filename_vfs.substr(filename_vfs.ReverseFind("\\mods\\official\\") + 15);
filename_vfs.Replace("\\", "/");
filename = filename_vfs;
filename_xml = filename_vfs;
#endif
CXeromyces XeroFile;
if (XeroFile.Load(filename) != PSRETURN_OK)
if (XeroFile.Load(filename_xml) != PSRETURN_OK)
throw CFileUnpacker::CFileReadError();
// Define all the elements and attributes used in the XML file
@ -307,3 +306,10 @@ void CMapReader::ReadXML(const char* filename)
}
}
}
void CMapReader::DelayLoadFinished()
{
// we were dynamically allocated by CWorld::Initialize
delete this;
}

View File

@ -22,19 +22,22 @@ public:
private:
// UnpackMap: unpack the given data from the raw data stream into local variables
void UnpackMap(CFileUnpacker& unpacker);
void UnpackMap();
// UnpackTerrain: unpack the terrain from the input stream
void UnpackTerrain(CFileUnpacker& unpacker);
void UnpackTerrain();
// UnpackObjects: unpack world objects from the input stream
void UnpackObjects(CFileUnpacker& unpacker);
void UnpackObjects();
// UnpackObjects: unpack lighting parameters from the input stream
void UnpackLightEnv(CFileUnpacker& unpacker);
void UnpackLightEnv();
// ApplyData: take all the input data, and rebuild the scene from it
void ApplyData(CFileUnpacker& unpacker, CTerrain *pTerrain, CUnitManager *pUnitMan, CLightEnv *pLightEnv);
void ApplyData();
// ReadXML: read some other data (entities, etc) in XML format
void ReadXML(const char* filename);
void ReadXML();
// clean up everything used during delayed load
void DelayLoadFinished();
// size of map
u32 m_MapSize;
@ -50,6 +53,13 @@ private:
std::vector<SObjectDesc> m_Objects;
// lightenv stored in file
CLightEnv m_LightEnv;
// state latched by LoadMap and held until DelayedLoadFinished
CFileUnpacker unpacker;
CTerrain *pTerrain;
CUnitManager *pUnitMan;
CLightEnv *pLightEnv;
CStr filename_xml;
};
#endif

View File

@ -170,8 +170,6 @@ void CObjectManager::DeleteObjectBase(CObjectBase* base)
void CObjectManager::LoadObjects()
{
TIMER(__CObjectManager__LoadObjects);
AddObjectType("");
CStr root ("art/actors");

View File

@ -137,8 +137,6 @@ void CTextureManager::BuildTerrainTypes()
void CTextureManager::LoadTerrainTextures()
{
TIMER(__CTextureManager__LoadTerrainTextures);
// find all the terrain types by directory name
BuildTerrainTypes();

View File

@ -955,7 +955,8 @@ static int ProgressiveLoad()
// no load active => no-op (skip code below)
case 1:
return 1;
// current task isn't complete (we don't care about this distinction)
// current task didn't complete. we only care about this insofar as the
// load process is therefore not yet finished.
case ERR_TIMED_OUT:
break;
// just finished loading
@ -1161,15 +1162,6 @@ sle(11340106);
if(oglHaveExtension("WGL_EXT_swap_control"))
wglSwapIntervalEXT(g_VSync? 1 : 0);
#ifdef _MSC_VER
u64 CURTSC=rdtsc();
debug_out(
"----------------------------------------\n"\
"low-level ready (elapsed = %f ms)\n"\
"----------------------------------------\n", (CURTSC-PREVTSC)/2e9*1e3);
PREVTSC=CURTSC;
#endif
MICROLOG(L"init ps");
InitPs();
@ -1256,16 +1248,11 @@ TIMER(init_after_InitRenderer);
g_Console->RegisterFunc(Testing, L"Testing");
#ifdef _MSC_VER
{
u64 CURTSC=rdtsc();
debug_out(
"----------------------------------------\n"\
"READY (elapsed = %f ms)\n"\
"----------------------------------------\n", (CURTSC-PREVTSC)/2e9*1e3);
PREVTSC=CURTSC;
}
#endif
"----------------------------------------\n"
);

View File

@ -22,13 +22,19 @@ static int progress_percent = 0;
// set by LDR_EndRegistering; used for progress % calculation. may be 0.
static double total_estimated_duration;
// used by LDR_ProgressiveLoad to add up the duration of requests that
// time out by themselves (and therefore may be split across multiple calls)
static double request_duration;
// main purpose is to indicate whether a load is in progress, so that
// LDR_ProgressiveLoad can return 0 iff loading just completed.
// the REGISTERING state allows us to detect 2 simultaneous loads (bogus).
// the REGISTERING state allows us to detect 2 simultaneous loads (bogus);
// FIRST_LOAD is used to skip the first timeslice (see LDR_ProgressiveLoad).
static enum
{
IDLE,
REGISTERING,
FIRST_LOAD,
LOADING,
}
state = IDLE;
@ -122,9 +128,10 @@ int LDR_EndRegistering()
if(load_requests.empty())
debug_warn("LDR_EndRegistering: no LoadRequests queued");
state = LOADING;
state = FIRST_LOAD;
progress_percent = 0;
total_estimated_duration = std::accumulate(load_requests.begin(), load_requests.end(), 0.0, DurationAdder());
request_duration = 0.0;
return 0;
}
@ -191,21 +198,36 @@ static bool HaveTimeForNextTask(double time_left, double time_budget, int estima
int LDR_ProgressiveLoad(double time_budget, wchar_t* description_,
size_t max_chars, int* progress_percent_)
{
int ret; // single exit; this is returned
double time_left = time_budget;
// don't do any work the first call so that a graphics update
// happens before the first (probably lengthy) timeslice.
if(state == FIRST_LOAD)
{
state = LOADING;
ret = ERR_TIMED_OUT; // make caller think we did something
goto done;
}
// we're called unconditionally from the main loop, so this isn't
// an error; there is just nothing to do.
if(state != LOADING)
return 1;
const double end_time = get_time() + time_budget;
int ret; // single exit; this is returned
while(!load_requests.empty())
{
double time_left = end_time - get_time();
// do actual work of loading
const LoadRequest& lr = load_requests.front();
const double t0 = get_time();
ret = lr.func(lr.param, time_left);
const double elapsed_time = get_time() - t0;
// time accounting
time_left -= elapsed_time;
request_duration += elapsed_time;
wcscpy_s(description_, max_chars, lr.description); // HACK, used below
// .. either finished entirely, or failed => remove from queue
if(ret != ERR_TIMED_OUT)
load_requests.pop_front();
@ -224,12 +246,13 @@ int LDR_ProgressiveLoad(double time_budget, wchar_t* description_,
progress_percent += (int)(fraction * 100.0);
assert(0 <= progress_percent && progress_percent <= 100);
}
debug_out("LOADER: completed %ls in %f ms\n", description_, elapsed_time*1e3);
request_duration = 0.0;
// check if we're out of time; take into account next task length.
// note: do this at the end of the loop to make sure there's
// progress even if the timer is low-resolution (=> time_left = 0).
time_left = end_time - get_time();
if(!HaveTimeForNextTask(time_left, time_budget, lr.estimated_duration_ms))
// if(!HaveTimeForNextTask(time_left, time_budget, lr.estimated_duration_ms))
{
ret = ERR_TIMED_OUT;
goto done;
@ -240,6 +263,7 @@ int LDR_ProgressiveLoad(double time_budget, wchar_t* description_,
state = IDLE;
ret = 0;
// set output params (there are several return points above)
done:
*progress_percent_ = progress_percent;

51
source/ps/LoaderThunks.h Normal file
View File

@ -0,0 +1,51 @@
template<class T> struct MemFun_t
{
T* const this_;
void(T::*func)(void);
MemFun_t(T* this__, void(T::*func_)(void))
: this_(this__), func(func_) {}
};
template<class T> static int MemFunThunk(void* param, double time_left)
{
MemFun_t<T>* const mf = (MemFun_t<T>*)param;
(mf->this_->*mf->func)();
delete mf;
return 0;
}
template<class T> void RegMemFun(T* this_, void(T::*func)(void),
const wchar_t* description, int estimated_duration_ms)
{
void* param = new MemFun_t<T>(this_, func);
THROW_ERR(LDR_Register(MemFunThunk<T>, param, description, estimated_duration_ms));
}
////////////////////////////////////////////////////////
template<class T, class Arg> struct MemFun1_t
{
T* const this_;
Arg arg;
void(T::*func)(Arg);
MemFun1_t(T* this__, void(T::*func_)(Arg), Arg arg_)
: this_(this__), func(func_), arg(arg_) {}
};
template<class T, class Arg> static int MemFun1Thunk(void* param, double time_left)
{
MemFun1_t<T, Arg>* const mf = (MemFun1_t<T, Arg>*)param;
(mf->this_->*mf->func)(mf->arg);
delete mf;
return 0;
}
template<class T, class Arg> void RegMemFun1(T* this_, void(T::*func)(Arg), Arg arg,
const wchar_t* description, int estimated_duration_ms)
{
void* param = new MemFun1_t<T, Arg>(this_, func, arg);
THROW_ERR(LDR_Register(MemFun1Thunk<T, Arg>, param, description, estimated_duration_ms));
}

View File

@ -13,6 +13,7 @@
#include "EntityManager.h"
#include "timer.h"
#include "Loader.h"
#include "LoaderThunks.h"
#define LOG_CATEGORY "world"
@ -20,10 +21,8 @@ CLightEnv g_LightEnv;
void CWorld::Initialize(CGameAttributes *pAttribs)
{
TIMER(CWorld__Initialize);
// TODO: Find a better way of handling these global things
ONCE(g_EntityTemplateCollection.loadTemplates());
ONCE(RegMemFun(CBaseEntityCollection::GetSingletonPtr(), &CBaseEntityCollection::loadTemplates, L"LoadTemplates", 960));
// Load the map, if one was specified
if (pAttribs->m_MapFile.Length())
@ -32,39 +31,23 @@ void CWorld::Initialize(CGameAttributes *pAttribs)
mapfilename += (CStr)pAttribs->m_MapFile;
CMapReader* reader = 0;
try {
CMapReader reader;
reader.LoadMap(mapfilename, &m_Terrain, &m_UnitManager, &g_LightEnv);
reader = new CMapReader;
reader->LoadMap(mapfilename, &m_Terrain, &m_UnitManager, &g_LightEnv);
// fails immediately, or registers for delay loading
} catch (...) {
delete reader;
LOG(ERROR, LOG_CATEGORY, "Failed to load map %s", mapfilename.c_str());
throw PSERROR_Game_World_MapLoadFailed();
}
}
}
struct ThunkParams
{
CWorld* const this_;
CGameAttributes* const pAttribs;
ThunkParams(CWorld* this__, CGameAttributes* pAttribs_)
: this_(this__), pAttribs(pAttribs_) {}
};
static int LoadThunk(void* param, double time_left)
{
const ThunkParams* p = (const ThunkParams*)param;
CWorld* const this_ = p->this_;
CGameAttributes* const pAttribs = p->pAttribs;
this_->Initialize(pAttribs);
delete p;
return 0;
}
void CWorld::RegisterInit(CGameAttributes *pAttribs)
{
void* param = new ThunkParams(this, pAttribs);
THROW_ERR(LDR_Register(LoadThunk, param, L"CWorld", 1000));
Initialize(pAttribs);
}

View File

@ -1153,10 +1153,8 @@ inline void CopyTriple(unsigned char* dst,const unsigned char* src)
// LoadAlphaMaps: load the 14 default alpha maps, pack them into one composite texture and
// calculate the coordinate of each alphamap within this packed texture .. need to add
// validation that all maps are the same size
bool CRenderer::LoadAlphaMaps()
void CRenderer::LoadAlphaMaps()
{
TIMER(__CRenderer__LoadAlphaMaps);
Handle textures[NumAlphaMaps];
@ -1183,7 +1181,7 @@ bool CRenderer::LoadAlphaMaps()
for (i=0;i<NumAlphaMaps;i++) {
textures[i]=tex_load(fnames[i]);
if (textures[i] <= 0) {
return false;
throw textures[i]; // FIXTHROW
}
}
@ -1253,8 +1251,6 @@ bool CRenderer::LoadAlphaMaps()
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
delete[] data;
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -207,7 +207,7 @@ public:
bool IsTextureTransparent(CTexture* texture);
// load the default set of alphamaps; return false if any alphamap fails to load, true otherwise
bool LoadAlphaMaps();
void LoadAlphaMaps();
void UnloadAlphaMaps();

View File

@ -13,6 +13,7 @@
#include "Unit.h"
#include "Model.h"
#include "Loader.h"
#include "LoaderThunks.h"
#include "gui/CGUI.h"
@ -39,29 +40,9 @@ void CSimulation::Initialize(CGameAttributes *pAttribs)
}
struct ThunkParams
{
CSimulation* const this_;
CGameAttributes* const pAttribs;
ThunkParams(CSimulation* this__, CGameAttributes* pAttribs_)
: this_(this__), pAttribs(pAttribs_) {}
};
static int LoadThunk(void* param, double time_left)
{
const ThunkParams* p = (const ThunkParams*)param;
CSimulation* const this_ = p->this_;
CGameAttributes* const pAttribs = p->pAttribs;
this_->Initialize(pAttribs);
delete p;
return 0;
}
void CSimulation::RegisterInit(CGameAttributes *pAttribs)
{
void* param = new ThunkParams(this, pAttribs);
THROW_ERR(LDR_Register(LoadThunk, param, L"CSimulation", 1000));
RegMemFun1(this, &CSimulation::Initialize, pAttribs, L"CSimulation", 31);
}