2022-10-21 19:51:57 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "GeometryCollection/GeometryCollectionISMPoolComponent.h"
2023-10-20 11:08:43 -04:00
# include "ChaosLog.h"
# include "Components/InstancedStaticMeshComponent.h"
2022-10-22 17:33:07 -04:00
# include "Engine/CollisionProfile.h"
2022-10-21 19:52:03 -04:00
# include "Engine/StaticMesh.h"
2024-04-22 10:52:10 -04:00
# include "Engine/World.h"
2022-10-21 19:51:57 -04:00
2022-11-22 20:17:33 -05:00
# include UE_INLINE_GENERATED_CPP_BY_NAME(GeometryCollectionISMPoolComponent)
2023-10-20 11:08:43 -04:00
// Don't release ISM components when they empty, but keep them (and their scene proxy) alive.
// This can remove the high cost associated with repeated registration, scene proxy creation and mesh draw command creation.
2024-04-22 10:52:10 -04:00
// But it can also have a high memory overhead since the ISMs retain hard references to their static meshes.
static bool GComponentKeepAlive = false ;
2024-09-03 16:09:07 -04:00
FAutoConsoleVariableRef CVarGCISMPoolComponentKeepAlive (
TEXT ( " r.GC.ISMPool.ComponentKeepAlive " ) ,
2023-10-20 11:08:43 -04:00
GComponentKeepAlive ,
TEXT ( " Keep ISM components alive when all their instances are removed. " ) ) ;
2023-04-14 17:05:40 -04:00
2023-10-20 11:08:43 -04:00
// Use a FreeList to enable recycling of ISM components.
// ISM components aren't unregistered, but their scene proxy is destroyed.
// When recycling a component, a new mesh description can be used.
// This removes the high CPU cost of unregister/register.
// But there is more CPU cost to recycling a component then to simply keeping it alive because scene proxy creation and mesh draw command caching isn't cheap.
// The component memory cost is kept bounded when compared to keeping components alive.
static bool GComponentRecycle = true ;
2024-09-03 16:09:07 -04:00
FAutoConsoleVariableRef CVarGCISMPoolComponentRecycle (
TEXT ( " r.GC.ISMPool.ComponentRecycle " ) ,
2023-10-20 11:08:43 -04:00
GComponentRecycle ,
TEXT ( " Recycle ISM components to a free list for reuse when all their instances are removed. " ) ) ;
// Target free list size when recycling ISM components.
// We try to maintain a pool of free components for fast allocation, but want to clean up when numbers get too high.
2023-08-11 20:20:15 -04:00
static int32 GComponentFreeListTargetSize = 50 ;
2024-09-03 16:09:07 -04:00
FAutoConsoleVariableRef CVarGCISMPoolComponentFreeListTargetSize (
TEXT ( " r.GC.ISMPool.ComponentFreeListTargetSize " ) ,
2023-08-11 20:20:15 -04:00
GComponentFreeListTargetSize ,
2023-10-20 11:08:43 -04:00
TEXT ( " Target size for number of ISM components in the recycling free list. " ) ) ;
2023-04-14 17:05:40 -04:00
2024-04-22 10:52:10 -04:00
// Keep copies of all custom instance data for restoration on readding an instance.
2024-02-09 10:59:46 -05:00
static bool GShadowCopyCustomData = false ;
FAutoConsoleVariableRef CVarShadowCopyCustomData (
2024-09-03 16:09:07 -04:00
TEXT ( " r.GC.ISMPool.ShadowCopyCustomData " ) ,
2024-02-09 10:59:46 -05:00
GShadowCopyCustomData ,
TEXT ( " Keeps a copy of custom instance data so it can be restored if the instance is removed and readded. " ) ) ;
2023-10-12 15:12:32 -04:00
2024-02-06 19:05:22 -05:00
void FGeometryCollectionMeshInfo : : ShadowCopyCustomData ( int32 InstanceCount , int32 NumCustomDataFloatsPerInstance , TArrayView < const float > CustomDataFloats )
{
2024-02-06 19:33:42 -05:00
CustomData . SetNum ( InstanceCount * NumCustomDataFloatsPerInstance + NumCustomDataFloatsPerInstance ) ;
2024-02-06 19:05:22 -05:00
for ( int32 InstanceIndex = 0 ; InstanceIndex < InstanceCount ; + + InstanceIndex )
{
int32 Offset = InstanceIndex * NumCustomDataFloatsPerInstance ;
FMemory : : Memcpy ( & CustomData [ Offset ] , CustomDataFloats . GetData ( ) + Offset , NumCustomDataFloatsPerInstance * CustomDataFloats . GetTypeSize ( ) ) ;
}
}
TArrayView < const float > FGeometryCollectionMeshInfo : : CustomDataSlice ( int32 InstanceIndex , int32 NumCustomDataFloatsPerInstance )
{
TArrayView < const float > DataView = CustomData ;
return DataView . Slice ( InstanceIndex * NumCustomDataFloatsPerInstance , NumCustomDataFloatsPerInstance ) ;
}
FGeometryCollectionMeshGroup : : FMeshId FGeometryCollectionMeshGroup : : AddMesh ( const FGeometryCollectionStaticMeshInstance & MeshInstance , int32 InstanceCount , const FGeometryCollectionMeshInfo & ISMInstanceInfo , TArrayView < const float > CustomDataFloats )
2022-10-21 19:51:57 -04:00
{
const FMeshId MeshInfoIndex = MeshInfos . Emplace ( ISMInstanceInfo ) ;
2024-02-06 19:05:22 -05:00
2024-02-09 10:59:46 -05:00
if ( bAllowPerInstanceRemoval & & GShadowCopyCustomData )
2024-02-06 19:05:22 -05:00
{
FGeometryCollectionMeshInfo & MeshInfo = MeshInfos [ MeshInfoIndex ] ;
MeshInfo . ShadowCopyCustomData ( InstanceCount , MeshInstance . Desc . NumCustomDataFloats , CustomDataFloats ) ;
}
2022-10-21 19:51:57 -04:00
return MeshInfoIndex ;
}
2023-05-25 18:53:40 -04:00
bool FGeometryCollectionMeshGroup : : BatchUpdateInstancesTransforms ( FGeometryCollectionISMPool & ISMPool , FMeshId MeshId , int32 StartInstanceIndex , TArrayView < const FTransform > NewInstancesTransforms , bool bWorldSpace , bool bMarkRenderStateDirty , bool bTeleport )
2022-10-21 19:51:57 -04:00
{
if ( MeshInfos . IsValidIndex ( MeshId ) )
{
2023-10-12 20:59:48 -04:00
return ISMPool . BatchUpdateInstancesTransforms ( MeshInfos [ MeshId ] , StartInstanceIndex , NewInstancesTransforms , bWorldSpace , bMarkRenderStateDirty , bTeleport , bAllowPerInstanceRemoval ) ;
2022-10-21 19:51:57 -04:00
}
UE_LOG ( LogChaos , Warning , TEXT ( " UGeometryCollectionISMPoolComponent : Invalid mesh Id (%d) for this mesh group " ) , MeshId ) ;
return false ;
}
2023-09-06 08:12:44 -04:00
void FGeometryCollectionMeshGroup : : BatchUpdateInstanceCustomData ( FGeometryCollectionISMPool & ISMPool , int32 CustomFloatIndex , float CustomFloatValue )
{
for ( const FGeometryCollectionMeshInfo & MeshInfo : MeshInfos )
{
ISMPool . BatchUpdateInstanceCustomData ( MeshInfo , CustomFloatIndex , CustomFloatValue ) ;
}
}
2022-10-21 19:51:57 -04:00
void FGeometryCollectionMeshGroup : : RemoveAllMeshes ( FGeometryCollectionISMPool & ISMPool )
{
for ( const FGeometryCollectionMeshInfo & MeshInfo : MeshInfos )
{
2023-10-20 11:08:43 -04:00
ISMPool . RemoveInstancesFromISM ( MeshInfo ) ;
2022-10-21 19:51:57 -04:00
}
MeshInfos . Empty ( ) ;
}
2024-04-22 10:52:10 -04:00
void FGeometryCollectionISM : : CreateISM ( USceneComponent * InOwningComponent )
2022-10-21 19:51:57 -04:00
{
2024-04-22 10:52:10 -04:00
check ( InOwningComponent ) ;
2022-10-21 19:51:57 -04:00
2024-04-22 10:52:10 -04:00
AActor * OwningActor = InOwningComponent - > GetOwner ( ) ;
USceneComponent * RootComponent = OwningActor - > GetRootComponent ( ) ;
ISMComponent = NewObject < UInstancedStaticMeshComponent > ( InOwningComponent , NAME_None , RF_Transient | RF_DuplicateTransient ) ;
2023-04-03 23:33:01 -04:00
2023-08-11 16:28:11 -04:00
ISMComponent - > SetRemoveSwap ( ) ;
ISMComponent - > SetCanEverAffectNavigation ( false ) ;
ISMComponent - > SetCollisionEnabled ( ECollisionEnabled : : NoCollision ) ;
2024-04-22 10:52:10 -04:00
ISMComponent - > SetupAttachment ( RootComponent ) ;
2023-08-11 16:28:11 -04:00
ISMComponent - > RegisterComponent ( ) ;
2024-04-22 10:52:10 -04:00
# if WITH_EDITOR
UWorld const * World = InOwningComponent - > GetWorld ( ) ;
const bool bShowInWorldOutliner = World & & World - > IsGameWorld ( ) ;
if ( bShowInWorldOutliner )
{
OwningActor - > AddInstanceComponent ( ISMComponent ) ;
}
# endif
2023-08-11 16:28:11 -04:00
}
2023-11-03 16:01:49 -04:00
void FGeometryCollectionISM : : InitISM ( const FGeometryCollectionStaticMeshInstance & InMeshInstance , bool bKeepAlive , bool bOverrideTransformUpdates )
2023-08-11 16:28:11 -04:00
{
MeshInstance = InMeshInstance ;
check ( ISMComponent ! = nullptr ) ;
2023-11-07 16:19:51 -05:00
UStaticMesh * StaticMesh = MeshInstance . StaticMesh . Get ( ) ;
// We should only get here for valid static mesh objects.
check ( StaticMesh ! = nullptr ) ;
2023-08-11 16:28:11 -04:00
# if WITH_EDITOR
2023-11-07 16:19:51 -05:00
const FName ISMName = MakeUniqueObjectName ( ISMComponent - > GetOwner ( ) , UInstancedStaticMeshComponent : : StaticClass ( ) , StaticMesh - > GetFName ( ) ) ;
2023-08-11 16:28:11 -04:00
const FString ISMNameString = ISMName . ToString ( ) ;
ISMComponent - > Rename ( * ISMNameString ) ;
# endif
2023-11-03 16:01:49 -04:00
ISMComponent - > bUseAttachParentBound = bOverrideTransformUpdates ;
ISMComponent - > SetAbsolute ( bOverrideTransformUpdates , bOverrideTransformUpdates , bOverrideTransformUpdates ) ;
2024-08-13 19:18:47 -04:00
bool bDisallowNanite = false ;
2023-08-11 16:28:11 -04:00
ISMComponent - > EmptyOverrideMaterials ( ) ;
2023-04-03 23:33:01 -04:00
for ( int32 MaterialIndex = 0 ; MaterialIndex < MeshInstance . MaterialsOverrides . Num ( ) ; MaterialIndex + + )
{
2023-11-07 16:19:51 -05:00
UMaterialInterface * Material = MeshInstance . MaterialsOverrides [ MaterialIndex ] . Get ( ) ;
// We should only get here for valid material objects.
check ( Material ! = nullptr ) ;
ISMComponent - > SetMaterial ( MaterialIndex , Material ) ;
2024-08-13 19:18:47 -04:00
// Nanite doesn't support translucent materials.
bDisallowNanite | = Material - > GetBlendMode ( ) = = BLEND_Translucent ;
2023-04-03 23:33:01 -04:00
}
2023-10-27 17:11:06 -04:00
2023-11-07 16:19:51 -05:00
ISMComponent - > SetStaticMesh ( StaticMesh ) ;
2023-10-27 17:11:06 -04:00
ISMComponent - > SetMobility ( ( MeshInstance . Desc . Flags & FISMComponentDescription : : StaticMobility ) ! = 0 ? EComponentMobility : : Static : EComponentMobility : : Movable ) ;
2023-08-11 16:28:11 -04:00
ISMComponent - > NumCustomDataFloats = MeshInstance . Desc . NumCustomDataFloats ;
2023-05-06 02:08:34 -04:00
for ( int32 DataIndex = 0 ; DataIndex < MeshInstance . CustomPrimitiveData . Num ( ) ; DataIndex + + )
{
2023-08-11 16:28:11 -04:00
ISMComponent - > SetDefaultCustomPrimitiveDataFloat ( DataIndex , MeshInstance . CustomPrimitiveData [ DataIndex ] ) ;
2023-05-06 02:08:34 -04:00
}
2023-04-03 23:33:01 -04:00
2023-08-11 16:28:11 -04:00
const bool bReverseCulling = ( MeshInstance . Desc . Flags & FISMComponentDescription : : ReverseCulling ) ! = 0 ;
// Instead of reverse culling we put the mirror in the component transform so that PRIMITIVE_SCENE_DATA_FLAG_DETERMINANT_SIGN will be set for use by materials.
//ISMComponent->SetReverseCulling(bReverseCulling);
const FVector Scale = bReverseCulling ? FVector ( - 1 , 1 , 1 ) : FVector ( 1 , 1 , 1 ) ;
2023-11-03 16:01:49 -04:00
if ( bOverrideTransformUpdates )
2023-09-12 15:54:43 -04:00
{
2023-11-03 16:01:49 -04:00
FTransform TempTm = ISMComponent - > GetAttachParent ( ) ?
ISMComponent - > GetAttachParent ( ) - > GetComponentToWorld ( ) :
FTransform : : Identity ;
// Apply above identified scale to the transform directly
TempTm . SetScale3D ( TempTm . GetScale3D ( ) * Scale ) ;
ISMComponent - > SetComponentToWorld ( TempTm ) ;
ISMComponent - > UpdateComponentTransform ( EUpdateTransformFlags : : None , ETeleportType : : None ) ;
2023-11-08 08:29:29 -05:00
ISMComponent - > MarkRenderTransformDirty ( ) ;
2023-09-12 15:54:43 -04:00
}
2023-11-03 16:01:49 -04:00
else
{
const FTransform NewRelativeTransform ( FQuat : : Identity , MeshInstance . Desc . Position , Scale ) ;
if ( ! ISMComponent - > GetRelativeTransform ( ) . Equals ( NewRelativeTransform ) )
{
// If we're not overriding the transform and need a relative offset, apply that here
ISMComponent - > SetRelativeTransform ( FTransform ( FQuat : : Identity , MeshInstance . Desc . Position , Scale ) ) ;
}
}
2023-10-16 22:31:55 -04:00
if ( ( MeshInstance . Desc . Flags & FISMComponentDescription : : DistanceCullPrimitive ) ! = 0 )
{
ISMComponent - > SetCachedMaxDrawDistance ( MeshInstance . Desc . EndCullDistance ) ;
}
2023-11-03 16:01:49 -04:00
2023-08-11 16:28:11 -04:00
ISMComponent - > SetCullDistances ( MeshInstance . Desc . StartCullDistance , MeshInstance . Desc . EndCullDistance ) ;
ISMComponent - > SetCastShadow ( ( MeshInstance . Desc . Flags & FISMComponentDescription : : AffectShadow ) ! = 0 ) ;
ISMComponent - > bAffectDynamicIndirectLighting = ( MeshInstance . Desc . Flags & FISMComponentDescription : : AffectDynamicIndirectLighting ) ! = 0 ;
ISMComponent - > bAffectDistanceFieldLighting = ( MeshInstance . Desc . Flags & FISMComponentDescription : : AffectDistanceFieldLighting ) ! = 0 ;
2023-10-20 14:52:53 -04:00
ISMComponent - > bCastFarShadow = ( MeshInstance . Desc . Flags & FISMComponentDescription : : AffectFarShadow ) ! = 0 ;
2023-08-11 16:28:11 -04:00
ISMComponent - > bWorldPositionOffsetWritesVelocity = ( MeshInstance . Desc . Flags & FISMComponentDescription : : WorldPositionOffsetWritesVelocity ) ! = 0 ;
2023-10-18 16:50:31 -04:00
ISMComponent - > bEvaluateWorldPositionOffset = ( MeshInstance . Desc . Flags & FISMComponentDescription : : EvaluateWorldPositionOffset ) ! = 0 ;
2023-08-11 16:28:11 -04:00
ISMComponent - > bUseGpuLodSelection = ( MeshInstance . Desc . Flags & FISMComponentDescription : : GpuLodSelection ) ! = 0 ;
ISMComponent - > bOverrideMinLOD = MeshInstance . Desc . MinLod > 0 ;
ISMComponent - > MinLOD = MeshInstance . Desc . MinLod ;
ISMComponent - > SetLODDistanceScale ( MeshInstance . Desc . LodScale ) ;
2023-10-24 03:24:43 -04:00
ISMComponent - > SetUseConservativeBounds ( true ) ;
ISMComponent - > bComputeFastLocalBounds = true ;
2024-08-13 19:18:47 -04:00
ISMComponent - > bDisallowNanite = bDisallowNanite ;
2023-08-11 16:28:11 -04:00
ISMComponent - > SetMeshDrawCommandStatsCategory ( MeshInstance . Desc . StatsCategory ) ;
ISMComponent - > ComponentTags = MeshInstance . Desc . Tags ;
2024-08-27 10:09:50 -04:00
// Use a fixed seed to avoid getting a different seed at every run (see UInstancedStaticMeshComponent::OnRegister())
// A possible improvement would be to compute an hash from the owner Geometry Collection component and use that as the seed.
ISMComponent - > InstancingRandomSeed = 1 ;
2022-10-21 19:51:57 -04:00
}
2023-04-14 09:07:04 -04:00
FInstanceGroups : : FInstanceGroupId FGeometryCollectionISM : : AddInstanceGroup ( int32 InstanceCount , TArrayView < const float > CustomDataFloats )
2022-10-21 19:51:57 -04:00
{
2023-03-21 17:54:35 -04:00
// When adding new group it will always have a single range
2023-04-14 09:07:04 -04:00
const FInstanceGroups : : FInstanceGroupId InstanceGroupIndex = InstanceGroups . AddGroup ( InstanceCount ) ;
const FInstanceGroups : : FInstanceGroupRange & NewInstanceGroup = InstanceGroups . GroupRanges [ InstanceGroupIndex ] ;
2023-03-06 15:17:02 -05:00
2023-10-11 23:18:41 -04:00
// Ensure that remapping arrays are big enough to hold any new items.
2024-01-19 16:41:35 -05:00
InstanceIds . SetNum ( InstanceGroups . GetMaxInstanceIndex ( ) , EAllowShrinking : : No ) ;
2023-10-11 23:18:41 -04:00
2022-10-29 03:00:57 -04:00
FTransform ZeroScaleTransform ;
ZeroScaleTransform . SetIdentityZeroScale ( ) ;
2023-03-06 15:17:02 -05:00
TArray < FTransform > ZeroScaleTransforms ;
ZeroScaleTransforms . Init ( ZeroScaleTransform , InstanceCount ) ;
2023-04-14 09:07:04 -04:00
ISMComponent - > PreAllocateInstancesMemory ( InstanceCount ) ;
2023-10-11 23:18:41 -04:00
TArray < FPrimitiveInstanceId > AddedInstanceIds = ISMComponent - > AddInstancesById ( ZeroScaleTransforms , true , true ) ;
2023-04-14 09:07:04 -04:00
for ( int32 InstanceIndex = 0 ; InstanceIndex < InstanceCount ; + + InstanceIndex )
{
2023-10-11 23:18:41 -04:00
InstanceIds [ NewInstanceGroup . Start + InstanceIndex ] = AddedInstanceIds [ InstanceIndex ] ;
2023-04-14 09:07:04 -04:00
}
// Set any custom data.
2023-03-08 23:38:52 -05:00
if ( CustomDataFloats . Num ( ) )
{
const int32 NumCustomDataFloats = ISMComponent - > NumCustomDataFloats ;
if ( ensure ( NumCustomDataFloats * InstanceCount = = CustomDataFloats . Num ( ) ) )
{
for ( int32 InstanceIndex = 0 ; InstanceIndex < InstanceCount ; + + InstanceIndex )
{
2023-10-11 23:18:41 -04:00
ISMComponent - > SetCustomDataById ( AddedInstanceIds [ InstanceIndex ] , CustomDataFloats . Slice ( InstanceIndex * NumCustomDataFloats , NumCustomDataFloats ) ) ;
2023-03-08 23:38:52 -05:00
}
}
}
2022-10-21 19:51:57 -04:00
return InstanceGroupIndex ;
}
2023-10-20 11:08:43 -04:00
FGeometryCollectionISMPool : : FGeometryCollectionISMPool ( )
: bCachedKeepAlive ( GComponentKeepAlive )
, bCachedRecycle ( GComponentRecycle )
{
}
FGeometryCollectionISMPool : : FISMIndex FGeometryCollectionISMPool : : GetOrAddISM ( UGeometryCollectionISMPoolComponent * OwningComponent , const FGeometryCollectionStaticMeshInstance & MeshInstance , bool & bOutISMCreated )
2022-10-21 19:51:57 -04:00
{
2023-04-18 18:06:56 -04:00
FISMIndex * ISMIndexPtr = MeshToISMIndex . Find ( MeshInstance ) ;
if ( ISMIndexPtr ! = nullptr )
2022-10-21 19:51:57 -04:00
{
2023-10-20 11:08:43 -04:00
bOutISMCreated = false ;
2023-04-18 18:06:56 -04:00
return * ISMIndexPtr ;
}
2023-08-11 16:28:11 -04:00
// Take an ISM from the current FreeLists if available instead of allocating a new slot.
2023-04-18 18:06:56 -04:00
FISMIndex ISMIndex = INDEX_NONE ;
2023-10-13 12:39:49 -04:00
if ( FreeListISM . Num ( ) )
2023-08-11 16:28:11 -04:00
{
ISMIndex = FreeListISM . Last ( ) ;
FreeListISM . RemoveAt ( FreeListISM . Num ( ) - 1 ) ;
2022-10-21 19:51:57 -04:00
}
2023-08-11 20:20:15 -04:00
else if ( FreeList . Num ( ) )
{
ISMIndex = FreeList . Last ( ) ;
FreeList . RemoveAt ( FreeList . Num ( ) - 1 ) ;
2024-04-22 10:52:10 -04:00
ISMs [ ISMIndex ] . CreateISM ( OwningComponent ) ;
2023-08-11 20:20:15 -04:00
}
2022-10-21 19:51:57 -04:00
else
{
2023-08-11 20:20:15 -04:00
ISMIndex = ISMs . AddDefaulted ( ) ;
2024-04-22 10:52:10 -04:00
ISMs [ ISMIndex ] . CreateISM ( OwningComponent ) ;
2022-10-21 19:51:57 -04:00
}
2023-04-18 18:06:56 -04:00
2023-11-03 16:01:49 -04:00
ISMs [ ISMIndex ] . InitISM ( MeshInstance , bCachedKeepAlive , bDisableBoundsAndTransformUpdate ) ;
2023-08-11 20:20:15 -04:00
2023-10-20 11:08:43 -04:00
bOutISMCreated = true ;
2023-04-18 18:06:56 -04:00
MeshToISMIndex . Add ( MeshInstance , ISMIndex ) ;
return ISMIndex ;
}
2023-10-20 11:08:43 -04:00
FGeometryCollectionMeshInfo FGeometryCollectionISMPool : : AddInstancesToISM ( UGeometryCollectionISMPoolComponent * OwningComponent , const FGeometryCollectionStaticMeshInstance & MeshInstance , int32 InstanceCount , TArrayView < const float > CustomDataFloats )
2023-04-18 18:06:56 -04:00
{
2023-10-20 11:08:43 -04:00
bool bISMCreated = false ;
2023-04-18 18:06:56 -04:00
FGeometryCollectionMeshInfo Info ;
2023-10-20 11:08:43 -04:00
Info . ISMIndex = GetOrAddISM ( OwningComponent , MeshInstance , bISMCreated ) ;
2023-03-08 23:38:52 -05:00
Info . InstanceGroupIndex = ISMs [ Info . ISMIndex ] . AddInstanceGroup ( InstanceCount , CustomDataFloats ) ;
2022-10-21 19:51:57 -04:00
return Info ;
}
2023-10-12 20:59:48 -04:00
bool FGeometryCollectionISMPool : : BatchUpdateInstancesTransforms ( FGeometryCollectionMeshInfo & MeshInfo , int32 StartInstanceIndex , TArrayView < const FTransform > NewInstancesTransforms , bool bWorldSpace , bool bMarkRenderStateDirty , bool bTeleport , bool bAllowPerInstanceRemoval )
2023-05-25 18:53:40 -04:00
{
2023-10-11 23:18:41 -04:00
if ( ! ISMs . IsValidIndex ( MeshInfo . ISMIndex ) )
2022-10-21 19:51:57 -04:00
{
2023-10-11 23:18:41 -04:00
UE_LOG ( LogChaos , Warning , TEXT ( " UGeometryCollectionISMPoolComponent : Invalid ISM Id (%d) when updating the transform " ) , MeshInfo . ISMIndex ) ;
return false ;
2022-10-21 19:51:57 -04:00
}
2023-10-11 23:18:41 -04:00
FGeometryCollectionISM & ISM = ISMs [ MeshInfo . ISMIndex ] ;
const FInstanceGroups : : FInstanceGroupRange & InstanceGroup = ISM . InstanceGroups . GroupRanges [ MeshInfo . InstanceGroupIndex ] ;
// If ISM component has identity transform (the common case) then we can skip world space to component space maths inside BatchUpdateInstancesTransforms()
bWorldSpace & = ! ISM . ISMComponent - > GetComponentTransform ( ) . Equals ( FTransform : : Identity , 0.f ) ;
// The transform count should fit within the instance group.
// Clamp it if it doesn't, but if we hit this ensure we need to investigate why.
ensure ( StartInstanceIndex + NewInstancesTransforms . Num ( ) < = InstanceGroup . Count ) ;
const int32 NumTransforms = FMath : : Min ( NewInstancesTransforms . Num ( ) , InstanceGroup . Count - StartInstanceIndex ) ;
// Loop over transforms.
// todo: There may be some value in batching InstanceIds and caling one function for each of Add/Remove/Update.
// However the ISM batched calls themselves seem to be just simple loops over the single instance calls, so probably no benefit.
for ( int InstanceIndex = StartInstanceIndex ; InstanceIndex < StartInstanceIndex + NumTransforms ; + + InstanceIndex )
{
FPrimitiveInstanceId InstanceId = ISM . InstanceIds [ InstanceGroup . Start + InstanceIndex ] ;
FTransform const & Transform = NewInstancesTransforms [ InstanceIndex ] ;
2023-10-12 20:59:48 -04:00
if ( bAllowPerInstanceRemoval )
2023-10-11 23:18:41 -04:00
{
2023-10-12 15:12:32 -04:00
if ( Transform . GetScale3D ( ) . IsZero ( ) & & InstanceId . IsValid ( ) )
{
// Zero scale is used to indicate that we should remove the instance from the ISM.
ISM . ISMComponent - > RemoveInstanceById ( InstanceId ) ;
ISM . InstanceIds [ InstanceGroup . Start + InstanceIndex ] = FPrimitiveInstanceId ( ) ;
continue ;
}
else if ( ! Transform . GetScale3D ( ) . IsZero ( ) & & ! InstanceId . IsValid ( ) )
{
// Re-add the instance to the ISM if the scale becomes non-zero.
2024-02-06 19:05:22 -05:00
FPrimitiveInstanceId Id = ISM . ISMComponent - > AddInstanceById ( Transform , bWorldSpace ) ;
ISM . InstanceIds [ InstanceGroup . Start + InstanceIndex ] = Id ;
2024-02-09 10:59:46 -05:00
if ( MeshInfo . CustomData . Num ( ) )
{
ISM . ISMComponent - > SetCustomDataById ( Id , MeshInfo . CustomDataSlice ( InstanceIndex , ISM . ISMComponent - > NumCustomDataFloats ) ) ;
}
2023-10-12 15:12:32 -04:00
continue ;
}
2023-10-11 23:18:41 -04:00
}
2023-10-12 15:12:32 -04:00
if ( InstanceId . IsValid ( ) )
2023-10-11 23:18:41 -04:00
{
ISM . ISMComponent - > UpdateInstanceTransformById ( InstanceId , Transform , bWorldSpace , bTeleport ) ;
}
}
return true ;
2022-10-21 19:51:57 -04:00
}
2023-09-06 08:12:44 -04:00
void FGeometryCollectionISMPool : : BatchUpdateInstanceCustomData ( FGeometryCollectionMeshInfo const & MeshInfo , int32 CustomFloatIndex , float CustomFloatValue )
{
if ( ! ISMs . IsValidIndex ( MeshInfo . ISMIndex ) )
{
return ;
}
FGeometryCollectionISM & ISM = ISMs [ MeshInfo . ISMIndex ] ;
2023-09-26 19:15:34 -04:00
if ( ! ensure ( CustomFloatIndex < ISM . MeshInstance . Desc . NumCustomDataFloats ) )
2023-09-06 08:12:44 -04:00
{
return ;
}
const FInstanceGroups : : FInstanceGroupRange & InstanceGroup = ISM . InstanceGroups . GroupRanges [ MeshInfo . InstanceGroupIndex ] ;
for ( int32 InstanceIndex = 0 ; InstanceIndex < InstanceGroup . Count ; + + InstanceIndex )
{
2023-10-11 23:18:41 -04:00
const FPrimitiveInstanceId InstanceId = ISM . InstanceIds [ InstanceGroup . Start + InstanceIndex ] ;
if ( InstanceId . IsValid ( ) )
{
ISM . ISMComponent - > SetCustomDataValueById ( InstanceId , CustomFloatIndex , CustomFloatValue ) ;
}
2023-09-06 08:12:44 -04:00
}
}
2023-10-20 11:08:43 -04:00
void FGeometryCollectionISMPool : : RemoveInstancesFromISM ( const FGeometryCollectionMeshInfo & MeshInfo )
2022-10-21 19:51:57 -04:00
{
if ( ISMs . IsValidIndex ( MeshInfo . ISMIndex ) )
{
FGeometryCollectionISM & ISM = ISMs [ MeshInfo . ISMIndex ] ;
2023-04-14 09:07:04 -04:00
const FInstanceGroups : : FInstanceGroupRange & InstanceGroup = ISM . InstanceGroups . GroupRanges [ MeshInfo . InstanceGroupIndex ] ;
2023-10-11 23:18:41 -04:00
for ( int32 Index = 0 ; Index < InstanceGroup . Count ; + + Index )
2023-04-14 09:07:04 -04:00
{
2023-10-11 23:18:41 -04:00
FPrimitiveInstanceId InstanceId = ISM . InstanceIds [ InstanceGroup . Start + Index ] ;
if ( InstanceId . IsValid ( ) )
2023-04-14 17:05:40 -04:00
{
2023-10-11 23:18:41 -04:00
// todo: Could RemoveInstanceByIds() instead as long as that function can handle skipping invalid InstanceIds.
ISM . ISMComponent - > RemoveInstanceById ( InstanceId ) ;
2023-04-14 17:05:40 -04:00
}
}
2023-10-11 23:18:41 -04:00
# if DO_CHECK
// clear the IDs
for ( int32 Index = 0 ; Index < InstanceGroup . Count ; + + Index )
{
ISM . InstanceIds [ InstanceGroup . Start + Index ] = FPrimitiveInstanceId ( ) ;
}
# endif
2022-10-21 19:51:57 -04:00
ISM . InstanceGroups . RemoveGroup ( MeshInfo . InstanceGroupIndex ) ;
2023-04-06 17:29:18 -04:00
2023-04-18 18:06:56 -04:00
if ( ISM . InstanceGroups . IsEmpty ( ) )
2023-04-06 17:29:18 -04:00
{
2023-10-20 11:08:43 -04:00
ensure ( ISM . ISMComponent - > PerInstanceSMData . Num ( ) = = 0 ) ;
2023-04-18 18:06:56 -04:00
// No live instances, so take opportunity to reset indexing.
2023-04-14 09:07:04 -04:00
ISM . InstanceGroups . Reset ( ) ;
2023-10-11 23:18:41 -04:00
ISM . InstanceIds . Reset ( ) ;
2023-10-20 11:08:43 -04:00
RemoveISM ( MeshInfo . ISMIndex , bCachedKeepAlive , bCachedRecycle ) ;
if ( ! bCachedKeepAlive )
{
MeshToISMIndex . Remove ( ISM . MeshInstance ) ;
}
2023-04-18 18:06:56 -04:00
}
2023-10-20 11:08:43 -04:00
}
}
2023-10-18 16:50:31 -04:00
2023-10-20 11:08:43 -04:00
void FGeometryCollectionISMPool : : RemoveISM ( FISMIndex ISMIndex , bool bKeepAlive , bool bRecycle )
{
FGeometryCollectionISM & ISM = ISMs [ ISMIndex ] ;
ensure ( ISM . InstanceGroups . IsEmpty ( ) ) ;
ensure ( ISM . InstanceIds . IsEmpty ( ) ) ;
2023-08-11 16:28:11 -04:00
2023-10-20 11:08:43 -04:00
if ( bKeepAlive )
{
// Nothing to do.
}
else if ( bRecycle )
{
// Recycle to the free list.
2023-08-11 16:28:11 -04:00
# if WITH_EDITOR
2023-10-20 11:08:43 -04:00
ISM . ISMComponent - > Rename ( nullptr ) ;
2023-08-11 16:28:11 -04:00
# endif
2023-10-20 11:08:43 -04:00
FreeListISM . Add ( ISMIndex ) ;
}
else
{
// Completely unregister and destroy the component and mark the ISM slot as free.
ISM . ISMComponent - > DestroyComponent ( ) ;
ISM . ISMComponent = nullptr ;
FreeList . Add ( ISMIndex ) ;
2023-03-21 17:54:35 -04:00
}
}
2022-10-21 19:51:57 -04:00
void FGeometryCollectionISMPool : : Clear ( )
{
MeshToISMIndex . Reset ( ) ;
2023-10-20 11:08:43 -04:00
PrellocationQueue . Reset ( ) ;
2023-08-11 20:20:15 -04:00
FreeList . Reset ( ) ;
2023-08-11 16:28:11 -04:00
FreeListISM . Reset ( ) ;
2022-10-21 19:51:57 -04:00
if ( ISMs . Num ( ) > 0 )
{
if ( AActor * OwningActor = ISMs [ 0 ] . ISMComponent - > GetOwner ( ) )
{
for ( FGeometryCollectionISM & ISM : ISMs )
{
ISM . ISMComponent - > DestroyComponent ( ) ;
}
}
ISMs . Reset ( ) ;
}
}
2023-10-20 11:08:43 -04:00
void FGeometryCollectionISMPool : : RequestPreallocateMeshInstance ( const FGeometryCollectionStaticMeshInstance & MeshInstance )
2023-08-11 20:20:15 -04:00
{
2023-10-20 11:08:43 -04:00
// Preallocation only makes sense when we are keeping empty components alive.
if ( bCachedKeepAlive )
2023-08-11 20:20:15 -04:00
{
2023-10-20 11:08:43 -04:00
uint32 KeyHash = GetTypeHash ( MeshInstance ) ;
if ( MeshToISMIndex . FindByHash ( KeyHash , MeshInstance ) = = nullptr )
{
PrellocationQueue . AddByHash ( KeyHash , MeshInstance ) ;
}
}
}
2023-10-18 16:50:31 -04:00
2023-11-07 16:19:51 -05:00
static bool AreWeakPointersValid ( FGeometryCollectionStaticMeshInstance & InMeshInstance )
{
if ( ! InMeshInstance . StaticMesh . IsValid ( ) )
{
return false ;
}
for ( TWeakObjectPtr < UMaterialInterface > Material : InMeshInstance . MaterialsOverrides )
{
if ( ! Material . IsValid ( ) )
{
return false ;
}
}
return true ;
}
2023-10-20 11:08:43 -04:00
void FGeometryCollectionISMPool : : ProcessPreallocationRequests ( UGeometryCollectionISMPoolComponent * OwningComponent , int32 MaxPreallocations )
{
int32 NumAdded = 0 ;
for ( TSet < FGeometryCollectionStaticMeshInstance > : : TIterator It ( PrellocationQueue ) ; It ; + + It )
{
bool bISMCreated = false ;
2023-11-07 16:19:51 -05:00
// Objects in the entries of the preallocation queue may no longer be loaded.
if ( AreWeakPointersValid ( * It ) )
{
GetOrAddISM ( OwningComponent , * It , bISMCreated ) ;
}
2023-10-20 11:08:43 -04:00
It . RemoveCurrent ( ) ;
2023-08-11 20:20:15 -04:00
2023-10-20 11:08:43 -04:00
if ( bISMCreated )
{
if ( + + NumAdded > = MaxPreallocations )
{
break ;
}
}
}
}
2023-11-03 16:01:49 -04:00
void FGeometryCollectionISMPool : : UpdateAbsoluteTransforms ( const FTransform & BaseTransform , EUpdateTransformFlags UpdateTransformFlags , ETeleportType Teleport )
{
for ( const FGeometryCollectionISM & GcIsm : ISMs )
{
const bool bReverseCulling = ( GcIsm . MeshInstance . Desc . Flags & FISMComponentDescription : : ReverseCulling ) ! = 0 ;
check ( GcIsm . MeshInstance . Desc . Position = = FVector : : ZeroVector ) ;
2023-11-30 18:11:12 -05:00
if ( UInstancedStaticMeshComponent * Ism = GcIsm . ISMComponent )
2023-12-08 17:46:18 -05:00
{
2023-11-30 18:11:12 -05:00
if ( bReverseCulling )
{
// As in InitISM we need to apply the inverted X scale for reverse culling.
// Just copy the transform and set an inverted scale to apply to the ISM
FVector BaseScale = BaseTransform . GetScale3D ( ) ;
BaseScale . X = - BaseScale . X ;
FTransform Flipped = BaseTransform ;
Flipped . SetScale3D ( BaseScale ) ;
2023-12-08 17:46:18 -05:00
2023-11-30 18:11:12 -05:00
Ism - > SetComponentToWorld ( Flipped ) ;
}
else
{
Ism - > SetComponentToWorld ( BaseTransform ) ;
}
2023-11-03 16:01:49 -04:00
2023-11-30 18:11:12 -05:00
Ism - > UpdateComponentTransform ( UpdateTransformFlags | EUpdateTransformFlags : : SkipPhysicsUpdate , Teleport ) ;
Ism - > MarkRenderTransformDirty ( ) ;
}
2023-11-03 16:01:49 -04:00
}
}
2023-10-20 11:08:43 -04:00
void FGeometryCollectionISMPool : : Tick ( UGeometryCollectionISMPoolComponent * OwningComponent )
{
// Recache component lifecycle state from cvar.
const bool bRemovedKeepAlive = bCachedKeepAlive & & ! GComponentKeepAlive ;
const bool bRemovedReycle = bCachedRecycle & & ! GComponentRecycle ;
bCachedKeepAlive = GComponentKeepAlive ;
bCachedRecycle = GComponentRecycle ;
// If we disabled keep alive behavior since last update then deal with the zombie components.
if ( bRemovedKeepAlive )
{
for ( int32 ISMIndex = 0 ; ISMIndex < ISMs . Num ( ) ; + + ISMIndex )
{
FGeometryCollectionISM & ISM = ISMs [ ISMIndex ] ;
if ( ISM . ISMComponent & & ISM . InstanceGroups . IsEmpty ( ) )
{
// Actually release the ISM.
RemoveISM ( ISMIndex , false , bCachedRecycle ) ;
MeshToISMIndex . Remove ( ISM . MeshInstance ) ;
}
}
}
// Process preallocation queue.
if ( ! bCachedKeepAlive )
{
PrellocationQueue . Reset ( ) ;
}
else if ( ! PrellocationQueue . IsEmpty ( ) )
{
// Preallocate components per tick until the queue is empty.
const int32 PreallocateCountPerTick = 2 ;
ProcessPreallocationRequests ( OwningComponent , PreallocateCountPerTick ) ;
}
if ( FreeListISM . Num ( ) > 0 )
{
// Release components per tick until we reach minimum pool size.
const int32 RemoveCountPerTick = 1 ;
const int32 FreeListTargetSize = bRemovedReycle ? 0 : FMath : : Max ( FMath : : Max ( FreeListISM . Num ( ) - RemoveCountPerTick , GComponentFreeListTargetSize ) , 0 ) ;
while ( FreeListISM . Num ( ) > FreeListTargetSize )
{
2024-01-19 16:41:35 -05:00
const int32 ISMIndex = FreeListISM . Pop ( EAllowShrinking : : No ) ;
2023-10-20 11:08:43 -04:00
RemoveISM ( ISMIndex , false , false ) ;
}
2023-08-11 20:20:15 -04:00
}
}
2022-10-21 19:51:57 -04:00
UGeometryCollectionISMPoolComponent : : UGeometryCollectionISMPoolComponent ( const FObjectInitializer & ObjectInitializer )
: NextMeshGroupId ( 0 )
{
2023-08-11 20:20:15 -04:00
PrimaryComponentTick . bCanEverTick = true ;
PrimaryComponentTick . bStartWithTickEnabled = true ;
PrimaryComponentTick . bAllowTickOnDedicatedServer = false ;
PrimaryComponentTick . TickInterval = 0.25f ;
}
void UGeometryCollectionISMPoolComponent : : TickComponent ( float DeltaTime , enum ELevelTick TickType , FActorComponentTickFunction * ThisTickFunction )
{
Super : : TickComponent ( DeltaTime , TickType , ThisTickFunction ) ;
2023-10-20 11:08:43 -04:00
Pool . Tick ( this ) ;
2022-10-21 19:51:57 -04:00
}
2023-10-12 20:59:48 -04:00
UGeometryCollectionISMPoolComponent : : FMeshGroupId UGeometryCollectionISMPoolComponent : : CreateMeshGroup ( bool bAllowPerInstanceRemoval )
2022-10-21 19:51:57 -04:00
{
2023-10-12 20:59:48 -04:00
FGeometryCollectionMeshGroup Group ;
Group . bAllowPerInstanceRemoval = bAllowPerInstanceRemoval ;
MeshGroups . Add ( NextMeshGroupId , Group ) ;
2022-10-21 19:51:57 -04:00
return NextMeshGroupId + + ;
}
void UGeometryCollectionISMPoolComponent : : DestroyMeshGroup ( FMeshGroupId MeshGroupId )
{
if ( FGeometryCollectionMeshGroup * MeshGroup = MeshGroups . Find ( MeshGroupId ) )
{
MeshGroup - > RemoveAllMeshes ( Pool ) ;
MeshGroups . Remove ( MeshGroupId ) ;
}
}
2023-04-03 23:33:01 -04:00
UGeometryCollectionISMPoolComponent : : FMeshId UGeometryCollectionISMPoolComponent : : AddMeshToGroup ( FMeshGroupId MeshGroupId , const FGeometryCollectionStaticMeshInstance & MeshInstance , int32 InstanceCount , TArrayView < const float > CustomDataFloats )
2022-10-21 19:51:57 -04:00
{
if ( FGeometryCollectionMeshGroup * MeshGroup = MeshGroups . Find ( MeshGroupId ) )
{
2023-10-20 11:08:43 -04:00
const FGeometryCollectionMeshInfo ISMInstanceInfo = Pool . AddInstancesToISM ( this , MeshInstance , InstanceCount , CustomDataFloats ) ;
2024-02-06 19:05:22 -05:00
return MeshGroup - > AddMesh ( MeshInstance , InstanceCount , ISMInstanceInfo , CustomDataFloats ) ;
2022-10-21 19:51:57 -04:00
}
UE_LOG ( LogChaos , Warning , TEXT ( " UGeometryCollectionISMPoolComponent : Trying to add a mesh to a mesh group (%d) that does not exists " ) , MeshGroupId ) ;
return INDEX_NONE ;
}
bool UGeometryCollectionISMPoolComponent : : BatchUpdateInstancesTransforms ( FMeshGroupId MeshGroupId , FMeshId MeshId , int32 StartInstanceIndex , const TArray < FTransform > & NewInstancesTransforms , bool bWorldSpace , bool bMarkRenderStateDirty , bool bTeleport )
2023-05-25 18:53:40 -04:00
{
return BatchUpdateInstancesTransforms ( MeshGroupId , MeshId , StartInstanceIndex , MakeArrayView ( NewInstancesTransforms ) , bWorldSpace , bMarkRenderStateDirty , bTeleport ) ;
}
bool UGeometryCollectionISMPoolComponent : : BatchUpdateInstancesTransforms ( FMeshGroupId MeshGroupId , FMeshId MeshId , int32 StartInstanceIndex , TArrayView < const FTransform > NewInstancesTransforms , bool bWorldSpace , bool bMarkRenderStateDirty , bool bTeleport )
2022-10-21 19:51:57 -04:00
{
if ( FGeometryCollectionMeshGroup * MeshGroup = MeshGroups . Find ( MeshGroupId ) )
{
return MeshGroup - > BatchUpdateInstancesTransforms ( Pool , MeshId , StartInstanceIndex , NewInstancesTransforms , bWorldSpace , bMarkRenderStateDirty , bTeleport ) ;
}
UE_LOG ( LogChaos , Warning , TEXT ( " UGeometryCollectionISMPoolComponent : Trying to update instance with mesh group (%d) that not exists " ) , MeshGroupId ) ;
return false ;
}
2023-04-14 09:07:04 -04:00
2023-09-06 08:12:44 -04:00
bool UGeometryCollectionISMPoolComponent : : BatchUpdateInstanceCustomData ( FMeshGroupId MeshGroupId , int32 CustomFloatIndex , float CustomFloatValue )
{
if ( FGeometryCollectionMeshGroup * MeshGroup = MeshGroups . Find ( MeshGroupId ) )
{
MeshGroup - > BatchUpdateInstanceCustomData ( Pool , CustomFloatIndex , CustomFloatValue ) ;
return true ;
}
UE_LOG ( LogChaos , Warning , TEXT ( " UGeometryCollectionISMPoolComponent : Trying to update instance with mesh group (%d) that not exists " ) , MeshGroupId ) ;
return false ;
}
2023-04-18 18:06:56 -04:00
void UGeometryCollectionISMPoolComponent : : PreallocateMeshInstance ( const FGeometryCollectionStaticMeshInstance & MeshInstance )
{
2023-10-20 11:08:43 -04:00
Pool . RequestPreallocateMeshInstance ( MeshInstance ) ;
2023-04-18 18:06:56 -04:00
}
2023-11-13 10:09:25 -05:00
void UGeometryCollectionISMPoolComponent : : SetTickablePoolManagement ( bool bEnablePoolManagement )
{
if ( ! bEnablePoolManagement )
{
// Disable the keep alive and recycle pool management systems.
// This also disables preallocation for this pool.
Pool . bCachedKeepAlive = false ;
Pool . bCachedRecycle = false ;
}
// Disable the Tick that is used to manage the pool.
PrimaryComponentTick . SetTickFunctionEnable ( bEnablePoolManagement ) ;
}
2023-11-03 16:01:49 -04:00
void UGeometryCollectionISMPoolComponent : : SetOverrideTransformUpdates ( bool bOverrideUpdates )
{
Pool . bDisableBoundsAndTransformUpdate = bOverrideUpdates ;
}
void UGeometryCollectionISMPoolComponent : : UpdateAbsoluteTransforms ( const FTransform & BaseTransform , EUpdateTransformFlags UpdateTransformFlags , ETeleportType Teleport )
{
Pool . UpdateAbsoluteTransforms ( BaseTransform , UpdateTransformFlags , Teleport ) ;
}
2023-04-14 09:07:04 -04:00
void UGeometryCollectionISMPoolComponent : : GetResourceSizeEx ( FResourceSizeEx & CumulativeResourceSize )
{
Super : : GetResourceSizeEx ( CumulativeResourceSize ) ;
int32 SizeBytes =
MeshGroups . GetAllocatedSize ( )
+ Pool . MeshToISMIndex . GetAllocatedSize ( )
+ Pool . ISMs . GetAllocatedSize ( )
2023-08-11 20:20:15 -04:00
+ Pool . FreeList . GetAllocatedSize ( )
2023-10-13 12:39:49 -04:00
+ Pool . FreeListISM . GetAllocatedSize ( ) ;
2023-04-14 09:07:04 -04:00
for ( FGeometryCollectionISM ISM : Pool . ISMs )
{
2023-10-11 23:18:41 -04:00
SizeBytes + = ISM . InstanceIds . GetAllocatedSize ( )
2023-04-14 09:07:04 -04:00
+ ISM . InstanceGroups . GroupRanges . GetAllocatedSize ( )
+ ISM . InstanceGroups . FreeList . GetAllocatedSize ( ) ;
}
CumulativeResourceSize . AddDedicatedSystemMemoryBytes ( SizeBytes ) ;
}