You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
520 lines
15 KiB
C++
520 lines
15 KiB
C++
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================
|
|
UnrealFrontendMain.cpp: Implements the UnrealFrontend application's main loop.
|
|
=============================================================================*/
|
|
|
|
#include "UnrealFrontendMain.h"
|
|
|
|
#include "RequiredProgramMainCPPInclude.h"
|
|
#include "AutomationController.h"
|
|
#include "DeviceManager.h"
|
|
#include "ProfilerClient.h"
|
|
#include "SessionFrontend.h"
|
|
#include "StatsData.h"
|
|
#include "StatsFile.h"
|
|
#include "EditorStyle.h"
|
|
#include "SlateReflector.h"
|
|
|
|
|
|
IMPLEMENT_APPLICATION(UnrealFrontend, "UnrealFrontend");
|
|
|
|
#define IDEAL_FRAMERATE 60;
|
|
|
|
namespace WorkspaceMenu
|
|
{
|
|
TSharedRef<FWorkspaceItem> DeveloperTools = FWorkspaceItem::NewGroup( NSLOCTEXT("UnrealFrontend", "DeveloperToolsMenu", "Developer Tools") );
|
|
}
|
|
|
|
|
|
void RunPackageCommand()
|
|
{
|
|
FString SourceDir;
|
|
FParse::Value(FCommandLine::Get(), TEXT("-SOURCEDIR="), SourceDir);
|
|
|
|
ITargetPlatformManagerModule* TPM = GetTargetPlatformManager();
|
|
if (TPM)
|
|
{
|
|
const TArray<ITargetPlatform*>& Platforms = TPM->GetActiveTargetPlatforms();
|
|
|
|
for (int32 Index = 0; Index < Platforms.Num(); ++Index)
|
|
{
|
|
if (Platforms[Index]->PackageBuild(SourceDir))
|
|
{
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
bool RunDeployCommand()
|
|
{
|
|
bool bDeployed = false;
|
|
|
|
// get the target device
|
|
FString Device;
|
|
FParse::Value(FCommandLine::Get(), TEXT("-DEVICE="), Device);
|
|
|
|
// get the file manifest
|
|
FString Manifest;
|
|
FParse::Value(FCommandLine::Get(), TEXT("-MANIFEST="), Manifest);
|
|
|
|
FString SourceDir;
|
|
FParse::Value(FCommandLine::Get(), TEXT("-SOURCEDIR="), SourceDir);
|
|
|
|
ITargetPlatformManagerModule* TPM = GetTargetPlatformManager();
|
|
if (!TPM)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Initialize the messaging subsystem so we can do device discovery.
|
|
FModuleManager::LoadModuleChecked<IMessagingModule>("Messaging");
|
|
|
|
// load plug-in modules
|
|
// @todo: allow for better plug-in support in standalone Slate apps
|
|
IPluginManager::Get().LoadModulesForEnabledPlugins(ELoadingPhase::PreDefault);
|
|
|
|
double DeltaTime = 0.0;
|
|
double LastTime = FPlatformTime::Seconds();
|
|
static int32 MasterDisableChangeTagStartFrame = -1;
|
|
|
|
// We track the message sent time because we have to keep updating the loop until the message is *actually sent*. (ie all packets queued, sent, buffer flushed, etc.)
|
|
double MessageSentTime = 0.0;
|
|
bool bMessageSent = false;
|
|
while ( !GIsRequestingExit && (MessageSentTime > LastTime + 1.0 || MessageSentTime <= 0.1) )
|
|
{
|
|
FTaskGraphInterface::Get().ProcessThreadUntilIdle(ENamedThreads::GameThread);
|
|
FTicker::GetCoreTicker().Tick(DeltaTime);
|
|
FPlatformProcess::Sleep(0);
|
|
|
|
DeltaTime = FPlatformTime::Seconds() - LastTime;
|
|
LastTime = FPlatformTime::Seconds();
|
|
|
|
if ( !bMessageSent )
|
|
{
|
|
const TArray<ITargetPlatform*>& Platforms = TPM->GetActiveTargetPlatforms();
|
|
|
|
FString Platform;
|
|
FString DeviceName;
|
|
Device.Split(TEXT("@"), &Platform, &DeviceName);
|
|
FTargetDeviceId DeviceId(Platform, DeviceName);
|
|
ITargetDevicePtr TargetDevice;
|
|
for (int32 Index = 0; Index < Platforms.Num(); ++Index)
|
|
{
|
|
TargetDevice = Platforms[Index]->GetDevice(DeviceId);
|
|
if (TargetDevice.IsValid())
|
|
{
|
|
FString OutId;
|
|
if (Platforms[Index]->PackageBuild(SourceDir))
|
|
{
|
|
if (TargetDevice->Deploy(SourceDir, OutId))
|
|
{
|
|
bDeployed = true;
|
|
}
|
|
MessageSentTime = LastTime;
|
|
bMessageSent = true;
|
|
}
|
|
else
|
|
{
|
|
MessageSentTime = LastTime;
|
|
bMessageSent = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return bDeployed;
|
|
}
|
|
|
|
|
|
bool RunLaunchCommand(const FString& Params)
|
|
{
|
|
bool bLaunched = false;
|
|
|
|
// get the target device
|
|
FString Device;
|
|
FParse::Value(FCommandLine::Get(), TEXT("-DEVICE="), Device);
|
|
|
|
// get the executable to launch
|
|
FString Executable;
|
|
FParse::Value(FCommandLine::Get(), TEXT("-EXE="), Executable);
|
|
|
|
ITargetPlatformManagerModule* TPM = GetTargetPlatformManager();
|
|
if (!TPM)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Initialize the messaging subsystem so we can do device discovery.
|
|
FModuleManager::LoadModuleChecked<IMessagingModule>("Messaging");
|
|
|
|
// load plug-in modules
|
|
// @todo: allow for better plug-in support in standalone Slate apps
|
|
IPluginManager::Get().LoadModulesForEnabledPlugins(ELoadingPhase::PreDefault);
|
|
|
|
double DeltaTime = 0.0;
|
|
double LastTime = FPlatformTime::Seconds();
|
|
static int32 MasterDisableChangeTagStartFrame = -1;
|
|
|
|
// We track the message sent time because we have to keep updating the loop until the message is *actually sent*. (ie all packets queued, sent, buffer flushed, etc.)
|
|
double MessageSentTime = 0.0;
|
|
bool bMessageSent = false;
|
|
while ( !GIsRequestingExit && (MessageSentTime > LastTime + 1.0 || MessageSentTime <= 0.1) )
|
|
{
|
|
FTaskGraphInterface::Get().ProcessThreadUntilIdle(ENamedThreads::GameThread);
|
|
FTicker::GetCoreTicker().Tick(DeltaTime);
|
|
FPlatformProcess::Sleep(0);
|
|
|
|
DeltaTime = FPlatformTime::Seconds() - LastTime;
|
|
LastTime = FPlatformTime::Seconds();
|
|
|
|
if ( !bMessageSent )
|
|
{
|
|
const TArray<ITargetPlatform*>& Platforms = TPM->GetActiveTargetPlatforms();
|
|
|
|
FString Platform;
|
|
FString DeviceName;
|
|
Device.Split(TEXT("@"), &Platform, &DeviceName);
|
|
FTargetDeviceId DeviceId(Platform, DeviceName);
|
|
ITargetDevicePtr TargetDevice;
|
|
for (int32 Index = 0; Index < Platforms.Num(); ++Index)
|
|
{
|
|
TargetDevice = Platforms[Index]->GetDevice(DeviceId);
|
|
if (TargetDevice.IsValid())
|
|
{
|
|
uint32 OutId;
|
|
if (TargetDevice->Run(Executable, Params, &OutId))
|
|
{
|
|
MessageSentTime = LastTime;
|
|
bMessageSent = true;
|
|
bLaunched = true;
|
|
}
|
|
else
|
|
{
|
|
MessageSentTime = LastTime;
|
|
bMessageSent = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return bLaunched;
|
|
}
|
|
|
|
|
|
void WriteString(FArchive* File, const ANSICHAR* Format, ...)
|
|
{
|
|
if( File != NULL )
|
|
{
|
|
check(File);
|
|
ANSICHAR Array[1024];
|
|
va_list ArgPtr;
|
|
va_start(ArgPtr,Format);
|
|
// Build the string
|
|
int32 Result = FCStringAnsi::GetVarArgs(Array,ARRAY_COUNT(Array),ARRAY_COUNT(Array)-1,Format,ArgPtr);
|
|
// Now write that to the file
|
|
File->Serialize((void*)Array,Result);
|
|
}
|
|
}
|
|
|
|
|
|
void RunStatsConvertCommand()
|
|
{
|
|
// get the target file
|
|
FString TargetFile;
|
|
FParse::Value(FCommandLine::Get(), TEXT("-INFILE="), TargetFile);
|
|
FString OutFile;
|
|
FParse::Value(FCommandLine::Get(), TEXT("-OUTFILE="), OutFile);
|
|
FString StatListString;
|
|
FParse::Value(FCommandLine::Get(), TEXT("-STATLIST="), StatListString);
|
|
|
|
// get the list of stats
|
|
TArray<FString> StatList;
|
|
if (StatListString.ParseIntoArray(&StatList, TEXT("+"), true) == 0)
|
|
{
|
|
StatList.Add(TEXT("STAT_FrameTime"));
|
|
}
|
|
|
|
// open a csv file for write
|
|
TAutoPtr<FArchive> FileWriter( IFileManager::Get().CreateFileWriter( *OutFile ) );
|
|
if (!FileWriter)
|
|
{
|
|
UE_LOG( LogStats, Error, TEXT( "Could not open output file: %s" ), *OutFile );
|
|
return;
|
|
}
|
|
|
|
// @TODO yrx 2014-03-24 move to function
|
|
// attempt to read the data and convert to csv
|
|
const int64 Size = IFileManager::Get().FileSize( *TargetFile );
|
|
if( Size < 4 )
|
|
{
|
|
UE_LOG( LogStats, Error, TEXT( "Could not open input file: %s" ), *TargetFile );
|
|
return;
|
|
}
|
|
|
|
TAutoPtr<FArchive> FileReader( IFileManager::Get().CreateFileReader( *TargetFile ) );
|
|
if( !FileReader )
|
|
{
|
|
UE_LOG( LogStats, Error, TEXT( "Could not open input file: %s" ), *TargetFile );
|
|
return;
|
|
}
|
|
|
|
FStatsReadStream Stream;
|
|
if( !Stream.ReadHeader( *FileReader ) )
|
|
{
|
|
UE_LOG( LogStats, Error, TEXT( "Could not open input file, bad magic: %s" ), *TargetFile );
|
|
return;
|
|
}
|
|
|
|
// This is not supported yet.
|
|
if (Stream.Header.bRawStatsFile)
|
|
{
|
|
UE_LOG( LogStats, Error, TEXT( "Could not open input file, not supported type (raw): %s" ), *TargetFile );
|
|
return;
|
|
}
|
|
|
|
// output the csv header
|
|
WriteString( FileWriter, "Frame,Name,Value\r\n" );
|
|
|
|
// read in the data
|
|
TArray<FStatMessage> Messages;
|
|
FStatsThreadState ThreadState;
|
|
while(FileReader->Tell() < FileReader->TotalSize())
|
|
{
|
|
// read the message
|
|
FStatMessage Message(Stream.ReadMessage(*FileReader));
|
|
if (Message.NameAndInfo.GetShortName() != TEXT("Unknown FName"))
|
|
{
|
|
if (Message.NameAndInfo.GetField<EStatOperation>() == EStatOperation::AdvanceFrameEventGameThread)
|
|
{
|
|
new (Messages) FStatMessage(Message);
|
|
ThreadState.AddMessages(Messages);
|
|
Messages.Reset(Messages.Num());
|
|
|
|
// get the frame time and the render time
|
|
if (ThreadState.CurrentGameFrame > 1)
|
|
{
|
|
// get the thread stats
|
|
TArray<FStatMessage> Stats;
|
|
ThreadState.GetInclusiveAggregateStackStats(ThreadState.CurrentGameFrame, Stats);
|
|
for (int32 Index = 0; Index < Stats.Num(); ++Index)
|
|
{
|
|
FStatMessage const& Meta = Stats[Index];
|
|
// UE_LOG(LogTemp, Display, TEXT("Stat: %s"), *Meta.NameAndInfo.GetShortName().ToString());
|
|
|
|
for (int32 Jndex = 0; Jndex < StatList.Num(); ++Jndex)
|
|
{
|
|
if (Meta.NameAndInfo.GetShortName().ToString() == StatList[Jndex])
|
|
{
|
|
float CycleTime = 0.0f;
|
|
if (Meta.NameAndInfo.GetFlag(EStatMetaFlags::IsPackedCCAndDuration))
|
|
{
|
|
CycleTime = FPlatformTime::ToMilliseconds( FromPackedCallCountDuration_Duration(Meta.GetValue_int64()) );
|
|
}
|
|
else if (Meta.NameAndInfo.GetFlag(EStatMetaFlags::IsCycle))
|
|
{
|
|
CycleTime = FPlatformTime::ToMilliseconds( Meta.GetValue_int64() );
|
|
}
|
|
else
|
|
{
|
|
CycleTime = Meta.GetValue_int64();
|
|
}
|
|
|
|
// write out to the csv file
|
|
WriteString(FileWriter, "%d,%S,%f\r\n", ThreadState.CurrentGameFrame, *StatList[Jndex], CycleTime);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
new (Messages) FStatMessage(Message);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void RunUI()
|
|
{
|
|
FString UnrealFrontendLayoutIni = FPaths::GetPath(GEngineIni) + "/Layout.ini";
|
|
|
|
FSlateApplication::InitializeAsStandaloneApplication(GetStandardStandaloneRenderer());
|
|
|
|
// The frontend currently relies on EditorStyle being loaded
|
|
FModuleManager::LoadModuleChecked<IEditorStyleModule>("EditorStyle");
|
|
|
|
// initialize messaging subsystem
|
|
FModuleManager::LoadModuleChecked<IMessagingModule>("Messaging");
|
|
|
|
// load plug-in modules
|
|
// @todo: allow for better plug-in support in standalone Slate apps
|
|
IPluginManager::Get().LoadModulesForEnabledPlugins(ELoadingPhase::PreDefault);
|
|
|
|
// initialize automation tests
|
|
IAutomationControllerModule& AutomationControllerModule = FModuleManager::LoadModuleChecked<IAutomationControllerModule>("AutomationController");
|
|
AutomationControllerModule.Init();
|
|
|
|
// initialize profiler
|
|
FModuleManager::Get().LoadModule("ProfilerClient");
|
|
|
|
// initialize user interface
|
|
FModuleManager::Get().LoadModule("DeviceManager");
|
|
FModuleManager::Get().LoadModule("SessionFrontend");
|
|
FModuleManager::Get().LoadModule("SessionLauncher");
|
|
FModuleManager::Get().LoadModule("SettingsEditor");
|
|
|
|
// Create developer tools menu with widget reflector.
|
|
FModuleManager::LoadModuleChecked<ISlateReflectorModule>("SlateReflector").RegisterTabSpawner(WorkspaceMenu::DeveloperTools);
|
|
|
|
// check to see if want the widget reflector
|
|
const bool bAllowDebugTools = FParse::Param(FCommandLine::Get(), TEXT("DebugTools"));
|
|
|
|
// set the application name
|
|
FGlobalTabmanager::Get()->SetApplicationTitle(NSLOCTEXT("UnrealFrontend", "AppTitle", "Unreal Frontend"));
|
|
|
|
// restore application layout
|
|
TSharedRef<FTabManager::FLayout> NewLayout = FTabManager::NewLayout("SessionFrontendLayout_v1.1")
|
|
->AddArea
|
|
(
|
|
FTabManager::NewArea(1280.f, 720.0f)
|
|
->Split
|
|
(
|
|
FTabManager::NewStack()
|
|
->AddTab(FName("DeviceManager"), ETabState::OpenedTab)
|
|
->AddTab(FName("MessagingDebugger"), ETabState::ClosedTab)
|
|
->AddTab(FName("SessionLauncher"), ETabState::OpenedTab)
|
|
->AddTab(FName("SessionFrontend"), ETabState::OpenedTab)
|
|
)
|
|
)
|
|
->AddArea
|
|
(
|
|
FTabManager::NewArea(600.0f, 600.0f)
|
|
->SetWindow(FVector2D(10.0f, 10.0f), false)
|
|
->Split
|
|
(
|
|
FTabManager::NewStack()->AddTab("WidgetReflector", bAllowDebugTools ? ETabState::OpenedTab : ETabState::ClosedTab)
|
|
)
|
|
);
|
|
|
|
TSharedRef<FTabManager::FLayout> UserConfiguredNewLayout = FLayoutSaveRestore::LoadFromConfig(UnrealFrontendLayoutIni, NewLayout);
|
|
FGlobalTabmanager::Get()->RestoreFrom(UserConfiguredNewLayout, TSharedPtr<SWindow>());
|
|
|
|
// enter main loop
|
|
double DeltaTime = 0.0;
|
|
double LastTime = FPlatformTime::Seconds();
|
|
const float IdealFrameTime = 1.0f / IDEAL_FRAMERATE;
|
|
static int32 MasterDisableChangeTagStartFrame = -1;
|
|
|
|
while (!GIsRequestingExit)
|
|
{
|
|
//Save the state of the tabs here rather than after close of application (the tabs are undesirably saved out with ClosedTab state on application close)
|
|
//UserConfiguredNewLayout = FGlobalTabmanager::Get()->PersistLayout();
|
|
|
|
FTaskGraphInterface::Get().ProcessThreadUntilIdle(ENamedThreads::GameThread);
|
|
|
|
FSlateApplication::Get().PumpMessages();
|
|
FSlateApplication::Get().Tick();
|
|
FTicker::GetCoreTicker().Tick(DeltaTime);
|
|
AutomationControllerModule.Tick();
|
|
|
|
// throttle frame rate
|
|
FPlatformProcess::Sleep(FMath::Max<float>(0.0f, IdealFrameTime - (FPlatformTime::Seconds() - LastTime)));
|
|
|
|
double CurrentTime = FPlatformTime::Seconds();
|
|
DeltaTime = CurrentTime - LastTime;
|
|
LastTime = CurrentTime;
|
|
|
|
#if STATS
|
|
FThreadStats::ExplicitFlush();
|
|
FThreadStats::WaitForStats();
|
|
MasterDisableChangeTagStartFrame = FThreadStats::MasterDisableChangeTag();
|
|
#endif
|
|
|
|
GLog->FlushThreadedLogs();
|
|
}
|
|
|
|
// save application layout
|
|
FLayoutSaveRestore::SaveToConfig(UnrealFrontendLayoutIni, UserConfiguredNewLayout);
|
|
GConfig->Flush(false, UnrealFrontendLayoutIni);
|
|
|
|
// shut down application
|
|
FSlateApplication::Shutdown();
|
|
}
|
|
|
|
|
|
int32 UnrealFrontendMain( const TCHAR* CommandLine )
|
|
{
|
|
// check to see if we should run in command line mode
|
|
FString Command;
|
|
FString Params;
|
|
FString NewCommandLine = CommandLine;
|
|
|
|
bool bRunCommand = FParse::Value(*NewCommandLine, TEXT("-RUN="), Command);
|
|
|
|
if (bRunCommand)
|
|
{
|
|
// extract off any -PARAM= parameters so that they aren't accidentally parsed by engine init
|
|
FParse::Value(*NewCommandLine, TEXT("-PARAMS="), Params);
|
|
if (Params.Len() > 0)
|
|
{
|
|
// remove from the command line
|
|
NewCommandLine = NewCommandLine.Replace(*Params, TEXT(""));
|
|
|
|
// trim the quotes
|
|
Params = Params.TrimQuotes();
|
|
}
|
|
}
|
|
|
|
if (!FParse::Param(*NewCommandLine, TEXT("-Messaging")))
|
|
{
|
|
NewCommandLine += TEXT(" -Messaging");
|
|
}
|
|
|
|
GEngineLoop.PreInit(*NewCommandLine);
|
|
|
|
// Tell the module manager is may now process newly-loaded UObjects when new C++ modules are loaded
|
|
FModuleManager::Get().StartProcessingNewlyLoadedObjects();
|
|
|
|
bool bReturnValue = true;
|
|
if (bRunCommand)
|
|
{
|
|
if (Command.Equals(TEXT("PACKAGE"), ESearchCase::IgnoreCase))
|
|
{
|
|
RunPackageCommand();
|
|
}
|
|
else if (Command.Equals(TEXT("DEPLOY"), ESearchCase::IgnoreCase))
|
|
{
|
|
bReturnValue = RunDeployCommand();
|
|
}
|
|
else if (Command.Equals(TEXT("LAUNCH"), ESearchCase::IgnoreCase))
|
|
{
|
|
bReturnValue = RunLaunchCommand(Params);
|
|
}
|
|
else if (Command.Equals(TEXT("CONVERT"), ESearchCase::IgnoreCase))
|
|
{
|
|
RunStatsConvertCommand();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RunUI();
|
|
}
|
|
|
|
FEngineLoop::AppPreExit();
|
|
FModuleManager::Get().UnloadModulesAtShutdown();
|
|
|
|
#if STATS
|
|
FThreadStats::StopThread();
|
|
#endif
|
|
|
|
FTaskGraphInterface::Shutdown();
|
|
|
|
return bReturnValue ? 0 : -1;
|
|
}
|