Files
UnrealEngineUWP/Engine/Source/Developer/Virtualization/Private/VirtualizationFileBackend.cpp
paul chipchase 71b969d9c4 Cherrypicking Mirage code base (disabled) from Dev-Cooker
- The mirage specific code is disabled behind the define UE_USE_VIRTUALBULKDATA, this means that some code paths in Texture/Mesh are much more complex than they need to be as we support both old and new paths. Once the system has been turned on and confirmed to cause no issues then this will be stripped out.
- SavePackageUtilities.cpp, SavePackage.cpp and SavePackage2.cpp are editgrates rather than integrations as those files have changes in DevCooker that we don't want to bring over immediately.
- Also includes a prototype system for storing bulkdata in a sidecar file in the workspace domain rather than in the .uasset/.umap file which although has been discontinued as part of mirage, will have applications for future work for non-virtualized projects and/or text based assets.

#rb Patrick.Finegan (all changes have been reviewed when submitted to Dev-Cooker)
#tests Cooking and running ShooterGame/Frosty and other sample programs using megascan assets
#rnx
#preflight  608be50d870cf400013ff99d

[CL 16167285 by paul chipchase in ue5-main branch]
2021-04-30 08:14:54 -04:00

155 lines
4.7 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Virtualization/IVirtualizationBackend.h"
#include "Containers/StringView.h"
#include "HAL/FileManager.h"
#include "Misc/Parse.h"
#include "Misc/Paths.h"
#include "Virtualization/VirtualizationManager.h"
namespace UE::Virtualization
{
/**
* A basic backend based on the file system. This can be used to access/store virtualization
* data either on a local disk or a network share. It is intended to be used as a caching system
* to speed up operations (running a local cache or a shared cache for a site) rather than as the
* proper backend solution.
*
* Ini file setup:
* 'Name'=(Type=FileSystem, Path="XXX")
* Where 'Name' is the backend name in the hierarchy and 'XXX' if the path to the directory where
* you want to files to be stored.
*/
class FFileSystemBackend : public IVirtualizationBackend
{
public:
FFileSystemBackend(FStringView ConfigName)
: IVirtualizationBackend(EOperations::Both)
{
Name = TEXT("FFileSystemBackend - ");
Name.Append(ConfigName);
}
virtual ~FFileSystemBackend() = default;
private:
virtual bool Initialize(const FString& ConfigEntry) override
{
if (!FParse::Value(*ConfigEntry, TEXT("Path="), RootDirectory))
{
UE_LOG(LogVirtualization, Error, TEXT("[%s] 'Path=' not found in the config file"), *GetDebugString());
return false;
}
FPaths::NormalizeDirectoryName(RootDirectory);
if (RootDirectory.IsEmpty())
{
UE_LOG(LogVirtualization, Error, TEXT("[%s] Config file entry 'Path=' was empty"), *GetDebugString());
return false;
}
// TODO: Validate that the given path is usable?
UE_LOG(LogVirtualization, Log, TEXT("[%s] Using path: '%s'"), *GetDebugString(), *RootDirectory);
return true;
}
virtual bool PushData(const FPayloadId& Id, const FCompressedBuffer& Payload) override
{
TRACE_CPUPROFILER_EVENT_SCOPE(FFileSystemBackend::PushData);
if (DoesExist(Id))
{
UE_LOG(LogVirtualization, Verbose, TEXT("[%s] Already has a copy of the payload '%s'."), *GetDebugString(), *Id.ToString());
return true;
}
const FString FilePath = CreateFilePath(Id);
{
// TODO: Should we write to a temp file and then move it once it has written?
TUniquePtr<FArchive> FileAr(IFileManager::Get().CreateFileWriter(*FilePath));
if (FileAr == nullptr)
{
UE_LOG(LogVirtualization, Error, TEXT("[%s] Failed to push payload '%s' to '%s'"), *GetDebugString(), *Id.ToString(), *FilePath);
return false;
}
for (const FSharedBuffer& Buffer : Payload.GetCompressed().GetSegments())
{
// Const cast because FArchive requires a non-const pointer!
FileAr->Serialize(const_cast<void*>(Buffer.GetData()), static_cast<int64>(Buffer.GetSize()));
}
}
return true;
}
virtual FCompressedBuffer PullData(const FPayloadId& Id) override
{
TRACE_CPUPROFILER_EVENT_SCOPE(FFileSystemBackend::PullData);
const FString FilePath = CreateFilePath(Id);
// TODO: Should we allow the error severity to be configured via ini or just not report this case at all?
if (!IFileManager::Get().FileExists(*FilePath))
{
UE_LOG(LogVirtualization, Verbose, TEXT("[%s] Does not contain the payload '%s'"), *GetDebugString(), *Id.ToString());
return FCompressedBuffer();
}
TUniquePtr<FArchive> FileAr(IFileManager::Get().CreateFileReader(*FilePath));
if (FileAr == nullptr)
{
const uint32 SystemError = FPlatformMisc::GetLastError();
// If we have a system error we can give a more informative error message but don't output it if the error is zero as
// this can lead to very confusing error messages.
if (SystemError != 0)
{
TCHAR SystemErrorMsg[2048] = { 0 };
FPlatformMisc::GetSystemErrorMessage(SystemErrorMsg, sizeof(SystemErrorMsg), SystemError);
UE_LOG(LogVirtualization, Error, TEXT("[%s] Failed to load payload '%s' file '%s' due to system error: '%s' (%d))"), *GetDebugString(), *Id.ToString(), *FilePath, SystemErrorMsg, SystemError);
}
else
{
UE_LOG(LogVirtualization, Error, TEXT("[%s] Failed to load payload '%s' from '%s' (reason unknown)"), *GetDebugString(), *Id.ToString(), *FilePath);
}
return FCompressedBuffer();
}
return FCompressedBuffer::FromCompressed(*FileAr);
}
bool DoesExist(const FPayloadId& Id)
{
TRACE_CPUPROFILER_EVENT_SCOPE(FFileSystemBackend::DoesExist);
const FString FilePath = CreateFilePath(Id);
return IFileManager::Get().FileExists(*FilePath);
}
virtual FString GetDebugString() const override
{
return Name;
}
FString CreateFilePath(const FPayloadId& PayloadId)
{
return FString::Printf(TEXT("%s/%s.payload"), *RootDirectory, *PayloadId.ToString());
}
private:
FString Name;
FString RootDirectory;
};
UE_REGISTER_VIRTUALIZATION_BACKEND_FACTORY(FFileSystemBackend, FileSystem);
} // namespace UE::Virtualization