77 lines
3.7 KiB
C++
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;
|
|
}
|
|
}
|