wip(standalone): GPU step 22 -- startup multi-batch pump, pool before handoff: cached 1.12-1.20 s

Per-batch GPU timeline (submit, timestamps, query, consume). Startup Draws submit
several ready batches per pump (80 ms startup budget, 16 ms per batch); the output
pool is reserved before the warm-up-pending deferral.
My runs outside the sandbox (Step22.exe, marker removed first, interleaved):
  CPU 1.295 / 1.314 / 1.284 / 1.342 s
  GPU cached (marker match, -Timeline): 1.197 / 1.121 / 1.133 s
      (1,124 / 1,380 / 1,415 GPU tiles; output pool at 655-680 ms in all three)
  GPU marker miss: 1.446 (-Timeline), 1.447, 1.312 s (no -Timeline). The 1.447 run's
      shape compile ended at 776 ms vs 634-643 ms in its neighbours, so this is
      startup variance, not a clear regression; to be watched.
Known tradeoff: startup pumps can stall the main thread 65-108 ms (submit calls run
past the budget); fine before the first view, to check in an interactive viewer.
Audits: hashes w1/w8, walk 5,642/5,642, near 960 / far 1,114 0 diffs vs CPU and
WARP, thin 353, survival ~6.2 GiB, ten captures as step 18.

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-08 00:00:06 +02:00
1 parent 38f6009200
commit 052b6de354
7 files changed
+274 -43

No files matched your search

