Files
logan 611a1a5952 docs: client-perf analysis, bench harness and work journal
Everything the next agent needs to run the Phase 2 optimisation loop on a
machine that can launch the client:

- analysis/client-perf/: LEVERS (ranked levers + corrections), BENCH-MATRIX
  (world, scenes, config IDs, blocks B0-B14 with decision rules),
  PLAYER-TUNING, SPAWN-TUNING, research briefs, the jar scanner and its
  output, observed default configs, and proposed-configs/ (build.py
  generates tiers and bench variants and fails on unknown keys;
  apply_variant.py applies a variant and merges options.txt).
- analysis/client-perf/bench/onion-bench/: bench-world datapack (scene
  teleports, Create grid, mob pen, sound storm, 120 s teleport tour).
- analysis/audit/: the static recipe/dependency audit, including gen_fixes.py,
  which generates the shipped Paxi datapack and was never tracked.
- phase0/: per-machine measurement harness (Steam Deck, 7700K/1060,
  6700K/1080 Ti).
- .pi/journal.md: running work log; read it first.

All of it is packwizignored, so nothing here ships to players.
2026-10-14 18:15:32 -04:00

16 KiB
Raw Permalink Blame History

Onion MC — client performance levers (Phase 1, unmeasured)

Fleet (from phase0/): P0 Steam Deck (GPU/shared-RAM bound), P1 7700K + GTX 1060 6 GB (VRAM bound), P2 6700K + 1080 Ti with ~8 GB free RAM (main-thread + heap bound). Every machine is 4C/8T. That makes CPU-side render cost, chunk/LOD worker threads and GC the common denominator.

Impact estimates are informed guesses until Phase 2 measures them. Tags: V verified in source or config, D documented upstream, C community, I inference. Sources are in research/ and data/.


1. The shape of the problem

From data/jarscan-report.md:

Surface Size Who dominates
Mixin classes on per-frame paths 951 (121 @Overwrite / @Redirect) Sable 127, Iris 114, Controlify 84 (GUI), Sodium 48, EMF 42, Create 30, ETF 27, Subtle Effects 26
Mods subscribing to per-frame events 58 Puzzles Lib, Create, Alex's Caves, Mowzie's, Twilight Forest, Ars Nouveau, Balm, Xaero's
Mods on client / player / entity tick events 79 —
Block + item atlas ~60k 16×16-sprite equivalents (≥4096²) Handcrafted 27 %, Chipped 21 %, Rechiseled 9 % (58 % together)
Baked models / blockstates 69,442 / 16,361 Chipped 16.8k, Rechiseled 13.9k, Handcrafted 12.4k models (62 %)
Entity types ~600 Alex's Mobs 140, Alex's Caves 92, Twilight Forest 80, Ars Nouveau 51
Animated atlas textures 312 Frights' Delight 69, TF 44, Chipped 30, Ars 29
Post-processing / shader payloads — Lodestone 59 files, Alex's Caves 38 (7 post), Charta 31, Exposure 21 (19 post); vanilla core-shader overrides from Sable, Sodium, Xaero's ×2

What a player pays by default today, before choosing any options:

  • Shaders and resource packs are not on by default. packwiz only downloads them; the last real launch had resourcePacks:[] and no shaderpack. So Fresh Animations, Better Leaves, Complementary and MakeUp cost nothing until a player enables them. I
  • Distant Horizons is mandatory and its defaults are heavy: 256-chunk LOD radius, SSAO on, AA on, MEDIUM/MEDIUM quality, transparency COMPLETE. V (observed DistantHorizons.toml)
  • Every mod's own defaults ship unchanged. The pack ships no client performance config. Its only configs are PetProtect, Akashic Tome and the Paxi datapack.
  • The JVM, heap and GC are whatever each player's Prism instance has. The old instance had MaxMemAlloc=8192 and stock G1.

That last bullet is the reason for §2.


2. Meta-levers (enablers; do these first)

# Lever Why
M1 A way to ship tuned defaults without clobbering player changes. Resolved (2026-10-14, Phase 1b): use Default Options (config/defaultoptions/, client-only, copy-if-absent), plus an enforced packwiz file for modernfix-mixins.properties, which is read at mixin time. preserve survives packwiz refresh but leaks into the server dir, which matters for DH [server]/[common] and SSRD COMMON. Evidence: proposed-configs/README.md §1. Without this, every config lever below depends on 10 players hand-editing files.
M2 Per-tier profiles (Deck / mid / high). Ship one conservative default, plus a short "if your machine is X, change Y" doc. Optionally make heavy client-only cosmetics packwiz optional (today only Controlify is). Fleet constraints differ in direction. P2 should lower DH threads; P1 should lower DH distance and quality. One global ordering won't fit (already noted in phase0/P2).
M3 Profile with spark on the client (/sparkc profiler; spark is already in the pack) in Phase 2, not only FPS percentiles. 951 frame-path mixins means attribution must come from a profiler, not from guessing which mod is slow.

