// Copyright UnexGames 2025. All Rights Reserved. #include "ThreadUtility.h" #include "Engine/Engine.h" #include "ThreadBase.h" #if WITH_EDITOR #include "Editor.h" #else #include "Misc/CoreDelegates.h" #endif #define LOCTEXT_NAMESPACE "UThreadUtility" #if WITH_EDITOR FDelegateHandle UThreadUtility::EndPIEHandle = FDelegateHandle(); #else FDelegateHandle UThreadUtility::PreExitHandle = FDelegateHandle(); #endif TArray UThreadUtility::RootObjects = {}; int32 UThreadUtility::TaskIndex = 0; int32 UThreadUtility::MutexIndex = 0; int32 UThreadUtility::ThreadPoolIndex = 0; UThreadUtility::UThreadUtility() { #if WITH_EDITOR EndPIEHandle = FEditorDelegates::PrePIEEnded.AddStatic(&UThreadUtility::OnEndPIE); #else PreExitHandle = FCoreDelegates::OnPreExit.AddStatic(&UThreadUtility::OnPreExit); #endif } UThreadUtility::~UThreadUtility() { #if WITH_EDITOR FEditorDelegates::PrePIEEnded.Remove(EndPIEHandle); #else FCoreDelegates::OnPreExit.Remove(PreExitHandle); #endif TaskIndex = 0; MutexIndex = 0; ThreadPoolIndex = 0; } UObject* UThreadUtility::GetContextWorld(UObject* WorldContextObject) { return GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::ReturnNull); } bool UThreadUtility::CanUseAudioThread() { return (FPlatformMisc::AllowAudioThread()); } bool UThreadUtility::IsPrintThreadSafe() { return (FPlatformMisc::IsLocalPrintThreadSafe()); } bool UThreadUtility::IsUseRenderThread() { return (FPlatformMisc::UseRenderThread()); } bool UThreadUtility::CanSupportMultiThreadedFileHandles() { return (FGenericPlatformMisc::SupportsMultithreadedFileHandles()); } void UThreadUtility::CancelTask(UThreadBase* Task) { if (Task != nullptr && Task->IsValidLowLevel() && IsValid(Task) && !Task->IsUnreachable()) { Task->Cancel(); } } void UThreadUtility::WaitToFinish(UThreadBase* Task) { if (Task != nullptr && Task->IsValidLowLevel() && IsValid(Task) && !Task->IsUnreachable()) { Task->WaitToFinish(); } } bool UThreadUtility::IsRunning(UThreadBase* Task) { if (Task != nullptr && Task->IsValidLowLevel() && IsValid(Task) && !Task->IsUnreachable()) { return Task->IsRunning(); } return false; } bool UThreadUtility::IsCanceled(UThreadBase* Task) { if (Task != nullptr && Task->IsValidLowLevel() && IsValid(Task) && !Task->IsUnreachable()) { return Task->IsCanceled(); } return true; } int32 UThreadUtility::MixThreeIntegers(int32 Integer1, int32 Integer2, int32 Integer3) { unsigned long a = (unsigned long)Integer1; unsigned long b = (unsigned long)Integer2; unsigned long c = (unsigned long)Integer3; a = a - b; a = a - c; a = a ^ (c >> 13); b = b - c; b = b - a; b = b ^ (a << 8); c = c - a; c = c - b; c = c ^ (b >> 13); a = a - b; a = a - c; a = a ^ (c >> 12); b = b - c; b = b - a; b = b ^ (a << 16); c = c - a; c = c - b; c = c ^ (b >> 5); a = a - b; a = a - c; a = a ^ (c >> 3); b = b - c; b = b - a; b = b ^ (a << 10); c = c - a; c = c - b; c = c ^ (b >> 15); return (int32)c; } void UThreadUtility::GetRandomScale(const FVector& Min, const FVector& Max, EScaleType Type, const FRandomStream& RandomStream, FVector& Scale) { TFunction Interpolate = [&](float Alpha, int32 Axis) { return Min[Axis] + (Alpha * (Max[Axis] - Min[Axis])); }; Scale = FVector(1.0f); float LockRand = 0.0f; switch (Type) { case EScaleType::Uniform: Scale.X = Interpolate(RandomStream.GetFraction(), 0); Scale.Y = Scale.X; Scale.Z = Scale.X; break; case EScaleType::Free: Scale.X = Interpolate(RandomStream.GetFraction(), 0); Scale.Y = Interpolate(RandomStream.GetFraction(), 1); Scale.Z = Interpolate(RandomStream.GetFraction(), 2); break; case EScaleType::LockXY: LockRand = RandomStream.GetFraction(); Scale.X = Interpolate(LockRand, 0); Scale.Y = Interpolate(LockRand, 1); Scale.Z = Interpolate(RandomStream.GetFraction(), 2); break; case EScaleType::LockXZ: LockRand = RandomStream.GetFraction(); Scale.X = Interpolate(LockRand, 0); Scale.Y = Interpolate(RandomStream.GetFraction(), 1); Scale.Z = Interpolate(LockRand, 2); break; case EScaleType::LockYZ: LockRand = RandomStream.GetFraction(); Scale.X = Interpolate(RandomStream.GetFraction(), 0); Scale.Y = Interpolate(LockRand, 1); Scale.Z = Interpolate(LockRand, 2); break; } } bool UThreadUtility::IsSupportsMultithreading() { return(FPlatformProcess::SupportsMultithreading()); } void UThreadUtility::SetNameToThread(FString ThreadName) { FPlatformProcess::SetThreadName(*ThreadName); } void UThreadUtility::AddToRoot(UObject* Object) { if (Object) { if (!Object->IsRooted()) { if (!RootObjects.Contains(Object)) { RootObjects.Add(Object); Object->AddToRoot(); } } } } void UThreadUtility::RemoveFromRoot(UObject* Object) { if(Object) { if (Object->IsRooted()) { if (RootObjects.Contains(Object)) { RootObjects.Remove(Object); Object->RemoveFromRoot(); } } } } void UThreadUtility::OnEndPIE(const bool bIsSimulating) { for (auto Object : RootObjects) { if (Object != nullptr && Object->IsValidLowLevel() && IsValid(Object) && !Object->IsUnreachable()) { if (Object->IsRooted()) { if (RootObjects.Contains(Object)) { Object->RemoveFromRoot(); } } } } RootObjects.Empty(); } void UThreadUtility::OnPreExit() { for (auto Object : RootObjects) { if (Object) { if (Object->IsRooted()) { if (RootObjects.Contains(Object)) { Object->RemoveFromRoot(); } } } } RootObjects.Empty(); } #undef LOCTEXT_NAMESPACE