Files
UnrealEngine/Engine/Source/Developer/DerivedDataCache/Private/DerivedDataBuildJobContext.cpp
2025-05-18 13:04:45 +08:00

239 lines
7.7 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "DerivedDataBuildJobContext.h"
#include "Containers/StringConv.h"
#include "DerivedDataBuildJob.h"
#include "DerivedDataBuildOutput.h"
#include "DerivedDataBuildPrivate.h"
#include "DerivedDataBuildTypes.h"
#include "DerivedDataCache.h"
#include "DerivedDataRequestOwner.h"
#include "DerivedDataValue.h"
#include "Hash/Blake3.h"
#include "Misc/AsciiSet.h"
#include "Misc/StringBuilder.h"
#include "UObject/NameTypes.h"
namespace UE::DerivedData::Private
{
FBuildJobContext::FBuildJobContext(
IBuildJob& InJob,
const FCacheKey& InCacheKey,
const IBuildFunction& InFunction,
FBuildOutputBuilder& InOutputBuilder)
: Job(InJob)
, CacheKey(InCacheKey)
, TypeName(InFunction.GetName())
, Function(InFunction)
, OutputBuilder(InOutputBuilder)
, CachePolicyMask(~ECachePolicy::None)
, BuildPolicyMask(~EBuildPolicy::None)
{
}
const FSharedString& FBuildJobContext::GetName() const
{
return Job.GetName();
}
void FBuildJobContext::AddConstant(FUtf8StringView Key, FCbObject&& Value)
{
Constants.EmplaceByHash(GetTypeHash(Key), Key, MoveTemp(Value));
}
void FBuildJobContext::AddInput(FUtf8StringView Key, const FCompressedBuffer& Value)
{
Inputs.EmplaceByHash(GetTypeHash(Key), Key, Value);
}
void FBuildJobContext::AddError(FStringView Message)
{
OutputBuilder.AddMessage({FTCHARToUTF8(Message), EBuildOutputMessageLevel::Error});
}
void FBuildJobContext::AddWarning(FStringView Message)
{
OutputBuilder.AddMessage({FTCHARToUTF8(Message), EBuildOutputMessageLevel::Warning});
}
void FBuildJobContext::AddMessage(FStringView Message)
{
OutputBuilder.AddMessage({FTCHARToUTF8(Message), EBuildOutputMessageLevel::Display});
}
void FBuildJobContext::ResetInputs()
{
Constants.Empty();
Inputs.Empty();
}
FCbObject FBuildJobContext::FindConstant(FUtf8StringView Key) const
{
if (const FCbObject* Object = Constants.FindByHash(GetTypeHash(Key), Key))
{
return *Object;
}
return FCbObject();
}
FSharedBuffer FBuildJobContext::FindInput(FUtf8StringView Key) const
{
if (const FCompressedBuffer* Input = Inputs.FindByHash(GetTypeHash(Key), Key))
{
FSharedBuffer Buffer = Input->Decompress();
const FBlake3Hash RawHash = FBlake3::HashBuffer(Buffer);
if (RawHash == Input->GetRawHash() && Buffer.GetSize() == Input->GetRawSize())
{
return Buffer;
}
else
{
TStringBuilder<256> Error;
Error << TEXT("Input '") << Key << TEXT("' was expected to have raw hash ") << Input->GetRawHash()
<< TEXT(" and raw size ") << Input->GetRawSize() << TEXT(" but has raw hash ") << RawHash
<< TEXT(" and raw size ") << Buffer.GetSize() << TEXT(" after decompression for build of '")
<< Job.GetName() << TEXT("' by ") << Job.GetFunction() << TEXT(".");
OutputBuilder.AddLog({ANSITEXTVIEW("LogDerivedDataBuild"), FTCHARToUTF8(Error), EBuildOutputLogLevel::Error});
UE_LOG(LogDerivedDataBuild, Error, TEXT("%.*s"), Error.Len(), Error.GetData());
}
}
return FSharedBuffer();
}
void FBuildJobContext::AddMeta(FUtf8StringView Key, const FCbField& Meta)
{
constexpr FAsciiSet Valid("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
checkf(!Key.IsEmpty() && FAsciiSet::HasOnly(Key, Valid),
TEXT("Key '%s' for metadata must be alphanumeric and non-empty."), *WriteToString<32>(Key));
OutputBuilder.AddMeta(Key, Meta);
}
void FBuildJobContext::AddValue(const FValueId& Id, const FValue& Value)
{
OutputBuilder.AddValue(Id, Value);
}
void FBuildJobContext::AddValue(const FValueId& Id, const FCompressedBuffer& Buffer)
{
AddValue(Id, FValue(Buffer));
}
void FBuildJobContext::AddValue(const FValueId& Id, const FCompositeBuffer& Buffer, const uint64 BlockSize)
{
AddValue(Id, FValue::Compress(Buffer, BlockSize));
}
void FBuildJobContext::AddValue(const FValueId& Id, const FSharedBuffer& Buffer, const uint64 BlockSize)
{
AddValue(Id, FValue::Compress(Buffer, BlockSize));
}
void FBuildJobContext::AddValue(const FValueId& Id, const FCbObject& Object)
{
AddValue(Id, FValue::Compress(Object.GetBuffer()));
}
void FBuildJobContext::BeginBuild(IRequestOwner& InOwner, TUniqueFunction<void ()>&& InOnEndBuild)
{
Owner = &InOwner;
OnEndBuild = MoveTemp(InOnEndBuild);
Function.Build(*this);
if (!bIsAsyncBuild)
{
EndBuild();
}
}
void FBuildJobContext::EndBuild()
{
OnEndBuild();
BuildCompleteEvent.Notify();
Owner = nullptr;
}
void FBuildJobContext::BeginAsyncBuild()
{
checkf(!bIsAsyncBuild, TEXT("BeginAsyncBuild may only be called once for build of '%s' by %s."),
*Job.GetName(), *WriteToString<32>(Job.GetFunction()));
bIsAsyncBuild = true;
Owner->Begin(this);
}
void FBuildJobContext::EndAsyncBuild()
{
checkf(bIsAsyncBuild, TEXT("EndAsyncBuild may only be called after BeginAsyncBuild for build of '%s' by %s."),
*Job.GetName(), *WriteToString<32>(Job.GetFunction()));
checkf(!bIsAsyncBuildComplete, TEXT("EndAsyncBuild may only be called once for build of '%s' by %s."),
*Job.GetName(), *WriteToString<32>(Job.GetFunction()));
bIsAsyncBuildComplete = true;
Owner->End(this, [this] { EndBuild(); });
}
void FBuildJobContext::SetTypeName(const FUtf8SharedString& InTypeName)
{
checkf(!TypeName.IsEmpty(), TEXT("Empty type name not allowed for build of '%s' by %s."),
*Job.GetName(), *WriteToString<32>(Job.GetFunction()));
TypeName = InTypeName;
}
void FBuildJobContext::SetCacheBucket(FCacheBucket Bucket)
{
checkf(!Bucket.IsNull(), TEXT("Null cache bucket not allowed for build of '%s' by %s. "
"The cache can be disabled by calling SetCachePolicyMask(~ECachePolicy::Default)."),
*Job.GetName(), *WriteToString<32>(Job.GetFunction()));
CacheKey.Bucket = Bucket;
}
void FBuildJobContext::SetCachePolicyMask(ECachePolicy Policy)
{
checkf(EnumHasAllFlags(Policy, ECachePolicy::SkipData),
TEXT("SkipData flags may not be masked out on the cache policy for build of '%s' by %s. "
"Flags for skipping data may be set indirectly through FBuildPolicy."),
*Job.GetName(), *WriteToString<32>(Job.GetFunction()));
checkf(EnumHasAllFlags(Policy, ECachePolicy::KeepAlive),
TEXT("KeepAlive flag may not be masked out on the cache policy for build of '%s' by %s. "
"Flags for cache record lifetime may be set indirectly through FBuildPolicy."),
*Job.GetName(), *WriteToString<32>(Job.GetFunction()));
checkf(EnumHasAllFlags(Policy, ECachePolicy::PartialRecord),
TEXT("PartialRecord flag may not be masked out on the cache policy for build of '%s' by %s."),
*Job.GetName(), *WriteToString<32>(Job.GetFunction()));
CachePolicyMask = Policy;
}
void FBuildJobContext::SetBuildPolicyMask(EBuildPolicy Policy)
{
checkf(EnumHasAllFlags(Policy, EBuildPolicy::Cache),
TEXT("Cache flags may not be masked out on the build policy for build of '%s' by %s. "
"Flags for modifying cache operations may be set through SetCachePolicyMask."),
*Job.GetName(), *WriteToString<32>(Job.GetFunction()));
checkf(EnumHasAllFlags(Policy, EBuildPolicy::CacheKeepAlive),
TEXT("CacheKeepAlive flag may not be masked out on the build policy for build of '%s' by %s. "
"Flags for cache record lifetime may only be set through the build session."),
*Job.GetName(), *WriteToString<32>(Job.GetFunction()));
checkf(EnumHasAllFlags(Policy, EBuildPolicy::SkipData),
TEXT("SkipData flag may not be masked out on the build policy for build of '%s' by %s. "
"Flags for skipping the data may only be set through the build session."),
*Job.GetName(), *WriteToString<32>(Job.GetFunction()));
BuildPolicyMask = Policy;
}
void FBuildJobContext::SetPriority(EPriority Priority)
{
}
void FBuildJobContext::Cancel()
{
checkf(bIsAsyncBuild, TEXT("Cancel may only be called after BeginAsyncBuild for build of '%s' by %s."),
*Job.GetName(), *WriteToString<32>(Job.GetFunction()));
Function.CancelAsyncBuild(*this);
}
void FBuildJobContext::Wait()
{
BuildCompleteEvent.Wait();
}
} // UE::DerivedData::Private