measure(step2c): setup once per chunk, still 1.6 us/sample -- exact per-sample density misses 600 ms
Editor-only, default off. 259^3 ring split into 1,000 aligned 32^3 chunks; cache/index builds bounded by chunks (948 room/SDF caches, 800 active-op lists, 8,532 index tables); 800 block setups + 201,243 scalar fallback points (1.16%); no per-point GetDensityAt on supported routes. Six workers: live 7.07 s wall / 1.678 us per sample; prepared 7.92 s / 1.595 us. Candidate visits 21.2 -> 15.8 per sample, evaluations 6.31 -> 5.75; room / tunnel / tunnel-core evaluations unchanged (48.1 M / 5.0 M / 38.4 M for 17.4 M samples). Passage evals 18.1 M -> 8.4 M. Exact: 0 Q16/sign disagreements, five seeds, same hash on 1 and 6 workers, reach shrink caught. Luna's verdict: 600 ms is not reachable while every lattice point evaluates the exact current density; 27.7 worker-CPU seconds need >= 7.7x less work. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
1 parent
fd9f809c1c
commit
43c11ccaa8
9 files changed
+484
-143
No files matched your search
@@ -75,13 +75,13 @@ namespace VoxelForgeRegionEval
|
||||
return static_cast<int64>(Dimension) * Dimension * Dimension;
|
||||
}
|
||||
|
||||
struct FRegionBrickSample
|
||||
struct FRegionChunkSample
|
||||
{
|
||||
int32 LocalIndex = 0;
|
||||
int32 OutputIndex = 0;
|
||||
};
|
||||
|
||||
struct FRegionBrick
|
||||
struct FRegionChunk
|
||||
{
|
||||
FIntVector Origin = FIntVector::ZeroValue;
|
||||
int32 SizeX = 0;
|
||||
@@ -90,7 +90,7 @@ namespace VoxelForgeRegionEval
|
||||
int32 LatticeX = 0;
|
||||
int32 LatticeY = 0;
|
||||
int32 LatticeZ = 0;
|
||||
TArray<FRegionBrickSample> RequestedSamples;
|
||||
TArray<FRegionChunkSample> RequestedSamples;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -148,8 +148,9 @@ namespace VoxelForgeRegionEval
|
||||
float ReachScale,
|
||||
TArray<float>* OutDensities,
|
||||
FTiming& OutTiming,
|
||||
bool bBrickProgram,
|
||||
int32 RequestedWorkerCount)
|
||||
bool bRegionProgram,
|
||||
int32 RequestedWorkerCount,
|
||||
bool bRequestWholeRunCounters)
|
||||
{
|
||||
OutTiming = FTiming();
|
||||
const int32 NumWorkers = FMath::Clamp(RequestedWorkerCount, 1, 32);
|
||||
@@ -167,58 +168,70 @@ namespace VoxelForgeRegionEval
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const VoxelDensityProfile::EMode PreviousProfileMode = VoxelDensityProfile::GetMode();
|
||||
const uint32 PreviousProfileInterval = VoxelDensityProfile::GetSampleInterval();
|
||||
if (OutDensities != nullptr && SampleCount > MAX_int32)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const bool bCollectWholeRunCounters = bRequestWholeRunCounters && bFullRing;
|
||||
if (bCollectWholeRunCounters)
|
||||
{
|
||||
VoxelDensityProfile::Reset();
|
||||
VoxelDensityProfile::SetMode(VoxelDensityProfile::EMode::Attribution, 16384);
|
||||
}
|
||||
const double StartSeconds = FPlatformTime::Seconds();
|
||||
TArray<FRegionBrick> Bricks;
|
||||
TArray<int32> WorkBricks;
|
||||
if (bBrickProgram)
|
||||
TArray<FRegionChunk> Regions;
|
||||
TArray<int32> WorkRegions;
|
||||
if (bRegionProgram)
|
||||
{
|
||||
const int32 LastX = OriginVoxels.X + Dimension - 1;
|
||||
const int32 LastY = OriginVoxels.Y + Dimension - 1;
|
||||
const int32 LastZ = OriginVoxels.Z + Dimension - 1;
|
||||
const int32 FirstCellX = FMath::FloorToInt(static_cast<float>(OriginVoxels.X) / 8.0f);
|
||||
const int32 FirstCellY = FMath::FloorToInt(static_cast<float>(OriginVoxels.Y) / 8.0f);
|
||||
const int32 FirstCellZ = FMath::FloorToInt(static_cast<float>(OriginVoxels.Z) / 8.0f);
|
||||
const int32 LastCellX = FMath::FloorToInt(static_cast<float>(LastX) / 8.0f);
|
||||
const int32 LastCellY = FMath::FloorToInt(static_cast<float>(LastY) / 8.0f);
|
||||
const int32 LastCellZ = FMath::FloorToInt(static_cast<float>(LastZ) / 8.0f);
|
||||
const int32 FirstCellX = FMath::FloorToInt(static_cast<float>(OriginVoxels.X) / CHUNK_SIZE);
|
||||
const int32 FirstCellY = FMath::FloorToInt(static_cast<float>(OriginVoxels.Y) / CHUNK_SIZE);
|
||||
const int32 FirstCellZ = FMath::FloorToInt(static_cast<float>(OriginVoxels.Z) / CHUNK_SIZE);
|
||||
const int32 LastCellX = FMath::FloorToInt(static_cast<float>(LastX) / CHUNK_SIZE);
|
||||
const int32 LastCellY = FMath::FloorToInt(static_cast<float>(LastY) / CHUNK_SIZE);
|
||||
const int32 LastCellZ = FMath::FloorToInt(static_cast<float>(LastZ) / CHUNK_SIZE);
|
||||
const int32 CellsX = LastCellX - FirstCellX + 1;
|
||||
const int32 CellsY = LastCellY - FirstCellY + 1;
|
||||
const int32 CellsZ = LastCellZ - FirstCellZ + 1;
|
||||
Bricks.Reserve(CellsX * CellsY * CellsZ);
|
||||
Regions.Reserve(CellsX * CellsY * CellsZ);
|
||||
for (int32 CellZ = FirstCellZ; CellZ <= LastCellZ; ++CellZ)
|
||||
for (int32 CellY = FirstCellY; CellY <= LastCellY; ++CellY)
|
||||
for (int32 CellX = FirstCellX; CellX <= LastCellX; ++CellX)
|
||||
{
|
||||
FRegionBrick& Brick = Bricks.AddDefaulted_GetRef();
|
||||
const int32 CellMinX = CellX * 8;
|
||||
const int32 CellMinY = CellY * 8;
|
||||
const int32 CellMinZ = CellZ * 8;
|
||||
Brick.Origin = FIntVector(
|
||||
FRegionChunk& Region = Regions.AddDefaulted_GetRef();
|
||||
const int32 CellMinX = CellX * CHUNK_SIZE;
|
||||
const int32 CellMinY = CellY * CHUNK_SIZE;
|
||||
const int32 CellMinZ = CellZ * CHUNK_SIZE;
|
||||
Region.Origin = FIntVector(
|
||||
FMath::Max(CellMinX, OriginVoxels.X),
|
||||
FMath::Max(CellMinY, OriginVoxels.Y),
|
||||
FMath::Max(CellMinZ, OriginVoxels.Z));
|
||||
const int32 EndX = FMath::Min(CellMinX + 8, LastX + 1);
|
||||
const int32 EndY = FMath::Min(CellMinY + 8, LastY + 1);
|
||||
const int32 EndZ = FMath::Min(CellMinZ + 8, LastZ + 1);
|
||||
Brick.SizeX = EndX - Brick.Origin.X;
|
||||
Brick.SizeY = EndY - Brick.Origin.Y;
|
||||
Brick.SizeZ = EndZ - Brick.Origin.Z;
|
||||
Brick.LatticeX = Brick.Origin.X - OriginVoxels.X;
|
||||
Brick.LatticeY = Brick.Origin.Y - OriginVoxels.Y;
|
||||
Brick.LatticeZ = Brick.Origin.Z - OriginVoxels.Z;
|
||||
const int32 EndX = FMath::Min(CellMinX + CHUNK_SIZE, LastX + 1);
|
||||
const int32 EndY = FMath::Min(CellMinY + CHUNK_SIZE, LastY + 1);
|
||||
const int32 EndZ = FMath::Min(CellMinZ + CHUNK_SIZE, LastZ + 1);
|
||||
Region.SizeX = EndX - Region.Origin.X;
|
||||
Region.SizeY = EndY - Region.Origin.Y;
|
||||
Region.SizeZ = EndZ - Region.Origin.Z;
|
||||
Region.LatticeX = Region.Origin.X - OriginVoxels.X;
|
||||
Region.LatticeY = Region.Origin.Y - OriginVoxels.Y;
|
||||
Region.LatticeZ = Region.Origin.Z - OriginVoxels.Z;
|
||||
}
|
||||
|
||||
if (bFullRing)
|
||||
{
|
||||
WorkBricks.Reserve(Bricks.Num());
|
||||
for (int32 BrickIndex = 0; BrickIndex < Bricks.Num(); ++BrickIndex)
|
||||
WorkRegions.Reserve(Regions.Num());
|
||||
for (int32 RegionIndex = 0; RegionIndex < Regions.Num(); ++RegionIndex)
|
||||
{
|
||||
WorkBricks.Add(BrickIndex);
|
||||
WorkRegions.Add(RegionIndex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WorkBricks.Reserve(Bricks.Num());
|
||||
WorkRegions.Reserve(Regions.Num());
|
||||
for (int32 OutputIndex = 0; OutputIndex < SampleIndices->Num(); ++OutputIndex)
|
||||
{
|
||||
const int64 LatticeIndex = (*SampleIndices)[OutputIndex];
|
||||
@@ -233,25 +246,25 @@ namespace VoxelForgeRegionEval
|
||||
const int32 WorldX = OriginVoxels.X + X;
|
||||
const int32 WorldY = OriginVoxels.Y + Y;
|
||||
const int32 WorldZ = OriginVoxels.Z + Z;
|
||||
const int32 CellX = FMath::FloorToInt(static_cast<float>(WorldX) / 8.0f);
|
||||
const int32 CellY = FMath::FloorToInt(static_cast<float>(WorldY) / 8.0f);
|
||||
const int32 CellZ = FMath::FloorToInt(static_cast<float>(WorldZ) / 8.0f);
|
||||
const int32 BrickIndex = ((CellZ - FirstCellZ) * CellsY
|
||||
const int32 CellX = FMath::FloorToInt(static_cast<float>(WorldX) / CHUNK_SIZE);
|
||||
const int32 CellY = FMath::FloorToInt(static_cast<float>(WorldY) / CHUNK_SIZE);
|
||||
const int32 CellZ = FMath::FloorToInt(static_cast<float>(WorldZ) / CHUNK_SIZE);
|
||||
const int32 RegionIndex = ((CellZ - FirstCellZ) * CellsY
|
||||
+ (CellY - FirstCellY)) * CellsX + (CellX - FirstCellX);
|
||||
FRegionBrick& Brick = Bricks[BrickIndex];
|
||||
const int32 LocalX = WorldX - Brick.Origin.X;
|
||||
const int32 LocalY = WorldY - Brick.Origin.Y;
|
||||
const int32 LocalZ = WorldZ - Brick.Origin.Z;
|
||||
FRegionBrickSample& BrickSample = Brick.RequestedSamples.AddDefaulted_GetRef();
|
||||
BrickSample.LocalIndex =
|
||||
(LocalZ * Brick.SizeY + LocalY) * Brick.SizeX + LocalX;
|
||||
BrickSample.OutputIndex = OutputIndex;
|
||||
FRegionChunk& Region = Regions[RegionIndex];
|
||||
const int32 LocalX = WorldX - Region.Origin.X;
|
||||
const int32 LocalY = WorldY - Region.Origin.Y;
|
||||
const int32 LocalZ = WorldZ - Region.Origin.Z;
|
||||
FRegionChunkSample& ChunkSample = Region.RequestedSamples.AddDefaulted_GetRef();
|
||||
ChunkSample.LocalIndex =
|
||||
(LocalZ * Region.SizeY + LocalY) * Region.SizeX + LocalX;
|
||||
ChunkSample.OutputIndex = OutputIndex;
|
||||
}
|
||||
for (int32 BrickIndex = 0; BrickIndex < Bricks.Num(); ++BrickIndex)
|
||||
for (int32 RegionIndex = 0; RegionIndex < Regions.Num(); ++RegionIndex)
|
||||
{
|
||||
if (!Bricks[BrickIndex].RequestedSamples.IsEmpty())
|
||||
if (!Regions[RegionIndex].RequestedSamples.IsEmpty())
|
||||
{
|
||||
WorkBricks.Add(BrickIndex);
|
||||
WorkRegions.Add(RegionIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -275,6 +288,8 @@ namespace VoxelForgeRegionEval
|
||||
TArray<uint64> WorkerNonFinite;
|
||||
TArray<uint64> WorkerEvaluatedSamples;
|
||||
TArray<uint64> WorkerFallbackSamples;
|
||||
TArray<uint64> WorkerGeneralPathEntries;
|
||||
TArray<uint64> WorkerSupportedBlockSetupEntries;
|
||||
TArray<uint64> WorkerEvaluationFailures;
|
||||
WorkerSeconds.SetNumZeroed(NumWorkers);
|
||||
WorkerCpuSeconds.SetNumZeroed(NumWorkers);
|
||||
@@ -283,6 +298,8 @@ namespace VoxelForgeRegionEval
|
||||
WorkerNonFinite.SetNumZeroed(NumWorkers);
|
||||
WorkerEvaluatedSamples.SetNumZeroed(NumWorkers);
|
||||
WorkerFallbackSamples.SetNumZeroed(NumWorkers);
|
||||
WorkerGeneralPathEntries.SetNumZeroed(NumWorkers);
|
||||
WorkerSupportedBlockSetupEntries.SetNumZeroed(NumWorkers);
|
||||
WorkerEvaluationFailures.SetNumZeroed(NumWorkers);
|
||||
bool bAllWorkerCpuFromOsThreadTimes = true;
|
||||
ParallelFor(NumWorkers, [&](int32 WorkerIndex)
|
||||
@@ -291,51 +308,78 @@ namespace VoxelForgeRegionEval
|
||||
// inherit its task-level sampler registration. Keep this registration scoped to the
|
||||
// research worker partition; with sampling disabled it is one relaxed flag load.
|
||||
FScopedVoxelStackRegistration StackRegistration;
|
||||
FIntVector PreviousTileOrigin = FIntVector::ZeroValue;
|
||||
int32 PreviousTileStep = 1;
|
||||
int32 PreviousTileCells = 0;
|
||||
VoxelGenLOD::GetThreadTileCacheWindow(
|
||||
PreviousTileOrigin, PreviousTileStep, PreviousTileCells);
|
||||
const int32 PreviousOctaveBias = VoxelGenLOD::GetThreadOctaveBias();
|
||||
VoxelCaveMorphology::SetResearchBrickBinningEnabled(bPrepared);
|
||||
VoxelCaveMorphology::SetResearchBrickRoomReachScale(ReachScale);
|
||||
VoxelGenLOD::SetThreadSampleStep(1);
|
||||
VoxelGenLOD::SetThreadOctaveBias(0);
|
||||
VoxelGenLOD::SetThreadTileContext(OriginVoxels, Dimension);
|
||||
VoxelGenLOD::SetResearchRegionCacheWindowEnabled(bRegionProgram && bPrepared);
|
||||
WorkerThreadIds[WorkerIndex] = FPlatformTLS::GetCurrentThreadId();
|
||||
const int64 WorkCount = bBrickProgram ? WorkBricks.Num() : SampleCount;
|
||||
const int64 WorkCount = bRegionProgram ? WorkRegions.Num() : SampleCount;
|
||||
const int64 Begin = (WorkCount * WorkerIndex) / NumWorkers;
|
||||
const int64 End = (WorkCount * (WorkerIndex + 1)) / NumWorkers;
|
||||
const double WorkerStart = FPlatformTime::Seconds();
|
||||
double WorkerCpuStart = 0.0;
|
||||
const bool bHaveWorkerCpuStart = GetCurrentThreadCpuSeconds(WorkerCpuStart);
|
||||
TArray<float> BrickDensities;
|
||||
TArray<float> RegionDensities;
|
||||
for (int64 OutputIndex = Begin; OutputIndex < End; ++OutputIndex)
|
||||
{
|
||||
if (bBrickProgram)
|
||||
if (bRegionProgram)
|
||||
{
|
||||
const FRegionBrick& Brick = Bricks[WorkBricks[static_cast<int32>(OutputIndex)]];
|
||||
int32 BrickFallbackSamples = 0;
|
||||
if (!Generator.EvaluateDensityBrick(
|
||||
Brick.Origin, Brick.SizeX, Brick.SizeY, Brick.SizeZ,
|
||||
BrickDensities, BrickFallbackSamples)
|
||||
|| BrickDensities.Num() != Brick.SizeX * Brick.SizeY * Brick.SizeZ)
|
||||
const FRegionChunk& Region = Regions[WorkRegions[static_cast<int32>(OutputIndex)]];
|
||||
if (bPrepared)
|
||||
{
|
||||
// Build/cache one bounded graph window for the current CHUNK_SIZE region.
|
||||
// A ring-wide context made each worker retain a duplicate ring-sized graph
|
||||
// even though its density block only consumes this one chunk.
|
||||
const FIntVector CacheWindowOrigin(
|
||||
FMath::FloorToInt(static_cast<float>(Region.Origin.X) / CHUNK_SIZE) * CHUNK_SIZE,
|
||||
FMath::FloorToInt(static_cast<float>(Region.Origin.Y) / CHUNK_SIZE) * CHUNK_SIZE,
|
||||
FMath::FloorToInt(static_cast<float>(Region.Origin.Z) / CHUNK_SIZE) * CHUNK_SIZE);
|
||||
VoxelGenLOD::SetThreadTileContext(
|
||||
CacheWindowOrigin, CHUNK_SIZE);
|
||||
}
|
||||
int32 RegionFallbackSamples = 0;
|
||||
uint64 RegionGeneralPathEntries = 0;
|
||||
uint64 RegionSupportedSetupEntries = 0;
|
||||
if (!Generator.EvaluateDensityChunk(
|
||||
Region.Origin, Region.SizeX, Region.SizeY, Region.SizeZ,
|
||||
RegionDensities, RegionFallbackSamples,
|
||||
RegionGeneralPathEntries, RegionSupportedSetupEntries)
|
||||
|| RegionDensities.Num() != Region.SizeX * Region.SizeY * Region.SizeZ)
|
||||
{
|
||||
++WorkerEvaluationFailures[WorkerIndex];
|
||||
continue;
|
||||
}
|
||||
const int32 BrickSampleCount = BrickDensities.Num();
|
||||
WorkerEvaluatedSamples[WorkerIndex] += static_cast<uint64>(BrickSampleCount);
|
||||
WorkerFallbackSamples[WorkerIndex] += static_cast<uint64>(BrickFallbackSamples);
|
||||
const int32 RegionSampleCount = RegionDensities.Num();
|
||||
WorkerEvaluatedSamples[WorkerIndex] += static_cast<uint64>(RegionSampleCount);
|
||||
WorkerFallbackSamples[WorkerIndex] += static_cast<uint64>(RegionFallbackSamples);
|
||||
WorkerGeneralPathEntries[WorkerIndex] += RegionGeneralPathEntries;
|
||||
WorkerSupportedBlockSetupEntries[WorkerIndex] += RegionSupportedSetupEntries;
|
||||
if (bFullRing && OutDensities != nullptr)
|
||||
{
|
||||
for (int32 Bz = 0; Bz < Brick.SizeZ; ++Bz)
|
||||
for (int32 By = 0; By < Brick.SizeY; ++By)
|
||||
for (int32 Bx = 0; Bx < Brick.SizeX; ++Bx)
|
||||
for (int32 Bz = 0; Bz < Region.SizeZ; ++Bz)
|
||||
for (int32 By = 0; By < Region.SizeY; ++By)
|
||||
for (int32 Bx = 0; Bx < Region.SizeX; ++Bx)
|
||||
{
|
||||
const int32 LocalIndex = (Bz * Brick.SizeY + By) * Brick.SizeX + Bx;
|
||||
const int32 LocalIndex = (Bz * Region.SizeY + By) * Region.SizeX + Bx;
|
||||
const int64 LatticeIndex = LinearIndex(
|
||||
Brick.LatticeX + Bx, Brick.LatticeY + By,
|
||||
Brick.LatticeZ + Bz, Dimension);
|
||||
(*OutDensities)[static_cast<int32>(LatticeIndex)] = BrickDensities[LocalIndex];
|
||||
Region.LatticeX + Bx, Region.LatticeY + By,
|
||||
Region.LatticeZ + Bz, Dimension);
|
||||
(*OutDensities)[static_cast<int32>(LatticeIndex)] = RegionDensities[LocalIndex];
|
||||
}
|
||||
}
|
||||
else if (OutDensities != nullptr)
|
||||
{
|
||||
for (const FRegionBrickSample& Sample : Brick.RequestedSamples)
|
||||
for (const FRegionChunkSample& Sample : Region.RequestedSamples)
|
||||
{
|
||||
(*OutDensities)[Sample.OutputIndex] = BrickDensities[Sample.LocalIndex];
|
||||
(*OutDensities)[Sample.OutputIndex] = RegionDensities[Sample.LocalIndex];
|
||||
}
|
||||
}
|
||||
continue;
|
||||
@@ -355,6 +399,8 @@ namespace VoxelForgeRegionEval
|
||||
static_cast<float>(OriginX + X),
|
||||
static_cast<float>(OriginY + Y),
|
||||
static_cast<float>(OriginZ + Z));
|
||||
++WorkerEvaluatedSamples[WorkerIndex];
|
||||
++WorkerGeneralPathEntries[WorkerIndex];
|
||||
if (OutDensities != nullptr)
|
||||
{
|
||||
(*OutDensities)[static_cast<int32>(OutputIndex)] = Density;
|
||||
@@ -373,20 +419,34 @@ namespace VoxelForgeRegionEval
|
||||
}
|
||||
VoxelCaveMorphology::SetResearchBrickBinningEnabled(false);
|
||||
VoxelCaveMorphology::SetResearchBrickRoomReachScale(1.0f);
|
||||
VoxelGenLOD::SetResearchRegionCacheWindowEnabled(false);
|
||||
VoxelGenLOD::SetThreadTileContext(PreviousTileOrigin, PreviousTileCells);
|
||||
VoxelGenLOD::SetThreadSampleStep(PreviousTileStep);
|
||||
VoxelGenLOD::SetThreadOctaveBias(PreviousOctaveBias);
|
||||
}, EParallelForFlags::None);
|
||||
|
||||
VoxelDensityProfile::FSnapshot ProfileSnapshot;
|
||||
if (bCollectWholeRunCounters)
|
||||
{
|
||||
ProfileSnapshot = VoxelDensityProfile::Snapshot();
|
||||
VoxelDensityProfile::SetMode(PreviousProfileMode, PreviousProfileInterval);
|
||||
VoxelDensityProfile::Reset();
|
||||
}
|
||||
|
||||
OutTiming.WallSeconds = FPlatformTime::Seconds() - StartSeconds;
|
||||
OutTiming.SampleCount = SampleCount;
|
||||
OutTiming.bBrickProgram = bBrickProgram;
|
||||
OutTiming.BrickCount = bBrickProgram ? WorkBricks.Num() : 0;
|
||||
OutTiming.bRegionProgram = bRegionProgram;
|
||||
OutTiming.bHasWholeRunCounters = bCollectWholeRunCounters;
|
||||
OutTiming.RegionCount = bRegionProgram ? WorkRegions.Num() : 0;
|
||||
for (int32 WorkerIndex = 0; WorkerIndex < NumWorkers; ++WorkerIndex)
|
||||
{
|
||||
OutTiming.WorkerCpuSeconds += WorkerCpuSeconds[WorkerIndex];
|
||||
bAllWorkerCpuFromOsThreadTimes &= WorkerCpuFromOsThreadTimes[WorkerIndex] != 0;
|
||||
OutTiming.NonFiniteSamples += WorkerNonFinite[WorkerIndex];
|
||||
OutTiming.EvaluatedSamples += bBrickProgram
|
||||
? static_cast<int64>(WorkerEvaluatedSamples[WorkerIndex]) : 0;
|
||||
OutTiming.EvaluatedSamples += static_cast<int64>(WorkerEvaluatedSamples[WorkerIndex]);
|
||||
OutTiming.FallbackSamples += static_cast<int64>(WorkerFallbackSamples[WorkerIndex]);
|
||||
OutTiming.GeneralPathEntries += WorkerGeneralPathEntries[WorkerIndex];
|
||||
OutTiming.SupportedBlockSetupEntries += WorkerSupportedBlockSetupEntries[WorkerIndex];
|
||||
if (WorkerEvaluationFailures[WorkerIndex] != 0)
|
||||
{
|
||||
return false;
|
||||
@@ -403,10 +463,75 @@ namespace VoxelForgeRegionEval
|
||||
{
|
||||
OutTiming.FallbackShare = static_cast<double>(OutTiming.FallbackSamples)
|
||||
/ static_cast<double>(OutTiming.EvaluatedSamples);
|
||||
OutTiming.GeneralPathEntriesPerSample = static_cast<double>(OutTiming.GeneralPathEntries)
|
||||
/ static_cast<double>(OutTiming.EvaluatedSamples);
|
||||
}
|
||||
OutTiming.NanosecondsPerSample = OutTiming.SampleCount > 0
|
||||
? OutTiming.WorkerCpuSeconds * 1.0e9 / static_cast<double>(OutTiming.SampleCount)
|
||||
: 0.0;
|
||||
|
||||
if (bCollectWholeRunCounters)
|
||||
{
|
||||
using VoxelDensityProfile::ECounter;
|
||||
using VoxelDensityProfile::EBucket;
|
||||
const auto Counter = [&ProfileSnapshot](ECounter Id)
|
||||
{
|
||||
return ProfileSnapshot.Counters[static_cast<int32>(Id)];
|
||||
};
|
||||
OutTiming.SdfCacheBuilds = Counter(ECounter::SdfCacheBuild);
|
||||
OutTiming.ActiveOpListBuilds = Counter(ECounter::OpBlockBuilds);
|
||||
OutTiming.SdfSpatialIndexBuilds = Counter(ECounter::SdfSpatialIndexBuild);
|
||||
OutTiming.SdfBrickIndexBuilds = Counter(ECounter::SdfBrickIndexBuild);
|
||||
OutTiming.BroadPhaseIndexBuilds = OutTiming.SdfSpatialIndexBuilds
|
||||
+ OutTiming.SdfBrickIndexBuilds;
|
||||
OutTiming.RoomCandidateVisits = Counter(ECounter::CaveRoomCandidates);
|
||||
OutTiming.RoomEvaluations = Counter(ECounter::CaveRoomEvaluated);
|
||||
OutTiming.TunnelCandidateVisits = Counter(ECounter::CaveTunnelCandidates);
|
||||
OutTiming.TunnelEvaluations = Counter(ECounter::CaveTunnelEvaluated);
|
||||
OutTiming.TunnelCoreCandidateVisits = Counter(ECounter::TunnelCoreCandidates);
|
||||
OutTiming.TunnelCoreEvaluations = Counter(ECounter::TunnelCoreEvaluated);
|
||||
OutTiming.PassageCandidateVisits = Counter(ECounter::PassageCandidates);
|
||||
OutTiming.PassageEvaluations = Counter(ECounter::PassageEvaluated);
|
||||
OutTiming.TotalCandidateVisits = OutTiming.RoomCandidateVisits
|
||||
+ OutTiming.TunnelCandidateVisits + OutTiming.TunnelCoreCandidateVisits
|
||||
+ OutTiming.PassageCandidateVisits;
|
||||
OutTiming.TotalCandidateEvaluations = OutTiming.RoomEvaluations
|
||||
+ OutTiming.TunnelEvaluations + OutTiming.TunnelCoreEvaluations
|
||||
+ OutTiming.PassageEvaluations;
|
||||
if (OutTiming.SampleCount > 0)
|
||||
{
|
||||
OutTiming.CandidateVisitsPerSample = static_cast<double>(OutTiming.TotalCandidateVisits)
|
||||
/ static_cast<double>(OutTiming.SampleCount);
|
||||
OutTiming.CandidateEvaluationsPerSample = static_cast<double>(OutTiming.TotalCandidateEvaluations)
|
||||
/ static_cast<double>(OutTiming.SampleCount);
|
||||
}
|
||||
const auto AttributedNsPerSample = [&ProfileSnapshot, SampleCount](EBucket Bucket)
|
||||
{
|
||||
return SampleCount > 0
|
||||
? FPlatformTime::ToSeconds64(ProfileSnapshot.Cycles[static_cast<int32>(Bucket)])
|
||||
* 1.0e9 / static_cast<double>(SampleCount)
|
||||
: 0.0;
|
||||
};
|
||||
OutTiming.GetDensityAtNsPerSample = AttributedNsPerSample(EBucket::GetDensityAt);
|
||||
OutTiming.OperatorBlockNsPerSample = AttributedNsPerSample(EBucket::OperatorBlock);
|
||||
OutTiming.RoomGraphSourceNsPerSample = AttributedNsPerSample(EBucket::RoomGraphSource);
|
||||
OutTiming.RoomGraphBuildNsPerSample = AttributedNsPerSample(EBucket::RoomGraphBuild);
|
||||
OutTiming.RoomGraphSdfNsPerSample = AttributedNsPerSample(EBucket::RoomGraphSdf);
|
||||
OutTiming.FusedEvaluatorNsPerSample = AttributedNsPerSample(EBucket::FusedEvaluator);
|
||||
OutTiming.TunnelCoreSdfNsPerSample = AttributedNsPerSample(EBucket::TunnelCoreSdf);
|
||||
OutTiming.TunnelCoreWorldNsPerSample = AttributedNsPerSample(EBucket::TunnelCoreWorld);
|
||||
OutTiming.TunnelCorePostsNsPerSample = AttributedNsPerSample(EBucket::TunnelCorePosts);
|
||||
OutTiming.PassageModifierNsPerSample = AttributedNsPerSample(EBucket::PassageModifier);
|
||||
OutTiming.PassageStructuralPostsNsPerSample = AttributedNsPerSample(EBucket::PassageStructuralPosts);
|
||||
OutTiming.PassageFloorNsPerSample = AttributedNsPerSample(EBucket::PassageLandingFloor);
|
||||
OutTiming.StructuralPostsNsPerSample = AttributedNsPerSample(EBucket::DensityStructuralPosts);
|
||||
OutTiming.DensityPrologueNsPerSample = AttributedNsPerSample(EBucket::DensityPrologue);
|
||||
OutTiming.DensityCoreNsPerSample = AttributedNsPerSample(EBucket::DensityCore);
|
||||
OutTiming.DisturbancesNsPerSample = AttributedNsPerSample(EBucket::DensityDisturbances);
|
||||
OutTiming.DensityBoundarySealNsPerSample = AttributedNsPerSample(EBucket::DensityBoundarySeal);
|
||||
OutTiming.DiffLayerNsPerSample = AttributedNsPerSample(EBucket::DensityDiffLayer);
|
||||
OutTiming.DensityTailNsPerSample = AttributedNsPerSample(EBucket::DensityTail);
|
||||
}
|
||||
if (OutDensities != nullptr)
|
||||
{
|
||||
for (const float Density : *OutDensities)
|
||||
@@ -452,16 +577,24 @@ namespace VoxelForgeRegionEval
|
||||
}
|
||||
using VoxelDensityProfile::ECounter;
|
||||
Result.Room = Snapshot.Counters[static_cast<int32>(ECounter::CaveRoomCandidates)];
|
||||
Result.RoomEvaluated = Snapshot.Counters[static_cast<int32>(ECounter::CaveRoomEvaluated)];
|
||||
Result.Tunnel = Snapshot.Counters[static_cast<int32>(ECounter::CaveTunnelCandidates)];
|
||||
Result.TunnelEvaluated = Snapshot.Counters[static_cast<int32>(ECounter::CaveTunnelEvaluated)];
|
||||
Result.TunnelCore = Snapshot.Counters[static_cast<int32>(ECounter::TunnelCoreCandidates)];
|
||||
Result.TunnelCoreEvaluated = Snapshot.Counters[static_cast<int32>(ECounter::TunnelCoreEvaluated)];
|
||||
Result.Passage = Snapshot.Counters[static_cast<int32>(ECounter::PassageCandidates)];
|
||||
Result.PassageEvaluated = Snapshot.Counters[static_cast<int32>(ECounter::PassageEvaluated)];
|
||||
Result.TunnelCacheLookups = Snapshot.Counters[static_cast<int32>(ECounter::TunnelCacheLookup)];
|
||||
Result.TunnelCacheHits = Snapshot.Counters[static_cast<int32>(ECounter::TunnelCacheHit)];
|
||||
Result.TunnelCacheMisses = Snapshot.Counters[static_cast<int32>(ECounter::TunnelCacheMiss)];
|
||||
Result.SdfCacheBuilds = Snapshot.Counters[static_cast<int32>(ECounter::SdfCacheBuild)];
|
||||
Result.Total = Result.Room + Result.Tunnel + Result.TunnelCore + Result.Passage;
|
||||
Result.TotalEvaluated = Result.RoomEvaluated + Result.TunnelEvaluated
|
||||
+ Result.TunnelCoreEvaluated + Result.PassageEvaluated;
|
||||
Result.PerSample = Result.SampleCount > 0
|
||||
? static_cast<double>(Result.Total) / Result.SampleCount : 0.0;
|
||||
Result.EvaluatedPerSample = Result.SampleCount > 0
|
||||
? static_cast<double>(Result.TotalEvaluated) / Result.SampleCount : 0.0;
|
||||
Result.AttributionSampleInterval = Snapshot.SampleInterval;
|
||||
const auto AttributedNanosecondsPerSample = [&](VoxelDensityProfile::EBucket Bucket)
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user