wip(standalone): GPU step 20 -- detail band batched by 8 columns; gap instrumented

ApplyDetailBand claims eight adjacent columns per batch, keeping each column's scalar
order: shape stage ~586 -> ~566 ms in Luna's profile. Hashes w1/w8 identical.
My runs outside the sandbox (Step20.exe, marker removed first, interleaved):
  CPU 1.319 / 1.287 / 1.288 s
  GPU 1.320 (marker miss, 0 GPU tiles) / 1.244 / 1.215 / 1.219 s (match, ~1,400 GPU)
Shape compile 549-570 ms in both modes. The 669-821 ms gap in cached runs is still
there: export starts only after [ChunkCrossingWork] reports the ~840 CPU tiles
(LOD0-2) finished, so GPU export waits behind CPU meshing on the shared workers.
Slowest GPU tile 1.7 / 18.4 / 27.3 ms in runs b/c/d.
Audits: hashes, walk 5,642/5,642, near 960 / far 1,114 0 diffs vs CPU and WARP,
thin 353, survival 6.14 GB, ten captures as step 18. Far vertex-distance diagnostic:
247 bound failures (max 64.56 cm), unchanged since step 16.

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-07 22:35:22 +02:00
1 parent 89fd9a0021
commit 2cb3344e1b
6 files changed
+146 -18

No files matched your search

