Add real-time lighting and shadows, training courses and targets, and animated salvage crates. Include bilingual assets and curated verification results.
68 lines
5.1 KiB
PowerShell
68 lines
5.1 KiB
PowerShell
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"
|