// Copyright Epic Games, Inc. All Rights Reserved. #include "Widgets/SProfilerMiniView.h" #if STATS #include "Fonts/SlateFontInfo.h" #include "Misc/Paths.h" #include "Rendering/DrawElements.h" #include "Brushes/SlateColorBrush.h" #include "Fonts/FontMeasure.h" #include "Styling/CoreStyle.h" #include "Framework/Application/SlateApplication.h" #include "ProfilerSession.h" #include "ProfilerStyle.h" SProfilerMiniView::SProfilerMiniView() : bIsActiveTimerRegistered( false ) { Reset(); } SProfilerMiniView::~SProfilerMiniView() { } void SProfilerMiniView::Construct( const FArguments& InArgs ) { BindCommands(); } void SProfilerMiniView::Reset() { MaxFrameTime = 0.0f; AllFrames.Empty(); RecentlyAddedFrames.Empty(); StatMetadata = nullptr; FMemory::Memzero( PaintStateMemory ); PaintState = nullptr; MousePositionOnButtonDown = FVector2D::ZeroVector; SelectionBoxFrameStart = 0; SelectionBoxFrameEnd = 0; HoveredFrameIndex = 0; DistanceDragged = 0.0f; NumPixelsPerSample = 0; NumPixelsPerFrame = 0.0f; bIsLeftMousePressed = false; bIsRightMousePressed = false; bCanBeStartDragged = false; bCanBeEndDragged = false; bAllowSelectionBoxZooming = false; CursorType = EMiniviewCursor::Default; } EActiveTimerReturnType SProfilerMiniView::EnsureDataUpdateDuringPreview(double InCurrentTime, float InDeltaTime) { if (RecentlyAddedFrames.Num() > 0) { bUpdateData = true; return EActiveTimerReturnType::Continue; } bIsActiveTimerRegistered = false; return EActiveTimerReturnType::Stop; } void SProfilerMiniView::Tick( const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime ) { if( ThisGeometry != AllottedGeometry ) { // Refresh. MaxFrameTime = 0.0f; bUpdateData = true; } ThisGeometry = AllottedGeometry; if( IsReady() ) { NumPixelsPerFrame = (float)AllottedGeometry.Size.X / (float)AllFrames.Num(); } if( ShouldUpdateData() ) { ProcessData(); bUpdateData = false; } } int32 SProfilerMiniView::OnPaint( const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const { // SCOPE_LOG_TIME_FUNC(); // Rendering info. const bool bEnabled = ShouldBeEnabled( bParentEnabled ); const ESlateDrawEffect DrawEffects = bEnabled ? ESlateDrawEffect::None : ESlateDrawEffect::DisabledEffect; const FSlateBrush* MiniViewArea = FProfilerStyle::Get().GetBrush( "Brushes.White25" ); const FSlateBrush* WhiteBrush = FProfilerStyle::Get().GetBrush( "Brushes.White" ); PaintState = new((void*)PaintStateMemory) FSlateOnPaintState( AllottedGeometry, MyCullingRect, OutDrawElements, LayerId, InWidgetStyle, DrawEffects ); const float MiniViewSizeX = static_cast(AllottedGeometry.Size.X); const float MiniViewSizeY = static_cast(AllottedGeometry.Size.Y); const TSharedRef< FSlateFontMeasure > FontMeasureService = FSlateApplication::Get().GetRenderer()->GetFontMeasureService(); FSlateFontInfo SummaryFont = FCoreStyle::GetDefaultFontStyle("Regular", 8); const float MaxFontCharHeight = static_cast(FontMeasureService->Measure( TEXT( "!" ), SummaryFont ).Y); // Draw background. FSlateDrawElement::MakeBox ( OutDrawElements, LayerId, AllottedGeometry.ToPaintGeometry( FVector2D( MiniViewSizeX, AllottedGeometry.Size.Y ), FSlateLayoutTransform() ), MiniViewArea, DrawEffects, MiniViewArea->GetTint( InWidgetStyle ) * InWidgetStyle.GetColorAndOpacityTint() ); LayerId++; // Draw all samples. if( IsReady() ) { static const FSlateColorBrush SolidWhiteBrush = FSlateColorBrush( FColorList::White ); // #Profiler 2014-04-24 move to the global scope. const FColor GameThreadColor = FColorList::Red; const FColor RenderThreadColor = FColorList::Blue; const FColor OtherThreadsColor = FColorList::Grey; const float SampleScaleY = MiniViewSizeY / MaxFrameTime; float CurrentSamplePosX = 0; float NextSamplePosX = GetNumPixelsPerSample(); for( const FMiniViewSample& MiniViewSample : MiniViewSamples ) { const float AllSizeY = FMath::TruncToFloat( MiniViewSample.TotalThreadTime*SampleScaleY ); const float GTSizeY = FMath::TruncToFloat( MiniViewSample.GameThreadTime*SampleScaleY ); const float RTSizeY = FMath::TruncToFloat( MiniViewSample.RenderThreadTime*SampleScaleY ); const float OtherSizeY = AllSizeY - GTSizeY - RTSizeY; const float DestSamplePosX0 = FMath::TruncToFloat( CurrentSamplePosX ); const float DestSamplePosX1 = FMath::TruncToFloat( NextSamplePosX ); const float DestSampleSizeX = DestSamplePosX1 - DestSamplePosX0; // The game thread on the bottom. FSlateDrawElement::MakeBox ( OutDrawElements, LayerId, AllottedGeometry.ToPaintGeometry( FVector2D( DestSampleSizeX, GTSizeY ), FSlateLayoutTransform(FVector2D( DestSamplePosX0, MiniViewSizeY - GTSizeY )) ), &SolidWhiteBrush, DrawEffects, GameThreadColor ); // Next the render thread. FSlateDrawElement::MakeBox ( OutDrawElements, LayerId, AllottedGeometry.ToPaintGeometry( FVector2D( DestSampleSizeX, RTSizeY ), FSlateLayoutTransform(FVector2D( DestSamplePosX0, MiniViewSizeY - GTSizeY - RTSizeY )) ), &SolidWhiteBrush, DrawEffects, RenderThreadColor ); // Disabled for now. #if 0 // Finally the others threads as one sample. FSlateDrawElement::MakeBox ( OutDrawElements, LayerId, AllottedGeometry.ToPaintGeometry( FVector2D( DestSamplePosX, MiniViewSizeY - AllSizeY ), FVector2D( DestSampleSizeX, OtherSizeY ) ), &SolidWhiteBrush, DrawEffects, OtherThreadsColor ); #endif // 0 CurrentSamplePosX = NextSamplePosX; NextSamplePosX += GetNumPixelsPerSample(); } // Draw the selection box LayerId++; const int32 MaxFrameIndex = AllFrames.Num() - 1; const double SelectionBoxX0 = FMath::TruncToInt( FrameIndexToPosition( SelectionBoxFrameStart ) ); const double SelectionBoxX1 = FMath::TruncToInt( FrameIndexToPosition( SelectionBoxFrameEnd + 1 )); if( SelectionBoxFrameStart > 0 ) { FSlateDrawElement::MakeBox ( OutDrawElements, LayerId, AllottedGeometry.ToPaintGeometry( FVector2D( SelectionBoxX0, AllottedGeometry.Size.Y ), FSlateLayoutTransform(FVector2D( 0.0, 0.0 )) ), &SolidWhiteBrush, DrawEffects, FColorList::Grey.WithAlpha( 192 ) ); } if( SelectionBoxFrameEnd < MaxFrameIndex ) { FSlateDrawElement::MakeBox ( OutDrawElements, LayerId, AllottedGeometry.ToPaintGeometry( FVector2D( MiniViewSizeX - SelectionBoxX1, AllottedGeometry.Size.Y ), FSlateLayoutTransform(FVector2D( SelectionBoxX1, 0.0 )) ), &SolidWhiteBrush, DrawEffects, FColorList::Grey.WithAlpha( 192 ) ); } // Draw the filler, to hide the difference between window's width and mini-view samples' width. const double FillerSizeX = MiniViewSizeX - CurrentSamplePosX; if( FillerSizeX > 0.0 ) { const double FillerPosX0 = MiniViewSizeX - FillerSizeX; FSlateDrawElement::MakeBox ( OutDrawElements, LayerId, AllottedGeometry.ToPaintGeometry( FVector2D( FillerSizeX + 1.0, AllottedGeometry.Size.Y ), FSlateLayoutTransform(FVector2D( FillerPosX0, 0.0 )) ), &SolidWhiteBrush, DrawEffects, // #Profiler: 2014-04-09 How to get this color from Slate? FColor(96,96,96) ); } // Border of the selection box. LayerId++; FSlateDrawElement::MakeBox ( OutDrawElements, LayerId, AllottedGeometry.ToPaintGeometry( FVector2D( SelectionBoxX1 - SelectionBoxX0, AllottedGeometry.Size.Y ), FSlateLayoutTransform(FVector2D( SelectionBoxX0, 0.0 )) ), FProfilerStyle::Get().GetBrush( "PlainBorder" ), DrawEffects, FColorList::Green ); // Draw the current mouse position with the highlighted sample. LayerId++; // Draw the basic information about threads and data range. const float MarkerPosX = 4.0f; const float MarkerPosY = 4.0f; // Upper left DrawText( TEXT( "Rendering thread" ), SummaryFont, FVector2D( MarkerPosX, MarkerPosY ), RenderThreadColor, FColor::Black, FVector2D( 1.0, 1.0 ) ); // Down left DrawText( TEXT( "Game thread" ), SummaryFont, FVector2D( MarkerPosX, MiniViewSizeY - MarkerPosY - MaxFontCharHeight ), GameThreadColor, FColor::Black, FVector2D( 1.0, 1.0 ) ); // Upper right const FString ThreadTimeMax = FString::Printf( TEXT( "%5.2f MS" ), MaxFrameTime ); const double ThreadTimeMaxSizeX = FontMeasureService->Measure( ThreadTimeMax, SummaryFont ).X; DrawText( ThreadTimeMax, SummaryFont, FVector2D( MiniViewSizeX - ThreadTimeMaxSizeX - MarkerPosX, MarkerPosY ), FColor::White, FColor::Black, FVector2D( 1.0, 1.0 ) ); // Down right const FString ThreadTimeMin = TEXT( "0.0 MS" ); const double ThreadTimeMinSizeX = FontMeasureService->Measure( ThreadTimeMin, SummaryFont ).X; DrawText( ThreadTimeMin, SummaryFont, FVector2D( MiniViewSizeX - ThreadTimeMinSizeX - MarkerPosX, MiniViewSizeY - MarkerPosY - MaxFontCharHeight ), FColor::White, FColor::Black, FVector2D( 1.0, 1.0 ) ); } #if 0/*DEBUG_PROFILER_PERFORMANCE*/ LayerId++; // Draw debug information. float GraphDescPosY = 0; // Debug text. FSlateDrawElement::MakeText ( OutDrawElements, LayerId, AllottedGeometry.ToOffsetPaintGeometry( FVector2D( 16.0f, GraphDescPosY ) ), FString::Printf( TEXT( "SelectionBox FrameStart=%4i, FrameEnd=%4i (%3i) Hovered=%4i, NumPixelsPerSample=%2.1f" ), SelectionBoxFrameStart, SelectionBoxFrameEnd, SelectionBoxFrameEnd - SelectionBoxFrameStart, HoveredFrameIndex, GetNumPixelsPerSample() ), SummaryFont, MyCullingRect, DrawEffects, FLinearColor::White ); GraphDescPosY += MaxFontCharHeight + 1.0f; FSlateDrawElement::MakeText ( OutDrawElements, LayerId, AllottedGeometry.ToOffsetPaintGeometry( FVector2D( 16.0f, GraphDescPosY ) ), FString::Printf( TEXT( "AllFrames=%4i, MiniViewSamples=%3i, MaxFrameTime=%2.0f DistanceDragged=%2.0f" ), AllFrames.Num(), MiniViewSamples.Num(), MaxFrameTime, DistanceDragged ), SummaryFont, MyCullingRect, DrawEffects, FLinearColor::White ); GraphDescPosY += MaxFontCharHeight + 1.0f; FSlateDrawElement::MakeText ( OutDrawElements, LayerId, AllottedGeometry.ToOffsetPaintGeometry( FVector2D( 16.0f, GraphDescPosY ) ), FString::Printf( TEXT( "bCanBeStartD=%1i, bCanBeEndD=%1i" ), (int32)bCanBeStartDragged, (int32)bCanBeEndDragged ), SummaryFont, MyCullingRect, DrawEffects, FLinearColor::White ); GraphDescPosY += MaxFontCharHeight + 1.0f; #endif // DEBUG_PROFILER_PERFORMANCE return SCompoundWidget::OnPaint( Args, AllottedGeometry, MyCullingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled && IsEnabled() ); } void SProfilerMiniView::DrawText( const FString& Text, const FSlateFontInfo& FontInfo, FVector2D Position, const FColor& TextColor, const FColor& ShadowColor, FVector2D ShadowOffset ) const { if( ShadowOffset.SizeSquared() > 0.0f ) { FSlateDrawElement::MakeText ( PaintState->OutDrawElements, PaintState->LayerId, PaintState->AllottedGeometry.ToOffsetPaintGeometry( Position + ShadowOffset ), Text, FontInfo, PaintState->DrawEffects, ShadowColor ); } FSlateDrawElement::MakeText ( PaintState->OutDrawElements, ++PaintState->LayerId, PaintState->AllottedGeometry.ToOffsetPaintGeometry( Position ), Text, FontInfo, PaintState->DrawEffects, TextColor ); } FReply SProfilerMiniView::OnMouseButtonDown( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent ) { FReply Reply = FReply::Unhandled(); if( IsReady() ) { MousePositionOnButtonDown = MyGeometry.AbsoluteToLocal( MouseEvent.GetScreenSpacePosition() ); if( MouseEvent.GetEffectingButton() == EKeys::LeftMouseButton ) { bIsLeftMousePressed = true; if( bCanBeStartDragged ) { DistanceDragged = FrameIndexToPosition( SelectionBoxFrameStart ); } else if( bCanBeEndDragged ) { DistanceDragged = FrameIndexToPosition( SelectionBoxFrameEnd ); } else { // Clicked outside the selection box, so move the selection box to that position. DistanceDragged = static_cast(MousePositionOnButtonDown.X); } if( bCanBeStartDragged || bCanBeEndDragged ) { // Capture mouse, so we can move outside this widget. Reply = FReply::Handled().CaptureMouse( SharedThis( this ) ); } } else if( MouseEvent.GetEffectingButton() == EKeys::RightMouseButton ) { bIsRightMousePressed = true; } } return Reply; } FReply SProfilerMiniView::OnMouseButtonUp( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent ) { FReply Reply = FReply::Unhandled(); if( IsReady() ) { const FVector2D MousePositionOnButtonUp = MyGeometry.AbsoluteToLocal( MouseEvent.GetScreenSpacePosition() ); const bool bIsValidForMouseClick = MousePositionOnButtonUp.Equals( MousePositionOnButtonDown, MOUSE_SNAP_DISTANCE ); if( MouseEvent.GetEffectingButton() == EKeys::LeftMouseButton ) { if( bIsLeftMousePressed ) { if( !bCanBeStartDragged && !bCanBeEndDragged ) { MoveSelectionBox( PositionToFrameIndex( DistanceDragged ) ); } else if( bCanBeStartDragged || bCanBeEndDragged ) { // Release the mouse, we are no longer dragging. Reply = FReply::Handled().ReleaseMouseCapture(); } } bIsLeftMousePressed = false; } else if( MouseEvent.GetEffectingButton() == EKeys::RightMouseButton ) { if( bIsRightMousePressed ) { if( bIsValidForMouseClick ) { ShowContextMenu( MouseEvent.GetScreenSpacePosition() ); Reply = FReply::Handled(); } } bIsRightMousePressed = false; } } return Reply; } FReply SProfilerMiniView::OnMouseMove( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent ) { // SCOPE_LOG_TIME_FUNC(); FReply Reply = FReply::Unhandled(); if( IsReady() ) { const FVector2D MousePosition = MyGeometry.AbsoluteToLocal( MouseEvent.GetScreenSpacePosition() ); HoveredFrameIndex = PositionToFrameIndex( static_cast(MousePosition.X) ); const float CursorPosXDelta = static_cast(MouseEvent.GetCursorDelta().X); if( MouseEvent.IsMouseButtonDown( EKeys::LeftMouseButton ) ) { if( HasMouseCapture() && !MouseEvent.GetCursorDelta().IsZero() ) { DistanceDragged += CursorPosXDelta; const int32 MouseFrameIndex = PositionToFrameIndex( DistanceDragged ); if( !bAllowSelectionBoxZooming ) { const int32 SelectionBoxSize = SelectionBoxFrameEnd - SelectionBoxFrameStart; if( bCanBeStartDragged ) { SelectionBoxFrameStart = MouseFrameIndex; SelectionBoxFrameStart = FMath::Clamp( SelectionBoxFrameStart, 0, FMath::Max( AllFrames.Num() - SelectionBoxSize - 1, 0 ) ); SelectionBoxFrameEnd = FMath::Min( SelectionBoxFrameStart + SelectionBoxSize, AllFrames.Num() - 1 ); } else if( bCanBeEndDragged ) { SelectionBoxFrameEnd = MouseFrameIndex; SelectionBoxFrameEnd = FMath::Clamp( SelectionBoxFrameEnd, SelectionBoxSize - 1, AllFrames.Num() - -1 ); SelectionBoxFrameStart = FMath::Max( SelectionBoxFrameEnd - SelectionBoxSize, 0 ); } } else { } // Inform other widgets that we have moved the selection box. SelectionBoxChangedEvent.Broadcast( SelectionBoxFrameStart, SelectionBoxFrameEnd ); Reply = FReply::Handled(); } } else { const float StartEdgeDistance = FrameIndexToPosition(SelectionBoxFrameStart) - static_cast(MousePosition.X); const float EndEdgeDistance = static_cast(MousePosition.X) - FrameIndexToPosition(SelectionBoxFrameEnd); bCanBeStartDragged = StartEdgeDistance < (float)MOUSE_SNAP_DISTANCE && StartEdgeDistance > 0.0f; bCanBeEndDragged = EndEdgeDistance < (float)MOUSE_SNAP_DISTANCE && EndEdgeDistance > 0.0f; if( StartEdgeDistance <= 0.0f && EndEdgeDistance <= 0.0f ) { // Drag the whole selection box. bCanBeStartDragged = true; bCanBeEndDragged = true; CursorType = EMiniviewCursor::Hand; } else if( bCanBeStartDragged != bCanBeEndDragged ) { CursorType = EMiniviewCursor::Arrow; } else { CursorType = EMiniviewCursor::Default; } } } return Reply; } void SProfilerMiniView::OnMouseEnter( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent ) { } void SProfilerMiniView::OnMouseLeave( const FPointerEvent& MouseEvent ) { if( !HasMouseCapture() ) { bIsLeftMousePressed = false; bIsRightMousePressed = false; bCanBeStartDragged = false; bCanBeEndDragged = false; CursorType = EMiniviewCursor::Default; } } FReply SProfilerMiniView::OnMouseWheel( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent ) { FReply Reply = FReply::Unhandled(); return Reply; } FReply SProfilerMiniView::OnMouseButtonDoubleClick( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent ) { FReply Reply = FReply::Unhandled(); return Reply; } FCursorReply SProfilerMiniView::OnCursorQuery( const FGeometry& MyGeometry, const FPointerEvent& CursorEvent ) const { FCursorReply CursorReply = FCursorReply::Unhandled(); if( CursorType == EMiniviewCursor::Arrow ) { CursorReply = FCursorReply::Cursor( EMouseCursor::ResizeLeftRight ); } else if( CursorType == EMiniviewCursor::Hand ) { CursorReply = FCursorReply::Cursor( EMouseCursor::GrabHand ); } return CursorReply; } void SProfilerMiniView::ShowContextMenu( const FVector2D& ScreenSpacePosition ) { } void SProfilerMiniView::BindCommands() { } void SProfilerMiniView::AddThreadTime( int32 InFrameIndex, const TMap& InThreadMS, const TSharedRef& InStatMetaData ) { FFrameThreadTimes FrameThreadTimes; FrameThreadTimes.FrameNumber = InFrameIndex; FrameThreadTimes.ThreadTimes = InThreadMS; RecentlyAddedFrames.Add( FrameThreadTimes ); if (!bIsActiveTimerRegistered) { bIsActiveTimerRegistered = true; RegisterActiveTimer(0.f, FWidgetActiveTimerDelegate::CreateSP(this, &SProfilerMiniView::EnsureDataUpdateDuringPreview)); } StatMetadata = InStatMetaData; } void SProfilerMiniView::MoveWithoutZoomSelectionBox( int32 FrameStart, int32 FrameEnd ) { const int32 MaxFrameIndex = FMath::Max( AllFrames.Num() - 1, 0 ); SelectionBoxFrameStart = FMath::Clamp( FrameStart, 0, MaxFrameIndex ); SelectionBoxFrameEnd = FMath::Clamp( FrameEnd, 0, MaxFrameIndex ); bAllowSelectionBoxZooming = false; } void SProfilerMiniView::MoveAndZoomSelectionBox( int32 FrameStart, int32 FrameEnd ) { const int32 MaxFrameIndex = FMath::Max( AllFrames.Num() - 1, 0 ); SelectionBoxFrameStart = FMath::Clamp( FrameStart, 0, MaxFrameIndex ); SelectionBoxFrameEnd = FMath::Clamp( FrameEnd, 0, MaxFrameIndex ); //bAllowSelectionBoxZooming = true; } void SProfilerMiniView::MoveSelectionBox( int32 FrameIndex ) { const int32 SelectionBoxSize = SelectionBoxFrameEnd - SelectionBoxFrameStart; const int32 SelectionBoxHalfSize = SelectionBoxSize / 2; const int32 CenterFrameIndex = FMath::Clamp( FrameIndex - SelectionBoxHalfSize, 0, AllFrames.Num() - 1 - SelectionBoxSize ); // Inform other widgets that we have moved the selection box. SelectionBoxFrameStart = CenterFrameIndex; SelectionBoxFrameEnd = CenterFrameIndex + SelectionBoxSize; SelectionBoxChangedEvent.Broadcast( SelectionBoxFrameStart, SelectionBoxFrameEnd ); } const int32 SProfilerMiniView::PositionToFrameIndex( const float InPositionX ) const { int32 FrameIndex = 0; if( IsReady() ) { const int32 NumAllFrames = AllFrames.Num(); const int32 NumMiniViewSamples = MiniViewSamples.Num(); const float ScaleRatio = (float)NumAllFrames / (float)NumMiniViewSamples; const float MouseSampleIndex = InPositionX / GetNumPixelsPerSample(); FrameIndex = FMath::TruncToInt(MouseSampleIndex * ScaleRatio); FrameIndex = FMath::Clamp( FrameIndex, 0, NumAllFrames - 1 ); } return FrameIndex; } void SProfilerMiniView::ProcessData() { static FTotalTimeAndCount TimeAndCount( 0.0f, 0 ); //SCOPE_LOG_TIME_FUNC_WITH_GLOBAL( &TimeAndCount ); AllFrames.Append( RecentlyAddedFrames ); RecentlyAddedFrames.Reset(); if( !IsReady() ) { return; } UpdateNumPixelsPerSample(); const int32 NumMiniViewSamples = FMath::TruncToInt(static_cast(ThisGeometry.Size.X) / GetNumPixelsPerSample()); MiniViewSamples.Reset( NumMiniViewSamples ); MiniViewSamples.AddZeroed( NumMiniViewSamples ); const int32 NumAllFrames = AllFrames.Num(); const uint32 GameThreadID = StatMetadata->GetGameThreadID(); const TArray& RenderThreadIDs = StatMetadata->GetRenderThreadID(); // Prepare data to fit into the window. { // Aggregate. const float ScaleRatio = (float)NumMiniViewSamples / (float)NumAllFrames; for( int32 FrameIndex = 0; FrameIndex < NumAllFrames; ++FrameIndex ) { const FFrameThreadTimes& FrameThreadTimes = AllFrames[FrameIndex]; const int32 SampleIndex = FMath::TruncToInt( ScaleRatio * (float)FrameIndex ); FMiniViewSample& Dest = MiniViewSamples[SampleIndex]; Dest.AddFrameAndFindMax( FrameThreadTimes ); } for( FMiniViewSample& It : MiniViewSamples ) { It.CalculateMaxThreadTime( GameThreadID, RenderThreadIDs ); } } #if 0 else { // Replicate. const float ScaleRatio = (float)NumAllFrames / (float)WindowNumSamples; float CurrentFrameIndex = 0.0f; for( int32 SampleIndex = 0; SampleIndex < WindowNumSamples; ++SampleIndex ) { const int FrameIndex = FMath::TruncToInt( CurrentFrameIndex ); const FFrameThreadTimes& FrameThreadTimes = AllFrames[FrameIndex]; FMiniViewThreadTimes& Dest = WindowFrames[SampleIndex]; Dest.Aggregate( AllFrames[FrameIndex] ); Dest.CalculateMaxThreadTime( GameThreadID, RenderThreadIDs ); CurrentFrameIndex += ScaleRatio; } } #endif // 0 // Calculate max thread time, used to scaling the displayed samples. for( const FMiniViewSample& MiniViewSample : MiniViewSamples ) { MaxFrameTime = FMath::Max( MaxFrameTime, MiniViewSample.TotalThreadTime ); } MaxFrameTime = FMath::Clamp( MaxFrameTime, 0.0f, (float)MAX_VISIBLE_THREADTIME ); } #endif // STATS