// Copyright Epic Games, Inc. All Rights Reserved. #include "CPUSolver.h" #include "LightmassScene.h" #include "Importer.h" #include "Exporter.h" #include "LightmassSwarm.h" #include "LightingSystem.h" #include "HAL/PlatformProcess.h" #include "HAL/FileManager.h" #include "UnrealLightmass.h" #include "Misc/OutputDeviceRedirector.h" namespace Lightmass { /** Global statistics */ FGlobalStatistics GStatistics; /** Global Swarm instance. */ FLightmassSwarm* GSwarm = NULL; /** Whether we should report detailed stats back to Unreal. */ bool GReportDetailedStats = false; /** Whether Lightmass is running in debug mode (-debug), using a hardcoded job and not requesting tasks from Swarm. */ bool GDebugMode = false; /** How many tasks to prefetch per worker thread. */ float GNumTasksPerThreadPrefetch = 1.0f; /** Report statistics back to Unreal. */ void ReportStatistics() { if ( GReportDetailedStats ) { double RequestTime = GStatistics.ThreadStatistics.RequestTime + GStatistics.ThreadStatistics.RequestTimeoutTime; double TrackedTime = RequestTime + GStatistics.ThreadStatistics.TextureMappingTime + GStatistics.ThreadStatistics.ExportTime; double UnTrackedTime = GStatistics.ThreadStatistics.TotalTime - TrackedTime; // Send back detailed information to the Unreal log. GSwarm->SendTextMessage( TEXT("Lightmass on %s: %s total, %s importing, %s setup, %s photons, %s processing, %s extra exporting [%d/%d mappings].\n") TEXT(" Threads: %d threads, %.0f total thread seconds (out of %.0f available)\n") TEXT(" - %6.2f%% %7.1fs Requesting tasks\n") TEXT(" ---> %6.2f%% %7.1fs Requesting tasks from Swarm\n") TEXT(" - %6.2f%% %7.1fs Processing texture mappings\n") TEXT(" - %6.2f%% %7.1fs Exporting %d mappings\n") TEXT(" - %6.2f%% %7.1fs Untracked thread time\n") TEXT("\n") , FPlatformProcess::ComputerName() , *FPlatformTime::PrettyTime(GStatistics.TotalTimeEnd - GStatistics.TotalTimeStart) , *FPlatformTime::PrettyTime(GStatistics.ImportTimeEnd - GStatistics.ImportTimeStart) , *FPlatformTime::PrettyTime(GStatistics.SceneSetupTime) , *FPlatformTime::PrettyTime(GStatistics.PhotonsEnd - GStatistics.PhotonsStart) , *FPlatformTime::PrettyTime(GStatistics.WorkTimeEnd - GStatistics.WorkTimeStart) , *FPlatformTime::PrettyTime(GStatistics.ExtraExportTime) , GStatistics.ThreadStatistics.NumTextureMappings , GStatistics.NumTotalMappings , GStatistics.NumThreads , GStatistics.ThreadStatistics.TotalTime , (GStatistics.WorkTimeEnd - GStatistics.WorkTimeStart) * GStatistics.NumThreads , RequestTime / GStatistics.ThreadStatistics.TotalTime * 100.0 , RequestTime , GStatistics.ThreadStatistics.SwarmRequestTime / GStatistics.ThreadStatistics.TotalTime * 100.0 , GStatistics.ThreadStatistics.SwarmRequestTime , GStatistics.ThreadStatistics.TextureMappingTime / GStatistics.ThreadStatistics.TotalTime * 100.0 , GStatistics.ThreadStatistics.TextureMappingTime , GStatistics.ThreadStatistics.ExportTime / GStatistics.ThreadStatistics.TotalTime * 100.0 , GStatistics.ThreadStatistics.ExportTime , GStatistics.NumExportedMappings , UnTrackedTime / GStatistics.ThreadStatistics.TotalTime * 100.0 , UnTrackedTime ); GSwarm->SendTextMessage( TEXT(" Read amount: %3.2fMB (%.3f sec, %d calls)\n") TEXT(" Write amount: %3.2fMB (%.3f sec, %d calls)\n") , (double)GSwarm->GetTotalBytesRead() / 1000.0 / 1000.0 , GSwarm->GetTotalSecondsRead() , GSwarm->GetTotalNumReads() , (double)GSwarm->GetTotalBytesWritten() / 1000.0 / 1000.0 , GSwarm->GetTotalSecondsWritten() , GSwarm->GetTotalNumWrites() ); if ( GDebugMode == false ) { UE_LOG(LogLightmass, Log, TEXT("Time in SendMessage() = %s"), *FPlatformTime::PrettyTime(GStatistics.SendMessageTime) ); UE_LOG(LogLightmass, Log, TEXT("Task request roundtrip = %s"), *FPlatformTime::PrettyTime(FTiming::GetAverageTiming()) ); } } else { // Send back timing information to the Unreal log. GSwarm->SendTextMessage( TEXT("Lightmass on %s: %s total, %s importing, %s setup, %s photons, %s processing, %s extra exporting [%d/%d mappings]. Threads: %s total, %s processing.") , FPlatformProcess::ComputerName() , *FPlatformTime::PrettyTime(GStatistics.TotalTimeEnd - GStatistics.TotalTimeStart) , *FPlatformTime::PrettyTime(GStatistics.ImportTimeEnd - GStatistics.ImportTimeStart) , *FPlatformTime::PrettyTime(GStatistics.SceneSetupTime) , *FPlatformTime::PrettyTime(GStatistics.PhotonsEnd - GStatistics.PhotonsStart) , *FPlatformTime::PrettyTime(GStatistics.WorkTimeEnd - GStatistics.WorkTimeStart) , *FPlatformTime::PrettyTime(GStatistics.ExtraExportTime) , GStatistics.ThreadStatistics.NumTextureMappings , GStatistics.NumTotalMappings , *FPlatformTime::PrettyTime(GStatistics.ThreadStatistics.TotalTime) , *FPlatformTime::PrettyTime(GStatistics.ThreadStatistics.TextureMappingTime) ); } } /** Transfers back the current log file to the instigator. */ void ReportLogFile() { GLog->Flush(); // Get the log filename and replace ".log" with "_Result.log" FString LogFilename = FLightmassLog::Get()->GetLogFilename(); FString ChannelName = LogFilename.Left( LogFilename.Len() - 4 ) + TEXT("_Result.log"); FArchive* File = IFileManager::Get().CreateFileReader(*LogFilename); bool bIsOk = false; if ( File != NULL ) { int64 Filesize = File->TotalSize(); int32 Channel = GSwarm->OpenChannel( *ChannelName, NSwarm::SWARM_JOB_CHANNEL_WRITE, true ); if ( Channel >= 0 ) { static char Buffer[4096]; bIsOk = true; while ( Filesize > 0 ) { int64 NumBytesToRead = FMath::Min( Filesize, 4096LL ); File->Serialize( Buffer, NumBytesToRead ); GSwarm->Write( Buffer, NumBytesToRead ); Filesize -= NumBytesToRead; } GSwarm->PopChannel( true ); } delete File; } if ( !bIsOk ) { UE_LOG(LogLightmass, Log, TEXT("Failed to send back log file through Swarm!")); } } /** * Entry point for starting the static lighting process * * @param SceneGuid Guid of the scene to process * @param NumThreads Number of concurrent threads to use for lighting building * @param bDumpTextures If true, 2d lightmaps will be dumped too */ void BuildStaticLighting(const FGuid& SceneGuid, int32 NumThreads, bool bDumpTextures) { // Place a marker in the memory profile data. UE::Private::GMalloc->Exec(NULL, TEXT("SNAPSHOTMEMORY"), *GLog); UE_LOG(LogLightmass, Log, TEXT("Building static lighting...")); // time it double SetupTimeStart = FPlatformTime::Seconds(); // Start initializing GCPUFrequency. StartInitCPUFrequency(); // Startup Swarm. GStatistics.ImportTimeStart = FPlatformTime::Seconds(); NSwarm::FSwarmInterface::Initialize(*(FString(FPlatformProcess::BaseDir()) + TEXT("..\\DotNET\\SwarmInterface.dll"))); check(&NSwarm::FSwarmInterface::Get() != NULL); GSwarm = new FLightmassSwarm( NSwarm::FSwarmInterface::Get(), SceneGuid, FMath::TruncToInt(GNumTasksPerThreadPrefetch*NumThreads) ); GSwarm->SendMessage( NSwarm::FTimingMessage( NSwarm::PROGSTATE_BeginJob, -1 ) ); FLightmassImporter Importer( GSwarm ); FScene Scene; if( !Importer.ImportScene( Scene, SceneGuid ) ) { UE_LOG(LogLightmass, Log, TEXT("Failed to import scene file")); exit( 1 ); } GStatistics.ImportTimeEnd = FPlatformTime::Seconds(); // Finish initializing GCPUFrequency. FinishInitCPUFrequency(); // setup the desired lighting options FLightingBuildOptions LightingOptions; FLightmassSolverExporter Exporter( GSwarm, Scene, bDumpTextures ); // Place a marker in the memory profile data. UE::Private::GMalloc->Exec(NULL, TEXT("SNAPSHOTMEMORY"), *GLog); double LightTimeStart = FPlatformTime::Seconds(); // Create the global lighting system to kick off the processing FStaticLightingSystem LightingSystem(LightingOptions, Scene, Exporter, NumThreads ); GStatistics.TotalTimeEnd = FPlatformTime::Seconds(); // Place a marker in the memory profile data. UE::Private::GMalloc->Exec(NULL, TEXT("SNAPSHOTMEMORY"), *GLog); // Report back statistics over Swarm. ReportStatistics(); double EndTime = FPlatformTime::Seconds(); UE_LOG(LogLightmass, Log, TEXT("Lighting complete [Startup = %s, Lighting = %s]"), *FPlatformTime::PrettyTime(LightTimeStart - SetupTimeStart), *FPlatformTime::PrettyTime(EndTime - LightTimeStart)); if (GReportDetailedStats) { LightingSystem.GetAggregateMesh().DumpPostBuildStats(); } // Transfer back the log to the instigator. ReportLogFile(); // Shutdown Swarm FLightmassSwarm* Swarm = GSwarm; GSwarm = NULL; delete Swarm; // Write out memory profiling data to the .mprof file. UE::Private::GMalloc->Exec(NULL, TEXT("DUMPALLOCSTOFILE"), *GLog); } } //namespace Lightmass