Skip to content

PSO System

PlatanoGames edited this page Feb 23, 2026 · 4 revisions

PSO System v2.0 — Eliminating the First 30 Seconds of Hitching

"Every player notices the first 30 seconds. It's where you lose the benefit of the doubt."


The Problem

Modern rendering pipelines are built on Pipeline State Objects — immutable bundles that describe exactly how the GPU should draw a particular material with a particular mesh format through a particular rendering pass. When Unreal Engine encounters a new combination for the first time, it has to compile that PSO on the fly. During compilation, the GPU stalls. The player sees a hitch.

This is not a theoretical concern. It is the single most common source of visible stuttering in the first minute of gameplay. Every new material, every new mesh type, every shadow pass variant that has not been seen before triggers a synchronous pipeline compilation. The frame budget is blown. The player perceives "bad performance" before they have even started playing.

The engine provides a built-in PSO caching system, but it operates reactively. It records what was needed and caches it for next time. The first run is always cold. The developer's options are limited:

  1. Manual precache lists — Tedious, error-prone, and immediately stale when content changes.
  2. Bundled cache files — Requires shipping platform-specific binary blobs that may not match the player's GPU driver version.
  3. Accept the hitching — The default for most shipped titles. Players complain. Reviews mention stuttering.

None of these are satisfactory. The problem is systemic: you need to know which PSOs will be required before they are required, and you need to compile them asynchronously before the player sees the affected geometry.


How PGX Solves It

PGX approaches PSO warm-up as a data-driven pipeline with five key properties:

1. Auto-Discovery from Configuration Assets

Rather than maintaining manual lists, developers create configuration assets that declare which materials and mesh types their game uses. The system discovers these assets automatically at startup through the engine's Asset Registry. No registration code. No initialization boilerplate. Drop the asset in your project, and the system finds it.

2. Deterministic State Machine

The warm-up process follows a strict six-state machine:

  +------+     +----------+     +-----------+     +----------+
  | Idle |---->| Loading  |---->| Compiling |---->| Complete |
  +------+     +----------+     +-----------+     +----------+
                    |                 |
                    |                 +----------> +--------+
                    |                              | Failed |
                    |                              +--------+
                    |                 +----------> +--------+
                    +-----------------+            | Paused |
                                                   +--------+

Every transition is observable. Every state change fires a delegate. There are no hidden intermediate states, no "probably done" heuristics. The system is either compiling or it is not.

3. Context-Based Activation

Not all PSOs need to be warm at all times. A main menu does not need the same shaders as a combat encounter. The system supports four activation modes:

  • On Init — Warm everything at game startup (simplest, highest memory)
  • On Level Load — Warm only what the target level needs
  • On Game Flow Tag — Warm when a specific game state is reached
  • On Explicit Call — Developer triggers warm-up manually (maximum control)

Each configuration asset declares its activation context. The system only compiles what the current context demands.

4. Concurrency Policies

What happens when a second warm-up is requested while one is already running? Three policies handle this:

  • Reject New — Ignore the request. First warm-up wins.
  • Cancel and Restart — Abort in-progress work, start fresh with the new request.
  • Merge and Continue — Add new entries to the current batch without restarting.

The right choice depends on the game. A linear narrative might prefer Reject. An open world with streaming might prefer Merge. The developer chooses per configuration asset.

5. Cache Persistence

Compiled PSO results are cached to disk. Four save policies control when:

  • On Complete Only — Save once when all entries finish
  • Periodic — Save at intervals during compilation
  • On Level Transition — Save before changing levels
  • Manual — Developer controls save timing explicitly

The cache is keyed by material path, vertex factory type, and packed rendering parameters. Duplicate entries are deduplicated before compilation begins.


Architecture (ASCII Diagram)

