RUGENDOCS
Engine & contributing

Performance

What rugen 0.7.0 measured and changed so the engine runs well on old hardware, and how to take the same numbers on your machine.

rugen 0.7.0 was an optimisation-only release. The bar was simple: a match must run at 60 fps with time to spare on a 2012 dual-core laptop with an integrated GPU and 4 GB of memory, and a Steam Deck must never notice the engine. Everything below was measured with rugen bench, before and after, on the same machine (a 2021 six-core laptop with an integrated AMD GPU), with the classic 1v1 simulation bit-identical throughout (the golden checksums in crates/sim pin it).

Before and after

0.6.50.7.0
Convert a 105 MB fighter archive20 s, 1.1 GB peak memory2.9 s, 0.36 GB
Convert Goku (81 MB of sprites)29 s, 2.2 GB3.5 s, 0.5 GB
Same, on one core (old dual-core proxy)~130 s~12 s
Memory of a loaded fighter (Goku)109 MB40 MB
Two fighters loaded, headless match138 MB48 MB
GPU textures kept after a match endsall of them, forever (973 for one match)freed
Draw calls per fight frame60–7540–49
Title screen draw calls23455
Frame spikes in the first 5 s of a fight (over 33 ms)2–6 (sprite decode, sound decode)0
Sim step, 1v1 with supers (mean / worst)0.070 / 0.20 ms0.064 / 0.19 ms
8-frame rollback re-simulation, 1v10.56 ms0.51 ms
Sim step, Simul 4v40.22 ms0.17–0.21 ms
Fight load from .rgc110 ms55 ms
OpenGL fallbackselected but never actually usedworks; auto low-spec mode

Numbers of a quiet run; mean of several. The simulation was already far inside its budget (an online 8-frame rollback has 4 ms on a slow CPU; it takes 0.5 ms here, so about 2 ms there). The real problems were elsewhere: conversion, memory, textures and first-draw hitches.

What changed

Conversion. Sprites were compressed with zstd at level 19, which was 92 % of the time; level 6 makes files about 10 % larger and eight times faster. The SFF is streamed through the encoder instead of being decoded twice into memory, and the written file is checked block by block instead of being decoded again. Verification reuses the compiled logic and caches the common files; stage preloading reads the stored bytes instead of rebuilding a whole SFF. Existing .rgc / .rgs files are untouched and still load.

Memory. A fighter’s sprite data stays in the file and each sprite is read when it is first drawn; sounds are decoded when played, on a worker thread, into a bank capped at 32 MB; the raw controller text is dropped once a state is compiled. A fight waits for a preload in flight instead of loading the same fighter twice. The match screen now gives back every GPU texture when it ends.

Rendering. One vertex and one index buffer kept across frames, a glyph atlas per font (every character was its own texture and draw call), no per-frame clones of the stage, animations and explod lists, redundant pipeline switches skipped. The device is requested with downlevel limits so OpenGL-only adapters get a device instead of a panic, and the adapter, backend and driver are logged at start.

Hitches. Sprites decode on a worker thread and upload under a per-frame budget (bytes, textures and time), with a look-ahead over every animation on screen; a local fight is loaded on the versus screen and its stage, lifebar and common actions are uploaded there. The hot-reload check runs on its own thread; the frame pacing naps instead of spinning.

Low-spec mode. [video] render_scale draws the fight at 0.75 or 0.5 of the window and scales it up with sharp pixels; auto turns it on for integrated GPUs on the OpenGL fallback. OPTIONS → VIDEO → RENDER SCALE.

A fight drawn at render scale 0.5: visibly chunkier pixels, same layout and HUD.
Low-spec mode at 0.5: a quarter of the pixels to shade, scaled up sharp.

Simulation. The expression VM no longer allocates per trigger line, bytecode ops shrank from 48 to 24 bytes, name tables use a fast hash, and helpers without keyctrl carry no command state. A deep checksum (every field of every player, explod and projectile) sits next to the fast one in the golden tests so an optimisation cannot change play unnoticed.

Measure your own machine

rugen bench                       # two fighters from chars/, 3600 frames
rugen bench --chars Goku,Sasuke --convert chars/originals/Goku.zip --out bench.md
RUGEN_FRAMESTATS=1 rugen          # per-second frame phases in the log while playing

rugen bench prints a markdown table: simulation step, snapshot, checksum and a real 8-frame rollback for a 1v1, a Simul 4v4 and an arena match; load times and peak memory; a 900-frame fight with frame statistics (update, draw-list build, submit, present, draw calls, textures); and a conversion in a child process with its own peak memory. --backend gl runs the fight through the OpenGL fallback.

Something wrong on this page? Edit it on GitHub.

SEARCH !!

    ↑↓ to move, Enter to open