2019-12-26 15:32:37 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
2019-10-04 13:11:45 -04:00
# include "DatasmithMeshExporter.h"
2020-09-24 00:43:27 -04:00
# include "DatasmithExporterManager.h"
2019-10-04 13:11:45 -04:00
# include "DatasmithMesh.h"
# include "DatasmithMeshUObject.h"
# include "DatasmithSceneFactory.h"
# include "DatasmithUtils.h"
2020-02-17 13:28:31 -05:00
# include "Containers/LockFreeList.h"
2019-10-04 13:11:45 -04:00
# include "HAL/FileManager.h"
2020-05-06 17:58:18 -04:00
# include "HAL/PlatformFileManager.h"
2020-03-18 11:01:39 -04:00
# include "Misc/Guid.h"
2019-10-04 13:11:45 -04:00
# include "Misc/Paths.h"
# include "Serialization/MemoryWriter.h"
2020-02-17 13:28:31 -05:00
# include "StaticMeshAttributes.h"
2020-01-23 16:28:59 -05:00
# include "StaticMeshOperations.h"
2019-10-04 13:11:45 -04:00
# include "UVMapSettings.h"
2021-03-05 19:27:14 -04:00
/**
* Number of datsmith meshes pending deletion for which the Garbage Collection will be triggered .
*/
constexpr int32 DSMeshGarbageCollectionThreshold { 2000 } ;
2020-02-17 13:28:31 -05:00
/**
* Implementation class of the DatasmithMeshExporter
* We use a lockfree UDatasmithMesh pool to avoid creating new UObject when exporting and reduces our memory footprint .
*/
class FDatasmithMeshExporterImpl
2019-10-04 13:11:45 -04:00
{
2020-02-17 13:28:31 -05:00
public :
2020-09-24 00:43:27 -04:00
struct FDatasmithMeshExporterOptions
{
FDatasmithMeshExporterOptions ( const FString & InFullPath , FDatasmithMesh & InMesh , EDSExportLightmapUV InLightmapUV , FDatasmithMesh * InCollisionMesh = nullptr )
: MeshFullPath ( InFullPath )
, Mesh ( InMesh )
, LightmapUV ( InLightmapUV )
, CollisionMesh ( InCollisionMesh )
{ }
FString MeshFullPath ;
FDatasmithMesh & Mesh ;
EDSExportLightmapUV LightmapUV ;
FDatasmithMesh * CollisionMesh ;
} ;
2020-03-18 11:01:39 -04:00
FDatasmithMeshExporterImpl ( ) :
UniqueID ( FGuid : : NewGuid ( ) )
{ }
~ FDatasmithMeshExporterImpl ( )
{
ClearUDatasmithMeshPool ( ) ;
}
2019-10-04 13:11:45 -04:00
2020-02-17 13:28:31 -05:00
/**
2020-03-18 11:01:39 -04:00
* Generate and fill a UDatasmithMesh from a FDatasmithMesh .
2020-11-24 18:42:39 -04:00
* Note : The returned TSharedPtr uses a special destructor to return the UDatasmithMesh to a pool instead of destroying it ( which wouldn ' t work anyways ) .
2020-02-17 13:28:31 -05:00
*/
2020-03-18 11:01:39 -04:00
TSharedPtr < UDatasmithMesh > GeneratePooledUDatasmithMeshFromFDatasmithMesh ( const FDatasmithMesh & Mesh , bool bIsCollisionMesh ) ;
2020-02-17 13:28:31 -05:00
2020-09-24 00:43:27 -04:00
bool DoExport ( TSharedPtr < IDatasmithMeshElement > & MeshElement , const FDatasmithMeshExporterOptions & ExportOptions ) ;
void PreExport ( const FDatasmithMeshExporterOptions & ExporterOptions ) ;
bool ExportMeshes ( const FDatasmithMeshExporterOptions & ExporterOptions , FMD5Hash & OutHash ) ;
void PostExport ( const FDatasmithMesh & DatasmithMesh , TSharedPtr < IDatasmithMeshElement > & MeshElement ) ;
2020-02-17 13:28:31 -05:00
void CreateDefaultUVs ( FDatasmithMesh & DatasmithMesh ) ;
void RegisterStaticMeshAttributes ( FMeshDescription & MeshDescription ) ;
FString LastError ;
private :
2020-03-18 11:01:39 -04:00
/**
* This function allows reusing an instanced UDatasmithMesh . Reusing the same object will avoid creating new garbage in memory .
*/
void FillUDatasmithMeshFromFDatasmithMesh ( TSharedPtr < UDatasmithMesh > & UMesh , const FDatasmithMesh & Mesh , bool bValidateRawMesh ) ;
TSharedPtr < UDatasmithMesh > GetPooledUDatasmithMesh ( bool bIsCollisionMesh ) ;
void ReturnUDatasmithMeshToPool ( UDatasmithMesh * & UMesh ) ;
void ClearUDatasmithMeshPool ( ) ;
/**
* A pool of UDatasmithMesh that we use to avoid creating new UObject , this greatly reduce the garbage created when one instance of FDatasmithMeshExporter is used to export multiple Meshes .
*/
2020-02-17 13:28:31 -05:00
TLockFreePointerListLIFO < UDatasmithMesh > DatasmithMeshUObjectPool ;
static TAtomic < int32 > NumberOfUMeshPendingGC ;
2020-03-18 11:01:39 -04:00
/**
* This exporter ' s UniqueID . Used to make sure pooled meshes are not using the same names across different threads .
*/
FGuid UniqueID ;
/**
* Custom deleter used by the TSharedPtr of a pooled UDatasmithMesh to return it to the pool instead of deleting them .
*/
struct FPooledUDatasmithMeshDeleter
{
FPooledUDatasmithMeshDeleter ( FDatasmithMeshExporterImpl * InExporterPtr )
: ExporterPtr ( InExporterPtr )
{ }
FORCEINLINE void operator ( ) ( UDatasmithMesh * Object ) const
{
check ( Object ! = nullptr & & ExporterPtr ! = nullptr ) ;
ExporterPtr - > ReturnUDatasmithMeshToPool ( Object ) ;
}
FDatasmithMeshExporterImpl * ExporterPtr ;
} ;
2020-02-17 13:28:31 -05:00
} ;
TAtomic < int32 > FDatasmithMeshExporterImpl : : NumberOfUMeshPendingGC ( 0 ) ;
FDatasmithMeshExporter : : FDatasmithMeshExporter ( )
{
Impl = new FDatasmithMeshExporterImpl ( ) ;
}
FDatasmithMeshExporter : : ~ FDatasmithMeshExporter ( )
{
delete Impl ;
Impl = nullptr ;
2019-10-04 13:11:45 -04:00
}
2020-09-24 00:43:27 -04:00
FString GetMeshFilePath ( const TCHAR * Filepath , const TCHAR * Filename )
2019-10-04 13:11:45 -04:00
{
FString NormalizedFilepath = Filepath ;
FPaths : : NormalizeDirectoryName ( NormalizedFilepath ) ;
FString NormalizedFilename = Filename ;
FPaths : : NormalizeFilename ( NormalizedFilename ) ;
2020-09-24 00:43:27 -04:00
return FPaths : : Combine ( * NormalizedFilepath , FPaths : : SetExtension ( NormalizedFilename , UDatasmithMesh : : GetFileExtension ( ) ) ) ;
}
2019-10-04 13:11:45 -04:00
2020-09-24 00:43:27 -04:00
bool FDatasmithMeshExporterImpl : : ExportMeshes ( const FDatasmithMeshExporterOptions & ExporterOptions , FMD5Hash & OutHash )
{
2020-03-18 11:01:39 -04:00
TArray < TSharedPtr < UDatasmithMesh > , TInlineAllocator < 2 > > MeshesToExport ;
2019-10-04 13:11:45 -04:00
2020-02-17 13:28:31 -05:00
// Static mesh, we keep a static UDatasmithMesh alive as a utility object and re-use it for every export instead of creating a new one every time. This avoid creating garbage in memory.
2020-03-18 11:01:39 -04:00
bool bIsCollisionMesh = false ;
2020-09-24 00:43:27 -04:00
MeshesToExport . Add ( GeneratePooledUDatasmithMeshFromFDatasmithMesh ( ExporterOptions . Mesh , bIsCollisionMesh ) ) ;
2019-10-04 13:11:45 -04:00
// Collision mesh
2020-09-24 00:43:27 -04:00
if ( ExporterOptions . CollisionMesh )
2019-10-04 13:11:45 -04:00
{
2020-03-18 11:01:39 -04:00
bIsCollisionMesh = true ;
2020-09-24 00:43:27 -04:00
MeshesToExport . Add ( GeneratePooledUDatasmithMeshFromFDatasmithMesh ( * ExporterOptions . CollisionMesh , bIsCollisionMesh ) ) ;
2019-10-04 13:11:45 -04:00
}
IPlatformFile & PlatformFile = FPlatformFileManager : : Get ( ) . GetPlatformFile ( ) ;
2020-09-24 00:43:27 -04:00
TUniquePtr < FArchive > Archive ( IFileManager : : Get ( ) . CreateFileWriter ( * ExporterOptions . MeshFullPath ) ) ;
2019-10-04 13:11:45 -04:00
if ( ! Archive . IsValid ( ) )
{
2020-09-24 00:43:27 -04:00
LastError = FString : : Printf ( TEXT ( " Failed writing to file %s " ) , * ExporterOptions . MeshFullPath ) ;
2019-10-04 13:11:45 -04:00
2020-09-24 00:43:27 -04:00
return false ;
2019-10-04 13:11:45 -04:00
}
int32 NumMeshes = MeshesToExport . Num ( ) ;
* Archive < < NumMeshes ;
FMD5 MD5 ;
2020-03-18 11:01:39 -04:00
for ( TSharedPtr < UDatasmithMesh > & MeshToExport : MeshesToExport )
2019-10-04 13:11:45 -04:00
{
TArray < uint8 > Bytes ;
FMemoryWriter MemoryWriter ( Bytes , true ) ;
MemoryWriter . ArIgnoreClassRef = false ;
MemoryWriter . ArIgnoreArchetypeRef = false ;
MemoryWriter . ArNoDelta = false ;
2020-09-24 00:43:27 -04:00
MemoryWriter . SetWantBinaryPropertySerialization ( true ) ;
2019-10-04 13:11:45 -04:00
MeshToExport - > Serialize ( MemoryWriter ) ;
// Calculate the Hash of all the mesh to export
2020-09-24 00:43:27 -04:00
for ( FDatasmithMeshSourceModel & Model : MeshToExport - > SourceModels )
2019-10-04 13:11:45 -04:00
{
uint8 * Buffer = ( uint8 * ) Model . RawMeshBulkData . GetBulkData ( ) . LockReadOnly ( ) ;
2020-09-24 00:43:27 -04:00
MD5 . Update ( Buffer , Model . RawMeshBulkData . GetBulkData ( ) . GetBulkDataSize ( ) ) ;
2019-10-04 13:11:45 -04:00
Model . RawMeshBulkData . GetBulkData ( ) . Unlock ( ) ;
}
* Archive < < Bytes ;
}
2020-09-24 00:43:27 -04:00
OutHash . Set ( MD5 ) ;
2019-10-04 13:11:45 -04:00
Archive - > Close ( ) ;
2020-09-24 00:43:27 -04:00
return true ;
}
2019-10-04 13:11:45 -04:00
2020-09-24 00:43:27 -04:00
TSharedPtr < IDatasmithMeshElement > FDatasmithMeshExporter : : ExportToUObject ( const TCHAR * Filepath , const TCHAR * Filename , FDatasmithMesh & Mesh , FDatasmithMesh * CollisionMesh , EDSExportLightmapUV LightmapUV )
{
FString FullPath ( GetMeshFilePath ( Filepath , Filename ) ) ;
FDatasmithMeshExporterImpl : : FDatasmithMeshExporterOptions ExportOptions ( MoveTemp ( FullPath ) , Mesh , LightmapUV , CollisionMesh ) ;
2019-10-04 13:11:45 -04:00
2020-09-24 00:43:27 -04:00
TSharedPtr < IDatasmithMeshElement > ExportedMeshElement ;
Impl - > DoExport ( ExportedMeshElement , ExportOptions ) ;
2020-11-24 18:42:39 -04:00
2020-09-24 00:43:27 -04:00
return ExportedMeshElement ;
}
bool FDatasmithMeshExporter : : ExportToUObject ( TSharedPtr < IDatasmithMeshElement > & MeshElement , const TCHAR * Filepath , FDatasmithMesh & Mesh , FDatasmithMesh * CollisionMesh , EDSExportLightmapUV LightmapUV )
{
FString FullPath ( GetMeshFilePath ( Filepath , MeshElement - > GetName ( ) ) ) ;
FDatasmithMeshExporterImpl : : FDatasmithMeshExporterOptions ExportOptions ( MoveTemp ( FullPath ) , Mesh , LightmapUV , CollisionMesh ) ;
2020-11-24 18:42:39 -04:00
2020-09-24 00:43:27 -04:00
return Impl - > DoExport ( MeshElement , ExportOptions ) ;
2019-10-04 13:11:45 -04:00
}
2020-02-17 13:28:31 -05:00
FString FDatasmithMeshExporter : : GetLastError ( ) const
{
return Impl - > LastError ;
}
2020-09-24 00:43:27 -04:00
bool FDatasmithMeshExporterImpl : : DoExport ( TSharedPtr < IDatasmithMeshElement > & MeshElement , const FDatasmithMeshExporterOptions & ExportOptions )
2019-10-04 13:11:45 -04:00
{
2020-09-24 00:43:27 -04:00
FMD5Hash Hash ;
PreExport ( ExportOptions ) ;
if ( ExportMeshes ( ExportOptions , Hash ) )
{
// If no existing MeshElement provided, create one.
if ( ! MeshElement )
{
FString BaseFileName = FPaths : : GetBaseFilename ( ExportOptions . MeshFullPath ) ;
MeshElement = FDatasmithSceneFactory : : CreateMesh ( * BaseFileName ) ;
}
MeshElement - > SetFile ( * ExportOptions . MeshFullPath ) ;
MeshElement - > SetFileHash ( Hash ) ;
PostExport ( ExportOptions . Mesh , MeshElement ) ;
return true ;
}
return false ;
}
void FDatasmithMeshExporterImpl : : PreExport ( const FDatasmithMeshExporterOptions & ExporterOptions )
{
FDatasmithMesh & Mesh = ExporterOptions . Mesh ;
2019-10-04 13:11:45 -04:00
// If the mesh doesn't have a name, use the filename as its name
if ( FCString : : Strlen ( Mesh . GetName ( ) ) = = 0 )
{
2020-09-24 00:43:27 -04:00
Mesh . SetName ( * FPaths : : GetBaseFilename ( ExporterOptions . MeshFullPath ) ) ;
2019-10-04 13:11:45 -04:00
}
bool bHasUVs = Mesh . GetUVChannelsCount ( ) > 0 ;
if ( ! bHasUVs )
{
CreateDefaultUVs ( Mesh ) ;
}
for ( int32 LODIndex = 0 ; LODIndex < Mesh . GetLODsCount ( ) ; + + LODIndex )
{
2021-02-03 14:57:28 -04:00
if ( FDatasmithMesh * LODMesh = Mesh . GetLOD ( LODIndex ) )
{
CreateDefaultUVs ( * LODMesh ) ;
}
2019-10-04 13:11:45 -04:00
}
}
2020-09-24 00:43:27 -04:00
void FDatasmithMeshExporterImpl : : PostExport ( const FDatasmithMesh & DatasmithMesh , TSharedPtr < IDatasmithMeshElement > & MeshElement )
2019-10-04 13:11:45 -04:00
{
FBox Extents = DatasmithMesh . GetExtents ( ) ;
float Width = Extents . Max [ 0 ] - Extents . Min [ 0 ] ;
float Height = Extents . Max [ 2 ] - Extents . Min [ 2 ] ;
float Depth = Extents . Max [ 1 ] - Extents . Min [ 1 ] ;
MeshElement - > SetDimensions ( DatasmithMesh . ComputeArea ( ) , Width , Height , Depth ) ;
MeshElement - > SetLightmapSourceUV ( DatasmithMesh . GetLightmapSourceUVChannel ( ) ) ;
}
2020-02-17 13:28:31 -05:00
void FDatasmithMeshExporterImpl : : CreateDefaultUVs ( FDatasmithMesh & Mesh )
2019-10-04 13:11:45 -04:00
{
if ( Mesh . GetUVChannelsCount ( ) > 0 )
{
return ;
}
// Get the mesh description to generate BoxUV.
FMeshDescription MeshDescription ;
RegisterStaticMeshAttributes ( MeshDescription ) ;
FDatasmithMeshUtils : : ToMeshDescription ( Mesh , MeshDescription ) ;
FUVMapParameters UVParameters ( Mesh . GetExtents ( ) . GetCenter ( ) , FQuat : : Identity , Mesh . GetExtents ( ) . GetSize ( ) , FVector : : OneVector , FVector2D : : UnitVector ) ;
TMap < FVertexInstanceID , FVector2D > TexCoords ;
2020-01-23 16:28:59 -05:00
FStaticMeshOperations : : GenerateBoxUV ( MeshDescription , UVParameters , TexCoords ) ;
2020-11-24 18:42:39 -04:00
2019-10-04 13:11:45 -04:00
// Put the results in a map to determine the number of unique values.
TMap < FVector2D , TArray < int32 > > UniqueTexCoordMap ;
for ( const TPair < FVertexInstanceID , FVector2D > & Pair : TexCoords )
{
TArray < int32 > & MappedIndices = UniqueTexCoordMap . FindOrAdd ( Pair . Value ) ;
MappedIndices . Add ( Pair . Key . GetValue ( ) ) ;
}
//Set the UV values
Mesh . AddUVChannel ( ) ;
Mesh . SetUVCount ( 0 , UniqueTexCoordMap . Num ( ) ) ;
int32 UVIndex = 0 ;
TArray < int32 > IndicesMapping ;
IndicesMapping . AddZeroed ( TexCoords . Num ( ) ) ;
for ( const TPair < FVector2D , TArray < int32 > > & UniqueCoordPair : UniqueTexCoordMap )
{
Mesh . SetUV ( 0 , UVIndex , UniqueCoordPair . Key . X , UniqueCoordPair . Key . Y ) ;
for ( int32 IndicesIndex : UniqueCoordPair . Value )
{
IndicesMapping [ IndicesIndex ] = UVIndex ;
}
UVIndex + + ;
}
//Map the UV indices.
for ( int32 FaceIndex = 0 ; FaceIndex < Mesh . GetFacesCount ( ) ; + + FaceIndex )
{
const int32 IndicesOffset = FaceIndex * 3 ;
check ( IndicesOffset + 2 < IndicesMapping . Num ( ) ) ;
Mesh . SetFaceUV ( FaceIndex , 0 , IndicesMapping [ IndicesOffset + 0 ] , IndicesMapping [ IndicesOffset + 1 ] , IndicesMapping [ IndicesOffset + 2 ] ) ;
}
}
2020-02-17 13:28:31 -05:00
void FDatasmithMeshExporterImpl : : RegisterStaticMeshAttributes ( FMeshDescription & MeshDescription )
2019-10-04 13:11:45 -04:00
{
2020-07-16 08:23:15 -04:00
FStaticMeshAttributes ( MeshDescription ) . Register ( ) ;
2019-10-04 13:11:45 -04:00
}
2020-02-17 13:28:31 -05:00
2020-03-18 11:01:39 -04:00
TSharedPtr < UDatasmithMesh > FDatasmithMeshExporterImpl : : GeneratePooledUDatasmithMeshFromFDatasmithMesh ( const FDatasmithMesh & Mesh , bool bIsCollisionMesh )
2020-02-17 13:28:31 -05:00
{
2020-03-18 11:01:39 -04:00
TSharedPtr < UDatasmithMesh > PooledMesh = GetPooledUDatasmithMesh ( bIsCollisionMesh ) ;
FillUDatasmithMeshFromFDatasmithMesh ( PooledMesh , Mesh , ! bIsCollisionMesh ) ;
PooledMesh - > bIsCollisionMesh = bIsCollisionMesh ;
return PooledMesh ;
}
/**
* This function allows reusing an instanced UDatasmithMesh . Reusing the same object will avoid creating new garbage in memory .
*/
void FDatasmithMeshExporterImpl : : FillUDatasmithMeshFromFDatasmithMesh ( TSharedPtr < UDatasmithMesh > & UMesh , const FDatasmithMesh & Mesh , bool bValidateRawMesh )
{
UMesh - > MeshName = Mesh . GetName ( ) ;
FRawMesh RawMesh ;
FDatasmithMeshUtils : : ToRawMesh ( Mesh , RawMesh , bValidateRawMesh ) ;
FDatasmithMeshSourceModel BaseModel ;
BaseModel . RawMeshBulkData . SaveRawMesh ( RawMesh ) ;
UMesh - > SourceModels . Add ( BaseModel ) ;
for ( int32 LODIndex = 0 ; LODIndex < Mesh . GetLODsCount ( ) ; + + LODIndex )
2020-02-17 13:28:31 -05:00
{
2021-02-03 14:57:28 -04:00
if ( const FDatasmithMesh * LODMesh = Mesh . GetLOD ( LODIndex ) )
{
FDatasmithMeshUtils : : ToRawMesh ( * LODMesh , RawMesh , bValidateRawMesh ) ;
2020-03-18 11:01:39 -04:00
2021-02-03 14:57:28 -04:00
FRawMeshBulkData LODRawMeshBulkData ;
LODRawMeshBulkData . SaveRawMesh ( RawMesh ) ;
2020-03-18 11:01:39 -04:00
2021-02-03 14:57:28 -04:00
FDatasmithMeshSourceModel LODModel ;
LODModel . RawMeshBulkData = LODRawMeshBulkData ;
2020-03-18 11:01:39 -04:00
2021-02-03 14:57:28 -04:00
UMesh - > SourceModels . Add ( LODModel ) ;
}
2020-03-18 11:01:39 -04:00
}
}
TSharedPtr < UDatasmithMesh > FDatasmithMeshExporterImpl : : GetPooledUDatasmithMesh ( bool bIsCollisionMesh )
{
const FString GuidString = UniqueID . ToString ( ) ;
const FString CollisionString = bIsCollisionMesh ? TEXT ( " _Collision " ) : TEXT ( " " ) ;
const FString PooledMeshName = FString : : Printf ( TEXT ( " DatasmithExporter_%s_TransientPooledUDatasmithMesh%s " ) , * GuidString , * CollisionString ) ;
if ( UDatasmithMesh * PooledMesh = DatasmithMeshUObjectPool . Pop ( ) )
{
PooledMesh - > Rename ( * PooledMeshName ) ;
return TSharedPtr < UDatasmithMesh > ( PooledMesh , FPooledUDatasmithMeshDeleter ( this ) ) ;
2020-02-17 13:28:31 -05:00
}
2020-11-24 18:42:39 -04:00
{
2021-02-03 14:57:28 -04:00
// Can't create new objects while the GC is running.
FGCScopeGuard GCGuard ;
2020-11-24 18:42:39 -04:00
return TSharedPtr < UDatasmithMesh > ( NewObject < UDatasmithMesh > ( ( UObject * ) GetTransientPackage ( ) , * PooledMeshName , RF_Transient | RF_MarkAsRootSet ) , FPooledUDatasmithMeshDeleter ( this ) ) ;
}
2020-02-17 13:28:31 -05:00
}
void FDatasmithMeshExporterImpl : : ReturnUDatasmithMeshToPool ( UDatasmithMesh * & UMesh )
{
//Clear the UDatasmithMesh.
UMesh - > SourceModels . Empty ( ) ;
//Put it back into the pool
DatasmithMeshUObjectPool . Push ( UMesh ) ;
//Null the reference to make sure we don't use the object returned to the pool.
UMesh = nullptr ;
}
void FDatasmithMeshExporterImpl : : ClearUDatasmithMeshPool ( )
{
TArray < UDatasmithMesh * > PooledMeshes ;
DatasmithMeshUObjectPool . PopAll ( PooledMeshes ) ;
//Keep track of the number of garbage UObject generated by clearing the cache so that we can trigger the GC after a while.
NumberOfUMeshPendingGC + = PooledMeshes . Num ( ) ;
2021-03-05 19:27:14 -04:00
2021-03-18 15:20:03 -04:00
auto ClearUObjectFlags = [ PooledMeshes ( MoveTemp ( PooledMeshes ) ) ] ( )
2021-03-05 19:27:14 -04:00
{
for ( UDatasmithMesh * UMesh : PooledMeshes )
{
UMesh - > RemoveFromRoot ( ) ;
2021-11-18 14:37:34 -05:00
UMesh - > MarkAsGarbage ( ) ;
2021-03-05 19:27:14 -04:00
//This object won't be reused, we must make sure there is no Async flag so that the GC knows it can safely collect it.
UMesh - > ClearInternalFlags ( EInternalObjectFlags : : Async ) ;
}
} ;
# if IS_PROGRAM
2021-03-18 15:20:03 -04:00
if ( FDatasmithExporterManager : : WasInitializedWithGameThread ( ) )
{
FSimpleDelegate Command ;
Command . BindLambda ( MoveTemp ( ClearUObjectFlags ) ) ;
2021-03-05 19:27:14 -04:00
2021-03-18 15:20:03 -04:00
// No need to synchronize to the game thread for this, we can lazily update the object flags by pushing a command, that way the flags will be updated before the next GC without blocking the current thread.
const bool bWakeUpGameThread = false ;
FDatasmithExporterManager : : PushCommandIntoGameThread ( MoveTemp ( Command ) , bWakeUpGameThread ) ;
}
else
2021-03-05 19:27:14 -04:00
# endif
2021-03-18 15:20:03 -04:00
if ( IsInGameThread ( ) )
{
ClearUObjectFlags ( ) ;
}
else
{
// Can't modify the GC flags while it's running.
FGCScopeGuard GCGuard ;
ClearUObjectFlags ( ) ;
}
2021-03-05 19:27:14 -04:00
//Even if our UObjects are basically empty at this point and don't have a big memory footprint, the engine will assert when reaching around 131k UObjects.
//So we need to call the GC before that point.
if ( NumberOfUMeshPendingGC > DSMeshGarbageCollectionThreshold )
2020-02-17 13:28:31 -05:00
{
2020-11-24 18:42:39 -04:00
if ( FDatasmithExporterManager : : RunGarbageCollection ( ) )
{
NumberOfUMeshPendingGC = 0 ;
}
2020-02-17 13:28:31 -05:00
}
}