Files

68 lines
5.1 KiB
PowerShell
Raw Permalink Normal View History

param(
[ValidateSet('Very Low','Low','Medium','High','Very High','Ultra')][string]$Quality = 'Medium',
[int]$SampleSeconds = 120,
[int]$Repetitions = 3,
[int]$WarmupSeconds = 60,
[int]$FrameCap = -1,
[ValidateSet(0,1)][int]$RouteBranch = 0,
[string]$OutputFolder,
[switch]$VisualOnly
)
$ErrorActionPreference = 'Stop'
$projectRoot = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..\..'))
$exe = Join-Path $projectRoot 'Builds\Windows\x64\Lighting\Ironspore.exe'
if (-not (Test-Path -LiteralPath $exe -PathType Leaf)) { throw 'Build the lighting player through Unity MCP first.' }
$otherGames = @(Get-CimInstance Win32_Process -Filter "name = 'Ironspore.exe'")
if ($otherGames.Count -gt 0) { throw 'Another Ironspore player is running. Finish it before starting an isolated validation run.' }
if (-not $OutputFolder) {
$stamp = [DateTimeOffset]::UtcNow.ToOffset([TimeSpan]::FromHours(8)).ToString('yyyyMMdd-HHmmss')
$OutputFolder = Join-Path $projectRoot ('Docs\QA\Lighting\TestResults\Run-' + $stamp)
}
$OutputFolder = [IO.Path]::GetFullPath($OutputFolder)
if (Test-Path -LiteralPath (Join-Path $OutputFolder 'report.json')) { throw 'The output already contains a report; choose a new folder.' }
New-Item -ItemType Directory -Path $OutputFolder -Force | Out-Null
$environment = [ordered]@{
recordedUtc = [DateTimeOffset]::UtcNow.ToString('O')
project = $projectRoot
cpu = @(Get-CimInstance Win32_Processor | Select-Object Name,NumberOfCores,NumberOfLogicalProcessors)
ramGiB = [math]::Round((Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory / 1GB, 2)
gpu = @(Get-CimInstance Win32_VideoController | Select-Object Name,DriverVersion)
battery = @(Get-CimInstance Win32_Battery | Select-Object BatteryStatus,EstimatedChargeRemaining)
powerPlan = (powercfg /getactivescheme | Out-String).Trim()
runtimeAssemblySha256 = (Get-FileHash -LiteralPath (Join-Path (Split-Path -Parent $exe) 'Ironspore_Data\Managed\Ironspore.Runtime.dll') -Algorithm SHA256).Hash
profileSha256 = (Get-FileHash -LiteralPath (Join-Path $projectRoot 'Assets\Game\Data\Lighting\Daylight.asset') -Algorithm SHA256).Hash
competingPlayers = @()
}
$gpuTool = Get-Command nvidia-smi -ErrorAction SilentlyContinue
if ($gpuTool) { $environment.nvidia = @(& $gpuTool.Source --query-gpu=name,driver_version,memory.total,power.draw,temperature.gpu,clocks.current.graphics --format=csv,noheader) }
[IO.File]::WriteAllText((Join-Path $OutputFolder 'environment.json'), ($environment | ConvertTo-Json -Depth 6), [Text.UTF8Encoding]::new($false))
$playerLog = Join-Path $OutputFolder 'Player.log'
$arguments = @('-lighting-qa','-lighting-quality',('"' + $Quality + '"'),'-lighting-output',('"' + $OutputFolder + '"'),'-lighting-tag',('"' + $Quality + '"'),'-lighting-seconds',$SampleSeconds,'-lighting-repeats',$Repetitions,'-lighting-warmup',$WarmupSeconds,'-lighting-cap',$FrameCap,'-lighting-branch',$RouteBranch,'-screen-width','1920','-screen-height','1080','-screen-fullscreen','1','-logFile',('"' + $playerLog + '"'))
if ($VisualOnly) { $arguments += '-lighting-visual-only' }
# This is the interactive game under visual verification, so expose its actual window.
$player = Start-Process -FilePath $exe -ArgumentList $arguments -WorkingDirectory (Split-Path -Parent $exe) -WindowStyle Normal -PassThru
Write-Output "Validating $Quality at 1920x1080; player PID $($player.Id); output $OutputFolder"
while (-not $player.HasExited) {
$competing = @(Get-CimInstance Win32_Process -Filter "name = 'Ironspore.exe'" | Where-Object { $_.ProcessId -ne $player.Id })
if ($competing.Count -gt 0) {
$environment.competingPlayers = @($competing | Select-Object ProcessId,ExecutablePath)
[IO.File]::WriteAllText((Join-Path $OutputFolder 'environment.json'), ($environment | ConvertTo-Json -Depth 6), [Text.UTF8Encoding]::new($false))
$owned = Get-CimInstance Win32_Process -Filter ("ProcessId = " + $player.Id)
if ($owned -and $owned.ExecutablePath -eq $exe) { Stop-Process -Id $player.Id }
throw 'A competing player started. This run is invalid; only this validation player was stopped.'
}
Start-Sleep -Seconds 2
$player.Refresh()
}
$reportPath = Join-Path $OutputFolder 'report.json'
if (-not (Test-Path -LiteralPath $reportPath)) { throw 'The player exited without a report. Inspect Player.log.' }
$report = Get-Content -LiteralPath $reportPath -Raw | ConvertFrom-Json
if (-not $report.completed -or -not $report.visualPassed) { throw 'Visual verification did not pass; inspect report.json and screenshots.' }
if ($report.dynamicResolution -or [math]::Abs($report.renderWidthScale - 1) -gt .001 -or [math]::Abs($report.renderHeightScale - 1) -gt .001) { throw 'The test did not render at native resolution.' }
if (-not $VisualOnly) {
if ($report.segments.Count -ne 2 * $Repetitions) { throw 'The expected camera/repetition samples are incomplete.' }
$report.segments | Select-Object name,meanFps,p95Ms,p99Ms,gpuP95Ms,waypointsVisited,passed | Format-Table
if (@($report.segments | Where-Object { -not $_.passed }).Count -gt 0) { throw 'Performance or route coverage did not pass.' }
}
Write-Output "Completed validation: $reportPath"