diff --git a/src/Modules/MaterialSystem.cpp b/src/Modules/MaterialSystem.cpp index daf7275c..d105ceff 100644 --- a/src/Modules/MaterialSystem.cpp +++ b/src/Modules/MaterialSystem.cpp @@ -45,6 +45,7 @@ class MemTexRegen : public ITextureRegenerator { }; REDECL(MaterialSystem::UncacheUnusedMaterials); +REDECL(MaterialSystem::CacheUsedMaterials); REDECL(MaterialSystem::CreateMaterial); DETOUR(MaterialSystem::UncacheUnusedMaterials, bool bRecomputeStateSnapshots) { @@ -56,6 +57,22 @@ DETOUR(MaterialSystem::UncacheUnusedMaterials, bool bRecomputeStateSnapshots) { return result; } +// CacheUsedMaterials ends in CTextureManager::ForceAllTexturesIntoHardware, +// which queues a draw of every listed texture without referencing it. When an +// entity released its last reference to a texture earlier in the frame (the +// client runs this after every full update, which recreates all entities), the +// render thread frees that texture before it gets to the preload draw, which +// then crashes. `record` right after a load hits this on maps with a playing +// movie screen (mp_coop_wall_5). Turning threading off for the call runs the +// pending queue first, like the engine does around screenshots. +DETOUR(MaterialSystem::CacheUsedMaterials) { + auto AllowThreading = materialSystem->materials->Original<_AllowThreading>(Offsets::AllowThreading); + bool allowed = AllowThreading(thisptr, false, 0); + auto result = MaterialSystem::CacheUsedMaterials(thisptr); + AllowThreading(thisptr, allowed, 0); + return result; +} + DETOUR_T(IMaterial *, MaterialSystem::CreateMaterial, const char *pMaterialName, void *pVMTKeyValues) { if (sar.game->Is(SourceGame_Portal2 | SourceGame_Portal2_2011 | SourceGame_PortalReloaded | SourceGame_PortalStoriesMel)) { // Patched by Valve, no need for us to do it again @@ -88,6 +105,9 @@ bool MaterialSystem::Init() { if (this->materials) { this->materials->Hook(MaterialSystem::UncacheUnusedMaterials_Hook, MaterialSystem::UncacheUnusedMaterials, Offsets::UncacheUnusedMaterials); this->materials->Hook(MaterialSystem::CreateMaterial_Hook, MaterialSystem::CreateMaterial, Offsets::CreateMaterial); + if (Offsets::CacheUsedMaterials) { + this->materials->Hook(MaterialSystem::CacheUsedMaterials_Hook, MaterialSystem::CacheUsedMaterials, Offsets::CacheUsedMaterials); + } this->RemoveMaterial = this->materials->Original<_RemoveMaterial>(Offsets::RemoveMaterial); diff --git a/src/Modules/MaterialSystem.hpp b/src/Modules/MaterialSystem.hpp index 7075e810..baea3d99 100644 --- a/src/Modules/MaterialSystem.hpp +++ b/src/Modules/MaterialSystem.hpp @@ -26,8 +26,11 @@ class MaterialSystem : public Module { using _KeyValues_SetString = void(__rescall*)(void* thisptr, const char *key, const char *val); _KeyValues_SetString KeyValues_SetString = nullptr; + using _AllowThreading = bool(__rescall *)(void *thisptr, bool bAllow, int nServiceThread); + public: DECL_DETOUR(UncacheUnusedMaterials, bool bRecomputeStateSnapshots); + DECL_DETOUR(CacheUsedMaterials); DECL_DETOUR_T(IMaterial *, CreateMaterial, const char *pMaterialName, void *pVMTKeyValues); bool Init() override; diff --git a/src/Offsets/Portal 2 10090.hpp b/src/Offsets/Portal 2 10090.hpp index 4f99a7ed..da96dc59 100644 --- a/src/Offsets/Portal 2 10090.hpp +++ b/src/Offsets/Portal 2 10090.hpp @@ -344,6 +344,8 @@ OFFSET_DEFAULT(snd_p, 98, 116) OFFSET_DEFAULT(snd_vol, 108, 122) OFFSET_DEFAULT(StartTouch, 102, 103) OFFSET_DEFAULT(UncacheUnusedMaterials, 77, 77) +OFFSET_DEFAULT(CacheUsedMaterials, 78, 78) // client -> "LevelInitPreEntity - CacheUsedMaterials - Start" xref -> CPrecacheRegister::LevelInitPreEntity() -> following vtable call -> CMaterialSystem::CacheUsedMaterials +OFFSET_DEFAULT(AllowThreading, 135, 135) // materialsystem -> "Queued Material System: ENABLED!\n" xref -> CMaterialSystem::AllowThreading -> vtable index OFFSET_DEFAULT(CreateMaterial, 81, 81) OFFSET_DEFAULT(FindMaterial, 82, 82) OFFSET_DEFAULT(CreateProceduralTexture, 91, 91) diff --git a/src/Offsets/Portal 2 8151.hpp b/src/Offsets/Portal 2 8151.hpp index a7d5c605..75b95597 100644 --- a/src/Offsets/Portal 2 8151.hpp +++ b/src/Offsets/Portal 2 8151.hpp @@ -1,6 +1,9 @@ #include "Offsets/Portal 2 10090.hpp" // Portal Stories: Mel, Reloaded +OFFSET_DEFAULT(CacheUsedMaterials, 0, 0) // texture preload fix only verified on Portal 2 10090 +OFFSET_DEFAULT(AllowThreading, 0, 0) + OFFSET_LINUX(tickcount, 74) OFFSET_LINUX(interval_per_tick, 82) OFFSET_LINUX(demoplayer, 92)