Files
UnrealEngine/Engine/Source/Runtime/Renderer/Public/ViewDebug.h
2025-05-18 13:04:45 +08:00

237 lines
6.0 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Misc/ScopeRWLock.h"
#include "PrimitiveSceneInfo.h"
#include "PrimitiveComponentId.h"
#include "Components/ComponentInterfaces.h"
#include "GameFramework/Actor.h"
#if !UE_BUILD_SHIPPING // TODO: Decide whether or not the struct should be entirely stripped out of shipping
class UMaterialInterface;
class FScene;
class FViewInfo;
class FViewCommands;
DECLARE_MULTICAST_DELEGATE(FOnUpdateViewDebugInfo);
/**
* A collection of debug data associated with the current on screen view.
*/
struct FViewDebugInfo
{
friend class FDrawPrimitiveDebuggerModule;
private:
static RENDERER_API FViewDebugInfo Instance;
RENDERER_API FViewDebugInfo();
public:
/**
* Gets a reference to the view debug information that is used by the renderer.
* @returns The debug information that is used by the renderer.
*/
static inline FViewDebugInfo& Get()
{
return Instance;
}
/**
* Data collected about a single primitive being drawn to the screen.
*/
struct FPrimitiveInfo
{
TWeakObjectPtr<UObject> Owner;
FPrimitiveComponentId ComponentId;
IPrimitiveComponent* ComponentInterface;
TWeakObjectPtr<UObject> ComponentUObject;
FPrimitiveSceneInfo* PrimitiveSceneInfo;
FString Name;
FPrimitiveStats Stats;
TArray<TWeakObjectPtr<UMaterialInterface>> Materials;
TWeakObjectPtr<UMaterialInterface> OverlayMaterial;
int32 LODAtLastCapture;
bool operator<(const FPrimitiveInfo& Other) const
{
// Sort by name to group similar assets together, then by exact primitives so we can ignore duplicates
const int32 NameCompare = Name.Compare(Other.Name);
if (NameCompare != 0)
{
return NameCompare < 0;
}
return PrimitiveSceneInfo < Other.PrimitiveSceneInfo;
}
bool IsPrimitiveValid() const
{
bool bValid = true;
bValid &= Owner.IsValid();
bValid &= ComponentInterface != nullptr;
if (bValid)
{
bValid &= ComponentUObject.IsValid();
if (bValid)
{
bValid &= !ComponentInterface->IsUnreachable();
}
}
return bValid;
}
FORCEINLINE bool HasLODs() const
{
return !Stats.LODStats.IsEmpty();
}
FORCEINLINE bool IsLODIndexValid(int32 LOD) const
{
return LOD >= 0 && LOD < Stats.LODStats.Num();
}
RENDERER_API int32 ComputeCurrentLODIndex(int32 PlayerIndex = 0, int32 ViewIndex = 0) const;
FORCEINLINE FPrimitiveLODStats* GetCurrentLOD(int32 PlayerIndex = 0, int32 ViewIndex = 0)
{
int32 LOD = ComputeCurrentLODIndex(PlayerIndex, ViewIndex);
if (!IsLODIndexValid(LOD)) LOD = LODAtLastCapture;
return IsLODIndexValid(LOD) ? &Stats.LODStats[LOD] : nullptr;
}
FORCEINLINE const FPrimitiveLODStats* GetCurrentLOD(int32 PlayerIndex = 0, int32 ViewIndex = 0) const
{
int32 LOD = ComputeCurrentLODIndex(PlayerIndex, ViewIndex);
if (!IsLODIndexValid(LOD)) LOD = LODAtLastCapture;
return IsLODIndexValid(LOD) ? &Stats.LODStats[LOD] : nullptr;
}
FORCEINLINE FPrimitiveLODStats* GetLOD(int32 LOD)
{
return IsLODIndexValid(LOD) ? &Stats.LODStats[LOD] : nullptr;
}
FORCEINLINE const FPrimitiveLODStats* GetLOD(int32 LOD) const
{
return IsLODIndexValid(LOD) ? &Stats.LODStats[LOD] : nullptr;
}
FORCEINLINE UMaterialInterface* GetMaterial(uint16 Index) const
{
return Index < Materials.Num() ? Materials[Index].Get() : nullptr;
}
FORCEINLINE int32 GetNumLODs() const
{
return Stats.LODStats.Num();
}
FORCEINLINE FString GetOwnerName() const
{
if (const AActor* Actor = Cast<AActor>(Owner))
{
return Actor->GetHumanReadableName();
}
return ComponentInterface->GetOwnerName();
}
FORCEINLINE FVector GetPrimitiveLocation() const
{
return ComponentInterface->GetTransform().GetLocation();
}
};
private:
bool bHasEverUpdated;
bool bIsOutdated;
bool bShouldUpdate;
bool bShouldCaptureSingleFrame;
bool bShouldClearCapturedData;
FOnUpdateViewDebugInfo OnUpdate;
mutable FRWLock Lock;
TMap<FPrimitiveComponentId, FPrimitiveInfo> Primitives;
RENDERER_API void ProcessPrimitive(FPrimitiveSceneInfo* PrimitiveSceneInfo, const FViewInfo& View, FScene* Scene, IPrimitiveComponent* DebugComponent);
RENDERER_API void CaptureNextFrame();
RENDERER_API void EnableLiveCapture();
RENDERER_API void DisableLiveCapture();
RENDERER_API void ClearCaptureData();
public:
RENDERER_API void ProcessPrimitives(FScene* Scene, const FViewInfo& View, const FViewCommands& ViewCommands);
/**
* Writes the draw call count of all currently tracked primitives to a csv file.
* The file will be stored in /Saved/Profiling/Primitives/...
*/
RENDERER_API void DumpDrawCallsToCSV();
/**
* Writes detailed information about all currently tracked primitives to a csv file.
* The file will be stored in /Saved/Profiling/Primitives/...
*/
RENDERER_API void DumpToCSV() const;
/**
* Performs an operation for each primitive currently tracked.
* @param Action The action to perform for each primitive.
*/
template <typename CallableT>
void ForEachPrimitive(CallableT Action) const
{
const FPrimitiveSceneInfo* LastPrimitiveSceneInfo = nullptr;
FRWScopeLock ScopeLock(Lock, SLT_ReadOnly);
for (const auto& [PrimitiveId, Primitive] : Primitives)
{
if (Primitive.PrimitiveSceneInfo != LastPrimitiveSceneInfo)
{
Invoke(Action, Primitive);
LastPrimitiveSceneInfo = Primitive.PrimitiveSceneInfo;
}
}
}
/**
* Checks if this debug information has ever been updated.
* @returns True if the information has been updated at least once.
*/
RENDERER_API bool HasEverUpdated() const;
/**
* Checks if current information is from an older frame.
* @returns True if the data in this object is outdated.
*/
RENDERER_API bool IsOutOfDate() const;
template <typename UserClass>
FDelegateHandle AddUpdateHandler(UserClass* UserObject, void (UserClass::*Func)())
{
return OnUpdate.AddRaw(UserObject, Func);
}
FDelegateHandle AddUpdateHandler(void (*Func)())
{
return OnUpdate.AddStatic(Func);
}
void RemoveUpdateHandler(const FDelegateHandle& Handle)
{
OnUpdate.Remove(Handle);
}
};
#endif