diff --git a/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h b/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h index 0f7ecf0..e4deebc 100644 --- a/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h +++ b/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h @@ -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 desiredTiles); void buildFarWorld(); bool isCompiled() const noexcept; diff --git a/Standalone/Core/src/ShapeWorldInternal.h b/Standalone/Core/src/ShapeWorldInternal.h index fa45eab..24c5363 100644 --- a/Standalone/Core/src/ShapeWorldInternal.h +++ b/Standalone/Core/src/ShapeWorldInternal.h @@ -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(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 NativeOnlyViolations{0}; diff --git a/Standalone/Core/src/VoxelForgeCore.cpp b/Standalone/Core/src/VoxelForgeCore.cpp index d9403c6..a7ea6b3 100644 --- a/Standalone/Core/src/VoxelForgeCore.cpp +++ b/Standalone/Core/src/VoxelForgeCore.cpp @@ -1226,7 +1226,8 @@ namespace VoxelForgeCore bool World::compileTileRegion(std::span 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::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(); 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. diff --git a/Standalone/Research/WorldAssemblerM3/Linux12/REPORT.md b/Standalone/Research/WorldAssemblerM3/Linux12/REPORT.md new file mode 100644 index 0000000..1322c16 --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux12/REPORT.md @@ -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. diff --git a/Standalone/Source/StreamBenchmarkHeadless.cpp b/Standalone/Source/StreamBenchmarkHeadless.cpp index 72cb0e5..b57d01f 100644 --- a/Standalone/Source/StreamBenchmarkHeadless.cpp +++ b/Standalone/Source/StreamBenchmarkHeadless.cpp @@ -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); } diff --git a/Standalone/Source/StreamBenchmarkPipeline.h b/Standalone/Source/StreamBenchmarkPipeline.h index 9fc5c4e..edeaca1 100644 --- a/Standalone/Source/StreamBenchmarkPipeline.h +++ b/Standalone/Source/StreamBenchmarkPipeline.h @@ -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(ShapeFreeDesired.Tiles.size())) @@ -371,6 +372,106 @@ namespace StandaloneStreamBenchmark return 9; } } + if(bAuditBasePageEquivalence) + { + std::vector 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( + 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 AuditTiles={AuditKey}; + VoxelForgeCore::TileRegionCompileStats PageBuildStats; + std::size_t PageTileCount=0; + const auto PageCompileStart=Clock::now(); + const bool bPageBuilt=Shape.compileTileRegion(AuditTiles, + static_cast(std::clamp(WorkerCount,1,16)),Scheduler, + CompileError,&PageTileCount,&PageBuildStats,true,true); + const double PageCompileSeconds=std::chrono::duration( + Clock::now()-PageCompileStart).count(); + const auto PageGenerationStart=Clock::now(); + const auto PageTile=bPageBuilt?Shape.generateTile(AuditKey,HashOptions,PageError) + :std::optional{}; + const double PageGenerationSeconds=std::chrono::duration( + 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(bDensitySame)+static_cast(bMeshSame) + +static_cast(bPayloadSame)+static_cast(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(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(BaseTile->densityHash), + static_cast(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(Desired.Tiles.size())) { @@ -431,6 +532,7 @@ namespace StandaloneStreamBenchmark std::array ColumnCaptureCompleteByLOD{}; ColumnCaptureCompleteByLOD.fill(true); std::array WorkerSecondsByLOD{}; + std::array BaseRouteWorkerSecondsByLOD{},PageRouteWorkerSecondsByLOD{}; std::array ColumnEvalMsByLOD{},MarchingExclusiveMsByLOD{}; std::array GroundRefineExclusiveMsByLOD{},CapRefineExclusiveMsByLOD{}; std::array MeshAssemblyMsByLOD{}; @@ -438,6 +540,7 @@ namespace StandaloneStreamBenchmark std::array DensitySamplesByLOD{}; std::array SkirtTrianglesByLOD{}; std::array TileCountByLOD{}; + std::array BaseRouteTilesByLOD{},PageRouteTilesByLOD{}; for(const FVoxelTileKey& Tile:Desired.Tiles) if(Tile.Level>=0&&Tile.Level(ExpectedTilesByLOD.size())) ++ExpectedTilesByLOD[static_cast(Tile.Level)]; @@ -487,6 +590,19 @@ namespace StandaloneStreamBenchmark const std::size_t LOD=static_cast(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 MovingTilesByLOD{}; std::array MovingWorkerSecondsByLOD{}; + std::array MovingSamplesByLOD{},MovingNativeFloatCallsByLOD{}, + MovingNativeLatticeCallsByLOD{},MovingNativeOnlyTilesByLOD{}, + MovingAnalyticPageTilesByLOD{},MovingNativeSurfaceTilesByLOD{}; + std::array MovingBaseRouteTilesByLOD{}; + std::array MovingBaseRouteWorkerSecondsByLOD{}, + MovingPageRouteWorkerSecondsByLOD{}; + std::array MovingColumnComputeMsByLOD{},MovingClassificationMsByLOD{}, + MovingMcExclusiveMsByLOD{},MovingMcDensityGridMsByLOD{}, + MovingMcCellClassMsByLOD{},MovingMcGradientMsByLOD{}, + MovingMcInterpolationMsByLOD{},MovingMcDedupMsByLOD{},MovingMcOtherMsByLOD{}; std::vector MovingDensityRows,MovingMeshRows, MovingPayloadRows,MovingTopologyRows; int32 MovingFinished=0; @@ -652,6 +778,32 @@ namespace StandaloneStreamBenchmark const std::size_t LOD=static_cast(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[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(MovingSamplesByLOD[LOD]), + static_cast(MovingSamplesByLOD[LOD])/MovingTilesByLOD[LOD], + static_cast(MovingNativeFloatCallsByLOD[LOD]), + static_cast(MovingNativeLatticeCallsByLOD[LOD]), + static_cast(MovingNativeSurfaceTilesByLOD[LOD]), + static_cast(MovingNativeOnlyTilesByLOD[LOD]), + static_cast(MovingAnalyticPageTilesByLOD[LOD]), + static_cast(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(DensitySamplesByLOD[LOD])/TileCountByLOD[LOD]:0.0; std::printf("[WorldAssemblerLODSamples] lod=%zu samples=%llu samples_per_tile=%.1f\n", LOD,static_cast(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(BaseRouteTilesByLOD[LOD]), + BaseRouteTilesByLOD[LOD] + ?BaseRouteWorkerSecondsByLOD[LOD]*1000.0/BaseRouteTilesByLOD[LOD]:0.0, + static_cast(PageRouteTilesByLOD[LOD]), + PageRouteTilesByLOD[LOD] + ?PageRouteWorkerSecondsByLOD[LOD]*1000.0/PageRouteTilesByLOD[LOD]:0.0); } } const double NeighborhoodSeconds=std::chrono::duration(NeighborhoodReady-ColdStart).count();