+------------------------------------------------------------------+
|                     PSO System v2.0                               |
+------------------------------------------------------------------+
|                                                                   |
|  +------------------+    +------------------+    +--------------+ |
|  | Config Discovery |    | Warm-Up Engine   |    | Cache Layer  | |
|  |                  |    |                  |    |              | |
|  | AssetRegistry    |    | State Machine    |    | Dedup Keys   | |
|  | scan at Init()   |--->| 6-state          |--->| Disk Save    | |
|  |                  |    | deterministic    |    | 4 policies   | |
|  | N config assets  |    | batch async      |    |              | |
|  +------------------+    +------------------+    +--------------+ |
|          |                       |                       |        |
|          v                       v                       v        |
|  +------------------+    +------------------+    +--------------+ |
|  | Context System   |    | RHI Pipeline     |    | Recording    | |
|  |                  |    | Submission        |    | (Editor)     | |
|  | 4 activation     |    |                  |    |              | |
|  | modes            |    | Material + VF    |    | Capture real | |
|  | GameplayTag      |    | combinations     |    | PSO needs    | |
|  | driven           |    | to GPU compiler  |    | Export JSON  | |
|  +------------------+    +------------------+    +--------------+ |
|                                                                   |
|  +-------------------------------------------------------------+ |
|  | Delegates (4 native + 6 dynamic)                             | |
|  |                                                               | |
|  | OnWarmUpBegin | OnProgress | OnComplete | OnFailed            | |
|  | OnEntryCompiled | OnRecordingUpdate | OnStateChanged          | |
|  +-------------------------------------------------------------+ |
+------------------------------------------------------------------+

Capabilities

Warm-Up Control

Capability Description
Request by context Warm only the PSOs tagged for a specific gameplay context
Request all Warm everything discovered, regardless of context
Pause / Resume Suspend and resume compilation without losing progress
Cancel Abort all in-progress work and return to Idle

Query

Capability Description
Current state Which of the 6 states the system is in right now
Progress Total entries, completed, failed, percentage, elapsed time
Active contexts Which context tags are currently being warmed
Config count How many configuration assets were discovered
Cache dirty Whether there are compiled results not yet saved to disk

Recording (Editor Only)

Recording mode is the answer to "how do I know which PSOs my game actually needs?"

During a play session in the editor, the system hooks into the rendering pipeline and captures every PSO compilation that occurs organically. Each recorded entry includes the material, vertex factory, render pass, timestamp, compilation time, and whether it caused a visible hitch.

The developer plays through their game normally. When done, they export the recording as JSON or convert it directly into configuration asset entries. This transforms the question from "what materials exist in my project" (which might be thousands) to "what materials actually rendered during gameplay" (which is the minimum viable warm-up set).

Vertex Factory Coverage

The system handles eight vertex factory types that cover the vast majority of Unreal Engine rendering:

  • Static Mesh
  • Skeletal Mesh
  • Instanced Static Mesh
  • Spline Mesh
  • Landscape
  • Niagara Sprite Particles
  • Niagara Mesh Particles
  • Custom (developer-specified)

Render Pass Hints

Each entry can specify which render passes it targets:

  • Auto (let the system decide)
  • Base Pass (main color rendering)
  • Shadow Depth
  • Depth Only (pre-pass)
  • Custom Depth (outline/selection)
  • Velocity (motion vectors)
  • Light Function

This prevents wasteful compilation of pass combinations that a material will never use.


Configuration

All configuration happens through Data Assets. The developer creates one or more configuration assets, each containing:

Per-Entry Settings:

  • Material reference (soft reference, does not force-load)
  • Vertex factory type
  • Render pass hint
  • Context tag (which activation context this entry belongs to)
  • Human-readable label (for inspector display)

Per-Asset Settings:

  • Activation mode (when this batch should compile)
  • Concurrency policy (what to do on overlap)
  • Save policy (when to persist cache)
  • Context tag (the tag that activates this configuration)
  • Discovery mode (how the system finds this asset)

Project Settings:

  • Default activation mode
  • Default concurrency policy
  • Cache file location
  • Maximum concurrent compilations
  • Recording output path (editor only)

The system auto-discovers configuration assets via the Asset Registry at initialization. No registration code required. Place the asset in your project content, and it becomes part of the warm-up pipeline.


Editor Tooling

PSO Inspector

A dedicated editor panel with three sections:

Warm-Up Status — Real-time display of the current state machine state, progress percentage, entry counts (total / completed / failed), elapsed time, and active context tags. Color-coded state indicators follow the cyan system color.

Discovered Configs — Lists all configuration assets found by auto-discovery. Shows per-asset entry counts, activation modes, and context tags. Allows quick navigation to any configuration asset.

Recording Session — Controls for starting and stopping recording mode. Displays captured entries in real-time as PSO compilations occur during play. One-click export to JSON. One-click conversion to configuration asset entries.

Auto-Populator Tool

A separate editor utility that generates configuration assets from Content Browser selections. Select a folder of materials, run the populator, and it creates properly-configured entries with appropriate vertex factory and pass hint defaults. This provides a bulk starting point that recording mode can then refine.

Console Commands

14 commands provide full control from the console:

