feat(render): implement Campaign AR and terrain fidelity
This commit is contained in:
parent
99cf26e00c
commit
7a5f96ede5
368 changed files with 50611 additions and 950 deletions
325
tools/atmospheric-performance-matrix-common.ps1
Normal file
325
tools/atmospheric-performance-matrix-common.ps1
Normal file
|
|
@ -0,0 +1,325 @@
|
|||
Set-StrictMode -Version Latest
|
||||
|
||||
function Get-AtmosphericPresetUnavailableReasonClassification {
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string]$Reason,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateSet('low', 'medium', 'high', 'auto')][string]$Preset)
|
||||
|
||||
if ($Preset -eq 'auto') {
|
||||
$performancePattern = "^Automatic quality disabled render pack 'acdream\.atmospheric' because Low remained over its declared performance budget for 180 stable samples: GPU p99 (?<gpu>[0-9]+(?:\.[0-9]+)?) ms \(budget (?<gpuBudget>[0-9]+(?:\.[0-9]+)?) ms\), CPU p99 (?<cpu>[0-9]+(?:\.[0-9]+)?) ms \(budget (?<cpuBudget>[0-9]+(?:\.[0-9]+)?) ms\), resident GPU bytes (?<resident>[0-9]+) \(budget (?<residentBudget>[0-9]+)\)\.$"
|
||||
$performance = [Regex]::Match(
|
||||
$Reason,
|
||||
$performancePattern,
|
||||
[Text.RegularExpressions.RegexOptions]::CultureInvariant)
|
||||
if ($performance.Success) {
|
||||
$culture = [Globalization.CultureInfo]::InvariantCulture
|
||||
$gpu = [double]::Parse($performance.Groups['gpu'].Value, $culture)
|
||||
$gpuBudget = [double]::Parse($performance.Groups['gpuBudget'].Value, $culture)
|
||||
$cpu = [double]::Parse($performance.Groups['cpu'].Value, $culture)
|
||||
$cpuBudget = [double]::Parse($performance.Groups['cpuBudget'].Value, $culture)
|
||||
$resident = [long]::Parse($performance.Groups['resident'].Value, $culture)
|
||||
$residentBudget = [long]::Parse($performance.Groups['residentBudget'].Value, $culture)
|
||||
if ($gpu -gt $gpuBudget -or $cpu -gt $cpuBudget -or
|
||||
$resident -gt $residentBudget) {
|
||||
return [pscustomobject][ordered]@{
|
||||
Supported = $true
|
||||
Classification = 'PerformanceUnavailable'
|
||||
Reason = $Reason
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($resolvedPreset in @('low', 'medium', 'high')) {
|
||||
$classification = Get-AtmosphericPresetUnavailableReasonClassification `
|
||||
-Reason $Reason -Preset $resolvedPreset
|
||||
if ($classification.Supported) { return $classification }
|
||||
}
|
||||
return [pscustomobject][ordered]@{
|
||||
Supported = $false
|
||||
Classification = 'UnexpectedFailure'
|
||||
Reason = $Reason
|
||||
}
|
||||
}
|
||||
|
||||
# Keep this allow-list deliberately narrow. A shader, validation, pipeline,
|
||||
# draw, or ordinary runtime failure must not become a passing
|
||||
# "unavailable" row merely because its prose contains words such as
|
||||
# unsupported, resource, memory, or capability.
|
||||
$escapedPreset = [Regex]::Escape($Preset)
|
||||
$preparationPrefix = "(?:Render pack 'acdream\.atmospheric' could not be prepared: )?"
|
||||
$resourcePatterns = @(
|
||||
"^${preparationPrefix}Render pack preset '$escapedPreset' needs [1-9][0-9]* resident GPU bytes at [1-9][0-9]*x[1-9][0-9]*; its declared ceiling is [1-9][0-9]*\. Select a compatible preset or reduce the main-world resolution\.$",
|
||||
"^${preparationPrefix}Render pack preset '$escapedPreset' needs [1-9][0-9]* resident GPU bytes at [1-9][0-9]*x[1-9][0-9]*; this host permits [1-9][0-9]* under its .+ policy\.$",
|
||||
"^${preparationPrefix}Render pack preset '$escapedPreset' needs [1-9][0-9]* transient multisample GPU bytes at [1-9][0-9]*x[1-9][0-9]* x[1-9][0-9]*; this host permits [1-9][0-9]* under its .+ policy\.$",
|
||||
"^Directional shadow rendering failed: Render pack preset '$escapedPreset' needs [1-9][0-9]* resident GPU bytes after materializing its scene-dependent shadow command buffers; the active pack budget is [1-9][0-9]* bytes\.$",
|
||||
"^Preset '$escapedPreset' declares a [1-9][0-9]*-byte resident GPU ceiling, but this host permits [1-9][0-9]* bytes under its .+ policy\.$"
|
||||
)
|
||||
foreach ($pattern in $resourcePatterns) {
|
||||
if ([Regex]::IsMatch($Reason, $pattern, [Text.RegularExpressions.RegexOptions]::CultureInvariant)) {
|
||||
return [pscustomobject][ordered]@{
|
||||
Supported = $true
|
||||
Classification = 'ResourceUnavailable'
|
||||
Reason = $Reason
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$capabilityPatterns = @(
|
||||
"^Pack 'acdream\.atmospheric' requires unsupported capability '[A-Za-z][A-Za-z0-9]*'\.$",
|
||||
"^Preset '$escapedPreset' requires unsupported capability '[A-Za-z][A-Za-z0-9]*'\.$",
|
||||
"^Preset '$escapedPreset' resource '[a-z0-9][a-z0-9.-]*' needs [1-9][0-9]* image-array layers; this device provides [0-9]+\.$",
|
||||
"^Preset '$escapedPreset' resource '[a-z0-9][a-z0-9.-]*' needs [1-9][0-9]*(?:\.[0-9]+)?x[1-9][0-9]*(?:\.[0-9]+)?; this device's maximum 2-D image edge is [1-9][0-9]*\.$",
|
||||
"^${preparationPrefix}Render pack preset '$escapedPreset' resolves an image to [1-9][0-9]*x[1-9][0-9]* at [1-9][0-9]*x[1-9][0-9]*; this device's maximum 2-D image edge is [1-9][0-9]*\.$",
|
||||
"^${preparationPrefix}Render pack preset '$escapedPreset' needs [1-9][0-9]* image-array layers; this device provides [0-9]+\.$"
|
||||
)
|
||||
foreach ($pattern in $capabilityPatterns) {
|
||||
if ([Regex]::IsMatch($Reason, $pattern, [Text.RegularExpressions.RegexOptions]::CultureInvariant)) {
|
||||
return [pscustomobject][ordered]@{
|
||||
Supported = $true
|
||||
Classification = 'CapabilityUnavailable'
|
||||
Reason = $Reason
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [pscustomobject][ordered]@{
|
||||
Supported = $false
|
||||
Classification = 'UnexpectedFailure'
|
||||
Reason = $Reason
|
||||
}
|
||||
}
|
||||
|
||||
function Test-AtmosphericPerformanceMetadataEvidence {
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string]$MetadataPath,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateSet('retail', 'low', 'medium', 'high', 'auto')][string]$Preset,
|
||||
[Parameter(Mandatory = $true)][int]$ExpectedWidth,
|
||||
[Parameter(Mandatory = $true)][int]$ExpectedHeight,
|
||||
[switch]$AllowSafeFallback)
|
||||
|
||||
$failures = [Collections.Generic.List[string]]::new()
|
||||
function Require([object]$Value, [string]$Name, [string]$Context) {
|
||||
$property = $Value.PSObject.Properties[$Name]
|
||||
if ($null -eq $property) { throw "$Context is missing required property '$Name'." }
|
||||
return $property.Value
|
||||
}
|
||||
function Fail([string]$Message) { $null = $failures.Add($Message) }
|
||||
function Require-FiniteNonNegative([object]$Value, [string]$Name) {
|
||||
$number = [double](Require $Value $Name 'RenderPack.Performance')
|
||||
if (-not [double]::IsFinite($number) -or $number -lt 0) {
|
||||
Fail "$Name must be finite and non-negative"
|
||||
}
|
||||
return $number
|
||||
}
|
||||
|
||||
$metadata = Get-Content -Raw -LiteralPath $MetadataPath | ConvertFrom-Json
|
||||
if ([int](Require $metadata 'SchemaVersion' 'metadata') -ne 1) {
|
||||
Fail 'metadata SchemaVersion must be 1'
|
||||
}
|
||||
if ([int](Require $metadata 'Width' 'metadata') -ne $ExpectedWidth -or
|
||||
[int](Require $metadata 'Height' 'metadata') -ne $ExpectedHeight) {
|
||||
Fail "capture extent must be ${ExpectedWidth}x${ExpectedHeight}"
|
||||
}
|
||||
$pack = Require $metadata 'RenderPack' 'metadata'
|
||||
$enhanced = $Preset -ne 'retail'
|
||||
$state = [int](Require $pack 'State' 'RenderPack')
|
||||
$fallback = $enhanced -and $state -eq 3
|
||||
$expectedPackId = if ($enhanced -and -not $fallback) { 'acdream.atmospheric' } else { 'retail' }
|
||||
$expectedVersion = if ($enhanced -and -not $fallback) { '1.0.0' } else { '' }
|
||||
$expectedPreset = if ($enhanced -and -not $fallback) { $Preset } else { 'off' }
|
||||
$expectedState = if ($enhanced -and -not $fallback) { 2 } elseif ($fallback) { 3 } else { 0 }
|
||||
$actualEffectiveQuality = [string](
|
||||
Require $pack 'EffectiveQuality' 'RenderPack')
|
||||
$expectedQuality = if ($enhanced -and -not $fallback -and $Preset -ne 'auto') {
|
||||
$Preset
|
||||
} else { 'off' }
|
||||
foreach ($comparison in @(
|
||||
@('PackId', $expectedPackId), @('PackVersion', $expectedVersion),
|
||||
@('PresetId', $expectedPreset))) {
|
||||
$actual = [string](Require $pack ([string]$comparison[0]) 'RenderPack')
|
||||
if ($actual -cne [string]$comparison[1]) {
|
||||
Fail "$($comparison[0]) was '$actual', expected '$($comparison[1])'"
|
||||
}
|
||||
}
|
||||
if ($enhanced -and -not $fallback -and $Preset -eq 'auto') {
|
||||
if ($actualEffectiveQuality -cnotin @('low', 'medium', 'high')) {
|
||||
Fail "Automatic EffectiveQuality was '$actualEffectiveQuality', expected low, medium, or high"
|
||||
}
|
||||
}
|
||||
elseif ($actualEffectiveQuality -cne $expectedQuality) {
|
||||
Fail "EffectiveQuality was '$actualEffectiveQuality', expected '$expectedQuality'"
|
||||
}
|
||||
if ($state -ne $expectedState) {
|
||||
Fail "activation state must be $expectedState"
|
||||
}
|
||||
$generation = [int](Require $pack 'ActivationGeneration' 'RenderPack')
|
||||
if (($fallback -and $generation -lt 1) -or
|
||||
($enhanced -and -not $fallback -and $Preset -eq 'auto' -and
|
||||
$generation -lt 1) -or
|
||||
($enhanced -and -not $fallback -and $Preset -ne 'auto' -and
|
||||
$generation -ne 1) -or
|
||||
(-not $enhanced -and $generation -ne 0)) {
|
||||
Fail "activation generation is invalid for state $state"
|
||||
}
|
||||
$failureReason = [string](Require $pack 'FailureReason' 'RenderPack')
|
||||
$unavailableClassification = $null
|
||||
if ($fallback) {
|
||||
if (-not $AllowSafeFallback) {
|
||||
Fail 'FailedToRetail fallback was not explicitly allowed for this capture'
|
||||
}
|
||||
if ([string]::IsNullOrWhiteSpace($failureReason)) {
|
||||
Fail 'FailedToRetail fallback must preserve one exact failure reason'
|
||||
}
|
||||
else {
|
||||
$reasonClassification = Get-AtmosphericPresetUnavailableReasonClassification `
|
||||
-Reason $failureReason -Preset $Preset
|
||||
$unavailableClassification = $reasonClassification.Classification
|
||||
if (-not $reasonClassification.Supported) {
|
||||
Fail 'FailedToRetail reason is not a strict resource/capability/Auto-performance unavailability'
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif (-not [string]::IsNullOrWhiteSpace($failureReason)) {
|
||||
Fail 'render-pack FailureReason must be empty'
|
||||
}
|
||||
|
||||
$performance = Require $pack 'Performance' 'RenderPack'
|
||||
$topResident = [long](Require $pack 'RetainedGpuBytes' 'RenderPack')
|
||||
$topTransient = [long](Require $pack 'TransientGpuBytes' 'RenderPack')
|
||||
$nestedResident = [long](Require $performance 'ResidentGpuBytes' 'RenderPack.Performance')
|
||||
$nestedTransient = [long](Require $performance 'TransientGpuBytes' 'RenderPack.Performance')
|
||||
if ($topResident -ne $nestedResident) { Fail 'top-level and performance resident GPU bytes disagree' }
|
||||
if ($topTransient -ne $nestedTransient) { Fail 'top-level and performance transient GPU bytes disagree' }
|
||||
|
||||
$casterCount = [int](Require $pack 'ShadowCasterCount' 'RenderPack')
|
||||
$cascadeCount = [int](Require $pack 'CascadeDrawCount' 'RenderPack')
|
||||
$classificationCalls = [int](Require $pack 'CpuClassificationCalls' 'RenderPack')
|
||||
$drawCalls = [int](Require $pack 'DrawCalls' 'RenderPack')
|
||||
$dispatchCalls = [int](Require $pack 'DispatchCalls' 'RenderPack')
|
||||
$imageCount = [int](Require $pack 'ImageCount' 'RenderPack')
|
||||
$bufferCount = [int](Require $pack 'BufferCount' 'RenderPack')
|
||||
$passes = @(Require $pack 'Passes' 'RenderPack')
|
||||
|
||||
if ($fallback) {
|
||||
if ($casterCount -ne 0 -or $cascadeCount -ne 0 -or $classificationCalls -ne 0 -or
|
||||
$drawCalls -ne 0 -or $dispatchCalls -ne 0 -or $passes.Count -ne 0 -or
|
||||
$imageCount -ne 0 -or $bufferCount -ne 0 -or
|
||||
$topResident -ne 0 -or $topTransient -ne 0) {
|
||||
Fail 'FailedToRetail fallback must have zero pack work and resources'
|
||||
}
|
||||
foreach ($countName in @('CpuSampleCount', 'AbsoluteReceiverCpuSampleCount', 'GpuSampleCount')) {
|
||||
if ([int](Require $performance $countName 'RenderPack.Performance') -ne 0) {
|
||||
Fail "FailedToRetail fallback must have zero $countName"
|
||||
}
|
||||
}
|
||||
foreach ($metric in @(
|
||||
'IncrementalCpuMillisecondsP50', 'IncrementalCpuMillisecondsP95',
|
||||
'IncrementalCpuMillisecondsP99', 'AbsoluteReceiverCpuMillisecondsP50',
|
||||
'AbsoluteReceiverCpuMillisecondsP95', 'AbsoluteReceiverCpuMillisecondsP99',
|
||||
'InclusiveGpuMillisecondsP50', 'InclusiveGpuMillisecondsP95',
|
||||
'InclusiveGpuMillisecondsP99', 'ResidentGpuBytes', 'TransientGpuBytes')) {
|
||||
if ([double](Require $performance $metric 'RenderPack.Performance') -ne 0) {
|
||||
Fail "FailedToRetail fallback must report zero $metric"
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif (-not $enhanced) {
|
||||
if ($casterCount -ne 0 -or $cascadeCount -ne 0 -or $classificationCalls -ne 0 -or
|
||||
$drawCalls -ne 0 -or $dispatchCalls -ne 0 -or $passes.Count -ne 0 -or
|
||||
$imageCount -ne 0 -or $bufferCount -ne 0 -or
|
||||
$topResident -ne 0 -or $topTransient -ne 0) {
|
||||
Fail 'retail row must have zero pack work, resources, and classification'
|
||||
}
|
||||
}
|
||||
else {
|
||||
$shapePreset = if ($Preset -eq 'auto') {
|
||||
$actualEffectiveQuality
|
||||
} else { $Preset }
|
||||
$expectedCascades = @{ low = 2; medium = 3; high = 4 }[$shapePreset]
|
||||
if ($casterCount -le 0) { Fail 'enhanced row must contain at least one shadow caster' }
|
||||
if ($cascadeCount -ne $expectedCascades) {
|
||||
Fail "$Preset/$shapePreset must render exactly $expectedCascades cascades"
|
||||
}
|
||||
if ($classificationCalls -ne 0) { Fail 'warmed capture must perform zero CPU classifications' }
|
||||
$expectedPassIds = [Collections.Generic.List[string]]::new()
|
||||
$expectedPassIds.Add('atmospheric-world-receiver')
|
||||
if ($shapePreset -eq 'low') {
|
||||
$expectedPassIds.Add('directional-shadow-multiview')
|
||||
}
|
||||
else {
|
||||
for ($cascade = 0; $cascade -lt $expectedCascades; $cascade++) {
|
||||
$expectedPassIds.Add("directional-shadow-cascade-$cascade")
|
||||
}
|
||||
}
|
||||
$postPassIds = @(
|
||||
'atmospheric-sun-occlusion', 'atmospheric-sun-rays',
|
||||
'atmospheric-volumetric-shafts', 'atmospheric-bloom-downsample',
|
||||
'atmospheric-bloom-blur-horizontal', 'atmospheric-bloom-blur-vertical',
|
||||
'atmospheric-filmic')
|
||||
foreach ($id in $postPassIds) { $expectedPassIds.Add($id) }
|
||||
$actualPassIds = @($passes | ForEach-Object { [string](Require $_ 'PassId' 'RenderPack.Passes[]') })
|
||||
if (($actualPassIds -join '|') -cne (@($expectedPassIds) -join '|')) {
|
||||
Fail "pass order/shape was '$($actualPassIds -join ',')'"
|
||||
}
|
||||
# The fixed offline scene resolves five prepared shadow submissions:
|
||||
# terrain plus the stable opaque/alpha-cutout world runs. Low replays
|
||||
# those once through multiview; Medium/High replay them per cascade.
|
||||
$shadowDrawsPerPass = 5
|
||||
# Low now uses the quarter-resolution separable graph as well. Its
|
||||
# volumetric pass remains declared for one stable API shape but records
|
||||
# zero draws because the Low preset disables volumetric strength.
|
||||
$postDraws = 6
|
||||
$expectedDraws = $postDraws + $(if ($shapePreset -eq 'low') {
|
||||
$shadowDrawsPerPass
|
||||
} else {
|
||||
$shadowDrawsPerPass * $expectedCascades
|
||||
})
|
||||
$summedDraws = [int](($passes | Measure-Object -Property DrawCalls -Sum).Sum)
|
||||
$summedDispatches = [int](($passes | Measure-Object -Property DispatchCalls -Sum).Sum)
|
||||
if ($drawCalls -ne $expectedDraws -or $summedDraws -ne $expectedDraws) {
|
||||
Fail "draw shape must total $expectedDraws calls"
|
||||
}
|
||||
if ($dispatchCalls -ne 0 -or $summedDispatches -ne 0) {
|
||||
Fail 'atmospheric pack must issue zero dispatch calls'
|
||||
}
|
||||
foreach ($pass in $passes) {
|
||||
$passId = [string](Require $pass 'PassId' 'RenderPack.Passes[]')
|
||||
$expectedPassDraws = if ($passId -like 'directional-shadow-*') {
|
||||
$shadowDrawsPerPass
|
||||
}
|
||||
elseif ($passId -in @('atmospheric-world-receiver', 'atmospheric-volumetric-shafts')) { 0 }
|
||||
else { 1 }
|
||||
if ([int](Require $pass 'DrawCalls' 'RenderPack.Passes[]') -ne $expectedPassDraws -or
|
||||
[int](Require $pass 'DispatchCalls' 'RenderPack.Passes[]') -ne 0) {
|
||||
Fail "pass '$passId' must record exactly $expectedPassDraws draws and zero dispatches"
|
||||
}
|
||||
}
|
||||
foreach ($countName in @('CpuSampleCount', 'AbsoluteReceiverCpuSampleCount', 'GpuSampleCount')) {
|
||||
if ([int](Require $performance $countName 'RenderPack.Performance') -ne 2048) {
|
||||
Fail "$countName must contain the complete 2048-sample window"
|
||||
}
|
||||
}
|
||||
foreach ($metric in @(
|
||||
'IncrementalCpuMillisecondsP50', 'IncrementalCpuMillisecondsP95',
|
||||
'IncrementalCpuMillisecondsP99', 'AbsoluteReceiverCpuMillisecondsP50',
|
||||
'AbsoluteReceiverCpuMillisecondsP95', 'AbsoluteReceiverCpuMillisecondsP99',
|
||||
'InclusiveGpuMillisecondsP50', 'InclusiveGpuMillisecondsP95',
|
||||
'InclusiveGpuMillisecondsP99')) { $null = Require-FiniteNonNegative $performance $metric }
|
||||
}
|
||||
|
||||
return [pscustomobject][ordered]@{
|
||||
Passed = $failures.Count -eq 0
|
||||
Failures = @($failures)
|
||||
Outcome = if ($fallback) { 'Unavailable' } elseif ($enhanced) { 'Active' } else { 'Retail' }
|
||||
UnavailableClassification = $unavailableClassification
|
||||
FailureReason = if ($fallback) { $failureReason } else { $null }
|
||||
EffectiveQuality = $actualEffectiveQuality
|
||||
ShadowCasterCount = $casterCount
|
||||
CascadeDrawCount = $cascadeCount
|
||||
CpuClassificationCalls = $classificationCalls
|
||||
PassIds = @($passes | ForEach-Object { [string]$_.PassId })
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue