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

177 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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`](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.