wip(standalone): add missing GPU skirt shader binaries and step 6-8 scripts/report
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
1 parent
b6ac950073
commit
7f64e2e0aa
9 files changed
+348
No files matched your search
@@ -0,0 +1,32 @@
|
||||
$ErrorActionPreference='Stop'
|
||||
$log=Join-Path $PSScriptRoot 'step6_whole_near_packed.stdout.log'
|
||||
$tiles=[Collections.Generic.List[object]]::new()
|
||||
foreach($line in [IO.File]::ReadLines($log)){
|
||||
if(-not $line.StartsWith('[GpuAgreement]')){continue}
|
||||
$count=[regex]::Match($line,'vertex_comparisons_cpu_gpu=(\d+)')
|
||||
$mean=[regex]::Match($line,'vertex_mean_cm=([0-9.eE+-]+)')
|
||||
$max=[regex]::Match($line,'vertex_max_cm=([0-9.eE+-]+)')
|
||||
$topology=[regex]::Match($line,'topology_diff_cells_hw=(\d+) topology_diff_cells_warp=(\d+)')
|
||||
$repeat=[regex]::Match($line,'repeated_gpu_outputs_identical=(\d)')
|
||||
if($count.Success -and $mean.Success -and $max.Success -and $topology.Success){
|
||||
$tiles.Add([pscustomobject]@{
|
||||
Count=[double]$count.Groups[1].Value
|
||||
MeanCm=[double]::Parse($mean.Groups[1].Value,[Globalization.CultureInfo]::InvariantCulture)
|
||||
MaxCm=[double]::Parse($max.Groups[1].Value,[Globalization.CultureInfo]::InvariantCulture)
|
||||
TopologyGpu=[int64]$topology.Groups[1].Value
|
||||
TopologyWarp=[int64]$topology.Groups[2].Value
|
||||
Repeat=if($repeat.Success){[int]$repeat.Groups[1].Value}else{0}
|
||||
})
|
||||
}
|
||||
}
|
||||
$compared=($tiles|Measure-Object -Property Count -Sum).Sum
|
||||
$weighted=0.0
|
||||
foreach($tile in $tiles){$weighted+=$tile.Count*$tile.MeanCm}
|
||||
$maxTile=$tiles|Sort-Object MaxCm -Descending|Select-Object -First 1
|
||||
$maxGpuTopology=($tiles|Measure-Object -Property TopologyGpu -Sum).Sum
|
||||
$maxWarpTopology=($tiles|Measure-Object -Property TopologyWarp -Sum).Sum
|
||||
$repeatFailures=($tiles|Where-Object Repeat -ne 1).Count
|
||||
Write-Output ("tiles={0} vertex_pairs={1} weighted_mean_cm={2:R} weighted_mean_mm={3:R} max_cm={4:R} max_mm={5:R} max_tile={6} topology_gpu={7} topology_warp={8} repeat_failures={9}" -f `
|
||||
$tiles.Count,[int64]$compared,($weighted/[Math]::Max(1.0,$compared)),
|
||||
(10.0*$weighted/[Math]::Max(1.0,$compared)),$maxTile.MaxCm,(10.0*$maxTile.MaxCm),
|
||||
$maxTile.ToString(),[int64]$maxGpuTopology,[int64]$maxWarpTopology,$repeatFailures)
|
||||
@@ -0,0 +1,105 @@
|
||||
[CmdletBinding()]
|
||||
param([switch]$SkipCore,[string]$OutputName='VoxelForgeGpuFieldStep6NearRunner.exe')
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$HostProcess = Get-Process -Id $PID
|
||||
$HostProcess.PriorityClass = 'BelowNormal'
|
||||
$HostProcess.ProcessorAffinity = [IntPtr]0xFF00
|
||||
$Standalone = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..\..\..'))
|
||||
$BuildDir = Join-Path $Standalone 'Build\VoxelForgeGpuFieldStep6NearRunner'
|
||||
$WrapperDir = Join-Path $BuildDir 'wrappers'
|
||||
$BinDir = Join-Path $Standalone 'bin'
|
||||
$Output = Join-Path $BinDir $OutputName
|
||||
$CoreLib = Join-Path $BuildDir 'VoxelForgeGpuFieldPortCore.lib'
|
||||
$VcVars = 'E:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat'
|
||||
|
||||
if (-not (Test-Path -LiteralPath $VcVars -PathType Leaf)) { throw "MSVC setup script missing: $VcVars" }
|
||||
[IO.Directory]::CreateDirectory($BuildDir) | Out-Null
|
||||
[IO.Directory]::CreateDirectory($WrapperDir) | Out-Null
|
||||
[IO.Directory]::CreateDirectory($BinDir) | Out-Null
|
||||
|
||||
$Manifest = Join-Path $Standalone 'Core\core_sources.txt'
|
||||
$Sources = @(Get-Content -LiteralPath $Manifest | ForEach-Object { $_.Trim() } |
|
||||
Where-Object { $_ -and -not $_.StartsWith('#') })
|
||||
$CompileSources = [Collections.Generic.List[string]]::new()
|
||||
foreach ($SourceName in $Sources) {
|
||||
$Source = [IO.Path]::GetFullPath((Join-Path $Standalone $SourceName))
|
||||
if (-not $Source.StartsWith($Standalone, [StringComparison]::OrdinalIgnoreCase)) {
|
||||
throw "Core source escapes Standalone: $SourceName"
|
||||
}
|
||||
if ($SourceName.Replace('\','/') -eq 'Core/src/VoxelForgeCore.cpp') {
|
||||
$CompileSources.Add($Source)
|
||||
continue
|
||||
}
|
||||
$WrapperName = 'gpu_vfcore_' + ($SourceName.Replace('\','/') -replace '[^A-Za-z0-9]+','_')
|
||||
$WrapperPath = Join-Path $WrapperDir ($WrapperName + '.cpp')
|
||||
$SourceInclude = $Source.Replace('\','/')
|
||||
$WrapperText = @"
|
||||
#include "CoreNamespacePreamble.h"
|
||||
namespace vfcore::detail
|
||||
{
|
||||
#include "$SourceInclude"
|
||||
}
|
||||
"@
|
||||
[IO.File]::WriteAllText($WrapperPath, $WrapperText, [Text.UTF8Encoding]::new($false))
|
||||
$CompileSources.Add($WrapperPath)
|
||||
}
|
||||
|
||||
$IncludeArgs = @(
|
||||
('/I"{0}"' -f (Join-Path $Standalone 'Core\include')),
|
||||
('/I"{0}"' -f (Join-Path $Standalone 'Core\src')),
|
||||
('/I"{0}"' -f (Join-Path $Standalone 'Compat')),
|
||||
('/I"{0}"' -f (Join-Path $Standalone 'Source')),
|
||||
('/I"{0}"' -f (Join-Path $Standalone 'Source\VoxelForge\Public')),
|
||||
('/I"{0}"' -f (Join-Path $Standalone 'Source\VoxelForge\Private'))
|
||||
)
|
||||
$Common = @('/nologo','/std:c++20','/O2','/arch:AVX2','/fp:precise','/EHsc','/MP8','/D_CRT_SECURE_NO_WARNINGS') + $IncludeArgs
|
||||
$CoreRsp = Join-Path $BuildDir 'field_core_compile.rsp'
|
||||
[IO.File]::WriteAllLines($CoreRsp, $Common + @('/c') + @($CompileSources | ForEach-Object { '"{0}"' -f $_ }), [Text.UTF8Encoding]::new($false))
|
||||
$ObjectNames = @($CompileSources | ForEach-Object { '"{0}.obj"' -f [IO.Path]::GetFileNameWithoutExtension($_) })
|
||||
$LibRsp = Join-Path $BuildDir 'field_core_library.rsp'
|
||||
[IO.File]::WriteAllLines($LibRsp, @('/nologo',('/OUT:"{0}"' -f $CoreLib)) + $ObjectNames, [Text.UTF8Encoding]::new($false))
|
||||
$RunnerRsp = Join-Path $BuildDir 'field_runner_link.rsp'
|
||||
$Runner = Join-Path $PSScriptRoot 'GpuFieldPort.cpp'
|
||||
$RunnerRspLines = $Common + @(
|
||||
('/Fe"{0}"' -f $Output),
|
||||
('"{0}"' -f $Runner),
|
||||
('"{0}"' -f $CoreLib),
|
||||
'/link','d3d11.lib','dxgi.lib','d3dcompiler.lib','user32.lib','gdi32.lib','psapi.lib'
|
||||
)
|
||||
[IO.File]::WriteAllLines($RunnerRsp, $RunnerRspLines, [Text.UTF8Encoding]::new($false))
|
||||
|
||||
Push-Location $BuildDir
|
||||
try {
|
||||
$BuildCommands = [Collections.Generic.List[string]]::new()
|
||||
if (-not $SkipCore) {
|
||||
$BuildCommands.Add(('call "{0}" >nul && cl.exe @"{1}"' -f $VcVars, $CoreRsp))
|
||||
$BuildCommands.Add(('call "{0}" >nul && lib.exe @"{1}"' -f $VcVars, $LibRsp))
|
||||
}
|
||||
$BuildCommands.Add(('call "{0}" >nul && cl.exe @"{1}"' -f $VcVars, $RunnerRsp))
|
||||
foreach ($BuildCommand in $BuildCommands) {
|
||||
$attempt = 0
|
||||
do {
|
||||
& $env:ComSpec /d /s /c $BuildCommand
|
||||
$code = $LASTEXITCODE
|
||||
if ($code -eq -1073741502 -and $attempt -eq 0) {
|
||||
Write-Host '0xC0000142 encountered; waiting 60 seconds before the single permitted retry.'
|
||||
Start-Sleep -Seconds 60
|
||||
$attempt++
|
||||
continue
|
||||
}
|
||||
if ($code -eq -1073741502) { throw '0xC0000142 repeated after the single retry; stopping.' }
|
||||
if ($code -ne 0) { throw "GPU port build command failed with exit code $code" }
|
||||
break
|
||||
} while ($true)
|
||||
}
|
||||
if (-not $SkipCore) {
|
||||
& (Join-Path $Standalone 'Core\CheckCoreSymbols.ps1') -LibraryPath $CoreLib
|
||||
if ($LASTEXITCODE -ne 0) { throw "GPU port Core symbol check failed with exit code $LASTEXITCODE" }
|
||||
}
|
||||
if (-not (Test-Path -LiteralPath $Output -PathType Leaf)) { throw "GPU runner missing: $Output" }
|
||||
} finally {
|
||||
Pop-Location
|
||||
}
|
||||
Write-Host "Built $CoreLib"
|
||||
Write-Host "Built $Output"
|
||||
@@ -0,0 +1,64 @@
|
||||
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"step6_cpu_{view}.png"
|
||||
gpu_path = ROOT / f"step6_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": view,
|
||||
"size": f"{cpu.shape[1]}x{cpu.shape[0]}",
|
||||
"mae_rgb": float(diff.mean()),
|
||||
"rmse_rgb": float(np.sqrt(np.square(diff, dtype=np.float64).mean())),
|
||||
"p95_pixel_max": float(np.percentile(pixel_max, 95)),
|
||||
"p99_pixel_max": float(np.percentile(pixel_max, 99)),
|
||||
"max_pixel_channel": int(pixel_max.max()),
|
||||
"changed_pct": float(np.count_nonzero(pixel_max) * 100.0 / pixel_max.size),
|
||||
"over_8_pct": float(np.count_nonzero(pixel_max > 8) * 100.0 / pixel_max.size),
|
||||
"over_32_pct": float(np.count_nonzero(pixel_max > 32) * 100.0 / pixel_max.size),
|
||||
}
|
||||
)
|
||||
|
||||
lines = [
|
||||
"# Step 6 CPU/GPU capture comparison",
|
||||
"",
|
||||
"Each pair is a 1280x800 back-buffer capture from the fixed camera. `mae_rgb` and `rmse_rgb` are channel errors on the 0 to 255 scale; pixel percentiles use the maximum RGB channel error per pixel.",
|
||||
"",
|
||||
"| View | MAE RGB | RMSE RGB | p95 pixel max | p99 pixel max | Max channel | Changed pixels | >8 | >32 |",
|
||||
"|---|---:|---:|---:|---:|---:|---:|---:|---:|",
|
||||
]
|
||||
for row in rows:
|
||||
lines.append(
|
||||
f"| {row['view']} | {row['mae_rgb']:.4f} | {row['rmse_rgb']:.4f} | "
|
||||
f"{row['p95_pixel_max']:.0f} | {row['p99_pixel_max']:.0f} | {row['max_pixel_channel']} | "
|
||||
f"{row['changed_pct']:.2f}% | {row['over_8_pct']:.2f}% | {row['over_32_pct']:.2f}% |"
|
||||
)
|
||||
|
||||
report = ROOT / "step6_capture_comparison.md"
|
||||
report.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
||||
print("\n".join(lines))
|
||||
print(f"\nWrote {report}")
|
||||
@@ -0,0 +1,64 @@
|
||||
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"step7_cpu_{view}.png"
|
||||
gpu_path = ROOT / f"step7_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": view,
|
||||
"size": f"{cpu.shape[1]}x{cpu.shape[0]}",
|
||||
"mae_rgb": float(diff.mean()),
|
||||
"rmse_rgb": float(np.sqrt(np.square(diff, dtype=np.float64).mean())),
|
||||
"p95_pixel_max": float(np.percentile(pixel_max, 95)),
|
||||
"p99_pixel_max": float(np.percentile(pixel_max, 99)),
|
||||
"max_pixel_channel": int(pixel_max.max()),
|
||||
"changed_pct": float(np.count_nonzero(pixel_max) * 100.0 / pixel_max.size),
|
||||
"over_8_pct": float(np.count_nonzero(pixel_max > 8) * 100.0 / pixel_max.size),
|
||||
"over_32_pct": float(np.count_nonzero(pixel_max > 32) * 100.0 / pixel_max.size),
|
||||
}
|
||||
)
|
||||
|
||||
lines = [
|
||||
"# Step 6 CPU/GPU capture comparison",
|
||||
"",
|
||||
"Each pair is a 1280x800 back-buffer capture from the fixed camera. `mae_rgb` and `rmse_rgb` are channel errors on the 0 to 255 scale; pixel percentiles use the maximum RGB channel error per pixel.",
|
||||
"",
|
||||
"| View | MAE RGB | RMSE RGB | p95 pixel max | p99 pixel max | Max channel | Changed pixels | >8 | >32 |",
|
||||
"|---|---:|---:|---:|---:|---:|---:|---:|---:|",
|
||||
]
|
||||
for row in rows:
|
||||
lines.append(
|
||||
f"| {row['view']} | {row['mae_rgb']:.4f} | {row['rmse_rgb']:.4f} | "
|
||||
f"{row['p95_pixel_max']:.0f} | {row['p99_pixel_max']:.0f} | {row['max_pixel_channel']} | "
|
||||
f"{row['changed_pct']:.2f}% | {row['over_8_pct']:.2f}% | {row['over_32_pct']:.2f}% |"
|
||||
)
|
||||
|
||||
report = ROOT / "step7_capture_comparison.md"
|
||||
report.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
||||
print("\n".join(lines))
|
||||
print(f"\nWrote {report}")
|
||||
@@ -0,0 +1,25 @@
|
||||
[CmdletBinding()]
|
||||
param()
|
||||
$ErrorActionPreference='Stop'
|
||||
$hostProcess=Get-Process -Id $PID
|
||||
$hostProcess.PriorityClass='BelowNormal'
|
||||
$hostProcess.ProcessorAffinity=[IntPtr]0xFF00
|
||||
$standalone=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..\..\..'))
|
||||
$shader=Join-Path $standalone 'Research\OwnerShapeWorld\gpu_first_port\VoxelForgeGpuMesher.hlsl'
|
||||
$output=Join-Path $PSScriptRoot 'precompiled'
|
||||
$fxc=(Get-ChildItem -LiteralPath 'C:\Program Files (x86)\Windows Kits\10\bin' -Filter fxc.exe -Recurse -ErrorAction SilentlyContinue |
|
||||
Where-Object {$_.FullName -match '\\x64\\fxc\.exe$'} | Sort-Object FullName -Descending | Select-Object -First 1).FullName
|
||||
if(-not $fxc){throw 'fxc.exe was not found'}
|
||||
[IO.Directory]::CreateDirectory($output)|Out-Null
|
||||
foreach($entry in @('EvaluateFarFieldCS','ClassifyCS','ClassifyFarCS','ScanGroupsCS','EmitCS','NoiseProbeCS','BuildFarConnectorRingsCS','EmitFarConnectorPanelsCS')){
|
||||
$binary=Join-Path $output ($entry+'.cso')
|
||||
for($attempt=0;$attempt -lt 2;$attempt++){
|
||||
& $fxc /nologo /T cs_5_0 /E $entry /O3 /Gis /Ges /Fo $binary $shader
|
||||
$code=$LASTEXITCODE
|
||||
if($code -eq -1073741502 -and $attempt -eq 0){Write-Output '0xC0000142 encountered; waiting 60 seconds before one retry.';Start-Sleep -Seconds 60;continue}
|
||||
if($code -eq -1073741502){throw "0xC0000142 repeated while compiling $entry"}
|
||||
if($code -ne 0){throw "fxc failed for $entry with exit code $code"}
|
||||
break
|
||||
}
|
||||
Write-Output "COMPILED $entry -> $binary"
|
||||
}
|
||||
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,58 @@
|
||||
# GPU port, step 8 report
|
||||
|
||||
Build: `VoxelForgeGpuFieldStep8Final.exe`, from the current Standalone sources with FXC bytecode regenerated. All viewer runs used `/MP8`, BelowNormal priority, affinity `0xFF00`; the capture and interleaved-run scripts enforced the 8 GB process ceiling and closed each viewer they started. No `0xC0000142` occurred.
|
||||
|
||||
## First-present stall
|
||||
|
||||
The controlled no-hold runs reproduce a long first GPU-backed present on this machine. The first `Present` after one GPU tile had been submitted took 2.36–2.59 s, including with `DXGI_PRESENT_DO_NOT_WAIT`; it returned `DXGI_STATUS_OCCLUDED`. A CPU-only first present took 1.54 ms. The long call reproduced after a CPU run, after a 60-second idle, with a renamed executable, and in visible and hidden window controls. That identifies the trigger as the first GPU-backed present in the process. It does not identify the internal AMD/Windows cause; the owner's separate warm-process results remain different.
|
||||
|
||||
GPU mode now presents a CPU-ready frame before submitting far work, then retains that front buffer while the GPU clipmap warms. Across three interleaved GPU launches, the first CPU present took 2.45–2.84 ms at 0.958–0.983 s; the first fully resident GPU clipmap was ready at 4.093–4.137 s. The first post-warm present took 0.200–0.337 ms. This hides the long present from the first displayed frame; it does not make the full GPU clipmap ready sooner.
|
||||
|
||||
## Whole-view timing and GPU budget
|
||||
|
||||
Commands in `RunStep8ColdInterleaved.ps1`: `VoxelForgeGpuFieldStep8Final.exe --clip-radius=3 --workers=8 --quit-after-clipmap --cpu-far` and the same command with `--gpu-far`, interleaved CPU/GPU runs.
|
||||
|
||||
| Path | Process start to full clipmap ready |
|
||||
|---|---:|
|
||||
| CPU, A/B/C | 1.489 / 1.346 / 1.345 s |
|
||||
| GPU hybrid, A/B/C | 4.131 / 4.137 / 4.093 s |
|
||||
|
||||
The GPU hybrid median is 4.131 s versus 1.346 s for CPU, 2.785 s slower. Headless CPU clipmap generation on this final build was 0.780 s at 8 workers and 3.939 s at 1 worker.
|
||||
|
||||
For the GPU runs, 1,434 tiles were submitted in 46–47 batches of up to 32. GPU dispatch timestamps totaled 703–820 ms, or 0.490–0.572 ms per tile on average; the slowest tile took 79–127 ms. Maximum batch spans were 86–134 ms. At 90 Hz, the average suggests about 19–22 tiles per 11.111 ms slice, but the current per-tile and batch tails exceed a frame budget by a wide margin and need finer slicing.
|
||||
|
||||
CPU far-input preparation was 178–191 ms wall time (1.35–1.43 worker-seconds); upload/enqueue summed to 49–116 ms. The GPU queue envelope was 3.01–3.13 s while measured GPU busy time was 0.735–0.820 s; the timeline attributes 2.20–2.39 s to idle/poll time, with a single pump call taking about 1.92–2.08 s. The hybrid does not beat CPU yet. The measured floor is at least the 0.70–0.82 s of GPU dispatch plus preparation and submission; the remaining multi-second wait/pump overhead is still on the critical path.
|
||||
|
||||
Peak GPU-mode private memory was 3.84–3.99 GB and sampled DXGI local memory peaked at about 3.03 GB, conservatively below 8 GB combined.
|
||||
|
||||
## GPU skirts and field agreement
|
||||
|
||||
The skirt field query now applies the CPU-decided critical-passage primitive rule to arbitrary clipping probes, and evaluates triangle-center probes in the CPU's centimeter-to-voxel arithmetic order. Ordinary rock/air field values remain GPU-evaluated. This reduced the remote face audit from 13 mismatching tiles to one.
|
||||
|
||||
| Audit | Tiles | GPU skirt triangles | CPU/GPU skirt result |
|
||||
|---|---:|---:|---|
|
||||
| Owner-start camera, all exposed far faces | 1,098 | 42,828 | 0 triangle differences; mean vertex distance 0.000233 cm; max 0.11055 cm; GPU and WARP pass |
|
||||
| Remote camera `(500,0,0)`, all exposed far faces | 1,138 | 43,676 | 8 triangle differences on tile `(4,3,-1,lod3)`; mean 0.000504 cm; max 17.9066 cm; GPU and WARP fail that tile |
|
||||
|
||||
The remote skirt audit is therefore not a full pass. Its remaining discrepancy is too large to waive as float noise.
|
||||
|
||||
The whole remote far-surface audit tested all 1,150 eligible tiles (116 near-transition keys were intentionally skipped). Hardware and WARP had zero lattice sign/topology differences; all 145,494 protected cells and 88 thin-feature cells had zero differences. Repeat GPU outputs were bit-identical. CPU/GPU surface vertex mean distance was 0.1163 cm (1.163 mm), max 64.5602 cm across 2,543,435 comparisons, with 245 tiles over the quantization bound. These largest outliers are far/off-view LOD7/8 vertices. GPU/WARP mean was 0.000453 cm and max 0.8839 cm across 12,229,092 comparisons.
|
||||
|
||||
The GPU-mesh far-survival audit passed: 7,200/7,200 boundary samples, 10,976/10,976 connectors, zero boundary holes, zero missing connector samples, zero connector holes, zero unsampled connectors.
|
||||
|
||||
## Captures and remaining remote gap
|
||||
|
||||
`RunStep8CaptureMatrix.ps1 -ExecutableName VoxelForgeGpuFieldStep8Final.exe` captured all six fixed views and four remote yaws only after `[InitialClipmapReady]`. The pixel comparison is in [step8_capture_comparison.md](step8_capture_comparison.md). The six local views remain close. Remote MAE RGB was 4.2933 at yaw 0, 1.5692 at yaw 90, 1.6511 at yaw 180, and 1.7465 at yaw 270. The horizontal mid-band still shows a visible GPU gap against the CPU captures, including at full residency. The skirt emitter and connector isolation captures did not eliminate it, so the remote look audit fails.
|
||||
|
||||
Capture files are `step8_{cpu,gpu}_{chamber,tunnel,passage,far,passage_floor_closeup,tunnel_bend,remote_yaw0,remote_yaw90,remote_yaw180,remote_yaw270}.png` in this directory.
|
||||
|
||||
## CPU reference checks
|
||||
|
||||
Final-build headless runs at 1 and 8 workers both passed Walkability `5642/5642`. The W1 and W8 CPU hashes match each other and the saved reference at all 44 fields:
|
||||
|
||||
- Density: `0xdb2991c73215af4c`
|
||||
- Mesh: `0x8236c5edd7fc8e4f`
|
||||
- Payload: `0x4c48fa75a543d58e`
|
||||
- Topology: `0xbea22e76b34a4dfc`
|
||||
|
||||
The port is not ready to call complete: the startup present is hidden successfully, and owner-start skirts, far survival, CPU hashes, and protected/thin topology pass. The remote skirt has one large outlier, remote captures still show a gap, and the hybrid whole-view remains slower than CPU.
|
||||
Reference in new issue
Block a user