diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/BuildGpuFieldViewer.ps1 b/Standalone/Research/OwnerShapeWorld/gpu_field_port/BuildGpuFieldViewer.ps1 index 999011d..30a7ac9 100644 --- a/Standalone/Research/OwnerShapeWorld/gpu_field_port/BuildGpuFieldViewer.ps1 +++ b/Standalone/Research/OwnerShapeWorld/gpu_field_port/BuildGpuFieldViewer.ps1 @@ -120,7 +120,7 @@ if ($Target -eq 'All') { Sort-Object -Property FullName -Descending | Select-Object -First 1 if ($FxC) { [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') $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" diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/CompareStep12Captures.py b/Standalone/Research/OwnerShapeWorld/gpu_field_port/CompareStep12Captures.py new file mode 100644 index 0000000..efb26eb --- /dev/null +++ b/Standalone/Research/OwnerShapeWorld/gpu_field_port/CompareStep12Captures.py @@ -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="") diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/GpuFieldPort.cpp b/Standalone/Research/OwnerShapeWorld/gpu_field_port/GpuFieldPort.cpp index b17693b..6f9bc03 100644 --- a/Standalone/Research/OwnerShapeWorld/gpu_field_port/GpuFieldPort.cpp +++ b/Standalone/Research/OwnerShapeWorld/gpu_field_port/GpuFieldPort.cpp @@ -551,7 +551,7 @@ namespace ComPtr device; ComPtr context; ComPtr immediateContext,deferredContext; - ComPtr fieldEval, classify, fieldClassify, scan, emit, emitNear, noiseProbe; + ComPtr fieldEval, fieldEvalNear, classify, fieldClassify, scan, emit, emitNear, noiseProbe; ComPtr connectorRings,connectorPanels; ComPtr classifyFarSkirts,scanFarSkirts,emitFarSkirts; ComPtr memoryAdapter; @@ -595,6 +595,7 @@ namespace entry,std::chrono::duration(std::chrono::steady_clock::now()-CreateStart).count()); }; Create("EvaluateFarFieldCS",fieldEval); + Create("EvaluateNearFieldCS",fieldEvalNear); Create("ClassifyCS",classify); Create("ClassifyFarCS",fieldClassify); Create("ScanGroupsCS",scan); @@ -613,8 +614,8 @@ namespace void QueueTinyShaderWarmup(bool dispatchShaders,const std::string& entryFilter) { const auto WarmupStart=Clock::now(); - static constexpr std::array entries={ - "EvaluateFarFieldCS","ClassifyCS","ClassifyFarCS","ScanGroupsCS","EmitCS", + static constexpr std::array entries={ + "EvaluateFarFieldCS","EvaluateNearFieldCS","ClassifyCS","ClassifyFarCS","ScanGroupsCS","EmitCS", "EmitNearCS","BuildFarConnectorRingsCS","EmitFarConnectorPanelsCS", "ClassifyFarSkirtsCS","ScanFarSkirtsCS","EmitFarSkirtsCS","NoiseProbeCS"}; if(!entryFilter.empty()&&std::find(entries.begin(),entries.end(),entryFilter)==entries.end()) @@ -623,7 +624,8 @@ namespace TileConstants tile{}; tile.cells=1;tile.sampleDim=3;tile.cellCount=cellCount;tile.groupCount=groupCount; 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; FarFieldConstants field{}; 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(); context->CSSetUnorderedAccessViews(0,8,evalUavs,nullptr); Dispatch(fieldEval.Get(),"EvaluateFarFieldCS");ClearBindings(); + Dispatch(fieldEvalNear.Get(),"EvaluateNearFieldCS");ClearBindings(); ID3D11ShaderResourceView* classifySrvs[16]{}; classifySrvs[0]=signSrv.Get();classifySrvs[1]=triangleSrv.Get(); @@ -1612,7 +1615,7 @@ namespace beginKernel(0); context->CSSetConstantBuffers(1,1,farCb.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]{}; fieldSrvs[10]=farShapeSrv.Get();fieldSrvs[11]=farRangeSrv.Get(); fieldSrvs[12]=farIdSrv.Get();fieldSrvs[13]=farProtectedSrv.Get(); diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep12CaptureMatrix.ps1 b/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep12CaptureMatrix.ps1 new file mode 100644 index 0000000..c3d78ab --- /dev/null +++ b/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep12CaptureMatrix.ps1 @@ -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 + } + } +} diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep12ConnectorLoopMeasurement.ps1 b/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep12ConnectorLoopMeasurement.ps1 new file mode 100644 index 0000000..982936d --- /dev/null +++ b/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep12ConnectorLoopMeasurement.ps1 @@ -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 + } +} diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep12CpuReference.ps1 b/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep12CpuReference.ps1 new file mode 100644 index 0000000..d8b6818 --- /dev/null +++ b/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep12CpuReference.ps1 @@ -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 + } +} diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep12FinalInterleaved.ps1 b/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep12FinalInterleaved.ps1 new file mode 100644 index 0000000..5313a1a --- /dev/null +++ b/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep12FinalInterleaved.ps1 @@ -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 + } +} diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep12FinalShaderFirstUse.ps1 b/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep12FinalShaderFirstUse.ps1 new file mode 100644 index 0000000..c0c25d0 --- /dev/null +++ b/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep12FinalShaderFirstUse.ps1 @@ -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 + } +} diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep12GpuCrossing.ps1 b/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep12GpuCrossing.ps1 new file mode 100644 index 0000000..0508625 --- /dev/null +++ b/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep12GpuCrossing.ps1 @@ -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 +} diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep12GpuSurvival.ps1 b/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep12GpuSurvival.ps1 new file mode 100644 index 0000000..0796827 --- /dev/null +++ b/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep12GpuSurvival.ps1 @@ -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 +} diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep12PostPoolInterleaved.ps1 b/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep12PostPoolInterleaved.ps1 new file mode 100644 index 0000000..4351030 --- /dev/null +++ b/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep12PostPoolInterleaved.ps1 @@ -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 + } +} diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep12ShaderBaseline.ps1 b/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep12ShaderBaseline.ps1 new file mode 100644 index 0000000..194a77c --- /dev/null +++ b/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep12ShaderBaseline.ps1 @@ -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 + } +} diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep12ShaderFirstUse.ps1 b/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep12ShaderFirstUse.ps1 new file mode 100644 index 0000000..6e63c20 --- /dev/null +++ b/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep12ShaderFirstUse.ps1 @@ -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 + } +} diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep12SkirtLoopMeasurement.ps1 b/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep12SkirtLoopMeasurement.ps1 new file mode 100644 index 0000000..d42924d --- /dev/null +++ b/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep12SkirtLoopMeasurement.ps1 @@ -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 + } +} diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep12WholeNearAgreement.ps1 b/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep12WholeNearAgreement.ps1 new file mode 100644 index 0000000..d10bb07 --- /dev/null +++ b/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep12WholeNearAgreement.ps1 @@ -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 +} diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/STEP12_REPORT.md b/Standalone/Research/OwnerShapeWorld/gpu_field_port/STEP12_REPORT.md new file mode 100644 index 0000000..a7602a1 --- /dev/null +++ b/Standalone/Research/OwnerShapeWorld/gpu_field_port/STEP12_REPORT.md @@ -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. diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/precompiled/BuildFarConnectorRingsCS.cso b/Standalone/Research/OwnerShapeWorld/gpu_field_port/precompiled/BuildFarConnectorRingsCS.cso index 7a26919..34b9628 100644 Binary files a/Standalone/Research/OwnerShapeWorld/gpu_field_port/precompiled/BuildFarConnectorRingsCS.cso and b/Standalone/Research/OwnerShapeWorld/gpu_field_port/precompiled/BuildFarConnectorRingsCS.cso differ diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/precompiled/ClassifyFarSkirtsCS.cso b/Standalone/Research/OwnerShapeWorld/gpu_field_port/precompiled/ClassifyFarSkirtsCS.cso index e574a4a..c49ed6c 100644 Binary files a/Standalone/Research/OwnerShapeWorld/gpu_field_port/precompiled/ClassifyFarSkirtsCS.cso and b/Standalone/Research/OwnerShapeWorld/gpu_field_port/precompiled/ClassifyFarSkirtsCS.cso differ diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/precompiled/EmitFarSkirtsCS.cso b/Standalone/Research/OwnerShapeWorld/gpu_field_port/precompiled/EmitFarSkirtsCS.cso index fd3ed55..b65d9d7 100644 Binary files a/Standalone/Research/OwnerShapeWorld/gpu_field_port/precompiled/EmitFarSkirtsCS.cso and b/Standalone/Research/OwnerShapeWorld/gpu_field_port/precompiled/EmitFarSkirtsCS.cso differ diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/precompiled/EvaluateFarFieldCS.cso b/Standalone/Research/OwnerShapeWorld/gpu_field_port/precompiled/EvaluateFarFieldCS.cso index df0598b..51e4c5b 100644 Binary files a/Standalone/Research/OwnerShapeWorld/gpu_field_port/precompiled/EvaluateFarFieldCS.cso and b/Standalone/Research/OwnerShapeWorld/gpu_field_port/precompiled/EvaluateFarFieldCS.cso differ diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/precompiled/EvaluateNearFieldCS.cso b/Standalone/Research/OwnerShapeWorld/gpu_field_port/precompiled/EvaluateNearFieldCS.cso new file mode 100644 index 0000000..5686502 Binary files /dev/null and b/Standalone/Research/OwnerShapeWorld/gpu_field_port/precompiled/EvaluateNearFieldCS.cso differ diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/step12_capture_comparison.md b/Standalone/Research/OwnerShapeWorld/gpu_field_port/step12_capture_comparison.md new file mode 100644 index 0000000..bde3ba2 --- /dev/null +++ b/Standalone/Research/OwnerShapeWorld/gpu_field_port/step12_capture_comparison.md @@ -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 | diff --git a/Standalone/Research/OwnerShapeWorld/gpu_first_port/VoxelForgeGpuMesher.hlsl b/Standalone/Research/OwnerShapeWorld/gpu_first_port/VoxelForgeGpuMesher.hlsl index 89062de..7922877 100644 --- a/Standalone/Research/OwnerShapeWorld/gpu_first_port/VoxelForgeGpuMesher.hlsl +++ b/Standalone/Research/OwnerShapeWorld/gpu_first_port/VoxelForgeGpuMesher.hlsl @@ -464,73 +464,85 @@ void EvaluateFarFieldCS(uint3 dispatchId : SV_DispatchThreadID) + float3(int(x) - 1, int(y) - 1, int(z) - 1) * StepVoxels; float density; uint distanceEvaluations=0; - float nearNormalDensity=0.0f; - if(NearFieldMode!=0) + const FarFieldSpatialBin range = FarFieldFindBin(position); + float cave = 100000.0f, overlay = -100000.0f; + [loop] + for(uint item=0;item0.0f?0.05f:-0.05f); - const uint packedProxy=FarNearPackedProxy[index]; - if(packedProxy!=0xffffffffu) + const VF_FieldPrimitive shape=FarShapes[FarSpatialBinShapeIds[range.offset+item]]; + if((shape.flags&VF_FIELD_PRIMITIVE_CARVE)!=0) { - // 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; + if(position.xshape.maxVoxel[0]+6 + ||position.yshape.maxVoxel[1]+6 + ||position.zshape.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; } } - else - { - const FarFieldSpatialBin range=FarFieldFindBin(position); - float cave=100000.0f,overlay=-100000.0f; - [loop] - for(uint item=0;itemshape.maxVoxel[0]+6 - ||position.yshape.maxVoxel[1]+6 - ||position.zshape.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 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 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),protectedMinimum); - else if(protectedSolid!=0)density=-max(abs(density),protectedMinimum); + if(protectedAir!=0)density=max(abs(density),0.05f); + else if(protectedSolid!=0)density=-max(abs(density),0.05f); FarFieldDensity[index] = density; - FarNearNormalDensityOutput[index]=nearNormalDensity; + FarNearNormalDensityOutput[index]=0.0f; 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 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; polygonCount=0; - [unroll] + [loop] for(uint edge=0;edge<3;++edge) { FarSkirtVertex start=input[edge]; @@ -778,7 +790,7 @@ float FarConnectorRadius(float3 center, float3 direction, float nominalRadius) residual=FarRockDistanceAt(center+direction*radius,cachedBinCoord,cachedRange); converged=abs(residual)<=1.0f; } - [unroll] + [loop] for(uint refine=0;refine<2&&!converged;++refine) { const float distance=residual; @@ -807,7 +819,7 @@ float FarConnectorRadius(float3 center, float3 direction, float nominalRadius) { float lo=previous,hi=distance; const bool previousSolid=previousDistance>=0.0f; - [unroll] + [loop] for(uint refine=0;refine<8;++refine) { const float mid=(lo+hi)*0.5f; diff --git a/Standalone/Source/StandaloneViewer.cpp b/Standalone/Source/StandaloneViewer.cpp index 49c2bb4..04f4781 100644 --- a/Standalone/Source/StandaloneViewer.cpp +++ b/Standalone/Source/StandaloneViewer.cpp @@ -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, Eligible.empty()||Eligible.back()!=Key?"cpu_fallback":"gpu_eligible"); } + const std::unordered_set 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) std::printf("[GpuRemoteBandFallback] lod3_cpu_tiles=%llu lod4_cpu_tiles=%llu reason=remote_capture_pixel_coverage\n", static_cast(RemoteLod3CpuFallbackTiles),