standalone: 64 m full-detail override, detailed round tunnels, optional mesh cache -- generation itself NOT faster

ClipRadius override 3 (64 m at 25 cm). Uncached full detail 20.3 s on 8 workers (512 tiles at
~51 ms serial each should be ~3-4 s: the in-stream inefficiency is unsolved); full clipmap 170.6 s.
The 1 s / 5 s targets were met only from an on-disk mesh cache (.vfmesh, not committed) of
already-generated tiles: a revisit speed-up, not generation speed. Round tunnels gained
deterministic multi-scale wall relief and lost the larger mouth wedges (1.54% of lattice signs
changed, mean 0.20 m). Hashes stable across runs and 1 vs 6 workers.

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-03 08:59:46 +02:00
1 parent 81b7d6d0ff
commit 5441c7da5c
181 files changed
+1674 -51

No files matched your search

+205 -4
View File
@@ -3,7 +3,12 @@
#include "WorldMesher.h"
#include <condition_variable>
#include <algorithm>
#include <array>
#include <bit>
#include <cstdint>
#include <cstring>
#include <fstream>
#include <unordered_set>
struct FStandaloneTileKeyHash
@@ -31,9 +36,172 @@ struct FStandaloneCpuTile
std::vector<uint32> Indices;
uint64 DensitySamples=0;
double WorkerSeconds=0.0;
uint64 DensityHash=14695981039346656037ull;
bool bHasDensityHash=false;
std::array<uint64,VoxelDensityProfile::BucketCount> ProfileCycles{};
};
struct FStandaloneMeshCacheMetadata
{
uint64 SourceDataHash=0;
int32 Seed=0;
int32 ClipRadius=3;
int32 FullResClipLevels=2;
int32 CoarseTileCells=16;
int32 StrateContentCutMinLevel=9;
int32 SkirtCells=0;
int32 LODOctaveDrop=0;
int32 bGenerateSkirts=0;
uint32 AlgorithmVersion=1;
uint32 TunnelMode=0;
uint32 VoxelSizeCm=25;
};
class FStandaloneMeshCache
{
public:
static constexpr uint32 CurrentVersion=1;
void Clear(){Tiles.clear();bLoaded=false;BytesLoaded=0;}
bool IsLoaded()const{return bLoaded;}
size_t Num()const{return Tiles.size();}
uint64 GetBytesLoaded()const{return BytesLoaded;}
const FStandaloneCpuTile* Find(const FVoxelTileKey& Key)const
{
const auto It=Tiles.find(Key);
return It==Tiles.end()?nullptr:&It->second;
}
static bool Save(const std::string& Path,const FStandaloneMeshCacheMetadata& Metadata,
std::vector<FStandaloneCpuTile>& InTiles,std::string& Error)
{
std::vector<FStandaloneCpuTile*> Ordered;
Ordered.reserve(InTiles.size());
for(FStandaloneCpuTile& Tile:InTiles)Ordered.push_back(&Tile);
std::sort(Ordered.begin(),Ordered.end(),[](const FStandaloneCpuTile* A,const FStandaloneCpuTile* B)
{
if(A->Key.Level!=B->Key.Level)return A->Key.Level<B->Key.Level;
if(A->Key.Coord.Z!=B->Key.Coord.Z)return A->Key.Coord.Z<B->Key.Coord.Z;
if(A->Key.Coord.Y!=B->Key.Coord.Y)return A->Key.Coord.Y<B->Key.Coord.Y;
return A->Key.Coord.X<B->Key.Coord.X;
});
std::ofstream File(Path,std::ios::binary|std::ios::trunc);
if(!File){Error="could not open mesh cache for writing: "+Path;return false;}
struct FHeader
{
char Magic[8];uint32 Version;uint32 Endian;uint64 SourceDataHash;
int32 Seed,ClipRadius,FullResClipLevels,CoarseTileCells,StrateContentCutMinLevel;
int32 SkirtCells,LODOctaveDrop,bGenerateSkirts;
uint32 AlgorithmVersion,TunnelMode,VoxelSizeCm;
uint64 TileCount;
};
static_assert(sizeof(FHeader)==80);
const FHeader Header{{'V','F','M','E','S','H','1','\0'},1,0x01020304u,
Metadata.SourceDataHash,Metadata.Seed,Metadata.ClipRadius,Metadata.FullResClipLevels,
Metadata.CoarseTileCells,Metadata.StrateContentCutMinLevel,Metadata.SkirtCells,
Metadata.LODOctaveDrop,Metadata.bGenerateSkirts,Metadata.AlgorithmVersion,
Metadata.TunnelMode,Metadata.VoxelSizeCm,static_cast<uint64>(Ordered.size())};
File.write(reinterpret_cast<const char*>(&Header),sizeof(Header));
struct FRecord
{
int32 Level,X,Y,Z;uint64 Samples,DensityHash;uint32 VertexCount,IndexCount;
};
static_assert(sizeof(FRecord)==40);
for(const FStandaloneCpuTile* Tile:Ordered)
{
if(Tile->Vertices.size()>UINT32_MAX||Tile->Indices.size()>UINT32_MAX
||Tile->Indices.size()%3!=0)
{Error="mesh cache tile exceeds the 32-bit record limits";return false;}
const FRecord Record{Tile->Key.Level,Tile->Key.Coord.X,Tile->Key.Coord.Y,
Tile->Key.Coord.Z,Tile->DensitySamples,Tile->DensityHash,
static_cast<uint32>(Tile->Vertices.size()),static_cast<uint32>(Tile->Indices.size())};
File.write(reinterpret_cast<const char*>(&Record),sizeof(Record));
if(!Tile->Vertices.empty())File.write(reinterpret_cast<const char*>(Tile->Vertices.data()),
static_cast<std::streamsize>(Tile->Vertices.size()*sizeof(FStandaloneCpuVertex)));
if(!Tile->Indices.empty())File.write(reinterpret_cast<const char*>(Tile->Indices.data()),
static_cast<std::streamsize>(Tile->Indices.size()*sizeof(uint32)));
if(!File){Error="mesh cache write failed: "+Path;return false;}
}
File.flush();
if(!File){Error="mesh cache flush failed: "+Path;return false;}
return true;
}
bool Load(const std::string& Path,const FStandaloneMeshCacheMetadata& Expected,std::string& Error)
{
Clear();
std::ifstream File(Path,std::ios::binary);
if(!File){Error="cache file is missing or unreadable";return false;}
struct FHeader
{
char Magic[8];uint32 Version;uint32 Endian;uint64 SourceDataHash;
int32 Seed,ClipRadius,FullResClipLevels,CoarseTileCells,StrateContentCutMinLevel;
int32 SkirtCells,LODOctaveDrop,bGenerateSkirts;
uint32 AlgorithmVersion,TunnelMode,VoxelSizeCm;
uint64 TileCount;
};
static_assert(sizeof(FHeader)==80);
FHeader H{};File.read(reinterpret_cast<char*>(&H),sizeof(H));
const char ExpectedMagic[8]={'V','F','M','E','S','H','1','\0'};
if(!File||std::memcmp(H.Magic,ExpectedMagic,sizeof(ExpectedMagic))!=0
||H.Version!=1||H.Endian!=0x01020304u||H.TileCount>1000000)
{Error="mesh cache header is invalid";return false;}
if(H.SourceDataHash!=Expected.SourceDataHash||H.Seed!=Expected.Seed
||H.ClipRadius!=Expected.ClipRadius||H.FullResClipLevels!=Expected.FullResClipLevels
||H.CoarseTileCells!=Expected.CoarseTileCells
||H.StrateContentCutMinLevel!=Expected.StrateContentCutMinLevel
||H.SkirtCells!=Expected.SkirtCells||H.LODOctaveDrop!=Expected.LODOctaveDrop
||H.bGenerateSkirts!=Expected.bGenerateSkirts
||H.AlgorithmVersion!=Expected.AlgorithmVersion||H.TunnelMode!=Expected.TunnelMode
||H.VoxelSizeCm!=Expected.VoxelSizeCm)
{Error="mesh cache metadata does not match this world configuration";return false;}
struct FRecord
{
int32 Level,X,Y,Z;uint64 Samples,DensityHash;uint32 VertexCount,IndexCount;
};
static_assert(sizeof(FRecord)==40);
std::unordered_map<FVoxelTileKey,FStandaloneCpuTile,FStandaloneTileKeyHash> Loaded;
Loaded.reserve(static_cast<size_t>(H.TileCount)*2+1);
for(uint64 I=0;I<H.TileCount;++I)
{
FRecord Record{};File.read(reinterpret_cast<char*>(&Record),sizeof(Record));
if(!File||Record.Level<0||Record.Level>8||Record.VertexCount>10000000
||Record.IndexCount>60000000)
{Error="mesh cache contains an invalid tile record";return false;}
FStandaloneCpuTile Tile;Tile.Key=FVoxelTileKey(FIntVector(Record.X,Record.Y,Record.Z),Record.Level);
Tile.DensitySamples=Record.Samples;Tile.DensityHash=Record.DensityHash;Tile.bHasDensityHash=true;
Tile.Vertices.resize(Record.VertexCount);Tile.Indices.resize(Record.IndexCount);
if(!Tile.Vertices.empty())File.read(reinterpret_cast<char*>(Tile.Vertices.data()),
static_cast<std::streamsize>(Tile.Vertices.size()*sizeof(FStandaloneCpuVertex)));
if(!Tile.Indices.empty())File.read(reinterpret_cast<char*>(Tile.Indices.data()),
static_cast<std::streamsize>(Tile.Indices.size()*sizeof(uint32)));
if(!File){Error="mesh cache ended inside tile geometry";return false;}
for(const uint32 Index:Tile.Indices)
if(Index>=Tile.Vertices.size())
{Error="mesh cache contains an out-of-range mesh index";return false;}
for(const FStandaloneCpuVertex& Vertex:Tile.Vertices)
for(const float Value:Vertex.Position)
if(!std::isfinite(Value))
{Error="mesh cache contains a non-finite vertex";return false;}
for(const FStandaloneCpuVertex& Vertex:Tile.Vertices)
for(const float Value:Vertex.Normal)
if(!std::isfinite(Value))
{Error="mesh cache contains a non-finite normal";return false;}
if(!Loaded.emplace(Tile.Key,std::move(Tile)).second)
{Error="mesh cache contains duplicate tile keys";return false;}
}
const std::streamoff End=File.tellg();
if(End<0){Error="could not determine mesh cache size";return false;}
BytesLoaded=static_cast<uint64>(End);Tiles.swap(Loaded);bLoaded=true;
return true;
}
private:
std::unordered_map<FVoxelTileKey,FStandaloneCpuTile,FStandaloneTileKeyHash> Tiles;
bool bLoaded=false;
uint64 BytesLoaded=0;
};
struct FStandaloneStreamingUpdate
{
FIntVector CenterChunk=FIntVector::ZeroValue;
@@ -113,8 +281,12 @@ namespace StandaloneWorldMesher
class FStandaloneStreamingGenerator
{
public:
explicit FStandaloneStreamingGenerator(FStandaloneOwnerWorld& InWorld,int32 InWorkerCount=6)
:World(&InWorld),WorkerCount(FMath::Clamp(InWorkerCount,1,32))
explicit FStandaloneStreamingGenerator(FStandaloneOwnerWorld& InWorld,int32 InWorkerCount=8,
bool bInHashDensity=false,
const FStandaloneMeshCache* InMeshCache=nullptr,
int32 InMaxLevelFilter=-1)
:World(&InWorld),WorkerCount(FMath::Clamp(InWorkerCount,1,8)),bHashDensity(bInHashDensity),
MeshCache(InMeshCache),MaxLevelFilter(InMaxLevelFilter)
{
VoxelGenLOD::InitializeConsoleSwitches();
UVoxelMarchingCubesMesher::InitializeConsoleSwitches();
@@ -155,6 +327,9 @@ public:
uint64 CrossingIndex=0)
{
FStandaloneDesiredTileSet NewDesired=StandaloneWorldMesher::BuildDesiredSet(*World,CameraPositionVoxels);
if(MaxLevelFilter>=0)
NewDesired.Tiles.erase(std::remove_if(NewDesired.Tiles.begin(),NewDesired.Tiles.end(),
[&](const FVoxelTileKey& Key){return Key.Level>MaxLevelFilter;}),NewDesired.Tiles.end());
FStandaloneStreamingUpdate Stats;
Stats.CenterChunk=NewDesired.CenterChunk;
Stats.DesiredTiles=static_cast<int32>(NewDesired.Tiles.size());
@@ -221,12 +396,23 @@ public:
FIntVector CenterChunk()const
{std::lock_guard<std::mutex> Lock(Mutex);return CurrentDesired.CenterChunk;}
int32 GetWorkerCount()const{return WorkerCount;}
uint64 GetCacheHits()const{return CacheHitCount.load(std::memory_order_relaxed);}
private:
FStandaloneCpuTile Generate(const FStandaloneTileJob& Job)
{
const auto StartTime=FClock::now();
FStandaloneCpuTile Out;Out.Key=Job.Key;Out.RequestedCrossing=Job.RequestedCrossing;
if(MeshCache)
{
if(const FStandaloneCpuTile* Cached=MeshCache->Find(Job.Key))
{
Out=*Cached;Out.RequestedCrossing=Job.RequestedCrossing;
Out.WorkerSeconds=std::chrono::duration<double>(FClock::now()-StartTime).count();
CacheHitCount.fetch_add(1,std::memory_order_relaxed);
return Out;
}
}
UVoxelMarchingCubesMesher Mesher;
Mesher.SetGenerator(World->Generator.get());
Mesher.bGenerateSkirts=World->bGenerateSkirts;
@@ -240,8 +426,19 @@ private:
const bool bProfile=VoxelDensityProfile::GetMode()!=VoxelDensityProfile::EMode::Disabled;
const VoxelDensityProfile::FSnapshot Before=bProfile
? VoxelDensityProfile::SnapshotCurrentThread():VoxelDensityProfile::FSnapshot{};
std::vector<float> DensityValues;
FVoxelMeshData Mesh=Mesher.GenerateMesh(Job.Key.OriginVoxels(),Step,Cells,nullptr,
Job.BandZMin,Job.BandZMax,&SampleCount);
Job.BandZMin,Job.BandZMax,&SampleCount,nullptr,nullptr,
bHashDensity?&DensityValues:nullptr);
if(bHashDensity)
{
Out.bHasDensityHash=true;
for(const float Density:DensityValues)
{
Out.DensityHash^=std::bit_cast<uint32>(Density);
Out.DensityHash*=1099511628211ull;
}
}
if(bProfile)
{
const VoxelDensityProfile::FSnapshot After=VoxelDensityProfile::SnapshotCurrentThread();
@@ -290,7 +487,11 @@ private:
using FClock=std::chrono::steady_clock;
FStandaloneOwnerWorld* World=nullptr;
int32 WorkerCount=6;
int32 WorkerCount=8;
bool bHashDensity=false;
const FStandaloneMeshCache* MeshCache=nullptr;
int32 MaxLevelFilter=-1;
std::atomic<uint64> CacheHitCount{0};
mutable std::mutex Mutex;
std::condition_variable Wake;
bool bStop=true;