standalone: VoxelForgeCore link-safe next to Unreal -- internals in vfcore::detail, symbol check

- The copied generator, mesher and Compat stand-ins compile inside vfcore::detail.
- CheckCoreSymbols.ps1 (run by Build.ps1) fails on any unqualified Unreal-style symbol in the
  library: 16,255 on 557f6aa, 0 now. A UE 5.7 CoreMinimal.h + VoxelForgeCore.h smoke compile passes.
- Output identical: 11 hash rows at W1/W8, routes 5642/5642, look/passage/far audits pass,
  bad-shell control fails, six captures byte-identical.
Cost, measured by me interleaved (8 workers, cold, 4 runs each, first run excluded): 0.963 s vs
0.937 s for 557f6aa, ~27 ms in shape compilation; same math, so codegen/inlining. To be found.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-10-05 15:20:29 +02:00
1 parent 557f6aac30
commit aeeedaa73d
11 files changed
+461 -87

No files matched your search

+61 -10
View File
@@ -26,6 +26,7 @@
#include <fstream>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <limits>
#include <map>
#include <numeric>
@@ -351,6 +352,7 @@ namespace
std::chrono::steady_clock::time_point FullShapeBuildStart{};
FStandaloneMeshCache MeshCache;
std::string DataPath;
VFJson::Value OwnerJsonRoot;
bool bHasCameraOverride=false;
V3 CameraPositionOverride{};
float CameraYawOverride=0.0f,CameraPitchOverride=0.0f;
@@ -656,7 +658,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
ShapeWorld.reset();
Snapshot={};
World=std::make_shared<FStandaloneOwnerWorld>();
if(!StandaloneOwnerLoader::Load(DataPath,*World,Error))return false;
if(!StandaloneOwnerLoader::Resolve(OwnerJsonRoot,*World,Error))return false;
if(!bParityChecked&&Seed==World->Seed&&!bRoundTunnels)PrintParity();
const int32 ExportClipRadius=World->ClipRadius;
World->ClipRadius=ClipRadius;
@@ -722,8 +724,10 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
Min.X-=NearBuildPad;Min.Y-=NearBuildPad;Min.Z-=NearBuildPad;
Max.X+=NearBuildPad;Max.Y+=NearBuildPad;Max.Z+=NearBuildPad;
}
Snapshot=VoxelForgeCore::MakeStandaloneSnapshot(World);
if(!Snapshot){Error="the standalone adapter could not make a resolved core snapshot";return false;}
Snapshot=VoxelForgeCore::MakeStandaloneSnapshotFromParsedJson(OwnerJsonRoot,Seed,ClipRadius,
MaxClipLevelOverride,bRoundTunnels,bRoundTunnelEnhanced,Error);
if(!Snapshot)
{if(Error.empty())Error="the standalone adapter could not make a resolved core snapshot";return false;}
ShapeWorld=std::make_unique<VoxelForgeCore::World>(Snapshot);
const int32 ShapeWorkers=bNeighborhoodOnly?(WorkerCount>=8?2:1):WorkerCount;
VoxelForgeCore::ShapeBuildRequest Request;
@@ -1941,6 +1945,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
}
int RunPipelinedStreamingBenchmark(std::shared_ptr<FStandaloneOwnerWorld> OwnerWorld,
const VoxelForgeCore::ResolvedWorldSnapshot& CoreSnapshot,
const FVector& CameraPositionVoxels,int32 WorkerCount,int32 MaxLevel,
VoxelForgeCore::DetailVariant DetailVariant,const VoxelForgeCore::JobScheduler& Scheduler,
bool bHashDensity,bool bSkipUvAndColors,bool bSkipMaterialGroups,bool bSkipUV,bool bSkipColors,
@@ -1948,7 +1953,15 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
{
using Clock=std::chrono::steady_clock;
FStandaloneOwnerWorld& World=*OwnerWorld;
const bool bRoundTunnels=World.Generator->bStandaloneResearchRoundTunnels;
const bool bEnhancedTunnelWalls=World.Generator->bStandaloneResearchEnhancedWalls;
VoxelForgeCore::World Shape(CoreSnapshot);
FStandaloneDesiredTileSet Desired=StandaloneWorldMesher::BuildDesiredSet(World,CameraPositionVoxels);
const auto CoreTiles=Shape.desiredTiles(
{CameraPositionVoxels.X,CameraPositionVoxels.Y,CameraPositionVoxels.Z});
Desired.Tiles.clear();Desired.Tiles.reserve(CoreTiles.size());
for(const auto& Key:CoreTiles)
Desired.Tiles.emplace_back(FIntVector(Key.coordinate.x,Key.coordinate.y,Key.coordinate.z),Key.lod);
if(MaxLevel>=0)
Desired.Tiles.erase(std::remove_if(Desired.Tiles.begin(),Desired.Tiles.end(),
[&](const FVoxelTileKey& Tile){return Tile.Level>MaxLevel;}),Desired.Tiles.end());
@@ -1978,7 +1991,15 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
if(Neighborhood.size()!=27)
{std::fprintf(stderr,"[StreamPipeline] expected 27 player-neighborhood tiles, found %zu.\n",Neighborhood.size());return 7;}
VoxelForgeCore::World Shape(VoxelForgeCore::MakeStandaloneSnapshot(OwnerWorld));
// The initial desired set now owns all clipmap metadata needed by this Core-only run.
// Release the separate compatibility-world generation graph before compiling Core.
World.Generator.reset();
World.Manager.reset();
World.DefinitionStorage.clear();
World.BiomeStorage.clear();
World.OperationStorage.clear();
World.DensitySamples.clear();
std::string BuildError;
const bool bIncludeFarWorld=MaxLevel<0||MaxLevel>=World.FullResClipLevels;
const auto ShapeBuildStart=Clock::now();
@@ -1989,8 +2010,8 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
ShapeRequest.clipMaxVoxel={ClipMax.X,ClipMax.Y,ClipMax.Z};
ShapeRequest.workerLimit=static_cast<uint32>(std::clamp(WorkerCount,1,8));
ShapeRequest.detail=DetailVariant;ShapeRequest.includeFarWorld=bIncludeFarWorld;
ShapeRequest.roundTunnels=World.Generator->bStandaloneResearchRoundTunnels;
ShapeRequest.enhancedTunnelWalls=World.Generator->bStandaloneResearchEnhancedWalls;
ShapeRequest.roundTunnels=bRoundTunnels;
ShapeRequest.enhancedTunnelWalls=bEnhancedTunnelWalls;
if(!Shape.compile(ShapeRequest,Scheduler,BuildError))
{std::fprintf(stderr,"[StreamPipeline] full shape build failed: %s\n",BuildError.c_str());return 2;}
const auto ShapeReady=Clock::now();
@@ -1998,7 +2019,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
FStandaloneStreamingGenerator Streamer(World,WorkerCount,bHashDensity,nullptr,MaxLevel,
bSkipUvAndColors,bSkipMaterialGroups,bSkipUV,bSkipColors,&Shape);
const auto StreamStart=Clock::now();
const FStandaloneStreamingUpdate Update=Streamer.Start(CameraPositionVoxels);
const FStandaloneStreamingUpdate Update=Streamer.StartWithDesired(Desired);
if(Update.DesiredTiles!=static_cast<int32>(Desired.Tiles.size()))
{
Streamer.Stop();
@@ -2153,11 +2174,23 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
auto PlayerNeighborhoodCompleted=std::chrono::steady_clock::time_point{};
std::vector<FStandaloneCpuTile> CacheTiles;
if(!BakeCachePath.empty())CacheTiles.reserve(Desired.Tiles.size());
if(CoreWorld&&!FarAudit)
{
// Tile keys and band limits are already captured in Desired. Core owns field and
// mesh generation, so discard the unused compatibility generator before streaming.
World.Generator.reset();
World.Manager.reset();
World.DefinitionStorage.clear();
World.BiomeStorage.clear();
World.OperationStorage.clear();
World.DensitySamples.clear();
}
const auto Start=std::chrono::steady_clock::now();
FStandaloneStreamingGenerator Streamer(World,WorkerCount,
bHashDensity||!BakeCachePath.empty(),Cache,MaxLevel,
bSkipUvAndColors,bSkipMaterialGroups,bSkipUV,bSkipColors,CoreWorld);
const FStandaloneStreamingUpdate Update=Streamer.Start(CameraPositionVoxels);
const FStandaloneStreamingUpdate Update=CoreWorld
?Streamer.StartWithDesired(Desired):Streamer.Start(CameraPositionVoxels);
const double LOD0Width=static_cast<double>(2*World.ClipRadius+2)
*CHUNK_SIZE*VOXEL_SIZE/100.0;
std::printf("[StreamBenchmarkConfig] clip_radius=%d lod0_cube=%.0fm voxel=25cm fullres_levels=%d max_level_filter=%d workers=%d full_detail_tiles=%d desired_tiles=%d variant=%s uv=%s colors=%s material_groups=%s hash=%s\n",
@@ -2564,7 +2597,17 @@ int main(int Argc,char** Argv)
App.bProfileDetailBand=bProfileDetailBand;
std::string Error;
App.World=std::make_shared<FStandaloneOwnerWorld>();
if(!StandaloneOwnerLoader::Load(App.DataPath,*App.World,Error)){std::fprintf(stderr,"Owner data load failed: %s\n",Error.c_str());return 2;}
{
std::ifstream JsonFile(App.DataPath,std::ios::binary);
if(!JsonFile){std::fprintf(stderr,"Owner data load failed: cannot open %s\n",App.DataPath.c_str());return 2;}
std::string OwnerJsonText{std::istreambuf_iterator<char>(JsonFile),std::istreambuf_iterator<char>{}};
if(!JsonFile&& !JsonFile.eof())
{std::fprintf(stderr,"Owner data load failed: read error for %s\n",App.DataPath.c_str());return 2;}
if(!VFJson::Parser(std::move(OwnerJsonText)).Parse(App.OwnerJsonRoot,Error))
{std::fprintf(stderr,"Owner data load failed: %s\n",Error.c_str());return 2;}
}
if(!StandaloneOwnerLoader::Resolve(App.OwnerJsonRoot,*App.World,Error))
{std::fprintf(stderr,"Owner data load failed: %s\n",Error.c_str());return 2;}
App.Seed=App.World->Seed;
if(App.bParityOnly)
{
@@ -2597,6 +2640,14 @@ int main(int Argc,char** Argv)
&&!bStreamPipeline
&&!App.BuildShapeWorldForCamera(Error,bNeedFarShapeWorld))
{std::fprintf(stderr,"Shape interval build failed: %s\n",Error.c_str());return 2;}
if(bStreamPipeline)
{
App.Snapshot=VoxelForgeCore::MakeStandaloneSnapshotFromParsedJson(App.OwnerJsonRoot,
App.Seed,App.ClipRadius,App.MaxClipLevelOverride,App.bRoundTunnels,
App.bRoundTunnelEnhanced,Error);
if(!App.Snapshot)
{std::fprintf(stderr,"Shape interval build failed: %s\n",Error.c_str());return 2;}
}
if(App.bShapeAuditOnly)
{
if(bPassageDetailAuditOnly)
@@ -2614,7 +2665,7 @@ int main(int Argc,char** Argv)
if(bStreamBenchmark)
{
if(bStreamPipeline)
return RunPipelinedStreamingBenchmark(App.World,
return RunPipelinedStreamingBenchmark(App.World,App.Snapshot,
FVector(App.Position.X*4.0,App.Position.Y*4.0,App.Position.Z*4.0),
App.WorkerCount,StreamBenchmarkMaxLevel,App.ShapeDetailVariant,App.CoreScheduler,
bStreamHash||!BakeMeshCachePath.empty(),bSkipUvAndColors,bSkipMaterialGroups,