Files
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

194 lines
4.7 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Misc/DisplayClusterTickableGameObject.h"
#include "Misc/SecureHash.h"
/**
* Cache template for named data with automatic deletion of unused items after timeout.
*/
template<typename InDataType>
class TDisplayClusterDataCache
{
public:
/** Data type of stored objects
*
* The following methods must be implemented in DataType:
*
* // Return DataCache timeout in frames. A negative value disables the timeout.
* static int32 GetDataCacheTimeOutInFrames();
*
* // Return true if DataCache is enabled.
* static bool IsDataCacheEnabled();
*
* // Method for releasing a cached data item, called before its destructor
* void ReleaseDataCacheItem();
*
* // Returns the unique name of this texture for DataCache.
* inline const FString& GetDataCacheName() const;
*/
typedef InDataType DataType;
virtual ~TDisplayClusterDataCache()
{
Release();
}
/** Helper to perform the very common case of hashing an FString into a hex representation. */
static inline FString HashString(const FString& InString)
{
return FMD5::HashAnsiString(*InString.ToLower());
}
/** Retrieve an existing object by a unique name. */
TSharedPtr<DataType, ESPMode::ThreadSafe> Find(const FString& InUniqueDataName)
{
if (DataType::IsDataCacheEnabled())
{
if (FCachedObject* ExistCachedObject = CachedObjects.FindByPredicate([InUniqueDataName](const FCachedObject& CachedObjectIt)
{
return CachedObjectIt.IsNameEqual(InUniqueDataName);
}))
{
return ExistCachedObject->DataRef;
}
}
return nullptr;
}
/** Register a new object. */
void Add(const TSharedPtr<DataType, ESPMode::ThreadSafe>& InDataRef)
{
if (DataType::IsDataCacheEnabled())
{
CachedObjects.Add(FCachedObject(InDataRef));
UpdateTickableGameObject();
}
}
protected:
/**
* Release the cache
*/
void Release()
{
CachedObjects.Empty();
UpdateTickableGameObject();
}
/**
* When the number of DataRef references drops to 1, these items will be removed after a timeout.
*/
void Tick(float DeltaTime)
{
// If the cache is disabled, clear it
if (!DataType::IsDataCacheEnabled())
{
Release();
return;
}
const int32 TimeOutInFrames = DataType::GetDataCacheTimeOutInFrames();
// Updating the time for all objects in use
for (int32 Index = 0; Index < CachedObjects.Num(); Index++)
{
if (CachedObjects[Index].DataRef.GetSharedReferenceCount() >1 || TimeOutInFrames < 0)
{
// When this object used, reset this counter
CachedObjects[Index].FramesInCachedState = 0;
}
else
{
// After the data ref counter remains 1, this counter will be incremented every frame
CachedObjects[Index].FramesInCachedState++;
// Remove after timout
if (CachedObjects[Index].FramesInCachedState > TimeOutInFrames)
{
// Delete the current element, and reuse the current array index,
// because the next element will move to the index of the current
CachedObjects.RemoveAt(Index--);
}
}
}
UpdateTickableGameObject();
}
/** Create or remove tickable game object. */
inline void UpdateTickableGameObject()
{
if (CachedObjects.IsEmpty())
{
// Unregister tick event
if (TickableGameObject.IsValid() && TickHandle.IsValid())
{
TickableGameObject->OnTick().Remove(TickHandle);
}
TickableGameObject.Reset();
}
else
{
if (!TickableGameObject.IsValid())
{
TickableGameObject = MakeUnique<FDisplayClusterTickableGameObject >();
TickHandle = TickableGameObject->OnTick().AddRaw(this, &TDisplayClusterDataCache<DataType>::Tick);
}
}
}
private:
struct FCachedObject
{
FCachedObject(const TSharedPtr<DataType, ESPMode::ThreadSafe>& InDataRef)
: DataRef(InDataRef)
{ }
~FCachedObject()
{
Release();
}
/** Release referenced data. */
void Release()
{
if (DataRef.IsValid())
{
// Calls the special release function before the destructor
DataRef->ReleaseDataCacheItem();
DataRef.Reset();
}
FramesInCachedState = 0;
}
inline bool IsNameEqual(const FString& InName) const
{
return DataRef.IsValid() && DataRef->GetDataCacheName() == InName;
}
// reference to data
TSharedPtr<DataType, ESPMode::ThreadSafe> DataRef;
// After the data ref counter remains 1, this counter will be incremented every frame
int32 FramesInCachedState = 0;
};
// Cached objects map
TArray<FCachedObject> CachedObjects;
// When CachedObjects is not empty, this ticking object will be created.
// Also, this object will be deleted when CachedObjects becomes empty.
TUniquePtr<FDisplayClusterTickableGameObject> TickableGameObject;
// Event delegate container of the Tickable object
FDelegateHandle TickHandle;
};