# 69 — BPC_PerformanceScaler

## Blueprint Spec — UE 5.5–5.7

---

### Parent Class
`ActorComponent`

### Dependencies
- [`BPC_RenderPipelineManager`](../12-settings/149_BPC_RenderPipelineManager.md) — **Delegates all CVar application** (GI, shadows, upscaling, Nanite, quality tiers)
- [`DA_RenderPipelineProfile`](../14-data-assets/DA_RenderPipelineProfile.md) — Reads render config per platform + quality tier
- [`BPC_LightingManager`](65_BPC_LightingManager.md) — Controls light quality
- [`BPC_AudioManager`](66_BPC_AudioManager.md) — Controls audio quality
- [`BPC_VFXManager`](67_BPC_VFXManager.md) — Controls particle LOD
- [`BPC_AtmosphereController`](64_BPC_AtmosphereController.md) — Preset complexity reduction
- [`SS_SaveManager`](../05-saveload/28_SS_SaveManager.md) — Save performance settings
- [`GI_GameFramework`](../01-core/04_GI_GameFramework.md) — Frame time measurement

### Purpose
Automatically adjusts rendering, audio, and gameplay quality settings based on real-time performance metrics (frame time, draw calls, memory usage). **Delegates all render pipeline CVar changes to `BPC_RenderPipelineManager`** — this component focuses on monitoring performance and deciding WHEN to change quality tiers. The RenderPipelineManager handles the HOW (which CVars to execute, whether a reload is needed). Supports manual user override via Settings menu and adaptive automatic mode. Maintains a performance budget that is dynamically allocated across subsystems.

### Enums

**EPerformanceQualityLevel**

| Value | Description |
|-------|-------------|
| Low | Minimum quality, max performance |
| Medium | Balanced quality/performance |
| High | High quality |
| Ultra | Maximum quality, no compromises |
| Cinematic | Maximum visual fidelity, 30fps target |

**EScalingTarget**

| Value | Description |
|-------|-------------|
| ResolutionScale | Screen percentage |
| ShadowQuality | Shadow resolution and distance |
| TextureQuality | Texture streaming pool |
| PostProcessQuality | Post-process complexity |
| ParticleLOD | VFX particle count |
| AudioQuality | Audio layer count |
| ViewDistance | Draw distance |
| FoliageDensity | Foliage and grass density |
| AntiAliasing | Anti-aliasing method |
| GlobalIllumination | GI quality |

### Variables

| Name | Type | Description |
|------|------|-------------|
| `CurrentQualityLevel` | EPerformanceQualityLevel | Active quality level |
| `TargetQualityLevel` | EPerformanceQualityLevel | Quality being transitioned to |
| `bAdaptiveMode` | Bool | Auto-adjust based on performance |
| `bUserOverride` | Bool | Player manually set quality |
| `FrameTimeHistory` | TArray\<Float\> | Rolling frame time samples |
| `AverageFrameTime` | Float | Smoothed average frame time |
| `FrameTimeVariance` | Float | Frame time stability metric |
| `TargetFrameTime` | Float | Desired frame time (e.g., 16.67ms for 60fps) |
| `PerformanceBudget` | Float | Allowed ms per frame |
| `ScalerSettings` | TMap\<EScalingTarget, float\> | Current scaling multipliers |
| `bIsScalingInProgress` | Bool | Currently adjusting settings |
| `MeasurementInterval` | Float | Seconds between performance checks |
| `AdjustmentCooldown` | Float | Min time between quality changes |
| `LastAdjustmentTime` | Float | Last quality change timestamp |
| `QualityOverrideLevel` | EPerformanceQualityLevel | Manual override level |
| `PlatformProfile` | FName | Detected platform profile |

### Functions

