Files
UnrealEngine/Engine/Plugins/Experimental/Buoyancy/Source/Runtime/Private/BuoyancyParticleData.cpp
2025-05-18 13:04:45 +08:00

193 lines
6.3 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "BuoyancyParticleData.h"
bool bBuoyancyParticleDataHashIndex = true;
FAutoConsoleVariableRef CVarBuoyancyParticleDataHashIndex(TEXT("p.Buoyancy.ParticleData.HashIndex"), bBuoyancyParticleDataHashIndex, TEXT("If true, use an FHashTable to index particle data maps."));
float BuoyancyParticleDataHashSizeRatioGrowThreshold = 1.5f;
FAutoConsoleVariableRef CVarBuoyancyParticleDataHashSizeRatioGrowThreshold(TEXT("p.Buoyancy.ParticleData.HashSizeRatioGrowThreshold"), BuoyancyParticleDataHashSizeRatioGrowThreshold, TEXT("If the ratio of index size to hash size passes this threshold, a rehash will occur to optimize memory. Only occurs if bBuoyancyParticleDataHashIndex is true. A value of less than 1 will result in a sparser hash table, a value of greater than 1 results in a denser table. A value of 0 or less will cause the optimization to be skipped. No value can guarantee zero hash collisions."));
int32 BuoyancyParticleDataMinHashSize = 1024;
FAutoConsoleVariableRef CVarBuoyancyParticleDataMinHashSize(TEXT("p.Buoyancy.ParticleData.MinHashSize"), BuoyancyParticleDataMinHashSize, TEXT("Hash size is not allowed to shrink below this size. Must be a power of 2!"));
int32 BuoyancyParticleDataMaxHashSize = 8192;
FAutoConsoleVariableRef CVarBuoyancyParticleDataMaxHashSize(TEXT("p.Buoyancy.ParticleData.MaxHashSize"), BuoyancyParticleDataMaxHashSize, TEXT("Hash size is not allowed to grow beyond this size. Must be a power of 2!"));
FBuoyancyParticleData::FBuoyancyParticleData()
: IndexMap(BuoyancyParticleDataMinHashSize)
{ }
FBuoyancyParticleData::~FBuoyancyParticleData()
{ }
void FBuoyancyParticleData::Reset()
{
SCOPE_CYCLE_COUNTER(STAT_BuoyancyParticleData_Reset)
LLM_SCOPE_BYTAG(BuoyancyParticleDataTag);
IndexMap.Clear();
ReverseIndexMap.Reset();
Interactions.Reset();
Submersions.Reset();
PrevSubmersions.Reset();
SubmersionMetaData.Reset();
PrevSubmersionMetaData.Reset();
SubmergedShapes.Reset();
}
SIZE_T FBuoyancyParticleData::GetAllocatedSize() const
{
return
IndexMap.GetAllocatedSize() +
ReverseIndexMap.GetAllocatedSize() +
Interactions.GetAllocatedSize() +
Submersions.GetAllocatedSize() +
PrevSubmersions.GetAllocatedSize() +
SubmersionMetaData.GetAllocatedSize() +
PrevSubmersionMetaData.GetAllocatedSize() +
SubmergedShapes.GetAllocatedSize();
}
int32 FBuoyancyParticleData::GetIndex(const Chaos::FGeometryParticleHandle& ParticleHandle)
{
int32 ParticleIndex, ParticleKey;
return GetIndex(ParticleHandle, ParticleIndex, ParticleKey);
}
int32 FBuoyancyParticleData::GetIndex(const Chaos::FGeometryParticleHandle& ParticleHandle, int32& OutParticleIndex, int32& OutParticleKey)
{
OutParticleIndex = ParticleHandle.UniqueIdx().Idx;
// If we're not hashing indices here, return the particle index itself
if (bBuoyancyParticleDataHashIndex == false)
{
OutParticleKey = INDEX_NONE;
return OutParticleIndex;
}
OutParticleKey = MurmurFinalize32(OutParticleIndex);
return GetIndex(OutParticleIndex, OutParticleKey);
}
int32 FBuoyancyParticleData::GetIndex(const int32 ParticleIndex, const int32 ParticleKey)
{
for (uint32 DataIndex = IndexMap.First(ParticleKey); IndexMap.IsValid(DataIndex); DataIndex = IndexMap.Next(DataIndex))
{
if (ReverseIndexMap[DataIndex] == ParticleIndex)
{
return DataIndex;
}
}
return INDEX_NONE;
}
int32 FBuoyancyParticleData::AddOrGetIndex(const Chaos::FGeometryParticleHandle& ParticleHandle)
{
LLM_SCOPE_BYTAG(BuoyancyParticleDataTag);
const int32 ParticleIndex = ParticleHandle.UniqueIdx().Idx;
// If we're not hashing indices here, return the particle index itself
if (bBuoyancyParticleDataHashIndex == false)
{
return ParticleIndex;
}
const int32 ParticleKey = MurmurFinalize32(ParticleIndex);
int32 DataIndex = GetIndex(ParticleIndex, ParticleKey);
if (DataIndex == INDEX_NONE)
{
DataIndex = ReverseIndexMap.Add(ParticleIndex);
IndexMap.Add(ParticleKey, DataIndex);
OptimizeMemory();
}
return DataIndex;
}
bool FBuoyancyParticleData::RemoveIndex(const Chaos::FGeometryParticleHandle& ParticleHandle)
{
LLM_SCOPE_BYTAG(BuoyancyParticleDataTag);
int32 ParticleIndex, ParticleKey;
const int32 DataIndex = GetIndex(ParticleHandle, ParticleIndex, ParticleKey);
// Remove the key from our index map
IndexMap.Remove(ParticleKey, DataIndex);
// Remove the data index from all sparse arrays
ReverseIndexMap.RemoveAt(DataIndex);
Interactions.RemoveAt(DataIndex);
Submersions.RemoveAt(DataIndex);
PrevSubmersions.RemoveAt(DataIndex);
SubmersionMetaData.RemoveAt(DataIndex);
PrevSubmersionMetaData.RemoveAt(DataIndex);
SubmergedShapes.RemoveAt(DataIndex);
OptimizeMemory();
return true;
}
void FBuoyancyParticleData::OptimizeMemory()
{
SCOPE_CYCLE_COUNTER(STAT_BuoyancyParticleData_OptimizeMemory)
LLM_SCOPE_BYTAG(BuoyancyParticleDataTag);
// Shrink all maps to fit data
ReverseIndexMap.Shrink();
Interactions.Shrink();
Submersions.Shrink();
PrevSubmersions.Shrink();
SubmersionMetaData.Shrink();
PrevSubmersionMetaData.Shrink();
SubmergedShapes.Shrink();
if (bBuoyancyParticleDataHashIndex == false)
{
return;
}
const float HashSizeRatioThreshold = BuoyancyParticleDataHashSizeRatioGrowThreshold;
if (HashSizeRatioThreshold <= 0.f)
{
return;
}
// Compute the actual hash size ratio - if it hasn't passed the threshold,
// nothing to do.
const float IndexSize = IndexMap.GetIndexSize();
const float HashSize = IndexMap.GetHashSize();
const float HashSizeRatio = IndexSize / FMath::Max(1.f, HashSize);
if (HashSizeRatio <= HashSizeRatioThreshold)
{
return;
}
// Find the smallest power of two which is greater than IndexSize. If it didn't
// change, do nothing.
const int32 MinHashSize = BuoyancyParticleDataMinHashSize;
const int32 MaxHashSize = BuoyancyParticleDataMaxHashSize;
const int32 NewRawHashSize = FMath::Pow(2, FMath::CeilToFloat(FMath::Log2(IndexSize)));
const int32 NewHashSize = FMath::Clamp(NewRawHashSize, MinHashSize, MaxHashSize);
if (HashSize == NewHashSize)
{
return;
}
// If we have a new hash size, reallocate our IndexMap with our new hash size, and
// repopulate it using the reverse index map data.
IndexMap.Clear(NewHashSize, IndexSize);
for (auto It = ReverseIndexMap.CreateConstIterator(); It; ++It)
{
const int32 DataIndex = It.GetIndex();
const int32 ParticleIndex = *It;
const int32 ParticleKey = MurmurFinalize32(ParticleIndex);
IndexMap.Add(ParticleKey, DataIndex);
}
}