+158 -41
View File
@@ -1393,10 +1393,13 @@ namespace
uint32 ResidentCount=0;
std::vector<FVoxelTileKey> Keys;
uint64 TimestampFrequency=0;
uint64 GpuStartTimestamp=0,GpuEndTimestamp=0;
double GpuDispatchMs=0.0;
std::chrono::steady_clock::time_point PreparedAt{};
std::chrono::steady_clock::time_point SubmitStartedAt{};
std::chrono::steady_clock::time_point SubmittedAt{};
std::chrono::steady_clock::time_point CompletedAt{};
std::chrono::steady_clock::time_point ConsumedAt{};
bool bGpuComplete=false,bTimingResolved=false;
};
@@ -1559,6 +1562,7 @@ namespace
std::atomic<uint64> GpuFarPreparationGeneration{0};
std::atomic<bool> bGpuFarPreparationFinished{false};
bool bGpuFarPreparationStarted=false,bGpuFarInputsFinalized=false;
bool bGpuFarPreparationDeferredReported=false;
bool bGpuFarFirstPreparedInputReported=false;
bool bGpuWarmupCompleted=false,bGpuFarTimelineEnabled=false;
bool bGpuWarmupRoutePending=false;
@@ -1605,6 +1609,7 @@ namespace
uint64 GpuFarSubmissionBatches=0;
uint32 GpuFarMaxTilesPerSubmissionBatch=0;
uint32 GpuFarOutputCapacitySlots=0,GpuFarNextOutputSlot=0;
uint32 GpuFarOutputPoolReservedSlots=0;
std::vector<uint32> GpuFarFreeOutputSlots;
std::unordered_map<FVoxelTileKey,VoxelForgeCore::FarFieldTileInput,FStandaloneTileKeyHash> GpuFarPreparedInputs;
std::unordered_map<FVoxelTileKey,std::string,FStandaloneTileKeyHash> GpuFarPreparationErrors;
@@ -1756,6 +1761,7 @@ namespace
GpuFarCandidateTiles.clear();GpuFarBaseCpuFallbackTiles.clear();
GpuFarCpuFallbackTiles.clear();GpuFarNeedPreparation.clear();
bGpuFarPreparationStarted=false;bGpuFarInputsFinalized=false;
bGpuFarPreparationDeferredReported=false;
bGpuFarPreparationFinished.store(false,std::memory_order_relaxed);
bGpuWarmupRoutePending=false;
GpuFarWarmupCpuClaimedTiles=0;GpuFarWarmupGpuTakeoverTiles=0;
@@ -1778,6 +1784,7 @@ namespace
GpuSkirtDrawAuditSumIndices=GpuSkirtDrawAuditMaxIndices=0;
GpuFarPoolReserveMs=0.0;
GpuFarSubmissionBatches=0;GpuFarMaxTilesPerSubmissionBatch=0;
GpuFarOutputPoolReservedSlots=0;
DirtyBatchSet.clear();DesiredTiles.clear();DesiredTileSet.clear();CrossingWork.clear();
DeferredLeaverTiles.clear();
for(int32 I=0;I<=MaxLevels;++I)
@@ -2668,7 +2675,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
{
if(Tile.CompletedAt.time_since_epoch().count()!=0)return;
Tile.CompletedAt=std::chrono::steady_clock::now();
if(!bGpuFarTimelineEnabled)return;
if(!bGpuFarTimelineEnabled||BatchIndex!=UINT32_MAX)return;
std::printf("[GpuTileTimeline] event=completed key=(%d,%d,%d,lod%d) batch=%u at_ms=%.3f frame=%llu submitted_ms=%.3f\n",
Key.Coord.X,Key.Coord.Y,Key.Coord.Z,Key.Level,BatchIndex,
ColdTimelineMilliseconds(Tile.CompletedAt),
@@ -2685,7 +2692,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
if(Tile.bColdTimelineResidentReported)return;
Tile.bColdTimelineResidentReported=true;
const auto ResidentAt=std::chrono::steady_clock::now();
if(bGpuFarTimelineEnabled)
if(bGpuFarTimelineEnabled&&Tile.BatchIndex==UINT32_MAX)
std::printf("[GpuTileTimeline] event=resident key=(%d,%d,%d,lod%d) batch=%u at_ms=%.3f frame=%llu submitted_ms=%.3f completed_ms=%.3f\n",
Key.Coord.X,Key.Coord.Y,Key.Coord.Z,Key.Level,Tile.BatchIndex,
ColdTimelineMilliseconds(ResidentAt),
@@ -2696,10 +2703,69 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
FGpuFarBatch& Batch=GpuFarBatches[Tile.BatchIndex];
++Batch.ResidentCount;
if(bGpuFarTimelineEnabled&&Batch.ResidentCount==Batch.TileCount)
{
Batch.ConsumedAt=ResidentAt;
std::printf("[GpuBatchTimeline] event=resident batch=%u at_ms=%.3f frame=%llu tile_count=%u submitted_ms=%.3f completed_ms=%.3f\n",
Tile.BatchIndex,ColdTimelineMilliseconds(ResidentAt),
static_cast<unsigned long long>(ColdTimelineFrame()),Batch.TileCount,
ColdTimelineMilliseconds(Batch.SubmittedAt),ColdTimelineMilliseconds(Batch.CompletedAt));
std::printf("[GpuBatchTimeline] event=consumed batch=%u at_ms=%.3f frame=%llu tile_count=%u submit_to_gpu_complete_ms=%.3f gpu_complete_to_consumed_ms=%.3f output=direct_gpu_buffers\n",
Tile.BatchIndex,ColdTimelineMilliseconds(ResidentAt),
static_cast<unsigned long long>(ColdTimelineFrame()),Batch.TileCount,
ColdTimelineMilliseconds(Batch.CompletedAt)-ColdTimelineMilliseconds(Batch.SubmittedAt),
ColdTimelineMilliseconds(ResidentAt)-ColdTimelineMilliseconds(Batch.CompletedAt));
}
}
}
void ReportGpuBatchTimelineSummary()
{
if(!bGpuFarTimelineEnabled)return;
uint64 FirstGpuStart=0,CommonFrequency=0;
for(const FGpuFarBatch& Batch:GpuFarBatches)
{
if(Batch.bTimingResolved&&Batch.TimestampFrequency&&Batch.GpuStartTimestamp)
{
FirstGpuStart=Batch.GpuStartTimestamp;
CommonFrequency=Batch.TimestampFrequency;
break;
}
}
uint64 PreviousGpuEnd=0,PreviousFrequency=0;
for(std::size_t I=0;I<GpuFarBatches.size();++I)
{
const FGpuFarBatch& Batch=GpuFarBatches[I];
const auto AtMs=[this](const std::chrono::steady_clock::time_point& At)
{
return At.time_since_epoch().count()!=0
?ColdTimelineMilliseconds(At):-1.0;
};
double GpuStartFromFirstMs=-1.0,GpuEndFromFirstMs=-1.0,GpuGapBeforeMs=-1.0;
if(Batch.bTimingResolved&&Batch.TimestampFrequency&&Batch.GpuStartTimestamp
&&CommonFrequency==Batch.TimestampFrequency&&FirstGpuStart)
{
GpuStartFromFirstMs=(static_cast<double>(Batch.GpuStartTimestamp)
-static_cast<double>(FirstGpuStart))*1000.0/Batch.TimestampFrequency;
GpuEndFromFirstMs=(static_cast<double>(Batch.GpuEndTimestamp)
-static_cast<double>(FirstGpuStart))*1000.0/Batch.TimestampFrequency;
if(PreviousGpuEnd&&PreviousFrequency==Batch.TimestampFrequency)
GpuGapBeforeMs=(static_cast<double>(Batch.GpuStartTimestamp)
-static_cast<double>(PreviousGpuEnd))*1000.0/Batch.TimestampFrequency;
}
std::printf("[GpuBatchTimeline] event=summary batch=%zu tiles=%u cpu_batch_ready_ms=%.3f cpu_submit_begin_ms=%.3f cpu_submit_end_ms=%.3f gpu_start_tick=%llu gpu_end_tick=%llu timestamp_frequency_hz=%llu gpu_start_from_first_ms=%.3f gpu_end_from_first_ms=%.3f gpu_gap_from_previous_end_ms=%.3f gpu_busy_ms=%.3f completion_observed_ms=%.3f consumed_ms=%.3f submit_to_completion_observed_ms=%.3f completion_to_consumed_ms=%.3f timing_resolved=%s\n",
I,Batch.TileCount,AtMs(Batch.PreparedAt),AtMs(Batch.SubmitStartedAt),
AtMs(Batch.SubmittedAt),static_cast<unsigned long long>(Batch.GpuStartTimestamp),
static_cast<unsigned long long>(Batch.GpuEndTimestamp),
static_cast<unsigned long long>(Batch.TimestampFrequency),GpuStartFromFirstMs,
GpuEndFromFirstMs,GpuGapBeforeMs,Batch.GpuDispatchMs,AtMs(Batch.CompletedAt),
AtMs(Batch.ConsumedAt),Batch.CompletedAt.time_since_epoch().count()!=0
?AtMs(Batch.CompletedAt)-AtMs(Batch.SubmittedAt):-1.0,
Batch.ConsumedAt.time_since_epoch().count()!=0
&&Batch.CompletedAt.time_since_epoch().count()!=0
?AtMs(Batch.ConsumedAt)-AtMs(Batch.CompletedAt):-1.0,
Batch.bTimingResolved?"yes":"no");
if(Batch.bTimingResolved&&Batch.TimestampFrequency&&Batch.GpuEndTimestamp)
{PreviousGpuEnd=Batch.GpuEndTimestamp;PreviousFrequency=Batch.TimestampFrequency;}
}
}
@@ -2741,13 +2807,16 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
{
BatchTileIt->second.bGpuComplete=true;
ReportGpuTileCompletion(BatchKey,BatchTileIt->second,Tile.BatchIndex);
RecordGpuCrossingTileReady(BatchKey);
ReportGpuTileResident(BatchKey,BatchTileIt->second);
}
}
}
if(Batch.bGpuComplete&&!Batch.bTimingResolved)
{
if(VoxelForgeGpuFarViewer::ResolveGpuTiming(Context,Batch.TimingQueries,
Batch.GpuDispatchMs,&Batch.TimestampFrequency))
Batch.GpuDispatchMs,&Batch.TimestampFrequency,
&Batch.GpuStartTimestamp,&Batch.GpuEndTimestamp))
{
Batch.bTimingResolved=true;
GpuFarSubmissionSpanMs+=Batch.GpuDispatchMs;
@@ -2933,6 +3002,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
GpuFarCandidateTiles.clear();GpuFarBaseCpuFallbackTiles.clear();
GpuFarCpuFallbackTiles.clear();GpuFarNeedPreparation.clear();
bGpuFarPreparationStarted=false;bGpuFarInputsFinalized=false;
bGpuFarPreparationDeferredReported=false;
bGpuFarFirstPreparedInputReported=false;
bGpuFarPreparationFinished.store(false,std::memory_order_relaxed);
bGpuWarmupRoutePending=false;
@@ -3006,6 +3076,11 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
bGpuWarmupRoutePending=bGpuFarShaderPrewarm&&!bGpuFarAuditEnabled
&&!bGpuWarmupCompleted&&!bGpuFarWarmupBeforeWorkers
&&!GpuFarCandidateTiles.empty();
if(bGpuFarTimelineEnabled)
std::printf("[ColdTimeline] event=gpu_output_pool_candidates at_ms=%.3f slots=%u candidates=%zu warmup_handoff_pending=%s\n",
ColdTimelineMilliseconds(std::chrono::steady_clock::now()),
GpuFarOutputCapacitySlots,GpuFarCandidateTiles.size(),
bGpuWarmupRoutePending?"yes":"no");
if(bGpuWarmupRoutePending)
{
GpuFarCpuFallbackTiles.reserve(GpuFarCpuFallbackTiles.size()+GpuFarCandidateTiles.size());
@@ -3050,40 +3125,53 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
void StartGpuFarPreparation()
{
if(!bGpuFarEnabled||!ShapeWorld||!ShapeWorld->hasFarWorld()||bGpuFarPreparationStarted)return;
if(bGpuWarmupDeferredUntilFirstView&&!bGpuWarmupDeferredStarted)return;
if(GpuFarCandidateTiles.empty())
{
bGpuFarPreparationStarted=true;
bGpuFarInputsFinalized=true;
bGpuFarPreparationFinished.store(true,std::memory_order_relaxed);
return;
}
if(GpuFarOutputPoolReservedSlots<GpuFarOutputCapacitySlots)
{
const auto ReserveStart=std::chrono::steady_clock::now();
std::printf("[ColdTimeline] stage=gpu_output_pool begin_ms=%.3f slots=%u reason=reserve_before_warmup_handoff\n",
ColdTimelineMilliseconds(ReserveStart),GpuFarOutputCapacitySlots);
std::string PoolError;
if(!VoxelForgeGpuFarViewer::ReserveFarOutputPool(Device,Context,GpuFarShaderPath,
GpuFarOutputCapacitySlots,static_cast<uint32>(World->CoarseTileCells),PoolError))
{
std::fprintf(stderr,"[GpuFarPoolError] %s\n",PoolError.c_str());
bGpuFarEnabled=false;bGpuFarPreparationStarted=true;
GpuFarOutputCapacitySlots=0;GpuFarOutputPoolReservedSlots=0;
for(auto& Pair:GpuFarTiles)ReleaseGpuFarTile(Pair.second);
GpuFarTiles.clear();GpuFarCandidateTiles.clear();GpuFarQueue.clear();GpuFarQueued.clear();
GpuFarNeedPreparation.clear();GpuFarEligibleTiles.clear();
AddGpuFarCpuFallbackTiles(DesiredTiles);
bGpuFarInputsFinalized=true;
bGpuFarPreparationFinished.store(true,std::memory_order_relaxed);
return;
}
const double ReserveMs=std::chrono::duration<double,std::milli>(
std::chrono::steady_clock::now()-ReserveStart).count();
GpuFarPoolReserveMs+=ReserveMs;
GpuFarOutputPoolReservedSlots=GpuFarOutputCapacitySlots;
std::printf("[ColdTimeline] stage=gpu_output_pool end_ms=%.3f duration_ms=%.3f reserved_slots=%u\n",
ColdTimelineMilliseconds(std::chrono::steady_clock::now()),ReserveMs,
GpuFarOutputPoolReservedSlots);
}
if(bGpuWarmupRoutePending&&!bGpuWarmupCompleted)
{
std::printf("[GpuFarPrepDeferred] reason=shader_warmup_cpu_tiles_own_candidates tiles=%zu\n",
GpuFarCandidateTiles.size());
if(!bGpuFarPreparationDeferredReported)
{
bGpuFarPreparationDeferredReported=true;
std::printf("[GpuFarPrepDeferred] reason=shader_warmup_cpu_tiles_own_candidates tiles=%zu output_pool_reserved_slots=%u\n",
GpuFarCandidateTiles.size(),GpuFarOutputPoolReservedSlots);
}
return;
}
bGpuFarPreparationStarted=true;
if(GpuFarCandidateTiles.empty())
{
bGpuFarInputsFinalized=true;
bGpuFarPreparationFinished.store(true,std::memory_order_relaxed);
return;
}
const auto ReserveStart=std::chrono::steady_clock::now();
std::printf("[ColdTimeline] stage=gpu_output_pool begin_ms=%.3f slots=%u\n",
ColdTimelineMilliseconds(ReserveStart),GpuFarOutputCapacitySlots);
std::string PoolError;
if(!VoxelForgeGpuFarViewer::ReserveFarOutputPool(Device,Context,GpuFarShaderPath,
GpuFarOutputCapacitySlots,static_cast<uint32>(World->CoarseTileCells),PoolError))
{
std::fprintf(stderr,"[GpuFarPoolError] %s\n",PoolError.c_str());
bGpuFarEnabled=false;GpuFarOutputCapacitySlots=0;
for(auto& Pair:GpuFarTiles)ReleaseGpuFarTile(Pair.second);
GpuFarTiles.clear();GpuFarCandidateTiles.clear();GpuFarQueue.clear();GpuFarQueued.clear();
GpuFarNeedPreparation.clear();GpuFarEligibleTiles.clear();
AddGpuFarCpuFallbackTiles(DesiredTiles);
bGpuFarInputsFinalized=true;
bGpuFarPreparationFinished.store(true,std::memory_order_relaxed);
return;
}
GpuFarPoolReserveMs+=std::chrono::duration<double,std::milli>(
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();
@@ -3317,7 +3405,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
return true;
}
void PumpGpuFarGeneration()
void PumpGpuFarGeneration(double MaxPumpBudgetMilliseconds=8.0)
{
if(!bGpuFarEnabled||!ShapeWorld||!ShapeWorld->hasFarWorld()||!Device||!Context)return;
if(bGpuWarmupDeferredUntilFirstView&&!bGpuWarmupDeferredStarted)
@@ -3341,6 +3429,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
std::string PipelineError;
if(!EnsureGpuFarPipeline(PipelineError))
{bGpuFarEnabled=false;AddGpuFarCpuFallbackTiles(DesiredTiles);return;}
StartGpuFarPreparation();
if(!CompleteGpuShaderWarmupAndRoute())return;
StartGpuFarPreparation();
if(!bGpuFarEnabled||!FinalizeGpuFarPreparation()||GpuFarQueue.empty())return;
@@ -3348,18 +3437,18 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
&&!bDisableGpuColdStartDrain&&bGpuFarWarmupBeforeWorkers;
constexpr std::size_t StartupFirstGpuBatchTarget=32;
constexpr std::size_t StartupGpuBatchTarget=256;
constexpr double StartupGpuPumpBudgetMilliseconds=8.0;
constexpr double StartupGpuPumpBudgetMilliseconds=16.0;
const bool bWaitForStreamingGpuBatch=!bColdDrainBeforeWholeView
&&!bGpuFarAuditEnabled&&!bInitialClipmapReported;
const std::size_t GpuPumpTileLimit=bWaitForStreamingGpuBatch
?(GpuFarSubmissionBatches==0?StartupFirstGpuBatchTarget:StartupGpuBatchTarget):32;
const double GpuPumpBudgetMilliseconds=bWaitForStreamingGpuBatch
?StartupGpuPumpBudgetMilliseconds:4.0;
const double GpuPumpBudgetMilliseconds=std::min(MaxPumpBudgetMilliseconds,
bWaitForStreamingGpuBatch?StartupGpuPumpBudgetMilliseconds:4.0);
if(bWaitForStreamingGpuBatch
&&!bGpuFarPreparationFinished.load(std::memory_order_acquire)
&&GpuFarQueue.size()<StartupFirstGpuBatchTarget)return;
if(bWaitForStreamingGpuBatch&&GpuFarSubmissionBatches==0)
std::printf("[GpuFarStreamingPolicy] first_batch_tiles=%zu start_threshold=%zu later_batch_limit=%zu pump_budget_ms=%.1f order=lod_then_camera_distance\n",
std::printf("[GpuFarStreamingPolicy] first_batch_tiles=%zu start_threshold=%zu later_batch_limit=%zu per_draw_budget_ms=80.0 per_batch_build_budget_ms=%.1f submit_batches_per_draw=until_budget_or_input_starvation order=lod_then_camera_distance\n",
GpuFarQueue.size(),StartupFirstGpuBatchTarget,StartupGpuBatchTarget,
StartupGpuPumpBudgetMilliseconds);
const auto PumpStart=std::chrono::steady_clock::now();
@@ -3911,6 +4000,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
BatchIndex,ColdTimelineMilliseconds(Batch.PreparedAt),
static_cast<unsigned long long>(ColdTimelineFrame()),Batch.TileCount);
std::string BatchError;
Batch.SubmitStartedAt=std::chrono::steady_clock::now();
if(!VoxelForgeGpuFarViewer::SubmitFarTileBatch(Device,Context,GpuFarShaderPath,
&Batch.CompletionEvent,&Batch.TimingQueries,BatchError))
{
@@ -3932,16 +4022,23 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
{
It->second.BatchIndex=BatchIndex;
It->second.SubmittedAt=Batch.SubmittedAt;
if(bGpuFarTimelineEnabled)std::printf("[GpuTileTimeline] event=submitted key=(%d,%d,%d,lod%d) batch=%u at_ms=%.3f frame=%llu prepared_ms=%.3f\n",
if(bGpuFarTimelineEnabled&&!bDeferredBatch)std::printf("[GpuTileTimeline] event=submitted key=(%d,%d,%d,lod%d) batch=%u at_ms=%.3f frame=%llu prepared_ms=%.3f\n",
Key.Coord.X,Key.Coord.Y,Key.Coord.Z,Key.Level,BatchIndex,
ColdTimelineMilliseconds(Batch.SubmittedAt),
static_cast<unsigned long long>(ColdTimelineFrame()),
ColdTimelineMilliseconds(It->second.PreparedAt));
}
}
if(bGpuFarTimelineEnabled)std::printf("[GpuBatchTimeline] event=submitted batch=%u at_ms=%.3f frame=%llu tile_count=%u\n",
BatchIndex,ColdTimelineMilliseconds(Batch.SubmittedAt),
if(bGpuFarTimelineEnabled)std::printf("[GpuBatchTimeline] event=cpu_submit batch=%u begin_ms=%.3f end_ms=%.3f cpu_submit_ms=%.3f frame=%llu tile_count=%u\n",
BatchIndex,ColdTimelineMilliseconds(Batch.SubmitStartedAt),
ColdTimelineMilliseconds(Batch.SubmittedAt),
std::chrono::duration<double,std::milli>(
Batch.SubmittedAt-Batch.SubmitStartedAt).count(),
static_cast<unsigned long long>(ColdTimelineFrame()),Batch.TileCount);
if(bGpuFarTimelineEnabled)std::printf("[GpuBatchTimeline] event=submitted batch=%u at_ms=%.3f frame=%llu tile_count=%u cpu_submit_begin_ms=%.3f\n",
BatchIndex,ColdTimelineMilliseconds(Batch.SubmittedAt),
static_cast<unsigned long long>(ColdTimelineFrame()),Batch.TileCount,
ColdTimelineMilliseconds(Batch.SubmitStartedAt));
++GpuFarSubmissionBatches;
GpuFarMaxTilesPerSubmissionBatch=std::max<uint32>(GpuFarMaxTilesPerSubmissionBatch,
static_cast<uint32>(Built));
@@ -4699,6 +4796,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
std::printf("[GpuFarMemory] dxgi_local_sampled_peak_mb=%.1f dxgi_local_budget_mb=%.1f\n",
GpuFarLocalMemoryPeakBytes/(1024.0*1024.0),
GpuFarLocalMemoryBudgetBytes/(1024.0*1024.0));
ReportGpuBatchTimelineSummary();
}
else
{
@@ -4784,7 +4882,26 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
const auto CpuPumpEnd=std::chrono::steady_clock::now();
const bool bDeferGpuUntilCpuFrame=bGpuFarEnabled&&bPresentCpuFrameBeforeGpu
&&!bCpuReadyFramePresentedBeforeGpu;
if(!bDeferGpuUntilCpuFrame)PumpGpuFarGeneration();
if(!bDeferGpuUntilCpuFrame)
{
// Startup holds the last CPU front buffer while the GPU route takes over.
// Allow several ready batches to be submitted in one draw before resuming it.
constexpr double StartupGpuPumpFrameBudgetMilliseconds=80.0;
const auto GpuPumpFrameStart=std::chrono::steady_clock::now();
uint64 PreviousSubmissionCount=GpuFarSubmissionBatches;
for(;;)
{
const double ElapsedGpuPumpMs=std::chrono::duration<double,std::milli>(
std::chrono::steady_clock::now()-GpuPumpFrameStart).count();
const double RemainingGpuPumpMs=StartupGpuPumpFrameBudgetMilliseconds
-ElapsedGpuPumpMs;
if(RemainingGpuPumpMs<=0.0)break;
PumpGpuFarGeneration(RemainingGpuPumpMs);
if(bGpuFarAuditEnabled||bInitialClipmapReported
||GpuFarSubmissionBatches==PreviousSubmissionCount)break;
PreviousSubmissionCount=GpuFarSubmissionBatches;
}
}
const auto GpuPumpEnd=std::chrono::steady_clock::now();
const auto Now=std::chrono::steady_clock::now();
if(FlightTargetCrossings>0&&bFullDetailReported&&!bFlightStarted)