feat(worms): asset-owned worm off switch; offline stack symboliser -- cheap-worm default to be reverted

Worms round (Codex; reviewed by me against its report and PERF-SAMPLED.md).

Part 1, a real off switch: UVoxelStrateDefinition::bEnableWorms (default true, so existing assets
are unchanged). It is deliberately not in FStrateGenerationParams, so the 83-field memo-key guard
is untouched. It resolves after composer candidates, season vectors and boundary blending, is
included in the generation fingerprint, and clamps composer overrides, so a roll cannot re-enable
it. With strength 0 the fused path skips the worm block, the op-stack builder omits the worm
source, and the interval/classifier paths do no worm work. Proof: disabled == hand-zeroed control
byte for byte (70D310A4, 74,672 triangles), capability 20,856 / 10,917, connected, zero worm rows
in the symbolised sampler. voxel.WormsForceOff is a dev-only, world-changing measurement
override, included in the fingerprint.

Part 0: the sampler writes a module map (733 modules, identity checked), and a post-exit
offline symboliser (VoxelStackOfflineSymbolizer, editor module) rebuilds the all-LOD / LOD0 /
LOD1+ exclusive / inclusive / source-line tables. A planted cost was found (5 named rows) and then
removed. DbgHelp still never runs in the measured process.

Part 2, "cheap worms": FAILED to deliver, and the default is wrong. No candidate was faster:
lattice-2/4 gave no gain, and value noise was within noise static and ~2.1% WORSE in moving worker
seconds. Yet this commit carries voxel.WormNoiseMode=1 (value noise) as the default, a field change
(29/64 test tiles) with no benefit, selected after value-noise variants were tried until one
passed capability (three were rejected at 20,821-20,828). The next round reverts the default to
exact Perlin (mode 0) and removes the no-gain world-changing experiment modes.

The finding that matters: disabling worms entirely (WormsForceOff A/B, two interleaved runs each)
saves only ~2.5% static and ~3.4% moving worker time. The sampler's "worm line
VoxelGenerator.cpp:3985 = 19.4% of LOD0" was a misattribution of inlined noise code to one PDB
line. Line-level attribution of inlined code must be confirmed by ablation before a round targets
it. That is why the worm block skip (26b3476) found nothing.

Runs: the full ledger is in PERF-SAMPLED.md. Failures: one moving wrapper hit the graceful-close
timeout and was rerun; the first offline-symboliser commandlet hit the DDC no-writable-nodes fatal
(crash folder UECC-...CE75ACD5, 08:01) and was retried with the writable Saved\DDC.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TaFdXhTdYMkW1xrV6ApT6Y
This commit is contained in:
Fr0zkaandClaude Opus 5 committed 2026-09-14 08:19:50 +02:00
1 parent 11f0c1335b
commit c132243b30
15 files changed
+2406 -552

No files matched your search

