You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#rb Ionut.Matasaru Matt Peters #preflight 628b8690693c5e1de2773a39 [CL 20327158 by Catalin Dragoiu in ue5-main branch]
2397 lines
68 KiB
C++
2397 lines
68 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "CookPackageData.h"
|
|
|
|
#include "Algo/AnyOf.h"
|
|
#include "Algo/Count.h"
|
|
#include "Algo/Find.h"
|
|
#include "AssetCompilingManager.h"
|
|
#include "AssetRegistry/IAssetRegistry.h"
|
|
#include "Async/ParallelFor.h"
|
|
#include "Cooker/CookPlatformManager.h"
|
|
#include "Cooker/CookRequestCluster.h"
|
|
#include "CookOnTheSide/CookOnTheFlyServer.h"
|
|
#include "Containers/StringView.h"
|
|
#include "EditorDomain/EditorDomain.h"
|
|
#include "Engine/Console.h"
|
|
#include "HAL/FileManager.h"
|
|
#include "HAL/PlatformTime.h"
|
|
#include "Misc/CommandLine.h"
|
|
#include "Misc/CoreMiscDefines.h"
|
|
#include "Misc/PackageAccessTrackingOps.h"
|
|
#include "Misc/PackageName.h"
|
|
#include "Misc/Parse.h"
|
|
#include "Misc/Paths.h"
|
|
#include "Misc/PreloadableFile.h"
|
|
#include "Misc/ScopeExit.h"
|
|
#include "Misc/ScopeLock.h"
|
|
#include "ShaderCompiler.h"
|
|
#include "UObject/Object.h"
|
|
#include "UObject/Package.h"
|
|
#include "UObject/ReferenceChainSearch.h"
|
|
#include "UObject/UObjectGlobals.h"
|
|
#include "UObject/UObjectHash.h"
|
|
#include "UObject/ObjectRedirector.h"
|
|
|
|
namespace UE::Cook
|
|
{
|
|
|
|
float GPollAsyncPeriod = .100f;
|
|
static FAutoConsoleVariableRef CVarPollAsyncPeriod(
|
|
TEXT("cook.PollAsyncPeriod"),
|
|
GPollAsyncPeriod,
|
|
TEXT("Minimum time in seconds between PollPendingCookedPlatformDatas."),
|
|
ECVF_Default);
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// FPackageData
|
|
FPackageData::FPlatformData::FPlatformData()
|
|
: bRequested(false), bCookAttempted(false), bCookSucceeded(false), bExplored(false), bSaveTimedOut(false)
|
|
{
|
|
}
|
|
|
|
FPackageData::FPackageData(FPackageDatas& PackageDatas, const FName& InPackageName, const FName& InFileName)
|
|
: GeneratedOwner(nullptr), PackageName(InPackageName), FileName(InFileName), PackageDatas(PackageDatas)
|
|
, Instigator(EInstigator::NotYetRequested), bIsUrgent(0)
|
|
, bIsVisited(0), bIsPreloadAttempted(0)
|
|
, bIsPreloaded(0), bHasSaveCache(0), bHasBeginPrepareSaveFailed(0), bCookedPlatformDataStarted(0)
|
|
, bCookedPlatformDataCalled(0), bCookedPlatformDataComplete(0), bMonitorIsCooked(0)
|
|
, bInitializedGeneratorSave(0), bCompletedGeneration(0), bGenerated(0), bKeepReferencedDuringGC(0)
|
|
{
|
|
SetState(EPackageState::Idle);
|
|
SendToState(EPackageState::Idle, ESendFlags::QueueAdd);
|
|
}
|
|
|
|
FPackageData::~FPackageData()
|
|
{
|
|
// ClearReferences should have been called earlier, but call it here in case it was missed
|
|
ClearReferences();
|
|
// We need to send OnLastCookedPlatformRemoved message to the monitor, so call SetPlatformsNotCooked
|
|
ClearCookProgress();
|
|
// Update the monitor's counters and call exit functions
|
|
SendToState(EPackageState::Idle, ESendFlags::QueueNone);
|
|
}
|
|
|
|
void FPackageData::ClearReferences()
|
|
{
|
|
DestroyGeneratorPackage();
|
|
}
|
|
|
|
const FName& FPackageData::GetPackageName() const
|
|
{
|
|
return PackageName;
|
|
}
|
|
|
|
const FName& FPackageData::GetFileName() const
|
|
{
|
|
return FileName;
|
|
}
|
|
|
|
void FPackageData::SetFileName(const FName& InFileName)
|
|
{
|
|
FileName = InFileName;
|
|
}
|
|
|
|
int32 FPackageData::GetNumRequestedPlatforms() const
|
|
{
|
|
int32 Result = 0;
|
|
for (const TPair<const ITargetPlatform*, FPlatformData>& Pair : PlatformDatas)
|
|
{
|
|
Result += Pair.Value.bRequested ? 1 : 0;
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
void FPackageData::SetPlatformsRequested(TConstArrayView<const ITargetPlatform*> TargetPlatforms, bool bRequested)
|
|
{
|
|
for (const ITargetPlatform* TargetPlatform : TargetPlatforms)
|
|
{
|
|
PlatformDatas.FindOrAdd(TargetPlatform).bRequested = true;
|
|
}
|
|
}
|
|
|
|
void FPackageData::ClearRequestedPlatforms()
|
|
{
|
|
for (TPair<const ITargetPlatform*, FPlatformData>& Pair : PlatformDatas)
|
|
{
|
|
Pair.Value.bRequested = false;
|
|
}
|
|
}
|
|
|
|
bool FPackageData::HasAllRequestedPlatforms(const TArrayView<const ITargetPlatform* const>& Platforms) const
|
|
{
|
|
if (Platforms.Num() == 0)
|
|
{
|
|
return true;
|
|
}
|
|
if (PlatformDatas.Num() == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (const ITargetPlatform* QueryPlatform : Platforms)
|
|
{
|
|
const FPlatformData* PlatformData = PlatformDatas.Find(QueryPlatform);
|
|
if (!PlatformData || !PlatformData->bRequested)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool FPackageData::AreAllRequestedPlatformsCooked(bool bAllowFailedCooks) const
|
|
{
|
|
for (const TPair<const ITargetPlatform*, FPlatformData>& Pair : PlatformDatas)
|
|
{
|
|
if (Pair.Value.bRequested && (!Pair.Value.bCookAttempted || (!bAllowFailedCooks && !Pair.Value.bCookSucceeded)))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool FPackageData::AreAllRequestedPlatformsExplored() const
|
|
{
|
|
for (const TPair<const ITargetPlatform*, FPlatformData>& Pair : PlatformDatas)
|
|
{
|
|
if (Pair.Value.bRequested && !Pair.Value.bExplored)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool FPackageData::HasAllExploredPlatforms(const TArrayView<const ITargetPlatform* const>& Platforms) const
|
|
{
|
|
if (Platforms.Num() == 0)
|
|
{
|
|
return true;
|
|
}
|
|
if (PlatformDatas.Num() == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (const ITargetPlatform* QueryPlatform : Platforms)
|
|
{
|
|
const FPlatformData* PlatformData = FindPlatformData(QueryPlatform);
|
|
if (!PlatformData || !PlatformData->bExplored)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void FPackageData::SetIsUrgent(bool Value)
|
|
{
|
|
bool OldValue = static_cast<bool>(bIsUrgent);
|
|
if (OldValue != Value)
|
|
{
|
|
bIsUrgent = Value != 0;
|
|
PackageDatas.GetMonitor().OnUrgencyChanged(*this);
|
|
}
|
|
}
|
|
|
|
void FPackageData::UpdateRequestData(const TConstArrayView<const ITargetPlatform*> InRequestedPlatforms,
|
|
bool bInIsUrgent, FCompletionCallback&& InCompletionCallback, FInstigator&& InInstigator, bool bAllowUpdateUrgency)
|
|
{
|
|
if (IsInProgress())
|
|
{
|
|
AddCompletionCallback(MoveTemp(InCompletionCallback));
|
|
|
|
bool bUrgencyChanged = false;
|
|
if (bInIsUrgent && !GetIsUrgent())
|
|
{
|
|
bUrgencyChanged = true;
|
|
SetIsUrgent(true);
|
|
}
|
|
|
|
if (!HasAllRequestedPlatforms(InRequestedPlatforms))
|
|
{
|
|
// Send back to the Request state (canceling any current operations) and then add the new platforms
|
|
if (GetState() != EPackageState::Request)
|
|
{
|
|
SendToState(EPackageState::Request, ESendFlags::QueueAddAndRemove);
|
|
}
|
|
SetPlatformsRequested(InRequestedPlatforms, true);
|
|
}
|
|
else if (bUrgencyChanged && bAllowUpdateUrgency)
|
|
{
|
|
SendToState(GetState(), ESendFlags::QueueAddAndRemove);
|
|
}
|
|
}
|
|
else if (InRequestedPlatforms.Num() > 0)
|
|
{
|
|
SetRequestData(InRequestedPlatforms, bInIsUrgent, MoveTemp(InCompletionCallback), MoveTemp(InInstigator));
|
|
SendToState(EPackageState::Request, ESendFlags::QueueAddAndRemove);
|
|
}
|
|
}
|
|
|
|
void FPackageData::SetRequestData(const TArrayView<const ITargetPlatform* const>& InRequestedPlatforms,
|
|
bool bInIsUrgent, FCompletionCallback&& InCompletionCallback, FInstigator&& InInstigator)
|
|
{
|
|
check(!CompletionCallback);
|
|
check(GetNumRequestedPlatforms() == 0)
|
|
check(!bIsUrgent);
|
|
|
|
check(InRequestedPlatforms.Num() != 0);
|
|
SetPlatformsRequested(InRequestedPlatforms, true);
|
|
SetIsUrgent(bInIsUrgent);
|
|
AddCompletionCallback(MoveTemp(InCompletionCallback));
|
|
if (Instigator.Category == EInstigator::NotYetRequested)
|
|
{
|
|
OnPackageDataFirstRequested(MoveTemp(InInstigator));
|
|
}
|
|
}
|
|
|
|
void FPackageData::ClearInProgressData()
|
|
{
|
|
ClearRequestedPlatforms();
|
|
SetIsUrgent(false);
|
|
CompletionCallback = FCompletionCallback();
|
|
}
|
|
|
|
void FPackageData::SetPlatformsCooked(const TConstArrayView<const ITargetPlatform*> TargetPlatforms,
|
|
const TConstArrayView<bool> Succeeded)
|
|
{
|
|
check(TargetPlatforms.Num() == Succeeded.Num());
|
|
for (int32 n = 0; n < TargetPlatforms.Num(); ++n)
|
|
{
|
|
SetPlatformCooked(TargetPlatforms[n], Succeeded[n]);
|
|
}
|
|
}
|
|
|
|
void FPackageData::SetPlatformsCooked(const TConstArrayView<const ITargetPlatform*> TargetPlatforms,
|
|
bool bSucceeded)
|
|
{
|
|
for (const ITargetPlatform* TargetPlatform : TargetPlatforms)
|
|
{
|
|
SetPlatformCooked(TargetPlatform, bSucceeded);
|
|
}
|
|
}
|
|
|
|
void FPackageData::SetPlatformCooked(const ITargetPlatform* TargetPlatform, bool bSucceeded)
|
|
{
|
|
bool bHasAnyOthers = false;
|
|
bool bModified = false;
|
|
bool bExists = false;
|
|
for (TPair<const ITargetPlatform*, FPlatformData>& Pair : PlatformDatas)
|
|
{
|
|
if (Pair.Key == TargetPlatform)
|
|
{
|
|
bExists = true;
|
|
bModified = bModified | (Pair.Value.bCookAttempted == false);
|
|
Pair.Value.bCookAttempted = true;
|
|
Pair.Value.bCookSucceeded = bSucceeded;
|
|
// Clear the SaveTimedOut when get a cook result, in case we save again later and need to allow retry again
|
|
Pair.Value.bSaveTimedOut = false;
|
|
}
|
|
else
|
|
{
|
|
bHasAnyOthers = bHasAnyOthers | (Pair.Value.bCookAttempted != false);
|
|
}
|
|
}
|
|
if (!bExists)
|
|
{
|
|
FPlatformData& Value = PlatformDatas.FindOrAdd(TargetPlatform);
|
|
Value.bCookAttempted = true;
|
|
Value.bCookSucceeded = bSucceeded;
|
|
Value.bSaveTimedOut = false;
|
|
bModified = true;
|
|
}
|
|
if (bModified && !bHasAnyOthers)
|
|
{
|
|
PackageDatas.GetMonitor().OnFirstCookedPlatformAdded(*this);
|
|
}
|
|
}
|
|
|
|
void FPackageData::ClearCookProgress(const TConstArrayView<const ITargetPlatform*> TargetPlatforms)
|
|
{
|
|
for (const ITargetPlatform* TargetPlatform : TargetPlatforms)
|
|
{
|
|
ClearCookProgress(TargetPlatform);
|
|
}
|
|
}
|
|
|
|
void FPackageData::ClearCookProgress()
|
|
{
|
|
bool bModifiedCookAttempted = false;
|
|
for (TPair<const ITargetPlatform*, FPlatformData>& Pair : PlatformDatas)
|
|
{
|
|
bModifiedCookAttempted = bModifiedCookAttempted | (Pair.Value.bCookAttempted != false);
|
|
Pair.Value.bCookAttempted = false;
|
|
Pair.Value.bCookSucceeded = false;
|
|
Pair.Value.bExplored = false;
|
|
Pair.Value.bSaveTimedOut = false;
|
|
}
|
|
if (bModifiedCookAttempted)
|
|
{
|
|
PackageDatas.GetMonitor().OnLastCookedPlatformRemoved(*this);
|
|
}
|
|
}
|
|
|
|
void FPackageData::ClearCookProgress(const ITargetPlatform* TargetPlatform)
|
|
{
|
|
bool bHasAnyOthers = false;
|
|
bool bModifiedCookAttempted = false;
|
|
for (TPair<const ITargetPlatform*, FPlatformData>& Pair : PlatformDatas)
|
|
{
|
|
if (Pair.Key == TargetPlatform)
|
|
{
|
|
bModifiedCookAttempted = bModifiedCookAttempted | (Pair.Value.bCookAttempted != false);
|
|
Pair.Value.bCookAttempted = false;
|
|
Pair.Value.bCookSucceeded = false;
|
|
Pair.Value.bExplored = false;
|
|
Pair.Value.bSaveTimedOut = false;
|
|
}
|
|
else
|
|
{
|
|
bHasAnyOthers = bHasAnyOthers | (Pair.Value.bCookAttempted != false);
|
|
}
|
|
}
|
|
if (bModifiedCookAttempted && !bHasAnyOthers)
|
|
{
|
|
PackageDatas.GetMonitor().OnLastCookedPlatformRemoved(*this);
|
|
}
|
|
}
|
|
|
|
const TSortedMap<const ITargetPlatform*, FPackageData::FPlatformData>& FPackageData::GetPlatformDatas() const
|
|
{
|
|
return PlatformDatas;
|
|
}
|
|
|
|
FPackageData::FPlatformData& FPackageData::FindOrAddPlatformData(const ITargetPlatform* TargetPlatform)
|
|
{
|
|
return PlatformDatas.FindOrAdd(TargetPlatform);
|
|
}
|
|
|
|
FPackageData::FPlatformData* FPackageData::FindPlatformData(const ITargetPlatform* TargetPlatform)
|
|
{
|
|
return PlatformDatas.Find(TargetPlatform);
|
|
}
|
|
|
|
const FPackageData::FPlatformData* FPackageData::FindPlatformData(const ITargetPlatform* TargetPlatform) const
|
|
{
|
|
return PlatformDatas.Find(TargetPlatform);
|
|
}
|
|
|
|
bool FPackageData::HasAnyCookedPlatform() const
|
|
{
|
|
return Algo::AnyOf(PlatformDatas,
|
|
[](const TPair<const ITargetPlatform*, FPlatformData>& Pair) { return Pair.Value.bCookAttempted; });
|
|
}
|
|
|
|
bool FPackageData::HasAnyCookedPlatforms(const TArrayView<const ITargetPlatform* const>& Platforms,
|
|
bool bIncludeFailed) const
|
|
{
|
|
if (PlatformDatas.Num() == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (const ITargetPlatform* QueryPlatform : Platforms)
|
|
{
|
|
if (HasCookedPlatform(QueryPlatform, bIncludeFailed))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool FPackageData::HasAllCookedPlatforms(const TArrayView<const ITargetPlatform* const>& Platforms,
|
|
bool bIncludeFailed) const
|
|
{
|
|
if (Platforms.Num() == 0)
|
|
{
|
|
return true;
|
|
}
|
|
if (PlatformDatas.Num() == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (const ITargetPlatform* QueryPlatform : Platforms)
|
|
{
|
|
if (!HasCookedPlatform(QueryPlatform, bIncludeFailed))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool FPackageData::HasCookedPlatform(const ITargetPlatform* Platform, bool bIncludeFailed) const
|
|
{
|
|
ECookResult Result = GetCookResults(Platform);
|
|
return (Result == ECookResult::Succeeded) | ((Result == ECookResult::Failed) & (bIncludeFailed != 0));
|
|
}
|
|
|
|
ECookResult FPackageData::GetCookResults(const ITargetPlatform* Platform) const
|
|
{
|
|
const FPlatformData* PlatformData = PlatformDatas.Find(Platform);
|
|
if (PlatformData && PlatformData->bCookAttempted)
|
|
{
|
|
return PlatformData->bCookSucceeded ? ECookResult::Succeeded : ECookResult::Failed;
|
|
}
|
|
return ECookResult::Unseen;
|
|
}
|
|
|
|
UPackage* FPackageData::GetPackage() const
|
|
{
|
|
return Package.Get();
|
|
}
|
|
|
|
void FPackageData::SetPackage(UPackage* InPackage)
|
|
{
|
|
Package = InPackage;
|
|
}
|
|
|
|
EPackageState FPackageData::GetState() const
|
|
{
|
|
return static_cast<EPackageState>(State);
|
|
}
|
|
|
|
/** Boilerplate-reduction struct that defines all multi-state properties and sets them based on the given state. */
|
|
struct FStateProperties
|
|
{
|
|
EPackageStateProperty Properties;
|
|
explicit FStateProperties(EPackageState InState)
|
|
{
|
|
switch (InState)
|
|
{
|
|
case EPackageState::Idle:
|
|
Properties = EPackageStateProperty::None;
|
|
break;
|
|
case EPackageState::Request:
|
|
Properties = EPackageStateProperty::InProgress;
|
|
break;
|
|
case EPackageState::LoadPrepare:
|
|
Properties = EPackageStateProperty::InProgress | EPackageStateProperty::Loading;
|
|
break;
|
|
case EPackageState::LoadReady:
|
|
Properties = EPackageStateProperty::InProgress | EPackageStateProperty::Loading;
|
|
break;
|
|
// TODO_SaveQueue: When we add state PrepareForSave, it will also have bHasPackage = true,
|
|
case EPackageState::Save:
|
|
Properties = EPackageStateProperty::InProgress | EPackageStateProperty::HasPackage;
|
|
break;
|
|
default:
|
|
check(false);
|
|
Properties = EPackageStateProperty::None;
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
|
|
void FPackageData::SendToState(EPackageState NextState, ESendFlags SendFlags)
|
|
{
|
|
EPackageState OldState = GetState();
|
|
switch (OldState)
|
|
{
|
|
case EPackageState::Idle:
|
|
OnExitIdle();
|
|
break;
|
|
case EPackageState::Request:
|
|
if (!!(SendFlags & ESendFlags::QueueRemove))
|
|
{
|
|
ensure(PackageDatas.GetRequestQueue().Remove(this) == 1);
|
|
}
|
|
OnExitRequest();
|
|
break;
|
|
case EPackageState::LoadPrepare:
|
|
if (!!(SendFlags & ESendFlags::QueueRemove))
|
|
{
|
|
ensure(PackageDatas.GetLoadPrepareQueue().Remove(this) == 1);
|
|
}
|
|
OnExitLoadPrepare();
|
|
break;
|
|
case EPackageState::LoadReady:
|
|
if (!!(SendFlags & ESendFlags::QueueRemove))
|
|
{
|
|
ensure(PackageDatas.GetLoadReadyQueue().Remove(this) == 1);
|
|
}
|
|
OnExitLoadReady();
|
|
break;
|
|
case EPackageState::Save:
|
|
if (!!(SendFlags & ESendFlags::QueueRemove))
|
|
{
|
|
ensure(PackageDatas.GetSaveQueue().Remove(this) == 1);
|
|
}
|
|
OnExitSave();
|
|
break;
|
|
default:
|
|
check(false);
|
|
break;
|
|
}
|
|
|
|
FStateProperties OldProperties(OldState);
|
|
FStateProperties NewProperties(NextState);
|
|
// Exit state properties from highest to lowest; enter state properties from lowest to highest.
|
|
// This ensures that properties that rely on earlier properties are constructed later and torn down earlier
|
|
// than the earlier properties.
|
|
for (EPackageStateProperty Iterator = EPackageStateProperty::Max;
|
|
Iterator >= EPackageStateProperty::Min;
|
|
Iterator = static_cast<EPackageStateProperty>(static_cast<uint32>(Iterator) >> 1))
|
|
{
|
|
if (((OldProperties.Properties & Iterator) != EPackageStateProperty::None) &
|
|
((NewProperties.Properties & Iterator) == EPackageStateProperty::None))
|
|
{
|
|
switch (Iterator)
|
|
{
|
|
case EPackageStateProperty::InProgress:
|
|
OnExitInProgress();
|
|
break;
|
|
case EPackageStateProperty::Loading:
|
|
OnExitLoading();
|
|
break;
|
|
case EPackageStateProperty::HasPackage:
|
|
OnExitHasPackage();
|
|
break;
|
|
default:
|
|
check(false);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
for (EPackageStateProperty Iterator = EPackageStateProperty::Min;
|
|
Iterator <= EPackageStateProperty::Max;
|
|
Iterator = static_cast<EPackageStateProperty>(static_cast<uint32>(Iterator) << 1))
|
|
{
|
|
if (((OldProperties.Properties & Iterator) == EPackageStateProperty::None) &
|
|
((NewProperties.Properties & Iterator) != EPackageStateProperty::None))
|
|
{
|
|
switch (Iterator)
|
|
{
|
|
case EPackageStateProperty::InProgress:
|
|
OnEnterInProgress();
|
|
break;
|
|
case EPackageStateProperty::Loading:
|
|
OnEnterLoading();
|
|
break;
|
|
case EPackageStateProperty::HasPackage:
|
|
OnEnterHasPackage();
|
|
break;
|
|
default:
|
|
check(false);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
SetState(NextState);
|
|
switch (NextState)
|
|
{
|
|
case EPackageState::Idle:
|
|
OnEnterIdle();
|
|
break;
|
|
case EPackageState::Request:
|
|
OnEnterRequest();
|
|
if (((SendFlags & ESendFlags::QueueAdd) != ESendFlags::QueueNone))
|
|
{
|
|
PackageDatas.GetRequestQueue().AddRequest(this);
|
|
}
|
|
break;
|
|
case EPackageState::LoadPrepare:
|
|
OnEnterLoadPrepare();
|
|
if ((SendFlags & ESendFlags::QueueAdd) != ESendFlags::QueueNone)
|
|
{
|
|
if (GetIsUrgent())
|
|
{
|
|
PackageDatas.GetLoadPrepareQueue().AddFront(this);
|
|
}
|
|
else
|
|
{
|
|
PackageDatas.GetLoadPrepareQueue().Add(this);
|
|
}
|
|
}
|
|
break;
|
|
case EPackageState::LoadReady:
|
|
OnEnterLoadReady();
|
|
if ((SendFlags & ESendFlags::QueueAdd) != ESendFlags::QueueNone)
|
|
{
|
|
if (GetIsUrgent())
|
|
{
|
|
PackageDatas.GetLoadReadyQueue().AddFront(this);
|
|
}
|
|
else
|
|
{
|
|
PackageDatas.GetLoadReadyQueue().Add(this);
|
|
}
|
|
}
|
|
break;
|
|
case EPackageState::Save:
|
|
OnEnterSave();
|
|
if (((SendFlags & ESendFlags::QueueAdd) != ESendFlags::QueueNone))
|
|
{
|
|
if (GetIsUrgent())
|
|
{
|
|
PackageDatas.GetSaveQueue().AddFront(this);
|
|
}
|
|
else
|
|
{
|
|
PackageDatas.GetSaveQueue().Add(this);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
check(false);
|
|
break;
|
|
}
|
|
|
|
PackageDatas.GetMonitor().OnStateChanged(*this, OldState);
|
|
}
|
|
|
|
void FPackageData::CheckInContainer() const
|
|
{
|
|
switch (GetState())
|
|
{
|
|
case EPackageState::Idle:
|
|
break;
|
|
case EPackageState::Request:
|
|
check(PackageDatas.GetRequestQueue().Contains(this));
|
|
break;
|
|
case EPackageState::LoadPrepare:
|
|
check(PackageDatas.GetLoadPrepareQueue().Contains(this));
|
|
break;
|
|
case EPackageState::LoadReady:
|
|
check(Algo::Find(PackageDatas.GetLoadReadyQueue(), this) != nullptr);
|
|
break;
|
|
case EPackageState::Save:
|
|
// The save queue is huge and often pushed at end. Check last element first and then scan.
|
|
check(PackageDatas.GetSaveQueue().Num() && (PackageDatas.GetSaveQueue().Last() == this || Algo::Find(PackageDatas.GetSaveQueue(), this)));
|
|
break;
|
|
default:
|
|
check(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool FPackageData::IsInProgress() const
|
|
{
|
|
return IsInStateProperty(EPackageStateProperty::InProgress);
|
|
}
|
|
|
|
bool FPackageData::IsInStateProperty(EPackageStateProperty Property) const
|
|
{
|
|
return (FStateProperties(GetState()).Properties & Property) != EPackageStateProperty::None;
|
|
}
|
|
|
|
void FPackageData::OnEnterIdle()
|
|
{
|
|
// Note that this might be on construction of the PackageData
|
|
}
|
|
|
|
void FPackageData::OnExitIdle()
|
|
{
|
|
if (PackageDatas.GetLogDiscoveredPackages())
|
|
{
|
|
UE_LOG(LogCook, Warning, TEXT("Missing dependency: Package %s discovered after initial dependency search."), *WriteToString<256>(PackageName));
|
|
}
|
|
}
|
|
|
|
void FPackageData::OnEnterRequest()
|
|
{
|
|
// It is not valid to enter the request state without requested platforms; it indicates a bug due to e.g.
|
|
// calling SendToState without UpdateRequestData from Idle
|
|
check(GetNumRequestedPlatforms() > 0);
|
|
}
|
|
|
|
void FPackageData::OnExitRequest()
|
|
{
|
|
}
|
|
|
|
void FPackageData::OnEnterLoadPrepare()
|
|
{
|
|
}
|
|
|
|
void FPackageData::OnExitLoadPrepare()
|
|
{
|
|
}
|
|
|
|
void FPackageData::OnEnterLoadReady()
|
|
{
|
|
}
|
|
|
|
void FPackageData::OnExitLoadReady()
|
|
{
|
|
}
|
|
|
|
void FPackageData::OnEnterSave()
|
|
{
|
|
check(GetPackage() != nullptr && GetPackage()->IsFullyLoaded());
|
|
|
|
check(!GetHasBeginPrepareSaveFailed());
|
|
CheckObjectCacheEmpty();
|
|
CheckCookedPlatformDataEmpty();
|
|
}
|
|
|
|
void FPackageData::OnExitSave()
|
|
{
|
|
PackageDatas.GetCookOnTheFlyServer().ReleaseCookedPlatformData(*this, false /* bCompletedSave */);
|
|
ClearObjectCache();
|
|
SetHasBeginPrepareSaveFailed(false);
|
|
}
|
|
|
|
void FPackageData::OnEnterInProgress()
|
|
{
|
|
PackageDatas.GetMonitor().OnInProgressChanged(*this, true);
|
|
}
|
|
|
|
void FPackageData::OnExitInProgress()
|
|
{
|
|
PackageDatas.GetMonitor().OnInProgressChanged(*this, false);
|
|
UE::Cook::FCompletionCallback LocalCompletionCallback(MoveTemp(GetCompletionCallback()));
|
|
if (LocalCompletionCallback)
|
|
{
|
|
LocalCompletionCallback(this);
|
|
}
|
|
ClearInProgressData();
|
|
}
|
|
|
|
void FPackageData::OnEnterLoading()
|
|
{
|
|
CheckPreloadEmpty();
|
|
}
|
|
|
|
void FPackageData::OnExitLoading()
|
|
{
|
|
ClearPreload();
|
|
}
|
|
|
|
void FPackageData::OnEnterHasPackage()
|
|
{
|
|
}
|
|
|
|
void FPackageData::OnExitHasPackage()
|
|
{
|
|
SetPackage(nullptr);
|
|
}
|
|
|
|
void FPackageData::OnPackageDataFirstRequested(FInstigator&& InInstigator)
|
|
{
|
|
TracePackage(GetPackageName().ToUnstableInt(), GetPackageName().ToString());
|
|
Instigator = MoveTemp(InInstigator);
|
|
PackageDatas.DebugInstigator(*this);
|
|
}
|
|
|
|
void FPackageData::SetState(EPackageState NextState)
|
|
{
|
|
State = static_cast<uint32>(NextState);
|
|
}
|
|
|
|
FCompletionCallback& FPackageData::GetCompletionCallback()
|
|
{
|
|
return CompletionCallback;
|
|
}
|
|
|
|
void FPackageData::AddCompletionCallback(FCompletionCallback&& InCompletionCallback)
|
|
{
|
|
if (InCompletionCallback)
|
|
{
|
|
// We don't yet have a mechanism for calling two completion callbacks.
|
|
// CompletionCallbacks only come from external requests, and it should not be possible to request twice,
|
|
// so a failed check here shouldn't happen.
|
|
check(!CompletionCallback);
|
|
CompletionCallback = MoveTemp(InCompletionCallback);
|
|
}
|
|
}
|
|
|
|
bool FPackageData::TryPreload()
|
|
{
|
|
check(IsInStateProperty(EPackageStateProperty::Loading));
|
|
if (GetIsPreloadAttempted())
|
|
{
|
|
return true;
|
|
}
|
|
if (FindObjectFast<UPackage>(nullptr, GetPackageName()))
|
|
{
|
|
// If the package has already loaded, then there is no point in further preloading
|
|
ClearPreload();
|
|
SetIsPreloadAttempted(true);
|
|
return true;
|
|
}
|
|
if (IsGenerated())
|
|
{
|
|
// Deferred populate generated packages are loaded from their generator, not from disk
|
|
ClearPreload();
|
|
SetIsPreloadAttempted(true);
|
|
return true;
|
|
}
|
|
if (!PreloadableFile.Get())
|
|
{
|
|
if (FEditorDomain* EditorDomain = FEditorDomain::Get())
|
|
{
|
|
EditorDomain->PrecachePackageDigest(GetPackageName());
|
|
}
|
|
TStringBuilder<NAME_SIZE> FileNameString;
|
|
GetFileName().ToString(FileNameString);
|
|
PreloadableFile.Set(MakeShared<FPreloadableArchive>(FileNameString.ToString()), *this);
|
|
PreloadableFile.Get()->InitializeAsync([this]()
|
|
{
|
|
TStringBuilder<NAME_SIZE> FileNameString;
|
|
// Note this async callback has an read of this->GetFilename and a write of PreloadableFileOpenResult
|
|
// outside of a critical section. This read and write is allowed because GetFilename does
|
|
// not change until this is destructed, and the destructor does not run and other threads do not read
|
|
// or write PreloadableFileOpenResult until after PreloadableFile.Get() has finished initialization
|
|
// and this callback is therefore complete.
|
|
// The code that accomplishes that waiting is in TryPreload (IsInitialized) and ClearPreload (ReleaseCache)
|
|
this->GetFileName().ToString(FileNameString);
|
|
FPackagePath PackagePath = FPackagePath::FromLocalPath(FileNameString);
|
|
FOpenPackageResult Result = IPackageResourceManager::Get().OpenReadPackage(PackagePath);
|
|
if (Result.Archive)
|
|
{
|
|
this->PreloadableFileOpenResult.CopyMetaData(Result);
|
|
}
|
|
return Result.Archive.Release();
|
|
},
|
|
FPreloadableFile::Flags::PreloadHandle | FPreloadableFile::Flags::Prime);
|
|
}
|
|
const TSharedPtr<FPreloadableArchive>& FilePtr = PreloadableFile.Get();
|
|
if (!FilePtr->IsInitialized())
|
|
{
|
|
if (GetIsUrgent())
|
|
{
|
|
// For urgent requests, wait on them to finish preloading rather than letting them run asynchronously
|
|
// and coming back to them later
|
|
FilePtr->WaitForInitialization();
|
|
check(FilePtr->IsInitialized());
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
if (FilePtr->TotalSize() < 0)
|
|
{
|
|
UE_LOG(LogCook, Warning, TEXT("Failed to find file when preloading %s."), *GetFileName().ToString());
|
|
SetIsPreloadAttempted(true);
|
|
PreloadableFile.Reset(*this);
|
|
PreloadableFileOpenResult = FOpenPackageResult();
|
|
return true;
|
|
}
|
|
|
|
TStringBuilder<NAME_SIZE> FileNameString;
|
|
GetFileName().ToString(FileNameString);
|
|
if (!IPackageResourceManager::TryRegisterPreloadableArchive(FPackagePath::FromLocalPath(FileNameString),
|
|
FilePtr, PreloadableFileOpenResult))
|
|
{
|
|
UE_LOG(LogCook, Warning, TEXT("Failed to register %s for preload."), *GetFileName().ToString());
|
|
SetIsPreloadAttempted(true);
|
|
PreloadableFile.Reset(*this);
|
|
PreloadableFileOpenResult = FOpenPackageResult();
|
|
return true;
|
|
}
|
|
|
|
SetIsPreloaded(true);
|
|
SetIsPreloadAttempted(true);
|
|
return true;
|
|
}
|
|
|
|
void FPackageData::FTrackedPreloadableFilePtr::Set(TSharedPtr<FPreloadableArchive>&& InPtr, FPackageData& Owner)
|
|
{
|
|
Reset(Owner);
|
|
if (InPtr)
|
|
{
|
|
Ptr = MoveTemp(InPtr);
|
|
Owner.PackageDatas.GetMonitor().OnPreloadAllocatedChanged(Owner, true);
|
|
}
|
|
}
|
|
|
|
void FPackageData::FTrackedPreloadableFilePtr::Reset(FPackageData& Owner)
|
|
{
|
|
if (Ptr)
|
|
{
|
|
Owner.PackageDatas.GetMonitor().OnPreloadAllocatedChanged(Owner, false);
|
|
Ptr.Reset();
|
|
}
|
|
}
|
|
|
|
void FPackageData::ClearPreload()
|
|
{
|
|
const TSharedPtr<FPreloadableArchive>& FilePtr = PreloadableFile.Get();
|
|
if (GetIsPreloaded())
|
|
{
|
|
check(FilePtr);
|
|
TStringBuilder<NAME_SIZE> FileNameString;
|
|
GetFileName().ToString(FileNameString);
|
|
if (IPackageResourceManager::UnRegisterPreloadableArchive(FPackagePath::FromLocalPath(FileNameString)))
|
|
{
|
|
UE_LOG(LogCook, Display, TEXT("PreloadableFile was created for %s but never used. This is wasteful and bad for cook performance."),
|
|
*PackageName.ToString());
|
|
}
|
|
FilePtr->ReleaseCache(); // ReleaseCache to conserve memory if the Linker still has a pointer to it
|
|
}
|
|
else
|
|
{
|
|
check(!FilePtr || !FilePtr->IsCacheAllocated());
|
|
}
|
|
|
|
PreloadableFile.Reset(*this);
|
|
PreloadableFileOpenResult = FOpenPackageResult();
|
|
SetIsPreloaded(false);
|
|
SetIsPreloadAttempted(false);
|
|
}
|
|
|
|
void FPackageData::CheckPreloadEmpty()
|
|
{
|
|
check(!GetIsPreloadAttempted());
|
|
check(!PreloadableFile.Get());
|
|
check(!GetIsPreloaded());
|
|
}
|
|
|
|
TArray<FWeakObjectPtr>& FPackageData::GetCachedObjectsInOuter()
|
|
{
|
|
return CachedObjectsInOuter;
|
|
}
|
|
|
|
void FPackageData::CheckObjectCacheEmpty() const
|
|
{
|
|
check(CachedObjectsInOuter.Num() == 0);
|
|
check(!GetHasSaveCache());
|
|
}
|
|
|
|
void FPackageData::CreateObjectCache()
|
|
{
|
|
if (GetHasSaveCache())
|
|
{
|
|
return;
|
|
}
|
|
|
|
UPackage* LocalPackage = GetPackage();
|
|
if (LocalPackage && LocalPackage->IsFullyLoaded())
|
|
{
|
|
PackageName = LocalPackage->GetFName();
|
|
TArray<UObject*> ObjectsInOuter;
|
|
GetObjectsWithOuter(LocalPackage, ObjectsInOuter);
|
|
CachedObjectsInOuter.Reset(ObjectsInOuter.Num());
|
|
for (UObject* Object : ObjectsInOuter)
|
|
{
|
|
FWeakObjectPtr ObjectWeakPointer(Object);
|
|
// ignore pending kill objects; they will not be serialized out so we don't need to call
|
|
// BeginCacheForCookedPlatformData on them
|
|
if (!ObjectWeakPointer.Get())
|
|
{
|
|
continue;
|
|
}
|
|
CachedObjectsInOuter.Emplace(MoveTemp(ObjectWeakPointer));
|
|
}
|
|
SetHasSaveCache(true);
|
|
}
|
|
else
|
|
{
|
|
check(false);
|
|
}
|
|
}
|
|
|
|
void FPackageData::RecreateObjectCache()
|
|
{
|
|
check(GetPackage());
|
|
if (GetHasSaveCache())
|
|
{
|
|
// It is not valid to recreate the ObjectCache when we have already started calling BeginCacheForCookedPlatformData on the objects in it.
|
|
// ReleaseCookedPlatformData must be called first to tear down all of the calls and reset GetCookedPlatformDataNextIndex to 0
|
|
check(GetCookedPlatformDataNextIndex() == 0);
|
|
ClearObjectCache();
|
|
}
|
|
CreateObjectCache();
|
|
}
|
|
|
|
void FPackageData::ClearObjectCache()
|
|
{
|
|
CachedObjectsInOuter.Empty();
|
|
SetHasSaveCache(false);
|
|
}
|
|
|
|
const int32& FPackageData::GetNumPendingCookedPlatformData() const
|
|
{
|
|
return NumPendingCookedPlatformData;
|
|
}
|
|
|
|
int32& FPackageData::GetNumPendingCookedPlatformData()
|
|
{
|
|
return NumPendingCookedPlatformData;
|
|
}
|
|
|
|
const int32& FPackageData::GetCookedPlatformDataNextIndex() const
|
|
{
|
|
return CookedPlatformDataNextIndex;
|
|
}
|
|
|
|
int32& FPackageData::GetCookedPlatformDataNextIndex()
|
|
{
|
|
return CookedPlatformDataNextIndex;
|
|
}
|
|
|
|
void FPackageData::CheckCookedPlatformDataEmpty() const
|
|
{
|
|
check(GetCookedPlatformDataNextIndex() == 0);
|
|
check(!GetCookedPlatformDataStarted());
|
|
check(!GetCookedPlatformDataCalled());
|
|
check(!GetCookedPlatformDataComplete());
|
|
}
|
|
|
|
void FPackageData::ClearCookedPlatformData()
|
|
{
|
|
CookedPlatformDataNextIndex = 0;
|
|
// Note that GetNumPendingCookedPlatformData is not cleared; it persists across Saves and CookSessions
|
|
SetCookedPlatformDataStarted(false);
|
|
SetCookedPlatformDataCalled(false);
|
|
SetCookedPlatformDataComplete(false);
|
|
}
|
|
|
|
void FPackageData::ResetGenerationProgress()
|
|
{
|
|
SetInitializedGeneratorSave(false);
|
|
SetCompletedGeneration(false);
|
|
}
|
|
|
|
void FPackageData::OnRemoveSessionPlatform(const ITargetPlatform* Platform)
|
|
{
|
|
PlatformDatas.Remove(Platform);
|
|
}
|
|
|
|
bool FPackageData::HasReferencedObjects() const
|
|
{
|
|
return Package != nullptr || CachedObjectsInOuter.Num() > 0;
|
|
}
|
|
|
|
void FPackageData::RemapTargetPlatforms(const TMap<ITargetPlatform*, ITargetPlatform*>& Remap)
|
|
{
|
|
typedef TSortedMap<const ITargetPlatform*, FPlatformData> MapType;
|
|
MapType NewPlatformDatas;
|
|
NewPlatformDatas.Reserve(PlatformDatas.Num());
|
|
for (TPair<const ITargetPlatform*, FPlatformData>& ExistingPair : PlatformDatas)
|
|
{
|
|
ITargetPlatform* NewKey = Remap[ExistingPair.Key];
|
|
NewPlatformDatas.FindOrAdd(NewKey) = MoveTemp(ExistingPair.Value);
|
|
}
|
|
|
|
// The save state (and maybe more in the future) depend on the order of the request platforms remaining
|
|
// unchanged, due to CookedPlatformDataNextIndex. If we change that order due to the remap, we need to
|
|
// demote back to request.
|
|
if (IsInProgress() && GetState() != EPackageState::Request)
|
|
{
|
|
bool bDemote = true;
|
|
MapType::TConstIterator OldIter = PlatformDatas.CreateConstIterator();
|
|
MapType::TConstIterator NewIter = NewPlatformDatas.CreateConstIterator();
|
|
for (; OldIter; ++OldIter, ++NewIter)
|
|
{
|
|
if (OldIter.Key() != NewIter.Key())
|
|
{
|
|
bDemote = true;
|
|
}
|
|
}
|
|
if (bDemote)
|
|
{
|
|
SendToState(EPackageState::Request, ESendFlags::QueueAddAndRemove);
|
|
}
|
|
}
|
|
PlatformDatas = MoveTemp(NewPlatformDatas);
|
|
}
|
|
|
|
bool FPackageData::IsSaveInvalidated() const
|
|
{
|
|
if (GetState() != EPackageState::Save)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return GetPackage() == nullptr || !GetPackage()->IsFullyLoaded() ||
|
|
Algo::AnyOf(CachedObjectsInOuter, [](const FWeakObjectPtr& WeakPtr)
|
|
{
|
|
// TODO: Keep track of which objects were public, and only invalidate the save if the object
|
|
// that has been deleted or marked pending kill was public
|
|
// Until we make that change, we will unnecessarily invalidate and demote some packages after a
|
|
// garbage collect
|
|
return WeakPtr.Get() == nullptr;
|
|
});
|
|
}
|
|
|
|
void FPackageData::SetGeneratedOwner(FGeneratorPackage* InGeneratedOwner)
|
|
{
|
|
check(IsGenerated());
|
|
check(!(GeneratedOwner && InGeneratedOwner));
|
|
GeneratedOwner = InGeneratedOwner;
|
|
}
|
|
|
|
bool FPackageData::GeneratorPackageRequiresGC() const
|
|
{
|
|
// We consider that if a FPackageData has valid GeneratorPackage helper object,
|
|
// this means that COTFS's process of generating packages was not completed
|
|
// either due to an error or because it has exceeded a maximum memory threshold.
|
|
return IsGenerating() && !GetHasBeginPrepareSaveFailed();
|
|
}
|
|
|
|
UE::Cook::FGeneratorPackage* FPackageData::CreateGeneratorPackage(const UObject* InSplitDataObject,
|
|
ICookPackageSplitter* InCookPackageSplitterInstance)
|
|
{
|
|
check(!GetGeneratorPackage());
|
|
GeneratorPackage.Reset(new UE::Cook::FGeneratorPackage(*this, InSplitDataObject,
|
|
InCookPackageSplitterInstance));
|
|
return GetGeneratorPackage();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// FGeneratorPackage
|
|
|
|
FGeneratorPackage::FGeneratorPackage(UE::Cook::FPackageData& InOwner, const UObject* InSplitDataObject,
|
|
ICookPackageSplitter* InCookPackageSplitterInstance)
|
|
: Owner(InOwner)
|
|
, SplitDataObjectName(*InSplitDataObject->GetFullName())
|
|
{
|
|
check(InCookPackageSplitterInstance);
|
|
CookPackageSplitterInstance.Reset(InCookPackageSplitterInstance);
|
|
}
|
|
|
|
FGeneratorPackage::~FGeneratorPackage()
|
|
{
|
|
ConditionalNotifyCompletion(ICookPackageSplitter::ETeardown::Canceled);
|
|
ClearGeneratedPackages();
|
|
}
|
|
|
|
void FGeneratorPackage::ConditionalNotifyCompletion(ICookPackageSplitter::ETeardown Status)
|
|
{
|
|
if (!bNotifiedCompletion)
|
|
{
|
|
bNotifiedCompletion = true;
|
|
CookPackageSplitterInstance->Teardown(Status);
|
|
}
|
|
}
|
|
|
|
void FGeneratorPackage::ClearGeneratedPackages()
|
|
{
|
|
for (FGeneratedStruct& GeneratedStruct : PackagesToGenerate)
|
|
{
|
|
if (GeneratedStruct.PackageData)
|
|
{
|
|
check(GeneratedStruct.PackageData->GetGeneratedOwner() == this);
|
|
GeneratedStruct.PackageData->SetGeneratedOwner(nullptr);
|
|
GeneratedStruct.PackageData = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool FGeneratorPackage::TryGenerateList(UObject* OwnerObject, FPackageDatas& PackageDatas)
|
|
{
|
|
UPackage* OwnerPackage = Owner.GetPackage();
|
|
check(OwnerPackage);
|
|
TArray<ICookPackageSplitter::FGeneratedPackage> GeneratorDatas =
|
|
CookPackageSplitterInstance->GetGenerateList(OwnerPackage, OwnerObject);
|
|
PackagesToGenerate.Reset(GeneratorDatas.Num());
|
|
for (ICookPackageSplitter::FGeneratedPackage& SplitterData : GeneratorDatas)
|
|
{
|
|
FGeneratedStruct& GeneratedStruct = PackagesToGenerate.Emplace_GetRef();
|
|
GeneratedStruct.RelativePath = MoveTemp(SplitterData.RelativePath);
|
|
GeneratedStruct.Dependencies = MoveTemp(SplitterData.Dependencies);
|
|
FString PackageName = FPaths::RemoveDuplicateSlashes(FString::Printf(TEXT("%s/%s/%s"),
|
|
*this->Owner.GetPackageName().ToString(), GeneratedPackageSubPath, *GeneratedStruct.RelativePath));
|
|
|
|
if (!SplitterData.GetCreateAsMap().IsSet())
|
|
{
|
|
UE_LOG(LogCook, Error, TEXT("PackageSplitter did not specify whether CreateAsMap is true for generated package. Splitter=%s, Generated=%s."),
|
|
*this->GetSplitDataObjectName().ToString(), *PackageName);
|
|
return false;
|
|
}
|
|
GeneratedStruct.bCreateAsMap = *SplitterData.GetCreateAsMap();
|
|
|
|
const FName PackageFName(*PackageName);
|
|
UE::Cook::FPackageData* PackageData = PackageDatas.TryAddPackageDataByPackageName(PackageFName,
|
|
false /* bRequireExists */, GeneratedStruct.bCreateAsMap);
|
|
if (!PackageData)
|
|
{
|
|
UE_LOG(LogCook, Error, TEXT("PackageSplitter could not find mounted filename for generated packagepath. Splitter=%s, Generated=%s."),
|
|
*this->GetSplitDataObjectName().ToString(), *PackageName);
|
|
return false;
|
|
}
|
|
if (IFileManager::Get().FileExists(*PackageData->GetFileName().ToString()))
|
|
{
|
|
UE_LOG(LogCook, Warning, TEXT("PackageSplitter specified a generated package that already exists in the workspace domain. Splitter=%s, Generated=%s."),
|
|
*this->GetSplitDataObjectName().ToString(), *PackageName);
|
|
return false;
|
|
}
|
|
GeneratedStruct.PackageData = PackageData;
|
|
PackageData->SetGenerated(true);
|
|
// No package should be generated by two different splitters. If an earlier run of this splitter generated
|
|
// the package, the package's owner should have been reset to null when we called ClearGeneratedPackages
|
|
// between then and now
|
|
check(PackageData->GetGeneratedOwner() == nullptr);
|
|
PackageData->SetGeneratedOwner(this);
|
|
}
|
|
RemainingToPopulate = GeneratorDatas.Num();
|
|
return true;
|
|
}
|
|
|
|
FGeneratorPackage::FGeneratedStruct* FGeneratorPackage::FindGeneratedStruct(FPackageData* PackageData)
|
|
{
|
|
for (FGeneratedStruct& GeneratedStruct : PackagesToGenerate)
|
|
{
|
|
if (GeneratedStruct.PackageData == PackageData)
|
|
{
|
|
return &GeneratedStruct;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
UObject* FGeneratorPackage::FindSplitDataObject() const
|
|
{
|
|
FString ObjectPath = GetSplitDataObjectName().ToString();
|
|
|
|
// SplitDataObjectName is a FullObjectPath; strip off the leading <ClassName> in
|
|
// "<ClassName> <Package>.<Object>:<SubObject>"
|
|
int32 ClassDelimiterIndex = -1;
|
|
if (ObjectPath.FindChar(' ', ClassDelimiterIndex))
|
|
{
|
|
ObjectPath.RightChopInline(ClassDelimiterIndex + 1);
|
|
}
|
|
return FindObject<UObject>(nullptr, *ObjectPath);
|
|
}
|
|
|
|
void FGeneratorPackage::PostGarbageCollect()
|
|
{
|
|
if (!bGeneratedList)
|
|
{
|
|
return;
|
|
}
|
|
if (Owner.GetState() == EPackageState::Save)
|
|
{
|
|
// UCookOnTheFlyServer::PreCollectGarbage adds references for the Generator package and all its public
|
|
// objects, so it should still be loaded
|
|
if (!Owner.GetPackage() || !FindSplitDataObject())
|
|
{
|
|
UE_LOG(LogCook, Error, TEXT("PackageSplitter object was deleted by garbage collection while generation was still ongoing. This will break the generation.")
|
|
TEXT("\n\tSplitter=%s."), *GetSplitDataObjectName().ToString());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// After the Generator Package is saved, we drop our references to it and it can be garbage collected
|
|
// If we have any packages left to populate, our splitter contract requires that it be garbage collected;
|
|
// we promise that the package is not partially GC'd during calls to TryPopulateGeneratedPackage
|
|
// The splitter can opt-out of this contract and keep it referenced itself if it desires.
|
|
UPackage* OwnerPackage = FindObject<UPackage>(nullptr, *Owner.GetPackageName().ToString());
|
|
if (OwnerPackage)
|
|
{
|
|
if (RemainingToPopulate > 0 &&
|
|
!Owner.IsKeepReferencedDuringGC() &&
|
|
!CookPackageSplitterInstance->UseInternalReferenceToAvoidGarbageCollect())
|
|
{
|
|
UE_LOG(LogCook, Error, TEXT("PackageSplitter found the Generator package still in memory after it should have been deleted by GC.")
|
|
TEXT("\n\tThis is unexpected since garbage has been collected and the package should have been unreferenced so it should have been collected, and will break population of Generated packages.")
|
|
TEXT("\n\tSplitter=%s"), *GetSplitDataObjectName().ToString());
|
|
EReferenceChainSearchMode SearchMode = EReferenceChainSearchMode::Shortest
|
|
| EReferenceChainSearchMode::PrintAllResults
|
|
| EReferenceChainSearchMode::FullChain;
|
|
FReferenceChainSearch RefChainSearch(OwnerPackage, SearchMode);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bWasOwnerReloaded = true;
|
|
}
|
|
}
|
|
|
|
bool bHasIssuedWarning = false;
|
|
for (FGeneratedStruct& GeneratedStruct : PackagesToGenerate)
|
|
{
|
|
if (FindObject<UPackage>(nullptr, *GeneratedStruct.PackageData->GetPackageName().ToString()))
|
|
{
|
|
if (!GeneratedStruct.PackageData->IsKeepReferencedDuringGC() &&
|
|
!GeneratedStruct.bHasSaved &&
|
|
!bHasIssuedWarning)
|
|
{
|
|
UE_LOG(LogCook, Warning, TEXT("PackageSplitter found a package it generated that was not removed from memory during garbage collection. This will cause errors later during population.")
|
|
TEXT("\n\tSplitter=%s, Generated=%s."), *GetSplitDataObjectName().ToString(), *GeneratedStruct.PackageData->GetPackageName().ToString());
|
|
bHasIssuedWarning = true; // Only issue the warning once per GC
|
|
}
|
|
}
|
|
else
|
|
{
|
|
GeneratedStruct.bHasCreatedPackage = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
UPackage* FGeneratorPackage::CreateGeneratedUPackage(FGeneratorPackage::FGeneratedStruct& GeneratedStruct,
|
|
const UPackage* OwnerPackage, const TCHAR* GeneratedPackageName)
|
|
{
|
|
UPackage* GeneratedPackage = CreatePackage(GeneratedPackageName);
|
|
PRAGMA_DISABLE_DEPRECATION_WARNINGS
|
|
GeneratedPackage->SetGuid(OwnerPackage->GetGuid());
|
|
PRAGMA_ENABLE_DEPRECATION_WARNINGS
|
|
GeneratedPackage->SetPersistentGuid(OwnerPackage->GetPersistentGuid());
|
|
GeneratedPackage->SetPackageFlags(PKG_CookGenerated);
|
|
GeneratedStruct.bHasCreatedPackage = true;
|
|
return GeneratedPackage;
|
|
}
|
|
|
|
void FGeneratorPackage::SetGeneratorSaved(UPackage* GeneratorUPackage)
|
|
{
|
|
UObject* SplitObject = FindSplitDataObject();
|
|
if (!SplitObject || !GeneratorUPackage)
|
|
{
|
|
UE_LOG(LogCook, Error, TEXT("PackageSplitter: %s was GarbageCollected before we finished saving it. This will cause a failure later when we attempt to save it again and generate a second time. Splitter=%s."),
|
|
(!GeneratorUPackage ? TEXT("UPackage") : TEXT("SplitDataObject")), *GetSplitDataObjectName().ToString());
|
|
}
|
|
else
|
|
{
|
|
GetCookPackageSplitterInstance()->PostSaveGeneratorPackage(GeneratorUPackage, SplitObject);
|
|
}
|
|
bOwnerHasSaved = true;
|
|
if (IsComplete())
|
|
{
|
|
ConditionalNotifyCompletion(ICookPackageSplitter::ETeardown::Complete);
|
|
}
|
|
}
|
|
|
|
void FGeneratorPackage::SetGeneratedSaved(FPackageData& PackageData)
|
|
{
|
|
FGeneratedStruct* GeneratedStruct = FindGeneratedStruct(&PackageData);
|
|
if (!GeneratedStruct)
|
|
{
|
|
UE_LOG(LogCook, Warning, TEXT("PackageSplitter called SetGeneratedSaved on a package that does not belong to the splitter.")
|
|
TEXT("\n\tSplitter=%s, Generated=%s."), *GetSplitDataObjectName().ToString(),
|
|
*PackageData.GetPackageName().ToString());
|
|
return;
|
|
}
|
|
if (GeneratedStruct->bHasSaved)
|
|
{
|
|
return;
|
|
}
|
|
GeneratedStruct->bHasSaved = true;
|
|
--RemainingToPopulate;
|
|
check(RemainingToPopulate >= 0);
|
|
if (IsComplete())
|
|
{
|
|
ConditionalNotifyCompletion(ICookPackageSplitter::ETeardown::Complete);
|
|
}
|
|
}
|
|
|
|
bool FGeneratorPackage::IsComplete() const
|
|
{
|
|
return bGeneratedList && RemainingToPopulate == 0;
|
|
}
|
|
|
|
void FGeneratorPackage::GetIntermediateMountPoint(FString& OutPackagePath, FString& OutLocalFilePath) const
|
|
{
|
|
const FString OwnerShortName = FPackageName::GetShortName(Owner.GetPackageName().ToString());
|
|
OutPackagePath = FPaths::RemoveDuplicateSlashes(FString::Printf(TEXT("/%s%s/"),
|
|
*OwnerShortName, UE::Cook::GeneratedPackageSubPath));
|
|
OutLocalFilePath = FPaths::RemoveDuplicateSlashes(FString::Printf(TEXT("%s/Cooked/%s/%s/"),
|
|
*FPaths::ProjectIntermediateDir(), *OwnerShortName, UE::Cook::GeneratedPackageSubPath));
|
|
|
|
}
|
|
FString FGeneratorPackage::GetIntermediateLocalPath(const FGeneratorPackage::FGeneratedStruct& GeneratedStruct) const
|
|
{
|
|
FString UnusedPackagePath;
|
|
FString MountLocalFilePath;
|
|
GetIntermediateMountPoint(UnusedPackagePath, MountLocalFilePath);
|
|
FString Extension = FPaths::GetExtension(GeneratedStruct.PackageData->GetFileName().ToString(),
|
|
true /* bIncludeDot */);
|
|
return FPaths::RemoveDuplicateSlashes(FString::Printf(TEXT("%s/%s%s"),
|
|
*MountLocalFilePath, *GeneratedStruct.RelativePath, *Extension));
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// FPendingCookedPlatformData
|
|
|
|
|
|
FPendingCookedPlatformData::FPendingCookedPlatformData(UObject* InObject, const ITargetPlatform* InTargetPlatform,
|
|
FPackageData& InPackageData, bool bInNeedsResourceRelease, UCookOnTheFlyServer& InCookOnTheFlyServer)
|
|
: Object(InObject), TargetPlatform(InTargetPlatform), PackageData(InPackageData)
|
|
, CookOnTheFlyServer(InCookOnTheFlyServer), CancelManager(nullptr), ClassName(InObject->GetClass()->GetFName())
|
|
, bHasReleased(false), bNeedsResourceRelease(bInNeedsResourceRelease)
|
|
{
|
|
check(InObject);
|
|
PackageData.GetNumPendingCookedPlatformData() += 1;
|
|
}
|
|
|
|
FPendingCookedPlatformData::FPendingCookedPlatformData(FPendingCookedPlatformData&& Other)
|
|
: Object(Other.Object), TargetPlatform(Other.TargetPlatform), PackageData(Other.PackageData)
|
|
, CookOnTheFlyServer(Other.CookOnTheFlyServer), CancelManager(Other.CancelManager), ClassName(Other.ClassName)
|
|
, bHasReleased(Other.bHasReleased), bNeedsResourceRelease(Other.bNeedsResourceRelease)
|
|
{
|
|
Other.Object = nullptr;
|
|
}
|
|
|
|
FPendingCookedPlatformData::~FPendingCookedPlatformData()
|
|
{
|
|
Release();
|
|
}
|
|
|
|
bool FPendingCookedPlatformData::PollIsComplete()
|
|
{
|
|
if (bHasReleased)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
UObject* LocalObject = Object.Get();
|
|
if (!LocalObject)
|
|
{
|
|
Release();
|
|
return true;
|
|
}
|
|
UE_TRACK_REFERENCING_PACKAGE_SCOPED(LocalObject->GetPackage(), PackageAccessTrackingOps::NAME_CookerBuildObject);
|
|
if (RouteIsCachedCookedPlatformDataLoaded(LocalObject, TargetPlatform))
|
|
{
|
|
Release();
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
#if DEBUG_COOKONTHEFLY
|
|
UE_LOG(LogCook, Display, TEXT("Object %s isn't cached yet"), *LocalObject->GetFullName());
|
|
#endif
|
|
/*if ( LocalObject->IsA(UMaterial::StaticClass()) )
|
|
{
|
|
if (GShaderCompilingManager->HasShaderJobs() == false)
|
|
{
|
|
UE_LOG(LogCook, Warning, TEXT("Shader compiler is in a bad state! Shader %s is finished compile but shader compiling manager did not notify shader. "),
|
|
*LocalObject->GetPathName());
|
|
}
|
|
}*/
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void FPendingCookedPlatformData::Release()
|
|
{
|
|
if (bHasReleased)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (bNeedsResourceRelease)
|
|
{
|
|
int32* CurrentAsyncCache = CookOnTheFlyServer.CurrentAsyncCacheForType.Find(ClassName);
|
|
// bNeedsRelease should not have been set if the AsyncCache does not have an entry for the class
|
|
check(CurrentAsyncCache != nullptr);
|
|
*CurrentAsyncCache += 1;
|
|
}
|
|
|
|
PackageData.GetNumPendingCookedPlatformData() -= 1;
|
|
check(PackageData.GetNumPendingCookedPlatformData() >= 0);
|
|
if (CancelManager)
|
|
{
|
|
CancelManager->Release(*this);
|
|
CancelManager = nullptr;
|
|
}
|
|
|
|
Object = nullptr;
|
|
bHasReleased = true;
|
|
}
|
|
|
|
void FPendingCookedPlatformData::RemapTargetPlatforms(const TMap<ITargetPlatform*, ITargetPlatform*>& Remap)
|
|
{
|
|
TargetPlatform = Remap[TargetPlatform];
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// FPendingCookedPlatformDataCancelManager
|
|
|
|
|
|
void FPendingCookedPlatformDataCancelManager::Release(FPendingCookedPlatformData& Data)
|
|
{
|
|
--NumPendingPlatforms;
|
|
if (NumPendingPlatforms <= 0)
|
|
{
|
|
check(NumPendingPlatforms == 0);
|
|
UObject* LocalObject = Data.Object.Get();
|
|
if (LocalObject)
|
|
{
|
|
LocalObject->ClearAllCachedCookedPlatformData();
|
|
}
|
|
delete this;
|
|
}
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// FPackageDataMonitor
|
|
FPackageDataMonitor::FPackageDataMonitor()
|
|
{
|
|
FMemory::Memset(NumUrgentInState, 0);
|
|
}
|
|
|
|
int32 FPackageDataMonitor::GetNumUrgent() const
|
|
{
|
|
int32 NumUrgent = 0;
|
|
for (EPackageState State = EPackageState::Min;
|
|
State <= EPackageState::Max;
|
|
State = static_cast<EPackageState>(static_cast<uint32>(State) + 1))
|
|
{
|
|
NumUrgent += NumUrgentInState[static_cast<uint32>(State) - static_cast<uint32>(EPackageState::Min)];
|
|
}
|
|
return NumUrgent;
|
|
}
|
|
|
|
int32 FPackageDataMonitor::GetNumUrgent(EPackageState InState) const
|
|
{
|
|
check(EPackageState::Min <= InState && InState <= EPackageState::Max);
|
|
return NumUrgentInState[static_cast<uint32>(InState) - static_cast<uint32>(EPackageState::Min)];
|
|
}
|
|
|
|
int32 FPackageDataMonitor::GetNumPreloadAllocated() const
|
|
{
|
|
return NumPreloadAllocated;
|
|
}
|
|
|
|
int32 FPackageDataMonitor::GetNumInProgress() const
|
|
{
|
|
return NumInProgress;
|
|
}
|
|
|
|
int32 FPackageDataMonitor::GetNumCooked() const
|
|
{
|
|
return NumCooked;
|
|
}
|
|
|
|
void FPackageDataMonitor::OnInProgressChanged(FPackageData& PackageData, bool bInProgress)
|
|
{
|
|
NumInProgress += bInProgress ? 1 : -1;
|
|
check(NumInProgress >= 0);
|
|
}
|
|
|
|
void FPackageDataMonitor::OnPreloadAllocatedChanged(FPackageData& PackageData, bool bPreloadAllocated)
|
|
{
|
|
NumPreloadAllocated += bPreloadAllocated ? 1 : -1;
|
|
check(NumPreloadAllocated >= 0);
|
|
}
|
|
|
|
void FPackageDataMonitor::OnFirstCookedPlatformAdded(FPackageData& PackageData)
|
|
{
|
|
if (!PackageData.GetMonitorIsCooked())
|
|
{
|
|
++NumCooked;
|
|
PackageData.SetMonitorIsCooked(true);
|
|
}
|
|
}
|
|
|
|
void FPackageDataMonitor::OnLastCookedPlatformRemoved(FPackageData& PackageData)
|
|
{
|
|
if (PackageData.GetMonitorIsCooked())
|
|
{
|
|
--NumCooked;
|
|
PackageData.SetMonitorIsCooked(false);
|
|
}
|
|
}
|
|
|
|
void FPackageDataMonitor::OnUrgencyChanged(FPackageData& PackageData)
|
|
{
|
|
int32 Delta = PackageData.GetIsUrgent() ? 1 : -1;
|
|
TrackUrgentRequests(PackageData.GetState(), Delta);
|
|
}
|
|
|
|
void FPackageDataMonitor::OnStateChanged(FPackageData& PackageData, EPackageState OldState)
|
|
{
|
|
if (!PackageData.GetIsUrgent())
|
|
{
|
|
return;
|
|
}
|
|
|
|
TrackUrgentRequests(OldState, -1);
|
|
TrackUrgentRequests(PackageData.GetState(), 1);
|
|
}
|
|
|
|
void FPackageDataMonitor::TrackUrgentRequests(EPackageState State, int32 Delta)
|
|
{
|
|
check(EPackageState::Min <= State && State <= EPackageState::Max);
|
|
NumUrgentInState[static_cast<uint32>(State) - static_cast<uint32>(EPackageState::Min)] += Delta;
|
|
check(NumUrgentInState[static_cast<uint32>(State) - static_cast<uint32>(EPackageState::Min)] >= 0);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// FPackageDatas
|
|
|
|
IAssetRegistry* FPackageDatas::AssetRegistry = nullptr;
|
|
|
|
FPackageDatas::FPackageDatas(UCookOnTheFlyServer& InCookOnTheFlyServer)
|
|
: CookOnTheFlyServer(InCookOnTheFlyServer)
|
|
, LastPollAsyncTime(0)
|
|
{
|
|
}
|
|
|
|
void FPackageDatas::SetBeginCookConfigSettings()
|
|
{
|
|
FString FileOrPackageName;
|
|
ShowInstigatorPackageData = nullptr;
|
|
if (FParse::Value(FCommandLine::Get(), TEXT("-CookShowInstigator="), FileOrPackageName))
|
|
{
|
|
FString LocalPath;
|
|
FString PackageName;
|
|
if (!FPackageName::TryConvertToMountedPath(FileOrPackageName, &LocalPath, &PackageName, nullptr, nullptr, nullptr))
|
|
{
|
|
UE_LOG(LogCook, Fatal, TEXT("-CookShowInstigator argument %s is not a mounted filename or packagename"),
|
|
*FileOrPackageName);
|
|
}
|
|
else
|
|
{
|
|
FName PackageFName(*PackageName);
|
|
ShowInstigatorPackageData = TryAddPackageDataByPackageName(PackageFName);
|
|
if (!ShowInstigatorPackageData)
|
|
{
|
|
UE_LOG(LogCook, Fatal, TEXT("-CookShowInstigator argument %s could not be found on disk"),
|
|
*FileOrPackageName);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Discoveries during the processing of the initial cluster are expected, so LogDiscoveredPackages must be off.
|
|
SetLogDiscoveredPackages(false);
|
|
}
|
|
|
|
FPackageDatas::~FPackageDatas()
|
|
{
|
|
Clear();
|
|
}
|
|
|
|
void FPackageDatas::OnAssetRegistryGenerated(IAssetRegistry& InAssetRegistry)
|
|
{
|
|
AssetRegistry = &InAssetRegistry;
|
|
}
|
|
|
|
FString FPackageDatas::GetReferencerName() const
|
|
{
|
|
return TEXT("FPackageDatas");
|
|
}
|
|
|
|
void FPackageDatas::AddReferencedObjects(FReferenceCollector& Collector)
|
|
{
|
|
return CookOnTheFlyServer.CookerAddReferencedObjects(Collector);
|
|
}
|
|
|
|
FPackageDataMonitor& FPackageDatas::GetMonitor()
|
|
{
|
|
return Monitor;
|
|
}
|
|
|
|
UCookOnTheFlyServer& FPackageDatas::GetCookOnTheFlyServer()
|
|
{
|
|
return CookOnTheFlyServer;
|
|
}
|
|
|
|
FRequestQueue& FPackageDatas::GetRequestQueue()
|
|
{
|
|
return RequestQueue;
|
|
}
|
|
|
|
FPackageDataQueue& FPackageDatas::GetSaveQueue()
|
|
{
|
|
return SaveQueue;
|
|
}
|
|
|
|
FPackageData& FPackageDatas::FindOrAddPackageData(const FName& PackageName, const FName& NormalizedFileName)
|
|
{
|
|
{
|
|
FReadScopeLock ExistenceReadLock(ExistenceLock);
|
|
FPackageData** PackageDataMapAddr = PackageNameToPackageData.Find(PackageName);
|
|
if (PackageDataMapAddr != nullptr)
|
|
{
|
|
FPackageData** FileNameMapAddr = FileNameToPackageData.Find(NormalizedFileName);
|
|
checkf(FileNameMapAddr, TEXT("Package %s is being added with filename %s, but it already exists with filename %s, ")
|
|
TEXT("and it is not present in FileNameToPackageData map under the new name."),
|
|
*PackageName.ToString(), *NormalizedFileName.ToString(), *(*PackageDataMapAddr)->GetFileName().ToString());
|
|
checkf(*FileNameMapAddr == *PackageDataMapAddr,
|
|
TEXT("Package %s is being added with filename %s, but that filename maps to a different package %s."),
|
|
*PackageName.ToString(), *NormalizedFileName.ToString(), *(*FileNameMapAddr)->GetPackageName().ToString());
|
|
return **PackageDataMapAddr;
|
|
}
|
|
|
|
checkf(FileNameToPackageData.Find(NormalizedFileName) == nullptr,
|
|
TEXT("Package \"%s\" and package \"%s\" share the same filename \"%s\"."),
|
|
*PackageName.ToString(), *(*FileNameToPackageData.Find(NormalizedFileName))->GetPackageName().ToString(),
|
|
*NormalizedFileName.ToString());
|
|
}
|
|
return CreatePackageData(PackageName, NormalizedFileName);
|
|
}
|
|
|
|
FPackageData* FPackageDatas::FindPackageDataByPackageName(const FName& PackageName)
|
|
{
|
|
if (PackageName.IsNone())
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
FReadScopeLock ExistenceReadLock(ExistenceLock);
|
|
FPackageData** PackageDataMapAddr = PackageNameToPackageData.Find(PackageName);
|
|
return PackageDataMapAddr ? *PackageDataMapAddr : nullptr;
|
|
}
|
|
|
|
FPackageData* FPackageDatas::TryAddPackageDataByPackageName(const FName& PackageName, bool bRequireExists,
|
|
bool bCreateAsMap)
|
|
{
|
|
if (PackageName.IsNone())
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
{
|
|
FReadScopeLock ExistenceReadLock(ExistenceLock);
|
|
FPackageData** PackageDataMapAddr = PackageNameToPackageData.Find(PackageName);
|
|
if (PackageDataMapAddr != nullptr)
|
|
{
|
|
return *PackageDataMapAddr;
|
|
}
|
|
}
|
|
|
|
FName FileName = LookupFileNameOnDisk(PackageName, bRequireExists, bCreateAsMap);
|
|
if (FileName.IsNone())
|
|
{
|
|
// This will happen if PackageName does not exist on disk
|
|
return nullptr;
|
|
}
|
|
{
|
|
FReadScopeLock ExistenceReadLock(ExistenceLock);
|
|
checkf(FileNameToPackageData.Find(FileName) == nullptr,
|
|
TEXT("Package \"%s\" and package \"%s\" share the same filename \"%s\"."),
|
|
*PackageName.ToString(), *(*FileNameToPackageData.Find(FileName))->GetPackageName().ToString(),
|
|
*FileName.ToString());
|
|
}
|
|
return &CreatePackageData(PackageName, FileName);
|
|
}
|
|
|
|
FPackageData& FPackageDatas::AddPackageDataByPackageNameChecked(const FName& PackageName, bool bRequireExists,
|
|
bool bCreateAsMap)
|
|
{
|
|
FPackageData* PackageData = TryAddPackageDataByPackageName(PackageName, bRequireExists, bCreateAsMap);
|
|
check(PackageData);
|
|
return *PackageData;
|
|
}
|
|
|
|
FPackageData* FPackageDatas::FindPackageDataByFileName(const FName& InFileName)
|
|
{
|
|
FName FileName(GetStandardFileName(InFileName));
|
|
if (FileName.IsNone())
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
FReadScopeLock ExistenceReadLock(ExistenceLock);
|
|
FPackageData** PackageDataMapAddr = FileNameToPackageData.Find(FileName);
|
|
return PackageDataMapAddr ? *PackageDataMapAddr : nullptr;
|
|
}
|
|
|
|
FPackageData* FPackageDatas::TryAddPackageDataByFileName(const FName& InFileName)
|
|
{
|
|
return TryAddPackageDataByStandardFileName(GetStandardFileName(InFileName));
|
|
}
|
|
|
|
FPackageData* FPackageDatas::TryAddPackageDataByStandardFileName(const FName& FileName, bool bExactMatchRequired,
|
|
FName* OutFoundFileName)
|
|
{
|
|
FName FoundFileName = FileName;
|
|
ON_SCOPE_EXIT{ if (OutFoundFileName) { *OutFoundFileName = FoundFileName; } };
|
|
if (FileName.IsNone())
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
{
|
|
FReadScopeLock ExistenceReadLock(ExistenceLock);
|
|
FPackageData** PackageDataMapAddr = FileNameToPackageData.Find(FileName);
|
|
if (PackageDataMapAddr != nullptr)
|
|
{
|
|
return *PackageDataMapAddr;
|
|
}
|
|
}
|
|
|
|
FName ExistingFileName;
|
|
FName PackageName = LookupPackageNameOnDisk(FileName, bExactMatchRequired, ExistingFileName);
|
|
if (PackageName.IsNone())
|
|
{
|
|
return nullptr;
|
|
}
|
|
if (ExistingFileName.IsNone())
|
|
{
|
|
if (!bExactMatchRequired)
|
|
{
|
|
FReadScopeLock ExistenceReadLock(ExistenceLock);
|
|
FPackageData** PackageDataMapAddr = PackageNameToPackageData.Find(PackageName);
|
|
if (PackageDataMapAddr != nullptr)
|
|
{
|
|
FoundFileName = (*PackageDataMapAddr)->GetFileName();
|
|
return *PackageDataMapAddr;
|
|
}
|
|
}
|
|
UE_LOG(LogCook, Warning, TEXT("Unexpected failure to cook filename '%s'. It is mapped to PackageName '%s', but does not exist on disk and we cannot verify the extension."),
|
|
*FileName.ToString(), *PackageName.ToString());
|
|
return nullptr;
|
|
}
|
|
FoundFileName = ExistingFileName;
|
|
return &CreatePackageData(PackageName, ExistingFileName);
|
|
}
|
|
|
|
FPackageData& FPackageDatas::CreatePackageData(FName PackageName, FName FileName)
|
|
{
|
|
check(!PackageName.IsNone());
|
|
check(!FileName.IsNone());
|
|
FPackageData* PackageData = new FPackageData(*this, PackageName, FileName);
|
|
|
|
FWriteScopeLock ExistenceWriteLock(ExistenceLock);
|
|
FPackageData*& ExistingByPackageName = PackageNameToPackageData.FindOrAdd(PackageName);
|
|
FPackageData*& ExistingByFileName = FileNameToPackageData.FindOrAdd(FileName);
|
|
if (ExistingByPackageName)
|
|
{
|
|
// The other CreatePackageData call should have added the FileName as well
|
|
check(ExistingByFileName == ExistingByPackageName);
|
|
delete PackageData;
|
|
return *ExistingByPackageName;
|
|
}
|
|
// If no other CreatePackageData added the PackageName, then they should not have added
|
|
// the FileName either
|
|
check(!ExistingByFileName);
|
|
ExistingByPackageName = PackageData;
|
|
ExistingByFileName = PackageData;
|
|
PackageDatas.Add(PackageData);
|
|
return *PackageData;
|
|
}
|
|
|
|
FPackageData& FPackageDatas::AddPackageDataByFileNameChecked(const FName& FileName)
|
|
{
|
|
FPackageData* PackageData = TryAddPackageDataByFileName(FileName);
|
|
check(PackageData);
|
|
return *PackageData;
|
|
}
|
|
|
|
FName FPackageDatas::GetFileNameByPackageName(FName PackageName, bool bRequireExists, bool bCreateAsMap)
|
|
{
|
|
FPackageData* PackageData = TryAddPackageDataByPackageName(PackageName, bRequireExists, bCreateAsMap);
|
|
return PackageData ? PackageData->GetFileName() : NAME_None;
|
|
}
|
|
|
|
FName FPackageDatas::GetFileNameByFlexName(FName PackageOrFileName, bool bRequireExists, bool bCreateAsMap)
|
|
{
|
|
FString Buffer = PackageOrFileName.ToString();
|
|
if (!FPackageName::TryConvertFilenameToLongPackageName(Buffer, Buffer))
|
|
{
|
|
return NAME_None;
|
|
}
|
|
return GetFileNameByPackageName(FName(Buffer), bRequireExists, bCreateAsMap);
|
|
}
|
|
|
|
FName FPackageDatas::LookupFileNameOnDisk(FName PackageName, bool bRequireExists, bool bCreateAsMap)
|
|
{
|
|
FString FilenameOnDisk;
|
|
if (TryLookupFileNameOnDisk(PackageName, FilenameOnDisk))
|
|
{
|
|
}
|
|
else if (!bRequireExists)
|
|
{
|
|
FString Extension = bCreateAsMap ? FPackageName::GetMapPackageExtension() : FPackageName::GetAssetPackageExtension();
|
|
if (!FPackageName::TryConvertLongPackageNameToFilename(PackageName.ToString(), FilenameOnDisk, Extension))
|
|
{
|
|
return NAME_None;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return NAME_None;
|
|
}
|
|
FilenameOnDisk = FPaths::ConvertRelativePathToFull(FilenameOnDisk);
|
|
FPaths::MakeStandardFilename(FilenameOnDisk);
|
|
return FName(FilenameOnDisk);
|
|
}
|
|
|
|
bool FPackageDatas::TryLookupFileNameOnDisk(FName PackageName, FString& OutFileName)
|
|
{
|
|
FString PackageNameStr = PackageName.ToString();
|
|
|
|
// Verse packages are editor-generated in-memory packages which don't have a corresponding
|
|
// asset file (yet). However, we still want to cook these packages out, producing cooked
|
|
// asset files for packaged projects.
|
|
if (FPackageName::IsVersePackage(PackageNameStr))
|
|
{
|
|
if (FindPackage(/*Outer =*/nullptr, *PackageNameStr))
|
|
{
|
|
return FPackageName::TryConvertLongPackageNameToFilename(PackageNameStr, OutFileName,
|
|
FPackageName::GetAssetPackageExtension());
|
|
}
|
|
// else, the cooker could be responding to a NotifyUObjectCreated() event, and the object hasn't
|
|
// been fully constructed yet (missing from the FindObject() list) -- in this case, we've found
|
|
// that the linker loader is creating a dummy object to fill a referencing import slot, not loading
|
|
// the proper object (which means we want to ignore it).
|
|
}
|
|
|
|
if (!AssetRegistry)
|
|
{
|
|
return FPackageName::DoesPackageExist(PackageNameStr, &OutFileName, false /* InAllowTextFormats */);
|
|
}
|
|
else
|
|
{
|
|
FString PackageExtension;
|
|
if (!AssetRegistry->DoesPackageExistOnDisk(PackageName, nullptr, &PackageExtension))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return FPackageName::TryConvertLongPackageNameToFilename(PackageNameStr, OutFileName, PackageExtension);
|
|
}
|
|
}
|
|
|
|
FName FPackageDatas::LookupPackageNameOnDisk(FName NormalizedFileName, bool bExactMatchRequired, FName& FoundFileName)
|
|
{
|
|
FoundFileName = NormalizedFileName;
|
|
if (NormalizedFileName.IsNone())
|
|
{
|
|
return NAME_None;
|
|
}
|
|
FString Buffer = NormalizedFileName.ToString();
|
|
if (!FPackageName::TryConvertFilenameToLongPackageName(Buffer, Buffer))
|
|
{
|
|
return NAME_None;
|
|
}
|
|
FName PackageName = FName(*Buffer);
|
|
|
|
FName DiscoveredFileName = LookupFileNameOnDisk(PackageName, true /* bRequireExists */, false /* bCreateAsMap */);
|
|
if (DiscoveredFileName == NormalizedFileName || !bExactMatchRequired)
|
|
{
|
|
FoundFileName = DiscoveredFileName;
|
|
return PackageName;
|
|
}
|
|
else
|
|
{
|
|
// Either the file does not exist on disk or NormalizedFileName did not match its format or extension
|
|
return NAME_None;
|
|
}
|
|
}
|
|
|
|
FName FPackageDatas::GetStandardFileName(FName FileName)
|
|
{
|
|
FString FileNameString(FileName.ToString());
|
|
FPaths::MakeStandardFilename(FileNameString);
|
|
return FName(FileNameString);
|
|
}
|
|
|
|
FName FPackageDatas::GetStandardFileName(FStringView InFileName)
|
|
{
|
|
FString FileName(InFileName);
|
|
FPaths::MakeStandardFilename(FileName);
|
|
return FName(FileName);
|
|
}
|
|
|
|
void FPackageDatas::AddExistingPackageDatasForPlatform(TConstArrayView<FConstructPackageData> ExistingPackages,
|
|
const ITargetPlatform* TargetPlatform)
|
|
{
|
|
int32 NumPackages = ExistingPackages.Num();
|
|
|
|
// Make the list unique
|
|
TMap<FName, FName> UniquePackages;
|
|
UniquePackages.Reserve(NumPackages);
|
|
for (const FConstructPackageData& PackageToAdd : ExistingPackages)
|
|
{
|
|
FName& AddedFileName = UniquePackages.FindOrAdd(PackageToAdd.PackageName, PackageToAdd.NormalizedFileName);
|
|
check(AddedFileName == PackageToAdd.NormalizedFileName);
|
|
}
|
|
TArray<FConstructPackageData> UniqueArray;
|
|
if (UniquePackages.Num() != NumPackages)
|
|
{
|
|
NumPackages = UniquePackages.Num();
|
|
UniqueArray.Reserve(NumPackages);
|
|
for (TPair<FName, FName>& Pair : UniquePackages)
|
|
{
|
|
UniqueArray.Add(FConstructPackageData{ Pair.Key, Pair.Value });
|
|
}
|
|
ExistingPackages = UniqueArray;
|
|
}
|
|
|
|
// parallelize the read-only operations (and write NewPackageDataObjects by index which has no threading issues)
|
|
TArray<FPackageData*> NewPackageDataObjects;
|
|
NewPackageDataObjects.AddZeroed(NumPackages);
|
|
FWriteScopeLock ExistenceWriteLock(ExistenceLock);
|
|
ParallelFor(NumPackages,
|
|
[&ExistingPackages, TargetPlatform, &NewPackageDataObjects, this](int Index)
|
|
{
|
|
FName PackageName = ExistingPackages[Index].PackageName;
|
|
FName NormalizedFileName = ExistingPackages[Index].NormalizedFileName;
|
|
check(!PackageName.IsNone());
|
|
check(!NormalizedFileName.IsNone());
|
|
|
|
FPackageData** PackageDataMapAddr = FileNameToPackageData.Find(NormalizedFileName);
|
|
FPackageData* PackageData = nullptr;
|
|
if (PackageDataMapAddr != nullptr)
|
|
{
|
|
PackageData = *PackageDataMapAddr;
|
|
}
|
|
else
|
|
{
|
|
// create the package data and remember it for updating caches after the the ParallelFor
|
|
PackageData = new FPackageData(*this, PackageName, NormalizedFileName);
|
|
NewPackageDataObjects[Index] = PackageData;
|
|
}
|
|
PackageData->SetPlatformCooked(TargetPlatform, true /* Succeeded */);
|
|
});
|
|
|
|
// update cache for all newly created objects (copied from CreatePackageData)
|
|
for (FPackageData* PackageData : NewPackageDataObjects)
|
|
{
|
|
if (PackageData)
|
|
{
|
|
FPackageData* ExistingByFileName = FileNameToPackageData.Add(PackageData->FileName, PackageData);
|
|
// We looked up by FileName in the loop; it should still have been unset before the write we just did
|
|
check(ExistingByFileName == PackageData);
|
|
FPackageData* ExistingByPackageName = PackageNameToPackageData.FindOrAdd(PackageData->PackageName, PackageData);
|
|
// If no other CreatePackageData added the FileName, then they should not have added
|
|
// the PackageName either
|
|
check(ExistingByPackageName == PackageData);
|
|
PackageDatas.Add(PackageData);
|
|
}
|
|
}
|
|
}
|
|
|
|
FPackageData* FPackageDatas::UpdateFileName(FName PackageName)
|
|
{
|
|
FWriteScopeLock ExistenceWriteLock(ExistenceLock);
|
|
|
|
FPackageData** PackageDataAddr = PackageNameToPackageData.Find(PackageName);
|
|
if (!PackageDataAddr)
|
|
{
|
|
FName NewFileName = LookupFileNameOnDisk(PackageName);
|
|
check(NewFileName.IsNone() || !FileNameToPackageData.Find(NewFileName));
|
|
return nullptr;
|
|
}
|
|
FPackageData* PackageData = *PackageDataAddr;
|
|
FName OldFileName = PackageData->GetFileName();
|
|
bool bIsMap = FPackageName::IsMapPackageExtension(*FPaths::GetExtension(OldFileName.ToString()));
|
|
FName NewFileName = LookupFileNameOnDisk(PackageName, false /* bRequireExists */, bIsMap);
|
|
if (OldFileName == NewFileName)
|
|
{
|
|
return PackageData;
|
|
}
|
|
if (NewFileName.IsNone())
|
|
{
|
|
UE_LOG(LogCook, Error, TEXT("Cannot update FileName for package %s because the package is no longer mounted."),
|
|
*PackageName.ToString())
|
|
return PackageData;
|
|
}
|
|
|
|
check(!OldFileName.IsNone());
|
|
FPackageData* ExistingByFileName;
|
|
ensure(FileNameToPackageData.RemoveAndCopyValue(OldFileName, ExistingByFileName));
|
|
check(ExistingByFileName == PackageData);
|
|
|
|
PackageData->SetFileName(NewFileName);
|
|
FPackageData* AddedByFileName = FileNameToPackageData.FindOrAdd(NewFileName, PackageData);
|
|
check(AddedByFileName == PackageData);
|
|
|
|
return PackageData;
|
|
}
|
|
|
|
int32 FPackageDatas::GetNumCooked()
|
|
{
|
|
return Monitor.GetNumCooked();
|
|
}
|
|
|
|
void FPackageDatas::GetCookedPackagesForPlatform(const ITargetPlatform* Platform, TArray<FPackageData*>& CookedPackages,
|
|
bool bGetFailedCookedPackages, bool bGetSuccessfulCookedPackages)
|
|
{
|
|
for (FPackageData* PackageData : PackageDatas)
|
|
{
|
|
ECookResult CookResults = PackageData->GetCookResults(Platform);
|
|
if (((CookResults == ECookResult::Succeeded) & (bGetSuccessfulCookedPackages != 0)) |
|
|
((CookResults == ECookResult::Failed) & (bGetFailedCookedPackages != 0)))
|
|
{
|
|
CookedPackages.Add(PackageData);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FPackageDatas::Clear()
|
|
{
|
|
FWriteScopeLock ExistenceWriteLock(ExistenceLock);
|
|
PendingCookedPlatformDatas.Empty(); // These destructors will dereference PackageDatas
|
|
RequestQueue.Empty();
|
|
SaveQueue.Empty();
|
|
PackageNameToPackageData.Empty();
|
|
FileNameToPackageData.Empty();
|
|
for (FPackageData* PackageData : PackageDatas)
|
|
{
|
|
PackageData->ClearReferences();
|
|
}
|
|
for (FPackageData* PackageData : PackageDatas)
|
|
{
|
|
delete PackageData;
|
|
}
|
|
PackageDatas.Empty();
|
|
ShowInstigatorPackageData = nullptr;
|
|
}
|
|
|
|
void FPackageDatas::ClearCookedPlatforms()
|
|
{
|
|
for (FPackageData* PackageData : PackageDatas)
|
|
{
|
|
PackageData->ClearCookProgress();
|
|
}
|
|
}
|
|
|
|
void FPackageDatas::OnRemoveSessionPlatform(const ITargetPlatform* TargetPlatform)
|
|
{
|
|
for (FPackageData* PackageData : PackageDatas)
|
|
{
|
|
PackageData->OnRemoveSessionPlatform(TargetPlatform);
|
|
}
|
|
}
|
|
|
|
TArray<FPendingCookedPlatformData>& FPackageDatas::GetPendingCookedPlatformDatas()
|
|
{
|
|
return PendingCookedPlatformDatas;
|
|
}
|
|
|
|
void FPackageDatas::PollPendingCookedPlatformDatas(bool bForce)
|
|
{
|
|
if (PendingCookedPlatformDatas.Num() == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!bForce)
|
|
{
|
|
// ProcessAsyncResults and IsCachedCookedPlatformDataLoaded can be expensive to call
|
|
// Cap the frequency at which we call them.
|
|
double CurrentTime = FPlatformTime::Seconds();
|
|
if (CurrentTime < LastPollAsyncTime + GPollAsyncPeriod)
|
|
{
|
|
return;
|
|
}
|
|
LastPollAsyncTime = CurrentTime;
|
|
}
|
|
|
|
GShaderCompilingManager->ProcessAsyncResults(true /* bLimitExecutionTime */,
|
|
false /* bBlockOnGlobalShaderCompletion */);
|
|
FAssetCompilingManager::Get().ProcessAsyncTasks(true);
|
|
|
|
FPendingCookedPlatformData* Datas = PendingCookedPlatformDatas.GetData();
|
|
for (int Index = 0; Index < PendingCookedPlatformDatas.Num();)
|
|
{
|
|
if (Datas[Index].PollIsComplete())
|
|
{
|
|
PendingCookedPlatformDatas.RemoveAtSwap(Index, 1 /* Count */, false /* bAllowShrinking */);
|
|
}
|
|
else
|
|
{
|
|
++Index;
|
|
}
|
|
}
|
|
}
|
|
|
|
TArray<FPackageData*>::RangedForIteratorType FPackageDatas::begin()
|
|
{
|
|
return PackageDatas.begin();
|
|
}
|
|
|
|
TArray<FPackageData*>::RangedForIteratorType FPackageDatas::end()
|
|
{
|
|
return PackageDatas.end();
|
|
}
|
|
|
|
void FPackageDatas::RemapTargetPlatforms(const TMap<ITargetPlatform*, ITargetPlatform*>& Remap)
|
|
{
|
|
for (FPackageData* PackageData : PackageDatas)
|
|
{
|
|
PackageData->RemapTargetPlatforms(Remap);
|
|
}
|
|
for (FPendingCookedPlatformData& CookedPlatformData : PendingCookedPlatformDatas)
|
|
{
|
|
CookedPlatformData.RemapTargetPlatforms(Remap);
|
|
}
|
|
}
|
|
|
|
void FPackageDatas::DebugInstigator(FPackageData& PackageData)
|
|
{
|
|
if (ShowInstigatorPackageData != &PackageData)
|
|
{
|
|
return;
|
|
}
|
|
|
|
TArray<FInstigator> Chain = CookOnTheFlyServer.GetInstigatorChain(PackageData.GetPackageName());
|
|
TStringBuilder<256> ChainText;
|
|
if (Chain.Num() == 0)
|
|
{
|
|
ChainText << TEXT("<NoInstigator>");
|
|
}
|
|
bool bFirst = true;
|
|
for (FInstigator& Instigator : Chain)
|
|
{
|
|
if (!bFirst) ChainText << TEXT(" <- ");
|
|
ChainText << TEXT("{ ") << Instigator.ToString() << TEXT(" }");
|
|
bFirst = false;
|
|
}
|
|
UE_LOG(LogCook, Display, TEXT("Instigator chain of %s: %s"), *PackageData.GetPackageName().ToString(), ChainText.ToString());
|
|
}
|
|
|
|
void FRequestQueue::Empty()
|
|
{
|
|
NormalRequests.Empty();
|
|
UrgentRequests.Empty();
|
|
}
|
|
|
|
bool FRequestQueue::IsEmpty() const
|
|
{
|
|
return Num() == 0;
|
|
}
|
|
|
|
uint32 FRequestQueue::Num() const
|
|
{
|
|
uint32 Count = UnclusteredRequests.Num() + ReadyRequestsNum();
|
|
for (const FRequestCluster& RequestCluster : RequestClusters)
|
|
{
|
|
Count += RequestCluster.NumPackageDatas();
|
|
}
|
|
return Count;
|
|
}
|
|
|
|
bool FRequestQueue::Contains(const FPackageData* InPackageData) const
|
|
{
|
|
FPackageData* PackageData = const_cast<FPackageData*>(InPackageData);
|
|
if (UnclusteredRequests.Contains(PackageData) || NormalRequests.Contains(PackageData) || UrgentRequests.Contains(PackageData))
|
|
{
|
|
return true;
|
|
}
|
|
for (const FRequestCluster& RequestCluster : RequestClusters)
|
|
{
|
|
if (RequestCluster.Contains(PackageData))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
uint32 FRequestQueue::RemoveRequest(FPackageData* PackageData)
|
|
{
|
|
uint32 OriginalNum = Num();
|
|
UnclusteredRequests.Remove(PackageData);
|
|
NormalRequests.Remove(PackageData);
|
|
UrgentRequests.Remove(PackageData);
|
|
for (FRequestCluster& RequestCluster : RequestClusters)
|
|
{
|
|
RequestCluster.RemovePackageData(PackageData);
|
|
}
|
|
uint32 Result = OriginalNum - Num();
|
|
check(Result == 0 || Result == 1);
|
|
return Result;
|
|
}
|
|
|
|
uint32 FRequestQueue::Remove(FPackageData* PackageData)
|
|
{
|
|
return RemoveRequest(PackageData);
|
|
}
|
|
|
|
bool FRequestQueue::IsReadyRequestsEmpty() const
|
|
{
|
|
return ReadyRequestsNum() == 0;
|
|
}
|
|
|
|
uint32 FRequestQueue::ReadyRequestsNum() const
|
|
{
|
|
return UrgentRequests.Num() + NormalRequests.Num();
|
|
}
|
|
|
|
FPackageData* FRequestQueue::PopReadyRequest()
|
|
{
|
|
for (auto Iterator = UrgentRequests.CreateIterator(); Iterator; ++Iterator)
|
|
{
|
|
FPackageData* PackageData = *Iterator;
|
|
Iterator.RemoveCurrent();
|
|
return PackageData;
|
|
}
|
|
for (auto Iterator = NormalRequests.CreateIterator(); Iterator; ++Iterator)
|
|
{
|
|
FPackageData* PackageData = *Iterator;
|
|
Iterator.RemoveCurrent();
|
|
return PackageData;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void FRequestQueue::AddRequest(FPackageData* PackageData, bool bForceUrgent)
|
|
{
|
|
if (!PackageData->AreAllRequestedPlatformsExplored())
|
|
{
|
|
UnclusteredRequests.Add(PackageData);
|
|
}
|
|
else
|
|
{
|
|
AddReadyRequest(PackageData, bForceUrgent);
|
|
}
|
|
}
|
|
|
|
void FRequestQueue::AddReadyRequest(FPackageData* PackageData, bool bForceUrgent)
|
|
{
|
|
if (bForceUrgent || PackageData->GetIsUrgent())
|
|
{
|
|
UrgentRequests.Add(PackageData);
|
|
}
|
|
else
|
|
{
|
|
NormalRequests.Add(PackageData);
|
|
}
|
|
}
|
|
|
|
bool FLoadPrepareQueue::IsEmpty()
|
|
{
|
|
return Num() == 0;
|
|
}
|
|
|
|
int32 FLoadPrepareQueue::Num() const
|
|
{
|
|
return PreloadingQueue.Num() + EntryQueue.Num();
|
|
}
|
|
|
|
FPackageData* FLoadPrepareQueue::PopFront()
|
|
{
|
|
if (!PreloadingQueue.IsEmpty())
|
|
{
|
|
return PreloadingQueue.PopFrontValue();
|
|
}
|
|
else
|
|
{
|
|
return EntryQueue.PopFrontValue();
|
|
}
|
|
}
|
|
|
|
void FLoadPrepareQueue::Add(FPackageData* PackageData)
|
|
{
|
|
EntryQueue.Add(PackageData);
|
|
}
|
|
|
|
void FLoadPrepareQueue::AddFront(FPackageData* PackageData)
|
|
{
|
|
PreloadingQueue.AddFront(PackageData);
|
|
}
|
|
|
|
bool FLoadPrepareQueue::Contains(const FPackageData* PackageData) const
|
|
{
|
|
return (Algo::Find(PreloadingQueue, PackageData) != nullptr) ||
|
|
(Algo::Find(EntryQueue, PackageData) != nullptr);
|
|
}
|
|
|
|
uint32 FLoadPrepareQueue::Remove(FPackageData* PackageData)
|
|
{
|
|
return PreloadingQueue.Remove(PackageData) + EntryQueue.Remove(PackageData);
|
|
}
|
|
|
|
FPoppedPackageDataScope::FPoppedPackageDataScope(FPackageData& InPackageData)
|
|
#if COOK_CHECKSLOW_PACKAGEDATA
|
|
: PackageData(InPackageData)
|
|
#endif
|
|
{
|
|
}
|
|
|
|
#if COOK_CHECKSLOW_PACKAGEDATA
|
|
FPoppedPackageDataScope::~FPoppedPackageDataScope()
|
|
{
|
|
PackageData.CheckInContainer();
|
|
}
|
|
#endif
|
|
|
|
}
|