wip(standalone): GPU step 24 -- warmup policy off the critical path; first launch = CPU

Luna: warmup policy (marker read, hashes, adapter query) now resolves on a
background thread; startup fflush(stdout) removed (a redirected flush measured
136 ms). Device-init -> generation-setup gap 0.056 ms on the miss route.
Deferred-batch replay runner reproduces 85-86% of the viewer's GPU time
(240 vs 279 ms); the old "10x" compared one LOD2 tile to a mixed LOD0-8 mean.
Remaining sequence-dependent spikes (11-29 ms on tiles that take ~0.1 ms alone)
are unexplained. Open: a marker-miss survival audit once stalled after
[CpuReadyFrameBeforeGpu].

My runs outside the sandbox (Step24.exe, marker removed first, interleaved, -Timeline):
  CPU 1.350 / 1.281 / 1.266 s
  GPU marker miss 1.336 s (all-CPU fallback, generation setup at 88 ms)
  GPU cached 1.167 / 1.166 / 1.168 s (GPU timestamps 220 / 255 / 205 ms,
      max dispatch 5.6 / 22.0 / 1.0 ms; 1,134-1,236 GPU tiles)
  Steadier than step 23 (1.07-1.17) but not faster; <=1.0 s not reached.
Audits: hashes w1/w8, walk 5,642/5,642, near 960 / far 1,114 0 diffs vs CPU and
WARP, thin 353, survival 6.19 GB, 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:06:49 +02:00
1 parent bb082f3ae9
commit 94d50edb47
9 files changed
+753 -47

No files matched your search

