Files
UE5-Modular-Game-Framework/docs/blueprints/12-settings/149_BPC_RenderPipelineManager.md
Lefteris Notas 15d6e88780 feat: Implement BPC_PlatformServiceAbstraction for unified platform detection and SDK routing
- Added BPC_PlatformServiceAbstraction to centralize platform detection and SDK routing for achievements, cloud saves, and overlays.
- Updated dependencies across various systems to utilize the new platform service for consistent platform handling.
- Deprecated old platform enums in favor of a unified EPlatformFamily enum.
- Enhanced documentation for affected systems to reflect changes in platform handling and dependencies.
2026-05-22 18:22:42 +03:00

435 lines
22 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 149 — Render Pipeline Manager (`BPC_RenderPipelineManager`)
> **Blueprint-Only Implementation** — UE 5.55.7 supports all render pipeline configuration from Blueprints via `Execute Console Command` and `UGameUserSettings` API calls. This component wraps the complexity of per-platform render pipeline selection, upscaler configuration, and pre-level-load CVar application behind a simple `ApplyQualityPreset(PresetName)` interface.
---
## Purpose
Central authority for render pipeline configuration. Reads `DA_RenderPipelineProfile` Data Assets to determine the appropriate rendering method (Lumen vs Baked Lightmass), shadow system (VSM vs CSM), upscaler (DLSS/FSR/TSR/PSSR), and mesh strategy (Nanite vs LOD) for the current platform and quality preset. Applies settings via UE5 console variables, coordinates with `BPC_PerformanceScaler` for runtime quality tier adjustments, and broadcasts pipeline changes so dependent systems (Planar Capture, Audio, UI) can adapt.
## Dependencies
- **Requires:** [`DA_RenderPipelineProfile`](../14-data-assets/DA_RenderPipelineProfile.md) (render configs), [`BPC_PlatformServiceAbstraction`](../01-core/150_BPC_PlatformServiceAbstraction.md) (central platform detection), [`SS_SettingsSystem`](105_SS_SettingsSystem.md) (saved quality preferences), [`GI_GameFramework`](../01-core/04_GI_GameFramework.md) (game phase for pre-load apply)
- **Required By:** [`BPC_PerformanceScaler`](../10-adaptive/91_BPC_PerformanceScaler.md) (delegates CVar application), [`SS_PlanarCaptureManager`](../17-capture/138_SS_PlanarCaptureManager.md) (capture quality cap on pipeline change), [`WBP_SettingsMenu`](../06-ui/57_WBP_SettingsMenu.md) (Video tab quality controls)
- **Engine/Plugin Requirements:** DLSS Plugin (optional), FSR Plugin (optional), XeSS Plugin (optional), GameplayTags, Enhanced Input
## Class Info
| Property | Value |
|----------|-------|
| **Parent Class** | `ActorComponent` |
| **Class Type** | Blueprint Component |
| **Asset Path** | `Content/Framework/Settings/BPC_RenderPipelineManager` |
| **Implements Interfaces** | None |
| **Attachment** | Player Controller (`PC_CoreController`) |
---
## 1. Enums
*See [`DA_RenderPipelineProfile`](../14-data-assets/DA_RenderPipelineProfile.md) for: `ERenderPipelineMethod`, `EShadowMethod`, `EReflectionMethod`, `EUpscalerMethod`, `EMeshStrategy`.*
*See [`BPC_PlatformServiceAbstraction`](../01-core/150_BPC_PlatformServiceAbstraction.md) for: `EPlatformFamily` (unified — all systems use this).*
### `ERenderPipelineChangeType`
| Value | Description |
|-------|-------------|
| `NonDestructive = 0` | Change is safe at runtime (resolution, texture quality, FPS cap) |
| `RequiresLevelReload = 1` | Change requires reloading the current level (GI method, Nanite, shadow method) |
| `RequiresEngineRestart = 2` | Change requires full engine restart (HWRT toggle on some platforms) |
---
## 2. Structs
### `SActivePipelineState`
| Field | Type | Description |
|-------|------|-------------|
| `Platform` | `EPlatformFamily` | Detected platform (from BPC_PlatformServiceAbstraction) |
| `ActivePreset` | `FName` | Currently applied quality preset name |
| `PendingPreset` | `FName` | Queued preset awaiting reload |
| `ActivePipelineProfile` | `DA_RenderPipelineProfile` | Currently loaded profile Data Asset |
| `bPipelineApplied` | `bool` | Whether full pipeline has been applied |
| `bPendingReloadRequired` | `bool` | Whether a level reload is needed to apply changes |
| `AppliedConfig` | `SRenderPipelineConfig` | Currently active render config values |
---
## 3. Variables
### Configuration (Instance Editable)
| Variable | Type | Default | Category | Description |
|----------|------|---------|----------|-------------|
| `PlatformProfileMap` | `TMap<EPlatformFamily, DA_RenderPipelineProfile>` | `Empty` | `Config` | One Data Asset per platform family |
| `DefaultQualityPreset` | `FName` | `Medium` | `Config` | Preset to use on first launch |
| `bApplyPipelineOnBeginPlay` | `bool` | `true` | `Config` | Automatically apply pipeline on BeginPlay |
| `bAutoDetectPlatform` | `bool` | `true` | `Config` | Auto-detect platform at startup |
### Internal (Private)
| Variable | Type | Default | Category | Description |
|----------|------|---------|----------|-------------|
| `ActivePipeline` | `DA_RenderPipelineProfile` | `None` | `State` | Currently active profile |
| `ActiveState` | `SActivePipelineState` | — | `State` | Full current pipeline state |
| `DetectedPlatform` | `EPlatformFamily` | `PC_High` | `State` | Auto-detected platform |
| `bIsInitialized` | `bool` | `false` | `State` | Whether Initialize completed |
| `CachedPlayerController` | `APlayerController` | `None` | `Cache` | Owner PC reference |
| `bReloadNeededOnNextLevel` | `bool` | `false` | `State` | Reload flag for next level load |
---
## 4. Functions
### Public Functions
#### `Initialize` → `void`
- **Description:** Detects platform, loads the appropriate `DA_RenderPipelineProfile`, reads saved quality from `SS_SettingsSystem`, and applies initial pipeline settings.
- **Flow:**
1. Get Owner → Cast to PlayerController → cache
2. Bind to `GI_GameFramework.GetSubsystem(BPC_PlatformServiceAbstraction).OnPlatformReady`
3. In handler: read `PlatformService.GetPlatformFamily()` → set `DetectedPlatform`
3. Lookup `DA_RenderPipelineProfile` from `PlatformProfileMap` by `DetectedPlatform`
4. If not found: log error, fallback to `PC_High` profile
5. Read `SS_SettingsSystem.GetSettingFloat("QualityPreset")` → resolve preset name
6. If no saved setting: use `DefaultQualityPreset`
7. Call `ApplyQualityPreset(PresetName)`
8. Bind to `SS_SettingsSystem.OnSettingChanged` → listen for quality changes
9. Bind to `GI_GameFramework.OnGamePhaseChanged` → apply pending reloads on level change
10. Set `bIsInitialized = true`
11. Broadcast `OnPipelineManagerInitialized`
#### `ApplyQualityPreset` → `ERenderPipelineChangeType`
- **Description:** Apply a quality preset by name. Returns whether a level reload is needed.
- **Parameters:**
| Param | Type | Description |
|-------|------|-------------|
| `PresetName` | `FName` | Quality preset to apply ("Low", "Medium", "High", "Ultra", "Cinematic") |
| `bForceApply` | `bool` | Force re-apply even if already active |
- **Flow:**
1. Validate `bIsInitialized` and `ActivePipeline` is valid
2. Lookup `SRenderPipelineConfig` from `ActivePipeline.QualityPresets[PresetName]`
3. If not found: log error, return
4. Compare with `ActiveState.AppliedConfig`:
- If identical and not `bForceApply`: return `NonDestructive`
5. Determine change type:
- If `GIMethod`, `ShadowMethod`, or `MeshStrategy` changed → `RequiresLevelReload`
- Otherwise → `NonDestructive`
6. If `RequiresLevelReload` and game is mid-session:
- Store as `PendingPreset`, set `bReloadNeededOnNextLevel = true`
- Show UI notification "Settings will apply on next level load"
- Broadcast `OnPipelineReloadRequired`
- Return `RequiresLevelReload`
7. If safe to apply now: call `ApplyRenderConfig(PresetName, Config)`
8. Save to `SS_SettingsSystem.SetSetting("QualityPreset", PresetName)`
9. Broadcast `OnRenderPipelineChanged(PresetName, ChangeType)`
10. Return change type
#### `ApplyRenderConfig` → `void`
- **Description:** Executes console commands to apply a render configuration immediately.
- **Parameters:**
| Param | Type | Description |
|-------|------|-------------|
| `PresetName` | `FName` | Preset being applied |
| `Config` | `SRenderPipelineConfig` | Configuration to apply |
- **Flow:**
1. Execute console commands for GI method:
```
ExecuteConsoleCommand("r.DynamicGlobalIlluminationMethod {GIMethod}")
ExecuteConsoleCommand("r.Lumen.DiffuseIndirect.Allow {LumenAllowed}")
```
2. Execute shadow commands:
```
ExecuteConsoleCommand("r.Shadow.Virtual.Enable {VSMEnabled}")
ExecuteConsoleCommand("sg.ShadowQuality {ShadowQuality}")
```
3. Execute upscaling commands:
```
ExecuteConsoleCommand("r.ScreenPercentage {ResolutionScale*100}")
ExecuteConsoleCommand("r.TemporalAA.Upsampling {TSREnabled}")
// DLSS/FSR plugin-specific commands
```
4. Execute mesh/Nanite commands:
```
ExecuteConsoleCommand("r.Nanite {NaniteEnabled}")
ExecuteConsoleCommand("r.StaticMeshLODDistanceScale {LODScale}")
```
5. Execute scalability groups:
```
ExecuteConsoleCommand("sg.TextureQuality {TextureQuality}")
ExecuteConsoleCommand("sg.PostProcessQuality {PostProcessQuality}")
ExecuteConsoleCommand("sg.ViewDistanceQuality {ViewDistanceQuality}")
ExecuteConsoleCommand("sg.FoliageQuality {FoliageQuality}")
ExecuteConsoleCommand("sg.AntiAliasingQuality {AntiAliasingQuality}")
```
6. Execute post-process toggles:
```
ExecuteConsoleCommand("r.MotionBlurQuality {MotionBlur}")
ExecuteConsoleCommand("r.DepthOfFieldQuality {DoF}")
```
7. Execute platform-specific overrides from `SPlatformRenderDefaults.ConsoleVariables`
8. Set `ActiveState.AppliedConfig = Config`
9. Set `ActiveState.ActivePreset = PresetName`
10. Set `bReloadNeededOnNextLevel = false`
#### `ApplyPendingReload` → `void`
- **Description:** Called on level transition or game restart. Applies queued pipeline changes.
- **Flow:**
1. If `bReloadNeededOnNextLevel == false`: return
2. Get `SRenderPipelineConfig` for `ActiveState.PendingPreset`
3. Call `ApplyRenderConfig(PendingPreset, Config)`
4. Broadcast `OnRenderPipelineChanged(PendingPreset, RequiresLevelReload)`
#### `DetectPlatform` → `EPlatformFamily` *(deprecated — use BPC_PlatformServiceAbstraction.GetPlatformFamily() instead)*
- **Description:** Auto-detect the current platform and GPU capability.
- **Flow:**
1. Check `UGameplayStatics::GetPlatformName()`:
- "PS5" → detect if Pro → `PS5_Pro` or `PS5`
- "PS4" → `PS4`
- "XboxOne" → `Xbox_One`
- "XSX" or "XboxSeries" → `Xbox_Series`
- "Switch" → `Switch`
- "Win64" → check GPU:
```cpp
IPlatformFile& PlatformFile = FPlatformFileManager::GetPlatformFile();
// Check GPU name via GRHIAdapterName
// RTX 2000+ / RX 6000+ → PC_High
// GTX 9001600 / iGPU → PC_Low
```
2. Store result in `DetectedPlatform`
3. Return platform
#### `SetUpscalerMethod` → `void`
- **Description:** Override the upscaler independently of quality preset.
- **Parameters:** `Method` (EUpscalerMethod), `Quality` (int32)
- **Flow:** Updates only the upscaler CVars without touching other settings.
#### `GetActiveUpscaler` → `EUpscalerMethod`
- **Description:** Returns the currently active upscaler. Read-only.
#### `GetActivePipelineConfig` → `SRenderPipelineConfig`
- **Description:** Returns a copy of the currently applied render config. Read-only.
#### `IsNaniteEnabled` → `bool`
- **Description:** Quick check if Nanite is currently active.
#### `IsLumenEnabled` → `bool`
- **Description:** Quick check if Lumen GI is currently active.
#### `RequiresReloadForPresetChange` → `bool`
- **Description:** Check if switching from current preset to a new one would require a level reload.
- **Parameters:** `NewPresetName` (FName)
#### `GetAvailableUpscalers` → `TArray<EUpscalerMethod>`
- **Description:** Returns which upscalers are available (based on installed plugins + platform).
#### `SetDynamicResolutionTarget` → `void`
- **Description:** Enable/disable dynamic resolution scaling.
- **Parameters:** `TargetFrameTimeMs` (float), `bEnabled` (bool)
---
## 5. Event Dispatchers
| Dispatcher | Parameters | Bind Access | Description |
|------------|-----------|-------------|-------------|
| `OnPipelineManagerInitialized` | — | `Public` | Fired after Initialize completes |
| `OnRenderPipelineChanged` | `FName PresetName`, `ERenderPipelineChangeType ChangeType` | `Public` | Fired when pipeline configuration changes |
| `OnPipelineReloadRequired` | `FName PendingPreset` | `Public` | Fired when a change requires level reload — UI shows warning |
| `OnPipelineApplied` | `FName PresetName` | `Public` | Fired after all CVars have been executed |
| `OnUpscalerChanged` | `EUpscalerMethod NewMethod`, `int32 Quality` | `Public` | Fired when upscaler is changed |
| `OnPlatformDetected` | `EPlatformFamily Platform` | `Public` | Fired after platform detection |
---
## 6. Overridden Events
### Event: `BeginPlay`
- **Description:** Startup. Calls `Initialize()` then conditionally applies pipeline.
- **Flow:**
1. Call `Initialize()`
2. If `bApplyPipelineOnBeginPlay`: apply the default/saved preset
3. If game phase is `MainMenu` (not in-game): pipeline can be applied immediately (no reload warning needed)
### Event: `OnGamePhaseChanged` (bound to `GI_GameFramework`)
- **Description:** When transitioning from MainMenu to InGame or between levels, apply any pending pipeline changes.
- **Flow:**
1. If `bReloadNeededOnNextLevel`: call `ApplyPendingReload()`
2. Notify `SS_PlanarCaptureManager` of new pipeline state
---
## 7. Blueprint Graph Logic Flow
```mermaid
flowchart TD
A[BeginPlay / GI_GameFramework.OnGamePhaseChanged] --> B[Initialize]
B --> C[DetectPlatform]
C --> D[Load DA_RenderPipelineProfile for platform]
D --> E[Read saved QualityPreset from SS_SettingsSystem]
E --> F[ApplyQualityPreset PresetName]
G[User changes quality in settings] --> H[WBP_SettingsMenu → SS_SettingsSystem.SetSetting]
H --> I[SS_SettingsSystem.OnSettingChanged → BPC_RenderPipelineManager]
I --> J[ApplyQualityPreset NewPresetName]
J --> K{NonDestructive?}
K -->|Yes| L[ApplyRenderConfig immediately]
K -->|No - Needs Reload| M[Store as PendingPreset]
M --> N[Show UI Warning: 'Settings apply on next level']
M --> O[Broadcast OnPipelineReloadRequired]
L --> P[Broadcast OnRenderPipelineChanged]
Q[Level Transition / GamePhase change] --> R{Reload pending?}
R -->|Yes| S[ApplyPendingReload]
S --> T[ApplyRenderConfig with pending preset]
T --> P
P --> U[Notify SS_PlanarCaptureManager]
U --> V[PlanarCapture: adjust GlobalQualityCap]
```
---
## 8. Communication Matrix
| Who Talks | How | What Is Sent |
|-----------|-----|-------------|
| `WBP_SettingsMenu` | `Indirect (SS_SettingsSystem)` | User selects quality preset → `SetSetting("QualityPreset", "High")` |
| `SS_SettingsSystem` | `Dispatcher` | `OnSettingChanged("QualityPreset")` → `BPC_RenderPipelineManager.ApplyQualityPreset()` |
| `BPC_PerformanceScaler` | `Function Call` | `BPC_RenderPipelineManager.ApplyQualityPreset()` for adaptive scaling |
| `BPC_RenderPipelineManager` | `Dispatcher` | `OnRenderPipelineChanged(PresetName)` → `SS_PlanarCaptureManager.AdjustGlobalQualityCap()` |
| `BPC_RenderPipelineManager` | `Dispatcher` | `OnPipelineReloadRequired(PendingPreset)` → `WBP_SettingsMenu.ShowReloadWarning()` |
| `SS_PlanarCaptureManager` | `Function Call` | `BPC_RenderPipelineManager.IsLumenEnabled()` — adjusts capture Lumen budget |
| `WBP_NotificationToast` | `Dispatcher` | `OnPipelineReloadRequired` → show "Settings will apply on next area" toast |
| `BPC_AudioAtmosphereController` | `Dispatcher` | `OnRenderPipelineChanged` → adjust audio quality budget |
---
## 9. Integration with Planar Capture System
When `BPC_RenderPipelineManager` applies a render pipeline change:
1. If Lumen is **disabled** (baked lightmass mode):
- `SS_PlanarCaptureManager` auto-caps `GlobalQualityCap` to `Medium` (no Hero tier Lumen captures)
- All `BPC_PlanarCapture` instances set `bEnableLumen = false` on their quality profiles
- Capture frame budget is increased (no Lumen overhead → can run more captures at higher FPS)
2. If Nanite is **disabled** (traditional LOD mode):
- Captures render faster (no Nanite rasterization overhead in capture pass)
- `SS_PlanarCaptureManager` can increase `GlobalQualityCap` by one tier
3. If upscaler is **active** (DLSS/FSR/TSR):
- Main view renders at lower internal resolution
- Planar captures render at configured quality tier resolution (independent of upscaler)
4. `BPC_PlanarCapture.ApplyQualityTier()` checks `BPC_RenderPipelineManager.IsLumenEnabled()`:
```
If !IsLumenEnabled() AND profile.bEnableLumen:
Force bEnableLumen = false // Prevent capture from trying to enable Lumen
```
---
## 10. Validation / Testing Checklist
- [ ] `DetectPlatform` correctly identifies PS5, PS4, Xbox Series, Xbox One, PC_High, PC_Low
- [ ] `ApplyQualityPreset("Low")` sets GI to None/Baked, switches to CSM, disables Nanite
- [ ] `ApplyQualityPreset("High")` sets GI to Lumen, switches to VSM, enables Nanite
- [ ] Switching from High→Low mid-game shows "requires level reload" warning
- [ ] Switching from High→Low in Main Menu applies immediately (no warning needed)
- [ ] Switching Low→High mid-game: PendingPreset stored, applies on next level load
- [ ] Planar capture budget adjusts: lower cap when Lumen is on, higher when off
- [ ] DLSS is available on PC_High with NVIDIA GPU — shows in `GetAvailableUpscalers()`
- [ ] FSR is available on all platforms — always in available list
- [ ] PSSR only available when `DetectedPlatform == PS5_Pro`
- [ ] NIS is available on Switch — fallback on non-NVIDIA PC
- [ ] `SetUpscalerMethod` independently changes upscaler without affecting other settings
- [ ] `GetActivePipelineConfig()` returns current state accurately after apply
- [ ] Edge case: `PlatformProfileMap` missing platform → logs error, falls back to `PC_High`
- [ ] Edge case: Invalid preset name → logs warning, no CVars changed
- [ ] Edge case: Console variable execution fails → logs error per failed CVar, continues
- [ ] Edge case: Hot-swap GPU (eGPU) → `DetectPlatform` called again, pipeline may change
---
## 11. Manual Implementation Guide
### 11.1 Class Setup
1. Create Blueprint Class: parent `ActorComponent`, name `BPC_RenderPipelineManager`
2. Path: `Content/Framework/Settings/`
3. Add all variables, enums, structs, and event dispatchers from this spec
### 11.2 Key UE5 Nodes
| Node | Where to Find | Used For |
|------|---------------|----------|
| `Execute Console Command` | Right-click → "Execute Console Command" | Applying all CVar changes |
| `Get Platform Name` | Right-click → "Get Platform Name" | Platform detection |
| `Get Game User Settings` | Right-click → "Get Game User Settings" | Accessing scalability API |
| `Set Overall Scalability Level` | On GameUserSettings | Bulk quality tier application |
| `Get Game Instance` → `Get Subsystem(SS_SettingsSystem)` | Subsystem access | Reading/writing quality setting |
| `Get Game Instance` → `Get Subsystem(SS_PlanarCaptureManager)` | Subsystem access | Adjust capture budget |
| `Switch on EPlatformFamily` | Right-click → "Switch" | Platform-specific logic |
| `Make SRenderPipelineConfig` | Right-click → "Make Struct" | Building config structs |
### 11.3 Node-by-Node: ApplyRenderConfig
```
[Function: ApplyRenderConfig(PresetName, Config)]
Step 1: Get Owner → Cast to PlayerController → cache
Step 2: Break SRenderPipelineConfig → get all fields
Step 3: Switch on Config.GIMethod:
Lumen_GI → ExecuteConsoleCommand("r.DynamicGlobalIlluminationMethod 1")
ExecuteConsoleCommand("r.Lumen.Reflections.Allow 1")
Baked_Lightmass → ExecuteConsoleCommand("r.DynamicGlobalIlluminationMethod 0")
ExecuteConsoleCommand("r.Lumen.Reflections.Allow 0")
SSGI → ExecuteConsoleCommand("r.DynamicGlobalIlluminationMethod 2")
None → ExecuteConsoleCommand("r.DynamicGlobalIlluminationMethod 0")
Step 4: ExecuteConsoleCommand("r.Shadow.Virtual.Enable {VSM?1:0}")
Step 5: ExecuteConsoleCommand("sg.ShadowQuality {ShadowQuality}")
Step 6: ExecuteConsoleCommand("r.ScreenPercentage {ResolutionScale*100}")
Step 7: Switch on Config.Upscaler:
TSR → ExecuteConsoleCommand("r.TemporalAA.Upsampling 1")
DLSS → ExecuteConsoleCommand("r.NGX.DLSS.Enable 1")
ExecuteConsoleCommand("r.NGX.DLSS.Quality {UpscalerQuality}")
FSR → ExecuteConsoleCommand("r.FidelityFX.FSR2.Enabled 1")
NIS → ExecuteConsoleCommand("r.NIS.Enable 1")
Step 8: ExecuteConsoleCommand("r.Nanite {NaniteEnabled?1:0}")
Step 9: Execute Console Command "sg.TextureQuality {TextureQuality}"
Execute Console Command "sg.PostProcessQuality {PostProcessQuality}"
Execute Console Command "sg.ViewDistanceQuality {ViewDistanceQuality}"
Execute Console Command "sg.FoliageQuality {FoliageQuality}"
Step 10: ExecuteConsoleCommand("r.MotionBlurQuality {MotionBlur?2:0}")
Step 11: ExecuteConsoleCommand("r.DepthOfFieldQuality {DoF?2:0}")
Step 12: Set ActiveState.AppliedConfig = Config
Step 13: Set ActiveState.ActivePreset = PresetName
Step 14: Set bReloadNeededOnNextLevel = false
Step 15: Broadcast OnPipelineApplied(PresetName)
```
### 11.4 Networking
- **Local client only.** Render pipeline is per-client hardware. No replication.
- On listen servers: the server-host player runs their own render pipeline; other clients run theirs.
---
## 12. Blueprint Build Checklist
- [ ] Create Blueprint class: `BPC_RenderPipelineManager` (parent: `ActorComponent`)
- [ ] Add enums: `ERenderPipelineMethod`, `EShadowMethod`, `EReflectionMethod`, `EUpscalerMethod`, `EMeshStrategy`, `EPlatformFamily`, `ERenderPipelineChangeType`
- [ ] Add structs: `SRenderPipelineConfig`, `SPlatformRenderDefaults`, `SActivePipelineState`
- [ ] Add all variables from Section 3
- [ ] Build `BeginPlay` → `Initialize` chain
- [ ] Implement `DetectPlatform` with Switch on Platform Name
- [ ] Implement `ApplyQualityPreset` with reload detection logic
- [ ] Implement `ApplyRenderConfig` with full CVar execution
- [ ] Implement `ApplyPendingReload` bound to `GI_GameFramework.OnGamePhaseChanged`
- [ ] Implement `SetUpscalerMethod` / `GetAvailableUpscalers` / `IsLumenEnabled` / `IsNaniteEnabled`
- [ ] Create all 6 event dispatchers
- [ ] Bind to `SS_SettingsSystem.OnSettingChanged` (QualityPreset key)
- [ ] Bind to `GI_GameFramework.OnGamePhaseChanged`
- [ ] Create at least one `DA_RenderPipelineProfile` instance per target platform
- [ ] Test: Low preset disables Lumen, Nanite, switches to CSM
- [ ] Test: High preset enables Lumen, Nanite, VSM
- [ ] Test: Mid-session pipeline switch shows reload warning
- [ ] Test: Main Menu pipeline switch applies immediately
- [ ] Test: Planar capture quality cap adjusts on pipeline change
---
*Blueprint Spec: Render Pipeline Manager. Conforms to TEMPLATE.md v2.0 — part of the UE5 Modular Game Framework, SETTINGS layer.*