void FObjectMemoryAnalyzer::AnalyzeObjects( UClass* InBaseClass ) { if (InBaseClass == NULL) { InBaseClass = UObject::StaticClass(); } uint32 ExclusionFlags = (AnalyzeFlags&EAnalyzeFlags::IncludeDefaultObjects)==0 ? (RF_ClassDefaultObject|RF_ArchetypeObject) : 0; // Determine root objects for( FObjectIterator It(InBaseClass, false, (EObjectFlags)ExclusionFlags); It; ++It ) { UObject* Object = *It; if (!(AnalyzeFlags&EAnalyzeFlags::IncludeDefaultObjects) && Object->IsDefaultSubobject()) { continue; }; FObjectMemoryUsage& Annotation = MemUsageAnnotations.GetAnnotationRef(Object); if ( Object->HasAllFlags(RF_Standalone) ) { Annotation.Flags |= FObjectMemoryUsage::EObjFlags::IsRoot; } ProcessSubObjRecursive(Object, Object); } // mark 'loose' objets as root objects as well for( FObjectIterator It(InBaseClass, false, (EObjectFlags)ExclusionFlags); It; ++It ) { UObject* Object = *It; if (!(AnalyzeFlags&EAnalyzeFlags::IncludeDefaultObjects) && Object->IsDefaultSubobject()) { continue; }; FObjectMemoryUsage& Annotation = MemUsageAnnotations.GetAnnotationRef(Object); if (!Annotation.IsRoot() && !Annotation.IsReferencedByRoot() && !Annotation.IsReferencedByNonRoot()) { Annotation.Flags |= FObjectMemoryUsage::EObjFlags::IsRoot; } } for( FObjectIterator It(InBaseClass, false, (EObjectFlags)ExclusionFlags); It; ++It ) { UObject* Object = *It; if (!(AnalyzeFlags&EAnalyzeFlags::IncludeDefaultObjects) && Object->IsDefaultSubobject()) { continue; }; FObjectMemoryUsage& Annotation = MemUsageAnnotations.GetAnnotationRef(Object); SIZE_T InclusiveSize = CalculateSizeRecursive(Object); Annotation.InclusiveMemoryUsage = InclusiveSize; } }
int32 FObjectMemoryAnalyzer::GetResults(TArray<FObjectMemoryUsage>& Results) { if (BaseClass != NULL) { uint32 ExclusionFlags = (AnalyzeFlags&EAnalyzeFlags::IncludeDefaultObjects)==0 ? (RF_ClassDefaultObject|RF_ArchetypeObject) : 0; for( FObjectIterator It(BaseClass, false, (EObjectFlags)ExclusionFlags); It; ++It ) { UObject* Object = *It; if (!(AnalyzeFlags&EAnalyzeFlags::IncludeDefaultObjects) && Object->IsDefaultSubobject()) { continue; }; FObjectMemoryUsage& Annotation = MemUsageAnnotations.GetAnnotationRef(Object); if (Annotation.IsRoot()) { Annotation.Object = Object; Results.Add(Annotation); } } } if (ObjectList.Num() > 0) { for (int32 i=0; i < ObjectList.Num(); ++i) { FObjectMemoryUsage& Annotation = MemUsageAnnotations.GetAnnotationRef(ObjectList[i]); check(Annotation.IsRoot()); Annotation.Object = ObjectList[i]; Results.Add(Annotation); } } return Results.Num(); }
void UObjectPropertyBase::ExportTextItem( FString& ValueStr, const void* PropertyValue, const void* DefaultValue, UObject* Parent, int32 PortFlags, UObject* ExportRootScope ) const { UObject* Temp = GetObjectPropertyValue(PropertyValue); if( Temp != NULL ) { if (PortFlags & PPF_DebugDump) { ValueStr += Temp->GetFullName(); } else if (Parent && !Parent->HasAnyFlags(RF_ClassDefaultObject) && Temp->IsDefaultSubobject()) { if ((PortFlags & PPF_Delimited) && (!Temp->GetFName().IsValidXName(INVALID_OBJECTNAME_CHARACTERS))) { ValueStr += FString::Printf(TEXT("\"%s\""), *Temp->GetName().ReplaceQuotesWithEscapedQuotes()); } else { ValueStr += Temp->GetName(); } } else { ValueStr += GetExportPath(Temp, Parent, ExportRootScope, PortFlags); } } else { ValueStr += TEXT("None"); } }
bool UObjectPropertyBase::Identical( const void* A, const void* B, uint32 PortFlags ) const { UObject* ObjectA = A ? GetObjectPropertyValue(A) : NULL; UObject* ObjectB = B ? GetObjectPropertyValue(B) : NULL; if (!ObjectA && !ObjectB) { return true; } if (!ObjectA || !ObjectB) { return false; } // Compare actual pointers. We don't do this during PIE because we want to be sure to serialize everything. An example is the LevelScriptActor being serialized against its CDO, // which contains actor references. We want to serialize those references so they are fixed up. const bool bDuplicatingForPIE = (PortFlags&PPF_DuplicateForPIE) != 0; bool bResult = !bDuplicatingForPIE ? (ObjectA == ObjectB) : false; // always serialize the cross level references, because they could be NULL // @todo: okay, this is pretty hacky overall - we should have a PortFlag or something // that is set during SavePackage. Other times, we don't want to immediately return false // (instead of just this ExportDefProps case) // instance testing if (!bResult && ObjectA->GetClass() == ObjectB->GetClass()) { bool bPerformDeepComparison = (PortFlags&PPF_DeepComparison) != 0; if ((PortFlags&PPF_DeepCompareInstances) && !bPerformDeepComparison) { bPerformDeepComparison = ObjectA->IsTemplate() != ObjectB->IsTemplate(); } if (!bResult && bPerformDeepComparison) { // In order for deep comparison to be match they both need to have the same name and that name needs to be included in the instancing table for the class if (ObjectA->GetFName() == ObjectB->GetFName() && ObjectA->GetClass()->GetDefaultSubobjectByName(ObjectA->GetFName())) { checkSlow(ObjectA->IsDefaultSubobject() && ObjectB->IsDefaultSubobject() && ObjectA->GetClass()->GetDefaultSubobjectByName(ObjectA->GetFName()) == ObjectB->GetClass()->GetDefaultSubobjectByName(ObjectB->GetFName())); // equivalent bResult = AreInstancedObjectsIdentical(ObjectA,ObjectB,PortFlags); } } } return bResult; }
void UObjectPropertyBase::ExportTextItem( FString& ValueStr, const void* PropertyValue, const void* DefaultValue, UObject* Parent, int32 PortFlags, UObject* ExportRootScope ) const { UObject* Temp = GetObjectPropertyValue(PropertyValue); if (0 != (PortFlags & PPF_ExportCpp)) { FString::Printf(TEXT("%s%s*"), PropertyClass->GetPrefixCPP(), *PropertyClass->GetName()); ValueStr += Temp ? FString::Printf(TEXT("LoadObject<%s%s>(nullptr, TEXT(\"%s\"))") , PropertyClass->GetPrefixCPP() , *PropertyClass->GetName() , *(Temp->GetPathName().ReplaceCharWithEscapedChar())) : TEXT("nullptr"); return; } if( Temp != NULL ) { if (PortFlags & PPF_DebugDump) { ValueStr += Temp->GetFullName(); } else if (Parent && !Parent->HasAnyFlags(RF_ClassDefaultObject) && Temp->IsDefaultSubobject()) { if ((PortFlags & PPF_Delimited) && (!Temp->GetFName().IsValidXName(INVALID_OBJECTNAME_CHARACTERS))) { ValueStr += FString::Printf(TEXT("\"%s\""), *Temp->GetName().ReplaceQuotesWithEscapedQuotes()); } else { ValueStr += Temp->GetName(); } } else { ValueStr += GetExportPath(Temp, Parent, ExportRootScope, PortFlags); } } else { ValueStr += TEXT("None"); } }
void UExporter::ExportObjectInner(const FExportObjectInnerContext* Context, UObject* Object, FOutputDevice& Ar, uint32 PortFlags, bool bSkipComponents) { // indent all the text in here TextIndent += 3; FExportObjectInnerContext::InnerList ObjectInners; if ( Context ) { const FExportObjectInnerContext::InnerList* Inners = Context->ObjectToInnerMap.Find( Object ); if ( Inners ) { ObjectInners = *Inners; } } else { for (TObjectIterator<UObject> It; It; ++It) { if ( It->GetOuter() == Object ) { ObjectInners.Add( *It ); } } } TArray<UObject*> Components; if (!bSkipComponents) { // first export the components Object->CollectDefaultSubobjects(Components, false); } if (!(PortFlags & PPF_SeparateDefine)) { for ( int32 ObjIndex = 0 ; ObjIndex < ObjectInners.Num() ; ++ObjIndex ) { // NOTE: We ignore inner objects that have been tagged for death UObject* Obj = ObjectInners[ObjIndex]; if ( !Obj->IsPendingKill() && !Obj->IsDefaultSubobject() && !Obj->HasAnyFlags(RF_TextExportTransient) && FCString::Stricmp(*Obj->GetClass()->GetName(), TEXT("Model")) != 0) { // export the object UExporter::ExportToOutputDevice( Context, Obj, NULL, Ar, (PortFlags & PPF_Copy) ? TEXT("Copy") : TEXT("T3D"), TextIndent, PortFlags | PPF_SeparateDeclare, false, ExportRootScope ); } } if (!bSkipComponents) { ExportComponentDefinitions(Context, Components, Ar, PortFlags | PPF_SeparateDeclare); } } if (!(PortFlags & PPF_SeparateDeclare)) { for ( int32 ObjIndex = 0 ; ObjIndex < ObjectInners.Num() ; ++ObjIndex ) { // NOTE: We ignore inner objects that have been tagged for death UObject* Obj = ObjectInners[ObjIndex]; if ( !Obj->IsPendingKill() && !Obj->IsDefaultSubobject() && !Obj->HasAnyFlags(RF_TextExportTransient) && FCString::Stricmp(*Obj->GetClass()->GetName(), TEXT("Model")) != 0) { // export the object UExporter::ExportToOutputDevice( Context, Obj, NULL, Ar, (PortFlags & PPF_Copy) ? TEXT("Copy") : TEXT("T3D"), TextIndent, PortFlags | PPF_SeparateDefine, false, ExportRootScope ); // don't reexport below in ExportProperties Obj->Mark(OBJECTMARK_TagImp); } } if (!bSkipComponents) { ExportComponentDefinitions(Context, Components, Ar, PortFlags | PPF_SeparateDefine); } // export the object's properties // Note: we use archetype as the object to diff properties against before they exported. When object is created, they should create from archetype // and using this system, it should recover all properties it needs to copy uint8 *CompareObject; if (Object->HasAnyFlags(RF_ClassDefaultObject)) { CompareObject = (uint8*)Object; } else { CompareObject = (uint8*)Object->GetArchetype(); } ExportProperties( Context, Ar, Object->GetClass(), (uint8*)Object, TextIndent, Object->GetClass(), CompareObject, Object, PortFlags, ExportRootScope ); if (!bSkipComponents) { // Export anything extra for the components. Used for instanced foliage. // This is done after the actor properties so these are set when regenerating the extra data objects. ExportComponentExtra( Context, Components, Ar, PortFlags ); } } // remove indent TextIndent -= 3; }