You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
- Implemented only for the structured cache initially.
- Records are stored as compact binary packages with payloads stored within the record if small enough.
- Records are stored in Records/{Bucket}/{01}/{23}/{456789abcdef0123456789abcdef01234567} based on the hash in the cache key.
- Content that exceeds the size allowed for storage within the record is stored in Content/{01}/{23}/{456789abcdef0123456789abcdef01234567} based on the hash of the content.
- Both records and content are written to disk with their hash as a suffix, and are always checked on load.
- Maximum size of compressed data stored in the package can be configured by MaxRecordSizeKB and MaxValueSizeKB.
- Maintenance of the cache, to delete unused files, has been rewritten and can now scan ~175k files/second on modern hardware, though is limited to 10k files/second in the config to avoid adding too much overhead.
#rb Zousar.Shaker
#preflight 618b75c14a0c609a29204032
#preflight 618bfb5ec717ba4e929b7ac0
#ROBOMERGE-AUTHOR: devin.doucette
#ROBOMERGE-SOURCE: CL 18133085 in //UE5/Release-5.0/... via CL 18133356
#ROBOMERGE-BOT: STARSHIP (Release-Engine-Staging -> Release-Engine-Test) (v889-18060218)
#ROBOMERGE[STARSHIP]: UE5-Main
[CL 18133430 by devin doucette in ue5-release-engine-test branch]
409 lines
16 KiB
C++
409 lines
16 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#pragma once
|
|
|
|
#include "CoreTypes.h"
|
|
#include "Containers/StringFwd.h"
|
|
#include "DerivedDataCacheKey.h"
|
|
#include "DerivedDataPayloadId.h"
|
|
#include "DerivedDataRequestTypes.h"
|
|
#include "Math/NumericLimits.h"
|
|
#include "Misc/EnumClassFlags.h"
|
|
#include "Templates/Function.h"
|
|
#include "Templates/RefCounting.h"
|
|
|
|
#define UE_API DERIVEDDATACACHE_API
|
|
|
|
namespace UE::DerivedData { class FCacheRecord; }
|
|
namespace UE::DerivedData { class FCacheRecordPolicy; }
|
|
namespace UE::DerivedData { class ICacheStoreMaintainer; }
|
|
namespace UE::DerivedData { class IRequestOwner; }
|
|
namespace UE::DerivedData { struct FCacheChunkRequest; }
|
|
namespace UE::DerivedData { struct FCacheGetChunkCompleteParams; }
|
|
namespace UE::DerivedData { struct FCacheGetCompleteParams; }
|
|
namespace UE::DerivedData { struct FCachePutCompleteParams; }
|
|
namespace UE::DerivedData::Private { class ICacheRecordPolicyShared; }
|
|
|
|
namespace UE::DerivedData
|
|
{
|
|
|
|
using FOnCachePutComplete = TUniqueFunction<void (FCachePutCompleteParams&& Params)>;
|
|
using FOnCacheGetComplete = TUniqueFunction<void (FCacheGetCompleteParams&& Params)>;
|
|
using FOnCacheGetChunkComplete = TUniqueFunction<void (FCacheGetChunkCompleteParams&& Params)>;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* Flags to control the behavior of cache requests.
|
|
*
|
|
* The cache policy flags can be combined to support a variety of usage patterns. Examples:
|
|
*
|
|
* Get(Default): read from any cache; put to writable caches if missing.
|
|
* Get(Remote): read any remote cache; put to writable remote caches if missing.
|
|
* Get(Local): read any local cache; put to writable local caches if missing.
|
|
* Get(Query | StoreRemote): read from any cache; put to writable remote caches if missing.
|
|
* Get(Query | StoreLocal): read from any cache; put to writable local caches if missing.
|
|
* Get(Query | SkipData): check for existence in any cache; do not modify any cache.
|
|
* Get(Default | SkipData): check for existence in any cache; put to writable caches if missing.
|
|
*
|
|
* Put(Default): write to every writable cache.
|
|
* Put(Remote): write to every writable remote cache, skipping local caches.
|
|
* Put(Local): write to every writable local cache, skipping remote caches.
|
|
*/
|
|
enum class ECachePolicy : uint32
|
|
{
|
|
/** A value without any flags set. */
|
|
None = 0,
|
|
|
|
/** Allow a cache request to query local caches. */
|
|
QueryLocal = 1 << 0,
|
|
/** Allow a cache request to query remote caches. */
|
|
QueryRemote = 1 << 1,
|
|
/** Allow a cache request to query any caches. */
|
|
Query = QueryLocal | QueryRemote,
|
|
|
|
/** Allow cache records and values to be stored in local caches. */
|
|
StoreLocal = 1 << 2,
|
|
/** Allow cache records and values to be stored in remote caches. */
|
|
StoreRemote = 1 << 3,
|
|
/** Allow cache records and values to be stored in any caches. */
|
|
Store = StoreLocal | StoreRemote,
|
|
|
|
/** Skip fetching the metadata for record requests. */
|
|
SkipMeta = 1 << 4,
|
|
/** Skip fetching the value for record, chunk, or value requests. */
|
|
SkipValue = 1 << 5,
|
|
/** Skip fetching the attachments for record requests. */
|
|
SkipAttachments = 1 << 6,
|
|
/**
|
|
* Skip fetching the data for any requests.
|
|
*
|
|
* Put requests with skip flags may assume that record existence implies payload existence.
|
|
*/
|
|
SkipData = SkipMeta | SkipValue | SkipAttachments,
|
|
|
|
/**
|
|
* Keep records in the cache for at least the duration of the session.
|
|
*
|
|
* This is a hint that the record may be accessed again in this session. This is mainly meant
|
|
* to be used when subsequent accesses will not tolerate a cache miss.
|
|
*/
|
|
KeepAlive = 1 << 7,
|
|
|
|
/**
|
|
* Partial output will be provided with the error status when a required payload is missing.
|
|
*
|
|
* This is meant for cases when the missing payloads can be individually recovered or rebuilt
|
|
* without rebuilding the whole record. The cache automatically adds this flag when there are
|
|
* other cache stores that it may be able to recover missing payloads from.
|
|
*
|
|
* Requests for records would return records where the missing payloads have a hash and size,
|
|
* but no data. Requests for chunks or values would return the hash and size, but no data.
|
|
*/
|
|
PartialOnError = 1 << 8,
|
|
|
|
/** Allow cache requests to query and store records and values in local caches. */
|
|
Local = QueryLocal | StoreLocal,
|
|
/** Allow cache requests to query and store records and values in remote caches. */
|
|
Remote = QueryRemote | StoreRemote,
|
|
|
|
/** Allow cache requests to query and store records and values in any caches. */
|
|
Default = Query | Store,
|
|
|
|
/** Do not allow cache requests to query or store records and values in any caches. */
|
|
Disable = None,
|
|
};
|
|
|
|
ENUM_CLASS_FLAGS(ECachePolicy);
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/** A payload ID and the cache policy to use for that payload. */
|
|
struct FCachePayloadPolicy
|
|
{
|
|
FPayloadId Id;
|
|
ECachePolicy Policy = ECachePolicy::Default;
|
|
};
|
|
|
|
/** Interface for the private implementation of the cache record policy. */
|
|
class Private::ICacheRecordPolicyShared
|
|
{
|
|
public:
|
|
virtual ~ICacheRecordPolicyShared() = default;
|
|
virtual void AddRef() const = 0;
|
|
virtual void Release() const = 0;
|
|
virtual TConstArrayView<FCachePayloadPolicy> GetPayloadPolicies() const = 0;
|
|
virtual void AddPayloadPolicy(const FCachePayloadPolicy& Policy) = 0;
|
|
virtual void Build() = 0;
|
|
};
|
|
|
|
/**
|
|
* Flags to control the behavior of cache requests, with optional overrides by payload.
|
|
*
|
|
* Examples:
|
|
* - A base policy of Disable with payload policy overrides of Default will fetch those payloads,
|
|
* if they exist in the record, and skip data for any other payloads.
|
|
* - A base policy of Default with payload policy overrides of (Query | SkipData) will skip those
|
|
* payloads, but still check if they exist, and will load any other payloads.
|
|
*/
|
|
class FCacheRecordPolicy
|
|
{
|
|
public:
|
|
/** Construct a cache record policy that uses the default policy. */
|
|
FCacheRecordPolicy() = default;
|
|
|
|
/** Construct a cache record policy with a uniform policy for the record and every payload. */
|
|
inline FCacheRecordPolicy(ECachePolicy Policy)
|
|
: RecordPolicy(Policy)
|
|
, DefaultPayloadPolicy(Policy)
|
|
{
|
|
}
|
|
|
|
/** Returns true if the record and every payload use the same cache policy. */
|
|
inline bool IsUniform() const { return !Shared && RecordPolicy == DefaultPayloadPolicy; }
|
|
|
|
/** Returns the cache policy to use for the record. */
|
|
inline ECachePolicy GetRecordPolicy() const { return RecordPolicy; }
|
|
|
|
/** Returns the cache policy to use for the payload. */
|
|
UE_API ECachePolicy GetPayloadPolicy(const FPayloadId& Id) const;
|
|
|
|
/** Returns the cache policy to use for payloads with no override. */
|
|
inline ECachePolicy GetDefaultPayloadPolicy() const { return DefaultPayloadPolicy; }
|
|
|
|
/** Returns the array of cache policy overrides for payloads, sorted by ID. */
|
|
inline TConstArrayView<FCachePayloadPolicy> GetPayloadPolicies() const
|
|
{
|
|
return Shared ? Shared->GetPayloadPolicies() : TConstArrayView<FCachePayloadPolicy>();
|
|
}
|
|
|
|
private:
|
|
friend class FCacheRecordPolicyBuilder;
|
|
|
|
ECachePolicy RecordPolicy = ECachePolicy::Default;
|
|
ECachePolicy DefaultPayloadPolicy = ECachePolicy::Default;
|
|
TRefCountPtr<const Private::ICacheRecordPolicyShared> Shared;
|
|
};
|
|
|
|
/** A cache record policy builder is used to construct a cache record policy. */
|
|
class FCacheRecordPolicyBuilder
|
|
{
|
|
public:
|
|
/** Construct a policy builder that uses the default policy as its base policy. */
|
|
FCacheRecordPolicyBuilder() = default;
|
|
|
|
/** Construct a policy builder that uses the provided policy for the record and payloads with no override. */
|
|
inline explicit FCacheRecordPolicyBuilder(ECachePolicy Policy)
|
|
: BasePolicy(Policy)
|
|
{
|
|
}
|
|
|
|
/** Adds a cache policy override for a payload. */
|
|
UE_API void AddPayloadPolicy(const FCachePayloadPolicy& Policy);
|
|
inline void AddPayloadPolicy(const FPayloadId& Id, ECachePolicy Policy) { AddPayloadPolicy({Id, Policy}); }
|
|
|
|
/** Build a cache record policy, which makes this builder subsequently unusable. */
|
|
UE_API FCacheRecordPolicy Build();
|
|
|
|
private:
|
|
ECachePolicy BasePolicy = ECachePolicy::Default;
|
|
TRefCountPtr<Private::ICacheRecordPolicyShared> Shared;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* Interface to a store of cache records.
|
|
*
|
|
* Functions on this interface may be called from any thread. When a callback is provided, it may
|
|
* be invoked from a different thread than the request was made on, and may be invoked before the
|
|
* request function returns, both of which must be supported by the caller and the callback.
|
|
*/
|
|
class ICacheStore
|
|
{
|
|
public:
|
|
virtual ~ICacheStore() = default;
|
|
|
|
/**
|
|
* Asynchronous request to put cache records according to the policy.
|
|
*
|
|
* The callback will always be called for every key, and may be called from an arbitrary thread.
|
|
* Records may finish storing in any order, and from multiple threads concurrently.
|
|
*
|
|
* A cache store is free to interpret a record containing only a key as a request to delete that
|
|
* record from the store. Records may contain payloads that do not have data, and these payloads
|
|
* must reference an existing copy of the payload in the store, if available, and must otherwise
|
|
* be stored as a partial record that can attempt recovery of missing payloads when fetched.
|
|
*
|
|
* @param Records The cache records to store. Must have a key.
|
|
* @param Context A description of the request. An object path is typically sufficient.
|
|
* @param Policy Flags to control the behavior of the request. See ECachePolicy.
|
|
* @param Owner The owner to execute the request within.
|
|
* @param OnComplete A callback invoked for every record as it completes or is canceled.
|
|
*/
|
|
virtual void Put(
|
|
TConstArrayView<FCacheRecord> Records,
|
|
FStringView Context,
|
|
ECachePolicy Policy,
|
|
IRequestOwner& Owner,
|
|
FOnCachePutComplete&& OnComplete = FOnCachePutComplete()) = 0;
|
|
|
|
/**
|
|
* Asynchronous request to get cache records according to the policy.
|
|
*
|
|
* The callback will always be called for every key, and may be called from an arbitrary thread.
|
|
* Records may become available in any order, and from multiple threads concurrently.
|
|
*
|
|
* Records may propagate into other cache stores, in accordance with the policy. A propagated
|
|
* record may be a partial record, with some payloads missing data, depending on the policy.
|
|
*
|
|
* When payloads are required by the policy, but not available, the status must be Error. The
|
|
* cache store must produce a partial record with the available payloads when the policy flag
|
|
* PartialOnError is set for the missing payloads.
|
|
*
|
|
* @param Keys The keys identifying the cache records to query.
|
|
* @param Context A description of the request. An object path is typically sufficient.
|
|
* @param Policy Flags to control the behavior of the request. See FCacheRecordPolicy.
|
|
* @param Owner The owner to execute the request within.
|
|
* @param OnComplete A callback invoked for every key as it completes or is canceled.
|
|
*/
|
|
virtual void Get(
|
|
TConstArrayView<FCacheKey> Keys,
|
|
FStringView Context,
|
|
FCacheRecordPolicy Policy,
|
|
IRequestOwner& Owner,
|
|
FOnCacheGetComplete&& OnComplete) = 0;
|
|
|
|
/**
|
|
* Asynchronous request to get chunks, which are subsets of payloads, from cache records.
|
|
*
|
|
* The callback will always be called for every chunk, and may be called from an arbitrary thread.
|
|
* Chunks may become available in any order, and from multiple threads concurrently.
|
|
*
|
|
* There is no requirement that the cache store validate the existence of other payloads from
|
|
* the requested records. Requests for whole payloads may propagate those payloads into other
|
|
* cache stores, in accordance with the policy, through a put of a partial record.
|
|
*
|
|
* @param Chunks The keys, IDs, offsets, and sizes of the chunks to query.
|
|
* @param Context A description of the request. An object path is typically sufficient.
|
|
* @param Owner The owner to execute the request within.
|
|
* @param OnComplete A callback invoked for every chunk as it completes or is canceled.
|
|
*/
|
|
virtual void GetChunks(
|
|
TConstArrayView<FCacheChunkRequest> Chunks,
|
|
FStringView Context,
|
|
IRequestOwner& Owner,
|
|
FOnCacheGetChunkComplete&& OnComplete) = 0;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* Interface to the cache.
|
|
*
|
|
* @see ICacheStore for cache record storage.
|
|
*/
|
|
class ICache : public ICacheStore
|
|
{
|
|
public:
|
|
virtual ~ICache() = default;
|
|
|
|
/**
|
|
* Cancel all queued and active cache requests and invoke their callbacks.
|
|
*
|
|
* This is meant to be called before destroying the cache. Any new requests that are made during
|
|
* execution of this function will also be canceled, because callbacks might queue requests when
|
|
* they are invoked. This does not block any future cache requests, which must be handled by the
|
|
* owner of the cache if future requests are meant to be avoided.
|
|
*/
|
|
virtual void CancelAll() = 0;
|
|
|
|
/** Returns the interface to the background cache store maintenance. */
|
|
virtual ICacheStoreMaintainer& GetMaintainer() = 0;
|
|
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/** Parameters for the completion callback for cache put requests. */
|
|
struct FCachePutCompleteParams
|
|
{
|
|
/** Key for the part of the put request that completed or was canceled. */
|
|
FCacheKey Key;
|
|
|
|
/** Status of the cache request. */
|
|
EStatus Status = EStatus::Error;
|
|
};
|
|
|
|
/** Parameters for the completion callback for cache get requests. */
|
|
struct FCacheGetCompleteParams
|
|
{
|
|
/**
|
|
* Record for the part of the get request that completed or was canceled.
|
|
*
|
|
* The key is always populated. The remainder of the record is populated when Status is Ok.
|
|
*
|
|
* The value, attachments, and metadata may be skipped based on cache policy flags. When a value
|
|
* or attachment has been skipped, it will have a payload but its data will be null.
|
|
*/
|
|
FCacheRecord&& Record;
|
|
|
|
/** Status of the cache request. */
|
|
EStatus Status = EStatus::Error;
|
|
};
|
|
|
|
/** Parameters to request a chunk, which is a subset of a payload, from a cache record. */
|
|
struct FCacheChunkRequest
|
|
{
|
|
/** The key identifying the cache record to fetch the payload from. */
|
|
FCacheKey Key;
|
|
|
|
/** The ID of the payload to fetch from the cache record. */
|
|
FPayloadId Id;
|
|
|
|
/** The offset into the raw bytes of the payload at which to start fetching. */
|
|
uint64 RawOffset = 0;
|
|
|
|
/** The maximum number of raw bytes of the payload to fetch, starting from the offset. */
|
|
uint64 RawSize = MAX_uint64;
|
|
|
|
/** Flags to control the behavior of the request. See ECachePolicy. */
|
|
ECachePolicy Policy = ECachePolicy::Default;
|
|
};
|
|
|
|
/** Parameters for the completion callback for cache chunk requests. */
|
|
struct FCacheGetChunkCompleteParams
|
|
{
|
|
/** Key from the chunk request that completed or was canceled. */
|
|
FCacheKey Key;
|
|
|
|
/** ID from the chunk request that completed or was canceled. */
|
|
FPayloadId Id;
|
|
|
|
/** Offset from the chunk request that completed or was canceled. */
|
|
uint64 RawOffset = 0;
|
|
|
|
/** Size, in bytes, of the subset of the payload that was fetched, if any. */
|
|
uint64 RawSize = 0;
|
|
|
|
/** Hash of the entire payload, even if only a subset was fetched. */
|
|
const FIoHash& RawHash;
|
|
|
|
/** Data for the subset of the payload that was fetched when Status is Ok, otherwise null. */
|
|
FSharedBuffer&& RawData;
|
|
|
|
/** Status of the cache request. */
|
|
EStatus Status = EStatus::Error;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/** Returns a reference to the cache. Asserts if not available. */
|
|
UE_API ICache& GetCache();
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
} // UE::DerivedData
|
|
|
|
#undef UE_API
|