# 149 — Render Pipeline Manager (`BPC_RenderPipelineManager`) > **Blueprint-Only Implementation** — UE 5.5–5.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` | `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 900–1600 / 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` - **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.*