wip(standalone): GPU step 25 -- shape profile, roughness endpoint reuse, export before CPU frame; first launch REGRESSED

Luna: shape_compile profiled (8 workers, detail band ~323 ms of ~545 ms over 9.39M
candidates; roughness dominates). Exact vertical roughness endpoint reuse in
ShapeWorldInternal.h, hashes unchanged: ~548-577 -> ~535-548 ms. Overlapping
pipeline/pool with shape or streaming made things slower, so it stays serial (pool
precompile now 4-20 ms). Export starts before the first CPU frame. Marker-miss stall
reproduced and gated differently; the forced-cold survival audit now hits
DXGI 0x887A0005 (device removed) on a timestamp query and is NOT passing. Auto
survival passes (6.16 GB). The vertex count at InitialClipmapReady counts CPU
batches only (GPU-resident tiles excluded) -- confirmed counting, not missing geometry.

My runs outside the sandbox (Step25.exe, marker removed first, interleaved, -Timeline):
  CPU 1.329 / 1.295 / 1.318 s
  GPU cached 1.152 / 1.170 / 1.137 s, all 1,434 GPU tiles (timestamps 292 / 260 / 356 ms,
      max dispatch 19.0 / 15.9 / 75.2 ms)
  GPU marker miss 2.015 s -- REGRESSION vs step 24's 1.336 s. CPU had all 2,226 tiles by
      ~1.3 s, but near_all_tiles_ready waited for the post-view shader warmup
      (queue 1,025 ms, finished 1,955 ms; handoff cpu_started=1434 takeover=0).
Audits: hashes w1/w8, walk 5,642/5,642, near 960 / far 1,114 0 diffs vs CPU and
WARP, thin 353, ten captures 2+2 edge pixels as before.

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 02:55:32 +02:00
1 parent 94d50edb47
commit 20acf57678
9 files changed
+380 -20

No files matched your search