3. Ranked levers

Tier 1 — biggest expected wins, config or policy only

# Lever Mechanism Helps Risk Conf.
1 Distant Horizons defaults per tier. lodChunkRenderDistanceRadius 256 → ~64–96 (P0), 128–192 (P1), ~128 (P2). Turn SSAO off on P0 and P1. Lower the CPU thread preset on P2. Keep cave culling on. LODs come from the server; do /dh pregen server-side, not Chunky. DH cost scales with LOD distance in VRAM, GPU and CPU. It is the largest always-on renderer the pack adds. all; P1 VRAM, P0 everything visual only V/D
2 Shader policy. Default off, opt-in per machine. If on: MakeUp UF, or Complementary at ≤ MEDIUM profile, shadow distance ≤ 8 chunks, entity shadows off. With any shaderpack active, Flywheel marks both GPU backends unsupported (!ShadersModHelper.isShaderPackInUse(), checked in source). Create then renders every kinetic block entity on the CPU, and again in the shadow pass. EMF/GeckoLib entities are also redrawn for shadows, and DH adds a shadow pass over all LODs. all; P0 hardest none (policy) V
3 Entity count, set on the server. Shipped in d80f85b, see SPAWN-TUNING.md. Tune Alex's Mobs spawn weights: the 1.21.1 port has an open issue about inflated vanilla spawn rates (#62). Cap Fowlplay: #214 reports ~400 birds even after tuning. Cap Critters & Companions spawns via datapack (it has no config). Every entity pipeline (vanilla, EMF/FA, GeckoLib, ETF, LambDynamicLights, Perception trails) multiplies by visible entity count. all clients + server tick gameplay feel D/C
4 EMF/ETF throttles. animationLODDistance 20 → ~8–12. Consider retainDetailOnLowFps=false on P0. modelUpdateFrequency Average → Slow. Keep animationFrameSkipDuringIrisShadowPass=true (already the default, V observed). Treat the FA Extensions pack as opt-in. FA turns every vanilla mob into an animated CEM model. EMF's frame-skip-by-distance is the throttle. P0, P2 animation smoothness at range V
5 JVM heap and GC per machine (Prism settings; packwiz can't ship them). Heap: P0 5–6 G, P1 6–8 G, P2 ≤ 5–6 G (not the old instance's 8 G). Benchmark three GCs: client-tuned G1 (brucethemoose set), Generational ZGC (-XX:+UseZGC -XX:+ZGenerational; plain UseZGC on JDK 21 is the non-generational one), and Shenandoah. DH warns about G1 itself. GC pauses show up as 1 % lows. P2's 8 GB headroom makes heap size a hard constraint. P2, P0 low D/V
6 ModernFix mixin.perf.dynamic_resources=true. Off by default (V in the observed modernfix-mixins.properties). Also consider dynamic_entity_renderers. 69k baked models and 16k blockstates sit in heap. The author documents ~−55 % memory even with FerriteCore. ModernFix has a CTM sub-option (dynamic_resources.ctm); test against Fusion and Athena. P2, P0 (RAM) med: missing textures or crashes with CTM/model mods D/V
7 Flywheel lightSmoothness SMOOTH → FLAT or TRI_LINEAR (flywheel-client.toml). The config comment itself says "may have a large performance impact". It only matters when shaders are off, i.e. when Flywheel is actually used. all, Create-heavy bases visual V
7b Sable / SSRD sub-level budget. Sable has no renderer of its own. Its SODIUM_REACHAROUND path runs Sodium's chunk renderer once per visible sub-level, so cost is roughly sub-levels × meshed sections, on top of terrain. Default the client SSRD distance low (8–16 chunks), not the 128-chunk server cap. Keep sub_level_skylight_shadows=false (the default, V observed). Each airship is a second terrain pass. Under shaders, shadows also trigger open Iris #3164 sub-level corruption. all; P0, P1 first visual range of airships V (config) / I (magnitude)
8 LambDynamicLights: mode → fast or fastest. On P0/P2 also set light_sources.entities=false (keep self). Each moving light source triggers Sodium chunk-section rebuilds. On 4-core CPUs those compete with chunk builders, which default to 2 threads. P2, P0 less responsive light V

