wip(assembler): round L3 -- Core forces FP contraction off and refuses to generate if it can't
CoreNamespacePreamble.h applies per-compiler contraction-off pragmas (Clang, MSVC, GCC; unknown compiler = #error) and a fused-multiply-add self-test that World::compile runs before building. Clang -ffp-contract=on and GCC's default (fast) now reproduce the L1/Windows hashes (they diverged in L2); Clang -ffp-contract=fast ignores the pragma and is refused at startup (negative control). Seed sweep with every cube on real terrain: 48 seeds x 2 recipes x 3 builds x W1/W12, 576 runs, 0 mismatches, 0 empty meshes. Cost +0.5% (2.326 -> 2.337 s, interleaved). World unchanged. MSVC/UBT not compiled here: Windows to-do list in Linux3/REPORT.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
303b7b9f24
commit
046f2af495
10 files changed
+1051
-2
No files matched your search
@@ -7,12 +7,15 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
@@ -33,6 +36,7 @@ namespace
|
||||
bool StreamHash=false;
|
||||
bool ProfileColumnOverlap=false;
|
||||
bool ForceGenerate=false;
|
||||
bool MeshCoverage=false;
|
||||
bool HasSeedOverride=false;
|
||||
int SeedOverride=0;
|
||||
bool HasRenderDistanceOverride=false;
|
||||
@@ -41,6 +45,7 @@ namespace
|
||||
bool TlsProbeRequested=false;
|
||||
int TlsProbeWorkers=0;
|
||||
bool HasCamera=false;
|
||||
bool AutoSurfaceCamera=false;
|
||||
FVector CameraMeters=FVector::ZeroVector;
|
||||
};
|
||||
|
||||
@@ -81,6 +86,8 @@ namespace
|
||||
" --camera=X,Y,Z,YAW,PITCH (position in metres) --stream-hash\n"
|
||||
" --profile-column-overlap --far-horizon-lod8-step=N --force-generate\n"
|
||||
" --seed=N --recipe=surface-world|floating-islands\n"
|
||||
" --auto-surface-camera (place the 27-tile cube on this seed's surface)\n"
|
||||
" --mesh-coverage (report non-empty tiles and indexed triangles)\n"
|
||||
" --render-distance-chunks=N (0 disables the extra far ring)\n"
|
||||
" --tls-probe=N (Linux worker TLS/stack measurement; N=0..12)\n"
|
||||
" --process-affinity=MASK --process-priority=Normal --frame-limit=FPS (viewer compatibility only)\n",
|
||||
@@ -93,6 +100,8 @@ namespace
|
||||
else if(arg=="--stream-hash")options.StreamHash=true;
|
||||
else if(arg=="--profile-column-overlap")options.ProfileColumnOverlap=true;
|
||||
else if(arg=="--force-generate")options.ForceGenerate=true;
|
||||
else if(arg=="--mesh-coverage")options.MeshCoverage=true;
|
||||
else if(arg=="--auto-surface-camera")options.AutoSurfaceCamera=true;
|
||||
else if(arg.starts_with("--recipe="))
|
||||
{
|
||||
const std::string_view recipe=arg.substr(std::string_view("--recipe=").size());
|
||||
@@ -221,6 +230,48 @@ int main(int argc,char** argv)
|
||||
&&!StandaloneWorldAssemblyPrototype::AttachFloatingIslandWorldAssembly(
|
||||
*ownerWorld,ownerJsonRoot,worldAssembly,error))
|
||||
{std::fprintf(stderr,"Floating-island assembly setup failed: %s\n",error.c_str());return 2;}
|
||||
if(options.AutoSurfaceCamera)
|
||||
{
|
||||
constexpr std::int64_t UnitsPerVoxel=VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel;
|
||||
if(options.FloatingIslandsRecipe)
|
||||
{
|
||||
const VoxelForgeCore::FloatingIslandAnchorV1* chosen=nullptr;
|
||||
for(const auto& islandSet:worldAssembly.floatingIslands)
|
||||
for(const auto& island:islandSet.manifest.plan.islands)
|
||||
if(!chosen||island.radiusSubvoxels>chosen->radiusSubvoxels)chosen=&island;
|
||||
if(!chosen)
|
||||
{std::fprintf(stderr,"[SeedSurfacePlacement] FAIL: island plan has no anchors\n");return 2;}
|
||||
cameraVoxels={
|
||||
static_cast<float>(chosen->centerSubvoxels[0])/UnitsPerVoxel,
|
||||
static_cast<float>(chosen->centerSubvoxels[1])/UnitsPerVoxel,
|
||||
static_cast<float>(chosen->centerSubvoxels[2])/UnitsPerVoxel};
|
||||
std::printf("[SeedSurfacePlacement] recipe=floating-islands seed=%d island_radius_vox=%.3f camera_voxel=(%.6f,%.6f,%.6f) status=PASS\n",
|
||||
seed,static_cast<double>(chosen->radiusSubvoxels)/UnitsPerVoxel,
|
||||
cameraVoxels.X,cameraVoxels.Y,cameraVoxels.Z);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto xSubvoxels=static_cast<std::int64_t>(std::llround(cameraVoxels.X*UnitsPerVoxel));
|
||||
const auto ySubvoxels=static_cast<std::int64_t>(std::llround(cameraVoxels.Y*UnitsPerVoxel));
|
||||
const auto currentZSubvoxels=static_cast<std::int64_t>(std::llround(cameraVoxels.Z*UnitsPerVoxel));
|
||||
std::int64_t selectedGround=0,bestDistance=std::numeric_limits<std::int64_t>::max();
|
||||
for(const auto& stratum:worldAssembly.plan.strates)
|
||||
{
|
||||
VoxelForgeCore::Heightfield2D field(worldAssembly.recipe,stratum);
|
||||
if(!field.isValid())continue;
|
||||
const auto column=field.point(xSubvoxels,ySubvoxels,0);
|
||||
const std::int64_t distance=std::abs(column.groundHeightSubvoxels-currentZSubvoxels);
|
||||
if(distance<bestDistance)
|
||||
{bestDistance=distance;selectedGround=column.groundHeightSubvoxels;}
|
||||
}
|
||||
if(bestDistance==std::numeric_limits<std::int64_t>::max())
|
||||
{std::fprintf(stderr,"[SeedSurfacePlacement] FAIL: no valid SurfaceWorld heightfield\n");return 2;}
|
||||
cameraVoxels.Z=static_cast<float>(static_cast<double>(selectedGround)/UnitsPerVoxel);
|
||||
std::printf("[SeedSurfacePlacement] recipe=surface-world seed=%d ground_voxel=%.6f camera_voxel=(%.6f,%.6f,%.6f) status=PASS\n",
|
||||
seed,static_cast<double>(selectedGround)/UnitsPerVoxel,
|
||||
cameraVoxels.X,cameraVoxels.Y,cameraVoxels.Z);
|
||||
}
|
||||
}
|
||||
std::printf("[WorldAssemblerPlanTiming] total_ms=%.3f status=PASS\n",
|
||||
std::chrono::duration<double,std::milli>(Clock::now()-planStart).count());
|
||||
|
||||
@@ -233,5 +284,6 @@ int main(int argc,char** argv)
|
||||
return StandaloneStreamBenchmark::RunPipelinedStreamingBenchmark(ownerWorld,snapshot,
|
||||
cameraVoxels,options.WorkerCount,options.MaxLevel,VoxelForgeCore::DetailVariant::coherent,
|
||||
scheduler,options.StreamHash,false,false,false,false,coldStart,
|
||||
options.ProfileColumnOverlap,options.FarHorizonLOD8InteriorStepVoxels);
|
||||
options.ProfileColumnOverlap,options.FarHorizonLOD8InteriorStepVoxels,
|
||||
options.MeshCoverage);
|
||||
}
|
||||
@@ -62,7 +62,7 @@ namespace StandaloneStreamBenchmark
|
||||
VoxelForgeCore::DetailVariant DetailVariant,const VoxelForgeCore::JobScheduler& Scheduler,
|
||||
bool bHashDensity,bool bSkipUvAndColors,bool bSkipMaterialGroups,bool bSkipUV,bool bSkipColors,
|
||||
std::chrono::steady_clock::time_point ColdStart,bool bProfileColumnOverlap,
|
||||
int32 FarHorizonLOD8InteriorStepVoxels)
|
||||
int32 FarHorizonLOD8InteriorStepVoxels,bool bReportMeshCoverage=false)
|
||||
{
|
||||
using Clock=std::chrono::steady_clock;
|
||||
FStandaloneOwnerWorld& World=*OwnerWorld;
|
||||
@@ -166,6 +166,7 @@ namespace StandaloneStreamBenchmark
|
||||
std::vector<FHashedTile> DensityRows,MeshRows,PayloadRows,TopologyRows;
|
||||
int32 Finished=0,NeighborhoodFinished=0,FullDetailFinished=0;
|
||||
int32 FullDetailExpected=0;
|
||||
uint64 WholeNonEmptyMeshTiles=0,WholeTriangleCount=0;
|
||||
struct FColumnXY
|
||||
{
|
||||
std::int64_t X=0,Y=0;
|
||||
@@ -222,6 +223,12 @@ namespace StandaloneStreamBenchmark
|
||||
for(const FStandaloneCpuTile& Tile:Ready)
|
||||
{
|
||||
++Finished;
|
||||
if(bReportMeshCoverage)
|
||||
{
|
||||
const uint64 TileTriangleCount=static_cast<uint64>(Tile.Indices.size()/3);
|
||||
WholeTriangleCount+=TileTriangleCount;
|
||||
if(TileTriangleCount>0)++WholeNonEmptyMeshTiles;
|
||||
}
|
||||
if(Tile.Key.Level>=0&&Tile.Key.Level<static_cast<int32>(TileCountByLOD.size()))
|
||||
{
|
||||
const std::size_t LOD=static_cast<std::size_t>(Tile.Key.Level);
|
||||
@@ -348,6 +355,10 @@ namespace StandaloneStreamBenchmark
|
||||
WorkerCount,ShapeSeconds,NeighborhoodMeshSeconds,NeighborhoodSeconds,
|
||||
FullDetailSeconds,WholeViewSeconds,NeighborhoodFinished,
|
||||
FullDetailFinished,FullDetailExpected,Finished,Desired.Tiles.size(),StreamSeconds);
|
||||
if(bReportMeshCoverage)
|
||||
std::printf("[PipelineMeshCoverage] tiles=%d nonempty_mesh_tiles=%llu indexed_triangles=%llu\n",
|
||||
Finished,static_cast<unsigned long long>(WholeNonEmptyMeshTiles),
|
||||
static_cast<unsigned long long>(WholeTriangleCount));
|
||||
std::printf("[WorldAssemblerTilePipeline] full_detail_tiles=%d solid_boxes=%llu air_boxes=%llu mixed_boxes=%llu unknown_boxes=%llu whole_tile_skips=%llu field_calls=%llu column_cache_hits=%llu column_cache_misses=%llu column_compute_worker_ms=%.3f classification_worker_ms=%.3f meshing_worker_ms=%.3f\n",
|
||||
FullDetailExpected,static_cast<unsigned long long>(NativeSolidBoxes),
|
||||
static_cast<unsigned long long>(NativeAirBoxes),
|
||||
|
||||
Reference in new issue
Block a user