Files
rougeGame/Source/UEAdapter/Private/IronsPerformanceSubsystem.cpp

77 lines
3.7 KiB
C++

#include "IronsPerformanceSubsystem.h"
#include "Engine/GameInstance.h"
#include "Engine/World.h"
#include "CoreGlobals.h"
#include "HAL/PlatformTime.h"
#include "RenderTimer.h"
#include "RHI.h"
#include "DynamicRHI.h"
#include "RHICommandList.h"
namespace {
bool Valid(double Value) { return FMath::IsFinite(Value)&&Value>=0; }
double CyclesMs(uint32 Cycles) { return Cycles>0?FPlatformTime::ToMilliseconds(Cycles):-1; }
}
void FIronsPerformanceWindow::Reset() { Samples.Reset(); }
void FIronsPerformanceWindow::Add(double End,double Seconds,double Game,double Render,double Rhi,double Gpu)
{
if(!FMath::IsFinite(End)||!FMath::IsFinite(Seconds)||Seconds<=0)return;
// Reject backwards clocks rather than mixing two timelines.
if(!Samples.IsEmpty()&&End<Samples.Last().End)Reset();
Samples.RemoveAll([End](const FSample& S){return S.End<=End-1.0;});
if(Samples.Num()>=2048)Samples.RemoveAt(0,1,EAllowShrinking::No);
Samples.Add({End,Seconds,Game,Render,Rhi,Gpu});
}
FIronsPerformanceSnapshot FIronsPerformanceWindow::Read(double Now) const
{
FIronsPerformanceSnapshot Result;
double Seconds=0,Sums[4]={};int32 Counts[4]={};
for(const auto& S:Samples){
if(S.End<=Now-1.0||S.End>Now)continue;
Seconds+=S.Seconds;++Result.Samples;
const double Values[]={S.Game,S.Render,S.Rhi,S.Gpu};
for(int32 I=0;I<4;++I)if(Valid(Values[I])){Sums[I]+=Values[I];++Counts[I];}
}
if(Result.Samples>0&&Seconds>0){Result.FrameMs=Seconds*1000/Result.Samples;Result.Fps=Result.Samples/Seconds;}
double* Values[]={&Result.GameMs,&Result.RenderMs,&Result.RhiMs,&Result.GpuMs};
for(int32 I=0;I<4;++I)if(Counts[I]>0)*Values[I]=Sums[I]/Counts[I];
// Do not keep showing an old GPU/RHI value after the source becomes unavailable.
if(!Samples.IsEmpty()){
const auto& Last=Samples.Last();
if(!Valid(Last.Game))Result.GameMs=-1;if(!Valid(Last.Render))Result.RenderMs=-1;
if(!Valid(Last.Rhi))Result.RhiMs=-1;if(!Valid(Last.Gpu))Result.GpuMs=-1;
}
return Result;
}
void UIronsPerformanceSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);bInitialized=true;
}
void UIronsPerformanceSubsystem::Deinitialize()
{
bInitialized=false;Window.Reset();SampleWorld.Reset();Super::Deinitialize();
}
bool UIronsPerformanceSubsystem::IsTickable() const { return bInitialized&&!IsTemplate(); }
UWorld* UIronsPerformanceSubsystem::GetTickableGameObjectWorld() const { return GetGameInstance()?GetGameInstance()->GetWorld():nullptr; }
TStatId UIronsPerformanceSubsystem::GetStatId() const { RETURN_QUICK_DECLARE_CYCLE_STAT(IronsPerformance,STATGROUP_Tickables); }
void UIronsPerformanceSubsystem::Tick(float DeltaTime)
{
if(LastFrame==GFrameCounter)return;LastFrame=GFrameCounter;
UWorld* World=GetTickableGameObjectWorld();const double Now=FPlatformTime::Seconds();
if(!World||SampleWorld.Get()!=World){
SampleWorld=World;Window.Reset();LastSampleTime=Now;LastPublishTime=Now;
const uint64 Revision=Snapshot.Revision+1;Snapshot={};Snapshot.Revision=Revision;Raw={};return;
}
const double Seconds=Now-LastSampleTime;LastSampleTime=Now;
Raw.FrameMs=Seconds*1000;Raw.Fps=Seconds>0?1/Seconds:-1;
Raw.GameMs=CyclesMs(GGameThreadTime);Raw.RenderMs=CyclesMs(GRenderThreadTime);
Raw.RhiMs=IsRunningRHIInSeparateThread()?CyclesMs(GRHIThreadTime):-1;
Raw.GpuMs=GDynamicRHI&&GSupportsTimestampRenderQueries?CyclesMs(RHIGetGPUFrameCycles()):-1;
Window.Add(Now,Seconds,Raw.GameMs,Raw.RenderMs,Raw.RhiMs,Raw.GpuMs);
if(Now-LastPublishTime>=.25){
const uint64 Revision=Snapshot.Revision+1;Snapshot=Window.Read(Now);Snapshot.Revision=Revision;LastPublishTime=Now;
}
}