Files
UnrealEngine/Engine/Source/Runtime/RuntimeAssetCache/Private/RuntimeAssetCache.cpp
2025-05-18 13:04:45 +08:00

211 lines
5.3 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "RuntimeAssetCache.h"
#include "Misc/ScopeLock.h"
#include "Stats/StatsMisc.h"
#include "Misc/ConfigCacheIni.h"
#include "RuntimeAssetCachePluginInterface.h"
#include "RuntimeAssetCacheBackend.h"
#include "RuntimeAssetCacheBucket.h"
#include "Misc/RuntimeErrors.h"
DEFINE_STAT(STAT_RAC_ASyncWaitTime);
FRuntimeAssetCache::FRuntimeAssetCache()
{
FString BucketName;
int32 BucketSize;
TArray<FString> BucketNames;
GConfig->GetArray(TEXT("RuntimeAssetCache"), TEXT("BucketConfigs"), BucketNames, GEngineIni);
for (FString& Bucket : BucketNames)
{
if (FParse::Value(*Bucket, TEXT("Name="), BucketName))
{
BucketSize = FRuntimeAssetCacheBucket::DefaultBucketSize;
FParse::Value(*Bucket, TEXT("Size="), BucketSize);
FRuntimeAssetCacheBucket* BucketDesc = FRuntimeAssetCacheBackend::Get().PreLoadBucket(FName(*BucketName), BucketSize);
Buckets.Add(FName(*BucketName), BucketDesc);
}
}
}
FRuntimeAssetCache::~FRuntimeAssetCache()
{
for (auto& Bucket : Buckets)
{
delete Bucket.Value;
}
Buckets.Empty();
}
int32 FRuntimeAssetCache::GetCacheSize(FName Bucket) const
{
return ensureAsRuntimeWarning(Buckets.Contains(Bucket)) ? Buckets[Bucket]->GetSize() : 0;
}
int32 FRuntimeAssetCache::GetAsynchronous(IRuntimeAssetCacheBuilder* CacheBuilder, const FOnRuntimeAssetCacheAsyncComplete& OnComplete)
{
if (ensureAsRuntimeWarning(CacheBuilder != nullptr))
{
int32 Handle = GetNextHandle();
/** Must return a valid handle */
check(Handle != 0);
/** Make sure task isn't processed twice. */
check(!PendingTasks.Contains(Handle));
checkf(CacheBuilder->IsBuildThreadSafe(), TEXT("CacheBuilder %s Build function is not thread safe, but builder was used in asynchronous code. Use GetSynchronous instead."), CacheBuilder->GetBuilderName());
FAsyncTask<FRuntimeAssetCacheAsyncWorker>* AsyncTask = new FAsyncTask<FRuntimeAssetCacheAsyncWorker>(CacheBuilder, &Buckets, Handle, OnComplete);
{
FScopeLock ScopeLock(&SynchronizationObject);
PendingTasks.Add(Handle, AsyncTask);
}
AddToAsyncCompletionCounter(1);
AsyncTask->StartBackgroundTask();
return Handle;
}
else
{
// Using this with other functions is going to cause knock-on errors
return 0;
}
}
int32 FRuntimeAssetCache::GetAsynchronous(IRuntimeAssetCacheBuilder* CacheBuilder)
{
return GetAsynchronous(CacheBuilder, FOnRuntimeAssetCacheAsyncComplete());
}
FVoidPtrParam FRuntimeAssetCache::GetSynchronous(IRuntimeAssetCacheBuilder* CacheBuilder)
{
if (ensureAsRuntimeWarning(CacheBuilder))
{
if (ensureMsgf(!CacheBuilder->ShouldBuildAsynchronously(), TEXT("CacheBuilder %s can be only called asynchronously."), CacheBuilder->GetBuilderName()))
{
FAsyncTask<FRuntimeAssetCacheAsyncWorker>* AsyncTask = new FAsyncTask<FRuntimeAssetCacheAsyncWorker>(CacheBuilder, &Buckets, -1, FOnRuntimeAssetCacheAsyncComplete());
AddToAsyncCompletionCounter(1);
AsyncTask->StartSynchronousTask();
return AsyncTask->GetTask().GetDataAndSize();
}
}
return FVoidPtrParam::NullPtr();
}
bool FRuntimeAssetCache::ClearCache()
{
/** Tell backend to clean itself up. */
bool bResult = FRuntimeAssetCacheBackend::Get().ClearCache();
if (!bResult)
{
return bResult;
}
/** If backend is cleaned up, clean up all buckets. */
for (auto& Bucket : Buckets)
{
Bucket.Value->Reset();
}
return bResult;
}
bool FRuntimeAssetCache::ClearCache(FName BucketName)
{
/** Tell backend to clean bucket up. */
bool bResult = FRuntimeAssetCacheBackend::Get().ClearCache(BucketName);
if (!bResult)
{
return bResult;
}
/** If backend is cleaned up, clean up all buckets. */
for (auto& Bucket : Buckets)
{
Bucket.Value->Reset();
}
return bResult;
}
void FRuntimeAssetCache::AddToAsyncCompletionCounter(int32 Value)
{
PendingTasksCounter.Add(Value);
check(PendingTasksCounter.GetValue() >= 0);
}
void FRuntimeAssetCache::WaitAsynchronousCompletion(int32 Handle)
{
STAT(double ThisTime = 0);
{
SCOPE_SECONDS_COUNTER(ThisTime);
FAsyncTask<FRuntimeAssetCacheAsyncWorker>* AsyncTask = NULL;
{
FScopeLock ScopeLock(&SynchronizationObject);
AsyncTask = PendingTasks.FindRef(Handle);
}
if (ensureAsRuntimeWarning(AsyncTask))
{
AsyncTask->EnsureCompletion();
}
}
INC_FLOAT_STAT_BY(STAT_RAC_ASyncWaitTime, (float)ThisTime);
}
FVoidPtrParam FRuntimeAssetCache::GetAsynchronousResults(int32 Handle)
{
FAsyncTask<FRuntimeAssetCacheAsyncWorker>* AsyncTask = nullptr;
{
FScopeLock ScopeLock(&SynchronizationObject);
PendingTasks.RemoveAndCopyValue(Handle, AsyncTask);
if (ensureAsRuntimeWarning(AsyncTask))
{
AsyncTask->GetTask().FireCompletionDelegate();
}
}
if (ensureAsRuntimeWarning(AsyncTask))
{
FVoidPtrParam Data = AsyncTask->GetTask().GetDataAndSize();
delete AsyncTask;
return Data;
}
else
{
return FVoidPtrParam::NullPtr();
}
}
bool FRuntimeAssetCache::PollAsynchronousCompletion(int32 Handle)
{
FAsyncTask<FRuntimeAssetCacheAsyncWorker>* AsyncTask = nullptr;
{
FScopeLock ScopeLock(&SynchronizationObject);
AsyncTask = PendingTasks.FindRef(Handle);
}
ensureAsRuntimeWarning(AsyncTask);
return AsyncTask ? AsyncTask->IsDone() : true;
}
void FRuntimeAssetCache::Tick()
{
FScopeLock ScopeLock(&SynchronizationObject);
for (auto Kvp : PendingTasks)
{
if (Kvp.Value->IsDone())
{
Kvp.Value->GetTask().FireCompletionDelegate();
}
}
}