A functional core OSS version is available on GitHub: https://github.com/gregorik/PSO-Autopilot. The present Fab version is fully featured, including all USP's described in the manual.
PSO Autopilot is a reliable, production-ready solution to Unreal Engine 5's notorious shader compilation stutter and lag.
The "Shader Compilation" plugins on various marketplaces attempt to solve stutter by brute-forcing the Engine: loading all of your project's assets into memory simultaneously. If you are building a massive 50GB open-world game, this simplistic approach guarantees catastrophic RAM spikes, completely frozen loading screens, and eventually, Out-Of-Memory (OOM) crashes on consoles or lower-end PCs.
PSO Autopilot is different:it was created from the ground up to orchestrate Unreal's native hardware pipeline asynchronously, managing memory, threads, and driver caches with architectural precision.
Core functionality deep-dive:
Memory-Safe Chunking: The plugin uses FStreamableManager to asynchronously load constrained batches of assets (default 100). It unloads them and explicitly forces Garbage Collection (GEngine->ForceGarbageCollection) between batches, which effectively mitigates Out-Of-Memory (OOM) crashes on large projects.
Time-Sliced Game Thread Yielding: The ProcessBatchTimeSliced() function respects the MaxProcessingTimeMsPerFrame budget (default 5ms), breaking up the workload and yielding the Game Thread to keep UI animations smooth.
Smart Cache Skipping: The plugin generates an MD5 fingerprint to bypass redundant warmups on subsequent boots by reading/writing to GGameUserSettingsIni.
Engine PSO Pacing: It checks FShaderPipelineCache::NumPrecompilesRemaining() to ensure it doesn't overrun the engine's built-in background shader compilation queue.
Empty FName poisoning in asset queries - An empty DirectoriesToScan entry (Path="") in DefaultGame.ini silently injected an empty FName into FARFilter::PackagePaths, causing GetAssets() to return zero results with no error. Added an IsEmpty() guard in ScanForAssets() and removed the poisoned config line.
Asset registry race condition in -game mode - IsLoadingAssets() returned false before the background asset gatherer had even started, producing an empty scan. Added Registry.SearchAllAssets(true) to force synchronous registry population before querying.
Non-material assets silently skipped - ForceAssetWarmup() only handled UMaterialInterface directly. Meshes, particle systems, and other assets referencing materials were ignored. Replaced the raw property scan with FReferenceFinder to extract all referenced UMaterialInterface objects from any loaded asset.
BootLoader delegate leak - APSOAutopilotBootLoader had no EndPlay override. Subsystem delegates were never unbound on actor destruction, leading to dangling callbacks and potential crashes on level transitions. Added proper delegate unbinding in EndPlay.
High-Priority Fixes
Dead code removal - Removed ~50 lines of unreachable BuildWarmupFingerprint() declaration and implementation that had been superseded by the async thread-pool fingerprint computation.
Missing StopWarmup() method - There was no way to cancel an in-progress warmup. Added StopWarmup() as a BlueprintCallable method that resets the state machine, releases loaded assets, and broadcasts completion.
Fragile material extraction via raw property iteration - The original ForceAssetWarmup walked UObject properties manually to find material references, which was brittle across engine versions. Replaced with the engine's FReferenceFinder utility for robust, recursive reference discovery.
Untracked forced-streaming textures - Textures forced into VRAM via SetForceMipLevelsToBeResident() were never tracked. Added ForcedStreamingTextures tracking set so residency can be refreshed or released post-warmup (60-second extended residency applied on completion).
Medium-Priority Fixes
Fingerprint future timeout - The async fingerprint computation on the thread pool had no timeout guard. If the thread pool stalled, the subsystem would hang indefinitely in the Fingerprinting state. Added a 10-second timeout with fallback to force a full warmup on timeout.
CDO settings mutations stacking across PIE runs - APSOAutopilotDemoManager mutated the settings CDO (Class Default Object) directly without calling ReloadConfig() first. Settings overrides from previous runs accumulated. Added ReloadConfig() before applying overrides to start from clean defaults each run.
Custom HUDClass ignored during forced validation - When bForceValidationRun was true, the demo manager always spawned the default widget, ignoring any user-specified HUDClass. Fixed to respect the configured HUD class in all code paths.
StartWarmup re-entry from Finished state - The state guard only checked CurrentState != EPSOWarmupState::Idle, but a completed warmup left the state at Finished. Calling StartWarmup() again was silently rejected. Updated the guard to allow restart from both Idle and Finished states.
Deprecated GetUsedTextures 5-parameter API - UE 5.7 changed the GetUsedTextures signature, producing compiler warnings. Updated to the current 4-parameter overload.
Low-Priority Fixes
CanContainContent: true with empty Content directory - The .uplugin declared CanContainContent: true but the plugin ships no content assets. Set to false to avoid Fab validator warnings and unnecessary Content directory expectations.
Virtual Texture preheat incomplete - UVirtualTexture2D assets were discovered but UpdateResource() was never called during the streaming phase. Added explicit UpdateResource() calls and tracking via PendingVirtualTextures set to properly page virtual textures into GPU memory.
Flat spinner widget appearance - The spinner used WhiteSquareTexture which looked flat and dated. Replaced with a gradient-based circular spinner with rotation animation and pulsing opacity for a polished loading indicator.
Redundant BuildFallbackWidgetTree() calls - Both NativeConstruct implementations (DemoWidget and LoadingScreenWidget) called the fallback widget builder unconditionally, even when UMG bindings were present. Added checks to only build the C++ Slate tree when no designer-bound widgets exist.
Streaming telemetry message missing resource count - The "Waiting for texture streaming..." log message didn't include how many resources were pending. Added GetNumWantingResources() count to the status broadcast for better diagnostics.
Architecture
State machine: 10-state EPSOWarmupState enum drives all subsystem behavior through Tick().
Module loading phase: PreDefault to ensure availability before gameplay subsystems.
Async fingerprint: MD5 hash computed on thread pool (engine version + scan directories + asset paths + timestamps).
Replaced shader-cache-only warming with real PSO precache paths for warmed assets.
Added asset-specific PSO precache for primitive assets through transient static mesh and skeletal mesh components.
Added explicit material PrecachePSOs(...) fallback with local, nanite, and particle vertex factory coverage.
Switched batch completion to plugin-owned PSO request and graph-event tracking instead of waiting on the global engine precompile backlog.
Logged async load failures, counted them against the run, and blocked fingerprint persistence after any warmup failure.
Sanitized scan settings before use so blank or duplicate directories and invalid class paths cannot collapse asset discovery.
Waited for pending virtual texture initialization and streaming before marking a batch complete.
Restored mutable settings after demo and boot overrides so changes do not leak across warmup runs.
Changed
Warmup execution now uses a per-run settings snapshot instead of rereading mutable defaults mid-run.
Added the module dependencies required by the new PSO precache path.
Synced the same source changes into Plugins/PSOAutopilot, PluginBuild, and PluginStaging.
Texture streaming infinite hang β The StreamingTextures state had no timeout, so a single texture that never finished streaming could stall the entire warmup indefinitely. Added a 30-second timeout that logs a warning and advances to the next batch.
Batch load stuck-state recovery β FStreamableManager::RequestAsyncLoad could silently never complete if an asset was corrupted or had cascading dependency issues. Added a 60-second watchdog timer in the LoadingBatch state. On timeout, the pending load is cancelled, the batch is marked as failed, and processing continues with the next batch. BatchLoadStartTime tracking added to Initialize(), StartWarmup(), and BeginLoadingBatch().
Texture mip residency too short β SetForceMipLevelsToBeResident() was called with a 30-second duration, but large warmups could take several minutes. Textures loaded in early batches would drop their high-res mips before the loading screen finished. Increased to 300 seconds (5 minutes).
SearchAllAssets hitching the Editor β The synchronous Registry.SearchAllAssets(true) call (added in v1.1 for -game mode) was also running in Editor builds where it caused a multi-second hitch. Wrapped in if (!GIsEditor) guard so it only runs in standalone game mode where the async gatherer hasn't started yet.
Multi-fingerprint cache β The fingerprint system only stored a single LastCompletedFingerprint value, meaning different scan configurations (e.g., multiple Boot Loaders scanning different directories) would overwrite each other. Replaced with an LRU cache storing up to 8 completed fingerprints as a comma-separated CompletedFingerprints key. Legacy single-entry format is migrated automatically on first read.
Incomplete GC purge between batches β ForceGarbageCollection(false) only collected unreachable objects but didn't purge them from memory. Changed to ForceGarbageCollection(true) for a full purge, ensuring batch memory is actually reclaimed before the next load.
Transient mesh components parented to wrong outer β UStaticMeshComponent objects created for PSO precaching were parented to the subsystem's outer (the GameInstance), which prevented them from being garbage collected between batches. Changed to NewObject<UStaticMeshComponent>(GetTransientPackage()) so they live in the transient package and are properly cleaned up.
Boot Loader level transition crash β UGameplayStatics::OpenLevel() was called without validating that the target level exists. On a typo or missing map, this could crash or produce cryptic errors. Added FPackageName::SearchForPackageOnDisk() validation with a warning log if the level is not found.
Dead Outer parameter β After fixing transient components to use GetTransientPackage(), the UObject* Outer parameter on QueuePrimitiveAssetPSOPrecache() became unused dead code. Removed from both the function signature and all call sites.
Demo widget telemetry panel cut off β The bottom-left telemetry panel in UPSOAutopilotDemoWidget was sized too small (640x280) and positioned where it clipped off-screen. Enlarged to 920x420 and repositioned from (36,-36) to (24,-24).
Demo widget text overflow β Long status messages, cache comparison text, and metrics text overflowed the panel bounds. Added SetAutoWrapText(true) on MetricsText, CacheStatusText, and ComparisonSummaryText.
Documentation
Comprehensive manual rewrite β Expanded Docs/index.html from 595 lines to ~850 lines:
New State Machine section with visual diagram and per-state descriptions
New Fingerprint & Caching section covering MD5 hashing, editor timestamps, and multi-configuration LRU cache
New Best Practices section (targeting, batch tuning, dedicated loading levels, standalone testing)
Full Boot Loader property reference table
Configuration section rewritten with HTML tables organized by category (Targeting, Memory, Performance, Visual Continuity, Validation)
Troubleshooting FAQ expanded from 3 items to 22 items across 8 categories:
PSO Autopilot now covers more real startup scenarios by discovering warmup content from explicit roots, runtime-queued assets, and package dependencies instead of relying only on a basic directory scan.
Gameplay packages can now preload before OnWarmupComplete, which shifts more first-run cost under the loading screen instead of into the first seconds of gameplay.
The packaged demo flow now works end to end, including boot-level warmup, gameplay map preload, map handoff, and duplicate-warmup suppression on the destination map.
New in This Release
Added ExplicitAssetsToWarm for maps, blueprints, labels, and other startup-critical assets that are not guaranteed to be found through directory scanning alone.
Added PackagesToPreload so destination gameplay packages can load before the warmup sequence completes.
Added runtime APIs for dynamic startup flows:
RegisterRuntimeWarmupAsset(...)
QueuePackagePreload(...)
QueuePackagePreloadByName(...)
Added dependency-expansion controls:
bExpandPackageDependencies
DependencyExpansionDepth
bIncludeSoftPackageReferences
bIncludeManagementReferences
Added a PreloadingPackages warmup state, retained-package tracking, and preload telemetry so package loading is treated as a first-class part of startup warmup.
Added boot-loader controls for destination-level warmup seeding and package preload:
bRegisterLevelAsWarmupSeed
ExplicitWarmupAssetsOnBeginPlay
PackagesToPreloadOnBeginPlay
bPreloadLevelPackagesBeforeOpen
Improvements
Replaced the blocking SearchAllAssets(true) path with a non-blocking asset-registry discovery flow that starts the search once and polls until the registry is ready.
Expanded warmup coverage beyond direct material assets to include worlds, loaded level actors, blueprint-generated classes, class default objects, default subobjects, simple construction script templates, inheritable component handler templates, and primitive components found through referenced objects.
Updated warmup fingerprinting to hash the final discovered asset set and dependency-expansion settings, so instant-skip decisions reflect the actual startup workload instead of just the initial scan input.
Updated warmup completion rules so package preload is part of the startup pipeline, and preload failures block fingerprint persistence the same way asset load failures already do.
Updated the demo boot flow to pass PSOAutopilotWarmupCompleted=1 during travel so the gameplay map can distinguish a boot-flow handoff from a direct launch.
Updated the demo widget to initialize from the subsystem's current state after travel instead of assuming every map load starts a fresh warmup pass.
Fixes
Fixed packaged demo boot flow when LevelToOpenOnComplete is unset by falling back to /Game/Demo/Demo_PSOTorture instead of stalling at the boot map.
Fixed packaged map travel after preload by releasing retained preloaded packages before OpenLevel(...).
Fixed packaged demo content availability by always cooking /Game/DemoMaterials and by updating the demo-generation script to write the gameplay destination instead of None.
Fixed cooked-runtime warmup discovery fallback so packaged builds can still assemble the intended warmup set when registry-backed results are sparse.
Fixed material PSO teardown during garbage collection by removing the plugin-side double release of engine-owned PSO precache request IDs.
Fixed the demo gameplay map starting a second warmup after a successful boot warmup by honoring the boot-flow handoff option in the gameplay manager.
Documentation
Updated Plugins/PSOAutopilot/Docs/README.md to better explain real-project startup coverage, first-run cost shifting, the recommended boot-level flow, and destination-level package preload.
Packaging and Validation
Synced the same source and documentation changes into Plugins/PSOAutopilot, PluginBuild, and PluginStaging.
Build.bat UnrealEditor Win64 Development -Project=PSOAutopilot.uproject -NoUBA succeeded.
BuildPlugin validation passed for UnrealEditor Win64 Development, UnrealGame Win64 Development, and UnrealGame Win64 Shipping.
A live packaged gameplay smoke test passed in Intermediate/PackagedSmokeTest7:
boot warmup discovered 1001 assets
/Game/Demo/Demo_PSOTorture was preloaded during warmup
the retained preload was released before map travel
the gameplay map opened with ?PSOAutopilotWarmupCompleted=1
the gameplay map loaded successfully and skipped the duplicate warmup
Notes
This release moves substantially more startup work into a controlled loading phase, but projects still need to register representative warmup roots for content that is created or selected only at runtime.
Package preloading improves first-run smoothness, but it can increase memory pressure during startup compared with PSO-only warmup.
Added a Startup Setup wizard so first-time users can configure PSO Autopilot from a guided editor tab instead of assembling the startup flow manually.
Added Simple Setup presets that translate approachable choices into the existing advanced startup preparation, content loading, referenced-content, and frame-budget settings.
Added a preflight validator with green/yellow/red setup results and one-click fix actions for common onboarding and packaging problems.
Added the ability to explicitly exclude specific object types (like Blueprints or LevelSequences) from the asset scan, preventing Editor-level crashes during heavy bulk-loads.
Added ClassesToExcludeFromScan to UPSOAutopilotSettings to allow developers to safely bypass problematic asset types during directory scanning.
Child classes of any excluded class are also automatically excluded via dynamic GetDerivedClassNames lookup.
New in This Release
Added a new PSOAutopilotEditor module and registered Window > PSO Autopilot Startup Setup.
Added Simple Setup settings:
SetupMode
SimplePreset
SimpleGameplayLevel
SimpleLoadingWidgetClass
SimpleContentFolders
bSimpleEnableSmartSkip
bSimpleEnableTexturePreheat
Added three Simple Mode presets:
Safe Startup for low-memory, low-hitch startup behavior.
Balanced as the recommended default.
Fast Warmup for smaller projects that can spend more frame time and memory during loading.
Added setup validation for:
game level selection and disk existence,
game level cook coverage,
loading screen validity,
active scan-folder validity,
explicit warmup asset validity,
package preload path validity,
batch-size sanity,
risky ignored content types,
recommended packaged content,
smart cache skip,
loading level selection and package existence,
startup-loader presence in the generated loading level,
project startup level pointing at the loading level,
loading level versus game level mismatch.
Added automatic fix actions:
apply Simple Setup,
use the built-in loading screen,
add /Game as default content to prepare,
remove invalid content and preload entries,
apply the Balanced preset for unreasonable batch size,
clear risky ignored content types from advanced exclusions,
create or open the default loading level,
include recommended content in packaging,
enable smart cache skip,
set the project startup level,
create or update a PSO Autopilot Loader actor.
Added a one-node Blueprint entry point, Start PSO Autopilot Loading Flow, plus FPSOAutopilotSimpleOptions for the recommended gameplay-level warmup, loading widget, package preload, cleanup, and level-travel sequence.
Added friendly loading progress messages and a separate GetTechnicalStatusMessage() telemetry path for advanced/debug loading panels.
Improvements
Simple Setup now feeds the runtime startup-preparation snapshot directly, so the beginner configuration path uses the same production subsystem as Advanced Mode.
Simple Setup now exposes only game level, loading screen, content to prepare, quality preset, smart skip, and texture preparation; low-level tuning remains hidden until Advanced mode is selected.
Presets now ship buyer-ready tuning decisions: Safe Startup, Balanced, and Fast Warmup.
The smart-skip path is now configurable through the Simple Setup toggle or the Advanced bEnableSmartCacheSkip setting.
Added APSOAutopilotBootLoader::ConfigureForSimpleSetup(...) so editor tooling and Blueprint utility code can configure the startup flow without direct property access.
Updated the demo project defaults to use Simple Setup with /Game/Demo/Demo_PSOTorture and /Game/DemoMaterials.
Reframed the bundled sample as a marketplace demo: boot map, loading screen, gameplay map, cold-vs-cached toggle, compact telemetry overlay, and a clear Warmup skipped because cache matched. cache-hit message.
Set the demo projectβs default startup map to /Game/Demo/Demo_BootFlow and added the demo maps/content folders to packaging settings.
Updated the README with a 5-minute beginner setup flow focused on the new wizard.
Updated the README and HTML manual with the one-node Blueprint loading-flow path.
Updated the README and HTML manual with packaged-build demo instructions and the 1001-asset packaged smoke-test proof point.
Updated the built-in loading screen so technical details live in an optional telemetry panel instead of the primary player-facing status line.
Renamed user-facing Project Settings, wizard, Blueprint, and built-in loading-screen labels to use buyer-facing startup-preparation language while keeping technical C++ names unchanged.
Improved the Boot Loader Details panel as the main product surface: display name is now PSO Autopilot Loader, the primary setup properties appear first, and overrides/debug/demo controls are grouped behind advanced categories with recommendation-focused tooltips.
Packaging and Validation
Build.bat UnrealEditor Win64 Development -Project=PSOAutopilot.uproject -NoUBA succeeded.
BuildPlugin validation passed for UnrealEditor Win64 Development, UnrealGame Win64 Development, and UnrealGame Win64 Shipping.
Notes
The wizard now uses a hidden default loading level for startup-flow generation, can create/open that level, and points the project startup level at it before creating or updating the startup-loader actor.
The wizard validates setup and compiles successfully, but the UI was not manually clicked through in an interactive editor session during this pass.
Fixed a packaging time regression caused by the Setup Wizard. The wizard no longer recommends adding the entire /Game folder to DirectoriesToAlwaysCook, which previously disabled the engine's reference-based cook optimization and forced cooking of all unreferenced assets.
Added a new HasRootCookDirectory validation check to the Setup Wizard. It now detects if /Game is included in the project's Cook Directories and provides a one-click Remove /Game auto-repair action.
Fixed repeat startup warmups after an Unreal restart by making smart-cache hits skip the asset warmup even when the engine reports a pipeline-cache backlog during fresh process startup.
Fixed stale setup-loader debug flags carrying forward when an existing loader is updated by the wizard. Re-running setup now clears Ignore Previous Runs and demo hold overrides so the loader does not reset the smart-skip fingerprint on every startup.
Smart-cache hits now bypass demo minimum-duration holds, so a matched repeat run completes immediately instead of looking like another warmup.
Fixed the setup wizard's loading screen path so it creates and selects an editable Widget Blueprint at /Game/PSOAutopilot/WBP_PSOAutopilotLoadingScreen instead of silently pointing new startup flows at the native C++ fallback widget.
Added an Editor Iteration Mode setting, defaulting to Use Unreal Editor Compilation, so PIE/editor preview uses Unreal's standard shader compilation path unless the user explicitly switches to Run PSO Autopilot Warmup.
Fixed a use case that occurs when loading a massive amount of complex materials concurrently.
Implemented a background task throttle in the time-sliced batch processing loop. The plugin now caps active PSO precache requests to 250 in-flight tasks and yields the frame, preventing the renderer's uniform buffers from being overwhelmed by unchecked background threads.
Manual (https://gregorigin.com/PSO_Autopilot/) and FAQ (https://gregorigin.com/PSO_Autopilot_FAQ/) are expanded and extended.