wip(standalone): GPU step 15 -- cold-start drain: hybrid whole view 3.2 -> 1.25-1.68 s

Timeline showed the post-export window was frame-paced: the 4-batch / 2 ms
per-frame upload budget held CPU-fallback tiles up to 755 ms after they
finished, and 35 GPU batches drained one per frame. Before the first whole
view, GPU work now drains as one batch and CPU fallback starts with GPU input
preparation (--gpu-far-no-cold-drain = paced control).

Luna's race: CPU 1.393 s vs GPU 1.677 s. My interleaved runs (Step15Timeline,
RunGpuFarViewer): GPU 2.902 (first launch) / 1.252 / 1.209 s; CPU 2.196 /
2.250 / 2.061 s. The CPU viewer still uses the paced upload budget at cold
start (stream 674 -> 2250 ms in my runs vs 741 -> 1422 in Luna's), so the
race is not yet fair. Audits: hashes, walk 5,642/5,642, 0 diffs vs CPU/WARP.

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 08:47:36 +02:00
1 parent f322b1c089
commit c152708a67
16 files changed
+3378 -86

No files matched your search

+391 -83
View File
@@ -1317,6 +1317,10 @@ namespace
uint8 ActiveSkirtFaceMask=0x3f;
uint64 Samples=0;
double WorkerSeconds=0.0;
std::chrono::steady_clock::time_point CpuQueuedAt{};
std::chrono::steady_clock::time_point CpuStartedAt{};
std::chrono::steady_clock::time_point CpuFinishedAt{};
bool bColdTimelineResidentReported=false;
};
struct FStreamingGpuBatch
{
@@ -1365,17 +1369,25 @@ namespace
uint8 VisibleSkirtFaceMask=0;
bool bSkirtDrawArgsAudited=false;
double GpuDispatchMs=0.0;
std::chrono::steady_clock::time_point PreparedAt{};
std::chrono::steady_clock::time_point SubmittedAt{};
std::chrono::steady_clock::time_point CompletedAt{};
bool bGpuComplete=false;
bool bTimingResolved=false;
bool bColdTimelineResidentReported=false;
};
struct FGpuFarBatch
{
ID3D11Query* CompletionEvent=nullptr;
VoxelForgeGpuFarViewer::GpuTimingQueries TimingQueries{};
uint32 TileCount=0;
uint32 ResidentCount=0;
std::vector<FVoxelTileKey> Keys;
uint64 TimestampFrequency=0;
double GpuDispatchMs=0.0;
std::chrono::steady_clock::time_point PreparedAt{};
std::chrono::steady_clock::time_point SubmittedAt{};
std::chrono::steady_clock::time_point CompletedAt{};
bool bGpuComplete=false,bTimingResolved=false;
};
@@ -1487,6 +1499,7 @@ namespace
bool bShapeCompare=false;
bool bFarShapeStagePending=false;
bool bGpuFarEnabled=false;
bool bDisableGpuColdStartDrain=false;
bool bGpuFarAuditEnabled=false;
bool bGpuFarShaderPrewarm=false,bDisableGpuWarmupPresentHold=false;
bool bGpuFarWarmupResourcesOnly=false;
@@ -1495,6 +1508,10 @@ namespace
bool bGpuFarWarmupOnly=false;
std::string GpuWarmupEntryFilter;
std::thread GpuShaderWarmupThread;
std::thread GpuFarPreparationThread;
std::atomic<bool> bGpuFarPreparationFinished{false};
bool bGpuFarPreparationStarted=false,bGpuFarInputsFinalized=false;
bool bGpuWarmupCompleted=false,bGpuFarTimelineEnabled=false;
bool bGpuShaderWarmupQueueSucceeded=false;
std::string GpuShaderWarmupQueueError;
bool bGpuFarAuditReported=false;
@@ -1529,6 +1546,8 @@ namespace
std::vector<uint32> GpuFarFreeOutputSlots;
std::unordered_map<FVoxelTileKey,VoxelForgeCore::FarFieldTileInput,FStandaloneTileKeyHash> GpuFarPreparedInputs;
std::unordered_map<FVoxelTileKey,std::string,FStandaloneTileKeyHash> GpuFarPreparationErrors;
std::unordered_map<FVoxelTileKey,std::chrono::steady_clock::time_point,FStandaloneTileKeyHash> GpuFarPreparedAt;
std::vector<FVoxelTileKey> GpuFarCandidateTiles,GpuFarCpuFallbackTiles,GpuFarNeedPreparation;
double GpuFarPreparationWallMs=0.0,GpuFarPreparationWorkerMs=0.0;
uint64 GpuFarPumpCalls=0,GpuFarCompletionPolls=0;
double GpuFarFirstPumpMs=-1.0,GpuFarLastPumpMs=-1.0;
@@ -1568,6 +1587,7 @@ namespace
std::string CapturePath;
std::string MeshCachePath;
std::chrono::steady_clock::time_point ColdProcessStart{};
std::atomic<uint64> ColdTimelineFrameOrdinal{0};
std::chrono::steady_clock::time_point GenerationStart{};
bool bShowLevel[MaxLevels+1]={true,true,true,true,true,true,true,true,true};
bool Keys[256]{};
@@ -1595,6 +1615,7 @@ namespace
double BatchUploadBudgetMilliseconds=2.0;
~FApp(){
if(GpuFarPreparationThread.joinable())GpuFarPreparationThread.join();
if(GpuShaderWarmupThread.joinable())GpuShaderWarmupThread.join();
StopGeneration(true);ReleaseMeshes();
if(SolidRasterizer)SolidRasterizer->Release();if(WireRasterizer)WireRasterizer->Release();
@@ -1619,6 +1640,15 @@ namespace
Tile={};
}
double ColdTimelineMilliseconds(std::chrono::steady_clock::time_point At)const
{
return At.time_since_epoch().count()==0?-1.0:
std::chrono::duration<double,std::milli>(At-ColdProcessStart).count();
}
uint64 ColdTimelineFrame()const
{return ColdTimelineFrameOrdinal.load(std::memory_order_relaxed);}
static void ReleaseGpuFarBatch(FGpuFarBatch& Batch)
{
if(Batch.CompletionEvent)Batch.CompletionEvent->Release();
@@ -1654,7 +1684,11 @@ namespace
for(FGpuFarBatch& Batch:GpuFarBatches)ReleaseGpuFarBatch(Batch);
GpuFarBatches.clear();GpuFarQueue.clear();GpuFarQueued.clear();GpuFarEligibleTiles.clear();
GpuFarFreeOutputSlots.clear();GpuFarNextOutputSlot=0;
GpuFarPreparedInputs.clear();GpuFarPreparationErrors.clear();
GpuFarPreparedInputs.clear();GpuFarPreparationErrors.clear();GpuFarPreparedAt.clear();
GpuFarCandidateTiles.clear();GpuFarCpuFallbackTiles.clear();GpuFarNeedPreparation.clear();
bGpuFarPreparationStarted=false;bGpuFarInputsFinalized=false;
bGpuFarPreparationFinished.store(false,std::memory_order_relaxed);
bGpuWarmupCompleted=false;
GpuFarPreparationWallMs=0.0;GpuFarPreparationWorkerMs=0.0;
GpuFarPumpCalls=0;GpuFarCompletionPolls=0;
GpuFarFirstPumpMs=GpuFarLastPumpMs=GpuFarFirstSubmitMs=GpuFarLastSubmitMs=-1.0;
@@ -1683,6 +1717,8 @@ namespace
(void)bCancel;
if(Streamer)Streamer->Stop();
Streamer.reset();
if(GpuFarPreparationThread.joinable())GpuFarPreparationThread.join();
bGpuFarPreparationStarted=false;
if(FullShapeBuildThread.joinable())FullShapeBuildThread.join();
PendingFullShapeWorld.reset();
bFullShapeBuildFinished.store(false,std::memory_order_relaxed);
@@ -2259,14 +2295,17 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
(!bShapeWorld&&MeshCache.IsLoaded())?&MeshCache:nullptr,
bFarShapeStagePending?World->FullResClipLevels-1:-1,
false,false,false,false,ShapeWorld.get());
if(bGpuFarTimelineEnabled)
Streamer->EnableColdTimelineTrace(ColdProcessStart,&ColdTimelineFrameOrdinal);
if(bGpuFarEnabled&&ShapeWorld&&ShapeWorld->hasFarWorld())
{
ScheduleGpuFarTiles(DesiredTiles);
if(bGpuFarAuditEnabled)
{
std::vector<FVoxelTileKey> AuditKeys;AuditKeys.reserve(GpuFarEligibleTiles.size());
std::vector<FVoxelTileKey> AuditKeys;AuditKeys.reserve(GpuFarCandidateTiles.size());
for(const FVoxelTileKey& Key:DesiredTiles)
if(Key.Level>=6&&GpuFarEligibleTiles.contains(Key))AuditKeys.push_back(Key);
if(Key.Level>=6&&std::find(GpuFarCandidateTiles.begin(),GpuFarCandidateTiles.end(),Key)
!=GpuFarCandidateTiles.end())AuditKeys.push_back(Key);
GpuFarAudit=std::make_unique<FFarSurvivalAudit>(*World,*ShapeWorld,AuditKeys,false);
}
}
@@ -2293,6 +2332,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
std::printf("Streaming seed %d%s%s: %d desired tiles, %d full-detail tiles, %d workers; upload budget %d batches / %.1f ms per frame.\n",
Seed,TunnelMode,bShapeWorld?" [shape intervals]":"",Update.DesiredTiles,
Update.FullResTiles,Streamer->GetWorkerCount(),MaxBatchUploadsPerFrame,BatchUploadBudgetMilliseconds);
StartGpuFarPreparation();
if(bShapePipelineActive)
{
std::string ShapeError;
@@ -2316,6 +2356,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
void StartFarShapeGeneration()
{
if(!bFarShapeStagePending||!ShapeWorld||ShapeWorld->hasFarWorld()||!Streamer)return;
if(GpuFarPreparationThread.joinable())GpuFarPreparationThread.join();
ShapeWorld->buildFarWorld();
bFarShapeStagePending=false;
Streamer->Stop();
@@ -2324,14 +2365,17 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
Streamer.reset();
Streamer=std::make_unique<FStandaloneStreamingGenerator>(*World,WorkerCount,false,nullptr,-1,
false,false,false,false,ShapeWorld.get());
if(bGpuFarTimelineEnabled)
Streamer->EnableColdTimelineTrace(ColdProcessStart,&ColdTimelineFrameOrdinal);
if(bGpuFarEnabled)
{
ScheduleGpuFarTiles(DesiredTiles);
if(bGpuFarAuditEnabled&&World)
{
std::vector<FVoxelTileKey> AuditKeys;AuditKeys.reserve(GpuFarEligibleTiles.size());
std::vector<FVoxelTileKey> AuditKeys;AuditKeys.reserve(GpuFarCandidateTiles.size());
for(const FVoxelTileKey& Key:DesiredTiles)
if(Key.Level>=6&&GpuFarEligibleTiles.contains(Key))AuditKeys.push_back(Key);
if(Key.Level>=6&&std::find(GpuFarCandidateTiles.begin(),GpuFarCandidateTiles.end(),Key)
!=GpuFarCandidateTiles.end())AuditKeys.push_back(Key);
GpuFarAudit=std::make_unique<FFarSurvivalAudit>(*World,*ShapeWorld,AuditKeys,false);
}
}
@@ -2344,6 +2388,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
bGenerationRunning=true;
std::printf("[FarStreamStart] desired=%d retained=%d new_jobs=%d workers=%d\n",
Update.DesiredTiles,Update.RetainedResidentTiles,Update.NewJobs,WorkerCount);
StartGpuFarPreparation();
std::fflush(stdout);
}
@@ -2359,6 +2404,46 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
}
}
void ReportGpuTileCompletion(const FVoxelTileKey& Key,FGpuFarTile& Tile,
uint32 BatchIndex=UINT32_MAX)
{
if(Tile.CompletedAt.time_since_epoch().count()!=0)return;
Tile.CompletedAt=std::chrono::steady_clock::now();
if(!bGpuFarTimelineEnabled)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),
static_cast<unsigned long long>(ColdTimelineFrame()),
ColdTimelineMilliseconds(Tile.SubmittedAt));
std::printf("[GpuTileTimeline] event=uploaded key=(%d,%d,%d,lod%d) batch=%u at_ms=%.3f frame=%llu method=direct_gpu_compute_output copy_upload=none\n",
Key.Coord.X,Key.Coord.Y,Key.Coord.Z,Key.Level,BatchIndex,
ColdTimelineMilliseconds(Tile.CompletedAt),
static_cast<unsigned long long>(ColdTimelineFrame()));
}
void ReportGpuTileResident(const FVoxelTileKey& Key,FGpuFarTile& Tile)
{
if(Tile.bColdTimelineResidentReported)return;
Tile.bColdTimelineResidentReported=true;
const auto ResidentAt=std::chrono::steady_clock::now();
if(bGpuFarTimelineEnabled)
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),
static_cast<unsigned long long>(ColdTimelineFrame()),
ColdTimelineMilliseconds(Tile.SubmittedAt),ColdTimelineMilliseconds(Tile.CompletedAt));
if(Tile.BatchIndex!=UINT32_MAX&&Tile.BatchIndex<GpuFarBatches.size())
{
FGpuFarBatch& Batch=GpuFarBatches[Tile.BatchIndex];
++Batch.ResidentCount;
if(bGpuFarTimelineEnabled&&Batch.ResidentCount==Batch.TileCount)
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));
}
}
bool IsRenderedTileReady(const FVoxelTileKey& Key)
{
if(bGpuFarEnabled&&ShapeWorld&&ShapeWorld->hasFarWorld()
@@ -2375,10 +2460,30 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
{
Batch.bGpuComplete=true;
const auto PollAt=std::chrono::steady_clock::now();
Batch.CompletedAt=PollAt;
const double PollMs=std::chrono::duration<double,std::milli>(PollAt-ColdProcessStart).count();
if(GpuFarFirstCompletionPollMs<0.0)GpuFarFirstCompletionPollMs=PollMs;
GpuFarLastCompletionPollMs=PollMs;++GpuFarCompletionPolls;
Batch.CompletionEvent->Release();Batch.CompletionEvent=nullptr;
if(bGpuFarTimelineEnabled)
{
std::printf("[GpuBatchTimeline] event=completed batch=%u at_ms=%.3f frame=%llu tile_count=%u submitted_ms=%.3f\n",
Tile.BatchIndex,ColdTimelineMilliseconds(PollAt),
static_cast<unsigned long long>(ColdTimelineFrame()),Batch.TileCount,
ColdTimelineMilliseconds(Batch.SubmittedAt));
std::printf("[GpuBatchTimeline] event=uploaded batch=%u at_ms=%.3f frame=%llu method=direct_gpu_compute_output copy_upload=none\n",
Tile.BatchIndex,ColdTimelineMilliseconds(PollAt),
static_cast<unsigned long long>(ColdTimelineFrame()));
}
for(const FVoxelTileKey& BatchKey:Batch.Keys)
{
auto BatchTileIt=GpuFarTiles.find(BatchKey);
if(BatchTileIt!=GpuFarTiles.end())
{
BatchTileIt->second.bGpuComplete=true;
ReportGpuTileCompletion(BatchKey,BatchTileIt->second,Tile.BatchIndex);
}
}
}
if(Batch.bGpuComplete&&!Batch.bTimingResolved)
{
@@ -2402,7 +2507,11 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
GpuFarGpuDispatchMaxMs=std::max(GpuFarGpuDispatchMaxMs,TileDispatchMs);
}
}
if(Batch.bGpuComplete)RecordGpuCrossingTileReady(Key);
if(Batch.bGpuComplete)
{
RecordGpuCrossingTileReady(Key);
ReportGpuTileResident(Key,Tile);
}
return Batch.bGpuComplete;
}
if(!Tile.bGpuComplete&&Tile.CompletionEvent&&Context
@@ -2410,6 +2519,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
{
Tile.bGpuComplete=true;
Tile.CompletionEvent->Release();Tile.CompletionEvent=nullptr;
ReportGpuTileCompletion(Key,Tile);
}
if(Tile.bGpuComplete&&!Tile.bTimingResolved)
{
@@ -2425,7 +2535,11 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
Tile.bTimingResolved=true;
}
}
if(Tile.bGpuComplete)RecordGpuCrossingTileReady(Key);
if(Tile.bGpuComplete)
{
RecordGpuCrossingTileReady(Key);
ReportGpuTileResident(Key,Tile);
}
return Tile.bGpuComplete;
}
return ResidentTiles.contains(Key);
@@ -2436,9 +2550,11 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
if(NeedPreparation.empty())return;
const auto PrepareStart=std::chrono::steady_clock::now();
std::printf("[ColdTimeline] stage=gpu_input_export begin_ms=%.3f tiles=%zu\n",
std::chrono::duration<double,std::milli>(PrepareStart-ColdProcessStart).count(),NeedPreparation.size());
ColdTimelineMilliseconds(PrepareStart),NeedPreparation.size());
std::vector<std::optional<VoxelForgeCore::FarFieldTileInput>> Inputs(NeedPreparation.size());
std::vector<std::string> Errors(NeedPreparation.size());
std::vector<std::chrono::steady_clock::time_point> FinishedAt(NeedPreparation.size());
std::vector<uint64> FinishedFrame(NeedPreparation.size());
const std::size_t ThreadCount=std::min<std::size_t>(8,NeedPreparation.size());
std::atomic<std::size_t> Next{0};
std::vector<double> WorkerElapsed(ThreadCount,0.0);
@@ -2475,6 +2591,8 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
}
}
catch(const std::exception& Error) { Errors[Index]=Error.what(); }
FinishedAt[Index]=std::chrono::steady_clock::now();
FinishedFrame[Index]=ColdTimelineFrame();
}
WorkerElapsed[Worker]=std::chrono::duration<double,std::milli>(
std::chrono::steady_clock::now()-WorkerStart).count();
@@ -2488,7 +2606,17 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
std::size_t Failed=0;
for(std::size_t Index=0;Index<NeedPreparation.size();++Index)
{
if(Inputs[Index])GpuFarPreparedInputs.emplace(NeedPreparation[Index],std::move(*Inputs[Index]));
if(Inputs[Index])
{
GpuFarPreparedInputs.emplace(NeedPreparation[Index],std::move(*Inputs[Index]));
GpuFarPreparedAt.emplace(NeedPreparation[Index],FinishedAt[Index]);
const FVoxelTileKey& Key=NeedPreparation[Index];
if(bGpuFarTimelineEnabled)
std::printf("[GpuTileTimeline] event=prepared key=(%d,%d,%d,lod%d) at_ms=%.3f frame=%llu\n",
Key.Coord.X,Key.Coord.Y,Key.Coord.Z,Key.Level,
ColdTimelineMilliseconds(FinishedAt[Index]),
static_cast<unsigned long long>(FinishedFrame[Index]));
}
else
{
++Failed;
@@ -2499,7 +2627,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
std::printf("[GpuFarInputPrepare] tiles=%zu workers=%zu wall_ms=%.3f worker_sum_ms=%.3f failed=%zu\n",
NeedPreparation.size(),ThreadCount,PreparationMs,WorkerSumMs,Failed);
std::printf("[ColdTimeline] stage=gpu_input_export end_ms=%.3f duration_ms=%.3f worker_sum_ms=%.3f\n",
std::chrono::duration<double,std::milli>(std::chrono::steady_clock::now()-ColdProcessStart).count(),
ColdTimelineMilliseconds(std::chrono::steady_clock::now()),
PreparationMs,WorkerSumMs);
std::fflush(stdout);
}
@@ -2507,27 +2635,11 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
void ScheduleGpuFarTiles(const std::vector<FVoxelTileKey>& Keys)
{
GpuFarQueue.clear();GpuFarQueued.clear();GpuFarEligibleTiles.clear();
GpuFarPreparedInputs.clear();GpuFarPreparationErrors.clear();
GpuFarPreparedInputs.clear();GpuFarPreparationErrors.clear();GpuFarPreparedAt.clear();
GpuFarCandidateTiles.clear();GpuFarCpuFallbackTiles.clear();GpuFarNeedPreparation.clear();
bGpuFarPreparationStarted=false;bGpuFarInputsFinalized=false;
bGpuFarPreparationFinished.store(false,std::memory_order_relaxed);
if(!bGpuFarEnabled||!ShapeWorld||!ShapeWorld->hasFarWorld())return;
if(bGpuFarShaderPrewarm)
{
if(GpuShaderWarmupThread.joinable())GpuShaderWarmupThread.join();
if(!bGpuShaderWarmupQueueSucceeded)
{
std::fprintf(stderr,"[GpuShaderWarmupError] queue: %s\n",
GpuShaderWarmupQueueError.empty()?"warmup queue failed":GpuShaderWarmupQueueError.c_str());
bGpuFarEnabled=false;
return;
}
std::string WarmupError;
if(!VoxelForgeGpuFarViewer::CompleteFarShaderWarmup(Device,Context,
GpuFarShaderPath,WarmupError))
{
std::fprintf(stderr,"[GpuShaderWarmupError] %s\n",WarmupError.c_str());
bGpuFarEnabled=false;
return;
}
}
std::vector<FVoxelTileKey> CpuTiles;CpuTiles.reserve(Keys.size());
std::vector<FVoxelTileKey> Eligible;Eligible.reserve(Keys.size());
uint64 SkirtTransitionFallbackTiles=0;
@@ -2606,49 +2718,102 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
std::printf("[CaptureOutputSlotProbe] slot=%d pool_slots=%u\n",
CaptureOutputSlot,GpuFarOutputCapacitySlots);
}
if(!Eligible.empty())
GpuFarCandidateTiles=std::move(Eligible);
GpuFarCpuFallbackTiles=std::move(CpuTiles);
GpuFarNeedPreparation.reserve(GpuFarCandidateTiles.size());
for(const FVoxelTileKey& Key:GpuFarCandidateTiles)
if(!GpuFarTiles.contains(Key))GpuFarNeedPreparation.push_back(Key);
if(Streamer)Streamer->SetDesiredTileFilter(GpuFarCpuFallbackTiles);
const std::size_t NearGpuTiles=static_cast<std::size_t>(std::count_if(
GpuFarCandidateTiles.begin(),GpuFarCandidateTiles.end(),[&](const FVoxelTileKey& Key)
{return Key.Level<World->FullResClipLevels;}));
std::printf("[GpuFieldEligibility] candidate_gpu_tiles=%zu near_gpu_tiles=%zu cpu_fallback_tiles=%zu\n",
GpuFarCandidateTiles.size(),NearGpuTiles,GpuFarCpuFallbackTiles.size());
std::printf("[GpuSkirtSeamFallback] near_tiles=%llu reason=fine_visible_lod_skirts gpu_far_skirts=active\n",
static_cast<unsigned long long>(SkirtTransitionFallbackTiles));
}
void AddGpuFarCpuFallbackTiles(const std::vector<FVoxelTileKey>& Additional)
{
if(Additional.empty()||!Streamer)return;
std::unordered_set<FVoxelTileKey,FStandaloneTileKeyHash> Seen(
GpuFarCpuFallbackTiles.begin(),GpuFarCpuFallbackTiles.end());
std::size_t Added=0;
for(const FVoxelTileKey& Key:Additional)
if(Seen.insert(Key).second){GpuFarCpuFallbackTiles.push_back(Key);++Added;}
if(Added==0)return;
Streamer->SetDesiredTileFilter(GpuFarCpuFallbackTiles);
const FVector CameraVoxels(Position.X*4.0,Position.Y*4.0,Position.Z*4.0);
std::vector<FVoxelTileKey> ResidentKeys;ResidentKeys.reserve(ResidentTiles.size());
for(const auto& Pair:ResidentTiles)ResidentKeys.push_back(Pair.first);
const FStandaloneStreamingUpdate Update=Streamer->UpdateDesired(
CameraVoxels,ResidentKeys,CrossingIndex);
FCrossingWork& Work=CrossingWork[CrossingIndex];
Work.SubmittedJobs+=Update.NewJobs;Work.PendingJobs+=Update.NewJobs;
std::printf("[GpuFarPrepFallbackAdded] tiles=%zu queued_jobs=%d cpu_fallback_total=%zu\n",
Added,Update.NewJobs,GpuFarCpuFallbackTiles.size());
}
void StartGpuFarPreparation()
{
if(!bGpuFarEnabled||!ShapeWorld||!ShapeWorld->hasFarWorld()||bGpuFarPreparationStarted)return;
bGpuFarPreparationStarted=true;
if(GpuFarCandidateTiles.empty())
{
std::string PoolError;const auto ReserveStart=std::chrono::steady_clock::now();
std::printf("[ColdTimeline] stage=gpu_output_pool begin_ms=%.3f slots=%u\n",
std::chrono::duration<double,std::milli>(ReserveStart-ColdProcessStart).count(),
GpuFarOutputCapacitySlots);
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;
CpuTiles=Keys;Eligible.clear();GpuFarTiles.clear();
}
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",
std::chrono::duration<double,std::milli>(std::chrono::steady_clock::now()-ColdProcessStart).count(),
GpuFarPoolReserveMs);
SampleGpuFarMemory();
bGpuFarInputsFinalized=true;
bGpuFarPreparationFinished.store(true,std::memory_order_relaxed);
return;
}
std::vector<FVoxelTileKey> NeedPreparation;NeedPreparation.reserve(Eligible.size());
for(const FVoxelTileKey& Key:Eligible)
if(!GpuFarTiles.contains(Key))NeedPreparation.push_back(Key);
if(!NeedPreparation.empty())
PrepareGpuFarInputs(std::move(NeedPreparation));
std::vector<FVoxelTileKey> PreparedEligible;PreparedEligible.reserve(Eligible.size());
for(const FVoxelTileKey& Key:Eligible)
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))
{
if(!GpuFarTiles.contains(Key)&&!GpuFarPreparedInputs.contains(Key))
{
CpuTiles.push_back(Key);
const auto Error=GpuFarPreparationErrors.find(Key);
std::fprintf(stderr,"[GpuFieldInputFallback] key=(%d,%d,%d,lod%d) reason=%s\n",
Key.Coord.X,Key.Coord.Y,Key.Coord.Z,Key.Level,
Error==GpuFarPreparationErrors.end()?"missing prepared input":Error->second.c_str());
continue;
}
GpuFarEligibleTiles.insert(Key);
if(!GpuFarTiles.contains(Key))
{GpuFarQueue.push_back(Key);GpuFarQueued.insert(Key);}
PreparedEligible.push_back(Key);
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;
}
Eligible.swap(PreparedEligible);
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);
SampleGpuFarMemory();
if(GpuFarNeedPreparation.empty())
{
bGpuFarPreparationFinished.store(true,std::memory_order_relaxed);
return;
}
bGpuFarPreparationFinished.store(false,std::memory_order_relaxed);
const std::vector<FVoxelTileKey> NeedPreparation=GpuFarNeedPreparation;
GpuFarPreparationThread=std::thread([this,NeedPreparation]()
{
try { PrepareGpuFarInputs(NeedPreparation); }
catch(const std::exception& Error)
{
for(const FVoxelTileKey& Key:NeedPreparation)
GpuFarPreparationErrors.emplace(Key,Error.what());
std::fprintf(stderr,"[GpuFarInputPrepareError] %s\n",Error.what());
}
catch(...)
{
for(const FVoxelTileKey& Key:NeedPreparation)
GpuFarPreparationErrors.emplace(Key,"unknown input preparation failure");
std::fprintf(stderr,"[GpuFarInputPrepareError] unknown failure\n");
}
bGpuFarPreparationFinished.store(true,std::memory_order_release);
});
}
void SortGpuFarQueue()
{
std::sort(GpuFarQueue.begin(),GpuFarQueue.end(),[&](const FVoxelTileKey& A,const FVoxelTileKey& B)
{
if(A.Level!=B.Level)return A.Level<B.Level;
@@ -2662,20 +2827,79 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
return AC<BC;
});
GpuFarQueued.clear();for(const FVoxelTileKey& Key:GpuFarQueue)GpuFarQueued.insert(Key);
if(Streamer)Streamer->SetDesiredTileFilter(CpuTiles);
}
bool FinalizeGpuFarPreparation()
{
if(bGpuFarInputsFinalized)return true;
if(!bGpuFarPreparationStarted
||!bGpuFarPreparationFinished.load(std::memory_order_acquire))return false;
if(GpuFarPreparationThread.joinable())GpuFarPreparationThread.join();
if(bGpuFarShaderPrewarm&&!bGpuWarmupCompleted)
{
if(GpuShaderWarmupThread.joinable())GpuShaderWarmupThread.join();
if(!bGpuShaderWarmupQueueSucceeded)
{
std::fprintf(stderr,"[GpuShaderWarmupError] queue: %s\n",
GpuShaderWarmupQueueError.empty()?"warmup queue failed":GpuShaderWarmupQueueError.c_str());
bGpuFarEnabled=false;
}
else
{
std::string WarmupError;
if(!VoxelForgeGpuFarViewer::CompleteFarShaderWarmup(Device,Context,
GpuFarShaderPath,WarmupError))
{
std::fprintf(stderr,"[GpuShaderWarmupError] %s\n",WarmupError.c_str());
bGpuFarEnabled=false;
}
else bGpuWarmupCompleted=true;
}
}
if(!bGpuFarEnabled)
{
GpuFarCandidateTiles.clear();GpuFarQueue.clear();GpuFarQueued.clear();
GpuFarEligibleTiles.clear();
for(auto& Pair:GpuFarTiles)ReleaseGpuFarTile(Pair.second);
GpuFarTiles.clear();
AddGpuFarCpuFallbackTiles(DesiredTiles);
bGpuFarInputsFinalized=true;
return false;
}
std::vector<FVoxelTileKey> Failed;
for(const FVoxelTileKey& Key:GpuFarCandidateTiles)
{
if(GpuFarTiles.contains(Key))
{GpuFarEligibleTiles.insert(Key);continue;}
if(!GpuFarPreparedInputs.contains(Key))
{
Failed.push_back(Key);
const auto Error=GpuFarPreparationErrors.find(Key);
std::fprintf(stderr,"[GpuFieldInputFallback] key=(%d,%d,%d,lod%d) reason=%s\n",
Key.Coord.X,Key.Coord.Y,Key.Coord.Z,Key.Level,
Error==GpuFarPreparationErrors.end()?"missing prepared input":Error->second.c_str());
continue;
}
GpuFarEligibleTiles.insert(Key);
GpuFarQueue.push_back(Key);
}
SortGpuFarQueue();
AddGpuFarCpuFallbackTiles(Failed);
bGpuFarInputsFinalized=true;
const std::size_t NearGpuTiles=static_cast<std::size_t>(std::count_if(
GpuFarEligibleTiles.begin(),GpuFarEligibleTiles.end(),[&](const FVoxelTileKey& Key)
{return Key.Level<World->FullResClipLevels;}));
std::printf("[GpuFieldEligibility] eligible_gpu_tiles=%zu near_gpu_tiles=%zu cpu_fallback_tiles=%zu\n",
GpuFarEligibleTiles.size(),NearGpuTiles,Keys.size()-GpuFarEligibleTiles.size());
std::printf("[GpuSkirtSeamFallback] near_tiles=%llu reason=fine_visible_lod_skirts gpu_far_skirts=active\n",
static_cast<unsigned long long>(SkirtTransitionFallbackTiles));
GpuFarEligibleTiles.size(),NearGpuTiles,GpuFarCpuFallbackTiles.size());
return true;
}
void PumpGpuFarGeneration()
{
if(!bGpuFarEnabled||!ShapeWorld||!ShapeWorld->hasFarWorld()||!Device||!Context
||GpuFarQueue.empty())return;
||!FinalizeGpuFarPreparation()||GpuFarQueue.empty())return;
const bool bColdDrainBeforeWholeView=!bInitialClipmapReported&&!bGpuFarAuditEnabled
&&!bDisableGpuColdStartDrain;
const auto PumpStart=std::chrono::steady_clock::now();
const double PumpMs=std::chrono::duration<double,std::milli>(PumpStart-ColdProcessStart).count();
if(GpuFarFirstPumpMs<0.0)GpuFarFirstPumpMs=PumpMs;
@@ -2697,7 +2921,8 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
}
const auto Start=std::chrono::steady_clock::now();
int32 Built=0;
std::vector<FVoxelTileKey> BuiltKeys;BuiltKeys.reserve(32);
std::vector<FVoxelTileKey> BuiltKeys;
BuiltKeys.reserve(bColdDrainBeforeWholeView?GpuFarQueue.size():32);
do
{
const FVoxelTileKey Key=GpuFarQueue.front();GpuFarQueue.pop_front();GpuFarQueued.erase(Key);
@@ -2762,6 +2987,9 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
Tile.VertexCapacityBytes=static_cast<uint64>(
VoxelForgeGpuFarViewer::PooledOutputVertexCapacityPerTile)*32u;
Tile.VisibleSkirtFaceMask=PreparedSkirtFaceMask;
const auto PreparedAtIt=GpuFarPreparedAt.find(Key);
if(PreparedAtIt!=GpuFarPreparedAt.end())Tile.PreparedAt=PreparedAtIt->second;
Tile.SubmittedAt=std::chrono::steady_clock::now();
Tile.GpuDispatchMs=GpuDispatchMs;
GpuFarEnqueueWallSeconds+=EnqueueMs/1000.0;
if(GpuDispatchMs>0.0)
@@ -2773,6 +3001,12 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
++GpuFarTilesSubmitted;++Built;
GpuFarTiles.emplace(Key,Tile);
BuiltKeys.push_back(Key);
if(!bDeferredBatch&&bGpuFarTimelineEnabled)
std::printf("[GpuTileTimeline] event=submitted key=(%d,%d,%d,lod%d) batch=none at_ms=%.3f frame=%llu prepared_ms=%.3f path=direct_dispatch\n",
Key.Coord.X,Key.Coord.Y,Key.Coord.Z,Key.Level,
ColdTimelineMilliseconds(Tile.SubmittedAt),
static_cast<unsigned long long>(ColdTimelineFrame()),
ColdTimelineMilliseconds(Tile.PreparedAt));
if(!bGpuFarFirstTileReported)
{
bGpuFarFirstTileReported=true;
@@ -2782,13 +3016,21 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
std::fflush(stdout);
}
}
while(Built<32&&!GpuFarQueue.empty()
&&std::chrono::duration<double,std::milli>(std::chrono::steady_clock::now()-Start).count()<4.0);
while(!GpuFarQueue.empty()&&(bColdDrainBeforeWholeView
||(Built<32&&std::chrono::duration<double,std::milli>(
std::chrono::steady_clock::now()-Start).count()<4.0)));
if(Built>0)
{
if(bDeferredBatch)
{
FGpuFarBatch Batch;Batch.TileCount=static_cast<uint32>(Built);
Batch.PreparedAt=std::chrono::steady_clock::now();
Batch.Keys=BuiltKeys;
const uint32 BatchIndex=static_cast<uint32>(GpuFarBatches.size());
if(bGpuFarTimelineEnabled)
std::printf("[GpuBatchTimeline] event=prepared batch=%u at_ms=%.3f frame=%llu tile_count=%u path=direct_gpu_output\n",
BatchIndex,ColdTimelineMilliseconds(Batch.PreparedAt),
static_cast<unsigned long long>(ColdTimelineFrame()),Batch.TileCount);
std::string BatchError;
if(!VoxelForgeGpuFarViewer::SubmitFarTileBatch(Device,Context,GpuFarShaderPath,
&Batch.CompletionEvent,&Batch.TimingQueries,BatchError))
@@ -2803,13 +3045,24 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
Batch.SubmittedAt-ColdProcessStart).count();
if(GpuFarFirstSubmitMs<0.0)GpuFarFirstSubmitMs=SubmitMs;
GpuFarLastSubmitMs=SubmitMs;
const uint32 BatchIndex=static_cast<uint32>(GpuFarBatches.size());
GpuFarBatches.push_back(Batch);
for(const FVoxelTileKey& Key:BuiltKeys)
{
auto It=GpuFarTiles.find(Key);
if(It!=GpuFarTiles.end())It->second.BatchIndex=BatchIndex;
if(It!=GpuFarTiles.end())
{
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",
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),
static_cast<unsigned long long>(ColdTimelineFrame()),Batch.TileCount);
++GpuFarSubmissionBatches;
GpuFarMaxTilesPerSubmissionBatch=std::max<uint32>(GpuFarMaxTilesPerSubmissionBatch,
static_cast<uint32>(Built));
@@ -3219,14 +3472,46 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
};
std::sort(Dirty.begin(),Dirty.end(),[&](const auto& A,const auto& B)
{const double DA=Distance(A),DB=Distance(B);return DA==DB?A.Level<B.Level:DA<DB;});
const bool bDrainColdStart=bGpuFarEnabled&&!bInitialClipmapReported
&&!bDisableGpuColdStartDrain;
const auto Start=std::chrono::steady_clock::now();int32 Uploaded=0;
for(const FStandaloneBatchKey& Key:Dirty)
{
if(Uploaded>=MaxBatchUploadsPerFrame)break;
if(Uploaded>0&&BatchUploadBudgetMilliseconds>0.0
if(!bDrainColdStart&&Uploaded>=MaxBatchUploadsPerFrame)break;
if(!bDrainColdStart&&Uploaded>0&&BatchUploadBudgetMilliseconds>0.0
&&std::chrono::duration<double,std::milli>(std::chrono::steady_clock::now()-Start).count()>=BatchUploadBudgetMilliseconds)break;
if(!DirtyBatchSet.contains(Key))continue;
if(RebuildBatch(Key)){DirtyBatchSet.erase(Key);++Uploaded;}
if(RebuildBatch(Key))
{
DirtyBatchSet.erase(Key);++Uploaded;
if(bGpuFarTimelineEnabled)
{
const auto UploadAt=std::chrono::steady_clock::now();
const bool bHasUploadBuffer=StreamingBatches.contains(Key);
std::printf("[CpuUploadBatchTimeline] event=uploaded batch=(lod%d,%d,%d,%d) at_ms=%.3f frame=%llu has_buffer=%s\n",
Key.Level,Key.X,Key.Y,Key.Z,ColdTimelineMilliseconds(UploadAt),
static_cast<unsigned long long>(ColdTimelineFrame()),
bHasUploadBuffer?"yes":"no_empty_mesh");
const auto MembersIt=BatchMembers.find(Key);
if(MembersIt!=BatchMembers.end())for(const FVoxelTileKey& TileKey:MembersIt->second)
{
auto ResidentIt=ResidentTiles.find(TileKey);
if(ResidentIt==ResidentTiles.end()||ResidentIt->second.bColdTimelineResidentReported)continue;
FResidentTileMesh& Resident=ResidentIt->second;
Resident.bColdTimelineResidentReported=true;
std::printf("[CpuTileTimeline] event=uploaded key=(%d,%d,%d,lod%d) at_ms=%.3f frame=%llu batch=(lod%d,%d,%d,%d) queued_ms=%.3f started_ms=%.3f finished_ms=%.3f method=%s\n",
TileKey.Coord.X,TileKey.Coord.Y,TileKey.Coord.Z,TileKey.Level,
ColdTimelineMilliseconds(UploadAt),static_cast<unsigned long long>(ColdTimelineFrame()),
Key.Level,Key.X,Key.Y,Key.Z,ColdTimelineMilliseconds(Resident.CpuQueuedAt),
ColdTimelineMilliseconds(Resident.CpuStartedAt),ColdTimelineMilliseconds(Resident.CpuFinishedAt),
bHasUploadBuffer?"cpu_batch_vertex_index":"empty_surface_no_gpu_copy");
std::printf("[CpuTileTimeline] event=resident key=(%d,%d,%d,lod%d) at_ms=%.3f frame=%llu batch=(lod%d,%d,%d,%d)\n",
TileKey.Coord.X,TileKey.Coord.Y,TileKey.Coord.Z,TileKey.Level,
ColdTimelineMilliseconds(UploadAt),static_cast<unsigned long long>(ColdTimelineFrame()),
Key.Level,Key.X,Key.Y,Key.Z);
}
}
}
}
}
@@ -3346,6 +3631,8 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
Resident.TriangleSkirtFaceMasks=std::move(Tile.TriangleSkirtFaceMasks);
Resident.ActiveSkirtFaceMask=ComputeVisibleSkirtFaceMask(Tile.Key,DesiredTileSet);
Resident.Samples=Tile.DensitySamples;Resident.WorkerSeconds=Tile.WorkerSeconds;
Resident.CpuQueuedAt=Tile.QueuedAt;Resident.CpuStartedAt=Tile.StartedAt;
Resident.CpuFinishedAt=Tile.CompletedAt;
ResidentTiles.emplace(Tile.Key,std::move(Resident));
const FStandaloneBatchKey Batch=BatchForTile(Tile.Key);
BatchMembers[Batch].push_back(Tile.Key);MarkBatchDirty(Batch);
@@ -3534,7 +3821,14 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
void Draw()
{
const uint64 FrameOrdinal=ColdTimelineFrameOrdinal.fetch_add(1,std::memory_order_relaxed)+1;
const auto DrawStart=std::chrono::steady_clock::now();
const bool bTraceColdFrame=bGpuFarTimelineEnabled&&!bInitialClipmapReported;
if(bTraceColdFrame)
std::printf("[ColdFrame] stage=begin frame=%llu at_ms=%.3f inter_draw_gap_ms=%.3f\n",
static_cast<unsigned long long>(FrameOrdinal),ColdTimelineMilliseconds(DrawStart),
LastDrawEndAt.time_since_epoch().count()!=0
?std::chrono::duration<double,std::milli>(DrawStart-LastDrawEndAt).count():0.0);
const double InterDrawGapMs=LastDrawEndAt.time_since_epoch().count()!=0
?std::chrono::duration<double,std::milli>(DrawStart-LastDrawEndAt).count():0.0;
PumpGeneration();
@@ -3706,8 +4000,14 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
++GpuWarmupHeldPresents;
// Keep pumping CPU and GPU work at the display cadence without
// calling the driver present path that stalls on its first GPU frame.
std::this_thread::sleep_for(std::chrono::milliseconds(11));
std::this_thread::sleep_for(std::chrono::milliseconds(
bGpuFarEnabled&&!bDisableGpuColdStartDrain?1:11));
LastDrawEndAt=std::chrono::steady_clock::now();
if(bTraceColdFrame)
std::printf("[ColdFrame] stage=end frame=%llu at_ms=%.3f cpu_pump_ms=%.3f gpu_pump_ms=%.3f held=1 present_ms=0.000\n",
static_cast<unsigned long long>(FrameOrdinal),ColdTimelineMilliseconds(LastDrawEndAt),
std::chrono::duration<double,std::milli>(CpuPumpEnd-DrawStart).count(),
std::chrono::duration<double,std::milli>(GpuPumpEnd-CpuPumpEnd).count());
return;
}
const auto BeforePresent=std::chrono::steady_clock::now();
@@ -3786,6 +4086,12 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
InterDrawGapMs,CpuPumpMs,GpuPumpMs,
std::chrono::duration<double,std::milli>(BeforePresent-GpuPumpEnd).count(),
PresentSyncInterval,PresentCallMs,TitleMs);
if(bTraceColdFrame)
std::printf("[ColdFrame] stage=end frame=%llu at_ms=%.3f cpu_pump_ms=%.3f gpu_pump_ms=%.3f pre_present_ms=%.3f present_ms=%.3f present_sync=%u present_hr=0x%08lx held=0\n",
static_cast<unsigned long long>(FrameOrdinal),ColdTimelineMilliseconds(DrawEnd),
CpuPumpMs,GpuPumpMs,
std::chrono::duration<double,std::milli>(BeforePresent-GpuPumpEnd).count(),
PresentCallMs,PresentSyncInterval,static_cast<unsigned long>(PresentResult));
LastDrawEndAt=DrawEnd;
}
};
@@ -5632,6 +5938,8 @@ int main(int Argc,char** Argv)
else if(Arg=="--affinity-all-cpu"){}
else if(Arg=="--gpu-far-no-present-hold")
App.bDisableGpuWarmupPresentHold=true;
else if(Arg=="--gpu-far-no-cold-drain")App.bDisableGpuColdStartDrain=true;
else if(Arg=="--gpu-far-timeline")App.bGpuFarTimelineEnabled=true;
else if(Arg=="--gpu-far-no-prewarm")
App.bGpuFarShaderPrewarm=false;
else if(Arg=="--cpu-far")App.bGpuFarEnabled=false,App.bShapeWorld=true,App.bForceGenerate=true;