#include "IronsPerformanceOverlay.h" #include "IronsHost.h" #include "IronsPerformanceSubsystem.h" #include "Blueprint/WidgetTree.h" #include "Components/CanvasPanel.h" #include "Rendering/DrawElements.h" #include "Styling/CoreStyle.h" #include "Fonts/CompositeFont.h" #include "Fonts/FontMeasure.h" #include "Framework/Application/SlateApplication.h" #include "Misc/Paths.h" namespace { const TSharedPtr& PerformanceFont(){ static TSharedPtr Font=MakeShared(TEXT("Regular"),FPaths::ProjectContentDir()/TEXT("IRONSPORE/Raw/Fonts/IronsSansSC-Regular.ttf"),EFontHinting::Default,EFontLoadingPolicy::LazyLoad);return Font; } } FIronsPerformanceLayout FIronsPerformanceLayout::Make(FVector2D Size,bool Enabled,int32 Corner) { FIronsPerformanceLayout L; Size.X=FMath::Max(1.0,Size.X);Size.Y=FMath::Max(1.0,Size.Y); L.PanelScale=FMath::Min(Size.X/1920.f,Size.Y/1080.f); Corner=FMath::Clamp(Corner,0,3); const bool Left=Corner==1||Corner==3,Bottom=Corner>=2; const float S=L.PanelScale,Width=660*S,Height=72*S,Margin=16*S; const float X=Left?Margin:Size.X-Margin-Width,Y=Bottom?Size.Y-Margin-Height:Margin; L.Panel=FSlateRect(X,Y,X+Width,Y+Height); const float Band=Enabled?104*S:0; L.ContentScale=FMath::Min(Size.X/1920.f,(Size.Y-Band)/1080.f); L.ContentOrigin=(Size-FVector2D(1920,1080)*L.ContentScale-FVector2D(0,Band))*.5f; if(!Bottom)L.ContentOrigin.Y+=Band; return L; } void UIronsPerformanceOverlay::NativeConstruct() { Super::NativeConstruct();SetIsFocusable(false);SetVisibility(ESlateVisibility::HitTestInvisible); if(WidgetTree&&!WidgetTree->RootWidget)WidgetTree->RootWidget=WidgetTree->ConstructWidget(); } void UIronsPerformanceOverlay::NativeTick(const FGeometry& Geometry,float DeltaTime) { Super::NativeTick(Geometry,DeltaTime); auto H=GetGameInstance();auto Metrics=GetGameInstance()?GetGameInstance()->GetSubsystem():nullptr; if(!H||!Metrics)return;const auto& S=Metrics->GetSnapshot(); const float Scale=FIronsPerformanceLayout::Make(Geometry.GetLocalSize(),true,H->PerformanceCorner).PanelScale; if(CachedRevision==S.Revision&&bCachedEnglish==H->bEnglish&&FMath::IsNearlyEqual(CachedScale,Scale))return; CachedRevision=S.Revision;bCachedEnglish=H->bEnglish;CachedScale=Scale; auto Fit=[&](const FString& Text,float Width,int32 Size){ FSlateFontInfo Info(PerformanceFont(),FMath::Max(1,FMath::RoundToInt(Size*Scale))); const auto Measure=FSlateApplication::Get().GetRenderer()->GetFontMeasureService(); const float Measured=Measure->Measure(Text,Info).X; if(Measured>Width*Scale)Info.Size=FMath::Max(7,FMath::FloorToInt(Info.Size*Width*Scale/Measured)); Measure->Measure(Text,Info); // Prepare the actual glyph size before either widget paints. return Info.Size; }; const TCHAR* Keys[]={TEXT("ue.perfFps"),TEXT("ue.perfFrame"),TEXT("ue.perfGpu"),TEXT("ue.perfGame"),TEXT("ue.perfRender"),TEXT("ue.perfRhi")}; const double Numbers[]={S.Fps,S.FrameMs,S.GpuMs,S.GameMs,S.RenderMs,S.RhiMs}; for(int32 I=0;I<6;++I){ Labels[I]=H->Label(Keys[I]);const double N=Numbers[I]; Values[I]=N<0||!FMath::IsFinite(N)?TEXT("—"):I==0?FString::Printf(TEXT("%.1f"),N):FString::Printf(TEXT("%.2f ms"),N); LabelSizes[I]=Fit(Labels[I],96,14);ValueSizes[I]=Fit(Values[I],106,16); Colors[I]=FLinearColor(.4f,.55f,.6f); if(N>=0&&FMath::IsFinite(N)){double Ms=I==0?1000/FMath::Max(N,.001):N; Colors[I]=Ms>33.3334?FLinearColor(1,.34f,.28f):Ms>16.6667?FLinearColor(.98f,.73f,.3f):FLinearColor(.35f,.88f,.83f);} } } int32 UIronsPerformanceOverlay::NativePaint(const FPaintArgs& Args,const FGeometry& G,const FSlateRect& Cull,FSlateWindowElementList& Out,int32 Layer,const FWidgetStyle& Style,bool Enabled)const { int32 Z=Super::NativePaint(Args,G,Cull,Out,Layer,Style,Enabled); auto H=GetGameInstance();if(!H||!H->bShowPerformance)return Z; const auto L=FIronsPerformanceLayout::Make(G.GetLocalSize(),true,H->PerformanceCorner);const float S=L.PanelScale; auto Geo=[&](float X,float Y,float W,float Height){return G.ToPaintGeometry(FVector2D(W,Height)*S,FSlateLayoutTransform(FVector2D(L.Panel.Left,L.Panel.Top)+FVector2D(X,Y)*S));}; FSlateDrawElement::MakeBox(Out,++Z,Geo(0,0,660,72),FCoreStyle::Get().GetBrush(TEXT("WhiteBrush")),ESlateDrawEffect::None,FLinearColor(.012f,.025f,.033f,.94f)); FSlateDrawElement::MakeBox(Out,++Z,Geo(0,0,3,72),FCoreStyle::Get().GetBrush(TEXT("WhiteBrush")),ESlateDrawEffect::None,FLinearColor(.25f,.7f,.7f)); auto DrawText=[&](float X,float Y,float Width,const FString& Text,int32 Size,FLinearColor Color){ FSlateFontInfo Info(PerformanceFont(),FMath::Max(1,Size)); Out.PushClip(FSlateClippingZone(Geo(X,Y,Width,25))); FSlateDrawElement::MakeText(Out,++Z,Geo(X,Y,Width,25),Text,Info,ESlateDrawEffect::None,Color); Out.PopClip(); }; for(int32 I=0;I<6;++I){float X=14+(I%3)*216,Y=9+(I/3)*32; DrawText(X,Y,96,Labels[I],LabelSizes[I],FLinearColor(.7f,.8f,.83f)); DrawText(X+102,Y-1,106,Values[I],ValueSizes[I],Colors[I]); } return Z; }