wip(assembler): round S2 (Sol) -- shared per-tile feature queries for every Core family; island placement 30x faster

Island-only worker context replaced by a shared worker-local candidate domain
(WorldAssemblyFeatureTileListV1 / ScopedWorldAssemblyFeatureQueriesV1) serving
SurfaceWorld, FloatingIslands and far-shell queries; a per-domain cost hint keeps the
full scan on small plans. Content placement prepares the same queries and skips proven
air: owner island cell p95 7.07 -> 0.24 ms; populated fixture 572 exact placements,
3.27 s -> 29 ms. Full/candidate hashes equal with dropped-feature controls on both
families; S1 default W1/W8/W16 hashes reproduced; Core-off and M2 fixtures pass.
Default contract for future families in ARCHITECTURE 8.23. Run on gpt-6.1-sol medium.

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-10-18 19:50:45 +02:00
1 parent d6e69420e8
commit 6ddb834b1c
19 files changed
+1847 -130

No files matched your search

+38
View File
@@ -1326,6 +1326,44 @@ figures measure anchor-search work and steady update overhead, not spawn/asset-i
See Standalone/Research/WorldAssemblerM3/Round5/REPORT.md for the test matrix, owner-plan hash proof, See Standalone/Research/WorldAssemblerM3/Round5/REPORT.md for the test matrix, owner-plan hash proof,
and content-placement measurements. and content-placement measurements.
### 8.23 Shared assembler query domains — World Assembler S2 / M3 Round 6
Meshing, far-shell field queries and placement share `ScopedWorldAssemblyFeatureQueriesV1`.
Each family prepares conservative candidates for an inclusive domain, including normal halos.
Scopes borrow immutable recipe/plan pointers from the owning world/snapshot, live on the worker,
restore the previous scope when nested, and fall back to full evaluation outside their domain.
Queues, generation epochs, edit locks, streaming policy and canonical field arithmetic are unchanged.
`candidateEvaluationUseful` is a cost hint resolved once per domain: the measured one-feature
SurfaceWorld and four-island plans prefer the full evaluator. Larger plans use candidates. Validation
can force candidates to prove the bounds even for a small plan; this never changes recipe identity.
The default family contract for caves, shafts, pits, passages and rivers is:
1. Declare conservative inclusive influence bounds, including blend, displacement and warp.
Unknown influence retains the feature. Keep canonical operation order.
2. If remote features can determine exact positive density, provide sound value-side certificates
or evaluate them. Geometric AABBs alone cannot preserve arbitrary density values; island root
ellipsoids retain S1's slope and smooth-min certificate. Optional union-group data belongs to the
family proof, not to the engine's notion of Euclidean distance.
3. Families with a proven air base may declare conservative solid-support bounds. The shared column
union skips only proven air; solid-base caves and unknown support retain their ordinary march.
Preserve the original sampling phase, crossing count, bisection and final interpolation.
4. Audit full-scan versus candidates, halos and outside-domain fallback with equal hashes and a
deliberately removed needed feature before enabling a new proof.
SurfaceWorld's biome-site search remains its bounded local lattice. Cached XY column evaluation
uses finite feature candidates; types 1/3 affect every Z, while types 2/4 are neutral outside their
validated boxes. Shelf source-column queries outside the scope use the full column evaluator.
Core meshing prepares both native families, including cross-strate tiles. FarShells remains derived
repair geometry with its existing connector/stratum filtering; its canonical field calls benefit
from the same scope wherever their samples lie inside the domain.
Content snapshots declare support bounds once. Decoration cells and landmark columns prepare the
shared domain with a gradient halo, and retain exact crossings. Core-off retains its legacy march.
Validation compares complete placement transforms and canonical anchor residuals, and applies then
clears an isolated populated HISM fixture under BuildHost null RHI. These CPU costs do not measure
rendering or GPU upload. See the Round 6 report for results and limits.
## 9. Multiplayer model (listen-server first, dedicated-friendly) ## 9. Multiplayer model (listen-server first, dedicated-friendly)
> **Status: transport is DESIGN ONLY — nothing is networked in-tree yet** (no `Replicated`/`HasAuthority`/RPCs; a > **Status: transport is DESIGN ONLY — nothing is networked in-tree yet** (no `Replicated`/`HasAuthority`/RPCs; a
+19 -5
View File
@@ -882,11 +882,25 @@ uses the owner/Core ratio as well. Edits use sequenced `applyEditTransaction` ca
Core work drains, then regenerate Core's affected keys under a new epoch. Existing streaming anchors Core work drains, then regenerate Core's affected keys under a new epoch. Existing streaming anchors
participate; the headless move proof covers `CollisionOnly` policy. participate; the headless move proof covers `CollisionOnly` policy.
The reusable `BuildWorldAssemblyFeatureTileListV1` accepts conservative inclusive feature bounds for The reusable `BuildWorldAssemblyFeatureTileListV1` accepts conservative inclusive feature bounds.
future caves, shafts, pits, and passages. Floating Islands builds one `FloatingIslandTileFeatureListV1` `ScopedWorldAssemblyFeatureQueriesV1` replaces the island-only ShapeWorld worker context: meshing,
per tile and the scoped worker context makes `FloatingIslandFieldV1::densityAt` evaluate only that derived shells, decoration cells and landmark columns share its nestable worker scope. Recipe/plan
candidate list. Candidate order stays canonical, the field hash remains bit-identical, and a dropped- identity and an inclusive domain guard each query; outside queries fall back to the full evaluator.
feature negative control is part of the standalone Round 5 audit. See ARCHITECTURE §8.23 and WorldAssemblerM3/Round6/REPORT.md.
| Symbol | Canonical file | Role |
|---|---|---|
| `WorldAssemblyFeatureTileListV1`, `WorldAssemblyFeatureGroupBoundsV1`, `WorldAssemblyFeatureQueryV1` | `Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h` | Stable-order candidates, optional exact value-side group certificates and borrowed recipe/plan identity. Old island names remain aliases. |
| `ScopedWorldAssemblyFeatureQueriesV1::{find,ScopedWorldAssemblyFeatureQueriesV1}`, `WorldAssemblyFeatureTileListV1::candidateEvaluationUseful` | `Standalone/Core/src/WorldAssemblerInternal.h` / public header | Worker-local domain, cost hint resolved once per query, forced validation, nested restoration and full-scan fallback; replaces ShapeWorld's island context. |
| `Heightfield2D::buildFeatureTileList`, `world_assembly_detail::FeatureRange` | `Standalone/Core/src/WorldAssemblerInternal.h` | XY bounds for column writers; exact no-op bounds for finite boxes/shelves; canonical operation order. |
| `FloatingIslandFieldV1::{solidSupportBounds,buildFeatureTileList,densitySubvoxels}` | `Standalone/Core/src/FloatingIslandsInternal.h` | Finite support declarations, root-aware distance certificates, automatic shared-scope consumption. |
| `WorldAssemblyFeatureColumnRangeV1`, `BuildWorldAssemblyFeatureColumnRangeV1` | Public Core header / `Standalone/Core/src/WorldAssemblerInternal.h` | Conservative column support union for air-base families; invalid bounds are unknown. |
| `VF_ContentFeatureQueries`, `VF_ConstrainContentMarch` | `Private/VoxelContentManager.cpp` | Immutable queries prepared per cell/landmark; skips proven air while preserving march phase and exact crossing refinement. |
| `UVoxelContentManager::MeasurePlacementCostForValidation` | `Private/VoxelContentManager.cpp` | Authored/populated full-versus-candidate transforms, canonical anchor checks, batch and isolated HISM application cost. |
| `UVoxelContentManager::ApplyRegion` | `Private/VoxelContentManager.cpp` / `Public/VoxelContentManager.h` | Normal region application retains landmark footprints; its default-true footprint flag isolates the temporary populated validation fixture. |
| `RunCoreSharedFeatureCostAudit`, `RunCoreContentPlacementCostAudit` | `Source/VoxelForgeEditor/Private/VoxelForgeExploreCommandlet.cpp` | Paired owner-plan tile/shell timings and hashes, placement metrics and transient BuildHost plan exports. |
| `TileGenerationOptions::useAssemblyFeatureQueries`, `TileGenerationStats::{featureListMilliseconds,farShellMilliseconds}` | Public Core header / `Standalone/Core/src/VoxelForgeCore.cpp` | Default-on candidate route, full-scan validation control and stage timing; no identity change. |
| `SharedFeatureListAudit.cpp`, `BuildSharedFeatureListAudit.ps1` | `Standalone/Research/WorldAssemblerM3/Round6/` | Owner-plan/stress hashes, shared-scope probes and deliberately dropped needed features. |
`-coreworldtest=1` in the BuildHost editor commandlet validates initial tiles, expected collision `-coreworldtest=1` in the BuildHost editor commandlet validates initial tiles, expected collision
bodies/empty tiles, anchor movement, a carve transaction, travel to the B landing and native surface bodies/empty tiles, anchor movement, a carve transaction, travel to the B landing and native surface
+210 -10
View File
@@ -133,6 +133,7 @@ struct FCoreFloatingIslandFieldSnapshot
VoxelForgeCore::FloatingIslandRecipeV1 Recipe; VoxelForgeCore::FloatingIslandRecipeV1 Recipe;
VoxelForgeCore::FloatingIslandPlanV1 Plan; VoxelForgeCore::FloatingIslandPlanV1 Plan;
TUniquePtr<VoxelForgeCore::FloatingIslandFieldV1> Field; TUniquePtr<VoxelForgeCore::FloatingIslandFieldV1> Field;
std::vector<VoxelForgeCore::WorldAssemblyFeatureBoundsV1> SolidSupportBounds;
}; };
struct FCoreFloatingIslandSnapshot struct FCoreFloatingIslandSnapshot
@@ -231,6 +232,49 @@ struct FCoreFloatingIslandSnapshot
} }
}; };
// Shared consumer preparation; snapshots own all pointers for the entire worker lifetime.
// Preparation partagee : les snapshots gardent les pointeurs vivants durant toute la tache.
static std::vector<VoxelForgeCore::WorldAssemblyFeatureQueryV1> VF_ContentFeatureQueries(
const FCoreSurfaceSnapshot* Surface, const FCoreFloatingIslandSnapshot* Islands,
int32 StrateIndex, float MinX, float MinY, float MinZ, float MaxX, float MaxY, float MaxZ)
{
using namespace VoxelForgeCore;
constexpr double Q = WorldAssemblySubvoxelsPerVoxel;
const std::array<int64,3> Min{static_cast<int64>(std::floor(MinX*Q)),
static_cast<int64>(std::floor(MinY*Q)),static_cast<int64>(std::floor(MinZ*Q))};
const std::array<int64,3> Max{static_cast<int64>(std::ceil(MaxX*Q)),
static_cast<int64>(std::ceil(MaxY*Q)),static_cast<int64>(std::ceil(MaxZ*Q))};
std::vector<WorldAssemblyFeatureQueryV1> Queries;
if (Surface)
if (const auto* Plan = Surface->FindPlan(StrateIndex))
{
const Heightfield2D Field(Surface->Recipe,*Plan);
Queries.push_back({Field.recipe(),Field.plan(),Field.buildFeatureTileList(Min,Max)});
}
if (Islands)
if (const auto* Entry = Islands->FindField(StrateIndex))
Queries.push_back({&Entry->Recipe,&Entry->Plan,Entry->Field->buildFeatureTileList(Min,Max)});
return Queries;
}
// Preserve the original march lattice and crossing refinement, skipping only certified air.
// Garder la phase du pas et le raffinement d'origine; sauter uniquement l'air prouve.
static bool VF_ConstrainContentMarch(std::span<const VoxelForgeCore::WorldAssemblyFeatureBoundsV1> Bounds,
float X, float Y, float Step, float& Top, float& Bottom)
{
constexpr double Q = VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel;
const auto Range = VoxelForgeCore::BuildWorldAssemblyFeatureColumnRangeV1(Bounds,
static_cast<int64>(std::llround(X*Q)),static_cast<int64>(std::llround(Y*Q)));
if (!Range.known) return true;
if (Range.empty) return false;
const float SupportTop = static_cast<float>(Range.maximumZSubvoxels/Q);
const float SupportBottom = static_cast<float>(Range.minimumZSubvoxels/Q);
const float SkipSteps = FMath::Max(0.0f,FMath::FloorToFloat((Top-SupportTop)/Step)-1.0f);
Top -= SkipSteps*Step;
Bottom = FMath::Max(Bottom,SupportBottom-Step);
return Top >= Bottom;
}
// Global safety cap on spawned decoration ACTORS per cell (across all entries). // Global safety cap on spawned decoration ACTORS per cell (across all entries).
// HISM instances are exempt — they're batched render data, capped per entry by MaxPerChunk. // HISM instances are exempt — they're batched render data, capped per entry by MaxPerChunk.
static constexpr int32 GMaxDecorationActorsPerCell = 400; static constexpr int32 GMaxDecorationActorsPerCell = 400;
@@ -330,6 +374,7 @@ void UVoxelContentManager::Initialize(AActor* InOwner, UVoxelStrateManager* InSt
IslandPlan.strateIndex); IslandPlan.strateIndex);
continue; continue;
} }
Entry->SolidSupportBounds = Entry->Field->solidSupportBounds();
IslandSnapshot->Fields.Add(IslandPlan.strateIndex, Entry); IslandSnapshot->Fields.Add(IslandPlan.strateIndex, Entry);
} }
if (!IslandSnapshot->Fields.IsEmpty()) if (!IslandSnapshot->Fields.IsEmpty())
@@ -799,7 +844,7 @@ bool UVoxelContentManager::MeasurePlacementCostForValidation(
const int32 Step = FMath::Max(1, Settings->DecorationMarchStepVoxels); const int32 Step = FMath::Max(1, Settings->DecorationMarchStepVoxels);
const int32 MaxCrossings = FMath::Max(1, Settings->DecorationMaxCrossingsPerColumn); const int32 MaxCrossings = FMath::Max(1, Settings->DecorationMaxCrossingsPerColumn);
const float ColumnDepth = static_cast<float>(FMath::Max(8, Settings->DecorationColumnDepthVoxels)); const float ColumnDepth = static_cast<float>(FMath::Max(8, Settings->DecorationColumnDepthVoxels));
TArray<double> CellMilliseconds; TArray<double> CellMilliseconds, FullScanCellMilliseconds;
CellMilliseconds.Reserve(SampleCellCount); CellMilliseconds.Reserve(SampleCellCount);
int64 TotalColumns = 0; int64 TotalColumns = 0;
int64 TotalDensityQueries = 0; int64 TotalDensityQueries = 0;
@@ -810,7 +855,7 @@ bool UVoxelContentManager::MeasurePlacementCostForValidation(
const int32 X = Sample % GridSide - GridSide / 2; const int32 X = Sample % GridSide - GridSide / 2;
const int32 Y = Sample / GridSide - GridSide / 2; const int32 Y = Sample / GridSide - GridSide / 2;
const FIntPoint Cell(PlayerCell.X + X, PlayerCell.Y + Y); const FIntPoint Cell(PlayerCell.X + X, PlayerCell.Y + Y);
const double CellStart = FPlatformTime::Seconds(); double CellElapsed = 0.0, FullScanCellElapsed = 0.0;
for (const auto& GridAudit : { for (const auto& GridAudit : {
std::make_pair(&NearGrid, &NearEntriesAudit), std::make_pair(&NearGrid, &NearEntriesAudit),
std::make_pair(&FarGrid, &FarEntriesAudit)}) std::make_pair(&FarGrid, &FarEntriesAudit)})
@@ -822,9 +867,26 @@ bool UVoxelContentManager::MeasurePlacementCostForValidation(
if (Entries.IsEmpty()) continue; if (Entries.IsEmpty()) continue;
TArray<FDecoSpawn> Spawns; TArray<FDecoSpawn> Spawns;
FPlacementBuildCounters Counters; FPlacementBuildCounters Counters;
TArray<FDecoSpawn> FullScanSpawns;
const double FullStart = FPlatformTime::Seconds();
BuildCellSpawns(Generator, OwnerXf, Cell, CurrentCtx, Entries, EntryBiome,
static_cast<uint32>(Seed), Grid.Spacing, static_cast<float>(Step),
MaxCrossings, ColumnDepth, FullScanSpawns, nullptr, false);
FullScanCellElapsed += (FPlatformTime::Seconds()-FullStart)*1000.0;
const double CandidateStart = FPlatformTime::Seconds();
BuildCellSpawns(Generator, OwnerXf, Cell, CurrentCtx, Entries, EntryBiome, BuildCellSpawns(Generator, OwnerXf, Cell, CurrentCtx, Entries, EntryBiome,
static_cast<uint32>(Seed), Grid.Spacing, static_cast<float>(Step), static_cast<uint32>(Seed), Grid.Spacing, static_cast<float>(Step),
MaxCrossings, ColumnDepth, Spawns, &Counters); MaxCrossings, ColumnDepth, Spawns, &Counters);
CellElapsed += (FPlatformTime::Seconds()-CandidateStart)*1000.0;
if (Spawns.Num()!=FullScanSpawns.Num())
{ OutError=TEXT("Feature-query placement count changed from full scan."); return false; }
for (int32 Index=0;Index<Spawns.Num();++Index)
{
if (Spawns[Index].EntryIdx!=FullScanSpawns[Index].EntryIdx
|| !Spawns[Index].Xf.Equals(FullScanSpawns[Index].Xf,0.0f))
{ OutError=TEXT("Feature-query placement transform changed from full scan."); return false; }
++OutAudit.ExactPlacementComparisons;
}
TotalColumns += Counters.Columns; TotalColumns += Counters.Columns;
TotalDensityQueries += Counters.DensityQueries; TotalDensityQueries += Counters.DensityQueries;
TotalSurfaceQueries += Counters.SurfaceQueries; TotalSurfaceQueries += Counters.SurfaceQueries;
@@ -875,7 +937,10 @@ bool UVoxelContentManager::MeasurePlacementCostForValidation(
} }
} }
} }
CellMilliseconds.Add((FPlatformTime::Seconds() - CellStart) * 1000.0); CellMilliseconds.Add(CellElapsed);
FullScanCellMilliseconds.Add(FullScanCellElapsed);
OutAudit.ActiveBatchMilliseconds += CellElapsed;
OutAudit.FullScanActiveBatchMilliseconds += FullScanCellElapsed;
} }
auto MedianAndP95 = [](TArray<double> Samples, double& OutMedian, double& OutP95) auto MedianAndP95 = [](TArray<double> Samples, double& OutMedian, double& OutP95)
@@ -899,6 +964,117 @@ bool UVoxelContentManager::MeasurePlacementCostForValidation(
MedianAndP95(MoveTemp(CellMilliseconds), OutAudit.CellMedianMilliseconds, MedianAndP95(MoveTemp(CellMilliseconds), OutAudit.CellMedianMilliseconds,
OutAudit.CellP95Milliseconds); OutAudit.CellP95Milliseconds);
double UnusedMedian = 0.0;
MedianAndP95(MoveTemp(FullScanCellMilliseconds),UnusedMedian,OutAudit.FullScanCellP95Milliseconds);
// Deliberately populated fixture alongside the unmodified authored palette. No actors are spawned.
// Fixture volontairement peuplee, a cote de la palette auteur intacte. Aucun acteur cree.
UStaticMesh* FixtureMesh=nullptr;
for (const auto* Entries : {&NearEntriesAudit,&FarEntriesAudit})
for (const auto& Entry : *Entries)
if (!FixtureMesh && Entry.Profile.InstancedMesh) FixtureMesh=Entry.Profile.InstancedMesh;
if (!FixtureMesh)
FixtureMesh=LoadObject<UStaticMesh>(nullptr,TEXT("/Engine/BasicShapes/Cube.Cube"));
TMap<FIntPoint,FDecoRegionBuild> FixtureBuilds;
TArray<FStrateDecoration> FixtureEntries;
TArray<int32> FixtureBiomes;
const int32 BiomeCount = CurrentCtx.BiomeCtx.IsValid() ? CurrentCtx.BiomeCtx.Biomes.Num() : 0;
for (int32 Biome=0;Biome<FMath::Max(1,BiomeCount);++Biome)
for (int32 Wet=0;Wet<2;++Wet)
{
FStrateDecoration Entry;
Entry.Profile.ActorClass=AActor::StaticClass();
Entry.Profile.InstancedMesh=FixtureMesh;
Entry.Profile.SurfacePlacement=ESurfaceType::Floor;
Entry.Profile.bPlaceBelowWater=Wet!=0;
Entry.SpawnDensity=1.0f;
Entry.MaxPerChunk=64;
FixtureEntries.Add(Entry);
FixtureBiomes.Add(BiomeCount ? Biome : -1);
}
TArray<double> PopulatedTimes, FullPopulatedTimes;
for (int32 Sample=0;Sample<SampleCellCount;++Sample)
{
const FIntPoint Cell(PlayerCell.X+Sample%GridSide-GridSide/2,
PlayerCell.Y+Sample/GridSide-GridSide/2);
TArray<FDecoSpawn> FullSpawns, Spawns;
const double FullStart=FPlatformTime::Seconds();
BuildCellSpawns(Generator,OwnerXf,Cell,CurrentCtx,FixtureEntries,FixtureBiomes,
static_cast<uint32>(Seed),4,static_cast<float>(Step),MaxCrossings,ColumnDepth,
FullSpawns,nullptr,false);
const double FullElapsed=(FPlatformTime::Seconds()-FullStart)*1000.0;
const double CandidateStart=FPlatformTime::Seconds();
BuildCellSpawns(Generator,OwnerXf,Cell,CurrentCtx,FixtureEntries,FixtureBiomes,
static_cast<uint32>(Seed),4,static_cast<float>(Step),MaxCrossings,ColumnDepth,Spawns);
const double Elapsed=(FPlatformTime::Seconds()-CandidateStart)*1000.0;
OutAudit.PopulatedBatchMilliseconds+=Elapsed;
OutAudit.FullScanPopulatedBatchMilliseconds+=FullElapsed;
if (Spawns.Num()!=FullSpawns.Num())
{ OutError=TEXT("Populated fixture count differs from full scan."); return false; }
const double MergeStart=FPlatformTime::Seconds();
if (FixtureMesh && !Spawns.IsEmpty())
{
auto& Build=FixtureBuilds.FindOrAdd(CellToRegion(Cell,RegionSize()));
auto& Bucket=Build.MeshBuckets.FindOrAdd(FixtureMesh);
for (const auto& Spawn : Spawns)
{
Bucket.Profile=FixtureEntries[Spawn.EntryIdx].Profile;
Bucket.Xforms.Add(Spawn.Xf);
}
}
OutAudit.PopulatedMergeMilliseconds+=(FPlatformTime::Seconds()-MergeStart)*1000.0;
for (int32 Index=0;Index<Spawns.Num();++Index)
{
if (!Spawns[Index].Xf.Equals(FullSpawns[Index].Xf,0.0f))
{ OutError=TEXT("Populated fixture anchor differs from full scan."); return false; }
++OutAudit.ExactPlacementComparisons;
const FVector Local=OwnerXf.InverseTransformPosition(Spawns[Index].Xf.GetLocation());
float Density=0.0f;
if (CurrentCtx.bNativeFloatingIslands && CurrentCtx.NativeIslands.IsValid()
&& CurrentCtx.NativeIslands->GetDensity(CurrentCtx.StrateIndex,
Local.X/CurrentCtx.VoxelSizeCm,Local.Y/CurrentCtx.VoxelSizeCm,
Local.Z/CurrentCtx.VoxelSizeCm,Density))
{
OutAudit.MaximumDensityResidual=FMath::Max(OutAudit.MaximumDensityResidual,
FMath::Abs(static_cast<double>(Density)));
++OutAudit.AnchorChecks;
if (FMath::Abs(Density)>0.25f)
{ OutError=TEXT("Populated fixture is not anchored on the canonical field."); return false; }
}
}
if (!Spawns.IsEmpty())
{
++OutAudit.PopulatedCells;
OutAudit.PopulatedPlacements+=Spawns.Num();
PopulatedTimes.Add(Elapsed);
FullPopulatedTimes.Add(FullElapsed);
}
}
if ((CurrentCtx.bNativeSurface||CurrentCtx.bNativeFloatingIslands) && OutAudit.PopulatedPlacements==0)
{ OutError=TEXT("Populated content fixture produced no placements."); return false; }
if (FixtureMesh && OutAudit.PopulatedPlacements>0)
{
// Exercise actual region application, then close this isolated fixture immediately.
// Appliquer une vraie region puis detruire immediatement cette fixture isolee.
FDecoGrid FixtureGrid;
const double ApplyStart=FPlatformTime::Seconds();
// Fixture footprints are isolated from live landmark suppression. / Isoler les empreintes de fixture.
for (auto& Pair : FixtureBuilds) ApplyRegion(FixtureGrid,Pair.Key,Pair.Value,false);
OutAudit.PopulatedApplyMilliseconds=(FPlatformTime::Seconds()-ApplyStart)*1000.0;
for (const auto& Pair : FixtureGrid.Regions)
for (const auto& WeakHISM : Pair.Value.Instances)
if (const auto* HISM=WeakHISM.Get()) OutAudit.AppliedInstances+=HISM->GetInstanceCount();
for (const auto& Pair : FixtureBuilds) ClearDecorationRegion(FixtureGrid,Pair.Key);
if (OutAudit.AppliedInstances!=OutAudit.PopulatedPlacements)
{
OutError=FString::Printf(TEXT("Populated HISM fixture applied %d of %d placements (%d regions, mesh %s)."),
OutAudit.AppliedInstances,OutAudit.PopulatedPlacements,FixtureBuilds.Num(),*FixtureMesh->GetPathName());
return false;
}
}
MedianAndP95(MoveTemp(PopulatedTimes),UnusedMedian,OutAudit.PopulatedCellP95Milliseconds);
MedianAndP95(MoveTemp(FullPopulatedTimes),UnusedMedian,OutAudit.FullScanPopulatedCellP95Milliseconds);
// The game-thread update cost is sampled with task dispatch disabled; worker-cell cost is reported // The game-thread update cost is sampled with task dispatch disabled; worker-cell cost is reported
// separately above, so the frame figure is not polluted by waiting for asynchronous work. // separately above, so the frame figure is not polluted by waiting for asynchronous work.
UpdateDecorations(PlayerWorldPos); UpdateDecorations(PlayerWorldPos);
@@ -1092,7 +1268,7 @@ void UVoxelContentManager::BuildCellSpawns(const UVoxelGenerator* Gen, const FTr
const TArray<int32>& EntryBiome, uint32 InSeed, const TArray<int32>& EntryBiome, uint32 InSeed,
int32 Spacing, float Step, int32 MaxCrossings, float ColumnDepth, int32 Spacing, float Step, int32 MaxCrossings, float ColumnDepth,
TArray<FDecoSpawn>& OutSpawns, TArray<FDecoSpawn>& OutSpawns,
FPlacementBuildCounters* OutCounters) FPlacementBuildCounters* OutCounters, bool bUseFeatureQueries)
{ {
if (OutCounters != nullptr) *OutCounters = FPlacementBuildCounters(); if (OutCounters != nullptr) *OutCounters = FPlacementBuildCounters();
if (!Gen || Entries.Num() == 0) return; if (!Gen || Entries.Num() == 0) return;
@@ -1101,6 +1277,14 @@ void UVoxelContentManager::BuildCellSpawns(const UVoxelGenerator* Gen, const FTr
const int32 CellOriginVX = Cell.X * CHUNK_SIZE; const int32 CellOriginVX = Cell.X * CHUNK_SIZE;
const int32 CellOriginVY = Cell.Y * CHUNK_SIZE; const int32 CellOriginVY = Cell.Y * CHUNK_SIZE;
const auto FeatureQueries = bUseFeatureQueries ? VF_ContentFeatureQueries(
Ctx.bNativeSurface ? Ctx.NativeSurface.Get() : nullptr,
Ctx.bNativeFloatingIslands ? Ctx.NativeIslands.Get() : nullptr,Ctx.StrateIndex,
CellOriginVX-1.0f,CellOriginVY-1.0f,Ctx.BottomVoxelZ-1.0f,
CellOriginVX+CHUNK_SIZE+1.0f,CellOriginVY+CHUNK_SIZE+1.0f,Ctx.TopVoxelZ+1.0f)
: std::vector<VoxelForgeCore::WorldAssemblyFeatureQueryV1>{};
const VoxelForgeCore::ScopedWorldAssemblyFeatureQueriesV1 FeatureScope(FeatureQueries);
TArray<int32> EntryCount; EntryCount.Init(0, Entries.Num()); TArray<int32> EntryCount; EntryCount.Init(0, Entries.Num());
int32 TotalActors = 0; int32 TotalActors = 0;
@@ -1437,12 +1621,17 @@ void UVoxelContentManager::BuildCellSpawns(const UVoxelGenerator* Gen, const FTr
} }
// NON-SURFACE archetypes (caves/shafts/islands): ray-march the density column for crossings. // NON-SURFACE archetypes (caves/shafts/islands): ray-march the density column for crossings.
float PrevD = D(VX, VY, Ctx.TopVoxelZ); float MarchTop = Ctx.TopVoxelZ, MarchBottom = Ctx.BottomVoxelZ;
if (bUseFeatureQueries && Ctx.bNativeFloatingIslands && Ctx.NativeIslands.IsValid())
if (const auto* Entry = Ctx.NativeIslands->FindField(Ctx.StrateIndex))
if (!VF_ConstrainContentMarch(Entry->SolidSupportBounds,VX,VY,Step,MarchTop,MarchBottom))
continue;
float PrevD = D(VX, VY, MarchTop);
int32 Crossings = 0; int32 Crossings = 0;
bool bSeenAir = (PrevD >= 0.0f); // GetDensityAt >= 0 == air bool bSeenAir = (PrevD >= 0.0f); // GetDensityAt >= 0 == air
float SolidRun = 0.0f; // contiguous solid voxels since the last open air float SolidRun = 0.0f; // contiguous solid voxels since the last open air
for (float Z = Ctx.TopVoxelZ - Step; Z >= Ctx.BottomVoxelZ && Crossings < MaxCrossings; Z -= Step) for (float Z = MarchTop - Step; Z >= MarchBottom && Crossings < MaxCrossings; Z -= Step)
{ {
const float Dz = D(VX, VY, Z); const float Dz = D(VX, VY, Z);
if ((PrevD >= 0.0f) != (Dz >= 0.0f)) // straddles IsoLevel 0 (air ↔ solid) if ((PrevD >= 0.0f) != (Dz >= 0.0f)) // straddles IsoLevel 0 (air ↔ solid)
@@ -1615,7 +1804,8 @@ void UVoxelContentManager::MarkCellDone(FDecoGrid& G, const FIntPoint& Region, c
// Build the region's components: one HISM per mesh (all cells merged → one batched AddInstances), actors // Build the region's components: one HISM per mesh (all cells merged → one batched AddInstances), actors
// spawned inline. Moves the region into G.Regions; the build is removed by the caller. // spawned inline. Moves the region into G.Regions; the build is removed by the caller.
void UVoxelContentManager::ApplyRegion(FDecoGrid& G, const FIntPoint& Region, FDecoRegionBuild& Build) void UVoxelContentManager::ApplyRegion(FDecoGrid& G, const FIntPoint& Region, FDecoRegionBuild& Build,
bool bApplyLandmarkFootprints)
{ {
TRACE_CPUPROFILER_EVENT_SCOPE(VoxelForge_DecoApply); // total game-thread cost to apply one region TRACE_CPUPROFILER_EVENT_SCOPE(VoxelForge_DecoApply); // total game-thread cost to apply one region
@@ -1714,7 +1904,7 @@ void UVoxelContentManager::ApplyRegion(FDecoGrid& G, const FIntPoint& Region, FD
// but this region just streamed in FRESH → re-clear under any loaded suppressing landmark overlapping it // but this region just streamed in FRESH → re-clear under any loaded suppressing landmark overlapping it
// (the per-HISM spatial query early-outs for the non-overlapping majority). Keeps temple floors clear as // (the per-HISM spatial query early-outs for the non-overlapping majority). Keeps temple floors clear as
// you leave and return. // you leave and return.
if (Content.Instances.Num() > 0) if (bApplyLandmarkFootprints && Content.Instances.Num() > 0)
{ {
for (const TPair<FIntVector, FLandmarkInstance>& LP : LandmarkInstances) for (const TPair<FIntVector, FLandmarkInstance>& LP : LandmarkInstances)
{ {
@@ -1781,6 +1971,11 @@ bool UVoxelContentManager::FindLandmarkColumn(const UVoxelGenerator* Gen, const
{ {
if (!Gen) return false; if (!Gen) return false;
const auto FeatureQueries = VF_ContentFeatureQueries(
Ctx.bNativeSurface ? Ctx.NativeSurface.Get() : nullptr,
Ctx.bNativeFloatingIslands ? Ctx.NativeIslands.Get() : nullptr,Ctx.StrateIndex,
VX-1.0f,VY-1.0f,Ctx.BottomVoxelZ-1.0f,VX+1.0f,VY+1.0f,Ctx.TopVoxelZ+1.0f);
const VoxelForgeCore::ScopedWorldAssemblyFeatureQueriesV1 FeatureScope(FeatureQueries);
const auto Density = [&](float SampleX, float SampleY, float SampleZ) const auto Density = [&](float SampleX, float SampleY, float SampleZ)
{ {
if (Ctx.bNativeFloatingIslands && Ctx.NativeIslands.IsValid()) if (Ctx.bNativeFloatingIslands && Ctx.NativeIslands.IsValid())
@@ -1852,10 +2047,15 @@ bool UVoxelContentManager::FindLandmarkColumn(const UVoxelGenerator* Gen, const
// Bounded by ColDepth like the decoration march (this runs SYNCHRONOUSLY on the game thread): // Bounded by ColDepth like the decoration march (this runs SYNCHRONOUSLY on the game thread):
// once past open air, a solid run longer than ColDepth means bedrock down to the strate floor — // once past open air, a solid run longer than ColDepth means bedrock down to the strate floor —
// stop instead of paying GetDensityAt across the whole remaining band. // stop instead of paying GetDensityAt across the whole remaining band.
float PrevD = Density(VX, VY, Ctx.TopVoxelZ); float MarchTop = Ctx.TopVoxelZ, MarchBottom = Ctx.BottomVoxelZ;
if (Ctx.bNativeFloatingIslands && Ctx.NativeIslands.IsValid())
if (const auto* Entry = Ctx.NativeIslands->FindField(Ctx.StrateIndex))
if (!VF_ConstrainContentMarch(Entry->SolidSupportBounds,VX,VY,Step,MarchTop,MarchBottom))
return false;
float PrevD = Density(VX, VY, MarchTop);
bool bSeenAir = (PrevD >= 0.0f); bool bSeenAir = (PrevD >= 0.0f);
float SolidRun = 0.0f; float SolidRun = 0.0f;
for (float Z = Ctx.TopVoxelZ - Step; Z >= Ctx.BottomVoxelZ; Z -= Step) for (float Z = MarchTop - Step; Z >= MarchBottom; Z -= Step)
{ {
const float Dz = Density(VX, VY, Z); const float Dz = Density(VX, VY, Z);
if ((PrevD >= 0.0f) != (Dz >= 0.0f)) // air ↔ solid crossing if ((PrevD >= 0.0f) != (Dz >= 0.0f)) // air ↔ solid crossing
+16 -2
View File
@@ -137,6 +137,19 @@ public:
double MaximumDensityResidual = 0.0; double MaximumDensityResidual = 0.0;
double DensityQueriesPerCell = 0.0; double DensityQueriesPerCell = 0.0;
double SurfaceQueriesPerCell = 0.0; double SurfaceQueriesPerCell = 0.0;
double FullScanCellP95Milliseconds = 0.0;
double ActiveBatchMilliseconds = 0.0;
double FullScanActiveBatchMilliseconds = 0.0;
double PopulatedBatchMilliseconds = 0.0;
double FullScanPopulatedBatchMilliseconds = 0.0;
double PopulatedMergeMilliseconds = 0.0;
double PopulatedApplyMilliseconds = 0.0;
int32 AppliedInstances = 0;
double PopulatedCellP95Milliseconds = 0.0;
double FullScanPopulatedCellP95Milliseconds = 0.0;
int32 PopulatedPlacements = 0;
int32 PopulatedCells = 0;
int32 ExactPlacementComparisons = 0;
double CellMedianMilliseconds = 0.0; double CellMedianMilliseconds = 0.0;
double CellP95Milliseconds = 0.0; double CellP95Milliseconds = 0.0;
double UpdateMedianMilliseconds = 0.0; double UpdateMedianMilliseconds = 0.0;
@@ -338,7 +351,7 @@ private:
const TArray<int32>& EntryBiome, uint32 InSeed, const TArray<int32>& EntryBiome, uint32 InSeed,
int32 Spacing, float Step, int32 MaxCrossings, float ColumnDepth, int32 Spacing, float Step, int32 MaxCrossings, float ColumnDepth,
TArray<FDecoSpawn>& OutSpawns, TArray<FDecoSpawn>& OutSpawns,
FPlacementBuildCounters* OutCounters = nullptr); FPlacementBuildCounters* OutCounters = nullptr, bool bUseFeatureQueries = true);
// Each step operates on ONE grid (G = NearGrid or FarGrid). LaunchDecoTasks throttles against the // Each step operates on ONE grid (G = NearGrid or FarGrid). LaunchDecoTasks throttles against the
// COMBINED in-flight count (OtherInFlight = the other grid's in-flight cells) so the two grids share // COMBINED in-flight count (OtherInFlight = the other grid's in-flight cells) so the two grids share
@@ -348,7 +361,8 @@ private:
void ProcessDecoResults(const FIntPoint& PlayerCell); void ProcessDecoResults(const FIntPoint& PlayerCell);
void MergeCellResult(FDecoGrid& G, const FDecoCellResult& Result); // fold one cell's spawns into its region build void MergeCellResult(FDecoGrid& G, const FDecoCellResult& Result); // fold one cell's spawns into its region build
void MarkCellDone(FDecoGrid& G, const FIntPoint& Region, const FIntPoint& Cell, uint32 BuildId); // idempotent per-cell accounting void MarkCellDone(FDecoGrid& G, const FIntPoint& Region, const FIntPoint& Cell, uint32 BuildId); // idempotent per-cell accounting
void ApplyRegion(FDecoGrid& G, const FIntPoint& Region, FDecoRegionBuild& Build); void ApplyRegion(FDecoGrid& G, const FIntPoint& Region, FDecoRegionBuild& Build,
bool bApplyLandmarkFootprints = true);
void RebuildDesiredCells(FDecoGrid& G, const FIntPoint& PlayerCell); void RebuildDesiredCells(FDecoGrid& G, const FIntPoint& PlayerCell);
void ClearDecorationRegion(FDecoGrid& G, const FIntPoint& Region); void ClearDecorationRegion(FDecoGrid& G, const FIntPoint& Region);
void ClearAllDecorations(); void ClearAllDecorations();
@@ -15147,6 +15147,53 @@ bool RunCoreOffActorTest(const FExploreArguments& Arguments, FString& OutError)
return true; return true;
} }
// Paired engine timings include preparation and derived shells, with canonical output hashes.
// Mesures moteur appariees : preparation et coques derivees incluses, avec hashes canoniques.
bool RunCoreSharedFeatureCostAudit(VoxelForgeCore::World& World,
const std::vector<VoxelForgeCore::TileKey>& Keys,const TCHAR* Label,FString& OutError)
{
for (const int32 LOD : {0,1,7,8})
{
TArray<double> FullTimes,CandidateTimes,FullShellTimes,CandidateShellTimes,ListTimes;
for (const auto& Key : Keys)
{
if (Key.lod!=LOD || FullTimes.Num()>=8) continue;
VoxelForgeCore::TileGenerationOptions Options;
Options.hashDensity=true;
Options.useAssemblyFeatureQueries=false;
std::string Error;
// Warm both routes before measuring: column/page caches must not favor the second route.
// Chauffer les deux parcours : les caches ne doivent pas favoriser le second.
if (!World.generateTile(Key,Options,Error)) return false;
Options.useAssemblyFeatureQueries=true;
if (!World.generateTile(Key,Options,Error)) return false;
Options.useAssemblyFeatureQueries=false;
const double FullStart=FPlatformTime::Seconds();
auto Full=World.generateTile(Key,Options,Error);
FullTimes.Add((FPlatformTime::Seconds()-FullStart)*1000.0);
Options.useAssemblyFeatureQueries=true;
const double CandidateStart=FPlatformTime::Seconds();
auto Candidate=World.generateTile(Key,Options,Error);
CandidateTimes.Add((FPlatformTime::Seconds()-CandidateStart)*1000.0);
if (!Full||!Candidate||Full->densityHash!=Candidate->densityHash
||Full->meshHash!=Candidate->meshHash||Full->payloadHash!=Candidate->payloadHash
||Full->topologyHash!=Candidate->topologyHash)
{ OutError=TEXT("Shared feature full/candidate tile hashes differ."); return false; }
FullShellTimes.Add(Full->diagnostics.farShellMilliseconds);
CandidateShellTimes.Add(Candidate->diagnostics.farShellMilliseconds);
ListTimes.Add(Candidate->diagnostics.featureListMilliseconds);
}
const auto P95=[](TArray<double> Values)
{ Values.Sort(); return Values.IsEmpty() ? 0.0 : Values[FMath::Clamp(
FMath::CeilToInt(Values.Num()*0.95f)-1,0,Values.Num()-1)]; };
UE_LOG(LogTemp,Display,
TEXT("[VoxelForgeSharedFeatureCost] label=%s lod=%d tiles=%d hashes_identical=1 full_p95_ms=%.5f candidate_p95_ms=%.5f list_p95_ms=%.5f full_shell_p95_ms=%.5f candidate_shell_p95_ms=%.5f"),
Label,LOD,FullTimes.Num(),P95(FullTimes),P95(CandidateTimes),P95(ListTimes),
P95(FullShellTimes),P95(CandidateShellTimes));
}
return true;
}
bool RunCoreContentPlacementCostAudit(AVoxelWorld* Actor, const FVector& CoreLocalProbeCm, bool RunCoreContentPlacementCostAudit(AVoxelWorld* Actor, const FVector& CoreLocalProbeCm,
const TCHAR* Label, FString& OutError); const TCHAR* Label, FString& OutError);
@@ -15382,6 +15429,7 @@ bool RunCoreFloatingIslandActorIntegrationTest(
return false; return false;
} }
ReferenceWorld.retainTileRegions(Desired); ReferenceWorld.retainTileRegions(Desired);
if (!RunCoreSharedFeatureCostAudit(ReferenceWorld,Desired,TEXT("islands"),OutError)) return false;
VoxelForgeCore::TileGenerationOptions Options; VoxelForgeCore::TileGenerationOptions Options;
Options.hashDensity = true; Options.hashDensity = true;
std::vector<FStandaloneCoreAdapterTileHashes> AggregateRows; std::vector<FStandaloneCoreAdapterTileHashes> AggregateRows;
@@ -16272,6 +16320,15 @@ bool RunCoreContentPlacementCostAudit(AVoxelWorld* Actor, const FVector& CoreLoc
const float CoreVoxelSizeCm = Actor->CoreVoxelSizeCm; const float CoreVoxelSizeCm = Actor->CoreVoxelSizeCm;
const FTransform OwnerXf = Actor->GetActorTransform(); const FTransform OwnerXf = Actor->GetActorTransform();
FString AssemblyJson, AssemblyHash;
if (Actor->GetVoxelForgeCoreWorldAssembly(AssemblyJson,AssemblyHash))
{
const FString ExportDir=FPaths::Combine(FPaths::ProjectSavedDir(),TEXT("CoreWorldValidation/Output"));
IFileManager::Get().MakeDirectory(*ExportDir,true);
FFileHelper::SaveStringToFile(AssemblyJson,
*FPaths::Combine(ExportDir,FString::Printf(TEXT("s2_assembly_%s.json"),Label)));
}
UVoxelContentManager::FDecorationPlacementCostAudit CoreAudit; UVoxelContentManager::FDecorationPlacementCostAudit CoreAudit;
Actor->bUseVoxelForgeCore = true; Actor->bUseVoxelForgeCore = true;
const FVector CorePlayerWorld = OwnerXf.TransformPosition(CoreLocalProbeCm); const FVector CorePlayerWorld = OwnerXf.TransformPosition(CoreLocalProbeCm);
@@ -16319,6 +16376,20 @@ bool RunCoreContentPlacementCostAudit(AVoxelWorld* Actor, const FVector& CoreLoc
CoreAudit.CellP95Milliseconds, CoreAudit.UpdateMedianMilliseconds, CoreAudit.CellP95Milliseconds, CoreAudit.UpdateMedianMilliseconds,
CoreAudit.UpdateP95Milliseconds, CoreAudit.Placements, CoreAudit.AnchorChecks, CoreAudit.UpdateP95Milliseconds, CoreAudit.Placements, CoreAudit.AnchorChecks,
CoreAudit.MaximumAnchorErrorCm, CoreAudit.MaximumDensityResidual); CoreAudit.MaximumAnchorErrorCm, CoreAudit.MaximumDensityResidual);
UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeContentCostS2] label=%s authored_full_p95_ms=%.5f authored_candidate_p95_ms=%.5f authored_active_batch_full_ms=%.5f authored_active_batch_candidate_ms=%.5f populated_cells=%d populated_placements=%d populated_full_p95_ms=%.5f populated_candidate_p95_ms=%.5f exact_transform_comparisons=%d anchor_checks=%d max_density_residual=%.6f"),
Label,CoreAudit.FullScanCellP95Milliseconds,CoreAudit.CellP95Milliseconds,
CoreAudit.FullScanActiveBatchMilliseconds,CoreAudit.ActiveBatchMilliseconds,
CoreAudit.PopulatedCells,CoreAudit.PopulatedPlacements,
CoreAudit.FullScanPopulatedCellP95Milliseconds,CoreAudit.PopulatedCellP95Milliseconds,
CoreAudit.ExactPlacementComparisons,CoreAudit.AnchorChecks,CoreAudit.MaximumDensityResidual);
UE_LOG(LogTemp,Display,
TEXT("[VoxelForgeContentActiveS2] label=%s cells=%d placements=%d full_batch_ms=%.5f candidate_batch_ms=%.5f merge_ms=%.5f apply_ms=%.5f applied_instances=%d full_total_cpu_ms=%.5f candidate_total_cpu_ms=%.5f rhi=null"),
Label,CoreAudit.SampledCells,CoreAudit.PopulatedPlacements,
CoreAudit.FullScanPopulatedBatchMilliseconds,CoreAudit.PopulatedBatchMilliseconds,
CoreAudit.PopulatedMergeMilliseconds,CoreAudit.PopulatedApplyMilliseconds,CoreAudit.AppliedInstances,
CoreAudit.FullScanPopulatedBatchMilliseconds+CoreAudit.PopulatedMergeMilliseconds+CoreAudit.PopulatedApplyMilliseconds,
CoreAudit.PopulatedBatchMilliseconds+CoreAudit.PopulatedMergeMilliseconds+CoreAudit.PopulatedApplyMilliseconds);
UE_LOG(LogTemp, Display, UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeContentCost] label=%s route=legacy voxel_cm=%.3f tile_cell_cm=%.3f authored_entries=%d synthetic=%d cells=%d columns_per_cell=%d density_queries_per_cell=%.2f surface_queries_per_cell=%.2f cell_median_ms=%.5f cell_p95_ms=%.5f update_median_ms=%.5f update_p95_ms=%.5f placements=%d anchor_checks=%d max_anchor_error_cm=%.6f max_density_residual=%.6f"), TEXT("[VoxelForgeContentCost] label=%s route=legacy voxel_cm=%.3f tile_cell_cm=%.3f authored_entries=%d synthetic=%d cells=%d columns_per_cell=%d density_queries_per_cell=%.2f surface_queries_per_cell=%.2f cell_median_ms=%.5f cell_p95_ms=%.5f update_median_ms=%.5f update_p95_ms=%.5f placements=%d anchor_checks=%d max_anchor_error_cm=%.6f max_density_residual=%.6f"),
Label, VOXEL_SIZE, CHUNK_SIZE * VOXEL_SIZE, Label, VOXEL_SIZE, CHUNK_SIZE * VOXEL_SIZE,
@@ -16767,6 +16838,12 @@ bool RunCoreWorldActorIntegrationTest(const FExploreArguments& Arguments, FStrin
return true; return true;
} }
if (!RunCoreSharedFeatureCostAudit(ReferenceWorld,Desired,TEXT("surface"),OutError)) return false;
if (FParse::Param(FCommandLine::Get(),TEXT("corefeatureauditonly")))
{
UE_LOG(LogTemp,Display,TEXT("[VoxelForgeSharedFeatureCost] pass complete=1 warmed_routes=1"));
return true;
}
VoxelForgeCore::TileGenerationOptions Options; VoxelForgeCore::TileGenerationOptions Options;
Options.hashDensity = true; Options.hashDensity = true;
if (Arguments.CoreHybridMesherVersion == 2) if (Arguments.CoreHybridMesherVersion == 2)
@@ -56,6 +56,18 @@ namespace VoxelForgeCore
const std::array<std::int64_t, 3>& tileMinimumSubvoxels, const std::array<std::int64_t, 3>& tileMinimumSubvoxels,
const std::array<std::int64_t, 3>& tileMaximumSubvoxels); const std::array<std::int64_t, 3>& tileMaximumSubvoxels);
struct WorldAssemblyFeatureColumnRangeV1
{
bool known = false;
bool empty = false;
std::int64_t minimumZSubvoxels = 0, maximumZSubvoxels = 0;
};
// Only families whose base is air may use this solid-support union to shorten a march.
// Reserve aux familles a fond d'air : union conservative du support solide de la colonne.
VFCORE_API WorldAssemblyFeatureColumnRangeV1 BuildWorldAssemblyFeatureColumnRangeV1(
std::span<const WorldAssemblyFeatureBoundsV1> solidSupportBounds,
std::int64_t xSubvoxels, std::int64_t ySubvoxels);
struct VFCORE_API TileKeyHash struct VFCORE_API TileKeyHash
{ {
std::size_t operator()(const TileKey& key) const noexcept; std::size_t operator()(const TileKey& key) const noexcept;
@@ -114,6 +126,8 @@ namespace VoxelForgeCore
double classificationMilliseconds = 0.0; double classificationMilliseconds = 0.0;
double columnComputeMilliseconds = 0.0; double columnComputeMilliseconds = 0.0;
double meshingMilliseconds = 0.0; double meshingMilliseconds = 0.0;
double featureListMilliseconds = 0.0;
double farShellMilliseconds = 0.0;
bool tileWasClassified = false; bool tileWasClassified = false;
bool meshingSkipped = false; bool meshingSkipped = false;
bool featureRefinedResolution = false; bool featureRefinedResolution = false;
@@ -386,25 +400,60 @@ namespace VoxelForgeCore
std::uint64_t contentHash = 0; std::uint64_t contentHash = 0;
}; };
struct FloatingIslandFeatureGroupBoundsV1 struct WorldAssemblyFeatureGroupBoundsV1
{ {
std::array<std::int64_t, 3> minimumSubvoxels{}; std::array<std::int64_t, 3> minimumSubvoxels{};
std::array<std::int64_t, 3> maximumSubvoxels{}; std::array<std::int64_t, 3> maximumSubvoxels{};
}; };
struct FloatingIslandTileFeatureListV1 struct WorldAssemblyFeatureTileListV1
{ {
// Flat stable-order IDs for island and connector features that touch this tile. // Stable recipe-order indices for features that touch this query domain.
// Identifiants ordonnes des features qui touchent ce carreau. // Indices dans l'ordre de recette des features touchant ce domaine.
std::vector<std::uint32_t> featureIndices; std::vector<std::uint32_t> featureIndices;
// Cheap conservative group bounds preserve exact positive SDF distances while distant // Optional ordered-union group certificates preserve exact positive density values.
// feature evaluators are skipped. Bornes legeres pour garder les distances SDF exactes. // Certificats optionnels de groupes d'union pour conserver les valeurs positives exactes.
std::vector<FloatingIslandFeatureGroupBoundsV1> groupBounds; std::vector<WorldAssemblyFeatureGroupBoundsV1> groupBounds;
// Groups are ordered by a tile-wide conservative distance lower bound. Samples stop at // Groups are ordered by a tile-wide conservative distance lower bound. Samples stop at
// the first group that cannot beat their current density. Ordre par distance minimale. // the first group that cannot beat their current density. Ordre par distance minimale.
std::vector<std::uint32_t> groupSearchOrder; std::vector<std::uint32_t> groupSearchOrder;
std::vector<std::int64_t> groupDistanceLowerBoundsSubvoxels; std::vector<std::int64_t> groupDistanceLowerBoundsSubvoxels;
bool groupSearchComplete = true; bool groupSearchComplete = true;
// Performance hint only: the shared engine may prefer the exact full scan on small plans.
// Indication de cout seulement : le moteur peut preferer le parcours complet exact.
bool candidateEvaluationUseful = true;
// Exact-domain guard: outside queries always fall back to the canonical full scan.
// Hors du domaine prepare, revenir au parcours complet canonique.
std::array<std::int64_t, 3> minimumSubvoxels{};
std::array<std::int64_t, 3> maximumSubvoxels{};
};
using FloatingIslandTileFeatureListV1 = WorldAssemblyFeatureTileListV1;
using FloatingIslandFeatureGroupBoundsV1 = WorldAssemblyFeatureGroupBoundsV1;
struct WorldAssemblyFeatureQueryV1
{
const void* recipe = nullptr;
const void* plan = nullptr;
WorldAssemblyFeatureTileListV1 features;
};
// One worker-local, nestable scope for meshing, shells and placement in every family.
// Une portee locale au worker, emboitable, pour toutes les familles et tous les consommateurs.
class VFCORE_API ScopedWorldAssemblyFeatureQueriesV1
{
public:
explicit ScopedWorldAssemblyFeatureQueriesV1(std::span<const WorldAssemblyFeatureQueryV1> queries,
bool forceCandidatesForValidation = false) noexcept;
~ScopedWorldAssemblyFeatureQueriesV1();
ScopedWorldAssemblyFeatureQueriesV1(const ScopedWorldAssemblyFeatureQueriesV1&) = delete;
ScopedWorldAssemblyFeatureQueriesV1& operator=(const ScopedWorldAssemblyFeatureQueriesV1&) = delete;
static const WorldAssemblyFeatureTileListV1* find(const void* recipe, const void* plan,
std::int64_t x, std::int64_t y, std::int64_t z, bool columnOnly = false) noexcept;
private:
std::span<const WorldAssemblyFeatureQueryV1> queries_;
bool forceCandidates_ = false;
const ScopedWorldAssemblyFeatureQueriesV1* previous_ = nullptr;
}; };
struct VFCORE_API FloatingIslandManifestV1 struct VFCORE_API FloatingIslandManifestV1
@@ -510,6 +559,7 @@ namespace VoxelForgeCore
FloatingIslandTileFeatureListV1 buildFeatureTileList( FloatingIslandTileFeatureListV1 buildFeatureTileList(
const std::array<std::int64_t, 3>& tileMinimumSubvoxels, const std::array<std::int64_t, 3>& tileMinimumSubvoxels,
const std::array<std::int64_t, 3>& tileMaximumSubvoxels) const; const std::array<std::int64_t, 3>& tileMaximumSubvoxels) const;
std::vector<WorldAssemblyFeatureBoundsV1> solidSupportBounds() const;
std::int64_t densitySubvoxelsForTile(std::int64_t xSubvoxels, std::int64_t densitySubvoxelsForTile(std::int64_t xSubvoxels,
std::int64_t ySubvoxels, std::int64_t zSubvoxels, std::int64_t ySubvoxels, std::int64_t zSubvoxels,
const FloatingIslandTileFeatureListV1& featureList) const noexcept; const FloatingIslandTileFeatureListV1& featureList) const noexcept;
@@ -601,6 +651,9 @@ namespace VoxelForgeCore
bool isValid() const noexcept; bool isValid() const noexcept;
HeightfieldPointSample point(std::int64_t xSubvoxels, std::int64_t ySubvoxels, HeightfieldPointSample point(std::int64_t xSubvoxels, std::int64_t ySubvoxels,
std::int64_t zSubvoxels) const noexcept; std::int64_t zSubvoxels) const noexcept;
WorldAssemblyFeatureTileListV1 buildFeatureTileList(
const std::array<std::int64_t, 3>& tileMinimumSubvoxels,
const std::array<std::int64_t, 3>& tileMaximumSubvoxels) const;
HeightfieldBoxClass classifyLatticeBox(std::int64_t minimumXSubvoxels, HeightfieldBoxClass classifyLatticeBox(std::int64_t minimumXSubvoxels,
std::int64_t minimumYSubvoxels, std::int64_t minimumZSubvoxels, std::int64_t minimumYSubvoxels, std::int64_t minimumZSubvoxels,
std::int64_t maximumXSubvoxels, std::int64_t maximumYSubvoxels, std::int64_t maximumXSubvoxels, std::int64_t maximumYSubvoxels,
@@ -928,6 +981,9 @@ namespace VoxelForgeCore
bool skipUv = false; bool skipUv = false;
bool skipColors = false; bool skipColors = false;
bool skipMaterialGroups = false; bool skipMaterialGroups = false;
// Validation control only; never participates in recipe/world identity.
// Controle de validation seulement, sans effet sur l'identite du monde.
bool useAssemblyFeatureQueries = true;
}; };
class VFCORE_API World class VFCORE_API World
+21 -5
View File
@@ -1645,18 +1645,18 @@ bool FloatingIslandFieldV1::isValid() const noexcept
std::int64_t FloatingIslandFieldV1::densitySubvoxels(std::int64_t x, std::int64_t FloatingIslandFieldV1::densitySubvoxels(std::int64_t x,
std::int64_t y, std::int64_t z) const noexcept std::int64_t y, std::int64_t z) const noexcept
{ {
// The selected evaluator validates once; do not scan the plan twice per scoped sample.
// L'evaluateur choisi valide une seule fois; ne pas reparcourir le plan a chaque echantillon.
if (const auto* list = ScopedWorldAssemblyFeatureQueriesV1::find(recipe_, plan_, x, y, z))
return densitySubvoxelsForTile(x, y, z, *list);
if (!isValid() || !floating_island_detail::ValidScalar(x) if (!isValid() || !floating_island_detail::ValidScalar(x)
|| !floating_island_detail::ValidScalar(y) || !floating_island_detail::ValidScalar(z)) || !floating_island_detail::ValidScalar(y) || !floating_island_detail::ValidScalar(z))
return floating_island_detail::MaxDensity; return floating_island_detail::MaxDensity;
return floating_island_detail::FieldDensity(*recipe_, *plan_, x, y, z); return floating_island_detail::FieldDensity(*recipe_, *plan_, x, y, z);
} }
FloatingIslandTileFeatureListV1 FloatingIslandFieldV1::buildFeatureTileList( std::vector<WorldAssemblyFeatureBoundsV1> FloatingIslandFieldV1::solidSupportBounds() const
const std::array<std::int64_t, 3>& tileMinimumSubvoxels,
const std::array<std::int64_t, 3>& tileMaximumSubvoxels) const
{ {
FloatingIslandTileFeatureListV1 result;
if (!isValid()) return result;
std::vector<WorldAssemblyFeatureBoundsV1> features; std::vector<WorldAssemblyFeatureBoundsV1> features;
features.reserve(plan_->islands.size() + plan_->clusterEdges.size()); features.reserve(plan_->islands.size() + plan_->clusterEdges.size());
for (const auto& island : plan_->islands) for (const auto& island : plan_->islands)
@@ -1666,6 +1666,22 @@ FloatingIslandTileFeatureListV1 FloatingIslandFieldV1::buildFeatureTileList(
features.push_back({edge.stableId, edge.influenceMinimumSubvoxels, features.push_back({edge.stableId, edge.influenceMinimumSubvoxels,
edge.influenceMaximumSubvoxels}); edge.influenceMaximumSubvoxels});
return features;
}
FloatingIslandTileFeatureListV1 FloatingIslandFieldV1::buildFeatureTileList(
const std::array<std::int64_t, 3>& tileMinimumSubvoxels,
const std::array<std::int64_t, 3>& tileMaximumSubvoxels) const
{
FloatingIslandTileFeatureListV1 result;
// The measured four-island plan is cheaper to scan; larger plans amortize group certificates.
// Le plan mesure de quatre iles coute moins en parcours complet; les grands plans amortissent les bornes.
result.candidateEvaluationUseful=plan_->islands.size()>4;
result.minimumSubvoxels = tileMinimumSubvoxels;
result.maximumSubvoxels = tileMaximumSubvoxels;
if (!isValid()) return result;
const auto features = solidSupportBounds();
result.featureIndices = BuildWorldAssemblyFeatureTileListV1(features, result.featureIndices = BuildWorldAssemblyFeatureTileListV1(features,
tileMinimumSubvoxels, tileMaximumSubvoxels); tileMinimumSubvoxels, tileMaximumSubvoxels);
const std::size_t islandCount = plan_->islands.size(); const std::size_t islandCount = plan_->islands.size();
+1 -50
View File
@@ -32,43 +32,6 @@
class FStandaloneShapeWorld class FStandaloneShapeWorld
{ {
public: public:
struct FNativeIslandTileFeatureContext
{
const VoxelForgeCore::FloatingIslandPlanV1* Plan = nullptr;
const VoxelForgeCore::FloatingIslandTileFeatureListV1* FeatureList = nullptr;
};
class FScopedNativeIslandTileFeatureContext
{
public:
FScopedNativeIslandTileFeatureContext(const VoxelForgeCore::FloatingIslandPlanV1* plan,
const VoxelForgeCore::FloatingIslandTileFeatureListV1* featureList)
{
Context.Plan = plan;
Context.FeatureList = featureList;
Previous = SetNativeIslandTileFeatureContext(plan && featureList ? &Context : nullptr);
}
~FScopedNativeIslandTileFeatureContext()
{ RestoreNativeIslandTileFeatureContext(Previous); }
FScopedNativeIslandTileFeatureContext(const FScopedNativeIslandTileFeatureContext&) = delete;
FScopedNativeIslandTileFeatureContext& operator=(const FScopedNativeIslandTileFeatureContext&) = delete;
private:
FNativeIslandTileFeatureContext Context{};
const FNativeIslandTileFeatureContext* Previous = nullptr;
};
static const FNativeIslandTileFeatureContext* SetNativeIslandTileFeatureContext(
const FNativeIslandTileFeatureContext* context)
{
auto& active = ActiveNativeIslandTileFeatureContext();
const auto* previous = active;
active = context;
return previous;
}
static void RestoreNativeIslandTileFeatureContext(const FNativeIslandTileFeatureContext* context)
{ ActiveNativeIslandTileFeatureContext() = context; }
static const FNativeIslandTileFeatureContext* GetNativeIslandTileFeatureContext()
{ return ActiveNativeIslandTileFeatureContext(); }
double GetCoreVoxelSizeCm() const double GetCoreVoxelSizeCm() const
{ {
return Owner == nullptr || Owner->CoreVoxelSizeMicrons == 0 return Owner == nullptr || Owner->CoreVoxelSizeMicrons == 0
@@ -1140,12 +1103,6 @@ public:
} }
private: private:
static const FNativeIslandTileFeatureContext*& ActiveNativeIslandTileFeatureContext()
{
static thread_local const FNativeIslandTileFeatureContext* Context = nullptr;
return Context;
}
static uint64 PackLatticeKey(int32 X,int32 Y,int32 Z) static uint64 PackLatticeKey(int32 X,int32 Y,int32 Z)
{ {
constexpr uint64 Mask=0x1FFFFFull; constexpr uint64 Mask=0x1FFFFFull;
@@ -1961,13 +1918,7 @@ private:
const VoxelForgeCore::FloatingIslandFieldV1 Field( const VoxelForgeCore::FloatingIslandFieldV1 Field(
Plan->manifest.recipe,Plan->manifest.plan); Plan->manifest.recipe,Plan->manifest.plan);
if(!Field.isValid())return false; if(!Field.isValid())return false;
std::int64_t Density=0; std::int64_t Density=Field.densitySubvoxels(ToFixed(X),ToFixed(Y),ToFixed(Z));
const auto* TileContext=GetNativeIslandTileFeatureContext();
if(TileContext&&TileContext->Plan==&Plan->manifest.plan&&TileContext->FeatureList)
Density=Field.densitySubvoxelsForTile(ToFixed(X),ToFixed(Y),ToFixed(Z),
*TileContext->FeatureList);
else
Density=Field.densitySubvoxels(ToFixed(X),ToFixed(Y),ToFixed(Z));
const std::int64_t Bottom=Plan->manifest.recipe.layoutMinimumZSubvoxels; const std::int64_t Bottom=Plan->manifest.recipe.layoutMinimumZSubvoxels;
const std::int64_t Top=Plan->manifest.recipe.layoutMaximumZSubvoxels; const std::int64_t Top=Plan->manifest.recipe.layoutMaximumZSubvoxels;
const std::int64_t Seal=Plan->manifest.recipe.boundarySealSubvoxels; const std::int64_t Seal=Plan->manifest.recipe.boundarySealSubvoxels;
+30 -47
View File
@@ -1154,68 +1154,51 @@ namespace VoxelForgeCore
} }
} }
} }
const WorldAssemblyFloatingIslandPlanV1* IslandTilePlan=nullptr; // Shared query domain includes the normal halo; all overlapping strate families participate.
FloatingIslandTileFeatureListV1 IslandTileFeatureList; // Domaine partage avec marge de normales; inclure toutes les familles des strates traversees.
if(bHasWorldAssembly) const std::int64_t queryScale=WorldAssemblySubvoxelsPerVoxel;
const std::array<std::int64_t,3> queryMinimum={
(static_cast<std::int64_t>(origin.X)-step)*queryScale,
(static_cast<std::int64_t>(origin.Y)-step)*queryScale,
(static_cast<std::int64_t>(origin.Z)-step)*queryScale};
const std::array<std::int64_t,3> queryMaximum={
(static_cast<std::int64_t>(origin.X)+extent+step)*queryScale,
(static_cast<std::int64_t>(origin.Y)+extent+step)*queryScale,
(static_cast<std::int64_t>(origin.Z)+extent+step)*queryScale};
const auto FeatureListStart=std::chrono::steady_clock::now();
std::vector<WorldAssemblyFeatureQueryV1> featureQueries;
if(bHasWorldAssembly&&options.useAssemblyFeatureQueries)
{ {
for(const auto& plan:impl_->worldAssembly->plan.strates)
{
const Heightfield2D field(impl_->worldAssembly->recipe,plan);
featureQueries.push_back({field.recipe(),field.plan(),
field.buildFeatureTileList(queryMinimum,queryMaximum)});
}
for(const auto& entry:impl_->worldAssembly->floatingIslands) for(const auto& entry:impl_->worldAssembly->floatingIslands)
{ {
const auto& scale=entry.manifest.recipe; const FloatingIslandFieldV1 field(entry.manifest.recipe,entry.manifest.plan);
const std::int64_t ownerToCoreNumerator=scale.ownerToCoreScaleNumerator; featureQueries.push_back({field.recipe(),field.plan(),
const std::int64_t ownerToCoreDenominator=scale.ownerToCoreScaleDenominator; field.buildFeatureTileList(queryMinimum,queryMaximum)});
if(ownerToCoreNumerator<=0||ownerToCoreDenominator<=0)continue;
const std::int64_t coreVoxelsPerOwnerChunk=static_cast<std::int64_t>(CHUNK_SIZE)
*ownerToCoreNumerator/ownerToCoreDenominator;
if(coreVoxelsPerOwnerChunk<=0)continue;
const std::int64_t stratumBottom=static_cast<std::int64_t>(entry.bottomChunkZ)
*coreVoxelsPerOwnerChunk;
const std::int64_t stratumTop=(static_cast<std::int64_t>(entry.topChunkZ)+1)
*coreVoxelsPerOwnerChunk;
if(static_cast<std::int64_t>(origin.Z)>stratumTop
||static_cast<std::int64_t>(origin.Z)+extent<stratumBottom)continue;
IslandTilePlan=&entry;
break;
}
if(IslandTilePlan)
{
const FloatingIslandFieldV1 field(IslandTilePlan->manifest.recipe,
IslandTilePlan->manifest.plan);
const std::int64_t scale=WorldAssemblySubvoxelsPerVoxel;
// Include the mesher's one-sample gradient halo on each face.
// Inclure la marge d'un echantillon du maillage sur chaque face.
const std::int64_t sampleHalo=static_cast<std::int64_t>(step)*scale;
const std::array<std::int64_t,3> tileMinimum={
static_cast<std::int64_t>(origin.X)*scale-sampleHalo,
static_cast<std::int64_t>(origin.Y)*scale-sampleHalo,
static_cast<std::int64_t>(origin.Z)*scale-sampleHalo};
const std::array<std::int64_t,3> tileMaximum={
(static_cast<std::int64_t>(origin.X)+extent)*scale+sampleHalo,
(static_cast<std::int64_t>(origin.Y)+extent)*scale+sampleHalo,
(static_cast<std::int64_t>(origin.Z)+extent)*scale+sampleHalo};
IslandTileFeatureList=field.buildFeatureTileList(tileMinimum,tileMaximum);
} }
} }
FVoxelMeshData mesh; out.diagnostics.featureListMilliseconds=std::chrono::duration<double,std::milli>(
if(IslandTilePlan) std::chrono::steady_clock::now()-FeatureListStart).count();
{ const ScopedWorldAssemblyFeatureQueriesV1 featureScope(featureQueries);
FStandaloneShapeWorld::FScopedNativeIslandTileFeatureContext tileFeatureContext( FVoxelMeshData mesh=mesher.GenerateMesh(origin,step,cells,nullptr,
&IslandTilePlan->manifest.plan,&IslandTileFeatureList);
mesh=mesher.GenerateMesh(origin,step,cells,nullptr,
MeshBandMinZVoxel,MeshBandMaxZVoxel,&sampleCount,nullptr,nullptr,
(options.hashDensity||options.captureDensitySamples)?&densityValues:nullptr);
}
else
mesh=mesher.GenerateMesh(origin,step,cells,nullptr,
MeshBandMinZVoxel,MeshBandMaxZVoxel,&sampleCount,nullptr,nullptr, MeshBandMinZVoxel,MeshBandMaxZVoxel,&sampleCount,nullptr,nullptr,
(options.hashDensity||options.captureDensitySamples)?&densityValues:nullptr); (options.hashDensity||options.captureDensitySamples)?&densityValues:nullptr);
const double MeshingMilliseconds=std::chrono::duration<double,std::milli>( const double MeshingMilliseconds=std::chrono::duration<double,std::milli>(
std::chrono::steady_clock::now()-MeshingStart).count(); std::chrono::steady_clock::now()-MeshingStart).count();
const auto FarShellStart=std::chrono::steady_clock::now();
const FStandaloneShapeWorld& farShape=tileShape.HasFarWorld()?tileShape:*impl_->shape; const FStandaloneShapeWorld& farShape=tileShape.HasFarWorld()?tileShape:*impl_->shape;
if(farShape.HasFarWorld()) if(farShape.HasFarWorld())
{ {
AppendFarStratumShells(world,farShape,nativeKey,step,mesh); AppendFarStratumShells(world,farShape,nativeKey,step,mesh);
AppendFarConnectorShells(farShape,nativeKey,step,world.CoarseTileCells,mesh); AppendFarConnectorShells(farShape,nativeKey,step,world.CoarseTileCells,mesh);
} }
out.diagnostics.farShellMilliseconds=std::chrono::duration<double,std::milli>(
std::chrono::steady_clock::now()-FarShellStart).count();
if(impl_->worldAssembly&&key.lod>=8) if(impl_->worldAssembly&&key.lod>=8)
{ {
const auto CapStats=RefineLod8NativeCap(world,*impl_->worldAssembly,nativeKey,mesh); const auto CapStats=RefineLod8NativeCap(world,*impl_->worldAssembly,nativeKey,mesh);
+125 -2
View File
@@ -15,6 +15,45 @@
namespace VoxelForgeCore namespace VoxelForgeCore
{ {
namespace
{
thread_local const ScopedWorldAssemblyFeatureQueriesV1* ActiveAssemblyQueries = nullptr;
}
ScopedWorldAssemblyFeatureQueriesV1::ScopedWorldAssemblyFeatureQueriesV1(
std::span<const WorldAssemblyFeatureQueryV1> queries, bool forceCandidatesForValidation) noexcept
: queries_(queries), forceCandidates_(forceCandidatesForValidation), previous_(ActiveAssemblyQueries)
{
// Resolve cost hints once per domain, never rediscover them in the sample loop.
// Resoudre les indications de cout une fois par domaine, hors de la boucle d'echantillons.
ActiveAssemblyQueries = nullptr;
for (const auto& query : queries_)
if (forceCandidates_ || query.features.candidateEvaluationUseful)
{ ActiveAssemblyQueries = this; break; }
}
ScopedWorldAssemblyFeatureQueriesV1::~ScopedWorldAssemblyFeatureQueriesV1()
{ ActiveAssemblyQueries = previous_; }
const WorldAssemblyFeatureTileListV1* ScopedWorldAssemblyFeatureQueriesV1::find(
const void* recipe, const void* plan, std::int64_t x, std::int64_t y,
std::int64_t z, bool columnOnly) noexcept
{
const std::array<std::int64_t, 3> point{x,y,z};
if (ActiveAssemblyQueries)
for (const auto& query : ActiveAssemblyQueries->queries_)
{
if (query.recipe != recipe || query.plan != plan
|| (!ActiveAssemblyQueries->forceCandidates_ && !query.features.candidateEvaluationUseful)) continue;
bool inside = true;
for (std::size_t axis=0; axis<(columnOnly ? 2u : 3u); ++axis)
inside = inside && point[axis]>=query.features.minimumSubvoxels[axis]
&& point[axis]<=query.features.maximumSubvoxels[axis];
if (inside) return &query.features;
}
return nullptr;
}
std::vector<std::uint32_t> BuildWorldAssemblyFeatureTileListV1( std::vector<std::uint32_t> BuildWorldAssemblyFeatureTileListV1(
std::span<const WorldAssemblyFeatureBoundsV1> features, std::span<const WorldAssemblyFeatureBoundsV1> features,
const std::array<std::int64_t, 3>& tileMinimumSubvoxels, const std::array<std::int64_t, 3>& tileMinimumSubvoxels,
@@ -52,8 +91,54 @@ std::vector<std::uint32_t> BuildWorldAssemblyFeatureTileListV1(
return result; return result;
} }
WorldAssemblyFeatureColumnRangeV1 BuildWorldAssemblyFeatureColumnRangeV1(
std::span<const WorldAssemblyFeatureBoundsV1> bounds, std::int64_t x, std::int64_t y)
{
WorldAssemblyFeatureColumnRangeV1 result;
result.known=true;
result.empty=true;
for(const auto& bound:bounds)
{
for(std::size_t axis=0;axis<3;++axis)
if(bound.minimumSubvoxels[axis]>bound.maximumSubvoxels[axis]) return {};
if(x<bound.minimumSubvoxels[0]||x>bound.maximumSubvoxels[0]
||y<bound.minimumSubvoxels[1]||y>bound.maximumSubvoxels[1])continue;
if(result.empty)
{
result.minimumZSubvoxels=bound.minimumSubvoxels[2];
result.maximumZSubvoxels=bound.maximumSubvoxels[2];
result.empty=false;
}
else
{
result.minimumZSubvoxels=std::min(result.minimumZSubvoxels,bound.minimumSubvoxels[2]);
result.maximumZSubvoxels=std::max(result.maximumZSubvoxels,bound.maximumSubvoxels[2]);
}
}
return result;
}
namespace world_assembly_detail namespace world_assembly_detail
{ {
// Preserve recipe order without allocating or looking up metadata in the point loop.
// Garder l'ordre de recette sans allocation ni recherche de metadonnees par point.
struct FeatureRange
{
const std::vector<WorldAssemblyFeatureV1>& features;
const WorldAssemblyFeatureTileListV1* list;
struct Iterator
{
const FeatureRange* range;
std::size_t index;
const WorldAssemblyFeatureV1& operator*() const
{ return range->features[range->list ? range->list->featureIndices[index] : index]; }
Iterator& operator++() { ++index; return *this; }
bool operator!=(const Iterator& other) const { return index!=other.index; }
};
Iterator begin() const { return {this,0}; }
Iterator end() const { return {this,list ? list->featureIndices.size() : features.size()}; }
};
constexpr std::int64_t Q16 = 1ll << 16; constexpr std::int64_t Q16 = 1ll << 16;
constexpr std::int64_t MinimumCliffShelfThicknessSubvoxels = 2 * WorldAssemblySubvoxelsPerVoxel; constexpr std::int64_t MinimumCliffShelfThicknessSubvoxels = 2 * WorldAssemblySubvoxelsPerVoxel;
constexpr std::int64_t MaxSerializedMagnitude = 1000000000000ll; constexpr std::int64_t MaxSerializedMagnitude = 1000000000000ll;
@@ -1294,7 +1379,8 @@ namespace world_assembly_detail
:CapHeightV2(recipe,plan,neighbor,x,y); :CapHeightV2(recipe,plan,neighbor,x,y);
std::int64_t cap=primaryCap+((neighborCap-primaryCap) std::int64_t cap=primaryCap+((neighborCap-primaryCap)
*(static_cast<std::int64_t>(Q16)-selection.primaryWeightQ16))/Q16; *(static_cast<std::int64_t>(Q16)-selection.primaryWeightQ16))/Q16;
for(const auto& feature:plan.features) const auto* list=ScopedWorldAssemblyFeatureQueriesV1::find(&recipe,&plan,x,y,0,true);
for(const auto& feature:FeatureRange{plan.features,list})
{ {
if(feature.type==1) if(feature.type==1)
{ {
@@ -1730,6 +1816,42 @@ bool Heightfield2D::isValid() const noexcept
}); });
} }
WorldAssemblyFeatureTileListV1 Heightfield2D::buildFeatureTileList(
const std::array<std::int64_t,3>& minimum,
const std::array<std::int64_t,3>& maximum) const
{
WorldAssemblyFeatureTileListV1 result;
// One shelf in the staged owner plan has no useful scan to shorten. Keep its full evaluator.
// Une seule corniche dans le plan auteur ne justifie pas la selection par echantillon.
result.candidateEvaluationUseful=plan_->features.size()>1;
result.minimumSubvoxels=minimum;
result.maximumSubvoxels=maximum;
std::vector<WorldAssemblyFeatureBoundsV1> bounds;
bounds.reserve(plan_->features.size());
for(const auto& feature:plan_->features)
{
WorldAssemblyFeatureBoundsV1 bound{feature.stableId,
feature.minimumSubvoxels,feature.maximumSubvoxels};
// Column writers affect every Z; finite volumes have an exact no-op outside their box.
// Les modificateurs de colonne agissent sur tout Z; les volumes sont neutres hors boite.
if(feature.type==1||feature.type==3)
{
bound.minimumSubvoxels[2]=std::numeric_limits<std::int64_t>::min();
bound.maximumSubvoxels[2]=std::numeric_limits<std::int64_t>::max();
}
else if(feature.type!=2&&feature.type!=4)
{
// Unknown future operations must opt into a proof before they can be culled.
// Une operation inconnue reste active tant que sa preuve manque.
bound.minimumSubvoxels.fill(std::numeric_limits<std::int64_t>::min());
bound.maximumSubvoxels.fill(std::numeric_limits<std::int64_t>::max());
}
bounds.push_back(bound);
}
result.featureIndices=BuildWorldAssemblyFeatureTileListV1(bounds,minimum,maximum);
return result;
}
HeightfieldPointSample Heightfield2D::point(std::int64_t x, std::int64_t y, HeightfieldPointSample Heightfield2D::point(std::int64_t x, std::int64_t y,
std::int64_t z) const noexcept std::int64_t z) const noexcept
{ {
@@ -1744,7 +1866,8 @@ HeightfieldPointSample Heightfield2D::point(std::int64_t x, std::int64_t y,
out.capHeightSubvoxels-z); out.capHeightSubvoxels-z);
if(recipe_->evaluatorVersion>=2) if(recipe_->evaluatorVersion>=2)
{ {
for(const auto& feature:plan_->features) const auto* list=ScopedWorldAssemblyFeatureQueriesV1::find(recipe_,plan_,x,y,z);
for(const auto& feature:FeatureRange{plan_->features,list})
{ {
if(feature.type==2) if(feature.type==2)
{ {
@@ -1138,3 +1138,59 @@ link-safety passed. No 0xC0000142 retry or new UECC folder occurred. I did not o
editor or build his project, edit any .uasset, create a link, commit, push or stash. editor or build his project, edit any .uasset, create a link, commit, push or stash.
— Luna — Luna
## World assembler S2 / M3 Round 6 — 2026-10-18
The shared engine now owns a family-neutral, worker-local feature-query scope. I removed the
island-only context from ShapeWorld; SurfaceWorld finite features, island groups, meshing, far-shell
field calls, decoration cells and landmark columns use the same domain/identity/halo contract.
S1's root-aware positive-density bounds and ordered connector composition are preserved.
The measurements matter here: the staged SurfaceWorld has one finite shelf, and the staged island
plan has four islands. Forced candidate bookkeeping gave no useful small-plan gain and regressed
the island tile timings by 7–13%. I added a shared cost hint, resolved once per domain, so these
small plans keep their full evaluator. Richer plans still use candidates. With the actual staged
owner template expanded to 256 islands, forced-list p95 was 110.570 -> 7.3619 ms (about 15x), with
identical hashes and a detected dropped-feature control. The composed surface stress (64 shelves,
one XY height writer and one cap writer) was 0.7084 -> 0.4078 ms, again exact with its control caught.
The S1 57x stress number used a different Round 3 template, so I do not compare those as a regression.
The placement cost came from marching almost the entire strate and using a full plan at every
step. Family-declared solid-support bounds now feed a shared column range query, alongside the
same feature scopes. We skip only certified air and keep the old march phase, bisection,
interpolation and transforms. Owner-palette p95 fell 7.0706 -> 0.2385 ms, with 58.84 density calls
per cell versus S1's 7,763.38. Its sampled authored palette still places zero objects; I keep that
limitation explicit rather than claiming those timings include spawning trees.
A separate populated fixture produced 572 placements across 14 cells. All 572 full/candidate
transforms match exactly, all canonical anchor checks pass, and the largest density residual is
0.035156 Core voxel units. The 64-cell active batch, including merge and real HISM application,
was 3271.147 -> 29.105 ms. All 572 instances were applied and then cleared. This is synchronous
CPU work under null RHI; it excludes rendering, GPU upload, async tree completion and owner tree
Blueprint logic. One run failed the new count assertion because normal region application clears
live landmark footprints. I isolated the validation fixture from those footprints; normal content
keeps them enabled. The corrected run passes.
BuildHost SurfaceWorld passed its full actor/streaming/collision/edit checks and W1/W8/W16 with
S1's default 25 cm hashes. The final island actor also keeps S1's entire W1/W8/W16 matrix. The final
focused surface replay checks LOD0/1/7/8 full-versus-default tile hashes after the cost hint. M2 V1/V2
fixture repeats, borders, MC identity and all 125 doorway phases pass. A separate Core-off run
proves unchanged legacy density and 12.5 cm scale. All builds/link audits passed. No timeout,
0xC0000142 or new UECC folder occurred. The failed HISM fixture run exited normally with test status 1.
My next structural suggestion is an immutable per-strate family-provider table, carrying declared
bounds, cost hints, value certificates and optional support/surface queries. It would avoid repeating
concrete family enumeration and rebuilding metadata in each consumer. I have not rebuilt that
boundary this round; the report lists the allocation, many-strate, hash and stale-provider evidence
needed first. Preserve the existing bounded biome lattice and cave interval machinery meanwhile.
New source files are `Round6/SharedFeatureListAudit.cpp` and `Round6/BuildSharedFeatureListAudit.ps1`
under WorldAssemblerM3. The report lists every modified source, likely compile spots, all failed and
replayed runs, timing limitations and the plain-words owner note. CODEMAP section 3 and ARCHITECTURE
section 8.23 are updated. No owner project/editor was opened, closed or built. No .uasset was edited,
and no link, commit, push or stash was created.
[Full S2 report](../../WorldAssemblerM3/Round6/REPORT.md).
— Sol
@@ -0,0 +1,61 @@
[CmdletBinding()]
param([string]$IslandAssemblyPath, [string]$SurfaceAssemblyPath)
$ErrorActionPreference = 'Stop'
$PluginRoot = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..\..\..\..'))
$HostOutput = Join-Path $PluginRoot 'Saved\BuildHost\HostProject\Plugins\VoxelForge\Saved\CoreWorldValidation\User\Saved\CoreWorldValidation\Output'
if (!$IslandAssemblyPath) { $IslandAssemblyPath = Join-Path $HostOutput 's2_assembly_FloatingIslands.json' }
if (!$SurfaceAssemblyPath) { $SurfaceAssemblyPath = Join-Path $HostOutput 's2_assembly_SurfaceWorld.json' }
$Results = Join-Path $PSScriptRoot 'results'
$BuildRoot = Join-Path $PluginRoot 'Standalone\Build\VoxelForgeCore'
$Exe = Join-Path $BuildRoot 'SharedFeatureListAudit.exe'
$Rsp = Join-Path $BuildRoot 'shared-feature-list-audit.rsp'
$VcVars = 'E:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat'
$Source = Join-Path $PSScriptRoot 'SharedFeatureListAudit.cpp'
$Library = Join-Path $BuildRoot 'VoxelForgeCore.lib'
foreach ($Required in @($IslandAssemblyPath,$SurfaceAssemblyPath,$VcVars,$Library,$Source)) {
if (!(Test-Path -LiteralPath $Required -PathType Leaf)) { throw "Missing audit input: $Required" }
}
[IO.Directory]::CreateDirectory($Results) | Out-Null
$Lines = @('/nologo','/std:c++20','/O2','/arch:AVX2','/fp:precise','/EHsc',
('/I"{0}"' -f (Join-Path $PluginRoot 'Standalone\Core\include')),
('/Fe"{0}"' -f $Exe),('"{0}"' -f $Source),('"{0}"' -f $Library))
[IO.File]::WriteAllLines($Rsp,$Lines,[Text.UTF8Encoding]::new($false))
Push-Location $BuildRoot
try {
$Command = 'call "{0}" >nul && cl.exe @"{1}"' -f $VcVars,$Rsp
& $env:ComSpec /d /s /c $Command
if ($LASTEXITCODE -eq -1073741502) { & $env:ComSpec /d /s /c $Command }
if ($LASTEXITCODE -ne 0) { throw "Audit compile failed: $LASTEXITCODE" }
} finally { Pop-Location }
for ($Attempt=1;$Attempt -le 2;++$Attempt) {
$Stdout = Join-Path $Results "shared_audit_attempt_$Attempt.stdout.log"
$Stderr = Join-Path $Results "shared_audit_attempt_$Attempt.stderr.log"
$Process = Start-Process -FilePath $Exe -WindowStyle Hidden -PassThru -WorkingDirectory $BuildRoot `
-ArgumentList @(('"{0}"' -f $IslandAssemblyPath),('"{0}"' -f $SurfaceAssemblyPath),('"{0}"' -f $Results)) `
-RedirectStandardOutput $Stdout -RedirectStandardError $Stderr
$Process.PriorityClass = [Diagnostics.ProcessPriorityClass]::Normal
$Process.ProcessorAffinity = [IntPtr]0xFFF
$Timer = [Diagnostics.Stopwatch]::StartNew()
$Peak = 0L
while (!$Process.WaitForExit(250)) {
$Process.Refresh()
$Peak = [Math]::Max($Peak,$Process.PrivateMemorySize64)
if ($Timer.Elapsed.TotalSeconds -gt 900 -or $Peak -gt 8GB) {
Stop-Process -Id $Process.Id -Force
throw 'Shared audit exceeded 15 minutes or 8 GiB.'
}
}
$Process.WaitForExit()
$Process.Refresh()
$ExitCode = $Process.ExitCode
if ($null -eq $ExitCode -and (Select-String -LiteralPath $Stdout -SimpleMatch 'SHARED_FEATURE_AUDIT_PASS' -Quiet)) { $ExitCode=0 }
"attempt=$Attempt exit=$ExitCode elapsed_s=$($Timer.Elapsed.TotalSeconds) peak_private_bytes=$Peak" |
Add-Content (Join-Path $Results 'shared_audit_ledger.txt')
Get-Content $Stdout
Get-Content $Stderr
if ($ExitCode -eq -1073741502 -and $Attempt -eq 1) { continue }
if ($ExitCode -ne 0 -or !(Select-String -LiteralPath $Stdout -SimpleMatch 'SHARED_FEATURE_AUDIT_PASS' -Quiet)) {
throw "Shared feature audit failed: $ExitCode"
}
break
}
@@ -0,0 +1,269 @@
# World assembler S2 / M3 Round 6
Date: 2026-10-18. Author: Sol. Base: `d6e6942`, branch `assembler/world`. No commit, push or stash.
## Shared engine change
The island-only worker context has been removed from `ShapeWorldInternal.h`.
`WorldAssemblyFeatureTileListV1` and `ScopedWorldAssemblyFeatureQueriesV1` now carry a shared,
worker-local candidate domain, keyed by immutable recipe/plan identity. Nested scopes restore their
predecessor; samples outside the prepared domain use the full evaluator. Tile preparation includes
the mesher's normal halo and prepares both existing native families. Derived far-shell field calls
stay in that scope too. The old island payload names remain aliases so S1 research tools still build.
Families retain their mathematics: SurfaceWorld declares XY influence for column writers and finite
no-op boxes for volumetric exceptions; islands retain S1's root-aware positive-density certificate
and ordered island/connector groups. The engine never assumes that density magnitude is Euclidean
distance. Unknown operations stay active. A shared `candidateEvaluationUseful` cost hint is resolved
once per domain. The measured one-feature SurfaceWorld and four-island plans prefer the full scan;
larger plans retain culling. Audits force candidates on small plans to prove their bounds too. No density arithmetic, recipe serialization/version,
voxel-size resolution, queue, Epoch, edit lock or streaming policy was changed.
The default contract for future caves, shafts, pits, passages and rivers is documented in
ARCHITECTURE §8.23. Declare conservative bounds and any value-side proof needed to preserve exact
positive values. Preserve operation order; unknown proofs evaluate normally. An air-base family can
also declare conservative solid support, allowing the shared column union to shorten a search.
Solid-base caves cannot use that air proof. Every new proof needs equal full/candidate hashes and a
dropped-needed-feature control.
Surface biome-site selection already searches a fixed local lattice and is cached by XY. It is not
a global feature scan. We preserve that machinery and target its finite feature loops. Existing
cave extraction keeps its existing line/spatial bins; this round makes no new cave performance claim.
FarShells remains derived repair geometry with existing connector/stratum candidate filtering, rather
than becoming another canonical generator. Its field queries inherit the shared scope.
## Placement cause and correction
Native island placement previously scanned the entire owner strate, calling a full-plan evaluator
at each step. The owner workload used about 7,763 density queries per cell in S1. Its sampling cell
was mostly air, so optimizing only the top-anchor helper did not remove the dominant work.
Snapshots now declare conservative support bounds once. Decoration cells and landmark columns
prepare the same shared feature queries as meshing. A shared support-union query skips proven air,
including entirely empty columns. The march retains its original lattice phase, crossing budget,
four bisections, interpolation, normals and placement hashes. No alternate approximate top surface
was introduced. Companion queries get their own nested domain when needed.
Validation preserves the owner's palette and reports it separately from a deliberately populated
fixture. That fixture uses dense deterministic floor placements, both water classifications, the
resolved biome tags, and a staged decoration mesh (engine cube fallback if none is available).
It checks exact full/candidate transforms and canonical island density residuals, then merges and
applies real HISM regions and clears them immediately. CPU totals include cell preparation, merge
and region application; the identical application/merge cost is shared by both comparison totals.
The isolated HISM fixture bypasses live landmark footprints, which otherwise suppress its instances.
Normal region application keeps those footprints enabled. Null RHI does not measure GPU upload,
rendering, a rendered frame, or asynchronous cluster-tree completion. Existing native content
snapshots still describe the planned base field; this round does not add edit-aware reanchoring.
## Measurements and proof
The field audit replays the plans exported from BuildHost's staged owner route. These are the
same transient owner-height fixtures described in Round 4: the saved settings point to `DA_Strate4`,
while the logged SurfaceWorld layout is reconstructed from `DA_Strate1` without saving an asset.
No Season is assigned in these copies. The island plan has 4 islands and 3 connectors; the surface
plan has one finite type-4 shelf. The stress cases extend those plans to 256 islands/240 connectors
and 64 surface shelves plus one XY height writer and one XY cap writer. They do not change any production recipe.
### Forced candidate proof and cost
Each island row samples 64 tiles / 64,000 points, including the normal halo. Each surface row
samples 64 tiles / 46,656 points and hashes density, ground, cap and biome ID (186,624 values).
Surface timings start both paths with cold column caches by changing only the local validation
cache epoch. Final timing replay ran without an overlapping build or Unreal process.
| Family / plan | Full-scan p95 ms | Forced-candidate p95 ms | List build p95 ms | Full and candidate hash | Negative control |
|---|---:|---:|---:|---|---|
| Islands, staged owner | 0.9803 | 1.0194 | 0.0012 | `cb94db3ca317d001` | detected |
| Islands, 256 stress | 110.570 | 7.3619 | 0.0803 | `d83bac7778f3d62a` | detected |
| Surface, staged owner | 0.4748 | 0.4559 | 0.0003 | `4207efd4ac869108` | detected |
| Surface, 64 shelves + height/cap writers | 0.7084 | 0.4078 | 0.0011 | `f1c6ea123284cc38` | detected |
Every row has zero mismatches. Each control removes a needed feature; the island control removes
its group certificate too. The shared-scope audit also checks identity/domain fallback, nested empty
scope restoration, scope teardown and the full-scan cost hint.
The owner island point audit offers no useful culling gain. Stress p95 improves about **15×**;
3,362 stress lists cost 216.615 ms in total (108 owner lists cost 0.0766 ms). The stress template comes
from the actual staged owner plan, whereas S1's standalone audit used Round 3's manifest; its 57×
figure is not an apples-to-apples regression comparison.
Surface's single feature has no material point-query gain. Its column computation alone is about
0.368 ms of the 0.475 ms p95, and the existing bounded biome/cap path stays intact. The stress case
reduces p95 by about 42% while column cost stays similar (0.3699/0.3552 ms). There is no reason to
force a global biome/cap feature list into this owner plan.
Warmed BuildHost tile timings initially exposed a 7–13% island regression from forcing group
bookkeeping on four islands. That led to the shared cost hint. The final default path keeps its small-plan timing:
| Staged plan | LOD | Full tile p95 ms | Default shared-engine p95 ms | Full / shared shell p95 ms |
|---|---:|---:|---:|---:|
| Islands | 0 | 94.984 | 95.094 | 0.0002 / 0.0003 |
| Islands | 1 | 87.280 | 87.577 | 0.0005 / 0.0003 |
| Islands | 7 | 20.442 | 20.409 | 16.918 / 16.882 |
| Islands | 8 | 73.038 | 72.923 | 69.930 / 69.844 |
| Surface | 0 | 11.238 | 11.178 | 0.0001 / 0.0001 |
| Surface | 1 | 7.346 | 7.354 | 0.0001 / 0.0001 |
| Surface | 7 | 32.739 | 32.381 | 26.378 / 26.021 |
| Surface | 8 | 168.545 | 168.063 | 135.960 / 136.310 |
Each row measures eight owner clipmap keys after warming both routes, including list preparation,
meshing and density hashes. All four output hashes match on every paired key. This is a small,
hash-capture workload, not a rendered-frame percentile. The far-shell stage excludes LOD8 cap
refinement. There is no measured reason to add another shell-specific feature index for these
small owner plans; larger canonical fields inherit their family culling through the shared scope.
### Island placement and active populated cost
| Workload | Full scan | Shared engine | Result |
|---|---:|---:|---|
| Owner palette, cell p95 | 7.0706 ms | 0.2385 ms | about 29.6× faster |
| Owner palette, 64-cell preparation | 412.960 ms | 3.840 ms | same zero authored placements |
| Populated fixture, populated-cell p95 | 53.9875 ms | 2.1501 ms | 14 populated cells / 572 placements |
| Populated fixture, all 64 cells | 3270.478 ms | 28.436 ms | exact transforms match |
| Merge / real HISM apply | 0.0623 / 0.6068 ms | same products/cost | 572 applied instances |
| Populated active batch CPU total | 3271.147 ms | 29.105 ms | about 112× faster |
Candidate density calls are 58.84 per authored cell, versus S1's 7,763.38 for the same owner plan.
Legacy remains 193.25 calls/cell and 0.2855 ms p95. Stationary manager update p95 is 0.0005 ms;
that idle number is separate from the active batch totals above.
All 572 populated transforms match their full-scan counterparts exactly. All 572 canonical island
anchor checks pass; maximum density residual is 0.035156 Core voxel density units. The existing
terrain/content tests pass on both island and connector: island height difference is 0.373 cm,
connector difference 0.096 cm. Capsule contacts, Blueprint carve/fill, water and atmosphere pass.
No approximate anchor shortcut is used.
The six-entry authored palette still produces zero placements in these 64 cells. The populated
fixture is explicit synthetic coverage; it does not claim the cost of the owner's tree Blueprint
logic or rendered foliage. Active totals sum worker preparation and synchronous merge/application
calls over the whole batch, not one frame. Footprint suppression is disabled only for the isolated
fixture, which is immediately cleared. Async HISM tree completion and rendering are not included.
The earlier SurfaceWorld populated diagnostic produced 8,192 synthetic transforms with exact
full/candidate agreement, but had no mesh application in that build and warm-cache ordering;
its speed figures are not used as a SurfaceWorld improvement claim.
### Default identity
No recipe version was bumped: all changes select exact evaluators or skip certified air.
The full SurfaceWorld actor run reproduced S1's W1/W8/W16 matrix:
`0cd2a1a4152ee4aa / 17775c835711e5a3 / a27224958fc27d7b / 3821fea90b134ce4`
(density / mesh / payload / topology). Island assembly/recipe/plan remain
`9c372e7bfb5b8310 / 5e0370d9f95f1c15 / dbbda4932378047a`.
The final-source island W1/W8/W16 matrix is also exactly S1:
`4dcfb37585d7d986 / 725a18374c5623de / fb345d8cb319d18b / 76f2846be8efb5f4`.
The full SurfaceWorld actor pass precedes the final cost hint; the final-source focused replay
checks its LOD0/1/7/8 full-versus-default hashes after that selection-only change. Core-off's separate
final run proves unchanged legacy density at three probes and 12.5 cm. M2 V1/V2 five-fixture repeats,
same-LOD borders, V2 MC identity and 125 doorway phases all pass. Existing doorway contour
mismatch remains 22.844 cm in that fixture audit; this round does not claim a new seam solution.
### Run ledger and limits
| Run | Result / limit |
|---|---|
| Standalone Core library builds (four iterations) | all first-attempt passes; final link-safety audit checks 220,200 symbol lines |
| BuildHost UBT/UHT builds (six iterations) | all first-attempt passes; latest 62.03 s; 18 Core shims, link-safety 95,738 symbol lines; five staged asset hashes verified |
| Preliminary island actor | pass, about 3m57s, 3.19 GiB peak; canonical hashes and 572 anchor checks |
| Full SurfaceWorld actor | pass, about 12m15s, 3.58 GiB peak; streaming, collisions, motion, edits, geometry/anchors, W1/W8/W16 |
| Island actor with new HISM fixture | exit 1, about 4m14s; landmark footprint suppression hit the fixture count assertion; field/worker/anchor gates passed |
| Final island actor, cost hint and isolated HISM fixture | pass, about 3m52s, 3.03 GiB peak; W1/W8/W16, 572 exact anchors and applied instances |
| Final focused SurfaceWorld tile replay | pass, about 21s, 2.70 GiB peak; 32 paired LOD0/1/7/8 keys, all hashes equal |
| Final M2 V1/V2 fixtures | pass, about 17s, 2.34 GiB peak; repeats, borders, MC identity, 125 doorway phases |
| Final Core-off | pass, about 14s, 2.14 GiB peak; legacy density and scale unchanged |
| Final composed standalone audit | pass, 7.87s, about 64 MiB peak; all four full/candidate rows exact and controls detected |
Build iterations cover initial integration, added diagnostics, active fixture application, warmed
measurements, duplicate-validation removal, and the cost-hint/fixture fix. Their logs are the six
`results/host_build*.log` files and four `results/core_build*.log` files. The standalone audit was
replayed as those diagnostics were refined; the final composed stress also exercises XY height/cap
writers at unrelated Z values. Every replay passed its field/control assertions.
One actor run failed its new populated-HISM count
assertion after passing field/worker/anchor gates: normal `ApplyRegion` also clears live landmark
footprints. The fixture now explicitly isolates those footprints; normal application defaults to
keeping them enabled. The failed log is `results/islands_hism_fixture_failed.log` (exit 1, no crash).
Preliminary surface tile timings used a cold full path followed by a warm candidate path. Those
numbers are discarded; the final focused replay warms both paths. The first standalone audit
also overlapped compilation; it passed functional controls, but its timings are discarded. Idle
replays and the final-source replay are all recorded in `results/shared_audit_ledger.txt`.
An audit iteration removed duplicate plan validation from scoped island samples. All standalone
field audits passed; `vswhere.exe` was not found by the installed VC setup script, but CL compiled
and the audit ran successfully. A documentation-edit attempt failed on Python's default Windows
encoding; it changed no files and was corrected with UTF-8/apply_patch.
The first preliminary island launch used the existing launcher; I applied 12-CPU affinity during
that run, then added `-corelimit=12` and 12-CPU affinity to the launcher for subsequent runs. Builds
used at most eight UBT actions, or eight UBT plus four standalone compiler actions. W16 remains a
logical determinism partition under the 12-CPU execution cap. Each Unreal launch checks 4 GiB free
RAM and refuses another Unreal process. Every process is guarded at 8 GiB and 900 seconds.
No viewer or GPU capture was launched. No run exceeded its memory/time guard, no timeout occurred,
and no run returned `0xC0000142` (so its one-retry rule was never needed).
Crash audit: `Saved/BuildHost/HostProject/Saved/Crashes/` still contains only
`UECC-Windows-F1A3EFD5469F5DD62E68EBBACAC84376_0000` (2026-09-17). The isolated User crash root
still contains only `UECC-Windows-7B23387E4235BCFEDE42BC9D637E41CB_0000` (2026-10-08).
No new UECC folder exists in the host, staged plugin or isolated User crash roots. The final
commandlets finished with zero errors; known missing staged `/Game/VoxelForge/Blocks/NewDataAsset`
and certificate-store warnings remain. The failed fixture run logged its explicit test error and
exited normally.
Evidence: `results/SUMMARY.json`, `results/shared_audit_attempt_1.stdout.log`,
`results/island_tile_lists.json`, `results/shared_audit_ledger.txt`, `results/surface_full_actor.log`,
`results/islands_full_actor_final.log`, `results/surface_warmed_final.log`,
`results/m2_fixtures_final.log` and `results/core_off_final.log`. Owner plans exported under
BuildHost's isolated User Saved directory are the standalone replay inputs.
No owner project/editor was opened, closed or built, no `.uasset` was edited, and no junction,
symlink, commit, push or stash was created. All authored writes and build outputs stayed inside
this plugin's workspace/BuildHost.
## My view of the boundary
The next structural improvement should be a prepared immutable family-provider table per strate.
Today the shared scope is generic, but the world and content snapshot adapters still enumerate the
two concrete field families, and preparation rebuilds metadata vectors for each query. A provider
could publish stable feature IDs, bounds, an optional value certificate and optional surface/support
queries once, while the engine handles consumer domains, ordering, cache lifetime and instrumentation.
That would also give future cave/rivers placement the same entry point without adding another
content snapshot class. I have not rebuilt the architecture for that proposal in this round.
Evidence required before doing it: a many-strate preparation profile showing the cost of rebuilding
bounds/group summaries; separate point-evaluation and list-build allocation counts; equal full versus
prepared hashes across owner plans, halos and edited epochs; a deliberately stale-provider control;
and no regression in time to collision-ready terrain or peak memory. Preserve the current bounded
lattice path for procedural biomes and the current cave interval engine until a measured replacement
wins. A universal list of shapes would not capture those processes or their proofs.
## Files and compile spots
New source files:
- `Standalone/Research/WorldAssemblerM3/Round6/SharedFeatureListAudit.cpp` — standalone owner/stress audit.
- `Standalone/Research/WorldAssemblerM3/Round6/BuildSharedFeatureListAudit.ps1` — guarded audit compile/run script.
Modified runtime/Core sources:
- `Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h`
- `Standalone/Core/src/WorldAssemblerInternal.h`
- `Standalone/Core/src/FloatingIslandsInternal.h`
- `Standalone/Core/src/ShapeWorldInternal.h`
- `Standalone/Core/src/VoxelForgeCore.cpp`
- `Source/VoxelForge/Private/VoxelContentManager.cpp`
- `Source/VoxelForge/Public/VoxelContentManager.h`
- `Source/VoxelForgeEditor/Private/VoxelForgeExploreCommandlet.cpp`
Also updated `Tools/TestVoxelForgeCoreWorldHost.ps1`, CODEMAP §3, ARCHITECTURE §8.23 and the dialogue.
Likely compile spots: the public Core API/export boundary and canonical Core shim; shared scope/TLS
linkage; `VoxelContentManager.h` parsing and its runtime translation unit; editor commandlet timing
and fixture code. No new runtime translation unit or manifest shim is required.
## Owner note
Keep Core enabled at the existing 25 cm setting. There is no new generator setting to configure.
The shared engine now narrows terrain searches for both native families and skips empty space when
placing island content. Terrain shapes and default hashes are preserved. The populated validation
fixture measures placement without changing your decoration assets. Ready to build your project
when you choose; I only build and launch the isolated plugin BuildHost.
@@ -0,0 +1,601 @@
#include "VoxelForgeCore/VoxelForgeCore.h"
#include <algorithm>
#include <array>
#include <chrono>
#include <cmath>
#include <cstdint>
#include <filesystem>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <numeric>
#include <sstream>
#include <stdexcept>
#include <string>
#include <vector>
namespace
{
using namespace VoxelForgeCore;
using Clock = std::chrono::steady_clock;
constexpr std::int64_t Q = WorldAssemblySubvoxelsPerVoxel;
constexpr std::int64_t TileQ = 32 * Q;
constexpr std::int64_t TileStepQ = 4 * Q;
constexpr std::int64_t MesherHaloQ = Q;
struct Tile
{
std::array<std::int64_t, 3> minimum{};
std::array<std::int64_t, 3> maximum{};
FloatingIslandTileFeatureListV1 featureList;
double buildMilliseconds = 0.0;
};
struct Measurement
{
std::uint64_t tilesBuilt = 0;
std::uint64_t sampledTiles = 0;
std::uint64_t samples = 0;
std::uint64_t candidateEvaluations = 0;
std::uint64_t fullEvaluations = 0;
std::uint64_t exactMismatches = 0;
std::uint64_t droppedFeatureDetected = 0;
double listBuildTotalMilliseconds = 0.0;
double listBuildP95Milliseconds = 0.0;
double beforeP95Milliseconds = 0.0;
double afterP95Milliseconds = 0.0;
double beforeMedianMilliseconds = 0.0;
double afterMedianMilliseconds = 0.0;
double meanCandidateFeatures = 0.0;
double p95CandidateFeatures = 0.0;
std::string beforeHash;
std::string afterHash;
bool negativeControlPassed = false;
};
std::string Hex64(std::uint64_t value)
{
std::ostringstream output;
output << std::hex << std::setfill('0') << std::setw(16) << value;
return output.str();
}
std::uint64_t HashValues(const std::vector<std::int64_t>& values)
{
std::uint64_t hash = 14695981039346656037ull;
for (const auto value : values)
for (int byte = 0; byte < 8; ++byte)
{
hash ^= static_cast<std::uint8_t>(static_cast<std::uint64_t>(value) >> (byte * 8));
hash *= 1099511628211ull;
}
return hash;
}
double Percentile95(std::vector<double> values)
{
if (values.empty()) return 0.0;
std::sort(values.begin(), values.end());
const std::size_t index = (values.size() * 95 + 99) / 100 - 1;
return values[std::min(index, values.size() - 1)];
}
double Median(std::vector<double> values)
{
if (values.empty()) return 0.0;
std::sort(values.begin(), values.end());
const std::size_t middle = values.size() / 2;
if (values.size() & 1u) return values[middle];
return 0.5 * (values[middle - 1] + values[middle]);
}
std::int64_t FloorDiv(std::int64_t value, std::int64_t divisor)
{
std::int64_t quotient = value / divisor;
const std::int64_t remainder = value % divisor;
if (remainder != 0 && ((remainder < 0) != (divisor < 0))) --quotient;
return quotient;
}
void ShiftBounds(std::array<std::int64_t, 3>& minimum,
std::array<std::int64_t, 3>& maximum, const std::array<std::int64_t, 3>& delta)
{
for (std::size_t axis = 0; axis < 3; ++axis)
{ minimum[axis] += delta[axis]; maximum[axis] += delta[axis]; }
}
std::uint64_t SyntheticId(std::uint32_t index, std::uint64_t salt)
{
std::uint64_t value = (static_cast<std::uint64_t>(index) << 32) ^ salt;
value += 0x9e3779b97f4a7c15ull;
value = (value ^ (value >> 30)) * 0xbf58476d1ce4e5b9ull;
value = (value ^ (value >> 27)) * 0x94d049bb133111ebull;
value ^= value >> 31;
return value == 0 ? 1 : value;
}
FloatingIslandPlanV1 MakeStressPlan(const FloatingIslandRecipeV1& recipe,
const FloatingIslandPlanV1& source, std::size_t islandCount)
{
if (source.islands.empty()) throw std::runtime_error("source plan has no island template");
FloatingIslandPlanV1 result;
result.islands.reserve(islandCount);
const auto& prototype = source.islands.front();
constexpr std::size_t Columns = 16;
const std::int64_t spacing = 80 * Q;
for (std::size_t index = 0; index < islandCount; ++index)
{
FloatingIslandAnchorV1 island = prototype;
const std::int64_t dx = static_cast<std::int64_t>(index % Columns) * spacing;
const std::int64_t dy = static_cast<std::int64_t>(index / Columns) * spacing;
const std::int64_t dz = static_cast<std::int64_t>(index % 3) * 4 * Q;
const std::array<std::int64_t, 3> delta{dx, dy, dz};
island.centerSubvoxels = {prototype.centerSubvoxels[0] + dx,
prototype.centerSubvoxels[1] + dy, prototype.centerSubvoxels[2] + dz};
island.topZSubvoxels += dz;
island.bottomZSubvoxels += dz;
ShiftBounds(island.influenceMinimumSubvoxels, island.influenceMaximumSubvoxels, delta);
island.ordinal = static_cast<std::uint32_t>(index);
island.stableId = SyntheticId(static_cast<std::uint32_t>(index), 0x49534c414e44ull);
result.islands.push_back(island);
}
result.clusterEdges.reserve(islandCount - islandCount / Columns);
for (std::size_t index = 0; index < islandCount; ++index)
{
if (index % Columns + 1 >= Columns || index + 1 >= islandCount) continue;
const auto& from = result.islands[index];
const auto& to = result.islands[index + 1];
FloatingIslandConnectorV1 edge;
edge.stableId = SyntheticId(static_cast<std::uint32_t>(index), 0x425249444745ull);
edge.fromIslandId = from.stableId;
edge.toIslandId = to.stableId;
edge.startSubvoxels = {from.centerSubvoxels[0] + from.radiusSubvoxels,
from.centerSubvoxels[1], from.topZSubvoxels};
edge.endSubvoxels = {to.centerSubvoxels[0] - to.radiusSubvoxels,
to.centerSubvoxels[1], to.topZSubvoxels};
edge.halfWidthSubvoxels = recipe.bridgeHalfWidthSubvoxels;
edge.thicknessSubvoxels = recipe.bridgeThicknessSubvoxels;
const std::int64_t pad = recipe.blendRadiusSubvoxels + recipe.roughnessSubvoxels;
const std::int64_t archDepth = recipe.geometryVersion >= 2
? 2 * edge.thicknessSubvoxels : 0;
const std::int64_t xyPad = edge.halfWidthSubvoxels + pad;
edge.influenceMinimumSubvoxels = {
std::min(edge.startSubvoxels[0], edge.endSubvoxels[0]) - xyPad,
std::min(edge.startSubvoxels[1], edge.endSubvoxels[1]) - xyPad,
std::min(edge.startSubvoxels[2], edge.endSubvoxels[2])
- edge.thicknessSubvoxels - pad - archDepth};
edge.influenceMaximumSubvoxels = {
std::max(edge.startSubvoxels[0], edge.endSubvoxels[0]) + xyPad,
std::max(edge.startSubvoxels[1], edge.endSubvoxels[1]) + xyPad,
std::max(edge.startSubvoxels[2], edge.endSubvoxels[2]) + pad};
result.clusterEdges.push_back(edge);
}
return result;
}
std::vector<Tile> BuildMixedTiles(const FloatingIslandFieldV1& field,
const std::array<std::int64_t, 3>& tileLimitMinimum,
const std::array<std::int64_t, 3>& tileLimitMaximum,
Measurement& measurement)
{
std::vector<Tile> tiles;
for (std::int64_t z = tileLimitMinimum[2]; z <= tileLimitMaximum[2]; z += TileQ)
for (std::int64_t y = tileLimitMinimum[1]; y <= tileLimitMaximum[1]; y += TileQ)
for (std::int64_t x = tileLimitMinimum[0]; x <= tileLimitMaximum[0]; x += TileQ)
{
Tile tile;
tile.minimum = {x, y, z};
tile.maximum = {x + TileQ, y + TileQ, z + TileQ};
const std::array<std::int64_t, 3> sampleMinimum{
x - MesherHaloQ, y - MesherHaloQ, z - MesherHaloQ};
const std::array<std::int64_t, 3> sampleMaximum{
x + TileQ + MesherHaloQ, y + TileQ + MesherHaloQ, z + TileQ + MesherHaloQ};
const auto begin = Clock::now();
tile.featureList = field.buildFeatureTileList(sampleMinimum, sampleMaximum);
tile.buildMilliseconds = std::chrono::duration<double, std::milli>(Clock::now() - begin).count();
measurement.listBuildTotalMilliseconds += tile.buildMilliseconds;
++measurement.tilesBuilt;
if (!tile.featureList.featureIndices.empty()) tiles.push_back(std::move(tile));
}
return tiles;
}
Measurement MeasureField(const FloatingIslandFieldV1& field,
std::array<std::int64_t, 3> limitMinimum,
std::array<std::int64_t, 3> limitMaximum)
{
Measurement measurement;
for (std::size_t axis = 0; axis < 3; ++axis)
{
limitMinimum[axis] = FloorDiv(limitMinimum[axis], TileQ) * TileQ;
limitMaximum[axis] = FloorDiv(limitMaximum[axis], TileQ) * TileQ;
}
auto tiles = BuildMixedTiles(field, limitMinimum, limitMaximum, measurement);
if (tiles.empty()) throw std::runtime_error("tile bounds produced no candidate tiles");
std::vector<double> buildTimes, candidateCounts;
buildTimes.reserve(tiles.size()); candidateCounts.reserve(tiles.size());
for (const Tile& tile : tiles)
{ buildTimes.push_back(tile.buildMilliseconds); candidateCounts.push_back(static_cast<double>(tile.featureList.featureIndices.size())); }
measurement.listBuildP95Milliseconds = Percentile95(std::move(buildTimes));
measurement.meanCandidateFeatures = std::accumulate(candidateCounts.begin(), candidateCounts.end(), 0.0)
/ static_cast<double>(candidateCounts.size());
measurement.p95CandidateFeatures = Percentile95(std::move(candidateCounts));
constexpr std::size_t MaximumSampleTiles = 64;
if (tiles.size() > MaximumSampleTiles)
{
std::vector<Tile> selected;
selected.reserve(MaximumSampleTiles);
for (std::size_t slot = 0; slot < MaximumSampleTiles; ++slot)
{
const std::size_t index = slot * (tiles.size() - 1) / (MaximumSampleTiles - 1);
selected.push_back(std::move(tiles[index]));
}
tiles = std::move(selected);
}
std::vector<double> beforeTimes, afterTimes;
std::vector<std::int64_t> beforeValues, afterValues;
beforeTimes.reserve(tiles.size()); afterTimes.reserve(tiles.size());
for (const Tile& tile : tiles)
{
// Cover the mesher's one-sample LOD0 halo as well as the tile at four-voxel spacing.
// Couvrir la marge d'un echantillon du maillage LOD0 et la tuile a pas de quatre voxels.
const auto buildAxis = [](std::int64_t minimum, std::int64_t maximum)
{
std::vector<std::int64_t> values;
const std::int64_t start = minimum - MesherHaloQ;
const std::int64_t end = maximum + MesherHaloQ;
for (std::int64_t value = start; value <= end; value += TileStepQ)
values.push_back(value);
if (values.empty() || values.back() != end) values.push_back(end);
return values;
};
const auto xs = buildAxis(tile.minimum[0], tile.maximum[0]);
const auto ys = buildAxis(tile.minimum[1], tile.maximum[1]);
const auto zs = buildAxis(tile.minimum[2], tile.maximum[2]);
std::vector<std::array<std::int64_t, 3>> points;
points.reserve(xs.size() * ys.size() * zs.size());
for (const std::int64_t z : zs)
for (const std::int64_t y : ys)
for (const std::int64_t x : xs)
points.push_back({x, y, z});
measurement.samples += points.size();
measurement.fullEvaluations += points.size();
measurement.candidateEvaluations += points.size() * tile.featureList.featureIndices.size();
const auto beforeStart = Clock::now();
for (const auto& point : points)
beforeValues.push_back(field.densitySubvoxels(point[0], point[1], point[2]));
beforeTimes.push_back(std::chrono::duration<double, std::milli>(Clock::now() - beforeStart).count());
const auto afterStart = Clock::now();
const WorldAssemblyFeatureQueryV1 query{field.recipe(),field.plan(),tile.featureList};
{
const ScopedWorldAssemblyFeatureQueriesV1 scope(std::span(&query,1),true);
for (const auto& point : points)
afterValues.push_back(field.densitySubvoxels(point[0],point[1],point[2]));
}
afterTimes.push_back(std::chrono::duration<double, std::milli>(Clock::now() - afterStart).count());
++measurement.sampledTiles;
}
measurement.beforeP95Milliseconds = Percentile95(beforeTimes);
measurement.afterP95Milliseconds = Percentile95(afterTimes);
measurement.beforeMedianMilliseconds = Median(beforeTimes);
measurement.afterMedianMilliseconds = Median(afterTimes);
measurement.beforeHash = Hex64(HashValues(beforeValues));
measurement.afterHash = Hex64(HashValues(afterValues));
const std::size_t common = std::min(beforeValues.size(), afterValues.size());
for (std::size_t index = 0; index < common; ++index)
if (beforeValues[index] != afterValues[index]) ++measurement.exactMismatches;
if (field.plan() && !field.plan()->islands.empty())
{
constexpr std::uint32_t droppedFeature = 0;
const auto& island = field.plan()->islands.front();
const std::array<std::int64_t, 3> point{island.centerSubvoxels[0], island.centerSubvoxels[1],
island.bottomZSubvoxels + (island.topZSubvoxels - island.bottomZSubvoxels) / 2};
std::array<std::int64_t, 3> minimum{};
std::array<std::int64_t, 3> maximum{};
for (std::size_t axis = 0; axis < 3; ++axis)
{
minimum[axis] = FloorDiv(point[axis], TileQ) * TileQ;
maximum[axis] = minimum[axis] + TileQ;
}
auto sampleMinimum = minimum;
auto sampleMaximum = maximum;
for (std::size_t axis = 0; axis < 3; ++axis)
{
sampleMinimum[axis] -= MesherHaloQ;
sampleMaximum[axis] += MesherHaloQ;
}
FloatingIslandTileFeatureListV1 dropped = field.buildFeatureTileList(
sampleMinimum, sampleMaximum);
auto candidate = std::lower_bound(dropped.featureIndices.begin(), dropped.featureIndices.end(),
droppedFeature);
if (candidate != dropped.featureIndices.end() && *candidate == droppedFeature)
{
dropped.featureIndices.erase(candidate);
for (std::size_t axis = 0; axis < 3; ++axis)
{
dropped.groupBounds[droppedFeature].minimumSubvoxels[axis] += 100000000000ll;
dropped.groupBounds[droppedFeature].maximumSubvoxels[axis] += 100000000000ll;
}
const auto order = std::find(dropped.groupSearchOrder.begin(),
dropped.groupSearchOrder.end(), droppedFeature);
if (order != dropped.groupSearchOrder.end())
{
const std::size_t orderIndex = static_cast<std::size_t>(order - dropped.groupSearchOrder.begin());
dropped.groupSearchOrder.erase(order);
dropped.groupDistanceLowerBoundsSubvoxels.erase(
dropped.groupDistanceLowerBoundsSubvoxels.begin() + orderIndex);
dropped.groupSearchComplete = false;
const auto full = field.densitySubvoxels(point[0], point[1], point[2]);
const WorldAssemblyFeatureQueryV1 badQuery{field.recipe(),field.plan(),dropped};
const ScopedWorldAssemblyFeatureQueriesV1 badScope(std::span(&badQuery,1),true);
const auto bad = field.densitySubvoxels(point[0],point[1],point[2]);
if (full != bad) ++measurement.droppedFeatureDetected;
}
}
}
measurement.negativeControlPassed = measurement.droppedFeatureDetected > 0;
return measurement;
}
void Print(const char* label, const Measurement& measurement,
std::size_t islandCount, std::size_t connectorCount)
{
std::cout << label << " islands=" << islandCount << " connectors=" << connectorCount
<< " forced_candidates=1 list_tiles_built=" << measurement.tilesBuilt
<< " sampled_tiles=" << measurement.sampledTiles
<< " sample_count=" << measurement.samples
<< " mean_candidates=" << measurement.meanCandidateFeatures
<< " p95_candidates=" << measurement.p95CandidateFeatures
<< " list_build_total_ms=" << measurement.listBuildTotalMilliseconds
<< " list_build_p95_ms=" << measurement.listBuildP95Milliseconds
<< " full_tile_p95_ms=" << measurement.beforeP95Milliseconds
<< " candidate_tile_p95_ms=" << measurement.afterP95Milliseconds
<< " full_tile_median_ms=" << measurement.beforeMedianMilliseconds
<< " candidate_tile_median_ms=" << measurement.afterMedianMilliseconds
<< " exact_mismatches=" << measurement.exactMismatches
<< " full_hash=" << measurement.beforeHash
<< " candidate_hash=" << measurement.afterHash
<< " negative_control_detected=" << measurement.negativeControlPassed << '\n';
}
}
int IslandAuditMain(int argc, char** argv)
{
try
{
if (argc != 3)
{ std::cerr << "usage: FloatingIslandTileListAudit <manifest.json> <output.json>\n"; return 2; }
std::ifstream input(argv[1], std::ios::binary);
if (!input) throw std::runtime_error("could not open the recorded round-3 island manifest");
const std::string json{std::istreambuf_iterator<char>(input), std::istreambuf_iterator<char>()};
std::string error;
const auto manifest = FloatingIslandManifestV1::FromJson(json, error);
if (!manifest || !manifest->validate(error)) throw std::runtime_error("manifest invalid: " + error);
FloatingIslandFieldV1 field(manifest->recipe, manifest->plan);
if (!field.isValid()) throw std::runtime_error("round-3 production field did not validate");
const auto bounds = field.influenceBounds();
const auto& center=manifest->plan.islands.front().centerSubvoxels;
WorldAssemblyFeatureQueryV1 guardQuery{field.recipe(),field.plan(),field.buildFeatureTileList(center,center)};
if(!guardQuery.features.candidateEvaluationUseful)
{
const ScopedWorldAssemblyFeatureQueriesV1 preferredFull(std::span(&guardQuery,1));
if(ScopedWorldAssemblyFeatureQueriesV1::find(field.recipe(),field.plan(),center[0],center[1],center[2]))
throw std::runtime_error("shared scope ignored the full-scan cost hint");
}
const auto outsideX=center[0]+1000*Q;
const auto outsideFull=field.densitySubvoxels(outsideX,center[1],center[2]);
{
const ScopedWorldAssemblyFeatureQueriesV1 scope(std::span(&guardQuery,1),true);
if(field.densitySubvoxels(outsideX,center[1],center[2])!=outsideFull)
throw std::runtime_error("outside-domain fallback changed density");
{
const ScopedWorldAssemblyFeatureQueriesV1 emptyScope(std::span<const WorldAssemblyFeatureQueryV1>{});
if(ScopedWorldAssemblyFeatureQueriesV1::find(field.recipe(),field.plan(),center[0],center[1],center[2]))
throw std::runtime_error("nested empty scope inherited candidates");
}
if(!ScopedWorldAssemblyFeatureQueriesV1::find(field.recipe(),field.plan(),center[0],center[1],center[2]))
throw std::runtime_error("nested scope did not restore candidates");
}
if(ScopedWorldAssemblyFeatureQueriesV1::find(field.recipe(),field.plan(),center[0],center[1],center[2]))
throw std::runtime_error("scope leaked candidates");
const Measurement today = MeasureField(field, bounds.minimumSubvoxels, bounds.maximumSubvoxels);
const FloatingIslandPlanV1 stressPlan = MakeStressPlan(manifest->recipe, manifest->plan, 256);
FloatingIslandFieldV1 stressField(manifest->recipe, stressPlan);
if (!stressField.isValid()) throw std::runtime_error("synthetic stress field did not validate");
const auto stressBounds = stressField.influenceBounds();
const Measurement stress = MeasureField(stressField,
stressBounds.minimumSubvoxels, stressBounds.maximumSubvoxels);
Print("today", today, manifest->plan.islands.size(), manifest->plan.clusterEdges.size());
Print("stress", stress, stressPlan.islands.size(), stressPlan.clusterEdges.size());
std::ofstream output(argv[2], std::ios::trunc);
if (!output) throw std::runtime_error("could not create audit result JSON");
output << std::setprecision(12) << "{\n"
<< " \"baseline\":{\"islands\":" << manifest->plan.islands.size()
<< ",\"connectors\":" << manifest->plan.clusterEdges.size()
<< ",\"tiles_built\":" << today.tilesBuilt << ",\"sampled_tiles\":" << today.sampledTiles
<< ",\"samples\":" << today.samples << ",\"mean_candidates\":" << today.meanCandidateFeatures
<< ",\"p95_candidates\":" << today.p95CandidateFeatures
<< ",\"list_build_total_ms\":" << today.listBuildTotalMilliseconds
<< ",\"list_build_p95_ms\":" << today.listBuildP95Milliseconds
<< ",\"before_p95_ms\":" << today.beforeP95Milliseconds
<< ",\"after_p95_ms\":" << today.afterP95Milliseconds
<< ",\"before_median_ms\":" << today.beforeMedianMilliseconds
<< ",\"after_median_ms\":" << today.afterMedianMilliseconds
<< ",\"exact_mismatches\":" << today.exactMismatches
<< ",\"before_hash\":\"" << today.beforeHash << "\",\"after_hash\":\"" << today.afterHash
<< "\",\"negative_control\":" << (today.negativeControlPassed ? "true" : "false") << "},\n"
<< " \"stress\":{\"islands\":" << stressPlan.islands.size()
<< ",\"connectors\":" << stressPlan.clusterEdges.size()
<< ",\"tiles_built\":" << stress.tilesBuilt << ",\"sampled_tiles\":" << stress.sampledTiles
<< ",\"samples\":" << stress.samples << ",\"mean_candidates\":" << stress.meanCandidateFeatures
<< ",\"p95_candidates\":" << stress.p95CandidateFeatures
<< ",\"list_build_total_ms\":" << stress.listBuildTotalMilliseconds
<< ",\"list_build_p95_ms\":" << stress.listBuildP95Milliseconds
<< ",\"before_p95_ms\":" << stress.beforeP95Milliseconds
<< ",\"after_p95_ms\":" << stress.afterP95Milliseconds
<< ",\"before_median_ms\":" << stress.beforeMedianMilliseconds
<< ",\"after_median_ms\":" << stress.afterMedianMilliseconds
<< ",\"exact_mismatches\":" << stress.exactMismatches
<< ",\"before_hash\":\"" << stress.beforeHash << "\",\"after_hash\":\"" << stress.afterHash
<< "\",\"negative_control\":" << (stress.negativeControlPassed ? "true" : "false") << "}\n}\n";
if (!today.negativeControlPassed || !stress.negativeControlPassed)
throw std::runtime_error("dropped-feature negative control was not detected");
if (today.exactMismatches || stress.exactMismatches)
throw std::runtime_error("tile feature evaluation changed canonical field values");
std::cout << "TILE_LIST_AUDIT_PASS\n";
return 0;
}
catch (const std::exception& exception)
{
std::cerr << "FLOATING_ISLAND_TILE_LIST_AUDIT_FAIL: " << exception.what() << '\n';
return 1;
}
}
// Surface finite features use the same scope as islands. Biome sites stay in their fixed local window.
// Les features finies de surface partagent la portee; les sites biomes restent dans leur fenetre locale.
void MeasureSurface(WorldAssemblyRecipeV1 recipe, WorldAssemblyStratumPlanV1 plan,
const char* label, bool stress)
{
if(stress)
{
const auto originals=plan.features;
for(int copy=1;copy<64;++copy)
for(auto feature:originals)
{
feature.stableId=SyntheticId(static_cast<std::uint32_t>(plan.features.size()),0x53555246);
for(int axis=0;axis<2;++axis)
{
const auto delta=(axis==0 ? copy%8 : copy/8)*512*Q;
feature.minimumSubvoxels[axis]+=delta;
feature.maximumSubvoxels[axis]+=delta;
}
plan.features.push_back(feature);
}
}
if(stress)
{
// One composed stress case also exercises XY column writers at unrelated Z values.
// Une fixture composee couvre aussi les modificateurs XY a des altitudes independantes.
const auto& shelf=plan.features.front();
WorldAssemblyFeatureV1 cliff;
cliff.stableId=SyntheticId(10000,0x434c494646);
cliff.type=1;
cliff.minimumSubvoxels={(shelf.minimumSubvoxels[0]+shelf.maximumSubvoxels[0])/2-8*Q,
(shelf.minimumSubvoxels[1]+shelf.maximumSubvoxels[1])/2-8*Q,0};
cliff.maximumSubvoxels={cliff.minimumSubvoxels[0]+16*Q,cliff.minimumSubvoxels[1]+16*Q,0};
cliff.parametersSubvoxels={8*Q,8*Q,0,0};
auto cap=cliff;
cap.stableId=SyntheticId(10001,0x434150);
cap.type=3;
cap.parametersSubvoxels={4*Q,32*Q,8*Q,0};
plan.features.push_back(cliff);
plan.features.push_back(cap);
}
if(stress) plan.contentHash ^= 0x5354524553534341ull; // Separate validation cache epochs. / Epoques distinctes.
Heightfield2D field(recipe,plan);
std::vector<double> fullTimes,candidateTimes,buildTimes,counts,fullColumnTimes,candidateColumnTimes;
std::vector<std::int64_t> fullValues,candidateValues;
std::uint64_t mismatches=0;
bool negative=false;
if(plan.features.empty())throw std::runtime_error("surface has no finite features to audit");
// 64 bounded tile probes, spanning each feature and its faces, including normal halos.
for(int tile=0;tile<64;++tile)
{
const auto& feature=plan.features[static_cast<std::size_t>(tile)%plan.features.size()];
const std::int64_t x=(feature.minimumSubvoxels[0]+feature.maximumSubvoxels[0])/2;
const std::int64_t y=(feature.minimumSubvoxels[1]+feature.maximumSubvoxels[1])/2;
const auto column=field.point(x,y,0);
const std::int64_t z=(tile%3==0) ? column.groundHeightSubvoxels :
(tile%3==1 ? column.capHeightSubvoxels :
(feature.minimumSubvoxels[2]+feature.maximumSubvoxels[2])/2);
const std::array<std::int64_t,3> lo{x-17*Q,y-17*Q,z-17*Q};
const std::array<std::int64_t,3> hi{x+17*Q,y+17*Q,z+17*Q};
const auto buildStart=Clock::now();
WorldAssemblyFeatureQueryV1 query{field.recipe(),field.plan(),field.buildFeatureTileList(lo,hi)};
buildTimes.push_back(std::chrono::duration<double,std::milli>(Clock::now()-buildStart).count());
counts.push_back(static_cast<double>(query.features.featureIndices.size()));
auto sample=[&](std::vector<std::int64_t>& values)
{
for(int iz=0;iz<9;++iz)for(int iy=0;iy<9;++iy)for(int ix=0;ix<9;++ix)
{
const auto p=field.point(lo[0]+ix*4*Q,lo[1]+iy*4*Q,lo[2]+iz*4*Q);
values.push_back(p.densitySubvoxels);
values.push_back(p.groundHeightSubvoxels);
values.push_back(p.capHeightSubvoxels);
values.push_back(p.biomeId);
}
};
// Change only the local cache epoch: both timed paths start with cold columns.
// Epoch de cache local seulement : les deux parcours partent de colonnes froides.
++plan.contentHash;
Heightfield2D::resetThreadColumnCacheStats();
const auto start=Clock::now();sample(fullValues);
fullTimes.push_back(std::chrono::duration<double,std::milli>(Clock::now()-start).count());
fullColumnTimes.push_back(Heightfield2D::threadColumnCacheStats().columnComputeMilliseconds);
++plan.contentHash;
Heightfield2D::resetThreadColumnCacheStats();
const auto candidateStart=Clock::now();
{const ScopedWorldAssemblyFeatureQueriesV1 scope(std::span(&query,1),true);sample(candidateValues);}
candidateTimes.push_back(std::chrono::duration<double,std::milli>(Clock::now()-candidateStart).count());
candidateColumnTimes.push_back(Heightfield2D::threadColumnCacheStats().columnComputeMilliseconds);
if(!negative)
{
if (!query.features.featureIndices.empty())
query.features.featureIndices.erase(query.features.featureIndices.begin());
std::vector<std::int64_t> bad;
++plan.contentHash;
{const ScopedWorldAssemblyFeatureQueriesV1 scope(std::span(&query,1),true);sample(bad);}
const auto offset=fullValues.size()-bad.size();
for(std::size_t i=0;i<bad.size();++i)if(bad[i]!=fullValues[offset+i])negative=true;
++plan.contentHash; // Discard deliberately poisoned negative-control columns. / Purger le controle negatif.
}
}
for(std::size_t i=0;i<fullValues.size();++i)if(fullValues[i]!=candidateValues[i])++mismatches;
std::cout<<"surface label="<<label<<" forced_candidates=1 features="<<plan.features.size()
<<" values="<<fullValues.size()<<" full_hash="<<Hex64(HashValues(fullValues))
<<" candidate_hash="<<Hex64(HashValues(candidateValues))
<<" mismatches="<<mismatches<<" negative_control="<<negative
<<" full_p95_ms="<<Percentile95(fullTimes)<<" candidate_p95_ms="<<Percentile95(candidateTimes)
<<" build_p95_ms="<<Percentile95(buildTimes)<<" candidates_p95="<<Percentile95(counts)
<<" full_column_p95_ms="<<Percentile95(fullColumnTimes)
<<" candidate_column_p95_ms="<<Percentile95(candidateColumnTimes)<<'\n';
if(mismatches||!negative)throw std::runtime_error("surface candidate proof failed");
}
int main(int argc,char** argv)
{
try
{
if(argc!=4)throw std::runtime_error("usage: audit owner_island_assembly owner_surface_assembly results_dir");
const auto read=[](const char* path)
{std::ifstream input(path);return std::string(std::istreambuf_iterator<char>(input),{});};
std::string error;
auto islands=WorldAssemblyManifest::FromJson(read(argv[1]),error);
if(!islands||islands->floatingIslands.empty())throw std::runtime_error("island assembly: "+error);
const auto islandPath=std::filesystem::path(argv[3])/"owner_island_manifest.json";
{std::ofstream output(islandPath);output<<islands->floatingIslands.front().manifest.ToJson();}
std::string exe=argv[0],islandFile=islandPath.string();
std::string resultFile=(std::filesystem::path(argv[3])/"island_tile_lists.json").string();
char* islandArgs[]{exe.data(),islandFile.data(),resultFile.data()};
if(IslandAuditMain(3,islandArgs)!=0)return 1;
auto surface=WorldAssemblyManifest::FromJson(read(argv[2]),error);
if(!surface||surface->plan.strates.empty())throw std::runtime_error("surface assembly: "+error);
MeasureSurface(surface->recipe,surface->plan.strates.front(),"owner",false);
MeasureSurface(surface->recipe,surface->plan.strates.front(),"stress64",true);
std::cout<<"SHARED_FEATURE_AUDIT_PASS\n";
return 0;
}
catch(const std::exception& e){std::cerr<<"SHARED_FEATURE_AUDIT_FAIL: "<<e.what()<<'\n';return 1;}
}
@@ -0,0 +1,224 @@
{
"field_audit": [
{
"islands": 4,
"connectors": 3,
"forced_candidates": 1,
"list_tiles_built": 108,
"sampled_tiles": 64,
"sample_count": 64000,
"mean_candidates": 2.63158,
"p95_candidates": 6,
"list_build_total_ms": 0.0766,
"list_build_p95_ms": 0.0012,
"full_tile_p95_ms": 0.9803,
"candidate_tile_p95_ms": 1.0194,
"full_tile_median_ms": 0.85265,
"candidate_tile_median_ms": 0.88875,
"exact_mismatches": 0,
"full_hash": "cb94db3ca317d001",
"candidate_hash": "cb94db3ca317d001",
"negative_control_detected": 1
},
{
"islands": 256,
"connectors": 240,
"forced_candidates": 1,
"list_tiles_built": 3362,
"sampled_tiles": 64,
"sample_count": 64000,
"mean_candidates": 3.31231,
"p95_candidates": 4,
"list_build_total_ms": 216.615,
"list_build_p95_ms": 0.0803,
"full_tile_p95_ms": 110.57,
"candidate_tile_p95_ms": 7.3619,
"full_tile_median_ms": 109.41,
"candidate_tile_median_ms": 4.12255,
"exact_mismatches": 0,
"full_hash": "d83bac7778f3d62a",
"candidate_hash": "d83bac7778f3d62a",
"negative_control_detected": 1
},
{
"label": "owner",
"forced_candidates": 1,
"features": 1,
"values": 186624,
"full_hash": "4207efd4ac869108",
"candidate_hash": "4207efd4ac869108",
"mismatches": 0,
"negative_control": 1,
"full_p95_ms": 0.4748,
"candidate_p95_ms": 0.4559,
"build_p95_ms": 0.0003,
"candidates_p95": 1,
"full_column_p95_ms": 0.3679,
"candidate_column_p95_ms": 0.3693
},
{
"label": "stress64",
"forced_candidates": 1,
"features": 66,
"values": 186624,
"full_hash": "f1c6ea123284cc38",
"candidate_hash": "f1c6ea123284cc38",
"mismatches": 0,
"negative_control": 1,
"full_p95_ms": 0.7084,
"candidate_p95_ms": 0.4078,
"build_p95_ms": 0.0011,
"candidates_p95": 1,
"full_column_p95_ms": 0.3699,
"candidate_column_p95_ms": 0.3552
}
],
"default_tile_audit": [
{
"label": "islands",
"lod": 0,
"tiles": 8,
"hashes_identical": 1,
"full_p95_ms": 94.9835,
"candidate_p95_ms": 95.0935,
"list_p95_ms": 0.0021,
"full_shell_p95_ms": 0.0002,
"candidate_shell_p95_ms": 0.0003
},
{
"label": "islands",
"lod": 1,
"tiles": 8,
"hashes_identical": 1,
"full_p95_ms": 87.2796,
"candidate_p95_ms": 87.5774,
"list_p95_ms": 0.0035,
"full_shell_p95_ms": 0.0005,
"candidate_shell_p95_ms": 0.0003
},
{
"label": "islands",
"lod": 7,
"tiles": 8,
"hashes_identical": 1,
"full_p95_ms": 20.4421,
"candidate_p95_ms": 20.4088,
"list_p95_ms": 0.0017,
"full_shell_p95_ms": 16.9176,
"candidate_shell_p95_ms": 16.8824
},
{
"label": "islands",
"lod": 8,
"tiles": 8,
"hashes_identical": 1,
"full_p95_ms": 73.0376,
"candidate_p95_ms": 72.9233,
"list_p95_ms": 0.0016,
"full_shell_p95_ms": 69.9303,
"candidate_shell_p95_ms": 69.8442
},
{
"label": "surface",
"lod": 0,
"tiles": 8,
"hashes_identical": 1,
"full_p95_ms": 11.2377,
"candidate_p95_ms": 11.1781,
"list_p95_ms": 0.0006,
"full_shell_p95_ms": 0.0001,
"candidate_shell_p95_ms": 0.0001
},
{
"label": "surface",
"lod": 1,
"tiles": 8,
"hashes_identical": 1,
"full_p95_ms": 7.3458,
"candidate_p95_ms": 7.3544,
"list_p95_ms": 0.0007,
"full_shell_p95_ms": 0.0001,
"candidate_shell_p95_ms": 0.0001
},
{
"label": "surface",
"lod": 7,
"tiles": 8,
"hashes_identical": 1,
"full_p95_ms": 32.7389,
"candidate_p95_ms": 32.3814,
"list_p95_ms": 0.0006,
"full_shell_p95_ms": 26.3776,
"candidate_shell_p95_ms": 26.021
},
{
"label": "surface",
"lod": 8,
"tiles": 8,
"hashes_identical": 1,
"full_p95_ms": 168.5449,
"candidate_p95_ms": 168.0627,
"list_p95_ms": 0.0008,
"full_shell_p95_ms": 135.9596,
"candidate_shell_p95_ms": 136.31
}
],
"island_placement": [
{
"label": "FloatingIslands",
"authored_full_p95_ms": 7.0706,
"authored_candidate_p95_ms": 0.2385,
"authored_active_batch_full_ms": 412.95957,
"authored_active_batch_candidate_ms": 3.84022,
"populated_cells": 14,
"populated_placements": 572,
"populated_full_p95_ms": 53.9875,
"populated_candidate_p95_ms": 2.1501,
"exact_transform_comparisons": 572,
"anchor_checks": 572,
"max_density_residual": 0.035156
}
],
"island_active": [
{
"label": "FloatingIslands",
"cells": 64,
"placements": 572,
"full_batch_ms": 3270.4782,
"candidate_batch_ms": 28.43621,
"merge_ms": 0.06231,
"apply_ms": 0.6068,
"applied_instances": 572,
"full_total_cpu_ms": 3271.14731,
"candidate_total_cpu_ms": 29.10532,
"rhi": "null"
}
],
"hash_matrices": [
{
"worker_hash_matrix": "1/8/16",
"stable": 1,
"density": "0x4dcfb37585d7d986",
"mesh": "0x725a18374c5623de",
"payload": "0xfb345d8cb319d18b",
"topology": "0x76f2846be8efb5f4"
},
{
"worker_hash_matrix": "1/8/16",
"stable": 1,
"density": "0x0cd2a1a4152ee4aa",
"mesh": "0x17775c835711e5a3",
"payload": "0xa27224958fc27d7b",
"topology": "0x3821fea90b134ce4"
}
],
"limits": {
"unreal_core_limit": 12,
"max_ubt_actions": 8,
"max_standalone_compile_actions": 4,
"process_limit_gib": 8,
"launch_memory_gate_gib": 4,
"step_limit_seconds": 900
},
"known_failed_run": "islands_hism_fixture_failed.log: exit 1; fixture collided with live landmark suppression; isolated fixture passes."
}
@@ -0,0 +1,4 @@
{
"baseline":{"islands":4,"connectors":3,"tiles_built":108,"sampled_tiles":64,"samples":64000,"mean_candidates":2.63157894737,"p95_candidates":6,"list_build_total_ms":0.0766,"list_build_p95_ms":0.0012,"before_p95_ms":0.9803,"after_p95_ms":1.0194,"before_median_ms":0.85265,"after_median_ms":0.88875,"exact_mismatches":0,"before_hash":"cb94db3ca317d001","after_hash":"cb94db3ca317d001","negative_control":true},
"stress":{"islands":256,"connectors":240,"tiles_built":3362,"sampled_tiles":64,"samples":64000,"mean_candidates":3.31231409875,"p95_candidates":4,"list_build_total_ms":216.6153,"list_build_p95_ms":0.0803,"before_p95_ms":110.5695,"after_p95_ms":7.3619,"before_median_ms":109.40955,"after_median_ms":4.12255,"exact_mismatches":0,"before_hash":"d83bac7778f3d62a","after_hash":"d83bac7778f3d62a","negative_control":true}
}
@@ -0,0 +1 @@
{"schema_version":1,"content_hash":"87222553193002d8","recipe":{"schema_version":1,"units_per_voxel":256,"owner_voxel_size_microns":125000,"core_voxel_size_microns":250000,"owner_to_core_scale_numerator":1,"owner_to_core_scale_denominator":2,"seed":0,"origin_x_q":0,"origin_y_q":0,"center_top_z_q":-126976,"layout_min_z_q":-258048,"layout_max_z_q":4096,"primary_island_count":3,"island_density_q16":32768,"spacing_q":14336,"minimum_radius_q":3072,"maximum_radius_q":6144,"thickness_ratio_q16":39322,"vertical_jitter_q16":39322,"top_flatten_q16":39322,"taper_end_min_q16":29491,"taper_end_max_q16":45875,"geometry_version":2,"crust_depth_bonus_q16":9830,"root_depth_ratio_q16":49152,"blend_radius_q":768,"contour_warp_q":1613,"roughness_q":384,"boundary_seal_q":512,"bridge_half_width_q":768,"bridge_thickness_q":1024,"route_constraints":{"version":1,"capsule_radius_q":348,"capsule_height_q":1802,"maximum_grade_q16":17560,"sweep_spacing_q":128,"support_probe_radius_q":230,"support_search_depth_q":512,"minimum_clearance_q":128,"minimum_support_probes":3},"content_hash":"5e0370d9f95f1c15"},"plan":{"content_hash":"dbbda4932378047a","islands":[{"stable_id":"e6e461e18b3a9c87","ordinal":0,"center_q":[-10035,4422,-129150],"radius_q":3605,"top_half_q":721,"top_z_q":-128429,"bottom_z_q":-133837,"taper_end_q16":41663,"bounds_min_q":[-17941,-3484,-136525],"bounds_max_q":[-2129,12328,-125741]},{"stable_id":"2bc1820baba047f6","ordinal":1,"center_q":[0,-5178,-127714],"radius_q":3693,"top_half_q":738,"top_z_q":-126976,"bottom_z_q":-132516,"taper_end_q16":33506,"bounds_min_q":[-7994,-13172,-135204],"bounds_max_q":[7994,2816,-124288]},{"stable_id":"adc259cbe2a395e9","ordinal":2,"center_q":[10035,4681,-127887],"radius_q":5181,"top_half_q":1036,"top_z_q":-126851,"bottom_z_q":-134623,"taper_end_q16":39164,"bounds_min_q":[553,-4801,-137311],"bounds_max_q":[19517,14163,-124163]},{"stable_id":"7855fb022cc561c9","ordinal":3,"center_q":[-6451,-19514,-124414],"radius_q":5840,"top_half_q":1168,"top_z_q":-123246,"bottom_z_q":-132006,"taper_end_q16":33440,"bounds_min_q":[-16592,-29655,-134694],"bounds_max_q":[3690,-9373,-120558]}],"cluster_edges":[{"stable_id":"eaa5dd310e846520","from_island_id":"e6e461e18b3a9c87","to_island_id":"2bc1820baba047f6","start_q":[-8680,3126,-128554],"end_q":[-1388,-3851,-126819],"half_width_q":768,"thickness_q":1024,"bounds_min_q":[-10600,-5771,-132778],"bounds_max_q":[532,5046,-125667],"sharp_tag_ready":true},{"stable_id":"034023599b466c51","from_island_id":"2bc1820baba047f6","to_island_id":"adc259cbe2a395e9","start_q":[1370,-3832,-126951],"end_q":[8113,2793,-126831],"half_width_q":768,"thickness_q":1024,"bounds_min_q":[-550,-5752,-131175],"bounds_max_q":[10033,4713,-125679],"sharp_tag_ready":true},{"stable_id":"4962fa885545fb26","from_island_id":"2bc1820baba047f6","to_island_id":"7855fb022cc561c9","start_q":[-788,-6929,-126846],"end_q":[-5205,-16745,-123222],"half_width_q":768,"thickness_q":1024,"bounds_min_q":[-7125,-18665,-131070],"bounds_max_q":[1132,-5009,-122070],"sharp_tag_ready":true}],"route":[{"feature_id":"2e805a1f1f778cbb","position_q":[-10035,4422,-127367],"radius_q":1024},{"feature_id":"5be3b2b1e01722ea","position_q":[-9318,3736,-127414],"radius_q":1024},{"feature_id":"ab4eda612c1f03b5","position_q":[-8601,3051,-127336],"radius_q":1024},{"feature_id":"ba2fd2b050d0dd8c","position_q":[-7885,2365,-127288],"radius_q":1024},{"feature_id":"fc5829ee129fdc4f","position_q":[-7168,1679,-127113],"radius_q":1024},{"feature_id":"1393bb00329a9d5e","position_q":[-6451,993,-126951],"radius_q":1024},{"feature_id":"1dabc9715c93e2c9","position_q":[-5734,308,-126760],"radius_q":1024},{"feature_id":"0ea2008a85e73050","position_q":[-5018,-378,-126563],"radius_q":1024},{"feature_id":"754f55a5f1465cf3","position_q":[-4301,-1064,-126408],"radius_q":1024},{"feature_id":"1b6752776b4a7d22","position_q":[-3584,-1749,-126236],"radius_q":1024},{"feature_id":"0ab3b704db76084d","position_q":[-2867,-2435,-126017],"radius_q":1024},{"feature_id":"bd6980911e0879c4","position_q":[-2150,-3121,-125838],"radius_q":1024},{"feature_id":"ad164d7d0c049d87","position_q":[-1434,-3807,-125738],"radius_q":1024},{"feature_id":"9661463acbada2d6","position_q":[-717,-4492,-125928],"radius_q":1024},{"feature_id":"280a96547fadb0a1","position_q":[0,-5178,-125908],"radius_q":1024},{"feature_id":"531455b42fc13108","position_q":[717,-4474,-125939],"radius_q":1024},{"feature_id":"55d04c20849d7fcb","position_q":[1434,-3770,-125798],"radius_q":1024},{"feature_id":"ac4ebe80860dbdfa","position_q":[2150,-3065,-125893],"radius_q":1024},{"feature_id":"ff942855bf8e3fc5","position_q":[2867,-2361,-125886],"radius_q":1024},{"feature_id":"e6b8bba6c6acaf9c","position_q":[3584,-1657,-125861],"radius_q":1024},{"feature_id":"ae47379bbd4f7fdf","position_q":[4301,-953,-125839],"radius_q":1024},{"feature_id":"98779442384a30ee","position_q":[5018,-249,-125844],"radius_q":1024},{"feature_id":"f4c7757b2ee9fe19","position_q":[5734,456,-125814],"radius_q":1024},{"feature_id":"3ea33fae0fLine truncated
@@ -0,0 +1,9 @@
attempt=1 exit=0 elapsed_s=8.7570529 peak_private_bytes=68067328
Final timing replay: no overlapping BuildHost build or Unreal process.
attempt=1 exit=0 elapsed_s=8.0385819 peak_private_bytes=66658304
Final source timing replay: redundant scoped validation removed; idle build/Unreal.
attempt=1 exit=0 elapsed_s=7.9966612 peak_private_bytes=66658304
Final cost-hint source: force candidates for proof; no overlapping build/Unreal.
attempt=1 exit=0 elapsed_s=8.0365247 peak_private_bytes=65884160
Final composed stress: 64 shelves plus XY cliff/cap writers; no overlapping build/Unreal.
attempt=1 exit=0 elapsed_s=7.8703235 peak_private_bytes=66654208
+22 -2
View File
@@ -12,6 +12,7 @@ param(
[switch]$MismatchedManifestFallback, [switch]$MismatchedManifestFallback,
[switch]$CoreOffTest, [switch]$CoreOffTest,
[switch]$CorePlanOnly, [switch]$CorePlanOnly,
[switch]$SharedFeatureAuditOnly,
[int]$CoreTestSeed = [int]::MinValue [int]$CoreTestSeed = [int]::MinValue
) )
@@ -34,7 +35,8 @@ $DerivedDataCache = Join-Path $PluginRoot 'Saved\BuildHost\VFCoreWorldDDC'
$ZenData = Join-Path $OutputRoot 'ZenData' $ZenData = Join-Path $OutputRoot 'ZenData'
$EditorCmd = 'E:\Program Files\Epic Games\UE_5.7\Engine\Binaries\Win64\UnrealEditor-Cmd.exe' $EditorCmd = 'E:\Program Files\Epic Games\UE_5.7\Engine\Binaries\Win64\UnrealEditor-Cmd.exe'
$MemoryLimitBytes = 8GB $MemoryLimitBytes = 8GB
$CrashRoots = @((Join-Path $HostRoot 'Saved\Crashes'), (Join-Path $StagePlugin 'Saved\Crashes')) $CrashRoots = @((Join-Path $HostRoot 'Saved\Crashes'), (Join-Path $StagePlugin 'Saved\Crashes'),
(Join-Path $UserDirectory 'Saved\Crashes'))
$CoreSizeSuffix = '' $CoreSizeSuffix = ''
if ([Math]::Abs($CoreVoxelSizeCm - 25.0) -gt 0.000001) { if ([Math]::Abs($CoreVoxelSizeCm - 25.0) -gt 0.000001) {
$CoreSizeText = $CoreVoxelSizeCm.ToString('0.####', [Globalization.CultureInfo]::InvariantCulture) $CoreSizeText = $CoreVoxelSizeCm.ToString('0.####', [Globalization.CultureInfo]::InvariantCulture)
@@ -127,6 +129,10 @@ if ($HasManifestOverride -and -not (Test-Path -LiteralPath $WorldAssemblyManifes
} }
if ($MismatchedManifestFallback) { $RunName = 'mismatched_fallback' } if ($MismatchedManifestFallback) { $RunName = 'mismatched_fallback' }
if ($SharedFeatureAuditOnly) {
if ($SelectedModeCount -ne 0) { throw '-SharedFeatureAuditOnly uses the SurfaceWorld owner route.' }
$RunName = 'shared_feature_audit'
}
$foreignEditors = @(Get-Process -ErrorAction SilentlyContinue | $foreignEditors = @(Get-Process -ErrorAction SilentlyContinue |
Where-Object { $_.ProcessName -like 'UnrealEditor*' }) Where-Object { $_.ProcessName -like 'UnrealEditor*' })
@@ -221,7 +227,7 @@ $CoreOffTestFlag = if ($CoreOffTest) { 1 } else { 0 }
$CorePlanOnlyFlag = if ($CorePlanOnly) { 1 } else { 0 } $CorePlanOnlyFlag = if ($CorePlanOnly) { 1 } else { 0 }
$BaseArguments = @( $BaseArguments = @(
('-project="{0}"' -f $HostProject), ('-project="{0}"' -f $HostProject),
'-nullrhi', '-unattended', '-nop4', '-nosplash', '-NoSound', '-nullrhi', '-unattended', '-nop4', '-nosplash', '-NoSound', '-corelimit=12',
'-stdout', '-FullStdOutLogOutput', '-forcelogflush', '-NoZenAutoLaunch', '-stdout', '-FullStdOutLogOutput', '-forcelogflush', '-NoZenAutoLaunch',
'-notraceserver', '-nocrashreports', '-ddc=NoZenLocalFallback', '-notraceserver', '-nocrashreports', '-ddc=NoZenLocalFallback',
('-userdir="{0}"' -f $UserDirectory), ('-userdir="{0}"' -f $UserDirectory),
@@ -241,6 +247,7 @@ $BaseArguments = @(
("-corehybridfixturesonly=$HybridFixturesOnlyFlag"), ("-corehybridfixturesonly=$HybridFixturesOnlyFlag"),
('-out="{0}"' -f $OutputDirectory) ('-out="{0}"' -f $OutputDirectory)
) )
if ($SharedFeatureAuditOnly) { $BaseArguments += '-corefeatureauditonly' }
if ($HasManifestOverride) { if ($HasManifestOverride) {
$BaseArguments += ('-coreworldassembly="{0}"' -f $StagedWorldAssembly) $BaseArguments += ('-coreworldassembly="{0}"' -f $StagedWorldAssembly)
} }
@@ -285,6 +292,9 @@ try {
-RedirectStandardOutput $StdoutPath -RedirectStandardError $StderrPath ` -RedirectStandardOutput $StdoutPath -RedirectStandardError $StderrPath `
-WindowStyle Hidden -PassThru -WindowStyle Hidden -PassThru
$Process.PriorityClass = [Diagnostics.ProcessPriorityClass]::Normal $Process.PriorityClass = [Diagnostics.ProcessPriorityClass]::Normal
# W16 is a logical determinism partition; at most 12 logical CPUs execute host work.
# W16 reste un partitionnement logique; limiter l'execution hote a 12 processeurs logiques.
$Process.ProcessorAffinity = [IntPtr]0xFFF
$StartedUtc = [datetime]::UtcNow $StartedUtc = [datetime]::UtcNow
$PeakPrivateBytes = [int64]0 $PeakPrivateBytes = [int64]0
while (-not $Process.HasExited) { while (-not $Process.HasExited) {
@@ -351,6 +361,16 @@ if (-not (Test-Path -LiteralPath $FinalLog -PathType Leaf)) {
$FinalLog = Join-Path $LogDirectory "CoreWorldValidation-$RunName.retry.log" $FinalLog = Join-Path $LogDirectory "CoreWorldValidation-$RunName.retry.log"
} }
$Text = Get-Content -LiteralPath $FinalLog -Raw $Text = Get-Content -LiteralPath $FinalLog -Raw
if ($SharedFeatureAuditOnly) {
if ($Text -notmatch '\[VoxelForgeSharedFeatureCost\] pass complete=1 warmed_routes=1') {
throw "Shared feature audit did not pass: $FinalLog"
}
if (@(Get-NewCrashFolders).Count -gt 0) { throw 'Shared feature audit produced a new crash folder.' }
Write-Host 'VOXELFORGE_SHARED_FEATURE_AUDIT_PASS warmed_routes=1'
Write-Host "Core actor log: $FinalLog"
Write-Host "Peak commandlet private bytes sampled: $PeakPrivateBytes"
return
}
if ($CoreFallbackTest) { if ($CoreFallbackTest) {
foreach ($Marker in @( foreach ($Marker in @(
'[VoxelForgeCoreFallbackTest] fallback=PASS core_disabled=1 legacy_generator=1', '[VoxelForgeCoreFallbackTest] fallback=PASS core_disabled=1 legacy_generator=1',