From e416bdbbfe2f571f91a88d3e001187ddad234296 Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Fri, 5 Jan 2024 01:22:55 +0100 Subject: [PATCH] DataCache: add proper fallback mdl handler and fix rendering Add a proper class for dealing with fallback models and the warnings thereof. Also reverted the 'old_gather_props' removal, as otherwise fallback models won't draw (new gather props solution doesn't call CMDLCache::GetHardwareData()). --- src/datacache/mdlcache.cpp | 122 ++++++++++++++++++++++--------------- src/datacache/mdlcache.h | 110 ++++++++++++++++++++++++++------- src/engine/cmodel_bsp.cpp | 15 ++++- 3 files changed, 176 insertions(+), 71 deletions(-) diff --git a/src/datacache/mdlcache.cpp b/src/datacache/mdlcache.cpp index c94ea833..a436406c 100644 --- a/src/datacache/mdlcache.cpp +++ b/src/datacache/mdlcache.cpp @@ -15,9 +15,8 @@ #include "rtech/rtech_utils.h" #include "public/studio.h" -RMDLFallBack_t* g_pMDLFallback = new RMDLFallBack_t(); -std::unordered_set g_vBadMDLHandles; - +CStudioFallbackHandler g_StudioMdlFallbackHandler; +#define IS_VALID_DATACACHE_HANDLE(cacheHandle) (cacheHandle && cacheHandle != DC_INVALID_HANDLE) //----------------------------------------------------------------------------- // Purpose: finds an MDL @@ -48,21 +47,18 @@ studiohdr_t* CMDLCache::FindMDL(CMDLCache* const cache, const MDLHandle_t handle studiocache_t* studioCache = pStudioData->GetStudioCache(); // Store error and empty fallback models. - if (studioCache && studioCache != DC_INVALID_HANDLE) + if (IS_VALID_DATACACHE_HANDLE(studioCache)) { studiohdr_t* const pStudioHDR = pStudioData->GetStudioCache()->GetStudioHdr(); if (pStudioHDR) { - if (!g_pMDLFallback->m_hErrorMDL && V_ComparePath(pStudioHDR->name, ERROR_MODEL)) + // Typically, you would only check for '(m_nFlags & STUDIODATA_ERROR_MODEL)', + // but for some reason this game doesn't have this flag set on that model. + if (!HasErrorModel() && ((pStudioData->m_nFlags & STUDIODATA_ERROR_MODEL) + || V_ComparePath(pStudioHDR->name, ERROR_MODEL))) { - g_pMDLFallback->m_pErrorHDR = pStudioHDR; - g_pMDLFallback->m_hErrorMDL = handle; - } - else if (!g_pMDLFallback->m_hEmptyMDL && V_ComparePath(pStudioHDR->name, EMPTY_MODEL)) - { - g_pMDLFallback->m_pEmptyHDR = pStudioHDR; - g_pMDLFallback->m_hEmptyMDL = handle; + g_StudioMdlFallbackHandler.SetFallbackModel(pStudioHDR, handle); } } } @@ -220,13 +216,7 @@ studiohdr_t* CMDLCache::FindUncachedMDL(CMDLCache* const cache, const MDLHandle_ return pStudioHdr; } -//----------------------------------------------------------------------------- -// Purpose: gets the studio physics cache from cache pool by handle -// Input : *this - -// handle - -// Output : a pointer to the studiophysicsref_t object -//----------------------------------------------------------------------------- -studiophysicsref_t* CMDLCache::GetStudioPhysicsCache(const MDLHandle_t handle) +studiocache_t* CMDLCache::GetStudioCache(const MDLHandle_t handle) { if (handle == MDLHANDLE_INVALID) return nullptr; @@ -234,20 +224,10 @@ studiophysicsref_t* CMDLCache::GetStudioPhysicsCache(const MDLHandle_t handle) const studiodata_t* const studioData = GetStudioData(handle); if (!studioData) - { - Warning(eDLL_T::ENGINE, "Attempted to load studio physics ref on model \"%s\" with no studio data!\n", GetModelName(handle)); return nullptr; - } - const studiocache_t* const studioCache = studioData->GetStudioCache(); - - if (!studioCache || studioCache == DC_INVALID_HANDLE) - { - Warning(eDLL_T::ENGINE, "Attempted to load studio physics ref on model \"%s\" with invalid studio cache!\n", GetModelName(handle)); - return nullptr; - } - - return studioCache->GetPhysicsCache(); + studiocache_t* const studioCache = studioData->GetStudioCache(); + return studioCache; } //----------------------------------------------------------------------------- @@ -258,7 +238,15 @@ studiophysicsref_t* CMDLCache::GetStudioPhysicsCache(const MDLHandle_t handle) //----------------------------------------------------------------------------- vcollide_t* CMDLCache::GetVCollide(CMDLCache* const cache, const MDLHandle_t handle) { - studiophysicsref_t* const physicsCache = cache->GetStudioPhysicsCache(handle); + studiocache_t* const studioCache = cache->GetStudioCache(handle); + + if (!IS_VALID_DATACACHE_HANDLE(studioCache)) + { + Warning(eDLL_T::ENGINE, "Attempted to load vcollide on model \"%s\" with invalid studio data!\n", cache->GetModelName(handle)); + return nullptr; + } + + studiophysicsref_t* const physicsCache = studioCache->GetPhysicsCache(); if (!physicsCache) return nullptr; @@ -279,7 +267,15 @@ vcollide_t* CMDLCache::GetVCollide(CMDLCache* const cache, const MDLHandle_t han //----------------------------------------------------------------------------- void* CMDLCache::GetPhysicsGeometry(CMDLCache* const cache, const MDLHandle_t handle) { - studiophysicsref_t* const physicsCache = cache->GetStudioPhysicsCache(handle); + studiocache_t* const studioCache = cache->GetStudioCache(handle); + + if (!IS_VALID_DATACACHE_HANDLE(studioCache)) + { + Warning(eDLL_T::ENGINE, "Attempted to load physics geometry on model \"%s\" with invalid studio data!\n", cache->GetModelName(handle)); + return nullptr; + } + + studiophysicsref_t* const physicsCache = studioCache->GetPhysicsCache(); if (!physicsCache) return nullptr; @@ -300,24 +296,38 @@ void* CMDLCache::GetPhysicsGeometry(CMDLCache* const cache, const MDLHandle_t ha //----------------------------------------------------------------------------- studiohwdata_t* CMDLCache::GetHardwareData(CMDLCache* const cache, const MDLHandle_t handle) { - const studiodata_t* pStudioData = cache->GetStudioData(handle); + const studiodata_t* pStudioData = nullptr; cache->GetStudioData(handle); + const studiocache_t* studioCache = cache->GetStudioCache(handle); - if (!pStudioData) + if (!IS_VALID_DATACACHE_HANDLE(studioCache)) { - if (!g_pMDLFallback->m_hErrorMDL) + if (!HasErrorModel()) { - Error(eDLL_T::ENGINE, EXIT_FAILURE, "Studio hardware with handle \"%hu\" not found and \"%s\" couldn't be loaded.\n", handle, ERROR_MODEL); + Error(eDLL_T::ENGINE, NO_ERROR, "Studio hardware for model \"%s\" not found and \"%s\" couldn't be loaded!\n", + cache->GetModelName(handle), ERROR_MODEL); + + assert(0); // Should never be hit! return nullptr; } - pStudioData = cache->GetStudioData(g_pMDLFallback->m_hErrorMDL); + else + { + // Only spew the message once. + if (g_StudioMdlFallbackHandler.AddToSuppressionList(handle)) + { + Warning(eDLL_T::ENGINE, "Studio hardware for model \"%s\" not found; replacing with \"%s\".\n", + cache->GetModelName(handle), ERROR_MODEL); + } + } + + pStudioData = cache->GetStudioData(GetErrorModelHandle()); + studioCache = pStudioData->GetStudioCache(); + } + else + { + pStudioData = cache->GetStudioData(handle); } - const studiocache_t* const dataCache = pStudioData->GetStudioCache(); - - if (!dataCache || dataCache == DC_INVALID_HANDLE) - return nullptr; - - studiophysicsref_t* const physicsCache = dataCache->GetPhysicsCache(); + studiophysicsref_t* const physicsCache = studioCache->GetPhysicsCache(); AcquireSRWLockExclusive(g_pMDLLock); CMDLCache__CheckData(physicsCache, 1i64); // !!! DECLARED INLINE IN < S3 !!! @@ -334,11 +344,27 @@ studiohwdata_t* CMDLCache::GetHardwareData(CMDLCache* const cache, const MDLHand //----------------------------------------------------------------------------- studiohdr_t* CMDLCache::GetErrorModel(void) { - return g_pMDLFallback->m_pErrorHDR; + // NOTE: must enable the old gather props logic for fallback models to draw !!! + // The new one won't call GetHardwareData on bad model handles. + // TODO[ AMOS ]: move this elsewhere; correct place is GatherStaticPropsSecondPass(). + g_StudioMdlFallbackHandler.EnableLegacyGatherProps(); + + return g_StudioMdlFallbackHandler.GetFallbackModelHeader(); +} +const char* CMDLCache::GetErrorModelName(void) +{ + const studiohdr_t* const errorStudioHdr = g_StudioMdlFallbackHandler.GetFallbackModelHeader(); + assert(errorStudioHdr); + + return errorStudioHdr ? errorStudioHdr->name : "(invalid)"; } MDLHandle_t CMDLCache::GetErrorModelHandle(void) { - return g_pMDLFallback->m_hErrorMDL; + return g_StudioMdlFallbackHandler.GetFallbackModelHandle(); +} +bool CMDLCache::HasErrorModel(void) +{ + return g_StudioMdlFallbackHandler.HasFallbackModel(); } //----------------------------------------------------------------------------- @@ -348,8 +374,8 @@ MDLHandle_t CMDLCache::GetErrorModelHandle(void) //----------------------------------------------------------------------------- bool CMDLCache::IsKnownBadModel(const MDLHandle_t handle) { - auto p = g_vBadMDLHandles.insert(handle); - return !p.second; + // Only adds if it didn't exist yet, else it returns false. + return g_StudioMdlFallbackHandler.AddBadModelHandle(handle); } void VMDLCache::Detour(const bool bAttach) const diff --git a/src/datacache/mdlcache.h b/src/datacache/mdlcache.h index b490f23c..14df1352 100644 --- a/src/datacache/mdlcache.h +++ b/src/datacache/mdlcache.h @@ -9,29 +9,95 @@ #include "public/vphysics/vcollide.h" #include "public/rtech/ipakfile.h" -struct RMDLFallBack_t +class CStudioFallbackHandler { - studiohdr_t* m_pErrorHDR; - studiohdr_t* m_pEmptyHDR; - MDLHandle_t m_hErrorMDL; - MDLHandle_t m_hEmptyMDL; - - RMDLFallBack_t(void) - : m_pErrorHDR(nullptr) - , m_pEmptyHDR(nullptr) - , m_hErrorMDL(NULL) - , m_hEmptyMDL(NULL) - { - } +public: + CStudioFallbackHandler(void) + : m_pFallbackHDR(nullptr) + , m_hFallbackMDL(NULL) + {} // This must be cleared if 'common.rpak' is getting unloaded! - void Clear(void) + inline void Clear(void) { - m_pErrorHDR = nullptr; - m_pEmptyHDR = nullptr; - m_hErrorMDL = NULL; - m_hEmptyMDL = NULL; + m_pFallbackHDR = nullptr; + m_hFallbackMDL = NULL; + + m_BadMdlHandles.clear(); } + + inline bool HasFallbackModel() const + { + return !!m_hFallbackMDL; + } + + inline void SetFallbackModel(studiohdr_t* const studioHdr, const MDLHandle_t handle) + { + m_pFallbackHDR = studioHdr; + m_hFallbackMDL = handle; + } + + inline studiohdr_t* GetFallbackModelHeader() const + { + return m_pFallbackHDR; + } + + inline MDLHandle_t GetFallbackModelHandle() const + { + return m_hFallbackMDL; + } + + inline void EnableLegacyGatherProps() + { + if (!old_gather_props->GetBool()) + old_gather_props->SetValue(true); + } + + inline void DisableLegacyGatherProps() + { + if (old_gather_props->GetBool()) + old_gather_props->SetValue(false); + } + + inline bool AddBadModelHandle(const MDLHandle_t handle) + { + auto p = m_BadMdlHandles.insert(handle); + return !p.second; + } + + inline void ClearBadModelHandleCache() + { + m_BadMdlHandles.clear(); + } + + inline bool HasInvalidModelHandles() + { + return !m_BadMdlHandles.empty(); + } + + inline bool AddToSuppressionList(const MDLHandle_t handle) + { + auto p = m_SuppressedHandles.insert(handle); + return p.second; + } + + inline void ClearSuppresionList() + { + m_SuppressedHandles.clear(); + } + +private: + studiohdr_t* m_pFallbackHDR; + MDLHandle_t m_hFallbackMDL; + + // Keep track of bad model handles so we don't log the + // same one twice or more to the console and cause a + // significant performance impact. + std::unordered_set m_BadMdlHandles; + + // Don't spam on these handles when trying to get + // hardware data. + std::unordered_set m_SuppressedHandles; }; @@ -123,8 +189,7 @@ struct studiodata_t PakHandle_t m_PakHandle; }; -extern RMDLFallBack_t* g_pMDLFallback; -extern std::unordered_set g_vBadMDLHandles; +extern CStudioFallbackHandler g_StudioMdlFallbackHandler; class CMDLCache : public CTier1AppSystem { @@ -133,15 +198,16 @@ public: static void FindCachedMDL(CMDLCache* const cache, studiodata_t* const pStudioData, void* a3); static studiohdr_t* FindUncachedMDL(CMDLCache* const cache, const MDLHandle_t handle, studiodata_t* const pStudioData, void* a4); - studiophysicsref_t* GetStudioPhysicsCache(const MDLHandle_t handle); - + studiocache_t* GetStudioCache(const MDLHandle_t handle); static vcollide_t* GetVCollide(CMDLCache* const cache, const MDLHandle_t handle); static void* GetPhysicsGeometry(CMDLCache* const cache, const MDLHandle_t handle); static studiohwdata_t* GetHardwareData(CMDLCache* const cache, const MDLHandle_t handle); static studiohdr_t* GetErrorModel(void); + static const char* GetErrorModelName(void); static MDLHandle_t GetErrorModelHandle(void); + static bool HasErrorModel(void); static bool IsKnownBadModel(const MDLHandle_t handle); diff --git a/src/engine/cmodel_bsp.cpp b/src/engine/cmodel_bsp.cpp index 9422d114..7476e566 100644 --- a/src/engine/cmodel_bsp.cpp +++ b/src/engine/cmodel_bsp.cpp @@ -266,6 +266,17 @@ void Mod_ProcessPakQueue() #endif // !DEDICATED } + // The old gather props is set if a model couldn't be + // loaded properly. If we unload level assets, we just + // enable the new implementation again and re-evaluate + // on the next level load. If we load a missing/bad + // model again, we toggle the old implementation as + // otherwise the fallback models won't render; the new + // gather props solution does not attempt to obtain + // studio hardware data on bad mdl handles. See + // 'CMDLCache::GetErrorModel' for more information. + g_StudioMdlFallbackHandler.DisableLegacyGatherProps(); + g_pakLoadApi->UnloadPak(*(PakHandle_t*)v10); Mod_UnloadPakFile(); // Unload mod pak files. @@ -461,7 +472,9 @@ void Mod_UnloadPakFile(void) } } g_vLoadedPakHandle.Purge(); - g_vBadMDLHandles.clear(); + + g_StudioMdlFallbackHandler.ClearBadModelHandleCache(); + g_StudioMdlFallbackHandler.ClearSuppresionList(); } void VModel_BSP::Detour(const bool bAttach) const