Files
UnrealEngine/Engine/Source/Runtime/Renderer/Private/Lumen/LumenSparseSpanArray.h
2025-05-18 13:04:45 +08:00

204 lines
4.8 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
LumenSparseSpanArray.h:
=============================================================================*/
#pragma once
#include "CoreMinimal.h"
#include "Engine/EngineTypes.h"
#include "SpanAllocator.h"
// Sparse array with stable indices and contiguous span allocation
template <typename ElementType>
class TSparseSpanArray
{
public:
int32 Num() const
{
return Elements.Num();
}
void Reserve(int32 NumElements)
{
Elements.Reserve(NumElements);
}
int32 AddSpan(int32 NumElements)
{
check(NumElements > 0);
const int32 InsertIndex = SpanAllocator.Allocate(NumElements);
// Resize element array
if (SpanAllocator.GetMaxSize() > Elements.Num())
{
const int32 NumElementsToAdd = SpanAllocator.GetMaxSize() - Elements.Num();
Elements.AddDefaulted(NumElementsToAdd);
AllocatedElementsBitArray.Add(false, NumElementsToAdd);
}
// Reuse existing elements
for (int32 ElementIndex = InsertIndex; ElementIndex < InsertIndex + NumElements; ++ElementIndex)
{
checkSlow(!IsAllocated(ElementIndex));
Elements[ElementIndex] = ElementType();
}
AllocatedElementsBitArray.SetRange(InsertIndex, NumElements, true);
return InsertIndex;
}
void RemoveSpan(int32 FirstElementIndex, int32 NumElements)
{
check(NumElements > 0);
for (int32 ElementIndex = FirstElementIndex; ElementIndex < FirstElementIndex + NumElements; ++ElementIndex)
{
checkSlow(IsAllocated(ElementIndex));
Elements[ElementIndex] = ElementType();
}
SpanAllocator.Free(FirstElementIndex, NumElements);
AllocatedElementsBitArray.SetRange(FirstElementIndex, NumElements, false);
}
void Consolidate()
{
SpanAllocator.Consolidate();
if (Elements.Num() > SpanAllocator.GetMaxSize())
{
Elements.SetNum(SpanAllocator.GetMaxSize());
AllocatedElementsBitArray.SetNumUninitialized(SpanAllocator.GetMaxSize());
}
}
void Reset()
{
Elements.Reset();
SpanAllocator.Reset();
AllocatedElementsBitArray.SetNumUninitialized(0);
}
ElementType& operator[](int32 Index)
{
checkSlow(IsAllocated(Index));
return Elements[Index];
}
const ElementType& operator[](int32 Index) const
{
checkSlow(IsAllocated(Index));
return Elements[Index];
}
bool IsAllocated(int32 ElementIndex) const
{
if (ElementIndex < AllocatedElementsBitArray.Num())
{
return AllocatedElementsBitArray[ElementIndex];
}
return false;
}
SIZE_T GetAllocatedSize() const
{
return Elements.GetAllocatedSize() + AllocatedElementsBitArray.GetAllocatedSize() + SpanAllocator.GetAllocatedSize();
}
class TRangedForIterator
{
public:
TRangedForIterator(TSparseSpanArray<ElementType>& InArray, int32 InElementIndex)
: Array(InArray)
, ElementIndex(InElementIndex)
{
// Scan for the first valid element.
while (ElementIndex < Array.Elements.Num() && !Array.AllocatedElementsBitArray[ElementIndex])
{
++ElementIndex;
}
}
TRangedForIterator operator++()
{
// Scan for the next first valid element.
do
{
++ElementIndex;
} while (ElementIndex < Array.Elements.Num() && !Array.AllocatedElementsBitArray[ElementIndex]);
return *this;
}
bool operator!=(const TRangedForIterator& Other) const
{
return ElementIndex != Other.ElementIndex;
}
ElementType& operator*()
{
return Array.Elements[ElementIndex];
}
private:
TSparseSpanArray<ElementType>& Array;
int32 ElementIndex;
};
class TRangedForConstIterator
{
public:
TRangedForConstIterator(const TSparseSpanArray<ElementType>& InArray, int32 InElementIndex)
: Array(InArray)
, ElementIndex(InElementIndex)
{
// Scan for the first valid element.
while (ElementIndex < Array.Elements.Num() && !Array.AllocatedElementsBitArray[ElementIndex])
{
++ElementIndex;
}
}
TRangedForConstIterator operator++()
{
// Scan for the next first valid element.
do
{
++ElementIndex;
} while (ElementIndex < Array.Elements.Num() && !Array.AllocatedElementsBitArray[ElementIndex]);
return *this;
}
bool operator!=(const TRangedForConstIterator& Other) const
{
return ElementIndex != Other.ElementIndex;
}
const ElementType& operator*() const
{
return Array.Elements[ElementIndex];
}
private:
const TSparseSpanArray<ElementType>& Array;
int32 ElementIndex;
};
// Iterate over all allocated elements (skip free ones)
TRangedForIterator begin() { return TRangedForIterator(*this, 0); }
TRangedForIterator end() { return TRangedForIterator(*this, Elements.Num()); }
TRangedForConstIterator begin() const { return TRangedForConstIterator(*this, 0); }
TRangedForConstIterator end() const { return TRangedForConstIterator(*this, Elements.Num()); }
private:
TArray<ElementType> Elements;
TBitArray<> AllocatedElementsBitArray;
FSpanAllocator SpanAllocator;
};