Files
UnrealEngine/Engine/Source/Editor/UnrealEd/Private/Cooker/CookRequests.cpp
2025-05-18 13:04:45 +08:00

346 lines
9.5 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "CookRequests.h"
#include "Algo/Find.h"
#include "CookOnTheSide/CookLog.h"
#include "CookPackageData.h"
#include "CookPlatformManager.h"
#include "Cooker/CookTypes.h"
#include "HAL/Platform.h"
#include "Interfaces/ITargetPlatform.h"
#include "Logging/LogCategory.h"
#include "Logging/LogMacros.h"
#include "Misc/AssertionMacros.h"
#include "Misc/ScopeLock.h"
#include "Templates/UnrealTemplate.h"
#include "Trace/Detail/Channel.h"
#include "UObject/UnrealNames.h"
namespace UE::Cook
{
//////////////////////////////////////////////////////////////////////////
// FFilePlatformRequest
FFilePlatformRequest::FFilePlatformRequest(const FName& InFilename, FInstigator&& InInstigator)
:FFilePlatformRequest(InFilename, MoveTemp(InInstigator),
TArray<const ITargetPlatform*>(), FCompletionCallback())
{
}
FFilePlatformRequest::FFilePlatformRequest(const FName& InFilename, FInstigator&& InInstigator,
const ITargetPlatform* InPlatform, FCompletionCallback&& InCompletionCallback)
:FFilePlatformRequest(InFilename, MoveTemp(InInstigator),
TArrayView<const ITargetPlatform* const>({ InPlatform }), MoveTemp(InCompletionCallback))
{
}
FFilePlatformRequest::FFilePlatformRequest(const FName& InFilename, FInstigator&& InInstigator,
const TArrayView<const ITargetPlatform* const>& InPlatforms, FCompletionCallback&& InCompletionCallback)
:FFilePlatformRequest(InFilename, MoveTemp(InInstigator),
TArray<const ITargetPlatform*>(InPlatforms.GetData(), InPlatforms.Num()), MoveTemp(InCompletionCallback))
{
}
FFilePlatformRequest::FFilePlatformRequest(const FName& InFilename, FInstigator&& InInstigator,
TArray<const ITargetPlatform*>&& InPlatforms, FCompletionCallback&& InCompletionCallback)
: Platforms(MoveTemp(InPlatforms))
, CompletionCallback(MoveTemp(InCompletionCallback))
, Instigator(MoveTemp(InInstigator))
{
SetFilename(InFilename.ToString());
}
FFilePlatformRequest::FFilePlatformRequest(const FFilePlatformRequest& InFilePlatformRequest)
: Filename(InFilePlatformRequest.Filename)
, Platforms(InFilePlatformRequest.Platforms)
, Instigator(InFilePlatformRequest.Instigator)
, bUrgent(InFilePlatformRequest.bUrgent)
{
check(!InFilePlatformRequest.CompletionCallback); // CompletionCallbacks can not be copied, so the caller's intent is not clear in this constructor if the input has one
}
FFilePlatformRequest::FFilePlatformRequest(FFilePlatformRequest&& InFilePlatformRequest)
: Filename(MoveTemp(InFilePlatformRequest.Filename))
, Platforms(MoveTemp(InFilePlatformRequest.Platforms))
, CompletionCallback(MoveTemp(InFilePlatformRequest.CompletionCallback))
, Instigator(MoveTemp(InFilePlatformRequest.Instigator))
, bUrgent(InFilePlatformRequest.bUrgent)
{
}
FFilePlatformRequest& FFilePlatformRequest::operator=(FFilePlatformRequest&& InFileRequest)
{
Filename = MoveTemp(InFileRequest.Filename);
Platforms = MoveTemp(InFileRequest.Platforms);
check(!CompletionCallback); // We don't support holding multiple completion callbacks
CompletionCallback = MoveTemp(InFileRequest.CompletionCallback);
Instigator = MoveTemp(InFileRequest.Instigator);
bUrgent = InFileRequest.bUrgent;
return *this;
}
void FFilePlatformRequest::SetFilename(FString InFilename)
{
Filename = FPackageDatas::GetStandardFileName(InFilename);
}
const FName& FFilePlatformRequest::GetFilename() const
{
return Filename;
}
FInstigator& FFilePlatformRequest::GetInstigator()
{
return Instigator;
}
const TArray<const ITargetPlatform*>& FFilePlatformRequest::GetPlatforms() const
{
return Platforms;
}
TArray<const ITargetPlatform*>& FFilePlatformRequest::GetPlatforms()
{
return Platforms;
}
void FFilePlatformRequest::RemovePlatform(const ITargetPlatform* Platform)
{
Platforms.Remove(Platform);
}
void FFilePlatformRequest::AddPlatform(const ITargetPlatform* Platform)
{
check(Platform != nullptr);
Platforms.Add(Platform);
}
bool FFilePlatformRequest::HasPlatform(const ITargetPlatform* Platform) const
{
return Platforms.Find(Platform) != INDEX_NONE;
}
FCompletionCallback& FFilePlatformRequest::GetCompletionCallback()
{
return CompletionCallback;
}
bool FFilePlatformRequest::IsValid() const
{
return Filename != NAME_None;
}
void FFilePlatformRequest::Clear()
{
Filename = TEXT("");
Platforms.Empty();
}
bool FFilePlatformRequest::operator==(const FFilePlatformRequest& InFileRequest) const
{
if (InFileRequest.Filename == Filename)
{
if (InFileRequest.Platforms == Platforms)
{
return true;
}
}
return false;
}
FString FFilePlatformRequest::ToString() const
{
FString Result = FString::Printf(TEXT("%s;"), *Filename.ToString());
for (const ITargetPlatform* Platform : Platforms)
{
Result += FString::Printf(TEXT("%s,"), *Platform->PlatformName());
}
return Result;
}
void FFilePlatformRequest::RemapTargetPlatforms(const TMap<ITargetPlatform*, ITargetPlatform*>& Remap)
{
RemapArrayElements(Platforms, Remap);
}
//////////////////////////////////////////////////////////////////////////
// FExternalRequests
int32 FExternalRequests::GetNumRequests() const
{
return RequestCount.load(std::memory_order_relaxed);
}
bool FExternalRequests::HasRequests() const
{
return GetNumRequests() > 0;
}
void FExternalRequests::AddCallback(FSchedulerCallback&& Callback)
{
FScopeLock ScopeLock(&RequestLock);
Callbacks.Add(MoveTemp(Callback));
RequestCount.fetch_add(1, std::memory_order_relaxed);
}
void FExternalRequests::EnqueueUnique(FFilePlatformRequest&& FileRequest, bool bForceFrontOfQueue)
{
FScopeLock ScopeLock(&RequestLock);
FName Filename = FileRequest.GetFilename();
FFilePlatformRequest* ExistingRequest = RequestMap.Find(Filename);
if (!ExistingRequest)
{
RequestMap.Add(Filename, MoveTemp(FileRequest));
if (bForceFrontOfQueue)
{
Queue.AddFront(Filename);
}
else
{
Queue.Add(Filename);
}
RequestCount.fetch_add(1, std::memory_order_relaxed);
}
else
{
if (FileRequest.GetCompletionCallback())
{
check(!ExistingRequest->GetCompletionCallback()); // We don't support multiple callbacks
ExistingRequest->GetCompletionCallback() = MoveTemp(FileRequest.GetCompletionCallback());
}
// add the requested platforms to the platform list
for (const ITargetPlatform* Platform : FileRequest.GetPlatforms())
{
ExistingRequest->GetPlatforms().AddUnique(Platform);
}
if (bForceFrontOfQueue)
{
FName* ExistingName = Algo::Find(Queue, Filename);
int32 Index = Queue.ConvertPointerToIndex(ExistingName);
check(Index != INDEX_NONE);
if (Index != 0)
{
Queue[Index] = Queue[0];
Queue[0] = Filename;
}
}
}
}
EExternalRequestType FExternalRequests::DequeueNextCluster(TArray<FSchedulerCallback>& OutCallbacks, TArray<FFilePlatformRequest>& OutBuildRequests)
{
FScopeLock ScopeLock(&RequestLock);
if (ThreadUnsafeDequeueCallbacks(OutCallbacks))
{
return EExternalRequestType::Callback;
}
else if (Queue.Num())
{
OutBuildRequests.Reserve(Queue.Num() + OutBuildRequests.Num());
int32 NumDequeued = Queue.Num();
while (Queue.Num())
{
FName Filename = Queue.PopFrontValue();
OutBuildRequests.Add(RequestMap.FindAndRemoveChecked(Filename));
}
RequestCount.fetch_add(-NumDequeued, std::memory_order_relaxed);
return EExternalRequestType::Cook;
}
else
{
return EExternalRequestType::None;
}
}
bool FExternalRequests::DequeueCallbacks(TArray<FSchedulerCallback>& OutCallbacks)
{
FScopeLock ScopeLock(&RequestLock);
return ThreadUnsafeDequeueCallbacks(OutCallbacks);
}
bool FExternalRequests::ThreadUnsafeDequeueCallbacks(TArray<FSchedulerCallback>& OutCallbacks)
{
if (Callbacks.Num() > 0)
{
OutCallbacks = MoveTemp(Callbacks);
Callbacks.Empty();
RequestCount.fetch_add(-OutCallbacks.Num(), std::memory_order_relaxed);
return true;
}
else
{
return false;
}
}
void FExternalRequests::EmptyRequests()
{
FScopeLock ScopeLock(&RequestLock);
Queue.Empty();
RequestMap.Empty();
Callbacks.Empty();
RequestCount.store(0, std::memory_order_relaxed);
}
void FExternalRequests::DequeueAll(TArray<FSchedulerCallback>& OutCallbacks, TArray<FFilePlatformRequest>& OutCookRequests)
{
FScopeLock ScopeLock(&RequestLock);
OutCallbacks = MoveTemp(Callbacks);
Callbacks.Empty();
for (TPair<FName, FFilePlatformRequest>& Request : RequestMap)
{
OutCookRequests.Add(MoveTemp(Request.Value));
}
RequestMap.Empty();
Queue.Empty();
RequestCount.store(0, std::memory_order_relaxed);
}
void FExternalRequests::OnRemoveSessionPlatform(const ITargetPlatform* TargetPlatform)
{
FScopeLock ScopeLock(&RequestLock);
// The caller should not be removing platforms if we have an active request referencing that platform, but in case they did, remove the platform
// from all pending requests
for (TPair<FName, FFilePlatformRequest>& kvpair : RequestMap)
{
FFilePlatformRequest& Request = kvpair.Value;
Request.GetPlatforms().Remove(TargetPlatform);
if (Request.GetPlatforms().Num() == 0)
{
UE_LOG(LogCook, Error, TEXT("RemovePlatform call has left an empty list of platforms requested in CookOnTheSide request."));
}
}
}
void FExternalRequests::RemapTargetPlatforms(const TMap<ITargetPlatform*, ITargetPlatform*>& Remap)
{
for (TPair<FName, FFilePlatformRequest>& KVPair : RequestMap)
{
KVPair.Value.RemapTargetPlatforms(Remap);
}
}
void FExternalRequests::LogAllRequestedFiles()
{
UE_LOG(LogCook, Display, TEXT("Listing initially requested set of files to be cooked (does not include list of transitive dependencies):"));
for (const TPair<FName, FFilePlatformRequest>& KVPair : RequestMap)
{
UE_LOG(LogCook, Display, TEXT("%s"), *KVPair.Value.ToString());
}
UE_LOG(LogCook, Display, TEXT("----------------------------------------------------------\n\n"));
}
}