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:
Fr0zkaandClaude Opus 5.5 committed 2026-09-30 04:18:44 +02:00
1 parent fd9f809c1c
commit 43c11ccaa8
9 files changed
+484 -143

No files matched your search

@@ -1416,6 +1416,11 @@ namespace
static void VF_BuildSpatialIndex(
FChunkSDFSpatialIndex& OutIndex, int32 ItemCount, FGetBounds&& GetBounds)
{
if (VoxelDensityProfile::AreCountersEnabled())
{
VoxelDensityProfile::AddCounter(
VoxelDensityProfile::ECounter::SdfSpatialIndexBuild);
}
OutIndex.Reset();
if (ItemCount <= 0)
{
@@ -1811,6 +1816,11 @@ namespace
static void VF_BuildBrickIndex(
FChunkSDFBrickIndex& OutIndex, int32 ItemCount, FGetBounds&& GetBounds)
{
if (VoxelDensityProfile::AreCountersEnabled())
{
VoxelDensityProfile::AddCounter(
VoxelDensityProfile::ECounter::SdfBrickIndexBuild);
}
OutIndex.Reset();
if (ItemCount <= 0)
{
@@ -640,6 +640,8 @@ namespace VoxelDensityProfile
case ECounter::TunnelCacheHit: return TEXT("TunnelCacheHit");
case ECounter::TunnelCacheMiss: return TEXT("TunnelCacheMiss");
case ECounter::SdfCacheBuild: return TEXT("SdfCacheBuild");
case ECounter::SdfSpatialIndexBuild: return TEXT("SdfSpatialIndexBuild");
case ECounter::SdfBrickIndexBuild: return TEXT("SdfBrickIndexBuild");
case ECounter::RoomGraphBuildGeneratorTile: return TEXT("RoomGraphBuildGeneratorTile");
case ECounter::RoomGraphBuildGeneratorTunnelCore: return TEXT("RoomGraphBuildGeneratorTunnelCore");
case ECounter::RoomGraphBuildOpShared: return TEXT("RoomGraphBuildOpShared");
+28 -8
View File
@@ -38,6 +38,9 @@ namespace VoxelMath
namespace
{
#if WITH_EDITOR
thread_local bool GVoxelResearchRegionCacheWindowEnabled = false;
#endif
// The default-on branch uses VoxelMath::IsFiniteFast, while 0 retains the original
// FMath::IsFinite call for a real within-round A/B and sampler comparison.
FAutoConsoleVariableRef CVarVoxelForgeFastIsFinite(
@@ -884,6 +887,12 @@ bool VoxelGenLOD::IsTileCacheWindowEnabled(bool bFusedPath)
{
return false;
}
#if WITH_EDITOR
if (GVoxelResearchRegionCacheWindowEnabled)
{
return true;
}
#endif
const int32 RequiredMinLOD = bFusedPath
? GVoxelForgeTileCacheWindowMinLOD
: GVoxelForgeTileCacheWindowOpStackMinLOD;
@@ -891,6 +900,13 @@ bool VoxelGenLOD::IsTileCacheWindowEnabled(bool bFusedPath)
return FMath::Max(SampleStep, 1) >= RequiredStep;
}
#if WITH_EDITOR
void VoxelGenLOD::SetResearchRegionCacheWindowEnabled(bool bEnabled)
{
GVoxelResearchRegionCacheWindowEnabled = bEnabled;
}
#endif
bool VoxelGenLOD::GetThreadTileCacheWindow(
FIntVector& OutOriginVoxels, int32& OutStep, int32& OutCellsPerAxis)
{
@@ -2722,15 +2738,19 @@ void UVoxelGenerator::EndDensityBlock() const
}
#if WITH_EDITOR
bool UVoxelGenerator::EvaluateDensityBrick(
bool UVoxelGenerator::EvaluateDensityChunk(
FIntVector OriginVoxels, int32 SizeX, int32 SizeY, int32 SizeZ,
TArray<float>& OutDensities, int32& OutFallbackSamples) const
TArray<float>& OutDensities, int32& OutFallbackSamples,
uint64& OutGeneralPathEntries, uint64& OutSupportedBlockSetupEntries) const
{
OutDensities.Reset();
OutFallbackSamples = 0;
OutGeneralPathEntries = 0;
OutSupportedBlockSetupEntries = 0;
const int64 SampleCount64 = static_cast<int64>(SizeX) * SizeY * SizeZ;
if (SizeX <= 0 || SizeY <= 0 || SizeZ <= 0
|| SizeX > 8 || SizeY > 8 || SizeZ > 8 || SampleCount64 > 512)
|| SizeX > CHUNK_SIZE || SizeY > CHUNK_SIZE || SizeZ > CHUNK_SIZE
|| SampleCount64 > static_cast<int64>(CHUNK_SIZE) * CHUNK_SIZE * CHUNK_SIZE)
{
return false;
}
@@ -2741,10 +2761,7 @@ bool UVoxelGenerator::EvaluateDensityBrick(
return FMath::FloorToInt(static_cast<float>(A) / static_cast<float>(CellSize))
== FMath::FloorToInt(static_cast<float>(B) / static_cast<float>(CellSize));
};
if (!SameCell(OriginVoxels.X, Last.X, 8)
|| !SameCell(OriginVoxels.Y, Last.Y, 8)
|| !SameCell(OriginVoxels.Z, Last.Z, 8)
|| !SameCell(OriginVoxels.X, Last.X, CHUNK_SIZE)
if (!SameCell(OriginVoxels.X, Last.X, CHUNK_SIZE)
|| !SameCell(OriginVoxels.Y, Last.Y, CHUNK_SIZE)
|| !SameCell(OriginVoxels.Z, Last.Z, CHUNK_SIZE))
{
@@ -2761,17 +2778,19 @@ bool UVoxelGenerator::EvaluateDensityBrick(
static_cast<float>(OriginVoxels.X),
static_cast<float>(OriginVoxels.Y),
static_cast<float>(OriginVoxels.Z));
OutGeneralPathEntries = 1;
if (GVoxelDensityBlockSession.bBatchSucceeded)
{
OutDensities.SetNumUninitialized(SampleCount);
FMemory::Memcpy(
OutDensities.GetData(), GVoxelDensityBlockSession.BatchResults.GetData(),
static_cast<SIZE_T>(SampleCount) * sizeof(float));
OutSupportedBlockSetupEntries = 1;
EndDensityBlock();
return OutDensities.Num() == SampleCount;
}
// Recipes without the editor brick port retain the canonical point evaluator. The first
// Routes without the editor block program retain the canonical point evaluator. The first
// call already produced point zero; the remaining samples are explicit counted fallbacks.
GVoxelDensityBlockSession.bBatchRequested = false;
OutDensities.SetNumUninitialized(SampleCount);
@@ -2787,6 +2806,7 @@ bool UVoxelGenerator::EvaluateDensityBrick(
static_cast<float>(OriginVoxels.Z + Z));
}
OutFallbackSamples = SampleCount;
OutGeneralPathEntries = static_cast<uint64>(SampleCount);
EndDensityBlock();
return true;
}
@@ -106,6 +106,8 @@ namespace VoxelDensityProfile
TunnelCacheHit,
TunnelCacheMiss,
SdfCacheBuild,
SdfSpatialIndexBuild,
SdfBrickIndexBuild,
RoomGraphBuildGeneratorTile,
RoomGraphBuildGeneratorTunnelCore,
RoomGraphBuildOpShared,
+9 -4
View File
@@ -77,6 +77,10 @@ namespace VoxelGenLOD
// Called by the module's game-thread startup before any worker can enter GetDensityAt.
VOXELFORGE_API void InitializeConsoleSwitches();
VOXELFORGE_API bool IsTileCacheWindowEnabled(bool bFusedPath = true);
#if WITH_EDITOR
/** Research-only override for the bounded editor region evaluator; default false per thread. */
VOXELFORGE_API void SetResearchRegionCacheWindowEnabled(bool bEnabled);
#endif
VOXELFORGE_API bool GetThreadTileCacheWindow(
FIntVector& OutOriginVoxels, int32& OutStep, int32& OutCellsPerAxis);
VOXELFORGE_API void SetThreadTileContext(
@@ -181,10 +185,11 @@ public:
void EndDensityBlock() const;
#if WITH_EDITOR
/** Editor-only region-program entry point; unsupported routes report live point fallbacks. */
bool EvaluateDensityBrick(FIntVector OriginVoxels, int32 SizeX, int32 SizeY, int32 SizeZ,
TArray<float>& OutDensities,
int32& OutFallbackSamples) const;
/** Editor-only chunk program; supported routes enter GetDensityAt once to seed a whole block. */
bool EvaluateDensityChunk(FIntVector OriginVoxels, int32 SizeX, int32 SizeY, int32 SizeZ,
TArray<float>& OutDensities, int32& OutFallbackSamples,
uint64& OutGeneralPathEntries,
uint64& OutSupportedBlockSetupEntries) const;
#endif
/**