feat(streaming): horizon distance with real tiles only -- sheets off by default
The sheet system models terrain as one ground and one cap height per column, which cannot express this world's overhangs, floating islands and open sky; three attempts to fix the MC/sheet seam produced holes, then caps over the void, then both. Measured alternative: cover the render distance with real marching-cubes tiles and switch the far-sheet ring off. Benchmarks at RenderDistanceChunks=768, all tiles loaded at steady state: L4 57,473 tiles / 30.1M tris / 837 MiB / 2,514 s worker / 296 s to settle; L5 10,841 / 8.1M / 226 MiB / 375 s / 108 s; L6 3,281 / 2.9M / 82 MiB / 127 s / 77 s. Defaults now MaxClipLevel=5, bFarSheetRing=false. Invariants: strate crossing PASS, gate stress PASS (140.06 m, 0 full stops), determinism PASS with 10,841 tile hashes identical vs -voxel.CrtFma3=0. Peak memory ~4.2 GiB in all three. Owner action required: his DA_Settings still serializes MaxClipLevel=3 (asset edits are out of scope for Codex), so his world needs that value raised to 5 to use the new far field. At twice the distance the estimate is ~39.5k tiles / 29-30M tris / ~0.8 GiB / ~1,500 worker-seconds, which would want L6 rather than L5. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
1 parent
ea3f0d9a52
commit
370c39e473
7 files changed
+475
-32
No files matched your search
+343
-10
@@ -1,6 +1,6 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[ValidateSet('canonical', 'owner', 'probe', 'perf', 'tests', 'surface-fall', 'crossing', 'gate-stress', 'parity')]
|
||||
[ValidateSet('canonical', 'owner', 'probe', 'perf', 'tests', 'surface-fall', 'crossing', 'gate-stress', 'parity', 'horizon', 'horizon-render', 'determinism')]
|
||||
[string]$Scenario = 'canonical',
|
||||
[switch]$Build,
|
||||
[ValidateSet('owner', 'default')]
|
||||
@@ -8,7 +8,12 @@ param(
|
||||
[hashtable]$Cvars = @{},
|
||||
[string]$TestFilter,
|
||||
[string]$Out,
|
||||
[string]$Label
|
||||
[string]$Label,
|
||||
[ValidateRange(0, 8)]
|
||||
[int]$HorizonLevel = 5,
|
||||
[ValidateRange(1, 100000)]
|
||||
[int]$HorizonRenderDistanceChunks = 768,
|
||||
[string]$SharedDDC
|
||||
)
|
||||
|
||||
# One guarded entry point for all VoxelForge measurements. It intentionally keeps the Unreal
|
||||
@@ -28,7 +33,7 @@ $EngineRoot = 'E:\Program Files\Epic Games\UE_5.7'
|
||||
$EditorExe = Join-Path $EngineRoot 'Engine\Binaries\Win64\UnrealEditor-Cmd.exe'
|
||||
$UbtDll = Join-Path $EngineRoot 'Engine\Binaries\DotNET\UnrealBuildTool\UnrealBuildTool.dll'
|
||||
$LaunchTimeoutSeconds = 1800
|
||||
$UnrealPrivateMemoryLimitBytes = 6GB
|
||||
$UnrealPrivateMemoryLimitBytes = 8GB
|
||||
# The VoxelForge category includes two late, several-minute op-stack sweeps. They remain in
|
||||
# the suite; when the serialized aggregate launch reaches this 30-minute guard, validate those
|
||||
# named tests with -TestFilter so every test still runs under the standing launch limit.
|
||||
@@ -44,6 +49,15 @@ $BuildRecord = [ordered]@{ requested = [bool]$Build; status = 'not_requested' }
|
||||
$AssetEvidence = @()
|
||||
$CommandletMetrics = [ordered]@{}
|
||||
$GameMetrics = [ordered]@{}
|
||||
$HorizonMetrics = [ordered]@{
|
||||
requested = $Scenario -in @('horizon', 'horizon-render')
|
||||
status = 'not_run'
|
||||
level = $HorizonLevel
|
||||
render_distance_chunks = $HorizonRenderDistanceChunks
|
||||
far_sheet_ring = $false
|
||||
}
|
||||
$HorizonRenderMetrics = [ordered]@{ requested = $Scenario -eq 'horizon-render'; status = 'not_run' }
|
||||
$DeterminismEvidence = [ordered]@{ requested = $Scenario -eq 'determinism'; status = 'not_run' }
|
||||
$SurfaceFallMetrics = [ordered]@{
|
||||
requested = $Scenario -eq 'surface-fall' -or
|
||||
($Scenario -eq 'tests' -and [string]::IsNullOrWhiteSpace($TestFilter))
|
||||
@@ -66,6 +80,7 @@ $LocalAppDataRoot = Join-Path ([Environment]::GetFolderPath('LocalApplicationDat
|
||||
$RunRoot = $null
|
||||
$ResultPath = $null
|
||||
$SummaryPath = $null
|
||||
$DDCPath = $null
|
||||
|
||||
function Add-Failure([string]$Reason) {
|
||||
if ([string]::IsNullOrWhiteSpace($Reason)) { return }
|
||||
@@ -485,7 +500,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')
|
||||
used_by_scenario = $Scenario -in @('owner', 'probe', 'parity', 'tests', 'surface-fall', 'crossing', 'gate-stress', 'horizon', 'horizon-render', 'determinism')
|
||||
source_path = $source
|
||||
source_sha256 = Get-Sha256 $source
|
||||
staged_path = $target
|
||||
@@ -533,7 +548,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')
|
||||
used_by_scenario = $Scenario -in @('owner', 'probe', 'parity', 'tests', 'surface-fall', 'crossing', 'gate-stress', 'horizon', 'horizon-render', 'determinism')
|
||||
source_path = $source
|
||||
source_sha256 = Get-Sha256 $source
|
||||
staged_path = $target
|
||||
@@ -558,13 +573,19 @@ function Merge-Cvars([System.Collections.IDictionary]$Base) {
|
||||
return $merged
|
||||
}
|
||||
|
||||
function New-CommonArguments([string]$Kind, [string]$LogPath, $CvarMap) {
|
||||
function New-CommonArguments([string]$Kind, [string]$LogPath, $CvarMap, [bool]$EnableRender = $false) {
|
||||
$safeLabel = ($Label -replace '[^A-Za-z0-9_.-]', '_')
|
||||
$args = [System.Collections.Generic.List[string]]::new()
|
||||
# Quoting the whole -project= token is intentional: this is the failure mode that used to
|
||||
# swallow every argument after "Projet Unreal".
|
||||
[void]$args.Add(('-project="{0}"' -f $HostProject))
|
||||
[void]$args.Add('-nullrhi')
|
||||
if ($EnableRender) {
|
||||
[void]$args.Add('-RenderOffscreen')
|
||||
[void]$args.Add('-ResX=1280')
|
||||
[void]$args.Add('-ResY=720')
|
||||
} else {
|
||||
[void]$args.Add('-nullrhi')
|
||||
}
|
||||
[void]$args.Add('-DDC-ForceMemoryCache')
|
||||
[void]$args.Add('-unattended')
|
||||
[void]$args.Add('-nop4')
|
||||
@@ -580,7 +601,7 @@ function New-CommonArguments([string]$Kind, [string]$LogPath, $CvarMap) {
|
||||
[void]$args.Add(('-userdir="{0}"' -f (Join-Path $RunRoot 'User')))
|
||||
[void]$args.Add(('-ZenDataPath="{0}"' -f (Join-Path $RunRoot 'ZenData')))
|
||||
[void]$args.Add('-ddc=NoZenLocalFallback')
|
||||
[void]$args.Add(('-LocalDataCachePath="{0}"' -f (Join-Path $RunRoot 'DDC')))
|
||||
[void]$args.Add(('-LocalDataCachePath="{0}"' -f $DDCPath))
|
||||
[void]$args.Add(('-abslog="{0}"' -f $LogPath))
|
||||
if ($Kind -eq 'commandlet') {
|
||||
[void]$args.Add('-run=VoxelForgeExplore')
|
||||
@@ -720,6 +741,212 @@ function Get-StreamingMetrics([string]$LogPath) {
|
||||
return $metrics
|
||||
}
|
||||
|
||||
function Get-HorizonMetrics([string]$TracePath, $Ledger, $LatencyMetrics) {
|
||||
$metrics = [ordered]@{
|
||||
status = 'missing'
|
||||
trace = $TracePath
|
||||
desired_tiles_by_level = [ordered]@{}
|
||||
loaded_tiles_by_level = [ordered]@{}
|
||||
vertices_by_level = [ordered]@{}
|
||||
triangles_by_level = [ordered]@{}
|
||||
geometry_memory_bytes_by_level = [ordered]@{}
|
||||
geometry_memory_mib_by_level = [ordered]@{}
|
||||
desired_tiles_total = 0
|
||||
loaded_tiles_total = 0
|
||||
vertices_total = 0
|
||||
triangles_total = 0
|
||||
geometry_memory_bytes_total = 0
|
||||
geometry_memory_mib_total = 0.0
|
||||
worker_generation_seconds = if ($null -ne $LatencyMetrics) {
|
||||
Get-JsonValue $LatencyMetrics 'worker_generation_sum_s' $null
|
||||
} else { $null }
|
||||
settled_trace_seconds = $null
|
||||
trace_summary_wall_seconds = $null
|
||||
process_wall_seconds = $null
|
||||
peak_private_bytes = if ($null -ne $Ledger) {
|
||||
[int64](Get-JsonValue $Ledger 'peak_private_bytes' 0)
|
||||
} else { 0L }
|
||||
peak_private_gib = 0.0
|
||||
trace_records_seen = $null
|
||||
trace_records_written = $null
|
||||
trace_records_suppressed = $null
|
||||
}
|
||||
$metrics.peak_private_gib = [Math]::Round($metrics.peak_private_bytes / 1GB, 3)
|
||||
|
||||
if (-not (Test-Path -LiteralPath $TracePath -PathType Leaf)) { return $metrics }
|
||||
$events = [System.Collections.Generic.List[object]]::new()
|
||||
foreach ($line in @(Get-Content -LiteralPath $TracePath -ErrorAction SilentlyContinue)) {
|
||||
if ([string]::IsNullOrWhiteSpace($line)) { continue }
|
||||
try { [void]$events.Add(($line | ConvertFrom-Json -ErrorAction Stop)) } catch { }
|
||||
}
|
||||
$steady = @($events | Where-Object { [string](Get-JsonValue $_ 'type' '') -eq 'steady_state' } | Select-Object -Last 1)
|
||||
$summary = @($events | Where-Object { [string](Get-JsonValue $_ 'type' '') -eq 'summary' } | Select-Object -Last 1)
|
||||
$desiredEvents = @($events | Where-Object { [string](Get-JsonValue $_ 'type' '') -eq 'steady_desired_level' })
|
||||
$loadedEvents = @($events | Where-Object { [string](Get-JsonValue $_ 'type' '') -eq 'steady_loaded_level' })
|
||||
if ($steady.Count -eq 0 -or $summary.Count -eq 0 -or $desiredEvents.Count -eq 0) {
|
||||
return $metrics
|
||||
}
|
||||
|
||||
foreach ($event in $desiredEvents) {
|
||||
$level = [string][int](Get-JsonValue $event 'level' -1)
|
||||
$metrics.desired_tiles_by_level[$level] = [int](Get-JsonValue $event 'tiles' 0)
|
||||
}
|
||||
foreach ($event in $loadedEvents) {
|
||||
$level = [string][int](Get-JsonValue $event 'level' -1)
|
||||
$metrics.loaded_tiles_by_level[$level] = [int](Get-JsonValue $event 'tiles' 0)
|
||||
$metrics.vertices_by_level[$level] = [int64](Get-JsonValue $event 'vertices' 0)
|
||||
$metrics.triangles_by_level[$level] = [int64](Get-JsonValue $event 'triangles' 0)
|
||||
$bytes = [int64](Get-JsonValue $event 'geometry_bytes' 0)
|
||||
$metrics.geometry_memory_bytes_by_level[$level] = $bytes
|
||||
$metrics.geometry_memory_mib_by_level[$level] = [Math]::Round($bytes / 1MB, 3)
|
||||
}
|
||||
foreach ($value in $metrics.desired_tiles_by_level.Values) { $metrics.desired_tiles_total += [int64]$value }
|
||||
foreach ($value in $metrics.loaded_tiles_by_level.Values) { $metrics.loaded_tiles_total += [int64]$value }
|
||||
foreach ($value in $metrics.vertices_by_level.Values) { $metrics.vertices_total += [int64]$value }
|
||||
foreach ($value in $metrics.triangles_by_level.Values) { $metrics.triangles_total += [int64]$value }
|
||||
foreach ($value in $metrics.geometry_memory_bytes_by_level.Values) { $metrics.geometry_memory_bytes_total += [int64]$value }
|
||||
$metrics.geometry_memory_mib_total = [Math]::Round($metrics.geometry_memory_bytes_total / 1MB, 3)
|
||||
$metrics.settled_trace_seconds = [double](Get-JsonValue $steady[0] 't_s' 0.0)
|
||||
$metrics.trace_summary_wall_seconds = [double](Get-JsonValue $summary[0] 'wall_s' 0.0)
|
||||
$metrics.trace_records_seen = [int](Get-JsonValue $summary[0] 'tile_records_seen' 0)
|
||||
$metrics.trace_records_written = [int](Get-JsonValue $summary[0] 'tile_records_written' 0)
|
||||
$metrics.trace_records_suppressed = [int](Get-JsonValue $summary[0] 'tile_records_suppressed' 0)
|
||||
if ($null -ne $Ledger) {
|
||||
$started = [datetime]::Parse([string](Get-JsonValue $Ledger 'started_utc' ''))
|
||||
$ended = [datetime]::Parse([string](Get-JsonValue $Ledger 'ended_utc' ''))
|
||||
$metrics.process_wall_seconds = [Math]::Round(($ended.ToUniversalTime() - $started.ToUniversalTime()).TotalSeconds, 3)
|
||||
}
|
||||
$metrics.status = 'ok'
|
||||
return $metrics
|
||||
}
|
||||
|
||||
function Get-HorizonRenderMetrics($Ledger, [string]$LogPath) {
|
||||
$metrics = [ordered]@{
|
||||
status = 'failed'
|
||||
log = $LogPath
|
||||
capture_requested = $null
|
||||
capture_elapsed_seconds = $null
|
||||
capture_visible_tiles = $null
|
||||
capture_triangles = $null
|
||||
images = @()
|
||||
}
|
||||
$lines = @(Select-String -LiteralPath $LogPath -Pattern '\[VoxelForgeTestCeiling\] capture_requested=' -ErrorAction SilentlyContinue)
|
||||
if ($lines.Count -gt 0) {
|
||||
$line = [string]$lines[-1].Line
|
||||
$metrics.raw = $line
|
||||
foreach ($key in @('capture_requested', 'elapsed_s', 'visible_tiles', 'triangles')) {
|
||||
$match = [regex]::Match($line, ('{0}=(?<v>[^ ]+)' -f $key))
|
||||
if ($match.Success) {
|
||||
if ($key -eq 'elapsed_s') { $metrics.capture_elapsed_seconds = [double]$match.Groups['v'].Value }
|
||||
elseif ($key -eq 'visible_tiles') { $metrics.capture_visible_tiles = [int64]$match.Groups['v'].Value }
|
||||
elseif ($key -eq 'triangles') { $metrics.capture_triangles = [int64]$match.Groups['v'].Value }
|
||||
else { $metrics.capture_requested = [int]$match.Groups['v'].Value }
|
||||
}
|
||||
}
|
||||
}
|
||||
$started = if ($null -ne $Ledger) {
|
||||
[datetime]::Parse([string](Get-JsonValue $Ledger 'started_utc' ''))
|
||||
} else { [datetime]::MinValue }
|
||||
$roots = @(
|
||||
(Join-Path $HostRoot 'Saved'),
|
||||
$RunRoot,
|
||||
(Join-Path $RunRoot 'User'))
|
||||
$images = [System.Collections.Generic.List[object]]::new()
|
||||
foreach ($root in $roots) {
|
||||
if (-not (Test-Path -LiteralPath $root -PathType Container)) { continue }
|
||||
foreach ($image in @(Get-ChildItem -LiteralPath $root -Filter '*.png' -File -Recurse -ErrorAction SilentlyContinue)) {
|
||||
if ($image.LastWriteTimeUtc -lt $started.ToUniversalTime().AddSeconds(-2)) { continue }
|
||||
[void]$images.Add([ordered]@{
|
||||
path = $image.FullName
|
||||
bytes = [int64]$image.Length
|
||||
modified_utc = $image.LastWriteTimeUtc.ToString('o')
|
||||
})
|
||||
}
|
||||
}
|
||||
$metrics.images = @($images | Sort-Object path -Unique)
|
||||
if ($metrics.capture_requested -eq 1 -and $metrics.images.Count -gt 0) {
|
||||
$metrics.status = 'passed'
|
||||
} elseif ($null -eq $Ledger -or (Get-JsonValue $Ledger 'status' 'failed') -ne 'passed') {
|
||||
$metrics.status = 'not_run'
|
||||
}
|
||||
return $metrics
|
||||
}
|
||||
|
||||
function Compare-DeterminismDumps([string]$DefaultPath, [string]$CrtFma3OffPath) {
|
||||
$defaultJson = Read-JsonFile $DefaultPath
|
||||
$fmaJson = Read-JsonFile $CrtFma3OffPath
|
||||
$evidence = [ordered]@{
|
||||
status = 'failed'
|
||||
default_dump = $DefaultPath
|
||||
crt_fma3_off_dump = $CrtFma3OffPath
|
||||
default_record_count = 0
|
||||
crt_fma3_off_record_count = 0
|
||||
compared_tile_count = 0
|
||||
first_differences = @()
|
||||
}
|
||||
if ($null -eq $defaultJson -or $null -eq $fmaJson) {
|
||||
$evidence.reason = 'one or both tile hash dumps are missing'
|
||||
return $evidence
|
||||
}
|
||||
$defaultRecords = @((Get-JsonValue $defaultJson 'records' @()))
|
||||
$fmaRecords = @((Get-JsonValue $fmaJson 'records' @()))
|
||||
$evidence.default_record_count = $defaultRecords.Count
|
||||
$evidence.crt_fma3_off_record_count = $fmaRecords.Count
|
||||
$defaultByKey = @{}
|
||||
$fmaByKey = @{}
|
||||
foreach ($record in $defaultRecords) {
|
||||
$key = "$($record.tile_x):$($record.tile_y):$($record.tile_z):$($record.level)"
|
||||
$defaultByKey[$key] = $record
|
||||
}
|
||||
foreach ($record in $fmaRecords) {
|
||||
$key = "$($record.tile_x):$($record.tile_y):$($record.tile_z):$($record.level)"
|
||||
$fmaByKey[$key] = $record
|
||||
}
|
||||
$differences = [System.Collections.Generic.List[object]]::new()
|
||||
$keys = @($defaultByKey.Keys) + @($fmaByKey.Keys) | Sort-Object -Unique
|
||||
foreach ($key in $keys) {
|
||||
if (-not $defaultByKey.ContainsKey($key)) {
|
||||
[void]$differences.Add([ordered]@{ tile = $key; reason = 'missing_from_default' })
|
||||
continue
|
||||
}
|
||||
if (-not $fmaByKey.ContainsKey($key)) {
|
||||
[void]$differences.Add([ordered]@{ tile = $key; reason = 'missing_from_crt_fma3_off' })
|
||||
continue
|
||||
}
|
||||
$left = $defaultByKey[$key]
|
||||
$right = $fmaByKey[$key]
|
||||
foreach ($property in @('origin_x_voxels', 'origin_y_voxels', 'origin_z_voxels',
|
||||
'step', 'cells', 'band_chunk_lo', 'band_chunk_hi',
|
||||
'sheet_tile', 'empty', 'triangle_count', 'geometry_hash')) {
|
||||
if ([string](Get-JsonValue $left $property '') -ne [string](Get-JsonValue $right $property '')) {
|
||||
[void]$differences.Add([ordered]@{
|
||||
tile = $key
|
||||
reason = "property_mismatch:$property"
|
||||
default = Get-JsonValue $left $property $null
|
||||
crt_fma3_off = Get-JsonValue $right $property $null
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
$evidence.compared_tile_count = [Math]::Min($defaultByKey.Count, $fmaByKey.Count)
|
||||
$evidence.first_differences = @($differences | Select-Object -First 10)
|
||||
$leftConfig = Get-JsonValue $defaultJson 'runtime_config' $null
|
||||
$rightConfig = Get-JsonValue $fmaJson 'runtime_config' $null
|
||||
$evidence.runtime_config_match = (ConvertTo-CompactJson $leftConfig) -eq (ConvertTo-CompactJson $rightConfig)
|
||||
$evidence.default_runtime_config = $leftConfig
|
||||
$evidence.crt_fma3_off_runtime_config = $rightConfig
|
||||
if ($differences.Count -eq 0 -and $evidence.runtime_config_match -and
|
||||
$defaultByKey.Count -eq $fmaByKey.Count) {
|
||||
$evidence.status = 'passed'
|
||||
} else {
|
||||
$evidence.reason = if ($differences.Count -gt 0) { 'per-tile geometry differs' }
|
||||
elseif (-not $evidence.runtime_config_match) { 'runtime configuration differs' }
|
||||
else { 'tile record counts differ' }
|
||||
}
|
||||
return $evidence
|
||||
}
|
||||
|
||||
function Get-SurfaceFallMetrics([string]$LogPath) {
|
||||
$lineMatch = @(Select-String -LiteralPath $LogPath -Pattern '\[VoxelForgeSurfaceFallTest\] result=' -ErrorAction SilentlyContinue |
|
||||
Select-Object -Last 1)
|
||||
@@ -926,7 +1153,17 @@ try {
|
||||
Ensure-Directory (Join-Path $RunRoot 'Logs')
|
||||
Ensure-Directory (Join-Path $RunRoot 'User')
|
||||
Ensure-Directory (Join-Path $RunRoot 'ZenData')
|
||||
Ensure-Directory (Join-Path $RunRoot 'DDC')
|
||||
$DDCPath = Join-Path $RunRoot 'DDC'
|
||||
if (-not [string]::IsNullOrWhiteSpace($SharedDDC)) {
|
||||
if (-not [IO.Path]::IsPathRooted($SharedDDC)) {
|
||||
throw "-SharedDDC must be an absolute path: $SharedDDC"
|
||||
}
|
||||
$DDCPath = Get-FullPath $SharedDDC
|
||||
if (-not (Test-UnderPath $DDCPath (Join-Path $PluginRoot 'Saved'))) {
|
||||
throw "-SharedDDC must stay under the plugin Saved directory: $DDCPath"
|
||||
}
|
||||
}
|
||||
Ensure-Directory $DDCPath
|
||||
$localBefore = Get-StateSnapshot $LocalAppDataRoot
|
||||
|
||||
foreach ($required in @($EditorExe, $UbtDll, $HostProject, $ProjectFile)) {
|
||||
@@ -944,7 +1181,7 @@ try {
|
||||
$buildStdout = Join-Path $RunRoot 'Logs\build.stdout.log'
|
||||
$buildError = Join-Path $RunRoot 'Logs\build.err.log'
|
||||
$buildArgs = @(('"{0}"' -f $UbtDll), 'UnrealEditor', 'Win64', 'Development', ('-Project="{0}"' -f $HostProject),
|
||||
'-WaitMutex', '-FromMsBuild', '-architecture=x64', '-NoUBA', '-MaxParallelActions=2',
|
||||
'-WaitMutex', '-FromMsBuild', '-architecture=x64', '-NoUBA', '-MaxParallelActions=4',
|
||||
('-Log="{0}"' -f $buildLog))
|
||||
$BuildRecord = Invoke-BuildProcess $buildArgs $buildLog $buildError $buildStdout
|
||||
if ($BuildRecord.status -ne 'passed') { Add-Failure "staged build $($BuildRecord.status)" }
|
||||
@@ -1017,6 +1254,94 @@ try {
|
||||
(Join-Path $RunRoot 'Logs\probe.log') (Join-Path $RunRoot 'Logs\probe.stdout.log') (Join-Path $RunRoot 'Logs\probe.stderr.log')
|
||||
$CommandletMetrics = Get-CommandletMetrics $commandletOut 'owner probe'
|
||||
}
|
||||
'horizon' {
|
||||
$tracePath = Join-Path $RunRoot 'horizon_trace.jsonl'
|
||||
$baseCvars['voxel.TestRenderDistanceChunks'] = $HorizonRenderDistanceChunks
|
||||
$baseCvars['voxel.TestMaxClipLevel'] = $HorizonLevel
|
||||
$baseCvars['voxel.TestFarSheetRing'] = 0
|
||||
$baseCvars['voxel.StartupTraceFile'] = $tracePath
|
||||
$baseCvars['voxel.StartupTraceMaxTiles'] = 16384
|
||||
$baseCvars['voxel.TestExitOnSteady'] = 1
|
||||
$baseCvars['voxel.OuterClassifierMode'] = 0
|
||||
$args = New-CommonArguments 'game' (Join-Path $RunRoot 'Logs\horizon.log') (Merge-Cvars $baseCvars)
|
||||
$gameLedger = Invoke-UnrealLaunch "horizon-l$($HorizonLevel)-$($HorizonRenderDistanceChunks)-game" $args `
|
||||
(Join-Path $RunRoot 'Logs\horizon.log') (Join-Path $RunRoot 'Logs\horizon.stdout.log') (Join-Path $RunRoot 'Logs\horizon.stderr.log')
|
||||
$GameMetrics = Get-StreamingMetrics (Join-Path $RunRoot 'Logs\horizon.log')
|
||||
$HorizonMetrics = Get-HorizonMetrics $tracePath $gameLedger $GameMetrics
|
||||
if ($HorizonMetrics.status -ne 'ok') {
|
||||
Add-Failure "horizon measurement trace did not settle: $($HorizonMetrics.status)"
|
||||
}
|
||||
}
|
||||
'horizon-render' {
|
||||
$tracePath = Join-Path $RunRoot 'horizon_render_trace.jsonl'
|
||||
$baseCvars['voxel.TestRenderDistanceChunks'] = $HorizonRenderDistanceChunks
|
||||
$baseCvars['voxel.TestMaxClipLevel'] = $HorizonLevel
|
||||
$baseCvars['voxel.TestFarSheetRing'] = 0
|
||||
$baseCvars['voxel.StartupTraceFile'] = $tracePath
|
||||
$baseCvars['voxel.StartupTraceMaxTiles'] = 16384
|
||||
$baseCvars['voxel.TestCeilingView'] = 1
|
||||
$baseCvars['voxel.TestCeilingCaptureDelaySeconds'] = 90
|
||||
$baseCvars['voxel.TestExitSeconds'] = 180
|
||||
$baseCvars['voxel.OuterClassifierMode'] = 0
|
||||
$renderLog = Join-Path $RunRoot 'Logs\horizon-render.log'
|
||||
$args = New-CommonArguments 'game' $renderLog (Merge-Cvars $baseCvars) $true
|
||||
$gameLedger = Invoke-UnrealLaunch "horizon-l$($HorizonLevel)-$($HorizonRenderDistanceChunks)-render" $args `
|
||||
$renderLog (Join-Path $RunRoot 'Logs\horizon-render.stdout.log') (Join-Path $RunRoot 'Logs\horizon-render.stderr.log')
|
||||
$GameMetrics = Get-StreamingMetrics $renderLog
|
||||
$HorizonMetrics = Get-HorizonMetrics $tracePath $gameLedger $GameMetrics
|
||||
$HorizonRenderMetrics = Get-HorizonRenderMetrics $gameLedger $renderLog
|
||||
if ($HorizonMetrics.status -ne 'ok') {
|
||||
Add-Failure "horizon render trace did not settle: $($HorizonMetrics.status)"
|
||||
}
|
||||
if ($HorizonRenderMetrics.status -ne 'passed') {
|
||||
Add-Failure "horizon render did not produce a capture: $($HorizonRenderMetrics.status)"
|
||||
}
|
||||
}
|
||||
'determinism' {
|
||||
$defaultDump = Join-Path $RunRoot 'tile_hashes_default.json'
|
||||
$crtFma3OffDump = Join-Path $RunRoot 'tile_hashes_crt_fma3_off.json'
|
||||
$defaultTrace = Join-Path $RunRoot 'determinism_default_trace.jsonl'
|
||||
$crtFma3OffTrace = Join-Path $RunRoot 'determinism_crt_fma3_off_trace.jsonl'
|
||||
$determinismBase = [ordered]@{
|
||||
'voxel.TestRenderDistanceChunks' = $HorizonRenderDistanceChunks
|
||||
'voxel.TestMaxClipLevel' = $HorizonLevel
|
||||
'voxel.TestFarSheetRing' = 0
|
||||
'voxel.TestExitOnSteady' = 1
|
||||
'voxel.StartupTraceMaxTiles' = 16384
|
||||
'voxel.OuterClassifierMode' = 0
|
||||
}
|
||||
$defaultCvars = [ordered]@{}
|
||||
foreach ($key in $determinismBase.Keys) { $defaultCvars[$key] = $determinismBase[$key] }
|
||||
$defaultCvars['voxel.StartupTraceFile'] = $defaultTrace
|
||||
$defaultCvars['voxel.TileHashDump'] = $defaultDump
|
||||
$defaultLog = Join-Path $RunRoot 'Logs\determinism-default.log'
|
||||
$defaultArgs = New-CommonArguments 'game' $defaultLog (Merge-Cvars $defaultCvars)
|
||||
$defaultLedger = Invoke-UnrealLaunch "determinism-default-l$($HorizonLevel)-$($HorizonRenderDistanceChunks)" $defaultArgs `
|
||||
$defaultLog (Join-Path $RunRoot 'Logs\determinism-default.stdout.log') (Join-Path $RunRoot 'Logs\determinism-default.stderr.log')
|
||||
$defaultMetrics = Get-HorizonMetrics $defaultTrace $defaultLedger (Get-StreamingMetrics $defaultLog)
|
||||
|
||||
$crtFma3OffCvars = [ordered]@{}
|
||||
foreach ($key in $determinismBase.Keys) { $crtFma3OffCvars[$key] = $determinismBase[$key] }
|
||||
$crtFma3OffCvars['voxel.StartupTraceFile'] = $crtFma3OffTrace
|
||||
$crtFma3OffCvars['voxel.TileHashDump'] = $crtFma3OffDump
|
||||
$crtFma3OffCvars['voxel.CrtFma3'] = 0
|
||||
$crtFma3OffLog = Join-Path $RunRoot 'Logs\determinism-crt-fma3-off.log'
|
||||
$crtFma3OffArgs = New-CommonArguments 'game' $crtFma3OffLog (Merge-Cvars $crtFma3OffCvars)
|
||||
$crtFma3OffLedger = Invoke-UnrealLaunch "determinism-crt-fma3-off-l$($HorizonLevel)-$($HorizonRenderDistanceChunks)" $crtFma3OffArgs `
|
||||
$crtFma3OffLog (Join-Path $RunRoot 'Logs\determinism-crt-fma3-off.stdout.log') (Join-Path $RunRoot 'Logs\determinism-crt-fma3-off.stderr.log')
|
||||
$crtFma3OffMetrics = Get-HorizonMetrics $crtFma3OffTrace $crtFma3OffLedger (Get-StreamingMetrics $crtFma3OffLog)
|
||||
|
||||
$DeterminismEvidence = Compare-DeterminismDumps $defaultDump $crtFma3OffDump
|
||||
$DeterminismEvidence.level = $HorizonLevel
|
||||
$DeterminismEvidence.render_distance_chunks = $HorizonRenderDistanceChunks
|
||||
$DeterminismEvidence.far_sheet_ring = $false
|
||||
$DeterminismEvidence.default_measurement = $defaultMetrics
|
||||
$DeterminismEvidence.crt_fma3_off_measurement = $crtFma3OffMetrics
|
||||
$GameMetrics = $crtFma3OffMetrics
|
||||
if ($DeterminismEvidence.status -ne 'passed') {
|
||||
Add-Failure "determinism comparison failed: $(Get-JsonValue $DeterminismEvidence 'reason' 'unknown difference')"
|
||||
}
|
||||
}
|
||||
'perf' {
|
||||
$baseCvars['voxel.TestExitSeconds'] = 15
|
||||
$baseCvars['voxel.ProfileTileGeneration'] = 1
|
||||
@@ -1201,6 +1526,7 @@ $result = [ordered]@{
|
||||
scenario = $Scenario
|
||||
label = $Label
|
||||
run_directory = $RunRoot
|
||||
ddc_path = $DDCPath
|
||||
project = $ProjectFile
|
||||
staged_host = $HostProject
|
||||
assets_mode = $Assets
|
||||
@@ -1220,6 +1546,9 @@ $result = [ordered]@{
|
||||
probe_amplitudes = if ($CommandletMetrics.Contains('probe_amplitudes')) { $CommandletMetrics.probe_amplitudes } else { [ordered]@{} }
|
||||
commandlet = $CommandletMetrics
|
||||
game = $GameMetrics
|
||||
horizon = $HorizonMetrics
|
||||
horizon_render = $HorizonRenderMetrics
|
||||
determinism = $DeterminismEvidence
|
||||
surface_fall = $SurfaceFallMetrics
|
||||
strate_crossing = $StrateCrossingMetrics
|
||||
collision_gate_stress = $CollisionGateStressMetrics
|
||||
@@ -1246,6 +1575,7 @@ try {
|
||||
[void]$summaryLines.Add("$(if ($pass) { 'PASS' } else { 'FAIL' }) $reason")
|
||||
[void]$summaryLines.Add("scenario=$Scenario label=$Label")
|
||||
[void]$summaryLines.Add("run_directory=$RunRoot")
|
||||
[void]$summaryLines.Add("ddc_path=$DDCPath")
|
||||
[void]$summaryLines.Add("assets=$Assets")
|
||||
[void]$summaryLines.Add("canonical_baseline=$(ConvertTo-CompactJson $result.canonical_baseline)")
|
||||
[void]$summaryLines.Add("capability_counts=$(ConvertTo-CompactJson $result.capability_counts)")
|
||||
@@ -1257,6 +1587,9 @@ try {
|
||||
[void]$summaryLines.Add("collision_gate_stress=$(ConvertTo-CompactJson $result.collision_gate_stress)")
|
||||
[void]$summaryLines.Add("probe_amplitudes=$(ConvertTo-CompactJson $result.probe_amplitudes)")
|
||||
[void]$summaryLines.Add("worker_seconds=$(ConvertTo-CompactJson $result.worker_seconds)")
|
||||
[void]$summaryLines.Add("horizon=$(ConvertTo-CompactJson $result.horizon)")
|
||||
[void]$summaryLines.Add("horizon_render=$(ConvertTo-CompactJson $result.horizon_render)")
|
||||
[void]$summaryLines.Add("determinism=$(ConvertTo-CompactJson $result.determinism)")
|
||||
[void]$summaryLines.Add("p50_p95=$(ConvertTo-CompactJson $result.p50_p95)")
|
||||
[void]$summaryLines.Add("tests=$(ConvertTo-CompactJson $result.tests)")
|
||||
[void]$summaryLines.Add("parity=$(ConvertTo-CompactJson $result.parity)")
|
||||
|
||||
Reference in new issue
Block a user