Files
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

805 lines
32 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Chaos/Core.h"
#include "Chaos/ParticleHandleFwd.h"
#include "Chaos/Serializable.h"
#include "Containers/Set.h"
#include "Chaos/ChaosArchive.h"
#include "ChaosVDContextProvider.h"
#include "ChaosVDOptionalDataChannel.h"
#include "Chaos/ParticleIterator.h"
#include "HAL/ThreadSafeBool.h"
#ifndef CHAOS_VISUAL_DEBUGGER_ENABLED
#define CHAOS_VISUAL_DEBUGGER_ENABLED WITH_CHAOS_VISUAL_DEBUGGER
#endif
#ifndef CHAOS_VISUAL_DEBUGGER_WITHOUT_TRACE
#define CHAOS_VISUAL_DEBUGGER_WITHOUT_TRACE 0
#endif
#include "Chaos/Framework/PhysicsSolverBase.h"
#include "ChaosVisualDebugger/ChaosVDTraceMacros.h"
#if WITH_CHAOS_VISUAL_DEBUGGER
#include "Chaos/ChaosDebugNameDefines.h"
#include "ChaosVDMemWriterReader.h"
#include "ChaosVDRuntimeModule.h"
#include "Containers/StripedMap.h"
#include "DataWrappers/ChaosVDImplicitObjectDataWrapper.h"
#include "Trace/Trace.h"
#include "Trace/Trace.inl"
namespace Chaos
{
class FRigidClustering;
class FAccelerationStructureHandle;
class FCharacterGroundConstraintContainer;
class FPBDConstraintContainer;
class FPBDJointConstraints;
}
UE_TRACE_MINIMAL_CHANNEL_EXTERN(ChaosVDChannel, CHAOS_API)
UE_TRACE_MINIMAL_EVENT_BEGIN_EXTERN(ChaosVDLogger, ChaosVDSolverFrameStart)
UE_TRACE_MINIMAL_EVENT_FIELD(int32, SolverID)
UE_TRACE_MINIMAL_EVENT_FIELD(uint64, Cycle)
UE_TRACE_MINIMAL_EVENT_FIELD(UE::Trace::WideString, DebugName)
UE_TRACE_MINIMAL_EVENT_FIELD(bool, IsKeyFrame)
UE_TRACE_MINIMAL_EVENT_FIELD(bool, IsReSimulated)
UE_TRACE_MINIMAL_EVENT_FIELD(int32, CurrentFrameNumber)
UE_TRACE_MINIMAL_EVENT_END()
UE_TRACE_MINIMAL_EVENT_BEGIN_EXTERN(ChaosVDLogger, ChaosVDSolverFrameEnd)
UE_TRACE_MINIMAL_EVENT_FIELD(int32, SolverID)
UE_TRACE_MINIMAL_EVENT_FIELD(uint64, Cycle)
UE_TRACE_MINIMAL_EVENT_END()
UE_TRACE_MINIMAL_EVENT_BEGIN_EXTERN(ChaosVDLogger, ChaosVDParticleCreated)
UE_TRACE_MINIMAL_EVENT_FIELD(int32, SolverID)
UE_TRACE_MINIMAL_EVENT_FIELD(uint64, Cycle)
UE_TRACE_MINIMAL_EVENT_FIELD(int32, ParticleID)
UE_TRACE_MINIMAL_EVENT_END()
UE_TRACE_MINIMAL_EVENT_BEGIN_EXTERN(ChaosVDLogger, ChaosVDParticleDestroyed)
UE_TRACE_MINIMAL_EVENT_FIELD(int32, SolverID)
UE_TRACE_MINIMAL_EVENT_FIELD(uint64, Cycle)
UE_TRACE_MINIMAL_EVENT_FIELD(int32, ParticleID)
UE_TRACE_MINIMAL_EVENT_END()
UE_TRACE_MINIMAL_EVENT_BEGIN_EXTERN(ChaosVDLogger, ChaosVDSolverStepStart)
UE_TRACE_MINIMAL_EVENT_FIELD(int32, SolverID)
UE_TRACE_MINIMAL_EVENT_FIELD(uint64, Cycle)
UE_TRACE_MINIMAL_EVENT_FIELD(UE::Trace::WideString, StepName)
UE_TRACE_MINIMAL_EVENT_END()
UE_TRACE_MINIMAL_EVENT_BEGIN_EXTERN(ChaosVDLogger, ChaosVDSolverStepEnd)
UE_TRACE_MINIMAL_EVENT_FIELD(int32, SolverID)
UE_TRACE_MINIMAL_EVENT_FIELD(uint64, Cycle)
UE_TRACE_MINIMAL_EVENT_FIELD(uint32, StepNumber)
UE_TRACE_MINIMAL_EVENT_END()
UE_TRACE_MINIMAL_EVENT_BEGIN_EXTERN(ChaosVDLogger, ChaosVDBinaryDataStart)
UE_TRACE_MINIMAL_EVENT_FIELD(uint64, Cycle)
UE_TRACE_MINIMAL_EVENT_FIELD(UE::Trace::WideString, TypeName)
UE_TRACE_MINIMAL_EVENT_FIELD(int32, DataID)
UE_TRACE_MINIMAL_EVENT_FIELD(uint32, DataSize)
UE_TRACE_MINIMAL_EVENT_FIELD(uint32, OriginalSize)
UE_TRACE_MINIMAL_EVENT_FIELD(bool, IsCompressed)
UE_TRACE_MINIMAL_EVENT_END()
UE_TRACE_MINIMAL_EVENT_BEGIN_EXTERN(ChaosVDLogger, ChaosVDBinaryDataContent)
UE_TRACE_MINIMAL_EVENT_FIELD(uint64, Cycle)
UE_TRACE_MINIMAL_EVENT_FIELD(int32, DataID)
UE_TRACE_MINIMAL_EVENT_FIELD(uint8[], RawData)
UE_TRACE_MINIMAL_EVENT_END()
UE_TRACE_MINIMAL_EVENT_BEGIN_EXTERN(ChaosVDLogger, ChaosVDBinaryDataEnd)
UE_TRACE_MINIMAL_EVENT_FIELD(uint64, Cycle)
UE_TRACE_MINIMAL_EVENT_FIELD(int32, DataID)
UE_TRACE_MINIMAL_EVENT_END()
UE_TRACE_MINIMAL_EVENT_BEGIN_EXTERN(ChaosVDLogger, ChaosVDSolverSimulationSpace)
UE_TRACE_MINIMAL_EVENT_FIELD(int32, SolverID)
UE_TRACE_MINIMAL_EVENT_FIELD(uint64, Cycle)
CVD_DEFINE_TRACE_VECTOR(Chaos::FReal, Position)
CVD_DEFINE_TRACE_ROTATOR(Chaos::FReal, Rotation)
UE_TRACE_MINIMAL_EVENT_END()
UE_TRACE_MINIMAL_EVENT_BEGIN_EXTERN(ChaosVDLogger, ChaosVDNonSolverLocation)
UE_TRACE_MINIMAL_EVENT_FIELD(uint64, Cycle)
CVD_DEFINE_TRACE_VECTOR(Chaos::FReal, Position)
UE_TRACE_MINIMAL_EVENT_FIELD(UE::Trace::WideString, DebugName)
UE_TRACE_MINIMAL_EVENT_END()
UE_TRACE_MINIMAL_EVENT_BEGIN_EXTERN(ChaosVDLogger, ChaosVDNonSolverTransform)
UE_TRACE_MINIMAL_EVENT_FIELD(uint64, Cycle)
CVD_DEFINE_TRACE_VECTOR(Chaos::FReal, Position)
CVD_DEFINE_TRACE_VECTOR(Chaos::FReal, Scale)
CVD_DEFINE_TRACE_ROTATOR(Chaos::FReal, Rotation)
UE_TRACE_MINIMAL_EVENT_FIELD(UE::Trace::WideString, DebugName)
UE_TRACE_MINIMAL_EVENT_END()
UE_TRACE_MINIMAL_EVENT_BEGIN_EXTERN(ChaosVDLogger, ChaosVDNetworkTickOffset)
UE_TRACE_MINIMAL_EVENT_FIELD(int32, Offset)
UE_TRACE_MINIMAL_EVENT_FIELD(int32, SolverID)
UE_TRACE_MINIMAL_EVENT_END()
UE_TRACE_MINIMAL_EVENT_BEGIN_EXTERN(ChaosVDLogger, ChaosVDRolledBackDataID)
UE_TRACE_MINIMAL_EVENT_FIELD(int32, DataID)
UE_TRACE_MINIMAL_EVENT_END()
UE_TRACE_MINIMAL_EVENT_BEGIN_EXTERN(ChaosVDLogger, ChaosVDUsesAutoRTFM)
UE_TRACE_MINIMAL_EVENT_FIELD(bool, bUsingAutoRTFM)
UE_TRACE_MINIMAL_EVENT_END()
UE_TRACE_MINIMAL_EVENT_BEGIN_EXTERN(ChaosVDLogger, ChaosVDTraceRelevancyVolume)
CVD_DEFINE_TRACE_VECTOR(Chaos::FReal, BoxMin)
CVD_DEFINE_TRACE_VECTOR(Chaos::FReal, BoxMax)
UE_TRACE_MINIMAL_EVENT_END()
UE_TRACE_MINIMAL_EVENT_BEGIN_EXTERN(ChaosVDLogger, ChaosVDDummyEvent)
UE_TRACE_MINIMAL_EVENT_FIELD(int32, SolverID)
UE_TRACE_MINIMAL_EVENT_END()
struct FChaosVDContext;
struct FChaosVDQueryVisitStep;
struct FChaosVDCollisionResponseParams;
struct FChaosVDCollisionObjectQueryParams;
struct FChaosVDCollisionQueryParams;
enum class EChaosVDSceneQueryMode;
enum class EChaosVDSceneQueryType;
struct FCollisionObjectQueryParams;
struct FCollisionResponseParams;
struct FCollisionQueryParams;
enum ECollisionChannel : int;
namespace Chaos
{
namespace VisualDebugger
{
class FChaosVDSerializableNameTable;
}
class FPBDCollisionConstraints;
class FPBDRigidsSOAs;
class FImplicitObject;
class FPhysicsSolverBase;
template <typename T, int d>
class TGeometryParticleHandles;
class FPBDCollisionConstraint;
class FParticlePairMidPhase;
template <typename PayloadType, typename T, int d>
class ISpatialAccelerationCollection;
}
using FChaosVDImplicitObjectWrapper = FChaosVDImplicitObjectDataWrapper<Chaos::FImplicitObjectPtr, Chaos::FChaosArchive>;
using FChaosVDSerializableNameTable = Chaos::VisualDebugger::FChaosVDSerializableNameTable;
enum class EChaosVDTraceBinaryDataOptions
{
None = 0,
/** Will trace the provided data buffer into CVD's trace channel, regardless id CVD's trace system is fully initialized.
* Data traced in this way, will not be backwards compatible as the required header data will not be ready yet */
ForceTrace = 1 << 0
};
ENUM_CLASS_FLAGS(EChaosVDTraceBinaryDataOptions)
struct FChaosVDParticleMetadata;
namespace Chaos::VD
{
template<typename TObjectID>
class FCachedObjectTraceIDByPtr
{
public:
template<typename ObjectType, typename HashProducer>
TObjectID FindOrProduceTraceObjectID(const ObjectType* ObjectPtr, const HashProducer& InHashProducer, bool& OutWasAlreadyCached)
{
if (ObjectPtr == nullptr)
{
return 0;
}
bool bHasCachedID = true;
TObjectID FoundID = CachedIDsByPtr.FindOrProduce(reinterpret_cast<const void*>(ObjectPtr), [&InHashProducer, &bHasCachedID]()
{
bHasCachedID = false;
return InHashProducer();
});
if (bHasCachedID)
{
OutWasAlreadyCached = true;
return FoundID;
}
return FoundID;
}
template<typename ObjectType>
void RemoveCachedTraceObjectID(const ObjectType* Implicit)
{
if (Implicit == nullptr)
{
return;
}
CachedIDsByPtr.Remove(reinterpret_cast<const void*>(Implicit));
}
void Reset()
{
CachedIDsByPtr.Reset();
}
private:
static constexpr int32 StripCount = 32;
TStripedMap<StripCount, const void*, TObjectID> CachedIDsByPtr;
};
struct FRecordingSessionState
{
FRecordingSessionState();
DECLARE_DELEGATE_RetVal_TwoParams(FChaosVDParticleMetadata, FParticleMetaDataGeneratorDelegate, const IPhysicsProxyBase*, const Chaos::FGeometryParticleHandle* ParticleHandle)
FParticleMetaDataGeneratorDelegate ExternalParticleMetadataGenerator;
FDelegateHandle RecordingStartedDelegateHandle;
FDelegateHandle RecordingStoppedDelegateHandle;
FDelegateHandle RecordingFullCaptureRequestedHandle;
TSet<int32> SolverIDsForDeltaRecording;
TSet<int32> RequestedFullCaptureSolverIDs;
TSharedRef<FChaosVDSerializableNameTable> CVDNameTable;
FBox TraceRelevancyVolume;
std::atomic<bool> bIsTracing;
FCachedObjectTraceIDByPtr<uint32> TracedGeometryHashCache;
FCachedObjectTraceIDByPtr<uint64> ParticleMetadataIDsCache;
FRWLock DeltaRecordingStatesLock;
uint64 GenerateUniqueID()
{
uint64 NewID = 0;
UE_AUTORTFM_OPEN
{
NewID = InternalGenericIDCounter++;
};
return NewID;
}
static constexpr uint64 InvalidUniqueID = 0;
private:
std::atomic<uint64> InternalGenericIDCounter;
};
}
/** Class containing all the Tracing logic to record data for the Chaos Visual Debugger tool */
class FChaosVisualDebuggerTrace
{
public:
/**
* Traces data from a Particle Handle. The CVD context currently pushed into will be used to tie this particle data to a specific solver frame and step
* @param ParticleHandle Handle to process and Trace
*/
static CHAOS_API void TraceParticle(const Chaos::FGeometryParticleHandle* ParticleHandle);
/**
* Traces data from a collection of Particle Handles using. The CVD context currently pushed into will be used to tie this particle data to a specific solver frame and step.
* It does not handle Full and Delta Recording automatically
* @param ParticleHandles Handles collection to process and Trace
*/
static CHAOS_API void TraceParticles(const Chaos::TGeometryParticleHandles<Chaos::FReal, 3>& ParticleHandles);
/**
* Traces the destruction event for the provided particle handled so it can be reproduces in the CVD tool
* @param ParticleHandle Handle that is being destroyed
*/
static CHAOS_API void TraceParticleDestroyed(const Chaos::FGeometryParticleHandle* ParticleHandle);
/**
* Traces data from particles on the provided FPBDRigidsSOAs. It traces only the DirtyParticles view unless a full capture was requested
* @param ParticlesSoA Particles SoA to evaluate and trace
*/
static CHAOS_API void TraceParticlesSoA(const Chaos::FPBDRigidsSOAs& ParticlesSoA, Chaos::FRigidClustering* ClusteringData = nullptr);
/** Traces the provided particle view in parallel */
template<typename ParticleType>
static void TraceParticlesView(const Chaos::TParticleView<ParticleType>& ParticlesView);
/** Traces a Particle pair MidPhase as binary data */
static CHAOS_API void TraceMidPhase(const Chaos::FParticlePairMidPhase* MidPhase);
/** Traces a Particle pair MidPhase as binary data from a provided CollisionConstraints object */
static CHAOS_API void TraceMidPhasesFromCollisionConstraints(Chaos::FPBDCollisionConstraints& InCollisionConstraints);
/** Traces all joint constraints in the provided container */
static CHAOS_API void TraceJointsConstraints(Chaos::FPBDJointConstraints& InJointConstraints);
/** Traces all character ground constraints in the provided container */
static CHAOS_API void TraceCharacterGroundConstraints(Chaos::FCharacterGroundConstraintContainer& InConstraints);
/** Traces a Particle pair MidPhase as binary data */
static CHAOS_API void TraceCollisionConstraint(const Chaos::FPBDCollisionConstraint* CollisionConstraint);
/** Traces a Particle pair MidPhase as binary data in parallel */
static CHAOS_API void TraceCollisionConstraintView(TArrayView<Chaos::FPBDCollisionConstraint* const> CollisionConstraintView);
/** Traces all supported constraints in the provided containers view */
static CHAOS_API void TraceConstraintsContainer(TConstArrayView<Chaos::FPBDConstraintContainer*> ConstraintContainersView);
/** Traces the start of a solver frame and it pushes its context data to the CVD TLS context stack */
static CHAOS_API void TraceSolverFrameStart(const FChaosVDContext& ContextData, const FString& InDebugName, int32 FrameNumber = INDEX_NONE);
/** Traces the end of a solver frame and removes its context data to the CVD TLS context stack */
static CHAOS_API void TraceSolverFrameEnd(const FChaosVDContext& ContextData);
/** Traces the start of a solver step
* @param StepName Name of the step. It will be used in the CVD Tool's UI
*/
static CHAOS_API void TraceSolverStepStart(FStringView StepName);
/** Traces the end of a solver step */
static CHAOS_API void TraceSolverStepEnd();
/** Traces the provider transform as simulation space of the solver that is currently on the CVD Context Stack
* @param Transform Simulation space Transform
*/
static CHAOS_API void TraceSolverSimulationSpace(const Chaos::FRigidTransform3& Transform);
/**
* Traces a binary blob of data outside of any solver frame solver step scope.
* @param InData Data to trace
* @param TypeName Type name the data represents. It is used during Trace Analysis serialize it back (this is not automatic)
*/
static CHAOS_API void TraceBinaryData(TConstArrayView<uint8> InData, FStringView TypeName, EChaosVDTraceBinaryDataOptions Options = EChaosVDTraceBinaryDataOptions::None);
/**
* Serializes the implicit object contained in the wrapper and trace its it as binary data
* The trace event is not tied to a particular Solver Frame/Step
* @param WrappedGeometryData Wrapper containing a ptr to the implicit and its ID
*/
UE_DEPRECATED(5.7, "Use the TraceImplicitObject version that takes a ptr to the implicit object itself")
static CHAOS_API void TraceImplicitObject(FChaosVDImplicitObjectWrapper WrappedGeometryData);
/**
* Serializes a implicit object
* The trace event is not tied to a particular Solver Frame/Step
* @param GeometryPtr Geometry instance to trace
* @return Hash of the traced geometry that will be used when the recording is loaded to match it to the correct piece of data
*/
static CHAOS_API uint32 TraceImplicitObject(Chaos::FImplicitObject* GeometryPtr);
/**
* Gathers and serializes the debug name and other metadata for the provider particle once. Any subsequent calls will only return the existing hash for the debug name
* The trace event is not tied to a particular Solver Frame/Step
* @param InParticleHandle Handle to the particle from which we want to trace its debug name
* @return Id of the traced particle metadata that will be used when the recording is loaded to match it to the correct piece of data
*/
static CHAOS_API uint64 TraceParticleMetadata(Chaos::FGeometryParticleHandle* InParticleHandle);
/**
* Removes an implicit object from the serialized geometry IDs cache, to ensure we re-serialize it with any new changes
* @param CachedGeometryToInvalidate Ptr to the Geometry we want to invalidate from the cache
*/
static CHAOS_API void InvalidateGeometryFromCache(const Chaos::FImplicitObject* CachedGeometryToInvalidate);
/**
* Removes the associated metadata id for a particle from the traced data IDs cache, to ensure we re-serialize it with any new changes
* @param InParticleToInvalidate Ptr to the particle we want to invalidate from the cache
*/
static CHAOS_API void InvalidateParticleMetadataFromCache(Chaos::FGeometryParticleHandle* InParticleToInvalidate);
/** Records a visit step of a scene query. This needs to me called within the scope of an inflight scene query
* @param InputGeometry : Geometry used as input to perform the query we are recording
* @param GeometryOrientation : Orientation of the input geometry
* @param Start : Start location of the Query.
* @param End : End location of the Query
* @param TraceChannel : Trace channel used for the query we are recording.
* @param Params : Collision query params used for the query we are recording.
* @param ResponseParams : Collision response params used for the query we are recording.
* @param ObjectParams : Collision object query params used for the query we are recording.
* @param SolverID : ID of the solver this where query is being performed.
* @param bIsRetry : Set to true if the query que are recording it is from a retry attempt.
* */
static CHAOS_API void TraceSceneQueryStart(const Chaos::FImplicitObject* InputGeometry, const FQuat& GeometryOrientation, const FVector& Start, const FVector& End, ECollisionChannel TraceChannel, FChaosVDCollisionQueryParams&& Params, FChaosVDCollisionResponseParams&& ResponseParams, FChaosVDCollisionObjectQueryParams&& ObjectParams, EChaosVDSceneQueryType QueryType, EChaosVDSceneQueryMode QueryMode, int32 SolverID, bool bIsRetry);
/** Records a visit step of a scene query. This needs to me called within the scope of an inflight scene query
* @param InQueryVisitData : Processed Scene Query step data.
* */
static CHAOS_API void TraceSceneQueryVisit(FChaosVDQueryVisitStep&& InQueryVisitData);
/** Records all the suppported accelerations structures contained by the provided acceleration structure collection
* @param InAccelerationCollection : Ptr to the collection we want to evaluate and trace.
* */
static CHAOS_API void TraceSceneAccelerationStructures(const Chaos::ISpatialAccelerationCollection<Chaos::FAccelerationStructureHandle, Chaos::FReal, 3>* InAccelerationCollection);
/** Records the current tick offset for any given solver. This is used in the CVD editor to sync solver tracks based on their network tick
* @param TickOffset : Offset relative to the server's solver current tick number.
* @param SolverID : Solver ID of the solver that has this offset.
* */
static CHAOS_API void TraceNetworkTickOffset(int32 TickOffset, int32 SolverID);
/** Records the provided box and the rest of the arguments, so then it can be visualized via debug draw during playback in the CVD editor
* @param InBox : Shape to record.
* @param Tag : FName that will be used as a tag for filtering & search, and debug draw as a text tag in CVD's viewport.
* @param Color : Color to apply to this shape when it is debug drawn in CVD.
* @param SolverID : ID of the solver this shape should be associated with. if no ID is provided, this shape will be added as part of the current game frame data bucket.
*/
static CHAOS_API void TraceDebugDrawBox(const FBox& InBox, FName Tag = NAME_None, FColor Color = FColor::Blue, int32 SolverID = INDEX_NONE);
/** Records the provided Line and the rest of the arguments, so then it can be visualized via debug draw during playback in the CVD editor
* @param InStartLocation : Start point of the line.
* @param InEndLocation : End point of the line.
* @param Tag : FName that will be used as a tag for filtering & search, and debug draw as a text tag in CVD's viewport.
* @param Color : Color to apply to this shape when it is debug drawn in CVD.
* @param SolverID : ID of the solver this shape should be associated with. if no ID is provided, this shape will be added as part of the current game frame data bucket.
*/
static CHAOS_API void TraceDebugDrawLine(const FVector& InStartLocation, const FVector& InEndLocation, FName Tag = NAME_None, FColor Color = FColor::Blue, int32 SolverID = INDEX_NONE);
/** Records the provided Vector and the rest of the arguments, so then it can be visualized via debug draw during playback in the CVD editor
* @param InStartLocation : Start point of the line.
* @param InVector : Vector we want to record.
* @param Tag : FName that will be used as a tag for filtering & search, and debug draw as a text tag in CVD's viewport.
* @param Color : Color to apply to this shape when it is debug drawn in CVD.
* @param SolverID : ID of the solver this shape should be associated with. if no ID is provided, this shape will be added as part of the current game frame data bucket.
*/
static CHAOS_API void TraceDebugDrawVector(const FVector& InStartLocation, const FVector& InVector, FName Tag = NAME_None, FColor Color = FColor::Blue, int32 SolverID = INDEX_NONE);
/** Records the provided Sphere and the rest of the arguments, so then it can be visualized via debug draw during playback in the CVD editor
* @param Center : Origin point of the Sphere.
* @param Radius : Radius of the Sphere.
* @param Tag : FName that will be used as a tag for filtering & search, and debug draw as a text tag in CVD's viewport.
* @param Color : Color to apply to this shape when it is debug drawn in CVD.
* @param SolverID : ID of the solver this shape should be associated with. if no ID is provided, this shape will be added as part of the current game frame data bucket.
*/
static CHAOS_API void TraceDebugDrawSphere(const FVector& Center, float Radius, FName Tag = NAME_None, FColor Color = FColor::Blue, int32 SolverID = INDEX_NONE);
/** Records the provided Implicit Object and the rest of the arguments, so then it can be visualized via debug draw during playback in the CVD editor
* @param Implicit : Ptr to the implicit object to record.
* @param InParentTransform : Root transform of the object that owns this geometry.
* @param Tag : FName that will be used as a tag for filtering & search, and debug draw as a text tag in CVD's viewport.
* @param Color : Color to apply to this shape when it is debug drawn in CVD.
* @param SolverID : ID of the solver this shape should be associated with. if no ID is provided, this shape will be added as part of the current game frame data bucket.
*/
static CHAOS_API void TraceDebugDrawImplicitObject(const Chaos::FImplicitObject* Implicit, const FTransform& InParentTransform, FName Tag = NAME_None, FColor Color = FColor::Blue, int32 SolverID = INDEX_NONE);
/**
* Sets a bounding box to be used to filter out particles and other data outside of it.
* @param InTraceRelevancyVolume Volume within which object's data should be traced
*/
static CHAOS_API void SetTraceRelevancyVolume(const FBox& InTraceRelevancyVolume);
/*
* Returns the currently bounding box used to determine if we should trace data or not, for objects that can be applied to.
*/
static FBox GetTraceRelevancyVolume()
{
return SessionState.TraceRelevancyVolume;
}
/**
* Evaluates the provided bounds to see if it is withing the current relevancy volume, if any.
* @param InChaosBounds Bounds to evaluate
* @return Returns true if the bounds are relevant or if there is no relevancy volume configured
*/
static bool IsInRelevancyVolume(const Chaos::FAABB3& InChaosBounds)
{
if (!SessionState.TraceRelevancyVolume.IsValid)
{
// An invalid volume means we want to trace everything
return true;
}
return SessionState.TraceRelevancyVolume.Intersect(FBox(InChaosBounds.Min(), InChaosBounds.Max()));
}
/**
* Evaluates the provided bounds to see if it is withing the current relevancy volume, if any.
* @param InBounds Bounds to evaluate
* @return Returns true if the bounds are relevant or if there is no relevancy volume configured
*/
static bool IsInRelevancyVolume(const FBox& InBounds)
{
if (!SessionState.TraceRelevancyVolume.IsValid)
{
// An invalid volume means we want to trace everything
return true;
}
return SessionState.TraceRelevancyVolume.Intersect(InBounds);
}
/**
* Evaluates the provided sphere to see if it is withing the current relevancy volume, if any.
* @param Center Center of the sphere
* @param Radius Radius of the Sphere
* @return Returns true if the sphere is relevant or if there is no relevancy volume configured
*/
static bool IsInRelevancyVolume(const FVector& Center, double Radius)
{
if (!SessionState.TraceRelevancyVolume.IsValid)
{
// An invalid volume means we want to trace everything
return true;
}
return FMath::SphereAABBIntersection(Center, Radius * Radius, SessionState.TraceRelevancyVolume);
}
/**
* Evaluates the provided point to see if it is withing the current relevancy volume, if any.
* @param InPosition point to evaluate
* @return Returns true if the point is relevant or if there is no relevancy volume configured
*/
static bool IsInRelevancyVolume(const Chaos::FVec3& InPosition)
{
if (!SessionState.TraceRelevancyVolume.IsValid)
{
// An invalid volume means we want to trace everything
return true;
}
return FMath::PointBoxIntersection(InPosition, SessionState.TraceRelevancyVolume);
}
/**
* Evaluates the provided line to see if it is withing the current relevancy volume, if any.
* @param InStartPoint Start point of the line to evaluate
* @param InEndPoint End point of the line to evaluate
* @return Returns true if the line is relevant or if there is no relevancy volume configured
*/
static bool IsInRelevancyVolume(const Chaos::FVec3& InStartPoint, const Chaos::FVec3& InEndPoint)
{
if (!SessionState.TraceRelevancyVolume.IsValid)
{
// An invalid volume means we want to trace everything
return true;
}
return SessionState.TraceRelevancyVolume.Intersect(FBox(FVector::Min(InStartPoint, InEndPoint), FVector::Max(InStartPoint, InEndPoint)));
}
static bool IsInRelevancyVolume(const Chaos::FGeometryParticleHandle* InParticleHandle);
static bool IsInRelevancyVolume(const Chaos::FImplicitObject* Geometry, const Chaos::FRigidTransform3& InTransform);
/** Returns the ID of the main solver of the provided world
* @param World Ptr to the world we want to get the solverID from
*/
template<typename WorldType>
static int32 GetSolverIDFromWorld(WorldType* World);
/** Returns the CVD solver ID of the provided solver
* @param Solver Reference to the solver instance we want to get the id from
*/
static int32 CHAOS_API GetSolverID(Chaos::FPhysicsSolverBase& Solver);
/** Returns true if the provided solver ID needs a Full Capture */
static CHAOS_API bool ShouldPerformFullCapture(int32 SolverID);
/**
* Gets the CVD Context data form an object that has such data. Usually Solvers
* @tparam T type of the object with CVD context
* @param ObjectWithContext A reference to where to get the CVD Context
* @param OutCVDContext A reference to the CVD contexts in the provided object
*/
template<typename T>
static void GetCVDContext(T& ObjectWithContext, FChaosVDContext& OutCVDContext);
/**
* Returns the debug name string of the provided object
*/
template<typename T>
static FString GetDebugName(T& ObjectWithDebugName);
/** Returns true if a CVD trace is running */
static CHAOS_API bool IsTracing();
/** Binds to the static events triggered by the ChaosVD Runtime module */
static void RegisterEventHandlers();
/** Unbinds to the static events triggered by the ChaosVD Runtime module */
static void UnregisterEventHandlers();
static CHAOS_API TSharedRef<FChaosVDSerializableNameTable>& GetNameTableInstance();
/**
* If the provided array is not empty, all data channels that are not in the list and can be disabled, will be disabled
* @param EnabledDataChannelsOverrideList List of the only channels that should be enabled by default
*/
static CHAOS_API void OverrideDefaultEnabledDataChannels(TConstArrayView<FString> EnabledDataChannelsOverrideList);
static CHAOS_API void RegisterExternalParticleDebugNameGenerator(const Chaos::VD::FRecordingSessionState::FParticleMetaDataGeneratorDelegate& InCallback);
private:
/**
* Traces data from all Child Particles from any Cluster Particle inside the provided view array, using the provided CVD Context and Clustering Data
* @param ParticlesView Particles Array view to process and Trace
* @param ClusteringData Object containing the mappings required to find all child particles for a given cluster particle
* @param CVDContextData Context to be used to tie this Trace event with specific solver frame and step
*/
static CHAOS_API void TraceParticleClusterChildData(const Chaos::TParticleView<Chaos::TPBDRigidParticles<Chaos::FReal, 3>>& ParticlesView, Chaos::FRigidClustering* ClusteringData, const FChaosVDContext& CVDContextData);
/**
* Traces data from a Particle Handle using the provided CVD Context
* @param ParticleHandle Handle to process and Trace
* @param ContextData Context to be used to tie this Trace event with specific solver frame and step
*/
static CHAOS_API void TraceParticle(Chaos::FGeometryParticleHandle* ParticleHandle, const FChaosVDContext& ContextData);
/**
* Traces the provided location using with the provided ID -
* These are not tied to any solver step so they will be recorded as part of the current game frame data
* @param InLocation Location to Trace
* @param DebugNameID Name to use as ID
*/
static void TraceNonSolverLocation(const FVector& InLocation, FStringView DebugNameID);
/**
* Traces the provided transform using with the provided ID -
* These are not tied to any solver step so they will be recorded as part of the current game frame data
* @param InTransform Transform to Trace
* @param DebugNameID Name to use as ID
*/
static void TraceNonSolverTransform(const FTransform& InTransform, FStringView DebugNameID);
/**
* Evaluated the current thread context to determine if we can trace the requested shape. It also updates the solver ID passed as reference
* if it was invalid, but we have a valid cvd context
* @param OutSolverID Solver ID to update if needed using the CVD context data
* @return True if we can trace shapes
*/
static bool CanTraceDebugDrawShape(int32& OutSolverID);
/** Resets the state of the CVD Tracer */
static void Reset();
static void HandleRecordingStop();
static void TraceArchiveHeader();
static void HandleRecordingStart();
/** Sets up the tracer to perform a full capture in the next solver frame */
static void PerformFullCapture(EChaosVDFullCaptureFlags CaptureOptions);
/** Setups the current Solver frame for a full capture if needed */
static void SetupForFullCaptureIfNeeded(int32 SolverID, bool& bOutFullCaptureRequested);
static Chaos::VD::FRecordingSessionState SessionState;
friend class FChaosEngineModule;
};
template <typename ParticleType>
void FChaosVisualDebuggerTrace::TraceParticlesView(const Chaos::TParticleView<ParticleType>& ParticlesView)
{
if (!IsTracing())
{
return;
}
const FChaosVDContext* CVDContextData = FChaosVDThreadContext::Get().GetCurrentContext(EChaosVDContextType::Solver);
if (!ensure(CVDContextData))
{
return;
}
FChaosVDContext CopyContext = *CVDContextData;
ParticlesView.ParallelFor([CopyContext](auto& Particle, int32 Index)
{
CVD_SCOPE_CONTEXT(CopyContext);
TraceParticle(Particle.Handle());
});
}
template <typename WorldType>
int32 FChaosVisualDebuggerTrace::GetSolverIDFromWorld(WorldType* World)
{
int32 SolverID = INDEX_NONE;
if (World)
{
if (Chaos::FPhysicsSolverBase* Solver = World->GetPhysicsScene() ? World->GetPhysicsScene()->GetSolver() : nullptr)
{
SolverID = GetSolverID(*Solver);
}
}
return SolverID;
}
template <typename T>
void FChaosVisualDebuggerTrace::GetCVDContext(T& ObjectWithContext, FChaosVDContext& OutCVDContext)
{
OutCVDContext = ObjectWithContext.GetChaosVDContextData();
}
template <typename T>
FString FChaosVisualDebuggerTrace::GetDebugName(T& ObjectWithDebugName)
{
#if CHAOS_SOLVER_DEBUG_NAME
return ObjectWithDebugName.GetDebugName().ToString();
#else
return FString("COMPILED OUT");
#endif
}
struct FChaosVDScopeSolverStep
{
FChaosVDScopeSolverStep(FStringView StepName)
{
CVD_TRACE_SOLVER_STEP_START(StepName);
}
~FChaosVDScopeSolverStep()
{
CVD_TRACE_SOLVER_STEP_END();
}
};
template<typename T>
struct FChaosVDScopeSolverFrame
{
FChaosVDScopeSolverFrame(T& InSolverRef) : SolverRef(InSolverRef)
{
CVD_TRACE_SOLVER_START_FRAME(T, SolverRef);
}
~FChaosVDScopeSolverFrame()
{
CVD_TRACE_SOLVER_END_FRAME(T, SolverRef);
}
T& SolverRef;
};
struct FChaosVDScopeSceneQueryVisit
{
FChaosVDScopeSceneQueryVisit(FChaosVDQueryVisitStep& InVisitData) : VisitData(InVisitData)
{
}
~FChaosVDScopeSceneQueryVisit()
{
CVD_TRACE_SCENE_QUERY_VISIT(MoveTemp(VisitData));
}
FChaosVDQueryVisitStep& VisitData;
};
namespace Chaos::VisualDebugger
{
template<typename TDataToSerialize>
void WriteDataToBuffer(TArray<uint8>& InOutDataBuffer, TDataToSerialize& Data)
{
FChaosVDMemoryWriter MemWriterAr(InOutDataBuffer, FChaosVisualDebuggerTrace::GetNameTableInstance());
MemWriterAr.SetShouldSkipUpdateCustomVersion(true);
Data.Serialize(MemWriterAr);
}
template<typename TDataToSerialize, typename TArchive>
void WriteDataToBuffer(TArray<uint8>& InOutDataBuffer, TDataToSerialize& Data)
{
FChaosVDMemoryWriter MemWriterAr(InOutDataBuffer, FChaosVisualDebuggerTrace::GetNameTableInstance());
TArchive Ar(MemWriterAr);
Ar.SetShouldSkipUpdateCustomVersion(true);
Data.Serialize(Ar);
}
}
#endif // WITH_CHAOS_VISUAL_DEBUGGER