+131 -31
View File
@@ -1556,6 +1556,12 @@ namespace
bool bGpuFarWarmupOnly=false;
std::string GpuWarmupEntryFilter;
std::thread GpuShaderWarmupThread;
std::thread GpuWarmupPolicyThread;
std::atomic<bool> bGpuWarmupPolicyResolutionFinished{false};
bool bGpuWarmupPolicyResolutionStarted=false;
bool bGpuWarmupLaunchPolicyResolved=false;
bool bGpuWarmupCacheMarkerMatches=false;
double GpuWarmupPolicyResolutionMs=0.0;
std::shared_ptr<FGpuFarPreparationBatch> GpuFarPreparationState;
std::mutex GpuFarPreparedResultsMutex;
std::deque<FPreparedGpuFarInputResult> GpuFarPreparedResults;
@@ -1685,6 +1691,7 @@ namespace
double BatchUploadBudgetMilliseconds=2.0;
~FApp(){
if(GpuWarmupPolicyThread.joinable())GpuWarmupPolicyThread.join();
if(GpuShaderWarmupThread.joinable())GpuShaderWarmupThread.join();
StopGeneration(true);ReleaseMeshes();
if(SolidRasterizer)SolidRasterizer->Release();if(WireRasterizer)WireRasterizer->Release();
@@ -2061,10 +2068,13 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
return true;
};
MixText("gpu-far-warmup-signature-v1");
MixText("gpu-far-warmup-signature-v2");
const std::filesystem::path ShaderImplementation=GpuFarShaderPath.parent_path()
.parent_path()/"gpu_field_port"/"GpuFieldPort.cpp";
if(!MixFile(GpuFarShaderPath,true)||!MixFile(ShaderImplementation,true))return false;
const std::filesystem::path ViewerImplementation=GpuFarShaderPath.parent_path()
.parent_path()/"gpu_field_port"/"GpuFarViewer.h";
if(!MixFile(GpuFarShaderPath,true)||!MixFile(ShaderImplementation,true)
||!MixFile(ViewerImplementation,true))return false;
const std::filesystem::path PrecompiledDirectory=GpuFarShaderPath.parent_path()
.parent_path()/"gpu_field_port"/"precompiled";
for(const char* Entry:WarmupEntries)
@@ -2083,49 +2093,105 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
return true;
}
void ResolveGpuWarmupLaunchPolicy()
void StartGpuWarmupPolicyResolutionAsync()
{
if(!bGpuFarEnabled||!bGpuFarShaderPrewarm)return;
const bool bKeyAvailable=BuildGpuWarmupCacheKey();
bool bCacheMarkerMatches=false;
if(bKeyAvailable&&!GpuWarmupStatePath.empty())
{
std::ifstream Marker(GpuWarmupStatePath,std::ios::binary);
std::string SavedKey;
if(Marker&&std::getline(Marker,SavedKey))
bCacheMarkerMatches=SavedKey==GpuWarmupCacheKey;
}
if(!bGpuFarEnabled||!bGpuFarShaderPrewarm||bGpuWarmupPolicyResolutionStarted)return;
bGpuWarmupPolicyResolutionStarted=true;
bGpuWarmupPolicyResolutionFinished.store(false,std::memory_order_relaxed);
bGpuWarmupLaunchPolicyResolved=false;
bGpuWarmupCacheMarkerMatches=false;
GpuWarmupPolicyResolutionMs=0.0;
if(bGpuFarWarmupBeforeWorkers
||GpuWarmupLaunchPolicy==EGpuWarmupLaunchPolicy::ForceDeferred)
bGpuWarmupDeferredUntilFirstView=true;
else if(GpuWarmupLaunchPolicy==EGpuWarmupLaunchPolicy::ForceEager)
bGpuWarmupDeferredUntilFirstView=false;
else
bGpuWarmupDeferredUntilFirstView=true;
GpuWarmupPolicyThread=std::thread([this]
{
const auto ResolveStart=std::chrono::steady_clock::now();
bool bCacheMarkerMatches=false;
try
{
const bool bKeyAvailable=BuildGpuWarmupCacheKey();
if(bKeyAvailable&&!GpuWarmupStatePath.empty())
{
std::ifstream Marker(GpuWarmupStatePath,std::ios::binary);
std::string SavedKey;
if(Marker&&std::getline(Marker,SavedKey))
bCacheMarkerMatches=SavedKey==GpuWarmupCacheKey;
}
}
catch(const std::exception& Error)
{
std::fprintf(stderr,"[GpuWarmupPolicyError] %s\n",Error.what());
}
bGpuWarmupCacheMarkerMatches=bCacheMarkerMatches;
GpuWarmupPolicyResolutionMs=std::chrono::duration<double,std::milli>(
std::chrono::steady_clock::now()-ResolveStart).count();
bGpuWarmupPolicyResolutionFinished.store(true,std::memory_order_release);
});
std::printf("[ColdTimeline] event=gpu_warmup_policy_async_started at_ms=%.3f default_route=%s\n",
std::chrono::duration<double,std::milli>(
std::chrono::steady_clock::now()-ColdProcessStart).count(),
bGpuWarmupDeferredUntilFirstView?"after_first_CPU_view":"startup_overlap");
}
bool ResolveGpuWarmupLaunchPolicy(bool bWaitForResolution=false)
{
if(!bGpuFarEnabled||!bGpuFarShaderPrewarm)return true;
if(!bGpuWarmupPolicyResolutionStarted)StartGpuWarmupPolicyResolutionAsync();
if(bGpuWarmupLaunchPolicyResolved)return true;
if(!bGpuWarmupPolicyResolutionFinished.load(std::memory_order_acquire))
{
if(!bWaitForResolution)return false;
if(GpuWarmupPolicyThread.joinable())GpuWarmupPolicyThread.join();
}
else if(GpuWarmupPolicyThread.joinable())GpuWarmupPolicyThread.join();
if(!bGpuWarmupPolicyResolutionFinished.load(std::memory_order_acquire))return false;
if(bGpuFarWarmupBeforeWorkers)
{
bGpuWarmupDeferredUntilFirstView=false;
std::printf("[GpuWarmupLaunchDecision] mode=before_workers cache_marker=%s key_available=%s identity=%s\n",
bCacheMarkerMatches?"match":"miss",bKeyAvailable?"yes":"no",
bKeyAvailable?GpuWarmupCacheIdentity.c_str():"unavailable");
return;
std::printf("[GpuWarmupLaunchDecision] mode=before_workers cache_marker=%s key_available=%s identity=%s resolution_ms=%.3f\n",
bGpuWarmupCacheMarkerMatches?"match":"miss",
bGpuWarmupCacheKeyAvailable?"yes":"no",
bGpuWarmupCacheKeyAvailable?GpuWarmupCacheIdentity.c_str():"unavailable",
GpuWarmupPolicyResolutionMs);
bGpuWarmupLaunchPolicyResolved=true;
return true;
}
const bool bDeferredRouteWasAlreadyStarted=bGpuWarmupDeferredStarted;
if(GpuWarmupLaunchPolicy==EGpuWarmupLaunchPolicy::ForceDeferred)
bGpuWarmupDeferredUntilFirstView=true;
else if(GpuWarmupLaunchPolicy==EGpuWarmupLaunchPolicy::ForceEager)
bGpuWarmupDeferredUntilFirstView=false;
else
bGpuWarmupDeferredUntilFirstView=!bCacheMarkerMatches;
bGpuWarmupDeferredUntilFirstView=!bGpuWarmupCacheMarkerMatches
||bDeferredRouteWasAlreadyStarted;
const char* Mode=GpuWarmupLaunchPolicy==EGpuWarmupLaunchPolicy::ForceDeferred
?"forced_cold_start":(GpuWarmupLaunchPolicy==EGpuWarmupLaunchPolicy::ForceEager
?"forced_cached_start":"auto");
std::printf("[GpuWarmupLaunchDecision] mode=%s cache_marker=%s route=%s key_available=%s state_path=%s identity=%s\n",
Mode,bCacheMarkerMatches?"match":"miss",
std::printf("[GpuWarmupLaunchDecision] mode=%s cache_marker=%s route=%s key_available=%s state_path=%s identity=%s resolution_ms=%.3f\n",
Mode,bGpuWarmupCacheMarkerMatches?"match":"miss",
bGpuWarmupDeferredUntilFirstView?"after_first_CPU_view":"startup_overlap",
bKeyAvailable?"yes":"no",GpuWarmupStatePath.string().c_str(),
bKeyAvailable?GpuWarmupCacheIdentity.c_str():"unavailable");
bGpuWarmupCacheKeyAvailable?"yes":"no",GpuWarmupStatePath.string().c_str(),
bGpuWarmupCacheKeyAvailable?GpuWarmupCacheIdentity.c_str():"unavailable",
GpuWarmupPolicyResolutionMs);
bGpuWarmupLaunchPolicyResolved=true;
return true;
}
void MarkGpuWarmupCacheReady()
{
if(!bGpuFarShaderPrewarm||bGpuFarWarmupResourcesOnly
||!GpuWarmupEntryFilter.empty()||!bGpuWarmupCacheKeyAvailable
||GpuWarmupStatePath.empty())return;
||!GpuWarmupEntryFilter.empty()||GpuWarmupStatePath.empty())return;
ResolveGpuWarmupLaunchPolicy(true);
if(!bGpuWarmupCacheKeyAvailable)return;
const std::filesystem::path TemporaryPath=GpuWarmupStatePath.wstring()
+L"."+std::to_wstring(GetCurrentProcessId())+L".tmp";
{
@@ -2463,6 +2529,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
{std::fprintf(stderr,"World load failed: %s\n",Error.c_str());MessageBoxA(Window,Error.c_str(),"VoxelForge viewer",MB_ICONERROR);return;}
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);
const bool bPipelineCanStartNow=!bGpuWarmupDeferredUntilFirstView
||bGpuWarmupDeferredStarted;
if(bGpuFarEnabled&&bPipelineCanStartNow&&!EnsureGpuFarPipeline(Error))
@@ -3410,6 +3477,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
void PumpGpuFarGeneration(double MaxPumpBudgetMilliseconds=8.0)
{
if(!bGpuFarEnabled||!ShapeWorld||!ShapeWorld->hasFarWorld()||!Device||!Context)return;
ResolveGpuWarmupLaunchPolicy(false);
if(bGpuWarmupDeferredUntilFirstView&&!bGpuWarmupDeferredStarted)
{
if(!bWholeViewPresented)return;
@@ -3431,6 +3499,9 @@ 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;}
if(bGpuFarShaderPrewarm&&!bGpuWarmupDeferredUntilFirstView
&&!bGpuWarmupCompleted&&!GpuShaderWarmupThread.joinable())
StartGpuShaderWarmupAsync();
StartGpuFarPreparation();
if(!CompleteGpuShaderWarmupAndRoute())return;
StartGpuFarPreparation();
@@ -3438,12 +3509,12 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
const bool bColdDrainBeforeWholeView=!bInitialClipmapReported&&!bGpuFarAuditEnabled
&&!bDisableGpuColdStartDrain&&bGpuFarWarmupBeforeWorkers;
constexpr std::size_t StartupFirstGpuBatchTarget=32;
constexpr std::size_t StartupGpuBatchTarget=256;
constexpr std::size_t StartupGpuBatchTarget=384;
constexpr double StartupGpuPumpBudgetMilliseconds=16.0;
const bool bWaitForStreamingGpuBatch=!bColdDrainBeforeWholeView
&&!bGpuFarAuditEnabled&&!bInitialClipmapReported;
const std::size_t GpuPumpTileLimit=bWaitForStreamingGpuBatch
?(GpuFarSubmissionBatches==0?StartupFirstGpuBatchTarget:StartupGpuBatchTarget):32;
const std::size_t GpuPumpTileLimit=bGpuFarAuditEnabled?StartupFirstGpuBatchTarget
:(GpuFarSubmissionBatches==0?StartupFirstGpuBatchTarget:StartupGpuBatchTarget);
const double GpuPumpBudgetMilliseconds=std::min(MaxPumpBudgetMilliseconds,
bWaitForStreamingGpuBatch?StartupGpuPumpBudgetMilliseconds:4.0);
if(bWaitForStreamingGpuBatch
@@ -5277,7 +5348,20 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
if(!GApp)return DefWindowProc(H,Message,W,L);
switch(Message)
{
case WM_SIZE:if(W!=SIZE_MINIMIZED)GApp->Resize(LOWORD(L),HIWORD(L));return 0;
case WM_SIZE:
if(W!=SIZE_MINIMIZED)
{
const UINT NewWidth=LOWORD(L),NewHeight=HIWORD(L);
const bool bResizeNeeded=static_cast<int32>(NewWidth)!=GApp->Width
||static_cast<int32>(NewHeight)!=GApp->Height;
const auto ResizeStart=std::chrono::steady_clock::now();
if(bResizeNeeded)GApp->Resize(NewWidth,NewHeight);
std::printf("[ColdTimeline] event=viewer_window_size width=%u height=%u resized=%s duration_ms=%.3f\n",
NewWidth,NewHeight,bResizeNeeded?"yes":"no",
std::chrono::duration<double,std::milli>(
std::chrono::steady_clock::now()-ResizeStart).count());
}
return 0;
case WM_KEYDOWN:
{
const bool bWasDown=(L&(1ll<<30))!=0;if(W<256)GApp->Keys[W]=true;
@@ -7379,17 +7463,30 @@ int main(int Argc,char** Argv)
std::printf("[ColdTimeline] stage=viewer_device_init end_ms=%.3f duration_ms=%.3f\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()-DeviceInitStart).count());
App.ResolveGpuWarmupLaunchPolicy();
App.StartGpuWarmupPolicyResolutionAsync();
if(!App.bGpuFarWarmupOnly)
{
ShowWindow(App.Window,SW_SHOW);UpdateWindow(App.Window);
const auto WindowShowStart=std::chrono::steady_clock::now();
ShowWindow(App.Window,SW_SHOW);
std::printf("[ColdTimeline] stage=viewer_window_show duration_ms=%.3f\n",
std::chrono::duration<double,std::milli>(
std::chrono::steady_clock::now()-WindowShowStart).count());
const auto WindowUpdateStart=std::chrono::steady_clock::now();
UpdateWindow(App.Window);
std::printf("[ColdTimeline] stage=viewer_window_update duration_ms=%.3f\n",
std::chrono::duration<double,std::milli>(
std::chrono::steady_clock::now()-WindowUpdateStart).count());
const auto StartupBannerStart=std::chrono::steady_clock::now();
std::printf("Controls: hold right mouse to look; WASD move; Space/Ctrl (E/Q) up/down; Shift faster; wheel changes speed; F wireframe; G toggles legacy/detailed round walls; H toggles CPU/GPU far tiles; 1-9 toggle LOD; 0 toggle all levels; N/P change seed; T swaps base/round tunnels; V swaps owner density/shape intervals; Esc quit.\n");
std::printf("Owner data: %s; starting at first-strate open-space sample (%.2f, %.2f, %.2f) m.\n",
App.DataPath.c_str(),App.Position.X,App.Position.Y,App.Position.Z);
std::printf("[ColdTimeline] stage=viewer_startup_banner duration_ms=%.3f\n",
std::chrono::duration<double,std::milli>(
std::chrono::steady_clock::now()-StartupBannerStart).count());
}
std::fflush(stdout);
if(App.bGpuFarEnabled&&(App.bGpuFarWarmupOnly||App.bGpuFarWarmupBeforeWorkers))
{
App.ResolveGpuWarmupLaunchPolicy(true);
std::string GpuInitError;
if(!App.EnsureGpuFarPipeline(GpuInitError))return 3;
}
@@ -7433,6 +7530,9 @@ int main(int Argc,char** Argv)
App.bGpuWarmupCompleted=true;
std::printf("[GpuShaderWarmupBeforeWorkers] completed_before_generation_workers=true\n");
}
std::printf("[ColdTimeline] event=generation_setup_call at_ms=%.3f\n",
std::chrono::duration<double,std::milli>(
std::chrono::steady_clock::now()-ColdProcessStart).count());
App.StartGeneration();
MSG Message{};
while(Message.message!=WM_QUIT)