Files
UnrealEngine/Engine/Plugins/Experimental/LearningAgents/Source/Learning/Private/LearningFrameSet.cpp
2025-05-18 13:04:45 +08:00

845 lines
25 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "LearningFrameSet.h"
#include "Async/ParallelFor.h"
namespace UE::Learning
{
namespace FrameSet::Private
{
static inline void FramesCheck(const TLearningArrayView<1, const int32> Frames)
{
const int32 FrameNum = Frames.Num();
for (int32 FrameIdx = 0; FrameIdx < FrameNum - 1; FrameIdx++)
{
check(Frames[FrameIdx + 0] >= 0);
check(Frames[FrameIdx + 1] >= 0);
check(Frames[FrameIdx + 0] < Frames[FrameIdx + 1]);
}
}
static inline int32 FramesUnion(
TLearningArrayView<1, int32> OutFrames,
const TLearningArrayView<1, const int32> LhsFrames,
const TLearningArrayView<1, const int32> RhsFrames)
{
TRACE_CPUPROFILER_EVENT_SCOPE(Learning::FrameSet::Private::FramesUnion);
FramesCheck(LhsFrames);
FramesCheck(RhsFrames);
// Number of frames in lhs and rhs
const int32 LhsNum = LhsFrames.Num();
const int32 RhsNum = RhsFrames.Num();
// Event index for each list of frames
int32 LhsIndex = 0;
int32 RhsIndex = 0;
int32 OutIndex = 0;
// While both frames to process
while (LhsIndex < LhsNum && RhsIndex < RhsNum)
{
// Time of the next lhs, and rhs frames
const int32 LhsT = LhsFrames[LhsIndex];
const int32 RhsT = RhsFrames[RhsIndex];
// Frame from lhs is coming first
if (LhsT < RhsT)
{
OutFrames[OutIndex] = LhsT;
OutIndex++;
LhsIndex++;
}
// Frame from rhs is coming first
else if (RhsT < LhsT)
{
OutFrames[OutIndex] = RhsT;
OutIndex++;
RhsIndex++;
}
// Frame from lhs and rhs coming at same time
else
{
check(LhsT == RhsT);
OutFrames[OutIndex] = LhsT;
OutIndex++;
LhsIndex++;
RhsIndex++;
}
}
// Process any remaining lhs frames
while (LhsIndex < LhsNum)
{
OutFrames[OutIndex] = LhsFrames[LhsIndex];
OutIndex++;
LhsIndex++;
}
// Process any remaining rhs frames
while (RhsIndex < RhsNum)
{
OutFrames[OutIndex] = RhsFrames[RhsIndex];
OutIndex++;
RhsIndex++;
}
FramesCheck(OutFrames.Slice(0, OutIndex));
// Return number of frames added
return OutIndex;
}
static inline int32 FramesIntersection(
TLearningArrayView<1, int32> OutFrames,
const TLearningArrayView<1, const int32> LhsFrames,
const TLearningArrayView<1, const int32> RhsFrames)
{
TRACE_CPUPROFILER_EVENT_SCOPE(Learning::FrameSet::Private::FramesIntersection);
FramesCheck(LhsFrames);
FramesCheck(RhsFrames);
// Number of frames in lhs and rhs
const int32 LhsNum = LhsFrames.Num();
const int32 RhsNum = RhsFrames.Num();
// Event index for each list of frames
int32 LhsIndex = 0;
int32 RhsIndex = 0;
int32 OutIndex = 0;
// While both frames to process
while (LhsIndex < LhsNum && RhsIndex < RhsNum)
{
// Time of the next lhs, and rhs frames
const int32 LhsT = LhsFrames[LhsIndex];
const int32 RhsT = RhsFrames[RhsIndex];
// Frame from lhs is coming first
if (LhsT < RhsT)
{
LhsIndex++;
}
// Frame from rhs is coming first
else if (RhsT < LhsT)
{
RhsIndex++;
}
// Frame from lhs and rhs coming at same time
else
{
check(LhsT == RhsT);
OutFrames[OutIndex] = LhsT;
OutIndex++;
LhsIndex++;
RhsIndex++;
}
}
FramesCheck(OutFrames.Slice(0, OutIndex));
// Return number of frames added
return OutIndex;
}
static inline int32 FramesDifference(
TLearningArrayView<1, int32> OutFrames,
const TLearningArrayView<1, const int32> LhsFrames,
const TLearningArrayView<1, const int32> RhsFrames)
{
TRACE_CPUPROFILER_EVENT_SCOPE(Learning::FrameSet::Private::FramesDifference);
FramesCheck(LhsFrames);
FramesCheck(RhsFrames);
// Number of frames in lhs and rhs
const int32 LhsNum = LhsFrames.Num();
const int32 RhsNum = RhsFrames.Num();
// Event index for each list of frames
int32 LhsIndex = 0;
int32 RhsIndex = 0;
int32 OutIndex = 0;
// While both frames to process
while (LhsIndex < LhsNum && RhsIndex < RhsNum)
{
// Time of the next lhs, and rhs frames
const int32 LhsT = LhsFrames[LhsIndex];
const int32 RhsT = RhsFrames[RhsIndex];
// Frame from lhs is coming first
if (LhsT < RhsT)
{
OutFrames[OutIndex] = LhsT;
OutIndex++;
LhsIndex++;
}
// Frame from rhs is coming first
else if (RhsT < LhsT)
{
RhsIndex++;
}
// Frame from lhs and rhs coming at same time
else
{
check(LhsT == RhsT);
LhsIndex++;
RhsIndex++;
}
}
// Process any remaining lhs frames
while (LhsIndex < LhsNum)
{
OutFrames[OutIndex] = LhsFrames[LhsIndex];
OutIndex++;
LhsIndex++;
}
FramesCheck(OutFrames.Slice(0, OutIndex));
// Return number of frames added
return OutIndex;
}
}
void FFrameSet::Check() const
{
check(EntrySequences.Num() == EntryFrameOffsets.Num());
check(EntrySequences.Num() == EntryFrameNums.Num());
const int32 EntryNum = EntrySequences.Num();
for (int32 EntryIdx = 0; EntryIdx < EntryNum - 1; EntryIdx++)
{
check(EntrySequences[EntryIdx + 0] < EntrySequences[EntryIdx + 1]);
}
for (int32 EntryIdx = 0; EntryIdx < EntryNum; EntryIdx++)
{
check(EntryFrameNums[EntryIdx] > 0);
FrameSet::Private::FramesCheck(GetEntryFrames(EntryIdx));
}
}
bool FFrameSet::IsEmpty() const { return EntrySequences.IsEmpty(); }
void FFrameSet::Empty()
{
EntrySequences.Empty();
EntryFrameOffsets.Empty();
EntryFrameNums.Empty();
Frames.Empty();
}
int32 FFrameSet::GetEntryNum() const { return EntrySequences.Num(); }
TLearningArrayView<1, const int32> FFrameSet::GetEntrySequences() const { return EntrySequences; }
TLearningArrayView<1, const int32> FFrameSet::GetEntryFrameNums() const { return EntryFrameNums; }
int32 FFrameSet::GetEntrySequence(const int32 EntryIdx) const { return EntrySequences[EntryIdx]; }
int32 FFrameSet::GetEntryFrameNum(const int32 EntryIdx) const { return EntryFrameNums[EntryIdx]; }
TLearningArrayView<1, const int32> FFrameSet::GetEntryFrames(const int32 EntryIdx) const { return Frames.Slice(EntryFrameOffsets[EntryIdx], EntryFrameNums[EntryIdx]); }
int32 FFrameSet::GetEntryFrame(const int32 EntryIdx, const int32 FrameIdx) const { return Frames[EntryFrameOffsets[EntryIdx] + FrameIdx]; }
float FFrameSet::GetEntryFrameTime(const int32 EntryIdx, const int32 FrameIdx, const float FrameDeltaTime) const { return GetEntryFrame(EntryIdx, FrameIdx) * FrameDeltaTime; }
int32 FFrameSet::GetEntryOffset(const int32 EntryIdx) const { return EntryFrameOffsets[EntryIdx]; }
int32 FFrameSet::GetTotalFrameNum() const { return Frames.Num(); }
bool FFrameSet::ContainsSequence(const int32 Sequence) const
{
return EntrySequences.ArrayView().Contains(Sequence);
}
bool FFrameSet::Contains(const int32 Sequence, const int32 Frame) const
{
const int32 EntryIdx = FindSequenceEntry(Sequence);
return EntryIdx != INDEX_NONE && GetEntryFrames(EntryIdx).Contains(Frame);
}
int32 FFrameSet::FindSequenceEntry(const int32 Sequence) const
{
return EntrySequences.ArrayView().Find(Sequence);
}
bool FFrameSet::Find(int32& OutEntryIdx, int32& OutFrameIdx, const int32 Sequence, const int32 Frame) const
{
const int32 EntryIdx = FindSequenceEntry(Sequence);
if (EntryIdx != INDEX_NONE)
{
const int32 FoundFrameIdx = GetEntryFrames(EntryIdx).ArrayView().Find(Frame);
if (FoundFrameIdx != INDEX_NONE)
{
OutEntryIdx = EntryIdx;
OutFrameIdx = FoundFrameIdx;
return true;
}
}
OutEntryIdx = INDEX_NONE;
OutFrameIdx = INDEX_NONE;
return false;
}
bool FFrameSet::FindNearest(int32& OutEntryIdx, int32& OutFrameIdx, int32& OutFrameDifference, const int32 Sequence, const int32 Frame) const
{
const int32 EntryIdx = FindSequenceEntry(Sequence);
if (EntryIdx != INDEX_NONE)
{
OutFrameDifference = INT32_MAX;
OutEntryIdx = INDEX_NONE;
OutFrameIdx = INDEX_NONE;
const int32 FrameNum = GetEntryFrameNum(EntryIdx);
for (int32 FrameIdx = 0; FrameIdx < FrameNum; FrameIdx++)
{
const int32 FrameDifference = GetEntryFrame(EntryIdx, FrameIdx) - Frame;
if (OutFrameDifference == INT32_MAX || FMath::Abs(FrameDifference) < OutFrameDifference)
{
OutFrameDifference = FrameDifference;
OutEntryIdx = EntryIdx;
OutFrameIdx = FrameIdx;
}
}
return OutFrameDifference != INT32_MAX;
}
OutFrameDifference = INT32_MAX;
OutEntryIdx = INDEX_NONE;
OutFrameIdx = INDEX_NONE;
return false;
}
bool FFrameSet::FindNearestInRange(int32& OutEntryIdx, int32& OutFrameIdx, int32& OutFrameDifference, const int32 Sequence, const int32 Frame, const int32 RangeStart, const int32 RangeLength) const
{
check(Frame >= RangeStart && Frame < RangeStart + RangeLength);
const int32 EntryIdx = FindSequenceEntry(Sequence);
if (EntryIdx != INDEX_NONE)
{
OutFrameDifference = INT32_MAX;
OutEntryIdx = INDEX_NONE;
OutFrameIdx = INDEX_NONE;
const int32 FrameNum = GetEntryFrameNum(EntryIdx);
for (int32 FrameIdx = 0; FrameIdx < FrameNum; FrameIdx++)
{
const int32 EntryFrame = GetEntryFrame(EntryIdx, FrameIdx);
if (EntryFrame >= RangeStart && EntryFrame < RangeStart + RangeLength)
{
const int32 FrameDifference = EntryFrame - Frame;
if (OutFrameDifference == INT32_MAX || FMath::Abs(FrameDifference) < OutFrameDifference)
{
OutFrameDifference = FrameDifference;
OutEntryIdx = EntryIdx;
OutFrameIdx = FrameIdx;
}
}
}
return OutFrameDifference != INT32_MAX;
}
OutFrameDifference = INT32_MAX;
OutEntryIdx = INDEX_NONE;
OutFrameIdx = INDEX_NONE;
return false;
}
bool FFrameSet::FindOffset(int32& OutEntryIdx, int32& OutFrameIdx, const int32 Offset) const
{
const int32 EntryNum = GetEntryNum();
for (int32 EntryIdx = 0; EntryIdx < EntryNum; EntryIdx++)
{
const int32 FrameNum = GetEntryFrameNum(EntryIdx);
const int32 FrameOffset = GetEntryOffset(EntryIdx);
if (Offset >= FrameOffset && Offset < FrameOffset + FrameNum)
{
OutEntryIdx = EntryIdx;
OutFrameIdx = Offset - FrameOffset;
return true;
}
}
OutEntryIdx = INDEX_NONE;
OutFrameIdx = INDEX_NONE;
return false;
}
void FFrameSet::AddEntry(
const int32 InSequence,
const TLearningArrayView<1, const int32> InFrames)
{
check(!EntrySequences.ArrayView().Contains(InSequence));
UE::Learning::FrameSet::Private::FramesCheck(InFrames);
if (InFrames.IsEmpty()) { return; }
const int32 CurrFrameOffset = Frames.Num();
const int32 AddFrameNum = InFrames.Num();
Frames.SetNumUninitialized({ CurrFrameOffset + AddFrameNum });
Array::Copy(Frames.Slice(CurrFrameOffset, AddFrameNum), InFrames);
const int32 CurrEntryNum = EntrySequences.Num();
EntrySequences.SetNumUninitialized({ CurrEntryNum + 1 });
EntryFrameOffsets.SetNumUninitialized({ CurrEntryNum + 1 });
EntryFrameNums.SetNumUninitialized({ CurrEntryNum + 1 });
EntrySequences[CurrEntryNum] = InSequence;
EntryFrameOffsets[CurrEntryNum] = CurrFrameOffset;
EntryFrameNums[CurrEntryNum] = AddFrameNum;
Check();
}
namespace FrameSet
{
bool Equal(const FFrameSet& Lhs, const FFrameSet& Rhs)
{
return
Lhs.EntrySequences.Num() == Rhs.EntrySequences.Num() &&
Lhs.Frames.Num() == Rhs.Frames.Num() &&
Array::Equal(Lhs.EntrySequences, Rhs.EntrySequences) &&
Array::Equal(Lhs.EntryFrameOffsets, Rhs.EntryFrameOffsets) &&
Array::Equal(Lhs.EntryFrameNums, Rhs.EntryFrameNums) &&
Array::Equal(Lhs.Frames, Rhs.Frames);
}
void Union(FFrameSet& Out, const FFrameSet& Lhs, const FFrameSet& Rhs)
{
TRACE_CPUPROFILER_EVENT_SCOPE(Learning::FrameSet::Union);
if (Equal(Lhs, Rhs)) { Out = Lhs; return; }
const int32 LhsEntryNum = Lhs.GetEntryNum();
const int32 RhsEntryNum = Rhs.GetEntryNum();
const int32 LhsFrameNum = Lhs.GetTotalFrameNum();
const int32 RhsFrameNum = Rhs.GetTotalFrameNum();
// Allocate potential maximum number of entries and frames we might to output
Out.EntrySequences.SetNumUninitialized({ LhsEntryNum + RhsEntryNum });
Out.EntryFrameOffsets.SetNumUninitialized({ LhsEntryNum + RhsEntryNum });
Out.EntryFrameNums.SetNumUninitialized({ LhsEntryNum + RhsEntryNum });
Out.Frames.SetNumUninitialized({ LhsFrameNum + RhsFrameNum });
// Entry index for each list of frames
int32 OutIndex = 0;
int32 LhsIndex = 0;
int32 RhsIndex = 0;
// Output entry index
int32 EventIndex = 0;
// While both sets have entries
while (LhsIndex < LhsEntryNum && RhsIndex < RhsEntryNum)
{
// If entry from lhs is first
if (Lhs.GetEntrySequence(LhsIndex) < Rhs.GetEntrySequence(RhsIndex))
{
// Append frames to output
if (Lhs.GetEntryFrameNum(LhsIndex) > 0)
{
Out.EntrySequences[OutIndex] = Lhs.GetEntrySequence(LhsIndex);
Out.EntryFrameOffsets[OutIndex] = EventIndex;
Out.EntryFrameNums[OutIndex] = Lhs.GetEntryFrameNum(LhsIndex);
Array::Copy(Out.Frames.Slice(EventIndex, Lhs.GetEntryFrameNum(LhsIndex)), Lhs.GetEntryFrames(LhsIndex));
EventIndex += Lhs.GetEntryFrameNum(LhsIndex);
OutIndex++;
}
LhsIndex++;
}
// If entry from rhs is first
else if (Rhs.GetEntrySequence(RhsIndex) < Lhs.GetEntrySequence(LhsIndex))
{
// Append frames to output
if (Rhs.GetEntryFrameNum(RhsIndex) > 0)
{
Out.EntrySequences[OutIndex] = Rhs.GetEntrySequence(RhsIndex);
Out.EntryFrameOffsets[OutIndex] = EventIndex;
Out.EntryFrameNums[OutIndex] = Rhs.GetEntryFrameNum(RhsIndex);
Array::Copy(Out.Frames.Slice(EventIndex, Rhs.GetEntryFrameNum(RhsIndex)), Rhs.GetEntryFrames(RhsIndex));
EventIndex += Rhs.GetEntryFrameNum(RhsIndex);
OutIndex++;
}
RhsIndex++;
}
// If both contain the same entry
else
{
check(Rhs.GetEntrySequence(RhsIndex) == Lhs.GetEntrySequence(LhsIndex));
// Append union to output
const int32 FrameNum = Private::FramesUnion(
Out.Frames.Slice(EventIndex, LhsFrameNum + RhsFrameNum - EventIndex),
Lhs.GetEntryFrames(LhsIndex),
Rhs.GetEntryFrames(RhsIndex));
check(FrameNum <= Lhs.GetEntryFrameNum(LhsIndex) + Rhs.GetEntryFrameNum(RhsIndex));
if (FrameNum > 0)
{
Out.EntrySequences[OutIndex] = Lhs.GetEntrySequence(LhsIndex);
Out.EntryFrameOffsets[OutIndex] = EventIndex;
Out.EntryFrameNums[OutIndex] = FrameNum;
EventIndex += FrameNum;
OutIndex++;
}
LhsIndex++; RhsIndex++;
}
}
// Process any remaining lhs entries
while (LhsIndex < LhsEntryNum)
{
// Append frames to output
if (Lhs.GetEntryFrameNum(LhsIndex) > 0)
{
Out.EntrySequences[OutIndex] = Lhs.GetEntrySequence(LhsIndex);
Out.EntryFrameOffsets[OutIndex] = EventIndex;
Out.EntryFrameNums[OutIndex] = Lhs.GetEntryFrameNum(LhsIndex);
Array::Copy(Out.Frames.Slice(EventIndex, Lhs.GetEntryFrameNum(LhsIndex)), Lhs.GetEntryFrames(LhsIndex));
EventIndex += Lhs.GetEntryFrameNum(LhsIndex);
OutIndex++;
}
LhsIndex++;
}
// Process any remaining rhs entries
while (RhsIndex < RhsEntryNum)
{
// Append frames to output
if (Rhs.GetEntryFrameNum(RhsIndex) > 0)
{
Out.EntrySequences[OutIndex] = Rhs.GetEntrySequence(RhsIndex);
Out.EntryFrameOffsets[OutIndex] = EventIndex;
Out.EntryFrameNums[OutIndex] = Rhs.GetEntryFrameNum(RhsIndex);
Array::Copy(Out.Frames.Slice(EventIndex, Rhs.GetEntryFrameNum(RhsIndex)), Rhs.GetEntryFrames(RhsIndex));
EventIndex += Rhs.GetEntryFrameNum(RhsIndex);
OutIndex++;
}
RhsIndex++;
}
// Resize output to match what was added
Out.EntrySequences.SetNumUninitialized({ OutIndex });
Out.EntryFrameOffsets.SetNumUninitialized({ OutIndex });
Out.EntryFrameNums.SetNumUninitialized({ OutIndex });
Out.Frames.SetNumUninitialized({ EventIndex });
Out.Check();
}
void Intersection(FFrameSet& Out, const FFrameSet& Lhs, const FFrameSet& Rhs)
{
TRACE_CPUPROFILER_EVENT_SCOPE(Learning::FrameSet::Intersection);
if (Equal(Lhs, Rhs)) { Out = Lhs; return; }
const int32 LhsEntryNum = Lhs.GetEntryNum();
const int32 RhsEntryNum = Rhs.GetEntryNum();
const int32 LhsFrameNum = Lhs.GetTotalFrameNum();
const int32 RhsFrameNum = Rhs.GetTotalFrameNum();
// Allocate potential maximum number of entries and frames we might to output
Out.EntrySequences.SetNumUninitialized({ FMath::Max(LhsEntryNum, RhsEntryNum) });
Out.EntryFrameOffsets.SetNumUninitialized({ FMath::Max(LhsEntryNum, RhsEntryNum) });
Out.EntryFrameNums.SetNumUninitialized({ FMath::Max(LhsEntryNum, RhsEntryNum) });
Out.Frames.SetNumUninitialized({ LhsFrameNum + RhsFrameNum });
// Entry index for each list of frames
int32 OutIndex = 0;
int32 LhsIndex = 0;
int32 RhsIndex = 0;
// Output entry index
int32 EventIndex = 0;
// While both sets have entries
while (LhsIndex < LhsEntryNum && RhsIndex < RhsEntryNum)
{
// If entry from lhs is first
if (Lhs.GetEntrySequence(LhsIndex) < Rhs.GetEntrySequence(RhsIndex))
{
LhsIndex++;
}
// If entry from rhs is first
else if (Rhs.GetEntrySequence(RhsIndex) < Lhs.GetEntrySequence(LhsIndex))
{
RhsIndex++;
}
// If both contain the same entry
else
{
check(Rhs.GetEntrySequence(RhsIndex) == Lhs.GetEntrySequence(LhsIndex));
// Append intersection to output
const int32 FrameNum = Private::FramesIntersection(
Out.Frames.Slice(EventIndex, LhsFrameNum + RhsFrameNum - EventIndex),
Lhs.GetEntryFrames(LhsIndex),
Rhs.GetEntryFrames(RhsIndex));
check(FrameNum <= Lhs.GetEntryFrameNum(LhsIndex) + Rhs.GetEntryFrameNum(RhsIndex));
if (FrameNum > 0)
{
Out.EntrySequences[OutIndex] = Lhs.GetEntrySequence(LhsIndex);
Out.EntryFrameOffsets[OutIndex] = EventIndex;
Out.EntryFrameNums[OutIndex] = FrameNum;
EventIndex += FrameNum;
OutIndex++;
}
LhsIndex++; RhsIndex++;
}
}
// Resize output to match what was added
Out.EntrySequences.SetNumUninitialized({ OutIndex });
Out.EntryFrameOffsets.SetNumUninitialized({ OutIndex });
Out.EntryFrameNums.SetNumUninitialized({ OutIndex });
Out.Frames.SetNumUninitialized({ EventIndex });
Out.Check();
}
void Difference(FFrameSet& Out, const FFrameSet& Lhs, const FFrameSet& Rhs)
{
TRACE_CPUPROFILER_EVENT_SCOPE(Learning::FrameSet::Difference);
if (Equal(Lhs, Rhs)) { Out = FFrameSet(); return; }
const int32 LhsEntryNum = Lhs.GetEntryNum();
const int32 RhsEntryNum = Rhs.GetEntryNum();
const int32 LhsFrameNum = Lhs.GetTotalFrameNum();
const int32 RhsFrameNum = Rhs.GetTotalFrameNum();
// Allocate potential maximum number of entries and frames we might to output
Out.EntrySequences.SetNumUninitialized({ LhsEntryNum });
Out.EntryFrameOffsets.SetNumUninitialized({ LhsEntryNum });
Out.EntryFrameNums.SetNumUninitialized({ LhsEntryNum });
Out.Frames.SetNumUninitialized({ LhsFrameNum + RhsFrameNum });
// Entry index for each list of frames
int32 OutIndex = 0;
int32 LhsIndex = 0;
int32 RhsIndex = 0;
// Output entry index
int32 EventIndex = 0;
// While both sets have entries
while (LhsIndex < LhsEntryNum && RhsIndex < RhsEntryNum)
{
// If entry from lhs is first
if (Lhs.GetEntrySequence(LhsIndex) < Rhs.GetEntrySequence(RhsIndex))
{
// Append frames to output
if (Lhs.GetEntryFrameNum(LhsIndex) > 0)
{
Out.EntrySequences[OutIndex] = Lhs.GetEntrySequence(LhsIndex);
Out.EntryFrameOffsets[OutIndex] = EventIndex;
Out.EntryFrameNums[OutIndex] = Lhs.GetEntryFrameNum(LhsIndex);
Array::Copy(Out.Frames.Slice(EventIndex, Lhs.GetEntryFrameNum(LhsIndex)), Lhs.GetEntryFrames(LhsIndex));
EventIndex += Lhs.GetEntryFrameNum(LhsIndex);
OutIndex++;
}
LhsIndex++;
}
// If entry from rhs is first
else if (Rhs.GetEntrySequence(RhsIndex) < Lhs.GetEntrySequence(LhsIndex))
{
RhsIndex++;
}
// If both contain the same entry
else
{
check(Rhs.GetEntrySequence(RhsIndex) == Lhs.GetEntrySequence(LhsIndex));
// Append difference to output
const int32 FrameNum = Private::FramesDifference(
Out.Frames.Slice(EventIndex, LhsFrameNum + RhsFrameNum - EventIndex),
Lhs.GetEntryFrames(LhsIndex),
Rhs.GetEntryFrames(RhsIndex));
check(FrameNum <= Lhs.GetEntryFrameNum(LhsIndex) + Rhs.GetEntryFrameNum(RhsIndex));
if (FrameNum > 0)
{
Out.EntrySequences[OutIndex] = Lhs.GetEntrySequence(LhsIndex);
Out.EntryFrameOffsets[OutIndex] = EventIndex;
Out.EntryFrameNums[OutIndex] = FrameNum;
EventIndex += FrameNum;
OutIndex++;
}
LhsIndex++; RhsIndex++;
}
}
// Process any remaining lhs entries
while (LhsIndex < LhsEntryNum)
{
// Append frames to output
if (Lhs.GetEntryFrameNum(LhsIndex) > 0)
{
Out.EntrySequences[OutIndex] = Lhs.GetEntrySequence(LhsIndex);
Out.EntryFrameOffsets[OutIndex] = EventIndex;
Out.EntryFrameNums[OutIndex] = Lhs.GetEntryFrameNum(LhsIndex);
Array::Copy(Out.Frames.Slice(EventIndex, Lhs.GetEntryFrameNum(LhsIndex)), Lhs.GetEntryFrames(LhsIndex));
EventIndex += Lhs.GetEntryFrameNum(LhsIndex);
OutIndex++;
}
LhsIndex++;
}
// Resize output to match what was added
Out.EntrySequences.SetNumUninitialized({ OutIndex });
Out.EntryFrameOffsets.SetNumUninitialized({ OutIndex });
Out.EntryFrameNums.SetNumUninitialized({ OutIndex });
Out.Frames.SetNumUninitialized({ EventIndex });
Out.Check();
}
void AllFrameEntries(
TLearningArrayView<1, int32> OutFrameEntries,
const FFrameSet& FrameSet)
{
check(OutFrameEntries.Num() == FrameSet.GetTotalFrameNum());
const int32 TotalFrameNum = FrameSet.GetTotalFrameNum();
const int32 EntryNum = FrameSet.GetEntryNum();
int32 FrameOffset = 0;
for (int32 EntryIdx = 0; EntryIdx < EntryNum; EntryIdx++)
{
const int32 FrameNum = FrameSet.GetEntryFrameNum(EntryIdx);
Array::Set(OutFrameEntries.Slice(FrameOffset, FrameNum), EntryIdx);
FrameOffset += FrameNum;
}
check(FrameOffset == TotalFrameNum);
}
void AllFrameIndices(
TLearningArrayView<1, int32> OutFrameIndices,
const FFrameSet& FrameSet)
{
check(OutFrameIndices.Num() == FrameSet.GetTotalFrameNum());
const int32 TotalFrameNum = FrameSet.GetTotalFrameNum();
const int32 EntryNum = FrameSet.GetEntryNum();
int32 FrameOffset = 0;
for (int32 EntryIdx = 0; EntryIdx < EntryNum; EntryIdx++)
{
const int32 FrameNum = FrameSet.GetEntryFrameNum(EntryIdx);
for (int32 FrameIdx = 0; FrameIdx < FrameNum; FrameIdx++)
{
OutFrameIndices[FrameOffset + FrameIdx] = FrameIdx;
}
FrameOffset += FrameNum;
}
check(FrameOffset == TotalFrameNum);
}
void AllFrameSequences(
TLearningArrayView<1, int32> OutFrameSequences,
const FFrameSet& FrameSet)
{
check(OutFrameSequences.Num() == FrameSet.GetTotalFrameNum());
const int32 TotalFrameNum = FrameSet.GetTotalFrameNum();
const int32 EntryNum = FrameSet.GetEntryNum();
int32 FrameOffset = 0;
for (int32 EntryIdx = 0; EntryIdx < EntryNum; EntryIdx++)
{
const int32 FrameNum = FrameSet.GetEntryFrameNum(EntryIdx);
Array::Set(OutFrameSequences.Slice(FrameOffset, FrameNum), FrameSet.GetEntrySequence(EntryIdx));
FrameOffset += FrameNum;
}
check(FrameOffset == TotalFrameNum);
}
void AllFrameTimes(
TLearningArrayView<1, float> OutFrameTimes,
const FFrameSet& FrameSet,
const float FrameDeltaTime)
{
check(OutFrameTimes.Num() == FrameSet.GetTotalFrameNum());
const int32 TotalFrameNum = FrameSet.GetTotalFrameNum();
for (int32 FrameIdx = 0; FrameIdx < TotalFrameNum; FrameIdx++)
{
OutFrameTimes[FrameIdx] = FrameSet.Frames[FrameIdx] * FrameDeltaTime;
}
}
void ForEachFrame(
const FFrameSet& FrameSet,
const TFunctionRef<void(
const int32 TotalFrameIdx,
const int32 EntryIdx,
const int32 FrameIdx)> Body)
{
const int32 EntryNum = FrameSet.GetEntryNum();
for (int32 EntryIdx = 0; EntryIdx < EntryNum; EntryIdx++)
{
const int32 FrameNum = FrameSet.GetEntryFrameNum(EntryIdx);
const int32 FrameOffset = FrameSet.GetEntryOffset(EntryIdx);
for (int32 FrameIdx = 0; FrameIdx < FrameNum; FrameIdx++)
{
Body(
FrameOffset + FrameIdx,
EntryIdx,
FrameIdx);
}
}
}
void ParallelForEachFrame(
const FFrameSet& FrameSet,
const TFunctionRef<void(
const int32 TotalFrameIdx,
const int32 EntryIdx,
const int32 FrameIdx)> Body)
{
const int32 TotalFrameNum = FrameSet.GetTotalFrameNum();
TLearningArray<1, int32> FrameEntries;
TLearningArray<1, int32> FrameIndices;
FrameEntries.SetNumUninitialized({ TotalFrameNum });
FrameIndices.SetNumUninitialized({ TotalFrameNum });
AllFrameEntries(FrameEntries, FrameSet);
AllFrameIndices(FrameIndices, FrameSet);
ParallelFor(TotalFrameNum, [&Body, &FrameEntries, &FrameIndices](const int32 TotalFrameIdx) {
Body(
TotalFrameIdx,
FrameEntries[TotalFrameIdx],
FrameIndices[TotalFrameIdx]);
});
}
}
}