| Name | Inputs | Outputs | Description |
|------|--------|---------|-------------|
| `Initialize` | — | — | Read saved settings, detect platform |
| `SetQualityLevel` | Level: EPerformanceQualityLevel | — | Apply quality level |
| `SetAdaptiveMode` | bEnabled: Bool | — | Toggle adaptive scaling |
| `GetCurrentQualityLevel` | — | EPerformanceQualityLevel | Current level |
| `GetRecommendedQuality` | — | EPerformanceQualityLevel | System-recommended level |
| `MeasurePerformance` | — | — | Sample frame time from GI_GameFramework |
| `EvaluateScaling` | — | — | Decide if scaling is needed |
| `ApplyScalerSettings` | Level: EPerformanceQualityLevel | — | Apply settings to subsystems |
| `SetResolutionScale` | Scale: Float | — | Screen percentage |
| `SetShadowQuality` | Level: Int32 | — | Shadow resolution preset |
| `SetTexturePoolSize` | SizeMB: Int32 | — | Texture streaming pool |
| `SetViewDistance` | Distance: Float | — | Draw distance |
| `SetFoliageDensity` | Density: Float | — | Foliage cull distance |
| `SetParticleLOD` | LODLevel: Int32 | — | Particle LOD for VFXManager |
| `SetAudioQuality` | Quality: Int32 | — | Audio layer count |
| `SetGlobalIllumination` | Quality: Int32 | — | GI method |
| `NotifySubsystemScaled` | Target: EScalingTarget | — | Broadcast scaled subsystems |
| `SavePerformanceSettings` | — | — | Persist to SS_SaveManager |
| `OnFrameTimeSpike` | SpikeValue: Float | — | Handle sudden frame drop |

### Blueprint Flow

```
[BeginPlay]
  └─► Initialize():
         Detect platform (Console, PC Low/Med/High, Steam Deck, etc.)
         Load saved settings from SS_SaveManager
         If no saved settings:
              GetRecommendedQuality() based on platform
         Apply quality level
         Start measurement timer with MeasurementInterval

[Timer - MeasurePerformance]
  └─► MeasurePerformance():
         Get frame time from GI_GameFramework.FrameTimeHistory
         Add to local FrameTimeHistory, keep rolling window (last 60 frames)
         Calculate AverageFrameTime (exponential moving average)
         Calculate FrameTimeVariance (standard deviation of history)
         If bAdaptiveMode and not bUserOverride:
              EvaluateScaling()

[EvaluateScaling]
  └─► If AverageFrameTime > TargetFrameTime * 1.2:
         (Running slow, too many frames over target)
         If TimeSince LastAdjustment > AdjustmentCooldown:
              SetQualityLevel(CurrentQualityLevel - 1)
  └─► If AverageFrameTime < TargetFrameTime * 0.7 and FrameTimeVariance < 2.0:
         (Running fast, stable, can upgrade)
         If TimeSince LastAdjustment > AdjustmentCooldown * 2:
              SetQualityLevel(CurrentQualityLevel + 1)
  └─► If FrameTimeVariance > 5.0:
         (Unstable frame times, reduce quality for stability)
         SetQualityLevel(CurrentQualityLevel - 1)

[SetQualityLevel]
  └─► If CurrentQualityLevel == Level: return
  └─► TargetQualityLevel = Level
  └─► bIsScalingInProgress = true
  └─► Delegate to BPC_RenderPipelineManager.ApplyQualityPreset(PresetName)
       → RenderPipelineManager handles ALL CVar application
       → Reads DA_RenderPipelineProfile for current platform
       → Returns whether reload is required
  └─► For each non-render subsystem:
         BPC_LightingManager:  Reduce dynamic light count
         BPC_AudioManager:     Reduce active layers and spatial audio
         BPC_VFXManager:       SetParticleLOD mapped to QualityLevel
         BPC_AtmosphereController: Reduce preset complexity
  └─► bIsScalingInProgress = false
  └─► CurrentQualityLevel = TargetQualityLevel
  └─► OnQualityLevelChanged.Broadcast(CurrentQualityLevel)
  └─► SavePerformanceSettings()

[ApplyScalerSettings - Quality Levels]
  └─► Low:
         ResolutionScale = 0.7
         ShadowQuality = 0 (off/low)
         TexturePoolSize = 512 MB
         PostProcessQuality = Low
         ParticleLOD = 2 (minimum)
         AudioQuality = 0 (mono, 2 layers max)
         ViewDistance = Low
         FoliageDensity = 0.3
         AntiAliasing = TSR Low
         GlobalIllumination = None
  └─► Medium:
         ResolutionScale = 0.85
         ShadowQuality = 1 (medium)
         TexturePoolSize = 1024 MB
         PostProcessQuality = Medium
         ParticleLOD = 1 (reduced)
         AudioQuality = 1 (stereo, 4 layers)
         ViewDistance = Medium
         FoliageDensity = 0.6
         AntiAliasing = TSR Medium
         GlobalIllumination = Lumen Low
  └─► High:
         ResolutionScale = 1.0
         ShadowQuality = 2 (high)
         TexturePoolSize = 2048 MB
         PostProcessQuality = High
         ParticleLOD = 0 (full)
         AudioQuality = 2 (surround, all layers)
         ViewDistance = High
         FoliageDensity = 1.0
         AntiAliasing = TSR High
         GlobalIllumination = Lumen High
  └─► Ultra:
         ResolutionScale = 1.0
         ShadowQuality = 3 (ultra)
         TexturePoolSize = 4096 MB
         PostProcessQuality = Cinematic
         ParticleLOD = 0 (full with extras)
         AudioQuality = 3 (full spatial, max layers)
         ViewDistance = Epic
         FoliageDensity = 1.5
         AntiAliasing = TSR Epic
         GlobalIllumination = Lumen Ultra
  └─► Cinematic:
         ResolutionScale = 1.0 (or 1.25 for supersampling)
         ShadowQuality = 3 (ultra with contact shadows)
         TexturePoolSize = 8192 MB
         PostProcessQuality = Cinematic with extra effects
         ParticleLOD = 0 with extra presets
         AudioQuality = 3
         ViewDistance = Epic with forced high-resolution impostors
         FoliageDensity = 2.0
         AntiAliasing = TSR Epic with extra samples
         GlobalIllumination = Lumen Ultra + Path Tracing toggle

[OnFrameTimeSpike]
  └─► If spike > 50ms (sudden freeze):
         SetQualityLevel(Low) temporarily
         After 5 seconds of stable performance:
              Gradually restore to previous level
```

