wip(assembler): round L12 -- region pages keep LOD1 block proofs: 24 m crossing 5x faster
Analytic region pages now retain the immutable shared manifest, so LOD1 page tiles get the L9 uniform-block proofs (71.7 -> 5.6 ms per tile). Reviewer W12 no-hash: 24 m crossing 0.898 -> 0.166 s; Luna: 1 km 5.89 -> 1.25 s. Spawn unchanged; all L1/FloatingIslands hashes and the 24 m crossing hash unchanged (W1/W12). KNOWN BUG (pre-existing, measured here): the same tile key yields different density from the base shape (manifest field) than from a region page (legacy field), sometimes different mesh too, so output depends on streaming history. To be fixed in L13. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CSFYgsdW8ejZSyiMf4qRSX
This commit is contained in:
1 parent
b1cfaf53b8
commit
32b8670833
6 files changed
+292
-23
No files matched your search
@@ -120,6 +120,9 @@ namespace VoxelForgeCore
|
||||
struct TileGenerationStats
|
||||
{
|
||||
TileClass tileClass = TileClass::unknown;
|
||||
bool nativeOnlyRoute = false;
|
||||
bool analyticRegionPageRoute = false;
|
||||
bool nativeSurfaceTile = false;
|
||||
std::uint64_t solidBoxes = 0;
|
||||
std::uint64_t airBoxes = 0;
|
||||
std::uint64_t mixedBoxes = 0;
|
||||
@@ -1122,7 +1125,8 @@ namespace VoxelForgeCore
|
||||
const JobScheduler& scheduler, std::string& error,
|
||||
std::size_t* outCompiledTiles = nullptr,
|
||||
TileRegionCompileStats* outStats = nullptr,
|
||||
bool analyticOnly = true);
|
||||
bool analyticOnly = true,
|
||||
bool diagnosticIncludeBaseCoveredTiles = false);
|
||||
void retainTileRegions(std::span<const TileKey> desiredTiles);
|
||||
void buildFarWorld();
|
||||
bool isCompiled() const noexcept;
|
||||
|
||||
@@ -319,6 +319,7 @@ public:
|
||||
Owner=&World;WorldAssembly=InWorldAssembly;
|
||||
WorkerCount=std::clamp(Workers,1,16);DetailVariant=Variant;
|
||||
Scheduler=InScheduler;RoundTunnelsEnabled=bInRoundTunnels;
|
||||
bUseNativeManifestFields=true;
|
||||
bAnalyticOnly=false;
|
||||
bProfileDetailBand=bInProfileDetailBand;
|
||||
for(int32 A=0;A<3;++A)
|
||||
@@ -470,10 +471,16 @@ public:
|
||||
bool BuildAnalyticRegion(FStandaloneOwnerWorld& World,const FIntVector& MinVoxels,
|
||||
const FIntVector& MaxVoxels,const FIntVector& ClipMinVoxels,
|
||||
const FIntVector& ClipMaxVoxels,EDetailVariant Variant,
|
||||
bool bInRoundTunnels,std::string& Error)
|
||||
bool bInRoundTunnels,
|
||||
const VoxelForgeCore::WorldAssemblyManifest* InWorldAssembly,
|
||||
std::string& Error)
|
||||
{
|
||||
using Clock=std::chrono::steady_clock;
|
||||
Owner=&World;WorkerCount=1;DetailVariant=Variant;Scheduler=nullptr;
|
||||
Owner=&World;WorldAssembly=InWorldAssembly;
|
||||
WorkerCount=1;DetailVariant=Variant;Scheduler=nullptr;
|
||||
// Region pages use the immutable manifest for conservative block proofs, while keeping
|
||||
// the legacy SurfaceWorld density evaluator so existing tile hashes remain exact.
|
||||
bUseNativeManifestFields=false;
|
||||
RoundTunnelsEnabled=bInRoundTunnels;bAnalyticOnly=true;bProfileDetailBand=false;
|
||||
for(int32 A=0;A<3;++A)
|
||||
{
|
||||
@@ -509,7 +516,7 @@ public:
|
||||
{
|
||||
Owner=&World;WorldAssembly=InWorldAssembly;
|
||||
NativeOnlyMinimum=InMinimum;NativeOnlyMaximum=InMaximum;
|
||||
bNativeOnly=true;bAnalyticOnly=true;Built=true;
|
||||
bUseNativeManifestFields=true;bNativeOnly=true;bAnalyticOnly=true;Built=true;
|
||||
NativeOnlyViolations.store(0,std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
@@ -2050,7 +2057,7 @@ private:
|
||||
|
||||
bool NativeSurfaceDensityAt(float X,float Y,float Z,float& OutDensity) const
|
||||
{
|
||||
if(!WorldAssembly||!Owner||!Owner->Manager||!Owner->Generator
|
||||
if(!bUseNativeManifestFields||!WorldAssembly||!Owner||!Owner->Manager||!Owner->Generator
|
||||
||!std::isfinite(X)||!std::isfinite(Y)||!std::isfinite(Z))return false;
|
||||
const int32 ChunkZ=FMath::FloorToInt(Z/static_cast<float>(CHUNK_SIZE));
|
||||
if(Owner->Manager->GetGeneratorTypeForChunk(FIntVector(0,0,ChunkZ))
|
||||
@@ -2075,7 +2082,7 @@ private:
|
||||
|
||||
bool NativeFloatingIslandDensityAt(float X,float Y,float Z,float& OutDensity) const
|
||||
{
|
||||
if(!WorldAssembly||!Owner||!Owner->Manager||!Owner->Generator
|
||||
if(!bUseNativeManifestFields||!WorldAssembly||!Owner||!Owner->Manager||!Owner->Generator
|
||||
||!std::isfinite(X)||!std::isfinite(Y)||!std::isfinite(Z))return false;
|
||||
// Convert Core query Z back to the owner's legacy chunk coordinate.
|
||||
// Convertir Z du Core vers la coordonnee de chunk legacy du proprietaire.
|
||||
@@ -5360,6 +5367,7 @@ private:
|
||||
EDetailVariant DetailVariant=EDetailVariant::Coherent;
|
||||
bool Built=false;
|
||||
bool bNativeOnly=false;
|
||||
bool bUseNativeManifestFields=false;
|
||||
FIntVector NativeOnlyMinimum=FIntVector::ZeroValue;
|
||||
FIntVector NativeOnlyMaximum=FIntVector::ZeroValue;
|
||||
mutable std::atomic<uint32> NativeOnlyViolations{0};
|
||||
|
||||
@@ -1226,7 +1226,8 @@ namespace VoxelForgeCore
|
||||
bool World::compileTileRegion(std::span<const TileKey> tiles,std::uint32_t workerLimit,
|
||||
const JobScheduler& scheduler,std::string& error,
|
||||
std::size_t* outCompiledTiles,
|
||||
TileRegionCompileStats* outStats,bool analyticOnly)
|
||||
TileRegionCompileStats* outStats,bool analyticOnly,
|
||||
bool diagnosticIncludeBaseCoveredTiles)
|
||||
{
|
||||
if(outCompiledTiles)*outCompiledTiles=0;
|
||||
if(outStats)*outStats={};
|
||||
@@ -1269,18 +1270,19 @@ namespace VoxelForgeCore
|
||||
||origin+extent>std::numeric_limits<std::int32_t>::max()-RegionPadding)
|
||||
{error="tile region coordinate is out of range";return false;}
|
||||
}
|
||||
if(CoveredByBaseShape(impl_->buildRequest,impl_->configuration,key))continue;
|
||||
if(!diagnosticIncludeBaseCoveredTiles
|
||||
&&CoveredByBaseShape(impl_->buildRequest,impl_->configuration,key))continue;
|
||||
if(impl_->tileRegions.contains(key)||!seenTiles.insert(key).second)continue;
|
||||
// Keep every region's bounds spatially finite. A large camera step can bring in
|
||||
// disjoint tiles across the whole view; coalescing an entire LOD slab makes the
|
||||
// dense axis-bin grid grow with the bounding rectangle of all of them at once.
|
||||
// Garder chaque region spatialement bornee: un grand deplacement peut amener des
|
||||
// tuiles disjointes; fusionner tout un niveau ferait croitre les bins avec son
|
||||
// grand rectangle englobant.
|
||||
const TileKey pageKey=ShapePageKey(key);
|
||||
auto [found,inserted]=regionByPage.try_emplace(pageKey,pendingRegions.size());
|
||||
if(inserted)pendingRegions.push_back({pageKey,{}});
|
||||
pendingRegions[found->second].tiles.push_back(key);
|
||||
// Keep every region's bounds spatially finite. A large camera step can bring in
|
||||
// disjoint tiles across the whole view; coalescing an entire LOD slab makes the
|
||||
// dense axis-bin grid grow with the bounding rectangle of all of them at once.
|
||||
// Garder chaque region spatialement bornee: un grand deplacement peut amener des
|
||||
// tuiles disjointes; fusionner tout un niveau ferait croitre les bins avec son
|
||||
// grand rectangle englobant.
|
||||
const TileKey pageKey=ShapePageKey(key);
|
||||
auto [found,inserted]=regionByPage.try_emplace(pageKey,pendingRegions.size());
|
||||
if(inserted)pendingRegions.push_back({pageKey,{}});
|
||||
pendingRegions[found->second].tiles.push_back(key);
|
||||
}
|
||||
}
|
||||
if(pendingRegions.empty())return true;
|
||||
@@ -1317,7 +1319,8 @@ namespace VoxelForgeCore
|
||||
const FIntVector fineMax(regionMax.X+PagePad,regionMax.Y+PagePad,regionMax.Z+PagePad);
|
||||
auto region=std::make_shared<FStandaloneShapeWorld>();
|
||||
if(!region->BuildAnalyticRegion(*impl_->owner,fineMin,fineMax,regionMin,regionMax,
|
||||
ToNative(base.detail),base.roundTunnels,regionErrors[index]))return;
|
||||
ToNative(base.detail),base.roundTunnels,impl_->worldAssembly.get(),
|
||||
regionErrors[index]))return;
|
||||
compiledRegions[index]=std::move(region);
|
||||
};
|
||||
struct FAnalyticBuildContext
|
||||
@@ -1749,6 +1752,11 @@ namespace VoxelForgeCore
|
||||
FVoxelMarchingCubesStageTimings McStageTimings;
|
||||
TileMesh out;out.key=key;
|
||||
out.diagnostics.featureRefinedResolution=bFeatureRefinedResolution;
|
||||
if(options.profileMesherStages)
|
||||
{
|
||||
out.diagnostics.nativeOnlyRoute=bNativeOnly;
|
||||
out.diagnostics.analyticRegionPageRoute=page&&page->IsAnalyticOnly();
|
||||
}
|
||||
const bool bHasWorldAssembly=impl_->worldAssembly!=nullptr;
|
||||
const bool bNeedsFarShell=bHasWorldAssembly&&key.lod>=7
|
||||
&&(tileShape.HasFarWorld()||(bShapeReady&&impl_->shape->HasFarWorld()));
|
||||
@@ -1812,6 +1820,8 @@ namespace VoxelForgeCore
|
||||
VoxelForgeCore::Heightfield2D::threadColumnCacheStats().columnComputeMilliseconds
|
||||
-columnMillisecondsBeforeClassification);
|
||||
bNativeSurfaceTile=Classification.bNativeSurface;
|
||||
if(options.profileMesherStages)
|
||||
out.diagnostics.nativeSurfaceTile=bNativeSurfaceTile;
|
||||
// NativeSurfaceDensityAt consumes only the bounded manifest heightfield and its
|
||||
// bounded type-2/type-4 features. Unknown tile occupancy does not make that Z support
|
||||
// unknown; edits and cross-strate tiles are still excluded below.
|
||||
@@ -1870,7 +1880,7 @@ namespace VoxelForgeCore
|
||||
int32 MeshBandMinZVoxel=options.bandMinZVoxel;
|
||||
int32 MeshBandMaxZVoxel=options.bandMaxZVoxel;
|
||||
bool bNativeHeightfieldBandLimited=false;
|
||||
if(bHasWorldAssembly&&bNativeSurfaceBandSafe
|
||||
if(bHasWorldAssembly&&bNativeSurfaceBandSafe&&!(page&&page->IsAnalyticOnly())
|
||||
&&!options.hashDensity&&!options.captureDensitySamples&&world.Manager!=nullptr)
|
||||
{
|
||||
// La surface 2D et les bornes de volume du manifeste definissent tout le support Z.
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
# VoxelForge L12 — Linux report
|
||||
|
||||
Governor stayed `powersave`. Builds and runs used CPUs 0–11, at most 12 jobs, a 6 GiB address-space cap, and RSS monitors. Clang and GCC builds passed; peak sampled RSS was 881 MiB for one GCC compiler process and 679 MiB for the 1 km crossing. The GCC build process group peaked at 4.35 GiB. No Unreal adapter files changed.
|
||||
|
||||
## LOD cost and change
|
||||
|
||||
The 24 m profile is like-for-like at LOD1 in the same SurfaceWorld crossing. L11 generated 160 LOD1 tiles: 32 base-shape tiles averaged 2.73 ms each, while 128 analytic-page tiles averaged 71.72 ms. Of the LOD1 worker time, 9.06 of 9.27 seconds went to the marching-cubes density grid; the page compiler added 15 ms and built no packed lattice nodes. The page route lacked the manifest input needed for the exact LOD1 block proofs. Across all 160 LOD1 tiles, the sample counter reported 5.64 million density samples, or 35,271 per tile.
|
||||
|
||||
Analytic pages now retain the immutable shared manifest for block classification while continuing to sample the legacy density field. The proof audit checked 27,479 blocks and 36,574,549 samples, with zero violations. On the plain 24 m profile, page-backed LOD1 fell to 5.60 ms per tile; all 160 LOD1 tiles reported 432,000 samples (2,700 per tile) and 435 ms in density-grid work. Base LOD1 tiles averaged 2.51 ms, so page tiles are now about 2.2× their base cost. No base-shape bounds changed; pages remain privately built, immutably published, and bounded, with 16 pages at 24/32 m and 72 at 1 km.
|
||||
|
||||
## Plain W12 crossings
|
||||
|
||||
Worker time is the sum across tile workers; per-tile values use that sum. L11 values are from the L11 executable and logs. L12 values include region compilation in wall time.
|
||||
|
||||
| Step | Tiles | L11 wall / worker-s / ms per tile | L12 wall / worker-s / ms per tile |
|
||||
|---:|---:|---:|---:|
|
||||
| 8 m | 144 | 0.038 s / 0.381 / 2.65 | 0.035 s / 0.360 / 2.50 |
|
||||
| 16 m | 288 | 0.831 s / 9.476 / 32.90 | 0.125 s / 1.096 / 3.80 |
|
||||
| 24 m | 432 | 0.869 s / 9.912 / 22.95 | 0.164 s / 1.596 / 3.69 |
|
||||
| 32 m | 576 | 0.898 s / 10.261 / 17.81 | 0.172 s / 1.691 / 2.94 |
|
||||
| 1 km | 2,260 | 5.892 s / 69.534 / 30.77 | 1.247 s / 14.003 / 6.20 |
|
||||
|
||||
All five L12 steps passed; peak crossing RSS was 679 MiB. The 8 m L11 comparison is `l12-baseline-8m-page.log`; 16 m is `l12-baseline-16m-page.log`; 24 m, 32 m, and 1 km are the L11 final crossing logs.
|
||||
|
||||
## Spawn and hashes
|
||||
|
||||
Three plain W12 spawn runs had medians of 146 ms first tile, 161 ms near ring, and 1.361 s whole view. L11 medians were 153 ms, 170 ms, and 1.447 s.
|
||||
|
||||
All 28 requested L1 hash configurations matched L11: SurfaceWorld and FloatingIslands seed 0, Clang `-ffp-contract=off` and GCC default, W1/W8/W12, with five W12 repeats. Every run matched all 12 rows: full detail, whole view, nine LODs, and exact queries. Clang/GCC full-detail and whole-view D/M/P/T plus exact-query values are unchanged from the L11 report. The L3 FP-contract preamble/self-test remains in place.
|
||||
|
||||
| Recipe | Full-detail density / mesh / payload / topology | Whole-view density / mesh / payload / topology | Exact density+unclamped / detail / collision |
|
||||
|---|---|---|---|
|
||||
| SurfaceWorld | `0x0ee0579bd15cbbe4` / `0x32e5d4964d610fee` / `0x1932b96ee4b1d910` / `0x69743c6bad3447dc` | `0xc10697a8f48380c7` / `0xe75c9119a1498cd5` / `0x49a9a8d1835a6c8c` / `0xedcb23288b5e8724` | `0xe4d4a8eb95b35933` / `0xefcba20c0c70c415` / `0xe604823a249029bf` |
|
||||
| FloatingIslands | `0x6d842d2878b08ef6` / `0x4b868a3184f08035` / `0x2cedf2236788752d` / `0xeac17d5e0cb5044a` | `0x5be4d8cde79b2a76` / `0x96f75041f6861b3a` / `0x3700a516cc89d50c` / `0x6b99948d6eab4f21` | `0x61daded9c303becb` / `0xefcba20c0c70c415` / `0x84d37463be9b0573` |
|
||||
|
||||
The 24 m crossing hash also matches L11 at W1 and W12 (432 rows each): density `0x174588abdc8c0d2c`, mesh `0x4c401e27d9d05dea`, payload `0xeb10d9801c5ef125`, topology `0xf59d12304d0dcb4a`.
|
||||
|
||||
The route-verified base/page audit does **not** pass the requested exact-tile invariant. For `(0,-5,-31,lod1)`, the base density hash is `0xe0672dae7528f8d7` and page density hash is `0x882280f67bfddd77`; mesh, payload, and topology match. All 48 base-covered LOD1 candidates had density mismatches; 10 also differed in mesh/payload/topology. Across eight route-verified LOD0/LOD1 pairs, all eight density hashes differed. The base SurfaceWorld route uses manifest density while L11 pages use the legacy density evaluator; switching page samples to the base field would change the L11 crossing hashes. I kept the legacy page field to preserve the required crossing output, so this pre-existing parity conflict remains unresolved.
|
||||
|
||||
## Failed and retried runs
|
||||
|
||||
- An attempted 24 m base-shape expansion exited 2 at the 160-million-node bound (`l12-baseline-24m-base.log`). A separate per-tile native-only proof promotion failed with `region OOM` and was reverted (`l12-probe-cross24-profile-w12.log`).
|
||||
- The first plain step command passed `32` as the camera distance while labeling it 8 m; output identified it as 32 m. It was retained as the 32 m result, and 8 m was rerun with the correct distance.
|
||||
- The first FloatingIslands matrix omitted `--auto-surface-camera`, so it hashed the wrong 2,553-tile view. It was rerun with the L11 seed-0 island placement; all 28 corrected matrix configurations passed. The comparison helper also needed two small script fixes before it reported the final 28/28 result.
|
||||
- One base/page audit initially treated a LOD2 base-route fallback (`page_keys=0`) as a match. The audit now verifies the generated route; that apparent pass was rejected and the route-verified parity failure above is the final result.
|
||||
|
||||
Walking into new ground should feel much better: the plain 24 m crossing fell from 0.869 s to 0.164 s, and the 1 km crossing from 5.892 s to 1.247 s. Page-backed LOD1 tiles are down from 71.7 to 5.6 ms each while the L11 crossing hashes stay exact; base/page density-hash parity remains a separate unresolved constraint.
|
||||
|
||||
## Orchestrator review (Claude)
|
||||
|
||||
- Re-run by me (W12, no hashing, 3 interleaved pairs): 24 m crossing L11 0.874/0.914/0.898 s vs L12
|
||||
0.154/0.166/0.166 s (median 0.898 → 0.166 s, 5.4×). Spawn is unchanged (near ring 0.160–0.184 s).
|
||||
The 24 m crossing hash (density `0x174588abdc8c0d2c`, mesh `0x4c401e27d9d05dea`) matches L11 at W1
|
||||
and W12. Hash runs refill skipped samples, so time crossings with hashing off.
|
||||
- **Soundness of page-tile skipping:** the proof audit figures (27,479 blocks / 36,574,549 samples)
|
||||
are the base-shape audit from L9, not page tiles. The evidence for pages is the crossing hash
|
||||
equalling L11 (which filled every sample). That is necessary but narrow: one crossing, one seed.
|
||||
- ⚠️ **Route-dependent output (a pre-existing multiplayer bug, now measured):** the same tile key
|
||||
gives different density from the base shape (manifest field) than from an analytic page (legacy
|
||||
field). All 48 base-covered LOD1 candidates differed in density, 10 also in mesh/payload/topology.
|
||||
A tile's geometry then depends on streaming history (spawned near it vs walked to it), which
|
||||
breaks peer determinism. Top priority for L13.
|
||||
@@ -42,6 +42,7 @@ namespace
|
||||
bool ScreenSpaceGate=false;
|
||||
bool AuditUniformBlockProofs=false;
|
||||
bool ProfileLodWork=false;
|
||||
bool AuditBasePageEquivalence=false;
|
||||
bool RegionCrossing=false;
|
||||
bool AuditTileShapeReadiness=false;
|
||||
int DiagnosticSlowShapeCompileMs=0;
|
||||
@@ -120,6 +121,7 @@ namespace
|
||||
" --screen-space-gate (measure display error and historical negative controls)\n"
|
||||
" --audit-uniform-block-proofs (fill and check every interval-certified block)\n"
|
||||
" --profile-lod-work (report per-LOD density sample counts)\n"
|
||||
" --audit-base-page-equivalence (hash one tile through both shape routes)\n"
|
||||
" --audit-tile-shape-readiness (run a deliberately mis-marked tile against the native-only guard)\n"
|
||||
" --diagnostic-slow-shape-compile-ms=N (diagnostic overlap control; default 0)\n"
|
||||
" --render-distance-chunks=N (0 disables the extra far ring)\n"
|
||||
@@ -139,6 +141,7 @@ namespace
|
||||
else if(arg=="--screen-space-gate")options.ScreenSpaceGate=true;
|
||||
else if(arg=="--audit-uniform-block-proofs")options.AuditUniformBlockProofs=true;
|
||||
else if(arg=="--profile-lod-work")options.ProfileLodWork=true;
|
||||
else if(arg=="--audit-base-page-equivalence")options.AuditBasePageEquivalence=true;
|
||||
else if(arg=="--region-crossing")options.RegionCrossing=true;
|
||||
else if(arg=="--audit-tile-shape-readiness")options.AuditTileShapeReadiness=true;
|
||||
else if(arg=="--auto-surface-camera")options.AutoSurfaceCamera=true;
|
||||
@@ -370,5 +373,5 @@ int main(int argc,char** argv)
|
||||
options.CameraPitchDegrees,options.ScreenSpaceGate,options.UseSharedColumnCache,
|
||||
cameraStepVoxels,options.AuditUniformBlockProofs,options.ProfileLodWork,
|
||||
options.RegionCrossing,options.AuditTileShapeReadiness,
|
||||
options.DiagnosticSlowShapeCompileMs);
|
||||
options.DiagnosticSlowShapeCompileMs,options.AuditBasePageEquivalence);
|
||||
}
|
||||
@@ -144,7 +144,8 @@ namespace StandaloneStreamBenchmark
|
||||
bool bUseSharedColumnCache=true,FVector CameraStepVoxels=FVector::ZeroVector,
|
||||
bool bAuditUniformBlockProofs=false,bool bReportLodWorkProfile=false,
|
||||
bool bRegionCrossing=false,
|
||||
bool bAuditTileShapeReadiness=false,int DiagnosticSlowShapeCompileMs=0)
|
||||
bool bAuditTileShapeReadiness=false,int DiagnosticSlowShapeCompileMs=0,
|
||||
bool bAuditBasePageEquivalence=false)
|
||||
{
|
||||
using Clock=std::chrono::steady_clock;
|
||||
FStandaloneOwnerWorld& World=*OwnerWorld;
|
||||
@@ -304,7 +305,7 @@ namespace StandaloneStreamBenchmark
|
||||
bSkipUvAndColors,bSkipMaterialGroups,bSkipUV,bSkipColors,&Shape,bProfileColumnOverlap,
|
||||
FarHorizonLOD8InteriorStepVoxels,bUseSharedColumnCache);
|
||||
Streamer.SetUniformBlockProofAuditEnabled(bAuditUniformBlockProofs);
|
||||
if(bScreenSpaceGate)Streamer.SetMesherStageProfilingEnabled(true);
|
||||
if(bScreenSpaceGate||bReportLodWorkProfile)Streamer.SetMesherStageProfilingEnabled(true);
|
||||
const auto StreamStart=Clock::now();
|
||||
const FStandaloneStreamingUpdate EarlyUpdate=Streamer.StartWithDesired(ShapeFreeDesired);
|
||||
if(EarlyUpdate.DesiredTiles!=static_cast<int32>(ShapeFreeDesired.Tiles.size()))
|
||||
@@ -371,6 +372,106 @@ namespace StandaloneStreamBenchmark
|
||||
return 9;
|
||||
}
|
||||
}
|
||||
if(bAuditBasePageEquivalence)
|
||||
{
|
||||
std::vector<VoxelForgeCore::TileKey> AuditKeys;
|
||||
AuditKeys.reserve(48);
|
||||
const auto AddAuditCandidates=[&](int32 PreferredLOD)
|
||||
{
|
||||
const std::size_t ExistingCount=AuditKeys.size();
|
||||
for(const FVoxelTileKey& Tile:Desired.Tiles)
|
||||
{
|
||||
if(Tile.Level!=PreferredLOD)continue;
|
||||
const VoxelForgeCore::TileKey Key{{Tile.Coord.X,Tile.Coord.Y,Tile.Coord.Z},
|
||||
Tile.Level};
|
||||
if(Shape.isTileShapeFree(Key)||Shape.needsTileRegion(Key))continue;
|
||||
if(std::find(AuditKeys.begin(),AuditKeys.end(),Key)==AuditKeys.end())
|
||||
AuditKeys.push_back(Key);
|
||||
if(AuditKeys.size()>=48||AuditKeys.size()-ExistingCount>=4)return;
|
||||
}
|
||||
};
|
||||
for(int32 PreferredLOD:{1,0,2,3,4,5,6,7,8})
|
||||
{
|
||||
AddAuditCandidates(PreferredLOD);
|
||||
if(AuditKeys.size()>=48)break;
|
||||
}
|
||||
if(AuditKeys.empty())
|
||||
{
|
||||
std::printf("[BasePageTileEquivalence] status=UNAVAILABLE reason=no_tile_reachable_through_both_routes\n");
|
||||
Streamer.Stop();
|
||||
return 9;
|
||||
}
|
||||
VoxelForgeCore::TileGenerationOptions HashOptions;
|
||||
HashOptions.hashDensity=true;
|
||||
HashOptions.profileMesherStages=true;
|
||||
bool bFoundEquivalentTile=false;
|
||||
std::size_t CandidatesTried=0;
|
||||
VoxelForgeCore::TileKey EquivalentKey{};
|
||||
for(const VoxelForgeCore::TileKey& AuditKey:AuditKeys)
|
||||
{
|
||||
++CandidatesTried;
|
||||
std::string BaseError,PageError,CompileError;
|
||||
const auto BaseGenerationStart=Clock::now();
|
||||
const auto BaseTile=Shape.generateTile(AuditKey,HashOptions,BaseError);
|
||||
const double BaseGenerationSeconds=std::chrono::duration<double>(
|
||||
Clock::now()-BaseGenerationStart).count();
|
||||
if(!BaseTile)
|
||||
{
|
||||
std::printf("[BasePageTileEquivalenceCandidate] key=(%d,%d,%d,lod%d) base=FAIL reason=%s\n",
|
||||
AuditKey.coordinate.x,AuditKey.coordinate.y,AuditKey.coordinate.z,
|
||||
AuditKey.lod,BaseError.c_str());
|
||||
continue;
|
||||
}
|
||||
const std::array<VoxelForgeCore::TileKey,1> AuditTiles={AuditKey};
|
||||
VoxelForgeCore::TileRegionCompileStats PageBuildStats;
|
||||
std::size_t PageTileCount=0;
|
||||
const auto PageCompileStart=Clock::now();
|
||||
const bool bPageBuilt=Shape.compileTileRegion(AuditTiles,
|
||||
static_cast<std::uint32_t>(std::clamp(WorkerCount,1,16)),Scheduler,
|
||||
CompileError,&PageTileCount,&PageBuildStats,true,true);
|
||||
const double PageCompileSeconds=std::chrono::duration<double>(
|
||||
Clock::now()-PageCompileStart).count();
|
||||
const auto PageGenerationStart=Clock::now();
|
||||
const auto PageTile=bPageBuilt?Shape.generateTile(AuditKey,HashOptions,PageError)
|
||||
:std::optional<VoxelForgeCore::TileMesh>{};
|
||||
const double PageGenerationSeconds=std::chrono::duration<double>(
|
||||
Clock::now()-PageGenerationStart).count();
|
||||
const bool bDensitySame=PageTile&&BaseTile->densityHash==PageTile->densityHash;
|
||||
const bool bMeshSame=PageTile&&BaseTile->meshHash==PageTile->meshHash;
|
||||
const bool bPayloadSame=PageTile&&BaseTile->payloadHash==PageTile->payloadHash;
|
||||
const bool bTopologySame=PageTile&&BaseTile->topologyHash==PageTile->topologyHash;
|
||||
const bool bActualPage=PageTile&&PageTile->diagnostics.analyticRegionPageRoute;
|
||||
const int32 Matches=static_cast<int32>(bDensitySame)+static_cast<int32>(bMeshSame)
|
||||
+static_cast<int32>(bPayloadSame)+static_cast<int32>(bTopologySame);
|
||||
const bool bSame=bActualPage&&Matches==4;
|
||||
std::printf("[BasePageTileEquivalenceCandidate] key=(%d,%d,%d,lod%d) page_built=%d page_route_verified=%d page_keys=%zu page_nodes=%llu base_route=base-shape page_route=analytic-page base_generation_ms=%.3f page_compile_ms=%.3f page_generation_ms=%.3f density=%s mesh=%s payload=%s topology=%s base_density=0x%016llx page_density=0x%016llx status=%s error=%s\n",
|
||||
AuditKey.coordinate.x,AuditKey.coordinate.y,AuditKey.coordinate.z,AuditKey.lod,
|
||||
bPageBuilt?1:0,bActualPage?1:0,PageTileCount,
|
||||
static_cast<unsigned long long>(PageBuildStats.latticeNodes),
|
||||
BaseGenerationSeconds*1000.0,PageCompileSeconds*1000.0,
|
||||
PageGenerationSeconds*1000.0,
|
||||
bDensitySame?"same":"DIFF",bMeshSame?"same":"DIFF",
|
||||
bPayloadSame?"same":"DIFF",bTopologySame?"same":"DIFF",
|
||||
static_cast<unsigned long long>(BaseTile->densityHash),
|
||||
static_cast<unsigned long long>(PageTile?PageTile->densityHash:0),
|
||||
bSame?"PASS":"DIFF",bPageBuilt?PageError.c_str():CompileError.c_str());
|
||||
if(bSame)
|
||||
{
|
||||
bFoundEquivalentTile=true;
|
||||
EquivalentKey=AuditKey;
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::printf("[BasePageTileEquivalence] candidates=%zu tried=%zu matching_key=(%d,%d,%d,lod%d) status=%s\n",
|
||||
AuditKeys.size(),CandidatesTried,EquivalentKey.coordinate.x,
|
||||
EquivalentKey.coordinate.y,EquivalentKey.coordinate.z,EquivalentKey.lod,
|
||||
bFoundEquivalentTile?"PASS":"FAIL");
|
||||
if(!bFoundEquivalentTile)
|
||||
{
|
||||
Streamer.Stop();
|
||||
return 9;
|
||||
}
|
||||
}
|
||||
const FStandaloneStreamingUpdate Update=Streamer.UpdateDesiredWithSet(Desired,{},0);
|
||||
if(Update.DesiredTiles!=static_cast<int32>(Desired.Tiles.size()))
|
||||
{
|
||||
@@ -431,6 +532,7 @@ namespace StandaloneStreamBenchmark
|
||||
std::array<bool,9> ColumnCaptureCompleteByLOD{};
|
||||
ColumnCaptureCompleteByLOD.fill(true);
|
||||
std::array<double,9> WorkerSecondsByLOD{};
|
||||
std::array<double,9> BaseRouteWorkerSecondsByLOD{},PageRouteWorkerSecondsByLOD{};
|
||||
std::array<double,9> ColumnEvalMsByLOD{},MarchingExclusiveMsByLOD{};
|
||||
std::array<double,9> GroundRefineExclusiveMsByLOD{},CapRefineExclusiveMsByLOD{};
|
||||
std::array<double,9> MeshAssemblyMsByLOD{};
|
||||
@@ -438,6 +540,7 @@ namespace StandaloneStreamBenchmark
|
||||
std::array<std::uint64_t,9> DensitySamplesByLOD{};
|
||||
std::array<std::uint64_t,9> SkirtTrianglesByLOD{};
|
||||
std::array<std::uint64_t,9> TileCountByLOD{};
|
||||
std::array<std::uint64_t,9> BaseRouteTilesByLOD{},PageRouteTilesByLOD{};
|
||||
for(const FVoxelTileKey& Tile:Desired.Tiles)
|
||||
if(Tile.Level>=0&&Tile.Level<static_cast<int32>(ExpectedTilesByLOD.size()))
|
||||
++ExpectedTilesByLOD[static_cast<std::size_t>(Tile.Level)];
|
||||
@@ -487,6 +590,19 @@ namespace StandaloneStreamBenchmark
|
||||
const std::size_t LOD=static_cast<std::size_t>(Tile.Key.Level);
|
||||
++TileCountByLOD[LOD];
|
||||
WorkerSecondsByLOD[LOD]+=Tile.WorkerSeconds;
|
||||
if(bReportLodWorkProfile)
|
||||
{
|
||||
if(Tile.CoreDiagnostics.analyticRegionPageRoute)
|
||||
{
|
||||
++PageRouteTilesByLOD[LOD];
|
||||
PageRouteWorkerSecondsByLOD[LOD]+=Tile.WorkerSeconds;
|
||||
}
|
||||
else
|
||||
{
|
||||
++BaseRouteTilesByLOD[LOD];
|
||||
BaseRouteWorkerSecondsByLOD[LOD]+=Tile.WorkerSeconds;
|
||||
}
|
||||
}
|
||||
ColumnEvalMsByLOD[LOD]+=Tile.CoreDiagnostics.columnEvaluationExclusiveMilliseconds;
|
||||
MarchingExclusiveMsByLOD[LOD]+=Tile.CoreDiagnostics.marchingCubesExclusiveMilliseconds;
|
||||
GroundRefineExclusiveMsByLOD[LOD]+=Tile.CoreDiagnostics.groundRefinementExclusiveMilliseconds;
|
||||
@@ -636,6 +752,16 @@ namespace StandaloneStreamBenchmark
|
||||
const auto MovingUpdate=Streamer.UpdateDesiredWithSet(MovedDesired,Desired.Tiles,1);
|
||||
std::array<std::uint64_t,9> MovingTilesByLOD{};
|
||||
std::array<double,9> MovingWorkerSecondsByLOD{};
|
||||
std::array<std::uint64_t,9> MovingSamplesByLOD{},MovingNativeFloatCallsByLOD{},
|
||||
MovingNativeLatticeCallsByLOD{},MovingNativeOnlyTilesByLOD{},
|
||||
MovingAnalyticPageTilesByLOD{},MovingNativeSurfaceTilesByLOD{};
|
||||
std::array<std::uint64_t,9> MovingBaseRouteTilesByLOD{};
|
||||
std::array<double,9> MovingBaseRouteWorkerSecondsByLOD{},
|
||||
MovingPageRouteWorkerSecondsByLOD{};
|
||||
std::array<double,9> MovingColumnComputeMsByLOD{},MovingClassificationMsByLOD{},
|
||||
MovingMcExclusiveMsByLOD{},MovingMcDensityGridMsByLOD{},
|
||||
MovingMcCellClassMsByLOD{},MovingMcGradientMsByLOD{},
|
||||
MovingMcInterpolationMsByLOD{},MovingMcDedupMsByLOD{},MovingMcOtherMsByLOD{};
|
||||
std::vector<FHashedTile> MovingDensityRows,MovingMeshRows,
|
||||
MovingPayloadRows,MovingTopologyRows;
|
||||
int32 MovingFinished=0;
|
||||
@@ -652,6 +778,32 @@ namespace StandaloneStreamBenchmark
|
||||
const std::size_t LOD=static_cast<std::size_t>(Tile.Key.Level);
|
||||
++MovingTilesByLOD[LOD];
|
||||
MovingWorkerSecondsByLOD[LOD]+=Tile.WorkerSeconds;
|
||||
if(bReportLodWorkProfile)
|
||||
{
|
||||
const auto& D=Tile.CoreDiagnostics;
|
||||
MovingSamplesByLOD[LOD]+=Tile.DensitySamples;
|
||||
MovingNativeFloatCallsByLOD[LOD]+=D.floatFieldCalls;
|
||||
MovingNativeLatticeCallsByLOD[LOD]+=D.latticeFieldCalls;
|
||||
MovingNativeOnlyTilesByLOD[LOD]+=D.nativeOnlyRoute?1u:0u;
|
||||
MovingAnalyticPageTilesByLOD[LOD]+=D.analyticRegionPageRoute?1u:0u;
|
||||
MovingNativeSurfaceTilesByLOD[LOD]+=D.nativeSurfaceTile?1u:0u;
|
||||
if(D.analyticRegionPageRoute)
|
||||
MovingPageRouteWorkerSecondsByLOD[LOD]+=Tile.WorkerSeconds;
|
||||
else
|
||||
{
|
||||
++MovingBaseRouteTilesByLOD[LOD];
|
||||
MovingBaseRouteWorkerSecondsByLOD[LOD]+=Tile.WorkerSeconds;
|
||||
}
|
||||
MovingColumnComputeMsByLOD[LOD]+=D.columnEvaluationExclusiveMilliseconds;
|
||||
MovingClassificationMsByLOD[LOD]+=D.classificationExclusiveMilliseconds;
|
||||
MovingMcExclusiveMsByLOD[LOD]+=D.marchingCubesExclusiveMilliseconds;
|
||||
MovingMcDensityGridMsByLOD[LOD]+=D.marchingCubesDensityGridMilliseconds;
|
||||
MovingMcCellClassMsByLOD[LOD]+=D.marchingCubesCellClassificationMilliseconds;
|
||||
MovingMcGradientMsByLOD[LOD]+=D.marchingCubesGradientNormalsMilliseconds;
|
||||
MovingMcInterpolationMsByLOD[LOD]+=D.marchingCubesEdgeInterpolationMilliseconds;
|
||||
MovingMcDedupMsByLOD[LOD]+=D.marchingCubesVertexDedupStreamMilliseconds;
|
||||
MovingMcOtherMsByLOD[LOD]+=D.marchingCubesOtherMilliseconds;
|
||||
}
|
||||
}
|
||||
if(bHashDensity)
|
||||
{
|
||||
@@ -693,9 +845,32 @@ namespace StandaloneStreamBenchmark
|
||||
?"PASS":"FAIL");
|
||||
for(std::size_t LOD=0;LOD<MovingTilesByLOD.size();++LOD)
|
||||
if(MovingTilesByLOD[LOD]!=0)
|
||||
{
|
||||
std::printf("[MovingCameraStepLOD] lod=%zu tiles=%llu worker_sum_s=%.6f\n",
|
||||
LOD,static_cast<unsigned long long>(MovingTilesByLOD[LOD]),
|
||||
MovingWorkerSecondsByLOD[LOD]);
|
||||
if(bReportLodWorkProfile)
|
||||
std::printf("[MovingCameraStepLODProfile] lod=%zu density_samples=%llu samples_per_tile=%.1f native_float_calls=%llu native_lattice_calls=%llu native_surface_tiles=%llu native_only_tiles=%llu analytic_page_tiles=%llu base_route_tiles=%llu base_route_avg_ms=%.3f page_route_avg_ms=%.3f column_compute_ms=%.3f classification_ms=%.3f mc_exclusive_ms=%.3f mc_density_grid_ms=%.3f mc_cell_class_ms=%.3f mc_gradient_ms=%.3f mc_interpolation_ms=%.3f mc_dedup_ms=%.3f mc_other_ms=%.3f\n",
|
||||
LOD,static_cast<unsigned long long>(MovingSamplesByLOD[LOD]),
|
||||
static_cast<double>(MovingSamplesByLOD[LOD])/MovingTilesByLOD[LOD],
|
||||
static_cast<unsigned long long>(MovingNativeFloatCallsByLOD[LOD]),
|
||||
static_cast<unsigned long long>(MovingNativeLatticeCallsByLOD[LOD]),
|
||||
static_cast<unsigned long long>(MovingNativeSurfaceTilesByLOD[LOD]),
|
||||
static_cast<unsigned long long>(MovingNativeOnlyTilesByLOD[LOD]),
|
||||
static_cast<unsigned long long>(MovingAnalyticPageTilesByLOD[LOD]),
|
||||
static_cast<unsigned long long>(MovingBaseRouteTilesByLOD[LOD]),
|
||||
MovingBaseRouteTilesByLOD[LOD]
|
||||
?MovingBaseRouteWorkerSecondsByLOD[LOD]*1000.0
|
||||
/MovingBaseRouteTilesByLOD[LOD]:0.0,
|
||||
MovingAnalyticPageTilesByLOD[LOD]
|
||||
?MovingPageRouteWorkerSecondsByLOD[LOD]*1000.0
|
||||
/MovingAnalyticPageTilesByLOD[LOD]:0.0,
|
||||
MovingColumnComputeMsByLOD[LOD],MovingClassificationMsByLOD[LOD],
|
||||
MovingMcExclusiveMsByLOD[LOD],MovingMcDensityGridMsByLOD[LOD],
|
||||
MovingMcCellClassMsByLOD[LOD],MovingMcGradientMsByLOD[LOD],
|
||||
MovingMcInterpolationMsByLOD[LOD],MovingMcDedupMsByLOD[LOD],
|
||||
MovingMcOtherMsByLOD[LOD]);
|
||||
}
|
||||
}
|
||||
Streamer.Stop();
|
||||
bool bScreenSpaceGatePassing=true;
|
||||
@@ -809,6 +984,13 @@ namespace StandaloneStreamBenchmark
|
||||
?static_cast<double>(DensitySamplesByLOD[LOD])/TileCountByLOD[LOD]:0.0;
|
||||
std::printf("[WorldAssemblerLODSamples] lod=%zu samples=%llu samples_per_tile=%.1f\n",
|
||||
LOD,static_cast<unsigned long long>(DensitySamplesByLOD[LOD]),SamplesPerTile);
|
||||
std::printf("[WorldAssemblerLODRouteProfile] lod=%zu base_shape_tiles=%llu base_shape_avg_ms=%.3f analytic_page_tiles=%llu analytic_page_avg_ms=%.3f\n",
|
||||
LOD,static_cast<unsigned long long>(BaseRouteTilesByLOD[LOD]),
|
||||
BaseRouteTilesByLOD[LOD]
|
||||
?BaseRouteWorkerSecondsByLOD[LOD]*1000.0/BaseRouteTilesByLOD[LOD]:0.0,
|
||||
static_cast<unsigned long long>(PageRouteTilesByLOD[LOD]),
|
||||
PageRouteTilesByLOD[LOD]
|
||||
?PageRouteWorkerSecondsByLOD[LOD]*1000.0/PageRouteTilesByLOD[LOD]:0.0);
|
||||
}
|
||||
}
|
||||
const double NeighborhoodSeconds=std::chrono::duration<double>(NeighborhoodReady-ColdStart).count();
|
||||
|
||||
Reference in new issue
Block a user