mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-15 23:20:36 +01:00
Tools/vmap_assembler: Multithread building vmaps
This commit is contained in:
@@ -40,6 +40,11 @@ public:
|
||||
_impl.join();
|
||||
}
|
||||
|
||||
void Stop()
|
||||
{
|
||||
_impl.stop();
|
||||
}
|
||||
|
||||
private:
|
||||
boost::asio::thread_pool _impl;
|
||||
};
|
||||
|
||||
@@ -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,45 +71,68 @@ 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;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
Optional<uint32> mapId = Trinity::StringTo<uint32>(file.filename().string());
|
||||
if (!mapId)
|
||||
{
|
||||
printf("Invalid Map ID %s\n", file.filename().string().c_str());
|
||||
return abortThreads();
|
||||
}
|
||||
|
||||
printf("spawning Map %u\n", *mapId);
|
||||
|
||||
MapSpawns data;
|
||||
data.MapId = *mapId;
|
||||
if (!readMapSpawns(dirf.get(), &data))
|
||||
return abortThreads();
|
||||
|
||||
if (!convertMap(data))
|
||||
return abortThreads();
|
||||
|
||||
spawnedModelFilesByThread[workerIndex].merge(data.SpawnedModelFiles);
|
||||
});
|
||||
}
|
||||
|
||||
// export Map data
|
||||
while (!mapSpawnFiles.empty())
|
||||
{
|
||||
boost::filesystem::path file = std::move(mapSpawnFiles.front());
|
||||
mapSpawnFiles.pop_front();
|
||||
while (mapsToProcess && !aborted)
|
||||
std::this_thread::sleep_for(1s);
|
||||
|
||||
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;
|
||||
}
|
||||
if (aborted)
|
||||
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 false;
|
||||
}
|
||||
|
||||
printf("spawning Map %u\n", *mapId);
|
||||
|
||||
MapSpawns data;
|
||||
data.MapId = *mapId;
|
||||
if (!readMapSpawns(dirf.get(), &data))
|
||||
return false;
|
||||
|
||||
if (!convertMap(data))
|
||||
return false;
|
||||
}
|
||||
for (std::set<std::string>& modelsForThread : spawnedModelFilesByThread)
|
||||
spawnedModelFiles.merge(modelsForThread);
|
||||
|
||||
// add an object models, listed in temp_gameobject_models file
|
||||
exportGameobjectModels();
|
||||
@@ -115,18 +140,26 @@ namespace VMAP
|
||||
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('/');
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user