wip(standalone): speed round -- interrupted by a PC hard reset at 10:16 (Kernel-Power 41)
Verified before the reset (density + full mesh hashes equal at 1/8/16 workers, routes 5642/5642, look audits green): - merged field lookups, parallel mask merge, compact inline spatial bins, line-local relief hash cache, lattice-specific mesher entry, fused sign+magnitude evaluation, far connectors as one capsule each (181,836 -> 16,491 far primitives). - crater edit (sphere+box+capsule, one transaction across LOD0/1): 15-27 ms; player 3x3x3 neighbourhood meshes ready ~18 ms after streaming starts. NOT yet verified (in the tree, unhashed): standalone-only skips of UV/colour arrays and of the ceiling/ground material grouping (best cold whole view 0.958 s @16 workers) and a far-bin reserve tweak. Those skips drop data the plugin's materials may need: not a game-path win as-is. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
1 parent
97c0455939
commit
ee5b05b357
16 files changed
+1011
-156
No files matched your search
@@ -670,7 +670,8 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
}
|
||||
if(!FineTiles){Error="no full-detail tiles are available for shape compilation";return false;}
|
||||
ShapeWorld=std::make_unique<FStandaloneShapeWorld>();
|
||||
if(!ShapeWorld->Build(*World,Min,Max,ClipMin,ClipMax,WorkerCount,ShapeDetailVariant,bIncludeFarWorld,Error))
|
||||
if(!ShapeWorld->Build(*World,Min,Max,ClipMin,ClipMax,WorkerCount,
|
||||
ShapeDetailVariant,bIncludeFarWorld,Error))
|
||||
{ShapeWorld.reset();return false;}
|
||||
FStandaloneShapeWorldAccess::SetActive(ShapeWorld.get());
|
||||
bFarShapeStagePending=bShapeWorld&&!ShapeWorld->HasFarWorld();
|
||||
@@ -1721,11 +1722,24 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
Desired.Tiles.begin(),Desired.Tiles.end(),[&](const FVoxelTileKey& Tile)
|
||||
{return Tile.Level<World.FullResClipLevels;}));
|
||||
std::array<std::vector<FHashedTile>,MaxLevels+1> HashRows;
|
||||
std::array<std::vector<FHashedTile>,MaxLevels+1> MeshHashRows;
|
||||
std::array<uint64,MaxLevels+1> SamplesByLevel{};
|
||||
std::array<uint64,MaxLevels+1> VerticesByLevel{};
|
||||
std::array<uint64,MaxLevels+1> IndicesByLevel{};
|
||||
std::array<uint64,MaxLevels+1> TilesByLevel{};
|
||||
std::array<double,MaxLevels+1> WorkerSecondsByLevel{};
|
||||
std::array<double,MaxLevels+1> FirstCompletionByLevel{};
|
||||
std::array<double,MaxLevels+1> LastCompletionByLevel{};
|
||||
std::array<uint64,MaxLevels+1> OutputBytesByLevel{};
|
||||
FirstCompletionByLevel.fill(std::numeric_limits<double>::infinity());
|
||||
std::unordered_set<FVoxelTileKey,FStandaloneTileKeyHash> PlayerNeighborhoodTiles;
|
||||
for(const FVoxelTileKey& Tile:Desired.Tiles)
|
||||
if(Tile.Level==0&&std::abs(Tile.Coord.X-Desired.CenterChunk.X)<=1
|
||||
&&std::abs(Tile.Coord.Y-Desired.CenterChunk.Y)<=1
|
||||
&&std::abs(Tile.Coord.Z-Desired.CenterChunk.Z)<=1)
|
||||
PlayerNeighborhoodTiles.insert(Tile);
|
||||
std::unordered_set<FVoxelTileKey,FStandaloneTileKeyHash> PlayerNeighborhoodReady;
|
||||
auto PlayerNeighborhoodCompleted=std::chrono::steady_clock::time_point{};
|
||||
std::vector<FStandaloneCpuTile> CacheTiles;
|
||||
if(!BakeCachePath.empty())CacheTiles.reserve(Desired.Tiles.size());
|
||||
const auto Start=std::chrono::steady_clock::now();
|
||||
@@ -1758,6 +1772,12 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
{
|
||||
++Finished;
|
||||
const int32 Level=Tile.Key.Level;
|
||||
if(PlayerNeighborhoodTiles.contains(Tile.Key))
|
||||
{
|
||||
PlayerNeighborhoodReady.insert(Tile.Key);
|
||||
if(PlayerNeighborhoodReady.size()==PlayerNeighborhoodTiles.size())
|
||||
PlayerNeighborhoodCompleted=Tile.CompletedAt;
|
||||
}
|
||||
if(Level>=0&&Level<=MaxLevels)
|
||||
{
|
||||
SamplesByLevel[Level]+=Tile.DensitySamples;
|
||||
@@ -1765,7 +1785,13 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
IndicesByLevel[Level]+=Tile.Indices.size();
|
||||
++TilesByLevel[Level];
|
||||
WorkerSecondsByLevel[Level]+=Tile.WorkerSeconds;
|
||||
const double CompletedSeconds=std::chrono::duration<double>(Tile.CompletedAt-Start).count();
|
||||
FirstCompletionByLevel[Level]=std::min(FirstCompletionByLevel[Level],CompletedSeconds);
|
||||
LastCompletionByLevel[Level]=std::max(LastCompletionByLevel[Level],CompletedSeconds);
|
||||
OutputBytesByLevel[Level]+=static_cast<uint64>(Tile.Vertices.capacity())*sizeof(FStandaloneCpuVertex)
|
||||
+static_cast<uint64>(Tile.Indices.capacity())*sizeof(uint32);
|
||||
if(Tile.bHasDensityHash)HashRows[Level].push_back({Tile.Key,Tile.DensityHash});
|
||||
if(Tile.bHasMeshHash)MeshHashRows[Level].push_back({Tile.Key,Tile.MeshHash});
|
||||
}
|
||||
if(Level<World.FullResClipLevels)++FullDetailFinished;
|
||||
if(bShardAudit&&Level==0)
|
||||
@@ -1793,12 +1819,17 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
FullDetailSeconds=std::chrono::duration<double>(
|
||||
std::chrono::steady_clock::now()-Start).count();
|
||||
std::vector<FHashedTile> FullRows;
|
||||
std::vector<FHashedTile> FullMeshRows;
|
||||
for(int32 Level=0;Level<World.FullResClipLevels&&Level<=MaxLevels;++Level)
|
||||
{
|
||||
FullRows.insert(FullRows.end(),HashRows[Level].begin(),HashRows[Level].end());
|
||||
FullMeshRows.insert(FullMeshRows.end(),MeshHashRows[Level].begin(),MeshHashRows[Level].end());
|
||||
}
|
||||
if(bHashDensity||!BakeCachePath.empty())
|
||||
std::printf("[StreamFullDetail] wall=%.3fs generated=%d/%d resident_tiles_cpu_only=1 density_hash=0x%016llx\n",
|
||||
std::printf("[StreamFullDetail] wall=%.3fs generated=%d/%d resident_tiles_cpu_only=1 density_hash=0x%016llx mesh_hash=0x%016llx\n",
|
||||
FullDetailSeconds,FullDetailFinished,FullDetailExpected,
|
||||
static_cast<unsigned long long>(HashTileRows(FullRows)));
|
||||
static_cast<unsigned long long>(HashTileRows(FullRows)),
|
||||
static_cast<unsigned long long>(HashTileRows(FullMeshRows)));
|
||||
else
|
||||
std::printf("[StreamFullDetail] wall=%.3fs generated=%d/%d resident_tiles_cpu_only=1 hash=off\n",
|
||||
FullDetailSeconds,FullDetailFinished,FullDetailExpected);
|
||||
@@ -1807,18 +1838,34 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
}
|
||||
const double FullClipSeconds=std::chrono::duration<double>(
|
||||
std::chrono::steady_clock::now()-Start).count();
|
||||
std::vector<FHashedTile> AllRows;
|
||||
if(bHashDensity||!BakeCachePath.empty())
|
||||
for(auto& LevelRows:HashRows)AllRows.insert(AllRows.end(),LevelRows.begin(),LevelRows.end());
|
||||
if(bHashDensity||!BakeCachePath.empty())
|
||||
std::printf("[StreamFullClipmap] wall=%.3fs generated=%d/%d worker_sum=%.3fs tiles=%d density_hash=0x%016llx\n",
|
||||
FullClipSeconds,Finished,DesiredTotal,
|
||||
std::accumulate(WorkerSecondsByLevel.begin(),WorkerSecondsByLevel.end(),0.0),
|
||||
DesiredTotal,static_cast<unsigned long long>(HashTileRows(AllRows)));
|
||||
if(!PlayerNeighborhoodTiles.empty()&&PlayerNeighborhoodReady.size()==PlayerNeighborhoodTiles.size())
|
||||
std::printf("[PlayerNeighborhood] lod0_tiles=%zu/%zu mesh_ready=%.3fs collision_cook=unavailable_in_standalone\n",
|
||||
PlayerNeighborhoodReady.size(),PlayerNeighborhoodTiles.size(),
|
||||
std::chrono::duration<double>(PlayerNeighborhoodCompleted-Start).count());
|
||||
else
|
||||
std::printf("[StreamFullClipmap] wall=%.3fs generated=%d/%d worker_sum=%.3fs tiles=%d hash=off\n",
|
||||
FullClipSeconds,Finished,DesiredTotal,
|
||||
std::accumulate(WorkerSecondsByLevel.begin(),WorkerSecondsByLevel.end(),0.0),DesiredTotal);
|
||||
std::printf("[PlayerNeighborhood] lod0_tiles=%zu/%zu mesh_ready=missing collision_cook=unavailable_in_standalone\n",
|
||||
PlayerNeighborhoodReady.size(),PlayerNeighborhoodTiles.size());
|
||||
const double TotalWorkerSeconds=std::accumulate(WorkerSecondsByLevel.begin(),WorkerSecondsByLevel.end(),0.0);
|
||||
const double WorkerBusyPercent=100.0*TotalWorkerSeconds
|
||||
/(std::max(1,WorkerCount)*std::max(0.001,FullClipSeconds));
|
||||
std::vector<FHashedTile> AllRows;
|
||||
std::vector<FHashedTile> AllMeshRows;
|
||||
if(bHashDensity||!BakeCachePath.empty())
|
||||
for(int32 Level=0;Level<=MaxLevels;++Level)
|
||||
{
|
||||
AllRows.insert(AllRows.end(),HashRows[Level].begin(),HashRows[Level].end());
|
||||
AllMeshRows.insert(AllMeshRows.end(),MeshHashRows[Level].begin(),MeshHashRows[Level].end());
|
||||
}
|
||||
if(bHashDensity||!BakeCachePath.empty())
|
||||
std::printf("[StreamFullClipmap] wall=%.3fs generated=%d/%d worker_sum=%.3fs worker_busy=%.1f%% worker_idle=%.1f%% tiles=%d density_hash=0x%016llx mesh_hash=0x%016llx\n",
|
||||
FullClipSeconds,Finished,DesiredTotal,TotalWorkerSeconds,WorkerBusyPercent,
|
||||
std::max(0.0,100.0-WorkerBusyPercent),
|
||||
DesiredTotal,static_cast<unsigned long long>(HashTileRows(AllRows)),
|
||||
static_cast<unsigned long long>(HashTileRows(AllMeshRows)));
|
||||
else
|
||||
std::printf("[StreamFullClipmap] wall=%.3fs generated=%d/%d worker_sum=%.3fs worker_busy=%.1f%% worker_idle=%.1f%% tiles=%d hash=off\n",
|
||||
FullClipSeconds,Finished,DesiredTotal,TotalWorkerSeconds,WorkerBusyPercent,
|
||||
std::max(0.0,100.0-WorkerBusyPercent),DesiredTotal);
|
||||
std::printf("[StreamCacheUse] hits=%llu requested=%d\n",
|
||||
static_cast<unsigned long long>(Streamer.GetCacheHits()),DesiredTotal);
|
||||
if(bShardAudit)
|
||||
@@ -1835,16 +1882,23 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
{
|
||||
if(TilesByLevel[Level]==0)continue;
|
||||
if(bHashDensity||!BakeCachePath.empty())
|
||||
std::printf("[StreamLevel] lod=%d tiles=%llu samples=%.3fM worker_sum=%.3fs vertices=%llu indices=%llu density_hash=0x%016llx\n",
|
||||
std::printf("[StreamLevel] lod=%d tiles=%llu samples=%.3fM worker_sum=%.3fs first_ready=%.3fs complete=%.3fs worker_busy=%.1f%% output=%.2fMiB vertices=%llu indices=%llu density_hash=0x%016llx mesh_hash=0x%016llx\n",
|
||||
Level,static_cast<unsigned long long>(TilesByLevel[Level]),
|
||||
static_cast<double>(SamplesByLevel[Level])/1.0e6,WorkerSecondsByLevel[Level],
|
||||
FirstCompletionByLevel[Level],LastCompletionByLevel[Level],
|
||||
100.0*WorkerSecondsByLevel[Level]/(WorkerCount*std::max(0.001,LastCompletionByLevel[Level])),
|
||||
static_cast<double>(OutputBytesByLevel[Level])/1048576.0,
|
||||
static_cast<unsigned long long>(VerticesByLevel[Level]),
|
||||
static_cast<unsigned long long>(IndicesByLevel[Level]),
|
||||
static_cast<unsigned long long>(HashTileRows(HashRows[Level])));
|
||||
static_cast<unsigned long long>(HashTileRows(HashRows[Level])),
|
||||
static_cast<unsigned long long>(HashTileRows(MeshHashRows[Level])));
|
||||
else
|
||||
std::printf("[StreamLevel] lod=%d tiles=%llu samples=%.3fM worker_sum=%.3fs vertices=%llu indices=%llu hash=off\n",
|
||||
std::printf("[StreamLevel] lod=%d tiles=%llu samples=%.3fM worker_sum=%.3fs first_ready=%.3fs complete=%.3fs worker_busy=%.1f%% output=%.2fMiB vertices=%llu indices=%llu hash=off\n",
|
||||
Level,static_cast<unsigned long long>(TilesByLevel[Level]),
|
||||
static_cast<double>(SamplesByLevel[Level])/1.0e6,WorkerSecondsByLevel[Level],
|
||||
FirstCompletionByLevel[Level],LastCompletionByLevel[Level],
|
||||
100.0*WorkerSecondsByLevel[Level]/(WorkerCount*std::max(0.001,LastCompletionByLevel[Level])),
|
||||
static_cast<double>(OutputBytesByLevel[Level])/1048576.0,
|
||||
static_cast<unsigned long long>(VerticesByLevel[Level]),
|
||||
static_cast<unsigned long long>(IndicesByLevel[Level]));
|
||||
}
|
||||
@@ -1864,6 +1918,78 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
if(bShardAudit&&(MeshSheetStats.ThinSheets!=0||MeshSheetStats.SmallComponentsInView!=0))return 13;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int RunEditTransactionBenchmark(FStandaloneOwnerWorld& World,FStandaloneShapeWorld& Shape,
|
||||
const FVector& CameraPositionVoxels,int32 WorkerCount)
|
||||
{
|
||||
using Clock=std::chrono::steady_clock;
|
||||
// Place the spell across the LOD0/LOD1 clipmap seam so one edit exercises every visible
|
||||
// level it intersects, instead of timing only the center LOD0 tiles.
|
||||
const double X=CameraPositionVoxels.X+3.0*CHUNK_SIZE;
|
||||
const double Y=CameraPositionVoxels.Y,Z=CameraPositionVoxels.Z;
|
||||
std::vector<FStandaloneShapeWorld::FEditBrush> Brushes(3);
|
||||
Brushes[0].Shape=FStandaloneShapeWorld::FEditBrush::EShape::Sphere;
|
||||
Brushes[0].Operation=FStandaloneShapeWorld::FEditBrush::EOperation::Carve;
|
||||
Brushes[0].A={X,Y,Z};Brushes[0].Radius=20.0;
|
||||
Brushes[1].Shape=FStandaloneShapeWorld::FEditBrush::EShape::RoundedBox;
|
||||
Brushes[1].Operation=FStandaloneShapeWorld::FEditBrush::EOperation::Carve;
|
||||
Brushes[1].A={X,Y,Z+12.0};Brushes[1].HalfExtent={11.0,11.0,4.0};Brushes[1].Rounding=3.0;
|
||||
Brushes[2].Shape=FStandaloneShapeWorld::FEditBrush::EShape::Capsule;
|
||||
Brushes[2].Operation=FStandaloneShapeWorld::FEditBrush::EOperation::AddRock;
|
||||
Brushes[2].A={X-22.0,Y,Z-8.0};Brushes[2].B={X+22.0,Y,Z-8.0};Brushes[2].Radius=3.0;
|
||||
const auto EditStart=Clock::now();
|
||||
Shape.SetEditTransaction(std::move(Brushes));
|
||||
|
||||
FStandaloneDesiredTileSet Desired=StandaloneWorldMesher::BuildDesiredSet(World,CameraPositionVoxels);
|
||||
const FIntVector EditMin=Shape.GetEditBoundsMin(),EditMax=Shape.GetEditBoundsMax();
|
||||
std::vector<FVoxelTileKey> Affected;Affected.reserve(128);
|
||||
std::array<uint64,MaxLevels+1> AffectedByLevel{},FinishedByLevel{};
|
||||
for(const FVoxelTileKey& Tile:Desired.Tiles)
|
||||
{
|
||||
const FIntVector Origin=Tile.OriginVoxels();const int32 Extent=Tile.ExtentVoxels();
|
||||
const int32 Pad=std::max(2,Tile.StepVoxels());
|
||||
if(Origin.X-Pad>EditMax.X||Origin.X+Extent+Pad<EditMin.X
|
||||
||Origin.Y-Pad>EditMax.Y||Origin.Y+Extent+Pad<EditMin.Y
|
||||
||Origin.Z-Pad>EditMax.Z||Origin.Z+Extent+Pad<EditMin.Z)continue;
|
||||
Affected.push_back(Tile);
|
||||
if(Tile.Level>=0&&Tile.Level<=MaxLevels)++AffectedByLevel[Tile.Level];
|
||||
}
|
||||
if(Affected.empty())
|
||||
{
|
||||
std::fprintf(stderr,"[EditTransaction] failed: no desired tiles intersect the edit bounds.\n");
|
||||
return 9;
|
||||
}
|
||||
FStandaloneStreamingGenerator Streamer(World,WorkerCount,false,nullptr,-1);
|
||||
Streamer.SetDesiredTileFilter(Affected);
|
||||
const auto Start=Clock::now();
|
||||
const FStandaloneStreamingUpdate Update=Streamer.Start(CameraPositionVoxels);
|
||||
int32 Finished=0;
|
||||
double WorkerSeconds=0.0;
|
||||
while(Finished<Update.DesiredTiles)
|
||||
{
|
||||
std::vector<FStandaloneCpuTile> Ready=Streamer.TakeReady();
|
||||
if(Ready.empty()){std::this_thread::sleep_for(std::chrono::milliseconds(1));continue;}
|
||||
for(const FStandaloneCpuTile& Tile:Ready)
|
||||
{
|
||||
++Finished;WorkerSeconds+=Tile.WorkerSeconds;
|
||||
if(Tile.Key.Level>=0&&Tile.Key.Level<=MaxLevels)++FinishedByLevel[Tile.Key.Level];
|
||||
}
|
||||
}
|
||||
const double RemeshWall=std::chrono::duration<double>(Clock::now()-Start).count();
|
||||
Streamer.Stop();
|
||||
std::printf("[EditTransaction] operation=crater_batch transaction_count=1 brushes=3 diameter=10.0m offset_from_player=%.2fm affected_tiles=%d/%zu workers=%d cache_hits=%llu remesh_wall=%.3fs worker_sum=%.3fs edit_to_ready=%.3fs status=%s\n",
|
||||
3.0*CHUNK_SIZE*VOXEL_SIZE/100.0,
|
||||
Finished,Affected.size(),WorkerCount,static_cast<unsigned long long>(Streamer.GetCacheHits()),
|
||||
RemeshWall,WorkerSeconds,std::chrono::duration<double>(Clock::now()-EditStart).count(),
|
||||
Finished==static_cast<int32>(Affected.size())?"PASS":"FAIL");
|
||||
for(int32 Level=0;Level<=MaxLevels;++Level)
|
||||
if(AffectedByLevel[Level])
|
||||
std::printf("[EditTransactionLevel] lod=%d tiles=%llu remeshed=%llu status=%s\n",Level,
|
||||
static_cast<unsigned long long>(AffectedByLevel[Level]),
|
||||
static_cast<unsigned long long>(FinishedByLevel[Level]),
|
||||
AffectedByLevel[Level]==FinishedByLevel[Level]?"PASS":"FAIL");
|
||||
return Finished==static_cast<int32>(Affected.size())?0:10;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int Argc,char** Argv)
|
||||
@@ -1878,6 +2004,7 @@ int main(int Argc,char** Argv)
|
||||
bool bStreamBenchmark=false;
|
||||
bool bShardAudit=false;
|
||||
bool bStreamHash=false;
|
||||
bool bEditBenchmark=false;
|
||||
bool bRoundWallCompare=false;
|
||||
int32 RoundCompareTileCount=16;
|
||||
std::string BakeMeshCachePath;
|
||||
@@ -1907,6 +2034,8 @@ int main(int Argc,char** Argv)
|
||||
else if(Arg=="--round-tunnels-legacy")
|
||||
{App.bRoundTunnels=true;App.bRoundTunnelEnhanced=false;}
|
||||
else if(Arg=="--stream-benchmark")bStreamBenchmark=true;
|
||||
else if(Arg=="--edit-benchmark")
|
||||
{bEditBenchmark=true;bStreamBenchmark=true;App.bShapeWorld=true;}
|
||||
else if(Arg=="--stream-hash")bStreamHash=true;
|
||||
else if(Arg=="--shard-audit")bShardAudit=true;
|
||||
else if(Arg=="--compare-round-walls")
|
||||
@@ -1984,6 +2113,9 @@ int main(int Argc,char** Argv)
|
||||
return RunRoundWallComparison(*App.World,App.Position,RoundCompareTileCount);
|
||||
if(bStreamBenchmark)
|
||||
{
|
||||
if(bEditBenchmark)
|
||||
return RunEditTransactionBenchmark(*App.World,*App.ShapeWorld,
|
||||
FVector(App.Position.X*4.0,App.Position.Y*4.0,App.Position.Z*4.0),App.WorkerCount);
|
||||
const double FullDetailWidth=static_cast<double>(2*App.ClipRadius+2)
|
||||
*CHUNK_SIZE*VOXEL_SIZE/100.0;
|
||||
std::printf("Standalone clip override: exported radius=%d (112 m cube) -> radius=%d (%.0f m LOD0 cube), voxel size=25 cm.\n",
|
||||
|
||||
Reference in new issue
Block a user