### Event Dispatchers

| Name | Payload | Description |
|------|---------|-------------|
| `OnQualityLevelChanged` | NewLevel: EPerformanceQualityLevel | Broadcast quality change |
| `OnScalingTargetAdjusted` | Target: EScalingTarget, NewValue: Float | Per-target adjustment |

### Communications With

| Target | Method | Why |
|--------|--------|-----|
| [`BPC_RenderPipelineManager`](../12-settings/149_BPC_RenderPipelineManager.md) | Direct call | **All render CVar application** — delegates quality tier to pipeline manager |
| [`GI_GameFramework`](../01-core/04_GI_GameFramework.md) | Direct call | Frame time data source |
| [`SS_SaveManager`](../05-saveload/28_SS_SaveManager.md) | Direct call | Save/load settings |
| [`BPC_LightingManager`](65_BPC_LightingManager.md) | Get from player | Light quality reduction |
| [`BPC_AudioManager`](66_BPC_AudioManager.md) | Get from player | Audio quality reduction |
| [`BPC_VFXManager`](67_BPC_VFXManager.md) | Get from player | Particle LOD control |
| [`BPC_AtmosphereController`](64_BPC_AtmosphereController.md) | Get from player | Preset complexity reduction |
| [`WBP_SettingsUI`](../06-ui/44_WBP_SettingsUI.md) | Event | User quality override |
| [`BPC_PlayerController`] | Cast | Console command execution |

### Reuse Notes
- Works with any subsystem that exposes quality API: Lighting, Audio, VFX, etc.
- Frame time history rolling window: last 60 frames for smooth average
- Adaptive mode is off by default; user must enable in settings
- User override locks quality level until adaptive mode is re-enabled
- PlatformProfile stores detected hardware for initial recommendation
- Adjustment cooldown prevents oscillation between quality levels
- Per-scaling-target notifications enable UI to show specific quality changes