From 81a70d7c96493bc904c949c8d46d11ea0020bc8c Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Tue, 30 Oct 2018 12:37:22 +0100 Subject: [PATCH] - fix race condition crash - rewrote kexWorker to use std::function - added --threads, --samples, --size command line arguments - changed the default threading to be what the processor supports (64 threads on my Threadripper, muhahaha!) --- src/level/level.cpp | 9 +- src/lightmap/lightmap.cpp | 66 ++---- src/lightmap/lightmap.h | 3 + src/lightmap/worker.cpp | 419 +++----------------------------------- src/lightmap/worker.h | 69 +------ src/main.cpp | 292 +++++++++++++++++++++++++- 6 files changed, 343 insertions(+), 515 deletions(-) diff --git a/src/level/level.cpp b/src/level/level.cpp index af1df8f..21c4422 100644 --- a/src/level/level.cpp +++ b/src/level/level.cpp @@ -22,6 +22,9 @@ //#include "rejectbuilder.h" #include +extern int LMDims; +extern int Samples; + extern void ShowView (FLevel *level); enum @@ -597,9 +600,9 @@ void FProcessor::BuildNodes() void FProcessor::BuildLightmaps(const char *configFile) { LMBuilder.ambience = 0.0f; - LMBuilder.samples = 8; - LMBuilder.textureWidth = LIGHTMAP_MAX_SIZE; - LMBuilder.textureHeight = LIGHTMAP_MAX_SIZE; + LMBuilder.samples = Samples; + LMBuilder.textureWidth = LMDims; + LMBuilder.textureHeight = LMDims; Level.ParseConfigFile(configFile); Level.SetupDlight(); diff --git a/src/lightmap/lightmap.cpp b/src/lightmap/lightmap.cpp index ff2ee8e..896b2f0 100644 --- a/src/lightmap/lightmap.cpp +++ b/src/lightmap/lightmap.cpp @@ -42,30 +42,8 @@ #include #include -kexWorker lightmapWorker; - const kexVec3 kexLightmapBuilder::gridSize(64, 64, 128); -// -// LightmapWorkerFunc -// - -static void LightmapWorkerFunc(void *data, int id) -{ - kexLightmapBuilder *builder = static_cast(data); - builder->LightSurface(id); -} - -// -// LightGridWorkerFunc -// - -static void LightGridWorkerFunc(void *data, int id) -{ - kexLightmapBuilder *builder = static_cast(data); - builder->LightGrid(id); -} - // // kexLightmapBuilder::kexLightmapBuilder // @@ -536,23 +514,25 @@ void kexLightmapBuilder::TraceSurface(surface_t *surface) int width = surface->lightmapDims[0]; int height = surface->lightmapDims[1]; + std::unique_lock lock(mutex); + // now that we know the width and height of this block, see if we got // room for it in the light map texture. if not, then we must allocate // a new texture if(!MakeRoomForBlock(width, height, &x, &y, &surface->lightmapNum)) { // allocate a new texture for this block - lightmapWorker.LockMutex(); NewTexture(); - lightmapWorker.UnlockMutex(); if(!MakeRoomForBlock(width, height, &x, &y, &surface->lightmapNum)) { - Error("Lightmap allocation failed\n"); + Error("Lightmap allocation failed\n"); return; } } + lock.unlock(); + // calculate texture coordinates for(i = 0; i < surface->numVerts; i++) { @@ -567,9 +547,9 @@ void kexLightmapBuilder::TraceSurface(surface_t *surface) surface->lightmapOffs[1] = y; } - lightmapWorker.LockMutex(); + std::unique_lock lock(mutex); currentTexture = textures[surface->lightmapNum]; - lightmapWorker.UnlockMutex(); + lock.unlock(); // store results to lightmap texture for(i = 0; i < sampleHeight; i++) @@ -581,9 +561,9 @@ void kexLightmapBuilder::TraceSurface(surface_t *surface) surface->lightmapOffs[0]) * 3); // convert RGB to bytes - rgb[0] = (byte)(colorSamples[i][j][0] * 255); - rgb[1] = (byte)(colorSamples[i][j][1] * 255); - rgb[2] = (byte)(colorSamples[i][j][2] * 255); + rgb[0] = (uint32_t)(colorSamples[i][j][0] * 255 + 0.5f); + rgb[1] = (uint32_t)(colorSamples[i][j][1] * 255 + 0.5f); + rgb[2] = (uint32_t)(colorSamples[i][j][2] * 255 + 0.5f); currentTexture[offs + j * 3 + 0] = rgb[0]; currentTexture[offs + j * 3 + 1] = rgb[1]; @@ -611,12 +591,12 @@ void kexLightmapBuilder::LightSurface(const int surfid) BuildSurfaceParams(surfaces[surfid]); TraceSurface(surfaces[surfid]); - lightmapWorker.LockMutex(); + std::unique_lock lock(mutex); + remaining = (float)processed / (float)numsurfs; processed++; printf("%i%c surfaces done\r", (int)(remaining * 100.0f), '%'); - lightmapWorker.UnlockMutex(); } // @@ -822,11 +802,11 @@ void kexLightmapBuilder::LightGrid(const int gridid) kexMath::Clamp(gridMap[gridid].color, 0, 1); - lightmapWorker.LockMutex(); + std::unique_lock lock(mutex); + remaining = (float)processed / (float)numLightGrids; printf("%i%c cells done\r", (int)(remaining * 100.0f), '%'); - lightmapWorker.UnlockMutex(); } // @@ -841,15 +821,12 @@ void kexLightmapBuilder::CreateLightmaps(FLevel &doomMap) CreateLightGrid(); printf("------------- Tracing surfaces -------------\n"); - lightmapWorker.RunThreads(surfaces.Length(), this, LightmapWorkerFunc); - while(!lightmapWorker.FinishedAllJobs()) - { - Delay(1000); - } + kexWorker::RunJob(surfaces.Length(), [=](int id) { + LightSurface(id); + }); printf("Texels traced: %i \n\n", tracedTexels); - lightmapWorker.Destroy(); } // @@ -895,12 +872,9 @@ void kexLightmapBuilder::CreateLightGrid() (int)(gridBlock.x * gridBlock.y), hb_static); // process all grid cells - lightmapWorker.RunThreads(count, this, LightGridWorkerFunc); - - while(!lightmapWorker.FinishedAllJobs()) - { - Delay(1000); - } + kexWorker::RunJob(count, [=](int id) { + LightGrid(id); + }); printf("\nGrid cells: %i\n\n", count); } diff --git a/src/lightmap/lightmap.h b/src/lightmap/lightmap.h index 39c9046..af40755 100644 --- a/src/lightmap/lightmap.h +++ b/src/lightmap/lightmap.h @@ -28,6 +28,7 @@ #pragma once #include "surfaces.h" +#include #define LIGHTMAP_MAX_SIZE 1024 @@ -87,4 +88,6 @@ private: kexBBox worldGrid; kexBBox gridBound; kexVec3 gridBlock; + + std::mutex mutex; }; diff --git a/src/lightmap/worker.cpp b/src/lightmap/worker.cpp index 501f9a8..abb1b7b 100644 --- a/src/lightmap/worker.cpp +++ b/src/lightmap/worker.cpp @@ -1,407 +1,36 @@ -//----------------------------------------------------------------------------- -// Note: this is a modified version of dlight. It is not the original software. -//----------------------------------------------------------------------------- -// -// Copyright (c) 2013-2014 Samuel Villarreal -// svkaiser@gmail.com -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would be -// appreciated but is not required. -// -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// 3. This notice may not be removed or altered from any source -// distribution. -// -//----------------------------------------------------------------------------- -// -// DESCRIPTION: Worker Threads -// -//----------------------------------------------------------------------------- -#include "common.h" #include "worker.h" +#include +#include +#include -int kexWorker::maxWorkThreads = MAX_THREADS; +extern int NumThreads; -// -// WorkThread -// - -void *WorkThread(void *p) +void kexWorker::RunJob(int count, std::function callback) { - jobFuncArgs_t *args = (jobFuncArgs_t*)p; - kexWorker *worker = args->worker; + int numThreads = NumThreads; + if (numThreads <= 0) + numThreads = std::thread::hardware_concurrency(); + if (numThreads <= 0) + numThreads = 4; - while(1) - { - int jobid; + numThreads = std::min(numThreads, count); - worker->LockMutex(); + std::vector threads; + for (int threadIndex = 0; threadIndex < numThreads; threadIndex++) + { + threads.push_back(std::thread([=]() { - if(worker->FinishedAllJobs()) - { - worker->UnlockMutex(); - break; - } + int start = threadIndex * count / numThreads; + int end = std::min((threadIndex + 1) * count / numThreads, count); + for (int i = start; i < end; i++) + { + callback(i); + } - jobid = worker->DispatchJob(); - worker->UnlockMutex(); + })); + } - worker->RunJob(args->data, jobid); - } - - return 0; -} - -// -// kexWorker::kexWorker -// - -kexWorker::kexWorker() -{ - this->numWorkLoad = 0; - this->jobsWorked = 0; - this->job = NULL; - - memset(this->jobArgs, 0, sizeof(this->jobArgs)); -} - -// -// kexWorker::~kexWorker -// - -kexWorker::~kexWorker() -{ -} - -// -// kexWorker::LockMutex -// - -void kexWorker::LockMutex() -{ - mutex.lock(); -} - -// -// kexWorker::UnlockMutex -// - -void kexWorker::UnlockMutex() -{ - mutex.unlock(); -} - -// -// kexWorker::Destroy -// - -void kexWorker::Destroy() -{ -} - -// -// kexWorker::RunThreads -// - -void kexWorker::RunThreads(const int count, void *data, jobFunc_t jobFunc) -{ - job = jobFunc; - numWorkLoad = count; - jobsWorked = 0; - - for(int i = 0; i < kexWorker::maxWorkThreads; ++i) - { - jobArgs[i].worker = this; - jobArgs[i].jobID = i; - jobArgs[i].data = data; - - threads[i] = std::thread([=]() { WorkThread(&jobArgs[i]); }); - } - - for(int i = 0; i < kexWorker::maxWorkThreads; ++i) - { + for (int i = 0; i < numThreads; i++) threads[i].join(); - } } - -///////////////////////////////////////////////////////////////////////////// -// Stuff from old main.cpp - -void Error(const char *error, ...) -{ - va_list argptr; - - va_start(argptr, error); - vprintf(error, argptr); - va_end(argptr); - printf("\n"); - exit(1); -} - -char *Va(const char *str, ...) -{ - va_list v; - static char vastr[1024]; - - va_start(v, str); - vsprintf(vastr, str, v); - va_end(v); - - return vastr; -} - -void Delay(int ms) -{ - std::this_thread::sleep_for(std::chrono::milliseconds(ms)); -} - -// -// GetSeconds -// - -const int64_t GetSeconds() -{ - return time(0); -} - -// -// Main -// - -#if 0 -int main(int argc, char **argv) -{ - kexWadFile wadFile; - kexWadFile outWadFile; - FLevel doomMap; - lump_t *lmLump; - kexArray ignoreLumps; - kexLightmapBuilder builder; - kexStr configFile("strife_sve.cfg"); - bool bWriteTGA; - int map = 1; - int arg = 1; - - printf("DLight (c) 2013-2014 Samuel Villarreal\n\n"); - - if (argc < 1 || argv[1] == NULL) - { - printf("Usage: dlight [options] [wadfile]\n"); - printf("Use -help for list of options\n"); - return 0; - } - - basePath = argv[0]; - basePath.StripFile(); - - bWriteTGA = false; - - while (1) - { - if (!strcmp(argv[arg], "-help")) - { - printf("Options:\n"); - printf("-help: displays all known options\n"); - printf("-map: process lightmap for MAP##\n"); - printf("-samples: set texel sampling size (lowest = higher quaility but\n"); - printf(" slow compile time) must be in powers of two\n"); - printf("-ambience: set global ambience value for lightmaps (0.0 - 1.0)\n"); - printf("-size: lightmap texture dimentions for width and height\n"); - printf(" must be in powers of two (1, 2, 4, 8, 16, etc)\n"); - printf("-threads: set total number of threads (1 min, 128 max)\n"); - printf("-config: specify a config file to parse (default: strife_sve.cfg)\n"); - printf("-writetga: dumps lightmaps to targa (.TGA) files\n"); - arg++; - return 0; - } - else if (!strcmp(argv[arg], "-map")) - { - if (argv[arg + 1] == NULL) - { - Error("Specify map number for -map\n"); - return 1; - } - - map = atoi(argv[++arg]); - arg++; - } - else if (!strcmp(argv[arg], "-samples")) - { - if (argv[arg + 1] == NULL) - { - Error("Specify value for -samples\n"); - return 1; - } - - builder.samples = atoi(argv[++arg]); - if (builder.samples <= 0) - { - builder.samples = 1; - } - if (builder.samples > 128) - { - builder.samples = 128; - } - - builder.samples = kexMath::RoundPowerOfTwo(builder.samples); - arg++; - } - else if (!strcmp(argv[arg], "-ambience")) - { - if (argv[arg + 1] == NULL) - { - Error("Specify value for -ambience\n"); - return 1; - } - - builder.ambience = (float)atof(argv[++arg]); - if (builder.ambience < 0) - { - builder.ambience = 0; - } - if (builder.ambience > 1) - { - builder.ambience = 1; - } - } - else if (!strcmp(argv[arg], "-size")) - { - int lmDims; - - if (argv[arg + 1] == NULL) - { - Error("Specify value for -size\n"); - return 1; - } - - lmDims = atoi(argv[++arg]); - if (lmDims <= 0) - { - lmDims = 1; - } - if (lmDims > LIGHTMAP_MAX_SIZE) - { - lmDims = LIGHTMAP_MAX_SIZE; - } - - lmDims = kexMath::RoundPowerOfTwo(lmDims); - - builder.textureWidth = lmDims; - builder.textureHeight = lmDims; - arg++; - } - else if (!strcmp(argv[arg], "-threads")) - { - kexWorker::maxWorkThreads = atoi(argv[++arg]); - kexMath::Clamp(kexWorker::maxWorkThreads, 1, MAX_THREADS); - arg++; - } - else if (!strcmp(argv[arg], "-config")) - { - configFile = argv[++arg]; - arg++; - } - else if (!strcmp(argv[arg], "-writetga")) - { - bWriteTGA = true; - arg++; - } - else - { - break; - } - } - - if (argv[arg] == NULL) - { - printf("Usage: dlight [options] [wadfile]\n"); - return 0; - } - - if (!wadFile.Open(argv[arg])) - { - return 1; - } - - // concat the base path to light def file if there is none - if (configFile.IndexOf("\\") == -1 && configFile.IndexOf("/") == -1) - { - configFile = basePath + configFile; - } - - int starttime = (int)GetSeconds(); - - printf("---------------- Parsing config file ----------------\n\n"); - doomMap.ParseConfigFile(configFile.c_str()); - - printf("------------- Building level structures -------------\n\n"); - wadFile.SetCurrentMap(map); - doomMap.BuildMapFromWad(wadFile); - - printf("----------- Allocating surfaces from level ----------\n\n"); - Surface_AllocateFromMap(doomMap); - - printf("---------------- Allocating lights ----------------\n\n"); - doomMap.CreateLights(); - - printf("---------------- Creating lightmaps ---------------\n\n"); - builder.CreateLightmaps(doomMap); - doomMap.CleanupThingLights(); - - if (bWriteTGA) - { - builder.WriteTexturesToTGA(); - } - - printf("------------------ Rebuilding wad ----------------\n\n"); - wadFile.CreateBackup(); - - lmLump = wadFile.GetLumpFromName(Va("LM_MAP%02d", wadFile.currentmap)); - - if (lmLump) - { - int lumpnum = lmLump - wadFile.lumps; - - ignoreLumps.Push(lumpnum + ML_LM_LABEL); - ignoreLumps.Push(lumpnum + ML_LM_CELLS); - ignoreLumps.Push(lumpnum + ML_LM_SUN); - ignoreLumps.Push(lumpnum + ML_LM_SURFS); - ignoreLumps.Push(lumpnum + ML_LM_TXCRD); - ignoreLumps.Push(lumpnum + ML_LM_LMAPS); - } - - outWadFile.InitForWrite(); - outWadFile.CopyLumpsFromWadFile(wadFile, ignoreLumps); - outWadFile.AddLump(Va("LM_MAP%02d", wadFile.currentmap), 0, NULL); - - builder.AddLightGridLump(outWadFile); - builder.AddLightmapLumps(outWadFile); - - printf("------------- Writing %s -------------\n\n", wadFile.wadName.c_str()); - outWadFile.Write(wadFile.wadName); - outWadFile.Close(); - wadFile.Close(); - - printf("----------------- Shutting down -----------------\n\n"); - Mem_Purge(hb_static); - - int proctime = (int)GetSeconds() - starttime; - printf("\nBuild time: %d:%02d:%02d\n", - proctime / 3600, (proctime / 60) % 60, proctime % 60); - - return 0; -} -#endif diff --git a/src/lightmap/worker.h b/src/lightmap/worker.h index a1f8d76..fda65c4 100644 --- a/src/lightmap/worker.h +++ b/src/lightmap/worker.h @@ -1,75 +1,10 @@ -//----------------------------------------------------------------------------- -// Note: this is a modified version of dlight. It is not the original software. -//----------------------------------------------------------------------------- -// -// Copyright (c) 2013-2014 Samuel Villarreal -// svkaiser@gmail.com -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would be -// appreciated but is not required. -// -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// 3. This notice may not be removed or altered from any source -// distribution. -// #pragma once -#include -#include - -// There's a race condition in the code that causes it to sometimes fail if using multiple threads -//#define MAX_THREADS 128 -#define MAX_THREADS 1 - -class kexWorker; - -typedef void(*jobFunc_t)(void*, int); - -struct jobFuncArgs_t -{ - kexWorker *worker; - void *data; - int jobID; -}; +#include class kexWorker { public: - kexWorker(); - ~kexWorker(); - - void RunThreads(const int count, void *data, jobFunc_t jobFunc); - void LockMutex(); - void UnlockMutex(); - void Destroy(); - - bool FinishedAllJobs() { return jobsWorked == numWorkLoad; } - int DispatchJob() { int j = jobsWorked; jobsWorked++; return j; } - void RunJob(void *data, const int jobID) { job(data, jobID); } - - jobFuncArgs_t *Args(const int id) { return &jobArgs[id]; } - const int JobsWorked() const { return jobsWorked; } - - static int maxWorkThreads; - -private: - std::thread threads[MAX_THREADS]; - jobFuncArgs_t jobArgs[MAX_THREADS]; - std::mutex mutex; - jobFunc_t job; - int jobsWorked; - int numWorkLoad; + static void RunJob(int count, std::function callback); }; diff --git a/src/main.cpp b/src/main.cpp index b398f41..f4a9bf1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -53,6 +53,7 @@ #include #include #include +#include #include "framework/zdray.h" #include "wad/wad.h" @@ -113,6 +114,9 @@ bool GLOnly = true;// false; bool V5GLNodes = false; bool HaveSSE1, HaveSSE2; int SSELevel; +int NumThreads = 0; +int LMDims = LIGHTMAP_MAX_SIZE; +int Samples = 8; // PRIVATE DATA DEFINITIONS ------------------------------------------------ @@ -148,6 +152,9 @@ static option long_opts[] = {"no-sse", no_argument, 0, 1002}, {"no-sse2", no_argument, 0, 1003}, {"comments", no_argument, 0, 'c'}, + {"threads", required_argument, 0, 'j'}, + {"samples", required_argument, 0, 'Q'}, + {"size", required_argument, 0, 'S'}, {0,0,0,0} }; @@ -426,6 +433,21 @@ static void ParseArgs(int argc, char **argv) case 1003: // Disable only SSE2 ClassifyLine routine HaveSSE2 = false; break; + case 'j': + NumThreads = atoi(optarg); + break; + case 'Q': + Samples = atoi(optarg); + if (Samples <= 0) Samples = 1; + if (Samples > 128) Samples = 128; + Samples = kexMath::RoundPowerOfTwo(Samples); + break; + case 'S': + LMDims = atoi(optarg); + if (LMDims <= 0) LMDims = 1; + if (LMDims > LIGHTMAP_MAX_SIZE) LMDims = LIGHTMAP_MAX_SIZE; + LMDims = kexMath::RoundPowerOfTwo(LMDims); + break; case 1000: ShowUsage(); exit(0); @@ -466,6 +488,11 @@ static void ShowUsage() " -s, --split-cost=NNN Cost for splitting segs (default %d)\n" " -d, --diagonal-cost=NNN Cost for avoiding diagonal splitters (default %d)\n" " -P, --no-polyobjs Do not check for polyobject subsector splits\n" + " -j, --threads=NNN Number of threads used for raytracing (default %d)\n" + " -Q, --samples=NNN Set texel sampling size (lowest = higher quaility but\n" + " slow compile time) must be in powers of two\n" + " -S, --size=NNN lightmap texture dimensions for width and height\n" + " must be in powers of two (1, 2, 4, 8, 16, etc)\n" #ifdef _WIN32 " -v, --view View the nodes\n" #endif @@ -481,6 +508,7 @@ static void ShowUsage() , MaxSegs /* Partition size */ , SplitCost , AAPreference + , (int)std::thread::hardware_concurrency() ); } @@ -678,10 +706,6 @@ static void CheckSSE() } #endif -//========================================================================== -// -// Warn -// //========================================================================== void Warn(const char *format, ...) @@ -697,3 +721,263 @@ void Warn(const char *format, ...) vprintf(format, marker); va_end(marker); } + +void Error(const char *error, ...) +{ + va_list argptr; + + va_start(argptr, error); + vprintf(error, argptr); + va_end(argptr); + printf("\n"); + exit(1); +} + +char *Va(const char *str, ...) +{ + va_list v; + static char vastr[1024]; + + va_start(v, str); + vsprintf(vastr, str, v); + va_end(v); + + return vastr; +} + +void Delay(int ms) +{ + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); +} + +const int64_t GetSeconds() +{ + return time(0); +} + +#if 0 // Old dlight main function + +int main(int argc, char **argv) +{ + kexWadFile wadFile; + kexWadFile outWadFile; + FLevel doomMap; + lump_t *lmLump; + kexArray ignoreLumps; + kexLightmapBuilder builder; + kexStr configFile("strife_sve.cfg"); + bool bWriteTGA; + int map = 1; + int arg = 1; + + printf("DLight (c) 2013-2014 Samuel Villarreal\n\n"); + + if (argc < 1 || argv[1] == NULL) + { + printf("Usage: dlight [options] [wadfile]\n"); + printf("Use -help for list of options\n"); + return 0; + } + + basePath = argv[0]; + basePath.StripFile(); + + bWriteTGA = false; + + while (1) + { + if (!strcmp(argv[arg], "-help")) + { + printf("Options:\n"); + printf("-help: displays all known options\n"); + printf("-map: process lightmap for MAP##\n"); + printf("-samples: set texel sampling size (lowest = higher quaility but\n"); + printf(" slow compile time) must be in powers of two\n"); + printf("-ambience: set global ambience value for lightmaps (0.0 - 1.0)\n"); + printf("-size: lightmap texture dimentions for width and height\n"); + printf(" must be in powers of two (1, 2, 4, 8, 16, etc)\n"); + printf("-threads: set total number of threads (1 min, 128 max)\n"); + printf("-config: specify a config file to parse (default: strife_sve.cfg)\n"); + printf("-writetga: dumps lightmaps to targa (.TGA) files\n"); + arg++; + return 0; + } + else if (!strcmp(argv[arg], "-map")) + { + if (argv[arg + 1] == NULL) + { + Error("Specify map number for -map\n"); + return 1; + } + + map = atoi(argv[++arg]); + arg++; + } + else if (!strcmp(argv[arg], "-samples")) + { + if (argv[arg + 1] == NULL) + { + Error("Specify value for -samples\n"); + return 1; + } + + builder.samples = atoi(argv[++arg]); + if (builder.samples <= 0) + { + builder.samples = 1; + } + if (builder.samples > 128) + { + builder.samples = 128; + } + + builder.samples = kexMath::RoundPowerOfTwo(builder.samples); + arg++; + } + else if (!strcmp(argv[arg], "-ambience")) + { + if (argv[arg + 1] == NULL) + { + Error("Specify value for -ambience\n"); + return 1; + } + + builder.ambience = (float)atof(argv[++arg]); + if (builder.ambience < 0) + { + builder.ambience = 0; + } + if (builder.ambience > 1) + { + builder.ambience = 1; + } + } + else if (!strcmp(argv[arg], "-size")) + { + int lmDims; + + if (argv[arg + 1] == NULL) + { + Error("Specify value for -size\n"); + return 1; + } + + lmDims = atoi(argv[++arg]); + if (lmDims <= 0) + { + lmDims = 1; + } + if (lmDims > LIGHTMAP_MAX_SIZE) + { + lmDims = LIGHTMAP_MAX_SIZE; + } + + lmDims = kexMath::RoundPowerOfTwo(lmDims); + + builder.textureWidth = lmDims; + builder.textureHeight = lmDims; + arg++; + } + else if (!strcmp(argv[arg], "-threads")) + { + kexWorker::maxWorkThreads = atoi(argv[++arg]); + kexMath::Clamp(kexWorker::maxWorkThreads, 1, MAX_THREADS); + arg++; + } + else if (!strcmp(argv[arg], "-config")) + { + configFile = argv[++arg]; + arg++; + } + else if (!strcmp(argv[arg], "-writetga")) + { + bWriteTGA = true; + arg++; + } + else + { + break; + } + } + + if (argv[arg] == NULL) + { + printf("Usage: dlight [options] [wadfile]\n"); + return 0; + } + + if (!wadFile.Open(argv[arg])) + { + return 1; + } + + // concat the base path to light def file if there is none + if (configFile.IndexOf("\\") == -1 && configFile.IndexOf("/") == -1) + { + configFile = basePath + configFile; + } + + int starttime = (int)GetSeconds(); + + printf("---------------- Parsing config file ----------------\n\n"); + doomMap.ParseConfigFile(configFile.c_str()); + + printf("------------- Building level structures -------------\n\n"); + wadFile.SetCurrentMap(map); + doomMap.BuildMapFromWad(wadFile); + + printf("----------- Allocating surfaces from level ----------\n\n"); + Surface_AllocateFromMap(doomMap); + + printf("---------------- Allocating lights ----------------\n\n"); + doomMap.CreateLights(); + + printf("---------------- Creating lightmaps ---------------\n\n"); + builder.CreateLightmaps(doomMap); + doomMap.CleanupThingLights(); + + if (bWriteTGA) + { + builder.WriteTexturesToTGA(); + } + + printf("------------------ Rebuilding wad ----------------\n\n"); + wadFile.CreateBackup(); + + lmLump = wadFile.GetLumpFromName(Va("LM_MAP%02d", wadFile.currentmap)); + + if (lmLump) + { + int lumpnum = lmLump - wadFile.lumps; + + ignoreLumps.Push(lumpnum + ML_LM_LABEL); + ignoreLumps.Push(lumpnum + ML_LM_CELLS); + ignoreLumps.Push(lumpnum + ML_LM_SUN); + ignoreLumps.Push(lumpnum + ML_LM_SURFS); + ignoreLumps.Push(lumpnum + ML_LM_TXCRD); + ignoreLumps.Push(lumpnum + ML_LM_LMAPS); + } + + outWadFile.InitForWrite(); + outWadFile.CopyLumpsFromWadFile(wadFile, ignoreLumps); + outWadFile.AddLump(Va("LM_MAP%02d", wadFile.currentmap), 0, NULL); + + builder.AddLightGridLump(outWadFile); + builder.AddLightmapLumps(outWadFile); + + printf("------------- Writing %s -------------\n\n", wadFile.wadName.c_str()); + outWadFile.Write(wadFile.wadName); + outWadFile.Close(); + wadFile.Close(); + + printf("----------------- Shutting down -----------------\n\n"); + Mem_Purge(hb_static); + + int proctime = (int)GetSeconds() - starttime; + printf("\nBuild time: %d:%02d:%02d\n", + proctime / 3600, (proctime / 60) % 60, proctime % 60); + + return 0; +} + +#endif