2020-08-11 01:36:57 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "HeadlessChaosTestUtility.h"
# include "Chaos/ParticleHandle.h"
# include "Chaos/ErrorReporter.h"
# include "PhysicsProxy/SingleParticlePhysicsProxy.h"
# include "PhysicsProxy/GeometryCollectionPhysicsProxy.h"
# include "Chaos/Utilities.h"
# include "PBDRigidsSolver.h"
# include "ChaosSolversModule.h"
# include "Modules/ModuleManager.h"
# include "Chaos/ChaosEngineInterface.h"
# include "Chaos/ChaosScene.h"
# include "SQAccelerator.h"
# include "CollisionQueryFilterCallbackCore.h"
2020-09-01 14:07:48 -04:00
# include "BodyInstanceCore.h"
2020-08-11 01:36:57 -04:00
2020-11-24 18:42:39 -04:00
namespace Chaos
{
extern CHAOS_API float AsyncInterpolationMultiplier ;
}
2020-08-11 01:36:57 -04:00
namespace ChaosTest {
2021-04-29 19:32:06 -04:00
using namespace Chaos ;
2020-08-11 01:36:57 -04:00
using namespace ChaosInterface ;
2021-06-10 13:13:24 -04:00
// Returns true on raycast if we hit payload bounds.
struct FSimpleRaycastVisitor : ISpatialVisitor < FAccelerationStructureHandle >
{
using FPayload = FAccelerationStructureHandle ;
FVec3 Start ;
bool bHit ;
bool bQueryGameThread ; // Query game thread or physics thread data?
bool bUseQueryFilter ;
FCollisionFilterData FilterData ;
FSimpleRaycastVisitor ( const FVec3 & InStart , bool bInQueryGameThread )
: Start ( InStart )
, bHit ( false )
, bQueryGameThread ( bInQueryGameThread )
{
}
FSimpleRaycastVisitor ( const FVec3 & InStart , FCollisionFilterData & InFilterData , bool bInQueryGameThread )
: Start ( InStart )
, bHit ( false )
, bQueryGameThread ( bInQueryGameThread )
, bUseQueryFilter ( true )
, FilterData ( InFilterData )
{
}
virtual const void * GetQueryData ( ) const override
{
if ( bUseQueryFilter )
{
return & FilterData ;
}
return nullptr ;
}
enum class SQType
{
Raycast ,
Sweep ,
Overlap
} ;
bool VisitRaycast ( const TSpatialVisitorData < FPayload > & Data , FQueryFastData & CurData )
{
float OutTime = 0 ;
FVec3 OutPos ;
FVec3 OutNorm ;
int32 FaceIdx ;
if ( Data . Bounds . Raycast ( Start , CurData . Dir , CurData . CurrentLength , 0 , OutTime , OutPos , OutNorm , FaceIdx ) )
{
if ( bQueryGameThread )
{
FTransform ParticleTransform ( Data . Payload . GetExternalGeometryParticle_ExternalThread ( ) - > R ( ) , Data . Payload . GetExternalGeometryParticle_ExternalThread ( ) - > X ( ) ) ;
const FVec3 DirLocal = ParticleTransform . InverseTransformVectorNoScale ( CurData . Dir ) ;
const FVec3 StartLocal = ParticleTransform . InverseTransformPositionNoScale ( Start ) ;
bHit = Data . Payload . GetExternalGeometryParticle_ExternalThread ( ) - > Geometry ( ) - > Raycast ( StartLocal , DirLocal , CurData . CurrentLength , 0 , OutTime , OutPos , OutNorm , FaceIdx ) ;
}
else
{
FTransform ParticleTransform ( Data . Payload . GetGeometryParticleHandle_PhysicsThread ( ) - > R ( ) , Data . Payload . GetGeometryParticleHandle_PhysicsThread ( ) - > X ( ) ) ;
const FVec3 DirLocal = ParticleTransform . InverseTransformVectorNoScale ( CurData . Dir ) ;
const FVec3 StartLocal = ParticleTransform . InverseTransformPositionNoScale ( Start ) ;
bHit = Data . Payload . GetGeometryParticleHandle_PhysicsThread ( ) - > Geometry ( ) - > Raycast ( StartLocal , DirLocal , CurData . CurrentLength , 0 , OutTime , OutPos , OutNorm , FaceIdx ) ;
}
if ( bHit )
{
return false ;
}
}
return true ;
}
bool VisitSweep ( const TSpatialVisitorData < FPayload > & Data , FQueryFastData & CurData )
{
check ( false ) ;
return false ;
}
bool VisitOverlap ( const TSpatialVisitorData < FPayload > & Data )
{
check ( false ) ;
return false ;
}
virtual bool Overlap ( const TSpatialVisitorData < FPayload > & Instance ) override
{
check ( false ) ;
return false ;
}
virtual bool Raycast ( const TSpatialVisitorData < FPayload > & Instance , FQueryFastData & CurData ) override
{
return VisitRaycast ( Instance , CurData ) ;
}
virtual bool Sweep ( const TSpatialVisitorData < FPayload > & Instance , FQueryFastData & CurData ) override
{
check ( false ) ;
return false ;
}
} ;
2020-08-11 01:36:57 -04:00
FSQHitBuffer < ChaosInterface : : FOverlapHit > InSphereHelper ( const FChaosScene & Scene , const FTransform & InTM , const FReal Radius )
{
FChaosSQAccelerator SQAccelerator ( * Scene . GetSpacialAcceleration ( ) ) ;
FSQHitBuffer < ChaosInterface : : FOverlapHit > HitBuffer ;
FOverlapAllQueryCallback QueryCallback ;
2021-02-03 14:57:28 -04:00
SQAccelerator . Overlap ( TSphere < FReal , 3 > ( FVec3 ( 0 ) , Radius ) , InTM , HitBuffer , FChaosQueryFilterData ( ) , QueryCallback , FQueryDebugParams ( ) ) ;
2020-08-11 01:36:57 -04:00
return HitBuffer ;
}
2021-02-03 14:57:28 -04:00
2020-08-11 01:36:57 -04:00
GTEST_TEST ( EngineInterface , CreateAndReleaseActor )
{
2021-06-14 15:00:35 -04:00
FChaosScene Scene ( nullptr , /*AsyncDt=*/ - 1 ) ;
2021-02-03 14:57:28 -04:00
2020-08-11 01:36:57 -04:00
FActorCreationParams Params ;
Params . Scene = & Scene ;
2021-02-03 14:57:28 -04:00
FPhysicsActorHandle Proxy = nullptr ;
2020-08-11 01:36:57 -04:00
2021-02-03 14:57:28 -04:00
FChaosEngineInterface : : CreateActor ( Params , Proxy ) ;
auto & Particle = Proxy - > GetGameThreadAPI ( ) ;
EXPECT_NE ( Proxy , nullptr ) ;
2020-08-11 01:36:57 -04:00
{
2021-02-03 14:57:28 -04:00
auto Sphere = MakeUnique < TSphere < FReal , 3 > > ( FVec3 ( 0 ) , 3 ) ;
Proxy - > GetGameThreadAPI ( ) . SetGeometry ( MoveTemp ( Sphere ) ) ;
2020-08-11 01:36:57 -04:00
}
2021-02-03 14:57:28 -04:00
FChaosEngineInterface : : ReleaseActor ( Proxy , & Scene ) ;
EXPECT_EQ ( Proxy , nullptr ) ;
2020-08-11 01:36:57 -04:00
}
2021-02-03 14:57:28 -04:00
GTEST_TEST ( EngineInterface , CreateMoveAndReleaseInScene )
2020-08-11 01:36:57 -04:00
{
2021-06-14 15:00:35 -04:00
FChaosScene Scene ( nullptr , /*AsyncDt=*/ - 1 ) ;
2020-08-11 01:36:57 -04:00
FActorCreationParams Params ;
Params . Scene = & Scene ;
2021-02-03 14:57:28 -04:00
FPhysicsActorHandle Proxy = nullptr ;
2020-08-11 01:36:57 -04:00
2021-02-03 14:57:28 -04:00
FChaosEngineInterface : : CreateActor ( Params , Proxy ) ;
auto & Particle = Proxy - > GetGameThreadAPI ( ) ;
EXPECT_NE ( Proxy , nullptr ) ;
2020-08-11 01:36:57 -04:00
{
2021-02-03 14:57:28 -04:00
auto Sphere = MakeUnique < TSphere < FReal , 3 > > ( FVec3 ( 0 ) , 3 ) ;
Particle . SetGeometry ( MoveTemp ( Sphere ) ) ;
2020-08-11 01:36:57 -04:00
}
2021-02-03 14:57:28 -04:00
TArray < FPhysicsActorHandle > Proxys = { Proxy } ;
Scene . AddActorsToScene_AssumesLocked ( Proxys ) ;
2020-08-11 01:36:57 -04:00
//make sure acceleration structure has new actor right away
{
2021-02-03 14:57:28 -04:00
const auto HitBuffer = InSphereHelper ( Scene , FTransform : : Identity , 3 ) ;
EXPECT_EQ ( HitBuffer . GetNumHits ( ) , 1 ) ;
2020-08-11 01:36:57 -04:00
}
//make sure acceleration structure sees moved actor right away
2021-02-03 14:57:28 -04:00
const FTransform MovedTM ( FQuat : : Identity , FVec3 ( 100 , 0 , 0 ) ) ;
FChaosEngineInterface : : SetGlobalPose_AssumesLocked ( Proxy , MovedTM ) ;
2020-08-11 01:36:57 -04:00
{
2021-02-03 14:57:28 -04:00
const auto HitBuffer = InSphereHelper ( Scene , FTransform : : Identity , 3 ) ;
EXPECT_EQ ( HitBuffer . GetNumHits ( ) , 0 ) ;
2020-08-11 01:36:57 -04:00
2021-02-03 14:57:28 -04:00
const auto HitBuffer2 = InSphereHelper ( Scene , MovedTM , 3 ) ;
EXPECT_EQ ( HitBuffer2 . GetNumHits ( ) , 1 ) ;
2020-08-11 01:36:57 -04:00
}
//move actor back and acceleration structure sees it right away
2021-02-03 14:57:28 -04:00
FChaosEngineInterface : : SetGlobalPose_AssumesLocked ( Proxy , FTransform : : Identity ) ;
2020-08-11 01:36:57 -04:00
{
2021-02-03 14:57:28 -04:00
const auto HitBuffer = InSphereHelper ( Scene , FTransform : : Identity , 3 ) ;
EXPECT_EQ ( HitBuffer . GetNumHits ( ) , 1 ) ;
2020-08-11 01:36:57 -04:00
}
2021-02-03 14:57:28 -04:00
FChaosEngineInterface : : ReleaseActor ( Proxy , & Scene ) ;
EXPECT_EQ ( Proxy , nullptr ) ;
2020-08-11 01:36:57 -04:00
//make sure acceleration structure no longer has actor
{
2021-02-03 14:57:28 -04:00
const auto HitBuffer = InSphereHelper ( Scene , FTransform : : Identity , 3 ) ;
EXPECT_EQ ( HitBuffer . GetNumHits ( ) , 0 ) ;
2020-08-11 01:36:57 -04:00
}
}
2020-09-01 14:07:48 -04:00
template < typename TSolver >
2021-02-03 14:57:28 -04:00
void AdvanceSolverNoPushHelper ( TSolver * Solver , FReal Dt )
2020-09-01 14:07:48 -04:00
{
Solver - > AdvanceSolverBy ( Dt ) ;
}
2021-02-03 14:57:28 -04:00
GTEST_TEST ( EngineInterface , AccelerationStructureHasSyncTimestamp )
2020-09-01 14:07:48 -04:00
{
//make sure acceleration structure has appropriate sync time
2021-06-14 15:00:35 -04:00
FChaosScene Scene ( nullptr , /*AsyncDt=*/ - 1 ) ;
2020-09-01 14:07:48 -04:00
Scene . GetSolver ( ) - > SetThreadingMode_External ( EThreadingModeTemp : : SingleThread ) ;
2021-02-03 14:57:28 -04:00
EXPECT_EQ ( Scene . GetSpacialAcceleration ( ) - > GetSyncTimestamp ( ) , 0 ) ; //timestamp of 0 because we flush when scene is created
2020-09-01 14:07:48 -04:00
FReal TotalDt = 0 ;
2021-02-03 14:57:28 -04:00
for ( int Step = 1 ; Step < 10 ; + + Step )
2020-09-01 14:07:48 -04:00
{
FVec3 Grav ( 0 , 0 , - 1 ) ;
2021-06-14 15:00:35 -04:00
Scene . SetUpForFrame ( & Grav , 1 , 99999 , 99999 , 10 , false ) ;
2020-09-01 14:07:48 -04:00
Scene . StartFrame ( ) ;
Scene . GetSolver ( ) - > GetEvolution ( ) - > FlushSpatialAcceleration ( ) ; //make sure we get a new tree every step
Scene . EndFrame ( ) ;
2021-06-10 13:13:24 -04:00
EXPECT_EQ ( Scene . GetSpacialAcceleration ( ) - > GetSyncTimestamp ( ) , Step - 1 ) ;
2020-09-01 14:07:48 -04:00
}
}
2020-10-09 22:42:26 -04:00
GTEST_TEST ( EngineInterface , AccelerationStructureHasSyncTimestamp_MultiFrameDelay )
{
//make sure acceleration structure has appropriate sync time when PT falls behind GT
2021-06-14 15:00:35 -04:00
FChaosScene Scene ( nullptr , /*AsyncDt=*/ - 1 ) ;
2020-10-09 22:42:26 -04:00
Scene . GetSolver ( ) - > SetThreadingMode_External ( EThreadingModeTemp : : SingleThread ) ;
Scene . GetSolver ( ) - > SetStealAdvanceTasks_ForTesting ( true ) ; // prevents execution on StartFrame so we can execute task manually.
EXPECT_EQ ( Scene . GetSpacialAcceleration ( ) - > GetSyncTimestamp ( ) , 0 ) ; //timestamp of 0 because we flush when scene is created
FVec3 Grav ( 0 , 0 , - 1 ) ;
2021-06-14 15:00:35 -04:00
Scene . SetUpForFrame ( & Grav , 1 , 99999 , 99999 , 10 , false ) ;
2020-10-09 22:42:26 -04:00
// Game thread enqueues second solver task before first completes (we did not execute advance task)
Scene . StartFrame ( ) ;
Scene . EndFrame ( ) ;
Scene . StartFrame ( ) ;
// Execute first enqueued advance task
Scene . GetSolver ( ) - > PopAndExecuteStolenAdvanceTask_ForTesting ( ) ;
2020-11-24 18:42:39 -04:00
Scene . GetSolver ( ) - > GetEvolution ( ) - > FlushSpatialAcceleration ( ) ;
2020-10-09 22:42:26 -04:00
Scene . EndFrame ( ) ;
2021-06-10 13:13:24 -04:00
// Still timestamp 0, as we have only processed first PT step..
EXPECT_EQ ( Scene . GetSpacialAcceleration ( ) - > GetSyncTimestamp ( ) , 0 ) ;
2020-10-09 22:42:26 -04:00
Scene . StartFrame ( ) ;
2021-02-03 14:57:28 -04:00
2020-10-09 22:42:26 -04:00
// PT catches up during this frame
Scene . GetSolver ( ) - > PopAndExecuteStolenAdvanceTask_ForTesting ( ) ;
Scene . GetSolver ( ) - > PopAndExecuteStolenAdvanceTask_ForTesting ( ) ;
2020-11-24 18:42:39 -04:00
Scene . GetSolver ( ) - > GetEvolution ( ) - > FlushSpatialAcceleration ( ) ;
2020-10-09 22:42:26 -04:00
Scene . EndFrame ( ) ;
2021-02-03 14:57:28 -04:00
2021-06-10 13:13:24 -04:00
// New structure should be at 2, 3 steps have been processed, PT/GT are in sync.
EXPECT_EQ ( Scene . GetSpacialAcceleration ( ) - > GetSyncTimestamp ( ) , 2 ) ;
2021-02-03 14:57:28 -04:00
2020-10-09 22:42:26 -04:00
}
2021-02-03 14:57:28 -04:00
GTEST_TEST ( EngineInterface , AccelerationStructureHasSyncTimestamp_MultiFrameDelay2 )
2020-10-09 22:42:26 -04:00
{
//make sure acceleration structure has appropriate sync time when PT falls behind GT
2021-06-14 15:00:35 -04:00
FChaosScene Scene ( nullptr , /*AsyncDt=*/ - 1 ) ;
2020-10-09 22:42:26 -04:00
Scene . GetSolver ( ) - > SetThreadingMode_External ( EThreadingModeTemp : : SingleThread ) ;
Scene . GetSolver ( ) - > SetStealAdvanceTasks_ForTesting ( true ) ; // prevents execution on StartFrame so we can execute task manually.
2021-02-03 14:57:28 -04:00
EXPECT_EQ ( Scene . GetSpacialAcceleration ( ) - > GetSyncTimestamp ( ) , 0 ) ; //timestamp of 0 because we flush when scene is created
2020-10-09 22:42:26 -04:00
FVec3 Grav ( 0 , 0 , - 1 ) ;
2021-06-14 15:00:35 -04:00
Scene . SetUpForFrame ( & Grav , 1 , 99999 , 99999 , 10 , false ) ;
2020-10-09 22:42:26 -04:00
// PT not finished yet (we didn't execute solver task), should still be 0.
Scene . StartFrame ( ) ;
Scene . EndFrame ( ) ;
2021-02-03 14:57:28 -04:00
EXPECT_EQ ( Scene . GetSpacialAcceleration ( ) - > GetSyncTimestamp ( ) , 0 ) ;
2020-10-09 22:42:26 -04:00
// PT not finished yet (we didn't execute solver task), should still be 0.
Scene . StartFrame ( ) ;
Scene . EndFrame ( ) ;
2021-02-03 14:57:28 -04:00
EXPECT_EQ ( Scene . GetSpacialAcceleration ( ) - > GetSyncTimestamp ( ) , 0 ) ;
2020-10-09 22:42:26 -04:00
2021-06-10 13:13:24 -04:00
// First PT task finished this frame, we are two behind, still at 0 as structure is from first GT input (timestamp 0).
2020-10-09 22:42:26 -04:00
Scene . StartFrame ( ) ;
Scene . GetSolver ( ) - > PopAndExecuteStolenAdvanceTask_ForTesting ( ) ;
2020-11-24 18:42:39 -04:00
Scene . GetSolver ( ) - > GetEvolution ( ) - > FlushSpatialAcceleration ( ) ;
2020-10-09 22:42:26 -04:00
Scene . EndFrame ( ) ;
2021-06-10 13:13:24 -04:00
EXPECT_EQ ( Scene . GetSpacialAcceleration ( ) - > GetSyncTimestamp ( ) , 0 ) ;
2020-10-09 22:42:26 -04:00
Scene . GetSolver ( ) - > PopAndExecuteStolenAdvanceTask_ForTesting ( ) ;
Scene . GetSolver ( ) - > PopAndExecuteStolenAdvanceTask_ForTesting ( ) ;
2020-11-24 18:42:39 -04:00
Scene . GetSolver ( ) - > GetEvolution ( ) - > FlushSpatialAcceleration ( ) ;
2021-06-10 13:13:24 -04:00
// Remaining two PT tasks finish, we are caught up, GT is still time 0 as EndFrame has not updated our structure.
EXPECT_EQ ( Scene . GetSpacialAcceleration ( ) - > GetSyncTimestamp ( ) , 0 ) ;
2020-10-09 22:42:26 -04:00
2021-06-10 13:13:24 -04:00
// Popping acceleration structures from physics thread will give us timestamp of 2. (3 total GT inputs processed)
Scene . CopySolverAccelerationStructure ( ) ;
EXPECT_EQ ( Scene . GetSpacialAcceleration ( ) - > GetSyncTimestamp ( ) , 2 ) ;
// PT task this frame finishes before EndFrame, putting us at 3, in sync with GT.
2020-10-09 22:42:26 -04:00
Scene . StartFrame ( ) ;
Scene . GetSolver ( ) - > PopAndExecuteStolenAdvanceTask_ForTesting ( ) ;
2020-11-24 18:42:39 -04:00
Scene . GetSolver ( ) - > GetEvolution ( ) - > FlushSpatialAcceleration ( ) ;
2020-10-09 22:42:26 -04:00
Scene . EndFrame ( ) ;
2021-06-10 13:13:24 -04:00
EXPECT_EQ ( Scene . GetSpacialAcceleration ( ) - > GetSyncTimestamp ( ) , 3 ) ;
2020-10-09 22:42:26 -04:00
}
2020-10-22 19:19:16 -04:00
GTEST_TEST ( EngineInterface , PullFromPhysicsState_MultiFrameDelay )
{
2021-02-03 14:57:28 -04:00
// This test is designed to verify pulldata is being timestamped correctly, and that we will not write to a deleted GT Proxy
2020-10-22 19:19:16 -04:00
// in this case.
2021-06-14 15:00:35 -04:00
FChaosScene Scene ( nullptr , /*AsyncDt=*/ - 1 ) ;
2020-10-22 19:19:16 -04:00
Scene . GetSolver ( ) - > SetThreadingMode_External ( EThreadingModeTemp : : SingleThread ) ;
Scene . GetSolver ( ) - > SetStealAdvanceTasks_ForTesting ( true ) ; // prevents execution on StartFrame so we can execute task manually.
FVec3 Grav ( 0 , 0 , - 1 ) ;
2021-06-14 15:00:35 -04:00
Scene . SetUpForFrame ( & Grav , 1 , 99999 , 99999 , 10 , false ) ;
2020-10-22 19:19:16 -04:00
FActorCreationParams Params ;
Params . Scene = & Scene ;
Params . bSimulatePhysics = true ;
Params . bEnableGravity = true ;
Params . bStartAwake = true ;
2021-02-03 14:57:28 -04:00
// Create two Proxys, one to remove for test, the other to ensure we have > 0 proxies to hit the pull physics data path.
FPhysicsActorHandle Proxy = nullptr ;
FChaosEngineInterface : : CreateActor ( Params , Proxy ) ;
auto & Particle = Proxy - > GetGameThreadAPI ( ) ;
EXPECT_NE ( Proxy , nullptr ) ;
2020-10-22 19:19:16 -04:00
{
2021-02-03 14:57:28 -04:00
auto Sphere = MakeUnique < TSphere < FReal , 3 > > ( FVec3 ( 0 ) , 3 ) ;
Particle . SetGeometry ( MoveTemp ( Sphere ) ) ;
2020-10-22 19:19:16 -04:00
}
2021-02-03 14:57:28 -04:00
FPhysicsActorHandle Proxy2 = nullptr ;
FChaosEngineInterface : : CreateActor ( Params , Proxy2 ) ;
auto & Particle2 = Proxy2 - > GetGameThreadAPI ( ) ;
EXPECT_NE ( Proxy2 , nullptr ) ;
2020-10-22 19:19:16 -04:00
{
2021-02-03 14:57:28 -04:00
auto Sphere = MakeUnique < TSphere < FReal , 3 > > ( FVec3 ( 0 ) , 3 ) ;
Particle2 . SetGeometry ( MoveTemp ( Sphere ) ) ;
2020-10-22 19:19:16 -04:00
}
2021-02-03 14:57:28 -04:00
TArray < FPhysicsActorHandle > Proxys = { Proxy , Proxy2 } ;
Scene . AddActorsToScene_AssumesLocked ( Proxys ) ;
2020-10-22 19:19:16 -04:00
// verify external timestamps are as expected.
auto & MarshallingManager = Scene . GetSolver ( ) - > GetMarshallingManager ( ) ;
2021-06-10 13:13:24 -04:00
EXPECT_EQ ( MarshallingManager . GetExternalTimestamp_External ( ) , 0 ) ;
2020-10-22 19:19:16 -04:00
2021-02-03 14:57:28 -04:00
// Execute a frame such that Proxys should be initialized in physics thread and game thread.
2020-10-22 19:19:16 -04:00
Scene . StartFrame ( ) ;
2021-06-10 13:13:24 -04:00
EXPECT_EQ ( MarshallingManager . GetExternalTimestamp_External ( ) , 1 ) ;
2020-10-22 19:19:16 -04:00
Scene . GetSolver ( ) - > PopAndExecuteStolenAdvanceTask_ForTesting ( ) ;
Scene . EndFrame ( ) ;
// run GT frame, no PT task executed.
Scene . StartFrame ( ) ;
2021-06-10 13:13:24 -04:00
EXPECT_EQ ( MarshallingManager . GetExternalTimestamp_External ( ) , 2 ) ;
2020-10-22 19:19:16 -04:00
Scene . EndFrame ( ) ;
// enqueue another frame.
Scene . StartFrame ( ) ;
2021-06-10 13:13:24 -04:00
EXPECT_EQ ( MarshallingManager . GetExternalTimestamp_External ( ) , 3 ) ;
2021-02-03 14:57:28 -04:00
2021-06-10 13:13:24 -04:00
// Remove Proxy, is stamped with external time 3. PT needs to run 3 frames before this will be removed,
2020-10-22 19:19:16 -04:00
// as we are two PT tasks behind, and this has not been enqueued yet.
2021-02-03 14:57:28 -04:00
auto StaleProxy = Proxy ;
FChaosEngineInterface : : ReleaseActor ( Proxy , & Scene ) ;
EXPECT_EQ ( Proxy , nullptr ) ;
2021-03-05 19:27:14 -04:00
EXPECT_EQ ( StaleProxy - > GetSyncTimestamp ( ) - > bDeleted , true ) ;
2020-10-22 19:19:16 -04:00
2021-06-10 13:13:24 -04:00
// Run PT task for internal timestamp 1.
2020-10-22 19:19:16 -04:00
Scene . GetSolver ( ) - > PopAndExecuteStolenAdvanceTask_ForTesting ( ) ;
2021-06-10 13:13:24 -04:00
// Proxy should not get touched in Pull, as timestamp from removal should be greater than pulldata timestamp.
2020-10-22 19:19:16 -04:00
// (if it was touched we'd crash as it is now deleted).
Scene . EndFrame ( ) ;
Scene . StartFrame ( ) ;
2021-06-10 13:13:24 -04:00
EXPECT_EQ ( MarshallingManager . GetExternalTimestamp_External ( ) , 4 ) ;
2021-03-05 19:27:14 -04:00
EXPECT_EQ ( StaleProxy - > GetSyncTimestamp ( ) - > bDeleted , true ) ;
2020-10-22 19:19:16 -04:00
2021-02-03 14:57:28 -04:00
// run pt task for internal timestamp 3. Proxy still not removed on PT.
2020-10-22 19:19:16 -04:00
Scene . GetSolver ( ) - > PopAndExecuteStolenAdvanceTask_ForTesting ( ) ;
2021-02-03 14:57:28 -04:00
EXPECT_EQ ( Scene . GetSolver ( ) - > GetEvolution ( ) - > GetParticles ( ) . GetAllParticlesView ( ) . Num ( ) , 2 ) ; // none have been removed on pt, still 2 Proxys.
2020-10-22 19:19:16 -04:00
2021-02-03 14:57:28 -04:00
// Proxy should not get touched in pull, as timestamp from removal is less than pulldata timestamp (3 < 4)
2020-10-22 19:19:16 -04:00
// If this crashes in pull, that means this test has regressed. (Pulldata timestamp is likely wrong).
Scene . EndFrame ( ) ;
Scene . StartFrame ( ) ;
2021-06-10 13:13:24 -04:00
EXPECT_EQ ( MarshallingManager . GetExternalTimestamp_External ( ) , 5 ) ;
2021-03-05 19:27:14 -04:00
EXPECT_EQ ( StaleProxy - > GetSyncTimestamp ( ) - > bDeleted , true ) ;
2021-02-03 14:57:28 -04:00
EXPECT_EQ ( Scene . GetSolver ( ) - > GetEvolution ( ) - > GetParticles ( ) . GetAllParticlesView ( ) . Num ( ) , 2 ) ; // Proxys not yet removed on pt, still 2.
2020-10-22 19:19:16 -04:00
2021-02-03 14:57:28 -04:00
// This is PT task that should remove Proxy (internal timestamp 4, matching stamp on removed Proxy's dirty data).
2020-10-22 19:19:16 -04:00
Scene . GetSolver ( ) - > PopAndExecuteStolenAdvanceTask_ForTesting ( ) ;
2021-02-03 14:57:28 -04:00
EXPECT_EQ ( Scene . GetSolver ( ) - > GetEvolution ( ) - > GetParticles ( ) . GetAllParticlesView ( ) . Num ( ) , 1 ) ; // one Proxy removed on pt, one remaining.
2020-10-22 19:19:16 -04:00
// This PT task catches up to gamethread.
Scene . GetSolver ( ) - > PopAndExecuteStolenAdvanceTask_ForTesting ( ) ;
Scene . EndFrame ( ) ;
}
2021-06-10 13:13:24 -04:00
GTEST_TEST ( EngineInterface , UpdatingAccelerationStructurePrePreFilterOnShapeFilterChange )
{
2021-06-14 15:00:35 -04:00
const float PhysicsTimestep = 1 ; // 1 second
FChaosScene Scene ( nullptr , PhysicsTimestep ) ;
2021-06-10 13:13:24 -04:00
Scene . GetSolver ( ) - > SetThreadingMode_External ( EThreadingModeTemp : : SingleThread ) ;
float DeltaSeconds = PhysicsTimestep ;
FVec3 Grav ( 0 , 0 , - 1 ) ;
2021-06-14 15:00:35 -04:00
Scene . SetUpForFrame ( & Grav , DeltaSeconds , 9999 , 9999 , 9999 , false ) ;
2020-10-09 22:42:26 -04:00
2021-06-10 13:13:24 -04:00
// Raycast params, aimed to hit our particle at (0,0,0)
const FVector Start ( 0 , 0 , - 5 ) ;
const FVector Dir ( 0 , 0 , 1 ) ;
const float Length = 50 ;
// Init kinematic particle, sphere radius 3
FActorCreationParams Params ;
Params . Scene = & Scene ;
Params . bSimulatePhysics = false ;
Params . bEnableGravity = true ;
Params . bStartAwake = true ;
FPhysicsActorHandle Proxy = nullptr ;
FChaosEngineInterface : : CreateActor ( Params , Proxy ) ;
auto & Particle = Proxy - > GetGameThreadAPI ( ) ;
{
auto Sphere = MakeUnique < TSphere < FReal , 3 > > ( FVec3 ( 0 ) , 3 ) ;
Particle . SetGeometry ( MoveTemp ( Sphere ) ) ;
}
TArray < FPhysicsActorHandle > Proxys = { Proxy } ;
Scene . AddActorsToScene_AssumesLocked ( Proxys ) ;
// Execute a whole frame such that particle is initialized on physics thread
Scene . StartFrame ( ) ;
Scene . EndFrame ( ) ;
// Make query filter that will allow query against particle that blocks/touches all channels.
// Filter will fail against particle that has no query allowed (default query filter).
FCollisionFilterData QueryFilter ;
QueryFilter . Word0 = 1 ; // Setting to non-zero to set query type that will filter
// This is setting a somewhat arbritrary trace channels. It's very hard to make sense of these bitfields at this level of API.
// Below particle uses a filter that touches/blocks anything, so these bits are enough to make filter pass.
QueryFilter . Word3 = 7 < < 21 ;
// Get collision data off shape
for ( const TUniquePtr < Chaos : : FPerShapeData > & Shape : Particle . ShapesArray ( ) )
{
const FCollisionData & CollisionData = Shape - > GetCollisionData ( ) ;
EXPECT_EQ ( CollisionData . QueryData . Word0 , 0 ) ; // ensure query filter is defaulted to 0 (no query allowed at all)
// Verify query is filtered out with default collision data on shape
bool bFiltered = PrePreFilterImp ( QueryFilter , CollisionData . QueryData ) ;
EXPECT_EQ ( bFiltered , true ) ;
}
// Query against particle on game thread, should fail to hit due to particle filter being defaulted, no touch/block set.
{
bool bQueryGameThread = true ;
FSimpleRaycastVisitor Visitor ( Start , QueryFilter , bQueryGameThread ) ;
Scene . GetSpacialAcceleration ( ) - > Raycast ( Start , Dir , Length , Visitor ) ;
EXPECT_EQ ( Visitor . bHit , false ) ;
}
// Change filter data on game thread to contain touch/block on all channels.
FCollisionFilterData NewParticleQueryFilter ;
NewParticleQueryFilter . Word1 = TNumericLimits < int32 > : : Max ( ) ;
NewParticleQueryFilter . Word2 = TNumericLimits < int32 > : : Max ( ) ;
for ( const TUniquePtr < Chaos : : FPerShapeData > & Shape : Particle . ShapesArray ( ) )
{
const FCollisionData & CollisionData = Shape - > GetCollisionData ( ) ;
// Update filter
FCollisionData NewCollisionData = CollisionData ;
NewCollisionData . QueryData = NewParticleQueryFilter ;
Shape - > SetCollisionData ( NewCollisionData ) ;
// Filter with new data, ensuring we pass and are not filtered out.
bool bFiltered = PrePreFilterImp ( QueryFilter , NewCollisionData . QueryData ) ;
EXPECT_EQ ( bFiltered , false ) ;
}
// Update particle in GT accel structure so cached PrePreFilter updates
Scene . UpdateActorInAccelerationStructure ( Proxy ) ;
// Query against particle on game thread, should hit with new filter.
{
bool bQueryGameThread = true ;
FSimpleRaycastVisitor Visitor ( Start , QueryFilter , bQueryGameThread ) ;
Scene . GetSpacialAcceleration ( ) - > Raycast ( Start , Dir , Length , Visitor ) ;
EXPECT_EQ ( Visitor . bHit , true ) ;
}
// Tick to push to physics thread
Scene . StartFrame ( ) ;
Scene . EndFrame ( ) ;
// Query particle on physics thread, expected to hit with new filter.
// If this fails it means we did not update cached filter data in acceleration structure entry.
{
bool bQueryGameThread = false ;
FSimpleRaycastVisitor Visitor ( Start , QueryFilter , bQueryGameThread ) ;
Scene . GetSolver ( ) - > GetEvolution ( ) - > GetSpatialAcceleration ( ) - > Raycast ( Start , Dir , Length , Visitor ) ;
EXPECT_EQ ( Visitor . bHit , true ) ;
}
}
2021-06-14 18:51:24 -04:00
// Disabled until we move fix with kineamtic bounds update on PushToPhysicsState into this branch. Might also need to remove bounds computation in ApplyKinematicTarget.
GTEST_TEST ( EngineInterface , DISABLED_KinematicTargetsPassingGTWrongAccelBoundsBeforeHittingTarget )
2021-06-10 13:13:24 -04:00
{
// This test is designed to catch an edge case with kinematic targets (or other things interpolated over multiple physics steps), and acceleration structure bounds.
// Timestep is setup such that 1 GT frame = 10 physics steps, we have to make sure that if a non-final step gives an acceleration structure to game thread, in which
// kinematic has not reached target yet, that the bounds in structure representing interpolated position do not make it to game thread, otherwise game thread has
// position at target, but bounds that don't match.
2021-06-14 15:00:35 -04:00
const float PhysicsTimestep = 1 ; // 1 second
2021-06-10 13:13:24 -04:00
// Setup solver so we can manually execute each physics step.
2021-06-14 15:00:35 -04:00
FChaosScene Scene ( nullptr , PhysicsTimestep ) ;
2021-06-10 13:13:24 -04:00
Scene . GetSolver ( ) - > SetThreadingMode_External ( EThreadingModeTemp : : SingleThread ) ;
Scene . GetSolver ( ) - > SetStealAdvanceTasks_ForTesting ( true ) ;
// In this test we have a 10s Dt, split into 10 physics steps of 1s.
const int32 PhysicsStepsInFrame = 10 ;
float DeltaSeconds = PhysicsTimestep * PhysicsStepsInFrame ;
FVec3 Grav ( 0 , 0 , - 1 ) ;
2021-06-14 15:00:35 -04:00
Scene . SetUpForFrame ( & Grav , DeltaSeconds , 9999 , 9999 , 9999 , false ) ;
2021-06-11 06:48:21 -04:00
2021-06-10 13:13:24 -04:00
// Raycast params, aimed to hit our kinematic target (10,0,0)
const FVector Start ( 10 , 0 , - 5 ) ;
const FVector Dir ( 0 , 0 , 1 ) ;
const float Length = 50 ;
// Init kinematic particle, sphere radius 3
FActorCreationParams Params ;
Params . Scene = & Scene ;
Params . bSimulatePhysics = false ;
Params . bEnableGravity = true ;
Params . bStartAwake = true ;
FPhysicsActorHandle Proxy = nullptr ;
FChaosEngineInterface : : CreateActor ( Params , Proxy ) ;
auto & Particle = Proxy - > GetGameThreadAPI ( ) ;
{
auto Sphere = MakeUnique < TSphere < FReal , 3 > > ( FVec3 ( 0 ) , 3 ) ;
Particle . SetGeometry ( MoveTemp ( Sphere ) ) ;
}
TArray < FPhysicsActorHandle > Proxys = { Proxy } ;
Scene . AddActorsToScene_AssumesLocked ( Proxys ) ;
// Execute a whole frame such that particle is initialized on physics thread
Scene . StartFrame ( ) ;
for ( int32 PhysicsTicks = 0 ; PhysicsTicks < PhysicsStepsInFrame ; + + PhysicsTicks )
{
// Tick each physics step generated from game thread input
Scene . GetSolver ( ) - > PopAndExecuteStolenAdvanceTask_ForTesting ( ) ;
}
Scene . EndFrame ( ) ;
// Set kinematic target to (10,0,0) on game thread
FTransform Target ( FVector ( 10 , 0 , 0 ) ) ;
FChaosEngineInterface : : SetKinematicTarget_AssumesLocked ( Proxy , Target ) ;
// Confirm particle is at target on game thread with raycast.
{
FSimpleRaycastVisitor Visitor ( Start , true ) ;
Scene . GetSpacialAcceleration ( ) - > Raycast ( Start , Dir , Length , Visitor ) ;
EXPECT_EQ ( Visitor . bHit , true ) ;
}
// Tick game thread again, this enqueues 10 physics steps, kinematic will interpolate
// to target on physics thread over duration of these 10 steps.
Scene . StartFrame ( ) ;
for ( int32 PhysicsTick = 0 ; PhysicsTick < PhysicsStepsInFrame ; + + PhysicsTick )
{
Scene . GetSolver ( ) - > PopAndExecuteStolenAdvanceTask_ForTesting ( ) ;
if ( PhysicsTick = = 2 )
{
// On this arbritrary tick, copy acceleration structure to game thread,
// at this point we have sim'd only some of the physics steps for this frame.
// kinematic target is still interpolating, has not reached target of (10,0,0) yet.
// When this was broken this would give game thread a structure with
// the bounds of interpolated position (which is wrong because game thread particle is at target!)
Scene . CopySolverAccelerationStructure ( ) ;
// Verify the game thread particle can still be queried at target (verifying bounds and particle position are still correct)
{
FSimpleRaycastVisitor Visitor ( Start , true ) ;
Scene . GetSpacialAcceleration ( ) - > Raycast ( Start , Dir , Length , Visitor ) ;
EXPECT_EQ ( Visitor . bHit , true ) ;
}
}
}
// Finish frame
Scene . EndFrame ( ) ;
// Verify can still query game thread particle at our target.
{
FSimpleRaycastVisitor Visitor ( Start , true ) ;
Scene . GetSpacialAcceleration ( ) - > Raycast ( Start , Dir , Length , Visitor ) ;
EXPECT_EQ ( Visitor . bHit , true ) ;
}
}
2020-10-09 22:42:26 -04:00
2021-02-03 14:57:28 -04:00
GTEST_TEST ( EngineInterface , CreateActorPostFlush )
2020-09-01 14:07:48 -04:00
{
2021-06-14 15:00:35 -04:00
FChaosScene Scene ( nullptr , /*AsyncDt=*/ - 1 ) ;
2020-09-01 14:07:48 -04:00
Scene . GetSolver ( ) - > SetThreadingMode_External ( EThreadingModeTemp : : SingleThread ) ;
FActorCreationParams Params ;
Params . Scene = & Scene ;
2021-02-03 14:57:28 -04:00
FPhysicsActorHandle Proxy = nullptr ;
2020-09-01 14:07:48 -04:00
2021-02-03 14:57:28 -04:00
FChaosEngineInterface : : CreateActor ( Params , Proxy ) ;
auto & Particle = Proxy - > GetGameThreadAPI ( ) ;
EXPECT_NE ( Proxy , nullptr ) ;
2020-09-01 14:07:48 -04:00
{
2021-02-03 14:57:28 -04:00
auto Sphere = MakeUnique < TSphere < FReal , 3 > > ( FVec3 ( 0 ) , 3 ) ;
Particle . SetGeometry ( MoveTemp ( Sphere ) ) ;
2020-09-01 14:07:48 -04:00
}
//tick solver but don't call EndFrame (want to flush and swap manually)
{
FVec3 Grav ( 0 , 0 , - 1 ) ;
2021-06-14 15:00:35 -04:00
Scene . SetUpForFrame ( & Grav , 1 , 99999 , 99999 , 10 , false ) ;
2020-09-01 14:07:48 -04:00
Scene . StartFrame ( ) ;
}
//make sure acceleration structure is built
Scene . GetSolver ( ) - > GetEvolution ( ) - > FlushSpatialAcceleration ( ) ;
//create actor after structure is finished, but before swap happens
2021-02-03 14:57:28 -04:00
TArray < FPhysicsActorHandle > Proxys = { Proxy } ;
Scene . AddActorsToScene_AssumesLocked ( Proxys ) ;
2020-09-01 14:07:48 -04:00
Scene . CopySolverAccelerationStructure ( ) ; //trigger swap manually and see pending changes apply
{
2021-02-03 14:57:28 -04:00
const auto HitBuffer = InSphereHelper ( Scene , FTransform : : Identity , 3 ) ;
EXPECT_EQ ( HitBuffer . GetNumHits ( ) , 1 ) ;
2020-09-01 14:07:48 -04:00
}
}
2021-02-03 14:57:28 -04:00
GTEST_TEST ( EngineInterface , MoveActorPostFlush )
2020-09-01 14:07:48 -04:00
{
2021-06-14 15:00:35 -04:00
FChaosScene Scene ( nullptr , /*AsyncDt=*/ - 1 ) ;
2020-09-01 14:07:48 -04:00
Scene . GetSolver ( ) - > SetThreadingMode_External ( EThreadingModeTemp : : SingleThread ) ;
FActorCreationParams Params ;
Params . Scene = & Scene ;
2021-02-03 14:57:28 -04:00
FPhysicsActorHandle Proxy = nullptr ;
2020-09-01 14:07:48 -04:00
2021-02-03 14:57:28 -04:00
FChaosEngineInterface : : CreateActor ( Params , Proxy ) ;
auto & Particle = Proxy - > GetGameThreadAPI ( ) ;
EXPECT_NE ( Proxy , nullptr ) ;
2020-09-01 14:07:48 -04:00
{
2021-02-03 14:57:28 -04:00
auto Sphere = MakeUnique < TSphere < FReal , 3 > > ( FVec3 ( 0 ) , 3 ) ;
Particle . SetGeometry ( MoveTemp ( Sphere ) ) ;
2020-09-01 14:07:48 -04:00
}
//create actor before structure is ticked
2021-02-03 14:57:28 -04:00
TArray < FPhysicsActorHandle > Proxys = { Proxy } ;
Scene . AddActorsToScene_AssumesLocked ( Proxys ) ;
2020-09-01 14:07:48 -04:00
2021-02-03 14:57:28 -04:00
//tick solver so that Proxy is created, but don't call EndFrame (want to flush and swap manually)
2020-09-01 14:07:48 -04:00
{
FVec3 Grav ( 0 , 0 , - 1 ) ;
2021-06-14 15:00:35 -04:00
Scene . SetUpForFrame ( & Grav , 1 , 99999 , 99999 , 10 , false ) ;
2020-09-01 14:07:48 -04:00
Scene . StartFrame ( ) ;
}
//make sure acceleration structure is built
Scene . GetSolver ( ) - > GetEvolution ( ) - > FlushSpatialAcceleration ( ) ;
//move object to get hit (shows pending move is applied)
2021-02-03 14:57:28 -04:00
FChaosEngineInterface : : SetGlobalPose_AssumesLocked ( Proxy , FTransform ( FRotation3 : : FromIdentity ( ) , FVec3 ( 100 , 0 , 0 ) ) ) ;
2020-09-01 14:07:48 -04:00
Scene . CopySolverAccelerationStructure ( ) ; //trigger swap manually and see pending changes apply
{
2021-02-03 14:57:28 -04:00
TRigidTransform < FReal , 3 > OverlapTM ( FVec3 ( 100 , 0 , 0 ) , FRotation3 : : FromIdentity ( ) ) ;
const auto HitBuffer = InSphereHelper ( Scene , OverlapTM , 3 ) ;
EXPECT_EQ ( HitBuffer . GetNumHits ( ) , 1 ) ;
2020-09-01 14:07:48 -04:00
}
}
2021-02-03 14:57:28 -04:00
GTEST_TEST ( EngineInterface , RemoveActorPostFlush )
2020-09-01 14:07:48 -04:00
{
2021-06-14 15:00:35 -04:00
FChaosScene Scene ( nullptr , /*AsyncDt=*/ - 1 ) ;
2020-09-01 14:07:48 -04:00
Scene . GetSolver ( ) - > SetThreadingMode_External ( EThreadingModeTemp : : SingleThread ) ;
FActorCreationParams Params ;
Params . Scene = & Scene ;
2021-02-03 14:57:28 -04:00
FPhysicsActorHandle Proxy = nullptr ;
2020-09-01 14:07:48 -04:00
2021-02-03 14:57:28 -04:00
FChaosEngineInterface : : CreateActor ( Params , Proxy ) ;
auto & Particle = Proxy - > GetGameThreadAPI ( ) ;
EXPECT_NE ( Proxy , nullptr ) ;
2020-09-01 14:07:48 -04:00
{
2021-02-03 14:57:28 -04:00
auto Sphere = MakeUnique < TSphere < FReal , 3 > > ( FVec3 ( 0 ) , 3 ) ;
Particle . SetGeometry ( MoveTemp ( Sphere ) ) ;
2020-09-01 14:07:48 -04:00
}
//create actor before structure is ticked
2021-02-03 14:57:28 -04:00
TArray < FPhysicsActorHandle > Proxys = { Proxy } ;
Scene . AddActorsToScene_AssumesLocked ( Proxys ) ;
2020-09-01 14:07:48 -04:00
2021-02-03 14:57:28 -04:00
//tick solver so that Proxy is created, but don't call EndFrame (want to flush and swap manually)
2020-09-01 14:07:48 -04:00
{
FVec3 Grav ( 0 , 0 , - 1 ) ;
2021-06-14 15:00:35 -04:00
Scene . SetUpForFrame ( & Grav , 1 , 99999 , 99999 , 10 , false ) ;
2020-09-01 14:07:48 -04:00
Scene . StartFrame ( ) ;
}
//make sure acceleration structure is built
Scene . GetSolver ( ) - > GetEvolution ( ) - > FlushSpatialAcceleration ( ) ;
//delete object to get no hit
2021-02-03 14:57:28 -04:00
FChaosEngineInterface : : ReleaseActor ( Proxy , & Scene ) ;
2020-09-01 14:07:48 -04:00
Scene . CopySolverAccelerationStructure ( ) ; //trigger swap manually and see pending changes apply
{
2021-02-03 14:57:28 -04:00
const auto HitBuffer = InSphereHelper ( Scene , FTransform : : Identity , 3 ) ;
EXPECT_EQ ( HitBuffer . GetNumHits ( ) , 0 ) ;
2020-09-01 14:07:48 -04:00
}
}
2021-02-03 14:57:28 -04:00
GTEST_TEST ( EngineInterface , RemoveActorPostFlush0Dt )
2020-09-01 14:07:48 -04:00
{
2021-06-14 15:00:35 -04:00
FChaosScene Scene ( nullptr , /*AsyncDt=*/ - 1 ) ;
2020-09-01 14:07:48 -04:00
Scene . GetSolver ( ) - > SetThreadingMode_External ( EThreadingModeTemp : : SingleThread ) ;
FActorCreationParams Params ;
Params . Scene = & Scene ;
2021-02-03 14:57:28 -04:00
FPhysicsActorHandle Proxy = nullptr ;
2020-09-01 14:07:48 -04:00
2021-02-03 14:57:28 -04:00
FChaosEngineInterface : : CreateActor ( Params , Proxy ) ;
auto & Particle = Proxy - > GetGameThreadAPI ( ) ;
EXPECT_NE ( Proxy , nullptr ) ;
2020-09-01 14:07:48 -04:00
{
2021-02-03 14:57:28 -04:00
auto Sphere = MakeUnique < TSphere < FReal , 3 > > ( FVec3 ( 0 ) , 3 ) ;
Particle . SetGeometry ( MoveTemp ( Sphere ) ) ;
2020-09-01 14:07:48 -04:00
}
//create actor before structure is ticked
2021-02-03 14:57:28 -04:00
TArray < FPhysicsActorHandle > Proxys = { Proxy } ;
Scene . AddActorsToScene_AssumesLocked ( Proxys ) ;
2020-09-01 14:07:48 -04:00
2021-02-03 14:57:28 -04:00
//tick solver so that Proxy is created, but don't call EndFrame (want to flush and swap manually)
2020-09-01 14:07:48 -04:00
{
//use 0 dt to make sure pending operations are not sensitive to 0 dt
FVec3 Grav ( 0 , 0 , - 1 ) ;
2021-06-14 15:00:35 -04:00
Scene . SetUpForFrame ( & Grav , 0 , 99999 , 99999 , 10 , false ) ;
2020-09-01 14:07:48 -04:00
Scene . StartFrame ( ) ;
}
//make sure acceleration structure is built
Scene . GetSolver ( ) - > GetEvolution ( ) - > FlushSpatialAcceleration ( ) ;
//delete object to get no hit
2021-02-03 14:57:28 -04:00
FChaosEngineInterface : : ReleaseActor ( Proxy , & Scene ) ;
2020-09-01 14:07:48 -04:00
Scene . CopySolverAccelerationStructure ( ) ; //trigger swap manually and see pending changes apply
{
2021-02-03 14:57:28 -04:00
const auto HitBuffer = InSphereHelper ( Scene , FTransform : : Identity , 3 ) ;
EXPECT_EQ ( HitBuffer . GetNumHits ( ) , 0 ) ;
2020-09-01 14:07:48 -04:00
}
}
2021-02-03 14:57:28 -04:00
GTEST_TEST ( EngineInterface , CreateAndRemoveActorPostFlush )
2020-09-01 14:07:48 -04:00
{
2021-06-14 15:00:35 -04:00
FChaosScene Scene ( nullptr , /*AsyncDt=*/ - 1 ) ;
2020-09-01 14:07:48 -04:00
Scene . GetSolver ( ) - > SetThreadingMode_External ( EThreadingModeTemp : : SingleThread ) ;
FActorCreationParams Params ;
Params . Scene = & Scene ;
2021-02-03 14:57:28 -04:00
FPhysicsActorHandle Proxy = nullptr ;
2020-09-01 14:07:48 -04:00
//tick solver, but don't call EndFrame (want to flush and swap manually)
{
FVec3 Grav ( 0 , 0 , - 1 ) ;
2021-06-14 15:00:35 -04:00
Scene . SetUpForFrame ( & Grav , 1 , 99999 , 99999 , 10 , false ) ;
2020-09-01 14:07:48 -04:00
Scene . StartFrame ( ) ;
}
//make sure acceleration structure is built
Scene . GetSolver ( ) - > GetEvolution ( ) - > FlushSpatialAcceleration ( ) ;
2021-02-03 14:57:28 -04:00
FChaosEngineInterface : : CreateActor ( Params , Proxy ) ;
auto & Particle = Proxy - > GetGameThreadAPI ( ) ;
EXPECT_NE ( Proxy , nullptr ) ;
2020-09-01 14:07:48 -04:00
{
2021-02-03 14:57:28 -04:00
auto Sphere = MakeUnique < TSphere < FReal , 3 > > ( FVec3 ( 0 ) , 3 ) ;
Particle . SetGeometry ( MoveTemp ( Sphere ) ) ;
2020-09-01 14:07:48 -04:00
}
//create actor after flush
2021-02-03 14:57:28 -04:00
TArray < FPhysicsActorHandle > Proxys = { Proxy } ;
Scene . AddActorsToScene_AssumesLocked ( Proxys ) ;
2020-09-01 14:07:48 -04:00
//delete object right away to get no hit
2021-02-03 14:57:28 -04:00
FChaosEngineInterface : : ReleaseActor ( Proxy , & Scene ) ;
2020-09-01 14:07:48 -04:00
Scene . CopySolverAccelerationStructure ( ) ; //trigger swap manually and see pending changes apply
{
2021-02-03 14:57:28 -04:00
const auto HitBuffer = InSphereHelper ( Scene , FTransform : : Identity , 3 ) ;
EXPECT_EQ ( HitBuffer . GetNumHits ( ) , 0 ) ;
2020-09-01 14:07:48 -04:00
}
}
2021-02-03 14:57:28 -04:00
GTEST_TEST ( EngineInterface , CreateDelayed )
2020-09-01 14:07:48 -04:00
{
2021-02-03 14:57:28 -04:00
for ( int Delay = 0 ; Delay < 4 ; + + Delay )
2020-09-01 14:07:48 -04:00
{
2021-06-14 15:00:35 -04:00
FChaosScene Scene ( nullptr , /*AsyncDt=*/ - 1 ) ;
2020-09-01 14:07:48 -04:00
Scene . GetSolver ( ) - > SetThreadingMode_External ( EThreadingModeTemp : : SingleThread ) ;
Scene . GetSolver ( ) - > GetMarshallingManager ( ) . SetTickDelay_External ( Delay ) ;
FActorCreationParams Params ;
Params . Scene = & Scene ;
2021-02-03 14:57:28 -04:00
FPhysicsActorHandle Proxy = nullptr ;
2020-09-01 14:07:48 -04:00
2021-02-03 14:57:28 -04:00
FChaosEngineInterface : : CreateActor ( Params , Proxy ) ;
auto & Particle = Proxy - > GetGameThreadAPI ( ) ;
EXPECT_NE ( Proxy , nullptr ) ;
2020-09-01 14:07:48 -04:00
{
2021-02-03 14:57:28 -04:00
auto Sphere = MakeUnique < TSphere < FReal , 3 > > ( FVec3 ( 0 ) , 3 ) ;
Particle . SetGeometry ( MoveTemp ( Sphere ) ) ;
2020-09-01 14:07:48 -04:00
}
//create actor after flush
2021-02-03 14:57:28 -04:00
TArray < FPhysicsActorHandle > Proxys = { Proxy } ;
Scene . AddActorsToScene_AssumesLocked ( Proxys ) ;
2020-09-01 14:07:48 -04:00
2021-02-03 14:57:28 -04:00
for ( int Repeat = 0 ; Repeat < Delay ; + + Repeat )
2020-09-01 14:07:48 -04:00
{
//tick solver
{
FVec3 Grav ( 0 , 0 , - 1 ) ;
2021-06-14 15:00:35 -04:00
Scene . SetUpForFrame ( & Grav , 1 , 99999 , 99999 , 1 , false ) ;
2020-09-01 14:07:48 -04:00
Scene . StartFrame ( ) ;
Scene . EndFrame ( ) ;
}
//make sure sim hasn't seen it yet
{
FPBDRigidsEvolution * Evolution = Scene . GetSolver ( ) - > GetEvolution ( ) ;
const auto & SOA = Evolution - > GetParticles ( ) ;
2021-02-03 14:57:28 -04:00
EXPECT_EQ ( SOA . GetAllParticlesView ( ) . Num ( ) , 0 ) ;
2020-09-01 14:07:48 -04:00
}
//make sure external thread knows about it
{
2021-02-03 14:57:28 -04:00
const auto HitBuffer = InSphereHelper ( Scene , FTransform : : Identity , 3 ) ;
EXPECT_EQ ( HitBuffer . GetNumHits ( ) , 1 ) ;
2020-09-01 14:07:48 -04:00
}
}
//tick solver one last time
{
FVec3 Grav ( 0 , 0 , - 1 ) ;
2021-06-14 15:00:35 -04:00
Scene . SetUpForFrame ( & Grav , 1 , 99999 , 99999 , 1 , false ) ;
2020-09-01 14:07:48 -04:00
Scene . StartFrame ( ) ;
Scene . EndFrame ( ) ;
}
//now sim knows about it
{
FPBDRigidsEvolution * Evolution = Scene . GetSolver ( ) - > GetEvolution ( ) ;
const auto & SOA = Evolution - > GetParticles ( ) ;
2021-02-03 14:57:28 -04:00
EXPECT_EQ ( SOA . GetAllParticlesView ( ) . Num ( ) , 1 ) ;
2020-09-01 14:07:48 -04:00
}
2021-02-03 14:57:28 -04:00
Particle . SetX ( FVec3 ( 5 , 0 , 0 ) ) ;
2020-09-01 14:07:48 -04:00
2021-02-03 14:57:28 -04:00
for ( int Repeat = 0 ; Repeat < Delay ; + + Repeat )
2020-09-01 14:07:48 -04:00
{
//tick solver
{
FVec3 Grav ( 0 , 0 , - 1 ) ;
2021-06-14 15:00:35 -04:00
Scene . SetUpForFrame ( & Grav , 1 , 99999 , 99999 , 1 , false ) ;
2020-09-01 14:07:48 -04:00
Scene . StartFrame ( ) ;
Scene . EndFrame ( ) ;
}
//make sure sim hasn't seen new X yet
{
FPBDRigidsEvolution * Evolution = Scene . GetSolver ( ) - > GetEvolution ( ) ;
const auto & SOA = Evolution - > GetParticles ( ) ;
2021-02-03 14:57:28 -04:00
const auto & InternalProxy = * SOA . GetAllParticlesView ( ) . Begin ( ) ;
EXPECT_EQ ( InternalProxy . X ( ) [ 0 ] , 0 ) ;
2020-09-01 14:07:48 -04:00
}
}
//tick solver one last time
{
FVec3 Grav ( 0 , 0 , - 1 ) ;
2021-06-14 15:00:35 -04:00
Scene . SetUpForFrame ( & Grav , 1 , 99999 , 99999 , 1 , false ) ;
2020-09-01 14:07:48 -04:00
Scene . StartFrame ( ) ;
Scene . EndFrame ( ) ;
}
//now sim knows about new X
{
FPBDRigidsEvolution * Evolution = Scene . GetSolver ( ) - > GetEvolution ( ) ;
const auto & SOA = Evolution - > GetParticles ( ) ;
2021-02-03 14:57:28 -04:00
const auto & InternalProxy = * SOA . GetAllParticlesView ( ) . Begin ( ) ;
EXPECT_EQ ( InternalProxy . X ( ) [ 0 ] , 5 ) ;
2020-09-01 14:07:48 -04:00
}
//make sure commands are also deferred
int Count = 0 ;
int ExternalCount = 0 ;
2020-12-11 14:21:20 -04:00
TUniqueFunction < void ( ) > Lambda = [ & ] ( )
2020-09-01 14:07:48 -04:00
{
+ + Count ;
2021-02-03 14:57:28 -04:00
EXPECT_EQ ( Count , 1 ) ; //only hit once on internal thread
EXPECT_EQ ( ExternalCount , Delay ) ; //internal hits with expected delay
2020-09-01 14:07:48 -04:00
} ;
Scene . GetSolver ( ) - > EnqueueCommandImmediate ( Lambda ) ;
2021-02-03 14:57:28 -04:00
for ( int Repeat = 0 ; Repeat < Delay + 1 ; + + Repeat )
2020-09-01 14:07:48 -04:00
{
//tick solver
FVec3 Grav ( 0 , 0 , - 1 ) ;
2021-06-14 15:00:35 -04:00
Scene . SetUpForFrame ( & Grav , 1 , 99999 , 99999 , 1 , false ) ;
2020-09-01 14:07:48 -04:00
Scene . StartFrame ( ) ;
Scene . EndFrame ( ) ;
+ + ExternalCount ;
}
}
2021-02-03 14:57:28 -04:00
2020-09-01 14:07:48 -04:00
}
2021-02-03 14:57:28 -04:00
GTEST_TEST ( EngineInterface , RemoveDelayed )
2020-09-01 14:07:48 -04:00
{
2021-02-03 14:57:28 -04:00
for ( int Delay = 0 ; Delay < 4 ; + + Delay )
2020-09-01 14:07:48 -04:00
{
2021-06-14 15:00:35 -04:00
FChaosScene Scene ( nullptr , /*AsyncDt=*/ - 1 ) ;
2020-09-01 14:07:48 -04:00
Scene . GetSolver ( ) - > SetThreadingMode_External ( EThreadingModeTemp : : SingleThread ) ;
Scene . GetSolver ( ) - > GetMarshallingManager ( ) . SetTickDelay_External ( Delay ) ;
FActorCreationParams Params ;
Params . Scene = & Scene ;
Params . bSimulatePhysics = true ; //simulate so that sync body is triggered
Params . bStartAwake = true ;
2021-02-03 14:57:28 -04:00
FPhysicsActorHandle Proxy = nullptr ;
FChaosEngineInterface : : CreateActor ( Params , Proxy ) ;
auto & Particle = Proxy - > GetGameThreadAPI ( ) ;
EXPECT_NE ( Proxy , nullptr ) ;
2020-09-01 14:07:48 -04:00
{
2021-02-03 14:57:28 -04:00
auto Sphere = MakeUnique < TSphere < FReal , 3 > > ( FVec3 ( 0 ) , 3 ) ;
Particle . SetGeometry ( MoveTemp ( Sphere ) ) ;
Particle . SetV ( FVec3 ( 0 , 0 , - 1 ) ) ;
2020-09-01 14:07:48 -04:00
}
2021-02-03 14:57:28 -04:00
//make second simulating Proxy that we don't delete. Needed to trigger a sync
2020-09-01 14:07:48 -04:00
//this is because some data is cleaned up on GT immediately
2021-02-03 14:57:28 -04:00
FPhysicsActorHandle Proxy2 = nullptr ;
FChaosEngineInterface : : CreateActor ( Params , Proxy2 ) ;
auto & Particle2 = Proxy2 - > GetGameThreadAPI ( ) ;
EXPECT_NE ( Proxy2 , nullptr ) ;
2020-09-01 14:07:48 -04:00
{
2021-02-03 14:57:28 -04:00
auto Sphere = MakeUnique < TSphere < FReal , 3 > > ( FVec3 ( 0 ) , 3 ) ;
Particle2 . SetGeometry ( MoveTemp ( Sphere ) ) ;
Particle2 . SetV ( FVec3 ( 0 , - 1 , 0 ) ) ;
2020-09-01 14:07:48 -04:00
}
//create actor
2021-02-03 14:57:28 -04:00
TArray < FPhysicsActorHandle > Proxys = { Proxy , Proxy2 } ;
Scene . AddActorsToScene_AssumesLocked ( Proxys ) ;
2020-09-01 14:07:48 -04:00
//tick until it's being synced from sim
2021-02-03 14:57:28 -04:00
for ( int Repeat = 0 ; Repeat < Delay ; + + Repeat )
2020-09-01 14:07:48 -04:00
{
{
FVec3 Grav ( 0 , 0 , 0 ) ;
2021-06-14 15:00:35 -04:00
Scene . SetUpForFrame ( & Grav , 1 , 99999 , 99999 , 10 , false ) ;
2020-09-01 14:07:48 -04:00
Scene . StartFrame ( ) ;
Scene . EndFrame ( ) ;
}
}
//x starts at 0
2021-02-03 14:57:28 -04:00
EXPECT_NEAR ( Particle . X ( ) [ 2 ] , 0 , 1e-4 ) ;
EXPECT_NEAR ( Particle2 . X ( ) [ 1 ] , 0 , 1e-4 ) ;
2020-09-01 14:07:48 -04:00
//tick solver and see new position synced from sim
{
FVec3 Grav ( 0 , 0 , 0 ) ;
2021-06-14 15:00:35 -04:00
Scene . SetUpForFrame ( & Grav , 1 , 99999 , 99999 , 10 , false ) ;
2020-09-01 14:07:48 -04:00
Scene . StartFrame ( ) ;
Scene . EndFrame ( ) ;
2021-02-03 14:57:28 -04:00
EXPECT_NEAR ( Particle . X ( ) [ 2 ] , - 1 , 1e-4 ) ;
EXPECT_NEAR ( Particle2 . X ( ) [ 1 ] , - 1 , 1e-4 ) ;
2020-09-01 14:07:48 -04:00
}
//tick solver and delete in between solver finishing and sync
{
FVec3 Grav ( 0 , 0 , 0 ) ;
2021-06-14 15:00:35 -04:00
Scene . SetUpForFrame ( & Grav , 1 , 99999 , 99999 , 10 , false ) ;
2020-09-01 14:07:48 -04:00
Scene . StartFrame ( ) ;
2021-02-03 14:57:28 -04:00
//delete Proxy
FChaosEngineInterface : : ReleaseActor ( Proxy , & Scene ) ;
2020-09-01 14:07:48 -04:00
Scene . EndFrame ( ) ;
2021-02-03 14:57:28 -04:00
EXPECT_NEAR ( Particle2 . X ( ) [ 1 ] , - 2 , 1e-4 ) ; //other Proxy keeps moving
2020-09-01 14:07:48 -04:00
}
//tick again and don't crash
2021-02-03 14:57:28 -04:00
for ( int Repeat = 0 ; Repeat < Delay + 1 ; + + Repeat )
2020-09-01 14:07:48 -04:00
{
{
FVec3 Grav ( 0 , 0 , 0 ) ;
2021-06-14 15:00:35 -04:00
Scene . SetUpForFrame ( & Grav , 1 , 99999 , 99999 , 10 , false ) ;
2020-09-01 14:07:48 -04:00
Scene . StartFrame ( ) ;
Scene . EndFrame ( ) ;
2021-02-03 14:57:28 -04:00
EXPECT_NEAR ( Particle2 . X ( ) [ 1 ] , - 3 - Repeat , 1e-4 ) ; //other Proxy keeps moving
2020-09-01 14:07:48 -04:00
}
}
}
}
2021-02-03 14:57:28 -04:00
GTEST_TEST ( EngineInterface , MoveDelayed )
2020-09-24 00:43:27 -04:00
{
2021-02-03 14:57:28 -04:00
for ( int Delay = 0 ; Delay < 4 ; + + Delay )
2020-09-24 00:43:27 -04:00
{
2021-06-14 15:00:35 -04:00
FChaosScene Scene ( nullptr , /*AsyncDt=*/ - 1 ) ;
2020-09-24 00:43:27 -04:00
Scene . GetSolver ( ) - > SetThreadingMode_External ( EThreadingModeTemp : : SingleThread ) ;
Scene . GetSolver ( ) - > GetMarshallingManager ( ) . SetTickDelay_External ( Delay ) ;
FActorCreationParams Params ;
Params . Scene = & Scene ;
Params . bSimulatePhysics = true ; //simulated so that gt conflicts with sim thread
Params . bStartAwake = true ;
2021-02-03 14:57:28 -04:00
FPhysicsActorHandle Proxy = nullptr ;
FChaosEngineInterface : : CreateActor ( Params , Proxy ) ;
auto & Particle = Proxy - > GetGameThreadAPI ( ) ;
EXPECT_NE ( Proxy , nullptr ) ;
2020-09-24 00:43:27 -04:00
{
2021-02-03 14:57:28 -04:00
auto Sphere = MakeUnique < TSphere < FReal , 3 > > ( FVec3 ( 0 ) , 3 ) ;
Particle . SetGeometry ( MoveTemp ( Sphere ) ) ;
Particle . SetV ( FVec3 ( 0 , 0 , - 1 ) ) ;
2020-09-24 00:43:27 -04:00
}
//create actor
2021-02-03 14:57:28 -04:00
TArray < FPhysicsActorHandle > Proxys = { Proxy } ;
Scene . AddActorsToScene_AssumesLocked ( Proxys ) ;
2020-09-24 00:43:27 -04:00
//tick until it's being synced from sim
2021-02-03 14:57:28 -04:00
for ( int Repeat = 0 ; Repeat < Delay ; + + Repeat )
2020-09-24 00:43:27 -04:00
{
{
FVec3 Grav ( 0 , 0 , 0 ) ;
2021-06-14 15:00:35 -04:00
Scene . SetUpForFrame ( & Grav , 1 , 99999 , 99999 , 10 , false ) ;
2020-09-24 00:43:27 -04:00
Scene . StartFrame ( ) ;
Scene . EndFrame ( ) ;
}
}
//x starts at 0
2021-02-03 14:57:28 -04:00
EXPECT_NEAR ( Particle . X ( ) [ 2 ] , 0 , 1e-4 ) ;
2020-09-24 00:43:27 -04:00
//tick solver and see new position synced from sim
{
FVec3 Grav ( 0 , 0 , 0 ) ;
2021-06-14 15:00:35 -04:00
Scene . SetUpForFrame ( & Grav , 1 , 99999 , 99999 , 10 , false ) ;
2020-09-24 00:43:27 -04:00
Scene . StartFrame ( ) ;
Scene . EndFrame ( ) ;
2021-02-03 14:57:28 -04:00
EXPECT_NEAR ( Particle . X ( ) [ 2 ] , - 1 , 1e-4 ) ;
2020-09-24 00:43:27 -04:00
}
//set new x position and make sure we see it right away even though there's delay
2021-02-03 14:57:28 -04:00
FChaosEngineInterface : : SetGlobalPose_AssumesLocked ( Proxy , FTransform ( FQuat : : Identity , FVec3 ( 0 , 0 , 10 ) ) ) ;
2020-09-24 00:43:27 -04:00
2021-02-03 14:57:28 -04:00
for ( int Repeat = 0 ; Repeat < Delay ; + + Repeat )
2020-09-24 00:43:27 -04:00
{
{
FVec3 Grav ( 0 , 0 , 0 ) ;
2021-06-14 15:00:35 -04:00
Scene . SetUpForFrame ( & Grav , 1 , 99999 , 99999 , 10 , false ) ;
2020-09-24 00:43:27 -04:00
Scene . StartFrame ( ) ;
Scene . EndFrame ( ) ;
2021-02-03 14:57:28 -04:00
EXPECT_NEAR ( Particle . X ( ) [ 2 ] , 10 , 1e-4 ) ; //until we catch up, just use GT data
2020-09-24 00:43:27 -04:00
}
}
//tick solver one last time, should see sim results from the place we teleported to
{
FVec3 Grav ( 0 , 0 , 0 ) ;
2021-06-14 15:00:35 -04:00
Scene . SetUpForFrame ( & Grav , 1 , 99999 , 99999 , 10 , false ) ;
2020-09-24 00:43:27 -04:00
Scene . StartFrame ( ) ;
Scene . EndFrame ( ) ;
2021-02-03 14:57:28 -04:00
EXPECT_NEAR ( Particle . X ( ) [ 2 ] , 9 , 1e-4 ) ;
2020-09-24 00:43:27 -04:00
}
//set x after sim but before EndFrame, make sure to see gt position since it was written after
{
FVec3 Grav ( 0 , 0 , 0 ) ;
2021-06-14 15:00:35 -04:00
Scene . SetUpForFrame ( & Grav , 1 , 99999 , 99999 , 10 , false ) ;
2020-09-24 00:43:27 -04:00
Scene . StartFrame ( ) ;
2021-02-03 14:57:28 -04:00
FChaosEngineInterface : : SetGlobalPose_AssumesLocked ( Proxy , FTransform ( FQuat : : Identity , FVec3 ( 0 , 0 , 100 ) ) ) ;
2020-09-24 00:43:27 -04:00
Scene . EndFrame ( ) ;
2021-02-03 14:57:28 -04:00
EXPECT_NEAR ( Particle . X ( ) [ 2 ] , 100 , 1e-4 ) ;
2020-09-24 00:43:27 -04:00
}
2021-02-03 14:57:28 -04:00
for ( int Repeat = 0 ; Repeat < Delay ; + + Repeat )
2020-09-24 00:43:27 -04:00
{
{
FVec3 Grav ( 0 , 0 , 0 ) ;
2021-06-14 15:00:35 -04:00
Scene . SetUpForFrame ( & Grav , 1 , 99999 , 99999 , 10 , false ) ;
2020-09-24 00:43:27 -04:00
Scene . StartFrame ( ) ;
Scene . EndFrame ( ) ;
2021-02-03 14:57:28 -04:00
EXPECT_NEAR ( Particle . X ( ) [ 2 ] , 100 , 1e-4 ) ; //until we catch up, just use GT data
2020-09-24 00:43:27 -04:00
}
}
//tick solver one last time, should see sim results from the place we teleported to
{
FVec3 Grav ( 0 , 0 , 0 ) ;
2021-06-14 15:00:35 -04:00
Scene . SetUpForFrame ( & Grav , 1 , 99999 , 99999 , 10 , false ) ;
2020-09-24 00:43:27 -04:00
Scene . StartFrame ( ) ;
Scene . EndFrame ( ) ;
2021-02-03 14:57:28 -04:00
EXPECT_NEAR ( Particle . X ( ) [ 2 ] , 99 , 1e-4 ) ;
2020-09-24 00:43:27 -04:00
}
}
}
2020-09-01 14:07:48 -04:00
GTEST_TEST ( EngineInterface , SimRoundTrip )
{
2021-06-14 15:00:35 -04:00
FChaosScene Scene ( nullptr , /*AsyncDt=*/ - 1 ) ;
2020-09-01 14:07:48 -04:00
Scene . GetSolver ( ) - > SetThreadingMode_External ( EThreadingModeTemp : : SingleThread ) ;
FActorCreationParams Params ;
Params . Scene = & Scene ;
2021-02-03 14:57:28 -04:00
FPhysicsActorHandle Proxy = nullptr ;
2020-09-01 14:07:48 -04:00
2021-02-03 14:57:28 -04:00
FChaosEngineInterface : : CreateActor ( Params , Proxy ) ;
auto & Particle = Proxy - > GetGameThreadAPI ( ) ;
2020-09-01 14:07:48 -04:00
{
2021-02-03 14:57:28 -04:00
auto Sphere = MakeUnique < TSphere < FReal , 3 > > ( FVec3 ( 0 ) , 3 ) ;
Particle . SetGeometry ( MoveTemp ( Sphere ) ) ;
2020-09-01 14:07:48 -04:00
}
2021-02-03 14:57:28 -04:00
TArray < FPhysicsActorHandle > Proxys = { Proxy } ;
Scene . AddActorsToScene_AssumesLocked ( Proxys ) ;
Particle . SetObjectState ( EObjectStateType : : Dynamic ) ;
Particle . AddForce ( FVec3 ( 0 , 0 , 10 ) * Particle . M ( ) ) ;
2020-09-01 14:07:48 -04:00
FVec3 Grav ( 0 , 0 , 0 ) ;
2021-06-14 15:00:35 -04:00
Scene . SetUpForFrame ( & Grav , 1 , 99999 , 99999 , 10 , false ) ;
2020-09-01 14:07:48 -04:00
Scene . StartFrame ( ) ;
Scene . EndFrame ( ) ;
//integration happened and we get results back
2021-02-03 14:57:28 -04:00
EXPECT_EQ ( Particle . X ( ) , FVec3 ( 0 , 0 , 10 ) ) ;
EXPECT_EQ ( Particle . V ( ) , FVec3 ( 0 , 0 , 10 ) ) ;
2020-09-01 14:07:48 -04:00
}
2020-10-29 13:38:15 -04:00
GTEST_TEST ( EngineInterface , SimInterpolated )
{
2020-11-24 18:42:39 -04:00
//Need to test:
//position interpolation
2021-02-03 14:57:28 -04:00
//position interpolation from an inactive Proxy (i.e a step function)
//position interpolation from an active to an inactive Proxy (i.e a step function but reversed)
//interpolation to a deleted Proxy
2020-11-24 18:42:39 -04:00
//state change should be a step function (sleep state)
//wake events must be collapsed (sleep awake sleep becomes sleep)
//collision events must be collapsed
//forces are averaged
2021-02-03 14:57:28 -04:00
const FReal FixedDT = 1 ;
2021-06-14 15:00:35 -04:00
FChaosScene Scene ( nullptr , FixedDT ) ;
Scene . GetSolver ( ) - > SetThreadingMode_External ( EThreadingModeTemp : : SingleThread ) ;
2020-11-24 18:42:39 -04:00
FActorCreationParams Params ;
Params . Scene = & Scene ;
2021-02-03 14:57:28 -04:00
FPhysicsActorHandle Proxy = nullptr ;
FPhysicsActorHandle Proxy2 = nullptr ;
2020-11-24 18:42:39 -04:00
2021-02-03 14:57:28 -04:00
FChaosEngineInterface : : CreateActor ( Params , Proxy ) ;
auto & Particle = Proxy - > GetGameThreadAPI ( ) ;
2020-11-24 18:42:39 -04:00
{
auto Sphere = MakeUnique < TSphere < FReal , 3 > > ( FVec3 ( 0 ) , 3 ) ;
2021-02-03 14:57:28 -04:00
Particle . SetGeometry ( MoveTemp ( Sphere ) ) ;
2020-11-24 18:42:39 -04:00
}
2021-04-21 09:39:56 -04:00
Params . bSimulatePhysics = true ;
2021-02-03 14:57:28 -04:00
FChaosEngineInterface : : CreateActor ( Params , Proxy2 ) ;
auto & Particle2 = Proxy2 - > GetGameThreadAPI ( ) ;
2020-11-24 18:42:39 -04:00
{
auto Sphere = MakeUnique < TSphere < FReal , 3 > > ( FVec3 ( 0 ) , 3 ) ;
2021-02-03 14:57:28 -04:00
Particle2 . SetGeometry ( MoveTemp ( Sphere ) ) ;
2020-11-24 18:42:39 -04:00
}
2021-02-03 14:57:28 -04:00
TArray < FPhysicsActorHandle > Proxys = { Proxy , Proxy2 } ;
Scene . AddActorsToScene_AssumesLocked ( Proxys ) ;
Particle . SetObjectState ( EObjectStateType : : Dynamic ) ;
const FReal ZVel = 10 ;
const FReal ZStart = 100 ;
const FVec3 ConstantForce ( 0 , 0 , 1 * Particle2 . M ( ) ) ;
Particle . SetV ( FVec3 ( 0 , 0 , ZVel ) ) ;
Particle . SetX ( FVec3 ( 0 , 0 , ZStart ) ) ;
2020-11-24 18:42:39 -04:00
const int32 NumGTSteps = 24 ;
const int32 NumPTSteps = 24 / 4 ;
struct FCallback : public TSimCallbackObject < FSimCallbackNoInput >
{
2020-12-11 14:21:20 -04:00
virtual void OnPreSimulate_Internal ( ) override
2020-11-24 18:42:39 -04:00
{
2020-12-11 14:21:20 -04:00
EXPECT_EQ ( GetConsumerInput_Internal ( ) , nullptr ) ; //no inputs passed in
2020-11-24 18:42:39 -04:00
//we expect the dt to be 1
2020-12-11 14:21:20 -04:00
EXPECT_EQ ( GetDeltaTime_Internal ( ) , 1 ) ;
EXPECT_EQ ( GetSimTime_Internal ( ) , Count ) ;
2020-11-24 18:42:39 -04:00
Count + + ;
}
int32 Count = 0 ;
int32 NumPTSteps ;
} ;
auto Callback = Scene . GetSolver ( ) - > CreateAndRegisterSimCallbackObject_External < FCallback > ( ) ;
Callback - > NumPTSteps = NumPTSteps ;
2021-02-03 14:57:28 -04:00
FReal Time = 0 ;
const FReal GTDt = FixedDT * 0.25f ;
2020-12-11 14:21:20 -04:00
for ( int32 Step = 0 ; Step < NumGTSteps ; Step + + )
2020-11-24 18:42:39 -04:00
{
//set force every external frame
2021-02-03 14:57:28 -04:00
Particle2 . AddForce ( ConstantForce ) ;
2020-11-24 18:42:39 -04:00
FVec3 Grav ( 0 , 0 , 0 ) ;
2021-06-14 15:00:35 -04:00
Scene . SetUpForFrame ( & Grav , GTDt , 99999 , 99999 , 1 , false ) ;
2020-11-24 18:42:39 -04:00
Scene . StartFrame ( ) ;
Scene . EndFrame ( ) ;
2020-12-11 14:21:20 -04:00
2020-11-24 18:42:39 -04:00
Time + = GTDt ;
2021-02-03 14:57:28 -04:00
const FReal InterpolatedTime = Time - FixedDT * Chaos : : AsyncInterpolationMultiplier ;
const FReal ExpectedVFromForce = Time ;
2020-12-11 14:21:20 -04:00
if ( InterpolatedTime < 0 )
2020-11-24 18:42:39 -04:00
{
//not enough time to interpolate so just take initial value
2021-02-03 14:57:28 -04:00
EXPECT_NEAR ( Particle . X ( ) [ 2 ] , ZStart , 1e-2 ) ;
EXPECT_NEAR ( Particle2 . V ( ) [ 2 ] , 0 , 1e-2 ) ;
2020-11-24 18:42:39 -04:00
}
else
{
//interpolated
2021-02-03 14:57:28 -04:00
EXPECT_NEAR ( Particle . X ( ) [ 2 ] , ZStart + ZVel * InterpolatedTime , 1e-2 ) ;
EXPECT_NEAR ( Particle2 . V ( ) [ 2 ] , InterpolatedTime , 1e-2 ) ;
2020-11-24 18:42:39 -04:00
}
}
EXPECT_EQ ( Callback - > Count , NumPTSteps ) ;
2021-02-03 14:57:28 -04:00
const FReal LastInterpolatedTime = NumGTSteps * GTDt - FixedDT * Chaos : : AsyncInterpolationMultiplier ;
EXPECT_NEAR ( Particle . X ( ) [ 2 ] , ZStart + ZVel * LastInterpolatedTime , 1e-2 ) ;
EXPECT_NEAR ( Particle . V ( ) [ 2 ] , ZVel , 1e-2 ) ;
2020-11-24 18:42:39 -04:00
}
2021-05-27 13:40:37 -04:00
GTEST_TEST ( EngineInterface , SetKinematicTarget )
{
// Need to test:
// GT particle position is immediately updated after calling SetKinematicTarget_AssumesLocked
// GT particle positions and velocities are correctly updated
// PT particle positions and velocities are correctly updated
// Velocity becomes zero if no KinematicTarget is set in the current frame
// Particle positions and velocities are correct after SetKinematicTarget_AssumesLocked, SetKinematicTarget_AssumesLocked
// Velocity is zero if only SetGlobalPose_AssumesLocked is called (Teleport)
// Particle positions and velocities are correct after SetGlobalPose_AssumesLocked, SetKinematicTarget_AssumesLocked (Teleport)
// Particle positions and velocities are correct after SetKinematicTarget_AssumesLocked, SetGlobalPose_AssumesLocked (Teleport, KinematicTarget is cleared)
2021-06-14 15:00:35 -04:00
FChaosScene Scene ( nullptr , /*AsyncDt=*/ - 1 ) ;
2021-05-27 13:40:37 -04:00
Scene . GetSolver ( ) - > SetThreadingMode_External ( EThreadingModeTemp : : SingleThread ) ;
FActorCreationParams Params ;
Params . Scene = & Scene ;
FPhysicsActorHandle Proxy = nullptr ;
FChaosEngineInterface : : CreateActor ( Params , Proxy ) ;
auto & Particle = Proxy - > GetGameThreadAPI ( ) ;
{
auto Sphere = MakeUnique < TSphere < FReal , 3 > > ( FVec3 ( 0 ) , 3 ) ;
Particle . SetGeometry ( MoveTemp ( Sphere ) ) ;
}
TArray < FPhysicsActorHandle > Proxys = { Proxy } ;
Scene . AddActorsToScene_AssumesLocked ( Proxys ) ;
Particle . SetObjectState ( EObjectStateType : : Kinematic ) ;
struct FDummyInput : FSimCallbackInput
{
FSingleParticlePhysicsProxy * Proxy ;
FVec3 CorrectX ;
FVec3 CorrectV ;
void Reset ( ) { }
} ;
struct FCallback : public TSimCallbackObject < FDummyInput >
{
virtual void OnPreSimulate_Internal ( ) override
{
auto Handle = GetConsumerInput_Internal ( ) - > Proxy - > GetPhysicsThreadAPI ( ) ;
EXPECT_EQ ( Handle - > X ( ) , GetConsumerInput_Internal ( ) - > CorrectX ) ;
EXPECT_EQ ( Handle - > V ( ) , GetConsumerInput_Internal ( ) - > CorrectV ) ;
}
} ;
auto Callback = Scene . GetSolver ( ) - > CreateAndRegisterSimCallbackObject_External < FCallback > ( ) ;
Callback - > GetProducerInputData_External ( ) - > Proxy = Proxy ;
FVec3 Grav ( 0 , 0 , 0 ) ;
float Dt = 1 ;
auto AdvanceFrameAndRunTest = [ & ] ( const FVec3 & CorrectX , const FVec3 & CorrectV )
{
2021-06-14 15:00:35 -04:00
Scene . SetUpForFrame ( & Grav , Dt , 99999 , 99999 , 10 , false ) ;
2021-05-27 13:40:37 -04:00
Scene . StartFrame ( ) ;
Scene . EndFrame ( ) ;
// Test X and V on GT
EXPECT_EQ ( Particle . X ( ) , CorrectX ) ;
EXPECT_EQ ( Particle . V ( ) , CorrectV ) ;
// Test X and V on PT, this is going to be used in OnPreSimulate_Internal in next frame.
Callback - > GetProducerInputData_External ( ) - > CorrectX = CorrectX ;
Callback - > GetProducerInputData_External ( ) - > CorrectV = CorrectV ;
} ;
// Set initial transform
FVec3 CurrentX = FVec3 ( 1 , 2 , 3 ) ;
FVec3 CurrentV = FVec3 ( 0 , 0 , 0 ) ;
FChaosEngineInterface : : SetGlobalPose_AssumesLocked ( Proxy , FTransform ( CurrentX ) ) ;
Callback - > GetProducerInputData_External ( ) - > CorrectX = CurrentX ;
Callback - > GetProducerInputData_External ( ) - > CorrectV = CurrentV ;
AdvanceFrameAndRunTest ( CurrentX , CurrentV ) ;
// Test SetKinematicTarget_AssumesLocked
CurrentX = FVec3 ( 2 , 3 , 4 ) ;
CurrentV = FVec3 ( 1 , 1 , 1 ) ;
FChaosEngineInterface : : SetKinematicTarget_AssumesLocked ( Proxy , FTransform ( CurrentX ) ) ;
// Test if position is immediately updated on GT after SetKinematicTarget_AssumesLocked
EXPECT_EQ ( Particle . X ( ) , CurrentX ) ;
AdvanceFrameAndRunTest ( CurrentX , CurrentV ) ;
// Test if velocity becomes zero when no kinematic target is set
CurrentX = FVec3 ( 2 , 3 , 4 ) ;
CurrentV = FVec3 ( 0 , 0 , 0 ) ;
AdvanceFrameAndRunTest ( CurrentX , CurrentV ) ;
// Test if particle positions and velocities are correct after SetKinematicTarget_AssumesLocked, SetKinematicTarget_AssumesLocked
CurrentX = FVec3 ( 0 , 0 , 0 ) ;
CurrentV = FVec3 ( - 2 , - 3 , - 4 ) ;
FChaosEngineInterface : : SetKinematicTarget_AssumesLocked ( Proxy , FTransform ( FVec3 ( 1 , 2 , 3 ) ) ) ;
FChaosEngineInterface : : SetKinematicTarget_AssumesLocked ( Proxy , FTransform ( CurrentX ) ) ;
AdvanceFrameAndRunTest ( CurrentX , CurrentV ) ;
// Test if velocity is zero if only SetGlobalPose_AssumesLocked is called (Teleport)
CurrentX = FVec3 ( 0 , 0 , 0 ) ;
CurrentV = FVec3 ( 0 , 0 , 0 ) ;
FChaosEngineInterface : : SetGlobalPose_AssumesLocked ( Proxy , FTransform ( CurrentX ) ) ;
Callback - > GetProducerInputData_External ( ) - > CorrectX = CurrentX ;
AdvanceFrameAndRunTest ( CurrentX , CurrentV ) ;
// Test if particle positions and velocities are correct after SetGlobalPose_AssumesLocked, SetKinematicTarget_AssumesLocked
CurrentX = FVec3 ( - 1 , - 2 , - 3 ) ;
CurrentV = FVec3 ( 0 , 0 , 0 ) ;
FChaosEngineInterface : : SetGlobalPose_AssumesLocked ( Proxy , FTransform ( CurrentX ) ) ;
FChaosEngineInterface : : SetKinematicTarget_AssumesLocked ( Proxy , FTransform ( CurrentX ) ) ;
Callback - > GetProducerInputData_External ( ) - > CorrectX = CurrentX ;
AdvanceFrameAndRunTest ( CurrentX , CurrentV ) ;
// Test if particle positions and velocities are correct after SetKinematicTarget_AssumesLocked, SetGlobalPose_AssumesLocked
CurrentX = FVec3 ( 3 , 2 , 1 ) ;
CurrentV = FVec3 ( 0 , 0 , 0 ) ;
FChaosEngineInterface : : SetKinematicTarget_AssumesLocked ( Proxy , FTransform ( CurrentX ) ) ;
FChaosEngineInterface : : SetGlobalPose_AssumesLocked ( Proxy , FTransform ( CurrentX ) ) ;
Callback - > GetProducerInputData_External ( ) - > CorrectX = CurrentX ;
AdvanceFrameAndRunTest ( CurrentX , CurrentV ) ;
// Test if the PT positions and velocities are right from previous frame
CurrentX = FVec3 ( 3 , 2 , 1 ) ;
CurrentV = FVec3 ( 0 , 0 , 0 ) ;
AdvanceFrameAndRunTest ( CurrentX , CurrentV ) ;
}
2021-03-05 19:27:14 -04:00
GTEST_TEST ( EngineInterface , PerPropertySetOnGT )
{
//Need to test:
//setting transform, velocities, wake state, on external thread means we overwrite results until sim catches up
//deleted proxy does not incorrectly update after it's deleted on gt
const FReal FixedDT = 1 ;
2021-06-14 15:00:35 -04:00
FChaosScene Scene ( nullptr , FixedDT ) ;
Scene . GetSolver ( ) - > SetThreadingMode_External ( EThreadingModeTemp : : SingleThread ) ;
2021-03-05 19:27:14 -04:00
Scene . GetSolver ( ) - > EnableAsyncMode ( 1 ) ; //tick 1 dt at a time
FActorCreationParams Params ;
Params . Scene = & Scene ;
FPhysicsActorHandle Proxy = nullptr ;
FChaosEngineInterface : : CreateActor ( Params , Proxy ) ;
auto & Particle = Proxy - > GetGameThreadAPI ( ) ;
{
auto Sphere = MakeUnique < TSphere < FReal , 3 > > ( FVec3 ( 0 ) , 3 ) ;
Particle . SetGeometry ( MoveTemp ( Sphere ) ) ;
}
TArray < FPhysicsActorHandle > Proxys = { Proxy } ;
Scene . AddActorsToScene_AssumesLocked ( Proxys ) ;
Particle . SetObjectState ( EObjectStateType : : Dynamic ) ;
const FReal ZVel = 10 ;
const FReal ZStart = 100 ;
Particle . SetV ( FVec3 ( 0 , 0 , ZVel ) ) ;
Particle . SetX ( FVec3 ( 0 , 0 , ZStart ) ) ;
const int32 NumGTSteps = 100 ;
const FVec3 TeleportLocation ( 5 , 5 , ZStart ) ;
FReal Time = 0 ;
const FReal GTDt = FixedDT * 0.5f ;
const int32 ChangeVelStep = 20 ;
const FReal ChangeVelTime = ChangeVelStep * GTDt ;
const FReal YVelAfterChange = 10 ;
const int32 TeleportStep = 10 ;
const FReal TeleportTime = TeleportStep * GTDt ;
bool bHasTeleportedOnGT = false ;
bool bVelHasChanged = false ;
bool bWasPutToSleep = false ;
bool bWasWoken = false ;
const int32 SleepStep = 50 ;
const int32 WakeStep = 70 ;
const FReal PutToSleepTime = SleepStep * GTDt ;
const FReal WokenTime = WakeStep * GTDt ;
FReal SleepZPosition ( 0 ) ;
for ( int32 Step = 0 ; Step < NumGTSteps ; Step + + )
{
if ( Step = = TeleportStep )
{
Particle . SetX ( TeleportLocation ) ;
bHasTeleportedOnGT = true ;
}
if ( Step = = ChangeVelStep )
{
Particle . SetV ( FVec3 ( 0 , YVelAfterChange , ZVel ) ) ;
bVelHasChanged = true ;
}
if ( Step = = SleepStep )
{
bWasPutToSleep = true ;
Particle . SetObjectState ( EObjectStateType : : Sleeping ) ;
SleepZPosition = Particle . X ( ) [ 2 ] ; //record position when gt wants to sleep
}
if ( Step = = WakeStep )
{
bWasWoken = true ;
Particle . SetV ( FVec3 ( 0 , YVelAfterChange , ZVel ) ) ;
Particle . SetObjectState ( EObjectStateType : : Dynamic ) ;
}
FVec3 Grav ( 0 , 0 , 0 ) ;
2021-06-14 15:00:35 -04:00
Scene . SetUpForFrame ( & Grav , GTDt , 99999 , 99999 , 10 , false ) ;
2021-03-05 19:27:14 -04:00
Scene . StartFrame ( ) ;
Scene . EndFrame ( ) ;
Time + = GTDt ;
const FReal InterpolatedTime = Time - FixedDT * Chaos : : AsyncInterpolationMultiplier ;
if ( InterpolatedTime < 0 )
{
//not enough time to interpolate so just take initial value
EXPECT_NEAR ( Particle . X ( ) [ 2 ] , ZStart , 1e-2 ) ;
}
else
{
//interpolated
if ( bHasTeleportedOnGT )
{
EXPECT_NEAR ( Particle . X ( ) [ 0 ] , TeleportLocation [ 0 ] , 1e-2 ) ; //X never changes so as soon as gt teleports we should see it
//if we haven't caught up to teleport, we just use the value set on GT for z value
if ( InterpolatedTime < TeleportTime )
{
EXPECT_NEAR ( Particle . X ( ) [ 2 ] , TeleportLocation [ 2 ] , 1e-3 ) ;
}
else
{
if ( ! bWasPutToSleep )
{
//caught up so expect normal movement to marshal back
EXPECT_NEAR ( Particle . X ( ) [ 2 ] , TeleportLocation [ 2 ] + ZVel * ( InterpolatedTime - TeleportTime ) , 1e-2 ) ;
}
else if ( InterpolatedTime < WokenTime )
{
//currently asleep so position is held constant
EXPECT_NEAR ( Particle . X ( ) [ 2 ] , SleepZPosition , 1e-2 ) ;
if ( ! bWasWoken )
{
EXPECT_NEAR ( Particle . V ( ) [ 2 ] , 0 , 1e-2 ) ;
}
else
{
EXPECT_NEAR ( Particle . V ( ) [ 2 ] , ZVel , 1e-2 ) ;
}
}
else
{
//woke back up so position is moving again
EXPECT_NEAR ( Particle . X ( ) [ 2 ] , SleepZPosition + ZVel * ( InterpolatedTime - WokenTime ) , 1e-2 ) ;
EXPECT_NEAR ( Particle . V ( ) [ 2 ] , ZVel , 1e-2 ) ;
}
}
}
else
{
EXPECT_NEAR ( Particle . X ( ) [ 2 ] , ZStart + ZVel * InterpolatedTime , 1e-2 ) ;
}
if ( bVelHasChanged )
{
if ( ! bWasPutToSleep | | bWasWoken )
{
EXPECT_EQ ( Particle . V ( ) [ 1 ] , YVelAfterChange ) ;
}
else
{
//asleep so velocity is 0
EXPECT_EQ ( Particle . V ( ) [ 1 ] , 0 ) ;
}
}
else
{
EXPECT_EQ ( Particle . V ( ) [ 1 ] , 0 ) ;
}
if ( bWasPutToSleep & & ! bWasWoken )
{
EXPECT_EQ ( Particle . ObjectState ( ) , EObjectStateType : : Sleeping ) ;
}
else
{
EXPECT_EQ ( Particle . ObjectState ( ) , EObjectStateType : : Dynamic ) ;
}
}
}
const FReal LastInterpolatedTime = NumGTSteps * GTDt - FixedDT * Chaos : : AsyncInterpolationMultiplier ;
EXPECT_EQ ( Particle . V ( ) [ 2 ] , ZVel ) ;
EXPECT_EQ ( Particle . V ( ) [ 1 ] , YVelAfterChange ) ;
}
2020-12-11 14:21:20 -04:00
GTEST_TEST ( EngineInterface , FlushCommand )
{
//Need to test:
//flushing commands works and sees state changes for both fixed dt and not
//sim callback is not called
bool bHitOnShutDown = false ;
{
2021-06-14 15:00:35 -04:00
FChaosScene Scene ( nullptr , /*AsyncDt=*/ - 1 ) ;
2020-12-11 14:21:20 -04:00
Scene . GetSolver ( ) - > SetThreadingMode_External ( EThreadingModeTemp : : SingleThread ) ;
Scene . GetSolver ( ) - > EnableAsyncMode ( 1 ) ; //tick 1 dt at a time
FActorCreationParams Params ;
Params . Scene = & Scene ;
2021-02-03 14:57:28 -04:00
FPhysicsActorHandle Proxy = nullptr ;
2020-12-11 14:21:20 -04:00
2021-02-03 14:57:28 -04:00
FChaosEngineInterface : : CreateActor ( Params , Proxy ) ;
auto & Particle = Proxy - > GetGameThreadAPI ( ) ;
2020-12-11 14:21:20 -04:00
{
auto Sphere = MakeUnique < TSphere < FReal , 3 > > ( FVec3 ( 0 ) , 3 ) ;
2021-02-03 14:57:28 -04:00
Particle . SetGeometry ( MoveTemp ( Sphere ) ) ;
2020-12-11 14:21:20 -04:00
}
2021-02-03 14:57:28 -04:00
TArray < FPhysicsActorHandle > Proxys = { Proxy } ;
Scene . AddActorsToScene_AssumesLocked ( Proxys ) ;
Particle . SetX ( FVec3 ( 0 , 0 , 3 ) ) ;
2020-12-11 14:21:20 -04:00
Scene . GetSolver ( ) - > EnqueueCommandImmediate ( [ Proxy ] ( )
2021-04-29 19:32:06 -04:00
{
//sees change immediately
2021-02-03 14:57:28 -04:00
EXPECT_EQ ( Proxy - > GetPhysicsThreadAPI ( ) - > X ( ) [ 2 ] , 3 ) ;
2021-04-29 19:32:06 -04:00
} ) ;
2020-12-11 14:21:20 -04:00
struct FCallback : public TSimCallbackObject < >
{
virtual void OnPreSimulate_Internal ( ) override
{
EXPECT_FALSE ( true ) ; //this should never hit
}
} ;
auto Callback = Scene . GetSolver ( ) - > CreateAndRegisterSimCallbackObject_External < FCallback > ( ) ;
FVec3 Grav ( 0 , 0 , 0 ) ;
2021-06-14 15:00:35 -04:00
Scene . SetUpForFrame ( & Grav , 0 , 99999 , 99999 , 10 , false ) ; //flush with dt 0
2020-12-11 14:21:20 -04:00
Scene . StartFrame ( ) ;
Scene . EndFrame ( ) ;
Scene . GetSolver ( ) - > EnqueueCommandImmediate ( [ & bHitOnShutDown ] ( )
2021-04-29 19:32:06 -04:00
{
2020-12-11 14:21:20 -04:00
//command enqueued and then solver shuts down, so flush must happen
2021-04-29 19:32:06 -04:00
bHitOnShutDown = true ;
} ) ;
2020-12-11 14:21:20 -04:00
}
2021-02-03 14:57:28 -04:00
2020-12-11 14:21:20 -04:00
EXPECT_TRUE ( bHitOnShutDown ) ;
}
2020-11-24 18:42:39 -04:00
GTEST_TEST ( EngineInterface , SimSubstep )
{
//Need to test:
//forces and torques are extrapolated (i.e. held constant for sub-steps)
//kinematic targets are interpolated over the sub-step
//identical inputs are given to sub-steps
2021-02-03 14:57:28 -04:00
const FReal FixedDT = 1 ;
2021-06-14 15:00:35 -04:00
FChaosScene Scene ( nullptr , FixedDT ) ;
Scene . GetSolver ( ) - > SetThreadingMode_External ( EThreadingModeTemp : : SingleThread ) ;
2020-10-29 13:38:15 -04:00
FActorCreationParams Params ;
Params . Scene = & Scene ;
2021-02-03 14:57:28 -04:00
FPhysicsActorHandle Proxy = nullptr ;
2020-10-29 13:38:15 -04:00
2021-02-03 14:57:28 -04:00
FChaosEngineInterface : : CreateActor ( Params , Proxy ) ;
auto & Particle = Proxy - > GetGameThreadAPI ( ) ;
2020-10-29 13:38:15 -04:00
{
auto Sphere = MakeUnique < TSphere < FReal , 3 > > ( FVec3 ( 0 ) , 3 ) ;
2021-02-03 14:57:28 -04:00
Particle . SetGeometry ( MoveTemp ( Sphere ) ) ;
2020-10-29 13:38:15 -04:00
}
2020-11-24 18:42:39 -04:00
2021-02-03 14:57:28 -04:00
TArray < FPhysicsActorHandle > Proxys = { Proxy } ;
Scene . AddActorsToScene_AssumesLocked ( Proxys ) ;
Particle . SetObjectState ( EObjectStateType : : Dynamic ) ;
Particle . SetGravityEnabled ( true ) ;
2020-10-29 13:38:15 -04:00
2020-11-24 18:42:39 -04:00
struct FDummyInput : FSimCallbackInput
2020-10-29 13:38:15 -04:00
{
2020-11-24 18:42:39 -04:00
int32 ExternalFrame ;
2021-02-03 14:57:28 -04:00
void Reset ( ) { }
2020-11-24 18:42:39 -04:00
} ;
struct FCallback : public TSimCallbackObject < FDummyInput >
{
2020-12-11 14:21:20 -04:00
virtual void OnPreSimulate_Internal ( ) override
2020-10-29 13:38:15 -04:00
{
2020-12-11 14:21:20 -04:00
EXPECT_EQ ( GetConsumerInput_Internal ( ) - > ExternalFrame , ExpectedFrame ) ;
EXPECT_NEAR ( GetSimTime_Internal ( ) , InternalSteps * GetDeltaTime_Internal ( ) , 1e-2 ) ; //sim start is changing per sub-step
2020-11-24 18:42:39 -04:00
+ + InternalSteps ;
2020-10-29 13:38:15 -04:00
}
2020-11-24 18:42:39 -04:00
int32 ExpectedFrame ;
int32 InternalSteps = 0 ;
2020-10-29 13:38:15 -04:00
} ;
auto Callback = Scene . GetSolver ( ) - > CreateAndRegisterSimCallbackObject_External < FCallback > ( ) ;
2021-02-03 14:57:28 -04:00
FReal Time = 0 ;
const FReal GTDt = FixedDT * 4 ;
2020-11-24 18:42:39 -04:00
for ( int32 Step = 0 ; Step < 10 ; Step + + )
2020-10-29 13:38:15 -04:00
{
2020-11-24 18:42:39 -04:00
Callback - > ExpectedFrame = Step ;
Callback - > GetProducerInputData_External ( ) - > ExternalFrame = Step ; //make sure input matches for all sub-steps
//set force every external frame
2021-02-03 14:57:28 -04:00
Particle . AddForce ( FVec3 ( 0 , 0 , 1 * Particle . M ( ) ) ) ; //should counteract gravity
2020-11-24 18:42:39 -04:00
FVec3 Grav ( 0 , 0 , - 1 ) ;
2021-06-14 15:00:35 -04:00
Scene . SetUpForFrame ( & Grav , GTDt , 99999 , 99999 , 10 , false ) ;
2020-10-29 13:38:15 -04:00
Scene . StartFrame ( ) ;
Scene . EndFrame ( ) ;
2020-11-24 18:42:39 -04:00
Time + = GTDt ;
//should have no movement because forces cancel out
2021-02-03 14:57:28 -04:00
EXPECT_NEAR ( Particle . X ( ) [ 2 ] , 0 , 1e-2 ) ;
EXPECT_NEAR ( Particle . V ( ) [ 2 ] , 0 , 1e-2 ) ;
2020-11-24 18:42:39 -04:00
}
2020-10-29 13:38:15 -04:00
}
2020-12-11 14:21:20 -04:00
GTEST_TEST ( EngineInterface , SimDestroyedProxy )
{
//Need to test:
2021-02-03 14:57:28 -04:00
//destroyed proxy still valid in callback, but Proxy is nulled out
2020-12-11 14:21:20 -04:00
//valid for multiple sub-steps
2021-06-14 15:00:35 -04:00
FChaosScene Scene ( nullptr , /*AsyncDt=*/ - 1 ) ;
2020-12-11 14:21:20 -04:00
Scene . GetSolver ( ) - > SetThreadingMode_External ( EThreadingModeTemp : : SingleThread ) ;
2021-02-03 14:57:28 -04:00
const FReal FixedDT = 1 ;
2020-12-11 14:21:20 -04:00
Scene . GetSolver ( ) - > EnableAsyncMode ( FixedDT ) ; //tick 1 dt at a time
FActorCreationParams Params ;
Params . Scene = & Scene ;
2021-02-03 14:57:28 -04:00
FPhysicsActorHandle Proxy = nullptr ;
2020-12-11 14:21:20 -04:00
2021-02-03 14:57:28 -04:00
FChaosEngineInterface : : CreateActor ( Params , Proxy ) ;
auto & Particle = Proxy - > GetGameThreadAPI ( ) ;
2020-12-11 14:21:20 -04:00
{
auto Sphere = MakeUnique < TSphere < FReal , 3 > > ( FVec3 ( 0 ) , 3 ) ;
2021-02-03 14:57:28 -04:00
Particle . SetGeometry ( MoveTemp ( Sphere ) ) ;
2020-12-11 14:21:20 -04:00
}
2021-02-03 14:57:28 -04:00
TArray < FPhysicsActorHandle > Proxys = { Proxy } ;
Scene . AddActorsToScene_AssumesLocked ( Proxys ) ;
2020-12-11 14:21:20 -04:00
struct FDummyInput : FSimCallbackInput
{
2021-02-03 14:57:28 -04:00
FSingleParticlePhysicsProxy * Proxy ;
2020-12-11 14:21:20 -04:00
void Reset ( ) { }
} ;
struct FCallback : public TSimCallbackObject < FDummyInput >
{
virtual void OnPreSimulate_Internal ( ) override
{
2021-02-03 14:57:28 -04:00
EXPECT_EQ ( GetConsumerInput_Internal ( ) - > Proxy - > GetHandle_LowLevel ( ) , nullptr ) ;
2020-12-11 14:21:20 -04:00
}
} ;
auto Callback = Scene . GetSolver ( ) - > CreateAndRegisterSimCallbackObject_External < FCallback > ( ) ;
2021-02-03 14:57:28 -04:00
Callback - > GetProducerInputData_External ( ) - > Proxy = Proxy ;
Scene . GetSolver ( ) - > UnregisterObject ( Proxy ) ;
2020-12-11 14:21:20 -04:00
FVec3 Grav ( 0 , 0 , - 1 ) ;
2021-06-14 15:00:35 -04:00
Scene . SetUpForFrame ( & Grav , FixedDT * 3 , 99999 , 99999 , 10 , false ) ;
2020-12-11 14:21:20 -04:00
Scene . StartFrame ( ) ;
Scene . EndFrame ( ) ;
}
2021-05-27 13:40:37 -04:00
GTEST_TEST ( EngineInterface , OverlapOffsetActor )
{
2021-06-14 15:00:35 -04:00
FChaosScene Scene ( nullptr , /*AsyncDt=*/ - 1 ) ;
2021-05-27 13:40:37 -04:00
FActorCreationParams Params ;
Params . Scene = & Scene ;
Params . bSimulatePhysics = false ;
Params . bStatic = true ;
Params . InitialTM = FTransform : : Identity ;
Params . Scene = & Scene ;
2021-05-27 15:47:15 -04:00
FPhysicsActorHandle StaticCube = nullptr ;
2021-05-27 13:40:37 -04:00
FChaosEngineInterface : : CreateActor ( Params , StaticCube ) ;
ASSERT_NE ( StaticCube , nullptr ) ;
// Add geometry, placing a box at the origin
constexpr FReal BoxSize = static_cast < FReal > ( 50.0 ) ;
const FVec3 HalfBoxExtent { BoxSize } ;
// We require a union here, although the second geometry isn't used we need the particle to
// have more than one shape in its shapes array otherwise the query acceleration will treat
// it as a special case and skip bounds checking during the overlap
TArray < TUniquePtr < FImplicitObject > > Geoms ;
Geoms . Emplace ( MakeUnique < TBox < FReal , 3 > > ( - HalfBoxExtent , HalfBoxExtent ) ) ;
Geoms . Emplace ( MakeUnique < TBox < FReal , 3 > > ( - HalfBoxExtent , HalfBoxExtent ) ) ;
2021-05-27 15:47:15 -04:00
auto & Particle = StaticCube - > GetGameThreadAPI ( ) ;
{
TUniquePtr < FImplicitObjectUnion > GeomUnion = MakeUnique < FImplicitObjectUnion > ( MoveTemp ( Geoms ) ) ;
Particle . SetGeometry ( MoveTemp ( GeomUnion ) ) ;
}
TArray < FPhysicsActorHandle > Particles { StaticCube } ;
2021-05-27 13:40:37 -04:00
Scene . AddActorsToScene_AssumesLocked ( Particles ) ;
FChaosSQAccelerator SQ { * Scene . GetSpacialAcceleration ( ) } ;
FSQHitBuffer < ChaosInterface : : FOverlapHit > HitBuffer ;
FOverlapAllQueryCallback QueryCallback ;
// Here we query from a position under the box, but using a shape that has an offset. This tests
// a failure case that was previously present where the query system assumed that the QueryTM
// was inside the geometry being used to query.
const FTransform QueryTM { FVec3 { 0.0f , 0.0f , - 110.0f } } ;
constexpr FReal SphereRadius = static_cast < FReal > ( 50.0 ) ;
SQ . Overlap ( TSphere < FReal , 3 > ( FVec3 ( 0.0f , 0.0f , 100.0f ) , SphereRadius ) , QueryTM , HitBuffer , FChaosQueryFilterData ( ) , QueryCallback , FQueryDebugParams ( ) ) ;
EXPECT_TRUE ( HitBuffer . HasBlockingHit ( ) ) ;
}
2020-08-11 01:36:57 -04:00
}