Files
UnrealEngineUWP/Engine/Source/Runtime/UniversalObjectLocator/Public/UniversalObjectLocatorResolveParameterBuffer.inl
andrew rodham 71231a95e7 Added the ability to provide arbitrary parameters to UOL resolution functions
Parameters must be registered with the UOL module in much the same way as fragment types.
Tidied up globals into a singleton Registry class.
Added a function to FUniversalObjectLocator that allows manual addition of fragments.

Also removed the defunct IActorLocatorFragmentResolver interface in favor of supplying these parameters externally.
Corrected Anim Instance locators being always used when trying to address skeletal mesh components.

#rb david.bromberg

[CL 29890178 by andrew rodham in ue5-main branch]
2023-11-22 12:02:21 -05:00

187 lines
6.1 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "UniversalObjectLocatorResolveParams.h"
namespace UE::UniversalObjectLocator
{
template<typename T, typename ...ArgTypes>
T* FResolveParameterBuffer::AddParameter(TParameterTypeHandle<T> ParameterTypeHandle, ArgTypes&&... InArgs)
{
const UScriptStruct* ParameterStruct = ParameterTypeHandle.Resolve();
if (!ensureMsgf(ParameterStruct, TEXT("Parameter struct is no longer registered!")))
{
return nullptr;
}
const uint8 ParameterIndex = ParameterTypeHandle.GetIndex();
const uint32 ParameterBit = 1 << ParameterIndex;
return static_cast<T*>(AddParameterImpl<T>(ParameterBit, Forward<ArgTypes>(InArgs)...));
}
template<typename ParameterType, typename ...ArgTypes>
ParameterType* FResolveParameterBuffer::AddParameterImpl(uint32 ParameterBit, ArgTypes&&... InArgs)
{
checkf((AllParameters & ParameterBit) == 0, TEXT("Parameter already exists!"));
// Add the enum entry
AllParameters |= ParameterBit;
// Find the index of the new Parameter by counting how many bits are set before it
// For example, given ParameterBit=0b00010000 and AllParameters=0b00011011:
// (ParameterBit-1) = 0b00001111
// AllParameters & (ParameterBit-1) = 0b00001011
// CountBits(0b00001011) = 3
const int32 NewParameterIndex = static_cast<int32>(FMath::CountBits(AllParameters & (ParameterBit-1u)));
FResolveParameterHeader* ExistingHeaders = reinterpret_cast<FResolveParameterHeader*>(Memory);
uint64 RequiredAlignment = FMath::Max(alignof(ParameterType), alignof(FResolveParameterHeader));
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);
}
}
uint64 RequiredSizeof = 0u;
// Compute the required size of our allocation
{
// Allocate space for headers
RequiredSizeof += (ExistingNum+1) * sizeof(FResolveParameterHeader);
int32 Index = 0;
// Count up the sizes and alignments of pre-existing parameters that exist before this new entry
for (; Index < NewParameterIndex; ++Index)
{
RequiredSizeof = Align(RequiredSizeof, (uint64)ExistingHeaders[Index].Alignment);
RequiredSizeof += ExistingHeaders[Index].Sizeof;
}
// Count up the size and alignment for the new parameter
RequiredSizeof = Align(RequiredSizeof, alignof(ParameterType));
RequiredSizeof += sizeof(ParameterType);
++Index;
// Now count up the sizes and alignments of pre-existing parameters that exist after this new entry
for (; Index < ExistingNum+1; ++Index)
{
RequiredSizeof = Align(RequiredSizeof, (uint64)ExistingHeaders[Index-1].Alignment);
RequiredSizeof += ExistingHeaders[Index-1].Sizeof;
}
}
check( RequiredAlignment <= 0XFF );
/// ----------------------------------------
uint8* OldAllocation = Memory;
const bool bShouldFreeMemory = bCanFreeMemory;
// Make a new allocation if necessary
const bool bNeedsReallocation = !IsAligned(Memory, RequiredAlignment) || RequiredSizeof > Capacity;
if (bNeedsReallocation)
{
const uint64 RequiredCapacity = FMath::Max(RequiredSizeof, uint64(Capacity)*2);
check(RequiredCapacity <= std::numeric_limits<uint16>::max());
// 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));
bCanFreeMemory = true;
}
// We now have an extra entry
++Num;
uint8* ParameterPtr = Memory + RequiredSizeof;
auto RelocateParameter = [this, ExistingHeaders, OldAllocation, &ParameterPtr](int32 OldIndex)
{
// Go back
ParameterPtr -= ExistingHeaders[OldIndex].Sizeof;
ParameterPtr = AlignDown(ParameterPtr, ExistingHeaders[OldIndex].Alignment);
void* OldParameter = ExistingHeaders[OldIndex].Resolve(OldAllocation);
FMemory::Memmove(ParameterPtr, OldParameter, ExistingHeaders[OldIndex].Sizeof);
// Overwrite the old header with its new position for error checking when we write the new header
ExistingHeaders[OldIndex].ParameterOffset = static_cast<uint16>(ParameterPtr-Memory);
};
// Relocate old structs that proceed the new index
for (int32 Index = static_cast<int32>(Num) - 1; Index > NewParameterIndex; --Index)
{
RelocateParameter(Index-1);
}
// Make the new entry
{
ParameterPtr -= sizeof(ParameterType);
ParameterPtr = AlignDown(ParameterPtr, alignof(ParameterType));
ParameterType* NewParameterPtr = reinterpret_cast<ParameterType*>(ParameterPtr);
// Allocate the new type
new (NewParameterPtr) ParameterType { Forward<ArgTypes>(InArgs)... };
static_assert(alignof(ParameterType) < 0x7F, "Required alignment of parameter must fit in 7 bytes");
}
FResolveParameterHeader NewHeader = {
static_cast<uint16>(ParameterPtr-Memory),
sizeof(ParameterType),
alignof(ParameterType)
};
// Relocate the entries that are before the new one
for (int32 Index = NewParameterIndex-1; Index >= 0; --Index)
{
RelocateParameter(Index);
}
// Check that we have enough space before for the headers (this should always be the case)
check(ParameterPtr >= Memory + sizeof(FResolveParameterHeader)*Num);
FResolveParameterHeader* NewHeaders = reinterpret_cast<FResolveParameterHeader*>(Memory);
for (int32 Index = static_cast<int32>(Num)-1; Index > NewParameterIndex; --Index)
{
new (&NewHeaders[Index]) FResolveParameterHeader(ExistingHeaders[Index-1]);
};
{
new (&NewHeaders[NewParameterIndex]) FResolveParameterHeader(NewHeader);
}
if (Memory != OldAllocation)
{
for (int32 Index = NewParameterIndex-1; Index >= 0; --Index)
{
new (&NewHeaders[Index]) FResolveParameterHeader(ExistingHeaders[Index]);
};
}
// Tidy up the old allocation. We do not call destructors here because we relocated everything.
if (bNeedsReallocation && OldAllocation && bShouldFreeMemory)
{
FMemory::Free(OldAllocation);
}
return static_cast<ParameterType*>(NewHeaders[NewParameterIndex].Resolve(Memory));
}
} // namespace UE::UniversalObjectLocator