Files
UnrealEngineUWP/Engine/Source/Developer/Virtualization/Private/VirtualizationDDCBackend.cpp

197 lines
6.0 KiB
C++
Raw Normal View History

// Copyright Epic Games, Inc. All Rights Reserved.
#include "VirtualizationDDCBackend.h"
#include "Misc/Parse.h"
#include "DerivedDataCache.h"
#include "DerivedDataCacheRecord.h"
#include "DerivedDataRequestOwner.h"
#include "DerivedDataValue.h"
namespace UE::Virtualization
{
/** Utility function to help convert from UE::Virtualization::FIoHash to UE::DerivedData::FValueId */
static UE::DerivedData::FValueId ToDerivedDataValueId(const FIoHash& Id)
{
return UE::DerivedData::FValueId::FromHash(Id);
}
FDDCBackend::FDDCBackend(FStringView ProjectName, FStringView ConfigName, FStringView InDebugName)
: IVirtualizationBackend(ConfigName, InDebugName, EOperations::Push | EOperations::Pull)
, BucketName(TEXT("BulkData"))
, TransferPolicy(UE::DerivedData::ECachePolicy::None)
, QueryPolicy(UE::DerivedData::ECachePolicy::None)
{
}
bool FDDCBackend::Initialize(const FString& ConfigEntry)
{
TRACE_CPUPROFILER_EVENT_SCOPE(Initialize::Initialize);
FString BucketNameIniFile;
if(FParse::Value(*ConfigEntry, TEXT("Bucket="), BucketNameIniFile))
{
BucketName = BucketNameIniFile;
}
bool bAllowLocal = true;
FParse::Bool(*ConfigEntry, TEXT("LocalStorage="), bAllowLocal);
bool bAllowRemote = true;
FParse::Bool(*ConfigEntry, TEXT("RemoteStorage="), bAllowRemote);
UE_LOG(LogVirtualization, Log, TEXT("[%s] Bucket set to '%s"), *GetDebugName(), *BucketName);
UE_LOG(LogVirtualization, Log, TEXT("[%s] Use of local storage set to '%s"), *GetDebugName(), bAllowLocal ? TEXT("true") : TEXT("false"));
UE_LOG(LogVirtualization, Log, TEXT("[%s] Use of remote storage set to '%s"), *GetDebugName(), bAllowRemote ? TEXT("true") : TEXT("false"));
if (!bAllowLocal && !bAllowRemote)
{
UE_LOG(LogVirtualization, Error, TEXT("[%s] LocalStorage and RemoteStorage cannot both be disabled"), *GetDebugName());
return false;
}
if (bAllowLocal)
{
TransferPolicy |= UE::DerivedData::ECachePolicy::Local;
QueryPolicy |= UE::DerivedData::ECachePolicy::QueryLocal;
}
if (bAllowRemote)
{
TransferPolicy |= UE::DerivedData::ECachePolicy::Remote;
QueryPolicy |= UE::DerivedData::ECachePolicy::QueryRemote;
}
Bucket = UE::DerivedData::FCacheBucket(BucketName);
return true;
}
Add a number of ways for the VA backend connections to be changed to lazy initialize on first use. #rb Devin.Doucette #jira UE-161599 #rnx #preflight 6303c8d65a5d4e4624e7bf52 - There are some use cases that require the VA system to be initialized and configured correctly but would prefer that the backend connections only run if absolutely needed (usually when a payload is pulled or pushed for the first time), this change provides four different ways of doing this: -- Setting [Core.VirtualizationModule]LazyInitConnections=true in the Engine ini file -- Setting the define 'UE_VIRTUALIZATION_CONNECTION_LAZY_INIT' to 1 in a programs .target.cs -- Running with the commandline option -VA-LazyInitConnections -- Setting the cvar 'VA.LazyInitConnections' to 1 (only works if it is set before the VA system is initialized, changing it mid editor via the console does nothing) --- Note that after the config file, each setting there only opts into lazy initializing the connections, setting the cvar to 0 for example will not prevent the cmdline from opting in etc. - In the future we will allow the connection code to run async, so the latency can be hidden behind the editor loading, but for the current use case we are taking the minimal approach. -- This means we only support the backend being in 3 states. No connection has been made yet, the connection is broken and the connection is working. -- To keep things simple we only record if we have attempted to connect the backends or not. We don't check individual backends nor do we try to reconnect failed ones etc. This is all scheduled for a future work item. - If the connections are not initialized when the VA system is, we wait until the first time someone calls one of the virtualization methods that will actually use a connection: Push/Pull/Query -- We try connecting all of the backends at once, even if they won't be used in the call to keep things simple. - Only the source control backend makes use of the connection system. The horde storage (http) backend could take advantage too, but it is currently unused and most likely going to just be deleted so there seemed little point updating it. - If we try to run an operation on an unconnected backend we only log to verbose. This is to maintain existing behaviour where a failed backend would not be mounted at all. This logging will likely be revisited in a future work item. [CL 21511855 by paul chipchase in ue5-main branch]
2022-08-23 13:01:15 -04:00
IVirtualizationBackend::EConnectionStatus FDDCBackend::OnConnect()
{
return IVirtualizationBackend::EConnectionStatus::Connected;
}
bool FDDCBackend::PushData(TArrayView<FPushRequest> Requests, EPushFlags Flags)
{
TRACE_CPUPROFILER_EVENT_SCOPE(FDDCBackend::PushData);
UE::DerivedData::ICache& Cache = UE::DerivedData::GetCache();
UE::DerivedData::FRequestOwner Owner(UE::DerivedData::EPriority::Normal);
bool bWasSuccess = true;
const bool bEnableExistenceCheck = !EnumHasAllFlags(Flags, EPushFlags::Force);
// TODO: We tend not to memory bloat too much on large batches as the requests complete quite quickly
// however we might want to consider adding better control on how much total memory we can dedicate to
// loading payloads before we wait for requests to complete?
for (FPushRequest& Request : Requests)
{
if (bEnableExistenceCheck && DoesPayloadExist(Request.GetIdentifier()))
{
Request.SetResult(FPushResult::GetAsAlreadyExists());
}
else
{
UE::DerivedData::FRequestBarrier Barrier(Owner);
UE::DerivedData::FCacheKey Key;
Key.Bucket = Bucket;
Key.Hash = Request.GetIdentifier();
UE::DerivedData::FValue DerivedDataValue(Request.GetPayload());
check(DerivedDataValue.GetRawHash() == Request.GetIdentifier());
UE::DerivedData::FCacheRecordBuilder RecordBuilder(Key);
RecordBuilder.AddValue(ToDerivedDataValueId(Request.GetIdentifier()), DerivedDataValue);
UE::DerivedData::FCachePutResponse Result;
auto Callback = [&Request, &bWasSuccess](UE::DerivedData::FCachePutResponse&& Response)
{
if (Response.Status == UE::DerivedData::EStatus::Ok)
{
Request.SetResult(FPushResult::GetAsPushed());
}
else
{
Request.SetResult(FPushResult::GetAsError());
bWasSuccess = false;
}
};
// TODO: Improve the name when we start passing more context to this function
Cache.Put({ {{TEXT("Mirage")}, RecordBuilder.Build(), TransferPolicy} }, Owner, MoveTemp(Callback));
}
}
Owner.Wait();
return bWasSuccess;
}
bool FDDCBackend::PullData(TArrayView<FPullRequest> Requests, EPullFlags Flags, FText& OutErrors)
{
TRACE_CPUPROFILER_EVENT_SCOPE(FDDCBackend::PullData);
UE::DerivedData::ICache& Cache = UE::DerivedData::GetCache();
UE::DerivedData::FRequestOwner Owner(UE::DerivedData::EPriority::Normal);
for (FPullRequest& Request : Requests)
{
UE::DerivedData::FRequestBarrier Barrier(Owner);
UE::DerivedData::FCacheKey Key;
Key.Bucket = Bucket;
Key.Hash = Request.GetIdentifier();
auto Callback = [&Request](UE::DerivedData::FCacheGetResponse&& Response)
{
if (Response.Status == UE::DerivedData::EStatus::Ok)
{
UE::DerivedData::FValueId ValueId = ToDerivedDataValueId(Request.GetIdentifier());
Request.SetPayload(Response.Record.GetValue(ValueId).GetData());
}
};
// TODO: Improve the name when we start passing more context to this function
Cache.Get({ {{TEXT("Mirage")}, Key, TransferPolicy} }, Owner, MoveTemp(Callback));
}
Owner.Wait();
return true;
}
bool FDDCBackend::DoesPayloadExist(const FIoHash& Id)
{
TRACE_CPUPROFILER_EVENT_SCOPE(FDDCBackend::DoesPayloadExist);
UE::DerivedData::ICache& Cache = UE::DerivedData::GetCache();
UE::DerivedData::FCacheKey Key;
Key.Bucket = Bucket;
Key.Hash = Id;
UE::DerivedData::FRequestOwner Owner(UE::DerivedData::EPriority::Blocking);
UE::DerivedData::EStatus ResultStatus;
auto Callback = [&ResultStatus](UE::DerivedData::FCacheGetResponse&& Response)
{
ResultStatus = Response.Status;
};
// TODO: Improve the name when we start passing more context to this function
Cache.Get({{{TEXT("Mirage")}, Key, QueryPolicy | UE::DerivedData::ECachePolicy::SkipData}}, Owner, MoveTemp(Callback));
Owner.Wait();
return ResultStatus == UE::DerivedData::EStatus::Ok;
}
UE_REGISTER_VIRTUALIZATION_BACKEND_FACTORY(FDDCBackend, DDCBackend);
} // namespace UE::Virtualization