#include "IronsGame.h" #include "IronsPerformanceSubsystem.h" #include "IronsPerformanceOverlay.h" #include "Misc/CommandLine.h" #include "Misc/Parse.h" #include "Misc/FileHelper.h" #include "Misc/Paths.h" #include "HAL/PlatformTime.h" #include "HighResScreenshot.h" #include "Engine/GameViewportClient.h" #include "UnrealClient.h" #include "Kismet/KismetSystemLibrary.h" #include "Serialization/JsonSerializer.h" #include "UObject/UObjectIterator.h" #include "Containers/Ticker.h" void AIronsController::TickPerformanceAcceptance(float Dt) { #if !UE_BUILD_SHIPPING auto H=Host();if(!H||!H->bReady||!H->DataRoot.Contains(TEXT("/QA/")))return; FString Scenario,Folder; if(!FParse::Value(FCommandLine::Get(),TEXT("IronsporeQAScenario="),Scenario)||!Scenario.StartsWith(TEXT("performance"))|| !FParse::Value(FCommandLine::Get(),TEXT("IronsporeQAEvidence="),Folder)||!Folder.Contains(TEXT("/Docs/QA/")))return; const double Now=FPlatformTime::Seconds();if(PerformanceLastClock>0)AcceptanceClock+=Now-PerformanceLastClock;PerformanceLastClock=Now; if(AcceptanceClock<2.5f)return; const int32 Step=H->AcceptanceStep;auto Metrics=H->GetSubsystem(); auto Advance=[&](){++H->AcceptanceStep;AcceptanceClock=0;}; auto Action=[&](const TCHAR* Cmd){FIronsHitRegion Hit;Hit.Command=Cmd;Screen->Dispatch(Hit);}; auto Quit=[&](){UKismetSystemLibrary::QuitGame(this,this,EQuitPreference::Quit,false);}; auto Capture=[&](const TCHAR* Name){ auto O=MakeShared();O->SetStringField(TEXT("name"),Name);O->SetStringField(TEXT("page"),Page);O->SetStringField(TEXT("map"),GetWorld()->GetMapName()); O->SetBoolField(TEXT("enabled"),H->bShowPerformance);O->SetBoolField(TEXT("english"),H->bEnglish);O->SetNumberField(TEXT("corner"),H->PerformanceCorner); O->SetBoolField(TEXT("worldPaused"),GetWorld()->IsPaused());O->SetNumberField(TEXT("saveVersion"),H->Core().state().version); int32 VX=0,VY=0;GetViewportSize(VX,VY);O->SetNumberField(TEXT("width"),VX);O->SetNumberField(TEXT("height"),VY); int32 Overlays=0;for(TObjectIterator It;It;++It)if(It->GetWorld()==GetWorld()&&It->IsInViewport())++Overlays;O->SetNumberField(TEXT("overlayInstances"),Overlays); auto Numbers=[](const FIronsPerformanceSnapshot& S){auto J=MakeShared();J->SetNumberField(TEXT("fps"),S.Fps);J->SetNumberField(TEXT("frameMs"),S.FrameMs);J->SetNumberField(TEXT("gameMs"),S.GameMs);J->SetNumberField(TEXT("renderMs"),S.RenderMs);J->SetNumberField(TEXT("rhiMs"),S.RhiMs);J->SetNumberField(TEXT("gpuMs"),S.GpuMs);J->SetNumberField(TEXT("samples"),S.Samples);J->SetNumberField(TEXT("revision"),static_cast(S.Revision));return J;}; if(Metrics){O->SetObjectField(TEXT("metrics"),Numbers(Metrics->GetSnapshot()));O->SetObjectField(TEXT("raw"),Numbers(Metrics->GetRawSample()));} if(auto V=GetWorld()->GetGameViewport();V&&V->GetStatUnitData()){const auto* U=V->GetStatUnitData();auto J=MakeShared();J->SetNumberField(TEXT("frameMs"),U->RawFrameTime);J->SetNumberField(TEXT("gameMs"),U->RawGameThreadTime);J->SetNumberField(TEXT("renderMs"),U->RawRenderThreadTime);J->SetNumberField(TEXT("rhiMs"),U->RawRHITTime);J->SetNumberField(TEXT("gpuMs"),U->RawGPUFrameTime[0]);O->SetObjectField(TEXT("nativeStatUnitRaw"),J);} FString Json;FJsonSerializer::Serialize(O,TJsonWriterFactory<>::Create(&Json));FFileHelper::SaveStringToFile(Json,*(Folder/(FString(Name)+TEXT(".json"))),FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM); // Allow new-language glyph uploads and camera history to settle before capture. const FString Path=Folder/(FString(Name)+TEXT(".png")); FTSTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateLambda([Path](float){FScreenshotRequest::RequestScreenshot(Path,true,false);return false;}),.25f); }; if(Scenario==TEXT("performance-ui")){ if(Step==0){H->SetLanguage(false);Capture(TEXT("home-zh"));} else if(Step==1){SetPage(TEXT("hangar"));Capture(TEXT("hangar-zh"));} else if(Step==2){SetPage(TEXT("settings"));Capture(TEXT("settings-zh"));} else if(Step==3){H->SetLanguage(true);Capture(TEXT("settings-en"));} else {Quit();return;} Advance();return; } if(Scenario==TEXT("performance-restart")){ if(Step==0){SetPage(TEXT("settings"));Capture(TEXT("restart-disabled"));} else if(Step==1){Action(TEXT("performance"));Action(TEXT("performanceCorner"));H->SetLanguage(false);Capture(TEXT("restart-enabled"));} else if(Step==2){ConsoleCommand(TEXT("stat unit"),true);} else if(Step==3){Capture(TEXT("native-comparison"));} else {ConsoleCommand(TEXT("stat unit"),true);Quit();return;} Advance();return; } if(Scenario!=TEXT("performance"))return; if(Step==0){Capture(TEXT("home-default-zh"));} else if(Step==1){SetPage(TEXT("hangar"));Capture(TEXT("hangar-default-zh"));} else if(Step==2){SetPage(TEXT("settings"));Action(TEXT("performance"));Capture(TEXT("disabled-zh"));} else if(Step==3){Action(TEXT("performance"));Action(TEXT("performanceCorner"));H->SetLanguage(true);Capture(TEXT("top-left-en"));} else if(Step==4){Action(TEXT("performanceCorner"));SetPage(TEXT("home"));Capture(TEXT("bottom-right-en"));} else if(Step==5){Action(TEXT("performanceCorner"));SetPage(TEXT("settings"));Capture(TEXT("bottom-left-en"));} else if(Step==6){Action(TEXT("performanceCorner"));H->SetLanguage(false);Advance();Launch(true);return;} else if(Step==7){Capture(TEXT("training-shoulder"));} else if(Step==8){if(Mech())Mech()->SwitchView();Capture(TEXT("training-cockpit"));} else if(Step==9){SetPage(TEXT("pause"));PrimaryActorTick.bTickEvenWhenPaused=true;bShouldPerformFullTickWhenPaused=true;SetPause(true);Capture(TEXT("pause-begin"));} else if(Step==10){Capture(TEXT("pause-updating"));SetPause(false);bShouldPerformFullTickWhenPaused=false;PrimaryActorTick.bTickEvenWhenPaused=false;} else if(Step==11){Advance();ReturnHome();return;} else if(Step==12){Capture(TEXT("after-travel"));Action(TEXT("performance"));H->PerformanceCorner=3;H->SetLanguage(true);H->SavePreferences();} else {Quit();return;} Advance(); #endif }