perf: capture 1440p frame times; PASS the 60 FPS release check (#16)

Merged PR #16: 1440p perf capture (60 FPS check PASS)

Co-authored-by: Zwitschi <zwitschi82@gmail.com>
This commit was merged in pull request #16.
This commit is contained in:
zwitschi authored and zwitschi committed 2026-11-08 15:17:47 +01:00
1 parent 31f073d608
commit 4bc41e9faf
4 files changed
+939 -3

No files matched your search

@@ -14,6 +14,9 @@
#include "Kismet/GameplayStatics.h"
#include "Misc/CommandLine.h"
#include "Misc/Parse.h"
#include "Misc/FileHelper.h"
#include "Misc/Paths.h"
#include "HAL/FileManager.h"
// Development-only diagnostics for the walkable demo. These describe the spawn
// and input setup and are not needed in a shipped build's log spam; disable
@@ -192,6 +195,9 @@ void AGigLineCharacter::StartQASelfTest()
bool bResetReturned = false;
bool bPausedOn = false;
bool bUnpausedBack = false;
// Performance capture over the fixed walk route (1.0-3.0 s).
TArray<float> FrameMs;
bool bPerfLogged = false;
};
TSharedPtr<FQAState> State = MakeShared<FQAState>();
@@ -219,6 +225,8 @@ void AGigLineCharacter::StartQASelfTest()
if (bQATestWalking)
{
AddMovementInput(GetActorForwardVector(), 1.0f);
// Frame time for the perf capture (this ticker runs every frame).
State->FrameMs.Add(DeltaTime * 1000.0f);
}
// 3.0: stop and measure.
@@ -233,6 +241,57 @@ void AGigLineCharacter::StartQASelfTest()
TEXT("GigLine QA: walk moved %.0f cm in 2 s -> %s (Z %.0f -> %.0f)"),
Walked, State->bWalkMoved ? TEXT("PASS") : TEXT("FAIL"),
State->Start.Z, State->WalkEnd.Z);
// Perf: report frame-time stats over the fixed walk route.
if (!State->bPerfLogged && State->FrameMs.Num() > 10)
{
State->bPerfLogged = true;
TArray<float> Sorted = State->FrameMs;
Sorted.Sort();
const int32 N = Sorted.Num();
double Sum = 0.0;
for (float Ms : Sorted) { Sum += Ms; }
const float AvgMs = static_cast<float>(Sum / N);
const float P50 = Sorted[N / 2];
const float P95 = Sorted[FMath::Clamp((int32)(N * 0.95f), 0, N - 1)];
const float P99 = Sorted[FMath::Clamp((int32)(N * 0.99f), 0, N - 1)];
const float MaxMs = Sorted.Last();
FVector2D Res(0.0, 0.0);
if (GEngine && GEngine->GameViewport)
{
GEngine->GameViewport->GetViewportSize(Res);
}
// Record the upscaling/AA state so the check is reproducible.
auto CV = [](const TCHAR* Name)
{
IConsoleVariable* Var = IConsoleManager::Get().FindConsoleVariable(Name);
return Var ? Var->GetInt() : -999;
};
UE_LOG(LogTemp, Log,
TEXT("GigLine PERF: frames=%d avg=%.2fms (%.1f fps) p50=%.2f p95=%.2f p99=%.2f max=%.2f ms at %dx%d"),
N, AvgMs, AvgMs > 0.f ? 1000.f / AvgMs : 0.f, P50, P95, P99, MaxMs,
(int32)Res.X, (int32)Res.Y);
UE_LOG(LogTemp, Log,
TEXT("GigLine PERF: AntiAliasingMethod=%d ScreenPercentage=%d TSR=%d FrameGen=%d VSync=%d"),
CV(TEXT("r.AntiAliasingMethod")), CV(TEXT("r.ScreenPercentage")),
CV(TEXT("r.TSR.History.ScreenPercentage")),
CV(TEXT("r.Streamline.FrameGeneration.Mode")),
CV(TEXT("r.VSync")));
// Frame-time series, so the check has a graph source, not one sample.
{
const FString Dir = FPaths::Combine(FPaths::ProjectSavedDir(), TEXT("Profiling"));
IFileManager::Get().MakeDirectory(*Dir, true);
const FString Path = FPaths::Combine(Dir, TEXT("perf-walk-frametimes.csv"));
FString Csv(TEXT("frame,ms\n"));
for (int32 i = 0; i < N; ++i)
{
Csv += FString::Printf(TEXT("%d,%.3f\n"), i, State->FrameMs[i]);
}
FFileHelper::SaveStringToFile(Csv, *Path);
UE_LOG(LogTemp, Log, TEXT("GigLine PERF: frame times written to %s"), *Path);
}
}
}
// 3.5: reset should return to the spawn point.