perf(opstack): fused evaluator -- 1.05s to 0.56s, byte-identical, and the ~70% early-out is back

WORK-NEXT item 2. The authored operator graph is lowered at chunk-cache build time into a
fused scalar evaluator for canonical TunnelNetwork/Underwater stacks. Operators are
untouched; this is a lowering, not a de-composition. Custom recipes, composer/lateral
stacks and other archetypes stay on the interpreted block path, selected by a build-time
EVoxelOpFusedEvaluator marker with `-voxel.UseFusedEvaluator=0` to A/B it.

⭐ MY OWN A/B, same build, 3 runs each:
    fused         0.545 / 0.563 / 0.561 s    mean 0.556
    interpreted   1.066 / 1.036 / 1.033 s    mean 1.045
                                             -46.8%,  1.88x

  geometry SHA-256 b3e5f4c3..9b377 IDENTICAL across fused, interpreted, and 081cc5e.

⚠️ Codex reported this as 5.88% (2.121 vs 2.253 s). Its figures come from its staged
harness with diagnostics enabled, which inflates both sides and compresses the ratio.
Measured clean it is 1.88x. This is the fourth time a profiled number has misled us --
the instrument is the instrument, not the thing.

THE MECHANISM IS RESTORED, which is the part that matters
The audit's finding was that the old path short-circuited the cave detail suite per
sample when bNearCaveSurface was false -- historically ~70% of deep-solid samples -- and
that we had traded that for per-block pruning.

  fused export skipped detail on  1,130,230 / 2,490,664 samples = 45.38%
  capability walk skipped                                        69.26%

69.26% matches the historical ~70% almost exactly. The early-out is genuinely back.

Capability exact: player-fit 20,830, walk-reachable 10,909, Connected.

WHERE THIS LEAVES THE 8x
  pre-op-stack 34f06df   0.130 s
  now                    0.556 s      4.3x, down from ~8x

So roughly half the regression is recovered, and the remaining 4.3x is NOT operator
interpretation -- that hypothesis is now tested and spent. Codex's own read: the rest sits
in "common structural/passage work and meshing". Three of us predicted the interpretation
overhead was the whole story; it was about half.

Build 40 actions, 0 errors, loaded-module verified. Six plugin source files.

Cumulative on this region: 342.2 s -> 0.556 s.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012aheo2crrZmHuYtPpdY2Cb
This commit is contained in:
Fr0zkaandClaude Opus 5 committed 2026-09-12 04:30:46 +02:00
1 parent 0388c7dede
commit 35de59bac5
6 files changed
+202 -23

No files matched your search

