Files
UnrealEngineUWP/Engine/Source/Runtime/Engine/Private/SpanAllocator.cpp
ola olsson 825daf7529 Add persistent primitive index (FPrimitiveSceneInfo::PersistentIndex) which is stable for the life-time of a primitive (proxy) in FScene
- can be used to track information about a primitive over time without needing to track changes in PackedPrimitiveIndex
- will have the same high-water mark to packed index, but will persist for longer, potentially (allocator picks new from the front).
- Add PersistentPrimitiveIndex to FPrimitiveSceneData / SceneData.ush, to enable access on the GPU
- Add FSpanAllocator, an incremental improvement over FGrowOnlySpanAllocator - which shrinks as well and has efficient allocation of unit-sized allocations.

#rb rune.stubbe,Krzysztof.Narkowicz
#preflight 61e6b538837b79f7ccdc860f

#ROBOMERGE-AUTHOR: ola.olsson
#ROBOMERGE-SOURCE: CL 18638699 in //UE5/Release-5.0/... via CL 18638708 via CL 18638715
#ROBOMERGE-BOT: UE5 (Release-Engine-Test -> Main) (v899-18417669)

[CL 18638724 by ola olsson in ue5-main branch]
2022-01-18 07:54:10 -05:00

85 lines
2.4 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "SpanAllocator.h"
void FSpanAllocator::Consolidate()
{
// Consolidation
if (PendingFreeSpans.IsEmpty() && FirstNonEmptySpan == 0)
{
return;
}
// 1. Sort the Newly free list by span start, the existing free list is already sorted by construction
PendingFreeSpans.Sort();
// alternate free list, used during consolidation to avoid N^2 worst case, retained to avoid re-allocations.
TArray<FLinearAllocation, TInlineAllocator<10>> FreeSpansTmp;
FreeSpansTmp.Reset(FreeSpans.Num());
int32 PrevEndOffset = INDEX_NONE;
int32 PendingFreeIndex = 0;
// 2. Joint loop and merge over both free list and newly freed and fuse all adjacent, copy into new free list (to avoid compaction)
for (int32 Index = 0; Index < FreeSpans.Num() || PendingFreeIndex < PendingFreeSpans.Num(); )
{
FLinearAllocation Alloc = Index < FreeSpans.Num() ? FreeSpans[Index] : FLinearAllocation{ MAX_int32, 0 };
// Make sure we don't run out of both somehow...
check(PendingFreeIndex < PendingFreeSpans.Num() || Alloc.StartOffset < MAX_int32);
// Consume the next new alloc if it is before the next old one
if (PendingFreeIndex < PendingFreeSpans.Num() && PendingFreeSpans[PendingFreeIndex].StartOffset < Alloc.StartOffset)
{
Alloc = PendingFreeSpans[PendingFreeIndex++];
}
else
{
// Otherwise advance the old allocs
++Index;
}
check(Alloc.StartOffset < MAX_int32);
// Discard empty allocs
if (Alloc.Num > 0)
{
// Continues the previous one, fuse
if (PrevEndOffset == Alloc.StartOffset)
{
FreeSpansTmp.Last().Num += Alloc.Num;
}
else
{
FreeSpansTmp.Add(Alloc);
}
PrevEndOffset = FreeSpansTmp.Last().Num + FreeSpansTmp.Last().StartOffset;
}
}
// Trim last span
if (!FreeSpansTmp.IsEmpty() && FreeSpansTmp.Last().StartOffset + FreeSpansTmp.Last().Num == MaxSize)
{
MaxSize -= FreeSpansTmp.Last().Num;
FreeSpansTmp.Pop(false);
}
// 3. Store new free list
FreeSpans = MoveTemp(FreeSpansTmp);
PendingFreeSpans.Empty(PendingFreeSpans.Num());
FirstNonEmptySpan = 0;
}
int32 FSpanAllocator::SearchFreeList(int32 Num, int32 SearchStartIndex)
{
// Search free list for first matching
for (int32 Index = SearchStartIndex; Index < FreeSpans.Num(); Index++)
{
FLinearAllocation CurrentSpan = FreeSpans[Index];
if (CurrentSpan.Num >= Num)
{
return Index;
}
}
return INDEX_NONE;
}