Files
UnrealEngine/Engine/Source/Programs/UnrealLightmass/Private/CPUSolver/CPUSolver.cpp
2025-05-18 13:04:45 +08:00

233 lines
8.5 KiB
C++

// 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