Skip to content

MT/R4: parallel level loading and faster startup - #614

Open
qkff99 wants to merge 14 commits into
themrdemonized:all-in-one-vs2022-wpo-mtfrom
qkff99:codex/mt-parallel-level-load
Open

qkff99 wants to merge 14 commits into
themrdemonized:all-in-one-vs2022-wpo-mtfrom
qkff99:codex/mt-parallel-level-load

Conversation

@qkff99

@qkff99 qkff99 commented Jul 18, 2026

Copy link
Copy Markdown

Short version

Made using AI (GPT 5.6 Sol)

The engine was loading many unrelated things one after another.

This PR runs independent native loading work in parallel. Work that is unsafe to parallelize—Lua, object spawning, renderer publication, and D3D11 immediate-context calls—still runs in the original order on the owner thread.

The main target is the MT DX11/R4 build, especially large modpacks such as GAMMA.

What this changes

Faster level loading

R4 can now prepare several level components at the same time:

  • level geometry;
  • CFORM/collision data;
  • C++ shaders;
  • static visuals;
  • details;
  • HOM;
  • portals and sectors;
  • lights;
  • environment data;
  • sound geometry;
  • object models and textures.

Worker threads read and prepare data. Unsafe renderer state is still committed on the main/render thread.

A process-local static level cache also avoids rebuilding unchanged level data when possible.

Faster startup

The startup path now does less repeated filesystem work:

  • the initial VFS catalog is built in bulk;
  • archive data can use shared process-local mappings;
  • frequently used loose configs and scripts are prepared in memory;
  • native initialization work overlaps where dependencies allow it;
  • sound metadata prefetch can run in the background with -prefetch_sounds;
  • sound prefetch pauses during level loading to avoid I/O and CPU contention.

There is no permanent generated cache. Changed mods and files are detected normally on the next launch.

Cheaper precache

All 60 logical precache frames still happen, so frame callbacks and Lua-visible state are preserved.

Only expensive world rendering is reduced:

  • new game/new level/menu save: 12 world renders;
  • quickload/visited level: 1 final world render.

Render->Calculate(), frame updates, callbacks, loading-screen rendering and the final world render are preserved.

Load-session diagnostics

The engine now records the complete load session and its major parts:

  • server and Lua work;
  • native level preparation;
  • client spawn;
  • resource waiting;
  • precache;
  • time until the player receives control.

This is more useful than the old total loading time, which covers only part of the real loading process.

What this does not change

This PR does not:

  • run Lua on worker threads;
  • change Lua callback order;
  • change restart_all, on_register or script binders;
  • change save-file format;
  • change object IDs or parent/child order;
  • run net_Spawn in parallel;
  • move D3D11 immediate-context calls off the render thread;
  • add new dependencies, configuration options or public APIs.

In simple terms: native data preparation is parallel, but game-visible publication remains ordered.

Results

Tested on Ryzen 7 9800X3D and SATA SSD

These are single comparable runs on the same machine, not medians.

The level-loading comparisons use the existing total loading time log value.

Vanilla Anomaly 1.5.3

Scenario Original MT This PR Difference
New-game level load 2,128 ms 1,490 ms −30.0%
Cold save load from menu 2,267 ms 1,439 ms −36.5%
Hot save load 1,213 ms 1,321 ms +8.9%
New-level transition 2,390 ms 1,658 ms −30.6%
Visited-level transition 2,321 ms 1,350 ms −41.8%

GAMMA

Scenario Original MT This PR Difference
New-game level load 11,390 ms 4,582 ms −59.8%
Cold save load from menu 5,376 ms 4,598 ms −14.5%
Hot save load 4,545 ms 4,419 ms −2.8%
New-level transition 8,416 ms 4,812 ms −42.8%
Visited-level transition 5,345 ms 4,692 ms −12.2%

Startup to main menu

Build and scenario Original MT This PR Difference
Vanilla, no shader cache 5,281 ms 5,235 ms −0.9%
Vanilla, shader cache available 4,156 ms 4,297 ms +3.4%
GAMMA, no shader cache 24,719 ms 16,734 ms −32.3%
GAMMA, repeated warm launch 17,750 ms 11,140 ms −37.2%

The largest improvements appear in GAMMA new-game loading, new-level transitions and startup. Hot save is mostly limited by sequential Lua/ALife work and therefore improves much less.

Testing performed

The DX11-AVX x64 MT build was tested with:

  • Vanilla Anomaly 1.5.3;
  • GAMMA;
  • first and repeated startup;
  • startup with and without shader cache;
  • new game;
  • cold save loading from the menu;
  • hot save loading;
  • transitions to new levels;
  • transitions to visited levels;
  • repeated level transitions;
  • returning to the menu and quitting;
  • background sound prefetch.

The implementation also includes fixes for races found during testing in:

  • parallel shader/resource creation;
  • texture THM parsing;
  • level preparation and destruction;
  • sound-prefetch lifetime;
  • startup native initialization.

Scope and limitations

  • Intended for the MT DX11/R4 x64 build.
  • Other renderer paths are not the target of this PR.
  • Startup results depend heavily on Windows filesystem cache and modpack size.
  • The static level cache is process-local and is lost when the game exits.
  • Vanilla hot-save loading currently shows a small regression and needs reviewer attention.
  • This is a large internal performance change, so it should be reviewed and tested as an MT-specific feature before wider use.

@qkff99

qkff99 commented Jul 19, 2026

Copy link
Copy Markdown
Author

non-MT version - #616

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant