wip(standalone): GPU step 11 -- per-shader cold cost measured; far EmitCS split, still CPU wins

Cold first-use per shader (queue ms): EvaluateFarField 1757, EmitNear 2429,
FarConnectorRings 784, FarSkirts 507+975; the far EmitCS dropped 2327 -> 3.2
by splitting out the near midpoint fallback into EmitNearCS. Warm-up under
0xFF00 ~2.0 s (worker count and thread priority do not change it; 0xFFFF run:
1.71 s). Whole view median CPU 1.428 s vs hybrid 3.154 s. Audits unchanged.
AMD DxCache was last written 2026-10-06 23:27 -- shaders may no longer be
cached across launches (unverified).

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 06:13:59 +02:00
1 parent 10580650cb
commit b6ac950073
19 files changed
+904 -20

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','NoiseProbeCS','BuildFarConnectorRingsCS','EmitFarConnectorPanelsCS','ClassifyFarSkirtsCS','ScanFarSkirtsCS','EmitFarSkirtsCS')) { foreach ($Entry in @('EvaluateFarFieldCS','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"step10final_cpu_{view}.png"
gpu_path = ROOT / f"step10final_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 10 full-residency capture comparison",
"",
"All ten CPU/GPU pairs were captured after `[InitialClipmapReady]`. Images were compared as RGB arrays; no full-size capture was 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 / "step10_capture_comparison.md"
report.write_text("\n".join(lines) + "\n", encoding="utf-8")
print(report.read_text(encoding="utf-8"), end="")
@@ -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"step11_cpu_{view}.png"
gpu_path = ROOT / f"step11_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 11 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 / "step11_capture_comparison.md"
report.write_text("\n".join(lines) + "\n", encoding="utf-8")
print(report.read_text(encoding="utf-8"), end="")
@@ -25,7 +25,8 @@ namespace VoxelForgeGpuFarViewer
bool InitializeFarPipeline(ID3D11Device* device,ID3D11DeviceContext* context, bool InitializeFarPipeline(ID3D11Device* device,ID3D11DeviceContext* context,
const std::filesystem::path& shaderPath,std::string& error); const std::filesystem::path& shaderPath,std::string& error);
bool QueueFarShaderWarmup(ID3D11Device* device,ID3D11DeviceContext* context, bool QueueFarShaderWarmup(ID3D11Device* device,ID3D11DeviceContext* context,
const std::filesystem::path& shaderPath,bool dispatchShaders,std::string& error); const std::filesystem::path& shaderPath,bool dispatchShaders,
const std::string& entryFilter,std::string& error);
bool CompleteFarShaderWarmup(ID3D11Device* device,ID3D11DeviceContext* context, bool CompleteFarShaderWarmup(ID3D11Device* device,ID3D11DeviceContext* context,
const std::filesystem::path& shaderPath,std::string& error); const std::filesystem::path& shaderPath,std::string& error);
bool ReserveFarOutputPool(ID3D11Device* device,ID3D11DeviceContext* context, bool ReserveFarOutputPool(ID3D11Device* device,ID3D11DeviceContext* context,
@@ -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, noiseProbe; ComPtr<ID3D11ComputeShader> fieldEval, 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;
@@ -599,6 +599,7 @@ namespace
Create("ClassifyFarCS",fieldClassify); Create("ClassifyFarCS",fieldClassify);
Create("ScanGroupsCS",scan); Create("ScanGroupsCS",scan);
Create("EmitCS",emit); Create("EmitCS",emit);
Create("EmitNearCS",emitNear);
Create("NoiseProbeCS",noiseProbe); Create("NoiseProbeCS",noiseProbe);
Create("BuildFarConnectorRingsCS",connectorRings); Create("BuildFarConnectorRingsCS",connectorRings);
Create("EmitFarConnectorPanelsCS",connectorPanels); Create("EmitFarConnectorPanelsCS",connectorPanels);
@@ -609,13 +610,21 @@ namespace
std::chrono::duration<double,std::milli>(std::chrono::steady_clock::now()-PipelineStart).count()); std::chrono::duration<double,std::milli>(std::chrono::steady_clock::now()-PipelineStart).count());
} }
void QueueTinyShaderWarmup(bool dispatchShaders) 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={
"EvaluateFarFieldCS","ClassifyCS","ClassifyFarCS","ScanGroupsCS","EmitCS",
"EmitNearCS","BuildFarConnectorRingsCS","EmitFarConnectorPanelsCS",
"ClassifyFarSkirtsCS","ScanFarSkirtsCS","EmitFarSkirtsCS","NoiseProbeCS"};
if(!entryFilter.empty()&&std::find(entries.begin(),entries.end(),entryFilter)==entries.end())
throw std::runtime_error("unknown shader warmup entry: "+entryFilter);
constexpr std::uint32_t sampleCount=27,cellCount=1,groupCount=1; constexpr std::uint32_t sampleCount=27,cellCount=1,groupCount=1;
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=1;tile.voxelSizeCm=25.0f;tile.outputVertexCapacity=3; tile.stepVoxels=entryFilter=="EmitNearCS"?1:2;
tile.nearFieldMode=entryFilter=="EmitNearCS"?1u:0u;
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;
field.sampleCount=sampleCount;field.spatialBinMask=0;field.spatialBinSize=64; field.sampleCount=sampleCount;field.spatialBinMask=0;field.spatialBinSize=64;
@@ -645,16 +654,19 @@ namespace
auto detailLayers=Structured<FarFieldDetailLayerGpu>(nullptr,1,D3D11_BIND_SHADER_RESOURCE); auto detailLayers=Structured<FarFieldDetailLayerGpu>(nullptr,1,D3D11_BIND_SHADER_RESOURCE);
std::array<std::uint32_t,sampleCount> packedProxy{};packedProxy.fill(InvalidIndex); std::array<std::uint32_t,sampleCount> packedProxy{};packedProxy.fill(InvalidIndex);
auto proxies=Structured(packedProxy.data(),packedProxy.size(),D3D11_BIND_SHADER_RESOURCE); auto proxies=Structured(packedProxy.data(),packedProxy.size(),D3D11_BIND_SHADER_RESOURCE);
auto density=Structured<float>(nullptr,sampleCount,D3D11_BIND_SHADER_RESOURCE|D3D11_BIND_UNORDERED_ACCESS); std::array<float,sampleCount> zeroDensity{};
auto nearNormal=Structured<float>(nullptr,sampleCount,D3D11_BIND_SHADER_RESOURCE|D3D11_BIND_UNORDERED_ACCESS); auto density=Structured(zeroDensity.data(),sampleCount,D3D11_BIND_SHADER_RESOURCE|D3D11_BIND_UNORDERED_ACCESS);
auto nearNormal=Structured(zeroDensity.data(),sampleCount,D3D11_BIND_SHADER_RESOURCE|D3D11_BIND_UNORDERED_ACCESS);
auto evalCounters=Structured<std::uint32_t>(nullptr,sampleCount,D3D11_BIND_UNORDERED_ACCESS); auto evalCounters=Structured<std::uint32_t>(nullptr,sampleCount,D3D11_BIND_UNORDERED_ACCESS);
auto signs=Structured(&zeroWord,1,D3D11_BIND_SHADER_RESOURCE); auto signs=Structured(&zeroWord,1,D3D11_BIND_SHADER_RESOURCE);
std::array<std::int32_t,256*16> triTable{};triTable.fill(-1); std::array<std::int32_t,256*16> triTable{};triTable.fill(-1);
auto triangles=Structured(triTable.data(),triTable.size(),D3D11_BIND_SHADER_RESOURCE); auto triangles=Structured(triTable.data(),triTable.size(),D3D11_BIND_SHADER_RESOURCE);
auto cases=Structured<std::uint32_t>(nullptr,cellCount,D3D11_BIND_SHADER_RESOURCE|D3D11_BIND_UNORDERED_ACCESS); std::array<std::uint32_t,cellCount> zeroCell{};
auto localOffsets=Structured<std::uint32_t>(nullptr,cellCount,D3D11_BIND_SHADER_RESOURCE|D3D11_BIND_UNORDERED_ACCESS); std::array<std::uint32_t,groupCount> zeroGroup{};
auto groupCounts=Structured<std::uint32_t>(nullptr,groupCount,D3D11_BIND_SHADER_RESOURCE|D3D11_BIND_UNORDERED_ACCESS); auto cases=Structured(zeroCell.data(),cellCount,D3D11_BIND_SHADER_RESOURCE|D3D11_BIND_UNORDERED_ACCESS);
auto groupOffsets=Structured<std::uint32_t>(nullptr,groupCount,D3D11_BIND_SHADER_RESOURCE|D3D11_BIND_UNORDERED_ACCESS); auto localOffsets=Structured(zeroCell.data(),cellCount,D3D11_BIND_SHADER_RESOURCE|D3D11_BIND_UNORDERED_ACCESS);
auto groupCounts=Structured(zeroGroup.data(),groupCount,D3D11_BIND_SHADER_RESOURCE|D3D11_BIND_UNORDERED_ACCESS);
auto groupOffsets=Structured(zeroGroup.data(),groupCount,D3D11_BIND_SHADER_RESOURCE|D3D11_BIND_UNORDERED_ACCESS);
auto midpointIds=Structured(&invalidWord,1,D3D11_BIND_SHADER_RESOURCE); auto midpointIds=Structured(&invalidWord,1,D3D11_BIND_SHADER_RESOURCE);
MidpointRecord emptyMidpoint{}; MidpointRecord emptyMidpoint{};
auto midpointRecords=Structured(&emptyMidpoint,1,D3D11_BIND_SHADER_RESOURCE); auto midpointRecords=Structured(&emptyMidpoint,1,D3D11_BIND_SHADER_RESOURCE);
@@ -699,6 +711,16 @@ namespace
auto indices=RawMeshBuffer(3u*sizeof(std::uint32_t),D3D11_BIND_UNORDERED_ACCESS); auto indices=RawMeshBuffer(3u*sizeof(std::uint32_t),D3D11_BIND_UNORDERED_ACCESS);
auto panelVertices=RawMeshBuffer(64u*sizeof(GpuVertex),D3D11_BIND_UNORDERED_ACCESS); auto panelVertices=RawMeshBuffer(64u*sizeof(GpuVertex),D3D11_BIND_UNORDERED_ACCESS);
auto panelIndices=RawMeshBuffer(64u*sizeof(std::uint32_t),D3D11_BIND_UNORDERED_ACCESS); auto panelIndices=RawMeshBuffer(64u*sizeof(std::uint32_t),D3D11_BIND_UNORDERED_ACCESS);
std::array<std::uint32_t,11> zeroArgs{};
std::array<GpuVertex,3> zeroVertices{};
std::array<GpuVertex,64> zeroPanelVertices{};
std::array<std::uint32_t,3> zeroIndices{};
std::array<std::uint32_t,64> zeroPanelIndices{};
context->UpdateSubresource(args.Get(),0,nullptr,zeroArgs.data(),0,0);
context->UpdateSubresource(vertices.Get(),0,nullptr,zeroVertices.data(),0,0);
context->UpdateSubresource(indices.Get(),0,nullptr,zeroIndices.data(),0,0);
context->UpdateSubresource(panelVertices.Get(),0,nullptr,zeroPanelVertices.data(),0,0);
context->UpdateSubresource(panelIndices.Get(),0,nullptr,zeroPanelIndices.data(),0,0);
auto argsUav=RawUav(args.Get(),11); auto argsUav=RawUav(args.Get(),11);
auto vertexUav=RawUav(vertices.Get(),static_cast<UINT>(3u*sizeof(GpuVertex)/4u)); auto vertexUav=RawUav(vertices.Get(),static_cast<UINT>(3u*sizeof(GpuVertex)/4u));
auto vertexSrv=RawSrv(vertices.Get(),static_cast<UINT>(3u*sizeof(GpuVertex)/4u)); auto vertexSrv=RawSrv(vertices.Get(),static_cast<UINT>(3u*sizeof(GpuVertex)/4u));
@@ -721,6 +743,7 @@ namespace
ID3D11Buffer* constants[3]={tileCb.Get(),fieldCb.Get(),connectorCb.Get()}; ID3D11Buffer* constants[3]={tileCb.Get(),fieldCb.Get(),connectorCb.Get()};
context->CSSetConstantBuffers(0,3,constants); context->CSSetConstantBuffers(0,3,constants);
std::uint32_t dispatchCount=0;
auto ClearBindings=[&](UINT srvCount=20) auto ClearBindings=[&](UINT srvCount=20)
{ {
ID3D11ShaderResourceView* nullSrvs[20]{}; ID3D11ShaderResourceView* nullSrvs[20]{};
@@ -730,8 +753,12 @@ namespace
}; };
auto Dispatch=[&](ID3D11ComputeShader* shader,const char* entry) auto Dispatch=[&](ID3D11ComputeShader* shader,const char* entry)
{ {
if(!entryFilter.empty()&&entryFilter!=entry)return;
if(entryFilter.empty()&&(std::string_view(entry)=="ClassifyCS"
||std::string_view(entry)=="EmitNearCS"||std::string_view(entry)=="NoiseProbeCS"))return;
const auto start=Clock::now(); const auto start=Clock::now();
context->CSSetShader(shader,nullptr,0);context->Dispatch(1,1,1); 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::printf("[GpuShaderWarmup] entry=%s dispatch_call_ms=%.3f groups=1\n",entry,
std::chrono::duration<double,std::milli>(Clock::now()-start).count()); std::chrono::duration<double,std::milli>(Clock::now()-start).count());
}; };
@@ -774,6 +801,7 @@ namespace
ID3D11UnorderedAccessView* emitUavs[8]{};emitUavs[5]=vertexUav.Get();emitUavs[6]=indexUav.Get(); ID3D11UnorderedAccessView* emitUavs[8]{};emitUavs[5]=vertexUav.Get();emitUavs[6]=indexUav.Get();
context->CSSetUnorderedAccessViews(0,8,emitUavs,nullptr); context->CSSetUnorderedAccessViews(0,8,emitUavs,nullptr);
Dispatch(emit.Get(),"EmitCS");ClearBindings(); Dispatch(emit.Get(),"EmitCS");ClearBindings();
Dispatch(emitNear.Get(),"EmitNearCS");ClearBindings();
ID3D11ShaderResourceView* ringSrvs[5]={connectorPanelSrv.Get(),nullptr,shapeSrv.Get(),binSrv.Get(),shapeIdSrv.Get()}; ID3D11ShaderResourceView* ringSrvs[5]={connectorPanelSrv.Get(),nullptr,shapeSrv.Get(),binSrv.Get(),shapeIdSrv.Get()};
context->CSSetShaderResources(8,5,ringSrvs); context->CSSetShaderResources(8,5,ringSrvs);
@@ -819,8 +847,11 @@ namespace
D3D11_QUERY_DESC eventDesc{D3D11_QUERY_EVENT,0}; D3D11_QUERY_DESC eventDesc{D3D11_QUERY_EVENT,0};
Check(device->CreateQuery(&eventDesc,&shaderWarmupCompletion),"CreateQuery(shader warmup completion)"); Check(device->CreateQuery(&eventDesc,&shaderWarmupCompletion),"CreateQuery(shader warmup completion)");
context->End(shaderWarmupCompletion.Get());context->Flush(); context->End(shaderWarmupCompletion.Get());context->Flush();
std::printf("[GpuShaderWarmup] dispatched=11 tiny_samples=%u tiny_cells=%u queue_ms=%.3f completion=pending\n", if(dispatchShaders&&dispatchCount==0)
sampleCount,cellCount,std::chrono::duration<double,std::milli>(Clock::now()-WarmupStart).count()); 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",
dispatchCount,entryFilter.empty()?"production":entryFilter.c_str(),sampleCount,cellCount,
std::chrono::duration<double,std::milli>(Clock::now()-WarmupStart).count());
} }
bool CompleteTinyShaderWarmup() bool CompleteTinyShaderWarmup()
@@ -1628,7 +1659,7 @@ namespace
context->CSSetUnorderedAccessViews(0, 8, nullUavs, nullptr); context->CSSetUnorderedAccessViews(0, 8, nullUavs, nullptr);
beginKernel(3); beginKernel(3);
context->CSSetShader(emit.Get(), nullptr, 0); context->CSSetShader((farField&&tile.step>1?emit.Get():emitNear.Get()), nullptr, 0);
ID3D11ShaderResourceView* emitSrvs[19]{}; ID3D11ShaderResourceView* emitSrvs[19]{};
emitSrvs[1] = triSrv.Get(); emitSrvs[2] = densitySrv.Get(); emitSrvs[3] = caseSrv.Get(); emitSrvs[1] = triSrv.Get(); emitSrvs[2] = densitySrv.Get(); emitSrvs[3] = caseSrv.Get();
emitSrvs[4] = localOffsetSrv.Get(); emitSrvs[5] = groupOffsetSrv.Get(); emitSrvs[4] = localOffsetSrv.Get(); emitSrvs[5] = groupOffsetSrv.Get();
@@ -3092,11 +3123,12 @@ namespace VoxelForgeGpuFarViewer
} }
bool QueueFarShaderWarmup(ID3D11Device* device,ID3D11DeviceContext* context, bool QueueFarShaderWarmup(ID3D11Device* device,ID3D11DeviceContext* context,
const std::filesystem::path& shaderPath,bool dispatchShaders,std::string& error) const std::filesystem::path& shaderPath,bool dispatchShaders,
const std::string& entryFilter,std::string& error)
{ {
try try
{ {
GetFarViewerRunner(device,context,shaderPath).QueueTinyShaderWarmup(dispatchShaders); GetFarViewerRunner(device,context,shaderPath).QueueTinyShaderWarmup(dispatchShaders,entryFilter);
return true; return true;
} }
catch(const std::exception& e){error=e.what();return false;} catch(const std::exception& e){error=e.what();return false;}
@@ -0,0 +1,69 @@
[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"}
$Name='step11_allcpu_affinity_gpu'
$Arguments=@('--clip-radius=3','--workers=8','--quit-after-clipmap','--gpu-far',
'--gpu-far-no-present-hold','--affinity-all-cpu')
$StdoutPath=Join-Path $PSScriptRoot ($Name+'.stdout.log')
$StderrPath=Join-Path $PSScriptRoot ($Name+'.stderr.log')
$EightGB=8L*1024L*1024L*1024L
Write-Output "STEP11_ALLCPU_RUN name=$Name priority=BelowNormal affinity=0xFFFF 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'
$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: $Name"}
$LogText=Get-Content -LiteralPath $StdoutPath -Raw
if(-not$LogText.Contains('[InitialClipmapReady]'))
{throw "Full clipmap was not ready in $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})')
$Warmup=[regex]::Match($LogText,'(?m)^\[GpuShaderWarmup\] dispatched=[0-9]+ .*queue_ms=([0-9.]+)')
$Wait=[regex]::Match($LogText,'(?m)^\[GpuShaderWarmup\] completion_wait_ms=([0-9.]+)')
if(-not$Presented.Success){throw "Whole-view present marker missing in $Name"}
$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: $Name ($CombinedGB GiB)"}
$Controls=[regex]::Match($LogText,'(?m)^\[ProcessControls\].*')
Write-Output "STEP11_ALLCPU_RESULT name=$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)) controls=`"$($Controls.Value)`" exit=$ViewerExitCode"
if($ViewerExitCode-ne0){throw "Viewer returned $ViewerExitCode`: $Name"}
break
}
@@ -0,0 +1,80 @@
[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"}
$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="step11_${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 "STEP11_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 "STEP11_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
}
}
}
@@ -0,0 +1,63 @@
[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"}
$Reference=$null
foreach($Workers in @(1,8))
{
$Name="step11_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 "STEP11_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 "STEP11_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='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"}
$Runs=@(
@{Mode='cpu';Name='step11_race_cpu_A'},
@{Mode='gpu';Name='step11_race_gpu_A'},
@{Mode='gpu';Name='step11_race_gpu_B'},
@{Mode='cpu';Name='step11_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 "STEP11_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 "STEP11_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='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"}
$Arguments=@('--clip-radius=3','--workers=8','--quit-after-clipmap','--gpu-far-audit','--gpu-far-prewarm')
$Name='step11_gpu_survival'
$StdoutPath=Join-Path $PSScriptRoot ($Name+'.stdout.log')
$StderrPath=Join-Path $PSScriptRoot ($Name+'.stderr.log')
$EightGB=8L*1024L*1024L*1024L
Write-Output "STEP11_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 "STEP11_SURVIVAL_RESULT audit=$($Audit.Groups[1].Value) peak_private_gb=$([math]::Round($PeakPrivate/1GB,2)) dxgi_local_peak_mb=$DxgiMB conservative_private_plus_dxgi_gb=$([math]::Round($CombinedGB,2)) exit=$($Child.ExitCode) stdout=$StdoutPath stderr=$StderrPath"
if($Audit.Groups[1].Value-ne'PASS'){throw 'GPU survival audit failed.'}
if($Child.ExitCode-ne0){throw "Viewer returned $($Child.ExitCode)"}
break
}
@@ -0,0 +1,70 @@
[CmdletBinding()]
param([string]$ExecutableName='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='step11_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 "STEP11_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 "STEP11_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,85 @@
[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"}
$Runs=@(
@{Name='step11_warmup_workers8';Workers=8;Mode='overlap'},
@{Name='step11_warmup_workers4';Workers=4;Mode='overlap'},
@{Name='step11_warmup_workers7';Workers=7;Mode='overlap'},
@{Name='step11_warmup_workers6';Workers=6;Mode='overlap'},
@{Name='step11_warmup_thread_normal';Workers=8;Mode='normal_priority'},
@{Name='step11_warmup_before_workers';Workers=8;Mode='before_workers'}
)
$EightGB=8L*1024L*1024L*1024L
foreach($Run in $Runs)
{
$Arguments=@('--clip-radius=3',"--workers=$($Run.Workers)",'--quit-after-clipmap','--gpu-far-no-present-hold')
if($Run.Mode-eq'overlap'){$Arguments+='--gpu-far-prewarm'}
elseif($Run.Mode-eq'normal_priority'){$Arguments+=@('--gpu-far-prewarm','--gpu-far-warmup-normal-priority')}
else{$Arguments+='--gpu-far-warmup-before-workers'}
$StdoutPath=Join-Path $PSScriptRoot ($Run.Name+'.stdout.log')
$StderrPath=Join-Path $PSScriptRoot ($Run.Name+'.stderr.log')
Write-Output "STEP11_RUN name=$($Run.Name) mode=$($Run.Mode) workers=$($Run.Workers) 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 "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})')
$Queue=[regex]::Match($LogText,'(?m)^\[GpuShaderWarmup\] dispatched=[0-9]+ .*queue_ms=([0-9.]+)')
$Wait=[regex]::Match($LogText,'(?m)^\[GpuShaderWarmup\] completion_wait_ms=([0-9.]+)')
if(-not$Presented.Success){throw "Whole-view present marker missing in $($Run.Name)"}
$WarmupMs=if($Queue.Success){[double]$Queue.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 "STEP11_RESULT name=$($Run.Name) mode=$($Run.Mode) workers=$($Run.Workers) warmup_ms=$([math]::Round($WarmupMs,3)) ready_ms=$([math]::Round([double]$Ready.Groups[1].Value*1000,3)) 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"
if($Run.Mode-eq'normal_priority')
{Select-String -Path $StdoutPath -Pattern '^\[GpuShaderWarmupThread\]|^\[GpuShaderWarmup\] queue_thread=' | ForEach-Object {Write-Output $_.Line}}
if($Run.Mode-eq'before_workers')
{Select-String -Path $StdoutPath -Pattern '^\[GpuShaderWarmup\] queue_thread=|^\[GpuShaderWarmupBeforeWorkers\]' | ForEach-Object {Write-Output $_.Line}}
$Timeline=[regex]::Match($LogText,'(?m)^\[GpuFarTimeline\].*')
if($Timeline.Success){Write-Output $Timeline.Value}
if($ViewerExitCode-ne0){throw "Viewer returned $ViewerExitCode`: $($Run.Name)"}
break
}
}
@@ -0,0 +1,71 @@
# GPU field port, step 11: warm-up controls and far emitter split
Date: 2026-10-07. The Step 11 viewer and agreement runner were built from `Standalone\` sources with `/MP8`, BelowNormal process priority, and affinity `0xFF00`. All viewer runs were serial, hidden, and closed at exit. No `0xC0000142` retry was needed. The only affinity exception was the single 8-worker viewer run at `0xFFFF`. No capture was opened in an image viewer.
## Warm-up controls
`warm-up ms` is the logged queue time plus its completion wait. Each run reports whole-view readiness from `[InitialClipmapReady]` and the subsequent `[WholeViewPresented] at_ms` clock. The pre-split controls used the original 11-entry warm-up; after the split, startup dispatches the nine kernels used by the viewer's far path.
| Control | Warm-up before / after split | Ready / whole-view present before | Ready / whole-view present after |
|---|---:|---:|---:|
| 8 workers, `0xFF00` | 2,609 / 2,038 ms | 3,501 / 3,502.360 ms | 3,059 / 3,059.825 ms |
| 7 workers, `0xFF00` | 2,725 / 1,986 ms | 3,433 / 3,434.035 ms | 3,152 / 3,152.550 ms |
| 6 workers, `0xFF00` | 2,614 / 2,127 ms | 3,283 / 3,283.377 ms | 3,143 / 3,144.162 ms |
| 4 workers, `0xFF00` | 2,579 / 2,068 ms | 3,190 / 3,190.884 ms | 3,199 / 3,200.342 ms |
| 8 workers, warm-up thread at normal base priority | 2,669 / 2,076 ms | 3,498 / 3,498.548 ms | 3,089 / 3,090.267 ms |
| 8 workers, warm-up completes before generation workers | 2,367 / 1,758 ms | 3,623 / 3,623.833 ms | 3,306 / 3,306.937 ms |
Worker-count effects were not monotonic: the fastest post-split readiness was the 8-worker run, and changing the warm-up thread to normal base priority produced no meaningful improvement. Completing warm-up before generation workers shortened the queue by about 280 ms versus the overlapping 8-worker control, but made the full view about 247 ms slower because that time could no longer overlap generation.
Each worker/priority control is one fresh launch per variant, so small readiness differences can be run variation. The two post-split GPU race launches independently measured 2,001 and 2,033 ms of warm-up.
The one allowed all-CPU-affinity run used BelowNormal priority, 8 workers, and verified process mask `0xFFFF`: warm-up **1,710 ms**, ready **2,766 ms**, whole-view present return **2,767.140 ms**, host process exit **2,936.462 ms**. This is about 0.29 s faster warm-up and 0.39 s faster readiness than the post-split `0xFF00` 8-worker control, but it remains well behind CPU.
## Isolated shader first use
Each entry ran alone in a fresh viewer process at BelowNormal / `0xFF00`; no earlier dispatch of that entry occurred in that process. `queue ms` covers tiny-resource setup, dispatch submission, and `Flush`, so it measures the first-use path rather than a driver-internal compiler timer. The direct `Dispatch` call was only 0.003–0.007 ms and completion waits were at most 16 ms. Driver compilation is consistent with the long queue times, but was not directly observed.
| Entry | Original queue ms | After split queue ms |
|---|---:|---:|
| `EvaluateFarFieldCS` | 1,839 | 1,757 |
| `ClassifyCS` | 3.2 | 3.1 |
| `ClassifyFarCS` | 3.0 | 3.0 |
| `ScanGroupsCS` | 3.2 | 3.1 |
| `EmitCS` (original mixed path → far specialization) | 2,327 | 3.2 |
| `EmitNearCS` (retained original step-one path) | — | 2,429 |
| `BuildFarConnectorRingsCS` | 820 | 784 |
| `EmitFarConnectorPanelsCS` | 3.3 | 3.4 |
| `ClassifyFarSkirtsCS` | 478 | 507 |
| `ScanFarSkirtsCS` | 2.8 | 3.1 |
| `EmitFarSkirtsCS` | 1,031 | 975 |
| `NoiseProbeCS` | 3.3 | 3.4 |
The original `EmitCS` root helper included an analytic midpoint fallback that calls `NearFieldDensityAt`, which expands into the shared primitive-distance and owner-detail field routines. That fallback is only reachable for `StepVoxels == 1`; far tiles use larger steps. I split the step-one code into `EmitNearCS` and made `EmitCS` retain CPU-authored midpoint records while omitting that unreachable branch for the far path. FXC no longer reports X4714 for far `EmitCS`; the warning remains on `EmitNearCS`. The viewer dispatches the specialized entry for far tiles with `tile.step > 1`, preserving the original path for step-one callers. This reduced the far emitter's isolated queue from **2.327 s to 3.2 ms**. Other first-use costs remain, especially field evaluation, connector rings, and skirt emission.
Startup warm-up now dispatches the nine kernels used by the viewer's far path. It omits the CPU-path classifier, the step-one emitter, and the noise diagnostic. Under `0xFF00`, its queue fell from roughly **2.6–2.7 s to 2.0–2.13 s** in the worker sweep.
## Fresh-launch whole-view race
Order was CPU A, GPU A, GPU B, CPU B. Every launch used 8 workers, BelowNormal, and `0xFF00`.
| Run | Ready | `[WholeViewPresented]` | Host process exit | Warm-up |
|---|---:|---:|---:|---:|
| CPU A | 1,503 ms | 1,503.159 ms | 1,718.840 ms | — |
| GPU A | 3,183 ms | 3,183.824 ms | 3,367.918 ms | 2,001 ms |
| GPU B | 3,124 ms | 3,124.843 ms | 3,291.548 ms | 2,033 ms |
| CPU B | 1,352 ms | 1,352.567 ms | 1,488.795 ms | — |
Median viewer-clock time was **1.428 s CPU vs 3.154 s hybrid**, a **1.726 s / 2.21×** CPU lead. Median host-clock time was **1.604 s CPU vs 3.330 s hybrid**, a **1.726 s / 2.08×** CPU lead. The hybrid is still slower. Its measured floor includes about 2.0 s of shader warm-up; far input preparation was 104 ms, GPU busy time about 199 ms, and the GPU queue envelope 610–859 ms. These stages overlap, so they should not be added as independent durations.
The all-CPU-affinity result is separate from the race: **2.767 s** whole-view present return versus the normal-rule CPU median of **1.428 s**. All automated `Present` calls returned `DXGI_STATUS_OCCLUDED` (`0x087A0001`), so these clocks measure readiness and API return, not physical scanout.
## Correctness and memory
- Whole-far agreement: 1,114 eligible tiles, 152 near-transition tiles skipped; **zero GPU and WARP topology differences**, zero protected-exception differences across 127,401 cells, zero thin-feature differences across 353 cells, and bit-identical repeated GPU outputs.
- GPU-mesh survival audit: **PASS** at full clipmap residency; peak 3.46 GiB private plus 2,402.1 MiB DXGI local, **5.81 GiB combined**.
- CPU reference at workers 1 and 8: identical density `0xdb2991c73215af4c`, mesh `0x8236c5edd7fc8e4f`, payload `0x4c48fa75a543d58e`, and topology `0xbea22e76b34a4dfc`; walkability **5,642/5,642** in both runs.
- Ten CPU/GPU capture pairs reached `[InitialClipmapReady]`. Maximum combined capture memory was **6.11 GiB**. Numeric RGB comparison is in [step11_capture_comparison.md](step11_capture_comparison.md); the results remain within the Step 10 ranges.
Primary artifacts: `RunStep11WarmupControls.ps1`, `RunStep11ShaderFirstUse.ps1`, `RunStep11FinalInterleaved.ps1`, `RunStep11AllCpuAffinity.ps1`, `RunStep11GpuSurvival.ps1`, `RunStep11CpuReference.ps1`, `RunStep11CaptureMatrix.ps1`, `step11_whole_far_agreement.stdout.log`, `step11_gpu_survival.stdout.log`, and the `step11_*` run logs.
The previously reported direct-GPU LOD3/4 coverage loss and movement-crossing output-slot overflow remain unresolved; this step made no changes to those lower-priority paths. No commit, push, or stash was made.
@@ -0,0 +1,16 @@
# Step 10 full-residency capture comparison
All ten CPU/GPU pairs were captured after `[InitialClipmapReady]`. Images were compared as RGB arrays; no full-size capture was 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.5592% | 0.4098% | 107 |
@@ -0,0 +1,16 @@
# Step 11 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.4087 | 4.3554 | 2.2590% | 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 |
@@ -150,6 +150,27 @@ float EdgeRootParameter(int3 cell,uint edge,StructuredBuffer<float> density,
return VF_FieldRefineRoot(da,db,mid,hasMid); return VF_FieldRefineRoot(da,db,mid,hasMid);
} }
float FarEdgeRootParameter(int3 cell,uint edge,StructuredBuffer<float> density,
StructuredBuffer<uint> midpointIds,StructuredBuffer<MidpointRecord> midpointRecords)
{
const int2 corners=EdgeCorners[edge];
const int3 pa=cell+CornerOffset[corners.x];
const int3 pb=cell+CornerOffset[corners.y];
const float da=LatticeValue(density,pa),db=LatticeValue(density,pb);
const uint midId=midpointIds[CellIndex(uint3(cell))];
float mid=0.0f;uint hasMid=0;
if(midId!=0xffffffffu)
{
const MidpointRecord record=midpointRecords[midId];
hasMid=(record.metadata.x>>edge)&1;
mid=record.values[edge>>2][edge&3];
}
// Far tiles always have StepVoxels > 1. The exact midpoint field fallback in
// EdgeRootParameter is restricted to StepVoxels == 1, so omit that LOD0-only
// branch from the far entry point while retaining every CPU-authored midpoint.
return VF_FieldRefineRoot(da,db,mid,hasMid);
}
float3 EdgePosition(int3 cell,uint edge,float t) float3 EdgePosition(int3 cell,uint edge,float t)
{ {
const int2 corners = EdgeCorners[edge]; const int2 corners = EdgeCorners[edge];
@@ -1086,7 +1107,7 @@ RWByteAddressBuffer VertexBytes : register(u5);
RWByteAddressBuffer IndexBytes : register(u6); RWByteAddressBuffer IndexBytes : register(u6);
[numthreads(256,1,1)] [numthreads(256,1,1)]
void EmitCS(uint3 dispatchId : SV_DispatchThreadID) void EmitNearCS(uint3 dispatchId : SV_DispatchThreadID)
{ {
const uint index = dispatchId.x; const uint index = dispatchId.x;
if (index >= CellCount) return; if (index >= CellCount) return;
@@ -1120,6 +1141,41 @@ void EmitCS(uint3 dispatchId : SV_DispatchThreadID)
} }
} }
[numthreads(256,1,1)]
void EmitCS(uint3 dispatchId : SV_DispatchThreadID)
{
const uint index = dispatchId.x;
if (index >= CellCount) return;
const uint caseIndex = Cases[index];
const uint localBase = LocalOffsets[index] + PrefixGroups[index / 256];
if(localBase>=OutputVertexCapacity)return;
const uint base = OutputBaseVertex + localBase;
const uint3 cell = uint3(index % CellsPerAxis,
(index / CellsPerAxis) % CellsPerAxis,
index / (CellsPerAxis * CellsPerAxis));
[loop]
for (uint slot = 0; slot < 15; ++slot)
{
uint tableSlot = slot;
if (slot % 3 == 1) tableSlot = slot + 1;
else if (slot % 3 == 2) tableSlot = slot - 1;
const int edgeValue = Triangles[caseIndex * 16 + tableSlot];
if (edgeValue < 0) break;
if(localBase+slot>=OutputVertexCapacity)break;
const uint edge = uint(edgeValue);
const float rootT=FarEdgeRootParameter(int3(cell),edge,Density,MidpointCellIds,MidpointData);
GpuVertex v;
v.position = EdgePosition(int3(cell),edge,rootT);
v.normal = EdgeNormal(int3(cell),edge,rootT,Density);
v.uv = v.position.xy / VoxelSizeCm;
const uint byteAddress = (base + slot) * 32;
VertexBytes.Store3(byteAddress, asuint(v.position));
VertexBytes.Store3(byteAddress + 12, asuint(v.normal));
VertexBytes.Store2(byteAddress + 24, asuint(v.uv));
IndexBytes.Store((base + slot) * 4, base + slot);
}
}
[numthreads(64,1,1)] [numthreads(64,1,1)]
void ClassifyFarSkirtsCS(uint3 dispatchId : SV_DispatchThreadID) void ClassifyFarSkirtsCS(uint3 dispatchId : SV_DispatchThreadID)
{ {
+74 -3
View File
@@ -1490,6 +1490,9 @@ namespace
bool bGpuFarAuditEnabled=false; bool bGpuFarAuditEnabled=false;
bool bGpuFarShaderPrewarm=false,bDisableGpuWarmupPresentHold=false; bool bGpuFarShaderPrewarm=false,bDisableGpuWarmupPresentHold=false;
bool bGpuFarWarmupResourcesOnly=false; bool bGpuFarWarmupResourcesOnly=false;
bool bGpuFarWarmupNormalPriority=false,bGpuFarWarmupBeforeWorkers=false;
bool bGpuFarWarmupOnly=false;
std::string GpuWarmupEntryFilter;
std::thread GpuShaderWarmupThread; std::thread GpuShaderWarmupThread;
bool bGpuShaderWarmupQueueSucceeded=false; bool bGpuShaderWarmupQueueSucceeded=false;
std::string GpuShaderWarmupQueueError; std::string GpuShaderWarmupQueueError;
@@ -1882,11 +1885,22 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
bGpuShaderWarmupQueueSucceeded=false;GpuShaderWarmupQueueError.clear(); bGpuShaderWarmupQueueSucceeded=false;GpuShaderWarmupQueueError.clear();
GpuShaderWarmupThread=std::thread([this]() GpuShaderWarmupThread=std::thread([this]()
{ {
bool bNormalBasePriority=false;
if(bGpuFarWarmupNormalPriority)
{
// The process stays BelowNormal; HIGHEST relative priority raises this
// worker thread to a normal base priority on Windows.
bNormalBasePriority=SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_HIGHEST)!=0;
std::printf("[GpuShaderWarmupThread] priority=%s process=BelowNormal thread_relative=Highest\n",
bNormalBasePriority?"Normal":"set_failed");
std::fflush(stdout);
}
bGpuShaderWarmupQueueSucceeded=VoxelForgeGpuFarViewer::QueueFarShaderWarmup( bGpuShaderWarmupQueueSucceeded=VoxelForgeGpuFarViewer::QueueFarShaderWarmup(
Device,Context,GpuFarShaderPath,!bGpuFarWarmupResourcesOnly, Device,Context,GpuFarShaderPath,!bGpuFarWarmupResourcesOnly,GpuWarmupEntryFilter,
GpuShaderWarmupQueueError); GpuShaderWarmupQueueError);
}); });
std::printf("[GpuShaderWarmup] queue_thread=started overlap=CPU_shape_compile\n"); std::printf("[GpuShaderWarmup] queue_thread=started overlap=%s\n",
bGpuFarWarmupBeforeWorkers?"none_before_generation_workers":"CPU_shape_compile");
std::fflush(stdout); std::fflush(stdout);
} }
@@ -5520,7 +5534,14 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
int main(int Argc,char** Argv) int main(int Argc,char** Argv)
{ {
SetPriorityClass(GetCurrentProcess(),BELOW_NORMAL_PRIORITY_CLASS); SetPriorityClass(GetCurrentProcess(),BELOW_NORMAL_PRIORITY_CLASS);
SetProcessAffinityMask(GetCurrentProcess(),static_cast<DWORD_PTR>(0xFF00)); const bool bAllCpuAffinity=std::find_if(Argv+1,Argv+Argc,[](const char* Arg)
{return std::string_view(Arg)=="--affinity-all-cpu";})!=Argv+Argc;
const DWORD_PTR RequestedAffinity=static_cast<DWORD_PTR>(bAllCpuAffinity?0xFFFF:0xFF00);
const BOOL bAffinitySet=SetProcessAffinityMask(GetCurrentProcess(),RequestedAffinity);
DWORD_PTR ProcessAffinity=0,SystemAffinity=0;
GetProcessAffinityMask(GetCurrentProcess(),&ProcessAffinity,&SystemAffinity);
std::printf("[ProcessControls] priority_class=BelowNormal affinity_mask=0x%llX set=%s\n",
static_cast<unsigned long long>(ProcessAffinity),bAffinitySet?"true":"false");
const auto ColdProcessStart=std::chrono::steady_clock::now(); const auto ColdProcessStart=std::chrono::steady_clock::now();
FApp App;App.DataPath=DefaultDataPath().string();App.ColdProcessStart=ColdProcessStart; FApp App;App.DataPath=DefaultDataPath().string();App.ColdProcessStart=ColdProcessStart;
wchar_t ExecutablePath[MAX_PATH]{}; wchar_t ExecutablePath[MAX_PATH]{};
@@ -5584,6 +5605,18 @@ int main(int Argc,char** Argv)
App.bGpuFarEnabled=true,App.bGpuFarShaderPrewarm=true,App.bGpuFarWarmupResourcesOnly=true, App.bGpuFarEnabled=true,App.bGpuFarShaderPrewarm=true,App.bGpuFarWarmupResourcesOnly=true,
App.bShapeWorld=true,App.bForceGenerate=true,App.bPresentCpuFrameBeforeGpu=true, App.bShapeWorld=true,App.bForceGenerate=true,App.bPresentCpuFrameBeforeGpu=true,
App.bHoldCpuFrameDuringGpuWarmup=true; App.bHoldCpuFrameDuringGpuWarmup=true;
else if(Arg=="--gpu-far-warmup-normal-priority")
App.bGpuFarWarmupNormalPriority=true;
else if(Arg=="--gpu-far-warmup-before-workers")
App.bGpuFarEnabled=true,App.bGpuFarShaderPrewarm=true,App.bGpuFarWarmupBeforeWorkers=true,
App.bShapeWorld=true,App.bForceGenerate=true,App.bPresentCpuFrameBeforeGpu=true,
App.bHoldCpuFrameDuringGpuWarmup=true;
else if(Arg=="--gpu-far-warmup-only")
App.bGpuFarEnabled=true,App.bGpuFarShaderPrewarm=true,App.bGpuFarWarmupOnly=true,
App.bShapeWorld=true,App.bForceGenerate=true;
else if(Arg.starts_with("--gpu-far-warmup-entry="))
App.GpuWarmupEntryFilter=std::string(Arg.substr(std::string_view("--gpu-far-warmup-entry=").size()));
else if(Arg=="--affinity-all-cpu"){}
else if(Arg=="--gpu-far-no-present-hold") else if(Arg=="--gpu-far-no-present-hold")
App.bDisableGpuWarmupPresentHold=true; App.bDisableGpuWarmupPresentHold=true;
else if(Arg=="--gpu-far-no-prewarm") else if(Arg=="--gpu-far-no-prewarm")
@@ -5836,12 +5869,50 @@ int main(int Argc,char** Argv)
std::printf("[ColdTimeline] stage=viewer_device_init end_ms=%.3f duration_ms=%.3f\n", std::printf("[ColdTimeline] stage=viewer_device_init end_ms=%.3f duration_ms=%.3f\n",
std::chrono::duration<double,std::milli>(std::chrono::steady_clock::now()-ColdProcessStart).count(), std::chrono::duration<double,std::milli>(std::chrono::steady_clock::now()-ColdProcessStart).count(),
std::chrono::duration<double,std::milli>(std::chrono::steady_clock::now()-DeviceInitStart).count()); std::chrono::duration<double,std::milli>(std::chrono::steady_clock::now()-DeviceInitStart).count());
if(!App.bGpuFarWarmupOnly)
{
ShowWindow(App.Window,SW_SHOW);UpdateWindow(App.Window); ShowWindow(App.Window,SW_SHOW);UpdateWindow(App.Window);
std::printf("Controls: hold right mouse to look; WASD move; Space/Ctrl (E/Q) up/down; Shift faster; wheel changes speed; F wireframe; G toggles legacy/detailed round walls; H toggles CPU/GPU far tiles; 1-9 toggle LOD; 0 toggle all levels; N/P change seed; T swaps base/round tunnels; V swaps owner density/shape intervals; Esc quit.\n"); std::printf("Controls: hold right mouse to look; WASD move; Space/Ctrl (E/Q) up/down; Shift faster; wheel changes speed; F wireframe; G toggles legacy/detailed round walls; H toggles CPU/GPU far tiles; 1-9 toggle LOD; 0 toggle all levels; N/P change seed; T swaps base/round tunnels; V swaps owner density/shape intervals; Esc quit.\n");
std::printf("Owner data: %s; starting at first-strate open-space sample (%.2f, %.2f, %.2f) m.\n", std::printf("Owner data: %s; starting at first-strate open-space sample (%.2f, %.2f, %.2f) m.\n",
App.DataPath.c_str(),App.Position.X,App.Position.Y,App.Position.Z); App.DataPath.c_str(),App.Position.X,App.Position.Y,App.Position.Z);
}
std::fflush(stdout); std::fflush(stdout);
if(App.bGpuFarWarmupOnly)
{
const auto WarmupOnlyStart=std::chrono::steady_clock::now();
std::string WarmupError;
if(!VoxelForgeGpuFarViewer::QueueFarShaderWarmup(App.Device,App.Context,App.GpuFarShaderPath,
true,App.GpuWarmupEntryFilter,WarmupError))
{std::fprintf(stderr,"[GpuShaderWarmupError] queue: %s\n",WarmupError.c_str());return 5;}
if(!VoxelForgeGpuFarViewer::CompleteFarShaderWarmup(App.Device,App.Context,
App.GpuFarShaderPath,WarmupError))
{std::fprintf(stderr,"[GpuShaderWarmupError] completion: %s\n",WarmupError.c_str());return 5;}
uint64 WarmupDxgiUsage=0,WarmupDxgiBudget=0;
if(VoxelForgeGpuFarViewer::QueryFarGpuMemory(App.Device,App.Context,App.GpuFarShaderPath,
WarmupDxgiUsage,WarmupDxgiBudget,WarmupError))
std::printf("[GpuFarMemory] dxgi_local_sampled_peak_mb=%.3f budget_mb=%.3f\n",
static_cast<double>(WarmupDxgiUsage)/1048576.0,
static_cast<double>(WarmupDxgiBudget)/1048576.0);
else
std::printf("[GpuFarMemory] dxgi_local_sampled_peak_mb=unavailable reason=%s\n",
WarmupError.c_str());
std::printf("[GpuShaderWarmupOnlyComplete] entry=%s total_ms=%.3f\n",
App.GpuWarmupEntryFilter.empty()?"all":App.GpuWarmupEntryFilter.c_str(),
std::chrono::duration<double,std::milli>(std::chrono::steady_clock::now()-WarmupOnlyStart).count());
return 0;
}
App.StartGpuShaderWarmupAsync(); App.StartGpuShaderWarmupAsync();
if(App.bGpuFarWarmupBeforeWorkers)
{
if(App.GpuShaderWarmupThread.joinable())App.GpuShaderWarmupThread.join();
if(!App.bGpuShaderWarmupQueueSucceeded)
{std::fprintf(stderr,"[GpuShaderWarmupError] queue: %s\n",App.GpuShaderWarmupQueueError.c_str());return 5;}
std::string WarmupError;
if(!VoxelForgeGpuFarViewer::CompleteFarShaderWarmup(App.Device,App.Context,
App.GpuFarShaderPath,WarmupError))
{std::fprintf(stderr,"[GpuShaderWarmupError] completion: %s\n",WarmupError.c_str());return 5;}
std::printf("[GpuShaderWarmupBeforeWorkers] completed_before_generation_workers=true\n");
}
App.StartGeneration(); App.StartGeneration();
MSG Message{}; MSG Message{};
while(Message.message!=WM_QUIT) while(Message.message!=WM_QUIT)