// Copyright IHY. Vertical Partition Streaming plugin. #include "VerticalPartitionDebugComponent.h" #include "VerticalPartitionRuntimeSubsystem.h" #include "VerticalPartitionStatics.h" #include "Engine/Engine.h" #include "DrawDebugHelpers.h" static TAutoConsoleVariable CVarVPDebug( TEXT("vp.Debug"), 0, TEXT("Master Vertical Partition debug toggle (gates DebugDraw + DebugOverlay)."), ECVF_Default); static TAutoConsoleVariable CVarVPDebugDraw( TEXT("vp.DebugDraw"), 1, TEXT("Draw 3D coloured cell boxes / ids / counts (requires vp.Debug 1)."), ECVF_Default); static TAutoConsoleVariable CVarVPDebugOverlay( TEXT("vp.DebugOverlay"), 1, TEXT("Draw the on-screen runtime stats overlay (requires vp.Debug 1)."), ECVF_Default); UVerticalPartitionDebugComponent::UVerticalPartitionDebugComponent() { PrimaryComponentTick.bCanEverTick = true; PrimaryComponentTick.bStartWithTickEnabled = true; } UVerticalPartitionRuntimeSubsystem* UVerticalPartitionDebugComponent::GetSub() const { return UVerticalPartitionRuntimeSubsystem::Get(this); } void UVerticalPartitionDebugComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) { Super::TickComponent(DeltaTime, TickType, ThisTickFunction); if (CVarVPDebug.GetValueOnGameThread() == 0) { return; } UVerticalPartitionRuntimeSubsystem* Sub = GetSub(); if (!Sub || !Sub->IsReady()) { return; } if (CVarVPDebugDraw.GetValueOnGameThread() != 0) { DrawDebugCells(Sub); } if (CVarVPDebugOverlay.GetValueOnGameThread() != 0) { DrawOverlay(Sub); } } void UVerticalPartitionDebugComponent::DrawDebugCells(UVerticalPartitionRuntimeSubsystem* Sub) const { #if ENABLE_DRAW_DEBUG UWorld* World = GetWorld(); if (!World || !Sub) { return; } for (const TPair& Pair : Sub->GetCellRuntimes()) { const FVerticalCellRuntime& C = Pair.Value; if (!C.Bounds.IsValid) { continue; } // Purple overrides the state colour when a cell is force-locked. FColor Color = C.bForced ? FColor(180, 60, 220) : UVerticalPartitionStatics::GetStateColor(C.State).ToFColor(true); const FVector Center = C.Bounds.GetCenter(); const FVector Extent = C.Bounds.GetExtent(); DrawDebugBox(World, Center, Extent, Color, false, -1.f, 0, 4.f); const FString Label = FString::Printf(TEXT("%s [%s] actors:%d"), *C.Id.ToString(), C.bForced ? TEXT("LOCK") : *UEnum::GetDisplayValueAsText(C.State).ToString(), C.ResolvedActors.Num()); DrawDebugString(World, Center + FVector(0, 0, Extent.Z * 0.5f), Label, nullptr, Color, 0.f, true); } #endif // ENABLE_DRAW_DEBUG } void UVerticalPartitionDebugComponent::DrawOverlay(UVerticalPartitionRuntimeSubsystem* Sub) const { if (!GEngine || !Sub) { return; } const FVerticalRuntimeStats& S = Sub->GetStats(); const int32 Key = 0x5650; // 'VP' auto Line = [&](int32 Slot, const FColor& Col, const FString& Text) { GEngine->AddOnScreenDebugMessage(Key + Slot, 0.f, Col, Text); }; Line(0, FColor::Cyan, TEXT("==== Vertical Partition ====")); Line(1, FColor::White, FString::Printf(TEXT("Player Z cell: %d"), S.PlayerCellZ)); Line(2, FColor::Green, FString::Printf(TEXT("Full cells: %d"), S.FullCellsLoaded)); Line(3, FColor::Blue, FString::Printf(TEXT("HLOD cells: %d"), S.HLODCellsVisible)); Line(4, FColor::Silver, FString::Printf(TEXT("Unloaded: %d"), S.CellsUnloaded)); Line(5, FColor::Yellow, FString::Printf(TEXT("Loading/async: %d / %d"), S.CellsLoading, S.ActiveAsyncLoads)); Line(6, FColor::White, FString::Printf(TEXT("Est. memory: %.1f MB"), S.EstimatedMemoryMB)); Line(7, FColor::White, FString::Printf(TEXT("Update time: %.2f ms"), S.LastUpdateTimeMS)); Line(8, S.HitchesOverThreshold > 0 ? FColor::Red : FColor::White, FString::Printf(TEXT("Hitches: %d"), S.HitchesOverThreshold)); }