wip(standalone): GPU step 12 -- far evaluator cold use 1704 -> 3 ms; near detail now the floor
Split ordinary far evaluation from near-detail evaluation; [loop] in connector refinement and skirt clipping. Cold first use: EvaluateFarField 1704 -> 3.3 ms, connector rings 775 -> 630, skirt emit 948 -> 909, near detail 1783 ms (the floor). Warm-up still ~2.2 s; whole view CPU 1.351 s vs hybrid 3.210 s. Output-slot reuse fixed: crossing probe 1/1, 2,226/2,226 tiles. Audits: 0 topology/protected/thin diffs vs CPU and WARP, hashes + walk unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
1 parent
7f64e2e0aa
commit
05b1523c54
24 files changed
+1033
-66
No files matched your search
@@ -120,7 +120,7 @@ if ($Target -eq 'All') {
|
|||||||
Sort-Object -Property FullName -Descending | Select-Object -First 1
|
Sort-Object -Property FullName -Descending | Select-Object -First 1
|
||||||
if ($FxC) {
|
if ($FxC) {
|
||||||
[IO.Directory]::CreateDirectory($PrecompiledShaderDir) | Out-Null
|
[IO.Directory]::CreateDirectory($PrecompiledShaderDir) | Out-Null
|
||||||
foreach ($Entry in @('EvaluateFarFieldCS','ClassifyCS','ClassifyFarCS','ScanGroupsCS','EmitCS','EmitNearCS','NoiseProbeCS','BuildFarConnectorRingsCS','EmitFarConnectorPanelsCS','ClassifyFarSkirtsCS','ScanFarSkirtsCS','EmitFarSkirtsCS')) {
|
foreach ($Entry in @('EvaluateFarFieldCS','EvaluateNearFieldCS','ClassifyCS','ClassifyFarCS','ScanGroupsCS','EmitCS','EmitNearCS','NoiseProbeCS','BuildFarConnectorRingsCS','EmitFarConnectorPanelsCS','ClassifyFarSkirtsCS','ScanFarSkirtsCS','EmitFarSkirtsCS')) {
|
||||||
$ShaderBinary = Join-Path $PrecompiledShaderDir ($Entry+'.cso')
|
$ShaderBinary = Join-Path $PrecompiledShaderDir ($Entry+'.cso')
|
||||||
$CommandLine = '"{0}" /nologo /T cs_5_0 /E {1} /O3 /Gis /Ges /Fo "{2}" "{3}"' -f $FxC.FullName,$Entry,$ShaderBinary,$ShaderSource
|
$CommandLine = '"{0}" /nologo /T cs_5_0 /E {1} /O3 /Gis /Ges /Fo "{2}" "{3}"' -f $FxC.FullName,$Entry,$ShaderBinary,$ShaderSource
|
||||||
Invoke-FieldViewerBuildCommand $CommandLine "precompile HLSL entry $Entry"
|
Invoke-FieldViewerBuildCommand $CommandLine "precompile HLSL entry $Entry"
|
||||||
|
|||||||
@@ -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"step12_cpu_{view}.png"
|
||||||
|
gpu_path = ROOT / f"step12_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 12 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 / "step12_capture_comparison.md"
|
||||||
|
report.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
||||||
|
print(report.read_text(encoding="utf-8"), end="")
|
||||||
@@ -551,7 +551,7 @@ namespace
|
|||||||
ComPtr<ID3D11Device> device;
|
ComPtr<ID3D11Device> device;
|
||||||
ComPtr<ID3D11DeviceContext> context;
|
ComPtr<ID3D11DeviceContext> context;
|
||||||
ComPtr<ID3D11DeviceContext> immediateContext,deferredContext;
|
ComPtr<ID3D11DeviceContext> immediateContext,deferredContext;
|
||||||
ComPtr<ID3D11ComputeShader> fieldEval, classify, fieldClassify, scan, emit, emitNear, noiseProbe;
|
ComPtr<ID3D11ComputeShader> fieldEval, fieldEvalNear, classify, fieldClassify, scan, emit, emitNear, noiseProbe;
|
||||||
ComPtr<ID3D11ComputeShader> connectorRings,connectorPanels;
|
ComPtr<ID3D11ComputeShader> connectorRings,connectorPanels;
|
||||||
ComPtr<ID3D11ComputeShader> classifyFarSkirts,scanFarSkirts,emitFarSkirts;
|
ComPtr<ID3D11ComputeShader> classifyFarSkirts,scanFarSkirts,emitFarSkirts;
|
||||||
ComPtr<IDXGIAdapter3> memoryAdapter;
|
ComPtr<IDXGIAdapter3> memoryAdapter;
|
||||||
@@ -595,6 +595,7 @@ namespace
|
|||||||
entry,std::chrono::duration<double,std::milli>(std::chrono::steady_clock::now()-CreateStart).count());
|
entry,std::chrono::duration<double,std::milli>(std::chrono::steady_clock::now()-CreateStart).count());
|
||||||
};
|
};
|
||||||
Create("EvaluateFarFieldCS",fieldEval);
|
Create("EvaluateFarFieldCS",fieldEval);
|
||||||
|
Create("EvaluateNearFieldCS",fieldEvalNear);
|
||||||
Create("ClassifyCS",classify);
|
Create("ClassifyCS",classify);
|
||||||
Create("ClassifyFarCS",fieldClassify);
|
Create("ClassifyFarCS",fieldClassify);
|
||||||
Create("ScanGroupsCS",scan);
|
Create("ScanGroupsCS",scan);
|
||||||
@@ -613,8 +614,8 @@ namespace
|
|||||||
void QueueTinyShaderWarmup(bool dispatchShaders,const std::string& entryFilter)
|
void QueueTinyShaderWarmup(bool dispatchShaders,const std::string& entryFilter)
|
||||||
{
|
{
|
||||||
const auto WarmupStart=Clock::now();
|
const auto WarmupStart=Clock::now();
|
||||||
static constexpr std::array<std::string_view,12> entries={
|
static constexpr std::array<std::string_view,13> entries={
|
||||||
"EvaluateFarFieldCS","ClassifyCS","ClassifyFarCS","ScanGroupsCS","EmitCS",
|
"EvaluateFarFieldCS","EvaluateNearFieldCS","ClassifyCS","ClassifyFarCS","ScanGroupsCS","EmitCS",
|
||||||
"EmitNearCS","BuildFarConnectorRingsCS","EmitFarConnectorPanelsCS",
|
"EmitNearCS","BuildFarConnectorRingsCS","EmitFarConnectorPanelsCS",
|
||||||
"ClassifyFarSkirtsCS","ScanFarSkirtsCS","EmitFarSkirtsCS","NoiseProbeCS"};
|
"ClassifyFarSkirtsCS","ScanFarSkirtsCS","EmitFarSkirtsCS","NoiseProbeCS"};
|
||||||
if(!entryFilter.empty()&&std::find(entries.begin(),entries.end(),entryFilter)==entries.end())
|
if(!entryFilter.empty()&&std::find(entries.begin(),entries.end(),entryFilter)==entries.end())
|
||||||
@@ -623,7 +624,8 @@ namespace
|
|||||||
TileConstants tile{};
|
TileConstants tile{};
|
||||||
tile.cells=1;tile.sampleDim=3;tile.cellCount=cellCount;tile.groupCount=groupCount;
|
tile.cells=1;tile.sampleDim=3;tile.cellCount=cellCount;tile.groupCount=groupCount;
|
||||||
tile.stepVoxels=entryFilter=="EmitNearCS"?1:2;
|
tile.stepVoxels=entryFilter=="EmitNearCS"?1:2;
|
||||||
tile.nearFieldMode=entryFilter=="EmitNearCS"?1u:0u;
|
tile.nearFieldMode=(entryFilter.empty()||entryFilter=="EvaluateNearFieldCS"
|
||||||
|
||entryFilter=="EmitNearCS")?1u:0u;
|
||||||
tile.voxelSizeCm=25.0f;tile.outputVertexCapacity=3;
|
tile.voxelSizeCm=25.0f;tile.outputVertexCapacity=3;
|
||||||
FarFieldConstants field{};
|
FarFieldConstants field{};
|
||||||
field.strataLo=-100.0f;field.strataHi=100.0f;field.strataBlend=12.0f;
|
field.strataLo=-100.0f;field.strataHi=100.0f;field.strataBlend=12.0f;
|
||||||
@@ -772,6 +774,7 @@ namespace
|
|||||||
evalUavs[5]=nearNormalUav.Get();evalUavs[6]=evalCounterUav.Get();evalUavs[7]=densityUav.Get();
|
evalUavs[5]=nearNormalUav.Get();evalUavs[6]=evalCounterUav.Get();evalUavs[7]=densityUav.Get();
|
||||||
context->CSSetUnorderedAccessViews(0,8,evalUavs,nullptr);
|
context->CSSetUnorderedAccessViews(0,8,evalUavs,nullptr);
|
||||||
Dispatch(fieldEval.Get(),"EvaluateFarFieldCS");ClearBindings();
|
Dispatch(fieldEval.Get(),"EvaluateFarFieldCS");ClearBindings();
|
||||||
|
Dispatch(fieldEvalNear.Get(),"EvaluateNearFieldCS");ClearBindings();
|
||||||
|
|
||||||
ID3D11ShaderResourceView* classifySrvs[16]{};
|
ID3D11ShaderResourceView* classifySrvs[16]{};
|
||||||
classifySrvs[0]=signSrv.Get();classifySrvs[1]=triangleSrv.Get();
|
classifySrvs[0]=signSrv.Get();classifySrvs[1]=triangleSrv.Get();
|
||||||
@@ -1612,7 +1615,7 @@ namespace
|
|||||||
beginKernel(0);
|
beginKernel(0);
|
||||||
context->CSSetConstantBuffers(1,1,farCb.GetAddressOf());
|
context->CSSetConstantBuffers(1,1,farCb.GetAddressOf());
|
||||||
context->CSSetConstantBuffers(2,1,farConnectorConstantsBuffer.GetAddressOf());
|
context->CSSetConstantBuffers(2,1,farConnectorConstantsBuffer.GetAddressOf());
|
||||||
context->CSSetShader(fieldEval.Get(),nullptr,0);
|
context->CSSetShader(constants.nearFieldMode!=0?fieldEvalNear.Get():fieldEval.Get(),nullptr,0);
|
||||||
ID3D11ShaderResourceView* fieldSrvs[18]{};
|
ID3D11ShaderResourceView* fieldSrvs[18]{};
|
||||||
fieldSrvs[10]=farShapeSrv.Get();fieldSrvs[11]=farRangeSrv.Get();
|
fieldSrvs[10]=farShapeSrv.Get();fieldSrvs[11]=farRangeSrv.Get();
|
||||||
fieldSrvs[12]=farIdSrv.Get();fieldSrvs[13]=farProtectedSrv.Get();
|
fieldSrvs[12]=farIdSrv.Get();fieldSrvs[13]=farProtectedSrv.Get();
|
||||||
|
|||||||
@@ -0,0 +1,80 @@
|
|||||||
|
[CmdletBinding()]
|
||||||
|
param([string]$ExecutableName='VoxelForgeGpuFieldStep12Warmup.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="step12_${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 "STEP12_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()
|
||||||
|
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 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=$($Child.ExitCode)"}
|
||||||
|
$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 "STEP12_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=$($Child.ExitCode)"
|
||||||
|
if($Child.ExitCode-ne0){throw "Capture viewer returned $($Child.ExitCode): $Stem"}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+68
@@ -0,0 +1,68 @@
|
|||||||
|
[CmdletBinding()]
|
||||||
|
param([string]$ExecutableName='VoxelForgeGpuFieldStep12Warmup.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"}
|
||||||
|
|
||||||
|
$Entries=@('BuildFarConnectorRingsCS')
|
||||||
|
$EightGB=8L*1024L*1024L*1024L
|
||||||
|
foreach($Entry in $Entries)
|
||||||
|
{
|
||||||
|
$Name='step12_loop_connector_shader_'+$Entry
|
||||||
|
$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")
|
||||||
|
Write-Output "STEP12_LOOP_CONNECTOR_SHADER_RUN entry=$Entry freshness=fresh_process 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();$HostRunMs=$HostRunClock.Elapsed.TotalMilliseconds
|
||||||
|
$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 "Warmup process stopped at the 8 GiB private-memory ceiling: $Entry"}
|
||||||
|
$LogText=Get-Content -LiteralPath $StdoutPath -Raw
|
||||||
|
$Selected=[regex]::Match($LogText,'(?m)^\[GpuShaderWarmup\] dispatched=1 selected='+[regex]::Escape($Entry)+'.*queue_ms=([0-9.]+)')
|
||||||
|
$Dispatch=[regex]::Match($LogText,'(?m)^\[GpuShaderWarmup\] entry='+[regex]::Escape($Entry)+' dispatch_call_ms=([0-9.]+)')
|
||||||
|
$Wait=[regex]::Match($LogText,'(?m)^\[GpuShaderWarmup\] completion_wait_ms=([0-9.]+)')
|
||||||
|
$Create=[regex]::Match($LogText,'(?m)^\[GpuPipelineProfile\] stage=shader_object_create entry='+[regex]::Escape($Entry)+' duration_ms=([0-9.]+)')
|
||||||
|
if(-not$Selected.Success-or-not$Dispatch.Success-or-not$Wait.Success-or-not$Create.Success)
|
||||||
|
{throw "Isolated warmup markers missing for $Entry; exit=$ViewerExitCode"}
|
||||||
|
$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 "Warmup exceeded conservative private + DXGI-local 8 GiB ceiling: $Entry ($CombinedGB GiB)"}
|
||||||
|
Write-Output "STEP12_LOOP_CONNECTOR_SHADER_RESULT entry=$Entry object_create_ms=$($Create.Groups[1].Value) dispatch_call_ms=$($Dispatch.Groups[1].Value) isolated_queue_ms=$($Selected.Groups[1].Value) completion_wait_ms=$($Wait.Groups[1].Value) 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"
|
||||||
|
if($ViewerExitCode-ne0){throw "Warmup process returned $ViewerExitCode for $Entry"}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
[CmdletBinding()]
|
||||||
|
param([string]$ExecutableName='VoxelForgeGpuFieldStep12Warmup.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="step12_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 "STEP12_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($PeakPrivate-ge8GB)
|
||||||
|
{$OverMemoryLimit=$true;$Child.Kill();$Child.WaitForExit();break}
|
||||||
|
Start-Sleep -Milliseconds 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if(-not$Child.HasExited){$Child.Kill();$Child.WaitForExit()}
|
||||||
|
}
|
||||||
|
$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 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=$($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 "STEP12_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,77 @@
|
|||||||
|
[CmdletBinding()]
|
||||||
|
param([string]$ExecutableName='VoxelForgeGpuFieldStep12Warmup.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='step12_race_cpu_A'},
|
||||||
|
@{Mode='gpu';Name='step12_race_gpu_A'},
|
||||||
|
@{Mode='gpu';Name='step12_race_gpu_B'},
|
||||||
|
@{Mode='cpu';Name='step12_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 "STEP12_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=$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 "STEP12_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,70 @@
|
|||||||
|
[CmdletBinding()]
|
||||||
|
param([string]$ExecutableName='VoxelForgeGpuFieldStep12Warmup.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"}
|
||||||
|
|
||||||
|
$Entries=@('EvaluateFarFieldCS','EvaluateNearFieldCS','ClassifyCS','ClassifyFarCS','ScanGroupsCS','EmitCS','EmitNearCS',
|
||||||
|
'BuildFarConnectorRingsCS','EmitFarConnectorPanelsCS','ClassifyFarSkirtsCS',
|
||||||
|
'ScanFarSkirtsCS','EmitFarSkirtsCS','NoiseProbeCS')
|
||||||
|
$EightGB=8L*1024L*1024L*1024L
|
||||||
|
foreach($Entry in $Entries)
|
||||||
|
{
|
||||||
|
$Name='step12_final_shader_'+$Entry
|
||||||
|
$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")
|
||||||
|
Write-Output "STEP12_FINAL_SHADER_RUN entry=$Entry freshness=fresh_process 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();$HostRunMs=$HostRunClock.Elapsed.TotalMilliseconds
|
||||||
|
$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 "Warmup process stopped at the 8 GiB private-memory ceiling: $Entry"}
|
||||||
|
$LogText=Get-Content -LiteralPath $StdoutPath -Raw
|
||||||
|
$Selected=[regex]::Match($LogText,'(?m)^\[GpuShaderWarmup\] dispatched=1 selected='+[regex]::Escape($Entry)+'.*queue_ms=([0-9.]+)')
|
||||||
|
$Dispatch=[regex]::Match($LogText,'(?m)^\[GpuShaderWarmup\] entry='+[regex]::Escape($Entry)+' dispatch_call_ms=([0-9.]+)')
|
||||||
|
$Wait=[regex]::Match($LogText,'(?m)^\[GpuShaderWarmup\] completion_wait_ms=([0-9.]+)')
|
||||||
|
$Create=[regex]::Match($LogText,'(?m)^\[GpuPipelineProfile\] stage=shader_object_create entry='+[regex]::Escape($Entry)+' duration_ms=([0-9.]+)')
|
||||||
|
if(-not$Selected.Success-or-not$Dispatch.Success-or-not$Wait.Success-or-not$Create.Success)
|
||||||
|
{throw "Isolated warmup markers missing for $Entry; exit=$ViewerExitCode"}
|
||||||
|
$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 "Warmup exceeded conservative private + DXGI-local 8 GiB ceiling: $Entry ($CombinedGB GiB)"}
|
||||||
|
Write-Output "STEP12_FINAL_SHADER_RESULT entry=$Entry object_create_ms=$($Create.Groups[1].Value) dispatch_call_ms=$($Dispatch.Groups[1].Value) isolated_queue_ms=$($Selected.Groups[1].Value) completion_wait_ms=$($Wait.Groups[1].Value) 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"
|
||||||
|
if($ViewerExitCode-ne0){throw "Warmup process returned $ViewerExitCode for $Entry"}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[string]$ExecutableName='VoxelForgeGpuFieldStep12Warmup.exe',
|
||||||
|
[string]$LogStem='step12_gpu_crossing',
|
||||||
|
[int]$FlightSpeedMps=16
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference='Stop'
|
||||||
|
if([IO.Path]::GetFileName($ExecutableName)-ne$ExecutableName-or[IO.Path]::GetExtension($ExecutableName)-ne'.exe')
|
||||||
|
{throw 'ExecutableName must be an executable filename.'}
|
||||||
|
if([IO.Path]::GetFileName($LogStem)-ne$LogStem){throw 'LogStem must be a filename stem.'}
|
||||||
|
$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)
|
||||||
|
$StdoutPath=Join-Path $PSScriptRoot ($LogStem+'.stdout.log')
|
||||||
|
$StderrPath=Join-Path $PSScriptRoot ($LogStem+'.stderr.log')
|
||||||
|
if(-not(Test-Path -LiteralPath $ExecutablePath -PathType Leaf)){throw "Viewer not found: $ExecutablePath"}
|
||||||
|
$Arguments=@('--gpu-far','--clip-radius=3','--workers=8','--test-one-chunk-crossing',"--flight-speed-mps=$FlightSpeedMps")
|
||||||
|
Write-Output "VIEWER_COMMAND `"$ExecutablePath`" $($Arguments -join ' ')"
|
||||||
|
for($Attempt=0;$Attempt -lt 2;$Attempt++)
|
||||||
|
{
|
||||||
|
if($Attempt-gt 0)
|
||||||
|
{
|
||||||
|
Write-Host '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
|
||||||
|
$PeakWorkingSet=0L;$PeakPrivateMemory=0L;$OverMemoryLimit=$false
|
||||||
|
while(-not$Child.HasExited)
|
||||||
|
{
|
||||||
|
$Child.Refresh()
|
||||||
|
if($Child.HasExited){break}
|
||||||
|
$Child.PriorityClass='BelowNormal'
|
||||||
|
$Child.ProcessorAffinity=[IntPtr]0xFF00
|
||||||
|
$PeakWorkingSet=[Math]::Max($PeakWorkingSet,$Child.WorkingSet64)
|
||||||
|
$PeakPrivateMemory=[Math]::Max($PeakPrivateMemory,$Child.PrivateMemorySize64)
|
||||||
|
if($Child.PrivateMemorySize64-ge5GB)
|
||||||
|
{$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 "Viewer stopped at the private-memory limit; stdout=$StdoutPath stderr=$StderrPath"}
|
||||||
|
$LogText=Get-Content -LiteralPath $StdoutPath -Raw
|
||||||
|
$Memory=[regex]::Match($LogText,'(?m)^\[GpuFarMemory\] dxgi_local_sampled_peak_mb=([0-9.]+)')
|
||||||
|
if(-not$Memory.Success){throw 'GPU local-memory marker missing from crossing run.'}
|
||||||
|
$CombinedGB=($PeakPrivateMemory+[double]$Memory.Groups[1].Value*1MB)/1GB
|
||||||
|
if($CombinedGB-ge8.0){throw "Viewer exceeded the 8 GiB combined memory limit: $CombinedGB GiB"}
|
||||||
|
$Complete=$LogText.Contains('[FlightBenchmarkComplete]')
|
||||||
|
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)) dxgi_local_peak_mb=$($Memory.Groups[1].Value) conservative_private_plus_dxgi_gb=$([math]::Round($CombinedGB,2)) exit_code=$($Child.ExitCode) crossing_complete=$Complete stdout=$StdoutPath stderr=$StderrPath"
|
||||||
|
if(-not$Complete){exit 4}
|
||||||
|
exit $Child.ExitCode
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
[CmdletBinding()]
|
||||||
|
param([string]$ExecutableName='VoxelForgeGpuFieldStep12Warmup.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='step12_gpu_survival'
|
||||||
|
$StdoutPath=Join-Path $PSScriptRoot ($Name+'.stdout.log')
|
||||||
|
$StderrPath=Join-Path $PSScriptRoot ($Name+'.stderr.log')
|
||||||
|
$EightGB=8L*1024L*1024L*1024L
|
||||||
|
Write-Output "STEP12_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()
|
||||||
|
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 exceeded conservative private + DXGI-local 8 GiB ceiling: $CombinedGB GiB"}
|
||||||
|
Write-Output "STEP12_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,77 @@
|
|||||||
|
[CmdletBinding()]
|
||||||
|
param([string]$ExecutableName='VoxelForgeGpuFieldStep12Warmup.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='step12_postpool_race_cpu_A'},
|
||||||
|
@{Mode='gpu';Name='step12_postpool_race_gpu_A'},
|
||||||
|
@{Mode='gpu';Name='step12_postpool_race_gpu_B'},
|
||||||
|
@{Mode='cpu';Name='step12_postpool_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 "STEP12_POSTPOOL_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=$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 "STEP12_POSTPOOL_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,70 @@
|
|||||||
|
[CmdletBinding()]
|
||||||
|
param([string]$ExecutableName='VoxelForgeGpuFieldStep11Warmup.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"}
|
||||||
|
|
||||||
|
$Entries=@('EvaluateFarFieldCS','ClassifyCS','ClassifyFarCS','ScanGroupsCS','EmitCS','EmitNearCS',
|
||||||
|
'BuildFarConnectorRingsCS','EmitFarConnectorPanelsCS','ClassifyFarSkirtsCS',
|
||||||
|
'ScanFarSkirtsCS','EmitFarSkirtsCS','NoiseProbeCS')
|
||||||
|
$EightGB=8L*1024L*1024L*1024L
|
||||||
|
foreach($Entry in $Entries)
|
||||||
|
{
|
||||||
|
$Name='step12_baseline_shader_'+$Entry
|
||||||
|
$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")
|
||||||
|
Write-Output "STEP12_BASELINE_SHADER_RUN entry=$Entry freshness=fresh_process 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();$HostRunMs=$HostRunClock.Elapsed.TotalMilliseconds
|
||||||
|
$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 "Warmup process stopped at the 8 GiB private-memory ceiling: $Entry"}
|
||||||
|
$LogText=Get-Content -LiteralPath $StdoutPath -Raw
|
||||||
|
$Selected=[regex]::Match($LogText,'(?m)^\[GpuShaderWarmup\] dispatched=1 selected='+[regex]::Escape($Entry)+'.*queue_ms=([0-9.]+)')
|
||||||
|
$Dispatch=[regex]::Match($LogText,'(?m)^\[GpuShaderWarmup\] entry='+[regex]::Escape($Entry)+' dispatch_call_ms=([0-9.]+)')
|
||||||
|
$Wait=[regex]::Match($LogText,'(?m)^\[GpuShaderWarmup\] completion_wait_ms=([0-9.]+)')
|
||||||
|
$Create=[regex]::Match($LogText,'(?m)^\[GpuPipelineProfile\] stage=shader_object_create entry='+[regex]::Escape($Entry)+' duration_ms=([0-9.]+)')
|
||||||
|
if(-not$Selected.Success-or-not$Dispatch.Success-or-not$Wait.Success-or-not$Create.Success)
|
||||||
|
{throw "Isolated warmup markers missing for $Entry; exit=$ViewerExitCode"}
|
||||||
|
$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 "Warmup exceeded conservative private + DXGI-local 8 GiB ceiling: $Entry ($CombinedGB GiB)"}
|
||||||
|
Write-Output "STEP12_BASELINE_SHADER_RESULT entry=$Entry object_create_ms=$($Create.Groups[1].Value) dispatch_call_ms=$($Dispatch.Groups[1].Value) isolated_queue_ms=$($Selected.Groups[1].Value) completion_wait_ms=$($Wait.Groups[1].Value) 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"
|
||||||
|
if($ViewerExitCode-ne0){throw "Warmup process returned $ViewerExitCode for $Entry"}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
[CmdletBinding()]
|
||||||
|
param([string]$ExecutableName='VoxelForgeGpuFieldStep12Warmup.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"}
|
||||||
|
|
||||||
|
$Entries=@('EvaluateFarFieldCS','EvaluateNearFieldCS','ClassifyCS','ClassifyFarCS','ScanGroupsCS','EmitCS','EmitNearCS',
|
||||||
|
'BuildFarConnectorRingsCS','EmitFarConnectorPanelsCS','ClassifyFarSkirtsCS',
|
||||||
|
'ScanFarSkirtsCS','EmitFarSkirtsCS','NoiseProbeCS')
|
||||||
|
$EightGB=8L*1024L*1024L*1024L
|
||||||
|
foreach($Entry in $Entries)
|
||||||
|
{
|
||||||
|
$Name='step12_shader_'+$Entry
|
||||||
|
$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")
|
||||||
|
Write-Output "STEP12_SHADER_RUN entry=$Entry freshness=fresh_process 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();$HostRunMs=$HostRunClock.Elapsed.TotalMilliseconds
|
||||||
|
$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 "Warmup process stopped at the 8 GiB private-memory ceiling: $Entry"}
|
||||||
|
$LogText=Get-Content -LiteralPath $StdoutPath -Raw
|
||||||
|
$Selected=[regex]::Match($LogText,'(?m)^\[GpuShaderWarmup\] dispatched=1 selected='+[regex]::Escape($Entry)+'.*queue_ms=([0-9.]+)')
|
||||||
|
$Dispatch=[regex]::Match($LogText,'(?m)^\[GpuShaderWarmup\] entry='+[regex]::Escape($Entry)+' dispatch_call_ms=([0-9.]+)')
|
||||||
|
$Wait=[regex]::Match($LogText,'(?m)^\[GpuShaderWarmup\] completion_wait_ms=([0-9.]+)')
|
||||||
|
$Create=[regex]::Match($LogText,'(?m)^\[GpuPipelineProfile\] stage=shader_object_create entry='+[regex]::Escape($Entry)+' duration_ms=([0-9.]+)')
|
||||||
|
if(-not$Selected.Success-or-not$Dispatch.Success-or-not$Wait.Success-or-not$Create.Success)
|
||||||
|
{throw "Isolated warmup markers missing for $Entry; exit=$ViewerExitCode"}
|
||||||
|
$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 "Warmup exceeded conservative private + DXGI-local 8 GiB ceiling: $Entry ($CombinedGB GiB)"}
|
||||||
|
Write-Output "STEP12_SHADER_RESULT entry=$Entry object_create_ms=$($Create.Groups[1].Value) dispatch_call_ms=$($Dispatch.Groups[1].Value) isolated_queue_ms=$($Selected.Groups[1].Value) completion_wait_ms=$($Wait.Groups[1].Value) 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"
|
||||||
|
if($ViewerExitCode-ne0){throw "Warmup process returned $ViewerExitCode for $Entry"}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
[CmdletBinding()]
|
||||||
|
param([string]$ExecutableName='VoxelForgeGpuFieldStep12Warmup.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"}
|
||||||
|
|
||||||
|
$Entries=@('ClassifyFarSkirtsCS','EmitFarSkirtsCS')
|
||||||
|
$EightGB=8L*1024L*1024L*1024L
|
||||||
|
foreach($Entry in $Entries)
|
||||||
|
{
|
||||||
|
$Name='step12_loop_skirt_shader_'+$Entry
|
||||||
|
$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")
|
||||||
|
Write-Output "STEP12_LOOP_SKIRT_SHADER_RUN entry=$Entry freshness=fresh_process 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();$HostRunMs=$HostRunClock.Elapsed.TotalMilliseconds
|
||||||
|
$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 "Warmup process stopped at the 8 GiB private-memory ceiling: $Entry"}
|
||||||
|
$LogText=Get-Content -LiteralPath $StdoutPath -Raw
|
||||||
|
$Selected=[regex]::Match($LogText,'(?m)^\[GpuShaderWarmup\] dispatched=1 selected='+[regex]::Escape($Entry)+'.*queue_ms=([0-9.]+)')
|
||||||
|
$Dispatch=[regex]::Match($LogText,'(?m)^\[GpuShaderWarmup\] entry='+[regex]::Escape($Entry)+' dispatch_call_ms=([0-9.]+)')
|
||||||
|
$Wait=[regex]::Match($LogText,'(?m)^\[GpuShaderWarmup\] completion_wait_ms=([0-9.]+)')
|
||||||
|
$Create=[regex]::Match($LogText,'(?m)^\[GpuPipelineProfile\] stage=shader_object_create entry='+[regex]::Escape($Entry)+' duration_ms=([0-9.]+)')
|
||||||
|
if(-not$Selected.Success-or-not$Dispatch.Success-or-not$Wait.Success-or-not$Create.Success)
|
||||||
|
{throw "Isolated warmup markers missing for $Entry; exit=$ViewerExitCode"}
|
||||||
|
$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 "Warmup exceeded conservative private + DXGI-local 8 GiB ceiling: $Entry ($CombinedGB GiB)"}
|
||||||
|
Write-Output "STEP12_LOOP_SKIRT_SHADER_RESULT entry=$Entry object_create_ms=$($Create.Groups[1].Value) dispatch_call_ms=$($Dispatch.Groups[1].Value) isolated_queue_ms=$($Selected.Groups[1].Value) completion_wait_ms=$($Wait.Groups[1].Value) 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"
|
||||||
|
if($ViewerExitCode-ne0){throw "Warmup process returned $ViewerExitCode for $Entry"}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
[CmdletBinding()]
|
||||||
|
param([string]$ExecutableName='VoxelForgeGpuFarAgreementStep12.exe',[string]$LogStem='step12_whole_near_agreement',[int]$ClipRadius=3)
|
||||||
|
|
||||||
|
$ErrorActionPreference='Stop'
|
||||||
|
if([IO.Path]::GetFileName($LogStem)-ne$LogStem){throw 'LogStem must be a filename stem.'}
|
||||||
|
$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)
|
||||||
|
$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",'--whole-near')
|
||||||
|
|
||||||
|
for($Attempt=0;$Attempt -lt 2;$Attempt++)
|
||||||
|
{
|
||||||
|
if($Attempt -gt 0)
|
||||||
|
{
|
||||||
|
Write-Host '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
|
||||||
|
$PeakWorkingSet=0L;$PeakPrivateMemory=0L;$OverMemoryLimit=$false
|
||||||
|
while(-not$Child.HasExited)
|
||||||
|
{
|
||||||
|
$Child.Refresh()
|
||||||
|
if($Child.HasExited){break}
|
||||||
|
$Child.PriorityClass='BelowNormal'
|
||||||
|
$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.'}
|
||||||
|
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,60 @@
|
|||||||
|
# GPU field port, step 12: field shader specialization and pool reuse
|
||||||
|
|
||||||
|
Date: 2026-10-07. Builds used `/MP8`; test and viewer processes ran BelowNormal at affinity `0xFF00`, with eight workers. All viewers were hidden and closed after each run. No `0xC0000142` retry was needed. No files outside `Standalone\` were changed. The AMD shader cache and driver settings were left untouched.
|
||||||
|
|
||||||
|
## Shader first use
|
||||||
|
|
||||||
|
`isolated_queue_ms` is the first dispatch queue time in a fresh process. It includes submission and `Flush`; it is not a driver compiler timer. Direct dispatch calls remained about 0.003–0.006 ms. The per-entry runner and logs are in [RunStep12FinalShaderFirstUse.ps1](RunStep12FinalShaderFirstUse.ps1) and `step12_final_shader_*.stdout.log`.
|
||||||
|
|
||||||
|
The pre-change column comes from a same-session rerun of the Step 11 executable; the Step 11 report recorded 1,757 ms for `EvaluateFarFieldCS`, versus 1,703.5 ms in this rerun.
|
||||||
|
|
||||||
|
| Entry | Pre-change queue ms (Step 11 executable) | Step 12 queue ms |
|
||||||
|
|---|---:|---:|
|
||||||
|
| `EvaluateFarFieldCS` | 1,703.5 | **3.3** |
|
||||||
|
| `EvaluateNearFieldCS` | mixed into `EvaluateFarFieldCS` | 1,782.9 |
|
||||||
|
| `ClassifyCS` | 3.0 | 6.5 |
|
||||||
|
| `ClassifyFarCS` | 3.5 | 3.2 |
|
||||||
|
| `ScanGroupsCS` | 3.4 | 5.9 |
|
||||||
|
| `EmitCS` | 3.1 | 4.6 |
|
||||||
|
| `EmitNearCS` | 2,352.9 | 2,281.0 |
|
||||||
|
| `BuildFarConnectorRingsCS` | 774.8 | **630.3** |
|
||||||
|
| `EmitFarConnectorPanelsCS` | 3.4 | 3.2 |
|
||||||
|
| `ClassifyFarSkirtsCS` | 503.7 | 500.1 |
|
||||||
|
| `ScanFarSkirtsCS` | 4.2 | 3.3 |
|
||||||
|
| `EmitFarSkirtsCS` | 947.6 | **909.1** |
|
||||||
|
| `NoiseProbeCS` | 3.1 | 3.1 |
|
||||||
|
|
||||||
|
`EvaluateFarFieldCS` now contains only the ordinary far path. The previous near-detail branch is in `EvaluateNearFieldCS`, selected when a tile has near detail; both are dispatched by startup warm-up. This takes the far entry from 1.704 s to 3.3 ms, while the near entry still costs 1.783 s. The connector’s fixed two- and eight-iteration refinement loops now use `[loop]`; the skirt triangle clip loop also uses `[loop]`. The connector loop-only sample fell from 845 ms to 599 ms; the final isolated sample was 630 ms. The skirt-loop change was small, especially for emission. `EmitNearCS` still reports FXC X4714 and remains a 2.281 s first-use entry.
|
||||||
|
|
||||||
|
The cold table therefore improved the far specialization substantially, but the production warm-up floor remains about 2.2 s. The final race warm-up samples were 2.258 s and 2.164 s. Near-detail evaluation is still expensive, and the measured whole-view GPU path did not beat the CPU. The next compile-cost step likely needs shared field samples consumed by connector and skirt passes, so each entry does not inline another field evaluator. The cache hypothesis remains untested; no cache files or driver settings were touched.
|
||||||
|
|
||||||
|
## CPU and WARP agreement
|
||||||
|
|
||||||
|
The final whole-far audit tested 1,114 eligible tiles and skipped 152 near-transition tiles. GPU and WARP each had zero topology differences. Protected exceptions covered 127,401 cells with zero differences on either device; thin features covered 353 cells with zero differences. Repeated GPU outputs were bit-identical. See [step12_whole_far_agreement.stdout.log](step12_whole_far_agreement.stdout.log).
|
||||||
|
|
||||||
|
The separate whole-near audit tested 960 tiles with no skipped tiles. GPU and WARP each had zero topology differences. It covered 229,376 protected-exception cells with zero differences; no thin-feature cells were selected in this near-detail set. Repeated outputs were bit-identical. See [step12_whole_near_agreement.stdout.log](step12_whole_near_agreement.stdout.log).
|
||||||
|
|
||||||
|
CPU reference runs at workers 1 and 8 matched the unchanged density `0xdb2991c73215af4c`, mesh `0x8236c5edd7fc8e4f`, payload `0x4c48fa75a543d58e`, and topology `0xbea22e76b34a4dfc` hashes. Walkability passed **5,642/5,642** in both. The full-residency GPU survival audit passed at **5.91 GiB** combined private plus DXGI-local memory.
|
||||||
|
|
||||||
|
## Fresh-launch race
|
||||||
|
|
||||||
|
Order was CPU A, GPU A, GPU B, CPU B. These are the post-pool-fix runs from [RunStep12PostPoolInterleaved.ps1](RunStep12PostPoolInterleaved.ps1):
|
||||||
|
|
||||||
|
| Run | Viewer ready / present return | Host process exit | Warm-up |
|
||||||
|
|---|---:|---:|---:|
|
||||||
|
| CPU A | 1,345.760 ms | 1,501.247 ms | — |
|
||||||
|
| GPU A | 3,304.292 ms | 3,475.452 ms | 2,258.286 ms |
|
||||||
|
| GPU B | 3,115.863 ms | 3,305.389 ms | 2,164.036 ms |
|
||||||
|
| CPU B | 1,355.288 ms | 1,502.819 ms | — |
|
||||||
|
|
||||||
|
Median viewer-clock readiness was **1.351 s CPU vs 3.210 s hybrid**, a **1.860 s / 2.38×** CPU lead. Median host-clock time was **1.502 s CPU vs 3.390 s hybrid**, a **1.888 s / 2.26×** lead. The GPU warm-up floor is still about **2.2 s**; far input preparation took 102–104 ms and GPU dispatch timestamps summed to 195–198 ms. These stages overlap. Present returned `DXGI_STATUS_OCCLUDED`, so these timings measure readiness and API return, not physical scanout.
|
||||||
|
|
||||||
|
## Pool reuse, captures, and remaining coverage issue
|
||||||
|
|
||||||
|
`ScheduleGpuFarTiles` now releases pooled output slots for tiles that remain desired but are no longer GPU-eligible, such as tiles switching to the CPU fallback route. Before the fix, those records retained slots and the movement probe failed with `no free far-output slot for queued tile`. The post-fix one-crossing run completed **1/1** crossings in **0.477 s**, reported nearest-ready p50/p95 of **19.723 ms**, retained **2,226/2,226** tiles, and had no missing tiles. Peak private plus DXGI-local memory was **5.85 GiB**. See [step12_gpu_crossing.stdout.log](step12_gpu_crossing.stdout.log).
|
||||||
|
|
||||||
|
All ten CPU/GPU view pairs reached full clipmap residency. Captures were compared as numeric RGB arrays; no full-size capture was opened. Maximum combined capture memory was **6.39 GiB**. Results are in [step12_capture_comparison.md](step12_capture_comparison.md).
|
||||||
|
|
||||||
|
The direct-GPU LOD3/4 coverage issue on `(6,2,0,lod3)` remains unresolved. The viewer keeps the LOD3/4 CPU fallback because the direct-GPU probe showed 1.46% changed pixels (maximum channel difference 82); the prior report described missing foreground on the CPU side and no GPU-only foreground. The existing evidence does not identify a safe shader fix, so no direct-GPU coverage change was made.
|
||||||
|
|
||||||
|
No commit, push, or stash was made.
|
||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,16 @@
|
|||||||
|
# Step 12 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.2002 | 2.6637 | 4.2874% | 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 |
|
||||||
@@ -464,73 +464,85 @@ void EvaluateFarFieldCS(uint3 dispatchId : SV_DispatchThreadID)
|
|||||||
+ float3(int(x) - 1, int(y) - 1, int(z) - 1) * StepVoxels;
|
+ float3(int(x) - 1, int(y) - 1, int(z) - 1) * StepVoxels;
|
||||||
float density;
|
float density;
|
||||||
uint distanceEvaluations=0;
|
uint distanceEvaluations=0;
|
||||||
float nearNormalDensity=0.0f;
|
const FarFieldSpatialBin range = FarFieldFindBin(position);
|
||||||
if(NearFieldMode!=0)
|
float cave = 100000.0f, overlay = -100000.0f;
|
||||||
|
[loop]
|
||||||
|
for(uint item=0;item<range.count;++item)
|
||||||
{
|
{
|
||||||
nearNormalDensity=NearFieldDensityAt(position,0,distanceEvaluations);
|
const VF_FieldPrimitive shape=FarShapes[FarSpatialBinShapeIds[range.offset+item]];
|
||||||
density=nearNormalDensity;
|
if((shape.flags&VF_FIELD_PRIMITIVE_CARVE)!=0)
|
||||||
if(abs(density)<0.05f)
|
|
||||||
density=density<0.0f?-0.05f:(density>0.0f?0.05f:-0.05f);
|
|
||||||
const uint packedProxy=FarNearPackedProxy[index];
|
|
||||||
if(packedProxy!=0xffffffffu)
|
|
||||||
{
|
{
|
||||||
// The CPU uploads sparse interpolation magnitudes. The GPU decides
|
if(position.x<shape.minVoxel[0]-6||position.x>shape.maxVoxel[0]+6
|
||||||
// ordinary signs, except for the one-quantum band where float sign
|
||position.y<shape.minVoxel[1]-6||position.y>shape.maxVoxel[1]+6
|
||||||
// noise can flip an entire cell against the packed CPU cache.
|
||position.z<shape.minVoxel[2]-6||position.z>shape.maxVoxel[2]+6)continue;
|
||||||
const bool packedSignOverride=(packedProxy&0x40000000u)!=0u;
|
float distance=VF_FieldPrimitiveDistance(shape,position.x,position.y,position.z);
|
||||||
const bool packedRockSign=(packedProxy&0x80000000u)!=0u;
|
++distanceEvaluations;
|
||||||
const uint magnitudeQ=packedProxy&0x3fffffffu;
|
if((shape.flags&VF_FIELD_PRIMITIVE_HAS_FLOOR)!=0)
|
||||||
const float magnitude=magnitudeQ==0u?4.125f:(float)magnitudeQ*(1.0f/256.0f);
|
{
|
||||||
const bool negative=packedSignOverride?packedRockSign:(density<=0.0f);
|
const float floorZ=VF_FieldPrimitiveFloorAt(shape,position.x,position.y);
|
||||||
density=negative?-magnitude:magnitude;
|
distance=VF_FieldSmoothMax(distance,floorZ-position.z,shape.floorBlendRadius);
|
||||||
|
}
|
||||||
|
cave=VF_FieldSmoothMin(cave,distance,shape.blendRadius);
|
||||||
|
}
|
||||||
|
else if(position.x>=shape.minVoxel[0]&&position.x<=shape.maxVoxel[0]
|
||||||
|
&&position.y>=shape.minVoxel[1]&&position.y<=shape.maxVoxel[1]
|
||||||
|
&&position.z>=shape.minVoxel[2]&&position.z<=shape.maxVoxel[2])
|
||||||
|
{
|
||||||
|
overlay=max(overlay,-VF_FieldPrimitiveDistance(shape,position.x,position.y,position.z));
|
||||||
|
++distanceEvaluations;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
const float base=min(position.z-FarStrataLo,FarStrataHi-position.z);
|
||||||
{
|
const float rockDistance=VF_FieldSmoothMin(base,max(cave,overlay),FarStrataBlend);
|
||||||
const FarFieldSpatialBin range=FarFieldFindBin(position);
|
const float rawDensity=-rockDistance;
|
||||||
float cave=100000.0f,overlay=-100000.0f;
|
const float magnitude=max(abs(rawDensity),0.05f);
|
||||||
[loop]
|
density=rockDistance>=0.0f?-magnitude:magnitude;
|
||||||
for(uint item=0;item<range.count;++item)
|
|
||||||
{
|
|
||||||
const VF_FieldPrimitive shape=FarShapes[FarSpatialBinShapeIds[range.offset+item]];
|
|
||||||
if((shape.flags&VF_FIELD_PRIMITIVE_CARVE)!=0)
|
|
||||||
{
|
|
||||||
if(position.x<shape.minVoxel[0]-6||position.x>shape.maxVoxel[0]+6
|
|
||||||
||position.y<shape.minVoxel[1]-6||position.y>shape.maxVoxel[1]+6
|
|
||||||
||position.z<shape.minVoxel[2]-6||position.z>shape.maxVoxel[2]+6)continue;
|
|
||||||
float distance=VF_FieldPrimitiveDistance(shape,position.x,position.y,position.z);
|
|
||||||
++distanceEvaluations;
|
|
||||||
if((shape.flags&VF_FIELD_PRIMITIVE_HAS_FLOOR)!=0)
|
|
||||||
{
|
|
||||||
const float floorZ=VF_FieldPrimitiveFloorAt(shape,position.x,position.y);
|
|
||||||
distance=VF_FieldSmoothMax(distance,floorZ-position.z,shape.floorBlendRadius);
|
|
||||||
}
|
|
||||||
cave=VF_FieldSmoothMin(cave,distance,shape.blendRadius);
|
|
||||||
}
|
|
||||||
else if(position.x>=shape.minVoxel[0]&&position.x<=shape.maxVoxel[0]
|
|
||||||
&&position.y>=shape.minVoxel[1]&&position.y<=shape.maxVoxel[1]
|
|
||||||
&&position.z>=shape.minVoxel[2]&&position.z<=shape.maxVoxel[2])
|
|
||||||
{
|
|
||||||
overlay=max(overlay,-VF_FieldPrimitiveDistance(shape,position.x,position.y,position.z));
|
|
||||||
++distanceEvaluations;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const float base=min(position.z-FarStrataLo,FarStrataHi-position.z);
|
|
||||||
const float rockDistance=VF_FieldSmoothMin(base,max(cave,overlay),FarStrataBlend);
|
|
||||||
const float rawDensity=-rockDistance;
|
|
||||||
const float magnitude=max(abs(rawDensity),0.05f);
|
|
||||||
density=rockDistance>=0.0f?-magnitude:magnitude;
|
|
||||||
}
|
|
||||||
const uint protectedAir = (FarProtectedAir[index >> 5] >> (index & 31)) & 1;
|
const uint protectedAir = (FarProtectedAir[index >> 5] >> (index & 31)) & 1;
|
||||||
const uint protectedSolid = (FarProtectedSolid[index >> 5] >> (index & 31)) & 1;
|
const uint protectedSolid = (FarProtectedSolid[index >> 5] >> (index & 31)) & 1;
|
||||||
const float protectedMinimum=NearFieldMode!=0?1.0f/256.0f:0.05f;
|
if(protectedAir!=0)density=max(abs(density),0.05f);
|
||||||
if(protectedAir!=0)density=max(abs(density),protectedMinimum);
|
else if(protectedSolid!=0)density=-max(abs(density),0.05f);
|
||||||
else if(protectedSolid!=0)density=-max(abs(density),protectedMinimum);
|
|
||||||
FarFieldDensity[index] = density;
|
FarFieldDensity[index] = density;
|
||||||
FarNearNormalDensityOutput[index]=nearNormalDensity;
|
FarNearNormalDensityOutput[index]=0.0f;
|
||||||
if (FarEvalCounterEnabled != 0) FarFieldEvalCounters[index] = distanceEvaluations;
|
if (FarEvalCounterEnabled != 0) FarFieldEvalCounters[index] = distanceEvaluations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[numthreads(64,1,1)]
|
||||||
|
void EvaluateNearFieldCS(uint3 dispatchId : SV_DispatchThreadID)
|
||||||
|
{
|
||||||
|
const uint index=dispatchId.x;
|
||||||
|
if(index>=FarSampleCount)return;
|
||||||
|
const uint x=index%SampleDim;
|
||||||
|
const uint y=(index/SampleDim)%SampleDim;
|
||||||
|
const uint z=index/(SampleDim*SampleDim);
|
||||||
|
const float3 position=float3(OriginX,OriginY,OriginZ)
|
||||||
|
+float3(int(x)-1,int(y)-1,int(z)-1)*StepVoxels;
|
||||||
|
uint distanceEvaluations=0;
|
||||||
|
const float nearNormalDensity=NearFieldDensityAt(position,0,distanceEvaluations);
|
||||||
|
float density=nearNormalDensity;
|
||||||
|
if(abs(density)<0.05f)
|
||||||
|
density=density<0.0f?-0.05f:(density>0.0f?0.05f:-0.05f);
|
||||||
|
const uint packedProxy=FarNearPackedProxy[index];
|
||||||
|
if(packedProxy!=0xffffffffu)
|
||||||
|
{
|
||||||
|
// The CPU uploads sparse interpolation magnitudes. The GPU decides
|
||||||
|
// ordinary signs, except for the one-quantum band where float sign
|
||||||
|
// noise can flip an entire cell against the packed CPU cache.
|
||||||
|
const bool packedSignOverride=(packedProxy&0x40000000u)!=0u;
|
||||||
|
const bool packedRockSign=(packedProxy&0x80000000u)!=0u;
|
||||||
|
const uint magnitudeQ=packedProxy&0x3fffffffu;
|
||||||
|
const float magnitude=magnitudeQ==0u?4.125f:(float)magnitudeQ*(1.0f/256.0f);
|
||||||
|
const bool negative=packedSignOverride?packedRockSign:(density<=0.0f);
|
||||||
|
density=negative?-magnitude:magnitude;
|
||||||
|
}
|
||||||
|
const uint protectedAir=(FarProtectedAir[index>>5]>>(index&31))&1;
|
||||||
|
const uint protectedSolid=(FarProtectedSolid[index>>5]>>(index&31))&1;
|
||||||
|
if(protectedAir!=0)density=max(abs(density),1.0f/256.0f);
|
||||||
|
else if(protectedSolid!=0)density=-max(abs(density),1.0f/256.0f);
|
||||||
|
FarFieldDensity[index]=density;
|
||||||
|
FarNearNormalDensityOutput[index]=nearNormalDensity;
|
||||||
|
if(FarEvalCounterEnabled!=0)FarFieldEvalCounters[index]=distanceEvaluations;
|
||||||
|
}
|
||||||
|
|
||||||
float FarRockDistanceAt(float3 position,int3 cachedBinCoord,FarFieldSpatialBin cachedRange)
|
float FarRockDistanceAt(float3 position,int3 cachedBinCoord,FarFieldSpatialBin cachedRange)
|
||||||
{
|
{
|
||||||
float cave = 100000.0f;
|
float cave = 100000.0f;
|
||||||
@@ -632,7 +644,7 @@ void FarSkirtClipTriangle(FarSkirtVertex a,FarSkirtVertex b,FarSkirtVertex c,
|
|||||||
{
|
{
|
||||||
FarSkirtVertex input[3];input[0]=a;input[1]=b;input[2]=c;
|
FarSkirtVertex input[3];input[0]=a;input[1]=b;input[2]=c;
|
||||||
polygonCount=0;
|
polygonCount=0;
|
||||||
[unroll]
|
[loop]
|
||||||
for(uint edge=0;edge<3;++edge)
|
for(uint edge=0;edge<3;++edge)
|
||||||
{
|
{
|
||||||
FarSkirtVertex start=input[edge];
|
FarSkirtVertex start=input[edge];
|
||||||
@@ -778,7 +790,7 @@ float FarConnectorRadius(float3 center, float3 direction, float nominalRadius)
|
|||||||
residual=FarRockDistanceAt(center+direction*radius,cachedBinCoord,cachedRange);
|
residual=FarRockDistanceAt(center+direction*radius,cachedBinCoord,cachedRange);
|
||||||
converged=abs(residual)<=1.0f;
|
converged=abs(residual)<=1.0f;
|
||||||
}
|
}
|
||||||
[unroll]
|
[loop]
|
||||||
for(uint refine=0;refine<2&&!converged;++refine)
|
for(uint refine=0;refine<2&&!converged;++refine)
|
||||||
{
|
{
|
||||||
const float distance=residual;
|
const float distance=residual;
|
||||||
@@ -807,7 +819,7 @@ float FarConnectorRadius(float3 center, float3 direction, float nominalRadius)
|
|||||||
{
|
{
|
||||||
float lo=previous,hi=distance;
|
float lo=previous,hi=distance;
|
||||||
const bool previousSolid=previousDistance>=0.0f;
|
const bool previousSolid=previousDistance>=0.0f;
|
||||||
[unroll]
|
[loop]
|
||||||
for(uint refine=0;refine<8;++refine)
|
for(uint refine=0;refine<8;++refine)
|
||||||
{
|
{
|
||||||
const float mid=(lo+hi)*0.5f;
|
const float mid=(lo+hi)*0.5f;
|
||||||
|
|||||||
@@ -2575,6 +2575,15 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
|||||||
Key.Coord.X,Key.Coord.Y,Key.Coord.Z,Key.Level,
|
Key.Coord.X,Key.Coord.Y,Key.Coord.Z,Key.Level,
|
||||||
Eligible.empty()||Eligible.back()!=Key?"cpu_fallback":"gpu_eligible");
|
Eligible.empty()||Eligible.back()!=Key?"cpu_fallback":"gpu_eligible");
|
||||||
}
|
}
|
||||||
|
const std::unordered_set<FVoxelTileKey,FStandaloneTileKeyHash> CurrentEligible(
|
||||||
|
Eligible.begin(),Eligible.end());
|
||||||
|
for(auto It=GpuFarTiles.begin();It!=GpuFarTiles.end();)
|
||||||
|
{
|
||||||
|
if(CurrentEligible.contains(It->first)){++It;continue;}
|
||||||
|
GpuFarFreeOutputSlots.push_back(It->second.OutputSlot);
|
||||||
|
ReleaseGpuFarTile(It->second);
|
||||||
|
It=GpuFarTiles.erase(It);
|
||||||
|
}
|
||||||
if(RemoteLod3CpuFallbackTiles+RemoteLod4CpuFallbackTiles>0)
|
if(RemoteLod3CpuFallbackTiles+RemoteLod4CpuFallbackTiles>0)
|
||||||
std::printf("[GpuRemoteBandFallback] lod3_cpu_tiles=%llu lod4_cpu_tiles=%llu reason=remote_capture_pixel_coverage\n",
|
std::printf("[GpuRemoteBandFallback] lod3_cpu_tiles=%llu lod4_cpu_tiles=%llu reason=remote_capture_pixel_coverage\n",
|
||||||
static_cast<unsigned long long>(RemoteLod3CpuFallbackTiles),
|
static_cast<unsigned long long>(RemoteLod3CpuFallbackTiles),
|
||||||
|
|||||||
Reference in new issue
Block a user