wip(assembler): round L13 -- one density field for every route (multiplayer determinism fix)
Region pages fell back to the legacy GetDensityAt evaluator while the base shape used the Core manifest field, so a tile's density (and sometimes mesh) depended on streaming history. Pages are now bound to their world's base FStandaloneShapeWorld evaluator before publication; density, lattice, detail, classification and solid queries all delegate to it. WorldAssemblyEvaluatorVersion 5 -> 6. Route-parity audit: fails on L12 (5/5); after the fix 0 mismatches over 87 tile pairs (base/page, base/native-only, edited, two histories), 56 exact-query pairs, 2.3M page samples, 4 seeds/recipes. Page-tile proof audit: 1,274 blocks, 0 violations. The spawn and island worlds are unchanged; crossing output changed as intended. Reviewer interleaved spawn: +1.1% (noise). 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
32b8670833
commit
976d012a86
10 files changed
+767
-11
No files matched your search
@@ -43,6 +43,7 @@ namespace
|
||||
bool AuditUniformBlockProofs=false;
|
||||
bool ProfileLodWork=false;
|
||||
bool AuditBasePageEquivalence=false;
|
||||
bool AuditRouteParity=false;
|
||||
bool RegionCrossing=false;
|
||||
bool AuditTileShapeReadiness=false;
|
||||
int DiagnosticSlowShapeCompileMs=0;
|
||||
@@ -122,6 +123,7 @@ namespace
|
||||
" --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-route-parity (compare page, native-only, exact-query and mesh 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"
|
||||
@@ -142,6 +144,7 @@ namespace
|
||||
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=="--audit-route-parity")options.AuditRouteParity=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;
|
||||
@@ -373,5 +376,6 @@ int main(int argc,char** argv)
|
||||
options.CameraPitchDegrees,options.ScreenSpaceGate,options.UseSharedColumnCache,
|
||||
cameraStepVoxels,options.AuditUniformBlockProofs,options.ProfileLodWork,
|
||||
options.RegionCrossing,options.AuditTileShapeReadiness,
|
||||
options.DiagnosticSlowShapeCompileMs,options.AuditBasePageEquivalence);
|
||||
options.DiagnosticSlowShapeCompileMs,options.AuditBasePageEquivalence,
|
||||
options.AuditRouteParity);
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <bit>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
@@ -23,6 +24,113 @@
|
||||
|
||||
namespace StandaloneStreamBenchmark
|
||||
{
|
||||
struct FRouteDensityComparison
|
||||
{
|
||||
std::uint64_t samples=0;
|
||||
std::uint64_t signMismatches=0;
|
||||
std::uint64_t crossingEdgeMismatches=0;
|
||||
std::uint64_t sharedCrossingEdges=0;
|
||||
std::uint64_t exactQuerySamples=0;
|
||||
std::uint64_t exactQueryMismatches=0;
|
||||
double maxAbsoluteDelta=0.0;
|
||||
double sumAbsoluteDelta=0.0;
|
||||
double maxSurfaceDisplacementVoxels=0.0;
|
||||
double sumSurfaceDisplacementVoxels=0.0;
|
||||
bool valid=false;
|
||||
};
|
||||
|
||||
inline FRouteDensityComparison CompareRouteDensities(
|
||||
const VoxelForgeCore::TileMesh& A,const VoxelForgeCore::TileMesh& B,
|
||||
const VoxelForgeCore::World& World)
|
||||
{
|
||||
FRouteDensityComparison Result;
|
||||
if(A.debugDensitySamples.empty()
|
||||
||A.debugDensitySamples.size()!=B.debugDensitySamples.size())return Result;
|
||||
const double Root=std::cbrt(static_cast<double>(A.debugDensitySamples.size()));
|
||||
const std::int32_t Dim=static_cast<std::int32_t>(std::llround(Root));
|
||||
if(Dim<4||static_cast<std::size_t>(Dim)*Dim*Dim!=A.debugDensitySamples.size())return Result;
|
||||
const std::int32_t GridDim=Dim-3;
|
||||
const std::int32_t Extent=CHUNK_SIZE<<A.key.lod;
|
||||
if(GridDim<=0||Extent%GridDim!=0)return Result;
|
||||
const std::int32_t Step=Extent/GridDim;
|
||||
const FVoxelTileKey NativeKey(
|
||||
FIntVector(A.key.coordinate.x,A.key.coordinate.y,A.key.coordinate.z),A.key.lod);
|
||||
const FIntVector Origin=NativeKey.OriginVoxels();
|
||||
const auto Index=[Dim](std::int32_t X,std::int32_t Y,std::int32_t Z)
|
||||
{return (static_cast<std::size_t>(Z)*Dim+Y)*Dim+X;};
|
||||
const auto IsSolid=[](float Density){return Density<0.0f;};
|
||||
const auto EdgeCrosses=[&](const std::vector<float>& Values,
|
||||
std::int32_t X0,std::int32_t Y0,std::int32_t Z0,
|
||||
std::int32_t X1,std::int32_t Y1,std::int32_t Z1)
|
||||
{
|
||||
return IsSolid(Values[Index(X0,Y0,Z0)])!=IsSolid(Values[Index(X1,Y1,Z1)]);
|
||||
};
|
||||
for(std::int32_t Z=0;Z<Dim;++Z)
|
||||
for(std::int32_t Y=0;Y<Dim;++Y)
|
||||
for(std::int32_t X=0;X<Dim;++X)
|
||||
{
|
||||
const std::size_t I=Index(X,Y,Z);
|
||||
const float DA=A.debugDensitySamples[I],DB=B.debugDensitySamples[I];
|
||||
if(!std::isfinite(DA)||!std::isfinite(DB))return Result;
|
||||
const double Delta=std::abs(static_cast<double>(DA)-DB);
|
||||
Result.maxAbsoluteDelta=std::max(Result.maxAbsoluteDelta,Delta);
|
||||
Result.sumAbsoluteDelta+=Delta;
|
||||
Result.signMismatches+=IsSolid(DA)!=IsSolid(DB)?1u:0u;
|
||||
++Result.samples;
|
||||
|
||||
const std::int32_t WorldX=Origin.X+(X-1)*Step;
|
||||
const std::int32_t WorldY=Origin.Y+(Y-1)*Step;
|
||||
const std::int32_t WorldZ=Origin.Z+(Z-1)*Step;
|
||||
const VoxelForgeCore::Vec3i Lattice{WorldX,WorldY,WorldZ};
|
||||
const float ExactLattice=World.densityAtLattice(Lattice);
|
||||
const float ExactPoint=World.densityAt({static_cast<double>(WorldX),
|
||||
static_cast<double>(WorldY),static_cast<double>(WorldZ)});
|
||||
++Result.exactQuerySamples;
|
||||
if(std::bit_cast<std::uint32_t>(DA)!=std::bit_cast<std::uint32_t>(ExactLattice)
|
||||
||std::bit_cast<std::uint32_t>(DA)!=std::bit_cast<std::uint32_t>(ExactPoint))
|
||||
++Result.exactQueryMismatches;
|
||||
}
|
||||
|
||||
const auto CompareEdge=[&](std::int32_t X0,std::int32_t Y0,std::int32_t Z0,
|
||||
std::int32_t X1,std::int32_t Y1,std::int32_t Z1)
|
||||
{
|
||||
const bool CrossA=EdgeCrosses(A.debugDensitySamples,X0,Y0,Z0,X1,Y1,Z1);
|
||||
const bool CrossB=EdgeCrosses(B.debugDensitySamples,X0,Y0,Z0,X1,Y1,Z1);
|
||||
if(CrossA!=CrossB)++Result.crossingEdgeMismatches;
|
||||
if(!CrossA||!CrossB)return;
|
||||
const float A0=A.debugDensitySamples[Index(X0,Y0,Z0)];
|
||||
const float A1=A.debugDensitySamples[Index(X1,Y1,Z1)];
|
||||
const float B0=B.debugDensitySamples[Index(X0,Y0,Z0)];
|
||||
const float B1=B.debugDensitySamples[Index(X1,Y1,Z1)];
|
||||
const double TA=static_cast<double>(A0)/(static_cast<double>(A0)-A1);
|
||||
const double TB=static_cast<double>(B0)/(static_cast<double>(B0)-B1);
|
||||
const double Displacement=std::abs(TA-TB)*Step;
|
||||
Result.maxSurfaceDisplacementVoxels=std::max(
|
||||
Result.maxSurfaceDisplacementVoxels,Displacement);
|
||||
Result.sumSurfaceDisplacementVoxels+=Displacement;
|
||||
++Result.sharedCrossingEdges;
|
||||
};
|
||||
for(std::int32_t Z=0;Z<Dim;++Z)
|
||||
for(std::int32_t Y=0;Y<Dim;++Y)
|
||||
for(std::int32_t X=0;X<Dim;++X)
|
||||
{
|
||||
if(X+1<Dim)CompareEdge(X,Y,Z,X+1,Y,Z);
|
||||
if(Y+1<Dim)CompareEdge(X,Y,Z,X,Y+1,Z);
|
||||
if(Z+1<Dim)CompareEdge(X,Y,Z,X,Y,Z+1);
|
||||
}
|
||||
Result.valid=true;
|
||||
return Result;
|
||||
}
|
||||
|
||||
inline bool SameTileOutput(const VoxelForgeCore::TileMesh& A,
|
||||
const VoxelForgeCore::TileMesh& B)
|
||||
{
|
||||
return A.hasDensityHash&&B.hasDensityHash&&A.hasMeshHash&&B.hasMeshHash
|
||||
&&A.hasPayloadHash&&B.hasPayloadHash&&A.hasTopologyHash&&B.hasTopologyHash
|
||||
&&A.densityHash==B.densityHash&&A.meshHash==B.meshHash
|
||||
&&A.payloadHash==B.payloadHash&&A.topologyHash==B.topologyHash;
|
||||
}
|
||||
|
||||
struct FSceneExportWriter
|
||||
{
|
||||
std::ofstream File;
|
||||
@@ -145,7 +253,7 @@ namespace StandaloneStreamBenchmark
|
||||
bool bAuditUniformBlockProofs=false,bool bReportLodWorkProfile=false,
|
||||
bool bRegionCrossing=false,
|
||||
bool bAuditTileShapeReadiness=false,int DiagnosticSlowShapeCompileMs=0,
|
||||
bool bAuditBasePageEquivalence=false)
|
||||
bool bAuditBasePageEquivalence=false,bool bAuditRouteParity=false)
|
||||
{
|
||||
using Clock=std::chrono::steady_clock;
|
||||
FStandaloneOwnerWorld& World=*OwnerWorld;
|
||||
@@ -472,6 +580,447 @@ namespace StandaloneStreamBenchmark
|
||||
return 9;
|
||||
}
|
||||
}
|
||||
if(bAuditRouteParity)
|
||||
{
|
||||
struct FRouteAuditCandidate
|
||||
{
|
||||
VoxelForgeCore::TileKey Key;
|
||||
bool bWouldRequestPage=false;
|
||||
};
|
||||
struct FRouteAuditRow
|
||||
{
|
||||
FRouteAuditCandidate Candidate;
|
||||
std::optional<VoxelForgeCore::TileMesh> Base;
|
||||
};
|
||||
VoxelForgeCore::TileGenerationOptions CaptureOptions;
|
||||
CaptureOptions.hashDensity=true;
|
||||
CaptureOptions.captureDensitySamples=true;
|
||||
CaptureOptions.forceFullDensityCapture=true;
|
||||
CaptureOptions.profileMesherStages=true;
|
||||
std::vector<FRouteAuditRow> Rows;
|
||||
Rows.reserve(static_cast<std::size_t>(World.FullResClipLevels)*8);
|
||||
std::vector<VoxelForgeCore::TileKey> PageKeys;
|
||||
PageKeys.reserve(Rows.capacity());
|
||||
const int32 AuditPageLODs=std::min(World.FullResClipLevels,3);
|
||||
std::vector<const FStandaloneDesiredTileSet*> CandidateSets{&Desired};
|
||||
if(bHasCameraStep)CandidateSets.push_back(&MovedDesired);
|
||||
for(int32 LOD=0;LOD<AuditPageLODs;++LOD)
|
||||
{
|
||||
for(const bool bPageRequested:{true,false})
|
||||
{
|
||||
int32 SurfaceCandidates=0;
|
||||
bool bHaveFlatCandidate=false;
|
||||
int32 Inspected=0;
|
||||
for(const FStandaloneDesiredTileSet* CandidateSet:CandidateSets)
|
||||
{
|
||||
for(const FVoxelTileKey& Tile:CandidateSet->Tiles)
|
||||
{
|
||||
if(Tile.Level!=LOD)continue;
|
||||
const VoxelForgeCore::TileKey Key{{Tile.Coord.X,Tile.Coord.Y,Tile.Coord.Z},Tile.Level};
|
||||
if(Shape.isTileShapeFree(Key)
|
||||
||Shape.needsTileRegion(Key)!=bPageRequested)continue;
|
||||
if(Inspected++>=256)break;
|
||||
std::string BaseError;
|
||||
auto Base=Shape.generateTile(Key,CaptureOptions,BaseError);
|
||||
if(!Base)
|
||||
{
|
||||
std::printf("[RouteParityCandidate] route=%s key=(%d,%d,%d,lod%d) base=FAIL error=%s\n",
|
||||
bPageRequested?"actual-page":"base-covered-page",Key.coordinate.x,
|
||||
Key.coordinate.y,Key.coordinate.z,Key.lod,BaseError.c_str());
|
||||
continue;
|
||||
}
|
||||
const bool bHasSurface=!Base->vertices.empty()&&!Base->indices.empty();
|
||||
if(bHasSurface&&SurfaceCandidates<3)
|
||||
{
|
||||
Rows.push_back({{Key,bPageRequested},std::move(Base)});
|
||||
PageKeys.push_back(Key);
|
||||
++SurfaceCandidates;
|
||||
}
|
||||
else if(!bHasSurface&&!bHaveFlatCandidate)
|
||||
{
|
||||
Rows.push_back({{Key,bPageRequested},std::move(Base)});
|
||||
PageKeys.push_back(Key);
|
||||
bHaveFlatCandidate=true;
|
||||
}
|
||||
if(SurfaceCandidates>=3&&bHaveFlatCandidate)break;
|
||||
}
|
||||
if(SurfaceCandidates>=3&&bHaveFlatCandidate)break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(Rows.empty())
|
||||
{
|
||||
std::printf("[RouteParityAudit] status=UNAVAILABLE reason=no_page_route_candidates\n");
|
||||
Streamer.Stop();
|
||||
return 9;
|
||||
}
|
||||
|
||||
VoxelForgeCore::TileGenerationOptions PageOptions=CaptureOptions;
|
||||
PageOptions.auditUniformBlockProofs=true;
|
||||
int32 BasePageMismatches=0,PageRoutesVerified=0;
|
||||
std::uint64_t PageProofBlocks=0,PageProofSamples=0,PageProofFallbacks=0,
|
||||
PageProofUnknown=0,PageProofViolations=0;
|
||||
std::uint64_t PageExactSamples=0,PageExactMismatches=0;
|
||||
double MaxDensityDelta=0.0,SumDensityDelta=0.0;
|
||||
std::uint64_t ComparedDensitySamples=0,SignMismatches=0,
|
||||
CrossingEdgeMismatches=0,SharedCrossingEdges=0;
|
||||
double MaxSurfaceDisplacement=0.0,SumSurfaceDisplacement=0.0;
|
||||
for(const FRouteAuditRow& Row:Rows)
|
||||
if(Row.Base)
|
||||
{
|
||||
const FRouteDensityComparison Query=CompareRouteDensities(
|
||||
*Row.Base,*Row.Base,Shape);
|
||||
PageExactSamples+=Query.exactQuerySamples;
|
||||
PageExactMismatches+=Query.exactQueryMismatches;
|
||||
}
|
||||
else ++BasePageMismatches;
|
||||
VoxelForgeCore::TileRegionCompileStats PageStats;
|
||||
std::size_t CoveredPageKeys=0;
|
||||
std::string PageCompileError;
|
||||
const bool bPagesCompiled=Shape.compileTileRegion(PageKeys,
|
||||
static_cast<std::uint32_t>(std::clamp(WorkerCount,1,16)),Scheduler,
|
||||
PageCompileError,&CoveredPageKeys,&PageStats,true,true);
|
||||
if(!bPagesCompiled)
|
||||
{
|
||||
std::printf("[RouteParityAudit] page_compile=FAIL error=%s\n",PageCompileError.c_str());
|
||||
Streamer.Stop();
|
||||
return 9;
|
||||
}
|
||||
for(FRouteAuditRow& Row:Rows)
|
||||
{
|
||||
std::string Error;
|
||||
const auto Page=Shape.generateTile(Row.Candidate.Key,PageOptions,Error);
|
||||
const bool bVerifiedPage=Page&&Page->diagnostics.analyticRegionPageRoute;
|
||||
PageRoutesVerified+=bVerifiedPage?1:0;
|
||||
const bool bSame=Row.Base&&bVerifiedPage&&SameTileOutput(*Row.Base,*Page);
|
||||
const FRouteDensityComparison Comparison=Page&&Row.Base
|
||||
?CompareRouteDensities(*Page,*Row.Base,Shape):FRouteDensityComparison{};
|
||||
if(!bSame||!Comparison.valid||Comparison.exactQueryMismatches!=0)
|
||||
++BasePageMismatches;
|
||||
PageExactSamples+=Comparison.exactQuerySamples;
|
||||
PageExactMismatches+=Comparison.exactQueryMismatches;
|
||||
MaxDensityDelta=std::max(MaxDensityDelta,Comparison.maxAbsoluteDelta);
|
||||
SumDensityDelta+=Comparison.sumAbsoluteDelta;
|
||||
ComparedDensitySamples+=Comparison.samples;
|
||||
SignMismatches+=Comparison.signMismatches;
|
||||
CrossingEdgeMismatches+=Comparison.crossingEdgeMismatches;
|
||||
SharedCrossingEdges+=Comparison.sharedCrossingEdges;
|
||||
MaxSurfaceDisplacement=std::max(MaxSurfaceDisplacement,
|
||||
Comparison.maxSurfaceDisplacementVoxels);
|
||||
SumSurfaceDisplacement+=Comparison.sumSurfaceDisplacementVoxels;
|
||||
if(Page)
|
||||
{
|
||||
PageProofBlocks+=Page->diagnostics.uniformBlockProofsChecked;
|
||||
PageProofSamples+=Page->diagnostics.uniformBlockProofSamplesChecked;
|
||||
PageProofFallbacks+=Page->diagnostics.uniformBlockProofFallbackBlocks;
|
||||
PageProofUnknown+=Page->diagnostics.uniformBlockProofUnknownBlocks;
|
||||
PageProofViolations+=Page->diagnostics.uniformBlockProofViolations;
|
||||
}
|
||||
std::printf("[RouteParityPair] route=base/page key=(%d,%d,%d,lod%d) page_requested=%d page_verified=%d same_density=%d same_mesh=%d same_payload=%d same_topology=%d samples=%llu max_abs_density_delta=%.9g sign_mismatches=%llu edge_crossing_mismatches=%llu shared_surface_edges=%llu max_surface_delta_vox=%.9g exact_query_mismatches=%llu base_native_float=%llu page_native_float=%llu first_density=(%.9g,%.9g) status=%s error=%s\n",
|
||||
Row.Candidate.Key.coordinate.x,Row.Candidate.Key.coordinate.y,
|
||||
Row.Candidate.Key.coordinate.z,Row.Candidate.Key.lod,
|
||||
Row.Candidate.bWouldRequestPage?1:0,bVerifiedPage?1:0,
|
||||
Row.Base&&Page&&Row.Base->densityHash==Page->densityHash?1:0,
|
||||
Row.Base&&Page&&Row.Base->meshHash==Page->meshHash?1:0,
|
||||
Row.Base&&Page&&Row.Base->payloadHash==Page->payloadHash?1:0,
|
||||
Row.Base&&Page&&Row.Base->topologyHash==Page->topologyHash?1:0,
|
||||
static_cast<unsigned long long>(Comparison.samples),
|
||||
Comparison.maxAbsoluteDelta,
|
||||
static_cast<unsigned long long>(Comparison.signMismatches),
|
||||
static_cast<unsigned long long>(Comparison.crossingEdgeMismatches),
|
||||
static_cast<unsigned long long>(Comparison.sharedCrossingEdges),
|
||||
Comparison.maxSurfaceDisplacementVoxels,
|
||||
static_cast<unsigned long long>(Comparison.exactQueryMismatches),
|
||||
static_cast<unsigned long long>(Row.Base?Row.Base->diagnostics.floatFieldCalls:0),
|
||||
static_cast<unsigned long long>(Page?Page->diagnostics.floatFieldCalls:0),
|
||||
Row.Base&&!Row.Base->debugDensitySamples.empty()
|
||||
?Row.Base->debugDensitySamples.front():0.0f,
|
||||
Page&&!Page->debugDensitySamples.empty()?Page->debugDensitySamples.front():0.0f,
|
||||
bSame&&Comparison.valid&&Comparison.exactQueryMismatches==0?"PASS":"FAIL",
|
||||
Page?Error.c_str():Error.c_str());
|
||||
}
|
||||
|
||||
int32 NativeMismatches=0,NativeRoutesVerified=0;
|
||||
std::uint64_t NativeExactSamples=0,NativeExactMismatches=0;
|
||||
int32 NativePairs=0;
|
||||
VoxelForgeCore::TileGenerationOptions NativeOptions=CaptureOptions;
|
||||
NativeOptions.diagnosticForceNativeOnly=true;
|
||||
for(const FVoxelTileKey& Tile:ShapeFreeTiles)
|
||||
{
|
||||
if(NativePairs>=9)break;
|
||||
if(!Neighborhood.contains(Tile))continue;
|
||||
const VoxelForgeCore::TileKey Key{{Tile.Coord.X,Tile.Coord.Y,Tile.Coord.Z},Tile.Level};
|
||||
std::string BaseError,NativeError;
|
||||
const auto Base=Shape.generateTile(Key,CaptureOptions,BaseError);
|
||||
const auto Native=Shape.generateTile(Key,NativeOptions,NativeError);
|
||||
++NativePairs;
|
||||
const bool bVerifiedNative=Native&&Native->diagnostics.nativeOnlyRoute;
|
||||
NativeRoutesVerified+=bVerifiedNative?1:0;
|
||||
const bool bSame=Base&&bVerifiedNative&&SameTileOutput(*Base,*Native);
|
||||
const FRouteDensityComparison Comparison=Base&&Native
|
||||
?CompareRouteDensities(*Native,*Base,Shape):FRouteDensityComparison{};
|
||||
NativeExactSamples+=Comparison.exactQuerySamples;
|
||||
NativeExactMismatches+=Comparison.exactQueryMismatches;
|
||||
if(!bSame||!Comparison.valid||Comparison.exactQueryMismatches!=0)
|
||||
++NativeMismatches;
|
||||
std::printf("[RouteParityPair] route=compiled/native-only key=(%d,%d,%d,lod%d) native_verified=%d same_density=%d same_mesh=%d same_payload=%d same_topology=%d samples=%llu exact_query_mismatches=%llu status=%s error=%s\n",
|
||||
Key.coordinate.x,Key.coordinate.y,Key.coordinate.z,Key.lod,
|
||||
bVerifiedNative?1:0,Base&&Native&&Base->densityHash==Native->densityHash?1:0,
|
||||
Base&&Native&&Base->meshHash==Native->meshHash?1:0,
|
||||
Base&&Native&&Base->payloadHash==Native->payloadHash?1:0,
|
||||
Base&&Native&&Base->topologyHash==Native->topologyHash?1:0,
|
||||
static_cast<unsigned long long>(Comparison.samples),
|
||||
static_cast<unsigned long long>(Comparison.exactQueryMismatches),
|
||||
bSame&&Comparison.valid&&Comparison.exactQueryMismatches==0?"PASS":"FAIL",
|
||||
Native?NativeError.c_str():NativeError.c_str());
|
||||
}
|
||||
|
||||
int32 ExactQueryPairs=0,ExactQueryMismatches=0;
|
||||
std::uint64_t ExactQuerySamples=0;
|
||||
for(int32 LOD=World.FullResClipLevels;LOD<=World.MaxClipLevel&&LOD<9;++LOD)
|
||||
{
|
||||
int32 Added=0;
|
||||
for(const FVoxelTileKey& Tile:Desired.Tiles)
|
||||
{
|
||||
if(Tile.Level!=LOD||Added>=2)continue;
|
||||
const VoxelForgeCore::TileKey Key{{Tile.Coord.X,Tile.Coord.Y,Tile.Coord.Z},Tile.Level};
|
||||
std::string Error;
|
||||
const auto Mesh=Shape.generateTile(Key,CaptureOptions,Error);
|
||||
const FRouteDensityComparison Comparison=Mesh
|
||||
?CompareRouteDensities(*Mesh,*Mesh,Shape):FRouteDensityComparison{};
|
||||
++ExactQueryPairs;++Added;
|
||||
ExactQuerySamples+=Comparison.exactQuerySamples;
|
||||
if(!Mesh||!Comparison.valid||Comparison.exactQueryMismatches!=0)
|
||||
++ExactQueryMismatches;
|
||||
std::printf("[RouteParityPair] route=tile/exact-query key=(%d,%d,%d,lod%d) samples=%llu exact_query_mismatches=%llu status=%s error=%s\n",
|
||||
Key.coordinate.x,Key.coordinate.y,Key.coordinate.z,Key.lod,
|
||||
static_cast<unsigned long long>(Comparison.exactQuerySamples),
|
||||
static_cast<unsigned long long>(Comparison.exactQueryMismatches),
|
||||
Mesh&&Comparison.valid&&Comparison.exactQueryMismatches==0?"PASS":"FAIL",
|
||||
Error.c_str());
|
||||
}
|
||||
}
|
||||
int32 EditPairs=0,EditMismatches=0;
|
||||
const auto EditRow=std::find_if(Rows.begin(),Rows.end(),[](const FRouteAuditRow& Row)
|
||||
{
|
||||
return Row.Base&&!Row.Candidate.bWouldRequestPage&&Row.Candidate.Key.lod<=1
|
||||
&&!Row.Base->vertices.empty()&&!Row.Base->indices.empty();
|
||||
});
|
||||
if(EditRow!=Rows.end())
|
||||
{
|
||||
VoxelForgeCore::World EditWorld(CoreSnapshot);
|
||||
std::string EditCompileError;
|
||||
if(!EditWorld.compile(ShapeRequest,Scheduler,EditCompileError))
|
||||
{
|
||||
++EditMismatches;
|
||||
std::printf("[RouteParityEdit] status=FAIL stage=compile error=%s\n",
|
||||
EditCompileError.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
const VoxelForgeCore::TileKey Key=EditRow->Candidate.Key;
|
||||
const std::int32_t Extent=CHUNK_SIZE<<Key.lod;
|
||||
const std::array<VoxelForgeCore::TileKey,1> EditKeys={Key};
|
||||
VoxelForgeCore::TileGenerationOptions EditOptions=CaptureOptions;
|
||||
EditOptions.auditUniformBlockProofs=true;
|
||||
std::string BeforeError,ApplyError,BaseError,PageCompileError,PageError;
|
||||
const auto Before=EditWorld.generateTile(Key,EditOptions,BeforeError);
|
||||
VoxelForgeCore::EditTransaction Transaction;
|
||||
Transaction.sequence=1;
|
||||
VoxelForgeCore::EditBrush Brush;
|
||||
Brush.shape=VoxelForgeCore::EditBrush::Shape::sphere;
|
||||
Brush.operation=VoxelForgeCore::EditBrush::Operation::densityOffset;
|
||||
bool bSurfaceCenterFound=false;
|
||||
if(Before&&!Before->debugDensitySamples.empty())
|
||||
{
|
||||
const std::int32_t Dim=static_cast<std::int32_t>(std::llround(
|
||||
std::cbrt(static_cast<double>(Before->debugDensitySamples.size()))));
|
||||
const std::int32_t GridDim=Dim-3;
|
||||
if(Dim>=4&&static_cast<std::size_t>(Dim)*Dim*Dim
|
||||
==Before->debugDensitySamples.size()&&GridDim>0&&Extent%GridDim==0)
|
||||
{
|
||||
const std::int32_t Step=Extent/GridDim;
|
||||
const std::int32_t OriginX=Key.coordinate.x*Extent;
|
||||
const std::int32_t OriginY=Key.coordinate.y*Extent;
|
||||
const std::int32_t OriginZ=Key.coordinate.z*Extent;
|
||||
const auto Index=[Dim](std::int32_t X,std::int32_t Y,std::int32_t Z)
|
||||
{return (static_cast<std::size_t>(Z)*Dim+Y)*Dim+X;};
|
||||
for(std::int32_t Z=0;Z<Dim&&!bSurfaceCenterFound;++Z)
|
||||
for(std::int32_t Y=0;Y<Dim&&!bSurfaceCenterFound;++Y)
|
||||
for(std::int32_t X=0;X<Dim&&!bSurfaceCenterFound;++X)
|
||||
{
|
||||
for(const std::array<std::int32_t,3>& Delta:
|
||||
{std::array<std::int32_t,3>{1,0,0},
|
||||
std::array<std::int32_t,3>{0,1,0},
|
||||
std::array<std::int32_t,3>{0,0,1}})
|
||||
{
|
||||
const std::int32_t NX=X+Delta[0],NY=Y+Delta[1],NZ=Z+Delta[2];
|
||||
if(NX>=Dim||NY>=Dim||NZ>=Dim)continue;
|
||||
const float D0=Before->debugDensitySamples[Index(X,Y,Z)];
|
||||
const float D1=Before->debugDensitySamples[Index(NX,NY,NZ)];
|
||||
if((D0<0.0f)==(D1<0.0f))continue;
|
||||
const double T=static_cast<double>(D0)
|
||||
/(static_cast<double>(D0)-D1);
|
||||
Brush.a={OriginX+(X-1+T*Delta[0])*Step,
|
||||
OriginY+(Y-1+T*Delta[1])*Step,
|
||||
OriginZ+(Z-1+T*Delta[2])*Step};
|
||||
bSurfaceCenterFound=true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!bSurfaceCenterFound)
|
||||
Brush.a={static_cast<double>(Key.coordinate.x*Extent)+Extent*0.5,
|
||||
static_cast<double>(Key.coordinate.y*Extent)+Extent*0.5,
|
||||
static_cast<double>(Key.coordinate.z*Extent)+Extent*0.5};
|
||||
Brush.radius=10.0;
|
||||
Brush.strength=-48.0f;
|
||||
Transaction.brushes.push_back(Brush);
|
||||
const auto Applied=Before?EditWorld.applyEditTransaction(Transaction,
|
||||
EditKeys,ApplyError):std::optional<VoxelForgeCore::EditResult>{};
|
||||
const auto EditedBase=Applied
|
||||
?EditWorld.generateTile(Key,EditOptions,BaseError)
|
||||
:std::optional<VoxelForgeCore::TileMesh>{};
|
||||
VoxelForgeCore::TileRegionCompileStats EditPageStats;
|
||||
std::size_t EditPageKeys=0;
|
||||
const bool bEditPageBuilt=EditedBase&&EditWorld.compileTileRegion(EditKeys,
|
||||
static_cast<std::uint32_t>(std::clamp(WorkerCount,1,16)),Scheduler,
|
||||
PageCompileError,&EditPageKeys,&EditPageStats,true,true);
|
||||
const auto EditedPage=bEditPageBuilt
|
||||
?EditWorld.generateTile(Key,EditOptions,PageError)
|
||||
:std::optional<VoxelForgeCore::TileMesh>{};
|
||||
const FRouteDensityComparison EditComparison=EditedBase&&EditedPage
|
||||
?CompareRouteDensities(*EditedPage,*EditedBase,EditWorld)
|
||||
:FRouteDensityComparison{};
|
||||
const bool bEditChanged=Before&&EditedBase
|
||||
&&Before->densityHash!=EditedBase->densityHash;
|
||||
const bool bEditPageVerified=EditedPage
|
||||
&&EditedPage->diagnostics.analyticRegionPageRoute;
|
||||
const bool bEditSame=EditedBase&&EditedPage&&bEditPageVerified
|
||||
&&SameTileOutput(*EditedBase,*EditedPage);
|
||||
const bool bEditPass=Before&&Applied&&EditedBase&&bEditPageBuilt
|
||||
&&bEditPageVerified&&bEditChanged&&bEditSame&&EditComparison.valid
|
||||
&&EditComparison.exactQueryMismatches==0;
|
||||
++EditPairs;
|
||||
if(!bEditPass)++EditMismatches;
|
||||
if(EditedPage)
|
||||
{
|
||||
PageProofBlocks+=EditedPage->diagnostics.uniformBlockProofsChecked;
|
||||
PageProofSamples+=EditedPage->diagnostics.uniformBlockProofSamplesChecked;
|
||||
PageProofFallbacks+=EditedPage->diagnostics.uniformBlockProofFallbackBlocks;
|
||||
PageProofUnknown+=EditedPage->diagnostics.uniformBlockProofUnknownBlocks;
|
||||
PageProofViolations+=EditedPage->diagnostics.uniformBlockProofViolations;
|
||||
}
|
||||
std::printf("[RouteParityEdit] key=(%d,%d,%d,lod%d) sequence=%llu before_hash=0x%016llx edited_base_hash=0x%016llx edited_page_hash=0x%016llx edit_changed=%d page_verified=%d same_mesh=%d same_payload=%d same_topology=%d samples=%llu exact_query_mismatches=%llu status=%s error=%s\n",
|
||||
Key.coordinate.x,Key.coordinate.y,Key.coordinate.z,Key.lod,
|
||||
static_cast<unsigned long long>(Transaction.sequence),
|
||||
static_cast<unsigned long long>(Before?Before->densityHash:0),
|
||||
static_cast<unsigned long long>(EditedBase?EditedBase->densityHash:0),
|
||||
static_cast<unsigned long long>(EditedPage?EditedPage->densityHash:0),
|
||||
bEditChanged?1:0,bEditPageVerified?1:0,
|
||||
EditedBase&&EditedPage&&EditedBase->meshHash==EditedPage->meshHash?1:0,
|
||||
EditedBase&&EditedPage&&EditedBase->payloadHash==EditedPage->payloadHash?1:0,
|
||||
EditedBase&&EditedPage&&EditedBase->topologyHash==EditedPage->topologyHash?1:0,
|
||||
static_cast<unsigned long long>(EditComparison.samples),
|
||||
static_cast<unsigned long long>(EditComparison.exactQueryMismatches),
|
||||
bEditPass?"PASS":"FAIL",
|
||||
!Before?BeforeError.c_str():!Applied?ApplyError.c_str():
|
||||
!EditedBase?BaseError.c_str():!bEditPageBuilt?PageCompileError.c_str():
|
||||
PageError.c_str());
|
||||
}
|
||||
}
|
||||
int32 HistoryPairs=0,HistoryMismatches=0;
|
||||
if(bHasCameraStep&&bRegionCrossing)
|
||||
{
|
||||
const auto MovedAnchor=std::find_if(MovedDesired.Tiles.begin(),MovedDesired.Tiles.end(),
|
||||
[](const FVoxelTileKey& Tile){return Tile.Level==0;});
|
||||
const auto HistoryRow=std::find_if(Rows.begin(),Rows.end(),
|
||||
[&](const FRouteAuditRow& Row)
|
||||
{
|
||||
if(!Row.Base||Row.Candidate.bWouldRequestPage)return false;
|
||||
return std::none_of(MovedDesired.Tiles.begin(),MovedDesired.Tiles.end(),
|
||||
[&](const FVoxelTileKey& Tile)
|
||||
{
|
||||
return Tile.Level==Row.Candidate.Key.lod
|
||||
&&Tile.Coord.X==Row.Candidate.Key.coordinate.x
|
||||
&&Tile.Coord.Y==Row.Candidate.Key.coordinate.y
|
||||
&&Tile.Coord.Z==Row.Candidate.Key.coordinate.z;
|
||||
});
|
||||
});
|
||||
if(HistoryRow!=Rows.end()&&MovedAnchor!=MovedDesired.Tiles.end())
|
||||
{
|
||||
VoxelForgeCore::ShapeBuildRequest MovedShapeRequest=ShapeRequest;
|
||||
const FIntVector MovedOrigin=MovedAnchor->OriginVoxels();
|
||||
const std::int32_t MovedExtent=MovedAnchor->ExtentVoxels();
|
||||
MovedShapeRequest.minVoxel={MovedOrigin.X,MovedOrigin.Y,MovedOrigin.Z};
|
||||
MovedShapeRequest.maxVoxel={MovedOrigin.X+MovedExtent,
|
||||
MovedOrigin.Y+MovedExtent,MovedOrigin.Z+MovedExtent};
|
||||
MovedShapeRequest.clipMinVoxel=MovedShapeRequest.minVoxel;
|
||||
MovedShapeRequest.clipMaxVoxel=MovedShapeRequest.maxVoxel;
|
||||
VoxelForgeCore::World MovedWorld(CoreSnapshot);
|
||||
std::string MovedShapeError,MovedTileError;
|
||||
const bool bMovedCompiled=MovedWorld.compile(MovedShapeRequest,Scheduler,
|
||||
MovedShapeError);
|
||||
const auto MovedTile=bMovedCompiled
|
||||
?MovedWorld.generateTile(HistoryRow->Candidate.Key,CaptureOptions,MovedTileError)
|
||||
:std::optional<VoxelForgeCore::TileMesh>{};
|
||||
const FRouteDensityComparison HistoryComparison=MovedTile
|
||||
?CompareRouteDensities(*MovedTile,*HistoryRow->Base,MovedWorld)
|
||||
:FRouteDensityComparison{};
|
||||
const bool bHistorySame=MovedTile
|
||||
&&SameTileOutput(*MovedTile,*HistoryRow->Base)
|
||||
&&HistoryComparison.valid&&HistoryComparison.exactQueryMismatches==0;
|
||||
++HistoryPairs;
|
||||
if(!bHistorySame)++HistoryMismatches;
|
||||
std::printf("[RouteParityHistory] from=initial_base to=moved_world key=(%d,%d,%d,lod%d) same_density=%d same_mesh=%d same_payload=%d same_topology=%d samples=%llu max_abs_density_delta=%.9g exact_query_mismatches=%llu status=%s error=%s\n",
|
||||
HistoryRow->Candidate.Key.coordinate.x,HistoryRow->Candidate.Key.coordinate.y,
|
||||
HistoryRow->Candidate.Key.coordinate.z,HistoryRow->Candidate.Key.lod,
|
||||
MovedTile&&MovedTile->densityHash==HistoryRow->Base->densityHash?1:0,
|
||||
MovedTile&&MovedTile->meshHash==HistoryRow->Base->meshHash?1:0,
|
||||
MovedTile&&MovedTile->payloadHash==HistoryRow->Base->payloadHash?1:0,
|
||||
MovedTile&&MovedTile->topologyHash==HistoryRow->Base->topologyHash?1:0,
|
||||
static_cast<unsigned long long>(HistoryComparison.samples),
|
||||
HistoryComparison.maxAbsoluteDelta,
|
||||
static_cast<unsigned long long>(HistoryComparison.exactQueryMismatches),
|
||||
bHistorySame?"PASS":"FAIL",
|
||||
!bMovedCompiled?MovedShapeError.c_str():MovedTileError.c_str());
|
||||
}
|
||||
}
|
||||
const double MeanDensityDelta=ComparedDensitySamples
|
||||
?SumDensityDelta/static_cast<double>(ComparedDensitySamples):0.0;
|
||||
const double MeanSurfaceDisplacement=SharedCrossingEdges
|
||||
?SumSurfaceDisplacement/static_cast<double>(SharedCrossingEdges):0.0;
|
||||
const bool bRouteParityPassing=BasePageMismatches==0&&NativeMismatches==0
|
||||
&&ExactQueryMismatches==0&&EditPairs==1&&EditMismatches==0
|
||||
&&HistoryPairs==(bHasCameraStep&&bRegionCrossing?1:0)&&HistoryMismatches==0
|
||||
&&PageRoutesVerified==static_cast<int32>(Rows.size())
|
||||
&&NativeRoutesVerified==NativePairs&&PageProofViolations==0
|
||||
&&PageExactMismatches==0&&NativeExactMismatches==0;
|
||||
std::printf("[RouteParityAudit] seed=%d page_pairs=%zu page_verified=%d page_mismatches=%d native_only_pairs=%d native_verified=%d native_mismatches=%d higher_lod_exact_pairs=%d higher_lod_exact_mismatches=%d edit_pairs=%d edit_mismatches=%d history_pairs=%d history_mismatches=%d exact_query_samples=%llu exact_query_mismatches=%llu page_density_samples=%llu max_abs_density_delta=%.9g mean_abs_density_delta=%.9g sign_mismatches=%llu edge_crossing_mismatches=%llu shared_surface_edges=%llu max_surface_delta_vox=%.9g mean_surface_delta_vox=%.9g page_proof_blocks=%llu page_proof_samples=%llu page_proof_fallbacks=%llu page_proof_unknown=%llu page_proof_violations=%llu status=%s\n",
|
||||
World.Seed,Rows.size(),PageRoutesVerified,BasePageMismatches,
|
||||
NativePairs,NativeRoutesVerified,NativeMismatches,ExactQueryPairs,
|
||||
ExactQueryMismatches,EditPairs,EditMismatches,HistoryPairs,HistoryMismatches,
|
||||
static_cast<unsigned long long>(PageExactSamples
|
||||
+NativeExactSamples+ExactQuerySamples),
|
||||
static_cast<unsigned long long>(PageExactMismatches
|
||||
+NativeExactMismatches+ExactQueryMismatches),
|
||||
static_cast<unsigned long long>(ComparedDensitySamples),MaxDensityDelta,
|
||||
MeanDensityDelta,static_cast<unsigned long long>(SignMismatches),
|
||||
static_cast<unsigned long long>(CrossingEdgeMismatches),
|
||||
static_cast<unsigned long long>(SharedCrossingEdges),MaxSurfaceDisplacement,
|
||||
MeanSurfaceDisplacement,static_cast<unsigned long long>(PageProofBlocks),
|
||||
static_cast<unsigned long long>(PageProofSamples),
|
||||
static_cast<unsigned long long>(PageProofFallbacks),
|
||||
static_cast<unsigned long long>(PageProofUnknown),
|
||||
static_cast<unsigned long long>(PageProofViolations),
|
||||
bRouteParityPassing?"PASS":"FAIL");
|
||||
if(!bRouteParityPassing)
|
||||
{
|
||||
Streamer.Stop();
|
||||
return 9;
|
||||
}
|
||||
}
|
||||
const FStandaloneStreamingUpdate Update=Streamer.UpdateDesiredWithSet(Desired,{},0);
|
||||
if(Update.DesiredTiles!=static_cast<int32>(Desired.Tiles.size()))
|
||||
{
|
||||
|
||||
@@ -122,7 +122,8 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
const FVoxelSharedDensityGrid* InSharedDensityGrid,
|
||||
std::vector<float>* OutDebugDensitySamples,
|
||||
FVoxelMarchingCubesStageTimings* OutStageTimings,
|
||||
FVoxelUniformBlockProofAuditStats* OutBlockProofAudit)
|
||||
FVoxelUniformBlockProofAuditStats* OutBlockProofAudit,
|
||||
bool bForceFullDensityCapture)
|
||||
{
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE(VoxelForge_MesherGenerateMesh);
|
||||
const uint64 StageMesherStartCycles=OutStageTimings?FPlatformTime::Cycles64():0;
|
||||
@@ -865,7 +866,8 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
// allocating the density grid; no samples are skipped for a tile that can contain
|
||||
// geometry, and the final geometry remains byte-identical because this path emits none.
|
||||
if (bHaveUniformBlock && bAllRelevantBlocksSame && bAllRelevantBlocksUniform
|
||||
&& !(Step == 2 && OutDebugDensitySamples != nullptr))
|
||||
&& !(Step == 2 && OutDebugDensitySamples != nullptr)
|
||||
&& !(bForceFullDensityCapture && OutDebugDensitySamples != nullptr))
|
||||
{
|
||||
return MeshData;
|
||||
}
|
||||
|
||||
@@ -120,7 +120,8 @@ public:
|
||||
const FVoxelSharedDensityGrid* InSharedDensityGrid = nullptr,
|
||||
std::vector<float>* OutDebugDensitySamples = nullptr,
|
||||
FVoxelMarchingCubesStageTimings* OutStageTimings = nullptr,
|
||||
FVoxelUniformBlockProofAuditStats* OutBlockProofAudit = nullptr);
|
||||
FVoxelUniformBlockProofAuditStats* OutBlockProofAudit = nullptr,
|
||||
bool bForceFullDensityCapture = false);
|
||||
|
||||
//=========================================================================
|
||||
// SERVICES (injectés par AVoxelWorld)
|
||||
|
||||
Reference in new issue
Block a user