Files
UnrealEngine/Engine/Source/Runtime/MovieScene/Public/Evaluation/MovieScenePlaybackCapabilities.h
2025-05-18 13:04:45 +08:00

819 lines
29 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreTypes.h"
#include "Evaluation/IMovieScenePlaybackCapability.h"
#include "EntitySystem/RelativePtr.h"
#include "MovieSceneFwd.h"
#include "Templates/AlignmentTemplates.h"
#include "Templates/PointerIsConvertibleFromTo.h"
#include <type_traits>
class IMovieScenePlayer;
class IMovieScenePlaybackClient;
namespace UE::MovieScene
{
enum class EPlaybackCapabilityStorageMode : uint8
{
Inline = 0,
RawPointer,
SharedPointer
};
/**
* Return value for accessing raw capabalities.
*/
struct FPlaybackCapabilityPtr
{
void* Ptr = 0;
EPlaybackCapabilityStorageMode StorageMode;
template<typename T>
T* ResolveOptional() const
{
if (Ptr)
{
switch (StorageMode)
{
case EPlaybackCapabilityStorageMode::Inline:
return static_cast<T*>(Ptr);
case EPlaybackCapabilityStorageMode::RawPointer:
return *static_cast<T**>(Ptr);
case EPlaybackCapabilityStorageMode::SharedPointer:
return static_cast<TSharedPtr<T>*>(Ptr)->Get();
default:
checkf(false, TEXT("Unexpected playback capability storage mode"));
return nullptr;
}
}
return nullptr;
}
template<typename T>
T& ResolveChecked() const
{
check(Ptr);
switch (StorageMode)
{
case EPlaybackCapabilityStorageMode::Inline:
return *static_cast<T*>(Ptr);
case EPlaybackCapabilityStorageMode::RawPointer:
return **static_cast<T**>(Ptr);
case EPlaybackCapabilityStorageMode::SharedPointer:
return *static_cast<TSharedPtr<T>*>(Ptr)->Get();
default:
checkf(false, TEXT("Unexpected playback capability storage mode"));
// Nothing reasonable to do, let's just proceed as if it was inline
return *static_cast<T*>(Ptr);
}
}
};
// Utility callbacks for the concrete capability objects we have in a container.
using FPlaybackCapabilityInterfaceCastHelper = IPlaybackCapability*(*)(void* Ptr);
using FPlaybackCapabilityDestructionHelper = void(*)(void* Ptr);
// Callback for casting a stored capabilty object to an IPlaybackCapability pointer if possible.
template<typename StorageType>
struct TPlaybackCapabilityInterfaceCast
{
static IPlaybackCapability* InterfaceCast(void* Ptr)
{
if constexpr (TPointerIsConvertibleFromTo<StorageType, IPlaybackCapability>::Value)
{
return static_cast<IPlaybackCapability*>((StorageType*)Ptr);
}
else
{
return nullptr;
}
}
};
template<typename PointedType>
struct TPlaybackCapabilityInterfaceCast<PointedType*>
{
static IPlaybackCapability* InterfaceCast(void* Ptr)
{
if constexpr (TPointerIsConvertibleFromTo<PointedType, IPlaybackCapability>::Value)
{
PointedType* TypedPtr = *(PointedType**)Ptr;
return static_cast<IPlaybackCapability*>(TypedPtr);
}
else
{
return nullptr;
}
};
};
template<typename PointedType>
struct TPlaybackCapabilityInterfaceCast<TSharedPtr<PointedType>>
{
static IPlaybackCapability* InterfaceCast(void* Ptr)
{
if constexpr (TPointerIsConvertibleFromTo<PointedType, IPlaybackCapability>::Value)
{
TSharedPtr<PointedType>& TypedPtr = *(TSharedPtr<PointedType>*)Ptr;
return static_cast<IPlaybackCapability*>(TypedPtr.Get());
}
else
{
return nullptr;
}
};
};
// Callback for destroying the stored capability object, whether that's the capability itself
// (when stored inline), or a shared pointer, or whatever else.
template<typename StorageType>
struct TPlaybackCapabilityDestructor
{
static void Destroy(void* Ptr)
{
StorageType* StoragePtr = (StorageType*)Ptr;
StoragePtr->~StorageType();
}
};
// Helper callbacks for managing the lifetime of a capability.
struct FPlaybackCapabilityHelpers
{
FPlaybackCapabilityInterfaceCastHelper InterfaceCast = nullptr;
FPlaybackCapabilityDestructionHelper Destructor = nullptr;
};
template<typename StorageType>
struct TPlaybackCapabilityHelpers
{
static FPlaybackCapabilityHelpers GetHelpers()
{
FPlaybackCapabilityHelpers Helpers;
Helpers.InterfaceCast = &TPlaybackCapabilityInterfaceCast<StorageType>::InterfaceCast;
Helpers.Destructor = &TPlaybackCapabilityDestructor<StorageType>::Destroy;
return Helpers;
}
};
// Storage traits for playback capabilities, only for internal use.
template<typename StorageType, typename CapabilityType, typename=void>
struct TPlaybackCapabilityStorageTraits;
template<typename StorageType, typename CapabilityType>
struct TPlaybackCapabilityStorageTraits<StorageType, CapabilityType, typename TEnableIf<TPointerIsConvertibleFromTo<StorageType, CapabilityType>::Value>::Type>
{
static EPlaybackCapabilityStorageMode GetStorageMode() { return EPlaybackCapabilityStorageMode::Inline; }
static uint8 ComputePointerOffset(StorageType* StoragePtr)
{
const uint64 PointerOffset =
reinterpret_cast<uint64>(static_cast<CapabilityType*>(StoragePtr))
-
reinterpret_cast<uint64>(StoragePtr);
check(PointerOffset <= TNumericLimits<uint8>::Max());
return static_cast<uint8>(PointerOffset);
}
};
template<typename CapabilityType>
struct TPlaybackCapabilityStorageTraits<CapabilityType*, CapabilityType, void>
{
static EPlaybackCapabilityStorageMode GetStorageMode() { return EPlaybackCapabilityStorageMode::RawPointer; }
static uint8 ComputePointerOffset(CapabilityType** StoragePtr) { return 0; }
};
template<typename CapabilityType>
struct TPlaybackCapabilityStorageTraits<TSharedPtr<CapabilityType>, CapabilityType, void>
{
static EPlaybackCapabilityStorageMode GetStorageMode() { return EPlaybackCapabilityStorageMode::SharedPointer; }
static uint8 ComputePointerOffset(TSharedPtr<CapabilityType>* StoragePtr) { return 0; }
};
/**
* Header that describes an entry in the capabilities buffer.
*/
struct FPlaybackCapabilityHeader
{
#if UE_MOVIESCENE_ENTITY_DEBUG
// Extra debugging field.
IPlaybackCapabilityDebuggingTypedPtr* DebugPtr = nullptr;
#endif
// 2 Bytes
TRelativePtr<void, uint16> Capability;
// 2 Bytes
uint16 Sizeof = 0;
// 1 Byte
uint8 Alignment = 0;
// 1 Byte
uint8 PointerOffset = 0;
// 1 Byte
EPlaybackCapabilityStorageMode StorageMode = EPlaybackCapabilityStorageMode::Inline;
/** Resolve the given raw pointer into a capability pointer */
FPlaybackCapabilityPtr Resolve(void* InMemory) const
{
void* DerivedPointer = Capability.Resolve(InMemory);
return FPlaybackCapabilityPtr{ (void*)((uint8*)DerivedPointer + PointerOffset), StorageMode };
}
};
/**
* Basic, mostly untyped, implementation of the capabilities buffer.
*/
struct FPlaybackCapabilitiesImpl
{
protected:
FPlaybackCapabilitiesImpl() = default;
FPlaybackCapabilitiesImpl(const FPlaybackCapabilitiesImpl&) = delete;
FPlaybackCapabilitiesImpl& operator=(const FPlaybackCapabilitiesImpl&) = delete;
FPlaybackCapabilitiesImpl(FPlaybackCapabilitiesImpl&&) = delete;
FPlaybackCapabilitiesImpl& operator=(FPlaybackCapabilitiesImpl&&) = delete;
bool HasCapability(uint32 CapabilityBit) const
{
return (AllCapabilities & CapabilityBit) != 0;
}
FPlaybackCapabilityPtr FindCapability(uint32 CapabilityBit) const
{
if (HasCapability(CapabilityBit))
{
const int32 Index = GetCapabilityIndex(CapabilityBit);
check(Index >= 0 && Index < 255);
return GetHeader(static_cast<uint8>(Index)).Resolve(Memory);
}
return FPlaybackCapabilityPtr();
}
FPlaybackCapabilityPtr GetCapabilityChecked(uint32 CapabilityBit) const
{
const int32 Index = GetCapabilityIndex(CapabilityBit);
check(Index >= 0 && Index < 255);
return GetHeader(static_cast<uint8>(Index)).Resolve(Memory);
}
/**
* Creates and stores a new capability object at the given bit.
*
* - StorageType is what is actually constructed and stored in our memory buffer. It can be the same as
* CapabilityType, a subclass of it, a pointer to it, or a shared pointer to it.
*
* - CapabilityType is the base capability type, used onlyfor inline storage when the StorageType is a subclass
* and we need to compute the pointer offset.
*/
template<typename StorageType, typename CapabilityType, typename ...ArgTypes>
FPlaybackCapabilityPtr AddCapability(uint32 CapabilityBit, ArgTypes&&... InArgs)
{
checkf((AllCapabilities & CapabilityBit) == 0, TEXT("Capability already exists!"));
// Add the enum entry
AllCapabilities |= CapabilityBit;
// Find the index of the new capability by counting how many bits are set before it
// For example, given CapabilityBit=0b00010000 and AllCapabilities=0b00011011:
// (CapabilityBit-1) = 0b00001111
// AllCapabilities & (CapabilityBit-1) = 0b00001011
// CountBits(0b00001011) = 3
const int32 NewCapabilityIndex = static_cast<int32>(FMath::CountBits(AllCapabilities & (CapabilityBit-1u)));
const FPlaybackCapabilityHeader* ExistingHeaders = reinterpret_cast<const FPlaybackCapabilityHeader*>(Memory);
uint64 RequiredAlignment = FMath::Max(alignof(StorageType), alignof(FPlaybackCapabilityHeader));
const int32 ExistingNum = static_cast<int32>(Num);
// Compute our required alignment for the allocation
{
for (int32 Index = 0; Index < ExistingNum; ++Index)
{
RequiredAlignment = FMath::Max(RequiredAlignment, (uint64)ExistingHeaders[Index].Alignment);
}
}
// We'll keep track of where all our new capabilities will go, relative to the buffer start
TArray<uint16, TInlineAllocator<16>> NewCapabilityOffsets;
NewCapabilityOffsets.SetNum(ExistingNum + 1);
// Compute the required size of our allocation
uint64 RequiredSizeof = 0u;
{
// Allocate space for headers
RequiredSizeof += (ExistingNum + 1) * sizeof(FPlaybackCapabilityHeader);
int32 Index = 0;
// Count up the sizes and alignments of pre-existing capabilities that exist before this new entry
for (; Index < NewCapabilityIndex; ++Index)
{
RequiredSizeof = Align(RequiredSizeof, (uint64)ExistingHeaders[Index].Alignment);
NewCapabilityOffsets[Index] = static_cast<uint16>(RequiredSizeof);
RequiredSizeof += ExistingHeaders[Index].Sizeof;
}
// Count up the size and alignment for the new capability
RequiredSizeof = Align(RequiredSizeof, alignof(StorageType));
NewCapabilityOffsets[Index] = static_cast<uint16>(RequiredSizeof);
RequiredSizeof += sizeof(StorageType);
++Index;
// Now count up the sizes and alignments of pre-existing capabilities that exist after this new entry
for (; Index < ExistingNum+1; ++Index)
{
RequiredSizeof = Align(RequiredSizeof, (uint64)ExistingHeaders[Index-1].Alignment);
NewCapabilityOffsets[Index] = static_cast<uint16>(RequiredSizeof);
RequiredSizeof += ExistingHeaders[Index-1].Sizeof;
}
}
check( RequiredAlignment <= 0XFF );
/// ----------------------------------------
uint8* OldMemory = Memory;
// Make a new allocation if necessary
const bool bNeedsReallocation = RequiredAlignment > Alignment || RequiredSizeof > Capacity;
if (bNeedsReallocation)
{
// Use the greater of the required size or double the current size to allow some additional capcity
Capacity = static_cast<uint16>(FMath::Max(RequiredSizeof, uint64(Capacity)*2));
Memory = reinterpret_cast<uint8*>(FMemory::Malloc(Capacity, RequiredAlignment));
}
// We now have an extra entry
++Num;
// We now need to re-arrange memory carefully, going back to front in order to avoid overlaps.
// For instance we can only move headers at the end of the whole operation, otherwise they could overwrite
// the memory of the first couple capabilities before we've had a chance to move them. So we do:
//
// 1) Relocate capabilities from the last one up to where the new one will go
// 2) Allocate the new capability
// 3) Relocate capabilities from the one just before the new one, down to the first one
// 4) Relocate headers from the last one up to where the new one will go
// 5) Allocate the new header
// 6) Relocate headers from the one just before the new header, down to the first one
//
// In order to set the new capability pointers (obtained in steps 1-3) on the new headers (obtained in
// steps 4-6), we save these pointers in a temporary array.
const FPlaybackCapabilityHeader* OldHeaders = ExistingHeaders; // Better named variable for the rest of the steps...
auto RelocateCapability = [this, OldMemory, OldHeaders, &NewCapabilityOffsets](int32 OldIndex, int32 NewIndex)
{
void* NewCapabilityPtr = this->Memory + NewCapabilityOffsets[NewIndex];
void* OldCapabilityPtr = OldHeaders[OldIndex].Capability.Resolve(OldMemory);
FMemory::Memmove(NewCapabilityPtr, OldCapabilityPtr, OldHeaders[OldIndex].Sizeof);
};
// Step 1
int32 Index = static_cast<int32>(Num) - 1;
for (; Index > NewCapabilityIndex; --Index)
{
RelocateCapability(Index - 1, Index);
}
// Step 2
{
void* NewCapabilityPtr = this->Memory + NewCapabilityOffsets[Index];
StorageType* NewCapabilityTypedPtr = reinterpret_cast<StorageType*>(NewCapabilityPtr);
new (NewCapabilityTypedPtr) StorageType (Forward<ArgTypes>(InArgs)...);
}
--Index;
// Step 3
for (; Index >= 0; --Index)
{
RelocateCapability(Index, Index);
}
FPlaybackCapabilityHeader* NewHeaders = reinterpret_cast<FPlaybackCapabilityHeader*>(Memory);
auto RelocateHeader = [this, OldHeaders, NewHeaders, &NewCapabilityOffsets](int32 OldIndex, int32 NewIndex)
{
const FPlaybackCapabilityHeader& OldHeader(OldHeaders[OldIndex]);
FPlaybackCapabilityHeader* NewHeaderPtr = &NewHeaders[NewIndex];
new (NewHeaderPtr) FPlaybackCapabilityHeader(OldHeader);
// Re-create the relative capability pointer, since the offset has changed (it's one capability
// further down), and the base pointer may have changed too (if we re-allocated the buffer)
void* NewCapabilityPtr = this->Memory + NewCapabilityOffsets[NewIndex];
NewHeaderPtr->Capability = TRelativePtr<void, uint16>(this->Memory, NewCapabilityPtr);
// The new header will share its debugging pointer with the old header at this point. But the old
// header will be gone when we free the entire old memory buffer after step 6.
};
// Step 4
Index = static_cast<int32>(Num) - 1;
for (; Index > NewCapabilityIndex; --Index)
{
RelocateHeader(Index - 1, Index);
}
// Step 5
FPlaybackCapabilityHeader* NewHeaderPtr = &NewHeaders[Index];
{
using FStorageTraits = TPlaybackCapabilityStorageTraits<StorageType, CapabilityType>;
static_assert(alignof(StorageType) < 0x7F, "Required alignment of capability must fit in 7 bytes");
void* NewCapabilityPtr = Memory + NewCapabilityOffsets[Index];
StorageType* NewCapabilityTypedPtr = reinterpret_cast<StorageType*>(NewCapabilityPtr);
static_assert(sizeof(StorageType) <= MAX_uint16, "Can't store a playback capability with a size above 65535 bits. Store it as a raw or shared pointer.");
static_assert(alignof(StorageType) <= MAX_uint8, "Can't store a playback capability with an alignment above 255 bits. Store it as a raw or shared pointer.");
new (NewHeaderPtr) FPlaybackCapabilityHeader(); // Reset to default constructor
NewHeaderPtr->Capability.Reset(Memory, NewCapabilityPtr);
NewHeaderPtr->Sizeof = static_cast<uint16>(sizeof(StorageType));
NewHeaderPtr->Alignment = static_cast<uint8>(alignof(StorageType));
NewHeaderPtr->StorageMode = FStorageTraits::GetStorageMode();
// The pointer offset is whatever we need to add to the capability pointer in order to get
// a pointer to capability type itself (in case the storage type is a sub-class of it)
// We only need it if the capability object is stored inline inside our memory buffer.
NewHeaderPtr->PointerOffset = FStorageTraits::ComputePointerOffset(NewCapabilityTypedPtr);
#if UE_MOVIESCENE_ENTITY_DEBUG
// Set the debug pointer.
NewHeaderPtr->DebugPtr = new TPlaybackCapabilityDebuggingTypedPtr<StorageType>(NewCapabilityTypedPtr);
#endif
}
--Index;
// Step 6
for (; Index >= 0; --Index)
{
RelocateHeader(Index, Index);
}
/// ----------------------------------------
// Tidy up the old allocation. We do not call destructors here because we relocated everything.
if (bNeedsReallocation && OldMemory)
{
FMemory::Free(OldMemory);
}
// Insert the helpers for the new capability.
Helpers.Insert(TPlaybackCapabilityHelpers<StorageType>::GetHelpers(), NewCapabilityIndex);
// Return the new capability pointer. We call the header's Resolve method here because returning
// the pointer of the stored capability would return the derived type pointer.
// We want the base (capability) pointer.
return NewHeaderPtr->Resolve(Memory);
}
template<typename StorageType, typename CapabilityType, typename ...ArgTypes>
bool OverwriteCapability(uint32 CapabilityBit, ArgTypes&&... InArgs)
{
if (!ensureMsgf(HasCapability(CapabilityBit), TEXT("The given capability does not exist in this container")))
{
return false;
}
// Get the header for this capability
const int32 Index = GetCapabilityIndex(CapabilityBit);
FPlaybackCapabilityHeader& Header = GetHeader(static_cast<uint8>(Index));
// Check that we are overwriting the same storage mode
using FStorageTraits = TPlaybackCapabilityStorageTraits<StorageType, CapabilityType>;
EPlaybackCapabilityStorageMode GivenStorageMode = FStorageTraits::GetStorageMode();
if (!ensureMsgf(GivenStorageMode == Header.StorageMode, TEXT("The given capability storage mode does not match the existing one")))
{
return false;
}
// Check that the types and alignments match
size_t GivenSizeof = sizeof(StorageType);
uint16 GivenAlignment = alignof(StorageType);
if (!ensureMsgf(
(GivenSizeof == Header.Sizeof && GivenAlignment == Header.Alignment),
TEXT("The given capability size and alignment do not match the existing one")))
{
return false;
}
// Don't use the header's Resolve method, we don't want to offset the pointer, we want
// the actual pointer to the actual stored capability.
void* CapabilityPtr = Header.Capability.Resolve(Memory);
// Call the destructor on the previous capability, which is important if it was stored inline or as
// a shared pointer.
Helpers[Index].Destructor(CapabilityPtr);
// Allocate the new capability.
StorageType* TypedCapabilityPtr = reinterpret_cast<StorageType*>(CapabilityPtr);
new (TypedCapabilityPtr) StorageType(Forward<ArgTypes>(InArgs)...);
// If we have inline storage, we could potentially have changed from a stored base-class to
// a stored child-class, or vice-versa, or from one child-class to another child-class. In all these
// cases, the helper functions (destructor, interface cast, etc) have changed, so let's store the
// new helper functions on the header.
Helpers[Index] = TPlaybackCapabilityHelpers<StorageType>::GetHelpers();
#if UE_MOVIESCENE_ENTITY_DEBUG
// Update the debug pointer.
delete Header.DebugPtr;
Header.DebugPtr = new TPlaybackCapabilityDebuggingTypedPtr<StorageType>(TypedCapabilityPtr);
#endif
return true;
}
const FPlaybackCapabilityHeader& GetHeader(uint8 Index) const
{
check(Index < Num);
return reinterpret_cast<FPlaybackCapabilityHeader*>(Memory)[Index];
}
FPlaybackCapabilityHeader& GetHeader(uint8 Index)
{
check(Index < Num);
return reinterpret_cast<FPlaybackCapabilityHeader*>(Memory)[Index];
}
TArrayView<const FPlaybackCapabilityHeader> GetHeaders() const
{
return MakeArrayView(reinterpret_cast<FPlaybackCapabilityHeader*>(Memory), Num);
}
int32 GetCapabilityIndex(uint32 CapabilityBit) const
{
if ( (AllCapabilities & CapabilityBit) == 0)
{
return INDEX_NONE;
}
return FMath::CountBits(AllCapabilities & (CapabilityBit-1u));
}
// Schema:
// [header_1|...|header_n|entry_0|...|entry_n]
uint8* Memory = nullptr;
uint16 Alignment = 0u;
uint16 Capacity = 0u;
uint8 Num = 0u;
uint32 AllCapabilities = 0u;
// Function pointers for invalidating, destroying, etc. capabilities.
TArray<FPlaybackCapabilityHelpers> Helpers;
};
namespace Internal
{
/** Gets the type of the playback capability ID in a backwards compatible way. */
template<typename T, typename = void>
struct TPlaybackCapabilityIDType {};
/** Getter for the new GetPlaybackCapabilityID method. */
template<typename T>
struct TPlaybackCapabilityIDType<
T,
std::enable_if_t<
std::is_assignable_v<FPlaybackCapabilityID, decltype(T::GetPlaybackCapabilityID())>
>>
{
using Type = std::decay_t<decltype(T::GetPlaybackCapabilityID())>;
static Type Get() { return T::GetPlaybackCapabilityID(); }
};
/** Getter for the old ID static field. */
template<typename T>
struct TPlaybackCapabilityIDType<
T,
std::enable_if_t<
std::is_assignable_v<FPlaybackCapabilityID, decltype(T::ID)>
>>
{
using Type = std::decay_t<decltype(T::ID)>;
static Type Get() { return T::ID; }
};
} // namespace Internal
/**
* Actual playback capabilities container.
*/
struct FPlaybackCapabilities : FPlaybackCapabilitiesImpl
{
FPlaybackCapabilities() = default;
FPlaybackCapabilities(const FPlaybackCapabilities&) = delete;
FPlaybackCapabilities& operator=(const FPlaybackCapabilities&) = delete;
MOVIESCENE_API FPlaybackCapabilities(FPlaybackCapabilities&&);
MOVIESCENE_API FPlaybackCapabilities& operator=(FPlaybackCapabilities&&);
MOVIESCENE_API ~FPlaybackCapabilities();
/** Checks whether this container has the given capability */
template<typename T>
bool HasCapability() const
{
uint32 CapabilityBit = 1 << Internal::TPlaybackCapabilityIDType<T>::Get().Index;
return FPlaybackCapabilitiesImpl::HasCapability(CapabilityBit);
}
/** Finds the specified capability within the container, if present */
template<typename T>
T* FindCapability() const
{
// T must be the base capability type, and not a sub-class, because we are returning a pointer to it,
// and we can't check what sort of sub-class we have or not.
using CapabilityIDType = typename Internal::TPlaybackCapabilityIDType<T>::Type;
static_assert(std::is_same<T, typename CapabilityIDType::CapabilityType>::value, "You must pass the actual playback capability type as a template parameter, not a sub-class.");
const TPlaybackCapabilityID<T> CapabilityID(Internal::TPlaybackCapabilityIDType<T>::Get());
uint32 CapabilityBit = 1 << CapabilityID.Index;
FPlaybackCapabilityPtr Ptr = FPlaybackCapabilitiesImpl::FindCapability(CapabilityBit);
return Ptr.ResolveOptional<T>();
}
/** Returns the specified capability within the container, asserts if not found */
template<typename T>
T& GetCapabilityChecked() const
{
// T must be the base capability type, and not a sub-class, because we are returning a reference to it,
// and we can't check what sort of sub-class we have or not.
using CapabilityIDType = typename Internal::TPlaybackCapabilityIDType<T>::Type;
static_assert(std::is_same<T, typename CapabilityIDType::CapabilityType>::value, "You must pass the actual playback capability type as a template parameter, not a sub-class.");
const TPlaybackCapabilityID<T> CapabilityID(Internal::TPlaybackCapabilityIDType<T>::Get());
uint32 CapabilityBit = 1 << CapabilityID.Index;
FPlaybackCapabilityPtr Ptr = FPlaybackCapabilitiesImpl::GetCapabilityChecked(CapabilityBit);
return Ptr.ResolveChecked<T>();
}
/**
* Adds the specified capability to the container, using the supplied arguments to construct it.
* The capability object will be stored inline and owned by this container. It will be destroyed when the
* container itself is destroyed.
* If the template parameter is a sub-class of the playback capability class, that sub-class will be
* created and stored inline.
*/
template<typename T, typename ...ArgTypes>
T& AddCapability(ArgTypes&&... InArgs)
{
using CapabilityIDType = typename Internal::TPlaybackCapabilityIDType<T>::Type;
using CapabilityType = typename CapabilityIDType::CapabilityType;
CapabilityType& NewCapability = DoAddCapability<T>(Internal::TPlaybackCapabilityIDType<T>::Get(), Forward<ArgTypes>(InArgs)...);
return static_cast<T&>(NewCapability);
}
/**
* As per AsCapability, but with an explicit capability ID. This is useful when the first template
* parameter doesn't have a single static member called ID providing the capability ID.
*/
template<typename T, typename U, typename ...ArgTypes>
T& AddCapability(const TPlaybackCapabilityID<U> CapabilityID, ArgTypes&&... InArgs)
{
U& NewCapability = DoAddCapability<T>(CapabilityID, Forward<ArgTypes>(InArgs)...);
return static_cast<T&>(NewCapability);
}
/**
* Adds the specified capability to the container, as a simple raw pointer
* Ownership of the capability object being pointed to is the caller's responsability. This container will only
* store a raw pointer.
*/
template<typename T>
T& AddCapabilityRaw(T* InPointer)
{
using CapabilityIDType = typename Internal::TPlaybackCapabilityIDType<T>::Type;
using CapabilityType = typename CapabilityIDType::CapabilityType;
CapabilityType& NewCapability = DoAddCapability<CapabilityType*>(Internal::TPlaybackCapabilityIDType<T>::Get(), static_cast<CapabilityType*>(InPointer));
return static_cast<T&>(NewCapability);
}
/**
* Adds the specified capability to the container, as a shared pointer
* Ownership of the capability object being pointed to respects classic shared pointer semantics. This container
* only maintains one such shared pointer until the container is destroyed.
*/
template<typename T>
T& AddCapabilityShared(TSharedRef<T> InSharedRef)
{
using CapabilityIDType = typename Internal::TPlaybackCapabilityIDType<T>::Type;
using CapabilityType = typename CapabilityIDType::CapabilityType;
CapabilityType& NewCapability = DoAddCapability<TSharedPtr<CapabilityType>>(Internal::TPlaybackCapabilityIDType<T>::Get(), StaticCastSharedRef<CapabilityType>(InSharedRef));
return static_cast<T&>(NewCapability);
}
/**
* Overwrites an existing capability, stored inline and owned by this container.
* The previous storage mode of the capability must also be inline.
* If the template parameter is a sub-class of the playback capability, the previously stored playback capability
* must not only have been stored inline, but must have been of the same sub-class (or a sub-class with the exact
* same size and alignment).
*/
template<typename T, typename ...ArgTypes>
T& OverwriteCapability(ArgTypes&&... InArgs)
{
using CapabilityIDType = typename Internal::TPlaybackCapabilityIDType<T>::Type;
using CapabilityType = typename CapabilityIDType::CapabilityType;
CapabilityType& NewCapability = DoOverwriteCapability<T>(Internal::TPlaybackCapabilityIDType<T>::Get(), Forward<ArgTypes>(InArgs)...);
return static_cast<T&>(NewCapability);
}
/**
* Overwrites an existing capability, stored as a raw pointer on the container.
* The previous storage mode of the capability must also be a raw pointer.
*/
template<typename T>
T& OverwriteCapabilityRaw(T* InPointer)
{
using CapabilityIDType = typename Internal::TPlaybackCapabilityIDType<T>::Type;
using CapabilityType = typename CapabilityIDType::CapabilityType;
CapabilityType& NewCapability = DoOverwriteCapability<CapabilityType*>(Internal::TPlaybackCapabilityIDType<T>::Get(), static_cast<CapabilityType*>(InPointer));
return static_cast<T&>(NewCapability);
}
/**
* Overwrites an existing capability, stored as a shared pointer on the container.
* The previous storage mode of the capability must also be a shared pointer.
*/
template<typename T>
T& OverwriteCapabilityShared(TSharedRef<T> InSharedRef)
{
using CapabilityIDType = typename Internal::TPlaybackCapabilityIDType<T>::Type;
using CapabilityType = typename CapabilityIDType::CapabilityType;
CapabilityType& NewCapability = DoOverwriteCapability<TSharedPtr<CapabilityType>>(Internal::TPlaybackCapabilityIDType<T>::Get(), StaticCastSharedRef<CapabilityType>(InSharedRef));
return static_cast<T&>(NewCapability);
}
public:
/**
* Calls OnSubInstanceCreated on any capability that implements the IPlaybackCapability interface.
*/
void OnSubInstanceCreated(TSharedRef<const FSharedPlaybackState> Owner, const FInstanceHandle InstanceHandle);
/**
* Calls InvalidateCacheData on any capability that implements the IPlaybackCapability interface.
*/
void InvalidateCachedData(UMovieSceneEntitySystemLinker* Linker);
private:
template<typename Callback, typename ...ArgTypes>
void ForEachCapabilityInterface(Callback&& InCallback, ArgTypes&&... InArgs)
{
TArrayView<const FPlaybackCapabilityHeader> Headers = GetHeaders();
for (int32 Index = 0; Index < Headers.Num(); ++Index)
{
const FPlaybackCapabilityHeader& Header = Headers[Index];
const FPlaybackCapabilityHelpers& ThisHelpers = Helpers[Index];
check(ThisHelpers.InterfaceCast != nullptr);
{
void* Ptr = Header.Capability.Resolve(Memory);
if (IPlaybackCapability* Interface = (*ThisHelpers.InterfaceCast)(Ptr))
{
InCallback(*Interface, Forward<ArgTypes>(InArgs)...);
}
}
}
}
template<typename Impl, typename T, typename ...ArgTypes>
T& DoAddCapability(TPlaybackCapabilityID<T> CapabilityID, ArgTypes&&... InArgs)
{
uint32 CapabilityBit = 1 << CapabilityID.Index;
FPlaybackCapabilityPtr Ptr = FPlaybackCapabilitiesImpl::AddCapability<Impl, T>(CapabilityBit, Forward<ArgTypes>(InArgs)...);
return Ptr.ResolveChecked<T>();
}
template<typename Impl, typename T, typename ...ArgTypes>
T& DoOverwriteCapability(TPlaybackCapabilityID<T> CapabilityID, ArgTypes&&... InArgs)
{
uint32 CapabilityBit = 1 << CapabilityID.Index;
FPlaybackCapabilitiesImpl::OverwriteCapability<Impl, T>(CapabilityBit, Forward<ArgTypes>(InArgs)...);
return GetCapabilityChecked<T>();
}
void Destroy();
};
} // namespace UE::MovieScene