diff --git a/source/graphics/GameView.cpp b/source/graphics/GameView.cpp index e1974272e3..bc664eec49 100755 --- a/source/graphics/GameView.cpp +++ b/source/graphics/GameView.cpp @@ -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() { diff --git a/source/graphics/MapReader.cpp b/source/graphics/MapReader.cpp index 59f1a50aaf..5e1f7796e3 100755 --- a/source/graphics/MapReader.cpp +++ b/source/graphics/MapReader.cpp @@ -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()=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; +} \ No newline at end of file diff --git a/source/graphics/MapReader.h b/source/graphics/MapReader.h index 72ee6a3205..1a3a750208 100755 --- a/source/graphics/MapReader.h +++ b/source/graphics/MapReader.h @@ -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 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 diff --git a/source/graphics/ObjectManager.cpp b/source/graphics/ObjectManager.cpp index 73483dab8c..c2f6c02b3c 100755 --- a/source/graphics/ObjectManager.cpp +++ b/source/graphics/ObjectManager.cpp @@ -170,8 +170,6 @@ void CObjectManager::DeleteObjectBase(CObjectBase* base) void CObjectManager::LoadObjects() { - TIMER(__CObjectManager__LoadObjects); - AddObjectType(""); CStr root ("art/actors"); diff --git a/source/graphics/TextureManager.cpp b/source/graphics/TextureManager.cpp index 66633751bf..4b2f4b10a0 100755 --- a/source/graphics/TextureManager.cpp +++ b/source/graphics/TextureManager.cpp @@ -137,8 +137,6 @@ void CTextureManager::BuildTerrainTypes() void CTextureManager::LoadTerrainTextures() { - TIMER(__CTextureManager__LoadTerrainTextures); - // find all the terrain types by directory name BuildTerrainTypes(); diff --git a/source/main.cpp b/source/main.cpp index 3be191f358..25619ab757 100755 --- a/source/main.cpp +++ b/source/main.cpp @@ -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" +); diff --git a/source/ps/Loader.cpp b/source/ps/Loader.cpp index 35ce397241..ef73c285b5 100644 --- a/source/ps/Loader.cpp +++ b/source/ps/Loader.cpp @@ -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; diff --git a/source/ps/LoaderThunks.h b/source/ps/LoaderThunks.h new file mode 100644 index 0000000000..985b21e79a --- /dev/null +++ b/source/ps/LoaderThunks.h @@ -0,0 +1,51 @@ + +template struct MemFun_t +{ + T* const this_; + void(T::*func)(void); + MemFun_t(T* this__, void(T::*func_)(void)) + : this_(this__), func(func_) {} +}; + +template static int MemFunThunk(void* param, double time_left) +{ + MemFun_t* const mf = (MemFun_t*)param; + (mf->this_->*mf->func)(); + delete mf; + return 0; +} + +template void RegMemFun(T* this_, void(T::*func)(void), + const wchar_t* description, int estimated_duration_ms) +{ + void* param = new MemFun_t(this_, func); + THROW_ERR(LDR_Register(MemFunThunk, param, description, estimated_duration_ms)); +} + + +//////////////////////////////////////////////////////// + + +template 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 static int MemFun1Thunk(void* param, double time_left) +{ + MemFun1_t* const mf = (MemFun1_t*)param; + (mf->this_->*mf->func)(mf->arg); + delete mf; + return 0; +} + +template void RegMemFun1(T* this_, void(T::*func)(Arg), Arg arg, + const wchar_t* description, int estimated_duration_ms) +{ + void* param = new MemFun1_t(this_, func, arg); + THROW_ERR(LDR_Register(MemFun1Thunk, param, description, estimated_duration_ms)); +} \ No newline at end of file diff --git a/source/ps/World.cpp b/source/ps/World.cpp index 9eac6a1883..1acc921af0 100755 --- a/source/ps/World.cpp +++ b/source/ps/World.cpp @@ -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); } diff --git a/source/renderer/Renderer.cpp b/source/renderer/Renderer.cpp index 3bcbe623ea..84ce21e2d4 100755 --- a/source/renderer/Renderer.cpp +++ b/source/renderer/Renderer.cpp @@ -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;ithis_; - 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); }