501 lines
16 KiB
C++
501 lines
16 KiB
C++
// Portage UE 5.5 - lot debug. Voir LPDebugSubsystem.h.
|
|
|
|
#include "LPDebugSubsystem.h"
|
|
|
|
#include "LifePlay.h"
|
|
|
|
#if !UE_BUILD_SHIPPING
|
|
|
|
#include "Chaos/TriangleMeshImplicitObject.h"
|
|
#include "Components/PrimitiveComponent.h"
|
|
#include "Components/StaticMeshComponent.h"
|
|
#include "Engine/Engine.h"
|
|
#include "Engine/World.h"
|
|
#include "EngineUtils.h"
|
|
#include "HAL/FileManager.h"
|
|
#include "HAL/IConsoleManager.h"
|
|
#include "HAL/PlatformMemory.h"
|
|
#include "Misc/CommandLine.h"
|
|
#include "Misc/CoreDelegates.h"
|
|
#include "Misc/FileHelper.h"
|
|
#include "Misc/Parse.h"
|
|
#include "Misc/Paths.h"
|
|
#include "PhysicsEngine/BodySetup.h"
|
|
#include "ProfilingDebugging/MiscTrace.h"
|
|
|
|
namespace
|
|
{
|
|
FAutoConsoleCommand* MemSnapshotCommand = nullptr;
|
|
FAutoConsoleCommand* CollisionDumpCommand = nullptr;
|
|
FAutoConsoleCommand* BookmarkCommand = nullptr;
|
|
FAutoConsoleCommand* HitchSummaryCommand = nullptr;
|
|
ULPDebugSubsystem* CommandOwner = nullptr;
|
|
|
|
ULPDebugSubsystem* FindDebugSubsystem()
|
|
{
|
|
return CommandOwner;
|
|
}
|
|
|
|
UWorld* FindPlayWorld()
|
|
{
|
|
if (!GEngine)
|
|
{
|
|
return nullptr;
|
|
}
|
|
if (UWorld* Play = GEngine->GetCurrentPlayWorld())
|
|
{
|
|
return Play;
|
|
}
|
|
for (const FWorldContext& Context : GEngine->GetWorldContexts())
|
|
{
|
|
UWorld* World = Context.World();
|
|
if (World && (World->WorldType == EWorldType::Game || World->WorldType == EWorldType::PIE))
|
|
{
|
|
return World;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
FString DebugDir()
|
|
{
|
|
const FString Dir = FPaths::ProjectSavedDir() / TEXT("Profiling/LPDebug");
|
|
IFileManager::Get().MakeDirectory(*Dir, true);
|
|
return Dir;
|
|
}
|
|
|
|
void WriteMemSnapshot()
|
|
{
|
|
const FString Stamp = FDateTime::Now().ToString(TEXT("%Y%m%d_%H%M%S"));
|
|
const FString Path = DebugDir() / FString::Printf(TEXT("MemSnapshot_%s.txt"), *Stamp);
|
|
const FPlatformMemoryStats Stats = FPlatformMemory::GetStats();
|
|
TArray<FString> Lines;
|
|
Lines.Add(FString::Printf(TEXT("When=%s"), *FDateTime::Now().ToString()));
|
|
Lines.Add(FString::Printf(TEXT("UsedPhysicalMB=%.1f"), Stats.UsedPhysical / (1024.0 * 1024.0)));
|
|
Lines.Add(FString::Printf(TEXT("PeakUsedPhysicalMB=%.1f"), Stats.PeakUsedPhysical / (1024.0 * 1024.0)));
|
|
Lines.Add(FString::Printf(TEXT("AvailablePhysicalMB=%.1f"), Stats.AvailablePhysical / (1024.0 * 1024.0)));
|
|
Lines.Add(FString::Printf(TEXT("UsedVirtualMB=%.1f"), Stats.UsedVirtual / (1024.0 * 1024.0)));
|
|
|
|
UWorld* World = FindPlayWorld();
|
|
if (GEngine)
|
|
{
|
|
const bool bReport = GEngine->Exec(World, TEXT("memreport"));
|
|
Lines.Add(FString::Printf(TEXT("memreport=%s"), bReport ? TEXT("ok") : TEXT("echec")));
|
|
}
|
|
const FString ReportDir = FPaths::ProjectSavedDir() / TEXT("Profiling/MemReports");
|
|
TArray<FString> Reports;
|
|
IFileManager::Get().FindFilesRecursive(Reports, *ReportDir, TEXT("*.memreport"), true, false);
|
|
if (Reports.Num() > 0)
|
|
{
|
|
Reports.Sort();
|
|
Lines.Add(FString::Printf(TEXT("NewestMemReport=%s"), *Reports.Last()));
|
|
}
|
|
Lines.Add(TEXT("PhysicsMB=voir LLM (colonne Physics) ou le MemReport ; ce fichier ne remplace pas la session B"));
|
|
FFileHelper::SaveStringArrayToFile(Lines, *Path);
|
|
UE_LOG(LogLifePlay, Display, TEXT("LPDebug MemSnapshot : %s"), *Path);
|
|
}
|
|
|
|
struct FCollisionOwner
|
|
{
|
|
FString Actor;
|
|
FString ClassName;
|
|
FString Component;
|
|
FString Profile;
|
|
int64 Triangles = 0;
|
|
int32 TriMeshes = 0;
|
|
int32 Simple = 0;
|
|
int32 Capsules = 0;
|
|
};
|
|
|
|
struct FCollisionTally
|
|
{
|
|
FString WorldName;
|
|
int32 Prim = 0;
|
|
int32 Simulating = 0;
|
|
int32 QueryOnly = 0;
|
|
int32 Persons = 0;
|
|
int32 Players = 0;
|
|
int32 Street = 0;
|
|
int32 UniqueBodies = 0;
|
|
int32 UniqueSimple = 0;
|
|
int32 UniqueTriMeshes = 0;
|
|
int32 UniqueCapsules = 0;
|
|
int64 Triangles = 0;
|
|
TMap<FString, int32> ByClass;
|
|
TMap<FString, int64> TrisByClass;
|
|
TArray<FCollisionOwner> Owners;
|
|
};
|
|
|
|
int64 CountTriangles(const UBodySetup* Body)
|
|
{
|
|
int64 Count = 0;
|
|
if (!Body)
|
|
{
|
|
return 0;
|
|
}
|
|
for (const Chaos::FTriangleMeshImplicitObjectPtr& Mesh : Body->TriMeshGeometries)
|
|
{
|
|
if (Mesh)
|
|
{
|
|
Count += Mesh->Elements().GetNumTriangles();
|
|
}
|
|
}
|
|
return Count;
|
|
}
|
|
|
|
FString ComponentLabel(const UPrimitiveComponent* Prim)
|
|
{
|
|
if (const UStaticMeshComponent* MeshComp = Cast<UStaticMeshComponent>(Prim))
|
|
{
|
|
if (const UStaticMesh* Mesh = MeshComp->GetStaticMesh())
|
|
{
|
|
return Mesh->GetName();
|
|
}
|
|
}
|
|
return Prim ? Prim->GetName() : FString();
|
|
}
|
|
|
|
FCollisionTally GatherCollision()
|
|
{
|
|
FCollisionTally Tally;
|
|
UWorld* World = FindPlayWorld();
|
|
Tally.WorldName = World ? World->GetName() : TEXT("(aucune)");
|
|
if (!World)
|
|
{
|
|
return Tally;
|
|
}
|
|
|
|
TSet<const UBodySetup*> Bodies;
|
|
for (TActorIterator<AActor> It(World); It; ++It)
|
|
{
|
|
const FString ClassName = It->GetClass()->GetName();
|
|
TInlineComponentArray<UPrimitiveComponent*> Comps;
|
|
It->GetComponents(Comps);
|
|
for (UPrimitiveComponent* Prim : Comps)
|
|
{
|
|
if (!Prim || !Prim->IsCollisionEnabled())
|
|
{
|
|
continue;
|
|
}
|
|
++Tally.Prim;
|
|
Tally.ByClass.FindOrAdd(ClassName)++;
|
|
if (ClassName.StartsWith(TEXT("Person_C")))
|
|
{
|
|
++Tally.Persons;
|
|
}
|
|
else if (ClassName.StartsWith(TEXT("PC_C")))
|
|
{
|
|
++Tally.Players;
|
|
}
|
|
else if (ClassName.Contains(TEXT("StreetMap")))
|
|
{
|
|
++Tally.Street;
|
|
}
|
|
if (Prim->IsSimulatingPhysics())
|
|
{
|
|
++Tally.Simulating;
|
|
}
|
|
if (Prim->GetCollisionEnabled() == ECollisionEnabled::QueryOnly)
|
|
{
|
|
++Tally.QueryOnly;
|
|
}
|
|
|
|
UBodySetup* Body = Prim->GetBodySetup();
|
|
if (!Body || Bodies.Contains(Body))
|
|
{
|
|
continue;
|
|
}
|
|
Bodies.Add(Body);
|
|
const int64 Tris = CountTriangles(Body);
|
|
const int32 Capsules = Body->AggGeom.SphylElems.Num();
|
|
const int32 Simple = Body->AggGeom.GetElementCount();
|
|
Tally.UniqueBodies++;
|
|
Tally.UniqueSimple += Simple;
|
|
Tally.UniqueCapsules += Capsules;
|
|
Tally.UniqueTriMeshes += Body->TriMeshGeometries.Num();
|
|
Tally.Triangles += Tris;
|
|
Tally.TrisByClass.FindOrAdd(ClassName) += Tris;
|
|
|
|
if (Tris > 0 || Simple > 0)
|
|
{
|
|
FCollisionOwner& Owner = Tally.Owners.AddDefaulted_GetRef();
|
|
Owner.Actor = It->GetName();
|
|
Owner.ClassName = ClassName;
|
|
Owner.Component = ComponentLabel(Prim);
|
|
Owner.Profile = Prim->GetCollisionProfileName().ToString();
|
|
Owner.Triangles = Tris;
|
|
Owner.TriMeshes = Body->TriMeshGeometries.Num();
|
|
Owner.Simple = Simple;
|
|
Owner.Capsules = Capsules;
|
|
}
|
|
}
|
|
}
|
|
|
|
Tally.Owners.Sort([](const FCollisionOwner& A, const FCollisionOwner& B)
|
|
{
|
|
return A.Triangles > B.Triangles;
|
|
});
|
|
return Tally;
|
|
}
|
|
|
|
FString WriteCollisionDump(const TCHAR* Reason, const FCollisionTally* Existing = nullptr)
|
|
{
|
|
const FCollisionTally Tally = Existing ? *Existing : GatherCollision();
|
|
const FString Stamp = FDateTime::Now().ToString(TEXT("%Y%m%d_%H%M%S"));
|
|
const FString Path = DebugDir() / FString::Printf(TEXT("CollisionDump_%s.txt"), *Stamp);
|
|
|
|
TArray<FString> Lines;
|
|
Lines.Add(FString::Printf(TEXT("When=%s"), *FDateTime::Now().ToString()));
|
|
Lines.Add(FString::Printf(TEXT("Reason=%s"), Reason));
|
|
Lines.Add(FString::Printf(TEXT("World=%s"), *Tally.WorldName));
|
|
Lines.Add(FString::Printf(TEXT("CollisionPrimitives=%d"), Tally.Prim));
|
|
Lines.Add(FString::Printf(TEXT("PersonPrimitives=%d"), Tally.Persons));
|
|
Lines.Add(FString::Printf(TEXT("PlayerPrimitives=%d"), Tally.Players));
|
|
Lines.Add(FString::Printf(TEXT("StreetMapPrimitives=%d"), Tally.Street));
|
|
Lines.Add(FString::Printf(TEXT("Simulating=%d"), Tally.Simulating));
|
|
Lines.Add(FString::Printf(TEXT("QueryOnly=%d"), Tally.QueryOnly));
|
|
Lines.Add(FString::Printf(TEXT("UniqueBodySetups=%d"), Tally.UniqueBodies));
|
|
Lines.Add(FString::Printf(TEXT("UniqueSimpleShapes=%d"), Tally.UniqueSimple));
|
|
Lines.Add(FString::Printf(TEXT("UniqueCapsules=%d"), Tally.UniqueCapsules));
|
|
Lines.Add(FString::Printf(TEXT("UniqueTriMeshes=%d"), Tally.UniqueTriMeshes));
|
|
Lines.Add(FString::Printf(TEXT("Triangles=%lld"), Tally.Triangles));
|
|
Lines.Add(TEXT("TopClasses:"));
|
|
TArray<TPair<FString, int32>> Classes;
|
|
for (const TPair<FString, int32>& Pair : Tally.ByClass)
|
|
{
|
|
Classes.Add(Pair);
|
|
}
|
|
Classes.Sort([](const TPair<FString, int32>& A, const TPair<FString, int32>& B) { return A.Value > B.Value; });
|
|
for (int32 i = 0; i < Classes.Num() && i < 15; ++i)
|
|
{
|
|
const int64 Tris = Tally.TrisByClass.FindRef(Classes[i].Key);
|
|
Lines.Add(FString::Printf(TEXT(" %d %s triangles=%lld"), Classes[i].Value, *Classes[i].Key, Tris));
|
|
}
|
|
Lines.Add(TEXT("OwnersByTriangles:"));
|
|
for (int32 i = 0; i < Tally.Owners.Num() && i < 12; ++i)
|
|
{
|
|
const FCollisionOwner& Owner = Tally.Owners[i];
|
|
Lines.Add(FString::Printf(
|
|
TEXT(" triangles=%lld trimesh=%d simple=%d capsules=%d profile=%s %s %s %s"),
|
|
Owner.Triangles, Owner.TriMeshes, Owner.Simple, Owner.Capsules,
|
|
*Owner.Profile, *Owner.ClassName, *Owner.Actor, *Owner.Component));
|
|
}
|
|
if (Tally.WorldName == TEXT("(aucune)"))
|
|
{
|
|
Lines.Add(TEXT("Note=monde de jeu absent (appeler la commande une fois la partie chargee)"));
|
|
}
|
|
Lines.Add(TEXT("Switch=-LPCollisionLog pour echantillonner apres le chargement, pas au ExecCmds de demarrage"));
|
|
FFileHelper::SaveStringArrayToFile(Lines, *Path);
|
|
UE_LOG(LogLifePlay, Display, TEXT("LPDebug collision : %d primitives, %d trimesh, %lld triangles (%s) -> %s"),
|
|
Tally.Prim, Tally.UniqueTriMeshes, Tally.Triangles, Reason, *Path);
|
|
TRACE_BOOKMARK(TEXT("LP Collision %lld tris"), Tally.Triangles);
|
|
return Path;
|
|
}
|
|
|
|
void WriteBookmark(const TArray<FString>& Args)
|
|
{
|
|
const FString Text = Args.Num() > 0 ? FString::Join(Args, TEXT(" ")) : TEXT("manuel");
|
|
UE_LOG(LogLifePlay, Display, TEXT("LPDebug bookmark : %s"), *Text);
|
|
TRACE_BOOKMARK(TEXT("LP %s"), *Text);
|
|
}
|
|
}
|
|
|
|
void ULPDebugSubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
|
{
|
|
Super::Initialize(Collection);
|
|
|
|
if (!MemSnapshotCommand)
|
|
{
|
|
MemSnapshotCommand = new FAutoConsoleCommand(
|
|
TEXT("LifePlay.MemSnapshot"),
|
|
TEXT("Ecrit Saved/Profiling/LPDebug/MemSnapshot_*.txt et lance memreport."),
|
|
FConsoleCommandDelegate::CreateLambda([]() { WriteMemSnapshot(); }));
|
|
CollisionDumpCommand = new FAutoConsoleCommand(
|
|
TEXT("LifePlay.CollisionDump"),
|
|
TEXT("Ecrit un releve des primitives de collision du monde courant."),
|
|
FConsoleCommandDelegate::CreateLambda([]() { WriteCollisionDump(TEXT("console")); }));
|
|
BookmarkCommand = new FAutoConsoleCommand(
|
|
TEXT("LifePlay.Bookmark"),
|
|
TEXT("Signet Insights (TRACE_BOOKMARK) et ligne LogLifePlay. Argument : texte du signet."),
|
|
FConsoleCommandWithArgsDelegate::CreateStatic(&WriteBookmark));
|
|
HitchSummaryCommand = new FAutoConsoleCommand(
|
|
TEXT("LifePlay.HitchSummary"),
|
|
TEXT("Ecrit le bilan courant des a-coups (-LPHitchLog)."),
|
|
FConsoleCommandDelegate::CreateLambda([]()
|
|
{
|
|
if (ULPDebugSubsystem* Sub = FindDebugSubsystem())
|
|
{
|
|
Sub->LogHitchSummary(TEXT("console"));
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogLifePlay, Display, TEXT("LPDebug hitch : sous-systeme absent"));
|
|
}
|
|
}));
|
|
CommandOwner = this;
|
|
}
|
|
|
|
bHitchLog = FParse::Param(FCommandLine::Get(), TEXT("LPHitchLog"));
|
|
HitchMs = 50.f;
|
|
FParse::Value(FCommandLine::Get(), TEXT("LPHitchMs="), HitchMs);
|
|
bCollisionLog = FParse::Param(FCommandLine::Get(), TEXT("LPCollisionLog"));
|
|
if (bHitchLog || bCollisionLog)
|
|
{
|
|
StartSeconds = FPlatformTime::Seconds();
|
|
LastFrameSeconds = StartSeconds;
|
|
}
|
|
if (bHitchLog)
|
|
{
|
|
UE_LOG(LogLifePlay, Display, TEXT("LPDebug hitch : actif, seuil %.1f ms"), HitchMs);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogLifePlay, Display, TEXT("LPDebug hitch : inactif"));
|
|
}
|
|
if (bCollisionLog)
|
|
{
|
|
const FString Stamp = FDateTime::Now().ToString(TEXT("%Y%m%d_%H%M%S"));
|
|
CollisionTrackPath = DebugDir() / FString::Printf(TEXT("CollisionTrack_%s.txt"), *Stamp);
|
|
FFileHelper::SaveStringToFile(
|
|
TEXT("t\tworld\tprim\tpersons\tplayers\tstreet\ttrimesh\ttriangles\n"),
|
|
*CollisionTrackPath);
|
|
NextCollisionSeconds = FPlatformTime::Seconds() + 2.0;
|
|
UE_LOG(LogLifePlay, Display, TEXT("LPDebug collision : actif, premier echantillon dans 2 s -> %s"), *CollisionTrackPath);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogLifePlay, Display, TEXT("LPDebug collision : inactif"));
|
|
}
|
|
if (bHitchLog || bCollisionLog)
|
|
{
|
|
FCoreDelegates::OnEndFrame.AddUObject(this, &ULPDebugSubsystem::OnEndFrame);
|
|
}
|
|
}
|
|
|
|
void ULPDebugSubsystem::Deinitialize()
|
|
{
|
|
if (bHitchLog)
|
|
{
|
|
LogHitchSummary(TEXT("fin"));
|
|
}
|
|
if (bCollisionLog)
|
|
{
|
|
LogCollisionSummary(TEXT("fin"));
|
|
}
|
|
if (bHitchLog || bCollisionLog)
|
|
{
|
|
FCoreDelegates::OnEndFrame.RemoveAll(this);
|
|
}
|
|
if (CommandOwner == this)
|
|
{
|
|
CommandOwner = nullptr;
|
|
}
|
|
Super::Deinitialize();
|
|
}
|
|
|
|
void ULPDebugSubsystem::OnEndFrame()
|
|
{
|
|
const double Now = FPlatformTime::Seconds();
|
|
if (bCollisionLog && Now >= NextCollisionSeconds)
|
|
{
|
|
NextCollisionSeconds = Now + 2.0;
|
|
SampleCollision();
|
|
}
|
|
if (!bHitchLog)
|
|
{
|
|
return;
|
|
}
|
|
const double DeltaMs = (Now - LastFrameSeconds) * 1000.0;
|
|
LastFrameSeconds = Now;
|
|
if (DeltaMs >= HitchMs)
|
|
{
|
|
++HitchCount;
|
|
MaxHitchMs = FMath::Max(MaxHitchMs, DeltaMs);
|
|
if (HitchLines < 100 || (HitchCount % 25) == 0)
|
|
{
|
|
++HitchLines;
|
|
UE_LOG(LogLifePlay, Warning, TEXT("LPDebug hitch : %.1f ms (seuil %.1f) #%d"), DeltaMs, HitchMs, HitchCount);
|
|
TRACE_BOOKMARK(TEXT("LP Hitch %.1f ms"), DeltaMs);
|
|
}
|
|
}
|
|
|
|
if (!bLoggedTwoMinuteSummary && (Now - StartSeconds) >= 120.0)
|
|
{
|
|
bLoggedTwoMinuteSummary = true;
|
|
LogHitchSummary(TEXT("2 min"));
|
|
}
|
|
}
|
|
|
|
void ULPDebugSubsystem::LogHitchSummary(const TCHAR* When) const
|
|
{
|
|
const double Elapsed = StartSeconds > 0.0 ? FPlatformTime::Seconds() - StartSeconds : 0.0;
|
|
UE_LOG(LogLifePlay, Display, TEXT("LPDebug hitch : %d hitch(s) en %.1f s (seuil %.1f ms), max %.1f ms, %s"),
|
|
HitchCount, Elapsed, HitchMs, MaxHitchMs, When);
|
|
}
|
|
|
|
void ULPDebugSubsystem::SampleCollision()
|
|
{
|
|
const FCollisionTally Tally = GatherCollision();
|
|
++CollisionSamples;
|
|
PeakPrim = FMath::Max(PeakPrim, Tally.Prim);
|
|
PeakTriangles = FMath::Max(PeakTriangles, Tally.Triangles);
|
|
const double Elapsed = StartSeconds > 0.0 ? FPlatformTime::Seconds() - StartSeconds : FPlatformTime::Seconds();
|
|
const FString Line = FString::Printf(
|
|
TEXT("%.1f\t%s\t%d\t%d\t%d\t%d\t%d\t%lld\n"),
|
|
Elapsed, *Tally.WorldName, Tally.Prim, Tally.Persons, Tally.Players, Tally.Street,
|
|
Tally.UniqueTriMeshes, Tally.Triangles);
|
|
FFileHelper::SaveStringToFile(Line, *CollisionTrackPath, FFileHelper::EEncodingOptions::AutoDetect, &IFileManager::Get(), FILEWRITE_Append);
|
|
UE_LOG(LogLifePlay, Display, TEXT("LPDebug collision : %.0f s %s prim=%d pnj=%d trimesh=%d triangles=%lld"),
|
|
Elapsed, *Tally.WorldName, Tally.Prim, Tally.Persons, Tally.UniqueTriMeshes, Tally.Triangles);
|
|
|
|
const bool bGrew = Tally.Triangles >= LastDumpedTriangles + 1000 || Tally.UniqueTriMeshes > LastDumpedTriMeshes;
|
|
if (CollisionDumps < 4 && Tally.Triangles > 0 && bGrew)
|
|
{
|
|
WriteCollisionDump(TEXT("suivi"), &Tally);
|
|
++CollisionDumps;
|
|
LastDumpedTriangles = Tally.Triangles;
|
|
LastDumpedTriMeshes = Tally.UniqueTriMeshes;
|
|
}
|
|
}
|
|
|
|
void ULPDebugSubsystem::LogCollisionSummary(const TCHAR* When) const
|
|
{
|
|
if (CollisionDumps == 0 && PeakPrim > 0)
|
|
{
|
|
WriteCollisionDump(TEXT("fin"));
|
|
}
|
|
UE_LOG(LogLifePlay, Display, TEXT("LPDebug collision : %d echantillon(s), %d dump(s), max %d primitives, %lld triangles, %s"),
|
|
CollisionSamples, CollisionDumps, PeakPrim, PeakTriangles, When);
|
|
}
|
|
|
|
#else
|
|
|
|
void ULPDebugSubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
|
{
|
|
Super::Initialize(Collection);
|
|
}
|
|
|
|
void ULPDebugSubsystem::Deinitialize()
|
|
{
|
|
Super::Deinitialize();
|
|
}
|
|
|
|
void ULPDebugSubsystem::OnEndFrame()
|
|
{
|
|
}
|
|
|
|
void ULPDebugSubsystem::LogHitchSummary(const TCHAR* When) const
|
|
{
|
|
(void)When;
|
|
}
|
|
|
|
void ULPDebugSubsystem::SampleCollision()
|
|
{
|
|
}
|
|
|
|
void ULPDebugSubsystem::LogCollisionSummary(const TCHAR* When) const
|
|
{
|
|
(void)When;
|
|
}
|
|
|
|
#endif
|