@@ -12,6 +12,7 @@
#include "VoxelStartupTrace.h"
#include "VoxelTerrainOpDefinition.h" // For UVoxelTerrainOpDefinition::ApplyTo
#include "VoxelBiomeDefinition.h" // For UVoxelBiomeDefinition (biome context flatten)
#include "VoxelWormField.h" // World-changing worm evaluator mode/lattice identity
#include "HAL/IConsoleManager.h"
#include "Misc/CommandLine.h"
#include "Misc/Crc.h"
@@ -33,6 +34,42 @@ namespace
GVoxelForgeFloorRound1PassageProjectionCache,
TEXT("Reuse exact passage floor projections within one density sample; 0 disables the A/B cache."));
// This is a measurement override only. The asset-owned bEnableWorms field remains the real
// per-strate contract; this process-wide switch exists so the game harness can measure the
// cost of enabled versus disabled worms without editing an owner's asset. It is resolved once
// before generation params are published so workers cannot disagree mid-world.
int32 GVoxelForgeWormsForceOff = 0;
std::atomic<int32> GResolvedWormsForceOff(-1);
FAutoConsoleVariableRef CVarVoxelForgeWormsForceOff(
TEXT("voxel.WormsForceOff"),
GVoxelForgeWormsForceOff,
TEXT("WORLD-CHANGING DEVELOPMENT-ONLY measurement override. 0=asset behavior, 1=force "
"all worm strengths to zero. Never vary this between multiplayer peers or world "
"regenerations; it is not the per-strate off switch."),
ECVF_Default);
bool VF_WormsForceOff()
{
int32 Existing = GResolvedWormsForceOff.load(std::memory_order_acquire);
if (Existing >= 0)
{
return Existing != 0;
}
int32 CommandLineValue = GVoxelForgeWormsForceOff;
FParse::Value(
FCommandLine::Get(), TEXT("voxel.WormsForceOff="), CommandLineValue);
const int32 Resolved = CommandLineValue != 0 ? 1 : 0;
int32 Expected = -1;
if (!GResolvedWormsForceOff.compare_exchange_strong(
Expected, Resolved, std::memory_order_release, std::memory_order_acquire))
{
return Expected != 0;
}
GVoxelForgeWormsForceOff = Resolved;
return Resolved != 0;
}
struct FRuntimeRoughnessOverrides
{
bool bSurfaceRoughness = false;
@@ -1429,6 +1466,15 @@ bool UVoxelStrateManager::SetComposerOverrideForStrate(
Override->CandidateSeed = CandidateSeed;
Override->Archetype = Archetype;
Override->Params = Params;
if ((VF_WormsForceOff() || !TargetSlot->Definition->bEnableWorms)
&& (Archetype == ECaveGeneratorType::TunnelNetwork
|| Archetype == ECaveGeneratorType::Underwater))
{
// The composer is allowed to roll a complete candidate, but a disabled asset must never
// retain a worm-enabled candidate in its runtime override, even for code that inspects
// the override before GetGenerationParams applies the final resolution gate.
Override->Params.TunnelNetworkParams.WormStrength = 0.0f;
}
VF_SetComposerRuntimeBounds(
Override->Params,
(float)(TargetSlot->TopChunkZ + 1) * CHUNK_SIZE,
@@ -5474,6 +5520,19 @@ FStrateGenerationParams UVoxelStrateManager::GetGenerationParams(const FIntVecto
const FStrateSlot& Slot = StrateLayout[SlotIdx];
// This is the one final resolution gate for the asset-owned worm switch. It is deliberately
// applied after composer/season replacements and after boundary blending: a disabled current
// strate can never be re-enabled by a rolled candidate or by an enabled neighbour.
const auto ResolveFinalParams = [&Slot](FStrateGenerationParams Result)
{
if (VF_WormsForceOff()
|| (Slot.Definition != nullptr && !Slot.Definition->bEnableWorms))
{
Result.WormStrength = 0.0f;
}
return VF_ApplyRuntimeRoughnessOverrides(Result);
};
#if WITH_EDITOR
if (const FVoxelStrateComposerSlotOverride* Override =
FindComposerOverride(Slot.StrateIndex))
@@ -5483,7 +5542,7 @@ FStrateGenerationParams UVoxelStrateManager::GetGenerationParams(const FIntVecto
Result.StrateBottomWorldZ = (float)Slot.BottomChunkZ * CHUNK_SIZE;
// A candidate is a hard replacement of this slot. Do not blend its rolled vector with an
// authored neighbour at a boundary; this is also how the offline fixture measures it.
return VF_ApplyRuntimeRoughnessOverrides(Result);
return ResolveFinalParams(Result);
}
#endif
@@ -5494,7 +5553,7 @@ FStrateGenerationParams UVoxelStrateManager::GetGenerationParams(const FIntVecto
Result.StrateBottomWorldZ = (float)Slot.BottomChunkZ * CHUNK_SIZE;
// Season vectors are already final, measured slot records. Vertical structural posts own
// their boundary; blending them with another selected recipe would describe neither one.
return VF_ApplyRuntimeRoughnessOverrides(Result);
return ResolveFinalParams(Result);
}
FStrateGenerationParams BaseParams = BuildParamsFromDefinition(Slot.Definition);
@@ -5589,7 +5648,7 @@ FStrateGenerationParams UVoxelStrateManager::GetGenerationParams(const FIntVecto
float Alpha = 1.0f - ((float)DistFromBottom / (float)EffectiveBlend);
Alpha = FMath::Clamp(Alpha, 0.0f, 1.0f);
return VF_ApplyRuntimeRoughnessOverrides(
return ResolveFinalParams(
FStrateGenerationParams::Lerp(BaseParams, BelowParams, Alpha));
}
break;
@@ -5643,7 +5702,7 @@ FStrateGenerationParams UVoxelStrateManager::GetGenerationParams(const FIntVecto
// Only blend if alpha > 0 (we're inside the warped transition zone)
if (Alpha > 0.0f)
{
return VF_ApplyRuntimeRoughnessOverrides(
return ResolveFinalParams(
FStrateGenerationParams::Lerp(BaseParams, BelowParams, Alpha));
}
}
@@ -5685,7 +5744,7 @@ FStrateGenerationParams UVoxelStrateManager::GetGenerationParams(const FIntVecto
float Alpha = 1.0f - ((float)DistFromTop / (float)EffectiveBlend);
Alpha = FMath::Clamp(Alpha, 0.0f, 1.0f);
return VF_ApplyRuntimeRoughnessOverrides(
return ResolveFinalParams(
FStrateGenerationParams::Lerp(BaseParams, AboveParams, Alpha));
}
break;
@@ -5715,7 +5774,7 @@ FStrateGenerationParams UVoxelStrateManager::GetGenerationParams(const FIntVecto
if (Alpha > 0.0f)
{
return VF_ApplyRuntimeRoughnessOverrides(
return ResolveFinalParams(
FStrateGenerationParams::Lerp(BaseParams, AboveParams, Alpha));
}
}
@@ -5725,7 +5784,7 @@ FStrateGenerationParams UVoxelStrateManager::GetGenerationParams(const FIntVecto
}
// Not near any boundary (or Hard transition) — use this strate's params directly
return VF_ApplyRuntimeRoughnessOverrides(BaseParams);
return ResolveFinalParams(BaseParams);
}
//=============================================================================
@@ -5745,8 +5804,15 @@ FStrateGenerationParams UVoxelStrateManager::BuildParamsFromDefinition(const UVo
{
if (!Definition) return FStrateGenerationParams();
// Base params only — terrain op fields stay 0 until per-room assignment.
return Definition->GenerationParams;
// Base params only — terrain op fields stay 0 until per-room assignment. Resolve the asset
// switch here as well as at the final return so a disabled neighbour cannot leak worm strength
// back through a boundary blend.
FStrateGenerationParams Result = Definition->GenerationParams;
if (VF_WormsForceOff() || !Definition->bEnableWorms)
{
Result.WormStrength = 0.0f;
}
return Result;
}
uint64 UVoxelStrateManager::GetGenerationParamsFingerprint() const
@@ -5817,6 +5883,13 @@ uint64 UVoxelStrateManager::GetGenerationParamsFingerprint() const
HashValue(Slot.HeightInChunks);
HashValue(Slot.Definition->GeneratorType);
HashValue(Slot.Definition->bUseOperatorStack);
HashValue(Slot.Definition->bEnableWorms);
HashValue(VF_WormsForceOff());
// The selected evaluator is a world input even though it is deliberately not part of
// FStrateGenerationParams (the fixed 83-field memo key). Keep persistent/verdict keys
// from crossing an explicit WormNoiseMode/WormLatticeStep experiment.
HashValue(VoxelWormField::GetNoiseMode());
HashValue(VoxelWormField::GetLatticeStep());
HashValue(Slot.Definition->StrateHeightInChunks);
HashValue(Slot.Definition->TransitionType);
HashValue(Slot.Definition->TransitionBlendChunks);