wip(assembler): round S10 -- LOD8 horizon sampled at MC spacing: LOD8 worker time 18.8 s -> 4.3 s

Planar far horizon samples the LOD8 interior at the MC cell spacing, keeping
128-voxel boundary strips; far recipe v3. LOD8 p95 59.8 -> 13.9 ms, spawn
worker-seconds ~41 -> 26.9, full set 7.8 -> 7.1 s, ring 0.49 s, LOD0 1.43 s,
40 m/s ring 75/75. S3/S4 2 cm agreement, 100% cap coverage, W1/W8/W12 and
W1/W8/W16 determinism pass. Report: Standalone/Research/WorldAssemblerM3/Round14.

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-19 12:23:18 +02:00
1 parent e7586a1256
commit b90d63d0f7
14 files changed
+616 -86

No files matched your search

@@ -125,6 +125,11 @@ namespace VoxelForgeCore
std::uint64_t columnCacheMisses = 0;
double classificationMilliseconds = 0.0;
double columnComputeMilliseconds = 0.0;
double columnEvaluationExclusiveMilliseconds = 0.0;
double marchingCubesExclusiveMilliseconds = 0.0;
double groundRefinementExclusiveMilliseconds = 0.0;
double capRefinementExclusiveMilliseconds = 0.0;
double meshAssemblyMilliseconds = 0.0;
double meshingMilliseconds = 0.0;
double featureListMilliseconds = 0.0;
double farShellMilliseconds = 0.0;
@@ -146,6 +151,9 @@ namespace VoxelForgeCore
std::uint64_t capRefinedCells = 0;
std::uint64_t capRefinedTriangles = 0;
bool capRefined = false;
std::uint64_t columnEvaluationWorkerHash = 0;
bool columnCoordinateCaptureComplete = true;
std::vector<std::array<std::int64_t, 2>> evaluatedColumnCoordinates;
};
struct TileMesh
@@ -628,6 +636,9 @@ namespace VoxelForgeCore
std::uint64_t columnHits = 0;
std::uint64_t columnMisses = 0;
double columnComputeMilliseconds = 0.0;
bool captureCoordinates = false;
bool coordinateCaptureComplete = true;
std::vector<std::array<std::int64_t, 2>> evaluatedCoordinates;
};
struct FieldInfluenceBounds
@@ -676,8 +687,8 @@ namespace VoxelForgeCore
std::int64_t minimumYSubvoxels, std::int64_t minimumZSubvoxels,
std::int64_t maximumXSubvoxels, std::int64_t maximumYSubvoxels,
std::int64_t maximumZSubvoxels, std::int32_t stepVoxels) const noexcept;
static void resetThreadColumnCacheStats() noexcept;
static HeightfieldColumnCacheStats threadColumnCacheStats() noexcept;
static void resetThreadColumnCacheStats(bool captureCoordinates = false) noexcept;
static const HeightfieldColumnCacheStats& threadColumnCacheStats() noexcept;
FieldInterval interval(std::int64_t minimumXSubvoxels,
std::int64_t minimumYSubvoxels,
std::int64_t minimumZSubvoxels,
@@ -1002,11 +1013,17 @@ namespace VoxelForgeCore
// Validation control only; never participates in recipe/world identity.
// Controle de validation seulement, sans effet sur l'identite du monde.
bool useAssemblyFeatureQueries = true;
// Opt-in diagnostic only; records XY columns evaluated by this LOD8 tile.
// Diagnostic seulement; enregistre les colonnes XY evaluees par cette tuile LOD8.
bool captureColumnCoordinates = false;
// LOD8-only planar horizon interior grid; zero follows the MC step, 128 restores S9.
// Grille interieure LOD8 seulement; zero suit la maille MC, 128 restaure S9.
std::int32_t lod8HorizonInteriorStepVoxels = 0;
// Bump when the distant cap representation changes. LOD4-8 mesh hashes are salted;
// density and LOD0-3 hashes remain unchanged.
// Incremente quand le plafond lointain change. Les hashes LOD4-8 incluent cette version;
// densite et hashes LOD0-3 restent inchanges.
std::uint32_t farHorizonVersion = 2;
std::uint32_t farHorizonVersion = 3;
};
class VFCORE_API World
+190 -71
View File
@@ -11,6 +11,8 @@
#include <unordered_map>
#include <unordered_set>
#include <set>
#include <functional>
#include <thread>
namespace vfcore::detail
{
@@ -247,6 +249,45 @@ namespace VoxelForgeCore
bool Refined = false;
};
std::vector<int32> BuildNativeHorizonGridCoordinates(int32 TileExtent,int32 InteriorStep)
{
constexpr int32 BoundaryStep=128;
std::vector<int32> Coordinates;
if(TileExtent<=0)return Coordinates;
if(InteriorStep<=BoundaryStep)
{
Coordinates.reserve(static_cast<std::size_t>(TileExtent/InteriorStep)+1u);
for(int32 Position=0;Position<TileExtent;Position+=InteriorStep)
Coordinates.push_back(Position);
if(Coordinates.empty()||Coordinates.back()!=TileExtent)Coordinates.push_back(TileExtent);
return Coordinates;
}
if(TileExtent<2*InteriorStep)
{
Coordinates.reserve(static_cast<std::size_t>(TileExtent/BoundaryStep)+1u);
for(int32 Position=0;Position<TileExtent;Position+=BoundaryStep)
Coordinates.push_back(Position);
if(Coordinates.empty()||Coordinates.back()!=TileExtent)Coordinates.push_back(TileExtent);
return Coordinates;
}
// Keep the shared 128-voxel boundary lattice for LOD transitions while the LOD8
// interior follows the coarse MC cell spacing. Preserve the exact same coordinates
// in neighboring tiles, independent of which worker builds either side.
// Garder la bordure commune de 128 voxels, avec un interieur a la maille MC de LOD8.
for(int32 Position=0;Position<InteriorStep&&Position<TileExtent;Position+=BoundaryStep)
Coordinates.push_back(Position);
if(Coordinates.empty()||Coordinates.back()!=InteriorStep)
Coordinates.push_back(InteriorStep);
for(int32 Position=InteriorStep+InteriorStep;Position<TileExtent-InteriorStep;
Position+=InteriorStep)
Coordinates.push_back(Position);
for(int32 Position=TileExtent-InteriorStep;Position<TileExtent;Position+=BoundaryStep)
if(Coordinates.empty()||Coordinates.back()!=Position)Coordinates.push_back(Position);
if(Coordinates.empty()||Coordinates.back()!=TileExtent)Coordinates.push_back(TileExtent);
return Coordinates;
}
FVector NormalizeCapNormal(const FVector& Value)
{
const double Length=std::sqrt(Value.X*Value.X+Value.Y*Value.Y+Value.Z*Value.Z);
@@ -299,17 +340,28 @@ namespace VoxelForgeCore
}
FNativeCapRefinementStats RefineFarNativeGround(const FStandaloneOwnerWorld& Owner,
const WorldAssemblyManifest& Manifest,const FVoxelTileKey& Key,FVoxelMeshData& Mesh)
const WorldAssemblyManifest& Manifest,const FVoxelTileKey& Key,int32 MeshStepVoxels,
int32 InteriorStepOverride,FVoxelMeshData& Mesh)
{
FNativeCapRefinementStats Stats;
if(Key.Level<5||!Owner.Generator)return Stats;
const int32 GridStepVoxels=std::min(128,1<<Key.Level);
constexpr int32 BoundaryStepVoxels=128;
constexpr int64 Scale=WorldAssemblySubvoxelsPerVoxel;
const int64 GridStepSubvoxels=static_cast<int64>(GridStepVoxels)*Scale;
const FIntVector TileOrigin=Key.OriginVoxels();
const int32 TileExtent=Key.ExtentVoxels();
const int32 RequestedStep=Key.Level==8
?(InteriorStepOverride>0?InteriorStepOverride:MeshStepVoxels)
:std::min(BoundaryStepVoxels,1<<Key.Level);
const int32 GridStepVoxels=Key.Level==8
?((RequestedStep<BoundaryStepVoxels||RequestedStep%BoundaryStepVoxels!=0
||TileExtent<=0||TileExtent%RequestedStep!=0)
?BoundaryStepVoxels:std::max(BoundaryStepVoxels,RequestedStep))
:RequestedStep;
if(TileExtent<=0||TileExtent%GridStepVoxels!=0)return Stats;
const int32 Cells=TileExtent/GridStepVoxels;
const std::vector<int32> GridCoordinates=
BuildNativeHorizonGridCoordinates(TileExtent,GridStepVoxels);
const int32 GridSize=static_cast<int32>(GridCoordinates.size());
if(GridSize<2)return Stats;
const double TileMinZ=static_cast<double>(TileOrigin.Z)*Scale;
const double TileMaxZ=static_cast<double>(TileOrigin.Z+TileExtent)*Scale;
const int32 OldSurfaceVertices=std::clamp(Mesh.NumSurfaceVertices,0,Mesh.Vertices.Num());
@@ -324,8 +376,9 @@ namespace VoxelForgeCore
struct FGroundProfile
{
int32 Cells=0,GridStep=0;
int32 GridStep=0;
double MinimumZ=0.0,MaximumZ=0.0;
std::vector<int32> Coordinates;
std::vector<double> Ground,Cap;
};
std::vector<FGroundProfile> GroundProfiles;
@@ -344,14 +397,14 @@ namespace VoxelForgeCore
FColor Color=FColor::White;
int32 VertexIndex=INDEX_NONE;
};
std::vector<FGridSample> Grid(static_cast<std::size_t>(Cells+1)*(Cells+1));
const auto GridIndex=[Cells](int32 X,int32 Y)
{return static_cast<std::size_t>(Y)*(Cells+1)+X;};
for(int32 Y=0;Y<=Cells;++Y)
for(int32 X=0;X<=Cells;++X)
std::vector<FGridSample> Grid(static_cast<std::size_t>(GridSize)*GridSize);
const auto GridIndex=[GridSize](int32 X,int32 Y)
{return static_cast<std::size_t>(Y)*GridSize+X;};
for(int32 Y=0;Y<GridSize;++Y)
for(int32 X=0;X<GridSize;++X)
{
const int64 XSub=(static_cast<int64>(TileOrigin.X)+static_cast<int64>(X)*GridStepVoxels)*Scale;
const int64 YSub=(static_cast<int64>(TileOrigin.Y)+static_cast<int64>(Y)*GridStepVoxels)*Scale;
const int64 XSub=(static_cast<int64>(TileOrigin.X)+GridCoordinates[X])*Scale;
const int64 YSub=(static_cast<int64>(TileOrigin.Y)+GridCoordinates[Y])*Scale;
const auto Column=Block.point(XSub,YSub,0);
auto& Sample=Grid[GridIndex(X,Y)];
Sample.Ground=static_cast<double>(Column.groundHeightSubvoxels);
@@ -364,13 +417,13 @@ namespace VoxelForgeCore
static_cast<uint8>(std::clamp(std::lround(Blend*255.0f),0l,255l)),
static_cast<uint8>(std::clamp(Neighbor,0,255)));
}
for(int32 Y=0;Y<=Cells;++Y)
for(int32 X=0;X<=Cells;++X)
for(int32 Y=0;Y<GridSize;++Y)
for(int32 X=0;X<GridSize;++X)
{
const int32 Left=std::max(0,X-1),Right=std::min(Cells,X+1);
const int32 Down=std::max(0,Y-1),Up=std::min(Cells,Y+1);
const double DX=static_cast<double>(Right-Left)*GridStepSubvoxels;
const double DY=static_cast<double>(Up-Down)*GridStepSubvoxels;
const int32 Left=std::max(0,X-1),Right=std::min(GridSize-1,X+1);
const int32 Down=std::max(0,Y-1),Up=std::min(GridSize-1,Y+1);
const double DX=static_cast<double>(GridCoordinates[Right]-GridCoordinates[Left])*Scale;
const double DY=static_cast<double>(GridCoordinates[Up]-GridCoordinates[Down])*Scale;
const double SlopeX=DX>0.0?(Grid[GridIndex(Right,Y)].Ground
-Grid[GridIndex(Left,Y)].Ground)/DX:0.0;
const double SlopeY=DY>0.0?(Grid[GridIndex(X,Up)].Ground
@@ -382,7 +435,7 @@ namespace VoxelForgeCore
}
FGroundProfile Profile;
Profile.Cells=Cells;Profile.GridStep=GridStepVoxels;
Profile.GridStep=GridStepVoxels;Profile.Coordinates=GridCoordinates;
Profile.MinimumZ=static_cast<double>(std::min(Plan.bottomChunkZ,Plan.topChunkZ))*CHUNK_SIZE;
Profile.MaximumZ=static_cast<double>(std::max(Plan.bottomChunkZ,Plan.topChunkZ)+1)*CHUNK_SIZE;
Profile.Ground.reserve(Grid.size());Profile.Cap.reserve(Grid.size());
@@ -392,25 +445,25 @@ namespace VoxelForgeCore
// Partager les sommets du profil entre cellules reduit le payload d'horizon.
// Share regular profile vertices across adjacent cells to bound horizon payload.
for(int32 Y=0;Y<=Cells;++Y)
for(int32 X=0;X<=Cells;++X)
for(int32 Y=0;Y<GridSize;++Y)
for(int32 X=0;X<GridSize;++X)
{
auto& Sample=Grid[GridIndex(X,Y)];
if(Sample.Ground>=Sample.Cap||Sample.Ground<TileMinZ||Sample.Ground>TileMaxZ)continue;
const int64 XSub=(static_cast<int64>(TileOrigin.X)+static_cast<int64>(X)*GridStepVoxels)*Scale;
const int64 YSub=(static_cast<int64>(TileOrigin.Y)+static_cast<int64>(Y)*GridStepVoxels)*Scale;
const int64 XSub=(static_cast<int64>(TileOrigin.X)+GridCoordinates[X])*Scale;
const int64 YSub=(static_cast<int64>(TileOrigin.Y)+GridCoordinates[Y])*Scale;
const double VoxelSizeCm=static_cast<double>(Owner.CoreVoxelSizeMicrons)/10000.0;
Sample.VertexIndex=static_cast<int32>(AddedVertices.size());
AddedVertices.emplace_back(static_cast<double>(TileOrigin.X+X*GridStepVoxels)*VoxelSizeCm,
static_cast<double>(TileOrigin.Y+Y*GridStepVoxels)*VoxelSizeCm,
AddedVertices.emplace_back(static_cast<double>(TileOrigin.X+GridCoordinates[X])*VoxelSizeCm,
static_cast<double>(TileOrigin.Y+GridCoordinates[Y])*VoxelSizeCm,
Sample.Ground/Scale*VoxelSizeCm);
AddedNormals.push_back(Sample.Normal);
if(bHasUVs)AddedUVs.emplace_back(static_cast<float>(XSub/Scale),static_cast<float>(YSub/Scale));
if(bHasColors)AddedColors.push_back(Sample.Color);
}
for(int32 Y=0;Y<Cells;++Y)
for(int32 X=0;X<Cells;++X)
for(int32 Y=0;Y+1<GridSize;++Y)
for(int32 X=0;X+1<GridSize;++X)
{
const int32 CellX[4]={X,X+1,X+1,X};
const int32 CellY[4]={Y,Y,Y+1,Y+1};
@@ -433,8 +486,8 @@ namespace VoxelForgeCore
{
const int32 GX=CellX[Corner],GY=CellY[Corner];
const auto& Sample=Grid[GridIndex(GX,GY)];
PolygonA[Corner].XSub=static_cast<double>(TileOrigin.X+GX*GridStepVoxels)*Scale;
PolygonA[Corner].YSub=static_cast<double>(TileOrigin.Y+GY*GridStepVoxels)*Scale;
PolygonA[Corner].XSub=static_cast<double>(TileOrigin.X+GridCoordinates[GX])*Scale;
PolygonA[Corner].YSub=static_cast<double>(TileOrigin.Y+GridCoordinates[GY])*Scale;
PolygonA[Corner].GroundSub=Sample.Ground;
PolygonA[Corner].CapSub=Sample.Cap;
PolygonA[Corner].ZSub=Sample.Ground;
@@ -487,16 +540,29 @@ namespace VoxelForgeCore
const auto InterpolateProfile=[](const std::vector<double>& Values,const FGroundProfile& Profile,
double LocalX,double LocalY)
{
const double GridX=std::clamp(LocalX/Profile.GridStep,0.0,static_cast<double>(Profile.Cells));
const double GridY=std::clamp(LocalY/Profile.GridStep,0.0,static_cast<double>(Profile.Cells));
const int32 X=std::min(Profile.Cells-1,static_cast<int32>(std::floor(GridX)));
const int32 Y=std::min(Profile.Cells-1,static_cast<int32>(std::floor(GridY)));
const double TX=GridX-X,TY=GridY-Y;
const std::size_t Width=static_cast<std::size_t>(Profile.Cells+1);
const std::size_t Width=Profile.Coordinates.size();
const auto FindCell=[&](double Local)
{
const double Clamped=std::clamp(Local,static_cast<double>(Profile.Coordinates.front()),
static_cast<double>(Profile.Coordinates.back()));
const auto Upper=std::upper_bound(Profile.Coordinates.begin(),Profile.Coordinates.end(),Clamped);
if(Upper==Profile.Coordinates.begin())return std::pair<std::size_t,double>{0u,0.0};
const std::size_t Index=std::min<std::size_t>(Width-2u,
static_cast<std::size_t>(Upper-Profile.Coordinates.begin()-1));
const double Start=Profile.Coordinates[Index];
const double Span=Profile.Coordinates[Index+1]-Profile.Coordinates[Index];
return std::pair<std::size_t,double>{Index,Span>0.0?(Clamped-Start)/Span:0.0};
};
const auto [X,TX]=FindCell(LocalX);
const auto [Y,TY]=FindCell(LocalY);
const auto At=[&](int32 PX,int32 PY){return Values[static_cast<std::size_t>(PY)*Width+PX];};
if(TX>=TY)
return At(X,Y)*(1.0-TX)+At(X+1,Y)*(TX-TY)+At(X+1,Y+1)*TY;
return At(X,Y)*(1.0-TY)+At(X+1,Y+1)*TX+At(X,Y+1)*(TY-TX);
return At(static_cast<int32>(X),static_cast<int32>(Y))*(1.0-TX)
+At(static_cast<int32>(X+1u),static_cast<int32>(Y))*(TX-TY)
+At(static_cast<int32>(X+1u),static_cast<int32>(Y+1u))*TY;
return At(static_cast<int32>(X),static_cast<int32>(Y))*(1.0-TY)
+At(static_cast<int32>(X+1u),static_cast<int32>(Y+1u))*TX
+At(static_cast<int32>(X),static_cast<int32>(Y+1u))*(TY-TX);
};
for(int32 Triangle=0;Triangle<OldSurfaceTriangleCount;++Triangle)
{
@@ -517,12 +583,13 @@ namespace VoxelForgeCore
if(LocalX<0.0||LocalY<0.0||LocalX>TileExtent||LocalY>TileExtent)continue;
for(const auto& Profile:GroundProfiles)
{
if(LocalX>Profile.Cells*Profile.GridStep||LocalY>Profile.Cells*Profile.GridStep)continue;
if(LocalX>Profile.Coordinates.back()||LocalY>Profile.Coordinates.back())continue;
const double Ground=InterpolateProfile(Profile.Ground,Profile,LocalX,LocalY);
const double Cap=InterpolateProfile(Profile.Cap,Profile,LocalX,LocalY);
if(Cap-Ground<=1.0||Ground<TileMinZ/Scale||Ground>TileMaxZ/Scale
||Ground<Profile.MinimumZ||Ground>Profile.MaximumZ)continue;
const double ReplacementBand=std::max(32.0,2.0*Profile.GridStep);
const double ReplacementBand=std::max(32.0,
2.0*std::min(Profile.GridStep,BoundaryStepVoxels));
if(Z>=Ground-ReplacementBand)
{
RemoveOldGround[static_cast<std::size_t>(Triangle)]=1;
@@ -574,17 +641,28 @@ namespace VoxelForgeCore
}
FNativeCapRefinementStats RefineFarNativeCap(const FStandaloneOwnerWorld& Owner,
const WorldAssemblyManifest& Manifest,const FVoxelTileKey& Key,FVoxelMeshData& Mesh)
const WorldAssemblyManifest& Manifest,const FVoxelTileKey& Key,int32 MeshStepVoxels,
int32 InteriorStepOverride,FVoxelMeshData& Mesh)
{
FNativeCapRefinementStats Stats;
if(Key.Level<4||!Owner.Generator)return Stats;
const int32 GridStepVoxels=Key.Level<5?std::min(128,1<<Key.Level):128;
constexpr int64 Scale=WorldAssemblySubvoxelsPerVoxel;
const int64 GridStepSubvoxels=static_cast<int64>(GridStepVoxels)*Scale;
constexpr int32 BoundaryStepVoxels=128;
const int32 RequestedStep=Key.Level==8
?(InteriorStepOverride>0?InteriorStepOverride:MeshStepVoxels)
:(Key.Level<5?std::min(BoundaryStepVoxels,1<<Key.Level):BoundaryStepVoxels);
const FIntVector TileOrigin=Key.OriginVoxels();
const int32 TileExtent=Key.ExtentVoxels();
if(TileExtent<=0||TileExtent%GridStepVoxels!=0)return Stats;
const int32 Cells=TileExtent/GridStepVoxels;
const int32 EffectiveGridStep=Key.Level==8
?((RequestedStep<BoundaryStepVoxels||RequestedStep%BoundaryStepVoxels!=0
||TileExtent<=0||TileExtent%RequestedStep!=0)
?BoundaryStepVoxels:std::max(BoundaryStepVoxels,RequestedStep))
:RequestedStep;
constexpr int64 Scale=WorldAssemblySubvoxelsPerVoxel;
if(TileExtent<=0||TileExtent%EffectiveGridStep!=0)return Stats;
const std::vector<int32> GridCoordinates=
BuildNativeHorizonGridCoordinates(TileExtent,EffectiveGridStep);
const int32 GridSize=static_cast<int32>(GridCoordinates.size());
if(GridSize<2)return Stats;
const int64 TileMinZ=static_cast<int64>(TileOrigin.Z)*Scale;
const int64 TileMaxZ=static_cast<int64>(TileOrigin.Z+TileExtent)*Scale;
const int32 OldSurfaceVertices=std::clamp(Mesh.NumSurfaceVertices,0,Mesh.Vertices.Num());
@@ -612,14 +690,14 @@ namespace VoxelForgeCore
FColor Color=FColor::White;
int32 VertexIndex=INDEX_NONE;
};
std::vector<FGridSample> Grid(static_cast<std::size_t>(Cells+1)*(Cells+1));
const auto GridIndex=[Cells](int32 X,int32 Y)
{return static_cast<std::size_t>(Y)*(Cells+1)+X;};
for(int32 Y=0;Y<=Cells;++Y)
for(int32 X=0;X<=Cells;++X)
std::vector<FGridSample> Grid(static_cast<std::size_t>(GridSize)*GridSize);
const auto GridIndex=[GridSize](int32 X,int32 Y)
{return static_cast<std::size_t>(Y)*GridSize+X;};
for(int32 Y=0;Y<GridSize;++Y)
for(int32 X=0;X<GridSize;++X)
{
const int64 XSub=(static_cast<int64>(TileOrigin.X)+static_cast<int64>(X)*GridStepVoxels)*Scale;
const int64 YSub=(static_cast<int64>(TileOrigin.Y)+static_cast<int64>(Y)*GridStepVoxels)*Scale;
const int64 XSub=(static_cast<int64>(TileOrigin.X)+GridCoordinates[X])*Scale;
const int64 YSub=(static_cast<int64>(TileOrigin.Y)+GridCoordinates[Y])*Scale;
const auto Column=Block.point(XSub,YSub,0);
auto& Sample=Grid[GridIndex(X,Y)];
Sample.Ground=static_cast<double>(Column.groundHeightSubvoxels);
@@ -633,13 +711,13 @@ namespace VoxelForgeCore
const uint8 B=static_cast<uint8>(std::clamp(std::lround(Blend*255.0f),0l,255l));
Sample.Color=FColor(R,0,B,A);
}
for(int32 Y=0;Y<=Cells;++Y)
for(int32 X=0;X<=Cells;++X)
for(int32 Y=0;Y<GridSize;++Y)
for(int32 X=0;X<GridSize;++X)
{
const int32 Left=std::max(0,X-1),Right=std::min(Cells,X+1);
const int32 Down=std::max(0,Y-1),Up=std::min(Cells,Y+1);
const double DX=static_cast<double>(Right-Left)*GridStepSubvoxels;
const double DY=static_cast<double>(Up-Down)*GridStepSubvoxels;
const int32 Left=std::max(0,X-1),Right=std::min(GridSize-1,X+1);
const int32 Down=std::max(0,Y-1),Up=std::min(GridSize-1,Y+1);
const double DX=static_cast<double>(GridCoordinates[Right]-GridCoordinates[Left])*Scale;
const double DY=static_cast<double>(GridCoordinates[Up]-GridCoordinates[Down])*Scale;
const double SlopeX=DX>0.0?(Grid[GridIndex(Right,Y)].Cap-Grid[GridIndex(Left,Y)].Cap)/DX:0.0;
const double SlopeY=DY>0.0?(Grid[GridIndex(X,Up)].Cap-Grid[GridIndex(X,Down)].Cap)/DY:0.0;
auto& Sample=Grid[GridIndex(X,Y)];
@@ -655,10 +733,10 @@ namespace VoxelForgeCore
if(Sample.Cap<Sample.Ground||Sample.Cap<TileMinZ||Sample.Cap>TileMaxZ)continue;
Sample.VertexIndex=static_cast<int32>(AddedVertices.size());
const std::size_t Index=static_cast<std::size_t>(&Sample-Grid.data());
const int32 GX=static_cast<int32>(Index%static_cast<std::size_t>(Cells+1));
const int32 GY=static_cast<int32>(Index/static_cast<std::size_t>(Cells+1));
const double XSub=static_cast<double>(TileOrigin.X+GX*GridStepVoxels)*Scale;
const double YSub=static_cast<double>(TileOrigin.Y+GY*GridStepVoxels)*Scale;
const int32 GX=static_cast<int32>(Index%static_cast<std::size_t>(GridSize));
const int32 GY=static_cast<int32>(Index/static_cast<std::size_t>(GridSize));
const double XSub=static_cast<double>(TileOrigin.X+GridCoordinates[GX])*Scale;
const double YSub=static_cast<double>(TileOrigin.Y+GridCoordinates[GY])*Scale;
const double VoxelSizeCm=static_cast<double>(Owner.CoreVoxelSizeMicrons)/10000.0;
AddedVertices.emplace_back(XSub/Scale*VoxelSizeCm,YSub/Scale*VoxelSizeCm,
Sample.Cap/Scale*VoxelSizeCm);
@@ -668,8 +746,8 @@ namespace VoxelForgeCore
if(bHasColors)AddedColors.push_back(Sample.Color);
}
for(int32 Y=0;Y<Cells;++Y)
for(int32 X=0;X<Cells;++X)
for(int32 Y=0;Y+1<GridSize;++Y)
for(int32 X=0;X+1<GridSize;++X)
{
const int32 CellX[4]={X,X+1,X+1,X};
const int32 CellY[4]={Y,Y,Y+1,Y+1};
@@ -692,8 +770,8 @@ namespace VoxelForgeCore
{
const int32 GX=CellX[Corner],GY=CellY[Corner];
const auto& Sample=Grid[GridIndex(GX,GY)];
PolygonA[Corner].XSub=static_cast<double>(TileOrigin.X+GX*GridStepVoxels)*Scale;
PolygonA[Corner].YSub=static_cast<double>(TileOrigin.Y+GY*GridStepVoxels)*Scale;
PolygonA[Corner].XSub=static_cast<double>(TileOrigin.X+GridCoordinates[GX])*Scale;
PolygonA[Corner].YSub=static_cast<double>(TileOrigin.Y+GridCoordinates[GY])*Scale;
PolygonA[Corner].ZSub=Sample.Cap;PolygonA[Corner].GroundSub=Sample.Ground;
PolygonA[Corner].Normal=Sample.Normal;PolygonA[Corner].Color=Sample.Color;
}
@@ -1400,7 +1478,8 @@ namespace VoxelForgeCore
&&bHasNativeSurfaceHorizonPlan&&!bHasIslandPlanInTile;
const bool bSkipFarShellRepair=key.lod>=7&&bUseNativeSurfaceHorizon;
FStandaloneShapeWorld::ResetThreadClassificationStats();
VoxelForgeCore::Heightfield2D::resetThreadColumnCacheStats();
VoxelForgeCore::Heightfield2D::resetThreadColumnCacheStats(
options.captureColumnCoordinates&&key.lod==8);
bool bNativeSurfaceTile=false;
bool bNativeSurfaceBandSafe=false;
if(bHasWorldAssembly)
@@ -1427,7 +1506,7 @@ namespace VoxelForgeCore
{
out.diagnostics.meshingSkipped=true;
const auto Counts=FStandaloneShapeWorld::GetThreadClassificationStats();
const auto Columns=VoxelForgeCore::Heightfield2D::threadColumnCacheStats();
const auto& Columns=VoxelForgeCore::Heightfield2D::threadColumnCacheStats();
out.diagnostics.solidBoxes=Counts.SolidBoxes;
out.diagnostics.airBoxes=Counts.AirBoxes;
out.diagnostics.mixedBoxes=Counts.MixedBoxes;
@@ -1571,9 +1650,18 @@ namespace VoxelForgeCore
out.diagnostics.featureListMilliseconds=std::chrono::duration<double,std::milli>(
std::chrono::steady_clock::now()-FeatureListStart).count();
const ScopedWorldAssemblyFeatureQueriesV1 featureScope(featureQueries);
const double ColumnMillisecondsBeforeMeshing=
VoxelForgeCore::Heightfield2D::threadColumnCacheStats().columnComputeMilliseconds;
const auto MarchingCubesStart=std::chrono::steady_clock::now();
FVoxelMeshData mesh=mesher.GenerateMesh(origin,step,cells,nullptr,
MeshBandMinZVoxel,MeshBandMaxZVoxel,&sampleCount,nullptr,nullptr,
(options.hashDensity||options.captureDensitySamples)?&densityValues:nullptr);
const double MarchingCubesWallMilliseconds=std::chrono::duration<double,std::milli>(
std::chrono::steady_clock::now()-MarchingCubesStart).count();
const double ColumnMillisecondsAfterMeshing=
VoxelForgeCore::Heightfield2D::threadColumnCacheStats().columnComputeMilliseconds;
out.diagnostics.marchingCubesExclusiveMilliseconds=std::max(0.0,
MarchingCubesWallMilliseconds-(ColumnMillisecondsAfterMeshing-ColumnMillisecondsBeforeMeshing));
const double MeshingMilliseconds=std::chrono::duration<double,std::milli>(
std::chrono::steady_clock::now()-MeshingStart).count();
out.diagnostics.farHorizonVersion=key.lod>=4?options.farHorizonVersion:0;
@@ -1612,9 +1700,29 @@ namespace VoxelForgeCore
{
const auto HorizonStart=std::chrono::steady_clock::now();
FNativeCapRefinementStats GroundStats;
const double ColumnMillisecondsBeforeGround=
VoxelForgeCore::Heightfield2D::threadColumnCacheStats().columnComputeMilliseconds;
const auto GroundStart=std::chrono::steady_clock::now();
if(key.lod>=4)
GroundStats=RefineFarNativeGround(world,*impl_->worldAssembly,nativeKey,mesh);
const auto CapStats=RefineFarNativeCap(world,*impl_->worldAssembly,nativeKey,mesh);
GroundStats=RefineFarNativeGround(world,*impl_->worldAssembly,nativeKey,step,
options.lod8HorizonInteriorStepVoxels,mesh);
const double GroundWallMilliseconds=std::chrono::duration<double,std::milli>(
std::chrono::steady_clock::now()-GroundStart).count();
const double ColumnMillisecondsAfterGround=
VoxelForgeCore::Heightfield2D::threadColumnCacheStats().columnComputeMilliseconds;
out.diagnostics.groundRefinementExclusiveMilliseconds=std::max(0.0,
GroundWallMilliseconds-(ColumnMillisecondsAfterGround-ColumnMillisecondsBeforeGround));
const double ColumnMillisecondsBeforeCap=
VoxelForgeCore::Heightfield2D::threadColumnCacheStats().columnComputeMilliseconds;
const auto CapStart=std::chrono::steady_clock::now();
const auto CapStats=RefineFarNativeCap(world,*impl_->worldAssembly,nativeKey,step,
options.lod8HorizonInteriorStepVoxels,mesh);
const double CapWallMilliseconds=std::chrono::duration<double,std::milli>(
std::chrono::steady_clock::now()-CapStart).count();
const double ColumnMillisecondsAfterCap=
VoxelForgeCore::Heightfield2D::threadColumnCacheStats().columnComputeMilliseconds;
out.diagnostics.capRefinementExclusiveMilliseconds=std::max(0.0,
CapWallMilliseconds-(ColumnMillisecondsAfterCap-ColumnMillisecondsBeforeCap));
out.diagnostics.farHorizonMilliseconds=std::chrono::duration<double,std::milli>(
std::chrono::steady_clock::now()-HorizonStart).count();
out.diagnostics.capRefined=CapStats.Refined;
@@ -1629,7 +1737,7 @@ namespace VoxelForgeCore
if(bHasWorldAssembly)
{
const auto Counts=FStandaloneShapeWorld::GetThreadClassificationStats();
const auto Columns=VoxelForgeCore::Heightfield2D::threadColumnCacheStats();
const auto& Columns=VoxelForgeCore::Heightfield2D::threadColumnCacheStats();
out.diagnostics.fieldCalls=Columns.pointCalls;
out.diagnostics.floatFieldCalls=Counts.NativeFloatFieldCalls;
out.diagnostics.latticeFieldCalls=Counts.NativeLatticeFieldCalls;
@@ -1637,7 +1745,15 @@ namespace VoxelForgeCore
out.diagnostics.columnCacheHits=Columns.columnHits;
out.diagnostics.columnCacheMisses=Columns.columnMisses;
out.diagnostics.columnComputeMilliseconds=Columns.columnComputeMilliseconds;
out.diagnostics.columnEvaluationExclusiveMilliseconds=Columns.columnComputeMilliseconds;
out.diagnostics.classificationMilliseconds=Counts.Milliseconds;
if(options.captureColumnCoordinates&&key.lod==8)
{
out.diagnostics.columnEvaluationWorkerHash=static_cast<std::uint64_t>(
std::hash<std::thread::id>{}(std::this_thread::get_id()))+1u;
out.diagnostics.columnCoordinateCaptureComplete=Columns.coordinateCaptureComplete;
out.diagnostics.evaluatedColumnCoordinates=Columns.evaluatedCoordinates;
}
if(bNativeSurfaceTile)
{
out.diagnostics.solidBoxes=Counts.SolidBoxes;
@@ -1653,6 +1769,7 @@ namespace VoxelForgeCore
}
out.diagnostics.heightfieldBandLimited=bNativeHeightfieldBandLimited;
out.diagnostics.meshingMilliseconds=MeshingMilliseconds;
const auto MeshAssemblyStart=std::chrono::steady_clock::now();
out.meshPayloadAllocatedBytes=static_cast<std::uint64_t>(mesh.Vertices.GetAllocatedSize())
+static_cast<std::uint64_t>(mesh.Normals.GetAllocatedSize())
+static_cast<std::uint64_t>(mesh.UVs.GetAllocatedSize())
@@ -1696,6 +1813,8 @@ namespace VoxelForgeCore
}
out.hasDensityHash=out.hasMeshHash=out.hasPayloadHash=out.hasTopologyHash=true;
}
out.diagnostics.meshAssemblyMilliseconds=std::chrono::duration<double,std::milli>(
std::chrono::steady_clock::now()-MeshAssemblyStart).count();
return out;
}
+23 -3
View File
@@ -1487,6 +1487,17 @@ namespace world_assembly_detail
HeightfieldColumnStats.columnComputeMilliseconds+=std::chrono::duration<double,std::milli>(
std::chrono::steady_clock::now()-start).count();
++HeightfieldColumnStats.columnMisses;
if(HeightfieldColumnStats.captureCoordinates&&HeightfieldColumnStats.coordinateCaptureComplete)
{
try
{
HeightfieldColumnStats.evaluatedCoordinates.push_back({x,y});
}
catch(...)
{
HeightfieldColumnStats.coordinateCaptureComplete=false;
}
}
entry={&recipe,&plan,recipe.contentHash,plan.contentHash,x,y,column};
return column;
}
@@ -1954,10 +1965,19 @@ HeightfieldPointSample Heightfield2D::point(std::int64_t x, std::int64_t y,
return out;
}
void Heightfield2D::resetThreadColumnCacheStats() noexcept
{ world_assembly_detail::HeightfieldColumnStats={}; }
void Heightfield2D::resetThreadColumnCacheStats(bool captureCoordinates) noexcept
{
auto& Stats=world_assembly_detail::HeightfieldColumnStats;
Stats.pointCalls=0;
Stats.columnHits=0;
Stats.columnMisses=0;
Stats.columnComputeMilliseconds=0.0;
Stats.captureCoordinates=captureCoordinates;
Stats.coordinateCaptureComplete=true;
Stats.evaluatedCoordinates.clear();
}
HeightfieldColumnCacheStats Heightfield2D::threadColumnCacheStats() noexcept
const HeightfieldColumnCacheStats& Heightfield2D::threadColumnCacheStats() noexcept
{ return world_assembly_detail::HeightfieldColumnStats; }
HeightfieldBoxClass Heightfield2D::classifyLatticeBox(std::int64_t minimumX,
@@ -0,0 +1,213 @@
# World assembler S10 / M3 Round 14 — cheaper far ring
Date: 2026-10-19. Base: S9 / `e7586a1`. No commit, push or stash.
## Result
The planar far path now samples the LOD8 interior at the spacing that LOD8 marching cubes can
represent. At 25 cm, that is 512 voxels / 128 m per MC cell. Globally aligned 128-voxel (32 m)
strips remain along tile boundaries. LOD4–LOD7 coordinates and the 2 cm agreement tolerances are
unchanged. Far recipe version is 3; far mesh hashes change, while density and LOD0–LOD3 remain
unchanged.
On the owner SurfaceWorld plan, the warmed eight-tile BuildHost LOD8 full-tile p95 fell 76.7%,
from 59.848 ms to 13.933 ms. In the fresh 6 m/s actor spawn trace, the 2,578 initial tile jobs
used 26.870 worker-seconds; the LOD8 share was 4.288 seconds. Round13’s S8 spawn reference was
about 41 worker-seconds, with 18.798 seconds in its 306 LOD8 jobs. Initial readiness was 7.117 s,
below the 7.7 s target. The active 3×3 walkable ring was ready in 0.490 s, and all 512 LOD0 tiles
were ready at 1.428 s.
## Count first — exclusive cost and duplicate columns
The counters are measured per tile. Column evaluation is timed only around uncached column work.
MC, ground and cap counters subtract column time from their respective wall intervals. The mesh
assembly window covers final payload flattening and optional diagnostic hashing. Each p95 is
computed independently, so these p95 values are not additive.
| LOD8 warmed p95, BuildHost 25 cm | S9 | S10 | Change |
|---|---:|---:|---:|
| Full tile | 59.848 ms | 13.933 ms | −76.7% |
| Column evaluation | 31.319 ms | 7.478 ms | −76.1% |
| Marching cubes | 2.667 ms | 2.359 ms | −11.6% |
| Ground refinement | 11.666 ms | 1.690 ms | −85.5% |
| Cap refinement | 10.029 ms | 1.330 ms | −86.7% |
| Mesh assembly and diagnostic hash | 5.445 ms | 1.095 ms | −79.9% |
| Column-cache misses | 9,679 | 2,287 | −76.4% |
The high-cost S9 sample generated 8,192 ground triangles, removed 531 coarse ground triangles,
and generated 6,732 cap triangles. S10 generated 968 ground triangles, removed 534, and generated
840 cap triangles. The geometric checks below pass at the same tolerance.
The fresh W12 Standalone overlap capture counted 153 LOD8 tiles. S10 evaluated 106,219 unique XY
columns and repeated 32,738 neighboring tile evaluations, including 30,446 cross-worker repeats.
The same-binary step-128 control counted 660,283 unique columns, 44,319 neighboring repeats and
41,665 cross-worker repeats. This confirms that cross-worker duplicates remain, while the coarser
interior removes most column work without sharing cache state between workers.
| Fresh W12 Standalone pipeline | Default step 0 | Negative control step 128 |
|---|---:|---:|
| LOD8 worker sum | 3.206 s | 13.534 s |
| LOD8 unique XY columns | 106,219 | 660,283 |
| Repeated neighboring evaluations | 32,738 | 44,319 |
| Cross-worker neighboring repeats | 30,446 | 41,665 |
Both runs used the current v3 binary. Step 128 restores S9 sampling density, but retains the v3
hash salt; its far mesh hash is therefore expected to differ from S9’s v2 hash.
## BuildHost actor spawn and ring
The fresh 6 m/s trace used the S9 movement, collision and backlog settings: Legacy order, Dynamic
near reserve, collision lookahead on, 256 result cap, and a 12-CPU affinity mask. Per-LOD worker
generation sums for the 2,578 initial generation ordinals were:
| LOD | Initial tiles | Worker seconds |
|---:|---:|---:|
| 0 | 512 | 3.840 |
| 1 | 448 | 3.781 |
| 2 | 320 | 0.811 |
| 3 | 272 | 2.093 |
| 4 | 272 | 6.688 |
| 5 | 208 | 1.535 |
| 6 | 144 | 1.988 |
| 7 | 96 | 1.846 |
| 8 | 306 | 4.288 |
| **Total** | **2,578** | **26.870** |
At 6 m/s, initial readiness was 7.116525 s (2,578 desired, 2,617 loaded); spawn-tick p95/max was
1.824/4.993 ms. The support ring became ready at 0.490 s (9/9). The complete LOD0 set was ready at
1.427630 s, with a 0.990836 s measured build interval. Round13’s S9 6 m/s initial-ready value was
7.813133 s.
At 40 m/s (4,000 cm/s), all 75 moving support-center arrivals from 30.205 s through 45.011 s had
the full 9/9 walkable ring ready: 75/75, 100%. A stationary startup gate event before movement is
excluded from that moving-center denominator. Initial readiness was 7.087272 s. Spawn-tick
p95/max was 1.833/5.365 ms; the trace passed.
## Quality and determinism
- S3 ground agreement: 72 shared route points; far LOD7 and LOD8 maximum error 0.0427 cm, cross-LOD
error 0 cm, within 2 cm. The 100 cm datum-offset control measured 100.0427 cm and was detected.
- S4 cap agreement: 128 shared points; maximum error 0.0504 cm, within 2 cm. The 100 cm datum-offset
control measured 100.0015 cm and was detected. Cap census emitted all 385/385 requested bands.
- Cap coverage: 2,520/2,520 upward rays hit the cap (100%); no profile-roof, mesh or clip-edge misses.
- Core-off legacy passed at 12.5 cm. The M2 Hybrid V1 and V2 fixture sets each passed five analytic
fixtures and border checks. SurfaceWorld and FloatingIslands passed BuildHost actor validation at
25 cm. Water stayed fixed; carve and fill remained unrestricted.
- Standalone W1/W8/W12 produced identical hashes for the full 2,553-tile view: density
`c10697a8f48380c7`, mesh `e75c9119a1498cd5`, payload `49a9a8d1835a6c8c`, topology
`edcb23288b5e8724`.
- BuildHost SurfaceWorld W1/W8/W16 matrix was stable: density `0cd2a1a4152ee4aa`, mesh
`a06243aee16b65d5`, payload `c700162935c262d7`, topology `0af901aa42b452bc`. FloatingIslands
W1/W8/W16 was stable: density `4dcfb37585d7d986`, mesh `f14f76fbf065c6ed`, payload
`53035c45fd1a082e`, topology `23550100654a4ec3`. W16 was a logical partition with the process
still capped to 12 executing CPUs.
Far mesh hashes differ from S9 as expected under recipe version 3; current-build determinism passes.
## Matched captures
All captures are 640×400 and under 22 KB. Each was produced at 60 fps with VSync on, then the
Standalone viewer was closed after its back-buffer capture. Raw 1280×800 PNGs and run logs are under
`Saved/BuildHost/Round14/CaptureRaw/`.
| View | S9 sampling control, 128 voxels | S10 default, MC spacing |
|---|---|---|
| Far horizon, camera 0,-35,-487.94,0,-22 | [s9_far_horizon.jpg](captures/s9_far_horizon.jpg) (21.9 KB) | [s10_far_horizon.jpg](captures/s10_far_horizon.jpg) (18.0 KB) |
| Cap, camera 0,-35,-487.94,0,22.42 | [s9_cap.jpg](captures/s9_cap.jpg) (10.8 KB) | [s10_cap.jpg](captures/s10_cap.jpg) (10.8 KB) |
The cap is low contrast in this view, as in Round8; the 2 cm agreement, triangle census and ray
coverage provide the cap acceptance evidence.
## Builds, commands and hashes
Builds on the final source:
~~~powershell
.\Tools\BuildVoxelForgeCoreHost.ps1 -MaxParallelActions 12 -TimeoutSeconds 900
.\Standalone\Build.ps1 -Target All -CompileWorkers 12 -BuildPriority Normal
~~~
The fresh Standalone determinism processes used the same command with workers 1, 8 and 12:
~~~powershell
foreach ($workers in @(1, 8, 12)) {
.\Standalone\bin\VoxelForgeCoreStandalone.exe --workers=$workers --world-assembler-prototype --stream-benchmark --stream-hash --force-generate --clip-radius=3 --max-clip-level=8 --camera=0,-35,-487.94,0,-22 --far-horizon-lod8-step=0 --process-priority=Normal --process-affinity=4095 --frame-limit=60
}
~~~
The current-build duplicate count and same-binary step-128 control used:
~~~powershell
.\Standalone\bin\VoxelForgeCoreStandalone.exe --workers=12 --world-assembler-prototype --stream-pipeline --stream-hash --profile-far-column-overlap --force-generate --clip-radius=3 --max-clip-level=8 --camera=0,-35,-487.94,0,-22 --far-horizon-lod8-step=0 --process-priority=Normal --process-affinity=4095 --frame-limit=60
.\Standalone\bin\VoxelForgeCoreStandalone.exe --workers=12 --world-assembler-prototype --stream-pipeline --stream-hash --profile-far-column-overlap --force-generate --clip-radius=3 --max-clip-level=8 --camera=0,-35,-487.94,0,-22 --far-horizon-lod8-step=128 --process-priority=Normal --process-affinity=4095 --frame-limit=60
~~~
Quality and BuildHost actor commands:
~~~powershell
.\Standalone\bin\VoxelForgeCoreStandalone.exe --world-assembler-audit --workers=12 --clip-radius=3 --max-clip-level=8 --camera=0,-35,-487.94,0,-22 --process-priority=Normal --process-affinity=4095 --frame-limit=60
.\Tools\TestVoxelForgeCoreWorldHost.ps1 -CoreVoxelSizeCm 25 -TimeoutSeconds 900
.\Tools\TestVoxelForgeCoreWorldHost.ps1 -FloatingIslandsOnly -CoreVoxelSizeCm 25 -TimeoutSeconds 900
.\Tools\TestVoxelForgeCoreWorldHost.ps1 -CoreOffTest -CoreVoxelSizeCm 12.5 -TimeoutSeconds 900
.\Tools\TestVoxelForgeCoreWorldHost.ps1 -HybridFixturesOnly -CoreMesherRecipe WorldAssemblerHybridV1 -CoreVoxelSizeCm 25 -TimeoutSeconds 900
.\Tools\TestVoxelForgeCoreWorldHost.ps1 -HybridFixturesOnly -CoreMesherRecipe WorldAssemblerHybridV2 -CoreVoxelSizeCm 25 -TimeoutSeconds 900
~~~
The two fresh actor traces differed only in speed, 600 or 4,000 cm/s:
~~~powershell
.\Tools\TestVoxelForgeCoreWorldHost.ps1 -StreamingTraceOnly -StartupApplyBoost 0 -StreamingOrder Legacy -NearReserveMode Dynamic -CoreVoxelSizeCm 25 -StreamingCollisionLookahead 1 -TestMoveDirection '0,1,0' -CoreRegionConcurrentAdmission 1 -StreamingResultBacklogCap 256 -TestMoveSpeedCmPerSecond 600 -TimeoutSeconds 900
.\Tools\TestVoxelForgeCoreWorldHost.ps1 -StreamingTraceOnly -StartupApplyBoost 0 -StreamingOrder Legacy -NearReserveMode Dynamic -CoreVoxelSizeCm 25 -StreamingCollisionLookahead 1 -TestMoveDirection '0,1,0' -CoreRegionConcurrentAdmission 1 -StreamingResultBacklogCap 256 -TestMoveSpeedCmPerSecond 4000 -TimeoutSeconds 900
~~~
Final binary SHA-256:
| Binary | SHA-256 |
|---|---|
| BuildHost UnrealEditor-VoxelForgeCore.dll | `D87CC56BACCDE6559705EEAEA7DB73D1A6ABF79484D37A3887B94A9FD62BABD3` |
| BuildHost UnrealEditor-VoxelForge.dll | `9D0522ECC9A6AC52D9F3ACC2FD44399FE70F557C5F11D0203AB4C4B8EE75C198` |
| BuildHost UnrealEditor-VoxelForgeEditor.dll | `AA196DA5A6DB3C84B5EC4F002AEBC787386DBB8F2798F5D1EEC03010BF642BBA` |
| Standalone VoxelForgeCoreStandalone.exe | `E316D7E3E0B3D3639DAE61D6E2D1091F38C59A210B3B7DD8124928D21A8119A5` |
## Run notes, files and safety review
- BuildHost and Standalone ran at Normal priority with no more than 12 executing CPUs; free memory
was above 17 GB before the final launches. BuildHost peak private memory was 3.76 GiB for
SurfaceWorld, 3.27 GiB for FloatingIslands, 3.39 GiB for the 6 m/s trace and 3.67 GiB for the
40 m/s trace. The Standalone overlap/control processes peaked below 0.72 GiB. No process exceeded
8 GiB, and no launch overlapped another Unreal/Standalone process.
- Standalone’s first compile attempt failed in `StandaloneViewer.cpp` because one helper referred to
`App` outside its scope; that reference was corrected and the final build passed. The existing
`vswhere.exe` lookup warning and `VoxelStrateManager.cpp` C4838 warnings remained.
- Three BuildHost CVar attempts (direct argument and two `ExecCmds` forms) read back zero and did not
change the worker grid; that no-op CVar/harness path was removed. An early Standalone step-128
attempt also did not reach the pipeline; CLI pass-through was corrected and the final same-binary
step-128 control above is effective. No CVar remains in the final source.
- One high-pitch cap capture was black and was discarded. A separate early capture shell lacked its
memory-probe type because the PowerShell session was fresh; that capture completed without a
pre-launch memory gate. The matched final captures were rerun with the gate active. No
`0xC0000142` occurred and no run needed retrying.
- The only UECC directory was the pre-existing
`UECC-Windows-F1A3EFD5469F5DD62E68EBBACAC84376_0000`, timestamped 2026-09-17; no new UECC folder
was created.
- No new source files. Changed source files and compile spots:
| File | Compile spot |
|---|---|
| `Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h`, `Standalone/Core/src/VoxelForgeCore.cpp`, `Standalone/Core/src/WorldAssemblerInternal.h` | VoxelForgeCore in Standalone and BuildHost |
| `Standalone/Source/StreamingWorld.h`, `Standalone/Source/StandaloneViewer.cpp` | StandaloneViewer / VoxelForgeCoreStandalone.exe |
| `Source/VoxelForgeEditor/Private/VoxelForgeExploreCommandlet.cpp` | BuildHost VoxelForgeEditor module |
| `Tools/TestVoxelForgeCoreWorldHost.ps1` | PowerShell harness only; recipe-specific fixture logs |
Async-safety review: no actor async source changed. Epoch checks, MPSC queues, EndPlay draining,
worker `bShuttingDown` checks and `ModsLock` remain unchanged. Region-page publication, shared_ptr
lifetime and concurrent eviction remain unchanged. New duplicate capture stays thread-local and is
copied into the tile result; its worker-id hash is diagnostic only. UObject and RealtimeMeshComponent
mutation remain on the game thread. No owner `.uproject`, editor, `.uasset`, or RealtimeMeshComponent
file was modified. `git diff --check` passed. No commit, push or stash.
### Dialogue
**User:** Keep the 12-thread cap; count exclusive LOD8 costs first, optimize the planar far path, and verify quality and determinism.
**Codex:** The LOD8 interior now follows the MC spacing, with 128-voxel boundary strips preserved. The owner-plan LOD8 p95 fell from 59.848 ms to 13.933 ms; cap and ground gates, both BuildHost actor matrices, current-build worker hashes, and the 40 m/s support ring passed.
Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

+105 -5
View File
@@ -1720,6 +1720,7 @@ namespace
bool Keys[256]{};
int32 Seed=0;
int32 WorkerCount=8;
int32 FarHorizonLOD8InteriorStepVoxels=0;
int32 ClipRadius=3;
int32 MaxClipLevelOverride=-1;
int32 CaptureTileLimit=0;
@@ -2903,7 +2904,8 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
Streamer=std::make_unique<FStandaloneStreamingGenerator>(*World,StreamWorkers,false,
(!bShapeWorld&&MeshCache.IsLoaded())?&MeshCache:nullptr,
bFarShapeStagePending?World->FullResClipLevels-1:-1,
false,false,false,false,ShapeWorld.get());
false,false,false,false,ShapeWorld.get(),false,
FarHorizonLOD8InteriorStepVoxels);
if(bGpuFarTimelineEnabled)
Streamer->EnableColdTimelineTrace(ColdProcessStart,&ColdTimelineFrameOrdinal);
if(bGpuFarEnabled&&ShapeWorld&&ShapeWorld->hasFarWorld())
@@ -6974,7 +6976,8 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
const FVector& CameraPositionVoxels,int32 WorkerCount,int32 MaxLevel,
VoxelForgeCore::DetailVariant DetailVariant,const VoxelForgeCore::JobScheduler& Scheduler,
bool bHashDensity,bool bSkipUvAndColors,bool bSkipMaterialGroups,bool bSkipUV,bool bSkipColors,
std::chrono::steady_clock::time_point ColdStart)
std::chrono::steady_clock::time_point ColdStart,bool bProfileFarColumnOverlap,
int32 FarHorizonLOD8InteriorStepVoxels)
{
using Clock=std::chrono::steady_clock;
FStandaloneOwnerWorld& World=*OwnerWorld;
@@ -7042,7 +7045,8 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
const auto ShapeReady=Clock::now();
const double ShapeSeconds=std::chrono::duration<double>(ShapeReady-ShapeBuildStart).count();
FStandaloneStreamingGenerator Streamer(World,WorkerCount,bHashDensity,nullptr,MaxLevel,
bSkipUvAndColors,bSkipMaterialGroups,bSkipUV,bSkipColors,&Shape);
bSkipUvAndColors,bSkipMaterialGroups,bSkipUV,bSkipColors,&Shape,bProfileFarColumnOverlap,
FarHorizonLOD8InteriorStepVoxels);
const auto StreamStart=Clock::now();
const FStandaloneStreamingUpdate Update=Streamer.StartWithDesired(Desired);
if(Update.DesiredTiles!=static_cast<int32>(Desired.Tiles.size()))
@@ -7055,6 +7059,43 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
std::vector<FHashedTile> DensityRows,MeshRows,PayloadRows,TopologyRows;
int32 Finished=0,NeighborhoodFinished=0,FullDetailFinished=0;
int32 FullDetailExpected=0;
struct FColumnXY
{
std::int64_t X=0,Y=0;
bool operator==(const FColumnXY&)const=default;
};
struct FColumnXYHash
{
std::size_t operator()(const FColumnXY& Value)const noexcept
{
const std::size_t X=std::hash<std::int64_t>{}(Value.X);
const std::size_t Y=std::hash<std::int64_t>{}(Value.Y);
return X^(Y+static_cast<std::size_t>(0x9E3779B9u)+(X<<6)+(X>>2));
}
};
struct FFirstColumnUse
{
FVoxelTileKey Tile;
std::uint64_t WorkerHash=0;
};
std::unordered_map<FColumnXY,FFirstColumnUse,FColumnXYHash> LOD8ColumnFirstUse;
std::uint64_t LOD8ColumnDuplicateEvents=0,LOD8NeighbourDuplicateEvents=0;
std::uint64_t LOD8CrossWorkerNeighbourDuplicateEvents=0;
std::size_t LOD8ColumnCaptureTiles=0;
bool bLOD8ColumnCaptureComplete=true;
std::array<double,9> WorkerSecondsByLOD{};
std::array<double,9> ColumnEvalMsByLOD{},MarchingExclusiveMsByLOD{};
std::array<double,9> GroundRefineExclusiveMsByLOD{},CapRefineExclusiveMsByLOD{};
std::array<double,9> MeshAssemblyMsByLOD{};
std::array<std::uint64_t,9> TileCountByLOD{};
const auto AreLOD8Neighbours=[](const FVoxelTileKey& A,const FVoxelTileKey& B)
{
if(A.Level!=8||B.Level!=8)return false;
const std::int64_t DX=std::abs(static_cast<std::int64_t>(A.Coord.X)-B.Coord.X);
const std::int64_t DY=std::abs(static_cast<std::int64_t>(A.Coord.Y)-B.Coord.Y);
const std::int64_t DZ=std::abs(static_cast<std::int64_t>(A.Coord.Z)-B.Coord.Z);
return std::max({DX,DY,DZ})<=1&&(DX+DY+DZ)>0;
};
uint64 NativeSolidBoxes=0,NativeAirBoxes=0,NativeMixedBoxes=0,NativeUnknownBoxes=0;
uint64 NativeFieldCalls=0,NativeColumnHits=0,NativeColumnMisses=0,NativeMeshingSkips=0;
double NativeClassificationMilliseconds=0.0,NativeColumnComputeMilliseconds=0.0;
@@ -7070,6 +7111,41 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
for(const FStandaloneCpuTile& Tile:Ready)
{
++Finished;
if(Tile.Key.Level>=0&&Tile.Key.Level<static_cast<int32>(TileCountByLOD.size()))
{
const std::size_t LOD=static_cast<std::size_t>(Tile.Key.Level);
++TileCountByLOD[LOD];
WorkerSecondsByLOD[LOD]+=Tile.WorkerSeconds;
ColumnEvalMsByLOD[LOD]+=Tile.CoreDiagnostics.columnEvaluationExclusiveMilliseconds;
MarchingExclusiveMsByLOD[LOD]+=Tile.CoreDiagnostics.marchingCubesExclusiveMilliseconds;
GroundRefineExclusiveMsByLOD[LOD]+=Tile.CoreDiagnostics.groundRefinementExclusiveMilliseconds;
CapRefineExclusiveMsByLOD[LOD]+=Tile.CoreDiagnostics.capRefinementExclusiveMilliseconds;
MeshAssemblyMsByLOD[LOD]+=Tile.CoreDiagnostics.meshAssemblyMilliseconds;
}
if(bProfileFarColumnOverlap&&Tile.Key.Level==8)
{
++LOD8ColumnCaptureTiles;
bLOD8ColumnCaptureComplete=bLOD8ColumnCaptureComplete
&&Tile.CoreDiagnostics.columnCoordinateCaptureComplete;
std::vector<std::array<std::int64_t,2>> Coordinates=
Tile.CoreDiagnostics.evaluatedColumnCoordinates;
std::sort(Coordinates.begin(),Coordinates.end());
Coordinates.erase(std::unique(Coordinates.begin(),Coordinates.end()),Coordinates.end());
for(const auto& Coordinate:Coordinates)
{
const FColumnXY XY{Coordinate[0],Coordinate[1]};
const auto [First,Inserted]=LOD8ColumnFirstUse.try_emplace(XY,
FFirstColumnUse{Tile.Key,Tile.CoreDiagnostics.columnEvaluationWorkerHash});
if(Inserted)continue;
++LOD8ColumnDuplicateEvents;
if(AreLOD8Neighbours(First->second.Tile,Tile.Key))
{
++LOD8NeighbourDuplicateEvents;
if(First->second.WorkerHash!=Tile.CoreDiagnostics.columnEvaluationWorkerHash)
++LOD8CrossWorkerNeighbourDuplicateEvents;
}
}
}
WholeViewReady=std::max(WholeViewReady,Tile.CompletedAt);
if(Neighborhood.contains(Tile.Key))
{
@@ -7115,6 +7191,22 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
}
}
Streamer.Stop();
if(bProfileFarColumnOverlap)
{
std::printf("[WorldAssemblerLOD8ColumnOverlap] tiles=%zu unique_xy_columns=%zu repeated_tile_evaluations=%llu neighbouring_duplicate_evaluations=%llu cross_worker_neighbouring_duplicates=%llu capture_complete=%d\n",
LOD8ColumnCaptureTiles,LOD8ColumnFirstUse.size(),
static_cast<unsigned long long>(LOD8ColumnDuplicateEvents),
static_cast<unsigned long long>(LOD8NeighbourDuplicateEvents),
static_cast<unsigned long long>(LOD8CrossWorkerNeighbourDuplicateEvents),
bLOD8ColumnCaptureComplete?1:0);
}
for(std::size_t LOD=0;LOD<TileCountByLOD.size();++LOD)
{
std::printf("[WorldAssemblerLODWorker] lod=%zu tiles=%llu worker_sum_s=%.6f column_eval_exclusive_ms=%.3f mc_exclusive_ms=%.3f ground_refine_exclusive_ms=%.3f cap_refine_exclusive_ms=%.3f mesh_assembly_ms=%.3f\n",
LOD,static_cast<unsigned long long>(TileCountByLOD[LOD]),WorkerSecondsByLOD[LOD],
ColumnEvalMsByLOD[LOD],MarchingExclusiveMsByLOD[LOD],
GroundRefineExclusiveMsByLOD[LOD],CapRefineExclusiveMsByLOD[LOD],MeshAssemblyMsByLOD[LOD]);
}
const double NeighborhoodSeconds=std::chrono::duration<double>(NeighborhoodReady-ColdStart).count();
const double NeighborhoodMeshSeconds=std::chrono::duration<double>(NeighborhoodReady-ShapeReady).count();
const double FullDetailSeconds=std::chrono::duration<double>(FullDetailReady-ColdStart).count();
@@ -7160,7 +7252,8 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
static_cast<unsigned long long>(HashTileRows(TopologyRows)));
}
return NeighborhoodFinished==static_cast<int32>(Neighborhood.size())
&&FullDetailFinished==FullDetailExpected&&Finished==Update.DesiredTiles&&bHashesPassing?0:6;
&&FullDetailFinished==FullDetailExpected&&Finished==Update.DesiredTiles&&bHashesPassing
&&(!bProfileFarColumnOverlap||(bLOD8ColumnCaptureComplete&&LOD8ColumnCaptureTiles>0))?0:6;
}
int RunStreamingBenchmark(FStandaloneOwnerWorld& World,VoxelForgeCore::World* CoreWorld,
const FVector& CameraPositionVoxels,
@@ -9508,6 +9601,7 @@ int main(int Argc,char** Argv)
std::string WorldAssemblyManifestOutputPath;
bool bStreamBenchmark=false;
bool bStreamPipeline=false;
bool bProfileFarColumnOverlap=false;
bool bCoarseFirstStreamOrder=false;
bool bProfileDetailBand=false;
bool bPassageDetailAuditOnly=false;
@@ -9530,6 +9624,9 @@ int main(int Argc,char** Argv)
{
const std::string_view Arg=Argv[I];
if(Arg.starts_with("--capture="))App.CapturePath=std::string(Arg.substr(10));
else if(Arg.starts_with("--far-horizon-lod8-step="))
App.FarHorizonLOD8InteriorStepVoxels=std::max(0,std::atoi(
std::string(Arg.substr(std::string_view("--far-horizon-lod8-step=").size())).c_str()));
else if(Arg.starts_with("--capture-flight-sequence-prefix="))
App.FlightCaptureSequencePrefix=std::string(
Arg.substr(std::string_view("--capture-flight-sequence-prefix=").size()));
@@ -9642,6 +9739,8 @@ int main(int Argc,char** Argv)
else if(Arg=="--coarse-first-stream-order")bCoarseFirstStreamOrder=true;
else if(Arg=="--stream-pipeline")
{bStreamPipeline=true;bStreamBenchmark=true;App.bShapeWorld=true;App.bForceGenerate=true;}
else if(Arg=="--profile-far-column-overlap")
{bProfileFarColumnOverlap=true;bStreamPipeline=true;bStreamBenchmark=true;App.bShapeWorld=true;App.bForceGenerate=true;}
else if(Arg=="--edit-benchmark")
{bEditBenchmark=true;bStreamBenchmark=true;App.bShapeWorld=true;}
else if(Arg=="--stream-hash")bStreamHash=true;
@@ -9858,7 +9957,8 @@ int main(int Argc,char** Argv)
FVector(App.Position.X*4.0,App.Position.Y*4.0,App.Position.Z*4.0),
App.WorkerCount,StreamBenchmarkMaxLevel,App.ShapeDetailVariant,App.CoreScheduler,
bStreamHash||!BakeMeshCachePath.empty(),bSkipUvAndColors,bSkipMaterialGroups,
bSkipUV,bSkipColors,ColdProcessStart);
bSkipUV,bSkipColors,ColdProcessStart,bProfileFarColumnOverlap,
App.FarHorizonLOD8InteriorStepVoxels);
if(bEditBenchmark)
return RunEditTransactionBenchmark(*App.World,*App.ShapeWorld,
FVector(App.Position.X*4.0,App.Position.Y*4.0,App.Position.Z*4.0),App.WorkerCount);
+10 -2
View File
@@ -423,11 +423,15 @@ public:
bool bInSkipMaterialGroups=false,
bool bInSkipUV=false,
bool bInSkipColors=false,
VoxelForgeCore::World* InCoreWorld=nullptr)
VoxelForgeCore::World* InCoreWorld=nullptr,
bool bInCaptureFarColumnCoordinates=false,
int32 InLod8HorizonInteriorStepVoxels=0)
:World(&InWorld),WorkerCount(FMath::Clamp(InWorkerCount,1,16)),bHashDensity(bInHashDensity),
MeshCache(InMeshCache),MaxLevelFilter(InMaxLevelFilter),
CoreWorld(InCoreWorld),bSkipUvAndColors(bInSkipUvAndColors),bSkipMaterialGroups(bInSkipMaterialGroups),
bSkipUV(bInSkipUV),bSkipColors(bInSkipColors)
bSkipUV(bInSkipUV),bSkipColors(bInSkipColors),
bCaptureFarColumnCoordinates(bInCaptureFarColumnCoordinates),
Lod8HorizonInteriorStepVoxels(std::max(0,InLod8HorizonInteriorStepVoxels))
{
VoxelGenLOD::InitializeConsoleSwitches();
UVoxelMarchingCubesMesher::InitializeConsoleSwitches();
@@ -795,6 +799,8 @@ private:
Options.bandMinZVoxel=Job.BandZMin;Options.bandMaxZVoxel=Job.BandZMax;
Options.hashDensity=bHashDensity;Options.skipUvAndColors=bSkipUvAndColors;
Options.skipMaterialGroups=bSkipMaterialGroups;Options.skipUv=bSkipUV;Options.skipColors=bSkipColors;
Options.captureColumnCoordinates=bCaptureFarColumnCoordinates&&Level==8;
Options.lod8HorizonInteriorStepVoxels=Lod8HorizonInteriorStepVoxels;
std::string Error;
auto Result=CoreWorld->generateTile(
{{Job.Key.Coord.X,Job.Key.Coord.Y,Job.Key.Coord.Z},Job.Key.Level},Options,Error);
@@ -965,6 +971,8 @@ private:
bool bSkipMaterialGroups=false;
bool bSkipUV=false;
bool bSkipColors=false;
bool bCaptureFarColumnCoordinates=false;
int32 Lod8HorizonInteriorStepVoxels=0;
std::atomic<uint64> CacheHitCount{0};
mutable std::mutex Mutex;
std::condition_variable Wake;