wip(standalone): GPU step 14 -- warm-up 2.04 -> 1.05 s; cold path now waits on streaming, not shaders

Per-kernel fences in the production viewer put 1.73-1.98 s on the near-detail
Flush (shader creation 17-19 ms, dispatch calls ~0): skipping only that
dispatch cut idle warm-up 1.893 -> 0.521 s. Near detail rewritten with a
shared owner-roughness loop and shared Perlin samples: DXBC 448,588 -> 120,860
bytes, cold first use 1,596 -> 709 ms. Warm-up median 1.052 s, yet whole view
is unchanged (CPU 1.352 s vs hybrid 3.196 s): after input export the hybrid
spends 1.78-2.00 s streaming 1,082 GPU + 1,144 CPU-fallback tiles.
Audits: 0 diffs near/far vs CPU and 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 08:03:00 +02:00
1 parent a96fd0b95b
commit f322b1c089
14 files changed
+774 -38

No files matched your search

@@ -0,0 +1,59 @@
from pathlib import Path
import numpy as np
from PIL import Image
ROOT = Path(__file__).resolve().parent
VIEWS = (
"chamber",
"tunnel",
"passage",
"far",
"passage_floor_closeup",
"tunnel_bend",
"remote_yaw0",
"remote_yaw90",
"remote_yaw180",
"remote_yaw270",
)
rows = []
for view in VIEWS:
cpu_path = ROOT / f"step14_cpu_{view}.png"
gpu_path = ROOT / f"step14_gpu_{view}.png"
cpu = np.asarray(Image.open(cpu_path).convert("RGB"), dtype=np.int16)
gpu = np.asarray(Image.open(gpu_path).convert("RGB"), dtype=np.int16)
if cpu.shape != gpu.shape:
raise RuntimeError(f"dimension mismatch for {view}: {cpu.shape} vs {gpu.shape}")
diff = np.abs(cpu - gpu)
pixel_max = diff.max(axis=2)
rows.append(
(
view,
f"{cpu.shape[1]}x{cpu.shape[0]}",
float(diff.mean()),
float(np.sqrt(np.square(diff, dtype=np.float64).mean())),
float(np.count_nonzero(pixel_max) * 100.0 / pixel_max.size),
float(np.count_nonzero(pixel_max > 32) * 100.0 / pixel_max.size),
int(pixel_max.max()),
)
)
lines = [
"# Step 14 full-residency capture comparison",
"",
"All ten CPU/GPU pairs were captured after `[InitialClipmapReady]`. The files were compared numerically as RGB arrays and were not opened in an image viewer.",
"",
"| View | Size | RGB MAE | RGB RMSE | Changed pixels | Pixels max channel >32 | Max channel |",
"|---|---:|---:|---:|---:|---:|---:|",
]
for row in rows:
view, size, mae, rmse, changed, over32, maximum = row
lines.append(
f"| {view} | {size} | {mae:.4f} | {rmse:.4f} | {changed:.4f}% | {over32:.4f}% | {maximum} |"
)
report = ROOT / "step14_capture_comparison.md"
report.write_text("\n".join(lines) + "\n", encoding="utf-8")
print(report.read_text(encoding="utf-8"), end="")
@@ -26,7 +26,7 @@ namespace VoxelForgeGpuFarViewer
const std::filesystem::path& shaderPath,std::string& error);
bool QueueFarShaderWarmup(ID3D11Device* device,ID3D11DeviceContext* context,
const std::filesystem::path& shaderPath,bool dispatchShaders,
const std::string& entryFilter,std::string& error);
const std::string& entryFilter,bool fenceEachKernel,std::string& error);
bool CompleteFarShaderWarmup(ID3D11Device* device,ID3D11DeviceContext* context,
const std::filesystem::path& shaderPath,std::string& error);
bool ReserveFarOutputPool(ID3D11Device* device,ID3D11DeviceContext* context,
@@ -628,22 +628,27 @@ namespace
std::chrono::duration<double,std::milli>(std::chrono::steady_clock::now()-PipelineStart).count());
}
void QueueTinyShaderWarmup(bool dispatchShaders,const std::string& entryFilter)
void QueueTinyShaderWarmup(bool dispatchShaders,const std::string& entryFilter,
bool fenceEachKernel)
{
const auto WarmupStart=Clock::now();
const auto ResourceCreateStart=Clock::now();
static constexpr std::array<std::string_view,14> entries={
"EvaluateFarFieldCS","EvaluateNearFieldCS","ClassifyCS","ClassifyFarCS","ScanGroupsCS","EmitCS",
"ApplyNearFieldDetailCS",
"EmitNearCS","BuildFarConnectorRingsCS","EmitFarConnectorPanelsCS",
"ClassifyFarSkirtsCS","ScanFarSkirtsCS","EmitFarSkirtsCS","NoiseProbeCS"};
if(!entryFilter.empty()&&std::find(entries.begin(),entries.end(),entryFilter)==entries.end())
const bool skipEntryFilter=entryFilter.starts_with("skip:");
const std::string_view filteredEntry=skipEntryFilter
?std::string_view(entryFilter).substr(5):std::string_view(entryFilter);
if(!entryFilter.empty()&&std::find(entries.begin(),entries.end(),filteredEntry)==entries.end())
throw std::runtime_error("unknown shader warmup entry: "+entryFilter);
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=entryFilter=="EmitNearCS"?1:2;
tile.nearFieldMode=(entryFilter.empty()||entryFilter=="EvaluateNearFieldCS"
||entryFilter=="ApplyNearFieldDetailCS"||entryFilter=="EmitNearCS")?1u:0u;
tile.stepVoxels=filteredEntry=="EmitNearCS"?1:2;
tile.nearFieldMode=(entryFilter.empty()||filteredEntry=="EvaluateNearFieldCS"
||filteredEntry=="ApplyNearFieldDetailCS"||filteredEntry=="EmitNearCS")?1u:0u;
tile.voxelSizeCm=25.0f;tile.outputVertexCapacity=3;
FarFieldConstants field{};
field.strataLo=-100.0f;field.strataHi=100.0f;field.strataBlend=12.0f;
@@ -763,6 +768,12 @@ namespace
auto noiseInputSrv=Srv<NoiseProbeInput>(noiseInputs.Get(),noiseInput.size());
auto noiseOutputUav=Uav(noiseOutputs.Get(),noiseInput.size());
const double resourceCreateMs=std::chrono::duration<double,std::milli>(
Clock::now()-ResourceCreateStart).count();
if(fenceEachKernel)
std::printf("[GpuShaderWarmupProfile] stage=warmup_resource_create duration_ms=%.3f\n",
resourceCreateMs);
if(!dispatchShaders)
{
std::printf("[GpuShaderWarmup] resources_only=true tiny_samples=%u tiny_cells=%u dispatches=0\n",
@@ -780,121 +791,174 @@ namespace
context->CSSetShaderResources(0,srvCount,nullSrvs);
context->CSSetUnorderedAccessViews(0,8,nullUavs,nullptr);
};
auto Dispatch=[&](ID3D11ComputeShader* shader,const char* entry)
auto Dispatch=[&](ID3D11ComputeShader* shader,const char* entry,
Clock::time_point stateStart)
{
if(!entryFilter.empty()&&entryFilter!=entry)return;
if(entryFilter.empty()&&(std::string_view(entry)=="ClassifyCS"
if(!entryFilter.empty()&&!skipEntryFilter&&filteredEntry!=entry)return;
if(skipEntryFilter&&filteredEntry==entry)return;
if((entryFilter.empty()||skipEntryFilter)&&(std::string_view(entry)=="ClassifyCS"
||std::string_view(entry)=="EmitNearCS"||std::string_view(entry)=="NoiseProbeCS"))return;
const auto start=Clock::now();
context->CSSetShader(shader,nullptr,0);context->Dispatch(1,1,1);
++dispatchCount;
std::printf("[GpuShaderWarmup] entry=%s dispatch_call_ms=%.3f groups=1\n",entry,
std::chrono::duration<double,std::milli>(Clock::now()-start).count());
const double stateMs=std::chrono::duration<double,std::milli>(start-stateStart).count();
const double dispatchMs=std::chrono::duration<double,std::milli>(Clock::now()-start).count();
if(fenceEachKernel)
{
D3D11_QUERY_DESC eventDesc{D3D11_QUERY_EVENT,0};
ComPtr<ID3D11Query> eventQuery;
const auto queryStart=Clock::now();
Check(device->CreateQuery(&eventDesc,&eventQuery),
(std::string("CreateQuery(shader warmup ")+entry+")").c_str());
const double queryCreateMs=std::chrono::duration<double,std::milli>(
Clock::now()-queryStart).count();
context->End(eventQuery.Get());
const auto flushStart=Clock::now();
context->Flush();
const double flushMs=std::chrono::duration<double,std::milli>(
Clock::now()-flushStart).count();
const auto fenceStart=Clock::now();
for(;;)
{
const HRESULT hr=immediateContext->GetData(eventQuery.Get(),nullptr,0,
D3D11_ASYNC_GETDATA_DONOTFLUSH);
if(hr==S_OK)break;
if(FAILED(hr))throw std::runtime_error(
std::string("GetData(shader warmup ")+entry+") failed");
if(std::chrono::duration<double>(Clock::now()-fenceStart).count()>10.0)
throw std::runtime_error(std::string("shader warmup fence timed out: ")+entry);
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
const double fenceWaitMs=std::chrono::duration<double,std::milli>(
Clock::now()-fenceStart).count();
std::printf("[GpuShaderWarmupProfile] entry=%s state_bind_ms=%.3f first_dispatch_ms=%.3f query_create_ms=%.3f flush_ms=%.3f fence_wait_ms=%.3f fence=complete\n",
entry,stateMs,dispatchMs,queryCreateMs,flushMs,fenceWaitMs);
}
else
std::printf("[GpuShaderWarmup] entry=%s dispatch_call_ms=%.3f groups=1\n",entry,dispatchMs);
};
ID3D11ShaderResourceView* evalSrvs[21]{};
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();
auto stateStart=Clock::now();
context->CSSetShaderResources(0,20,evalSrvs);
ID3D11UnorderedAccessView* evalUavs[8]{};
evalUavs[1]=nearDetailBaseUav.Get();
evalUavs[5]=nearNormalUav.Get();evalUavs[6]=evalCounterUav.Get();evalUavs[7]=densityUav.Get();
context->CSSetUnorderedAccessViews(0,8,evalUavs,nullptr);
Dispatch(fieldEval.Get(),"EvaluateFarFieldCS");ClearBindings();
Dispatch(fieldEvalNear.Get(),"EvaluateNearFieldCS");ClearBindings();
Dispatch(fieldEval.Get(),"EvaluateFarFieldCS",stateStart);ClearBindings();
stateStart=Clock::now();
Dispatch(fieldEvalNear.Get(),"EvaluateNearFieldCS",stateStart);ClearBindings();
ID3D11ShaderResourceView* nearDetailSrvs[21]{};
nearDetailSrvs[13]=protectedAirSrv.Get();nearDetailSrvs[14]=protectedSolidSrv.Get();
nearDetailSrvs[16]=detailSrv.Get();nearDetailSrvs[17]=proxySrv.Get();
nearDetailSrvs[20]=nearDetailBaseSrv.Get();
stateStart=Clock::now();
context->CSSetShaderResources(0,21,nearDetailSrvs);
ID3D11UnorderedAccessView* nearDetailUavs[8]{};
nearDetailUavs[5]=nearNormalUav.Get();nearDetailUavs[7]=densityUav.Get();
context->CSSetUnorderedAccessViews(0,8,nearDetailUavs,nullptr);
Dispatch(fieldEvalNearDetail.Get(),"ApplyNearFieldDetailCS");ClearBindings();
Dispatch(fieldEvalNearDetail.Get(),"ApplyNearFieldDetailCS",stateStart);ClearBindings();
ID3D11ShaderResourceView* classifySrvs[16]{};
classifySrvs[0]=signSrv.Get();classifySrvs[1]=triangleSrv.Get();
stateStart=Clock::now();
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);
Dispatch(classify.Get(),"ClassifyCS",stateStart);ClearBindings(16);
ID3D11ShaderResourceView* farClassifySrvs[16]{};
farClassifySrvs[1]=triangleSrv.Get();farClassifySrvs[15]=densitySrv.Get();
stateStart=Clock::now();
context->CSSetShaderResources(0,16,farClassifySrvs);
context->CSSetUnorderedAccessViews(0,8,classifyUavs,nullptr);
Dispatch(fieldClassify.Get(),"ClassifyFarCS");ClearBindings(16);
Dispatch(fieldClassify.Get(),"ClassifyFarCS",stateStart);ClearBindings(16);
ID3D11ShaderResourceView* scanSrvs[9]{};scanSrvs[8]=groupCountSrv.Get();
stateStart=Clock::now();
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);
Dispatch(scan.Get(),"ScanGroupsCS",stateStart);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();
stateStart=Clock::now();
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();
Dispatch(emitNear.Get(),"EmitNearCS");ClearBindings();
Dispatch(emit.Get(),"EmitCS",stateStart);ClearBindings();
stateStart=Clock::now();
Dispatch(emitNear.Get(),"EmitNearCS",stateStart);ClearBindings();
ID3D11ShaderResourceView* ringSrvs[5]={connectorPanelSrv.Get(),nullptr,shapeSrv.Get(),binSrv.Get(),shapeIdSrv.Get()};
stateStart=Clock::now();
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();
Dispatch(connectorRings.Get(),"BuildFarConnectorRingsCS",stateStart);ClearBindings();
ID3D11ShaderResourceView* panelSrvs[2]={connectorPanelSrv.Get(),connectorRingSrv.Get()};
stateStart=Clock::now();
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();
Dispatch(connectorPanels.Get(),"EmitFarConnectorPanelsCS",stateStart);ClearBindings();
ID3D11ShaderResourceView* skirtSrvs[22]{};
skirtSrvs[10]=shapeSrv.Get();skirtSrvs[11]=binSrv.Get();skirtSrvs[12]=shapeIdSrv.Get();
skirtSrvs[13]=protectedAirSrv.Get();skirtSrvs[14]=protectedSolidSrv.Get();skirtSrvs[19]=vertexSrv.Get();
skirtSrvs[21]=skirtProbeSrv.Get();
stateStart=Clock::now();
context->CSSetShaderResources(0,22,skirtSrvs);
ID3D11UnorderedAccessView* classifySkirtUavs[8]{};
classifySkirtUavs[0]=skirtScratchUav.Get();classifySkirtUavs[2]=skirtProbeUav.Get();
classifySkirtUavs[4]=argsUav.Get();
context->CSSetUnorderedAccessViews(0,8,classifySkirtUavs,nullptr);
Dispatch(classifyFarSkirts.Get(),"ClassifyFarSkirtsCS");ClearBindings();
Dispatch(classifyFarSkirts.Get(),"ClassifyFarSkirtsCS",stateStart);ClearBindings();
ID3D11UnorderedAccessView* scanSkirtUavs[8]{};
stateStart=Clock::now();
scanSkirtUavs[0]=skirtScratchUav.Get();scanSkirtUavs[4]=argsUav.Get();
context->CSSetUnorderedAccessViews(0,8,scanSkirtUavs,nullptr);
Dispatch(scanFarSkirts.Get(),"ScanFarSkirtsCS");ClearBindings();
Dispatch(scanFarSkirts.Get(),"ScanFarSkirtsCS",stateStart);ClearBindings();
stateStart=Clock::now();
context->CSSetShaderResources(0,22,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();
Dispatch(emitFarSkirts.Get(),"EmitFarSkirtsCS",stateStart);ClearBindings();
ID3D11ShaderResourceView* noiseSrvs[10]{};noiseSrvs[9]=noiseInputSrv.Get();
stateStart=Clock::now();
context->CSSetShaderResources(0,10,noiseSrvs);
ID3D11UnorderedAccessView* noiseUavs[8]{};noiseUavs[6]=noiseOutputUav.Get();
context->CSSetUnorderedAccessViews(0,8,noiseUavs,nullptr);
Dispatch(noiseProbe.Get(),"NoiseProbeCS");ClearBindings(10);
Dispatch(noiseProbe.Get(),"NoiseProbeCS",stateStart);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();
if(!fenceEachKernel)
{
D3D11_QUERY_DESC eventDesc{D3D11_QUERY_EVENT,0};
Check(device->CreateQuery(&eventDesc,&shaderWarmupCompletion),"CreateQuery(shader warmup completion)");
context->End(shaderWarmupCompletion.Get());context->Flush();
}
if(dispatchShaders&&dispatchCount==0)
throw std::runtime_error("shader warmup selected no dispatches");
std::printf("[GpuShaderWarmup] dispatched=%u selected=%s tiny_samples=%u tiny_cells=%u queue_ms=%.3f completion=pending\n",
std::printf("[GpuShaderWarmup] dispatched=%u selected=%s tiny_samples=%u tiny_cells=%u queue_ms=%.3f completion=%s\n",
dispatchCount,entryFilter.empty()?"production":entryFilter.c_str(),sampleCount,cellCount,
std::chrono::duration<double,std::milli>(Clock::now()-WarmupStart).count());
std::chrono::duration<double,std::milli>(Clock::now()-WarmupStart).count(),
fenceEachKernel?"per_kernel_fenced":"pending");
}
bool CompleteTinyShaderWarmup()
@@ -3219,11 +3283,12 @@ namespace VoxelForgeGpuFarViewer
bool QueueFarShaderWarmup(ID3D11Device* device,ID3D11DeviceContext* context,
const std::filesystem::path& shaderPath,bool dispatchShaders,
const std::string& entryFilter,std::string& error)
const std::string& entryFilter,bool fenceEachKernel,std::string& error)
{
try
{
GetFarViewerRunner(device,context,shaderPath).QueueTinyShaderWarmup(dispatchShaders,entryFilter);
GetFarViewerRunner(device,context,shaderPath).QueueTinyShaderWarmup(
dispatchShaders,entryFilter,fenceEachKernel);
return true;
}
catch(const std::exception& e){error=e.what();return false;}
@@ -0,0 +1,80 @@
[CmdletBinding()]
param([string]$ExecutableName='VoxelForgeGpuFieldStep14Warmup.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="step14_${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 "STEP14_CAPTURE mode=$Mode view=$($View.Name) affinity=0xFF00 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
try
{
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
}
}
finally
{
if(-not$Child.HasExited){$Child.Kill();$Child.WaitForExit()}
}
$Child.WaitForExit();$Child.Refresh();$ChildExitCode=[int]$Child.ExitCode
if($ChildExitCode-eq-1073741502-and$Attempt-eq0){continue}
if($ChildExitCode-eq-1073741502){throw '0xC0000142 repeated after its single permitted retry.'}
if($OverMemoryLimit){throw "Capture viewer stopped at the 8 GiB 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=$ChildExitCode"}
$Memory=[regex]::Match($LogText,'(?m)^\[GpuFarMemory\] dxgi_local_sampled_peak_mb=([0-9.]+)')
$CombinedGB=$PeakPrivate/1GB
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)"}
$Ready=[regex]::Match($LogText,'(?m)^\[InitialClipmapReady\] ([0-9.]+) s')
Write-Output "STEP14_CAPTURE_DONE mode=$Mode view=$($View.Name) ready_s=$($Ready.Groups[1].Value) peak_private_gb=$([math]::Round($PeakPrivate/1GB,2)) conservative_private_plus_dxgi_gb=$([math]::Round($CombinedGB,2)) path=$CapturePath exit=$ChildExitCode"
if($ChildExitCode-ne0){throw "Capture viewer returned $ChildExitCode`: $Stem"}
break
}
}
}
@@ -0,0 +1,63 @@
[CmdletBinding()]
param([string]$ExecutableName='VoxelForgeGpuFieldStep14Warmup.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="step14_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 "STEP14_CPU_REFERENCE workers=$Workers affinity=0xFF00 priority=BelowNormal 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
try
{
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(8L*1GB))
{$OverMemoryLimit=$true;$Child.Kill();$Child.WaitForExit();break}
Start-Sleep -Milliseconds 100
}
}
finally
{
if(-not$Child.HasExited){$Child.Kill();$Child.WaitForExit()}
}
$Child.WaitForExit();$Child.Refresh();$ChildExitCode=[int]$Child.ExitCode
if($ChildExitCode-eq-1073741502-and$Attempt-eq0){continue}
if($ChildExitCode-eq-1073741502){throw '0xC0000142 repeated after its single retry; stopping.'}
if($OverMemoryLimit){throw "CPU reference 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 failed full-generation/hash/walkability checks for W$Workers; exit=$ChildExitCode"}
$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 "STEP14_CPU_REFERENCE_RESULT workers=$Workers hashes=$($Hashes -join ',') walkability=5642/5642 peak_private_gb=$([math]::Round($PeakPrivate/1GB,2)) exit=$ChildExitCode"
if($ChildExitCode-ne0){throw "CPU reference returned $ChildExitCode`: W$Workers"}
break
}
}
@@ -0,0 +1,77 @@
[CmdletBinding()]
param([string]$ExecutableName='VoxelForgeGpuFieldStep14Warmup.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"}
$Runs=@(
@{Mode='cpu';Name='step14_race_cpu_A'},
@{Mode='gpu';Name='step14_race_gpu_A'},
@{Mode='gpu';Name='step14_race_gpu_B'},
@{Mode='cpu';Name='step14_race_cpu_B'}
)
$EightGB=8L*1024L*1024L*1024L
foreach($Run in $Runs)
{
$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 "STEP14_RACE_RUN mode=$($Run.Mode) name=$($Run.Name) priority=BelowNormal affinity=0xFF00 workers=8 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
try
{
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
}
}
finally
{
if(-not$Child.HasExited){$Child.Kill();$Child.WaitForExit()}
}
$Child.WaitForExit();$Child.Refresh();$HostRunClock.Stop();$HostRunMs=$HostRunClock.Elapsed.TotalMilliseconds
$ViewerExitCode=[int]$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"}
$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)"}
$Warmup=[regex]::Match($LogText,'(?m)^\[GpuShaderWarmup\] dispatched=[0-9]+ .*queue_ms=([0-9.]+)')
$Wait=[regex]::Match($LogText,'(?m)^\[GpuShaderWarmup\] completion_wait_ms=([0-9.]+)')
$WarmupMs=if($Warmup.Success){[double]$Warmup.Groups[1].Value+$(if($Wait.Success){[double]$Wait.Groups[1].Value}else{0.0})}else{0.0}
$Memory=[regex]::Match($LogText,'(?m)^\[GpuFarMemory\] dxgi_local_sampled_peak_mb=([0-9.]+)')
$CombinedGB=$PeakPrivate/1GB
if($Memory.Success){$CombinedGB=($PeakPrivate+[double]$Memory.Groups[1].Value*1MB)/1GB}
if($CombinedGB-ge8.0){throw "Viewer exceeded conservative private + DXGI-local 8 GiB ceiling: $($Run.Name) ($CombinedGB GiB)"}
Write-Output "STEP14_RACE_RESULT mode=$($Run.Mode) name=$($Run.Name) warmup_ms=$([math]::Round($WarmupMs,3)) ready_ms=$([math]::Round([double]$Ready.Groups[1].Value*1000,3)) viewer_whole_view_present_return_ms=$($Presented.Groups[1].Value) present_ms=$($Presented.Groups[2].Value) present_hr=0x$($Presented.Groups[3].Value.ToUpperInvariant()) host_process_exit_ms=$([math]::Round($HostRunMs,3)) peak_private_gb=$([math]::Round($PeakPrivate/1GB,2)) conservative_private_plus_dxgi_gb=$([math]::Round($CombinedGB,2)) exit=$ViewerExitCode"
$Controls=[regex]::Match($LogText,'(?m)^\[ProcessControls\].*')
if($Controls.Success){Write-Output $Controls.Value}
if($ViewerExitCode-ne0){throw "Viewer returned $ViewerExitCode`: $($Run.Name)"}
break
}
}
@@ -0,0 +1,59 @@
[CmdletBinding()]
param([string]$ExecutableName='VoxelForgeGpuFieldStep14Warmup.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')
$Name='step14_gpu_survival'
$StdoutPath=Join-Path $PSScriptRoot ($Name+'.stdout.log')
$StderrPath=Join-Path $PSScriptRoot ($Name+'.stderr.log')
$EightGB=8L*1024L*1024L*1024L
Write-Output "STEP14_SURVIVAL_RUN 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
try
{
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
}
}
finally
{
if(-not$Child.HasExited){$Child.Kill();$Child.WaitForExit()}
}
$Child.WaitForExit();$Child.Refresh();$ChildExitCode=[int]$Child.ExitCode
if($ChildExitCode-eq-1073741502-and$Attempt-eq0){continue}
if($ChildExitCode-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=$ChildExitCode"}
$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 exceeded conservative private + DXGI-local 8 GiB ceiling: $CombinedGB GiB"}
Write-Output "STEP14_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=$ChildExitCode stdout=$StdoutPath stderr=$StderrPath"
if($Audit.Groups[1].Value-ne'PASS'){throw 'GPU survival audit failed.'}
if($ChildExitCode-ne0){throw "Viewer returned $ChildExitCode"}
break
}
@@ -0,0 +1,67 @@
[CmdletBinding()]
param(
[string]$ExecutableName='VoxelForgeGpuFieldStep14Warmup.exe',
[string]$Entry='ApplyNearFieldDetailCS',
[string]$SampleName='near_detail'
)
$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"}
if($Entry-ne'ApplyNearFieldDetailCS'){throw "Unsupported Step 14 shader entry: $Entry"}
$Name='step14_shader_'+$SampleName
$StdoutPath=Join-Path $PSScriptRoot ($Name+'.stdout.log')
$StderrPath=Join-Path $PSScriptRoot ($Name+'.stderr.log')
$Arguments=@('--workers=8','--gpu-far-warmup-only',"--gpu-far-warmup-entry=$Entry",'--gpu-far-warmup-kernel-fences')
$EightGB=8L*1024L*1024L*1024L
Write-Output "STEP14_SHADER_RUN entry=$Entry sample=$SampleName freshness=fresh_process priority=BelowNormal affinity=0xFF00 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
try
{
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
}
}
finally
{
if(-not$Child.HasExited){$Child.Kill();$Child.WaitForExit()}
}
$Child.WaitForExit();$Child.Refresh();$HostRunClock.Stop()
$ViewerExitCode=[int]$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 "Shader profile stopped at the 8 GiB private-memory ceiling: $SampleName"}
$LogText=Get-Content -LiteralPath $StdoutPath -Raw
$Create=[regex]::Match($LogText,'(?m)^\[GpuPipelineProfile\] stage=shader_object_create entry='+[regex]::Escape($Entry)+' duration_ms=([0-9.]+)')
$Profile=[regex]::Match($LogText,'(?m)^\[GpuShaderWarmupProfile\] entry='+[regex]::Escape($Entry)+' .*')
$Warmup=[regex]::Match($LogText,'(?m)^\[GpuShaderWarmup\] dispatched=1 selected='+[regex]::Escape($Entry)+'.*queue_ms=([0-9.]+)')
$Memory=[regex]::Match($LogText,'(?m)^\[GpuFarMemory\] dxgi_local_sampled_peak_mb=([0-9.]+)')
if(-not$Create.Success-or-not$Profile.Success-or-not$Warmup.Success)
{throw "Isolated profile markers missing for $Entry; exit=$ViewerExitCode"}
$CombinedGB=$PeakPrivate/1GB
if($Memory.Success){$CombinedGB=($PeakPrivate+[double]$Memory.Groups[1].Value*1MB)/1GB}
if($CombinedGB-ge8.0){throw "Shader profile exceeded private + DXGI-local 8 GiB ceiling: $SampleName ($CombinedGB GiB)"}
Write-Output "STEP14_SHADER_RESULT entry=$Entry sample=$SampleName object_create_ms=$($Create.Groups[1].Value) profile_ms=$($Profile.Groups[0].Value) isolated_queue_ms=$($Warmup.Groups[1].Value) host_process_exit_ms=$([math]::Round($HostRunClock.Elapsed.TotalMilliseconds,3)) peak_private_gb=$([math]::Round($PeakPrivate/1GB,2)) conservative_private_plus_dxgi_gb=$([math]::Round($CombinedGB,2)) exit=$ViewerExitCode"
if($ViewerExitCode-ne0){throw "Viewer returned $ViewerExitCode for $SampleName"}
break
}
@@ -0,0 +1,83 @@
[CmdletBinding()]
param([string]$ExecutableName='VoxelForgeGpuFieldStep14Warmup.exe',[switch]$SkipControlOnly,[string]$Label='profile')
$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"}
$Runs=@(
@{Name="step14_${Label}_workers_active";Idle=$false;WarmupOnly=$false},
@{Name="step14_${Label}_workers_idle";Idle=$true;WarmupOnly=$false},
@{Name="step14_${Label}_skip_near_detail";Idle=$true;WarmupOnly=$true}
)
if($SkipControlOnly){$Runs=@($Runs[2])}
$EightGB=8L*1024L*1024L*1024L
foreach($Run in $Runs)
{
if($Run.WarmupOnly)
{$Arguments=@('--workers=8','--gpu-far-warmup-only','--gpu-far-warmup-kernel-fences',
'--gpu-far-warmup-entry=skip:ApplyNearFieldDetailCS')}
else
{
$Arguments=@('--clip-radius=3','--workers=8','--quit-after-clipmap','--gpu-far',
'--gpu-far-warmup-kernel-fences')
if($Run.Idle){$Arguments+='--gpu-far-warmup-before-workers'}
}
$StdoutPath=Join-Path $PSScriptRoot ($Run.Name+'.stdout.log')
$StderrPath=Join-Path $PSScriptRoot ($Run.Name+'.stderr.log')
Write-Output "STEP14_PROFILE_RUN name=$($Run.Name) cpu_workers_idle=$($Run.Idle.ToString().ToLowerInvariant()) priority=BelowNormal affinity=0xFF00 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
try
{
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
}
}
finally
{
if(-not$Child.HasExited){$Child.Kill();$Child.WaitForExit()}
}
$Child.WaitForExit();$Child.Refresh();$HostRunClock.Stop()
$ViewerExitCode=[int]$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 "Profile process stopped at the 8 GiB private-memory ceiling: $($Run.Name)"}
$LogText=Get-Content -LiteralPath $StdoutPath -Raw
if(-not$Run.WarmupOnly-and-not$LogText.Contains('[InitialClipmapReady]'))
{throw "Full clipmap did not become ready in $($Run.Name); exit=$ViewerExitCode"}
if(-not$LogText.Contains('[GpuShaderWarmupProfile] stage=warmup_resource_create'))
{throw "Warmup profile markers are missing in $($Run.Name); exit=$ViewerExitCode"}
$Warmup=[regex]::Match($LogText,'(?m)^\[GpuShaderWarmup\] dispatched=[0-9]+ .*queue_ms=([0-9.]+) completion=per_kernel_fenced')
$Ready=[regex]::Match($LogText,'(?m)^\[InitialClipmapReady\] ([0-9.]+) s')
$Memory=[regex]::Match($LogText,'(?m)^\[GpuFarMemory\] dxgi_local_sampled_peak_mb=([0-9.]+)')
if(-not$Warmup.Success-or(-not$Run.WarmupOnly-and-not$Ready.Success))
{throw "Warmup or ready timing missing in $($Run.Name)"}
$CombinedGB=$PeakPrivate/1GB
if($Memory.Success){$CombinedGB=($PeakPrivate+[double]$Memory.Groups[1].Value*1MB)/1GB}
if($CombinedGB-ge8.0){throw "Warmup profile exceeded private + DXGI-local 8 GiB ceiling: $($Run.Name) ($CombinedGB GiB)"}
$ReadyMs=if($Ready.Success){[math]::Round([double]$Ready.Groups[1].Value*1000,3)}else{'not_applicable'}
Write-Output "STEP14_PROFILE_RESULT name=$($Run.Name) cpu_workers_idle=$($Run.Idle.ToString().ToLowerInvariant()) warmup_ms=$($Warmup.Groups[1].Value) ready_ms=$ReadyMs host_process_exit_ms=$([math]::Round($HostRunClock.Elapsed.TotalMilliseconds,3)) peak_private_gb=$([math]::Round($PeakPrivate/1GB,2)) conservative_private_plus_dxgi_gb=$([math]::Round($CombinedGB,2)) exit=$ViewerExitCode"
if($ViewerExitCode-ne0){throw "Viewer returned $ViewerExitCode`: $($Run.Name)"}
break
}
}
@@ -0,0 +1,80 @@
# GPU port, step 14: warm-up attribution and near-detail compile cost
Date: 2026-10-07. All edits and generated artifacts are under `Standalone\`. MSVC builds used `/MP8`; viewer and probe processes ran hidden at BelowNormal priority and affinity `0xFF00`. No `0xC0000142` retry was needed, maximum conservative private plus DXGI-local memory was 5.44 GiB, and no cache or driver settings were changed. No viewer remained open, and no full-size capture was opened in an image viewer. No commit, push, or stash was made.
## Result
The production warm-up was not the sum of isolated shader first-use times. In the production queue, `ApplyNearFieldDetailCS` accounts for most of the first-use stall, and its driver submission cost is paid on the immediate-context `Flush`. Creating its shader object, making warm-up resources, binding state, dispatching, and waiting on the final event are all small by comparison. A control that creates every shader object and warm-up resource but skips only the near-detail dispatch cuts the warm-up substantially; the connector-ring dispatch then becomes the next visible first-use stall.
The near-detail HLSL now shares the anchored/direct owner-roughness loop body and evaluates standard/ridged fractal outputs from the same Perlin samples. The Step 13 `ApplyNearFieldDetailCS` bytecode was 448,588 bytes; the Step 14 bytecode is 120,860 bytes, a 73.1% reduction. Its isolated first-use queue fell from 1,595.548 ms to 709.471 ms, a 55.5% reduction.
Production warm-up fell from the Step 13 median of 2,041.698 ms to 1,052.313 ms. Whole-view readiness did not improve with it: the Step 14 interleaved race measured 1.352 s CPU versus 3.196 s hybrid. Most shader warm-up overlaps CPU shape compilation; the remaining hybrid floor is the tile stream after warm-up.
## Warm-up attribution
The viewer now has an opt-in `--gpu-far-warmup-kernel-fences` profile. It records pipeline shader-object creation, warm-up resource creation, resource/state binding, each first dispatch, event-query creation, `Flush`, and completion fence, in production dispatch order. The normal production queue remains batched and uses one final event. `--gpu-far-warmup-before-workers` runs the same path before CPU generation workers start.
Baseline Step 13 shader, per-kernel fenced:
| Stage | CPU workers active | CPU workers idle |
|---|---:|---:|
| Shader pipeline creation, all entries | 52.733 ms | 61.128 ms |
| `ApplyNearFieldDetailCS` shader-object creation | 16.586 ms | 18.803 ms |
| Warm-up resource creation | 2.356 ms | 2.395 ms |
| Near-detail state binding | 0.005 ms | 0.006 ms |
| Near-detail dispatch call | 0.002 ms | 0.003 ms |
| Near-detail `Flush` | **1,980.816 ms** | **1,734.219 ms** |
| Near-detail completion fence | 7.101 ms | 8.804 ms |
| Entire fenced warm-up | 2,139.680 ms | 1,892.968 ms |
The skip control used the same shader-object creation and resources, then fenced all production entries except `ApplyNearFieldDetailCS`. Its idle-worker queue was 520.764 ms; `BuildFarConnectorRingsCS` took 363.553 ms in `Flush`. With near detail dispatched first, that ring `Flush` was under 0.4 ms in both baseline profiles. This demonstrates why adding isolated cold costs overcounts production: the entries share driver-side first-use work. The near-detail dispatch accounts for the majority of the warm-up, while the connector ring is the secondary cold entry.
The final Step 14 per-kernel profile measured near-detail `Flush` at 964.597 ms with workers active and 667.969 ms with workers idle. The corresponding complete fenced queues were 1,138.410 ms and 837.211 ms. The final skip-near-detail control was 535.680 ms, with a 382.239 ms connector-ring `Flush`.
The evidence identifies first-use driver work on the shader submission path: direct dispatch calls remain about 0.002–0.006 ms, object creation is milliseconds, resource creation is about 2 ms, and the long delay lands in `Flush`; the skip control removes that dispatch while leaving object creation intact. The D3D11 API does not expose a driver compiler timer, so the exact internal AMD operation cannot be separated from other driver translation/validation work. The measured behavior is consistent with synchronous first-use shader translation/compilation at `Flush`, serialized by the per-entry control.
## Near-detail restructuring
- Kept the generic looped value-noise routine introduced in Step 13.
- Replaced separate anchored (step 8) and direct (step 2) owner-roughness call paths with a `[loop]` over those two sample modes. The direct sample is still skipped when passage influence is at or below 0.40.
- Added `NearOwnerFractalPair`, which computes the standard and ridged accumulators from one Perlin sample per octave. The owner noise type selects the same prior output, including the averaged type.
- `ApplyNearFieldDetailCS` does not call `NearFieldDensityAt` or the root-refinement helper, so root refinement was not part of this kernel and was not removed.
The isolated shader profile after both changes reported object creation 5.831 ms, state binding 0.001 ms, dispatch 0.003 ms, `Flush` 689.707 ms, event wait 16.272 ms, and queue 709.471 ms. The event wait includes the profiling loop's 1 ms polling interval; it is not a GPU execution-time estimate.
## Correctness, survival, and captures
- Whole near audit: **960/960 tiles**, zero hardware topology differences, zero WARP topology differences, zero differences across **229,376 protected cells**, and bit-identical repeated GPU output.
- Whole far audit: **1,114** eligible tiles, 152 near-transition tiles skipped, zero hardware and WARP topology differences, zero differences across **127,401 protected cells** and **353 thin-feature cells**, and bit-identical repeated GPU output.
- CPU reference at 1 and 8 workers: identical density `0xdb2991c73215af4c`, mesh `0x8236c5edd7fc8e4f`, payload `0x4c48fa75a543d58e`, topology `0xbea22e76b34a4dfc`; walkability **5,642/5,642**.
- Full-residency GPU survival audit passed at **5.08 GiB** conservative private plus DXGI-local memory.
- All ten 1280×800 CPU/GPU capture pairs reached full residency. Numeric RGB results exactly match the Step 13 comparison: MAE range **0.0484–1.6092**; the highest combined private plus DXGI-local sample was **5.44 GiB**. See [step14_capture_comparison.md](step14_capture_comparison.md). The PNGs were compared as arrays and not viewed.
## Interleaved whole-view race
Order was CPU A, GPU A, GPU B, CPU B. All runs used 8 workers. The GPU warm-up numbers below are the normal batched production path without per-kernel fences.
| Run | Warm-up | Viewer ready / present return | Host process exit |
|---|---:|---:|---:|
| CPU A | — | 1,353 ms | 1,522.097 ms |
| GPU A | 1,048.305 ms | 3,084 ms | 3,235.229 ms |
| GPU B | 1,056.321 ms | 3,308 ms | 3,466.244 ms |
| CPU B | — | 1,350 ms | 1,471.182 ms |
Median viewer readiness was **1.352 s CPU vs 3.196 s hybrid**: CPU leads by **1.845 s / 2.36×**. Median host-clock process exit was **1.497 s CPU vs 3.351 s hybrid**: CPU leads by **1.854 s / 2.24×**. Median warm-up was **1.052 s**, about 0.989 s below Step 13. `Present` returned `DXGI_STATUS_OCCLUDED`, so these clocks measure readiness and API return, not physical scanout.
The warm-up overlaps shape compilation: GPU shape compilation took 835–903 ms while a 1.048–1.056 s warm-up ran. After GPU input export completed at 1.302–1.306 s, whole-view readiness took another 1.778–2.003 s. Those runs queued 1,082 GPU tiles in 35 batches and kept 1,144 CPU fallback tiles; CPU input preparation was 112–119 ms wall time and measured GPU dispatch timestamps summed to 200–216 ms. The current floor is therefore the post-warm-up hybrid tile stream and CPU fallback/submission path, not near-detail compilation alone. The GPU path still loses the whole-view race.
## Lower-priority coverage case
The direct-GPU `(6,2,0,lod3)` coverage issue remains open. The viewer's established CPU fallback for remote LOD3/4 remains enabled; this step's whole-far audit skips near transitions and does not claim direct LOD3/4 coverage parity. The prior diagnostic and capture evidence remains in [STEP9_REPORT.md](STEP9_REPORT.md).
## Reproduction artifacts
- Warm-up controls: [RunStep14WarmupProfile.ps1](RunStep14WarmupProfile.ps1) and `step14_profile_*` / `step14_final_*` logs.
- Near shader compile and first-use: [CompileStep14Shader.ps1](CompileStep14Shader.ps1), [RunStep14ShaderFirstUse.ps1](RunStep14ShaderFirstUse.ps1), and the `step14_shader_*.stdout.log` files.
- Agreement: `step14_whole_near_agreement.stdout.log`, `step14_whole_far_agreement.stdout.log`.
- CPU reference: [RunStep14CpuReference.ps1](RunStep14CpuReference.ps1) and `step14_cpu_reference_w*.stdout.log`.
- Survival: [RunStep14GpuSurvival.ps1](RunStep14GpuSurvival.ps1) and `step14_gpu_survival.stdout.log`.
- Captures: [RunStep14CaptureMatrix.ps1](RunStep14CaptureMatrix.ps1), [CompareStep14Captures.py](CompareStep14Captures.py), and `step14_capture_comparison.md`.
- Race: [RunStep14FinalInterleaved.ps1](RunStep14FinalInterleaved.ps1) and `step14_race_*.stdout.log`.
@@ -0,0 +1,16 @@
# Step 14 full-residency capture comparison
All ten CPU/GPU pairs were captured after `[InitialClipmapReady]`. The files were compared numerically as RGB arrays and were not opened in an image viewer.
| View | Size | RGB MAE | RGB RMSE | Changed pixels | Pixels max channel >32 | Max channel |
|---|---:|---:|---:|---:|---:|---:|
| chamber | 1280x800 | 0.6076 | 1.8655 | 42.6368% | 0.0183% | 124 |
| tunnel | 1280x800 | 0.6383 | 3.3799 | 31.7335% | 0.2772% | 130 |
| passage | 1280x800 | 1.6092 | 3.6878 | 50.4982% | 0.1733% | 52 |
| far | 1280x800 | 0.7909 | 2.1558 | 44.8325% | 0.0198% | 128 |
| passage_floor_closeup | 1280x800 | 0.9314 | 2.4800 | 43.0219% | 0.0500% | 100 |
| tunnel_bend | 1280x800 | 0.8129 | 2.1599 | 40.9629% | 0.0708% | 55 |
| remote_yaw0 | 1280x800 | 0.1994 | 2.6632 | 4.2472% | 0.2415% | 125 |
| remote_yaw90 | 1280x800 | 0.4083 | 4.3552 | 2.2432% | 0.6948% | 94 |
| remote_yaw180 | 1280x800 | 0.0484 | 1.3206 | 2.9787% | 0.0341% | 102 |
| remote_yaw270 | 1280x800 | 0.2355 | 3.1104 | 5.5593% | 0.4098% | 107 |
@@ -289,6 +289,80 @@ FarFieldSpatialBin FarFieldFindBin(float3 position)
return emptyBin;
}
void NearOwnerFractalPair(float x,float y,float z,int octaves,
out float standardValue,out float ridgedValue)
{
float standardTotal=0.0f,ridgedTotal=0.0f,maxValue=0.0f;
float frequency=1.0f,amplitude=1.0f,weight=1.0f;
[loop]
for(int octave=0;octave<octaves;++octave)
{
const float noise=VF_FieldPerlin3D(x*frequency,y*frequency,z*frequency);
standardTotal+=noise*amplitude;
float ridge=noise*1.25f;
ridge=1.0f-abs(ridge);
ridge=ridge*ridge;
ridge*=weight;
weight=clamp(ridge*2.0f,0.0f,1.0f);
ridgedTotal+=ridge*amplitude;
maxValue+=amplitude;
amplitude*=0.5f;
frequency*=2.0f;
}
standardValue=standardTotal/maxValue;
ridgedValue=(ridgedTotal/maxValue)*2.0f-1.0f;
}
float NearOwnerRoughnessCore(float x,float y,float z,uint seed,
float surfaceRoughness,float roughnessFrequency,float verticalScale,
float domainWarpStrength,float domainWarpFrequency,uint noiseType)
{
if(!(roughnessFrequency>0.0f))return 0.0f;
const float effectiveZ=verticalScale>0.0f&&verticalScale!=1.0f?z/verticalScale:z;
const float mainX=x*roughnessFrequency+VF_FieldVoxelSeedOffset(seed,11.3f);
const float mainY=y*roughnessFrequency+VF_FieldVoxelSeedOffset(seed,13.7f);
const float mainZ=effectiveZ*roughnessFrequency+VF_FieldVoxelSeedOffset(seed,17.1f);
float fineX=x*roughnessFrequency*3.0f+VF_FieldVoxelSeedOffset(seed,19.1f)+2000.0f;
float fineY=y*roughnessFrequency*3.0f+VF_FieldVoxelSeedOffset(seed,23.7f)+2500.0f;
float fineZ=effectiveZ*roughnessFrequency*3.0f+VF_FieldVoxelSeedOffset(seed,29.3f)+3000.0f;
float mx=mainX,my=mainY,mz=mainZ;
if(domainWarpStrength>0.0f)
{
const float wf=domainWarpFrequency,ws=domainWarpStrength;
float wx=VF_FieldPerlin3D(x*wf+VF_FieldVoxelSeedOffset(seed,5.2f),
y*wf+VF_FieldVoxelSeedOffset(seed,1.3f),effectiveZ*wf+VF_FieldVoxelSeedOffset(seed,9.7f));
float wy=VF_FieldPerlin3D(x*wf+100.0f+VF_FieldVoxelSeedOffset(seed,7.7f),
y*wf+200.0f+VF_FieldVoxelSeedOffset(seed,3.1f),effectiveZ*wf+300.0f);
float wz=VF_FieldPerlin3D(x*wf+400.0f,y*wf+500.0f+VF_FieldVoxelSeedOffset(seed,11.9f),
effectiveZ*wf+600.0f+VF_FieldVoxelSeedOffset(seed,13.3f));
wx*=1.25f*ws;wy*=1.25f*ws;wz*=1.25f*ws;
mx+=wx;my+=wy;mz+=wz;
fineX+=wx;fineY+=wy;fineZ+=wz;
}
float rough=0.0f,fine=0.0f;
if(noiseType==3u)
{
rough=VF_FieldOwnerCellular(mx,my,mz);
fine=VF_FieldOwnerCellular(fineX,fineY,fineZ);
}
else
{
float roughStandard,roughRidged,fineStandard,fineRidged;
NearOwnerFractalPair(mx,my,mz,3,roughStandard,roughRidged);
NearOwnerFractalPair(fineX,fineY,fineZ,2,fineStandard,fineRidged);
if(noiseType==1u)
{ rough=roughRidged;fine=fineRidged; }
else if(noiseType==2u)
{
rough=(roughStandard+roughRidged)*0.5f;
fine=(fineStandard+fineRidged)*0.5f;
}
else
{ rough=roughStandard;fine=fineStandard; }
}
return (rough+fine*0.4f)*1.25f*surfaceRoughness;
}
float NearOwnerRoughnessAtStep(float3 position,float surfaceSdf,float caveInfluence,
FarFieldDetailLayer layer,uint anchorStep)
{
@@ -301,17 +375,17 @@ float NearOwnerRoughnessAtStep(float3 position,float surfaceSdf,float caveInflue
float total;
if(sampleZ1>sampleZ0)
{
const float lower=VF_FieldOwnerRoughnessCore(position.x,position.y,sampleZ0,
const float lower=NearOwnerRoughnessCore(position.x,position.y,sampleZ0,
FarFieldSeed,layer.surfaceRoughness,layer.roughnessFrequency,layer.verticalScale,
layer.domainWarpStrength,layer.domainWarpFrequency,layer.roughnessNoiseType);
const float upper=VF_FieldOwnerRoughnessCore(position.x,position.y,sampleZ1,
const float upper=NearOwnerRoughnessCore(position.x,position.y,sampleZ1,
FarFieldSeed,layer.surfaceRoughness,layer.roughnessFrequency,layer.verticalScale,
layer.domainWarpStrength,layer.domainWarpFrequency,layer.roughnessNoiseType);
const float alpha=(position.z-sampleZ0)/(sampleZ1-sampleZ0);
total=lower+(upper-lower)*alpha;
}
else
total=VF_FieldOwnerRoughnessCore(position.x,position.y,position.z,
total=NearOwnerRoughnessCore(position.x,position.y,position.z,
FarFieldSeed,layer.surfaceRoughness,layer.roughnessFrequency,layer.verticalScale,
layer.domainWarpStrength,layer.domainWarpFrequency,layer.roughnessNoiseType);
if(surfaceSdf<0.0f)
@@ -324,9 +398,18 @@ float NearOwnerRoughness(float3 position,float surfaceSdf,float caveInfluence,
float passageInfluence,FarFieldDetailLayer layer)
{
const float passage=saturate(passageInfluence);
const float anchored=NearOwnerRoughnessAtStep(position,surfaceSdf,caveInfluence,layer,8);
float anchored=0.0f,direct=0.0f;
[loop]
for(uint sampleIndex=0;sampleIndex<2;++sampleIndex)
{
if(sampleIndex==1&&passage<=0.40f)continue;
const uint anchorStep=sampleIndex==0?8u:2u;
const float value=NearOwnerRoughnessAtStep(position,surfaceSdf,caveInfluence,
layer,anchorStep);
if(sampleIndex==0)anchored=value;
else direct=value;
}
if(passage<=0.40f)return anchored;
const float direct=NearOwnerRoughnessAtStep(position,surfaceSdf,caveInfluence,layer,2);
if(passage>=0.60f)return direct;
const float directWeight=VF_FieldSmoothUnit((passage-0.40f)/0.20f);
const float anchoredWeight=1.0f-directWeight;