+49 -6
View File
@@ -1579,6 +1579,8 @@ namespace
uint64 StartupNonBlockingPresentCalls=0,StartupNonBlockingPresentSkips=0;
double GpuFarGpuDispatchMs=0.0;
double GpuFarGpuDispatchMaxMs=0.0;
FVoxelTileKey GpuFarGpuDispatchMaxKey{};
bool bGpuFarGpuDispatchMaxKeyValid=false;
double GpuFarSubmissionSpanMs=0.0,GpuFarSubmissionSpanMaxMs=0.0;
uint64 GpuFarLocalMemoryPeakBytes=0,GpuFarLocalMemoryBudgetBytes=0;
double GpuFarPoolReserveMs=0.0;
@@ -1750,6 +1752,7 @@ namespace
GpuFarAudit.reset();bGpuFarAuditReported=false;
GpuFarTilesSubmitted=0;GpuFarEnqueueWallSeconds=0.0;GpuFarGpuDispatchMs=0.0;
GpuFarGpuDispatchMaxMs=0.0;GpuFarSubmissionSpanMs=0.0;GpuFarSubmissionSpanMaxMs=0.0;
GpuFarGpuDispatchMaxKey={};bGpuFarGpuDispatchMaxKeyValid=false;
GpuFarLocalMemoryPeakBytes=0;GpuFarLocalMemoryBudgetBytes=0;
bGpuFarFirstTileReported=false;
GpuSkirtDrawAuditTiles=GpuSkirtDrawAuditZeroCounts=GpuSkirtDrawAuditOverflow=0;
@@ -2743,7 +2746,11 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
{
Tile.GpuDispatchMs=TileDispatchMs;Tile.bTimingResolved=true;
GpuFarGpuDispatchMs+=TileDispatchMs;
GpuFarGpuDispatchMaxMs=std::max(GpuFarGpuDispatchMaxMs,TileDispatchMs);
if(TileDispatchMs>GpuFarGpuDispatchMaxMs)
{
GpuFarGpuDispatchMaxMs=TileDispatchMs;
GpuFarGpuDispatchMaxKey=Key;bGpuFarGpuDispatchMaxKeyValid=true;
}
}
}
if(Batch.bGpuComplete)
@@ -2769,7 +2776,11 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
{
Tile.GpuDispatchMs=AsyncDispatchMs;
GpuFarGpuDispatchMs+=AsyncDispatchMs;
GpuFarGpuDispatchMaxMs=std::max(GpuFarGpuDispatchMaxMs,AsyncDispatchMs);
if(AsyncDispatchMs>GpuFarGpuDispatchMaxMs)
{
GpuFarGpuDispatchMaxMs=AsyncDispatchMs;
GpuFarGpuDispatchMaxKey=Key;bGpuFarGpuDispatchMaxKeyValid=true;
}
}
Tile.bTimingResolved=true;
}
@@ -2784,10 +2795,16 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
return ResidentTiles.contains(Key);
}
void PrepareGpuFarInputs(std::vector<FVoxelTileKey> NeedPreparation)
void PrepareGpuFarInputs(std::vector<FVoxelTileKey> NeedPreparation,
std::chrono::steady_clock::time_point DispatchAt)
{
if(NeedPreparation.empty())return;
const auto PrepareStart=std::chrono::steady_clock::now();
if(bGpuFarTimelineEnabled)
std::printf("[ColdTimeline] event=gpu_input_export_thread_started at_ms=%.3f dispatch_to_start_ms=%.3f frame=%llu\n",
ColdTimelineMilliseconds(PrepareStart),
std::chrono::duration<double,std::milli>(PrepareStart-DispatchAt).count(),
static_cast<unsigned long long>(ColdTimelineFrame()));
std::printf("[ColdTimeline] stage=gpu_input_export begin_ms=%.3f tiles=%zu\n",
ColdTimelineMilliseconds(PrepareStart),NeedPreparation.size());
const std::size_t ThreadCount=std::min<std::size_t>(8,NeedPreparation.size());
@@ -3017,19 +3034,36 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
std::chrono::steady_clock::now()-ReserveStart).count();
std::printf("[ColdTimeline] stage=gpu_output_pool end_ms=%.3f duration_ms=%.3f\n",
ColdTimelineMilliseconds(std::chrono::steady_clock::now()),GpuFarPoolReserveMs);
const auto PoolEnd=std::chrono::steady_clock::now();
const auto MemorySampleStart=std::chrono::steady_clock::now();
SampleGpuFarMemory();
const double MemorySampleMs=std::chrono::duration<double,std::milli>(
std::chrono::steady_clock::now()-MemorySampleStart).count();
if(GpuFarNeedPreparation.empty())
{
bGpuFarPreparationFinished.store(true,std::memory_order_relaxed);
return;
}
bGpuFarPreparationFinished.store(false,std::memory_order_relaxed);
const auto PreparationListStart=std::chrono::steady_clock::now();
std::vector<FVoxelTileKey> NeedPreparation=GpuFarNeedPreparation;
std::stable_sort(NeedPreparation.begin(),NeedPreparation.end(),
[&](const FVoxelTileKey& A,const FVoxelTileKey& B){return GpuFarKeyLess(A,B);});
GpuFarPreparationThread=std::thread([this,NeedPreparation]()
const double PreparationListMs=std::chrono::duration<double,std::milli>(
std::chrono::steady_clock::now()-PreparationListStart).count();
const auto DispatchAt=std::chrono::steady_clock::now();
if(bGpuFarTimelineEnabled)
std::printf("[ColdTimeline] event=gpu_input_export_thread_dispatch at_ms=%.3f after_pool_ms=%.3f memory_sample_ms=%.3f list_copy_sort_ms=%.3f tiles=%zu\n",
ColdTimelineMilliseconds(DispatchAt),
std::chrono::duration<double,std::milli>(DispatchAt-PoolEnd).count(),
MemorySampleMs,PreparationListMs,NeedPreparation.size());
GpuFarPreparationThread=std::thread([this,NeedPreparation,DispatchAt]()
{
try { PrepareGpuFarInputs(NeedPreparation); }
const bool bPrioritySet=SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_NORMAL)!=0;
if(bGpuFarTimelineEnabled)
std::printf("[GpuFarPreparationThread] priority=%s process=BelowNormal thread_relative=Normal\n",
bPrioritySet?"Normal":"set_failed");
try { PrepareGpuFarInputs(NeedPreparation,DispatchAt); }
catch(const std::exception& Error)
{
std::lock_guard Lock(GpuFarPreparedResultsMutex);
@@ -3784,7 +3818,11 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
if(GpuDispatchMs>0.0)
{
GpuFarGpuDispatchMs+=GpuDispatchMs;
GpuFarGpuDispatchMaxMs=std::max(GpuFarGpuDispatchMaxMs,GpuDispatchMs);
if(GpuDispatchMs>GpuFarGpuDispatchMaxMs)
{
GpuFarGpuDispatchMaxMs=GpuDispatchMs;
GpuFarGpuDispatchMaxKey=Key;bGpuFarGpuDispatchMaxKeyValid=true;
}
Tile.bTimingResolved=true;
}
++GpuFarTilesSubmitted;++Built;
@@ -4577,6 +4615,11 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
}
const double AverageGpuMs=GpuFarTilesSubmitted
?GpuFarGpuDispatchMs/static_cast<double>(GpuFarTilesSubmitted):0.0;
if(bGpuFarGpuDispatchMaxKeyValid)
std::printf("[GpuFarMaxDispatchTile] key=(%d,%d,%d,lod%d) dispatch_ms=%.6f\n",
GpuFarGpuDispatchMaxKey.Coord.X,GpuFarGpuDispatchMaxKey.Coord.Y,
GpuFarGpuDispatchMaxKey.Coord.Z,GpuFarGpuDispatchMaxKey.Level,
GpuFarGpuDispatchMaxMs);
std::printf("[GpuFarModeTiming] mode=%s cpu_mesh_tiles=%llu cpu_mesh_worker_sum_s=%.6f cpu_input_export_worker_sum_s=%.6f gpu_submitted_tiles=%llu gpu_busy_timestamp_sum_ms=%.3f whole_view_wall_s=%.6f warmup_cpu_claimed=%llu warmup_gpu_takeover=%llu\n",
bGpuFarAuditEnabled?"gpu_audit":"gpu_hybrid",
static_cast<unsigned long long>(CpuMeshTiles),CpuMeshWorkerSeconds,