diff options
author | Shauren <shauren.trinity@gmail.com> | 2024-07-06 12:56:59 +0200 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2024-07-06 12:56:59 +0200 |
commit | 11a252e601522d840c5c2ceb3331495c51a2e5d3 (patch) | |
tree | e33c6a36874562c4ef986d54cc14ff1472eb851d | |
parent | a0f13391a0404d859cf4f8b8dee0c801f2640473 (diff) |
Tools/vmap_assembler: Multithread building vmaps
-rw-r--r-- | src/common/Threading/ThreadPool.h | 5 | ||||
-rw-r--r-- | src/tools/vmap4_assembler/TileAssembler.cpp | 107 | ||||
-rw-r--r-- | src/tools/vmap4_assembler/TileAssembler.h | 10 | ||||
-rw-r--r-- | src/tools/vmap4_assembler/VMapAssembler.cpp | 12 |
4 files changed, 89 insertions, 45 deletions
diff --git a/src/common/Threading/ThreadPool.h b/src/common/Threading/ThreadPool.h index 1606bf98d1e..c99bdf1af4d 100644 --- a/src/common/Threading/ThreadPool.h +++ b/src/common/Threading/ThreadPool.h @@ -40,6 +40,11 @@ public: _impl.join(); } + void Stop() + { + _impl.stop(); + } + private: boost::asio::thread_pool _impl; }; diff --git a/src/tools/vmap4_assembler/TileAssembler.cpp b/src/tools/vmap4_assembler/TileAssembler.cpp index e0a3962cf2b..6be3e1b70b6 100644 --- a/src/tools/vmap4_assembler/TileAssembler.cpp +++ b/src/tools/vmap4_assembler/TileAssembler.cpp @@ -17,11 +17,13 @@ #include "TileAssembler.h" #include "BoundingIntervalHierarchy.h" +#include "Duration.h" #include "IteratorPair.h" #include "MapTree.h" #include "Memory.h" #include "StringConvert.h" #include "StringFormat.h" +#include "ThreadPool.h" #include "VMapDefinitions.h" #include <boost/filesystem/directory.hpp> #include <boost/filesystem/operations.hpp> @@ -43,8 +45,8 @@ namespace VMAP //================================================================= - TileAssembler::TileAssembler(std::string pSrcDirName, std::string pDestDirName) - : iDestDir(std::move(pDestDirName)), iSrcDir(std::move(pSrcDirName)) + TileAssembler::TileAssembler(std::string srcDirName, std::string destDirName, uint32 threads) + : iSrcDir(std::move(srcDirName)), iDestDir(std::move(destDirName)), iThreads(threads) { } @@ -69,64 +71,95 @@ namespace VMAP // else already exists - this is fine, continue } - std::deque<boost::filesystem::path> mapSpawnFiles; + // export Map data + Trinity::ThreadPool threadPool(iThreads); + bool aborted = false; + std::once_flag abortedFlag; + auto abortThreads = [&threadPool, &aborted, &abortedFlag] + { + std::call_once(abortedFlag, [&] { threadPool.Stop(); aborted = true; }); + }; + + // Every worker thread gets its dedicated output container to avoid having to synchronize access + std::atomic<std::size_t> workerIndexGen; + std::vector<std::set<std::string>> spawnedModelFilesByThread(iThreads); + + std::atomic<std::size_t> mapsToProcess; + for (boost::filesystem::directory_entry const& directoryEntry : dirBin) { if (!boost::filesystem::is_regular_file(directoryEntry)) continue; - mapSpawnFiles.push_back(directoryEntry.path()); - } + ++mapsToProcess; + threadPool.PostWork([this, file = directoryEntry.path(), &abortThreads, &workerIndexGen, &spawnedModelFilesByThread, &mapsToProcess] + { + thread_local std::size_t workerIndex = workerIndexGen++; + --mapsToProcess; - // export Map data - while (!mapSpawnFiles.empty()) - { - boost::filesystem::path file = std::move(mapSpawnFiles.front()); - mapSpawnFiles.pop_front(); + auto dirf = Trinity::make_unique_ptr_with_deleter(fopen(file.string().c_str(), "rb"), &::fclose); + if (!dirf) + { + printf("Could not read dir_bin file!\n"); + return abortThreads(); + } - auto dirf = Trinity::make_unique_ptr_with_deleter(fopen(file.string().c_str(), "rb"), &::fclose); - if (!dirf) - { - printf("Could not read dir_bin file!\n"); - return false; - } + Optional<uint32> mapId = Trinity::StringTo<uint32>(file.filename().string()); + if (!mapId) + { + printf("Invalid Map ID %s\n", file.filename().string().c_str()); + return abortThreads(); + } - Optional<uint32> mapId = Trinity::StringTo<uint32>(file.filename().string()); - if (!mapId) - { - printf("Invalid Map ID %s\n", file.filename().string().c_str()); - return false; - } + printf("spawning Map %u\n", *mapId); - printf("spawning Map %u\n", *mapId); + MapSpawns data; + data.MapId = *mapId; + if (!readMapSpawns(dirf.get(), &data)) + return abortThreads(); - MapSpawns data; - data.MapId = *mapId; - if (!readMapSpawns(dirf.get(), &data)) - return false; + if (!convertMap(data)) + return abortThreads(); - if (!convertMap(data)) - return false; + spawnedModelFilesByThread[workerIndex].merge(data.SpawnedModelFiles); + }); } + while (mapsToProcess && !aborted) + std::this_thread::sleep_for(1s); + + if (aborted) + return false; + + for (std::set<std::string>& modelsForThread : spawnedModelFilesByThread) + spawnedModelFiles.merge(modelsForThread); + // add an object models, listed in temp_gameobject_models file exportGameobjectModels(); // export objects printf("\nConverting Model Files\n"); for (std::string const& spawnedModelFile : spawnedModelFiles) { - printf("Converting %s\n", spawnedModelFile.c_str()); - if (!convertRawFile(spawnedModelFile)) + threadPool.PostWork([&] { - printf("error converting %s\n", spawnedModelFile.c_str()); - break; - } + printf("Converting %s\n", spawnedModelFile.c_str()); + if (!convertRawFile(spawnedModelFile)) + { + printf("error converting %s\n", spawnedModelFile.c_str()); + abortThreads(); + } + }); } + threadPool.Join(); + + if (aborted) + return false; + return true; } - bool TileAssembler::convertMap(MapSpawns& data) + bool TileAssembler::convertMap(MapSpawns& data) const { float constexpr invTileSize = 1.0f / 533.33333f; @@ -142,7 +175,6 @@ namespace VMAP continue; mapSpawns.push_back(&spawn); - spawnedModelFiles.insert(spawn.name); std::map<uint32, std::set<uint32>>& tileEntries = (spawn.flags & MOD_PARENT_SPAWN) ? data.ParentTileEntries : data.TileEntries; @@ -285,12 +317,13 @@ namespace VMAP } data->UniqueEntries.emplace(spawn.ID, spawn); + data->SpawnedModelFiles.insert(spawn.name); } return true; } - bool TileAssembler::calculateTransformedBound(ModelSpawn &spawn) + bool TileAssembler::calculateTransformedBound(ModelSpawn &spawn) const { std::string modelFilename(iSrcDir); modelFilename.push_back('/'); diff --git a/src/tools/vmap4_assembler/TileAssembler.h b/src/tools/vmap4_assembler/TileAssembler.h index f803793a5b5..3586a9daec6 100644 --- a/src/tools/vmap4_assembler/TileAssembler.h +++ b/src/tools/vmap4_assembler/TileAssembler.h @@ -56,6 +56,7 @@ namespace VMAP { uint32 MapId = 0; std::map<uint32, ModelSpawn> UniqueEntries; + std::set<std::string> SpawnedModelFiles; std::map<uint32 /*packedTileId*/, std::set<uint32 /*Id*/>> TileEntries; std::map<uint32 /*packedTileId*/, std::set<uint32 /*Id*/>> ParentTileEntries; }; @@ -92,17 +93,18 @@ namespace VMAP class TileAssembler { private: - std::string iDestDir; std::string iSrcDir; + std::string iDestDir; + uint32 iThreads; std::set<std::string> spawnedModelFiles; public: - TileAssembler(std::string pSrcDirName, std::string pDestDirName); + TileAssembler(std::string srcDirName, std::string destDirName, uint32 threads); bool convertWorld2(); - bool convertMap(MapSpawns& data); + bool convertMap(MapSpawns& data) const; static bool readMapSpawns(FILE* dirf, MapSpawns* data); - bool calculateTransformedBound(ModelSpawn &spawn); + bool calculateTransformedBound(ModelSpawn &spawn) const; void exportGameobjectModels(); bool convertRawFile(const std::string& pModelFilename); diff --git a/src/tools/vmap4_assembler/VMapAssembler.cpp b/src/tools/vmap4_assembler/VMapAssembler.cpp index b492b207879..9a4886b2919 100644 --- a/src/tools/vmap4_assembler/VMapAssembler.cpp +++ b/src/tools/vmap4_assembler/VMapAssembler.cpp @@ -24,6 +24,7 @@ #include <boost/program_options.hpp> #include <iostream> #include <string> +#include <thread> namespace po = boost::program_options; @@ -34,9 +35,10 @@ namespace po = boost::program_options; * @param [in] argv raw command line arguments * @param [out] src raw data dir * @param [out] dest vmap dest dir + * @param [out] threads number of threads to use * @return Non-empty optional if program should exit immediately (holds exit code in that case) */ -Optional<int> HandleArgs(int argc, char* argv[], std::string* src, std::string* dest); +Optional<int> HandleArgs(int argc, char* argv[], std::string* src, std::string* dest, uint32* threads); int main(int argc, char* argv[]) { @@ -45,14 +47,15 @@ int main(int argc, char* argv[]) Trinity::Locale::Init(); std::string src, dest; - if (Optional<int> exitCode = HandleArgs(argc, argv, &src, &dest)) + uint32 threads = 0; + if (Optional<int> exitCode = HandleArgs(argc, argv, &src, &dest, &threads)) return *exitCode; Trinity::Banner::Show("VMAP assembler", [](char const* text) { std::cout << text << std::endl; }, nullptr); std::cout << "using " << src << " as source directory and writing output to " << dest << std::endl; - VMAP::TileAssembler ta(std::move(src), std::move(dest)); + VMAP::TileAssembler ta(std::move(src), std::move(dest), threads); if (!ta.convertWorld2()) { @@ -64,10 +67,11 @@ int main(int argc, char* argv[]) return 0; } -Optional<int> HandleArgs(int argc, char* argv[], std::string* src, std::string* dest) +Optional<int> HandleArgs(int argc, char* argv[], std::string* src, std::string* dest, uint32* threads) { po::options_description visible("Usage: vmap4assembler [OPTION]... [SRC] [DEST]\n\nWhere OPTION can be any of"); visible.add_options() + ("threads", po::value<uint32>(threads)->default_value(std::thread::hardware_concurrency()), "number of threads to use") ("help,h", "print usage message") ("version,v", "print version build info"); |