Files
UnrealEngine/Engine/Plugins/Mutable/Source/CustomizableObject/Private/MuCO/MutableStreamRequest.cpp
2025-05-18 13:04:45 +08:00

298 lines
8.6 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "MutableStreamRequest.h"
#include "MuCO/CustomizableObjectPrivate.h"
#include "MuCO/CustomizableObjectSystemPrivate.h"
#include "HAL/PlatformFileManager.h"
#if WITH_EDITOR
#include "DerivedDataCache.h"
#include "DerivedDataCacheInterface.h"
#include "DerivedDataCacheKey.h"
#include "DerivedDataRequestOwner.h"
#endif
FMutableStreamRequest::FMutableStreamRequest(const TSharedPtr<FModelStreamableBulkData>& InModelStreamableBulkData) :
ModelStreamableBulkData(InModelStreamableBulkData)
{
}
const TSharedPtr<FModelStreamableBulkData>& FMutableStreamRequest::GetModelStreamableBulkData() const
{
return ModelStreamableBulkData;
}
void FMutableStreamRequest::AddBlock(const FMutableStreamableBlock& Block, MutablePrivate::EStreamableDataType DataType, uint16 ResourceType, TArrayView<uint8> AllocatedMemoryView)
{
if (bIsStreaming)
{
check(false);
return;
}
IAsyncReadFileHandle* FileHandle = nullptr;
#if WITH_EDITOR
if (!ModelStreamableBulkData->bIsStoredInDDC)
{
int32 FileHandleIndex = OpenFilesIds.Find(Block.FileId);
if (FileHandleIndex == INDEX_NONE)
{
const FString& FullFileName = ModelStreamableBulkData->FullFilePath + GetDataTypeExtension(DataType);
IAsyncReadFileHandle* ReadFileHandle = FPlatformFileManager::Get().GetPlatformFile().OpenAsyncRead(*FullFileName);
OpenFileHandles.Emplace(ReadFileHandle);
FileHandleIndex = OpenFilesIds.Add(Block.FileId);
check(OpenFileHandles.Num() == OpenFilesIds.Num());
}
if (OpenFileHandles.IsValidIndex(FileHandleIndex))
{
FileHandle = OpenFileHandles[FileHandleIndex].Get();
}
}
#endif
BlockReadInfos.Emplace(Block.Offset, FileHandle, AllocatedMemoryView, Block.FileId, DataType, ResourceType, Block.Flags);
}
UE::Tasks::FTask FMutableStreamRequest::Stream()
{
if (bIsStreaming)
{
check(false);
return UE::Tasks::MakeCompletedTask<void>();
}
bIsStreaming = true;
for (const FBlockReadInfo& Block : BlockReadInfos)
{
HeapMemory->CompletionEvents.Emplace(TEXT("AsyncReadDataReadyEvent"));
}
UE::Tasks::Launch(TEXT("CustomizableObjectReadRequestTask"),
[
ModelStreamableBulkData = ModelStreamableBulkData,
BlockReadInfos = MoveTemp(BlockReadInfos),
HeapMemory = this->HeapMemory
]() mutable
{
MUTABLE_CPUPROFILER_SCOPE(CustomizableInstanceLoadBlocksAsyncRead_Request);
FScopeLock Lock(&HeapMemory->ReadRequestLock);
// Task cancelled, early out.
if (HeapMemory->bIsCancelled)
{
// Trigger preallocated events to complete GatherStreamingRequestsCompletionTask prerequisites
for (UE::Tasks::FTaskEvent& CompletionEvent : HeapMemory->CompletionEvents)
{
CompletionEvent.Trigger();
}
return;
}
#if WITH_EDITOR
if (ModelStreamableBulkData->bIsStoredInDDC)
{
using namespace UE::DerivedData;
// Skip loading values by default
FCacheRecordPolicyBuilder PolicyBuilder(ECachePolicy::Default | ECachePolicy::SkipData);
TArray<FValueId> ResourceIds;
ResourceIds.Reserve(BlockReadInfos.Num());
// Override policy for the resources to load.
for (const FBlockReadInfo& Block : BlockReadInfos)
{
FValueId ResourceId = GetDerivedDataValueIdForResource(Block.DataType, Block.FileId, Block.ResourceType, Block.ResourceFlags);
if (ResourceIds.Find(ResourceId) == INDEX_NONE)
{
// Add value policy once.
PolicyBuilder.AddValuePolicy(ResourceId, ECachePolicy::Default);
}
ResourceIds.Add(ResourceId);
}
FCacheGetRequest Request;
Request.Name = ModelStreamableBulkData->FullFilePath;
Request.Key = ModelStreamableBulkData->DDCKey;
Request.Policy = PolicyBuilder.Build();
TSharedPtr<UE::DerivedData::FRequestOwner>& DDCRequest = HeapMemory->DDCReadRequest.Add_GetRef(MakeShared<FRequestOwner>(EPriority::High));
GetCache().Get(MakeArrayView(&Request, 1), *DDCRequest,
[HeapMemory, BlockReadInfos = MoveTemp(BlockReadInfos), CapturedResourceIds = MoveTemp(ResourceIds)](FCacheGetResponse&& Response) mutable
{
bool bSuccess = Response.Status == EStatus::Ok;
if (ensure(bSuccess))
{
const int32 NumResources = CapturedResourceIds.Num();
for (int32 Index = 0; Index < NumResources; ++Index)
{
const FCompressedBuffer& CompressedBuffer = Response.Record.GetValue(CapturedResourceIds[Index]).GetData();
if (ensure(!CompressedBuffer.IsNull()))
{
const FBlockReadInfo& Block = BlockReadInfos[Index];
const uint64 Size = Block.AllocatedMemoryView.Num();
if (Size < CompressedBuffer.GetRawSize())
{
check(CompressedBuffer.GetRawSize() >= Block.Offset + Size);
FSharedBuffer DecompressedBuffer = CompressedBuffer.Decompress();
FMemory::Memcpy(Block.AllocatedMemoryView.GetData(), reinterpret_cast<const uint8*>(DecompressedBuffer.GetData()) + Block.Offset, Size);
}
else
{
check(CompressedBuffer.TryDecompressTo(MakeMemoryView(Block.AllocatedMemoryView.GetData(), Size)));
}
}
}
}
for (UE::Tasks::FTaskEvent& CompletionEvent : HeapMemory->CompletionEvents)
{
CompletionEvent.Trigger();
}
});
return;
}
#endif
const bool bUseFBulkData = !ModelStreamableBulkData->StreamableBulkData.IsEmpty();
const EAsyncIOPriorityAndFlags Priority = CVarMutableHighPriorityLoading.GetValueOnAnyThread() ? AIOP_High : AIOP_Normal;
const int32 NumBlocks = BlockReadInfos.Num();
for (int32 Index = 0; Index < NumBlocks; ++Index)
{
const FBlockReadInfo& Block = BlockReadInfos[Index];
UE::Tasks::FTaskEvent& CompletionEvent = HeapMemory->CompletionEvents[Index];
if (bUseFBulkData)
{
FBulkDataIORequestCallBack IOCallback =
[CompletionEvent, FileId = Block.FileId](bool bWasCancelled, IBulkDataIORequest*) mutable
{
CompletionEvent.Trigger();
};
check(ModelStreamableBulkData->StreamableBulkData.IsValidIndex(Block.FileId));
FByteBulkData& ByteBulkData = ModelStreamableBulkData->StreamableBulkData[Block.FileId];
HeapMemory->BulkReadRequests.Add(TSharedPtr<IBulkDataIORequest>(ByteBulkData.CreateStreamingRequest(
BulkDataFileOffset + Block.Offset,
(int64)Block.AllocatedMemoryView.Num(),
Priority,
&IOCallback,
Block.AllocatedMemoryView.GetData())));
}
else if (Block.FileHandle)
{
FAsyncFileCallBack ReadRequestCallBack =
[CompletionEvent, FileId = Block.FileId](bool bWasCancelled, IAsyncReadRequest*) mutable
{
CompletionEvent.Trigger();
};
HeapMemory->ReadRequests.Add(TSharedPtr<IAsyncReadRequest>(Block.FileHandle->ReadRequest(
BulkDataFileOffset + Block.Offset,
(int64)Block.AllocatedMemoryView.Num(),
Priority,
&ReadRequestCallBack,
Block.AllocatedMemoryView.GetData())));
}
else
{
ensure(false);
CompletionEvent.Trigger();
}
}
},
UE::Tasks::ETaskPriority::High);
return UE::Tasks::Launch(TEXT("GatherStreamingRequestsCompletionTask"),
[
OpenFileHandles = OpenFileHandles,
HeapMemory = this->HeapMemory
]() mutable
{
{
FScopeLock Lock(&HeapMemory->ReadRequestLock);
for (TSharedPtr<IAsyncReadRequest>& ReadRequest : HeapMemory->ReadRequests)
{
if (ReadRequest)
{
ReadRequest->WaitCompletion();
}
}
for (TSharedPtr<IBulkDataIORequest>& BulkReadRequest : HeapMemory->BulkReadRequests)
{
if (BulkReadRequest)
{
BulkReadRequest->WaitCompletion();
}
}
HeapMemory->BulkReadRequests.Empty();
HeapMemory->ReadRequests.Empty();
}
OpenFileHandles.Empty();
},
HeapMemory->CompletionEvents,
UE::Tasks::ETaskPriority::High);
}
void FMutableStreamRequest::Cancel()
{
FScopeLock Lock(&HeapMemory->ReadRequestLock);
if (!HeapMemory->bIsCancelled)
{
HeapMemory->bIsCancelled = true;
for (TSharedPtr<IAsyncReadRequest>& ReadRequest : HeapMemory->ReadRequests)
{
if (ReadRequest)
{
ReadRequest->Cancel();
}
}
for (TSharedPtr<IBulkDataIORequest>& BulkReadRequest : HeapMemory->BulkReadRequests)
{
if (BulkReadRequest)
{
BulkReadRequest->Cancel();
}
}
}
}
FMutableStreamRequest::FBlockReadInfo::FBlockReadInfo(uint64 InOffset, IAsyncReadFileHandle* InFileHandle, TArrayView<uint8>& InAllocatedMemoryView,
uint32 InFileId, MutablePrivate::EStreamableDataType InDataType, uint16 InResourceType, uint16 InResourceFlags)
: Offset(InOffset)
, FileHandle(InFileHandle)
, AllocatedMemoryView(InAllocatedMemoryView)
, FileId(InFileId)
, ResourceType(InResourceType)
, ResourceFlags(InResourceFlags)
, DataType(InDataType)
{
}