wip(standalone): GPU step 10 -- first-submit stall bisected to cold shader use under 8 workers

Top-down A/B/A controls: dispatching the 11 real compute shaders once removes
the 1.3-1.7 s first GPU Present (resource creation alone does not); 1 worker
also removes it and 8 workers bring it back. Default --gpu-far now prewarms
shaders asynchronously during shape compile (--gpu-far-no-prewarm = control).
The warm-up queue itself takes ~2.75 s, so whole view is unchanged:
CPU 1.354 s vs GPU 3.559 s (viewer clock), 1.473 vs 3.731 s (host clock).
Audits: 0 sign/topology diffs vs CPU/WARP, hashes + walk 5,642/5,642, survival.

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 05:38:14 +02:00
1 parent b0da995978
commit 10580650cb
10 files changed
+738 -6

No files matched your search

@@ -22,6 +22,12 @@ namespace VoxelForgeGpuFarViewer
ID3D11Query* end=nullptr;
ID3D11Query* disjoint=nullptr;
};
bool InitializeFarPipeline(ID3D11Device* device,ID3D11DeviceContext* context,
const std::filesystem::path& shaderPath,std::string& error);
bool QueueFarShaderWarmup(ID3D11Device* device,ID3D11DeviceContext* context,
const std::filesystem::path& shaderPath,bool dispatchShaders,std::string& error);
bool CompleteFarShaderWarmup(ID3D11Device* device,ID3D11DeviceContext* context,
const std::filesystem::path& shaderPath,std::string& error);
bool ReserveFarOutputPool(ID3D11Device* device,ID3D11DeviceContext* context,
const std::filesystem::path& shaderPath,std::uint32_t tileSlots,std::uint32_t cells,
std::string& error);
@@ -560,6 +560,7 @@ namespace
std::vector<FarOutputChunk> farOutputChunks;
std::uint32_t farOutputCapacitySlots=0;
std::uint32_t farOutputSlotsPerChunk=128,farMaxVerticesPerTile=0;
ComPtr<ID3D11Query> shaderWarmupCompletion;
static constexpr std::uint32_t FarOutputVertexCapacityPerTile=
VoxelForgeGpuFarViewer::PooledOutputVertexCapacityPerTile;
// Full clip-radius-3 skirt audit peaks at 27,840 panel+skirt vertices per
@@ -608,6 +609,240 @@ namespace
std::chrono::duration<double,std::milli>(std::chrono::steady_clock::now()-PipelineStart).count());
}
void QueueTinyShaderWarmup(bool dispatchShaders)
{
const auto WarmupStart=Clock::now();
constexpr std::uint32_t sampleCount=27,cellCount=1,groupCount=1;
TileConstants tile{};
tile.cells=1;tile.sampleDim=3;tile.cellCount=cellCount;tile.groupCount=groupCount;
tile.stepVoxels=1;tile.voxelSizeCm=25.0f;tile.outputVertexCapacity=3;
FarFieldConstants field{};
field.strataLo=-100.0f;field.strataHi=100.0f;field.strataBlend=12.0f;
field.sampleCount=sampleCount;field.spatialBinMask=0;field.spatialBinSize=64;
field.spatialBinCapacity=1;
FarConnectorConstants connector{};connector.panelVertexCapacity=64;
const auto ConstantBuffer=[&](const void* data,UINT byteWidth,const char* label)
{
D3D11_BUFFER_DESC desc{};desc.ByteWidth=byteWidth;desc.Usage=D3D11_USAGE_DEFAULT;
desc.BindFlags=D3D11_BIND_CONSTANT_BUFFER;
D3D11_SUBRESOURCE_DATA initial{};initial.pSysMem=data;
ComPtr<ID3D11Buffer> result;
Check(device->CreateBuffer(&desc,&initial,&result),label);return result;
};
auto tileCb=ConstantBuffer(&tile,sizeof(tile),"CreateBuffer(shader warmup tile constants)");
auto fieldCb=ConstantBuffer(&field,sizeof(field),"CreateBuffer(shader warmup field constants)");
auto connectorCb=ConstantBuffer(&connector,sizeof(connector),"CreateBuffer(shader warmup connector constants)");
FarFieldPrimitiveGpu emptyShape{};
FarFieldSpatialBin emptyBin{};
std::uint32_t zeroWord=0,invalidWord=InvalidIndex;
auto shapes=Structured(&emptyShape,1,D3D11_BIND_SHADER_RESOURCE);
auto bins=Structured(&emptyBin,1,D3D11_BIND_SHADER_RESOURCE);
auto shapeIds=Structured(&zeroWord,1,D3D11_BIND_SHADER_RESOURCE);
auto protectedAir=Structured(&zeroWord,1,D3D11_BIND_SHADER_RESOURCE);
auto protectedSolid=Structured(&zeroWord,1,D3D11_BIND_SHADER_RESOURCE);
auto detailLayers=Structured<FarFieldDetailLayerGpu>(nullptr,1,D3D11_BIND_SHADER_RESOURCE);
std::array<std::uint32_t,sampleCount> packedProxy{};packedProxy.fill(InvalidIndex);
auto proxies=Structured(packedProxy.data(),packedProxy.size(),D3D11_BIND_SHADER_RESOURCE);
auto density=Structured<float>(nullptr,sampleCount,D3D11_BIND_SHADER_RESOURCE|D3D11_BIND_UNORDERED_ACCESS);
auto nearNormal=Structured<float>(nullptr,sampleCount,D3D11_BIND_SHADER_RESOURCE|D3D11_BIND_UNORDERED_ACCESS);
auto evalCounters=Structured<std::uint32_t>(nullptr,sampleCount,D3D11_BIND_UNORDERED_ACCESS);
auto signs=Structured(&zeroWord,1,D3D11_BIND_SHADER_RESOURCE);
std::array<std::int32_t,256*16> triTable{};triTable.fill(-1);
auto triangles=Structured(triTable.data(),triTable.size(),D3D11_BIND_SHADER_RESOURCE);
auto cases=Structured<std::uint32_t>(nullptr,cellCount,D3D11_BIND_SHADER_RESOURCE|D3D11_BIND_UNORDERED_ACCESS);
auto localOffsets=Structured<std::uint32_t>(nullptr,cellCount,D3D11_BIND_SHADER_RESOURCE|D3D11_BIND_UNORDERED_ACCESS);
auto groupCounts=Structured<std::uint32_t>(nullptr,groupCount,D3D11_BIND_SHADER_RESOURCE|D3D11_BIND_UNORDERED_ACCESS);
auto groupOffsets=Structured<std::uint32_t>(nullptr,groupCount,D3D11_BIND_SHADER_RESOURCE|D3D11_BIND_UNORDERED_ACCESS);
auto midpointIds=Structured(&invalidWord,1,D3D11_BIND_SHADER_RESOURCE);
MidpointRecord emptyMidpoint{};
auto midpointRecords=Structured(&emptyMidpoint,1,D3D11_BIND_SHADER_RESOURCE);
FarFieldConnectorPanel emptyPanel{};
auto panelBuffer=Structured(&emptyPanel,1,D3D11_BIND_SHADER_RESOURCE);
FarConnectorRingRecord emptyRing{};
auto connectorRingBuffer=Structured(&emptyRing,1,D3D11_BIND_SHADER_RESOURCE|D3D11_BIND_UNORDERED_ACCESS);
auto skirtScratch=Structured<std::uint32_t>(nullptr,3,D3D11_BIND_UNORDERED_ACCESS);
auto densitySrv=Srv<float>(density.Get(),sampleCount);
auto nearNormalSrv=Srv<float>(nearNormal.Get(),sampleCount);
auto shapeSrv=Srv<FarFieldPrimitiveGpu>(shapes.Get(),1);
auto binSrv=Srv<FarFieldSpatialBin>(bins.Get(),1);
auto shapeIdSrv=Srv<std::uint32_t>(shapeIds.Get(),1);
auto protectedAirSrv=Srv<std::uint32_t>(protectedAir.Get(),1);
auto protectedSolidSrv=Srv<std::uint32_t>(protectedSolid.Get(),1);
auto detailSrv=Srv<FarFieldDetailLayerGpu>(detailLayers.Get(),1);
auto proxySrv=Srv<std::uint32_t>(proxies.Get(),sampleCount);
auto signSrv=Srv<std::uint32_t>(signs.Get(),1);
auto triangleSrv=Srv<std::int32_t>(triangles.Get(),triTable.size());
auto caseSrv=Srv<std::uint32_t>(cases.Get(),cellCount);
auto localOffsetSrv=Srv<std::uint32_t>(localOffsets.Get(),cellCount);
auto groupCountSrv=Srv<std::uint32_t>(groupCounts.Get(),groupCount);
auto groupOffsetSrv=Srv<std::uint32_t>(groupOffsets.Get(),groupCount);
auto midpointIdSrv=Srv<std::uint32_t>(midpointIds.Get(),1);
auto midpointRecordSrv=Srv<MidpointRecord>(midpointRecords.Get(),1);
auto connectorPanelSrv=Srv<FarFieldConnectorPanel>(panelBuffer.Get(),1);
auto connectorRingSrv=Srv<FarConnectorRingRecord>(connectorRingBuffer.Get(),1);
auto densityUav=Uav(density.Get(),sampleCount);
auto nearNormalUav=Uav(nearNormal.Get(),sampleCount);
auto evalCounterUav=Uav(evalCounters.Get(),sampleCount);
auto caseUav=Uav(cases.Get(),cellCount);
auto localOffsetUav=Uav(localOffsets.Get(),cellCount);
auto groupCountUav=Uav(groupCounts.Get(),groupCount);
auto groupOffsetUav=Uav(groupOffsets.Get(),groupCount);
auto connectorRingUav=Uav(connectorRingBuffer.Get(),1);
auto skirtScratchUav=Uav(skirtScratch.Get(),3);
auto args=RawMeshBuffer(11u*sizeof(std::uint32_t),D3D11_BIND_UNORDERED_ACCESS,
D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS|D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS);
auto vertices=RawMeshBuffer(3u*sizeof(GpuVertex),D3D11_BIND_UNORDERED_ACCESS|D3D11_BIND_SHADER_RESOURCE);
auto indices=RawMeshBuffer(3u*sizeof(std::uint32_t),D3D11_BIND_UNORDERED_ACCESS);
auto panelVertices=RawMeshBuffer(64u*sizeof(GpuVertex),D3D11_BIND_UNORDERED_ACCESS);
auto panelIndices=RawMeshBuffer(64u*sizeof(std::uint32_t),D3D11_BIND_UNORDERED_ACCESS);
auto argsUav=RawUav(args.Get(),11);
auto vertexUav=RawUav(vertices.Get(),static_cast<UINT>(3u*sizeof(GpuVertex)/4u));
auto vertexSrv=RawSrv(vertices.Get(),static_cast<UINT>(3u*sizeof(GpuVertex)/4u));
auto indexUav=RawUav(indices.Get(),3);
auto panelVertexUav=RawUav(panelVertices.Get(),static_cast<UINT>(64u*sizeof(GpuVertex)/4u));
auto panelIndexUav=RawUav(panelIndices.Get(),64);
std::array<NoiseProbeInput,64> noiseInput{};
auto noiseInputs=Structured(noiseInput.data(),noiseInput.size(),D3D11_BIND_SHADER_RESOURCE);
auto noiseOutputs=Structured<float>(nullptr,noiseInput.size(),D3D11_BIND_UNORDERED_ACCESS);
auto noiseInputSrv=Srv<NoiseProbeInput>(noiseInputs.Get(),noiseInput.size());
auto noiseOutputUav=Uav(noiseOutputs.Get(),noiseInput.size());
if(!dispatchShaders)
{
std::printf("[GpuShaderWarmup] resources_only=true tiny_samples=%u tiny_cells=%u dispatches=0\n",
sampleCount,cellCount);
return;
}
ID3D11Buffer* constants[3]={tileCb.Get(),fieldCb.Get(),connectorCb.Get()};
context->CSSetConstantBuffers(0,3,constants);
auto ClearBindings=[&](UINT srvCount=20)
{
ID3D11ShaderResourceView* nullSrvs[20]{};
ID3D11UnorderedAccessView* nullUavs[8]{};
context->CSSetShaderResources(0,srvCount,nullSrvs);
context->CSSetUnorderedAccessViews(0,8,nullUavs,nullptr);
};
auto Dispatch=[&](ID3D11ComputeShader* shader,const char* entry)
{
const auto start=Clock::now();
context->CSSetShader(shader,nullptr,0);context->Dispatch(1,1,1);
std::printf("[GpuShaderWarmup] entry=%s dispatch_call_ms=%.3f groups=1\n",entry,
std::chrono::duration<double,std::milli>(Clock::now()-start).count());
};
ID3D11ShaderResourceView* evalSrvs[20]{};
evalSrvs[10]=shapeSrv.Get();evalSrvs[11]=binSrv.Get();evalSrvs[12]=shapeIdSrv.Get();
evalSrvs[13]=protectedAirSrv.Get();evalSrvs[14]=protectedSolidSrv.Get();
evalSrvs[16]=detailSrv.Get();evalSrvs[17]=proxySrv.Get();
context->CSSetShaderResources(0,20,evalSrvs);
ID3D11UnorderedAccessView* evalUavs[8]{};
evalUavs[5]=nearNormalUav.Get();evalUavs[6]=evalCounterUav.Get();evalUavs[7]=densityUav.Get();
context->CSSetUnorderedAccessViews(0,8,evalUavs,nullptr);
Dispatch(fieldEval.Get(),"EvaluateFarFieldCS");ClearBindings();
ID3D11ShaderResourceView* classifySrvs[16]{};
classifySrvs[0]=signSrv.Get();classifySrvs[1]=triangleSrv.Get();
context->CSSetShaderResources(0,16,classifySrvs);
ID3D11UnorderedAccessView* classifyUavs[8]{};
classifyUavs[0]=caseUav.Get();classifyUavs[1]=localOffsetUav.Get();classifyUavs[2]=groupCountUav.Get();
context->CSSetUnorderedAccessViews(0,8,classifyUavs,nullptr);
Dispatch(classify.Get(),"ClassifyCS");ClearBindings(16);
ID3D11ShaderResourceView* farClassifySrvs[16]{};
farClassifySrvs[1]=triangleSrv.Get();farClassifySrvs[15]=densitySrv.Get();
context->CSSetShaderResources(0,16,farClassifySrvs);
context->CSSetUnorderedAccessViews(0,8,classifyUavs,nullptr);
Dispatch(fieldClassify.Get(),"ClassifyFarCS");ClearBindings(16);
ID3D11ShaderResourceView* scanSrvs[9]{};scanSrvs[8]=groupCountSrv.Get();
context->CSSetShaderResources(0,9,scanSrvs);
ID3D11UnorderedAccessView* scanUavs[8]{};scanUavs[3]=groupOffsetUav.Get();scanUavs[4]=argsUav.Get();
context->CSSetUnorderedAccessViews(0,8,scanUavs,nullptr);
Dispatch(scan.Get(),"ScanGroupsCS");ClearBindings(9);
ID3D11ShaderResourceView* emitSrvs[20]{};
emitSrvs[1]=triangleSrv.Get();emitSrvs[2]=densitySrv.Get();emitSrvs[3]=caseSrv.Get();
emitSrvs[4]=localOffsetSrv.Get();emitSrvs[5]=groupOffsetSrv.Get();
emitSrvs[6]=midpointIdSrv.Get();emitSrvs[7]=midpointRecordSrv.Get();
context->CSSetShaderResources(0,20,emitSrvs);
ID3D11UnorderedAccessView* emitUavs[8]{};emitUavs[5]=vertexUav.Get();emitUavs[6]=indexUav.Get();
context->CSSetUnorderedAccessViews(0,8,emitUavs,nullptr);
Dispatch(emit.Get(),"EmitCS");ClearBindings();
ID3D11ShaderResourceView* ringSrvs[5]={connectorPanelSrv.Get(),nullptr,shapeSrv.Get(),binSrv.Get(),shapeIdSrv.Get()};
context->CSSetShaderResources(8,5,ringSrvs);
ID3D11UnorderedAccessView* ringUavs[8]{};ringUavs[4]=argsUav.Get();ringUavs[7]=connectorRingUav.Get();
context->CSSetUnorderedAccessViews(0,8,ringUavs,nullptr);
Dispatch(connectorRings.Get(),"BuildFarConnectorRingsCS");ClearBindings();
ID3D11ShaderResourceView* panelSrvs[2]={connectorPanelSrv.Get(),connectorRingSrv.Get()};
context->CSSetShaderResources(8,2,panelSrvs);
ID3D11UnorderedAccessView* panelUavs[8]{};panelUavs[5]=panelVertexUav.Get();panelUavs[6]=panelIndexUav.Get();
context->CSSetUnorderedAccessViews(0,8,panelUavs,nullptr);
Dispatch(connectorPanels.Get(),"EmitFarConnectorPanelsCS");ClearBindings();
ID3D11ShaderResourceView* skirtSrvs[20]{};
skirtSrvs[10]=shapeSrv.Get();skirtSrvs[11]=binSrv.Get();skirtSrvs[12]=shapeIdSrv.Get();
skirtSrvs[13]=protectedAirSrv.Get();skirtSrvs[14]=protectedSolidSrv.Get();skirtSrvs[19]=vertexSrv.Get();
context->CSSetShaderResources(0,20,skirtSrvs);
ID3D11UnorderedAccessView* classifySkirtUavs[8]{};
classifySkirtUavs[0]=skirtScratchUav.Get();classifySkirtUavs[4]=argsUav.Get();
context->CSSetUnorderedAccessViews(0,8,classifySkirtUavs,nullptr);
Dispatch(classifyFarSkirts.Get(),"ClassifyFarSkirtsCS");ClearBindings();
ID3D11UnorderedAccessView* scanSkirtUavs[8]{};
scanSkirtUavs[0]=skirtScratchUav.Get();scanSkirtUavs[4]=argsUav.Get();
context->CSSetUnorderedAccessViews(0,8,scanSkirtUavs,nullptr);
Dispatch(scanFarSkirts.Get(),"ScanFarSkirtsCS");ClearBindings();
context->CSSetShaderResources(0,20,skirtSrvs);
ID3D11UnorderedAccessView* emitSkirtUavs[8]{};
emitSkirtUavs[0]=skirtScratchUav.Get();emitSkirtUavs[4]=argsUav.Get();
emitSkirtUavs[5]=panelVertexUav.Get();emitSkirtUavs[6]=panelIndexUav.Get();
context->CSSetUnorderedAccessViews(0,8,emitSkirtUavs,nullptr);
Dispatch(emitFarSkirts.Get(),"EmitFarSkirtsCS");ClearBindings();
ID3D11ShaderResourceView* noiseSrvs[10]{};noiseSrvs[9]=noiseInputSrv.Get();
context->CSSetShaderResources(0,10,noiseSrvs);
ID3D11UnorderedAccessView* noiseUavs[8]{};noiseUavs[6]=noiseOutputUav.Get();
context->CSSetUnorderedAccessViews(0,8,noiseUavs,nullptr);
Dispatch(noiseProbe.Get(),"NoiseProbeCS");ClearBindings(10);
ID3D11Buffer* nullConstants[3]{};context->CSSetConstantBuffers(0,3,nullConstants);
context->CSSetShader(nullptr,nullptr,0);
D3D11_QUERY_DESC eventDesc{D3D11_QUERY_EVENT,0};
Check(device->CreateQuery(&eventDesc,&shaderWarmupCompletion),"CreateQuery(shader warmup completion)");
context->End(shaderWarmupCompletion.Get());context->Flush();
std::printf("[GpuShaderWarmup] dispatched=11 tiny_samples=%u tiny_cells=%u queue_ms=%.3f completion=pending\n",
sampleCount,cellCount,std::chrono::duration<double,std::milli>(Clock::now()-WarmupStart).count());
}
bool CompleteTinyShaderWarmup()
{
if(!shaderWarmupCompletion)return true;
const auto start=Clock::now();
for(;;)
{
const HRESULT hr=immediateContext->GetData(shaderWarmupCompletion.Get(),nullptr,0,
D3D11_ASYNC_GETDATA_DONOTFLUSH);
if(hr==S_OK)break;
if(FAILED(hr))throw std::runtime_error("GetData(shader warmup completion) failed");
if(std::chrono::duration<double>(Clock::now()-start).count()>10.0)
throw std::runtime_error("shader warmup dispatch did not complete within 10 seconds");
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
const double waitMs=std::chrono::duration<double,std::milli>(Clock::now()-start).count();
shaderWarmupCompletion.Reset();
std::printf("[GpuShaderWarmup] completion_wait_ms=%.3f status=complete\n",waitMs);
return true;
}
void InitializeExternal(ID3D11Device* inDevice, ID3D11DeviceContext* inContext,
const std::filesystem::path& shaderPath)
{
@@ -2845,6 +3080,38 @@ namespace VoxelForgeGpuFarViewer
}
}
bool InitializeFarPipeline(ID3D11Device* device,ID3D11DeviceContext* context,
const std::filesystem::path& shaderPath,std::string& error)
{
try
{
GetFarViewerRunner(device,context,shaderPath);
return true;
}
catch(const std::exception& e){error=e.what();return false;}
}
bool QueueFarShaderWarmup(ID3D11Device* device,ID3D11DeviceContext* context,
const std::filesystem::path& shaderPath,bool dispatchShaders,std::string& error)
{
try
{
GetFarViewerRunner(device,context,shaderPath).QueueTinyShaderWarmup(dispatchShaders);
return true;
}
catch(const std::exception& e){error=e.what();return false;}
}
bool CompleteFarShaderWarmup(ID3D11Device* device,ID3D11DeviceContext* context,
const std::filesystem::path& shaderPath,std::string& error)
{
try
{
return GetFarViewerRunner(device,context,shaderPath).CompleteTinyShaderWarmup();
}
catch(const std::exception& e){error=e.what();return false;}
}
bool ReserveFarOutputPool(ID3D11Device* device,ID3D11DeviceContext* context,
const std::filesystem::path& shaderPath,std::uint32_t tileSlots,std::uint32_t cells,
std::string& error)
@@ -1,5 +1,5 @@
[CmdletBinding()]
param([string]$LogStem='whole_far_rx7900_warp',[switch]$WholeFar,[int]$ClipRadius=3)
param([string]$ExecutableName='VoxelForgeGpuFarAgreement.exe',[string]$LogStem='whole_far_rx7900_warp',[switch]$WholeFar,[int]$ClipRadius=3)
$ErrorActionPreference='Stop'
if([IO.Path]::GetFileName($LogStem)-ne$LogStem){throw 'LogStem must be a filename stem.'}
@@ -7,9 +7,11 @@ $HostProcess=Get-Process -Id $PID
$HostProcess.PriorityClass='BelowNormal'
$HostProcess.ProcessorAffinity=[IntPtr]0xFF00
$StandaloneRoot=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..\..\..'))
$ExecutablePath=Join-Path $StandaloneRoot 'bin\VoxelForgeGpuFarAgreement.exe'
$ExecutablePath=Join-Path $StandaloneRoot ('bin\'+$ExecutableName)
$StdoutPath=Join-Path $PSScriptRoot ($LogStem+'.stdout.log')
$StderrPath=Join-Path $PSScriptRoot ($LogStem+'.stderr.log')
if([IO.Path]::GetFileName($ExecutableName)-ne$ExecutableName-or[IO.Path]::GetExtension($ExecutableName)-ne'.exe')
{throw 'ExecutableName must be an executable filename.'}
if(-not(Test-Path -LiteralPath $ExecutablePath -PathType Leaf)){throw "GPU field runner not found: $ExecutablePath"}
$Arguments=@("--clip-radius=$ClipRadius");if($WholeFar){$Arguments+='--whole-far'}
@@ -23,7 +25,7 @@ for($Attempt=0;$Attempt -lt 2;$Attempt++)
Remove-Item -LiteralPath $StdoutPath,$StderrPath -Force -ErrorAction SilentlyContinue
$Child=Start-Process -FilePath $ExecutablePath -ArgumentList $Arguments -WorkingDirectory $StandaloneRoot `
-WindowStyle Hidden -PassThru -RedirectStandardOutput $StdoutPath -RedirectStandardError $StderrPath
$PeakWorkingSet=0L;$PeakPrivateMemory=0L
$PeakWorkingSet=0L;$PeakPrivateMemory=0L;$OverMemoryLimit=$false
while(-not$Child.HasExited)
{
$Child.Refresh()
@@ -32,11 +34,15 @@ for($Attempt=0;$Attempt -lt 2;$Attempt++)
$Child.ProcessorAffinity=[IntPtr]0xFF00
$PeakWorkingSet=[Math]::Max($PeakWorkingSet,$Child.WorkingSet64)
$PeakPrivateMemory=[Math]::Max($PeakPrivateMemory,$Child.PrivateMemorySize64)
# Reserve 1 GiB for this diagnostic's WARP + hardware allocations.
if($Child.PrivateMemorySize64-ge(7L*1GB))
{$OverMemoryLimit=$true;$Child.Kill();$Child.WaitForExit();break}
Start-Sleep -Milliseconds 100
}
$Child.WaitForExit();$Child.Refresh()
if($Child.ExitCode -eq -1073741502 -and $Attempt -eq 0){continue}
if($Child.ExitCode -eq -1073741502){throw '0xC0000142 repeated after its single retry; stopping.'}
Write-Output "RUN_EXIT pid=$($Child.Id) priority=BelowNormal affinity=0xFF00 peak_working_set_gb=$([math]::Round($PeakWorkingSet/1GB,2)) peak_private_gb=$([math]::Round($PeakPrivateMemory/1GB,2)) exit_code=$($Child.ExitCode) stdout=$StdoutPath stderr=$StderrPath"
if($OverMemoryLimit){throw 'GPU field runner stopped at the conservative 8 GiB private + 1 GiB GPU-reserve ceiling.'}
Write-Output "RUN_EXIT pid=$($Child.Id) priority=BelowNormal affinity=0xFF00 peak_working_set_gb=$([math]::Round($PeakWorkingSet/1GB,2)) peak_private_gb=$([math]::Round($PeakPrivateMemory/1GB,2)) conservative_private_plus_gpu_reserve_gb=$([math]::Round(($PeakPrivateMemory+1GB)/1GB,2)) exit_code=$($Child.ExitCode) stdout=$StdoutPath stderr=$StderrPath"
exit $Child.ExitCode
}
@@ -0,0 +1,72 @@
[CmdletBinding()]
param([string]$ExecutableName='VoxelForgeGpuFieldStep10Prewarm.exe')
$ErrorActionPreference='Stop'
$HostProcess=Get-Process -Id $PID
$HostProcess.PriorityClass='BelowNormal'
$HostProcess.ProcessorAffinity=[IntPtr]0xFF00
$StandaloneRoot=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..\..\..'))
$ExecutablePath=Join-Path $StandaloneRoot ('bin\'+$ExecutableName)
if([IO.Path]::GetFileName($ExecutableName)-ne$ExecutableName-or[IO.Path]::GetExtension($ExecutableName)-ne'.exe')
{throw 'ExecutableName must be an executable filename.'}
if(-not(Test-Path -LiteralPath $ExecutablePath -PathType Leaf)){throw "Viewer not found: $ExecutablePath"}
$Views=@(
@{Name='chamber';Camera='-11.75,-123.25,-19.85,0,0'},
@{Name='tunnel';Camera='-11.75,-123.25,-19.85,90,0'},
@{Name='passage';Camera='-39.19,6.27,-28.51,63,0'},
@{Name='far';Camera='-11.61244,-118.2212,-19.8452,0,0'},
@{Name='passage_floor_closeup';Camera='-39.19,6.27,-25.51,63,-25'},
@{Name='tunnel_bend';Camera='-198.97,-99.57,-45.58,33.59,23.17'},
@{Name='remote_yaw0';Camera='500,0,0,0,0'},
@{Name='remote_yaw90';Camera='500,0,0,90,0'},
@{Name='remote_yaw180';Camera='500,0,0,180,0'},
@{Name='remote_yaw270';Camera='500,0,0,270,0'}
)
$EightGB=8L*1024L*1024L*1024L
foreach($View in $Views)
{
foreach($Mode in @('cpu','gpu'))
{
$Stem="step10final_${Mode}_$($View.Name)"
$CaptureName="$Stem.png"
$Arguments=@('--clip-radius=3','--workers=8','--quit-after-clipmap','--capture-after-clipmap',
"--camera=$($View.Camera)","--$Mode-far",
"--capture=Research/OwnerShapeWorld/gpu_field_port/$CaptureName")
$StdoutPath=Join-Path $PSScriptRoot ($Stem+'.stdout.log')
$StderrPath=Join-Path $PSScriptRoot ($Stem+'.stderr.log')
$CapturePath=Join-Path $PSScriptRoot $CaptureName
Write-Output "STEP10_CAPTURE mode=$Mode view=$($View.Name) command=`"$ExecutablePath`" $($Arguments -join ' ')"
for($Attempt=0;$Attempt-lt2;$Attempt++)
{
if($Attempt-gt0)
{Write-Output '0xC0000142 encountered; waiting 60 seconds before the single permitted retry.';Start-Sleep -Seconds 60}
Remove-Item -LiteralPath $StdoutPath,$StderrPath -Force -ErrorAction SilentlyContinue
$Child=Start-Process -FilePath $ExecutablePath -ArgumentList $Arguments -WorkingDirectory $StandaloneRoot `
-WindowStyle Hidden -PassThru -RedirectStandardOutput $StdoutPath -RedirectStandardError $StderrPath
$PeakPrivate=0L;$OverMemoryLimit=$false
while(-not$Child.HasExited)
{
$Child.Refresh();if($Child.HasExited){break}
$Child.PriorityClass='BelowNormal';$Child.ProcessorAffinity=[IntPtr]0xFF00
$PeakPrivate=[Math]::Max($PeakPrivate,$Child.PrivateMemorySize64)
if($PeakPrivate-ge$EightGB)
{$OverMemoryLimit=$true;$Child.Kill();$Child.WaitForExit();break}
Start-Sleep -Milliseconds 100
}
$Child.WaitForExit();$Child.Refresh()
if($Child.ExitCode-eq-1073741502-and$Attempt-eq0){continue}
if($Child.ExitCode-eq-1073741502){throw '0xC0000142 repeated after its single permitted retry.'}
if($OverMemoryLimit){throw "Capture viewer stopped at the private-memory ceiling: $Stem"}
$LogText=Get-Content -LiteralPath $StdoutPath -Raw
if(-not$LogText.Contains('[InitialClipmapReady]')-or-not(Test-Path -LiteralPath $CapturePath -PathType Leaf))
{throw "Full-residency capture missing: $Stem; exit=$($Child.ExitCode)"}
$Memory=[regex]::Match($LogText,'(?m)^\[GpuFarMemory\] dxgi_local_sampled_peak_mb=([0-9.]+)')
$CombinedGB=$PeakPrivate/$EightGB
if($Memory.Success){$CombinedGB=($PeakPrivate+[double]$Memory.Groups[1].Value*1MB)/1GB}
if($CombinedGB-ge8.0){throw "Capture exceeded conservative private + DXGI-local 8 GiB ceiling: $Stem ($CombinedGB GiB)"}
Write-Output "STEP10_CAPTURE_DONE mode=$Mode view=$($View.Name) ready=$($LogText.Contains('[InitialClipmapReady]')) peak_private_gb=$([math]::Round($PeakPrivate/1GB,2)) conservative_private_plus_dxgi_gb=$([math]::Round($CombinedGB,2)) path=$CapturePath exit=$($Child.ExitCode)"
if($Child.ExitCode-ne0){throw "Capture viewer returned $($Child.ExitCode): $Stem"}
break
}
}
}
@@ -0,0 +1,56 @@
[CmdletBinding()]
param([string]$ExecutableName='VoxelForgeGpuFieldStep10Prewarm.exe')
$ErrorActionPreference='Stop'
$HostProcess=Get-Process -Id $PID
$HostProcess.PriorityClass='BelowNormal'
$HostProcess.ProcessorAffinity=[IntPtr]0xFF00
$StandaloneRoot=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..\..\..'))
$ExecutablePath=Join-Path $StandaloneRoot ('bin\'+$ExecutableName)
if([IO.Path]::GetFileName($ExecutableName)-ne$ExecutableName-or[IO.Path]::GetExtension($ExecutableName)-ne'.exe')
{throw 'ExecutableName must be an executable filename.'}
if(-not(Test-Path -LiteralPath $ExecutablePath -PathType Leaf)){throw "Viewer not found: $ExecutablePath"}
$Reference=$null
foreach($Workers in @(1,8))
{
$Name="step10_cpu_reference_w$Workers"
$StdoutPath=Join-Path $PSScriptRoot ($Name+'.stdout.log')
$StderrPath=Join-Path $PSScriptRoot ($Name+'.stderr.log')
$Arguments=@('--stream-benchmark','--shape-world','--force-generate','--stream-hash',
'--clip-radius=3',"--workers=$Workers")
Write-Output "STEP10_CPU_REFERENCE workers=$Workers command=`"$ExecutablePath`" $($Arguments -join ' ')"
for($Attempt=0;$Attempt-lt2;$Attempt++)
{
if($Attempt-gt0)
{Write-Output '0xC0000142 encountered; waiting 60 seconds before the single permitted retry.';Start-Sleep -Seconds 60}
Remove-Item -LiteralPath $StdoutPath,$StderrPath -Force -ErrorAction SilentlyContinue
$Child=Start-Process -FilePath $ExecutablePath -ArgumentList $Arguments -WorkingDirectory $StandaloneRoot `
-WindowStyle Hidden -PassThru -RedirectStandardOutput $StdoutPath -RedirectStandardError $StderrPath
$PeakPrivate=0L;$OverMemoryLimit=$false
while(-not$Child.HasExited)
{
$Child.Refresh();if($Child.HasExited){break}
$Child.PriorityClass='BelowNormal';$Child.ProcessorAffinity=[IntPtr]0xFF00
$PeakPrivate=[Math]::Max($PeakPrivate,$Child.PrivateMemorySize64)
if($PeakPrivate-ge8GB)
{$OverMemoryLimit=$true;$Child.Kill();$Child.WaitForExit();break}
Start-Sleep -Milliseconds 100
}
$Child.WaitForExit();$Child.Refresh()
if($Child.ExitCode-eq-1073741502-and$Attempt-eq0){continue}
if($Child.ExitCode-eq-1073741502){throw '0xC0000142 repeated after its single retry; stopping.'}
if($OverMemoryLimit){throw "CPU reference run stopped at the 8 GiB private-memory ceiling: W$Workers"}
$LogText=Get-Content -LiteralPath $StdoutPath -Raw
$Stream=[regex]::Match($LogText,'(?m)^\[StreamFullClipmap\].*generated=2226/2226.*density_hash=(0x[0-9a-fA-F]+) mesh_hash=(0x[0-9a-fA-F]+) payload_hash=(0x[0-9a-fA-F]+) topology_hash=(0x[0-9a-fA-F]+)')
$Walkability=[regex]::Match($LogText,'(?m)^\[Walkability\] PASS 5642/5642')
if(-not$Stream.Success-or-not$Walkability.Success)
{throw "CPU reference run failed full-generation/hash/walkability checks for W$Workers; exit=$($Child.ExitCode)"}
$Hashes=@($Stream.Groups[1].Value.ToLowerInvariant(),$Stream.Groups[2].Value.ToLowerInvariant(),
$Stream.Groups[3].Value.ToLowerInvariant(),$Stream.Groups[4].Value.ToLowerInvariant())
if($null-eq$Reference){$Reference=$Hashes}
elseif(($Reference-join'|')-ne($Hashes-join'|')){throw 'CPU hashes differ between W1 and W8.'}
Write-Output "STEP10_CPU_REFERENCE_RESULT workers=$Workers hashes=$($Hashes -join ',') walkability=5642/5642 peak_private_gb=$([math]::Round($PeakPrivate/1GB,2)) exit=$($Child.ExitCode)"
if($Child.ExitCode-ne0){throw "CPU reference returned $($Child.ExitCode): W$Workers"}
break
}
}
@@ -0,0 +1,68 @@
[CmdletBinding()]
param([string]$ExecutableName='VoxelForgeGpuFieldStep10Prewarm.exe')
$ErrorActionPreference='Stop'
$HostProcess=Get-Process -Id $PID
$HostProcess.PriorityClass='BelowNormal'
$HostProcess.ProcessorAffinity=[IntPtr]0xFF00
$StandaloneRoot=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..\..\..'))
$ExecutablePath=Join-Path $StandaloneRoot ('bin\'+$ExecutableName)
if([IO.Path]::GetFileName($ExecutableName)-ne$ExecutableName-or[IO.Path]::GetExtension($ExecutableName)-ne'.exe')
{throw 'ExecutableName must be an executable filename.'}
if(-not(Test-Path -LiteralPath $ExecutablePath -PathType Leaf)){throw "Viewer not found: $ExecutablePath"}
$Sequence=@(
@{Mode='cpu';Name='step10_final_interleaved_cpu_A'},
@{Mode='gpu';Name='step10_final_interleaved_gpu_A'},
@{Mode='gpu';Name='step10_final_interleaved_gpu_B'},
@{Mode='cpu';Name='step10_final_interleaved_cpu_B'}
)
$EightGB=8L*1024L*1024L*1024L
foreach($Run in $Sequence)
{
$Arguments=@('--clip-radius=3','--workers=8','--quit-after-clipmap',"--$($Run.Mode)-far")
if($Run.Mode-eq'gpu'){$Arguments+=@('--gpu-far-no-present-hold')}
$StdoutPath=Join-Path $PSScriptRoot ($Run.Name+'.stdout.log')
$StderrPath=Join-Path $PSScriptRoot ($Run.Name+'.stderr.log')
Write-Output "STEP10_FINAL_RUN mode=$($Run.Mode) name=$($Run.Name) command=`"$ExecutablePath`" $($Arguments -join ' ')"
for($Attempt=0;$Attempt-lt2;$Attempt++)
{
if($Attempt-gt0)
{Write-Output '0xC0000142 encountered; waiting 60 seconds before the single permitted retry.';Start-Sleep -Seconds 60}
Remove-Item -LiteralPath $StdoutPath,$StderrPath -Force -ErrorAction SilentlyContinue
$HostRunClock=[Diagnostics.Stopwatch]::StartNew()
$Child=Start-Process -FilePath $ExecutablePath -ArgumentList $Arguments -WorkingDirectory $StandaloneRoot `
-WindowStyle Hidden -PassThru -RedirectStandardOutput $StdoutPath -RedirectStandardError $StderrPath
$PeakPrivate=0L;$OverMemoryLimit=$false
while(-not$Child.HasExited)
{
$Child.Refresh();if($Child.HasExited){break}
$Child.PriorityClass='BelowNormal';$Child.ProcessorAffinity=[IntPtr]0xFF00
$PeakPrivate=[Math]::Max($PeakPrivate,$Child.PrivateMemorySize64)
if($Child.PrivateMemorySize64-ge$EightGB)
{$OverMemoryLimit=$true;$Child.Kill();$Child.WaitForExit();break}
Start-Sleep -Milliseconds 50
}
$Child.WaitForExit();$Child.Refresh()
$HostRunClock.Stop();$HostRunMs=$HostRunClock.Elapsed.TotalMilliseconds
if($Child.ExitCode-eq-1073741502-and$Attempt-eq0){continue}
if($Child.ExitCode-eq-1073741502){throw '0xC0000142 repeated after its single retry; stopping.'}
if($OverMemoryLimit){throw "Viewer stopped at the private-memory ceiling: $($Run.Name)"}
$LogText=Get-Content -LiteralPath $StdoutPath -Raw
if(-not$LogText.Contains('[InitialClipmapReady]'))
{throw "Full clipmap was not ready in $($Run.Name); exit=$($Child.ExitCode)"}
$Ready=[regex]::Match($LogText,'(?m)^\[InitialClipmapReady\] ([0-9.]+) s')
$Presented=[regex]::Match($LogText,'(?m)^\[WholeViewPresented\] at_ms=([0-9.]+) present_ms=([0-9.]+).*hr=0x([0-9A-Fa-f]{8})')
if(-not$Presented.Success){throw "Whole-view present marker missing in $($Run.Name)"}
$GpuPresent=[regex]::Match($LogText,'(?m)^\[GpuFirstBackedPresent\].*call_ms=([0-9.]+)')
$Memory=[regex]::Match($LogText,'(?m)^\[GpuFarMemory\] dxgi_local_sampled_peak_mb=([0-9.]+)')
$CombinedGB=$PeakPrivate/$EightGB
if($Memory.Success){$CombinedGB=($PeakPrivate+[double]$Memory.Groups[1].Value*1MB)/1GB}
if($CombinedGB-ge8.0){throw "Viewer exceeded the conservative private + DXGI-local 8 GiB ceiling: $($Run.Name) ($CombinedGB GiB)"}
$ViewerReadyMs=[double]$Ready.Groups[1].Value*1000.0
$ViewerPresentAtMs=[double]$Presented.Groups[1].Value
Write-Output "STEP10_CLOCK mode=$($Run.Mode) name=$($Run.Name) viewer_ready_ms=$ViewerReadyMs viewer_whole_view_present_return_ms=$ViewerPresentAtMs host_process_exit_ms=$([math]::Round($HostRunMs,3))"
Write-Output "STEP10_FINAL_RESULT mode=$($Run.Mode) name=$($Run.Name) ready_s=$($Ready.Groups[1].Value) whole_view_present_at_ms=$($Presented.Groups[1].Value) present_ms=$($Presented.Groups[2].Value) present_hr=0x$($Presented.Groups[3].Value.ToUpperInvariant()) first_gpu_present_ms=$(if($GpuPresent.Success){$GpuPresent.Groups[1].Value}else{'n/a'}) peak_private_gb=$([math]::Round($PeakPrivate/1GB,2)) conservative_private_plus_dxgi_gb=$([math]::Round($CombinedGB,2)) exit=$($Child.ExitCode)"
if($Child.ExitCode-ne0){throw "Viewer returned $($Child.ExitCode): $($Run.Name)"}
break
}
}
@@ -0,0 +1,51 @@
[CmdletBinding()]
param([string]$ExecutableName='VoxelForgeGpuFieldStep10Prewarm.exe')
$ErrorActionPreference='Stop'
$HostProcess=Get-Process -Id $PID
$HostProcess.PriorityClass='BelowNormal'
$HostProcess.ProcessorAffinity=[IntPtr]0xFF00
$StandaloneRoot=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..\..\..'))
$ExecutablePath=Join-Path $StandaloneRoot ('bin\'+$ExecutableName)
if([IO.Path]::GetFileName($ExecutableName)-ne$ExecutableName-or[IO.Path]::GetExtension($ExecutableName)-ne'.exe')
{throw 'ExecutableName must be an executable filename.'}
if(-not(Test-Path -LiteralPath $ExecutablePath -PathType Leaf)){throw "Viewer not found: $ExecutablePath"}
$Arguments=@('--clip-radius=3','--workers=8','--quit-after-clipmap','--gpu-far-audit','--gpu-far-prewarm')
$StdoutPath=Join-Path $PSScriptRoot 'step10_gpu_survival.stdout.log'
$StderrPath=Join-Path $PSScriptRoot 'step10_gpu_survival.stderr.log'
$EightGB=8L*1024L*1024L*1024L
Write-Output "STEP10_SURVIVAL command=`"$ExecutablePath`" $($Arguments -join ' ')"
for($Attempt=0;$Attempt-lt2;$Attempt++)
{
if($Attempt-gt0)
{Write-Output '0xC0000142 encountered; waiting 60 seconds before the single permitted retry.';Start-Sleep -Seconds 60}
Remove-Item -LiteralPath $StdoutPath,$StderrPath -Force -ErrorAction SilentlyContinue
$Child=Start-Process -FilePath $ExecutablePath -ArgumentList $Arguments -WorkingDirectory $StandaloneRoot `
-WindowStyle Hidden -PassThru -RedirectStandardOutput $StdoutPath -RedirectStandardError $StderrPath
$PeakPrivate=0L;$OverMemoryLimit=$false
while(-not$Child.HasExited)
{
$Child.Refresh();if($Child.HasExited){break}
$Child.PriorityClass='BelowNormal';$Child.ProcessorAffinity=[IntPtr]0xFF00
$PeakPrivate=[Math]::Max($PeakPrivate,$Child.PrivateMemorySize64)
if($Child.PrivateMemorySize64-ge$EightGB)
{$OverMemoryLimit=$true;$Child.Kill();$Child.WaitForExit();break}
Start-Sleep -Milliseconds 100
}
$Child.WaitForExit();$Child.Refresh()
if($Child.ExitCode-eq-1073741502-and$Attempt-eq0){continue}
if($Child.ExitCode-eq-1073741502){throw '0xC0000142 repeated after its single retry; stopping.'}
if($OverMemoryLimit){throw 'GPU survival audit stopped at the private-memory ceiling.'}
$LogText=Get-Content -LiteralPath $StdoutPath -Raw
if(-not$LogText.Contains('[InitialClipmapReady]')){throw "GPU survival audit did not reach full residency; exit=$($Child.ExitCode)"}
$Audit=[regex]::Match($LogText,'(?m)^\[GpuFarSurvivalAudit\].*status=(PASS|FAIL)')
$Memory=[regex]::Match($LogText,'(?m)^\[GpuFarMemory\] dxgi_local_sampled_peak_mb=([0-9.]+)')
if(-not$Audit.Success){throw 'GPU survival audit result marker missing.'}
$DxgiMB=if($Memory.Success){[double]$Memory.Groups[1].Value}else{0.0}
$CombinedGB=($PeakPrivate+$DxgiMB*1MB)/1GB
if($CombinedGB-ge8.0){throw "GPU survival process exceeded conservative private + DXGI-local memory ceiling: $CombinedGB GiB"}
Write-Output "STEP10_SURVIVAL_RESULT audit=$($Audit.Groups[1].Value) peak_private_gb=$([math]::Round($PeakPrivate/1GB,2)) dxgi_local_peak_mb=$DxgiMB conservative_private_plus_dxgi_gb=$([math]::Round($CombinedGB,2)) exit=$($Child.ExitCode) stdout=$StdoutPath stderr=$StderrPath"
if($Audit.Groups[1].Value-ne'PASS'){throw 'GPU survival audit failed.'}
if($Child.ExitCode-ne0){throw "Viewer returned $($Child.ExitCode)"}
break
}
@@ -0,0 +1,70 @@
[CmdletBinding()]
param([string]$ExecutableName='VoxelForgeGpuFieldStep10Prewarm.exe')
$ErrorActionPreference='Stop'
$HostProcess=Get-Process -Id $PID
$HostProcess.PriorityClass='BelowNormal'
$HostProcess.ProcessorAffinity=[IntPtr]0xFF00
$StandaloneRoot=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..\..\..'))
$ExecutablePath=Join-Path $StandaloneRoot ('bin\'+$ExecutableName)
if([IO.Path]::GetFileName($ExecutableName)-ne$ExecutableName-or[IO.Path]::GetExtension($ExecutableName)-ne'.exe')
{throw 'ExecutableName must be an executable filename.'}
if(-not(Test-Path -LiteralPath $ExecutablePath -PathType Leaf)){throw "Viewer not found: $ExecutablePath"}
$Sequence=@(
@{Warmup='none';Name='step10_bisect_no_warmup_A'},
@{Warmup='resources';Name='step10_bisect_resources_only'},
@{Warmup='dispatch';Name='step10_bisect_shader_dispatch_B'},
@{Warmup='none';Name='step10_bisect_no_warmup_A_restore'}
)
$EightGB=8L*1024L*1024L*1024L
foreach($Run in $Sequence)
{
$Arguments=@('--clip-radius=3','--workers=8','--quit-after-clipmap','--gpu-far','--gpu-far-no-prewarm','--gpu-far-no-present-hold')
if($Run.Warmup-eq'dispatch'){$Arguments+=@('--gpu-far-prewarm')}
elseif($Run.Warmup-eq'resources'){$Arguments+=@('--gpu-far-warmup-resources-only')}
$StdoutPath=Join-Path $PSScriptRoot ($Run.Name+'.stdout.log')
$StderrPath=Join-Path $PSScriptRoot ($Run.Name+'.stderr.log')
Write-Output "STEP10_BISECT name=$($Run.Name) warmup=$($Run.Warmup) command=`"$ExecutablePath`" $($Arguments -join ' ')"
for($Attempt=0;$Attempt-lt2;$Attempt++)
{
if($Attempt-gt0)
{Write-Output '0xC0000142 encountered; waiting 60 seconds before the single permitted retry.';Start-Sleep -Seconds 60}
Remove-Item -LiteralPath $StdoutPath,$StderrPath -Force -ErrorAction SilentlyContinue
$Child=Start-Process -FilePath $ExecutablePath -ArgumentList $Arguments -WorkingDirectory $StandaloneRoot `
-WindowStyle Normal -PassThru -RedirectStandardOutput $StdoutPath -RedirectStandardError $StderrPath
$PeakPrivate=0L;$OverMemoryLimit=$false
while(-not$Child.HasExited)
{
$Child.Refresh();if($Child.HasExited){break}
$Child.PriorityClass='BelowNormal';$Child.ProcessorAffinity=[IntPtr]0xFF00
$PeakPrivate=[Math]::Max($PeakPrivate,$Child.PrivateMemorySize64)
if($Child.PrivateMemorySize64-ge$EightGB)
{$OverMemoryLimit=$true;$Child.Kill();$Child.WaitForExit();break}
Start-Sleep -Milliseconds 50
}
$Child.WaitForExit();$Child.Refresh();$ViewerExitCode=$Child.ExitCode
if($ViewerExitCode-eq-1073741502-and$Attempt-eq0){continue}
if($ViewerExitCode-eq-1073741502){throw '0xC0000142 repeated after its single retry; stopping.'}
if($OverMemoryLimit){throw "Viewer stopped at the 8 GiB private-memory ceiling: $($Run.Name)"}
$LogText=Get-Content -LiteralPath $StdoutPath -Raw
if(-not$LogText.Contains('[InitialClipmapReady]'))
{throw "Full clipmap was not ready in $($Run.Name); exit=$ViewerExitCode"}
$FirstGpuPresent=Select-String -Path $StdoutPath -Pattern '^\[GpuFirstBackedPresent\]' |
Select-Object -First 1
if(-not$FirstGpuPresent){throw "GPU-backed present marker missing in $($Run.Name)"}
$Ready=[regex]::Match($LogText,'(?m)^\[InitialClipmapReady\] ([0-9.]+) s')
$Timeline=[regex]::Match($LogText,'(?m)^\[GpuFarTimeline\].*pump_cpu_max_ms=([0-9.]+).*queue_envelope_ms=([0-9.]+).*gpu_busy_ms=([0-9.]+)')
$Memory=[regex]::Match($LogText,'(?m)^\[GpuFarMemory\] dxgi_local_sampled_peak_mb=([0-9.]+)')
$CombinedGB=$PeakPrivate/$EightGB
if($Memory.Success){$CombinedGB=($PeakPrivate+[double]$Memory.Groups[1].Value*1MB)/1GB}
if($CombinedGB-ge8.0){throw "Viewer exceeded the conservative private + DXGI-local 8 GiB ceiling: $($Run.Name) ($CombinedGB GiB)"}
Write-Output "STEP10_RESULT name=$($Run.Name) warmup=$($Run.Warmup) ready_s=$($Ready.Groups[1].Value) first_gpu_present=`"$($FirstGpuPresent.Line)`" gpu_timeline=`"$($Timeline.Value)`" peak_private_gb=$([math]::Round($PeakPrivate/1GB,2)) conservative_private_plus_dxgi_gb=$([math]::Round($CombinedGB,2)) exit=$ViewerExitCode"
if($Run.Warmup-ne'none')
{
Select-String -Path $StdoutPath -Pattern '^\[GpuShaderWarmup\]' | ForEach-Object {Write-Output $_.Line}
}
if($ViewerExitCode-ne0){throw "Viewer returned $ViewerExitCode`: $($Run.Name)"}
break
}
}
@@ -0,0 +1,54 @@
# GPU field port: step 10 first-submit bisect
Date: 2026-10-07. The `/MP8` viewer and agreement runner were rebuilt under `BelowNormal` priority and process affinity `0xFF00`. No `0xC0000142` retry was needed. All test viewers exited, and no capture was opened in an image viewer.
## Finding
The 1.3–1.7 s first GPU-backed `Present` delay requires the eight-worker cold path in these controls. With the normal eight workers, dispatching all real compute shaders once on a tiny input before GPU tile submission reduced that `Present` call to sub-millisecond in the bisect run. Allocating the same scratch buffers and views without dispatching did not. Turning prewarm off restored the delay. Separately, one worker with prewarm off also removed it, and restoring eight workers brought it back.
This isolates the observed trigger to first-use shader dispatch work contending with the eight-worker startup path. It does not identify the driver's internal operation: lazy DXBC-to-ISA compilation is consistent with the result, but was not directly observed.
| Control | Workers | Prewarm | Ready | First GPU-backed `Present` |
|---|---:|---|---:|---:|
| A, full viewer | 8 | off | 3.493 s | 1,678.139 ms |
| Scratch resources and views only | 8 | no dispatch | 3.588 s | 1,565.936 ms |
| All-shader dispatch B | 8 | 11 real shaders | 3.494 s | 0.142 ms |
| A restored | 8 | off | 3.211 s | 1,269.500 ms |
| Worker control B | 1 | off | 3.596 s | 2.412 ms |
| Eight-worker restore | 8 | off | 3.507 s | 1,726.355 ms |
The dispatch control used 27 samples and one cell, and dispatched `EvaluateFarFieldCS`, `ClassifyCS`, `ClassifyFarCS`, `ScanGroupsCS`, `EmitCS`, both connector shaders, all three skirt shaders, and `NoiseProbeCS`. Its asynchronous queue took 2.749 s; completion wait was 0.003 ms. The separate resource-only mode allocated the warm-up scratch inputs, outputs, and SRV/UAV views, then recorded `dispatches=0`.
The worker A/B/A shows that changing worker count alone changes the cold `Present` from 1.68 s to 2.4 ms and back to 1.73 s. The shader A/B/A shows that resource creation alone does not, while actual shader dispatch does. Together these controls point to cold shader first-use work interacting with CPU scheduling pressure. Full-size real-input upload, `Map` versus `UpdateSubresource`, query-object count, and submission batch count were not independently toggled; the resource-only control covers the warm-up scratch set, not the complete per-tile resource graph.
## Change
Plain `--gpu-far` now starts the shader dispatch warm-up asynchronously before `StartGeneration`, while CPU shape compilation runs. It joins the warm-up before preparing/submitting GPU tiles. `--gpu-far-no-prewarm` retains the cold control; `--gpu-far-no-present-hold` independently disables the startup hold for measurement. The viewer logs the first GPU-backed `Present` separately from the full-view `Present`.
The warm-up dispatches all 11 production compute shaders on the tiny scratch inputs, fences the work, and releases the scratch resources before normal submissions. The default route remains eight workers. FXC still emits the existing X4714 `EmitCS` register-pressure warning.
## Fresh-launch whole-view race
Each process was launched in CPU A, GPU A, GPU B, CPU B order. The viewer clock comes from `[WholeViewPresented] at_ms`; the host clock is an independent stopwatch from immediately before `Start-Process` through child exit.
| Run | Viewer full-view `Present` return | Host process exit | First GPU-backed `Present` |
|---|---:|---:|---:|
| CPU A | 1,348.330 ms | 1,472.630 ms | — |
| GPU A | 3,579.329 ms | 3,772.968 ms | 0.123 ms |
| GPU B | 3,538.216 ms | 3,689.223 ms | 25.477 ms |
| CPU B | 1,359.016 ms | 1,473.172 ms | — |
Median viewer-clock time is **1.354 s CPU vs 3.559 s GPU** (GPU 2.63× slower, +2.205 s). Median host-clock time is **1.473 s CPU vs 3.731 s GPU** (GPU 2.53× slower, +2.258 s). The prewarm removes the multi-second first-submit call; it does not make the hybrid faster overall. These runs still return `DXGI_STATUS_OCCLUDED` (`0x087A0001`), so they measure readiness and API return rather than physical scanout.
## Parity, survival, and captures
- Fresh whole-far CPU/GPU/WARP agreement covered 1,114 eligible tiles and skipped 152 near-transition tiles. The audit found zero ordinary sign differences, zero protected-air or protected-solid sign differences, zero topology differences, zero thin-feature topology differences, and bit-identical repeated GPU outputs. It audited 127,401 protected-exception cells and 353 thin-feature cells.
- The GPU-mesh survival audit passed at full clipmap residency. Peak private memory was 3.26 GiB; sampled DXGI local usage was 2,404.1 MiB; conservative combined peak was 5.60 GiB.
- All ten CPU/GPU capture pairs reached `[InitialClipmapReady]` and exited successfully. Peak conservative private-plus-DXGI memory across captures was 5.91 GiB. RGB comparisons are in [step10_capture_comparison.md](step10_capture_comparison.md); the full-size captures were compared numerically and never opened.
- CPU reference checks at one and eight workers match density `0xdb2991c73215af4c`, mesh `0x8236c5edd7fc8e4f`, payload `0x4c48fa75a543d58e`, and topology `0xbea22e76b34a4dfc`. Walkability remains **5,642/5,642** for both worker counts.
## Remaining work
The direct-GPU LOD3/4 coverage loss and the output-slot overflow that blocked a valid movement-crossing measurement remain from step 9. They were not changed in this step. The hybrid's end-to-end floor is still over two seconds slower than CPU despite the hidden first-submit stall.
Primary evidence: [RunStep10WarmupBisect.ps1](RunStep10WarmupBisect.ps1), [RunStep10FinalInterleaved.ps1](RunStep10FinalInterleaved.ps1), [RunStep10GpuSurvival.ps1](RunStep10GpuSurvival.ps1), [RunStep10CaptureMatrix.ps1](RunStep10CaptureMatrix.ps1), `step10_bisect_*.stdout.log`, `step10_bisect_workers*.stdout.log`, `step10_final_interleaved_*.stdout.log`, `step10_whole_far_agreement.stdout.log`, and `step10_gpu_survival.stdout.log`.
+84 -2
View File
@@ -1488,6 +1488,11 @@ namespace
bool bFarShapeStagePending=false;
bool bGpuFarEnabled=false;
bool bGpuFarAuditEnabled=false;
bool bGpuFarShaderPrewarm=false,bDisableGpuWarmupPresentHold=false;
bool bGpuFarWarmupResourcesOnly=false;
std::thread GpuShaderWarmupThread;
bool bGpuShaderWarmupQueueSucceeded=false;
std::string GpuShaderWarmupQueueError;
bool bGpuFarAuditReported=false;
bool bGpuSkirtEmitEnabled=true,bGpuConnectorRepairEnabled=true;
bool bDisableVSync=false;
@@ -1536,6 +1541,7 @@ namespace
bool bFullDetailReported=false;
bool bInitialClipmapReported=false;
bool bWholeViewPresented=false;
bool bGpuFirstBackedPresentReported=false;
bool bProfileAttribution=false;
bool bProfileDetailBand=false;
bool bCompareEvaluators=false;
@@ -1585,6 +1591,7 @@ namespace
double BatchUploadBudgetMilliseconds=2.0;
~FApp(){
if(GpuShaderWarmupThread.joinable())GpuShaderWarmupThread.join();
StopGeneration(true);ReleaseMeshes();
if(SolidRasterizer)SolidRasterizer->Release();if(WireRasterizer)WireRasterizer->Release();
if(Constants)Constants->Release();if(InputLayout)InputLayout->Release();
@@ -1849,7 +1856,38 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
D3D11_RASTERIZER_DESC Raster{};Raster.FillMode=D3D11_FILL_SOLID;Raster.CullMode=D3D11_CULL_NONE;
Raster.DepthClipEnable=TRUE;if(FAILED(Device->CreateRasterizerState(&Raster,&SolidRasterizer)))return false;
Raster.FillMode=D3D11_FILL_WIREFRAME;if(FAILED(Device->CreateRasterizerState(&Raster,&WireRasterizer)))return false;
return Resize(Width,Height);
if(!Resize(Width,Height))return false;
if(bGpuFarEnabled)
{
std::string GpuInitError;
const auto GpuPipelineStart=std::chrono::steady_clock::now();
if(!VoxelForgeGpuFarViewer::InitializeFarPipeline(Device,Context,GpuFarShaderPath,
GpuInitError))
{
std::fprintf(stderr,"[GpuPipelineInitError] %s\n",GpuInitError.c_str());
return false;
}
std::printf("[ColdTimeline] stage=gpu_pipeline_init end_ms=%.3f duration_ms=%.3f prewarm=%s\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()-GpuPipelineStart).count(),
bGpuFarShaderPrewarm?(bGpuFarWarmupResourcesOnly?"async_resources_only":"async_dispatch")
:"disabled");
}
return true;
}
void StartGpuShaderWarmupAsync()
{
if(!bGpuFarShaderPrewarm||GpuShaderWarmupThread.joinable())return;
bGpuShaderWarmupQueueSucceeded=false;GpuShaderWarmupQueueError.clear();
GpuShaderWarmupThread=std::thread([this]()
{
bGpuShaderWarmupQueueSucceeded=VoxelForgeGpuFarViewer::QueueFarShaderWarmup(
Device,Context,GpuFarShaderPath,!bGpuFarWarmupResourcesOnly,
GpuShaderWarmupQueueError);
});
std::printf("[GpuShaderWarmup] queue_thread=started overlap=CPU_shape_compile\n");
std::fflush(stdout);
}
size_t PrintParity()
@@ -2455,6 +2493,25 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
GpuFarQueue.clear();GpuFarQueued.clear();GpuFarEligibleTiles.clear();
GpuFarPreparedInputs.clear();GpuFarPreparationErrors.clear();
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;
@@ -3636,6 +3693,17 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
const auto PresentCallStart=std::chrono::steady_clock::now();
const HRESULT PresentResult=SwapChain->Present(PresentSyncInterval,PresentFlags);
const auto PresentCallEnd=std::chrono::steady_clock::now();
if(GpuFarTilesSubmitted>0&&!bGpuFirstBackedPresentReported)
{
bGpuFirstBackedPresentReported=true;
std::printf("[GpuFirstBackedPresent] at_ms=%.3f call_ms=%.3f sync=%u flags=0x%08x hr=0x%08lx submitted_tiles=%llu batches=%llu\n",
std::chrono::duration<double,std::milli>(PresentCallEnd-ColdProcessStart).count(),
std::chrono::duration<double,std::milli>(PresentCallEnd-PresentCallStart).count(),
PresentSyncInterval,PresentFlags,static_cast<unsigned long>(PresentResult),
static_cast<unsigned long long>(GpuFarTilesSubmitted),
static_cast<unsigned long long>(GpuFarSubmissionBatches));
std::fflush(stdout);
}
if(bInitialClipmapReported&&!bWholeViewPresented)
{
bWholeViewPresented=true;
@@ -5506,8 +5574,20 @@ int main(int Argc,char** Argv)
else if(Arg=="--density-hash")App.bDensityHash=true;
else if(Arg=="--shape-world")App.bShapeWorld=true;
else if(Arg=="--gpu-far")
App.bGpuFarEnabled=true,App.bShapeWorld=true,App.bForceGenerate=true,
App.bGpuFarEnabled=true,App.bGpuFarShaderPrewarm=true,App.bShapeWorld=true,App.bForceGenerate=true,
App.bPresentCpuFrameBeforeGpu=true,App.bHoldCpuFrameDuringGpuWarmup=true;
else if(Arg=="--gpu-far-prewarm")
App.bGpuFarEnabled=true,App.bGpuFarShaderPrewarm=true,App.bShapeWorld=true,
App.bForceGenerate=true,App.bPresentCpuFrameBeforeGpu=true,
App.bHoldCpuFrameDuringGpuWarmup=true;
else if(Arg=="--gpu-far-warmup-resources-only")
App.bGpuFarEnabled=true,App.bGpuFarShaderPrewarm=true,App.bGpuFarWarmupResourcesOnly=true,
App.bShapeWorld=true,App.bForceGenerate=true,App.bPresentCpuFrameBeforeGpu=true,
App.bHoldCpuFrameDuringGpuWarmup=true;
else if(Arg=="--gpu-far-no-present-hold")
App.bDisableGpuWarmupPresentHold=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;
else if(Arg=="--gpu-far-audit")
{App.bGpuFarEnabled=true;App.bGpuFarAuditEnabled=true;App.bShapeWorld=true;App.bForceGenerate=true;App.bQuitAfterClipmap=true;}
@@ -5635,6 +5715,7 @@ int main(int Argc,char** Argv)
App.bPresentCpuFrameBeforeGpu=false;
App.bHoldCpuFrameDuringGpuWarmup=false;
}
if(App.bDisableGpuWarmupPresentHold)App.bHoldCpuFrameDuringGpuWarmup=false;
App.bProfileDetailBand=bProfileDetailBand;
std::string Error;
std::printf("[ColdTimeline] stage=owner_data_load begin_ms=%.3f\n",
@@ -5760,6 +5841,7 @@ int main(int Argc,char** Argv)
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::fflush(stdout);
App.StartGpuShaderWarmupAsync();
App.StartGeneration();
MSG Message{};
while(Message.message!=WM_QUIT)