Command Purpose
pgx.pso.status Display current state and progress
pgx.pso.warmup Request warm-up for a specific context
pgx.pso.warmup.all Request warm-up for all contexts
pgx.pso.pause Pause current compilation
pgx.pso.resume Resume paused compilation
pgx.pso.cancel Cancel all in-progress work
pgx.pso.progress Show detailed progress (entries/percentages)
pgx.pso.contexts List active context tags
pgx.pso.configs List discovered configuration assets
pgx.pso.cache.save Force cache save to disk
pgx.pso.cache.status Show cache state (clean/dirty)
pgx.pso.record.start Begin recording session (editor)
pgx.pso.record.stop End recording session (editor)
pgx.pso.record.export Export recording to JSON (editor)

Integration Points

With Level Transitions

When configured for "On Level Load" activation, the PSO system coordinates with the level transition pipeline. Before the player sees the new level, shaders for that level's materials are already compiled. This is the most common integration pattern.

With Game State

When configured for "On Game Flow Tag" activation, the system can warm shaders for a specific gameplay context (e.g., entering combat, opening inventory) before the context is visually active. This requires no coupling between systems — only a shared GameplayTag convention.

With Loading Screens

The loading screen system can query PSO warm-up progress and display combined loading progress to the player. The formula weights asset loading progress and PSO compilation progress into a single percentage. A timeout prevents the loading screen from waiting indefinitely if PSO compilation encounters errors.

With Profile System

The platform profile system can constrain PSO behavior. On lower-spec hardware, the system might limit concurrent compilation threads to avoid overwhelming the GPU during warm-up. On high-spec hardware, it might enable more aggressive pre-compilation.

Blueprint Access

19 Blueprint nodes across 5 categories:

Category Nodes Purpose
Core 4 Request, Pause, Resume, Cancel
Query 5 State, progress, percentage, completion check, dirty flag
Advanced 5 Context-specific request, add/remove contexts, list contexts, save cache
Debug 2 Config count, pending compilations
Native 2 Shader cache batch control (low-level)

The Native category exposes direct control over the engine's shader cache batching, which is useful for developers who need fine-grained control over when the GPU compiler is active.


Why This Matters

Shader compilation hitching is one of the most common complaints in modern game reviews. It is especially visible in:

  • First boot — Everything is cold-cached. Every material is new.
  • Level transitions — New environments mean new shader combinations.
  • Cutscenes — Fixed camera means the player stares directly at the hitching.
  • Competitive multiplayer — A hitch during a firefight is not just visual — it is gameplay-affecting.

The standard industry response has been to either ship pre-baked PSO caches (fragile, platform-specific, and large) or to accept the problem and hope players do not notice (they do).

PGX's approach is different: make the warm-up process data-driven, context-aware, and observable. The developer knows exactly what is being compiled, when, and whether it succeeded. The system does not require manual lists that rot when content changes. Recording mode captures ground truth from actual gameplay. Context activation prevents wasting GPU time on shaders that are not needed yet.

The result is a game that feels polished from the first frame. No stuttering. No "just wait a moment while shaders compile." The GPU has already done the work before the player's eyes reach the screen.

For teams shipping on multiple platforms, the context-based activation model means the same configuration assets work everywhere. The cache is per-platform, but the configuration is universal.


Resumen en Espanol

El sistema PSO resuelve el problema de los tirones (hitches) que ocurren durante los primeros segundos de juego, causados por la compilacion en tiempo real de Pipeline State Objects (combinaciones de material + tipo de malla + pase de renderizado).

En lugar de listas manuales o caches binarios fragiles, PGX usa Data Assets de configuracion que el sistema descubre automaticamente. El desarrollador declara que materiales y tipos de malla usa su juego, y el sistema los pre-compila de forma asincrona antes de que el jugador los vea.

Caracteristicas principales:

  • Maquina de estados de 6 fases: Idle, Loading, Compiling, Complete, Failed, Paused
  • 4 modos de activacion: al iniciar, al cargar nivel, por tag de estado, o manualmente
  • 3 politicas de concurrencia: rechazar nuevo, cancelar y reiniciar, o fusionar
  • Modo de grabacion: captura los PSOs reales que tu juego necesita durante sesiones de prueba
  • Cache persistente a disco con 4 politicas de guardado
  • 14 comandos de consola, 19 nodos Blueprint, inspector dedicado

El resultado: un juego que se siente pulido desde el primer frame. Sin tirones. Sin compilaciones visibles. El GPU ya hizo el trabajo antes de que los ojos del jugador lleguen a la pantalla.

Clone this wiki locally