diff --git a/Engine/Build/Commit.gitdeps.xml b/Engine/Build/Commit.gitdeps.xml index 201dd7c67b87..a487aaa017de 100644 --- a/Engine/Build/Commit.gitdeps.xml +++ b/Engine/Build/Commit.gitdeps.xml @@ -17053,8 +17053,8 @@ - - + + @@ -17304,9 +17304,9 @@ - - - + + + @@ -17331,7 +17331,7 @@ - + @@ -17387,7 +17387,7 @@ - + @@ -24967,7 +24967,7 @@ - + @@ -25077,7 +25077,7 @@ - + @@ -25119,7 +25119,7 @@ - + @@ -25308,7 +25308,7 @@ - + @@ -25411,7 +25411,7 @@ - + @@ -25519,7 +25519,7 @@ - + @@ -25558,7 +25558,7 @@ - + @@ -25868,7 +25868,6 @@ - @@ -26116,7 +26115,7 @@ - + @@ -26212,7 +26211,7 @@ - + @@ -26364,7 +26363,7 @@ - + @@ -26393,7 +26392,7 @@ - + @@ -26875,7 +26874,7 @@ - + @@ -26983,7 +26982,6 @@ - @@ -27328,7 +27326,7 @@ - + @@ -27346,7 +27344,7 @@ - + @@ -27402,7 +27400,7 @@ - + @@ -27439,7 +27437,7 @@ - + @@ -27550,7 +27548,7 @@ - + @@ -27707,7 +27705,7 @@ - + @@ -27811,7 +27809,7 @@ - + @@ -28111,7 +28109,6 @@ - @@ -28166,7 +28163,7 @@ - + @@ -28190,7 +28187,7 @@ - + @@ -28936,7 +28933,7 @@ - + @@ -29034,7 +29031,7 @@ - + @@ -29228,7 +29225,7 @@ - + @@ -29668,7 +29665,7 @@ - + @@ -29960,7 +29957,6 @@ - @@ -30022,7 +30018,7 @@ - + @@ -30168,6 +30164,7 @@ + @@ -30233,7 +30230,7 @@ - + @@ -30353,7 +30350,7 @@ - + @@ -30362,7 +30359,7 @@ - + @@ -30499,7 +30496,7 @@ - + @@ -30860,7 +30857,7 @@ - + @@ -31164,6 +31161,7 @@ + @@ -31563,7 +31561,7 @@ - + @@ -31614,7 +31612,7 @@ - + @@ -31631,7 +31629,7 @@ - + @@ -31727,9 +31725,9 @@ - + - + @@ -32072,7 +32070,7 @@ - + @@ -32092,7 +32090,7 @@ - + @@ -32145,7 +32143,7 @@ - + @@ -32229,7 +32227,7 @@ - + @@ -32325,6 +32323,7 @@ + @@ -32461,7 +32460,7 @@ - + @@ -32716,7 +32715,7 @@ - + @@ -32843,7 +32842,7 @@ - + @@ -32873,7 +32872,7 @@ - + @@ -32925,7 +32924,7 @@ - + @@ -33515,7 +33514,7 @@ - + @@ -33543,7 +33542,7 @@ - + @@ -33636,7 +33635,7 @@ - + @@ -34171,7 +34170,7 @@ - + @@ -34218,7 +34217,7 @@ - + @@ -34357,7 +34356,7 @@ - + @@ -34513,7 +34512,7 @@ - + @@ -35736,7 +35735,7 @@ - + @@ -35963,7 +35962,7 @@ - + @@ -35982,7 +35981,7 @@ - + @@ -36017,7 +36016,7 @@ - + @@ -36164,7 +36163,7 @@ - + @@ -36361,6 +36360,7 @@ + @@ -36497,7 +36497,7 @@ - + @@ -36646,7 +36646,6 @@ - @@ -37173,7 +37172,7 @@ - + @@ -37648,7 +37647,7 @@ - + @@ -38153,6 +38152,7 @@ + @@ -38181,7 +38181,7 @@ - + @@ -38240,7 +38240,7 @@ - + @@ -38282,7 +38282,7 @@ - + @@ -38331,6 +38331,7 @@ + @@ -38560,7 +38561,7 @@ - + @@ -38611,7 +38612,7 @@ - + @@ -39082,7 +39083,7 @@ - + @@ -39192,7 +39193,7 @@ - + @@ -39201,7 +39202,7 @@ - + @@ -39285,7 +39286,7 @@ - + @@ -39577,7 +39578,7 @@ - + @@ -39598,7 +39599,7 @@ - + @@ -39655,7 +39656,7 @@ - + @@ -39964,6 +39965,7 @@ + @@ -40003,7 +40005,7 @@ - + @@ -40293,7 +40295,7 @@ - + @@ -40838,7 +40840,7 @@ - + @@ -41466,7 +41468,7 @@ - + @@ -41612,7 +41614,7 @@ - + @@ -41687,7 +41689,7 @@ - + @@ -41748,7 +41750,7 @@ - + @@ -42010,7 +42012,7 @@ - + @@ -42213,7 +42215,6 @@ - @@ -42329,7 +42330,6 @@ - @@ -42444,7 +42444,7 @@ - + @@ -42946,7 +42946,6 @@ - @@ -44125,7 +44124,6 @@ - @@ -44218,7 +44216,6 @@ - @@ -44310,6 +44307,7 @@ + diff --git a/Engine/Config/BaseEngine.ini b/Engine/Config/BaseEngine.ini index 4f330076ff0b..b07f8bc9ef6d 100644 --- a/Engine/Config/BaseEngine.ini +++ b/Engine/Config/BaseEngine.ini @@ -1486,6 +1486,7 @@ gc.FlushStreamingOnGC=0 gc.NumRetriesBeforeForcingGC=0 gc.AllowParallelGC=True gc.TimeBetweenPurgingPendingKillObjects=60 +gc.MaxObjectsInEditor=16777216 [Internationalization] +LocalizationPaths=../../../Engine/Content/Localization/Engine diff --git a/Engine/Plugins/Developer/GameplayDebugger/Source/GameplayDebuggerModule/Private/GameplayDebuggerModuleSettings.cpp b/Engine/Plugins/Developer/GameplayDebugger/Source/GameplayDebuggerModule/Private/GameplayDebuggerModuleSettings.cpp index bed04d96366a..32bf093696a5 100644 --- a/Engine/Plugins/Developer/GameplayDebugger/Source/GameplayDebuggerModule/Private/GameplayDebuggerModuleSettings.cpp +++ b/Engine/Plugins/Developer/GameplayDebugger/Source/GameplayDebuggerModule/Private/GameplayDebuggerModuleSettings.cpp @@ -165,7 +165,7 @@ void UGameplayDebuggerModuleSettings::RegisterClass(class UGameplayDebuggerBaseO void UGameplayDebuggerModuleSettings::RegisterClass(UClass* InClass) { #if ENABLED_GAMEPLAY_DEBUGGER - if (!InClass || InClass->HasAnyClassFlags(CLASS_Abstract) || InClass->HasAnyFlags(RF_Unreachable)) + if (!InClass || InClass->HasAnyClassFlags(CLASS_Abstract) || InClass->IsUnreachable()) { return; } @@ -203,7 +203,7 @@ void UGameplayDebuggerModuleSettings::UnregisterClass(class UGameplayDebuggerBas { #if ENABLED_GAMEPLAY_DEBUGGER UClass *InClass = BaseObject ? BaseObject->GetClass() : nullptr; - const bool bIsValidClass = !(!InClass || InClass->HasAnyClassFlags(CLASS_Abstract) || InClass->HasAnyFlags(RF_Unreachable)); + const bool bIsValidClass = !(!InClass || InClass->HasAnyClassFlags(CLASS_Abstract) || InClass->IsUnreachable()); if (bIsValidClass && ManualClasses.Find(InClass) != INDEX_NONE && InClass->GetName().Find(TEXT("REINST_")) == INDEX_NONE && InClass->GetName().Find(TEXT("SKEL_")) == INDEX_NONE) { @@ -288,7 +288,7 @@ void UGameplayDebuggerModuleSettings::UpdateCategories(bool bUpdateConfig) /** add all new categories and classes */ for (UClass* Class : Results) { - if (Class == nullptr || Class->HasAnyClassFlags(CLASS_Abstract) || Class->HasAnyFlags(RF_Unreachable)) + if (Class == nullptr || Class->HasAnyClassFlags(CLASS_Abstract) || Class->IsUnreachable()) { continue; } diff --git a/Engine/Plugins/Experimental/LiveEditor/Source/LiveEditor/Private/LiveEditorManager.cpp b/Engine/Plugins/Experimental/LiveEditor/Source/LiveEditor/Private/LiveEditorManager.cpp index 74b07ee15e3c..f7613d96f4d9 100644 --- a/Engine/Plugins/Experimental/LiveEditor/Source/LiveEditor/Private/LiveEditorManager.cpp +++ b/Engine/Plugins/Experimental/LiveEditor/Source/LiveEditor/Private/LiveEditorManager.cpp @@ -524,7 +524,8 @@ bool FLiveEditorManager::Activate( const FString &Name ) FActiveBlueprintRecord Record; Record.Name = Name; - auto Instance = NewObject(GetTransientPackage(), Blueprint->GeneratedClass, NAME_None, RF_Transient | RF_Public | RF_RootSet | RF_Standalone); + auto Instance = NewObject(GetTransientPackage(), Blueprint->GeneratedClass, NAME_None, RF_Transient | RF_Public | RF_Standalone); + Instance->AddToRoot(); Instance->DoInit(); Record.Blueprint = Instance; diff --git a/Engine/Plugins/NetcodeUnitTest/NetcodeUnitTest/Source/NetcodeUnitTest/Private/UnitTestManager.cpp b/Engine/Plugins/NetcodeUnitTest/NetcodeUnitTest/Source/NetcodeUnitTest/Private/UnitTestManager.cpp index cca417e45450..65435a39d58b 100644 --- a/Engine/Plugins/NetcodeUnitTest/NetcodeUnitTest/Source/NetcodeUnitTest/Private/UnitTestManager.cpp +++ b/Engine/Plugins/NetcodeUnitTest/NetcodeUnitTest/Source/NetcodeUnitTest/Private/UnitTestManager.cpp @@ -110,7 +110,7 @@ void UUnitTestManager::Initialize() } // Add this object to the root set, to disable garbage collection until desired (it is not referenced by any UProperties) - SetFlags(RF_RootSet); + AddToRoot(); // Add a log hook if (!GLog->IsRedirectingTo(this)) diff --git a/Engine/Plugins/Runtime/Steam/SteamVR/Source/SteamVR/Private/SteamVRHMD.cpp b/Engine/Plugins/Runtime/Steam/SteamVR/Source/SteamVR/Private/SteamVRHMD.cpp index f8ecc40eac4b..d67f291cf75d 100644 --- a/Engine/Plugins/Runtime/Steam/SteamVR/Source/SteamVR/Private/SteamVRHMD.cpp +++ b/Engine/Plugins/Runtime/Steam/SteamVR/Source/SteamVR/Private/SteamVRHMD.cpp @@ -413,7 +413,7 @@ bool FSteamVRHMD::GetTrackedObjectOrientationAndPosition(uint32 DeviceId, FQuat& { bool bHasValidPose = false; - if (DeviceId >= 0 && DeviceId < vr::k_unMaxTrackedDeviceCount) + if (DeviceId < vr::k_unMaxTrackedDeviceCount) { CurrentOrientation = TrackingFrame.DeviceOrientation[DeviceId]; CurrentPosition = TrackingFrame.DevicePosition[DeviceId]; diff --git a/Engine/Source/Developer/BlueprintCompilerCppBackend/Private/BlueprintCompilerCppBackendUtils.cpp b/Engine/Source/Developer/BlueprintCompilerCppBackend/Private/BlueprintCompilerCppBackendUtils.cpp index 1902b5228b6d..502843c33b2d 100644 --- a/Engine/Source/Developer/BlueprintCompilerCppBackend/Private/BlueprintCompilerCppBackendUtils.cpp +++ b/Engine/Source/Developer/BlueprintCompilerCppBackend/Private/BlueprintCompilerCppBackendUtils.cpp @@ -262,7 +262,7 @@ FString FEmitHelper::GetCppName(const UField* Field, bool bUInterface) , *AsClass->GetName()); } auto AsStruct = CastChecked(Field); - if (AsStruct->HasAnyFlags(RF_Native)) + if (AsStruct->IsNative()) { return FString::Printf(TEXT("%s%s"), AsStruct->GetPrefixCPP(), *AsStruct->GetName()); } @@ -276,7 +276,7 @@ FString FEmitHelper::GetCppName(const UField* Field, bool bUInterface) if (const UStruct* Owner = AsProperty->GetOwnerStruct()) { if ((Cast(Owner) || - !Owner->HasAnyFlags(RF_Native))) + !Owner->IsNative())) { return ::UnicodeToCPPIdentifier(AsProperty->GetName(), AsProperty->HasAnyPropertyFlags(CPF_Deprecated), TEXT("bpv__")); } @@ -284,7 +284,7 @@ FString FEmitHelper::GetCppName(const UField* Field, bool bUInterface) return AsProperty->GetNameCPP(); } - if (!Field->HasAnyFlags(RF_Native) && !Field->IsA()) + if (!Field->IsNative() && !Field->IsA()) { return ::UnicodeToCPPIdentifier(Field->GetName(), false, TEXT("bpf__")); } diff --git a/Engine/Source/Developer/CrashDebugHelper/Private/Windows/WindowsPlatformStackWalkExt.cpp b/Engine/Source/Developer/CrashDebugHelper/Private/Windows/WindowsPlatformStackWalkExt.cpp index 5b606fc0f4bf..4c5ac94da9dd 100644 --- a/Engine/Source/Developer/CrashDebugHelper/Private/Windows/WindowsPlatformStackWalkExt.cpp +++ b/Engine/Source/Developer/CrashDebugHelper/Private/Windows/WindowsPlatformStackWalkExt.cpp @@ -168,6 +168,15 @@ void FWindowsPlatformStackWalkExt::SetSymbolPathsFromModules() const bool bUseCachedData = CrashInfo.PDBCacheEntry.IsValid(); FString CombinedPath = TEXT( "" ); + // Use symbol cache from command line + FString DebugSymbols; + if (FParse::Value(FCommandLine::Get(), TEXT("DebugSymbols="), DebugSymbols)) + { + CombinedPath += TEXT("SRV*"); + CombinedPath += DebugSymbols; + CombinedPath += TEXT(";"); + } + // For externally launched minidump diagnostics. if( bUseCachedData ) { diff --git a/Engine/Source/Developer/HotReload/Private/HotReload.cpp b/Engine/Source/Developer/HotReload/Private/HotReload.cpp index e0793856698f..175a5c97414d 100644 --- a/Engine/Source/Developer/HotReload/Private/HotReload.cpp +++ b/Engine/Source/Developer/HotReload/Private/HotReload.cpp @@ -160,13 +160,6 @@ private: */ void ReplaceReferencesToReconstructedCDOs(); -#if WITH_ENGINE - /** - * Recompiles blueprints that were touched during this hot-reload. - */ - void RecompileTouchedBlueprints(); -#endif // WITH_ENGINE - /** * Callback for async ompilation */ @@ -372,6 +365,9 @@ private: /** Reconstructed CDOs map during hot-reload. */ TMap ReconstructedCDOsMap; + + /** Keeps record of hot-reload session starting time. */ + double HotReloadStartTime; }; namespace HotReloadDefs @@ -820,7 +816,7 @@ ECompilationResult::Type FHotReloadModule::DoHotReloadInternal(bool bRecompileFi TArray ScriptStructs; for (FRawObjectIterator It; It; ++It) { - if (UFunction* Function = Cast(*It)) + if (UFunction* Function = Cast(static_cast(It->Object))) { if (Native NewFunction = HotReloadFunctionRemap.FindRef(Function->GetNativeFunc())) { @@ -829,7 +825,7 @@ ECompilationResult::Type FHotReloadModule::DoHotReloadInternal(bool bRecompileFi } } - if (UScriptStruct* ScriptStruct = Cast(*It)) + if (UScriptStruct* ScriptStruct = Cast(static_cast(It->Object))) { if (Packages.ContainsByPredicate([=](UPackage* Package) { return ScriptStruct->IsIn(Package); }) && ScriptStruct->GetCppStructOps()) { @@ -849,10 +845,6 @@ ECompilationResult::Type FHotReloadModule::DoHotReloadInternal(bool bRecompileFi } HotReloadAr.Logf(ELogVerbosity::Warning, TEXT("HotReload successful (%d functions remapped %d scriptstructs remapped)"), Count, ScriptStructs.Num()); -#if WITH_ENGINE - RecompileTouchedBlueprints(); -#endif // WITH_ENGINE - HotReloadFunctionRemap.Empty(); ReplaceReferencesToReconstructedCDOs(); @@ -863,6 +855,8 @@ ECompilationResult::Type FHotReloadModule::DoHotReloadInternal(bool bRecompileFi const bool bWasTriggeredAutomatically = !bIsHotReloadingFromEditor; BroadcastHotReload( bWasTriggeredAutomatically ); + + HotReloadAr.Logf(ELogVerbosity::Warning, TEXT("HotReload took %4.1fs."), FPlatformTime::Seconds() - HotReloadStartTime); } else if (ECompilationResult::Failed(CompilationResult) && bRecompileFinished) { @@ -886,82 +880,155 @@ void FHotReloadModule::ReplaceReferencesToReconstructedCDOs() return; } + // Thread pool manager. We need new thread pool with increased + // amount of stack size. Standard GThreadPool was encountering + // stack overflow error during serialization. + static struct FReplaceReferencesThreadPool + { + FReplaceReferencesThreadPool() + { + Pool = FQueuedThreadPool::Allocate(); + int32 NumThreadsInThreadPool = FPlatformMisc::NumberOfWorkerThreadsToSpawn(); + verify(Pool->Create(NumThreadsInThreadPool, 256 * 1024)); + } + + ~FReplaceReferencesThreadPool() + { + Pool->Destroy(); + } + + FQueuedThreadPool* GetPool() { return Pool; } + + private: + FQueuedThreadPool* Pool; + } ThreadPoolManager; + TArray OldCDOs; ReconstructedCDOsMap.GetKeys(OldCDOs); + // Structure to store CDOs reference info. + struct FReferenceInfo + { + UObject* Referencer; + UObject* Referencee; + UProperty* ReferencingProperty; + + FReferenceInfo(UObject* InReferencer, UObject* InReferencee, UProperty* InReferencingProperty) + : Referencer(InReferencer), Referencee(InReferencee), ReferencingProperty(InReferencingProperty) + {} + }; + + // Async task to enable multithreaded CDOs reference search. + class FFindRefTask : public FNonAbandonableTask + { + public: + FFindRefTask(const TArray* InReferencees, int32 ReserveElements = 0) + : Referencees(*InReferencees) + { + ObjectsArray.Reserve(ReserveElements); + } + + void DoWork() + { + for (UObject* Object : ObjectsArray) + { + FFindReferencersArchive FindRefsArchive(Object, Referencees); + + TMap ReferenceCounts; + TMultiMap ReferencingProperties; + + FindRefsArchive.GetReferenceCounts(ReferenceCounts, ReferencingProperties); + + for (auto& ReferencingProperty : ReferencingProperties) + { + static const UProperty* PropertyToSkip = +#if WITH_ENGINE + UBlueprintGeneratedClass::StaticClass()->FindPropertyByName(GET_MEMBER_NAME_CHECKED(UBlueprintGeneratedClass, OverridenArchetypeForCDO)); +#else + nullptr; +#endif + if (!ReferencingProperty.Value->IsA() || ReferencingProperty.Value == PropertyToSkip) + { + continue; + } + + References.Emplace(Object, ReferencingProperty.Key, ReferencingProperty.Value); + } + } + } + + TArray& GetObjectsArray() + { + return ObjectsArray; + } + + FORCEINLINE TStatId GetStatId() const + { + RETURN_QUICK_DECLARE_CYCLE_STAT(FFindRefTask, STATGROUP_ThreadPoolAsyncTasks); + } + + const TArray& GetReferences() const { return References; } + + private: + const TArray& Referencees; + TArray ObjectsArray; + TArray References; + }; + + const int32 NumberOfThreads = FPlatformMisc::NumberOfWorkerThreadsToSpawn(); + const int32 NumObjects = GUObjectArray.GetObjectArrayNum(); + const int32 ObjectsPerTask = FMath::CeilToInt((float)NumObjects / NumberOfThreads); + + // Create tasks. + TArray> Tasks; + Tasks.Reserve(NumberOfThreads); + + for (int32 TaskId = 0; TaskId < NumberOfThreads; ++TaskId) + { + Tasks.Emplace(&OldCDOs, ObjectsPerTask); + } + + // Distribute objects uniformly between tasks. + int32 CurrentTaskId = 0; for (FObjectIterator ObjIter; ObjIter; ++ObjIter) { UObject* CurObject = *ObjIter; - FFindReferencersArchive FindRefsArchive(CurObject, OldCDOs); - - TMap ReferenceCounts; - TMultiMap ReferencingProperties; - - FindRefsArchive.GetReferenceCounts(ReferenceCounts, ReferencingProperties); - - for (auto& ReferencingProperty : ReferencingProperties) + if (CurObject->IsPendingKill()) { - static const UProperty* PropertyToSkip = -#if WITH_ENGINE - UBlueprintGeneratedClass::StaticClass()->FindPropertyByName(GET_MEMBER_NAME_CHECKED(UBlueprintGeneratedClass, OverridenArchetypeForCDO)); -#else - nullptr; -#endif - if (!ReferencingProperty.Value->IsA() || ReferencingProperty.Value == PropertyToSkip) - { - continue; - } + continue; + } - UObject* OldCDO = ReferencingProperty.Key; - UObjectProperty* Prop = (UObjectProperty*)ReferencingProperty.Value; + Tasks[CurrentTaskId].GetTask().GetObjectsArray().Add(CurObject); + CurrentTaskId = (CurrentTaskId + 1) % NumberOfThreads; + } - Prop->SetObjectPropertyValue((uint8*)CurObject + Prop->GetOffset_ForInternal(), ReconstructedCDOsMap[OldCDO]); + // Run async tasks in worker threads. + for (int32 TaskId = 0; TaskId < NumberOfThreads; ++TaskId) + { + Tasks[TaskId].StartBackgroundTask(ThreadPoolManager.GetPool()); + } + + // Wait until tasks are finished and replace found references + // in main thread. + for (int32 TaskId = 0; TaskId < NumberOfThreads; ++TaskId) + { + Tasks[TaskId].EnsureCompletion(); + + const TArray& References = Tasks[TaskId].GetTask().GetReferences(); + + for (const FReferenceInfo& Reference : References) + { + UObject* OldCDO = Reference.Referencee; + UObjectProperty* Prop = (UObjectProperty*)Reference.ReferencingProperty; + + Prop->SetObjectPropertyValue((uint8*)Reference.Referencer + Prop->GetOffset_ForInternal(), ReconstructedCDOsMap[OldCDO]); } } ReconstructedCDOsMap.Empty(); } -#if WITH_ENGINE -void FHotReloadModule::RecompileTouchedBlueprints() -{ - bool bCollectGarbage = HotReloadBPSetToRecompile.Num() > 0; - - while (HotReloadBPSetToRecompile.Num()) - { - auto Iter = HotReloadBPSetToRecompile.CreateIterator(); - TWeakObjectPtr BPPtr = *Iter; - Iter.RemoveCurrent(); - if (auto BP = BPPtr.Get()) - { - FKismetEditorUtilities::CompileBlueprint(BP, false, true); - } - } - - if (bCollectGarbage) - { - // Garbage collect to make sure the old class and actors are disposed of - CollectGarbage(GARBAGE_COLLECTION_KEEPFLAGS); - } - - HotReloadBPSetToRecompile.Empty(); - - while (HotReloadBPSetToRecompileBytecodeOnly.Num()) - { - auto Iter = HotReloadBPSetToRecompileBytecodeOnly.CreateIterator(); - TWeakObjectPtr BPPtr = *Iter; - Iter.RemoveCurrent(); - if (auto BP = BPPtr.Get()) - { - FKismetEditorUtilities::RecompileBlueprintBytecode(BP); - } - } - - HotReloadBPSetToRecompileBytecodeOnly.Empty(); -} -#endif // WITH_ENGINE - ECompilationResult::Type FHotReloadModule::RebindPackages(TArray InPackages, TArray DependentModules, const bool bWaitForCompletion, FOutputDevice &Ar) { ECompilationResult::Type Result = ECompilationResult::Unknown; @@ -1010,7 +1077,7 @@ ECompilationResult::Type FHotReloadModule::RebindPackagesInternal(TArray ModuleNames; for (UPackage* Package : InPackages) @@ -1034,12 +1101,12 @@ ECompilationResult::Type FHotReloadModule::RebindPackagesInternal(TArrayClassDefaultObject = StaticAllocateObject(InClass, InOuter, InName, InFlags, false); + InClass->ClassDefaultObject = StaticAllocateObject(InClass, InOuter, InName, InFlags, EInternalObjectFlags::None, false); check(InClass->ClassDefaultObject); const bool bShouldInitilizeProperties = false; const bool bCopyTransientsFromClassDefaults = false; diff --git a/Engine/Source/Developer/Profiler/Private/ProfilerRawStats.cpp b/Engine/Source/Developer/Profiler/Private/ProfilerRawStats.cpp index afb5410b83c6..5245d0e38de1 100644 --- a/Engine/Source/Developer/Profiler/Private/ProfilerRawStats.cpp +++ b/Engine/Source/Developer/Profiler/Private/ProfilerRawStats.cpp @@ -442,7 +442,7 @@ void FRawStatsMemoryProfiler::UpdateGenerateMemoryMapProgress( const int32 Alloc void FRawStatsMemoryProfiler::ProcessSpecialMessageMarkerOperation( const FStatMessage& Message, const FStackState& StackState ) { const FName RawName = Message.NameAndInfo.GetRawName(); - if (RawName == FStatConstants::NAME_NamedMarker) + if (RawName == FStatConstants::RAW_NamedMarker) { const FName NamedMarker = Message.GetValue_FName(); Snapshots.Add( TPairInitializer( LastSequenceTagForNamedMarker, NamedMarker ) ); diff --git a/Engine/Source/Developer/Profiler/Public/ProfilerCommon.h b/Engine/Source/Developer/Profiler/Public/ProfilerCommon.h index 34ae50bd981b..f47f387aa406 100644 --- a/Engine/Source/Developer/Profiler/Public/ProfilerCommon.h +++ b/Engine/Source/Developer/Profiler/Public/ProfilerCommon.h @@ -120,38 +120,58 @@ public: class FBinaryFindIndex { public: + /** + * Executes a binary search for element Item in array Array using the <= operator + * (i.e. uses the comparison Array[i] <= Item). Assumes that Array is pre-sorted. + * + * @param Array The Array to search + * @param Item The item to search for + * @param FirstIndex Optional. Function will not search before this index. Default is 0 + * @param LastIndex Optional. Function will not search beyond this index. Default is INDEX_NONE + * @return Returns the last index of the element that is smaller than or equal to Item, or, if Item is not found, returns 0. + */ template static int32 LessEqual( const TArray& Array, const T& Item, const int32 FirstIndex = 0, const int32 LastIndex = INDEX_NONE ) { const int32 LocalLastIndex = LastIndex == INDEX_NONE ? Array.Num() : LastIndex; - int32 Lenght = LocalLastIndex - FirstIndex; - int32 Middle = Lenght / 2; + int32 Length = LocalLastIndex - FirstIndex; + int32 Middle = Length; int32 Offset = FirstIndex; while( Middle > 0 ) { - Middle = Lenght / 2; + Middle = Length / 2; if( Array[Offset + Middle] <= Item ) { Offset += Middle; } - Lenght -= Middle; + Length -= Middle; } return Offset; } + /** + * Executes a binary search for element Item in array Array using the >= operator + * (i.e. uses the comparison Array[i] >= Item). Assumes that Array is pre-sorted. + * + * @param Array The Array to search + * @param Item The item to search for + * @param FirstIndex Optional. Function will not search before this index. Default is 0 + * @param LastIndex Optional. Function will not search beyond this index. Default is INDEX_NONE + * @return Returns the first index of the element that is greater than or equal to Item, or, if Item is not found, returns the last index + 1. + */ template static int32 GreaterEqual( const TArray& Array, const T& Item, const int32 FirstIndex = 0, const int32 LastIndex = INDEX_NONE ) { const int32 LocalLastIndex = LastIndex == INDEX_NONE ? Array.Num() : LastIndex; - int32 Lenght = LocalLastIndex - FirstIndex; - int32 Middle = Lenght / 2; + int32 Length = LocalLastIndex - FirstIndex; + int32 Middle = Length; int32 Offset = FirstIndex; int32 Edge = 0; while( Middle > 0 ) { - Middle = Lenght / 2; + Middle = Length / 2; if( Array[Offset + Middle] >= Item ) { Edge = 0; @@ -162,8 +182,8 @@ public: Offset += Middle; } - Lenght -= Middle; + Length -= Middle; } return Offset+Edge; } -}; +}; \ No newline at end of file diff --git a/Engine/Source/Developer/ProfilerClient/Private/ProfilerClientManager.cpp b/Engine/Source/Developer/ProfilerClient/Private/ProfilerClientManager.cpp index 736cd4988bda..01f9bdc68096 100644 --- a/Engine/Source/Developer/ProfilerClient/Private/ProfilerClientManager.cpp +++ b/Engine/Source/Developer/ProfilerClient/Private/ProfilerClientManager.cpp @@ -249,6 +249,8 @@ void FProfilerClientManager::SetPreviewState( const bool bRequestedPreviewState, void FProfilerClientManager::LoadCapture( const FString& DataFilepath, const FGuid& ProfileId ) { #if STATS + UE_LOG( LogStats, Warning, TEXT( "Started loading: %s" ), *DataFilepath ); + // start an async load LoadConnection = &Connections.FindOrAdd(ProfileId); LoadConnection->InstanceId = ProfileId; @@ -402,8 +404,7 @@ void FServiceConnection::Initialize( const FProfilerServiceAuthorize& Message, c // read the message new (StatMessages)FStatMessage( Stream.ReadMessage( MemoryReader ) ); } - static FStatNameAndInfo Adv(NAME_AdvanceFrame, "", "", TEXT(""), EStatDataType::ST_int64, true, false); - new (StatMessages) FStatMessage(Adv.GetEncodedName(), EStatOperation::AdvanceFrameEventGameThread, 1LL, false); + new (StatMessages) FStatMessage(FStatConstants::AdvanceFrame.GetEncodedName(), EStatOperation::AdvanceFrameEventGameThread, 1LL, false); } // generate a thread state from the data @@ -978,28 +979,56 @@ void FServiceConnection::GenerateAccumulators(TArray& Stats, TArra SCOPE_CYCLE_COUNTER(STAT_PC_GenerateAccumulator) for (int32 Index = 0; Index < Stats.Num(); ++Index) { - FStatMessage& Stat = Stats[Index]; - if (Stat.NameAndInfo.GetField() == EStatDataType::ST_int64) - { - // add a count accumulator - FProfilerCountAccumulator Data; - Data.StatId = FindOrAddStat(Stat.NameAndInfo, STATTYPE_AccumulatorDWORD); - Data.Value = Stat.GetValue_int64(); - CountAccumulators.Add(Data); - } - else if (Stat.NameAndInfo.GetField() == EStatDataType::ST_double) - { - // add a float accumulator - FProfilerFloatAccumulator Data; - Data.StatId = FindOrAddStat(Stat.NameAndInfo, STATTYPE_AccumulatorFLOAT); - Data.Value = Stat.GetValue_double(); - FloatAccumulators.Add(Data); + const FStatMessage& StatMessage = Stats[Index]; + const FName GroupName = StatMessage.NameAndInfo.GetGroupName(); - const FName StatName = Stat.NameAndInfo.GetShortName(); - if (StatName == TEXT("STAT_SecondsPerCycle")) + uint32 StatType = STATTYPE_Error; + if (StatMessage.NameAndInfo.GetField() == EStatDataType::ST_int64) + { + if (StatMessage.NameAndInfo.GetFlag( EStatMetaFlags::IsCycle )) { - FScopeLock ScopeLock( &CriticalSection ); - MetaData.SecondsPerCycle = Stat.GetValue_double(); + StatType = STATTYPE_CycleCounter; + } + else if (StatMessage.NameAndInfo.GetFlag( EStatMetaFlags::IsMemory )) + { + StatType = STATTYPE_MemoryCounter; + } + else + { + StatType = STATTYPE_AccumulatorDWORD; + } + } + else if (StatMessage.NameAndInfo.GetField() == EStatDataType::ST_double) + { + StatType = STATTYPE_AccumulatorFLOAT; + } + + if (StatType != STATTYPE_Error) + { + const int32 StatId = FindOrAddStat( StatMessage.NameAndInfo, StatType ); + + if (StatMessage.NameAndInfo.GetField() == EStatDataType::ST_int64) + { + // add a count accumulator + FProfilerCountAccumulator Data; + Data.StatId = StatId; + Data.Value = StatMessage.GetValue_int64(); + CountAccumulators.Add( Data ); + } + else if (StatMessage.NameAndInfo.GetField() == EStatDataType::ST_double) + { + // add a float accumulator + FProfilerFloatAccumulator Data; + Data.StatId = StatId; + Data.Value = StatMessage.GetValue_double(); + FloatAccumulators.Add( Data ); + + const FName StatName = StatMessage.NameAndInfo.GetShortName(); + if (StatName == FStatConstants::NAME_SecondsPerCycle) + { + FScopeLock ScopeLock( &CriticalSection ); + MetaData.SecondsPerCycle = StatMessage.GetValue_double(); + } } } } @@ -1091,8 +1120,7 @@ bool FServiceConnection::ReadAndConvertStatMessages( FArchive& Reader, bool bUse Reader << UncompressedData; if( UncompressedData.HasReachedEndOfCompressedData() ) { - static FStatNameAndInfo Adv( NAME_AdvanceFrame, "", "", TEXT( "" ), EStatDataType::ST_int64, true, false ); - GenerateNewFrame( FStatMessage( Adv.GetEncodedName(), EStatOperation::AdvanceFrameEventGameThread, 0ll, false ), Reader, bUseInAsync ); + GenerateNewFrame( FStatMessage( FStatConstants::AdvanceFrame.GetEncodedName(), EStatOperation::AdvanceFrameEventGameThread, 0ll, false ), Reader, bUseInAsync ); return true; } @@ -1126,61 +1154,6 @@ bool FServiceConnection::ReadAndConvertStatMessages( FArchive& Reader, bool bUse } } // Obsolete. Remove later. - else - { - while( Reader.Tell() < Reader.TotalSize() ) - { - // read the message - FStatMessage Message( Stream.ReadMessage( Reader, bIsFinalized ) ); - ReadMessages++; - - if( Message.NameAndInfo.GetShortName() != TEXT( "Unknown FName" ) ) - { - if( Message.NameAndInfo.GetField() == EStatOperation::SpecialMessageMarker ) - { - // Simply break the loop. - // The profiler supports more advanced handling of this message. - return true; - } - else if( Message.NameAndInfo.GetField() == EStatOperation::AdvanceFrameEventGameThread && ReadMessages > 2 ) - { - AddCollectedStatMessages( Message ); - - // create an old format data frame from the data - GenerateProfilerDataFrame(); - - { - // add the frame to the work list - FScopeLock ScopeLock( &CriticalSection ); - DataFrames.Add( CurrentData ); - DataLoadingProgress = (double)Reader.Tell() / (double)Reader.TotalSize(); - } - - if( bUseInAsync ) - { - if( DataFrames.Num() > FProfilerClientManager::MaxFramesPerTick ) - { - while( DataFrames.Num() ) - { - FPlatformProcess::Sleep( 0.001f ); - } - } - } - } - - new (Messages)FStatMessage( Message ); - } - else - { - break; - } - - if( !bUseInAsync && DataFrames.Num() < FProfilerClientManager::MaxFramesPerTick ) - { - return false; - } - } - } if( Reader.Tell() >= Reader.TotalSize() ) { diff --git a/Engine/Source/Editor/BlueprintGraph/Private/BlueprintNodeTemplateCache.cpp b/Engine/Source/Editor/BlueprintGraph/Private/BlueprintNodeTemplateCache.cpp index f867968bc850..ceca7a82657e 100644 --- a/Engine/Source/Editor/BlueprintGraph/Private/BlueprintNodeTemplateCache.cpp +++ b/Engine/Source/Editor/BlueprintGraph/Private/BlueprintNodeTemplateCache.cpp @@ -114,7 +114,7 @@ static UEdGraph* BlueprintNodeTemplateCacheImpl::FindCompatibleGraph(UBlueprint* UEdGraph* FoundGraph = nullptr; TArray BlueprintChildObjs; - GetObjectsWithOuter(BlueprintOuter, BlueprintChildObjs, /*bIncludeNestedObjects =*/false, /*ExclusionFlags =*/RF_PendingKill); + GetObjectsWithOuter(BlueprintOuter, BlueprintChildObjs, /*bIncludeNestedObjects =*/false, /*ExclusionFlags =*/ RF_NoFlags, /** InternalExcludeFlags */ EInternalObjectFlags::PendingKill); for (UObject* Child : BlueprintChildObjs) { diff --git a/Engine/Source/Editor/BlueprintGraph/Private/EdGraphSchema_K2.cpp b/Engine/Source/Editor/BlueprintGraph/Private/EdGraphSchema_K2.cpp index b666bc00a309..e97e717b6b85 100644 --- a/Engine/Source/Editor/BlueprintGraph/Private/EdGraphSchema_K2.cpp +++ b/Engine/Source/Editor/BlueprintGraph/Private/EdGraphSchema_K2.cpp @@ -3917,7 +3917,7 @@ void UEdGraphSchema_K2::ReconstructNode(UEdGraphNode& TargetNode, bool bIsBatchR { UEdGraphPin* Pin = Cast(NodeChildren[Iter]); const bool bIsValidPin = !Pin - || (Pin->HasAllFlags(RF_PendingKill) && !Pin->LinkedTo.Num()) + || (Pin->IsPendingKill() && !Pin->LinkedTo.Num()) || TargetNode.Pins.Contains(Pin); if (!bIsValidPin) { diff --git a/Engine/Source/Editor/BlueprintGraph/Private/K2Node_CallFunction.cpp b/Engine/Source/Editor/BlueprintGraph/Private/K2Node_CallFunction.cpp index 1040a091571d..c580b168ae59 100644 --- a/Engine/Source/Editor/BlueprintGraph/Private/K2Node_CallFunction.cpp +++ b/Engine/Source/Editor/BlueprintGraph/Private/K2Node_CallFunction.cpp @@ -2356,7 +2356,7 @@ bool UK2Node_CallFunction::HasExternalDependencies(TArray* Optio continue; } - if (DepStruct && !DepStruct->HasAnyFlags(RF_Native)) + if (DepStruct && !DepStruct->IsNative()) { if (OptionalOutput) { diff --git a/Engine/Source/Editor/BlueprintGraph/Private/K2Node_FunctionTerminator.cpp b/Engine/Source/Editor/BlueprintGraph/Private/K2Node_FunctionTerminator.cpp index 43601795e74b..4c50288e6d88 100644 --- a/Engine/Source/Editor/BlueprintGraph/Private/K2Node_FunctionTerminator.cpp +++ b/Engine/Source/Editor/BlueprintGraph/Private/K2Node_FunctionTerminator.cpp @@ -73,7 +73,7 @@ bool UK2Node_FunctionTerminator::HasExternalDependencies(TArray* continue; } - if (DepStruct && !DepStruct->HasAnyFlags(RF_Native)) + if (DepStruct && !DepStruct->IsNative()) { if (OptionalOutput) { diff --git a/Engine/Source/Editor/EditorSettingsViewer/Private/EditorSettingsViewerModule.cpp b/Engine/Source/Editor/EditorSettingsViewer/Private/EditorSettingsViewerModule.cpp index bdfb175eb452..d1a88038c694 100644 --- a/Engine/Source/Editor/EditorSettingsViewer/Private/EditorSettingsViewerModule.cpp +++ b/Engine/Source/Editor/EditorSettingsViewer/Private/EditorSettingsViewerModule.cpp @@ -13,6 +13,7 @@ #include "Tests/AutomationTestSettings.h" #include "BlueprintEditorSettings.h" +#include "CrashReporterSettings.h" #define LOCTEXT_NAMESPACE "FEditorSettingsViewerModule" @@ -137,6 +138,13 @@ protected: GetMutableDefault() ); + // Crash Reporter settings + SettingsModule.RegisterSettings("Editor", "General", "CrashReporter", + LOCTEXT("CrashReporterSettingsName", "Crash Reporter"), + LOCTEXT("CrashReporterSettingsDescription", "Various Crash Reporter related settings."), + GetMutableDefault() + ); + // experimental features SettingsModule.RegisterSettings("Editor", "General", "Experimental", LOCTEXT("ExperimentalettingsName", "Experimental"), @@ -227,6 +235,7 @@ protected: SettingsModule->UnregisterSettings("Editor", "General", "AutomationTest"); SettingsModule->UnregisterSettings("Editor", "General", "Internationalization"); SettingsModule->UnregisterSettings("Editor", "General", "Experimental"); + SettingsModule->UnregisterSettings("Editor", "General", "CrashReporter"); // level editor settings SettingsModule->UnregisterSettings("Editor", "LevelEditor", "PlayIn"); diff --git a/Engine/Source/Editor/Kismet/Private/BlueprintEditor.cpp b/Engine/Source/Editor/Kismet/Private/BlueprintEditor.cpp index 072f2467d56c..557a94c42e78 100644 --- a/Engine/Source/Editor/Kismet/Private/BlueprintEditor.cpp +++ b/Engine/Source/Editor/Kismet/Private/BlueprintEditor.cpp @@ -7786,7 +7786,7 @@ bool FBlueprintEditor::IsSelectionNativeVariable() { UProperty* VariableProperty = VarNode->VariableReference.ResolveMember( VarNode->GetBlueprintClassFromNode() ); - if( VariableProperty && VariableProperty->HasAllFlags( RF_Native )) + if( VariableProperty && VariableProperty->IsNative()) { return true; } diff --git a/Engine/Source/Editor/Kismet/Private/FindInBlueprintManager.cpp b/Engine/Source/Editor/Kismet/Private/FindInBlueprintManager.cpp index b81b7b7828d6..f8488088b76d 100644 --- a/Engine/Source/Editor/Kismet/Private/FindInBlueprintManager.cpp +++ b/Engine/Source/Editor/Kismet/Private/FindInBlueprintManager.cpp @@ -1564,7 +1564,7 @@ bool FFindInBlueprintSearchManager::ContinueSearchQuery(const FStreamSearch* InS while(SearchIdx < SearchArray.Num()) { // If the Blueprint is not marked for deletion, and the asset is valid, we will check to see if we want to refresh the searchable data. - if( SearchArray[SearchIdx].bMarkedForDeletion || (SearchArray[SearchIdx].Blueprint.IsValid() && SearchArray[SearchIdx].Blueprint->HasAllFlags(RF_PendingKill)) ) + if( SearchArray[SearchIdx].bMarkedForDeletion || (SearchArray[SearchIdx].Blueprint.IsValid() && SearchArray[SearchIdx].Blueprint->IsPendingKill()) ) { // Mark it for deletion, it will be removed on next save SearchArray[SearchIdx].bMarkedForDeletion = true; @@ -1704,7 +1704,7 @@ void FFindInBlueprintSearchManager::CleanCache() // Here it builds the new map/array, clean of deleted content. // If the database item is not marked for deletion and not pending kill (if loaded), keep it in the database - if( !SearchArray[SearchValuePair.Value].bMarkedForDeletion && !(SearchArray[SearchValuePair.Value].Blueprint.IsValid() && SearchArray[SearchValuePair.Value].Blueprint->HasAllFlags(RF_PendingKill)) ) + if( !SearchArray[SearchValuePair.Value].bMarkedForDeletion && !(SearchArray[SearchValuePair.Value].Blueprint.IsValid() && SearchArray[SearchValuePair.Value].Blueprint->IsPendingKill()) ) { // Build the new map/array NewSearchMap.Add(SearchValuePair.Key, NewSearchArray.Add(MoveTemp(SearchArray[SearchValuePair.Value])) ); diff --git a/Engine/Source/Editor/Kismet/Private/SBlueprintEditorSelectedDebugObjectWidget.cpp b/Engine/Source/Editor/Kismet/Private/SBlueprintEditorSelectedDebugObjectWidget.cpp index c9a9b9924049..d0cc758f70e5 100644 --- a/Engine/Source/Editor/Kismet/Private/SBlueprintEditorSelectedDebugObjectWidget.cpp +++ b/Engine/Source/Editor/Kismet/Private/SBlueprintEditorSelectedDebugObjectWidget.cpp @@ -393,7 +393,7 @@ void SBlueprintEditorSelectedDebugObjectWidget::GenerateDebugObjectNames(bool bR continue; } - if (!TestObject->HasAnyFlags(RF_PendingKill | RF_ClassDefaultObject)) + if (!TestObject->HasAnyFlags(RF_ClassDefaultObject) && !TestObject->IsPendingKill()) { UObject *ObjOuter = TestObject; UWorld *ObjWorld = nullptr; @@ -434,58 +434,58 @@ void SBlueprintEditorSelectedDebugObjectWidget::GenerateDebugObjectNames(bool bR } else { - for (TObjectIterator It; It; ++It) + for (TObjectIterator It; It; ++It) + { + UObject* TestObject = *It; + + // Skip Blueprint preview objects (don't allow them to be selected for debugging) + if (PreviewWorld != nullptr && TestObject->IsIn(PreviewWorld)) { - UObject* TestObject = *It; + continue; + } - // Skip Blueprint preview objects (don't allow them to be selected for debugging) - if (PreviewWorld != nullptr && TestObject->IsIn(PreviewWorld)) + const bool bPassesFlags = !TestObject->HasAnyFlags(RF_ClassDefaultObject) && !TestObject->IsPendingKill(); + const bool bGeneratedByAnyBlueprint = TestObject->GetClass()->ClassGeneratedBy != nullptr; + const bool bGeneratedByThisBlueprint = bGeneratedByAnyBlueprint && TestObject->IsA(GetBlueprintObj()->GeneratedClass); + + if (bPassesFlags && bGeneratedByThisBlueprint) + { + UObject *ObjOuter = TestObject; + UWorld *ObjWorld = nullptr; + do // Run through at least once in case the TestObject is a UGameInstance { - continue; - } + UGameInstance *ObjGameInstance = Cast(ObjOuter); - const bool bPassesFlags = !TestObject->HasAnyFlags(RF_PendingKill | RF_ClassDefaultObject); - const bool bGeneratedByAnyBlueprint = TestObject->GetClass()->ClassGeneratedBy != nullptr; - const bool bGeneratedByThisBlueprint = bGeneratedByAnyBlueprint && TestObject->IsA(GetBlueprintObj()->GeneratedClass); + ObjOuter = ObjOuter->GetOuter(); + ObjWorld = ObjGameInstance ? ObjGameInstance->GetWorld() : Cast(ObjOuter); + } while (ObjWorld == nullptr && ObjOuter != nullptr); - if (bPassesFlags && bGeneratedByThisBlueprint) + if (ObjWorld) { - UObject *ObjOuter = TestObject; - UWorld *ObjWorld = nullptr; - do // Run through at least once in case the TestObject is a UGameInstance + // Make check on owning level (not streaming level) + if (ObjWorld->PersistentLevel && ObjWorld->PersistentLevel->OwningWorld) { - UGameInstance *ObjGameInstance = Cast(ObjOuter); + ObjWorld = ObjWorld->PersistentLevel->OwningWorld; + } - ObjOuter = ObjOuter->GetOuter(); - ObjWorld = ObjGameInstance ? ObjGameInstance->GetWorld() : Cast(ObjOuter); - } while (ObjWorld == nullptr && ObjOuter != nullptr); - - if (ObjWorld) + // We have a specific debug world and the object isn't in it + if (DebugWorld && ObjWorld != DebugWorld) { - // Make check on owning level (not streaming level) - if (ObjWorld->PersistentLevel && ObjWorld->PersistentLevel->OwningWorld) - { - ObjWorld = ObjWorld->PersistentLevel->OwningWorld; - } + continue; + } - // We have a specific debug world and the object isn't in it - if (DebugWorld && ObjWorld != DebugWorld) - { - continue; - } - - if ((ObjWorld->WorldType == EWorldType::Editor) && (GUnrealEd->GetPIEViewport() == nullptr)) - { - AddDebugObject(TestObject); - } - else if (ObjWorld->WorldType == EWorldType::PIE) - { - AddDebugObject(TestObject); - } + if ((ObjWorld->WorldType == EWorldType::Editor) && (GUnrealEd->GetPIEViewport() == nullptr)) + { + AddDebugObject(TestObject); + } + else if (ObjWorld->WorldType == EWorldType::PIE) + { + AddDebugObject(TestObject); } } } } + } // Attempt to restore the old selection if (bRestoreSelection && DebugObjectsComboBox.IsValid()) diff --git a/Engine/Source/Editor/Kismet/Private/SMyBlueprint.cpp b/Engine/Source/Editor/Kismet/Private/SMyBlueprint.cpp index 1de7b191ba8c..1b103e0cc76f 100644 --- a/Engine/Source/Editor/Kismet/Private/SMyBlueprint.cpp +++ b/Engine/Source/Editor/Kismet/Private/SMyBlueprint.cpp @@ -2222,7 +2222,7 @@ void SMyBlueprint::OnDeleteGraph(UEdGraph* InGraph, EEdGraphSchemaAction_K2Graph FBlueprintEditorUtils::RemoveGraph(GetBlueprintObj(), InGraph, EGraphRemoveFlags::Recompile); BlueprintEditorPtr.Pin()->CloseDocumentTab(InGraph); - for (TObjectIterator It(RF_ClassDefaultObject | RF_PendingKill); It; ++It) + for (TObjectIterator It(RF_ClassDefaultObject, /** bIncludeDerivedClasses */ true, /** InternalExcludeFlags */ EInternalObjectFlags::PendingKill); It; ++It) { if (It->GetGraph() != InGraph) { @@ -2274,7 +2274,7 @@ void SMyBlueprint::OnDeleteDelegate(FEdGraphSchemaAction_K2Delegate* InDelegateA FBlueprintEditorUtils::RemoveMemberVariable(BlueprintObj, GraphToActOn->GetFName()); FBlueprintEditorUtils::RemoveGraph(BlueprintObj, GraphToActOn, EGraphRemoveFlags::Recompile); - for (TObjectIterator It(RF_ClassDefaultObject | RF_PendingKill); It; ++It) + for (TObjectIterator It(RF_ClassDefaultObject, /** bIncludeDerivedClasses */ true, /** InternalExcludeFlags */ EInternalObjectFlags::PendingKill); It; ++It) { if (!It->IsPendingKill() && It->GetGraph() && !It->GetGraph()->IsPendingKill()) { @@ -2622,7 +2622,7 @@ bool SMyBlueprint::IsNativeVariable() const { UProperty* VarProperty = VarAction->GetProperty(); - if( VarProperty && VarProperty->HasAllFlags( RF_Native )) + if( VarProperty && VarProperty->IsNative()) { return true; } diff --git a/Engine/Source/Editor/Kismet/Private/SSCSEditor.cpp b/Engine/Source/Editor/Kismet/Private/SSCSEditor.cpp index 46e0a583054f..46913deaa2e5 100644 --- a/Engine/Source/Editor/Kismet/Private/SSCSEditor.cpp +++ b/Engine/Source/Editor/Kismet/Private/SSCSEditor.cpp @@ -5313,7 +5313,7 @@ FSCSEditorTreeNodePtrType SSCSEditor::AddTreeNodeFromComponent(USceneComponent* FSCSEditorTreeNodePtrType NewNodePtr; check(InSceneComponent != NULL); - ensure(!InSceneComponent->HasAnyFlags(RF_PendingKill)); + ensure(!InSceneComponent->IsPendingKill()); // If the given component has a parent, and if we're not in "instance" mode OR the owner of the parent matches the Actor instance we're editing if(InSceneComponent->AttachParent != NULL diff --git a/Engine/Source/Editor/KismetCompiler/Private/KismetCompiler.cpp b/Engine/Source/Editor/KismetCompiler/Private/KismetCompiler.cpp index 8c970d7a4d52..67dcd42b4bd5 100644 --- a/Engine/Source/Editor/KismetCompiler/Private/KismetCompiler.cpp +++ b/Engine/Source/Editor/KismetCompiler/Private/KismetCompiler.cpp @@ -215,7 +215,7 @@ void FKismetCompilerContext::CleanAndSanitizeClass(UBlueprintGeneratedClass* Cla // Set properties we need to regenerate the class with ClassToClean->PropertyLink = ParentClass->PropertyLink; ClassToClean->ClassWithin = ParentClass; - ClassToClean->ClassConfigName = ClassToClean->HasAnyFlags(RF_Native) ? FName(ClassToClean->StaticConfigName()) : FName(*ParentClass->GetConfigName()); + ClassToClean->ClassConfigName = ClassToClean->IsNative() ? FName(ClassToClean->StaticConfigName()) : FName(*ParentClass->GetConfigName()); ClassToClean->DebugData = FBlueprintDebugData(); } @@ -451,7 +451,7 @@ void FKismetCompilerContext::ValidateVariableNames() { NewVarName = FBlueprintEditorUtils::FindUniqueKismetName(Blueprint, VarNameStr); } - else if (ParentClass->HasAnyFlags(RF_Native)) // the above case handles when the parent is a blueprint + else if (ParentClass->IsNative()) // the above case handles when the parent is a blueprint { if (auto ExisingField = FindField(ParentClass, *VarNameStr)) { diff --git a/Engine/Source/Editor/KismetCompiler/Private/UserDefinedStructureCompilerUtils.cpp b/Engine/Source/Editor/KismetCompiler/Private/UserDefinedStructureCompilerUtils.cpp index 95b7109f6fbf..87ace6c43f19 100644 --- a/Engine/Source/Editor/KismetCompiler/Private/UserDefinedStructureCompilerUtils.cpp +++ b/Engine/Source/Editor/KismetCompiler/Private/UserDefinedStructureCompilerUtils.cpp @@ -52,7 +52,7 @@ struct FUserDefinedStructureCompilerInner DuplicatedStruct->AddToRoot(); CastChecked(DuplicatedStruct->EditorData)->RecreateDefaultInstance(); - for (auto StructProperty : TObjectRange(RF_ClassDefaultObject | RF_PendingKill)) + for (auto StructProperty : TObjectRange(RF_ClassDefaultObject, /** bIncludeDerivedClasses */ true, /** InternalExcludeFlags */ EInternalObjectFlags::PendingKill)) { if (StructProperty && (StructureToReinstance == StructProperty->Struct)) { @@ -68,7 +68,7 @@ struct FUserDefinedStructureCompilerInner { check(OwnerStruct != DuplicatedStruct); const bool bValidStruct = (OwnerStruct->GetOutermost() != GetTransientPackage()) - && !OwnerStruct->HasAnyFlags(RF_PendingKill) + && !OwnerStruct->IsPendingKill() && (EUserDefinedStructureStatus::UDSS_Duplicate != OwnerStruct->Status.GetValue()); if (bValidStruct) @@ -86,7 +86,7 @@ struct FUserDefinedStructureCompilerInner DuplicatedStruct->RemoveFromRoot(); - for (auto Blueprint : TObjectRange(RF_ClassDefaultObject | RF_PendingKill)) + for (auto Blueprint : TObjectRange(RF_ClassDefaultObject, /** bIncludeDerivedClasses */ true, /** InternalExcludeFlags */ EInternalObjectFlags::PendingKill)) { if (Blueprint && !BlueprintsToRecompile.Contains(Blueprint)) { @@ -341,13 +341,13 @@ void FUserDefinedStructureCompilerUtils::CompileStruct(class UUserDefinedStruct* // UPDATE ALL THINGS DEPENDENT ON COMPILED STRUCTURES TSet BlueprintsThatHaveBeenRecompiled; - for (TObjectIterator It(RF_Transient | RF_PendingKill | RF_ClassDefaultObject, true); It && ChangedStructs.Num(); ++It) + for (TObjectIterator It(RF_Transient | RF_ClassDefaultObject, /** bIncludeDerivedClasses */ true, /** InternalExcludeFlags */ EInternalObjectFlags::PendingKill); It && ChangedStructs.Num(); ++It) { bool bReconstruct = false; UK2Node* Node = *It; - if (Node && !Node->HasAnyFlags(RF_Transient | RF_PendingKill)) + if (Node && !Node->HasAnyFlags(RF_Transient) && !Node->IsPendingKill()) { // If this is a struct operation node operation on the changed struct we must reconstruct if (UK2Node_StructOperation* StructOpNode = Cast(Node)) diff --git a/Engine/Source/Editor/MainFrame/Private/Frame/MainFrameActions.cpp b/Engine/Source/Editor/MainFrame/Private/Frame/MainFrameActions.cpp index cf54b9e58e02..ac57ab5682af 100644 --- a/Engine/Source/Editor/MainFrame/Private/Frame/MainFrameActions.cpp +++ b/Engine/Source/Editor/MainFrame/Private/Frame/MainFrameActions.cpp @@ -584,10 +584,10 @@ void FMainFrameActionCallbacks::PackageProject( const FName InPlatformInfoName ) OptionalParams += TEXT(" -build"); } - // we can include the crash reporter for any non-code projects or internal builds (we don't have the symbols to interpret external builds of code-based projects) - if (PackagingSettings->IncludeCrashReporter && (!bProjectHasCode || FEngineBuildSettings::IsInternalBuild()) && bTargetPlatformCanUseCrashReporter) + // Whether to include the crash reporter. + if (PackagingSettings->IncludeCrashReporter && bTargetPlatformCanUseCrashReporter) { - OptionalParams += TEXT(" -CrashReporter"); + OptionalParams += TEXT( " -CrashReporter" ); } if (PackagingSettings->bBuildHttpChunkInstallData) diff --git a/Engine/Source/Editor/NiagaraEditor/Private/NiagaraEffectEditor.cpp b/Engine/Source/Editor/NiagaraEditor/Private/NiagaraEffectEditor.cpp index f4ae80468186..c8d35b8c4f5f 100644 --- a/Engine/Source/Editor/NiagaraEditor/Private/NiagaraEffectEditor.cpp +++ b/Engine/Source/Editor/NiagaraEditor/Private/NiagaraEffectEditor.cpp @@ -85,7 +85,8 @@ void FNiagaraEffectEditor::InitNiagaraEffectEditor(const EToolkitMode::Type Mode if (!Sequencer.IsValid()) { - MovieScene = NewObject(InEffect, FName("Niagara Effect MovieScene"), RF_RootSet); + MovieScene = NewObject(InEffect, FName("Niagara Effect MovieScene")); + MovieScene->AddToRoot(); auto NewAnimation = NewObject(MovieScene); MovieScene->SetPlaybackRange(InTime, OutTime); NewAnimation->MovieScene = MovieScene; diff --git a/Engine/Source/Editor/PropertyEditor/Private/ObjectPropertyNode.cpp b/Engine/Source/Editor/PropertyEditor/Private/ObjectPropertyNode.cpp index 16e0cafe46a3..79abbda3f2e7 100644 --- a/Engine/Source/Editor/PropertyEditor/Private/ObjectPropertyNode.cpp +++ b/Engine/Source/Editor/PropertyEditor/Private/ObjectPropertyNode.cpp @@ -63,7 +63,7 @@ void FObjectPropertyNode::PurgeKilledObjects() { TWeakObjectPtr Object = Objects[Index]; - if ( !Object.IsValid() || Object->HasAnyFlags(RF_PendingKill) ) + if ( !Object.IsValid() || Object->IsPendingKill() ) { Objects.RemoveAt(Index, 1); } diff --git a/Engine/Source/Editor/PropertyEditor/Private/PropertyEditorModule.cpp b/Engine/Source/Editor/PropertyEditor/Private/PropertyEditorModule.cpp index 60ef4faef4d5..3b770634d459 100644 --- a/Engine/Source/Editor/PropertyEditor/Private/PropertyEditorModule.cpp +++ b/Engine/Source/Editor/PropertyEditor/Private/PropertyEditorModule.cpp @@ -119,7 +119,7 @@ static bool ShouldShowProperty(const FPropertyAndParent& PropertyAndParent, bool { const UClass* PropertyOwnerClass = Cast(Property.GetOuter()); const bool bDisableEditOnTemplate = PropertyOwnerClass - && PropertyOwnerClass->HasAllFlags(RF_Native) + && PropertyOwnerClass->IsNative() && Property.HasAnyPropertyFlags(CPF_DisableEditOnTemplate); if(bDisableEditOnTemplate) { diff --git a/Engine/Source/Editor/SizeMap/SSizeMap.cpp b/Engine/Source/Editor/SizeMap/SSizeMap.cpp index ae1f0eaa96cc..209b1be5c507 100644 --- a/Engine/Source/Editor/SizeMap/SSizeMap.cpp +++ b/Engine/Source/Editor/SizeMap/SSizeMap.cpp @@ -84,7 +84,8 @@ namespace SizeMapInternals // Only look at objects which are valid const bool bIsValidObject = Object != nullptr && // Object should not be null - !Object->HasAnyFlags( RF_Transient | RF_PendingKill ); // Should not be transient or pending kill + !Object->HasAnyFlags(RF_Transient) && // Should not be transient + !Object->IsPendingKill(); // Should not be pending kill if( bIsValidObject ) { // Skip objects that we've already processed diff --git a/Engine/Source/Editor/UnrealEd/Classes/CookOnTheSide/CookOnTheFlyServer.h b/Engine/Source/Editor/UnrealEd/Classes/CookOnTheSide/CookOnTheFlyServer.h index 781400083633..e522164c1ebd 100644 --- a/Engine/Source/Editor/UnrealEd/Classes/CookOnTheSide/CookOnTheFlyServer.h +++ b/Engine/Source/Editor/UnrealEd/Classes/CookOnTheSide/CookOnTheFlyServer.h @@ -631,11 +631,21 @@ private: FScopeLock NamesLock(&NamesCritical); return Names.Contains(InName); } + void Remove(FName InName) + { + FScopeLock NamesLock(&NamesCritical); + Names.Remove(InName); + } void Empty() { FScopeLock NamesLock(&NamesCritical); Names.Empty(); } + void GetNames(TSet& OutNames) + { + FScopeLock NamesLock(&NamesCritical); + OutNames.Append(Names); + } }; private: /** Current cook mode the cook on the fly server is running in */ diff --git a/Engine/Source/Editor/UnrealEd/Private/AssetDeleteModel.cpp b/Engine/Source/Editor/UnrealEd/Private/AssetDeleteModel.cpp index 851cd0e2dcb8..8fc9177b5906 100644 --- a/Engine/Source/Editor/UnrealEd/Private/AssetDeleteModel.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/AssetDeleteModel.cpp @@ -655,11 +655,11 @@ void FPendingDelete::CheckForReferences() // Check and see whether we are referenced by any objects that won't be garbage collected (*including* the undo buffer) FReferencerInformationList ReferencesIncludingUndo; - bool bReferencedInMemoryOrUndoStack = IsReferenced(Object, GARBAGE_COLLECTION_KEEPFLAGS, true, &ReferencesIncludingUndo); + bool bReferencedInMemoryOrUndoStack = IsReferenced(Object, GARBAGE_COLLECTION_KEEPFLAGS, EInternalObjectFlags::GarbageCollectionKeepFlags, true, &ReferencesIncludingUndo); // Determine the in-memory references, *excluding* the undo buffer GEditor->Trans->DisableObjectSerialization(); - bIsReferencedInMemoryByNonUndo = IsReferenced(Object, GARBAGE_COLLECTION_KEEPFLAGS, true, &MemoryReferences); + bIsReferencedInMemoryByNonUndo = IsReferenced(Object, GARBAGE_COLLECTION_KEEPFLAGS, EInternalObjectFlags::GarbageCollectionKeepFlags, true, &MemoryReferences); GEditor->Trans->EnableObjectSerialization(); // see if this object is the transaction buffer - set a flag so we know we need to clear the undo stack @@ -681,12 +681,12 @@ void FPendingDelete::CheckForReferences() FReferencerInformation& RefInfo = *RefIt; if ( RefInfo.Referencer->IsA( Blueprint->GeneratedClass ) ) { - if ( IsReferenced( RefInfo.Referencer, GARBAGE_COLLECTION_KEEPFLAGS, true, &ReferencesIncludingUndo ) ) + if (IsReferenced(RefInfo.Referencer, GARBAGE_COLLECTION_KEEPFLAGS, EInternalObjectFlags::GarbageCollectionKeepFlags, true, &ReferencesIncludingUndo)) { GEditor->Trans->DisableObjectSerialization(); FReferencerInformationList ReferencesExcludingUndo; - if ( IsReferenced( RefInfo.Referencer, GARBAGE_COLLECTION_KEEPFLAGS, true, &ReferencesExcludingUndo ) ) + if (IsReferenced(RefInfo.Referencer, GARBAGE_COLLECTION_KEEPFLAGS, EInternalObjectFlags::GarbageCollectionKeepFlags, true, &ReferencesExcludingUndo)) { bIsReferencedInMemoryByUndo = ( ReferencesIncludingUndo.InternalReferences.Num() + ReferencesIncludingUndo.ExternalReferences.Num() ) > ( ReferencesExcludingUndo.InternalReferences.Num() + ReferencesExcludingUndo.ExternalReferences.Num() ); } diff --git a/Engine/Source/Editor/UnrealEd/Private/Commandlets/ContentCommandlets.cpp b/Engine/Source/Editor/UnrealEd/Private/Commandlets/ContentCommandlets.cpp index 0e4c9e33dbde..ca46ed95dfc9 100644 --- a/Engine/Source/Editor/UnrealEd/Private/Commandlets/ContentCommandlets.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/Commandlets/ContentCommandlets.cpp @@ -311,7 +311,7 @@ void UResavePackagesCommandlet::LoadAndSaveOnePackage(const FString& Filename) if( !Linker ) { VerboseMessage(TEXT("Aborting...package could not be loaded")); - CollectGarbage(RF_Native); + CollectGarbage(RF_NoFlags); return; } @@ -549,7 +549,7 @@ void UResavePackagesCommandlet::LoadAndSaveOnePackage(const FString& Filename) } VerboseMessage(TEXT("Pre CollectGarbage")); - CollectGarbage(RF_Native); + CollectGarbage(RF_NoFlags); VerboseMessage(TEXT("Post CollectGarbage")); } @@ -1318,7 +1318,7 @@ int32 UWrangleContentCommandlet::Main( const FString& Params ) } // close this package - CollectGarbage(RF_Native); + CollectGarbage(RF_NoFlags); } } } @@ -1472,7 +1472,7 @@ int32 UWrangleContentCommandlet::Main( const FString& Params ) // collect garbage every 20 packages (we aren't fully loading, so it doesn't need to be often) if ((PackageIndex % 20) == 0) { - CollectGarbage(RF_Native); + CollectGarbage(RF_NoFlags); } } } @@ -1565,7 +1565,7 @@ int32 UWrangleContentCommandlet::Main( const FString& Params ) SavePackageHelper(Package, *CutdownPackageName, RF_AllFlags, GWarn, NULL, SAVE_CutdownPackage); // close up this package - CollectGarbage(RF_Native); + CollectGarbage(RF_NoFlags); } } } @@ -1686,7 +1686,7 @@ int32 UWrangleContentCommandlet::Main( const FString& Params ) // finally save it out SavePackageHelper(MovedPackage, *MovedFilename); - CollectGarbage(RF_Native); + CollectGarbage(RF_NoFlags); } } @@ -1825,7 +1825,7 @@ int32 UListMaterialsUsedWithMeshEmittersCommandlet::Main( const FString& Params // Collect garbage every 10 packages instead of every package makes the commandlet run much faster if( (++GCIndex % 10) == 0 ) { - CollectGarbage(RF_Native); + CollectGarbage(RF_NoFlags); } } @@ -1914,7 +1914,7 @@ int32 UListStaticMeshesImportedFromSpeedTreesCommandlet::Main(const FString& Par // Collect garbage every 10 packages instead of every package makes the commandlet run much faster if ((++GCIndex % 10) == 0) { - CollectGarbage(RF_Native); + CollectGarbage(RF_NoFlags); } } diff --git a/Engine/Source/Editor/UnrealEd/Private/Commandlets/CookCommandlet.cpp b/Engine/Source/Editor/UnrealEd/Private/Commandlets/CookCommandlet.cpp index 0aa5a6a88cb2..2389ac9150ee 100644 --- a/Engine/Source/Editor/UnrealEd/Private/Commandlets/CookCommandlet.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/Commandlets/CookCommandlet.cpp @@ -178,7 +178,7 @@ bool UCookCommandlet::CookOnTheFly( FGuid InstanceId, int32 Timeout, bool bForce UE_LOG(LogCookCommandlet, Display, TEXT("GC...")); - CollectGarbage( RF_Native ); + CollectGarbage(RF_NoFlags); } @@ -642,7 +642,7 @@ void UCookCommandlet::CleanSandbox(const TArray& Platforms) } // Collect garbage to ensure we don't have any packages hanging around from dependent time stamp determination - CollectGarbage(RF_Native); + CollectGarbage(RF_NoFlags); } } @@ -1245,7 +1245,7 @@ bool UCookCommandlet::NewCook( const TArray& Platforms, TArray UE_LOG( LogCookCommandlet, Display, TEXT( "GC..." ) ); - CollectGarbage( RF_Native ); + CollectGarbage(RF_NoFlags); } else { @@ -1417,7 +1417,7 @@ bool UCookCommandlet::Cook(const TArray& Platforms, TArray= GCInterval || FileIndex < 0) { UE_LOG(LogDerivedDataCacheCommandlet, Display, TEXT("GC (Full)...")); - CollectGarbage( RF_Native ); + CollectGarbage(RF_NoFlags); NumProcessedSinceLastGC = 0; } else { UE_LOG(LogDerivedDataCacheCommandlet, Display, TEXT("GC...")); - CollectGarbage( RF_Native | RF_Standalone ); + CollectGarbage(RF_Standalone); } GCTime += FPlatformTime::Seconds() - StartGCTime; diff --git a/Engine/Source/Editor/UnrealEd/Private/Commandlets/FixupRedirectsCommandlet.cpp b/Engine/Source/Editor/UnrealEd/Private/Commandlets/FixupRedirectsCommandlet.cpp index 935409c5af24..2f7a711f930c 100644 --- a/Engine/Source/Editor/UnrealEd/Private/Commandlets/FixupRedirectsCommandlet.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/Commandlets/FixupRedirectsCommandlet.cpp @@ -232,7 +232,7 @@ int32 UFixupRedirectsCommandlet::Main( const FString& Params ) if (((++GCIndex) % 50) == 0 || bNeedToCollectGarbage || Package->ContainsMap()) { // collect garbage to close the package - CollectGarbage(RF_Native); + CollectGarbage(RF_NoFlags); UE_LOG(LogFixupRedirectsCommandlet, Display, TEXT("GC...")); // reset our counter GCIndex = 0; @@ -481,7 +481,7 @@ int32 UFixupRedirectsCommandlet::Main( const FString& Params ) // collect garbage to close the package UE_LOG(LogFixupRedirectsCommandlet, Display, TEXT("Collecting Garbage...")); - CollectGarbage(RF_Native); + CollectGarbage(RF_NoFlags); } UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("")); @@ -619,7 +619,7 @@ int32 UFixupRedirectsCommandlet::Main( const FString& Params ) if (((++GCIndex) % 50) == 0 || bIsDirty || Package->ContainsMap()) { // collect garbage to close the package - CollectGarbage(RF_Native); + CollectGarbage(RF_NoFlags); UE_LOG(LogFixupRedirectsCommandlet, Display, TEXT("GC...")); // reset our counter GCIndex = 0; diff --git a/Engine/Source/Editor/UnrealEd/Private/Commandlets/GatherTextFromAssetsCommandlet.cpp b/Engine/Source/Editor/UnrealEd/Private/Commandlets/GatherTextFromAssetsCommandlet.cpp index 68d700cf8870..ca002448f7b1 100644 --- a/Engine/Source/Editor/UnrealEd/Private/Commandlets/GatherTextFromAssetsCommandlet.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/Commandlets/GatherTextFromAssetsCommandlet.cpp @@ -96,7 +96,7 @@ void UGatherTextFromAssetsCommandlet::ProcessPackages( const TArray< UPackage* > for(UPackage* const Package : PackagesToProcess) { TArray ObjectsInPackage; - GetObjectsWithOuter(Package, ObjectsInPackage, true, RF_Transient | RF_PendingKill); + GetObjectsWithOuter(Package, ObjectsInPackage, true, RF_Transient, EInternalObjectFlags::PendingKill); for (UObject* const Object : ObjectsInPackage) { TArray GatherableTextDataArray; @@ -417,7 +417,7 @@ int32 UGatherTextFromAssetsCommandlet::Main(const FString& Params) } } - CollectGarbage( RF_Native ); + CollectGarbage(RF_NoFlags); //Now go through the remaining packages in the main array and process them in batches. int32 PackagesPerBatchCount = 100; @@ -501,7 +501,7 @@ int32 UGatherTextFromAssetsCommandlet::Main(const FString& Params) } } - CollectGarbage( RF_Native ); + CollectGarbage(RF_NoFlags); LoadedPackages.Empty(PackagesPerBatchCount); LoadedPackageFileNames.Empty(PackagesPerBatchCount); } diff --git a/Engine/Source/Editor/UnrealEd/Private/Commandlets/GenerateBlueprintAPICommandlet.cpp b/Engine/Source/Editor/UnrealEd/Private/Commandlets/GenerateBlueprintAPICommandlet.cpp index 5eed6fe2806a..b84aefa39094 100644 --- a/Engine/Source/Editor/UnrealEd/Private/Commandlets/GenerateBlueprintAPICommandlet.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/Commandlets/GenerateBlueprintAPICommandlet.cpp @@ -442,7 +442,7 @@ static UBlueprint* GenerateBlueprintAPIUtils::MakeTempBlueprint(UClass* ParentCl } FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(MadeBlueprint); - MadeBlueprint->SetFlags(RF_RootSet); // to keep the BP from being garbage collected + MadeBlueprint->AddToRoot(); // to keep the BP from being garbage collected FKismetEditorUtilities::CompileBlueprint(MadeBlueprint); ClassBlueprints.Add(ParentClass, MadeBlueprint); } diff --git a/Engine/Source/Editor/UnrealEd/Private/Commandlets/GenerateDistillFileSetsCommandlet.cpp b/Engine/Source/Editor/UnrealEd/Private/Commandlets/GenerateDistillFileSetsCommandlet.cpp index 88a2c32475f9..77cbba7e19af 100644 --- a/Engine/Source/Editor/UnrealEd/Private/Commandlets/GenerateDistillFileSetsCommandlet.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/Commandlets/GenerateDistillFileSetsCommandlet.cpp @@ -217,7 +217,7 @@ int32 UGenerateDistillFileSetsCommandlet::Main( const FString& InParams ) } } UE_LOG(LogGenerateDistillFileSetsCommandlet, Display, TEXT( "Collecting garbage..." ) ); - CollectGarbage(RF_Native); + CollectGarbage(RF_NoFlags); } } } diff --git a/Engine/Source/Editor/UnrealEd/Private/Commandlets/PackageUtilities.cpp b/Engine/Source/Editor/UnrealEd/Private/Commandlets/PackageUtilities.cpp index cffea4a3dae5..65b9de3281f4 100644 --- a/Engine/Source/Editor/UnrealEd/Private/Commandlets/PackageUtilities.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/Commandlets/PackageUtilities.cpp @@ -715,7 +715,7 @@ int32 ULoadPackageCommandlet::Main( const FString& Params ) } if (!bFast || FileIndex % 100 == 99) { - CollectGarbage( RF_Native ); + CollectGarbage(RF_NoFlags); } } GIsEditor = GIsServer = GIsClient = true; @@ -1465,7 +1465,7 @@ int32 UPkgInfoCommandlet::Main( const FString& Params ) Reporter->GeneratePackageReport(Linker); } - CollectGarbage(RF_Native); + CollectGarbage(RF_NoFlags); } // turn off as it makes diffing hard @@ -2532,7 +2532,7 @@ int32 UReplaceActorCommandlet::Main(const FString& Params) // get rid of the loaded world UE_LOG(LogPackageUtilities, Warning, TEXT("GCing...")); - CollectGarbage(RF_Native); + CollectGarbage(RF_NoFlags); } // UEditorEngine::FinishDestroy() expects GWorld to exist diff --git a/Engine/Source/Editor/UnrealEd/Private/Commandlets/ParticleSystemAuditCommandlet.cpp b/Engine/Source/Editor/UnrealEd/Private/Commandlets/ParticleSystemAuditCommandlet.cpp index d4f4ceb87b8a..1f3ab094eb94 100644 --- a/Engine/Source/Editor/UnrealEd/Private/Commandlets/ParticleSystemAuditCommandlet.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/Commandlets/ParticleSystemAuditCommandlet.cpp @@ -267,7 +267,7 @@ bool UParticleSystemAuditCommandlet::ProcessParticleSystems() if (PackageSwitches > 10) { - ::CollectGarbage(RF_Native); + ::CollectGarbage(RF_NoFlags); PackageSwitches = 0; } @@ -280,7 +280,7 @@ bool UParticleSystemAuditCommandlet::ProcessParticleSystems() // Probably don't need to do this, but just in case we have any 'hanging' packages // and more processing steps are added later, let's clean up everything... - ::CollectGarbage(RF_Native); + ::CollectGarbage(RF_NoFlags); double ProcessParticleSystemsTime = FPlatformTime::Seconds() - StartProcessParticleSystemsTime; UE_LOG(LogParticleSystemAuditCommandlet, Log, TEXT("Took %5.3f seconds to process referenced particle systems..."), ProcessParticleSystemsTime); diff --git a/Engine/Source/Editor/UnrealEd/Private/CookOnTheFlyServer.cpp b/Engine/Source/Editor/UnrealEd/Private/CookOnTheFlyServer.cpp index a948eb8668dc..2b2a55c1b9da 100644 --- a/Engine/Source/Editor/UnrealEd/Private/CookOnTheFlyServer.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/CookOnTheFlyServer.cpp @@ -2048,6 +2048,10 @@ uint32 UCookOnTheFlyServer::TickCookOnTheSide( const float TimeSlice, uint32 &Co continue; } + // This package is valid, so make sure it wasn't previously marked as being an uncooked editor only package or it would get removed from the + // asset registry at the end of the cook + UncookedEditorOnlyPackages.Remove(Package->GetFName()); + const FName StandardPackageFilename = GetCachedStandardPackageFileFName(Package); check(IsInGameThread()); if (NeverCookPackageList.Contains(StandardPackageFilename)) @@ -2215,7 +2219,7 @@ uint32 UCookOnTheFlyServer::TickCookOnTheSide( const float TimeSlice, uint32 &Co } //@todo ResetLoaders outside of this (ie when Package is NULL) causes problems w/ default materials - if (Package->HasAnyFlags(RF_RootSet) == false && ((CurrentCookMode==ECookMode::CookOnTheFly)) ) + if (Package->IsRooted() == false && ((CurrentCookMode==ECookMode::CookOnTheFly)) ) { SCOPE_TIMER(ResetLoaders); ResetLoaders(Package); @@ -3937,9 +3941,15 @@ void UCookOnTheFlyServer::CookByTheBookFinished() IgnorePackageNames.Add(FName(*LongPackageName)); } + // Ignore packages that weren't cooked because they were only referenced by editor-only properties + TSet UncookedEditorOnlyPackageNames; + UncookedEditorOnlyPackages.GetNames(UncookedEditorOnlyPackageNames); + for (FName UncookedEditorOnlyPackage : UncookedEditorOnlyPackageNames) + { + IgnorePackageNames.Add(UncookedEditorOnlyPackage); + } - Manifest.Value->SaveAssetRegistry(SandboxRegistryFilename, &IgnorePackageNames); Manifest.Value->SaveCookedPackageAssetRegistry(SandboxCookedAssetRegistryFilename, true); diff --git a/Engine/Source/Editor/UnrealEd/Private/DataTableEditorUtils.cpp b/Engine/Source/Editor/UnrealEd/Private/DataTableEditorUtils.cpp index 5bd8e54bebd6..2c199fa190b0 100644 --- a/Engine/Source/Editor/UnrealEd/Private/DataTableEditorUtils.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/DataTableEditorUtils.cpp @@ -175,7 +175,7 @@ void FDataTableEditorUtils::BroadcastPostChange(UDataTable* DataTable, EDataTabl { if (DataTable && (EDataTableChangeInfo::RowList == Info)) { - for (TObjectIterator It(RF_Transient | RF_PendingKill | RF_ClassDefaultObject); It; ++It) + for (TObjectIterator It(RF_Transient | RF_ClassDefaultObject, /** bIncludeDerivedClasses */ true, /** InternalExcludeFlags */ EInternalObjectFlags::PendingKill); It; ++It) { It->OnDataTableRowListChanged(DataTable); } diff --git a/Engine/Source/Editor/UnrealEd/Private/Dialogs/DlgReferenceTree.cpp b/Engine/Source/Editor/UnrealEd/Private/Dialogs/DlgReferenceTree.cpp index dc090c14c46c..3f8d4ca04c2d 100644 --- a/Engine/Source/Editor/UnrealEd/Private/Dialogs/DlgReferenceTree.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/Dialogs/DlgReferenceTree.cpp @@ -22,7 +22,7 @@ FArchiveGenerateReferenceGraph::FArchiveGenerateReferenceGraph( FReferenceGraph& UObject* Object = *It; // Skip transient and those about to be deleted - if( !Object->HasAnyFlags( RF_Transient | RF_PendingKill ) ) + if( !Object->HasAnyFlags( RF_Transient ) && !Object->IsPendingKill() ) { // only serialize non actors objects which have not been visited. // actors are skipped because we have don't need them to show the reference tree @@ -44,7 +44,8 @@ FArchive& FArchiveGenerateReferenceGraph::operator<<( UObject*& Object ) // Only look at objects which are valid const bool bValidObject = Object && // Object should not be NULL - !Object->HasAnyFlags( RF_Transient | RF_PendingKill ) && // Should not be transient or pending kill + !Object->HasAnyFlags( RF_Transient ) && // Should not be transient + !Object->IsPendingKill() && // nor pending kill (Cast(Object) == NULL); // skip UClasses if( bValidObject ) diff --git a/Engine/Source/Editor/UnrealEd/Private/EditorActor.cpp b/Engine/Source/Editor/UnrealEd/Private/EditorActor.cpp index d9132ed2cad1..5a2e2c058465 100644 --- a/Engine/Source/Editor/UnrealEd/Private/EditorActor.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/EditorActor.cpp @@ -58,7 +58,7 @@ public: : FExportObjectInnerContext(false) { // For each object . . . - for (UObject* InnerObj : TObjectRange(RF_ClassDefaultObject | RF_PendingKill)) + for (UObject* InnerObj : TObjectRange(RF_ClassDefaultObject, /** bIncludeDerivedClasses */ true, /** IternalExcludeFlags */ EInternalObjectFlags::PendingKill)) { UObject* OuterObj = InnerObj->GetOuter(); diff --git a/Engine/Source/Editor/UnrealEd/Private/EditorEngine.cpp b/Engine/Source/Editor/UnrealEd/Private/EditorEngine.cpp index 6da983db9259..ff996eaee77a 100644 --- a/Engine/Source/Editor/UnrealEd/Private/EditorEngine.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/EditorEngine.cpp @@ -1827,7 +1827,7 @@ void UEditorEngine::Cleanse( bool ClearSelection, bool Redraw, const FText& Tran // will occur next time someone tries to use it. This is caused by // the fact that the loading routing will check that already // existed, but the object was missing in cache. - const EObjectFlags FlagsToClear = RF_Standalone | RF_RootSet | RF_Transactional; + const EObjectFlags FlagsToClear = RF_Standalone | RF_Transactional; TArray PackagesToUnload; for (TObjectIterator RedirIt; RedirIt; ++RedirIt) { @@ -1854,6 +1854,7 @@ void UEditorEngine::Cleanse( bool ClearSelection, bool Redraw, const FText& Tran { // In case this isn't redirector-only package, clear just the redirector. RedirIt->ClearFlags(FlagsToClear); + RedirIt->RemoveFromRoot(); } } @@ -1864,9 +1865,11 @@ void UEditorEngine::Cleanse( bool ClearSelection, bool Redraw, const FText& Tran for (auto* Object : PackageObjects) { Object->ClearFlags(FlagsToClear); + Object->RemoveFromRoot(); } PackageToUnload->ClearFlags(FlagsToClear); + PackageToUnload->RemoveFromRoot(); } // Collect garbage. diff --git a/Engine/Source/Editor/UnrealEd/Private/EditorSelectUtils.cpp b/Engine/Source/Editor/UnrealEd/Private/EditorSelectUtils.cpp index b7ebea700152..8136006e0daf 100644 --- a/Engine/Source/Editor/UnrealEd/Private/EditorSelectUtils.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/EditorSelectUtils.cpp @@ -469,13 +469,13 @@ bool UUnrealEdEngine::CanSelectActor(AActor* Actor, bool bInSelected, bool bSele } // Ensure that neither the level nor the actor is being destroyed or is unreachable - EObjectFlags InvalidSelectableFlags = RF_PendingKill|RF_BeginDestroyed|RF_Unreachable; - if( Actor->GetLevel()->HasAnyFlags(InvalidSelectableFlags) ) + const EObjectFlags InvalidSelectableFlags = RF_BeginDestroyed; + if (Actor->GetLevel()->HasAnyFlags(InvalidSelectableFlags) || Actor->GetLevel()->IsPendingKillOrUnreachable()) { UE_LOG(LogEditorSelectUtils, Warning, TEXT("SelectActor: %s (%s)"), TEXT("The requested operation could not be completed because the level has invalid flags."),*Actor->GetActorLabel()); return false; } - if( Actor->HasAnyFlags(InvalidSelectableFlags) ) + if (Actor->HasAnyFlags(InvalidSelectableFlags) || Actor->IsPendingKillOrUnreachable()) { UE_LOG(LogEditorSelectUtils, Warning, TEXT("SelectActor: %s (%s)"), TEXT("The requested operation could not be completed because the actor has invalid flags."),*Actor->GetActorLabel()); return false; diff --git a/Engine/Source/Editor/UnrealEd/Private/EditorServer.cpp b/Engine/Source/Editor/UnrealEd/Private/EditorServer.cpp index ee559ef20adf..bb23e0dc0ae7 100644 --- a/Engine/Source/Editor/UnrealEd/Private/EditorServer.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/EditorServer.cpp @@ -1861,7 +1861,8 @@ void UEditorEngine::EditorDestroyWorld( FWorldContext & Context, const FText& Cl if (ContextWorld->WorldType != EWorldType::Preview && ContextWorld->WorldType != EWorldType::Inactive) { // Go away, come again never! - ContextWorld->ClearFlags(RF_Standalone | RF_RootSet | RF_Transactional); + ContextWorld->ClearFlags(RF_Standalone | RF_Transactional); + ContextWorld->RemoveFromRoot(); // If this was a memory-only world, we should inform the asset registry that this asset is going away forever. if (WorldPackage) diff --git a/Engine/Source/Editor/UnrealEd/Private/Fbx/FbxSkeletalMeshImport.cpp b/Engine/Source/Editor/UnrealEd/Private/Fbx/FbxSkeletalMeshImport.cpp index a7c748b02ef7..2509eb676707 100644 --- a/Engine/Source/Editor/UnrealEd/Private/Fbx/FbxSkeletalMeshImport.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/Fbx/FbxSkeletalMeshImport.cpp @@ -1102,7 +1102,7 @@ USkeletalMesh* UnFbx::FFbxImporter::ImportSkeletalMesh(UObject* InParent, TArray if ( !FbxShapeArray ) { - UObject* ExistingObject = StaticFindObjectFast(UObject::StaticClass(), InParent, *Name.ToString(), false, false, RF_PendingKill); + UObject* ExistingObject = StaticFindObjectFast(UObject::StaticClass(), InParent, *Name.ToString(), false, false, RF_NoFlags, EInternalObjectFlags::PendingKill); USkeletalMesh* ExistingSkelMesh = Cast(ExistingObject); if (ExistingSkelMesh) diff --git a/Engine/Source/Editor/UnrealEd/Private/Kismet2/BlueprintEditorUtils.cpp b/Engine/Source/Editor/UnrealEd/Private/Kismet2/BlueprintEditorUtils.cpp index a12effca5734..46e379d4e9f8 100644 --- a/Engine/Source/Editor/UnrealEd/Private/Kismet2/BlueprintEditorUtils.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/Kismet2/BlueprintEditorUtils.cpp @@ -2591,7 +2591,8 @@ void FBlueprintEditorUtils::RemoveGraph(UBlueprint* Blueprint, class UEdGraph* G GraphToRemove->GetSchema()->HandleGraphBeingDeleted(*GraphToRemove); GraphToRemove->Rename(NULL, Blueprint->GetOuter(), REN_DoNotDirty | REN_DontCreateRedirectors); - GraphToRemove->ClearFlags(RF_Standalone | RF_RootSet | RF_Public); + GraphToRemove->ClearFlags(RF_Standalone | RF_Public); + GraphToRemove->RemoveFromRoot(); if (Flags & EGraphRemoveFlags::MarkTransient) { @@ -2887,7 +2888,7 @@ void FBlueprintEditorUtils::GetDependentBlueprints(UBlueprint* Blueprint, TArray { // we know the class is correct so a fast cast is ok here UBlueprint* TestBP = (UBlueprint*) *ObjIt; - if (TestBP && !TestBP->HasAnyFlags(RF_PendingKill)) + if (TestBP && !TestBP->IsPendingKill()) { EnsureCachedDependenciesUpToDate(TestBP); @@ -6916,7 +6917,7 @@ bool FBlueprintEditorUtils::KismetDiagnosticExec(const TCHAR* InStream, FOutputD for( FObjectIterator it; it; ++it ) { UObject* CurrObj = *it; - if( CurrObj->HasAnyFlags(RF_RootSet) ) + if( CurrObj->IsRooted() ) { UE_LOG(LogBlueprintDebug, Log, TEXT(" - %s"), *CurrObj->GetFullName()); } @@ -7245,7 +7246,7 @@ void FBlueprintEditorUtils::PostEditChangeBlueprintActors(UBlueprint* Blueprint, const bool bIncludeDerivedClasses = false; TArray MatchingBlueprintObjects; - GetObjectsOfClass(Blueprint->GeneratedClass, MatchingBlueprintObjects, bIncludeDerivedClasses, (RF_ClassDefaultObject | RF_PendingKill)); + GetObjectsOfClass(Blueprint->GeneratedClass, MatchingBlueprintObjects, bIncludeDerivedClasses, RF_ClassDefaultObject, EInternalObjectFlags::PendingKill); for (auto ObjIt : MatchingBlueprintObjects) { @@ -7427,7 +7428,7 @@ void FBlueprintEditorUtils::AnalyticsTrackNewNode( UEdGraphNode *NewNode ) bool FBlueprintEditorUtils::IsObjectADebugCandidate( AActor* InActorObject, UBlueprint* InBlueprint, bool bInDisallowDerivedBlueprints ) { - const bool bPassesFlags = !InActorObject->HasAnyFlags(RF_PendingKill | RF_ClassDefaultObject); + const bool bPassesFlags = !InActorObject->HasAnyFlags(RF_ClassDefaultObject) && !InActorObject->IsPendingKill(); bool bCanDebugThisObject; if( bInDisallowDerivedBlueprints == true ) { diff --git a/Engine/Source/Editor/UnrealEd/Private/Kismet2/EnumEditorUtils.cpp b/Engine/Source/Editor/UnrealEd/Private/Kismet2/EnumEditorUtils.cpp index 3312ba9ff8f7..064b120b6d0f 100644 --- a/Engine/Source/Editor/UnrealEd/Private/Kismet2/EnumEditorUtils.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/Kismet2/EnumEditorUtils.cpp @@ -262,7 +262,7 @@ void FEnumEditorUtils::BroadcastChanges(const UUserDefinedEnum* Enum, const TArr { return Node && (NULL != Cast(Node->GetOuter())) - && !Node->HasAnyFlags(RF_Transient | RF_PendingKill); + && !Node->HasAnyFlags(RF_Transient) && !Node->IsPendingKill(); } }; diff --git a/Engine/Source/Editor/UnrealEd/Private/Kismet2/Kismet2.cpp b/Engine/Source/Editor/UnrealEd/Private/Kismet2/Kismet2.cpp index 02317dfa7b76..eefb48aab081 100644 --- a/Engine/Source/Editor/UnrealEd/Private/Kismet2/Kismet2.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/Kismet2/Kismet2.cpp @@ -245,7 +245,7 @@ void FBlueprintUnloader::UnloadBlueprint(const bool bResetPackage) // make sure the blueprint is properly trashed (remove it from the package) UnloadingBp->SetFlags(RF_Transient); - UnloadingBp->ClearFlags(RF_Standalone | RF_RootSet | RF_Transactional); + UnloadingBp->ClearFlags(RF_Standalone | RF_Transactional); UnloadingBp->RemoveFromRoot(); UnloadingBp->MarkPendingKill(); // if it's in the undo buffer, then we have to clear that... @@ -660,12 +660,12 @@ bool FKismetEditorUtilities::IsReferencedByUndoBuffer(UBlueprint* Blueprint) { UObject* BlueprintObj = Blueprint; FReferencerInformationList ReferencesIncludingUndo; - IsReferenced(BlueprintObj, GARBAGE_COLLECTION_KEEPFLAGS, /*bCheckSubObjects =*/true, &ReferencesIncludingUndo); + IsReferenced(BlueprintObj, GARBAGE_COLLECTION_KEEPFLAGS, EInternalObjectFlags::GarbageCollectionKeepFlags, /*bCheckSubObjects =*/true, &ReferencesIncludingUndo); FReferencerInformationList ReferencesExcludingUndo; // Determine the in-memory references, *excluding* the undo buffer GEditor->Trans->DisableObjectSerialization(); - IsReferenced(BlueprintObj, GARBAGE_COLLECTION_KEEPFLAGS, /*bCheckSubObjects =*/true, &ReferencesExcludingUndo); + IsReferenced(BlueprintObj, GARBAGE_COLLECTION_KEEPFLAGS, EInternalObjectFlags::GarbageCollectionKeepFlags, /*bCheckSubObjects =*/true, &ReferencesExcludingUndo); GEditor->Trans->EnableObjectSerialization(); // see if this object is the transaction buffer - set a flag so we know we need to clear the undo stack diff --git a/Engine/Source/Editor/UnrealEd/Private/ObjectTools.cpp b/Engine/Source/Editor/UnrealEd/Private/ObjectTools.cpp index 8f8a1ca9abc8..1830eddf21aa 100644 --- a/Engine/Source/Editor/UnrealEd/Private/ObjectTools.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/ObjectTools.cpp @@ -494,6 +494,30 @@ namespace ObjectTools TArray ReplaceableObjects; // Objects whose references could not be successfully replaced TArray UnreplaceableObjects; + + void AppendUnique(const FForceReplaceInfo& ForceReplaceInfo) + { + const TArray& ForceReplaceInfoDirtiedPackages = ForceReplaceInfo.DirtiedPackages; + DirtiedPackages.Reserve(DirtiedPackages.Num() + ForceReplaceInfoDirtiedPackages.Num()); + for (UPackage* Package : ForceReplaceInfoDirtiedPackages) + { + DirtiedPackages.AddUnique(Package); + } + + const TArray& ForceReplaceInfoReplaceableObjects = ForceReplaceInfo.ReplaceableObjects; + ReplaceableObjects.Reserve(ReplaceableObjects.Num() + ForceReplaceInfoReplaceableObjects.Num()); + for (UObject* Object : ForceReplaceInfoReplaceableObjects) + { + ReplaceableObjects.Add(Object); + } + + const TArray& ForceReplaceInfoUnreplaceableObjects = ForceReplaceInfo.UnreplaceableObjects; + UnreplaceableObjects.Reserve(UnreplaceableObjects.Num() + ForceReplaceInfoUnreplaceableObjects.Num()); + for (UObject* Object : ForceReplaceInfoUnreplaceableObjects) + { + UnreplaceableObjects.Add(Object); + } + } }; /** @@ -630,7 +654,7 @@ namespace ObjectTools // If an object is "unreplaceable" store it separately to warn the user about later else { - OutInfo.UnreplaceableObjects.Add( CurObjToReplace ); + OutInfo.UnreplaceableObjects.AddUnique(CurObjToReplace); } } } @@ -789,12 +813,39 @@ namespace ObjectTools } FForceReplaceInfo ReplaceInfo; + FForceReplaceInfo GeneratedClassReplaceInfo; + // Scope the reregister context below to complete after object deletion and before garbage collection { // Replacing references inside already loaded objects could cause rendering issues, so globally detach all components from their scenes for now FGlobalComponentReregisterContext ReregisterContext; - ForceReplaceReferences( ObjectToConsolidateTo, ObjectsToConsolidate, ReplaceInfo ); + ForceReplaceReferences(ObjectToConsolidateTo, ObjectsToConsolidate, ReplaceInfo); + + if (UBlueprint* ObjectToConsolidateTo_BP = Cast(ObjectToConsolidateTo)) + { + // Replace all UClass/TSubClassOf properties of generated class. + TArray ObjectsToConsolidate_BP; + TArray OldGeneratedClasses; + ObjectsToConsolidate_BP.Reserve(ObjectsToConsolidate.Num()); + OldGeneratedClasses.Reserve(ObjectsToConsolidate.Num()); + for (UObject* ObjectToConsolidate : ObjectsToConsolidate) + { + UClass* OldGeneratedClass = Cast(ObjectToConsolidate)->GeneratedClass; + ObjectsToConsolidate_BP.Add(OldGeneratedClass); + OldGeneratedClasses.Add(OldGeneratedClass); + } + + ForceReplaceReferences(ObjectToConsolidateTo_BP->GeneratedClass, ObjectsToConsolidate_BP, GeneratedClassReplaceInfo); + + // Repair the references of GeneratedClass on the object being consolidated so they can be properly disposed of upon deletion. + for (int32 Index = 0, MaxIndex = ObjectsToConsolidate.Num(); Index < MaxIndex; ++Index) + { + Cast(ObjectsToConsolidate[Index])->GeneratedClass = OldGeneratedClasses[Index]; + } + + ReplaceInfo.AppendUnique(GeneratedClassReplaceInfo); + } DirtiedPackages.Append( ReplaceInfo.DirtiedPackages ); UnconsolidatableObjects.Append( ReplaceInfo.UnreplaceableObjects ); } @@ -859,7 +910,10 @@ namespace ObjectTools Redirector->DestinationObject = ObjectToConsolidateTo; // Keep track of the object name so we can rename the redirector later - RedirectorToObjectNameMap.Add(Redirector, CurObjName); + if (!RedirectorToObjectNameMap.FindKey(CurObjName)) + { + RedirectorToObjectNameMap.Add(Redirector, CurObjName); + } // If consolidating blueprints, make sure redirectors are created for the consolidated blueprint class and CDO if ( BlueprintToConsolidateTo != NULL && BlueprintToConsolidate != NULL ) @@ -1007,19 +1061,19 @@ namespace ObjectTools FReferencerInformationList Refs; - if ( IsReferenced( Object,RF_Native | RF_Public, true, &Refs) ) + if (IsReferenced(Object, RF_Public, EInternalObjectFlags::Native, true, &Refs)) { FStringOutputDevice Ar; - Object->OutputReferencers( Ar, &Refs ); - UE_LOG(LogObjectTools, Warning, TEXT("%s"), *Ar ); // also print the objects to the log so you can actually utilize the data - + Object->OutputReferencers(Ar, &Refs); + UE_LOG(LogObjectTools, Warning, TEXT("%s"), *Ar); // also print the objects to the log so you can actually utilize the data + // Display a dialog containing all referencers; the dialog is designed to destroy itself upon being closed, so this // allocation is ok and not a memory leak - SGenericDialogWidget::OpenDialog(NSLOCTEXT("ObjectTools", "ShowReferencers", "Show Referencers"), SNew(STextBlock).Text( FText::FromString(Ar) )); + SGenericDialogWidget::OpenDialog(NSLOCTEXT("ObjectTools", "ShowReferencers", "Show Referencers"), SNew(STextBlock).Text(FText::FromString(Ar))); } else { - FMessageDialog::Open( EAppMsgType::Ok, FText::Format(NSLOCTEXT("UnrealEd", "ObjectNotReferenced", "Object '{0}' Is Not Referenced"), FText::FromString(Object->GetName())) ); + FMessageDialog::Open(EAppMsgType::Ok, FText::Format(NSLOCTEXT("UnrealEd", "ObjectNotReferenced", "Object '{0}' Is Not Referenced"), FText::FromString(Object->GetName()))); } GEditor->GetSelectedObjects()->Select( Object ); @@ -1239,7 +1293,7 @@ namespace ObjectTools { if(Object) { - if(IsReferenced(Object,RF_Native | RF_Public)) + if(IsReferenced(Object, RF_Public, EInternalObjectFlags::Native)) { TArray ObjectsToSelect; @@ -1392,13 +1446,13 @@ namespace ObjectTools if ( bPerformReferenceCheck ) { FReferencerInformationList FoundReferences; - bIsReferenced = IsReferenced(Package, GARBAGE_COLLECTION_KEEPFLAGS, true, &FoundReferences); + bIsReferenced = IsReferenced(Package, GARBAGE_COLLECTION_KEEPFLAGS, EInternalObjectFlags::GarbageCollectionKeepFlags, true, &FoundReferences); if ( bIsReferenced ) { // determine whether the transaction buffer is the only thing holding a reference to the object // and if so, offer the user the option to reset the transaction buffer. GEditor->Trans->DisableObjectSerialization(); - bIsReferenced = IsReferenced(Package, GARBAGE_COLLECTION_KEEPFLAGS, true, &FoundReferences); + bIsReferenced = IsReferenced(Package, GARBAGE_COLLECTION_KEEPFLAGS, EInternalObjectFlags::GarbageCollectionKeepFlags, true, &FoundReferences); GEditor->Trans->EnableObjectSerialization(); // only ref to this object is the transaction buffer, clear the transaction buffer @@ -1783,13 +1837,13 @@ namespace ObjectTools FReferencerInformationList Refs; // Check and see whether we are referenced by any objects that won't be garbage collected. - bool bIsReferenced = IsReferenced( ObjectToDelete, GARBAGE_COLLECTION_KEEPFLAGS, true, &Refs ); + bool bIsReferenced = IsReferenced(ObjectToDelete, GARBAGE_COLLECTION_KEEPFLAGS, EInternalObjectFlags::GarbageCollectionKeepFlags, true, &Refs); if ( bIsReferenced ) { // determine whether the transaction buffer is the only thing holding a reference to the object // and if so, offer the user the option to reset the transaction buffer. GEditor->Trans->DisableObjectSerialization(); - bIsReferenced = IsReferenced( ObjectToDelete, GARBAGE_COLLECTION_KEEPFLAGS, true, &Refs ); + bIsReferenced = IsReferenced(ObjectToDelete, GARBAGE_COLLECTION_KEEPFLAGS, EInternalObjectFlags::GarbageCollectionKeepFlags, true, &Refs); GEditor->Trans->EnableObjectSerialization(); // only ref to this object is the transaction buffer, clear the transaction buffer @@ -3453,7 +3507,7 @@ namespace ObjectTools // If the object is not flagged for GC and it is in one of the level packages do an indepth search to see what references it. - if( !Obj->HasAnyFlags( RF_PendingKill | RF_Unreachable ) && LevelPackages.Find( Obj->GetOutermost() ) != NULL ) + if( !Obj->IsPendingKillOrUnreachable() && LevelPackages.Find( Obj->GetOutermost() ) != NULL ) { // Determine if the current object is in one of the search levels. This is the same as UObject::IsIn except that we can // search through many levels at once. diff --git a/Engine/Source/Editor/UnrealEd/Private/PackageTools.cpp b/Engine/Source/Editor/UnrealEd/Private/PackageTools.cpp index 72eb0da3c00b..0e9d51508db8 100644 --- a/Engine/Source/Editor/UnrealEd/Private/PackageTools.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/PackageTools.cpp @@ -41,7 +41,7 @@ namespace PackageTools GetObjectsWithOuter(PackageBeingUnloaded, ObjectsInPackage); for ( UObject* Object : ObjectsInPackage ) { - if ( !Object->HasAnyFlags(RF_Unreachable) ) + if ( !Object->IsUnreachable() ) { if ( ObjectsThatHadFlagsCleared.Find(Object) ) { diff --git a/Engine/Source/Editor/UnrealEd/Private/Settings/SettingsClasses.cpp b/Engine/Source/Editor/UnrealEd/Private/Settings/SettingsClasses.cpp index 18882b451a8c..d57976b51ceb 100644 --- a/Engine/Source/Editor/UnrealEd/Private/Settings/SettingsClasses.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/Settings/SettingsClasses.cpp @@ -2,6 +2,7 @@ #include "UnrealEd.h" #include "ISourceControlModule.h" +#include "CrashReporterSettings.h" #include "Components/BillboardComponent.h" #include "AI/Navigation/NavigationSystem.h" #include "Components/ArrowComponent.h" @@ -567,4 +568,11 @@ bool UProjectPackagingSettings::CanEditChange( const UProperty* InProperty ) con return Super::CanEditChange(InProperty); } +/* UCrashReporterSettings interface +*****************************************************************************/ +UCrashReporterSettings::UCrashReporterSettings(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) +{ +} + #undef LOCTEXT_NAMESPACE diff --git a/Engine/Source/Editor/UnrealEd/Private/SourceCodeNavigation.cpp b/Engine/Source/Editor/UnrealEd/Private/SourceCodeNavigation.cpp index 6d1b818f422e..24cf3fa5720a 100644 --- a/Engine/Source/Editor/UnrealEd/Private/SourceCodeNavigation.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/SourceCodeNavigation.cpp @@ -1427,16 +1427,16 @@ bool FSourceCodeNavigation::NavigateToProperty( UProperty* InProperty ) { bool bResult = false; - if( InProperty && InProperty->HasAllFlags( RF_Native )) + if (InProperty && InProperty->IsNative()) { FString SourceFilePath; - const bool bFileLocated = FindClassHeaderPath( InProperty, SourceFilePath ) && - IFileManager::Get().FileSize( *SourceFilePath ) != INDEX_NONE; + const bool bFileLocated = FindClassHeaderPath(InProperty, SourceFilePath) && + IFileManager::Get().FileSize(*SourceFilePath) != INDEX_NONE; - if( bFileLocated ) + if (bFileLocated) { - const FString AbsoluteSourcePath = IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead( *SourceFilePath ); - bResult = OpenSourceFile( AbsoluteSourcePath ); + const FString AbsoluteSourcePath = IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*SourceFilePath); + bResult = OpenSourceFile(AbsoluteSourcePath); } } return bResult; diff --git a/Engine/Source/Editor/UnrealEd/Private/StaticLightingSystem/StaticLightingSystem.cpp b/Engine/Source/Editor/UnrealEd/Private/StaticLightingSystem/StaticLightingSystem.cpp index 62408fa0dc96..6ac3bf437426 100644 --- a/Engine/Source/Editor/UnrealEd/Private/StaticLightingSystem/StaticLightingSystem.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/StaticLightingSystem/StaticLightingSystem.cpp @@ -487,7 +487,7 @@ bool FStaticLightingSystem::BeginLightmassProcess() (*LightIt)->GetWorld()->DestroyActor(*LightIt); } - for(TObjectIterator LightIt(RF_ClassDefaultObject|RF_PendingKill);LightIt;++LightIt) + for (TObjectIterator LightIt(RF_ClassDefaultObject, /** bIncludeDerivedClasses */ true, /** InternalExcludeFlags */ EInternalObjectFlags::PendingKill); LightIt; ++LightIt) { ULightComponentBase* const Light = *LightIt; const bool bLightIsInWorld = Light->GetOwner() diff --git a/Engine/Source/Editor/UnrealEd/Private/Tests/BlueprintAutomationTests.cpp b/Engine/Source/Editor/UnrealEd/Private/Tests/BlueprintAutomationTests.cpp index d59e40016128..dd3ce8485a19 100644 --- a/Engine/Source/Editor/UnrealEd/Private/Tests/BlueprintAutomationTests.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/Tests/BlueprintAutomationTests.cpp @@ -321,7 +321,7 @@ public: { UE_LOG(LogBlueprintAutomationTests, Log, TEXT("No need to unload '%s' from the transient package."), *BlueprintObj->GetName()); } - else if (OldPackage->HasAnyFlags(RF_RootSet) || BlueprintObj->HasAnyFlags(RF_RootSet)) + else if (OldPackage->IsRooted() || BlueprintObj->IsRooted()) { UE_LOG(LogBlueprintAutomationTests, Error, TEXT("Cannot unload '%s' when its root is set (it will not be garbage collected, leaving it in an erroneous state)."), *OldPackage->GetName()); } @@ -346,7 +346,7 @@ public: // make sure the blueprint is properly trashed so we can rerun tests on it BlueprintObj->SetFlags(RF_Transient); - BlueprintObj->ClearFlags(RF_Standalone | RF_RootSet | RF_Transactional); + BlueprintObj->ClearFlags(RF_Standalone | RF_Transactional); BlueprintObj->RemoveFromRoot(); BlueprintObj->MarkPendingKill(); @@ -362,7 +362,7 @@ public: // holding onto any references to the blueprints we want unloaded GEditor->Trans->Reset(NSLOCTEXT("BpAutomation", "BpAutomationTest", "Blueprint Automation Test")); #endif // #if WITH_EDITOR - CollectGarbage(RF_Native); + CollectGarbage(RF_NoFlags); } } @@ -430,7 +430,7 @@ public: bool bHasReferences = false; FReferencerInformationList Refs; - if (IsReferenced(Obj, RF_Native | RF_Public, true, &Refs)) + if (IsReferenced(Obj, RF_Public, EInternalObjectFlags::None, true, &Refs)) { ExternalRefsOut = Refs.ExternalReferences; bHasReferences = true; @@ -783,7 +783,7 @@ public: // holding onto any references to the blueprints we want unloaded GEditor->Trans->Reset(NSLOCTEXT("BpAutomation", "BpAutomationTest", "Blueprint Automation Test")); #endif // #if WITH_EDITOR - CollectGarbage(RF_Native); + CollectGarbage(RF_NoFlags); } } @@ -1220,7 +1220,7 @@ bool FBlueprintReparentTest::RunTest(const FString& BlueprintAssetPath) GEditor->Trans->Reset(NSLOCTEXT("BpAutomation", "ReparentTest", "Reparent Blueprint Test")); #endif // #if WITH_EDITOR // make sure the unloaded blueprints are properly flushed (for future tests) - CollectGarbage(RF_Native); + CollectGarbage(RF_NoFlags); } return !bTestFailed; @@ -1252,7 +1252,7 @@ void FBlueprintRenameAndCloneTest::GetTests(TArray& OutBeautifiedNames, if (UPackage* ExistingPackage = FindPackage(NULL, *PackageName)) { - if (ExistingPackage->HasAnyFlags(RF_RootSet)) + if (ExistingPackage->IsRooted()) { continue; } @@ -1349,7 +1349,7 @@ bool FBlueprintRenameAndCloneTest::RunTest(const FString& BlueprintAssetPath) GEditor->Trans->Reset(NSLOCTEXT("BpAutomation", "RenameCloneTest", "Rename and Clone Test")); #endif // #if WITH_EDITOR // make sure the unloaded blueprints are properly flushed (for future tests) - CollectGarbage(RF_Native); + CollectGarbage(RF_NoFlags); } return !bTestFailed; diff --git a/Engine/Source/Editor/UnrealEd/Private/TexAlignTools.cpp b/Engine/Source/Editor/UnrealEd/Private/TexAlignTools.cpp index becef2a62252..5315257e9c4b 100644 --- a/Engine/Source/Editor/UnrealEd/Private/TexAlignTools.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/TexAlignTools.cpp @@ -454,11 +454,14 @@ void FTexAlignTools::Init() { // Create the list of aligners. Aligners.Empty(); - Aligners.Add(NewObject(GetTransientPackage(), NAME_None, RF_Public | RF_RootSet | RF_Standalone)); - Aligners.Add(NewObject(GetTransientPackage(), NAME_None, RF_Public | RF_RootSet | RF_Standalone)); - Aligners.Add(NewObject(GetTransientPackage(), NAME_None, RF_Public | RF_RootSet | RF_Standalone)); - Aligners.Add(NewObject(GetTransientPackage(), NAME_None, RF_Public | RF_RootSet | RF_Standalone)); - + Aligners.Add(NewObject(GetTransientPackage(), NAME_None, RF_Public | RF_Standalone)); + Aligners.Add(NewObject(GetTransientPackage(), NAME_None, RF_Public | RF_Standalone)); + Aligners.Add(NewObject(GetTransientPackage(), NAME_None, RF_Public | RF_Standalone)); + Aligners.Add(NewObject(GetTransientPackage(), NAME_None, RF_Public | RF_Standalone)); + for (UObject* Aligner : Aligners) + { + Aligner->AddToRoot(); + } FEditorDelegates::FitTextureToSurface.AddRaw(this, &FTexAlignTools::OnEditorFitTextureToSurface); } diff --git a/Engine/Source/Editor/UnrealEd/Public/CrashReporterSettings.h b/Engine/Source/Editor/UnrealEd/Public/CrashReporterSettings.h new file mode 100644 index 000000000000..cc92f55aef2f --- /dev/null +++ b/Engine/Source/Editor/UnrealEd/Public/CrashReporterSettings.h @@ -0,0 +1,39 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +/*============================================================================= + CrashReporterSettings.h: Declares the UCrashReporterSettings class. +=============================================================================*/ + +#pragma once + +#include "CrashReporterSettings.generated.h" + +/** + * Implements per-project cooker settings exposed to the editor + */ +UCLASS(config = EditorPerProjectUserSettings) +class UNREALED_API UCrashReporterSettings + : public UObject +{ + GENERATED_UCLASS_BODY() + +public: + + /** + * Directory for uploading locally built binaries and .PDB files + */ + UPROPERTY(EditAnywhere, config, Category = CrashReporter) + FString UploadSymbolsPath; + + /** + * Local downstream PDB storage path (used for caching remote .PDB files) + */ + UPROPERTY(EditAnywhere, config, Category = CrashReporter) + FString DownstreamStorage; + + /** + * Remote PDB storage (directory path or http/https URL) + */ + UPROPERTY(EditAnywhere, config, Category = CrashReporter) + TArray RemoteStorage; +}; diff --git a/Engine/Source/Editor/UnrealEd/Public/PackageHelperFunctions.h b/Engine/Source/Editor/UnrealEd/Public/PackageHelperFunctions.h index cbe7c849e0ba..5ecc69f529a5 100644 --- a/Engine/Source/Editor/UnrealEd/Public/PackageHelperFunctions.h +++ b/Engine/Source/Editor/UnrealEd/Public/PackageHelperFunctions.h @@ -263,7 +263,7 @@ void DoActionToAllPackages( UCommandlet* Commandlet, const FString& Params ) if( ( (++GCIndex % 10) == 0 ) || ( bGCEveryPackage == true ) ) { - CollectGarbage(RF_Native); + CollectGarbage(RF_NoFlags); } } } diff --git a/Engine/Source/Editor/WorldBrowser/Private/Tiles/WorldTileModel.cpp b/Engine/Source/Editor/WorldBrowser/Private/Tiles/WorldTileModel.cpp index 43bdac0e06d0..7714aa7f41ba 100644 --- a/Engine/Source/Editor/WorldBrowser/Private/Tiles/WorldTileModel.cpp +++ b/Engine/Source/Editor/WorldBrowser/Private/Tiles/WorldTileModel.cpp @@ -28,7 +28,8 @@ FWorldTileModel::FWorldTileModel(FWorldTileCollectionModel& InWorldModel, int32 UWorldComposition* WorldComposition = LevelCollectionModel.GetWorld()->WorldComposition; // Tile display details object - TileDetails = NewObject(GetTransientPackage(), NAME_None, RF_RootSet | RF_Transient); + TileDetails = NewObject(GetTransientPackage(), NAME_None, RF_Transient); + TileDetails->AddToRoot(); // Subscribe to tile properties changes TileDetails->PositionChangedEvent.AddRaw(this, &FWorldTileModel::OnPositionPropertyChanged); diff --git a/Engine/Source/Programs/CrashReporter/CrashReportWebSite/CrashReportWebSite.sln b/Engine/Source/Programs/CrashReporter/CrashReportWebSite/CrashReportWebSite.sln index abf100a7da24..ff45d4d3deda 100644 --- a/Engine/Source/Programs/CrashReporter/CrashReportWebSite/CrashReportWebSite.sln +++ b/Engine/Source/Programs/CrashReporter/CrashReportWebSite/CrashReportWebSite.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 -VisualStudioVersion = 12.0.30324.0 +VisualStudioVersion = 12.0.40121.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrashReportWebSite", "CrashReportWebSite.csproj", "{7A6D44E8-D8F2-4540-8A4A-0EF1A87D65AB}" EndProject @@ -12,26 +12,19 @@ EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU - Development|Any CPU = Development|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {7A6D44E8-D8F2-4540-8A4A-0EF1A87D65AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7A6D44E8-D8F2-4540-8A4A-0EF1A87D65AB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7A6D44E8-D8F2-4540-8A4A-0EF1A87D65AB}.Development|Any CPU.ActiveCfg = Development|Any CPU - {7A6D44E8-D8F2-4540-8A4A-0EF1A87D65AB}.Development|Any CPU.Build.0 = Development|Any CPU {7A6D44E8-D8F2-4540-8A4A-0EF1A87D65AB}.Release|Any CPU.ActiveCfg = Release|Any CPU {7A6D44E8-D8F2-4540-8A4A-0EF1A87D65AB}.Release|Any CPU.Build.0 = Release|Any CPU {B83DE8C9-9429-4765-86BD-8EEEA89B138F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B83DE8C9-9429-4765-86BD-8EEEA89B138F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B83DE8C9-9429-4765-86BD-8EEEA89B138F}.Development|Any CPU.ActiveCfg = Development|Any CPU - {B83DE8C9-9429-4765-86BD-8EEEA89B138F}.Development|Any CPU.Build.0 = Development|Any CPU {B83DE8C9-9429-4765-86BD-8EEEA89B138F}.Release|Any CPU.ActiveCfg = Development|Any CPU {B83DE8C9-9429-4765-86BD-8EEEA89B138F}.Release|Any CPU.Build.0 = Development|Any CPU {5D7D66E8-8C76-4AF9-B3EC-2EF03421D730}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5D7D66E8-8C76-4AF9-B3EC-2EF03421D730}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5D7D66E8-8C76-4AF9-B3EC-2EF03421D730}.Development|Any CPU.ActiveCfg = Development|Any CPU - {5D7D66E8-8C76-4AF9-B3EC-2EF03421D730}.Development|Any CPU.Build.0 = Development|Any CPU {5D7D66E8-8C76-4AF9-B3EC-2EF03421D730}.Release|Any CPU.ActiveCfg = Development|Any CPU {5D7D66E8-8C76-4AF9-B3EC-2EF03421D730}.Release|Any CPU.Build.0 = Development|Any CPU EndGlobalSection diff --git a/Engine/Source/Programs/CrashReporter/CrashReportWebSite/Models/CrashReport.cs b/Engine/Source/Programs/CrashReporter/CrashReportWebSite/Models/CrashReport.cs index 97626301151d..ebaa04c345a0 100644 --- a/Engine/Source/Programs/CrashReporter/CrashReportWebSite/Models/CrashReport.cs +++ b/Engine/Source/Programs/CrashReporter/CrashReportWebSite/Models/CrashReport.cs @@ -181,7 +181,8 @@ namespace Tools.CrashReporter.CrashReportWebSite.Models { this.AffectedVersions.Add( Crash.BuildVersion ); } - if( !string.IsNullOrEmpty( Crash.Branch ) && Crash.Branch.StartsWith( "UE4" ) ) + // Depot || Stream + if (!string.IsNullOrEmpty( Crash.Branch ) && ( Crash.Branch.StartsWith( "UE4" ) || Crash.Branch.StartsWith( "//UE4" ) )) { this.BranchesFoundIn.Add( Crash.Branch ); } @@ -273,7 +274,16 @@ namespace Tools.CrashReporter.CrashReportWebSite.Models { if( !string.IsNullOrEmpty(BranchName) ) { - this.ToJiraBranches.Add( CrashReporterConstants.P4_DEPOT_PREFIX + BranchName ); + // Stream + if (BranchName.StartsWith( "//UE4" )) + { + this.ToJiraBranches.Add( BranchName ); + } + // Depot + else + { + this.ToJiraBranches.Add( CrashReporterConstants.P4_DEPOT_PREFIX + BranchName ); + } } } diff --git a/Engine/Source/Programs/CrashReporter/CrashReportWebSite/Models/CrashRepository.cs b/Engine/Source/Programs/CrashReporter/CrashReportWebSite/Models/CrashRepository.cs index 473b8a042b81..f75ce0f1af25 100644 --- a/Engine/Source/Programs/CrashReporter/CrashReportWebSite/Models/CrashRepository.cs +++ b/Engine/Source/Programs/CrashReporter/CrashReportWebSite/Models/CrashRepository.cs @@ -89,7 +89,8 @@ namespace Tools.CrashReporter.CrashReportWebSite.Models { var BranchList = Context.Crashes .Where( n => n.TimeOfCrash > DateTime.Now.AddMonths( -3 ) ) - .Where( n => n.Branch.StartsWith( "UE4" ) ) + // Depot || Stream + .Where( n => n.Branch.StartsWith( "UE4" ) || n.Branch.StartsWith( "//UE4" ) ) .Select( n => n.Branch ) .Distinct() .ToList(); @@ -105,6 +106,25 @@ namespace Tools.CrashReporter.CrashReportWebSite.Models } } + private static List PlatformsAsListItems = null; + /// + /// Static list of platforms for filtering + /// + public static List GetPlatformsAsListItems() + { + if( PlatformsAsListItems == null ) + { + string[] PlatformNames = { "Win64", "Win32", "Mac", "Linux", "PS4", "XboxOne" }; + List Platforms = new List( PlatformNames ); + + PlatformsAsListItems = Platforms + .Select( listitem => new SelectListItem { Selected = false, Text = listitem, Value = listitem } ) + .ToList(); + PlatformsAsListItems.Insert( 0, new SelectListItem { Selected = true, Text = "", Value = "" } ); + } + return PlatformsAsListItems; + + } private static DateTime LastVersionDate = DateTime.UtcNow.AddDays( -1 ); private static List VersionsAsSelectList = null; @@ -397,6 +417,7 @@ namespace Tools.CrashReporter.CrashReportWebSite.Models DateTo = (long)( FormData.DateTo - CrashesViewModel.Epoch ).TotalMilliseconds, BranchName = FormData.BranchName, VersionName = FormData.VersionName, + PlatformName = FormData.PlatformName, GameName = FormData.GameName, GroupCounts = GroupCounts, RealUserName = UniqueUser != null ? UniqueUser.ToString() : null, @@ -815,6 +836,17 @@ namespace Tools.CrashReporter.CrashReportWebSite.Models ); } + // Filter by PlatformName + if (!string.IsNullOrEmpty( FormData.PlatformName )) + { + Results = + ( + from CrashDetail in Results + where CrashDetail.PlatformName.Contains( FormData.PlatformName ) + select CrashDetail + ); + } + // Filter by GameName if (!string.IsNullOrEmpty( FormData.GameName )) { diff --git a/Engine/Source/Programs/CrashReporter/CrashReportWebSite/Models/CrashesViewModel.cs b/Engine/Source/Programs/CrashReporter/CrashReportWebSite/Models/CrashesViewModel.cs index d2f1a6766159..acd5b50e4cb9 100644 --- a/Engine/Source/Programs/CrashReporter/CrashReportWebSite/Models/CrashesViewModel.cs +++ b/Engine/Source/Programs/CrashReporter/CrashReportWebSite/Models/CrashesViewModel.cs @@ -68,6 +68,9 @@ namespace Tools.CrashReporter.CrashReportWebSite.Models /// The version to filter by. public string VersionName { get; set; } + /// The platform to filter by. + public string PlatformName { get; set; } + /// The name of the game to filter by. public string GameName { get; set; } @@ -83,6 +86,9 @@ namespace Tools.CrashReporter.CrashReportWebSite.Models /// A collection of Version names used in the drop down on the main search form public List VersionNames { get; set; } + /// A collection of Platform names used in the drop down on the main search form + public List PlatformNames { get; set; } + /// The set of statuses a crash could have its status set to. public IEnumerable SetStatus { get { return new List( new string[] { "Unset", "Reviewed", "New", "Coder", "EngineQA", "GameQA" } ); } } @@ -101,6 +107,7 @@ namespace Tools.CrashReporter.CrashReportWebSite.Models DateTime ToDate = DateTime.Today.ToUniversalTime(); BranchNames = CrashRepository.GetBranchesAsListItems(); VersionNames = CrashRepository.GetVersionsAsListItems(); + PlatformNames = CrashRepository.GetPlatformsAsListItems(); DateFrom = (long)( FromDate - Epoch ).TotalMilliseconds; DateTo = (long)( ToDate - Epoch ).TotalMilliseconds; CrashType = "CrashesAsserts"; diff --git a/Engine/Source/Programs/CrashReporter/CrashReportWebSite/Models/FormHelper.cs b/Engine/Source/Programs/CrashReporter/CrashReportWebSite/Models/FormHelper.cs index ca343a144a07..ad5e43e212bf 100644 --- a/Engine/Source/Programs/CrashReporter/CrashReportWebSite/Models/FormHelper.cs +++ b/Engine/Source/Programs/CrashReporter/CrashReportWebSite/Models/FormHelper.cs @@ -68,6 +68,9 @@ namespace Tools.CrashReporter.CrashReportWebSite.Models /// The version name to filter by. public string VersionName = ""; + /// The platform name to filter by. + public string PlatformName = ""; + /// The game to filter by. public string GameName = ""; @@ -184,6 +187,7 @@ namespace Tools.CrashReporter.CrashReportWebSite.Models GetFormParameter( Request, Form, "UserGroup", UserGroup, out UserGroup ); GetFormParameter( Request, Form, "BranchName", BranchName, out BranchName ); GetFormParameter( Request, Form, "VersionName", VersionName, out VersionName ); + GetFormParameter( Request, Form, "PlatformName", PlatformName, out PlatformName ); GetFormParameter( Request, Form, "GameName", GameName, out GameName ); diff --git a/Engine/Source/Programs/CrashReporter/CrashReportWebSite/Views/Crashes/Index.aspx b/Engine/Source/Programs/CrashReporter/CrashReportWebSite/Views/Crashes/Index.aspx index 3c8c4fad0b67..59cc450a6669 100644 --- a/Engine/Source/Programs/CrashReporter/CrashReportWebSite/Views/Crashes/Index.aspx +++ b/Engine/Source/Programs/CrashReporter/CrashReportWebSite/Views/Crashes/Index.aspx @@ -217,6 +217,13 @@ + + +

Filter by Platform

+ + + <%=Html.DropDownListFor( m=>m.PlatformName, Model.PlatformNames )%> + @@ -272,6 +279,7 @@ DateTo = Model.DateTo, BranchName = Model.BranchName, VersionName = Model.VersionName, + PlatformName = Model.PlatformName, GameName = Model.GameName, RealUserName = Model.RealUserName } @@ -297,6 +305,7 @@ DateTo = Model.DateTo, BranchName = Model.BranchName, VersionName = Model.VersionName, + PlatformName = Model.PlatformName, GameName = Model.GameName, RealUserName = Model.RealUserName } @@ -328,6 +337,7 @@ DateTo = Model.DateTo, BranchName = Model.BranchName, VersionName = Model.VersionName, + PlatformName = Model.PlatformName, GameName = Model.GameName, RealUserName = Model.RealUserName } diff --git a/Engine/Source/Programs/CrashReporter/CrashReportWebSite/Views/Helpers/UrlHelperExtension.cs b/Engine/Source/Programs/CrashReporter/CrashReportWebSite/Views/Helpers/UrlHelperExtension.cs index 2f6588a47d80..e36bfbd38acc 100644 --- a/Engine/Source/Programs/CrashReporter/CrashReportWebSite/Views/Helpers/UrlHelperExtension.cs +++ b/Engine/Source/Programs/CrashReporter/CrashReportWebSite/Views/Helpers/UrlHelperExtension.cs @@ -44,6 +44,7 @@ namespace Tools.CrashReporter.CrashReportWebSite.Views.Helpers DateTo = Model.DateTo, BranchName = Model.BranchName, VersionName = Model.VersionName, + PlatformName = Model.PlatformName, GameName = Model.GameName } ); @@ -100,6 +101,7 @@ namespace Tools.CrashReporter.CrashReportWebSite.Views.Helpers DateTo = Model.DateTo, BranchName = Model.BranchName, VersionName = Model.VersionName, + PlatformName = Model.PlatformName, GameName = Model.GameName } ); @@ -139,6 +141,7 @@ namespace Tools.CrashReporter.CrashReportWebSite.Views.Helpers DateTo = Model.DateTo, BranchName = Model.BranchName, VersionName = Model.VersionName, + PlatformName = Model.PlatformName, GameName = Model.GameName } ); diff --git a/Engine/Source/Programs/CrashReporter/CrashReportWebSite/Views/Home/Index.aspx b/Engine/Source/Programs/CrashReporter/CrashReportWebSite/Views/Home/Index.aspx index 825c3db2294b..ad8af2347058 100644 --- a/Engine/Source/Programs/CrashReporter/CrashReportWebSite/Views/Home/Index.aspx +++ b/Engine/Source/Programs/CrashReporter/CrashReportWebSite/Views/Home/Index.aspx @@ -209,6 +209,13 @@ + + +

Filter by Platform

+ + + <%=Html.DropDownListFor( m=>m.PlatformName, Model.PlatformNames )%> + diff --git a/Engine/Source/Programs/UnrealBuildTool/Configuration/BuildConfiguration.cs b/Engine/Source/Programs/UnrealBuildTool/Configuration/BuildConfiguration.cs index a794e904b477..63911c73de63 100644 --- a/Engine/Source/Programs/UnrealBuildTool/Configuration/BuildConfiguration.cs +++ b/Engine/Source/Programs/UnrealBuildTool/Configuration/BuildConfiguration.cs @@ -448,6 +448,13 @@ namespace UnrealBuildTool [XmlConfig] public static bool bUseUBTMakefiles; + /// + /// Enables support for fast UHT parsing by caching results of previous UHT runs. If a module or UnrealHeaderTool.manifest + /// gets changed, all modules up to first changed one get loaded from makefile and the rest is parsed regularly. + /// + [XmlConfig] + public static bool bUseUHTMakefiles; + /// /// Whether DMUCS/Distcc may be used. /// @@ -647,6 +654,10 @@ namespace UnrealBuildTool // NOTE: This feature is new and has a number of known issues (search the code for '@todo ubtmake') bUseUBTMakefiles = true; + // Enables support for caching results of previous UHT runs to speed up iterative development. + // This feature is new and can have issues that weren't discovered in isolated testing so disabling by default. + bUseUHTMakefiles = false; + // Distcc requires some setup - so by default disable it so we don't break local or remote building bAllowDistcc = false; bAllowDistccLocalFallback = true; diff --git a/Engine/Source/Programs/UnrealBuildTool/System/ExternalExecution.cs b/Engine/Source/Programs/UnrealBuildTool/System/ExternalExecution.cs index 0750e46b061b..25f25fd3ff2d 100644 --- a/Engine/Source/Programs/UnrealBuildTool/System/ExternalExecution.cs +++ b/Engine/Source/Programs/UnrealBuildTool/System/ExternalExecution.cs @@ -648,7 +648,8 @@ namespace UnrealBuildTool } } - + // Set to true if makefiles need invalidating + static public bool bInvalidateUHTMakefile = false; /// /// Builds and runs the header tool and touches the header directories. @@ -774,6 +775,11 @@ namespace UnrealBuildTool CmdLine += " -FailIfGeneratedCodeChanges"; } + if (!bInvalidateUHTMakefile && BuildConfiguration.bUseUHTMakefiles) + { + CmdLine += " -UseMakefiles"; + } + Log.TraceInformation(" Running UnrealHeaderTool {0}", CmdLine); Stopwatch s = new Stopwatch(); diff --git a/Engine/Source/Programs/UnrealBuildTool/System/ProjectFileGenerator.cs b/Engine/Source/Programs/UnrealBuildTool/System/ProjectFileGenerator.cs index 19e6b44d24fe..24042697cc10 100644 --- a/Engine/Source/Programs/UnrealBuildTool/System/ProjectFileGenerator.cs +++ b/Engine/Source/Programs/UnrealBuildTool/System/ProjectFileGenerator.cs @@ -1788,7 +1788,12 @@ namespace UnrealBuildTool TargetRules = TargetRulesObject, TargetFilePath = TargetFilePath, ProjectFilePath = ProjectFilePath - }; + }; + + if (TargetName == "UnrealCodeAnalyzer") + { + ProjectFile.ShouldBuildByDefaultForSolutionTargets = false; + } if (TargetName == "ShaderCompileWorker") // @todo projectfiles: Ideally, the target rules file should set this { diff --git a/Engine/Source/Programs/UnrealBuildTool/System/UnrealBuildTool.cs b/Engine/Source/Programs/UnrealBuildTool/System/UnrealBuildTool.cs index 4ff09d75b66a..d60275669511 100644 --- a/Engine/Source/Programs/UnrealBuildTool/System/UnrealBuildTool.cs +++ b/Engine/Source/Programs/UnrealBuildTool/System/UnrealBuildTool.cs @@ -766,6 +766,10 @@ namespace UnrealBuildTool { BuildConfiguration.bUseUBTMakefiles = false; } + else if (LowercaseArg == "-uhtmakefiles") + { + BuildConfiguration.bUseUHTMakefiles = true; + } else if (LowercaseArg == "-nosimplygon") { UEBuildConfiguration.bCompileSimplygon = false; @@ -1725,6 +1729,9 @@ namespace UnrealBuildTool TargetDescs[0].TargetName, TargetDescs.Count > 1 ? (" (and " + (TargetDescs.Count - 1).ToString() + " more)") : "", ReasonNotLoaded); + + // Invalidate UHT makefiles too + ExternalExecution.bInvalidateUHTMakefile = true; } } @@ -2132,17 +2139,14 @@ namespace UnrealBuildTool } var Processes = BuildHostPlatform.Current.GetProcesses(); - var RootDir = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Modules.First().FullyQualifiedName), "..", "..", ".."); - var EditorRunsDir = Path.Combine(RootDir, "Engine", "Intermediate", "EditorRuns"); + var EditorRunsDir = Path.Combine(UnrealBuildTool.EngineDirectory.FullName, "Intermediate", "EditorRuns"); - if (!Directory.Exists(EditorRunsDir)) - { - return false; - } + if (!Directory.Exists(EditorRunsDir)) + { + return false; + } - var EditorRunsFiles = new DirectoryInfo(EditorRunsDir).GetFiles(); - - var NormalizedProcFileName = Path.GetFullPath(EditorProcessFilenames[0].ToString()).TrimEnd(Path.DirectorySeparatorChar).TrimEnd(Path.AltDirectorySeparatorChar).ToLowerInvariant(); + var EditorRunsFiles = new DirectoryInfo(EditorRunsDir).GetFiles(); foreach(var File in EditorRunsFiles) { @@ -2155,13 +2159,12 @@ namespace UnrealBuildTool continue; } - // Don't break here to allow clean-up of other stale files. - if (!bIsRunning) - { - // Otherwise check if the path matches. - var FileProcName = Path.GetFullPath(Proc.Filename).TrimEnd(Path.DirectorySeparatorChar).TrimEnd(Path.AltDirectorySeparatorChar).ToLowerInvariant(); - bIsRunning = NormalizedProcFileName == FileProcName; - } + // Don't break here to allow clean-up of other stale files. + if (!bIsRunning) + { + // Otherwise check if the path matches. + bIsRunning = new FileReference(Proc.Filename).CanonicalName == EditorProcessFilenames[0].CanonicalName; + } } } return bIsRunning; @@ -2440,6 +2443,14 @@ namespace UnrealBuildTool return null; } + // Check to see if any BuildConfiguration files have changed since the last build + if (XmlConfigLoader.NewestXmlTimestamp > UBTMakefileInfo.LastWriteTime) + { + Log.TraceVerbose("Makefile is older than BuildConfiguration.xml, ignoring it" ); + ReasonNotLoaded = "BuildConfiguration.xml is newer"; + return null; + } + UBTMakefile LoadedUBTMakefile = null; try diff --git a/Engine/Source/Programs/UnrealBuildTool/Utilities/XmlConfigLoader.cs b/Engine/Source/Programs/UnrealBuildTool/Utilities/XmlConfigLoader.cs index 7b33b9d28df9..7648aeb353ee 100644 --- a/Engine/Source/Programs/UnrealBuildTool/Utilities/XmlConfigLoader.cs +++ b/Engine/Source/Programs/UnrealBuildTool/Utilities/XmlConfigLoader.cs @@ -392,6 +392,9 @@ namespace UnrealBuildTool // Tells if config file exists in this location. public bool bExists { get; protected set; } + // Timestamp of file. + public DateTime Timestamp { get; protected set; } + public XmlConfigLocation(string[] FSLocations, string IDEFolderName, bool bCreateIfDoesNotExist = false) { bool bExists; @@ -400,6 +403,17 @@ namespace UnrealBuildTool this.IDEFolderName = IDEFolderName; this.bCreateIfDoesNotExist = bCreateIfDoesNotExist; this.bExists = bExists; + if (bExists) + { + try + { + Timestamp = new FileInfo(FSLocation).LastWriteTime; + } + catch (Exception) + { + Timestamp = DateTime.MaxValue; + } + } } public XmlConfigLocation(string FSLocation, string IDEFolderName, bool bCreateIfDoesNotExist = false) @@ -494,7 +508,7 @@ namespace UnrealBuildTool * a. UE4/Engine/Programs/UnrealBuildTool * b. UE4/Engine/Programs/NotForLicensees/UnrealBuildTool * c. UE4/Engine/Saved/UnrealBuildTool - * d. /Unreal Engine/UnrealBuildTool -- the location is + * d. /Unreal Engine/UnrealBuildTool -- the location is * chosen by existence and if both exist most recently used. * * The UBT is looking for it in all four places in the given order and @@ -516,6 +530,14 @@ namespace UnrealBuildTool }; } + public static DateTime NewestXmlTimestamp + { + get + { + return ConfigLocationHierarchy.Max(x => x.Timestamp); + } + } + /// /// Loads BuildConfiguration from XML into memory. /// diff --git a/Engine/Source/Programs/UnrealFrontend/Private/Commands/StatsConvertCommand.cpp b/Engine/Source/Programs/UnrealFrontend/Private/Commands/StatsConvertCommand.cpp index 26f0d1cd3a0a..9e4867cd124a 100644 --- a/Engine/Source/Programs/UnrealFrontend/Private/Commands/StatsConvertCommand.cpp +++ b/Engine/Source/Programs/UnrealFrontend/Private/Commands/StatsConvertCommand.cpp @@ -117,57 +117,25 @@ void FStatsConvertCommand::ReadAndConvertStatMessages( FArchive& Reader, FArchiv const bool bIsFinalized = Stream.Header.IsFinalized(); float DataLoadingProgress = 0.0f; - if( bHasCompressedData ) + // Sanity checks. + check( bHasCompressedData ); + + while( Reader.Tell() < Reader.TotalSize() ) { - while( Reader.Tell() < Reader.TotalSize() ) + // Read the compressed data. + FCompressedStatsData UncompressedData( SrcData, DestData ); + Reader << UncompressedData; + if( UncompressedData.HasReachedEndOfCompressedData() ) { - // Read the compressed data. - FCompressedStatsData UncompressedData( SrcData, DestData ); - Reader << UncompressedData; - if( UncompressedData.HasReachedEndOfCompressedData() ) - { - return; - } - - FMemoryReader MemoryReader( DestData, true ); - - while( MemoryReader.Tell() < MemoryReader.TotalSize() ) - { - // read the message - FStatMessage Message( Stream.ReadMessage( MemoryReader, bIsFinalized ) ); - ReadMessages++; - if( ReadMessages % 32768 == 0 ) - { - UE_LOG( LogStats, Log, TEXT( "StatsConvertCommand progress: %.1f%%" ), DataLoadingProgress ); - } - - if( Message.NameAndInfo.GetShortName() != TEXT( "Unknown FName" ) ) - { - if( Message.NameAndInfo.GetField() == EStatOperation::AdvanceFrameEventGameThread && ReadMessages > 2 ) - { - new (Messages) FStatMessage( Message ); - ThreadState.AddMessages( Messages ); - Messages.Reset(); - - CollectAndWriteStatsValues( Writer ); - DataLoadingProgress = (double)Reader.Tell() / (double)Reader.TotalSize() * 100.0f; - } - - new (Messages) FStatMessage( Message ); - } - else - { - break; - } - } + return; } - } - else - { - while( Reader.Tell() < Reader.TotalSize() ) + + FMemoryReader MemoryReader( DestData, true ); + + while( MemoryReader.Tell() < MemoryReader.TotalSize() ) { // read the message - FStatMessage Message( Stream.ReadMessage( Reader, bIsFinalized ) ); + FStatMessage Message( Stream.ReadMessage( MemoryReader, bIsFinalized ) ); ReadMessages++; if( ReadMessages % 32768 == 0 ) { @@ -176,13 +144,7 @@ void FStatsConvertCommand::ReadAndConvertStatMessages( FArchive& Reader, FArchiv if( Message.NameAndInfo.GetShortName() != TEXT( "Unknown FName" ) ) { - if( Message.NameAndInfo.GetField() == EStatOperation::SpecialMessageMarker ) - { - // Simply break the loop. - // The profiler supports more advanced handling of this message. - return; - } - else if( Message.NameAndInfo.GetField() == EStatOperation::AdvanceFrameEventGameThread && ReadMessages > 2 ) + if( Message.NameAndInfo.GetField() == EStatOperation::AdvanceFrameEventGameThread && ReadMessages > 2 ) { new (Messages) FStatMessage( Message ); ThreadState.AddMessages( Messages ); @@ -218,7 +180,7 @@ void FStatsConvertCommand::CollectAndWriteStatsValues( FArchive& Writer ) FStatMessage const& Meta = Stats[Index]; //UE_LOG(LogTemp, Display, TEXT("Stat: %s"), *Meta.NameAndInfo.GetShortName().ToString()); - if (Meta.NameAndInfo.GetShortName() == TEXT("STAT_SecondsPerCycle")) + if (Meta.NameAndInfo.GetShortName() == FStatConstants::NAME_SecondsPerCycle) { // SecondsPerCycle may vary over time, so we update it here MillisecondsPerCycle = Meta.GetValue_double() * 1000.0f; diff --git a/Engine/Source/Programs/UnrealFrontend/Private/UnrealFrontendMain.cpp b/Engine/Source/Programs/UnrealFrontend/Private/UnrealFrontendMain.cpp index 6ba00a64fffb..32ee58e1e893 100644 --- a/Engine/Source/Programs/UnrealFrontend/Private/UnrealFrontendMain.cpp +++ b/Engine/Source/Programs/UnrealFrontend/Private/UnrealFrontendMain.cpp @@ -21,6 +21,9 @@ IMPLEMENT_APPLICATION(UnrealFrontend, "UnrealFrontend"); */ int32 UnrealFrontendMain( const TCHAR* CommandLine ) { + // Override the stack size for the thread pool. + FQueuedThreadPool::OverrideStackSize = 256 * 1024; + FCommandLine::Set(CommandLine); FString Command; diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/ClassDeclarationMetaData.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/ClassDeclarationMetaData.cpp index 486571fb7b60..cc42cb1be196 100644 --- a/Engine/Source/Programs/UnrealHeaderTool/Private/ClassDeclarationMetaData.cpp +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/ClassDeclarationMetaData.cpp @@ -406,7 +406,7 @@ void FClassDeclarationMetaData::MergeAndValidateClassFlags(const FString& Declar FError::Throwf(TEXT("'abstract': NoExport class missing abstract keyword from class declaration (must change C++ version first)")); Class->ClassFlags |= CLASS_Abstract; } - else if (Class->HasAnyFlags(RF_Native)) + else if (Class->IsNative()) { FError::Throwf(TEXT("'abstract': missing abstract keyword from class declaration - class will no longer be exported as abstract")); } diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/ClassMaps.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/ClassMaps.cpp index 642ec28cef1b..6368a5053599 100644 --- a/Engine/Source/Programs/UnrealHeaderTool/Private/ClassMaps.cpp +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/ClassMaps.cpp @@ -5,6 +5,7 @@ #include "ClassMaps.h" #include "UnrealSourceFile.h" #include "UnrealTypeDefinitionInfo.h" +#include "UHTMakefile/UHTMakefile.h" TMap > GUnrealSourceFilesMap; TMap > GTypeDefinitionInfoMap; @@ -19,10 +20,13 @@ TMap GGeneratedCodeCRCs; TMap GEnumUnderlyingTypes; TMap > GClassDeclarations; -TSharedRef AddTypeDefinition(FUnrealSourceFile& SourceFile, UField* Field, int32 Line) +TSharedRef AddTypeDefinition(FUHTMakefile& UHTMakefile, FUnrealSourceFile* SourceFile, UField* Field, int32 Line) { - TSharedRef DefinitionInfo = MakeShareable(new FUnrealTypeDefinitionInfo(SourceFile, Line)); + FUnrealTypeDefinitionInfo* UnrealTypeDefinitionInfo = new FUnrealTypeDefinitionInfo(*SourceFile, Line); + UHTMakefile.AddUnrealTypeDefinitionInfo(SourceFile, UnrealTypeDefinitionInfo); + TSharedRef DefinitionInfo = MakeShareable(UnrealTypeDefinitionInfo); + UHTMakefile.AddTypeDefinitionInfoMapEntry(SourceFile, Field, UnrealTypeDefinitionInfo); GTypeDefinitionInfoMap.Add(Field, DefinitionInfo); return DefinitionInfo; diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/ClassMaps.h b/Engine/Source/Programs/UnrealHeaderTool/Private/ClassMaps.h index 84b9c951602e..024269337006 100644 --- a/Engine/Source/Programs/UnrealHeaderTool/Private/ClassMaps.h +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/ClassMaps.h @@ -39,13 +39,31 @@ enum EAccessSpecifier ACCESS_Num, }; +inline FArchive& operator<<(FArchive& Ar, EAccessSpecifier& ObjectType) +{ + if (Ar.IsLoading()) + { + int32 Value; + Ar << Value; + ObjectType = EAccessSpecifier(Value); + } + else if (Ar.IsSaving()) + { + int32 Value = (int32)ObjectType; + Ar << Value; + } + + return Ar; +} + /** * Add type definition info to global map. * + * @param UHTMakefile Makefile to which data is saved. * @param SourceFile SourceFile in which type was defined. * @param Field Defined type. * @param Line Line on which the type was defined. * * @returns Type definition info. */ -TSharedRef AddTypeDefinition(FUnrealSourceFile& SourceFile, UField* Field, int32 Line); \ No newline at end of file +TSharedRef AddTypeDefinition(FUHTMakefile& UHTMakefile, FUnrealSourceFile* SourceFile, UField* Field, int32 Line); \ No newline at end of file diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/Classes.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/Classes.cpp index 0d2353720a30..63a577b700b7 100644 --- a/Engine/Source/Programs/UnrealHeaderTool/Private/Classes.cpp +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/Classes.cpp @@ -180,7 +180,7 @@ FClass* FClasses::FindScriptClass(const FString& InClassName, FString& OutErrorM return NULL; } -TArray FClasses::GetClassesInPackage(UPackage* InPackage) const +TArray FClasses::GetClassesInPackage(const UPackage* InPackage) const { TArray Result; Result.Add(UObjectClass); diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/Classes.h b/Engine/Source/Programs/UnrealHeaderTool/Private/Classes.h index 3606ad579dd9..c242edf59de1 100644 --- a/Engine/Source/Programs/UnrealHeaderTool/Private/Classes.h +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/Classes.h @@ -71,7 +71,7 @@ public: * @param InPackage The package to return the classes from. * @return The classes in the specified package. */ - TArray GetClassesInPackage(UPackage* InPackage = ANY_PACKAGE) const; + TArray GetClassesInPackage(const UPackage* InPackage = ANY_PACKAGE) const; // Anything in here should eventually be removed when this class encapsulates its own data structure, rather than being 'poked' by the outside #if WIP_UHT_REFACTOR diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/CodeGenerator.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/CodeGenerator.cpp index 36974c108570..16e1e6b84adc 100644 --- a/Engine/Source/Programs/UnrealHeaderTool/Private/CodeGenerator.cpp +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/CodeGenerator.cpp @@ -12,6 +12,8 @@ #include "StringUtils.h" #include "IPluginManager.h" #include "Runtime/Core/Public/Features/IModularFeatures.h" +#include "UHTMakefile/UHTMakefile.h" +#include "ScopeExit.h" ///////////////////////////////////////////////////// // Globals @@ -27,7 +29,7 @@ static bool bVerifyContents = false; static const bool bMultiLineUFUNCTION = true; static const bool bMultiLineUPROPERTY = true; -static TSharedRef PerformInitialParseOnHeader(UPackage* InParent, const FString& FileName, EObjectFlags Flags, const TCHAR* Buffer); +static TSharedRef PerformInitialParseOnHeader(UPackage* InParent, const FString& FileName, EObjectFlags Flags, const TCHAR* Buffer, FUHTMakefile& UHTMakefile); FCompilerMetadataManager GScriptHelper; @@ -52,38 +54,37 @@ const TCHAR* FindIdentifierExactMatch(const TCHAR* StringBegin, const TCHAR* Str return StringBegin; } - auto FindLen = Identifier.Len(); - auto StringToSearch = StringBegin; + int32 FindLen = Identifier.Len(); + const TCHAR* StringToSearch = StringBegin; for (;;) { - auto IdentifierStart = FCString::Strstr(StringToSearch, *Identifier); + const TCHAR* IdentifierStart = FCString::Strstr(StringToSearch, *Identifier); if (IdentifierStart == nullptr) { // Not found. return nullptr; } - if ((IdentifierStart > StringEnd) || (IdentifierStart + FindLen + 1 > StringEnd)) + if (IdentifierStart > StringEnd || IdentifierStart + FindLen + 1 > StringEnd) { // Found match is out of string range. return nullptr; } - if ((IdentifierStart == StringBegin) && (!FChar::IsIdentifier(*(IdentifierStart + FindLen + 1)))) + if (IdentifierStart == StringBegin && !FChar::IsIdentifier(*(IdentifierStart + FindLen + 1))) { // Found match is at the beginning of string. return IdentifierStart; } - if ((IdentifierStart + FindLen == StringEnd) && (!FChar::IsIdentifier(*(IdentifierStart - 1)))) + if (IdentifierStart + FindLen == StringEnd && !FChar::IsIdentifier(*(IdentifierStart - 1))) { // Found match ends with end of string. return IdentifierStart; } - if ((!FChar::IsIdentifier(*(IdentifierStart + FindLen))) - && (!FChar::IsIdentifier(*(IdentifierStart - 1)))) + if (!FChar::IsIdentifier(*(IdentifierStart + FindLen)) && !FChar::IsIdentifier(*(IdentifierStart - 1))) { // Found match is in the middle of string return IdentifierStart; @@ -107,7 +108,7 @@ const TCHAR* FindIdentifierExactMatch(const TCHAR* StringBegin, const TCHAR* Str */ int32 FindIdentifierExactMatch(const FString& String, const FString& Identifier) { - auto IdentifierPtr = FindIdentifierExactMatch(*String, *String + String.Len(), Identifier); + const TCHAR* IdentifierPtr = FindIdentifierExactMatch(*String, *String + String.Len(), Identifier); if (IdentifierPtr == nullptr) { return INDEX_NONE; @@ -152,7 +153,7 @@ static struct FFlagAudit { FString Name; uint64 Flags; - Pair(UObject* Source, const TCHAR* FlagType, uint64 InFlags) + Pair(const UObject* Source, const TCHAR* FlagType, uint64 InFlags) { Name = Source->GetFullName() + TEXT("[") + FlagType + TEXT("]"); Flags = InFlags; @@ -161,7 +162,7 @@ static struct FFlagAudit TArray Items; - void Add(UObject* Source, const TCHAR* FlagType, uint64 Flags) + void Add(const UObject* Source, const TCHAR* FlagType, uint64 Flags) { new (Items) Pair(Source, FlagType, Flags); } @@ -230,7 +231,7 @@ static struct FFlagAudit } } TheFlagAudit; -void ConvertToBuildIncludePath(UPackage* Package, FString& LocalPath) +void ConvertToBuildIncludePath(const UPackage* Package, FString& LocalPath) { FPaths::MakePathRelativeTo(LocalPath, *GPackageToManifestModuleMap.FindChecked(Package)->IncludeBase); } @@ -253,11 +254,11 @@ bool FindPackageLocation(const TCHAR* InPackage, FString& OutLocation, FString& FString CheckPackage(InPackage); - auto* ModuleInfoPtr = CheckedPackageList.FindRef(CheckPackage); + FManifestModule* ModuleInfoPtr = CheckedPackageList.FindRef(CheckPackage); if (!ModuleInfoPtr) { - auto* ModuleInfoPtr2 = GManifest.Modules.FindByPredicate([&](FManifestModule& Module) { return Module.Name == CheckPackage; }); + FManifestModule* ModuleInfoPtr2 = GManifest.Modules.FindByPredicate([&](FManifestModule& Module) { return Module.Name == CheckPackage; }); if (ModuleInfoPtr2 && IFileManager::Get().DirectoryExists(*ModuleInfoPtr2->BaseDirectory)) { ModuleInfoPtr = ModuleInfoPtr2; @@ -266,7 +267,9 @@ bool FindPackageLocation(const TCHAR* InPackage, FString& OutLocation, FString& } if (!ModuleInfoPtr) + { return false; + } OutLocation = ModuleInfoPtr->BaseDirectory; OutHeaderLocation = ModuleInfoPtr->GeneratedIncludeDirectory; @@ -305,7 +308,7 @@ FString Macroize(const TCHAR* MacroName, const TCHAR* StringToMacroize) static FString GetGeneratedCodeCRCTag(UField* Field) { FString Tag; - auto FieldCrc = GGeneratedCodeCRCs.Find(Field); + const uint32* FieldCrc = GGeneratedCodeCRCs.Find(Field); if (FieldCrc) { Tag = FString::Printf(TEXT(" // %u"), *FieldCrc); @@ -441,7 +444,7 @@ FString CreateLiteralString(const FString& Str) return Result; } -static FString GetMetaDataCodeForObject(UObject* Object, const TCHAR* SymbolName, const TCHAR* Spaces) +static FString GetMetaDataCodeForObject(const UObject* Object, const TCHAR* SymbolName, const TCHAR* Spaces) { TMap* MetaData = UMetaData::GetMapForObject(Object); @@ -450,7 +453,7 @@ static FString GetMetaDataCodeForObject(UObject* Object, const TCHAR* SymbolName { typedef TKeyValuePair KVPType; TArray KVPs; - for (auto& KVP : *MetaData) + for (TPair& KVP : *MetaData) { KVPs.Add(KVPType(KVP.Key, KVP.Value)); } @@ -459,7 +462,7 @@ static FString GetMetaDataCodeForObject(UObject* Object, const TCHAR* SymbolName // even when metadata is added in a different order KVPs.Sort([](const KVPType& Lhs, const KVPType& Rhs) { return Lhs.Key < Rhs.Key; }); - for (const auto& KVP : KVPs) + for (const KVPType& KVP : KVPs) { Result += FString::Printf(TEXT("%sMetaData->SetValue(%s, TEXT(\"%s\"), %s);\r\n"), Spaces, SymbolName, *KVP.Key.ToString(), *CreateLiteralString(KVP.Value)); } @@ -964,7 +967,7 @@ FString FNativeClassHeaderGenerator::PropertyNew(FString& Meta, UProperty* Prop, ExtraArgs = FString::Printf(TEXT(", %s"), *GetSingletonName(TargetFunction)); } - FString Constructor = FString::Printf(TEXT("new(EC_InternalUseOnlyConstructor, %s, TEXT(\"%s\"), RF_Public|RF_Transient|RF_Native) U%s(%s, 0x%016llx%s);"), + FString Constructor = FString::Printf(TEXT("new(EC_InternalUseOnlyConstructor, %s, TEXT(\"%s\"), RF_Public|RF_Transient|RF_MarkAsNative) U%s(%s, 0x%016llx%s);"), *OuterString, *FNativeClassHeaderGenerator::GetOverriddenName(Prop), *Prop->GetClass()->GetName(), @@ -1177,13 +1180,13 @@ static void FindNoExportStructs(TArray& Structs, UStruct* Start) } } -FString GetPackageSingletonName(UPackage* Package) +FString GetPackageSingletonName(const UPackage* Package) { static FString ClassString = NameLookupCPP.GetNameCPP(UPackage::StaticClass()); - return FString(TEXT("Z_Construct_")) + ClassString + TEXT("_") + FPackageName::GetShortName(Package) + TEXT("()"); + return FString(TEXT("Z_Construct_")) + ClassString + TEXT("_") + Package->GetName().Replace(TEXT("/"), TEXT("_")) + TEXT("()"); } -void FNativeClassHeaderGenerator::ExportGeneratedPackageInitCode(UPackage* InPackage) +void FNativeClassHeaderGenerator::ExportGeneratedPackageInitCode(const UPackage* InPackage) { FString ApiString = GetAPIString(); FString SingletonName(GetPackageSingletonName(InPackage)); @@ -1214,7 +1217,7 @@ void FNativeClassHeaderGenerator::ExportGeneratedPackageInitCode(UPackage* InPac FGuid Guid; uint32 CombinedCRC = 0; - for (auto& Split : GeneratedFunctionBodyTextSplit) + for (TUniqueObj& Split : GeneratedFunctionBodyTextSplit) { uint32 SplitCRC = GenerateTextCRC(*Split->ToUpper()); if (CombinedCRC == 0) @@ -1328,8 +1331,9 @@ void FNativeClassHeaderGenerator::ExportNativeGeneratedInitCode(FClass* Class, F } else { - GeneratedClassRegisterFunctionText.Logf(TEXT("\t\tUClass* OuterClass = Cast(StaticFindObjectFast(UClass::StaticClass(), %s, TEXT(\"%s\")));\r\n"), - *GetPackageSingletonName(CastChecked(Class->GetOutermost())), *Class->GetName()); + const FString DynamicClassPackageName = FClass::GetTypePackageName(Class); + GeneratedClassRegisterFunctionText.Logf(TEXT("\t\tUPackage* OuterPackage = FindOrConstructDynamicTypePackage(TEXT(\"%s\"));\r\n"), *DynamicClassPackageName); + GeneratedClassRegisterFunctionText.Logf(TEXT("\t\tUClass* OuterClass = Cast(StaticFindObjectFast(UClass::StaticClass(), OuterPackage, TEXT(\"%s\")));\r\n"), *Class->GetName()); GeneratedClassRegisterFunctionText.Logf(TEXT("\t\tif (!OuterClass || !(OuterClass->ClassFlags & CLASS_Constructed))\r\n")); } @@ -1461,6 +1465,7 @@ void FNativeClassHeaderGenerator::ExportNativeGeneratedInitCode(FClass* Class, F // Calculate generated class initialization code CRC so that we know when it changes after hot-reload uint32 ClassCrc = GenerateTextCRC(*GeneratedClassRegisterFunctionText); GGeneratedCodeCRCs.Add(Class, ClassCrc); + UHTMakefile.AddGeneratedCodeCRC(CurrentSourceFile.Top(), Class, ClassCrc); // Emit the IMPLEMENT_CLASS macro to go in the generated cpp file. if (!bIsDynamic) { @@ -1546,7 +1551,7 @@ void FNativeClassHeaderGenerator::ExportFunction(UFunction* Function, FScope* Sc FString UFunctionType = bIsDelegate ? TEXT("UDelegateFunction") : TEXT("UFunction"); - CurrentFunctionText.Logf(TEXT("\t\t\tReturnFunction = new(EC_InternalUseOnlyConstructor, Outer, TEXT(\"%s\"), RF_Public|RF_Transient|RF_Native) %s(FObjectInitializer(), %s, 0x%08X, %d%s);\r\n"), + CurrentFunctionText.Logf(TEXT("\t\t\tReturnFunction = new(EC_InternalUseOnlyConstructor, Outer, TEXT(\"%s\"), RF_Public|RF_Transient|RF_MarkAsNative) %s(FObjectInitializer(), %s, 0x%08X, %d%s);\r\n"), *FNativeClassHeaderGenerator::GetOverriddenName(Function), *UFunctionType, *SuperFunctionString, @@ -1588,7 +1593,7 @@ void FNativeClassHeaderGenerator::ExportFunction(UFunction* Function, FScope* Sc uint32 FunctionCrc = GenerateTextCRC(*CurrentFunctionText); GGeneratedCodeCRCs.Add(Function, FunctionCrc); - + UHTMakefile.AddGeneratedCodeCRC(CurrentSourceFile.Top(), Function, FunctionCrc); GetGeneratedFunctionTextDevice() += CurrentFunctionText; } @@ -1637,7 +1642,7 @@ void FNativeClassHeaderGenerator::ExportNatives(FClass* Class) GeneratedPackageCPP.Logf(TEXT("\t}\r\n")); } -void FNativeClassHeaderGenerator::ExportInterfaceCallFunctions(const TArray& InCallbackFunctions, UClass* Class, FClassMetaData* ClassData, FUHTStringBuilder& HeaderOutput) +void FNativeClassHeaderGenerator::ExportInterfaceCallFunctions(const TArray& InCallbackFunctions, UClass* Class, FUHTStringBuilder& HeaderOutput) { TArray CallbackFunctions = InCallbackFunctions; CallbackFunctions.Sort(); @@ -1863,6 +1868,14 @@ void ExportAutoIncludes(FStringOutputDevice& Out, const FUnrealSourceFile& Sourc void FNativeClassHeaderGenerator::ExportClassesFromSourceFileInner(FUnrealSourceFile& SourceFile) { + CurrentSourceFile.Push(&SourceFile); + ON_SCOPE_EXIT + { + CurrentSourceFile.Pop(); + }; + + NameLookupCPP.SetCurrentSourceFile(&SourceFile); + UHTMakefile.AddToHeaderOrder(&SourceFile); TArray Enums; TArray Structs; TArray DelegateFunctions; @@ -1890,7 +1903,7 @@ void FNativeClassHeaderGenerator::ExportClassesFromSourceFileInner(FUnrealSource ExportGeneratedEnumsInitCode(Enums); // export boilerplate macros for structs - ExportGeneratedStructBodyMacros(SourceFile, Structs); + ExportGeneratedStructBodyMacros(Structs); // export delegate wrapper function implementations ExportDelegateDefinitions(SourceFile, DelegateFunctions, true); @@ -1950,12 +1963,12 @@ void FNativeClassHeaderGenerator::ExportClassesFromSourceFileInner(FUnrealSource } const bool bCastedClass = Class->HasAnyCastFlag(CASTCLASS_AllFlags) && SuperClass && Class->ClassCastFlags != SuperClass->ClassCastFlags; - UInterfaceBoilerplate.Logf(TEXT("\tDECLARE_CLASS(%s, %s, COMPILED_IN_FLAGS(CLASS_Abstract%s), %s, %s, %s_API)\r\n"), + UInterfaceBoilerplate.Logf(TEXT("\tDECLARE_CLASS(%s, %s, COMPILED_IN_FLAGS(CLASS_Abstract%s), %s, TEXT(\"%s\"), %s_API)\r\n"), ClassCPPName, SuperClassCPPName, *GetClassFlagExportText(Class), bCastedClass ? *FString::Printf(TEXT("CASTCLASS_%s"), ClassCPPName) : TEXT("0"), - *FPackageName::GetShortName(Class->GetOuter()->GetName()), + *FClass::GetTypePackageName(Class), *APIArg); UInterfaceBoilerplate.Logf(TEXT("\tDECLARE_SERIALIZER(%s)\r\n"), ClassCPPName); @@ -1988,7 +2001,7 @@ void FNativeClassHeaderGenerator::ExportClassesFromSourceFileInner(FUnrealSource InterfaceBoilerplate.Logf(TEXT("protected:\r\n\tvirtual ~%s() {}\r\npublic:\r\n"), *InterfaceCPPName); InterfaceBoilerplate.Logf(TEXT("\ttypedef %s UClassType;\r\n"), ClassCPPName); - ExportInterfaceCallFunctions(CallbackFunctions, Class, ClassData, InterfaceBoilerplate); + ExportInterfaceCallFunctions(CallbackFunctions, Class, InterfaceBoilerplate); // we'll need a way to get to the UObject portion of a native interface, so that we can safely pass native interfaces // to script VM functions @@ -2059,13 +2072,13 @@ void FNativeClassHeaderGenerator::ExportClassesFromSourceFileInner(FUnrealSource APIArg = TEXT("NO"); } const bool bCastedClass = Class->HasAnyCastFlag(CASTCLASS_AllFlags) && SuperClass && Class->ClassCastFlags != SuperClass->ClassCastFlags; - ClassBoilerplate.Logf(TEXT("\tDECLARE_CLASS(%s, %s, COMPILED_IN_FLAGS(%s%s), %s, %s, %s_API)\r\n"), + ClassBoilerplate.Logf(TEXT("\tDECLARE_CLASS(%s, %s, COMPILED_IN_FLAGS(%s%s), %s, TEXT(\"%s\"), %s_API)\r\n"), ClassCPPName, SuperClassCPPName ? SuperClassCPPName : TEXT("None"), Class->HasAnyClassFlags(CLASS_Abstract) ? TEXT("CLASS_Abstract") : TEXT("0"), *GetClassFlagExportText(Class), bCastedClass ? *FString::Printf(TEXT("CASTCLASS_%s"), ClassCPPName) : TEXT("0"), - *FPackageName::GetShortName(*Class->GetOuter()->GetName()), + *FClass::GetTypePackageName(Class), *APIArg); ClassBoilerplate.Logf(TEXT("\tDECLARE_SERIALIZER(%s)\r\n"), ClassCPPName); @@ -2408,7 +2421,7 @@ FString GetBuildPath(FUnrealSourceFile& SourceFile) return Out; } -FString FNativeClassHeaderGenerator::GetListOfPublicHeaderGroupIncludesString(UPackage* InPackage) +FString FNativeClassHeaderGenerator::GetListOfPublicHeaderGroupIncludesString(const UPackage* InPackage) { FUHTStringBuilder Out; @@ -2466,6 +2479,11 @@ void FNativeClassHeaderGenerator::ExportConstructorsMacros(const FString& Constr void FNativeClassHeaderGenerator::ExportClassesFromSourceFileWrapper(FUnrealSourceFile& SourceFile) { + CurrentSourceFile.Push(&SourceFile); + ON_SCOPE_EXIT + { + CurrentSourceFile.Pop(); + }; check(!GeneratedHeaderText.Len()); ExportClassesFromSourceFileInner(SourceFile); @@ -2557,6 +2575,12 @@ void FNativeClassHeaderGenerator::ExportClassesFromSourceFileWrapper(FUnrealSour void FNativeClassHeaderGenerator::ExportSourceFileHeaderRecursive(FClasses& AllClasses, FUnrealSourceFile* SourceFile, TSet& VisitedSet, bool bCheckDependenciesOnly) { + CurrentSourceFile.Push(SourceFile); + ON_SCOPE_EXIT + { + CurrentSourceFile.Pop(); + }; + bool bIsCorrectHeader = SourceFile->GetPackage() == Package; // Check for circular header dependencies between export classes. @@ -2677,7 +2701,7 @@ void FNativeClassHeaderGenerator::ExportEnums( const TArray& Enums ) } // Exports the header text for the list of structs specified (GENERATED_BODY impls) -void FNativeClassHeaderGenerator::ExportGeneratedStructBodyMacros(FUnrealSourceFile& SourceFile, const TArray& NativeStructs) +void FNativeClassHeaderGenerator::ExportGeneratedStructBodyMacros(const TArray& NativeStructs) { // reverse the order. for (int32 i = NativeStructs.Num() - 1; i >= 0; --i) @@ -2703,7 +2727,7 @@ void FNativeClassHeaderGenerator::ExportGeneratedStructBodyMacros(FUnrealSourceF const FString StaticClassLine = FString::Printf(TEXT("\t%sstatic class UScriptStruct* StaticStruct();\r\n"), *RequiredAPI); const FString CombinedLine = FriendLine + StaticClassLine; - const FString MacroName = SourceFile.GetGeneratedBodyMacroName(Struct->StructMacroDeclaredLineNumber); + const FString MacroName = CurrentSourceFile.Top()->GetGeneratedBodyMacroName(Struct->StructMacroDeclaredLineNumber); const FString Macroized = Macroize(*MacroName, *CombinedLine); GeneratedHeaderText.Log(*Macroized); @@ -2722,11 +2746,16 @@ void FNativeClassHeaderGenerator::ExportGeneratedStructBodyMacros(FUnrealSourceF OuterName = NameLookupCPP.GetNameCPP(CastChecked(Struct->GetOuter())); OuterName += TEXT("::StaticClass()"); } - else + else if (!bIsDynamic) { OuterName = GetPackageSingletonName(CastChecked(Struct->GetOuter())); GeneratedPackageCPP.Logf(TEXT("\textern %sclass UPackage* %s;\r\n"), *FriendApiString, *OuterName); } + else + { + OuterName = TEXT("StructPackage"); + GeneratedPackageCPP.Logf(TEXT("\tclass UPackage* %s = FindOrConstructDynamicTypePackage(TEXT(\"%s\"));\r\n"), *OuterName, *FClass::GetTypePackageName(Struct)); + } if (!bIsDynamic) { @@ -2734,7 +2763,6 @@ void FNativeClassHeaderGenerator::ExportGeneratedStructBodyMacros(FUnrealSourceF } else { - FString PackageSingletonName = GetPackageSingletonName(CastChecked(Struct->GetOuter())); GeneratedPackageCPP.Logf(TEXT("\tclass UScriptStruct* Singleton = Cast(StaticFindObjectFast(UScriptStruct::StaticClass(), %s, TEXT(\"%s\")));\r\n"), *OuterName, *Struct->GetName()); } @@ -2796,12 +2824,17 @@ void FNativeClassHeaderGenerator::ExportGeneratedStructBodyMacros(FUnrealSourceF // Structs can either have a UClass or UPackage as outer (if delcared in non-UClass header). if (ScriptStruct->GetOuter()->IsA(UStruct::StaticClass())) { - GeneratedStructRegisterFunctionText.Logf(TEXT("\t\tUStruct* Outer=%s;\r\n"), *GetSingletonName(CastChecked(ScriptStruct->GetOuter()))); + GeneratedStructRegisterFunctionText.Logf(TEXT("\t\tUStruct* Outer = %s;\r\n"), *GetSingletonName(CastChecked(ScriptStruct->GetOuter()))); + } + else if (!bIsDynamic) + { + GeneratedStructRegisterFunctionText.Logf(TEXT("\t\tUPackage* Outer = %s;\r\n"), *GetPackageSingletonName(CastChecked(ScriptStruct->GetOuter()))); } else { - GeneratedStructRegisterFunctionText.Logf(TEXT("\t\tUPackage* Outer=%s;\r\n"), *GetPackageSingletonName(CastChecked(ScriptStruct->GetOuter()))); + GeneratedStructRegisterFunctionText.Logf(TEXT("\t\tUPackage* Outer = FindOrConstructDynamicTypePackage(TEXT(\"%s\"));\r\n"), *FClass::GetTypePackageName(ScriptStruct)); } + GeneratedStructRegisterFunctionText.Logf(TEXT("\t\textern uint32 %s();\r\n"), *CRCFuncName); GeneratedStructRegisterFunctionText.Logf(TEXT("\t\tstatic UScriptStruct* ReturnStruct = FindExistingStructIfHotReloadOrDynamic(Outer, TEXT(\"%s\"), sizeof(%s), %s(), %s);\r\n"), *ScriptStruct->GetName(), NameLookupCPP.GetNameCPP(Struct), *CRCFuncName, bIsDynamic ? TEXT("true") : TEXT("false")); GeneratedStructRegisterFunctionText.Logf(TEXT("\t\tif (!ReturnStruct)\r\n")); @@ -2825,7 +2858,7 @@ void FNativeClassHeaderGenerator::ExportGeneratedStructBodyMacros(FUnrealSourceF ExplicitSizeString = FString::Printf(TEXT(", sizeof(%s), ALIGNOF(%s)"), NameLookupCPP.GetNameCPP(ScriptStruct), NameLookupCPP.GetNameCPP(ScriptStruct)); } - GeneratedStructRegisterFunctionText.Logf(TEXT("\t\t\tReturnStruct = new(EC_InternalUseOnlyConstructor, Outer, TEXT(\"%s\"), RF_Public|RF_Transient|RF_Native) UScriptStruct(FObjectInitializer(), %s, %s, EStructFlags(0x%08X)%s);\r\n"), + GeneratedStructRegisterFunctionText.Logf(TEXT("\t\t\tReturnStruct = new(EC_InternalUseOnlyConstructor, Outer, TEXT(\"%s\"), RF_Public|RF_Transient|RF_MarkAsNative) UScriptStruct(FObjectInitializer(), %s, %s, EStructFlags(0x%08X)%s);\r\n"), *ScriptStruct->GetName(), *BaseStructString, *CppStructOpsString, @@ -2858,6 +2891,7 @@ void FNativeClassHeaderGenerator::ExportGeneratedStructBodyMacros(FUnrealSourceF uint32 StructCrc = GenerateTextCRC(*GeneratedStructRegisterFunctionText); GGeneratedCodeCRCs.Add(ScriptStruct, StructCrc); + UHTMakefile.AddGeneratedCodeCRC(CurrentSourceFile.Top(), ScriptStruct, StructCrc); auto& GeneratedFunctionText = GetGeneratedFunctionTextDevice(); GeneratedFunctionText += GeneratedStructRegisterFunctionText; @@ -2887,24 +2921,39 @@ void FNativeClassHeaderGenerator::ExportGeneratedEnumsInitCode(const TArray(Enum->GetOuter())); + if (!bIsDynamic) + { + PackageSingletonName = GetPackageSingletonName(CastChecked(Enum->GetOuter())); + } + else + { + PackageSingletonName = FClass::GetTypePackageName(Enum); + } GeneratedPackageCPP.Logf(TEXT("static class UEnum* %s_StaticEnum()\r\n"), *Enum->GetName()); GeneratedPackageCPP.Logf(TEXT("{\r\n")); - GeneratedPackageCPP.Logf(TEXT("\textern %sclass UPackage* %s;\r\n"), *FriendApiString, *PackageSingletonName); - + if (!bIsDynamic) { + GeneratedPackageCPP.Logf(TEXT("\textern %sclass UPackage* %s;\r\n"), *FriendApiString, *PackageSingletonName); GeneratedPackageCPP.Logf(TEXT("\tstatic class UEnum* Singleton = NULL;\r\n")); } else { - GeneratedPackageCPP.Logf(TEXT("\tclass UEnum* Singleton = Cast(StaticFindObjectFast(UEnum::StaticClass(), %s, TEXT(\"%s\")));\r\n"), - *PackageSingletonName, *Enum->GetName()); + GeneratedPackageCPP.Logf(TEXT("\tclass UPackage* EnumPackage = FindOrConstructDynamicTypePackage(TEXT(\"%s\"));\r\n"), *PackageSingletonName); + GeneratedPackageCPP.Logf(TEXT("\tclass UEnum* Singleton = Cast(StaticFindObjectFast(UEnum::StaticClass(), EnumPackage, TEXT(\"%s\")));\r\n"), *Enum->GetName()); } GeneratedPackageCPP.Logf(TEXT("\tif (!Singleton)\r\n")); GeneratedPackageCPP.Logf(TEXT("\t{\r\n")); - GeneratedPackageCPP.Logf(TEXT("\t\textern %sclass UEnum* %s;\r\n"), *FriendApiString, *StaticConstructionString); - GeneratedPackageCPP.Logf(TEXT("\t\tSingleton = GetStaticEnum(%s, %s, TEXT(\"%s\"));\r\n"), *SingletonName, *PackageSingletonName, *Enum->GetName()); + GeneratedPackageCPP.Logf(TEXT("\t\textern %sclass UEnum* %s;\r\n"), *FriendApiString, *StaticConstructionString); + if (!bIsDynamic) + { + GeneratedPackageCPP.Logf(TEXT("\t\tSingleton = GetStaticEnum(%s, %s, TEXT(\"%s\"));\r\n"), *SingletonName, *PackageSingletonName, *Enum->GetName()); + } + else + { + GeneratedPackageCPP.Logf(TEXT("\t\tSingleton = GetStaticEnum(%s, EnumPackage, TEXT(\"%s\"));\r\n"), *SingletonName, *Enum->GetName()); + } GeneratedPackageCPP.Logf(TEXT("\t}\r\n")); GeneratedPackageCPP.Logf(TEXT("\treturn Singleton;\r\n")); @@ -2929,16 +2978,20 @@ void FNativeClassHeaderGenerator::ExportGeneratedEnumsInitCode(const TArray(Enum->GetOuter()))); } - else + else if (!bIsDynamic) { GeneratedEnumRegisterFunctionText.Logf(TEXT("\t\tUPackage* Outer=%s;\r\n"), *GetPackageSingletonName(CastChecked(Enum->GetOuter()))); } + else + { + GeneratedEnumRegisterFunctionText.Logf(TEXT("\t\tUPackage* Outer = FindOrConstructDynamicTypePackage(TEXT(\"%s\"));\r\n"), *PackageSingletonName); + } GeneratedEnumRegisterFunctionText.Logf(TEXT("\t\textern uint32 %s();\r\n"), *CRCFuncName); GeneratedEnumRegisterFunctionText.Logf(TEXT("\t\tstatic UEnum* ReturnEnum = FindExistingEnumIfHotReload(Outer, TEXT(\"%s\"), 0, %s());\r\n"), *Enum->GetName(), *CRCFuncName); GeneratedEnumRegisterFunctionText.Logf(TEXT("\t\tif (!ReturnEnum)\r\n")); GeneratedEnumRegisterFunctionText.Logf(TEXT("\t\t{\r\n")); - GeneratedEnumRegisterFunctionText.Logf(TEXT("\t\t\tReturnEnum = new(EC_InternalUseOnlyConstructor, Outer, TEXT(\"%s\"), RF_Public|RF_Transient|RF_Native) UEnum(FObjectInitializer());\r\n"), *Enum->GetName()); + GeneratedEnumRegisterFunctionText.Logf(TEXT("\t\t\tReturnEnum = new(EC_InternalUseOnlyConstructor, Outer, TEXT(\"%s\"), RF_Public|RF_Transient|RF_MarkAsNative) UEnum(FObjectInitializer());\r\n"), *Enum->GetName()); GeneratedEnumRegisterFunctionText.Logf(TEXT("\t\t\tTArray> EnumNames;\r\n")); for (int32 Index = 0; Index < Enum->NumEnums(); Index++) { @@ -2973,6 +3026,7 @@ void FNativeClassHeaderGenerator::ExportGeneratedEnumsInitCode(const TArrayLinkChild(%s); // %u\r\n"), *EnumSingletonName, EnumCrc); } @@ -3126,7 +3180,7 @@ void FNativeClassHeaderGenerator::ExportDelegateDefinitions(FUnrealSourceFile& S // Only exporting function prototype DelegateOutput.Logf(TEXT(";\r\n")); - ExportFunction(Function, &SourceFile.GetScope().Get(), false); + ExportFunction(Function, &CurrentSourceFile.Top()->GetScope().Get(), false); } else { @@ -3273,7 +3327,7 @@ void ExportProtoDeclaration(FOutputDevice& Out, const FString& MessageName, TFie * @param Indent starting indentation level * @param Output optional output redirect */ -void FNativeClassHeaderGenerator::ExportProtoMessage(const TArray& InCallbackFunctions, FClassMetaData* ClassData, int32 Indent, FUHTStringBuilder* Output) +void FNativeClassHeaderGenerator::ExportProtoMessage(const TArray& InCallbackFunctions, int32 Indent, FUHTStringBuilder* Output) { // Parms struct definitions. FUHTStringBuilder HeaderOutput; @@ -4443,7 +4497,7 @@ TArray FNativeClassHeaderGenerator::ExportCallbackFunctions(FUnrealS PrologMacroCalls.Logf(TEXT("\t%s\r\n"), *MacroName); // export .proto files for any net service functions - ExportProtoMessage(CallbackFunctions, ClassData); + ExportProtoMessage(CallbackFunctions); // export .java files for any net service functions ExportMCPMessage(CallbackFunctions, ClassData); @@ -4692,13 +4746,14 @@ FUHTStringBuilder& FNativeClassHeaderGenerator::GetGeneratedFunctionTextDevice() // Constructor. FNativeClassHeaderGenerator::FNativeClassHeaderGenerator( - UPackage* InPackage, + const UPackage* InPackage, const TArray& SourceFiles, FClasses& AllClasses, bool InAllowSaveExportedHeaders #if WITH_HOT_RELOAD_CTORS , bool bInExportVTableConstructors #endif // WITH_HOT_RELOAD_CTORS + , FUHTMakefile& InUHTMakefile ) : API (FPackageName::GetShortName(InPackage).ToUpper()) , Package (InPackage) @@ -4708,6 +4763,7 @@ FNativeClassHeaderGenerator::FNativeClassHeaderGenerator( #if WITH_HOT_RELOAD_CTORS , bExportVTableConstructors (bInExportVTableConstructors) #endif // WITH_HOT_RELOAD_CTORS + , UHTMakefile(InUHTMakefile) { const FString PackageName = FPackageName::GetShortName(Package); @@ -5265,12 +5321,12 @@ void GetScriptPlugins(TArray& ScriptPlugins) bool bSupportedPlugin = ScriptGenerator->SupportsTarget(GManifest.TargetName); if (bSupportedPlugin) { - // Find the right output direcotry for this plugin base on its target (Engine-side) plugin name. + // Find the right output directory for this plugin base on its target (Engine-side) plugin name. FString GeneratedCodeModuleName = ScriptGenerator->GetGeneratedCodeModuleName(); const FManifestModule* GeneratedCodeModule = NULL; FString OutputDirectory; FString IncludeBase; - for (const auto& Module : GManifest.Modules) + for (const FManifestModule& Module : GManifest.Modules) { if (Module.Name == GeneratedCodeModuleName) { @@ -5349,36 +5405,8 @@ void ResolveSuperClasses(UPackage* Package) } } -ECompilationResult::Type UnrealHeaderTool_Main(const FString& ModuleInfoFilename) -{ - check(GIsUCCMakeStandaloneHeaderGenerator); - ECompilationResult::Type Result = ECompilationResult::Succeeded; - - if ( !FParse::Param( FCommandLine::Get(), TEXT("IgnoreWarnings")) ) +ECompilationResult::Type PreparseModules(FUHTMakefile& UHTMakefile, const FString& ModuleInfoPath, int32& NumFailures) { - GWarn->TreatWarningsAsErrors = true; - } - - FString ModuleInfoPath = FPaths::GetPath(ModuleInfoFilename); - - // Load the manifest file, giving a list of all modules to be processed, pre-sorted by dependency ordering -#if !PLATFORM_EXCEPTIONS_DISABLED - try -#endif - { - GManifest = FManifest::LoadFromFile(ModuleInfoFilename); - } -#if !PLATFORM_EXCEPTIONS_DISABLED - catch (const TCHAR* Ex) - { - UE_LOG(LogCompile, Error, TEXT("Failed to load manifest file '%s': %s"), *ModuleInfoFilename, Ex); - return GCompilationResult; - } -#endif - - // Load classes for editing. - int32 NumFailures = 0; - // Three passes. 1) Public 'Classes' headers (legacy) 2) Public headers 3) Private headers enum EHeaderFolderTypes { @@ -5389,20 +5417,30 @@ ECompilationResult::Type UnrealHeaderTool_Main(const FString& ModuleInfoFilename FolderType_Count }; - double TotalModulePreparseTime = 0.0; - double TotalParseAndCodegenTime = 0.0; - - for (const auto& Module : GManifest.Modules) + ECompilationResult::Type Result = ECompilationResult::Succeeded; + for (FManifestModule& Module : GManifest.Modules) { if (Result != ECompilationResult::Succeeded) { break; } - double ThisModulePreparseTime = 0.0; - int32 NumHeadersPreparsed = 0; - FDurationTimer ThisModuleTimer(ThisModulePreparseTime); - ThisModuleTimer.Start(); + FName ModuleName = FName(*Module.Name); + UHTMakefile.SetCurrentModuleName(ModuleName); + bool bLoadFromMakefile = UHTMakefile.CanLoadModule(Module); + if (bLoadFromMakefile) + { + // Load module data from makefile. + UHTMakefile.LoadModuleData(ModuleName, Module); + continue; + } + UHTMakefile.AddModule(ModuleName); + + // Mark that we'll need to append newly constructed objects to ones loaded from makefile. + UHTMakefile.SetShouldMoveNewObjects(); + + // Force regeneration of all subsequent modules, otherwise data will get corrupted. + Module.ForceRegeneration(); UPackage* Package = Cast(StaticFindObjectFast(UPackage::StaticClass(), NULL, FName(*Module.LongPackageName), false, false)); if (Package == NULL) @@ -5419,8 +5457,15 @@ ECompilationResult::Type UnrealHeaderTool_Main(const FString& ModuleInfoFilename Package->SetPackageFlags(PKG_EditorOnly); } + // Add new module or overwrite whatever we had loaded, that data is obsolete. + UHTMakefile.AddPackage(Package); GPackageToManifestModuleMap.Add(Package, &Module); + double ThisModulePreparseTime = 0.0; + int32 NumHeadersPreparsed = 0; + FDurationTimer ThisModuleTimer(ThisModulePreparseTime); + ThisModuleTimer.Start(); + // Pre-parse the headers for (int32 PassIndex = 0; PassIndex < FolderType_Count && Result == ECompilationResult::Succeeded; ++PassIndex) { @@ -5454,18 +5499,25 @@ ECompilationResult::Type UnrealHeaderTool_Main(const FString& ModuleInfoFilename FError::Throwf(TEXT("UnrealHeaderTool was unable to load source file '%s'"), *FullFilename); } - TSharedRef UnrealSourceFile = PerformInitialParseOnHeader(Package, RawFilename, RF_Public | RF_Standalone, *HeaderFile); - + TSharedRef UnrealSourceFile = PerformInitialParseOnHeader(Package, RawFilename, RF_Public | RF_Standalone, *HeaderFile, UHTMakefile); + FUnrealSourceFile* UnrealSourceFilePtr = &UnrealSourceFile.Get(); + TArray DefinedClasses = UnrealSourceFile->GetDefinedClasses(); + for (UClass* DefinedClass : DefinedClasses) + { + UHTMakefile.AddClass(UnrealSourceFilePtr, DefinedClass); + } GUnrealSourceFilesMap.Add(RawFilename, UnrealSourceFile); + UHTMakefile.AddUnrealSourceFilesMapEntry(UnrealSourceFilePtr, RawFilename); if (CurrentlyProcessing == PublicClassesHeaders) { for (auto* Class : UnrealSourceFile->GetDefinedClasses()) { GPublicClassSet.Add(Class); + UHTMakefile.AddPublicClassSetEntry(UnrealSourceFilePtr, Class); } - GPublicSourceFileSet.Add(&UnrealSourceFile.Get()); + GPublicSourceFileSet.Add(UnrealSourceFilePtr); } // Save metadata for the class path, both for it's include path and relative to the module base directory @@ -5532,6 +5584,10 @@ ECompilationResult::Type UnrealHeaderTool_Main(const FString& ModuleInfoFilename } } + // Don't resolve superclasses for module when loading from makefile. + // Data is only partially loaded at this point. + if (!bLoadFromMakefile) + { #if !PLATFORM_EXCEPTIONS_DISABLED try #endif @@ -5555,10 +5611,69 @@ ECompilationResult::Type UnrealHeaderTool_Main(const FString& ModuleInfoFilename #endif ThisModuleTimer.Stop(); - TotalModulePreparseTime += ThisModulePreparseTime; UE_LOG(LogCompile, Log, TEXT("Preparsed module %s containing %i files(s) in %.2f secs."), *Module.LongPackageName, NumHeadersPreparsed, ThisModulePreparseTime); } + } + return Result; +} + +ECompilationResult::Type UnrealHeaderTool_Main(const FString& ModuleInfoFilename) +{ + check(GIsUCCMakeStandaloneHeaderGenerator); + ECompilationResult::Type Result = ECompilationResult::Succeeded; + + if ( !FParse::Param( FCommandLine::Get(), TEXT("IgnoreWarnings")) ) + { + GWarn->TreatWarningsAsErrors = true; + } + + FString ModuleInfoPath = FPaths::GetPath(ModuleInfoFilename); + + // Load the manifest file, giving a list of all modules to be processed, pre-sorted by dependency ordering +#if !PLATFORM_EXCEPTIONS_DISABLED + try +#endif + { + GManifest = FManifest::LoadFromFile(ModuleInfoFilename); + } +#if !PLATFORM_EXCEPTIONS_DISABLED + catch (const TCHAR* Ex) + { + UE_LOG(LogCompile, Error, TEXT("Failed to load manifest file '%s': %s"), *ModuleInfoFilename, Ex); + return GCompilationResult; + } +#endif + + // Counters. + int32 NumFailures = 0; + double TotalModulePreparseTime = 0.0; + double TotalParseAndCodegenTime = 0.0; + + // Check if makefiles should be used. If not, only makefile serialization is skipped. + // as the rest of code doesn't impact performance and we don't want to add ifs around + // every makefile related piece of code. + bool bUseMakefile = FParse::Param(FCommandLine::Get(), TEXT("UseMakefiles")); + + FUHTMakefile UHTMakefile; + UHTMakefile.SetNameLookupCPP(&NameLookupCPP); + UHTMakefile.SetManifest(&GManifest); + + // Declaring outside of bUseMakefile scope as the same value is used when saving makefile. + FString MakefilePath; + if (bUseMakefile) + { + MakefilePath = FPaths::Combine(*ModuleInfoPath, TEXT("UHT.makefile")); + UHTMakefile.LoadFromFile(*MakefilePath, &GManifest); + } + UHTMakefile.StartPreloading(); + { + FDurationTimer TotalModulePreparseTimer(TotalModulePreparseTime); + TotalModulePreparseTimer.Start(); + PreparseModules(UHTMakefile, ModuleInfoPath, NumFailures); + TotalModulePreparseTimer.Stop(); + } + UHTMakefile.StopPreloading(); // Do the actual parse of the headers and generate for them if (Result == ECompilationResult::Succeeded) { @@ -5610,7 +5725,12 @@ ECompilationResult::Type UnrealHeaderTool_Main(const FString& ModuleInfoFilename GetScriptPlugins(ScriptPlugins); } - for (const auto& Module : GManifest.Modules) + if (UHTMakefile.ShouldMoveNewObjects()) + { + UHTMakefile.MoveNewObjects(); + } + + for (const FManifestModule& Module : GManifest.Modules) { if (UPackage* Package = Cast(StaticFindObjectFast(UPackage::StaticClass(), NULL, FName(*Module.LongPackageName), false, false))) { @@ -5631,9 +5751,9 @@ ECompilationResult::Type UnrealHeaderTool_Main(const FString& ModuleInfoFilename } UseVTableConstructorsCache; Result = FHeaderParser::ParseAllHeadersInside(AllClasses, GWarn, Package, Module, ScriptPlugins, - Module.ModuleType != EBuildModuleType::Game || UseVTableConstructorsCache.bUseVTableConstructors); + Module.ModuleType != EBuildModuleType::Game || UseVTableConstructorsCache.bUseVTableConstructors, UHTMakefile); #else // WITH_HOT_RELOAD_CTORS - Result = FHeaderParser::ParseAllHeadersInside(AllClasses, GWarn, Package, Module, ScriptPlugins); + Result = FHeaderParser::ParseAllHeadersInside(AllClasses, GWarn, Package, Module, ScriptPlugins, UHTMakefile); #endif // WITH_HOT_RELOAD_CTORS if (Result != ECompilationResult::Succeeded) { @@ -5645,7 +5765,7 @@ ECompilationResult::Type UnrealHeaderTool_Main(const FString& ModuleInfoFilename { FScopedDurationTimer PluginTimeTracker(GPluginOverheadTime); - for (auto ScriptGenerator : ScriptPlugins) + for (IScriptGeneratorPluginInterface* ScriptGenerator : ScriptPlugins) { ScriptGenerator->FinishExport(); } @@ -5682,15 +5802,23 @@ ECompilationResult::Type UnrealHeaderTool_Main(const FString& ModuleInfoFilename UE_LOG(LogCompile, Error, TEXT("Number of generated files mismatch ref=%d, ver=%d"), RefFileNames.Num(), VerFileNames.Num()); } } + TheFlagAudit.WriteResults(); GIsRequestingExit = true; - if ((Result == ECompilationResult::Succeeded) && (NumFailures > 0)) + if ((Result != ECompilationResult::Succeeded) || (NumFailures > 0)) { + // Makefile might be corrupted, it's safer to delete it now. + IFileManager::Get().Delete(*MakefilePath); return ECompilationResult::OtherCompilationError; } + if (bUseMakefile) + { + UHTMakefile.SaveToFile(*MakefilePath); + } + return Result; } @@ -5743,7 +5871,7 @@ UClass* ProcessParsedClass(bool bClassIsAnInterface, TArray &De const static bool bVerboseOutput = FParse::Param(FCommandLine::Get(), TEXT("VERBOSE")); - if (ResultClass == nullptr || !ResultClass->HasAnyFlags(RF_Native)) + if (ResultClass == nullptr || !ResultClass->IsNative()) { // detect if the same class name is used in multiple packages if (ResultClass == nullptr) @@ -5785,7 +5913,7 @@ UClass* ProcessParsedClass(bool bClassIsAnInterface, TArray &De } -TSharedRef PerformInitialParseOnHeader(UPackage* InParent, const FString& FileName, EObjectFlags Flags, const TCHAR* Buffer) +TSharedRef PerformInitialParseOnHeader(UPackage* InParent, const FString& FileName, EObjectFlags Flags, const TCHAR* Buffer, FUHTMakefile& UHTMakefile) { const TCHAR* InBuffer = Buffer; @@ -5799,19 +5927,21 @@ TSharedRef PerformInitialParseOnHeader(UPackage* InParent, co TArray ParsedClassArray; FHeaderParser::SimplifiedClassParse(Buffer, /*out*/ ParsedClassArray, /*out*/ DependsOn, ClassHeaderTextStrippedOfCppText); - TSharedRef UnrealSourceFile = MakeShareable(new FUnrealSourceFile(InParent, FileName, ClassHeaderTextStrippedOfCppText)); - + FUnrealSourceFile* UnrealSourceFilePtr = new FUnrealSourceFile(InParent, FileName, ClassHeaderTextStrippedOfCppText); + TSharedRef UnrealSourceFile = MakeShareable(UnrealSourceFilePtr); + UHTMakefile.AddUnrealSourceFile(UnrealSourceFilePtr); + UHTMakefile.AddToHeaderOrder(UnrealSourceFilePtr); for (auto& ParsedClassInfo : ParsedClassArray) { UClass* ResultClass = ProcessParsedClass(ParsedClassInfo.IsInterface(), DependsOn, ParsedClassInfo.GetClassName(), ParsedClassInfo.GetBaseClassName(), InParent, Flags); - FScope::AddTypeScope(ResultClass, &UnrealSourceFile->GetScope().Get()); + FScope::AddTypeScope(ResultClass, &UnrealSourceFile->GetScope().Get(), UnrealSourceFilePtr, UHTMakefile); - AddTypeDefinition(*UnrealSourceFile, ResultClass, ParsedClassInfo.GetClassDefLine()); + AddTypeDefinition(UHTMakefile, UnrealSourceFilePtr, ResultClass, ParsedClassInfo.GetClassDefLine()); UnrealSourceFile->AddDefinedClass(ResultClass, MoveTemp(ParsedClassInfo)); } - for (const auto& DependsOnElement : DependsOn) + for (auto& DependsOnElement : DependsOn) { UnrealSourceFile->GetIncludes().Add(DependsOnElement); } diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/GeneratedCodeVersion.h b/Engine/Source/Programs/UnrealHeaderTool/Private/GeneratedCodeVersion.h index 9689dcb48c82..41dbbb1cf666 100644 --- a/Engine/Source/Programs/UnrealHeaderTool/Private/GeneratedCodeVersion.h +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/GeneratedCodeVersion.h @@ -5,7 +5,7 @@ // This MUST be kept in sync with EGeneratedBodyVersion in UBT defined in ExternalExecution.cs // and with ToGeneratedBodyVersion function below // -enum class EGeneratedCodeVersion : int32 +enum class EGeneratedCodeVersion : uint8 { None, V1, @@ -13,6 +13,22 @@ enum class EGeneratedCodeVersion : int32 VLatest = V2 }; +inline FArchive& operator<<(FArchive& Ar, EGeneratedCodeVersion& Type) +{ + if (Ar.IsLoading()) + { + uint8 Value; + Ar << Value; + Type = (EGeneratedCodeVersion)Value; + } + else if (Ar.IsSaving()) + { + uint8 Value = (uint8)Type; + Ar << Value; + } + return Ar; +} + inline EGeneratedCodeVersion ToGeneratedCodeVersion(const FString& InString) { if (InString.Compare(TEXT("V1")) == 0) diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/HeaderParser.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/HeaderParser.cpp index ca31c577cf94..89bf9dd593f2 100644 --- a/Engine/Source/Programs/UnrealHeaderTool/Private/HeaderParser.cpp +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/HeaderParser.cpp @@ -21,6 +21,7 @@ #include "Specifiers/InterfaceSpecifiers.h" #include "Specifiers/StructSpecifiers.h" #include "Specifiers/VariableSpecifiers.h" +#include "UHTMakefile/UHTMakefile.h" double GPluginOverheadTime = 0.0; double GHeaderCodeGenTime = 0.0; @@ -184,7 +185,7 @@ namespace for (const auto& Specifier : Specifiers) { - switch ((EFunctionSpecifier)Algo::FindSortedStringCaseInsensitive(*Specifier.Key, GFunctionSpecifierStrings, ARRAY_COUNT(GFunctionSpecifierStrings))) + switch ((EFunctionSpecifier)Algo::FindSortedStringCaseInsensitive(*Specifier.Key, GFunctionSpecifierStrings)) { default: { @@ -486,87 +487,130 @@ namespace return bDefaultToInstanced; } - UProperty* CreateVariableProperty(FPropertyBase& VarProperty, UObject* Scope, FName Name, EObjectFlags ObjectFlags, EVariableCategory::Type VariableCategory) + UProperty* CreateVariableProperty(FPropertyBase& VarProperty, UObject* Scope, FName Name, EObjectFlags ObjectFlags, EVariableCategory::Type VariableCategory, FUHTMakefile& UHTMakefile, FUnrealSourceFile* UnrealSourceFile) { switch (VarProperty.Type) { case CPT_Byte: { - auto* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UByteProperty(FObjectInitializer()); + UByteProperty* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UByteProperty(FObjectInitializer()); Result->Enum = VarProperty.Enum; + UHTMakefile.AddByteProperty(UnrealSourceFile, Result); return Result; } case CPT_Int8: - return new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UInt8Property(FObjectInitializer()); + { + UInt8Property* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UInt8Property(FObjectInitializer()); + UHTMakefile.AddInt8Property(UnrealSourceFile, Result); + return Result; + } case CPT_Int16: - return new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UInt16Property(FObjectInitializer()); + { + UInt16Property* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UInt16Property(FObjectInitializer()); + UHTMakefile.AddInt16Property(UnrealSourceFile, Result); + return Result; + } case CPT_Int: - return new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UIntProperty(FObjectInitializer()); + { + UIntProperty* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UIntProperty(FObjectInitializer()); + UHTMakefile.AddIntProperty(UnrealSourceFile, Result); + return Result; + } case CPT_Int64: - return new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UInt64Property(FObjectInitializer()); + { + UInt64Property* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UInt64Property(FObjectInitializer()); + UHTMakefile.AddInt64Property(UnrealSourceFile, Result); + return Result; + } case CPT_UInt16: - return new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UUInt16Property(FObjectInitializer()); + { + UUInt16Property* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UUInt16Property(FObjectInitializer()); + UHTMakefile.AddUInt16Property(UnrealSourceFile, Result); + return Result; + } case CPT_UInt32: - return new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UUInt32Property(FObjectInitializer()); + { + UUInt32Property* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UUInt32Property(FObjectInitializer()); + UHTMakefile.AddUInt32Property(UnrealSourceFile, Result); + return Result; + } case CPT_UInt64: - return new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UUInt64Property(FObjectInitializer()); + { + UUInt64Property* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UUInt64Property(FObjectInitializer()); + UHTMakefile.AddUInt64Property(UnrealSourceFile, Result); + return Result; + } case CPT_Bool: { - auto* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UBoolProperty(FObjectInitializer()); + UBoolProperty* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UBoolProperty(FObjectInitializer()); Result->SetBoolSize(sizeof(bool), true); + UHTMakefile.AddBoolProperty(UnrealSourceFile, Result); return Result; } case CPT_Bool8: { - auto* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UBoolProperty(FObjectInitializer()); + UBoolProperty* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UBoolProperty(FObjectInitializer()); Result->SetBoolSize((VariableCategory == EVariableCategory::Return) ? sizeof(bool) : sizeof(uint8), VariableCategory == EVariableCategory::Return); + UHTMakefile.AddBoolProperty(UnrealSourceFile, Result); return Result; } case CPT_Bool16: { - auto* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UBoolProperty(FObjectInitializer()); + UBoolProperty* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UBoolProperty(FObjectInitializer()); Result->SetBoolSize((VariableCategory == EVariableCategory::Return) ? sizeof(bool) : sizeof(uint16), VariableCategory == EVariableCategory::Return); + UHTMakefile.AddBoolProperty(UnrealSourceFile, Result); return Result; } case CPT_Bool32: { - auto* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UBoolProperty(FObjectInitializer()); + UBoolProperty* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UBoolProperty(FObjectInitializer()); Result->SetBoolSize((VariableCategory == EVariableCategory::Return) ? sizeof(bool) : sizeof(uint32), VariableCategory == EVariableCategory::Return); + UHTMakefile.AddBoolProperty(UnrealSourceFile, Result); return Result; } case CPT_Bool64: { - auto* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UBoolProperty(FObjectInitializer()); + UBoolProperty* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UBoolProperty(FObjectInitializer()); Result->SetBoolSize((VariableCategory == EVariableCategory::Return) ? sizeof(bool) : sizeof(uint64), VariableCategory == EVariableCategory::Return); + UHTMakefile.AddBoolProperty(UnrealSourceFile, Result); return Result; } case CPT_Float: - return new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UFloatProperty(FObjectInitializer()); + { + UFloatProperty* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UFloatProperty(FObjectInitializer()); + UHTMakefile.AddFloatProperty(UnrealSourceFile, Result); + return Result; + } case CPT_Double: - return new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UDoubleProperty(FObjectInitializer()); + { + UDoubleProperty* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UDoubleProperty(FObjectInitializer()); + UHTMakefile.AddDoubleProperty(UnrealSourceFile, Result); + return Result; + } case CPT_ObjectReference: check(VarProperty.PropertyClass); if (VarProperty.PropertyClass->IsChildOf(UClass::StaticClass())) { - auto* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UClassProperty(FObjectInitializer()); + UClassProperty* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UClassProperty(FObjectInitializer()); Result->MetaClass = VarProperty.MetaClass; Result->PropertyClass = VarProperty.PropertyClass; + UHTMakefile.AddClassProperty(UnrealSourceFile, Result); return Result; } else @@ -577,8 +621,9 @@ namespace AddEditInlineMetaData(VarProperty.MetaData); } - auto* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UObjectProperty(FObjectInitializer()); + UObjectProperty* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UObjectProperty(FObjectInitializer()); Result->PropertyClass = VarProperty.PropertyClass; + UHTMakefile.AddObjectProperty(UnrealSourceFile, Result); return Result; } @@ -586,8 +631,9 @@ namespace { check(VarProperty.PropertyClass); - auto* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UWeakObjectProperty(FObjectInitializer()); + UWeakObjectProperty* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UWeakObjectProperty(FObjectInitializer()); Result->PropertyClass = VarProperty.PropertyClass; + UHTMakefile.AddWeakObjectProperty(UnrealSourceFile, Result); return Result; } @@ -595,8 +641,9 @@ namespace { check(VarProperty.PropertyClass); - auto* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) ULazyObjectProperty(FObjectInitializer()); + ULazyObjectProperty* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) ULazyObjectProperty(FObjectInitializer()); Result->PropertyClass = VarProperty.PropertyClass; + UHTMakefile.AddLazyObjectProperty(UnrealSourceFile, Result); return Result; } @@ -605,15 +652,17 @@ namespace if (VarProperty.PropertyClass->IsChildOf(UClass::StaticClass())) { - auto* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UAssetClassProperty(FObjectInitializer()); + UAssetClassProperty* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UAssetClassProperty(FObjectInitializer()); Result->MetaClass = VarProperty.MetaClass; Result->PropertyClass = VarProperty.PropertyClass; + UHTMakefile.AddAssetClassProperty(UnrealSourceFile, Result); return Result; } else { - auto* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UAssetObjectProperty(FObjectInitializer()); + UAssetObjectProperty* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UAssetObjectProperty(FObjectInitializer()); Result->PropertyClass = VarProperty.PropertyClass; + UHTMakefile.AddAssetObjectProperty(UnrealSourceFile, Result); return Result; } @@ -622,19 +671,32 @@ namespace check(VarProperty.PropertyClass); check(VarProperty.PropertyClass->HasAnyClassFlags(CLASS_Interface)); - auto* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UInterfaceProperty(FObjectInitializer()); + UInterfaceProperty* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UInterfaceProperty(FObjectInitializer()); Result->InterfaceClass = VarProperty.PropertyClass; + UHTMakefile.AddInterfaceProperty(UnrealSourceFile, Result); return Result; } case CPT_Name: - return new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UNameProperty(FObjectInitializer()); + { + UNameProperty* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UNameProperty(FObjectInitializer()); + UHTMakefile.AddNameProperty(UnrealSourceFile, Result); + return Result; + } case CPT_String: - return new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UStrProperty(FObjectInitializer()); + { + UStrProperty* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UStrProperty(FObjectInitializer()); + UHTMakefile.AddStrProperty(UnrealSourceFile, Result); + return Result; + } case CPT_Text: - return new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UTextProperty(FObjectInitializer()); + { + UTextProperty* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UTextProperty(FObjectInitializer()); + UHTMakefile.AddTextProperty(UnrealSourceFile, Result); + return Result; + } case CPT_Struct: { @@ -643,16 +705,25 @@ namespace VarProperty.PropertyFlags |= CPF_ContainsInstancedReference; } - auto* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UStructProperty(FObjectInitializer()); + UStructProperty* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UStructProperty(FObjectInitializer()); Result->Struct = VarProperty.Struct; + UHTMakefile.AddStructProperty(UnrealSourceFile, Result); return Result; } case CPT_Delegate: - return new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UDelegateProperty(FObjectInitializer()); + { + UDelegateProperty* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UDelegateProperty(FObjectInitializer()); + UHTMakefile.AddDelegateProperty(UnrealSourceFile, Result); + return Result; + } case CPT_MulticastDelegate: - return new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UMulticastDelegateProperty(FObjectInitializer()); + { + UMulticastDelegateProperty* Result = new (EC_InternalUseOnlyConstructor, Scope, Name, ObjectFlags) UMulticastDelegateProperty(FObjectInitializer()); + UHTMakefile.AddMulticastDelegateProperty(UnrealSourceFile, Result); + return Result; + } default: FError::Throwf(TEXT("Unknown property type %i"), (uint8)VarProperty.Type); @@ -670,7 +741,7 @@ namespace */ void ValidateMetaDataFormat(UField* Field, const FString& InKey, const FString& InValue) { - switch ((ECheckedMetadataSpecifier)Algo::FindSortedStringCaseInsensitive(*InKey, GCheckedMetadataSpecifierStrings, ARRAY_COUNT(GCheckedMetadataSpecifierStrings))) + switch ((ECheckedMetadataSpecifier)Algo::FindSortedStringCaseInsensitive(*InKey, GCheckedMetadataSpecifierStrings)) { default: { @@ -1038,9 +1109,10 @@ void AddModuleRelativePathToMetadata(UField* Type, TMap &MetaDat // // Compile an enumeration definition. // -UEnum* FHeaderParser::CompileEnum(FUnrealSourceFile& SourceFile) +UEnum* FHeaderParser::CompileEnum() { - auto Scope = SourceFile.GetScope(); + FUnrealSourceFile* CurrentSourceFile = GetCurrentSourceFile(); + auto Scope = CurrentSourceFile->GetScope(); CheckAllow( TEXT("'Enum'"), ALLOW_TypeDecl ); @@ -1107,13 +1179,12 @@ UEnum* FHeaderParser::CompileEnum(FUnrealSourceFile& SourceFile) } ParseFieldMetaData(EnumToken.MetaData, EnumToken.Identifier); - // Create enum definition. - UEnum* Enum = new(EC_InternalUseOnlyConstructor, SourceFile.GetPackage(), EnumToken.Identifier, RF_Public) UEnum(FObjectInitializer()); + UEnum* Enum = new(EC_InternalUseOnlyConstructor, CurrentSourceFile->GetPackage(), EnumToken.Identifier, RF_Public) UEnum(FObjectInitializer()); Scope->AddType(Enum); - AddTypeDefinition(SourceFile, Enum, InputLine); - + AddTypeDefinition(UHTMakefile, CurrentSourceFile, Enum, InputLine); + UHTMakefile.AddEnum(CurrentSourceFile, Enum); // Validate the metadata for the enum ValidateMetaDataFormat(Enum, EnumToken.MetaData); @@ -1133,6 +1204,7 @@ UEnum* FHeaderParser::CompileEnum(FUnrealSourceFile& SourceFile) } GEnumUnderlyingTypes.Add(Enum, CPT_Byte); + UHTMakefile.AddGEnumUnderlyingType(CurrentSourceFile, Enum, CPT_Byte); } // Get opening brace. @@ -1618,9 +1690,10 @@ EAccessSpecifier FHeaderParser::ParseAccessProtectionSpecifier(FToken& Token) /** * Compile a struct definition. */ -UScriptStruct* FHeaderParser::CompileStructDeclaration(FClasses& AllClasses, FUnrealSourceFile& SourceFile) +UScriptStruct* FHeaderParser::CompileStructDeclaration(FClasses& AllClasses) { - auto Scope = SourceFile.GetScope(); + FUnrealSourceFile* CurrentSourceFile = GetCurrentSourceFile(); + auto Scope = CurrentSourceFile->GetScope(); // Make sure structs can be declared here. CheckAllow( TEXT("'struct'"), ALLOW_TypeDecl );//@TODO: UCREMOVAL: After the switch: Make this require global scope @@ -1668,7 +1741,7 @@ UScriptStruct* FHeaderParser::CompileStructDeclaration(FClasses& AllClasses, FUn // Process the list of specifiers for (const FPropertySpecifier& Specifier : SpecifiersFound) { - switch ((EStructSpecifier)Algo::FindSortedStringCaseInsensitive(*Specifier.Key, GStructSpecifierStrings, ARRAY_COUNT(GStructSpecifierStrings))) + switch ((EStructSpecifier)Algo::FindSortedStringCaseInsensitive(*Specifier.Key, GStructSpecifierStrings)) { default: { @@ -1834,14 +1907,14 @@ UScriptStruct* FHeaderParser::CompileStructDeclaration(FClasses& AllClasses, FUn { StructFlags |= (BaseStruct->StructFlags&STRUCT_Inherit); } - // Create. - UScriptStruct* Struct = new(EC_InternalUseOnlyConstructor, SourceFile.GetPackage(), *EffectiveStructName, RF_Public) UScriptStruct(FObjectInitializer(), BaseStruct); + UScriptStruct* Struct = new(EC_InternalUseOnlyConstructor, CurrentSourceFile->GetPackage(), *EffectiveStructName, RF_Public) UScriptStruct(FObjectInitializer(), BaseStruct); + UHTMakefile.AddScriptStruct(CurrentSourceFile, Struct); Scope->AddType(Struct); - FScope::AddTypeScope(Struct, &SourceFile.GetScope().Get()); + FScope::AddTypeScope(Struct, &CurrentSourceFile->GetScope().Get(), CurrentSourceFile, UHTMakefile); - AddTypeDefinition(SourceFile, Struct, InputLine); + AddTypeDefinition(UHTMakefile, CurrentSourceFile, Struct, InputLine); AddModuleRelativePathToMetadata(Struct, MetaData); @@ -1884,7 +1957,8 @@ UScriptStruct* FHeaderParser::CompileStructDeclaration(FClasses& AllClasses, FUn StructToken.Struct = Struct; // add this struct to the compiler's persistent tracking system - GScriptHelper.AddClassData(StructToken.Struct); + FClassMetaData* ClassMetaData = GScriptHelper.AddClassData(StructToken.Struct, UHTMakefile, CurrentSourceFile); + UHTMakefile.AddGScriptHelperEntry(CurrentSourceFile, Struct, ClassMetaData); } int32 SavedLineNumber = InputLine; @@ -1927,7 +2001,7 @@ UScriptStruct* FHeaderParser::CompileStructDeclaration(FClasses& AllClasses, FUn Struct->StructMacroDeclaredLineNumber = InputLine; RequireSymbol(TEXT("("), TEXT("'struct'")); - CompileVersionDeclaration(SourceFile, Struct); + CompileVersionDeclaration(Struct); RequireSymbol(TEXT(")"), TEXT("'struct'")); @@ -2480,8 +2554,10 @@ void FHeaderParser::VerifyRepNotifyCallbacks( UClass* TargetClass ) // // Process a compiler directive. // -void FHeaderParser::CompileDirective(FClasses& AllClasses, FUnrealSourceFile& SourceFile) +void FHeaderParser::CompileDirective(FClasses& AllClasses) { + FUnrealSourceFile* CurrentSourceFilePtr = GetCurrentSourceFile(); + TSharedRef CurrentSourceFile = CurrentSourceFilePtr->AsShared(); FToken Directive; int32 LineAtStartOfDirective = InputLine; @@ -2516,7 +2592,7 @@ void FHeaderParser::CompileDirective(FClasses& AllClasses, FUnrealSourceFile& So } else if (Directive.Matches(TEXT("include"))) { - FString ExpectedHeaderName = SourceFile.GetGeneratedHeaderFilename(); + FString ExpectedHeaderName = CurrentSourceFile->GetGeneratedHeaderFilename(); FToken IncludeName; if (GetToken(IncludeName) && (IncludeName.TokenType == TOKEN_Const) && (IncludeName.Type == CPT_String)) { @@ -2680,7 +2756,7 @@ FIndexRange* ParsedVarIndexRange bool bSeenBlueprintEditSpecifier = false; for (const FPropertySpecifier& Specifier : SpecifiersFound) { - EVariableSpecifier SpecID = (EVariableSpecifier)Algo::FindSortedStringCaseInsensitive(*Specifier.Key, GVariableSpecifierStrings, ARRAY_COUNT(GVariableSpecifierStrings)); + EVariableSpecifier SpecID = (EVariableSpecifier)Algo::FindSortedStringCaseInsensitive(*Specifier.Key, GVariableSpecifierStrings); if (VariableCategory == EVariableCategory::Member) { switch (SpecID) @@ -3279,7 +3355,9 @@ FIndexRange* ParsedVarIndexRange OriginalVarTypeFlags |= VarProperty.PropertyFlags & (CPF_ContainsInstancedReference | CPF_InstancedReference); // propagate these to the array, we will fix them later OriginalVarTypeFlags |= MapKeyType .PropertyFlags & (CPF_ContainsInstancedReference | CPF_InstancedReference); // propagate these to the array, we will fix them later VarType.PropertyFlags = OriginalVarTypeFlags; - VarProperty.MapKeyProp = MakeShareable(new FToken(MapKeyType)); + FToken* MapKeyProp = new FToken(MapKeyType); + VarProperty.MapKeyProp = MakeShareable(MapKeyProp); + UHTMakefile.AddToken(GetCurrentSourceFile(), MapKeyProp); VarProperty.MapKeyProp->PropertyFlags = OriginalVarTypeFlags; FToken CloseTemplateToken; @@ -3877,6 +3955,7 @@ UProperty* FHeaderParser::GetVarNameAndDim { check(Scope); + FUnrealSourceFile* CurrentSourceFile = GetCurrentSourceFile(); EObjectFlags ObjectFlags = RF_Public; if (VariableCategory == EVariableCategory::Member && CurrentAccessSpecifier == ACCESS_Private) { @@ -4107,6 +4186,7 @@ UProperty* FHeaderParser::GetVarNameAndDim if (VarProperty.ArrayType == EArrayType::Dynamic) { Array = new (EC_InternalUseOnlyConstructor, Scope, PropertyName, ObjectFlags) UArrayProperty(FObjectInitializer()); + UHTMakefile.AddArrayProperty(CurrentSourceFile, Array); NewScope = Array; ObjectFlags = RF_Public; } @@ -4117,12 +4197,13 @@ UProperty* FHeaderParser::GetVarNameAndDim else if (VarProperty.MapKeyProp.IsValid()) { Map = new (EC_InternalUseOnlyConstructor, Scope, PropertyName, ObjectFlags) UMapProperty(FObjectInitializer()); + UHTMakefile.AddMapProperty(CurrentSourceFile, Map); NewScope = Map; ObjectFlags = RF_Public; - NewMapKeyProperty = CreateVariableProperty(*VarProperty.MapKeyProp, NewScope, *(PropertyName.ToString() + TEXT("_Key")), ObjectFlags, VariableCategory); + NewMapKeyProperty = CreateVariableProperty(*VarProperty.MapKeyProp, NewScope, *(PropertyName.ToString() + TEXT("_Key")), ObjectFlags, VariableCategory, UHTMakefile, CurrentSourceFile); } - NewProperty = CreateVariableProperty(VarProperty, NewScope, PropertyName, ObjectFlags, VariableCategory); + NewProperty = CreateVariableProperty(VarProperty, NewScope, PropertyName, ObjectFlags, VariableCategory, UHTMakefile, CurrentSourceFile); auto PropagateFlags = [](uint64 FlagsToPropagate, FPropertyBase& From, UProperty* To) { // Copy some of the property flags to the inner property. @@ -4184,11 +4265,10 @@ UProperty* FHeaderParser::GetVarNameAndDim VarProperty.TokenProperty = NewProperty; FClassMetaData* ScopeData = GScriptHelper.FindClassData(Scope); check(ScopeData); - ScopeData->AddProperty(VarProperty); + ScopeData->AddProperty(VarProperty, UHTMakefile, CurrentSourceFile); // if we had any metadata, add it to the class AddMetaDataToClassData(VarProperty.TokenProperty, VarProperty.MetaData); - return NewProperty; } @@ -4199,7 +4279,7 @@ UProperty* FHeaderParser::GetVarNameAndDim // // Compile a declaration in Token. Returns 1 if compiled, 0 if not. // -bool FHeaderParser::CompileDeclaration(FClasses& AllClasses, FUnrealSourceFile& SourceFile, FToken& Token) +bool FHeaderParser::CompileDeclaration(FClasses& AllClasses, FToken& Token) { EAccessSpecifier AccessSpecifier = ParseAccessProtectionSpecifier(Token); if (AccessSpecifier) @@ -4241,7 +4321,7 @@ bool FHeaderParser::CompileDeclaration(FClasses& AllClasses, FUnrealSourceFile& FError::Throwf(TEXT("%s must occur inside the native interface definition"), Token.Identifier); } RequireSymbol(TEXT("("), Token.Identifier); - CompileVersionDeclaration(SourceFile, GetCurrentClass()); + CompileVersionDeclaration(GetCurrentClass()); RequireSymbol(TEXT(")"), Token.Identifier); auto* ClassData = GetCurrentClassData(); @@ -4270,7 +4350,7 @@ bool FHeaderParser::CompileDeclaration(FClasses& AllClasses, FUnrealSourceFile& FError::Throwf(TEXT("%s must occur inside the interface definition"), Token.Identifier); } RequireSymbol(TEXT("("), Token.Identifier); - CompileVersionDeclaration(SourceFile, GetCurrentClass()); + CompileVersionDeclaration(GetCurrentClass()); RequireSymbol(TEXT(")"), Token.Identifier); auto* ClassData = GetCurrentClassData(); @@ -4313,7 +4393,7 @@ bool FHeaderParser::CompileDeclaration(FClasses& AllClasses, FUnrealSourceFile& } RequireSymbol(TEXT("("), Token.Identifier); - CompileVersionDeclaration(SourceFile, GetCurrentClass()); + CompileVersionDeclaration(GetCurrentClass()); RequireSymbol(TEXT(")"), Token.Identifier); ClassData->SetGeneratedBodyLine(InputLine); @@ -4340,19 +4420,19 @@ bool FHeaderParser::CompileDeclaration(FClasses& AllClasses, FUnrealSourceFile& if (Token.Matches(TEXT("UFUNCTION"), ESearchCase::CaseSensitive)) { - CompileFunctionDeclaration(SourceFile, AllClasses); + CompileFunctionDeclaration(AllClasses); return true; } if (Token.Matches(TEXT("UDELEGATE"))) { - CompileDelegateDeclaration(SourceFile, AllClasses, Token.Identifier, EDelegateSpecifierAction::Parse); + CompileDelegateDeclaration(AllClasses, Token.Identifier, EDelegateSpecifierAction::Parse); return true; } if (IsValidDelegateDeclaration(Token)) // Legacy delegate parsing - it didn't need a UDELEGATE { - CompileDelegateDeclaration(SourceFile, AllClasses, Token.Identifier); + CompileDelegateDeclaration(AllClasses, Token.Identifier); return true; } @@ -4368,21 +4448,21 @@ bool FHeaderParser::CompileDeclaration(FClasses& AllClasses, FUnrealSourceFile& if (Token.Matches(TEXT("UENUM"))) { // Enumeration definition. - CompileEnum(SourceFile); + CompileEnum(); return true; } if (Token.Matches(TEXT("USTRUCT"))) { // Struct definition. - CompileStructDeclaration(AllClasses, SourceFile); + CompileStructDeclaration(AllClasses); return true; } if (Token.Matches(TEXT("#"))) { // Compiler directive. - CompileDirective(AllClasses, SourceFile); + CompileDirective(AllClasses); return true; } @@ -4614,12 +4694,14 @@ bool FHeaderParser::SafeMatchSymbol( const TCHAR* Match ) FClass* FHeaderParser::ParseClassNameDeclaration(FClasses& AllClasses, FString& DeclaredClassName, FString& RequiredAPIMacroIfPresent) { + FUnrealSourceFile* CurrentSourceFile = GetCurrentSourceFile(); ParseNameWithPotentialAPIMacroPrefix(/*out*/ DeclaredClassName, /*out*/ RequiredAPIMacroIfPresent, TEXT("class")); FClass* FoundClass = AllClasses.FindClass(*GetClassNameWithPrefixRemoved(*DeclaredClassName)); check(FoundClass); - GScriptHelper.AddClassData(FoundClass); + FClassMetaData* ClassMetaData = GScriptHelper.AddClassData(FoundClass, UHTMakefile, CurrentSourceFile); + UHTMakefile.AddGScriptHelperEntry(CurrentSourceFile, FoundClass, ClassMetaData); // Get parent class. bool bSpecifiesParentClass = false; @@ -4706,6 +4788,7 @@ FClass* FHeaderParser::ParseClassNameDeclaration(FClasses& AllClasses, FString& void FHeaderParser::HandleOneInheritedClass(FClasses& AllClasses, UClass* Class, FString InterfaceName) { + FUnrealSourceFile* CurrentSourceFile = GetCurrentSourceFile(); // Check for UInterface derived interface inheritance if (UClass* Interface = AllClasses.FindScriptClass(InterfaceName)) { @@ -4723,7 +4806,7 @@ void FHeaderParser::HandleOneInheritedClass(FClasses& AllClasses, UClass* Class, { FClassMetaData* ClassData = GScriptHelper.FindClassData(Class); check(ClassData); - ClassData->AddInheritanceParent(Interface); + ClassData->AddInheritanceParent(Interface, UHTMakefile, CurrentSourceFile); } } else @@ -4731,7 +4814,7 @@ void FHeaderParser::HandleOneInheritedClass(FClasses& AllClasses, UClass* Class, // Non-UObject inheritance FClassMetaData* ClassData = GScriptHelper.FindClassData(Class); check(ClassData); - ClassData->AddInheritanceParent(InterfaceName); + ClassData->AddInheritanceParent(InterfaceName, UHTMakefile, CurrentSourceFile); } } @@ -4819,7 +4902,9 @@ void FHeaderParser::CompileClassDeclaration(FClasses& AllClasses) // Make sure our parent classes is parsed. for (UClass* Temp = Class->GetSuperClass(); Temp; Temp = Temp->GetSuperClass()) { - if (!(Temp->ClassFlags & (CLASS_Parsed | CLASS_Intrinsic))) + bool bIsParsed = !!(Temp->ClassFlags & CLASS_Parsed); + bool bIsIntrinsic = !!(Temp->ClassFlags & CLASS_Intrinsic); + if (!(bIsParsed || bIsIntrinsic)) { FError::Throwf(TEXT("'%s' can't be compiled: Parent class '%s' has errors"), *Class->GetName(), *Temp->GetName()); } @@ -4834,7 +4919,7 @@ void FHeaderParser::CompileClassDeclaration(FClasses& AllClasses) ClassData->SetPrologLine(PrologFinishLine); ClassDeclarationData->MergeAndValidateClassFlags(DeclaredClassName, PrevClassFlags, Class, AllClasses); - Class->SetFlags(RF_Native); + Class->SetInternalFlags(EInternalObjectFlags::Native); // Class metadata MetaData.Append(ClassDeclarationData->MetaData); @@ -4873,7 +4958,7 @@ void FHeaderParser::CompileClassDeclaration(FClasses& AllClasses) for (int32 ParentIndex = InheritanceParents.Num() - 1; ParentIndex >= 0; ParentIndex--) { // if this base class corresponds to an interface class, assign the vtable UProperty in the class's Interfaces map now... - if (UClass* InheritedInterface = InheritanceParents[ParentIndex].InterfaceClass) + if (UClass* InheritedInterface = InheritanceParents[ParentIndex]->InterfaceClass) { if (FImplementedInterface* Found = Class->Interfaces.FindByPredicate([=](const FImplementedInterface& Impl) { return Impl.Class == InheritedInterface; })) { @@ -4974,6 +5059,7 @@ bool FHeaderParser::TryParseIInterfaceClass(FClasses& AllClasses) */ void FHeaderParser::CompileInterfaceDeclaration(FClasses& AllClasses) { + FUnrealSourceFile* CurrentSourceFile = GetCurrentSourceFile(); // Start of an interface block. Since Interfaces and Classes are always at the same nesting level, // whereever a class declaration is allowed, an interface declaration is also allowed. CheckAllow( TEXT("'interface'"), ALLOW_Class ); @@ -5014,13 +5100,13 @@ void FHeaderParser::CompileInterfaceDeclaration(FClasses& AllClasses) FError::Throwf(TEXT("Native classes cannot extend non-native classes") ); } - InterfaceClass->SetFlags(RF_Native); + InterfaceClass->SetInternalFlags(EInternalObjectFlags::Native); InterfaceClass->ClassFlags |= CLASS_Native; // Process all of the interface specifiers for (const FPropertySpecifier& Specifier : SpecifiersFound) { - switch ((EInterfaceSpecifier)Algo::FindSortedStringCaseInsensitive(*Specifier.Key, GInterfaceSpecifierStrings, ARRAY_COUNT(GInterfaceSpecifierStrings))) + switch ((EInterfaceSpecifier)Algo::FindSortedStringCaseInsensitive(*Specifier.Key, GInterfaceSpecifierStrings)) { default: { @@ -5056,7 +5142,8 @@ void FHeaderParser::CompileInterfaceDeclaration(FClasses& AllClasses) } // Try parsing metadata for the interface - FClassMetaData* ClassData = GScriptHelper.AddClassData(InterfaceClass); + FClassMetaData* ClassData = GScriptHelper.AddClassData(InterfaceClass, UHTMakefile, CurrentSourceFile); + UHTMakefile.AddGScriptHelperEntry(CurrentSourceFile, InterfaceClass, ClassData); check(ClassData); ClassData->SetPrologLine(PrologFinishLine); @@ -5210,12 +5297,11 @@ void FHeaderParser::ParseParameterList(FClasses& AllClasses, UFunction* Function } while( MatchSymbol(TEXT(",")) ); RequireSymbol( TEXT(")"), TEXT("parameter list") ); } - -void FHeaderParser::CompileDelegateDeclaration(FUnrealSourceFile& SourceFile, FClasses& AllClasses, const TCHAR* DelegateIdentifier, EDelegateSpecifierAction::Type SpecifierAction) +void FHeaderParser::CompileDelegateDeclaration(FClasses& AllClasses, const TCHAR* DelegateIdentifier, EDelegateSpecifierAction::Type SpecifierAction) { + FUnrealSourceFile* CurrentSourceFile = GetCurrentSourceFile(); TMap MetaData; - - AddModuleRelativePathToMetadata(SourceFile, MetaData); + AddModuleRelativePathToMetadata(*CurrentSourceFile, MetaData); FFuncInfo FuncInfo; @@ -5326,8 +5412,10 @@ void FHeaderParser::CompileDelegateDeclaration(FUnrealSourceFile& SourceFile, FC FuncInfo.MacroLine = InputLine; auto* DelegateSignatureFunction = CreateDelegateFunction(FuncInfo); + UHTMakefile.AddDelegateFunction(CurrentSourceFile, DelegateSignatureFunction); - GScriptHelper.AddClassData(DelegateSignatureFunction); + FClassMetaData* ClassMetaData = GScriptHelper.AddClassData(DelegateSignatureFunction, UHTMakefile, CurrentSourceFile); + UHTMakefile.AddGScriptHelperEntry(CurrentSourceFile, DelegateSignatureFunction, ClassMetaData); DelegateSignatureFunction->FunctionFlags |= FuncInfo.FunctionFlags; @@ -5407,13 +5495,13 @@ void FHeaderParser::CompileDelegateDeclaration(FUnrealSourceFile& SourceFile, FC /** * Parses and compiles a function declaration */ -void FHeaderParser::CompileFunctionDeclaration(FUnrealSourceFile& SourceFile, FClasses& AllClasses) +void FHeaderParser::CompileFunctionDeclaration(FClasses& AllClasses) { CheckAllow(TEXT("'Function'"), ALLOW_Function); + FUnrealSourceFile* CurrentSourceFile = GetCurrentSourceFile(); TMap MetaData; - - AddModuleRelativePathToMetadata(SourceFile, MetaData); + AddModuleRelativePathToMetadata(*CurrentSourceFile, MetaData); // New-style UFUNCTION() syntax TArray SpecifiersFound; @@ -5691,8 +5779,10 @@ void FHeaderParser::CompileFunctionDeclaration(FUnrealSourceFile& SourceFile, FC } auto* TopFunction = CreateFunction(FuncInfo); + UHTMakefile.AddFunction(CurrentSourceFile, TopFunction); - GScriptHelper.AddClassData(TopFunction); + FClassMetaData* ClassMetaData = GScriptHelper.AddClassData(TopFunction, UHTMakefile, CurrentSourceFile); + UHTMakefile.AddGScriptHelperEntry(CurrentSourceFile, TopFunction, ClassMetaData); TopFunction->FunctionFlags |= FuncInfo.FunctionFlags; @@ -6139,7 +6229,6 @@ void FHeaderParser::CompileVariableDeclaration(FClasses& AllClasses, UStruct* St // Get variable type. FPropertyBase OriginalProperty(CPT_None); - FIndexRange TypeRange; GetVarType( AllClasses, &FScope::GetTypeScope(Struct).Get(), OriginalProperty, DisallowFlags, /*OuterPropertyType=*/ NULL, EPropertyDeclarationStyle::UPROPERTY, EVariableCategory::Member, &TypeRange ); OriginalProperty.PropertyFlags |= EdFlags; @@ -6286,7 +6375,7 @@ void FHeaderParser::CompileVariableDeclaration(FClasses& AllClasses, UStruct* St // Compile a statement: Either a declaration or a command. // Returns 1 if success, 0 if end of file. // -bool FHeaderParser::CompileStatement(FClasses& AllClasses, FUnrealSourceFile& SourceFile) +bool FHeaderParser::CompileStatement(FClasses& AllClasses) { // Get a token and compile it. FToken Token; @@ -6295,7 +6384,7 @@ bool FHeaderParser::CompileStatement(FClasses& AllClasses, FUnrealSourceFile& So // End of file. return false; } - else if (!CompileDeclaration(AllClasses, SourceFile, Token)) + else if (!CompileDeclaration(AllClasses, Token)) { FError::Throwf(TEXT("'%s': Bad command or expression"), Token.Identifier ); } @@ -6433,17 +6522,21 @@ void FHeaderParser::FinalizeScriptExposedFunctions(UClass* Class) // Parses the header associated with the specified class. // Returns result enumeration. // -ECompilationResult::Type FHeaderParser::ParseHeader(FClasses& AllClasses, FUnrealSourceFile& SourceFile) +ECompilationResult::Type FHeaderParser::ParseHeader(FClasses& AllClasses, FUnrealSourceFile* SourceFile) { - if (SourceFile.IsParsed()) + SetCurrentSourceFile(SourceFile); + UHTMakefile.AddToHeaderOrder(SourceFile); + NameLookupCPP.SetCurrentSourceFile(SourceFile); + FUnrealSourceFile* CurrentSourceFile = SourceFile; + if (CurrentSourceFile->IsParsed()) { return ECompilationResult::Succeeded; } - SourceFile.MarkAsParsed(); + CurrentSourceFile->MarkAsParsed(); // Early-out if this class has previously failed some aspect of parsing - if (FailedFilesAnnotation.Get(&SourceFile)) + if (FailedFilesAnnotation.Get(CurrentSourceFile)) { return ECompilationResult::OtherCompilationError; } @@ -6462,16 +6555,16 @@ ECompilationResult::Type FHeaderParser::ParseHeader(FClasses& AllClasses, FUnrea if (FParse::Param(FCommandLine::Get(), TEXT("VERBOSE"))) { // Message. - Warn->Logf(TEXT("Parsing %s"), *SourceFile.GetFilename()); + Warn->Logf(TEXT("Parsing %s"), *CurrentSourceFile->GetFilename()); } // Init compiler variables. - ResetParser(*SourceFile.GetContent()); + ResetParser(*CurrentSourceFile->GetContent()); // Init nesting. NestLevel = 0; TopNest = NULL; - PushNest(NEST_GlobalScope, nullptr, &SourceFile); + PushNest(NEST_GlobalScope, nullptr, CurrentSourceFile); // C++ classes default to private access level CurrentAccessSpecifier = ACCESS_Private; @@ -6487,7 +6580,7 @@ ECompilationResult::Type FHeaderParser::ParseHeader(FClasses& AllClasses, FUnrea #endif { // Parse entire program. - while (CompileStatement(AllClasses, SourceFile)) + while (CompileStatement(AllClasses)) { bEmptyFile = false; @@ -6498,7 +6591,7 @@ ECompilationResult::Type FHeaderParser::ParseHeader(FClasses& AllClasses, FUnrea PopNest(NEST_GlobalScope, TEXT("Global scope")); - auto ScopeTypeIterator = SourceFile.GetScope()->GetTypeIterator(); + auto ScopeTypeIterator = CurrentSourceFile->GetScope()->GetTypeIterator(); while (ScopeTypeIterator.MoveNext()) { auto* Type = *ScopeTypeIterator; @@ -6548,7 +6641,7 @@ ECompilationResult::Type FHeaderParser::ParseHeader(FClasses& AllClasses, FUnrea // First-pass success. Result = ECompilationResult::Succeeded; - for (auto* Class : SourceFile.GetDefinedClasses()) + for (auto* Class : CurrentSourceFile->GetDefinedClasses()) { PostParsingClassSetup(Class); @@ -6564,11 +6657,11 @@ ECompilationResult::Type FHeaderParser::ParseHeader(FClasses& AllClasses, FUnrea bNoExportClassesOnly = bNoExportClassesOnly && Class->HasAnyClassFlags(CLASS_NoExport); } - check(SourceFile.IsParsed()); + check(CurrentSourceFile->IsParsed()); if (!bSpottedAutogeneratedHeaderInclude && !bEmptyFile && !bNoExportClassesOnly) { - const FString ExpectedHeaderName = SourceFile.GetGeneratedHeaderFilename(); + const FString ExpectedHeaderName = CurrentSourceFile->GetGeneratedHeaderFilename(); FError::Throwf(TEXT("Expected an include at the top of the header: '#include \"%s\"'"), *ExpectedHeaderName); } } @@ -6578,7 +6671,7 @@ ECompilationResult::Type FHeaderParser::ParseHeader(FClasses& AllClasses, FUnrea if (NestLevel == 0) { // Pushing nest so there is a file context for this error. - PushNest(NEST_GlobalScope, nullptr, &SourceFile); + PushNest(NEST_GlobalScope, nullptr, CurrentSourceFile); } // Handle compiler error. @@ -6590,7 +6683,7 @@ ECompilationResult::Type FHeaderParser::ParseHeader(FClasses& AllClasses, FUnrea Warn->Log(ELogVerbosity::Error, ErrorMsg); } - FailedFilesAnnotation.Set(&SourceFile); + FailedFilesAnnotation.Set(CurrentSourceFile); Result = GCompilationResult; } #endif @@ -6602,16 +6695,16 @@ ECompilationResult::Type FHeaderParser::ParseHeader(FClasses& AllClasses, FUnrea Global functions. -----------------------------------------------------------------------------*/ -ECompilationResult::Type FHeaderParser::ParseRestOfModulesSourceFiles(FClasses& AllClasses, UPackage* ModulePackage, FHeaderParser& HeaderParser) +ECompilationResult::Type FHeaderParser::ParseRestOfModulesSourceFiles(FClasses& AllClasses, UPackage* ModulePackage, FHeaderParser& HeaderParser, FUHTMakefile& UHTMakefile) { for (auto& Pair : GUnrealSourceFilesMap) { - FUnrealSourceFile& SourceFile = Pair.Value.Get(); + FUnrealSourceFile* SourceFile = &Pair.Value.Get(); - if (SourceFile.GetPackage() == ModulePackage && (!SourceFile.IsParsed() || SourceFile.GetDefinedClassesCount() == 0)) + if (SourceFile->GetPackage() == ModulePackage && (!SourceFile->IsParsed() || SourceFile->GetDefinedClassesCount() == 0)) { ECompilationResult::Type Result; - if ((Result = ParseHeaders(AllClasses, HeaderParser, SourceFile, true)) != ECompilationResult::Succeeded) + if ((Result = ParseHeaders(AllClasses, HeaderParser, SourceFile, true, UHTMakefile)) != ECompilationResult::Succeeded) { return Result; } @@ -6622,22 +6715,23 @@ ECompilationResult::Type FHeaderParser::ParseRestOfModulesSourceFiles(FClasses& } // Parse Class's annotated headers and optionally its child classes. -ECompilationResult::Type FHeaderParser::ParseHeaders(FClasses& AllClasses, FHeaderParser& HeaderParser, FUnrealSourceFile& SourceFile, bool bParseSubclasses) +ECompilationResult::Type FHeaderParser::ParseHeaders(FClasses& AllClasses, FHeaderParser& HeaderParser, FUnrealSourceFile* SourceFile, bool bParseSubclasses, FUHTMakefile& UHTMakefile) { ECompilationResult::Type Result = ECompilationResult::Succeeded; - if (SourceFile.AreDependenciesResolved()) + if (SourceFile->AreDependenciesResolved()) { return Result; } - SourceFile.MarkDependenciesResolved(); + SourceFile->MarkDependenciesResolved(); TArray SourceFilesRequired; - for (auto& Include : SourceFile.GetIncludes()) + static const FString ObjectHeader = FString(TEXT("Object.h")); + for (auto& Include : SourceFile->GetIncludes()) { - if (Include.GetId() == "Object.h") + if (Include.GetId() == ObjectHeader) { continue; } @@ -6650,7 +6744,7 @@ ECompilationResult::Type FHeaderParser::ParseHeaders(FClasses& AllClasses, FHead } } - auto Classes = SourceFile.GetDefinedClasses(); + auto Classes = SourceFile->GetDefinedClasses(); for (auto* Class : Classes) { @@ -6659,19 +6753,20 @@ ECompilationResult::Type FHeaderParser::ParseHeaders(FClasses& AllClasses, FHead SourceFilesRequired.Add(>ypeDefinitionInfoMap[ParentClass]->GetUnrealSourceFile()); } } + UHTMakefile.GetHeaderDescriptor(SourceFile).AddPrerequesites(SourceFilesRequired); for (auto* RequiredFile : SourceFilesRequired) { - SourceFile.GetScope()->IncludeScope(&RequiredFile->GetScope().Get()); + SourceFile->GetScope()->IncludeScope(&RequiredFile->GetScope().Get()); - ECompilationResult::Type SuperClassParseResult = ParseHeaders(AllClasses, HeaderParser, *RequiredFile, true); + ECompilationResult::Type SuperClassParseResult = ParseHeaders(AllClasses, HeaderParser, RequiredFile, true, UHTMakefile); if (SuperClassParseResult == ECompilationResult::Succeeded) { continue; } - SuperClassParseResult = ParseHeaders(AllClasses, HeaderParser, *RequiredFile, false); + SuperClassParseResult = ParseHeaders(AllClasses, HeaderParser, RequiredFile, false, UHTMakefile); if (SuperClassParseResult != ECompilationResult::Succeeded) { @@ -6769,6 +6864,8 @@ void FHeaderParser::ExportNativeHeaders( #if WITH_HOT_RELOAD_CTORS , bool bExportVTableConstructors #endif // WITH_HOT_RELOAD_CTORS + , FUHTMakefile& UHTMakefile + , const FManifestModule& Module ) { // Build a list of header filenames @@ -6776,7 +6873,6 @@ void FHeaderParser::ExportNativeHeaders( new (ClassHeaderFilenames) FString(); auto SourceFiles = GetSourceFilesWithInheritanceOrdering(CurrentPackage, AllClasses); - if (SourceFiles.Num() > 0) { const static bool bQuiet = !FParse::Param(FCommandLine::Get(),TEXT("VERBOSE")); @@ -6802,7 +6898,19 @@ void FHeaderParser::ExportNativeHeaders( UE_LOG(LogCompile, Warning, TEXT("Exporting native class declarations")); } } + UHTMakefile.StartExporting(); + + FName ModuleName = FName(*Module.Name); + bool bNeedsRegeneration = Module.NeedsRegeneration(); + bool bUHTMakefileContainsModuleData = UHTMakefile.HasModule(ModuleName); + bool bLoadFromMakefile = !bNeedsRegeneration && bUHTMakefileContainsModuleData; + if (bLoadFromMakefile) + { + UHTMakefile.LoadModuleData(ModuleName, Module); + } + else + { // Export native class definitions to package header files. FNativeClassHeaderGenerator( CurrentPackage, @@ -6812,15 +6920,19 @@ void FHeaderParser::ExportNativeHeaders( #if WITH_HOT_RELOAD_CTORS , bExportVTableConstructors #endif // WITH_HOT_RELOAD_CTORS + , UHTMakefile ); } + UHTMakefile.StopExporting(); +} } -FHeaderParser::FHeaderParser(FFeedbackContext* InWarn) +FHeaderParser::FHeaderParser(FFeedbackContext* InWarn, FUHTMakefile& InUHTMakefile) : FBaseParser () , Warn (InWarn) , bSpottedAutogeneratedHeaderInclude(false) , TopNest (NULL) +, UHTMakefile(InUHTMakefile) { FScriptLocation::Compiler = this; @@ -6916,19 +7028,18 @@ ECompilationResult::Type FHeaderParser::ParseAllHeadersInside( #if WITH_HOT_RELOAD_CTORS , bool bExportVTableConstructors #endif // WITH_HOT_RELOAD_CTORS -) + , FUHTMakefile& UHTMakefile + ) { // Disable loading of objects outside of this package (or more exactly, objects which aren't UFields, CDO, or templates) TGuardValue AutoRestoreVerifyObjectRefsFlag(GVerifyObjectReferencesOnly, true); - + UHTMakefile.SetCurrentModuleName(FName(*Module.Name)); // Create the header parser and register it as the warning context. // Note: This must be declared outside the try block, since the catch block will log into it. - FHeaderParser HeaderParser(Warn); + FHeaderParser HeaderParser(Warn, UHTMakefile); HeaderParser.CurrentlyParsedModule = &Module; Warn->SetContext(&HeaderParser); - // Set up a filename for the error context if we don't even get as far parsing a class - HeaderParser.Filename = IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*GTypeDefinitionInfoMap[ModuleClasses.GetRootClass()]->GetUnrealSourceFile().GetFilename()); // Hierarchically parse all classes. ECompilationResult::Type Result = ECompilationResult::Succeeded; @@ -6936,24 +7047,43 @@ ECompilationResult::Type FHeaderParser::ParseAllHeadersInside( try #endif { - for (auto* SourceFilePtr : GPublicSourceFileSet) + UHTMakefile.StartLoading(); + FName ModuleName = FName(*Module.Name); + UHTMakefile.SetCurrentModuleName(ModuleName); + bool bNeedsRegeneration = Module.NeedsRegeneration(); + bool bUHTMakefileContainsModuleData = UHTMakefile.HasModule(ModuleName); + bool bLoadFromMakefile = !bNeedsRegeneration && bUHTMakefileContainsModuleData; + if (bLoadFromMakefile) { - FUnrealSourceFile& SourceFile = *SourceFilePtr; + UHTMakefile.LoadModuleData(ModuleName, Module); + } + else + { + // Set up a filename for the error context if we don't even get as far parsing a class + FClass* RootClass = ModuleClasses.GetRootClass(); + const TSharedRef& TypeDefinitionInfo = GTypeDefinitionInfoMap[RootClass]; + const FUnrealSourceFile& RootSourceFile = TypeDefinitionInfo->GetUnrealSourceFile(); + const FString& RootFilename = RootSourceFile.GetFilename(); - if (SourceFile.GetPackage() == CurrentPackage && (!SourceFile.IsParsed() || SourceFile.GetDefinedClassesCount() == 0)) + HeaderParser.Filename = IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*RootFilename); + + for (FUnrealSourceFile* SourceFile : GPublicSourceFileSet) { - Result = ParseHeaders(ModuleClasses, HeaderParser, SourceFile, true); - if (Result != ECompilationResult::Succeeded) + if (SourceFile->GetPackage() == CurrentPackage && (!SourceFile->IsParsed() || SourceFile->GetDefinedClassesCount() == 0)) { - return Result; + Result = ParseHeaders(ModuleClasses, HeaderParser, SourceFile, true, UHTMakefile); + if (Result != ECompilationResult::Succeeded) + { + return Result; + } } } + if (Result == ECompilationResult::Succeeded) + { + Result = FHeaderParser::ParseRestOfModulesSourceFiles(ModuleClasses, CurrentPackage, HeaderParser, UHTMakefile); + } } - - if (Result == ECompilationResult::Succeeded) - { - Result = FHeaderParser::ParseRestOfModulesSourceFiles(ModuleClasses, CurrentPackage, HeaderParser); - } + UHTMakefile.StopLoading(); // Export the autogenerated code wrappers if (Result == ECompilationResult::Succeeded) @@ -6973,7 +7103,9 @@ ECompilationResult::Type FHeaderParser::ParseAllHeadersInside( #if WITH_HOT_RELOAD_CTORS , bExportVTableConstructors #endif // WITH_HOT_RELOAD_CTORS - ); + , UHTMakefile + , Module + ); } GHeaderCodeGenTime += ExportTime; @@ -6989,7 +7121,7 @@ ECompilationResult::Type FHeaderParser::ParseAllHeadersInside( } } #if !PLATFORM_EXCEPTIONS_DISABLED - catch( TCHAR* ErrorMsg ) + catch (TCHAR* ErrorMsg) { Warn->Log(ELogVerbosity::Error, ErrorMsg); Result = GCompilationResult; @@ -7896,8 +8028,10 @@ void FHeaderParser::SkipDeprecatedMacroIfNecessary() RequireSymbol(TEXT(")"), TEXT("DEPRECATED macro")); } -void FHeaderParser::CompileVersionDeclaration(FUnrealSourceFile& SourceFile, UStruct* Struct) +void FHeaderParser::CompileVersionDeclaration(UStruct* Struct) { + FUnrealSourceFile* CurrentSourceFilePtr = GetCurrentSourceFile(); + TSharedRef CurrentSourceFile = CurrentSourceFilePtr->AsShared(); // Do nothing if we're at the end of file. FToken Token; if (!GetToken(Token, true, ESymbolParseOption::Normal)) @@ -7917,7 +8051,7 @@ void FHeaderParser::CompileVersionDeclaration(FUnrealSourceFile& SourceFile, USt if (Token.TokenType == ETokenType::TOKEN_Symbol && !FCString::Stricmp(Token.Identifier, TEXT(")"))) { - SourceFile.GetGeneratedCodeVersions().FindOrAdd(Struct) = Version; + CurrentSourceFile->GetGeneratedCodeVersions().FindOrAdd(Struct) = Version; UngetToken(Token); return; } @@ -7925,7 +8059,7 @@ void FHeaderParser::CompileVersionDeclaration(FUnrealSourceFile& SourceFile, USt // Overwrite with version specified by macro. Version = ToGeneratedCodeVersion(Token.Identifier); - SourceFile.GetGeneratedCodeVersions().FindOrAdd(Struct) = Version; + CurrentSourceFile->GetGeneratedCodeVersions().FindOrAdd(Struct) = Version; } void FHeaderParser::ResetClassData() diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/HeaderParser.h b/Engine/Source/Programs/UnrealHeaderTool/Private/HeaderParser.h index 1c635eacb677..9ef93a277900 100644 --- a/Engine/Source/Programs/UnrealHeaderTool/Private/HeaderParser.h +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/HeaderParser.h @@ -13,6 +13,7 @@ class FClass; class FClasses; class FScope; class FHeaderProvider; +class FUHTMakefile; extern double GPluginOverheadTime; extern double GHeaderCodeGenTime; @@ -156,6 +157,7 @@ public: #if WITH_HOT_RELOAD_CTORS , bool bExportVTableConstructors #endif // WITH_HOT_RELOAD_CTORS + , FUHTMakefile& UHTMakefile ); // Performs a preliminary parse of the text in the specified buffer, pulling out: @@ -205,7 +207,7 @@ public: * * @return Result enumeration. */ - static ECompilationResult::Type ParseHeaders(FClasses& AllClasses, FHeaderParser& HeaderParser, FUnrealSourceFile& SourceFile, bool bParseSubclasses); + static ECompilationResult::Type ParseHeaders(FClasses& AllClasses, FHeaderParser& HeaderParser, FUnrealSourceFile* SourceFile, bool bParseSubclasses, FUHTMakefile& UHTMakefile); protected: friend struct FScriptLocation; @@ -216,6 +218,9 @@ protected: // Filename currently being parsed FString Filename; + // Makefile to which parsing intermediate data will be saved. + FUHTMakefile& UHTMakefile; + // Was the first include in the file a validly formed auto-generated header include? bool bSpottedAutogeneratedHeaderInclude; @@ -252,7 +257,12 @@ protected: */ FUnrealSourceFile* GetCurrentSourceFile() const { - return GetCurrentFileScope()->GetSourceFile(); + return CurrentSourceFile; + } + + void SetCurrentSourceFile(FUnrealSourceFile* UnrealSourceFile) + { + CurrentSourceFile = UnrealSourceFile; } /** @@ -382,7 +392,7 @@ protected: protected: // Constructor. - FHeaderParser(FFeedbackContext* InWarn); + FHeaderParser(FFeedbackContext* InWarn, FUHTMakefile& InUHTMakefile); ~FHeaderParser() { @@ -411,13 +421,14 @@ protected: /** * Parse rest of the module's source files. * - * @param AllClasses the class tree containing all classes in the current package - * @param ModulePackage current package - * @param HeaderParser the header parser + * @param AllClasses The class tree containing all classes in the current package. + * @param ModulePackage Current package. + * @param HeaderParser The header parser. + * @param UHTMakefile Makefile to which parsing data is saved. * * @return Result enumeration. */ - static ECompilationResult::Type ParseRestOfModulesSourceFiles(FClasses& AllClasses, UPackage* ModulePackage, FHeaderParser& HeaderParser); + static ECompilationResult::Type ParseRestOfModulesSourceFiles(FClasses& AllClasses, UPackage* ModulePackage, FHeaderParser& HeaderParser, FUHTMakefile& UHTMakefile); //@TODO: Remove this method static void ParseClassName(const TCHAR* Temp, FString& ClassName); @@ -431,8 +442,10 @@ protected: /** * Begins the process of exporting C++ class declarations for native classes in the specified package * - * @param CurrentPackage the package being compiled - * @param AllClasses the class tree for CurrentPackage + * @param CurrentPackage The package being compiled. + * @param AllClasses The class tree for CurrentPackage. + * @param UHTMakefile Makefile to which parsing data is saved. + * @param Module Currently exported module. */ static void ExportNativeHeaders( UPackage* CurrentPackage, @@ -441,6 +454,8 @@ protected: #if WITH_HOT_RELOAD_CTORS , bool bExportVTableConstructors #endif // WITH_HOT_RELOAD_CTORS + , FUHTMakefile& UHTMakefile + , const FManifestModule& Module ); // FContextSupplier interface. @@ -456,12 +471,12 @@ protected: * * @returns Compilation result enum. */ - ECompilationResult::Type ParseHeader(FClasses& AllClasses, FUnrealSourceFile& SourceFile); - void CompileDirective(FClasses& AllClasses, FUnrealSourceFile& SourceFile); + ECompilationResult::Type ParseHeader(FClasses& AllClasses, FUnrealSourceFile* SourceFile); + void CompileDirective(FClasses& AllClasses); void FinalizeScriptExposedFunctions(UClass* Class); - UEnum* CompileEnum(FUnrealSourceFile& SourceFile); - UScriptStruct* CompileStructDeclaration(FClasses& AllClasses, FUnrealSourceFile& SourceFile); - bool CompileDeclaration(FClasses& AllClasses, FUnrealSourceFile& SourceFile, FToken& Token); + UEnum* CompileEnum(); + UScriptStruct* CompileStructDeclaration(FClasses& AllClasses); + bool CompileDeclaration(FClasses& AllClasses, FToken& Token); /** Skip C++ (noexport) declaration. */ bool SkipDeclaration(FToken& Token); @@ -496,15 +511,15 @@ protected: UDelegateFunction* CreateDelegateFunction(const FFuncInfo &FuncInfo) const; void CompileClassDeclaration(FClasses& AllClasses); - void CompileDelegateDeclaration(FUnrealSourceFile& SourceFile, FClasses& AllClasses, const TCHAR* DelegateIdentifier, EDelegateSpecifierAction::Type SpecifierAction = EDelegateSpecifierAction::DontParse); - void CompileFunctionDeclaration(FUnrealSourceFile& SourceFile, FClasses& AllClasses); + void CompileDelegateDeclaration(FClasses& AllClasses, const TCHAR* DelegateIdentifier, EDelegateSpecifierAction::Type SpecifierAction = EDelegateSpecifierAction::DontParse); + void CompileFunctionDeclaration(FClasses& AllClasses); void CompileVariableDeclaration (FClasses& AllClasses, UStruct* Struct); void CompileInterfaceDeclaration(FClasses& AllClasses); FClass* ParseInterfaceNameDeclaration(FClasses& AllClasses, FString& DeclaredInterfaceName, FString& RequiredAPIMacroIfPresent); bool TryParseIInterfaceClass(FClasses& AllClasses); - bool CompileStatement(FClasses& AllClasses, FUnrealSourceFile& SourceFile); + bool CompileStatement(FClasses& AllClasses); // Checks to see if a particular kind of command is allowed on this nesting level. bool IsAllowedInThisNesting(uint32 AllowFlags); @@ -684,6 +699,9 @@ protected: static void ValidatePropertyIsDeprecatedIfNecessary(FPropertyBase& VarProperty, FToken* OuterPropertyType); private: + // Source file currently parsed by UHT. + FUnrealSourceFile* CurrentSourceFile; + // Module currently parsed by UHT. const FManifestModule* CurrentlyParsedModule; @@ -701,7 +719,7 @@ private: void SkipDeprecatedMacroIfNecessary(); // Parses possible version declaration in generated code, e.g. GENERATED_BODY(). - void CompileVersionDeclaration(FUnrealSourceFile& SourceFile, UStruct* Struct); + void CompileVersionDeclaration(UStruct* Struct); }; ///////////////////////////////////////////////////// diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/HeaderProvider.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/HeaderProvider.cpp index f67c4e9c8863..98c8724e301d 100644 --- a/Engine/Source/Programs/UnrealHeaderTool/Private/HeaderProvider.cpp +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/HeaderProvider.cpp @@ -26,6 +26,7 @@ bool TryFindSourceFileWithPredicate(FUnrealSourceFile*& Out, Predicate Pred) return false; } + FUnrealSourceFile* FHeaderProvider::Resolve() { if (Type != EHeaderProviderSourceType::Resolved) @@ -55,15 +56,15 @@ FUnrealSourceFile* FHeaderProvider::Resolve() { FString SlashId = TEXT("/") + Id; FString BackslashId = TEXT("\\") + Id; - TryFindSourceFileWithPredicate(Cache, + TryFindSourceFileWithPredicate(Cache, [&SlashId, &BackslashId](const FUnrealSourceFile& SourceFile) - { + { return SourceFile.GetFilename().EndsWith(SlashId) || SourceFile.GetFilename().EndsWith(BackslashId); - } - ); - } + } + ); } } + } Type = EHeaderProviderSourceType::Resolved; } @@ -92,6 +93,12 @@ const FUnrealSourceFile* FHeaderProvider::GetResolved() const return Cache; } +FUnrealSourceFile* FHeaderProvider::GetResolved() +{ + check(Type == EHeaderProviderSourceType::Resolved); + return Cache; +} + bool operator==(const FHeaderProvider& A, const FHeaderProvider& B) { if (A.Cache != B.Cache) diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/HeaderProvider.h b/Engine/Source/Programs/UnrealHeaderTool/Private/HeaderProvider.h index 511bb06f5fd6..56c147584f72 100644 --- a/Engine/Source/Programs/UnrealHeaderTool/Private/HeaderProvider.h +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/HeaderProvider.h @@ -5,12 +5,14 @@ #include "UnrealString.h" class FUnrealSourceFile; +class FUHTMakefile; enum class EHeaderProviderSourceType { ClassName, FileName, - Resolved + Resolved, + Invalid, }; class FHeaderProvider @@ -18,9 +20,16 @@ class FHeaderProvider friend bool operator==(const FHeaderProvider& A, const FHeaderProvider& B); public: FHeaderProvider(EHeaderProviderSourceType Type, const FString& Id, bool bAutoInclude = false); + FHeaderProvider() + : Type(EHeaderProviderSourceType::Invalid) + , Id(FString()) + , Cache(nullptr) + , bAutoInclude(false) + { } FUnrealSourceFile* Resolve(); const FUnrealSourceFile* GetResolved() const; + FUnrealSourceFile* GetResolved(); FString ToString() const; @@ -29,7 +38,10 @@ public: EHeaderProviderSourceType GetType() const; bool IsAutoInclude() const { return bAutoInclude; } - + void SetCache(FUnrealSourceFile* InCache) + { + Cache = InCache; + } private: EHeaderProviderSourceType Type; FString Id; diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/Manifest.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/Manifest.cpp index a315524e8e5c..6bce3171ea2a 100644 --- a/Engine/Source/Programs/UnrealHeaderTool/Private/Manifest.cpp +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/Manifest.cpp @@ -176,3 +176,72 @@ FManifest FManifest::LoadFromFile(const FString& Filename) return Result; } + +bool FManifestModule::NeedsRegeneration() const +{ + if (ShouldForceRegeneration()) + { + return true; + } + FString Timestamp; + TCHAR TimestampText[] = TEXT("Timestamp"); + Timestamp.Empty(GeneratedIncludeDirectory.Len() + ARRAY_COUNT(TimestampText)); + Timestamp += GeneratedIncludeDirectory; + Timestamp += TEXT("Timestamp"); + + if (!FPaths::FileExists(Timestamp)) + { + // No timestamp, must regenerate. + return true; + } + + FDateTime TimestampFileLastModify = IFileManager::Get().GetTimeStamp(*Timestamp); + + for (const FString& Header : PublicUObjectClassesHeaders) + { + if (IFileManager::Get().GetTimeStamp(*Header) > TimestampFileLastModify) + { + UE_LOG(LogCompile, Log, TEXT("File %s is newer than last timestamp. Regenerating reflection data for module %s."), *Header, *Name); + return true; + } + } + + for (const FString& Header : PublicUObjectHeaders) + { + if (IFileManager::Get().GetTimeStamp(*Header) > TimestampFileLastModify) + { + UE_LOG(LogCompile, Log, TEXT("File %s is newer than last timestamp. Regenerating reflection data for module %s."), *Header, *Name); + return true; + } + } + + for (const FString& Header : PrivateUObjectHeaders) + { + if (IFileManager::Get().GetTimeStamp(*Header) > TimestampFileLastModify) + { + UE_LOG(LogCompile, Log, TEXT("File %s is newer than last timestamp. Regenerating reflection data for module %s."), *Header, *Name); + return true; + } + } + + // No header is newer than timestamp, no need to regenerate. + return false; +} + +bool FManifestModule::IsCompatibleWith(const FManifestModule& ManifestModule) +{ + return Name == ManifestModule.Name + && ModuleType == ManifestModule.ModuleType + && LongPackageName == ManifestModule.LongPackageName + && BaseDirectory == ManifestModule.BaseDirectory + && IncludeBase == ManifestModule.IncludeBase + && GeneratedIncludeDirectory == ManifestModule.GeneratedIncludeDirectory + && BaseDirectory == ManifestModule.BaseDirectory + && PublicUObjectClassesHeaders == ManifestModule.PublicUObjectClassesHeaders + && PublicUObjectHeaders == ManifestModule.PublicUObjectHeaders + && PrivateUObjectHeaders == ManifestModule.PrivateUObjectHeaders + && PCH == ManifestModule.PCH + && GeneratedCPPFilenameBase == ManifestModule.GeneratedCPPFilenameBase + && SaveExportedHeaders == SaveExportedHeaders + && GeneratedCodeVersion == GeneratedCodeVersion; +} diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/Manifest.h b/Engine/Source/Programs/UnrealHeaderTool/Private/Manifest.h index a704fa7e6c62..fde24c0422b1 100644 --- a/Engine/Source/Programs/UnrealHeaderTool/Private/Manifest.h +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/Manifest.h @@ -2,7 +2,7 @@ #pragma once #include "UnrealSourceFile.h" - +#include "IScriptGeneratorPluginInterface.h" struct FManifestModule { @@ -44,6 +44,44 @@ struct FManifestModule /** Version of generated code. */ EGeneratedCodeVersion GeneratedCodeVersion; + + /** Returns true if module headers were modified since last code generation. */ + bool NeedsRegeneration() const; + + /** Returns true if modules are compatible. Used to determine if module data can be loaded from makefile. */ + bool IsCompatibleWith(const FManifestModule& ManifestModule); + + friend FArchive& operator<<(FArchive& Ar, FManifestModule& ManifestModule) + { + Ar << ManifestModule.Name; + Ar << ManifestModule.ModuleType; + Ar << ManifestModule.LongPackageName; + Ar << ManifestModule.BaseDirectory; + Ar << ManifestModule.IncludeBase; + Ar << ManifestModule.GeneratedIncludeDirectory; + Ar << ManifestModule.PublicUObjectClassesHeaders; + Ar << ManifestModule.PublicUObjectHeaders; + Ar << ManifestModule.PrivateUObjectHeaders; + Ar << ManifestModule.PCH; + Ar << ManifestModule.GeneratedCPPFilenameBase; + Ar << ManifestModule.SaveExportedHeaders; + Ar << ManifestModule.GeneratedCodeVersion; + + return Ar; + } + + bool ShouldForceRegeneration() const + { + return bForceRegeneration; + } + + void ForceRegeneration() + { + bForceRegeneration = true; + } + +private: + bool bForceRegeneration; }; @@ -65,4 +103,15 @@ struct FManifest * @return The loaded module info. */ static FManifest LoadFromFile(const FString& Filename); + + friend FArchive& operator<<(FArchive& Ar, FManifest& Manifest) + { + Ar << Manifest.IsGameTarget; + Ar << Manifest.RootLocalPath; + Ar << Manifest.RootBuildPath; + Ar << Manifest.TargetName; + Ar << Manifest.Modules; + + return Ar; + } }; diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/NativeClassExporter.h b/Engine/Source/Programs/UnrealHeaderTool/Private/NativeClassExporter.h index 6b9515726c83..2142c91963ed 100644 --- a/Engine/Source/Programs/UnrealHeaderTool/Private/NativeClassExporter.h +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/NativeClassExporter.h @@ -40,6 +40,9 @@ typedef FStringOutputDeviceCountLines FUHTStringBuilderLineCounter; struct FNativeClassHeaderGenerator { private: + /** Stack of currently parsed source files. */ + TArray CurrentSourceFile; + FString API; /** @@ -57,7 +60,7 @@ private: FString GeneratedProtoFilenameBase; /** the name of the java file where mcp definitions are generated */ FString GeneratedMCPFilenameBase; - UPackage* Package; + const UPackage* Package; FUHTStringBuilder PreHeaderText; FUHTStringBuilder EnumForeachText; TArray ListOfPublicHeaderGroupIncludes; // This is built up each time from scratch each time for each header groups @@ -120,6 +123,8 @@ private: /** All properties that need to be forward declared. */ TArray ForwardDeclarations; + /** Makefile to save parsing data to. */ + FUHTMakefile& UHTMakefile; // This exists because it makes debugging much easier on VC2010, since the visualizers can't properly understand templates with templated args struct HeaderDependents : TArray { @@ -201,7 +206,7 @@ private: /** * Export functions used to find and call C++ or script implementation of a script function in the interface */ - void ExportInterfaceCallFunctions(const TArray& CallbackFunctions, UClass* Class, FClassMetaData* ClassData, FUHTStringBuilder& HeaderOutput); + void ExportInterfaceCallFunctions(const TArray& CallbackFunctions, UClass* Class, FUHTStringBuilder& HeaderOutput); /** * Export UInterface boilerplate. @@ -268,10 +273,9 @@ private: /** * Exports the macro declarations for GENERATED_BODY() for each Foo in the list of structs specified * - * @param SourceFile the source file * @param Structs the structs to export */ - void ExportGeneratedStructBodyMacros(FUnrealSourceFile& SourceFile, const TArray& NativeStructs); + void ExportGeneratedStructBodyMacros(const TArray& NativeStructs); /** * Exports a local mirror of the specified struct; used to get offsets for noexport structs @@ -324,11 +328,10 @@ private: * Generate a .proto message declaration for any functions marked as requiring one * * @param InCallbackFunctions array of functions for consideration to generate .proto definitions - * @param ClassData class data * @param Indent starting indentation level * @param Output optional output redirect */ - void ExportProtoMessage(const TArray& InCallbackFunctions, FClassMetaData* ClassData, int32 Indent = 0, FUHTStringBuilder* Output = NULL); + void ExportProtoMessage(const TArray& InCallbackFunctions, int32 Indent = 0, FUHTStringBuilder* Output = NULL); /** * Generate a .java message declaration for any functions marked as requiring one @@ -443,7 +446,7 @@ private: * * @param Package Package to export code for. **/ - void ExportGeneratedPackageInitCode(UPackage* Package); + void ExportGeneratedPackageInitCode(const UPackage* Package); /** * Function to output the C++ code necessary to set up the given array of properties @@ -532,19 +535,20 @@ private: /** * Gets list of public headers for the given package in the form of string of includes. */ - FString GetListOfPublicHeaderGroupIncludesString(UPackage* Package); + FString GetListOfPublicHeaderGroupIncludesString(const UPackage* Package); public: // Constructor FNativeClassHeaderGenerator( - UPackage* InPackage, + const UPackage* InPackage, const TArray& SourceFiles, FClasses& AllClasses, bool InAllowSaveExportedHeaders #if WITH_HOT_RELOAD_CTORS , bool bExportVTableConstructors #endif // WITH_HOT_RELOAD_CTORS + , FUHTMakefile& UHTMakefile ); /** diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/ParserClass.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/ParserClass.cpp index 3f119710e42d..2cf5b0088cf4 100644 --- a/Engine/Source/Programs/UnrealHeaderTool/Private/ParserClass.cpp +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/ParserClass.cpp @@ -91,4 +91,25 @@ bool FClass::IsDynamic(UField* Field) { static const FName NAME_ReplaceConverted(TEXT("ReplaceConverted")); return Field->HasMetaData(NAME_ReplaceConverted); -} \ No newline at end of file +} + +FString FClass::GetTypePackageName(UField* Field) +{ + static const FName NAME_ReplaceConverted(TEXT("ReplaceConverted")); + FString PackageName = Field->GetMetaData(NAME_ReplaceConverted); + if (PackageName.Len()) + { + int32 ObjectDotIndex = INDEX_NONE; + // Strip the object name + if (PackageName.FindChar(TEXT('.'), ObjectDotIndex)) + { + PackageName = PackageName.Mid(0, ObjectDotIndex); + } + } + else + { + PackageName = Field->GetOutermost()->GetName(); + } + return PackageName; +} + diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/ParserClass.h b/Engine/Source/Programs/UnrealHeaderTool/Private/ParserClass.h index 31c71fd592d2..ba3da195553f 100644 --- a/Engine/Source/Programs/UnrealHeaderTool/Private/ParserClass.h +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/ParserClass.h @@ -47,4 +47,7 @@ public: /** Helper function that checks if the field is a dynamic type (can be constructed post-startup) */ static bool IsDynamic(UField* Field); + + /** Helper function to get the source replaced package name */ + static FString GetTypePackageName(UField* Field); }; diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/ParserHelper.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/ParserHelper.cpp index a172167c06bb..51a9c8afbfb1 100644 --- a/Engine/Source/Programs/UnrealHeaderTool/Private/ParserHelper.cpp +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/ParserHelper.cpp @@ -4,43 +4,11 @@ #include "UnrealHeaderTool.h" #include "ParserHelper.h" #include "DefaultValueHelper.h" +#include "UHTMakefile/UHTMakefile.h" ///////////////////////////////////////////////////// // FClassMetaData -/** - * Finds the metadata for the struct specified - * - * @param Struct the struct to search for - * - * @return pointer to the metadata for the struct specified, or NULL - * if the struct doesn't exist in the list (for example, if it - * is declared in a package that is already compiled and has had its - * source stripped) - */ -FStructData* FClassMetaData::FindStructData( UScriptStruct* Struct ) -{ - FStructData* Result = NULL; - - TScopedPointer* pStructData = StructData.Find(Struct); - if ( pStructData != NULL ) - { - Result = pStructData->GetOwnedPointer(); - } - - if ( Result == NULL ) - { - UClass* OwnerClass = Struct->GetOwnerClass(); - FClassMetaData* OwnerClassData = GScriptHelper.FindClassData(OwnerClass); - if ( OwnerClassData && OwnerClassData != this ) - { - Result = OwnerClassData->FindStructData(Struct); - } - } - - return Result; -} - /** * Finds the metadata for the property specified * @@ -98,20 +66,7 @@ FTokenData* FClassMetaData::FindTokenData( UProperty* Prop ) // struct property UScriptStruct* OuterStruct = Cast(Outer); check(OuterStruct != NULL); - - TScopedPointer* pStructInfo = StructData.Find(OuterStruct); - if ( pStructInfo != NULL ) - { - FStructData* StructInfo = pStructInfo->GetOwnedPointer(); - check(StructInfo); - - FPropertyData& StructProperties = StructInfo->GetStructPropertyData(); - Result = StructProperties.Find(Prop); - } - else - { - OuterClass = OuterStruct->GetOwnerClass(); - } + OuterClass = OuterStruct->GetOwnerClass(); } } @@ -127,6 +82,18 @@ FTokenData* FClassMetaData::FindTokenData( UProperty* Prop ) return Result; } +void FClassMetaData::AddInheritanceParent(const FString& InParent, FUHTMakefile& UHTMakefile, FUnrealSourceFile* UnrealSourceFile) +{ + MultipleInheritanceParents.Add(new FMultipleInheritanceBaseClass(InParent)); + UHTMakefile.AddMultipleInheritanceBaseClass(UnrealSourceFile, MultipleInheritanceParents.Last()); +} + +void FClassMetaData::AddInheritanceParent(UClass* ImplementedInterfaceClass, FUHTMakefile& UHTMakefile, FUnrealSourceFile* UnrealSourceFile) +{ + MultipleInheritanceParents.Add(new FMultipleInheritanceBaseClass(ImplementedInterfaceClass)); + UHTMakefile.AddMultipleInheritanceBaseClass(UnrealSourceFile, MultipleInheritanceParents.Last()); +} + ///////////////////////////////////////////////////// // FPropertyBase @@ -287,3 +254,68 @@ bool FFunctionData::TryFindForFunction(UFunction* Function, FFunctionData*& OutD OutData = &(*Output).Get(); return true; } + +FClassMetaData* FCompilerMetadataManager::AddClassData(UStruct* Struct, FUHTMakefile& UHTMakefile, FUnrealSourceFile* UnrealSourceFile) +{ + TScopedPointer* pClassData = Find(Struct); + if (pClassData == NULL) + { + pClassData = &Emplace(Struct, new FClassMetaData()); + if (UnrealSourceFile) + { + UHTMakefile.AddClassMetaData(UnrealSourceFile, *pClassData); + } + } + + return *pClassData; +} + +FTokenData* FPropertyData::Set(UProperty* InKey, const FTokenData& InValue, FUHTMakefile& UHTMakefile, FUnrealSourceFile* UnrealSourceFile) +{ + FTokenData* Result = NULL; + + TSharedPtr* pResult = Super::Find(InKey); + if (pResult != NULL) + { + Result = pResult->Get(); + *Result = FTokenData(InValue); + } + else + { + pResult = &Super::Emplace(InKey, new FTokenData(InValue)); + Result = pResult->Get(); + UHTMakefile.AddPropertyDataEntry(UnrealSourceFile, *pResult, InKey); + } + + return Result; +} + +const TCHAR* FNameLookupCPP::GetNameCPP(UStruct* Struct, bool bForceInterface /*= false */) +{ + TCHAR* NameCPP = StructNameMap.FindRef(Struct); + if (NameCPP && !bForceInterface) + { + return NameCPP; + } + + FString DesiredStructName = Struct->GetName(); + FString TempName = FString(bForceInterface ? TEXT("I") : Struct->GetPrefixCPP()) + DesiredStructName; + int32 StringLength = TempName.Len(); + + NameCPP = new TCHAR[StringLength + 1]; + FCString::Strcpy(NameCPP, StringLength + 1, *TempName); + NameCPP[StringLength] = 0; + + if (bForceInterface) + { + InterfaceAllocations.Add(NameCPP); + UHTMakefile->AddInterfaceAllocation(UnrealSourceFile, NameCPP); + } + else + { + StructNameMap.Add(Struct, NameCPP); + UHTMakefile->AddStructNameMapEntry(UnrealSourceFile, Struct, NameCPP); + } + + return NameCPP; +} diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/ParserHelper.h b/Engine/Source/Programs/UnrealHeaderTool/Private/ParserHelper.h index 9a8b8c7d1ea2..259e4e3c5eb5 100644 --- a/Engine/Source/Programs/UnrealHeaderTool/Private/ParserHelper.h +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/ParserHelper.h @@ -66,7 +66,7 @@ struct ERefQualifier /** * Basic information describing a type. */ -class FPropertyBase +class FPropertyBase : public TSharedFromThis { public: // Variables. @@ -593,6 +593,8 @@ public: //@} static const TCHAR* GetPropertyTypeText( EPropertyType Type ); + + friend struct FPropertyBaseArchiveProxy; }; // @@ -848,6 +850,8 @@ public: ImpliedPropertyFlags ); } + + friend struct FTokenArchiveProxy; }; /** @@ -1036,9 +1040,9 @@ struct FTokenData * Class for storing data about a list of properties. Though FToken contains a reference to its * associated UProperty, it's faster lookup to use the UProperty as the key in a TMap. */ -class FPropertyData : public TMap< UProperty*, TScopedPointer > +class FPropertyData : public TMap< UProperty*, TSharedPtr > { - typedef TMap > Super; + typedef TMap > Super; public: /** @@ -1051,10 +1055,10 @@ public: { FTokenData* Result = NULL; - TScopedPointer* pResult = Super::Find(Key); + TSharedPtr* pResult = Super::Find(Key); if ( pResult != NULL ) { - Result = pResult->GetOwnedPointer(); + Result = pResult->Get(); } return Result; } @@ -1062,10 +1066,10 @@ public: { const FTokenData* Result = NULL; - const TScopedPointer* pResult = Super::Find(Key); + const TSharedPtr* pResult = Super::Find(Key); if ( pResult != NULL ) { - Result = pResult->GetOwnedPointer(); + Result = pResult->Get(); } return Result; } @@ -1079,24 +1083,7 @@ public: * * @return a pointer to token data created associated with the property */ - FTokenData* Set(UProperty* InKey, const FTokenData& InValue) - { - FTokenData* Result = NULL; - - TScopedPointer* pResult = Super::Find(InKey); - if ( pResult != NULL ) - { - Result = pResult->GetOwnedPointer(); - *Result = FTokenData(InValue); - } - else - { - pResult = &Super::Emplace(InKey, new FTokenData(InValue)); - Result = *pResult; - } - - return Result; - } + FTokenData* Set(UProperty* InKey, const FTokenData& InValue, FUHTMakefile& UHTMakefile, FUnrealSourceFile* UnrealSourceFile); /** * (debug) Dumps the values of this FPropertyData to the log file @@ -1105,10 +1092,10 @@ public: */ void Dump( int32 Indent ) { - for ( TMap >::TIterator It(*this); It; ++It ) + for (auto& Kvp : *this) { - TScopedPointer& PointerVal = It.Value(); - FToken& Token = PointerVal.GetOwnedPointer()->Token; + TSharedPtr& PointerVal = Kvp.Value; + FToken& Token = PointerVal->Token; if ( Token.Type != CPT_None ) { UE_LOG(LogCompile, Log, TEXT("%s%s"), FCString::Spc(Indent), *Token.Describe()); @@ -1136,10 +1123,10 @@ public: * * @param PropertyToken token that should be added to the list */ - void AddStructProperty( const FToken& PropertyToken ) + void AddStructProperty(const FTokenData& PropertyToken, FUHTMakefile& UHTMakefile, FUnrealSourceFile* UnrealSourceFile) { - check(PropertyToken.TokenProperty); - StructPropertyData.Set(PropertyToken.TokenProperty, PropertyToken); + check(PropertyToken.Token.TokenProperty); + StructPropertyData.Set(PropertyToken.Token.TokenProperty, PropertyToken, UHTMakefile, UnrealSourceFile); } FPropertyData& GetStructPropertyData() @@ -1166,6 +1153,8 @@ public: /** Constructor */ FStructData( const FToken& StructToken ) : StructData(StructToken) {} + + friend struct FStructDataArchiveProxy; }; /** @@ -1187,10 +1176,10 @@ class FFunctionData * * @param PropertyToken token that should be added to the list */ - void AddParameter( const FToken& PropertyToken ) + void AddParameter(const FToken& PropertyToken, FUHTMakefile& UHTMakefile, FUnrealSourceFile* UnrealSourceFile) { check(PropertyToken.TokenProperty); - ParameterData.Set(PropertyToken.TokenProperty, PropertyToken); + ParameterData.Set(PropertyToken.TokenProperty, PropertyToken, UHTMakefile, UnrealSourceFile); } /** @@ -1247,7 +1236,7 @@ public: * * @param PropertyToken the property to add */ - void AddProperty( const FToken& PropertyToken ) + void AddProperty(const FToken& PropertyToken, FUHTMakefile& UHTMakefile, FUnrealSourceFile* UnrealSourceFile) { const UProperty* Prop = PropertyToken.TokenProperty; check(Prop); @@ -1259,7 +1248,7 @@ public: } else { - AddParameter(PropertyToken); + AddParameter(PropertyToken, UHTMakefile, UnrealSourceFile); } } @@ -1358,11 +1347,8 @@ class FClassMetaData /** member properties for this class */ FPropertyData GlobalPropertyData; - /** structs declared in this class */ - TMap< UScriptStruct*, TScopedPointer > StructData; - /** base classes to multiply inherit from (other than the main base class */ - TArray MultipleInheritanceParents; + TArray MultipleInheritanceParents; /** whether this class declares delegate functions or properties */ bool bContainsDelegates; @@ -1455,41 +1441,13 @@ public: bContainsDelegates = true; } - /** - * Adds a new struct to be tracked - * - * @param StructToken the token for the struct to add - * - * @return a pointer to the newly added FStructData - */ - FStructData* AddStruct( const FToken& StructToken ) - { - check(StructToken.Struct != NULL); - - FStructData* Result = NULL; - - TScopedPointer* pStructData = StructData.Find(StructToken.Struct); - if ( pStructData != NULL ) - { - Result = pStructData->GetOwnedPointer(); - *Result = FStructData(StructToken); - } - else - { - pStructData = &StructData.Emplace(StructToken.Struct, new FStructData(StructToken)); - Result = pStructData->GetOwnedPointer(); - } - - return Result; - } - /** * Adds a new property to be tracked. Determines the correct list for the property based on * its owner (function, struct, etc). * * @param PropertyToken the property to add */ - void AddProperty( const FToken& PropertyToken ) + void AddProperty(const FToken& PropertyToken, FUHTMakefile& UHTMakefile, FUnrealSourceFile* UnrealSourceFile) { UProperty* Prop = PropertyToken.TokenProperty; check(Prop); @@ -1499,7 +1457,7 @@ public: if ( OuterClass != NULL ) { // global property - GlobalPropertyData.Set(Prop,PropertyToken); + GlobalPropertyData.Set(Prop, PropertyToken, UHTMakefile, UnrealSourceFile); } else { @@ -1508,19 +1466,7 @@ public: if ( OuterFunction != NULL ) { // function parameter, return, or local property - FFunctionData::FindForFunction(OuterFunction)->AddProperty(PropertyToken); - } - else - { - // struct property - UScriptStruct* OuterStruct = Cast(Outer); - if (OuterStruct != NULL) - { - TScopedPointer* StructInfo = StructData.Find(OuterStruct); - check(StructInfo!=NULL); - - (*StructInfo)->AddStructProperty(PropertyToken); - } + FFunctionData::FindForFunction(OuterFunction)->AddProperty(PropertyToken, UHTMakefile, UnrealSourceFile); } } @@ -1580,18 +1526,6 @@ public: */ FFunctionData* FindFunctionData( UFunction* Func ); - /** - * Finds the metadata for the struct specified - * - * @param Struct the struct to search for - * - * @return pointer to the metadata for the struct specified, or NULL - * if the struct doesn't exist in the list (for example, if it - * is declared in a package that is already compiled and has had its - * source stripped) - */ - FStructData* FindStructData( UScriptStruct* Struct ); - /** * Finds the metadata for the property specified * @@ -1614,27 +1548,25 @@ public: /** * Add a string to the list of inheritance parents for this class. * - * @param Inparent The C++ class name to add to the multiple inheritance list + * @param Inparent The C++ class name to add to the multiple inheritance list + * @param UHTMakefile Makefile to save parsing data to. + * @param UnrealSourceFile Currently parsed source file. */ - void AddInheritanceParent(const FString& InParent) - { - new(MultipleInheritanceParents) FMultipleInheritanceBaseClass(InParent); - } + void AddInheritanceParent(const FString& InParent, FUHTMakefile& UHTMakefile, FUnrealSourceFile* UnrealSourceFile); /** * Add a string to the list of inheritance parents for this class. * * @param Inparent The C++ class name to add to the multiple inheritance list + * @param UHTMakefile Makefile to save parsing data to. + * @param UnrealSourceFile Currently parsed source file. */ - void AddInheritanceParent(UClass* ImplementedInterfaceClass) - { - new(MultipleInheritanceParents) FMultipleInheritanceBaseClass(ImplementedInterfaceClass); - } + void AddInheritanceParent(UClass* ImplementedInterfaceClass, FUHTMakefile& UHTMakefile, FUnrealSourceFile* UnrealSourceFile); /** * Return the list of inheritance parents */ - const TArray& GetInheritanceParents() const + const TArray& GetInheritanceParents() const { return MultipleInheritanceParents; } @@ -1653,7 +1585,6 @@ public: void Shrink() { GlobalPropertyData.Shrink(); - StructData.Shrink(); MultipleInheritanceParents.Shrink(); } @@ -1673,6 +1604,8 @@ public: // GENERATED_BODY access specifier to preserve. EAccessSpecifier GeneratedBodyMacroAccessSpecifier; + + friend struct FClassMetaDataArchiveProxy; }; /** @@ -1690,16 +1623,7 @@ public: * * @return a pointer to the newly added metadata for the class specified */ - FClassMetaData* AddClassData(UStruct* Struct) - { - TScopedPointer* pClassData = Find(Struct); - if (pClassData == NULL) - { - pClassData = &Emplace(Struct, new FClassMetaData()); - } - - return *pClassData; - } + FClassMetaData* AddClassData(UStruct* Struct, FUHTMakefile& UHTMakefile, FUnrealSourceFile* UnrealSourceFile); /** * Find the metadata associated with the class specified @@ -1733,6 +1657,8 @@ public: MetaData->Shrink(); } } + + friend struct FCompilerMetadataManagerArchiveProxy; }; /*----------------------------------------------------------------------------- @@ -1798,38 +1724,25 @@ struct FNameLookupCPP * @param Struct UStruct to obtain C++ name for * @return Name used for C++ declaration */ - const TCHAR* GetNameCPP( UStruct* Struct, bool bForceInterface = false ) - { - TCHAR* NameCPP = StructNameMap.FindRef( Struct ); - if (NameCPP && !bForceInterface) - { - return NameCPP; - } + const TCHAR* GetNameCPP( UStruct* Struct, bool bForceInterface = false ); - FString DesiredStructName = Struct->GetName(); - FString TempName = FString(bForceInterface ? TEXT("I") : Struct->GetPrefixCPP()) + DesiredStructName; - int32 StringLength = TempName.Len(); - - NameCPP = new TCHAR[StringLength + 1]; - FCString::Strcpy( NameCPP, StringLength + 1, *TempName ); - NameCPP[StringLength] = 0; - - if (bForceInterface) + void SetUHTMakefile(FUHTMakefile* InUHTMakefile) { - InterfaceAllocations.Add(NameCPP); + UHTMakefile = InUHTMakefile; } - else + void SetCurrentSourceFile(FUnrealSourceFile* InUnrealSourceFile) { - StructNameMap.Add( Struct, NameCPP ); - } - - return NameCPP; + UnrealSourceFile = InUnrealSourceFile; } - private: /** Map of UStruct pointers to C++ names */ TMap StructNameMap; TArray InterfaceAllocations; + + friend struct FNameLookupCPPArchiveProxy; + friend class FUHTMakefile; + FUHTMakefile* UHTMakefile; + FUnrealSourceFile* UnrealSourceFile; }; extern FNameLookupCPP NameLookupCPP; diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/Scope.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/Scope.cpp index c84f57cfde6c..ac638d701a14 100644 --- a/Engine/Source/Programs/UnrealHeaderTool/Private/Scope.cpp +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/Scope.cpp @@ -3,14 +3,13 @@ #include "UnrealHeaderTool.h" #include "ParserHelper.h" #include "Scope.h" +#include "UHTMakefile/UHTMakefile.h" extern FCompilerMetadataManager GScriptHelper; FScope::FScope(FScope* InParent) : Parent(InParent) -{ - check(Parent); -} +{ } FScope::FScope() : Parent(nullptr) @@ -85,11 +84,13 @@ TSharedRef FScope::GetTypeScope(UStruct* Type) return *ScopeRefPtr; } -TSharedRef FScope::AddTypeScope(UStruct* Type, FScope* ParentScope) +TSharedRef FScope::AddTypeScope(UStruct* Type, FScope* ParentScope, FUnrealSourceFile* UnrealSourceFile, FUHTMakefile& UHTMakefile) { - TSharedRef Scope = MakeShareable(new FStructScope(Type, ParentScope)); + FStructScope* ScopePtr = new FStructScope(Type, ParentScope); + TSharedRef Scope = MakeShareable(ScopePtr); ScopeMap.Add(Type, Scope); + UHTMakefile.AddStructScope(UnrealSourceFile, ScopePtr); return Scope; } @@ -141,13 +142,22 @@ bool FScope::ContainsTypes() const return TypeMap.Num() > 0; } +FFileScope* FScope::GetFileScope() +{ + FScope* CurrentScope = this; + while (!CurrentScope->IsFileScope()) + { + CurrentScope = const_cast(CurrentScope->GetParent()); + } + + return CurrentScope->AsFileScope(); +} + TMap > FScope::ScopeMap; FFileScope::FFileScope(FName InName, FUnrealSourceFile* InSourceFile) : SourceFile(InSourceFile), Name(InName) -{ - -} +{ } void FFileScope::IncludeScope(FFileScope* IncludedScope) { @@ -179,8 +189,3 @@ FStructScope::FStructScope(UStruct* InStruct, FScope* InParent) { } - -FClassMetaData* FStructScope::GetClassMetaData() const -{ - return GScriptHelper.FindClassData(Struct); -} diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/Scope.h b/Engine/Source/Programs/UnrealHeaderTool/Private/Scope.h index eddc5caebf53..43346445275e 100644 --- a/Engine/Source/Programs/UnrealHeaderTool/Private/Scope.h +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/Scope.h @@ -8,6 +8,9 @@ class UStruct; class FClassMetaData; class FUnrealSourceFile; +class FUHTMakefile; +class FFileScope; +class FStructScope; // Traits to achieve conditional types for const/non-const iterators. template @@ -25,7 +28,7 @@ public: }; // Base class representing type scope. -class FScope +class FScope : public TSharedFromThis { public: // Default constructor i.e. Parent == nullptr @@ -38,6 +41,9 @@ public: virtual ~FScope() { }; + virtual FFileScope* AsFileScope() { return nullptr; } + virtual FStructScope* AsStructScope() { return nullptr; } + /** * Adds type to the scope. * @@ -108,7 +114,7 @@ public: * * @returns Newly added scope. */ - static TSharedRef AddTypeScope(UStruct* Type, FScope* ParentScope); + static TSharedRef AddTypeScope(UStruct* Type, FScope* ParentScope, FUnrealSourceFile* UnrealSourceFile, FUHTMakefile& UHTMakefile); /** * Gets structs, enums and delegate functions from this scope. @@ -238,6 +244,7 @@ public: */ bool ContainsTypes() const; + FFileScope* GetFileScope(); private: // This scopes parent. const FScope* Parent; @@ -247,6 +254,10 @@ private: // Global map type <-> scope. static TMap > ScopeMap; + + friend struct FScopeArchiveProxy; + friend struct FStructScopeArchiveProxy; + friend class FUHTMakefile; }; /** @@ -255,6 +266,10 @@ private: class FFileScope : public FScope { public: + FFileScope() + : Name(NAME_None) + , SourceFile(nullptr) + { } // Constructor. FFileScope(FName Name, FUnrealSourceFile* SourceFile); @@ -265,6 +280,8 @@ public: */ void IncludeScope(FFileScope* IncludedScope); + virtual FFileScope* AsFileScope() override { return this; } + /** * Gets scope name. */ @@ -297,6 +314,16 @@ public: } } + + const TArray& GetIncludedScopes() const + { + return IncludedScopes; + } + + void SetSourceFile(FUnrealSourceFile* InSourceFile) + { + SourceFile = InSourceFile; + } private: // Source file. FUnrealSourceFile* SourceFile; @@ -306,6 +333,8 @@ private: // Included scopes list. TArray IncludedScopes; + + friend struct FFileScopeArchiveProxy; }; @@ -318,16 +347,12 @@ public: // Constructor. FStructScope(UStruct* Struct, FScope* Parent); + virtual FStructScope* AsStructScope() override { return this; } /** * Gets struct associated with this scope. */ UStruct* GetStruct() const; - /** - * Gets metadata associated with class of this scope. - */ - FClassMetaData* GetClassMetaData() const; - /** * Gets scope name. */ @@ -336,6 +361,8 @@ public: private: // Struct associated with this scope. UStruct* Struct; + + friend struct FStructScopeArchiveProxy; }; /** diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/SimplifiedParsingClassInfo.h b/Engine/Source/Programs/UnrealHeaderTool/Private/SimplifiedParsingClassInfo.h index 106ad52c34ba..ada813aeb277 100644 --- a/Engine/Source/Programs/UnrealHeaderTool/Private/SimplifiedParsingClassInfo.h +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/SimplifiedParsingClassInfo.h @@ -18,6 +18,8 @@ public: , bClassIsAnInterface(bInClassIsAnInterface) {} + FSimplifiedParsingClassInfo() { } + /** * Gets class name. */ @@ -50,6 +52,16 @@ public: return bClassIsAnInterface; } + friend FArchive& operator<<(FArchive& Ar, FSimplifiedParsingClassInfo& SimplifiedParsingClassInfo) + { + Ar << SimplifiedParsingClassInfo.ClassName; + Ar << SimplifiedParsingClassInfo.BaseClassName; + Ar << SimplifiedParsingClassInfo.ClassDefLine; + Ar << SimplifiedParsingClassInfo.bClassIsAnInterface; + + return Ar; + } + private: // Name. FString ClassName; diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/ClassArchiveProxy.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/ClassArchiveProxy.cpp new file mode 100644 index 000000000000..7daff691257b --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/ClassArchiveProxy.cpp @@ -0,0 +1,119 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#pragma once +#include "UnrealHeaderTool.h" +#include "UHTMakefile/UHTMakefile.h" +#include "UHTMakefile/ClassArchiveProxy.h" + +FClassArchiveProxy::FClassArchiveProxy(FUHTMakefile& UHTMakefile, const UClass* Class) + : FStructArchiveProxy(UHTMakefile, Class) +{ + ClassUnique = Class->ClassUnique; + ClassFlags = Class->ClassFlags; + ClassCastFlags = Class->ClassCastFlags; + ClassWithinIndex = UHTMakefile.GetClassIndex(Class->ClassWithin); + ClassGeneratedByIndex = UHTMakefile.GetObjectIndex(Class->ClassGeneratedBy); + ClassConfigName = FNameArchiveProxy(UHTMakefile, Class->ClassConfigName); + bCooked = Class->bCooked; + ClassReps.Empty(Class->ClassReps.Num()); + for (FRepRecord RepRecord : Class->ClassReps) + { + ClassReps.Add(FRepRecordArchiveProxy(UHTMakefile, RepRecord)); + } + + NetFields.Empty(Class->NetFields.Num()); + for (UField* Field : Class->NetFields) + { + NetFields.Add(UHTMakefile.GetFieldIndex(Field)); + } + + FuncMap.Empty(Class->FuncMap.Num()); + for (auto& Kvp : Class->FuncMap) + { + FuncMap.Add(TPairInitializer(FNameArchiveProxy(UHTMakefile, Kvp.Key), UHTMakefile.GetFunctionIndex(Kvp.Value))); + } + + Interfaces.Empty(Class->Interfaces.Num()); + for (FImplementedInterface ImplementedInterface : Class->Interfaces) + { + Interfaces.Add(FImplementedInterfaceArchiveProxy(UHTMakefile, ImplementedInterface)); + } +} + +void FClassArchiveProxy::Resolve(UClass* Class, const FUHTMakefile& UHTMakefile) const +{ + FStructArchiveProxy::Resolve(Class, UHTMakefile); + Class->SetSuperStruct(UHTMakefile.GetStructByIndex(SuperStructIndex)); + Class->ClassWithin = UHTMakefile.GetClassByIndex(ClassWithinIndex); + Class->ClassGeneratedBy = UHTMakefile.GetObjectByIndex(ClassGeneratedByIndex); + + Class->NetFields.Empty(NetFields.Num()); + for (FSerializeIndex FieldIndex : NetFields) + { + Class->NetFields.Add(UHTMakefile.GetFieldByIndex(FieldIndex)); + } + + Class->FuncMap.Empty(FuncMap.Num()); + for (auto& Kvp : FuncMap) + { + Class->FuncMap.Add(Kvp.Key.CreateName(UHTMakefile), UHTMakefile.GetFunctionByIndex(Kvp.Value)); + } + + Class->Interfaces.Empty(Interfaces.Num()); + for (FImplementedInterfaceArchiveProxy ImplementedInterfaceArchiveProxy : Interfaces) + { + FImplementedInterface ImplementedInterface = ImplementedInterfaceArchiveProxy.CreateImplementedInterface(UHTMakefile); + ImplementedInterfaceArchiveProxy.Resolve(ImplementedInterface, UHTMakefile); + Class->Interfaces.Add(ImplementedInterface); + } + Class->ClassConfigName = ClassConfigName.CreateName(UHTMakefile); +} + +void FClassArchiveProxy::AddReferencedNames(const UClass* Class, FUHTMakefile& UHTMakefile) +{ + FStructArchiveProxy::AddReferencedNames(Class, UHTMakefile); + UHTMakefile.AddName(Class->ClassConfigName); + for (auto& Kvp : Class->FuncMap) + { + UHTMakefile.AddName(Kvp.Key); + } +} + +FArchive& operator<<(FArchive& Ar, FClassArchiveProxy& ClassArchiveProxy) +{ + Ar << static_cast(ClassArchiveProxy); + Ar << ClassArchiveProxy.ClassUnique; + Ar << ClassArchiveProxy.ClassFlags; + Ar << ClassArchiveProxy.ClassCastFlags; + Ar << ClassArchiveProxy.ClassWithinIndex; + Ar << ClassArchiveProxy.ClassGeneratedByIndex; + Ar << ClassArchiveProxy.ClassConfigName; + Ar << ClassArchiveProxy.bCooked; + Ar << ClassArchiveProxy.ClassReps; + Ar << ClassArchiveProxy.NetFields; + Ar << ClassArchiveProxy.FuncMap; + Ar << ClassArchiveProxy.Interfaces; + Ar << ClassArchiveProxy.ConvertedSubobjectsFromBPGC; + + return Ar; +} + +UClass* FClassArchiveProxy::CreateClass(const FUHTMakefile& UHTMakefile) const +{ + UObject* Outer = UHTMakefile.GetObjectByIndex(OuterIndex); + FName ClassName = Name.CreateName(UHTMakefile); + UClass* Class = FindObject(Outer, *ClassName.ToString()); + if (Class == nullptr || !Class->HasAnyClassFlags(CLASS_Native)) + { + Class = new(EC_InternalUseOnlyConstructor, Outer, ClassName, (EObjectFlags)ObjectFlagsUint32) UClass(FObjectInitializer(), nullptr); + } + PostConstruct(Class); + + return Class; +} + +void FClassArchiveProxy::PostConstruct(UClass* Class) const +{ + Class->ClassFlags |= ClassFlags; + Class->ClassCastFlags |= ClassCastFlags; +} diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/ClassArchiveProxy.h b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/ClassArchiveProxy.h new file mode 100644 index 000000000000..eb338873f013 --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/ClassArchiveProxy.h @@ -0,0 +1,34 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#pragma once +#include "UHTMakefile/StructArchiveProxy.h" +#include "UHTMakefile/RepRecordArchiveProxy.h" +#include "UHTMakefile/ImplementedInterfaceArchiveProxy.h" + +/* See UHTMakefile.h for overview how makefiles work. */ +struct FClassArchiveProxy : public FStructArchiveProxy +{ + FClassArchiveProxy() { } + FClassArchiveProxy(FUHTMakefile& UHTMakefile, const UClass* Class); + + UClass* CreateClass(const FUHTMakefile& UHTMakefile) const; + void PostConstruct(UClass* Class) const; + void Resolve(UClass* Class, const FUHTMakefile& UHTMakefile) const; + + friend FArchive& operator<<(FArchive& Ar, FClassArchiveProxy& ClassArchiveProxy); + + static void AddReferencedNames(const UClass* Class, FUHTMakefile& UHTMakefile); + + int32 ClassUnique; + uint32 ClassFlags; + EClassCastFlags ClassCastFlags; + FSerializeIndex ClassWithinIndex; + FSerializeIndex ClassGeneratedByIndex; + FNameArchiveProxy ClassConfigName; + bool bCooked; + TArray ClassReps; + TArray NetFields; + TArray> FuncMap; + TArray Interfaces; + TArray ConvertedSubobjectsFromBPGC; +}; diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/ClassMetaDataArchiveProxy.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/ClassMetaDataArchiveProxy.cpp new file mode 100644 index 000000000000..2c669f4fc369 --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/ClassMetaDataArchiveProxy.cpp @@ -0,0 +1,83 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. +#include "UnrealHeaderTool.h" +#include "UHTMakefile/UHTMakefile.h" +#include "UHTMakefile/ClassMetadataArchiveProxy.h" + +FClassMetaDataArchiveProxy::FClassMetaDataArchiveProxy(const FUHTMakefile& UHTMakefile, const FClassMetaData* ClassMetaData) +{ + GlobalPropertyData = FPropertyDataArchiveProxy(UHTMakefile, &ClassMetaData->GlobalPropertyData); + + for (FMultipleInheritanceBaseClass* MultipleInheritanceBaseClass : ClassMetaData->MultipleInheritanceParents) + { + MultipleInheritanceParents.Add(FMultipleInheritanceBaseClassArchiveProxy(UHTMakefile, MultipleInheritanceBaseClass)); + } + + bContainsDelegates = ClassMetaData->bContainsDelegates; + PrologLine = ClassMetaData->PrologLine; + GeneratedBodyLine = ClassMetaData->GeneratedBodyLine; + InterfaceGeneratedBodyLine = ClassMetaData->InterfaceGeneratedBodyLine; + bConstructorDeclared = ClassMetaData->bConstructorDeclared; + bDefaultConstructorDeclared = ClassMetaData->bDefaultConstructorDeclared; + bObjectInitializerConstructorDeclared = ClassMetaData->bObjectInitializerConstructorDeclared; + bCustomVTableHelperConstructorDeclared = ClassMetaData->bCustomVTableHelperConstructorDeclared; + GeneratedBodyMacroAccessSpecifier = ClassMetaData->GeneratedBodyMacroAccessSpecifier; +} + +void FClassMetaDataArchiveProxy::AddReferencedNames(const FClassMetaData* ClassMetaData, FUHTMakefile& UHTMakefile) +{ + FPropertyDataArchiveProxy::AddReferencedNames(&ClassMetaData->GlobalPropertyData, UHTMakefile); + + for (FMultipleInheritanceBaseClass* MultipleInheritanceBaseClass : ClassMetaData->MultipleInheritanceParents) + { + FMultipleInheritanceBaseClassArchiveProxy::AddReferencedNames(MultipleInheritanceBaseClass, UHTMakefile); + } +} + +TScopedPointer FClassMetaDataArchiveProxy::CreateClassMetaData() const +{ + FClassMetaData* ClassMetaData = new FClassMetaData(); + PostConstruct(ClassMetaData); + return TScopedPointer(ClassMetaData); +} + +void FClassMetaDataArchiveProxy::PostConstruct(FClassMetaData* ClassMetaData) const +{ + ClassMetaData->bContainsDelegates = bContainsDelegates; + ClassMetaData->PrologLine = PrologLine; + ClassMetaData->GeneratedBodyLine = GeneratedBodyLine; + ClassMetaData->InterfaceGeneratedBodyLine = InterfaceGeneratedBodyLine; + ClassMetaData->bConstructorDeclared = bConstructorDeclared; + ClassMetaData->bDefaultConstructorDeclared = bDefaultConstructorDeclared; + ClassMetaData->bObjectInitializerConstructorDeclared = bObjectInitializerConstructorDeclared; + ClassMetaData->bCustomVTableHelperConstructorDeclared = bCustomVTableHelperConstructorDeclared; + ClassMetaData->GeneratedBodyMacroAccessSpecifier = GeneratedBodyMacroAccessSpecifier; +} + +void FClassMetaDataArchiveProxy::Resolve(FClassMetaData* ClassMetaData, FUHTMakefile& UHTMakefile) +{ + GlobalPropertyData.Resolve(&ClassMetaData->GlobalPropertyData, UHTMakefile); + + for (const FMultipleInheritanceBaseClassArchiveProxy& MultipleInheritanceBaseClassArchiveProxy : MultipleInheritanceParents) + { + FMultipleInheritanceBaseClass* MultipleInheritanceBaseClass = MultipleInheritanceBaseClassArchiveProxy.CreateMultipleInheritanceBaseClass(); + MultipleInheritanceBaseClassArchiveProxy.Resolve(MultipleInheritanceBaseClass, UHTMakefile); + ClassMetaData->MultipleInheritanceParents.Add(MultipleInheritanceBaseClass); + } +} + +FArchive& operator<<(FArchive& Ar, FClassMetaDataArchiveProxy& ClassMetaDataArchiveProxy) +{ + Ar << ClassMetaDataArchiveProxy.GlobalPropertyData; + Ar << ClassMetaDataArchiveProxy.MultipleInheritanceParents; + Ar << ClassMetaDataArchiveProxy.bContainsDelegates; + Ar << ClassMetaDataArchiveProxy.PrologLine; + Ar << ClassMetaDataArchiveProxy.GeneratedBodyLine; + Ar << ClassMetaDataArchiveProxy.InterfaceGeneratedBodyLine; + Ar << ClassMetaDataArchiveProxy.bConstructorDeclared; + Ar << ClassMetaDataArchiveProxy.bDefaultConstructorDeclared; + Ar << ClassMetaDataArchiveProxy.bObjectInitializerConstructorDeclared; + Ar << ClassMetaDataArchiveProxy.bCustomVTableHelperConstructorDeclared; + Ar << ClassMetaDataArchiveProxy.GeneratedBodyMacroAccessSpecifier; + + return Ar; +} diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/ClassMetaDataArchiveProxy.h b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/ClassMetaDataArchiveProxy.h new file mode 100644 index 000000000000..576a33d01aae --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/ClassMetaDataArchiveProxy.h @@ -0,0 +1,35 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#pragma once +#include "UHTMakefile/MultipleInheritanceBaseClassArchiveProxy.h" +#include "UHTMakefile/PropertyDataArchiveProxy.h" +#include "UHTMakefile/StructDataArchiveProxy.h" + +class FUHTMakefile; +class FClassMetaData; + +/* See UHTMakefile.h for overview how makefiles work. */ +struct FClassMetaDataArchiveProxy +{ + FClassMetaDataArchiveProxy(const FUHTMakefile& UHTMakefile, const FClassMetaData* ClassMetaData); + FClassMetaDataArchiveProxy() { } + + static void AddReferencedNames(const FClassMetaData* FClassMetaData, FUHTMakefile& UHTMakefile); + + friend FArchive& operator<<(FArchive& Ar, FClassMetaDataArchiveProxy& ClassMetaDataArchiveProxy); + TScopedPointer CreateClassMetaData() const; + void PostConstruct(FClassMetaData* ClassMetaData) const; + void Resolve(FClassMetaData* ClassMetaData, FUHTMakefile& UHTMakefile); + + FPropertyDataArchiveProxy GlobalPropertyData; + TArray MultipleInheritanceParents; + bool bContainsDelegates; + int32 PrologLine; + int32 GeneratedBodyLine; + int32 InterfaceGeneratedBodyLine; + bool bConstructorDeclared; + bool bDefaultConstructorDeclared; + bool bObjectInitializerConstructorDeclared; + bool bCustomVTableHelperConstructorDeclared; + EAccessSpecifier GeneratedBodyMacroAccessSpecifier; +}; diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/CompilerMetadataManagerArchiveProxy.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/CompilerMetadataManagerArchiveProxy.cpp new file mode 100644 index 000000000000..b45a8922d075 --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/CompilerMetadataManagerArchiveProxy.cpp @@ -0,0 +1,36 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. +#include "UnrealHeaderTool.h" +#include "UHTMakefile/UHTMakefile.h" +#include "UHTMakefile/CompilerMetadataManagerArchiveProxy.h" + +FCompilerMetadataManagerArchiveProxy::FCompilerMetadataManagerArchiveProxy(const FUHTMakefile& UHTMakefile, TArray>& CompilerMetadataManager) +{ + for (auto& Kvp : CompilerMetadataManager) + { + Array.Add(TPairInitializer(UHTMakefile.GetStructIndex(Kvp.Key), FClassMetaDataArchiveProxy(UHTMakefile, Kvp.Value))); + } +} + +void FCompilerMetadataManagerArchiveProxy::AddReferencedNames(const FCompilerMetadataManager* CompilerMetadataManager, FUHTMakefile& UHTMakefile) +{ + for (auto& Kvp : *CompilerMetadataManager) + { + FClassMetaDataArchiveProxy::AddReferencedNames(Kvp.Value, UHTMakefile); + } +} + +void FCompilerMetadataManagerArchiveProxy::Resolve(int32 Index, FCompilerMetadataManager& CompilerMetadataManager, FUHTMakefile& UHTMakefile) +{ + auto& Kvp = Array[Index]; + UStruct* Struct = UHTMakefile.GetStructByIndex(Kvp.Key); + FClassMetaData* ClassMetaData = GScriptHelper.AddClassData(Struct, UHTMakefile, nullptr); + Kvp.Value.PostConstruct(ClassMetaData); + Kvp.Value.Resolve(ClassMetaData, UHTMakefile); + UHTMakefile.CreateGScriptHelperEntry(Struct, ClassMetaData); +} + +FArchive& operator<<(FArchive& Ar, FCompilerMetadataManagerArchiveProxy& CompilerMetadataManagerArchiveProxy) +{ + Ar << CompilerMetadataManagerArchiveProxy.Array; + return Ar; +} diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/CompilerMetadataManagerArchiveProxy.h b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/CompilerMetadataManagerArchiveProxy.h new file mode 100644 index 000000000000..32bcfe11fd42 --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/CompilerMetadataManagerArchiveProxy.h @@ -0,0 +1,21 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#pragma once +#include "UHTMakefile/ClassMetaDataArchiveProxy.h" + +class FCompilerMetadataManager; +class FUHTMakefile; + +/* See UHTMakefile.h for overview how makefiles work. */ +struct FCompilerMetadataManagerArchiveProxy +{ + FCompilerMetadataManagerArchiveProxy(const FUHTMakefile& UHTMakefile, TArray>& CompilerMetadataManager); + FCompilerMetadataManagerArchiveProxy() { } + + static void AddReferencedNames(const FCompilerMetadataManager* CompilerMetadataManager, FUHTMakefile& UHTMakefile); + + friend FArchive& operator<<(FArchive& Ar, FCompilerMetadataManagerArchiveProxy& CompilerMetadataManagerArchiveProxy); + void Resolve(int32 Index, FCompilerMetadataManager& CompilerMetadataManager, FUHTMakefile& UHTMakefile); + + TArray> Array; +}; diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/EnumArchiveProxy.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/EnumArchiveProxy.cpp new file mode 100644 index 000000000000..f25416a6c834 --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/EnumArchiveProxy.cpp @@ -0,0 +1,64 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. +#include "UnrealHeaderTool.h" +#include "UHTMakefile/UHTMakefile.h" +#include "UHTMakefile/EnumArchiveProxy.h" + +FEnumArchiveProxy::FEnumArchiveProxy(FUHTMakefile& UHTMakefile, const UEnum* Enum) + : FFieldArchiveProxy(UHTMakefile, Enum) +{ + EnumFlags = static_cast(Enum->GetFlags()); + CppForm = static_cast(Enum->GetCppForm()); + + EnumValues.Empty(Enum->NumEnums()); + for (int32 i = 0; i < Enum->NumEnums(); ++i) + { + FName EnumName = Enum->GetNameByIndex(i); + uint8 Value = Enum->GetValueByIndex(i); + EnumValues.Add(TPairInitializer(FNameArchiveProxy(UHTMakefile, EnumName), Value)); + } + + CppType = Enum->CppType; +} + +UEnum* FEnumArchiveProxy::CreateEnum(const FUHTMakefile& UHTMakefile) const +{ + UObject* Outer = UHTMakefile.GetObjectByIndex(OuterIndex); + + // Create enum definition. + UEnum* Enum = new(EC_InternalUseOnlyConstructor, Outer, Name.CreateName(UHTMakefile), (EObjectFlags)ObjectFlagsUint32) UEnum(FObjectInitializer()); + PostConstruct(Enum, UHTMakefile); + return Enum; +} + +void FEnumArchiveProxy::PostConstruct(UEnum* Enum, const FUHTMakefile& UHTMakefile) const +{ + TArray> Names; + Names.Empty(EnumValues.Num()); + for (auto& Kvp : EnumValues) + { + Names.Add(TPairInitializer(Kvp.Key.CreateName(UHTMakefile), Kvp.Value)); + } + + Enum->SetEnums(Names, (UEnum::ECppForm)CppForm); + Enum->CppType = CppType; +} + +void FEnumArchiveProxy::AddReferencedNames(const UEnum* Enum, FUHTMakefile& UHTMakefile) +{ + FFieldArchiveProxy::AddReferencedNames(Enum, UHTMakefile); + for (int32 i = 0; i < Enum->NumEnums(); ++i) + { + UHTMakefile.AddName(Enum->GetNameByIndex(i)); + } +} + +FArchive& operator<<(FArchive& Ar, FEnumArchiveProxy& EnumArchiveProxy) +{ + Ar << static_cast(EnumArchiveProxy); + Ar << EnumArchiveProxy.EnumFlags; + Ar << EnumArchiveProxy.CppForm; + Ar << EnumArchiveProxy.EnumValues; + Ar << EnumArchiveProxy.CppType; + + return Ar; +} diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/EnumArchiveProxy.h b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/EnumArchiveProxy.h new file mode 100644 index 000000000000..82f03b682f51 --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/EnumArchiveProxy.h @@ -0,0 +1,22 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. +#pragma once +#include "UHTMakefile/FieldArchiveProxy.h" + +/* See UHTMakefile.h for overview how makefiles work. */ +struct FEnumArchiveProxy : public FFieldArchiveProxy +{ + FEnumArchiveProxy() { } + FEnumArchiveProxy(FUHTMakefile& UHTMakefile, const UEnum* Enum); + + UEnum* CreateEnum(const FUHTMakefile& UHTMakefile) const; + void PostConstruct(UEnum* Enum, const FUHTMakefile& UHTMakefile) const; + + static void AddReferencedNames(const UEnum* Enum, FUHTMakefile& UHTMakefile); + + friend FArchive& operator<<(FArchive& Ar, FEnumArchiveProxy& EnumArchiveProxy); + + uint32 EnumFlags; + uint32 CppForm; + FString CppType; + TArray> EnumValues; +}; diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/FieldArchiveProxy.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/FieldArchiveProxy.cpp new file mode 100644 index 000000000000..0cc6fa8c4584 --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/FieldArchiveProxy.cpp @@ -0,0 +1,30 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#include "UnrealHeaderTool.h" +#include "UHTMakefile/FieldArchiveProxy.h" +#include "UHTMakefile/UHTMakefile.h" + +FFieldArchiveProxy::FFieldArchiveProxy(FUHTMakefile& UHTMakefile, const UField* Field) + : FObjectBaseArchiveProxy(UHTMakefile, Field) +{ + NextIndex = UHTMakefile.GetFieldIndex(Field->Next); +} + +void FFieldArchiveProxy::Resolve(UField* Field, const FUHTMakefile& UHTMakefile) const +{ + FObjectBaseArchiveProxy::Resolve(Field, UHTMakefile); + Field->Next = UHTMakefile.GetFieldByIndex(NextIndex); +} + +void FFieldArchiveProxy::AddReferencedNames(const UField* Field, FUHTMakefile& UHTMakefile) +{ + FObjectBaseArchiveProxy::AddReferencedNames(Field, UHTMakefile); +} + +FArchive& operator<<(FArchive& Ar, FFieldArchiveProxy& FieldArchiveProxy) +{ + Ar << static_cast(FieldArchiveProxy); + Ar << FieldArchiveProxy.NextIndex; + + return Ar; +} diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/FieldArchiveProxy.h b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/FieldArchiveProxy.h new file mode 100644 index 000000000000..d3c8f3abac22 --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/FieldArchiveProxy.h @@ -0,0 +1,21 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#pragma once +#include "UHTMakefile/ObjectBaseArchiveProxy.h" + +class FUHTMakefile; + +/* See UHTMakefile.h for overview how makefiles work. */ +struct FFieldArchiveProxy : public FObjectBaseArchiveProxy +{ + FFieldArchiveProxy() { } + FFieldArchiveProxy(FUHTMakefile& UHTMakefile, const UField* Field); + + void Resolve(UField* Field, const FUHTMakefile& UHTMakefile) const; + static void AddReferencedNames(const UField* Field, FUHTMakefile& UHTMakefile); + + friend FArchive& operator<<(FArchive& Ar, FFieldArchiveProxy& FieldArchiveProxy); + + FSerializeIndex NextIndex; +}; diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/FileScopeArchiveProxy.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/FileScopeArchiveProxy.cpp new file mode 100644 index 000000000000..eb2711ada03a --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/FileScopeArchiveProxy.cpp @@ -0,0 +1,55 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#include "UnrealHeaderTool.h" +#include "UHTMakefile/UHTMakefile.h" +#include "UHTMakefile/FileScopeArchiveProxy.h" +#include "Scope.h" + +FArchive& operator<<(FArchive& Ar, FFileScopeArchiveProxy& FileScopeArchiveProxy) +{ + Ar << static_cast(FileScopeArchiveProxy); + Ar << FileScopeArchiveProxy.SourceFileIndex; + Ar << FileScopeArchiveProxy.Name; + Ar << FileScopeArchiveProxy.IncludedScopesIndexes; + + return Ar; +} + +FFileScopeArchiveProxy::FFileScopeArchiveProxy(const FUHTMakefile& UHTMakefile, const FFileScope* FileScope) + : FScopeArchiveProxy(UHTMakefile, FileScope) +{ + SourceFileIndex = UHTMakefile.GetUnrealSourceFileIndex(FileScope->GetSourceFile()); + Name = FNameArchiveProxy(UHTMakefile, FileScope->GetName()); + IncludedScopesIndexes.Empty(FileScope->GetIncludedScopes().Num()); + + for (const FFileScope* IncludedScope : FileScope->GetIncludedScopes()) + { + IncludedScopesIndexes.Add(UHTMakefile.GetFileScopeIndex(IncludedScope)); + } +} + +void FFileScopeArchiveProxy::AddReferencedNames(const FFileScope* FileScope, FUHTMakefile& UHTMakefile) +{ + FScopeArchiveProxy::AddReferencedNames(FileScope, UHTMakefile); + UHTMakefile.AddName(FileScope->Name); +} + +FFileScope* FFileScopeArchiveProxy::CreateFileScope(const FUHTMakefile& UHTMakefile) const +{ + return new FFileScope(Name.CreateName(UHTMakefile), nullptr); +} + +void FFileScopeArchiveProxy::PostConstruct(FFileScope* FileScope) const +{ + +} + +void FFileScopeArchiveProxy::Resolve(FFileScope* FileScope, const FUHTMakefile& UHTMakefile) const +{ + FScopeArchiveProxy::Resolve(FileScope, UHTMakefile); + FileScope->SetSourceFile(UHTMakefile.GetUnrealSourceFileByIndex(SourceFileIndex)); + for (int32 Index : IncludedScopesIndexes) + { + FileScope->IncludeScope(UHTMakefile.GetFileScopeByIndex(Index)); + } +} diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/FileScopeArchiveProxy.h b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/FileScopeArchiveProxy.h new file mode 100644 index 000000000000..3d07c2f22acb --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/FileScopeArchiveProxy.h @@ -0,0 +1,25 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#pragma once +#include "UHTMakefile/ScopeArchiveProxy.h" + +class FUHTMakefile; +class FFileScope; + +/* See UHTMakefile.h for overview how makefiles work. */ +struct FFileScopeArchiveProxy : public FScopeArchiveProxy +{ + FFileScopeArchiveProxy(const FUHTMakefile& UHTMakefile, const FFileScope* FileScope); + FFileScopeArchiveProxy() { } + + static void AddReferencedNames(const FFileScope* FileScope, FUHTMakefile& UHTMakefile); + + TArray IncludedScopesIndexes; + int32 SourceFileIndex; + FNameArchiveProxy Name; + + friend FArchive& operator<<(FArchive& Ar, FFileScopeArchiveProxy& FileScopeArchiveProxy); + FFileScope* CreateFileScope(const FUHTMakefile& UHTMakefile) const; + void PostConstruct(FFileScope* FileScope) const; + void Resolve(FFileScope* FileScope, const FUHTMakefile& UHTMakefile) const; +}; \ No newline at end of file diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/FunctionArchiveProxy.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/FunctionArchiveProxy.cpp new file mode 100644 index 000000000000..f8bb453cba7d --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/FunctionArchiveProxy.cpp @@ -0,0 +1,89 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#include "UnrealHeaderTool.h" +#include "UHTMakefile/UHTMakefile.h" +#include "UHTMakefile/FunctionArchiveProxy.h" + +FFunctionArchiveProxy::FFunctionArchiveProxy(FUHTMakefile& UHTMakefile, const UFunction* Function) + : FStructArchiveProxy(UHTMakefile, Function) +{ + FunctionFlags = Function->FunctionFlags; + RepOffset = Function->RepOffset; + NumParms = Function->NumParms; + ParmsSize = Function->ParmsSize; + ReturnValueOffset = Function->ReturnValueOffset; + RPCId = Function->RPCId; + RPCResponseId = Function->RPCResponseId; + FirstPropertyToInitIndex = UHTMakefile.GetPropertyIndex(Function->FirstPropertyToInit); +#if UE_BLUEPRINT_EVENTGRAPH_FASTCALLS + EventGraphFunctionIndex = UHTMakefile.GetFunctionIndex(Function->EventGraphFunction); + EventGraphCallOffset = Function->EventGraphCallOffset; +#else + EventGraphFunctionIndex = -1; + EventGraphCallOffset = -1; +#endif // UE_BLUEPRINT_EVENTGRAPH_FASTCALLS +} + +UFunction* FFunctionArchiveProxy::CreateFunction(const FUHTMakefile& UHTMakefile) const +{ + UObject* Outer = UHTMakefile.GetObjectByIndex(OuterIndex); + UFunction* Function = new(EC_InternalUseOnlyConstructor, Outer, Name.CreateName(UHTMakefile), (EObjectFlags)ObjectFlagsUint32) UFunction(FObjectInitializer()); + PostConstruct(Function); + return Function; +} + +void FFunctionArchiveProxy::PostConstruct(UFunction* Function) const +{ + Function->FunctionFlags = FunctionFlags; + Function->RepOffset = RepOffset; + Function->NumParms = NumParms; + Function->ParmsSize = ParmsSize; + Function->ReturnValueOffset = ReturnValueOffset; + Function->RPCId = RPCId; + Function->RPCResponseId = RPCResponseId; + Function->EventGraphCallOffset = EventGraphCallOffset; +} + +void FFunctionArchiveProxy::Resolve(UFunction* Function, const FUHTMakefile& UHTMakefile) const +{ + Function->FirstPropertyToInit = UHTMakefile.GetPropertyByIndex(FirstPropertyToInitIndex); +#if UE_BLUEPRINT_EVENTGRAPH_FASTCALLS + Function->EventGraphFunction = UHTMakefile.GetFunctionByIndex(EventGraphFunctionIndex); +#else + Function->EventGraphFunction = nullptr; +#endif // UE_BLUEPRINT_EVENTGRAPH_FASTCALLS +} + +FArchive& operator<<(FArchive& Ar, FFunctionArchiveProxy& FunctionArchiveProxy) +{ + Ar << static_cast(FunctionArchiveProxy); + Ar << FunctionArchiveProxy.FunctionFlags; + Ar << FunctionArchiveProxy.RepOffset; + Ar << FunctionArchiveProxy.NumParms; + Ar << FunctionArchiveProxy.ParmsSize; + Ar << FunctionArchiveProxy.ReturnValueOffset; + Ar << FunctionArchiveProxy.RPCId; + Ar << FunctionArchiveProxy.RPCResponseId; + Ar << FunctionArchiveProxy.FirstPropertyToInitIndex; + Ar << FunctionArchiveProxy.EventGraphFunctionIndex; + Ar << FunctionArchiveProxy.EventGraphCallOffset; + + return Ar; +} + +FDelegateFunctionArchiveProxy::FDelegateFunctionArchiveProxy(FUHTMakefile& UHTMakefile, const UDelegateFunction* DelegateFunction) + : FFunctionArchiveProxy(UHTMakefile, DelegateFunction) +{ } + +UDelegateFunction* FDelegateFunctionArchiveProxy::CreateDelegateFunction(const FUHTMakefile& UHTMakefile) const +{ + UObject* Outer = UHTMakefile.GetObjectByIndex(OuterIndex); + UDelegateFunction* DelegateFunction = new(EC_InternalUseOnlyConstructor, Outer, Name.CreateName(UHTMakefile), (EObjectFlags)ObjectFlagsUint32) UDelegateFunction(FObjectInitializer()); + PostConstruct(DelegateFunction); + return DelegateFunction; +} + +void FDelegateFunctionArchiveProxy::PostConstruct(UDelegateFunction* DelegateFunction) const +{ + FFunctionArchiveProxy::PostConstruct(DelegateFunction); +} diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/FunctionArchiveProxy.h b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/FunctionArchiveProxy.h new file mode 100644 index 000000000000..37c853e7bfbb --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/FunctionArchiveProxy.h @@ -0,0 +1,46 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#pragma once +#include "UHTMakefile/StructArchiveProxy.h" + +/* See UHTMakefile.h for overview how makefiles work. */ +struct FFunctionArchiveProxy : public FStructArchiveProxy +{ + FFunctionArchiveProxy() { } + FFunctionArchiveProxy(FUHTMakefile& UHTMakefile, const UFunction* Function); + + UFunction* CreateFunction(const FUHTMakefile& UHTMakefile) const; + void PostConstruct(UFunction* Function) const; + void Resolve(UFunction* Function, const FUHTMakefile& UHTMakefile) const; + + friend FArchive& operator<<(FArchive& Ar, FFunctionArchiveProxy& FunctionArchiveProxy); + + static void AddReferencedNames(const UFunction* Function, FUHTMakefile& UHTMakefile) + { + FStructArchiveProxy::AddReferencedNames(Function, UHTMakefile); + } + + uint32 FunctionFlags; + uint16 RepOffset; + uint8 NumParms; + uint16 ParmsSize; + uint16 ReturnValueOffset; + uint16 RPCId; + uint16 RPCResponseId; + int32 FirstPropertyToInitIndex; + int32 EventGraphFunctionIndex; + int32 EventGraphCallOffset; +}; + +struct FDelegateFunctionArchiveProxy : public FFunctionArchiveProxy +{ + FDelegateFunctionArchiveProxy() { } + FDelegateFunctionArchiveProxy(FUHTMakefile& UHTMakefile, const UDelegateFunction* DelegateFunction); + UDelegateFunction* CreateDelegateFunction(const FUHTMakefile& UHTMakefile) const; + void PostConstruct(UDelegateFunction* DelegateFunction) const; + static void AddReferencedNames(const UDelegateFunction* DelegateFunction, FUHTMakefile& UHTMakefile) + { + FFunctionArchiveProxy::AddReferencedNames(DelegateFunction, UHTMakefile); + } +}; + diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/HeaderProviderArchiveProxy.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/HeaderProviderArchiveProxy.cpp new file mode 100644 index 000000000000..ed6630882e0f --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/HeaderProviderArchiveProxy.cpp @@ -0,0 +1,28 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#include "UnrealHeaderTool.h" +#include "UHTMakefile/UHTMakefile.h" +#include "UHTMakefile/HeaderProviderArchiveProxy.h" +#include "HeaderProvider.h" + + +FHeaderProvider FHeaderProviderArchiveProxy::CreateHeaderProvider() const +{ + FHeaderProvider Result = FHeaderProvider(static_cast(Type), Id, bAutoInclude); + return Result; +} + +void FHeaderProviderArchiveProxy::ResolveHeaderProvider(FHeaderProvider& HeaderProvider, const FUHTMakefile& UHTMakefile) const +{ + HeaderProvider.SetCache(UHTMakefile.GetUnrealSourceFileByIndex(CacheIndex)); +} + +FArchive& operator<<(FArchive& Ar, FHeaderProviderArchiveProxy& HeaderProviderArchiveProxy) +{ + Ar << HeaderProviderArchiveProxy.Type; + Ar << HeaderProviderArchiveProxy.Id; + Ar << HeaderProviderArchiveProxy.CacheIndex; + Ar << HeaderProviderArchiveProxy.bAutoInclude; + + return Ar; +} diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/HeaderProviderArchiveProxy.h b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/HeaderProviderArchiveProxy.h new file mode 100644 index 000000000000..357bae827c28 --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/HeaderProviderArchiveProxy.h @@ -0,0 +1,19 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#pragma once + +class FUHTMakefile; +class FHeaderProvider; + +/* See UHTMakefile.h for overview how makefiles work. */ +struct FHeaderProviderArchiveProxy +{ + FHeaderProvider CreateHeaderProvider() const; + void ResolveHeaderProvider(FHeaderProvider& HeaderProvider, const FUHTMakefile& UHTMakefile) const; + + friend FArchive& operator<<(FArchive& Ar, FHeaderProviderArchiveProxy& HeaderProviderArchiveProxy); + uint8 Type; + FString Id; + int32 CacheIndex; + bool bAutoInclude; +}; diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/ImplementedInterfaceArchiveProxy.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/ImplementedInterfaceArchiveProxy.cpp new file mode 100644 index 000000000000..890ea3a0f836 --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/ImplementedInterfaceArchiveProxy.cpp @@ -0,0 +1,32 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#pragma once +#include "UnrealHeaderTool.h" +#include "UHTMakefile/UHTMakefile.h" +#include "UHTMakefile/ImplementedInterfaceArchiveProxy.h" + +FImplementedInterfaceArchiveProxy::FImplementedInterfaceArchiveProxy(FUHTMakefile& UHTMakefile, const FImplementedInterface& ImplementedInterface) +{ + ClassIndex = UHTMakefile.GetClassIndex(ImplementedInterface.Class); + PointerOffset = ImplementedInterface.PointerOffset; + bImplementedByK2 = ImplementedInterface.bImplementedByK2; +} + +FImplementedInterface FImplementedInterfaceArchiveProxy::CreateImplementedInterface(const FUHTMakefile& UHTMakefile) const +{ + return FImplementedInterface(nullptr, PointerOffset, bImplementedByK2); +} + +void FImplementedInterfaceArchiveProxy::Resolve(FImplementedInterface& ImplementedInterface, const FUHTMakefile& UHTMakefile) const +{ + ImplementedInterface.Class = UHTMakefile.GetClassByIndex(ClassIndex); +} + +FArchive& operator<<(FArchive& Ar, FImplementedInterfaceArchiveProxy& ImplementedInterfaceArchiveProxy) +{ + Ar << ImplementedInterfaceArchiveProxy.ClassIndex; + Ar << ImplementedInterfaceArchiveProxy.PointerOffset; + Ar << ImplementedInterfaceArchiveProxy.bImplementedByK2; + + return Ar; +} diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/ImplementedInterfaceArchiveProxy.h b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/ImplementedInterfaceArchiveProxy.h new file mode 100644 index 000000000000..487c000ed4d0 --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/ImplementedInterfaceArchiveProxy.h @@ -0,0 +1,21 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#pragma once + +class FUHTMakefile; + +/* See UHTMakefile.h for overview how makefiles work. */ +struct FImplementedInterfaceArchiveProxy +{ + FImplementedInterfaceArchiveProxy() { } + FImplementedInterfaceArchiveProxy(FUHTMakefile& UHTMakefile, const FImplementedInterface& ImplementedInterface); + + FImplementedInterface CreateImplementedInterface(const FUHTMakefile& UHTMakefile) const; + void Resolve(FImplementedInterface& ImplementedInterface, const FUHTMakefile& UHTMakefile) const; + + friend FArchive& operator<<(FArchive& Ar, FImplementedInterfaceArchiveProxy& ImplementedInterfaceArchiveProxy); + + FSerializeIndex ClassIndex; + int32 PointerOffset; + bool bImplementedByK2; +}; \ No newline at end of file diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/MakefileHelpers.h b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/MakefileHelpers.h new file mode 100644 index 000000000000..552487324d91 --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/MakefileHelpers.h @@ -0,0 +1,171 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. +#pragma once + +#include "UHTMakefile/NameArchiveProxy.h" + +static const int32 IndexOfNativeClass = -2; + +struct FSerializeIndex +{ + FSerializeIndex() + : Index(INDEX_NONE) + , NameProxy(FNameArchiveProxy()) + , Name(NAME_None) + { } + + FSerializeIndex(const FUHTMakefile& UHTMakefile, FName InName) + : Index(IndexOfNativeClass) + , NameProxy(FNameArchiveProxy(UHTMakefile, InName)) + , Name(InName) + { } + + FSerializeIndex(FName InName, int32 InIndex) + : Index(InIndex) + , NameProxy(FNameArchiveProxy()) + , Name(InName) + { } + + explicit FSerializeIndex(int32 InIndex) + : Index(InIndex) + , NameProxy(FNameArchiveProxy()) + , Name(NAME_None) + { } + + bool IsNone() const + { + return Name == NAME_None && Index == INDEX_NONE; + } + + bool HasName() + { + return Index == IndexOfNativeClass; + } + + int32 Index; + FNameArchiveProxy NameProxy; + FName Name; +}; + +inline FSerializeIndex operator+(FSerializeIndex Lhs, FSerializeIndex Rhs) +{ + check(Lhs.Name == NAME_None); + check(Rhs.Name == NAME_None); + return FSerializeIndex(NAME_None, Lhs.Index + Rhs.Index); +} + +inline FSerializeIndex operator+(FSerializeIndex Lhs, int Rhs) +{ + check(Lhs.Name == NAME_None); + return FSerializeIndex(NAME_None, Lhs.Index + Rhs); +} + +inline FSerializeIndex& operator+=(FSerializeIndex& Lhs, int32 Rhs) +{ + check(Lhs.Name == NAME_None); + Lhs.Index += Rhs; + return Lhs; +} + +inline FSerializeIndex& operator-=(FSerializeIndex& Lhs, int32 Rhs) +{ + check(Lhs.Name == NAME_None); + Lhs.Index -= Rhs; + return Lhs; +} + +inline bool operator==(FSerializeIndex& Lhs, int32 Rhs) +{ + check(Lhs.Name == NAME_None || Rhs == IndexOfNativeClass || Rhs == INDEX_NONE); + return Lhs.Index == Rhs; +} + +inline bool operator!=(FSerializeIndex& Lhs, int32 Rhs) +{ + return !(Lhs == Rhs); +} + +inline FArchive& operator<<(FArchive& Ar, FSerializeIndex& SerializeIndex) +{ + Ar << SerializeIndex.NameProxy; + Ar << SerializeIndex.Index; + + return Ar; +} + + +enum class ESerializedObjectType : uint32 +{ + EPackage, + EClass, + EField, + EEnum, + EStruct, + EScriptStruct, + EProperty, + EByteProperty, + EInt8Property, + EInt16Property, + EIntProperty, + EInt64Property, + EUInt16Property, + EUInt32Property, + EUInt64Property, + EFloatProperty, + EDoubleProperty, + EBoolProperty, + ENameProperty, + EStrProperty, + ETextProperty, + EDelegateProperty, + EMulticastDelegateProperty, + EObjectPropertyBase, + EClassProperty, + EObjectProperty, + EWeakObjectProperty, + ELazyObjectProperty, + EAssetObjectProperty, + EAssetClassProperty, + EInterfaceProperty, + EStructProperty, + EMapProperty, + EArrayProperty, + EUnrealSourceFile, + EFileScope, + EStructScope, + EScope, + EFunction, + EDelegateFunction, + EUnrealTypeDefinitionInfo, + ETypeDefinitionInfoMapEntry, + EGeneratedCodeCRC, + EPropertyBase, + EGScriptHelperEntry, + EClassMetaData, + EPropertyDataEntry, + EPublicClassSetEntry, + EPublicSourceFileSetEntry, + EUnrealSourceFilesMapEntry, + EMultipleInheritanceBaseClass, + EToken, + EEnumUnderlyingType, + EStructNameMapEntry, + EInterfaceAllocation, +}; + +enum class EUHTMakefileLoadingPhase : uint8 +{ + Preload, + Load, + Export, + Max, +}; + +inline uint8 operator+(EUHTMakefileLoadingPhase UHTMakefileLoadingPhase) +{ + return static_cast(UHTMakefileLoadingPhase); +} + +inline EUHTMakefileLoadingPhase& operator++(EUHTMakefileLoadingPhase& UHTMakefileLoadingPhase) +{ + return UHTMakefileLoadingPhase = static_cast(+UHTMakefileLoadingPhase + 1); +} \ No newline at end of file diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/MetadataArchiveProxy.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/MetadataArchiveProxy.cpp new file mode 100644 index 000000000000..2709cf68e0f0 --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/MetadataArchiveProxy.cpp @@ -0,0 +1,83 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#include "UnrealHeaderTool.h" +#include "UHTMakefile/UHTMakefile.h" +#include "UHTMakefile/MetadataArchiveProxy.h" + +FMetadataArchiveProxy::FMetadataArchiveProxy(FUHTMakefile& UHTMakefile, UMetaData* Metadata) + : FObjectBaseArchiveProxy(UHTMakefile, Metadata) +{ + for (auto& Kvp : UMetaData::KeyRedirectMap) + { + KeyRedirectMap.Add(TPairInitializer(FNameArchiveProxy(UHTMakefile, Kvp.Key), FNameArchiveProxy(UHTMakefile, Kvp.Value))); + } + + for (auto& Kvp : Metadata->ObjectMetaDataMap) + { + FSerializeIndex Index = UHTMakefile.GetObjectIndex(Kvp.Key.Get()); + TArray> Value; + Value.Empty(Kvp.Value.Num()); + for (const auto& KvpInner : Kvp.Value) + { + Value.Add(TPairInitializer(FNameArchiveProxy(UHTMakefile, KvpInner.Key), KvpInner.Value)); + } + ObjectMetaDataMapProxy.Add(TPairInitializer>>(Index, Value)); + } +} + +UMetaData* FMetadataArchiveProxy::CreateMetadata(const FUHTMakefile& UHTMakefile) const +{ + UObject* Outer = UHTMakefile.GetObjectByIndex(OuterIndex); + UMetaData* Metadata = new (EC_InternalUseOnlyConstructor, Outer, Name.CreateName(UHTMakefile), (EObjectFlags)ObjectFlagsUint32) UMetaData(FObjectInitializer()); + for (auto& Kvp : KeyRedirectMap) + { + UMetaData::KeyRedirectMap.Add(Kvp.Key.CreateName(UHTMakefile), Kvp.Value.CreateName(UHTMakefile)); + } + + return Metadata; +} + +void FMetadataArchiveProxy::Resolve(UMetaData* Metadata, const FUHTMakefile& UHTMakefile) const +{ + for (auto& Kvp : ObjectMetaDataMapProxy) + { + UObject* Object = UHTMakefile.GetObjectByIndex(Kvp.Key); + TMap& AddedObj = Metadata->ObjectMetaDataMap.Add(Object); + AddedObj.Reserve(AddedObj.Num() + Kvp.Value.Num()); + for (const auto& KvpInner : Kvp.Value) + { + AddedObj.Add(KvpInner.Key.CreateName(UHTMakefile), KvpInner.Value); + } + } +} + +void FMetadataArchiveProxy::AddReferencedNames(UMetaData* Metadata, FUHTMakefile& UHTMakefile) +{ + FObjectBaseArchiveProxy::AddReferencedNames(Metadata, UHTMakefile); + for (auto& Kvp : Metadata->ObjectMetaDataMap) + { + for (const auto& KvpInner : Kvp.Value) + { + UHTMakefile.AddName(KvpInner.Key); + } + } +} + +void FMetadataArchiveProxy::AddStaticallyReferencedNames(FUHTMakefile& UHTMakefile) +{ + for (auto& Kvp : UMetaData::KeyRedirectMap) + { + UHTMakefile.AddName(Kvp.Key); + UHTMakefile.AddName(Kvp.Value); + } +} + +FArchive& operator<<(FArchive& Ar, FMetadataArchiveProxy& MetadataArchiveProxy) +{ + Ar << static_cast(MetadataArchiveProxy); + Ar << MetadataArchiveProxy.ObjectMetaDataMapProxy; + Ar << MetadataArchiveProxy.KeyRedirectMap; + + return Ar; +} + diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/MetadataArchiveProxy.h b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/MetadataArchiveProxy.h new file mode 100644 index 000000000000..f5f345e96971 --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/MetadataArchiveProxy.h @@ -0,0 +1,25 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#pragma once +#include "UHTMakefile/ObjectBaseArchiveProxy.h" + +class FUHTMakefile; + +/* See UHTMakefile.h for overview how makefiles work. */ +struct FMetadataArchiveProxy : public FObjectBaseArchiveProxy +{ + FMetadataArchiveProxy() { } + FMetadataArchiveProxy(FUHTMakefile& UHTMakefile, UMetaData* Metadata); + + friend FArchive& operator<<(FArchive& Ar, FMetadataArchiveProxy& MetadataArchiveProxy); + + UMetaData* CreateMetadata(const FUHTMakefile& UHTMakefile) const; + void Resolve(UMetaData* Metadata, const FUHTMakefile& UHTMakefile) const; + + TArray> KeyRedirectMap; + TArray>>> ObjectMetaDataMapProxy; + + static void AddReferencedNames(UMetaData* Metadata, FUHTMakefile& UHTMakefile); + + static void AddStaticallyReferencedNames(FUHTMakefile& UHTMakefile); +}; diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/MultipleInheritanceBaseClassArchiveProxy.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/MultipleInheritanceBaseClassArchiveProxy.cpp new file mode 100644 index 000000000000..92f3be0460f2 --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/MultipleInheritanceBaseClassArchiveProxy.cpp @@ -0,0 +1,34 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. +#include "UnrealHeaderTool.h" +#include "UHTMakefile/UHTMakefile.h" +#include "UHTMakefile/MultipleInheritanceBaseClassArchiveProxy.h" + +FMultipleInheritanceBaseClassArchiveProxy::FMultipleInheritanceBaseClassArchiveProxy(const FUHTMakefile& UHTMakefile, const FMultipleInheritanceBaseClass* MultipleInheritanceBaseClass) +{ + ClassName = MultipleInheritanceBaseClass->ClassName; + InterfaceClassIndex = UHTMakefile.GetClassIndex(MultipleInheritanceBaseClass->InterfaceClass); +} + +void FMultipleInheritanceBaseClassArchiveProxy::AddReferencedNames(const FMultipleInheritanceBaseClass* MultipleInheritanceBaseClass, FUHTMakefile& UHTMakefile) +{ + +} + +FArchive& operator<<(FArchive& Ar, FMultipleInheritanceBaseClassArchiveProxy& MultipleInheritanceBaseClassArchiveProxy) +{ + Ar << MultipleInheritanceBaseClassArchiveProxy.ClassName; + Ar << MultipleInheritanceBaseClassArchiveProxy.InterfaceClassIndex; + + return Ar; +} + +void FMultipleInheritanceBaseClassArchiveProxy::Resolve(FMultipleInheritanceBaseClass* MultipleInheritanceBaseClass, const FUHTMakefile& UHTMakefile) const +{ + MultipleInheritanceBaseClass->InterfaceClass = UHTMakefile.GetClassByIndex(InterfaceClassIndex); +} + +FMultipleInheritanceBaseClass* FMultipleInheritanceBaseClassArchiveProxy::CreateMultipleInheritanceBaseClass() const +{ + return new FMultipleInheritanceBaseClass(ClassName); +} + diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/MultipleInheritanceBaseClassArchiveProxy.h b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/MultipleInheritanceBaseClassArchiveProxy.h new file mode 100644 index 000000000000..3797eb53fa20 --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/MultipleInheritanceBaseClassArchiveProxy.h @@ -0,0 +1,22 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#pragma once + +class FUHTMakefile; +struct FMultipleInheritanceBaseClass; + +/* See UHTMakefile.h for overview how makefiles work. */ +struct FMultipleInheritanceBaseClassArchiveProxy +{ + FMultipleInheritanceBaseClassArchiveProxy(const FUHTMakefile& UHTMakefile, const FMultipleInheritanceBaseClass* MultipleInheritanceBaseClass); + FMultipleInheritanceBaseClassArchiveProxy() { } + + static void AddReferencedNames(const FMultipleInheritanceBaseClass* MultipleInheritanceBaseClass, FUHTMakefile& UHTMakefile); + + friend FArchive& operator<<(FArchive& Ar, FMultipleInheritanceBaseClassArchiveProxy& MultipleInheritanceBaseClassArchiveProxy); + void Resolve(FMultipleInheritanceBaseClass* MultipleInheritanceBaseClass, const FUHTMakefile& UHTMakefile) const; + FMultipleInheritanceBaseClass* CreateMultipleInheritanceBaseClass() const; + + FString ClassName; + FSerializeIndex InterfaceClassIndex; +}; diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/NameArchiveProxy.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/NameArchiveProxy.cpp new file mode 100644 index 000000000000..f8c2a25f3ad8 --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/NameArchiveProxy.cpp @@ -0,0 +1,31 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#include "UnrealHeaderTool.h" +#include "UHTMakefile/UHTMakefile.h" +#include "UHTMakefile/NameArchiveProxy.h" + +FNameArchiveProxy::FNameArchiveProxy(const FUHTMakefile& UHTMakefile, FName Name) +{ + Number = Name.GetNumber(); + NameMapIndex = UHTMakefile.GetNameIndex(Name); +} + +FName FNameArchiveProxy::CreateName(const FUHTMakefile& UHTMakefile) const +{ + // if the name wasn't loaded (because it wasn't valid in this context) + FName MappedName = UHTMakefile.GetNameByIndex(NameMapIndex); + if (MappedName.IsNone()) + { + return NAME_None; + } + + return FName(MappedName, Number); +} + +FArchive& operator<<(FArchive& Ar, FNameArchiveProxy& NameArchiveProxy) +{ + Ar << NameArchiveProxy.NameMapIndex; + Ar << NameArchiveProxy.Number; + + return Ar; +} diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/NameArchiveProxy.h b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/NameArchiveProxy.h new file mode 100644 index 000000000000..ac12e38dd15f --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/NameArchiveProxy.h @@ -0,0 +1,19 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#pragma once + +class FUHTMakefile; + +/* See UHTMakefile.h for overview how makefiles work. */ +struct FNameArchiveProxy +{ + FNameArchiveProxy() { } + FNameArchiveProxy(const FUHTMakefile& UHTMakefile, FName Name); + + FName CreateName(const FUHTMakefile& UHTMakefile) const; + + friend FArchive& operator<<(FArchive& Ar, FNameArchiveProxy& NameArchiveProxy); + + int32 NameMapIndex; + int32 Number; +}; diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/NameLookupCPPArchiveProxy.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/NameLookupCPPArchiveProxy.cpp new file mode 100644 index 000000000000..fad7d4332604 --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/NameLookupCPPArchiveProxy.cpp @@ -0,0 +1,33 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. +#include "UnrealHeaderTool.h" +#include "UHTMakefile/UHTMakefile.h" +#include "UHTMakefile/NameLookupCPPArchiveProxy.h" + +FNameLookupCPPArchiveProxy::FNameLookupCPPArchiveProxy(const FUHTMakefile& UHTMakefile, const FNameLookupCPP* NameLookupCPP) +{ + TArray> StructNameMap; + StructNameMap.Empty(NameLookupCPP->StructNameMap.Num()); + for (auto& Kvp : NameLookupCPP->StructNameMap) + { + StructNameMap.Add(TPairInitializer(UHTMakefile.GetStructIndex(Kvp.Key), Kvp.Value)); + } + TArray InterfaceAllocations; + InterfaceAllocations.Empty(NameLookupCPP->InterfaceAllocations.Num()); + for (TCHAR* InterfaceAllocation : NameLookupCPP->InterfaceAllocations) + { + InterfaceAllocations.Add(InterfaceAllocation); + } +} + +FArchive& operator<<(FArchive& Ar, FNameLookupCPPArchiveProxy& NameLookupCPPArchiveProxy) +{ + Ar << NameLookupCPPArchiveProxy.InterfaceAllocations; + Ar << NameLookupCPPArchiveProxy.StructNameMap; + + return Ar; +} + +void FNameLookupCPPArchiveProxy::Resolve(FNameLookupCPP* NameLookupCPP, FUHTMakefile& UHTMakefile) +{ + +} diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/NameLookupCPPArchiveProxy.h b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/NameLookupCPPArchiveProxy.h new file mode 100644 index 000000000000..922906b2c66c --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/NameLookupCPPArchiveProxy.h @@ -0,0 +1,19 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#pragma once + +class FUHTMakefile; +struct FNameLookupCPP; + +/* See UHTMakefile.h for overview how makefiles work. */ +struct FNameLookupCPPArchiveProxy +{ + FNameLookupCPPArchiveProxy(const FUHTMakefile& UHTMakefile, const FNameLookupCPP* NameLookupCPP); + FNameLookupCPPArchiveProxy() { } + + friend FArchive& operator<<(FArchive& Ar, FNameLookupCPPArchiveProxy& NameLookupCPPArchiveProxy); + void Resolve(FNameLookupCPP* NameLookupCPP, FUHTMakefile& UHTMakefile); + + TArray> StructNameMap; + TArray InterfaceAllocations; +}; diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/ObjectBaseArchiveProxy.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/ObjectBaseArchiveProxy.cpp new file mode 100644 index 000000000000..23a0d2514ad3 --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/ObjectBaseArchiveProxy.cpp @@ -0,0 +1,46 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#include "UnrealHeaderTool.h" +#include "UHTMakefile/UHTMakefile.h" +#include "UHTMakefile/ObjectBaseArchiveProxy.h" + +FObjectBaseArchiveProxy::FObjectBaseArchiveProxy(FUHTMakefile& UHTMakefile, const UObjectBase* ObjectBase) +{ + ObjectFlagsUint32 = static_cast(ObjectBase->ObjectFlags); + ClassIndex = UHTMakefile.GetClassIndex(ObjectBase->Class); + Name = FNameArchiveProxy(UHTMakefile, ObjectBase->Name); + OuterIndex = UHTMakefile.GetObjectIndex(ObjectBase->Outer); +} + +UObjectBase* FObjectBaseArchiveProxy::CreateObjectBase(const FUHTMakefile& UHTMakefile) const +{ + UObjectBase* ObjectBase = new UObjectBase(nullptr, EObjectFlags(ObjectFlagsUint32), EInternalObjectFlags::Native, nullptr, Name.CreateName(UHTMakefile)); + PostConstruct(ObjectBase); + return ObjectBase; +} + +void FObjectBaseArchiveProxy::Resolve(UObjectBase* ObjectBase, const FUHTMakefile& UHTMakefile) const +{ + ObjectBase->Class = UHTMakefile.GetClassByIndex(ClassIndex); + ObjectBase->Outer = UHTMakefile.GetObjectByIndex(OuterIndex); +} + +void FObjectBaseArchiveProxy::AddReferencedNames(const UObjectBase* ObjectBase, FUHTMakefile& UHTMakefile) +{ + UHTMakefile.AddName(ObjectBase->Name); +} + +void FObjectBaseArchiveProxy::PostConstruct(UObjectBase* ObjectBase) const +{ + ObjectBase->ObjectFlags = static_cast(ObjectFlagsUint32); +} + +FArchive& operator<<(FArchive& Ar, FObjectBaseArchiveProxy& ObjectBaseArchiveProxy) +{ + Ar << ObjectBaseArchiveProxy.ObjectFlagsUint32; + Ar << ObjectBaseArchiveProxy.ClassIndex; + Ar << ObjectBaseArchiveProxy.Name; + Ar << ObjectBaseArchiveProxy.OuterIndex; + + return Ar; +} diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/ObjectBaseArchiveProxy.h b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/ObjectBaseArchiveProxy.h new file mode 100644 index 000000000000..58b954310b74 --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/ObjectBaseArchiveProxy.h @@ -0,0 +1,24 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#pragma once + +class FUHTMakefile; + +/* See UHTMakefile.h for overview how makefiles work. */ +struct FObjectBaseArchiveProxy +{ + FObjectBaseArchiveProxy() { } + FObjectBaseArchiveProxy(FUHTMakefile& UHTMakefile, const UObjectBase* ObjectBase); + + UObjectBase* CreateObjectBase(const FUHTMakefile& UHTMakefile) const; + void Resolve(UObjectBase* ObjectBase, const FUHTMakefile& UHTMakefile) const; + + friend FArchive& operator<<(FArchive& Ar, FObjectBaseArchiveProxy& ObjectBaseArchiveProxy); + + static void AddReferencedNames(const UObjectBase* ObjectBase, FUHTMakefile& UHTMakefile); + void PostConstruct(UObjectBase* ObjectBase) const; + uint32 ObjectFlagsUint32; + FSerializeIndex ClassIndex; + FNameArchiveProxy Name; + FSerializeIndex OuterIndex; +}; diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/PackageArchiveProxy.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/PackageArchiveProxy.cpp new file mode 100644 index 000000000000..74f61ec56c8d --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/PackageArchiveProxy.cpp @@ -0,0 +1,51 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#include "UnrealHeaderTool.h" +#include "UHTMakefile/UHTMakefile.h" +#include "UHTMakefile/PackageArchiveProxy.h" + +FPackageArchiveProxy::FPackageArchiveProxy(FUHTMakefile& UHTMakefile, UPackage* Package) + : FObjectBaseArchiveProxy(UHTMakefile, Package) +{ + PackageFlags = Package->GetPackageFlags(); + MetadataArchiveProxy = FMetadataArchiveProxy(UHTMakefile, Package->GetMetaData()); +} + +FArchive& operator<<(FArchive& Ar, FPackageArchiveProxy& PackageArchiveProxy) +{ + Ar << static_cast(PackageArchiveProxy); + Ar << PackageArchiveProxy.PackageFlags; + Ar << PackageArchiveProxy.MetadataArchiveProxy; + + return Ar; +} + +void FPackageArchiveProxy::AddReferencedNames(UPackage* Package, FUHTMakefile& UHTMakefile) +{ + FObjectBaseArchiveProxy::AddReferencedNames(Package, UHTMakefile); + FMetadataArchiveProxy::AddReferencedNames(Package->GetMetaData(), UHTMakefile); +} + +void FPackageArchiveProxy::Resolve(UPackage* Package, const FUHTMakefile& UHTMakefile) const +{ + FObjectBaseArchiveProxy::Resolve(Package, UHTMakefile); + MetadataArchiveProxy.Resolve(Package->GetMetaData(), UHTMakefile); +} + +UPackage* FPackageArchiveProxy::CreatePackage(const FUHTMakefile& UHTMakefile) +{ + FName PackageName = Name.CreateName(UHTMakefile); + UPackage* Package = Cast(StaticFindObjectFast(UPackage::StaticClass(), nullptr, PackageName, false, false)); + if (!Package) + { + Package = ::CreatePackage(nullptr, *PackageName.ToString()); + } + PostConstruct(Package); + return Package; +} + +void FPackageArchiveProxy::PostConstruct(UPackage* Package) const +{ + FObjectBaseArchiveProxy::PostConstruct(Package); + Package->SetPackageFlagsTo(PackageFlags); +} diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/PackageArchiveProxy.h b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/PackageArchiveProxy.h new file mode 100644 index 000000000000..37a21df6cfa7 --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/PackageArchiveProxy.h @@ -0,0 +1,24 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#pragma once +#include "UHTMakefile/ObjectBaseArchiveProxy.h" +#include "UHTMakefile/MetadataArchiveProxy.h" + +class FUHTMakefile; + +/* See UHTMakefile.h for overview how makefiles work. */ +struct FPackageArchiveProxy : public FObjectBaseArchiveProxy +{ + FPackageArchiveProxy() { } + FPackageArchiveProxy(FUHTMakefile& UHTMakefile, UPackage* Package); + + uint32 PackageFlags; + FMetadataArchiveProxy MetadataArchiveProxy; + + static void AddReferencedNames(UPackage* Package, FUHTMakefile& UHTMakefile); + + friend FArchive& operator<<(FArchive& Ar, FPackageArchiveProxy& PackageArchiveProxy); + void Resolve(UPackage* Package, const FUHTMakefile& UHTMakefile) const; + UPackage* CreatePackage(const FUHTMakefile& UHTMakefile); + void PostConstruct(UPackage* Package) const; +}; diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/PropertyArchiveProxy.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/PropertyArchiveProxy.cpp new file mode 100644 index 000000000000..eeac6d9ae95c --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/PropertyArchiveProxy.cpp @@ -0,0 +1,584 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#pragma once +#include "UnrealHeaderTool.h" +#include "UHTMakefile/UHTMakefile.h" +#include "UHTMakefile/PropertyArchiveProxy.h" + +FPropertyArchiveProxy::FPropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UProperty* Property) + : FFieldArchiveProxy(UHTMakefile, Property) +{ + ArrayDim = Property->ArrayDim; + ElementSize = Property->ElementSize; + PropertyFlags = Property->PropertyFlags; + RepIndex = Property->RepIndex; + RepNotifyFunc = FNameArchiveProxy(UHTMakefile, Property->RepNotifyFunc); + Offset_Internal = Property->Offset_Internal; + PropertyLinkNextIndex = UHTMakefile.GetPropertyIndex(Property->PropertyLinkNext); + NextRefIndex = UHTMakefile.GetPropertyIndex(Property->NextRef); + DestructorLinkNextIndex = UHTMakefile.GetPropertyIndex(Property->DestructorLinkNext); + PostConstructLinkNextIndex = UHTMakefile.GetPropertyIndex(Property->PostConstructLinkNext); +} + +UProperty* FPropertyArchiveProxy::CreateProperty(const FUHTMakefile& UHTMakefile) const +{ + UObject* Outer = UHTMakefile.GetObjectByIndex(OuterIndex); + UProperty* Property = new (EC_InternalUseOnlyConstructor, Outer, Name.CreateName(UHTMakefile), (EObjectFlags)ObjectFlagsUint32) UProperty(FObjectInitializer()); + PostConstruct(Property, UHTMakefile); + return Property; +} + +void FPropertyArchiveProxy::Resolve(UProperty* Property, const FUHTMakefile& UHTMakefile) const +{ + FFieldArchiveProxy::Resolve(Property, UHTMakefile); + Property->PropertyLinkNext = UHTMakefile.GetPropertyByIndex(PropertyLinkNextIndex); + Property->NextRef = UHTMakefile.GetPropertyByIndex(NextRefIndex); + Property->DestructorLinkNext = UHTMakefile.GetPropertyByIndex(DestructorLinkNextIndex); + Property->PostConstructLinkNext = UHTMakefile.GetPropertyByIndex(PostConstructLinkNextIndex); +} + +void FPropertyArchiveProxy::AddReferencedNames(const UProperty* Property, FUHTMakefile& UHTMakefile) +{ + FFieldArchiveProxy::AddReferencedNames(Property, UHTMakefile); + UHTMakefile.AddName(Property->RepNotifyFunc); +} + +void FPropertyArchiveProxy::PostConstruct(UProperty* Property, const FUHTMakefile& UHTMakefile) const +{ + Property->ArrayDim = ArrayDim; + Property->ElementSize = ElementSize; + Property->PropertyFlags = PropertyFlags; + Property->RepIndex = RepIndex; + Property->RepNotifyFunc = RepNotifyFunc.CreateName(UHTMakefile); + Property->Offset_Internal = Offset_Internal; +} + +FArchive& operator<<(FArchive& Ar, FPropertyArchiveProxy& PropertyArchiveProxy) +{ + Ar << static_cast(PropertyArchiveProxy); + Ar << PropertyArchiveProxy.ArrayDim; + Ar << PropertyArchiveProxy.ElementSize; + Ar << PropertyArchiveProxy.PropertyFlags; + Ar << PropertyArchiveProxy.RepIndex; + Ar << PropertyArchiveProxy.RepNotifyFunc; + Ar << PropertyArchiveProxy.Offset_Internal; + Ar << PropertyArchiveProxy.PropertyLinkNextIndex; + Ar << PropertyArchiveProxy.NextRefIndex; + Ar << PropertyArchiveProxy.DestructorLinkNextIndex; + Ar << PropertyArchiveProxy.PostConstructLinkNextIndex; + + return Ar; +} + +FArchive& operator<<(FArchive& Ar, FArrayPropertyArchiveProxy& ArrayPropertyArchiveProxy) +{ + Ar << static_cast(ArrayPropertyArchiveProxy); + Ar << ArrayPropertyArchiveProxy.InnerIndex; + + return Ar; +} + +FArchive& operator<<(FArchive& Ar, FMapPropertyArchiveProxy& MapPropertyArchiveProxy) +{ + Ar << static_cast(MapPropertyArchiveProxy); + Ar << MapPropertyArchiveProxy.KeyPropIndex; + Ar << MapPropertyArchiveProxy.ValuePropIndex; + Ar << MapPropertyArchiveProxy.MapLayout; + + return Ar; +} + +FBytePropertyArchiveProxy::FBytePropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UByteProperty* ByteProperty) + : FPropertyArchiveProxy(UHTMakefile, ByteProperty) +{ + EnumIndex = UHTMakefile.GetEnumIndex(ByteProperty->Enum); +} + +UByteProperty* FBytePropertyArchiveProxy::CreateByteProperty(const FUHTMakefile& UHTMakefile) const +{ + UObject* Outer = UHTMakefile.GetObjectByIndex(OuterIndex); + UByteProperty* ByteProperty = new (EC_InternalUseOnlyConstructor, Outer, Name.CreateName(UHTMakefile), (EObjectFlags)ObjectFlagsUint32) UByteProperty(FObjectInitializer()); + PostConstruct(ByteProperty, UHTMakefile); + return ByteProperty; +} + +void FBytePropertyArchiveProxy::Resolve(UByteProperty* ByteProperty, const FUHTMakefile& UHTMakefile) const +{ + FPropertyArchiveProxy::Resolve(ByteProperty, UHTMakefile); + ByteProperty->Enum = UHTMakefile.GetEnumByIndex(EnumIndex); +} + +FArchive& operator<<(FArchive& Ar, FBytePropertyArchiveProxy& BytePropertyArchiveProxy) +{ + Ar << static_cast(BytePropertyArchiveProxy); + Ar << BytePropertyArchiveProxy.EnumIndex; + + return Ar; +} + +FObjectPropertyBaseArchiveProxy::FObjectPropertyBaseArchiveProxy(FUHTMakefile& UHTMakefile, const UObjectPropertyBase* ObjectPropertyBase) + : FPropertyArchiveProxy(UHTMakefile, ObjectPropertyBase) +{ + PropertyClassIndex = UHTMakefile.GetClassIndex(ObjectPropertyBase->PropertyClass); +} + +UObjectPropertyBase* FObjectPropertyBaseArchiveProxy::CreateObjectPropertyBase(const FUHTMakefile& UHTMakefile) const +{ + UObject* Outer = UHTMakefile.GetObjectByIndex(OuterIndex); + UObjectPropertyBase* ObjectPropertyBase = new (EC_InternalUseOnlyConstructor, Outer, Name.CreateName(UHTMakefile), (EObjectFlags)ObjectFlagsUint32) UObjectPropertyBase(FObjectInitializer()); + PostConstruct(ObjectPropertyBase, UHTMakefile); + return ObjectPropertyBase; +} + +void FObjectPropertyBaseArchiveProxy::Resolve(UObjectPropertyBase* ObjectPropertyBase, const FUHTMakefile& UHTMakefile) const +{ + FPropertyArchiveProxy::Resolve(ObjectPropertyBase, UHTMakefile); + ObjectPropertyBase->PropertyClass = UHTMakefile.GetClassByIndex(PropertyClassIndex); +} + +FArchive& operator<<(FArchive& Ar, FObjectPropertyBaseArchiveProxy& ObjectPropertyBaseArchiveProxy) +{ + Ar << static_cast(ObjectPropertyBaseArchiveProxy); + Ar << ObjectPropertyBaseArchiveProxy.PropertyClassIndex; + + return Ar; +} + +FInt8PropertyArchiveProxy::FInt8PropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UInt8Property* Int8Property) + : FPropertyArchiveProxy(UHTMakefile, Int8Property) +{ } + +UInt8Property* FInt8PropertyArchiveProxy::CreateInt8Property(const FUHTMakefile& UHTMakefile) const +{ + UObject* Outer = UHTMakefile.GetObjectByIndex(OuterIndex); + UInt8Property* Int8Property = new (EC_InternalUseOnlyConstructor, Outer, Name.CreateName(UHTMakefile), (EObjectFlags)ObjectFlagsUint32) UInt8Property(FObjectInitializer()); + PostConstruct(Int8Property, UHTMakefile); + return Int8Property; +} + +FInt16PropertyArchiveProxy::FInt16PropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UInt16Property* Int16Property) + : FPropertyArchiveProxy(UHTMakefile, Int16Property) +{ } + +UInt16Property* FInt16PropertyArchiveProxy::CreateInt16Property(const FUHTMakefile& UHTMakefile) const +{ + UObject* Outer = UHTMakefile.GetObjectByIndex(OuterIndex); + UInt16Property* Int16Property = new (EC_InternalUseOnlyConstructor, Outer, Name.CreateName(UHTMakefile), (EObjectFlags)ObjectFlagsUint32) UInt16Property(FObjectInitializer()); + PostConstruct(Int16Property, UHTMakefile); + return Int16Property; +} + +FIntPropertyArchiveProxy::FIntPropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UIntProperty* IntProperty) + : FPropertyArchiveProxy(UHTMakefile, IntProperty) +{ } + +UIntProperty* FIntPropertyArchiveProxy::CreateIntProperty(const FUHTMakefile& UHTMakefile) const +{ + UObject* Outer = UHTMakefile.GetObjectByIndex(OuterIndex); + UIntProperty* IntProperty = new (EC_InternalUseOnlyConstructor, Outer, Name.CreateName(UHTMakefile), (EObjectFlags)ObjectFlagsUint32) UIntProperty(FObjectInitializer()); + PostConstruct(IntProperty, UHTMakefile); + return IntProperty; +} + +FInt64PropertyArchiveProxy::FInt64PropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UInt64Property* Int64Property) + : FPropertyArchiveProxy(UHTMakefile, Int64Property) +{ } + +UInt64Property* FInt64PropertyArchiveProxy::CreateInt64Property(const FUHTMakefile& UHTMakefile) const +{ + UObject* Outer = UHTMakefile.GetObjectByIndex(OuterIndex); + UInt64Property* Int64Property = new (EC_InternalUseOnlyConstructor, Outer, Name.CreateName(UHTMakefile), (EObjectFlags)ObjectFlagsUint32) UInt64Property(FObjectInitializer()); + PostConstruct(Int64Property, UHTMakefile); + return Int64Property; +} + +FUInt16PropertyArchiveProxy::FUInt16PropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UUInt16Property* UInt16Property) + : FPropertyArchiveProxy(UHTMakefile, UInt16Property) +{ } + +UUInt16Property* FUInt16PropertyArchiveProxy::CreateUInt16Property(const FUHTMakefile& UHTMakefile) const +{ + UObject* Outer = UHTMakefile.GetObjectByIndex(OuterIndex); + UUInt16Property* UInt16Property = new (EC_InternalUseOnlyConstructor, Outer, Name.CreateName(UHTMakefile), (EObjectFlags)ObjectFlagsUint32) UUInt16Property(FObjectInitializer()); + PostConstruct(UInt16Property, UHTMakefile); + return UInt16Property; +} + +FUInt32PropertyArchiveProxy::FUInt32PropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UUInt32Property* UInt32Property) + : FPropertyArchiveProxy(UHTMakefile, UInt32Property) +{ } + +UUInt32Property* FUInt32PropertyArchiveProxy::CreateUInt32Property(const FUHTMakefile& UHTMakefile) const +{ + UObject* Outer = UHTMakefile.GetObjectByIndex(OuterIndex); + UUInt32Property* UInt32Property = new (EC_InternalUseOnlyConstructor, Outer, Name.CreateName(UHTMakefile), (EObjectFlags)ObjectFlagsUint32) UUInt32Property(FObjectInitializer()); + PostConstruct(UInt32Property, UHTMakefile); + return UInt32Property; +} + +FUInt64PropertyArchiveProxy::FUInt64PropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UUInt64Property* UInt64Property) + : FPropertyArchiveProxy(UHTMakefile, UInt64Property) +{ } + +UUInt64Property* FUInt64PropertyArchiveProxy::CreateUInt64Property(const FUHTMakefile& UHTMakefile) const +{ + UObject* Outer = UHTMakefile.GetObjectByIndex(OuterIndex); + UUInt64Property* UInt64Property = new (EC_InternalUseOnlyConstructor, Outer, Name.CreateName(UHTMakefile), (EObjectFlags)ObjectFlagsUint32) UUInt64Property(FObjectInitializer()); + PostConstruct(UInt64Property, UHTMakefile); + return UInt64Property; +} + +FFloatPropertyArchiveProxy::FFloatPropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UFloatProperty* FloatProperty) + : FPropertyArchiveProxy(UHTMakefile, FloatProperty) +{ } + +UFloatProperty* FFloatPropertyArchiveProxy::CreateFloatProperty(const FUHTMakefile& UHTMakefile) const +{ + UObject* Outer = UHTMakefile.GetObjectByIndex(OuterIndex); + UFloatProperty* FloatProperty = new (EC_InternalUseOnlyConstructor, Outer, Name.CreateName(UHTMakefile), (EObjectFlags)ObjectFlagsUint32) UFloatProperty(FObjectInitializer()); + PostConstruct(FloatProperty, UHTMakefile); + return FloatProperty; +} + +FDoublePropertyArchiveProxy::FDoublePropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UDoubleProperty* DoubleProperty) + : FPropertyArchiveProxy(UHTMakefile, DoubleProperty) +{ } + +UDoubleProperty* FDoublePropertyArchiveProxy::CreateDoubleProperty(const FUHTMakefile& UHTMakefile) const +{ + UObject* Outer = UHTMakefile.GetObjectByIndex(OuterIndex); + UDoubleProperty* DoubleProperty = new (EC_InternalUseOnlyConstructor, Outer, Name.CreateName(UHTMakefile), (EObjectFlags)ObjectFlagsUint32) UDoubleProperty(FObjectInitializer()); + PostConstruct(DoubleProperty, UHTMakefile); + return DoubleProperty; +} + +FBoolPropertyArchiveProxy::FBoolPropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UBoolProperty* BoolProperty) + : FPropertyArchiveProxy(UHTMakefile, BoolProperty) +{ + FieldSize = BoolProperty->FieldSize; + ByteOffset = BoolProperty->ByteOffset; + ByteMask = BoolProperty->ByteMask; + FieldMask = BoolProperty->FieldMask; +} + +UBoolProperty* FBoolPropertyArchiveProxy::CreateBoolProperty(const FUHTMakefile& UHTMakefile) const +{ + UObject* Outer = UHTMakefile.GetObjectByIndex(OuterIndex); + UBoolProperty* BoolProperty = new (EC_InternalUseOnlyConstructor, Outer, Name.CreateName(UHTMakefile), (EObjectFlags)ObjectFlagsUint32) UBoolProperty(FObjectInitializer()); + PostConstruct(BoolProperty, UHTMakefile); + return BoolProperty; +} + +void FBoolPropertyArchiveProxy::PostConstruct(UBoolProperty* BoolProperty, const FUHTMakefile& UHTMakefile) const +{ + FPropertyArchiveProxy::PostConstruct(BoolProperty, UHTMakefile); + BoolProperty->FieldSize = FieldSize; + BoolProperty->ByteOffset = ByteOffset; + BoolProperty->ByteMask = ByteMask; + BoolProperty->FieldMask = FieldMask; +} + +FNamePropertyArchiveProxy::FNamePropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UNameProperty* NameProperty) : FPropertyArchiveProxy(UHTMakefile, NameProperty) +{ } + +UNameProperty* FNamePropertyArchiveProxy::CreateNameProperty(const FUHTMakefile& UHTMakefile) const +{ + UObject* Outer = UHTMakefile.GetObjectByIndex(OuterIndex); + UNameProperty* NameProperty = new (EC_InternalUseOnlyConstructor, Outer, Name.CreateName(UHTMakefile), (EObjectFlags)ObjectFlagsUint32) UNameProperty(FObjectInitializer()); + PostConstruct(NameProperty, UHTMakefile); + return NameProperty; +} + +FStrPropertyArchiveProxy::FStrPropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UStrProperty* StrProperty) + : FPropertyArchiveProxy(UHTMakefile, StrProperty) +{ } + +UStrProperty* FStrPropertyArchiveProxy::CreateStrProperty(const FUHTMakefile& UHTMakefile) const +{ + UObject* Outer = UHTMakefile.GetObjectByIndex(OuterIndex); + UStrProperty* StrProperty = new (EC_InternalUseOnlyConstructor, Outer, Name.CreateName(UHTMakefile), (EObjectFlags)ObjectFlagsUint32) UStrProperty(FObjectInitializer()); + PostConstruct(StrProperty, UHTMakefile); + return StrProperty; +} + +FTextPropertyArchiveProxy::FTextPropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UTextProperty* TextProperty) + : FPropertyArchiveProxy(UHTMakefile, TextProperty) +{ } + +UTextProperty* FTextPropertyArchiveProxy::CreateTextProperty(const FUHTMakefile& UHTMakefile) const +{ + UObject* Outer = UHTMakefile.GetObjectByIndex(OuterIndex); + UTextProperty* TextProperty = new (EC_InternalUseOnlyConstructor, Outer, Name.CreateName(UHTMakefile), (EObjectFlags)ObjectFlagsUint32) UTextProperty(FObjectInitializer()); + PostConstruct(TextProperty, UHTMakefile); + return TextProperty; +} + +FDelegatePropertyArchiveProxy::FDelegatePropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UDelegateProperty* DelegateProperty) + : FPropertyArchiveProxy(UHTMakefile, DelegateProperty) +{ + SignatureFunctionIndex = UHTMakefile.GetFunctionIndex(DelegateProperty->SignatureFunction); +} + +UDelegateProperty* FDelegatePropertyArchiveProxy::CreateDelegateProperty(const FUHTMakefile& UHTMakefile) const +{ + UObject* Outer = UHTMakefile.GetObjectByIndex(OuterIndex); + UDelegateProperty* DelegateProperty = new (EC_InternalUseOnlyConstructor, Outer, Name.CreateName(UHTMakefile), (EObjectFlags)ObjectFlagsUint32) UDelegateProperty(FObjectInitializer()); + PostConstruct(DelegateProperty, UHTMakefile); + return DelegateProperty; +} + +void FDelegatePropertyArchiveProxy::Resolve(UDelegateProperty* DelegateProperty, const FUHTMakefile& UHTMakefile) +{ + FPropertyArchiveProxy::Resolve(DelegateProperty, UHTMakefile); + DelegateProperty->SignatureFunction = UHTMakefile.GetFunctionByIndex(SignatureFunctionIndex); +} + +void FDelegatePropertyArchiveProxy::AddReferencedNames(const UDelegateProperty* Property, FUHTMakefile& UHTMakefile) +{ + FPropertyArchiveProxy::AddReferencedNames(Property, UHTMakefile); +} + +FMulticastDelegatePropertyArchiveProxy::FMulticastDelegatePropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UMulticastDelegateProperty* MulticastDelegateProperty) + : FPropertyArchiveProxy(UHTMakefile, MulticastDelegateProperty) +{ } + +UMulticastDelegateProperty* FMulticastDelegatePropertyArchiveProxy::CreateMulticastDelegateProperty(const FUHTMakefile& UHTMakefile) const +{ + UObject* Outer = UHTMakefile.GetObjectByIndex(OuterIndex); + UMulticastDelegateProperty* MulticastDelegateProperty = new (EC_InternalUseOnlyConstructor, Outer, Name.CreateName(UHTMakefile), (EObjectFlags)ObjectFlagsUint32) UMulticastDelegateProperty(FObjectInitializer()); + PostConstruct(MulticastDelegateProperty, UHTMakefile); + return MulticastDelegateProperty; +} + +FClassPropertyArchiveProxy::FClassPropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UClassProperty* ClassProperty) + : FObjectPropertyBaseArchiveProxy(UHTMakefile, ClassProperty) +{ + MetaClassIndex = UHTMakefile.GetClassIndex(ClassProperty->MetaClass); +} + +UClassProperty* FClassPropertyArchiveProxy::CreateClassProperty(const FUHTMakefile& UHTMakefile) const +{ + UObject* Outer = UHTMakefile.GetObjectByIndex(OuterIndex); + UClassProperty* ClassProperty = new (EC_InternalUseOnlyConstructor, Outer, Name.CreateName(UHTMakefile), (EObjectFlags)ObjectFlagsUint32) UClassProperty(FObjectInitializer()); + PostConstruct(ClassProperty, UHTMakefile); + return ClassProperty; +} + +void FClassPropertyArchiveProxy::Resolve(UClassProperty* ClassProperty, const FUHTMakefile& UHTMakefile) const +{ + FObjectPropertyBaseArchiveProxy::Resolve(ClassProperty, UHTMakefile); + ClassProperty->MetaClass = UHTMakefile.GetClassByIndex(MetaClassIndex); +} + +FArchive& operator<<(FArchive& Ar, FClassPropertyArchiveProxy& ClassPropertyArchiveProxy) +{ + Ar << static_cast(ClassPropertyArchiveProxy); + Ar << ClassPropertyArchiveProxy.MetaClassIndex; + + return Ar; +} + +FObjectPropertyArchiveProxy::FObjectPropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UObjectProperty* ObjectProperty) + : FObjectPropertyBaseArchiveProxy(UHTMakefile, ObjectProperty) +{ } + +UObjectProperty* FObjectPropertyArchiveProxy::CreateObjectProperty(const FUHTMakefile& UHTMakefile) const +{ + UObject* Outer = UHTMakefile.GetObjectByIndex(OuterIndex); + UObjectProperty* ObjectProperty = new (EC_InternalUseOnlyConstructor, Outer, Name.CreateName(UHTMakefile), (EObjectFlags)ObjectFlagsUint32) UObjectProperty(FObjectInitializer()); + PostConstruct(ObjectProperty, UHTMakefile); + return ObjectProperty; +} + +FWeakObjectPropertyArchiveProxy::FWeakObjectPropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UWeakObjectProperty* WeakObjectProperty) + : FObjectPropertyBaseArchiveProxy(UHTMakefile, WeakObjectProperty) +{ } + +UWeakObjectProperty* FWeakObjectPropertyArchiveProxy::CreateWeakObjectProperty(const FUHTMakefile& UHTMakefile) const +{ + UObject* Outer = UHTMakefile.GetObjectByIndex(OuterIndex); + UWeakObjectProperty* WeakObjectProperty = new (EC_InternalUseOnlyConstructor, Outer, Name.CreateName(UHTMakefile), (EObjectFlags)ObjectFlagsUint32) UWeakObjectProperty(FObjectInitializer()); + PostConstruct(WeakObjectProperty, UHTMakefile); + return WeakObjectProperty; +} + +FLazyObjectPropertyArchiveProxy::FLazyObjectPropertyArchiveProxy(FUHTMakefile& UHTMakefile, const ULazyObjectProperty* LazyObjectProperty) + : FObjectPropertyBaseArchiveProxy(UHTMakefile, LazyObjectProperty) +{ } + +ULazyObjectProperty* FLazyObjectPropertyArchiveProxy::CreateLazyObjectProperty(const FUHTMakefile& UHTMakefile) const +{ + UObject* Outer = UHTMakefile.GetObjectByIndex(OuterIndex); + ULazyObjectProperty* LazyObjectProperty = new (EC_InternalUseOnlyConstructor, Outer, Name.CreateName(UHTMakefile), (EObjectFlags)ObjectFlagsUint32) ULazyObjectProperty(FObjectInitializer()); + PostConstruct(LazyObjectProperty, UHTMakefile); + return LazyObjectProperty; +} + +FAssetObjectPropertyArchiveProxy::FAssetObjectPropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UAssetObjectProperty* AssetObjectProperty) + : FObjectPropertyBaseArchiveProxy(UHTMakefile, AssetObjectProperty) +{ } + +UAssetObjectProperty* FAssetObjectPropertyArchiveProxy::CreateAssetObjectProperty(const FUHTMakefile& UHTMakefile) const +{ + UObject* Outer = UHTMakefile.GetObjectByIndex(OuterIndex); + UAssetObjectProperty* AssetObjectProperty = new (EC_InternalUseOnlyConstructor, Outer, Name.CreateName(UHTMakefile), (EObjectFlags)ObjectFlagsUint32) UAssetObjectProperty(FObjectInitializer()); + PostConstruct(AssetObjectProperty, UHTMakefile); + return AssetObjectProperty; +} + +FAssetClassPropertyArchiveProxy::FAssetClassPropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UAssetClassProperty* AssetClassProperty) + : FObjectPropertyBaseArchiveProxy(UHTMakefile, AssetClassProperty) +{ + MetaClassIndex = UHTMakefile.GetClassIndex(AssetClassProperty->MetaClass); +} + +UAssetClassProperty* FAssetClassPropertyArchiveProxy::CreateAssetClassProperty(const FUHTMakefile& UHTMakefile) const +{ + UObject* Outer = UHTMakefile.GetObjectByIndex(OuterIndex); + UAssetClassProperty* AssetClassProperty = new (EC_InternalUseOnlyConstructor, Outer, Name.CreateName(UHTMakefile), (EObjectFlags)ObjectFlagsUint32) UAssetClassProperty(FObjectInitializer()); + PostConstruct(AssetClassProperty, UHTMakefile); + return AssetClassProperty; +} + +void FAssetClassPropertyArchiveProxy::Resolve(UAssetClassProperty* AssetClassProperty, const FUHTMakefile& UHTMakefile) const +{ + FObjectPropertyBaseArchiveProxy::Resolve(AssetClassProperty, UHTMakefile); + AssetClassProperty->MetaClass = UHTMakefile.GetClassByIndex(MetaClassIndex); +} + +FArchive& operator<<(FArchive& Ar, FAssetClassPropertyArchiveProxy& AssetClassPropertyArchiveProxy) +{ + Ar << static_cast(AssetClassPropertyArchiveProxy); + Ar << AssetClassPropertyArchiveProxy.MetaClassIndex; + + return Ar; +} + +FInterfacePropertyArchiveProxy::FInterfacePropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UInterfaceProperty* InterfaceProperty) + : FPropertyArchiveProxy(UHTMakefile, InterfaceProperty) +{ + InterfaceClassIndex = UHTMakefile.GetClassIndex(InterfaceProperty->InterfaceClass); +} + +UInterfaceProperty* FInterfacePropertyArchiveProxy::CreateInterfaceProperty(const FUHTMakefile& UHTMakefile) const +{ + UObject* Outer = UHTMakefile.GetObjectByIndex(OuterIndex); + UInterfaceProperty* InterfaceProperty = new (EC_InternalUseOnlyConstructor, Outer, Name.CreateName(UHTMakefile), (EObjectFlags)ObjectFlagsUint32) UInterfaceProperty(FObjectInitializer()); + PostConstruct(InterfaceProperty, UHTMakefile); + return InterfaceProperty; +} + +void FInterfacePropertyArchiveProxy::Resolve(UInterfaceProperty* InterfaceProperty, const FUHTMakefile& UHTMakefile) const +{ + FPropertyArchiveProxy::Resolve(InterfaceProperty, UHTMakefile); + InterfaceProperty->InterfaceClass = UHTMakefile.GetClassByIndex(InterfaceClassIndex); +} + +FArchive& operator<<(FArchive& Ar, FInterfacePropertyArchiveProxy& InterfacePropertyArchiveProxy) +{ + Ar << static_cast(InterfacePropertyArchiveProxy); + Ar << InterfacePropertyArchiveProxy.InterfaceClassIndex; + + return Ar; +} + +FStructPropertyArchiveProxy::FStructPropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UStructProperty* StructProperty) + : FPropertyArchiveProxy(UHTMakefile, StructProperty) +{ + StructIndex = UHTMakefile.GetScriptStructIndex(StructProperty->Struct); +} + +UStructProperty* FStructPropertyArchiveProxy::CreateStructProperty(const FUHTMakefile& UHTMakefile) const +{ + UObject* Outer = UHTMakefile.GetObjectByIndex(OuterIndex); + UStructProperty* StructProperty = new (EC_InternalUseOnlyConstructor, Outer, Name.CreateName(UHTMakefile), (EObjectFlags)ObjectFlagsUint32) UStructProperty(FObjectInitializer()); + PostConstruct(StructProperty, UHTMakefile); + return StructProperty; +} + +void FStructPropertyArchiveProxy::Resolve(UStructProperty* StructProperty, const FUHTMakefile& UHTMakefile) const +{ + FPropertyArchiveProxy::Resolve(StructProperty, UHTMakefile); + StructProperty->Struct = UHTMakefile.GetScriptStructByIndex(StructIndex); +} + +FArchive& operator<<(FArchive& Ar, FStructPropertyArchiveProxy& StructPropertyArchiveProxy) +{ + Ar << static_cast(StructPropertyArchiveProxy); + Ar << StructPropertyArchiveProxy.StructIndex; + + return Ar; +} + +FArchive& operator<<(FArchive& Ar, FScriptSparseArrayLayout& ScriptSparseArrayLayout) +{ + Ar << ScriptSparseArrayLayout.ElementOffset; + Ar << ScriptSparseArrayLayout.Alignment; + Ar << ScriptSparseArrayLayout.Size; + + return Ar; +} + +FArchive& operator<<(FArchive& Ar, FScriptSetLayout& ScriptSetLayout) +{ + Ar << ScriptSetLayout.ElementOffset; + Ar << ScriptSetLayout.HashNextIdOffset; + Ar << ScriptSetLayout.HashIndexOffset; + Ar << ScriptSetLayout.Size; + Ar << ScriptSetLayout.SparseArrayLayout; + + return Ar; +} + +FArchive& operator<<(FArchive& Ar, FScriptMapLayout& ScriptMapLayout) +{ + Ar << ScriptMapLayout.KeyOffset; + Ar << ScriptMapLayout.ValueOffset; + Ar << ScriptMapLayout.SetLayout; + + return Ar; +} + +FMapPropertyArchiveProxy::FMapPropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UMapProperty* MapProperty) + : FPropertyArchiveProxy(UHTMakefile, MapProperty) +{ + KeyPropIndex = UHTMakefile.GetPropertyIndex(MapProperty->KeyProp); + ValuePropIndex = UHTMakefile.GetPropertyIndex(MapProperty->ValueProp); + MapLayout = MapProperty->MapLayout; +} + +UMapProperty* FMapPropertyArchiveProxy::CreateMapProperty(const FUHTMakefile& UHTMakefile) const +{ + UObject* Outer = UHTMakefile.GetObjectByIndex(OuterIndex); + UMapProperty* MapProperty = new (EC_InternalUseOnlyConstructor, Outer, Name.CreateName(UHTMakefile), (EObjectFlags)ObjectFlagsUint32) UMapProperty(FObjectInitializer()); + PostConstruct(MapProperty, UHTMakefile); + return MapProperty; +} + +void FMapPropertyArchiveProxy::Resolve(UMapProperty* MapProperty, const FUHTMakefile& UHTMakefile) const +{ + FPropertyArchiveProxy::Resolve(MapProperty, UHTMakefile); + MapProperty->KeyProp = UHTMakefile.GetPropertyByIndex(KeyPropIndex); + MapProperty->ValueProp = UHTMakefile.GetPropertyByIndex(ValuePropIndex); + MapProperty->MapLayout = MapLayout; +} + +UArrayProperty* FArrayPropertyArchiveProxy::CreateArrayProperty(const FUHTMakefile& UHTMakefile) const +{ + UObject* Outer = UHTMakefile.GetObjectByIndex(OuterIndex); + UArrayProperty* ArrayProperty = new (EC_InternalUseOnlyConstructor, Outer, Name.CreateName(UHTMakefile), (EObjectFlags)ObjectFlagsUint32) UArrayProperty(FObjectInitializer()); + PostConstruct(ArrayProperty, UHTMakefile); + return ArrayProperty; +} + +void FArrayPropertyArchiveProxy::Resolve(UArrayProperty* ArrayProperty, const FUHTMakefile& UHTMakefile) const +{ + FPropertyArchiveProxy::Resolve(ArrayProperty, UHTMakefile); + ArrayProperty->Inner = UHTMakefile.GetPropertyByIndex(InnerIndex); +} + +FArrayPropertyArchiveProxy::FArrayPropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UArrayProperty* ArrayProperty) + : FPropertyArchiveProxy(UHTMakefile, ArrayProperty) +{ + InnerIndex = UHTMakefile.GetPropertyIndex(ArrayProperty->Inner); +} diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/PropertyArchiveProxy.h b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/PropertyArchiveProxy.h new file mode 100644 index 000000000000..ee829f8ed193 --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/PropertyArchiveProxy.h @@ -0,0 +1,420 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#pragma once +#include "UHTMakefile/FieldArchiveProxy.h" + + +/* See UHTMakefile.h for overview how makefiles work. */ +struct FPropertyArchiveProxy : public FFieldArchiveProxy +{ + FPropertyArchiveProxy() { } + FPropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UProperty* Property); + + UProperty* CreateProperty(const FUHTMakefile& UHTMakefile) const; + void Resolve(UProperty* Property, const FUHTMakefile& UHTMakefile) const; + + friend FArchive& operator<<(FArchive& Ar, FPropertyArchiveProxy& PropertyArchiveProxy); + + static void AddReferencedNames(const UProperty* Property, FUHTMakefile& UHTMakefile); + void PostConstruct(UProperty* Property, const FUHTMakefile& UHTMakefile) const; + int32 ArrayDim; + int32 ElementSize; + uint64 PropertyFlags; + uint16 RepIndex; + FNameArchiveProxy RepNotifyFunc; + int32 Offset_Internal; + int32 PropertyLinkNextIndex; + int32 NextRefIndex; + int32 DestructorLinkNextIndex; + int32 PostConstructLinkNextIndex; +}; + +struct FBytePropertyArchiveProxy : public FPropertyArchiveProxy +{ + FBytePropertyArchiveProxy() { } + FBytePropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UByteProperty* ByteProperty); + + UByteProperty* CreateByteProperty(const FUHTMakefile& UHTMakefile) const; + + void Resolve(UByteProperty* ByteProperty, const FUHTMakefile& UHTMakefile) const; + static void AddReferencedNames(const UByteProperty* Property, FUHTMakefile& UHTMakefile) + { + FPropertyArchiveProxy::AddReferencedNames(Property, UHTMakefile); + } + + friend FArchive& operator<<(FArchive& Ar, FBytePropertyArchiveProxy& BytePropertyArchiveProxy); + + int32 EnumIndex; +}; + +struct FInt8PropertyArchiveProxy : public FPropertyArchiveProxy +{ + FInt8PropertyArchiveProxy() { } + FInt8PropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UInt8Property* Int8Property); + + UInt8Property* CreateInt8Property(const FUHTMakefile& UHTMakefile) const; + static void AddReferencedNames(const UInt8Property* Property, FUHTMakefile& UHTMakefile) + { + FPropertyArchiveProxy::AddReferencedNames(Property, UHTMakefile); + } +}; + +struct FInt16PropertyArchiveProxy : public FPropertyArchiveProxy +{ + FInt16PropertyArchiveProxy() { } + FInt16PropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UInt16Property* Int16Property); + + UInt16Property* CreateInt16Property(const FUHTMakefile& UHTMakefile) const; + static void AddReferencedNames(const UInt16Property* Property, FUHTMakefile& UHTMakefile) + { + FPropertyArchiveProxy::AddReferencedNames(Property, UHTMakefile); + } +}; + +struct FIntPropertyArchiveProxy : public FPropertyArchiveProxy +{ + FIntPropertyArchiveProxy() { } + FIntPropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UIntProperty* IntProperty); + + UIntProperty* CreateIntProperty(const FUHTMakefile& UHTMakefile) const; + static void AddReferencedNames(const UIntProperty* Property, FUHTMakefile& UHTMakefile) + { + FPropertyArchiveProxy::AddReferencedNames(Property, UHTMakefile); + } +}; + +struct FInt64PropertyArchiveProxy : public FPropertyArchiveProxy +{ + FInt64PropertyArchiveProxy() { } + FInt64PropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UInt64Property* Int64Property); + + UInt64Property* CreateInt64Property(const FUHTMakefile& UHTMakefile) const; + static void AddReferencedNames(const UInt64Property* Property, FUHTMakefile& UHTMakefile) + { + FPropertyArchiveProxy::AddReferencedNames(Property, UHTMakefile); + } +}; + +struct FUInt16PropertyArchiveProxy : public FPropertyArchiveProxy +{ + FUInt16PropertyArchiveProxy() { } + FUInt16PropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UUInt16Property* UInt16Property); + + UUInt16Property* CreateUInt16Property(const FUHTMakefile& UHTMakefile) const; + static void AddReferencedNames(const UUInt16Property* Property, FUHTMakefile& UHTMakefile) + { + FPropertyArchiveProxy::AddReferencedNames(Property, UHTMakefile); + } +}; + +struct FUInt32PropertyArchiveProxy : public FPropertyArchiveProxy +{ + FUInt32PropertyArchiveProxy() { } + FUInt32PropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UUInt32Property* UInt32Property); + + UUInt32Property* CreateUInt32Property(const FUHTMakefile& UHTMakefile) const; + static void AddReferencedNames(const UUInt32Property* Property, FUHTMakefile& UHTMakefile) + { + FPropertyArchiveProxy::AddReferencedNames(Property, UHTMakefile); + } +}; + +struct FUInt64PropertyArchiveProxy : public FPropertyArchiveProxy +{ + FUInt64PropertyArchiveProxy() { } + FUInt64PropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UUInt64Property* UInt64Property); + + UUInt64Property* CreateUInt64Property(const FUHTMakefile& UHTMakefile) const; + static void AddReferencedNames(const UUInt64Property* Property, FUHTMakefile& UHTMakefile) + { + FPropertyArchiveProxy::AddReferencedNames(Property, UHTMakefile); + } +}; + +struct FFloatPropertyArchiveProxy : public FPropertyArchiveProxy +{ + FFloatPropertyArchiveProxy() { } + FFloatPropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UFloatProperty* FloatProperty); + + UFloatProperty* CreateFloatProperty(const FUHTMakefile& UHTMakefile) const; + static void AddReferencedNames(const UFloatProperty* Property, FUHTMakefile& UHTMakefile) + { + FPropertyArchiveProxy::AddReferencedNames(Property, UHTMakefile); + } +}; + +struct FDoublePropertyArchiveProxy : public FPropertyArchiveProxy +{ + FDoublePropertyArchiveProxy() { } + FDoublePropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UDoubleProperty* DoubleProperty); + + UDoubleProperty* CreateDoubleProperty(const FUHTMakefile& UHTMakefile) const; + static void AddReferencedNames(const UDoubleProperty* Property, FUHTMakefile& UHTMakefile) + { + FPropertyArchiveProxy::AddReferencedNames(Property, UHTMakefile); + } +}; + +struct FBoolPropertyArchiveProxy : public FPropertyArchiveProxy +{ + FBoolPropertyArchiveProxy() { } + FBoolPropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UBoolProperty* BoolProperty); + + UBoolProperty* CreateBoolProperty(const FUHTMakefile& UHTMakefile) const; + + void PostConstruct(UBoolProperty* BoolProperty, const FUHTMakefile& UHTMakefile) const; + + static void AddReferencedNames(const UBoolProperty* Property, FUHTMakefile& UHTMakefile) + { + FPropertyArchiveProxy::AddReferencedNames(Property, UHTMakefile); + } + + friend FArchive& operator<<(FArchive& Ar, FBytePropertyArchiveProxy& BytePropertyArchiveProxy); + + uint8 FieldSize; + uint8 ByteOffset; + uint8 ByteMask; + uint8 FieldMask; +}; + +struct FNamePropertyArchiveProxy : public FPropertyArchiveProxy +{ + FNamePropertyArchiveProxy() { } + FNamePropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UNameProperty* NameProperty); + + UNameProperty* CreateNameProperty(const FUHTMakefile& UHTMakefile) const; + static void AddReferencedNames(const UNameProperty* Property, FUHTMakefile& UHTMakefile) + { + FPropertyArchiveProxy::AddReferencedNames(Property, UHTMakefile); + } +}; + +struct FStrPropertyArchiveProxy : public FPropertyArchiveProxy +{ + FStrPropertyArchiveProxy::FStrPropertyArchiveProxy() { } + FStrPropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UStrProperty* StrProperty); + static void AddReferencedNames(const UStrProperty* Property, FUHTMakefile& UHTMakefile) + { + FPropertyArchiveProxy::AddReferencedNames(Property, UHTMakefile); + } + + UStrProperty* CreateStrProperty(const FUHTMakefile& UHTMakefile) const; +}; + +struct FTextPropertyArchiveProxy : public FPropertyArchiveProxy +{ + FTextPropertyArchiveProxy::FTextPropertyArchiveProxy() { } + FTextPropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UTextProperty* TextProperty); + + UTextProperty* CreateTextProperty(const FUHTMakefile& UHTMakefile) const; + static void AddReferencedNames(const UTextProperty* Property, FUHTMakefile& UHTMakefile) + { + FPropertyArchiveProxy::AddReferencedNames(Property, UHTMakefile); + } +}; + +struct FDelegatePropertyArchiveProxy : public FPropertyArchiveProxy +{ + FDelegatePropertyArchiveProxy::FDelegatePropertyArchiveProxy() { } + FDelegatePropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UDelegateProperty* DelegateProperty); + + UDelegateProperty* CreateDelegateProperty(const FUHTMakefile& UHTMakefile) const; + void Resolve(UDelegateProperty* DelegateProperty, const FUHTMakefile& UHTMakefile); + static void AddReferencedNames(const UDelegateProperty* Property, FUHTMakefile& UHTMakefile); + + int32 SignatureFunctionIndex; +}; + +struct FMulticastDelegatePropertyArchiveProxy : public FPropertyArchiveProxy +{ + FMulticastDelegatePropertyArchiveProxy::FMulticastDelegatePropertyArchiveProxy() { } + FMulticastDelegatePropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UMulticastDelegateProperty* MulticastDelegateProperty); + + UMulticastDelegateProperty* CreateMulticastDelegateProperty(const FUHTMakefile& UHTMakefile) const; + static void AddReferencedNames(const UMulticastDelegateProperty* Property, FUHTMakefile& UHTMakefile) + { + FPropertyArchiveProxy::AddReferencedNames(Property, UHTMakefile); + } +}; + +struct FObjectPropertyBaseArchiveProxy : public FPropertyArchiveProxy +{ + FObjectPropertyBaseArchiveProxy::FObjectPropertyBaseArchiveProxy() { } + FObjectPropertyBaseArchiveProxy(FUHTMakefile& UHTMakefile, const UObjectPropertyBase* ObjectPropertyBase); + + UObjectPropertyBase* CreateObjectPropertyBase(const FUHTMakefile& UHTMakefile) const; + + void Resolve(UObjectPropertyBase* ObjectPropertyBase, const FUHTMakefile& UHTMakefile) const; + static void AddReferencedNames(const UObjectPropertyBase* Property, FUHTMakefile& UHTMakefile) + { + FPropertyArchiveProxy::AddReferencedNames(Property, UHTMakefile); + } + + friend FArchive& operator<<(FArchive& Ar, FObjectPropertyBaseArchiveProxy& ObjectPropertyBaseArchiveProxy); + + FSerializeIndex PropertyClassIndex; +}; + +struct FClassPropertyArchiveProxy : public FObjectPropertyBaseArchiveProxy +{ + FClassPropertyArchiveProxy::FClassPropertyArchiveProxy() { } + FClassPropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UClassProperty* ClassProperty); + + UClassProperty* CreateClassProperty(const FUHTMakefile& UHTMakefile) const; + + void Resolve(UClassProperty* ClassProperty, const FUHTMakefile& UHTMakefile) const; + static void AddReferencedNames(const UClassProperty* Property, FUHTMakefile& UHTMakefile) + { + FPropertyArchiveProxy::AddReferencedNames(Property, UHTMakefile); + } + + friend FArchive& operator<<(FArchive& Ar, FClassPropertyArchiveProxy& ClassPropertyArchiveProxy); + + FSerializeIndex MetaClassIndex; +}; + +struct FObjectPropertyArchiveProxy : public FObjectPropertyBaseArchiveProxy +{ + FObjectPropertyArchiveProxy::FObjectPropertyArchiveProxy() { } + FObjectPropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UObjectProperty* ObjectProperty); + static void AddReferencedNames(const UObjectProperty* Property, FUHTMakefile& UHTMakefile) + { + FPropertyArchiveProxy::AddReferencedNames(Property, UHTMakefile); + } + + UObjectProperty* CreateObjectProperty(const FUHTMakefile& UHTMakefile) const; +}; + +struct FWeakObjectPropertyArchiveProxy : public FObjectPropertyBaseArchiveProxy +{ + FWeakObjectPropertyArchiveProxy::FWeakObjectPropertyArchiveProxy() { } + FWeakObjectPropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UWeakObjectProperty* WeakObjectProperty); + + UWeakObjectProperty* CreateWeakObjectProperty(const FUHTMakefile& UHTMakefile) const; + static void AddReferencedNames(const UWeakObjectProperty* Property, FUHTMakefile& UHTMakefile) + { + FPropertyArchiveProxy::AddReferencedNames(Property, UHTMakefile); + } +}; + +struct FLazyObjectPropertyArchiveProxy : public FObjectPropertyBaseArchiveProxy +{ + FLazyObjectPropertyArchiveProxy::FLazyObjectPropertyArchiveProxy() { } + FLazyObjectPropertyArchiveProxy(FUHTMakefile& UHTMakefile, const ULazyObjectProperty* LazyObjectProperty); + + ULazyObjectProperty* CreateLazyObjectProperty(const FUHTMakefile& UHTMakefile) const; + static void AddReferencedNames(const ULazyObjectProperty* Property, FUHTMakefile& UHTMakefile) + { + FPropertyArchiveProxy::AddReferencedNames(Property, UHTMakefile); + } +}; + +struct FAssetObjectPropertyArchiveProxy : public FObjectPropertyBaseArchiveProxy +{ + FAssetObjectPropertyArchiveProxy::FAssetObjectPropertyArchiveProxy() { } + FAssetObjectPropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UAssetObjectProperty* AssetObjectProperty); + + UAssetObjectProperty* CreateAssetObjectProperty(const FUHTMakefile& UHTMakefile) const; + static void AddReferencedNames(const UAssetObjectProperty* Property, FUHTMakefile& UHTMakefile) + { + FPropertyArchiveProxy::AddReferencedNames(Property, UHTMakefile); + } +}; + +struct FAssetClassPropertyArchiveProxy : public FObjectPropertyBaseArchiveProxy +{ + FAssetClassPropertyArchiveProxy::FAssetClassPropertyArchiveProxy() { } + FAssetClassPropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UAssetClassProperty* AssetClassProperty); + + UAssetClassProperty* CreateAssetClassProperty(const FUHTMakefile& UHTMakefile) const; + + void Resolve(UAssetClassProperty* AssetClassProperty, const FUHTMakefile& UHTMakefile) const; + static void AddReferencedNames(const UAssetClassProperty* Property, FUHTMakefile& UHTMakefile) + { + FPropertyArchiveProxy::AddReferencedNames(Property, UHTMakefile); + } + + friend FArchive& operator<<(FArchive& Ar, FAssetClassPropertyArchiveProxy& AssetClassPropertyArchiveProxy); + + FSerializeIndex MetaClassIndex; +}; + +struct FInterfacePropertyArchiveProxy : public FPropertyArchiveProxy +{ + FInterfacePropertyArchiveProxy::FInterfacePropertyArchiveProxy() { } + FInterfacePropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UInterfaceProperty* InterfaceProperty); + + UInterfaceProperty* CreateInterfaceProperty(const FUHTMakefile& UHTMakefile) const; + static void AddReferencedNames(const UInterfaceProperty* Property, FUHTMakefile& UHTMakefile) + { + FPropertyArchiveProxy::AddReferencedNames(Property, UHTMakefile); + } + + void Resolve(UInterfaceProperty* InterfaceProperty, const FUHTMakefile& UHTMakefile) const; + + friend FArchive& operator<<(FArchive& Ar, FInterfacePropertyArchiveProxy& InterfacePropertyArchiveProxy); + + FSerializeIndex InterfaceClassIndex; +}; + +struct FStructPropertyArchiveProxy : public FPropertyArchiveProxy +{ + FStructPropertyArchiveProxy::FStructPropertyArchiveProxy() { } + FStructPropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UStructProperty* StructProperty); + + UStructProperty* CreateStructProperty(const FUHTMakefile& UHTMakefile) const; + static void AddReferencedNames(const UStructProperty* Property, FUHTMakefile& UHTMakefile) + { + FPropertyArchiveProxy::AddReferencedNames(Property, UHTMakefile); + } + + void Resolve(UStructProperty* StructProperty, const FUHTMakefile& UHTMakefile) const; + + friend FArchive& operator<<(FArchive& Ar, FStructPropertyArchiveProxy& StructPropertyArchiveProxy); + + FSerializeIndex StructIndex; +}; + +FArchive& operator<<(FArchive& Ar, FScriptSparseArrayLayout& ScriptSparseArrayLayout);; + +FArchive& operator<<(FArchive& Ar, FScriptSetLayout& ScriptSetLayout);; + +FArchive& operator<<(FArchive& Ar, FScriptMapLayout& ScriptMapLayout); + +struct FMapPropertyArchiveProxy : public FPropertyArchiveProxy +{ + FMapPropertyArchiveProxy::FMapPropertyArchiveProxy() { } + FMapPropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UMapProperty* MapProperty); + + UMapProperty* CreateMapProperty(const FUHTMakefile& UHTMakefile) const; + + void Resolve(UMapProperty* MapProperty, const FUHTMakefile& UHTMakefile) const; + static void AddReferencedNames(const UMapProperty* Property, FUHTMakefile& UHTMakefile) + { + FPropertyArchiveProxy::AddReferencedNames(Property, UHTMakefile); + } + + friend FArchive& operator<<(FArchive& Ar, FMapPropertyArchiveProxy& MapPropertyArchiveProxy); + + int32 KeyPropIndex; + int32 ValuePropIndex; + FScriptMapLayout MapLayout; +}; + +struct FArrayPropertyArchiveProxy : public FPropertyArchiveProxy +{ + FArrayPropertyArchiveProxy::FArrayPropertyArchiveProxy() { } + FArrayPropertyArchiveProxy(FUHTMakefile& UHTMakefile, const UArrayProperty* ArrayProperty); + + UArrayProperty* CreateArrayProperty(const FUHTMakefile& UHTMakefile) const; + static void AddReferencedNames(const UArrayProperty* Property, FUHTMakefile& UHTMakefile) + { + FPropertyArchiveProxy::AddReferencedNames(Property, UHTMakefile); + } + + void Resolve(UArrayProperty* ArrayProperty, const FUHTMakefile& UHTMakefile) const; + + friend FArchive& operator<<(FArchive& Ar, FArrayPropertyArchiveProxy& ArrayPropertyArchiveProxy); + + int32 InnerIndex; +}; diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/PropertyBaseArchiveProxy.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/PropertyBaseArchiveProxy.cpp new file mode 100644 index 000000000000..7ab3c72cf1f3 --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/PropertyBaseArchiveProxy.cpp @@ -0,0 +1,154 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#pragma once +#include "UnrealHeaderTool.h" +#include "UHTMakefile/UHTMakefile.h" +#include "UHTMakefile/PropertyBaseArchiveProxy.h" +#include "ParserHelper.h" + +FPropertyBaseArchiveProxy::FPropertyBaseArchiveProxy(const FUHTMakefile& UHTMakefile, const FPropertyBase* PropertyBase) +{ + Type = PropertyBase->Type; + ArrayType = PropertyBase->ArrayType; + PropertyFlags = PropertyBase->PropertyFlags; + ImpliedPropertyFlags = PropertyBase->ImpliedPropertyFlags; + RefQualifier = PropertyBase->RefQualifier; + MapKeyPropIndex = UHTMakefile.GetPropertyBaseIndex(PropertyBase->MapKeyProp.Get()); + PropertyExportFlags = PropertyBase->PropertyExportFlags; + + switch (Type) + { + case CPT_Byte: + EnumIndex = UHTMakefile.GetEnumIndex(PropertyBase->Enum); + break; + case CPT_ObjectReference: + case CPT_WeakObjectReference: + case CPT_LazyObjectReference: + case CPT_AssetObjectReference: + PropertyClassIndex = UHTMakefile.GetClassIndex(PropertyBase->PropertyClass); + break; + case CPT_Delegate: + case CPT_MulticastDelegate: + FunctionIndex = UHTMakefile.GetFunctionIndex(PropertyBase->Function); + break; + case CPT_Struct: + StructIndex = UHTMakefile.GetScriptStructIndex(PropertyBase->Struct); + break; + default: + break; + } + + MetaClassIndex = UHTMakefile.GetClassIndex(PropertyBase->MetaClass); + DelegateName = FNameArchiveProxy(UHTMakefile, PropertyBase->DelegateName); + RepNotifyName = FNameArchiveProxy(UHTMakefile, PropertyBase->RepNotifyName);; + ExportInfo = PropertyBase->ExportInfo; + MetaData.Empty(PropertyBase->MetaData.Num()); + for (auto& Kvp : PropertyBase->MetaData) + { + MetaData.Add(TPairInitializer(FNameArchiveProxy(UHTMakefile, Kvp.Key), Kvp.Value)); + } + PointerType = PropertyBase->PointerType; +} + +void FPropertyBaseArchiveProxy::AddReferencedNames(const FPropertyBase* PropertyBase, FUHTMakefile& UHTMakefile) +{ + UHTMakefile.AddName(PropertyBase->DelegateName); + UHTMakefile.AddName(PropertyBase->RepNotifyName); +} + +FPropertyBase* FPropertyBaseArchiveProxy::CreatePropertyBase(const FUHTMakefile& UHTMakefile) const +{ + FPropertyBase* PropertyBase = new FPropertyBase(Type); + PostConstruct(PropertyBase); + return PropertyBase; +} + +void FPropertyBaseArchiveProxy::PostConstruct(FPropertyBase* PropertyBase) const +{ + PropertyBase->Type = Type; + PropertyBase->ArrayType = ArrayType; + PropertyBase->PropertyFlags = PropertyFlags; + PropertyBase->ImpliedPropertyFlags = ImpliedPropertyFlags; + PropertyBase->RefQualifier = RefQualifier; + PropertyBase->PropertyExportFlags = PropertyExportFlags; + PropertyBase->ExportInfo = ExportInfo; + PropertyBase->PointerType = PointerType; +} + +void FPropertyBaseArchiveProxy::Resolve(FPropertyBase* PropertyBase, const FUHTMakefile& UHTMakefile) const +{ + FPropertyBase* MapKeyProp = UHTMakefile.GetPropertyBaseByIndex(MapKeyPropIndex); + PropertyBase->MapKeyProp = MapKeyProp && MapKeyProp->HasBeenAlreadyMadeSharable() ? MapKeyProp->AsShared() : TSharedPtr(MapKeyProp); + + switch (PropertyBase->Type) + { + case CPT_Byte: + PropertyBase->Enum = UHTMakefile.GetEnumByIndex(EnumIndex); + break; + case CPT_ObjectReference: + case CPT_WeakObjectReference: + case CPT_LazyObjectReference: + case CPT_AssetObjectReference: + PropertyBase->PropertyClass = UHTMakefile.GetClassByIndex(PropertyClassIndex); + break; + case CPT_Delegate: + case CPT_MulticastDelegate: + PropertyBase->Function = UHTMakefile.GetFunctionByIndex(FunctionIndex); + break; + case CPT_Struct: + PropertyBase->Struct = UHTMakefile.GetScriptStructByIndex(StructIndex); + break; + default: + break; + } + + PropertyBase->MetaClass = UHTMakefile.GetClassByIndex(MetaClassIndex); + PropertyBase->DelegateName = DelegateName.CreateName(UHTMakefile); + PropertyBase->RepNotifyName = RepNotifyName.CreateName(UHTMakefile); + PropertyBase->MetaData.Empty(MetaData.Num()); + for (auto& Kvp : MetaData) + { + PropertyBase->MetaData.Add(Kvp.Key.CreateName(UHTMakefile), Kvp.Value); + } +} + +FArchive& operator<<(FArchive& Ar, FPropertyBaseArchiveProxy& PropertyBaseArchiveProxy) +{ + Ar << PropertyBaseArchiveProxy.Type; + Ar << PropertyBaseArchiveProxy.ArrayType; + Ar << PropertyBaseArchiveProxy.PropertyFlags; + Ar << PropertyBaseArchiveProxy.ImpliedPropertyFlags; + Ar << PropertyBaseArchiveProxy.RefQualifier; + Ar << PropertyBaseArchiveProxy.MapKeyPropIndex; + Ar << PropertyBaseArchiveProxy.PropertyExportFlags; + Ar << PropertyBaseArchiveProxy.MetaClassIndex; + Ar << PropertyBaseArchiveProxy.DelegateName; + Ar << PropertyBaseArchiveProxy.RepNotifyName; + Ar << PropertyBaseArchiveProxy.ExportInfo; + Ar << PropertyBaseArchiveProxy.MetaData; + Ar << PropertyBaseArchiveProxy.PointerType; + + switch (PropertyBaseArchiveProxy.Type) + { + case CPT_Byte: + Ar << PropertyBaseArchiveProxy.EnumIndex; + break; + case CPT_ObjectReference: + case CPT_WeakObjectReference: + case CPT_LazyObjectReference: + case CPT_AssetObjectReference: + Ar << PropertyBaseArchiveProxy.PropertyClassIndex; + break; + case CPT_Delegate: + case CPT_MulticastDelegate: + Ar << PropertyBaseArchiveProxy.FunctionIndex; + break; + case CPT_Struct: + Ar << PropertyBaseArchiveProxy.StructIndex; + break; + default: + break; + } + + return Ar; +} diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/PropertyBaseArchiveProxy.h b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/PropertyBaseArchiveProxy.h new file mode 100644 index 000000000000..a075ea78d3ca --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/PropertyBaseArchiveProxy.h @@ -0,0 +1,135 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. +#pragma once +#include "ParserHelper.h" +/* See UHTMakefile.h for overview how makefiles work. */ + +class FPropertyBase; +class FUHTMakefile; + +inline FArchive& operator<<(FArchive& Ar, EPropertyType& PropertyType) +{ + if (Ar.IsLoading()) + { + int32 Value; + Ar << Value; + PropertyType = (EPropertyType)Value; + } + else if (Ar.IsSaving()) + { + int32 Value = (int32)PropertyType; + Ar << Value; + } + + return Ar; +} + +inline FArchive& operator<<(FArchive& Ar, ETokenType& TokenType) +{ + if (Ar.IsLoading()) + { + int32 Value; + Ar << Value; + TokenType = (ETokenType)Value; + } + else if (Ar.IsSaving()) + { + int32 Value = (int32)TokenType; + Ar << Value; + } + + return Ar; +} + +inline FArchive& operator<<(FArchive& Ar, EArrayType::Type& ArrayType) +{ + if (Ar.IsLoading()) + { + int32 Value; + Ar << Value; + ArrayType = (EArrayType::Type)Value; + } + else if (Ar.IsSaving()) + { + int32 Value = (int32)ArrayType; + Ar << Value; + } + + return Ar; +} + +inline FArchive& operator<<(FArchive& Ar, ERefQualifier::Type& ArrayType) +{ + if (Ar.IsLoading()) + { + int32 Value; + Ar << Value; + ArrayType = (ERefQualifier::Type)Value; + } + else if (Ar.IsSaving()) + { + int32 Value = (int32)ArrayType; + Ar << Value; + } + + return Ar; +} + +inline FArchive& operator<<(FArchive& Ar, EPointerType::Type& ArrayType) +{ + if (Ar.IsLoading()) + { + int32 Value; + Ar << Value; + ArrayType = (EPointerType::Type)Value; + } + else if (Ar.IsSaving()) + { + int32 Value = (int32)ArrayType; + Ar << Value; + } + + return Ar; +} + +struct FPropertyBaseArchiveProxy +{ + FPropertyBaseArchiveProxy(const FUHTMakefile& UHTMakefile, const FPropertyBase* PropertyBase); + FPropertyBaseArchiveProxy() { } + + static void AddReferencedNames(const FPropertyBase* PropertyBase, FUHTMakefile& UHTMakefile); + + friend FArchive& operator<<(FArchive& Ar, FPropertyBaseArchiveProxy& PropertyBaseArchiveProxy); + FPropertyBase* CreatePropertyBase(const FUHTMakefile& UHTMakefile) const; + + void PostConstruct(FPropertyBase* PropertyBase) const; + + void Resolve(FPropertyBase* PropertyBase, const FUHTMakefile& UHTMakefile) const; + + EPropertyType Type; + EArrayType::Type ArrayType; + uint64 PropertyFlags; + uint64 ImpliedPropertyFlags; + ERefQualifier::Type RefQualifier; + int32 MapKeyPropIndex; + + uint32 PropertyExportFlags; + FSerializeIndex PropertyClassIndex; + FSerializeIndex StructIndex; + union + { + int32 EnumIndex; + int32 FunctionIndex; +#if PLATFORM_64BITS + int64 StringSize; +#else + int32 StringSize; +#endif + }; + + FSerializeIndex MetaClassIndex; + FNameArchiveProxy DelegateName; + FNameArchiveProxy RepNotifyName; + FString ExportInfo; + TArray> MetaData; + EPointerType::Type PointerType; +}; diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/PropertyDataArchiveProxy.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/PropertyDataArchiveProxy.cpp new file mode 100644 index 000000000000..4346ff3c13ad --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/PropertyDataArchiveProxy.cpp @@ -0,0 +1,40 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. +#include "UnrealHeaderTool.h" +#include "UHTMakefile/UHTMakefile.h" +#include "UHTMakefile/PropertyDataArchiveProxy.h" +#include "UHTMakefile/TokenDataArchiveProxy.h" + +FPropertyDataArchiveProxy::FPropertyDataArchiveProxy(const FUHTMakefile& UHTMakefile, const FPropertyData* PropertyData) +{ + for (const auto& Kvp : *PropertyData) + { + EntriesIndexes.Add(UHTMakefile.GetPropertyDataEntryIndex(&Kvp)); + } +} + +void FPropertyDataArchiveProxy::AddReferencedNames(const FPropertyData* PropertyData, FUHTMakefile& UHTMakefile) +{ } + +FArchive& operator<<(FArchive& Ar, FPropertyDataArchiveProxy& PropertyDataArchiveProxy) +{ + Ar << PropertyDataArchiveProxy.EntriesIndexes; + + return Ar; +} + +FPropertyData* FPropertyDataArchiveProxy::CreatePropertyData(const FUHTMakefile& UHTMakefile) const +{ + return new FPropertyData(); +} + +void FPropertyDataArchiveProxy::Resolve(FPropertyData* PropertyData, FUHTMakefile& UHTMakefile) +{ + PropertyData->Empty(EntriesIndexes.Num()); + for (int32 Index : EntriesIndexes) + { + const TPair>* Entry = UHTMakefile.GetPropertyDataEntryByIndex(Index); + UHTMakefile.ResolvePropertyDataEntry(Index); + PropertyData->Add(Entry->Key, Entry->Value); + } +} + diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/PropertyDataArchiveProxy.h b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/PropertyDataArchiveProxy.h new file mode 100644 index 000000000000..f30fb5fb0abb --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/PropertyDataArchiveProxy.h @@ -0,0 +1,22 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#pragma once +#include "UHTMakefile/TokenDataArchiveProxy.h" + +class FUHTMakefile; +class FPropertyData; + +/* See UHTMakefile.h for overview how makefiles work. */ +struct FPropertyDataArchiveProxy +{ + FPropertyDataArchiveProxy(const FUHTMakefile& UHTMakefile, const FPropertyData* PropertyData); + FPropertyDataArchiveProxy() { } + + static void AddReferencedNames(const FPropertyData* PropertyData, FUHTMakefile& UHTMakefile); + + friend FArchive& operator<<(FArchive& Ar, FPropertyDataArchiveProxy& PropertyDataArchiveProxy); + FPropertyData* CreatePropertyData(const FUHTMakefile& UHTMakefile) const; + void Resolve(FPropertyData* PropertyData, FUHTMakefile& UHTMakefile); + + TArray EntriesIndexes; +}; diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/RepRecordArchiveProxy.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/RepRecordArchiveProxy.cpp new file mode 100644 index 000000000000..7b97bfcb1b74 --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/RepRecordArchiveProxy.cpp @@ -0,0 +1,31 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#pragma once +#include "UnrealHeaderTool.h" +#include "UHTMakefile/UHTMakefile.h" +#include "UHTMakefile/RepRecordArchiveProxy.h" + +FRepRecordArchiveProxy::FRepRecordArchiveProxy(const FUHTMakefile& UHTMakefile, const FRepRecord& RepRecord) +{ + PropertyIndex = UHTMakefile.GetPropertyIndex(RepRecord.Property); + Index = RepRecord.Index; +} + +FRepRecord FRepRecordArchiveProxy::CreateRepRecord(const FUHTMakefile& UHTMakefile) const +{ + return FRepRecord(nullptr, Index); +} + +FArchive& operator<<(FArchive& Ar, FRepRecordArchiveProxy& RepRecordArchiveProxy) +{ + Ar << RepRecordArchiveProxy.PropertyIndex; + Ar << RepRecordArchiveProxy.Index; + + return Ar; +} + +void FRepRecordArchiveProxy::Resolve(FRepRecord& RepRecord, const FUHTMakefile& UHTMakefile) const +{ + RepRecord.Property = UHTMakefile.GetPropertyByIndex(PropertyIndex); +} + diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/RepRecordArchiveProxy.h b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/RepRecordArchiveProxy.h new file mode 100644 index 000000000000..ad32570e5f4a --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/RepRecordArchiveProxy.h @@ -0,0 +1,19 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#pragma once + +/* See UHTMakefile.h for overview how makefiles work. */ +struct FRepRecordArchiveProxy +{ + FRepRecordArchiveProxy() { } + FRepRecordArchiveProxy(const FUHTMakefile& UHTMakefile, const FRepRecord& RepRecord); + + int32 PropertyIndex; + int32 Index; + + FRepRecord CreateRepRecord(const FUHTMakefile& UHTMakefile) const; + + friend FArchive& operator<<(FArchive& Ar, FRepRecordArchiveProxy& RepRecordArchiveProxy); + + void Resolve(FRepRecord& RepRecord, const FUHTMakefile& UHTMakefile) const; +}; diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/ScopeArchiveProxy.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/ScopeArchiveProxy.cpp new file mode 100644 index 000000000000..f80f7115657e --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/ScopeArchiveProxy.cpp @@ -0,0 +1,42 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#include "UnrealHeaderTool.h" +#include "UHTMakefile/UHTMakefile.h" +#include "UHTMakefile/ScopeArchiveProxy.h" +#include "Scope.h" + +FArchive& operator<<(FArchive& Ar, FScopeArchiveProxy& ScopeArchiveProxy) +{ + Ar << ScopeArchiveProxy.ParentIndex; + Ar << ScopeArchiveProxy.TypeMap; + + return Ar; +} + +FScopeArchiveProxy::FScopeArchiveProxy(const FUHTMakefile& UHTMakefile, const FScope* Scope) +{ + ParentIndex = UHTMakefile.GetScopeIndex(Scope->GetParent()); + for (auto& Kvp : Scope->TypeMap) + { + TypeMap.Add(TPairInitializer(FNameArchiveProxy(UHTMakefile, Kvp.Key), UHTMakefile.GetFieldIndex(Kvp.Value))); + } +} + +void FScopeArchiveProxy::AddReferencedNames(const FScope* Scope, FUHTMakefile& UHTMakefile) +{ + for (auto& Kvp : Scope->TypeMap) + { + UHTMakefile.AddName(Kvp.Key); + } +} + +void FScopeArchiveProxy::Resolve(FScope* Scope, const FUHTMakefile& UHTMakefile) const +{ + Scope->Parent = UHTMakefile.GetScopeByIndex(ParentIndex); + for (auto& Kvp : TypeMap) + { + FName Name = Kvp.Key.CreateName(UHTMakefile); + UField* Field = UHTMakefile.GetFieldByIndex(Kvp.Value); + Scope->TypeMap.Add(Name, Field); + } +} diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/ScopeArchiveProxy.h b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/ScopeArchiveProxy.h new file mode 100644 index 000000000000..d02cbc583bc5 --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/ScopeArchiveProxy.h @@ -0,0 +1,21 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#pragma once + +class FUHTMakefile; +class FScope; + +/* See UHTMakefile.h for overview how makefiles work. */ +struct FScopeArchiveProxy +{ + FScopeArchiveProxy(const FUHTMakefile& UHTMakefile, const FScope* Scope); + FScopeArchiveProxy() { } + + static void AddReferencedNames(const FScope* Scope, FUHTMakefile& UHTMakefile); + + friend FArchive& operator<<(FArchive& Ar, FScopeArchiveProxy& ScopeArchiveProxy); + void Resolve(FScope* Scope, const FUHTMakefile& UHTMakefile) const; + + int32 ParentIndex; + TArray> TypeMap; +}; diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/ScriptStructArchiveProxy.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/ScriptStructArchiveProxy.cpp new file mode 100644 index 000000000000..66de973f409b --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/ScriptStructArchiveProxy.cpp @@ -0,0 +1,51 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#pragma once +#include "UnrealHeaderTool.h" +#include "UHTMakefile/UHTMakefile.h" +#include "UHTMakefile/StructArchiveProxy.h" + +FScriptStructArchiveProxy::FScriptStructArchiveProxy(FUHTMakefile& UHTMakefile, const UScriptStruct* ScriptStruct) + : FStructArchiveProxy(UHTMakefile, ScriptStruct) +{ + StructFlagsUInt32 = (uint32)ScriptStruct->StructFlags; + StructMacroDeclaredLineNumber = ScriptStruct->StructMacroDeclaredLineNumber; + bCppStructOpsFromBaseClass = ScriptStruct->bCppStructOpsFromBaseClass; + bPrepareCppStructOpsCompleted = ScriptStruct->bPrepareCppStructOpsCompleted; +} + +UScriptStruct* FScriptStructArchiveProxy::CreateScriptStruct(const FUHTMakefile& UHTMakefile) const +{ + UObject* Outer = Cast(UHTMakefile.GetObjectByIndex(OuterIndex)); + UScriptStruct* SuperStruct = UHTMakefile.GetScriptStructByIndex(SuperStructIndex); + FName StructName = Name.CreateName(UHTMakefile); + UScriptStruct* ScriptStruct = new (EC_InternalUseOnlyConstructor, Outer, Name.CreateName(UHTMakefile), (EObjectFlags)ObjectFlagsUint32) UScriptStruct(FObjectInitializer(), SuperStruct); + PostConstruct(ScriptStruct); + + + return ScriptStruct; +} + +void FScriptStructArchiveProxy::PostConstruct(UScriptStruct* ScriptStruct) const +{ + ScriptStruct->StructFlags = (EStructFlags)StructFlagsUInt32; + ScriptStruct->StructMacroDeclaredLineNumber = StructMacroDeclaredLineNumber; + ScriptStruct->bCppStructOpsFromBaseClass = bCppStructOpsFromBaseClass; + ScriptStruct->bPrepareCppStructOpsCompleted = bPrepareCppStructOpsCompleted; +} + +void FScriptStructArchiveProxy::Resolve(UScriptStruct* ScriptStruct, const FUHTMakefile& UHTMakefile) const +{ + FStructArchiveProxy::Resolve(ScriptStruct, UHTMakefile); +} + +FArchive& operator<<(FArchive& Ar, FScriptStructArchiveProxy& ScriptStructArchiveProxy) +{ + Ar << static_cast(ScriptStructArchiveProxy); + Ar << ScriptStructArchiveProxy.StructFlagsUInt32; + Ar << ScriptStructArchiveProxy.StructMacroDeclaredLineNumber; + Ar << ScriptStructArchiveProxy.bCppStructOpsFromBaseClass; + Ar << ScriptStructArchiveProxy.bPrepareCppStructOpsCompleted; + + return Ar; +} diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/ScriptStructArchiveProxy.h b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/ScriptStructArchiveProxy.h new file mode 100644 index 000000000000..8ef74f85b0c8 --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/ScriptStructArchiveProxy.h @@ -0,0 +1,28 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#pragma once +#include "UHTMakefile/StructArchiveProxy.h" + +/* See UHTMakefile.h for overview how makefiles work. */ +struct FScriptStructArchiveProxy : public FStructArchiveProxy +{ + FScriptStructArchiveProxy() { } + FScriptStructArchiveProxy(FUHTMakefile& UHTMakefile, const UScriptStruct* ScriptStruct); + + UScriptStruct* CreateScriptStruct(const FUHTMakefile& UHTMakefile) const; + + void PostConstruct(UScriptStruct* ScriptStruct) const; + + void Resolve(UScriptStruct* Struct, const FUHTMakefile& UHTMakefile) const; + + friend FArchive& operator<<(FArchive& Ar, FScriptStructArchiveProxy& StructArchiveProxy); + static void AddReferencedNames(UScriptStruct* ScriptStruct, FUHTMakefile& UHTMakefile) + { + FStructArchiveProxy::AddReferencedNames(ScriptStruct, UHTMakefile); + } + + uint32 StructFlagsUInt32; + int32 StructMacroDeclaredLineNumber; + bool bCppStructOpsFromBaseClass; + bool bPrepareCppStructOpsCompleted; +}; diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/StructArchiveProxy.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/StructArchiveProxy.cpp new file mode 100644 index 000000000000..acf3e9577104 --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/StructArchiveProxy.cpp @@ -0,0 +1,76 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#include "UnrealHeaderTool.h" +#include "UHTMakefile/UHTMakefile.h" +#include "UHTMakefile/StructArchiveProxy.h" + +FStructArchiveProxy::FStructArchiveProxy(FUHTMakefile& UHTMakefile, const UStruct* Struct) + : FFieldArchiveProxy(UHTMakefile, Struct) +{ + SuperStructIndex = UHTMakefile.GetStructIndex(Struct->SuperStruct); + ChildrenIndex = UHTMakefile.GetFieldIndex(Struct->Children); + PropertiesSize = Struct->PropertiesSize; + MinAlignment = Struct->PropertiesSize; + Script = Struct->Script; + PropertyLinkIndex = UHTMakefile.GetPropertyIndex(Struct->PropertyLink); + RefLinkIndex = UHTMakefile.GetPropertyIndex(Struct->RefLink); + DestructorLinkIndex = UHTMakefile.GetPropertyIndex(Struct->DestructorLink); + PostConstructLinkIndex = UHTMakefile.GetPropertyIndex(Struct->PostConstructLink); + ScriptObjectReferenceIndexes.Empty(Struct->ScriptObjectReferences.Num()); + for (UObject* ScriptObjectReference : Struct->ScriptObjectReferences) + { + ScriptObjectReferenceIndexes.Add(UHTMakefile.GetObjectIndex(ScriptObjectReference)); + } +} + +UStruct* FStructArchiveProxy::CreateStruct(const FUHTMakefile& UHTMakefile) const +{ + UObject* Outer = UHTMakefile.GetObjectByIndex(OuterIndex); + UStruct* Struct = new (EC_InternalUseOnlyConstructor, Outer, Name.CreateName(UHTMakefile), (EObjectFlags)ObjectFlagsUint32) UStruct(FObjectInitializer()); + PostConstruct(Struct); + + + return Struct; +} + +void FStructArchiveProxy::PostConstruct(UStruct* Struct) const +{ + Struct->PropertiesSize = PropertiesSize; + Struct->MinAlignment = MinAlignment; + Struct->Script = Script; +} + +void FStructArchiveProxy::Resolve(UStruct* Struct, const FUHTMakefile& UHTMakefile) const +{ + FFieldArchiveProxy::Resolve(Struct, UHTMakefile); + if (!Struct->IsA()) + { + Struct->SuperStruct = UHTMakefile.GetStructByIndex(SuperStructIndex); + } + Struct->Children = UHTMakefile.GetFieldByIndex(ChildrenIndex); + Struct->PropertyLink = UHTMakefile.GetPropertyByIndex(PropertyLinkIndex); + Struct->RefLink = UHTMakefile.GetPropertyByIndex(RefLinkIndex); + Struct->DestructorLink = UHTMakefile.GetPropertyByIndex(DestructorLinkIndex); + Struct->PostConstructLink = UHTMakefile.GetPropertyByIndex(PostConstructLinkIndex); + for (FSerializeIndex ScriptObjectReferenceIndex : ScriptObjectReferenceIndexes) + { + Struct->ScriptObjectReferences.Add(UHTMakefile.GetObjectByIndex(ScriptObjectReferenceIndex)); + } +} + +FArchive& operator<<(FArchive& Ar, FStructArchiveProxy& StructArchiveProxy) +{ + Ar << static_cast(StructArchiveProxy); + Ar << StructArchiveProxy.SuperStructIndex; + Ar << StructArchiveProxy.ChildrenIndex; + Ar << StructArchiveProxy.PropertiesSize; + Ar << StructArchiveProxy.MinAlignment; + Ar << StructArchiveProxy.Script; + Ar << StructArchiveProxy.PropertyLinkIndex; + Ar << StructArchiveProxy.RefLinkIndex; + Ar << StructArchiveProxy.DestructorLinkIndex; + Ar << StructArchiveProxy.PostConstructLinkIndex; + Ar << StructArchiveProxy.ScriptObjectReferenceIndexes; + + return Ar; +} diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/StructArchiveProxy.h b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/StructArchiveProxy.h new file mode 100644 index 000000000000..578a513bd02d --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/StructArchiveProxy.h @@ -0,0 +1,30 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#pragma once +#include "UHTMakefile/FieldArchiveProxy.h" + +/* See UHTMakefile.h for overview how makefiles work. */ +struct FStructArchiveProxy : public FFieldArchiveProxy +{ + FStructArchiveProxy() { } + FStructArchiveProxy(FUHTMakefile& UHTMakefile, const UStruct* Struct); + + UStruct* CreateStruct(const FUHTMakefile& UHTMakefile) const; + + void PostConstruct(UStruct* Struct) const; + + void Resolve(UStruct* Struct, const FUHTMakefile& UHTMakefile) const; + + friend FArchive& operator<<(FArchive& Ar, FStructArchiveProxy& StructArchiveProxy); + + FSerializeIndex SuperStructIndex; + FSerializeIndex ChildrenIndex; + int32 PropertiesSize; + int32 MinAlignment; + TArray Script; + int32 PropertyLinkIndex; + int32 RefLinkIndex; + int32 DestructorLinkIndex; + int32 PostConstructLinkIndex; + TArray ScriptObjectReferenceIndexes; +}; diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/StructDataArchiveProxy.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/StructDataArchiveProxy.cpp new file mode 100644 index 000000000000..d71d78ee8bbd --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/StructDataArchiveProxy.cpp @@ -0,0 +1,32 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. +#include "UnrealHeaderTool.h" +#include "UHTMakefile/UHTMakefile.h" +#include "UHTMakefile/StructDataArchiveProxy.h" + +FStructDataArchiveProxy::FStructDataArchiveProxy(const FUHTMakefile& UHTMakefile, const FStructData* InStructData) +{ + StructData = FTokenArchiveProxy(UHTMakefile, &InStructData->StructData); + StructPropertyData = FPropertyDataArchiveProxy(UHTMakefile, &InStructData->StructPropertyData); +} + +void FStructDataArchiveProxy::AddReferencedNames(const FStructData* InStructData, FUHTMakefile& UHTMakefile) +{ + FTokenArchiveProxy::AddReferencedNames(&InStructData->StructData, UHTMakefile); + FPropertyDataArchiveProxy::AddReferencedNames(&InStructData->StructPropertyData, UHTMakefile); +} + +FStructData* FStructDataArchiveProxy::CreateStructData(FUHTMakefile& UHTMakefile) +{ + FToken* Token = StructData.CreateToken(); + FStructData* Result = new FStructData(*Token); + StructPropertyData.Resolve(&Result->StructPropertyData, UHTMakefile); + return Result; +} + +FArchive& operator<<(FArchive& Ar, FStructDataArchiveProxy& StructDataArchiveProxy) +{ + Ar << StructDataArchiveProxy.StructData; + Ar << StructDataArchiveProxy.StructPropertyData; + + return Ar; +} diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/StructDataArchiveProxy.h b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/StructDataArchiveProxy.h new file mode 100644 index 000000000000..742cb4277027 --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/StructDataArchiveProxy.h @@ -0,0 +1,23 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#pragma once +#include "UHTMakefile/PropertyDataArchiveProxy.h" +#include "UHTMakefile/TokenArchiveProxy.h" + +class FUHTMakefile; +class FStructData; + +/* See UHTMakefile.h for overview how makefiles work. */ +struct FStructDataArchiveProxy +{ + FStructDataArchiveProxy(const FUHTMakefile& UHTMakefile, const FStructData* StructData); + FStructDataArchiveProxy() { } + + static void AddReferencedNames(const FStructData* StructData, FUHTMakefile& UHTMakefile); + + friend FArchive& operator<<(FArchive& Ar, FStructDataArchiveProxy& StructDataArchiveProxy); + FStructData* CreateStructData(FUHTMakefile& UHTMakefile); + + FTokenArchiveProxy StructData; + FPropertyDataArchiveProxy StructPropertyData; +}; diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/StructScopeArchiveProxy.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/StructScopeArchiveProxy.cpp new file mode 100644 index 000000000000..cd80680d18ac --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/StructScopeArchiveProxy.cpp @@ -0,0 +1,38 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#include "UnrealHeaderTool.h" +#include "UHTMakefile/UHTMakefile.h" +#include "UHTMakefile/StructScopeArchiveProxy.h" +#include "Scope.h" + +FArchive& operator<<(FArchive& Ar, FStructScopeArchiveProxy& StructScopeArchiveProxy) +{ + Ar << static_cast(StructScopeArchiveProxy); + Ar << StructScopeArchiveProxy.StructIndex; + + return Ar; +} + +FStructScopeArchiveProxy::FStructScopeArchiveProxy(const FUHTMakefile& UHTMakefile, const FStructScope* StructScope) + : FScopeArchiveProxy(UHTMakefile, StructScope) +{ + StructIndex = UHTMakefile.GetStructIndex(StructScope->Struct); +} + +void FStructScopeArchiveProxy::AddReferencedNames(const FStructScope* StructScope, FUHTMakefile& UHTMakefile) +{ + FScopeArchiveProxy::AddReferencedNames(StructScope, UHTMakefile); +} + +FStructScope* FStructScopeArchiveProxy::CreateStructScope(const FUHTMakefile& UHTMakefile) const +{ + return new FStructScope(nullptr, nullptr); +} + +void FStructScopeArchiveProxy::Resolve(FStructScope* StructScope, const FUHTMakefile& UHTMakefile) const +{ + FScopeArchiveProxy::Resolve(StructScope, UHTMakefile); + StructScope->Struct = UHTMakefile.GetStructByIndex(StructIndex); + + FScope::ScopeMap.Add(StructScope->Struct, StructScope->HasBeenAlreadyMadeSharable() ? StructScope->AsShared() : MakeShareable(StructScope)); +} diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/StructScopeArchiveProxy.h b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/StructScopeArchiveProxy.h new file mode 100644 index 000000000000..5f663e82fca9 --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/StructScopeArchiveProxy.h @@ -0,0 +1,22 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#pragma once +#include "UHTMakefile/ScopeArchiveProxy.h" + +class FUHTMakefile; +class FStructScope; + +/* See UHTMakefile.h for overview how makefiles work. */ +struct FStructScopeArchiveProxy : public FScopeArchiveProxy +{ + FStructScopeArchiveProxy(const FUHTMakefile& UHTMakefile, const FStructScope* StructScope); + FStructScopeArchiveProxy() { } + + static void AddReferencedNames(const FStructScope* StructScope, FUHTMakefile& UHTMakefile); + + friend FArchive& operator<<(FArchive& Ar, FStructScopeArchiveProxy& StructScopeArchiveProxy); + FStructScope* CreateStructScope(const FUHTMakefile& UHTMakefile) const; + void Resolve(FStructScope* StructScope, const FUHTMakefile& UHTMakefile) const; + + FSerializeIndex StructIndex; +}; diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/TokenArchiveProxy.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/TokenArchiveProxy.cpp new file mode 100644 index 000000000000..3bc57f6cf942 --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/TokenArchiveProxy.cpp @@ -0,0 +1,142 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. +#include "UnrealHeaderTool.h" +#include "UHTMakefile/UHTMakefile.h" +#include "UHTMakefile/TokenArchiveProxy.h" +#include "ParserHelper.h" + +/* See UHTMakefile.h for overview how makefiles work. */ +FTokenArchiveProxy::FTokenArchiveProxy(const FUHTMakefile& UHTMakefile, const FToken* Token) + : FPropertyBaseArchiveProxy(UHTMakefile, Token) +{ + TokenType = Token->TokenType; + TokenName = FNameArchiveProxy(UHTMakefile, Token->TokenName); + StartPos = Token->StartPos; + StartLine = Token->StartLine; + FMemory::Memcpy(Identifier, Token->Identifier, sizeof(TCHAR) * NAME_SIZE); + TokenPropertyIndex = UHTMakefile.GetPropertyIndex(Token->TokenProperty); + + switch (Token->Type) + { + case CPT_Byte: + Byte = Token->Byte; + break; + case CPT_Int: + Int = Token->Int; + break; + case CPT_Bool: + NativeBool = Token->NativeBool; + break; + case CPT_Float: + Float = Token->Float; + break; + case CPT_Double: + Double = Token->Double; + break; + case CPT_Name: + FMemory::Memcpy(NameBytes, Token->NameBytes, sizeof(FName)); + break; + case CPT_String: + FMemory::Memcpy(String, Token->String, sizeof(TCHAR) * MAX_STRING_CONST_SIZE); + break; + default: + break; + } +} + +void FTokenArchiveProxy::AddReferencedNames(const FToken* Token, FUHTMakefile& UHTMakefile) +{ + FPropertyBaseArchiveProxy::AddReferencedNames(Token, UHTMakefile); + UHTMakefile.AddName(Token->TokenName); +} + +FToken* FTokenArchiveProxy::CreateToken() const +{ + FToken* Token = new FToken(Type); + PostConstruct(Token); + + + return Token; +} + +void FTokenArchiveProxy::PostConstruct(FToken* Token) const +{ + FPropertyBaseArchiveProxy::PostConstruct(Token); + Token->TokenType = TokenType; + Token->StartPos = StartPos; + Token->StartLine = StartLine; + FMemory::Memcpy(Token->Identifier, Identifier, sizeof(TCHAR) * NAME_SIZE); + + switch (Type) + { + case CPT_Byte: + Token->Byte = Byte; + break; + case CPT_Int: + Token->Int = Int; + break; + case CPT_Bool: + Token->NativeBool = NativeBool; + break; + case CPT_Float: + Token->Float = Float; + break; + case CPT_Double: + Token->Double = Double; + break; + case CPT_Name: + FMemory::Memcpy(Token->NameBytes, NameBytes, sizeof(FName)); + break; + case CPT_String: + FMemory::Memcpy(Token->String, String, sizeof(TCHAR) * MAX_STRING_CONST_SIZE); + break; + default: + break; + } +} + +void FTokenArchiveProxy::Resolve(FToken* Token, const FUHTMakefile& UHTMakefile) const +{ + FPropertyBaseArchiveProxy::Resolve(Token, UHTMakefile); + Token->TokenName = TokenName.CreateName(UHTMakefile); + Token->TokenProperty = UHTMakefile.GetPropertyByIndex(TokenPropertyIndex); +} + +FArchive& operator<<(FArchive& Ar, FTokenArchiveProxy& TokenArchiveProxy) +{ + Ar << static_cast(TokenArchiveProxy); + Ar << TokenArchiveProxy.TokenType; + Ar << TokenArchiveProxy.TokenName; + Ar << TokenArchiveProxy.StartPos; + Ar << TokenArchiveProxy.StartLine; + Ar.Serialize(TokenArchiveProxy.Identifier, sizeof(TokenArchiveProxy.Identifier)); + Ar << TokenArchiveProxy.TokenPropertyIndex; + + switch (TokenArchiveProxy.Type) + { + case CPT_Byte: + Ar << TokenArchiveProxy.Byte; + break; + case CPT_Int: + Ar << TokenArchiveProxy.Int; + break; + case CPT_Bool: + Ar << TokenArchiveProxy.NativeBool; + break; + case CPT_Float: + Ar << TokenArchiveProxy.Float; + break; + case CPT_Double: + Ar << TokenArchiveProxy.Double; + break; + case CPT_Name: + Ar.Serialize(TokenArchiveProxy.NameBytes, sizeof(TokenArchiveProxy.NameBytes)); + break; + case CPT_String: + Ar.Serialize(TokenArchiveProxy.String, sizeof(TokenArchiveProxy.String)); + break; + default: + break; + } + + return Ar; +} diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/TokenArchiveProxy.h b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/TokenArchiveProxy.h new file mode 100644 index 000000000000..59793762affa --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/TokenArchiveProxy.h @@ -0,0 +1,40 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#pragma once +#include "UHTMakefile/PropertyBaseArchiveProxy.h" + +class FUHTMakefile; + +/* See UHTMakefile.h for overview how makefiles work. */ +struct FTokenArchiveProxy : public FPropertyBaseArchiveProxy +{ + FTokenArchiveProxy(const FUHTMakefile& UHTMakefile, const FToken* Token); + FTokenArchiveProxy() { } + + static void AddReferencedNames(const FToken* Token, FUHTMakefile& UHTMakefile); + + friend FArchive& operator<<(FArchive& Ar, FTokenArchiveProxy& TokenArchiveProxy); + FToken* CreateToken() const; + + void PostConstruct(FToken* Token) const; + + void Resolve(FToken* Token, const FUHTMakefile& UHTMakefile) const; + + ETokenType TokenType; + FNameArchiveProxy TokenName; + int32 StartPos; + int32 StartLine; + TCHAR Identifier[NAME_SIZE]; + int32 TokenPropertyIndex; + + union + { + uint8 Byte; // If CPT_Byte. + int32 Int; // If CPT_Int. + bool NativeBool; // if CPT_Bool + float Float; // If CPT_Float. + double Double; // If CPT_Double. + uint8 NameBytes[sizeof(FName)]; // If CPT_Name. + TCHAR String[MAX_STRING_CONST_SIZE]; // If CPT_String + }; +}; diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/TokenDataArchiveProxy.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/TokenDataArchiveProxy.cpp new file mode 100644 index 000000000000..ec0d7dafd142 --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/TokenDataArchiveProxy.cpp @@ -0,0 +1,30 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. +#include "UnrealHeaderTool.h" +#include "UHTMakefile/UHTMakefile.h" +#include "UHTMakefile/TokenArchiveProxy.h" +#include "UHTMakefile/TokenDataArchiveProxy.h" +#include "ParserHelper.h" + +FTokenDataArchiveProxy::FTokenDataArchiveProxy(const FUHTMakefile& UHTMakefile, const FTokenData* TokenData) +{ + TokenIndex = UHTMakefile.GetTokenIndex(&TokenData->Token); +} + +void FTokenDataArchiveProxy::AddReferencedNames(const FTokenData* TokenData, FUHTMakefile& UHTMakefile) +{ } + +FTokenData* FTokenDataArchiveProxy::CreateTokenData(const FUHTMakefile& UHTMakefile) const +{ + return new FTokenData(); +} + +void FTokenDataArchiveProxy::Resolve(FTokenData& TokenData, const FUHTMakefile& UHTMakefile) const +{ + TokenData.Token = *UHTMakefile.GetTokenByIndex(TokenIndex); +} + +FArchive& operator<<(FArchive& Ar, FTokenDataArchiveProxy& TokenDataArchiveProxy) +{ + Ar << TokenDataArchiveProxy.TokenIndex; + return Ar; +} diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/TokenDataArchiveProxy.h b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/TokenDataArchiveProxy.h new file mode 100644 index 000000000000..f7c95411a4c0 --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/TokenDataArchiveProxy.h @@ -0,0 +1,21 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#pragma once +#include "UHTMakefile/TokenArchiveProxy.h" + +class FUHTMakefile; + +/* See UHTMakefile.h for overview how makefiles work. */ +struct FTokenDataArchiveProxy +{ + FTokenDataArchiveProxy(const FUHTMakefile& UHTMakefile, const FTokenData* TokenData); + FTokenDataArchiveProxy() { } + + static void AddReferencedNames(const FTokenData* TokenData, FUHTMakefile& UHTMakefile); + + friend FArchive& operator<<(FArchive& Ar, FTokenDataArchiveProxy& TokenDataArchiveProxy); + FTokenData* CreateTokenData(const FUHTMakefile& UHTMakefile) const; + void Resolve(FTokenData& TokenData, const FUHTMakefile& UHTMakefile) const; + + int32 TokenIndex; +}; diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/TypeDefinitionInfoMapArchiveProxy.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/TypeDefinitionInfoMapArchiveProxy.cpp new file mode 100644 index 000000000000..3ff0e99ae231 --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/TypeDefinitionInfoMapArchiveProxy.cpp @@ -0,0 +1,41 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. +#include "UnrealHeaderTool.h" +#include "UHTMakefile/UHTMakefile.h" +#include "UHTMakefile/TypeDefinitionInfoMapArchiveProxy.h" +#include "ClassMaps.h" + + +FTypeDefinitionInfoMapArchiveProxy::FTypeDefinitionInfoMapArchiveProxy(FUHTMakefile& UHTMakefile, TArray>& TypeDefinitionInfoPairs) +{ + for (auto& Kvp : TypeDefinitionInfoPairs) + { + UField* Field = Kvp.Key; + + FSerializeIndex FieldIndex = UHTMakefile.GetFieldIndex(Field); + int32 UnrealTypeDefinitionInfoIndex = UHTMakefile.GetUnrealTypeDefinitionInfoIndex(Kvp.Value); + + TypeDefinitionInfoIndexes.Add(TPairInitializer(FieldIndex, UnrealTypeDefinitionInfoIndex)); + } +} + +void FTypeDefinitionInfoMapArchiveProxy::ResolveIndex(FUHTMakefile& UHTMakefile, int32 Index) +{ + auto& Kvp = TypeDefinitionInfoIndexes[Index]; + UField* Field = UHTMakefile.GetFieldByIndex(Kvp.Key); + FUnrealTypeDefinitionInfo* UnrealTypeDefinitionInfo = UHTMakefile.GetUnrealTypeDefinitionInfoByIndex(Kvp.Value); + TPair* Entry = UHTMakefile.GetTypeDefinitionInfoMapEntryByIndex(Index); + Entry->Key = Field; + Entry->Value = UnrealTypeDefinitionInfo; + GTypeDefinitionInfoMap.Add(Field, MakeShareable(UnrealTypeDefinitionInfo)); +} + +void FTypeDefinitionInfoMapArchiveProxy::ResolveClassIndex(FUHTMakefile& UHTMakefile, int32 Index) +{ + auto& Kvp = TypeDefinitionInfoIndexes[Index]; + UClass* Class = UHTMakefile.GetClassByIndex(Kvp.Key); + FUnrealTypeDefinitionInfo* UnrealTypeDefinitionInfo = UHTMakefile.GetUnrealTypeDefinitionInfoByIndex(Kvp.Value); + TPair* Entry = UHTMakefile.GetTypeDefinitionInfoMapEntryByIndex(Index); + Entry->Key = Class; + Entry->Value = UnrealTypeDefinitionInfo; + GTypeDefinitionInfoMap.Add(Class, MakeShareable(UnrealTypeDefinitionInfo)); +} diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/TypeDefinitionInfoMapArchiveProxy.h b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/TypeDefinitionInfoMapArchiveProxy.h new file mode 100644 index 000000000000..221bd8b9449f --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/TypeDefinitionInfoMapArchiveProxy.h @@ -0,0 +1,25 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#pragma once +#include "UnrealTypeDefinitionInfo.h" + +class FUnrealTypeDefinitionInfo; + +/* See UHTMakefile.h for overview how makefiles work. */ +struct FTypeDefinitionInfoMapArchiveProxy +{ + FTypeDefinitionInfoMapArchiveProxy(FUHTMakefile& UHTMakefile, TArray>& TypeDefinitionInfoPairs); + FTypeDefinitionInfoMapArchiveProxy() { } + + TArray> TypeDefinitionInfoIndexes; + + friend FArchive& operator<<(FArchive& Ar, FTypeDefinitionInfoMapArchiveProxy& FileScopeArchiveProxy) + { + Ar << FileScopeArchiveProxy.TypeDefinitionInfoIndexes; + + return Ar; + } + + void ResolveIndex(FUHTMakefile& UHTMakefile, int32 Index); + void ResolveClassIndex(FUHTMakefile& UHTMakefile, int32 Index); +}; \ No newline at end of file diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/UHTMakefile.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/UHTMakefile.cpp new file mode 100644 index 000000000000..df34be512b10 --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/UHTMakefile.cpp @@ -0,0 +1,2328 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. +#include "UnrealHeaderTool.h" +#include "UHTMakefile.h" +#include "ClassMaps.h" +#include "Scope.h" +#include "UnrealSourceFile.h" +#include "HeaderProvider.h" +#include "ClassArchiveProxy.h" +#include "PropertyArchiveProxy.h" +#include "StructArchiveProxy.h" +#include "PackageArchiveProxy.h" +#include "EnumArchiveProxy.h" +#include "ObjectBaseArchiveProxy.h" +#include "ScriptStructArchiveProxy.h" +#include "ModuleDescriptor.h" +#include "Manifest.h" +#include "ScopedTimers.h" +#include "ScopeExit.h" + + +void FUHTMakefile::SetupScopeArrays() +{ + TSet AllScopesSet; + for (auto& Kvp : FScope::ScopeMap) + { + AllScopesSet.Add(&Kvp.Value.Get()); + } + + for (FScope* Scope : AllScopesSet) + { + if (FFileScope* FileScope = Scope->AsFileScope()) + { + FileScopes.Add(FileScope); + } + else if (FStructScope* StructScope = Scope->AsStructScope()) + { + StructScopes.Add(StructScope); + } + else + { + Scopes.Add(Scope); + } + } +} + +void FUHTMakefile::SetupProxies() +{ + PackageProxies.Empty(Packages.Num()); + for (UPackage* Package : Packages) + { + PackageProxies.Add(FPackageArchiveProxy(*this, Package)); + } + + ClassProxies.Empty(Classes.Num()); + for (UClass* Class : Classes) + { + ClassProxies.Add(FClassArchiveProxy(*this, Class)); + } + + EnumProxies.Empty(Enums.Num()); + for (UEnum* Enum : Enums) + { + EnumProxies.Add(FEnumArchiveProxy(*this, Enum)); + } + + StructProxies.Empty(Structs.Num()); + for (UStruct* Struct : Structs) + { + StructProxies.Add(FStructArchiveProxy(*this, Struct)); + } + + ScriptStructProxies.Empty(ScriptStructs.Num()); + for (UScriptStruct* ScriptStruct : ScriptStructs) + { + ScriptStructProxies.Add(FScriptStructArchiveProxy(*this, ScriptStruct)); + } + + PropertyProxies.Empty(Properties.Num()); + for (UProperty* Property : Properties) + { + PropertyProxies.Add(FPropertyArchiveProxy(*this, Property)); + } + + BytePropertyProxies.Empty(ByteProperties.Num()); + for (UByteProperty* ByteProperty : ByteProperties) + { + BytePropertyProxies.Add(FBytePropertyArchiveProxy(*this, ByteProperty)); + } + + Int8PropertyProxies.Empty(Int8Properties.Num()); + for (UInt8Property* Int8Property : Int8Properties) + { + Int8PropertyProxies.Add(FInt8PropertyArchiveProxy(*this, Int8Property)); + } + + Int16PropertyProxies.Empty(Int16Properties.Num()); + for (UInt16Property* Int16Property : Int16Properties) + { + Int16PropertyProxies.Add(FInt16PropertyArchiveProxy(*this, Int16Property)); + } + + IntPropertyProxies.Empty(IntProperties.Num()); + for (UIntProperty* IntProperty : IntProperties) + { + IntPropertyProxies.Add(FIntPropertyArchiveProxy(*this, IntProperty)); + } + + Int64PropertyProxies.Empty(Int64Properties.Num()); + for (UInt64Property* Int64Property : Int64Properties) + { + Int64PropertyProxies.Add(FInt64PropertyArchiveProxy(*this, Int64Property)); + } + + UInt16PropertyProxies.Empty(UInt16Properties.Num()); + for (UUInt16Property* UInt16Property : UInt16Properties) + { + UInt16PropertyProxies.Add(FUInt16PropertyArchiveProxy(*this, UInt16Property)); + } + + UInt32PropertyProxies.Empty(UInt32Properties.Num()); + for (UUInt32Property* UInt32Property : UInt32Properties) + { + UInt32PropertyProxies.Add(FUInt32PropertyArchiveProxy(*this, UInt32Property)); + } + + UInt64PropertyProxies.Empty(UInt64Properties.Num()); + for (UUInt64Property* UInt64Property : UInt64Properties) + { + UInt64PropertyProxies.Add(FUInt64PropertyArchiveProxy(*this, UInt64Property)); + } + + FloatPropertyProxies.Empty(FloatProperties.Num()); + for (UFloatProperty* FloatProperty : FloatProperties) + { + FloatPropertyProxies.Add(FFloatPropertyArchiveProxy(*this, FloatProperty)); + } + + DoublePropertyProxies.Empty(DoubleProperties.Num()); + for (UDoubleProperty* DoubleProperty : DoubleProperties) + { + DoublePropertyProxies.Add(FDoublePropertyArchiveProxy(*this, DoubleProperty)); + } + + BoolPropertyProxies.Empty(BoolProperties.Num()); + for (UBoolProperty* BoolProperty : BoolProperties) + { + BoolPropertyProxies.Add(FBoolPropertyArchiveProxy(*this, BoolProperty)); + } + + NamePropertyProxies.Empty(NameProperties.Num()); + for (UNameProperty* NameProperty : NameProperties) + { + NamePropertyProxies.Add(FNamePropertyArchiveProxy(*this, NameProperty)); + } + + StrPropertyProxies.Empty(StrProperties.Num()); + for (UStrProperty* StrProperty : StrProperties) + { + StrPropertyProxies.Add(FStrPropertyArchiveProxy(*this, StrProperty)); + } + + TextPropertyProxies.Empty(TextProperties.Num()); + for (UTextProperty* TextProperty : TextProperties) + { + TextPropertyProxies.Add(FTextPropertyArchiveProxy(*this, TextProperty)); + } + + DelegatePropertyProxies.Empty(DelegateProperties.Num()); + for (UDelegateProperty* DelegateProperty : DelegateProperties) + { + DelegatePropertyProxies.Add(FDelegatePropertyArchiveProxy(*this, DelegateProperty)); + } + + MulticastDelegatePropertyProxies.Empty(MulticastDelegateProperties.Num()); + for (UMulticastDelegateProperty* MulticastDelegateProperty : MulticastDelegateProperties) + { + MulticastDelegatePropertyProxies.Add(FMulticastDelegatePropertyArchiveProxy(*this, MulticastDelegateProperty)); + } + + ObjectPropertyBaseProxies.Empty(ObjectPropertyBases.Num()); + for (UObjectPropertyBase* ObjectPropertyBase : ObjectPropertyBases) + { + ObjectPropertyBaseProxies.Add(FObjectPropertyBaseArchiveProxy(*this, ObjectPropertyBase)); + } + + ClassPropertyProxies.Empty(ClassProperties.Num()); + for (UClassProperty* ClassProperty : ClassProperties) + { + ClassPropertyProxies.Add(FClassPropertyArchiveProxy(*this, ClassProperty)); + } + + ObjectPropertyProxies.Empty(ObjectProperties.Num()); + for (UObjectProperty* ObjectProperty : ObjectProperties) + { + ObjectPropertyProxies.Add(FObjectPropertyArchiveProxy(*this, ObjectProperty)); + } + + WeakObjectPropertyProxies.Empty(WeakObjectProperties.Num()); + for (UWeakObjectProperty* WeakObjectProperty : WeakObjectProperties) + { + WeakObjectPropertyProxies.Add(FWeakObjectPropertyArchiveProxy(*this, WeakObjectProperty)); + } + + LazyObjectPropertyProxies.Empty(LazyObjectProperties.Num()); + for (ULazyObjectProperty* LazyObjectProperty : LazyObjectProperties) + { + LazyObjectPropertyProxies.Add(FLazyObjectPropertyArchiveProxy(*this, LazyObjectProperty)); + } + + AssetObjectPropertyProxies.Empty(AssetObjectProperties.Num()); + for (UAssetObjectProperty* AssetObjectProperty : AssetObjectProperties) + { + AssetObjectPropertyProxies.Add(FAssetObjectPropertyArchiveProxy(*this, AssetObjectProperty)); + } + + AssetClassPropertyProxies.Empty(AssetClassProperties.Num()); + for (UAssetClassProperty* AssetClassProperty : AssetClassProperties) + { + AssetClassPropertyProxies.Add(FAssetClassPropertyArchiveProxy(*this, AssetClassProperty)); + } + + InterfacePropertyProxies.Empty(InterfaceProperties.Num()); + for (UInterfaceProperty* InterfaceProperty : InterfaceProperties) + { + InterfacePropertyProxies.Add(FInterfacePropertyArchiveProxy(*this, InterfaceProperty)); + } + + StructPropertyProxies.Empty(StructProperties.Num()); + for (UStructProperty* StructProperty : StructProperties) + { + StructPropertyProxies.Add(FStructPropertyArchiveProxy(*this, StructProperty)); + } + + MapPropertyProxies.Empty(MapProperties.Num()); + for (UMapProperty* MapProperty : MapProperties) + { + MapPropertyProxies.Add(FMapPropertyArchiveProxy(*this, MapProperty)); + } + + ArrayPropertyProxies.Empty(ArrayProperties.Num()); + for (UArrayProperty* ArrayProperty : ArrayProperties) + { + ArrayPropertyProxies.Add(FArrayPropertyArchiveProxy(*this, ArrayProperty)); + } + + FunctionProxies.Empty(Functions.Num()); + for (UFunction* Function : Functions) + { + FunctionProxies.Add(FFunctionArchiveProxy(*this, Function)); + } + + DelegateFunctionProxies.Empty(DelegateFunctions.Num()); + for (UDelegateFunction* DelegateFunction : DelegateFunctions) + { + DelegateFunctionProxies.Add(FDelegateFunctionArchiveProxy(*this, DelegateFunction)); + } + + ModuleDescriptorsArchiveProxy.Empty(ModuleDescriptors.Num()); + for (auto& Kvp : ModuleDescriptors) + { + ModuleDescriptorsArchiveProxy.Add(TPairInitializer(FNameArchiveProxy(*this, Kvp.Key), Kvp.Value)); + } + + GeneratedCodeCRCProxies.Empty(GeneratedCodeCRCs.Num()); + for (auto& Kvp : GeneratedCodeCRCs) + { + GeneratedCodeCRCProxies.Add(TPairInitializer(GetFieldIndex(Kvp.Key), Kvp.Value)); + } + + TypeDefinitionInfoMapArchiveProxy = FTypeDefinitionInfoMapArchiveProxy(*this, TypeDefinitionInfoMap); + + ModuleNamesProxy.Empty(ModuleNames.Num()); + for (FName Name : ModuleNames) + { + ModuleNamesProxy.Add(FNameArchiveProxy(*this, Name)); + } + + UnrealSourceFileProxies.Empty(UnrealSourceFiles.Num()); + for (FUnrealSourceFile* UnrealSourceFile : UnrealSourceFiles) + { + UnrealSourceFileProxies.Add(FUnrealSourceFileArchiveProxy(*this, UnrealSourceFile)); + } + + UnrealTypeDefinitionInfoProxies.Empty(UnrealTypeDefinitionInfos.Num()); + for (FUnrealTypeDefinitionInfo* UnrealTypeDefinitionInfo : UnrealTypeDefinitionInfos) + { + UnrealTypeDefinitionInfoProxies.Add(FUnrealTypeDefinitionInfoArchiveProxy(*this, UnrealTypeDefinitionInfo)); + } + + FileScopeProxies.Empty(FileScopes.Num()); + for (FFileScope* FileScope : FileScopes) + { + FileScopeProxies.Add(FFileScopeArchiveProxy(*this, FileScope)); + } + + StructScopeProxies.Empty(StructScopes.Num()); + for (FStructScope* StructScope : StructScopes) + { + StructScopeProxies.Add(FStructScopeArchiveProxy(*this, StructScope)); + } + + ScopeProxies.Empty(Scopes.Num()); + for (FScope* Scope : Scopes) + { + ScopeProxies.Add(FScopeArchiveProxy(*this, Scope)); + } + + PublicClassSetEntryProxies.Empty(PublicClassSetEntries.Num()); + for (UClass* Class : PublicClassSetEntries) + { + PublicClassSetEntryProxies.Add(GetClassIndex(Class)); + } + + CompilerMetadataManagerArchiveProxy = FCompilerMetadataManagerArchiveProxy(*this, GScriptHelperEntries); + + MultipleInheritanceBaseClassProxies.Empty(MultipleInheritanceBaseClasses.Num()); + for (FMultipleInheritanceBaseClass* MultipleInheritanceBaseClass : MultipleInheritanceBaseClasses) + { + MultipleInheritanceBaseClassProxies.Add(FMultipleInheritanceBaseClassArchiveProxy(*this, MultipleInheritanceBaseClass)); + } + + TokenDataProxies.Empty(TokenDatas.Num()); + for (TSharedPtr& TokenData : TokenDatas) + { + TokenDataProxies.Add(FTokenDataArchiveProxy(*this, TokenData.Get())); + } + + PropertyDataEntryProxies.Empty(PropertyDataEntries.Num()); + for (auto& PropertyDataEntry : PropertyDataEntries) + { + PropertyDataEntryProxies.Add(TPairInitializer(GetPropertyIndex(PropertyDataEntry.Key), GetTokenIndex(&PropertyDataEntry.Value->Token))); + } + + UnrealSourceFilesMapEntryProxies.Empty(UnrealSourceFilesMapEntries.Num()); + for (auto& Kvp : UnrealSourceFilesMapEntries) + { + UnrealSourceFilesMapEntryProxies.Add(TPairInitializer(Kvp.Key, GetUnrealSourceFileIndex(Kvp.Value))); + } + + ClassMetaDataArchiveProxies.Empty(ClassMetaDatas.Num()); + for (const TScopedPointer& MetaData : ClassMetaDatas) + { + ClassMetaDataArchiveProxies.Add(FClassMetaDataArchiveProxy(*this, MetaData.GetOwnedPointer())); + } + + TokenProxies.Empty(Tokens.Num()); + for (FToken* Token : Tokens) + { + TokenProxies.Add(FTokenArchiveProxy(*this, Token)); + } + + PropertyBaseProxies.Empty(PropertyBases.Num()); + for (FPropertyBase* PropertyBase : PropertyBases) + { + PropertyBaseProxies.Add(FPropertyBaseArchiveProxy(*this, PropertyBase)); + } + + EnumUnderlyingTypeProxies.Empty(EnumUnderlyingTypes.Num()); + for (auto& Kvp : EnumUnderlyingTypes) + { + EnumUnderlyingTypeProxies.Add(TPairInitializer(GetEnumIndex(Kvp.Key), Kvp.Value)); + } + + StructNameMapEntryProxies.Empty(StructNameMapEntries.Num()); + for (auto& Kvp : StructNameMapEntries) + { + StructNameMapEntryProxies.Add(TPairInitializer(GetStructIndex(Kvp.Key), Kvp.Value)); + } + + InterfaceAllocationProxies.Empty(InterfaceAllocations.Num()); + for (TCHAR* Entry : InterfaceAllocations) + { + InterfaceAllocationProxies.Add(Entry); + } +} + +int32 FUHTMakefile::GetPropertyCount() const +{ + return Properties.Num() + + ByteProperties.Num() + + Int8Properties.Num() + + Int16Properties.Num() + + IntProperties.Num() + + Int64Properties.Num() + + UInt16Properties.Num() + + UInt32Properties.Num() + + UInt64Properties.Num() + + FloatProperties.Num() + + DoubleProperties.Num() + + BoolProperties.Num() + + NameProperties.Num() + + StrProperties.Num() + + TextProperties.Num() + + DelegateProperties.Num() + + MulticastDelegateProperties.Num() + + ObjectPropertyBases.Num() + + ClassProperties.Num() + + ObjectProperties.Num() + + WeakObjectProperties.Num() + + LazyObjectProperties.Num() + + AssetObjectProperties.Num() + + AssetClassProperties.Num() + + InterfaceProperties.Num() + + StructProperties.Num() + + MapProperties.Num() + + ArrayProperties.Num(); +} + +int32 FUHTMakefile::GetPropertyProxiesCount() const +{ + return Properties.Num() + + BytePropertyProxies.Num() + + Int8PropertyProxies.Num() + + Int16PropertyProxies.Num() + + IntPropertyProxies.Num() + + Int64PropertyProxies.Num() + + UInt16PropertyProxies.Num() + + UInt32PropertyProxies.Num() + + UInt64PropertyProxies.Num() + + FloatPropertyProxies.Num() + + DoublePropertyProxies.Num() + + BoolPropertyProxies.Num() + + NamePropertyProxies.Num() + + StrPropertyProxies.Num() + + TextPropertyProxies.Num() + + DelegatePropertyProxies.Num() + + MulticastDelegatePropertyProxies.Num() + + ObjectPropertyBaseProxies.Num() + + ClassPropertyProxies.Num() + + ObjectPropertyProxies.Num() + + WeakObjectPropertyProxies.Num() + + LazyObjectPropertyProxies.Num() + + AssetObjectPropertyProxies.Num() + + AssetClassPropertyProxies.Num() + + InterfacePropertyProxies.Num() + + StructPropertyProxies.Num() + + MapPropertyProxies.Num() + + ArrayPropertyProxies.Num(); +} + +int32 FUHTMakefile::GetFunctionCount() const +{ + return Functions.Num() + DelegateFunctions.Num(); +} + +int32 FUHTMakefile::GetFunctionProxiesCount() const +{ + return FunctionProxies.Num() + DelegateFunctionProxies.Num(); +} + +int32 FUHTMakefile::GetStructsCount() const +{ + return Structs.Num() + ScriptStructs.Num() + Classes.Num() + GetFunctionCount(); +} + +int32 FUHTMakefile::GetStructProxiesCount() const +{ + return StructProxies.Num() + ScriptStructProxies.Num() + ClassProxies.Num() + GetFunctionProxiesCount(); +} + +void FUHTMakefile::UpdateModulesCompatibility(FManifest* InManifest) +{ + TArray& ManifestModules = Manifest->Modules; + TArray& InManifestModules = InManifest->Modules; + int32 ManifestModulesNum = ManifestModules.Num(); + int32 InManifestModulesNum = InManifestModules.Num(); + int32 MinModuleIndex = FMath::Min(ManifestModulesNum, InManifestModulesNum); + bool bForceIncompatibility = false; + for (int32 i = 0; i < MinModuleIndex; ++i) + { + FManifestModule& InModule = InManifestModules[i]; + FManifestModule& Module = ManifestModules[i]; + if (bForceIncompatibility) + { + InModule.ForceRegeneration(); + } + + if (!Module.IsCompatibleWith(InModule)) + { + InModule.ForceRegeneration(); + bForceIncompatibility = true; + } + } + + if (bForceIncompatibility) + { + for (int32 i = MinModuleIndex; i < ManifestModulesNum; ++i) + { + ManifestModules[i].ForceRegeneration(); + } + + for (int32 i = MinModuleIndex; i < InManifestModulesNum; ++i) + { + InManifestModules[i].ForceRegeneration(); + } + } +} + +void FUHTMakefile::SaveToFile(const TCHAR* MakefilePath) +{ + FArchive* UHTMakefileArchive = IFileManager::Get().CreateFileWriter(MakefilePath); + if (!UHTMakefileArchive) + { + return; + } + + ON_SCOPE_EXIT + { + UHTMakefileArchive->Close(); + }; + + double UHTMakefileLoadTime = 0.0; + FDurationTimer UHTMakefileLoadTimer(UHTMakefileLoadTime); + UHTMakefileLoadTimer.Start(); + *UHTMakefileArchive << *this; + UHTMakefileLoadTimer.Stop(); + UE_LOG(LogCompile, Log, TEXT("Saving UHT makefile took %f seconds"), UHTMakefileLoadTime); +} + +void FUHTMakefile::LoadFromFile(const TCHAR* MakefilePath, FManifest* Manifest) +{ + FArchive* UHTMakefileArchive = IFileManager::Get().CreateFileReader(MakefilePath); + if (!UHTMakefileArchive) + { + return; + } + + ON_SCOPE_EXIT + { + UHTMakefileArchive->Close(); + }; + + double UHTMakefileLoadTime = 0.0; + FDurationTimer UHTMakefileLoadTimer(UHTMakefileLoadTime); + UHTMakefileLoadTimer.Start(); + *UHTMakefileArchive << *this; + UHTMakefileLoadTimer.Stop(); + UE_LOG(LogCompile, Log, TEXT("Loading UHT makefile took %f seconds"), UHTMakefileLoadTime); + + UpdateModulesCompatibility(Manifest); +} + +bool FUHTMakefile::CanLoadModule(const FManifestModule& ManifestModule) +{ + FName ModuleName = FName(*ManifestModule.Name); + bool bNeedsRegeneration = ManifestModule.NeedsRegeneration(); + bool bUHTMakefileContainsModuleData = HasModule(ModuleName); + bool bForceRegeneration = ShouldForceRegeneration(); + return !bNeedsRegeneration + && !bForceRegeneration + && bUHTMakefileContainsModuleData; +} + +void FUHTMakefile::ResolveTypeFullyCreatedDuringPreload(int32 Index) +{ + auto& Kvp = InitializationOrder[+EUHTMakefileLoadingPhase::Preload][Index]; + if (IsTypeFullyCreatedDuringPreload(Kvp.Key)) + { + ResolveObject(Kvp.Key, Kvp.Value); + } +} + +void FUHTMakefile::MoveNewObjects() +{ + Packages.Append(NewPackages); + NewPackages.Empty(); + UnrealSourceFiles.Append(NewUnrealSourceFiles); + NewUnrealSourceFiles.Empty(); + FileScopes.Append(NewFileScopes); + NewFileScopes.Empty(); + StructScopes.Append(NewStructScopes); + NewStructScopes.Empty(); + UnrealTypeDefinitionInfos.Append(NewUnrealTypeDefinitionInfos); + NewUnrealTypeDefinitionInfos.Empty(); + TypeDefinitionInfoMap.Append(NewTypeDefinitionInfoMap); + NewTypeDefinitionInfoMap.Empty(); + Classes.Append(NewClasses); + NewClasses.Empty(); + UnrealSourceFilesMapEntries.Append(NewUnrealSourceFilesMapEntries); + NewUnrealSourceFilesMapEntries.Empty(); + PublicClassSetEntries.Append(NewPublicClassSetEntries); + NewPublicClassSetEntries.Empty(); + InterfaceAllocations.Append(NewInterfaceAllocations); + NewInterfaceAllocations.Empty(); + StructNameMapEntries.Append(NewStructNameMapEntries); + NewStructNameMapEntries.Empty(); + + bShouldMoveNewObjects = false; +} + +int32 FUHTMakefile::GetPackageIndex(UPackage* Package) const +{ + if (!Package) + { + return INDEX_NONE; + } + + return PackageIndexes.FindChecked(Package); +} + +UPackage* FUHTMakefile::GetPackageByIndex(int32 Index) const +{ + if (Index == INDEX_NONE) + { + return nullptr; + } + + if (!(Packages.IsValidIndex(Index) && PackageProxies.IsValidIndex(Index))) + { + UE_LOG(LogCompile, Error, TEXT("Index: %d Packages.Num(): %d PackageProxies.Num(): %d"), Index, Packages.Num(), PackageProxies.Num()); + } + check(Packages.IsValidIndex(Index) && PackageProxies.IsValidIndex(Index)); + return Packages[Index]; +} + +int32 FUHTMakefile::GetTokenIndex(const FToken* Token) const +{ + if (!Token) + { + return INDEX_NONE; + } + + return TokenIndexes.FindChecked(Token); +} + +const FToken* FUHTMakefile::GetTokenByIndex(int32 Index) const +{ + if (Index == INDEX_NONE) + { + return nullptr; + } + + check(Tokens.IsValidIndex(Index)); + return Tokens[Index]; +} + +void FUHTMakefile::CreateObjectAtInitOrderIndex(int32 Index, EUHTMakefileLoadingPhase UHTMakefileLoadingPhase) +{ + auto& Kvp = InitializationOrder[+UHTMakefileLoadingPhase][Index]; + CreateObject(Kvp.Key, Kvp.Value); +} + +void FUHTMakefile::ResolveObjectAtInitOrderIndex(int32 Index, EUHTMakefileLoadingPhase UHTMakefileLoadingPhase) +{ + auto& Kvp = InitializationOrder[+UHTMakefileLoadingPhase][Index]; + + // Make sure types are not resolved twice. + if (IsTypeFullyCreatedDuringPreload(Kvp.Key) + && (UHTMakefileLoadingPhase == EUHTMakefileLoadingPhase::Preload)) + { + return; + } + + ResolveObject(Kvp.Key, Kvp.Value); +} + +void FUHTMakefile::CreateObject(ESerializedObjectType ObjectType, int32 Index) +{ + switch (ObjectType) + { + case ESerializedObjectType::EPropertyBase: + CreatePropertyBase(Index); + break; + case ESerializedObjectType::EPackage: + CreatePackage(Index); + break; + case ESerializedObjectType::EGeneratedCodeCRC: + CreateGeneratedCodeCRC(Index); + break; + case ESerializedObjectType::EClass: + CreateClass(Index); + break; + case ESerializedObjectType::EEnum: + CreateEnum(Index); + break; + case ESerializedObjectType::EStruct: + CreateStruct(Index); + break; + case ESerializedObjectType::EScriptStruct: + CreateScriptStruct(Index); + break; + case ESerializedObjectType::EProperty: + CreateProperty(Index); + break; + case ESerializedObjectType::EByteProperty: + CreateByteProperty(Index); + break; + case ESerializedObjectType::EInt8Property: + CreateInt8Property(Index); + break; + case ESerializedObjectType::EInt16Property: + CreateInt16Property(Index); + break; + case ESerializedObjectType::EIntProperty: + CreateIntProperty(Index); + break; + case ESerializedObjectType::EInt64Property: + CreateInt64Property(Index); + break; + case ESerializedObjectType::EUInt16Property: + CreateUInt16Property(Index); + break; + case ESerializedObjectType::EUInt32Property: + CreateUInt32Property(Index); + break; + case ESerializedObjectType::EUInt64Property: + CreateUInt64Property(Index); + break; + case ESerializedObjectType::EFloatProperty: + CreateFloatProperty(Index); + break; + case ESerializedObjectType::EDoubleProperty: + CreateDoubleProperty(Index); + break; + case ESerializedObjectType::EBoolProperty: + CreateBoolProperty(Index); + break; + case ESerializedObjectType::ENameProperty: + CreateNameProperty(Index); + break; + case ESerializedObjectType::EStrProperty: + CreateStrProperty(Index); + break; + case ESerializedObjectType::ETextProperty: + CreateTextProperty(Index); + break; + case ESerializedObjectType::EDelegateProperty: + CreateDelegateProperty(Index); + break; + case ESerializedObjectType::EMulticastDelegateProperty: + CreateMulticastDelegateProperty(Index); + break; + case ESerializedObjectType::EObjectPropertyBase: + CreateObjectPropertyBase(Index); + break; + case ESerializedObjectType::EClassProperty: + CreateClassProperty(Index); + break; + case ESerializedObjectType::EObjectProperty: + CreateObjectProperty(Index); + break; + case ESerializedObjectType::EWeakObjectProperty: + CreateWeakObjectProperty(Index); + break; + case ESerializedObjectType::ELazyObjectProperty: + CreateLazyObjectProperty(Index); + break; + case ESerializedObjectType::EAssetObjectProperty: + CreateAssetObjectProperty(Index); + break; + case ESerializedObjectType::EAssetClassProperty: + CreateAssetClassProperty(Index); + break; + case ESerializedObjectType::EInterfaceProperty: + CreateInterfaceProperty(Index); + break; + case ESerializedObjectType::EStructProperty: + CreateStructProperty(Index); + break; + case ESerializedObjectType::EMapProperty: + CreateMapProperty(Index); + break; + case ESerializedObjectType::EArrayProperty: + CreateArrayProperty(Index); + break; + case ESerializedObjectType::EFunction: + CreateFunction(Index); + break; + case ESerializedObjectType::EDelegateFunction: + CreateDelegateFunction(Index); + break; + case ESerializedObjectType::EFileScope: + // Intentionally empty, file scopes are created with UnrealSourceFiles + // CreateFileScope(Index); + break; + case ESerializedObjectType::EStructScope: + CreateStructScope(Index); + break; + case ESerializedObjectType::EUnrealTypeDefinitionInfo: + CreateUnrealTypeDefinitionInfo(Index); + break; + case ESerializedObjectType::ETypeDefinitionInfoMapEntry: + CreateTypeDefinitionInfoMapEntry(Index); + break; + case ESerializedObjectType::EUnrealSourceFile: + CreateUnrealSourceFile(Index); + break; + case ESerializedObjectType::EMultipleInheritanceBaseClass: + CreateMultipleInheritanceBaseClass(Index); + break; + case ESerializedObjectType::EUnrealSourceFilesMapEntry: + CreateUnrealSourceFilesMapEntry(Index); + break; + case ESerializedObjectType::EPublicClassSetEntry: + // Intentionally empty. + break; + case ESerializedObjectType::EClassMetaData: + CreateClassMetaData(Index); + break; + case ESerializedObjectType::EGScriptHelperEntry: + // Intentionally empty + break; + case ESerializedObjectType::EToken: + CreateToken(Index); + break; + case ESerializedObjectType::EPropertyDataEntry: + CreatePropertyDataEntry(Index); + break; + case ESerializedObjectType::EEnumUnderlyingType: + CreateGEnumUnderlyingType(Index); + break; + case ESerializedObjectType::EStructNameMapEntry: + CreateStructNameMapEntry(Index); + break; + case ESerializedObjectType::EInterfaceAllocation: + CreateInterfaceAllocation(Index); + break; + default: + check(0); + break; + } +} + +void FUHTMakefile::ResolveObject(ESerializedObjectType ObjectType, int32 Index) +{ + switch (ObjectType) + { + case ESerializedObjectType::EPropertyBase: + ResolvePropertyBase(Index); + break; + case ESerializedObjectType::EPackage: + ResolvePackage(Index); + break; + case ESerializedObjectType::EClass: + ResolveClass(Index); + break; + case ESerializedObjectType::EEnum: + ResolveEnum(Index); + break; + case ESerializedObjectType::EStruct: + ResolveStruct(Index); + break; + case ESerializedObjectType::EScriptStruct: + ResolveScriptStruct(Index); + break; + case ESerializedObjectType::EProperty: + ResolveProperty(Index); + break; + case ESerializedObjectType::EByteProperty: + ResolveByteProperty(Index); + break; + case ESerializedObjectType::EInt8Property: + ResolveInt8Property(Index); + break; + case ESerializedObjectType::EInt16Property: + ResolveInt16Property(Index); + break; + case ESerializedObjectType::EIntProperty: + ResolveIntProperty(Index); + break; + case ESerializedObjectType::EInt64Property: + ResolveInt64Property(Index); + break; + case ESerializedObjectType::EUInt16Property: + ResolveUInt16Property(Index); + break; + case ESerializedObjectType::EUInt32Property: + ResolveUInt32Property(Index); + break; + case ESerializedObjectType::EUInt64Property: + ResolveUInt64Property(Index); + break; + case ESerializedObjectType::EFloatProperty: + ResolveFloatProperty(Index); + break; + case ESerializedObjectType::EDoubleProperty: + ResolveDoubleProperty(Index); + break; + case ESerializedObjectType::EBoolProperty: + ResolveBoolProperty(Index); + break; + case ESerializedObjectType::ENameProperty: + ResolveNameProperty(Index); + break; + case ESerializedObjectType::EStrProperty: + ResolveStrProperty(Index); + break; + case ESerializedObjectType::ETextProperty: + ResolveTextProperty(Index); + break; + case ESerializedObjectType::EDelegateProperty: + ResolveDelegateProperty(Index); + break; + case ESerializedObjectType::EMulticastDelegateProperty: + ResolveMulticastDelegateProperty(Index); + break; + case ESerializedObjectType::EObjectPropertyBase: + ResolveObjectPropertyBase(Index); + break; + case ESerializedObjectType::EClassProperty: + ResolveClassProperty(Index); + break; + case ESerializedObjectType::EObjectProperty: + ResolveObjectProperty(Index); + break; + case ESerializedObjectType::EWeakObjectProperty: + ResolveWeakObjectProperty(Index); + break; + case ESerializedObjectType::ELazyObjectProperty: + ResolveLazyObjectProperty(Index); + break; + case ESerializedObjectType::EAssetObjectProperty: + ResolveAssetObjectProperty(Index); + break; + case ESerializedObjectType::EAssetClassProperty: + ResolveAssetClassProperty(Index); + break; + case ESerializedObjectType::EInterfaceProperty: + ResolveInterfaceProperty(Index); + break; + case ESerializedObjectType::EStructProperty: + ResolveStructProperty(Index); + break; + case ESerializedObjectType::EMapProperty: + ResolveMapProperty(Index); + break; + case ESerializedObjectType::EArrayProperty: + ResolveArrayProperty(Index); + break; + case ESerializedObjectType::EFunction: + ResolveFunction(Index); + break; + case ESerializedObjectType::EDelegateFunction: + ResolveDelegateFunction(Index); + break; + case ESerializedObjectType::EFileScope: + ResolveFileScope(Index); + break; + case ESerializedObjectType::EStructScope: + ResolveStructScope(Index); + break; + case ESerializedObjectType::EScope: + ResolveScope(Index); + break; + case ESerializedObjectType::EUnrealTypeDefinitionInfo: + ResolveUnrealTypeDefinitionInfo(Index); + break; + case ESerializedObjectType::ETypeDefinitionInfoMapEntry: + ResolveTypeDefinitionInfoMapEntry(Index); + break; + case ESerializedObjectType::EUnrealSourceFile: + ResolveUnrealSourceFile(Index); + break; + case ESerializedObjectType::EGeneratedCodeCRC: + ResolveGeneratedCodeCRC(Index); + break; + case ESerializedObjectType::EGScriptHelperEntry: + ResolveGScriptHelperEntry(Index); + break; + case ESerializedObjectType::EMultipleInheritanceBaseClass: + ResolveMultipleInheritanceBaseClass(Index); + break; + case ESerializedObjectType::EPublicClassSetEntry: + ResolvePublicClassSetEntry(Index); + break; + case ESerializedObjectType::EClassMetaData: + ResolveClassMetaData(Index); + break; + case ESerializedObjectType::EToken: + ResolveToken(Index); + break; + case ESerializedObjectType::EPropertyDataEntry: + ResolvePropertyDataEntry(Index); + break; + case ESerializedObjectType::EUnrealSourceFilesMapEntry: + // Intentionally empty. + break; + case ESerializedObjectType::EEnumUnderlyingType: + ResolveGEnumUnderlyingType(Index); + break; + case ESerializedObjectType::EStructNameMapEntry: + ResolveStructNameMapEntry(Index); + break; + case ESerializedObjectType::EInterfaceAllocation: + // Intentionally empty. + break; + default: + check(0); + break; + } +} + +int32 FUHTMakefile::GetUnrealSourceFileIndex(const FUnrealSourceFile* UnrealSourceFile) const +{ + if (!UnrealSourceFile) + { + return INDEX_NONE; + } + + const int32* IndexPtr = UnrealSourceFileIndexes.Find(UnrealSourceFile); + int32 Result = 0; + if (IndexPtr) + { + return *IndexPtr; + } + Result += UnrealSourceFiles.Num(); + + int32 Index = NewUnrealSourceFiles.IndexOfByKey(UnrealSourceFile); + if (Index != INDEX_NONE) + { + return Result + Index; + } + + check(0); + return INDEX_NONE; +} + +FUnrealSourceFile* FUHTMakefile::GetUnrealSourceFileByIndex(int32 Index) const +{ + if (Index == INDEX_NONE) + { + return nullptr; + } + + check(UnrealSourceFiles.IsValidIndex(Index) && UnrealSourceFileProxies.IsValidIndex(Index)); + return UnrealSourceFiles[Index]; +} + +int32 FUHTMakefile::GetNameIndex(FName Name) const +{ + return NameIndices.FindChecked(Name); +} + +FName FUHTMakefile::GetNameByIndex(int32 Index) const +{ + if (Index == INDEX_NONE) + { + return NAME_None; + } + + check(ReferencedNames.IsValidIndex(Index)); + return ReferencedNames[Index]; +} + +FSerializeIndex FUHTMakefile::GetObjectIndex(const UObject* Object) const +{ + if (!Object) + { + return FSerializeIndex(); + } + + FSerializeIndex Result = FSerializeIndex(0); + + FSerializeIndex Index = FSerializeIndex(Packages.IndexOfByKey(Object)); + if (Index.Index != INDEX_NONE) + { + return Result + Index; + } + Result += Packages.Num(); + + Index = GetFieldIndex(Cast(Object)); + if (Index.Index != INDEX_NONE) + { + if (Index.Index == IndexOfNativeClass) + { + return Index; + } + return Result + Index; + } + + check(0); + return FSerializeIndex(); +} + +UObject* FUHTMakefile::GetObjectByIndex(FSerializeIndex Index) const +{ + if (Index.IsNone()) + { + return nullptr; + } + + if (Index.HasName()) + { + return FindObjectFast(nullptr, Index.NameProxy.CreateName(*this), true, true); + } + + if (PackageProxies.IsValidIndex(Index.Index)) + { + return Packages[Index.Index]; + } + Index -= PackageProxies.Num(); + + return GetFieldByIndex(Index); +} + +void FUHTMakefile::LoadModuleData(FName ModuleName, const FManifestModule& ManifestModule) +{ + if (IsPreloading()) + { + if (ModuleDescriptors.Num() == 0) + { + for (auto& Kvp : ModuleDescriptorsArchiveProxy) + { + ModuleDescriptors.Add(Kvp.Key.CreateName(*this), Kvp.Value); + } + } + + FUHTMakefileModuleDescriptor& UHTMakefileModuleDescriptor = ModuleDescriptors[ModuleName]; + UHTMakefileModuleDescriptor.SetMakefile(this); + } + ModuleDescriptors[ModuleName].LoadModuleData(ManifestModule, LoadingPhase); +} + +void FUHTMakefile::ResolvePublicClassSetEntry(int32 Index) +{ + UClass* Class = GetClassByIndex(PublicClassSetEntryProxies[Index]); + PublicClassSetEntries.Add(Class); + GPublicClassSet.Add(Class); +} + +void FUHTMakefile::CreateTypeDefinitionInfoMapEntry(int32 Index) +{ + TypeDefinitionInfoMap.Add(TPairInitializer(nullptr, nullptr)); +} + +void FUHTMakefile::ResolveTypeDefinitionInfoMapEntry(int32 Index) +{ + TypeDefinitionInfoMapArchiveProxy.ResolveIndex(*this, Index); + auto& Kvp = TypeDefinitionInfoMap[Index]; + GTypeDefinitionInfoMap.Add(Kvp.Key, Kvp.Value->AsShared()); +} + +void FUHTMakefile::ResolveTypeDefinitionInfoMapEntryPreload(int32 Index) +{ + TypeDefinitionInfoMapArchiveProxy.ResolveClassIndex(*this, Index); + auto& Kvp = TypeDefinitionInfoMap[Index]; + GTypeDefinitionInfoMap.Add(Kvp.Key, Kvp.Value->AsShared()); +} + + +int32 FUHTMakefile::GetPropertyDataEntryIndex(const TPair>* Entry) const +{ + if (!Entry) + { + return INDEX_NONE; + } + + int32 Result = PropertyDataEntries.IndexOfByPredicate([Entry](const TPair>& Kvp) { return Kvp.Key == Entry->Key; }); + check(Result != INDEX_NONE); + return Result; +} +const TPair>* FUHTMakefile::GetPropertyDataEntryByIndex(int32 Index) const +{ + if (Index == INDEX_NONE) + { + return nullptr; + } + + check(PropertyDataEntries.IsValidIndex(Index)); + return &PropertyDataEntries[Index]; +} + +FSerializeIndex FUHTMakefile::GetFieldIndex(const UField* Field) const +{ + if (!Field) + { + return FSerializeIndex(); + } + + FSerializeIndex Result = FSerializeIndex(0); + FSerializeIndex SerializeIndex = GetStructIndex(Cast(Field)); + if (SerializeIndex.Index != INDEX_NONE) + { + if (SerializeIndex.Index == IndexOfNativeClass) + { + return SerializeIndex; + } + return Result + SerializeIndex; + } + Result += GetStructsCount(); + + int32 Index = GetPropertyIndex(Cast(Field)); + if (Index != INDEX_NONE) + { + return Result + Index; + } + Result += GetPropertyCount(); + + Index = EnumIndexes.FindChecked(Cast(Field)); + if (Index != INDEX_NONE) + { + return Result + Index; + } + + check(0); + return FSerializeIndex(); +} + +UField* FUHTMakefile::GetFieldByIndex(FSerializeIndex Index) const +{ + if (Index.IsNone()) + { + return nullptr; + } + + if (Index.HasName()) + { + return FindObjectFast(nullptr, Index.NameProxy.CreateName(*this), true, true); + } + + UField* Result = GetStructByIndex(Index); + if (Result) + { + return Result; + } + Index -= GetStructProxiesCount(); + + Result = GetPropertyByIndex(Index.Index); + if (Result) + { + return Result; + } + Index -= GetPropertyProxiesCount(); + + if (Enums.IsValidIndex(Index.Index)) + { + return Enums[Index.Index]; + } + + return nullptr; +} + +int32 FUHTMakefile::GetEnumIndex(const UEnum* Enum) const +{ + if (!Enum) + { + return INDEX_NONE; + } + + return EnumIndexes.FindChecked(Enum); +} + +UEnum* FUHTMakefile::GetEnumByIndex(int32 Index) const +{ + if (Index == INDEX_NONE) + { + return nullptr; + } + + if (Enums.IsValidIndex(Index)) + { + return Enums[Index]; + } + + return nullptr; +} + +FSerializeIndex FUHTMakefile::GetStructIndex(const UStruct* Struct) const +{ + if (!Struct) + { + return FSerializeIndex(); + } + + FSerializeIndex Result = FSerializeIndex(0); + + const int32* IndexPtr = StructIndexes.Find(Struct); + if (IndexPtr) + { + return Result + *IndexPtr; + } + Result += Structs.Num(); + + FSerializeIndex Index = GetScriptStructIndex(Cast(Struct)); + if (Index != INDEX_NONE) + { + if (Index == IndexOfNativeClass) + { + return Index; + } + return Result + Index; + } + Result += ScriptStructs.Num(); + + Index = GetClassIndex(Cast(Struct)); + if (Index != INDEX_NONE) + { + if (Index == IndexOfNativeClass) + { + return Index; + } + return Result + Index; + } + Result += Classes.Num(); + + Index = FSerializeIndex(GetFunctionIndex(Cast(Struct))); + if (Index != INDEX_NONE) + { + return Result + Index; + } + + check(0); + return FSerializeIndex(); +} + +UStruct* FUHTMakefile::GetStructByIndex(FSerializeIndex Index) const +{ + if (Index.IsNone()) + { + return nullptr; + } + + if (Index.HasName()) + { + return FindObjectFast(nullptr, Index.NameProxy.CreateName(*this), true, true); + } + + if (Structs.IsValidIndex(Index.Index) && StructProxies.IsValidIndex(Index.Index)) + { + return Structs[Index.Index]; + } + Index -= StructProxies.Num(); + + if (ScriptStructs.IsValidIndex(Index.Index) && ScriptStructProxies.IsValidIndex(Index.Index)) + { + return ScriptStructs[Index.Index]; + } + Index -= ScriptStructProxies.Num(); + + if (Classes.IsValidIndex(Index.Index) && ClassProxies.IsValidIndex(Index.Index)) + { + return Classes[Index.Index]; + } + Index -= ClassProxies.Num(); + + return GetFunctionByIndex(Index.Index); +} + +int32 FUHTMakefile::GetPropertyIndex(const UProperty* Property) const +{ + if (!Property) + { + return INDEX_NONE; + } + int32 Result = 0; + const int32* CurrentIndex = PropertyIndexes.Find(Property); + if (CurrentIndex) + { + return *CurrentIndex; + } + Result += Properties.Num(); + + CurrentIndex = BytePropertyIndexes.Find(Cast(Property)); + if (CurrentIndex) + { + return Result + *CurrentIndex; + } + Result += ByteProperties.Num(); + + CurrentIndex = Int8PropertyIndexes.Find(Cast(Property)); + if (CurrentIndex) + { + return Result + *CurrentIndex; + } + Result += Int8Properties.Num(); + + CurrentIndex = Int16PropertyIndexes.Find(Cast(Property)); + if (CurrentIndex) + { + return Result + *CurrentIndex; + } + Result += Int16Properties.Num(); + + CurrentIndex = IntPropertyIndexes.Find(Cast(Property)); + if (CurrentIndex) + { + return Result + *CurrentIndex; + } + Result += IntProperties.Num(); + + CurrentIndex = Int64PropertyIndexes.Find(Cast(Property)); + if (CurrentIndex) + { + return Result + *CurrentIndex; + } + Result += Int64Properties.Num(); + + CurrentIndex = UInt16PropertyIndexes.Find(Cast(Property)); + if (CurrentIndex) + { + return Result + *CurrentIndex; + } + Result += UInt16Properties.Num(); + + CurrentIndex = UInt32PropertyIndexes.Find(Cast(Property)); + if (CurrentIndex) + { + return Result + *CurrentIndex; + } + Result += UInt32Properties.Num(); + + CurrentIndex = UInt64PropertyIndexes.Find(Cast(Property)); + if (CurrentIndex) + { + return Result + *CurrentIndex; + } + Result += UInt64Properties.Num(); + + CurrentIndex = FloatPropertyIndexes.Find(Cast(Property)); + if (CurrentIndex) + { + return Result + *CurrentIndex; + } + Result += FloatProperties.Num(); + + CurrentIndex = DoublePropertyIndexes.Find(Cast(Property)); + if (CurrentIndex) + { + return Result + *CurrentIndex; + } + Result += DoubleProperties.Num(); + + CurrentIndex = BoolPropertyIndexes.Find(Cast(Property)); + if (CurrentIndex) + { + return Result + *CurrentIndex; + } + Result += BoolProperties.Num(); + + CurrentIndex = NamePropertyIndexes.Find(Cast(Property)); + if (CurrentIndex) + { + return Result + *CurrentIndex; + } + Result += NameProperties.Num(); + + CurrentIndex = StrPropertyIndexes.Find(Cast(Property)); + if (CurrentIndex) + { + return Result + *CurrentIndex; + } + Result += StrProperties.Num(); + + CurrentIndex = TextPropertyIndexes.Find(Cast(Property)); + if (CurrentIndex) + { + return Result + *CurrentIndex; + } + Result += TextProperties.Num(); + + CurrentIndex = DelegatePropertyIndexes.Find(Cast(Property)); + if (CurrentIndex) + { + return Result + *CurrentIndex; + } + Result += DelegateProperties.Num(); + + CurrentIndex = MulticastDelegatePropertyIndexes.Find(Cast(Property)); + if (CurrentIndex) + { + return Result + *CurrentIndex; + } + Result += MulticastDelegateProperties.Num(); + + CurrentIndex = ObjectPropertyBaseIndexes.Find(Cast(Property)); + if (CurrentIndex) + { + return Result + *CurrentIndex; + } + Result += ObjectPropertyBases.Num(); + + CurrentIndex = ClassPropertyIndexes.Find(Cast(Property)); + if (CurrentIndex) + { + return Result + *CurrentIndex; + } + Result += ClassProperties.Num(); + + CurrentIndex = ObjectPropertyIndexes.Find(Cast(Property)); + if (CurrentIndex) + { + return Result + *CurrentIndex; + } + Result += ObjectProperties.Num(); + + CurrentIndex = WeakObjectPropertyIndexes.Find(Cast(Property)); + if (CurrentIndex) + { + return Result + *CurrentIndex; + } + Result += WeakObjectProperties.Num(); + + CurrentIndex = LazyObjectPropertyIndexes.Find(Cast(Property)); + if (CurrentIndex) + { + return Result + *CurrentIndex; + } + Result += LazyObjectProperties.Num(); + + CurrentIndex = AssetObjectPropertyIndexes.Find(Cast(Property)); + if (CurrentIndex) + { + return Result + *CurrentIndex; + } + Result += AssetObjectProperties.Num(); + + CurrentIndex = AssetClassPropertyIndexes.Find(Cast(Property)); + if (CurrentIndex) + { + return Result + *CurrentIndex; + } + Result += AssetClassProperties.Num(); + + CurrentIndex = InterfacePropertyIndexes.Find(Cast(Property)); + if (CurrentIndex) + { + return Result + *CurrentIndex; + } + Result += InterfaceProperties.Num(); + + CurrentIndex = StructPropertyIndexes.Find(Cast(Property)); + if (CurrentIndex) + { + return Result + *CurrentIndex; + } + Result += StructProperties.Num(); + + CurrentIndex = MapPropertyIndexes.Find(Cast(Property)); + if (CurrentIndex) + { + return Result + *CurrentIndex; + } + Result += MapProperties.Num(); + + CurrentIndex = ArrayPropertyIndexes.Find(Cast(Property)); + if (CurrentIndex) + { + return Result + *CurrentIndex; + } + + check(0); + return INDEX_NONE; +} + +UProperty* FUHTMakefile::GetPropertyByIndex(int32 Index) const +{ + if (Index == INDEX_NONE) + { + return nullptr; + } + + if (Properties.IsValidIndex(Index) && PropertyProxies.IsValidIndex(Index)) + { + return Properties[Index]; + } + Index -= PropertyProxies.Num(); + + if (ByteProperties.IsValidIndex(Index) && BytePropertyProxies.IsValidIndex(Index)) + { + return ByteProperties[Index]; + } + Index -= BytePropertyProxies.Num(); + + if (Int8Properties.IsValidIndex(Index) && Int8PropertyProxies.IsValidIndex(Index)) + { + return Int8Properties[Index]; + } + Index -= Int8PropertyProxies.Num(); + + if (Int16Properties.IsValidIndex(Index) && Int16PropertyProxies.IsValidIndex(Index)) + { + return Int16Properties[Index]; + } + Index -= Int16PropertyProxies.Num(); + + if (IntProperties.IsValidIndex(Index) && IntPropertyProxies.IsValidIndex(Index)) + { + return IntProperties[Index]; + } + Index -= IntPropertyProxies.Num(); + + if (Int64Properties.IsValidIndex(Index) && Int64PropertyProxies.IsValidIndex(Index)) + { + return Int64Properties[Index]; + } + Index -= Int64PropertyProxies.Num(); + + if (UInt16Properties.IsValidIndex(Index) && UInt16PropertyProxies.IsValidIndex(Index)) + { + return UInt16Properties[Index]; + } + Index -= UInt16PropertyProxies.Num(); + + if (UInt32Properties.IsValidIndex(Index) && UInt32PropertyProxies.IsValidIndex(Index)) + { + return UInt32Properties[Index]; + } + Index -= UInt32PropertyProxies.Num(); + + if (UInt64Properties.IsValidIndex(Index) && UInt64PropertyProxies.IsValidIndex(Index)) + { + return UInt64Properties[Index]; + } + Index -= UInt64PropertyProxies.Num(); + + if (FloatProperties.IsValidIndex(Index) && FloatPropertyProxies.IsValidIndex(Index)) + { + return FloatProperties[Index]; + } + Index -= FloatPropertyProxies.Num(); + + if (DoubleProperties.IsValidIndex(Index) && DoublePropertyProxies.IsValidIndex(Index)) + { + return DoubleProperties[Index]; + } + Index -= DoublePropertyProxies.Num(); + + if (BoolProperties.IsValidIndex(Index) && BoolPropertyProxies.IsValidIndex(Index)) + { + return BoolProperties[Index]; + } + Index -= BoolPropertyProxies.Num(); + + if (NameProperties.IsValidIndex(Index) && NamePropertyProxies.IsValidIndex(Index)) + { + return NameProperties[Index]; + } + Index -= NamePropertyProxies.Num(); + + if (StrProperties.IsValidIndex(Index) && StrPropertyProxies.IsValidIndex(Index)) + { + return StrProperties[Index]; + } + Index -= StrPropertyProxies.Num(); + + if (TextProperties.IsValidIndex(Index) && TextPropertyProxies.IsValidIndex(Index)) + { + return TextProperties[Index]; + } + Index -= TextPropertyProxies.Num(); + + if (DelegateProperties.IsValidIndex(Index) && DelegatePropertyProxies.IsValidIndex(Index)) + { + return DelegateProperties[Index]; + } + Index -= DelegatePropertyProxies.Num(); + + if (MulticastDelegateProperties.IsValidIndex(Index) && MulticastDelegatePropertyProxies.IsValidIndex(Index)) + { + return MulticastDelegateProperties[Index]; + } + Index -= MulticastDelegatePropertyProxies.Num(); + + if (ObjectPropertyBases.IsValidIndex(Index) && ObjectPropertyBaseProxies.IsValidIndex(Index)) + { + return ObjectPropertyBases[Index]; + } + Index -= ObjectPropertyBaseProxies.Num(); + + if (ClassProperties.IsValidIndex(Index) && ClassPropertyProxies.IsValidIndex(Index)) + { + return ClassProperties[Index]; + } + Index -= ClassPropertyProxies.Num(); + + if (ObjectProperties.IsValidIndex(Index) && ObjectPropertyProxies.IsValidIndex(Index)) + { + return ObjectProperties[Index]; + } + Index -= ObjectPropertyProxies.Num(); + + if (WeakObjectProperties.IsValidIndex(Index) && WeakObjectPropertyProxies.IsValidIndex(Index)) + { + return WeakObjectProperties[Index]; + } + Index -= WeakObjectPropertyProxies.Num(); + + if (LazyObjectProperties.IsValidIndex(Index) && LazyObjectPropertyProxies.IsValidIndex(Index)) + { + return LazyObjectProperties[Index]; + } + Index -= LazyObjectPropertyProxies.Num(); + + if (AssetObjectProperties.IsValidIndex(Index) && AssetObjectPropertyProxies.IsValidIndex(Index)) + { + return AssetObjectProperties[Index]; + } + Index -= AssetObjectPropertyProxies.Num(); + + if (AssetClassProperties.IsValidIndex(Index) && AssetClassPropertyProxies.IsValidIndex(Index)) + { + return AssetClassProperties[Index]; + } + Index -= AssetClassPropertyProxies.Num(); + + if (InterfaceProperties.IsValidIndex(Index) && InterfacePropertyProxies.IsValidIndex(Index)) + { + return InterfaceProperties[Index]; + } + Index -= InterfacePropertyProxies.Num(); + + if (StructProperties.IsValidIndex(Index) && StructPropertyProxies.IsValidIndex(Index)) + { + return StructProperties[Index]; + } + Index -= StructPropertyProxies.Num(); + + if (MapProperties.IsValidIndex(Index) && MapPropertyProxies.IsValidIndex(Index)) + { + return MapProperties[Index]; + } + Index -= MapPropertyProxies.Num(); + + if (ArrayProperties.IsValidIndex(Index) && ArrayPropertyProxies.IsValidIndex(Index)) + { + return ArrayProperties[Index]; + } + + return nullptr; +} + +FSerializeIndex FUHTMakefile::GetScriptStructIndex(const UScriptStruct* ScriptStruct) const +{ + if (!ScriptStruct) + { + return FSerializeIndex(); + } + + //if (ScriptStruct->HasAnyClassFlags(CLASS_Native)) + //{ + // return FSerializeIndex(*this, ScriptStruct->GetFName()); + //} + + const int32* Index = ScriptStructIndexes.Find(ScriptStruct); + return FSerializeIndex(Index ? *Index : INDEX_NONE); +} + +UScriptStruct* FUHTMakefile::GetScriptStructByIndex(FSerializeIndex Index) const +{ + if (Index.IsNone()) + { + return nullptr; + } + + if (Index.HasName()) + { + return FindObjectFast(nullptr, Index.NameProxy.CreateName(*this), true, true); + } + + if (ScriptStructs.IsValidIndex(Index.Index) && ScriptStructProxies.IsValidIndex(Index.Index)) + { + return ScriptStructs[Index.Index]; + } + + return nullptr; +} + +int32 FUHTMakefile::GetPropertyBaseIndex(FPropertyBase* PropertyBase) const +{ + if (!PropertyBase) + { + return INDEX_NONE; + } + + int32 Result = 0; + const int32* IndexPtr = PropertyBaseIndexes.Find(PropertyBase); + if (IndexPtr) + { + return *IndexPtr; + } + Result += PropertyBases.Num(); + + return Result + GetTokenIndex(static_cast(PropertyBase)); +} + +FPropertyBase* FUHTMakefile::GetPropertyBaseByIndex(int32 Index) const +{ + if (Index == INDEX_NONE) + { + return nullptr; + } + + if (PropertyBases.IsValidIndex(Index)) + { + return PropertyBases[Index]; + } + Index -= PropertyBaseProxies.Num(); + + if (Tokens.IsValidIndex(Index)) + { + return Tokens[Index]; + } + + check(0); + return nullptr; +} + +FSerializeIndex FUHTMakefile::GetClassIndex(const UClass* Class) const +{ + if (!Class) + { + return FSerializeIndex(); + } + + if (Class->HasAnyClassFlags(CLASS_Native)) + { + return FSerializeIndex(*this, Class->GetFName()); + } + + return FSerializeIndex(ClassIndexes.FindChecked(Class)); +} + +UClass* FUHTMakefile::GetClassByIndex(FSerializeIndex Index) const +{ + if (Index.IsNone()) + { + return nullptr; + } + + if (Index.HasName()) + { + return FindObjectFast(nullptr, Index.NameProxy.CreateName(*this), true, true); + } + + if (Classes.IsValidIndex(Index.Index)) + { + return Classes[Index.Index]; + } + + return nullptr; +} + +int32 FUHTMakefile::GetFunctionIndex(const UFunction* Function) const +{ + if (!Function) + { + return INDEX_NONE; + } + + int32 Result = 0; + const int32* Index = FunctionIndexes.Find(Function); + if (Index) + { + return Result + *Index; + } + Result += Functions.Num(); + + Index = DelegateFunctionIndexes.Find(Cast(Function)); + if (Index) + { + return Result + *Index; + } + + check(0); + return INDEX_NONE; +} + +UFunction* FUHTMakefile::GetFunctionByIndex(int32 Index) const +{ + if (Index == INDEX_NONE) + { + return nullptr; + } + + if (Functions.IsValidIndex(Index)) + { + return Functions[Index]; + } + Index -= FunctionProxies.Num(); + + if (DelegateFunctions.IsValidIndex(Index)) + { + return DelegateFunctions[Index]; + } + Index -= DelegateFunctionProxies.Num(); + + return nullptr; +} + +int32 FUHTMakefile::GetFileScopeIndex(const FFileScope* FileScope) const +{ + if (!FileScope) + { + return INDEX_NONE; + } + + return FileScopeIndexes.FindChecked(FileScope); +} + +int32 FUHTMakefile::GetUnrealTypeDefinitionInfoIndex(FUnrealTypeDefinitionInfo* Info) const +{ + if (!Info) + { + return INDEX_NONE; + } + return UnrealTypeDefinitionInfoIndexes.FindChecked(Info); +} + +FFileScope* FUHTMakefile::GetFileScopeByIndex(int32 Index) const +{ + check(FileScopes.IsValidIndex(Index)); + return FileScopes[Index]; +} + +FUnrealTypeDefinitionInfo* FUHTMakefile::GetUnrealTypeDefinitionInfoByIndex(int32 Index) const +{ + check(UnrealTypeDefinitionInfos.IsValidIndex(Index)); + return UnrealTypeDefinitionInfos[Index]; +} + +int32 FUHTMakefile::GetScopeIndex(const FScope* Scope) const +{ + if (!Scope) + { + return INDEX_NONE; + } + + int32 Result = 0; + const int32* IndexPtr = ScopeIndexes.Find(Scope); + if (IndexPtr) + { + return Result + *IndexPtr; + } + Result += Scopes.Num(); + + int32 Index = GetStructScopeIndex(static_cast(Scope)); + if (Index != INDEX_NONE) + { + return Result + Index; + } + Result += StructScopes.Num(); + + Index = GetFileScopeIndex(static_cast(Scope)); + if (Index != INDEX_NONE) + { + return Result + Index; + } + + check(0); + return INDEX_NONE; +} + +FScope* FUHTMakefile::GetScopeByIndex(int32 Index) const +{ + if (Index == INDEX_NONE) + { + return nullptr; + } + + if (Scopes.IsValidIndex(Index) && ScopeProxies.IsValidIndex(Index)) + { + return Scopes[Index]; + } + Index -= ScopeProxies.Num(); + + if (StructScopes.IsValidIndex(Index) && StructScopeProxies.IsValidIndex(Index)) + { + return StructScopes[Index]; + } + Index -= StructScopeProxies.Num(); + + if (FileScopes.IsValidIndex(Index) && FileScopeProxies.IsValidIndex(Index)) + { + return FileScopes[Index]; + } + + check(0); + return new FStructScope(nullptr, nullptr); +} + +int32 FUHTMakefile::GetStructScopeIndex(const FStructScope* StructScope) const +{ + if (!StructScope) + { + return INDEX_NONE; + } + + const int32* Result = StructScopeIndexes.Find(StructScope); + return Result ? *Result : INDEX_NONE; +} + +FStructScope* FUHTMakefile::GetStructScopeByIndex(int32 Index) const +{ + check(StructScopes.IsValidIndex(Index)); + return StructScopes[Index]; +} + +void FUHTMakefile::SetupAndSaveReferencedNames(FArchive& Ar) +{ + TArray NativeClasses; + GetObjectsOfClass(UClass::StaticClass(), NativeClasses); + + for (UObject* Object : NativeClasses) + { + UClass* Class = static_cast(Object); + FClassArchiveProxy::AddReferencedNames(Class, *this); + } + + for (auto& Kvp : ModuleDescriptors) + { + AddName(Kvp.Key); + } + + for (UPackage* Package : Packages) + { + FPackageArchiveProxy::AddReferencedNames(Package, *this); + } + + for (UClass* Class : Classes) + { + FClassArchiveProxy::AddReferencedNames(Class, *this); + } + + for (UEnum* Enum : Enums) + { + FEnumArchiveProxy::AddReferencedNames(Enum, *this); + } + + for (UStruct* Struct : Structs) + { + FStructArchiveProxy::AddReferencedNames(Struct, *this); + } + + for (UScriptStruct* ScriptStruct : ScriptStructs) + { + FScriptStructArchiveProxy::AddReferencedNames(ScriptStruct, *this); + } + + for (UProperty* Property : Properties) + { + FPropertyArchiveProxy::AddReferencedNames(Property, *this); + } + + for (UByteProperty* ByteProperty : ByteProperties) + { + FObjectBaseArchiveProxy::AddReferencedNames(ByteProperty, *this); + } + + for (UInt8Property* Int8Property : Int8Properties) + { + FInt8PropertyArchiveProxy::AddReferencedNames(Int8Property, *this); + } + + for (UInt16Property* Int16Property : Int16Properties) + { + FInt16PropertyArchiveProxy::AddReferencedNames(Int16Property, *this); + } + + for (UIntProperty* IntProperty : IntProperties) + { + FIntPropertyArchiveProxy::AddReferencedNames(IntProperty, *this); + } + + for (UInt64Property* Int64Property : Int64Properties) + { + FInt64PropertyArchiveProxy::AddReferencedNames(Int64Property, *this); + } + + for (UUInt16Property* UInt16Property : UInt16Properties) + { + FUInt16PropertyArchiveProxy::AddReferencedNames(UInt16Property, *this); + } + + for (UUInt32Property* UInt32Property : UInt32Properties) + { + FUInt32PropertyArchiveProxy::AddReferencedNames(UInt32Property, *this); + } + + for (UUInt64Property* UInt64Property : UInt64Properties) + { + FUInt64PropertyArchiveProxy::AddReferencedNames(UInt64Property, *this); + } + + for (UFloatProperty* FloatProperty : FloatProperties) + { + FFloatPropertyArchiveProxy::AddReferencedNames(FloatProperty, *this); + } + + for (UDoubleProperty* DoubleProperty : DoubleProperties) + { + FDoublePropertyArchiveProxy::AddReferencedNames(DoubleProperty, *this); + } + + for (UBoolProperty* BoolProperty : BoolProperties) + { + FBoolPropertyArchiveProxy::AddReferencedNames(BoolProperty, *this); + } + + for (UNameProperty* NameProperty : NameProperties) + { + FNamePropertyArchiveProxy::AddReferencedNames(NameProperty, *this); + } + + for (UStrProperty* StrProperty : StrProperties) + { + FStrPropertyArchiveProxy::AddReferencedNames(StrProperty, *this); + } + + for (UTextProperty* TextProperty : TextProperties) + { + FTextPropertyArchiveProxy::AddReferencedNames(TextProperty, *this); + } + + for (UDelegateProperty* DelegateProperty : DelegateProperties) + { + FDelegatePropertyArchiveProxy::AddReferencedNames(DelegateProperty, *this); + } + + for (UMulticastDelegateProperty* MulticastDelegateProperty : MulticastDelegateProperties) + { + FMulticastDelegatePropertyArchiveProxy::AddReferencedNames(MulticastDelegateProperty, *this); + } + + for (UObjectPropertyBase* ObjectPropertyBase : ObjectPropertyBases) + { + FObjectPropertyBaseArchiveProxy::AddReferencedNames(ObjectPropertyBase, *this); + } + + for (UClassProperty* ClassProperty : ClassProperties) + { + FClassPropertyArchiveProxy::AddReferencedNames(ClassProperty, *this); + } + + for (UObjectProperty* ObjectProperty : ObjectProperties) + { + FObjectPropertyArchiveProxy::AddReferencedNames(ObjectProperty, *this); + } + + for (UWeakObjectProperty* WeakObjectProperty : WeakObjectProperties) + { + FWeakObjectPropertyArchiveProxy::AddReferencedNames(WeakObjectProperty, *this); + } + + for (ULazyObjectProperty* LazyObjectProperty : LazyObjectProperties) + { + FLazyObjectPropertyArchiveProxy::AddReferencedNames(LazyObjectProperty, *this); + } + + for (UAssetObjectProperty* AssetObjectProperty : AssetObjectProperties) + { + FAssetObjectPropertyArchiveProxy::AddReferencedNames(AssetObjectProperty, *this); + } + + for (UAssetClassProperty* AssetClassProperty : AssetClassProperties) + { + FAssetClassPropertyArchiveProxy::AddReferencedNames(AssetClassProperty, *this); + } + + for (UInterfaceProperty* InterfaceProperty : InterfaceProperties) + { + FInterfacePropertyArchiveProxy::AddReferencedNames(InterfaceProperty, *this); + } + + for (UStructProperty* StructProperty : StructProperties) + { + FStructPropertyArchiveProxy::AddReferencedNames(StructProperty, *this); + } + + for (UMapProperty* MapProperty : MapProperties) + { + FMapPropertyArchiveProxy::AddReferencedNames(MapProperty, *this); + } + + for (UArrayProperty* ArrayProperty : ArrayProperties) + { + FArrayPropertyArchiveProxy::AddReferencedNames(ArrayProperty, *this); + } + + for (UFunction* Function : Functions) + { + FFunctionArchiveProxy::AddReferencedNames(Function, *this); + } + + for (UDelegateFunction* DelegateFunction : DelegateFunctions) + { + FDelegateFunctionArchiveProxy::AddReferencedNames(DelegateFunction, *this); + } + + for (FFileScope* FileScope : FileScopes) + { + FFileScopeArchiveProxy::AddReferencedNames(FileScope, *this); + } + + CompilerMetadataManagerArchiveProxy.AddReferencedNames(&GScriptHelper, *this); + + for (FMultipleInheritanceBaseClass* MultipleInheritanceBaseClass : MultipleInheritanceBaseClasses) + { + FMultipleInheritanceBaseClassArchiveProxy::AddReferencedNames(MultipleInheritanceBaseClass, *this); + } + + for (FClassMetaData* ClassMetaData : ClassMetaDatas) + { + FClassMetaDataArchiveProxy::AddReferencedNames(ClassMetaData, *this); + } + + for (FToken* Token : Tokens) + { + FTokenArchiveProxy::AddReferencedNames(Token, *this); + } + + ReferencedNames.Empty(ReferencedNamesSet.Num()); + NameIndices.Empty(ReferencedNamesSet.Num()); + for (FName Name : ReferencedNamesSet) + { + ReferencedNames.Add(Name); + NameIndices.Add(Name, NameIndices.Num()); + } + int32 NameCount = NameIndices.Num(); + Ar << NameCount; + + for (FName Name : ReferencedNames) + { + Ar << *const_cast(Name.GetDisplayNameEntry()); + } + +} + +void FUHTMakefile::LoadAndSetupReferencedNames(FArchive& Ar) +{ + int32 NameCount; + Ar << NameCount; + for (int32 i = 0; i < NameCount; i++) + { + FNameEntry NameEntry(ENAME_LinkerConstructor); + Ar << NameEntry; + + FName Name = NameEntry.IsWide() ? FName(ENAME_LinkerConstructor, NameEntry.GetWideName()) : FName(ENAME_LinkerConstructor, NameEntry.GetAnsiName()); + NameIndices.Add(Name, NameIndices.Num()); + ReferencedNames.Add(Name); + } +} + +FArchive& operator<<(FArchive& Ar, FUHTMakefile& UHTMakefile) +{ + if (Ar.IsSaving()) + { + FMetadataArchiveProxy::AddStaticallyReferencedNames(UHTMakefile); + UHTMakefile.SetupAndSaveReferencedNames(Ar); + UHTMakefile.MoveNewObjects(); + UHTMakefile.SetupProxies(); + } + else if (Ar.IsLoading()) + { + UHTMakefile.LoadAndSetupReferencedNames(Ar); + } + Ar << *UHTMakefile.Manifest; + Ar << UHTMakefile.InitializationOrder; + Ar << UHTMakefile.RawFilenames; + Ar << UHTMakefile.PublicClassSetEntryProxies; + Ar << UHTMakefile.Version; + Ar << UHTMakefile.CompilerMetadataManagerArchiveProxy; + Ar << UHTMakefile.ModuleNamesProxy; + Ar << UHTMakefile.ModuleDescriptorsArchiveProxy; + Ar << UHTMakefile.PackageProxies; + Ar << UHTMakefile.ClassProxies; + Ar << UHTMakefile.StructProxies; + Ar << UHTMakefile.GeneratedCodeCRCProxies; + Ar << UHTMakefile.ScriptStructProxies; + Ar << UHTMakefile.EnumProxies; + Ar << UHTMakefile.UnrealSourceFileProxies; + Ar << UHTMakefile.FileScopeProxies; + Ar << UHTMakefile.StructScopeProxies; + Ar << UHTMakefile.ScopeProxies; + Ar << UHTMakefile.UnrealTypeDefinitionInfoProxies; + Ar << UHTMakefile.TypeDefinitionInfoMapArchiveProxy; + Ar << UHTMakefile.PropertyProxies; + Ar << UHTMakefile.BytePropertyProxies; + Ar << UHTMakefile.Int8PropertyProxies; + Ar << UHTMakefile.Int16PropertyProxies; + Ar << UHTMakefile.IntPropertyProxies; + Ar << UHTMakefile.Int64PropertyProxies; + Ar << UHTMakefile.UInt16PropertyProxies; + Ar << UHTMakefile.UInt32PropertyProxies; + Ar << UHTMakefile.UInt64PropertyProxies; + Ar << UHTMakefile.FloatPropertyProxies; + Ar << UHTMakefile.DoublePropertyProxies; + Ar << UHTMakefile.BoolPropertyProxies; + Ar << UHTMakefile.NamePropertyProxies; + Ar << UHTMakefile.StrPropertyProxies; + Ar << UHTMakefile.TextPropertyProxies; + Ar << UHTMakefile.DelegatePropertyProxies; + Ar << UHTMakefile.MulticastDelegatePropertyProxies; + Ar << UHTMakefile.ObjectPropertyBaseProxies; + Ar << UHTMakefile.ClassPropertyProxies; + Ar << UHTMakefile.ObjectPropertyProxies; + Ar << UHTMakefile.WeakObjectPropertyProxies; + Ar << UHTMakefile.LazyObjectPropertyProxies; + Ar << UHTMakefile.AssetObjectPropertyProxies; + Ar << UHTMakefile.AssetClassPropertyProxies; + Ar << UHTMakefile.InterfacePropertyProxies; + Ar << UHTMakefile.StructPropertyProxies; + Ar << UHTMakefile.MapPropertyProxies; + Ar << UHTMakefile.ArrayPropertyProxies; + Ar << UHTMakefile.DelegateFunctionProxies; + Ar << UHTMakefile.FunctionProxies; + Ar << UHTMakefile.UnrealSourceFilesMapEntryProxies; + Ar << UHTMakefile.ClassMetaDataArchiveProxies; + Ar << UHTMakefile.TokenProxies; + Ar << UHTMakefile.PropertyBaseProxies; + Ar << UHTMakefile.PropertyDataEntryProxies; + Ar << UHTMakefile.MultipleInheritanceBaseClassProxies; + Ar << UHTMakefile.EnumUnderlyingTypeProxies; + Ar << UHTMakefile.StructNameMapEntryProxies; + Ar << UHTMakefile.InterfaceAllocationProxies; + + if (Ar.IsLoading()) + { + UHTMakefile.ResolveModuleNames(); + } + + return Ar; +} + +FUnrealTypeDefinitionInfoArchiveProxy::FUnrealTypeDefinitionInfoArchiveProxy(const FUHTMakefile& UHTMakefile, const FUnrealTypeDefinitionInfo* UnrealTypeDefinitionInfo) +{ + LineNumber = UnrealTypeDefinitionInfo->GetLineNumber(); + UnrealSourceFileIndex = UHTMakefile.GetUnrealSourceFileIndex(&UnrealTypeDefinitionInfo->GetUnrealSourceFile()); +} + +void FUnrealTypeDefinitionInfoArchiveProxy::Resolve(FUnrealTypeDefinitionInfo*& UnrealTypeDefinitionInfo, const FUHTMakefile& UHTMakefile) const +{ + UnrealTypeDefinitionInfo = new FUnrealTypeDefinitionInfo(*UHTMakefile.GetUnrealSourceFileByIndex(UnrealSourceFileIndex), LineNumber); +} + +FUnrealTypeDefinitionInfo* FUnrealTypeDefinitionInfoArchiveProxy::CreateUnrealTypeDefinitionInfo(const FUHTMakefile& UHTMakefile) const +{ + return nullptr; +} diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/UHTMakefile.h b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/UHTMakefile.h new file mode 100644 index 000000000000..e71f6ebb8fa8 --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/UHTMakefile.h @@ -0,0 +1,1589 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#pragma once +#include "UHTMakefile/ScriptStructArchiveProxy.h" +#include "UHTMakefile/PropertyArchiveProxy.h" +#include "UHTMakefile/FunctionArchiveProxy.h" +#include "UHTMakefile/EnumArchiveProxy.h" +#include "UHTMakefile/PackageArchiveProxy.h" +#include "UHTMakefile/UnrealSourceFileArchiveProxy.h" +#include "UHTMakefile/ScopeArchiveProxy.h" +#include "UHTMakefile/FileScopeArchiveProxy.h" +#include "UHTMakefile/StructScopeArchiveProxy.h" +#include "UHTMakefile/ScopeArchiveProxy.h" +#include "UHTMakefile/ClassArchiveProxy.h" +#include "UHTMakefile/UHTMakefileModuleDescriptor.h" +#include "UHTMakefile/TypeDefinitionInfoMapArchiveProxy.h" +#include "UHTMakefile/PropertyBaseArchiveProxy.h" +#include "UHTMakefile/CompilerMetadataManagerArchiveProxy.h" + +/** + * UHT makefiles overview. + * + * Generating makefile + * UHT makefiles serve as cache for internal UHT data generated while parsing modules. + * Whenever a piece of data relevant for code generation is created, it's added to + * FUHTMakefile instance along with module and source file within which said piece + * is created and initialization order. Each type has separate array in which it's stored, + * base and derived classes are stored separately. + * Apart from data pointers, UHT makefiles store ModuleDescriptors, which are containers + * for HeaderDescriptors. Each header descriptor contains indexes of objects in order of + * initialization for each UHT phase - preparsing, full parsing, exporting. + * + * Saving makefile + * All objects stored within makefile have their corresponding proxy structs, which are + * serialized PODs. All data stored as value types in objects are copied to proxies and + * pointers are converted to indexes within FUHTMakefile arrays. Saving FUHTMakefile + * serializes all proxies, module descriptors and FManifest used to create FUHTMakefile. + * + * Loading makefile + * UHT makefile deserializes all proxy objects and FManifest. There are two steps determining + * what can be loaded from makefile: + * 1. Deserialized manifest is compared on per-module basis with one provided by UBT. + * All modules that are fully matching can be loaded from makefile until first + * non-matching one is found. All subsequent modules after first non-matching one + * must be parsed regularly. + * 2. All headers from manifest are checked for modifications against timestamps from previous + * UHT runs. If at least one header is changed, module can't be loaded from makefile + * and all subsequent modules are marked as needing regeneration. + * All modules that can be loaded are iterated on, creating objects according to + * initialization order. Each object is first created (which fills in value fields), and once all + * objects for module are created, they are resolved (which takes indexes from proxy structs + * and finds appropriate pointers). Loading is split to three phases to reflect the way data is + * generated. + */ +class FUHTMakefile; +class FUnrealSourceFile; +class FHeaderProvider; +class FFileScope; +class FUnrealTypeDefinitionInfo; +class FStructScope; +class FScope; +class FPropertyBase; +struct FManifestModule; +struct FManifest; + +inline FArchive& operator<<(FArchive& Ar, ESerializedObjectType& ObjectType) +{ + if (Ar.IsLoading()) + { + uint32 ObjectTypeUInt32; + Ar << ObjectTypeUInt32; + ObjectType = (ESerializedObjectType)ObjectTypeUInt32; + } + else if (Ar.IsSaving()) + { + uint32 ObjectTypeUInt32 = (uint32)ObjectType; + Ar << ObjectTypeUInt32; + } + + return Ar; +} + +struct FUnrealTypeDefinitionInfoArchiveProxy +{ + FUnrealTypeDefinitionInfoArchiveProxy(const FUHTMakefile& UHTMakefile, const FUnrealTypeDefinitionInfo* UnrealTypeDefinitionInfo); + FUnrealTypeDefinitionInfoArchiveProxy() { } + + int32 LineNumber; + int32 UnrealSourceFileIndex; + + friend FArchive& operator<<(FArchive& Ar, FUnrealTypeDefinitionInfoArchiveProxy& UnrealTypeDefinitionInfoArchiveProxy) + { + Ar << UnrealTypeDefinitionInfoArchiveProxy.LineNumber; + Ar << UnrealTypeDefinitionInfoArchiveProxy.UnrealSourceFileIndex; + + return Ar; + } + + void Resolve(FUnrealTypeDefinitionInfo*& UnrealTypeDefinitionInfo, const FUHTMakefile& UHTMakefile) const; + FUnrealTypeDefinitionInfo* CreateUnrealTypeDefinitionInfo(const FUHTMakefile& UHTMakefile) const; +}; + +class FUHTMakefile +{ +public: + FUHTMakefile() + : Version(CurrentVersion) + , LoadingPhase(EUHTMakefileLoadingPhase::Max) + , bShouldMoveNewObjects(false) + , bShouldForceRegeneration(false) + { + InitializationOrder.AddDefaulted(+EUHTMakefileLoadingPhase::Max); + } + + void StartPreloading() + { + LoadingPhase = EUHTMakefileLoadingPhase::Preload; + } + void StopPreloading() + { + LoadingPhase = EUHTMakefileLoadingPhase::Max; + + for (auto& Kvp : ModuleDescriptors) + { + Kvp.Value.StopPreloading(); + } + } + bool IsPreloading() + { + return LoadingPhase == EUHTMakefileLoadingPhase::Preload; + } + void StartLoading() + { + LoadingPhase = EUHTMakefileLoadingPhase::Load; + + for (auto& Kvp : ModuleDescriptors) + { + Kvp.Value.StartLoading(); + } + } + void StopLoading() + { + LoadingPhase = EUHTMakefileLoadingPhase::Max; + + for (auto& Kvp : ModuleDescriptors) + { + Kvp.Value.StopLoading(); + } + } + bool IsLoading() + { + return LoadingPhase == EUHTMakefileLoadingPhase::Load; + } + void StartExporting() + { + LoadingPhase = EUHTMakefileLoadingPhase::Export; + + for (auto& Kvp : ModuleDescriptors) + { + Kvp.Value.StartExporting(); + } + } + void StopExporting() + { + LoadingPhase = EUHTMakefileLoadingPhase::Max; + + for (auto& Kvp : ModuleDescriptors) + { + Kvp.Value.StopExporting(); + } + } + bool IsExporting() + { + return LoadingPhase == EUHTMakefileLoadingPhase::Export; + } + + void SetShouldMoveNewObjects() + { + bShouldMoveNewObjects = true; + } + bool ShouldMoveNewObjects() const + { + return bShouldMoveNewObjects; + } + void MoveNewObjects(); + + void AddModule(FName ModuleName) + { + FUHTMakefileModuleDescriptor& ModuleDescriptor = ModuleDescriptors.FindOrAdd(ModuleName); + ModuleDescriptor.Reset(); + ModuleDescriptor.SetMakefile(this); + if (!ModuleNames.Contains(ModuleName)) + { + ModuleNames.Add(ModuleName); + } + bShouldForceRegeneration = true; + } + bool HasModule(FName ModuleName) const + { + return ModuleNames.Contains(ModuleName); + } + void LoadModuleData(FName ModuleName, const FManifestModule& ManifestModule); + + void AddPackage(UPackage* Package) + { + FUHTMakefileModuleDescriptor& CurrentModule = GetCurrentModule(); + int32 PackageIndex = Packages.Num() + NewPackages.Add(Package); + CurrentModule.SetPackageIndex(PackageIndex); + CurrentModule.SetPackage(Package); + PackageIndexes.Add(Package, PackageIndex); + } + void CreatePackage(int32 Index) + { + checkSlow(Index == Packages.Num()); + UPackage* Package = PackageProxies[Index].CreatePackage(*this); + Index = Packages.Add(Package); + PackageIndexes.Add(Package, Index); + } + void ResolvePackage(int32 Index) + { + PackageProxies[Index].Resolve(Packages[Index], *this); + } + int32 GetPackageIndex(UPackage* Package) const; + UPackage* GetPackageByIndex(int32 Index) const; + + void AddClass(FUnrealSourceFile* UnrealSourceFile, UClass* Class) + { + int32 Index = Classes.Num() + NewClasses.Add(Class); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EClass, Index)); + ClassIndexes.Add(Class, Index); + } + void CreateClass(int32 Index) + { + checkSlow(Index == Classes.Num()); + UClass* Class = ClassProxies[Index].CreateClass(*this); + Index = Classes.Add(Class); + ClassIndexes.Add(Class, Index); + } + void ResolveClass(int32 Index) + { + ClassProxies[Index].Resolve(Classes[Index], *this); + } + FSerializeIndex GetClassIndex(const UClass* Class) const; + UClass* GetClassByIndex(FSerializeIndex ClassIndex) const; + + void AddEnum(FUnrealSourceFile* UnrealSourceFile, UEnum* Enum) + { + int32 Index = Enums.Add(Enum); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EEnum, Index)); + EnumIndexes.Add(Enum, Index); + } + void CreateEnum(int32 Index) + { + checkSlow(Index == Enums.Num()); + UEnum* Enum = EnumProxies[Index].CreateEnum(*this); + Index = Enums.Add(Enum); + EnumIndexes.Add(Enum, Index); + } + void ResolveEnum(int32 Index) + { + EnumProxies[Index].Resolve(Enums[Index], *this); + } + int32 GetEnumIndex(const UEnum* Enum) const; + UEnum* GetEnumByIndex(int32 EnumIndex) const; + + void AddStruct(FUnrealSourceFile* UnrealSourceFile, UStruct* Struct) + { + int32 Index = Structs.Add(Struct); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EStruct, Index)); + StructIndexes.Add(Struct, Index); + } + void CreateStruct(int32 Index) + { + checkSlow(Index == Structs.Num()); + UStruct* Struct = StructProxies[Index].CreateStruct(*this); + Index = Structs.Add(Struct); + StructIndexes.Add(Struct, Index); + } + void ResolveStruct(int32 Index) + { + StructProxies[Index].Resolve(Structs[Index], *this); + } + FSerializeIndex GetStructIndex(const UStruct* Struct) const; + UStruct* GetStructByIndex(FSerializeIndex StructIndex) const; + + void AddScriptStruct(FUnrealSourceFile* UnrealSourceFile, UScriptStruct* ScriptStruct) + { + int32 Index = ScriptStructs.Add(ScriptStruct); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EScriptStruct, Index)); + ScriptStructIndexes.Add(ScriptStruct, Index); + } + void CreateScriptStruct(int32 Index) + { + checkSlow(Index == ScriptStructs.Num()); + UScriptStruct* ScriptStruct = ScriptStructProxies[Index].CreateScriptStruct(*this); + Index = ScriptStructs.Add(ScriptStruct); + ScriptStructIndexes.Add(ScriptStruct, Index); + } + void ResolveScriptStruct(int32 Index) + { + ScriptStructProxies[Index].Resolve(ScriptStructs[Index], *this); + } + FSerializeIndex GetScriptStructIndex(const UScriptStruct* Struct) const; + UScriptStruct* GetScriptStructByIndex(FSerializeIndex Index) const; + + void AddPropertyBase(FUnrealSourceFile* UnrealSourceFile, FPropertyBase* PropertyBase) + { + int32 Index = PropertyBases.Add(PropertyBase); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EPropertyBase, Index)); + PropertyBaseIndexes.Add(PropertyBase, Index); + } + void CreatePropertyBase(int32 Index) + { + checkSlow(Index == PropertyBases.Num()); + FPropertyBase* PropertyBase = PropertyBaseProxies[Index].CreatePropertyBase(*this); + Index = PropertyBases.Add(PropertyBase); + PropertyBaseIndexes.Add(PropertyBase, Index); + } + void ResolvePropertyBase(int32 Index) + { + PropertyBaseProxies[Index].Resolve(PropertyBases[Index], *this); + } + int32 GetPropertyBaseIndex(FPropertyBase* PropertyBase) const; + FPropertyBase* GetPropertyBaseByIndex(int32 Index) const; + + void AddProperty(FUnrealSourceFile* UnrealSourceFile, UProperty* Property) + { + int32 Index = Properties.Add(Property); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EProperty, Index)); + PropertyIndexes.Add(Property, Index); + } + void CreateProperty(int32 Index) + { + checkSlow(Index == Properties.Num()); + UProperty* Property = PropertyProxies[Index].CreateProperty(*this); + Index = Properties.Add(Property); + PropertyIndexes.Add(Property, Index); + } + void ResolveProperty(int32 Index) + { + PropertyProxies[Index].Resolve(Properties[Index], *this); + } + int32 GetPropertyIndex(const UProperty* Property) const; + UProperty* GetPropertyByIndex(int32 Index) const; + + void AddByteProperty(FUnrealSourceFile* UnrealSourceFile, UByteProperty* ByteProperty) + { + int32 Index = ByteProperties.Add(ByteProperty); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EByteProperty, Index)); + BytePropertyIndexes.Add(ByteProperty, Index); + } + void CreateByteProperty(int32 Index) + { + checkSlow(Index == ByteProperties.Num()); + UByteProperty* ByteProperty = BytePropertyProxies[Index].CreateByteProperty(*this); + Index = ByteProperties.Add(ByteProperty); + BytePropertyIndexes.Add(ByteProperty, Index); + } + void ResolveByteProperty(int32 Index) + { + BytePropertyProxies[Index].Resolve(ByteProperties[Index], *this); + } + + void AddInt8Property(FUnrealSourceFile* UnrealSourceFile, UInt8Property* Int8Property) + { + int32 Index = Int8Properties.Add(Int8Property); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EInt8Property, Index)); + Int8PropertyIndexes.Add(Int8Property, Index); + } + void CreateInt8Property(int32 Index) + { + checkSlow(Index == Int8Properties.Num()); + UInt8Property* Int8Property = Int8PropertyProxies[Index].CreateInt8Property(*this); + Index = Int8Properties.Add(Int8Property); + Int8PropertyIndexes.Add(Int8Property, Index); + } + void ResolveInt8Property(int32 Index) + { + Int8PropertyProxies[Index].Resolve(Int8Properties[Index], *this); + } + + void AddInt16Property(FUnrealSourceFile* UnrealSourceFile, UInt16Property* Int16Property) + { + int32 Index = Int16Properties.Add(Int16Property); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EInt16Property, Index)); + Int16PropertyIndexes.Add(Int16Property, Index); + } + void CreateInt16Property(int32 Index) + { + checkSlow(Index == Int16Properties.Num()); + UInt16Property* Int16Property = Int16PropertyProxies[Index].CreateInt16Property(*this); + Index = Int16Properties.Add(Int16Property); + Int16PropertyIndexes.Add(Int16Property, Index); + } + void ResolveInt16Property(int32 Index) + { + Int16PropertyProxies[Index].Resolve(Int16Properties[Index], *this); + } + + void AddIntProperty(FUnrealSourceFile* UnrealSourceFile, UIntProperty* IntProperty) + { + int32 Index = IntProperties.Add(IntProperty); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EIntProperty, Index)); + IntPropertyIndexes.Add(IntProperty, Index); + } + void CreateIntProperty(int32 Index) + { + checkSlow(Index == IntProperties.Num()); + UIntProperty* IntProperty = IntPropertyProxies[Index].CreateIntProperty(*this); + Index = IntProperties.Add(IntProperty); + IntPropertyIndexes.Add(IntProperty, Index); + } + void ResolveIntProperty(int32 Index) + { + IntPropertyProxies[Index].Resolve(IntProperties[Index], *this); + } + + void AddInt64Property(FUnrealSourceFile* UnrealSourceFile, UInt64Property* Int64Property) + { + int32 Index = Int64Properties.Add(Int64Property); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EInt64Property, Index)); + Int64PropertyIndexes.Add(Int64Property, Index); + } + void CreateInt64Property(int32 Index) + { + checkSlow(Index == Int64Properties.Num()); + UInt64Property* Int64Property = Int64PropertyProxies[Index].CreateInt64Property(*this); + Index = Int64Properties.Add(Int64Property); + Int64PropertyIndexes.Add(Int64Property, Index); + } + void ResolveInt64Property(int32 Index) + { + Int64PropertyProxies[Index].Resolve(Int64Properties[Index], *this); + } + + void AddUInt16Property(FUnrealSourceFile* UnrealSourceFile, UUInt16Property* UInt16Property) + { + int32 Index = UInt16Properties.Add(UInt16Property); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EUInt16Property, Index)); + UInt16PropertyIndexes.Add(UInt16Property, Index); + } + void CreateUInt16Property(int32 Index) + { + checkSlow(Index == UInt16Properties.Num()); + UUInt16Property* UInt16Property = UInt16PropertyProxies[Index].CreateUInt16Property(*this); + Index = UInt16Properties.Add(UInt16Property); + UInt16PropertyIndexes.Add(UInt16Property, Index); + } + void ResolveUInt16Property(int32 Index) + { + UInt16PropertyProxies[Index].Resolve(UInt16Properties[Index], *this); + } + + void AddUInt32Property(FUnrealSourceFile* UnrealSourceFile, UUInt32Property* UInt32Property) + { + int32 Index = UInt32Properties.Add(UInt32Property); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EUInt32Property, Index)); + UInt32PropertyIndexes.Add(UInt32Property, Index); + } + void CreateUInt32Property(int32 Index) + { + checkSlow(Index == UInt32Properties.Num()); + UUInt32Property* UInt32Property = UInt32PropertyProxies[Index].CreateUInt32Property(*this); + Index = UInt32Properties.Add(UInt32Property); + UInt32PropertyIndexes.Add(UInt32Property, Index); + } + void ResolveUInt32Property(int32 Index) + { + UInt32PropertyProxies[Index].Resolve(UInt32Properties[Index], *this); + } + + void AddUInt64Property(FUnrealSourceFile* UnrealSourceFile, UUInt64Property* UInt64Property) + { + int32 Index = UInt64Properties.Add(UInt64Property); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EUInt64Property, Index)); + UInt64PropertyIndexes.Add(UInt64Property, Index); + } + void CreateUInt64Property(int32 Index) + { + checkSlow(Index == UInt64Properties.Num()); + UUInt64Property* UInt64Property = UInt64PropertyProxies[Index].CreateUInt64Property(*this); + Index = UInt64Properties.Add(UInt64Property); + UInt64PropertyIndexes.Add(UInt64Property, Index); + } + void ResolveUInt64Property(int32 Index) + { + UInt64PropertyProxies[Index].Resolve(UInt64Properties[Index], *this); + } + + void AddFloatProperty(FUnrealSourceFile* UnrealSourceFile, UFloatProperty* FloatProperty) + { + int32 Index = FloatProperties.Add(FloatProperty); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EFloatProperty, Index)); + FloatPropertyIndexes.Add(FloatProperty, Index); + } + void CreateFloatProperty(int32 Index) + { + checkSlow(Index == FloatProperties.Num()); + UFloatProperty* FloatProperty = FloatPropertyProxies[Index].CreateFloatProperty(*this); + Index = FloatProperties.Add(FloatProperty); + FloatPropertyIndexes.Add(FloatProperty, Index); + } + void ResolveFloatProperty(int32 Index) + { + FloatPropertyProxies[Index].Resolve(FloatProperties[Index], *this); + } + + void AddDoubleProperty(FUnrealSourceFile* UnrealSourceFile, UDoubleProperty* DoubleProperty) + { + int32 Index = DoubleProperties.Add(DoubleProperty); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EDoubleProperty, Index)); + DoublePropertyIndexes.Add(DoubleProperty, Index); + } + void CreateDoubleProperty(int32 Index) + { + checkSlow(Index == DoubleProperties.Num()); + UDoubleProperty* DoubleProperty = DoublePropertyProxies[Index].CreateDoubleProperty(*this); + Index = DoubleProperties.Add(DoubleProperty); + DoublePropertyIndexes.Add(DoubleProperty, Index); + } + void ResolveDoubleProperty(int32 Index) + { + DoublePropertyProxies[Index].Resolve(DoubleProperties[Index], *this); + } + + void AddBoolProperty(FUnrealSourceFile* UnrealSourceFile, UBoolProperty* BoolProperty) + { + int32 Index = BoolProperties.Add(BoolProperty); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EBoolProperty, Index)); + BoolPropertyIndexes.Add(BoolProperty, Index); + } + void CreateBoolProperty(int32 Index) + { + checkSlow(Index == BoolProperties.Num()); + UBoolProperty* BoolProperty = BoolPropertyProxies[Index].CreateBoolProperty(*this); + Index = BoolProperties.Add(BoolProperty); + BoolPropertyIndexes.Add(BoolProperty, Index); + } + void ResolveBoolProperty(int32 Index) + { + BoolPropertyProxies[Index].Resolve(BoolProperties[Index], *this); + } + + void AddNameProperty(FUnrealSourceFile* UnrealSourceFile, UNameProperty* NameProperty) + { + int32 Index = NameProperties.Add(NameProperty); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::ENameProperty, Index)); + NamePropertyIndexes.Add(NameProperty, Index); + } + void CreateNameProperty(int32 Index) + { + checkSlow(Index == NameProperties.Num()); + UNameProperty* NameProperty = NamePropertyProxies[Index].CreateNameProperty(*this); + Index = NameProperties.Add(NameProperty); + NamePropertyIndexes.Add(NameProperty, Index); + } + void ResolveNameProperty(int32 Index) + { + NamePropertyProxies[Index].Resolve(NameProperties[Index], *this); + } + + void AddStrProperty(FUnrealSourceFile* UnrealSourceFile, UStrProperty* StrProperty) + { + int32 Index = StrProperties.Add(StrProperty); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EStrProperty, Index)); + StrPropertyIndexes.Add(StrProperty, Index); + } + void CreateStrProperty(int32 Index) + { + checkSlow(Index == StrProperties.Num()); + UStrProperty* StrProperty = StrPropertyProxies[Index].CreateStrProperty(*this); + StrProperties.Add(StrProperty); + StrPropertyIndexes.Add(StrProperty, Index); + } + void ResolveStrProperty(int32 Index) + { + StrPropertyProxies[Index].Resolve(StrProperties[Index], *this); + } + + void AddTextProperty(FUnrealSourceFile* UnrealSourceFile, UTextProperty* TextProperty) + { + int32 Index = TextProperties.Add(TextProperty); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::ETextProperty, Index)); + TextPropertyIndexes.Add(TextProperty, Index); + } + void CreateTextProperty(int32 Index) + { + checkSlow(Index == TextProperties.Num()); + UTextProperty* TextProperty = TextPropertyProxies[Index].CreateTextProperty(*this); + Index = TextProperties.Add(TextProperty); + TextPropertyIndexes.Add(TextProperty, Index); + } + void ResolveTextProperty(int32 Index) + { + TextPropertyProxies[Index].Resolve(TextProperties[Index], *this); + } + + void AddDelegateProperty(FUnrealSourceFile* UnrealSourceFile, UDelegateProperty* DelegateProperty) + { + int32 Index = DelegateProperties.Add(DelegateProperty); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EDelegateProperty, Index)); + DelegatePropertyIndexes.Add(DelegateProperty, Index); + } + void CreateDelegateProperty(int32 Index) + { + checkSlow(Index == DelegateProperties.Num()); + UDelegateProperty* DelegateProperty = DelegatePropertyProxies[Index].CreateDelegateProperty(*this); + Index = DelegateProperties.Add(DelegateProperty); + DelegatePropertyIndexes.Add(DelegateProperty, Index); + } + void ResolveDelegateProperty(int32 Index) + { + DelegatePropertyProxies[Index].Resolve(DelegateProperties[Index], *this); + } + + void AddMulticastDelegateProperty(FUnrealSourceFile* UnrealSourceFile, UMulticastDelegateProperty* MulticastDelegateProperty) + { + int32 Index = MulticastDelegateProperties.Add(MulticastDelegateProperty); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EMulticastDelegateProperty, Index)); + MulticastDelegatePropertyIndexes.Add(MulticastDelegateProperty, Index); + } + void CreateMulticastDelegateProperty(int32 Index) + { + checkSlow(Index == MulticastDelegateProperties.Num()); + UMulticastDelegateProperty* MulticastDelegateProperty = MulticastDelegatePropertyProxies[Index].CreateMulticastDelegateProperty(*this); + Index = MulticastDelegateProperties.Add(MulticastDelegateProperty); + MulticastDelegatePropertyIndexes.Add(MulticastDelegateProperty, Index); + } + void ResolveMulticastDelegateProperty(int32 Index) + { + MulticastDelegatePropertyProxies[Index].Resolve(MulticastDelegateProperties[Index], *this); + } + + void AddObjectPropertyBase(FUnrealSourceFile* UnrealSourceFile, UObjectPropertyBase* ObjectPropertyBase) + { + int32 Index = ObjectPropertyBases.Add(ObjectPropertyBase); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EObjectPropertyBase, Index)); + ObjectPropertyBaseIndexes.Add(ObjectPropertyBase, Index); + } + void CreateObjectPropertyBase(int32 Index) + { + checkSlow(Index == ObjectPropertyBases.Num()); + UObjectPropertyBase* ObjectPropertyBase = ObjectPropertyBaseProxies[Index].CreateObjectPropertyBase(*this); + Index = ObjectPropertyBases.Add(ObjectPropertyBase); + ObjectPropertyBaseIndexes.Add(ObjectPropertyBase, Index); + } + void ResolveObjectPropertyBase(int32 Index) + { + ObjectPropertyBaseProxies[Index].Resolve(ObjectPropertyBases[Index], *this); + } + + void AddClassProperty(FUnrealSourceFile* UnrealSourceFile, UClassProperty* ClassProperty) + { + int32 Index = ClassProperties.Add(ClassProperty); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EClassProperty, Index)); + ClassPropertyIndexes.Add(ClassProperty, Index); + } + void CreateClassProperty(int32 Index) + { + checkSlow(Index == ClassProperties.Num()); + UClassProperty* ClassProperty = ClassPropertyProxies[Index].CreateClassProperty(*this); + Index = ClassProperties.Add(ClassProperty); + ClassPropertyIndexes.Add(ClassProperty, Index); + } + void ResolveClassProperty(int32 Index) + { + ClassPropertyProxies[Index].Resolve(ClassProperties[Index], *this); + } + + void AddObjectProperty(FUnrealSourceFile* UnrealSourceFile, UObjectProperty* ObjectProperty) + { + int32 Index = ObjectProperties.Add(ObjectProperty); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EObjectProperty, Index)); + ObjectPropertyIndexes.Add(ObjectProperty, Index); + } + void CreateObjectProperty(int32 Index) + { + checkSlow(Index == ObjectProperties.Num()); + UObjectProperty* ObjectProperty = ObjectPropertyProxies[Index].CreateObjectProperty(*this); + Index = ObjectProperties.Add(ObjectProperty); + ObjectPropertyIndexes.Add(ObjectProperty, Index); + } + void ResolveObjectProperty(int32 Index) + { + ObjectPropertyProxies[Index].Resolve(ObjectProperties[Index], *this); + } + + void AddWeakObjectProperty(FUnrealSourceFile* UnrealSourceFile, UWeakObjectProperty* WeakObjectProperty) + { + int32 Index = WeakObjectProperties.Add(WeakObjectProperty); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EWeakObjectProperty, Index)); + WeakObjectPropertyIndexes.Add(WeakObjectProperty, Index); + } + void CreateWeakObjectProperty(int32 Index) + { + checkSlow(Index == WeakObjectProperties.Num()); + UWeakObjectProperty* WeakObjectProperty = WeakObjectPropertyProxies[Index].CreateWeakObjectProperty(*this); + Index = WeakObjectProperties.Add(WeakObjectProperty); + WeakObjectPropertyIndexes.Add(WeakObjectProperty, Index); + } + void ResolveWeakObjectProperty(int32 Index) + { + WeakObjectPropertyProxies[Index].Resolve(WeakObjectProperties[Index], *this); + } + + void AddLazyObjectProperty(FUnrealSourceFile* UnrealSourceFile, ULazyObjectProperty* LazyObjectProperty) + { + int32 Index = LazyObjectProperties.Add(LazyObjectProperty); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::ELazyObjectProperty, Index)); + LazyObjectPropertyIndexes.Add(LazyObjectProperty, Index); + } + void CreateLazyObjectProperty(int32 Index) + { + checkSlow(Index == LazyObjectProperties.Num()); + ULazyObjectProperty* LazyObjectProperty = LazyObjectPropertyProxies[Index].CreateLazyObjectProperty(*this); + Index = LazyObjectProperties.Add(LazyObjectProperty); + LazyObjectPropertyIndexes.Add(LazyObjectProperty, Index); + } + void ResolveLazyObjectProperty(int32 Index) + { + LazyObjectPropertyProxies[Index].Resolve(LazyObjectProperties[Index], *this); + } + + void AddAssetObjectProperty(FUnrealSourceFile* UnrealSourceFile, UAssetObjectProperty* AssetObjectProperty) + { + int32 Index = AssetObjectProperties.Add(AssetObjectProperty); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EAssetObjectProperty, Index)); + AssetObjectPropertyIndexes.Add(AssetObjectProperty, Index); + } + void CreateAssetObjectProperty(int32 Index) + { + checkSlow(Index == AssetObjectProperties.Num()); + UAssetObjectProperty* AssetObjectProperty = AssetObjectPropertyProxies[Index].CreateAssetObjectProperty(*this); + Index = AssetObjectProperties.Add(AssetObjectProperty); + AssetObjectPropertyIndexes.Add(AssetObjectProperty, Index); + } + void ResolveAssetObjectProperty(int32 Index) + { + AssetObjectPropertyProxies[Index].Resolve(AssetObjectProperties[Index], *this); + } + + void AddAssetClassProperty(FUnrealSourceFile* UnrealSourceFile, UAssetClassProperty* AssetClassProperty) + { + int32 Index = AssetClassProperties.Add(AssetClassProperty); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EAssetClassProperty, Index)); + AssetClassPropertyIndexes.Add(AssetClassProperty, Index); + } + void CreateAssetClassProperty(int32 Index) + { + checkSlow(Index == AssetClassProperties.Num()); + UAssetClassProperty* AssetClassProperty = AssetClassPropertyProxies[Index].CreateAssetClassProperty(*this); + Index = AssetClassProperties.Add(AssetClassProperty); + AssetClassPropertyIndexes.Add(AssetClassProperty, Index); + } + void ResolveAssetClassProperty(int32 Index) + { + AssetClassPropertyProxies[Index].Resolve(AssetClassProperties[Index], *this); + } + + void AddInterfaceProperty(FUnrealSourceFile* UnrealSourceFile, UInterfaceProperty* InterfaceProperty) + { + int32 Index = InterfaceProperties.Add(InterfaceProperty); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EInterfaceProperty, Index)); + InterfacePropertyIndexes.Add(InterfaceProperty, Index); + } + void CreateInterfaceProperty(int32 Index) + { + checkSlow(Index == InterfaceProperties.Num()); + UInterfaceProperty* InterfaceProperty = InterfacePropertyProxies[Index].CreateInterfaceProperty(*this); + Index = InterfaceProperties.Add(InterfaceProperty); + InterfacePropertyIndexes.Add(InterfaceProperty, Index); + } + void ResolveInterfaceProperty(int32 Index) + { + InterfacePropertyProxies[Index].Resolve(InterfaceProperties[Index], *this); + } + + void AddStructProperty(FUnrealSourceFile* UnrealSourceFile, UStructProperty* StructProperty) + { + int32 Index = StructProperties.Add(StructProperty); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EStructProperty, Index)); + StructPropertyIndexes.Add(StructProperty, Index); + } + void CreateStructProperty(int32 Index) + { + checkSlow(Index == StructProperties.Num()); + UStructProperty* StructProperty = StructPropertyProxies[Index].CreateStructProperty(*this); + Index = StructProperties.Add(StructProperty); + StructPropertyIndexes.Add(StructProperty, Index); + } + void ResolveStructProperty(int32 Index) + { + StructPropertyProxies[Index].Resolve(StructProperties[Index], *this); + } + + void AddMapProperty(FUnrealSourceFile* UnrealSourceFile, UMapProperty* MapProperty) + { + int32 Index = MapProperties.Add(MapProperty); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EMapProperty, Index)); + MapPropertyIndexes.Add(MapProperty, Index); + } + void CreateMapProperty(int32 Index) + { + checkSlow(Index == MapProperties.Num()); + UMapProperty* MapProperty = MapPropertyProxies[Index].CreateMapProperty(*this); + Index = MapProperties.Add(MapProperty); + MapPropertyIndexes.Add(MapProperty, Index); + } + void ResolveMapProperty(int32 Index) + { + MapPropertyProxies[Index].Resolve(MapProperties[Index], *this); + } + + void AddArrayProperty(FUnrealSourceFile* UnrealSourceFile, UArrayProperty* ArrayProperty) + { + int32 Index = ArrayProperties.Add(ArrayProperty); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EArrayProperty, Index)); + ArrayPropertyIndexes.Add(ArrayProperty, Index); + } + void CreateArrayProperty(int32 Index) + { + checkSlow(Index == ArrayProperties.Num()); + UArrayProperty* ArrayProperty = ArrayPropertyProxies[Index].CreateArrayProperty(*this); + Index = ArrayProperties.Add(ArrayProperty); + ArrayPropertyIndexes.Add(ArrayProperty, Index); + } + void ResolveArrayProperty(int32 Index) + { + ArrayPropertyProxies[Index].Resolve(ArrayProperties[Index], *this); + } + + void AddFunction(FUnrealSourceFile* UnrealSourceFile, UFunction* Function) + { + int32 Index = Functions.Add(Function); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EFunction, Index)); + FunctionIndexes.Add(Function, Index); + } + void CreateFunction(int32 Index) + { + checkSlow(Index == Functions.Num()); + UFunction* Function = FunctionProxies[Index].CreateFunction(*this); + Index = Functions.Add(Function); + FunctionIndexes.Add(Function, Index); + } + void ResolveFunction(int32 Index) + { + FunctionProxies[Index].Resolve(Functions[Index], *this); + } + int32 GetFunctionIndex(const UFunction* Function) const; + UFunction* GetFunctionByIndex(int32 Index) const; + + void AddDelegateFunction(FUnrealSourceFile* UnrealSourceFile, UDelegateFunction* DelegateFunction) + { + int32 Index = DelegateFunctions.Add(DelegateFunction); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EDelegateFunction, Index)); + DelegateFunctionIndexes.Add(DelegateFunction, Index); + } + void CreateDelegateFunction(int32 Index) + { + checkSlow(Index == DelegateFunctions.Num()); + UDelegateFunction* DelegateFunction = DelegateFunctionProxies[Index].CreateDelegateFunction(*this); + Index = DelegateFunctions.Add(DelegateFunction); + DelegateFunctionIndexes.Add(DelegateFunction, Index); + } + void ResolveDelegateFunction(int32 Index) + { + DelegateFunctionProxies[Index].Resolve(DelegateFunctions[Index], *this); + } + + void AddFileScope(FFileScope* FileScope) + { + int32 Index = FileScopes.Add(FileScope); + FileScopeIndexes.Add(FileScope, Index); + } + void ResolveFileScope(int32 Index) const + { + FileScopeProxies[Index].Resolve(FileScopes[Index], *this); + } + int32 GetFileScopeIndex(const FFileScope* FileScope) const; + FFileScope* GetFileScopeByIndex(int32 Index) const; + + void AddStructScope(FUnrealSourceFile* UnrealSourceFile, FStructScope* StructScope) + { + int32 Index = NewStructScopes.Add(StructScope) + StructScopes.Num(); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EStructScope, Index)); + StructScopeIndexes.Add(StructScope, Index); + } + void CreateStructScope(int32 Index) + { + checkSlow(Index == StructScopes.Num()); + FStructScope* StructScope = StructScopeProxies[Index].CreateStructScope(*this); + Index = StructScopes.Add(StructScope); + StructScopeIndexes.Add(StructScope, Index); + } + void ResolveStructScope(int32 Index) + { + StructScopeProxies[Index].Resolve(StructScopes[Index], *this); + } + int32 GetStructScopeIndex(const FStructScope* StructScope) const; + FStructScope* GetStructScopeByIndex(int32 Index) const; + + void AddScope(FUnrealSourceFile* UnrealSourceFile, FScope* Scope) + { + int32 Index = Scopes.Add(Scope); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EScope, Index)); + ScopeIndexes.Add(Scope, Index); + } + void ResolveScope(int32 Index) + { + ScopeProxies[Index].Resolve(Scopes[Index], *this); + } + int32 GetScopeIndex(const FScope* Scope) const; + FScope* GetScopeByIndex(int32 Index) const; + + void AddUnrealTypeDefinitionInfo(FUnrealSourceFile* UnrealSourceFile, FUnrealTypeDefinitionInfo* UnrealTypeDefinitionInfo) + { + int32 Index = UnrealTypeDefinitionInfos.Num() + NewUnrealTypeDefinitionInfos.Add(UnrealTypeDefinitionInfo); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EUnrealTypeDefinitionInfo, Index)); + UnrealTypeDefinitionInfoIndexes.Add(UnrealTypeDefinitionInfo, Index); + } + void CreateUnrealTypeDefinitionInfo(int32 Index) + { + checkSlow(Index == UnrealTypeDefinitionInfos.Num()); + Index = UnrealTypeDefinitionInfos.Add(UnrealTypeDefinitionInfoProxies[Index].CreateUnrealTypeDefinitionInfo(*this)); + } + void ResolveUnrealTypeDefinitionInfo(int32 Index) + { + FUnrealTypeDefinitionInfo*& UnrealTypeDefinitionInfo = UnrealTypeDefinitionInfos[Index]; + UnrealTypeDefinitionInfoProxies[Index].Resolve(UnrealTypeDefinitionInfo, *this); + UnrealTypeDefinitionInfoIndexes.Add(UnrealTypeDefinitionInfo, Index); + } + int32 GetUnrealTypeDefinitionInfoIndex(FUnrealTypeDefinitionInfo* Info) const; + FUnrealTypeDefinitionInfo* GetUnrealTypeDefinitionInfoByIndex(int32 Index) const; + + void AddTypeDefinitionInfoMapEntry(FUnrealSourceFile* SourceFile, UField* Field, FUnrealTypeDefinitionInfo* UnrealTypeDefinitionInfo) + { + int32 Index = TypeDefinitionInfoMap.Num() + NewTypeDefinitionInfoMap.Add(TPairInitializer(Field, UnrealTypeDefinitionInfo)); + GetHeaderDescriptor(SourceFile).AddEntry(AddObject(ESerializedObjectType::ETypeDefinitionInfoMapEntry, Index)); + } + void CreateTypeDefinitionInfoMapEntry(int32 Index); + void ResolveTypeDefinitionInfoMapEntry(int32 Index); + void ResolveTypeDefinitionInfoMapEntryPreload(int32 Index); + TPair* GetTypeDefinitionInfoMapEntryByIndex(int32 Index) + { + return &TypeDefinitionInfoMap[Index]; + } + + void AddUnrealSourceFilesMapEntry(FUnrealSourceFile* UnrealSourceFile, const FString& RawFilename) + { + int32 Index = UnrealSourceFilesMapEntries.Num() + NewUnrealSourceFilesMapEntries.Add(TPairInitializer(RawFilename, UnrealSourceFile)); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EUnrealSourceFilesMapEntry, Index)); + } + void CreateUnrealSourceFilesMapEntry(int32 Index) + { + auto& Kvp = UnrealSourceFilesMapEntryProxies[Index]; + UnrealSourceFilesMapEntries.Add(TPairInitializer(Kvp.Key, GetUnrealSourceFileByIndex(Kvp.Value))); + } + void AddPublicClassSetEntry(FUnrealSourceFile* UnrealSourceFile, UClass* Class) + { + int32 Index = PublicClassSetEntries.Num() + NewPublicClassSetEntries.Add(Class); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EPublicClassSetEntry, Index)); + } + + void AddGEnumUnderlyingType(FUnrealSourceFile* UnrealSourceFile, UEnum* Enum, EPropertyType Type) + { + int32 Index = EnumUnderlyingTypes.Add(TPairInitializer(Enum, Type)); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EEnumUnderlyingType, Index)); + } + void CreateGEnumUnderlyingType(int32 Index) + { + EnumUnderlyingTypes.Add(TPairInitializer(nullptr, EnumUnderlyingTypeProxies[Index].Value)); + } + void ResolveGEnumUnderlyingType(int32 Index) + { + EnumUnderlyingTypes[Index].Key = GetEnumByIndex(EnumUnderlyingTypeProxies[Index].Key); + GEnumUnderlyingTypes.Add(EnumUnderlyingTypes[Index].Key, EnumUnderlyingTypes[Index].Value); + } + + void AddPublicSourceFileSetEntry(FUnrealSourceFile* UnrealSourceFile) + { + int32 Index = PublicSourceFileSetEntries.Add(UnrealSourceFile); + AddObject(ESerializedObjectType::EPublicSourceFileSetEntry, Index); + } + void ResolvePublicClassSetEntry(int32 Index); + + void AddUnrealSourceFile(FUnrealSourceFile* UnrealSourceFile) + { + int32 UnrealSourceFileIndex = UnrealSourceFiles.Num() + NewUnrealSourceFiles.Add(UnrealSourceFile); + FFileScope* FileScope = &UnrealSourceFile->GetScope().Get(); + int32 FileScopeIndex = FileScopes.Num() + NewFileScopes.Add(FileScope); + FUHTMakefileHeaderDescriptor& HeaderDescriptor = GetHeaderDescriptor(UnrealSourceFile); + check(HeaderDescriptor.GetSourceFile() == nullptr); + HeaderDescriptor.SetSourceFile(UnrealSourceFile); + HeaderDescriptor.AddEntry(AddObject(ESerializedObjectType::EUnrealSourceFile, UnrealSourceFileIndex)); + HeaderDescriptor.AddEntry(AddObject(ESerializedObjectType::EFileScope, FileScopeIndex)); + UnrealSourceFileIndexes.Add(UnrealSourceFile, UnrealSourceFileIndex); + FileScopeIndexes.Add(FileScope, FileScopeIndex); + } + void CreateUnrealSourceFile(int32 Index) + { + checkSlow(UnrealSourceFiles.Num() == Index); + FUnrealSourceFile* UnrealSourceFile = UnrealSourceFileProxies[Index].CreateUnrealSourceFile(*this); + Index = UnrealSourceFiles.Add(UnrealSourceFile); + UnrealSourceFileIndexes.Add(UnrealSourceFile, Index); + } + void ResolveUnrealSourceFile(int32 Index) + { + UnrealSourceFileProxies[Index].Resolve(UnrealSourceFiles[Index], *this); + } + int32 GetUnrealSourceFileIndex(const FUnrealSourceFile* UnrealSourceFile) const; + FUnrealSourceFile* GetUnrealSourceFileByIndex(int32 Index) const; + + void AddGScriptHelperEntry(FUnrealSourceFile* UnrealSourceFile, UStruct* Struct, FClassMetaData* ClassMetaData) + { + int32 Index = GScriptHelperEntries.Add(TPairInitializer(Struct, ClassMetaData)); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EGScriptHelperEntry, Index)); + } + void CreateGScriptHelperEntry(UStruct* Struct, FClassMetaData* ClassMetaData) + { + GScriptHelperEntries.Add(TPairInitializer(Struct, ClassMetaData)); + } + void ResolveGScriptHelperEntry(int32 Index) + { + CompilerMetadataManagerArchiveProxy.Resolve(Index, GScriptHelper, *this); + } + + void AddToken(FUnrealSourceFile* UnrealSourceFile, FToken* Token) + { + int32 Index = Tokens.Add(Token); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EToken, Index)); + TokenIndexes.Add(Token, Index); + } + void CreateToken(int32 Index) + { + FToken* Token = TokenProxies[Index].CreateToken(); + Index = Tokens.Add(Token); + TokenIndexes.Add(Token, Index); + } + void ResolveToken(int32 Index) + { + TokenProxies[Index].Resolve(Tokens[Index], *this); + } + int32 GetTokenIndex(const FToken* Token) const; + const FToken* GetTokenByIndex(int32 Index) const; + + void AddGeneratedCodeCRC(FUnrealSourceFile* SourceFile, UField* Field, uint32 CRC) + { + int32 Index = GeneratedCodeCRCs.Add(TPairInitializer(Field, CRC)); + GetHeaderDescriptor(SourceFile).AddEntry(AddObject(ESerializedObjectType::EGeneratedCodeCRC, Index)); + } + void CreateGeneratedCodeCRC(int32 Index) + { + checkSlow(Index == GeneratedCodeCRCs.Num()); + GeneratedCodeCRCs.Add(TPairInitializer(nullptr, GeneratedCodeCRCProxies[Index].Value)); + } + void ResolveGeneratedCodeCRC(int32 Index) + { + auto& Kvp = GeneratedCodeCRCs[Index]; + Kvp.Key = GetFieldByIndex(GeneratedCodeCRCProxies[Index].Key); + GGeneratedCodeCRCs.Add(Kvp.Key, Kvp.Value); + } + TPair& GetGeneratedCodeCRCProxy(int32 Index) + { + check(GeneratedCodeCRCProxies.IsValidIndex(Index)); + return GeneratedCodeCRCProxies[Index]; + } + + void AddClassMetaData(FUnrealSourceFile* SourceFile, TScopedPointer& ClassMetaData) + { + int32 Index = ClassMetaDatas.Add(ClassMetaData); + GetHeaderDescriptor(SourceFile).AddEntry(AddObject(ESerializedObjectType::EClassMetaData, Index)); + } + void CreateClassMetaData(int32 Index) + { + checkSlow(Index == ClassMetaDatas.Num()); + ClassMetaDatas.Add(ClassMetaDataArchiveProxies[Index].CreateClassMetaData()); + } + void ResolveClassMetaData(int32 Index) + { + ClassMetaDataArchiveProxies[Index].Resolve(ClassMetaDatas[Index], *this); + } + + void AddPropertyDataEntry(FUnrealSourceFile* UnrealSourceFile, TSharedPtr& TokenData, UProperty* Property) + { + FToken* Token = &TokenData->Token; + int32 TokenIndex = Tokens.Add(Token); + TokenIndexes.Add(Token, TokenIndex); + + FUHTMakefileHeaderDescriptor& HeaderDescriptor = GetHeaderDescriptor(UnrealSourceFile); + HeaderDescriptor.AddEntry(AddObject(ESerializedObjectType::EToken, TokenIndex)); + + int32 PropertyDataEntryIndex = PropertyDataEntries.Add(TPairInitializer>(Property, TokenData)); + HeaderDescriptor.AddEntry(AddObject(ESerializedObjectType::EPropertyDataEntry, PropertyDataEntryIndex)); + } + void CreatePropertyDataEntry(int32 Index) + { + PropertyDataEntries.Add(TPairInitializer>(nullptr, TSharedPtr(nullptr))); + } + void ResolvePropertyDataEntry(int32 Index) + { + auto& Entry = PropertyDataEntries[Index]; + auto& EntryProxy = PropertyDataEntryProxies[Index]; + Entry.Key = GetPropertyByIndex(EntryProxy.Key); + const FToken* Token = GetTokenByIndex(EntryProxy.Value); + TokenIndexes.FindAndRemoveChecked(Token); + Entry.Value = TSharedPtr(new FTokenData(*Token)); + Tokens[EntryProxy.Value] = &Entry.Value->Token; + TokenIndexes.Add(&Entry.Value->Token, EntryProxy.Value); + } + int32 GetPropertyDataEntryIndex(const TPair>* Entry) const; + const TPair>* GetPropertyDataEntryByIndex(int32 Index) const; + + void AddMultipleInheritanceBaseClass(FUnrealSourceFile* UnrealSourceFile, FMultipleInheritanceBaseClass* MultipleInheritanceBaseClass) + { + int32 Index = MultipleInheritanceBaseClasses.Add(MultipleInheritanceBaseClass); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EMultipleInheritanceBaseClass, Index)); + } + void CreateMultipleInheritanceBaseClass(int32 Index) + { + checkSlow(Index == MultipleInheritanceBaseClasses.Num()); + MultipleInheritanceBaseClasses.Add(MultipleInheritanceBaseClassProxies[Index].CreateMultipleInheritanceBaseClass()); + } + void ResolveMultipleInheritanceBaseClass(int32 Index) + { + MultipleInheritanceBaseClassProxies[Index].Resolve(MultipleInheritanceBaseClasses[Index], *this); + } + + void AddInterfaceAllocation(FUnrealSourceFile* UnrealSourceFile, TCHAR* InterfaceAllocation) + { + int32 Index = NewInterfaceAllocations.Add(InterfaceAllocation) + InterfaceAllocations.Num(); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EInterfaceAllocation, Index)); + } + void CreateInterfaceAllocation(int32 Index) + { + FString& InterfaceProxy = InterfaceAllocationProxies[Index]; + int32 Strlen = InterfaceProxy.Len(); + TCHAR* InterfaceAllocation; + if (Strlen) + { + InterfaceAllocation = new TCHAR[Strlen + 1]; + FMemory::Memcpy(InterfaceAllocation, *InterfaceProxy, Strlen + 1); + } + else + { + InterfaceAllocation = nullptr; + } + NameLookupCPP->InterfaceAllocations.Add(InterfaceAllocation); + InterfaceAllocations.Add(InterfaceAllocation); + } + + void AddStructNameMapEntry(FUnrealSourceFile* UnrealSourceFile, UStruct* Struct, TCHAR* NameCPP) + { + int32 Index = NewStructNameMapEntries.Add(TPairInitializer(Struct, NameCPP)) + StructNameMapEntries.Num(); + GetHeaderDescriptor(UnrealSourceFile).AddEntry(AddObject(ESerializedObjectType::EStructNameMapEntry, Index)); + } + void CreateStructNameMapEntry(int32 Index) + { + StructNameMapEntries.AddDefaulted(); + auto& ProxyKvp = StructNameMapEntryProxies[Index]; + auto& Kvp = StructNameMapEntries[Index]; + Kvp.Key = nullptr; + int32 Strlen = ProxyKvp.Value.Len(); + if (Strlen) + { + Kvp.Value = new TCHAR[Strlen + 1]; + FMemory::Memcpy(Kvp.Value, *ProxyKvp.Value, sizeof(TCHAR) * (Strlen + 1)); + } + else + { + Kvp.Value = nullptr; + } + } + void ResolveStructNameMapEntry(int32 Index) + { + auto& Kvp = StructNameMapEntries[Index]; + Kvp.Key = GetStructByIndex(StructNameMapEntryProxies[Index].Key); + NameLookupCPP->StructNameMap.Add(Kvp.Key, Kvp.Value); + } + + + FSerializeIndex GetFieldIndex(const UField* Field) const; + UField* GetFieldByIndex(FSerializeIndex Index) const; + + FSerializeIndex GetObjectIndex(const UObject* Object) const; + UObject* GetObjectByIndex(FSerializeIndex Index) const; + + int32 GetNameIndex(FName Name) const; + FName GetNameByIndex(int32 Index) const; + + friend FArchive& operator<<(FArchive& Ar, FUHTMakefile& UHTMakefile); + + void SetCurrentModuleName(FName InName) + { + CurrentModuleName = InName; + } + + FName GetCurrentModuleName() + { + check(CurrentModuleName != NAME_None); + return CurrentModuleName; + } + + + FUHTMakefileModuleDescriptor& GetCurrentModule() + { + return GetModuleDescriptor(GetCurrentModuleName()); + } + + FUHTMakefileHeaderDescriptor& GetHeaderDescriptorBySourceFileIndex(int32 UnrealSourceFileIndex) + { + FUHTMakefileModuleDescriptor& CurrentModule = GetCurrentModule(); + if (CurrentModule.HasHeaderDescriptor(UnrealSourceFileIndex)) + { + return CurrentModule.GetHeaderDescriptor(UnrealSourceFileIndex); + } + else + { + for (auto& Kvp : ModuleDescriptors) + { + if (Kvp.Key == CurrentModuleName) + { + continue; + } + + FUHTMakefileModuleDescriptor& ModuleDescriptor = Kvp.Value; + if (ModuleDescriptor.HasHeaderDescriptor(UnrealSourceFileIndex)) + { + FUHTMakefileHeaderDescriptor& HeaderDescriptor = ModuleDescriptor.GetHeaderDescriptor(UnrealSourceFileIndex); + return HeaderDescriptor; + } + } + } + + // Header descriptor not found, add a new one. + return CurrentModule.GetHeaderDescriptor(UnrealSourceFileIndex); + } + + FUHTMakefileHeaderDescriptor& GetHeaderDescriptor(FUnrealSourceFile* UnrealSourceFile) + { + int32 UnrealSourceFileIndex = GetUnrealSourceFileIndex(UnrealSourceFile); + return GetHeaderDescriptorBySourceFileIndex(UnrealSourceFileIndex); + } + + FUHTMakefileModuleDescriptor& GetModuleDescriptor(FName ModuleName) + { + return ModuleDescriptors.FindOrAdd(ModuleName); + } + int32 AddObject(ESerializedObjectType SerializedObjectType, int32 Index) + { + checkSlow(LoadingPhase != EUHTMakefileLoadingPhase::Max); + return InitializationOrder[+LoadingPhase].Add(TPairInitializer(SerializedObjectType, Index)); + } + + TPair GetFromInitializationOrder(int32 Index, EUHTMakefileLoadingPhase UHTMakefileLoadingPhase) + { + return InitializationOrder[+UHTMakefileLoadingPhase][Index]; + } + + void CreateObjectAtInitOrderIndex(int32 Index, EUHTMakefileLoadingPhase UHTMakefileLoadingPhase); + void ResolveObjectAtInitOrderIndex(int32 Index, EUHTMakefileLoadingPhase UHTMakefileLoadingPhase); + + void ResolveTypeFullyCreatedDuringPreload(int32 ObjectIndex); + + + void AddToHeaderOrder(FUnrealSourceFile* SourceFile) + { + for (auto& Kvp : ModuleDescriptors) + { + if (Kvp.Value.GetPackage() == SourceFile->GetPackage()) + { + Kvp.Value.AddToHeaderOrder(SourceFile); + break; + } + } + //GetCurrentModule().AddToHeaderOrder(SourceFile); + } + bool ShouldForceRegeneration() const + { + return bShouldForceRegeneration; + } + + void AddName(FName Name) + { + ReferencedNamesSet.Add(Name); + } + void SetNameLookupCPP(FNameLookupCPP* InNameLookupCPP) + { + NameLookupCPP = InNameLookupCPP; + NameLookupCPP->SetUHTMakefile(this); + } + void SetManifest(FManifest* InManifest) + { + Manifest = InManifest; + } + bool CanLoadModule(const FManifestModule& ManifestModule); + void LoadFromFile(const TCHAR* MakefilePath, FManifest* Manifest); + void SaveToFile(const TCHAR* MakefilePath); +private: + bool bShouldForceRegeneration; + bool bShouldMoveNewObjects; + void CreateObject(ESerializedObjectType ObjectType, int32 Index); + void ResolveObject(ESerializedObjectType ObjectType, int32 Index); + TArray ReferencedNames; + TArray>> InitializationOrder; + struct FUHTMakefileNameMapKeyFuncs : DefaultKeyFuncs + { + static FORCEINLINE bool Matches(FName A, FName B) + { + // The linker requires that FNames preserve case, but the numeric suffix can be ignored since + // that is stored separately for each FName instance saved + return A.IsEqual(B, ENameCase::CaseSensitive, false/*bCompareNumber*/); + } + + static FORCEINLINE uint32 GetKeyHash(FName Key) + { + return Key.GetComparisonIndex(); + } + }; + + TSet ReferencedNamesSet; + TMap> NameIndices; + + void ResolveModuleNames() + { + for (const FNameArchiveProxy& NameArchiveProxy : ModuleNamesProxy) + { + ModuleNames.Add(NameArchiveProxy.CreateName(*this)); + } + } + + void SetupScopeArrays(); + void SetupProxies(); + void SetupAndSaveReferencedNames(FArchive& Ar); + void LoadAndSetupReferencedNames(FArchive& Ar); + int32 GetPropertyCount() const; + int32 GetPropertyProxiesCount() const; + int32 GetFunctionCount() const; + int32 GetFunctionProxiesCount() const; + int32 GetStructsCount() const; + int32 GetStructProxiesCount() const; + + TArray MultipleInheritanceBaseClasses; + TArray MultipleInheritanceBaseClassProxies; + + TArray> TokenDatas; + TArray TokenDataProxies; + + TArray>> PropertyDataEntries; + TArray> PropertyDataEntryProxies; + + FCompilerMetadataManagerArchiveProxy CompilerMetadataManagerArchiveProxy; + TArray> GScriptHelperEntries; + + TArray> ModuleDescriptorsArchiveProxy; + TMap ModuleDescriptors; + + TArray TokenProxies; + TArray Tokens; + TMap TokenIndexes; + + TArray> GeneratedCodeCRCProxies; + TArray> GeneratedCodeCRCs; + + /** Current version of makefile. Bump if saved file format is changed. */ + uint64 Version; + + /** Current version. Bump if serialization code changes. */ + static const uint64 CurrentVersion = 1; + + bool IsTypeFullyCreatedDuringPreload(ESerializedObjectType SerializedObjectType) + { + return SerializedObjectType == ESerializedObjectType::ETypeDefinitionInfoMapEntry + || SerializedObjectType == ESerializedObjectType::EUnrealTypeDefinitionInfo; + } + void UpdateModulesCompatibility(FManifest* Manifest); + FManifest* Manifest; + FName CurrentModuleName; + + EUHTMakefileLoadingPhase LoadingPhase; + + TArray> EnumUnderlyingTypes; + TArray> EnumUnderlyingTypeProxies; + + TArray PropertyDatas; + TArray PropertyDataProxies; + + TArray ModuleNamesProxy; + TArray ModuleNames; + + TArray> ClassMetaDatas; + TArray ClassMetaDataArchiveProxies; + + TArray PackageProxies; + TArray Packages; + TMap PackageIndexes; + TArray NewPackages; + + TArray UnrealSourceFileProxies; + TArray UnrealSourceFiles; + TMap UnrealSourceFileIndexes; + TArray NewUnrealSourceFiles; + + TArray ClassProxies; + TArray Classes; + TMap ClassIndexes; + TArray NewClasses; + + TArray StructProxies; + TArray Structs; + TMap StructIndexes; + + TArray ScriptStructProxies; + TArray ScriptStructs; + TMap ScriptStructIndexes; + + TArray PropertyBaseProxies; + TArray PropertyBases; + TMap PropertyBaseIndexes; + + TArray EnumProxies; + TArray Enums; + TMap EnumIndexes; + + TArray FileScopeProxies; + TArray FileScopes; + TMap FileScopeIndexes; + TArray NewFileScopes; + + TArray StructScopeProxies; + TArray StructScopes; + TMap StructScopeIndexes; + TArray NewStructScopes; + + TArray ScopeProxies; + TArray Scopes; + TMap ScopeIndexes; + + TArray UnrealTypeDefinitionInfoProxies; + TArray UnrealTypeDefinitionInfos; + TMap UnrealTypeDefinitionInfoIndexes; + TArray NewUnrealTypeDefinitionInfos; + + TArray RawFilenames; + + TArray PublicClassSetEntryProxies; + TArray PublicClassSetEntries; + TArray NewPublicClassSetEntries; + + TArray PublicSourceFileSetEntryProxies; + TArray PublicSourceFileSetEntries; + + TArray> UnrealSourceFilesMapEntryProxies; + TArray> UnrealSourceFilesMapEntries; + TArray> NewUnrealSourceFilesMapEntries; + + FTypeDefinitionInfoMapArchiveProxy TypeDefinitionInfoMapArchiveProxy; + TArray> TypeDefinitionInfoMap; + TArray> NewTypeDefinitionInfoMap; + + TArray PropertyProxies; + TArray Properties; + TMap PropertyIndexes; + + TArray BytePropertyProxies; + TArray ByteProperties; + TMap BytePropertyIndexes; + + TArray Int8PropertyProxies; + TArray Int8Properties; + TMap Int8PropertyIndexes; + + TArray Int16PropertyProxies; + TArray Int16Properties; + TMap Int16PropertyIndexes; + + TArray IntPropertyProxies; + TArray IntProperties; + TMap IntPropertyIndexes; + + TArray Int64PropertyProxies; + TArray Int64Properties; + TMap Int64PropertyIndexes; + + TArray UInt16PropertyProxies; + TArray UInt16Properties; + TMap UInt16PropertyIndexes; + + TArray UInt32PropertyProxies; + TArray UInt32Properties; + TMap UInt32PropertyIndexes; + + TArray UInt64PropertyProxies; + TArray UInt64Properties; + TMap UInt64PropertyIndexes; + + TArray FloatPropertyProxies; + TArray FloatProperties; + TMap FloatPropertyIndexes; + + TArray DoublePropertyProxies; + TArray DoubleProperties; + TMap DoublePropertyIndexes; + + TArray BoolPropertyProxies; + TArray BoolProperties; + TMap BoolPropertyIndexes; + + TArray NamePropertyProxies; + TArray NameProperties; + TMap NamePropertyIndexes; + + TArray StrPropertyProxies; + TArray StrProperties; + TMap StrPropertyIndexes; + + TArray TextPropertyProxies; + TArray TextProperties; + TMap TextPropertyIndexes; + + TArray DelegatePropertyProxies; + TArray DelegateProperties; + TMap DelegatePropertyIndexes; + + TArray MulticastDelegatePropertyProxies; + TArray MulticastDelegateProperties; + TMap MulticastDelegatePropertyIndexes; + + TArray ObjectPropertyBaseProxies; + TArray ObjectPropertyBases; + TMap ObjectPropertyBaseIndexes; + + TArray ClassPropertyProxies; + TArray ClassProperties; + TMap ClassPropertyIndexes; + + TArray ObjectPropertyProxies; + TArray ObjectProperties; + TMap ObjectPropertyIndexes; + + TArray WeakObjectPropertyProxies; + TArray WeakObjectProperties; + TMap WeakObjectPropertyIndexes; + + TArray LazyObjectPropertyProxies; + TArray LazyObjectProperties; + TMap LazyObjectPropertyIndexes; + + TArray AssetObjectPropertyProxies; + TArray AssetObjectProperties; + TMap AssetObjectPropertyIndexes; + + TArray AssetClassPropertyProxies; + TArray AssetClassProperties; + TMap AssetClassPropertyIndexes; + + TArray InterfacePropertyProxies; + TArray InterfaceProperties; + TMap InterfacePropertyIndexes; + + TArray StructPropertyProxies; + TArray StructProperties; + TMap StructPropertyIndexes; + + TArray MapPropertyProxies; + TArray MapProperties; + TMap MapPropertyIndexes; + + TArray ArrayPropertyProxies; + TArray ArrayProperties; + TMap ArrayPropertyIndexes; + + TArray FunctionProxies; + TArray Functions; + TMap FunctionIndexes; + + TArray DelegateFunctionProxies; + TArray DelegateFunctions; + TMap DelegateFunctionIndexes; + + FNameLookupCPP* NameLookupCPP; + + TArray> StructNameMapEntryProxies; + TArray> StructNameMapEntries; + TArray> NewStructNameMapEntries; + + TArray InterfaceAllocationProxies; + TArray InterfaceAllocations; + TArray NewInterfaceAllocations; +}; diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/UHTMakefileHeaderDescriptor.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/UHTMakefileHeaderDescriptor.cpp new file mode 100644 index 000000000000..80306f5881fe --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/UHTMakefileHeaderDescriptor.cpp @@ -0,0 +1,116 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#include "UnrealHeaderTool.h" +#include "UHTMakefile/UHTMakefileHeaderDescriptor.h" +#include "UHTMakefile/UHTMakefile.h" +#include "ClassMaps.h" + +FUHTMakefileHeaderDescriptor::FUHTMakefileHeaderDescriptor(FUHTMakefile* InUHTMakefile /*= nullptr*/) + : LoadingPhase(EUHTMakefileLoadingPhase::Preload) + , UHTMakefile(InUHTMakefile) + , SourceFile(nullptr) +{ + InitializeObjectIndexes.AddDefaulted(+EUHTMakefileLoadingPhase::Max); + for (uint8 i = 0; i < +EUHTMakefileLoadingPhase::Max; ++i) + { + bCreatedObjects[i] = bResolvedObjects[i] = false; + } +} + +void FUHTMakefileHeaderDescriptor::AddPrerequesites(TArray& Prerequisites) +{ + PrerequisiteIndexes.Reserve(PrerequisiteIndexes.Num() + Prerequisites.Num()); + for (FUnrealSourceFile* UnrealSourceFile : Prerequisites) + { + PrerequisiteIndexes.Add(UHTMakefile->GetUnrealSourceFileIndex(UnrealSourceFile)); + } +} + +void FUHTMakefileHeaderDescriptor::CreateObjects(EUHTMakefileLoadingPhase UHTMakefileLoadingPhase) +{ + if (bCreatedObjects[+UHTMakefileLoadingPhase]) + { + return; + } + bCreatedObjects[+UHTMakefileLoadingPhase] = true; + + if (UHTMakefileLoadingPhase == EUHTMakefileLoadingPhase::Load) + { + // Handle prerequisites first. + for (int32 PrerequesiteIndex : PrerequisiteIndexes) + { + UHTMakefile->GetHeaderDescriptorBySourceFileIndex(PrerequesiteIndex).CreateObjects(UHTMakefileLoadingPhase); + } + } + + // Create own objects + for (int32 ObjectIndex : InitializeObjectIndexes[+UHTMakefileLoadingPhase]) + { + UHTMakefile->CreateObjectAtInitOrderIndex(ObjectIndex, UHTMakefileLoadingPhase); + } +} + +void FUHTMakefileHeaderDescriptor::ResolveObjects(EUHTMakefileLoadingPhase UHTMakefileLoadingPhase) +{ + if (UHTMakefileLoadingPhase == EUHTMakefileLoadingPhase::Preload) + { + // Resolve own objects + for (int32 ObjectIndex : InitializeObjectIndexes[+EUHTMakefileLoadingPhase::Preload]) + { + UHTMakefile->ResolveTypeFullyCreatedDuringPreload(ObjectIndex); + } + + return; + } + + if (bResolvedObjects[+UHTMakefileLoadingPhase]) + { + return; + } + + bResolvedObjects[+UHTMakefileLoadingPhase] = true; + + if (UHTMakefileLoadingPhase == EUHTMakefileLoadingPhase::Load) + { + // Handle prerequisites first. + for (int32 PrerequesityIndex : PrerequisiteIndexes) + { + UHTMakefile->GetHeaderDescriptor(UHTMakefile->GetUnrealSourceFileByIndex(PrerequesityIndex)).ResolveObjects(UHTMakefileLoadingPhase); + } + + for (int32 ObjectIndex : InitializeObjectIndexes[+EUHTMakefileLoadingPhase::Preload]) + { + UHTMakefile->ResolveObjectAtInitOrderIndex(ObjectIndex, EUHTMakefileLoadingPhase::Preload); + } + } + + // Resolve own objects + for (int32 ObjectIndex : InitializeObjectIndexes[+UHTMakefileLoadingPhase]) + { + UHTMakefile->ResolveObjectAtInitOrderIndex(ObjectIndex, UHTMakefileLoadingPhase); + } +} + +void FUHTMakefileHeaderDescriptor::StartLoading() +{ + LoadingPhase = EUHTMakefileLoadingPhase::Load; +} + +void FUHTMakefileHeaderDescriptor::Reset() +{ + PrerequisiteIndexes.Empty(); + InitializeObjectIndexes.Empty(); + UHTMakefile = nullptr; + SourceFile = nullptr; + LoadingPhase = EUHTMakefileLoadingPhase::Preload; + FMemory::Memzero(bCreatedObjects, sizeof(bCreatedObjects)); + FMemory::Memzero(bResolvedObjects, sizeof(bResolvedObjects)); +} + +FArchive& operator<<(FArchive& Ar, FUHTMakefileHeaderDescriptor& UHTMakefileHeaderDescriptor) +{ + Ar << UHTMakefileHeaderDescriptor.PrerequisiteIndexes; + Ar << UHTMakefileHeaderDescriptor.InitializeObjectIndexes; + + return Ar; +} diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/UHTMakefileHeaderDescriptor.h b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/UHTMakefileHeaderDescriptor.h new file mode 100644 index 000000000000..0f51c87517d7 --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/UHTMakefileHeaderDescriptor.h @@ -0,0 +1,92 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#pragma once + +class FUnrealSourceFile; + +/* See UHTMakefile.h for overview how makefiles work. */ +class FUHTMakefileHeaderDescriptor +{ +public: + FUHTMakefileHeaderDescriptor(FUHTMakefile* InUHTMakefile = nullptr); + + void AddPrerequesites(TArray& Prerequesities); + void CreateObjects(EUHTMakefileLoadingPhase UHTMakefileLoadingPhase); + void ResolveObjects(EUHTMakefileLoadingPhase UHTMakefileLoadingPhase); + friend FArchive& operator<<(FArchive& Ar, FUHTMakefileHeaderDescriptor& UHTMakefileHeaderDescriptor); + + void SetMakefile(FUHTMakefile* InUHTMakefile) + { + UHTMakefile = InUHTMakefile; + } + + FUHTMakefile* GetMakefile() + { + return UHTMakefile; + } + + void AddEntry(int32 Index) + { + check(LoadingPhase != EUHTMakefileLoadingPhase::Max); + InitializeObjectIndexes[+LoadingPhase].Add(Index); + } + + void StopPreloading() + { + LoadingPhase = EUHTMakefileLoadingPhase::Max; + } + + bool IsPreloading() + { + return LoadingPhase == EUHTMakefileLoadingPhase::Preload; + } + + void StartLoading(); + + void StopLoading() + { + LoadingPhase = EUHTMakefileLoadingPhase::Max; + } + + bool IsLoading() + { + return LoadingPhase == EUHTMakefileLoadingPhase::Load; + } + + void StartExporting() + { + LoadingPhase = EUHTMakefileLoadingPhase::Export; + } + + void StopExporting() + { + LoadingPhase = EUHTMakefileLoadingPhase::Export; + } + + bool IsExporting() + { + return LoadingPhase == EUHTMakefileLoadingPhase::Export; + } + + FUnrealSourceFile* GetSourceFile() const + { + return SourceFile; + } + + void SetSourceFile(FUnrealSourceFile* InSourceFile) + { + SourceFile = InSourceFile; + } + + void Reset(); + +private: + TArray PrerequisiteIndexes; + TArray> InitializeObjectIndexes; + FUHTMakefile* UHTMakefile; + + FUnrealSourceFile* SourceFile; + EUHTMakefileLoadingPhase LoadingPhase; + bool bCreatedObjects[(uint8)EUHTMakefileLoadingPhase::Max]; + bool bResolvedObjects[(uint8)EUHTMakefileLoadingPhase::Max]; +}; diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/UHTMakefileModuleDescriptor.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/UHTMakefileModuleDescriptor.cpp new file mode 100644 index 000000000000..4d64b495e572 --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/UHTMakefileModuleDescriptor.cpp @@ -0,0 +1,83 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#include "UnrealHeaderTool.h" +#include "UHTMakefile/UHTMakefileModuleDescriptor.h" +#include "UHTMakefile/UHTMakefile.h" +#include "ClassMaps.h" + +void FUHTMakefileModuleDescriptor::SetPackageIndex(int32 Index) +{ + PackageIndex = Index; + UHTMakefile->AddObject(ESerializedObjectType::EPackage, Index); +} + +void FUHTMakefileModuleDescriptor::LoadModuleData(const FManifestModule& ManifestModule, EUHTMakefileLoadingPhase UHTMakefileLoadingPhase) +{ + if (UHTMakefileLoadingPhase == EUHTMakefileLoadingPhase::Preload) + { + UHTMakefile->CreatePackage(GetPackageIndex()); + } + + for (int32 Index : HeadersOrder[+UHTMakefileLoadingPhase]) + { + if (UHTMakefileLoadingPhase == EUHTMakefileLoadingPhase::Preload) + { + HeaderDescriptors[Index].SetMakefile(UHTMakefile); + } + HeaderDescriptors[Index].CreateObjects(UHTMakefileLoadingPhase); + } + + if (UHTMakefileLoadingPhase == EUHTMakefileLoadingPhase::Load) + { + UHTMakefile->ResolvePackage(PackageIndex); + } + + for (int32 Index : HeadersOrder[+UHTMakefileLoadingPhase]) + { + HeaderDescriptors[Index].ResolveObjects(UHTMakefileLoadingPhase); + } + + if (UHTMakefileLoadingPhase == EUHTMakefileLoadingPhase::Preload) + { + GPackageToManifestModuleMap.Add(UHTMakefile->GetPackageByIndex(GetPackageIndex()), &ManifestModule); + } +} + +void FUHTMakefileModuleDescriptor::AddToHeaderOrder(FUnrealSourceFile* SourceFile) +{ + if (SourceFile->GetPackage() != Package) + { + return; + } + int32 SourceFileIndex = UHTMakefile->GetUnrealSourceFileIndex(SourceFile); + TArray& CurrentHeadersOrder = HeadersOrder[+LoadingPhase]; + checkSlow(!CurrentHeadersOrder.Contains(SourceFileIndex)); + CurrentHeadersOrder.Add(SourceFileIndex); +} + +void FUHTMakefileModuleDescriptor::Reset() +{ + PackageIndex = INDEX_NONE; + HeadersOrder.Reset(); + HeadersOrder.AddDefaulted(+EUHTMakefileLoadingPhase::Max); + LoadingPhase = EUHTMakefileLoadingPhase::Preload; + UHTMakefile = nullptr; + Package = nullptr; + + + for (auto& Kvp : HeaderDescriptors) + { + Kvp.Value.Reset(); + } + + HeaderDescriptors.Empty(); +} + +FArchive& operator<<(FArchive& Ar, FUHTMakefileModuleDescriptor& UHTMakefileModuleDescriptor) +{ + Ar << UHTMakefileModuleDescriptor.PackageIndex; + Ar << UHTMakefileModuleDescriptor.HeaderDescriptors; + Ar << UHTMakefileModuleDescriptor.HeadersOrder; + + return Ar; +} diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/UHTMakefileModuleDescriptor.h b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/UHTMakefileModuleDescriptor.h new file mode 100644 index 000000000000..a05b8738a82b --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/UHTMakefileModuleDescriptor.h @@ -0,0 +1,117 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#pragma once +#include "UHTMakefile/UHTMakefileHeaderDescriptor.h" + +class FUHTMakefile; +struct FManifestModule; + +/* See UHTMakefile.h for overview how makefiles work. */ +class FUHTMakefileModuleDescriptor +{ +public: + FUHTMakefileModuleDescriptor(FUHTMakefile* InUHTMakefile = nullptr) + : UHTMakefile(InUHTMakefile) + , Package(nullptr) + , LoadingPhase(EUHTMakefileLoadingPhase::Preload) + { + HeadersOrder.AddDefaulted(+EUHTMakefileLoadingPhase::Max); + } + + void StopPreloading() + { + LoadingPhase = EUHTMakefileLoadingPhase::Max; + + for (auto& Kvp : HeaderDescriptors) + { + Kvp.Value.StopPreloading(); + } + } + + void StartLoading() + { + LoadingPhase = EUHTMakefileLoadingPhase::Load; + + for (auto& Kvp : HeaderDescriptors) + { + Kvp.Value.StartLoading(); + } + } + + void StopLoading() + { + LoadingPhase = EUHTMakefileLoadingPhase::Max; + + for (auto& Kvp : HeaderDescriptors) + { + Kvp.Value.StopLoading(); + } + } + + void StartExporting() + { + LoadingPhase = EUHTMakefileLoadingPhase::Export; + + for (auto& Kvp : HeaderDescriptors) + { + Kvp.Value.StartExporting(); + } + } + + void StopExporting() + { + LoadingPhase = EUHTMakefileLoadingPhase::Max; + + for (auto& Kvp : HeaderDescriptors) + { + Kvp.Value.StopExporting(); + } + } + + void SetPackageIndex(int32 Index); + + int32 GetPackageIndex() const + { + return PackageIndex; + } + + bool HasHeaderDescriptor(int32 Index) + { + return HeaderDescriptors.Contains(Index); + } + + FUHTMakefileHeaderDescriptor& GetHeaderDescriptor(int32 Index) + { + FUHTMakefileHeaderDescriptor& HeaderDescriptor = HeaderDescriptors.FindOrAdd(Index); + HeaderDescriptor.SetMakefile(UHTMakefile); + return HeaderDescriptor; + } + void LoadModuleData(const FManifestModule& ManifestModule, EUHTMakefileLoadingPhase UHTMakefileLoadingPhase); + void SetMakefile(FUHTMakefile* InUHTMakefile) + { + UHTMakefile = InUHTMakefile; + } + + UPackage* GetPackage() const + { + return Package; + } + + void SetPackage(UPackage* InPackage) + { + Package = InPackage; + } + + void AddToHeaderOrder(FUnrealSourceFile* SourceFile); + void Reset(); +private: + friend FArchive& operator<<(FArchive& Ar, FUHTMakefileModuleDescriptor& UHTMakefileModuleDescriptor); + /** Index of this module's package in UHTMakefile Packages array. */ + int32 PackageIndex; + TMap HeaderDescriptors; + TArray> HeadersOrder; + + EUHTMakefileLoadingPhase LoadingPhase; + FUHTMakefile* UHTMakefile; + UPackage* Package; +}; diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/UnrealSourceFileArchiveProxy.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/UnrealSourceFileArchiveProxy.cpp new file mode 100644 index 000000000000..30d674f0b97b --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/UnrealSourceFileArchiveProxy.cpp @@ -0,0 +1,110 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#include "UnrealHeaderTool.h" +#include "UHTMakefile/UHTMakefile.h" +#include "UHTMakefile/UnrealSourceFileArchiveProxy.h" +#include "UnrealSourceFile.h" + +FUnrealSourceFileArchiveProxy::FUnrealSourceFileArchiveProxy(FUHTMakefile& UHTMakefile, FUnrealSourceFile* UnrealSourceFile) +{ + ScopeIndex = UHTMakefile.GetFileScopeIndex(&UnrealSourceFile->GetScope().Get()); + Filename = UnrealSourceFile->GetFilename(); + + UPackage* Package = UnrealSourceFile->GetPackage(); + PackageIndex = UHTMakefile.GetPackageIndex(Package); + + GeneratedFileName = UnrealSourceFile->GetGeneratedFilename(); + ModuleRelativePath = UnrealSourceFile->GetModuleRelativePath(); + IncludePath = UnrealSourceFile->GetIncludePath(); + + Includes.Empty(UnrealSourceFile->GetIncludes().Num()); + + for (FHeaderProvider& HeaderProvider : UnrealSourceFile->GetIncludes()) + { + if (HeaderProvider.GetId().EndsWith(TEXT(".h"))) + { + FHeaderProviderArchiveProxy HeaderProviderArchiveProxy; + HeaderProviderArchiveProxy.Type = static_cast(HeaderProvider.GetType()); + HeaderProviderArchiveProxy.Id = HeaderProvider.GetId(); + HeaderProviderArchiveProxy.CacheIndex = UHTMakefile.GetUnrealSourceFileIndex(HeaderProvider.Resolve()); + HeaderProviderArchiveProxy.bAutoInclude = HeaderProvider.IsAutoInclude(); + + Includes.Add(HeaderProviderArchiveProxy); + } + } + + DefinedClassesWithParsingInfo.Empty(UnrealSourceFile->GetDefinedClasses().Num()); + + for (auto& Kvp : UnrealSourceFile->GetDefinedClassesWithParsingInfo()) + { + DefinedClassesWithParsingInfo.Add(TPairInitializer(UHTMakefile.GetClassIndex(Kvp.Key), Kvp.Value)); + } + + GeneratedCodeVersionsArchiveProxy.Empty(UnrealSourceFile->GetGeneratedCodeVersions().Num()); + + for (auto& Kvp : UnrealSourceFile->GetGeneratedCodeVersions()) + { + FSerializeIndex StructIndex = UHTMakefile.GetStructIndex(Kvp.Key); + uint8 GeneratedCodeVersion = static_cast(Kvp.Value); + + GeneratedCodeVersionsArchiveProxy.Add(TPairInitializer(StructIndex, GeneratedCodeVersion)); + } +} + +FArchive& operator<<(FArchive& Ar, FUnrealSourceFileArchiveProxy& UnrealSourceFileArchiveProxy) +{ + Ar << UnrealSourceFileArchiveProxy.ScopeIndex; + Ar << UnrealSourceFileArchiveProxy.Filename; + Ar << UnrealSourceFileArchiveProxy.PackageIndex; + Ar << UnrealSourceFileArchiveProxy.GeneratedFileName; + Ar << UnrealSourceFileArchiveProxy.ModuleRelativePath; + Ar << UnrealSourceFileArchiveProxy.IncludePath; + Ar << UnrealSourceFileArchiveProxy.Includes; + Ar << UnrealSourceFileArchiveProxy.DefinedClassesWithParsingInfo; + Ar << UnrealSourceFileArchiveProxy.GeneratedCodeVersionsArchiveProxy; + + return Ar; +} + +FUnrealSourceFile* FUnrealSourceFileArchiveProxy::CreateUnrealSourceFile(FUHTMakefile& UHTMakefile) +{ + UPackage* Package = UHTMakefile.GetPackageByIndex(PackageIndex); + FUnrealSourceFile* UnrealSourceFile = new FUnrealSourceFile(Package, Filename, FString()); + UnrealSourceFile->SetGeneratedFilename(GeneratedFileName); + UnrealSourceFile->SetModuleRelativePath(ModuleRelativePath); + UnrealSourceFile->SetIncludePath(IncludePath); + GeneratedCodeVersionsArchiveProxy.Empty(UnrealSourceFile->GetGeneratedCodeVersions().Num()); + + UHTMakefile.AddFileScope(&UnrealSourceFile->GetScope().Get()); + + return UnrealSourceFile; +} + +void FUnrealSourceFileArchiveProxy::Resolve(FUnrealSourceFile* UnrealSourceFile, const FUHTMakefile& UHTMakefile) +{ + UnrealSourceFile->SetScope(UHTMakefile.GetFileScopeByIndex(ScopeIndex)); + UnrealSourceFile->GetIncludes().Empty(Includes.Num()); + for (const FHeaderProviderArchiveProxy& Include : Includes) + { + FHeaderProvider HeaderProvider = FHeaderProvider(static_cast(Include.Type), Include.Id, Include.bAutoInclude); + HeaderProvider.SetCache(UHTMakefile.GetUnrealSourceFileByIndex(Include.CacheIndex)); + UnrealSourceFile->GetIncludes().Add(HeaderProvider); + } + + for (const auto& Kvp : DefinedClassesWithParsingInfo) + { + UClass* Class = UHTMakefile.GetClassByIndex(Kvp.Key); + FSimplifiedParsingClassInfo SimplifiedClassParsingInfo = Kvp.Value; + UnrealSourceFile->AddDefinedClass(Class, SimplifiedClassParsingInfo); + } + + UnrealSourceFile->GetGeneratedCodeVersions().Empty(GeneratedCodeVersionsArchiveProxy.Num()); + for (const auto& Kvp : GeneratedCodeVersionsArchiveProxy) + { + EGeneratedCodeVersion GeneratedCodeVersion = static_cast(Kvp.Value); + UStruct* Struct = UHTMakefile.GetStructByIndex(Kvp.Key); + UnrealSourceFile->GetGeneratedCodeVersions().Add(Struct, GeneratedCodeVersion); + } + + UHTMakefile.ResolveFileScope(ScopeIndex); +} \ No newline at end of file diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/UnrealSourceFileArchiveProxy.h b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/UnrealSourceFileArchiveProxy.h new file mode 100644 index 000000000000..edbcf2711410 --- /dev/null +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UHTMakefile/UnrealSourceFileArchiveProxy.h @@ -0,0 +1,31 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#pragma once +#include "HeaderProviderArchiveProxy.h" +#include "SimplifiedParsingClassInfo.h" + +class FUHTMakefile; +class FUnrealSourceFile; + +/* See UHTMakefile.h for overview how makefiles work. */ +struct FUnrealSourceFileArchiveProxy +{ + FUnrealSourceFileArchiveProxy(FUHTMakefile& UHTMakefile, FUnrealSourceFile* UnrealSourceFile); + FUnrealSourceFileArchiveProxy() { } + + friend FArchive& operator<<(FArchive& Ar, FUnrealSourceFileArchiveProxy& UnrealSourceFileArchiveProxy); + FUnrealSourceFile* CreateUnrealSourceFile(FUHTMakefile& UHTMakefile); + void Resolve(FUnrealSourceFile* UnrealSourceFile, const FUHTMakefile& UHTMakefile); + + int32 ScopeIndex; + int32 PackageIndex; + + FString Filename; + FString GeneratedFileName; + FString ModuleRelativePath; + FString IncludePath; + + TArray Includes; + TArray> DefinedClassesWithParsingInfo; + TArray> GeneratedCodeVersionsArchiveProxy; +}; diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UnrealSourceFile.cpp b/Engine/Source/Programs/UnrealHeaderTool/Private/UnrealSourceFile.cpp index 82bb6ecfbc1f..15b964c7d9ae 100644 --- a/Engine/Source/Programs/UnrealHeaderTool/Private/UnrealSourceFile.cpp +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UnrealSourceFile.cpp @@ -5,6 +5,7 @@ #include "ParserHelper.h" #include "HeaderParser.h" #include "GeneratedCodeVersion.h" +#include "Scope.h" void FUnrealSourceFile::AddDefinedClass(UClass* Class, FSimplifiedParsingClassInfo ParsingInfo) { @@ -121,6 +122,19 @@ bool FUnrealSourceFile::AreDependenciesResolved() const return bDependenciesResolved; } +void FUnrealSourceFile::SetScope(FFileScope* InScope) +{ + if (&Scope.Get() != InScope) + { + Scope = TSharedRef(InScope); + } +} + +void FUnrealSourceFile::SetScope(TSharedRef InScope) +{ + Scope = InScope; +} + void FUnrealSourceFile::MarkAsParsed() { bParsed = true; diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UnrealSourceFile.h b/Engine/Source/Programs/UnrealHeaderTool/Private/UnrealSourceFile.h index 0fc330c58b7e..85b728607391 100644 --- a/Engine/Source/Programs/UnrealHeaderTool/Private/UnrealSourceFile.h +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UnrealSourceFile.h @@ -13,7 +13,7 @@ class FClassMetaData; /** * Contains information about source file that defines various UHT aware types. */ -class FUnrealSourceFile +class FUnrealSourceFile : public TSharedFromThis { public: // Constructor. @@ -281,8 +281,11 @@ public: * Checks if dependencies has been resolved. */ bool AreDependenciesResolved() const; - + friend FArchive& operator<<(FArchive& Ar, FUnrealSourceFile& UHTMakefile); + void SetScope(FFileScope* Scope); + void SetScope(TSharedRef Scope); private: + // File scope. TSharedRef Scope; diff --git a/Engine/Source/Programs/UnrealHeaderTool/Private/UnrealTypeDefinitionInfo.h b/Engine/Source/Programs/UnrealHeaderTool/Private/UnrealTypeDefinitionInfo.h index cb70af42622d..c153147037ad 100644 --- a/Engine/Source/Programs/UnrealHeaderTool/Private/UnrealTypeDefinitionInfo.h +++ b/Engine/Source/Programs/UnrealHeaderTool/Private/UnrealTypeDefinitionInfo.h @@ -8,14 +8,14 @@ class FUnrealSourceFile; /** * Class that stores information about type (USTRUCT/UCLASS) definition. */ -class FUnrealTypeDefinitionInfo +class FUnrealTypeDefinitionInfo : public TSharedFromThis { public: // Constructor FUnrealTypeDefinitionInfo(FUnrealSourceFile& InSourceFile, int32 InLineNumber) : SourceFile(InSourceFile) , LineNumber(InLineNumber) - {} + { } /** * Gets the line number in source file this type was defined in. @@ -34,7 +34,19 @@ public: return SourceFile; } + void SetLineNumber(int32 InLineNumber) + { + LineNumber = InLineNumber; + } + + const FUnrealSourceFile& GetUnrealSourceFile() const + { + return SourceFile; + } + private: FUnrealSourceFile& SourceFile; int32 LineNumber; -}; \ No newline at end of file + + friend struct FUnrealTypeDefinitionInfoArchiveProxy; +}; diff --git a/Engine/Source/Programs/UnrealHeaderTool/Public/IScriptGeneratorPluginInterface.h b/Engine/Source/Programs/UnrealHeaderTool/Public/IScriptGeneratorPluginInterface.h index 34d2b86e046a..85cfabece24b 100644 --- a/Engine/Source/Programs/UnrealHeaderTool/Public/IScriptGeneratorPluginInterface.h +++ b/Engine/Source/Programs/UnrealHeaderTool/Public/IScriptGeneratorPluginInterface.h @@ -20,7 +20,21 @@ struct EBuildModuleType Max }; - + friend FArchive& operator<<(FArchive& Ar, EBuildModuleType::Type& Type) + { + if (Ar.IsLoading()) + { + uint8 Value; + Ar << Value; + Type = (EBuildModuleType::Type)Value; + } + else if (Ar.IsSaving()) + { + uint8 Value = (uint8)Type; + Ar << Value; + } + return Ar; + } /** * Converts a string literal into EModuleType::Type value * diff --git a/Engine/Source/Programs/UnrealHeaderTool/Public/UnrealHeaderTool.h b/Engine/Source/Programs/UnrealHeaderTool/Public/UnrealHeaderTool.h index 797e6fa2975e..8555e487a58c 100644 --- a/Engine/Source/Programs/UnrealHeaderTool/Public/UnrealHeaderTool.h +++ b/Engine/Source/Programs/UnrealHeaderTool/Public/UnrealHeaderTool.h @@ -7,6 +7,7 @@ #include "Core.h" #include "CoreUObject.h" #include "CompilationResult.h" +#include "UHTMakefile/MakefileHelpers.h" DECLARE_LOG_CATEGORY_EXTERN(LogCompile, Log, All); diff --git a/Engine/Source/Programs/UnrealHeaderTool/Resources/UHTDebugging/TestObject.h b/Engine/Source/Programs/UnrealHeaderTool/Resources/UHTDebugging/TestObject.h index 0e6670e340ba..2786b4381bc5 100644 --- a/Engine/Source/Programs/UnrealHeaderTool/Resources/UHTDebugging/TestObject.h +++ b/Engine/Source/Programs/UnrealHeaderTool/Resources/UHTDebugging/TestObject.h @@ -5,6 +5,8 @@ #include "EnumOnlyHeader.h" #include "TestObject.generated.h" +PRAGMA_DISABLE_DEPRECATION_WARNINGS + UCLASS() class UTestObject : public UObject { @@ -118,3 +120,5 @@ public: UFUNCTION() int x; #endif }; + +PRAGMA_ENABLE_DEPRECATION_WARNINGS diff --git a/Engine/Source/Programs/UnrealHeaderTool/UnrealHeaderTool.Build.cs b/Engine/Source/Programs/UnrealHeaderTool/UnrealHeaderTool.Build.cs index c3b34d56d118..0424c08973aa 100644 --- a/Engine/Source/Programs/UnrealHeaderTool/UnrealHeaderTool.Build.cs +++ b/Engine/Source/Programs/UnrealHeaderTool/UnrealHeaderTool.Build.cs @@ -18,7 +18,13 @@ public class UnrealHeaderTool : ModuleRules } ); - PrivateIncludePaths.Add("Runtime/Launch/Private"); // For LaunchEngineLoop.cpp include + PrivateIncludePaths.AddRange( + new string[] + { + // For LaunchEngineLoop.cpp include + "Runtime/Launch/Private", + "Programs/UnrealHeaderTool/Private", + }); bEnableExceptions = true; } diff --git a/Engine/Source/Runtime/AssetRegistry/Private/AssetRegistry.cpp b/Engine/Source/Runtime/AssetRegistry/Private/AssetRegistry.cpp index 84cf56401fdd..218b8d4235f4 100644 --- a/Engine/Source/Runtime/AssetRegistry/Private/AssetRegistry.cpp +++ b/Engine/Source/Runtime/AssetRegistry/Private/AssetRegistry.cpp @@ -1555,6 +1555,66 @@ void AddDependsNodesRecursive(FDependsNode* InDependsNode, TArray } } +FDependsNode* FAssetRegistry::ResolveRedirector(FDependsNode* InDependency, TMap& InAllowedAssets, TMap& InCache) +{ + static const FName ObjectRedirectorClassName(TEXT("ObjectRedirector")); + + FDependsNode* Original = InDependency; + FDependsNode* Result = nullptr; + + if (InCache.Contains(InDependency)) + { + return InCache[InDependency]; + } + + while (Result == nullptr) + { + if (CachedAssetsByPackageName.Contains(InDependency->GetPackageName())) + { + auto DependencyAsset = CachedAssetsByPackageName[InDependency->GetPackageName()][0]; + + if (DependencyAsset->AssetClass == ObjectRedirectorClassName) + { + FDependsNode* Before = InDependency; + + InDependency->IterateOverDependencies([&](FDependsNode* InDepends, EAssetRegistryDependencyType::Type) + { + if (InAllowedAssets.Contains(InDepends->GetPackageName())) + { + Result = InDepends; + } + else if (CachedAssetsByPackageName.Contains(InDepends->GetPackageName())) + { + auto SubDependencyAsset = CachedAssetsByPackageName[InDepends->GetPackageName()][0]; + if (SubDependencyAsset->AssetClass == ObjectRedirectorClassName) + { + InDependency = InDepends; + } + } + }); + + if (Result == nullptr && InDependency == Before) + { + // The redirector doesn't point at any files that were cooked. Allow function to return with + // result as nullptr to indicate this + break; + } + } + else + { + Result = InDependency; + } + } + else + { + Result = InDependency; + } + } + + InCache.Add(Original, Result); + return Result; +} + void FAssetRegistry::SaveRegistryData(FArchive& Ar, TMap& Data, TArray* InMaps /* = nullptr */) { // Write mini asset registry header @@ -1614,73 +1674,60 @@ void FAssetRegistry::SaveRegistryData(FArchive& Ar, TMap& Da } } + TArray ProcessedDependencies; + TMap DependencyTypeCounts; + TMap RedirectCache; + for (auto DependentNode : Dependencies) { - auto HardDependencyCount = 0; - auto SoftDependencyCount = 0; - auto ReferencerCount = 0; + ProcessedDependencies.Empty(); + DependencyTypeCounts.Empty(); + DependencyTypeCounts.Add(EAssetRegistryDependencyType::Hard, 0); + DependencyTypeCounts.Add(EAssetRegistryDependencyType::Soft, 0); - DependentNode->IterateOverDependencies([&](FDependsNode* InDependency, EAssetRegistryDependencyType::Type InDependencyType) + auto DependencyProcessor = [&](FDependsNode* InDependency, EAssetRegistryDependencyType::Type InDependencyType) { bool bIsMap = InMaps && InMaps->Contains(InDependency->GetPackageName()); - if (!bIsMap && AssetIndexMap.Contains(InDependency->GetPackageName())) + if (!bIsMap) { - if (InDependencyType == EAssetRegistryDependencyType::Hard) + auto RedirectedDependency = ResolveRedirector(InDependency, Data, RedirectCache); + + if (RedirectedDependency && AssetIndexMap.Contains(RedirectedDependency->GetPackageName())) { - HardDependencyCount++; - } - else - { - SoftDependencyCount++; + ProcessedDependencies.Add(InDependency); + DependencyTypeCounts[InDependencyType] = DependencyTypeCounts[InDependencyType] + 1; } } - }); + }; + DependentNode->IterateOverDependencies(DependencyProcessor, EAssetRegistryDependencyType::Hard); + DependentNode->IterateOverDependencies(DependencyProcessor, EAssetRegistryDependencyType::Soft); + + int32 ReferencerCount = 0; DependentNode->IterateOverReferencers([&](FDependsNode* InReferencer) { if (AssetIndexMap.Contains(InReferencer->GetPackageName())) { + ProcessedDependencies.Add(InReferencer); ReferencerCount++; } }); + int32 HardDependencyCount = DependencyTypeCounts[EAssetRegistryDependencyType::Hard]; + int32 SoftDependencyCount = DependencyTypeCounts[EAssetRegistryDependencyType::Soft]; + Ar << HardDependencyCount; Ar << SoftDependencyCount; Ar << ReferencerCount; - DependentNode->IterateOverDependencies([&](FDependsNode* InDependency, EAssetRegistryDependencyType::Type InDependencyType) + for (auto Dependency : ProcessedDependencies) { - bool bIsMap = InMaps && InMaps->Contains(InDependency->GetPackageName()); - - if (!bIsMap && AssetIndexMap.Contains(InDependency->GetPackageName())) - { - int32 Index = AssetIndexMap[InDependency->GetPackageName()]; - Ar << Index; - } - }, - EAssetRegistryDependencyType::Hard); - - DependentNode->IterateOverDependencies([&](FDependsNode* InDependency, EAssetRegistryDependencyType::Type InDependencyType) - { - bool bIsMap = InMaps && InMaps->Contains(InDependency->GetPackageName()); - - if (!bIsMap && AssetIndexMap.Contains(InDependency->GetPackageName())) - { - int32 Index = AssetIndexMap[InDependency->GetPackageName()]; - Ar << Index; - } - }, - EAssetRegistryDependencyType::Soft); - - DependentNode->IterateOverReferencers([&](FDependsNode* InReferencer) - { - if (AssetIndexMap.Contains(InReferencer->GetPackageName())) - { - int32 Index = AssetIndexMap[InReferencer->GetPackageName()]; - Ar << Index; - } - }); + FDependsNode* RedirectedDependency = ResolveRedirector(Dependency, Data, RedirectCache); + check(RedirectedDependency); + int32 Index = AssetIndexMap[RedirectedDependency->GetPackageName()]; + Ar << Index; + } } } diff --git a/Engine/Source/Runtime/AssetRegistry/Private/AssetRegistry.h b/Engine/Source/Runtime/AssetRegistry/Private/AssetRegistry.h index 7f80418e6831..ddd9612500fc 100644 --- a/Engine/Source/Runtime/AssetRegistry/Private/AssetRegistry.h +++ b/Engine/Source/Runtime/AssetRegistry/Private/AssetRegistry.h @@ -129,6 +129,9 @@ private: /** Removes the asset data from the lookup maps */ bool RemoveAssetData(FAssetData* AssetData); + /** Find the first non-redirector dependency node starting from InDependency. */ + FDependsNode* ResolveRedirector(FDependsNode* InDependency, TMap& InAllowedAssets, TMap& InCache); + /** * Adds a root path to be discover files in, when asynchronously scanning the disk for asset files * diff --git a/Engine/Source/Runtime/Core/Private/Android/AndroidMemory.cpp b/Engine/Source/Runtime/Core/Private/Android/AndroidMemory.cpp index 25001ad3c226..9787b6c4eaf4 100644 --- a/Engine/Source/Runtime/Core/Private/Android/AndroidMemory.cpp +++ b/Engine/Source/Runtime/Core/Private/Android/AndroidMemory.cpp @@ -1,7 +1,7 @@ // Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. #include "CorePrivatePCH.h" -#include "MallocBinned.h" +#include "MallocBinned2.h" #include "MallocAnsi.h" #include "unistd.h" #include @@ -35,6 +35,8 @@ static int64 GetNativeHeapAllocatedSize() void FAndroidPlatformMemory::Init() { + FGenericPlatformMemory::Init(); + const FPlatformMemoryConstants& MemoryConstants = FPlatformMemory::GetConstants(); FPlatformMemoryStats MemoryStats = GetStats(); UE_LOG(LogInit, Log, TEXT("Memory total: Physical=%.2fMB (%dGB approx) Available=%.2fMB PageSize=%.1fKB"), @@ -90,7 +92,7 @@ FMalloc* FAndroidPlatformMemory::BaseAllocator() uint64 MemoryLimit = FMath::Min( uint64(1) << FMath::CeilLogTwo(MemoryConstants.TotalPhysical), 0x100000000); //return new FMallocAnsi(); - return new FMallocBinned(MemoryConstants.PageSize, MemoryLimit); + return new FMallocBinned2(MemoryConstants.PageSize, MemoryLimit); } void* FAndroidPlatformMemory::BinnedAllocFromOS( SIZE_T Size ) diff --git a/Engine/Source/Runtime/Core/Private/GenericPlatform/GenericPlatformCrashContext.cpp b/Engine/Source/Runtime/Core/Private/GenericPlatform/GenericPlatformCrashContext.cpp index 7d74d9fe17b6..6a2cdc9c7a94 100644 --- a/Engine/Source/Runtime/Core/Private/GenericPlatform/GenericPlatformCrashContext.cpp +++ b/Engine/Source/Runtime/Core/Private/GenericPlatform/GenericPlatformCrashContext.cpp @@ -93,7 +93,7 @@ void FGenericCrashContext::Initialize() const FGuid Guid = FGuid::NewGuid(); NCachedCrashContextProperties::CrashGUID = FString::Printf(TEXT("UE4CC-%s-%s"), *NCachedCrashContextProperties::PlatformNameIni, *Guid.ToString(EGuidFormats::Digits)); - // Initialize delegate for updating SecondsSinceStart, beacuse FPlatformTime::Seconds() is not POSIX safe. + // Initialize delegate for updating SecondsSinceStart, because FPlatformTime::Seconds() is not POSIX safe. const float PollingInterval = 1.0f; FTicker::GetCoreTicker().AddTicker( FTickerDelegate::CreateLambda( []( float DeltaTime ) { diff --git a/Engine/Source/Runtime/Core/Private/GenericPlatform/GenericPlatformMemory.cpp b/Engine/Source/Runtime/Core/Private/GenericPlatform/GenericPlatformMemory.cpp index 90e78ac049ae..d3e76971dd5f 100644 --- a/Engine/Source/Runtime/Core/Private/GenericPlatform/GenericPlatformMemory.cpp +++ b/Engine/Source/Runtime/Core/Private/GenericPlatform/GenericPlatformMemory.cpp @@ -1,6 +1,8 @@ // Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. #include "CorePrivatePCH.h" +#include "Ticker.h" +#include "Async.h" #include "MallocAnsi.h" #include "GenericPlatformMemoryPoolStats.h" #include "MemoryMisc.h" @@ -22,6 +24,41 @@ DEFINE_STAT(STAT_PeakUsedPhysical); DEFINE_STAT(STAT_UsedVirtual); DEFINE_STAT(STAT_PeakUsedVirtual); +/** Helper class used to update platform memory stats. */ +struct FGenericStatsUpdater +{ + /** Called once per second, enqueues stats update. */ + static bool EnqueueUpdateStats( float /*InDeltaTime*/ ) + { + AsyncTask( ENamedThreads::AnyThread, []() + { + DoUpdateStats(); + } ); + return true; // Tick again + } + + /** Gathers and sets all platform memory statistics into the corresponding stats. */ + static void DoUpdateStats() + { + // This is slow, so do it on the task graph. + FPlatformMemoryStats MemoryStats = FPlatformMemory::GetStats(); + SET_MEMORY_STAT( STAT_TotalPhysical, MemoryStats.TotalPhysical ); + SET_MEMORY_STAT( STAT_TotalVirtual, MemoryStats.TotalVirtual ); + SET_MEMORY_STAT( STAT_PageSize, MemoryStats.PageSize ); + SET_MEMORY_STAT( STAT_TotalPhysicalGB, MemoryStats.TotalPhysicalGB ); + + SET_MEMORY_STAT( STAT_AvailablePhysical, MemoryStats.AvailablePhysical ); + SET_MEMORY_STAT( STAT_AvailableVirtual, MemoryStats.AvailableVirtual ); + SET_MEMORY_STAT( STAT_UsedPhysical, MemoryStats.UsedPhysical ); + SET_MEMORY_STAT( STAT_PeakUsedPhysical, MemoryStats.PeakUsedPhysical ); + SET_MEMORY_STAT( STAT_UsedVirtual, MemoryStats.UsedVirtual ); + SET_MEMORY_STAT( STAT_PeakUsedVirtual, MemoryStats.PeakUsedVirtual ); + + // Platform specific stats. + FPlatformMemory::InternalUpdateStats( MemoryStats ); + } +}; + FGenericPlatformMemoryStats::FGenericPlatformMemoryStats() : FGenericPlatformMemoryConstants( FPlatformMemory::GetConstants() ) , AvailablePhysical( 0 ) @@ -57,8 +94,19 @@ void FGenericPlatformMemory::SetupMemoryPools() void FGenericPlatformMemory::Init() { - SetupMemoryPools(); - UE_LOG(LogMemory, Warning, TEXT("FGenericPlatformMemory::Init not implemented on this platform")); + if (FPlatformMemory::SupportBackupMemoryPool()) + { + SetupMemoryPools(); + } + +#if STATS + // Stats are updated only once per second. + const float PollingInterval = 1.0f; + FTicker::GetCoreTicker().AddTicker( FTickerDelegate::CreateStatic( &FGenericStatsUpdater::EnqueueUpdateStats ), PollingInterval ); + + // Update for the first time. + FGenericStatsUpdater::DoUpdateStats(); +#endif // STATS } void FGenericPlatformMemory::OnOutOfMemory(uint64 Size, uint32 Alignment) @@ -108,16 +156,16 @@ void FGenericPlatformMemory::GetStatsForMallocProfiler( FGenericMemoryStats& out FPlatformMemoryStats Stats = FPlatformMemory::GetStats(); // Base common stats for all platforms. - out_Stats.Add(TEXT("Total Physical"), Stats.TotalPhysical ); - out_Stats.Add(TEXT("Total Virtual"), Stats.TotalVirtual ); - out_Stats.Add(TEXT("Page Size"), Stats.PageSize ); - out_Stats.Add(TEXT("Total Physical GB"), (SIZE_T)Stats.TotalPhysicalGB ); - out_Stats.Add(TEXT("Available Physical"), Stats.AvailablePhysical ); - out_Stats.Add(TEXT("Available Virtual"), Stats.AvailableVirtual ); - out_Stats.Add(TEXT("Used Physical"), Stats.UsedPhysical ); - out_Stats.Add(TEXT("Peak Used Physical"), Stats.PeakUsedPhysical ); - out_Stats.Add(TEXT("Used Virtual"), Stats.UsedVirtual ); - out_Stats.Add(TEXT("Peak Used Virtual"), Stats.PeakUsedVirtual ); + out_Stats.Add( GET_STATDESCRIPTION( STAT_TotalPhysical ), Stats.TotalPhysical ); + out_Stats.Add( GET_STATDESCRIPTION( STAT_TotalVirtual ), Stats.TotalVirtual ); + out_Stats.Add( GET_STATDESCRIPTION( STAT_PageSize ), Stats.PageSize ); + out_Stats.Add( GET_STATDESCRIPTION( STAT_TotalPhysicalGB ), (SIZE_T)Stats.TotalPhysicalGB ); + out_Stats.Add( GET_STATDESCRIPTION( STAT_AvailablePhysical ), Stats.AvailablePhysical ); + out_Stats.Add( GET_STATDESCRIPTION( STAT_AvailableVirtual ), Stats.AvailableVirtual ); + out_Stats.Add( GET_STATDESCRIPTION( STAT_UsedPhysical ), Stats.UsedPhysical ); + out_Stats.Add( GET_STATDESCRIPTION( STAT_PeakUsedPhysical ), Stats.PeakUsedPhysical ); + out_Stats.Add( GET_STATDESCRIPTION( STAT_UsedVirtual ), Stats.UsedVirtual ); + out_Stats.Add( GET_STATDESCRIPTION( STAT_PeakUsedVirtual ), Stats.PeakUsedVirtual ); #endif // STATS } @@ -133,29 +181,6 @@ uint32 FGenericPlatformMemory::GetPhysicalGBRam() return FPlatformMemory::GetConstants().TotalPhysicalGB; } -void FGenericPlatformMemory::UpdateStats() -{ - // avoid getting OS data (costly on Linux, Windows - see CL 2460429) if we aren't collecting stats -#if STATS - if (FThreadStats::IsCollectingData(GET_STATID(STAT_TotalPhysical))) - { - FPlatformMemoryStats MemoryStats = FPlatformMemory::GetStats(); - - SET_MEMORY_STAT(STAT_TotalPhysical,MemoryStats.TotalPhysical); - SET_MEMORY_STAT(STAT_TotalVirtual,MemoryStats.TotalVirtual); - SET_MEMORY_STAT(STAT_PageSize,MemoryStats.PageSize); - SET_MEMORY_STAT(STAT_TotalPhysicalGB,MemoryStats.TotalPhysicalGB); - - SET_MEMORY_STAT(STAT_AvailablePhysical,MemoryStats.AvailablePhysical); - SET_MEMORY_STAT(STAT_AvailableVirtual,MemoryStats.AvailableVirtual); - SET_MEMORY_STAT(STAT_UsedPhysical,MemoryStats.UsedPhysical); - SET_MEMORY_STAT(STAT_PeakUsedPhysical,MemoryStats.PeakUsedPhysical); - SET_MEMORY_STAT(STAT_UsedVirtual,MemoryStats.UsedVirtual); - SET_MEMORY_STAT(STAT_PeakUsedVirtual,MemoryStats.PeakUsedVirtual); - } -#endif // STATS -} - void* FGenericPlatformMemory::BinnedAllocFromOS( SIZE_T Size ) { UE_LOG(LogMemory, Error, TEXT("FGenericPlatformMemory::BinnedAllocFromOS not implemented on this platform")); @@ -287,3 +312,9 @@ bool FGenericPlatformMemory::UnmapNamedSharedMemoryRegion(FSharedMemoryRegion * UE_LOG(LogHAL, Error, TEXT("FGenericPlatformMemory::UnmapNamedSharedMemoryRegion not implemented on this platform")); return false; } + + +void FGenericPlatformMemory::InternalUpdateStats( const FPlatformMemoryStats& MemoryStats ) +{ + // Generic method is empty. Implement at platform level. +} \ No newline at end of file diff --git a/Engine/Source/Runtime/Core/Private/HAL/MallocBinned.cpp b/Engine/Source/Runtime/Core/Private/HAL/MallocBinned.cpp index 0d5ddcfddc10..7f52b650423f 100644 --- a/Engine/Source/Runtime/Core/Private/HAL/MallocBinned.cpp +++ b/Engine/Source/Runtime/Core/Private/HAL/MallocBinned.cpp @@ -20,6 +20,655 @@ DEFINE_STAT(STAT_Binned_CurrentAllocs); DEFINE_STAT(STAT_Binned_TotalAllocs); DEFINE_STAT(STAT_Binned_SlackCurrent); +/** Information about a piece of free memory. 8 bytes */ +struct FMallocBinned::FFreeMem +{ + /** Next or MemLastPool[], always in order by pool. */ + FFreeMem* Next; + /** Number of consecutive free blocks here, at least 1. */ + uint32 NumFreeBlocks; +}; + +// Memory pool info. 32 bytes. +struct FMallocBinned::FPoolInfo +{ + /** Number of allocated elements in this pool, when counts down to zero can free the entire pool. */ + uint16 Taken; // 2 + /** Index of pool. Index into MemSizeToPoolTable[]. Valid when < MAX_POOLED_ALLOCATION_SIZE, MAX_POOLED_ALLOCATION_SIZE is OsTable. + When AllocSize is 0, this is the number of pages to step back to find the base address of an allocation. See FindPoolInfoInternal() + */ + uint16 TableIndex; // 4 + /** Number of bytes allocated */ + uint32 AllocSize; // 8 + /** Pointer to first free memory in this pool or the OS Allocation Size in bytes if this allocation is not binned*/ + FFreeMem* FirstMem; // 12/16 + FPoolInfo* Next; // 16/24 + FPoolInfo** PrevLink; // 20/32 +#if PLATFORM_32BITS + /** Explicit padding for 32 bit builds */ + uint8 Padding[12]; // 32 +#endif + + void SetAllocationSizes( uint32 InBytes, UPTRINT InOsBytes, uint32 InTableIndex, uint32 SmallAllocLimt ) + { + TableIndex=InTableIndex; + AllocSize=InBytes; + if (TableIndex == SmallAllocLimt) + { + FirstMem=(FFreeMem*)InOsBytes; + } + } + + uint32 GetBytes() const + { + return AllocSize; + } + + UPTRINT GetOsBytes( uint32 InPageSize, uint32 SmallAllocLimt ) const + { + if (TableIndex == SmallAllocLimt) + { + return (UPTRINT)FirstMem; + } + else + { + return Align(AllocSize, InPageSize); + } + } + + void Link( FPoolInfo*& Before ) + { + if( Before ) + { + Before->PrevLink = &Next; + } + Next = Before; + PrevLink = &Before; + Before = this; + } + + void Unlink() + { + if( Next ) + { + Next->PrevLink = PrevLink; + } + *PrevLink = Next; + } +}; + +/** Hash table struct for retrieving allocation book keeping information */ +struct FMallocBinned::PoolHashBucket +{ + UPTRINT Key; + FPoolInfo* FirstPool; + PoolHashBucket* Prev; + PoolHashBucket* Next; + + PoolHashBucket() + { + Key=0; + FirstPool=nullptr; + Prev=this; + Next=this; + } + + void Link( PoolHashBucket* After ) + { + Link(After, Prev, this); + } + + static void Link( PoolHashBucket* Node, PoolHashBucket* Before, PoolHashBucket* After ) + { + Node->Prev=Before; + Node->Next=After; + Before->Next=Node; + After->Prev=Node; + } + + void Unlink() + { + Next->Prev = Prev; + Prev->Next = Next; + Prev=this; + Next=this; + } +}; + +struct FMallocBinned::Private +{ + /** Default alignment for binned allocator */ + enum { DEFAULT_BINNED_ALLOCATOR_ALIGNMENT = sizeof(FFreeMem) }; + enum { PAGE_SIZE_LIMIT = 65536 }; + // BINNED_ALLOC_POOL_SIZE can be increased beyond 64k to cause binned malloc to allocate + // the small size bins in bigger chunks. If OS Allocation is slow, increasing + // this number *may* help performance but YMMV. + enum { BINNED_ALLOC_POOL_SIZE = 65536 }; + + // Implementation. + static CA_NO_RETURN void OutOfMemory(uint64 Size, uint32 Alignment=0) + { + // this is expected not to return + FPlatformMemory::OnOutOfMemory(Size, Alignment); + } + + static FORCEINLINE void TrackStats(FPoolTable* Table, SIZE_T Size) + { +#if STATS + // keep track of memory lost to padding + Table->TotalWaste += Table->BlockSize - Size; + Table->TotalRequests++; + Table->ActiveRequests++; + Table->MaxActiveRequests = FMath::Max(Table->MaxActiveRequests, Table->ActiveRequests); + Table->MaxRequest = Size > Table->MaxRequest ? Size : Table->MaxRequest; + Table->MinRequest = Size < Table->MinRequest ? Size : Table->MinRequest; +#endif + } + + /** + * Create a 64k page of FPoolInfo structures for tracking allocations + */ + static FPoolInfo* CreateIndirect(FMallocBinned& Allocator) + { + uint64 IndirectPoolBlockSizeBytes = Allocator.IndirectPoolBlockSize * sizeof(FPoolInfo); + + checkSlow(IndirectPoolBlockSizeBytes <= Allocator.PageSize); + FPoolInfo* Indirect = (FPoolInfo*)FPlatformMemory::BinnedAllocFromOS(IndirectPoolBlockSizeBytes); + if( !Indirect ) + { + OutOfMemory(IndirectPoolBlockSizeBytes); + } + FMemory::Memset(Indirect, 0, IndirectPoolBlockSizeBytes); + + BINNED_PEAK_STATCOUNTER(Allocator.OsPeak, BINNED_ADD_STATCOUNTER(Allocator.OsCurrent, (int64)(Align(IndirectPoolBlockSizeBytes, Allocator.PageSize)))); + BINNED_PEAK_STATCOUNTER(Allocator.WastePeak, BINNED_ADD_STATCOUNTER(Allocator.WasteCurrent, (int64)(Align(IndirectPoolBlockSizeBytes, Allocator.PageSize)))); + + return Indirect; + } + + /** + * Gets the FPoolInfo for a memory address. If no valid info exists one is created. + * NOTE: This function requires a mutex across threads, but its is the callers responsibility to + * acquire the mutex before calling + */ + static FORCEINLINE FPoolInfo* GetPoolInfo(FMallocBinned& Allocator, UPTRINT Ptr) + { + if (!Allocator.HashBuckets) + { + // Init tables. + Allocator.HashBuckets = (PoolHashBucket*)FPlatformMemory::BinnedAllocFromOS(Align(Allocator.MaxHashBuckets * sizeof(PoolHashBucket), Allocator.PageSize)); + + for (uint32 i = 0; i < Allocator.MaxHashBuckets; ++i) + { + new (Allocator.HashBuckets + i) PoolHashBucket(); + } + } + + UPTRINT Key = Ptr >> Allocator.HashKeyShift; + UPTRINT Hash = Key & (Allocator.MaxHashBuckets - 1); + UPTRINT PoolIndex = ((UPTRINT)Ptr >> Allocator.PoolBitShift) & Allocator.PoolMask; + + PoolHashBucket* Collision = &Allocator.HashBuckets[Hash]; + do + { + if (Collision->Key == Key || !Collision->FirstPool) + { + if (!Collision->FirstPool) + { + Collision->Key = Key; + InitializeHashBucket(Allocator, Collision); + CA_ASSUME(Collision->FirstPool); + } + return &Collision->FirstPool[PoolIndex]; + } + + Collision = Collision->Next; + } while (Collision != &Allocator.HashBuckets[Hash]); + + //Create a new hash bucket entry + PoolHashBucket* NewBucket = CreateHashBucket(Allocator); + NewBucket->Key = Key; + Allocator.HashBuckets[Hash].Link(NewBucket); + + return &NewBucket->FirstPool[PoolIndex]; + } + + static FORCEINLINE FPoolInfo* FindPoolInfo(FMallocBinned& Allocator, UPTRINT Ptr1, UPTRINT& AllocationBase) + { + uint16 NextStep = 0; + UPTRINT Ptr = Ptr1 &~ ((UPTRINT)Allocator.PageSize - 1); + for (uint32 i = 0, n = (BINNED_ALLOC_POOL_SIZE / Allocator.PageSize) + 1; i < n; ++i) + { + FPoolInfo* Pool = FindPoolInfoInternal(Allocator, Ptr, NextStep); + if (Pool) + { + AllocationBase = Ptr; + //checkSlow(Ptr1 >= AllocationBase && Ptr1 < AllocationBase + Pool->GetBytes()); + return Pool; + } + Ptr = ((Ptr - (Allocator.PageSize * NextStep)) - 1) &~ ((UPTRINT)Allocator.PageSize - 1); + } + AllocationBase = 0; + return nullptr; + } + + static FORCEINLINE FPoolInfo* FindPoolInfoInternal(FMallocBinned& Allocator, UPTRINT Ptr, uint16& JumpOffset) + { + checkSlow(Allocator.HashBuckets); + + uint32 Key = Ptr >> Allocator.HashKeyShift; + uint32 Hash = Key & (Allocator.MaxHashBuckets - 1); + uint32 PoolIndex = ((UPTRINT)Ptr >> Allocator.PoolBitShift) & Allocator.PoolMask; + + JumpOffset = 0; + + PoolHashBucket* Collision = &Allocator.HashBuckets[Hash]; + do + { + if (Collision->Key==Key) + { + if (!Collision->FirstPool[PoolIndex].AllocSize) + { + JumpOffset = Collision->FirstPool[PoolIndex].TableIndex; + return nullptr; + } + return &Collision->FirstPool[PoolIndex]; + } + Collision = Collision->Next; + } while (Collision != &Allocator.HashBuckets[Hash]); + + return nullptr; + } + + /** + * Returns a newly created and initialized PoolHashBucket for use. + */ + static FORCEINLINE PoolHashBucket* CreateHashBucket(FMallocBinned& Allocator) + { + PoolHashBucket* Bucket = AllocateHashBucket(Allocator); + InitializeHashBucket(Allocator, Bucket); + return Bucket; + } + + /** + * Initializes bucket with valid parameters + * @param bucket pointer to be initialized + */ + static FORCEINLINE void InitializeHashBucket(FMallocBinned& Allocator, PoolHashBucket* Bucket) + { + if (!Bucket->FirstPool) + { + Bucket->FirstPool = CreateIndirect(Allocator); + } + } + + /** + * Allocates a hash bucket from the free list of hash buckets + */ + static PoolHashBucket* AllocateHashBucket(FMallocBinned& Allocator) + { + if (!Allocator.HashBucketFreeList) + { + const uint32 PageSize = Allocator.PageSize; + + Allocator.HashBucketFreeList = (PoolHashBucket*)FPlatformMemory::BinnedAllocFromOS(PageSize); + BINNED_PEAK_STATCOUNTER(Allocator.OsPeak, BINNED_ADD_STATCOUNTER(Allocator.OsCurrent, PageSize)); + BINNED_PEAK_STATCOUNTER(Allocator.WastePeak, BINNED_ADD_STATCOUNTER(Allocator.WasteCurrent, PageSize)); + + for (UPTRINT i = 0, n = PageSize / sizeof(PoolHashBucket); i < n; ++i) + { + Allocator.HashBucketFreeList->Link(new (Allocator.HashBucketFreeList + i) PoolHashBucket()); + } + } + + PoolHashBucket* NextFree = Allocator.HashBucketFreeList->Next; + PoolHashBucket* Free = Allocator.HashBucketFreeList; + + Free->Unlink(); + if (NextFree == Free) + { + NextFree = nullptr; + } + Allocator.HashBucketFreeList = NextFree; + + return Free; + } + + static FPoolInfo* AllocatePoolMemory(FMallocBinned& Allocator, FPoolTable* Table, uint32 PoolSize, uint16 TableIndex) + { + const uint32 PageSize = Allocator.PageSize; + + // Must create a new pool. + uint32 Blocks = PoolSize / Table->BlockSize; + uint32 Bytes = Blocks * Table->BlockSize; + UPTRINT OsBytes = Align(Bytes, PageSize); + + checkSlow(Blocks >= 1); + checkSlow(Blocks * Table->BlockSize <= Bytes && PoolSize >= Bytes); + + // Allocate memory. + FFreeMem* Free = nullptr; + SIZE_T ActualPoolSize; //TODO: use this to reduce waste? + Free = (FFreeMem*)OSAlloc(Allocator, OsBytes, ActualPoolSize); + + checkSlow(!((UPTRINT)Free & (PageSize - 1))); + if( !Free ) + { + OutOfMemory(OsBytes); + } + + // Create pool in the indirect table. + FPoolInfo* Pool; + { +#ifdef USE_FINE_GRAIN_LOCKS + FScopeLock PoolInfoLock(&Allocator.AccessGuard); +#endif + Pool = GetPoolInfo(Allocator, (UPTRINT)Free); + for (UPTRINT i = (UPTRINT)PageSize, Offset = 0; i < OsBytes; i += PageSize, ++Offset) + { + FPoolInfo* TrailingPool = GetPoolInfo(Allocator, ((UPTRINT)Free) + i); + check(TrailingPool); + + //Set trailing pools to point back to first pool + TrailingPool->SetAllocationSizes(0, 0, Offset, Allocator.BinnedOSTableIndex); + } + + + BINNED_PEAK_STATCOUNTER(Allocator.OsPeak, BINNED_ADD_STATCOUNTER(Allocator.OsCurrent, OsBytes)); + BINNED_PEAK_STATCOUNTER(Allocator.WastePeak, BINNED_ADD_STATCOUNTER(Allocator.WasteCurrent, (OsBytes - Bytes))); + } + + // Init pool. + Pool->Link( Table->FirstPool ); + Pool->SetAllocationSizes(Bytes, OsBytes, TableIndex, Allocator.BinnedOSTableIndex); + Pool->Taken = 0; + Pool->FirstMem = Free; + +#if STATS + Table->NumActivePools++; + Table->MaxActivePools = FMath::Max(Table->MaxActivePools, Table->NumActivePools); +#endif + // Create first free item. + Free->NumFreeBlocks = Blocks; + Free->Next = nullptr; + + return Pool; + } + + static FORCEINLINE FFreeMem* AllocateBlockFromPool(FMallocBinned& Allocator, FPoolTable* Table, FPoolInfo* Pool, uint32 Alignment) + { + // Pick first available block and unlink it. + Pool->Taken++; + checkSlow(Pool->TableIndex < Allocator.BinnedOSTableIndex); // if this is false, FirstMem is actually a size not a pointer + checkSlow(Pool->FirstMem); + checkSlow(Pool->FirstMem->NumFreeBlocks > 0); + checkSlow(Pool->FirstMem->NumFreeBlocks < PAGE_SIZE_LIMIT); + FFreeMem* Free = (FFreeMem*)((uint8*)Pool->FirstMem + --Pool->FirstMem->NumFreeBlocks * Table->BlockSize); + if( !Pool->FirstMem->NumFreeBlocks ) + { + Pool->FirstMem = Pool->FirstMem->Next; + if( !Pool->FirstMem ) + { + // Move to exhausted list. + Pool->Unlink(); + Pool->Link( Table->ExhaustedPool ); + } + } + BINNED_PEAK_STATCOUNTER(Allocator.UsedPeak, BINNED_ADD_STATCOUNTER(Allocator.UsedCurrent, Table->BlockSize)); + return Align(Free, Alignment); + } + + /** + * Releases memory back to the system. This is not protected from multi-threaded access and it's + * the callers responsibility to Lock AccessGuard before calling this. + */ + static void FreeInternal(FMallocBinned& Allocator, void* Ptr) + { + MEM_TIME(MemTime -= FPlatformTime::Seconds()); + BINNED_DECREMENT_STATCOUNTER(Allocator.CurrentAllocs); + + UPTRINT BasePtr; + FPoolInfo* Pool = FindPoolInfo(Allocator, (UPTRINT)Ptr, BasePtr); +#if PLATFORM_IOS || PLATFORM_MAC + if (Pool == NULL) + { + UE_LOG(LogMemory, Warning, TEXT("Attempting to free a pointer we didn't allocate!")); + return; + } +#endif + checkSlow(Pool); + checkSlow(Pool->GetBytes() != 0); + if (Pool->TableIndex < Allocator.BinnedOSTableIndex) + { + FPoolTable* Table = Allocator.MemSizeToPoolTable[Pool->TableIndex]; +#ifdef USE_FINE_GRAIN_LOCKS + FScopeLock TableLock(&Table->CriticalSection); +#endif +#if STATS + Table->ActiveRequests--; +#endif + // If this pool was exhausted, move to available list. + if( !Pool->FirstMem ) + { + Pool->Unlink(); + Pool->Link( Table->FirstPool ); + } + + void* BaseAddress = (void*)BasePtr; + uint32 BlockSize = Table->BlockSize; + PTRINT OffsetFromBase = (PTRINT)Ptr - (PTRINT)BaseAddress; + check(OffsetFromBase >= 0); + uint32 AlignOffset = OffsetFromBase % BlockSize; + + // Patch pointer to include previously applied alignment. + Ptr = (void*)((PTRINT)Ptr - (PTRINT)AlignOffset); + + // Free a pooled allocation. + FFreeMem* Free = (FFreeMem*)Ptr; + Free->NumFreeBlocks = 1; + Free->Next = Pool->FirstMem; + Pool->FirstMem = Free; + BINNED_ADD_STATCOUNTER(Allocator.UsedCurrent, -(int64)(Table->BlockSize)); + + // Free this pool. + checkSlow(Pool->Taken >= 1); + if( --Pool->Taken == 0 ) + { +#if STATS + Table->NumActivePools--; +#endif + // Free the OS memory. + SIZE_T OsBytes = Pool->GetOsBytes(Allocator.PageSize, Allocator.BinnedOSTableIndex); + BINNED_ADD_STATCOUNTER(Allocator.OsCurrent, -(int64)OsBytes); + BINNED_ADD_STATCOUNTER(Allocator.WasteCurrent, -(int64)(OsBytes - Pool->GetBytes())); + Pool->Unlink(); + Pool->SetAllocationSizes(0, 0, 0, Allocator.BinnedOSTableIndex); + OSFree(Allocator, (void*)BasePtr, OsBytes); + } + } + else + { + // Free an OS allocation. + checkSlow(!((UPTRINT)Ptr & (Allocator.PageSize - 1))); + SIZE_T OsBytes = Pool->GetOsBytes(Allocator.PageSize, Allocator.BinnedOSTableIndex); + + BINNED_ADD_STATCOUNTER(Allocator.UsedCurrent, -(int64)Pool->GetBytes()); + BINNED_ADD_STATCOUNTER(Allocator.OsCurrent, -(int64)OsBytes); + BINNED_ADD_STATCOUNTER(Allocator.WasteCurrent, -(int64)(OsBytes - Pool->GetBytes())); + OSFree(Allocator, (void*)BasePtr, OsBytes); + } + + MEM_TIME(MemTime += FPlatformTime::Seconds()); + } + + static void PushFreeLockless(FMallocBinned& Allocator, void* Ptr) + { +#ifdef USE_LOCKFREE_DELETE + Allocator.PendingFreeList->Push(Ptr); +#else +#ifdef USE_COARSE_GRAIN_LOCKS + FScopeLock ScopedLock(&AccessGuard); +#endif + FreeInternal(Allocator, Ptr); +#endif + } + + /** + * Clear and Process the list of frees to be deallocated. It's the callers + * responsibility to Lock AccessGuard before calling this + */ + static void FlushPendingFrees(FMallocBinned& Allocator) + { +#ifdef USE_LOCKFREE_DELETE + if (!Allocator.PendingFreeList && !Allocator.bDoneFreeListInit) + { + Allocator.bDoneFreeListInit = true; + Allocator.PendingFreeList = new ((void*)Allocator.PendingFreeListMemory) TLockFreePointerList(); + } + + // Because a lockless list and TArray calls new/malloc internally, need to guard against re-entry + if (Allocator.bFlushingFrees || !Allocator.PendingFreeList) + { + return; + } + Allocator.bFlushingFrees = true; + Allocator.PendingFreeList->PopAll(Allocator.FlushedFrees); + for (uint32 i = 0, n = Allocator.FlushedFrees.Num(); i < n; ++i) + { + FreeInternal(Allocator, Allocator.FlushedFrees[i]); + } + Allocator.FlushedFrees.Reset(); + Allocator.bFlushingFrees = false; +#endif + } + + static FORCEINLINE void OSFree(FMallocBinned& Allocator, void* Ptr, SIZE_T Size) + { +#ifdef CACHE_FREED_OS_ALLOCS +#ifdef USE_FINE_GRAIN_LOCKS + FScopeLock MainLock(&Allocator.AccessGuard); +#endif + if (Size > MAX_CACHED_OS_FREES_BYTE_LIMIT / 4) + { + FPlatformMemory::BinnedFreeToOS(Ptr); + return; + } + + while (Allocator.FreedPageBlocksNum && (Allocator.FreedPageBlocksNum >= MAX_CACHED_OS_FREES || Allocator.CachedTotal + Size > MAX_CACHED_OS_FREES_BYTE_LIMIT)) + { + //Remove the oldest one + void* FreePtr = Allocator.FreedPageBlocks[0].Ptr; + Allocator.CachedTotal -= Allocator.FreedPageBlocks[0].ByteSize; + Allocator.FreedPageBlocksNum--; + if (Allocator.FreedPageBlocksNum) + { + FMemory::Memmove(&Allocator.FreedPageBlocks[0], &Allocator.FreedPageBlocks[1], sizeof(FFreePageBlock) * Allocator.FreedPageBlocksNum); + } + FPlatformMemory::BinnedFreeToOS(FreePtr); + } + Allocator.FreedPageBlocks[Allocator.FreedPageBlocksNum].Ptr = Ptr; + Allocator.FreedPageBlocks[Allocator.FreedPageBlocksNum].ByteSize = Size; + Allocator.CachedTotal += Size; + ++Allocator.FreedPageBlocksNum; +#else + (void)Size; + FPlatformMemory::BinnedFreeToOS(Ptr); +#endif + } + + static FORCEINLINE void* OSAlloc(FMallocBinned& Allocator, SIZE_T NewSize, SIZE_T& OutActualSize) + { +#ifdef CACHE_FREED_OS_ALLOCS + { +#ifdef USE_FINE_GRAIN_LOCKS + // We want to hold the lock a little as possible so release it + // before the big call to the OS + FScopeLock MainLock(&Allocator.AccessGuard); +#endif + for (uint32 i=0; i < Allocator.FreedPageBlocksNum; ++i) + { + // look for exact matches first, these are aligned to the page size, so it should be quite common to hit these on small pages sizes + if (Allocator.FreedPageBlocks[i].ByteSize == NewSize) + { + void* Ret = Allocator.FreedPageBlocks[i].Ptr; + OutActualSize = Allocator.FreedPageBlocks[i].ByteSize; + Allocator.CachedTotal -= Allocator.FreedPageBlocks[i].ByteSize; + if (i < Allocator.FreedPageBlocksNum - 1) + { + FMemory::Memmove(&Allocator.FreedPageBlocks[i], &Allocator.FreedPageBlocks[i + 1], sizeof(FFreePageBlock) * (Allocator.FreedPageBlocksNum - i - 1)); + } + Allocator.FreedPageBlocksNum--; + return Ret; + } + }; + for (uint32 i=0; i < Allocator.FreedPageBlocksNum; ++i) + { + // is it possible (and worth i.e. <25% overhead) to use this block + if (Allocator.FreedPageBlocks[i].ByteSize >= NewSize && Allocator.FreedPageBlocks[i].ByteSize * 3 <= NewSize * 4) + { + void* Ret = Allocator.FreedPageBlocks[i].Ptr; + OutActualSize = Allocator.FreedPageBlocks[i].ByteSize; + Allocator.CachedTotal -= Allocator.FreedPageBlocks[i].ByteSize; + if (i < Allocator.FreedPageBlocksNum - 1) + { + FMemory::Memmove(&Allocator.FreedPageBlocks[i], &Allocator.FreedPageBlocks[i + 1], sizeof(FFreePageBlock) * (Allocator.FreedPageBlocksNum - i - 1)); + } + Allocator.FreedPageBlocksNum--; + return Ret; + } + }; + } + OutActualSize = NewSize; + void* Ptr = FPlatformMemory::BinnedAllocFromOS(NewSize); + if (!Ptr) + { + //Are we holding on to much mem? Release it all. + FlushAllocCache(Allocator); + Ptr = FPlatformMemory::BinnedAllocFromOS(NewSize); + } + return Ptr; +#else + (void)OutActualSize; + return FPlatformMemory::BinnedAllocFromOS(NewSize); +#endif + } + +#ifdef CACHE_FREED_OS_ALLOCS + static void FlushAllocCache(FMallocBinned& Allocator) + { +#ifdef USE_FINE_GRAIN_LOCKS + FScopeLock MainLock(&Allocator.AccessGuard); +#endif + for (int i = 0, n = Allocator.FreedPageBlocksNum; i PageSize); // Check to catch 32 bit overflow in AddressLimit + + /** Shift to get the reference from the indirect tables */ + PoolBitShift = FPlatformMath::CeilLogTwo(PageSize); + IndirectPoolBitShift = FPlatformMath::CeilLogTwo(PageSize/sizeof(FPoolInfo)); + IndirectPoolBlockSize = PageSize/sizeof(FPoolInfo); + + MaxHashBuckets = AddressLimit >> (IndirectPoolBitShift+PoolBitShift); + MaxHashBucketBits = FPlatformMath::CeilLogTwo(MaxHashBuckets); + MaxHashBucketWaste = (MaxHashBuckets*sizeof(PoolHashBucket))/1024; + MaxBookKeepingOverhead = ((AddressLimit/PageSize)*sizeof(PoolHashBucket))/(1024*1024); + /** + * Shift required to get required hash table key. + */ + HashKeyShift = PoolBitShift+IndirectPoolBitShift; + /** Used to mask off the bits that have been used to lookup the indirect table */ + PoolMask = ( ( 1ull << ( HashKeyShift - PoolBitShift ) ) - 1 ); + BinnedSizeLimit = Private::PAGE_SIZE_LIMIT/2; + BinnedOSTableIndex = BinnedSizeLimit+EXTENDED_PAGE_POOL_ALLOCATION_COUNT; + + check((BinnedSizeLimit & (BinnedSizeLimit-1)) == 0); + + + // Init tables. + OsTable.FirstPool = nullptr; + OsTable.ExhaustedPool = nullptr; + OsTable.BlockSize = 0; + + /** The following options are not valid for page sizes less than 64k. They are here to reduce waste*/ + PagePoolTable[0].FirstPool = nullptr; + PagePoolTable[0].ExhaustedPool = nullptr; + PagePoolTable[0].BlockSize = PageSize == Private::PAGE_SIZE_LIMIT ? BinnedSizeLimit+(BinnedSizeLimit/2) : 0; + + PagePoolTable[1].FirstPool = nullptr; + PagePoolTable[1].ExhaustedPool = nullptr; + PagePoolTable[1].BlockSize = PageSize == Private::PAGE_SIZE_LIMIT ? PageSize+BinnedSizeLimit : 0; + + // Block sizes are based around getting the maximum amount of allocations per pool, with as little alignment waste as possible. + // Block sizes should be close to even divisors of the POOL_SIZE, and well distributed. They must be 16-byte aligned as well. + static const uint32 BlockSizes[POOL_COUNT] = + { + 8, 16, 32, 48, 64, 80, 96, 112, + 128, 160, 192, 224, 256, 288, 320, 384, + 448, 512, 576, 640, 704, 768, 896, 1024, + 1168, 1360, 1632, 2048, 2336, 2720, 3264, 4096, + 4672, 5456, 6544, 8192, 9360, 10912, 13104, 16384, + 21840, 32768 + }; + + for( uint32 i = 0; i < POOL_COUNT; i++ ) + { + PoolTable[i].FirstPool = nullptr; + PoolTable[i].ExhaustedPool = nullptr; + PoolTable[i].BlockSize = BlockSizes[i]; +#if STATS + PoolTable[i].MinRequest = PoolTable[i].BlockSize; +#endif + } + + for( uint32 i=0; i(Alignment, Private::DEFAULT_BINNED_ALLOCATOR_ALIGNMENT); + SIZE_T SpareBytesCount = FMath::Min(Private::DEFAULT_BINNED_ALLOCATOR_ALIGNMENT, Size); + Size = FMath::Max(PoolTable[0].BlockSize, Size + (Alignment - SpareBytesCount)); + MEM_TIME(MemTime -= FPlatformTime::Seconds()); + + BINNED_INCREMENT_STATCOUNTER(CurrentAllocs); + BINNED_INCREMENT_STATCOUNTER(TotalAllocs); + + FFreeMem* Free; + if( Size < BinnedSizeLimit ) + { + // Allocate from pool. + FPoolTable* Table = MemSizeToPoolTable[Size]; +#ifdef USE_FINE_GRAIN_LOCKS + FScopeLock TableLock(&Table->CriticalSection); +#endif + checkSlow(Size <= Table->BlockSize); + + Private::TrackStats(Table, Size); + + FPoolInfo* Pool = Table->FirstPool; + if( !Pool ) + { + Pool = Private::AllocatePoolMemory(*this, Table, Private::BINNED_ALLOC_POOL_SIZE/*PageSize*/, Size); + } + + Free = Private::AllocateBlockFromPool(*this, Table, Pool, Alignment); + } + else if ( ((Size >= BinnedSizeLimit && Size <= PagePoolTable[0].BlockSize) || + (Size > PageSize && Size <= PagePoolTable[1].BlockSize))) + { + // Bucket in a pool of 3*PageSize or 6*PageSize + uint32 BinType = Size < PageSize ? 0 : 1; + uint32 PageCount = 3*BinType + 3; + FPoolTable* Table = &PagePoolTable[BinType]; +#ifdef USE_FINE_GRAIN_LOCKS + FScopeLock TableLock(&Table->CriticalSection); +#endif + checkSlow(Size <= Table->BlockSize); + + Private::TrackStats(Table, Size); + + FPoolInfo* Pool = Table->FirstPool; + if( !Pool ) + { + Pool = Private::AllocatePoolMemory(*this, Table, PageCount*PageSize, BinnedSizeLimit+BinType); + } + + Free = Private::AllocateBlockFromPool(*this, Table, Pool, Alignment); + } + else + { + // Use OS for large allocations. + UPTRINT AlignedSize = Align(Size,PageSize); + SIZE_T ActualPoolSize; //TODO: use this to reduce waste? + Free = (FFreeMem*)Private::OSAlloc(*this, AlignedSize, ActualPoolSize); + if( !Free ) + { + Private::OutOfMemory(AlignedSize); + } + + void* AlignedFree = Align(Free, Alignment); + + // Create indirect. + FPoolInfo* Pool; + { +#ifdef USE_FINE_GRAIN_LOCKS + FScopeLock PoolInfoLock(&AccessGuard); +#endif + Pool = Private::GetPoolInfo(*this, (UPTRINT)Free); + + if ((UPTRINT)Free != ((UPTRINT)AlignedFree & ~((UPTRINT)PageSize - 1))) + { + // Mark the FPoolInfo for AlignedFree to jump back to the FPoolInfo for ptr. + for (UPTRINT i = (UPTRINT)PageSize, Offset = 0; i < AlignedSize; i += PageSize, ++Offset) + { + FPoolInfo* TrailingPool = Private::GetPoolInfo(*this, ((UPTRINT)Free) + i); + check(TrailingPool); + //Set trailing pools to point back to first pool + TrailingPool->SetAllocationSizes(0, 0, Offset, BinnedOSTableIndex); + } + } + } + Free = (FFreeMem*)AlignedFree; + Pool->SetAllocationSizes(Size, AlignedSize, BinnedOSTableIndex, BinnedOSTableIndex); + BINNED_PEAK_STATCOUNTER(OsPeak, BINNED_ADD_STATCOUNTER(OsCurrent, AlignedSize)); + BINNED_PEAK_STATCOUNTER(UsedPeak, BINNED_ADD_STATCOUNTER(UsedCurrent, Size)); + BINNED_PEAK_STATCOUNTER(WastePeak, BINNED_ADD_STATCOUNTER(WasteCurrent, (int64)(AlignedSize - Size))); + } + + MEM_TIME(MemTime += FPlatformTime::Seconds()); + return Free; +} + +void* FMallocBinned::Realloc( void* Ptr, SIZE_T NewSize, uint32 Alignment ) +{ + // Handle DEFAULT_ALIGNMENT for binned allocator. + if (Alignment == DEFAULT_ALIGNMENT) + { + Alignment = Private::DEFAULT_BINNED_ALLOCATOR_ALIGNMENT; + } + + Alignment = FMath::Max(Alignment, Private::DEFAULT_BINNED_ALLOCATOR_ALIGNMENT); + const uint32 NewSizeUnmodified = NewSize; + SIZE_T SpareBytesCount = FMath::Min(Private::DEFAULT_BINNED_ALLOCATOR_ALIGNMENT, NewSize); + if (NewSize) + { + NewSize = FMath::Max(PoolTable[0].BlockSize, NewSize + (Alignment - SpareBytesCount)); + } + MEM_TIME(MemTime -= FPlatformTime::Seconds()); + UPTRINT BasePtr; + void* NewPtr = Ptr; + if( Ptr && NewSize ) + { + FPoolInfo* Pool = Private::FindPoolInfo(*this, (UPTRINT)Ptr, BasePtr); + + if( Pool->TableIndex < BinnedOSTableIndex ) + { + // Allocated from pool, so grow or shrink if necessary. + check(Pool->TableIndex > 0); // it isn't possible to allocate a size of 0, Malloc will increase the size to DEFAULT_BINNED_ALLOCATOR_ALIGNMENT + if (NewSizeUnmodified > MemSizeToPoolTable[Pool->TableIndex]->BlockSize || NewSizeUnmodified <= MemSizeToPoolTable[Pool->TableIndex - 1]->BlockSize) + { + NewPtr = Malloc(NewSizeUnmodified, Alignment); + FMemory::Memcpy(NewPtr, Ptr, FMath::Min(NewSizeUnmodified, MemSizeToPoolTable[Pool->TableIndex]->BlockSize - (Alignment - SpareBytesCount))); + Free( Ptr ); + } + else if (((UPTRINT)Ptr & (UPTRINT)(Alignment - 1)) != 0) + { + NewPtr = Align(Ptr, Alignment); + FMemory::Memmove(NewPtr, Ptr, NewSize); + } + } + else + { + // Allocated from OS. + if( NewSize > Pool->GetOsBytes(PageSize, BinnedOSTableIndex) || NewSize * 3 < Pool->GetOsBytes(PageSize, BinnedOSTableIndex) * 2 ) + { + // Grow or shrink. + NewPtr = Malloc(NewSizeUnmodified, Alignment); + FMemory::Memcpy(NewPtr, Ptr, FMath::Min(NewSizeUnmodified, Pool->GetBytes())); + Free( Ptr ); + } + else + { +//need a lock to cover the SetAllocationSizes() +#ifdef USE_FINE_GRAIN_LOCKS + FScopeLock PoolInfoLock(&AccessGuard); +#endif + int32 UsedChange = (NewSize - Pool->GetBytes()); + + // Keep as-is, reallocation isn't worth the overhead. + BINNED_ADD_STATCOUNTER(UsedCurrent, UsedChange); + BINNED_PEAK_STATCOUNTER(UsedPeak, UsedCurrent); + BINNED_ADD_STATCOUNTER(WasteCurrent, (Pool->GetBytes() - NewSize)); + Pool->SetAllocationSizes(NewSizeUnmodified, Pool->GetOsBytes(PageSize, BinnedOSTableIndex), BinnedOSTableIndex, BinnedOSTableIndex); + } + } + } + else if( Ptr == nullptr ) + { + NewPtr = Malloc(NewSizeUnmodified, Alignment); + } + else + { + Free( Ptr ); + NewPtr = nullptr; + } + + MEM_TIME(MemTime += FPlatformTime::Seconds()); + return NewPtr; +} + +void FMallocBinned::Free( void* Ptr ) +{ + if( !Ptr ) + { + return; + } + + Private::PushFreeLockless(*this, Ptr); +} + +bool FMallocBinned::GetAllocationSize(void *Original, SIZE_T &SizeOut) +{ + if (!Original) + { + return false; + } + UPTRINT BasePtr; + FPoolInfo* Pool = Private::FindPoolInfo(*this, (UPTRINT)Original, BasePtr); + SizeOut = Pool->TableIndex < BinnedOSTableIndex ? MemSizeToPoolTable[Pool->TableIndex]->BlockSize : Pool->GetBytes(); + return true; +} + +bool FMallocBinned::ValidateHeap() +{ +#ifdef USE_COARSE_GRAIN_LOCKS + FScopeLock ScopedLock(&AccessGuard); +#endif + for( int32 i = 0; i < POOL_COUNT; i++ ) + { + FPoolTable* Table = &PoolTable[i]; +#ifdef USE_FINE_GRAIN_LOCKS + FScopeLock TableLock(&Table->CriticalSection); +#endif + for( FPoolInfo** PoolPtr = &Table->FirstPool; *PoolPtr; PoolPtr = &(*PoolPtr)->Next ) + { + FPoolInfo* Pool = *PoolPtr; + check(Pool->PrevLink == PoolPtr); + check(Pool->FirstMem); + for( FFreeMem* Free = Pool->FirstMem; Free; Free = Free->Next ) + { + check(Free->NumFreeBlocks > 0); + } + } + for( FPoolInfo** PoolPtr = &Table->ExhaustedPool; *PoolPtr; PoolPtr = &(*PoolPtr)->Next ) + { + FPoolInfo* Pool = *PoolPtr; + check(Pool->PrevLink == PoolPtr); + check(!Pool->FirstMem); + } + } + + return( true ); +} + +void FMallocBinned::UpdateStats() +{ + FMalloc::UpdateStats(); +#if STATS + SIZE_T LocalOsCurrent = 0; + SIZE_T LocalOsPeak = 0; + SIZE_T LocalWasteCurrent = 0; + SIZE_T LocalWastePeak = 0; + SIZE_T LocalUsedCurrent = 0; + SIZE_T LocalUsedPeak = 0; + SIZE_T LocalCurrentAllocs = 0; + SIZE_T LocalTotalAllocs = 0; + SIZE_T LocalSlackCurrent = 0; + + { +#ifdef USE_INTERNAL_LOCKS + FScopeLock ScopedLock( &AccessGuard ); +#endif + Private::UpdateSlackStat(*this); + + // Copy memory stats. + LocalOsCurrent = OsCurrent; + LocalOsPeak = OsPeak; + LocalWasteCurrent = WasteCurrent; + LocalWastePeak = WastePeak; + LocalUsedCurrent = UsedCurrent; + LocalUsedPeak = UsedPeak; + LocalCurrentAllocs = CurrentAllocs; + LocalTotalAllocs = TotalAllocs; + LocalSlackCurrent = SlackCurrent; + } + + SET_MEMORY_STAT( STAT_Binned_OsCurrent, LocalOsCurrent ); + SET_MEMORY_STAT( STAT_Binned_OsPeak, LocalOsPeak ); + SET_MEMORY_STAT( STAT_Binned_WasteCurrent, LocalWasteCurrent ); + SET_MEMORY_STAT( STAT_Binned_WastePeak, LocalWastePeak ); + SET_MEMORY_STAT( STAT_Binned_UsedCurrent, LocalUsedCurrent ); + SET_MEMORY_STAT( STAT_Binned_UsedPeak, LocalUsedPeak ); + SET_DWORD_STAT( STAT_Binned_CurrentAllocs, LocalCurrentAllocs ); + SET_DWORD_STAT( STAT_Binned_TotalAllocs, LocalTotalAllocs ); + SET_MEMORY_STAT( STAT_Binned_SlackCurrent, LocalSlackCurrent ); +#endif +} + +void FMallocBinned::DumpAllocatorStats( class FOutputDevice& Ar ) +{ + FBufferedOutputDevice BufferedOutput; + { +#ifdef USE_COARSE_GRAIN_LOCKS + FScopeLock ScopedLock( &AccessGuard ); +#endif + ValidateHeap(); +#if STATS + Private::UpdateSlackStat(*this); +#if !NO_LOGGING + // This is all of the memory including stuff too big for the pools + BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "Allocator Stats for %s:" ), GetDescriptiveName() ); + // Waste is the total overhead of the memory system + BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "Current Memory %.2f MB used, plus %.2f MB waste" ), UsedCurrent / (1024.0f * 1024.0f), (OsCurrent - UsedCurrent) / (1024.0f * 1024.0f) ); + BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "Peak Memory %.2f MB used, plus %.2f MB waste" ), UsedPeak / (1024.0f * 1024.0f), (OsPeak - UsedPeak) / (1024.0f * 1024.0f) ); + + BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "Current OS Memory %.2f MB, peak %.2f MB" ), OsCurrent / (1024.0f * 1024.0f), OsPeak / (1024.0f * 1024.0f) ); + BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "Current Waste %.2f MB, peak %.2f MB" ), WasteCurrent / (1024.0f * 1024.0f), WastePeak / (1024.0f * 1024.0f) ); + BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "Current Used %.2f MB, peak %.2f MB" ), UsedCurrent / (1024.0f * 1024.0f), UsedPeak / (1024.0f * 1024.0f) ); + BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "Current Slack %.2f MB" ), SlackCurrent / (1024.0f * 1024.0f) ); + + BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "Allocs % 6i Current / % 6i Total" ), CurrentAllocs, TotalAllocs ); + MEM_TIME( BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "Seconds % 5.3f" ), MemTime ) ); + MEM_TIME( BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "MSec/Allc % 5.5f" ), 1000.0 * MemTime / MemAllocs ) ); + + // This is the memory tracked inside individual allocation pools + BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "" ) ); + BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "Block Size Num Pools Max Pools Cur Allocs Total Allocs Min Req Max Req Mem Used Mem Slack Mem Waste Efficiency" ) ); + BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "---------- --------- --------- ---------- ------------ ------- ------- -------- --------- --------- ----------" ) ); + + uint32 TotalMemory = 0; + uint32 TotalWaste = 0; + uint32 TotalActiveRequests = 0; + uint32 TotalTotalRequests = 0; + uint32 TotalPools = 0; + uint32 TotalSlack = 0; + + FPoolTable* Table = nullptr; + for( int32 i = 0; i < BinnedSizeLimit + EXTENDED_PAGE_POOL_ALLOCATION_COUNT; i++ ) + { + if( Table == MemSizeToPoolTable[i] || MemSizeToPoolTable[i]->BlockSize == 0 ) + continue; + + Table = MemSizeToPoolTable[i]; + +#ifdef USE_FINE_GRAIN_LOCKS + FScopeLock TableLock(&Table->CriticalSection); +#endif + + uint32 TableAllocSize = (Table->BlockSize > BinnedSizeLimit ? (((3 * (i - BinnedSizeLimit)) + 3)*Private::BINNED_ALLOC_POOL_SIZE) : Private::BINNED_ALLOC_POOL_SIZE); + // The amount of memory allocated from the OS + uint32 MemAllocated = (Table->NumActivePools * TableAllocSize) / 1024; + // Amount of memory actually in use by allocations + uint32 MemUsed = (Table->BlockSize * Table->ActiveRequests) / 1024; + // Wasted memory due to pool size alignment + uint32 PoolMemWaste = Table->NumActivePools * (TableAllocSize - ((TableAllocSize / Table->BlockSize) * Table->BlockSize)) / 1024; + // Wasted memory due to individual allocation alignment. This is an estimate. + uint32 MemWaste = (uint32)(((double)Table->TotalWaste / (double)Table->TotalRequests) * (double)Table->ActiveRequests) / 1024 + PoolMemWaste; + // Memory that is reserved in active pools and ready for future use + uint32 MemSlack = MemAllocated - MemUsed - PoolMemWaste; + + BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "% 10i % 9i % 9i % 10i % 12i % 7i % 7i % 7iK % 8iK % 8iK % 9.2f%%" ), + Table->BlockSize, + Table->NumActivePools, + Table->MaxActivePools, + Table->ActiveRequests, + (uint32)Table->TotalRequests, + Table->MinRequest, + Table->MaxRequest, + MemUsed, + MemSlack, + MemWaste, + MemAllocated ? 100.0f * (MemAllocated - MemWaste) / MemAllocated : 100.0f ); + + TotalMemory += MemAllocated; + TotalWaste += MemWaste; + TotalSlack += MemSlack; + TotalActiveRequests += Table->ActiveRequests; + TotalTotalRequests += Table->TotalRequests; + TotalPools += Table->NumActivePools; + } + + BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "" ) ); + BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "%iK allocated in pools (with %iK slack and %iK waste). Efficiency %.2f%%" ), TotalMemory, TotalSlack, TotalWaste, TotalMemory ? 100.0f * (TotalMemory - TotalWaste) / TotalMemory : 100.0f ); + BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "Allocations %i Current / %i Total (in %i pools)" ), TotalActiveRequests, TotalTotalRequests, TotalPools ); + BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "" ) ); +#endif +#endif + } + + BufferedOutput.RedirectTo( Ar ); +} + +const TCHAR* FMallocBinned::GetDescriptiveName() +{ + return TEXT("binned"); +} diff --git a/Engine/Source/Runtime/Core/Private/HAL/MallocBinned2.cpp b/Engine/Source/Runtime/Core/Private/HAL/MallocBinned2.cpp new file mode 100644 index 000000000000..73dfef40c543 --- /dev/null +++ b/Engine/Source/Runtime/Core/Private/HAL/MallocBinned2.cpp @@ -0,0 +1,1175 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +/*============================================================================= + MallocBinned.cpp: Binned memory allocator +=============================================================================*/ + +#include "CorePrivatePCH.h" + +#include "MallocBinned2.h" +#include "MemoryMisc.h" +#include "HAL/PlatformAtomics.h" + +#define BINNED2_MEM_TIME(st) + +//when modifying the global allocator stats, if we are using COARSE locks, then all callsites for stat modification are covered by the allocator-wide access guard. Thus the stats can be modified directly. +//If we are using FINE locks, then we must modify the stats through atomics as the locks are either not actually covering the stat callsites, or are locking specific table locks which is not sufficient for stats. +#if STATS +# define BINNED2_INCREMENT_STATCOUNTER(counter) (FPlatformAtomics::InterlockedIncrement(&(counter))) +# define BINNED2_DECREMENT_STATCOUNTER(counter) (FPlatformAtomics::InterlockedDecrement(&(counter))) +# define BINNED2_ADD_STATCOUNTER(counter, value) (FPlatformAtomics::InterlockedAdd(&counter, (value))) +# define BINNED2_PEAK_STATCOUNTER(PeakCounter, CompareVal) { \ + volatile TDecay::Type NewCompare; \ + volatile TDecay::Type NewPeak; \ + do \ + { \ + NewCompare = (PeakCounter); \ + NewPeak = FMath::Max((PeakCounter), (CompareVal)); \ + } \ + while (FPlatformAtomics::InterlockedCompareExchange(&(PeakCounter), NewPeak, NewCompare) != NewCompare); \ + } +#else +# define BINNED2_INCREMENT_STATCOUNTER(counter) +# define BINNED2_DECREMENT_STATCOUNTER(counter) +# define BINNED2_ADD_STATCOUNTER(counter, value) +# define BINNED2_PEAK_STATCOUNTER(PeakCounter, CompareVal) +#endif + +/** Malloc binned allocator specific stats. */ +DECLARE_MEMORY_STAT_EXTERN(TEXT("Binned Os Current"), STAT_Binned2_OsCurrent,STATGROUP_MemoryAllocator, CORE_API); +DECLARE_MEMORY_STAT_EXTERN(TEXT("Binned Os Peak"), STAT_Binned2_OsPeak,STATGROUP_MemoryAllocator, CORE_API); +DECLARE_MEMORY_STAT_EXTERN(TEXT("Binned Waste Current"), STAT_Binned2_WasteCurrent,STATGROUP_MemoryAllocator, CORE_API); +DECLARE_MEMORY_STAT_EXTERN(TEXT("Binned Waste Peak"), STAT_Binned2_WastePeak,STATGROUP_MemoryAllocator, CORE_API); +DECLARE_MEMORY_STAT_EXTERN(TEXT("Binned Used Current"), STAT_Binned2_UsedCurrent,STATGROUP_MemoryAllocator, CORE_API); +DECLARE_MEMORY_STAT_EXTERN(TEXT("Binned Used Peak"), STAT_Binned2_UsedPeak,STATGROUP_MemoryAllocator, CORE_API); +DECLARE_DWORD_COUNTER_STAT_EXTERN(TEXT("Binned Current Allocs"), STAT_Binned2_CurrentAllocs,STATGROUP_MemoryAllocator, CORE_API); +DECLARE_DWORD_COUNTER_STAT_EXTERN(TEXT("Binned Total Allocs"), STAT_Binned2_TotalAllocs,STATGROUP_MemoryAllocator, CORE_API); +DECLARE_MEMORY_STAT_EXTERN(TEXT("Binned Slack Current"), STAT_Binned2_SlackCurrent,STATGROUP_MemoryAllocator, CORE_API); + +/** Malloc binned allocator specific stats. */ +DEFINE_STAT(STAT_Binned2_OsCurrent); +DEFINE_STAT(STAT_Binned2_OsPeak); +DEFINE_STAT(STAT_Binned2_WasteCurrent); +DEFINE_STAT(STAT_Binned2_WastePeak); +DEFINE_STAT(STAT_Binned2_UsedCurrent); +DEFINE_STAT(STAT_Binned2_UsedPeak); +DEFINE_STAT(STAT_Binned2_CurrentAllocs); +DEFINE_STAT(STAT_Binned2_TotalAllocs); +DEFINE_STAT(STAT_Binned2_SlackCurrent); + +/** Information about a piece of free memory. 8 bytes */ +struct FMallocBinned2::FFreeMem +{ + /** Next or MemLastPool[], always in order by pool. */ + FFreeMem* Next; + /** Number of consecutive free blocks here, at least 1. */ + uint32 NumFreeBlocks; +}; + +// Memory pool info. 32 bytes. +struct FMallocBinned2::FPoolInfo +{ + /** Number of allocated elements in this pool, when counts down to zero can free the entire pool. */ + uint16 Taken; // 2 + /** Index of pool. Index into MemSizeToPoolTable[]. Valid when < MAX_POOLED_ALLOCATION_SIZE, MAX_POOLED_ALLOCATION_SIZE is OsTable. + When AllocSize is 0, this is the number of pages to step back to find the base address of an allocation. See FindPoolInfoInternal() + */ + uint16 TableIndex; // 4 + /** Number of bytes allocated */ + uint32 AllocSize; // 8 + /** Pointer to first free memory in this pool or the OS Allocation Size in bytes if this allocation is not binned*/ + FFreeMem* FirstMem; // 12/16 + FPoolInfo* Next; // 16/24 + FPoolInfo** PrevLink; // 20/32 +#if PLATFORM_32BITS + /** Explicit padding for 32 bit builds */ + uint8 Padding[12]; // 32 +#endif + + void SetAllocationSizes( uint32 InBytes, UPTRINT InOsBytes, uint32 InTableIndex, uint32 SmallAllocLimt ) + { + TableIndex=InTableIndex; + AllocSize=InBytes; + if (TableIndex == SmallAllocLimt) + { + FirstMem=(FFreeMem*)InOsBytes; + } + } + + uint32 GetBytes() const + { + return AllocSize; + } + + UPTRINT GetOsBytes( uint32 InPageSize, uint32 SmallAllocLimt ) const + { + if (TableIndex == SmallAllocLimt) + { + return (UPTRINT)FirstMem; + } + else + { + return Align(AllocSize, InPageSize); + } + } + + void Link( FPoolInfo*& Before ) + { + if( Before ) + { + Before->PrevLink = &Next; + } + Next = Before; + PrevLink = &Before; + Before = this; + } + + void Unlink() + { + if( Next ) + { + Next->PrevLink = PrevLink; + } + *PrevLink = Next; + } +}; + +/** Hash table struct for retrieving allocation book keeping information */ +struct FMallocBinned2::PoolHashBucket +{ + UPTRINT Key; + FPoolInfo* FirstPool; + PoolHashBucket* Prev; + PoolHashBucket* Next; + + PoolHashBucket() + { + Key=0; + FirstPool=nullptr; + Prev=this; + Next=this; + } + + void Link( PoolHashBucket* After ) + { + Link(After, Prev, this); + } + + static void Link( PoolHashBucket* Node, PoolHashBucket* Before, PoolHashBucket* After ) + { + Node->Prev=Before; + Node->Next=After; + Before->Next=Node; + After->Prev=Node; + } + + void Unlink() + { + Next->Prev = Prev; + Prev->Next = Next; + Prev=this; + Next=this; + } +}; + +struct FMallocBinned2::Private +{ + /** Default alignment for binned allocator */ + enum { DEFAULT_BINNED_ALLOCATOR_ALIGNMENT = sizeof(FFreeMem) }; + enum { PAGE_SIZE_LIMIT = 65536 }; + // BINNED_ALLOC_POOL_SIZE can be increased beyond 64k to cause binned malloc to allocate + // the small size bins in bigger chunks. If OS Allocation is slow, increasing + // this number *may* help performance but YMMV. + enum { BINNED_ALLOC_POOL_SIZE = 65536 }; + + // Implementation. + static CA_NO_RETURN void OutOfMemory(uint64 Size, uint32 Alignment=0) + { + // this is expected not to return + FPlatformMemory::OnOutOfMemory(Size, Alignment); + } + + static FORCEINLINE void TrackStats(FPoolTable* Table, SIZE_T Size) + { +#if STATS + // keep track of memory lost to padding + Table->TotalWaste += Table->BlockSize - Size; + Table->TotalRequests++; + Table->ActiveRequests++; + Table->MaxActiveRequests = FMath::Max(Table->MaxActiveRequests, Table->ActiveRequests); + Table->MaxRequest = Size > Table->MaxRequest ? Size : Table->MaxRequest; + Table->MinRequest = Size < Table->MinRequest ? Size : Table->MinRequest; +#endif + } + + /** + * Create a 64k page of FPoolInfo structures for tracking allocations + */ + static FPoolInfo* CreateIndirect(FMallocBinned2& Allocator) + { + uint64 IndirectPoolBlockSizeBytes = Allocator.IndirectPoolBlockSize * sizeof(FPoolInfo); + + checkSlow(IndirectPoolBlockSizeBytes <= Allocator.PageSize); + FPoolInfo* Indirect = (FPoolInfo*)FPlatformMemory::BinnedAllocFromOS(IndirectPoolBlockSizeBytes); + if( !Indirect ) + { + OutOfMemory(IndirectPoolBlockSizeBytes); + } + FMemory::Memset(Indirect, 0, IndirectPoolBlockSizeBytes); + + BINNED2_PEAK_STATCOUNTER(Allocator.OsPeak, BINNED2_ADD_STATCOUNTER(Allocator.OsCurrent, (int64)(Align(IndirectPoolBlockSizeBytes, Allocator.PageSize)))); + BINNED2_PEAK_STATCOUNTER(Allocator.WastePeak, BINNED2_ADD_STATCOUNTER(Allocator.WasteCurrent, (int64)(Align(IndirectPoolBlockSizeBytes, Allocator.PageSize)))); + + return Indirect; + } + + /** + * Gets the FPoolInfo for a memory address. If no valid info exists one is created. + * NOTE: This function requires a mutex across threads, but its is the callers responsibility to + * acquire the mutex before calling + */ + static FORCEINLINE FPoolInfo* GetPoolInfo(FMallocBinned2& Allocator, UPTRINT Ptr) + { + if (!Allocator.HashBuckets) + { + // Init tables. + Allocator.HashBuckets = (PoolHashBucket*)FPlatformMemory::BinnedAllocFromOS(Align(Allocator.MaxHashBuckets * sizeof(PoolHashBucket), Allocator.PageSize)); + + for (uint32 i = 0; i < Allocator.MaxHashBuckets; ++i) + { + new (Allocator.HashBuckets + i) PoolHashBucket(); + } + } + + UPTRINT Key = Ptr >> Allocator.HashKeyShift; + UPTRINT Hash = Key & (Allocator.MaxHashBuckets - 1); + UPTRINT PoolIndex = ((UPTRINT)Ptr >> Allocator.PoolBitShift) & Allocator.PoolMask; + + PoolHashBucket* Collision = &Allocator.HashBuckets[Hash]; + do + { + if (Collision->Key == Key || !Collision->FirstPool) + { + if (!Collision->FirstPool) + { + Collision->Key = Key; + InitializeHashBucket(Allocator, Collision); + CA_ASSUME(Collision->FirstPool); + } + return &Collision->FirstPool[PoolIndex]; + } + + Collision = Collision->Next; + } while (Collision != &Allocator.HashBuckets[Hash]); + + //Create a new hash bucket entry + PoolHashBucket* NewBucket = CreateHashBucket(Allocator); + NewBucket->Key = Key; + Allocator.HashBuckets[Hash].Link(NewBucket); + + return &NewBucket->FirstPool[PoolIndex]; + } + + static FORCEINLINE FPoolInfo* FindPoolInfo(FMallocBinned2& Allocator, UPTRINT Ptr1, UPTRINT& AllocationBase) + { + uint16 NextStep = 0; + UPTRINT Ptr = Ptr1 &~ ((UPTRINT)Allocator.PageSize - 1); + for (uint32 i = 0, n = (BINNED_ALLOC_POOL_SIZE / Allocator.PageSize) + 1; i < n; ++i) + { + FPoolInfo* Pool = FindPoolInfoInternal(Allocator, Ptr, NextStep); + if (Pool) + { + AllocationBase = Ptr; + //checkSlow(Ptr1 >= AllocationBase && Ptr1 < AllocationBase + Pool->GetBytes()); + return Pool; + } + Ptr = ((Ptr - (Allocator.PageSize * NextStep)) - 1) &~ ((UPTRINT)Allocator.PageSize - 1); + } + AllocationBase = 0; + return nullptr; + } + + static FORCEINLINE FPoolInfo* FindPoolInfoInternal(FMallocBinned2& Allocator, UPTRINT Ptr, uint16& JumpOffset) + { + checkSlow(Allocator.HashBuckets); + + uint32 Key = Ptr >> Allocator.HashKeyShift; + uint32 Hash = Key & (Allocator.MaxHashBuckets - 1); + uint32 PoolIndex = ((UPTRINT)Ptr >> Allocator.PoolBitShift) & Allocator.PoolMask; + + JumpOffset = 0; + + PoolHashBucket* Collision = &Allocator.HashBuckets[Hash]; + do + { + if (Collision->Key==Key) + { + if (!Collision->FirstPool[PoolIndex].AllocSize) + { + JumpOffset = Collision->FirstPool[PoolIndex].TableIndex; + return nullptr; + } + return &Collision->FirstPool[PoolIndex]; + } + Collision = Collision->Next; + } while (Collision != &Allocator.HashBuckets[Hash]); + + return nullptr; + } + + /** + * Returns a newly created and initialized PoolHashBucket for use. + */ + static FORCEINLINE PoolHashBucket* CreateHashBucket(FMallocBinned2& Allocator) + { + PoolHashBucket* Bucket = AllocateHashBucket(Allocator); + InitializeHashBucket(Allocator, Bucket); + return Bucket; + } + + /** + * Initializes bucket with valid parameters + * @param bucket pointer to be initialized + */ + static FORCEINLINE void InitializeHashBucket(FMallocBinned2& Allocator, PoolHashBucket* Bucket) + { + if (!Bucket->FirstPool) + { + Bucket->FirstPool = CreateIndirect(Allocator); + } + } + + /** + * Allocates a hash bucket from the free list of hash buckets + */ + static PoolHashBucket* AllocateHashBucket(FMallocBinned2& Allocator) + { + if (!Allocator.HashBucketFreeList) + { + const uint32 PageSize = Allocator.PageSize; + + Allocator.HashBucketFreeList = (PoolHashBucket*)FPlatformMemory::BinnedAllocFromOS(PageSize); + BINNED2_PEAK_STATCOUNTER(Allocator.OsPeak, BINNED2_ADD_STATCOUNTER(Allocator.OsCurrent, PageSize)); + BINNED2_PEAK_STATCOUNTER(Allocator.WastePeak, BINNED2_ADD_STATCOUNTER(Allocator.WasteCurrent, PageSize)); + + for (UPTRINT i = 0, n = PageSize / sizeof(PoolHashBucket); i < n; ++i) + { + Allocator.HashBucketFreeList->Link(new (Allocator.HashBucketFreeList + i) PoolHashBucket()); + } + } + + PoolHashBucket* NextFree = Allocator.HashBucketFreeList->Next; + PoolHashBucket* Free = Allocator.HashBucketFreeList; + + Free->Unlink(); + if (NextFree == Free) + { + NextFree = nullptr; + } + Allocator.HashBucketFreeList = NextFree; + + return Free; + } + + static FPoolInfo* AllocatePoolMemory(FMallocBinned2& Allocator, FPoolTable* Table, uint32 PoolSize, uint16 TableIndex) + { + const uint32 PageSize = Allocator.PageSize; + + // Must create a new pool. + uint32 Blocks = PoolSize / Table->BlockSize; + uint32 Bytes = Blocks * Table->BlockSize; + UPTRINT OsBytes = Align(Bytes, PageSize); + + checkSlow(Blocks >= 1); + checkSlow(Blocks * Table->BlockSize <= Bytes && PoolSize >= Bytes); + + // Allocate memory. + FFreeMem* Free = nullptr; + SIZE_T ActualPoolSize; //TODO: use this to reduce waste? + Free = (FFreeMem*)OSAlloc(Allocator, OsBytes, ActualPoolSize); + + checkSlow(!((UPTRINT)Free & (PageSize - 1))); + if( !Free ) + { + OutOfMemory(OsBytes); + } + + // Create pool in the indirect table. + FPoolInfo* Pool; + { + Pool = GetPoolInfo(Allocator, (UPTRINT)Free); + for (UPTRINT i = (UPTRINT)PageSize, Offset = 0; i < OsBytes; i += PageSize, ++Offset) + { + FPoolInfo* TrailingPool = GetPoolInfo(Allocator, ((UPTRINT)Free) + i); + check(TrailingPool); + + //Set trailing pools to point back to first pool + TrailingPool->SetAllocationSizes(0, 0, Offset, Allocator.BinnedOSTableIndex); + } + + + BINNED2_PEAK_STATCOUNTER(Allocator.OsPeak, BINNED2_ADD_STATCOUNTER(Allocator.OsCurrent, OsBytes)); + BINNED2_PEAK_STATCOUNTER(Allocator.WastePeak, BINNED2_ADD_STATCOUNTER(Allocator.WasteCurrent, (OsBytes - Bytes))); + } + + // Init pool. + Pool->Link( Table->FirstPool ); + Pool->SetAllocationSizes(Bytes, OsBytes, TableIndex, Allocator.BinnedOSTableIndex); + Pool->Taken = 0; + Pool->FirstMem = Free; + +#if STATS + Table->NumActivePools++; + Table->MaxActivePools = FMath::Max(Table->MaxActivePools, Table->NumActivePools); +#endif + // Create first free item. + Free->NumFreeBlocks = Blocks; + Free->Next = nullptr; + + return Pool; + } + + static FORCEINLINE FFreeMem* AllocateBlockFromPool(FMallocBinned2& Allocator, FPoolTable* Table, FPoolInfo* Pool, uint32 Alignment) + { + // Pick first available block and unlink it. + Pool->Taken++; + checkSlow(Pool->TableIndex < Allocator.BinnedOSTableIndex); // if this is false, FirstMem is actually a size not a pointer + checkSlow(Pool->FirstMem); + checkSlow(Pool->FirstMem->NumFreeBlocks > 0); + checkSlow(Pool->FirstMem->NumFreeBlocks < PAGE_SIZE_LIMIT); + FFreeMem* Free = (FFreeMem*)((uint8*)Pool->FirstMem + --Pool->FirstMem->NumFreeBlocks * Table->BlockSize); + if( !Pool->FirstMem->NumFreeBlocks ) + { + Pool->FirstMem = Pool->FirstMem->Next; + if( !Pool->FirstMem ) + { + // Move to exhausted list. + Pool->Unlink(); + Pool->Link( Table->ExhaustedPool ); + } + } + BINNED2_PEAK_STATCOUNTER(Allocator.UsedPeak, BINNED2_ADD_STATCOUNTER(Allocator.UsedCurrent, Table->BlockSize)); + return Align(Free, Alignment); + } + + /** + * Releases memory back to the system. This is not protected from multi-threaded access and it's + * the callers responsibility to Lock AccessGuard before calling this. + */ + static void FreeInternal(FMallocBinned2& Allocator, void* Ptr) + { + BINNED2_MEM_TIME(MemTime -= FPlatformTime::Seconds()); + BINNED2_DECREMENT_STATCOUNTER(Allocator.CurrentAllocs); + + UPTRINT BasePtr; + FPoolInfo* Pool = FindPoolInfo(Allocator, (UPTRINT)Ptr, BasePtr); +#if PLATFORM_IOS || PLATFORM_MAC + if (Pool == NULL) + { + UE_LOG(LogMemory, Warning, TEXT("Attempting to free a pointer we didn't allocate!")); + return; + } +#endif + checkSlow(Pool); + checkSlow(Pool->GetBytes() != 0); + if (Pool->TableIndex < Allocator.BinnedOSTableIndex) + { + FPoolTable* Table = Allocator.MemSizeToPoolTable[Pool->TableIndex]; +#if STATS + Table->ActiveRequests--; +#endif + // If this pool was exhausted, move to available list. + if( !Pool->FirstMem ) + { + Pool->Unlink(); + Pool->Link( Table->FirstPool ); + } + + void* BaseAddress = (void*)BasePtr; + uint32 BlockSize = Table->BlockSize; + PTRINT OffsetFromBase = (PTRINT)Ptr - (PTRINT)BaseAddress; + check(OffsetFromBase >= 0); + uint32 AlignOffset = OffsetFromBase % BlockSize; + + // Patch pointer to include previously applied alignment. + Ptr = (void*)((PTRINT)Ptr - (PTRINT)AlignOffset); + + // Free a pooled allocation. + FFreeMem* Free = (FFreeMem*)Ptr; + Free->NumFreeBlocks = 1; + Free->Next = Pool->FirstMem; + Pool->FirstMem = Free; + BINNED2_ADD_STATCOUNTER(Allocator.UsedCurrent, -(int64)(Table->BlockSize)); + + // Free this pool. + checkSlow(Pool->Taken >= 1); + if( --Pool->Taken == 0 ) + { +#if STATS + Table->NumActivePools--; +#endif + // Free the OS memory. + SIZE_T OsBytes = Pool->GetOsBytes(Allocator.PageSize, Allocator.BinnedOSTableIndex); + BINNED2_ADD_STATCOUNTER(Allocator.OsCurrent, -(int64)OsBytes); + BINNED2_ADD_STATCOUNTER(Allocator.WasteCurrent, -(int64)(OsBytes - Pool->GetBytes())); + Pool->Unlink(); + Pool->SetAllocationSizes(0, 0, 0, Allocator.BinnedOSTableIndex); + OSFree(Allocator, (void*)BasePtr, OsBytes); + } + } + else + { + // Free an OS allocation. + checkSlow(!((UPTRINT)Ptr & (Allocator.PageSize - 1))); + SIZE_T OsBytes = Pool->GetOsBytes(Allocator.PageSize, Allocator.BinnedOSTableIndex); + + BINNED2_ADD_STATCOUNTER(Allocator.UsedCurrent, -(int64)Pool->GetBytes()); + BINNED2_ADD_STATCOUNTER(Allocator.OsCurrent, -(int64)OsBytes); + BINNED2_ADD_STATCOUNTER(Allocator.WasteCurrent, -(int64)(OsBytes - Pool->GetBytes())); + OSFree(Allocator, (void*)BasePtr, OsBytes); + } + + BINNED2_MEM_TIME(MemTime += FPlatformTime::Seconds()); + } + + static void PushFreeLockless(FMallocBinned2& Allocator, void* Ptr) + { + FreeInternal(Allocator, Ptr); + } + + static FORCEINLINE void OSFree(FMallocBinned2& Allocator, void* Ptr, SIZE_T Size) + { +#ifdef BINNED2_CACHE_FREED_OS_ALLOCS + if (Size > BINNED2_MAX_CACHED_OS_FREES_BYTE_LIMIT / 4) + { + FPlatformMemory::BinnedFreeToOS(Ptr); + return; + } + + while (Allocator.FreedPageBlocksNum && (Allocator.FreedPageBlocksNum >= BINNED2_MAX_CACHED_OS_FREES || Allocator.CachedTotal + Size > BINNED2_MAX_CACHED_OS_FREES_BYTE_LIMIT)) + { + //Remove the oldest one + void* FreePtr = Allocator.FreedPageBlocks[0].Ptr; + Allocator.CachedTotal -= Allocator.FreedPageBlocks[0].ByteSize; + Allocator.FreedPageBlocksNum--; + if (Allocator.FreedPageBlocksNum) + { + FMemory::Memmove(&Allocator.FreedPageBlocks[0], &Allocator.FreedPageBlocks[1], sizeof(FFreePageBlock) * Allocator.FreedPageBlocksNum); + } + FPlatformMemory::BinnedFreeToOS(FreePtr); + } + Allocator.FreedPageBlocks[Allocator.FreedPageBlocksNum].Ptr = Ptr; + Allocator.FreedPageBlocks[Allocator.FreedPageBlocksNum].ByteSize = Size; + Allocator.CachedTotal += Size; + ++Allocator.FreedPageBlocksNum; +#else + (void)Size; + FPlatformMemory::BinnedFreeToOS(Ptr); +#endif + } + + static FORCEINLINE void* OSAlloc(FMallocBinned2& Allocator, SIZE_T NewSize, SIZE_T& OutActualSize) + { +#ifdef BINNED2_CACHE_FREED_OS_ALLOCS + // We want to hold the lock a little as possible so release it + // before the big call to the OS + { + for (uint32 i=0; i < Allocator.FreedPageBlocksNum; ++i) + { + // look for exact matches first, these are aligned to the page size, so it should be quite common to hit these on small pages sizes + if (Allocator.FreedPageBlocks[i].ByteSize == NewSize) + { + void* Ret = Allocator.FreedPageBlocks[i].Ptr; + OutActualSize = Allocator.FreedPageBlocks[i].ByteSize; + Allocator.CachedTotal -= Allocator.FreedPageBlocks[i].ByteSize; + if (i < Allocator.FreedPageBlocksNum - 1) + { + FMemory::Memmove(&Allocator.FreedPageBlocks[i], &Allocator.FreedPageBlocks[i + 1], sizeof(FFreePageBlock) * (Allocator.FreedPageBlocksNum - i - 1)); + } + Allocator.FreedPageBlocksNum--; + return Ret; + } + }; + for (uint32 i=0; i < Allocator.FreedPageBlocksNum; ++i) + { + // is it possible (and worth i.e. <25% overhead) to use this block + if (Allocator.FreedPageBlocks[i].ByteSize >= NewSize && Allocator.FreedPageBlocks[i].ByteSize * 3 <= NewSize * 4) + { + void* Ret = Allocator.FreedPageBlocks[i].Ptr; + OutActualSize = Allocator.FreedPageBlocks[i].ByteSize; + Allocator.CachedTotal -= Allocator.FreedPageBlocks[i].ByteSize; + if (i < Allocator.FreedPageBlocksNum - 1) + { + FMemory::Memmove(&Allocator.FreedPageBlocks[i], &Allocator.FreedPageBlocks[i + 1], sizeof(FFreePageBlock) * (Allocator.FreedPageBlocksNum - i - 1)); + } + Allocator.FreedPageBlocksNum--; + return Ret; + } + }; + } + OutActualSize = NewSize; + void* Ptr = FPlatformMemory::BinnedAllocFromOS(NewSize); + if (!Ptr) + { + //Are we holding on to much mem? Release it all. + FlushAllocCache(Allocator); + Ptr = FPlatformMemory::BinnedAllocFromOS(NewSize); + } + return Ptr; +#else + (void)OutActualSize; + return FPlatformMemory::BinnedAllocFromOS(NewSize); +#endif + } + +#ifdef BINNED2_CACHE_FREED_OS_ALLOCS + static void FlushAllocCache(FMallocBinned2& Allocator) + { + for (int i = 0, n = Allocator.FreedPageBlocksNum; i PageSize); // Check to catch 32 bit overflow in AddressLimit + + /** Shift to get the reference from the indirect tables */ + PoolBitShift = FPlatformMath::CeilLogTwo(PageSize); + IndirectPoolBitShift = FPlatformMath::CeilLogTwo(PageSize/sizeof(FPoolInfo)); + IndirectPoolBlockSize = PageSize/sizeof(FPoolInfo); + + MaxHashBuckets = AddressLimit >> (IndirectPoolBitShift+PoolBitShift); + MaxHashBucketBits = FPlatformMath::CeilLogTwo(MaxHashBuckets); + MaxHashBucketWaste = (MaxHashBuckets*sizeof(PoolHashBucket))/1024; + MaxBookKeepingOverhead = ((AddressLimit/PageSize)*sizeof(PoolHashBucket))/(1024*1024); + /** + * Shift required to get required hash table key. + */ + HashKeyShift = PoolBitShift+IndirectPoolBitShift; + /** Used to mask off the bits that have been used to lookup the indirect table */ + PoolMask = ( ( 1ull << ( HashKeyShift - PoolBitShift ) ) - 1 ); + BinnedSizeLimit = Private::PAGE_SIZE_LIMIT/2; + BinnedOSTableIndex = BinnedSizeLimit+EXTENDED_PAGE_POOL_ALLOCATION_COUNT; + + check((BinnedSizeLimit & (BinnedSizeLimit-1)) == 0); + + + // Init tables. + OsTable.FirstPool = nullptr; + OsTable.ExhaustedPool = nullptr; + OsTable.BlockSize = 0; + + /** The following options are not valid for page sizes less than 64k. They are here to reduce waste*/ + PagePoolTable[0].FirstPool = nullptr; + PagePoolTable[0].ExhaustedPool = nullptr; + PagePoolTable[0].BlockSize = PageSize == Private::PAGE_SIZE_LIMIT ? BinnedSizeLimit+(BinnedSizeLimit/2) : 0; + + PagePoolTable[1].FirstPool = nullptr; + PagePoolTable[1].ExhaustedPool = nullptr; + PagePoolTable[1].BlockSize = PageSize == Private::PAGE_SIZE_LIMIT ? PageSize+BinnedSizeLimit : 0; + + // Block sizes are based around getting the maximum amount of allocations per pool, with as little alignment waste as possible. + // Block sizes should be close to even divisors of the POOL_SIZE, and well distributed. They must be 16-byte aligned as well. + static const uint32 BlockSizes[POOL_COUNT] = + { + 8, 16, 32, 48, 64, 80, 96, 112, + 128, 160, 192, 224, 256, 288, 320, 384, + 448, 512, 576, 640, 704, 768, 896, 1024, + 1168, 1360, 1632, 2048, 2336, 2720, 3264, 4096, + 4672, 5456, 6544, 8192, 9360, 10912, 13104, 16384, + 21840, 32768 + }; + + for( uint32 i = 0; i < POOL_COUNT; i++ ) + { + PoolTable[i].FirstPool = nullptr; + PoolTable[i].ExhaustedPool = nullptr; + PoolTable[i].BlockSize = BlockSizes[i]; +#if STATS + PoolTable[i].MinRequest = PoolTable[i].BlockSize; +#endif + } + + for( uint32 i=0; i(Alignment, Private::DEFAULT_BINNED_ALLOCATOR_ALIGNMENT); + SIZE_T SpareBytesCount = FMath::Min(Private::DEFAULT_BINNED_ALLOCATOR_ALIGNMENT, Size); + Size = FMath::Max(PoolTable[0].BlockSize, Size + (Alignment - SpareBytesCount)); + BINNED2_MEM_TIME(MemTime -= FPlatformTime::Seconds()); + + BINNED2_INCREMENT_STATCOUNTER(CurrentAllocs); + BINNED2_INCREMENT_STATCOUNTER(TotalAllocs); + + FFreeMem* Free; + if( Size < BinnedSizeLimit ) + { + // Allocate from pool. + FPoolTable* Table = MemSizeToPoolTable[Size]; + checkSlow(Size <= Table->BlockSize); + + Private::TrackStats(Table, Size); + + FPoolInfo* Pool = Table->FirstPool; + if( !Pool ) + { + Pool = Private::AllocatePoolMemory(*this, Table, Private::BINNED_ALLOC_POOL_SIZE/*PageSize*/, Size); + } + + Free = Private::AllocateBlockFromPool(*this, Table, Pool, Alignment); + } + else if ( ((Size >= BinnedSizeLimit && Size <= PagePoolTable[0].BlockSize) || + (Size > PageSize && Size <= PagePoolTable[1].BlockSize))) + { + // Bucket in a pool of 3*PageSize or 6*PageSize + uint32 BinType = Size < PageSize ? 0 : 1; + uint32 PageCount = 3*BinType + 3; + FPoolTable* Table = &PagePoolTable[BinType]; + checkSlow(Size <= Table->BlockSize); + + Private::TrackStats(Table, Size); + + FPoolInfo* Pool = Table->FirstPool; + if( !Pool ) + { + Pool = Private::AllocatePoolMemory(*this, Table, PageCount*PageSize, BinnedSizeLimit+BinType); + } + + Free = Private::AllocateBlockFromPool(*this, Table, Pool, Alignment); + } + else + { + // Use OS for large allocations. + UPTRINT AlignedSize = Align(Size,PageSize); + SIZE_T ActualPoolSize; //TODO: use this to reduce waste? + Free = (FFreeMem*)Private::OSAlloc(*this, AlignedSize, ActualPoolSize); + if( !Free ) + { + Private::OutOfMemory(AlignedSize); + } + + void* AlignedFree = Align(Free, Alignment); + + // Create indirect. + FPoolInfo* Pool; + { + Pool = Private::GetPoolInfo(*this, (UPTRINT)Free); + + if ((UPTRINT)Free != ((UPTRINT)AlignedFree & ~((UPTRINT)PageSize - 1))) + { + // Mark the FPoolInfo for AlignedFree to jump back to the FPoolInfo for ptr. + for (UPTRINT i = (UPTRINT)PageSize, Offset = 0; i < AlignedSize; i += PageSize, ++Offset) + { + FPoolInfo* TrailingPool = Private::GetPoolInfo(*this, ((UPTRINT)Free) + i); + check(TrailingPool); + //Set trailing pools to point back to first pool + TrailingPool->SetAllocationSizes(0, 0, Offset, BinnedOSTableIndex); + } + } + } + Free = (FFreeMem*)AlignedFree; + Pool->SetAllocationSizes(Size, AlignedSize, BinnedOSTableIndex, BinnedOSTableIndex); + BINNED2_PEAK_STATCOUNTER(OsPeak, BINNED2_ADD_STATCOUNTER(OsCurrent, AlignedSize)); + BINNED2_PEAK_STATCOUNTER(UsedPeak, BINNED2_ADD_STATCOUNTER(UsedCurrent, Size)); + BINNED2_PEAK_STATCOUNTER(WastePeak, BINNED2_ADD_STATCOUNTER(WasteCurrent, (int64)(AlignedSize - Size))); + } + + BINNED2_MEM_TIME(MemTime += FPlatformTime::Seconds()); + return Free; +} + +void* FMallocBinned2::Realloc( void* Ptr, SIZE_T NewSize, uint32 Alignment ) +{ + // Handle DEFAULT_ALIGNMENT for binned allocator. + if (Alignment == DEFAULT_ALIGNMENT) + { + Alignment = Private::DEFAULT_BINNED_ALLOCATOR_ALIGNMENT; + } + + Alignment = FMath::Max(Alignment, Private::DEFAULT_BINNED_ALLOCATOR_ALIGNMENT); + const uint32 NewSizeUnmodified = NewSize; + SIZE_T SpareBytesCount = FMath::Min(Private::DEFAULT_BINNED_ALLOCATOR_ALIGNMENT, NewSize); + if (NewSize) + { + NewSize = FMath::Max(PoolTable[0].BlockSize, NewSize + (Alignment - SpareBytesCount)); + } + BINNED2_MEM_TIME(MemTime -= FPlatformTime::Seconds()); + UPTRINT BasePtr; + void* NewPtr = Ptr; + if( Ptr && NewSize ) + { + FPoolInfo* Pool = Private::FindPoolInfo(*this, (UPTRINT)Ptr, BasePtr); + + if( Pool->TableIndex < BinnedOSTableIndex ) + { + // Allocated from pool, so grow or shrink if necessary. + check(Pool->TableIndex > 0); // it isn't possible to allocate a size of 0, Malloc will increase the size to DEFAULT_BINNED_ALLOCATOR_ALIGNMENT + if (NewSizeUnmodified > MemSizeToPoolTable[Pool->TableIndex]->BlockSize || NewSizeUnmodified <= MemSizeToPoolTable[Pool->TableIndex - 1]->BlockSize) + { + NewPtr = Malloc(NewSizeUnmodified, Alignment); + FMemory::Memcpy(NewPtr, Ptr, FMath::Min(NewSizeUnmodified, MemSizeToPoolTable[Pool->TableIndex]->BlockSize - (Alignment - SpareBytesCount))); + Free( Ptr ); + } + else if (((UPTRINT)Ptr & (UPTRINT)(Alignment - 1)) != 0) + { + NewPtr = Align(Ptr, Alignment); + FMemory::Memmove(NewPtr, Ptr, NewSize); + } + } + else + { + // Allocated from OS. + if( NewSize > Pool->GetOsBytes(PageSize, BinnedOSTableIndex) || NewSize * 3 < Pool->GetOsBytes(PageSize, BinnedOSTableIndex) * 2 ) + { + // Grow or shrink. + NewPtr = Malloc(NewSizeUnmodified, Alignment); + FMemory::Memcpy(NewPtr, Ptr, FMath::Min(NewSizeUnmodified, Pool->GetBytes())); + Free( Ptr ); + } + else + { + //need a lock to cover the SetAllocationSizes() + int32 UsedChange = (NewSize - Pool->GetBytes()); + + // Keep as-is, reallocation isn't worth the overhead. + BINNED2_ADD_STATCOUNTER(UsedCurrent, UsedChange); + BINNED2_PEAK_STATCOUNTER(UsedPeak, UsedCurrent); + BINNED2_ADD_STATCOUNTER(WasteCurrent, (Pool->GetBytes() - NewSize)); + Pool->SetAllocationSizes(NewSizeUnmodified, Pool->GetOsBytes(PageSize, BinnedOSTableIndex), BinnedOSTableIndex, BinnedOSTableIndex); + } + } + } + else if( Ptr == nullptr ) + { + NewPtr = Malloc(NewSizeUnmodified, Alignment); + } + else + { + Free( Ptr ); + NewPtr = nullptr; + } + + BINNED2_MEM_TIME(MemTime += FPlatformTime::Seconds()); + return NewPtr; +} + +void FMallocBinned2::Free( void* Ptr ) +{ + if( !Ptr ) + { + return; + } + + Private::PushFreeLockless(*this, Ptr); +} + +bool FMallocBinned2::GetAllocationSize(void *Original, SIZE_T &SizeOut) +{ + if (!Original) + { + return false; + } + UPTRINT BasePtr; + FPoolInfo* Pool = Private::FindPoolInfo(*this, (UPTRINT)Original, BasePtr); + SizeOut = Pool->TableIndex < BinnedOSTableIndex ? MemSizeToPoolTable[Pool->TableIndex]->BlockSize : Pool->GetBytes(); + return true; +} + +bool FMallocBinned2::ValidateHeap() +{ + for( int32 i = 0; i < POOL_COUNT; i++ ) + { + FPoolTable* Table = &PoolTable[i]; + for( FPoolInfo** PoolPtr = &Table->FirstPool; *PoolPtr; PoolPtr = &(*PoolPtr)->Next ) + { + FPoolInfo* Pool = *PoolPtr; + check(Pool->PrevLink == PoolPtr); + check(Pool->FirstMem); + for( FFreeMem* Free = Pool->FirstMem; Free; Free = Free->Next ) + { + check(Free->NumFreeBlocks > 0); + } + } + for( FPoolInfo** PoolPtr = &Table->ExhaustedPool; *PoolPtr; PoolPtr = &(*PoolPtr)->Next ) + { + FPoolInfo* Pool = *PoolPtr; + check(Pool->PrevLink == PoolPtr); + check(!Pool->FirstMem); + } + } + + return( true ); +} + +void FMallocBinned2::UpdateStats() +{ + FMalloc::UpdateStats(); +#if STATS + SIZE_T LocalOsCurrent = 0; + SIZE_T LocalOsPeak = 0; + SIZE_T LocalWasteCurrent = 0; + SIZE_T LocalWastePeak = 0; + SIZE_T LocalUsedCurrent = 0; + SIZE_T LocalUsedPeak = 0; + SIZE_T LocalCurrentAllocs = 0; + SIZE_T LocalTotalAllocs = 0; + SIZE_T LocalSlackCurrent = 0; + + { + Private::UpdateSlackStat(*this); + + // Copy memory stats. + LocalOsCurrent = OsCurrent; + LocalOsPeak = OsPeak; + LocalWasteCurrent = WasteCurrent; + LocalWastePeak = WastePeak; + LocalUsedCurrent = UsedCurrent; + LocalUsedPeak = UsedPeak; + LocalCurrentAllocs = CurrentAllocs; + LocalTotalAllocs = TotalAllocs; + LocalSlackCurrent = SlackCurrent; + } + + SET_MEMORY_STAT( STAT_Binned2_OsCurrent, LocalOsCurrent ); + SET_MEMORY_STAT( STAT_Binned2_OsPeak, LocalOsPeak ); + SET_MEMORY_STAT( STAT_Binned2_WasteCurrent, LocalWasteCurrent ); + SET_MEMORY_STAT( STAT_Binned2_WastePeak, LocalWastePeak ); + SET_MEMORY_STAT( STAT_Binned2_UsedCurrent, LocalUsedCurrent ); + SET_MEMORY_STAT( STAT_Binned2_UsedPeak, LocalUsedPeak ); + SET_DWORD_STAT( STAT_Binned2_CurrentAllocs, LocalCurrentAllocs ); + SET_DWORD_STAT( STAT_Binned2_TotalAllocs, LocalTotalAllocs ); + SET_MEMORY_STAT( STAT_Binned2_SlackCurrent, LocalSlackCurrent ); +#endif +} + +void FMallocBinned2::DumpAllocatorStats( class FOutputDevice& Ar ) +{ + FBufferedOutputDevice BufferedOutput; + { + ValidateHeap(); +#if STATS + Private::UpdateSlackStat(*this); +#if !NO_LOGGING + // This is all of the memory including stuff too big for the pools + BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "Allocator Stats for %s:" ), GetDescriptiveName() ); + // Waste is the total overhead of the memory system + BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "Current Memory %.2f MB used, plus %.2f MB waste" ), UsedCurrent / (1024.0f * 1024.0f), (OsCurrent - UsedCurrent) / (1024.0f * 1024.0f) ); + BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "Peak Memory %.2f MB used, plus %.2f MB waste" ), UsedPeak / (1024.0f * 1024.0f), (OsPeak - UsedPeak) / (1024.0f * 1024.0f) ); + + BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "Current OS Memory %.2f MB, peak %.2f MB" ), OsCurrent / (1024.0f * 1024.0f), OsPeak / (1024.0f * 1024.0f) ); + BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "Current Waste %.2f MB, peak %.2f MB" ), WasteCurrent / (1024.0f * 1024.0f), WastePeak / (1024.0f * 1024.0f) ); + BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "Current Used %.2f MB, peak %.2f MB" ), UsedCurrent / (1024.0f * 1024.0f), UsedPeak / (1024.0f * 1024.0f) ); + BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "Current Slack %.2f MB" ), SlackCurrent / (1024.0f * 1024.0f) ); + + BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "Allocs % 6i Current / % 6i Total" ), CurrentAllocs, TotalAllocs ); + BINNED2_MEM_TIME( BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "Seconds % 5.3f" ), MemTime ) ); + BINNED2_MEM_TIME( BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "MSec/Allc % 5.5f" ), 1000.0 * MemTime / MemAllocs ) ); + + // This is the memory tracked inside individual allocation pools + BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "" ) ); + BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "Block Size Num Pools Max Pools Cur Allocs Total Allocs Min Req Max Req Mem Used Mem Slack Mem Waste Efficiency" ) ); + BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "---------- --------- --------- ---------- ------------ ------- ------- -------- --------- --------- ----------" ) ); + + uint32 TotalMemory = 0; + uint32 TotalWaste = 0; + uint32 TotalActiveRequests = 0; + uint32 TotalTotalRequests = 0; + uint32 TotalPools = 0; + uint32 TotalSlack = 0; + + FPoolTable* Table = nullptr; + for( int32 i = 0; i < BinnedSizeLimit + EXTENDED_PAGE_POOL_ALLOCATION_COUNT; i++ ) + { + if( Table == MemSizeToPoolTable[i] || MemSizeToPoolTable[i]->BlockSize == 0 ) + continue; + + Table = MemSizeToPoolTable[i]; + + uint32 TableAllocSize = (Table->BlockSize > BinnedSizeLimit ? (((3 * (i - BinnedSizeLimit)) + 3)*Private::BINNED_ALLOC_POOL_SIZE) : Private::BINNED_ALLOC_POOL_SIZE); + // The amount of memory allocated from the OS + uint32 MemAllocated = (Table->NumActivePools * TableAllocSize) / 1024; + // Amount of memory actually in use by allocations + uint32 MemUsed = (Table->BlockSize * Table->ActiveRequests) / 1024; + // Wasted memory due to pool size alignment + uint32 PoolMemWaste = Table->NumActivePools * (TableAllocSize - ((TableAllocSize / Table->BlockSize) * Table->BlockSize)) / 1024; + // Wasted memory due to individual allocation alignment. This is an estimate. + uint32 MemWaste = (uint32)(((double)Table->TotalWaste / (double)Table->TotalRequests) * (double)Table->ActiveRequests) / 1024 + PoolMemWaste; + // Memory that is reserved in active pools and ready for future use + uint32 MemSlack = MemAllocated - MemUsed - PoolMemWaste; + + BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "% 10i % 9i % 9i % 10i % 12i % 7i % 7i % 7iK % 8iK % 8iK % 9.2f%%" ), + Table->BlockSize, + Table->NumActivePools, + Table->MaxActivePools, + Table->ActiveRequests, + (uint32)Table->TotalRequests, + Table->MinRequest, + Table->MaxRequest, + MemUsed, + MemSlack, + MemWaste, + MemAllocated ? 100.0f * (MemAllocated - MemWaste) / MemAllocated : 100.0f ); + + TotalMemory += MemAllocated; + TotalWaste += MemWaste; + TotalSlack += MemSlack; + TotalActiveRequests += Table->ActiveRequests; + TotalTotalRequests += Table->TotalRequests; + TotalPools += Table->NumActivePools; + } + + BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "" ) ); + BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "%iK allocated in pools (with %iK slack and %iK waste). Efficiency %.2f%%" ), TotalMemory, TotalSlack, TotalWaste, TotalMemory ? 100.0f * (TotalMemory - TotalWaste) / TotalMemory : 100.0f ); + BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "Allocations %i Current / %i Total (in %i pools)" ), TotalActiveRequests, TotalTotalRequests, TotalPools ); + BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "" ) ); +#endif +#endif + } + + BufferedOutput.RedirectTo( Ar ); +} + +const TCHAR* FMallocBinned2::GetDescriptiveName() +{ + return TEXT("binned"); +} diff --git a/Engine/Source/Runtime/Core/Private/HTML5/HTML5PlatformMemory.cpp b/Engine/Source/Runtime/Core/Private/HTML5/HTML5PlatformMemory.cpp index 98fdba9e2bb3..55114975c826 100644 --- a/Engine/Source/Runtime/Core/Private/HTML5/HTML5PlatformMemory.cpp +++ b/Engine/Source/Runtime/Core/Private/HTML5/HTML5PlatformMemory.cpp @@ -5,11 +5,13 @@ =============================================================================*/ #include "CorePrivatePCH.h" -#include "MallocBinned.h" +#include "MallocBinned2.h" #include "MallocAnsi.h" void FHTML5PlatformMemory::Init() { + FGenericPlatformMemory::Init(); + const FPlatformMemoryConstants& MemoryConstants = FPlatformMemory::GetConstants(); UE_LOG(LogInit, Log, TEXT("Memory total: Physical=%.1fGB (%dGB approx) "), (double)MemoryConstants.TotalPhysical / 1024.0f / 1024.0f / 1024.0f, @@ -49,7 +51,7 @@ FMalloc* FHTML5PlatformMemory::BaseAllocator() #if !PLATFORM_HTML5_WIN32 return new FMallocAnsi(); #else - return new FMallocBinned(32 * 1024, 1 << 30 ); + return new FMallocBinned2(32 * 1024, 1 << 30 ); #endif } diff --git a/Engine/Source/Runtime/Core/Private/IOS/IOSPlatformMemory.cpp b/Engine/Source/Runtime/Core/Private/IOS/IOSPlatformMemory.cpp index ee6ef71dad0b..2973a38dbe2e 100644 --- a/Engine/Source/Runtime/Core/Private/IOS/IOSPlatformMemory.cpp +++ b/Engine/Source/Runtime/Core/Private/IOS/IOSPlatformMemory.cpp @@ -5,12 +5,14 @@ =============================================================================*/ #include "CorePrivatePCH.h" -#include "MallocBinned.h" +#include "MallocBinned2.h" #include "MallocAnsi.h" void FIOSPlatformMemory::Init() { + FGenericPlatformMemory::Init(); + const FPlatformMemoryConstants& MemoryConstants = FPlatformMemory::GetConstants(); UE_LOG(LogInit, Log, TEXT("Memory total: Physical=%.1fGB (%dGB approx) Pagefile=%.1fGB Virtual=%.1fGB"), float(MemoryConstants.TotalPhysical/1024.0/1024.0/1024.0), @@ -97,7 +99,7 @@ FMalloc* FIOSPlatformMemory::BaseAllocator() uint64 MemoryLimit = FMath::Min( uint64(1) << FMath::CeilLogTwo(Stats.free_count * PageSize), 0x100000000); //return new FMallocAnsi(); - return new FMallocBinned(PageSize, MemoryLimit); + return new FMallocBinned2(PageSize, MemoryLimit); } void* FIOSPlatformMemory::BinnedAllocFromOS( SIZE_T Size ) diff --git a/Engine/Source/Runtime/Core/Private/Linux/LinuxPlatformMemory.cpp b/Engine/Source/Runtime/Core/Private/Linux/LinuxPlatformMemory.cpp index fb9ac83f6920..7495166a1457 100644 --- a/Engine/Source/Runtime/Core/Private/Linux/LinuxPlatformMemory.cpp +++ b/Engine/Source/Runtime/Core/Private/Linux/LinuxPlatformMemory.cpp @@ -7,7 +7,7 @@ #include "CorePrivatePCH.h" #include "MallocAnsi.h" #include "MallocJemalloc.h" -#include "MallocBinned.h" +#include "MallocBinned2.h" #include #include #include @@ -15,6 +15,8 @@ void FLinuxPlatformMemory::Init() { + FGenericPlatformMemory::Init(); + const FPlatformMemoryConstants& MemoryConstants = FPlatformMemory::GetConstants(); UE_LOG(LogInit, Log, TEXT(" - Physical RAM available (not considering process quota): %d GB (%lu MB, %lu KB, %lu bytes)"), MemoryConstants.TotalPhysicalGB, @@ -104,7 +106,7 @@ class FMalloc* FLinuxPlatformMemory::BaseAllocator() default: // intentional fall-through case Binned: - Allocator = new FMallocBinned(FPlatformMemory::GetConstants().PageSize & MAX_uint32, 0x100000000); + Allocator = new FMallocBinned2(FPlatformMemory::GetConstants().PageSize & MAX_uint32, 0x100000000); break; } diff --git a/Engine/Source/Runtime/Core/Private/Mac/MacPlatformMemory.cpp b/Engine/Source/Runtime/Core/Private/Mac/MacPlatformMemory.cpp index a3183f8c752c..a2692c39e024 100644 --- a/Engine/Source/Runtime/Core/Private/Mac/MacPlatformMemory.cpp +++ b/Engine/Source/Runtime/Core/Private/Mac/MacPlatformMemory.cpp @@ -7,13 +7,15 @@ #include "CorePrivatePCH.h" #include "MallocTBB.h" #include "MallocAnsi.h" -#include "MallocBinned.h" +#include "MallocBinned2.h" #include #include void FMacPlatformMemory::Init() { + FGenericPlatformMemory::Init(); + const FPlatformMemoryConstants& MemoryConstants = FPlatformMemory::GetConstants(); UE_LOG(LogInit, Log, TEXT("Memory total: Physical=%.1fGB (%dGB approx) Pagefile=%.1fGB Virtual=%.1fGB"), float(MemoryConstants.TotalPhysical/1024.0/1024.0/1024.0), @@ -48,7 +50,7 @@ FMalloc* FMacPlatformMemory::BaseAllocator() #elif (WITH_EDITORONLY_DATA || IS_PROGRAM) && TBB_ALLOCATOR_ALLOWED return new FMallocTBB(); #else - return new FMallocBinned((uint32)(GetConstants().PageSize&MAX_uint32), 0x100000000); + return new FMallocBinned2((uint32)(GetConstants().PageSize&MAX_uint32), 0x100000000); #endif } } diff --git a/Engine/Source/Runtime/Core/Private/Misc/App.cpp b/Engine/Source/Runtime/Core/Private/Misc/App.cpp index f7963fefe5c9..8b67fdad9709 100644 --- a/Engine/Source/Runtime/Core/Private/Misc/App.cpp +++ b/Engine/Source/Runtime/Core/Private/Misc/App.cpp @@ -146,14 +146,19 @@ bool FApp::ShouldUseThreadingForPerformance() #endif // HAVE_RUNTIME_THREADING_SWITCHES +static bool GUnfocusedVolumeMultiplierItialised = false; float FApp::GetUnfocusedVolumeMultiplier() { - static bool bInitialisedFromConfig = false; - - if (!bInitialisedFromConfig) + if (!GUnfocusedVolumeMultiplierItialised) { - bInitialisedFromConfig = true; + GUnfocusedVolumeMultiplierItialised = true; GConfig->GetFloat(TEXT("Audio"), TEXT("UnfocusedVolumeMultiplier"), UnfocusedVolumeMultiplier, GEngineIni); } return UnfocusedVolumeMultiplier; } + +void FApp::SetUnfocusedVolumeMultiplier(float InVolumeMultiplier) +{ + UnfocusedVolumeMultiplier = InVolumeMultiplier; + GUnfocusedVolumeMultiplierItialised = true; +} \ No newline at end of file diff --git a/Engine/Source/Runtime/Core/Private/Misc/CoreMisc.cpp b/Engine/Source/Runtime/Core/Private/Misc/CoreMisc.cpp index d029f1cc1c6e..c994e3eead1e 100644 --- a/Engine/Source/Runtime/Core/Private/Misc/CoreMisc.cpp +++ b/Engine/Source/Runtime/Core/Private/Misc/CoreMisc.cpp @@ -166,17 +166,23 @@ void FFileHelper::BufferToString( FString& Result, const uint8* Buffer, int32 Si */ bool FFileHelper::LoadFileToString( FString& Result, const TCHAR* Filename, uint32 VerifyFlags ) { - FArchive* Reader = IFileManager::Get().CreateFileReader( Filename ); + TUniquePtr Reader( IFileManager::Get().CreateFileReader( Filename ) ); if( !Reader ) { return 0; } int32 Size = Reader->TotalSize(); + if( !Size ) + { + Result.Empty(); + return true; + } + uint8* Ch = (uint8*)FMemory::Malloc(Size); Reader->Serialize( Ch, Size ); bool Success = Reader->Close(); - delete Reader; + Reader = nullptr; BufferToString( Result, Ch, Size ); // handle SHA verify of the file diff --git a/Engine/Source/Runtime/Core/Private/Misc/Paths.cpp b/Engine/Source/Runtime/Core/Private/Misc/Paths.cpp index 43cfd015e21a..683ad78f76ce 100644 --- a/Engine/Source/Runtime/Core/Private/Misc/Paths.cpp +++ b/Engine/Source/Runtime/Core/Private/Misc/Paths.cpp @@ -16,6 +16,35 @@ namespace UE4Paths_Private { auto IsSlashOrBackslash = [](TCHAR C) { return C == TEXT('/') || C == TEXT('\\'); }; auto IsNotSlashOrBackslash = [](TCHAR C) { return C != TEXT('/') && C != TEXT('\\'); }; + + FString GameSavedDir() + { + FString Result = FPaths::GameUserDir(); + + FString NonDefaultSavedDirSuffix; + if (FParse::Value(FCommandLine::Get(), TEXT("-saveddirsuffix="), NonDefaultSavedDirSuffix)) + { + for (int32 CharIdx = 0; CharIdx < NonDefaultSavedDirSuffix.Len(); ++CharIdx) + { + if (!FCString::Strchr(VALID_SAVEDDIRSUFFIX_CHARACTERS, NonDefaultSavedDirSuffix[CharIdx])) + { + NonDefaultSavedDirSuffix.RemoveAt(CharIdx, 1, false); + --CharIdx; + } + } + + if (!NonDefaultSavedDirSuffix.IsEmpty()) + { + Result += TEXT("Saved_") + NonDefaultSavedDirSuffix + TEXT("/"); + } + } + else + { + Result += TEXT("Saved/"); + } + + return Result; + } } bool FPaths::ShouldSaveToUserDir() @@ -123,7 +152,8 @@ FString FPaths::GameConfigDir() FString FPaths::GameSavedDir() { - return GameUserDir() + TEXT("Saved/"); + static FString Result = UE4Paths_Private::GameSavedDir(); + return Result; } FString FPaths::GameIntermediateDir() diff --git a/Engine/Source/Runtime/Core/Private/Serialization/Archive.cpp b/Engine/Source/Runtime/Core/Private/Serialization/Archive.cpp index 0667b658f419..16e44ee14d9e 100644 --- a/Engine/Source/Runtime/Core/Private/Serialization/Archive.cpp +++ b/Engine/Source/Runtime/Core/Private/Serialization/Archive.cpp @@ -80,6 +80,7 @@ void FArchive::Reset() ArIgnoreArchetypeRef = false; ArNoDelta = false; ArIgnoreOuterRef = false; + ArIgnoreClassGeneratedByRef = false; ArIgnoreClassRef = false; ArAllowLazyLoading = false; ArIsObjectReferenceCollector = false; diff --git a/Engine/Source/Runtime/Core/Private/Stats/Stats2.cpp b/Engine/Source/Runtime/Core/Private/Stats/Stats2.cpp index 9d4b5271b821..f734c78f2b71 100644 --- a/Engine/Source/Runtime/Core/Private/Stats/Stats2.cpp +++ b/Engine/Source/Runtime/Core/Private/Stats/Stats2.cpp @@ -1,6 +1,7 @@ // Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. #include "CorePrivatePCH.h" +#include "StatsData.h" #include "TaskGraphInterfaces.h" /*----------------------------------------------------------------------------- @@ -187,11 +188,10 @@ void FStats::AdvanceFrame( bool bDiscardCallstack, const FOnAdvanceRenderingThre // Update the seconds per cycle. SET_FLOAT_STAT( STAT_SecondsPerCycle, FPlatformTime::GetSecondsPerCycle() ); - static FStatNameAndInfo Adv( NAME_AdvanceFrame, "", "", TEXT( "" ), EStatDataType::ST_int64, true, false ); - FThreadStats::AddMessage( Adv.GetEncodedName(), EStatOperation::AdvanceFrameEventGameThread, Frame ); // we need to flush here if we aren't collecting stats to make sure the meta data is up to date + FThreadStats::AddMessage( FStatConstants::AdvanceFrame.GetEncodedName(), EStatOperation::AdvanceFrameEventGameThread, Frame ); // we need to flush here if we aren't collecting stats to make sure the meta data is up to date if( FPlatformProperties::IsServerOnly() ) { - FThreadStats::AddMessage( Adv.GetEncodedName(), EStatOperation::AdvanceFrameEventRenderThread, Frame ); // we need to flush here if we aren't collecting stats to make sure the meta data is up to date + FThreadStats::AddMessage( FStatConstants::AdvanceFrame.GetEncodedName(), EStatOperation::AdvanceFrameEventRenderThread, Frame ); // we need to flush here if we aren't collecting stats to make sure the meta data is up to date } if( AdvanceRenderingThreadStatsDelegate.IsBound() ) @@ -201,7 +201,7 @@ void FStats::AdvanceFrame( bool bDiscardCallstack, const FOnAdvanceRenderingThre else { // There is no rendering thread, so this message is sufficient to make stats happy and don't leak memory. - FThreadStats::AddMessage( Adv.GetEncodedName(), EStatOperation::AdvanceFrameEventRenderThread, Frame ); + FThreadStats::AddMessage( FStatConstants::AdvanceFrame.GetEncodedName(), EStatOperation::AdvanceFrameEventRenderThread, Frame ); } FThreadStats::ExplicitFlush( bDiscardCallstack ); diff --git a/Engine/Source/Runtime/Core/Private/Stats/StatsCommand.cpp b/Engine/Source/Runtime/Core/Private/Stats/StatsCommand.cpp index 842477d1c941..d198bce3f828 100644 --- a/Engine/Source/Runtime/Core/Private/Stats/StatsCommand.cpp +++ b/Engine/Source/Runtime/Core/Private/Stats/StatsCommand.cpp @@ -1640,7 +1640,7 @@ static void PrintStatsHelpToOutputDevice( FOutputDevice& Ar ) Ar.Log( TEXT("stat dumpave|dumpmax|dumpsum [-start | -stop | -num=30] [-ms=5.0] [-depth=maxint] - aggregate stats over multiple frames")); Ar.Log( TEXT("stat dumphitches - toggles dumping hitches")); - Ar.Log( TEXT("stat dumpevents [-ms=0.2] [-all] - toggles dumping events history for slow events, -all adds other threads besides game and render")); + Ar.Log( TEXT("stat dumpevents [-ms=0.2] [-all] - dumps events history for slow events, -all adds other threads besides game and render")); Ar.Log( TEXT("stat dumpnonframe - dumps non-frame stats, usually memory stats")); Ar.Log( TEXT("stat dumpcpu - dumps cpu stats")); diff --git a/Engine/Source/Runtime/Core/Private/Stats/StatsData.cpp b/Engine/Source/Runtime/Core/Private/Stats/StatsData.cpp index 70f637fd291b..1ed4b9f55c97 100644 --- a/Engine/Source/Runtime/Core/Private/Stats/StatsData.cpp +++ b/Engine/Source/Runtime/Core/Private/Stats/StatsData.cpp @@ -22,6 +22,7 @@ DECLARE_MEMORY_STAT( TEXT("Stat Messages"), STAT_StatMessagesMemory, STATGROUP_S const FName FStatConstants::NAME_ThreadRoot = "ThreadRoot"; const char* FStatConstants::ThreadGroupName = STAT_GROUP_TO_FStatGroup( STATGROUP_Threads )::GetGroupName(); const FName FStatConstants::NAME_ThreadGroup = FStatConstants::ThreadGroupName; +const FName FStatConstants::NAME_SecondsPerCycle = TEXT( "STAT_SecondsPerCycle" ); const FName FStatConstants::NAME_NoCategory = FName(TEXT("STATCAT_None")); const FString FStatConstants::StatsFileExtension = TEXT( ".ue4stats" ); @@ -29,9 +30,11 @@ const FString FStatConstants::StatsFileRawExtension = TEXT( ".ue4statsraw" ); const FString FStatConstants::ThreadNameMarker = TEXT( "Thread_" ); -const FName FStatConstants::NAME_EventWaitWithId = FStatNameAndInfo( GET_STATFNAME( STAT_EventWaitWithId ), true ).GetRawName(); -const FName FStatConstants::NAME_EventTriggerWithId = FStatNameAndInfo( GET_STATFNAME( STAT_EventTriggerWithId ), true ).GetRawName(); -const FName FStatConstants::NAME_NamedMarker = FStatNameAndInfo( GET_STATFNAME( STAT_NamedMarker ), true ).GetRawName(); +const FName FStatConstants::RAW_EventWaitWithId = FStatNameAndInfo( GET_STATFNAME( STAT_EventWaitWithId ), true ).GetRawName(); +const FName FStatConstants::RAW_EventTriggerWithId = FStatNameAndInfo( GET_STATFNAME( STAT_EventTriggerWithId ), true ).GetRawName(); +const FName FStatConstants::RAW_NamedMarker = FStatNameAndInfo( GET_STATFNAME( STAT_NamedMarker ), true ).GetRawName(); + +const FStatNameAndInfo FStatConstants::AdvanceFrame = FStatNameAndInfo( NAME_AdvanceFrame, "", "", TEXT( "" ), EStatDataType::ST_int64, true, false ); /*----------------------------------------------------------------------------- FRawStatStackNode @@ -1350,9 +1353,9 @@ void FStatsThreadState::GetRawStackStats(int64 TargetFrame, FRawStatStackNode& R const uint32 EventId = uint32(PacketEventIdAndCycles >> 32); const uint32 EventCycles = uint32(PacketEventIdAndCycles & MAX_uint32); - if (RawName == FStatConstants::NAME_EventWaitWithId || RawName==FStatConstants::NAME_EventTriggerWithId) + if (RawName == FStatConstants::RAW_EventWaitWithId || RawName==FStatConstants::RAW_EventTriggerWithId) { - if (FStatConstants::NAME_EventWaitWithId == RawName) + if (FStatConstants::RAW_EventWaitWithId == RawName) { TArray EventWaitStack; for (const auto& It : Stack) @@ -1374,7 +1377,7 @@ void FStatsThreadState::GetRawStackStats(int64 TargetFrame, FRawStatStackNode& R EventStats.Frame = EventStats.HasValidStacks() ? TargetFrame : 0; // Only to maintain history. } - if (FStatConstants::NAME_EventTriggerWithId == RawName) + if (FStatConstants::RAW_EventTriggerWithId == RawName) { TArray EventTriggerStack; for (const auto& It : Stack) @@ -1399,7 +1402,7 @@ void FStatsThreadState::GetRawStackStats(int64 TargetFrame, FRawStatStackNode& R EventStats.Frame = EventStats.HasValidStacks() ? TargetFrame : 0; // Only to maintain history. } } - else if (RawName == FStatConstants::NAME_NamedMarker) + else if (RawName == FStatConstants::RAW_NamedMarker) { } diff --git a/Engine/Source/Runtime/Core/Private/Windows/WindowsPlatformCrashContext.cpp b/Engine/Source/Runtime/Core/Private/Windows/WindowsPlatformCrashContext.cpp index c3de1ef81cad..fe125f751a8a 100644 --- a/Engine/Source/Runtime/Core/Private/Windows/WindowsPlatformCrashContext.cpp +++ b/Engine/Source/Runtime/Core/Private/Windows/WindowsPlatformCrashContext.cpp @@ -344,6 +344,12 @@ int32 ReportCrashUsingCrashReportClient(EXCEPTION_POINTERS* ExceptionInfo, const CrashReportClientArguments += FString( TEXT( " -AppName=" ) ) + ReportInformation.wzApplicationName; + const FString DownstreamStorage = FWindowsPlatformStackWalk::GetDownstreamStorage(); + if (!DownstreamStorage.IsEmpty()) + { + CrashReportClientArguments += FString(TEXT(" -DebugSymbols=")) + DownstreamStorage; + } + static const TCHAR CrashReportClientExeName[] = TEXT( "CrashReportClient.exe" ); FString CrashClientPath = FString( TEXT( "..\\..\\..\\Engine\\Binaries" ) ) / FPlatformProcess::GetBinariesSubdirectory() / CrashReportClientExeName; @@ -518,6 +524,10 @@ int32 ReportCrash( LPEXCEPTION_POINTERS ExceptionInfo ) GMalloc->Free( StackTrace ); } +#if !UE_BUILD_SHIPPING + FPlatformStackWalk::UploadLocalSymbols(); +#endif + return EXCEPTION_EXECUTE_HANDLER; } diff --git a/Engine/Source/Runtime/Core/Private/Windows/WindowsPlatformMemory.cpp b/Engine/Source/Runtime/Core/Private/Windows/WindowsPlatformMemory.cpp index 9eb7f5c32288..7f481b80db84 100644 --- a/Engine/Source/Runtime/Core/Private/Windows/WindowsPlatformMemory.cpp +++ b/Engine/Source/Runtime/Core/Private/Windows/WindowsPlatformMemory.cpp @@ -12,7 +12,7 @@ #endif // ENABLE_WIN_ALLOC_TRACKING #if !FORCE_ANSI_ALLOCATOR -#include "MallocBinned.h" +#include "MallocBinned2.h" #endif #include "AllowWindowsPlatformTypes.h" @@ -38,7 +38,7 @@ int WindowsAllocHook(int nAllocType, void *pvData, void FWindowsPlatformMemory::Init() { - FGenericPlatformMemory::SetupMemoryPools(); + FGenericPlatformMemory::Init(); #if PLATFORM_32BITS const int64 GB(1024*1024*1024); @@ -58,7 +58,6 @@ void FWindowsPlatformMemory::Init() MemoryConstants.TotalPhysicalGB ); #endif //PLATFORM_32BITS - UpdateStats(); DumpStats( *GLog ); } @@ -76,7 +75,7 @@ FMalloc* FWindowsPlatformMemory::BaseAllocator() #elif (WITH_EDITORONLY_DATA || IS_PROGRAM) && TBB_ALLOCATOR_ALLOWED return new FMallocTBB(); #else - return new FMallocBinned((uint32)(GetConstants().PageSize&MAX_uint32), (uint64)MAX_uint32+1); + return new FMallocBinned2((uint32)(GetConstants().PageSize&MAX_uint32), (uint64)MAX_uint32+1); #endif } @@ -101,6 +100,9 @@ FPlatformMemoryStats FWindowsPlatformMemory::GetStats() * PageSize */ + // This method is slow, do not call it too often. + // #TODO Should be executed only on the background thread. + FPlatformMemoryStats MemoryStats; // Gather platform memory stats. @@ -132,7 +134,7 @@ void FWindowsPlatformMemory::GetStatsForMallocProfiler( FGenericMemoryStats& out FPlatformMemoryStats Stats = GetStats(); // Windows specific stats. - out_Stats.Add(TEXT("Windows Specific Memory Stat"), Stats.WindowsSpecificMemoryStat ); + out_Stats.Add( GET_STATDESCRIPTION( STAT_WindowsSpecificMemoryStat ), Stats.WindowsSpecificMemoryStat ); #endif // STATS } @@ -162,30 +164,6 @@ const FPlatformMemoryConstants& FWindowsPlatformMemory::GetConstants() return MemoryConstants; } -void FWindowsPlatformMemory::UpdateStats() -{ -#if STATS - if (FThreadStats::IsCollectingData(GET_STATID(STAT_TotalPhysical))) - { - FPlatformMemoryStats MemoryStats = FPlatformMemory::GetStats(); - SET_MEMORY_STAT(STAT_TotalPhysical,MemoryStats.TotalPhysical); - SET_MEMORY_STAT(STAT_TotalVirtual,MemoryStats.TotalVirtual); - SET_MEMORY_STAT(STAT_PageSize,MemoryStats.PageSize); - SET_MEMORY_STAT(STAT_TotalPhysicalGB,MemoryStats.TotalPhysicalGB); - - SET_MEMORY_STAT(STAT_AvailablePhysical,MemoryStats.AvailablePhysical); - SET_MEMORY_STAT(STAT_AvailableVirtual,MemoryStats.AvailableVirtual); - SET_MEMORY_STAT(STAT_UsedPhysical,MemoryStats.UsedPhysical); - SET_MEMORY_STAT(STAT_PeakUsedPhysical,MemoryStats.PeakUsedPhysical); - SET_MEMORY_STAT(STAT_UsedVirtual,MemoryStats.UsedVirtual); - SET_MEMORY_STAT(STAT_PeakUsedVirtual,MemoryStats.PeakUsedVirtual); - - // Windows specific stats. - SET_MEMORY_STAT(STAT_WindowsSpecificMemoryStat,MemoryStats.WindowsSpecificMemoryStat); - } -#endif -} - void* FWindowsPlatformMemory::BinnedAllocFromOS( SIZE_T Size ) { return VirtualAlloc( NULL, Size, MEM_COMMIT, PAGE_READWRITE ); @@ -322,4 +300,11 @@ bool FWindowsPlatformMemory::UnmapNamedSharedMemoryRegion(FSharedMemoryRegion * return bAllSucceeded; } + +void FWindowsPlatformMemory::InternalUpdateStats( const FPlatformMemoryStats& MemoryStats ) +{ + // Windows specific stats. + SET_MEMORY_STAT( STAT_WindowsSpecificMemoryStat, MemoryStats.WindowsSpecificMemoryStat ); +} + #include "HideWindowsPlatformTypes.h" diff --git a/Engine/Source/Runtime/Core/Private/Windows/WindowsPlatformMisc.cpp b/Engine/Source/Runtime/Core/Private/Windows/WindowsPlatformMisc.cpp index 9b31b9f6e28d..6acd655d7440 100644 --- a/Engine/Source/Runtime/Core/Private/Windows/WindowsPlatformMisc.cpp +++ b/Engine/Source/Runtime/Core/Private/Windows/WindowsPlatformMisc.cpp @@ -29,7 +29,7 @@ #include "VarargsHelper.h" #if !FORCE_ANSI_ALLOCATOR - #include "MallocBinned.h" + #include "MallocBinned2.h" #include "AllowWindowsPlatformTypes.h" #include #include "HideWindowsPlatformTypes.h" @@ -2066,6 +2066,9 @@ void FWindowsPlatformMisc::PromptForRemoteDebugging(bool bIsEnsure) return; } + // Upload locally compiled files for remote debugging + FPlatformStackWalk::UploadLocalSymbols(); + FCString::Sprintf(GErrorRemoteDebugPromptMessage, TEXT("Have a programmer remote debug this crash?\n") TEXT("Hit NO to exit and submit error report as normal.\n") diff --git a/Engine/Source/Runtime/Core/Private/Windows/WindowsPlatformStackWalk.cpp b/Engine/Source/Runtime/Core/Private/Windows/WindowsPlatformStackWalk.cpp index e6a818c7426d..312c69b5bd20 100644 --- a/Engine/Source/Runtime/Core/Private/Windows/WindowsPlatformStackWalk.cpp +++ b/Engine/Source/Runtime/Core/Private/Windows/WindowsPlatformStackWalk.cpp @@ -4,6 +4,7 @@ #include "AllowWindowsPlatformTypes.h" #include + #include #include #include #include "HideWindowsPlatformTypes.h" @@ -20,14 +21,16 @@ static bool GStackWalkingInitialized = false; static bool GNeedToRefreshSymbols = false; +static const TCHAR* CrashReporterSettings = TEXT("/Script/UnrealEd.CrashReporterSettings"); + // NOTE: Make sure to enable Stack Frame pointers: bOmitFramePointers = false, or /Oy- // If GStackWalkingInitialized is true, traces will work anyway but will be much slower. #define USE_FAST_STACKTRACE 1 typedef bool (WINAPI *TFEnumProcesses)( uint32* lpidProcess, uint32 cb, uint32* cbNeeded); typedef bool (WINAPI *TFEnumProcessModules)(HANDLE hProcess, HMODULE *lphModule, uint32 cb, LPDWORD lpcbNeeded); -typedef uint32 (WINAPI *TFGetModuleBaseName)(HANDLE hProcess, HMODULE hModule, LPSTR lpBaseName, uint32 nSize); -typedef uint32 (WINAPI *TFGetModuleFileNameEx)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, uint32 nSize); +typedef uint32 (WINAPI *TFGetModuleBaseName)(HANDLE hProcess, HMODULE hModule, LPWSTR lpBaseName, uint32 nSize); +typedef uint32 (WINAPI *TFGetModuleFileNameEx)(HANDLE hProcess, HMODULE hModule, LPWSTR lpFilename, uint32 nSize); typedef bool (WINAPI *TFGetModuleInformation)(HANDLE hProcess, HMODULE hModule, LPMODULEINFO lpmodinfo, uint32 cb); static TFEnumProcesses FEnumProcesses; @@ -335,78 +338,183 @@ void FWindowsPlatformStackWalk::ProgramCounterToSymbolInfo( uint64 ProgramCounte } } +/** + * Get process module handle NULL-terminated list. + * On error this method returns NULL. + * + * IMPORTANT: Returned value must be deallocated by FMemory::Free(). + */ +static HMODULE* GetProcessModules(HANDLE ProcessHandle) +{ + const int32 NumModules = FWindowsPlatformStackWalk::GetProcessModuleCount(); + // Allocate start size (last element reserved for NULL value) + uint32 ResultBytes = NumModules * sizeof( HMODULE ); + HMODULE* ResultData = (HMODULE*)FMemory::Malloc( ResultBytes + sizeof( HMODULE ) ); + + uint32 BytesRequired = 0; + if (!FEnumProcessModules( ProcessHandle, ResultData, ResultBytes, (::DWORD *)&BytesRequired )) + { + FMemory::Free( ResultData ); + // Can't get process module list + return nullptr; + } + if (BytesRequired <= ResultBytes) + { + // Add end module list marker + ResultData[BytesRequired / sizeof( HMODULE )] = nullptr; + return ResultData; + } + + // No enough memory? + return nullptr; +} + +/** + * Upload locally built symbols to network symbol storage. + * + * Use case: + * Game designers use game from source (without prebuild game .dll-files). + * In this case all game .dll-files are compiled locally. + * For post-mortem debug programmers need .dll and .pdb files from designers. + */ +bool FWindowsPlatformStackWalk::UploadLocalSymbols() +{ + InitStackWalking(); + + // Upload locally compiled files to symbol storage. + FString SymbolStorage; + if (!GConfig->GetString( CrashReporterSettings, TEXT( "UploadSymbolsPath" ), SymbolStorage, GEditorPerProjectIni ) || SymbolStorage.IsEmpty()) + { + // Nothing to do. + return true; + } + // Prepare string + SymbolStorage.ReplaceInline( TEXT( "/" ), TEXT( "\\" ), ESearchCase::CaseSensitive ); + SymbolStorage = TEXT( "SRV*" ) + SymbolStorage; + + int32 ErrorCode = 0; + HANDLE ProcessHandle = GetCurrentProcess(); + + // Enumerate process modules. + HMODULE* ModuleHandlePointer = GetProcessModules( ProcessHandle ); + if (!ModuleHandlePointer) + { + ErrorCode = GetLastError(); + return false; + } + +#if WITH_EDITOR + // Get Unreal Engine Editor directory for detecting non-game editor binaries. + FString EnginePath = FPaths::ConvertRelativePathToFull( FPaths::EngineDir() ); + FPaths::MakePlatformFilename( EnginePath ); +#endif + + // Upload all locally built modules. + for (int32 ModuleIndex = 0; ModuleHandlePointer[ModuleIndex]; ModuleIndex++) + { + WCHAR ImageName[MAX_PATH] = {0}; + FGetModuleFileNameEx( ProcessHandle, ModuleHandlePointer[ModuleIndex], ImageName, MAX_PATH ); + +#if WITH_EDITOR + WCHAR RelativePath[MAX_PATH]; + // Skip binaries inside Unreal Engine Editor directory (non-game editor binaries) + if (PathRelativePathTo( RelativePath, *EnginePath, FILE_ATTRIBUTE_DIRECTORY, ImageName, 0 ) && FCString::Strncmp( RelativePath, TEXT( "..\\" ), 3 )) + { + continue; + } +#endif + + WCHAR DebugName[MAX_PATH]; + FCString::Strcpy( DebugName, ImageName ); + + if (PathRenameExtensionW( DebugName, L".pdb" )) + { + // Upload only if found .pdb file + if (PathFileExistsW( DebugName )) + { + // Upload original file + UE_LOG( LogWindows, Log, TEXT( "Uploading to symbol storage: %s" ), ImageName ); + if (!SymSrvStoreFileW( ProcessHandle, *SymbolStorage, ImageName, SYMSTOREOPT_PASS_IF_EXISTS )) + { + UE_LOG( LogWindows, Warning, TEXT( "Uploading to symbol storage failed: %s. Error: %d" ), ImageName, GetLastError() ); + } + + // Upload debug symbols + UE_LOG( LogWindows, Log, TEXT( "Uploading to symbol storage: %s" ), DebugName ); + if (!SymSrvStoreFileW( ProcessHandle, *SymbolStorage, DebugName, SYMSTOREOPT_PASS_IF_EXISTS )) + { + UE_LOG( LogWindows, Warning, TEXT( "Uploading to symbol storage failed: %s. Error: %d" ), DebugName, GetLastError() ); + } + } + } + } + return true; +} + /** * Loads modules for current process. */ -static void LoadProcessModules() +static void LoadProcessModules(const FString &RemoteStorage) { int32 ErrorCode = 0; HANDLE ProcessHandle = GetCurrentProcess(); - const int32 MAX_MOD_HANDLES = 1024; - HMODULE ModuleHandleArray[MAX_MOD_HANDLES] = {0}; - HMODULE* ModuleHandlePointer = ModuleHandleArray; - uint32 BytesRequired = 0; // Enumerate process modules. - bool bEnumProcessModulesSucceeded = FEnumProcessModules( ProcessHandle, ModuleHandleArray, sizeof(ModuleHandleArray), (::DWORD *)&BytesRequired ); - if( !bEnumProcessModulesSucceeded ) + HMODULE* ModuleHandlePointer = GetProcessModules(ProcessHandle); + if (!ModuleHandlePointer) { ErrorCode = GetLastError(); return; } - // Static array isn't sufficient so we dynamically allocate one. - bool bNeedToFreeModuleHandlePointer = false; - if( BytesRequired > sizeof( ModuleHandleArray ) ) - { - // Keep track of the fact that we need to free it again. - bNeedToFreeModuleHandlePointer = true; - ModuleHandlePointer = (HMODULE*) GMalloc->Malloc( BytesRequired ); - FEnumProcessModules( ProcessHandle, ModuleHandlePointer, sizeof(ModuleHandleArray), (::DWORD *)&BytesRequired ); - } - - // Find out how many modules we need to load modules for. - const int32 ModuleCount = BytesRequired / sizeof( HMODULE ); - // Load the modules. - for( int32 ModuleIndex = 0; ModuleIndex < ModuleCount; ModuleIndex++ ) + for( int32 ModuleIndex = 0; ModuleHandlePointer[ModuleIndex]; ModuleIndex++ ) { MODULEINFO ModuleInfo = {0}; - ANSICHAR ModuleName[FProgramCounterSymbolInfo::MAX_NAME_LENGHT] = {0}; - ANSICHAR ImageName[FProgramCounterSymbolInfo::MAX_NAME_LENGHT] = {0}; + WCHAR ModuleName[FProgramCounterSymbolInfo::MAX_NAME_LENGHT] = {0}; + WCHAR ImageName[FProgramCounterSymbolInfo::MAX_NAME_LENGHT] = {0}; #if PLATFORM_64BITS static_assert(sizeof( MODULEINFO ) == 24, "Broken alignment for 64bit Windows include."); #else static_assert(sizeof( MODULEINFO ) == 12, "Broken alignment for 32bit Windows include."); #endif - FGetModuleInformation( ProcessHandle, ModuleHandleArray[ModuleIndex], &ModuleInfo, sizeof( ModuleInfo ) ); - FGetModuleFileNameEx( ProcessHandle, ModuleHandleArray[ModuleIndex], ImageName, FProgramCounterSymbolInfo::MAX_NAME_LENGHT ); - FGetModuleBaseName( ProcessHandle, ModuleHandleArray[ModuleIndex], ModuleName, FProgramCounterSymbolInfo::MAX_NAME_LENGHT ); + FGetModuleInformation( ProcessHandle, ModuleHandlePointer[ModuleIndex], &ModuleInfo, sizeof( ModuleInfo ) ); + FGetModuleFileNameEx( ProcessHandle, ModuleHandlePointer[ModuleIndex], ImageName, FProgramCounterSymbolInfo::MAX_NAME_LENGHT ); + FGetModuleBaseName( ProcessHandle, ModuleHandlePointer[ModuleIndex], ModuleName, FProgramCounterSymbolInfo::MAX_NAME_LENGHT ); // Set the search path to find PDBs in the same folder as the DLL. - ANSICHAR SearchPath[MAX_PATH] = {0}; - ANSICHAR* FileName = NULL; - const auto Result = GetFullPathNameA( ImageName, MAX_PATH, SearchPath, &FileName ); + WCHAR SearchPath[MAX_PATH] = {0}; + WCHAR* FileName = NULL; + const auto Result = GetFullPathNameW( ImageName, MAX_PATH, SearchPath, &FileName ); - if( Result != 0 && Result < MAX_PATH ) + FString SearchPathList; + if (Result != 0 && Result < MAX_PATH) { *FileName = 0; - SymSetSearchPath(GetCurrentProcess(), SearchPath); + SearchPathList = SearchPath; + } + if (!RemoteStorage.IsEmpty()) + { + if (!SearchPathList.IsEmpty()) + { + SearchPathList.AppendChar(';'); + } + SearchPathList.Append(RemoteStorage); } + SymSetSearchPathW(ProcessHandle, *SearchPathList); + // Load module. - const DWORD64 BaseAddress = SymLoadModule64( ProcessHandle, ModuleHandleArray[ModuleIndex], ImageName, ModuleName, (DWORD64) ModuleInfo.lpBaseOfDll, (uint32) ModuleInfo.SizeOfImage ); + const DWORD64 BaseAddress = SymLoadModuleExW( ProcessHandle, ModuleHandlePointer[ModuleIndex], ImageName, ModuleName, (DWORD64) ModuleInfo.lpBaseOfDll, (uint32) ModuleInfo.SizeOfImage, NULL, 0 ); if( !BaseAddress ) { ErrorCode = GetLastError(); + UE_LOG(LogWindows, Warning, TEXT("SymLoadModuleExW. Error: %d"), GetLastError()); } } // Free the module handle pointer allocated in case the static array was insufficient. - if( bNeedToFreeModuleHandlePointer ) - { - GMalloc->Free( ModuleHandlePointer ); - } + FMemory::Free(ModuleHandlePointer); } int32 FWindowsPlatformStackWalk::GetProcessModuleCount() @@ -414,13 +522,10 @@ int32 FWindowsPlatformStackWalk::GetProcessModuleCount() FPlatformStackWalk::InitStackWalking(); HANDLE ProcessHandle = GetCurrentProcess(); - const int32 MAX_MOD_HANDLES = 1024; - HMODULE ModuleHandleArray[MAX_MOD_HANDLES] = {0}; - HMODULE* ModuleHandlePointer = ModuleHandleArray; uint32 BytesRequired = 0; // Enumerate process modules. - bool bEnumProcessModulesSucceeded = FEnumProcessModules( ProcessHandle, ModuleHandleArray, sizeof(ModuleHandleArray), (::DWORD *)&BytesRequired ); + bool bEnumProcessModulesSucceeded = FEnumProcessModules( ProcessHandle, NULL, 0, (::DWORD *)&BytesRequired ); if( !bEnumProcessModulesSucceeded ) { return 0; @@ -436,49 +541,34 @@ int32 FWindowsPlatformStackWalk::GetProcessModuleSignatures(FStackWalkModuleInfo FPlatformStackWalk::InitStackWalking(); HANDLE ProcessHandle = GetCurrentProcess(); - const int32 MAX_MOD_HANDLES = 1024; - HMODULE ModuleHandleArray[MAX_MOD_HANDLES] = {0}; - HMODULE* ModuleHandlePointer = ModuleHandleArray; - uint32 BytesRequired; // Enumerate process modules. - bool bEnumProcessModulesSucceeded = FEnumProcessModules( ProcessHandle, ModuleHandleArray, sizeof(ModuleHandleArray), (::DWORD *)&BytesRequired ); - if( !bEnumProcessModulesSucceeded ) + HMODULE* ModuleHandlePointer = GetProcessModules(ProcessHandle); + if (!ModuleHandlePointer) { return 0; } - // Static array isn't sufficient so we dynamically allocate one. - bool bNeedToFreeModuleHandlePointer = false; - if( BytesRequired > sizeof( ModuleHandleArray ) ) - { - // Keep track of the fact that we need to free it again. - bNeedToFreeModuleHandlePointer = true; - ModuleHandlePointer = (HMODULE*) FMemory::Malloc( BytesRequired ); - FEnumProcessModules( ProcessHandle, ModuleHandlePointer, sizeof(ModuleHandleArray), (::DWORD *)&BytesRequired ); - } - // Find out how many modules we need to load modules for. - const int32 ModuleCount = BytesRequired / sizeof( HMODULE ); IMAGEHLP_MODULEW64 Img = {0}; Img.SizeOfStruct = sizeof(Img); int32 SignatureIndex = 0; // Load the modules. - for( int32 ModuleIndex = 0; ModuleIndex < ModuleCount && SignatureIndex < ModuleSignaturesSize; ModuleIndex++ ) + for( int32 ModuleIndex = 0; ModuleHandlePointer[ModuleIndex] && SignatureIndex < ModuleSignaturesSize; ModuleIndex++ ) { MODULEINFO ModuleInfo = {0}; - ANSICHAR ModuleName[MAX_PATH] = {0}; - ANSICHAR ImageName[MAX_PATH] = {0}; + WCHAR ModuleName[MAX_PATH] = {0}; + WCHAR ImageName[MAX_PATH] = {0}; #if PLATFORM_64BITS static_assert(sizeof( MODULEINFO ) == 24, "Broken alignment for 64bit Windows include."); #else static_assert(sizeof( MODULEINFO ) == 12, "Broken alignment for 32bit Windows include."); #endif - FGetModuleInformation( ProcessHandle, ModuleHandleArray[ModuleIndex], &ModuleInfo,sizeof( ModuleInfo ) ); - FGetModuleFileNameEx( ProcessHandle, ModuleHandleArray[ModuleIndex], ImageName, MAX_PATH ); - FGetModuleBaseName( ProcessHandle, ModuleHandleArray[ModuleIndex], ModuleName, MAX_PATH ); + FGetModuleInformation( ProcessHandle, ModuleHandlePointer[ModuleIndex], &ModuleInfo, sizeof( ModuleInfo ) ); + FGetModuleFileNameEx( ProcessHandle, ModuleHandlePointer[ModuleIndex], ImageName, MAX_PATH ); + FGetModuleBaseName( ProcessHandle, ModuleHandlePointer[ModuleIndex], ModuleName, MAX_PATH ); // Load module. if(SymGetModuleInfoW64(ProcessHandle, (DWORD64)ModuleInfo.lpBaseOfDll, &Img)) @@ -500,10 +590,7 @@ int32 FWindowsPlatformStackWalk::GetProcessModuleSignatures(FStackWalkModuleInfo } // Free the module handle pointer allocated in case the static array was insufficient. - if( bNeedToFreeModuleHandlePointer ) - { - FMemory::Free( ModuleHandlePointer ); - } + FMemory::Free(ModuleHandlePointer); return SignatureIndex; } @@ -516,6 +603,51 @@ static void OnModulesChanged( FName ModuleThatChanged, EModuleChangeReason Reaso GNeedToRefreshSymbols = true; } +FString FWindowsPlatformStackWalk::GetDownstreamStorage() +{ + FString DownstreamStorage; + if (GConfig->GetString(CrashReporterSettings, TEXT("DownstreamStorage"), DownstreamStorage, GEditorPerProjectIni) && !DownstreamStorage.IsEmpty()) + { + DownstreamStorage = FPaths::ConvertRelativePathToFull(FPaths::RootDir(), DownstreamStorage); + } + else + { + DownstreamStorage = FPaths::ConvertRelativePathToFull(FPaths::EngineIntermediateDir(), TEXT("Symbols")); + } + FPaths::MakePlatformFilename(DownstreamStorage); + return DownstreamStorage; +} + +/** + * Create path symbol path. + * Reference: https://msdn.microsoft.com/en-us/library/ms681416%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396 + */ +static FString GetRemoteStorage(const FString& DownstreamStorage) +{ + TArray RemoteStorage; + GConfig->GetArray(CrashReporterSettings, TEXT("RemoteStorage"), RemoteStorage, GEditorPerProjectIni); + if (RemoteStorage.Num() > 0) + { + FString SymbolStorage; + for (int StorageIndex = 0; StorageIndex < RemoteStorage.Num(); ++StorageIndex) + { + if (StorageIndex > 0) + { + SymbolStorage.AppendChar(';'); + } + SymbolStorage.Append(TEXT("SRV*")); + SymbolStorage.Append(DownstreamStorage); + SymbolStorage.AppendChar('*'); + SymbolStorage.Append(RemoteStorage[StorageIndex]); + } + return SymbolStorage; + } + else + { + return FString(); + } +} + /** * Initializes the symbol engine if needed. */ @@ -538,8 +670,8 @@ bool FWindowsPlatformStackWalk::InitStackWalking() // Load dynamically linked PSAPI routines. FEnumProcesses = (TFEnumProcesses) FPlatformProcess::GetDllExport( DllHandle,TEXT("EnumProcesses")); FEnumProcessModules = (TFEnumProcessModules) FPlatformProcess::GetDllExport( DllHandle,TEXT("EnumProcessModules")); - FGetModuleFileNameEx = (TFGetModuleFileNameEx) FPlatformProcess::GetDllExport( DllHandle,TEXT("GetModuleFileNameExA")); - FGetModuleBaseName = (TFGetModuleBaseName) FPlatformProcess::GetDllExport( DllHandle,TEXT("GetModuleBaseNameA")); + FGetModuleFileNameEx = (TFGetModuleFileNameEx) FPlatformProcess::GetDllExport( DllHandle,TEXT("GetModuleFileNameExW")); + FGetModuleBaseName = (TFGetModuleBaseName) FPlatformProcess::GetDllExport( DllHandle,TEXT("GetModuleBaseNameW")); FGetModuleInformation = (TFGetModuleInformation) FPlatformProcess::GetDllExport( DllHandle,TEXT("GetModuleInformation")); // Abort if we can't look up the functions. @@ -569,8 +701,8 @@ bool FWindowsPlatformStackWalk::InitStackWalking() SymSetOptions( SymOpts ); // Initialize the symbol engine. - SymInitialize( GetCurrentProcess(), NULL, true ); - //LoadProcessModules(); + const FString RemoteStorage = GetRemoteStorage(GetDownstreamStorage()); + SymInitializeW( GetCurrentProcess(), RemoteStorage.IsEmpty() ? nullptr : *RemoteStorage, true ); GNeedToRefreshSymbols = false; GStackWalkingInitialized = true; @@ -580,7 +712,6 @@ bool FWindowsPlatformStackWalk::InitStackWalking() { // Refresh and reload symbols SymRefreshModuleList( GetCurrentProcess() ); - //LoadProcessModules(); GNeedToRefreshSymbols = false; } #endif diff --git a/Engine/Source/Runtime/Core/Public/Android/AndroidMath.h b/Engine/Source/Runtime/Core/Public/Android/AndroidMath.h index 5475f251f52d..286c9709b875 100644 --- a/Engine/Source/Runtime/Core/Public/Android/AndroidMath.h +++ b/Engine/Source/Runtime/Core/Public/Android/AndroidMath.h @@ -12,7 +12,39 @@ **/ struct FAndroidPlatformMath : public FGenericPlatformMath { - + /** + * Counts the number of leading zeros in the bit representation of the value + * + * @param Value the value to determine the number of leading zeros for + * + * @return the number of zeros before the first "on" bit + */ + static FORCEINLINE uint32 CountLeadingZeros(uint32 Value) + { + if (Value == 0) + { + return 32; + } + + return __builtin_clz(Value); + } + + /** + * Counts the number of trailing zeros in the bit representation of the value + * + * @param Value the value to determine the number of trailing zeros for + * + * @return the number of zeros after the last "on" bit + */ + static FORCEINLINE uint32 CountTrailingZeros(uint32 Value) + { + if (Value == 0) + { + return 32; + } + + return __builtin_ctz(Value); + } }; typedef FAndroidPlatformMath FPlatformMath; diff --git a/Engine/Source/Runtime/Core/Public/Async/Async.h b/Engine/Source/Runtime/Core/Public/Async/Async.h index eff3c1c7dcd2..050249f8cdb2 100644 --- a/Engine/Source/Runtime/Core/Public/Async/Async.h +++ b/Engine/Source/Runtime/Core/Public/Async/Async.h @@ -25,6 +25,12 @@ enum class EAsyncExecution /** * Template for setting a promise's value from a function. */ +template +inline void SetPromise(TPromise& Promise, TFunction Function) +{ + Promise.SetValue(Function()); +} + template inline void SetPromise(TPromise& Promise, TFunctionRef Function) { @@ -35,6 +41,13 @@ inline void SetPromise(TPromise& Promise, TFunctionRef /** * Template for setting a promise's value from a function (specialization for void results). */ +template<> +inline void SetPromise(TPromise& Promise, TFunction Function) +{ + Function(); + Promise.SetValue(); +} + template<> inline void SetPromise(TPromise& Promise, TFunctionRef Function) { diff --git a/Engine/Source/Runtime/Core/Public/Containers/Algo/FindSortedStringCaseInsensitive.h b/Engine/Source/Runtime/Core/Public/Containers/Algo/FindSortedStringCaseInsensitive.h index 5f4b83aafbd1..5db6bed3f9cd 100644 --- a/Engine/Source/Runtime/Core/Public/Containers/Algo/FindSortedStringCaseInsensitive.h +++ b/Engine/Source/Runtime/Core/Public/Containers/Algo/FindSortedStringCaseInsensitive.h @@ -14,4 +14,18 @@ namespace Algo * @return The index of the found string in the array, or -1 if the string was not found. */ CORE_API int32 FindSortedStringCaseInsensitive(const TCHAR* Str, const TCHAR* const* SortedArray, int32 ArrayCount); + + /** + * Finds a string in an array of sorted strings, by case-insensitive search, by using binary subdivision of the array. + * + * @param Str The string to look for. + * @param SortedArray The array of strings to search. The strings must be sorted lexicographically, case-insensitively. + * + * @return The index of the found string in the array, or -1 if the string was not found. + */ + template + FORCEINLINE int32 FindSortedStringCaseInsensitive(const TCHAR* Str, const TCHAR* const (&SortedArray)[ArraySize]) + { + return FindSortedStringCaseInsensitive(Str, SortedArray, ArraySize); + } } diff --git a/Engine/Source/Runtime/Core/Public/Containers/ContainerAllocationPolicies.h b/Engine/Source/Runtime/Core/Public/Containers/ContainerAllocationPolicies.h index d4f61520e350..c6274a0bfe82 100644 --- a/Engine/Source/Runtime/Core/Public/Containers/ContainerAllocationPolicies.h +++ b/Engine/Source/Runtime/Core/Public/Containers/ContainerAllocationPolicies.h @@ -4,6 +4,7 @@ #include "MemoryBase.h" #include "Misc/OutputDevice.h" +#include "Templates/MemoryOps.h" /** Used to determine the alignment of an element type. */ diff --git a/Engine/Source/Runtime/Core/Public/Containers/Set.h b/Engine/Source/Runtime/Core/Public/Containers/Set.h index e4a77735a58f..4d7f555b9824 100644 --- a/Engine/Source/Runtime/Core/Public/Containers/Set.h +++ b/Engine/Source/Runtime/Core/Public/Containers/Set.h @@ -298,7 +298,7 @@ public: if(!ConditionalRehash(ExpectedNumElements,true)) { // If the hash was already the desired size, clear the references to the elements that have now been removed. - for(int32 HashIndex = 0;HashIndex < HashSize;HashIndex++) + for (int32 HashIndex = 0, LocalHashSize = HashSize; HashIndex < LocalHashSize; ++HashIndex) { GetTypedHash(HashIndex) = FSetElementId(); } @@ -312,7 +312,7 @@ public: Elements.Reset(); // Clear the references to the elements that have now been removed. - for(int32 HashIndex = 0;HashIndex < HashSize;HashIndex++) + for (int32 HashIndex = 0, LocalHashSize = HashSize; HashIndex < LocalHashSize; ++HashIndex) { GetTypedHash(HashIndex) = FSetElementId(); } @@ -427,18 +427,23 @@ public: if (!KeyFuncs::bAllowDuplicateKeys) { // If the set doesn't allow duplicate keys, check for an existing element with the same key as the element being added. - FSetElementId ExistingId = FindId(KeyFuncs::GetSetKey(Element.Value)); - bIsAlreadyInSet = ExistingId.IsValidId(); - if (bIsAlreadyInSet) + + // Don't bother searching for a duplicate if this is the first element we're adding + if (Elements.Num() != 1) { - // If there's an existing element with the same key as the new element, replace the existing element with the new element. - MoveByRelocate(Elements[ExistingId].Value, Element.Value); + FSetElementId ExistingId = FindId(KeyFuncs::GetSetKey(Element.Value)); + bIsAlreadyInSet = ExistingId.IsValidId(); + if (bIsAlreadyInSet) + { + // If there's an existing element with the same key as the new element, replace the existing element with the new element. + MoveByRelocate(Elements[ExistingId].Value, Element.Value); - // Then remove the new element. - Elements.RemoveAtUninitialized(ElementId); + // Then remove the new element. + Elements.RemoveAtUninitialized(ElementId); - // Then point the return value at the replaced element. - ElementId = ExistingId; + // Then point the return value at the replaced element. + ElementId = ExistingId; + } } } @@ -494,7 +499,7 @@ public: */ void Remove(FSetElementId ElementId) { - if(HashSize) + if (Elements.Num()) { const auto& ElementBeingRemoved = Elements[ElementId]; @@ -522,7 +527,7 @@ public: */ FSetElementId FindId(KeyInitType Key) const { - if(HashSize) + if (Elements.Num()) { for(FSetElementId ElementId = GetTypedHash(KeyFuncs::GetKeyHash(Key)); ElementId.IsValidId(); @@ -583,7 +588,7 @@ public: { int32 NumRemovedElements = 0; - if(HashSize) + if (Elements.Num()) { FSetElementId* NextElementId = &GetTypedHash(KeyFuncs::GetKeyHash(Key)); while(NextElementId->IsValidId()) @@ -661,7 +666,7 @@ public: void Dump(FOutputDevice& Ar) { Ar.Logf( TEXT("TSet: %i elements, %i hash slots"), Elements.Num(), HashSize ); - for(int32 HashIndex = 0;HashIndex < HashSize;HashIndex++) + for (int32 HashIndex = 0, LocalHashSize = HashSize; HashIndex < LocalHashSize; ++HashIndex) { // Count the number of elements in this hash bucket. int32 NumElementsInBucket = 0; @@ -679,7 +684,7 @@ public: bool VerifyHashElementsKey(KeyInitType Key) { bool bResult=true; - if(HashSize) + if (Elements.Num()) { // iterate over all elements for the hash entry of the given key // and verify that the ids are valid @@ -699,7 +704,7 @@ public: void DumpHashElements(FOutputDevice& Ar) { - for(int32 HashIndex = 0;HashIndex < HashSize;HashIndex++) + for (int32 HashIndex = 0, LocalHashSize = HashSize; HashIndex < LocalHashSize; ++HashIndex) { Ar.Logf(TEXT(" Hash[%i]"),HashIndex); @@ -915,12 +920,13 @@ private: // Free the old hash. Hash.ResizeAllocation(0,0,sizeof(FSetElementId)); - if(HashSize) + int32 LocalHashSize = HashSize; + if (LocalHashSize) { // Allocate the new hash. - checkSlow(!(HashSize&(HashSize-1))); - Hash.ResizeAllocation(0,HashSize,sizeof(FSetElementId)); - for(int32 HashIndex = 0;HashIndex < HashSize;HashIndex++) + checkSlow(!(LocalHashSize & (HashSize - 1))); + Hash.ResizeAllocation(0, LocalHashSize, sizeof(FSetElementId)); + for (int32 HashIndex = 0; HashIndex < LocalHashSize; ++HashIndex) { GetTypedHash(HashIndex) = FSetElementId(); } diff --git a/Engine/Source/Runtime/Core/Public/Containers/UnrealString.h b/Engine/Source/Runtime/Core/Public/Containers/UnrealString.h index 6baa20fb98d0..fc1da745faca 100644 --- a/Engine/Source/Runtime/Core/Public/Containers/UnrealString.h +++ b/Engine/Source/Runtime/Core/Public/Containers/UnrealString.h @@ -1076,7 +1076,7 @@ public: return FPlatformString::Stricmp(Lhs, *Rhs) != 0; } - /** Get the length of the sting, excluding terminating character */ + /** Get the length of the string, excluding terminating character */ FORCEINLINE int32 Len() const { return Data.Num() ? Data.Num() - 1 : 0; diff --git a/Engine/Source/Runtime/Core/Public/Delegates/DelegateBase.h b/Engine/Source/Runtime/Core/Public/Delegates/DelegateBase.h index 8994ec056c29..473f20a0c140 100644 --- a/Engine/Source/Runtime/Core/Public/Delegates/DelegateBase.h +++ b/Engine/Source/Runtime/Core/Public/Delegates/DelegateBase.h @@ -4,7 +4,7 @@ #include "ContainerAllocationPolicies.h" -#ifndef WIN32 +#if !defined(_WIN32) || defined(_WIN64) // Let delegates store up to 32 bytes which are 16-byte aligned before we heap allocate typedef TAlignedBytes<16, 16> AlignedInlineDelegateType; typedef TInlineAllocator<2> DelegateAllocatorType; diff --git a/Engine/Source/Runtime/Core/Public/GenericPlatform/GenericPlatformMemory.h b/Engine/Source/Runtime/Core/Public/GenericPlatform/GenericPlatformMemory.h index b354be4d065b..62e7706f3e3b 100644 --- a/Engine/Source/Runtime/Core/Public/GenericPlatform/GenericPlatformMemory.h +++ b/Engine/Source/Runtime/Core/Public/GenericPlatform/GenericPlatformMemory.h @@ -189,6 +189,15 @@ struct CORE_API FGenericPlatformMemory /** Initializes the memory pools, should be called by the init function. */ static void SetupMemoryPools(); + + /** + * @return whether platform supports memory pools for crash reporting. + */ + static bool SupportBackupMemoryPool() + { + return false; + } + /** * @return the default allocator. */ @@ -214,9 +223,6 @@ struct CORE_API FGenericPlatformMemory */ static uint32 GetPhysicalGBRam(); - /** Called once per frame, gathers and sets all platform memory statistics into the corresponding stats. */ - static void UpdateStats(); - /** * Allocates pages from the OS. * @@ -350,4 +356,11 @@ public: * @return true if successful */ static bool UnmapNamedSharedMemoryRegion(FSharedMemoryRegion * MemoryRegion); + +protected: + friend struct FGenericStatsUpdater; + + /** Updates platform specific stats. This method is called through FGenericStatsUpdater from the task graph thread. */ + static void InternalUpdateStats( const FPlatformMemoryStats& MemoryStats ); + }; diff --git a/Engine/Source/Runtime/Core/Public/HAL/MallocBinned.h b/Engine/Source/Runtime/Core/Public/HAL/MallocBinned.h index 17810c4163f0..e8a4724ba7eb 100644 --- a/Engine/Source/Runtime/Core/Public/HAL/MallocBinned.h +++ b/Engine/Source/Runtime/Core/Public/HAL/MallocBinned.h @@ -90,103 +90,22 @@ DECLARE_MEMORY_STAT_EXTERN(TEXT("Binned Slack Current"), STAT_Binned_SlackCurren // class FMallocBinned : public FMalloc { + struct Private; + private: // Counts. enum { POOL_COUNT = 42 }; /** Maximum allocation for the pooled allocator */ - enum { EXTENED_PAGE_POOL_ALLOCATION_COUNT = 2 }; + enum { EXTENDED_PAGE_POOL_ALLOCATION_COUNT = 2 }; enum { MAX_POOLED_ALLOCATION_SIZE = 32768+1 }; - enum { PAGE_SIZE_LIMIT = 65536 }; - // BINNED_ALLOC_POOL_SIZE can be increased beyond 64k to cause binned malloc to allocate - // the small size bins in bigger chunks. If OS Allocation is slow, increasing - // this number *may* help performance but YMMV. - enum { BINNED_ALLOC_POOL_SIZE = 65536 }; // Forward declares. struct FFreeMem; struct FPoolTable; - - // Memory pool info. 32 bytes. - struct FPoolInfo - { - /** Number of allocated elements in this pool, when counts down to zero can free the entire pool. */ - uint16 Taken; // 2 - /** Index of pool. Index into MemSizeToPoolTable[]. Valid when < MAX_POOLED_ALLOCATION_SIZE, MAX_POOLED_ALLOCATION_SIZE is OsTable. - When AllocSize is 0, this is the number of pages to step back to find the base address of an allocation. See FindPoolInfoInternal() - */ - uint16 TableIndex; // 4 - /** Number of bytes allocated */ - uint32 AllocSize; // 8 - /** Pointer to first free memory in this pool or the OS Allocation Size in bytes if this allocation is not binned*/ - FFreeMem* FirstMem; // 12/16 - FPoolInfo* Next; // 16/24 - FPoolInfo** PrevLink; // 20/32 -#if PLATFORM_32BITS - /** Explicit padding for 32 bit builds */ - uint8 Padding[12]; // 32 -#endif - - void SetAllocationSizes( uint32 InBytes, UPTRINT InOsBytes, uint32 InTableIndex, uint32 SmallAllocLimt ) - { - TableIndex=InTableIndex; - AllocSize=InBytes; - if (TableIndex == SmallAllocLimt) - { - FirstMem=(FFreeMem*)InOsBytes; - } - } - - uint32 GetBytes() const - { - return AllocSize; - } - - UPTRINT GetOsBytes( uint32 InPageSize, uint32 SmallAllocLimt ) const - { - if (TableIndex == SmallAllocLimt) - { - return (UPTRINT)FirstMem; - } - else - { - return Align(AllocSize, InPageSize); - } - } - - void Link( FPoolInfo*& Before ) - { - if( Before ) - { - Before->PrevLink = &Next; - } - Next = Before; - PrevLink = &Before; - Before = this; - } - - void Unlink() - { - if( Next ) - { - Next->PrevLink = PrevLink; - } - *PrevLink = Next; - } - }; - - /** Information about a piece of free memory. 8 bytes */ - struct FFreeMem - { - /** Next or MemLastPool[], always in order by pool. */ - FFreeMem* Next; - /** Number of consecutive free blocks here, at least 1. */ - uint32 NumFreeBlocks; - }; - - /** Default alignment for binned allocator */ - enum { DEFAULT_BINNED_ALLOCATOR_ALIGNMENT = sizeof(FFreeMem) }; + struct FPoolInfo; + struct PoolHashBucket; #ifdef CACHE_FREED_OS_ALLOCS /** */ @@ -256,44 +175,6 @@ private: } }; - /** Hash table struct for retrieving allocation book keeping information */ - struct PoolHashBucket - { - UPTRINT Key; - FPoolInfo* FirstPool; - PoolHashBucket* Prev; - PoolHashBucket* Next; - - PoolHashBucket() - { - Key=0; - FirstPool=nullptr; - Prev=this; - Next=this; - } - - void Link( PoolHashBucket* After ) - { - Link(After, Prev, this); - } - - static void Link( PoolHashBucket* Node, PoolHashBucket* Before, PoolHashBucket* After ) - { - Node->Prev=Before; - Node->Next=After; - Before->Next=Node; - After->Prev=Node; - } - - void Unlink() - { - Next->Prev = Prev; - Prev->Next = Next; - Prev=this; - Next=this; - } - }; - uint64 TableAddressLimit; #ifdef USE_LOCKFREE_DELETE @@ -329,8 +210,8 @@ private: // Variables. FPoolTable PoolTable[POOL_COUNT]; FPoolTable OsTable; - FPoolTable PagePoolTable[EXTENED_PAGE_POOL_ALLOCATION_COUNT]; - FPoolTable* MemSizeToPoolTable[MAX_POOLED_ALLOCATION_SIZE+EXTENED_PAGE_POOL_ALLOCATION_COUNT]; + FPoolTable PagePoolTable[EXTENDED_PAGE_POOL_ALLOCATION_COUNT]; + FPoolTable* MemSizeToPoolTable[MAX_POOLED_ALLOCATION_SIZE+EXTENDED_PAGE_POOL_ALLOCATION_COUNT]; PoolHashBucket* HashBuckets; PoolHashBucket* HashBucketFreeList; @@ -357,828 +238,38 @@ private: double MemTime; #endif - // Implementation. - CA_NO_RETURN void OutOfMemory(uint64 Size, uint32 Alignment=0) - { - // this is expected not to return - FPlatformMemory::OnOutOfMemory(Size, Alignment); - } - - FORCEINLINE void TrackStats(FPoolTable* Table, SIZE_T Size) - { -#if STATS - // keep track of memory lost to padding - Table->TotalWaste += Table->BlockSize - Size; - Table->TotalRequests++; - Table->ActiveRequests++; - Table->MaxActiveRequests = FMath::Max(Table->MaxActiveRequests, Table->ActiveRequests); - Table->MaxRequest = Size > Table->MaxRequest ? Size : Table->MaxRequest; - Table->MinRequest = Size < Table->MinRequest ? Size : Table->MinRequest; -#endif - } - - /** - * Create a 64k page of FPoolInfo structures for tracking allocations - */ - FPoolInfo* CreateIndirect() - { - checkSlow(IndirectPoolBlockSize * sizeof(FPoolInfo) <= PageSize); - FPoolInfo* Indirect = (FPoolInfo*)FPlatformMemory::BinnedAllocFromOS(IndirectPoolBlockSize * sizeof(FPoolInfo)); - if( !Indirect ) - { - OutOfMemory(IndirectPoolBlockSize * sizeof(FPoolInfo)); - } - FMemory::Memset(Indirect, 0, IndirectPoolBlockSize*sizeof(FPoolInfo)); - BINNED_PEAK_STATCOUNTER(OsPeak, BINNED_ADD_STATCOUNTER(OsCurrent, (int64)(Align(IndirectPoolBlockSize * sizeof(FPoolInfo), PageSize)))); - BINNED_PEAK_STATCOUNTER(WastePeak, BINNED_ADD_STATCOUNTER(WasteCurrent, (int64)(Align(IndirectPoolBlockSize * sizeof(FPoolInfo), PageSize)))); - return Indirect; - } - - /** - * Gets the FPoolInfo for a memory address. If no valid info exists one is created. - * NOTE: This function requires a mutex across threads, but its is the callers responsibility to - * acquire the mutex before calling - */ - FORCEINLINE FPoolInfo* GetPoolInfo( UPTRINT Ptr ) - { - if (!HashBuckets) - { - // Init tables. - HashBuckets = (PoolHashBucket*)FPlatformMemory::BinnedAllocFromOS(Align(MaxHashBuckets*sizeof(PoolHashBucket), PageSize)); - - for (uint32 i=0; i>HashKeyShift; - UPTRINT Hash=Key&(MaxHashBuckets-1); - UPTRINT PoolIndex=((UPTRINT)Ptr >> PoolBitShift) & PoolMask; - - PoolHashBucket* collision=&HashBuckets[Hash]; - do - { - if (collision->Key==Key || !collision->FirstPool) - { - if (!collision->FirstPool) - { - collision->Key=Key; - InitializeHashBucket(collision); - CA_ASSUME(collision->FirstPool); - } - return &collision->FirstPool[PoolIndex]; - } - collision=collision->Next; - } while (collision!=&HashBuckets[Hash]); - //Create a new hash bucket entry - PoolHashBucket* NewBucket=CreateHashBucket(); - NewBucket->Key=Key; - HashBuckets[Hash].Link(NewBucket); - return &NewBucket->FirstPool[PoolIndex]; - } - - FORCEINLINE FPoolInfo* FindPoolInfo(UPTRINT Ptr1, UPTRINT& AllocationBase) - { - uint16 NextStep = 0; - UPTRINT Ptr=Ptr1&~((UPTRINT)PageSize-1); - for (uint32 i=0, n=(BINNED_ALLOC_POOL_SIZE/PageSize)+1; i= AllocationBase && Ptr1 < AllocationBase+Pool->GetBytes()); - return Pool; - } - Ptr = ((Ptr-(PageSize*NextStep))-1)&~((UPTRINT)PageSize-1); - } - AllocationBase=0; - return nullptr; - } - - FORCEINLINE FPoolInfo* FindPoolInfoInternal(UPTRINT Ptr, uint16& JumpOffset) - { - checkSlow(HashBuckets); - - uint32 Key=Ptr>>HashKeyShift; - uint32 Hash=Key&(MaxHashBuckets-1); - uint32 PoolIndex=((UPTRINT)Ptr >> PoolBitShift) & PoolMask; - JumpOffset=0; - - PoolHashBucket* collision=&HashBuckets[Hash]; - do - { - if (collision->Key==Key) - { - if (!collision->FirstPool[PoolIndex].AllocSize) - { - JumpOffset = collision->FirstPool[PoolIndex].TableIndex; - return nullptr; - } - return &collision->FirstPool[PoolIndex]; - } - collision=collision->Next; - } while (collision!=&HashBuckets[Hash]); - - return nullptr; - } - - /** - * Returns a newly created and initialized PoolHashBucket for use. - */ - FORCEINLINE PoolHashBucket* CreateHashBucket() - { - PoolHashBucket* bucket=AllocateHashBucket(); - InitializeHashBucket(bucket); - return bucket; - } - - /** - * Initializes bucket with valid parameters - * @param bucket pointer to be initialized - */ - FORCEINLINE void InitializeHashBucket(PoolHashBucket* bucket) - { - if (!bucket->FirstPool) - { - bucket->FirstPool=CreateIndirect(); - } - } - - /** - * Allocates a hash bucket from the free list of hash buckets - */ - PoolHashBucket* AllocateHashBucket() - { - if (!HashBucketFreeList) - { - HashBucketFreeList=(PoolHashBucket*)FPlatformMemory::BinnedAllocFromOS(PageSize); - BINNED_PEAK_STATCOUNTER(OsPeak, BINNED_ADD_STATCOUNTER(OsCurrent, PageSize)); - BINNED_PEAK_STATCOUNTER(WastePeak, BINNED_ADD_STATCOUNTER(WasteCurrent, PageSize)); - - for (UPTRINT i=0, n=(PageSize/sizeof(PoolHashBucket)); iLink(new (HashBucketFreeList+i) PoolHashBucket()); - } - } - PoolHashBucket* NextFree=HashBucketFreeList->Next; - PoolHashBucket* Free=HashBucketFreeList; - Free->Unlink(); - if (NextFree == Free) - { - NextFree = nullptr; - } - HashBucketFreeList=NextFree; - return Free; - } - - FPoolInfo* AllocatePoolMemory(FPoolTable* Table, uint32 PoolSize, uint16 TableIndex) - { - // Must create a new pool. - uint32 Blocks = PoolSize / Table->BlockSize; - uint32 Bytes = Blocks * Table->BlockSize; - UPTRINT OsBytes=Align(Bytes, PageSize); - checkSlow(Blocks >= 1); - checkSlow(Blocks * Table->BlockSize <= Bytes && PoolSize >= Bytes); - - // Allocate memory. - FFreeMem* Free = nullptr; - SIZE_T ActualPoolSize; //TODO: use this to reduce waste? - Free = (FFreeMem*)OSAlloc(OsBytes, ActualPoolSize); - - checkSlow(!((UPTRINT)Free & (PageSize-1))); - if( !Free ) - { - OutOfMemory(OsBytes); - } - - // Create pool in the indirect table. - FPoolInfo* Pool; - { -#ifdef USE_FINE_GRAIN_LOCKS - FScopeLock PoolInfoLock(&AccessGuard); -#endif - Pool = GetPoolInfo((UPTRINT)Free); - for (UPTRINT i=(UPTRINT)PageSize, Offset=0; iSetAllocationSizes(0, 0, Offset, BinnedOSTableIndex); - } - - - BINNED_PEAK_STATCOUNTER(OsPeak, BINNED_ADD_STATCOUNTER(OsCurrent,OsBytes)); - BINNED_PEAK_STATCOUNTER(WastePeak, BINNED_ADD_STATCOUNTER(WasteCurrent, (OsBytes - Bytes))); - } - - // Init pool. - Pool->Link( Table->FirstPool ); - Pool->SetAllocationSizes(Bytes, OsBytes, TableIndex, BinnedOSTableIndex); - Pool->Taken = 0; - Pool->FirstMem = Free; - -#if STATS - Table->NumActivePools++; - Table->MaxActivePools = FMath::Max(Table->MaxActivePools, Table->NumActivePools); -#endif - // Create first free item. - Free->NumFreeBlocks = Blocks; - Free->Next = nullptr; - - return Pool; - } - - FORCEINLINE FFreeMem* AllocateBlockFromPool(FPoolTable* Table, FPoolInfo* Pool, uint32 Alignment) - { - // Pick first available block and unlink it. - Pool->Taken++; - checkSlow(Pool->TableIndex < BinnedOSTableIndex); // if this is false, FirstMem is actually a size not a pointer - checkSlow(Pool->FirstMem); - checkSlow(Pool->FirstMem->NumFreeBlocks > 0); - checkSlow(Pool->FirstMem->NumFreeBlocks < PAGE_SIZE_LIMIT); - FFreeMem* Free = (FFreeMem*)((uint8*)Pool->FirstMem + --Pool->FirstMem->NumFreeBlocks * Table->BlockSize); - if( !Pool->FirstMem->NumFreeBlocks ) - { - Pool->FirstMem = Pool->FirstMem->Next; - if( !Pool->FirstMem ) - { - // Move to exhausted list. - Pool->Unlink(); - Pool->Link( Table->ExhaustedPool ); - } - } - BINNED_PEAK_STATCOUNTER(UsedPeak, BINNED_ADD_STATCOUNTER(UsedCurrent, Table->BlockSize)); - return Align(Free, Alignment); - } - - /** - * Releases memory back to the system. This is not protected from multi-threaded access and it's - * the callers responsibility to Lock AccessGuard before calling this. - */ - void FreeInternal( void* Ptr ) - { - MEM_TIME(MemTime -= FPlatformTime::Seconds()); - BINNED_DECREMENT_STATCOUNTER(CurrentAllocs); - - UPTRINT BasePtr; - FPoolInfo* Pool = FindPoolInfo((UPTRINT)Ptr, BasePtr); -#if PLATFORM_IOS || PLATFORM_MAC - if (Pool == NULL) - { - UE_LOG(LogMemory, Warning, TEXT("Attempting to free a pointer we didn't allocate!")); - return; - } -#endif - checkSlow(Pool); - checkSlow(Pool->GetBytes() != 0); - if( Pool->TableIndex < BinnedOSTableIndex ) - { - FPoolTable* Table=MemSizeToPoolTable[Pool->TableIndex]; -#ifdef USE_FINE_GRAIN_LOCKS - FScopeLock TableLock(&Table->CriticalSection); -#endif -#if STATS - Table->ActiveRequests--; -#endif - // If this pool was exhausted, move to available list. - if( !Pool->FirstMem ) - { - Pool->Unlink(); - Pool->Link( Table->FirstPool ); - } - - void* BaseAddress = (void*)BasePtr; - uint32 BlockSize = Table->BlockSize; - PTRINT OffsetFromBase = (PTRINT)Ptr - (PTRINT)BaseAddress; - check(OffsetFromBase >= 0); - uint32 AlignOffset = OffsetFromBase % BlockSize; - - // Patch pointer to include previously applied alignment. - Ptr = (void*)((PTRINT)Ptr - (PTRINT)AlignOffset); - - // Free a pooled allocation. - FFreeMem* Free = (FFreeMem*)Ptr; - Free->NumFreeBlocks = 1; - Free->Next = Pool->FirstMem; - Pool->FirstMem = Free; - BINNED_ADD_STATCOUNTER(UsedCurrent, -(int64)(Table->BlockSize)); - - // Free this pool. - checkSlow(Pool->Taken >= 1); - if( --Pool->Taken == 0 ) - { -#if STATS - Table->NumActivePools--; -#endif - // Free the OS memory. - SIZE_T OsBytes = Pool->GetOsBytes(PageSize, BinnedOSTableIndex); - BINNED_ADD_STATCOUNTER(OsCurrent, -(int64)(OsBytes)); - BINNED_ADD_STATCOUNTER(WasteCurrent, -(int64)(OsBytes - Pool->GetBytes())); - Pool->Unlink(); - Pool->SetAllocationSizes(0, 0, 0, BinnedOSTableIndex); - OSFree((void*)BasePtr, OsBytes); - } - } - else - { - // Free an OS allocation. - checkSlow(!((UPTRINT)Ptr & (PageSize-1))); - SIZE_T OsBytes = Pool->GetOsBytes(PageSize, BinnedOSTableIndex); - - BINNED_ADD_STATCOUNTER(UsedCurrent, -(int64)Pool->GetBytes()); - BINNED_ADD_STATCOUNTER(OsCurrent, -(int64)OsBytes); - BINNED_ADD_STATCOUNTER(WasteCurrent, -(int64)(OsBytes - Pool->GetBytes())); - OSFree((void*)BasePtr, OsBytes); - } - - MEM_TIME(MemTime += FPlatformTime::Seconds()); - } - - void PushFreeLockless(void* Ptr) - { -#ifdef USE_LOCKFREE_DELETE - PendingFreeList->Push(Ptr); -#else -#ifdef USE_COARSE_GRAIN_LOCKS - FScopeLock ScopedLock(&AccessGuard); -#endif - FreeInternal(Ptr); -#endif - } - - /** - * Clear and Process the list of frees to be deallocated. It's the callers - * responsibility to Lock AccessGuard before calling this - */ - void FlushPendingFrees() - { -#ifdef USE_LOCKFREE_DELETE - if (!PendingFreeList && !bDoneFreeListInit) - { - bDoneFreeListInit=true; - PendingFreeList = new ((void*)PendingFreeListMemory) TLockFreePointerList(); - } - // Because a lockless list and TArray calls new/malloc internally, need to guard against re-entry - if (bFlushingFrees || !PendingFreeList) - { - return; - } - bFlushingFrees=true; - PendingFreeList->PopAll(FlushedFrees); - for (uint32 i=0, n=FlushedFrees.Num(); i MAX_CACHED_OS_FREES_BYTE_LIMIT / 4) - { - FPlatformMemory::BinnedFreeToOS(Ptr); - return; - } - while (FreedPageBlocksNum && (FreedPageBlocksNum >= MAX_CACHED_OS_FREES || CachedTotal + Size > MAX_CACHED_OS_FREES_BYTE_LIMIT)) - { - //Remove the oldest one - void* FreePtr = FreedPageBlocks[0].Ptr; - CachedTotal -= FreedPageBlocks[0].ByteSize; - FreedPageBlocksNum--; - if (FreedPageBlocksNum) - { - FMemory::Memmove(&FreedPageBlocks[0], &FreedPageBlocks[1], sizeof(FFreePageBlock) * FreedPageBlocksNum); - } - FPlatformMemory::BinnedFreeToOS(FreePtr); - } - FreedPageBlocks[FreedPageBlocksNum].Ptr = Ptr; - FreedPageBlocks[FreedPageBlocksNum].ByteSize = Size; - CachedTotal += Size; - ++FreedPageBlocksNum; -#else - (void)Size; - FPlatformMemory::BinnedFreeToOS(Ptr); -#endif - } - - FORCEINLINE void* OSAlloc(SIZE_T NewSize, SIZE_T& OutActualSize) - { -#ifdef CACHE_FREED_OS_ALLOCS - { -#ifdef USE_FINE_GRAIN_LOCKS - // We want to hold the lock a little as possible so release it - // before the big call to the OS - FScopeLock MainLock(&AccessGuard); -#endif - for (uint32 i=0; i < FreedPageBlocksNum; ++i) - { - // look for exact matches first, these are aligned to the page size, so it should be quite common to hit these on small pages sizes - if (FreedPageBlocks[i].ByteSize == NewSize) - { - void* Ret=FreedPageBlocks[i].Ptr; - OutActualSize=FreedPageBlocks[i].ByteSize; - CachedTotal-=FreedPageBlocks[i].ByteSize; - if (i < FreedPageBlocksNum - 1) - { - FMemory::Memmove(&FreedPageBlocks[i], &FreedPageBlocks[i + 1], sizeof(FFreePageBlock) * (FreedPageBlocksNum - i - 1)); - } - FreedPageBlocksNum--; - return Ret; - } - }; - for (uint32 i=0; i < FreedPageBlocksNum; ++i) - { - // is it possible (and worth i.e. <25% overhead) to use this block - if (FreedPageBlocks[i].ByteSize >= NewSize && FreedPageBlocks[i].ByteSize * 3 <= NewSize * 4) - { - void* Ret=FreedPageBlocks[i].Ptr; - OutActualSize=FreedPageBlocks[i].ByteSize; - CachedTotal-=FreedPageBlocks[i].ByteSize; - if (i < FreedPageBlocksNum - 1) - { - FMemory::Memmove(&FreedPageBlocks[i], &FreedPageBlocks[i + 1], sizeof(FFreePageBlock) * (FreedPageBlocksNum - i - 1)); - } - FreedPageBlocksNum--; - return Ret; - } - }; - } - OutActualSize=NewSize; - void* Ptr = FPlatformMemory::BinnedAllocFromOS(NewSize); - if (!Ptr) - { - //Are we holding on to much mem? Release it all. - FlushAllocCache(); - Ptr = FPlatformMemory::BinnedAllocFromOS(NewSize); - } - return Ptr; -#else - (void)OutActualSize; - return FPlatformMemory::BinnedAllocFromOS(NewSize); -#endif - } - -#ifdef CACHE_FREED_OS_ALLOCS - void FlushAllocCache() - { -#ifdef USE_FINE_GRAIN_LOCKS - FScopeLock MainLock(&AccessGuard); -#endif - for (int i=0, n=FreedPageBlocksNum; i PageSize); // Check to catch 32 bit overflow in AddressLimit + // Malloc will adjust its internal structures to make lookups for memory allocations O(1) for this range. + // It is ok to go outside this range, lookups will just be a little slower + FMallocBinned(uint32 InPageSize, uint64 AddressLimit); - /** Shift to get the reference from the indirect tables */ - PoolBitShift = FPlatformMath::CeilLogTwo(PageSize); - IndirectPoolBitShift = FPlatformMath::CeilLogTwo(PageSize/sizeof(FPoolInfo)); - IndirectPoolBlockSize = PageSize/sizeof(FPoolInfo); - - MaxHashBuckets = AddressLimit >> (IndirectPoolBitShift+PoolBitShift); - MaxHashBucketBits = FPlatformMath::CeilLogTwo(MaxHashBuckets); - MaxHashBucketWaste = (MaxHashBuckets*sizeof(PoolHashBucket))/1024; - MaxBookKeepingOverhead = ((AddressLimit/PageSize)*sizeof(PoolHashBucket))/(1024*1024); - /** - * Shift required to get required hash table key. - */ - HashKeyShift = PoolBitShift+IndirectPoolBitShift; - /** Used to mask off the bits that have been used to lookup the indirect table */ - PoolMask = ( ( 1ull << ( HashKeyShift - PoolBitShift ) ) - 1 ); - BinnedSizeLimit = PAGE_SIZE_LIMIT/2; - BinnedOSTableIndex = BinnedSizeLimit+EXTENED_PAGE_POOL_ALLOCATION_COUNT; - - check((BinnedSizeLimit & (BinnedSizeLimit-1)) == 0); - - - // Init tables. - OsTable.FirstPool = nullptr; - OsTable.ExhaustedPool = nullptr; - OsTable.BlockSize = 0; - - /** The following options are not valid for page sizes less than 64k. They are here to reduce waste*/ - PagePoolTable[0].FirstPool = nullptr; - PagePoolTable[0].ExhaustedPool = nullptr; - PagePoolTable[0].BlockSize = PageSize == PAGE_SIZE_LIMIT ? BinnedSizeLimit+(BinnedSizeLimit/2) : 0; - - PagePoolTable[1].FirstPool = nullptr; - PagePoolTable[1].ExhaustedPool = nullptr; - PagePoolTable[1].BlockSize = PageSize == PAGE_SIZE_LIMIT ? PageSize+BinnedSizeLimit : 0; - - // Block sizes are based around getting the maximum amount of allocations per pool, with as little alignment waste as possible. - // Block sizes should be close to even divisors of the POOL_SIZE, and well distributed. They must be 16-byte aligned as well. - static const uint32 BlockSizes[POOL_COUNT] = - { - 8, 16, 32, 48, 64, 80, 96, 112, - 128, 160, 192, 224, 256, 288, 320, 384, - 448, 512, 576, 640, 704, 768, 896, 1024, - 1168, 1360, 1632, 2048, 2336, 2720, 3264, 4096, - 4672, 5456, 6544, 8192, 9360, 10912, 13104, 16384, - 21840, 32768 - }; - - for( uint32 i = 0; i < POOL_COUNT; i++ ) - { - PoolTable[i].FirstPool = nullptr; - PoolTable[i].ExhaustedPool = nullptr; - PoolTable[i].BlockSize = BlockSizes[i]; -#if STATS - PoolTable[i].MinRequest = PoolTable[i].BlockSize; -#endif - } - - for( uint32 i=0; i(Alignment, DEFAULT_BINNED_ALLOCATOR_ALIGNMENT); - SIZE_T SpareBytesCount = FMath::Min(DEFAULT_BINNED_ALLOCATOR_ALIGNMENT, Size); - Size = FMath::Max(PoolTable[0].BlockSize, Size + (Alignment - SpareBytesCount)); - MEM_TIME(MemTime -= FPlatformTime::Seconds()); - - BINNED_INCREMENT_STATCOUNTER(CurrentAllocs); - BINNED_INCREMENT_STATCOUNTER(TotalAllocs); - - FFreeMem* Free; - if( Size < BinnedSizeLimit ) - { - // Allocate from pool. - FPoolTable* Table = MemSizeToPoolTable[Size]; -#ifdef USE_FINE_GRAIN_LOCKS - FScopeLock TableLock(&Table->CriticalSection); -#endif - checkSlow(Size <= Table->BlockSize); - - TrackStats(Table, Size); - - FPoolInfo* Pool = Table->FirstPool; - if( !Pool ) - { - Pool = AllocatePoolMemory(Table, BINNED_ALLOC_POOL_SIZE/*PageSize*/, Size); - } - - Free = AllocateBlockFromPool(Table, Pool, Alignment); - } - else if ( ((Size >= BinnedSizeLimit && Size <= PagePoolTable[0].BlockSize) || - (Size > PageSize && Size <= PagePoolTable[1].BlockSize))) - { - // Bucket in a pool of 3*PageSize or 6*PageSize - uint32 BinType = Size < PageSize ? 0 : 1; - uint32 PageCount = 3*BinType + 3; - FPoolTable* Table = &PagePoolTable[BinType]; -#ifdef USE_FINE_GRAIN_LOCKS - FScopeLock TableLock(&Table->CriticalSection); -#endif - checkSlow(Size <= Table->BlockSize); - - TrackStats(Table, Size); - - FPoolInfo* Pool = Table->FirstPool; - if( !Pool ) - { - Pool = AllocatePoolMemory(Table, PageCount*PageSize, BinnedSizeLimit+BinType); - } - - Free = AllocateBlockFromPool(Table, Pool, Alignment); - } - else - { - // Use OS for large allocations. - UPTRINT AlignedSize = Align(Size,PageSize); - SIZE_T ActualPoolSize; //TODO: use this to reduce waste? - Free = (FFreeMem*)OSAlloc(AlignedSize, ActualPoolSize); - if( !Free ) - { - OutOfMemory(AlignedSize); - } - - void* AlignedFree = Align(Free, Alignment); - - // Create indirect. - FPoolInfo* Pool; - { -#ifdef USE_FINE_GRAIN_LOCKS - FScopeLock PoolInfoLock(&AccessGuard); -#endif - Pool = GetPoolInfo((UPTRINT)Free); - - if ((UPTRINT)Free != ((UPTRINT)AlignedFree & ~((UPTRINT)PageSize - 1))) - { - // Mark the FPoolInfo for AlignedFree to jump back to the FPoolInfo for ptr. - for (UPTRINT i = (UPTRINT)PageSize, Offset = 0; i < AlignedSize; i += PageSize, ++Offset) - { - FPoolInfo* TrailingPool = GetPoolInfo(((UPTRINT)Free) + i); - check(TrailingPool); - //Set trailing pools to point back to first pool - TrailingPool->SetAllocationSizes(0, 0, Offset, BinnedOSTableIndex); - } - } - } - Free = (FFreeMem*)AlignedFree; - Pool->SetAllocationSizes(Size, AlignedSize, BinnedOSTableIndex, BinnedOSTableIndex); - BINNED_PEAK_STATCOUNTER(OsPeak, BINNED_ADD_STATCOUNTER(OsCurrent, AlignedSize)); - BINNED_PEAK_STATCOUNTER(UsedPeak, BINNED_ADD_STATCOUNTER(UsedCurrent, Size)); - BINNED_PEAK_STATCOUNTER(WastePeak, BINNED_ADD_STATCOUNTER(WasteCurrent, (int64)(AlignedSize - Size))); - } - - MEM_TIME(MemTime += FPlatformTime::Seconds()); - return Free; - } + virtual void* Malloc( SIZE_T Size, uint32 Alignment ) override; /** * Realloc */ - virtual void* Realloc( void* Ptr, SIZE_T NewSize, uint32 Alignment ) override - { - // Handle DEFAULT_ALIGNMENT for binned allocator. - if (Alignment == DEFAULT_ALIGNMENT) - { - Alignment = DEFAULT_BINNED_ALLOCATOR_ALIGNMENT; - } + virtual void* Realloc( void* Ptr, SIZE_T NewSize, uint32 Alignment ) override; - Alignment = FMath::Max(Alignment, DEFAULT_BINNED_ALLOCATOR_ALIGNMENT); - const uint32 NewSizeUnmodified = NewSize; - SIZE_T SpareBytesCount = FMath::Min(DEFAULT_BINNED_ALLOCATOR_ALIGNMENT, NewSize); - if (NewSize) - { - NewSize = FMath::Max(PoolTable[0].BlockSize, NewSize + (Alignment - SpareBytesCount)); - } - MEM_TIME(MemTime -= FPlatformTime::Seconds()); - UPTRINT BasePtr; - void* NewPtr = Ptr; - if( Ptr && NewSize ) - { - FPoolInfo* Pool = FindPoolInfo((UPTRINT)Ptr, BasePtr); - - if( Pool->TableIndex < BinnedOSTableIndex ) - { - // Allocated from pool, so grow or shrink if necessary. - check(Pool->TableIndex > 0); // it isn't possible to allocate a size of 0, Malloc will increase the size to DEFAULT_BINNED_ALLOCATOR_ALIGNMENT - if (NewSizeUnmodified > MemSizeToPoolTable[Pool->TableIndex]->BlockSize || NewSizeUnmodified <= MemSizeToPoolTable[Pool->TableIndex - 1]->BlockSize) - { - NewPtr = Malloc(NewSizeUnmodified, Alignment); - FMemory::Memcpy(NewPtr, Ptr, FMath::Min(NewSizeUnmodified, MemSizeToPoolTable[Pool->TableIndex]->BlockSize - (Alignment - SpareBytesCount))); - Free( Ptr ); - } - else if (((UPTRINT)Ptr & (UPTRINT)(Alignment - 1)) != 0) - { - NewPtr = Align(Ptr, Alignment); - FMemory::Memmove(NewPtr, Ptr, NewSize); - } - } - else - { - // Allocated from OS. - if( NewSize > Pool->GetOsBytes(PageSize, BinnedOSTableIndex) || NewSize * 3 < Pool->GetOsBytes(PageSize, BinnedOSTableIndex) * 2 ) - { - // Grow or shrink. - NewPtr = Malloc(NewSizeUnmodified, Alignment); - FMemory::Memcpy(NewPtr, Ptr, FMath::Min(NewSizeUnmodified, Pool->GetBytes())); - Free( Ptr ); - } - else - { -//need a lock to cover the SetAllocationSizes() -#ifdef USE_FINE_GRAIN_LOCKS - FScopeLock PoolInfoLock(&AccessGuard); -#endif - int32 UsedChange = (NewSize - Pool->GetBytes()); - - // Keep as-is, reallocation isn't worth the overhead. - BINNED_ADD_STATCOUNTER(UsedCurrent, UsedChange); - BINNED_PEAK_STATCOUNTER(UsedPeak, UsedCurrent); - BINNED_ADD_STATCOUNTER(WasteCurrent, (Pool->GetBytes() - NewSize)); - Pool->SetAllocationSizes(NewSizeUnmodified, Pool->GetOsBytes(PageSize, BinnedOSTableIndex), BinnedOSTableIndex, BinnedOSTableIndex); - } - } - } - else if( Ptr == nullptr ) - { - NewPtr = Malloc(NewSizeUnmodified, Alignment); - } - else - { - Free( Ptr ); - NewPtr = nullptr; - } - - MEM_TIME(MemTime += FPlatformTime::Seconds()); - return NewPtr; - } - /** * Free */ - virtual void Free( void* Ptr ) override - { - if( !Ptr ) - { - return; - } - - PushFreeLockless(Ptr); - } + virtual void Free( void* Ptr ) override; /** * If possible determine the size of the memory allocated at the given address @@ -1187,98 +278,15 @@ public: * @param SizeOut - If possible, this value is set to the size of the passed in pointer * @return true if succeeded */ - virtual bool GetAllocationSize(void *Original, SIZE_T &SizeOut) override - { - if (!Original) - { - return false; - } - UPTRINT BasePtr; - FPoolInfo* Pool = FindPoolInfo((UPTRINT)Original, BasePtr); - SizeOut = Pool->TableIndex < BinnedOSTableIndex ? MemSizeToPoolTable[Pool->TableIndex]->BlockSize : Pool->GetBytes(); - return true; - } + virtual bool GetAllocationSize(void *Original, SIZE_T &SizeOut) override; /** * Validates the allocator's heap */ - virtual bool ValidateHeap() override - { -#ifdef USE_COARSE_GRAIN_LOCKS - FScopeLock ScopedLock(&AccessGuard); -#endif - for( int32 i = 0; i < POOL_COUNT; i++ ) - { - FPoolTable* Table = &PoolTable[i]; -#ifdef USE_FINE_GRAIN_LOCKS - FScopeLock TableLock(&Table->CriticalSection); -#endif - for( FPoolInfo** PoolPtr = &Table->FirstPool; *PoolPtr; PoolPtr = &(*PoolPtr)->Next ) - { - FPoolInfo* Pool = *PoolPtr; - check(Pool->PrevLink == PoolPtr); - check(Pool->FirstMem); - for( FFreeMem* Free = Pool->FirstMem; Free; Free = Free->Next ) - { - check(Free->NumFreeBlocks > 0); - } - } - for( FPoolInfo** PoolPtr = &Table->ExhaustedPool; *PoolPtr; PoolPtr = &(*PoolPtr)->Next ) - { - FPoolInfo* Pool = *PoolPtr; - check(Pool->PrevLink == PoolPtr); - check(!Pool->FirstMem); - } - } - - return( true ); - } - + virtual bool ValidateHeap() override; /** Called once per frame, gathers and sets all memory allocator statistics into the corresponding stats. MUST BE THREAD SAFE. */ - virtual void UpdateStats() override - { - FMalloc::UpdateStats(); -#if STATS - SIZE_T LocalOsCurrent = 0; - SIZE_T LocalOsPeak = 0; - SIZE_T LocalWasteCurrent = 0; - SIZE_T LocalWastePeak = 0; - SIZE_T LocalUsedCurrent = 0; - SIZE_T LocalUsedPeak = 0; - SIZE_T LocalCurrentAllocs = 0; - SIZE_T LocalTotalAllocs = 0; - SIZE_T LocalSlackCurrent = 0; - - { -#ifdef USE_INTERNAL_LOCKS - FScopeLock ScopedLock( &AccessGuard ); -#endif - UpdateSlackStat(); - - // Copy memory stats. - LocalOsCurrent = OsCurrent; - LocalOsPeak = OsPeak; - LocalWasteCurrent = WasteCurrent; - LocalWastePeak = WastePeak; - LocalUsedCurrent = UsedCurrent; - LocalUsedPeak = UsedPeak; - LocalCurrentAllocs = CurrentAllocs; - LocalTotalAllocs = TotalAllocs; - LocalSlackCurrent = SlackCurrent; - } - - SET_MEMORY_STAT( STAT_Binned_OsCurrent, LocalOsCurrent ); - SET_MEMORY_STAT( STAT_Binned_OsPeak, LocalOsPeak ); - SET_MEMORY_STAT( STAT_Binned_WasteCurrent, LocalWasteCurrent ); - SET_MEMORY_STAT( STAT_Binned_WastePeak, LocalWastePeak ); - SET_MEMORY_STAT( STAT_Binned_UsedCurrent, LocalUsedCurrent ); - SET_MEMORY_STAT( STAT_Binned_UsedPeak, LocalUsedPeak ); - SET_DWORD_STAT( STAT_Binned_CurrentAllocs, LocalCurrentAllocs ); - SET_DWORD_STAT( STAT_Binned_TotalAllocs, LocalTotalAllocs ); - SET_MEMORY_STAT( STAT_Binned_SlackCurrent, LocalSlackCurrent ); -#endif - } + virtual void UpdateStats() override; /** Writes allocator stats from the last update into the specified destination. */ virtual void GetAllocatorStats( FGenericMemoryStats& out_Stats ) override; @@ -1288,125 +296,7 @@ public: * * @param Ar [in] Output device */ - virtual void DumpAllocatorStats( class FOutputDevice& Ar ) override - { - FBufferedOutputDevice BufferedOutput; - { -#ifdef USE_COARSE_GRAIN_LOCKS - FScopeLock ScopedLock( &AccessGuard ); -#endif - ValidateHeap(); -#if STATS - UpdateSlackStat(); -#if !NO_LOGGING - // This is all of the memory including stuff too big for the pools - BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "Allocator Stats for %s:" ), GetDescriptiveName() ); - // Waste is the total overhead of the memory system - BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "Current Memory %.2f MB used, plus %.2f MB waste" ), UsedCurrent / (1024.0f * 1024.0f), (OsCurrent - UsedCurrent) / (1024.0f * 1024.0f) ); - BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "Peak Memory %.2f MB used, plus %.2f MB waste" ), UsedPeak / (1024.0f * 1024.0f), (OsPeak - UsedPeak) / (1024.0f * 1024.0f) ); + virtual void DumpAllocatorStats( class FOutputDevice& Ar ) override; - BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "Current OS Memory %.2f MB, peak %.2f MB" ), OsCurrent / (1024.0f * 1024.0f), OsPeak / (1024.0f * 1024.0f) ); - BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "Current Waste %.2f MB, peak %.2f MB" ), WasteCurrent / (1024.0f * 1024.0f), WastePeak / (1024.0f * 1024.0f) ); - BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "Current Used %.2f MB, peak %.2f MB" ), UsedCurrent / (1024.0f * 1024.0f), UsedPeak / (1024.0f * 1024.0f) ); - BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "Current Slack %.2f MB" ), SlackCurrent / (1024.0f * 1024.0f) ); - - BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "Allocs % 6i Current / % 6i Total" ), CurrentAllocs, TotalAllocs ); - MEM_TIME( BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "Seconds % 5.3f" ), MemTime ) ); - MEM_TIME( BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "MSec/Allc % 5.5f" ), 1000.0 * MemTime / MemAllocs ) ); - - // This is the memory tracked inside individual allocation pools - BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "" ) ); - BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "Block Size Num Pools Max Pools Cur Allocs Total Allocs Min Req Max Req Mem Used Mem Slack Mem Waste Efficiency" ) ); - BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "---------- --------- --------- ---------- ------------ ------- ------- -------- --------- --------- ----------" ) ); - - uint32 TotalMemory = 0; - uint32 TotalWaste = 0; - uint32 TotalActiveRequests = 0; - uint32 TotalTotalRequests = 0; - uint32 TotalPools = 0; - uint32 TotalSlack = 0; - - FPoolTable* Table = nullptr; - for( int32 i = 0; i < BinnedSizeLimit + EXTENED_PAGE_POOL_ALLOCATION_COUNT; i++ ) - { - if( Table == MemSizeToPoolTable[i] || MemSizeToPoolTable[i]->BlockSize == 0 ) - continue; - - Table = MemSizeToPoolTable[i]; - -#ifdef USE_FINE_GRAIN_LOCKS - FScopeLock TableLock(&Table->CriticalSection); -#endif - - uint32 TableAllocSize = (Table->BlockSize > BinnedSizeLimit ? (((3 * (i - BinnedSizeLimit)) + 3)*BINNED_ALLOC_POOL_SIZE) : BINNED_ALLOC_POOL_SIZE); - // The amount of memory allocated from the OS - uint32 MemAllocated = (Table->NumActivePools * TableAllocSize) / 1024; - // Amount of memory actually in use by allocations - uint32 MemUsed = (Table->BlockSize * Table->ActiveRequests) / 1024; - // Wasted memory due to pool size alignment - uint32 PoolMemWaste = Table->NumActivePools * (TableAllocSize - ((TableAllocSize / Table->BlockSize) * Table->BlockSize)) / 1024; - // Wasted memory due to individual allocation alignment. This is an estimate. - uint32 MemWaste = (uint32)(((double)Table->TotalWaste / (double)Table->TotalRequests) * (double)Table->ActiveRequests) / 1024 + PoolMemWaste; - // Memory that is reserved in active pools and ready for future use - uint32 MemSlack = MemAllocated - MemUsed - PoolMemWaste; - - BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "% 10i % 9i % 9i % 10i % 12i % 7i % 7i % 7iK % 8iK % 8iK % 9.2f%%" ), - Table->BlockSize, - Table->NumActivePools, - Table->MaxActivePools, - Table->ActiveRequests, - (uint32)Table->TotalRequests, - Table->MinRequest, - Table->MaxRequest, - MemUsed, - MemSlack, - MemWaste, - MemAllocated ? 100.0f * (MemAllocated - MemWaste) / MemAllocated : 100.0f ); - - TotalMemory += MemAllocated; - TotalWaste += MemWaste; - TotalSlack += MemSlack; - TotalActiveRequests += Table->ActiveRequests; - TotalTotalRequests += Table->TotalRequests; - TotalPools += Table->NumActivePools; - } - - BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "" ) ); - BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "%iK allocated in pools (with %iK slack and %iK waste). Efficiency %.2f%%" ), TotalMemory, TotalSlack, TotalWaste, TotalMemory ? 100.0f * (TotalMemory - TotalWaste) / TotalMemory : 100.0f ); - BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "Allocations %i Current / %i Total (in %i pools)" ), TotalActiveRequests, TotalTotalRequests, TotalPools ); - BufferedOutput.CategorizedLogf( LogMemory.GetCategoryName(), ELogVerbosity::Log, TEXT( "" ) ); -#endif -#endif - } - - BufferedOutput.RedirectTo( Ar ); - } - - virtual const TCHAR* GetDescriptiveName() override { return TEXT("binned"); } - -protected: - - void UpdateSlackStat() - { -#if STATS - SIZE_T LocalWaste = WasteCurrent; - double Waste = 0.0; - for( int32 PoolIndex = 0; PoolIndex < POOL_COUNT; PoolIndex++ ) - { - Waste += ( ( double )PoolTable[PoolIndex].TotalWaste / ( double )PoolTable[PoolIndex].TotalRequests ) * ( double )PoolTable[PoolIndex].ActiveRequests; - Waste += PoolTable[PoolIndex].NumActivePools * ( BINNED_ALLOC_POOL_SIZE - ( ( BINNED_ALLOC_POOL_SIZE / PoolTable[PoolIndex].BlockSize ) * PoolTable[PoolIndex].BlockSize ) ); - } - LocalWaste += ( uint32 )Waste; - SlackCurrent = OsCurrent - LocalWaste - UsedCurrent; -#endif // STATS - } - - /////////////////////////////////// - //// API platforms must implement - /////////////////////////////////// - /** - * @return the page size the OS uses - * NOTE: This MUST be implemented on each platform that uses this! - */ - uint32 BinnedGetPageSize(); + virtual const TCHAR* GetDescriptiveName() override; }; diff --git a/Engine/Source/Runtime/Core/Public/HAL/MallocBinned2.h b/Engine/Source/Runtime/Core/Public/HAL/MallocBinned2.h new file mode 100644 index 000000000000..97714532ea86 --- /dev/null +++ b/Engine/Source/Runtime/Core/Public/HAL/MallocBinned2.h @@ -0,0 +1,223 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#pragma once + +#define BINNED2_CACHE_FREED_OS_ALLOCS + +#if defined BINNED2_CACHE_FREED_OS_ALLOCS + #define BINNED2_MAX_CACHED_OS_FREES (64) + #if PLATFORM_64BITS + #define BINNED2_MAX_CACHED_OS_FREES_BYTE_LIMIT (64*1024*1024) + #else + #define BINNED2_MAX_CACHED_OS_FREES_BYTE_LIMIT (16*1024*1024) + #endif +#endif + +#if STATS +# if PLATFORM_64BITS +# define BINNED2_STAT volatile int64 +# else +# define BINNED2_STAT volatile int32 +# endif +#endif + +// +// Optimized virtual memory allocator. +// +class FMallocBinned2 : public FMalloc +{ + struct Private; + +private: + + // Counts. + enum { POOL_COUNT = 42 }; + + /** Maximum allocation for the pooled allocator */ + enum { EXTENDED_PAGE_POOL_ALLOCATION_COUNT = 2 }; + enum { MAX_POOLED_ALLOCATION_SIZE = 32768+1 }; + + // Forward declares. + struct FFreeMem; + struct FPoolTable; + struct FPoolInfo; + struct PoolHashBucket; + +#ifdef BINNED2_CACHE_FREED_OS_ALLOCS + /** */ + struct FFreePageBlock + { + void* Ptr; + SIZE_T ByteSize; + + FFreePageBlock() + { + Ptr = nullptr; + ByteSize = 0; + } + }; +#endif + + /** Pool table. */ + struct FPoolTable + { + FPoolInfo* FirstPool; + FPoolInfo* ExhaustedPool; + uint32 BlockSize; +#if STATS + /** Number of currently active pools */ + uint32 NumActivePools; + + /** Largest number of pools simultaneously active */ + uint32 MaxActivePools; + + /** Number of requests currently active */ + uint32 ActiveRequests; + + /** High watermark of requests simultaneously active */ + uint32 MaxActiveRequests; + + /** Minimum request size (in bytes) */ + uint32 MinRequest; + + /** Maximum request size (in bytes) */ + uint32 MaxRequest; + + /** Total number of requests ever */ + uint64 TotalRequests; + + /** Total waste from all allocs in this table */ + uint64 TotalWaste; +#endif + FPoolTable() + : FirstPool(nullptr) + , ExhaustedPool(nullptr) + , BlockSize(0) +#if STATS + , NumActivePools(0) + , MaxActivePools(0) + , ActiveRequests(0) + , MaxActiveRequests(0) + , MinRequest(0) + , MaxRequest(0) + , TotalRequests(0) + , TotalWaste(0) +#endif + { + + } + }; + + uint64 TableAddressLimit; + + // PageSize dependent constants + uint64 MaxHashBuckets; + uint64 MaxHashBucketBits; + uint64 MaxHashBucketWaste; + uint64 MaxBookKeepingOverhead; + /** Shift to get the reference from the indirect tables */ + uint64 PoolBitShift; + uint64 IndirectPoolBitShift; + uint64 IndirectPoolBlockSize; + /** Shift required to get required hash table key. */ + uint64 HashKeyShift; + /** Used to mask off the bits that have been used to lookup the indirect table */ + uint64 PoolMask; + uint64 BinnedSizeLimit; + uint64 BinnedOSTableIndex; + + // Variables. + FPoolTable PoolTable[POOL_COUNT]; + FPoolTable OsTable; + FPoolTable PagePoolTable[EXTENDED_PAGE_POOL_ALLOCATION_COUNT]; + FPoolTable* MemSizeToPoolTable[MAX_POOLED_ALLOCATION_SIZE+EXTENDED_PAGE_POOL_ALLOCATION_COUNT]; + + PoolHashBucket* HashBuckets; + PoolHashBucket* HashBucketFreeList; + + uint32 PageSize; + +#ifdef BINNED2_CACHE_FREED_OS_ALLOCS + FFreePageBlock FreedPageBlocks[BINNED2_MAX_CACHED_OS_FREES]; + uint32 FreedPageBlocksNum; + uint32 CachedTotal; +#endif + +#if STATS + BINNED2_STAT OsCurrent; + BINNED2_STAT OsPeak; + BINNED2_STAT WasteCurrent; + BINNED2_STAT WastePeak; + BINNED2_STAT UsedCurrent; + BINNED2_STAT UsedPeak; + BINNED2_STAT CurrentAllocs; + BINNED2_STAT TotalAllocs; + /** OsCurrent - WasteCurrent - UsedCurrent. */ + BINNED2_STAT SlackCurrent; + double MemTime; +#endif + +public: + // FMalloc interface. + // InPageSize - First parameter is page size, all allocs from BinnedAllocFromOS() MUST be aligned to this size + // AddressLimit - Second parameter is estimate of the range of addresses expected to be returns by BinnedAllocFromOS(). Binned + // Malloc will adjust its internal structures to make lookups for memory allocations O(1) for this range. + // It is ok to go outside this range, lookups will just be a little slower + FMallocBinned2(uint32 InPageSize, uint64 AddressLimit); + + virtual void InitializeStatsMetadata() override; + + virtual ~FMallocBinned2(); + + /** + * Returns if the allocator is guaranteed to be thread-safe and therefore + * doesn't need a unnecessary thread-safety wrapper around it. + */ + virtual bool IsInternallyThreadSafe() const override; + + /** + * Malloc + */ + virtual void* Malloc( SIZE_T Size, uint32 Alignment ) override; + + /** + * Realloc + */ + virtual void* Realloc( void* Ptr, SIZE_T NewSize, uint32 Alignment ) override; + + /** + * Free + */ + virtual void Free( void* Ptr ) override; + + /** + * If possible determine the size of the memory allocated at the given address + * + * @param Original - Pointer to memory we are checking the size of + * @param SizeOut - If possible, this value is set to the size of the passed in pointer + * @return true if succeeded + */ + virtual bool GetAllocationSize(void *Original, SIZE_T &SizeOut) override; + + /** + * Validates the allocator's heap + */ + virtual bool ValidateHeap() override; + + /** Called once per frame, gathers and sets all memory allocator statistics into the corresponding stats. MUST BE THREAD SAFE. */ + virtual void UpdateStats() override; + + /** Writes allocator stats from the last update into the specified destination. */ + virtual void GetAllocatorStats( FGenericMemoryStats& out_Stats ) override; + + /** + * Dumps allocator stats to an output device. Subclasses should override to add additional info + * + * @param Ar [in] Output device + */ + virtual void DumpAllocatorStats( class FOutputDevice& Ar ) override; + + virtual const TCHAR* GetDescriptiveName() override; +}; + +#undef BINNED2_STAT diff --git a/Engine/Source/Runtime/Core/Public/HTML5/HTML5PlatformMath.h b/Engine/Source/Runtime/Core/Public/HTML5/HTML5PlatformMath.h index df8fd736075f..704f20d876b5 100644 --- a/Engine/Source/Runtime/Core/Public/HTML5/HTML5PlatformMath.h +++ b/Engine/Source/Runtime/Core/Public/HTML5/HTML5PlatformMath.h @@ -8,4 +8,39 @@ #pragma once #include "GenericPlatform/GenericPlatformMath.h" -typedef FGenericPlatformMath FPlatformMath; +struct FGenericPlatformMath : public FPlatformMath +{ + /** + * Counts the number of leading zeros in the bit representation of the value + * + * @param Value the value to determine the number of leading zeros for + * + * @return the number of zeros before the first "on" bit + */ + static FORCEINLINE uint32 CountLeadingZeros(uint32 Value) + { + if (Value == 0) + { + return 32; + } + + return __builtin_clz(Value); + } + + /** + * Counts the number of trailing zeros in the bit representation of the value + * + * @param Value the value to determine the number of trailing zeros for + * + * @return the number of zeros after the last "on" bit + */ + static FORCEINLINE uint32 CountTrailingZeros(uint32 Value) + { + if (Value == 0) + { + return 32; + } + + return __builtin_ctz(Value); + } +}; diff --git a/Engine/Source/Runtime/Core/Public/IOS/IOSPlatformMath.h b/Engine/Source/Runtime/Core/Public/IOS/IOSPlatformMath.h index 3f2174c44049..6c86c133426e 100644 --- a/Engine/Source/Runtime/Core/Public/IOS/IOSPlatformMath.h +++ b/Engine/Source/Runtime/Core/Public/IOS/IOSPlatformMath.h @@ -12,7 +12,39 @@ **/ struct FIOSPlatformMath : public FGenericPlatformMath { - + /** + * Counts the number of leading zeros in the bit representation of the value + * + * @param Value the value to determine the number of leading zeros for + * + * @return the number of zeros before the first "on" bit + */ + static FORCEINLINE uint32 CountLeadingZeros(uint32 Value) + { + if (Value == 0) + { + return 32; + } + + return __builtin_clz(Value); + } + + /** + * Counts the number of trailing zeros in the bit representation of the value + * + * @param Value the value to determine the number of trailing zeros for + * + * @return the number of zeros after the last "on" bit + */ + static FORCEINLINE uint32 CountTrailingZeros(uint32 Value) + { + if (Value == 0) + { + return 32; + } + + return __builtin_ctz(Value); + } }; typedef FIOSPlatformMath FPlatformMath; diff --git a/Engine/Source/Runtime/Core/Public/Linux/LinuxPlatformMath.h b/Engine/Source/Runtime/Core/Public/Linux/LinuxPlatformMath.h index d06497ff6ae2..9c1cf6f5d729 100644 --- a/Engine/Source/Runtime/Core/Public/Linux/LinuxPlatformMath.h +++ b/Engine/Source/Runtime/Core/Public/Linux/LinuxPlatformMath.h @@ -74,6 +74,39 @@ struct FLinuxPlatformMath : public FGenericPlatformMath } #endif + /** + * Counts the number of leading zeros in the bit representation of the value + * + * @param Value the value to determine the number of leading zeros for + * + * @return the number of zeros before the first "on" bit + */ + static FORCEINLINE uint32 CountLeadingZeros(uint32 Value) + { + if (Value == 0) + { + return 32; + } + + return __builtin_clz(Value); + } + + /** + * Counts the number of trailing zeros in the bit representation of the value + * + * @param Value the value to determine the number of trailing zeros for + * + * @return the number of zeros after the last "on" bit + */ + static FORCEINLINE uint32 CountTrailingZeros(uint32 Value) + { + if (Value == 0) + { + return 32; + } + + return __builtin_ctz(Value); + } }; typedef FLinuxPlatformMath FPlatformMath; diff --git a/Engine/Source/Runtime/Core/Public/Mac/MacPlatformMath.h b/Engine/Source/Runtime/Core/Public/Mac/MacPlatformMath.h index 05a168649812..833fe201161c 100644 --- a/Engine/Source/Runtime/Core/Public/Mac/MacPlatformMath.h +++ b/Engine/Source/Runtime/Core/Public/Mac/MacPlatformMath.h @@ -71,6 +71,39 @@ struct FMacPlatformMath : public FGenericPlatformMath } #endif + /** + * Counts the number of leading zeros in the bit representation of the value + * + * @param Value the value to determine the number of leading zeros for + * + * @return the number of zeros before the first "on" bit + */ + static FORCEINLINE uint32 CountLeadingZeros(uint32 Value) + { + if (Value == 0) + { + return 32; + } + + return __builtin_clz(Value); + } + + /** + * Counts the number of trailing zeros in the bit representation of the value + * + * @param Value the value to determine the number of trailing zeros for + * + * @return the number of zeros after the last "on" bit + */ + static FORCEINLINE uint32 CountTrailingZeros(uint32 Value) + { + if (Value == 0) + { + return 32; + } + + return __builtin_ctz(Value); + } }; typedef FMacPlatformMath FPlatformMath; diff --git a/Engine/Source/Runtime/Core/Public/Misc/App.h b/Engine/Source/Runtime/Core/Public/Misc/App.h index b5c3d9f12b9b..a6b94b5b7469 100644 --- a/Engine/Source/Runtime/Core/Public/Misc/App.h +++ b/Engine/Source/Runtime/Core/Public/Misc/App.h @@ -515,6 +515,11 @@ public: */ static float GetUnfocusedVolumeMultiplier(); + /** + * Sets the Unfocused Volume Multiplier + */ + static void SetUnfocusedVolumeMultiplier(float InVolumeMultiplier); + private: /** Holds the instance identifier. */ diff --git a/Engine/Source/Runtime/Core/Public/Serialization/ArchiveBase.h b/Engine/Source/Runtime/Core/Public/Serialization/ArchiveBase.h index ae9a501928d1..a082c2ca0ede 100644 --- a/Engine/Source/Runtime/Core/Public/Serialization/ArchiveBase.h +++ b/Engine/Source/Runtime/Core/Public/Serialization/ArchiveBase.h @@ -660,6 +660,11 @@ public: return ArIgnoreOuterRef; } + FORCEINLINE bool IsIgnoringClassGeneratedByRef() const + { + return ArIgnoreClassGeneratedByRef; + } + FORCEINLINE bool IsIgnoringClassRef() const { return ArIgnoreClassRef; @@ -1032,7 +1037,10 @@ public: /** If true, we will not serialize the Outer reference in UObject. */ bool ArIgnoreOuterRef; - + + /** If true, we will not serialize ClassGeneratedBy reference in UClass. */ + bool ArIgnoreClassGeneratedByRef; + /** If true, UObject::Serialize will skip serialization of the Class property. */ bool ArIgnoreClassRef; diff --git a/Engine/Source/Runtime/Core/Public/Stats/StatsData.h b/Engine/Source/Runtime/Core/Public/Stats/StatsData.h index 65a92cfb2b44..7897cdc9dcc2 100644 --- a/Engine/Source/Runtime/Core/Public/Stats/StatsData.h +++ b/Engine/Source/Runtime/Core/Public/Stats/StatsData.h @@ -24,6 +24,9 @@ struct CORE_API FStatConstants static const char* ThreadGroupName; static const FName NAME_ThreadGroup; + /** Stat short name for seconds per cycle. */ + static const FName NAME_SecondsPerCycle; + /** Special case category, when we want to Stat to appear at the root of the menu (leaving the category blank omits it from the menu entirely) */ static const FName NAME_NoCategory; @@ -37,13 +40,16 @@ struct CORE_API FStatConstants static const FString ThreadNameMarker; /** A raw name for the event wait with id. */ - static const FName NAME_EventWaitWithId; + static const FName RAW_EventWaitWithId; /** A raw name for the event trigger with id. */ - static const FName NAME_EventTriggerWithId; + static const FName RAW_EventTriggerWithId; /** A raw name for the stat marker. */ - static const FName NAME_NamedMarker; + static const FName RAW_NamedMarker; + + /** A special meta data used to advance the frame. */ + static const FStatNameAndInfo AdvanceFrame; }; namespace LexicalConversion diff --git a/Engine/Source/Runtime/Core/Public/Templates/Function.h b/Engine/Source/Runtime/Core/Public/Templates/Function.h index 2d7167dbabe7..2118edde1ae3 100644 --- a/Engine/Source/Runtime/Core/Public/Templates/Function.h +++ b/Engine/Source/Runtime/Core/Public/Templates/Function.h @@ -5,6 +5,8 @@ #include #include "AreTypesEqual.h" +#include "ContainerAllocationPolicies.h" +#include "UnrealMathUtility.h" #include "UnrealMemory.h" // Disable visualization hack for shipping or test builds. @@ -47,15 +49,17 @@ template struct TIsATFunctionRef> { enum { Value = */ namespace UE4Function_Private { + struct FFunctionStorage; + /** * Common interface to a callable object owned by TFunction. */ struct IFunction_OwnedObject { /** - * Returns a heap-allocated copy of the object, created by copy construction. + * Creates a copy of the object in the allocator and returns a pointer to it. */ - virtual IFunction_OwnedObject* CloneByCopy() const = 0; + virtual IFunction_OwnedObject* CopyToEmptyStorage(FFunctionStorage& Storage) const = 0; /** * Returns the address of the object. @@ -75,6 +79,69 @@ namespace UE4Function_Private { } + #if !defined(_WIN32) || defined(_WIN64) + // Let TFunction store up to 32 bytes which are 16-byte aligned before we heap allocate + typedef TAlignedBytes<16, 16> AlignedInlineFunctionType; + typedef TInlineAllocator<2> FunctionAllocatorType; + #else + // ... except on Win32, because we can't pass 16-byte aligned types by value, as some TFunctions are. + // So we'll just keep it heap-allocated, which is always sufficiently aligned. + typedef TAlignedBytes<16, 8> AlignedInlineFunctionType; + typedef FHeapAllocator FunctionAllocatorType; + #endif + + struct FFunctionStorage + { + FFunctionStorage() + : AllocatedSize(0) + { + } + + FFunctionStorage(FFunctionStorage&& Other) + : AllocatedSize(0) + { + Allocator.MoveToEmpty(Other.Allocator); + AllocatedSize = Other.AllocatedSize; + Other.AllocatedSize = 0; + } + + void Empty() + { + Allocator.ResizeAllocation(0, 0, sizeof(UE4Function_Private::AlignedInlineFunctionType)); + AllocatedSize = 0; + } + + typedef FunctionAllocatorType::ForElementType AllocatorType; + + IFunction_OwnedObject* GetBoundObject() const + { + return AllocatedSize ? (IFunction_OwnedObject*)Allocator.GetAllocation() : nullptr; + } + + AllocatorType Allocator; + int32 AllocatedSize; + }; +} + +inline void* operator new(size_t Size, UE4Function_Private::FFunctionStorage& Storage) +{ + if (UE4Function_Private::IFunction_OwnedObject* Obj = Storage.GetBoundObject()) + { + Obj->~IFunction_OwnedObject(); + } + + int32 NewSize = FMath::DivideAndRoundUp(Size, sizeof(UE4Function_Private::AlignedInlineFunctionType)); + if (Storage.AllocatedSize != NewSize) + { + Storage.Allocator.ResizeAllocation(0, NewSize, sizeof(UE4Function_Private::AlignedInlineFunctionType)); + Storage.AllocatedSize = NewSize; + } + + return Storage.Allocator.GetAllocation(); +} + +namespace UE4Function_Private +{ /** * Implementation of IFunction_OwnedObject for a given T. */ @@ -97,17 +164,11 @@ namespace UE4Function_Private { } - /** - * Returns a heap-allocated copy of the object, created by copy construction. - */ - IFunction_OwnedObject* CloneByCopy() const override + IFunction_OwnedObject* CopyToEmptyStorage(FFunctionStorage& Storage) const override { - return new TFunction_OwnedObject(Obj); + return new (Storage) TFunction_OwnedObject(Obj); } - /** - * Returns the address of the object. - */ virtual void* GetAddress() override { return &Obj; @@ -131,7 +192,7 @@ namespace UE4Function_Private /** * A class which defines an operator() which will invoke the TFunctionRefCaller::Call function. */ - template + template struct TFunctionRefBase; #if ENABLE_TFUNCTIONREF_VISUALIZATION @@ -164,6 +225,60 @@ namespace UE4Function_Private { } + template + struct TFunctionRefBaseCommon + { + explicit TFunctionRefBaseCommon(ENoInit) + { + // Not really designed to be initialized directly, but want to be explicit about that. + } + + template + void Set(FunctorType* Functor) + { + Callable = &UE4Function_Private::TFunctionRefCaller::Call; + + #if ENABLE_TFUNCTIONREF_VISUALIZATION + // We placement new over the top of the same object each time. This is illegal, + // but it ensures that the vptr is set correctly for the bound type, and so is + // visualizable. We never depend on the state of this object at runtime, so it's + // ok. + new ((void*)&DebugPtrStorage) UE4Function_Private::TDebugHelper; + DebugPtrStorage.Ptr = (void*)Functor; + #endif + } + + void CopyAndReseat(const TFunctionRefBaseCommon& Other, void* Functor) + { + Callable = Other.Callable; + + #if ENABLE_TFUNCTIONREF_VISUALIZATION + // Use Memcpy to copy the other DebugPtrStorage, including vptr (because we don't know the bound type + // here), and then reseat the underlying pointer. Possibly even more evil than the Set code. + FMemory::Memcpy(&DebugPtrStorage, &Other.DebugPtrStorage, sizeof(DebugPtrStorage)); + DebugPtrStorage.Ptr = Functor; + #endif + } + + void Unset() + { + Callable = &UE4Function_Private::TFunctionRefAsserter::Call; + } + + CallableType* GetCallable() const + { + return Callable; + } + + private: + // A pointer to a function which invokes the call operator on the callable object + CallableType* Callable; + + #if ENABLE_TFUNCTIONREF_VISUALIZATION + // To help debug visualizers + UE4Function_Private::TDebugHelper DebugPtrStorage; + #endif + }; /** * Switch on the existence of variadics. Once all our supported compilers support variadics, a lot of this code @@ -198,30 +313,40 @@ namespace UE4Function_Private { static Ret Call(void* Obj, ParamTypes&...) { - // Attempting to call a null TFunction! - check(false); + checkf(false, TEXT("Attempting to call a null TFunction!")); // This doesn't need to be valid, because it'll never be reached, but it does at least need to compile. return FakeCall((Ret*)Obj); } }; - template - struct TFunctionRefBase + template + struct TFunctionRefBase : TFunctionRefBaseCommon { - typedef Ret (*FuncPtr)(void*, ParamTypes&...); + typedef TFunctionRefBaseCommon Super; + + explicit TFunctionRefBase(ENoInit) + : Super(NoInit) + { + } + + template + explicit TFunctionRefBase(FunctorType* Functor) + : Super(Functor) + { + } Ret operator()(ParamTypes... Params) const { - const auto* Derived = static_cast*>(this); - return Derived->Func(Derived->Ptr, Params...); + const DerivedType* Derived = static_cast(this); + return this->GetCallable()(Derived->GetPtr(), Params...); } }; #else /** - * Specialisations of the above classes which don't use variadic templates, up to a maximum arity of 4. + * Specializations of the above classes which don't use variadic templates, up to a maximum arity of 4. */ template struct TFunctionRefCaller { static Ret Call(void* Obj ) { return (*(Functor*)Obj)( ); } }; @@ -236,17 +361,17 @@ namespace UE4Function_Private template struct TFunctionRefCaller { static void Call(void* Obj, T0& P0, T1& P1, T2& P2 ) { (*(Functor*)Obj)(Forward(P0), Forward(P1), Forward(P2) ); } }; template struct TFunctionRefCaller { static void Call(void* Obj, T0& P0, T1& P1, T2& P2, T3& P3) { (*(Functor*)Obj)(Forward(P0), Forward(P1), Forward(P2), Forward(P3)); } }; - template struct TFunctionRefAsserter { static Ret Call(void* Obj ) { /* Attempting to call a null TFunction! */ check(false); return FakeCall((Ret*)Obj); } }; - template struct TFunctionRefAsserter { static Ret Call(void* Obj, T0& ) { /* Attempting to call a null TFunction! */ check(false); return FakeCall((Ret*)Obj); } }; - template struct TFunctionRefAsserter { static Ret Call(void* Obj, T0&, T1& ) { /* Attempting to call a null TFunction! */ check(false); return FakeCall((Ret*)Obj); } }; - template struct TFunctionRefAsserter { static Ret Call(void* Obj, T0&, T1&, T2& ) { /* Attempting to call a null TFunction! */ check(false); return FakeCall((Ret*)Obj); } }; - template struct TFunctionRefAsserter { static Ret Call(void* Obj, T0&, T1&, T2&, T3&) { /* Attempting to call a null TFunction! */ check(false); return FakeCall((Ret*)Obj); } }; + template struct TFunctionRefAsserter { static Ret Call(void* Obj ) { checkf(false, TEXT("Attempting to call a null TFunction!")); return FakeCall((Ret*)Obj); } }; + template struct TFunctionRefAsserter { static Ret Call(void* Obj, T0& ) { checkf(false, TEXT("Attempting to call a null TFunction!")); return FakeCall((Ret*)Obj); } }; + template struct TFunctionRefAsserter { static Ret Call(void* Obj, T0&, T1& ) { checkf(false, TEXT("Attempting to call a null TFunction!")); return FakeCall((Ret*)Obj); } }; + template struct TFunctionRefAsserter { static Ret Call(void* Obj, T0&, T1&, T2& ) { checkf(false, TEXT("Attempting to call a null TFunction!")); return FakeCall((Ret*)Obj); } }; + template struct TFunctionRefAsserter { static Ret Call(void* Obj, T0&, T1&, T2&, T3&) { checkf(false, TEXT("Attempting to call a null TFunction!")); return FakeCall((Ret*)Obj); } }; - template struct TFunctionRefBase { typedef Ret (*FuncPtr)(void* ); Ret operator()( ) const { const auto* Derived = static_cast*>(this); return Derived->Func(Derived->Ptr ); } }; - template struct TFunctionRefBase { typedef Ret (*FuncPtr)(void*, T0& ); Ret operator()(T0 P0 ) const { const auto* Derived = static_cast*>(this); return Derived->Func(Derived->Ptr, P0 ); } }; - template struct TFunctionRefBase { typedef Ret (*FuncPtr)(void*, T0&, T1& ); Ret operator()(T0 P0, T1 P1 ) const { const auto* Derived = static_cast*>(this); return Derived->Func(Derived->Ptr, P0, P1 ); } }; - template struct TFunctionRefBase { typedef Ret (*FuncPtr)(void*, T0&, T1&, T2& ); Ret operator()(T0 P0, T1 P1, T2 P2 ) const { const auto* Derived = static_cast*>(this); return Derived->Func(Derived->Ptr, P0, P1, P2 ); } }; - template struct TFunctionRefBase { typedef Ret (*FuncPtr)(void*, T0&, T1&, T2&, T3&); Ret operator()(T0 P0, T1 P1, T2 P2, T3 P3) const { const auto* Derived = static_cast*>(this); return Derived->Func(Derived->Ptr, P0, P1, P2, P3); } }; + template struct TFunctionRefBase : TFunctionRefBaseCommon { typedef TFunctionRefBaseCommon Super; explicit TFunctionRefBase(ENoInit) : Super(NoInit) {} template explicit TFunctionRefBaseCommon(FunctorType* Functor) : Super(Functor) {} Ret operator()( ) const { const DerivedType* Derived = static_cast(this); return this->Func(Derived->GetPtr() ); } }; + template struct TFunctionRefBase : TFunctionRefBaseCommon { typedef TFunctionRefBaseCommon Super; explicit TFunctionRefBase(ENoInit) : Super(NoInit) {} template explicit TFunctionRefBaseCommon(FunctorType* Functor) : Super(Functor) {} Ret operator()(T0 P0 ) const { const DerivedType* Derived = static_cast(this); return this->Func(Derived->GetPtr(), P0 ); } }; + template struct TFunctionRefBase : TFunctionRefBaseCommon { typedef TFunctionRefBaseCommon Super; explicit TFunctionRefBase(ENoInit) : Super(NoInit) {} template explicit TFunctionRefBaseCommon(FunctorType* Functor) : Super(Functor) {} Ret operator()(T0 P0, T1 P1 ) const { const DerivedType* Derived = static_cast(this); return this->Func(Derived->GetPtr(), P0, P1 ); } }; + template struct TFunctionRefBase : TFunctionRefBaseCommon { typedef TFunctionRefBaseCommon Super; explicit TFunctionRefBase(ENoInit) : Super(NoInit) {} template explicit TFunctionRefBaseCommon(FunctorType* Functor) : Super(Functor) {} Ret operator()(T0 P0, T1 P1, T2 P2 ) const { const DerivedType* Derived = static_cast(this); return this->Func(Derived->GetPtr(), P0, P1, P2 ); } }; + template struct TFunctionRefBase : TFunctionRefBaseCommon { typedef TFunctionRefBaseCommon Super; explicit TFunctionRefBase(ENoInit) : Super(NoInit) {} template explicit TFunctionRefBaseCommon(FunctorType* Functor) : Super(Functor) {} Ret operator()(T0 P0, T1 P1, T2 P2, T3 P3) const { const DerivedType* Derived = static_cast(this); return this->Func(Derived->GetPtr(), P0, P1, P2, P3); } }; #endif } @@ -279,7 +404,7 @@ namespace UE4Function_Private * { * for (const FString& Str : SomeBunchOfStrings) * { - * int32 Int = Convert(Str); + * int32 Int = Func(Str); * DoSomething(Int); * } * } @@ -303,14 +428,11 @@ namespace UE4Function_Private * } */ template -class TFunctionRef : public UE4Function_Private::TFunctionRefBase +class TFunctionRef : public UE4Function_Private::TFunctionRefBase, FuncType> { - template - friend class TFunction; + friend struct UE4Function_Private::TFunctionRefBase, FuncType>; - friend struct UE4Function_Private::TFunctionRefBase; - - typedef UE4Function_Private::TFunctionRefBase Super; + typedef UE4Function_Private::TFunctionRefBase, FuncType> Super; public: /** @@ -323,6 +445,7 @@ public: template ::Value && !TAreTypesEqual::Value>::Type> TFunctionRef(FunctorType& Functor) #endif + : Super(NoInit) { // This constructor is disabled for function types because we want it to call the function pointer overload. // It is also disabled for TFunctionRef types because VC is incorrectly treating it as a copy constructor. @@ -340,6 +463,7 @@ public: template ::Value && !TAreTypesEqual::Value>::Type> TFunctionRef(const FunctorType& Functor) #endif + : Super(NoInit) { // This constructor is disabled for function types because we want it to call the function pointer overload. // It is also disabled for TFunctionRef types because VC is incorrectly treating it as a copy constructor. @@ -357,6 +481,7 @@ public: template ::Value>::Type> TFunctionRef(FunctionType* Function) #endif + : Super(NoInit) { // This constructor is enabled only for function types because we don't want weird errors from it being called with arbitrary pointers. @@ -369,6 +494,7 @@ public: * Copy constructor. */ TFunctionRef(const TFunctionRef& Other) + : Super(NoInit) { // If visualization is enabled, then we need to do an explicit copy // to ensure that our hacky DebugPtrStorage's vptr is copied. @@ -400,14 +526,6 @@ public: #endif private: - /** - * Constructor which leaves the TFunctionRef uninitialized. This is only for use with - * TFunction. - */ - explicit TFunctionRef(ENoInit) - { - } - /** * Sets the state of the TFunctionRef given a pointer to a callable thing. */ @@ -418,16 +536,7 @@ private: // this won't compile. We convert it back again before we use it anyway. Ptr = (void*)Functor; - Func = &UE4Function_Private::TFunctionRefCaller::Call; - - #if ENABLE_TFUNCTIONREF_VISUALIZATION - // We placement new over the top of the same object each time. This is illegal, - // but it ensures that the vptr is set correctly for the bound type, and so is - // visualizable. We never depend on the state of this object at runtime, so it's - // ok. - new ((void*)&DebugPtrStorage) UE4Function_Private::TDebugHelper; - DebugPtrStorage.Ptr = Ptr; - #endif + Super::Set(Functor); } /** @@ -435,8 +544,8 @@ private: */ void Unset() { - Ptr = nullptr; - Func = &UE4Function_Private::TFunctionRefAsserter::Call; + Ptr = nullptr; + Super::Unset(); } /** @@ -445,35 +554,20 @@ private: */ void CopyAndReseat(const TFunctionRef& Other, void* Functor) { - Ptr = Functor; - Func = Other.Func; - - #if ENABLE_TFUNCTIONREF_VISUALIZATION - // Use Memcpy to copy the other DebugPtrStorage, including vptr (because we don't know the bound type - // here), and then reseat the underlying pointer. Possibly even more evil than the Set code. - FMemory::Memcpy(&DebugPtrStorage, &Other.DebugPtrStorage, sizeof(DebugPtrStorage)); - DebugPtrStorage.Ptr = Ptr; - #endif + Ptr = Functor; + Super::CopyAndReseat(Other, Functor); } /** - * Tests if the TFunctionRef is unset. + * Returns a pointer to the callable object - needed by TFunctionRefBase. */ - bool IsUnset() const + void* GetPtr() const { - return Ptr == nullptr; + return Ptr; } // A pointer to the callable object void* Ptr; - - // A pointer to a function which invokes the call operator on the callable object - typename Super::FuncPtr Func; - - #if ENABLE_TFUNCTIONREF_VISUALIZATION - // To help debug visualizers - UE4Function_Private::TDebugHelper DebugPtrStorage; - #endif }; /** @@ -492,7 +586,7 @@ private: * Example: * * // Something.h - * TFunction GetTransform(const FString& Prefix); + * TFunction GetTransform(); * * // Something.cpp * TFunction GetTransform(const FString& Prefix) @@ -514,9 +608,11 @@ private: * } */ template -class TFunction : public TFunctionRef +class TFunction : public UE4Function_Private::TFunctionRefBase, FuncType> { - typedef TFunctionRef Super; + friend struct UE4Function_Private::TFunctionRefBase, FuncType>; + + typedef UE4Function_Private::TFunctionRefBase, FuncType> Super; public: /** @@ -525,7 +621,6 @@ public: TFunction(TYPE_OF_NULLPTR = nullptr) : Super(NoInit) { - Func = nullptr; Super::Unset(); } @@ -556,9 +651,7 @@ public: // TFunction MyFunction = [=](float F) -> int32 { return MyFunctionRef(F); }; static_assert(!TIsATFunctionRef::Value, "Cannot construct a TFunction from a TFunctionRef"); - auto NewObj = new OwnedType(Forward(InFunc)); - - Func = NewObj; + OwnedType* NewObj = new (Storage) OwnedType(Forward(InFunc)); Super::Set(&NewObj->Obj); } @@ -568,14 +661,13 @@ public: TFunction(const TFunction& Other) : Super(NoInit) { - if (Other) + if (UE4Function_Private::IFunction_OwnedObject* OtherFunc = Other.Storage.GetBoundObject()) { - Func = Other.Func->CloneByCopy(); - Super::CopyAndReseat(Other, Func->GetAddress()); + UE4Function_Private::IFunction_OwnedObject* ThisFunc = OtherFunc->CopyToEmptyStorage(Storage); + Super::CopyAndReseat(Other, ThisFunc->GetAddress()); } else { - Func = nullptr; Super::Unset(); } } @@ -584,12 +676,11 @@ public: * Move constructor. */ TFunction(TFunction&& Other) - : Super(NoInit) + : Super (NoInit) + , Storage(MoveTemp(Other.Storage)) { - Func = Other.Func; - Super::CopyAndReseat(Other, Func->GetAddress()); + Super::CopyAndReseat(Other, Storage.GetBoundObject()->GetAddress()); - Other.Func = nullptr; Other.Unset(); } @@ -607,8 +698,11 @@ public: */ TFunction& operator=(TYPE_OF_NULLPTR) { - delete Func; - Func = nullptr; + if (UE4Function_Private::IFunction_OwnedObject* Obj = Storage.GetBoundObject()) + { + Obj->~IFunction_OwnedObject(); + } + Storage.Empty(); Super::Unset(); return *this; @@ -619,7 +713,10 @@ public: */ ~TFunction() { - delete Func; + if (UE4Function_Private::IFunction_OwnedObject* Obj = Storage.GetBoundObject()) + { + Obj->~IFunction_OwnedObject(); + } } /** @@ -627,7 +724,7 @@ public: */ FORCEINLINE_EXPLICIT_OPERATOR_BOOL() const { - return !Super::IsUnset(); + return !!Storage.GetBoundObject(); } /** @@ -637,14 +734,23 @@ public: SAFE_BOOL_OPERATORS(TFunction) private: - UE4Function_Private::IFunction_OwnedObject* Func; + /** + * Returns a pointer to the callable object - needed by TFunctionRefBase. + */ + void* GetPtr() const + { + UE4Function_Private::IFunction_OwnedObject* Ptr = Storage.GetBoundObject(); + return Ptr ? Ptr->GetAddress() : nullptr; + } + + UE4Function_Private::FFunctionStorage Storage; }; /** * Nullptr equality operator. */ template -bool operator==(TYPE_OF_NULLPTR, const TFunction& Func) +FORCEINLINE bool operator==(TYPE_OF_NULLPTR, const TFunction& Func) { return !Func; } @@ -653,7 +759,7 @@ bool operator==(TYPE_OF_NULLPTR, const TFunction& Func) * Nullptr equality operator. */ template -bool operator==(const TFunction& Func, TYPE_OF_NULLPTR) +FORCEINLINE bool operator==(const TFunction& Func, TYPE_OF_NULLPTR) { return !Func; } @@ -662,7 +768,7 @@ bool operator==(const TFunction& Func, TYPE_OF_NULLPTR) * Nullptr inequality operator. */ template -bool operator!=(TYPE_OF_NULLPTR, const TFunction& Func) +FORCEINLINE bool operator!=(TYPE_OF_NULLPTR, const TFunction& Func) { return (bool)Func; } @@ -671,7 +777,7 @@ bool operator!=(TYPE_OF_NULLPTR, const TFunction& Func) * Nullptr inequality operator. */ template -bool operator!=(const TFunction& Func, TYPE_OF_NULLPTR) +FORCEINLINE bool operator!=(const TFunction& Func, TYPE_OF_NULLPTR) { return (bool)Func; } diff --git a/Engine/Source/Runtime/Core/Public/UObject/NameTypes.h b/Engine/Source/Runtime/Core/Public/UObject/NameTypes.h index f0dd49bb91b7..deaacfae550e 100644 --- a/Engine/Source/Runtime/Core/Public/UObject/NameTypes.h +++ b/Engine/Source/Runtime/Core/Public/UObject/NameTypes.h @@ -60,6 +60,9 @@ typedef int32 NAME_INDEX; /** These characters cannot be used in long package names */ #define INVALID_LONGPACKAGE_CHARACTERS TEXT("\\:*?\"<>|' ,.&!\n\r\t@#") +/** These characters can be used in relative directory names (lowercase versions as well) */ +#define VALID_SAVEDDIRSUFFIX_CHARACTERS TEXT("_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz") + enum class ENameCase : uint8 { CaseSensitive, @@ -562,11 +565,16 @@ public: FORCEINLINE bool operator==(const FName& Other) const { - return IsEqual(Other, ENameCase::IgnoreCase); + #if WITH_CASE_PRESERVING_NAME + return GetComparisonIndexFast() == Other.GetComparisonIndexFast() && GetNumber() == Other.GetNumber(); + #else + static_assert(sizeof(CompositeComparisonValue) == sizeof(*this), "ComparisonValue does not cover the entire FName state"); + return CompositeComparisonValue == Other.CompositeComparisonValue; + #endif } FORCEINLINE bool operator!=(const FName& Other) const { - return !IsEqual(Other, ENameCase::IgnoreCase); + return !(*this == Other); } /** @@ -938,14 +946,25 @@ public: private: - /** Index into the Names array (used to find String portion of the string/number pair used for comparison) */ - NAME_INDEX ComparisonIndex; -#if WITH_CASE_PRESERVING_NAME - /** Index into the Names array (used to find String portion of the string/number pair used for display) */ - NAME_INDEX DisplayIndex; -#endif - /** Number portion of the string/number pair (stored internally as 1 more than actual, so zero'd memory will be the default, no-instance case) */ - uint32 Number; + union + { + struct + { + /** Index into the Names array (used to find String portion of the string/number pair used for comparison) */ + NAME_INDEX ComparisonIndex; + #if WITH_CASE_PRESERVING_NAME + /** Index into the Names array (used to find String portion of the string/number pair used for display) */ + NAME_INDEX DisplayIndex; + #endif + /** Number portion of the string/number pair (stored internally as 1 more than actual, so zero'd memory will be the default, no-instance case) */ + uint32 Number; + }; + + // Used to perform a single comparison in FName::operator== + #if !WITH_CASE_PRESERVING_NAME + uint64 CompositeComparisonValue; + #endif + }; /** Name hash. */ static FNameEntry* NameHash[ FNameDefs::NameHashBucketCount ]; diff --git a/Engine/Source/Runtime/Core/Public/Windows/WindowsPlatformMemory.h b/Engine/Source/Runtime/Core/Public/Windows/WindowsPlatformMemory.h index 2677e85a82ae..f3da9633fd5b 100644 --- a/Engine/Source/Runtime/Core/Public/Windows/WindowsPlatformMemory.h +++ b/Engine/Source/Runtime/Core/Public/Windows/WindowsPlatformMemory.h @@ -63,15 +63,22 @@ struct CORE_API FWindowsPlatformMemory //~ Begin FGenericPlatformMemory Interface static void Init(); + static bool SupportBackupMemoryPool() + { + return true; + } static class FMalloc* BaseAllocator(); static FPlatformMemoryStats GetStats(); static void GetStatsForMallocProfiler( FGenericMemoryStats& out_Stats ); static const FPlatformMemoryConstants& GetConstants(); - static void UpdateStats(); static void* BinnedAllocFromOS( SIZE_T Size ); static void BinnedFreeToOS( void* Ptr ); static FSharedMemoryRegion* MapNamedSharedMemoryRegion(const FString& InName, bool bCreate, uint32 AccessMode, SIZE_T Size); static bool UnmapNamedSharedMemoryRegion(FSharedMemoryRegion * MemoryRegion); +protected: + friend struct FGenericStatsUpdater; + + static void InternalUpdateStats( const FPlatformMemoryStats& MemoryStats ); //~ End FGenericPlatformMemory Interface }; diff --git a/Engine/Source/Runtime/Core/Public/Windows/WindowsPlatformStackWalk.h b/Engine/Source/Runtime/Core/Public/Windows/WindowsPlatformStackWalk.h index 128c67708e66..98b2a5a2efa2 100644 --- a/Engine/Source/Runtime/Core/Public/Windows/WindowsPlatformStackWalk.h +++ b/Engine/Source/Runtime/Core/Public/Windows/WindowsPlatformStackWalk.h @@ -20,6 +20,21 @@ struct CORE_API FWindowsPlatformStackWalk static int32 GetProcessModuleSignatures(FStackWalkModuleInfo *ModuleSignatures, const int32 ModuleSignaturesSize); static void RegisterOnModulesChanged(); + + /** + * Upload localy built symbols to network symbol storage. + * + * Use case: + * Game designers use game from source (without prebuild game .dll-files). + * In this case all game .dll-files are compiled locally. + * For post-mortem debug programmers need .dll and .pdb files from designers. + */ + static bool UploadLocalSymbols(); + + /** + * Get downstream storage with downloaded from remote symbol storage files. + */ + static FString GetDownstreamStorage(); }; diff --git a/Engine/Source/Runtime/CoreUObject/Private/Blueprint/BlueprintSupport.cpp b/Engine/Source/Runtime/CoreUObject/Private/Blueprint/BlueprintSupport.cpp index 2a1df6a5074d..6accd4f9026c 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/Blueprint/BlueprintSupport.cpp +++ b/Engine/Source/Runtime/CoreUObject/Private/Blueprint/BlueprintSupport.cpp @@ -981,7 +981,7 @@ int32 FLinkerLoad::ResolveDependencyPlaceholder(FLinkerPlaceholderBase* Placehol // holding onto objects that are spawned during the process (to ensure // they're not thrown away prematurely) bool const bIsAsyncLoadRef = (UnresolvedReferences.ExternalReferences.Num() == 1) && - PlaceholderObj->HasAnyFlags(RF_AsyncLoading) && (UnresolvedReferences.ExternalReferences[0].Referencer == FGCObject::GGCObjectReferencer); + PlaceholderObj->HasAnyInternalFlags(EInternalObjectFlags::AsyncLoading) && (UnresolvedReferences.ExternalReferences[0].Referencer == FGCObject::GGCObjectReferencer); DEFERRED_DEPENDENCY_CHECK(!bIsReferenced || bIsAsyncLoadRef); #endif // USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS @@ -1899,7 +1899,7 @@ void FReplaceCookedBPGC::AdditionalStubFieldInitialization(UField* Stub) Class->ClassConstructor = &DummyClassConstructor; Class->ClassAddReferencedObjects = &UObject::AddReferencedObjects; } - Stub->SetFlags(RF_Native); + Stub->SetInternalFlags(EInternalObjectFlags::Native); } #endif //WITH_EDITOR \ No newline at end of file diff --git a/Engine/Source/Runtime/CoreUObject/Private/Misc/PackageName.cpp b/Engine/Source/Runtime/CoreUObject/Private/Misc/PackageName.cpp index d7655884574b..04a6d1a86ed2 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/Misc/PackageName.cpp +++ b/Engine/Source/Runtime/CoreUObject/Private/Misc/PackageName.cpp @@ -40,7 +40,7 @@ FString FPackageName::GetShortName(const FString& LongName) return LongName.Mid(IndexOfLastSlash + 1); } -FString FPackageName::GetShortName(UPackage* Package) +FString FPackageName::GetShortName(const UPackage* Package) { check(Package != NULL); return GetShortName(Package->GetName()); diff --git a/Engine/Source/Runtime/CoreUObject/Private/Serialization/ArchiveFindCulprit.cpp b/Engine/Source/Runtime/CoreUObject/Private/Serialization/ArchiveFindCulprit.cpp index cf9c53370273..ea01a025aa4e 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/Serialization/ArchiveFindCulprit.cpp +++ b/Engine/Source/Runtime/CoreUObject/Private/Serialization/ArchiveFindCulprit.cpp @@ -32,7 +32,7 @@ FArchiveFindCulprit::FArchiveFindCulprit( UObject* InFind, UObject* Src, bool In FArchive& FArchiveFindCulprit::operator<<( UObject*& Obj ) { - if( Obj==Find ) + if (Obj == Find) { if (GetSerializedProperty() != nullptr) { @@ -41,11 +41,11 @@ FArchive& FArchiveFindCulprit::operator<<( UObject*& Obj ) Count++; } - if( PretendSaving && Obj && !Obj->IsPendingKill() ) + if (PretendSaving && Obj && !Obj->IsPendingKill()) { - if( (!Obj->HasAnyFlags(RF_Transient) || Obj->HasAnyFlags(RF_Public)) && !Obj->HasAnyMarks(OBJECTMARK_TagExp) ) + if ((!Obj->HasAnyFlags(RF_Transient) || Obj->HasAnyFlags(RF_Public)) && !Obj->HasAnyMarks(OBJECTMARK_TagExp)) { - if ( Obj->HasAnyFlags(RF_Native|RF_Standalone|RF_RootSet) ) + if (Obj->HasAnyFlags(RF_Standalone) || Obj->HasAnyInternalFlags(EInternalObjectFlags::Native | EInternalObjectFlags::RootSet)) { // serialize the object's Outer if this object could potentially be rooting the object we're attempting to find references to // otherwise, it's just spam diff --git a/Engine/Source/Runtime/CoreUObject/Private/Serialization/ArchiveTraceRoute.cpp b/Engine/Source/Runtime/CoreUObject/Private/Serialization/ArchiveTraceRoute.cpp index a95f28c11371..e26b2fdb4653 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/Serialization/ArchiveTraceRoute.cpp +++ b/Engine/Source/Runtime/CoreUObject/Private/Serialization/ArchiveTraceRoute.cpp @@ -88,7 +88,7 @@ FString FArchiveTraceRoute::PrintRootPath( const TMap& Rout ObjectReachability += TEXT(" (root)"); } - if( Object->HasAnyFlags(RF_Native) ) + if( Object->IsNative() ) { ObjectReachability += TEXT(" (native)"); } @@ -127,7 +127,7 @@ FArchiveTraceRoute::FArchiveTraceRoute( UObject* TargetObject, TMapHasAnyFlags(KeepFlags) ) @@ -148,7 +148,7 @@ FArchiveTraceRoute::FArchiveTraceRoute( UObject* TargetObject, TMapHasAnyFlags(RequiredFlags) ) + if ( CurrentObject->HasAnyFlags(RequiredFlags) || CurrentObject->IsRooted() ) { // make sure it isn't tagged CurrentObject->UnMark(OBJECTMARK_TagExp); @@ -415,7 +415,7 @@ void FArchiveTraceRoute::CalculateReferenceDepthsForNode( FObjectGraphNode* Obje { // if the object from this node has one of the required flags, don't process this object's referencers // as it's considered a "root" for the route - if ( !CurrentNode->NodeObject->HasAnyFlags(RequiredFlags) ) + if (!CurrentNode->NodeObject->HasAnyFlags(RequiredFlags) && !CurrentNode->NodeObject->IsRooted()) { CalculateReferenceDepthsForNode(CurrentNode); } diff --git a/Engine/Source/Runtime/CoreUObject/Private/Serialization/AsyncLoading.cpp b/Engine/Source/Runtime/CoreUObject/Private/Serialization/AsyncLoading.cpp index 621bb4178aca..a8178ba03a28 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/Serialization/AsyncLoading.cpp +++ b/Engine/Source/Runtime/CoreUObject/Private/Serialization/AsyncLoading.cpp @@ -132,21 +132,20 @@ public: #if THREADSAFE_UOBJECTS FScopeLock ReferencedObjectsLock(&ReferencedObjectsCritical); #endif - const EObjectFlags AsyncFlags = RF_Async | RF_AsyncLoading; + const EInternalObjectFlags AsyncFlags = EInternalObjectFlags::Async | EInternalObjectFlags::AsyncLoading; for (UObject* Obj : ReferencedObjects) { check(Obj); - Obj->AtomicallyClearFlags(AsyncFlags); - check(!Obj->HasAnyFlags(AsyncFlags)) + Obj->AtomicallyClearInternalFlags(AsyncFlags); + check(!Obj->HasAnyInternalFlags(AsyncFlags)) } ReferencedObjects.Reset(); } /** Removes all referenced objects and markes them for GC */ void EmptyReferencedObjectsAndCancelLoading() { - check(IsInGameThread()); const EObjectFlags LoadFlags = RF_NeedLoad | RF_NeedPostLoad | RF_NeedPostLoadSubobjects; - const EObjectFlags AsyncFlags = RF_Async | RF_AsyncLoading; + const EInternalObjectFlags AsyncFlags = EInternalObjectFlags::Async | EInternalObjectFlags::AsyncLoading; #if THREADSAFE_UOBJECTS FScopeLock ReferencedObjectsLock(&ReferencedObjectsCritical); @@ -155,29 +154,32 @@ public: // All of the referenced objects have been created by async loading code and may be in an invalid state so mark them for GC for (auto Object : ReferencedObjects) { - Object->ClearFlags(AsyncFlags); + Object->ClearInternalFlags(AsyncFlags); if (Object->HasAnyFlags(LoadFlags)) { Object->AtomicallyClearFlags(LoadFlags); - Object->AtomicallySetFlags(RF_PendingKill); + Object->MarkPendingKill(); } - check(!Object->HasAnyFlags(AsyncFlags | LoadFlags)); + check(!Object->HasAnyInternalFlags(AsyncFlags) && !Object->HasAnyFlags(LoadFlags)); } ReferencedObjects.Reset(); } #if !UE_BUILD_SHIPPING - /** Verifies that no object exists that has either RF_AsyncLoading|RF_Async set and is NOT being referenced by FAsyncObjectsReferencer */ + /** Verifies that no object exists that has either EInternalObjectFlags::AsyncLoading and EInternalObjectFlags::Async set and is NOT being referenced by FAsyncObjectsReferencer */ FORCENOINLINE void VerifyAssumptions() { + const EInternalObjectFlags AsyncFlags = EInternalObjectFlags::Async | EInternalObjectFlags::AsyncLoading; for (FRawObjectIterator It; It; ++It) { - UObject* Obj = *It; - if (Obj->HasAnyFlags(RF_AsyncLoading|RF_Async)) + FUObjectItem* ObjItem = *It; + checkSlow(ObjItem); + UObject* Object = static_cast(ObjItem->Object); + if (Object->HasAnyInternalFlags(AsyncFlags)) { - if (!Contains(Obj)) + if (!Contains(Object)) { - UE_LOG(LogStreaming, Error, TEXT("%s has RF_AsyncLoading|RF_Async set but is not referenced by FAsyncObjectsReferencer"), *Obj->GetPathName()); + UE_LOG(LogStreaming, Error, TEXT("%s has AsyncLoading|Async set but is not referenced by FAsyncObjectsReferencer"), *Object->GetPathName()); } } } @@ -379,7 +381,10 @@ void FAsyncLoadingThread::CancelAsyncLoadingInternal() QueuedPackagesCounter.Reset(); FUObjectThreadContext::Get().ObjLoaded.Empty(); - FAsyncObjectsReferencer::Get().EmptyReferencedObjectsAndCancelLoading(); + { + FGCScopeGuard GCGuard; + FAsyncObjectsReferencer::Get().EmptyReferencedObjectsAndCancelLoading(); + } // Notify everyone streaming is canceled. CancelLoadingEvent->Trigger(); @@ -994,7 +999,7 @@ void NotifyConstructedDuringAsyncLoading(UObject* Object, bool bSubObject) // finished routing PostLoad to all objects. if (!bSubObject) { - Object->SetFlags(RF_AsyncLoading); + Object->SetInternalFlags(EInternalObjectFlags::AsyncLoading); } FAsyncObjectsReferencer::Get().AddObject(Object); } @@ -1138,7 +1143,7 @@ bool FAsyncPackage::GiveUpTimeSlice() /** * Begin async loading process. Simulates parts of BeginLoad. * - * Objects created during BeginAsyncLoad and EndAsyncLoad will have RF_AsyncLoading set + * Objects created during BeginAsyncLoad and EndAsyncLoad will have EInternalObjectFlags::AsyncLoading set */ void FAsyncPackage::BeginAsyncLoad() { @@ -1286,7 +1291,7 @@ EAsyncPackageState::Type FAsyncPackage::Tick(bool InbUseTimeLimit, bool InbUseFu // End async loading, simulates EndLoad EndAsyncLoad(); - // Finish objects (removing RF_AsyncLoading, dissociate imports and forced exports, + // Finish objects (removing EInternalObjectFlags::AsyncLoading, dissociate imports and forced exports, // call completion callback, ... // If the load has failed, perform completion callbacks and then quit if (LoadingState == EAsyncPackageState::Complete || bLoadHasFailed) @@ -1875,6 +1880,7 @@ EAsyncPackageState::Type FAsyncPackage::PostLoadDeferredObjects(double InTickSta LastTypeOfWorkPerformed = TEXT("postloading_gamethread"); TArray& ObjLoadedInPostLoad = FUObjectThreadContext::Get().ObjLoaded; + TArray ObjLoadedInPostLoadLocal; while (DeferredPostLoadIndex < DeferredPostLoadObjects.Num() && !AsyncLoadingThread.IsAsyncLoadingSuspended() && @@ -1893,18 +1899,29 @@ EAsyncPackageState::Type FAsyncPackage::PostLoadDeferredObjects(double InTickSta // There's no going back to the async tick loop from here. UE_LOG(LogStreaming, Warning, TEXT("Detected %d objects loaded in PostLoad while streaming, this may cause hitches as we're blocking async loading to pre-load them."), ObjLoadedInPostLoad.Num()); - // Make sure all objects loaded in PostLoad get post-loaded too - DeferredPostLoadObjects.Append(ObjLoadedInPostLoad); + // Copy to local array because ObjLoadedInPostLoad can change while we're iterating over it + ObjLoadedInPostLoadLocal.Append(ObjLoadedInPostLoad); + ObjLoadedInPostLoad.Reset(); - // Preload (aka serialize) the objects loaded in PostLoad. - for (UObject* PreLoadObject : ObjLoadedInPostLoad) + while (ObjLoadedInPostLoadLocal.Num()) { - if (PreLoadObject && PreLoadObject->GetLinker()) + // Make sure all objects loaded in PostLoad get post-loaded too + DeferredPostLoadObjects.Append(ObjLoadedInPostLoadLocal); + + // Preload (aka serialize) the objects loaded in PostLoad. + for (UObject* PreLoadObject : ObjLoadedInPostLoadLocal) { - PreLoadObject->GetLinker()->Preload(PreLoadObject); + if (PreLoadObject && PreLoadObject->GetLinker()) + { + PreLoadObject->GetLinker()->Preload(PreLoadObject); + } } - } - ObjLoadedInPostLoad.Empty(); + + // Other objects could've been loaded while we were preloading, continue until we've processed all of them. + ObjLoadedInPostLoadLocal.Reset(); + ObjLoadedInPostLoadLocal.Append(ObjLoadedInPostLoad); + ObjLoadedInPostLoad.Reset(); + } } LastObjectWorkWasPerformedOn = Object; @@ -1916,16 +1933,16 @@ EAsyncPackageState::Type FAsyncPackage::PostLoadDeferredObjects(double InTickSta Result = (DeferredPostLoadIndex == DeferredPostLoadObjects.Num()) ? EAsyncPackageState::Complete : EAsyncPackageState::TimeOut; if (Result == EAsyncPackageState::Complete) { - // Clear async loading flags (we still want RF_Async, but RF_AsyncLoading can be cleared) + // Clear async loading flags (we still want RF_Async, but EInternalObjectFlags::AsyncLoading can be cleared) for (UObject* Object : DeferredFinalizeObjects) { - Object->AtomicallyClearFlags(RF_AsyncLoading); + Object->AtomicallyClearInternalFlags(EInternalObjectFlags::AsyncLoading); } // Mark package as having been fully loaded and update load time. if (LinkerRoot && !bLoadHasFailed) { - LinkerRoot->AtomicallyClearFlags(RF_AsyncLoading); + LinkerRoot->AtomicallyClearInternalFlags(EInternalObjectFlags::AsyncLoading); LinkerRoot->MarkAsFullyLoaded(); LinkerRoot->SetLoadTime(FPlatformTime::Seconds() - LoadStartTime); @@ -1941,7 +1958,7 @@ EAsyncPackageState::Type FAsyncPackage::PostLoadDeferredObjects(double InTickSta } /** - * Finish up objects and state, which means clearing the RF_AsyncLoading flag on newly created ones + * Finish up objects and state, which means clearing the EInternalObjectFlags::AsyncLoading flag on newly created ones * * @return true */ diff --git a/Engine/Source/Runtime/CoreUObject/Private/Serialization/BulkData.cpp b/Engine/Source/Runtime/CoreUObject/Private/Serialization/BulkData.cpp index 258d6f7de7c9..f4ce0ed7a96e 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/Serialization/BulkData.cpp +++ b/Engine/Source/Runtime/CoreUObject/Private/Serialization/BulkData.cpp @@ -13,7 +13,6 @@ #define TRACK_BULKDATA_USE 0 DECLARE_STATS_GROUP(TEXT("Bulk Data"), STATGROUP_BulkData, STATCAT_Advanced); -DECLARE_DWORD_ACCUMULATOR_STAT(TEXT("Leaked async buffers"), STAT_BulkDataAsyncLeaks, STATGROUP_BulkData); #if TRACK_BULKDATA_USE @@ -104,8 +103,7 @@ FUntypedBulkData::FUntypedBulkData( const FUntypedBulkData& Other ) // Prepare bulk data pointer. Can't call any functions that would call virtual GetElementSize on "this" as // we're in the constructor of the base class and would hence call a pure virtual. ElementCount = Other.ElementCount; - check(bShouldFreeOnEmpty); - BulkData = FMemory::Realloc( BulkData, Other.GetBulkDataSize(), BulkDataAlignment ); + BulkData.Reallocate( Other.GetBulkDataSize(), BulkDataAlignment ); // Copy data over. Copy( Other ); @@ -129,17 +127,8 @@ FUntypedBulkData::~FUntypedBulkData() } // Free memory. - if (bShouldFreeOnEmpty) - { - FMemory::Free(BulkData); - } - if (BulkData != BulkDataAsync) - { - FMemory::Free(BulkDataAsync); - DEC_DWORD_STAT_BY(STAT_BulkDataAsyncLeaks, 1); - } - BulkData = nullptr; - BulkDataAsync = nullptr; + BulkData .Deallocate(); + BulkDataAsync.Deallocate(); #if WITH_EDITOR // Detach from archive. @@ -346,7 +335,7 @@ ECompressionFlags FUntypedBulkData::GetDecompressionFlags() const */ bool FUntypedBulkData::IsBulkDataLoaded() const { - return BulkData != NULL; + return !!BulkData; } bool FUntypedBulkData::IsAsyncLoadingComplete() @@ -369,7 +358,8 @@ bool FUntypedBulkData::IsAvailableForUse() const void FUntypedBulkData::ResetAsyncData() { - BulkDataAsync = nullptr; + // Async data should be released by the time we get here + check(!BulkDataAsync); SerializeFuture = TFuture(); } @@ -393,16 +383,12 @@ void FUntypedBulkData::GetCopy( void** Dest, bool bDiscardInternalCopy ) if( BulkData ) { // Copy data into destination memory. - FMemory::Memcpy( *Dest, BulkData, GetBulkDataSize() ); + FMemory::Memcpy( *Dest, BulkData.Get(), GetBulkDataSize() ); // Discard internal copy if wanted and we're still attached to an archive or if we're // single use bulk data. if( bDiscardInternalCopy && (CanLoadFromDisk() || (BulkDataFlags & BULKDATA_SingleUse)) ) { - if( bShouldFreeOnEmpty ) - { - FMemory::Free( BulkData ); - } - BulkData = NULL; + BulkData.Deallocate(); } } // Data isn't currently loaded so we need to load it from disk. @@ -418,8 +404,7 @@ void FUntypedBulkData::GetCopy( void** Dest, bool bDiscardInternalCopy ) if (!BulkData && SerializeFuture.IsValid()) { WaitForAsyncLoading(); - BulkData = BulkDataAsync; - DEC_DWORD_STAT_BY(STAT_BulkDataAsyncLeaks, 1); + BulkData = MoveTemp(BulkDataAsync); ResetAsyncData(); } // The data is already loaded so we can simply use a mempcy. @@ -430,26 +415,43 @@ void FUntypedBulkData::GetCopy( void** Dest, bool bDiscardInternalCopy ) // also do this if the data is single use like e.g. when uploading texture data. if( bDiscardInternalCopy && (CanLoadFromDisk()|| (BulkDataFlags & BULKDATA_SingleUse)) ) { - *Dest = BulkData; - BulkData = nullptr; + *Dest = BulkData.ReleaseWithoutDeallocating(); ResetAsyncData(); } // Can't/ Don't discard so we need to allocate and copy. else { - // Allocate enough memory for data... - *Dest = FMemory::Malloc( GetBulkDataSize(), BulkDataAlignment ); - // ... and copy it into memory now pointed to by out parameter. - FMemory::Memcpy( *Dest, BulkData, GetBulkDataSize() ); + int32 BulkDataSize = GetBulkDataSize(); + if (BulkDataSize != 0) + { + // Allocate enough memory for data... + *Dest = FMemory::Malloc( BulkDataSize, BulkDataAlignment ); + + // ... and copy it into memory now pointed to by out parameter. + FMemory::Memcpy( *Dest, BulkData.Get(), BulkDataSize ); + } + else + { + *Dest = nullptr; + } } } // Data isn't currently loaded so we need to load it from disk. else { - // Allocate enougn memory for data... - *Dest = FMemory::Malloc( GetBulkDataSize(), BulkDataAlignment ); - // ... and directly load into it. - LoadDataIntoMemory( *Dest ); + int32 BulkDataSize = GetBulkDataSize(); + if (BulkDataSize != 0) + { + // Allocate enough memory for data... + *Dest = FMemory::Malloc( BulkDataSize, BulkDataAlignment ); + + // ... and directly load into it. + LoadDataIntoMemory( *Dest ); + } + else + { + *Dest = nullptr; + } } } } @@ -492,7 +494,7 @@ void* FUntypedBulkData::Lock( uint32 LockFlags ) UE_LOG(LogSerialization, Fatal,TEXT("Unknown lock flag %i"),LockFlags); } - return BulkData; + return BulkData.Get(); } const void* FUntypedBulkData::LockReadOnly() const @@ -508,7 +510,7 @@ const void* FUntypedBulkData::LockReadOnly() const mutable_this->LockStatus = LOCKSTATUS_ReadOnlyLock; check(BulkData); - return BulkData; + return BulkData.Get(); } /** @@ -523,9 +525,8 @@ void* FUntypedBulkData::Realloc( int32 InElementCount ) check( LockStatus == LOCKSTATUS_ReadWriteLock ); // Progate element count and reallocate data based on new size. ElementCount = InElementCount; - check(bShouldFreeOnEmpty); - BulkData = FMemory::Realloc( BulkData, GetBulkDataSize(), BulkDataAlignment ); - return BulkData; + BulkData.Reallocate( GetBulkDataSize(), BulkDataAlignment ); + return BulkData.Get(); } /** @@ -544,11 +545,7 @@ void FUntypedBulkData::Unlock() const // Free pointer if we're guaranteed to only to access the data once. if (BulkDataFlags & BULKDATA_SingleUse) { - if (bShouldFreeOnEmpty) - { - FMemory::Free(mutable_this->BulkData); - } - mutable_this->BulkData = NULL; + mutable_this->BulkData.Deallocate(); } } @@ -572,11 +569,7 @@ void FUntypedBulkData::RemoveBulkData() // Resize to 0 elements. ElementCount = 0; - if( ShouldFreeOnEmpty() ) - { - FMemory::Free( BulkData ); - } - BulkData = NULL; + BulkData.Deallocate(); } /** @@ -648,16 +641,6 @@ void FUntypedBulkData::ClearBulkDataFlags( uint32 BulkDataFlagsToClear ) BulkDataFlags &= ~BulkDataFlagsToClear; } -/** - * BulkData memory allocated from a resource should only be freed by the resource - * - * @return true if bulk data should free allocated memory - */ -bool FUntypedBulkData::ShouldFreeOnEmpty() const -{ - return bShouldFreeOnEmpty; -} - /*----------------------------------------------------------------------------- Serialization. @@ -671,19 +654,14 @@ void FUntypedBulkData::StartSerializingBulkData(FArchive& Ar, UObject* Owner, in // Async SerializeFuture = Async(EAsyncExecution::ThreadPool, [=]() { - BulkDataAsync = GetBulkDataResourceMemory(Owner, Idx); - if (!BulkDataAsync) - { - BulkDataAsync = FMemory::Realloc(BulkDataAsync, GetBulkDataSize(), BulkDataAlignment); - } - INC_DWORD_STAT_BY(STAT_BulkDataAsyncLeaks, 1); + BulkDataAsync.Reallocate(GetBulkDataSize(), BulkDataAlignment); FArchive* FileReaderAr = IFileManager::Get().CreateFileReader(*Filename, FILEREAD_Silent); checkf(FileReaderAr != NULL, TEXT("Attempted to load bulk data from an invalid filename '%s'."), *Filename); // Seek to the beginning of the bulk data in the file. FileReaderAr->Seek(BulkDataOffsetInFile); - SerializeBulkData(*FileReaderAr, BulkDataAsync); + SerializeBulkData(*FileReaderAr, BulkDataAsync.Get()); delete FileReaderAr; return true; @@ -746,11 +724,10 @@ void FUntypedBulkData::Serialize( FArchive& Ar, UObject* Owner, int32 Idx ) Ar << ElementCount; // Allocate bulk data. - check(bShouldFreeOnEmpty); - BulkData = FMemory::Realloc( BulkData, GetBulkDataSize(), BulkDataAlignment ); + BulkData.Reallocate( GetBulkDataSize(), BulkDataAlignment ); // Deserialize bulk data. - SerializeBulkData( Ar, BulkData ); + SerializeBulkData( Ar, BulkData.Get() ); } else if(Ar.IsSaving()) { @@ -768,7 +745,7 @@ void FUntypedBulkData::Serialize( FArchive& Ar, UObject* Owner, int32 Idx ) MakeSureBulkDataIsLoaded(); // Serialize bulk data. - SerializeBulkData(Ar, BulkData); + SerializeBulkData(Ar, BulkData.Get()); } } } @@ -843,14 +820,9 @@ void FUntypedBulkData::Serialize( FArchive& Ar, UObject* Owner, int32 Idx ) else { // Force non-lazy loading of inline bulk data to prevent PostLoad spikes. - // Memory for bulk data can come from preallocated GPU-accessible resource memory or default to system memory - BulkData = GetBulkDataResourceMemory(Owner, Idx); - if (!BulkData) - { - BulkData = FMemory::Realloc(BulkData, GetBulkDataSize(), BulkDataAlignment); - } + BulkData.Reallocate(GetBulkDataSize(), BulkDataAlignment); // if the payload is stored inline, just serialize it - SerializeBulkData(Ar, BulkData); + SerializeBulkData(Ar, BulkData.Get()); } } } @@ -865,34 +837,29 @@ void FUntypedBulkData::Serialize( FArchive& Ar, UObject* Owner, int32 Idx ) { StartSerializingBulkData(Ar, Owner, Idx, bPayloadInline); } - else - { - // memory for bulk data can come from preallocated GPU-accessible resource memory or default to system memory - BulkData = GetBulkDataResourceMemory(Owner,Idx); - if( !BulkData ) - { - BulkData = FMemory::Realloc( BulkData, GetBulkDataSize(), BulkDataAlignment ); - } - - if (bPayloadInline) - { - // if the payload is stored inline, just serialize it - SerializeBulkData( Ar, BulkData ); - } - else - { - // if the payload is NOT stored inline ... - - // store the current file offset - int64 CurOffset = Ar.Tell(); - // seek to the location in the file where the payload is stored - Ar.Seek(BulkDataOffsetInFile); - // serialize the payload - SerializeBulkData( Ar, BulkData ); - // seek to the location we came from - Ar.Seek(CurOffset); - } - } + else + { + BulkData.Reallocate( GetBulkDataSize(), BulkDataAlignment ); + + if (bPayloadInline) + { + // if the payload is stored inline, just serialize it + SerializeBulkData( Ar, BulkData.Get() ); + } + else + { + // if the payload is NOT stored inline ... + + // store the current file offset + int64 CurOffset = Ar.Tell(); + // seek to the location in the file where the payload is stored + Ar.Seek(BulkDataOffsetInFile); + // serialize the payload + SerializeBulkData( Ar, BulkData.Get() ); + // seek to the location we came from + Ar.Seek(CurOffset); + } + } } } // We're saving to the persistent archive. @@ -972,7 +939,7 @@ void FUntypedBulkData::Serialize( FArchive& Ar, UObject* Owner, int32 Idx ) int64 SavedBulkDataStartPos = Ar.Tell(); // Serialize bulk data. - SerializeBulkData( Ar, BulkData ); + SerializeBulkData( Ar, BulkData.Get() ); // store the payload endpos int64 SavedBulkDataEndPos = Ar.Tell(); @@ -1102,7 +1069,7 @@ void FUntypedBulkData::Copy( const FUntypedBulkData& Other ) check(BulkData); check(ElementCount == Other.GetElementCount() ); // Copy from src to dest. - FMemory::Memcpy( BulkData, Other.BulkData, Other.GetBulkDataSize() ); + FMemory::Memcpy( BulkData.Get(), Other.BulkData.Get(), Other.GetBulkDataSize() ); } } @@ -1116,10 +1083,7 @@ void FUntypedBulkData::InitializeMemberVariables() BulkDataOffsetInFile = INDEX_NONE; BulkDataSizeOnDisk = INDEX_NONE; BulkDataAlignment = DEFAULT_ALIGNMENT; - BulkData = nullptr; - BulkDataAsync = nullptr; LockStatus = LOCKSTATUS_Unlocked; - bShouldFreeOnEmpty = true; #if WITH_EDITOR Linker = nullptr; AttachedAr = nullptr; @@ -1249,21 +1213,20 @@ void FUntypedBulkData::MakeSureBulkDataIsLoaded() if (SerializeFuture.IsValid()) { WaitForAsyncLoading(); - BulkData = BulkDataAsync; - DEC_DWORD_STAT_BY(STAT_BulkDataAsyncLeaks, 1); + BulkData = MoveTemp(BulkDataAsync); ResetAsyncData(); } else { const int32 BytesNeeded = GetBulkDataSize(); // Allocate memory for bulk data. - BulkData = FMemory::Malloc(BytesNeeded, BulkDataAlignment); + BulkData.Reallocate(BytesNeeded, BulkDataAlignment); // Only load if there is something to load. E.g. we might have just created the bulk data array // in which case it starts out with a size of zero. if (BytesNeeded > 0) { - LoadDataIntoMemory(BulkData); + LoadDataIntoMemory(BulkData.Get()); } } } @@ -1286,8 +1249,7 @@ bool FUntypedBulkData::FlushAsyncLoading(void* Dest) if (bIsLoadingAsync) { WaitForAsyncLoading(); - FMemory::Memcpy(Dest, BulkDataAsync, GetBulkDataSize()); - DEC_DWORD_STAT_BY(STAT_BulkDataAsyncLeaks, 1); + FMemory::Memcpy(Dest, BulkDataAsync.Get(), GetBulkDataSize()); } return bIsLoadingAsync; } diff --git a/Engine/Source/Runtime/CoreUObject/Private/Serialization/DuplicateDataWriter.cpp b/Engine/Source/Runtime/CoreUObject/Private/Serialization/DuplicateDataWriter.cpp index 7f01ea59474f..80144ca6be14 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/Serialization/DuplicateDataWriter.cpp +++ b/Engine/Source/Runtime/CoreUObject/Private/Serialization/DuplicateDataWriter.cpp @@ -17,12 +17,14 @@ * @param InInstanceGraph the instancing graph to use when creating the duplicate objects. */ FDuplicateDataWriter::FDuplicateDataWriter( FUObjectAnnotationSparse& InDuplicatedObjects ,TArray& InObjectData,UObject* SourceObject, - UObject* DestObject,EObjectFlags InFlagMask, EObjectFlags InApplyFlags, FObjectInstancingGraph* InInstanceGraph, uint32 InPortFlags ) + UObject* DestObject, EObjectFlags InFlagMask, EObjectFlags InApplyFlags, EInternalObjectFlags InInternalFlagMask, EInternalObjectFlags InApplyInternalFlags, FObjectInstancingGraph* InInstanceGraph, uint32 InPortFlags) : DuplicatedObjectAnnotation(InDuplicatedObjects) , ObjectData(InObjectData) , Offset(0) , FlagMask(InFlagMask) , ApplyFlags(InApplyFlags) +, InternalFlagMask(InInternalFlagMask) +, ApplyInternalFlags(InApplyInternalFlags) , InstanceGraph(InInstanceGraph) { ArIsSaving = true; @@ -132,7 +134,9 @@ UObject* FDuplicateDataWriter::GetDuplicatedObject(UObject* Object, bool bCreate if(DupOuter != NULL) { // The object's outer is being duplicated, create a duplicate of this object. - UObject* NewEmptyDuplicate = StaticConstructObject_Internal(Object->GetClass(), DupOuter, Object->GetFName(), ApplyFlags|Object->GetMaskedFlags(FlagMask), + UObject* NewEmptyDuplicate = StaticConstructObject_Internal(Object->GetClass(), DupOuter, Object->GetFName(), + ApplyFlags | Object->GetMaskedFlags(FlagMask), + ApplyInternalFlags | (Object->GetInternalFlags() & InternalFlagMask), Object->GetArchetype(), true, InstanceGraph); Result = AddDuplicate(Object, NewEmptyDuplicate); diff --git a/Engine/Source/Runtime/CoreUObject/Private/Serialization/FindReferencersArchive.cpp b/Engine/Source/Runtime/CoreUObject/Private/Serialization/FindReferencersArchive.cpp index 3db00427bb8d..ff558f6eace2 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/Serialization/FindReferencersArchive.cpp +++ b/Engine/Source/Runtime/CoreUObject/Private/Serialization/FindReferencersArchive.cpp @@ -12,7 +12,7 @@ * @param InTargetObjects array of objects to search for references to * @param bFindAlsoWeakReferences should we also look into weak references? */ -FFindReferencersArchive::FFindReferencersArchive(UObject* InPotentialReferencer, TArray InTargetObjects, bool bFindAlsoWeakReferences) +FFindReferencersArchive::FFindReferencersArchive(UObject* InPotentialReferencer, const TArray& InTargetObjects, bool bFindAlsoWeakReferences) { // use the optimized RefLink to skip over properties which don't contain object references ArIsObjectReferenceCollector = true; diff --git a/Engine/Source/Runtime/CoreUObject/Private/UObject/Class.cpp b/Engine/Source/Runtime/CoreUObject/Private/UObject/Class.cpp index f8dd31148a66..15eae9eb5d2b 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/UObject/Class.cpp +++ b/Engine/Source/Runtime/CoreUObject/Private/UObject/Class.cpp @@ -28,6 +28,23 @@ DEFINE_LOG_CATEGORY(LogClass); ////////////////////////////////////////////////////////////////////////// +FThreadSafeBool& InternalSafeGetTokenStreamDirtyFlag() +{ + static FThreadSafeBool TokenStreamDirty(true); + return TokenStreamDirty; +} + +bool SetTokenStreamMaybeDirty(bool bDirty) +{ + bool bResult = InternalSafeGetTokenStreamDirtyFlag().AtomicSet(bDirty); + return bResult; +} + +bool IsTokenStreamDirty() +{ + return InternalSafeGetTokenStreamDirtyFlag(); +} + /** * Shared function called from the various InitializePrivateStaticClass functions generated my the IMPLEMENT_CLASS macro. */ @@ -1712,7 +1729,7 @@ void UStruct::TagSubobjects(EObjectFlags NewFlags) for (TFieldIterator It(this, EFieldIteratorFlags::ExcludeSuper); It; ++It) { UProperty* Property = *It; - if (Property && !Property->HasAnyFlags(GARBAGE_COLLECTION_KEEPFLAGS | RF_RootSet)) + if (Property && !Property->HasAnyFlags(GARBAGE_COLLECTION_KEEPFLAGS) && !Property->IsRooted()) { Property->SetFlags(NewFlags); Property->TagSubobjects(NewFlags); @@ -2874,7 +2891,7 @@ void UClass::TagSubobjects(EObjectFlags NewFlags) { Super::TagSubobjects(NewFlags); - if (ClassDefaultObject && !ClassDefaultObject->HasAnyFlags(GARBAGE_COLLECTION_KEEPFLAGS | RF_RootSet)) + if (ClassDefaultObject && !ClassDefaultObject->HasAnyFlags(GARBAGE_COLLECTION_KEEPFLAGS) && !ClassDefaultObject->IsRooted()) { ClassDefaultObject->SetFlags(NewFlags); ClassDefaultObject->TagSubobjects(NewFlags); @@ -2888,7 +2905,7 @@ void UClass::Bind() { UStruct::Bind(); - if( !GIsUCCMakeStandaloneHeaderGenerator && !ClassConstructor && HasAnyFlags(RF_Native) ) + if( !GIsUCCMakeStandaloneHeaderGenerator && !ClassConstructor && IsNative() ) { UE_LOG(LogClass, Fatal, TEXT("Can't bind to native class %s"), *GetPathName() ); } @@ -3469,7 +3486,10 @@ void UClass::Serialize( FArchive& Ar ) } } - Ar << ClassGeneratedBy; + if (!Ar.IsIgnoringClassGeneratedByRef()) + { + Ar << ClassGeneratedBy; + } if(Ar.IsLoading()) { @@ -3684,6 +3704,8 @@ UObject* UClass::GetArchetypeForCDO() const void UClass::PurgeClass(bool bRecompilingOnLoad) { + SetTokenStreamMaybeDirty(true); + ClassConstructor = nullptr; #if WITH_HOT_RELOAD_CTORS ClassVTableHelperCtorCaller = nullptr; @@ -3790,32 +3812,35 @@ UClass::UClass(const FObjectInitializer& ObjectInitializer) , ClassDefaultObject(NULL) { // If you add properties here, please update the other constructors and PurgeClass() + SetTokenStreamMaybeDirty(true); } /** * Create a new UClass given its superclass. */ -UClass::UClass(const FObjectInitializer& ObjectInitializer, UClass* InBaseClass ) -: UStruct( ObjectInitializer, InBaseClass ) +UClass::UClass(const FObjectInitializer& ObjectInitializer, UClass* InBaseClass) +: UStruct(ObjectInitializer, InBaseClass) , ClassUnique(0) , ClassFlags(0) , ClassCastFlags(0) -, ClassWithin( UObject::StaticClass() ) +, ClassWithin(UObject::StaticClass()) , ClassGeneratedBy(NULL) , bCooked(false) , ClassDefaultObject(NULL) { + SetTokenStreamMaybeDirty(true); + // If you add properties here, please update the other constructors and PurgeClass() UClass* ParentClass = GetSuperClass(); - if( ParentClass ) + if (ParentClass) { ClassWithin = ParentClass->ClassWithin; Bind(); // if this is a native class, we may have defined a StaticConfigName() which overrides // the one from the parent class, so get our config name from there - if ( HasAnyFlags(RF_Native) ) + if (IsNative()) { ClassConfigName = StaticConfigName(); } @@ -3868,6 +3893,8 @@ UClass::UClass // complains about this operation, but AFAIK it is safe (and we've been doing it a long time) // so the warning has been disabled for now: *(const TCHAR**)&ClassConfigName = InConfigName; //-V580 + + SetTokenStreamMaybeDirty(true); } #if WITH_HOT_RELOAD @@ -3886,6 +3913,8 @@ bool UClass::HotReloadPrivateStaticClass( class UClass* TClass_WithinClass_StaticClass ) { + SetTokenStreamMaybeDirty(true); + if (InSize != PropertiesSize) { UClass::GetDefaultPropertiesFeedbackContext().Logf(ELogVerbosity::Warning, TEXT("Property size mismatch. Will not update class %s (was %d, new %d)."), *GetName(), PropertiesSize, InSize); @@ -3966,7 +3995,7 @@ bool UClass::HotReloadPrivateStaticClass( int32 CountClass = 0; for ( FRawObjectIterator It; It; ++It ) { - UObject* Target = *It; + UObject* Target = static_cast(It->Object); if (OldVTable == *(void**)Target) { *(void**)Target = NewVTable; @@ -4332,7 +4361,7 @@ void GetPrivateStaticClassBody( InClassFlags, InClassCastFlags, InConfigName, - EObjectFlags(RF_Public | RF_Standalone | RF_Transient | RF_Native | RF_RootSet), + EObjectFlags(RF_Public | RF_Standalone | RF_Transient | RF_MarkAsNative | RF_MarkAsRootSet), InClassConstructor, #if WITH_HOT_RELOAD_CTORS InClassVTableHelperCtorCaller, @@ -4353,7 +4382,7 @@ void GetPrivateStaticClassBody( InClassFlags, InClassCastFlags, InConfigName, - EObjectFlags(RF_Public | RF_Standalone | RF_Transient | RF_Native | RF_Dynamic), + EObjectFlags(RF_Public | RF_Standalone | RF_Transient | RF_MarkAsNative | RF_Dynamic), InClassConstructor, #if WITH_HOT_RELOAD_CTORS InClassVTableHelperCtorCaller, diff --git a/Engine/Source/Runtime/CoreUObject/Private/UObject/CoreNative.cpp b/Engine/Source/Runtime/CoreUObject/Private/UObject/CoreNative.cpp index 7af624755247..4c70afcebbe6 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/UObject/CoreNative.cpp +++ b/Engine/Source/Runtime/CoreUObject/Private/UObject/CoreNative.cpp @@ -186,7 +186,7 @@ UObject* FObjectInstancingGraph::GetInstancedSubobject( UObject* SourceSubobject { // finally, create the component instance InstancedSubobject = StaticConstructObject_Internal(SourceSubobject->GetClass(), SubobjectOuter, - SubobjectName, SubobjectOuter->GetMaskedFlags(RF_PropagateToSubObjects), SourceSubobject, + SubobjectName, SubobjectOuter->GetMaskedFlags(RF_PropagateToSubObjects), EInternalObjectFlags::None, SourceSubobject, true, this); } } diff --git a/Engine/Source/Runtime/CoreUObject/Private/UObject/GCScopeLock.h b/Engine/Source/Runtime/CoreUObject/Private/UObject/GCScopeLock.h new file mode 100644 index 000000000000..9aaec38e8b3a --- /dev/null +++ b/Engine/Source/Runtime/CoreUObject/Private/UObject/GCScopeLock.h @@ -0,0 +1,109 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +/*============================================================================= + Garbage Collection scope lock. +=============================================================================*/ + +#pragma once + +/** Locks all UObject hash tables when performing GC */ +class FGCScopeLock +{ + /** Previous value of the GetGarbageCollectingFlag() */ + bool bPreviousGabageCollectingFlagValue; +public: + + static FThreadSafeBool& GetGarbageCollectingFlag(); + + /** + * We're storing the value of GetGarbageCollectingFlag in the constructor, it's safe as only + * one thread is ever going to be setting it and calling this code - the game thread. + **/ + FORCEINLINE FGCScopeLock() + : bPreviousGabageCollectingFlagValue(GetGarbageCollectingFlag()) + { + void LockUObjectHashTablesForGC(); + LockUObjectHashTablesForGC(); + GetGarbageCollectingFlag() = true; + } + FORCEINLINE ~FGCScopeLock() + { + GetGarbageCollectingFlag() = bPreviousGabageCollectingFlagValue; + void UnlockUObjectHashTablesForGC(); + UnlockUObjectHashTablesForGC(); + } +}; + + +/** +* Garbage Collection synchronization objects +* Will not lock other threads if GC is not running. +* Has the ability to only lock for GC if no other locks are present. +*/ +class FGCCSyncObject +{ + FThreadSafeCounter AsyncCounter; + FThreadSafeCounter GCCounter; + FCriticalSection Critical; +public: + /** Lock on non-game thread. Will block if GC is running. */ + void LockAsync() + { + if (!IsInGameThread()) + { + FScopeLock CriticalLock(&Critical); + + // Wait until GC is done if it's currently running + FPlatformProcess::ConditionalSleep([&]() + { + return GCCounter.GetValue() == 0; + }); + + AsyncCounter.Increment(); + } + } + /** Release lock from non-game thread */ + void UnlockAsync() + { + if (!IsInGameThread()) + { + AsyncCounter.Decrement(); + } + } + /** Lock for GC. Will block if any other thread has locked. */ + void GCLock() + { + FScopeLock CriticalLock(&Critical); + + // Wait until all other threads are done if they're currently holding the lock + FPlatformProcess::ConditionalSleep([&]() + { + return AsyncCounter.GetValue() == 0; + }); + + GCCounter.Increment(); + } + /** Checks if any async thread has a lock */ + bool IsAsyncLocked() + { + return AsyncCounter.GetValue() != 0; + } + /** Lock for GC. Will not block and return false if any other thread has already locked. */ + bool TryGCLock() + { + bool bSuccess = false; + FScopeLock CriticalLock(&Critical); + // If any other thread is currently locking we just exit + if (AsyncCounter.GetValue() == 0) + { + GCCounter.Increment(); + bSuccess = true; + } + return bSuccess; + } + /** Unlock GC */ + void GCUnlock() + { + GCCounter.Decrement(); + } +}; \ No newline at end of file diff --git a/Engine/Source/Runtime/CoreUObject/Private/UObject/GarbageCollection.cpp b/Engine/Source/Runtime/CoreUObject/Private/UObject/GarbageCollection.cpp index 2d548c370316..412dc6f0a683 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/UObject/GarbageCollection.cpp +++ b/Engine/Source/Runtime/CoreUObject/Private/UObject/GarbageCollection.cpp @@ -8,6 +8,7 @@ #include "TaskGraphInterfaces.h" #include "IConsoleManager.h" #include "LinkerPlaceholderClass.h" +#include "UObject/GCScopeLock.h" /*----------------------------------------------------------------------------- Garbage collection. @@ -56,113 +57,14 @@ static bool GIsPurgingObject = false; /** Helpful constant for determining how many token slots we need to store a pointer **/ static const uint32 GNumTokensPerPointer = sizeof(void*) / sizeof(uint32); -/** Locks all UObject hash tables when performing GC */ -class FGCScopeLock -{ - /** Previous value of the GetGarbageCollectingFlag() */ - bool bPreviousGabageCollectingFlagValue; -public: - - static FThreadSafeBool& GetGarbageCollectingFlag(); - - /** - * We're storing the value of GetGarbageCollectingFlag in the constructor, it's safe as only - * one thread is ever going to be setting it and calling this code - the game thread. - **/ - FORCEINLINE FGCScopeLock() - : bPreviousGabageCollectingFlagValue(GetGarbageCollectingFlag()) - { - void LockUObjectHashTablesForGC(); - LockUObjectHashTablesForGC(); - GetGarbageCollectingFlag() = true; - } - FORCEINLINE ~FGCScopeLock() - { - GetGarbageCollectingFlag() = bPreviousGabageCollectingFlagValue; - void UnlockUObjectHashTablesForGC(); - UnlockUObjectHashTablesForGC(); - } -}; FThreadSafeBool& FGCScopeLock::GetGarbageCollectingFlag() { static FThreadSafeBool IsGarbageCollecting(false); return IsGarbageCollecting; } -/** - * Garbage Collection synchronization objects - * Will not lock other threads if GC is not running. - * Has the ability to only lock for GC if no other locks are present. - */ -class FGCCSyncObject -{ - FThreadSafeCounter AsyncCounter; - FThreadSafeCounter GCCounter; - FCriticalSection Critical; -public: - /** Lock on non-game thread. Will block if GC is running. */ - void LockAsync() - { - if (!IsInGameThread()) - { - FScopeLock CriticalLock(&Critical); - - // Wait until GC is done if it's currently running - FPlatformProcess::ConditionalSleep([&]() - { - return GCCounter.GetValue() == 0; - }); +FGCCSyncObject GGarbageCollectionGuardCritical; - AsyncCounter.Increment(); - } - } - /** Release lock from non-game thread */ - void UnlockAsync() - { - if (!IsInGameThread()) - { - AsyncCounter.Decrement(); - } - } - /** Lock for GC. Will block if any other thread has locked. */ - void GCLock() - { - FScopeLock CriticalLock(&Critical); - - // Wait until all other threads are done if they're currently holding the lock - FPlatformProcess::ConditionalSleep([&]() - { - return AsyncCounter.GetValue() == 0; - }); - - GCCounter.Increment(); - } - /** Checks if any async thread has a lock */ - bool IsAsyncLocked() - { - return AsyncCounter.GetValue() != 0; - } - /** Lock for GC. Will not block and return false if any other thread has already locked. */ - bool TryGCLock() - { - bool bSuccess = false; - FScopeLock CriticalLock(&Critical); - // If any other thread is currently locking we just exit - if (AsyncCounter.GetValue() == 0) - { - GCCounter.Increment(); - bSuccess = true; - } - return bSuccess; - } - /** Unlock GC */ - void GCUnlock() - { - GCCounter.Decrement(); - } -}; - -static FGCCSyncObject GGarbageCollectionGuardCritical; FGCScopeGuard::FGCScopeGuard() { GGarbageCollectionGuardCritical.LockAsync(); @@ -403,19 +305,20 @@ static FORCEINLINE void HandleObjectReference(TArray& ObjectsToSeriali return; } + FUObjectItem* ObjectItem = GUObjectArray.ObjectToObjectItem(Object); // Remove references to pending kill objects if we're allowed to do so. - if( Object->HasAnyFlags( RF_PendingKill ) && bAllowReferenceElimination ) + if (ObjectItem->IsPendingKill() && bAllowReferenceElimination) { // Null out reference. Object = NULL; } // Add encountered object reference to list of to be serialized objects if it hasn't already been added. - else if( Object->HasAnyFlags( RF_Unreachable ) ) + else if (ObjectItem->IsUnreachable()) { if( GIsRunningParallelReachability ) { // Mark it as reachable. - if (Object->ThisThreadAtomicallyClearedRFUnreachable()) + if (ObjectItem->ThisThreadAtomicallyClearedRFUnreachable()) { // Add it to the list of objects to serialize. ObjectsToSerialize.Add( Object ); @@ -434,15 +337,15 @@ static FORCEINLINE void HandleObjectReference(TArray& ObjectsToSeriali #endif // Mark it as reachable. - Object->ClearFlags( RF_Unreachable ); + ObjectItem->ClearUnreachable(); // Add it to the list of objects to serialize. ObjectsToSerialize.Add( Object ); } } - if (Object && bStrongReference) + if (bStrongReference) { - Object->ClearFlags(RF_NoStrongReference); + ObjectItem->ClearNoStrongReference(); } #if PERF_DETAILED_PER_CLASS_GC_STATS GCurrentObjectRegularObjectRefs++; @@ -587,6 +490,9 @@ void FReferenceFinder::HandleObjectReference( UObject*& InObject, const UObject* } } +bool SetTokenStreamMaybeDirty(bool bDirty); +bool IsTokenStreamDirty(); + /** * Implementation of parallel realtime garbage collector using recursive subdivision * @@ -654,17 +560,94 @@ public: FArchiveRealtimeGC() {} + /** + * Marks all objects that don't have KeepFlags and EInternalObjectFlags::GarbageCollectionKeepFlags as unreachable + * This function is a template to speed up the case where we don't need to assemble the token stream (saves about 6ms on PS4) + */ + template + void MarkObjectsAsUnreachable(TArray& ObjectsToSerialize, const EObjectFlags KeepFlags) + { + const EInternalObjectFlags FastKeepFlags = EInternalObjectFlags::GarbageCollectionKeepFlags; + + // Iterate over all objects. Note that we iterate over the UObjectArray and usually check only internal flags which + // are part of the array so we don't suffer from cache misses as much as we would if we were to check ObjectFlags. + for (FRawObjectIterator It(true); It; ++It) + { + FUObjectItem* ObjectItem = *It; + checkSlow(ObjectItem); + UObject* Object = (UObject*)ObjectItem->Object; + + // We can't collect garbage during an async load operation and by now all unreachable objects should've been purged. + checkf(!ObjectItem->IsUnreachable(), TEXT("%s"), *Object->GetFullName()); + + // Keep track of how many objects are around. + GObjectCountDuringLastMarkPhase++; + + // Special case handling for objects that are part of the root set. + if (ObjectItem->IsRootSet()) + { + // IsValidLowLevel is extremely slow in this loop so only do it in debug + checkSlow(Object->IsValidLowLevel()); + // We cannot use RF_PendingKill on objects that are part of the root set. + checkCode(if (ObjectItem->IsPendingKill()) { UE_LOG(LogGarbage, Fatal, TEXT("Object %s is part of root set though has been marked RF_PendingKill!"), *Object->GetFullName()); }); + ObjectsToSerialize.Add(Object); + } + // Regular objects. + else + { + bool bMarkAsUnreachable = true; + if (!ObjectItem->IsPendingKill()) + { + // Internal flags are super fast to check + if (ObjectItem->HasAnyFlags(FastKeepFlags)) + { + bMarkAsUnreachable = false; + } + // If KeepFlags is non zero this is going to be very slow due to cache misses + else if (KeepFlags != RF_NoFlags && Object->HasAnyFlags(KeepFlags)) + { + bMarkAsUnreachable = false; + } + } + + // Mark objects as unreachable unless they have any of the passed in KeepFlags set and it's not marked for elimination.. + if (!bMarkAsUnreachable) + { + // IsValidLowLevel is extremely slow in this loop so only do it in debug + checkSlow(Object->IsValidLowLevel()); + ObjectsToSerialize.Add(Object); + } + else + { + ObjectItem->SetFlags(EInternalObjectFlags::Unreachable | EInternalObjectFlags::NoStrongReference); + } + } + + if (bAssembleTokenStream) + { + // Compile will strip this out when we don't need to update the token stream since this is a template. + // Otherwise the GC will suffer a big performance hit. + if (UClass* Class = dynamic_cast(Object)) + { + if (!Class->HasAnyClassFlags(CLASS_TokenStreamAssembled)) + { + Class->AssembleReferenceTokenStream(); + check(Class->HasAnyClassFlags(CLASS_TokenStreamAssembled)); + } + } + } + } + } + /** * Performs reachability analysis. * * @param KeepFlags Objects with these flags will be kept regardless of being referenced or not */ - void PerformReachabilityAnalysis( EObjectFlags KeepFlags, bool bForceSingleThreaded = false ) - { + void PerformReachabilityAnalysis(EObjectFlags KeepFlags, bool bForceSingleThreaded = false) + { DECLARE_SCOPE_CYCLE_COUNTER( TEXT( "FArchiveRealtimeGC::PerformReachabilityAnalysis" ), STAT_FArchiveRealtimeGC_PerformReachabilityAnalysis, STATGROUP_GC ); - UObject* CurrentObject = NULL; - /** Growing array of objects that require serialization */ TArray& ObjectsToSerialize = *FGCArrayPool::Get().GetArrayFromPool(); @@ -679,50 +662,14 @@ public: ObjectsToSerialize.Add(FGCObject::GGCObjectReferencer); } - for ( FRawObjectIterator It(true); It; ++It ) + if (!IsTokenStreamDirty()) { - UObject* Object = *It; - - //@todo UE4 - A prefetch was removed here. Re-add it. It wasn't right anyway, since it was ten items ahead and the consoles on have 8 prefetch slots - - // We can't collect garbage during an async load operation and by now all unreachable objects should've been purged. - checkf( !Object->HasAnyFlags(RF_Unreachable), TEXT("%s"), *Object->GetFullName() ); - - // Keep track of how many objects are around. - GObjectCountDuringLastMarkPhase++; - - // Special case handling for objects that are part of the root set. - if( Object->HasAnyFlags( RF_RootSet ) ) - { - check(Object->IsValidLowLevel()); - // We cannot use RF_PendingKill on objects that are part of the root set. - checkCode( if( Object->HasAnyFlags( RF_PendingKill ) ) { UE_LOG(LogGarbage, Fatal, TEXT("Object %s is part of root set though has been marked RF_PendingKill!"), *Object->GetFullName() ); } ); - ObjectsToSerialize.Add( Object ); - } - // Regular objects. - else - { - // Mark objects as unreachable unless they have any of the passed in KeepFlags set and it's not marked for elimination.. - if( Object->HasAnyFlags( KeepFlags ) && !Object->HasAnyFlags( RF_PendingKill ) ) - { - check(Object->IsValidLowLevel()); - ObjectsToSerialize.Add( Object ); - } - else - { - Object->SetFlags(RF_Unreachable | RF_NoStrongReference); - } - } - - // Assemble token stream for UClass objects. This is only done once for each class. - if (UClass* Class = dynamic_cast(Object)) - { - if (!Class->HasAnyClassFlags(CLASS_TokenStreamAssembled)) - { - Class->AssembleReferenceTokenStream(); - check(Class->HasAnyClassFlags(CLASS_TokenStreamAssembled)); - } - } + MarkObjectsAsUnreachable(ObjectsToSerialize, KeepFlags); + } + else + { + SetTokenStreamMaybeDirty(false); + MarkObjectsAsUnreachable(ObjectsToSerialize, KeepFlags); } if( ObjectsToSerialize.Num() ) @@ -1104,12 +1051,14 @@ void IncrementalPurgeGarbage( bool bUseTimeLimit, float TimeLimit ) while( GObjCurrentPurgeObjectIndex ) { - UObject* Object = *GObjCurrentPurgeObjectIndex; + FUObjectItem* ObjectItem = *GObjCurrentPurgeObjectIndex; + checkSlow(ObjectItem); //@todo UE4 - A prefetch was removed here. Re-add it. It wasn't right anyway, since it was ten items ahead and the consoles on have 8 prefetch slots - if( Object->HasAnyFlags(RF_Unreachable) ) + if (ObjectItem->IsUnreachable()) { + UObject* Object = static_cast(ObjectItem->Object); // Object should always have had BeginDestroy called on it and never already be destroyed check( Object->HasAnyFlags( RF_BeginDestroyed ) && !Object->HasAnyFlags( RF_FinishDestroyed ) ); @@ -1133,7 +1082,7 @@ void IncrementalPurgeGarbage( bool bUseTimeLimit, float TimeLimit ) // a resource, so we don't want to block iteration while waiting on the render thread. // Add the object index to our list of objects to revisit after we process everything else - GGCObjectsPendingDestruction.Add( *GObjCurrentPurgeObjectIndex ); + GGCObjectsPendingDestruction.Add(Object); GGCObjectsPendingDestructionCount++; } } @@ -1165,7 +1114,7 @@ void IncrementalPurgeGarbage( bool bUseTimeLimit, float TimeLimit ) UObject* Object = GGCObjectsPendingDestruction[ CurPendingObjIndex ]; // Object should never have been added to the list if it failed this criteria - check( Object != NULL && Object->HasAnyFlags( RF_Unreachable ) ); + check( Object != NULL && Object->IsUnreachable() ); // Object should always have had BeginDestroy called on it and never already be destroyed check( Object->HasAnyFlags( RF_BeginDestroyed ) && !Object->HasAnyFlags( RF_FinishDestroyed ) ); @@ -1252,9 +1201,11 @@ void IncrementalPurgeGarbage( bool bUseTimeLimit, float TimeLimit ) { //@todo UE4 - A prefetch was removed here. Re-add it. It wasn't right anyway, since it was ten items ahead and the consoles on have 8 prefetch slots - UObject* Object = *GObjCurrentPurgeObjectIndex; - if( Object->HasAnyFlags(RF_Unreachable) ) + FUObjectItem* ObjectItem = *GObjCurrentPurgeObjectIndex; + checkSlow(ObjectItem); + if (ObjectItem->IsUnreachable()) { + UObject* Object = (UObject*)ObjectItem->Object; check(Object->HasAllFlags(RF_FinishDestroyed|RF_BeginDestroyed)); GIsPurgingObject = true; Object->~UObject(); @@ -1412,7 +1363,7 @@ void CollectGarbageInternal(EObjectFlags KeepFlags, bool bPerformFullPurge) for( int32 ReferenceIndex=0; ReferenceIndexHasAnyFlags(RF_RootSet) || UObjectArray.IsDisregardForGC(ReferencedObject))) + if (ReferencedObject && !(ReferencedObject->IsRooted() || UObjectArray.IsDisregardForGC(ReferencedObject))) { UE_LOG(LogGarbage, Warning, TEXT("Disregard for GC object %s referencing %s which is not part of root set"), *Object->GetFullName(), @@ -1464,16 +1415,18 @@ void CollectGarbageInternal(EObjectFlags KeepFlags, bool bPerformFullPurge) { //@todo UE4 - A prefetch was removed here. Re-add it. It wasn't right anyway, since it was ten items ahead and the consoles on have 8 prefetch slots - UObject* Object = *It; - if( Object->HasAnyFlags( RF_Unreachable ) ) + FUObjectItem* ObjectItem = *It; + checkSlow(ObjectItem); + if (ObjectItem->IsUnreachable()) { // Begin the object's asynchronous destruction. + UObject* Object = (UObject*)ObjectItem->Object; Object->ConditionalBeginDestroy(); } - else if (Object->HasAnyFlags(RF_NoStrongReference)) + else if (ObjectItem->IsNoStrongReference()) { - Object->ClearFlags(RF_NoStrongReference); - Object->SetFlags(RF_PendingKill); + ObjectItem->ClearNoStrongReference(); + ObjectItem->SetPendingKill(); } } UE_LOG(LogGarbage, Log, TEXT("%f ms for unhashing unreachable objects"), (FPlatformTime::Seconds() - StartTime) * 1000 ); diff --git a/Engine/Source/Runtime/CoreUObject/Private/UObject/LinkerLoad.cpp b/Engine/Source/Runtime/CoreUObject/Private/UObject/LinkerLoad.cpp index 319fcff6f3b3..4e063ac8e83c 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/UObject/LinkerLoad.cpp +++ b/Engine/Source/Runtime/CoreUObject/Private/UObject/LinkerLoad.cpp @@ -2138,7 +2138,7 @@ void FLinkerLoad::GatherImportDependencies(int32 ImportIndex, TSetHasAnyFlags(RF_Native) && (!Import.XObject->HasAnyFlags(RF_ClassDefaultObject) || !Import.XObject->GetClass()->HasAllFlags(EObjectFlags(RF_Public|RF_Native|RF_Transient)))) || + (Import.XObject != NULL && !Import.XObject->IsNative() && (!Import.XObject->HasAnyFlags(RF_ClassDefaultObject) || !(Import.XObject->GetClass()->HasAllFlags(EObjectFlags(RF_Public | RF_Transient)) && Import.XObject->GetClass()->IsNative()))) || (Import.SourceLinker != NULL && Import.SourceIndex != INDEX_NONE); // make sure it succeeded @@ -2148,7 +2148,7 @@ void FLinkerLoad::GatherImportDependencies(int32 ImportIndex, TSetGetClass()->HasAnyClassFlags(CLASS_Intrinsic))) { UE_LOG(LogLinker, Warning, TEXT("VerifyImportInner failed [(%x, %d), (%x, %d)] for %s with linker: %s"), - Import.XObject, Import.XObject ? (Import.XObject->HasAnyFlags(RF_Native) ? 1 : 0) : 0, + Import.XObject, Import.XObject ? (Import.XObject->IsNative() ? 1 : 0) : 0, Import.SourceLinker, Import.SourceIndex, *GetImportFullName(ImportIndex), *this->Filename ); } @@ -2723,7 +2723,7 @@ bool FLinkerLoad::VerifyImportInner(const int32 ImportIndex, FString& WarningSuf UObject* FindObject = StaticFindObject(FindClass, FindOuter, *Import.ObjectName.ToString()); // Reference to in memory-only package's object, native transient class or CDO of such a class. - bool bIsInMemoryOnlyOrNativeTransient = bCameFromMemoryOnlyPackage || (FindObject != NULL && (FindObject->HasAllFlags(EObjectFlags(RF_Public | RF_Native | RF_Transient)) || (FindObject->HasAnyFlags(RF_ClassDefaultObject) && FindObject->GetClass()->HasAllFlags(EObjectFlags(RF_Public | RF_Native | RF_Transient))))); + bool bIsInMemoryOnlyOrNativeTransient = bCameFromMemoryOnlyPackage || (FindObject != NULL && (FindObject->HasAllFlags(EObjectFlags(RF_Public | RF_MarkAsNative | RF_Transient)) || (FindObject->HasAnyFlags(RF_ClassDefaultObject) && FindObject->GetClass()->HasAllFlags(EObjectFlags(RF_Public | RF_MarkAsNative | RF_Transient))))); // Check for structs which have been moved to another header (within the same class package). if (!FindObject && bIsInMemoryOnlyOrNativeTransient && FindClass == UScriptStruct::StaticClass()) { @@ -3525,7 +3525,7 @@ UObject* FLinkerLoad::CreateExport( int32 Index ) // There are other attempts to force our super struct to load, and I have not verified that they can all be removed // in favor of this one: if (!SuperStruct->HasAnyFlags(RF_LoadCompleted) - && !SuperStruct->HasAnyFlags(RF_Native) + && !SuperStruct->IsNative() && SuperStruct->GetLinker() && Export.SuperIndex.IsImport()) { @@ -3539,14 +3539,14 @@ UObject* FLinkerLoad::CreateExport( int32 Index ) } } - // Only UClass objects and UProperty objects of intrinsic classes can have RF_Native set. Those property objects are never - // serialized so we only have to worry about classes. If we encounter an object that is not a class and has RF_Native set + // Only UClass objects and UProperty objects of intrinsic classes can have Native flag set. Those property objects are never + // serialized so we only have to worry about classes. If we encounter an object that is not a class and has Native flag set // we warn about it and remove the flag. - if( (Export.ObjectFlags & RF_Native) != 0 && !LoadClass->IsChildOf(UField::StaticClass()) ) + if( (Export.ObjectFlags & RF_MarkAsNative) != 0 && !LoadClass->IsChildOf(UField::StaticClass()) ) { - UE_LOG(LogLinker, Warning,TEXT("%s %s has RF_Native set but is not a UField derived class"),*LoadClass->GetName(),*Export.ObjectName.ToString()); - // Remove RF_Native; - Export.ObjectFlags = EObjectFlags(Export.ObjectFlags & ~RF_Native); + UE_LOG(LogLinker, Warning,TEXT("%s %s has RF_MarkAsNative set but is not a UField derived class"),*LoadClass->GetName(),*Export.ObjectName.ToString()); + // Remove RF_MarkAsNative; + Export.ObjectFlags = EObjectFlags(Export.ObjectFlags & ~RF_MarkAsNative); } if ( !LoadClass->HasAnyClassFlags(CLASS_Intrinsic) ) @@ -3777,11 +3777,11 @@ UObject* FLinkerLoad::CreateExport( int32 Index ) if ((ObjectLoadFlags & RF_ClassDefaultObject) != 0) { UClass* SuperClass = LoadClass->GetSuperClass(); - if (SuperClass && !SuperClass->HasAnyFlags(RF_Native)) + if (SuperClass && !SuperClass->IsNative()) { UObject* SuperCDO = SuperClass->GetDefaultObject(); TArray SuperSubObjects; - GetObjectsWithOuter(SuperCDO, SuperSubObjects, /*bIncludeNestedObjects=*/ false, /*ExclusionFlags=*/ RF_Native); + GetObjectsWithOuter(SuperCDO, SuperSubObjects, /*bIncludeNestedObjects=*/ false, /*ExclusionFlags=*/ RF_NoFlags, /*InternalExclusionFlags=*/ EInternalObjectFlags::Native); for (UObject* SubObject : SuperSubObjects) { @@ -3806,9 +3806,14 @@ UObject* FLinkerLoad::CreateExport( int32 Index ) LoadClass, ThisParent, NewName, - EObjectFlags(ObjectLoadFlags | ((FPlatformProperties::RequiresCookedData() && GIsInitialLoad) ? RF_RootSet : 0)), + ObjectLoadFlags, + EInternalObjectFlags::None, Template ); + if (FPlatformProperties::RequiresCookedData() && GIsInitialLoad) + { + Export.Object->AddToRoot(); + } LoadClass = Export.Object->GetClass(); // this may have changed if we are overwriting a CDO component if (NewName != Export.ObjectName) diff --git a/Engine/Source/Runtime/CoreUObject/Private/UObject/LinkerManager.cpp b/Engine/Source/Runtime/CoreUObject/Private/UObject/LinkerManager.cpp index 846b3d5ca838..f4850e1a7566 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/UObject/LinkerManager.cpp +++ b/Engine/Source/Runtime/CoreUObject/Private/UObject/LinkerManager.cpp @@ -142,7 +142,7 @@ void FLinkerManager::DissociateImportsAndForcedExports() for (int32 ImportIndex = 0; ImportIndex < Linker->ImportMap.Num(); ImportIndex++) { FObjectImport& Import = Linker->ImportMap[ImportIndex]; - if (Import.XObject && !Import.XObject->HasAnyFlags(RF_Native)) + if (Import.XObject && !Import.XObject->IsNative()) { Import.XObject = nullptr; } diff --git a/Engine/Source/Runtime/CoreUObject/Private/UObject/LinkerPlaceholderClass.h b/Engine/Source/Runtime/CoreUObject/Private/UObject/LinkerPlaceholderClass.h index 9a1666b46066..fcc21769b2fe 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/UObject/LinkerPlaceholderClass.h +++ b/Engine/Source/Runtime/CoreUObject/Private/UObject/LinkerPlaceholderClass.h @@ -17,7 +17,7 @@ class FObjectInitializer; class ULinkerPlaceholderClass : public UClass, public TLinkerImportPlaceholder { public: - DECLARE_CASTED_CLASS_INTRINSIC_NO_CTOR(ULinkerPlaceholderClass, UClass, /*TStaticFlags =*/0, CoreUObject, /*TStaticCastFlags =*/0, NO_API) + DECLARE_CASTED_CLASS_INTRINSIC_NO_CTOR(ULinkerPlaceholderClass, UClass, /*TStaticFlags =*/0, TEXT("/Script/CoreUObject"), /*TStaticCastFlags =*/0, NO_API) ULinkerPlaceholderClass(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()); diff --git a/Engine/Source/Runtime/CoreUObject/Private/UObject/LinkerPlaceholderExportObject.h b/Engine/Source/Runtime/CoreUObject/Private/UObject/LinkerPlaceholderExportObject.h index 19cfc4989572..71d54de0390c 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/UObject/LinkerPlaceholderExportObject.h +++ b/Engine/Source/Runtime/CoreUObject/Private/UObject/LinkerPlaceholderExportObject.h @@ -16,7 +16,7 @@ class FObjectInitializer; class ULinkerPlaceholderExportObject : public UObject, public FLinkerPlaceholderBase { public: - DECLARE_CASTED_CLASS_INTRINSIC_NO_CTOR(ULinkerPlaceholderExportObject, UObject, /*TStaticFlags =*/0, CoreUObject, /*TStaticCastFlags =*/0, NO_API) + DECLARE_CASTED_CLASS_INTRINSIC_NO_CTOR(ULinkerPlaceholderExportObject, UObject, /*TStaticFlags =*/0, TEXT("/Script/CoreUObject"), /*TStaticCastFlags =*/0, NO_API) ULinkerPlaceholderExportObject(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()); diff --git a/Engine/Source/Runtime/CoreUObject/Private/UObject/LinkerPlaceholderFunction.h b/Engine/Source/Runtime/CoreUObject/Private/UObject/LinkerPlaceholderFunction.h index 344b98bf78a8..7218826091f3 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/UObject/LinkerPlaceholderFunction.h +++ b/Engine/Source/Runtime/CoreUObject/Private/UObject/LinkerPlaceholderFunction.h @@ -11,7 +11,7 @@ class FObjectInitializer; class ULinkerPlaceholderFunction : public UFunction, public TLinkerImportPlaceholder { public: - DECLARE_CASTED_CLASS_INTRINSIC_NO_CTOR(ULinkerPlaceholderFunction, UFunction, /*TStaticFlags =*/0, CoreUObject, /*TStaticCastFlags =*/0, NO_API) + DECLARE_CASTED_CLASS_INTRINSIC_NO_CTOR(ULinkerPlaceholderFunction, UFunction, /*TStaticFlags =*/0, TEXT("/Script/CoreUObject"), /*TStaticCastFlags =*/0, NO_API) ULinkerPlaceholderFunction(const FObjectInitializer& ObjectInitializer); diff --git a/Engine/Source/Runtime/CoreUObject/Private/UObject/Obj.cpp b/Engine/Source/Runtime/CoreUObject/Private/UObject/Obj.cpp index 88625435125d..6b326fed0e1c 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/UObject/Obj.cpp +++ b/Engine/Source/Runtime/CoreUObject/Private/UObject/Obj.cpp @@ -46,7 +46,7 @@ static UPackage* GObjTransientPkg = NULL; #endif UObject::UObject( EStaticConstructor, EObjectFlags InFlags ) -: UObjectBaseUtility(InFlags | RF_Native | RF_RootSet) +: UObjectBaseUtility(InFlags | RF_MarkAsNative | RF_MarkAsRootSet) { #if WITH_HOT_RELOAD_CTORS EnsureNotRetrievingVTablePtr(); @@ -507,7 +507,10 @@ void UObject::GetArchetypeInstances( TArray& Instances ) { // we need to evaluate CDOs as well, but nothing pending kill TArray IterObjects; - GetObjectsOfClass(GetClass(), IterObjects, true, RF_PendingKill); + { + const bool bIncludeNestedObjects = true; + GetObjectsOfClass(GetClass(), IterObjects, bIncludeNestedObjects, RF_NoFlags, EInternalObjectFlags::PendingKill); + } // if this object is the class default object, any object of the same class (or derived classes) could potentially be affected if ( !HasAnyFlags(RF_ArchetypeObject) ) @@ -936,11 +939,11 @@ void UObject::Serialize( FArchive& Ar ) Ar << WasKill; if (WasKill) { - SetFlags( RF_PendingKill ); + MarkPendingKill(); } else { - ClearFlags( RF_PendingKill ); + ClearPendingKill(); } } else if( Ar.IsSaving() ) @@ -1108,7 +1111,7 @@ bool UObject::CanCheckDefaultSubObjects(bool bForceCheck, bool& bResult) bResult = false; // these aren't in a suitable spot in their lifetime for testing bCanCheck = false; } - if (bCanCheck && (HasAnyFlags(RF_NeedLoad | RF_NeedPostLoad | RF_NeedPostLoadSubobjects | RF_Unreachable | RF_PendingKill) || GIsDuplicatingClassForReinstancing)) + if (bCanCheck && (HasAnyFlags(RF_NeedLoad | RF_NeedPostLoad | RF_NeedPostLoadSubobjects) || IsPendingKillOrUnreachable() || GIsDuplicatingClassForReinstancing)) { bResult = true; // these aren't in a suitable spot in their lifetime for testing bCanCheck = false; @@ -1319,7 +1322,7 @@ bool UObject::IsSafeForRootSet() const } // Exclude linkers from root set if we're using seekfree loading - if (!HasAnyFlags(RF_PendingKill)) + if (!IsPendingKill()) { return true; } @@ -1336,7 +1339,7 @@ void UObject::TagSubobjects(EObjectFlags NewFlags) for( TArray::TIterator it(MemberReferences); it; ++it ) { UObject* CurrentObject = *it; - if( CurrentObject && !CurrentObject->HasAnyFlags(GARBAGE_COLLECTION_KEEPFLAGS | RF_RootSet)) + if( CurrentObject && !CurrentObject->HasAnyFlags(GARBAGE_COLLECTION_KEEPFLAGS) && !CurrentObject->IsRooted()) { CurrentObject->SetFlags(NewFlags); CurrentObject->TagSubobjects(NewFlags); @@ -1997,7 +2000,7 @@ void UObject::ReinitializeProperties( UObject* SourceObject/*=NULL*/, FObjectIns // the properties for this object ensures that any cleanup required when an object is reinitialized from defaults occurs properly // for example, when re-initializing UPrimitiveComponents, the component must notify the rendering thread that its data structures are // going to be re-initialized - StaticConstructObject_Internal( GetClass(), GetOuter(), GetFName(), GetFlags(), SourceObject, !HasAnyFlags(RF_ClassDefaultObject), InstanceGraph ); + StaticConstructObject_Internal( GetClass(), GetOuter(), GetFName(), GetFlags(), GetInternalFlags(), SourceObject, !HasAnyFlags(RF_ClassDefaultObject), InstanceGraph ); } @@ -2024,7 +2027,8 @@ static void StaticShutdownAfterError() for( FRawObjectIterator It; It; ++It ) { - It->ShutdownAfterError(); + UObject* Object = static_cast(It->Object); + Object->ShutdownAfterError(); } } } @@ -2111,7 +2115,7 @@ void UObject::OutputReferencers( FOutputDevice& Ar, FReferencerInformationList* ObjectReachability += TEXT(" (root)"); } - if( RefInfo.Referencer->HasAnyFlags(RF_Native) ) + if( RefInfo.Referencer->IsNative() ) { ObjectReachability += TEXT(" (native)"); } @@ -2273,19 +2277,14 @@ static TArray PrivateInitObjectFlagList() DECLARE_OBJECT_FLAG( ClassDefaultObject ) DECLARE_OBJECT_FLAG( ArchetypeObject ) DECLARE_OBJECT_FLAG( Transactional ) - DECLARE_OBJECT_FLAG( Unreachable ) DECLARE_OBJECT_FLAG( Public ) DECLARE_OBJECT_FLAG( TagGarbageTemp ) DECLARE_OBJECT_FLAG( NeedLoad ) - DECLARE_OBJECT_FLAG( AsyncLoading ) DECLARE_OBJECT_FLAG( Transient ) DECLARE_OBJECT_FLAG( Standalone ) - DECLARE_OBJECT_FLAG( RootSet ) DECLARE_OBJECT_FLAG( BeginDestroyed ) DECLARE_OBJECT_FLAG( FinishDestroyed ) DECLARE_OBJECT_FLAG( NeedPostLoad ) - DECLARE_OBJECT_FLAG( Native ) - DECLARE_OBJECT_FLAG( PendingKill ) #undef DECLARE_OBJECT_FLAG #endif return ObjectFlagList; @@ -3044,7 +3043,7 @@ bool StaticExec( UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar ) // Skip objects that are trashed if ((Target->GetOutermost() == GetTransientPackage()) || Target->GetClass()->HasAnyClassFlags(CLASS_NewerVersionExists) - || Target->HasAnyFlags(RF_PendingKill)) + || Target->IsPendingKill()) { continue; } @@ -3704,11 +3703,13 @@ void StaticExit() // Valid object. GObjectCountDuringLastMarkPhase++; - UObject* Obj = *It; + FUObjectItem* ObjItem = *It; + checkSlow(ObjItem); + UObject* Obj = static_cast(ObjItem->Object); if (Obj && !Obj->IsA()) // Skip Structures, properties, etc.. They could be still necessary while GC. { // Mark as unreachable so purge phase will kill it. - It->SetFlags(RF_Unreachable); + ObjItem->SetUnreachable(); } } @@ -3721,11 +3722,13 @@ void StaticExit() // for ( FRawObjectIterator It; It; ++It ) { - UObject* Object = *It; - if( Object->HasAnyFlags( RF_Unreachable ) ) + FUObjectItem* ObjItem = *It; + checkSlow(ObjItem); + if (ObjItem->IsUnreachable()) { // Begin the object's asynchronous destruction. - Object->ConditionalBeginDestroy(); + UObject* Obj = static_cast(ObjItem->Object); + Obj->ConditionalBeginDestroy(); } } @@ -3736,16 +3739,18 @@ void StaticExit() for (FRawObjectIterator It; It; ++It) { // Mark as unreachable so purge phase will kill it. - It->SetFlags(RF_Unreachable); + It->SetUnreachable(); } for (FRawObjectIterator It; It; ++It) { - UObject* Object = *It; - if (Object->HasAnyFlags(RF_Unreachable)) + FUObjectItem* ObjItem = *It; + checkSlow(ObjItem); + if (ObjItem->IsUnreachable()) { // Begin the object's asynchronous destruction. - Object->ConditionalBeginDestroy(); + UObject* Obj = static_cast(ObjItem->Object); + Obj->ConditionalBeginDestroy(); } } @@ -3790,7 +3795,7 @@ void MarkObjectsToDisregardForGC() NumRootObjects++; Object->AddToRoot(); } - else if (Object->HasAnyFlags(RF_RootSet)) + else if (Object->IsRooted()) { Object->RemoveFromRoot(); } @@ -3850,7 +3855,7 @@ void UObject::PreDestroyFromReplication() /** IsNameStableForNetworking means an object can be referred to its path name (relative to outer) over the network */ bool UObject::IsNameStableForNetworking() const { - return HasAnyFlags(RF_WasLoaded | RF_DefaultSubObject | RF_Native) || IsDefaultSubobject(); + return HasAnyFlags(RF_WasLoaded | RF_DefaultSubObject) || IsNative() || IsDefaultSubobject(); } /** IsFullNameStableForNetworking means an object can be referred to its full path name over the network */ diff --git a/Engine/Source/Runtime/CoreUObject/Private/UObject/Package.cpp b/Engine/Source/Runtime/CoreUObject/Private/UObject/Package.cpp index 2806702c1151..a7cae8b0e370 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/UObject/Package.cpp +++ b/Engine/Source/Runtime/CoreUObject/Private/UObject/Package.cpp @@ -188,7 +188,7 @@ bool UPackage::IsFullyLoaded() // Newly created packages aren't loaded and therefore haven't been marked as being fully loaded. They are treated as fully // loaded packages though in this case, which is why we are looking to see whether the package exists on disk and assume it // has been fully loaded if it doesn't. - if( !bHasBeenFullyLoaded && !HasAnyFlags(RF_AsyncLoading) ) + if( !bHasBeenFullyLoaded && !HasAnyInternalFlags(EInternalObjectFlags::AsyncLoading) ) { FString DummyFilename; // Try to find matching package in package file cache. diff --git a/Engine/Source/Runtime/CoreUObject/Private/UObject/Property.cpp b/Engine/Source/Runtime/CoreUObject/Private/UObject/Property.cpp index 3e13eca82d66..cdaf6ef3c9f0 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/UObject/Property.cpp +++ b/Engine/Source/Runtime/CoreUObject/Private/UObject/Property.cpp @@ -350,12 +350,13 @@ void UProperty::Init() { // properties created in C++ should always be marked RF_Transient so that when the package containing // this property is saved, it doesn't try to save this UProperty into the ExportMap - SetFlags(RF_Transient | RF_Native); + SetFlags(RF_Transient); + SetInternalFlags(EInternalObjectFlags::Native); #if !WITH_EDITORONLY_DATA //@todo.COOKER/PACKAGER: Until we have a cooker/packager step, this can fire when WITH_EDITORONLY_DATA is not defined! // checkSlow(!HasAnyPropertyFlags(CPF_EditorOnly)); #endif // WITH_EDITORONLY_DATA - checkSlow(GetOuterUField()->HasAllFlags(RF_Native | RF_Transient)); + checkSlow(GetOuterUField()->HasAllFlags(RF_Transient) && GetOuterUField()->IsNative()); GetOuterUField()->AddCppProperty(this); } diff --git a/Engine/Source/Runtime/CoreUObject/Private/UObject/PropertyArray.cpp b/Engine/Source/Runtime/CoreUObject/Private/UObject/PropertyArray.cpp index 092fe1016506..eebc0fe199be 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/UObject/PropertyArray.cpp +++ b/Engine/Source/Runtime/CoreUObject/Private/UObject/PropertyArray.cpp @@ -79,7 +79,7 @@ void UArrayProperty::Serialize( FArchive& Ar ) { Super::Serialize( Ar ); Ar << Inner; - checkSlow(Inner || HasAnyFlags(RF_ClassDefaultObject | RF_PendingKill)); + checkSlow(Inner || HasAnyFlags(RF_ClassDefaultObject) || IsPendingKill()); } void UArrayProperty::AddReferencedObjects(UObject* InThis, FReferenceCollector& Collector) { diff --git a/Engine/Source/Runtime/CoreUObject/Private/UObject/PropertyBool.cpp b/Engine/Source/Runtime/CoreUObject/Private/UObject/PropertyBool.cpp index f62ff00588a1..112b9d2e3218 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/UObject/PropertyBool.cpp +++ b/Engine/Source/Runtime/CoreUObject/Private/UObject/PropertyBool.cpp @@ -112,14 +112,14 @@ void UBoolProperty::Serialize( FArchive& Ar ) if( Ar.IsLoading()) { Ar << NativeBool; - if (!HasAnyFlags(RF_PendingKill)) + if (!IsPendingKill()) { SetBoolSize( BoolSize, !!NativeBool ); } } else { - NativeBool = (!HasAnyFlags(RF_ClassDefaultObject|RF_PendingKill) && Ar.IsSaving()) ? (IsNativeBool() ? 1 : 0) : 0; + NativeBool = (!HasAnyFlags(RF_ClassDefaultObject) && !IsPendingKill() && Ar.IsSaving()) ? (IsNativeBool() ? 1 : 0) : 0; Ar << NativeBool; } } diff --git a/Engine/Source/Runtime/CoreUObject/Private/UObject/ReferenceChainSearch.cpp b/Engine/Source/Runtime/CoreUObject/Private/UObject/ReferenceChainSearch.cpp index 39f40e22204a..00397ce46301 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/UObject/ReferenceChainSearch.cpp +++ b/Engine/Source/Runtime/CoreUObject/Private/UObject/ReferenceChainSearch.cpp @@ -148,12 +148,12 @@ void FReferenceChainSearch::PrintReferencers( FReferenceChain& Referencer ) } CA_SUPPRESS(6011) - if( RefInfo.ReferencedBy->HasAnyFlags(RF_Native) ) + if( RefInfo.ReferencedBy->IsNative() ) { ObjectReachability += TEXT("(native) "); } - if( RefInfo.ReferencedBy->HasAnyFlags(RF_PendingKill) ) + if( RefInfo.ReferencedBy->IsPendingKill() ) { ObjectReachability += TEXT("(PendingKill) "); } @@ -163,12 +163,12 @@ void FReferenceChainSearch::PrintReferencers( FReferenceChain& Referencer ) ObjectReachability += TEXT("(standalone) "); } - if( RefInfo.ReferencedBy->HasAnyFlags(RF_Async) ) + if (RefInfo.ReferencedBy->HasAnyInternalFlags(EInternalObjectFlags::Async)) { ObjectReachability += TEXT("(async) "); } - if( RefInfo.ReferencedBy->HasAnyFlags(RF_AsyncLoading) ) + if (RefInfo.ReferencedBy->HasAnyInternalFlags(EInternalObjectFlags::AsyncLoading)) { ObjectReachability += TEXT("(asyncloading) "); } @@ -252,7 +252,7 @@ void CreateReferenceChain(FRefGraphItem* Node, FReferenceChainSearch::FReference ChainArray.Push(ThisChain); return; } - if (Node->Link.ReferencedObj->HasAnyFlags(GARBAGE_COLLECTION_KEEPFLAGS | RF_RootSet)) + if (Node->Link.ReferencedObj->HasAnyFlags(GARBAGE_COLLECTION_KEEPFLAGS) || Node->Link.ReferencedObj->IsRooted()) { return; } @@ -288,9 +288,11 @@ void FReferenceChainSearch::BuildRefGraph() // Create the first graph-nodes referencing the target object for (FRawObjectIterator It;It;++It) { - UObject* Obj = *It; + FUObjectItem* ObjItem = *It; + checkSlow(ObjItem); + UObject* Object = static_cast(ObjItem->Object); - TArray& RefList = ReferenceMap.FindChecked(Obj); + TArray& RefList = ReferenceMap.FindChecked(Object); for (int32 i=0; i < RefList.Num(); ++i) { @@ -317,14 +319,15 @@ void FReferenceChainSearch::BuildRefGraph() for (FRawObjectIterator It;It;++It) { - UObject* Obj = *It; - - TArray& RefList = ReferenceMap.FindChecked(Obj); + FUObjectItem* ObjItem = *It; + checkSlow(ObjItem->Object); + UObject* Object = (UObject*)ObjItem->Object; + TArray& RefList = ReferenceMap.FindChecked(Object); for (int32 i=0; i < RefList.Num(); ++i) { if (RefList[i].ReferenceType == EReferenceType::Invalid || - RefList[i].ReferencedObj->HasAnyFlags(GARBAGE_COLLECTION_KEEPFLAGS | RF_RootSet)) // references to rooted objects are not important + RefList[i].ReferencedObj->HasAnyFlags(GARBAGE_COLLECTION_KEEPFLAGS) || RefList[i].ReferencedObj->IsRooted()) // references to rooted objects are not important { continue; } @@ -375,7 +378,7 @@ void FReferenceChainSearch::BuildRefGraph() { FRefGraphItem* Node = It.Value(); - if (Node->Link.ReferencedBy->HasAnyFlags(GARBAGE_COLLECTION_KEEPFLAGS | RF_RootSet)) + if (Node->Link.ReferencedBy->HasAnyFlags(GARBAGE_COLLECTION_KEEPFLAGS) || Node->Link.ReferencedBy->IsRooted()) { FReferenceChain CurChain; CreateReferenceChain(Node, CurChain, Chains, ObjectToFind, Level); @@ -394,9 +397,9 @@ void FReferenceChainSearch::PerformSearch() for (FRawObjectIterator It;It;++It) { - UObject* CurrentObject = *It; - - ProcessObject(CurrentObject); + FUObjectItem* CurrentObject = *It; + UObject* Object = static_cast(CurrentObject->Object); + ProcessObject(Object); } BuildRefGraph(); @@ -830,12 +833,12 @@ FString FReferenceChainSearch::FReferenceChainLink::ToString() const ObjectReachability += TEXT("(root) "); } - if( ReferencedBy->HasAnyFlags(RF_Native) ) + if( ReferencedBy->IsNative() ) { ObjectReachability += TEXT("(native) "); } - if( ReferencedBy->HasAnyFlags(RF_PendingKill) ) + if( ReferencedBy->IsPendingKill() ) { ObjectReachability += TEXT("(PendingKill) "); } diff --git a/Engine/Source/Runtime/CoreUObject/Private/UObject/SavePackage.cpp b/Engine/Source/Runtime/CoreUObject/Private/UObject/SavePackage.cpp index a880e87efd91..8a770729f958 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/UObject/SavePackage.cpp +++ b/Engine/Source/Runtime/CoreUObject/Private/UObject/SavePackage.cpp @@ -7,6 +7,7 @@ #include "UObject/UObjectThreadContext.h" #include "BlueprintSupport.h" #include "DebugSerializationFlags.h" +#include "UObject/GCScopeLock.h" DEFINE_LOG_CATEGORY_STATIC(LogSavePackage, Log, All); @@ -812,11 +813,11 @@ FArchive& FArchiveSaveTagImports::operator<<( UObject*& Obj ) // Skip PendingKill objects and objects that are both not for client and not for server when cooking. if (Obj && !Obj->IsPendingKill() && (!IsCooking() || !Obj->HasAllMarks(ObjectMarks)) && !Obj->HasAnyMarks(OBJECTMARK_EditorOnly)) { - if( !Obj->HasAnyFlags(RF_Transient) || Obj->HasAllFlags(RF_Native) ) + if( !Obj->HasAnyFlags(RF_Transient) || Obj->IsNative() ) { // remember it as a dependency, unless it's a top level package or native const bool bIsTopLevelPackage = Obj->GetOuter() == NULL && dynamic_cast(Obj); - bool bIsNative = Obj->HasAnyFlags(RF_Native); + bool bIsNative = Obj->IsNative(); UObject* Outer = Obj->GetOuter(); const bool bIsEditorOnly = IsCookingForNoEditorDataPlatform(*this) && IsEditorOnlyObject(Obj); @@ -829,7 +830,7 @@ FArchive& FArchiveSaveTagImports::operator<<( UObject*& Obj ) // go up looking for native classes while (!bIsNative && Outer) { - if (dynamic_cast(Outer) && Outer->HasAnyFlags(RF_Native)) + if (dynamic_cast(Outer) && Outer->IsNative()) { bIsNative = true; } @@ -862,10 +863,14 @@ FArchive& FArchiveSaveTagImports::operator<<( UObject*& Obj ) Obj->UnMark(OBJECTMARK_NotForServer); } - UObject* Parent = Obj->GetOuter(); - if( Parent ) + // If the object has been excluded, don't add its outer + if (!Obj->HasAllMarks(ObjectMarks)) { - *this << Parent; + UObject* Parent = Obj->GetOuter(); + if( Parent ) + { + *this << Parent; + } } } } @@ -1404,27 +1409,27 @@ static void FindMostLikelyCulprit( TArray BadObjects, UObject*& MostLi FReferencerInformationList Refs; - if ( IsReferenced( Obj,RF_Native | RF_Public, true, &Refs) ) - { + if (IsReferenced(Obj, RF_Public, EInternalObjectFlags::Native, true, &Refs)) + { for (int32 i = 0; i < Refs.ExternalReferences.Num(); i++) { UObject* RefObj = Refs.ExternalReferences[i].Referencer; - if( RefObj->HasAnyMarks(EObjectMark(OBJECTMARK_TagExp|OBJECTMARK_TagImp)) ) + if (RefObj->HasAnyMarks(EObjectMark(OBJECTMARK_TagExp | OBJECTMARK_TagImp))) { - if ( RefObj->GetFName() == NAME_PersistentLevel || RefObj->GetClass()->GetFName() == WorldClassName ) + if (RefObj->GetFName() == NAME_PersistentLevel || RefObj->GetClass()->GetFName() == WorldClassName) { // these types of references should be ignored continue; } - UE_LOG(LogSavePackage, Warning, TEXT("\t%s (%i refs)"), *RefObj->GetFullName(), Refs.ExternalReferences[i].TotalReferences ); - for ( int32 j = 0; j < Refs.ExternalReferences[i].ReferencingProperties.Num(); j++ ) + UE_LOG(LogSavePackage, Warning, TEXT("\t%s (%i refs)"), *RefObj->GetFullName(), Refs.ExternalReferences[i].TotalReferences); + for (int32 j = 0; j < Refs.ExternalReferences[i].ReferencingProperties.Num(); j++) { const UProperty* Prop = Refs.ExternalReferences[i].ReferencingProperties[j]; UE_LOG(LogSavePackage, Warning, TEXT("\t\t%i) %s"), j, *Prop->GetFullName()); PropertyRef = Prop; } - + MostLikelyCulprit = Obj; } } @@ -2901,7 +2906,7 @@ static bool ValidateConformCompatibility(UPackage* NewPackage, FLinkerLoad* OldL { UClass* NewClass = (UClass*)StaticFindObjectFast(UClass::StaticClass(), NewPackage, OldLinker->ExportMap[i].ObjectName, true, false); UClass* OldClass = static_cast(OldLinker->Create(UClass::StaticClass(), OldLinker->ExportMap[i].ObjectName, OldLinker->LinkerRoot, LOAD_None, false)); - if (OldClass != NULL && NewClass != NULL && OldClass->HasAnyFlags(RF_Native) && NewClass->HasAnyFlags(RF_Native)) + if (OldClass != NULL && NewClass != NULL && OldClass->IsNative() && NewClass->IsNative()) { OldClass->ClassConstructor = NewClass->ClassConstructor; #if WITH_HOT_RELOAD_CTORS @@ -3231,6 +3236,7 @@ public: #endif +extern FGCCSyncObject GGarbageCollectionGuardCritical; ESavePackageResult UPackage::Save(UPackage* InOuter, UObject* Base, EObjectFlags TopLevelFlags, const TCHAR* Filename, FOutputDevice* Error, FLinkerLoad* Conform, bool bForceByteSwapping, bool bWarnOfLongFilename, uint32 SaveFlags, @@ -3302,7 +3308,6 @@ ESavePackageResult UPackage::Save(UPackage* InOuter, UObject* Base, EObjectFlags // The latter implies flushing all file handles which is a pre-requisite of saving a package. The code basically needs // to be sure that we are not reading from a file that is about to be overwritten and that there is no way we might // start reading from the file till we are done overwriting it. - FlushAsyncLoading(); UE_LOG_COOK_TIME(TEXT("FlushAsyncLoading")); @@ -3456,11 +3461,21 @@ ESavePackageResult UPackage::Save(UPackage* InOuter, UObject* Base, EObjectFlags UE_LOG_COOK_TIME(TEXT("TagPackageExports")); { - // set GIsSavingPackage here as it is now illegal to create any new object references; they potentially wouldn't be saved correctly + check(!IsGarbageCollecting()); + // set GIsSavingPackage here as it is now illegal to create any new object references; they potentially wouldn't be saved correctly struct FScopedSavingFlag { - FScopedSavingFlag() { GIsSavingPackage = true; } - ~FScopedSavingFlag() { GIsSavingPackage = false; } + FScopedSavingFlag() + { + // We need the same lock as GC so that no StaticFindObject can happen in parallel to saveing a package + GGarbageCollectionGuardCritical.GCLock(); + GIsSavingPackage = true; + } + ~FScopedSavingFlag() + { + GIsSavingPackage = false; + GGarbageCollectionGuardCritical.GCUnlock(); + } } IsSavingFlag; @@ -3710,7 +3725,7 @@ ESavePackageResult UPackage::Save(UPackage* InOuter, UObject* Base, EObjectFlags if ( !(Linker->Summary.PackageFlags & PKG_FilterEditorOnly) ) { TArray ObjectsInPackage; - GetObjectsWithOuter(InOuter, ObjectsInPackage, true, RF_Transient | RF_PendingKill); + GetObjectsWithOuter(InOuter, ObjectsInPackage, true, RF_Transient, EInternalObjectFlags::PendingKill); for (UObject* const Object : ObjectsInPackage) { FPropertyLocalizationDataGatherer PropertyLocalizationDataGatherer(Linker->GatherableTextDataMap); @@ -4638,20 +4653,20 @@ ESavePackageResult UPackage::Save(UPackage* InOuter, UObject* Base, EObjectFlags { if ( Import.XObject->GetOuter()->IsIn(InOuter) ) { - if ( !Import.XObject->HasAllFlags(RF_Native|RF_Transient) ) + if (!Import.XObject->HasAllFlags(RF_Transient) || !Import.XObject->IsNative()) { UE_LOG(LogSavePackage, Warning, TEXT("Bad Object=%s"),*Import.XObject->GetFullName()); } else { - // if an object is marked RF_Transient|RF_Native, it is either an intrinsic class or + // if an object is marked RF_Transient and native, it is either an intrinsic class or // a property of an intrinsic class. Only properties of intrinsic classes will have // an Outer that passes the check for "GetOuter()->IsIn(InOuter)" (thus ending up in this - // block of code). Just verify that the Outer for this property is also marked RF_Transient|RF_Native - check(Import.XObject->GetOuter()->HasAllFlags(RF_Native|RF_Transient)); + // block of code). Just verify that the Outer for this property is also marked RF_Transient and Native + check(Import.XObject->GetOuter()->HasAllFlags(RF_Transient) && Import.XObject->GetOuter()->IsNative()); } } - check(!Import.XObject->GetOuter()->IsIn(InOuter)||Import.XObject->HasAllFlags(EObjectFlags(RF_Native|RF_Transient))); + check(!Import.XObject->GetOuter()->IsIn(InOuter) || Import.XObject->HasAllFlags(RF_Transient) || Import.XObject->IsNative()); Import.OuterIndex = Linker->MapObject(Import.XObject->GetOuter()); } } diff --git a/Engine/Source/Runtime/CoreUObject/Private/UObject/ScriptCore.cpp b/Engine/Source/Runtime/CoreUObject/Private/UObject/ScriptCore.cpp index 70142d9e2209..99c160499f6c 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/UObject/ScriptCore.cpp +++ b/Engine/Source/Runtime/CoreUObject/Private/UObject/ScriptCore.cpp @@ -970,7 +970,7 @@ UFunction* UObject::FindFunctionChecked( FName InName ) const void UObject::ProcessEvent( UFunction* Function, void* Parms ) { - checkf(!HasAnyFlags(RF_Unreachable),TEXT("%s Function: '%s'"), *GetFullName(), *Function->GetPathName()); + checkf(!IsUnreachable(),TEXT("%s Function: '%s'"), *GetFullName(), *Function->GetPathName()); checkf(!FUObjectThreadContext::Get().IsRoutingPostLoad, TEXT("Cannot call UnrealScript (%s - %s) while PostLoading objects"), *GetFullName(), *Function->GetFullName()); // Reject. diff --git a/Engine/Source/Runtime/CoreUObject/Private/UObject/UObjectArray.cpp b/Engine/Source/Runtime/CoreUObject/Private/UObject/UObjectArray.cpp index 83636f5b622b..d5b2f77f1e05 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/UObject/UObjectArray.cpp +++ b/Engine/Source/Runtime/CoreUObject/Private/UObject/UObjectArray.cpp @@ -8,20 +8,16 @@ DEFINE_LOG_CATEGORY_STATIC(LogUObjectArray, Log, All); -void FUObjectArray::AllocatePermanentObjectPool(int32 MaxObjectsNotConsideredByGC) +void FUObjectArray::AllocateObjectPool(int32 MaxUObjects, int32 MaxObjectsNotConsideredByGC) { check(IsInGameThread()); // GObjFirstGCIndex is the index at which the garbage collector will start for the mark phase. ObjFirstGCIndex = MaxObjectsNotConsideredByGC; - // Presize array. + // Pre-size array. check(ObjObjects.Num() == 0); - if (ObjFirstGCIndex >= 0) - { - ObjObjects.Reserve(ObjFirstGCIndex); - } - FWeakObjectPtr::Init(); // this adds a delete listener + ObjObjects.PreAllocate(MaxUObjects); } void FUObjectArray::CloseDisregardForGC() @@ -44,7 +40,7 @@ void FUObjectArray::AllocateUObjectIndex(UObjectBase* Object, bool bMergingThrea { // Disregard from GC pool is only available from the game thread, at least for now check(IsInGameThread()); - Index = ObjObjects.AddZeroed(1); + Index = ObjObjects.AddSingle(); ObjLastNonGCIndex = Index; ObjFirstGCIndex = FMath::Max(ObjFirstGCIndex, Index + 1); } @@ -59,7 +55,7 @@ void FUObjectArray::AllocateUObjectIndex(UObjectBase* Object, bool bMergingThrea checkSlow(AvailableCount >= 0); #endif Index = (int32)(uintptr_t)AvailableIndex; - check(ObjObjects[Index]==nullptr); + check(ObjObjects[Index].Object==nullptr); } else { @@ -68,15 +64,16 @@ void FUObjectArray::AllocateUObjectIndex(UObjectBase* Object, bool bMergingThrea #else check(IsInGameThread()); #endif - Index = ObjObjects.AddZeroed(1); + Index = ObjObjects.AddSingle(); } check(Index >= ObjFirstGCIndex); } // Add to global table. - if (FPlatformAtomics::InterlockedCompareExchangePointer((void**)&ObjObjects[Index], Object, NULL) != NULL) // we use an atomic operation to check for unexpected concurrency, verify alignment, etc + if (FPlatformAtomics::InterlockedCompareExchangePointer((void**)&ObjObjects[Index].Object, Object, NULL) != NULL) // we use an atomic operation to check for unexpected concurrency, verify alignment, etc { UE_LOG(LogUObjectArray, Fatal, TEXT("Unexpected concurency while adding new object")); } + IndexToObject(Index)->ResetSerialNumberAndFlags(); Object->InternalIndex = Index; // @todo: threading: lock UObjectCreateListeners for (int32 ListenerIndex = 0; ListenerIndex < UObjectCreateListeners.Num(); ListenerIndex++) @@ -97,7 +94,7 @@ void FUObjectArray::FreeUObjectIndex(UObjectBase* Object) int32 Index = Object->InternalIndex; // At this point no two objects exist with the same index so no need to lock here - if (FPlatformAtomics::InterlockedCompareExchangePointer((void**)&ObjObjects[Index], NULL, Object) == NULL) // we use an atomic operation to check for unexpected concurrency, verify alignment, etc + if (FPlatformAtomics::InterlockedCompareExchangePointer((void**)&ObjObjects[Index].Object, NULL, Object) == NULL) // we use an atomic operation to check for unexpected concurrency, verify alignment, etc { UE_LOG(LogUObjectArray, Fatal, TEXT("Unexpected concurency while adding new object")); } @@ -111,6 +108,7 @@ void FUObjectArray::FreeUObjectIndex(UObjectBase* Object) // No point in filling this list when doing exit purge. Nothing should be allocated afterwards anyway. if (Index > ObjLastNonGCIndex && !GExitPurge) { + IndexToObject(Index)->ResetSerialNumberAndFlags(); ObjAvailableList.Push((int32*)(uintptr_t)Index); #if UE_GC_TRACK_OBJ_AVAILABLE ObjAvailableCount.Increment(); @@ -188,13 +186,13 @@ bool FUObjectArray::IsValid(const UObjectBase* Object) const UE_LOG(LogUObjectArray, Warning, TEXT("Invalid object index %i"), Index ); return false; } - const UObjectBase *Slot = ObjObjects[Index]; - if( Slot == NULL ) + const FUObjectItem& Slot = ObjObjects[Index]; + if( Slot.Object == NULL ) { UE_LOG(LogUObjectArray, Warning, TEXT("Empty slot") ); return false; } - if( Slot != Object ) + if( Slot.Object != Object ) { UE_LOG(LogUObjectArray, Warning, TEXT("Other object in slot") ); return false; @@ -202,6 +200,28 @@ bool FUObjectArray::IsValid(const UObjectBase* Object) const return true; } +int32 FUObjectArray::AllocateSerialNumber(int32 Index) +{ + FUObjectItem* ObjectItem = IndexToObject(Index); + checkSlow(ObjectItem); + + volatile int32 *SerialNumberPtr = &ObjectItem->SerialNumber; + int32 SerialNumber = *SerialNumberPtr; + if (!SerialNumber) + { + SerialNumber = MasterSerialNumber.Increment(); + UE_CLOG(SerialNumber <= START_SERIAL_NUMBER, LogUObjectArray, Fatal, TEXT("UObject serial numbers overflowed (trying to allocate serial number %d)."), SerialNumber); + int32 ValueWas = FPlatformAtomics::InterlockedCompareExchange((int32*)SerialNumberPtr, SerialNumber, 0); + if (ValueWas != 0) + { + // someone else go it first, use their value + SerialNumber = ValueWas; + } + } + checkSlow(SerialNumber > START_SERIAL_NUMBER); + return SerialNumber; +} + /** * Clears some internal arrays to get rid of false memory leaks */ diff --git a/Engine/Source/Runtime/CoreUObject/Private/UObject/UObjectBase.cpp b/Engine/Source/Runtime/CoreUObject/Private/UObject/UObjectBase.cpp index 6331c79c199e..5532b8dbcb99 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/UObject/UObjectBase.cpp +++ b/Engine/Source/Runtime/CoreUObject/Private/UObject/UObjectBase.cpp @@ -68,7 +68,7 @@ UObjectBase::UObjectBase( EObjectFlags InFlags ) * @param InName name of the new object * @param InObjectArchetype archetype to assign */ -UObjectBase::UObjectBase( UClass* InClass, EObjectFlags InFlags, UObject *InOuter, FName InName ) +UObjectBase::UObjectBase(UClass* InClass, EObjectFlags InFlags, EInternalObjectFlags InInternalFlags, UObject *InOuter, FName InName) : ObjectFlags (InFlags) , InternalIndex (INDEX_NONE) , Class (InClass) @@ -76,7 +76,7 @@ UObjectBase::UObjectBase( UClass* InClass, EObjectFlags InFlags, UObject *InOute { check(Class); // Add to global table. - AddObject(InName); + AddObject(InName, InInternalFlags); } @@ -140,10 +140,10 @@ void UObjectBase::DeferredRegister(UClass *UClassStaticClass,const TCHAR* Packag Class = UClassStaticClass; // Add to the global object table. - AddObject(FName(InName)); + AddObject(FName(InName), EInternalObjectFlags::None); // Make sure that objects disregarded for GC are part of root set. - check(!GUObjectArray.IsDisregardForGC(this) || (GetFlags() & RF_RootSet) ); + check(!GUObjectArray.IsDisregardForGC(this) || GUObjectArray.IndexToObject(InternalIndex)->IsRootSet()); } /** @@ -151,15 +151,31 @@ void UObjectBase::DeferredRegister(UClass *UClassStaticClass,const TCHAR* Packag * * @param Name name to assign to this uobject */ -void UObjectBase::AddObject(FName InName) +void UObjectBase::AddObject(FName InName, EInternalObjectFlags InSetInternalFlags) { Name = InName; + EInternalObjectFlags InternalFlagsToSet = InSetInternalFlags; if (!IsInGameThread()) { - ObjectFlags |= RF_Async; + InternalFlagsToSet |= EInternalObjectFlags::Async; + } + if (ObjectFlags & RF_MarkAsRootSet) + { + InternalFlagsToSet |= EInternalObjectFlags::RootSet; + ObjectFlags &= ~RF_MarkAsRootSet; + } + if (ObjectFlags & RF_MarkAsNative) + { + InternalFlagsToSet |= EInternalObjectFlags::Native; + ObjectFlags &= ~RF_MarkAsNative; } AllocateUObjectIndexForCurrentThread(this); check(InName != NAME_None && InternalIndex >= 0); + if (InternalFlagsToSet != EInternalObjectFlags::None) + { + GUObjectArray.IndexToObject(InternalIndex)->SetFlags(InternalFlagsToSet); + + } HashObject(this); check(IsValidLowLevel()); } @@ -555,8 +571,10 @@ void UClassCompiledInDefer(FFieldCompiledInInfo* ClassInfo, const TCHAR* Name, S if (ClassInfo->bHasChanged) { // Rename the old class and move it to transient package - ExistingClass->ClearFlags(RF_RootSet | RF_Standalone | RF_Public); - ExistingClass->GetDefaultObject()->ClearFlags(RF_RootSet | RF_Standalone | RF_Public); + ExistingClass->RemoveFromRoot(); + ExistingClass->ClearFlags(RF_Standalone | RF_Public); + ExistingClass->GetDefaultObject()->RemoveFromRoot(); + ExistingClass->GetDefaultObject()->ClearFlags(RF_Standalone | RF_Public); const FName OldClassRename = MakeUniqueObjectName(GetTransientPackage(), ExistingClass->GetClass(), *FString::Printf(TEXT("HOTRELOADED_%s"), *NameWithoutPrefix)); ExistingClass->Rename(*OldClassRename.ToString(), GetTransientPackage()); ExistingClass->SetFlags(RF_Transient); @@ -805,10 +823,10 @@ void ProcessNewlyLoadedUObjects() #endif } -static int32 GVarWarnIfTimeLimitExceeded; -static FAutoConsoleVariableRef CVarWarnIfTimeLimitExceeded( +static int32 GVarMaxObjectsNotConsideredByGC; +static FAutoConsoleVariableRef CMaxObjectsNotConsideredByGC( TEXT("gc.MaxObjectsNotConsideredByGC"), - GVarWarnIfTimeLimitExceeded, + GVarMaxObjectsNotConsideredByGC, TEXT("Placeholder console variable, currently not used in runtime."), ECVF_Default ); @@ -821,6 +839,22 @@ static FAutoConsoleVariableRef CSizeOfPermanentObjectPool( ECVF_Default ); +static int32 GMaxObjectsInEditor; +static FAutoConsoleVariableRef CMaxObjectsInEditor( + TEXT("gc.MaxObjectsInEditor"), + GMaxObjectsInEditor, + TEXT("Placeholder console variable, currently not used in runtime."), + ECVF_Default + ); + +static int32 GMaxObjectsInGame; +static FAutoConsoleVariableRef CMaxObjectsInGame( + TEXT("gc.MaxObjectsInGame"), + GMaxObjectsInGame, + TEXT("Placeholder console variable, currently not used in runtime."), + ECVF_Default + ); + /** * Final phase of UObject initialization. all auto register objects are added to the main data structures. */ @@ -829,6 +863,7 @@ void UObjectBaseInit() // Zero initialize and later on get value from .ini so it is overridable per game/ platform... int32 MaxObjectsNotConsideredByGC = 0; int32 SizeOfPermanentObjectPool = 0; + int32 MaxUObjects = 2 * 1024 * 1024; // Default to ~2M UObjects // To properly set MaxObjectsNotConsideredByGC look for "Log: XXX objects as part of root set at end of initial load." // in your log file. This is being logged from LaunchEnglineLoop after objects have been added to the root set. @@ -841,13 +876,21 @@ void UObjectBaseInit() // Not used on PC as in-place creation inside bigger pool interacts with the exit purge and deleting UObject directly. GConfig->GetInt( TEXT("/Script/Engine.GarbageCollectionSettings"), TEXT("gc.SizeOfPermanentObjectPool"), SizeOfPermanentObjectPool, GEngineIni ); + + // Maximum number of UObjects in cooked game + GConfig->GetInt(TEXT("/Script/Engine.GarbageCollectionSettings"), TEXT("gc.MaxObjectsInGame"), MaxUObjects, GEngineIni); + } + else + { + // Maximum number of UObjects in the editor + GConfig->GetInt(TEXT("/Script/Engine.GarbageCollectionSettings"), TEXT("gc.MaxObjectsInEditor"), MaxUObjects, GEngineIni); } // Log what we're doing to track down what really happens as log in LaunchEngineLoop doesn't report those settings in pristine form. - UE_LOG(LogInit, Log, TEXT("Presizing for %i objects not considered by GC, pre-allocating %i bytes."), MaxObjectsNotConsideredByGC, SizeOfPermanentObjectPool ); + UE_LOG(LogInit, Log, TEXT("Presizing for max %d objects, including %i objects not considered by GC, pre-allocating %i bytes for permanent pool."), MaxUObjects, MaxObjectsNotConsideredByGC, SizeOfPermanentObjectPool); GUObjectAllocator.AllocatePermanentObjectPool(SizeOfPermanentObjectPool); - GUObjectArray.AllocatePermanentObjectPool(MaxObjectsNotConsideredByGC); + GUObjectArray.AllocateObjectPool(MaxUObjects, MaxObjectsNotConsideredByGC); void InitAsyncThread(); InitAsyncThread(); @@ -965,7 +1008,6 @@ const TCHAR* DebugFullName(UObject* Object) return TEXT("None"); } } - namespace { #if WITH_HOT_RELOAD @@ -1058,7 +1100,8 @@ namespace if (Existing) { // Make sure the old struct is not used by anything - Existing->ClearFlags(RF_RootSet | RF_Standalone | RF_Public); + Existing->ClearFlags(RF_Standalone | RF_Public); + Existing->RemoveFromRoot(); const FName OldRename = MakeUniqueObjectName(GetTransientPackage(), Existing->GetClass(), *FString::Printf(TEXT("HOTRELOADED_%s"), Name)); Existing->Rename(*OldRename.ToString(), GetTransientPackage()); } @@ -1135,7 +1178,6 @@ UEnum* ConstructDynamicEnum(FName EnumName) return Result; } -/** Constructs a dynamic class/enum/struct given its name */ UObject* ConstructDynamicType(FName TypeName, FName TypeClass) { UObject* Result = nullptr; @@ -1152,4 +1194,16 @@ UObject* ConstructDynamicType(FName TypeName, FName TypeClass) Result = ConstructDynamicEnum(TypeName); } return Result; +} + +UPackage* FindOrConstructDynamicTypePackage(const TCHAR* PackageName) +{ + UPackage* Package = Cast(StaticFindObjectFast(UPackage::StaticClass(), nullptr, PackageName)); + if (!Package) + { + Package = CreatePackage(nullptr, PackageName); + Package->SetPackageFlags(PKG_CompiledIn); + } + check(Package); + return Package; } \ No newline at end of file diff --git a/Engine/Source/Runtime/CoreUObject/Private/UObject/UObjectGlobals.cpp b/Engine/Source/Runtime/CoreUObject/Private/UObject/UObjectGlobals.cpp index c0e8458381f0..e5aa3a244860 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/UObject/UObjectGlobals.cpp +++ b/Engine/Source/Runtime/CoreUObject/Private/UObject/UObjectGlobals.cpp @@ -166,7 +166,7 @@ namespace * @param ExclusiveFlags Ignores objects that contain any of the specified exclusive flags * @return Returns a pointer to the found object or NULL if none could be found */ -UObject* StaticFindObjectFast( UClass* ObjectClass, UObject* ObjectPackage, FName ObjectName, bool ExactClass, bool AnyPackage, EObjectFlags ExclusiveFlags ) +UObject* StaticFindObjectFast(UClass* ObjectClass, UObject* ObjectPackage, FName ObjectName, bool ExactClass, bool AnyPackage, EObjectFlags ExclusiveFlags, EInternalObjectFlags ExclusiveInternalFlags) { if (GIsSavingPackage || IsGarbageCollectingOnGameThread()) { @@ -174,9 +174,8 @@ UObject* StaticFindObjectFast( UClass* ObjectClass, UObject* ObjectPackage, FNam } // We don't want to return any objects that are currently being background loaded unless we're using FindObject during async loading. - ExclusiveFlags |= IsInAsyncLoadingThread() ? RF_NoFlags : RF_AsyncLoading; - - UObject* FoundObject = StaticFindObjectFastInternal( ObjectClass, ObjectPackage, ObjectName, ExactClass, AnyPackage, ExclusiveFlags ); + ExclusiveInternalFlags |= IsInAsyncLoadingThread() ? EInternalObjectFlags::None : EInternalObjectFlags::AsyncLoading; + UObject* FoundObject = StaticFindObjectFastInternal(ObjectClass, ObjectPackage, ObjectName, ExactClass, AnyPackage, ExclusiveFlags, ExclusiveInternalFlags); if (!FoundObject) { @@ -267,6 +266,7 @@ UObject* StaticFindObjectSafe( UClass* ObjectClass, UObject* ObjectParent, const { if (!GIsSavingPackage && !IsGarbageCollectingOnGameThread()) { + FGCScopeGuard GCAndSavepackageGuard; return StaticFindObject( ObjectClass, ObjectParent, InName, ExactClass ); } else @@ -927,6 +927,10 @@ UPackage* LoadPackageInternal(UPackage* InOuter, const TCHAR* InLongPackageName, } } + FName PackageName(InLongPackageName); + check(!InDependencyTracker.Contains(InLongPackageName)); + InDependencyTracker.Add(PackageName); + #if WITH_EDITOR TGuardValue IsEditorLoadingPackage(GIsEditorLoadingPackage, GIsEditor || GIsEditorLoadingPackage); #endif @@ -936,9 +940,11 @@ UPackage* LoadPackageInternal(UPackage* InOuter, const TCHAR* InLongPackageName, SlowTask.EnterProgressFrame(10); + // Try to load. + BeginLoad(); + if (InAssetRegistry) { - FName PackageName(InLongPackageName); TArray PackageDependencies; InAssetRegistry->GetDependencies(PackageName, PackageDependencies, EAssetRegistryDependencyType::Hard); @@ -946,24 +952,11 @@ UPackage* LoadPackageInternal(UPackage* InOuter, const TCHAR* InLongPackageName, { if (!InDependencyTracker.Contains(Dependency) && FindObjectFast(nullptr, Dependency, false, false) == nullptr) { - InDependencyTracker.Add(Dependency); LoadPackageInternal(InOuter, *Dependency.ToString(), LoadFlags, nullptr, InDependencyTracker, InAssetRegistry); } } - - // Check that the package we are going to load hasn't somehow been loaded behind our backs - // while loading a dependency. In some cases, circular dependencies can mean this happens - // and so there is no need to carry on any further. - Result = FindObjectFast(nullptr, InLongPackageName, false, false); - if (Result != nullptr) - { - return Result; - } } - // Try to load. - BeginLoad(); - bool bFullyLoadSkipped = false; SlowTask.EnterProgressFrame(30); @@ -1558,8 +1551,16 @@ FName MakeObjectNameFromDisplayLabel(const FString& DisplayLabel, const FName Cu * Constructor - zero initializes all members */ FObjectDuplicationParameters::FObjectDuplicationParameters( UObject* InSourceObject, UObject* InDestOuter ) -: SourceObject(InSourceObject), DestOuter(InDestOuter), DestName(NAME_None) -, FlagMask(RF_AllFlags), ApplyFlags(RF_NoFlags), PortFlags(PPF_None), DestClass(NULL), CreatedObjects(NULL) +: SourceObject(InSourceObject) +, DestOuter(InDestOuter) +, DestName(NAME_None) +, FlagMask(RF_AllFlags & ~(RF_MarkAsRootSet|RF_MarkAsNative)) +, InternalFlagMask(EInternalObjectFlags::AllFlags) +, ApplyFlags(RF_NoFlags) +, ApplyInternalFlags(EInternalObjectFlags::None) +, PortFlags(PPF_None) +, DestClass(NULL) +, CreatedObjects(NULL) { checkSlow(SourceObject); checkSlow(DestOuter); @@ -1569,7 +1570,7 @@ FObjectDuplicationParameters::FObjectDuplicationParameters( UObject* InSourceObj } -UObject* StaticDuplicateObject(UObject const* SourceObject, UObject* DestOuter, const TCHAR* DestName, EObjectFlags FlagMask, UClass* DestClass, EDuplicateForPie DuplicateForPIE) +UObject* StaticDuplicateObject(UObject const* SourceObject, UObject* DestOuter, const TCHAR* DestName, EObjectFlags FlagMask, UClass* DestClass, EDuplicateForPie DuplicateForPIE, EInternalObjectFlags InternalFlagsMask) { if (!IsAsyncLoading() && !IsLoading() && SourceObject->HasAnyFlags(RF_ClassDefaultObject)) { @@ -1601,6 +1602,7 @@ UObject* StaticDuplicateObject(UObject const* SourceObject, UObject* DestOuter, Parameters.DestClass = DestClass; } Parameters.FlagMask = FlagMask; + Parameters.InternalFlagMask = InternalFlagsMask; if( DuplicateForPIE == SDO_DuplicateForPie) { Parameters.PortFlags = PPF_DuplicateForPIE; @@ -1624,7 +1626,8 @@ UObject* StaticDuplicateObjectEx( FObjectDuplicationParameters& Parameters ) { // make sure we are not duplicating RF_RootSet as this flag is special // also make sure we are not duplicating the RF_ClassDefaultObject flag as this can only be set on the real CDO - Parameters.FlagMask &= ~(RF_RootSet|RF_ClassDefaultObject); + Parameters.FlagMask &= ~RF_ClassDefaultObject; + Parameters.InternalFlagMask &= ~EInternalObjectFlags::RootSet; } // disable object and component instancing while we're duplicating objects, as we're going to instance components manually a little further below @@ -1641,7 +1644,8 @@ UObject* StaticDuplicateObjectEx( FObjectDuplicationParameters& Parameters ) DupRootObject = StaticConstructObject_Internal( Parameters.DestClass, Parameters.DestOuter, Parameters.DestName, - Parameters.ApplyFlags|Parameters.SourceObject->GetMaskedFlags(Parameters.FlagMask), + Parameters.ApplyFlags | Parameters.SourceObject->GetMaskedFlags(Parameters.FlagMask), + Parameters.ApplyInternalFlags | (Parameters.SourceObject->GetInternalFlags() & Parameters.InternalFlagMask), Parameters.SourceObject->GetArchetype()->GetClass() == Parameters.DestClass ? Parameters.SourceObject->GetArchetype() : NULL, @@ -1677,6 +1681,8 @@ UObject* StaticDuplicateObjectEx( FObjectDuplicationParameters& Parameters ) DupRootObject, // Destination object to copy into Parameters.FlagMask, // Flags to be copied for duplicated objects Parameters.ApplyFlags, // Flags to always set on duplicated objects + Parameters.InternalFlagMask, // Internal Flags to be copied for duplicated objects + Parameters.ApplyInternalFlags, // Internal Flags to always set on duplicated objects &InstanceGraph, // Instancing graph Parameters.PortFlags ); // PortFlags @@ -1897,6 +1903,7 @@ UObject* StaticAllocateObject UObject* InOuter, FName InName, EObjectFlags InFlags, + EInternalObjectFlags InternalSetFlags, bool bCanRecycleSubobjects, bool* bOutRecycledSubobject ) @@ -1981,7 +1988,7 @@ UObject* StaticAllocateObject else { // Replace an existing object without affecting the original's address or index. - check(!Obj->HasAnyFlags(RF_Unreachable)); + check(!Obj->IsUnreachable()); check(!ObjectRestoreAfterInitProps); // otherwise recursive construction ObjectRestoreAfterInitProps = Obj->GetRestoreForUObjectOverwrite(); @@ -1989,12 +1996,13 @@ UObject* StaticAllocateObject // Remember linker, flags, index, and native class info. Linker = Obj->GetLinker(); LinkerIndex = Obj->GetLinkerIndex(); - InFlags |= Obj->GetMaskedFlags(RF_Native | RF_RootSet); + InternalSetFlags |= (Obj->GetInternalFlags() & (EInternalObjectFlags::Native | EInternalObjectFlags::RootSet)); if ( bCreatingCDO ) { check(Obj->HasAllFlags(RF_ClassDefaultObject)); Obj->SetFlags(InFlags); + Obj->SetInternalFlags(InternalSetFlags); // never call PostLoad on class default objects Obj->ClearFlags(RF_NeedPostLoad|RF_NeedPostLoadSubobjects); } @@ -2050,12 +2058,13 @@ UObject* StaticAllocateObject if (!bSubObject) { FMemory::Memzero((void *)Obj, TotalSize); - new ((void *)Obj) UObjectBase(InClass,InFlags,InOuter,InName); + new ((void *)Obj) UObjectBase(InClass, InFlags, InternalSetFlags, InOuter, InName); } else { // Propagate flags to subobjects created in the native constructor. Obj->SetFlags(InFlags); + Obj->SetInternalFlags(InternalSetFlags); } if (bWasConstructedOnOldObject) @@ -2078,13 +2087,10 @@ UObject* StaticAllocateObject // Sanity checks for async flags. // It's possible to duplicate an object on the game thread that is still being referenced // by async loading code or has been created on a different thread than the main thread. - if (Obj->HasAnyFlags(RF_AsyncLoading)) + Obj->ClearInternalFlags(EInternalObjectFlags::AsyncLoading); + if (Obj->HasAnyInternalFlags(EInternalObjectFlags::Async) && IsInGameThread()) { - Obj->ClearFlags(RF_AsyncLoading); - } - if (Obj->HasAnyFlags(RF_Async) && IsInGameThread()) - { - Obj->ClearFlags(RF_Async); + Obj->ClearInternalFlags(EInternalObjectFlags::Async); } } @@ -2242,7 +2248,7 @@ FObjectInitializer::~FObjectInitializer() // if this is a blueprint CDO that derives from another blueprint, and // that parent (archetype) CDO isn't fully serialized - if (!Class->HasAnyFlags(RF_Native) && !ArchetypeClass->HasAnyFlags(RF_Native) && bSuperCDONeedsLoad) + if (!Class->IsNative() && !ArchetypeClass->IsNative() && bSuperCDONeedsLoad) { FLinkerLoad* ClassLinker = Class->GetLinker(); if ((ClassLinker != nullptr) && (ClassLinker->LoadFlags & LOAD_DeferDependencyLoads) != 0x00) @@ -2593,15 +2599,19 @@ void FObjectInitializer::InitProperties(UObject* Obj, UClass* DefaultsClass, UOb bNeedInitialize = InitNonNativeProperty(P, Obj); } - if (bCopyTransientsFromClassDefaults && P->HasAnyPropertyFlags(CPF_Transient|CPF_DuplicateTransient|CPF_NonPIEDuplicateTransient)) + bool IsTransient = P->HasAnyPropertyFlags(CPF_Transient | CPF_DuplicateTransient | CPF_NonPIEDuplicateTransient); + if (!IsTransient || !P->ContainsInstancedObjectProperty()) { - // This is a duplicate. The value for all transient or non-duplicatable properties should be copied - // from the source class's defaults. - P->CopyCompleteValue_InContainer(Obj, ClassDefaults); - } - else if (P->IsInContainer(DefaultsClass)) - { - P->CopyCompleteValue_InContainer(Obj, DefaultData); + if (bCopyTransientsFromClassDefaults && IsTransient) + { + // This is a duplicate. The value for all transient or non-duplicatable properties should be copied + // from the source class's defaults. + P->CopyCompleteValue_InContainer(Obj, ClassDefaults); + } + else if (P->IsInContainer(DefaultsClass)) + { + P->CopyCompleteValue_InContainer(Obj, DefaultData); + } } } } @@ -2635,8 +2645,9 @@ UObject* StaticConstructObject_Internal UObject* InOuter /*=GetTransientPackage()*/, FName InName /*=NAME_None*/, EObjectFlags InFlags /*=0*/, + EInternalObjectFlags InternalSetFlags /*=0*/, UObject* InTemplate /*=NULL*/, - bool bCopyTransientsFromClassDefaults /*=false*/, + bool bCopyTransientsFromClassDefaults /*=false*/, FObjectInstancingGraph* InInstanceGraph /*=NULL*/ ) { @@ -2659,7 +2670,7 @@ UObject* StaticConstructObject_Internal const bool bCanRecycleSubobjects = bIsNativeFromCDO; #endif bool bRecycledSubobject = false; - Result = StaticAllocateObject(InClass, InOuter, InName, InFlags, bCanRecycleSubobjects, &bRecycledSubobject); + Result = StaticAllocateObject(InClass, InOuter, InName, InFlags, InternalSetFlags, bCanRecycleSubobjects, &bRecycledSubobject); check(Result != NULL); // Don't call the constructor on recycled subobjects, they haven't been destroyed. if (!bRecycledSubobject) @@ -2671,9 +2682,9 @@ UObject* StaticConstructObject_Internal if( GIsEditor && GUndo && (InFlags & RF_Transactional) && !(InFlags & RF_NeedLoad) && !InClass->IsChildOf(UField::StaticClass()) ) { // Set RF_PendingKill and update the undo buffer so an undo operation will set RF_PendingKill on the newly constructed object. - Result->SetFlags(RF_PendingKill); + Result->MarkPendingKill(); SaveToTransactionBuffer(Result, false); - Result->ClearFlags(RF_PendingKill); + Result->ClearPendingKill(); } return Result; } @@ -2689,7 +2700,7 @@ UObject* StaticConstructObject FObjectInstancingGraph* InInstanceGraph /*=NULL*/ ) { - return StaticConstructObject_Internal(InClass, InOuter, InName, InFlags, InTemplate, bCopyTransientsFromClassDefaults, InInstanceGraph); + return StaticConstructObject_Internal(InClass, InOuter, InName, InFlags, EInternalObjectFlags::None, InTemplate, bCopyTransientsFromClassDefaults, InInstanceGraph); } void FObjectInitializer::AssertIfInConstructor(UObject* Outer, const TCHAR* ErrorMessage) @@ -2712,9 +2723,10 @@ void FScopedObjectFlagMarker::SaveObjectFlags() { StoredObjectFlags.Empty(); - for ( FObjectIterator It; It; ++It ) + for (FObjectIterator It; It; ++It) { - StoredObjectFlags.Add(*It, It->GetFlags()); + UObject* Obj = *It; + StoredObjectFlags.Add(*It, FStoredObjectFlags(Obj->GetFlags(), Obj->GetInternalFlags())); } } @@ -2723,16 +2735,18 @@ void FScopedObjectFlagMarker::SaveObjectFlags() */ void FScopedObjectFlagMarker::RestoreObjectFlags() { - for ( TMap::TIterator It(StoredObjectFlags); It; ++It ) + for (TMap::TIterator It(StoredObjectFlags); It; ++It) { UObject* Object = It.Key(); - EObjectFlags PreviousObjectFlags = It.Value(); + FStoredObjectFlags& PreviousObjectFlags = It.Value(); // clear all flags Object->ClearFlags(RF_AllFlags); + Object->ClearInternalFlags(EInternalObjectFlags::AllFlags); // then reset the ones that were originally set - Object->SetFlags(PreviousObjectFlags); + Object->SetFlags(PreviousObjectFlags.Flags); + Object->SetInternalFlags(PreviousObjectFlags.InternalFlags); } } @@ -2826,7 +2840,7 @@ public: * @param FoundReferences If non-NULL, fill in with all objects that point to an object with SearchFlags set * @param */ - void PerformReachabilityAnalysis( EObjectFlags KeepFlags, EObjectFlags SearchFlags = RF_NoFlags, FReferencerInformationList* FoundReferences = NULL) + void PerformReachabilityAnalysis( EObjectFlags KeepFlags, EInternalObjectFlags InternalKeepFlags, EObjectFlags SearchFlags = RF_NoFlags, FReferencerInformationList* FoundReferences = NULL) { // Reset object count. extern int32 GObjectCountDuringLastMarkPhase; @@ -2842,11 +2856,11 @@ public: GObjectCountDuringLastMarkPhase++; // Special case handling for objects that are part of the root set. - if( Object->HasAnyFlags( RF_RootSet ) ) + if( Object->IsRooted() ) { checkSlow( Object->IsValidLowLevel() ); // We cannot use RF_PendingKill on objects that are part of the root set. - checkCode( if( Object->HasAnyFlags( RF_PendingKill ) ) { UE_LOG(LogUObjectGlobals, Fatal, TEXT("Object %s is part of root set though has been marked RF_PendingKill!"), *Object->GetFullName() ); } ); + checkCode( if( Object->IsPendingKill() ) { UE_LOG(LogUObjectGlobals, Fatal, TEXT("Object %s is part of root set though has been marked RF_PendingKill!"), *Object->GetFullName() ); } ); // Add to list of objects to serialize. ObjectsToSerialize.Add( Object ); } @@ -2854,13 +2868,15 @@ public: else { // Mark objects as unreachable unless they have any of the passed in KeepFlags set and none of the passed in Search. - if( (Object->HasAnyFlags(KeepFlags) || KeepFlags == RF_NoFlags) && !Object->HasAnyFlags( SearchFlags ) ) + if (!Object->HasAnyFlags(SearchFlags) && + ((KeepFlags == RF_NoFlags && InternalKeepFlags == EInternalObjectFlags::None) || Object->HasAnyFlags(KeepFlags) || Object->HasAnyInternalFlags(InternalKeepFlags)) + ) { - ObjectsToSerialize.Add( Object ); + ObjectsToSerialize.Add(Object); } else { - Object->SetFlags( RF_Unreachable ); + Object->SetInternalFlags(EInternalObjectFlags::Unreachable); } } } @@ -2911,7 +2927,7 @@ private: #endif // Mark it as reachable. - Object->ClearFlags( RF_Unreachable ); + Object->ThisThreadAtomicallyClearedRFUnreachable(); // Add it to the list of objects to serialize. ObjectsToSerialize.Add( Object ); @@ -2938,9 +2954,9 @@ private: CurrentReferenceInfo->TotalReferences++; } // Mark it as reachable. - InObject->ClearFlags(RF_Unreachable); + InObject->ThisThreadAtomicallyClearedRFUnreachable(); } - else if (InObject->HasAnyFlags(RF_Unreachable)) + else if (InObject->IsUnreachable()) { // Add encountered object reference to list of to be serialized objects if it hasn't already been added. AddToObjectList(InReferencingObject, InReferencingProperty, InObject); @@ -2970,9 +2986,9 @@ private: * @param FoundReferences If non-NULL fill in with list of objects that hold references * @return true if object is referenced, false otherwise */ -bool IsReferenced( UObject*& Obj, EObjectFlags KeepFlags, bool bCheckSubObjects, FReferencerInformationList* FoundReferences ) +bool IsReferenced(UObject*& Obj, EObjectFlags KeepFlags, EInternalObjectFlags InternalKeepFlags, bool bCheckSubObjects, FReferencerInformationList* FoundReferences) { - check(!Obj->HasAnyFlags(RF_Unreachable)); + check(!Obj->IsUnreachable()); FScopedObjectFlagMarker ObjectFlagMarker; bool bTempReferenceList = false; @@ -3006,12 +3022,12 @@ bool IsReferenced( UObject*& Obj, EObjectFlags KeepFlags, bool bCheckSubObjects, FCollectorTagUsedNonRecursive ObjectReferenceTagger; // Exclude passed in object when peforming reachability analysis. - ObjectReferenceTagger.PerformReachabilityAnalysis( KeepFlags, RF_TagGarbageTemp, FoundReferences ); + ObjectReferenceTagger.PerformReachabilityAnalysis(KeepFlags, InternalKeepFlags, RF_TagGarbageTemp, FoundReferences); bool bIsReferenced = false; if (FoundReferences) { - bIsReferenced = FoundReferences->ExternalReferences.Num() > 0 || !Obj->HasAnyFlags( RF_Unreachable ); + bIsReferenced = FoundReferences->ExternalReferences.Num() > 0 || !Obj->IsUnreachable(); // Move some from external to internal before returning for (int32 i = 0; i < FoundReferences->ExternalReferences.Num(); i++) { @@ -3032,7 +3048,7 @@ bool IsReferenced( UObject*& Obj, EObjectFlags KeepFlags, bool bCheckSubObjects, else { // Return whether the object was referenced and restore original state. - bIsReferenced = !Obj->HasAnyFlags( RF_Unreachable ); + bIsReferenced = !Obj->IsUnreachable(); } if (bTempReferenceList) diff --git a/Engine/Source/Runtime/CoreUObject/Private/UObject/UObjectHash.cpp b/Engine/Source/Runtime/CoreUObject/Private/UObject/UObjectHash.cpp index 531084da71d4..568ed369fc9a 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/UObject/UObjectHash.cpp +++ b/Engine/Source/Runtime/CoreUObject/Private/UObject/UObjectHash.cpp @@ -360,7 +360,7 @@ UObject* StaticFindObjectFastExplicitThreadSafe(FUObjectHashTables& ThreadHash, /** Finally check the explicit path */ if (ObjectPath == ObjectPathName) { - checkf(!Object->HasAnyFlags(RF_Unreachable), TEXT("%s"), *Object->GetFullName()); + checkf(!Object->IsUnreachable(), TEXT("%s"), *Object->GetFullName()); return Object; } } @@ -393,7 +393,7 @@ UObject* StaticFindObjectFastExplicit( UClass* ObjectClass, FName ObjectName, co return Result; } -UObject* StaticFindObjectFastInternalThreadSafe(FUObjectHashTables& ThreadHash, UClass* ObjectClass, UObject* ObjectPackage, FName ObjectName, bool bExactClass, bool bAnyPackage, EObjectFlags ExcludeFlags) +UObject* StaticFindObjectFastInternalThreadSafe(FUObjectHashTables& ThreadHash, UClass* ObjectClass, UObject* ObjectPackage, FName ObjectName, bool bExactClass, bool bAnyPackage, EObjectFlags ExcludeFlags, EInternalObjectFlags ExclusiveInternalFlags) { // If they specified an outer use that during the hashing UObject* Result = nullptr; @@ -415,9 +415,12 @@ UObject* StaticFindObjectFastInternalThreadSafe(FUObjectHashTables& ThreadHash, && Object->GetOuter() == ObjectPackage /** If a class was specified, check that the object is of the correct class */ - && (ObjectClass == nullptr || (bExactClass ? Object->GetClass() == ObjectClass : Object->IsA(ObjectClass)))) + && (ObjectClass == nullptr || (bExactClass ? Object->GetClass() == ObjectClass : Object->IsA(ObjectClass))) + + /** Include (or not) pending kill objects */ + && !Object->HasAnyInternalFlags(ExclusiveInternalFlags)) { - checkf(!Object->HasAnyFlags(RF_Unreachable), TEXT("%s"), *Object->GetFullName()); + checkf(!Object->IsUnreachable(), TEXT("%s"), *Object->GetFullName()); if (Result) { UE_LOG(LogUObjectHash, Warning, TEXT("Ambiguous search, could be %s or %s"), *GetFullNameSafe(Result), *GetFullNameSafe(Object)); @@ -470,7 +473,7 @@ UObject* StaticFindObjectFastInternalThreadSafe(FUObjectHashTables& ThreadHash, /** Ensure that the partial path provided matches the object found */ && (Object->GetPathName().EndsWith(ObjectNameString))) { - checkf(!Object->HasAnyFlags(RF_Unreachable), TEXT("%s"), *Object->GetFullName()); + checkf(!Object->IsUnreachable(), TEXT("%s"), *Object->GetFullName()); if (Result) { UE_LOG(LogUObjectHash, Warning, TEXT("Ambiguous search, could be %s or %s"), *GetFullNameSafe(Result), *GetFullNameSafe(Object)); @@ -490,7 +493,7 @@ UObject* StaticFindObjectFastInternalThreadSafe(FUObjectHashTables& ThreadHash, return Result; } -UObject* StaticFindObjectFastInternal( UClass* ObjectClass, UObject* ObjectPackage, FName ObjectName, bool bExactClass, bool bAnyPackage, EObjectFlags ExcludeFlags ) +UObject* StaticFindObjectFastInternal(UClass* ObjectClass, UObject* ObjectPackage, FName ObjectName, bool bExactClass, bool bAnyPackage, EObjectFlags ExcludeFlags, EInternalObjectFlags ExclusiveInternalFlags) { SCOPE_CYCLE_COUNTER( STAT_Hash_StaticFindObjectFastInternal ); INC_DWORD_STAT(STAT_FindObjectFast); @@ -498,7 +501,7 @@ UObject* StaticFindObjectFastInternal( UClass* ObjectClass, UObject* ObjectPacka check(ObjectPackage != ANY_PACKAGE); // this could never have returned anything but nullptr // If they specified an outer use that during the hashing auto& ThreadHash = FUObjectHashTables::Get(); - UObject* Result = StaticFindObjectFastInternalThreadSafe( ThreadHash, ObjectClass, ObjectPackage, ObjectName, bExactClass, bAnyPackage, ExcludeFlags ); + UObject* Result = StaticFindObjectFastInternalThreadSafe(ThreadHash, ObjectClass, ObjectPackage, ObjectName, bExactClass, bAnyPackage, ExcludeFlags, ExclusiveInternalFlags); return Result; } @@ -594,15 +597,14 @@ static void RemoveFromClassMap(FUObjectHashTables& ThreadHash, UObjectBase* Obje } } -void GetObjectsWithOuter(const class UObjectBase* Outer, TArray& Results, bool bIncludeNestedObjects, EObjectFlags ExclusionFlags) +void GetObjectsWithOuter(const class UObjectBase* Outer, TArray& Results, bool bIncludeNestedObjects, EObjectFlags ExclusionFlags, EInternalObjectFlags ExclusionInternalFlags) { - SCOPE_CYCLE_COUNTER( STAT_Hash_GetObjectsWithOuter ); - + SCOPE_CYCLE_COUNTER( STAT_Hash_GetObjectsWithOuter ); // We don't want to return any objects that are currently being background loaded unless we're using the object iterator during async loading. - ExclusionFlags |= RF_Unreachable; + ExclusionInternalFlags |= EInternalObjectFlags::Unreachable; if (!IsInAsyncLoadingThread()) { - ExclusionFlags = EObjectFlags(ExclusionFlags | RF_AsyncLoading); + ExclusionInternalFlags |= EInternalObjectFlags::AsyncLoading; } int32 StartNum = Results.Num(); auto& ThreadHash = FUObjectHashTables::Get(); @@ -613,7 +615,7 @@ void GetObjectsWithOuter(const class UObjectBase* Outer, TArray& Resu for (TSet::TConstIterator It(*Inners); It; ++It) { UObject *Object = static_cast(*It); - if (!Object->HasAnyFlags(ExclusionFlags)) + if (!Object->HasAnyFlags(ExclusionFlags) && !Object->HasAnyInternalFlags(ExclusionInternalFlags)) { Results.Add(Object); } @@ -632,7 +634,7 @@ void GetObjectsWithOuter(const class UObjectBase* Outer, TArray& Resu for (TSet::TConstIterator It(*InnerInners); It; ++It) { UObject *Object = static_cast(*It); - if (!Object->HasAnyFlags(ExclusionFlags)) + if (!Object->HasAnyFlags(ExclusionFlags) && !Object->HasAnyInternalFlags(ExclusionInternalFlags)) { Results.Add(Object); } @@ -644,13 +646,13 @@ void GetObjectsWithOuter(const class UObjectBase* Outer, TArray& Resu } } -void ForEachObjectWithOuter(const class UObjectBase* Outer, TFunctionRef Operation, bool bIncludeNestedObjects, EObjectFlags ExclusionFlags) +void ForEachObjectWithOuter(const class UObjectBase* Outer, TFunctionRef Operation, bool bIncludeNestedObjects, EObjectFlags ExclusionFlags, EInternalObjectFlags ExclusionInternalFlags) { // We don't want to return any objects that are currently being background loaded unless we're using the object iterator during async loading. - ExclusionFlags |= RF_Unreachable; + ExclusionInternalFlags |= EInternalObjectFlags::Unreachable; if (!IsInAsyncLoadingThread()) { - ExclusionFlags = EObjectFlags(ExclusionFlags | RF_AsyncLoading); + ExclusionInternalFlags |= EInternalObjectFlags::AsyncLoading; } FUObjectHashTables& ThreadHash = FUObjectHashTables::Get(); FHashTableLock HashLock(ThreadHash); @@ -666,7 +668,7 @@ void ForEachObjectWithOuter(const class UObjectBase* Outer, TFunctionRef::TConstIterator It(*Inners); It; ++It) { UObject *Object = static_cast(*It); - if (!Object->HasAnyFlags(ExclusionFlags)) + if (!Object->HasAnyFlags(ExclusionFlags) && !Object->HasAnyInternalFlags(ExclusionInternalFlags)) { Operation(Object); if (bIncludeNestedObjects) @@ -688,31 +690,31 @@ UObjectBase* FindObjectWithOuter(class UObjectBase* Outer, class UClass* ClassTo UObject* Result = nullptr; check( Outer && ClassToLookFor ); // We don't want to return any objects that are currently being background loaded unless we're using the object iterator during async loading. - EObjectFlags ExclusionFlags = RF_Unreachable; + EInternalObjectFlags ExclusionInternalFlags = EInternalObjectFlags::Unreachable; if (!IsInAsyncLoadingThread()) { - ExclusionFlags = EObjectFlags( ExclusionFlags | RF_AsyncLoading ); + ExclusionInternalFlags = EInternalObjectFlags::AsyncLoading; } if( NameToLookFor != NAME_None ) { - Result = StaticFindObjectFastInternal( ClassToLookFor, static_cast(Outer), NameToLookFor, false, false, ExclusionFlags ); + Result = StaticFindObjectFastInternal(ClassToLookFor, static_cast(Outer), NameToLookFor, false, false, RF_NoFlags, ExclusionInternalFlags); } else { auto& ThreadHash = FUObjectHashTables::Get(); - FHashTableLock HashLock( ThreadHash ); + FHashTableLock HashLock(ThreadHash); TSet const* Inners = ThreadHash.ObjectOuterMap.Find( Outer ); - if( Inners ) + if (Inners) { - for( TSet::TConstIterator It( *Inners ); It; ++It ) + for (TSet::TConstIterator It(*Inners); It; ++It) { UObject *Object = static_cast(*It); - if( Object->HasAnyFlags( ExclusionFlags ) ) + if (Object->HasAnyInternalFlags(ExclusionInternalFlags)) { continue; } - if( !Object->IsA( ClassToLookFor ) ) + if (!Object->IsA(ClassToLookFor)) { continue; } @@ -742,9 +744,11 @@ static void RecursivelyPopulateDerivedClasses(FUObjectHashTables& ThreadHash, UC } } -static void GetObjectsOfClassThreadSafe(FUObjectHashTables& ThreadHash, TSet& ClassesToSearch, TArray& Results, EObjectFlags ExclusionFlags) +static void GetObjectsOfClassThreadSafe(FUObjectHashTables& ThreadHash, TSet& ClassesToSearch, TArray& Results, EObjectFlags ExclusionFlags, EInternalObjectFlags ExclusionInternalFlags) { + ExclusionInternalFlags |= EInternalObjectFlags::Unreachable; FHashTableLock HashLock(ThreadHash); + for (auto ClassIt = ClassesToSearch.CreateConstIterator(); ClassIt; ++ClassIt) { TSet const* List = ThreadHash.ClassToObjectListMap.Find(*ClassIt); @@ -753,7 +757,7 @@ static void GetObjectsOfClassThreadSafe(FUObjectHashTables& ThreadHash, TSetCreateConstIterator(); ObjectIt; ++ObjectIt) { UObject *Object = static_cast(*ObjectIt); - if (!Object->HasAnyFlags(ExclusionFlags)) + if (!Object->HasAnyFlags(ExclusionFlags) && !Object->HasAnyInternalFlags(ExclusionInternalFlags)) { Results.Add(Object); } @@ -761,17 +765,16 @@ static void GetObjectsOfClassThreadSafe(FUObjectHashTables& ThreadHash, TSet& Results, bool bIncludeDerivedClasses, EObjectFlags AdditionalExcludeFlags) +void GetObjectsOfClass(UClass* ClassToLookFor, TArray& Results, bool bIncludeDerivedClasses, EObjectFlags ExclusionFlags, EInternalObjectFlags ExclusionInternalFlags) { SCOPE_CYCLE_COUNTER( STAT_Hash_GetObjectsOfClass ); - + // We don't want to return any objects that are currently being background loaded unless we're using the object iterator during async loading. - EObjectFlags ExclusionFlags = RF_Unreachable; + ExclusionInternalFlags |= EInternalObjectFlags::Unreachable; if (!IsInAsyncLoadingThread()) { - ExclusionFlags |= RF_AsyncLoading; + ExclusionInternalFlags |= EInternalObjectFlags::AsyncLoading; } - ExclusionFlags |= AdditionalExcludeFlags; TSet ClassesToSearch; ClassesToSearch.Add( ClassToLookFor ); @@ -782,20 +785,19 @@ void GetObjectsOfClass(UClass* ClassToLookFor, TArray& Results, bool RecursivelyPopulateDerivedClasses( ThreadHash, ClassToLookFor, ClassesToSearch ); } - GetObjectsOfClassThreadSafe( FUObjectHashTables::Get(), ClassesToSearch, Results, ExclusionFlags ); + GetObjectsOfClassThreadSafe(FUObjectHashTables::Get(), ClassesToSearch, Results, ExclusionFlags, ExclusionInternalFlags); check( Results.Num() <= GUObjectArray.GetObjectArrayNum() ); // otherwise we have a cycle in the outer chain, which should not be possible } -void ForEachObjectOfClass(UClass* ClassToLookFor, TFunctionRef Operation, bool bIncludeDerivedClasses, EObjectFlags AdditionalExcludeFlags) +void ForEachObjectOfClass(UClass* ClassToLookFor, TFunctionRef Operation, bool bIncludeDerivedClasses, EObjectFlags ExclusionFlags, EInternalObjectFlags ExclusionInternalFlags) { // We don't want to return any objects that are currently being background loaded unless we're using the object iterator during async loading. - EObjectFlags ExclusionFlags = RF_Unreachable; + ExclusionInternalFlags |= EInternalObjectFlags::Unreachable; if (!IsInAsyncLoadingThread()) { - ExclusionFlags |= RF_AsyncLoading; + ExclusionInternalFlags |= EInternalObjectFlags::AsyncLoading; } - ExclusionFlags |= AdditionalExcludeFlags; FUObjectHashTables& ThreadHash = FUObjectHashTables::Get(); FHashTableLock HashLock(ThreadHash); @@ -815,7 +817,7 @@ void ForEachObjectOfClass(UClass* ClassToLookFor, TFunctionRef for (auto ObjectIt = List->CreateConstIterator(); ObjectIt; ++ObjectIt) { UObject *Object = static_cast(*ObjectIt); - if (!Object->HasAnyFlags(ExclusionFlags)) + if (!Object->HasAnyFlags(ExclusionFlags) && !Object->HasAnyInternalFlags(ExclusionInternalFlags)) { Operation(Object); } diff --git a/Engine/Source/Runtime/CoreUObject/Private/UObject/UObjectMarks.cpp b/Engine/Source/Runtime/CoreUObject/Private/UObject/UObjectMarks.cpp index cc6ce3031a38..ad5abad205d5 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/UObject/UObjectMarks.cpp +++ b/Engine/Source/Runtime/CoreUObject/Private/UObject/UObjectMarks.cpp @@ -119,10 +119,10 @@ bool ObjectHasAllMarks(const class UObjectBase* Object, EObjectMark Marks) void GetObjectsWithAllMarks(TArray& Results, EObjectMark Marks) { // We don't want to return any objects that are currently being background loaded unless we're using the object iterator during async loading. - EObjectFlags ExclusionFlags = RF_Unreachable; + EInternalObjectFlags ExclusionFlags = EInternalObjectFlags::Unreachable; if (!IsInAsyncLoadingThread()) { - ExclusionFlags = EObjectFlags(ExclusionFlags | RF_AsyncLoading); + ExclusionFlags |= EInternalObjectFlags::AsyncLoading; } const TMap& Map = MarkAnnotation.GetAnnotationMap(); Results.Empty(Map.Num()); @@ -131,7 +131,7 @@ void GetObjectsWithAllMarks(TArray& Results, EObjectMark Marks) if ((It.Value().Marks & Marks) == Marks) { UObject* Item = (UObject*)It.Key(); - if (!Item->HasAnyFlags(ExclusionFlags)) + if (!Item->HasAnyInternalFlags(ExclusionFlags)) { Results.Add(Item); } @@ -142,10 +142,10 @@ void GetObjectsWithAllMarks(TArray& Results, EObjectMark Marks) void GetObjectsWithAnyMarks(TArray& Results, EObjectMark Marks) { // We don't want to return any objects that are currently being background loaded unless we're using the object iterator during async loading. - EObjectFlags ExclusionFlags = RF_Unreachable; + EInternalObjectFlags ExclusionFlags = EInternalObjectFlags::Unreachable; if (!IsInAsyncLoadingThread()) { - ExclusionFlags = EObjectFlags(ExclusionFlags | RF_AsyncLoading); + ExclusionFlags |= EInternalObjectFlags::AsyncLoading; } const TMap& Map = MarkAnnotation.GetAnnotationMap(); Results.Empty(Map.Num()); @@ -154,7 +154,7 @@ void GetObjectsWithAnyMarks(TArray& Results, EObjectMark Marks) if (It.Value().Marks & Marks) { UObject* Item = (UObject*)It.Key(); - if (!Item->HasAnyFlags(ExclusionFlags)) + if (!Item->HasAnyInternalFlags(ExclusionFlags)) { Results.Add(Item); } diff --git a/Engine/Source/Runtime/CoreUObject/Private/UObject/WeakObjectPtr.cpp b/Engine/Source/Runtime/CoreUObject/Private/UObject/WeakObjectPtr.cpp index 165b74e2f788..73f05a48fb4c 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/UObject/WeakObjectPtr.cpp +++ b/Engine/Source/Runtime/CoreUObject/Private/UObject/WeakObjectPtr.cpp @@ -14,181 +14,10 @@ DEFINE_LOG_CATEGORY_STATIC(LogWeakObjectPtr, Log, All); int32** GSerialNumberBlocksForDebugVisualizersRoot = 0; -/** Helper struct for a block of serial numbers **/ -struct FSerialNumberBlock -{ - enum - { - START_SERIAL_NUMBER = 1000, //Starting serial number...leave some room to catch corruption - SERIAL_NUMBER_BLOCK_SIZE = 16384, // number of UObjects oer block of serial numbers...if you change this, you need to change the visualizer - MAX_SERIAL_NUMBER_BLOCKS = 4096, // enough for about 7M UObjects, - MAX_UOBJECTS = SERIAL_NUMBER_BLOCK_SIZE * MAX_SERIAL_NUMBER_BLOCKS, - }; - - int32 SerialNumbers[SERIAL_NUMBER_BLOCK_SIZE]; - FSerialNumberBlock() - { - for (int32 Index = 0; Index < SERIAL_NUMBER_BLOCK_SIZE; Index++) - { - SerialNumbers[Index] = 0; - } - } -}; - -/** Helper class to manage the serial numbers in a way that is thread-safe (deletion of UObjects is assummed to be non-concurrent, but getting serial numbers is thread safe. **/ -class FSerialNumberManager : public FUObjectArray::FUObjectDeleteListener -{ - /** static list of blocks of serial numbers, these are allocated on demand (and leaked at shutdown) **/ - struct FSerialNumberBlock* Blocks[FSerialNumberBlock::MAX_SERIAL_NUMBER_BLOCKS]; - /** Current master serial number **/ - FThreadSafeCounter MasterSerialNumber; - -public: - - /** Constructor, intializes the blocks to unallocated and intializes the master serial number **/ - FSerialNumberManager() - : MasterSerialNumber(FSerialNumberBlock::START_SERIAL_NUMBER) - { - for (int32 Index = 0; Index < FSerialNumberBlock::MAX_SERIAL_NUMBER_BLOCKS; Index++) - { - Blocks[Index] = NULL; - } - GSerialNumberBlocksForDebugVisualizersRoot = (int32**)&Blocks; - - FCoreDelegates::GetSerialNumberBlocksForDebugVisualizersDelegate().BindStatic(&GetSerialNumberBlocksForDebugVisualizersPtr); - } - /** Destructor, does nothing, leaks the memory **/ - ~FSerialNumberManager() - { - } - - /** - * Given a UObject index fetch the block and subindex for the serial number - * @param Index - UObject Index - * @param OutBlock - Serial Number Block - * @param OutSubIndex - Serial Number SubIndex - */ - FORCEINLINE_DEBUGGABLE void GetBlock(const int32& InIndex, int32& OutBlock, int32& OutSubIndex) const - { - OutBlock = InIndex / FSerialNumberBlock::SERIAL_NUMBER_BLOCK_SIZE; - OutSubIndex = InIndex - OutBlock * FSerialNumberBlock::SERIAL_NUMBER_BLOCK_SIZE; - checkfSlow(InIndex >= 0 && OutSubIndex >= 0 && OutSubIndex < FSerialNumberBlock::SERIAL_NUMBER_BLOCK_SIZE, TEXT("[Index:%d] [SubIndex:%d] invalid index or subindex"), InIndex, OutSubIndex); - if (OutBlock >= FSerialNumberBlock::MAX_SERIAL_NUMBER_BLOCKS) - { - UE_LOG(LogWeakObjectPtr, Fatal, TEXT("[Index:%d] exceeds maximum number of UObjects, [Block:%d] increase MAX_SERIAL_NUMBER_BLOCKS"), InIndex, OutBlock); - } - } - - /** - * Given a UObject index return the serial number. If it doesn't have a serial number, give it one. Threadsafe. - * @param Index - UObject Index - * @return - the serial number for this UObject - */ - int32 GetAndAllocateSerialNumber(int32 Index) - { - int32 Block, SubIndex; - GetBlock( Index, Block, SubIndex ); - volatile FSerialNumberBlock** BlockPtr = (volatile FSerialNumberBlock**)&Blocks[Block]; - FSerialNumberBlock* BlockToUse = (FSerialNumberBlock*)*BlockPtr; - if (!BlockToUse) - { - BlockToUse = new FSerialNumberBlock(); - FSerialNumberBlock* ValueWas = (FSerialNumberBlock*)FPlatformAtomics::InterlockedCompareExchangePointer((void**)BlockPtr,BlockToUse,NULL); - if (ValueWas != NULL) - { - // some other thread already added this block - delete BlockToUse; - BlockToUse = ValueWas; - } - } - checkSlow(BlockToUse); - - volatile int32 *SerialNumberPtr = &BlockToUse->SerialNumbers[SubIndex]; - int32 SerialNumber = *SerialNumberPtr; - if (!SerialNumber) - { - SerialNumber = MasterSerialNumber.Increment(); - if (SerialNumber <= FSerialNumberBlock::START_SERIAL_NUMBER) - { - UE_LOG(LogWeakObjectPtr, Fatal,TEXT("UObject serial numbers overflowed.")); - } - int32 ValueWas = FPlatformAtomics::InterlockedCompareExchange((int32 *)SerialNumberPtr,SerialNumber,0); - if (ValueWas != 0) - { - // someone else go it first, use their value - SerialNumber = ValueWas; - } - } - checkSlow(SerialNumber > FSerialNumberBlock::START_SERIAL_NUMBER); - return SerialNumber; - } - - /** - * Given a UObject index return the serial number. If it doesn't have a serial number, return 0. Threadsafe. - * @param Index - UObject Index - * @return - the serial number for this UObject - */ - FORCEINLINE_DEBUGGABLE int32 GetSerialNumber(int32 Index) - { - int32 Block, SubIndex; - GetBlock( Index, Block, SubIndex ); - volatile FSerialNumberBlock** BlockPtr = (volatile FSerialNumberBlock**)&Blocks[Block]; - FSerialNumberBlock* BlockToUse = (FSerialNumberBlock*)*BlockPtr; - int32 SerialNumber = 0; - if (BlockToUse) - { - volatile int32 *SerialNumberPtr = &BlockToUse->SerialNumbers[SubIndex]; - SerialNumber = *SerialNumberPtr; - } - return SerialNumber; - } - /** - * Interface for FUObjectAllocator::FUObjectDeleteListener, resets the serial number for this index so that all weak pointers to this UObject are invalidated - * - * @param Object object that has been destroyed - * @param Index index of object that is being deleted - */ - virtual void NotifyUObjectDeleted(const UObjectBase *Object, int32 Index) - { - int32 Block, SubIndex; - GetBlock( Index, Block, SubIndex ); - volatile FSerialNumberBlock** BlockPtr = (volatile FSerialNumberBlock**)&Blocks[Block]; - FSerialNumberBlock* BlockToUse = (FSerialNumberBlock*)*BlockPtr; - if (BlockToUse) - { - volatile int32 *SerialNumberPtr = &BlockToUse->SerialNumbers[SubIndex]; - int32 SerialNumber = *SerialNumberPtr; - if (SerialNumber) - { - checkSlow(IsInGameThread()); - *SerialNumberPtr = 0; - FPlatformMisc::MemoryBarrier(); - checkSlow(!*SerialNumberPtr); // nobody should be creating these while we are zeroing them! - } - } - } - - static int32*** GetSerialNumberBlocksForDebugVisualizersPtr() - { - return &GSerialNumberBlocksForDebugVisualizersRoot; - } -}; - -static FSerialNumberManager GSerialNumberManager; - - /*----------------------------------------------------------------------------------------------------------- FWeakObjectPtr -------------------------------------------------------------------------------------------------------------*/ -/** - * Startup the weak object system -**/ -void FWeakObjectPtr::Init() -{ - GUObjectArray.AddUObjectDeleteListener(&GSerialNumberManager); -} - /** * Copy from an object pointer * @param Object object to create a weak pointer to @@ -199,7 +28,7 @@ void FWeakObjectPtr::operator=(const class UObject *Object) ) { ObjectIndex = GUObjectArray.ObjectToIndex((UObjectBase*)Object); - ObjectSerialNumber = GSerialNumberManager.GetAndAllocateSerialNumber(ObjectIndex); + ObjectSerialNumber = GUObjectArray.AllocateSerialNumber(ObjectIndex); checkSlow(SerialNumbersMatch()); } else @@ -208,46 +37,12 @@ void FWeakObjectPtr::operator=(const class UObject *Object) } } -/** - * internal function to test for serial number matches - * @return true if the serial number in this matches the central table -**/ -FORCEINLINE_DEBUGGABLE bool FWeakObjectPtr::SerialNumbersMatch() const -{ - checkSlow(ObjectSerialNumber > FSerialNumberBlock::START_SERIAL_NUMBER && ObjectIndex >= 0 && ObjectIndex < FSerialNumberBlock::MAX_UOBJECTS); // otherwise this is a corrupted weak pointer - int32 ActualSerialNumber = GSerialNumberManager.GetSerialNumber(ObjectIndex); - checkSlow(!ActualSerialNumber || ActualSerialNumber >= ObjectSerialNumber); // serial numbers should never shrink - return ActualSerialNumber == ObjectSerialNumber; -} - - bool FWeakObjectPtr::IsValid(bool bEvenIfPendingKill, bool bThreadsafeTest) const { // This is the external function, so we just pass through to the internal inlined method. return Internal_IsValid(bEvenIfPendingKill, bThreadsafeTest); } -FORCEINLINE_DEBUGGABLE bool FWeakObjectPtr::Internal_IsValid(bool bEvenIfPendingKill, bool bThreadsafeTest) const -{ - if (ObjectSerialNumber == 0) - { - checkSlow(ObjectIndex == 0 || ObjectIndex == -1); // otherwise this is a corrupted weak pointer - return false; - } - if (ObjectIndex < 0) - { - return false; - } - if (!SerialNumbersMatch()) - { - return false; - } - if (bThreadsafeTest) - { - return true; - } - return GUObjectArray.IsValid(ObjectIndex, bEvenIfPendingKill); -} bool FWeakObjectPtr::IsStale(bool bEvenIfPendingKill, bool bThreadsafeTest) const { @@ -260,7 +55,12 @@ bool FWeakObjectPtr::IsStale(bool bEvenIfPendingKill, bool bThreadsafeTest) cons { return true; } - if (!SerialNumbersMatch()) + FUObjectItem* ObjectItem = GUObjectArray.IndexToObject(ObjectIndex); + if (!ObjectItem) + { + return true; + } + if (!SerialNumbersMatch(ObjectItem)) { return true; } @@ -268,18 +68,7 @@ bool FWeakObjectPtr::IsStale(bool bEvenIfPendingKill, bool bThreadsafeTest) cons { return false; } - return GUObjectArray.IsStale(ObjectIndex, bEvenIfPendingKill); -} - -FORCEINLINE_DEBUGGABLE UObject* FWeakObjectPtr::Internal_Get(bool bEvenIfPendingKill) const -{ - UObject* Result = nullptr; - - if (Internal_IsValid(true, true)) - { - Result = (UObject*)(GUObjectArray.IndexToValidObject(GetObjectIndex(), bEvenIfPendingKill)); - } - return Result; + return GUObjectArray.IsStale(ObjectItem, bEvenIfPendingKill); } UObject* FWeakObjectPtr::Get(/*bool bEvenIfPendingKill = false*/) const @@ -298,7 +87,8 @@ UObject* FWeakObjectPtr::GetEvenIfUnreachable() const UObject* Result = nullptr; if (Internal_IsValid(true, true)) { - Result = static_cast(GUObjectArray.IndexToObject(GetObjectIndex(), true)); + FUObjectItem* ObjectItem = GUObjectArray.IndexToObject(GetObjectIndex(), true); + Result = static_cast(ObjectItem->Object); } return Result; } diff --git a/Engine/Source/Runtime/CoreUObject/Public/Interface.h b/Engine/Source/Runtime/CoreUObject/Public/Interface.h index aa13568e787e..7442f4c0ba57 100644 --- a/Engine/Source/Runtime/CoreUObject/Public/Interface.h +++ b/Engine/Source/Runtime/CoreUObject/Public/Interface.h @@ -11,7 +11,7 @@ class COREUOBJECT_API UInterface : public UObject { - DECLARE_CLASS_INTRINSIC(UInterface,UObject,CLASS_Interface|CLASS_Abstract,CoreUObject) + DECLARE_CLASS_INTRINSIC(UInterface, UObject, CLASS_Interface | CLASS_Abstract, TEXT("/Script/CoreUObject")) }; class COREUOBJECT_API IInterface diff --git a/Engine/Source/Runtime/CoreUObject/Public/Misc/PackageName.h b/Engine/Source/Runtime/CoreUObject/Public/Misc/PackageName.h index e4e0c6a80519..ae4307fb6648 100644 --- a/Engine/Source/Runtime/CoreUObject/Public/Misc/PackageName.h +++ b/Engine/Source/Runtime/CoreUObject/Public/Misc/PackageName.h @@ -119,7 +119,7 @@ public: * @param Package Package which name to convert. * @return Short package name. */ - static FString GetShortName(UPackage* Package); + static FString GetShortName(const UPackage* Package); /** * Converts package name to short name. * diff --git a/Engine/Source/Runtime/CoreUObject/Public/Misc/TextBuffer.h b/Engine/Source/Runtime/CoreUObject/Public/Misc/TextBuffer.h index 1ca49f99f9bd..8310ecdea513 100644 --- a/Engine/Source/Runtime/CoreUObject/Public/Misc/TextBuffer.h +++ b/Engine/Source/Runtime/CoreUObject/Public/Misc/TextBuffer.h @@ -14,7 +14,7 @@ class UTextBuffer : public UObject , public FOutputDevice { - DECLARE_CASTED_CLASS_INTRINSIC_WITH_API(UTextBuffer,UObject,0,CoreUObject,CASTCLASS_None,COREUOBJECT_API) + DECLARE_CASTED_CLASS_INTRINSIC_WITH_API(UTextBuffer, UObject, 0, TEXT("/Script/CoreUObject"), CASTCLASS_None, COREUOBJECT_API) public: diff --git a/Engine/Source/Runtime/CoreUObject/Public/Serialization/ArchiveUObject.h b/Engine/Source/Runtime/CoreUObject/Public/Serialization/ArchiveUObject.h index bd9048072f8c..2694caabcd97 100644 --- a/Engine/Source/Runtime/CoreUObject/Public/Serialization/ArchiveUObject.h +++ b/Engine/Source/Runtime/CoreUObject/Public/Serialization/ArchiveUObject.h @@ -409,7 +409,7 @@ public: * @param InTargetObjects array of objects to search for references to * @param bFindAlsoWeakReferences should we also look into weak references? */ - COREUOBJECT_API FFindReferencersArchive(class UObject* PotentialReferencer, TArray InTargetObjects, bool bFindAlsoWeakReferences = false); + COREUOBJECT_API FFindReferencersArchive(class UObject* PotentialReferencer, const TArray& InTargetObjects, bool bFindAlsoWeakReferences = false); /** * Retrieves the number of references from PotentialReferencer to the object specified. @@ -927,6 +927,8 @@ private: int64 Offset; EObjectFlags FlagMask; EObjectFlags ApplyFlags; + EInternalObjectFlags InternalFlagMask; + EInternalObjectFlags ApplyInternalFlags; /** * This is used to prevent object & component instancing resulting from the calls to StaticConstructObject(); instancing subobjects and components is pointless, @@ -1018,6 +1020,8 @@ public: UObject* DestObject, EObjectFlags InFlagMask, EObjectFlags InApplyMask, + EInternalObjectFlags InInternalFlagMask, + EInternalObjectFlags InApplyInternalFlags, FObjectInstancingGraph* InInstanceGraph, uint32 InPortFlags); }; @@ -1055,7 +1059,8 @@ public: bool bNullPrivateRefs, bool bIgnoreOuterRef, bool bIgnoreArchetypeRef, - bool bDelayStart=false + bool bDelayStart = false, + bool bIgnoreClassGeneratedByRef = true ) : SearchObject(InSearchObject), ReplacementMap(inReplacementMap) , Count(0), bNullPrivateReferences(bNullPrivateRefs) @@ -1064,6 +1069,7 @@ public: ArIsModifyingWeakAndStrongReferences = true; // Also replace weak references too! ArIgnoreArchetypeRef = bIgnoreArchetypeRef; ArIgnoreOuterRef = bIgnoreOuterRef; + ArIgnoreClassGeneratedByRef = bIgnoreClassGeneratedByRef; if ( !bDelayStart ) { @@ -1391,7 +1397,7 @@ public: */ FArchive& operator<<( UObject*& Object ) { - if ( Object != NULL && !(Object->HasAnyMarks(OBJECTMARK_TagExp) || Object->HasAnyFlags(RF_PendingKill|RF_Unreachable)) ) + if (Object != NULL && !(Object->HasAnyMarks(OBJECTMARK_TagExp) || Object->IsPendingKillOrUnreachable()) ) { Object->Mark(OBJECTMARK_TagExp); diff --git a/Engine/Source/Runtime/CoreUObject/Public/Serialization/AsyncLoading.h b/Engine/Source/Runtime/CoreUObject/Public/Serialization/AsyncLoading.h index 673e3cf87884..af780c2a98ad 100644 --- a/Engine/Source/Runtime/CoreUObject/Public/Serialization/AsyncLoading.h +++ b/Engine/Source/Runtime/CoreUObject/Public/Serialization/AsyncLoading.h @@ -252,7 +252,7 @@ private: /** * Begin async loading process. Simulates parts of BeginLoad. * - * Objects created during BeginAsyncLoad and EndAsyncLoad will have RF_AsyncLoading set + * Objects created during BeginAsyncLoad and EndAsyncLoad will have EInternalObjectFlags::AsyncLoading set */ void BeginAsyncLoad(); /** @@ -309,7 +309,7 @@ private: */ EAsyncPackageState::Type PostLoadObjects(); /** - * Finish up objects and state, which means clearing the RF_AsyncLoading flag on newly created ones + * Finish up objects and state, which means clearing the EInternalObjectFlags::AsyncLoading flag on newly created ones * * @return true */ diff --git a/Engine/Source/Runtime/CoreUObject/Public/Serialization/BulkData.h b/Engine/Source/Runtime/CoreUObject/Public/Serialization/BulkData.h index ea4754cbf4c1..18ccc6cc5d24 100644 --- a/Engine/Source/Runtime/CoreUObject/Public/Serialization/BulkData.h +++ b/Engine/Source/Runtime/CoreUObject/Public/Serialization/BulkData.h @@ -62,6 +62,88 @@ enum EBulkDataLockFlags */ struct COREUOBJECT_API FUntypedBulkData { +private: + // This struct represents an optional allocation. + struct FAllocatedPtr + { + FAllocatedPtr() + : Ptr (nullptr) + , bAllocated(false) + { + } + + FAllocatedPtr(FAllocatedPtr&& Other) + : Ptr (Other.Ptr) + , bAllocated(Other.bAllocated) + { + Other.Ptr = nullptr; + Other.bAllocated = false; + } + + FAllocatedPtr& operator=(FAllocatedPtr&& Other) + { + Swap(*this, Other); + Other.Deallocate(); + + return *this; + } + + ~FAllocatedPtr() + { + FMemory::Free(Ptr); + } + + void* Get() const + { + return Ptr; + } + + FORCEINLINE_EXPLICIT_OPERATOR_BOOL() const + { + return bAllocated; + } + + SAFE_BOOL_OPERATORS(FAllocatedPtr) + + void Reallocate(int32 Count, int32 Alignment = DEFAULT_ALIGNMENT) + { + if (Count) + { + Ptr = FMemory::Realloc(Ptr, Count, Alignment); + } + else + { + FMemory::Free(Ptr); + Ptr = nullptr; + } + + bAllocated = true; + } + + void* ReleaseWithoutDeallocating() + { + void* Result = Ptr; + Ptr = nullptr; + bAllocated = false; + return Result; + } + + void Deallocate() + { + FMemory::Free(Ptr); + Ptr = nullptr; + bAllocated = false; + } + + private: + FAllocatedPtr(const FAllocatedPtr&); + FAllocatedPtr& operator=(const FAllocatedPtr&); + + void* Ptr; + bool bAllocated; + }; + +public: friend class FLinkerLoad; /*----------------------------------------------------------------------------- @@ -85,20 +167,6 @@ struct COREUOBJECT_API FUntypedBulkData */ virtual ~FUntypedBulkData(); - /** - * Get resource memory preallocated for serializing bulk data into - * This is typically GPU accessible memory to avoid multiple allocations copies from system memory - * If NULL is returned then default to allocating from system memory - * - * @param Owner object with bulk data being serialized - * @param Idx entry when serializing out of an array - * @return pointer to resource memory or NULL - */ - virtual void* GetBulkDataResourceMemory(UObject* Owner,int32 Idx) - { - return NULL; - } - /** * Copies the source array into this one after detaching from archive. * @@ -228,13 +296,6 @@ struct COREUOBJECT_API FUntypedBulkData */ void ClearBulkDataFlags( uint32 BulkDataFlagsToClear ); - /** - * BulkData memory allocated from a resource should only be freed by the resource - * - * @return true if bulk data should free allocated memory - */ - bool ShouldFreeOnEmpty() const; - /** * Returns the filename this bulkdata resides in * @@ -427,17 +488,15 @@ private: int32 BulkDataAlignment; /** Pointer to cached bulk data */ - void* BulkData; - /** Pointer to cached async bulk data */ - void* BulkDataAsync; + FAllocatedPtr BulkData; + /** Pointer to cached async bulk data */ + FAllocatedPtr BulkDataAsync; /** Current lock status */ uint32 LockStatus; /** Async helper for loading bulk data on a separate thread */ TFuture SerializeFuture; protected: - /** true when data has been allocated internally by the bulk data and does not come from a preallocated resource */ - bool bShouldFreeOnEmpty; /** name of the package file containing the bulkdata */ FString Filename; #if WITH_EDITOR diff --git a/Engine/Source/Runtime/CoreUObject/Public/UObject/Class.h b/Engine/Source/Runtime/CoreUObject/Public/UObject/Class.h index 5536a60f4906..464b5939b05c 100644 --- a/Engine/Source/Runtime/CoreUObject/Public/UObject/Class.h +++ b/Engine/Source/Runtime/CoreUObject/Public/UObject/Class.h @@ -43,7 +43,7 @@ struct FRepRecord // class COREUOBJECT_API UField : public UObject { - DECLARE_CASTED_CLASS_INTRINSIC(UField,UObject,CLASS_Abstract,CoreUObject,CASTCLASS_UField) + DECLARE_CASTED_CLASS_INTRINSIC(UField, UObject, CLASS_Abstract, TEXT("/Script/CoreUObject"), CASTCLASS_UField) // Variables. UField* Next; @@ -236,7 +236,7 @@ class COREUOBJECT_API UField : public UObject */ class COREUOBJECT_API UStruct : public UField { - DECLARE_CASTED_CLASS_INTRINSIC(UStruct,UField,0,CoreUObject,CASTCLASS_UStruct) + DECLARE_CASTED_CLASS_INTRINSIC(UStruct, UField, 0, TEXT("/Script/CoreUObject"), CASTCLASS_UStruct) // Variables. protected: @@ -421,6 +421,11 @@ public: /** Try and find string metadata with the given key. If not found on this class, work up hierarchy looking for it. */ bool GetStringMetaDataHierarchical(const FName& Key, FString* OutValue = nullptr) const; #endif + +#if HACK_HEADER_GENERATOR + // Required by UHT makefiles for internal data serialization. + friend struct FStructArchiveProxy; +#endif // HACK_HEADER_GENERATOR }; enum EStructFlags @@ -987,7 +992,7 @@ public: #define IMPLEMENT_STRUCT(BaseName) \ static UScriptStruct::TAutoCppStructOps BaseName##_Ops(TEXT(#BaseName)); - DECLARE_CASTED_CLASS_INTRINSIC_NO_CTOR(UScriptStruct,UStruct,0,CoreUObject,CASTCLASS_UScriptStruct,COREUOBJECT_API) + DECLARE_CASTED_CLASS_INTRINSIC_NO_CTOR(UScriptStruct, UStruct, 0, TEXT("/Script/CoreUObject"), CASTCLASS_UScriptStruct, COREUOBJECT_API) COREUOBJECT_API UScriptStruct( EStaticConstructor, int32 InSize, EObjectFlags InFlags ); COREUOBJECT_API explicit UScriptStruct(const FObjectInitializer& ObjectInitializer, UScriptStruct* InSuperStruct, ICppStructOps* InCppStructOps = NULL, EStructFlags InStructFlags = STRUCT_NoFlags, SIZE_T ExplicitSize = 0, SIZE_T ExplicitAlignment = 0); @@ -999,6 +1004,9 @@ public: #if HACK_HEADER_GENERATOR int32 StructMacroDeclaredLineNumber; + + // Required by UHT makefiles for internal data serialization. + friend struct FScriptStructArchiveProxy; #endif private: @@ -1238,7 +1246,7 @@ private: // class COREUOBJECT_API UFunction : public UStruct { - DECLARE_CASTED_CLASS_INTRINSIC(UFunction, UStruct, 0, CoreUObject, CASTCLASS_UFunction) + DECLARE_CASTED_CLASS_INTRINSIC(UFunction, UStruct, 0, TEXT("/Script/CoreUObject"), CASTCLASS_UFunction) DECLARE_WITHIN(UClass) public: // Persistent variables. @@ -1386,7 +1394,7 @@ public: class COREUOBJECT_API UDelegateFunction : public UFunction { - DECLARE_CASTED_CLASS_INTRINSIC(UDelegateFunction, UFunction, 0, CoreUObject, CASTCLASS_UDelegateFunction) + DECLARE_CASTED_CLASS_INTRINSIC(UDelegateFunction, UFunction, 0, TEXT("/Script/CoreUObject"), CASTCLASS_UDelegateFunction) DECLARE_WITHIN(UObject) public: explicit UDelegateFunction(const FObjectInitializer& ObjectInitializer, UFunction* InSuperFunction, uint32 InFunctionFlags = 0, uint16 InRepOffset = 0, SIZE_T ParamsSize = 0); @@ -1402,7 +1410,7 @@ public: // class COREUOBJECT_API UEnum : public UField { - DECLARE_CASTED_CLASS_INTRINSIC_WITH_API(UEnum,UField,0,CoreUObject,CASTCLASS_UEnum,NO_API) + DECLARE_CASTED_CLASS_INTRINSIC_WITH_API(UEnum, UField, 0, TEXT("/Script/CoreUObject"), CASTCLASS_UEnum, NO_API) public: enum class ECppForm @@ -1880,7 +1888,7 @@ class COREUOBJECT_API UClass : public UStruct , private FFastIndexingClassTreeRegistrar #endif { - DECLARE_CASTED_CLASS_INTRINSIC_NO_CTOR(UClass,UStruct,0,CoreUObject,CASTCLASS_UClass,NO_API) + DECLARE_CASTED_CLASS_INTRINSIC_NO_CTOR(UClass, UStruct, 0, TEXT("/Script/CoreUObject"), CASTCLASS_UClass, NO_API) DECLARE_WITHIN(UPackage) public: @@ -2440,6 +2448,11 @@ protected: * @return the CDO for this class **/ virtual UObject* CreateDefaultObject(); + +#if HACK_HEADER_GENERATOR + // Required by UHT makefiles for internal data serialization. + friend struct FClassArchiveProxy; +#endif // HACK_HEADER_GENERATOR }; /** @@ -2447,7 +2460,7 @@ protected: */ class COREUOBJECT_API UDynamicClass : public UClass { - DECLARE_CASTED_CLASS_INTRINSIC_NO_CTOR(UDynamicClass, UClass, 0, CoreUObject, CASTCLASS_None, NO_API) + DECLARE_CASTED_CLASS_INTRINSIC_NO_CTOR(UDynamicClass, UClass, 0, TEXT("/Script/CoreUObject"), CASTCLASS_None, NO_API) DECLARE_WITHIN(UPackage) public: @@ -2796,7 +2809,7 @@ T* ConstructObject(UClass* Class, UObject* Outer, FName Name, EObjectFlags SetFl { checkf(Class, TEXT("ConstructObject called with a NULL class object")); checkSlow(Class->IsChildOf(T::StaticClass())); - return (T*)StaticConstructObject_Internal(Class, Outer, Name, SetFlags, Template, bCopyTransientsFromClassDefaults, InstanceGraph); + return (T*)StaticConstructObject_Internal(Class, Outer, Name, SetFlags, EInternalObjectFlags::None, Template, bCopyTransientsFromClassDefaults, InstanceGraph); } /** diff --git a/Engine/Source/Runtime/CoreUObject/Public/UObject/CoreNet.h b/Engine/Source/Runtime/CoreUObject/Public/UObject/CoreNet.h index a4aa1a73ec19..848f1691050b 100644 --- a/Engine/Source/Runtime/CoreUObject/Public/UObject/CoreNet.h +++ b/Engine/Source/Runtime/CoreUObject/Public/UObject/CoreNet.h @@ -120,7 +120,7 @@ private: // class COREUOBJECT_API UPackageMap : public UObject { - DECLARE_CLASS_INTRINSIC( UPackageMap, UObject, CLASS_Transient | CLASS_Abstract | 0, CoreUObject ); + DECLARE_CLASS_INTRINSIC(UPackageMap, UObject, CLASS_Transient | CLASS_Abstract | 0, TEXT("/Script/CoreUObject")); virtual bool WriteObject( FArchive & Ar, UObject* InOuter, FNetworkGUID NetGUID, FString ObjName ) { return false; } diff --git a/Engine/Source/Runtime/CoreUObject/Public/UObject/CoreObject.h b/Engine/Source/Runtime/CoreUObject/Public/UObject/CoreObject.h index 1ab8323069ab..b6f579a12562 100644 --- a/Engine/Source/Runtime/CoreUObject/Public/UObject/CoreObject.h +++ b/Engine/Source/Runtime/CoreUObject/Public/UObject/CoreObject.h @@ -48,7 +48,7 @@ class COREUOBJECT_API UPackage : public UObject // DECLARE_CASTED_CLASS_INTRINSIC(UPackage,UObject,0,CoreUObject, CASTCLASS_UPackage) #if WITH_HOT_RELOAD_CTORS - DECLARE_CASTED_CLASS_INTRINSIC_NO_CTOR_NO_VTABLE_CTOR( UPackage, UObject, 0, CoreUObject, CASTCLASS_UPackage, NO_API ) + DECLARE_CASTED_CLASS_INTRINSIC_NO_CTOR_NO_VTABLE_CTOR( UPackage, UObject, 0, TEXT("/Script/CoreUObject"), CASTCLASS_UPackage, NO_API ) /** DO NOT USE. This constructor is for internal usage only for hot-reload purposes. */ UPackage(FVTableHelper& Helper) : Super(Helper) @@ -56,7 +56,7 @@ class COREUOBJECT_API UPackage : public UObject { }; #else - DECLARE_CASTED_CLASS_INTRINSIC_NO_CTOR(UPackage,UObject,0,CoreUObject, CASTCLASS_UPackage, NO_API) + DECLARE_CASTED_CLASS_INTRINSIC_NO_CTOR(UPackage,UObject,0,TEXT("/Script/CoreUObject"), CASTCLASS_UPackage, NO_API) #endif @@ -582,7 +582,7 @@ PRAGMA_ENABLE_DEPRECATION_WARNINGS */ class COREUOBJECT_API UMetaData : public UObject { - DECLARE_CLASS_INTRINSIC(UMetaData, UObject, 0, CoreUObject) + DECLARE_CLASS_INTRINSIC(UMetaData, UObject, 0, TEXT("/Script/CoreUObject")) public: // Variables. @@ -690,6 +690,9 @@ public: #if HACK_HEADER_GENERATOR // Returns the remapped key name, or NAME_None was not remapped. static FName GetRemappedKeyName(FName OldKey); + + // Required by UHT makefiles for internal data serialization. + friend struct FMetadataArchiveProxy; #endif private: diff --git a/Engine/Source/Runtime/CoreUObject/Public/UObject/GCObject.h b/Engine/Source/Runtime/CoreUObject/Public/UObject/GCObject.h index 438793f941bd..752b15bf02a7 100644 --- a/Engine/Source/Runtime/CoreUObject/Public/UObject/GCObject.h +++ b/Engine/Source/Runtime/CoreUObject/Public/UObject/GCObject.h @@ -26,7 +26,7 @@ class UGCObjectReferencer : public UObject TArray ReferencedObjects; public: - DECLARE_CASTED_CLASS_INTRINSIC_WITH_API(UGCObjectReferencer,UObject,CLASS_Transient,CoreUObject,CASTCLASS_None,COREUOBJECT_API); + DECLARE_CASTED_CLASS_INTRINSIC_WITH_API(UGCObjectReferencer, UObject, CLASS_Transient, TEXT("/Script/CoreUObject"), CASTCLASS_None, COREUOBJECT_API); /** * Adds an object to the referencer list diff --git a/Engine/Source/Runtime/CoreUObject/Public/UObject/GarbageCollection.h b/Engine/Source/Runtime/CoreUObject/Public/UObject/GarbageCollection.h index e4b9ada43726..96d4b9612253 100644 --- a/Engine/Source/Runtime/CoreUObject/Public/UObject/GarbageCollection.h +++ b/Engine/Source/Runtime/CoreUObject/Public/UObject/GarbageCollection.h @@ -6,7 +6,7 @@ =============================================================================*/ /** Context sensitive keep flags for garbage collection */ -#define GARBAGE_COLLECTION_KEEPFLAGS (GIsEditor ? RF_Native|RF_AsyncLoading|RF_Standalone|RF_Async : RF_Native|RF_AsyncLoading|RF_Async) +#define GARBAGE_COLLECTION_KEEPFLAGS (GIsEditor ? RF_Standalone : RF_NoFlags) /*----------------------------------------------------------------------------- Realtime garbage collection helper classes. diff --git a/Engine/Source/Runtime/CoreUObject/Public/UObject/ObjectBase.h b/Engine/Source/Runtime/CoreUObject/Public/UObject/ObjectBase.h index 56e6b45934a9..221185276263 100644 --- a/Engine/Source/Runtime/CoreUObject/Public/UObject/ObjectBase.h +++ b/Engine/Source/Runtime/CoreUObject/Public/UObject/ObjectBase.h @@ -169,7 +169,7 @@ enum EClassFlags //CLASS_ = 0x00000020, /** All the properties on the class are shown in the advanced section (which is hidden by default) unless SimpleDisplay is specified on the property */ CLASS_AdvancedDisplay = 0x00000040, - /** Class is a native class - native interfaces will have CLASS_Native set, but not RF_Native */ + /** Class is a native class - native interfaces will have CLASS_Native set, but not RF_MarkAsNative */ CLASS_Native = 0x00000080, /** Don't export to C++ header. */ CLASS_NoExport = 0x00000100, @@ -439,23 +439,23 @@ enum EObjectFlags // The garbage collector also tends to look at these. RF_Public =0x00000001, ///< Object is visible outside its package. RF_Standalone =0x00000002, ///< Keep object around for editing even if unreferenced. - RF_Native =0x00000004, ///< Native (UClass only). + RF_MarkAsNative =0x00000004, ///< Object (UField) will be marked as native on construction (DO NOT USE THIS FLAG in HasAnyFlags() etc) RF_Transactional =0x00000008, ///< Object is transactional. RF_ClassDefaultObject =0x00000010, ///< This object is its class's default object RF_ArchetypeObject =0x00000020, ///< This object is a template for another object - treat like a class default object RF_Transient =0x00000040, ///< Don't save object. // This group of flags is primarily concerned with garbage collection. - RF_RootSet =0x00000080, ///< Object will not be garbage collected, even if unreferenced. - RF_Unreachable =0x00000100, ///< Object is not reachable on the object graph. + RF_MarkAsRootSet =0x00000080, ///< Object will be marked as root set on construction and not be garbage collected, even if unreferenced (DO NOT USE THIS FLAG in HasAnyFlags() etc) + //RF_Unused =0x00000100, /// RF_TagGarbageTemp =0x00000200, ///< This is a temp user flag for various utilities that need to use the garbage collector. The garbage collector itself does not interpret it. // The group of flags tracks the stages of the lifetime of a uobject RF_NeedLoad =0x00000400, ///< During load, indicates object needs loading. - RF_AsyncLoading =0x00000800, ///< Object is being asynchronously loaded. + //RF_Unused =0x00000800, /// RF_NeedPostLoad =0x00001000, ///< Object needs to be postloaded. RF_NeedPostLoadSubobjects =0x00002000, ///< During load, indicates that the object still needs to instance subobjects and fixup serialized component references - RF_PendingKill =0x00004000, ///< Objects that are pending destruction (invalid for gameplay but valid objects) + //RF_Unused =0x00004000, /// RF_BeginDestroyed =0x00008000, ///< BeginDestroy has been called on the object. RF_FinishDestroyed =0x00010000, ///< FinishDestroy has been called on the object. @@ -466,9 +466,9 @@ enum EObjectFlags RF_TextExportTransient =0x00100000, ///< Do not export object to text form (e.g. copy/paste). Generally used for sub-objects that can be regenerated from data in their parent object. RF_LoadCompleted =0x00200000, ///< Object has been completely serialized by linkerload at least once. DO NOT USE THIS FLAG, It should be replaced with RF_WasLoaded. RF_InheritableComponentTemplate = 0x00400000, ///< Archetype of the object can be in its super class - RF_Async = 0x00800000, ///< Object exists only on a different thread than the game thread. + //RF_Unused = 0x00800000, /// RF_StrongRefOnFrame = 0x01000000, ///< References to this object from persistent function frame are handled as strong ones. - RF_NoStrongReference = 0x02000000, ///< The object is not referenced by any strong reference. The flag is used by GC. + //RF_Unused = 0x02000000, /// RF_Dynamic = 0x04000000, // Field Only. Dynamic field - doesn't get constructed during static initialization, can be constructed multiple times }; @@ -476,7 +476,7 @@ enum EObjectFlags #define RF_AllFlags (EObjectFlags)0x03ffffff ///< All flags, used mainly for error checking // Predefined groups of the above -#define RF_Load ((EObjectFlags)(RF_Public | RF_Standalone | RF_Native | RF_Transactional | RF_ClassDefaultObject | RF_ArchetypeObject | RF_DefaultSubObject | RF_TextExportTransient | RF_InheritableComponentTemplate)) // Flags to load from Unrealfiles. +#define RF_Load ((EObjectFlags)(RF_Public | RF_Standalone | RF_Transactional | RF_ClassDefaultObject | RF_ArchetypeObject | RF_DefaultSubObject | RF_TextExportTransient | RF_InheritableComponentTemplate)) // Flags to load from Unrealfiles. #define RF_PropagateToSubObjects ((EObjectFlags)(RF_Public | RF_ArchetypeObject | RF_Transactional | RF_Transient)) // Sub-objects will inherit these flags from their SuperObject. FORCEINLINE EObjectFlags operator|(EObjectFlags Arg1,EObjectFlags Arg2) @@ -503,9 +503,26 @@ FORCEINLINE void operator|=(EObjectFlags& Dest,EObjectFlags Arg) Dest = EObjectFlags(Dest | Arg); } - //@} +/** Objects flags for internal use (GC, low level UObject code) */ +enum class EInternalObjectFlags : int32 +{ + None = 0, + // All the other bits are reserved, DO NOT ADD NEW FLAGS HERE! + Native = 1 << 25, ///< Native (UClass only). + Async = 1 << 26, ///< Object exists only on a different thread than the game thread. + AsyncLoading = 1 << 27, ///< Object is being asynchronously loaded. + Unreachable = 1 << 28, ///< Object is not reachable on the object graph. + PendingKill = 1 << 29, ///< Objects that are pending destruction (invalid for gameplay but valid objects) + RootSet = 1 << 30, ///< Object will not be garbage collected, even if unreferenced. + NoStrongReference = 1 << 31, ///< The object is not referenced by any strong reference. The flag is used by GC. + + GarbageCollectionKeepFlags = Native | Async | AsyncLoading, + // Make sure this is up to date! + AllFlags = Native | Async | AsyncLoading | Unreachable | PendingKill | RootSet | NoStrongReference +}; +ENUM_CLASS_FLAGS(EInternalObjectFlags); /*---------------------------------------------------------------------------- Core types. @@ -1264,7 +1281,7 @@ public: \ /** Returns a UClass object representing this class at runtime */ \ inline static UClass* StaticClass() \ { \ - return GetPrivateStaticClass(TEXT("/Script/") TEXT(#TPackage)); \ + return GetPrivateStaticClass(TPackage); \ } \ /** Returns the StaticClassFlags for this class */ \ inline static EClassCastFlags StaticClassCastFlags() \ @@ -1499,7 +1516,7 @@ public: \ #define IMPLEMENT_DYNAMIC_CLASS(TClass, ClassName, TClassCrc) \ UClass* TClass::GetPrivateStaticClass(const TCHAR* Package) \ { \ - UPackage* PrivateStaticClassOuter = CastChecked(StaticFindObjectFast(UPackage::StaticClass(), nullptr, Package)); \ + UPackage* PrivateStaticClassOuter = FindOrConstructDynamicTypePackage(Package); \ UClass* PrivateStaticClass = Cast(StaticFindObjectFast(UClass::StaticClass(), PrivateStaticClassOuter, (TCHAR*)ClassName)); \ if (!PrivateStaticClass) \ { \ @@ -1602,12 +1619,12 @@ public: #include "UObjectAllocator.h" -#include "UObjectHash.h" #include "UObjectGlobals.h" #include "UObjectMarks.h" #include "UObjectBase.h" #include "UObjectBaseUtility.h" #include "UObjectArray.h" +#include "UObjectHash.h" #include "WeakObjectPtr.h" #include "UObject.h" #include "UObjectIterator.h" diff --git a/Engine/Source/Runtime/CoreUObject/Public/UObject/ObjectRedirector.h b/Engine/Source/Runtime/CoreUObject/Public/UObject/ObjectRedirector.h index e982f29ffc7d..445fab562976 100644 --- a/Engine/Source/Runtime/CoreUObject/Public/UObject/ObjectRedirector.h +++ b/Engine/Source/Runtime/CoreUObject/Public/UObject/ObjectRedirector.h @@ -14,7 +14,7 @@ */ class UObjectRedirector : public UObject { - DECLARE_CASTED_CLASS_INTRINSIC_WITH_API(UObjectRedirector, UObject, 0, CoreUObject, CASTCLASS_None, COREUOBJECT_API) + DECLARE_CASTED_CLASS_INTRINSIC_WITH_API(UObjectRedirector, UObject, 0, TEXT("/Script/CoreUObject"), CASTCLASS_None, COREUOBJECT_API) // Variables. UObject* DestinationObject; diff --git a/Engine/Source/Runtime/CoreUObject/Public/UObject/UObject.h b/Engine/Source/Runtime/CoreUObject/Public/UObject/UObject.h index 1796fbabd6e6..e67d03df8322 100644 --- a/Engine/Source/Runtime/CoreUObject/Public/UObject/UObject.h +++ b/Engine/Source/Runtime/CoreUObject/Public/UObject/UObject.h @@ -33,7 +33,7 @@ namespace EResourceSizeMode class COREUOBJECT_API UObject : public UObjectBaseUtility { // Declarations. - DECLARE_CLASS(UObject,UObject,CLASS_Abstract|CLASS_NoExport,CASTCLASS_None,CoreUObject,NO_API) + DECLARE_CLASS(UObject,UObject,CLASS_Abstract|CLASS_NoExport,CASTCLASS_None,TEXT("/Script/CoreUObject"),NO_API) DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL(UObject) #if WITH_HOT_RELOAD_CTORS static UObject* __VTableCtorCaller(FVTableHelper& Helper) diff --git a/Engine/Source/Runtime/CoreUObject/Public/UObject/UObjectArray.h b/Engine/Source/Runtime/CoreUObject/Public/UObject/UObjectArray.h index a61bcc9f73e0..489052972fb5 100644 --- a/Engine/Source/Runtime/CoreUObject/Public/UObject/UObjectArray.h +++ b/Engine/Source/Runtime/CoreUObject/Public/UObject/UObjectArray.h @@ -7,11 +7,236 @@ #ifndef __UOBJECTARRAY_H__ #define __UOBJECTARRAY_H__ +/** +* Controls whether the number of available elements is being tracked in the ObjObjects array. +* By default it is only tracked in WITH_EDITOR builds as it adds a small amount of tracking overhead +*/ +#define UE_GC_TRACK_OBJ_AVAILABLE (WITH_EDITOR) + /** Used to test for stale weak pointers in the debug visualizers **/ extern COREUOBJECT_API int32** GSerialNumberBlocksForDebugVisualizersRoot; +/** +* Single item in the UObject array. +*/ +struct FUObjectItem +{ + // Pointer to the allocated object + class UObjectBase* Object; + // UObject internal flags + int32 Flags; + // Weak Object Pointer Serial number associated with the object + int32 SerialNumber; + + FORCEINLINE int32 GetSerialNumber() const + { + return SerialNumber; + } + + FORCEINLINE void SetFlags(EInternalObjectFlags FlagsToSet) + { + Flags |= int32(FlagsToSet); + } + + FORCEINLINE EInternalObjectFlags GetFlags() const + { + return EInternalObjectFlags(Flags & int32(EInternalObjectFlags::AllFlags)); + } + + FORCEINLINE void ClearFlags(EInternalObjectFlags FlagsToClear) + { + Flags &= ~int32(FlagsToClear); + } + + FORCEINLINE bool ThisThreadAtomicallyClearedFlag(EInternalObjectFlags FlagToClear) + { + static_assert(sizeof(int32) == sizeof(Flags), "Flags must be 32-bit for atomics."); + bool bIChangedIt = false; + while (1) + { + int32 StartValue = int32(Flags); + if (!(StartValue & int32(FlagToClear))) + { + break; + } + int32 OldValue = (int32)FPlatformAtomics::InterlockedCompareExchange((int32*)&Flags, StartValue & ~int32(FlagToClear), StartValue); + if (OldValue == StartValue) + { + bIChangedIt = true; + break; + } + // Remove later. + checkSlow(OldValue == (StartValue & ~int32(FlagToClear))); + } + return bIChangedIt; + } + + FORCEINLINE bool HasAnyFlags(EInternalObjectFlags InFlags) const + { + return !!(Flags & int32(InFlags)); + } + + FORCEINLINE void SetUnreachable() + { + Flags |= int32(EInternalObjectFlags::Unreachable); + } + FORCEINLINE void ClearUnreachable() + { + Flags &= ~int32(EInternalObjectFlags::Unreachable); + } + FORCEINLINE bool IsUnreachable() const + { + return !!(Flags & int32(EInternalObjectFlags::Unreachable)); + } + FORCEINLINE bool ThisThreadAtomicallyClearedRFUnreachable() + { + return ThisThreadAtomicallyClearedFlag(EInternalObjectFlags::Unreachable); + } + + FORCEINLINE void SetPendingKill() + { + Flags |= int32(EInternalObjectFlags::PendingKill); + } + FORCEINLINE void ClearPendingKill() + { + Flags &= ~int32(EInternalObjectFlags::PendingKill); + } + FORCEINLINE bool IsPendingKill() const + { + return !!(Flags & int32(EInternalObjectFlags::PendingKill)); + } + + FORCEINLINE void SetRootSet() + { + Flags |= int32(EInternalObjectFlags::RootSet); + } + FORCEINLINE void ClearRootSet() + { + Flags &= ~int32(EInternalObjectFlags::RootSet); + } + FORCEINLINE bool IsRootSet() const + { + return !!(Flags & int32(EInternalObjectFlags::RootSet)); + } + + FORCEINLINE void SetNoStrongReference() + { + Flags |= int32(EInternalObjectFlags::NoStrongReference); + } + FORCEINLINE void ClearNoStrongReference() + { + Flags &= ~int32(EInternalObjectFlags::NoStrongReference); + } + FORCEINLINE bool IsNoStrongReference() const + { + return !!(Flags & int32(EInternalObjectFlags::NoStrongReference)); + } + FORCEINLINE void ResetSerialNumberAndFlags() + { + Flags = 0; + SerialNumber = 0; + } +}; + +/** +* Fixed size UObject array. +*/ +class FFixedUObjectArray +{ + /** Static master table to chunks of pointers **/ + FUObjectItem* Objects; + /** Number of elements we currently have **/ + int32 MaxElements; + /** Current number of UObject slots */ + int32 NumElements; + +public: + + FFixedUObjectArray() + : Objects(nullptr) + , MaxElements(0) + , NumElements(0) + { + } + + ~FFixedUObjectArray() + { + FMemory::Free(Objects); + } + + /** + * Expands the array so that Element[Index] is allocated. New pointers are all zero. + * @param Index The Index of an element we want to be sure is allocated + **/ + void PreAllocate(int32 InMaxElements) + { + check(!Objects); + Objects = (FUObjectItem*)FMemory::Malloc(sizeof(FUObjectItem)* InMaxElements); + FMemory::Memzero(Objects, sizeof(FUObjectItem)* InMaxElements); + MaxElements = InMaxElements; + } + + int32 AddSingle() + { + int32 Result = NumElements; + checkf(NumElements + 1 <= MaxElements, TEXT("Maximum number of Objects exceeded, make sure you update MaxObjectsInGame/MaxObjectsInEditor in project settings.")); + check(Result == NumElements); + ++NumElements; + FPlatformMisc::MemoryBarrier(); + check(Objects[Result].Object == nullptr); + return Result; + } + + FORCEINLINE FUObjectItem const* GetObjectPtr(int32 Index) const + { + check(Index >= 0 && Index < NumElements); + return &Objects[Index]; + } + + /** + * Return the number of elements in the array + * Thread safe, but you know, someone might have added more elements before this even returns + * @return the number of elements in the array + **/ + FORCEINLINE int32 Num() const + { + return NumElements; + } + /** + * Return if this index is valid + * Thread safe, if it is valid now, it is valid forever. Other threads might be adding during this call. + * @param Index Index to test + * @return true, if this is a valid + **/ + FORCEINLINE bool IsValidIndex(int32 Index) const + { + return Index < Num() && Index >= 0; + } + /** + * Return a reference to an element + * @param Index Index to return + * @return a reference to the pointer to the element + * Thread safe, if it is valid now, it is valid forever. This might return nullptr, but by then, some other thread might have made it non-nullptr. + **/ + FORCEINLINE FUObjectItem const& operator[](int32 Index) const + { + FUObjectItem const* ItemPtr = GetObjectPtr(Index); + check(ItemPtr); + return *ItemPtr; + } + + /** + * Return a naked pointer to the fundamental data structure for debug visualizers. + **/ + UObjectBase*** GetRootBlockForDebuggerVisualizers() + { + return nullptr; + } +}; + + /*** -* +* * FUObjectArray replaces the functionality of GObjObjects and UObject::Index * * Note the layout of this data structure is mostly to emulate the old behavior and minimize code rework during code restructure. @@ -20,12 +245,15 @@ extern COREUOBJECT_API int32** GSerialNumberBlocksForDebugVisualizersRoot; * that non-GC objects come before GC ones during iteration. * **/ - - class COREUOBJECT_API FUObjectArray { public: + enum ESerialNumberConstants + { + START_SERIAL_NUMBER = 1000, + }; + /** * Base class for UObjectBase create class listeners */ @@ -65,7 +293,8 @@ public: FUObjectArray() : ObjFirstGCIndex(0), ObjLastNonGCIndex(INDEX_NONE), - OpenForDisregardForGC(true) + OpenForDisregardForGC(true), + MasterSerialNumber(START_SERIAL_NUMBER) { FCoreDelegates::GetObjectArrayForDebugVisualizersDelegate().BindStatic(GetObjectArrayForDebugVisualizers); } @@ -73,16 +302,10 @@ public: /** * Allocates and initializes the permanent object pool * + * @param MaxUObjects maximum number of UObjects that can ever exist in the array * @param MaxObjectsNotConsideredByGC number of objects in the permanent object pool */ - void AllocatePermanentObjectPool(int32 MaxObjectsNotConsideredByGC); - - /** - * Reserves array memory to hold the specified number of objects - * - * @param MaxObjectsNotConsideredByGC number of objects in the permanent object pool - */ - void ReserveUObjectPool(int32 InNumObjects); + void AllocateObjectPool(int32 MaxUObjects, int32 MaxObjectsNotConsideredByGC); /** * Disables the disregard for GC optimization. Commandlets can't use it. @@ -140,46 +363,75 @@ public: * @param Index index of object to return * @return Object at this index */ - FORCEINLINE class UObjectBase* IndexToObject(int32 Index) + FORCEINLINE FUObjectItem* IndexToObject(int32 Index) { check(Index >= 0); if (Index < ObjObjects.Num()) { - return (UObjectBase*)ObjObjects[Index]; + return const_cast(&ObjObjects[Index]); } - return NULL; + return nullptr; } - FORCEINLINE class UObjectBase* IndexToObject(int32 Index, bool bEvenIfPendingKill) + FORCEINLINE FUObjectItem* IndexToObjectUnsafeForGC(int32 Index) { - UObjectBaseUtility* Object = static_cast(IndexToObject(Index)); - if (Object && !bEvenIfPendingKill && Object->IsPendingKill()) + return const_cast(&ObjObjects[Index]); + } + + FORCEINLINE FUObjectItem* IndexToObject(int32 Index, bool bEvenIfPendingKill) + { + FUObjectItem* ObjectItem = IndexToObject(Index); + if (ObjectItem && ObjectItem->Object) { - Object = NULL; + if (!bEvenIfPendingKill && ObjectItem->IsPendingKill()) + { + ObjectItem = nullptr;; + } } - return Object; + return ObjectItem; } - FORCEINLINE class UObjectBase* IndexToValidObject(int32 Index, bool bEvenIfPendingKill) + FORCEINLINE FUObjectItem* ObjectToObjectItem(UObjectBase* Object) { - UObjectBaseUtility* Object = static_cast(IndexToObject(Index)); - return Internal_IsValid(Object, bEvenIfPendingKill) ? Object : nullptr; + FUObjectItem* ObjectItem = IndexToObject(Object->InternalIndex); + return ObjectItem; + } + + FORCEINLINE bool IsValid(FUObjectItem* ObjectItem, bool bEvenIfPendingKill) + { + if (ObjectItem) + { + return bEvenIfPendingKill ? !ObjectItem->IsUnreachable() : !(ObjectItem->IsUnreachable() || ObjectItem->IsPendingKill()); + } + return false; + } + + FORCEINLINE FUObjectItem* IndexToValidObject(int32 Index, bool bEvenIfPendingKill) + { + FUObjectItem* ObjectItem = IndexToObject(Index); + return IsValid(ObjectItem, bEvenIfPendingKill) ? ObjectItem : nullptr; } FORCEINLINE bool IsValid(int32 Index, bool bEvenIfPendingKill) { // This method assumes Index points to a valid object. - UObjectBaseUtility* Object = static_cast(IndexToObject(Index)); - return Internal_IsValid(Object, bEvenIfPendingKill); + FUObjectItem* ObjectItem = IndexToObject(Index); + return IsValid(ObjectItem, bEvenIfPendingKill); + } + + FORCEINLINE bool IsStale(FUObjectItem* ObjectItem, bool bEvenIfPendingKill) + { + // This method assumes ObjectItem is valid. + return bEvenIfPendingKill ? (ObjectItem->IsPendingKill() || ObjectItem->IsUnreachable()) : (ObjectItem->IsUnreachable()); } FORCEINLINE bool IsStale(int32 Index, bool bEvenIfPendingKill) { // This method assumes Index points to a valid object. - UObjectBaseUtility* Object = static_cast(IndexToObject(Index)); - if (Object) + FUObjectItem* ObjectItem = IndexToObject(Index); + if (ObjectItem) { - return bEvenIfPendingKill ? Object->HasAnyFlags(RF_Unreachable | RF_PendingKill) : Object->HasAnyFlags(RF_Unreachable); + return IsStale(ObjectItem, bEvenIfPendingKill); } return true; } @@ -284,6 +536,24 @@ public: */ void ShutdownUObjectArray(); + /** + * Given a UObject index return the serial number. If it doesn't have a serial number, give it one. Threadsafe. + * @param Index - UObject Index + * @return - the serial number for this UObject + */ + int32 AllocateSerialNumber(int32 Index); + + /** + * Given a UObject index return the serial number. If it doesn't have a serial number, return 0. Threadsafe. + * @param Index - UObject Index + * @return - the serial number for this UObject + */ + FORCEINLINE int32 GetSerialNumber(int32 Index) + { + FUObjectItem* ObjectItem = IndexToObject(Index); + checkSlow(ObjectItem); + return ObjectItem->GetSerialNumber(); + } /** * Low level iterator. @@ -348,6 +618,11 @@ public: return !(bool)*this; } + FORCEINLINE int32 GetIndex() const + { + return Index; + } + protected: /** @@ -355,7 +630,7 @@ public: * * @return the UObject at the iterator */ - FORCEINLINE UObjectBase* GetObject() const + FORCEINLINE FUObjectItem* GetObject() const { return CurrentObject; } @@ -366,12 +641,14 @@ public: FORCEINLINE bool Advance() { //@todo UE4 check this for LHS on Index on consoles + FUObjectItem* NextObject = nullptr; CurrentObject = nullptr; while(++Index < Array.GetObjectArrayNum()) { - CurrentObject = (UObjectBase*)Array.ObjObjects[Index]; - if (CurrentObject) + NextObject = const_cast(&Array.ObjObjects[Index]); + if (NextObject->Object) { + CurrentObject = NextObject; return true; } } @@ -383,21 +660,13 @@ public: /** index of the current element in the object array */ int32 Index; /** Current object */ - mutable UObjectBase* CurrentObject; + mutable FUObjectItem* CurrentObject; }; private: - FORCEINLINE bool Internal_IsValid(UObjectBaseUtility* Object, bool bEvenIfPendingKill) - { - if (Object) - { - return bEvenIfPendingKill ? !Object->HasAnyFlags(RF_Unreachable) : !Object->HasAnyFlags(RF_Unreachable | RF_PendingKill); - } - return false; - } - - typedef TStaticIndirectArrayThreadSafeRead TUObjectArray; + //typedef TStaticIndirectArrayThreadSafeRead TUObjectArray; + typedef FFixedUObjectArray TUObjectArray; /** * return the object array for use by debug visualizers @@ -433,6 +702,9 @@ private: #if THREADSAFE_UOBJECTS FCriticalSection UObjectDeleteListenersCritical; #endif + + /** Current master serial number **/ + FThreadSafeCounter MasterSerialNumber; }; /** Global UObject allocator */ @@ -445,7 +717,8 @@ struct FIndexToObject { static FORCEINLINE class UObjectBase* IndexToObject(int32 Index, bool bEvenIfPendingKill) { - return GUObjectArray.IndexToObject(Index, bEvenIfPendingKill); + FUObjectItem* ObjectItem = GUObjectArray.IndexToObject(Index, bEvenIfPendingKill); + return ObjectItem ? ObjectItem->Object : nullptr; } }; diff --git a/Engine/Source/Runtime/CoreUObject/Public/UObject/UObjectBase.h b/Engine/Source/Runtime/CoreUObject/Public/UObject/UObjectBase.h index 711df4bec71b..98c2c0570ee9 100644 --- a/Engine/Source/Runtime/CoreUObject/Public/UObject/UObjectBase.h +++ b/Engine/Source/Runtime/CoreUObject/Public/UObject/UObjectBase.h @@ -11,6 +11,7 @@ DECLARE_DWORD_COUNTER_STAT_EXTERN(TEXT("STAT_UObjectsStatGroupTester"), STAT_UOb class COREUOBJECT_API UObjectBase { + friend class UObjectBaseUtility; friend COREUOBJECT_API class UClass* Z_Construct_UClass_UObject(); friend class FUObjectArray; // for access to InternalIndex without revealing it to anyone else friend class FUObjectAllocator; // for access to destructor without revealing it to anyone else @@ -37,10 +38,11 @@ public: * Constructor used by StaticAllocateObject * @param InClass non NULL, this gives the class of the new object, if known at this time * @param InFlags RF_Flags to assign + * @param InInternalFlags EInternalObjectFlags to assign * @param InOuter outer for this object * @param InName name of the new object */ - UObjectBase( UClass* InClass, EObjectFlags InFlags, UObject *InOuter, FName InName ); + UObjectBase( UClass* InClass, EObjectFlags InFlags, EInternalObjectFlags InInternalFlags, UObject *InOuter, FName InName ); /** * Final destructor, removes the object from the object array, and indirectly, from any annotations @@ -79,8 +81,9 @@ private: * Add a newly created object to the name hash tables and the object array * * @param Name name to assign to this uobject + * @param InSetInternalFlags Internal object flags to be set on the object once it's been added to the array */ - void AddObject(FName Name); + void AddObject(FName Name, EInternalObjectFlags InSetInternalFlags); public: /** * Checks to see if the object appears to be valid @@ -200,34 +203,6 @@ public: while( FPlatformAtomics::InterlockedCompareExchange( (int32*)&ObjectFlags, NewFlags, OldFlags) != OldFlags ); } - /** - * Atomically clear the unreachable flag - * - * @return true if we are the thread that cleared RF_Unreachable - **/ - FORCEINLINE bool ThisThreadAtomicallyClearedRFUnreachable() - { - static_assert(sizeof(int32) == sizeof(EObjectFlags), "Flags must be 32-bit for atomics."); - bool bIChangedIt = false; - while (1) - { - int32 StartValue = int32(ObjectFlags); - if (!(StartValue & int32(RF_Unreachable))) - { - break; - } - EObjectFlags OldValue = (EObjectFlags)FPlatformAtomics::InterlockedCompareExchange((int32*)&ObjectFlags, StartValue & ~int32(RF_Unreachable),StartValue); - if (OldValue == StartValue) - { - bIChangedIt = true; - break; - } - // Remove later. - checkSlow(OldValue == (StartValue & ~int32(RF_Unreachable))); - } - return bIChangedIt; - } - private: /** Flags used to track and report various object states. This needs to be 8 byte aligned on 32-bit @@ -253,6 +228,11 @@ private: // This is used by the reinstancer to re-class and re-archetype the current instances of a class before recompiling friend class FBlueprintCompileReinstancer; void SetClass(UClass* NewClass); + +#if HACK_HEADER_GENERATOR + // Required by UHT makefiles for internal data serialization. + friend struct FObjectBaseArchiveProxy; +#endif // HACK_HEADER_GENERATOR }; namespace Internal diff --git a/Engine/Source/Runtime/CoreUObject/Public/UObject/UObjectBaseUtility.h b/Engine/Source/Runtime/CoreUObject/Public/UObject/UObjectBaseUtility.h index 075e2b88e959..6977726a2b46 100644 --- a/Engine/Source/Runtime/CoreUObject/Public/UObject/UObjectBaseUtility.h +++ b/Engine/Source/Runtime/CoreUObject/Public/UObject/UObjectBaseUtility.h @@ -6,6 +6,8 @@ #pragma once +#include "UObjectArray.h" + #if _MSC_VER == 1900 #ifdef PRAGMA_DISABLE_SHADOW_VARIABLE_WARNINGS PRAGMA_DISABLE_SHADOW_VARIABLE_WARNINGS @@ -30,10 +32,12 @@ public: FORCEINLINE void SetFlags( EObjectFlags NewFlags ) { + checkSlow(!(NewFlags & (RF_MarkAsNative | RF_MarkAsRootSet))); // These flags can't be used outside of constructors / internal code SetFlagsTo(GetFlags() | NewFlags); } FORCEINLINE void ClearFlags( EObjectFlags NewFlags ) { + checkSlow(!(NewFlags & (RF_MarkAsNative | RF_MarkAsRootSet)) || NewFlags == RF_AllFlags); // These flags can't be used outside of constructors / internal code SetFlagsTo(GetFlags() & ~NewFlags); } /** @@ -44,6 +48,7 @@ public: */ FORCEINLINE bool HasAnyFlags( EObjectFlags FlagsToCheck ) const { + checkSlow(!(FlagsToCheck & (RF_MarkAsNative | RF_MarkAsRootSet)) || FlagsToCheck == RF_AllFlags); // These flags can't be used outside of constructors / internal code return (GetFlags() & FlagsToCheck) != 0; } /** @@ -54,6 +59,7 @@ public: */ FORCEINLINE bool HasAllFlags( EObjectFlags FlagsToCheck ) const { + checkSlow(!(FlagsToCheck & (RF_MarkAsNative | RF_MarkAsRootSet)) || FlagsToCheck == RF_AllFlags); // These flags can't be used outside of constructors / internal code return ((GetFlags() & FlagsToCheck) == FlagsToCheck); } /** @@ -67,50 +73,6 @@ public: return EObjectFlags(GetFlags() & Mask); } - /** - * Checks the RF_PendingKill flag to see if it is dead but memory still valid - */ - FORCEINLINE bool IsPendingKill() const - { - return HasAnyFlags(RF_PendingKill); - } - - /** - * Marks this object as RF_PendingKill. - */ - FORCEINLINE void MarkPendingKill() - { - check(!IsRooted()); - SetFlags( RF_PendingKill ); - } - - // - // Add an object to the root set. This prevents the object and all - // its descendants from being deleted during garbage collection. - // - FORCEINLINE void AddToRoot() - { - SetFlags( RF_RootSet ); - } - - // - // Remove an object from the root set. - // - FORCEINLINE void RemoveFromRoot() - { - ClearFlags( RF_RootSet ); - } - - /** - * Returns true if this object is explicitly rooted - * - * @return true if the object was explicitly added as part of the root set. - */ - FORCEINLINE bool IsRooted() - { - return HasAnyFlags( RF_RootSet ); - } - /***********************/ /******** Marks ******* UObjectMarks.cpp */ /***********************/ @@ -157,6 +119,147 @@ public: return ObjectHasAllMarks(this,Marks); } + /** + * Checks the PendingKill flag to see if it is dead but memory still valid + */ + FORCEINLINE bool IsPendingKill() const + { + return GUObjectArray.IndexToObject(InternalIndex)->IsPendingKill(); + } + + /** + * Marks this object as RF_PendingKill. + */ + FORCEINLINE void MarkPendingKill() + { + check(!IsRooted()); + GUObjectArray.IndexToObject(InternalIndex)->SetPendingKill(); + } + + /** + * Unmarks this object as PendingKill. + */ + FORCEINLINE void ClearPendingKill() + { + GUObjectArray.IndexToObject(InternalIndex)->ClearPendingKill(); + } + + // + // Add an object to the root set. This prevents the object and all + // its descendants from being deleted during garbage collection. + // + FORCEINLINE void AddToRoot() + { + GUObjectArray.IndexToObject(InternalIndex)->SetRootSet(); + } + + // + // Remove an object from the root set. + // + FORCEINLINE void RemoveFromRoot() + { + GUObjectArray.IndexToObject(InternalIndex)->ClearRootSet(); + } + + /** + * Returns true if this object is explicitly rooted + * + * @return true if the object was explicitly added as part of the root set. + */ + FORCEINLINE bool IsRooted() + { + return GUObjectArray.IndexToObject(InternalIndex)->IsRootSet(); + } + + /** + * Atomically clear the unreachable flag + * + * @return true if we are the thread that cleared RF_Unreachable + **/ + FORCEINLINE bool ThisThreadAtomicallyClearedRFUnreachable() + { + return GUObjectArray.IndexToObject(InternalIndex)->ThisThreadAtomicallyClearedRFUnreachable(); + } + + /** + * Checks if the object is unreachable. + **/ + FORCEINLINE bool IsUnreachable() const + { + return GUObjectArray.IndexToObject(InternalIndex)->IsUnreachable(); + } + + /** + * Checks if the object is pending kill or unreachable. + **/ + FORCEINLINE bool IsPendingKillOrUnreachable() const + { + return GUObjectArray.IndexToObject(InternalIndex)->HasAnyFlags(EInternalObjectFlags::PendingKill | EInternalObjectFlags::Unreachable); + } + + /** + * Checks if the object is native. + **/ + FORCEINLINE bool IsNative() const + { + return GUObjectArray.IndexToObject(InternalIndex)->HasAnyFlags(EInternalObjectFlags::Native); + } + + /** + * Clears passed in internal flags . + * + * @param FlagsToClear Object flags to clear. + * @return true if any of the passed in flags are set, false otherwise (including no flags passed in). + */ + FORCEINLINE void SetInternalFlags(EInternalObjectFlags FlagsToSet) const + { + GUObjectArray.IndexToObject(InternalIndex)->SetFlags(FlagsToSet); + } + + /** + * Gets internal flags. + * + * @param FlagsToClear Object flags to clear. + * @return true if any of the passed in flags are set, false otherwise (including no flags passed in). + */ + FORCEINLINE EInternalObjectFlags GetInternalFlags() const + { + return GUObjectArray.IndexToObject(InternalIndex)->GetFlags(); + } + + /** + * Used to safely check whether any of the passed in internal flags are set. + * + * @param FlagsToCheck Object flags to check for. + * @return true if any of the passed in flags are set, false otherwise (including no flags passed in). + */ + FORCEINLINE bool HasAnyInternalFlags(EInternalObjectFlags FlagsToCheck) const + { + return GUObjectArray.IndexToObject(InternalIndex)->HasAnyFlags(FlagsToCheck); + } + + /** + * Clears passed in internal flags . + * + * @param FlagsToClear Object flags to clear. + * @return true if any of the passed in flags are set, false otherwise (including no flags passed in). + */ + FORCEINLINE void ClearInternalFlags(EInternalObjectFlags FlagsToClear) const + { + GUObjectArray.IndexToObject(InternalIndex)->ClearFlags(FlagsToClear); + } + + /** + * Atomically clears passed in internal flags . + * + * @param FlagsToClear Object flags to clear. + * @return true if any of the passed in flags are set, false otherwise (including no flags passed in). + */ + FORCEINLINE bool AtomicallyClearInternalFlags(EInternalObjectFlags FlagsToClear) const + { + return GUObjectArray.IndexToObject(InternalIndex)->ThisThreadAtomicallyClearedFlag(FlagsToClear); + } + /***********************/ /******** Names ********/ /***********************/ diff --git a/Engine/Source/Runtime/CoreUObject/Public/UObject/UObjectGlobals.h b/Engine/Source/Runtime/CoreUObject/Public/UObject/UObjectGlobals.h index 7078de23f6ab..8a55bc53cf1a 100644 --- a/Engine/Source/Runtime/CoreUObject/Public/UObject/UObjectGlobals.h +++ b/Engine/Source/Runtime/CoreUObject/Public/UObject/UObjectGlobals.h @@ -77,6 +77,11 @@ struct FObjectDuplicationParameters */ EObjectFlags FlagMask; + /** + * a bitmask of EInternalObjectFlags to propagate to the duplicate of SourceObject (and its subobjects). + */ + EInternalObjectFlags InternalFlagMask; + /** * a bitmask of EObjectFlags to set on each duplicate object created. Different from FlagMask in that only the bits * from FlagMask which are also set on the source object will be set on the duplicate, while the flags in this value @@ -84,6 +89,13 @@ struct FObjectDuplicationParameters */ EObjectFlags ApplyFlags; + /** + * a bitmask of EInternalObjectFlags to set on each duplicate object created. Different from FlagMask in that only the bits + * from FlagMask which are also set on the source object will be set on the duplicate, while the flags in this value + * will always be set. + */ + EInternalObjectFlags ApplyInternalFlags; + /** * Any PortFlags to be applied when serializing. */ @@ -153,9 +165,10 @@ COREUOBJECT_API void SafeLoadError( UObject* Outer, uint32 LoadFlags, const TCHA * @param ExactClass Whether to require an exact match with the passed in class * @param AnyPackage Whether to look in any package * @param ExclusiveFlags Ignores objects that contain any of the specified exclusive flags + * @param ExclusiveInternalFlags Ignores objects that contain any of the specified internal exclusive flags * @return Returns a pointer to the found object or NULL if none could be found */ -COREUOBJECT_API UObject* StaticFindObjectFast( UClass* Class, UObject* InOuter, FName InName, bool ExactClass=false, bool AnyPackage=false, EObjectFlags ExclusiveFlags=RF_NoFlags ); +COREUOBJECT_API UObject* StaticFindObjectFast(UClass* Class, UObject* InOuter, FName InName, bool ExactClass = false, bool AnyPackage = false, EObjectFlags ExclusiveFlags = RF_NoFlags, EInternalObjectFlags ExclusiveInternalFlags = EInternalObjectFlags::None); COREUOBJECT_API UObject* StaticFindObject( UClass* Class, UObject* InOuter, const TCHAR* Name, bool ExactClass=false ); COREUOBJECT_API UObject* StaticFindObjectChecked( UClass* Class, UObject* InOuter, const TCHAR* Name, bool ExactClass=false ); COREUOBJECT_API UObject* StaticFindObjectSafe( UClass* Class, UObject* InOuter, const TCHAR* Name, bool ExactClass=false ); @@ -200,6 +213,7 @@ COREUOBJECT_API UClass* StaticLoadClass(UClass* BaseClass, UObject* InOuter, con * @param InOuter the object to create this object within (the Outer property for the new object will be set to the value specified here). * @param Name the name to give the new object. If no value (NAME_None) is specified, the object will be given a unique name in the form of ClassName_#. * @param SetFlags the ObjectFlags to assign to the new object. some flags can affect the behavior of constructing the object. +* @param InternalSetFlags the InternalObjectFlags to assign to the new object. some flags can affect the behavior of constructing the object. * @param Template if specified, the property values from this object will be copied to the new object, and the new object's ObjectArchetype value will be set to this object. * If NULL, the class default object is used instead. * @param bInCopyTransientsFromClassDefaults - if true, copy transient from the class defaults instead of the pass in archetype ptr (often these are the same) @@ -208,7 +222,7 @@ COREUOBJECT_API UClass* StaticLoadClass(UClass* BaseClass, UObject* InOuter, con * * @return a pointer to a fully initialized object of the specified class. */ -COREUOBJECT_API UObject* StaticConstructObject_Internal(UClass* Class, UObject* InOuter = (UObject*)GetTransientPackage(), FName Name = NAME_None, EObjectFlags SetFlags = RF_NoFlags, UObject* Template = NULL, bool bCopyTransientsFromClassDefaults = false, struct FObjectInstancingGraph* InstanceGraph = NULL); +COREUOBJECT_API UObject* StaticConstructObject_Internal(UClass* Class, UObject* InOuter = (UObject*)GetTransientPackage(), FName Name = NAME_None, EObjectFlags SetFlags = RF_NoFlags, EInternalObjectFlags InternalSetFlags = EInternalObjectFlags::None, UObject* Template = NULL, bool bCopyTransientsFromClassDefaults = false, struct FObjectInstancingGraph* InstanceGraph = NULL); /** * Create a new instance of an object. The returned object will be fully initialized. If InFlags contains RF_NeedsLoad (indicating that the object still needs to load its object data from disk), components @@ -242,7 +256,7 @@ COREUOBJECT_API UObject* StaticConstructObject( UClass* Class, UObject* InOuter= * @param FlagMask a bitmask of EObjectFlags that should be propagated to the object copies. The resulting object copies will only have the object flags * specified copied from their source object. * @param DestClass optional class to specify for the destination object. MUST BE SERIALIZATION COMPATIBLE WITH SOURCE OBJECT!!! - * @param bMigrateArchetypes unused + * @param InternalFlagsMask bitmask of EInternalObjectFlags that should be propagated to the object copies. * * @return the duplicate of SourceObject. * @@ -253,7 +267,7 @@ enum EDuplicateForPie SDO_No_DuplicateForPie, SDO_DuplicateForPie, }; -COREUOBJECT_API UObject* StaticDuplicateObject(UObject const* SourceObject,UObject* DestOuter,const TCHAR* DestName,EObjectFlags FlagMask = RF_AllFlags,UClass* DestClass=NULL, EDuplicateForPie DuplicateForPIE = SDO_No_DuplicateForPie); +COREUOBJECT_API UObject* StaticDuplicateObject(UObject const* SourceObject,UObject* DestOuter,const TCHAR* DestName,EObjectFlags FlagMask = RF_AllFlags,UClass* DestClass=NULL, EDuplicateForPie DuplicateForPIE = SDO_No_DuplicateForPie, EInternalObjectFlags InternalFlagsMask = EInternalObjectFlags::AllFlags); COREUOBJECT_API UObject* StaticDuplicateObjectEx( struct FObjectDuplicationParameters& Parameters ); /** @@ -427,11 +441,12 @@ COREUOBJECT_API FName MakeObjectNameFromDisplayLabel(const FString& DisplayLabel * * @param Obj Object to check * @param KeepFlags Objects with these flags will be considered as being referenced +* @param InternalKeepFlags Objects with these internal flags will be considered as being referenced * @param bCheckSubObjects Treat subobjects as if they are the same as passed in object * @param FoundReferences If non-NULL fill in with list of objects that hold references * @return true if object is referenced, false otherwise */ -COREUOBJECT_API bool IsReferenced( UObject*& Res, EObjectFlags KeepFlags, bool bCheckSubObjects = false, FReferencerInformationList* FoundReferences = NULL ); +COREUOBJECT_API bool IsReferenced( UObject*& Res, EObjectFlags KeepFlags, EInternalObjectFlags InternalKeepFlags, bool bCheckSubObjects = false, FReferencerInformationList* FoundReferences = NULL ); /** * Blocks till all pending package/ linker requests are fulfilled. @@ -549,11 +564,12 @@ bool StaticAllocateObjectErrorTests( UClass* Class, UObject* InOuter, FName Name * @param InOuter the object to create this object within (the Outer property for the new object will be set to the value specified here). * @param Name the name to give the new object. If no value (NAME_None) is specified, the object will be given a unique name in the form of ClassName_#. * @param SetFlags the ObjectFlags to assign to the new object. some flags can affect the behavior of constructing the object. + * @param InternalSetFlags the InternalObjectFlags to assign to the new object. some flags can affect the behavior of constructing the object. * @param bCanReuseSubobjects if set to true, SAO will not attempt to destroy a subobject if it already exists in memory. * @param bOutReusedSubobject flag indicating if the object is a subobject that has already been created (in which case further initialization is not necessary). * @return a pointer to a fully initialized object of the specified class. */ -COREUOBJECT_API UObject* StaticAllocateObject( UClass* Class, UObject* InOuter, FName Name, EObjectFlags SetFlags, bool bCanReuseSubobjects = false, bool* bOutReusedSubobject = NULL); +COREUOBJECT_API UObject* StaticAllocateObject(UClass* Class, UObject* InOuter, FName Name, EObjectFlags SetFlags, EInternalObjectFlags InternalSetFlags = EInternalObjectFlags::None, bool bCanReuseSubobjects = false, bool* bOutReusedSubobject = NULL); /** Base class for TSubobjectPtr template. Holds the actual pointer and utility methods. */ class COREUOBJECT_API FSubobjectPtr @@ -1157,7 +1173,7 @@ T* NewObject(UObject* Outer = (UObject*)GetTransientPackage(), UClass* Class = T } checkf(Class, TEXT("NewObject called with a nullptr class object")); checkSlow(DebugIsClassChildOf_Internal(T::StaticClass(), Class)); - return static_cast(StaticConstructObject_Internal(Class, Outer, Name, Flags, Template, bCopyTransientsFromClassDefaults, InInstanceGraph)); + return static_cast(StaticConstructObject_Internal(Class, Outer, Name, Flags, EInternalObjectFlags::None, Template, bCopyTransientsFromClassDefaults, InInstanceGraph)); } template< class T > @@ -1340,11 +1356,24 @@ bool ContainsObjectOfClass( const TArray& ObjectArray, UClass* ClassToCheck, */ class FScopedObjectFlagMarker { + struct FStoredObjectFlags + { + FStoredObjectFlags() + : Flags(RF_NoFlags) + , InternalFlags(EInternalObjectFlags::None) + {} + FStoredObjectFlags(EObjectFlags InFlags, EInternalObjectFlags InInternalFlags) + : Flags(InFlags) + , InternalFlags(InInternalFlags) + {} + EObjectFlags Flags; + EInternalObjectFlags InternalFlags; + }; /** * Map that tracks the ObjectFlags set on all objects; we use a map rather than iterating over all objects twice because FObjectIterator * won't return objects that have RF_Unreachable set, and we may want to actually unset that flag. */ - TMap StoredObjectFlags; + TMap StoredObjectFlags; /** * Stores the object flags for all objects in the tracking array. @@ -1821,4 +1850,10 @@ extern COREUOBJECT_API bool GShouldVerifyGCAssumptions; /** A struct used as stub for deleted ones. */ COREUOBJECT_API UScriptStruct* GetFallbackStruct(); +/** Constructs dynamic type of a given class. */ +COREUOBJECT_API UObject* ConstructDynamicType(FName TypeName, FName TypeClass); + +/** Finds or constructs a package for dynamic type. */ +COREUOBJECT_API UPackage* FindOrConstructDynamicTypePackage(const TCHAR* PackageName); + #endif // __UNOBJGLOBALS_H__ diff --git a/Engine/Source/Runtime/CoreUObject/Public/UObject/UObjectHash.h b/Engine/Source/Runtime/CoreUObject/Public/UObject/UObjectHash.h index a28b21e7c6d7..739ae9618b1b 100644 --- a/Engine/Source/Runtime/CoreUObject/Public/UObject/UObjectHash.h +++ b/Engine/Source/Runtime/CoreUObject/Public/UObject/UObjectHash.h @@ -9,12 +9,6 @@ DECLARE_STATS_GROUP(TEXT("UObject Hash"), STATGROUP_UObjectHash, STATCAT_Advanced); -/** - * Controls whether the number of available elements is being tracked in the ObjObjects array. - * By default it is only tracked in WITH_EDITOR builds as it adds a small amount of tracking overhead - */ -#define UE_GC_TRACK_OBJ_AVAILABLE (WITH_EDITOR) - #if UE_GC_TRACK_OBJ_AVAILABLE DECLARE_DWORD_COUNTER_STAT_EXTERN(TEXT("NumObjects"), STAT_Hash_NumObjects, STATGROUP_UObjectHash, COREUOBJECT_API); #endif @@ -28,9 +22,10 @@ DECLARE_DWORD_COUNTER_STAT_EXTERN(TEXT("NumObjects"), STAT_Hash_NumObjects, STAT * @param ExactClass Whether to require an exact match with the passed in class * @param AnyPackage Whether to look in any package * @param ExclusiveFlags Ignores objects that contain any of the specified exclusive flags + * @param ExclusiveInternalFlags Ignores objects that contain any of the specified internal exclusive flags * @return Returns a pointer to the found object or NULL if none could be found */ -UObject* StaticFindObjectFastInternal( UClass* Class, UObject* InOuter, FName InName, bool ExactClass=0, bool AnyPackage=0, EObjectFlags ExclusiveFlags=RF_NoFlags ); +UObject* StaticFindObjectFastInternal(UClass* Class, UObject* InOuter, FName InName, bool ExactClass = false, bool AnyPackage = false, EObjectFlags ExclusiveFlags = RF_NoFlags, EInternalObjectFlags ExclusiveInternalFlags = EInternalObjectFlags::None); /** * Variation of StaticFindObjectFast that uses explicit path. @@ -42,7 +37,7 @@ UObject* StaticFindObjectFastInternal( UClass* Class, UObject* InOuter, FName In * @param ExclusiveFlags Ignores objects that contain any of the specified exclusive flags * @return Returns a pointer to the found object or NULL if none could be found */ -UObject* StaticFindObjectFastExplicit( UClass* ObjectClass, FName ObjectName, const FString& ObjectPathName, bool bExactClass, EObjectFlags ExcludeFlags=RF_NoFlags ); +UObject* StaticFindObjectFastExplicit(UClass* ObjectClass, FName ObjectName, const FString& ObjectPathName, bool bExactClass, EObjectFlags ExcludeFlags = RF_NoFlags); /** * Return all objects with a given outer @@ -51,8 +46,9 @@ UObject* StaticFindObjectFastExplicit( UClass* ObjectClass, FName ObjectName, co * @param Results Returned results * @param bIncludeNestedObjects If true, then things whose outers directly or indirectly have Outer as an outer are included, these are the nested objects. * @param ExclusionFlags Specifies flags to use as a filter for which objects to return + * @param ExclusiveInternalFlags Specifies internal flags to use as a filter for which objects to return */ -COREUOBJECT_API void GetObjectsWithOuter(const class UObjectBase* Outer, TArray& Results, bool bIncludeNestedObjects = true, EObjectFlags ExclusionFlags = RF_NoFlags); +COREUOBJECT_API void GetObjectsWithOuter(const class UObjectBase* Outer, TArray& Results, bool bIncludeNestedObjects = true, EObjectFlags ExclusionFlags = RF_NoFlags, EInternalObjectFlags ExclusionInternalFlags = EInternalObjectFlags::None); /** * Performs an operation on all objects with a given outer @@ -61,8 +57,9 @@ COREUOBJECT_API void GetObjectsWithOuter(const class UObjectBase* Outer, TArray< * @param Operation Function to be called for each object * @param bIncludeNestedObjects If true, then things whose outers directly or indirectly have Outer as an outer are included, these are the nested objects. * @param ExclusionFlags Specifies flags to use as a filter for which objects to return + * @param ExclusiveInternalFlags Specifies internal flags to use as a filter for which objects to return */ -COREUOBJECT_API void ForEachObjectWithOuter(const class UObjectBase* Outer, TFunctionRef Operation, bool bIncludeNestedObjects = true, EObjectFlags ExclusionFlags = RF_NoFlags); +COREUOBJECT_API void ForEachObjectWithOuter(const class UObjectBase* Outer, TFunctionRef Operation, bool bIncludeNestedObjects = true, EObjectFlags ExclusionFlags = RF_NoFlags, EInternalObjectFlags ExclusionInternalFlags = EInternalObjectFlags::None); /** * Find an objects with a given name and or class within an outer @@ -80,8 +77,9 @@ COREUOBJECT_API class UObjectBase* FindObjectWithOuter(class UObjectBase* Outer, * @param Results An output list of objects of the specified class. * @param bIncludeDerivedClasses If true, the results will include objects of child classes as well. * @param AdditionalExcludeFlags Objects with any of these flags will be excluded from the results. + * @param ExclusiveInternalFlags Specifies internal flags to use as a filter for which objects to return */ -COREUOBJECT_API void GetObjectsOfClass(UClass* ClassToLookFor, TArray& Results, bool bIncludeDerivedClasses = true, EObjectFlags AdditionalExcludeFlags=RF_ClassDefaultObject); +COREUOBJECT_API void GetObjectsOfClass(UClass* ClassToLookFor, TArray& Results, bool bIncludeDerivedClasses = true, EObjectFlags ExcludeFlags = RF_ClassDefaultObject, EInternalObjectFlags ExclusionInternalFlags = EInternalObjectFlags::None); /** * Performs an operation on all objects with a given outer @@ -91,7 +89,7 @@ COREUOBJECT_API void GetObjectsOfClass(UClass* ClassToLookFor, TArray * @param bIncludeDerivedClasses If true, the results will include objects of child classes as well. * @param AdditionalExcludeFlags Objects with any of these flags will be excluded from the results. */ -COREUOBJECT_API void ForEachObjectOfClass(UClass* ClassToLookFor, TFunctionRef Operation, bool bIncludeDerivedClasses = true, EObjectFlags AdditionalExcludeFlags=RF_ClassDefaultObject); +COREUOBJECT_API void ForEachObjectOfClass(UClass* ClassToLookFor, TFunctionRef Operation, bool bIncludeDerivedClasses = true, EObjectFlags ExcludeFlags = RF_ClassDefaultObject, EInternalObjectFlags ExclusionInternalFlags = EInternalObjectFlags::None); /** * Returns an array of classes that were derived from the specified class. diff --git a/Engine/Source/Runtime/CoreUObject/Public/UObject/UObjectIterator.h b/Engine/Source/Runtime/CoreUObject/Public/UObject/UObjectIterator.h index 9637b65c2694..e29524df1bc1 100644 --- a/Engine/Source/Runtime/CoreUObject/Public/UObject/UObjectIterator.h +++ b/Engine/Source/Runtime/CoreUObject/Public/UObject/UObjectIterator.h @@ -26,18 +26,18 @@ public: * Iterator dereference * @return the object pointer pointed at by the iterator */ - FORCEINLINE UObject* operator*() const + FORCEINLINE FUObjectItem* operator*() const { // casting UObjectBase to UObject for clients - return (UObject *)GetObject(); + return GetObject(); } /** * Iterator dereference * @return the object pointer pointed at by the iterator */ - FORCEINLINE UObject* operator->() const + FORCEINLINE FUObjectItem* operator->() const { - return (UObject *)GetObject(); + return GetObject(); } }; @@ -58,23 +58,24 @@ public: * @param bOnlyGCedObjects if true, skip all of the permanent objects * @param AdditionalExclusionFlags RF_* flags that should not be included in results */ - FObjectIterator( UClass* InClass=UObject::StaticClass(), bool bOnlyGCedObjects = false, EObjectFlags AdditionalExclusionFlags = RF_NoFlags ) : - FUObjectArray::TIterator( GUObjectArray, bOnlyGCedObjects ), - Class( InClass ), - ExclusionFlags(AdditionalExclusionFlags) + FObjectIterator(UClass* InClass = UObject::StaticClass(), bool bOnlyGCedObjects = false, EObjectFlags AdditionalExclusionFlags = RF_NoFlags, EInternalObjectFlags InInternalExclusionFlags = EInternalObjectFlags::None) + : FUObjectArray::TIterator(GUObjectArray, bOnlyGCedObjects) + , Class(InClass) + , ExclusionFlags(AdditionalExclusionFlags) + , InternalExclusionFlags(InInternalExclusionFlags) { // We don't want to return any objects that are currently being background loaded unless we're using the object iterator during async loading. - ExclusionFlags = EObjectFlags(ExclusionFlags | RF_Unreachable); + InternalExclusionFlags |= EInternalObjectFlags::Unreachable; if (!IsInAsyncLoadingThread()) { - ExclusionFlags = EObjectFlags(ExclusionFlags | RF_AsyncLoading); + InternalExclusionFlags |= EInternalObjectFlags::AsyncLoading; } check(Class); do { UObject *Object = **this; - if (!(Object->HasAnyFlags(ExclusionFlags) || (Class != UObject::StaticClass() && !Object->IsA(Class)))) + if (!(Object->HasAnyFlags(ExclusionFlags) || Object->HasAnyInternalFlags(InternalExclusionFlags) || (Class != UObject::StaticClass() && !Object->IsA(Class)))) { break; } @@ -86,10 +87,11 @@ public: * * @param Begin The iterator to get the end iterator of. */ - FObjectIterator( FUObjectArray::TIterator::EEndTagType, const FObjectIterator& Begin ) : - FUObjectArray::TIterator( FUObjectArray::TIterator::EndTag, Begin ), - Class( Begin.Class ), - ExclusionFlags(Begin.ExclusionFlags) + FObjectIterator( FUObjectArray::TIterator::EEndTagType, const FObjectIterator& Begin ) + : FUObjectArray::TIterator( FUObjectArray::TIterator::EndTag, Begin ) + , Class( Begin.Class ) + , ExclusionFlags(Begin.ExclusionFlags) + , InternalExclusionFlags(Begin.InternalExclusionFlags) { } @@ -100,12 +102,12 @@ public: { //@warning: behavior is partially mirrored in UnObjGC.cpp. Make sure to adapt code there as well if you make changes below. // verify that the async loading exclusion flag still matches (i.e. we didn't start/stop async loading within the scope of the iterator) - checkSlow(IsInAsyncLoadingThread() || (ExclusionFlags & RF_AsyncLoading)); + checkSlow(IsInAsyncLoadingThread() || int32(InternalExclusionFlags & EInternalObjectFlags::AsyncLoading)); while(Advance()) { UObject *Object = **this; - if (!(Object->HasAnyFlags(ExclusionFlags) || (Class != UObject::StaticClass() && !Object->IsA(Class)))) + if (!(Object->HasAnyFlags(ExclusionFlags) || (Class != UObject::StaticClass() && !Object->IsA(Class)) || Object->HasAnyInternalFlags(InternalExclusionFlags))) { break; } @@ -118,7 +120,8 @@ public: FORCEINLINE UObject* operator*() const { // casting UObjectBase to UObject for clients - return (UObject *)GetObject(); + FUObjectItem* ObjectItem = GetObject(); + return (UObject*)(ObjectItem ? ObjectItem->Object : nullptr); } /** * Iterator dereference @@ -126,7 +129,8 @@ public: */ FORCEINLINE UObject* operator->() const { - return (UObject *)GetObject(); + FUObjectItem* ObjectItem = GetObject(); + return (UObject*)(ObjectItem ? ObjectItem->Object : nullptr); } private: /** Class to restrict results to */ @@ -134,6 +138,8 @@ private: protected: /** Flags that returned objects must not have */ EObjectFlags ExclusionFlags; + /** Internal Flags that returned objects must not have */ + EInternalObjectFlags InternalExclusionFlags; }; /** @@ -153,10 +159,10 @@ public: /** * Constructor */ - explicit TObjectIterator(EObjectFlags AdditionalExclusionFlags = RF_ClassDefaultObject, bool bIncludeDerivedClasses = true) + explicit TObjectIterator(EObjectFlags AdditionalExclusionFlags = RF_ClassDefaultObject, bool bIncludeDerivedClasses = true, EInternalObjectFlags InternalExclusionFlags = EInternalObjectFlags::None) : Index(-1) { - GetObjectsOfClass(T::StaticClass(), ObjectArray, bIncludeDerivedClasses, AdditionalExclusionFlags); + GetObjectsOfClass(T::StaticClass(), ObjectArray, bIncludeDerivedClasses, AdditionalExclusionFlags, InternalExclusionFlags); Advance(); } @@ -246,8 +252,8 @@ protected: template<> class TObjectIterator : public FObjectIterator { public: - explicit TObjectIterator(EObjectFlags AdditionalExclusionFlags = RF_ClassDefaultObject) : - FObjectIterator(UObject::StaticClass(), false, AdditionalExclusionFlags) + explicit TObjectIterator(EObjectFlags AdditionalExclusionFlags = RF_ClassDefaultObject, bool bIncludeDerivedClasses = true, EInternalObjectFlags InternalExclusionFlags = EInternalObjectFlags::None) + : FObjectIterator(UObject::StaticClass(), false, AdditionalExclusionFlags, InternalExclusionFlags) { } @@ -277,10 +283,10 @@ public: void operator++() { // verify that the async loading exclusion flag still matches (i.e. we didn't start/stop async loading within the scope of the iterator) - checkSlow(IsInAsyncLoadingThread() || (ExclusionFlags & RF_AsyncLoading)); + checkSlow(IsInAsyncLoadingThread() || int32(InternalExclusionFlags & EInternalObjectFlags::AsyncLoading)); while(Advance()) { - if (!(*this)->HasAnyFlags(ExclusionFlags)) + if (!(*this)->HasAnyFlags(ExclusionFlags) && !(*this)->HasAnyInternalFlags(InternalExclusionFlags)) { break; } @@ -291,8 +297,8 @@ public: template struct TObjectRange { - TObjectRange(EObjectFlags AdditionalExclusionFlags = RF_ClassDefaultObject, bool bIncludeDerivedClasses = true) - : Begin(AdditionalExclusionFlags, bIncludeDerivedClasses) + TObjectRange(EObjectFlags AdditionalExclusionFlags = RF_ClassDefaultObject, bool bIncludeDerivedClasses = true, EInternalObjectFlags InInternalExclusionFlags = EInternalObjectFlags::None) + : Begin(AdditionalExclusionFlags, bIncludeDerivedClasses, InInternalExclusionFlags) { } @@ -305,8 +311,8 @@ struct TObjectRange template <> struct TObjectRange { - explicit TObjectRange(EObjectFlags AdditionalExclusionFlags = RF_ClassDefaultObject) - : Begin(AdditionalExclusionFlags) + explicit TObjectRange(EObjectFlags AdditionalExclusionFlags = RF_ClassDefaultObject, bool bIncludeDerivedClasses = true, EInternalObjectFlags InInternalExclusionFlags = EInternalObjectFlags::None) + : Begin(AdditionalExclusionFlags, bIncludeDerivedClasses, InInternalExclusionFlags) { } diff --git a/Engine/Source/Runtime/CoreUObject/Public/UObject/UTextProperty.h b/Engine/Source/Runtime/CoreUObject/Public/UObject/UTextProperty.h index 05e31f01b287..dd8fd3d163c4 100644 --- a/Engine/Source/Runtime/CoreUObject/Public/UObject/UTextProperty.h +++ b/Engine/Source/Runtime/CoreUObject/Public/UObject/UTextProperty.h @@ -8,7 +8,7 @@ typedef TProperty UTextProperty_Super; class COREUOBJECT_API UTextProperty : public UTextProperty_Super { - DECLARE_CASTED_CLASS_INTRINSIC(UTextProperty, UTextProperty_Super, 0, CoreUObject, CASTCLASS_UTextProperty) + DECLARE_CASTED_CLASS_INTRINSIC(UTextProperty, UTextProperty_Super, 0, TEXT("/Script/CoreUObject"), CASTCLASS_UTextProperty) public: diff --git a/Engine/Source/Runtime/CoreUObject/Public/UObject/UnrealType.h b/Engine/Source/Runtime/CoreUObject/Public/UObject/UnrealType.h index 9912110a71f4..653922c5b95d 100644 --- a/Engine/Source/Runtime/CoreUObject/Public/UObject/UnrealType.h +++ b/Engine/Source/Runtime/CoreUObject/Public/UObject/UnrealType.h @@ -54,7 +54,7 @@ namespace EExportedDeclaration // class COREUOBJECT_API UProperty : public UField { - DECLARE_CASTED_CLASS_INTRINSIC_NO_CTOR(UProperty,UField,CLASS_Abstract,CoreUObject,CASTCLASS_UProperty,NO_API) + DECLARE_CASTED_CLASS_INTRINSIC_NO_CTOR(UProperty, UField, CLASS_Abstract, TEXT("/Script/CoreUObject"), CASTCLASS_UProperty, NO_API) DECLARE_WITHIN(UField) // Persistent variables. @@ -794,6 +794,11 @@ public: /** returns true, if Other is property of exactly the same type */ virtual bool SameType(const UProperty* Other) const; + +#if HACK_HEADER_GENERATOR + // Required by UHT makefiles for internal data serialization. + friend struct FPropertyArchiveProxy; +#endif // HACK_HEADER_GENERATOR }; @@ -1046,7 +1051,7 @@ public: class COREUOBJECT_API UNumericProperty : public UProperty { - DECLARE_CASTED_CLASS_INTRINSIC(UNumericProperty,UProperty,CLASS_Abstract,CoreUObject,CASTCLASS_UNumericProperty) + DECLARE_CASTED_CLASS_INTRINSIC(UNumericProperty, UProperty, CLASS_Abstract, TEXT("/Script/CoreUObject"), CASTCLASS_UNumericProperty) UNumericProperty(ECppProperty, int32 InOffset, uint64 InFlags) : UProperty(FObjectInitializer::Get(), EC_CppProperty, InOffset, InFlags) @@ -1275,7 +1280,7 @@ public: // class COREUOBJECT_API UByteProperty : public TProperty_Numeric { - DECLARE_CASTED_CLASS_INTRINSIC(UByteProperty,TProperty_Numeric,0,CoreUObject,CASTCLASS_UByteProperty) + DECLARE_CASTED_CLASS_INTRINSIC(UByteProperty, TProperty_Numeric, 0, TEXT("/Script/CoreUObject"), CASTCLASS_UByteProperty) // Variables. UEnum* Enum; @@ -1326,7 +1331,7 @@ class COREUOBJECT_API UByteProperty : public TProperty_Numeric // class COREUOBJECT_API UInt8Property : public TProperty_Numeric { - DECLARE_CASTED_CLASS_INTRINSIC(UInt8Property,TProperty_Numeric,0,CoreUObject,CASTCLASS_UInt8Property) + DECLARE_CASTED_CLASS_INTRINSIC(UInt8Property, TProperty_Numeric, 0, TEXT("/Script/CoreUObject"), CASTCLASS_UInt8Property) UInt8Property(ECppProperty, int32 InOffset, uint64 InFlags) : TProperty_Numeric(FObjectInitializer::Get(), EC_CppProperty, InOffset, InFlags) @@ -1348,7 +1353,7 @@ class COREUOBJECT_API UInt8Property : public TProperty_Numeric // class COREUOBJECT_API UInt16Property : public TProperty_Numeric { - DECLARE_CASTED_CLASS_INTRINSIC(UInt16Property,TProperty_Numeric,0,CoreUObject,CASTCLASS_UInt16Property) + DECLARE_CASTED_CLASS_INTRINSIC(UInt16Property, TProperty_Numeric, 0, TEXT("/Script/CoreUObject"), CASTCLASS_UInt16Property) UInt16Property(ECppProperty, int32 InOffset, uint64 InFlags) : TProperty_Numeric(FObjectInitializer::Get(), EC_CppProperty, InOffset, InFlags) @@ -1371,7 +1376,7 @@ class COREUOBJECT_API UInt16Property : public TProperty_Numeric // class COREUOBJECT_API UIntProperty : public TProperty_Numeric { - DECLARE_CASTED_CLASS_INTRINSIC(UIntProperty,TProperty_Numeric,0,CoreUObject,CASTCLASS_UIntProperty) + DECLARE_CASTED_CLASS_INTRINSIC(UIntProperty, TProperty_Numeric, 0, TEXT("/Script/CoreUObject"), CASTCLASS_UIntProperty) UIntProperty(ECppProperty, int32 InOffset, uint64 InFlags) : TProperty_Numeric(FObjectInitializer::Get(), EC_CppProperty, InOffset, InFlags) @@ -1393,7 +1398,7 @@ class COREUOBJECT_API UIntProperty : public TProperty_Numeric // class COREUOBJECT_API UInt64Property : public TProperty_Numeric { - DECLARE_CASTED_CLASS_INTRINSIC(UInt64Property,TProperty_Numeric,0,CoreUObject,CASTCLASS_UInt64Property) + DECLARE_CASTED_CLASS_INTRINSIC(UInt64Property, TProperty_Numeric, 0, TEXT("/Script/CoreUObject"), CASTCLASS_UInt64Property) UInt64Property(ECppProperty, int32 InOffset, uint64 InFlags) : TProperty_Numeric(FObjectInitializer::Get(), EC_CppProperty, InOffset, InFlags) @@ -1415,7 +1420,7 @@ class COREUOBJECT_API UInt64Property : public TProperty_Numeric // class COREUOBJECT_API UUInt16Property : public TProperty_Numeric { - DECLARE_CASTED_CLASS_INTRINSIC(UUInt16Property,TProperty_Numeric,0,CoreUObject,CASTCLASS_UUInt16Property) + DECLARE_CASTED_CLASS_INTRINSIC(UUInt16Property, TProperty_Numeric, 0, TEXT("/Script/CoreUObject"), CASTCLASS_UUInt16Property) UUInt16Property(ECppProperty, int32 InOffset, uint64 InFlags) : TProperty_Numeric(FObjectInitializer::Get(), EC_CppProperty, InOffset, InFlags) @@ -1437,7 +1442,7 @@ class COREUOBJECT_API UUInt16Property : public TProperty_Numeric // class COREUOBJECT_API UUInt32Property : public TProperty_Numeric { - DECLARE_CASTED_CLASS_INTRINSIC(UUInt32Property,TProperty_Numeric,0,CoreUObject,CASTCLASS_UUInt32Property) + DECLARE_CASTED_CLASS_INTRINSIC(UUInt32Property, TProperty_Numeric, 0, TEXT("/Script/CoreUObject"), CASTCLASS_UUInt32Property) UUInt32Property( const FObjectInitializer& ObjectInitializer, ECppProperty, int32 InOffset, uint64 InFlags ) : TProperty_Numeric( ObjectInitializer, EC_CppProperty, InOffset, InFlags ) @@ -1454,7 +1459,7 @@ class COREUOBJECT_API UUInt32Property : public TProperty_Numeric // class COREUOBJECT_API UUInt64Property : public TProperty_Numeric { - DECLARE_CASTED_CLASS_INTRINSIC(UUInt64Property,TProperty_Numeric,0,CoreUObject,CASTCLASS_UUInt64Property) + DECLARE_CASTED_CLASS_INTRINSIC(UUInt64Property, TProperty_Numeric, 0, TEXT("/Script/CoreUObject"), CASTCLASS_UUInt64Property) UUInt64Property(ECppProperty, int32 InOffset, uint64 InFlags) : TProperty_Numeric(FObjectInitializer::Get(), EC_CppProperty, InOffset, InFlags) @@ -1476,7 +1481,7 @@ class COREUOBJECT_API UUInt64Property : public TProperty_Numeric // class COREUOBJECT_API UFloatProperty : public TProperty_Numeric { - DECLARE_CASTED_CLASS_INTRINSIC(UFloatProperty,TProperty_Numeric,0,CoreUObject,CASTCLASS_UFloatProperty) + DECLARE_CASTED_CLASS_INTRINSIC(UFloatProperty, TProperty_Numeric, 0, TEXT("/Script/CoreUObject"), CASTCLASS_UFloatProperty) UFloatProperty(ECppProperty, int32 InOffset, uint64 InFlags) : TProperty_Numeric(FObjectInitializer::Get(), EC_CppProperty, InOffset, InFlags) @@ -1502,7 +1507,7 @@ class COREUOBJECT_API UFloatProperty : public TProperty_Numeric // class COREUOBJECT_API UDoubleProperty : public TProperty_Numeric { - DECLARE_CASTED_CLASS_INTRINSIC(UDoubleProperty,TProperty_Numeric,0,CoreUObject,CASTCLASS_UDoubleProperty) + DECLARE_CASTED_CLASS_INTRINSIC(UDoubleProperty, TProperty_Numeric, 0, TEXT("/Script/CoreUObject"), CASTCLASS_UDoubleProperty) UDoubleProperty(ECppProperty, int32 InOffset, uint64 InFlags) : TProperty_Numeric(FObjectInitializer::Get(), EC_CppProperty, InOffset, InFlags) @@ -1526,7 +1531,7 @@ class COREUOBJECT_API UDoubleProperty : public TProperty_Numeric // class COREUOBJECT_API UBoolProperty : public UProperty { - DECLARE_CASTED_CLASS_INTRINSIC_NO_CTOR(UBoolProperty,UProperty,0,CoreUObject,CASTCLASS_UBoolProperty, NO_API) + DECLARE_CASTED_CLASS_INTRINSIC_NO_CTOR(UBoolProperty, UProperty, 0, TEXT("/Script/CoreUObject"), CASTCLASS_UBoolProperty, NO_API) // Variables. private: @@ -1647,6 +1652,11 @@ public: { return FieldMask == 0xff; } + +#if HACK_HEADER_GENERATOR + // Required by UHT makefiles for internal data serialization. + friend struct FBoolPropertyArchiveProxy; +#endif // HACK_HEADER_GENERATOR }; /*----------------------------------------------------------------------------- @@ -1658,7 +1668,7 @@ public: // class COREUOBJECT_API UObjectPropertyBase : public UProperty { - DECLARE_CASTED_CLASS_INTRINSIC(UObjectPropertyBase,UProperty,CLASS_Abstract,CoreUObject,CASTCLASS_UObjectPropertyBase) + DECLARE_CASTED_CLASS_INTRINSIC(UObjectPropertyBase, UProperty, CLASS_Abstract, TEXT("/Script/CoreUObject"), CASTCLASS_UObjectPropertyBase) // Variables. class UClass* PropertyClass; @@ -1870,7 +1880,7 @@ public: // class COREUOBJECT_API UObjectProperty : public TUObjectPropertyBase { - DECLARE_CASTED_CLASS_INTRINSIC(UObjectProperty,TUObjectPropertyBase,0,CoreUObject,CASTCLASS_UObjectProperty) + DECLARE_CASTED_CLASS_INTRINSIC(UObjectProperty, TUObjectPropertyBase, 0, TEXT("/Script/CoreUObject"), CASTCLASS_UObjectProperty) UObjectProperty(ECppProperty, int32 InOffset, uint64 InFlags, UClass* InClass) : TUObjectPropertyBase(FObjectInitializer::Get(), EC_CppProperty, InOffset, InFlags | CPF_HasGetValueTypeHash, InClass) @@ -1915,7 +1925,7 @@ public: // class COREUOBJECT_API UWeakObjectProperty : public TUObjectPropertyBase { - DECLARE_CASTED_CLASS_INTRINSIC(UWeakObjectProperty,TUObjectPropertyBase,0,CoreUObject,CASTCLASS_UWeakObjectProperty) + DECLARE_CASTED_CLASS_INTRINSIC(UWeakObjectProperty, TUObjectPropertyBase, 0, TEXT("/Script/CoreUObject"), CASTCLASS_UWeakObjectProperty) UWeakObjectProperty(ECppProperty, int32 InOffset, uint64 InFlags, UClass* InClass) : TUObjectPropertyBase(FObjectInitializer::Get(), EC_CppProperty, InOffset, InFlags, InClass) @@ -1954,7 +1964,7 @@ class COREUOBJECT_API UWeakObjectProperty : public TUObjectPropertyBase { - DECLARE_CASTED_CLASS_INTRINSIC(ULazyObjectProperty,TUObjectPropertyBase,0,CoreUObject,CASTCLASS_ULazyObjectProperty) + DECLARE_CASTED_CLASS_INTRINSIC(ULazyObjectProperty, TUObjectPropertyBase, 0, TEXT("/Script/CoreUObject"), CASTCLASS_ULazyObjectProperty) ULazyObjectProperty(ECppProperty, int32 InOffset, uint64 InFlags, UClass* InClass) : TUObjectPropertyBase(FObjectInitializer::Get(), EC_CppProperty, InOffset, InFlags | CPF_HasGetValueTypeHash, InClass) @@ -2004,7 +2014,7 @@ public: // class COREUOBJECT_API UAssetObjectProperty : public TUObjectPropertyBase { - DECLARE_CASTED_CLASS_INTRINSIC(UAssetObjectProperty,TUObjectPropertyBase,0,CoreUObject,CASTCLASS_UAssetObjectProperty) + DECLARE_CASTED_CLASS_INTRINSIC(UAssetObjectProperty, TUObjectPropertyBase, 0, TEXT("/Script/CoreUObject"), CASTCLASS_UAssetObjectProperty) UAssetObjectProperty(ECppProperty, int32 InOffset, uint64 InFlags, UClass* InClass) : TUObjectPropertyBase(FObjectInitializer::Get(), EC_CppProperty, InOffset, InFlags | CPF_HasGetValueTypeHash, InClass) @@ -2082,7 +2092,7 @@ public: // class COREUOBJECT_API UClassProperty : public UObjectProperty { - DECLARE_CASTED_CLASS_INTRINSIC(UClassProperty,UObjectProperty,0,CoreUObject,CASTCLASS_UClassProperty) + DECLARE_CASTED_CLASS_INTRINSIC(UClassProperty, UObjectProperty, 0, TEXT("/Script/CoreUObject"), CASTCLASS_UClassProperty) // Variables. class UClass* MetaClass; @@ -2146,7 +2156,7 @@ protected: // class COREUOBJECT_API UAssetClassProperty : public UAssetObjectProperty { - DECLARE_CASTED_CLASS_INTRINSIC(UAssetClassProperty,UAssetObjectProperty,0,CoreUObject,CASTCLASS_UAssetClassProperty) + DECLARE_CASTED_CLASS_INTRINSIC(UAssetClassProperty, UAssetObjectProperty, 0, TEXT("/Script/CoreUObject"), CASTCLASS_UAssetClassProperty) // Variables. class UClass* MetaClass; @@ -2193,7 +2203,7 @@ typedef TProperty UInterfaceProperty_Super; class COREUOBJECT_API UInterfaceProperty : public UInterfaceProperty_Super { - DECLARE_CASTED_CLASS_INTRINSIC(UInterfaceProperty,UInterfaceProperty_Super,0,CoreUObject,CASTCLASS_UInterfaceProperty) + DECLARE_CASTED_CLASS_INTRINSIC(UInterfaceProperty, UInterfaceProperty_Super, 0, TEXT("/Script/CoreUObject"), CASTCLASS_UInterfaceProperty) /** The native interface class that this interface property refers to */ class UClass* InterfaceClass; @@ -2266,7 +2276,7 @@ typedef TProperty_WithEqualityAndSerializer UNameProperty_Supe class COREUOBJECT_API UNameProperty : public UNameProperty_Super { - DECLARE_CASTED_CLASS_INTRINSIC(UNameProperty,UNameProperty_Super,0,CoreUObject,CASTCLASS_UNameProperty) + DECLARE_CASTED_CLASS_INTRINSIC(UNameProperty, UNameProperty_Super, 0, TEXT("/Script/CoreUObject"), CASTCLASS_UNameProperty) public: typedef UNameProperty_Super::TTypeFundamentals TTypeFundamentals; typedef TTypeFundamentals::TCppType TCppType; @@ -2309,7 +2319,7 @@ typedef TProperty_WithEqualityAndSerializer UStrProperty_Sup class COREUOBJECT_API UStrProperty : public UStrProperty_Super { - DECLARE_CASTED_CLASS_INTRINSIC(UStrProperty,UStrProperty_Super,0,CoreUObject,CASTCLASS_UStrProperty) + DECLARE_CASTED_CLASS_INTRINSIC(UStrProperty, UStrProperty_Super, 0, TEXT("/Script/CoreUObject"), CASTCLASS_UStrProperty) public: typedef UStrProperty_Super::TTypeFundamentals TTypeFundamentals; typedef TTypeFundamentals::TCppType TCppType; @@ -2352,7 +2362,7 @@ typedef TProperty UArrayProperty_Super; class COREUOBJECT_API UArrayProperty : public UArrayProperty_Super { - DECLARE_CASTED_CLASS_INTRINSIC(UArrayProperty,UArrayProperty_Super,0,CoreUObject,CASTCLASS_UArrayProperty) + DECLARE_CASTED_CLASS_INTRINSIC(UArrayProperty, UArrayProperty_Super, 0, TEXT("/Script/CoreUObject"), CASTCLASS_UArrayProperty) // Variables. UProperty* Inner; @@ -2409,7 +2419,7 @@ typedef TProperty UMapProperty_Super; class COREUOBJECT_API UMapProperty : public UMapProperty_Super { - DECLARE_CASTED_CLASS_INTRINSIC(UMapProperty, UMapProperty_Super, 0, CoreUObject, CASTCLASS_UMapProperty) + DECLARE_CASTED_CLASS_INTRINSIC(UMapProperty, UMapProperty_Super, 0, TEXT("/Script/CoreUObject"), CASTCLASS_UMapProperty) // Properties representing the key type and value type of the contained pairs UProperty* KeyProp; @@ -3195,7 +3205,7 @@ public: // class COREUOBJECT_API UStructProperty : public UProperty { - DECLARE_CASTED_CLASS_INTRINSIC(UStructProperty,UProperty,0,CoreUObject,CASTCLASS_UStructProperty) + DECLARE_CASTED_CLASS_INTRINSIC(UStructProperty, UProperty, 0, TEXT("/Script/CoreUObject"), CASTCLASS_UStructProperty) // Variables. class UScriptStruct* Struct; @@ -3262,7 +3272,7 @@ typedef TProperty UDelegateProperty_Super; class COREUOBJECT_API UDelegateProperty : public UDelegateProperty_Super { - DECLARE_CASTED_CLASS_INTRINSIC(UDelegateProperty,UDelegateProperty_Super,0,CoreUObject,CASTCLASS_UDelegateProperty) + DECLARE_CASTED_CLASS_INTRINSIC(UDelegateProperty, UDelegateProperty_Super, 0, TEXT("/Script/CoreUObject"), CASTCLASS_UDelegateProperty) /** Points to the source delegate function (the function declared with the delegate keyword) used in the declaration of this delegate property. */ UFunction* SignatureFunction; @@ -3315,7 +3325,7 @@ typedef TProperty UMulticastDelegatePropert class COREUOBJECT_API UMulticastDelegateProperty : public UMulticastDelegateProperty_Super { - DECLARE_CASTED_CLASS_INTRINSIC(UMulticastDelegateProperty,UMulticastDelegateProperty_Super,0,CoreUObject,CASTCLASS_UMulticastDelegateProperty) + DECLARE_CASTED_CLASS_INTRINSIC(UMulticastDelegateProperty, UMulticastDelegateProperty_Super, 0, TEXT("/Script/CoreUObject"), CASTCLASS_UMulticastDelegateProperty) /** Points to the source delegate function (the function declared with the delegate keyword) used in the declaration of this delegate property. */ UFunction* SignatureFunction; diff --git a/Engine/Source/Runtime/CoreUObject/Public/UObject/WeakObjectPtr.h b/Engine/Source/Runtime/CoreUObject/Public/UObject/WeakObjectPtr.h index 798c5259bb85..3f9c34f1828d 100644 --- a/Engine/Source/Runtime/CoreUObject/Public/UObject/WeakObjectPtr.h +++ b/Engine/Source/Runtime/CoreUObject/Public/UObject/WeakObjectPtr.h @@ -21,11 +21,6 @@ struct FWeakObjectPtr { public: - /** - * Startup the weak object system - **/ - static COREUOBJECT_API void Init(); - /** NULL constructor **/ FORCEINLINE FWeakObjectPtr() @@ -159,13 +154,65 @@ private: * internal function to test for serial number matches * @return true if the serial number in this matches the central table **/ - bool SerialNumbersMatch() const; + FORCEINLINE_DEBUGGABLE bool SerialNumbersMatch() const + { + checkSlow(ObjectSerialNumber > FUObjectArray::START_SERIAL_NUMBER && ObjectIndex >= 0); // otherwise this is a corrupted weak pointer + int32 ActualSerialNumber = GUObjectArray.GetSerialNumber(ObjectIndex); + checkSlow(!ActualSerialNumber || ActualSerialNumber >= ObjectSerialNumber); // serial numbers should never shrink + return ActualSerialNumber == ObjectSerialNumber; + } + + FORCEINLINE_DEBUGGABLE bool SerialNumbersMatch(FUObjectItem* ObjectItem) const + { + checkSlow(ObjectSerialNumber > FUObjectArray::START_SERIAL_NUMBER && ObjectIndex >= 0); // otherwise this is a corrupted weak pointer + const int32 ActualSerialNumber = ObjectItem->GetSerialNumber(); + checkSlow(!ActualSerialNumber || ActualSerialNumber >= ObjectSerialNumber); // serial numbers should never shrink + return ActualSerialNumber == ObjectSerialNumber; + } /** Private (inlined) version for internal use only. */ - bool Internal_IsValid(bool bEvenIfPendingKill, bool bThreadsafeTest) const; + FORCEINLINE_DEBUGGABLE bool Internal_IsValid(bool bEvenIfPendingKill, bool bThreadsafeTest) const + { + if (ObjectSerialNumber == 0) + { + checkSlow(ObjectIndex == 0 || ObjectIndex == -1); // otherwise this is a corrupted weak pointer + return false; + } + if (ObjectIndex < 0) + { + return false; + } + FUObjectItem* ObjectItem = GUObjectArray.IndexToObject(ObjectIndex); + if (!ObjectItem) + { + return false; + } + if (!SerialNumbersMatch(ObjectItem)) + { + return false; + } + if (bThreadsafeTest) + { + return true; + } + return GUObjectArray.IsValid(ObjectItem, bEvenIfPendingKill); + } /** Private (inlined) version for internal use only. */ - class UObject* Internal_Get(bool bEvenIfPendingKill) const; + FORCEINLINE_DEBUGGABLE UObject* Internal_Get(bool bEvenIfPendingKill) const + { + UObject* Result = nullptr; + + if (Internal_IsValid(true, true)) + { + FUObjectItem* ObjectItem = GUObjectArray.IndexToValidObject(GetObjectIndex(), bEvenIfPendingKill); + if (ObjectItem) + { + Result = (UObject*)ObjectItem->Object; + } + } + return Result; + } int32 ObjectIndex; int32 ObjectSerialNumber; diff --git a/Engine/Source/Runtime/Engine/Classes/Engine/CoreSettings.h b/Engine/Source/Runtime/Engine/Classes/Engine/CoreSettings.h index 12f1f186ebd0..7746f1d162ca 100644 --- a/Engine/Source/Runtime/Engine/Classes/Engine/CoreSettings.h +++ b/Engine/Source/Runtime/Engine/Classes/Engine/CoreSettings.h @@ -151,6 +151,16 @@ protected: ToolTip = "Size Of Permanent Object Pool (bytes). Works only in cooked builds.")) int32 SizeOfPermanentObjectPool; + UPROPERTY(EditAnywhere, config, Category = Optimization, meta = ( + ConsoleVariable = "gc.MaxObjectsInGame", DisplayName = "Maximum number of UObjects that can exist in cooked game", + ToolTip = "Maximum number of UObjects that can exist in cooked game. Keep this as small as possible.")) + int32 MaxObjectsInGame; + + UPROPERTY(EditAnywhere, config, Category = Optimization, meta = ( + ConsoleVariable = "gc.MaxObjectsInEditor", DisplayName = "Maximum number of UObjects that can exist in the editor", + ToolTip = "Maximum number of UObjects that can exist in the editor game. Make sure this can hold enough objects for the editor and commadlets within reasonable limit.")) + int32 MaxObjectsInEditor; + //~ Begin UObject Interface virtual void PostInitProperties() override; diff --git a/Engine/Source/Runtime/Engine/Private/Actor.cpp b/Engine/Source/Runtime/Engine/Private/Actor.cpp index 46cfe1bdc672..8ecf04d7283b 100644 --- a/Engine/Source/Runtime/Engine/Private/Actor.cpp +++ b/Engine/Source/Runtime/Engine/Private/Actor.cpp @@ -102,7 +102,7 @@ void AActor::InitializeDefaults() void FActorTickFunction::ExecuteTick(float DeltaTime, enum ELevelTick TickType, ENamedThreads::Type CurrentThread, const FGraphEventRef& MyCompletionGraphEvent) { - if (Target && !Target->HasAnyFlags(RF_PendingKill | RF_Unreachable)) + if (Target && !Target->IsPendingKillOrUnreachable()) { FScopeCycleCounterUObject ActorScope(Target); Target->TickActor(DeltaTime*Target->CustomTimeDilation, TickType, *this); @@ -230,7 +230,7 @@ void AActor::ResetOwnedComponents() TArray ActorChildren; OwnedComponents.Empty(); ReplicatedComponents.Empty(); - GetObjectsWithOuter(this, ActorChildren, true, RF_PendingKill); + GetObjectsWithOuter(this, ActorChildren, true, RF_NoFlags, EInternalObjectFlags::PendingKill); for (UObject* Child : ActorChildren) { @@ -272,7 +272,7 @@ UWorld* AActor::GetWorld() const { // CDO objects do not belong to a world // If the actors outer is destroyed or unreachable we are shutting down and the world should be NULL - return (!HasAnyFlags(RF_ClassDefaultObject) && !GetOuter()->HasAnyFlags(RF_BeginDestroyed|RF_Unreachable) ? GetLevel()->OwningWorld : NULL); + return (!HasAnyFlags(RF_ClassDefaultObject) && !GetOuter()->HasAnyFlags(RF_BeginDestroyed) && !GetOuter()->IsUnreachable() ? GetLevel()->OwningWorld : NULL); } FTimerManager& AActor::GetWorldTimerManager() const @@ -2720,9 +2720,9 @@ void AActor::PostActorConstruction() // Set IsPendingKill() to true so that when the initial undo record is made, // the actor will be treated as destroyed, in that undo an add will // actually work - SetFlags(RF_PendingKill); + MarkPendingKill(); Modify(false); - ClearFlags(RF_PendingKill); + ClearPendingKill(); } if (!IsPendingKill()) diff --git a/Engine/Source/Runtime/Engine/Private/Animation/AnimInstance.cpp b/Engine/Source/Runtime/Engine/Private/Animation/AnimInstance.cpp index ee2c9adaea85..f61609c809ec 100644 --- a/Engine/Source/Runtime/Engine/Private/Animation/AnimInstance.cpp +++ b/Engine/Source/Runtime/Engine/Private/Animation/AnimInstance.cpp @@ -937,7 +937,7 @@ void UAnimInstance::Serialize(FArchive& Ar) if (!Ar.IsLoading() || !Ar.IsSaving()) { - Ar << GetProxyOnGameThread().GetRequiredBones(); + Ar << GetProxyOnAnyThread().GetRequiredBones(); } } diff --git a/Engine/Source/Runtime/Engine/Private/Animation/AnimNotifyState_Trail.cpp b/Engine/Source/Runtime/Engine/Private/Animation/AnimNotifyState_Trail.cpp index cffa4bacdae7..37c18a1e789c 100644 --- a/Engine/Source/Runtime/Engine/Private/Animation/AnimNotifyState_Trail.cpp +++ b/Engine/Source/Runtime/Engine/Private/Animation/AnimNotifyState_Trail.cpp @@ -26,7 +26,7 @@ static void GetCandidateSystems(USkeletalMeshComponent& MeshComp, ParticleSystem { // No actor owner in some editor windows. Get PSCs spawned by the MeshComp. TArray Children; - GetObjectsWithOuter(&MeshComp, Children, false, RF_PendingKill); + GetObjectsWithOuter(&MeshComp, Children, false, RF_NoFlags, EInternalObjectFlags::PendingKill); for (UObject* Child : Children) { if (UParticleSystemComponent* ChildPSC = Cast(Child)) diff --git a/Engine/Source/Runtime/Engine/Private/Animation/AnimSequence.cpp b/Engine/Source/Runtime/Engine/Private/Animation/AnimSequence.cpp index 81dc52707096..ea6d117f5412 100644 --- a/Engine/Source/Runtime/Engine/Private/Animation/AnimSequence.cpp +++ b/Engine/Source/Runtime/Engine/Private/Animation/AnimSequence.cpp @@ -1830,7 +1830,7 @@ bool UAnimSequence::CopyAnimSequenceProperties(UAnimSequence* SourceAnimSeq, UAn DestAnimSeq->bDoNotOverrideCompression = SourceAnimSeq->bDoNotOverrideCompression; // Copy Compression Settings - DestAnimSeq->CompressionScheme = static_cast( StaticDuplicateObject( SourceAnimSeq->CompressionScheme, DestAnimSeq, TEXT("None"), ~RF_RootSet ) ); + DestAnimSeq->CompressionScheme = static_cast(StaticDuplicateObject(SourceAnimSeq->CompressionScheme, DestAnimSeq, TEXT("None"), RF_AllFlags, nullptr, SDO_No_DuplicateForPie, ~EInternalObjectFlags::RootSet)); DestAnimSeq->TranslationCompressionFormat = SourceAnimSeq->TranslationCompressionFormat; DestAnimSeq->RotationCompressionFormat = SourceAnimSeq->RotationCompressionFormat; DestAnimSeq->AdditiveAnimType = SourceAnimSeq->AdditiveAnimType; @@ -1939,7 +1939,7 @@ bool UAnimSequence::CopyNotifies(UAnimSequence* SourceAnimSeq, UAnimSequence* De // Copy the notify itself, and point the new one at it. if( SrcNotifyEvent.Notify ) { - DestAnimSeq->Notifies[NewNotifyIndex].Notify = static_cast( StaticDuplicateObject(SrcNotifyEvent.Notify, DestAnimSeq, TEXT("None"), ~RF_RootSet ) ); + DestAnimSeq->Notifies[NewNotifyIndex].Notify = static_cast(StaticDuplicateObject(SrcNotifyEvent.Notify, DestAnimSeq, TEXT("None"), RF_AllFlags, nullptr, SDO_No_DuplicateForPie, ~EInternalObjectFlags::RootSet)); } else { @@ -1948,7 +1948,7 @@ bool UAnimSequence::CopyNotifies(UAnimSequence* SourceAnimSeq, UAnimSequence* De if( SrcNotifyEvent.NotifyStateClass ) { - DestAnimSeq->Notifies[NewNotifyIndex].NotifyStateClass = static_cast( StaticDuplicateObject(SrcNotifyEvent.NotifyStateClass, DestAnimSeq, TEXT("None"), ~RF_RootSet ) ); + DestAnimSeq->Notifies[NewNotifyIndex].NotifyStateClass = static_cast(StaticDuplicateObject(SrcNotifyEvent.NotifyStateClass, DestAnimSeq, TEXT("None"), RF_AllFlags, nullptr, SDO_No_DuplicateForPie, ~EInternalObjectFlags::RootSet)); } else { diff --git a/Engine/Source/Runtime/Engine/Private/Blueprint.cpp b/Engine/Source/Runtime/Engine/Private/Blueprint.cpp index 003ed8927ff6..ca0205a35cde 100644 --- a/Engine/Source/Runtime/Engine/Private/Blueprint.cpp +++ b/Engine/Source/Runtime/Engine/Private/Blueprint.cpp @@ -663,7 +663,7 @@ UObject* UBlueprint::GetObjectBeingDebugged() if(DebugObj) { //Check whether the object has been deleted. - if(DebugObj->HasAnyFlags(RF_PendingKill)) + if(DebugObj->IsPendingKill()) { SetObjectBeingDebugged(NULL); DebugObj = NULL; @@ -677,7 +677,7 @@ UWorld* UBlueprint::GetWorldBeingDebugged() UWorld* DebugWorld = CurrentWorldBeingDebugged.Get(); if (DebugWorld) { - if(DebugWorld->HasAnyFlags(RF_PendingKill)) + if(DebugWorld->IsPendingKill()) { SetWorldBeingDebugged(NULL); DebugWorld = NULL; @@ -1043,13 +1043,13 @@ void UBlueprint::TagSubobjects(EObjectFlags NewFlags) { Super::TagSubobjects(NewFlags); - if (GeneratedClass && !GeneratedClass->HasAnyFlags(GARBAGE_COLLECTION_KEEPFLAGS | RF_RootSet)) + if (GeneratedClass && !GeneratedClass->HasAnyFlags(GARBAGE_COLLECTION_KEEPFLAGS) && !GeneratedClass->IsRooted()) { GeneratedClass->SetFlags(NewFlags); GeneratedClass->TagSubobjects(NewFlags); } - if (SkeletonGeneratedClass && SkeletonGeneratedClass != GeneratedClass && !SkeletonGeneratedClass->HasAnyFlags(GARBAGE_COLLECTION_KEEPFLAGS | RF_RootSet)) + if (SkeletonGeneratedClass && SkeletonGeneratedClass != GeneratedClass && !SkeletonGeneratedClass->HasAnyFlags(GARBAGE_COLLECTION_KEEPFLAGS) && !SkeletonGeneratedClass->IsRooted()) { SkeletonGeneratedClass->SetFlags(NewFlags); SkeletonGeneratedClass->TagSubobjects(NewFlags); diff --git a/Engine/Source/Runtime/Engine/Private/Collision/Collision.cpp b/Engine/Source/Runtime/Engine/Private/Collision/Collision.cpp index b4d626dd2fbe..595c41bfee5a 100644 --- a/Engine/Source/Runtime/Engine/Private/Collision/Collision.cpp +++ b/Engine/Source/Runtime/Engine/Private/Collision/Collision.cpp @@ -716,7 +716,7 @@ namespace CollisionResponseConsoleCommands FCollisionResponseTemplate Template; if (UCollisionProfile::Get()->GetProfileTemplate(ProfileToCheck, Template)) { - for (TObjectIterator Iter(RF_PendingKill); Iter; ++Iter) + for (TObjectIterator Iter(RF_NoFlags, /*bIncludeDerivedClasses*/ true, EInternalObjectFlags::PendingKill); Iter; ++Iter) { UPrimitiveComponent* Comp = *Iter; diff --git a/Engine/Source/Runtime/Engine/Private/Components/ActorComponent.cpp b/Engine/Source/Runtime/Engine/Private/Components/ActorComponent.cpp index 28e7282cc7f1..4a13278f6e9e 100644 --- a/Engine/Source/Runtime/Engine/Private/Components/ActorComponent.cpp +++ b/Engine/Source/Runtime/Engine/Private/Components/ActorComponent.cpp @@ -121,7 +121,7 @@ UActorComponent::UActorComponent(const FObjectInitializer& ObjectInitializer /*= PrimaryComponentTick.SetTickFunctionEnable(false); CreationMethod = EComponentCreationMethod::Native; - + bAutoRegister = true; bNetAddressable = false; bEditableWhenInherited = true; @@ -611,7 +611,7 @@ void UActorComponent::PostEditChangeChainProperty(FPropertyChangedChainEvent& Pr void UActorComponent::OnRegister() { - checkf(!HasAnyFlags(RF_Unreachable), TEXT("%s"), *GetDetailedInfo()); + checkf(!IsUnreachable(), TEXT("%s"), *GetDetailedInfo()); checkf(!GetOuter()->IsTemplate(), TEXT("'%s' (%s)"), *GetOuter()->GetFullName(), *GetDetailedInfo()); checkf(!IsTemplate(), TEXT("'%s' (%s)"), *GetOuter()->GetFullName(), *GetDetailedInfo() ); checkf(World, TEXT("OnRegister: %s to %s"), *GetDetailedInfo(), GetOwner() ? *GetOwner()->GetFullName() : TEXT("*** No Owner ***") ); @@ -688,7 +688,7 @@ FActorComponentInstanceData* UActorComponent::GetComponentInstanceData() const void FActorComponentTickFunction::ExecuteTick(float DeltaTime, enum ELevelTick TickType, ENamedThreads::Type CurrentThread, const FGraphEventRef& MyCompletionGraphEvent) { - if (Target && !Target->HasAnyFlags(RF_PendingKill | RF_Unreachable)) + if (Target && !Target->IsPendingKillOrUnreachable()) { FScopeCycleCounterUObject ComponentScope(Target); FScopeCycleCounterUObject AdditionalScope(Target->AdditionalStatObject()); @@ -796,7 +796,7 @@ void UActorComponent::RegisterComponentWithWorld(UWorld* InWorld) SCOPE_CYCLE_COUNTER(STAT_RegisterComponent); FScopeCycleCounterUObject ComponentScope(this); - checkf(!HasAnyFlags(RF_Unreachable), TEXT("%s"), *GetFullName()); + checkf(!IsUnreachable(), TEXT("%s"), *GetFullName()); if(IsPendingKill()) { @@ -833,7 +833,7 @@ void UActorComponent::RegisterComponentWithWorld(UWorld* InWorld) // Can only register with an Actor if we are created within one if(MyOwner) { - checkf(!MyOwner->HasAnyFlags(RF_Unreachable), TEXT("%s"), *GetFullName()); + checkf(!MyOwner->IsUnreachable(), TEXT("%s"), *GetFullName()); // can happen with undo because the owner will be restored "next" //checkf(!MyOwner->IsPendingKill(), TEXT("%s"), *GetFullName()); @@ -881,7 +881,7 @@ void UActorComponent::RegisterComponentWithWorld(UWorld* InWorld) if (IsCreatedByConstructionScript()) { TArray Children; - GetObjectsWithOuter(this, Children, true, RF_PendingKill); + GetObjectsWithOuter(this, Children, true, RF_NoFlags, EInternalObjectFlags::PendingKill); for (UObject* Child : Children) { @@ -1225,7 +1225,7 @@ void UActorComponent::RemoveTickPrerequisiteComponent(UActorComponent* Prerequis void UActorComponent::DoDeferredRenderUpdates_Concurrent() { - checkf(!HasAnyFlags(RF_Unreachable), TEXT("%s"), *GetFullName()); + checkf(!IsUnreachable(), TEXT("%s"), *GetFullName()); checkf(!IsTemplate(), TEXT("%s"), *GetFullName()); checkf(!IsPendingKill(), TEXT("%s"), *GetFullName()); @@ -1303,7 +1303,7 @@ void UActorComponent::MarkForNeededEndOfFrameUpdate() { ComponentWorld->MarkActorComponentForNeededEndOfFrameUpdate(this, RequiresGameThreadEndOfFrameUpdates()); } - else if (!HasAnyFlags(RF_Unreachable)) + else if (!IsUnreachable()) { // we don't have a world, do it right now. DoDeferredRenderUpdates_Concurrent(); @@ -1323,7 +1323,7 @@ void UActorComponent::MarkForNeededEndOfFrameRecreate() // by convention, recreates are always done on the gamethread ComponentWorld->MarkActorComponentForNeededEndOfFrameUpdate(this, RequiresGameThreadEndOfFrameRecreate()); } - else if (!HasAnyFlags(RF_Unreachable)) + else if (!IsUnreachable()) { // we don't have a world, do it right now. DoDeferredRenderUpdates_Concurrent(); diff --git a/Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp b/Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp index d7551fd19d8d..96336e9c0653 100644 --- a/Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp +++ b/Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp @@ -184,7 +184,7 @@ void FFindFloorResult::SetFromLineTrace(const FHitResult& InHit, const float InS void FCharacterMovementComponentPreClothTickFunction::ExecuteTick(float DeltaTime, enum ELevelTick TickType, ENamedThreads::Type CurrentThread, const FGraphEventRef& MyCompletionGraphEvent) { - if ( (TickType == LEVELTICK_All) && Target && !Target->HasAnyFlags(RF_PendingKill | RF_Unreachable)) + if ( (TickType == LEVELTICK_All) && Target && !Target->IsPendingKillOrUnreachable()) { FScopeCycleCounterUObject ComponentScope(Target); FScopeCycleCounterUObject AdditionalScope(Target->AdditionalStatObject()); diff --git a/Engine/Source/Runtime/Engine/Private/Components/LightComponent.cpp b/Engine/Source/Runtime/Engine/Private/Components/LightComponent.cpp index e8dbe7a8cc2a..b40c8e855859 100644 --- a/Engine/Source/Runtime/Engine/Private/Components/LightComponent.cpp +++ b/Engine/Source/Runtime/Engine/Private/Components/LightComponent.cpp @@ -1095,7 +1095,7 @@ void ULightComponent::ReassignStationaryLightChannels(UWorld* TargetWorld, bool TMap > LightToOverlapMap; // Build an array of all static shadowing lights that need to be assigned - for(TObjectIterator LightIt(RF_ClassDefaultObject|RF_PendingKill); LightIt; ++LightIt) + for (TObjectIterator LightIt(RF_ClassDefaultObject, /** bIncludeDerivedClasses */ true, /** InternalExcludeFlags */ EInternalObjectFlags::PendingKill); LightIt; ++LightIt) { ULightComponent* const LightComponent = *LightIt; diff --git a/Engine/Source/Runtime/Engine/Private/Components/PrimitiveComponent.cpp b/Engine/Source/Runtime/Engine/Private/Components/PrimitiveComponent.cpp index 08e7fd6fe4dd..30fb084d9dcf 100644 --- a/Engine/Source/Runtime/Engine/Private/Components/PrimitiveComponent.cpp +++ b/Engine/Source/Runtime/Engine/Private/Components/PrimitiveComponent.cpp @@ -285,7 +285,7 @@ void UPrimitiveComponent::GetUsedTextures(TArray& OutTextures, EMater **/ void FPrimitiveComponentPostPhysicsTickFunction::ExecuteTick(float DeltaTime, enum ELevelTick TickType, ENamedThreads::Type CurrentThread, const FGraphEventRef& MyCompletionGraphEvent) { - if ( (TickType == LEVELTICK_All) && Target && !Target->HasAnyFlags(RF_PendingKill | RF_Unreachable)) + if ( (TickType == LEVELTICK_All) && Target && !Target->IsPendingKillOrUnreachable()) { FScopeCycleCounterUObject ComponentScope(Target); FScopeCycleCounterUObject AdditionalScope(Target->AdditionalStatObject()); diff --git a/Engine/Source/Runtime/Engine/Private/Components/SkyLightComponent.cpp b/Engine/Source/Runtime/Engine/Private/Components/SkyLightComponent.cpp index 79bc01d72e4c..e8e0b0d5c100 100644 --- a/Engine/Source/Runtime/Engine/Private/Components/SkyLightComponent.cpp +++ b/Engine/Source/Runtime/Engine/Private/Components/SkyLightComponent.cpp @@ -351,7 +351,7 @@ void USkyLightComponent::CheckForErrors() USkyLightComponent* Component = *ComponentIt; if (Component != this - && !Component->HasAnyFlags(RF_PendingKill) + && !Component->IsPendingKill() && Component->bVisible && Component->bAffectsWorld && Component->GetOwner() diff --git a/Engine/Source/Runtime/Engine/Private/CoreSettings.cpp b/Engine/Source/Runtime/Engine/Private/CoreSettings.cpp index 8a89caf2d643..1fa85d49d374 100644 --- a/Engine/Source/Runtime/Engine/Private/CoreSettings.cpp +++ b/Engine/Source/Runtime/Engine/Private/CoreSettings.cpp @@ -108,6 +108,8 @@ UGarbageCollectionSettings::UGarbageCollectionSettings() NumRetriesBeforeForcingGC = 0; MaxObjectsNotConsideredByGC = 0; SizeOfPermanentObjectPool = 0; + MaxObjectsInEditor = 12 * 1024 * 1024; + MaxObjectsInGame = 2 * 1024 * 1024; } void UGarbageCollectionSettings::PostInitProperties() diff --git a/Engine/Source/Runtime/Engine/Private/DeviceProfiles/DeviceProfile.cpp b/Engine/Source/Runtime/Engine/Private/DeviceProfiles/DeviceProfile.cpp index 8ffb31e0414a..98e5213cedf2 100644 --- a/Engine/Source/Runtime/Engine/Private/DeviceProfiles/DeviceProfile.cpp +++ b/Engine/Source/Runtime/Engine/Private/DeviceProfiles/DeviceProfile.cpp @@ -108,7 +108,7 @@ void UDeviceProfile::PostEditChangeProperty( FPropertyChangedEvent& PropertyChan { UDeviceProfile* ParentProfile = *DeviceProfileIt; - if( !ParentProfile->HasAnyFlags(RF_PendingKill) ) + if( !ParentProfile->IsPendingKill() ) { int32 ProfileGeneration = 1; while( ParentProfile != NULL ) diff --git a/Engine/Source/Runtime/Engine/Private/InheritableComponentHandler.cpp b/Engine/Source/Runtime/Engine/Private/InheritableComponentHandler.cpp index 9eb294871633..39269fecf579 100644 --- a/Engine/Source/Runtime/Engine/Private/InheritableComponentHandler.cpp +++ b/Engine/Source/Runtime/Engine/Private/InheritableComponentHandler.cpp @@ -57,7 +57,7 @@ UActorComponent* UInheritableComponentHandler::CreateOverridenComponentTemplate( // kill so we can identify that situation here (see UE-13987/UE-13990) if (NewComponentTemplate->IsPendingKill()) { - NewComponentTemplate->ClearFlags(RF_PendingKill); + NewComponentTemplate->ClearPendingKill(); UEngine::FCopyPropertiesForUnrelatedObjectsParams CopyParams; CopyParams.bDoDelta = false; UEngine::CopyPropertiesForUnrelatedObjects(BestArchetype, NewComponentTemplate, CopyParams); diff --git a/Engine/Source/Runtime/Engine/Private/KismetMathLibrary.cpp b/Engine/Source/Runtime/Engine/Private/KismetMathLibrary.cpp index 50afc788fa65..1236b9b32f2e 100644 --- a/Engine/Source/Runtime/Engine/Private/KismetMathLibrary.cpp +++ b/Engine/Source/Runtime/Engine/Private/KismetMathLibrary.cpp @@ -1260,6 +1260,13 @@ bool UKismetMathLibrary::ClassIsChildOf(TSubclassOf TestClass, TS *****************************************************************************/ FDateTime UKismetMathLibrary::MakeDateTime(int32 Year, int32 Month, int32 Day, int32 Hour, int32 Minute, int32 Second, int32 Millisecond) { + if (!FDateTime::Validate(Year, Month, Day, Hour, Minute, Second, Millisecond)) + { + FFrame::KismetExecutionMessage(*FString::Printf(TEXT("DateTime in bad format (year %d, month %d, day %d, hour %d, minute %d, second %d, millisecond %d). E.g. year, month and day can't be zero."), Year, Month, Day, Hour, Minute, Second, Millisecond), ELogVerbosity::Warning); + + return FDateTime(1, 1, 1, 0, 0, 0, 0); + } + return FDateTime(Year, Month, Day, Hour, Minute, Second, Millisecond); } diff --git a/Engine/Source/Runtime/Engine/Private/Level.cpp b/Engine/Source/Runtime/Engine/Private/Level.cpp index f9053165d8c2..228f7dbe7c89 100644 --- a/Engine/Source/Runtime/Engine/Private/Level.cpp +++ b/Engine/Source/Runtime/Engine/Private/Level.cpp @@ -1231,7 +1231,7 @@ void ULevel::PostEditUndo() //Actors.Remove(nullptr); // removed because TTransArray exploded (undo followed by redo ends up with a different ArrayNum to originally) TSet ActorsSet(Actors); TArray InnerObjects; - GetObjectsWithOuter(this, InnerObjects, /*bIncludeNestedObjects*/ false, /*ExclusionFlags*/ RF_PendingKill); + GetObjectsWithOuter(this, InnerObjects, /*bIncludeNestedObjects*/ false, /*ExclusionFlags*/ RF_NoFlags, /* InternalExclusionFlags */ EInternalObjectFlags::PendingKill); for (UObject* InnerObject : InnerObjects) { AActor* InnerActor = Cast(InnerObject); @@ -1774,7 +1774,7 @@ TArray ULevel::GetLevelBlueprints() const { TArray LevelBlueprints; TArray LevelChildren; - GetObjectsWithOuter(this, LevelChildren, false, RF_PendingKill); + GetObjectsWithOuter(this, LevelChildren, false, RF_NoFlags, EInternalObjectFlags::PendingKill); for (UObject* LevelChild : LevelChildren) { diff --git a/Engine/Source/Runtime/Engine/Private/LevelStreaming.cpp b/Engine/Source/Runtime/Engine/Private/LevelStreaming.cpp index 5c1f84d65d8c..cb21280176a2 100644 --- a/Engine/Source/Runtime/Engine/Private/LevelStreaming.cpp +++ b/Engine/Source/Runtime/Engine/Private/LevelStreaming.cpp @@ -358,7 +358,7 @@ bool ULevelStreaming::RequestLevel(UWorld* PersistentWorld, bool bAllowLevelLoad } // Try to find the [to be] loaded package. - UPackage* LevelPackage = (UPackage*) StaticFindObjectFast(UPackage::StaticClass(), NULL, DesiredPackageName, 0, 0, RF_PendingKill); + UPackage* LevelPackage = (UPackage*)StaticFindObjectFast(UPackage::StaticClass(), NULL, DesiredPackageName, 0, 0, RF_NoFlags, EInternalObjectFlags::PendingKill); // Package is already or still loaded. if (LevelPackage) diff --git a/Engine/Source/Runtime/Engine/Private/PrimitiveComponentPhysics.cpp b/Engine/Source/Runtime/Engine/Private/PrimitiveComponentPhysics.cpp index 591d94eb95ad..c0a7d12a6eb9 100644 --- a/Engine/Source/Runtime/Engine/Private/PrimitiveComponentPhysics.cpp +++ b/Engine/Source/Runtime/Engine/Private/PrimitiveComponentPhysics.cpp @@ -800,7 +800,7 @@ void UPrimitiveComponent::UnWeldFromParent() { if (FBodyInstance* RootBI = RootComponent->GetBodyInstance(SocketName, false)) { - bool bRootIsBeingDeleted = RootComponent->HasAnyFlags(RF_PendingKill) || RootComponent->HasAnyFlags(RF_Unreachable); + bool bRootIsBeingDeleted = RootComponent->IsPendingKillOrUnreachable(); if (!bRootIsBeingDeleted) { //create new root diff --git a/Engine/Source/Runtime/Engine/Private/SkeletalMeshComponentPhysics.cpp b/Engine/Source/Runtime/Engine/Private/SkeletalMeshComponentPhysics.cpp index d025af9d07a6..a8049da35143 100644 --- a/Engine/Source/Runtime/Engine/Private/SkeletalMeshComponentPhysics.cpp +++ b/Engine/Source/Runtime/Engine/Private/SkeletalMeshComponentPhysics.cpp @@ -47,7 +47,7 @@ void FSkeletalMeshComponentPreClothTickFunction::ExecuteTick(float DeltaTime, en { QUICK_SCOPE_CYCLE_COUNTER(FSkeletalMeshComponentPreClothTickFunction_ExecuteTick); - if ((TickType == LEVELTICK_All) && Target && !Target->HasAnyFlags(RF_PendingKill | RF_Unreachable)) + if ((TickType == LEVELTICK_All) && Target && !Target->IsPendingKillOrUnreachable()) { Target->PreClothTick(DeltaTime, *this); } diff --git a/Engine/Source/Runtime/Engine/Private/SoundNodeMature.cpp b/Engine/Source/Runtime/Engine/Private/SoundNodeMature.cpp index 4a50f541e8b8..9c9be4374566 100644 --- a/Engine/Source/Runtime/Engine/Private/SoundNodeMature.cpp +++ b/Engine/Source/Runtime/Engine/Private/SoundNodeMature.cpp @@ -152,7 +152,7 @@ void USoundNodeMature::PostLoad() { Super::PostLoad(); - if( !GIsEditor && GEngine && !HasAnyFlags(RF_RootSet) && ChildNodes.Num() >= 2 ) + if( !GIsEditor && GEngine && !IsRooted() && ChildNodes.Num() >= 2 ) { // Make sure the SoundCue has gotten all the SoundWavePlayers in to memory GetOuter()->ConditionalPostLoad(); diff --git a/Engine/Source/Runtime/Engine/Private/StatsRender2.cpp b/Engine/Source/Runtime/Engine/Private/StatsRender2.cpp index f1dbbd64c251..0d5e68dea80c 100644 --- a/Engine/Source/Runtime/Engine/Private/StatsRender2.cpp +++ b/Engine/Source/Runtime/Engine/Private/StatsRender2.cpp @@ -18,7 +18,7 @@ enum class EStatRenderConsts NUM_COLUMNS = 5, }; -/** Should we use a solid fill or a graident? */ +/** Should we use a solid fill or a gradient? */ const bool bUseFlatBackgroundForStats = true; /** Enumerates stat font types and maximum length of the stat names. */ @@ -36,7 +36,7 @@ enum class EStatFontTypes : int32 struct FStatFont { /** Default constructor. */ - FStatFont( int32 InMaxDisplayedChars, int32 InFontHeight, int32 InFontHeightOffsets ) : + FStatFont( const int32 InMaxDisplayedChars, const int32 InFontHeight, const int32 InFontHeightOffsets ) : MaxDisplayedChars( InMaxDisplayedChars ), FontHeight( InFontHeight ), FontHeightOffset( InFontHeightOffsets ) @@ -55,7 +55,7 @@ struct FStatFont static FStatFont GStatFonts[(int32)EStatFontTypes::NumFonts] = { /** Tiny. */ - FStatFont( 36, 10, 1 ), + FStatFont( 40, 10, 1 ), /** Small. */ FStatFont( 72, 12, 2 ), }; @@ -97,9 +97,15 @@ struct FStatRenderGlobals /** Current size of the viewport, used to detect resolution changes. */ FIntPoint SizeXY; + /** The X size that can be used to render the stats. */ + int32 SafeSizeX; + /** Current stat font type. */ EStatFontTypes StatFontType; + /** Whether we are in the stereo mode. */ + bool bIsStereo; + /** Whether we need update internals. */ bool bNeedRefresh; @@ -121,9 +127,10 @@ struct FStatRenderGlobals /** * Initializes stat render globals. */ - void Initialize( const FIntPoint NewSizeXY ) + void Initialize( const int32 InSizeX, const int32 InSizeY, const int32 InSafeSizeX, const bool bInIsStereo ) { - if( SizeXY != NewSizeXY ) + FIntPoint NewSizeXY( InSizeX, InSizeY ); + if (NewSizeXY != SizeXY) { SizeXY = NewSizeXY; bNeedRefresh = true; @@ -134,6 +141,14 @@ struct FStatRenderGlobals SetNewFont( EStatFontTypes::Tiny ); } + SafeSizeX = InSafeSizeX; + bIsStereo = bInIsStereo; + + if (bIsStereo) + { + SetNewFont( EStatFontTypes::Tiny ); + } + if( bNeedRefresh ) { // This is the number of W characters to leave spacing for in the stat name column. @@ -146,7 +161,7 @@ struct FStatRenderGlobals int32 StatColumnSpaceSizeY = 0; int32 TimeColumnSpaceSizeY = 0; - // @TODO yrx 2015-04-17 Compute on the stats thread to get the exact measurement + // #TODO: Compute on the stats thread to get the exact measurement // Determine where the first column goes. StringSize(StatFont, AfterNameColumnOffset, StatColumnSpaceSizeY, *STATNAME_COLUMN_WIDTH); @@ -162,7 +177,8 @@ struct FStatRenderGlobals */ int32 GetNumCharsForStatName() const { - return GStatFonts[(int32)StatFontType].MaxDisplayedChars; + const int32 MaxDisplayedChars = GStatFonts[(int32)StatFontType].MaxDisplayedChars; + return !bIsStereo ? MaxDisplayedChars : MaxDisplayedChars / 2; } /** @@ -256,7 +272,7 @@ public: } StatCmdEngineExec; -static void RightJustify(FCanvas* Canvas, int32 X, int32 Y, TCHAR const* Text, FLinearColor const& Color) +static void RightJustify( FCanvas* Canvas, const int32 X, const int32 Y, TCHAR const* Text, FLinearColor const& Color ) { const FStatRenderGlobals& Globals = GetStatRenderGlobals(); @@ -276,7 +292,7 @@ static void RightJustify(FCanvas* Canvas, int32 X, int32 Y, TCHAR const* Text, F * @param Indent Indentation of this cycles, used when rendering hierarchy * @param bStackStat If false, this is a non-stack cycle counter, don't render the call count column */ -static int32 RenderCycle( const FComplexStatMessage& Item, class FCanvas* Canvas, int32 X, int32 Y, const int32 Indent, const bool bStackStat, float Budget, bool bIsBudgetIgnored) +static int32 RenderCycle( const FComplexStatMessage& Item, class FCanvas* Canvas, const int32 X, const int32 Y, const int32 Indent, const bool bStackStat, const float Budget, const bool bIsBudgetIgnored ) { const bool bBudget = Budget >= 0.f; const FStatRenderGlobals& Globals = GetStatRenderGlobals(); @@ -317,7 +333,7 @@ static int32 RenderCycle( const FComplexStatMessage& Item, class FCanvas* Canvas } } - // @TODO yrx 2015-04-17 Move to the stats thread to avoid expensive computation on the game thread. + // #TODO: Move to the stats thread to avoid expensive computation on the game thread. const FString StatDesc = Item.GetDescription(); const FString StatDisplay = StatDesc.Len() == 0 ? Item.GetShortName().GetPlainNameString() : StatDesc; @@ -370,7 +386,7 @@ static int32 RenderCycle( const FComplexStatMessage& Item, class FCanvas* Canvas static FString FormatStatValueFloat(const float Value) { const float Frac = FMath::Frac(Value); - // #YRX_Stats: 2015-07-30 Move to stats thread, add support for int64 type, int32 may not be sufficient all the time. + // #TODO: Move to stats thread, add support for int64 type, int32 may not be sufficient all the time. const int32 Integer = FMath::FloorToInt(Value); const FString IntString = FString::FormatAsNumber(Integer); const FString FracString = FString::Printf(TEXT("%0.2f"), Frac); @@ -394,7 +410,7 @@ static FString FormatStatValueInt64(const int64 Value) * * @return the height of headings rendered */ -static int32 RenderGroupedHeadings(class FCanvas* Canvas,int X,int32 Y,const bool bIsHierarchy, bool bBudget) +static int32 RenderGroupedHeadings( class FCanvas* Canvas, const int X, const int32 Y, const bool bIsHierarchy, const bool bBudget ) { // The heading looks like: // Stat [32chars] CallCount [8chars] IncAvg [8chars] IncMax [8chars] ExcAvg [8chars] ExcMax [8chars] @@ -439,7 +455,7 @@ static int32 RenderGroupedHeadings(class FCanvas* Canvas,int X,int32 Y,const boo * * @return the height of headings rendered */ -static int32 RenderCounterHeadings(class FCanvas* Canvas,int32 X,int32 Y) +static int32 RenderCounterHeadings( class FCanvas* Canvas, const int32 X, const int32 Y ) { // The heading looks like: // Stat [32chars] Value [8chars] Average [8chars] @@ -468,7 +484,7 @@ static int32 RenderCounterHeadings(class FCanvas* Canvas,int32 X,int32 Y) * * @return the height of headings rendered */ -static int32 RenderMemoryHeadings(class FCanvas* Canvas,int32 X,int32 Y) +static int32 RenderMemoryHeadings( class FCanvas* Canvas, const int32 X, const int32 Y ) { // The heading looks like: // Stat [32chars] MemUsed [8chars] PhysMem [8chars] @@ -493,7 +509,7 @@ static int32 RenderMemoryHeadings(class FCanvas* Canvas,int32 X,int32 Y) } // @param bAutoType true: automatically choose GB/MB/KB/... false: always use MB for easier comparisons -static FString GetMemoryString( double Value, bool bAutoType = true ) +static FString GetMemoryString( const double Value, const bool bAutoType = true ) { if (bAutoType) { @@ -515,7 +531,7 @@ static FString GetMemoryString( double Value, bool bAutoType = true ) return FString::Printf( TEXT( "%.2f MB" ), float( Value / (1024.0 * 1024.0) ) ); } -static int32 RenderMemoryCounter(const FGameThreadHudData& ViewData, const FComplexStatMessage& All,class FCanvas* Canvas,int32 X,int32 Y, float Budget, bool bIsBudgetIgnored) +static int32 RenderMemoryCounter( const FGameThreadHudData& ViewData, const FComplexStatMessage& All, class FCanvas* Canvas, const int32 X, const int32 Y, const float Budget, const bool bIsBudgetIgnored ) { FPlatformMemory::EMemoryCounterRegion Region = FPlatformMemory::EMemoryCounterRegion(All.NameAndInfo.GetField()); // At this moment we only have memory stats that are marked as non frame stats, so can't be cleared every frame. @@ -552,7 +568,7 @@ static int32 RenderMemoryCounter(const FGameThreadHudData& ViewData, const FComp return Globals.GetFontHeight(); } -static int32 RenderCounter(const FGameThreadHudData& ViewData, const FComplexStatMessage& All,class FCanvas* Canvas,int32 X,int32 Y, float Budget, bool bIsBudgetIgnored) +static int32 RenderCounter( const FGameThreadHudData& ViewData, const FComplexStatMessage& All, class FCanvas* Canvas, const int32 X, const int32 Y, const float Budget, const bool bIsBudgetIgnored ) { const FStatRenderGlobals& Globals = GetStatRenderGlobals(); @@ -599,7 +615,7 @@ static int32 RenderCounter(const FGameThreadHudData& ViewData, const FComplexSta return Globals.GetFontHeight(); } -void RenderHierCycles( FCanvas* Canvas, int32 X, int32& Y, const FHudGroup& HudGroup ) +void RenderHierCycles( FCanvas* Canvas, const int32 X, int32& Y, const FHudGroup& HudGroup ) { const FStatRenderGlobals& Globals = GetStatRenderGlobals(); const FTexture* BackgroundTexture = Globals.GetBackgroundTexture(); @@ -622,7 +638,7 @@ void RenderHierCycles( FCanvas* Canvas, int32 X, int32& Y, const FHudGroup& HudG } -int32 RenderGroupBudget(FCanvas* Canvas, int32 X, int32 Y, uint64 AvgTotalTime, uint64 MaxTotalTime, float GroupBudget) +int32 RenderGroupBudget( FCanvas* Canvas, const int32 X, const int32 Y, const uint64 AvgTotalTime, const uint64 MaxTotalTime, const float GroupBudget ) { // The budget looks like: // Stat [32chars] Value [8chars] Average [8chars] @@ -647,7 +663,7 @@ int32 RenderGroupBudget(FCanvas* Canvas, int32 X, int32 Y, uint64 AvgTotalTime, } template< typename T > -void RenderArrayOfStats( FCanvas* Canvas, int32 X, int32& Y, const TArray& Aggregates, const FGameThreadHudData& ViewData, const TSet& IgnoreBudgetStats, float TotalGroupBudget, const T& FunctionToCall ) +void RenderArrayOfStats( FCanvas* Canvas, const int32 X, int32& Y, const TArray& Aggregates, const FGameThreadHudData& ViewData, const TSet& IgnoreBudgetStats, const float TotalGroupBudget, const T& FunctionToCall ) { const FStatRenderGlobals& Globals = GetStatRenderGlobals(); const FTexture* BackgroundTexture = Globals.GetBackgroundTexture(); @@ -687,7 +703,7 @@ void RenderArrayOfStats( FCanvas* Canvas, int32 X, int32& Y, const TArrayGetDefaultObject()->DefaultTexture; @@ -776,22 +792,22 @@ static void RenderGroupedWithHierarchy(const FGameThreadHudData& ViewData, FView } - // Render memory counters. - if (HudGroup.MemoryAggregate.Num()) - { - Y += RenderMemoryHeadings(Canvas, X, Y); + // Render memory counters. + if (HudGroup.MemoryAggregate.Num()) + { + Y += RenderMemoryHeadings(Canvas, X, Y); RenderArrayOfStats(Canvas, X, Y, HudGroup.MemoryAggregate, ViewData, HudGroup.BudgetIgnoreStats, -1.f, RenderMemoryCounter); - Y += Globals.GetFontHeight(); - } + Y += Globals.GetFontHeight(); + } - // Render remaining counters. - if (HudGroup.CountersAggregate.Num()) - { - Y += RenderCounterHeadings(Canvas, X, Y); + // Render remaining counters. + if (HudGroup.CountersAggregate.Num()) + { + Y += RenderCounterHeadings(Canvas, X, Y); RenderArrayOfStats(Canvas, X, Y, HudGroup.CountersAggregate, ViewData, HudGroup.BudgetIgnoreStats, -1.f, RenderCounter); - Y += Globals.GetFontHeight(); + Y += Globals.GetFontHeight(); + } } - } } /** @@ -801,8 +817,9 @@ static void RenderGroupedWithHierarchy(const FGameThreadHudData& ViewData, FView * @param Canvas Canvas object to use for rendering * @param X the X location to start rendering at * @param Y the Y location to start rendering at + * @param SafeSizeX the X size that can be used to render the stats */ -void RenderStats(FViewport* Viewport, class FCanvas* Canvas, int32 X, int32 Y) +void RenderStats(FViewport* Viewport, class FCanvas* Canvas, int32 X, int32 Y, int32 SafeSizeX) { DECLARE_SCOPE_CYCLE_COUNTER( TEXT( "RenderStats" ), STAT_RenderStats, STATGROUP_StatSystem ); @@ -813,7 +830,9 @@ void RenderStats(FViewport* Viewport, class FCanvas* Canvas, int32 X, int32 Y) } FStatRenderGlobals& Globals = GetStatRenderGlobals(); - Globals.Initialize(Viewport->GetSizeXY()); + // SizeX is used to clip/arrange the rendered stats to avoid overlay in stereo mode. + const bool bIsStereo = Canvas->IsStereoRendering(); + Globals.Initialize( Viewport->GetSizeXY().X, Viewport->GetSizeXY().Y, SafeSizeX, bIsStereo ); if( !ViewData->bDrawOnlyRawStats ) { diff --git a/Engine/Source/Runtime/Engine/Private/Texture2D.cpp b/Engine/Source/Runtime/Engine/Private/Texture2D.cpp index 17de8643622c..1f813e7b9ed0 100644 --- a/Engine/Source/Runtime/Engine/Private/Texture2D.cpp +++ b/Engine/Source/Runtime/Engine/Private/Texture2D.cpp @@ -414,7 +414,7 @@ void UTexture2D::UpdateResource() void UTexture2D::PostLinkerChange() { // Changing the linker requires re-creating the resource to make sure streaming behavior is right. - if( !HasAnyFlags( RF_Unreachable | RF_BeginDestroyed | RF_NeedLoad | RF_NeedPostLoad ) ) + if( !HasAnyFlags( RF_BeginDestroyed | RF_NeedLoad | RF_NeedPostLoad ) && !IsUnreachable() ) { // Update the resource. UpdateResource(); @@ -576,7 +576,7 @@ bool UTexture2D::UpdateStreamingStatus( bool bWaitForMipFading /*= false*/ ) // We can't load the source art from a bulk data object if the texture itself is pending kill because the linker will have been detached. // In this case we don't rebuild the data and instead let the streaming request be cancelled. This will let the garbage collector finish // destroying the object. - if (!HasAnyFlags(RF_PendingKill | RF_Unreachable)) + if (!IsPendingKillOrUnreachable()) { ForceRebuildPlatformData(); } @@ -1467,7 +1467,7 @@ void FTexture2DResource::GetData( uint32 MipIndex, void* Dest, uint32 DestPitch #if WITH_EDITORONLY_DATA bMipIsInDerivedDataCache = MipMap.DerivedDataKey.IsEmpty() == false; #endif - if (bMipIsInDerivedDataCache || MipMap.BulkData.ShouldFreeOnEmpty()) + if (bMipIsInDerivedDataCache) { FMemory::Free(MipData[MipIndex]); } diff --git a/Engine/Source/Runtime/Engine/Private/UnrealEngine.cpp b/Engine/Source/Runtime/Engine/Private/UnrealEngine.cpp index b0b7762f32a9..697bc03930c7 100644 --- a/Engine/Source/Runtime/Engine/Private/UnrealEngine.cpp +++ b/Engine/Source/Runtime/Engine/Private/UnrealEngine.cpp @@ -7584,8 +7584,8 @@ void DrawStatsHUD( UWorld* World, FViewport* Viewport, FCanvas* Canvas, UCanvas* GEngine->RenderEngineStats(World, Viewport, Canvas, StatsXOffset, MessageY, X, Y, &ViewLocation, &ViewRotation); #if STATS - extern void RenderStats(FViewport* Viewport, class FCanvas* Canvas, int32 X, int32 Y); - RenderStats(Viewport, Canvas, StatsXOffset, Y); + extern void RenderStats(FViewport* Viewport, class FCanvas* Canvas, int32 X, int32 Y, int32 SizeX); + RenderStats( Viewport, Canvas, StatsXOffset, Y, CanvasObject != nullptr ? CanvasObject->CachedDisplayWidth - CanvasObject->SafeZonePadX * 2 : Viewport->GetSizeXY().X ); #endif } @@ -7709,7 +7709,6 @@ DEFINE_STAT(STAT_RedrawViewports); DEFINE_STAT(STAT_UpdateLevelStreaming); DEFINE_STAT(STAT_RHITickTime); DEFINE_STAT(STAT_IntentionalHitch); -DEFINE_STAT(STAT_PlatformMessageTime); DEFINE_STAT(STAT_FrameSyncTime); DEFINE_STAT(STAT_DeferredTickTime); diff --git a/Engine/Source/Runtime/Engine/Private/UnrealExporter.cpp b/Engine/Source/Runtime/Engine/Private/UnrealExporter.cpp index 2eb4951a4cc0..472530b19377 100644 --- a/Engine/Source/Runtime/Engine/Private/UnrealExporter.cpp +++ b/Engine/Source/Runtime/Engine/Private/UnrealExporter.cpp @@ -519,7 +519,7 @@ void UExporter::EmitEndObject( FOutputDevice& Ar ) FExportObjectInnerContext::FExportObjectInnerContext() { // For each object . . . - for (UObject* InnerObj : TObjectRange(RF_ClassDefaultObject | RF_PendingKill)) + for (UObject* InnerObj : TObjectRange(RF_ClassDefaultObject, true, EInternalObjectFlags::PendingKill)) { UObject* OuterObj = InnerObj->GetOuter(); if ( OuterObj ) @@ -544,7 +544,7 @@ FExportObjectInnerContext::FExportObjectInnerContext() FExportObjectInnerContext::FExportObjectInnerContext(TArray& ObjsToIgnore) { // For each object . . . - for (UObject* InnerObj : TObjectRange(RF_ClassDefaultObject | RF_PendingKill)) + for (UObject* InnerObj : TObjectRange(RF_ClassDefaultObject, true, EInternalObjectFlags::PendingKill)) { if (!ObjsToIgnore.Contains(InnerObj)) { @@ -583,7 +583,7 @@ void UExporter::ExportObjectInner(const FExportObjectInnerContext* Context, UObj else { // NOTE: We ignore inner objects that have been tagged for death - GetObjectsWithOuter(Object, TempInners, false, RF_PendingKill); + GetObjectsWithOuter(Object, TempInners, false, RF_NoFlags, EInternalObjectFlags::PendingKill); } FExportObjectInnerContext::InnerList const& UnsortedObjectInners = ContextInners ? *ContextInners : TempInners; diff --git a/Engine/Source/Runtime/Engine/Private/UserInterface/Canvas.cpp b/Engine/Source/Runtime/Engine/Private/UserInterface/Canvas.cpp index b32d530917e2..a2cb1b25626f 100644 --- a/Engine/Source/Runtime/Engine/Private/UserInterface/Canvas.cpp +++ b/Engine/Source/Runtime/Engine/Private/UserInterface/Canvas.cpp @@ -1281,7 +1281,7 @@ void UCanvas::UpdateSafeZoneData() CachedDisplayHeight = UnsafeSizeY; SafeZonePadX = (CachedDisplayWidth - (CachedDisplayWidth * SafeRegionPercentage.X))/2.f; - SafeZonePadY = CachedDisplayHeight - (CachedDisplayHeight * SafeRegionPercentage.Y)/2.f; + SafeZonePadY = (CachedDisplayHeight - (CachedDisplayHeight * SafeRegionPercentage.Y))/2.f; } else if(FSlateApplication::IsInitialized()) { diff --git a/Engine/Source/Runtime/Engine/Private/World.cpp b/Engine/Source/Runtime/Engine/Private/World.cpp index 98cc1d3b8d0d..7068f6f809ef 100644 --- a/Engine/Source/Runtime/Engine/Private/World.cpp +++ b/Engine/Source/Runtime/Engine/Private/World.cpp @@ -1134,7 +1134,7 @@ void UWorld::MarkObjectsPendingKill() { Object->MarkPendingKill(); }; - ForEachObjectWithOuter(this, MarkObjectPendingKill, true, RF_PendingKill); + ForEachObjectWithOuter(this, MarkObjectPendingKill, true, RF_NoFlags, EInternalObjectFlags::PendingKill); } UWorld* UWorld::CreateWorld(const EWorldType::Type InWorldType, bool bInformEngineOfWorld, FName WorldName, UPackage* InWorldPackage, bool bAddToRoot, ERHIFeatureLevel::Type InFeatureLevel) @@ -1706,7 +1706,7 @@ void UWorld::AddToWorld( ULevel* Level, const FTransform& LevelTransform ) check(Level); check(!Level->IsPendingKill()); - check(!Level->HasAnyFlags(RF_Unreachable)); + check(!Level->IsUnreachable()); FScopeCycleCounterUObject ContextScope(Level); @@ -1981,7 +1981,7 @@ void UWorld::RemoveFromWorld( ULevel* Level ) FScopeCycleCounterUObject Context(Level); check(Level); check(!Level->IsPendingKill()); - check(!Level->HasAnyFlags(RF_Unreachable)); + check(!Level->IsUnreachable()); if (CurrentLevelPendingVisibility == NULL && Level->bIsVisible) { @@ -4656,7 +4656,7 @@ void FSeamlessTravelHandler::StartLoadingDestination() { PackageFlags |= PKG_PlayInEditor; } - UPackage* EditorLevelPackage = (UPackage*)StaticFindObjectFast(UPackage::StaticClass(), NULL, URLMapFName, 0, 0, RF_PendingKill); + UPackage* EditorLevelPackage = (UPackage*)StaticFindObjectFast(UPackage::StaticClass(), NULL, URLMapFName, 0, 0, RF_NoFlags, EInternalObjectFlags::PendingKill); if (EditorLevelPackage) { PIEInstanceID = WorldContext.PIEInstance; diff --git a/Engine/Source/Runtime/Engine/Public/ComponentRecreateRenderStateContext.h b/Engine/Source/Runtime/Engine/Public/ComponentRecreateRenderStateContext.h index ef74e6880d48..ab4a5744db63 100644 --- a/Engine/Source/Runtime/Engine/Public/ComponentRecreateRenderStateContext.h +++ b/Engine/Source/Runtime/Engine/Public/ComponentRecreateRenderStateContext.h @@ -15,7 +15,7 @@ public: FComponentRecreateRenderStateContext(UActorComponent* InComponent) { check(InComponent); - checkf(!InComponent->HasAnyFlags(RF_Unreachable), TEXT("%s"), *InComponent->GetFullName()); + checkf(!InComponent->IsUnreachable(), TEXT("%s"), *InComponent->GetFullName()); if (InComponent->IsRegistered() && InComponent->IsRenderStateCreated()) { diff --git a/Engine/Source/Runtime/Engine/Public/ComponentReregisterContext.h b/Engine/Source/Runtime/Engine/Public/ComponentReregisterContext.h index 0f6d8470a3b9..dfab8d27d023 100644 --- a/Engine/Source/Runtime/Engine/Public/ComponentReregisterContext.h +++ b/Engine/Source/Runtime/Engine/Public/ComponentReregisterContext.h @@ -20,7 +20,7 @@ protected: UWorld* World = NULL; check(InComponent); - checkf(!InComponent->HasAnyFlags(RF_Unreachable), TEXT("%s"), *InComponent->GetFullName()); + checkf(!InComponent->IsUnreachable(), TEXT("%s"), *InComponent->GetFullName()); if(InComponent->IsRegistered() && InComponent->GetWorld()) { diff --git a/Engine/Source/Runtime/Engine/Public/EngineStats.h b/Engine/Source/Runtime/Engine/Public/EngineStats.h index 5cb05f6a767d..eb6ededd3774 100644 --- a/Engine/Source/Runtime/Engine/Public/EngineStats.h +++ b/Engine/Source/Runtime/Engine/Public/EngineStats.h @@ -26,7 +26,6 @@ DECLARE_CYCLE_STAT_EXTERN(TEXT("RedrawViewports"),STAT_RedrawViewports,STATGROUP DECLARE_CYCLE_STAT_EXTERN(TEXT("Update Level Streaming"),STAT_UpdateLevelStreaming,STATGROUP_Engine, ENGINE_API); DECLARE_CYCLE_STAT_EXTERN(TEXT("RHI Game Tick"),STAT_RHITickTime,STATGROUP_Engine, ENGINE_API); DECLARE_CYCLE_STAT_EXTERN(TEXT("Debug Hitch"),STAT_IntentionalHitch,STATGROUP_Engine, ); -DECLARE_CYCLE_STAT_EXTERN(TEXT("Platform Message Time"),STAT_PlatformMessageTime,STATGROUP_Engine, ENGINE_API); DECLARE_CYCLE_STAT_EXTERN(TEXT("Frame Sync Time"),STAT_FrameSyncTime,STATGROUP_Engine, ENGINE_API); DECLARE_CYCLE_STAT_EXTERN(TEXT("Deferred Tick Time"),STAT_DeferredTickTime,STATGROUP_Engine, ENGINE_API); diff --git a/Engine/Source/Runtime/Engine/Public/EngineUtils.h b/Engine/Source/Runtime/Engine/Public/EngineUtils.h index 7402ae7328c2..2d365f17f12c 100644 --- a/Engine/Source/Runtime/Engine/Public/EngineUtils.h +++ b/Engine/Source/Runtime/Engine/Public/EngineUtils.h @@ -178,8 +178,8 @@ public: { check(IsInGameThread()); check(CurrentWorld); - EObjectFlags ExcludeFlags = RF_ClassDefaultObject|RF_PendingKill; - GetObjectsOfClass(InClass, ObjectArray, true, ExcludeFlags); + EObjectFlags ExcludeFlags = RF_ClassDefaultObject; + GetObjectsOfClass(InClass, ObjectArray, true, ExcludeFlags, EInternalObjectFlags::PendingKill); auto ActorSpawnedDelegate = FOnActorSpawned::FDelegate::CreateRaw(this, &FActorIteratorState::OnActorSpawned); ActorSpawnedDelegateHandle = CurrentWorld->AddOnActorSpawnedHandler(ActorSpawnedDelegate); @@ -198,7 +198,7 @@ public: FORCEINLINE AActor* GetActorChecked() const { check(CurrentActor); - checkf(!CurrentActor->HasAnyFlags(RF_Unreachable), TEXT("%s"), *CurrentActor->GetFullName()); + checkf(!CurrentActor->IsUnreachable(), TEXT("%s"), *CurrentActor->GetFullName()); return CurrentActor; } diff --git a/Engine/Source/Runtime/Engine/Public/Model.h b/Engine/Source/Runtime/Engine/Public/Model.h index 38350e7e25c7..6eaea422f059 100644 --- a/Engine/Source/Runtime/Engine/Public/Model.h +++ b/Engine/Source/Runtime/Engine/Public/Model.h @@ -344,7 +344,7 @@ enum {MAX_POINTS = 128000}; class UModel : public UObject { #if WITH_HOT_RELOAD_CTORS - DECLARE_CASTED_CLASS_INTRINSIC_NO_CTOR_NO_VTABLE_CTOR(UModel, UObject, 0, Engine, 0, ENGINE_API) + DECLARE_CASTED_CLASS_INTRINSIC_NO_CTOR_NO_VTABLE_CTOR(UModel, UObject, 0, TEXT("/Script/Engine"), 0, ENGINE_API) /** DO NOT USE. This constructor is for internal usage only for hot-reload purposes. */ UModel(FVTableHelper& Helper); diff --git a/Engine/Source/Runtime/Engine/Public/StaticMeshResources.h b/Engine/Source/Runtime/Engine/Public/StaticMeshResources.h index c3d95d44e035..b55f748565e0 100644 --- a/Engine/Source/Runtime/Engine/Public/StaticMeshResources.h +++ b/Engine/Source/Runtime/Engine/Public/StaticMeshResources.h @@ -705,7 +705,7 @@ public: { if ( It->StaticMesh == InStaticMesh ) { - checkf( !It->HasAnyFlags(RF_Unreachable), TEXT("%s"), *It->GetFullName() ); + checkf( !It->IsUnreachable(), TEXT("%s"), *It->GetFullName() ); if ( It->bRenderStateCreated ) { diff --git a/Engine/Source/Runtime/Foliage/Private/InstancedFoliage.cpp b/Engine/Source/Runtime/Foliage/Private/InstancedFoliage.cpp index 8062f759bd7c..0068d3ac633e 100644 --- a/Engine/Source/Runtime/Foliage/Private/InstancedFoliage.cpp +++ b/Engine/Source/Runtime/Foliage/Private/InstancedFoliage.cpp @@ -531,7 +531,7 @@ void UFoliageType::PostEditChangeProperty(struct FPropertyChangedEvent& Property // Notify any currently-loaded InstancedFoliageActors if (IsFoliageReallocationRequiredForPropertyChange(PropertyChangedEvent)) { - for (TObjectIterator It(RF_ClassDefaultObject | RF_PendingKill); It; ++It) + for (TObjectIterator It(RF_ClassDefaultObject, /** bIncludeDerivedClasses */ true, /** InternalExcludeFalgs */ EInternalObjectFlags::PendingKill); It; ++It) { It->NotifyFoliageTypeChanged(this, bMeshChanged); } diff --git a/Engine/Source/Runtime/GameplayAbilities/Private/AbilitySystemComponent.cpp b/Engine/Source/Runtime/GameplayAbilities/Private/AbilitySystemComponent.cpp index 73c8956bf1f9..06a56f0a7d53 100644 --- a/Engine/Source/Runtime/GameplayAbilities/Private/AbilitySystemComponent.cpp +++ b/Engine/Source/Runtime/GameplayAbilities/Private/AbilitySystemComponent.cpp @@ -1284,7 +1284,7 @@ bool UAbilitySystemComponent::ReplicateSubobjects(class UActorChannel *Channel, for (UGameplayAbility* Ability : AllReplicatedInstancedAbilities) { - if (Ability && !Ability->HasAnyFlags(RF_PendingKill)) + if (Ability && !Ability->IsPendingKill()) { WroteSomething |= Channel->ReplicateSubobject(Ability, *Bunch, *RepFlags); } diff --git a/Engine/Source/Runtime/GameplayAbilities/Private/AbilitySystemComponent_Abilities.cpp b/Engine/Source/Runtime/GameplayAbilities/Private/AbilitySystemComponent_Abilities.cpp index 697e06a3c5f5..b3fe32697dbc 100644 --- a/Engine/Source/Runtime/GameplayAbilities/Private/AbilitySystemComponent_Abilities.cpp +++ b/Engine/Source/Runtime/GameplayAbilities/Private/AbilitySystemComponent_Abilities.cpp @@ -27,7 +27,7 @@ void UAbilitySystemComponent::InitializeComponent() InitAbilityActorInfo(Owner, Owner); // Default init to our outer owner TArray ChildObjects; - GetObjectsWithOuter(Owner, ChildObjects, false, RF_PendingKill); + GetObjectsWithOuter(Owner, ChildObjects, false, RF_NoFlags, EInternalObjectFlags::PendingKill); for (UObject* Obj : ChildObjects) { UAttributeSet* Set = Cast(Obj); diff --git a/Engine/Source/Runtime/GameplayAbilities/Private/GameplayAbilitiesModule.cpp b/Engine/Source/Runtime/GameplayAbilities/Private/GameplayAbilitiesModule.cpp index a7b72d7602e9..cc4626af9a4b 100644 --- a/Engine/Source/Runtime/GameplayAbilities/Private/GameplayAbilitiesModule.cpp +++ b/Engine/Source/Runtime/GameplayAbilities/Private/GameplayAbilitiesModule.cpp @@ -23,7 +23,8 @@ class FGameplayAbilitiesModule : public IGameplayAbilitiesModule checkf(SingletonClass != NULL, TEXT("Ability config value AbilitySystemGlobalsClassName is not a valid class name.")); - AbilitySystemGlobals = NewObject(GetTransientPackage(), SingletonClass, NAME_None, RF_RootSet); + AbilitySystemGlobals = NewObject(GetTransientPackage(), SingletonClass, NAME_None); + AbilitySystemGlobals->AddToRoot(); } check(AbilitySystemGlobals); diff --git a/Engine/Source/Runtime/GameplayTags/Private/GameplayTagsModule.cpp b/Engine/Source/Runtime/GameplayTags/Private/GameplayTagsModule.cpp index 828cdc72093a..9eb5d7826a1e 100644 --- a/Engine/Source/Runtime/GameplayTags/Private/GameplayTagsModule.cpp +++ b/Engine/Source/Runtime/GameplayTags/Private/GameplayTagsModule.cpp @@ -16,7 +16,8 @@ DEFINE_LOG_CATEGORY(LogGameplayTags); void FGameplayTagsModule::StartupModule() { // This code will execute after your module is loaded into memory (but after global variables are initialized, of course.) - GGameplayTagsManager = NewObject(GetTransientPackage(), NAME_None, RF_RootSet); + GGameplayTagsManager = NewObject(GetTransientPackage(), NAME_None); + GGameplayTagsManager->AddToRoot(); TArray GameplayTagTables; GConfig->GetArray(TEXT("GameplayTags"), TEXT("GameplayTagTableList"), GameplayTagTables, GEngineIni); diff --git a/Engine/Source/Runtime/GameplayTasks/Private/GameplayTasksComponent.cpp b/Engine/Source/Runtime/GameplayTasks/Private/GameplayTasksComponent.cpp index b9c09145e7ca..37c943ac9c9c 100644 --- a/Engine/Source/Runtime/GameplayTasks/Private/GameplayTasksComponent.cpp +++ b/Engine/Source/Runtime/GameplayTasks/Private/GameplayTasksComponent.cpp @@ -91,7 +91,7 @@ bool UGameplayTasksComponent::ReplicateSubobjects(UActorChannel* Channel, class { for (UGameplayTask* SimulatedTask : SimulatedTasks) { - if (SimulatedTask && !SimulatedTask->HasAnyFlags(RF_PendingKill)) + if (SimulatedTask && !SimulatedTask->IsPendingKill()) { WroteSomething |= Channel->ReplicateSubobject(SimulatedTask, *Bunch, *RepFlags); } diff --git a/Engine/Source/Runtime/Landscape/Private/Landscape.cpp b/Engine/Source/Runtime/Landscape/Private/Landscape.cpp index 5dd02e1ebfd3..5a2444c34a2d 100644 --- a/Engine/Source/Runtime/Landscape/Private/Landscape.cpp +++ b/Engine/Source/Runtime/Landscape/Private/Landscape.cpp @@ -2279,7 +2279,8 @@ bool LandscapeMaterialsParameterSetUpdater(FStaticParameterSet &StaticParameterS void ALandscapeProxy::Tick(float DeltaSeconds) { - if (!IsPendingKillPending() && !HasAnyFlags(RF_NeedLoad | RF_NeedPostLoad | RF_NeedPostLoadSubobjects | RF_Unreachable | RF_PendingKill | RF_ClassDefaultObject | RF_AsyncLoading)) + if (!IsPendingKillPending() && !HasAnyFlags(RF_NeedLoad | RF_NeedPostLoad | RF_NeedPostLoadSubobjects | RF_ClassDefaultObject) && + !HasAnyInternalFlags(EInternalObjectFlags::PendingKill | EInternalObjectFlags::AsyncLoading | EInternalObjectFlags::Unreachable)) { // this is NOT an actor tick, it is a FTickableGameObject tick // the super tick is for an actor tick... diff --git a/Engine/Source/Runtime/Landscape/Private/LandscapeSplines.cpp b/Engine/Source/Runtime/Landscape/Private/LandscapeSplines.cpp index 5da967e6a527..68598760ce09 100644 --- a/Engine/Source/Runtime/Landscape/Private/LandscapeSplines.cpp +++ b/Engine/Source/Runtime/Landscape/Private/LandscapeSplines.cpp @@ -1723,7 +1723,8 @@ void ULandscapeSplineSegment::PostInitProperties() Super::PostInitProperties(); #if WITH_EDITORONLY_DATA - if (!HasAnyFlags(RF_ClassDefaultObject | RF_NeedLoad | RF_AsyncLoading)) + if (!HasAnyFlags(RF_ClassDefaultObject | RF_NeedLoad) && + !HasAnyInternalFlags(EInternalObjectFlags::AsyncLoading)) { // create a new random seed for all new objects RandomSeed = FMath::Rand(); diff --git a/Engine/Source/Runtime/Launch/Private/LaunchEngineLoop.cpp b/Engine/Source/Runtime/Launch/Private/LaunchEngineLoop.cpp index f421c21fb284..2d6f67ee8b0f 100644 --- a/Engine/Source/Runtime/Launch/Private/LaunchEngineLoop.cpp +++ b/Engine/Source/Runtime/Launch/Private/LaunchEngineLoop.cpp @@ -2514,9 +2514,7 @@ void FEngineLoop::Tick() // early in the Tick() to get the callbacks for cvar changes called { -#if WITH_ENGINE QUICK_SCOPE_CYCLE_COUNTER(STAT_FEngineLoop_Tick_CallAllConsoleVariableSinks); -#endif IConsoleManager::Get().CallAllConsoleVariableSinks(); } @@ -2533,9 +2531,7 @@ void FEngineLoop::Tick() }); { -#if WITH_ENGINE QUICK_SCOPE_CYCLE_COUNTER(STAT_FEngineLoop_FlushThreadedLogs); -#endif // Flush debug output which has been buffered by other threads. GLog->FlushThreadedLogs(); } @@ -2549,25 +2545,19 @@ void FEngineLoop::Tick() } { -#if WITH_ENGINE QUICK_SCOPE_CYCLE_COUNTER(STAT_FEngineLoop_UpdateTimeAndHandleMaxTickRate); -#endif // Set FApp::CurrentTime, FApp::DeltaTime and potentially wait to enforce max tick rate. GEngine->UpdateTimeAndHandleMaxTickRate(); } { -#if WITH_ENGINE QUICK_SCOPE_CYCLE_COUNTER(STAT_FEngineLoop_TickFPSChart); -#endif GEngine->TickFPSChart( FApp::GetDeltaTime() ); } -#if WITH_ENGINE QUICK_SCOPE_CYCLE_COUNTER(STAT_FEngineLoop_Malloc_UpdateStats); -#endif - // Update platform memory and memory allocator stats. - FPlatformMemory::UpdateStats(); + + // Update memory allocator stats. GMalloc->UpdateStats(); } @@ -2591,9 +2581,6 @@ void FEngineLoop::Tick() }); { -#if WITH_ENGINE - SCOPE_CYCLE_COUNTER( STAT_PlatformMessageTime ); -#endif SCOPE_CYCLE_COUNTER(STAT_PumpMessages); FPlatformMisc::PumpMessages(true); } @@ -2601,9 +2588,8 @@ void FEngineLoop::Tick() bool bIdleMode; { -#if WITH_ENGINE QUICK_SCOPE_CYCLE_COUNTER(STAT_FEngineLoop_Idle); -#endif + // Idle mode prevents ticking and rendering completely bIdleMode = ShouldUseIdleMode(); if (bIdleMode) @@ -2615,9 +2601,8 @@ void FEngineLoop::Tick() if (FSlateApplication::IsInitialized() && !bIdleMode) { -#if WITH_ENGINE QUICK_SCOPE_CYCLE_COUNTER(STAT_FEngineLoop_Tick_SlateInput); -#endif + FSlateApplication& SlateApp = FSlateApplication::Get(); SlateApp.PollGameDeviceState(); // Gives widgets a chance to process any accumulated input @@ -2630,35 +2615,28 @@ void FEngineLoop::Tick() // wait for it to finish before we continue to tick or tick again // We do this right after GEngine->Tick() because that is where user code would initiate a load / movie. { -#if WITH_ENGINE QUICK_SCOPE_CYCLE_COUNTER(STAT_FEngineLoop_WaitForMovieToFinish); -#endif + GetMoviePlayer()->WaitForMovieToFinish(); } if (GShaderCompilingManager) { // Process any asynchronous shader compile results that are ready, limit execution time -#if WITH_ENGINE QUICK_SCOPE_CYCLE_COUNTER(STAT_FEngineLoop_Tick_GShaderCompilingManager); -#endif GShaderCompilingManager->ProcessAsyncResults(true, false); } if (GDistanceFieldAsyncQueue) { -#if WITH_ENGINE QUICK_SCOPE_CYCLE_COUNTER(STAT_FEngineLoop_Tick_GDistanceFieldAsyncQueue); -#endif GDistanceFieldAsyncQueue->ProcessAsyncTasks(); } if (FSlateApplication::IsInitialized() && !bIdleMode) { { -#if WITH_ENGINE QUICK_SCOPE_CYCLE_COUNTER(STAT_FEngineLoop_ProcessPlayerControllersSlateOperations); -#endif check(!IsRunningDedicatedServer()); // Process slate operations accumulated in the world ticks. @@ -2677,9 +2655,7 @@ void FEngineLoop::Tick() #if WITH_EDITOR { -#if WITH_ENGINE QUICK_SCOPE_CYCLE_COUNTER( STAT_FEngineLoop_Tick_AutomationController ); -#endif static FName AutomationController( "AutomationController" ); //Check if module loaded to support the change to allow this to be hot compilable. if (FModuleManager::Get().IsModuleLoaded( AutomationController )) @@ -2692,9 +2668,7 @@ void FEngineLoop::Tick() #if WITH_ENGINE #if WITH_AUTOMATION_WORKER { -#if WITH_ENGINE QUICK_SCOPE_CYCLE_COUNTER(STAT_FEngineLoop_Tick_AutomationWorker); -#endif //Check if module loaded to support the change to allow this to be hot compilable. static const FName AutomationWorkerModuleName = TEXT("AutomationWorker"); if (FModuleManager::Get().IsModuleLoaded(AutomationWorkerModuleName)) @@ -2706,9 +2680,7 @@ void FEngineLoop::Tick() #endif //WITH_ENGINE { -#if WITH_ENGINE SCOPE_CYCLE_COUNTER(STAT_RHITickTime); -#endif RHITick( FApp::GetDeltaTime() ); // Update RHI. } @@ -2727,9 +2699,7 @@ void FEngineLoop::Tick() PendingCleanupObjects = GetPendingCleanupObjects(); { -#if WITH_ENGINE SCOPE_CYCLE_COUNTER( STAT_FrameSyncTime ); -#endif // this could be perhaps moved down to get greater parallelizm // Sync game and render thread. Either total sync or allowing one frame lag. static FFrameEndSync FrameEndSync; @@ -2738,10 +2708,7 @@ void FEngineLoop::Tick() } { -#if WITH_ENGINE SCOPE_CYCLE_COUNTER( STAT_DeferredTickTime ); -#endif - // Delete the objects which were enqueued for deferred cleanup before the previous frame. delete PreviousPendingCleanupObjects; diff --git a/Engine/Source/Runtime/Online/OnlineSubsystem/Public/OnlineJsonSerializer.h b/Engine/Source/Runtime/Online/OnlineSubsystem/Public/OnlineJsonSerializer.h index 6b231c90a5e6..4eccfd3ef7ea 100644 --- a/Engine/Source/Runtime/Online/OnlineSubsystem/Public/OnlineJsonSerializer.h +++ b/Engine/Source/Runtime/Online/OnlineSubsystem/Public/OnlineJsonSerializer.h @@ -77,7 +77,25 @@ } #define ONLINE_JSON_SERIALIZE_OBJECT_SERIALIZABLE(JsonName, JsonSerializableObject) \ - Serializer.SerializeObject(TEXT(JsonName), JsonSerializableObject); + /* Process the JsonName field differently because it is an object */ \ + if (Serializer.IsLoading()) \ + { \ + /* Read in the value from the JsonName field */ \ + if (Serializer.GetObject()->HasTypedField(JsonName)) \ + { \ + TSharedPtr JsonObj = Serializer.GetObject()->GetObjectField(JsonName); \ + if (JsonObj.IsValid()) \ + { \ + JsonSerializableObject.FromJson(JsonObj); \ + } \ + } \ + } \ + else \ + { \ + /* Write the value to the Name field */ \ + Serializer.WriteIdentifierPrefix(TEXT(JsonName)); \ + JsonSerializableObject.Serialize(Serializer, true); \ + } /** Array of string data */ typedef TArray FJsonSerializableArray; @@ -87,8 +105,6 @@ typedef FOnlineKeyValuePairs FJsonSerializableKeyValueMap; typedef FOnlineKeyValuePairs FJsonSerializableKeyValueMapInt; typedef FOnlineKeyValuePairs FJsonSerializeableKeyValueMapVariant; -struct FOnlineJsonSerializable; - /** * Base interface used to serialize to/from JSON. Hides the fact there are separate read/write classes */ @@ -110,12 +126,12 @@ struct FOnlineJsonSerializerBase virtual void Serialize(const TCHAR* Name, float& Value) = 0; virtual void Serialize(const TCHAR* Name, double& Value) = 0; virtual void Serialize(const TCHAR* Name, FDateTime& Value) = 0; - virtual void SerializeObject(const TCHAR* Name, FOnlineJsonSerializable& Value) = 0; virtual void SerializeArray(FJsonSerializableArray& Array) = 0; virtual void SerializeArray(const TCHAR* Name, FJsonSerializableArray& Value) = 0; virtual void SerializeMap(const TCHAR* Name, FJsonSerializableKeyValueMap& Map) = 0; virtual void SerializeMap(const TCHAR* Name, FJsonSerializableKeyValueMapInt& Map) = 0; virtual TSharedPtr GetObject() = 0; + virtual void WriteIdentifierPrefix(const TCHAR* Name) = 0; }; /** @@ -267,13 +283,6 @@ public: JsonWriter->WriteValue(Name, Value.ToIso8601()); } } - /** - * Writes the field name and corresponding object value to the JSON data - * - * @param Name the field name to write out - * @param Object the object to write out - */ - virtual void SerializeObject(const TCHAR* Name, FOnlineJsonSerializable& Object) override; /** * Serializes an array of values * @@ -338,6 +347,11 @@ public: } JsonWriter->WriteObjectEnd(); } + + virtual void WriteIdentifierPrefix(const TCHAR* Name) + { + JsonWriter->WriteIdentifierPrefix(Name); + } }; /** @@ -501,13 +515,6 @@ public: FDateTime::ParseIso8601(*JsonObject->GetStringField(Name), Value); } } - /** - * Writes the field name and the corresponding object value to the JSON data - * - * @param Name the field name to write out - * @Object Value the object to write out - */ - virtual void SerializeObject(const TCHAR* Name, FOnlineJsonSerializable& Object) override; /** * Serializes an array of values * @@ -576,6 +583,12 @@ public: } } } + + virtual void WriteIdentifierPrefix(const TCHAR* Name) + { + // Should never be called on a reader + check(false); + } }; /** @@ -666,24 +679,3 @@ struct FOnlineJsonSerializable */ virtual void Serialize(FOnlineJsonSerializerBase& Serializer, bool bFlatObject) = 0; }; - -inline void FOnlineJsonSerializerReader::SerializeObject(const TCHAR* Name, FOnlineJsonSerializable& Value) -{ - /* Read in the value from the Name field */ - if (GetObject()->HasTypedField(Name)) - { - TSharedPtr JsonObj = GetObject()->GetObjectField(Name); - if (JsonObj.IsValid()) - { - Value.FromJson(JsonObj); - } - } -} - -template -inline void FOnlineJsonSerializerWriter::SerializeObject(const TCHAR* Name, FOnlineJsonSerializable& Object) -{ - /* Write the value to the Name field */ - JsonWriter->WriteIdentifierPrefix(Name); - Object.Serialize(*this, true); -} diff --git a/Engine/Source/Runtime/Online/OnlineSubsystemSteam/Private/VoiceInterfaceSteam.cpp b/Engine/Source/Runtime/Online/OnlineSubsystemSteam/Private/VoiceInterfaceSteam.cpp index 841e81807053..a44ff038baa5 100644 --- a/Engine/Source/Runtime/Online/OnlineSubsystemSteam/Private/VoiceInterfaceSteam.cpp +++ b/Engine/Source/Runtime/Online/OnlineSubsystemSteam/Private/VoiceInterfaceSteam.cpp @@ -113,7 +113,7 @@ void FOnlineVoiceSteam::Tick(float DeltaTime) void FOnlineVoiceSteam::StartNetworkedVoice(uint8 LocalUserNum) { // Validate the range of the entry - if (LocalUserNum >= 0 && LocalUserNum < MAX_LOCAL_PLAYERS) + if (LocalUserNum < MAX_LOCAL_PLAYERS) { LocalTalkers[LocalUserNum].bHasNetworkedVoice = true; UE_LOG(LogVoice, Log, TEXT("Starting networked voice for user: %d"), LocalUserNum); diff --git a/Engine/Source/Runtime/RenderCore/Private/RenderingThread.cpp b/Engine/Source/Runtime/RenderCore/Private/RenderingThread.cpp index f38603bc8189..8ae97f6bc864 100644 --- a/Engine/Source/Runtime/RenderCore/Private/RenderingThread.cpp +++ b/Engine/Source/Runtime/RenderCore/Private/RenderingThread.cpp @@ -333,9 +333,7 @@ static void AdvanceRenderingThreadStats(int64 StatsFrame, int32 MasterDisableCha { Frame = -StatsFrame; // mark this as a bad frame } - // @TODO yrx 2014-10-17 Add AddAdvanceFrame message - static FStatNameAndInfo Adv(NAME_AdvanceFrame, "", "", TEXT(""), EStatDataType::ST_int64, true, false); - FThreadStats::AddMessage(Adv.GetEncodedName(), EStatOperation::AdvanceFrameEventRenderThread, Frame); + FThreadStats::AddMessage(FStatConstants::AdvanceFrame.GetEncodedName(), EStatOperation::AdvanceFrameEventRenderThread, Frame); if( IsInActualRenderingThread() ) { FThreadStats::ExplicitFlush(); diff --git a/Engine/Source/Runtime/RenderCore/Public/RenderingThread.h b/Engine/Source/Runtime/RenderCore/Public/RenderingThread.h index 683a627a7204..c857ec9935d9 100644 --- a/Engine/Source/Runtime/RenderCore/Public/RenderingThread.h +++ b/Engine/Source/Runtime/RenderCore/Public/RenderingThread.h @@ -151,6 +151,7 @@ public: // All render commands run on the render thread static ENamedThreads::Type GetDesiredThread() { + check(!GIsThreadedRendering || ENamedThreads::RenderThread != ENamedThreads::GameThread); return ENamedThreads::RenderThread; } diff --git a/Engine/Source/Runtime/Renderer/Private/RendererScene.cpp b/Engine/Source/Runtime/Renderer/Private/RendererScene.cpp index 499314b7d8a8..9767b884c838 100644 --- a/Engine/Source/Runtime/Renderer/Private/RendererScene.cpp +++ b/Engine/Source/Runtime/Renderer/Private/RendererScene.cpp @@ -476,7 +476,7 @@ void FScene::AddPrimitive(UPrimitiveComponent* Primitive) { SCOPE_CYCLE_COUNTER(STAT_AddScenePrimitiveGT); - checkf(!Primitive->HasAnyFlags(RF_Unreachable), TEXT("%s"), *Primitive->GetFullName()); + checkf(!Primitive->IsUnreachable(), TEXT("%s"), *Primitive->GetFullName()); // Save the world transform for next time the primitive is added to the scene diff --git a/Engine/Source/Runtime/SlateCore/Public/Brushes/SlateDynamicImageBrush.h b/Engine/Source/Runtime/SlateCore/Public/Brushes/SlateDynamicImageBrush.h index 16e738fded8a..fe610f64aa21 100644 --- a/Engine/Source/Runtime/SlateCore/Public/Brushes/SlateDynamicImageBrush.h +++ b/Engine/Source/Runtime/SlateCore/Public/Brushes/SlateDynamicImageBrush.h @@ -110,7 +110,7 @@ private: // @todo Slate - Hack: This is to address an issue where the brush created and a GC occurs before the brush resource object becomes referenced // by the Slate resource manager. Don't add objects that are already in root set (and mark them as such) to avoid incorrect removing objects // from root set in destructor. - if (!ResourceObject->HasAllFlags(RF_RootSet)) + if (!ResourceObject->IsRooted()) { ResourceObject->AddToRoot(); bRemoveResourceFromRootSet = true; diff --git a/Engine/Source/Runtime/UMG/Private/Animation/WidgetAnimation.cpp b/Engine/Source/Runtime/UMG/Private/Animation/WidgetAnimation.cpp index 5db82b244cfc..7c8156942d5a 100644 --- a/Engine/Source/Runtime/UMG/Private/Animation/WidgetAnimation.cpp +++ b/Engine/Source/Runtime/UMG/Private/Animation/WidgetAnimation.cpp @@ -29,8 +29,10 @@ UWidgetAnimation* UWidgetAnimation::GetNullAnimation() if (!NullAnimation) { - NullAnimation = NewObject(GetTransientPackage(), NAME_None, RF_RootSet); - NullAnimation->MovieScene = NewObject(NullAnimation, FName("No Animation"), RF_RootSet); + NullAnimation = NewObject(GetTransientPackage(), NAME_None); + NullAnimation->AddToRoot(); + NullAnimation->MovieScene = NewObject(NullAnimation, FName("No Animation")); + NullAnimation->MovieScene->AddToRoot(); } return NullAnimation; diff --git a/Engine/Source/Runtime/UMG/Public/Binding/DynamicPropertyPath.h b/Engine/Source/Runtime/UMG/Public/Binding/DynamicPropertyPath.h index 0af8f7627bef..f3d0127cf468 100644 --- a/Engine/Source/Runtime/UMG/Public/Binding/DynamicPropertyPath.h +++ b/Engine/Source/Runtime/UMG/Public/Binding/DynamicPropertyPath.h @@ -331,7 +331,7 @@ private: if ( IsConcreteTypeCompatibleWithReflectedType(ReturnProperty) ) { // Ensure that the element sizes are the same, prevents the user from doing something terribly wrong. - if ( ReturnProperty->ElementSize == sizeof(T) && !ContainerObject->HasAnyFlags( RF_Unreachable ) ) + if ( ReturnProperty->ElementSize == sizeof(T) && !ContainerObject->IsUnreachable() ) { ContainerObject->ProcessEvent(Function, &OutValue); return true; diff --git a/Engine/Source/Runtime/UMG/Public/Components/Widget.h b/Engine/Source/Runtime/UMG/Public/Components/Widget.h index 743f39d12d1d..ef1bb1f12e2b 100644 --- a/Engine/Source/Runtime/UMG/Public/Components/Widget.h +++ b/Engine/Source/Runtime/UMG/Public/Components/Widget.h @@ -459,10 +459,10 @@ public: #if WITH_EDITOR FORCEINLINE bool CanSafelyRouteEvent() { - return !(IsDesignTime() || GIntraFrameDebuggingGameThread || HasAnyFlags(RF_Unreachable) || FUObjectThreadContext::Get().IsRoutingPostLoad); + return !(IsDesignTime() || GIntraFrameDebuggingGameThread || IsUnreachable() || FUObjectThreadContext::Get().IsRoutingPostLoad); } #else - FORCEINLINE bool CanSafelyRouteEvent() { return !(HasAnyFlags(RF_Unreachable) || FUObjectThreadContext::Get().IsRoutingPostLoad); } + FORCEINLINE bool CanSafelyRouteEvent() { return !(IsUnreachable() || FUObjectThreadContext::Get().IsRoutingPostLoad); } #endif #if WITH_EDITOR diff --git a/Engine/Source/ThirdParty/libJPG/jpgd.cpp b/Engine/Source/ThirdParty/libJPG/jpgd.cpp index 44baefe2e5b9..28ab81f78d30 100644 --- a/Engine/Source/ThirdParty/libJPG/jpgd.cpp +++ b/Engine/Source/ThirdParty/libJPG/jpgd.cpp @@ -566,7 +566,7 @@ namespace jpgd { // Tables and macro used to fully decode the DPCM differences. static const int s_extend_test[16] = { 0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000 }; - static const int s_extend_offset[16] = { 0, ((-1)<<1) + 1, ((-1)<<2) + 1, ((-1)<<3) + 1, ((-1)<<4) + 1, ((-1)<<5) + 1, ((-1)<<6) + 1, ((-1)<<7) + 1, ((-1)<<8) + 1, ((-1)<<9) + 1, ((-1)<<10) + 1, ((-1)<<11) + 1, ((-1)<<12) + 1, ((-1)<<13) + 1, ((-1)<<14) + 1, ((-1)<<15) + 1 }; + static const int s_extend_offset[16] = { 0, -1, -3, -7, -15, -31, -63, -127, -255, -511, -1023, -2047, -4095, -8191, -16383, -32767 }; static const int s_extend_mask[] = { 0, (1<<0), (1<<1), (1<<2), (1<<3), (1<<4), (1<<5), (1<<6), (1<<7), (1<<8), (1<<9), (1<<10), (1<<11), (1<<12), (1<<13), (1<<14), (1<<15), (1<<16) }; #define HUFF_EXTEND(x,s) ((x) < s_extend_test[s] ? (x) + s_extend_offset[s] : (x))