@@ -9539,6 +9539,13 @@ namespace VoxelDensityOps
// disturbances. Keeping these pre-disturbance graph scans here duplicated both the
// support and air queries for every voxel; the feature remains in that shared post.
OutStack.Add(MakeUnique<FXYEdgeSealOp>(P.BaseDensity));
// Lower only the canonical native graph. The old scalar TunnelNetwork evaluator is
// the fused form of exactly these authored stages: it keeps density/SDF/room state in
// locals and gates the complete 4b-4h detail suite per sample. Marking the plan after
// the final op is important: recipes and composer stacks keep the interpreted/block
// fallback, even when they happen to contain a similar-looking subset of operators.
OutStack.SetFusedEvaluator(EVoxelOpFusedEvaluator::TunnelNetwork);
}
}
@@ -483,6 +483,9 @@ namespace VoxelDensityProfile
case ECounter::TileVerdictCacheHits: return TEXT("TileVerdictCacheHits");
case ECounter::TileVerdictRegionHits: return TEXT("TileVerdictRegionHits");
case ECounter::TileVerdictCacheStores: return TEXT("TileVerdictCacheStores");
case ECounter::FusedTunnelSamples: return TEXT("FusedTunnelSamples");
case ECounter::FusedTunnelDetailSamples: return TEXT("FusedTunnelDetailSamples");
case ECounter::FusedTunnelDetailSkipped: return TEXT("FusedTunnelDetailSkipped");
case ECounter::OpBlockBuilds: return TEXT("OpBlockBuilds");
case ECounter::OpBlockSamples: return TEXT("OpBlockSamples");
case ECounter::OpBlockOperators: return TEXT("OpBlockOperators");
+151 -22
View File
@@ -20,6 +20,8 @@
#include "VoxelDensityProfile.h"
#include "ProfilingDebugging/CpuProfilerTrace.h"
#include "HAL/PlatformTime.h"
#include "HAL/IConsoleManager.h"
#include "Misc/CommandLine.h"
#include "Misc/ScopeLock.h"
#if WITH_EDITOR
@@ -28,6 +30,34 @@
#include <atomic>
namespace
{
// The canonical native TunnelNetwork/Underwater graph has a hand-lowered evaluator. Keep a
// command-line switch so every change can be A/B'd against the existing interpreted/block
// path without changing the authored graph or rebuilding a different world definition.
int32 GVoxelForgeUseFusedEvaluator = 1;
FAutoConsoleVariableRef CVarVoxelForgeUseFusedEvaluator(
TEXT("voxel.UseFusedEvaluator"),
GVoxelForgeUseFusedEvaluator,
TEXT("Use the lowered fused evaluator for canonical TunnelNetwork operator stacks."));
bool GVoxelForgeFusedEvaluatorSwitchParsed = false;
void VF_ParseFusedEvaluatorSwitch()
{
if (GVoxelForgeFusedEvaluatorSwitchParsed)
{
return;
}
GVoxelForgeFusedEvaluatorSwitchParsed = true;
int32 CommandLineValue = GVoxelForgeUseFusedEvaluator;
if (FParse::Value(
FCommandLine::Get(), TEXT("voxel.UseFusedEvaluator="), CommandLineValue))
{
GVoxelForgeUseFusedEvaluator = CommandLineValue;
}
}
}
//=============================================================================
// L'ADAPTATEUR DE CHAMP DE BIOMES / THE BIOME FIELD ADAPTER
//=============================================================================
@@ -1690,6 +1720,7 @@ void UVoxelGenerator::EndDensityBlock() const
float UVoxelGenerator::GetDensityAt(float WorldX, float WorldY, float WorldZ) const
{
TRACE_CPUPROFILER_EVENT_SCOPE(VoxelForge_GetDensityAt);
VF_ParseFusedEvaluatorSwitch();
VoxelDensityProfile::FScopedTimer DensityProfileTimer(
VoxelDensityProfile::EBucket::GetDensityAt);
VoxelDensityProfile::FScopedTimer DensityPrologueTimer(
@@ -2156,14 +2187,43 @@ float UVoxelGenerator::GetDensityAt(float WorldX, float WorldY, float WorldZ) co
VoxelDensityProfile::FScopedTimer DensityCoreTimer(
VoxelDensityProfile::EBucket::DensityCore);
FVoxelOpSample BlockCoreSample;
bool bUsedOpBlockSample = false;
float BlockDensity = 0.f;
bool bBlockHasTunnelCore = false;
float BlockTunnelCoreSDF = FLT_MAX;
bool bBlockTunnelCoreSupportFloor = false;
bool bBlockTunnelCoreRoomFloor = false;
bool bUsedFusedEvaluator = false;
FTunnelCoreWorldEvaluation FusedTunnelCore;
// Le seul point d'entrée de la pile dans le chemin de production. Elle rend la convention
// MC (négatif = solide) comme les fonctions d'archétype, donc les disturbances et la couche
// de diff qui suivent ne voient aucune différence.
if (CP_UseOpStack)
{
const bool bCanUseFusedEvaluator =
GVoxelForgeUseFusedEvaluator != 0
&& !CP_UseCustomRecipe
&& (CP_GenType == ECaveGeneratorType::TunnelNetwork
|| CP_GenType == ECaveGeneratorType::Underwater)
#if WITH_EDITOR
&& !CP_UseComposerRegions
#endif
&& ActiveOpStack->GetFusedEvaluator()
== EVoxelOpFusedEvaluator::TunnelNetwork;
if (bCanUseFusedEvaluator)
{
// This is the lowered canonical graph. It returns the same MC core as the
// interpreted stack, plus the source's structural hand-off, without allocating
// FVoxelOpSample or traversing the graph one operator at a time for this voxel.
bUsedFusedEvaluator = true;
Result = GetDensityWithParams(
WorldX, WorldY, WorldZ, CP_Tunnel, CP_TunnelFP, LayoutVersion,
/*bApplyLegacyStructuralPosts=*/false,
&FusedTunnelCore,
/*bCollectFusedDiagnostics=*/true);
}
const bool bIntegerLatticePoint =
WorldX == FMath::FloorToFloat(WorldX)
&& WorldY == FMath::FloorToFloat(WorldY)
@@ -2171,9 +2231,13 @@ float UVoxelGenerator::GetDensityAt(float WorldX, float WorldY, float WorldZ) co
const bool bBlockSafeGenerator =
CP_GenType == ECaveGeneratorType::TunnelNetwork
|| CP_GenType == ECaveGeneratorType::Underwater;
if (bIntegerLatticePoint && bBlockSafeGenerator
if (!bUsedFusedEvaluator && bIntegerLatticePoint && bBlockSafeGenerator
&& GVoxelDensityBlockSession.bActive)
{
// Keep the rich hand-off object in the fallback branch. The lowered evaluator
// must not construct a per-sample FVoxelOpSample merely because this compatibility
// path still needs one when the block session is enabled.
FVoxelOpSample BlockCoreSample;
bUsedOpBlockSample = GVoxelDensityBlockSession.FillChunk(
*ActiveOpStack,
ChunkCoord,
@@ -2182,10 +2246,21 @@ float UVoxelGenerator::GetDensityAt(float WorldX, float WorldY, float WorldZ) co
FMath::RoundToInt(WorldY),
FMath::RoundToInt(WorldZ)),
BlockCoreSample);
if (bUsedOpBlockSample)
{
BlockDensity = BlockCoreSample.Density;
bBlockHasTunnelCore = BlockCoreSample.bHasTunnelCoreWorldEvaluation;
BlockTunnelCoreSDF = BlockCoreSample.TunnelCoreWorldSDF;
bBlockTunnelCoreSupportFloor = BlockCoreSample.bTunnelCoreSupportFloor;
bBlockTunnelCoreRoomFloor = BlockCoreSample.bTunnelCoreRoomFloor;
}
}
if (!bUsedFusedEvaluator)
{
Result = bUsedOpBlockSample
? -BlockDensity
: ActiveOpStack->EvalMC(WorldX, WorldY, WorldZ);
}
Result = bUsedOpBlockSample
? -BlockCoreSample.Density
: ActiveOpStack->EvalMC(WorldX, WorldY, WorldZ);
}
else switch (CP_GenType)
{
@@ -2250,13 +2325,16 @@ float UVoxelGenerator::GetDensityAt(float WorldX, float WorldY, float WorldZ) co
bool bHavePreDisturbanceTunnelCore = false;
if (CP_UseOpStack)
{
if (bUsedOpBlockSample && BlockCoreSample.bHasTunnelCoreWorldEvaluation)
if (bUsedFusedEvaluator)
{
PreDisturbanceTunnelCore.SDF = BlockCoreSample.TunnelCoreWorldSDF;
PreDisturbanceTunnelCore.bSupportFloor =
BlockCoreSample.bTunnelCoreSupportFloor;
PreDisturbanceTunnelCore.bRoomFloor =
BlockCoreSample.bTunnelCoreRoomFloor;
PreDisturbanceTunnelCore = FusedTunnelCore;
bHavePreDisturbanceTunnelCore = true;
}
else if (bUsedOpBlockSample && bBlockHasTunnelCore)
{
PreDisturbanceTunnelCore.SDF = BlockTunnelCoreSDF;
PreDisturbanceTunnelCore.bSupportFloor = bBlockTunnelCoreSupportFloor;
PreDisturbanceTunnelCore.bRoomFloor = bBlockTunnelCoreRoomFloor;
bHavePreDisturbanceTunnelCore = true;
}
else
@@ -2576,7 +2654,9 @@ float UVoxelGenerator::GetDensityAt(float WorldX, float WorldY, float WorldZ) co
float UVoxelGenerator::GetDensityWithParams(float WorldX, float WorldY, float WorldZ,
const FStrateGenerationParams& Params,
uint32 ParamsFingerprint, uint32 LayoutVersion,
bool bApplyLegacyStructuralPosts) const
bool bApplyLegacyStructuralPosts,
FTunnelCoreWorldEvaluation* OutTunnelCore,
bool bCollectFusedDiagnostics) const
{
//=========================================================================
// STRATE DENSITY FUNCTION (Morphology Pipeline)
@@ -2603,6 +2683,16 @@ float UVoxelGenerator::GetDensityWithParams(float WorldX, float WorldY, float Wo
// At the end, we negate for the MC table (negative = solid there).
//=========================================================================
if (OutTunnelCore != nullptr)
{
*OutTunnelCore = FTunnelCoreWorldEvaluation();
}
if (bCollectFusedDiagnostics && VoxelDensityProfile::AreCountersEnabled())
{
VoxelDensityProfile::AddCounter(
VoxelDensityProfile::ECounter::FusedTunnelSamples);
}
const uint32 SeedU = (uint32)Seed;
//=========================================================================
@@ -2708,6 +2798,9 @@ float UVoxelGenerator::GetDensityWithParams(float WorldX, float WorldY, float Wo
// transition, ce qui est le nombre de reconstructions que ce cache aurait toujours dû faire.
thread_local uint32 CachedFingerprint = 0xFFFFFFFFu;
thread_local uint32 CachedLayout = 0xFFFFFFFFu;
thread_local bool CachedUsesFusedCacheWindow = false;
const bool bUseFusedCacheWindow = OutTunnelCore != nullptr;
// Index of the room with the smallest (most-inside) SDF for this voxel.
// Written by EvaluateSDFCached, read by the terrain ops block to pick the
@@ -2747,6 +2840,7 @@ float UVoxelGenerator::GetDensityWithParams(float WorldX, float WorldY, float Wo
const bool bNeedRebuild =
StrateIdx != CachedStrate || (uint32)Seed != CachedSeed ||
ParamsFingerprint != CachedFingerprint || LayoutVersion != CachedLayout ||
bUseFusedCacheWindow != CachedUsesFusedCacheWindow ||
WarpedX < CachedSMinX || WarpedX > CachedSMaxX ||
WarpedY < CachedSMinY || WarpedY > CachedSMaxY;
@@ -2757,15 +2851,24 @@ float UVoxelGenerator::GetDensityWithParams(float WorldX, float WorldY, float Wo
VoxelDensityProfile::AddCounter(
VoxelDensityProfile::ECounter::SdfCacheBuild);
}
// Center the search box on this query's chunk. Search area = chunk XY extent
// + CaveWarpStrength margin (covers warp displacement) + gradient sampling.
// MaxInfluence (room/tunnel reach) is added internally by BuildChunkCache.
const int32 CacheChunkX = FMath::FloorToInt(WorldX / (float)CHUNK_SIZE);
const int32 CacheChunkY = FMath::FloorToInt(WorldY / (float)CHUNK_SIZE);
const float ChunkMinX = CacheChunkX * (float)CHUNK_SIZE;
const float ChunkMinY = CacheChunkY * (float)CHUNK_SIZE;
const float ChunkMaxX = ChunkMinX + (float)CHUNK_SIZE;
const float ChunkMaxY = ChunkMinY + (float)CHUNK_SIZE;
// The lowered evaluator uses the same window-invariance contract as the prepared
// room-graph source, but keeps a four-chunk XY window per worker. A tile worker can
// visit neighbouring chunk keys in an arbitrary order; the larger deterministic
// window prevents those jumps from rebuilding the graph for every small tile while
// preserving the pointwise SDF. The public/reference call keeps its historical
// one-chunk window.
const int32 QueryChunkX = FMath::FloorToInt(WorldX / (float)CHUNK_SIZE);
const int32 QueryChunkY = FMath::FloorToInt(WorldY / (float)CHUNK_SIZE);
const int32 CacheRegionChunks = bUseFusedCacheWindow ? 4 : 1;
const int32 CacheRegionSize = CHUNK_SIZE * CacheRegionChunks;
const int32 CacheRegionX = FMath::FloorToInt(
WorldX / static_cast<float>(CacheRegionSize));
const int32 CacheRegionY = FMath::FloorToInt(
WorldY / static_cast<float>(CacheRegionSize));
const float ChunkMinX = CacheRegionX * (float)CacheRegionSize;
const float ChunkMinY = CacheRegionY * (float)CacheRegionSize;
const float ChunkMaxX = ChunkMinX + (float)CacheRegionSize;
const float ChunkMaxY = ChunkMinY + (float)CacheRegionSize;
const float Expansion = FMath::Abs(Params.CaveWarpStrength)
* VOXEL_NOISE_SCALE * 1.5f + 2.0f;
@@ -2781,7 +2884,10 @@ float UVoxelGenerator::GetDensityWithParams(float WorldX, float WorldY, float Wo
{
const int32 ChunkZ = FMath::FloorToInt(WorldZ / (float)CHUNK_SIZE);
UVoxelStrateDefinition* Def = StrateManager->GetStrateForChunk(
FIntVector(CacheChunkX, CacheChunkY, ChunkZ));
FIntVector(
bUseFusedCacheWindow ? QueryChunkX : CacheRegionX,
bUseFusedCacheWindow ? QueryChunkY : CacheRegionY,
ChunkZ));
if (Def) TerrainOps = &Def->TerrainOperations;
}
@@ -2798,6 +2904,7 @@ float UVoxelGenerator::GetDensityWithParams(float WorldX, float WorldY, float Wo
CachedSeed = (uint32)Seed;
CachedFingerprint = ParamsFingerprint;
CachedLayout = LayoutVersion;
CachedUsesFusedCacheWindow = bUseFusedCacheWindow;
}
// Evaluate SDF using cached rooms and tunnels (WARPED coordinates).
@@ -2901,6 +3008,21 @@ float UVoxelGenerator::GetDensityWithParams(float WorldX, float WorldY, float Wo
}
}
// The canonical stack's source publishes this same structural result before its detail
// modifiers run. The lowered path asks the scalar evaluator for it directly, so the common
// post-disturbance tail can consume the result without allocating an FVoxelOpSample or
// invoking the interpreted stack a second time for this sample.
if (OutTunnelCore != nullptr
&& Params.RoomDensity > 0.0f && Params.RoomSpacing > 0.0f)
{
// The no-column form is the exact scalar reference path: it performs the world-bound and
// floor projection for the current point without eagerly materialising a whole XY table.
// The interpreted source has a lazy worker LRU for block evaluation; building that table
// here would turn a cache-window change into hundreds of unnecessary column builds.
*OutTunnelCore = VoxelCaveMorphology::EvaluateTunnelCoreWorld(
WorldX, WorldY, WorldZ, SDFCache, nullptr);
}
//=========================================================================
// EARLY-OUT: Skip detail work for deep-solid voxels
//=========================================================================
@@ -2913,6 +3035,13 @@ float UVoxelGenerator::GetDensityWithParams(float WorldX, float WorldY, float Wo
// seal / modifiers, so we jump to Step 5 instead of returning early.
const float DetailThreshold = Params.SDFBlendRadius * 3.0f;
const bool bNearCaveSurface = (CaveSDF < DetailThreshold) && (CaveSDF < FLT_MAX);
if (bCollectFusedDiagnostics && VoxelDensityProfile::AreCountersEnabled())
{
VoxelDensityProfile::AddCounter(
bNearCaveSurface
? VoxelDensityProfile::ECounter::FusedTunnelDetailSamples
: VoxelDensityProfile::ECounter::FusedTunnelDetailSkipped);
}
//=========================================================================
// STEP 4b: SURFACE ROUGHNESS (volumetric, SDF-based)
@@ -57,6 +57,19 @@ struct VOXELFORGE_API FVoxelStrateOpContract
EVoxelOpResourceMask ProvidedResources = VoxelOpResources::None;
};
/**
* A lowered evaluator selected when a stack has a proven, canonical implementation.
*
* The authored operator list remains present for classification, diagnostics, tests, and the
* fallback evaluator. This is a plan choice made while the stack is assembled, not a runtime
* CPU-feature dispatch and not a second semantic graph.
*/
enum class EVoxelOpFusedEvaluator : uint8
{
None,
TunnelNetwork,
};
/**
* FVoxelOpStack — une liste ordonnée d'opérateurs + le pliage de verdict de boîte.
*
@@ -114,6 +127,9 @@ public:
void Add(TUniquePtr<IVoxelDensityOp> Op)
{
// Adding an op invalidates any previously selected lowered plan. Factories mark the plan
// only after the complete authored graph has been assembled.
FusedEvaluator = EVoxelOpFusedEvaluator::None;
FOpEntry Entry;
Entry.Op = MoveTemp(Op);
if (Entry.Op.Get() != nullptr)
@@ -133,6 +149,18 @@ public:
int32 Num() const { return Ops.Num(); }
/** Select the lowered evaluator for this complete authored graph. */
void SetFusedEvaluator(EVoxelOpFusedEvaluator InEvaluator)
{
FusedEvaluator = InEvaluator;
}
/** The lowered plan, or None when the general interpreted/block path must be used. */
EVoxelOpFusedEvaluator GetFusedEvaluator() const
{
return FusedEvaluator;
}
/** True when this stack publishes the requested immutable geometry resource. */
bool ProvidesResource(EVoxelOpResourceMask Resource) const
{
@@ -475,6 +503,7 @@ private:
TArray<FOpEntry> Ops;
FVoxelOpContext PreparedContext;
bool bHasPreparedContext = false;
EVoxelOpFusedEvaluator FusedEvaluator = EVoxelOpFusedEvaluator::None;
};
//=============================================================================
@@ -123,6 +123,9 @@ namespace VoxelDensityProfile
TileVerdictCacheHits,
TileVerdictRegionHits,
TileVerdictCacheStores,
FusedTunnelSamples,
FusedTunnelDetailSamples,
FusedTunnelDetailSkipped,
OpBlockBuilds,
OpBlockSamples,
OpBlockOperators,
+9 -1
View File
@@ -20,6 +20,7 @@ class UVoxelSettings;
class UVoxelStrateManager;
class UVoxelDiffLayer;
class UVoxelBiomeDefinition;
struct FTunnelCoreWorldEvaluation;
//=============================================================================
// LOD-AWARE OCTAVE REDUCTION (T2.b)
@@ -189,7 +190,14 @@ public:
float GetDensityWithParams(float WorldX, float WorldY, float WorldZ,
const FStrateGenerationParams& Params,
uint32 ParamsFingerprint, uint32 LayoutVersion,
bool bApplyLegacyStructuralPosts = true) const;
bool bApplyLegacyStructuralPosts = true,
// Optional hand-off used by a lowered canonical evaluator. When
// non-null, this receives the pre-disturbance TunnelNetwork core;
// it does not alter the scalar field result.
FTunnelCoreWorldEvaluation* OutTunnelCore = nullptr,
// Diagnostics are opt-in so ordinary/reference calls do not pay
// for fused-path counters.
bool bCollectFusedDiagnostics = false) const;
/**
* Densité pour une strate Slab (FlatPlain / CrystalChamber).