+184 -8
View File
@@ -1469,6 +1469,7 @@ namespace
std::once_flag StartOnce;
std::chrono::steady_clock::time_point DispatchAt{};
std::chrono::steady_clock::time_point PrepareStart{};
std::chrono::steady_clock::time_point CpuReadyAt{};
std::atomic<std::size_t> Remaining{0};
std::atomic<std::size_t> Failed{0};
std::atomic<uint64> WorkerNanoseconds{0};
@@ -1542,6 +1543,10 @@ namespace
bool bFarShapeStagePending=false;
bool bGpuFarEnabled=false;
bool bGpuFarPipelineInitialized=false;
std::thread GpuPipelineStartupThread;
bool bGpuPipelineStartupAttempted=false,bGpuPipelineStartupSucceeded=false;
bool bGpuPipelineStartupWasDeferred=false;
std::string GpuPipelineStartupError;
bool bDisableGpuColdStartDrain=false;
bool bGpuFarAuditEnabled=false;
bool bGpuFarShaderPrewarm=false,bDisableGpuWarmupPresentHold=false;
@@ -1577,6 +1582,8 @@ namespace
std::atomic<bool> bGpuShaderWarmupQueueFinished{false};
bool bGpuWarmupContextProtected=false;
std::string GpuShaderWarmupQueueError;
std::chrono::steady_clock::time_point GpuShaderWarmupQueueStartedAt{},GpuShaderWarmupQueueFinishedAt{};
std::chrono::steady_clock::time_point CpuReadyFrameBeforeGpuAt{};
bool bGpuFarAuditReported=false;
bool bGpuSkirtEmitEnabled=true,bGpuConnectorRepairEnabled=true;
bool bDisableVSync=false;
@@ -1617,6 +1624,8 @@ namespace
uint32 GpuFarMaxTilesPerSubmissionBatch=0;
uint32 GpuFarOutputCapacitySlots=0,GpuFarNextOutputSlot=0;
uint32 GpuFarOutputPoolReservedSlots=0;
std::size_t GpuFarPreReservePoolSlots=0;
int32 GpuFarPreReserveCoarseTileCells=0;
std::vector<uint32> GpuFarFreeOutputSlots;
std::unordered_map<FVoxelTileKey,VoxelForgeCore::FarFieldTileInput,FStandaloneTileKeyHash> GpuFarPreparedInputs;
std::unordered_map<FVoxelTileKey,std::string,FStandaloneTileKeyHash> GpuFarPreparationErrors;
@@ -1807,6 +1816,7 @@ namespace
(void)bCancel;
if(Streamer)Streamer->Stop();
Streamer.reset();
if(GpuPipelineStartupThread.joinable())GpuPipelineStartupThread.join();
GpuFarPreparationState.reset();
bGpuFarPreparationStarted=false;
if(FullShapeBuildThread.joinable())FullShapeBuildThread.join();
@@ -1995,7 +2005,18 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
bool EnsureGpuFarPipeline(std::string& Error)
{
if(!bGpuFarEnabled||bGpuFarPipelineInitialized)return true;
if(!bGpuFarEnabled)return true;
if(GpuPipelineStartupThread.joinable())
{
GpuPipelineStartupThread.join();
if(!bGpuPipelineStartupSucceeded&&!bGpuPipelineStartupWasDeferred)
{
Error=GpuPipelineStartupError.empty()
?"overlapped GPU pipeline initialization failed":GpuPipelineStartupError;
return false;
}
}
if(bGpuFarPipelineInitialized)return true;
if(!Device||!Context)
{Error="D3D device is unavailable for the GPU far pipeline";return false;}
const auto GpuPipelineStart=std::chrono::steady_clock::now();
@@ -2015,6 +2036,77 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
return true;
}
void StartGpuPipelineStartup(std::size_t PreReservePoolSlots,int32 CoarseTileCells)
{
if(!bGpuFarEnabled||bGpuFarPipelineInitialized||GpuPipelineStartupThread.joinable()
||bGpuPipelineStartupAttempted||bGpuFarWarmupBeforeWorkers)return;
if(!Device||!Context)return;
bGpuPipelineStartupAttempted=true;
GpuPipelineStartupThread=std::thread([this,PreReservePoolSlots,CoarseTileCells]()
{
ResolveGpuWarmupLaunchPolicy(true);
if(bGpuWarmupDeferredUntilFirstView)
{
bGpuPipelineStartupWasDeferred=true;
std::printf("[GpuPipelineOverlap] status=deferred reason=launch_policy_after_first_CPU_view\n");
return;
}
if(!SetGpuWarmupContextProtection(true))
{
bGpuPipelineStartupWasDeferred=true;
std::printf("[GpuPipelineOverlap] status=deferred reason=immediate_context_protection_unavailable\n");
return;
}
const auto PipelineStart=std::chrono::steady_clock::now();
std::printf("[GpuPipelineStartup] status=started action=pipeline_init_and_output_pool phase=post_shape_compile_before_cpu_workers\n");
std::printf("[ColdTimeline] stage=gpu_pipeline_init begin_ms=%.3f phase=post_shape_compile_before_cpu_workers\n",
std::chrono::duration<double,std::milli>(PipelineStart-ColdProcessStart).count());
if(!VoxelForgeGpuFarViewer::InitializeFarPipeline(Device,Context,GpuFarShaderPath,
GpuPipelineStartupError))
{
bGpuPipelineStartupSucceeded=false;
SetGpuWarmupContextProtection(false);
std::fprintf(stderr,"[GpuPipelineInitError] overlapped: %s\n",
GpuPipelineStartupError.c_str());
return;
}
bGpuFarPipelineInitialized=true;
bGpuPipelineStartupSucceeded=true;
std::printf("[ColdTimeline] stage=gpu_pipeline_init end_ms=%.3f duration_ms=%.3f prewarm=%s phase=post_shape_compile_before_cpu_workers\n",
std::chrono::duration<double,std::milli>(std::chrono::steady_clock::now()-ColdProcessStart).count(),
std::chrono::duration<double,std::milli>(std::chrono::steady_clock::now()-PipelineStart).count(),
bGpuFarShaderPrewarm?(bGpuFarWarmupResourcesOnly?"async_resources_only":"async_dispatch")
:"disabled");
if(PreReservePoolSlots>0)
{
const auto PoolStart=std::chrono::steady_clock::now();
std::printf("[ColdTimeline] stage=gpu_output_pool_precompile begin_ms=%.3f slots=%zu phase=post_shape_compile_before_cpu_workers\n",
ColdTimelineMilliseconds(PoolStart),PreReservePoolSlots);
std::string PoolError;
if(VoxelForgeGpuFarViewer::ReserveFarOutputPool(Device,Context,GpuFarShaderPath,
static_cast<uint32>(PreReservePoolSlots),static_cast<uint32>(CoarseTileCells),PoolError))
{
GpuFarOutputCapacitySlots=static_cast<uint32>(PreReservePoolSlots);
GpuFarOutputPoolReservedSlots=GpuFarOutputCapacitySlots;
const double PoolMs=std::chrono::duration<double,std::milli>(
std::chrono::steady_clock::now()-PoolStart).count();
GpuFarPoolReserveMs+=PoolMs;
std::printf("[ColdTimeline] stage=gpu_output_pool_precompile end_ms=%.3f duration_ms=%.3f reserved_slots=%u phase=post_shape_compile_before_cpu_workers\n",
ColdTimelineMilliseconds(std::chrono::steady_clock::now()),PoolMs,
GpuFarOutputPoolReservedSlots);
}
else
std::fprintf(stderr,"[GpuFarPoolError] precompile reservation deferred: %s\n",
PoolError.c_str());
}
// Shader warmup issues a long queue of GPU work and can throttle the
// CPU-heavy detail pass and initial meshing. Pipeline and pool setup
// start after shape compilation, before the CPU stream workers.
if(!bGpuFarShaderPrewarm||bGpuWarmupCompleted)
SetGpuWarmupContextProtection(false);
});
}
bool BuildGpuWarmupCacheKey()
{
GpuWarmupCacheKey.clear();
@@ -2183,6 +2275,15 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
bGpuWarmupCacheKeyAvailable?GpuWarmupCacheIdentity.c_str():"unavailable",
GpuWarmupPolicyResolutionMs);
bGpuWarmupLaunchPolicyResolved=true;
if(!bGpuWarmupDeferredUntilFirstView&&bGpuWarmupCacheMarkerMatches)
{
// This exact adapter/driver/shader key was recorded only after a
// successful warmup. Replaying tiny dispatches every process start
// competes with shape and tile workers without changing the pipeline.
bGpuWarmupCompleted=true;
std::printf("[GpuWarmupCacheHit] action=skip_redundant_dispatches identity=%s\n",
GpuWarmupCacheIdentity.c_str());
}
return true;
}
@@ -2237,11 +2338,13 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
return true;
}
void StartGpuShaderWarmupAsync()
void StartGpuShaderWarmupAsync(bool bShapeCompileOverlap=false)
{
if(!bGpuFarShaderPrewarm||bGpuWarmupCompleted||GpuShaderWarmupThread.joinable())return;
bGpuShaderWarmupQueueSucceeded=false;GpuShaderWarmupQueueError.clear();
bGpuShaderWarmupQueueFinished.store(false,std::memory_order_relaxed);
GpuShaderWarmupQueueStartedAt=std::chrono::steady_clock::now();
GpuShaderWarmupQueueFinishedAt={};
const bool bContextProtected=SetGpuWarmupContextProtection(true);
if(!bContextProtected)
std::fprintf(stderr,"[GpuWarmupContextProtection] enable_failed; async immediate-context protection is unavailable\n");
@@ -2262,11 +2365,19 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
Device,Context,GpuFarShaderPath,!bGpuFarWarmupResourcesOnly,GpuWarmupEntryFilter,
bGpuFarWarmupKernelFences,
GpuShaderWarmupQueueError);
GpuShaderWarmupQueueFinishedAt=std::chrono::steady_clock::now();
if(bGpuFarTimelineEnabled)
std::printf("[ColdTimeline] event=gpu_warmup_queue_finished at_ms=%.3f queue_ms=%.3f success=%s\n",
ColdTimelineMilliseconds(GpuShaderWarmupQueueFinishedAt),
std::chrono::duration<double,std::milli>(
GpuShaderWarmupQueueFinishedAt-GpuShaderWarmupQueueStartedAt).count(),
bGpuShaderWarmupQueueSucceeded?"yes":"no");
bGpuShaderWarmupQueueFinished.store(true,std::memory_order_release);
});
std::printf("[GpuShaderWarmup] queue_thread=started overlap=%s\n",
bGpuWarmupDeferredUntilFirstView?"after_first_CPU_view":
(bGpuFarWarmupBeforeWorkers?"none_before_generation_workers":"CPU_world_configuration"));
(bGpuFarWarmupBeforeWorkers?"none_before_generation_workers":
(bShapeCompileOverlap?"shape_compile":"CPU_world_configuration")));
std::fflush(stdout);
}
@@ -2413,6 +2524,45 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
Request.validatePassageRoutes=!bDeferWalkabilityValidation;
if(bDeferWalkabilityValidation)
std::printf("[DiagnosticDeferred] walkability=full_headless_or_route_audit workers=8 samples=5642\n");
GpuFarPreReservePoolSlots=0;
GpuFarPreReserveCoarseTileCells=World->CoarseTileCells;
const bool bCanPreReservePool=bGpuFarEnabled&&bIncludeFarWorld&&!bNeighborhoodOnly
&&!bCaptureOnlyTile&&!(CaptureTileLimit>0&&!CapturePath.empty());
if(bCanPreReservePool)
{
std::unordered_set<FVoxelTileKey,FStandaloneTileKeyHash> PoolDesiredSet;
PoolDesiredSet.reserve(Desired.Tiles.size()*2+1);
for(const FVoxelTileKey& Key:Desired.Tiles)PoolDesiredSet.insert(Key);
const int32 ShapeMin[3]={Min.X-2,Min.Y-2,Min.Z-2};
const int32 ShapeMax[3]={Max.X+2,Max.Y+2,Max.Z+2};
for(const FVoxelTileKey& Key:Desired.Tiles)
{
if(Key.Level<0)continue;
if(Key.Level<World->FullResClipLevels)
{
if(!World->bGenerateSkirts||ComputeVisibleSkirtFaceMask(Key,PoolDesiredSet)==0)
++GpuFarPreReservePoolSlots;
continue;
}
if(Key.Level>8)continue;
const int32 Cells=std::max(1,World->CoarseTileCells);
if(Cells>32)continue;
const int32 Extent=CHUNK_SIZE<<Key.Level;
const int32 Step=std::max(1,Extent/Cells);
const FIntVector Origin=Key.OriginVoxels();
const int32 Origins[3]={Origin.X,Origin.Y,Origin.Z};
bool bOutsideNearRegion=false;
for(int32 Axis=0;Axis<3;++Axis)
{
const double First=std::ceil((static_cast<double>(ShapeMin[Axis])-Origins[Axis])/Step);
const double Last=std::floor((static_cast<double>(ShapeMax[Axis])-Origins[Axis])/Step);
if(First>Cells+1||Last< -1){bOutsideNearRegion=true;break;}
}
if(bOutsideNearRegion)++GpuFarPreReservePoolSlots;
}
std::printf("[GpuOutputPoolPreReservePlan] desired=%zu candidate_slots=%zu status=geometry_match\n",
Desired.Tiles.size(),GpuFarPreReservePoolSlots);
}
const auto ShapeCompileStart=std::chrono::steady_clock::now();
std::printf("[ConfigureProfile] stage=snapshot_and_build_request duration_ms=%.3f\n",
std::chrono::duration<double,std::milli>(ShapeCompileStart-SnapshotStart).count());
@@ -2424,6 +2574,9 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
std::printf("[ColdTimeline] stage=shape_compile end_ms=%.3f duration_ms=%.3f\n",
std::chrono::duration<double,std::milli>(ShapeCompileEnd-ColdProcessStart).count(),
std::chrono::duration<double,std::milli>(ShapeCompileEnd-ShapeCompileStart).count());
if(bIncludeFarWorld&&!bNeighborhoodOnly)
StartGpuPipelineStartup(GpuFarPreReservePoolSlots,
GpuFarPreReserveCoarseTileCells);
bShapePipelineActive=bNeighborhoodOnly;
bNeighborhoodReadyReported=false;
bFarShapeStagePending=bShapeWorld&&!bNeighborhoodOnly&&!ShapeWorld->hasFarWorld();
@@ -2526,7 +2679,12 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
std::chrono::duration<double,std::milli>(GenerationStart-ColdProcessStart).count());
std::string Error;
if(!ConfigureWorld(Error))
{std::fprintf(stderr,"World load failed: %s\n",Error.c_str());MessageBoxA(Window,Error.c_str(),"VoxelForge viewer",MB_ICONERROR);return;}
{
if(GpuPipelineStartupThread.joinable())GpuPipelineStartupThread.join();
std::fprintf(stderr,"World load failed: %s\n",Error.c_str());
MessageBoxA(Window,Error.c_str(),"VoxelForge viewer",MB_ICONERROR);return;
}
if(GpuPipelineStartupThread.joinable())GpuPipelineStartupThread.join();
std::printf("[ColdTimeline] stage=generation_setup configured_ms=%.3f\n",
std::chrono::duration<double,std::milli>(std::chrono::steady_clock::now()-ColdProcessStart).count());
ResolveGpuWarmupLaunchPolicy(false);
@@ -2535,7 +2693,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
if(bGpuFarEnabled&&bPipelineCanStartNow&&!EnsureGpuFarPipeline(Error))
{MessageBoxA(Window,Error.c_str(),"VoxelForge GPU pipeline",MB_ICONERROR);return;}
if(bGpuFarShaderPrewarm&&!bGpuFarWarmupBeforeWorkers
&&!bGpuWarmupDeferredUntilFirstView)
&&!bGpuWarmupDeferredUntilFirstView&&!bGpuWarmupCompleted)
StartGpuShaderWarmupAsync();
const FVector CameraVoxels(Position.X*4.0,Position.Y*4.0,Position.Z*4.0);
BuildTargetChunk=StandaloneWorldMesher::CameraChunk(CameraVoxels);
@@ -2959,10 +3117,13 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
{
Batch->PrepareStart=std::chrono::steady_clock::now();
if(bGpuFarTimelineEnabled)
std::printf("[ColdTimeline] event=gpu_input_export_workers_started at_ms=%.3f dispatch_to_start_ms=%.3f frame=%llu shared_workers=%zu\n",
std::printf("[ColdTimeline] event=gpu_input_export_workers_started at_ms=%.3f dispatch_to_start_ms=%.3f cpu_ready_to_start_ms=%.3f frame=%llu shared_workers=%zu\n",
ColdTimelineMilliseconds(Batch->PrepareStart),
std::chrono::duration<double,std::milli>(
Batch->PrepareStart-Batch->DispatchAt).count(),
Batch->CpuReadyAt.time_since_epoch().count()!=0
?std::chrono::duration<double,std::milli>(
Batch->PrepareStart-Batch->CpuReadyAt).count():-1.0,
static_cast<unsigned long long>(ColdTimelineFrame()),
Batch->SharedWorkerCount);
std::printf("[ColdTimeline] stage=gpu_input_export begin_ms=%.3f tiles=%zu shared_workers=%zu\n",
@@ -3260,15 +3421,18 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
const auto DispatchAt=std::chrono::steady_clock::now();
auto Batch=std::make_shared<FGpuFarPreparationBatch>();
Batch->DispatchAt=DispatchAt;Batch->TileCount=NeedPreparation.size();
Batch->CpuReadyAt=CpuReadyFrameBeforeGpuAt;
Batch->SharedWorkerCount=Streamer?static_cast<std::size_t>(Streamer->GetWorkerCount()):0;
Batch->Generation=GpuFarPreparationGeneration.load(std::memory_order_acquire);
Batch->Remaining.store(NeedPreparation.size(),std::memory_order_relaxed);
GpuFarPreparationState=Batch;
bGpuFarPreparationFinished.store(false,std::memory_order_relaxed);
if(bGpuFarTimelineEnabled)
std::printf("[ColdTimeline] event=gpu_input_export_priority_queue at_ms=%.3f after_pool_ms=%.3f memory_sample_ms=%.3f list_copy_sort_ms=%.3f tiles=%zu shared_workers=%zu\n",
std::printf("[ColdTimeline] event=gpu_input_export_priority_queue at_ms=%.3f after_pool_ms=%.3f cpu_ready_to_queue_ms=%.3f memory_sample_ms=%.3f list_copy_sort_ms=%.3f tiles=%zu shared_workers=%zu\n",
ColdTimelineMilliseconds(DispatchAt),
std::chrono::duration<double,std::milli>(DispatchAt-PoolEnd).count(),
CpuReadyFrameBeforeGpuAt.time_since_epoch().count()!=0
?std::chrono::duration<double,std::milli>(DispatchAt-CpuReadyFrameBeforeGpuAt).count():-1.0,
MemorySampleMs,PreparationListMs,NeedPreparation.size(),Batch->SharedWorkerCount);
constexpr std::size_t GpuInputPriorityBurst=3;
std::printf("[GpuFarSchedulingPolicy] workers=%zu gpu_input_priority_burst=%zu cpu_mesh_jobs_per_burst=1 mode=shared_worker_pool\n",
@@ -3316,6 +3480,17 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
{
if(!bGpuFarShaderPrewarm||bGpuWarmupCompleted)return bGpuFarEnabled;
if(!bGpuShaderWarmupQueueFinished.load(std::memory_order_acquire))return false;
if(bGpuFarTimelineEnabled)
{
const auto ObservedAt=std::chrono::steady_clock::now();
const double CpuReadyToObservedMs=CpuReadyFrameBeforeGpuAt.time_since_epoch().count()!=0
?std::chrono::duration<double,std::milli>(ObservedAt-CpuReadyFrameBeforeGpuAt).count():-1.0;
std::printf("[ColdTimeline] event=gpu_warmup_completion_observed at_ms=%.3f queue_finished_at_ms=%.3f observe_delay_ms=%.3f cpu_ready_frame_presented=%s cpu_ready_to_observed_ms=%.3f frame=%llu\n",
ColdTimelineMilliseconds(ObservedAt),ColdTimelineMilliseconds(GpuShaderWarmupQueueFinishedAt),
std::chrono::duration<double,std::milli>(ObservedAt-GpuShaderWarmupQueueFinishedAt).count(),
CpuReadyFrameBeforeGpuAt.time_since_epoch().count()!=0?"yes":"no",
CpuReadyToObservedMs,static_cast<unsigned long long>(ColdTimelineFrame()));
}
const auto WarmupStart=std::chrono::steady_clock::now();
if(GpuShaderWarmupThread.joinable())GpuShaderWarmupThread.join();
if(!bGpuShaderWarmupQueueSucceeded)
@@ -3480,7 +3655,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
ResolveGpuWarmupLaunchPolicy(false);
if(bGpuWarmupDeferredUntilFirstView&&!bGpuWarmupDeferredStarted)
{
if(!bWholeViewPresented)return;
if(!bCpuReadyFramePresentedBeforeGpu)return;
std::string DeferredPipelineError;
if(!EnsureGpuFarPipeline(DeferredPipelineError))
{
@@ -5307,6 +5482,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
if(bDidPresent&&bDeferGpuUntilCpuFrame&&!StreamingBatches.empty())
{
bCpuReadyFramePresentedBeforeGpu=true;
CpuReadyFrameBeforeGpuAt=PresentCallEnd;
std::printf("[CpuReadyFrameBeforeGpu] at_ms=%.3f uploaded_cpu_batches=%zu next_action=submit_gpu_far\n",
std::chrono::duration<double,std::milli>(PresentCallEnd-ColdProcessStart).count(),
StreamingBatches.size());