Tier 2 — solid, smaller or more situational

# Lever Helps Conf.
9 Sound Physics Remastered: ray count 32 → 16, bounces 4 → 2, strict_occlusion=true (1 occlusion path instead of 9). Cap noisy Create and mob sounds in sound_rates.properties. Cost scales with concurrent sounds, so a Create floor is the worst case. P2, P0 V
10 Vanilla and Sodium options: render distance 8 to match the server (the old client had 12, which loads nothing extra in multiplayer). Biome blend 2 → 1 or 0. Mipmap 4 → 2 on P1/P0 (the atlas is ≥4096²). Entity distance 75–80 %. Sodium chunk threads auto-resolve to 2 on every fleet machine; try 3 on P1/P2. all V/I
11 Better Leaves needs leaf culling if anyone enables it. Use Cull Leaves (same author, made for it) or More Culling. Do not use Sodium Leaf Culling: it crashes on Sodium ≥ 0.8.11. all, forests D
12 Twilight Forest / Alex's Caves client effects. TF cloudBlockPrecipitationDistance → 0 and aurora toggles. AC biome_ambient_light_coloring=false for shader users. DH flags AC as partially incompatible (V, log). in those dimensions C/D
13 Jade. Pre-tune hide-blocks.json (player heads) and container-content display. Sophisticated Backpacks' "20–30 FPS when looking at a backpack" report was traced to Jade's content display. all C
14 Bump Puzzles Lib 21.1.62 → 21.1.64, as a bugfix, not a perf lever. The 21.1.64 cache is in FabricEventFactory, which isn't in the NeoForge jar. The NeoForge 62 → 64 diff is access-transformer restorations (21.1.63) plus one item-properties class. Applied in a01bc3b. — V (jar diff)
15 Xaero's: cap world-map loaded regions, keep the minimap modest. Radar as dots. P2 heap D
16 WATUT: raise tickReceiveAndRenderRateOfGUIUpdates, or dontSendDetailedGUIInfo=true. Watch the open TF conflict (#106). Perception: remove XP-orb trails. Subtle Effects: dust clouds off on P0. FWA: chain/lantern animations off on P0. P0, P2 V/C
17 Platform: mesa_glthread=true + gamemode on the Deck. NVCP "Prefer maximum performance" on P1. Leave threaded optimisation alone; Sodium manages it. P0, P1 D/C

Tier 3 — content weight (structural; decide after measuring)

  • Handcrafted + Chipped + Rechiseled = 58 % of the atlas and 62 % of all models. They cost startup time, heap (baked models) and some VRAM. Lever 6 mitigates the heap part. Removing any of them is a content decision, but they're the only lever that moves startup and model RAM materially. Chipped requires Athena, and Rechiseled requires Fusion.
  • Fresh Animations (+ Extensions): the largest documented entity-render multiplier, especially with shaders (EMF #443, Oculus era: 165 → 40 FPS with 30+ mobs). It's already opt-in because it's a resource pack. Keep it that way and say so in the player doc.

4. Mods to add (all confirmed with NeoForge 1.21.1 builds via the Modrinth API)

Owner decisions, 2026-10-14:

  • Adding any helpful mod is allowed. Default Options, Sodium Extra, Reese's Sodium Options, BadOptimizations and Particle Core were added in a01bc3b.
  • The trials are bench block B12b.
  • More Culling is out: its NeoForge build excludes 1.21.1.
  • Shaders stay opt-in.
Mod Why Verdict
Default Options (default-options) Lever M1 delivery mechanism; Balm already present Add
Sodium Extra + Reese's Sodium Options Expose particle, animation and fog toggles per machine. RSO works with Controlify, which matters on the Deck. Add
Particle Core (particle-core 0.3.3) Particle frustum culling, async ticking, per-type limits. Big particle sources here: Subtle Effects, Perception, Ars, Alex's Caves, Sooty Chimneys. Add, then verify
BadOptimizations (2.4.1) Lightmap and sky caching; self-disables on conflicts Add
Gnetum (3.3.6) Spreads HUD rendering over frames. This pack is HUD-heavy (Xaero's, Jade, AppleSkin, titles, plaques). The author says it's compatible with ImmediatelyFast. Replaces Exordium, which has no NF 1.21.1 build. Trial
Cull Leaves or More Culling Only if Better Leaves stays (lever 11) Add one
Better Block Entities (1.3.4, built for Sodium 0.8.12–0.8.13) Meshes static chests, signs and beds into terrain. Its EMF compat is only for 1.21.11+, so test it with FA. Trial
Flerovium Item, particle and entity render culling. Higher visual-glitch risk with layered models. Trial
Super Resolution (alpha) FSR upscaling. Potentially the biggest P0 lever, but alpha, and Complementary has no SR interface. Trial on P0 only
Ixeris Moves event polling off the render thread. Big win only on Windows with high-poll mice. Has open crash reports on older NVIDIA cards. Trial on P1
Iris Flywheel Compat Restores Flywheel instancing under shaders, which would neutralise most of lever 2's Create penalty. Unofficial and breaks on Create/Iris updates. Trial only if shaders are wanted

Skip: Sodium Leaf Culling (crashes), ScalableLux (breaks DH), Moonrise and C2ME on the client (no client gain in multiplayer), Exordium / Krypton / Nvidium / VulkanMod / FastChest / YOSBR (no NeoForge 1.21.1 build), Memory Leak Fix (obsolete), Sodium Options API (superseded by Sodium 0.8), Sodium Dynamic Lights (duplicates LambDynamicLights).

5. Mods worth considering for removal

Owner decision, 2026-10-14: remove no mods. Kept for reference only. Mitigate with config instead (spawn tuning: analysis/client-perf/SPAWN-TUNING.md).

Ordered by cost to value. None is urgent; measure first.

Mod Cost evidence Note
Miner's Delight It's the only consumer of Lodestone (10 frame mixins incl. 3 overwrite/redirect, 59 shader files, post effects). V (dependency scan) Removing one food addon drops a whole rendering library
Fowlplay Open 1.21.1 issue: hundreds of birds; per-tick flight pathfinding (server) Remove if spawn caps don't hold
Charta 31 shader files registered at load; niche use Only if nobody plays cards (both sides)
WATUT Streams other players' GUI state each tick; open TF conflict Flavour only
Emojiful Font-render hooks; known chat freezes upstream Low impact; social call
Fresh Animations Extensions Extra CEM models on top of FA Keep opt-in, recommend against on P0
AmbientSounds Largest jar (85 MB of ogg); RAM residency unmeasured Measure heap first

Not removable, contrary to r4's suggestion: one of Fusion/Athena. Chipped requires Athena and Rechiseled requires Fusion (V, neoforge.mods.toml dependencies).


6. Phase 2 — test order

Superseded by BENCH-MATRIX.md: world, scenes, config IDs, blocks B0–B13 with decision rules.

7. Corrections to subagent research (verified by the orchestrator)

Added in Phase 1b (2026-10-14):

  • DH default threads is 4 on the fleet, not 2. The default is ceil(nproc × 0.5) (ThreadPresetConfigEventHandler @ 3.3.2). The observed numberOfThreads = 2 came from the owner's 4-thread machine (nproc = 4). Every other observed DH value matches the source defaults.

  • Puzzles Lib 21.1.64 has no perf change on NeoForge (see lever 14). r9's lead and this file's earlier "the fix is in 21.1.64 for 1.21.1" were wrong for our loader.

  • r6's Shenandoah flags crash JDK 21. -XX:ShenandoahGCMode=iu needs -XX:+UnlockExperimentalVMOptions (tested on OpenJDK 21.0.12). Use plain -XX:+UseShenandoahGC.

  • SSRD's distance is a COMMON config (config/ssrd-common.toml, physicsRenderDistance, default 128, slider floor 16), and the server reads it too. It is not stored in Sodium's options.

  • r4 "remove one CTM engine": wrong. Both are hard dependencies (see §5).

  • r9 "Puzzles Lib ~8 % render-thread overhead": the measurement was on MC 26.2/Fabric. The fix is in 21.1.64 for 1.21.1; the size of the gain there is unknown.

  • r3/r8 cite EMF #443 for magnitude. That report is from the Oculus/Forge era and is closed. Treat the 4× figure as directional only.

  • Static scan's "Sable overrides vanilla core shaders" (r7 checked it): real but small, just worldborder.* from the nested Veil. The substantive part is Sable's runtime patching of Flywheel's internal shaders.

  • r1's Flywheel-under-shaders claim was checked in Backends.java on the 1.21.1 branch: confirmed.