wip(step3): 12.5 cm voxels, fall-through fixed, brick evaluator in game, organic tunnels -- speed gate FAILS
Stopped by the owner's decision (in-game lit captures were eating the round); he judges the look in game instead. 1. Fall-through with the research field: fixed. Deep drop, gate on: 2 holds (0.50 s), min foot clearance +43 cm; gate off: penetrates (-278 cm). Stress fixture offsets converted to metres. 2. VOXEL_SIZE 25 -> 12.5 cm. Physical sizes kept in metres (player, steps, gate, insets, research field sizes, dig radii, test thresholds); class defaults that count chunks/voxels doubled (ClipRadius 6, RenderDistanceChunks 1536, ViewDistance*, StrateViewMargin 6, PawnCollisionRadius 2, SkirtCells 4, EdgeSealThickness 128, decoration spacings, density volume res 256). Asset values override these defaults: the owner must double his own. Ring scale, six workers, idle, step-2f field before the look changes: 64 m cube (519^3) 4.55 s; 32 m cube (259^3) 0.95 s. 3. In game: tiles go through the brick evaluator (proved by a log marker); profile switch rebuilds the loaded world (~5,800 tiles); full-detail fill 12.4 s after spawn in the visible-window test. 4. Look: round winding tunnels, per-column ellipsoid/lattice caches. All step-2f geometry gates pass at 12.5 cm (0 crumbs, 1 solid component per profile, solid share 69-87%). 259^3 ring, six workers: 1.08-2.02 s (worst HyperComplex) -- the 600 ms gate FAILS. Lit captures failed (hidden/off-screen window, then blank frames); not pursued. 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
ca78352e43
commit
bc0f9ee791
33 files changed
+2691
-803
No files matched your search
+295
-8
@@ -1,6 +1,6 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[ValidateSet('canonical', 'owner', 'probe', 'perf', 'visual-edit', 'visual-lod', 'visual-gap-walk', 'visual-gap-inject', 'dig-motion', 'tests', 'surface-fall', 'crossing', 'gate-stress', 'parity', 'horizon', 'horizon-render', 'determinism', 'symbolize-stacks', 'region-eval', 'step2d', 'step2e', 'step2f', 'step2f-capture')]
|
||||
[ValidateSet('canonical', 'owner', 'probe', 'perf', 'visual-edit', 'visual-lod', 'visual-gap-walk', 'visual-gap-inject', 'dig-motion', 'tests', 'surface-fall', 'crossing', 'gate-stress', 'parity', 'horizon', 'horizon-render', 'determinism', 'symbolize-stacks', 'region-eval', 'step2d', 'step2e', 'step2f', 'step3-scale', 'step2f-capture', 'step3-game')]
|
||||
[string]$Scenario = 'canonical',
|
||||
[switch]$Build,
|
||||
[switch]$EnableRender,
|
||||
@@ -21,7 +21,7 @@ param(
|
||||
[ValidateRange(0, 8)]
|
||||
[int]$HorizonLevel = 5,
|
||||
[ValidateRange(1, 100000)]
|
||||
[int]$HorizonRenderDistanceChunks = 768,
|
||||
[int]$HorizonRenderDistanceChunks = 1536,
|
||||
[ValidateRange(0, 7)]
|
||||
[int]$Step2fProfile = 0,
|
||||
[ValidateSet('inside', 'wall-closeup')]
|
||||
@@ -64,6 +64,8 @@ $LaunchLedger = [System.Collections.Generic.List[object]]::new()
|
||||
$BuildRecord = [ordered]@{ requested = [bool]$Build; status = 'not_requested' }
|
||||
$AssetEvidence = @()
|
||||
$CommandletMetrics = [ordered]@{}
|
||||
$Step3ScaleMetrics = [ordered]@{ requested = $Scenario -eq 'step3-scale'; status = 'not_run' }
|
||||
$Step3GameMetrics = [ordered]@{ requested = $Scenario -eq 'step3-game'; status = 'not_run' }
|
||||
$GameMetrics = [ordered]@{}
|
||||
$HorizonMetrics = [ordered]@{
|
||||
requested = $Scenario -in @('horizon', 'horizon-render', 'step2f-capture')
|
||||
@@ -257,6 +259,56 @@ public static class VoxelForgeTestMemoryStatus {
|
||||
'@
|
||||
}
|
||||
|
||||
if (-not ('VoxelForgeTestSystemTimes' -as [type])) {
|
||||
Add-Type -TypeDefinition @'
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
public static class VoxelForgeTestSystemTimes {
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct FILETIME { public uint Low; public uint High; }
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern bool GetSystemTimes(out FILETIME idle, out FILETIME kernel, out FILETIME user);
|
||||
private static ulong ToUInt64(FILETIME value) { return ((ulong)value.High << 32) | value.Low; }
|
||||
public static double ReadBusyPercent(int intervalMilliseconds) {
|
||||
FILETIME idle0, kernel0, user0, idle1, kernel1, user1;
|
||||
if (!GetSystemTimes(out idle0, out kernel0, out user0)) throw new InvalidOperationException("GetSystemTimes failed before sample.");
|
||||
Thread.Sleep(intervalMilliseconds);
|
||||
if (!GetSystemTimes(out idle1, out kernel1, out user1)) throw new InvalidOperationException("GetSystemTimes failed after sample.");
|
||||
ulong idle = ToUInt64(idle1) - ToUInt64(idle0);
|
||||
ulong kernel = ToUInt64(kernel1) - ToUInt64(kernel0);
|
||||
ulong user = ToUInt64(user1) - ToUInt64(user0);
|
||||
ulong total = kernel + user;
|
||||
if (total == 0) return 100.0;
|
||||
ulong busy = total > idle ? total - idle : 0;
|
||||
return 100.0 * busy / total;
|
||||
}
|
||||
}
|
||||
'@
|
||||
}
|
||||
|
||||
function Wait-ForIdleMachine([string]$When) {
|
||||
$deadline = [datetime]::UtcNow.AddMinutes(10)
|
||||
$consecutiveIdleSamples = 0
|
||||
$lastBusyPercent = 100.0
|
||||
while ([datetime]::UtcNow -lt $deadline) {
|
||||
Assert-NoForeignUnreal "$When idle check"
|
||||
try { $lastBusyPercent = [VoxelForgeTestSystemTimes]::ReadBusyPercent(3000) }
|
||||
catch { throw "Could not measure system CPU use before $When; refusing to time this run: $($_.Exception.Message)" }
|
||||
if ($lastBusyPercent -le 15.0) {
|
||||
++$consecutiveIdleSamples
|
||||
if ($consecutiveIdleSamples -ge 2) {
|
||||
Write-Host ("Machine idle before {0}: two 3-second system samples, latest busy={1:N1}%." -f $When, $lastBusyPercent)
|
||||
return $lastBusyPercent
|
||||
}
|
||||
} else {
|
||||
$consecutiveIdleSamples = 0
|
||||
Write-Host ("Waiting for idle machine before {0}: 3-second system busy={1:N1}% (limit 15%)." -f $When, $lastBusyPercent)
|
||||
}
|
||||
}
|
||||
throw ("Machine did not stay under 15% CPU busy for two 3-second samples before {0}; last={1:N1}%." -f $When, $lastBusyPercent)
|
||||
}
|
||||
|
||||
function Assert-EnoughFreeMemory([string]$When) {
|
||||
$deadline = [datetime]::UtcNow.AddMinutes(30)
|
||||
$didWait = $false
|
||||
@@ -397,8 +449,9 @@ function Wait-OwnedProcess($Process, [int]$TimeoutSeconds, [switch]$EnforceMemor
|
||||
}
|
||||
|
||||
function Invoke-UnrealLaunch([string]$Kind, [string[]]$Arguments, [string]$LogPath,
|
||||
[string]$StdoutPath, [string]$StderrPath) {
|
||||
[string]$StdoutPath, [string]$StderrPath, [switch]$ShowWindow) {
|
||||
Assert-NoForeignUnreal "before $Kind launch"
|
||||
$idleCpuBusyPercent = Wait-ForIdleMachine "$Kind launch"
|
||||
Assert-EnoughFreeMemory "$Kind launch"
|
||||
Ensure-Directory (Split-Path -Parent $LogPath)
|
||||
Ensure-Directory (Split-Path -Parent $StdoutPath)
|
||||
@@ -414,6 +467,7 @@ function Invoke-UnrealLaunch([string]$Kind, [string[]]$Arguments, [string]$LogPa
|
||||
exit_code = $null
|
||||
timed_out = $false
|
||||
memory_limit_hit = $false
|
||||
prelaunch_cpu_busy_percent = [Math]::Round($idleCpuBusyPercent, 2)
|
||||
peak_private_bytes = 0L
|
||||
crash_reporters_reaped = @()
|
||||
new_crashes = @()
|
||||
@@ -426,12 +480,13 @@ function Invoke-UnrealLaunch([string]$Kind, [string[]]$Arguments, [string]$LogPa
|
||||
$isolatedLocalAppData = Join-Path $RunRoot 'LocalAppData'
|
||||
Ensure-Directory $isolatedLocalAppData
|
||||
$oldLocalAppData = $env:LOCALAPPDATA
|
||||
$windowStyle = if ($ShowWindow) { 'Normal' } else { 'Hidden' }
|
||||
try {
|
||||
# Unreal resolves several engine-wide stores from LOCALAPPDATA even when -userdir and
|
||||
# -LocalDataCachePath are supplied. Keep this child process inside the run directory.
|
||||
$env:LOCALAPPDATA = $isolatedLocalAppData
|
||||
$process = Start-Process -FilePath $EditorExe -ArgumentList $Arguments -WorkingDirectory $HostRoot `
|
||||
-RedirectStandardOutput $StdoutPath -RedirectStandardError $StderrPath -WindowStyle Hidden `
|
||||
-RedirectStandardOutput $StdoutPath -RedirectStandardError $StderrPath -WindowStyle $windowStyle `
|
||||
-PassThru
|
||||
try { $process.PriorityClass = [Diagnostics.ProcessPriorityClass]::BelowNormal }
|
||||
catch {
|
||||
@@ -579,7 +634,7 @@ function Sync-OwnerAssets {
|
||||
$null = Copy-Item -LiteralPath $source -Destination $target -Force -PassThru
|
||||
[void]$rows.Add([ordered]@{
|
||||
name = $assetName
|
||||
used_by_scenario = $Scenario -in @('owner', 'probe', 'parity', 'tests', 'surface-fall', 'crossing', 'gate-stress', 'visual-edit', 'visual-lod', 'visual-gap-walk', 'visual-gap-inject', 'dig-motion', 'horizon', 'horizon-render', 'determinism', 'region-eval', 'step2d', 'step2e', 'step2f', 'step2f-capture')
|
||||
used_by_scenario = $Scenario -in @('owner', 'probe', 'parity', 'tests', 'surface-fall', 'crossing', 'gate-stress', 'visual-edit', 'visual-lod', 'visual-gap-walk', 'visual-gap-inject', 'dig-motion', 'horizon', 'horizon-render', 'determinism', 'region-eval', 'step2d', 'step2e', 'step2f', 'step3-scale', 'step2f-capture', 'step3-game')
|
||||
source_path = $source
|
||||
source_sha256 = Get-Sha256 $source
|
||||
staged_path = $target
|
||||
@@ -674,7 +729,7 @@ function Get-AssetEvidence {
|
||||
$target = Join-Path $HostRoot "Content\VoxelForge\$assetName.uasset"
|
||||
[void]$rows.Add([ordered]@{
|
||||
name = $assetName
|
||||
used_by_scenario = $Scenario -in @('owner', 'probe', 'parity', 'tests', 'surface-fall', 'crossing', 'gate-stress', 'visual-edit', 'visual-lod', 'visual-gap-walk', 'visual-gap-inject', 'dig-motion', 'horizon', 'horizon-render', 'determinism', 'region-eval', 'step2d', 'step2e', 'step2f', 'step2f-capture')
|
||||
used_by_scenario = $Scenario -in @('owner', 'probe', 'parity', 'tests', 'surface-fall', 'crossing', 'gate-stress', 'visual-edit', 'visual-lod', 'visual-gap-walk', 'visual-gap-inject', 'dig-motion', 'horizon', 'horizon-render', 'determinism', 'region-eval', 'step2d', 'step2e', 'step2f', 'step3-scale', 'step2f-capture', 'step3-game')
|
||||
source_path = $source
|
||||
source_sha256 = Get-Sha256 $source
|
||||
staged_path = $target
|
||||
@@ -713,7 +768,6 @@ function New-CommonArguments([string]$Kind, [string]$LogPath, $CvarMap,
|
||||
} else {
|
||||
[void]$args.Add('-nullrhi')
|
||||
}
|
||||
[void]$args.Add('-DDC-ForceMemoryCache')
|
||||
[void]$args.Add('-unattended')
|
||||
[void]$args.Add('-nop4')
|
||||
[void]$args.Add('-nosplash')
|
||||
@@ -1011,6 +1065,147 @@ function Get-HorizonRenderMetrics($Ledger, [string]$LogPath) {
|
||||
return $metrics
|
||||
}
|
||||
|
||||
function Get-Step3GameMetrics([string]$LogPath, [string]$TracePath, $Ledger, $RenderMetrics) {
|
||||
$metrics = [ordered]@{
|
||||
status = 'missing'
|
||||
log = $LogPath
|
||||
startup_trace = $TracePath
|
||||
l0_tile_count = 0
|
||||
tile_generation_seconds_p50 = $null
|
||||
tile_generation_seconds_p95 = $null
|
||||
tile_generation_seconds_max = $null
|
||||
full_detail_ring_fill_seconds = $null
|
||||
full_detail_ring_lod0_tiles = $null
|
||||
moving_frames = $null
|
||||
moving_window_seconds = $null
|
||||
moving_frame_ms_p50 = $null
|
||||
moving_frame_ms_p95 = $null
|
||||
moving_frame_ms_p99 = $null
|
||||
moving_frame_ms_max = $null
|
||||
brick_evaluator_mesher_route_seen = $false
|
||||
profile_switch_applied = $false
|
||||
profile_switch_rebuilt_loaded_tiles = $false
|
||||
profile_switch_cleared_tile_count = 0
|
||||
captures = @()
|
||||
}
|
||||
if (Test-Path -LiteralPath $LogPath -PathType Leaf) {
|
||||
$metrics.brick_evaluator_mesher_route_seen = @(
|
||||
Select-String -LiteralPath $LogPath `
|
||||
-Pattern '\[VoxelForgeResearchBrickEvaluator\] mesher_grid=1' `
|
||||
-ErrorAction SilentlyContinue).Count -gt 0
|
||||
$tileSeconds = [System.Collections.Generic.List[double]]::new()
|
||||
foreach ($match in @(Select-String -LiteralPath $LogPath `
|
||||
-Pattern '\[VoxelForgeTileProfile\].*level=0\b.*aborted=0' `
|
||||
-ErrorAction SilentlyContinue)) {
|
||||
$seconds = [regex]::Match([string]$match.Line, 'seconds=(?<v>[-+0-9.eE]+)')
|
||||
if ($seconds.Success) {
|
||||
$value = [double]$seconds.Groups['v'].Value
|
||||
if ($value -eq $value -and -not [double]::IsInfinity($value) -and $value -ge 0.0) {
|
||||
$tileSeconds.Add($value)
|
||||
}
|
||||
}
|
||||
}
|
||||
$tileSeconds.Sort()
|
||||
$metrics.l0_tile_count = $tileSeconds.Count
|
||||
if ($tileSeconds.Count -gt 0) {
|
||||
$metrics.tile_generation_seconds_p50 = $tileSeconds[[Math]::Max(0, [Math]::Ceiling(0.50 * $tileSeconds.Count) - 1)]
|
||||
$metrics.tile_generation_seconds_p95 = $tileSeconds[[Math]::Max(0, [Math]::Ceiling(0.95 * $tileSeconds.Count) - 1)]
|
||||
$metrics.tile_generation_seconds_max = $tileSeconds[$tileSeconds.Count - 1]
|
||||
}
|
||||
$perfLine = @(Select-String -LiteralPath $LogPath -Pattern '\[VoxelForgeResearchGamePerf\]' -ErrorAction SilentlyContinue | Select-Object -Last 1)
|
||||
if ($perfLine.Count -gt 0) {
|
||||
$line = [string]$perfLine[0].Line
|
||||
foreach ($key in @('moving_frames', 'moving_window_s', 'p50_ms', 'p95_ms', 'p99_ms', 'max_ms')) {
|
||||
$value = [regex]::Match($line, ('{0}=(?<v>[-+0-9.eE]+)' -f [regex]::Escape($key)))
|
||||
if ($value.Success) {
|
||||
$target = switch ($key) {
|
||||
'moving_frames' { 'moving_frames' }
|
||||
'moving_window_s' { 'moving_window_seconds' }
|
||||
'p50_ms' { 'moving_frame_ms_p50' }
|
||||
'p95_ms' { 'moving_frame_ms_p95' }
|
||||
'p99_ms' { 'moving_frame_ms_p99' }
|
||||
'max_ms' { 'moving_frame_ms_max' }
|
||||
}
|
||||
$metrics[$target] = [double]$value.Groups['v'].Value
|
||||
}
|
||||
}
|
||||
}
|
||||
$switchLine = @(Select-String -LiteralPath $LogPath -Pattern '\[VoxelForgeResearchProfileSwitch\] applied=1' -ErrorAction SilentlyContinue | Select-Object -Last 1)
|
||||
$rebuildLine = @(Select-String -LiteralPath $LogPath -Pattern 'research_field_selection_changed.*rebuilding loaded world' -ErrorAction SilentlyContinue | Select-Object -Last 1)
|
||||
if ($switchLine.Count -gt 0) { $metrics.profile_switch_applied = $true }
|
||||
if ($rebuildLine.Count -gt 0) {
|
||||
$metrics.profile_switch_rebuilt_loaded_tiles = [regex]::IsMatch(
|
||||
[string]$switchLine[0].Line, 'loaded_tiles=(?<n>[1-9][0-9]*)')
|
||||
$regenerate = @(Select-String -LiteralPath $LogPath `
|
||||
-Pattern '\[VoxelWorld\] RegenerateAllChunks .*cleared (?<n>[0-9]+) tiles' `
|
||||
-ErrorAction SilentlyContinue |
|
||||
Where-Object { $_.LineNumber -gt $rebuildLine[0].LineNumber } |
|
||||
Select-Object -First 1)
|
||||
if ($regenerate.Count -gt 0) {
|
||||
$cleared = [regex]::Match([string]$regenerate[0].Line, 'cleared (?<n>[0-9]+) tiles')
|
||||
if ($cleared.Success) {
|
||||
$metrics.profile_switch_cleared_tile_count = [int]$cleared.Groups['n'].Value
|
||||
$metrics.profile_switch_rebuilt_loaded_tiles =
|
||||
$metrics.profile_switch_rebuilt_loaded_tiles -and
|
||||
$metrics.profile_switch_cleared_tile_count -gt 0
|
||||
}
|
||||
} else {
|
||||
$metrics.profile_switch_rebuilt_loaded_tiles = $false
|
||||
}
|
||||
}
|
||||
$captureLines = @(Select-String -LiteralPath $LogPath -Pattern '\[VoxelForgeTestCeiling\] capture_index=' -ErrorAction SilentlyContinue)
|
||||
foreach ($captureLine in $captureLines) {
|
||||
$index = [regex]::Match([string]$captureLine.Line, 'capture_index=(?<v>[12])')
|
||||
$accepted = [regex]::Match([string]$captureLine.Line, 'capture_requested=(?<v>[01])')
|
||||
if ($index.Success) {
|
||||
$metrics.captures += [ordered]@{
|
||||
index = [int]$index.Groups['v'].Value
|
||||
requested = $accepted.Success -and $accepted.Groups['v'].Value -eq '1'
|
||||
line = [string]$captureLine.Line
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Test-Path -LiteralPath $TracePath -PathType Leaf) {
|
||||
$ringEvents = [System.Collections.Generic.List[object]]::new()
|
||||
foreach ($line in @(Get-Content -LiteralPath $TracePath -ErrorAction SilentlyContinue)) {
|
||||
if ([string]::IsNullOrWhiteSpace($line)) { continue }
|
||||
try {
|
||||
$event = $line | ConvertFrom-Json -ErrorAction Stop
|
||||
if ([string](Get-JsonValue $event 'type' '') -eq 'lod0_ring_ready') {
|
||||
$ringEvents.Add($event)
|
||||
}
|
||||
} catch { }
|
||||
}
|
||||
if ($ringEvents.Count -gt 0) {
|
||||
$metrics.full_detail_ring_fill_seconds = [double](Get-JsonValue $ringEvents[0] 'duration_s' 0.0)
|
||||
$metrics.full_detail_ring_lod0_tiles = [int](Get-JsonValue $ringEvents[0] 'lod0_tiles' 0)
|
||||
}
|
||||
}
|
||||
$metrics.render = $RenderMetrics
|
||||
$metrics.streaming = Get-StreamingMetrics $LogPath
|
||||
$metrics.launch = if ($null -ne $Ledger) {
|
||||
[ordered]@{
|
||||
status = Get-JsonValue $Ledger 'status' 'missing'
|
||||
peak_private_bytes = Get-JsonValue $Ledger 'peak_private_bytes' 0
|
||||
pid = Get-JsonValue $Ledger 'pid' $null
|
||||
}
|
||||
} else { $null }
|
||||
$step3Passed = ($metrics.l0_tile_count -gt 0 -and
|
||||
$null -ne $metrics.full_detail_ring_fill_seconds -and
|
||||
$metrics.moving_frames -gt 0 -and
|
||||
$metrics.brick_evaluator_mesher_route_seen -and
|
||||
$metrics.profile_switch_applied -and
|
||||
$metrics.profile_switch_rebuilt_loaded_tiles -and
|
||||
$metrics.profile_switch_cleared_tile_count -gt 0 -and
|
||||
@($metrics.captures | Where-Object { $_.requested }).Count -ge 2 -and
|
||||
$null -ne $Ledger -and (Get-JsonValue $Ledger 'status' 'failed') -eq 'passed')
|
||||
$metrics.status = if ($step3Passed) {
|
||||
'passed'
|
||||
} else { 'failed' }
|
||||
return $metrics
|
||||
}
|
||||
|
||||
function Compare-DeterminismDumps([string]$DefaultPath, [string]$CrtFma3OffPath) {
|
||||
$defaultJson = Read-JsonFile $DefaultPath
|
||||
$fmaJson = Read-JsonFile $CrtFma3OffPath
|
||||
@@ -1128,7 +1323,7 @@ function Get-CollisionGateStressMetrics([string]$LogPath) {
|
||||
'spawn_height_vox', 'deep_drop',
|
||||
'gate_holds', 'gate_total_s', 'max_gate_s', 'full_stops',
|
||||
'full_stop_total_s', 'collision_gate', 'gate_progress_m',
|
||||
'horizontal_gate_probe', 'support_tile',
|
||||
'full_stop_probe_applied', 'full_stop_probe_observed', 'support_tile',
|
||||
'support_first_check_to_submit_s',
|
||||
'support_first_check_to_collision_ready_s', 'test_support_tile',
|
||||
'test_support_first_check_to_submit_s',
|
||||
@@ -1612,6 +1807,33 @@ try {
|
||||
}
|
||||
}
|
||||
}
|
||||
'step3-scale' {
|
||||
$scaleLog = Join-Path $RunRoot 'Logs\step3-scale.log'
|
||||
$args = New-CommonArguments 'commandlet' $scaleLog (Merge-Cvars $baseCvars)
|
||||
$args += New-CommandletArguments $commandletOut 'walk,export' ($Assets -eq 'owner') $false 128
|
||||
$researchOut = Join-Path $PluginRoot "Saved\Research\step3\scale\$Label"
|
||||
$args += @('-step3scale', '-regionworkers=6', '-profile=0', ('-researchout="{0}"' -f $researchOut))
|
||||
$commandletLedger = Invoke-UnrealLaunch 'step3-scale-commandlet' $args `
|
||||
$scaleLog (Join-Path $RunRoot 'Logs\step3-scale.stdout.log') `
|
||||
(Join-Path $RunRoot 'Logs\step3-scale.stderr.log')
|
||||
$scaleReportPath = Join-Path $researchOut 'step3_scale_benchmark.json'
|
||||
$scaleReport = Read-JsonFile $scaleReportPath
|
||||
if ($null -eq $scaleReport) {
|
||||
$Step3ScaleMetrics = [ordered]@{ requested = $true; status = 'missing'; output = $researchOut; report = $scaleReportPath }
|
||||
Add-Failure "step3 scale benchmark did not produce $scaleReportPath"
|
||||
} else {
|
||||
$Step3ScaleMetrics = [ordered]@{
|
||||
requested = $true
|
||||
status = if ((Get-JsonValue $scaleReport 'pass' $false) -and $commandletLedger.status -eq 'passed') { 'passed' } else { 'failed' }
|
||||
output = $researchOut
|
||||
report = $scaleReportPath
|
||||
result = $scaleReport
|
||||
}
|
||||
if (-not (Get-JsonValue $scaleReport 'pass' $false)) {
|
||||
Add-Failure 'step3 scale sample, determinism, or six-worker gate failed'
|
||||
}
|
||||
}
|
||||
}
|
||||
'symbolize-stacks' {
|
||||
if (([string]::IsNullOrWhiteSpace($StackSamplesPath)) -or -not (Test-Path -LiteralPath $StackSamplesPath -PathType Leaf)) {
|
||||
throw "-StackSamplesPath must name an existing raw sampler CSV."
|
||||
@@ -1718,6 +1940,67 @@ try {
|
||||
}
|
||||
}
|
||||
}
|
||||
'step3-game' {
|
||||
$tracePath = Join-Path $RunRoot 'step3_game_trace.jsonl'
|
||||
$gameLog = Join-Path $RunRoot 'Logs\step3-game.log'
|
||||
$baseCvars['voxel.TestRenderDistanceChunks'] = $HorizonRenderDistanceChunks
|
||||
$baseCvars['voxel.TestClipRadius'] = 6
|
||||
$baseCvars['voxel.TestMaxClipLevel'] = $HorizonLevel
|
||||
$baseCvars['voxel.StartupTraceFile'] = $tracePath
|
||||
$baseCvars['voxel.StartupTraceMaxTiles'] = 16384
|
||||
$baseCvars['voxel.OuterClassifierMode'] = 0
|
||||
$baseCvars['voxel.ResearchParameterizedCave'] = 1
|
||||
$baseCvars['voxel.ResearchParameterizedCaveSet'] = 0
|
||||
$baseCvars['voxel.ProfileTileGeneration'] = 1
|
||||
$baseCvars['voxel.ResearchGamePerf'] = 1
|
||||
$baseCvars['voxel.TestCeilingCameraX'] = 0
|
||||
$baseCvars['voxel.TestCeilingCameraY'] = 0
|
||||
$baseCvars['voxel.TestCeilingCameraZ'] = -8800
|
||||
$baseCvars['voxel.TestCeilingCameraPitch'] = 0
|
||||
$baseCvars['voxel.TestCeilingCameraYaw'] = 0
|
||||
$baseCvars['voxel.TestCeilingView'] = 1
|
||||
$baseCvars['voxel.TestCeilingCaptureDelaySeconds'] = 25
|
||||
$baseCvars['voxel.TestCeilingSecondCaptureDelaySeconds'] = 70
|
||||
$baseCvars['voxel.TestCeilingCameraMoveStartSeconds'] = 35
|
||||
$baseCvars['voxel.TestCeilingCameraMoveDurationSeconds'] = 25
|
||||
$baseCvars['voxel.TestCeilingCameraMoveSpeedCmPerSecond'] = 600
|
||||
$baseCvars['voxel.TestCeilingCameraMoveDirection'] = '1,0,0'
|
||||
$baseCvars['voxel.TestResearchProfileSwitchAfterSeconds'] = 30
|
||||
$baseCvars['voxel.TestResearchProfileSwitchTo'] = 1
|
||||
$baseCvars['voxel.TestExitSeconds'] = 85
|
||||
$args = New-CommonArguments 'game' $gameLog (Merge-Cvars $baseCvars) $true
|
||||
# Keep a real RHI game viewport for the two lit screenshots. The offscreen
|
||||
# viewport accepted high-res requests but only wrote its blank startup frame.
|
||||
$args = @($args | Where-Object { $_ -ne '-RenderOffscreen' })
|
||||
$args += '-windowed'
|
||||
$gameLedger = Invoke-UnrealLaunch 'step3-game-brick-evaluator' $args `
|
||||
$gameLog (Join-Path $RunRoot 'Logs\step3-game.stdout.log') `
|
||||
(Join-Path $RunRoot 'Logs\step3-game.stderr.log') -ShowWindow
|
||||
$GameMetrics = Get-StreamingMetrics $gameLog
|
||||
$HorizonMetrics = [ordered]@{ requested = $false; status = 'ring_and_tile_metrics_are_in_step3_game' }
|
||||
$HorizonRenderMetrics = Get-HorizonRenderMetrics $gameLedger $gameLog
|
||||
$Step3GameMetrics = Get-Step3GameMetrics $gameLog $tracePath $gameLedger $HorizonRenderMetrics
|
||||
$captureDirectory = Join-Path $PluginRoot "Saved\Research\step3\captures\$Label"
|
||||
Ensure-Directory $captureDirectory
|
||||
$images = @($HorizonRenderMetrics.images | Sort-Object modified_utc)
|
||||
if ($images.Count -lt 2) {
|
||||
Add-Failure "step3 game path did not produce both lit captures; found $($images.Count)"
|
||||
} else {
|
||||
$capturePaths = [System.Collections.Generic.List[string]]::new()
|
||||
$captureNames = @('OpenVaults_before_switch.png', 'TightNetwork_after_switch.png')
|
||||
for ($captureIndex = 0; $captureIndex -lt 2; ++$captureIndex) {
|
||||
$sourceImage = $images[$images.Count - 2 + $captureIndex]
|
||||
$destination = Join-Path $captureDirectory $captureNames[$captureIndex]
|
||||
Copy-Item -LiteralPath $sourceImage.path -Destination $destination -Force
|
||||
$capturePaths.Add($destination)
|
||||
}
|
||||
$HorizonRenderMetrics.saved_captures = @($capturePaths)
|
||||
$Step3GameMetrics.saved_captures = @($capturePaths)
|
||||
}
|
||||
if ($Step3GameMetrics.status -ne 'passed') {
|
||||
Add-Failure "step3 game path, profile rebuild, lit capture, ring, or moving frame metric failed: $($Step3GameMetrics.status)"
|
||||
}
|
||||
}
|
||||
'determinism' {
|
||||
$defaultDump = Join-Path $RunRoot 'tile_hashes_default.json'
|
||||
$crtFma3OffDump = Join-Path $RunRoot 'tile_hashes_crt_fma3_off.json'
|
||||
@@ -2060,6 +2343,8 @@ $result = [ordered]@{
|
||||
region_eval = if ($Scenario -eq 'region-eval') { $CommandletMetrics } else { $null }
|
||||
step2e = if ($Scenario -eq 'step2e') { $CommandletMetrics } else { $null }
|
||||
step2f = if ($Scenario -eq 'step2f') { $CommandletMetrics } else { $null }
|
||||
step3_scale = $Step3ScaleMetrics
|
||||
step3_game = $Step3GameMetrics
|
||||
game = $GameMetrics
|
||||
horizon = $HorizonMetrics
|
||||
horizon_render = $HorizonRenderMetrics
|
||||
@@ -2101,6 +2386,8 @@ try {
|
||||
[void]$summaryLines.Add("geometry_hash=$($result.geometry_hash)")
|
||||
[void]$summaryLines.Add("obj_sha256=$($result.obj_sha256)")
|
||||
[void]$summaryLines.Add("region_eval=$(ConvertTo-CompactJson $result.region_eval)")
|
||||
[void]$summaryLines.Add("step3_scale=$(ConvertTo-CompactJson $result.step3_scale)")
|
||||
[void]$summaryLines.Add("step3_game=$(ConvertTo-CompactJson $result.step3_game)")
|
||||
[void]$summaryLines.Add("surface_fall=$(ConvertTo-CompactJson $result.surface_fall)")
|
||||
[void]$summaryLines.Add("strate_crossing=$(ConvertTo-CompactJson $result.strate_crossing)")
|
||||
[void]$summaryLines.Add("visual_staleness=$(ConvertTo-CompactJson $result.visual_staleness)")
|
||||
|
||||
Reference in new issue
Block a user