fix: signed-overflow UB in VoxelHash made cave placement compiler-dependent; all generators LOD0-8; speed matrix (L60)
VoxelHash::Cell/Pair, legacy Cellular3D and the shared Core cellular hash added 0x7FFFFFFF to a signed int32 before casting to uint32; overflow is undefined, so Clang and GCC placed cave passage 1 differently (first differing sample at LOD1 tile (0,64,-512)). The bias is now done in unsigned arithmetic, in both the standalone copy and the Unreal plugin header (Source/VoxelForge/Public/VoxelCaveMorphology.h). Native caves, motifs and lattice now stream LOD0-8 with skirts and cross-level transitions. New verify.py --speed-matrix runs every generator on one asserted-identical view (5 interleaved runs): motifs 1.33, profiles 1.30-1.32, caves 1.41, natural SW 1.67, lattice 1.70, FI 4.93 s whole view. fast/medium/full PASS, Clang/GCC peer hashes equal. Research R29 (open-edge ledge) and R30 (compiler-divergence suspects). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
This commit is contained in:
1 parent
fbae82e024
commit
c0c204ba50
15 files changed
+938
-65
No files matched your search
@@ -1648,13 +1648,17 @@ int main(int argc,char** argv)
|
||||
if(!ParseOptions(argc,argv,options))return 2;
|
||||
if(options.TlsProbeRequested)
|
||||
return StandaloneTlsProbe::Run(options.TlsProbeWorkers,options.TlsProbeTouch);
|
||||
std::printf("[HeadlessConfig] data=%s workers=%d clip_radius=%d max_clip_level=%d max_level=%d hash=%s column_overlap=%s force_generate=%s recipe=%s shared_column_cache=%s camera_step_m=(%.3f,%.3f,%.3f) seed_override=%s render_distance_override=%s\n",
|
||||
std::printf("[HeadlessConfig] data=%s workers=%d clip_radius=%d max_clip_level=%d max_level=%d hash=%s column_overlap=%s force_generate=%s recipe=%s lattice_strate=%d shared_column_cache=%s camera_step_m=(%.3f,%.3f,%.3f) camera_override=%s camera_m=(%.6f,%.6f,%.6f) camera_rotation_deg=(%.3f,%.3f) seed_override=%s render_distance_override=%s\n",
|
||||
options.DataPath.string().c_str(),options.WorkerCount,options.ClipRadius,options.MaxClipLevel,
|
||||
options.MaxLevel,options.StreamHash?"on":"off",options.ProfileColumnOverlap?"on":"off",
|
||||
options.ForceGenerate?"on":"off",options.CavesRecipe?"caves"
|
||||
:options.LatticeRecipe?"lattice"
|
||||
:options.FloatingIslandsRecipe?"floating-islands":"surface-world",
|
||||
options.LatticeStrateIndex,
|
||||
options.UseSharedColumnCache?"on":"off",options.CameraStepMeters.X,options.CameraStepMeters.Y,
|
||||
options.CameraStepMeters.Z,
|
||||
options.CameraStepMeters.Z,options.HasCamera?"yes":"no",options.CameraMeters.X,
|
||||
options.CameraMeters.Y,options.CameraMeters.Z,options.CameraYawDegrees,
|
||||
options.CameraPitchDegrees,
|
||||
options.HasSeedOverride?"yes":"no",options.HasRenderDistanceOverride?"yes":"no");
|
||||
std::printf("[ColdTimeline] stage=owner_data_load begin_ms=%.3f\n",
|
||||
std::chrono::duration<double,std::milli>(Clock::now()-coldStart).count());
|
||||
|
||||
@@ -261,8 +261,12 @@ namespace VoxelHash
|
||||
FORCEINLINE uint32 Cell(int32 CellX, int32 CellY, uint32 Seed)
|
||||
{
|
||||
uint32 H = Seed;
|
||||
H ^= Mix((uint32)(CellX + 0x7FFFFFFF));
|
||||
H ^= Mix((uint32)(CellY + 0x7FFFFFFF) * 2654435761u);
|
||||
// Bias in unsigned arithmetic. Adding the signed cell coordinate first overflows for
|
||||
// positive cells and makes the world hash compiler-dependent.
|
||||
const uint32 BiasedX = static_cast<uint32>(CellX) + 0x7FFFFFFFu;
|
||||
const uint32 BiasedY = static_cast<uint32>(CellY) + 0x7FFFFFFFu;
|
||||
H ^= Mix(BiasedX);
|
||||
H ^= Mix(BiasedY * 2654435761u);
|
||||
return Mix(H);
|
||||
}
|
||||
|
||||
@@ -274,10 +278,14 @@ namespace VoxelHash
|
||||
int32 MinX = FMath::Min(AX, BX), MinY = FMath::Min(AY, BY);
|
||||
int32 MaxX = FMath::Max(AX, BX), MaxY = FMath::Max(AY, BY);
|
||||
uint32 H = Seed ^ 0xDEADBEEF;
|
||||
H ^= Mix((uint32)(MinX + 0x7FFFFFFF));
|
||||
H ^= Mix((uint32)(MinY + 0x7FFFFFFF) * 2654435761u);
|
||||
H ^= Mix((uint32)(MaxX + 0x7FFFFFFF) * 374761393u);
|
||||
H ^= Mix((uint32)(MaxY + 0x7FFFFFFF) * 668265263u);
|
||||
const uint32 BiasedMinX = static_cast<uint32>(MinX) + 0x7FFFFFFFu;
|
||||
const uint32 BiasedMinY = static_cast<uint32>(MinY) + 0x7FFFFFFFu;
|
||||
const uint32 BiasedMaxX = static_cast<uint32>(MaxX) + 0x7FFFFFFFu;
|
||||
const uint32 BiasedMaxY = static_cast<uint32>(MaxY) + 0x7FFFFFFFu;
|
||||
H ^= Mix(BiasedMinX);
|
||||
H ^= Mix(BiasedMinY * 2654435761u);
|
||||
H ^= Mix(BiasedMaxX * 374761393u);
|
||||
H ^= Mix(BiasedMaxY * 668265263u);
|
||||
return Mix(H);
|
||||
}
|
||||
|
||||
@@ -477,10 +485,13 @@ namespace VoxelNoise
|
||||
|
||||
// Hash the neighbor cell to get a feature point position [0,1)
|
||||
// Using three different hash mixes for X, Y, Z offsets
|
||||
const uint32 BiasedX = static_cast<uint32>(NX) + 0x7FFFFFFFu;
|
||||
const uint32 BiasedY = static_cast<uint32>(NY) + 0x7FFFFFFFu;
|
||||
const uint32 BiasedZ = static_cast<uint32>(NZ) + 0x7FFFFFFFu;
|
||||
uint32 H = VoxelHash::Mix(
|
||||
(uint32)(NX + 0x7FFFFFFF)
|
||||
^ VoxelHash::Mix((uint32)(NY + 0x7FFFFFFF) * 2654435761u)
|
||||
^ VoxelHash::Mix((uint32)(NZ + 0x7FFFFFFF) * 374761393u)
|
||||
BiasedX
|
||||
^ VoxelHash::Mix(BiasedY * 2654435761u)
|
||||
^ VoxelHash::Mix(BiasedZ * 374761393u)
|
||||
);
|
||||
|
||||
float FPX = (float)DX + VoxelHash::ToFloat01(H) - FracX;
|
||||
|
||||
Reference in new issue
Block a user