From 7dbbdc480eed4ff44ce43346bcf8a6f8c2364d54 Mon Sep 17 00:00:00 2001 From: DubbleClick Date: Tue, 5 Dec 2023 16:11:44 +0100 Subject: [PATCH] fix potentially wrong texture loading order (#20) * add gsl library to track memory allocations * rework creation to respect mod order slight slowdown but just to be safe * 1.6.1 * print info on timing * specify largeaddressaware (shouldn't matter since gw is linked with it, but can't hurt) --- CMakeLists.txt | 5 ++- cmake/msgsl.cmake | 11 +++++++ header/Defines.h | 12 +++++++ header/Main.h | 1 + modules/TextureClient.ixx | 66 +++++++++++++++++++++------------------ 5 files changed, 64 insertions(+), 31 deletions(-) create mode 100644 cmake/msgsl.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 1315e5d..7999780 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,7 @@ endif() set(VERSION_MAJOR 1) set(VERSION_MINOR 6) -set(VERSION_PATCH 0) +set(VERSION_PATCH 1) set(VERSION_TWEAK 0) set(VERSION_RC "${CMAKE_CURRENT_BINARY_DIR}/version.rc") @@ -35,6 +35,7 @@ list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") include(libzippp) include(minhook) include(dxtk) +include(msgsl) add_library(gMod SHARED) @@ -58,9 +59,11 @@ target_link_libraries(gMod PRIVATE psapi minhook directxtex + Microsoft.GSL::GSL ) target_link_options(gMod PRIVATE "$<$:/NODEFAULTLIB:LIBCMT>" + "/LARGEADDRESSAWARE" ) source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES ${SOURCES}) target_sources(gMod PRIVATE ${SOURCES}) diff --git a/cmake/msgsl.cmake b/cmake/msgsl.cmake new file mode 100644 index 0000000..1455239 --- /dev/null +++ b/cmake/msgsl.cmake @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.14) + +include(FetchContent) + +FetchContent_Declare(GSL + GIT_REPOSITORY "https://github.com/microsoft/GSL" + GIT_TAG "v4.0.0" + GIT_SHALLOW ON +) + +FetchContent_MakeAvailable(GSL) diff --git a/header/Defines.h b/header/Defines.h index 16fafa0..4d5867b 100644 --- a/header/Defines.h +++ b/header/Defines.h @@ -26,9 +26,21 @@ inline void Message(const char* format, ...) #endif } +inline void Info(const char* format, ...) +{ +#ifdef _DEBUG + va_list args; + va_start(args, format); + vprintf(format, args); + va_end(args); +#endif +} + inline void Warning(const char* format, ...) { #ifdef _DEBUG + static HANDLE hConsole = GetStdHandle(STD_ERROR_HANDLE); + [[maybe_unused]] static auto success = SetConsoleTextAttribute(hConsole, 6); va_list args; va_start(args, format); vfprintf(stderr, format, args); diff --git a/header/Main.h b/header/Main.h index 8fc85e1..c08cbb5 100644 --- a/header/Main.h +++ b/header/Main.h @@ -12,6 +12,7 @@ #include "Utils.h" #include +#include #include "Defines.h" #include "Error.h" diff --git a/modules/TextureClient.ixx b/modules/TextureClient.ixx index 688f0ff..57f2a15 100644 --- a/modules/TextureClient.ixx +++ b/modules/TextureClient.ixx @@ -27,9 +27,6 @@ public: int MergeUpdate(); //called from uMod_IDirect3DDevice9::BeginScene() void Initialize(); - // Add TextureFileStruct data, return size of data added. 0 on failure. - unsigned long AddFile(TexEntry& entry, bool compress = false); - std::vector OriginalTextures; // stores the pointer to the uMod_IDirect3DTexture9 objects created by the game std::vector OriginalVolumeTextures; @@ -53,9 +50,8 @@ private: int LockMutex(); int UnlockMutex(); HANDLE hMutex; - std::mutex mutex; - std::unordered_map modded_textures; + std::unordered_map> modded_textures; // array which stores the file in memory and the hash of each texture to be modded // called if a target texture is found @@ -83,8 +79,8 @@ TextureClient::~TextureClient() if (hMutex != nullptr) { CloseHandle(hMutex); } - for (const auto& it : modded_textures) { - delete it.second; + for (const auto texture_file_struct : modded_textures | std::views::values) { + delete texture_file_struct; } modded_textures.clear(); } @@ -159,47 +155,44 @@ int TextureClient::UnlockMutex() return RETURN_OK; } -unsigned long TextureClient::AddFile(TexEntry& entry, const bool compress) +gsl::owner AddFile(TexEntry& entry, const bool compress, const std::filesystem::path& dll_path) { - if (modded_textures.contains(entry.crc_hash)) { - return 0; - } - auto texture_file_struct = new TextureFileStruct(); + const auto texture_file_struct = new TextureFileStruct(); texture_file_struct->crc_hash = entry.crc_hash; - should_update = true; const auto dds_blob = TextureFunction::ConvertToCompressedDDS(entry, compress, dll_path); texture_file_struct->data.assign(static_cast(dds_blob.GetBufferPointer()), static_cast(dds_blob.GetBufferPointer()) + dds_blob.GetBufferSize()); - std::lock_guard lock(mutex); - modded_textures.emplace(entry.crc_hash, texture_file_struct); - return texture_file_struct->data.size(); + return texture_file_struct; } -unsigned ProcessModfile(TextureClient& client, const std::string& modfile, const bool compress) +std::vector> ProcessModfile(const std::string& modfile, const std::filesystem::path& dll_path, const bool compress) { const auto hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); - if (FAILED(hr)) return 0; + if (FAILED(hr)) return {}; Message("Initialize: loading file %s... ", modfile.c_str()); auto file_loader = ModfileLoader(modfile); auto entries = file_loader.GetContents(); if (entries.empty()) { Message("No entries found.\n"); - return 0; + return {}; } Message("%zu textures... ", entries.size()); - unsigned long file_bytes_loaded = 0; + std::vector> texture_file_structs; + texture_file_structs.reserve(entries.size()); + unsigned file_bytes_loaded = 0; for (auto& tpf_entry : entries) { - file_bytes_loaded += client.AddFile(tpf_entry, compress); + const auto tex_file_struct = AddFile(tpf_entry, compress, dll_path); + texture_file_structs.push_back(tex_file_struct); + file_bytes_loaded += texture_file_structs.back()->data.size(); } entries.clear(); Message("%d bytes loaded.\n", file_bytes_loaded); CoUninitialize(); - return file_bytes_loaded; + return texture_file_structs; } void TextureClient::LoadModsFromFile(const char* source) { static std::vector loaded_modfiles{}; - static unsigned long loaded_size = 0; Message("Initialize: searching in %s\n", source); std::ifstream file(source); @@ -226,20 +219,32 @@ void TextureClient::LoadModsFromFile(const char* source) files_size += std::filesystem::file_size(modfile); } } - std::vector> futures; + std::vector>>> futures; for (const auto modfile : modfiles) { - futures.emplace_back(std::async(std::launch::async, ProcessModfile, std::ref(*this), modfile, files_size > 400'000'000)); + futures.emplace_back(std::async(std::launch::async, ProcessModfile, modfile, dll_path, files_size > 400'000'000)); } - // Join all threads + auto loaded_size = 0u; for (auto& future : futures) { - loaded_size += future.get(); + const auto texture_file_structs = future.get(); + for (const auto texture_file_struct : texture_file_structs) { + if (!texture_file_struct->crc_hash) continue; + if (!modded_textures.contains(texture_file_struct->crc_hash)) { + modded_textures.emplace(texture_file_struct->crc_hash, texture_file_struct); + loaded_size += texture_file_struct->data.size(); + } + else { + delete texture_file_struct; + } + } + should_update = true; } Message("Finished loading mods from %s: Loaded %u bytes (%u mb)", source, loaded_size, loaded_size / 1024 / 1024); } void TextureClient::Initialize() { - Message("Initialize: begin\n"); + const auto t1 = std::chrono::high_resolution_clock::now(); + Info("Initialize: begin\n"); Message("Initialize: searching for modlist.txt\n"); char gwpath[MAX_PATH]{}; GetModuleFileName(GetModuleHandle(nullptr), gwpath, MAX_PATH); //ask for name and path of this executable @@ -254,8 +259,9 @@ void TextureClient::Initialize() LoadModsFromFile(modlist.string().c_str()); } } - - Message("Initialize: end\n"); + const auto t2 = std::chrono::high_resolution_clock::now(); + const auto ms = duration_cast(t2 - t1); + Info("Initialize: end, took %d ms\n", ms); } int TextureClient::AddTexture(uModTexturePtr auto pTexture)