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.
This commit is contained in:
logan committed 2026-10-14 18:15:32 -04:00
1 parent d80f85b3f6
commit 611a1a5952
102 files changed
+25853

No files matched your search

+190
View File
@@ -0,0 +1,190 @@
# Phase 2 benchmark matrix
This replaces `LEVERS.md` §6. It is meant to run without judgement calls: every config is a
generated directory, every scene is a datapack function, and every block ends in a decision
rule. The measurement protocol and machine prep are in `../../phase0/P{0,1,2}-*.md`
(uncapped, vsync off, p99 frametime + 1 % low, 15 s warm-up discarded, 3 runs, one variable
per comparison). This file adds what phase0 leaves open: the world, the scenes, the configs
and the order.
Machines: **P0** Steam Deck · **P1** 7700K + GTX 1060 6 GB (Windows) · **P2** 6700K + 1080 Ti,
~8 GB free RAM.
---
## 0. Prerequisite: boot check
The pack has never booted at its current size: 217 mods after `a01bc3b` added Default
Options, Sodium Extra, Reese's Sodium Options, BadOptimizations and Particle Core. Before
any timing, launch once and check `latest.log` for:
- missing deps and mixin apply failures;
- `fishermans-haven` biome-modifier errors;
- `ftbteams` references;
- Default Options' `Populated … with default options` lines (proves the seeding works);
- no YACL "Failed to deserialize" lines for `fowlplay` or `crittersandcompanions`
(spawn tuning, `SPAWN-TUNING.md`);
- a working Sodium video-settings screen (Reese's Sodium Options replaces it; SSRD injects a
slider into it).
r5 also flags a reported Iris 1.8.14-beta.1 ↔ Sodium 0.8.13 mixin incompatibility on
NeoForge 1.21.1, with a third-party "Iris-Sodium Compat" patch on CurseForge. Check for it
with a shaderpack enabled. **If the pack doesn't boot cleanly, fix that first. Bisect the
five new mods before anything older.**
## 1. Fixed inputs
| Input | Value |
| --- | --- |
| Pack | one commit, recorded in every row (`git rev-parse --short HEAD`). The Puzzles Lib bump goes in before B0 if approved, never mid-matrix. |
| Bench instance | a **separate** Prism instance per machine with the packwiz `PreLaunchCommand` **removed**, so it can't self-update mid-matrix. Mods are copied from a normal install of the recorded commit. |
| Java | Temurin 21 on all three (phase0). Check `java -XX:+UseShenandoahGC -version` works (needed for J3). |
| World | `bench-world-v1.zip`, built once on P2 (§2), sha256 recorded, unpacked identically on every machine |
| Configs | `analysis/client-perf/proposed-configs/out/<ID>/`, generated by `build.py` at the recorded commit |
| In-game pins | phase0 Step 7/8. The `options.txt` keys that matter are already in every bench variant (`maxFps:260`, `enableVsync:false`, `renderDistance:8`, `simulationDistance:8`). GUI scale, FOV, resolution and fullscreen are recorded per machine and never changed. |
| Resource packs / shaders | none, unless the block says otherwise |
### Applying a config
With the game closed, run
`python3 proposed-configs/apply_variant.py <ID> <instance>/.minecraft`. It copies all 10
config files (every variant has every file, so nothing from a previous variant survives)
and **merges** `options.txt` key by key, so GUI scale, resolution and keybinds are kept.
It also writes the ID to `.minecraft/BENCH_VARIANT.txt`; check it before each run. On
Windows without Python, copy the `config/` tree by hand and edit the listed `options.txt`
keys in game. Default Options seeds only missing files, so it never interferes.
**Relaunch for every config change**, even ones a mod could hot-reload. Run all of that
config's scenes in one session, in the order S1 → S2 → S3 → S4 → S5 → S6.
### JVM sets (Prism → Java arguments; `-Xmx` per block)
All four were checked to parse on OpenJDK 21.0.12.
| ID | Arguments (plus `-Xmx`) |
| --- | --- |
| **J0** (phase0 default) | `-XX:+UseG1GC -XX:+ParallelRefProcEnabled -XX:MaxGCPauseMillis=200 -XX:+UnlockExperimentalVMOptions -XX:+DisableExplicitGC -XX:G1NewSizePercent=30 -XX:G1MaxNewSizePercent=40 -XX:G1HeapRegionSize=8M -XX:G1ReservePercent=20 -XX:G1HeapWastePercent=5 -XX:G1MixedGCCountTarget=4 -XX:InitiatingHeapOccupancyPercent=15 -XX:G1MixedGCLiveThresholdPercent=90 -XX:G1RSetUpdatingPauseTimePercent=5 -XX:SurvivorRatio=32 -XX:+PerfDisableSharedMem -XX:MaxTenuringThreshold=1` |
| **J1** (client G1, brucethemoose) | `-XX:+UseG1GC -XX:MaxGCPauseMillis=37 -XX:G1HeapRegionSize=16M -XX:+UnlockExperimentalVMOptions -XX:G1NewSizePercent=23 -XX:G1ReservePercent=20 -XX:G1MixedGCCountTarget=3 -XX:G1HeapWastePercent=20 -XX:InitiatingHeapOccupancyPercent=10 -XX:G1RSetUpdatingPauseTimePercent=0 -XX:MaxTenuringThreshold=1 -XX:GCTimeRatio=99 -XX:+DisableExplicitGC -XX:+PerfDisableSharedMem` |
| **J2** (Generational ZGC) | `-XX:+UseZGC -XX:+ZGenerational -XX:+DisableExplicitGC -XX:+PerfDisableSharedMem`. Plain `-XX:+UseZGC` on JDK 21 is the old non-generational ZGC. |
| **J3** (Shenandoah) | `-XX:+UseShenandoahGC -XX:+DisableExplicitGC -XX:+PerfDisableSharedMem`. **Not** r6's `-XX:ShenandoahGCMode=iu`: on JDK 21 that aborts VM startup unless `-XX:+UnlockExperimentalVMOptions` is added (tested), and the mode is gone in later JDKs. |
Default heap outside B4 (phase0): P0 `-Xmx5G`, P1 `-Xmx8G`, P2 `-Xmx6G`, all with J0.
---
## 2. Bench world (built once on P2)
1. Copy `bench/onion-bench/` into a new world's `datapacks/` folder. Create the world:
Creative, cheats on, seed `onionbench`, default world type. Run `/function bench:setup`.
2. **S2**: `/function bench:s2`, then `/function bench:s2_setup`. Check that the shafts and
cogwheels **rotate**. If the `/fill`-placed network didn't initialise, break and re-place
one motor per row by hand. Accept when F3 shows Flywheel instances and every row turns.
3. **S3**: `/function bench:s3`, then `/function bench:s3_setup`. Check the counts with
`/execute if entity @e[type=villager,x=1999,y=150,z=-1,dx=25,dy=8,dz=25]` (expect 30).
4. **S4**: `/function bench:s4` (camera at 3000 172 -10, facing south). Build one
Aeronautics test ship (about 5×7×15 blocks, planks + glass + 1 propeller) and assemble it
so it hovers at y≈170. Copy it with a Create schematic to get **4 ships, centred at z =
32, 96, 192 and 384** (2, 6, 12 and 24 chunks away). SSRD 16 then cuts only the 384 one.
Record the sub-level count Sable shows. This is the only hand-built scene. It's baked into
the zip, so runs never rebuild it. To get the 0- and 1-ship states, break ships into
blocks *in a copy of the world*: `bench-world-v1-s4-0.zip` and `-s4-1.zip`.
5. **DH pre-generation**: use the upstream DH file
(`data/observed-default-configs/DistantHorizons.toml`: `SURFACE_THEN_CHUNKS`, radius
256), then stand at S1 until DH's F3 generation queue reads 0.
Run `/function bench:tour_start` with the same settings, waiting at each hop until the
queue drains (re-issue `bench:tour/stepN` by hand). Do the same at S4. Every bench config
then runs with `generatorPlan = "DISABLED"`, so no world generation happens during
measurement.
6. Leave the world at S1, save, quit and zip it.
### Scenes
| ID | Command | What it loads | Capture |
| --- | --- | --- | --- |
| **S1** vista | `/function bench:s1` | 0 200 0, facing south, 20° down. Pure terrain + DH LODs | 15 s warm-up + 30 s, hands off |
| **S2** Create floor | `/function bench:s2` | 2,304 kinetic BEs (Flywheel instancing; vanilla BE path under shaders) | 15 + 30 s |
| **S2s** sound storm | S2 + `/function bench:storm_on` (stop: `storm_off`) | 24 sounds/tick from 3 capped Create IDs (8 per ID per tick vs cap 4) | 15 + 30 s, sound on |
| **S3** mob pen | `/function bench:s3` | 30 villagers, 15 cows, 15 sheep, 10 allays carrying torches (moving LDL sources), 10 dropped torches | 15 + 30 s |
| **S4** airships | `/function bench:s4` | 0 / 1 / 4 Sable sub-levels at 2–24 chunks | 15 + 30 s |
| **S5** tour | `/function bench:s1`, wait 10 s, then `/function bench:tour_start` | 24 teleports of 160 blocks every 5 s along +x: chunk + LOD streaming, GC churn | **all 120 s, no warm-up discard** |
| **S6** GUI | at S1, press E with the JEI overlay open; mouse parked over the JEI list | GUI / JEI / tooltip path | 15 + 30 s |
---
## 3. Config IDs
Generated by `proposed-configs/build.py`. `out/MANIFEST.md` lists every changed key.
**C0** = every file at upstream default; **C1** = the proposed pack default (= mid tier).
All other IDs are C1 with **one** change.
| ID | Change vs C1 |
| --- | --- |
| C0 | everything back to upstream (DH 256 / SSAO on / 4 threads, Flywheel SMOOTH, LDL fancy, SPR 32/4, mip 4, blend 2, ModernFix dynres off, …) |
| C1-deck, C1-high | the tier deltas from `proposed-configs/README.md` §2 |
| dh-r256 / dh-r192 / dh-r64 | DH LOD radius (C1 = 128) |
| dh-off | C1, then DH options → *Enable Rendering* off after each launch. That toggle is GUI-only and never written to the file. |
| dh-ssao / dh-thr4 / dh-qlow | SSAO on / 4 DH threads / DH's LOW quality preset at radius 128 |
| fw-flat / fw-smooth | Flywheel `lightSmoothness` (C1 = TRI_LINEAR) |
| mf-dynres-off | ModernFix `dynamic_resources=false` |
| ssrd-32 / -64 / -128 | SSRD `physicsRenderDistance` (C1 = 16) |
| sable-noshade | Sable `sub_level_dynamic_shading=false` |
| emf-lod20 / emf-lod8 | EMF LOD 20 + Average (= upstream) / LOD 8 + `retainDetailOnLowFps=false` |
| ldl-fancy / ldl-fastest | LambDynamicLights mode (C1 = fast); `fastest` also turns entity light sources off |
| spr-up / spr-strict / spr-nocap | SPR rays 32/4 (upstream) / `strict_occlusion=true` / Create sound caps removed |
| sod-thr3 | Sodium `chunk_builder_threads=3` (C1 = 0 = auto = 2) |
| opt-blend2 / opt-blend0 / opt-mip4 | biome blend 5×5 / off; mipmap 4 |
---
## 4. Blocks, in run order
"Win" means the median p99 frametime improves by more than that machine's **B0 spread**, and
the 1 % low doesn't get worse by more than the spread. Ties keep the upstream value (less
change). FPS floors for tier picks: **P0 1 % low ≥ 30**, **P1/P2 1 % low ≥ 60**.
| Block | Machines | Configs (× JVM) | Scenes | Records | Decision rule |
| --- | --- | --- | --- | --- | --- |
| **B0** noise floor | all | C0 ×3 sessions | S1 | p99 / 1 % spread | Defines each machine's threshold. P2 also checks `MemAvailable` drift (phase0 Step 4). |
| **B1** baseline | all | C0 | S1–S6 | + `Game took N seconds to start` (ModernFix log line), the `Created: …blocks.png-atlas` line, `/sparkc heapsummary` after S1 loads, VRAM peak (P1/P2) | Reference numbers for everything below |
| **B2** composite | all; tiers on their machine | C1 (all), C1-deck (P0), C1-high (P2) | S1–S6 | as B1 | If C1 isn't a win on ≥ 2 machines, stop and bisect with B3–B11 before shipping anything |
| **B3** Distant Horizons | all | dh-r256, dh-r192, C1, dh-r64, dh-off | S1, S5 | + DH memory in F3 | **Radius per tier** = the largest radius meeting the FPS floor on S1 *and* S5: P0 → deck, P1 → default, P2 → high |
| | P0, P1 | dh-ssao, dh-qlow | S1 | | Adopt SSAO-off / LOW-quality for that tier on a win |
| | all | dh-thr4 | S1, S5 | | Keep 2 threads unless 4 is a win on S5 *and* not a loss on S1 |
| **B4** heap & GC | P2, then P0 | C1 (C1-deck on P0) × J0, J1, J2, J3 at `-Xmx6G` (P2) / `-Xmx5G` (P0); then the winner at 4 / 5 / 6 G | S5 (120 s), S2 | + `/sparkc gc` after S5 (pause count / max / total), swap delta (P2) | GC = best S5 1 % low. Heap = the smallest size within the spread of the best. Feeds `PLAYER-TUNING.md` §2. P1 isn't run (32 GB, not a constraint). |
| **B5** Sable / SSRD | P0, P1 | world s4-0 / s4-1 / s4-4 × C1; s4-4 × ssrd-32, ssrd-64, ssrd-128, sable-noshade | S4 | sub-level count | SSRD per tier = the largest value meeting the floor with 4 ships. sable-noshade ships only on a win *and* if it looks acceptable. |
| | P1 | C1, s4-4, J0 | S4, **45 min** (2 runs) | FPS at 0 / 15 / 30 / 45 min, `/sparkc heapsummary` at each | Decay > spread is a Sable #1011-class finding: report it, no config fix |
| **B6** ModernFix | P2, P0 | C1 vs mf-dynres-off, 3 cold launches each | S1, S2, S6 | startup, heap after load, missing-texture check (below) | Ship `dynamic_resources=true` only if: heap −300 MB or more *or* startup faster beyond spread, **and** zero missing textures, **and** no crash in 3 launches |
| **B7** shaders | P1, P2: all rows; P0: MakeUp rows only | C1 with: none / MakeUp `shadowless_medium` / MakeUp `medium` / Complementary `LOW` / Complementary `MEDIUM` (profiles set in Iris' shader settings) | S2, S1, S4 (s4-4) | `Flywheel` backend line in F3 (expect off with shaders); visual check for Iris #3164 on ships | Data for the owner's shader policy, no auto-decision. Report the FPS cost per profile and whether ship corruption appears with shadow profiles |
| **B8** entities | P2, P0 (P1: FA rows only) | C1 / C1 + Fresh Animations / C1 + FA + Extensions / emf-lod20 + FA / emf-lod8 + FA; ldl-fancy, ldl-fastest (no FA) | S3 | | EMF: adopt the smallest LOD that's a win with FA on. LDL: per tier, the cheapest mode that's a win. FA guidance → `PLAYER-TUNING.md` §4 |
| **B9** Flywheel | P0, P1 | fw-flat, C1, fw-smooth (no shaders) | S2 | | Per tier: the smoothest value meeting the floor |
| **B10** Sound Physics | P2, P0 | spr-up, C1, spr-strict, spr-nocap | S2s | | Adopt each change that's a win. Listen for obvious audio breakage. |
| **B11** vanilla / Sodium | P1, P2 | sod-thr3 | S5, S1 | | 3 threads only if S5 wins and S1 doesn't lose |
| | all | opt-blend2, opt-blend0 | S5 | | Lowest blend radius that's a win, per tier |
| | P1 | opt-mip4 | S1, S2 | VRAM peak | Mip 4 for mid/high only if VRAM peak ≤ 5.5 GB and no loss |
| **B12a** added-mod ablation | P2, P0 (P1: Sodium Extra only) | C1 with **one** of BadOptimizations / Particle Core / Sodium Extra removed from `mods/` | S1, S3, S2s | | Keep the mod unless removing it is a win (it costs FPS) or it breaks something visible. Sodium Extra + RSO stay regardless for their per-machine options. |
| **B12b** trials | per mod: Gnetum (all), Better Block Entities (all, with FA on and off), Flerovium (all), Super Resolution (P0), Ixeris (P1), Iris Flywheel Compat (with shaders, B7 scenes), Cull Leaves + MidnightLib (with Better Leaves) | C1 + one mod | the scenes that mod targets | | Owner pre-approved helpful adds. Add with `packwiz modrinth add` only on a win with no breakage. **Not More Culling**: its NeoForge build declares `minecraft [1.21,1.21.1)`. A new mod set means re-running B0 first. |
| **B13** profile | P2 | the worst scene from B2 under C1 | that scene | `/sparkc profiler start --timeout 60`, upload or save the report | Flag any mod over 5 % of render-thread time that isn't Sodium, Iris, DH or Flywheel |
| **B14** spawn tuning | P2 | singleplayer, defaults vs the shipped spawn files | river, jungle, warm ocean (10 min each) | per-species and total entity counts | Procedure and keep/back-off rule: `SPAWN-TUNING.md` §B14 |
**Missing-texture check (B6)**: in creative, open JEI, search `@chipped`, `@rechiseled` and
`@handcrafted`, scroll each list, and place a 4×4 wall of 3 connected-texture Rechiseled
blocks. Any magenta/black texture or broken CTM seam fails the check.
### Rough budget
Assumes ~4 min per launch (B1 measures the real figure) and ~3 min per scene for 3 × 45 s.
| Block | Launches (all machines) | Hours |
| --- | --- | --- |
| B0–B2 | ~12 | ~5 |
| B3 | ~20 | ~4 |
| B4 | ~14 | ~3 |
| B5 | ~12 + 1.5 h decay | ~4 |
| B6 | 12 cold launches | ~2 |
| B7–B11 | ~45 | ~7 |
| **Total** | | **~25 h machine time**, ~10 h per machine |
## 5. Output
One CSV row per run, using the phase0 recording sheet plus `config_id`, `jvm_set`, `xmx`,
`scene`, `world_sha`, `pack_commit`. When the matrix is done, write the winners back into
`proposed-configs/build.py` (DEFAULT/DECK/HIGH), regenerate, and only then present the files
for shipping.
+176
View File
@@ -0,0 +1,176 @@
# 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.
+134
View File
@@ -0,0 +1,134 @@
# Onion MC: performance settings for players
These are things the pack can't set for you, plus the settings to change for your machine.
Values marked *(provisional)* are waiting on Phase 2 benchmarks and may change.
## 1. Which tier are you?
| Tier | Typical machine | Examples |
| --- | --- | --- |
| **Deck** | handheld or integrated graphics, shared RAM | Steam Deck, laptop iGPU |
| **Mid** (pack default) | 4–6 core CPU, ~6 GB VRAM graphics card | GTX 1060 6 GB, RX 580 |
| **High** | 8 GB+ VRAM card | GTX 1080 Ti, RTX 3060+ |
The pack's defaults are the **mid** tier. They're applied only when the config file doesn't
exist yet. **If you played before the defaults shipped**, close the game and delete these
files from the instance folder to pick them up on the next launch. This resets those mods'
settings.
```
config/DistantHorizons.toml
config/sodium-options.json
config/entity_model_features.json
config/flywheel-client.toml
config/lambdynlights.toml
config/ssrd-common.toml
config/sound_physics_remastered/soundphysics.properties
```
Don't delete `options.txt`: that resets all your video, audio and control settings,
keybinds included. Set the video values in the table below by hand instead.
Then change these for your tier *(provisional)*:
| Setting | Where | Deck | Mid | High |
| --- | --- | --- | --- | --- |
| LOD render distance | Options → Distant Horizons button | 64 | 128 | 128 (192–256 if you have ≥16 GB free RAM and 6+ cores) |
| DH ambient occlusion (SSAO) | DH → Advanced → Graphics | off | off | on |
| DH quality preset | DH quick options | Low | Medium | Medium |
| Biome blend | Video Settings | off | 3×3 | 5×5 |
| Mipmap levels | Video Settings | 2 | 2 | 4 |
| Particles / entity shadows | Video Settings | Decreased / off | All / on | All / on |
| Leaves & weather quality | Sodium → Quality | Fast | Default | Default |
| Sub-level (airship) render distance | Sodium video settings slider | 16 | 16 | 32 |
| Dynamic lights | LambDynamicLights settings | Fastest, entities off | Fast | Fast, entities off |
Leave **render distance at 8**. The server sends 8 chunks, so anything higher loads nothing
extra in multiplayer. Distant Horizons draws everything beyond that.
## 2. Java memory and garbage collector (Prism → Edit Instance → Settings → Java)
Use **Java 21** (Temurin 21 recommended). More memory is *not* faster: a bigger heap makes
each GC pause longer.
| Your machine | Max memory (`-Xmx`) | Min memory |
| --- | --- | --- |
| Steam Deck (16 GB shared with the GPU) | **5 GB**, never above 6 | leave low (512 MB) |
| 16 GB PC | **6 GB** | leave low |
| 32 GB PC | **6–8 GB** | may equal max |
| PC with little free RAM (other apps/containers running) | min(6 GB, free RAM − 2 GB) | leave low |
**JVM arguments** *(provisional; Phase 2 block B4 picks the winner)*. Paste one line into
*Java arguments* (Prism adds `-Xmx` itself from the memory box):
- **Default (all machines):** client-tuned G1
```
-XX:+UseG1GC -XX:MaxGCPauseMillis=37 -XX:G1HeapRegionSize=16M -XX:+UnlockExperimentalVMOptions -XX:G1NewSizePercent=23 -XX:G1ReservePercent=20 -XX:G1MixedGCCountTarget=3 -XX:G1HeapWastePercent=20 -XX:InitiatingHeapOccupancyPercent=10 -XX:G1RSetUpdatingPauseTimePercent=0 -XX:MaxTenuringThreshold=1 -XX:GCTimeRatio=99 -XX:+DisableExplicitGC -XX:+PerfDisableSharedMem
```
- **32 GB machines, if you still get stutter:** Generational ZGC. Distant Horizons
recommends it in its log warning about G1.
```
-XX:+UseZGC -XX:+ZGenerational -XX:+DisableExplicitGC -XX:+PerfDisableSharedMem
```
`-XX:+ZGenerational` is required: on Java 21, `-XX:+UseZGC` alone selects the older
non-generational ZGC. Avoid ZGC on the Deck or on low-RAM PCs. It needs spare memory and
CPU those machines don't have.
Don't add `-XX:+AlwaysPreTouch` on the Deck or on low-RAM PCs. Don't copy
`-XX:ShenandoahGCMode=iu` from older guides: Java 21 refuses to start with it.
## 3. Shaders
*Pending the owner's shader policy. Interim advice:* **shaders off** is the pack default.
They cost more in this pack than usual:
- With any shaderpack on, **Create's GPU renderer (Flywheel) switches off**, so every
rotating shaft and machine is drawn the slow way, and drawn again for shadows.
- Shader **shadows corrupt airships** (Sable sub-levels; Iris issue #3164).
- Distant Horizons adds a shadow pass over all its terrain.
If you want shaders anyway:
| Tier | Pack | Profile (shader settings screen) |
| --- | --- | --- |
| Deck | MakeUp – Ultra Fast | `shadowless_low` or `shadowless_medium`. These have no shadows, so no airship corruption. |
| Mid | MakeUp – Ultra Fast | `shadowless_medium`, or `low` if you accept shadow artefacts on airships |
| High | Complementary Reimagined | `LOW` or `MEDIUM` (MEDIUM = 8-chunk shadows). Avoid `HIGH` and above near Create builds. |
## 4. Resource packs
The pack downloads these but doesn't enable them:
- **Fresh Animations** (+ **FA: Extensions**): re-models and animates every vanilla mob. It's
the most expensive optional visual in the pack, especially with shaders and near villages
or farms. **Deck: don't use it.** Mid: Fresh Animations alone is OK, but skip Extensions.
High: fine.
- **Better Leaves**: much denser leaves. The pack has no leaf-culling mod for it yet, so
**avoid it on Deck and mid** until one is added.
## 5. Steam Deck
- Run Prism in **Desktop Mode** (flatpak from prismlauncher.org).
- Prism → Edit Instance → Settings → **Environment variables**: `mesa_glthread=true`.
Minecraft uses Mesa's OpenGL driver (radeonsi), and glthread helps the Deck's CPU-bound
draw path.
- Prism → Custom Commands → **Wrapper command**: `gamemoderun` (the flatpak reaches GameMode
through the desktop portal).
- Native 1280×800. Cap FPS at the panel rate (60 LCD / 90 OLED) **after** tuning.
Uncapped only matters for benchmarks.
## 6. Other platforms
- **Windows + NVIDIA**: NVIDIA Control Panel → Manage 3D settings → Program settings → your
Java `javaw.exe` → *Power management mode: Prefer maximum performance*. Leave *Threaded
optimization* alone: the driver ignores it for Minecraft, and Sodium handles it.
- **Linux + NVIDIA**: use the proprietary driver, not nouveau. Sodium already sets
`__GL_THREADED_OPTIMIZATIONS=0`, so you don't need to.
## 7. Still slow? Change these first
1. Turn shaders off.
2. Lower the Distant Horizons LOD distance (biggest always-on cost).
3. Disable Fresh Animations.
4. Lower the airship render distance (Sodium video settings).
5. Lower biome blend (helps stutter while flying, not steady FPS).
+53
View File
@@ -0,0 +1,53 @@
# Client performance analysis — Phase 1 (static baseline)
Goal: map the **client-side optimisation surface** of Onion MC (212 mods, MC 1.21.1 /
NeoForge 21.1.251) and rank the levers *before* benchmarking. Nothing here has been
measured on a client yet; Phase 2 benchmarks it (see `../../phase0/` for the fleet harness).
**Start with [`LEVERS.md`](LEVERS.md)**: the prioritised lever list, plus add and remove
candidates.
## Layout
| Path | What |
| --- | --- |
| `LEVERS.md` | Synthesis: ranked levers, per-machine expectations, add/remove candidates, corrections |
| `proposed-configs/` | **Phase 1b**: staged tuned configs (tiers + bench variants, generated by `build.py`) and the delivery-mechanism decision (README §1) |
| `BENCH-MATRIX.md` | **Phase 1b**: the Phase 2 run sheet (world, scenes S1–S6, config IDs, blocks B0–B13, decision rules) |
| `bench/onion-bench/` | Datapack for the bench world: scene teleports, `/fill` setups, sound storm, 120 s tour |
| `SPAWN-TUNING.md` | Shipped server spawn tuning (Alex's Mobs, Fowl Play, Critters): evidence and the B14 measurement plan |
| `PLAYER-TUNING.md` | **Phase 1b**: player guide (tiers, JVM/heap, shaders, resource packs, Deck) |
| `data/jarscan-report.md` | Static scan of every pinned jar: per-frame mixins, per-frame event hooks, atlas/model/shader/sound payloads, entity counts |
| `data/jarscan.json` | Raw scan output (re-generate, don't hand-edit) |
| `data/observed-default-configs/` | Generated defaults from the last real client launch (2026-09-24, older 110-mod pack). Use these to see actual default values |
| `research/r1-render-core.md` | Sodium / Iris + shaderpacks / Flywheel / ModernFix / ImmediatelyFast / EntityCulling / mipmaps |
| `research/r2-dh-xaero.md` | Distant Horizons 3.3.2 client + Xaero's minimap / world map |
| `research/r3-entity-animation.md` | EMF/ETF + Fresh Animations, GeckoLib, player animation mods, LambDynamicLights |
| `research/r4-effects-audio-hud.md` | Sound Physics, AmbientSounds, Subtle Effects, Perception, WATUT, TF / Alex's Caves visuals, HUD mods |
| `research/r5-candidate-mods.md` | Perf mods *not* in the pack (availability on NeoForge 1.21.1, compat, verdict) |
| `research/r6-jvm-platform.md` | GC / heap / JVM, Mesa / NVIDIA driver, Steam Deck platform levers |
| `research/r7-sable-create-client.md` | Sable / Aeronautics / SSRD / Create + Flywheel client cost |
| `research/r8-sweep-content-mobs.md` | Known-issue sweep: mob and content mods |
| `research/r9-sweep-libs-ui-misc.md` | Known-issue sweep: libraries, UI, misc client hooks |
| `scripts/jarscan.py`, `scripts/report.py` | The scanner |
| `scripts/jx.py` | Jar inspection without unzip/java: `ls`, `cat`, `str` (class-file string constants); nested jars as `outer.jar!inner.jar` |
Re-run the scan after the mod set changes:
```sh
# jars: download every mods/*.pw.toml url into a dir (analysis/audit/fetch.py does this)
python3 scripts/jarscan.py /tmp/onion/jars ../.. data/jarscan.json
python3 scripts/report.py data/jarscan.json data/jarscan-report.md
```
## Method and confidence
- **Static scan.** The scanner parses class-file annotations to get real `@Mixin` targets and
injector kinds. It reads constant pools for event-type references and PNG headers for
texture sizes, and it skips the Fabric-only payloads of multi-loader jars. The numbers are
*surface*, not cost: a mixin on `LevelRenderer` may be a one-line early-return.
- **Research.** Nine subagent briefs (deepseek-v4.1-flash / glm-5.3-flash) are tagged
VERIFIED / DOCUMENTED / COMMUNITY / INFERENCE with URLs. Load-bearing claims were
spot-checked by the orchestrator. Corrections are listed in `LEVERS.md` §7.
- **Observed defaults.** These come from the old Prism instance's generated configs. They
are the authoritative "what a player gets today" values for the mods that existed then.
+63
View File
@@ -0,0 +1,63 @@
# Spawn tuning (server-side lever 3)
**Owner brief (2026-10-14):** tune spawns, but not in a way that hurts gameplay much. All
three files ship **enforced** from `config/` (this repo is the server directory, and clients
get them too, which only matters in singleplayer). Each file is **partial**: the mod fills
every other key with its default. Research came from three subagent briefs (2026-10-14);
every load-bearing fact below was re-checked against the pinned jar or the source at the
matching version. **Unmeasured:** bench block B14 (below) checks it.
## What shipped
### Alex's Mobs 1.22.17: `config/alexsmobs-common.toml`
- Format: NeoForge `ModConfigSpec` **COMMON** (`AlexsMobs.class`: `registerConfig(COMMON, …)`;
no custom filename). Keys are flat under `[spawning]` (`CommonConfig`: `push("spawning")`;
`buildInt` ignores its category argument). A partial file gets corrected and re-saved by
NeoForge.
- Changed (weight, 0–1000; group sizes are hard-coded in `AMWorldRegistry`): gazelle 40→20
(herds of 7), capuchinMonkey 28→14 (groups of 9–16), elephant 30→15, straddler 70→35,
rockyRoller 60→30, underminer 50→25, blobfish 30→15, mudskipper 28→14, caiman 29→15.
Everything rare is untouched.
- Issue #62 on Raguto/AlexsMobs-1.21.1 ("tons of monsters" / inflated *vanilla* spawns) is
open, unexplained, and no config addresses it. If it shows up, A/B test a world without
Alex's Mobs.
### Fowl Play 1.2.3: `config/fowlplay.json5`
- Format: YACL v3 JSON5 (`YACLIntegration`: `fowlplay.json5`). YACL 3.8.2
(`GsonConfigSerializer.loadSafely`) keeps defaults for missing fields and re-saves; a bad
value is logged and left at its default, not a crash.
- Changed (default → new): duck weight 4→2, max group 6→4 · goose 4→2, 4→3 · gull 5→3, 8→5 ·
sparrow 20→12, 6→4. Songbirds and raptors are untouched.
- Context: #214 (~400 ducks on rivers) is open. 1.2.1 added a 200-tick spawn gate. The
category caps (`fowlplay_birds` 20, `fowlplay_ambient_birds` 15) scale with loaded chunks
like vanilla, and only became configurable *after* 1.2.3. If counts are still high, the
next lever is a Paxi datapack that narrows `#fowlplay:spawns_ducks` (currently includes
forest tags), added through `analysis/audit/gen_fixes.py`.
### Critters and Companions 2.7.0: `config/crittersandcompanions-common.json5`
- It *does* have a spawn config (contrary to LEVERS' "no config"): YACL JSON5, nested
`spawning` object, one `List<SpawnEntry>` per mob. `SpawnEntry` is a record
`{biomeSpec, weight, min, max}` with no custom type adapter, so it serialises as plain Gson
record JSON. The jar has no biome-modifier JSON, so a datapack *cannot* reduce these spawns.
- A listed mob's list **replaces** its defaults, so each changed mob lists all its default
biomes. `"configVersion": 2` is required, otherwise the v1→v2 migration runs.
- Changed: sea bunny (the worst: warm ocean 32 × groups ≤4 → 20 × ≤3; other oceans 16 → 10,
max 4 → 3), leaf insect jungle 14→8 / forest 6→4, dragonfly lush 10→6, and the
ladybug / stag beetle / roly poly / snail / stick bug / weevil rules about −40 %. Unchanged:
jumping spider, red panda, ferret, dumbo octopus, otter, koi, shima enaga.
## Verify on first boot (part of BENCH-MATRIX §0)
- `latest.log` has no YACL "Failed to deserialize" lines for `fowlplay` or `crittersandcompanions`.
- After the first launch, each file has been re-saved in full with our values intact.
(In the repo working tree that shows up as a diff: **don't commit the expanded files**
unless you mean to.)
## B14: measure it (P2, singleplayer, Creative, `doMobSpawning true`)
Fresh world `onionspawn`, at a river/forest edge (ducks), a jungle (capuchins, leaf
insects) and a warm ocean (sea bunnies). Stand still 10 minutes at each, then count with
`/execute if entity @e[type=<id>,distance=..128]` for `fowlplay:duck`, `fowlplay:gull`,
`alexsmobs:capuchin_monkey`, `crittersandcompanions:sea_bunny`, and in total
(`@e[type=!player,distance=..128]`). Compare against the same world with these three
config files removed (defaults). **Keep the tuning** if totals drop and every species still
appears; **back it off** if a species effectively disappears. Spot-check that vanilla
passives (cows, sheep) still spawn normally.
@@ -0,0 +1 @@
tp @a 0 200 0 0 20
@@ -0,0 +1 @@
tp @a 1023 165 -10 0 40
@@ -0,0 +1,98 @@
# Create floor: 2304 kinetic block entities. Verify they rotate (see BENCH-MATRIX.md).
fill 998 150 -1 1048 150 48 minecraft:smooth_stone
fill 1000 151 0 1047 151 0 create:shaft[axis=x]
setblock 999 151 0 create:creative_motor[facing=east]
fill 1000 151 1 1047 151 1 create:cogwheel[axis=x]
setblock 999 151 1 create:creative_motor[facing=east]
fill 1000 151 2 1047 151 2 create:shaft[axis=x]
setblock 999 151 2 create:creative_motor[facing=east]
fill 1000 151 3 1047 151 3 create:cogwheel[axis=x]
setblock 999 151 3 create:creative_motor[facing=east]
fill 1000 151 4 1047 151 4 create:shaft[axis=x]
setblock 999 151 4 create:creative_motor[facing=east]
fill 1000 151 5 1047 151 5 create:cogwheel[axis=x]
setblock 999 151 5 create:creative_motor[facing=east]
fill 1000 151 6 1047 151 6 create:shaft[axis=x]
setblock 999 151 6 create:creative_motor[facing=east]
fill 1000 151 7 1047 151 7 create:cogwheel[axis=x]
setblock 999 151 7 create:creative_motor[facing=east]
fill 1000 151 8 1047 151 8 create:shaft[axis=x]
setblock 999 151 8 create:creative_motor[facing=east]
fill 1000 151 9 1047 151 9 create:cogwheel[axis=x]
setblock 999 151 9 create:creative_motor[facing=east]
fill 1000 151 10 1047 151 10 create:shaft[axis=x]
setblock 999 151 10 create:creative_motor[facing=east]
fill 1000 151 11 1047 151 11 create:cogwheel[axis=x]
setblock 999 151 11 create:creative_motor[facing=east]
fill 1000 151 12 1047 151 12 create:shaft[axis=x]
setblock 999 151 12 create:creative_motor[facing=east]
fill 1000 151 13 1047 151 13 create:cogwheel[axis=x]
setblock 999 151 13 create:creative_motor[facing=east]
fill 1000 151 14 1047 151 14 create:shaft[axis=x]
setblock 999 151 14 create:creative_motor[facing=east]
fill 1000 151 15 1047 151 15 create:cogwheel[axis=x]
setblock 999 151 15 create:creative_motor[facing=east]
fill 1000 151 16 1047 151 16 create:shaft[axis=x]
setblock 999 151 16 create:creative_motor[facing=east]
fill 1000 151 17 1047 151 17 create:cogwheel[axis=x]
setblock 999 151 17 create:creative_motor[facing=east]
fill 1000 151 18 1047 151 18 create:shaft[axis=x]
setblock 999 151 18 create:creative_motor[facing=east]
fill 1000 151 19 1047 151 19 create:cogwheel[axis=x]
setblock 999 151 19 create:creative_motor[facing=east]
fill 1000 151 20 1047 151 20 create:shaft[axis=x]
setblock 999 151 20 create:creative_motor[facing=east]
fill 1000 151 21 1047 151 21 create:cogwheel[axis=x]
setblock 999 151 21 create:creative_motor[facing=east]
fill 1000 151 22 1047 151 22 create:shaft[axis=x]
setblock 999 151 22 create:creative_motor[facing=east]
fill 1000 151 23 1047 151 23 create:cogwheel[axis=x]
setblock 999 151 23 create:creative_motor[facing=east]
fill 1000 151 24 1047 151 24 create:shaft[axis=x]
setblock 999 151 24 create:creative_motor[facing=east]
fill 1000 151 25 1047 151 25 create:cogwheel[axis=x]
setblock 999 151 25 create:creative_motor[facing=east]
fill 1000 151 26 1047 151 26 create:shaft[axis=x]
setblock 999 151 26 create:creative_motor[facing=east]
fill 1000 151 27 1047 151 27 create:cogwheel[axis=x]
setblock 999 151 27 create:creative_motor[facing=east]
fill 1000 151 28 1047 151 28 create:shaft[axis=x]
setblock 999 151 28 create:creative_motor[facing=east]
fill 1000 151 29 1047 151 29 create:cogwheel[axis=x]
setblock 999 151 29 create:creative_motor[facing=east]
fill 1000 151 30 1047 151 30 create:shaft[axis=x]
setblock 999 151 30 create:creative_motor[facing=east]
fill 1000 151 31 1047 151 31 create:cogwheel[axis=x]
setblock 999 151 31 create:creative_motor[facing=east]
fill 1000 151 32 1047 151 32 create:shaft[axis=x]
setblock 999 151 32 create:creative_motor[facing=east]
fill 1000 151 33 1047 151 33 create:cogwheel[axis=x]
setblock 999 151 33 create:creative_motor[facing=east]
fill 1000 151 34 1047 151 34 create:shaft[axis=x]
setblock 999 151 34 create:creative_motor[facing=east]
fill 1000 151 35 1047 151 35 create:cogwheel[axis=x]
setblock 999 151 35 create:creative_motor[facing=east]
fill 1000 151 36 1047 151 36 create:shaft[axis=x]
setblock 999 151 36 create:creative_motor[facing=east]
fill 1000 151 37 1047 151 37 create:cogwheel[axis=x]
setblock 999 151 37 create:creative_motor[facing=east]
fill 1000 151 38 1047 151 38 create:shaft[axis=x]
setblock 999 151 38 create:creative_motor[facing=east]
fill 1000 151 39 1047 151 39 create:cogwheel[axis=x]
setblock 999 151 39 create:creative_motor[facing=east]
fill 1000 151 40 1047 151 40 create:shaft[axis=x]
setblock 999 151 40 create:creative_motor[facing=east]
fill 1000 151 41 1047 151 41 create:cogwheel[axis=x]
setblock 999 151 41 create:creative_motor[facing=east]
fill 1000 151 42 1047 151 42 create:shaft[axis=x]
setblock 999 151 42 create:creative_motor[facing=east]
fill 1000 151 43 1047 151 43 create:cogwheel[axis=x]
setblock 999 151 43 create:creative_motor[facing=east]
fill 1000 151 44 1047 151 44 create:shaft[axis=x]
setblock 999 151 44 create:creative_motor[facing=east]
fill 1000 151 45 1047 151 45 create:cogwheel[axis=x]
setblock 999 151 45 create:creative_motor[facing=east]
fill 1000 151 46 1047 151 46 create:shaft[axis=x]
setblock 999 151 46 create:creative_motor[facing=east]
fill 1000 151 47 1047 151 47 create:cogwheel[axis=x]
setblock 999 151 47 create:creative_motor[facing=east]
@@ -0,0 +1 @@
tp @a 2011 158 -6 0 35
@@ -0,0 +1,84 @@
# Mob pen: 30 villagers, 15 cows, 15 sheep, 10 torch-carrying allays, 10 dropped torches.
fill 1999 150 -1 2024 157 24 minecraft:glass hollow
fill 2000 150 0 2023 150 23 minecraft:grass_block
kill @e[type=!player,x=1990,y=140,z=-10,dx=45,dy=30,dz=45]
summon minecraft:villager 2002 151 2 {PersistenceRequired:1b}
summon minecraft:villager 2004 151 2 {PersistenceRequired:1b}
summon minecraft:villager 2006 151 2 {PersistenceRequired:1b}
summon minecraft:villager 2008 151 2 {PersistenceRequired:1b}
summon minecraft:villager 2010 151 2 {PersistenceRequired:1b}
summon minecraft:villager 2012 151 2 {PersistenceRequired:1b}
summon minecraft:villager 2014 151 2 {PersistenceRequired:1b}
summon minecraft:villager 2016 151 2 {PersistenceRequired:1b}
summon minecraft:villager 2018 151 2 {PersistenceRequired:1b}
summon minecraft:villager 2020 151 2 {PersistenceRequired:1b}
summon minecraft:villager 2002 151 5 {PersistenceRequired:1b}
summon minecraft:villager 2004 151 5 {PersistenceRequired:1b}
summon minecraft:villager 2006 151 5 {PersistenceRequired:1b}
summon minecraft:villager 2008 151 5 {PersistenceRequired:1b}
summon minecraft:villager 2010 151 5 {PersistenceRequired:1b}
summon minecraft:villager 2012 151 5 {PersistenceRequired:1b}
summon minecraft:villager 2014 151 5 {PersistenceRequired:1b}
summon minecraft:villager 2016 151 5 {PersistenceRequired:1b}
summon minecraft:villager 2018 151 5 {PersistenceRequired:1b}
summon minecraft:villager 2020 151 5 {PersistenceRequired:1b}
summon minecraft:villager 2002 151 8 {PersistenceRequired:1b}
summon minecraft:villager 2004 151 8 {PersistenceRequired:1b}
summon minecraft:villager 2006 151 8 {PersistenceRequired:1b}
summon minecraft:villager 2008 151 8 {PersistenceRequired:1b}
summon minecraft:villager 2010 151 8 {PersistenceRequired:1b}
summon minecraft:villager 2012 151 8 {PersistenceRequired:1b}
summon minecraft:villager 2014 151 8 {PersistenceRequired:1b}
summon minecraft:villager 2016 151 8 {PersistenceRequired:1b}
summon minecraft:villager 2018 151 8 {PersistenceRequired:1b}
summon minecraft:villager 2020 151 8 {PersistenceRequired:1b}
summon minecraft:cow 2002 151 12 {PersistenceRequired:1b}
summon minecraft:cow 2006 151 12 {PersistenceRequired:1b}
summon minecraft:cow 2010 151 12 {PersistenceRequired:1b}
summon minecraft:cow 2014 151 12 {PersistenceRequired:1b}
summon minecraft:cow 2018 151 12 {PersistenceRequired:1b}
summon minecraft:cow 2002 151 15 {PersistenceRequired:1b}
summon minecraft:cow 2006 151 15 {PersistenceRequired:1b}
summon minecraft:cow 2010 151 15 {PersistenceRequired:1b}
summon minecraft:cow 2014 151 15 {PersistenceRequired:1b}
summon minecraft:cow 2018 151 15 {PersistenceRequired:1b}
summon minecraft:cow 2002 151 18 {PersistenceRequired:1b}
summon minecraft:cow 2006 151 18 {PersistenceRequired:1b}
summon minecraft:cow 2010 151 18 {PersistenceRequired:1b}
summon minecraft:cow 2014 151 18 {PersistenceRequired:1b}
summon minecraft:cow 2018 151 18 {PersistenceRequired:1b}
summon minecraft:sheep 2003 151 13 {PersistenceRequired:1b}
summon minecraft:sheep 2007 151 13 {PersistenceRequired:1b}
summon minecraft:sheep 2011 151 13 {PersistenceRequired:1b}
summon minecraft:sheep 2015 151 13 {PersistenceRequired:1b}
summon minecraft:sheep 2019 151 13 {PersistenceRequired:1b}
summon minecraft:sheep 2003 151 16 {PersistenceRequired:1b}
summon minecraft:sheep 2007 151 16 {PersistenceRequired:1b}
summon minecraft:sheep 2011 151 16 {PersistenceRequired:1b}
summon minecraft:sheep 2015 151 16 {PersistenceRequired:1b}
summon minecraft:sheep 2019 151 16 {PersistenceRequired:1b}
summon minecraft:sheep 2003 151 19 {PersistenceRequired:1b}
summon minecraft:sheep 2007 151 19 {PersistenceRequired:1b}
summon minecraft:sheep 2011 151 19 {PersistenceRequired:1b}
summon minecraft:sheep 2015 151 19 {PersistenceRequired:1b}
summon minecraft:sheep 2019 151 19 {PersistenceRequired:1b}
summon minecraft:allay 2002 154 20 {PersistenceRequired:1b,HandItems:[{id:"minecraft:torch",count:1},{}]}
summon minecraft:allay 2004 154 20 {PersistenceRequired:1b,HandItems:[{id:"minecraft:torch",count:1},{}]}
summon minecraft:allay 2006 154 20 {PersistenceRequired:1b,HandItems:[{id:"minecraft:torch",count:1},{}]}
summon minecraft:allay 2008 154 20 {PersistenceRequired:1b,HandItems:[{id:"minecraft:torch",count:1},{}]}
summon minecraft:allay 2010 154 20 {PersistenceRequired:1b,HandItems:[{id:"minecraft:torch",count:1},{}]}
summon minecraft:allay 2012 154 20 {PersistenceRequired:1b,HandItems:[{id:"minecraft:torch",count:1},{}]}
summon minecraft:allay 2014 154 20 {PersistenceRequired:1b,HandItems:[{id:"minecraft:torch",count:1},{}]}
summon minecraft:allay 2016 154 20 {PersistenceRequired:1b,HandItems:[{id:"minecraft:torch",count:1},{}]}
summon minecraft:allay 2018 154 20 {PersistenceRequired:1b,HandItems:[{id:"minecraft:torch",count:1},{}]}
summon minecraft:allay 2020 154 20 {PersistenceRequired:1b,HandItems:[{id:"minecraft:torch",count:1},{}]}
summon minecraft:item 2002 151 22 {Age:-32768s,PickupDelay:32767s,Item:{id:"minecraft:torch",count:1}}
summon minecraft:item 2004 151 22 {Age:-32768s,PickupDelay:32767s,Item:{id:"minecraft:torch",count:1}}
summon minecraft:item 2006 151 22 {Age:-32768s,PickupDelay:32767s,Item:{id:"minecraft:torch",count:1}}
summon minecraft:item 2008 151 22 {Age:-32768s,PickupDelay:32767s,Item:{id:"minecraft:torch",count:1}}
summon minecraft:item 2010 151 22 {Age:-32768s,PickupDelay:32767s,Item:{id:"minecraft:torch",count:1}}
summon minecraft:item 2012 151 22 {Age:-32768s,PickupDelay:32767s,Item:{id:"minecraft:torch",count:1}}
summon minecraft:item 2014 151 22 {Age:-32768s,PickupDelay:32767s,Item:{id:"minecraft:torch",count:1}}
summon minecraft:item 2016 151 22 {Age:-32768s,PickupDelay:32767s,Item:{id:"minecraft:torch",count:1}}
summon minecraft:item 2018 151 22 {Age:-32768s,PickupDelay:32767s,Item:{id:"minecraft:torch",count:1}}
summon minecraft:item 2020 151 22 {Age:-32768s,PickupDelay:32767s,Item:{id:"minecraft:torch",count:1}}
@@ -0,0 +1 @@
tp @a 3000 172 -10 0 5
@@ -0,0 +1,12 @@
# Run once after creating the bench world (creative, cheats on).
gamerule doDaylightCycle false
gamerule doWeatherCycle false
gamerule doMobSpawning false
gamerule doPatrolSpawning false
gamerule doTraderSpawning false
gamerule doInsomnia false
gamerule doFireTick false
gamerule announceAdvancements false
time set 6000
weather clear
difficulty normal
@@ -0,0 +1 @@
schedule clear bench:storm_tick
@@ -0,0 +1 @@
function bench:storm_tick
@@ -0,0 +1,26 @@
# Called every tick while the storm runs. Stop with bench:storm_off.
playsound create:mechanical_press_activation block @a 1004 151 4 1 1
playsound create:mechanical_press_activation block @a 1020 151 4 1 1
playsound create:mechanical_press_activation block @a 1036 151 4 1 1
playsound create:mechanical_press_activation block @a 1044 151 20 1 1
playsound create:mechanical_press_activation block @a 1004 151 28 1 1
playsound create:mechanical_press_activation block @a 1020 151 44 1 1
playsound create:mechanical_press_activation block @a 1036 151 36 1 1
playsound create:mechanical_press_activation block @a 1044 151 44 1 1
playsound create:cogs block @a 1004 151 4 1 1
playsound create:cogs block @a 1020 151 4 1 1
playsound create:cogs block @a 1036 151 4 1 1
playsound create:cogs block @a 1044 151 20 1 1
playsound create:cogs block @a 1004 151 28 1 1
playsound create:cogs block @a 1020 151 44 1 1
playsound create:cogs block @a 1036 151 36 1 1
playsound create:cogs block @a 1044 151 44 1 1
playsound create:mixing block @a 1004 151 4 1 1
playsound create:mixing block @a 1020 151 4 1 1
playsound create:mixing block @a 1036 151 4 1 1
playsound create:mixing block @a 1044 151 20 1 1
playsound create:mixing block @a 1004 151 28 1 1
playsound create:mixing block @a 1020 151 44 1 1
playsound create:mixing block @a 1036 151 36 1 1
playsound create:mixing block @a 1044 151 44 1 1
schedule function bench:storm_tick 1t
@@ -0,0 +1,2 @@
tp @a 0 180 0 -90 15
schedule function bench:tour/step1 100t
@@ -0,0 +1,2 @@
tp @a 160 180 0 -90 15
schedule function bench:tour/step2 100t
@@ -0,0 +1,2 @@
tp @a 1600 180 0 -90 15
schedule function bench:tour/step11 100t
@@ -0,0 +1,2 @@
tp @a 1760 180 0 -90 15
schedule function bench:tour/step12 100t
@@ -0,0 +1,2 @@
tp @a 1920 180 0 -90 15
schedule function bench:tour/step13 100t
@@ -0,0 +1,2 @@
tp @a 2080 180 0 -90 15
schedule function bench:tour/step14 100t
@@ -0,0 +1,2 @@
tp @a 2240 180 0 -90 15
schedule function bench:tour/step15 100t
@@ -0,0 +1,2 @@
tp @a 2400 180 0 -90 15
schedule function bench:tour/step16 100t
@@ -0,0 +1,2 @@
tp @a 2560 180 0 -90 15
schedule function bench:tour/step17 100t
@@ -0,0 +1,2 @@
tp @a 2720 180 0 -90 15
schedule function bench:tour/step18 100t
@@ -0,0 +1,2 @@
tp @a 2880 180 0 -90 15
schedule function bench:tour/step19 100t
@@ -0,0 +1,2 @@
tp @a 3040 180 0 -90 15
schedule function bench:tour/step20 100t
@@ -0,0 +1,2 @@
tp @a 320 180 0 -90 15
schedule function bench:tour/step3 100t
@@ -0,0 +1,2 @@
tp @a 3200 180 0 -90 15
schedule function bench:tour/step21 100t
@@ -0,0 +1,2 @@
tp @a 3360 180 0 -90 15
schedule function bench:tour/step22 100t
@@ -0,0 +1,2 @@
tp @a 3520 180 0 -90 15
schedule function bench:tour/step23 100t
@@ -0,0 +1,2 @@
tp @a 3680 180 0 -90 15
tellraw @a {"text":"bench tour done"}
@@ -0,0 +1,2 @@
tp @a 480 180 0 -90 15
schedule function bench:tour/step4 100t
@@ -0,0 +1,2 @@
tp @a 640 180 0 -90 15
schedule function bench:tour/step5 100t
@@ -0,0 +1,2 @@
tp @a 800 180 0 -90 15
schedule function bench:tour/step6 100t
@@ -0,0 +1,2 @@
tp @a 960 180 0 -90 15
schedule function bench:tour/step7 100t
@@ -0,0 +1,2 @@
tp @a 1120 180 0 -90 15
schedule function bench:tour/step8 100t
@@ -0,0 +1,2 @@
tp @a 1280 180 0 -90 15
schedule function bench:tour/step9 100t
@@ -0,0 +1,2 @@
tp @a 1440 180 0 -90 15
schedule function bench:tour/step10 100t
@@ -0,0 +1 @@
function bench:tour/step0
@@ -0,0 +1,23 @@
schedule clear bench:tour/step1
schedule clear bench:tour/step2
schedule clear bench:tour/step3
schedule clear bench:tour/step4
schedule clear bench:tour/step5
schedule clear bench:tour/step6
schedule clear bench:tour/step7
schedule clear bench:tour/step8
schedule clear bench:tour/step9
schedule clear bench:tour/step10
schedule clear bench:tour/step11
schedule clear bench:tour/step12
schedule clear bench:tour/step13
schedule clear bench:tour/step14
schedule clear bench:tour/step15
schedule clear bench:tour/step16
schedule clear bench:tour/step17
schedule clear bench:tour/step18
schedule clear bench:tour/step19
schedule clear bench:tour/step20
schedule clear bench:tour/step21
schedule clear bench:tour/step22
schedule clear bench:tour/step23
@@ -0,0 +1 @@
{"pack": {"pack_format": 48, "description": "Onion MC client-perf bench scenes (Phase 2)"}}
+312
View File
@@ -0,0 +1,312 @@
# Static client-perf surface scan
Generated by `scripts/jarscan.py` + `scripts/report.py` from the exact jars pinned in `mods/*.pw.toml`.
All numbers are static (what the jar *contains*), not runtime cost. They tell us where cost *can* come from.
- **frame mixins** = mixin classes whose `@Mixin` target is on a per-frame path (LevelRenderer, GameRenderer, entity/item/block-entity renderers, Gui, ParticleEngine, blaze3d, Sodium, Iris, Camera).
- **risky** = `@Overwrite`/`@Redirect` injectors inside those frame mixins (replace vanilla logic; most likely to conflict with Sodium/Iris/ModernFix).
- **frame events** = NeoForge/Architectury/Fabric per-frame event types referenced by the mod’s classes.
- **atlas sprites** = block+item textures in 16×16-sprite equivalents (what lands in `blocks.png-atlas`, mipmapped, in VRAM).
## Pack totals (client-loaded mods)
| metric | value |
| --- | --- |
| client-loaded mods | 209 (server-only: 3) |
| mixin classes (client-loaded) | 3883 |
| frame-path mixin classes | 951 |
| frame-path @Overwrite/@Redirect | 121 |
| mods hooking ≥1 per-frame event | 58 |
| mods hooking client/player/entity tick events | 79 |
| block+item textures | 25277 files, 15.4 Mpx ≈ 60138 16×16 sprites |
| implied blocks atlas (lower bound, + vanilla) | ≥ 4096×4096 ; ×4 B ×1.33 mip ≈ 85 MiB VRAM at the square size |
| animated block/item textures | 312 |
| entity textures | 37.7 Mpx |
| models / blockstates | 69442 / 16361 |
| entity types (lang proxy) | 603 |
| blocks / items (lang proxy) | 15422 / 5178 |
| geckolib geo+animation files | 264 |
| vanilla core-shader overrides | sable, sodium, xaeros-minimap, xaeros-world-map |
| post-effect files | alexs-caves-(unofficial-port)(7), charta(1), citadel-(1.21.1-port)(1), exposure(19), lodestonelib(2), playerrevive(1), shatterbyte-lib(1) |
## Per-frame mixin surface
| mod | side | frame mixins | risky | all mixins | hottest targets |
| --- | --- | ---: | ---: | ---: | --- |
| sable | both | 127 | 29 | 496 | LevelRenderer×23, GameRenderer×12, Camera×5, RenderTarget×4, EntityRenderDispatcher×3, EntityRenderer×3 |
| iris | client | 114 | 28 | 171 | LevelRenderer×11, GlStateManager×7, GameRenderer×4, RenderType×4, BufferBuilder×3, DebugScreenOverlay×3 |
| controlify | client | 84 | 0 | 143 | AbstractSelectionList×4, AbstractContainerScreen×3, AbstractWidget×3, CreativeModeInventoryScreen×3, Gui×3, Screen×3 |
| sodium | client | 48 | 28 | 111 | LevelRenderer×5, ItemRenderer×3, BufferBuilder×2, DebugScreenOverlay×2, RenderChunkRegion×2, RenderSystem×2 |
| entity-model-features | client | 42 | 0 | 64 | CustomHeadLayer×2, EntityModelSet×2, GameRenderer×2, ModelPart$Cube×2, ParrotOnShoulderLayer×2, ArrowRenderer×1 |
| create | both | 30 | 0 | 101 | LevelRenderer×6, GameRenderer×2, HumanoidArmorLayer×2, PoseStack×2, RenderSystem×2, Screen×2 |
| entitytexturefeatures | client | 27 | 0 | 60 | BlockEntityRenderDispatcher×1, BufferBuilder×1, ElytraLayer×1, EndCrystalRenderer×1, EnderDragonRenderer×1, EntityRenderDispatcher×1 |
| subtle-effects | both | 26 | 0 | 118 | ParticleEngine×3, GameRenderer×2, BubbleColumnUpParticle×1, BubbleColumnUpParticle$Provider×1, BubbleParticle×1, BubbleParticle$Provider×1 |
| immediatelyfast | client | 25 | 11 | 39 | GameRenderer×3, DebugScreenOverlay×2, GuiGraphics×2, AbstractContainerScreen×1, BufferUploader×1, ChatScreen×1 |
| modernfix | both | 21 | 4 | 156 | CreateWorldScreen×2, ItemRenderer×2, LivingEntityRenderer×2, ConnectScreen×1, CubeDefinition×1, EnderDragonRenderer×1 |
| create-aeronautics | both | 20 | 0 | 134 | LevelRenderer×5, CreativeModeInventoryScreen×2, AbstractContainerScreen×1, CompassItemPropertyFunction×1, CreateWorldScreen×1, CreateWorldScreen$WorldTab×1 |
| emotecraft | both | 18 | 2 | 29 | LivingEntityRenderer×2, ModelPart×2, PlayerModel×2, Camera×1, CapeLayer×1, ElytraLayer×1 |
| playeranimator | both | 16 | 2 | 17 | LivingEntityRenderer×2, PlayerModel×2, Camera×1, CapeLayer×1, ElytraLayer×1, EntityRenderDispatcher×1 |
| adorable-hamster-pets | both | 12 | 0 | 38 | Gui×2, LivingEntityRenderer×2, AbstractContainerScreen×1, BlockEntityRenderDispatcher×1, EntityRenderDispatcher×1, ItemInHandLayer×1 |
| exposure | both | 12 | 1 | 41 | DebugScreenOverlay×2, AnvilScreen×1, Camera×1, GameRenderer×1, Gui×1, HumanoidModel×1 |
| icys-better-horses | both | 12 | 0 | 56 | AbstractContainerScreen×2, HorseModel×2, LivingEntityRenderer×2, AbstractHorseRenderer×1, Camera×1, GameRenderer×1 |
| moonlight | both | 12 | 0 | 82 | AgeableListModel×1, CartographyTableScreen×1, GameRenderer×1, HumanoidModel×1, ItemInHandRenderer×1, Lighting×1 |
| 3dskinlayers | client | 11 | 0 | 23 | CustomHeadLayer×1, Deadmau5EarsLayer×1, GuiGraphics×1, ModelPart×1, NativeImage×1, PlayerModel×1 |
| alexs-caves-(unofficial-port) | both | 11 | 0 | 61 | HumanoidModel×3, Camera×1, GameRenderer×1, HumanoidArmorLayer×1, ItemFrameRenderer×1, LevelRenderer×1 |
| doggy-talents-next | both | 11 | 0 | 17 | Camera×2, CubeDefinition×1, CubeDeformation×1, Gui×1, LayerDefinition×1, LivingEntityRenderer×1 |
| lambdynamiclights | client | 11 | 0 | 42 | LevelRenderer×3, DebugScreenOverlay×1, EntityRenderer×1, GameRenderer×1, Particle×1, ParticleEngine×1 |
| lodestonelib | both | 10 | 3 | 15 | AbstractContainerScreen×1, CreativeModeInventoryScreen×1, EffectProgram×1, GameRenderer×1, Gui×1, GuiGraphics×1 |
| iceberg | both | 9 | 0 | 22 | GuiGraphics×2, HumanoidArmorLayer×2, TooltipRenderUtil×2, ClientTextTooltip×1, Gui×1, Screen×1 |
| shatterbyte-lib | both | 9 | 1 | 11 | GameRenderer×3, AbstractWidget×2, AbstractContainerScreen×1, Gui×1, ParticleEngine×1, Screen×1 |
| ssrd | both | 9 | 1 | 24 | BlockEntityRenderer×1, EntityRenderDispatcher×1, Frustum×1, GameRenderer×1, LevelRenderer×1, RenderSectionManager×1 |
| what-are-they-up-to | both | 9 | 1 | 11 | Screen×2, GameRenderer×1, Gui×1, NativeImage×1, ParticleEngine×1, PlayerModel×1 |
| xaeros-minimap | both | 9 | 0 | 30 | AbstractButton×1, AgeableListModel×1, EnderDragonRenderer$DragonModel×1, HierarchicalModel×1, ListModel×1, LlamaModel×1 |
| entityculling | client | 8 | 0 | 19 | BlockEntityRenderDispatcher×1, BlockEntityRenderer×1, DebugScreenOverlay×1, EntityRenderer×1, GuiGraphics×1, LevelRenderer×1 |
| fusion-connected-textures | client | 8 | 0 | 33 | EntityModelSet×1, EntityRenderDispatcher×1, EntityRenderer×1, EntityRenderers×1, ItemRenderer×1, RenderChunkRegion×1 |
| sound | client | 8 | 0 | 33 | AbstractContainerScreen×1, ChatComponent×1, ChatScreen×1, CreativeModeInventoryScreen×1, CreativeModeInventoryScreen$ItemPickerMenu×1, EditBox×1 |
| ars-nouveau | both | 7 | 1 | 46 | LevelRenderer×2, AbstractSliderButton×1, ChatComponent×1, EntityRenderer×1, GameRenderer×1, Screen×1 |
| chalk-mod | both | 7 | 0 | 16 | ClientTooltipComponent×2, DebugScreenOverlay×2, AbstractContainerScreen×1, EntityRenderer×1, LevelRenderer×1 |
| distanthorizons | both | 7 | 2 | 43 | GlFramebuffer×2, DebugScreenOverlay×1, FogRenderer×1, LevelRenderer×1, LightTexture×1, OptionsScreen×1 |
| fwa | client | 7 | 1 | 12 | BlockRenderer×2, BlockEntityRenderDispatcher×1, LevelRenderer×1, RenderChunkRegion×1, ShadowRenderer×1, ShulkerBoxRenderer×1 |
| slice-and-dice | both | 7 | 0 | 20 | BufferBuilder×1, GameRenderer×1, ItemRenderer×1, ParticleEngine×1, RenderSystem×1, RenderType×1 |
| artifacts | both | 6 | 0 | 56 | PlayerRenderer×3, GameRenderer×1, HumanoidModel×1, LivingEntityRenderer×1 |
| blueprint | both | 6 | 0 | 36 | Camera×1, ModelPart×1, PlayerRenderer×1, RabbitRenderer×1, SkinCustomizationScreen×1, WorldOpenFlows×1 |
| clientsort | both | 6 | 0 | 15 | AbstractContainerScreen×2, Screen×2, CreativeModeInventoryScreen×1, CreativeModeInventoryScreen$SlotWrapper×1 |
| dungeons_delight | both | 6 | 1 | 29 | Gui×2, AbstractContainerScreen×1, EffectRenderingInventoryScreen×1, HumanoidModel×1, LevelRenderer×1 |
| dynamic-fps | client | 6 | 0 | 10 | DebugScreenOverlay×1, GameRenderer×1, Gui×1, LoadingOverlay×1, ToastComponent×1, Window×1 |
## Per-frame event subscribers
| mod | side | frame events | tick events |
| --- | --- | --- | --- |
| puzzles-lib | both | ComputeFovModifierEvent, CustomizeGuiOverlayEvent, RenderBlockScreenEffectEvent, RenderFrameEvent, RenderGuiEvent, RenderGuiLayerEvent, RenderHandEvent, RenderHighlightEvent, RenderLevelStageEvent, RenderLivingEvent, RenderNameTagEvent, RenderPlayerEvent, RenderTooltipEvent, ViewportEvent | ClientTickEvent, EntityTickEvent, LevelTickEvent, PlayerTickEvent |
| create | both | CustomizeGuiOverlayEvent, RenderArmEvent, RenderFrameEvent, RenderGuiLayerEvent, RenderHandEvent, RenderHighlightEvent, RenderLevelStageEvent, RenderPlayerEvent, RenderTooltipEvent, ViewportEvent | ClientTickEvent, EntityTickEvent, LevelTickEvent, PlayerTickEvent |
| alexs-caves-(unofficial-port) | both | ComputeFovModifierEvent, CustomizeGuiOverlayEvent, RenderBlockScreenEffectEvent, RenderGuiLayerEvent, RenderHandEvent, RenderLevelStageEvent, RenderLivingEvent, RenderNameTagEvent, ViewportEvent | ClientTickEvent, EntityTickEvent, PlayerTickEvent |
| mowzies-mobs | both | ComputeFovModifierEvent, CustomizeGuiOverlayEvent, RenderFrameEvent, RenderGuiLayerEvent, RenderHandEvent, RenderLevelStageEvent, RenderLivingEvent, RenderNameTagEvent, ViewportEvent | EntityTickEvent, LevelTickEvent, PlayerTickEvent |
| the-twilight-forest | both | ComputeFovModifierEvent, CustomizeGuiOverlayEvent, RenderArmEvent, RenderFrameEvent, RenderGuiLayerEvent, RenderHighlightEvent, RenderLevelStageEvent, RenderLivingEvent, ViewportEvent | ClientTickEvent, EntityTickEvent, PlayerTickEvent |
| ars-nouveau | both | RenderFrameEvent, RenderGuiLayerEvent, RenderHandEvent, RenderHighlightEvent, RenderLevelStageEvent, RenderTooltipEvent | ClientTickEvent, LevelTickEvent, PlayerTickEvent |
| balm | both | ComputeFovModifierEvent, GuiDrawEvent, RenderGuiEvent, RenderGuiLayerEvent, RenderHandEvent, RenderHighlightEvent | ClientTickEvent, ClientTickHandler, EntityTickEvent, LevelTickEvent, PlayerTickEvent |
| alexs-mobs(1.21.1) | both | RenderGuiLayerEvent, RenderHandEvent, RenderLevelStageEvent, RenderLivingEvent, ViewportEvent | ClientTickEvent, EntityTickEvent |
| xaeros-minimap | both | CustomizeGuiOverlayEvent, RenderFrameEvent, RenderGuiEvent, RenderGuiLayerEvent, RenderLevelStageEvent | ClientTickEvent, PlayerTickEvent |
| architectury-api | both | ClientGuiEvent, CustomizeGuiOverlayEvent, RenderGuiEvent, RenderTooltipEvent | ClientTickEvent, LevelTickEvent, PlayerTickEvent |
| doggy-talents-next | both | RenderArmEvent, RenderLevelStageEvent, RenderPlayerEvent, ViewportEvent | ClientTickEvent |
| modernfix | both | CustomizeGuiOverlayEvent, RenderFrameEvent, RenderLivingEvent, RenderPlayerEvent | ClientTickEvent |
| adorable-hamster-pets | both | ClientGuiEvent, RenderLevelStageEvent, WorldRenderEvents | ClientTickEvent |
| apotheosis | both | RenderHighlightEvent, RenderLevelStageEvent, RenderTooltipEvent | ClientTickEvent, EntityTickEvent |
| citadel-(1.21.1-port) | both | RenderLevelStageEvent, RenderPlayerEvent, RenderTooltipEvent | ClientTickEvent, EntityTickEvent |
| lodestonelib | both | RenderFrameEvent, RenderLevelStageEvent, ViewportEvent | ClientTickEvent, LevelTickEvent |
| slice-and-dice | both | RenderGuiLayerEvent, RenderLevelStageEvent, RenderTooltipEvent | ClientTickEvent, LevelTickEvent |
| alltheleaks | both | CustomizeGuiOverlayEvent, RenderLivingEvent | ClientTickEvent, EntityTickEvent, LevelTickEvent |
| appleskin | both | CustomizeGuiOverlayEvent, RenderTooltipEvent | ClientTickEvent, EntityTickEvent |
| arsdelight | both | RenderLevelStageEvent, RenderLivingEvent | ClientTickEvent, PlayerTickEvent |
| artifacts | both | RenderArmEvent, RenderLivingEvent | ClientTickEvent, EntityTickEvent |
| barbeques-delight-forge | both | RenderLevelStageEvent, RenderLivingEvent | ClientTickEvent, PlayerTickEvent |
| create-aeronautics | both | RenderGuiEvent, RenderLevelStageEvent | ClientTickEvent, LevelTickEvent |
| creativecore | both | RenderFrameEvent, RenderGuiEvent | ClientTickEvent, LevelTickEvent |
| distanthorizons | both | RenderLevelStageEvent, WorldRenderEvents | |
| exposure | both | RenderGuiEvent, RenderLevelStageEvent | |
| extradelight | both | ComputeFovModifierEvent, ViewportEvent | |
| fruits-delight | both | RenderLevelStageEvent, RenderLivingEvent | ClientTickEvent, PlayerTickEvent |
| jade | both | CustomizeGuiOverlayEvent, RenderGuiEvent | ClientTickEvent |
| modonomicon | both | RenderFrameEvent, RenderLevelStageEvent | ClientTickEvent |
| patchouli | both | RenderFrameEvent, RenderTooltipEvent | ClientTickEvent |
| playerrevive | both | RenderFrameEvent, RenderGuiLayerEvent | ClientTickEvent, PlayerTickEvent |
| sable | both | RenderLevelStageEvent, ViewportEvent | ClientTickEvent |
| sophisticated-core | both | RenderLevelStageEvent, RenderTooltipEvent | ClientTickEvent, LevelTickEvent |
| twilight-delight | both | RenderLevelStageEvent, RenderLivingEvent | ClientTickEvent, PlayerTickEvent |
| akashic-tome | both | RenderGuiLayerEvent | |
| comforts | both | RenderPlayerEvent | ClientTickEvent |
| dynamic-fps | client | RenderGuiEvent | ClientTickEvent |
| ferrite-core | both | RenderLevelStageEvent | |
| ftb-library-forge | both | ClientGuiEvent | ClientTickEvent |
| fwa | client | RenderLevelStageEvent | |
| iceberg | both | RenderTooltipEvent | |
| iris | client | RenderLevelStageEvent | |
| jei | both | RenderTooltipEvent | ClientTickEvent |
| keybind-atlas | client | RenderGuiEvent | |
| legendary-tooltips | client | RenderTooltipEvent | |
| minopp | both | ComputeFovModifierEvent | ClientTickEvent |
| mru | both | HudRenderCallback | |
| music-disc-maker | both | RenderLevelStageEvent | ClientTickEvent |
| quick-skin | both | ClientGuiEvent | ClientTickEvent |
| rechiseled | both | RenderLevelStageEvent | |
| resourceful-lib | both | RenderHighlightEvent | |
| shatterbyte-lib | both | RenderLevelStageEvent | ClientTickEvent |
| sophisticated-backpacks | both | RenderLevelStageEvent | ClientTickEvent, EntityTickEvent, LevelTickEvent |
| sophisticated-storage | both | RenderHighlightEvent | ClientTickEvent, LevelTickEvent |
| subtle-effects | both | RenderLevelStageEvent | ClientTickEvent |
| xaeros-world-map | both | RenderFrameEvent | ClientTickEvent, PlayerTickEvent |
| zume | client | ViewportEvent | |
## Block/item atlas pressure
| mod | side | sprites (16² eq.) | files | animated | % of pack | models | blockstates |
| --- | --- | ---: | ---: | ---: | ---: | ---: | ---: |
| handcrafted | both | 16,359 | 976 | 0 | 27.2 | 12394 | 267 |
| chipped | both | 12,754 | 12299 | 30 | 21.2 | 16832 | 6981 |
| rechiseled | both | 5,680 | 657 | 0 | 9.4 | 13855 | 3627 |
| create | both | 4,012 | 1083 | 4 | 6.7 | 2684 | 643 |
| ars-nouveau | both | 3,778 | 547 | 29 | 6.3 | 870 | 200 |
| too-many-bows | both | 3,322 | 166 | 0 | 5.5 | 150 | 0 |
| extradelight | both | 1,847 | 1847 | 0 | 3.1 | 3759 | 822 |
| adorable-hamster-pets | both | 1,051 | 121 | 0 | 1.7 | 82 | 10 |
| doggy-talents-next | both | 981 | 192 | 0 | 1.6 | 164 | 6 |
| the-twilight-forest | both | 889 | 827 | 44 | 1.5 | 3107 | 523 |
| create-aeronautics | both | 740 | 261 | 1 | 1.2 | 605 | 141 |
| alexs-caves-(unofficial-port) | both | 717 | 711 | 27 | 1.2 | 1284 | 354 |
| macaws-doors | both | 702 | 702 | 0 | 1.2 | 1394 | 246 |
| dungeons_delight | both | 450 | 327 | 17 | 0.7 | 416 | 65 |
| wings-of-fire | both | 417 | 57 | 2 | 0.7 | 53 | 7 |
| alexs-mobs(1.21.1) | both | 410 | 254 | 13 | 0.7 | 438 | 37 |
| tiny-dragons | both | 378 | 9 | 1 | 0.6 | 25 | 3 |
| rustic-delight | both | 363 | 348 | 0 | 0.6 | 423 | 55 |
| sophisticated-storage | both | 349 | 271 | 0 | 0.6 | 299 | 59 |
| farmers-delight | both | 348 | 348 | 2 | 0.6 | 369 | 132 |
| immersive-paintings | both | 341 | 89 | 0 | 0.6 | 4 | 0 |
| fruits-delight | both | 254 | 254 | 0 | 0.4 | 649 | 200 |
| items-displayed-forge | both | 243 | 129 | 0 | 0.4 | 315 | 109 |
| twilight-delight | both | 233 | 221 | 11 | 0.4 | 356 | 95 |
| frights-delight | both | 217 | 172 | 69 | 0.4 | 271 | 43 |
| mowzies-mobs | both | 174 | 70 | 1 | 0.3 | 96 | 9 |
| veggies-delight | both | 170 | 170 | 0 | 0.3 | 178 | 29 |
| exposure | both | 166 | 52 | 1 | 0.3 | 54 | 8 |
| macaws-windows | both | 164 | 164 | 0 | 0.3 | 1887 | 328 |
| arsdelight | both | 159 | 142 | 0 | 0.3 | 165 | 26 |
## Animated atlas textures (uploaded every tick unless culled by Sodium)
| mod | side | animated |
| --- | --- | ---: |
| frights-delight | both | 69 |
| the-twilight-forest | both | 44 |
| chipped | both | 30 |
| ars-nouveau | both | 29 |
| alexs-caves-(unofficial-port) | both | 27 |
| macaws-lights-and-lamps | both | 18 |
| dungeons_delight | both | 17 |
| alexs-mobs(1.21.1) | both | 13 |
| apotheosis | both | 12 |
| twilight-delight | both | 11 |
| moonlight | both | 10 |
| create-enchantment-industry | both | 7 |
| create | both | 4 |
| apothic-enchanting | both | 3 |
| ends-delight | both | 2 |
| farmers-delight | both | 2 |
| macaws-roofs | both | 2 |
| slice-and-dice | both | 2 |
| sophisticated-core | both | 2 |
| wings-of-fire | both | 2 |
## Entity content (renderer + animation load)
| mod | side | entity types | entity tex Mpx | geckolib files |
| --- | --- | ---: | ---: | ---: |
| alexs-mobs(1.21.1) | both | 140 | 7.17 | 0 |
| alexs-caves-(unofficial-port) | both | 92 | 11.45 | 0 |
| the-twilight-forest | both | 80 | 0.95 | 0 |
| ars-nouveau | both | 51 | 0.37 | 132 |
| aquaculture | both | 40 | 0.05 | 0 |
| mowzies-mobs | both | 30 | 0.38 | 38 |
| critters-and-companions | both | 17 | 0.47 | 60 |
| items-displayed-forge | both | 17 | 0.07 | 0 |
| icys-better-horses | both | 16 | 5.37 | 14 |
| fowlplay | both | 14 | 0.09 | 0 |
| friends-and-foes-forge | both | 14 | 0.16 | 0 |
| fishermans-haven | both | 11 | 0.00 | 0 |
| tiny-dragons | both | 10 | 0.00 | 9 |
| create | both | 9 | 0.02 | 0 |
| adorable-hamster-pets | both | 7 | 0.26 | 4 |
| dungeons_delight | both | 7 | 0.05 | 0 |
| create-aeronautics | both | 6 | 0.01 | 0 |
| pet-cemetery | both | 6 | 0.01 | 0 |
| wings-of-fire | both | 6 | 0.00 | 2 |
| exposure | both | 5 | 0.00 | 0 |
| immersive-paintings | both | 4 | 0.00 | 0 |
| blueprint | both | 3 | 0.00 | 0 |
| doggy-talents-next | both | 3 | 10.09 | 0 |
| charta | both | 2 | 0.03 | 0 |
| handcrafted | both | 2 | 0.00 | 0 |
| jade | both | 2 | 0.00 | 0 |
| lootr | both | 2 | 0.00 | 0 |
| too-many-bows | both | 2 | 0.02 | 0 |
| artifacts | both | 1 | 0.10 | 0 |
| farmers-delight | both | 1 | 0.07 | 0 |
## Shader assets
| mod | side | shader files | post effects | core overrides |
| --- | --- | ---: | ---: | ---: |
| lodestonelib | both | 59 | 2 | 0 |
| alexs-caves-(unofficial-port) | both | 38 | 7 | 0 |
| charta | both | 31 | 1 | 0 |
| xaeros-minimap | both | 31 | 0 | 31 |
| xaeros-world-map | both | 31 | 0 | 31 |
| distanthorizons | both | 23 | 0 | 0 |
| exposure | both | 21 | 19 | 0 |
| what-are-they-up-to | both | 16 | 0 | 0 |
| ars-nouveau | both | 9 | 0 | 0 |
| sodium | client | 9 | 0 | 3 |
| blueprint | both | 6 | 0 | 0 |
| moonlight | both | 4 | 0 | 0 |
| playerrevive | both | 4 | 1 | 0 |
| the-twilight-forest | both | 4 | 0 | 0 |
| apotheosis | both | 3 | 0 | 0 |
| citadel-(1.21.1-port) | both | 3 | 1 | 0 |
| create | both | 3 | 0 | 0 |
| sable | both | 3 | 0 | 3 |
| shatterbyte-lib | both | 3 | 1 | 0 |
| ssrd | both | 1 | 0 | 0 |
## Sound payload
| mod | side | ogg MB |
| --- | --- | ---: |
| ambientsounds | client | 85.5 |
| alexs-caves-(unofficial-port) | both | 68.8 |
| mowzies-mobs | both | 33.2 |
| create-aeronautics | both | 26.2 |
| alexs-mobs(1.21.1) | both | 20.3 |
| the-twilight-forest | both | 17.3 |
| adorable-hamster-pets | both | 15.2 |
| sound | client | 7.9 |
| playerrevive | both | 5.1 |
| dungeons_delight | both | 5.1 |
| apothic-enchanting | both | 4.8 |
| doggy-talents-next | both | 4.3 |
| ars-nouveau | both | 4.3 |
| friends-and-foes-forge | both | 3.9 |
| apotheosis | both | 3.9 |
## Jar size
| mod | side | MB |
| --- | --- | ---: |
| ambientsounds | client | 84.8 |
| alexs-caves-(unofficial-port) | both | 74.9 |
| mowzies-mobs | both | 36.0 |
| music-disc-maker | both | 35.0 |
| create-aeronautics | both | 33.1 |
| distanthorizons | both | 28.2 |
| the-twilight-forest | both | 27.2 |
| alexs-mobs(1.21.1) | both | 26.6 |
| adorable-hamster-pets | both | 20.3 |
| ars-nouveau | both | 20.1 |
| create | both | 19.1 |
| chipped | both | 15.0 |
| sable | both | 13.0 |
| when-dungeons-arise | both | 11.8 |
| rechiseled | both | 11.5 |
| doggy-talents-next | both | 11.3 |
| extradelight | both | 11.0 |
| sound | client | 8.7 |
| controlify | client | 8.0 |
| fragmentum | both | 7.8 |
## Resource packs / shader packs
- **better-leaves** (both): atlas sprites 2,469, entity tex 0.00 Mpx, models 3223, blockstates 871, CEM/EMF files 0, shader files 0
- **complementary-reimagined** (both): atlas sprites 0, entity tex 0.00 Mpx, models 0, blockstates 0, CEM/EMF files 0, shader files 0
- **fresh-animations** (both): atlas sprites 0, entity tex 0.65 Mpx, models 0, blockstates 0, CEM/EMF files 240, shader files 0
- **fresh-animations-extensions** (both): atlas sprites 0, entity tex 0.48 Mpx, models 0, blockstates 0, CEM/EMF files 218, shader files 0
- **makeup-ultra-fast-shaders** (both): atlas sprites 0, entity tex 0.00 Mpx, models 0, blockstates 0, CEM/EMF files 0, shader files 0
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,981 @@
_version = 4
[server]
#
# Defines the distance the player will receive updates around.
realTimeUpdateDistanceRadiusInChunks = 256
#
# Prefix of the level keys sent to the clients.
# If the mod is running behind a proxy, each backend should use a unique value.
# If this value is empty, level key will be based on the server's seed hash.
levelKeyPrefix = ""
#
# Defines the distance allowed to be synchronized around the player.
# Should be the same or larger than maxGenerationRequestDistance in most cases.
maxSyncOnLoadRequestDistance = 4096
#
# If true, clients will receive updated LODs when joining or loading new LODs.
synchronizeOnLoad = true
#
# Custom server key used which can be used to always reuse the same LOD data folder,
# for cases when the server doesn't have a static IP for some reason.
# If this value is empty, the client itself decides which folder name to use.
# Requires rejoining the server to apply after changing.
serverKey = ""
#
# Defines the distance allowed to generate around the player.
maxGenerationRequestDistance = 4096
#
# Makes the server send level keys for each world.
# Disable this if you use alternative ways to send level keys.
sendLevelKeys = true
#
# DO NOT CHANGE UNLESS YOU KNOW WHAT YOU'RE DOING.
# Autogenerated ID used to prevent multiple independent servers from accidentally
# writing over each other's LODs when the same serverKey is set on both.
serverId = 1518119460
#
# How many LOD generation requests per second should a client send?
# Also limits the number of client requests allowed to stay in the server's queue.
generationRequestRateLimit = 20
#
# How many LOD sync requests per second should a client send?
# Also limits the amount of player's requests allowed to stay in the server's queue.
syncOnLoadRateLimit = 50
#
# If true, clients will receive real-time LOD updates for chunks outside the client's render distance.
enableRealTimeUpdates = true
#
# Maximum global speed for uploading LODs to the clients, in KB/s.
# Value of 0 disables the limit.
globalBandwidthLimit = 0
#
# Maximum per-player speed for uploading LODs to the clients, in KB/s.
# Value of 0 disables the limit.
playerBandwidthLimit = 500
#
# Enables adaptive transfer speed based on client performance.
# If true, DH will automatically adjust transfer rate to minimize connection lag.
# If false, transfer speed will remain fixed.
enableAdaptiveTransferSpeed = false
[common.lodBuilding]
#
# How should block data be compressed when creating LOD data?
# This setting will only affect new or updated LOD data,
# any data already generated when this setting is changed will be
# unaffected until it is modified or re-loaded.
#
# MERGE_SAME_BLOCKS
# Every block/biome change is recorded in the database.
# This is what DH 2.0 and 2.0.1 all used by default and will store a lot of data.
# Expected Compression Ratio: 1.0
#
# VISUALLY_EQUAL
# Only visible block/biome changes are recorded in the database.
# Hidden blocks (IE ores) are ignored.
# Expected Compression Ratio: 0.7
worldCompression = "VISUALLY_EQUAL"
#
# Requires a level restart to apply.
#
# Setting this to a less strict mode increases DB read/write
# speed at the cost of potential data loss/corruption if
# the computer looses power
# (unexpected program shutdown should be safe for all options).
#
# FULL
# ACID - data will never be lost or corrupted
# Slow
# At max throughput: ~2k saves per sec, ~70% disk usage
#
# NORMAL
# May not be durable - some data may be lost on power loss, but DB should be safe from corruption
# Fast
# At max throughput: ~14k saves per sec, ~40% disk usage
#
# OFF
# Not consistent - database may be corrupted during power loss
# Very Fast
# At max throughput: ~16k saves per sec, ~6% disk usage
databaseSyncMode = "NORMAL"
dataCompression = "Z_STD_BLOCK"
#
# Enabling this will drastically speed up chunk processing time
# if you are CPU limited.
# Disabling it will drastically speed up chunk processing time
# if you are disk speed/IO limited.
#
# Normally DH will attempt to skip creating LODs for chunks it's already seen
# and that haven't changed.
#
# Disabling this option can also fix a rare bug where
# modified chunks are skipped due to being too similar to
# their saved hash.
disableUnchangedChunkCheck = false
[common.multiThreading]
#
# How many threads should be used by Distant Horizons?
numberOfThreads = 2
#
# A value between 1.0 and 0.0 that represents the percentage
# of time each thread can run before going idle.
#
# This can be used to reduce CPU usage if the thread count
# is already set to 1 for the given option, or more finely
# tune CPU performance.
threadRunTimeRatio = "1.0"
#
# What Java thread priority should DH's primary thread pools run with?
#
# You probably don't need to change this unless you are also
# running C2ME and are seeing thread starvation in either C2ME or DH.
threadPriority = 5
[common.logging]
#
# If enabled, the mod will log information about the renderer setup, cleanup, and any issues it may encounter.
# This can be useful for debugging.
logRendererEventToFile = "INFO"
#
# If enabled, the mod will log information about the world generation process.
# This can be useful for debugging.
logWorldGenEventToFile = "INFO"
#
# If enabled, the mod will log information about the renderer OpenGL process.
# This can be useful for debugging.
logRendererGLEventToChat = "ERROR"
#
#
globalChatMaxLevel = "ERROR"
#
# If enabled, the mod will log information about the world generation process.
# This can be useful for debugging.
logWorldGenChunkLoadEventToFile = "INFO"
#
# If enabled, the mod will log information about the renderer OpenGL process.
# This can be useful for debugging.
logRendererGLEventToFile = "INFO"
#
# If enabled, the mod will log information about network operations.
# This can be useful for debugging.
logNetworkEventToFile = "INFO"
#
# If enabled, config changes sent by the server will be logged.
logConnectionConfigChangesToFile = "WARN"
#
#
globalFileMaxLevel = "INFO"
[common.logging.warning]
#
# If enabled, a message will be displayed in chat if explicit garbage collection
# is disabled.
# This is known to cause out-of-memory issues
# and is better solved with a concurrent garbage collector.
showExplicitGcDisabledWarning = false
#
# If enabled, a chat message will be displayed when DH has too many chunks
# queued for updating.
showSlowWorldGenSettingWarnings = true
#
# If enabled, a chat message will be displayed when DH has too many chunks
# queued for updating.
showUpdateQueueOverloadedChatWarning = false
#
# If enabled, a message will be logged if explicit garbage collection
# is disabled.
# This is known to cause out-of-memory issues
# and is better solved with a concurrent garbage collector.
logExplicitGcDisabledWarning = false
#
# If enabled, a chat message will be displayed if Java doesn't have enough
# memory allocated to run DH well.
showLowMemoryWarningOnStartup = true
#
# If enabled, a chat message will be displayed when a replay is started
# giving some basic information about how DH will function.
showReplayWarningOnStartup = true
#
# If enabled, a chat message will be displayed when a potentially problematic
# mod is installed alongside DH.
showModCompatibilityWarningsOnStartup = true
#
# If enabled, a chat message will be displayed if vanilla MC's
# render distance is higher than the recommended amount.
showHighVanillaRenderDistanceWarning = true
#
# If enabled, a chat message will be displayed if DH detects
# that any pooled objects have been garbage collected.
showPoolInsufficientMemoryWarning = true
#
# If enabled, a message will be logged if the garbage
# collector Java is currently using is known
# to cause frame stuttering and/or other issues.
logGarbageCollectorWarning = true
#
# If enabled, a chat message will be displayed in chat if the garbage
# collector Java is currently using is known
# to cause frame stuttering and/or other issues.
showGarbageCollectorWarning = true
[common.worldGenerator]
#
# How should distant generator progress be displayed?
#
# OVERLAY: may be the same as CHAT for some Minecraft versions
# CHAT
# LOG
# DISABLED
showGenerationProgress = "DISABLED"
#
# If the SURFACE_THEN_CHUNKS generator plan is
# in use, this percentage can be defined to reduce how far the
# second high-quality generation pass will go
# as a percentage of your total render distance:
# -1.0 = Infinity
# 0.0 = 0%
# 0.5 = 50%
# 1.0 = 100%
# 2.0 = 200%
#
# Does nothing on a dedicated server.
surfaceRegenMaxDistancePercent = "-1.0"
#
# The max radius in chunks around the central point where world generation is allowed.
# If this value is set to 0, generation bounds are disabled and the render distance will be used.
#
# This should only be set if you have a pre-generated world that has a very limited size.
# Setting this on a normal MC world will prevent the world generator from filling
# out your render distance.
generationMaxChunkRadius = 0
#
# If the Minecraft server thread is taking too long
# Distant Horizons will pause world gen to reduce server stuttering/lag.
limitIfServerUnhealthy = true
#
# How often should the distant generator progress be displayed?
generationProgressDisplayIntervalInSeconds = 2
#
# When logging generation progress also include the rate at which chunks
# are being generated.
# This can be useful for troubleshooting performance.
generationProgressIncludeChunksPerSecond = true
#
# How detailed should LODs be generated outside the vanilla render distance?
#
# PRE_EXISTING_ONLY
# Only create LOD data for already generated chunks.
#
# FEATURES
# Generate everything except structures.
# Note: This may cause world generator bugs or instability when paired with certain world generator mods.
#
# INTERNAL_SERVER
# Ask the local server to generate/load each chunk.
# This is the most compatible and will generate structures correctly,
# but may cause server/simulation lag.
# Note: unlike other modes this option DOES save generated chunks to
# Minecraft's region files.
chunkGeneratorMode = "FEATURES"
#
# Defines how LODs will be generated
# outside the vanilla render distance.
# In multiplayer, this may be overridden by the server.
#
# SURFACE_THEN_CHUNKS
# The rough surface will be generated first
# then chunks will be generated to fill in
# missing features (ie trees and villages).
#
# Recommended for modded or vanilla worlds.
#
# SURFACE_ONLY
# Only the rough surface will be generated.
# Chunk features like trees and villages
# won't be generated.
#
# Recommended for computers that don't have
# enough CPU power or RAM space to handle
# the chunk generator.
#
# CHUNKS_ONLY
# Slower than either surface options,
# but provides more accurate terrain.
#
# Recommended for custom worlds
# where the surface generator would
# generate incorrect terrain.
#
# DISABLED
# No distant terrain will be generated.
generatorPlan = "SURFACE_THEN_CHUNKS"
#
# If true and the player/camera is moving quickly
# Distant Horizons will pause world gen to improve loading speed
# and reduce CPU load.
pauseIfMovingQuickly = true
#
# For how many seconds should instructions for disabling the distant generator progress be displayed?
# Setting this to 0 hides the instructional message so the world gen progress is shown immediately when it starts.
generationProgressDisableMessageDisplayTimeInSeconds = 20
#
# The center Z chunk position that the world gen max radius is centered around.
generationCenterChunkZ = 0
#
# The center X chunk position that the world gen max radius is centered around.
generationCenterChunkX = 0
[client]
#
# Should Distant Horizon's config button appear in Minecraft's options screen next to the fov slider?
showDhOptionsButtonInMinecraftUi = true
[client.advanced.autoUpdater]
#
# If DH should use the nightly (provided by Gitlab), or stable (provided by Modrinth) build.
# If [AUTO] is selected DH will update to new stable releases if the current jar is a stable jar
# and will update to new nightly builds if the current jar is a nightly jar (IE the version number ends in '-dev').
updateBranch = "AUTO"
#
# Automatically check for updates on game launch?
enableAutoUpdater = true
#
# Should Distant Horizons silently, automatically download and install new versions?
# This setting is force disabled on dedicated servers for stability reasons.
enableSilentUpdates = false
[client.advanced.debugging]
#
# If enabled this will disable (most) vanilla Minecraft rendering.
#
# NOTE: Do not report any issues when this mode is on!
# This setting is only for fun and debugging.
# Mod compatibility is not guaranteed.
lodOnlyMode = false
#
# What renderer is active?
#
# DEFAULT: Default lod renderer
# DEBUG_TRIANGLE: Debug testing renderer
# DISABLED: Disable rendering
rendererMode = "DEFAULT"
#
# If true overlapping quads will be rendered as bright red for easy identification.
# If false the quads will be rendered normally.
showOverlappingQuadErrors = false
#
# If true OpenGL Buffer garbage collection will be logged
# this also includes the number of live buffers.
logBufferGarbageCollection = false
#
# Should specialized colors/rendering modes be used?
#
# OFF: LODs will be drawn with their normal colors.
# SHOW_DETAIL: LODs' color will be based on their detail level.
# SHOW_BLOCK_MATERIAL: LODs' color will be based on their material.
# SHOW_OVERLAPPING_QUADS: LODs will be drawn with total white, but overlapping quads will be drawn with red.
debugRenderingColors = "OFF"
#
# Stops vertex colors from being passed.
# Useful for debugging shaders
enableWhiteWorld = false
#
# If enabled the LODs will render as wireframe.
renderWireframe = false
#
# If true several keys can be used to toggle debug states.
# F6 - enable/disable LOD rendering
# F7 - enable/disable LOD only rendering
# F8 - cycle through the different debug rendering modes
enableDebugKeybindings = false
[client.advanced.debugging.debugWireframe]
#
# Render LOD section status?
showRenderSectionStatus = false
#
# Render queued network sync on load tasks?
showNetworkSyncOnLoadQueue = false
#
# Render full data update/lock status?
showFullDataUpdateStatus = false
#
# Render queued world gen tasks?
showWorldGenQueue = false
#
# Render Quad Tree Rendering status?
showQuadTreeRenderStatus = false
#
# If enabled, various wireframes for debugging internal functions will be drawn.
#
# NOTE: There WILL be performance hit!
# Additionally, only stuff that's loaded after you enable this
# will render their debug wireframes.
enableRendering = false
[client.advanced.debugging.openGl]
#
# Defines how OpenGL errors are handled.
# Requires rebooting Minecraft to change.
# Will catch OpenGL errors thrown by other mods.
overrideVanillaGLLogger = true
#
# Defines how OpenGL errors are handled.
# May incorrectly catch OpenGL errors thrown by other mods.
#
# IGNORE: Do nothing.
# LOG: write an error to the log.
# LOG_THROW: write to the log and throw an exception.
# Warning: this should only be enabled when debugging the LOD renderer
# as it may break Minecraft's renderer when an exception is thrown.
glErrorHandlingMode = "IGNORE"
#
# If true each Open GL error will only be logged once.
# Enabling this may cause some error logs to be missed.
# Does nothing if overrideVanillaGLLogger is set to false.
#
# Generally this can be kept as 'true' to prevent log spam.
# However, Please set this to 'false' if a developer needs your log to debug a GL issue.
onlyLogGlErrorsOnce = true
[client.advanced.debugging.exampleConfigScreen]
shortTest = "69"
mapTest = "{}"
byteTest = "8"
longTest = "42069"
listTest = ["option 1", "option 2", "option 3"]
boolTest = false
doubleTest = "420.69"
floatTest = "0.42069"
linkableTest = 420
intTest = 69420
stringTest = "Test input box"
[client.advanced.debugging.f3Screen]
#
# Shows info about the render thread tasks.
showRenderThreadTasks = false
#
# Shows how many chunks are queued for processing and the max count that can be queued.
showQueuedChunkUpdateCount = true
#
# Shows the memory use and array counts for each DH object pool.
showSeparatedObjectPools = false
#
# Shows the player's LOD position.
showPlayerPos = true
#
# Shows the combined memory use and array counts for all DH pooled objects.
showCombinedObjectPools = false
#
# Defines what internal detail level the player position will be shown as.
# Internal detail level means: 6 = 1x1 block, 7 = 2x2 blocks, etc.
playerPosSectionDetailLevel = 6
#
# Only show levels that DH is actively rendering.
onlyShowRenderingLevels = true
#
# Shows info about each thread pool.
showThreadPools = true
#
# Shows what levels are loaded and world gen/rendering info about those levels.
showLevelStatus = true
[client.advanced.debugging.positionFinderDebugging]
positionFinderEnable = false
positionFinderMinBlockY = -64
positionFinderZPos = 0
positionFinderXPos = 0
positionFinderMaxBlockY = 125
positionFinderDetailLevel = 6
positionFinderMarginPercent = "0.0"
[client.advanced.graphics]
#
# Enabling Anti-Aliasing smooths edges and reduces flickering when moving.
enableAntiAliasing = true
#
# Enable Screen Space Ambient Occlusion
enableSsao = true
#
# If true some vanilla graphics settings will be automatically changed
# during DH setup to provide a better experience.
#
# IE disabling vanilla clouds (which render on top of DH LODs),
# and chunk fading (DH already fades MC chunks)
overrideVanillaGraphicsSettings = true
[client.advanced.graphics.culling]
#
# Determines how far from the camera Distant Horizons will start rendering.
# Measured as a percentage of the vanilla render distance.
#
# -1 = auto, overdraw will change based on the vanilla render distance.
#
# Higher values will prevent LODs from rendering behind vanilla blocks at a higher distance,
# but may cause holes in the world.
# Holes are most likely to appear when flying through unloaded terrain.
#
# Increasing the vanilla render distance increases the effectiveness of this setting.
overdrawPrevention = "-1.0"
#
# Identical to the other frustum culling option
# only used when a shader mod is present using the DH API
# and the shadow pass is being rendered.
#
# Disable this if shadows render incorrectly.
disableShadowPassFrustumCulling = false
#
# A comma separated list of block resource locations that will be replaced by water
# if they're visible on the water's surface.
waterSubSurfaceBlockReplacementCsv = "minecraft:kelp,minecraft:tall_seagrass,minecraft:seagrass"
#
# A comma separated list of block resource locations that won't be rendered by DH.
# Air is always included in this list.
#
# Note:
# If you see gaps, or holes you may have to change
# worldCompression to [MERGE_SAME_BLOCKS] and re-generate the LODs.
ignoredRenderBlockCsv = "minecraft:barrier,minecraft:structure_void,minecraft:light,minecraft:tripwire,minecraft:brown_mushroom"
#
# A comma separated list of block resource locations that will be removed
# when on top of water.
waterSurfaceBlockReplacementCsv = "minecraft:lily_pad"
#
# Defines what blocks should be rendered as LODs.
#
# NONE: Include all blocks in the LODs
# NON_COLLIDING: Only render solid blocks in the LODs (tall grass, torches, etc. will be ignored)
blocksToIgnore = "NON_COLLIDING"
#
# If true LODs outside the player's camera
# aren't drawn, increasing GPU performance.
#
# If false all LODs are drawn, even those behind
# the player's camera, decreasing GPU performance.
#
# Disable this if you see LODs disappearing at the corners of your vision.
disableFrustumCulling = false
#
# If false all beacons near the camera won't be drawn to prevent vanilla overdraw.
# If true all beacons will be rendered.
#
# Generally this should be left as true. It's main purpose is for debugging
# beacon updating/rendering.
disableBeaconDistanceCulling = true
#
# Should the blocks underneath avoided blocks gain the color of the avoided block?
#
# True: a red flower will tint the grass below it red.
# False: skipped blocks will not change color of surface below them.
tintWithAvoidedBlocks = true
#
# If enabled caves won't be rendered.
#
# Note: for some world types this can cause
# overhangs or walls for floating objects.
# Tweaking the caveCullingHeight, can resolve some
# of those issues.
enableCaveCulling = true
#
# At what Y value should cave culling start?
# Lower this value if you get walls for areas with 0 light.
caveCullingHeight = 60
#
# A comma separated list of block resource locations that shouldn't be rendered
# if they are in a 0 sky light underground area.
# Air is always included in this list.
#
# Defaults to an empty list since most cave blocks will be automatically ignored due to being:
# transparent, non-solid, or liquids, but new blocks can be added here if needed.
ignoredRenderCaveBlockCsv = ""
#
# If set to true the overdraw prevention radius will get closer
# to the camera when flying/moving quickly.
#
# This helps reduce issues where Minecraft can't load or
# generate chunks fast enough to keep up with DH.
reduceOverdrawWithFastMovement = true
[client.advanced.graphics.texture]
#
# A comma separated list of block resource locations
# that DH will render their sides using the bottom texture.
# Partial matches/incomplete resource locations will also match.
#
# Example: "minecraft:grass_block,nylium"
#
# Changes require a restart.
blocksDontUseSideTextureCsv = "grass_block,mycelium,nylium,dirt_path,grass"
#
# The highest detail level number that can render with block textures.
# At higher detail levels each LOD covers multiple blocks,
# which can essentially make textures smaller than a pixel and therefore invisible.
#
# Larger numbers texture more distant LODs
# but increase memory usage.
maxTexturedLodDetailLevel = 2
#
# If true nearby and zoomed-in LODs will render with their block's
# actual texture instead of a single flat color.
#
# Only applies to high detail LODs where the texture is actually visible,
# distant LODs always use flat colors.
# Some shader packs nay not have an affect.
enableTexturedLods = true
#
# A comma separated list of block resource locations
# that DH won't render textures on.
# Partial matches/incomplete resource locations will also match.
#
# Example: "minecraft:grass_block,nylium"
#
# Changes require a restart.
blocksDontRenderTextureCsv = "minecraft:bamboo"
#
# A comma separated list of tag names
# that DH will render their sides using the bottom texture.
#
# Example: "grass_blocks,nylium"
#
# Changes require a restart.
blockTagsDontUseSideTextureCsv = "grass_blocks,nylium"
#
# A comma separated list of block resource locations
# that DH will use rasterization to determine
# the face textures.
#
# Can be used to fix issues with mods/blocks where
# a side uses just part of a modeled block's texture.
#
# Example: "minecraft:beacon"
#
# Changes require a restart.
blocksAlwaysRasterizeTextureCsv = "minecraft:beacon"
[client.advanced.graphics.noiseTexture]
#
# Should a noise texture be applied to LODs?
#
# This is done to simulate textures and make the LODs appear more detailed.
enableNoiseTexture = true
#
# Defines how far should the noise texture render before it fades away. (in blocks)
# Set to 0 to disable noise from fading away
noiseDropoff = 1024
#
# How many steps of noise should be applied to LODs?
noiseSteps = 4
#
# How intense should the noise should be?
noiseIntensity = "0.05"
[client.advanced.graphics.experimental]
#
# A comma separated list of dimension resource locations where DH won't render.
#
# Example: "minecraft:the_nether,minecraft:the_end"
# For Minecraft 1.12.2 and older:
# Open the F3 screen and copy the dimension_name:ID shown in yellow. Example: "overworld:0"
#
# Note:
# Some DH settings will be disabled and/or changed to improve
# visuals when DH rendering is disabled.
ignoredDimensionCsv = ""
#
# Requires a restart to change.
#
# Options:
# AUTO - changes based on the most likely API for that MC version
# OPEN_GL - The Default for MC 1.21.11 and older (supports Iris shaders)
# BLAZE_3D - The Default for MC 26.1.2 and newer (supports Vulkan)
renderingEngine = "AUTO"
#
# This is the earth size ratio when applying the curvature shader effect.
# Note: Enabling this feature may cause rendering bugs.
#
# 0 = flat/disabled
# 1 = 1 to 1 (6,371,000 blocks)
# 100 = 1 to 100 (63,710 blocks)
# 10000 = 1 to 10000 (637.1 blocks)
#
# Note: Due to current limitations, the min value is [50]
# and the max value is 5000. Any values outside this range
# will be set to 0 (disabled).
earthCurveRatio = 0
[client.advanced.graphics.genericRendering]
#
# A comma separated separated list of dimension resource locations where DH clouds will render.
#
# Example: "minecraft:overworld,minecraft:the_end"
#
# Changes require a world re-load.
dimensionEnabledCloudRenderingCsv = "minecraft:overworld"
#
# If true LOD clouds will be rendered.
enableCloudRendering = true
#
# If true LOD beacon beams will be rendered wider at extreme distances,
# making them easier to see.
# If false all LOD beacon beams will only ever be 1 block wide.
expandDistantBeacons = true
#
# Sets the maximum height beacons will render up to.
#
# Requires a world re-load to take affect.
beaconRenderHeight = 6000
#
# If true LOD beacon beams will be rendered.
enableBeaconRendering = true
#
# If true non terrain objects will be rendered by DH.
# i.e. beacon beams and clouds.
enableGenericRendering = true
#
# False = DH will render a single layer of clouds, like vanilla Minecraft.
# True = DH will render 3 layers of clouds at different heights.
enableMultiLayerClouds = true
[client.advanced.graphics.quality]
#
# What is the maximum detail LODs can render at?
# Higher settings will increase memory and GPU usage.
#
# CHUNK: render 1 LOD for each Chunk.
# HALF_CHUNK: render 4 LODs for each Chunk.
# FOUR_BLOCKS: render 16 LODs for each Chunk.
# TWO_BLOCKS: render 64 LODs for each Chunk.
# BLOCK: render 256 LODs for each Chunk (width of one block).
#
# Fastest: CHUNK
# Fanciest: BLOCK
maxHorizontalResolution = "BLOCK"
#
# If true LODs will fade away as you get closer to them.
# If false LODs will cut off abruptly at a set distance from the camera.
# This setting is affected by the vanilla overdraw prevention config.
ditherDhFade = true
#
# If true DH will try to use the camera position when
# determining LOD quality drop-off.
# If false DH will use the player's position.
#
# Enabling helps free-cam mods render correctly.
# Disabling helps multi-camera mods render correctly (ie Immersive Portals or camera mods).
useCameraPositionForQualityDropOff = true
#
# How bright LOD colors are.
#
# 0 = black
# 1 = normal
# 2 = near white
brightnessMultiplier = "1.0"
#
# How many detail levels zooming in can increase LOD quality by.
# Higher numbers allow stronger zooms to render crisper terrain,
# but will increase memory and GPU usage while zoomed in.
#
# A vanilla spyglass needs 4 detail levels to reach its full quality.
maxZoomQualityIncrease = 4
#
# How should LODs be shaded?
#
# AUTO: Uses the same side shading as vanilla Minecraft blocks.
# ENABLED: Simulates Minecraft's block shading for LODs.
# Can be used to force LOD shading when using some shaders.
# DISABLED: All LOD sides will be rendered with the same brightness.
lodShading = "AUTO"
#
# How saturated LOD colors are.
#
# 0 = black and white
# 1 = normal
# 2 = very saturated
saturationMultiplier = "1.0"
#
# This indicates how well LODs will represent
# overhangs, caves, floating islands, etc.
# Higher options will make the world more accurate, butwill increase memory and GPU usage.
#
# Lowest Quality: HEIGHT_MAP
# Highest Quality: PIXEL_ART
verticalQuality = "MEDIUM"
#
# The radius of the mod's render distance. (measured in chunks)
#
# Note: this is a best effort number.
# The actual render distance may be above or below this number
# depending on your other graphic settings.
lodChunkRenderDistanceRadius = 256
#
# How should the sides and bottom of grass block LODs render?
#
# AS_GRASS: all sides of dirt LOD's render using the top (green) color.
# FADE_TO_DIRT: sides fade from grass to dirt.
# AS_DIRT: sides render entirely as dirt.
grassSideRendering = "FADE_TO_DIRT"
#
# This indicates how far apart drops in LOD quality are.
#
# Higher settings will increase the distance between drops
# but will increase memory and GPU usage.
horizontalQuality = "MEDIUM"
#
# How should LOD transparency be handled.
#
# COMPLETE: LODs will render transparent.
# DISABLED: LODs will be opaque.
transparency = "COMPLETE"
#
# This is the same as vanilla Biome Blending settings for Lod area.
# Note: anything above '0' will slow down LOD loading time.
#
# '0' equals to Vanilla Biome Blending of '1x1' or 'OFF',
# '1' equals to Vanilla Biome Blending of '3x3',
# '2' equals to Vanilla Biome Blending of '5x5'...
lodBiomeBlending = 3
#
# How should vanilla Minecraft fade into Distant Horizons LODs?
#
# NONE: Fastest, there will be a pronounced border between DH and MC rendering.
# SINGLE_PASS: Fades after MC's transparent pass, opaque blocks underwater won't be faded.
# DOUBLE_PASS: Slowest, fades after both MC's opaque and transparent passes, provides the smoothest transition.
vanillaFadeMode = "DOUBLE_PASS"
#
# If true LOD quality will increase when the camera is zoomed in,
# IE when using a spyglass or zoom mod.
#
# Only LODs visible through the camera view are affected.
#
# When zoomed in LODs will load to the same detail level
# they would have if you were close to them.
increaseQualityWhenZoomedIn = true
[client.advanced.graphics.fog]
#
# Should Minecraft's fog render?
# Note: Other mods may conflict with this setting.
enableVanillaFog = false
#
# What is the maximum fog thickness?
#
# 0.0: No fog.
# 1.0: Fully opaque fog.
farFogMax = "1.0"
#
# Determines if fog is drawn on DH LODs.
enableDhFog = true
#
# At what distance should the far fog start?
#
# 0.0: Fog starts at the player's position.
# 1.0: Fog starts at the closest edge of the vanilla render distance.
# 1.414: Fog starts at the corner of the vanilla render distance.
farFogStart = "0.4"
#
# What is the minimum fog thickness?
#
# 0.0: No fog.
# 1.0: Fully opaque fog.
farFogMin = "0.0"
#
# What color should fog use?
#
# USE_WORLD_FOG_COLOR: Use the world's fog color.
# USE_SKY_COLOR: Use the sky's color.
colorMode = "USE_WORLD_FOG_COLOR"
#
# How should the fog thickness should be calculated?
#
# LINEAR: Linear based on distance (will ignore 'density')
# EXPONENTIAL: 1/(e^(distance*density))
# EXPONENTIAL_SQUARED: 1/(e^((distance*density)^2)
farFogFalloff = "EXPONENTIAL_SQUARED"
#
# Used in conjunction with the Fog Falloff.
farFogDensity = "2.5"
#
# Where should the far fog end?
#
# 0.0: Fog ends at player's position.
# 1.0: Fog ends at the closest edge of the vanilla render distance.
# 1.414: Fog ends at the corner of the vanilla render distance.
farFogEnd = "1.0"
[client.advanced.graphics.fog.heightFog]
#
# Where should the height fog start?
#
# ABOVE_CAMERA: Height fog starts at the camera and goes towards the sky
# BELOW_CAMERA: Height fog starts at the camera and goes towards the void
# ABOVE_AND_BELOW_CAMERA: Height fog starts from the camera to goes towards both the sky and void
# ABOVE_SET_HEIGHT: Height fog starts from a set height and goes towards the sky
# BELOW_SET_HEIGHT: Height fog starts from a set height and goes towards the void
# ABOVE_AND_BELOW_SET_HEIGHT: Height fog starts from a set height and goes towards both the sky and void
heightFogDirection = "BELOW_SET_HEIGHT"
#
# What is the minimum fog thickness?
#
# 0.0: No fog.
# 1.0: Fully opaque fog.
heightFogMin = "0.0"
#
# If the height fog is calculated around a set height, what is that height position?
heightFogBaseHeight = "80.0"
#
# What is the maximum fog thickness?
#
# 0.0: No fog.
# 1.0: Fully opaque fog.
heightFogMax = "1.0"
#
# How should the height fog thickness should be calculated?
#
# LINEAR: Linear based on height (will ignore 'density')
# EXPONENTIAL: 1/(e^(height*density))
# EXPONENTIAL_SQUARED: 1/(e^((height*density)^2)
heightFogFalloff = "EXPONENTIAL_SQUARED"
#
# What is the height fog's density?
heightFogDensity = "20.0"
#
# How should height effect the fog thickness?
# Note: height fog is combined with the other fog settings.
#
# SPHERICAL: Fog is calculated based on camera distance.
# CYLINDRICAL: Ignore height, fog is calculated based on horizontal distance.
#
# MAX: max(heightFog, farFog)
# ADDITION: heightFog + farFog
# MULTIPLY: heightFog * farFog
# INVERSE_MULTIPLY: 1 - (1-heightFog) * (1-farFog)
# LIMITED_ADDITION: farFog + max(farFog, heightFog)
# MULTIPLY_ADDITION: farFog + farFog * heightFog
# INVERSE_MULTIPLY_ADDITION: farFog + 1 - (1-heightFog) * (1-farFog)
# AVERAGE: farFog*0.5 + heightFog*0.5
heightFogMixMode = "SPHERICAL"
#
# Should the start of the height fog be offset?
#
# 0.0: Fog start with no offset.
# 1.0: Fog start with offset of the entire world's height. (Includes depth)
heightFogStart = "0.0"
#
# Should the end of the height fog be offset?
#
# 0.0: Fog end with no offset.
# 1.0: Fog end with offset of the entire world's height. (Include depth)
heightFogEnd = "0.6"
[client.advanced.multiplayer]
#
# How should multiplayer save folders should be named?
#
# NAME_ONLY: Example: "Minecraft Server"
# IP_ONLY: Example: "192.168.1.40"
# NAME_IP: Example: "Minecraft Server IP 192.168.1.40"
# NAME_IP_PORT: Example: "Minecraft Server IP 192.168.1.40:25565"NAME_IP_PORT_MC_VERSION: Example: "Minecraft Server IP 192.168.1.40:25565 GameVersion 1.16.5"
serverFolderNameMode = "NAME_ONLY"
@@ -0,0 +1,35 @@
[visuals]
#whether to cave maps are visible when held by players from the third-person perspective.
cave_maps_visible_in_third_person = true
#whether to shake the screen from tremorsaurus stomping, nuclear explosions, etc.
screen_shaking = true
#true if some block models, like uranium ore or abyssmarine bricks render as fullbright. May increase load time, no gameplay performance impact.
emissive_block_models = true
#whether to make the screen flash white during nuclear explosions.
nuclear_bomb_flash = true
#true if some biomes, such as primordial caves, have ambient light that makes the biome easier to see in.
biome_ambient_light = true
#true if some biomes, such as toxic caves, apply a color to ambient light. May conflict with shaders.
biome_ambient_light_coloring = true
#true if some biomes, such as primordial caves, have an always well-lit sky when in them. May conflict with shaders.
biome_sky_overrides = true
#true if some biomes, such as abyssal chasm, have an thicker water fog to them. May conflict with shaders.
biome_sky_fog_overrides = true
#true if ambersol block renders with rays of light emerging from it.
ambersol_shines = true
#true if irradiated effect makes mobs glow. May conflict with shaders.
radiation_glow_effect = true
#determines how far to the left the subterranodon flight indicator renders on the screen when mounted. Negative numbers will render it on the right.
# Default: 22
# Range: -12000 ~ 12000
subterranodon_indicator_x = 22
#determines how far from bottom the subterranodon flight indicator renders on the screen when mounted.
# Default: 6
# Range: -12000 ~ 12000
subterranodon_indicator_y = 6
#true if sugar rush makes the world more saturated. May conflict with shaders.
sugar_rush_saturation_effect = true
[audio]
#whether nuclear explosions briefly muffle other sounds.
nuclear_bomb_muffles_sounds = true
@@ -0,0 +1,14 @@
{
"ingredientDedupe": false,
"debugItemStackModifications": false,
"version": 1,
"debugNativeImage": false,
"disableSearchTree": false,
"logIntervalInMinutes": 10,
"showSummaryOnDebugScreen": true,
"memoryUsageWarningPercentage": 90,
"debugChunkLoading": false,
"debugThreadsStuck": false,
"disableEternalStarlightProgress": false,
"skipTickingUnloadedFluxNetworks": false
}
@@ -0,0 +1,228 @@
{
"general": {
"engines": 0,
"volume": 1,
"useSoundMasterSource": false,
"scanStepAmount": 100,
"playSoundWithOffset": true
},
"dimensions": {
"abyssalcraft": 1,
"betweenlands": 1,
"cave": 1,
"end": 1,
"galacticraft": 1,
"galacticraft.planets": 1,
"midnight": 1,
"nether": 1,
"surface": 1,
"twilightforest": 1,
"void": 1
},
"regions": {
"abyssalcraft_dark": {
"overall": 1,
"suspense.suspense": 1
},
"end_unknown": {
"overall": 1,
"suspense.suspense": 1
},
"galacticraft_planets_wind": {
"overall": 1,
"wind.light-wind": 1
},
"nether_suspense": {
"overall": 1,
"nether": 1
},
"twilightforest_unknown": {
"overall": 1,
"suspense.suspense": 1
},
"void_dark": {
"overall": 1,
"suspense.suspense": 1
},
"beach": {
"overall": 1,
"beach": 1,
"seagull": 1,
"seagull-long": 1
},
"cave_ambience": {
"overall": 1,
"cave": 1,
"cave-water": 1,
"cave-deep": 1,
"cave-large": 1
},
"cave_deep-dark": {
"overall": 1,
"deep-dark": 1,
"water-droplets": 1
},
"cave": {
"overall": 1
},
"cave_lush": {
"overall": 1,
"cave-lush": 1,
"frog": 1
},
"cold_artic": {
"overall": 1,
"heavy-wind": 1
},
"cold": {
"overall": 1,
"wind.light-wind": 1
},
"desert": {
"overall": 1,
"cicadas-desert": 1,
"wind.light-wind": 1
},
"forest_cold": {
"overall": 1,
"crow": 1
},
"forest": {
"overall": 1,
"bird-ambience": 1,
"owl": 1
},
"forest_roofed": {
"overall": 1,
"bird-ambience-spooky": 1
},
"grass": {
"overall": 1,
"cricket": 1,
"cricket-night": 1,
"cicadas-night": 1
},
"highland": {
"overall": 1,
"heavy-wind": 1,
"wind-in-leaves": 1
},
"jungle": {
"overall": 1,
"cricket-jungle": 1,
"cricket-jungle-night": 1,
"bird-ambience-jungle": 1,
"bird-ambience-jungle-night": 1
},
"mesa": {
"overall": 1,
"wind-mesa": 1
},
"mushroom_fields": {
"overall": 1,
"cicadas-mushroom-fields": 1,
"special-mushroom-fields": 1
},
"ocean": {
"overall": 1,
"ocean": 1,
"wind.light-wind": 1
},
"ocean_deep": {
"overall": 1,
"whale": 1
},
"pale-garden": {
"overall": 1,
"pale-garden-ambience": 1,
"owl": 1
},
"plains": {
"overall": 1,
"cricket": 1,
"cricket-night": 1,
"cicadas-night": 1
},
"rain": {
"overall": 1,
"rain": 1
},
"savanna": {
"overall": 1,
"cricket": 1,
"cicadas": 1,
"cicadas-desert": 1,
"cricket-warm-night": 1,
"wolf": 1,
"long-wolf": 1,
"bird-warm": 1
},
"sky": {
"overall": 1,
"wind.light-wind": 1
},
"space": {
"overall": 1
},
"storm_away": {
"overall": 1,
"storm-away": 1,
"howling-wind": 1
},
"storm_close": {
"overall": 1,
"storm-close": 1,
"wind": 1
},
"surface": {
"overall": 1
},
"swamp": {
"overall": 1,
"cricket-swamp": 1,
"cricket-warm-night": 1,
"frog": 1,
"mouring_dove": 1
},
"taiga": {
"overall": 1,
"bird-ambience-huge": 1
},
"underwater": {
"overall": 1,
"underwater": 1,
"underwater-deep": 1
},
"underworld": {
"overall": 1,
"underworld": 1
},
"warden": {
"overall": 1,
"warden": 1
}
},
"categories": {
"animal": {
"animal": 1,
"bird": {
"bird": 1,
"bird_day": 1,
"bird_night": 1
},
"cicadas": 1,
"cricket": {
"cricket": 1,
"cricket_day": 1,
"cricket_night": 1
}
},
"cave": 1,
"suspense": 1,
"weather": 1,
"wind": 1
},
"fade-volume": 0.005,
"fade-pitch": 0.005,
"silent-dimensions": []
}
@@ -0,0 +1,68 @@
#Lighting
[lights]
#Show the supporter message. This is set to false after the first time.
showSupporterMessage = true
#If dynamic lights are enabled
lightsEnabled = false
#How bright the touch light is
# Default: 8
# Range: 0 ~ 15
touchLightLuminance = 8
#How long the touch light lasts in ticks
# Default: 8
# Range: 0 ~ 40
touchLightDuration = 8
#Light level an entity should emit when dynamic lights are on
#Example entry: minecraft:blaze=15
entity_lights = ["minecraft:blaze=10", "ars_nouveau:orbit=15", "ars_nouveau:linger=15", "ars_nouveau:spell_proj=15", "minecraft:magma_cube=8", "ars_nouveau:flying_item=10", "minecraft:spectral_arrow=8", "ars_nouveau:follow_proj=10"]
#Light level an item should emit when held when dynamic lights are on
#Example entry: minecraft:stick=15
item_lights = ["minecraft:redstone_torch=10", "minecraft:soul_lantern=12", "minecraft:glow_ink_sac=10", "minecraft:verdant_froglight=15", "minecraft:blaze_rod=10", "minecraft:shroomlight=10", "minecraft:lantern=14", "minecraft:soul_torch=10", "minecraft:glow_berries=8", "minecraft:glowstone_dust=8", "minecraft:pearlescent_froglight=15", "minecraft:nether_star=14", "minecraft:glowstone=15", "minecraft:torch=14", "minecraft:ochre_froglight=15", "minecraft:lava_bucket=15"]
#Overlay
[overlays]
#X offset for the tooltip
# Default: 20
# Range: > -2147483648
xTooltip = 20
#Y offset for the tooltip
# Default: 0
# Range: > -2147483648
yTooltip = 0
#X offset for the Mana Bar
# Default: 0
# Range: > -2147483648
xManaBar = 0
#Y offset for the Mana Bar
# Default: 0
# Range: > -2147483648
yManaBar = 0
#X offset for the Spell Name
# Default: 10
# Range: > -2147483648
xSpellName = 10
#Y offset for the Spell Name
# Default: 30
# Range: > -2147483648
ySpellName = 30
#If the Storage Lectern should show the recipe book icon
showRecipeBook = true
#If scrolling in the Storage Lectern should be inverted
invertLecternScrolling = false
#Inform the player of Dynamic lights once.
informLights = false
#Whether the Selection HUD is toggled or held
toggleSelectionHUD = true
#Misc
[misc]
#Use simplified renderer for Warp Portals
no_end_portal_render = false
#Disables the skyweave renderer. Disable if your sky is broken with shaders.
disable_skyweave = false
#Show spell tooltips with glyphs instead of plain text
glyphTooltips = true
#Enables transparent/opaque rendering of elements in the book GUI. Disable if it leads to crash with Sodium derivatives
gui_transparency = true
#Disables translucent particles. Disable if your particles are invisible with shaders.
opaque_particles = false
@@ -0,0 +1,147 @@
#.
#Client-only settings - If you're looking for general settings, look inside your worlds serverconfig folder!
[client]
#.
#Show item descriptions on Shift and controls on Ctrl.
enableTooltips = true
#.
#Display a tooltip when looking at overstressed components.
enableOverstressedTooltip = true
#.
#Log a stack-trace when rendering issues happen within a moving contraption.
explainRenderErrors = false
#.
#Higher density means more spawned particles.
# Default: 0.5
# Range: 0.0 ~ 1.0
fanParticleDensity = 0.5
#.
#[in Blocks]
#Maximum Distance to the player at which items in Blocks' filter slots will be displayed
# Default: 10.0
# Range: 1.0 ~ 3.4028234663852886E38
filterItemRenderDistance = 10.0
#.
#Choose the menu row that the Create config button appears on in the main menu
#Set to 0 to disable the button altogether
# Default: 2
# Range: 0 ~ 4
mainMenuConfigButtonRow = 2
#.
#Offset the Create config button in the main menu by this many pixels on the X axis
#The sign (-/+) of this value determines what side of the row the button appears on (left/right)
# Default: -4
# Range: > -2147483648
mainMenuConfigButtonOffsetX = -4
#.
#Choose the menu row that the Create config button appears on in the in-game menu
#Set to 0 to disable the button altogether
# Default: 3
# Range: 0 ~ 5
ingameMenuConfigButtonRow = 3
#.
#Offset the Create config button in the in-game menu by this many pixels on the X axis
#The sign (-/+) of this value determines what side of the row the button appears on (left/right)
# Default: -4
# Range: > -2147483648
ingameMenuConfigButtonOffsetX = -4
#.
#Setting this to true will prevent Create from sending you a warning when playing with Fabulous graphics enabled
ignoreFabulousWarning = false
#.
#Disable to prevent being rotated while seated on a Moving Contraption
rotateWhenSeated = true
#.
#Configure your vision range when submerged in Create's custom fluids
[client.fluidFogSettings]
#.
#The vision range through honey will be multiplied by this factor
# Default: 1.0
# Range: 0.125 ~ 256.0
honey = 1.0
#.
#The vision range though chocolate will be multiplied by this factor
# Default: 1.0
# Range: 0.125 ~ 256.0
chocolate = 1.0
#.
#Settings for the Goggle Overlay
[client.goggleOverlay]
#.
#Offset the overlay from goggle- and hover- information by this many pixels on the respective axis; Use /create overlay
# Default: 20
# Range: > -2147483648
overlayOffsetX = 20
#.
#Offset the overlay from goggle- and hover- information by this many pixels on the respective axis; Use /create overlay
# Default: 0
# Range: > -2147483648
overlayOffsetY = 0
#.
#Enable this to use your custom colors for the Goggle- and Hover- Overlay
customColorsOverlay = false
#.
#The custom background color to use for the Goggle- and Hover- Overlays, if enabled
#[in Hex: #AaRrGgBb]
#[@cui:IntDisplay:#]
# Default: -267386864
# Range: > -2147483648
customBackgroundOverlay = -267386864
#.
#The custom top color of the border gradient to use for the Goggle- and Hover- Overlays, if enabled
#[in Hex: #AaRrGgBb]
#[@cui:IntDisplay:#]
# Default: 1347420415
# Range: > -2147483648
customBorderTopOverlay = 1347420415
#.
#The custom bot color of the border gradient to use for the Goggle- and Hover- Overlays, if enabled
#[in Hex: #AaRrGgBb]
#[@cui:IntDisplay:#]
# Default: 1344798847
# Range: > -2147483648
customBorderBotOverlay = 1344798847
#.
#Sound settings
[client.sound]
#.
#Make cogs rumble and machines clatter.
enableAmbientSounds = true
#.
#Maximum volume modifier of Ambient noise
# Default: 0.10000000149011612
# Range: 0.0 ~ 1.0
ambientVolumeCap = 0.10000000149011612
#.
#Mod Integration and Recipe Viewer
[client.recipeViewerIntegration]
#.
#How Recipe Viewer search should interact with Stock Keepers
#Allowed Values: SYNC_BOTH, SYNC_FROM_JEI, SYNC_FROM_STOCK_KEEPER, NONE
syncRecipeViewerSearch = "SYNC_BOTH"
#.
#Railway related settings
[client.trains]
#.
#How far away the Camera should zoom when seated on a train
# Default: 3.0
# Range: 0.0 ~ 3.4028234663852886E38
mountedZoomMultiplier = 3.0
#.
#Display nodes and edges of a Railway Network while f3 debug mode is active
showTrackGraphOnF3 = false
#.
#Additionally display materials of a Rail Network while f3 debug mode is active
showExtendedTrackGraphOnF3 = false
#.
#Display Track Networks and Trains on supported map mods
showTrainMapOverlay = true
#.
#Track Network Color on maps
#Allowed Values: RED, GREY, WHITE
trainMapColorTheme = "RED"
@@ -0,0 +1,36 @@
{
"allowedCEM": "ALL",
"logModelCreationData": false,
"debugOnRightClick": false,
"renderModeChoice": "NORMAL",
"vanillaModelHologramRenderMode_2": "OFF",
"modelExportMode": "NONE",
"automaticModelExporting": false,
"attemptPhysicsModPatch_2": "CUSTOM",
"modelUpdateFrequency": "Average",
"entityRenderModeOverrides": {},
"entityPhysicsModPatchOverrides": {},
"entityVanillaHologramOverrides": {},
"modelsNamesDisabled": [],
"allowEBEModConfigModify": true,
"animationLODDistance": 20,
"retainDetailOnLowFps": true,
"retainDetailOnLargerMobs": true,
"animationFrameSkipDuringIrisShadowPass": true,
"preventFirstPersonHandAnimating": false,
"onlyClientPlayerModel": false,
"doubleChestAnimFix": true,
"enforceOptifineVariationRequiresDefaultModel": false,
"enforceOptifineVariationRequiresDefaultModel_v2": false,
"resetPlayerModelEachRender": true,
"resetPlayerModelEachRender_v2": true,
"onlyDebugRenderOnHover": false,
"enforceOptifineSubFoldersVariantOnly": false,
"enforceOptiFineAnimSyntaxLimits": true,
"allowOptifineFallbackProperties": true,
"showReloadErrorToast": true,
"exportRotations": false,
"asmMaths": true,
"logASM": false,
"parrotShoulderPositionAnimatesByDefault": true
}
@@ -0,0 +1,41 @@
{
"optifine_limitRandomVariantGapsBy10": false,
"optifine_allowWeirdSkipsInTrueRandom": true,
"optifine_preventBaseTextureInOptifineDirectory": true,
"illegalPathSupportMode": "None",
"enableCustomTextures": true,
"enableCustomBlockEntities": true,
"textureUpdateFrequency_V2": "Fast",
"enableEmissiveTextures": true,
"enableEnchantedTextures": true,
"enableEmissiveBlockEntities": true,
"emissiveRenderMode": "DULL",
"alwaysCheckVanillaEmissiveSuffix": true,
"enableArmorAndTrims": true,
"skinFeaturesEnabled": true,
"skinTransparencyMode": "ETF_SKINS_ONLY",
"skinTransparencyInExtraPixels": true,
"skinFeaturesEnableTransparency": true,
"skinFeaturesEnableFullTransparency": false,
"tryETFTransparencyForAllSkins": false,
"enableEnemyTeamPlayersSkinFeatures": true,
"enableBlinking": true,
"blinkFrequency": 150,
"blinkLength": 1,
"advanced_IncreaseCacheSizeModifier": 1.0,
"debugLoggingMode": "None",
"logTextureDataInitialization": false,
"hideConfigButton": false,
"stackDebugPrinting": false,
"configButtonLoc": "BOTTOM_RIGHT",
"disableVanillaDirectoryVariantTextures": false,
"use3DSkinLayerPatch": true,
"enableFullBodyWardenTextures": true,
"entityEmissiveOverrides": {},
"propertiesDisabled": [],
"propertyInvertUpdatingOverrides": [],
"entityRandomOverrides": {},
"entityEmissiveBrightOverrides": {},
"entityRenderLayerOverrides": {},
"entityLightOverrides": {}
}
@@ -0,0 +1,149 @@
[ui]
#Tooltips for Developing Film Rolls and Copying Photographs will be shown on Film Rolls and Photographs respectively, describing the crafting recipe.
#Only when JEI is not installed. (Only JEI shows these recipes, not REI or EMI)
recipe_tooltips_without_jei = true
#Details about Camera configuring will be shown in Camera item tooltip.
camera_details_tooltip = true
#Film Roll Frames will be shown in the camera tooltip.
#Default: true
camera_film_frames_tooltip = true
#Film Roll fullness bar will be shown on the Camera item.
#Default: false
camera_shows_film_bar = false
#Photographer name will be shown in Photograph's tooltip.
photograph_photographer_name_tooltip = false
#Crosshair will not get in the way when holding a photograph.
photograph_in_hand_hide_crosshair = true
#When looking at the Camera Stand, in-world tooltip will show information about the camera on it. Default: true
camera_stand_tooltip = true
#Album will show how many photographs it contains in a tooltip.
album_show_photos_count = true
[ui.album]
#Color in hex format. AARRGGBB.
font_main_color = "FFB59774"
#Color in hex format. AARRGGBB.
font_secondary_color = "FFEFE4CA"
#Color in hex format. AARRGGBB.
selection_color = "FF8888FF"
#Color in hex format. AARRGGBB.
selection_unfocused_color = "FFBBBBFF"
[viewfinder]
#Clicking middle mouse button will open Viewfinder Controls. This is independent of Open Camera Controls keybind.
#Allows opening camera controls without dismounting from a vehicle - and keeping controls on sneak or other button as well.
#Default: true
middle_click_controls = true
#How much zooming influences mouse sensitivity.
#0 - no change to sensitivity. 1 - full effect.
#Default: 0.75
# Default: 0.75
# Range: 0.0 ~ 1.0
zoom_sensitivity_influence = 0.75
#Color in hex format. AARRGGBB.
background_color = "FA1F1D1B"
#Color in hex format. AARRGGBB.
font_main_color = "FF2B2622"
#Color in hex format. AARRGGBB.
font_secondary_color = "FF7A736C"
#HUD will be hidden while looking through viewfinder. Default: true
hide_hud_while_in_viewfinder = true
#X offset of a viewfinder status icon. Default: 0
# Default: 0
# Range: -999 ~ 999
status_icon_offset_x = 0
#Y offset of a viewfinder status icon. Default: 0
# Default: 0
# Range: -999 ~ 999
status_icon_offset_y = 0
[capture]
#Keep Post Effect (vanilla shader) when capturing an image.
#It is sometimes used by mods to change how player sees the world. (Cold Sweat's overheating blur, Supplementaries mob heads, for example).
#In vanilla, it's only used when spectating a creeper/enderman/etc.
#Default: false
keep_post_effect = false
#Delay in ticks before capturing an image when shooting with flash.
#If you experience flash synchronization issues (Flash having no effect on the image) - try increasing the value.
# Default: 4
# Range: 1 ~ 8
flash_capture_delay_ticks = 4
#Force legacy (pre 1.21) capturing method for taking images. Enable if you experiencing issues with resulting images.
#Direct method will be used regardless of this setting if mods defined in 'force_direct_capture_default_mods' is installed.
#Default: false
force_direct_capture = false
#Direct capture will be used if any of these mods is installed.
#Format: '["mod_id", "mod_id"]'. Default: [veil, pmweather]
force_direct_capture_default_mods = ["veil", "pmweather"]
#Delay in frames before capturing an image if 'direct_capture' method is in use.
#Set to higher value when leftovers of GUI elements (such as nameplates) are visible on the images
#(some shaders have temporal effects that take several frames to disappear fully)
# Default: 0
# Range: 0 ~ 100
direct_capture_delay_frames = 0
#Sets the game to 'panoramic mode' when capturing the image with background capture method (default).
#Enabling this might fix some graphical issues, such as water being invisible.
#If captured images are still not looking properly - enable 'force_direct_capture'.
#Default: false
background_capture_use_panoramic_mode = false
[capture.loading]
#Loading from file will only accept a path relative to game directory, when on the dedicated server (multiplayer).
#This option is for protection against malicious actions using Projector or load commands.
#Default: false
multiplayer_file_loading_only_from_game_dir = false
#How loading from URL should behave when on the dedicated server (multiplayer).
#This option is for protection against malicious actions using Projector or load commands.
#Default: ONLY_ALLOWED_DOMAINS
#Allowed Values: ALL, ONLY_ALLOWED_DOMAINS, NONE
multiplayer_url_loading = "ONLY_ALLOWED_DOMAINS"
#List of allowed domains for loading from URl, if 'multiplayer_url_loading' is set to ONLY_ALLOWED_DOMAINS.
multiplayer_url_loading_allowed_domains = ["i.imgur.com", "cdn.discordapp.com", "media.discordapp.net", "raw.githubusercontent.com"]
#List of allowed domains for loading from URl, if 'multiplayer_url_loading' is set to ONLY_ALLOWED_DOMAINS.
multiplayer_url_loading_allowed_subdomains = [".imgur.com", ".discordapp.com"]
[render]
#Makes photos in Photograph Frame render with alignment to 16 pixel grid (like paintings). Just for fun. Default: false
pixel_perfect_photograph_frame = false
#Photographs in Item Frame will be rendered as image instead of an item icon. Default: false
photograph_renders_in_item_frame = false
#Projected photographs (using Interplanar Projector) made by other players will be censored (pixelated). Default: false
censor_projected_photographs_made_by_others = false
#All photographs made by other players will will be censored (pixelated). Default: false
censor_all_photographs_made_by_others = false
#Distance from the player beyond which Photograph Frame would not be rendered. Default: 64
#Note: this number may not relate to distance in blocks exactly. It's influenced by render distance and entity distance settings.
# Default: 64
# Range: 8 ~ 128
photograph_frame_culling_distance = 64
#Depth offset to Photograph in Photograph Frame. Can be used to fix issues with some 3D resourcepacks. Value of 0.015 is good for 'Classic 3D' resourcepack. Default: 0.0
# Default: 0.0
# Range: -1.0 ~ 1.0
photograph_frame_image_offset = 0.0
[integration]
#Useful information about some items will be shown in JEI description category. Default: true
jei_information = true
#[Real Camera] Disable player model rendering when looking through viewfinder. Default: true
real_camera_disable_in_viewfinder = true
[export]
#When the Photograph you took is viewed in UI (by using a Photograph), image will be exported to '<instance>/exposures' folder as a png.
export_viewed_photographs = true
#When exporting, exposures will be organized to subfolders with world name.
export_organize_by_world = true
#Exported exposures will be scaled by this multiplier.
#Given the default exposure size of 320 pixels - this will produce:
#320/640/960/1280/etc image. Be careful with larger frame sizes.
#Default: 2
# Default: 2
# Range: 1 ~ 10
export_size_multiplier = 2
[tutorial]
#Toast that teaches hovering mouse over camera parts will be shown when attachments menu is first opened. Default: true.
#*This setting will be automatically set to false on first show.*
attachments_show_info_toast = true
#Toast that teaches wiki opening will be shown when attachments menu is first opened, after info toast. Default: true.
#*This setting will be automatically set to false on first show.*
attachments_show_wiki_toast = true
@@ -0,0 +1,24 @@
#Use a slightly more compact, but also slightly slower representation for block states
compactFastMap = false
#Replace objects used to detect multi-threaded access to chunks by a much smaller field. This option is disabled by default due to very rare and very hard-to-reproduce crashes, use at your own risk!
useSmallThreadingDetector = false
#Cache the predicate instances used in multipart models
cacheMultipartPredicates = true
#Do not create a new MultipartBakedModel instance for each block state using the same multipartmodel. Requires cacheMultipartPredicates to be enabled
multipartDeduplication = true
#Deduplicate cached data for blockstates, most importantly collision and render shapes
blockstateCacheDeduplication = true
#Save memory overhead from empty data component maps/patched
dataComponentPatch = true
#Avoid creation of new strings when creating ModelResourceLocations
modelResourceLocations = true
#Use smaller data structures for "simple" models, especially models with few side-specific faces
modelSides = true
#Replace the blockstate neighbor table
replaceNeighborLookup = true
#Populate the neighbor table used by vanilla. Enabling this slightly increases memory usage, but can help with issues in the rare case where mods access it directly.
populateNeighborTable = false
#Do not store the properties of a state explicitly and read themfrom the replace neighbor table instead. Requires replaceNeighborLookup to be enabled
replacePropertyMap = true
#Deduplicate vertex data of baked quads in the basic model implementations
bakedQuadDeduplication = true
@@ -0,0 +1,14 @@
#Select the backend to use. Set to "DEFAULT" to let Flywheel decide.
backend = "DEFAULT"
#Enable or disable instance update limiting with distance.
limitUpdates = true
#The number of worker threads to use. Set to -1 to let Flywheel decide. Set to 0 to disable parallelism. Requires a game restart to take effect.
# Default: -1
# Range: -1 ~ 4
workerThreads = -1
#Config options for Flywheel's built-in backends.
[flw_backends]
#How smooth Flywheel's shader-based lighting should be. May have a large performance impact.
#Allowed Values: FLAT, TRI_LINEAR, SMOOTH, SMOOTH_INNER_FACE_CORRECTED
lightSmoothness = "SMOOTH"
@@ -0,0 +1,22 @@
{
"REGULAR_INFO": "----- Regular config values below -----",
"font_atlas_resizing": true,
"map_atlas_generation": true,
"hud_batching": true,
"fast_text_lookup": true,
"fast_buffer_upload": true,
"COSMETIC_INFO": "----- Cosmetic only config values below (Does not optimize anything) -----",
"dont_add_info_into_debug_hud": false,
"EXPERIMENTAL_INFO": "----- Experimental config values below (Rendering glitches may occur) -----",
"experimental_disable_error_checking": false,
"experimental_disable_resource_pack_conflict_handling": false,
"experimental_sign_text_buffering": false,
"experimental_screen_batching": false,
"DEBUG_INFO": "----- Debug only config values below (Do not touch) -----",
"debug_only_and_not_recommended_disable_universal_batching": false,
"debug_only_and_not_recommended_disable_mod_conflict_handling": false,
"debug_only_and_not_recommended_disable_hardware_conflict_handling": false,
"debug_only_print_additional_error_information": false,
"debug_only_use_last_usage_for_batch_ordering": false,
"debug_only_detailed_memory_leak_detection": false
}
@@ -0,0 +1,6 @@
# This is the configuration file for Lithium.
#
# You can find information on editing this file and all the available options here:
# https://github.com/CaffeineMC/lithium-fabric/wiki/Configuration-File
#
# By default, this file will be empty except for this notice.
@@ -0,0 +1,2 @@
#These JEI plugins will be loaded on the main thread
blacklist_async_jei_plugins = ["jepb:jei_plugin"]
@@ -0,0 +1,105 @@
# This is the configuration file for ModernFix.
# In general, prefer using the config screen to editing this file. It can be accessed
# via the standard mod menu on your respective mod loader. Changes will, however,
# require restarting the game to take effect.
#
# The following options can be enabled or disabled if there is a compatibility issue.
# Add a line with your option name and =true or =false at the bottom of the file to enable
# or disable a rule. For example:
# mixin.perf.dynamic_resources=true
# Do not include the #. You may reset to defaults by deleting this file.
#
# To enable features that are still in testing, add a line at the bottom setting the stability level:
# stability_level=BETA
#
# Available options:
# mixin.bugfix.chunk_deadlock=true # (default)
# mixin.bugfix.cofh_core_crash=true # (default)
# mixin.bugfix.concurrency=true # (default)
# mixin.bugfix.ctm_resourceutil_cme=true # (default)
# mixin.bugfix.ender_dragon_leak=true # (default)
# mixin.bugfix.entity_pose_stack=true # (default)
# mixin.bugfix.extra_experimental_screen=true # (default)
# mixin.bugfix.fix_config_crashes=true # (default)
# mixin.bugfix.missing_block_entities=false # (default)
# mixin.bugfix.packet_leak=false # (default)
# mixin.bugfix.paper_chunk_patches=true # (default)
# mixin.bugfix.recipe_book_type_desync=true # (default)
# mixin.bugfix.restore_old_dragon_movement=false # (default)
# mixin.bugfix.skip_redundant_saves=false # (default)
# mixin.bugfix.sprite_borders=true # (default)
# mixin.bugfix.unsafe_modded_shape_caches=true # (default)
# mixin.bugfix.world_leaks=true # (default)
# mixin.bugfix.world_screen_skipped=true # (default)
# mixin.devenv=false # (default)
# mixin.feature.blockentity_incorrect_thread=false # (default)
# mixin.feature.branding=true # (default)
# mixin.feature.cause_lag_by_disabling_threads=false # (default)
# mixin.feature.direct_stack_trace=false # (default)
# mixin.feature.integrated_server_watchdog=true # (default)
# mixin.feature.log_stdout_in_log_files=true # (default)
# mixin.feature.mcfunction_profiling=true # (default)
# mixin.feature.measure_time=true # (default)
# mixin.feature.registry_event_progress=true # (default)
# mixin.feature.remove_chat_signing=false # (default)
# mixin.feature.remove_telemetry=true # (default)
# mixin.feature.snapshot_easter_egg=true # (default)
# mixin.feature.spam_thread_dump=false # (default)
# mixin.feature.spark_profile_launch=false # (default)
# mixin.feature.spark_profile_world_join=false # (default)
# mixin.feature.stalled_chunk_load_detection=false # (default)
# mixin.feature.suppress_narrator_stacktrace=true # (default)
# mixin.feature.warn_missing_perf_mods=true # (default)
# mixin.launch.class_search_cache=true # (default)
# mixin.perf.attribute_supplier_dedup=true # (default)
# mixin.perf.cache_blockstate_cache_arrays=true # (default)
# mixin.perf.cache_profile_texture_url=true # (default)
# mixin.perf.cache_strongholds=true # (default)
# mixin.perf.capability_list_compaction=true # (default)
# mixin.perf.chunk_meshing=true # (default)
# mixin.perf.clear_mixin_classinfo=false # (default)
# mixin.perf.compact_bit_storage=true # (default)
# mixin.perf.compact_entity_models=true # (default)
# mixin.perf.compact_imposterprotochunks=true # (default)
# mixin.perf.compact_mojang_registries=true # (default)
# mixin.perf.compress_unihex_font=true # (default)
# mixin.perf.datapack_reload_exceptions=true # (default)
# mixin.perf.dedicated_reload_executor=true # (default)
# mixin.perf.deduplicate_climate_parameters=false # (default)
# mixin.perf.deduplicate_location=false # (default)
# mixin.perf.deduplicate_wall_shapes=true # (default)
# mixin.perf.dynamic_dfu=true # (default)
# mixin.perf.dynamic_entity_renderers=false # (default)
# mixin.perf.dynamic_languages=true # (default)
# mixin.perf.dynamic_resources=false # (default)
# mixin.perf.dynamic_resources.ctm=true # (default)
# mixin.perf.dynamic_resources.ldlib=true # (default)
# mixin.perf.dynamic_structure_manager=true # (default)
# mixin.perf.encoder_cache_leak=true # (default)
# mixin.perf.faster_ingredients=true # (default)
# mixin.perf.faster_item_rendering=false # (default)
# mixin.perf.faster_texture_stitching=true # (default)
# mixin.perf.fix_loop_spin_waiting=true # (default)
# mixin.perf.forge_registry_alloc=true # (default)
# mixin.perf.ingredient_item_deduplication=true # (default)
# mixin.perf.lazy_search_tree_registry=true # (default)
# mixin.perf.memoize_creative_tab_build=true # (default)
# mixin.perf.model_optimizations=true # (default)
# mixin.perf.mojang_registry_size=true # (default)
# mixin.perf.optimize_surface_rules=true # (default)
# mixin.perf.patchouli_deduplicate_books=true # (default)
# mixin.perf.reduce_blockstate_cache_rebuilds=true # (default)
# mixin.perf.release_protochunks=true # (default)
# mixin.perf.remove_biome_temperature_cache=true # (default)
# mixin.perf.resourcepacks=true # (default)
# mixin.perf.smart_ingredient_sync=true # (default)
# mixin.perf.state_definition_construct=true # (default)
# mixin.perf.suspend_integrated_server_during_load=true # (default)
# mixin.perf.tag_id_caching=true # (default)
# mixin.perf.thread_priorities=true # (default)
# mixin.perf.ticking_chunk_alloc=true # (default)
# mixin.perf.worldgen_allocation=true # (default)
# mixin.safety=true # (default)
# stability_level=GA # (default)
#
# User overrides go here.
@@ -0,0 +1,17 @@
[client]
#Toggles the lantern glow effect, which may look bad with certain shaders.
glow_effect = true
#Toggles the Umvuthana footprint effects, which may decrease performance.
umvuthana_footprints = true
#Enable camera shaking during certain mob attacks and abilities.
do_camera_shake = true
#Play boss battle themes during boss encounters.
play_boss_music = true
#Use custom boss health bar textures, if the boss has them.
custom_boss_bar = true
#Use custom player animations.
custom_player_anims = true
#Set to true to hide your own 3rd-person player animations while you are in 1st-person view mode. This is useful with mods that render the 3rd-person model in 1st-person, as some of the Mowzie's Mobs 3rd-person animations can block the camera.
hide_player_anims_in_first_person = false
#Play Umvuthana Crane heal sounds. Turn this off if you are experiencing crashes when Cranes appear during Umvuthi's boss battle.
crane_heal_sounds = true
@@ -0,0 +1,8 @@
#EXPERIMENTAL: Enable the NeoForge block rendering pipeline - fixes the lighting of custom models.
experimentalForgeLightPipelineEnabled = false
#When enabled, NeoForge will show any warnings that occurred during loading.
showLoadWarnings = true
#Set to true to use a combined DEPTH_STENCIL attachment instead of two separate ones.
useCombinedDepthStencilAttachment = false
#A config option mainly for developers. Logs out configuration values that do not have translations when running a client in a development environment.
logUntranslatedConfigurationWarnings = true
@@ -0,0 +1,21 @@
#Whether sub-levels should apply block shading dynamically
sub_level_dynamic_shading = true
#Whether sub-levels can occlude the water surface
sub_level_water_occlusion = true
#Whether sub-levels should cast a shadow on the world
sub_level_skylight_shadows = false
#Whether to draw loaded chunks on the client in the chunk debug renderer
debug_draw_loaded_chunks = false
#The distance back in game-ticks that the snapshot interpolation should operate
# Default: 1.5
# Range: 0.0 ~ 100.0
sub_level_snapshot_interpolation_delay_ticks = 1.5
#The renderer to use for sub-levels
#Allowed Values: SODIUM_REACHAROUND
sub_level_renderer = "SODIUM_REACHAROUND"
#The zoom sensitivity for sub-level camera types
# Default: 0.2
# Range: 0.0 ~ 100.0
sub_level_zoom_sensitivity = 0.2
#If Sable should attempt to establish a UDP connection with the server, to receive sub-level movement data over a UDP channel
attempt_udp_networking = true
@@ -0,0 +1,36 @@
#.
#Settings of Simulated Items
[items]
#.
#.
#When to display physics properties in block tooltips
#Allowed Values: ALWAYS, SHIFT, GOGGLES, SHIFT_GOGGLES, NEVER
displayProperties = "GOGGLES"
#.
#The sensitivity of scrolling when holding a sub-level with the Creative Physics Staff
# Default: 0.6000000238418579
# Range: 0.0 ~ 3.4028234663852886E38
physics_staff_scroll_sensitivity = 0.6000000238418579
#.
#The sensitivity of rotation when holding a sub-level with the Creative Physics Staff
# Default: 0.3499999940395355
# Range: 0.0 ~ 3.4028234663852886E38
physics_staff_rotate_sensitivity = 0.3499999940395355
#.
#If ponders that have new additions should display the "(New!)" text
show_new_ponders = true
#.
#Settings of Simulated Blocks
[block]
#.
#.
#Offsets the Steering Wheel GUI on the X axis.
# Default: 0
# Range: -1000 ~ 1000
steering_wheel_x_offset = 0
#.
#Offsets the Steering Wheel GUI on the Y axis.
# Default: 0
# Range: -1000 ~ 1000
steering_wheel_y_offset = 0
@@ -0,0 +1,20 @@
{
"enableHat": true,
"enableJacket": true,
"enableLeftSleeve": true,
"enableRightSleeve": true,
"enableLeftPants": true,
"enableRightPants": true,
"baseVoxelSize": 1.15,
"bodyVoxelWidthSize": 1.05,
"headVoxelSize": 1.18,
"renderDistanceLOD": 14,
"enableSkulls": true,
"enableSkullsItems": true,
"skullVoxelSize": 1.1,
"fastRender": true,
"compatibilityMode": true,
"irisCompatibilityMode": false,
"applySodiumWorkaround": false,
"firstPersonPixelScaling": 1.1
}
@@ -0,0 +1,6 @@
# This is the configuration file for Sodium.
#
# You can find information on editing this file and all the available options here:
# https://github.com/CaffeineMC/sodium/wiki/Configuration-File
#
# By default, this file will be empty except for this notice.
@@ -0,0 +1,30 @@
{
"quality": {
"weather_quality": "DEFAULT",
"leaves_quality": "DEFAULT",
"enable_vignette": true,
"use_closest_point_entity_sort": false
},
"advanced": {
"enable_memory_tracing": false,
"use_advanced_staging_buffers": true,
"cpu_render_ahead_limit": 3
},
"performance": {
"chunk_builder_threads": 0,
"chunk_build_defer_mode": "ALWAYS",
"animate_only_visible_textures": true,
"use_entity_culling": true,
"use_fog_occlusion": true,
"use_block_face_culling": true,
"use_no_error_g_l_context": true,
"quad_splitting_mode": "SAFE"
},
"notifications": {
"has_cleared_donation_button": false,
"has_seen_donation_prompt": false
},
"debug": {
"terrain_sorting_enabled": true
}
}
@@ -0,0 +1,219 @@
# Values for blocks can be defined as follows:
#
# By sound type:
# WOOD=1.0
#
# By block tag:
# \#minecraft\:logs=1.0
#
# By block ID:
# minecraft\:oak_log=1.0
# Amethyst (Sound Type)
AMETHYST=1.0
# Amethyst Cluster (Sound Type)
AMETHYST_CLUSTER=1.0
# Ancient Debris (Sound Type)
ANCIENT_DEBRIS=1.0
# Anvil (Sound Type)
ANVIL=1.0
# Azalea (Sound Type)
AZALEA=1.0
# Azalea Leaves (Sound Type)
AZALEA_LEAVES=1.0
# Bamboo (Sound Type)
BAMBOO=0.1
# Bamboo Sapling (Sound Type)
BAMBOO_SAPLING=0.1
# Bamboo Wood (Sound Type)
BAMBOO_WOOD=1.0
# Bamboo Wood Hanging Sign (Sound Type)
BAMBOO_WOOD_HANGING_SIGN=1.0
# Basalt (Sound Type)
BASALT=1.0
# Big Dripleaf (Sound Type)
BIG_DRIPLEAF=1.0
# Bone Block (Sound Type)
BONE_BLOCK=1.0
# Calcite (Sound Type)
CALCITE=1.0
# Candle (Sound Type)
CANDLE=1.0
# Cave Vines (Sound Type)
CAVE_VINES=1.0
# Chain (Sound Type)
CHAIN=0.0
# Cherry Leaves (Sound Type)
CHERRY_LEAVES=1.0
# Cherry Sapling (Sound Type)
CHERRY_SAPLING=1.0
# Cherry Wood (Sound Type)
CHERRY_WOOD=1.0
# Cherry Wood Hanging Sign (Sound Type)
CHERRY_WOOD_HANGING_SIGN=1.0
# Chiseled Bookshelf (Sound Type)
CHISELED_BOOKSHELF=1.0
# Copper (Sound Type)
COPPER=1.0
# Coral Block (Sound Type)
CORAL_BLOCK=1.0
# Crop (Sound Type)
CROP=0.0
# Decorated Pot (Sound Type)
DECORATED_POT=1.0
# Decorated Pot Cracked (Sound Type)
DECORATED_POT_CRACKED=1.0
# Deepslate (Sound Type)
DEEPSLATE=1.0
# Deepslate Bricks (Sound Type)
DEEPSLATE_BRICKS=1.0
# Deepslate Tiles (Sound Type)
DEEPSLATE_TILES=1.0
# Dripstone Block (Sound Type)
DRIPSTONE_BLOCK=1.0
# Flowering Azalea (Sound Type)
FLOWERING_AZALEA=1.0
# Froglight (Sound Type)
FROGLIGHT=1.0
# Frogspawn (Sound Type)
FROGSPAWN=1.0
# Fungus (Sound Type)
FUNGUS=0.0
# Gilded Blackstone (Sound Type)
GILDED_BLACKSTONE=1.0
# Glass (Sound Type)
GLASS=0.1
# Glow Lichen (Sound Type)
GLOW_LICHEN=0.0
# Grass (Sound Type)
GRASS=1.0
# Gravel (Sound Type)
GRAVEL=1.0
# Hanging Roots (Sound Type)
HANGING_ROOTS=1.0
# Hanging Sign (Sound Type)
HANGING_SIGN=1.0
# Hard Crop (Sound Type)
HARD_CROP=1.0
# Honey Block (Sound Type)
HONEY_BLOCK=0.5
# Ladder (Sound Type)
LADDER=0.0
# Lantern (Sound Type)
LANTERN=1.0
# Large Amethyst Bud (Sound Type)
LARGE_AMETHYST_BUD=0.0
# Lily Pad (Sound Type)
LILY_PAD=0.0
# Lodestone (Sound Type)
LODESTONE=1.0
# Mangrove Roots (Sound Type)
MANGROVE_ROOTS=1.0
# Medium Amethyst Bud (Sound Type)
MEDIUM_AMETHYST_BUD=0.0
# Metal (Sound Type)
METAL=1.0
# Moss (Sound Type)
MOSS=0.75
# Moss Carpet (Sound Type)
MOSS_CARPET=0.1
# Mud (Sound Type)
MUD=1.0
# Muddy Mangrove Roots (Sound Type)
MUDDY_MANGROVE_ROOTS=1.0
# Mud Bricks (Sound Type)
MUD_BRICKS=1.0
# Netherite Block (Sound Type)
NETHERITE_BLOCK=1.0
# Netherrack (Sound Type)
NETHERRACK=1.0
# Nether Bricks (Sound Type)
NETHER_BRICKS=1.0
# Nether Gold Ore (Sound Type)
NETHER_GOLD_ORE=1.0
# Nether Ore (Sound Type)
NETHER_ORE=1.0
# Nether Sprouts (Sound Type)
NETHER_SPROUTS=1.0
# Nether Wart (Sound Type)
NETHER_WART=1.0
# Nether Wood (Sound Type)
NETHER_WOOD=1.0
# Nether Wood Hanging Sign (Sound Type)
NETHER_WOOD_HANGING_SIGN=1.0
# Nylium (Sound Type)
NYLIUM=1.0
# Packed Mud (Sound Type)
PACKED_MUD=1.0
# Pointed Dripstone (Sound Type)
POINTED_DRIPSTONE=0.0
# Polished Deepslate (Sound Type)
POLISHED_DEEPSLATE=1.0
# Powder Snow (Sound Type)
POWDER_SNOW=0.1
# Rooted Dirt (Sound Type)
ROOTED_DIRT=1.0
# Roots (Sound Type)
ROOTS=0.0
# Sand (Sound Type)
SAND=1.0
# Scaffolding (Sound Type)
SCAFFOLDING=0.0
# Sculk (Sound Type)
SCULK=1.0
# Sculk Catalyst (Sound Type)
SCULK_CATALYST=1.0
# Sculk Sensor (Sound Type)
SCULK_SENSOR=1.0
# Sculk Shrieker (Sound Type)
SCULK_SHRIEKER=1.0
# Sculk Vein (Sound Type)
SCULK_VEIN=1.0
# Shroomlight (Sound Type)
SHROOMLIGHT=1.0
# Slime Block (Sound Type)
SLIME_BLOCK=1.0
# Small Amethyst Bud (Sound Type)
SMALL_AMETHYST_BUD=0.0
# Small Dripleaf (Sound Type)
SMALL_DRIPLEAF=0.0
# Snow (Sound Type)
SNOW=0.1
# Soul Sand (Sound Type)
SOUL_SAND=1.0
# Soul Soil (Sound Type)
SOUL_SOIL=1.0
# Spore Blossom (Sound Type)
SPORE_BLOSSOM=0.0
# Stem (Sound Type)
STEM=1.0
# Stone (Sound Type)
STONE=1.0
# Suspicious Gravel (Sound Type)
SUSPICIOUS_GRAVEL=1.0
# Suspicious Sand (Sound Type)
SUSPICIOUS_SAND=1.0
# Sweet Berry Bush (Sound Type)
SWEET_BERRY_BUSH=0.0
# Tuff (Sound Type)
TUFF=1.0
# Twisting Vines (Sound Type)
TWISTING_VINES=0.0
# Vine (Sound Type)
VINE=0.0
# Wart Block (Sound Type)
WART_BLOCK=1.0
# Weeping Vines (Sound Type)
WEEPING_VINES=0.0
# Wet Grass (Sound Type)
WET_GRASS=0.1
# Wood (Sound Type)
WOOD=1.0
# Wool (Sound Type)
WOOL=1.5
# Jukebox (Block)
minecraft\:jukebox=0.0
# Lava (Block)
minecraft\:lava=0.75
# Water (Block)
minecraft\:water=0.25
@@ -0,0 +1,213 @@
# Values for blocks can be defined as follows:
#
# By sound type:
# WOOD=1.0
#
# By block tag:
# \#minecraft\:logs=1.0
#
# By block ID:
# minecraft\:oak_log=1.0
# Amethyst (Sound Type)
AMETHYST=1.5
# Amethyst Cluster (Sound Type)
AMETHYST_CLUSTER=0.5
# Ancient Debris (Sound Type)
ANCIENT_DEBRIS=0.5
# Anvil (Sound Type)
ANVIL=0.5
# Azalea (Sound Type)
AZALEA=0.5
# Azalea Leaves (Sound Type)
AZALEA_LEAVES=0.5
# Bamboo (Sound Type)
BAMBOO=0.5
# Bamboo Sapling (Sound Type)
BAMBOO_SAPLING=0.5
# Bamboo Wood (Sound Type)
BAMBOO_WOOD=0.5
# Bamboo Wood Hanging Sign (Sound Type)
BAMBOO_WOOD_HANGING_SIGN=0.5
# Basalt (Sound Type)
BASALT=1.5
# Big Dripleaf (Sound Type)
BIG_DRIPLEAF=0.5
# Bone Block (Sound Type)
BONE_BLOCK=1.5
# Calcite (Sound Type)
CALCITE=1.5
# Candle (Sound Type)
CANDLE=0.5
# Cave Vines (Sound Type)
CAVE_VINES=0.5
# Chain (Sound Type)
CHAIN=0.5
# Cherry Leaves (Sound Type)
CHERRY_LEAVES=0.5
# Cherry Sapling (Sound Type)
CHERRY_SAPLING=0.5
# Cherry Wood (Sound Type)
CHERRY_WOOD=0.5
# Cherry Wood Hanging Sign (Sound Type)
CHERRY_WOOD_HANGING_SIGN=0.5
# Chiseled Bookshelf (Sound Type)
CHISELED_BOOKSHELF=0.5
# Copper (Sound Type)
COPPER=1.25
# Coral Block (Sound Type)
CORAL_BLOCK=0.2
# Crop (Sound Type)
CROP=0.5
# Decorated Pot (Sound Type)
DECORATED_POT=0.5
# Decorated Pot Cracked (Sound Type)
DECORATED_POT_CRACKED=0.5
# Deepslate (Sound Type)
DEEPSLATE=1.5
# Deepslate Bricks (Sound Type)
DEEPSLATE_BRICKS=1.5
# Deepslate Tiles (Sound Type)
DEEPSLATE_TILES=1.5
# Dripstone Block (Sound Type)
DRIPSTONE_BLOCK=0.5
# Flowering Azalea (Sound Type)
FLOWERING_AZALEA=0.5
# Froglight (Sound Type)
FROGLIGHT=0.5
# Frogspawn (Sound Type)
FROGSPAWN=0.5
# Fungus (Sound Type)
FUNGUS=0.5
# Gilded Blackstone (Sound Type)
GILDED_BLACKSTONE=0.5
# Glass (Sound Type)
GLASS=0.75
# Glow Lichen (Sound Type)
GLOW_LICHEN=0.5
# Grass (Sound Type)
GRASS=0.3
# Gravel (Sound Type)
GRAVEL=0.3
# Hanging Roots (Sound Type)
HANGING_ROOTS=0.5
# Hanging Sign (Sound Type)
HANGING_SIGN=0.5
# Hard Crop (Sound Type)
HARD_CROP=0.5
# Honey Block (Sound Type)
HONEY_BLOCK=0.1
# Ladder (Sound Type)
LADDER=0.5
# Lantern (Sound Type)
LANTERN=0.5
# Large Amethyst Bud (Sound Type)
LARGE_AMETHYST_BUD=0.5
# Lily Pad (Sound Type)
LILY_PAD=0.5
# Lodestone (Sound Type)
LODESTONE=0.5
# Mangrove Roots (Sound Type)
MANGROVE_ROOTS=0.5
# Medium Amethyst Bud (Sound Type)
MEDIUM_AMETHYST_BUD=0.5
# Metal (Sound Type)
METAL=1.25
# Moss (Sound Type)
MOSS=0.1
# Moss Carpet (Sound Type)
MOSS_CARPET=0.5
# Mud (Sound Type)
MUD=0.5
# Muddy Mangrove Roots (Sound Type)
MUDDY_MANGROVE_ROOTS=0.5
# Mud Bricks (Sound Type)
MUD_BRICKS=0.5
# Netherite Block (Sound Type)
NETHERITE_BLOCK=1.5
# Netherrack (Sound Type)
NETHERRACK=1.1
# Nether Bricks (Sound Type)
NETHER_BRICKS=1.5
# Nether Gold Ore (Sound Type)
NETHER_GOLD_ORE=1.1
# Nether Ore (Sound Type)
NETHER_ORE=1.1
# Nether Sprouts (Sound Type)
NETHER_SPROUTS=0.5
# Nether Wart (Sound Type)
NETHER_WART=0.5
# Nether Wood (Sound Type)
NETHER_WOOD=0.5
# Nether Wood Hanging Sign (Sound Type)
NETHER_WOOD_HANGING_SIGN=0.5
# Nylium (Sound Type)
NYLIUM=0.5
# Packed Mud (Sound Type)
PACKED_MUD=0.5
# Pointed Dripstone (Sound Type)
POINTED_DRIPSTONE=0.5
# Polished Deepslate (Sound Type)
POLISHED_DEEPSLATE=1.5
# Powder Snow (Sound Type)
POWDER_SNOW=0.5
# Rooted Dirt (Sound Type)
ROOTED_DIRT=0.5
# Roots (Sound Type)
ROOTS=0.5
# Sand (Sound Type)
SAND=0.2
# Scaffolding (Sound Type)
SCAFFOLDING=0.5
# Sculk (Sound Type)
SCULK=0.5
# Sculk Catalyst (Sound Type)
SCULK_CATALYST=0.5
# Sculk Sensor (Sound Type)
SCULK_SENSOR=0.5
# Sculk Shrieker (Sound Type)
SCULK_SHRIEKER=0.5
# Sculk Vein (Sound Type)
SCULK_VEIN=0.5
# Shroomlight (Sound Type)
SHROOMLIGHT=0.5
# Slime Block (Sound Type)
SLIME_BLOCK=0.5
# Small Amethyst Bud (Sound Type)
SMALL_AMETHYST_BUD=0.5
# Small Dripleaf (Sound Type)
SMALL_DRIPLEAF=0.5
# Snow (Sound Type)
SNOW=0.15
# Soul Sand (Sound Type)
SOUL_SAND=0.2
# Soul Soil (Sound Type)
SOUL_SOIL=0.2
# Spore Blossom (Sound Type)
SPORE_BLOSSOM=0.5
# Stem (Sound Type)
STEM=0.4
# Stone (Sound Type)
STONE=1.5
# Suspicious Gravel (Sound Type)
SUSPICIOUS_GRAVEL=0.5
# Suspicious Sand (Sound Type)
SUSPICIOUS_SAND=0.5
# Sweet Berry Bush (Sound Type)
SWEET_BERRY_BUSH=0.5
# Tuff (Sound Type)
TUFF=1.5
# Twisting Vines (Sound Type)
TWISTING_VINES=0.5
# Vine (Sound Type)
VINE=0.5
# Wart Block (Sound Type)
WART_BLOCK=0.5
# Weeping Vines (Sound Type)
WEEPING_VINES=0.5
# Wet Grass (Sound Type)
WET_GRASS=0.5
# Wood (Sound Type)
WOOD=0.4
# Wool (Sound Type)
WOOL=0.1
@@ -0,0 +1,99 @@
# Enables/Disables all sound effects
enabled=true
# Affects how quiet a sound gets based on distance
# Lower values mean distant sounds are louder
# This setting requires you to be in singleplayer or having the mod installed on the server
# 1.0 is the physically correct value
attenuation_factor=1.0
# The ray distance at which reverb starts
# 0.0 disables reverb attenuation
reverb_attenuation_distance=0.0
# The volume of simulated reverberations
reverb_gain=1.0
# The brightness of reverberation
# Higher values result in more high frequencies in reverberation
# Lower values give a more muffled sound to the reverb
reverb_brightness=1.0
# The distance of reverb relative to the sound distance
reverb_distance=1.5
# The amount of sound that will be absorbed when traveling through blocks
block_absorption=1.0
# Higher values mean smaller objects won't be considered as occluding
occlusion_variation=0.35
# The default amount of sound reflectance energy for all blocks
# Lower values result in more conservative reverb simulation with shorter reverb tails
# Higher values result in more generous reverb simulation with higher reverb tails
default_block_reflectivity=0.5
# The default amount of occlusion for all blocks
# Lower values will result in sounds being less muffled through walls
# Higher values mean sounds will be not audible though thicker walls
default_block_occlusion_factor=1.0
# Minecraft won't allow sounds to play past a certain distance
# This parameter is a multiplier for how far away a sound source is allowed to be in order for it to actually play
# This setting only takes affect in singleplayer worlds and when installed on the server
sound_distance_allowance=4.0
# A value controlling the amount that air absorbs high frequencies with distance
# A value of 1.0 is physically correct for air with normal humidity and temperature
# Higher values mean air will absorb more high frequencies with distance
# 0 disables this effect
air_absorption=1.0
# How much sound is filtered when the player is underwater
# 0.0 means no filter
# 1.0 means fully filtered
underwater_filter=0.9
# Whether sounds like cave, nether or underwater ambient sounds should have sound physics
evaluate_ambient_sounds=false
# The number of rays to trace to determine reverberation for each sound source
# More rays provides more consistent tracing results but takes more time to calculate
# Decrease this value if you experience lag spikes when sounds play
environment_evaluation_ray_count=32
# The number of rays bounces to trace to determine reverberation for each sound source
# More bounces provides more echo and sound ducting but takes more time to calculate
# Decrease this value if you experience lag spikes when sounds play
environment_evaluation_ray_bounces=4
# If sound hits a non-full-square side, block occlusion is multiplied by this
non_full_block_occlusion_factor=0.25
# The maximum amount of rays to determine occlusion
# Directly correlates to the amount of blocks between walls that are considered
max_occlusion_rays=16
# The amount at which occlusion is capped
max_occlusion=64.0
# If enabled, the occlusion calculation only uses one path between the sound source and the listener instead of 9
strict_occlusion=false
# Whether to try calculating where the sound should come from based on reflections
sound_direction_evaluation=true
# Skip redirecting non-occluded sounds (the ones you can see directly)
redirect_non_occluded_sounds=true
# If music discs or other longer sounds should be frequently reevaluated
update_moving_sounds=false
# The interval in ticks that moving sounds are reevaluated
# Lower values mean more frequent reevaluation but also more lag
# This option only takes effect if update_moving_sounds is enabled
sound_update_interval=5
# The maximum distance a sound can be processed
max_sound_processing_distance=512.0
# Disable level clone and cache. This will fall back to original main thread access.
# WARNING! Enabling this will cause instability and issues with other mods.
unsafe_level_access=false
# The radius of chunks to clone for level access
level_clone_range=4
# The maximum number of ticks to retain the cloned level in the cache
level_clone_max_retain_ticks=20
# The maximum distance a player can move from the cloned origin before invalidation
level_clone_max_retain_block_distance=16
# Enables debug logging
debug_logging=false
# Provides more information about occlusion in the logs
occlusion_logging=false
# Provides more information about the environment calculation in the logs
environment_logging=false
# Provides more information about how long computations take
performance_logging=false
# If enabled, the path of the sound will be rendered in game
render_sound_bounces=false
# If enabled, occlusion will be visualized in game
render_occlusion=false
# Enables/Disables sound effects for Simple Voice Chat audio
simple_voice_chat_integration=true
# Enables/Disables hearing your own echo with Simple Voice Chat
simple_voice_chat_hear_self=false
@@ -0,0 +1,182 @@
# Client-perf configs: generator, tiers, bench variants
**Status (`a01bc3b`):** the owner approved the mechanism below. Default Options is in the
pack, and the `T-default` files are shipped under `config/defaultoptions/` via
`build.py --ship`. **Every value is still unmeasured.** `../BENCH-MATRIX.md` decides them.
ModernFix `dynamic_resources` is held back until B6 passes.
```sh
python3 build.py # regenerates out/ (stdlib only, ~1 s)
python3 build.py --ship # + copies T-default into config/defaultoptions/
~/go/bin/packwiz refresh # from the repo root, after --ship
python3 apply_variant.py C1 <instance>/.minecraft # bench: apply a variant (merges options.txt)
```
**Iteration loop:** edit `DEFAULT` / `DECK` / `HIGH` in `build.py`, then `build.py --ship`,
then `packwiz refresh`, then commit. The shipped defaults are seed-once: they reach fresh
installs only. Players who already have the files follow `../PLAYER-TUNING.md` §1.
| Path | What |
| --- | --- |
| `build.py` | Single source of truth: tiers, bench variants, base files. **Fails if an override names a key that isn't already in the base file.** That enforces the "confirmed keys only" rule mechanically. |
| `templates/` | Hand-written bases for files without a same-version observed default (keys taken from the pinned jar or its source, see §3) |
| `out/T-default`, `T-deck`, `T-mid`, `T-high` | Player tiers. `T-mid` = `T-default` |
| `out/C0`, `C1`, `C1-*`, `dh-*`, `fw-*`, … | Phase 2 bench variants (see `../BENCH-MATRIX.md`) |
| `out/MANIFEST.md` | Generated list of every changed key per variant |
| `apply_variant.py` | Bench: apply `out/<ID>/` to an instance, merging `options.txt` |
---
## 1. Delivery mechanism (LEVERS M1): resolved
**Recommendation: add Default Options, and stage the tuned files under
`config/defaultoptions/`. The one exception is `modernfix-mixins.properties`, which ships as
a normal (enforced) packwiz file.** Evidence follows. All of it was read from the exact
versions in use: packwiz `ef87d96` (the commit `~/go/bin/packwiz` was built from, per
`go version -m`), packwiz-installer `v0.5.14` (the jar in the Prism instance reports
`Implementation-Version: v0.5.14`), and Default Options `21.1.8` (source tag
`1.21.1@94e2f17` plus the Modrinth jar).
### packwiz `preserve = true`
| Question | Answer | Evidence |
| --- | --- | --- |
| How is it set? | Only by hand-editing `index.toml`; no CLI flag | `Preserve` exists only in `core/indexfiles.go:29`; no `cmd/` reference |
| Does `packwiz refresh` keep it? | **Yes**, as long as the file stays on disk and not ignored. `updateFileEntry` mutates the loaded entry in place. | `core/indexfiles.go` `updateFileEntry`. **Tested**: kept across refresh, a content change + refresh, and an unrelated `packwiz update` |
| When is it lost? | Any refresh while the file is absent or ignored deletes the entry (`Refresh()` drops entries that are not `markedFound`). Re-adding it comes back **without** `preserve`. | `core/index.go` `Refresh()`. **Tested**: delete → refresh → restore → refresh = `preserve` gone |
| What does the installer do? | Writes the file only if the destination **does not exist**. It never updates it afterwards. | `DownloadTask.kt:222-227` |
| Side effects | (a) When the installer skips the file, the manifest entry gets no `cachedLocation`, so every launch logs "invalidated, marked for redownloading" and the installer can't take its "pack unchanged" fast path (it re-reads the index each launch). (b) If the installer *did* write the file, a later removal from the index **deletes the player's copy** (`UpdateManager.kt:250-262`). | source |
| Timing | Pre-launch (Prism `PreLaunchCommand`), so it beats everything, including mixin-time configs | `instance.cfg` |
| Sided? | **No.** Plain index files have no `side`. This repo *is* the server directory, so a file in `config/` also configures the server. | `indexFile` struct |
### Default Options 21.1.8 (BlayTheNinth)
| Question | Answer | Evidence |
| --- | --- | --- |
| Compatible? | NeoForge `[21-beta,)`, MC `[1.21,)`, Balm `[21.0.39,)`. The pack has Balm 21.0.66. | jar `neoforge.mods.toml` |
| `options.txt` | `config/defaultoptions/options.txt` is copied **only if** `options.txt` is absent. `key_*` lines are stripped (keybinds have their own handler). | `DefaultOptionsDefaultHandlers`, `SimpleDefaultOptionsFileHandler.shouldLoadDefaults` |
| Any config file | `config/defaultoptions/extra/<path>` is copied to `<gamedir>/<path>` **only if** the target is absent | `ExtraDefaultOptionsHandler.loadDefaults` |
| Timing | `EARLY_LOAD` runs at `GameConfig.<init>` RETURN, which is in `Main`, *before* `new Minecraft()`. NeoForge 1.21.1 constructs mods inside `Minecraft.<init>` (`ClientModLoader.begin`, Minecraft.java.patch), so the files land before any mod constructor creates its config. They land **after** mixin bootstrap. | `GameConfigMixin`; NeoForge `1.21.1` patch |
| Sided? | Client-only mod (`dist = CLIENT`), and the files sit in `config/defaultoptions/`, which no server-side code reads | — |
| Updating defaults later | Editing the shipped defaults never clobbers a player's live file. Removing them never deletes anything. | copy-if-absent |
### Why this split
1. **Server bleed rules out `preserve` for most files.** `DistantHorizons.toml` carries
`[server]` and `[common.multiThreading]` sections that the server-side DH also reads.
`ssrd-common.toml` is a NeoForge **COMMON** config: SSRD 1.8.7 registers it with
`ModConfig.Type.COMMON` and reads `physicsRenderDistance` server-side in
`ChunkMapTrackedEntityMixin` and `SubLevelTrackingSystemMixin`. Shipping a client value of
16 into the repo's `config/` would also change the server's tracking.
`config/defaultoptions/extra/` keeps them client-only.
2. **Both mechanisms are seed-once.** Neither reaches an instance that already has the file.
Existing players need a one-time reset (see `../PLAYER-TUNING.md` §1). That's a wash.
3. **ModernFix is the exception.** It reads `config/modernfix-mixins.properties` from its
mixin plugin (`ModernFixMixinPlugin`), which runs before `GameConfig`, so Default Options
is too late on first launch. Ship it **enforced** (a plain packwiz file, no `preserve`).
It's a pack-correctness flag, not a preference, and enforcing it keeps it revertible for
everyone if `dynamic_resources` misbehaves. The server reads it too, but
`dynamic_resources` only patches client resource loading.
4. Default Options also gives default keybinds and default resource packs (its journal is
used only for resource packs), with no index hand-editing and nothing to silently lose on
a refresh.
### Proposed repo layout (not applied)
```
config/defaultoptions/options.txt <- out/T-default/options.txt
config/defaultoptions/extra/config/<file> <- out/T-default/config/<file> (all except ModernFix)
config/modernfix-mixins.properties <- out/T-default/config/modernfix-mixins.properties
```
`.packwizignore`, appended at the bottom (tested with `packwiz refresh` in a scratch copy:
it indexes exactly these 11 files and still ignores runtime `config/*.json`):
```gitignore
# Client perf defaults: seeded by Default Options (copy-if-absent, client only)
!config/defaultoptions
!config/defaultoptions/**
# Read at mixin bootstrap, before Default Options runs: enforced by packwiz
!config/modernfix-mixins.properties
```
Applied in `a01bc3b`, minus the ModernFix lines (held for B6). packwiz wrote
`side = "client"` for Default Options by itself.
---
## 2. Tiers
`T-default` is the pack-wide default and equals `T-mid`. `T-deck` and `T-high` are deltas on
top of it. Every fleet machine is 4C/8T, so the CPU-side levers stay on in every tier.
"High" means a strong GPU, not a strong CPU.
| File | Key | Upstream | **Default / mid** | Deck | High |
| --- | --- | --- | --- | --- | --- |
| options.txt | renderDistance / simulationDistance | 12 / 12 | **8 / 8** | | |
| | biomeBlendRadius | 2 | **1** | 0 | 2 |
| | mipmapLevels | 4 | **2** | | 4 |
| | entityDistanceScaling | 1.0 | **0.75** | | 1.0 |
| | particles / entityShadows | 0 / true | | 1 / false | |
| DistantHorizons.toml | lodChunkRenderDistanceRadius | 256 | **128** | 64 | |
| | enableSsao | true | **false** | | true |
| | numberOfThreads | ceil(nproc/2) = **4** | **2** | | |
| | horizontal / vertical quality, transparency, vanillaFadeMode, lodBiomeBlending | MEDIUM / MEDIUM, COMPLETE, DOUBLE_PASS, 3 | | LOW / LOW, DISABLED, SINGLE_PASS, 1 (= DH's own LOW preset) | |
| sodium-options.json | quality.weather_quality / leaves_quality | DEFAULT | | FAST / FAST | |
| entity_model_features.json | animationLODDistance | 20 | **12** | 8 | |
| | modelUpdateFrequency | Average | **Slow** | | |
| | retainDetailOnLowFps | true | | false | |
| flywheel-client.toml | flw_backends.lightSmoothness | SMOOTH | **TRI_LINEAR** | FLAT | SMOOTH |
| lambdynlights.toml | mode | fancy | **fast** | fastest | |
| | light_sources.entities | true | | false | false |
| soundphysics.properties | environment_evaluation_ray_count / ray_bounces | 32 / 4 | **16 / 2** | | |
| | strict_occlusion | false | | true | true |
| sound_rates.properties | 19 noisy Create sounds | -1 (unlimited) | **4 per tick** | | |
| ssrd-common.toml | physicsRenderDistance | 128 | **16** | | 32 |
| modernfix-mixins.properties | mixin.perf.dynamic_resources | false | **true (gated on B6)** | | |
| sable-client.toml | — | — | unchanged | | |
Notes:
- **Sable**: the defaults are already right (`sub_level_skylight_shadows=false`). The file is
staged only so the bench variant `sable-noshade` (`sub_level_dynamic_shading=false`) has a
base. Don't ship it unless B4 shows a win.
- **SSRD**: 16 is the floor of SSRD's own slider (`minPhysicsRenderDistance = 16`). Players
can still raise it in Sodium's video settings.
- **High-tier DH radius stays 128** because P2 (the fleet's "high" GPU) has only ~8 GB of
free RAM. A player with ≥16 GB free and a 6+ core CPU can go to 192–256
(`PLAYER-TUNING.md`).
- **DH presets are GUI-only** (`qualityPresetSetting` and `threadPresetSetting` are
`ONLY_IN_GUI`), so the files set the raw keys. The values are taken from DH's own preset
tables (`RenderQualityPresetConfigEventHandler`, `ThreadPresetConfigEventHandler` @ 3.3.2).
- **Stripped**: DH `[server] serverId` is a per-install `new Random().nextInt()` ("DO NOT
CHANGE"). It's removed so each install generates its own. DH re-adds missing keys with
their defaults (`ConfigFileHandler.loadEntry` → `saveEntry`).
---
## 3. Key provenance
"Observed" means the file was generated on 2026-09-24 by the **same jar version that's pinned
now**. Versions were checked against that launch's `latest.log` and the instance's `mods/`:
DH 3.3.2, Sodium 0.8.13, EMF 3.3.9, Create 6.0.10 / Flywheel 1.0.6, Sable 2.0.5, SPR 1.5.1.
For DH, all 172 observed values were also diffed against `Config.java` @ 3.3.2. The only
differences are the two machine-computed ones (`numberOfThreads`, `serverId`), so the file
holds no owner edits.
| File | Base | Keys confirmed in |
| --- | --- | --- |
| DistantHorizons.toml | observed (3.3.2) | enum values from `EDhApi*` classes in the jar; defaults and presets from DH core `3.3.2` source |
| sodium-options.json | observed (0.8.13) | `SodiumOptions$LeavesQuality` / `$WeatherQuality` = DEFAULT/FANCY/FAST/FABULOUS (nested mod jar); Gson `LOWER_CASE_WITH_UNDERSCORES` |
| entity_model_features.json | observed (3.3.9) | `ETFConfig$UpdateFrequency` = Never/Slow/Average/Fast/Instant (ETF 7.2.4 jar) |
| flywheel-client.toml | observed (1.0.6) | allowed values are listed in the file's own comment |
| sable-client.toml | observed (2.0.5) | — |
| soundphysics.properties | observed (1.5.1) | — |
| sound_rates.properties | template, partial | `SoundRateConfig.load()` starts from all registered sounds at -1 and overlays file entries (SPR `1.21.1-1.5.1` source). Over-cap sounds play without physics (`SoundPhysics.java`). Create IDs come from the observed list. |
| lambdynlights.toml | template | `DynamicLightsConfig` @ `v4.8.11+1.21.1`. Strings match the nested NeoForge jar. Modes are lowercase `off/fastest/fast/fancy`; dotted keys become TOML tables; missing keys fall back via `getOrElse`. |
| ssrd-common.toml | template | `Config.java` @ SSRD HEAD = 1.8.7 (comments and translation keys match the jar). `ModConfig.Type.COMMON` from `ssrd.class`. |
| modernfix-mixins.properties | template | option name and default from the observed file's own option list (5.27.24) |
| options.txt | template, partial | key names from the instance's real 1.21.1 `options.txt` (`version:3955`). `version` is included so vanilla doesn't run the options DataFixer from 0. |
Not drafted (keys unverified this session, Tier 2): Twilight Forest
`cloudBlockPrecipitationDistance`, Alex's Caves `biome_ambient_light_coloring`, Jade,
Xaero's, WATUT, Perception, Subtle Effects, FWA.
@@ -0,0 +1,60 @@
#!/usr/bin/env python3
"""Apply a generated variant to a Minecraft instance (game closed!).
python3 apply_variant.py <ID> <path/to/.minecraft> # e.g. C1, dh-r64, T-deck
Copies every config file of out/<ID>/ over the instance. options.txt is
*merged* key by key, so GUI scale, resolution, keybinds etc. survive variant
switches. Prints what changed. Stdlib only; works on Windows too.
"""
import shutil
import sys
from pathlib import Path
HERE = Path(__file__).resolve().parent
def merge_options(src, dst):
new = dict(l.split(":", 1) for l in src.read_text().splitlines() if ":" in l)
lines = dst.read_text().splitlines() if dst.exists() else []
seen, out, changed = set(), [], []
for l in lines:
k = l.split(":", 1)[0]
if k in new:
seen.add(k)
if l != f"{k}:{new[k]}":
changed.append(f"{k}:{new[k]}")
l = f"{k}:{new[k]}"
out.append(l)
for k, v in new.items():
if k not in seen:
out.append(f"{k}:{v}")
changed.append(f"{k}:{v} (added)")
dst.write_text("\n".join(out) + "\n")
return changed
def main():
if len(sys.argv) != 3:
sys.exit(__doc__)
vid, game = sys.argv[1], Path(sys.argv[2])
src = HERE / "out" / vid
if not src.is_dir():
sys.exit(f"{src} missing - run build.py first")
if not (game / "mods").is_dir():
sys.exit(f"{game} doesn't look like a .minecraft dir (no mods/)")
for f in sorted(p for p in src.rglob("*") if p.is_file()):
rel = f.relative_to(src)
dst = game / rel
if rel.as_posix() == "options.txt":
for c in merge_options(f, dst):
print(f"options.txt {c}")
continue
dst.parent.mkdir(parents=True, exist_ok=True)
shutil.copyfile(f, dst)
print(f"copied {rel.as_posix()}")
(game / "BENCH_VARIANT.txt").write_text(vid + "\n")
print(f"applied {vid}; recorded in BENCH_VARIANT.txt")
main()
@@ -0,0 +1,355 @@
#!/usr/bin/env python3
"""Generate the staged client-perf configs (tiers + Phase 2 bench variants).
Every override is applied to a *base* file and must hit a key that already
exists in that base, otherwise the build fails. Bases are either the observed
defaults generated by the exact pinned jar (DH 3.3.2, Sodium 0.8.13, EMF 3.3.9,
Flywheel 1.0.6, Sable 2.0.5, SPR 1.5.1 - versions verified against the
2026-09-24 launch log) or hand-authored templates whose keys were read from the
pinned jar's source (see README.md, "Key provenance").
Output: out/<variant>/<path relative to the game dir>, plus out/MANIFEST.md.
python3 build.py # from this directory; stdlib only
python3 build.py --ship # also copy T-default into the repo (see SHIP), then
# run `packwiz refresh` from the repo root
"""
import json
import re
import shutil
import sys
from pathlib import Path
HERE = Path(__file__).resolve().parent
OBSERVED = HERE.parent / "data" / "observed-default-configs"
TEMPLATES = HERE / "templates"
OUT = HERE / "out"
REPO = HERE.parents[2]
# What `--ship` copies from out/T-default into the repo, and where.
# Default Options seeds these client-side only, and only if the file is absent
# (see README §1). Not shipped: sable-client.toml and sodium-options.json (no change
# from upstream). modernfix-mixins.properties is held back until bench block B6
# passes; when it does, add it here with destination "config/modernfix-mixins.properties"
# (enforced packwiz file, because ModernFix reads it before Default Options runs) and
# re-include it in .packwizignore.
DO = "config/defaultoptions"
SHIP = {
"options.txt": f"{DO}/options.txt",
"config/DistantHorizons.toml": f"{DO}/extra/config/DistantHorizons.toml",
"config/entity_model_features.json": f"{DO}/extra/config/entity_model_features.json",
"config/flywheel-client.toml": f"{DO}/extra/config/flywheel-client.toml",
"config/lambdynlights.toml": f"{DO}/extra/config/lambdynlights.toml",
"config/ssrd-common.toml": f"{DO}/extra/config/ssrd-common.toml",
"config/sound_physics_remastered/soundphysics.properties":
f"{DO}/extra/config/sound_physics_remastered/soundphysics.properties",
"config/sound_physics_remastered/sound_rates.properties":
f"{DO}/extra/config/sound_physics_remastered/sound_rates.properties",
}
# game-dir path -> (base file, format, may_append_new_keys)
FILES = {
"options.txt": (TEMPLATES / "options.txt", "options", False),
"config/DistantHorizons.toml": (OBSERVED / "DistantHorizons.toml", "toml", False),
"config/sodium-options.json": (OBSERVED / "sodium-options.json", "json", False),
"config/entity_model_features.json": (OBSERVED / "entity_model_features.json", "json", False),
"config/flywheel-client.toml": (OBSERVED / "flywheel-client.toml", "toml", False),
"config/sable-client.toml": (OBSERVED / "sable-client.toml", "toml", False),
"config/ssrd-common.toml": (TEMPLATES / "config/ssrd-common.toml", "toml", False),
"config/lambdynlights.toml": (TEMPLATES / "config/lambdynlights.toml", "toml", False),
"config/modernfix-mixins.properties": (TEMPLATES / "config/modernfix-mixins.properties", "properties", False),
"config/sound_physics_remastered/soundphysics.properties": (
OBSERVED / "sound_physics_remastered/soundphysics.properties", "properties", False),
# SPR merges partial files over its generated defaults (SoundRateConfig.load)
"config/sound_physics_remastered/sound_rates.properties": (
TEMPLATES / "config/sound_physics_remastered/sound_rates.properties", "properties", True),
}
DH_Q = "client.advanced.graphics.quality"
DH_G = "client.advanced.graphics"
DH_T = "common.multiThreading"
DH_W = "common.worldGenerator"
# Create 6.0.10 sound IDs (from the observed sound_rates.properties, same Create version).
# Over the cap, SPR plays the sound dry (SoundPhysics.java: setDefaultEnvironment), it is not dropped.
CREATE_NOISY = [
"create:cogs", "create:mixing", "create:mixing_compounded_1",
"create:mechanical_press_activation", "create:mechanical_press_activation_compounded_1",
"create:mechanical_press_activation_belt", "create:mechanical_press_activation_belt_compounded_1",
"create:crushing_1", "create:crushing_2", "create:crushing_3",
"create:saw_activate_stone", "create:saw_activate_wood",
"create:funnel_flap", "create:funnel_flap_compounded_1",
"create:depot_slide", "create:depot_plop", "create:steam",
"create:sanding_short", "create:sanding_long",
]
def sound_caps(n):
return {s.replace(":", "\\:"): n for s in CREATE_NOISY}
# Observed DH values were computed on a 4-thread box; DH's real default is
# ceil(nproc * 0.5) (ThreadPresetConfigEventHandler) = 4 on every 8-thread fleet
# machine. Applied first to every variant so C0 is the fleet's true default.
UPSTREAM = {"config/DistantHorizons.toml": {(DH_T, "numberOfThreads"): 4}}
# Per-install values that must not ship: DH regenerates missing keys with their
# default (ConfigFileHandler.loadEntry -> saveEntry); serverId is new Random().nextInt().
STRIP = {"config/DistantHorizons.toml": [("server", "serverId")]}
# --------------------------------------------------------------------------
# Tiers. "default" is the pack-wide conservative default; deck/high are deltas
# on top of it; mid == default.
# --------------------------------------------------------------------------
DEFAULT = {
"options.txt": {
"renderDistance": 8, # server view-distance is 8; more loads nothing in MP
"simulationDistance": 8, # singleplayer only
"biomeBlendRadius": 1, # 3x3; chunk-build cost (Sodium tooltip)
"mipmapLevels": 2, # block atlas is >=4096^2
"entityDistanceScaling": 0.75, # slider steps are 0.25
},
"config/DistantHorizons.toml": {
(DH_Q, "lodChunkRenderDistanceRadius"): 128, # default 256
(DH_G, "enableSsao"): False, # default true
(DH_T, "numberOfThreads"): 2, # default ceil(nproc*0.5) = 4 on the fleet
},
"config/entity_model_features.json": {
"animationLODDistance": 12, # default 20; lower = more frame skipping
"modelUpdateFrequency": "Slow", # default Average; model refresh, not animation
},
"config/flywheel-client.toml": {
("flw_backends", "lightSmoothness"): "TRI_LINEAR", # default SMOOTH
},
"config/ssrd-common.toml": {
("", "physicsRenderDistance"): 16, # default 128; slider floor is minPhysicsRenderDistance=16
},
"config/lambdynlights.toml": {
("", "mode"): "fast", # default fancy; fast = TickMode.SLOW
},
"config/modernfix-mixins.properties": {
"mixin.perf.dynamic_resources": "true", # GATED on bench block B6
},
"config/sound_physics_remastered/soundphysics.properties": {
"environment_evaluation_ray_count": 16, # default 32
"environment_evaluation_ray_bounces": 2, # default 4
},
"config/sound_physics_remastered/sound_rates.properties": sound_caps(4),
}
DECK = {
"options.txt": {"biomeBlendRadius": 0, "particles": 1, "entityShadows": "false"},
# = DH's own LOW quality preset (RenderQualityPresetConfigEventHandler) + radius 64
"config/DistantHorizons.toml": {
(DH_Q, "lodChunkRenderDistanceRadius"): 64,
(DH_Q, "horizontalQuality"): "LOW",
(DH_Q, "verticalQuality"): "LOW",
(DH_Q, "transparency"): "DISABLED",
(DH_Q, "vanillaFadeMode"): "SINGLE_PASS",
(DH_Q, "lodBiomeBlending"): 1,
},
"config/sodium-options.json": {
"quality.weather_quality": "FAST",
"quality.leaves_quality": "FAST",
},
"config/entity_model_features.json": {
"animationLODDistance": 8,
"retainDetailOnLowFps": False,
},
"config/flywheel-client.toml": {("flw_backends", "lightSmoothness"): "FLAT"},
"config/lambdynlights.toml": {
("", "mode"): "fastest",
("light_sources", "entities"): False,
},
"config/sound_physics_remastered/soundphysics.properties": {"strict_occlusion": "true"},
}
HIGH = { # strong GPU, but still a 4C/8T CPU (true of every fleet machine)
"options.txt": {"mipmapLevels": 4, "biomeBlendRadius": 2, "entityDistanceScaling": 1.0},
"config/DistantHorizons.toml": {(DH_G, "enableSsao"): True},
"config/flywheel-client.toml": {("flw_backends", "lightSmoothness"): "SMOOTH"},
"config/ssrd-common.toml": {("", "physicsRenderDistance"): 32},
"config/lambdynlights.toml": {("light_sources", "entities"): False},
"config/sound_physics_remastered/soundphysics.properties": {"strict_occlusion": "true"},
}
# --------------------------------------------------------------------------
# Phase 2 bench variants. C0 = every file at upstream default (explicit, so stale
# instance edits can't leak in). Everything else is C1 (= T-mid) plus ONE change.
# BENCH is layered on every bench variant: uncapped, vsync off, phase0 pins,
# no DH world generation during runs (world is pre-generated).
# --------------------------------------------------------------------------
BENCH = {
"options.txt": {"maxFps": 260, "enableVsync": "false", "renderDistance": 8,
"simulationDistance": 8},
"config/DistantHorizons.toml": {(DH_W, "generatorPlan"): "DISABLED"},
}
def c1(extra):
return [DEFAULT, extra]
VARIANTS = {
# player tiers (no BENCH layer)
"T-default": ([DEFAULT], False),
"T-deck": ([DEFAULT, DECK], False),
"T-mid": ([DEFAULT], False),
"T-high": ([DEFAULT, HIGH], False),
# bench: composites
"C0": ([], True),
"C1": ([DEFAULT], True),
"C1-deck": ([DEFAULT, DECK], True),
"C1-high": ([DEFAULT, HIGH], True),
# bench: Distant Horizons
"dh-r256": (c1({"config/DistantHorizons.toml": {(DH_Q, "lodChunkRenderDistanceRadius"): 256}}), True),
"dh-r192": (c1({"config/DistantHorizons.toml": {(DH_Q, "lodChunkRenderDistanceRadius"): 192}}), True),
"dh-r64": (c1({"config/DistantHorizons.toml": {(DH_Q, "lodChunkRenderDistanceRadius"): 64}}), True),
"dh-ssao": (c1({"config/DistantHorizons.toml": {(DH_G, "enableSsao"): True}}), True),
"dh-thr4": (c1({"config/DistantHorizons.toml": {(DH_T, "numberOfThreads"): 4}}), True),
"dh-qlow": (c1({"config/DistantHorizons.toml": DECK["config/DistantHorizons.toml"]
| {(DH_Q, "lodChunkRenderDistanceRadius"): 128}}), True),
# bench: Flywheel / ModernFix / Sable / SSRD
"fw-flat": (c1({"config/flywheel-client.toml": {("flw_backends", "lightSmoothness"): "FLAT"}}), True),
"fw-smooth": (c1({"config/flywheel-client.toml": {("flw_backends", "lightSmoothness"): "SMOOTH"}}), True),
"mf-dynres-off": (c1({"config/modernfix-mixins.properties": {"mixin.perf.dynamic_resources": "false"}}), True),
"ssrd-32": (c1({"config/ssrd-common.toml": {("", "physicsRenderDistance"): 32}}), True),
"ssrd-64": (c1({"config/ssrd-common.toml": {("", "physicsRenderDistance"): 64}}), True),
"ssrd-128": (c1({"config/ssrd-common.toml": {("", "physicsRenderDistance"): 128}}), True),
"sable-noshade": (c1({"config/sable-client.toml": {("", "sub_level_dynamic_shading"): False}}), True),
# bench: entities / lights / sound
"emf-lod20": (c1({"config/entity_model_features.json": {"animationLODDistance": 20,
"modelUpdateFrequency": "Average"}}), True),
"emf-lod8": (c1({"config/entity_model_features.json": {"animationLODDistance": 8,
"retainDetailOnLowFps": False}}), True),
"ldl-fancy": (c1({"config/lambdynlights.toml": {("", "mode"): "fancy"}}), True),
"ldl-fastest": (c1({"config/lambdynlights.toml": {("", "mode"): "fastest",
("light_sources", "entities"): False}}), True),
"spr-up": (c1({"config/sound_physics_remastered/soundphysics.properties": {
"environment_evaluation_ray_count": 32, "environment_evaluation_ray_bounces": 4}}), True),
"spr-strict": (c1({"config/sound_physics_remastered/soundphysics.properties": {"strict_occlusion": "true"}}), True),
"spr-nocap": (c1({"config/sound_physics_remastered/sound_rates.properties": sound_caps(-1)}), True),
# bench: vanilla / Sodium
"sod-thr3": (c1({"config/sodium-options.json": {"performance.chunk_builder_threads": 3}}), True),
"opt-blend2": (c1({"options.txt": {"biomeBlendRadius": 2}}), True),
"opt-blend0": (c1({"options.txt": {"biomeBlendRadius": 0}}), True),
"opt-mip4": (c1({"options.txt": {"mipmapLevels": 4}}), True),
}
# --------------------------------------------------------------------------
def toml_val(v):
if isinstance(v, bool):
return "true" if v else "false"
if isinstance(v, (int, float)):
return str(v)
return json.dumps(v)
def apply_toml(text, ov, name):
lines = text.split("\n")
hdr = re.compile(r"^\s*\[([^\]]+)\]\s*$")
for (section, key), val in ov.items():
cur, hits = "", []
for i, ln in enumerate(lines):
m = hdr.match(ln)
if m:
cur = m.group(1).strip()
continue
if cur == section and re.match(rf"^\s*{re.escape(key)}\s*=", ln):
hits.append(i)
if len(hits) != 1:
raise SystemExit(f"{name}: [{section}] {key}: {len(hits)} matches in base")
i = hits[0]
indent = re.match(r"^(\s*)", lines[i]).group(1)
lines[i] = f"{indent}{key} = {toml_val(val)}"
return "\n".join(lines)
def apply_json(text, ov, name):
data = json.loads(text)
for path, val in ov.items():
node, parts = data, path.split(".")
for p in parts[:-1]:
node = node[p]
if parts[-1] not in node:
raise SystemExit(f"{name}: {path} not in base")
if type(node[parts[-1]]) is not type(val):
raise SystemExit(f"{name}: {path} type {type(node[parts[-1]]).__name__} != {type(val).__name__}")
node[parts[-1]] = val
return json.dumps(data, indent=2) + "\n"
def apply_kv(text, ov, name, sep, append):
lines = text.rstrip("\n").split("\n")
for key, val in ov.items():
sval = toml_val(val) if isinstance(val, bool) else str(val)
rx = re.compile(rf"^{re.escape(key)}{re.escape(sep)}")
hits = [i for i, ln in enumerate(lines) if rx.match(ln)]
if len(hits) > 1 or (not hits and not append):
raise SystemExit(f"{name}: {key}: {len(hits)} matches in base")
if hits:
lines[hits[0]] = f"{key}{sep}{sval}"
else:
lines.append(f"{key}{sep}{sval}")
return "\n".join(lines) + "\n"
def strip(text, keys, name):
lines = text.split("\n")
hdr = re.compile(r"^\s*\[([^\]]+)\]\s*$")
for section, key in keys:
cur, hits = "", []
for i, ln in enumerate(lines):
m = hdr.match(ln)
if m:
cur = m.group(1).strip()
elif cur == section and re.match(rf"^\s*{re.escape(key)}\s*=", ln):
hits.append(i)
if len(hits) != 1:
raise SystemExit(f"{name}: strip [{section}] {key}: {len(hits)} matches")
del lines[hits[0]]
return "\n".join(lines)
def render(layers, name):
base, fmt, append = FILES[name]
text = strip(base.read_text(), STRIP.get(name, []), name)
merged = {}
for layer in [UPSTREAM] + layers:
merged.update(layer.get(name, {}))
if fmt == "toml":
return apply_toml(text, merged, name), merged
if fmt == "json":
return apply_json(text, merged, name), merged
sep = ":" if fmt == "options" else "="
return apply_kv(text, merged, name, sep, append), merged
def main():
if OUT.exists():
shutil.rmtree(OUT)
manifest = ["# Generated by build.py - do not edit\n",
"Changed keys per variant, relative to the upstream default of each file.\n"]
for vid, (layers, bench) in VARIANTS.items():
layers = list(layers) + ([BENCH] if bench else [])
manifest.append(f"\n## {vid}\n")
for name in FILES:
text, merged = render(layers, name)
dest = OUT / vid / name
dest.parent.mkdir(parents=True, exist_ok=True)
dest.write_text(text)
for k, v in merged.items():
k = f"[{k[0]}] {k[1]}" if isinstance(k, tuple) else k
manifest.append(f"- `{name}`: `{k}` = `{v}`\n")
(OUT / "MANIFEST.md").write_text("".join(manifest))
print(f"wrote {len(VARIANTS)} variants x {len(FILES)} files to {OUT}")
if "--ship" in sys.argv:
shutil.rmtree(REPO / DO, ignore_errors=True)
for src, dst in SHIP.items():
(REPO / dst).parent.mkdir(parents=True, exist_ok=True)
shutil.copyfile(OUT / "T-default" / src, REPO / dst)
print(f"shipped {len(SHIP)} files to {REPO / DO}; now run `packwiz refresh`")
main()
@@ -0,0 +1,16 @@
mode = "fancy"
chunk_rebuild_scheduler = "culling"
[adaptive_ticking]
slow = 5
slower = 8
background_sleep = true
[light_sources]
entities = true
self = true
water_sensitive_check = true
beam = true
guardian_laser = true
sonic_boom = true
glowing_effect = true
@@ -0,0 +1 @@
mixin.perf.dynamic_resources=false
@@ -0,0 +1 @@
# Partial file: SPR merges these over its generated per-sound defaults (-1 = unlimited).
@@ -0,0 +1,12 @@
#The render distance for physics objects (SubLevels) in chunks.
# Default: 128
# Range: 1 ~ 1000000
physicsRenderDistance = 128
#The minimum render distance for physics objects (SubLevels) in chunks.
# Default: 16
# Range: 1 ~ 1000000
minPhysicsRenderDistance = 16
#The maximum render distance for physics objects (SubLevels) in chunks.
# Default: 4096
# Range: 1 ~ 1000000
maxPhysicsRenderDistance = 4096
@@ -0,0 +1,11 @@
version:3955
renderDistance:12
simulationDistance:12
biomeBlendRadius:2
mipmapLevels:4
entityDistanceScaling:1.0
particles:0
entityShadows:true
graphicsMode:1
maxFps:120
enableVsync:true
@@ -0,0 +1,278 @@
# Research: Core render stack levers
Scope: client-side render stack for **Onion MC** — Minecraft 1.21.1 / NeoForge 21.1.251, ~212 mods, ~10 players, server view-distance 8.
Fleet: **P0** Steam Deck (Zen2 8CU RDNA2 iGPU, 16 GB shared, 1280×800, Mesa/RADV) · **P1** i7-7700K + GTX 1060 **6 GB** (VRAM-bound) · **P2** i7-6700K + GTX 1080 Ti 11 GB with only ~8 GB free RAM (CPU/heap-bound).
Confidence tags used throughout:
- **VERIFIED** — read in the pinned mod's source/config code or its own shipped defaults.
- **DOCUMENTED** — official README / wiki / changelog / mod-author description.
- **COMMUNITY** — forum/Reddit/third-party guide.
- **INFERENCE** — my reasoning, no direct source.
Every non-inference claim has a URL. Config keys are only named where I actually saw them in source or a shipped properties file.
---
## Summary
The single biggest client-side render lever in this pack is **Iris/shaders, not Sodium**: with any shaderpack active, Flywheel 1.0.x marks *both* of its GPU backends unsupported, so Create 6 falls back to per-block-entity rendering and shader packs re-draw that content in the shadow pass; on top of that, DH's shadow pass is auto-enabled and renders *all* DH chunks. Sodium itself is already near-optimal on 0.8.13 (most impactful options — animate-only-visible-textures, fog occlusion, entity culling, block face culling, no-error GL context — are **on by default** and built into Sodium, no add-on needed). The remaining cheap wins are shader-quality knobs (shadow distance/quality, SSAO, entity shadows, volumetric clouds), ModernFix `mixin.perf.dynamic_resources=true` for the RAM-starved P2, mipmap-level reduction for the VRAM-bound P1, and Fresh-Animations/EMF animation throttles.
---
## Findings
### 1. Sodium 0.8.13 (`mc1.21.1-0.8.13`) — options that matter, and their real defaults
Sodium 0.8.13 is the 1.21.1 backport of Sodium 0.8; its changelog (0.8.12, the base of this line) claims **"up to +115%"** world-render improvement, reduced chunk-update micro-stutter, and slightly faster entity/particle rendering, and states compatibility "Create Aeronautics works as of 1.3.0, Sable as of 2.0.0, Veil works as of 4.1.2, Iris works as of 1.8.13". [Modrinth version page](https://modrinth.com/mod/sodium/version/mc1.21.1-0.8.12-neoforge) — **DOCUMENTED**. (Confirms the pack's version matrix is a supported one; it also warns Sodium is *not* compatible with "Sodium Options API".)
**Option exposure: everything in the task list is in Sodium's own Video Settings — no add-on required.** The 0.8.13 1.21.1 config builder registers pages *General / Quality / Performance / Advanced* and puts chunk-update threads, chunk-updates (defer), block face culling, fog occlusion, entity culling, animate-only-visible-textures, block-transparency-limits and **Use No Error Context on the Performance page**, and persistent-mapping + CPU render-ahead limit on Advanced. [SodiumConfigBuilder @ mc1.21.1-0.8.13](https://github.com/CaffeineMC/sodium/blob/mc1.21.1-0.8.13/common/src/main/java/net/caffeinemc/mods/sodium/client/gui/SodiumConfigBuilder.java) — **VERIFIED**.
Defaults are hard-coded in `PerformanceSettings` / `QualitySettings` / `AdvancedSettings` in [SodiumOptions.java @ mc1.21.1-0.8.13](https://github.com/CaffeineMC/sodium/blob/mc1.21.1-0.8.13/common/src/main/java/net/caffeinemc/mods/sodium/client/gui/SodiumOptions.java) — **VERIFIED**:
| Video option (label) | Config field | Default | Impact as labelled | Notes |
|---|---|---|---|---|
| Chunk Update Threads | `performance.chunkBuilderThreads` | `0` = auto | High | `0` resolves to `clamp(max(cores/3, cores−6), 1, 10)` → **2 threads on every 4C/8T fleet machine** ([ChunkBuilder.java](https://github.com/CaffeineMC/sodium/blob/mc1.21.1-0.8.13/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/compile/executor/ChunkBuilder.java)). Range is 0..`availableProcessors`. Requires renderer reload. |
| Chunk Updates | `performance.chunkBuildDeferMode` | `DeferMode.ALWAYS` ("Deferred") | High | Other values `ONE_FRAME` ("Soon"), `ZERO_FRAMES` ("Immediate"). Already at the frame-rate-friendliest setting. |
| Animate Only Visible Textures | `performance.animateOnlyVisibleTextures` | **`true`** | High | Already on. Tooltip: "significant performance improvement on some hardware, especially with heavier resource packs". |
| Use Fog Occlusion | `performance.useFogOcclusion` | **`true`** | Medium | Already on. |
| Use Entity Culling | `performance.useEntityCulling` | **`true`** | Medium | Already on. |
| Use Block Face Culling | `performance.useBlockFaceCulling` | **`true`** | Medium | Already on. |
| Block Transparency Limits (quad splitting) | `performance.quadSplittingMode` | `SAFE` | Medium | `BASIC`/`SAFE`/`UNLIMITED`. Controller: translucency sorting. |
| Use No Error Context | `performance.useNoErrorGLContext` | **`true`** | Low | Only enable-able on GL 4.6 / `GL_KHR_no_error`; needs game restart. Effect is "slight". |
| CPU Render-Ahead Limit | `advanced.cpuRenderAheadLimit` | `3` | (debug) | Docs say don't touch. |
| Use Persistent Mapping | `advanced.useAdvancedStagingBuffers` | `true` | (debug) | — |
| Entity Sorting | `quality.useClosestPointEntitySort` | `false` | Medium | "Enhanced" costs a little CPU. |
| Leaves | `quality.leavesQuality` (vanilla `cutoutLeaves`) | default/fancy | Medium | Fast = opaque, cullable leaves. |
| Mipmap Levels / Biome Blend / Entity Distance / Entity Shadows | vanilla-backed (`mipmapLevels`, `biomeBlendRadius`, `entityDistanceScaling`, `entityShadows`) | vanilla defaults | Med / Low / High / Medium | See §6. |
Two options people commonly cite (**"Hidden Fluid Culling"**, **"Improved Fluid Shaping"**, **"Texel Interpolation / pixel filtering mode"**) are **not present in the 0.8.13 / 1.21.1 build** — their lang keys exist only on Sodium `dev` (26.x). Do not look for them in-game on this version. [compare dev lang](https://github.com/CaffeineMC/sodium/blob/dev/common/src/main/resources/assets/sodium/lang/en_us.json) vs [0.8.13 lang](https://github.com/CaffeineMC/sodium/blob/mc1.21.1-0.8.13/common/src/main/resources/assets/sodium/lang/en_us.json) — **VERIFIED**.
**Add-on boundary (what is *not* in Sodium):** fog-type toggles, cloud height, particle-culling detail, and "turn off all fog" are Sodium-Extra/Extras features. [Sodium Extras description](https://modrinth.com/mod/sodium-extras) — **DOCUMENTED**. None of the options in this task's list requires an add-on; Sodium Extra is optional polish. (Neither Sodium Extra nor Sodium Extras is in the current pack.)
### 2. Iris 1.8.14-beta.1 + the two shaderpacks
**Complementary Reimagined r5.9.3** exposes a *Profile* selector plus per-setting overrides. The profile table and default shadow distances are shipped in the pack's `shaders.properties` (read from the author's repo, `main` branch — treat the exact numbers as "current author defaults, verify in-game for r5.9.3"): [ComplementaryReimagined/shaders/shaders.properties](https://github.com/ComplementaryDevelopment/ComplementaryReimagined/blob/main/shaders/shaders.properties) — **DOCUMENTED**.
| Profile | `SHADOW_QUALITY` | `shadowDistance` | `ENTITY_SHADOW` | `CLOUD_QUALITY` | `RAIN_PUDDLES` |
|---|---|---|---|---|---|
| POTATO | −1 (OFF) | 64 (4 ch) | −1 (off) | 1 | 0 |
| VERYLOW / LOW | 0 (Very Low) | 64 / 96 | off | 1 / 2 | 0 |
| MEDIUM | 2 (Medium) | 128 (8 ch) | off | 2 | 0 |
| HIGH | 2 (Medium) | **192 (12 ch)** | 1 | 2 | 0 |
| VERYHIGH | 2 | 192 | 1 | 2 | 2 |
| ULTRA | 2 | 224 (14 ch) | 1 | 3 | 2 |
| COMPLEMENTARY | 3 (High) | 256 (16 ch) | 1 | 3 | 2 |
Author-supplied cost notes in the pack's own lang file (same source) — **DOCUMENTED**:
- **Entity Shadows** — "impact performance way more than expected, costing **more than 50% of your fps in some cases**". This is the single biggest per-setting cost in the pack, and it is **off in every profile except HIGH and above**.
- **Colored Lighting** (ACT/voxel floodfill) — "each increment can cost more than twice the performance of the previous tier".
- **Shadow Distance** — "lower values may cause excessive Light Shaft leaking" (visual, not perf).
- Informational panel tells users to keep render distance "2–4 chunks higher than the shadow distance of the Profile", e.g. 16 chunks RD for the HIGH profile — i.e. **at server view-distance 8, no profile above MEDIUM's 8-chunk shadow distance is even productive**.
- Rain Puddles and volumetric clouds are called out as expensive.
**MakeUp Ultra Fast 9.5f** — its settings screen and defaults are in the pack's `shaders.properties` (open source): [MakeUpUltraFast/shaders/shaders.properties](https://github.com/javiergcim/MakeUpUltraFast/blob/master/shaders/shaders.properties) — **VERIFIED**. Relevant keys and shipped profile values:
| Key | Meaning | Values seen in shipped profiles |
|---|---|---|
| `SHADOW_CASTING` | shadows on/off | `no_effects` / `shadowless_*` profiles have `!SHADOW_CASTING` (off); `low`…`extremeplus` have it on |
| `SHADOW_DISTANCE_SLIDER` | slider | 0 (low), 1 (medium), 2 (high/extremeplus) |
| `SHADOW_QTY_SLIDER` | slider | 1 / 2 / 3 |
| `REFLECTION_SLIDER` | water/block reflections | 0–2 |
| `AO` / `AOSTEPS` / `AO_STRENGTH` | AO on/off and steps | `AOSTEPS=5` |
| `V_CLOUDS` / `CLOUD_STEPS_AVG` / `CLOUD_VOL_STYLE` | volumetric clouds | `CLOUD_STEPS_AVG=10` |
| `BLOOM` / `BLOOM_SAMPLES`, `VOL_LIGHT`, `CHROMA_ABER`, `AA_TYPE`, `DOF` | post | author notes Bloom/DoF/Shadows are the heaviest effects |
| `dhShadow.enabled` | DH shadow pass | **`false`** in MakeUp |
MakeUp also sets `shadow.culling = true`. Its README positions it as "Intended for low-spec computers … FAST!", with optional shadows/volumetric clouds/DoF/bloom. [MakeUp README](https://github.com/javiergcim/MakeUpUltraFast) — **DOCUMENTED**.
**Comparative cost:** I could not find any *primary/measured* head-to-head between these exact two packs; every comparison I found is COMMUNITY (e.g. "MakeUp Ultra Fast … gives better fps tho", [r/minecraftshaders](https://www.reddit.com/r/minecraftshaders/comments/1m9c569/any_nice_looking_shaders_that_run_well/)). The defensible statement is structural and comes from the profiles themselves: MakeUp's *default* profile ships with lower shadow distance/quality and `dhShadow` off, whereas Complementary's HIGH-and-above profiles enable entity shadows and 12–16-chunk shadows. That gap — especially **Entity Shadows** — is where the FPS difference will come from, not from the shader's core lighting. **INFERENCE**, with **DOCUMENTED** inputs.
**Iris-level knobs:** Iris exposes each pack's own options, and the shader-lab community wiki describes the shader-selection-menu sliders ("Render Quality", "Shadow Quality", "Antialiasing") as OptiFine-only and says baseline render distance with shaders should stay under 16 chunks. [shaderLABS Performance Tips](https://shaderlabs.org/wiki/Performance_Tips) — **COMMUNITY/DOCUMENTED** (page is old; treat as directional only).
### 3. Iris + Distant Horizons (mandatory in this pack)
- DH + Iris requires **Iris 1.7+** and a shaderpack *built for DH*; DH itself says most shaderpacks will not work. [DH Modrinth description](https://modrinth.com/mod/distanthorizons) — **DOCUMENTED**.
- Iris adds `dh_terrain`, `dh_water`, `dh_shadow` programs, and **"The DH shadow pass is automatically enabled … Distant Horizons has no set shadow distance; all chunks will be rendered."** [Iris docs — Distant Horizons](https://shaders.properties/current/reference/mod-support/distant_horizons/) — **DOCUMENTED**. This is the key interaction cost: *your shadow pass covers DH's full LOD distance*, not the vanilla 8 chunks.
- DH's own config (read from the current core repo) — **VERIFIED** keys/defaults: `Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius` (min/default/max **32/256/4096** chunks), `maxHorizontalResolution` default **BLOCK** (256 LODs per chunk — the "Fanciest"), `horizontalQuality` default **MEDIUM**, `verticalQuality` default **MEDIUM**, `Client.Advanced.Culling.overdrawPrevention` default **−1 (auto)**, `reduceOverdrawWithFastMovement` default **true**, `enableCaveCulling` default **true**, `Client.Advanced.Graphics.Quality.transparency` default **COMPLETE**, `vanillaFadeMode` default **DOUBLE_PASS** ("Slowest … provides the smoothest transition"), `lodBiomeBlending` default **3** ("anything above 0 will slow down LOD loading time"), `enableNoiseTexture` default **true**, far-fog `farFogStart` 0.4 / `farFogEnd` 1.0. Quality preset default `MEDIUM`, thread preset default `BALANCED`. [distant-horizons-core Config.java](https://gitlab.com/distant-horizons-team/distant-horizons-core/-/blob/main/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java) — **VERIFIED**.
- DH's official stutter advice, in order: use a concurrent GC (ZGC/Shenandoah), allocate 6–8 GB for 4–8 core CPUs, **lower DH's CPU load**, disable **Distant Generation**, lower the **Graphics Preset**. [DH Modrinth description](https://modrinth.com/mod/distanthorizons) — **DOCUMENTED**. Note "6–8 GB allocated" directly conflicts with P2's ~8 GB free RAM — that machine will be the DH-stutter canary.
### 4. Create 6.0.10 / Flywheel 1.0.6 with Iris shaders — **it falls back**
This is the most important verified finding. In Flywheel's 1.21.1 branch, both GPU backends declare themselves unsupported whenever a shaderpack is in use:
```java
INSTANCING: .supported(() -> GlCompat.SUPPORTS_INSTANCING && InstancingPrograms.allLoaded()
&& !ShadersModHelper.isShaderPackInUse())
INDIRECT : .supported(() -> GlCompat.SUPPORTS_INDIRECT && IndirectPrograms.allLoaded()
&& !ShadersModHelper.isShaderPackInUse())
```
[Flywheel Backends.java @ 1.21.1/dev](https://github.com/Engine-Room/Flywheel/blob/1.21.1/dev/common/src/backend/java/dev/engine_room/flywheel/backend/Backends.java) — **VERIFIED**; the same guard is present at the 1.21.1 `mc1.21.1/v1.0.5` tag, the nearest tagged release below the pack's 1.0.6 — **VERIFIED**. `ShadersModHelper` is Iris-backed (`IrisApi.isShaderPackInUse()`), and this is exactly what `IrisCompat` exists for. [IrisCompat.java](https://github.com/Engine-Room/Flywheel/blob/1.21.1/dev/common/src/main/java/dev/engine_room/flywheel/impl/compat/IrisCompat.java) — **VERIFIED**.
When neither backend is supported, `BackendManagerImpl.chooseBackend()` falls through to `OFF_BACKEND` (`flywheel:off`) and logs `"Flywheel backend fell back from '…' to 'flywheel:off'"`. [BackendManagerImpl.java](https://github.com/Engine-Room/Flywheel/blob/1.21.1/dev/common/src/main/java/dev/engine_room/flywheel/impl/BackendManagerImpl.java) — **VERIFIED**.
What that costs, confirmed in Create's own code: Create 6's kinetic/contraption renderers are *guarded* by Flywheel visualization availability —
```java
protected void renderSafe(T be, …) {
if (VisualizationManager.supportsVisualization(be.getLevel())) return;
…
}
```
[Create KineticBlockEntityRenderer @ mc1.21.1/dev](https://github.com/Creators-of-Create/Create/blob/mc1.21.1/dev/src/main/java/com/simibubi/create/content/kinetics/base/KineticBlockEntityRenderer.java) — **VERIFIED** (same pattern in `ShaftRenderer`). So with shaders on, every shaft/cog/gear/belt is drawn as an ordinary per-block-entity draw instead of one instanced/indirect batch. Iris's own tracking issue states this in plain terms: *"Create through Flywheel uses a highly-optimized rendering engine normally, but when Iris is enabled it has to use a slower fallback … Iris also has to render the Create content **twice** using this slower fallback path with most shader packs to implement dynamic shadows."* [Iris issue #1414](https://github.com/IrisShaders/Iris/issues/1414) — **DOCUMENTED** (coderbot16, Iris maintainer), corroborated by the Ocuwheel description: *"When Iris/Oculus shaderpacks are active, Flywheel can disable its GPU-instancing backend and fall back to CPU rendering."* [Ocuwheel](https://www.curseforge.com/minecraft/mc-mods/ocuwheel-oculus-flywheel-compat) — **DOCUMENTED**.
Mitigations exist but are third-party and not listed in the pack: **Iris Flywheel Compat** (`irisflw` / backend id seen in the wild as `irisflw:iris_instancing`) and the newer **Colorwheel**, which claims better compatibility. Iris maintainers explicitly warn the former "isn't supported by either team, and almost always breaks on a Create or Iris update. It's a brute force method". [Iris issue #1414 comments](https://github.com/IrisShaders/Iris/issues/1414) — **DOCUMENTED**. Also note Sodium's own 0.8.12 changelog lists **"Create Aeronautics works as of 1.3.0"** and **"Sable works as of 2.0.0"** — so the aeronautics half of Create *is* version-aligned, it is specifically the Flywheel backend gate that costs you. **INFERENCE** from DOCUMENTED inputs.
**Flywheel config levers (NeoForge client config, `flw_backends` section)** — [NeoForgeFlwConfig.java](https://github.com/Engine-Room/Flywheel/blob/1.21.1/dev/neoforge/src/main/java/dev/engine_room/flywheel/impl/NeoForgeFlwConfig.java) — **VERIFIED**:
- `backend` (default `"DEFAULT"`) — you can force `flywheel:instancing` / `flywheel:indirect` / `flywheel:off`, but the `supported()` test still gates it.
- `limitUpdates` (default `true`) — instance-update limiting by distance.
- `workerThreads` (default `-1` = auto, `0` = no parallelism, needs restart).
- `flw_backends.lightSmoothness` (default `SMOOTH`) — "May have a large performance impact"; **irrelevant while a shaderpack is active**, because the Flywheel engine is off.
There is also a `/flywheel backend` client command. **VERIFIED** (BackendArgument exists in the repo). [BackendArgument.java](https://github.com/Engine-Room/Flywheel/blob/1.21.1/dev/common/src/main/java/dev/engine_room/flywheel/impl/BackendArgument.java)
### 5. Veil + irisveil (Iris Veil Compat 0.3.0)
- Veil is a rendering SDK; **Create: Aeronautics and Sable are the prominent consumers** (Aeronautics "uses Veil for its airplane wings, glass panels, contrails, and other visual effects"). [iris-veil-compat README](https://github.com/leon-o/iris-veil-compat) — **DOCUMENTED**.
- Without a bridge, "Veil-rendered visuals bypass the shaderpack's pipeline entirely, causing them to appear broken or missing" — i.e. irisveil is a *correctness* mod first.
- Mechanism/overhead: it mixin-intercepts Veil shader compilation, **caches the processed shader source**, and at render time does **AST-level patching (glsl-transformer)** to inject Veil logic into the shaderpack's gbuffer vertex/fragment programs, then **creates an extra Iris `ShaderInstance`** to replace the original Veil shader. Cache invalidates on shaderpack switch/reload. [README](https://github.com/leon-o/iris-veil-compat) — **DOCUMENTED**.
- Cost profile (**INFERENCE** from that mechanism): a one-time compile/patch cost per shaderpack load, plus one extra ShaderInstance and its draw path per Veil object; not a per-frame CPU cost of the same order as the Flywheel fallback. It requires Iris 1.8.1+, Sodium, Veil 4.0.0+, and was tested against **Sable 2.0.6**.
- Note its pinned dev baseline is *exactly* this pack's stack: Iris 1.8.14-beta.1+1.21.1, Sodium 0.8.13+mc1.21.1, Veil 4.5.1, Flywheel 1.0.6, Create Aeronautics 1.3.2, Sable 2.0.6, NeoForge 21.1.255 (min 21.1.228). The README mentions "the existing **Iris Flywheel Compat** missing-access-transformer warning remains in the log" — i.e. the tested configuration *did* include Iris Flywheel Compat, which the pack does not list. [iris-veil-compat README](https://github.com/leon-o/iris-veil-compat) — **DOCUMENTED**. Worth confirming against the pack manifest.
### 6. ImmediatelyFast · EntityCulling · ModernFix · FerriteCore
**ImmediatelyFast 1.6.14** — batches immediate-mode draws (entities, block entities, particles, text, GUI/HUD, other mods' immediate rendering) and replaces vanilla map/HUD/text paths. Author's own benchmark table (Ryzen 5 1600 + GTX 1060 — close to P1) claims ~2× FPS for entity-heavy scenes, ~1.4× HUD, ~2× text, ~5× maps; notably the *same* test with **Iris+Sodium** only improved 60 → 76 FPS (1.27×) on 1000 cows, because the GPU bound. [ImmediatelyFast README](https://github.com/RaphiMC/ImmediatelyFast/blob/1.21/README.md) — **DOCUMENTED**. Shipped defaults (all "regular" options on): `font_atlas_resizing`, `map_atlas_generation`, `hud_batching`, `fast_text_lookup`, `fast_buffer_upload` = **true**; experimental (`experimental_disable_error_checking`, `experimental_disable_resource_pack_conflict_handling`, `experimental_sign_text_buffering`, `experimental_screen_batching`) = **false**. [ImmediatelyFastConfig.java @ 1.21](https://github.com/RaphiMC/ImmediatelyFast/blob/1.21/common/src/main/java/net/raphimc/immediatelyfast/feature/core/ImmediatelyFastConfig.java) — **VERIFIED**. Practical read: with shaders on, ImmediatelyFast mostly buys back CPU headroom, not GPU frames.
**EntityCulling 1.11.2** — asynchronous path-traced occlusion culling on spare CPU threads, plus entity tick culling; "Fully configurable … Whitelist entities and block entities that should not be culled or tick-culled", and there is a "treat leaves as solid blocks for Entity Culling" option to help in forests. [EntityCulling README](https://github.com/tr7zw/EntityCulling/blob/main/README.md) — **DOCUMENTED**. Shipped defaults (`config/entityculling.json`, configVersion 9) — [Config.java](https://github.com/tr7zw/EntityCulling/blob/main/EntityCulling-Versionless/src/main/java/dev/tr7zw/entityculling/versionless/Config.java) — **VERIFIED**:
| Key | Default | Comment |
|---|---|---|
| `safeMode` | `true` | — |
| `tracingDistance` | `128` | blocks of ray-tracing range |
| `tickCulling` | `true` | entity tick culling on |
| `captureRate` | `5` | how often visibility is recomputed |
| `hitboxLimit` | `50` | don't cull when too many hitboxes on screen |
| `sleepDelay` | `10` | thread sleep |
| `blockEntityFrustumCulling` | `true` | — |
| `solidLeaves` | `false` | "treat leaves as solid" — relevant with fast/cullable leaves |
| `skipEntityCulling` / `skipBlockEntityCulling` | `false` | global off-switches |
| `forceDisplayCulling` | `false` | — |
| `renderNametagsThroughWalls` | `true` | cosmetic |
| `blockEntityWhitelist` / `entityWhitelist` / `tickCullingWhitelist` | populated | **`tickCullingWhitelist` already contains `create:contraption`, `create:carriage_contraption`, `create:gantry_contraption`, `create:stationary_contraption` and `alexscaves:gum_worm`/`gum_worm_segment`** — i.e. this pack's Create + Alex's Caves content is already protected from tick-culling. **VERIFIED** |
**ModernFix 5.27.24 — defaults on NeoForge 1.21.1.** Options default to **enabled unless overridden**; the explicit override map is in source. [ModernFixEarlyConfig.java @ 1.21.1](https://github.com/embeddedt/ModernFix/blob/1.21.1/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java) — **VERIFIED**:
| Option (`mixin.*` in `config/modernfix-mixins.properties`) | Default | Why it matters here |
|---|---|---|
| `perf.dynamic_resources` | **false** | The big one: dynamic/on-demand model loading. Author's wiki: **1.7 GB → 945 MB (−55%)** on a modpack that *already had FerriteCore*. Off by default only for compat risk (startup crash / missing textures). |
| `perf.dynamic_entity_renderers` | **false** | (Option retained in the 1.21.1 defaults map; no matching mixin exists on this branch.) |
| `perf.faster_texture_stitching` | **true** (not overridden) | Speeds atlas stitching at load — valuable with Chipped/Rechiseled/Macaw's texture counts. Auto-disabled if OptiFine present. |
| `perf.faster_item_rendering` | **false** | Off by default. |
| `perf.deduplicate_location`, `perf.deduplicate_climate_parameters`, `perf.clear_mixin_classinfo` | **false** | Off by default. |
| `bugfix.packet_leak`, `bugfix.skip_redundant_saves`, `bugfix.missing_block_entities`, `feature.direct_stack_trace`, `feature.stalled_chunk_load_detection`, `feature.blockentity_incorrect_thread`, `feature.spam_thread_dump`, `feature.remove_chat_signing`, `bugfix.restore_old_dragon_movement` | **false** | Off by default. |
| `feature.integrated_server_watchdog`, `feature.snapshot_easter_egg`, `feature.warn_missing_perf_mods`, `feature.log_stdout_in_log_files` | **true** | — |
| `bugfix.fix_config_crashes`, `feature.registry_event_progress` | **true** on non-Fabric | NeoForge-only. |
| (implicit) `perf.thread_priorities` / `perf.boost_worker_count` | **true** | Sets the system property **`max.bg.threads = availableProcessors − 3`** (reserving server thread + client thread + GC). On the fleet's 4C/8T that's **5**. Can be overridden by `-Dmax.bg.threads`, and is auto-disabled when Smooth Boot / ThreadTweak are present. **VERIFIED** |
The dynamic_resources win is documented by the author as "disabled by default **to maximize compatibility**", with the failure modes being a startup crash or missing textures, and the recommendation that packs which test it should turn it on. [Dynamic Resources FAQ](https://github.com/embeddedt/ModernFix/wiki/Dynamic-Resources-FAQ) — **DOCUMENTED**.
**FerriteCore 7.0.3** — a memory mod (RAM savings, mostly client-side blockstate/model objects). Config file is **`config/ferritecore-mixin.toml`** (built from `Constants.MODID + "-mixin.toml"`). [ConfigFileHandler.java @ 1.21.1](https://github.com/malte0811/FerriteCore/blob/1.21.1/NeoForge/src/main/java/malte0811/ferritecore/mixin/platform/ConfigFileHandler.java) — **VERIFIED**. Options and defaults — [FerriteConfig.java @ 1.21.1](https://github.com/malte0811/FerriteCore/blob/1.21.1/Common/src/main/java/malte0811/ferritecore/mixin/config/FerriteConfig.java) — **VERIFIED**:
| Key | Default |
|---|---|
| `replaceNeighborLookup` | true |
| `replacePropertyMap` (requires the above) | true |
| `cacheMultipartPredicates` | true |
| `modelResourceLocations` | true |
| `multipartDeduplication` (requires predicates) | true |
| `blockstateCacheDeduplication` | true |
| `bakedQuadDeduplication` | true |
| `modelSides` | true |
| `dataComponentPatch` | true |
| `useSmallThreadingDetector` | **false** — "very rare and very hard-to-reproduce crashes, use at your own risk" |
| `compactFastMap` | **false** — "slightly more compact, but also slightly slower representation" |
| `populateNeighborTable` | **false** — "slightly increases memory usage" |
Its own `summary.md` quantifies the mechanism (e.g. ~600 MB of `Table` neighbor structures → ~7 MB of `FastMap`s in a 2020-era pack), i.e. FerriteCore is a bet that pays in **heap**, which is exactly P2's binding constraint. [FerriteCore summary.md](https://github.com/malte0811/FerriteCore/blob/1.21.1/summary.md) — **DOCUMENTED**. Turning on `compactFastMap` trades CPU for a little more RAM — the wrong direction on a 4C/8T main-thread-bound machine; **INFERENCE**.
### 7. Mipmaps, atlas/VRAM, biome blend, Better Leaves, Fresh Animations
**Mipmaps.** Vanilla caps usable mipmap levels at **4**, which is the "full pyramid" level for a 16×16 texture; "Anything higher than that will have incomplete mipmaps", and the optimal level scales with pack resolution (16 → 4, 32 → 5, 64 → 6 …). Because Minecraft stitches every block texture into one atlas, "the maximum mipmap level of this atlas is the smallest maximum mipmap level of any individual texture … a single left-over 16x16 texture will limit the atlas to 4 mipmap levels". [Better Mipmaps](https://modrinth.com/mod/better-mipmaps) — **DOCUMENTED**. Consequences, and the raw VRAM maths, are **INFERENCE**: a mipmap pyramid adds ≈ 1 + ¼ + 1/16 + … ≈ **+33 %** over the base atlas, and that multiplier applies to a *large* base once Chipped / Rechiseled / Macaw's / handcrafted are in play (these add thousands of models, and animated textures occupy atlas space for *every* frame of the animation). Sodium's own tooltip already warns that raising mipmaps "could adversely affect performance with resource packs that use many animated textures", and that the option requires an asset reload. [Sodium 0.8.13 lang](https://github.com/CaffeineMC/sodium/blob/mc1.21.1-0.8.13/common/src/main/resources/assets/sodium/lang/en_us.json) — **VERIFIED**. I could not find a *primary* measured figure for atlas size/VRAM of a 212-mod pack; treat "lower mipmap levels = less VRAM + less asset reload time, biggest for 6 GB P1" as a hypothesis to measure.
**Biome blend.** Sodium's tooltip: default **5×5**; "Higher values will **greatly increase the amount of time it takes to load or update chunks**, for diminishing improvements in quality." [Sodium 0.8.13 lang](https://github.com/CaffeineMC/sodium/blob/mc1.21.1-0.8.13/common/src/main/resources/assets/sodium/lang/en_us.json) — **VERIFIED**. This is a **chunk-build/micro-stutter** cost, not a steady-state FPS cost — relevant on all three machines (all 4C/8T), and it composes with Sodium's 2 chunk-builder threads and DH's `lodBiomeBlending` (default 3). The shaderLABS wiki notes vanilla's implementation is naive and that the fastest reliable reduction is simply lowering it. [shaderLABS](https://shaderlabs.org/wiki/Performance_Tips) — **COMMUNITY**.
**Better Leaves (Motschen's) 9.6.** "Better Leaves adds additional layers to leaf blocks, making them appear more bushy, dense and natural." Version 9.0 pre-generates textures per leaf type for performance; the pack explicitly says: *"I strongly recommend installing the **Cull Leaves** mod alongside Sodium/Embeddium to get more FPS."* [Motschen's Better Leaves](https://modrinth.com/project/uvpymuxq) — **DOCUMENTED**. Read: it *increases* per-leaf-block geometry (more quads → more mesh work + more overdraw) and relies on face culling to pay for it. Sodium's own `Leaves` setting ("Fast = transparent vs opaque") is the vanilla-side lever; Sodium 0.8.12's changelog warns **"sodiumleafculling does not work"** on this line, so don't plan on a Sodium leaf-culling add-on. [Sodium 0.8.12 changelog](https://modrinth.com/mod/sodium/version/mc1.21.1-0.8.12-neoforge) — **DOCUMENTED**. EntityCulling's `solidLeaves` option exists for exactly this scenario. **Cull Leaves is not in the pack.** [Community reports](https://www.reddit.com/r/Minecraft/comments/42glrg/leaves_causing_fps_issues_fixed_with_a_resource/) of leaves dominating FPS in forested/jungle areas are **COMMUNITY** but consistent with the pack's own geometry change.
**Fresh Animations 1.10.4 + EMF 3.3.9 + ETF 7.2.4.** Fresh Animations requires EMF/ETF on 1.19+. [Fresh Animations](https://modrinth.com/resourcepack/fresh-animations) — **DOCUMENTED**. Reported cost ~5–10 FPS in mob-dense areas [mcmodhub](https://mcmodhub.com/resource-packs/fresh-animations) — **COMMUNITY**; there is also a mod-side bug report of "Huge FPS drops with Fresh Animations and the mods needed for it (EMF/ETF)" [easy-villagers #243](https://github.com/henkelmax/easy-villagers/issues/243) — **DOCUMENTED (bug report)**. EMF exposes throttles (**VERIFIED** from [EMFConfig.java](https://github.com/Traben-0/Entity_Model_Features/blob/master/src/main/java/traben/entity_model_features/config/EMFConfig.java)): `animationLODDistance` (**default 20**), `modelUpdateFrequency` (**default `UpdateFrequency.Average`**), `animationFrameSkipDuringIrisShadowPass` (**default `true`** — a real shader-specific saving), `retainDetailOnLowFps` (true), `retainDetailOnLargerMobs` (true), `renderModeChoice` (`NORMAL`), `allowedCEM` (`ALL`). The shadow-pass frame-skip being on by default is worth noting given §3: it is one of the few places Iris+EMF already cooperate.
**Fusion/Athena CTM (1.3.15b / 4.0.6)** and **Chipped/Rechiseled/Macaw's** appear in this stack as texture/model-count amplifiers (atlas + model bake + D4). Fusion CTM is the 1.21.1 successor to the old "CTM" path that ModernFix's `dynamic_resources` explicitly carries compat mixins for (`dynamic_resources/ctm/…`, `…/ldlib/…`). [ModernFix tree @ 1.21.1](https://github.com/embeddedt/ModernFix/tree/1.21.1/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources) — **VERIFIED** presence of those compat patches; whether Fusion specifically is covered is **INFERENCE / to verify**.
---
## Top levers (ranked)
| # | Lever | Expected impact | Machine it helps | Risk | Confidence |
|---|---|---|---|---|---|
| 1 | **Decide whether shaders are mandatory per machine.** With shaders on, Flywheel's instancing/indirect backends are unavailable → Create kinetics render per-BE *and* twice for shadows. Running Complementary only on P1/P2 (or all-off) is the largest single swing. | **High** | all, especially P0 | none (policy) | **VERIFIED** (source) |
| 2 | **Add a Flywheel↔Iris bridge** (Iris Flywheel Compat / Colorwheel) if Create throughput matters with shaders. | **High** | P1/P2 (CPU), P0 | **High** (both are unofficial, "almost always breaks on a Create or Iris update") | **DOCUMENTED** |
| 3 | **Complementary profile ≤ MEDIUM + Shadow Distance ≤ 8, Entity Shadows OFF.** Entity shadows are author-flagged as >50 % FPS in some cases; profiles above MEDIUM push shadow distance past the server's 8-chunk view distance. | **High** | all, esp. P0 | none (visual) | **DOCUMENTED** |
| 4 | **DH settings + shaders:** lower DH `lodChunkRenderDistanceRadius` (default 256), consider `maxHorizontalResolution` below `BLOCK`, `horizontalQuality`/`verticalQuality` below MEDIUM, `vanillaFadeMode` off `DOUBLE_PASS`, `lodBiomeBlending` 3→0, and the DH *CPU/thread preset* down from BALANCED. Remember the DH shadow pass renders **all** DH chunks. | **High** | all; P2 (RAM/CPU) most | low–med (visual bands, DH border) | **VERIFIED** (defaults) + **DOCUMENTED** (behaviour) |
| 5 | **ModernFix `mixin.perf.dynamic_resources=true`** — the biggest documented memory/launch win (−55 % in the author's test *with* FerriteCore), default-off only for compat. | **High** (RAM/load) | **P2** (8 GB free) | Med (startup crash / missing textures) | **DOCUMENTED** + **VERIFIED** (default) |
| 6 | **Lower `mipmapLevels` 4 → 2/3** and/or trim high-res overlays: cuts the ~+33 % mipmap VRAM multiplier on an atlas inflated by Chipped/Rechiseled/Macaw's plus animated-texture frames. | **Med** | **P1** (6 GB VRAM), P0 | low (slight shimmer) | **INFERENCE** on the numbers; **DOCUMENTED** on the mechanism |
| 7 | **Verify Sodium's high-impact options are actually on**: `animateOnlyVisibleTextures`, `useFogOcclusion`, `useEntityCulling`, `useBlockFaceCulling`, `useNoErrorGLContext` — all default `true`, and all are the pack's most animated-texture/RP-heavy-mod sensitive ones. | **Med** | all, P0 (iGPU) most | none | **VERIFIED** (defaults) |
| 8 | **Sodium `chunkBuilderThreads` and `chunkBuildDeferMode`:** default auto = **2 threads** on all three 4C/8T machines with "Deferred" already selected. Try 3–4 threads on P1/P2; expect faster chunk fill, riskier frame times. | **Med** | P1/P2 (chunk-bound) | low–med (frame-time variance) | **VERIFIED** (formula) |
| 9 | **Biome blend 2 → 0/1** (and DH `lodBiomeBlending` 3 → 0) — pure chunk-load/micro-stutter lever, cheap to test. | **Med** | all (4C/8T) | none (visual) | **VERIFIED** (tooltip) |
| 10 | **EMF throttles:** raise `modelUpdateFrequency` toward slower, raise/lower `animationLODDistance`, keep `animationFrameSkipDuringIrisShadowPass=true`. | **Med** | P0/P1 | low (animation quality) | **VERIFIED** (keys/defaults) |
| 11 | **Better Leaves 9.6 + no Cull Leaves = paid geometry.** Add a leaf-culling mod (Cull Leaves) or drop the RP; set EntityCulling `solidLeaves=true` if leaves are opaque. Note `sodiumleafculling does not work` on this Sodium line. | **Med** | all, P0 | low–med | **DOCUMENTED** + **VERIFIED** (Sodium changelog/EntityCulling key) |
| 12 | **ModernFix `max.bg.threads` = cores−3** is already being set; if `-Dmax.bg.threads` is being passed in JVM args, confirm it isn't fighting this. | **Low–Med** | P1/P2 | low | **VERIFIED** |
| 13 | **FerriteCore: leave defaults.** Don't enable `compactFastMap` (CPU for RAM) on a main-thread-bound fleet. | **Low** | P2 only | low | **VERIFIED** (source) |
| 14 | **ImmediatelyFast:** leave defaults; the author's own bench shows only 1.27× under Iris+Sodium. | **Low** | P1/P2 (CPU) | none | **DOCUMENTED** |
---
## Open questions to verify by benchmark
1. **Does the pack actually ship an Iris↔Flywheel bridge?** The provided mod list has `irisveil` but no Iris Flywheel Compat / Colorwheel — yet irisveil's own validated baseline includes Iris Flywheel Compat. Check `mods/` and the log for `Flywheel backend fell back from 'flywheel:indirect' to 'flywheel:off'` and for `irisflw:iris_instancing`. **This single log line decides levers #1/#2.**
2. **What is the actual block-atlas size and VRAM on P1?** (Atlas texture allocation, plus mipmap multiplier.) Only answerable by measurement (e.g. a VRAM/F3 or driver-level capture) — needed before trusting lever #6.
3. **Complementary Reimagined r5.9.3's shipped default profile** — the repo table is authoritative for the settings, but the *default* profile on a fresh install should be read in-game (the repo has a `COMPLEMENTARY` profile the older docs predate).
4. **Whether MakeUp Ultra Fast 9.5f actually beats Complementary at equivalent visual settings** on P0/P1 — no primary comparison exists; measure.
5. **DH's real cost with the chosen shaderpack**: measure with DH rendering on/off, and with DH's shadow pass (`dhShadow.enabled`) forced off, to separate the DH-shadow cost from DH-terrain cost.
6. **ModernFix `dynamic_resources=true` compatibility with Fusion CTM / Athena / Chipped / Rechiseled / Macaw's** on this exact pack — author lists CTM-style compat patches but not these mods by name; test with a resource reload and a look for missing textures.
7. **Does `Animate Only Visible Textures` actually hold up with Fresh Animations + FA All Extensions + Better Leaves' generated textures** (author warns of "issues with some textures not being animated")?
8. **EntityCulling block-entity whitelist adequacy for this pack** — Create contraptions are tick-cull-whitelisted, but check whether specific Create/Alex's Caves block entities pop or vanish (the config is a plain JSON list and is safe to extend).
9. **Whether Sodium's `QuadSplittingMode = SAFE` is limiting translucency correctness/instability** in a Chipped/Rechiseled/glass-heavy base — the tooltip says `UNLIMITED` imposes no geometry cap but risks instability; only worth testing if sorting artifacts are visible.
10. **P2 memory headroom specifically**: with ~8 GB free, does DH's "6–8 GB allocated" recommendation plus ModernFix `dynamic_resources` fit, and does a concurrent GC (ZGC) change the micro-stutter picture?
---
## Sources
**Kept**
- Sodium, `mc1.21.1-0.8.13` source — `SodiumConfigBuilder.java`, `SodiumOptions.java`, `ChunkBuilder.java`, `lang/en_us.json`. https://github.com/CaffeineMC/sodium/tree/mc1.21.1-0.8.13 — the authoritative option set **and defaults** for the pinned version.
- Sodium version page / changelog, `mc1.21.1-0.8.12-neoforge` (the base of the 0.8.13 line). https://modrinth.com/mod/sodium/version/mc1.21.1-0.8.12-neoforge — perf claims + official mod-compat matrix.
- Flywheel 1.21.1 branch: `Backends.java`, `BackendManagerImpl.java`, `IrisCompat.java`, `ShadersModHelper.java`, `NeoForgeFlwConfig.java`. https://github.com/Engine-Room/Flywheel/tree/1.21.1/dev — proves the Iris→backend fallback and gives the real NeoForge config keys.
- Flywheel tag `mc1.21.1/v1.0.5`. https://github.com/Engine-Room/Flywheel/blob/mc1.21.1/v1.0.5/common/src/backend/java/dev/engine_room/flywheel/backend/Backends.java — confirms the shader gate predates 1.0.6.
- Iris issue #1414 ("Flywheel is not supported"), includes maintainer statement. https://github.com/IrisShaders/Iris/issues/1414
- Create `mc1.21.1/dev`: `KineticBlockEntityRenderer.java` (+ `ShaftRenderer.java`). https://github.com/Creators-of-Create/Create/blob/mc1.21.1/dev/src/main/java/com/simibubi/create/content/kinetics/base/KineticBlockEntityRenderer.java — proves the non-Flywheel fallback path exists and is used when visualization is unsupported.
- Iris docs — Distant Horizons integration (`dh_terrain`/`dh_water`/`dh_shadow`, auto-enabled shadow pass, no shadow distance). https://shaders.properties/current/reference/mod-support/distant_horizons/
- Distant Horizons core `Config.java` (current main). https://gitlab.com/distant-horizons-team/distant-horizons-core/-/blob/main/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java — verified config keys + defaults.
- Distant Horizons Modrinth description (shader requirements, stutter checklist). https://modrinth.com/mod/distanthorizons
- Complementary Reimagined `shaders.properties` + `lang/en_US.lang` (author repo). https://github.com/ComplementaryDevelopment/ComplementaryReimagined — profile table, shadow distances, and the author's own perf warnings.
- MakeUp Ultra Fast `shaders.properties` + README (author repo). https://github.com/javiergcim/MakeUpUltraFast — verified option keys, shipped profile values, `dhShadow.enabled=false`.
- Iris Veil Compat README (mechanism, overhead, tested baseline). https://github.com/leon-o/iris-veil-compat
- ModernFix `ModernFixEarlyConfig.java` @ 1.21.1 (default table). https://github.com/embeddedt/ModernFix/blob/1.21.1/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java
- ModernFix Dynamic Resources FAQ. https://github.com/embeddedt/ModernFix/wiki/Dynamic-Resources-FAQ — 1.7 GB → 945 MB with FerriteCore already installed; why it's off by default.
- FerriteCore `FerriteConfig.java`, `ConfigFileHandler.java`, `summary.md` @ 1.21.1. https://github.com/malte0811/FerriteCore/tree/1.21.1
- EntityCulling `Config.java` (configVersion 9 defaults + whitelists). https://github.com/tr7zw/EntityCulling/blob/main/EntityCulling-Versionless/src/main/java/dev/tr7zw/entityculling/versionless/Config.java
- ImmediatelyFast `ImmediatelyFastConfig.java` + README bench table. https://github.com/RaphiMC/ImmediatelyFast/blob/1.21/common/src/main/java/net/raphimc/immediatelyfast/feature/core/ImmediatelyFastConfig.java
- EMF `EMFConfig.java` (animation throttles incl. `animationFrameSkipDuringIrisShadowPass`). https://github.com/Traben-0/Entity_Model_Features/blob/master/src/main/java/traben/entity_model_features/config/EMFConfig.java
- Better Mipmaps description (vanilla 4-level cap, atlas-wide minimum, resolution↔level table). https://modrinth.com/mod/better-mipmaps
- Motschen's Better Leaves description (geometry change, Cull Leaves recommendation, v9 perf rework). https://modrinth.com/project/uvpymuxq
- shaderLABS Performance Tips wiki (biome blend, shader menu sliders, RAM guidance). https://shaderlabs.org/wiki/Performance_Tips
- Ocuwheel description (explicit "Flywheel disables GPU instancing under shaders, falls back to CPU rendering"). https://www.curseforge.com/minecraft/mc-mods/ocuwheel-oculus-flywheel-compat
- Sodium Extras description (which options are *not* in Sodium — fog toggles etc.). https://modrinth.com/mod/sodium-extras
**Dropped**
- shadersmods.com / mc-mods.com / mcmodhub "Complementary Reimagined review" pages — SEO content, and their profile tables describe r2.1 (2023). Used only as a secondary lead; superseded by the author's own `shaders.properties`.
- irisshaders.org / minecrafthub.io / mctoolbox.net guides — unverifiable marketing-style guides, no version-specific claims worth keeping.
- r/feedthebeast, r/minecraftshaders, r/Minecraft threads on MakeUp-vs-Complementary FPS — anecdotal, no controlled comparison.
- Sodium `dev`-branch lang file as a *description of 0.8.13* — kept only to demonstrate which options (hidden fluid culling etc.) are **absent** on 1.21.1.
- minecraft.wiki/w/Options.txt — returned HTTP 403, so the vanilla `mipmapLevels`/`biomeBlendRadius` defaults are stated from Sodium's own language file ("Default is 5x5" for biome blend) rather than the wiki.
@@ -0,0 +1,185 @@
# Research: Distant Horizons 3.3.x (client) + Xaero's Minimap 26.5.0 / World Map 1.46.0 — client-side performance
Pack context: Onion MC, MC 1.21.1 / NeoForge 21.1.251, pinned `DistantHorizons-3.3.2-1.21.1-fabric-neoforge.jar`, `xaerominimap-neoforge-1.21.1-26.5.0.jar`, `xaeroworldmap-neoforge-1.21.1-1.46.0.jar` (verified against `mods/*.pw.toml` in this repo). Dedicated server with DH; Iris 1.8.14-beta.1; shaderpacks Complementary Reimagined r5.9.3 + MakeUp Ultra Fast 9.5f. All 3 fleet machines are 4C/8T.
## Summary
DH 3.3.2's client cost splits into (a) CPU: LOD building/generation threads (thread preset; on 8 logical CPUs BALANCED = 4 threads, Minimal = 1), and (b) GPU/VRAM: LOD render distance × quality preset (distance is the dominant term; DH docs say >512 chunks gets "difficult due to high VRAM/GPU usage"; a 1.x-era dev-tracker data point put 1024 chunks at ~4 GB VRAM). In multiplayer the client cannot generate LODs at all — the server sends them (default 500 KB/s per player) — so client-side "distant generation" settings are singleplayer-only; server pregen via `/dh pregen` is the officially recommended path, and running Chunky alongside DH generation is explicitly warned against (holes). Xaero's pair is cheap: the minimap reuses world-map textures by default, world-map disk data grows with exploration but in-RAM region cache is bounded (cappable via a "max loaded regions" setting added in 1.41.2), and the map screen skips world rendering by default.
## Findings — Distant Horizons 3.3.2
### 1. What each client setting actually costs (keys verified from the pinned jar's `assets/distanthorizons/lang/en_us.json` + DH core source)
GUI path and TOML key names below were read from the 3.3.2 jar and DH core `Config.java`; every listed key exists in 3.3.2. [Config.java (DH core, main/3.3.x line)](https://gitlab.com/distant-horizons-team/distant-horizons-core/-/raw/main/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java), [lang file in jar 3.3.2](https://modrinth.com/mod/distanthorizons/version/3.3.2-1.21.1)
| Concern | Key (GUI path) | Verified facts |
|---|---|---|
| LOD render distance | `client.advanced.graphics.quality.lodChunkRenderDistanceRadius` | min 32 / **default 256** / max 4096 chunks; "best effort" number [Config.java](https://gitlab.com/distant-horizons-team/distant-horizons-core/-/raw/main/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java); official gallery: "By default DH renders 256 chunks wide… anything above 512 may become difficult due to high VRAM/GPU usage" [Modrinth gallery](https://modrinth.com/mod/distanthorizons/gallery) |
| Quality preset | `client.qualityPresetSetting` (MINIMUM/LOW/MEDIUM/HIGH/EXTREME) | Preset just sets the sub-options below; **generation is independent** — "DH will always generate LODs at the maximum possible quality so you can use any Quality preset you want later" (no regen needed) [DH FAQ General](https://gitlab.com/distant-horizons-team/distant-horizons/-/wikis/1-user-guide/1-frequently-asked-questions/1-general/General) |
| Horizontal quality / resolution | `…quality.horizontalQuality`, `…quality.maxHorizontalResolution` | "Higher settings will … increase memory and GPU usage"; resolution BLOCK = 256 LODs/chunk (fanciest) vs CHUNK = 1 LOD/chunk (fastest) [Config.java](https://gitlab.com/distant-horizons-team/distant-horizons-core/-/raw/main/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java) |
| Vertical quality | `…quality.verticalQuality` | HEIGHT_MAP (cheapest) → PIXEL_ART; "higher options … increase memory and GPU usage"; too-low vertical quality causes flat/discolored LODs [Config.java + Problems & Solutions](https://gitlab.com/distant-horizons-team/distant-horizons/-/wikis/1-user-guide/1-frequently-asked-questions/2-problems-and-solutions/Problems-and-Solutions) |
| Transparency | `…quality.transparency` | Only `DISABLED` or `COMPLETE` in 3.3.2 (fake transparency removed in 3.1.0-b); DISABLED = opaque LODs = cheaper; **3.1.1-b+ forces transparency ON when Iris is present** [changelog 3.1.0-b/3.1.1-b](https://modrinth.com/mod/distanthorizons/version/3.1.1-b-1.21.1) |
| Cave culling | `…culling.enableCaveCulling`, `…culling.caveCullingHeight` (default 60) | "culls all faces with skylight value of 0 under the cave culling height" — big face-count saver in mountains/overhangs [lang tooltip, jar 3.3.2] |
| Overdraw | `…culling.overdrawPrevention` (default −1 = auto), `…culling.reduceOverdrawWithFastMovement` | % of vanilla RD where DH starts rendering behind vanilla terrain; −1 auto-scales; holes-at-edge tradeoff documented; auto reduce while flying [Config.java + P&S quick guide](https://gitlab.com/distant-horizons-team/distant-horizons/-/wikis/1-user-guide/1-frequently-asked-questions/2-problems-and-solutions/Problems-and-Solutions) |
| Frustum culling | `…culling.disableFrustumCulling` (default false = culling ON), `…culling.disableShadowPassFrustumCulling` | Culling "increasing GPU performance"; leave enabled (disabled only if LODs pop at screen corners) [lang tooltip] |
| Vanilla fade mode | `…quality.vanillaFadeMode` (NONE/SINGLE_PASS/DOUBLE_PASS) | NONE "Fastest… pronounced border"; DOUBLE_PASS "Slowest… smoothest" (MEDIUM+ default) [Config.java] |
| DH SSAO / AA | `client.advanced.graphics.enableSsao`, `…enableAntiAliasing` | 3.3.0 added TAA; SSAO/AA off in MINIMUM/LOW presets [changelog 3.3.0](https://modrinth.com/mod/distanthorizons/version/3.3.0-1.21.1) + preset handler source |
| LOD textures | `…texture.enableTexturedLods`, `maxTexturedLodDetailLevel` | 3.2.0 feature; "Only available for base DH rendering, **Iris shaders will render LODs as untextured**" → no shader cost under Iris either way [changelog 3.2.0-b](https://modrinth.com/mod/distanthorizons/version/3.2.0-b-1.21.1) |
| DH clouds/beacons | `…genericRendering.enableCloudRendering`, `enableMultiLayerClouds`, `enableBeaconRendering` | DH renders its own 3-layer clouds; GPU filler; Complementary sets `dhClouds = off` (see §4) |
| Zoom quality | `…quality.increaseQualityWhenZoomedIn`, `maxZoomQualityIncrease` (default 4) | Loading crisper LODs while zoomed costs CPU/memory while zoomed [Config.java; 3.3.0 fixed "massive lag when zoomed in and spinning the camera"] |
| CPU threads | `client.threadPresetSetting` → `common.multiThreading.numberOfThreads`, `threadRunTimeRatio`, `threadPriority`, `useC2meThreadCount` | Preset = % of **logical** CPUs (source has table: 1.0 ⇒ 8 threads on 8 logical) + runtime ratio [ThreadPresetConfigEventHandler](https://gitlab.com/distant-horizons-team/distant-horizons-core/-/raw/main/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/ThreadPresetConfigEventHandler.java) |
| LOD storage | `common.lodBuilding.dataCompression` (LZ4/ZSTD…), `worldCompression` (MERGE_SAME_BLOCKS/VISUALLY_EQUAL), `databaseSyncMode` (FULL/NORMAL/OFF), `disableUnchangedChunkCheck` | LODs live in SQLite (`.minecraft/Distant_Horizons_server_data/<server>/` in MP); NORMAL sync default since 3.3.0 cut disk work ~50%; OFF risks corruption on power loss [General FAQ](https://gitlab.com/distant-horizons-team/distant-horizons/-/wikis/1-user-guide/1-frequently-asked-questions/1-general/General), [changelog 3.3.0](https://modrinth.com/mod/distanthorizons/version/3.3.0-1.21.1) |
| World gen (SP only) | `common.worldGenerator.enableDistantGeneration`, `generatorPlan` (SURFACE_THEN_CHUNKS/SURFACE_ONLY/CHUNKS_ONLY/DISABLED), `chunkGeneratorMode` (…/SURFACE/FEATURES/INTERNAL_SERVER), `enableFastSurfaceGenerator`, `limitIfServerUnhealthy`, `pauseIfMovingQuickly` | 3.3.0 added fast surface gen + "pause world gen if server thread overloaded or moving quickly — both options can be toggled… if faster world gen is desired over smoothness" [changelog 3.3.0](https://modrinth.com/mod/distanthorizons/version/3.3.0-1.21.1) |
Quality preset → what it sets (from [RenderQualityPresetConfigEventHandler.java](https://gitlab.com/distant-horizons-team/distant-horizons-core/-/raw/main/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/RenderQualityPresetConfigEventHandler.java)) — note the trap that **HIGH/EXTREME turn cave culling OFF**:
| Preset | maxHorizRes | Vertical | Horizontal | Transparency | SSAO | AA | Vanilla fade | Cave culling | Biome blending |
|---|---|---|---|---|---|---|---|---|---|
| Minimum | 2-block | Height map | Lowest | Disabled | off | off | None | on | 0 |
| Low | Block | Low | Low | Disabled | off | on | Single pass | on | 1 |
| Medium | Block | Medium | Medium | Complete | on | on | Double pass | on | 3 |
| High | Block | High | High | Complete | on | on | Double pass | **off** | 3 |
| Extreme | Block | Extreme | Extreme | Complete | on | on | Double pass | **off** | 3 |
Thread presets on the fleet's 4C/8T (source: `ThreadPresetConfigEventHandler` — percentages of `availableProcessors` = 8 logical):
| Preset | Threads | Runtime ratio |
|---|---|---|
| Minimal Impact | 1 | 0.5 |
| Low Impact | 2 | 1.0 |
| Balanced (default) | 4 | 1.0 |
| Aggressive | 6 | 1.0 |
| I Paid For The Whole CPU | 8 | 1.0 |
### 2. GPU upload settings — removed in 3.x
The old 2.x-era `gpuUploadMethod` (Direct / Single draw / Multi draw / Buffer storage) **no longer exists in 3.3.2**: the full option list in the pinned jar contains no upload-method key, and the 3.0.0-b changelog states "Remove instanced rendering config option — instanced rendering will always be used if available". The only related control left is `client.advanced.graphics.experimental.renderingEngine` (Auto / OpenGL / Blaze3D); Blaze3D is only for newer MC — on 1.21.1 it's effectively OpenGL/Auto, and DH ≥3.3 renders with reverse-Z depth. [jar lang file](https://modrinth.com/mod/distanthorizons/version/3.3.2-1.21.1), [changelog 3.0.0-b](https://modrinth.com/mod/distanthorizons/version/3.0.0-b-1.21.1), [changelog 3.1.0-b](https://modrinth.com/mod/distanthorizons/version/3.1.0-b-1.21.1). VERIFIED.
### 3. VRAM / RAM vs distance (no first-party table exists)
- Official qualitative: memory & GPU usage rise with horizontal/vertical quality, max resolution and LOD distance (config comments); >512 chunks "difficult due to high VRAM/GPU usage" [Config.java](https://gitlab.com/distant-horizons-team/distant-horizons-core/-/raw/main/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java), [Modrinth gallery](https://modrinth.com/mod/distanthorizons/gallery). DOCUMENTED.
- Only quantitative tracker data point is old (DH 1.x, 2022): "1024 with default settings uses ~4 GB VRAM and more than 8 GB RAM should be allocated to JRE or else there will be intense stuttering" [GitLab issue #157](https://gitlab.com/distant-horizons-team/distant-horizons/-/issues/157). COMMUNITY/dev-tracker, dated — treat as order-of-magnitude only.
- Heap guidance is first-party: 4–8 core CPUs → allocate 6–8 GB; 10+ cores → 12–16 GB; prefer ZGC (`-XX:+UseZGC`) over G1GC on Java 21 "known to cause stuttering"; DH even warns in-chat if G1GC is used [DH FAQ General](https://gitlab.com/distant-horizons-team/distant-horizons/-/wikis/1-user-guide/1-frequently-asked-questions/1-general/General), [P&S](https://gitlab.com/distant-horizons-team/distant-horizons/-/wikis/1-user-guide/1-frequently-asked-questions/2-problems-and-solutions/Problems-and-Solutions). VERIFIED.
- Community starting profile for mid hardware: LOD 96–192 chunks at Medium quality [distanthorizonsguide calculator](https://distanthorizonsguide.com/calculator). COMMUNITY.
- Official troubleshooting maps symptom→setting: CPU pegged → lower CPU preset or disable world gen; GPU pegged → lower quality preset or LOD distance; VRAM pegged → same [P&S](https://gitlab.com/distant-horizons-team/distant-horizons/-/wikis/1-user-guide/1-frequently-asked-questions/2-problems-and-solutions/Problems-and-Solutions). VERIFIED.
### 4. Iris shader cost & which packs support DH
- Iris ≥1.7 required; DH terrain renders through dedicated `dh_terrain`, `dh_water` and `dh_shadow` programs defined by the shaderpack — i.e. LODs are drawn with full shader effects, so shader cost scales to the LOD distance too; the DH shadow pass can be toggled per-pack via `dhShadow.enabled` [Iris docs — Distant Horizons](https://shaders.properties/current/reference/mod-support/distant_horizons/). VERIFIED.
- **Complementary Reimagined r5.x supports DH**: its `shaders.properties` has a `#ifdef DISTANT_HORIZONS` block with `blend.dh_water=…` and, importantly, `dhShadow.enabled = false` + `dhClouds = off` — the DH shadow pass and DH clouds are disabled, trimming shader work over distant terrain [Complementary Reimagined source](https://raw.githubusercontent.com/ComplementaryDevelopment/ComplementaryReimagined/main/shaders/shaders.properties). VERIFIED.
- **MakeUp Ultra Fast also has DH support**: `#ifdef DISTANT_HORIZONS: dhShadow.enabled = false; blend.dh_water=…` [MakeUp Ultra Fast source](https://raw.githubusercontent.com/javiergcim/MakeUpUltraFast/master/shaders/shaders.properties). VERIFIED. Both pack shaders therefore avoid the most expensive DH-over-shader path (DH shadows render *all* chunks into the shadow map).
- Shaders that don't support DH prevent LODs from rendering entirely [P&S](https://gitlab.com/distant-horizons-team/distant-horizons/-/wikis/1-user-guide/1-frequently-asked-questions/2-problems-and-solutions/Problems-and-Solutions). VERIFIED.
### 5. Client-generated vs server-provided LODs (this pack: server-provided)
- In multiplayer, clients **cannot** generate LODs: "Can clients generate instead? No, world generation is coupled with server-side logic" — the client requests and receives LODs (normal generation), gets real-time updates near players, and sync-on-load with timestamps [Server Owners wiki](https://gitlab.com/distant-horizons-team/distant-horizons/-/wikis/1-user-guide/1-frequently-asked-questions/5-server-owners/Server-Owners). VERIFIED.
- Server must have the same DH version; `Distant Generator Plan` must not be `DISABLED` on either side. Defaults allow **500 KB/s (4 Mbit/s) per player** of LOD traffic (`server.playerBandwidthLimit`); other keys: `generationRequestRateLimit`, `maxGenerationRequestDistance`, `enableRealTimeUpdates` + `realTimeUpdateDistanceRadiusInChunks`, `synchronizeOnLoad` + `syncOnLoadRateLimit` + `maxSyncOnLoadRequestDistance`, `globalBandwidthLimit`, `enableAdaptiveTransferSpeed` [Server Owners wiki + jar lang](https://gitlab.com/distant-horizons-team/distant-horizons/-/wikis/1-user-guide/1-frequently-asked-questions/5-server-owners/Server-Owners). VERIFIED. Client-side keys: `client.advanced.multiplayer.serverFolderNameMode` (local LOD DB naming).
- "Pre-generating LODs is strongly recommended to avoid performance issues" — command `/dh pregen start <dimension> <origin> <chunkRadius>`; avoid pregen on a live server. Client-side pregen alternative: copy server world, generate AFK, ship the SQLite DB back [Server Owners wiki](https://gitlab.com/distant-horizons-team/distant-horizons/-/wikis/1-user-guide/1-frequently-asked-questions/5-server-owners/Server-Owners). VERIFIED.
- World-gen mode notes: default `FEATURES` mode doesn't save generated chunks, so trees/structures can pop in/out at LOD borders; `INTERNAL_SERVER`/`FULL` prevents it [P&S](https://gitlab.com/distant-horizons-team/distant-horizons/-/wikis/1-user-guide/1-frequently-asked-questions/2-problems-and-solutions/Problems-and-Solutions). VERIFIED.
### 6. Known warnings relevant to this pack
- **DH + Chunky**: "Running Chunky will also generate & update LODs, but it's not recommended to rely on this… by loading a lot of chunks quickly you risk overloading DH and ending up with a lot of holes. Either use DH's pre-generation, or temporarily disable the mod entirely" [Server Owners wiki](https://gitlab.com/distant-horizons-team/distant-horizons/-/wikis/1-user-guide/1-frequently-asked-questions/5-server-owners/Server-Owners). DH 3.3.x also changed thread linking: "Don't link DH thread count to C2ME unless Chunky is also present… Using Chunky and DH isn't recommended due to the possibility of holes" [changelog 3.3.0](https://modrinth.com/mod/distanthorizons/version/3.3.0-1.21.1); 3.3.3 added auto-disable of DH worldgen while Chunky worldgen runs (3.3.2 does **not** have that guard). VERIFIED.
- **DH + Alex's Caves**: LODs fail to render; fix = set `biome_ambient_light_coloring = false` in `config/alexscaves-client.toml` (DH bug tracker issue #566) [DH wiki Mod Support](https://gitlab.com/distant-horizons-team/distant-horizons/-/wikis/1-user-guide/1-frequently-asked-questions/4-mod-support/Mod-Support). VERIFIED.
- **DH 3.3.2 + SSRD**: "Mark SSRD (Separate Sable Render Distance) 1.8.6 and below as incompatible — due to DH's change to rendering with Reverse Z depth". Pack has SSRD 1.8.7 → OK, but don't downgrade it [changelog 3.3.2](https://modrinth.com/mod/distanthorizons/version/3.3.2-1.21.1). VERIFIED.
- **Steam Deck / Mesa**: DH+ Sodium crash on Linux AMD/Intel Mesa GPUs → disable Sodium's "Use No Error context" (and delete DH config once) [P&S](https://gitlab.com/distant-horizons-team/distant-horizons/-/wikis/1-user-guide/1-frequently-asked-questions/2-problems-and-solutions/Problems-and-Solutions). DH 3.2.0 raised min OpenGL 3.2→3.3 (AMD fix) [changelog 3.2.0-b](https://modrinth.com/mod/distanthorizons/version/3.2.0-b-1.21.1). VERIFIED. Deck players run 128–256 LOD with Iris shaders at 30–40 FPS at 1200×800 [r/DistantHorizons](https://www.reddit.com/r/DistantHorizons/comments/1by5v4u/how_to_get_distant_horizons_working_on_steam_deck/), [r/SteamDeck](https://www.reddit.com/r/SteamDeck/comments/1lak2s5/how_i_got_minecraft_running_with_distant_horizons/). COMMUNITY.
- Black LODs from light-map mods; "Mod Compatibility Warnings" exist at `Advanced → Logging → Warnings` if a known-bad combo is present [P&S + jar lang]. VERIFIED (key exists).
### 7. Recommended starting settings per fleet machine
All values use verified keys; distances assume server VD 8 (vanilla) and server-side pregen so the client isn't starved of LODs. (INFERENCE from the verified mechanics + community profiles above.)
| Setting | (a) Steam Deck (RDNA2 8CU, 1280×800, shared RAM) | (b) P1 GTX 1060 6GB (VRAM-bound) | (c) P2 4C + ~8GB free RAM (heap-bound) |
|---|---|---|---|
| LOD render distance | 96–128 | 128–192 (step up slowly; shaders over 512-chunk LODs is where VRAM dies) | 96–128 (lower if heap spikes) |
| Quality preset | LOW | LOW–MEDIUM (watch VRAM; MEDIUM re-enables transparency+SSAO) | MEDIUM or LOW (quality is render-only cost; cheap to lower) |
| Cave culling | ON (do **not** use HIGH/EXTREME presets without re-enabling) | ON | ON |
| Thread preset | Minimal/Low Impact (leave headroom for Deck's APU) | Low Impact/Balanced | Balanced (4 threads) while playing; Minimal if stutter |
| SSAO / DH anti-aliasing | off (LOW preset) | off unless headroom | default |
| Transparency | Disabled (if no shader) | forced Complete under Iris | forced Complete under Iris |
| Vanilla fade mode | Single pass | Double pass (visual seam vs GPU) | Double pass |
| DH clouds/beacons | off if GPU-bound (Complementary already disables DH clouds) | off | default |
| JVM heap | 6–8 GB `-Xmx` + `-XX:+UseZGC` (wiki guidance) | 6–8 GB | ≤6 GB; prefer cutting LOD distance over heap |
| Generation | server provides LODs; keep `enableDistantGeneration` irrelevant in MP | same | same; keep client DB sync NORMAL |
## Findings — Xaero's Minimap 26.5.0 / World Map 1.46.0
### 8. Where the cost is, and the verified settings that control it
Field names below were extracted from the pinned jars' settings classes (`xaero/common/settings/ModSettings.class`, `xaero/map/settings/ModSettings.class`) — the mods are closed-source but NeoForge builds ship Mojang-mapped symbols. VERIFIED (jar extraction).
| Cost center | Mechanism | Relevant verified settings/keys |
|---|---|---|
| Minimap terrain | Chunk-to-pixel updates as you move; renders into its own FBO (`MinimapFBORenderer`, `bufferSize`) | `minimapSize` (bigger = more fill rate), `zoom`, `caveZoom` |
| Cave mode | Renders below-ground slices; needs roof search + extra depth scanning | `caveMaps` (on/off), `caveMapsDepth`, `caveMapsDisabled`, `manualCaveModeStart`, `autoCaveMode` (WM), per-dimension `defaultCaveModeType`, `legibleCaveMaps` |
| Shading | Extra per-chunk color math | `terrainDepth`, `terrainSlopes`, `biomeBlending`, `blockColours`, `ambientLightColored` |
| Entity radar | Per-frame entity scan + icon rendering (model-based icons are pre-rendered; heavy entity mods = more types) | `entityRadar`, `mainEntityAs`, `dotsStyle`, `smoothDots`, `radarHideInvisibleEntities`, display-names toggles; categories in `xaero/minimap` config |
| Overlays | Extra scans | `displayRedstone`, `displayStainedGlass`, `slimeChunks`, `lightOverlay*` |
| World Map rendering | Full-screen map; **skips world rendering by default while the map is open** — turning that off "significantly reduces your framerate" (official changelog) | world-map-screen "world render skip" option; `openMapAnimation`, `zoomButtons` |
| World Map writing | Client converts explored chunks into region data; "Optimized map writing… less useless chunk iteration" (26.2.0/1.42.0) | `mapWritingDistance` (limit writing distance; option exists since 1.35.0), **"Force Fast Writing"** — official: "Bad for game performance" |
Official changelog anchors: world map 1.42.0 (fast-writing "bad for game performance", world-render skip, optimized map writing), 1.41.2 ("Added a new option… for setting the **maximum number of loaded regions**"), 1.35.0 (map writing distance option), 1.37.2 ("Slightly optimised region unloading"); minimap 26.2.0 ("Optimized map writing"), 25.3.12 (BatchableBufferSource / ImmediatelyFast interplay) — [chocolateminecraft update pages](https://chocolateminecraft.com/update.php?mod_id=2) and [mod_id=0](https://chocolateminecraft.com/update.php?mod_id=0). DOCUMENTED.
### 9. Does world map memory scale with explored area?
- **On disk: yes, unbounded-ish** — regions are saved as `.zip` files per dimension/map under `xaero/world-map/…` (plus separate cache folders the FAQ says can be deleted) [Modrinth FAQ](https://modrinth.com/mod/xaeros-world-map). VERIFIED.
- **In RAM: bounded** — the mod keeps a *region cache* (region-cache fields exist in 1.46.0 bytecode; "Reload Regions"/"Reload All Regions" options manage the cache), region unloading is automatic, and since world map 1.41.2 there is a user setting for the **maximum number of loaded regions** (present in 1.46.0) — so heap use is a function of that cap + zoom level, not of total exploration [changelog 1.41.2](https://chocolateminecraft.com/update.php?mod_id=2). VERIFIED (mechanism) — a third-party benchmark measured roughly flat ~45–85 MB for the pair even with heavy exploration [Oreate guide](https://create.oreateai.com/create/performance-vs-features-the-real-difference-between-journeymap-and-xaeros). COMMUNITY (small blog, directionally consistent with the bounded-cache design).
- Resource pack changes force a full map regeneration (disk churn) [Modrinth description](https://modrinth.com/mod/xaeros-world-map). DOCUMENTED.
### 10. Minimap↔world map integration (perf-positive, keep defaults)
"By default uses the map textures generated by the world map mod instead of generating its own. This improves performance when using both mods" [Minimap description](https://modrinth.com/mod/xaeros-minimap). Also, config moved to `/config/xaero/minimap`, `/config/xaero/world-map`, shared `/config/xaero/lib` (25.3.2/1.40.2 config rework) — the pack can ship enforced defaults per machine there [changelog 25.3.2](https://chocolateminecraft.com/update.php?mod_id=0). VERIFIED. The world map screen's default world-render skipping + auto framerate limit (~625 fps cap) when skipped is another official perf behavior [changelog 1.42.0](https://chocolateminecraft.com/update.php?mod_id=2). VERIFIED.
### 11. Alternatives, briefly
- Community consensus ranks Xaero's as the lightest full-featured pair; JourneyMap is "slightly heavier on system resources" with more RAM/CPU headroom needed; FTB Chunks is often called the most performant but feature-poor and its map/claim data lives in the server world save (server TPS exposure) [r/feedthebeast comparison](https://www.reddit.com/r/feedthebeast/comments/1pnr3bo/for_modded_minecraft_which_minimap_has_more/), [Enigmatica6 issue #1340](https://github.com/EnigmaticaModpacks/Enigmatica6/issues/1340), [Astroworld guide](https://guide.astroworldmc.com/best-minecraft-minimap-mods). COMMUNITY — no first-party benchmarks either way; keep Xaero's.
## Sources
Kept:
- DH GitLab wiki (Server Owners; Problems & Solutions; Mod Support; General FAQ) — primary, maintained by the mod author. https://gitlab.com/distant-horizons-team/distant-horizons/-/wikis/home
- DH core source (Config.java, preset handlers, enums) — authoritative key/behavior definitions. https://gitlab.com/distant-horizons-team/distant-horizons-core
- Pinned jars' embedded lang/setting symbols (DH 3.3.2, XaeroMinimap 26.5.0, XaeroWorldMap 1.46.0) — exact-version config surface. https://modrinth.com/mod/distanthorizons/version/3.3.2-1.21.1
- Iris docs, DH mod-support reference — how shaderpacks integrate DH passes. https://shaders.properties/current/reference/mod-support/distant_horizons/
- Complementary Reimagined + MakeUp Ultra Fast `shaders.properties` — proof of DH support and of disabled DH shadow pass. (GitHub raw links above)
- Xaero's official descriptions + chocolateminecraft changelogs — feature/perf changes per version. https://chocolateminecraft.com/update.php?mod_id=2
- GitLab issues #157 (RAM/VRAM data point), #566 (Alex's Caves fix via wiki) — tracker history.
Dropped:
- distanthorizonsguide.com — useful but third-party SEO-adjacent; kept only its calculator profile as one COMMUNITY data point.
- Sportskeeda/best-settings listicles — no original data, stale version references.
- Steveplays28 shader-compat gist — 1.19.2-era, superseded by per-pack source verification.
- Older Reddit optimization threads (DH 1.18.2 era) — settings names/values changed in 3.x.
## Gaps
- No first-party per-distance VRAM/RAM table for DH 3.3.x exists; the only numbers are 2022-era (issue #157) and community profiles. Benchmark: F3 overlay + RTSS/`radeontop` at 64/128/256 chunks × LOW/MEDIUM with each shaderpack, per machine.
- Actual FPS/VRAM delta of Complementary's DH programs (`dh_terrain`/`dh_water`) vs base renderer, and whether its disabled `dhShadow` fully avoids DH shadow cost (it should by contract, but verify).
- Xaero's minimap cost with heavy entity mods (Alex's Mobs etc.) and with cave mode on — no measurements found; icon pre-rendering burst at first-encounter may stutter on Deck.
- World map "maximum number of loaded regions" default value on 1.46.0 and its heap effect (setting exists; default not visible without running the game).
- Whether the pack's server actually enables `enableRealTimeUpdates`/sync radius ≤ client LOD distance (affects client bandwidth/CPU when riding Create trains).
## Top levers
| # | Lever | Expected impact | Helps | Risk | Confidence |
|---|---|---|---|---|---|
| 1 | Cut DH LOD render distance to 96–128 (Deck/P2) or 128–192 (P1) — keep ≤512 always | High (GPU+VRAM+CPU all scale with it) | All three; most on P1 VRAM & Deck | Visual only | High |
| 2 | Quality preset LOW/MEDIUM **with cave culling re-checked ON** (never leave HIGH/EXTREME presets' culling-off in mountains) | High on GPU-heavy scenes, Med general | Deck, P1 | Slightly flatter distant terrain | High (preset mapping verified) |
| 3 | Server-side `/dh pregen` + keep per-player LOD bandwidth/rate limits (500 KB/s default) so clients stream prebuilt LODs instead of stressing CPU | High (client smoothness), Med (server) | All three; P2 CPU | Server disk/IO during pregen; don't pregen while playing | High |
| 4 | Thread preset: Minimal/Low while exploring fast, Balanced otherwise; keep `pauseIfMovingQuickly`/`limitIfServerUnhealthy` on | Med (stutter elimination on 4C/8T) | P2, Deck | Slower LOD fill-in | High |
| 5 | Heap: `-Xmx6–8G` + `-XX:+UseZGC` (≤6G on P2), keep DH DB sync NORMAL | Med (stutter/GC) | P2 most, Deck (shared RAM) | Lower heap on P2 may thrash if LOD RD kept high | High (official guidance) |
| 6 | Steam Deck: Sodium "Use No Error context" OFF (Mesa fix) + stay on OpenGL rendering engine | Med (crash/stability, some perf) | Deck | None | High (official wiki) |
| 7 | Xaero's: cap world-map "max loaded regions" + keep `mapWritingDistance` default; never enable Force Fast Writing; keep world-render skip ON | Low–Med (heap spikes on P2 when opening map) | P2, Deck | Slower mapping while flying fast | Med |
| 8 | Xaero's minimap: modest `minimapSize`, radar to dots + hide invisible entities, cave mode off above ground for users who don't need it | Low | Deck, P2 | Feature loss (QoL) | Low–Med (mechanism verified, magnitude not) |
| 9 | Keep both shaderpacks' built-in DH integration (dhShadow off) — do not swap in packs without `DISTANT_HORIZONS` support | Prevents catastrophic regressions | All | None (status quo) | High |
| 10 | Pack fixes: Alex's Caves `biome_ambient_light_coloring = false`; pin SSRD ≥1.8.7 with DH 3.3.2 | Prevents broken LODs/rendering | All | AC loses biome tint ambience | High |
## Open questions to verify by benchmark
1. VRAM at 128 vs 192 vs 256 LOD chunks with Complementary Reimagined r5.9.3 on the GTX 1060 6GB — where does the 6 GB budget break?
2. Steam Deck: 30 fps target — LOW preset @128 LOD vs MEDIUM @96 LOD with MakeUp Ultra Fast (which is cheaper per visual quality?).
3. Client thread occupancy (F3 "Show Thread Pools") with thread preset Minimal vs Balanced during flight — does P2's main thread still stutter at 4 DH threads?
4. Heap profile on P2 (8 GB free): peak JVM RSS with ZGC `-Xmx5G` at 128 LOD + world map opened after 2h exploration vs DH SQLite flush behavior.
5. Xaero's: minimap FPS cost with entity radar ON vs OFF in a mob-dense Alex's Caves area; cave-mode ON cost while caving.
6. Bandwidth/latency: server LOD streaming rate when teleporting (Create train) with `realTimeUpdateDistanceRadiusInChunks` at default vs reduced — any client hitching from sync-on-load bursts?
7. Does `enableFastSurfaceGenerator` on the server reduce time-to-filled-horizon for new areas enough to matter for clients joining fresh regions?
@@ -0,0 +1,485 @@
# Research: Entity & Animation Rendering Cost (client-side)
Scope: Minecraft 1.21.1 / NeoForge 21.1.251, Onion MC pack pins. Purely client-side
render/main-thread and heap cost of entity models, animations and dynamic lights.
Tags: **VERIFIED** = read in source/config code, **DOCUMENTED** = official
README/wiki/changelog, **COMMUNITY** = user reports/forums, **INFERENCE** = my reasoning.
---
## Summary
Entity/animation cost in this pack is dominated by three independent CPU-side pipelines
that **do not share configuration**: (1) EMF/CEM custom models driven by Fresh Animations
(`animationLODDistance`, `modelUpdateFrequency`), (2) GeckoLib 4.x models used by Mowzie's,
Fowlplay, dragons, etc. (no user-facing perf config in GeckoLib itself), and (3)
player-animation mods (Better Combat / playerAnimator / Emotecraft) layered on EMF. Only
pipeline (1) is throttled by the EMF knobs most guides mention; the biggest confirmed
per-mob cost multiplier is **Iris shadow-pass re-animation**, which EMF already skips by
default in this version. Dynamic lights (LambDynamicLights 4.8.x) are a separate risk
because each moving light source forces **chunk mesh rebuilds**, which is far more
expensive than animation math. Entity**Culling** (already in pack) and the vanilla
**Entity Distance** slider are the two levers that reduce the *number* of entities hitting
any of these pipelines, and are therefore the highest-leverage, lowest-risk changes.
---
## Findings
### A. EMF 3.x + ETF 7.x with Fresh Animations (+ All Extensions)
1. **EMF exposes exactly five performance-relevant settings.** From `EMFConfig.java`
(repo master = mod_version **3.3.11**; the pack's 3.3.9 is the same config surface):
`animationLODDistance` (int, default **20**, range 0–65), `retainDetailOnLowFps`
(default true), `retainDetailOnLargerMobs` (default true),
`animationFrameSkipDuringIrisShadowPass` (default true), and `modelUpdateFrequency`
(type `ETFConfig.UpdateFrequency`, default `Average`). `asmMaths` ("Compiled animation
math", default true) is the compiled-maths fast path. [VERIFIED]
https://github.com/Traben-0/Entity_Model_Features/blob/master/src/main/java/traben/entity_model_features/config/EMFConfig.java
2. **`animationLODDistance` is frame-skipping keyed to distance from the client player.**
Official tooltip: "Sets the distance at which animations will begin to skip frames,
lower the Level of Detail (LOD) … None = no frame skipping; 16 = will skip 1 frame for
every 16 blocks distance from player. Lower numbers mean more skipped frames and more
performance gain." Default is 20; 0 = the "None" entry on the slider. [DOCUMENTED —
mod lang file, same repo]
https://github.com/Traben-0/Entity_Model_Features/blob/master/src/main/resources/assets/entity_model_features/lang/en_us.json
The setting was introduced in the 1.3 changelog and the size-aware variant
(`retainDetailOnLargerMobs`) in the 2.0 changelog. [DOCUMENTED — CHANGELOG.MD]
https://github.com/Traben-0/Entity_Model_Features/blob/master/CHANGELOG.MD
3. **`retainDetailOnLowFps` and `retainDetailOnLargerMobs` deliberately soften the LOD
effect** (less skipping below 60 fps; less skipping on big mobs). Tooltips: "reduce the
Level of Detail (LOD) impact of the above setting according to the mobs size" / "…
according to your fps. Only applies when below 60 fps." Turning these **off** makes the
LOD slider do more work (more aggressive skipping). [DOCUMENTED — mod lang file]
https://github.com/Traben-0/Entity_Model_Features/blob/master/src/main/resources/assets/entity_model_features/lang/en_us.json
4. **`animationFrameSkipDuringIrisShadowPass` is the single most important EMF option and
is already ON.** Changelog 2.1.1: "added an optimization option that will skip
recalculating entity model animations during the iris shadow pass, on by default, and
**should always stay on**". With shaders, each animated model would otherwise be
animated a second time for the shadow pass. **Verify this is still `true` on all three
fleet machines** — it is the difference between 1× and 2× animation cost per frame.
[VERIFIED — config + DOCUMENTED — changelog]
https://github.com/Traben-0/Entity_Model_Features/blob/master/CHANGELOG.MD
5. **EMF 3.2+ already contains a large animation-speed fix.** Changelog 3.2: "significant
performance improvement for custom animations (**more than doubling frames** when many
animated entities are present)". The pack's EMF 3.3.9 includes this; do not "fix" FA
cost by downgrading. [DOCUMENTED]
https://github.com/Traben-0/Entity_Model_Features/blob/master/CHANGELOG.MD
6. **`modelUpdateFrequency` only controls *model refresh* (health/age/child-state
changes), not per-frame animation.** Tooltip: "Sets how often a mobs model will update
for changes like health & age. Never = never update, Slow = 3-5 seconds, Average =
about 1 second, Fast = less than a second, Instant = instant (possible lag with
hundred of mobs)". Default `Average`. Lowering it to `Slow`/`Never` is a cheap,
low-visual-impact win on busy farms/villager halls. [VERIFIED — config; DOCUMENTED —
lang file]
7. **Per-model opt-out exists** via `modelsNamesDisabled` (a `Set<String>` of
`<name>.jem`), exposed in the GUI as "Enable model" per .jem file. This lets you kill
the most expensive FA models (e.g. villager layers) without dropping FA globally.
[VERIFIED — config]
https://github.com/Traben-0/Entity_Model_Features/blob/master/src/main/java/traben/entity_model_features/config/EMFConfig.java
8. **`allowedCEM` gates which model namespaces EMF touches** (`All` / `Known only (mostly
vanilla/OptiFine)` / `Unknown only (mostly modded)` / `Off`). Setting it to
`Known only` stops EMF from building CEM models for modded mobs — useful if FA-derived
models on modded entities are the hot path. [VERIFIED — config]
9. **Measured Fresh-Animations cost is large and shader-amplified.** On the EMF issue
tracker, a user reported on 1.21.11 + Sodium/Iris: 50 cows on screen = 400+ fps (no
mods) → 110–130 fps with Fresh Animations → **30–40 fps with shaders + FA**; vanilla
animations on the same scene = 300–350 fps / 180–200 fps with shaders. The EMF author
replied the numbers are "more than I'd expect" but that FA "animations are just so
involved compared to vanilla" and you "aren't going to be easily pushing past 200 fps
with fresh animations on ever". A second reporter on 1.20.1 saw 165→40 fps with 30 cows
+ Beefier Bovine x FA + Complementary. The issue was closed as fixed in a later
release. [COMMUNITY — user reports on mod's own tracker; DOCUMENTED — maintainer reply]
https://github.com/Traben-0/Entity_Model_Features/issues/443
10. **Independent corroboration that FA + EMF/ETF drops FPS on villager-heavy scenes:**
easy-villagers #243 "Huge FPS drops with Fresh Animations and the mods needed for it
(EMF/ETF). Seems to be trying to run the animations even tho there's no animation
seen." [COMMUNITY]
https://github.com/henkelmax/easy-villagers/issues/243
11. **EMF recommends EntityCulling specifically for animation-heavy packs.** EMF README:
"Entity Culling … does wonders for reducing entity rendering lag, doing even more than
Sodium's included entity culling. This is very beneficial when using animation heavy
packs like Fresh Animations." [DOCUMENTED — official README]
https://github.com/Traben-0/Entity_Model_Features
12. **ETF: the cost-bearing knobs are the texture-update frequency and emissive/skin
toggles.** From `ETFConfig.java`: `textureUpdateFrequency_V2` (enum `UpdateFrequency`:
`Never` = -1, `Slow` = 80 t, `Average` = 20 t, `Fast` = 5 t, `Instant` = 1; **default
`Fast`**), `enableCustomTextures`, `enableEmissiveTextures`, `enableEmissiveBlockEntities`,
`emissiveRenderMode` (`DULL`/`BRIGHT`), `enableEnchantedTextures`, `enableArmorAndTrims`,
`enableBlinking`, `blinkFrequency` (150), `blinkLength` (1), `skinFeaturesEnabled`,
`skinTransparencyMode`, `use3DSkinLayerPatch`, `alwaysCheckVanillaEmissiveSuffix`,
`advanced_IncreaseCacheSizeModifier` (1.0), `enableFullBodyWardenTextures`,
per-entity override maps (`entityEmissiveOverrides`, `entityRandomOverrides`,
`entityEmissiveBrightOverrides`, `entityRenderLayerOverrides`, `entityLightOverrides`).
`UpdateFrequency` is a **tick delay** for re-evaluating random/variant properties, so
moving `Fast` (5 t, 4 Hz per entity) → `Average` (20 t, 1 Hz) cuts per-entity texture
bookkeeping 4×. [VERIFIED — source]
https://github.com/Traben-0/Entity_Texture_Features/blob/ETF-Main/src/main/java/traben/entity_texture_features/config/ETFConfig.java
13. **ETF rates misbehaving packs as a lag source.** README FAQ: "If you are experiencing
lag please check your most recent log file to see if any errors are being logged,
sometimes an incorrectly written properties file or missing textures may lag the
system." Relevant because FA All Extensions ships many `.properties` files — a single
mis-pathed texture can multiply cost. [DOCUMENTED]
https://github.com/Traben-0/Entity_Texture_Features/blob/ETF-Main/.github/README.md
14. **ETF emissive conflicts with Complementary shaders.** ETF FAQ: "Complimentary shaders
has its own in-built emissive textures that usually work fine but will conflict with
some resource packs. You can either disable Complimentary's emissives or ETF's
emissives to fix this." Relevant because the pack runs Complementary Reimagined r5.9.3.
[DOCUMENTED]
https://github.com/Traben-0/Entity_Texture_Features/blob/ETF-Main/.github/README.md
15. **Known pack-relevant interaction: ImmediatelyFast 1.6.14 vs FA All Extensions.**
ImmediatelyFast issue #180 (still open, last comment Dec 2025): ImmediatelyFast breaks
the custom models of Fresh Animations Extensions. The reporter's working set was
ETF + EMF + Embeddium(+extra) + FreshAnimations + FA+All_Extensions; adding
ImmediatelyFast broke entity rendering. Maintainer: "I added this as a low priority
issue to my todo list." The pack carries ImmediatelyFast 1.6.14 + FA All Extensions
1.8.1 — worth an A/B on entity visuals/cost. [COMMUNITY/DOCUMENTED — mod tracker]
https://github.com/RaphiMC/ImmediatelyFast/issues/180
16. **FA All Extensions is a bundle of official sub-packs** (details, creeper/spider
edits, emissive add-on, quivers, classic horses), i.e. it *adds* cost on top of base
FA rather than being neutral. [DOCUMENTED — mod description]
https://www.curseforge.com/minecraft/texture-packs/fresh-animations-extensions
### B. GeckoLib 4.x (Mowzie's, Fowlplay, dragons, Doggy Talents, etc.)
17. **GeckoLib is a library, and the pack's GeckoLib 4.9.3 has no end-user performance
config.** The official README documents no client perf options — its `wiki.geckolib.com`
is modder documentation (controllers, molang, render layers). Per-entity animation
cost is therefore set by each *consumer mod*, not by a config the pack can tune.
[VERIFIED — README has no config section]
https://github.com/bernie-g/geckolib
18. **Mowzie's Mobs genuinely requires GeckoLib 4.9.3 on 1.21.1 NeoForge** — reproduced in
a 1.21.1/NeoForge 21.1.248 + Mowzie's 1.8.2 + GeckoLib 4.9.3 report. So the pack's
GeckoLib load is not optional. [COMMUNITY/DOCUMENTED — mod repo issue]
https://github.com/BobMowzie/MowziesMobs-Public/issues/64
19. **Third-party GeckoLib optimizers exist and are directly relevant to 1.21.1 NeoForge.**
"GeckoLib Animation Optimizer" requires GeckoLib 4.9.0+ on 1.21.1 NeoForge; its
description claims configurable distance-based animation optimisation and camera
visibility culling enabled by default, block-occlusion culling and player optimisation
disabled by default, "fully client-side". "GeckolibBetterFPS" claims ~"25% faster
rendering of GeckoLib entities" and "reduces micro stutters when multiple GeckoLib
entities are present", but it is a MoePus mod historically targeted at 1.20.1 — check
1.21.1 build availability before proposing. [COMMUNITY — mod listings; treat the
percentages as vendor claims, not verified]
https://www.curseforge.com/minecraft/mc-mods/geckolib-animation-optimizer ,
https://modrinth.com/project/CZyWdBUo
20. **Important negative finding: EMF's animation LOD does NOT throttle GeckoLib.**
EMF only handles OptiFine CEM `.jem` models; GeckoLib renders through its own pipeline.
**INFERENCE**: the `animationLODDistance` lever benefits vanilla-mob/FA content only;
for GeckoLib-heavy scenes the only EMF-independent levers are EntityCulling, the
Entity Distance slider, and reducing the number of GeckoLib entities (spawn/mob count).
21. **Citadel (Alex's Mobs / Alex's Caves) is a different library from GeckoLib.** The
widely-reported Alex's Mobs perf complaints are about **server/world-gen and chunk
loading**, not client rendering — Citadel issue #156 is titled "Alex's Mobs X Citadel:
Slow Chunk loading", and r/feedthebeast discussion is about worldgen/chunky comparison.
A separate community "Alex's Mobs: Tweaks [Performance Optimization]" (AMT) addon
claims to "fix several expensive Alex's Mobs and Citadel hot paths". Treat client-side
Citadel cost as driven mainly by entity *count* and its own model complexity.
[COMMUNITY]
https://github.com/AlexModGuy/Citadel/issues/156 ,
https://www.curseforge.com/minecraft/mc-mods/amt
### C. 3D Skin Layers (1.11.3)
22. **3D Skin Layers already has a built-in distance LOD.** README: "The mod automatically
switches back to vanilla-style 2D rendering when players are more than **12 blocks
away**". The config option is "Level Of Detail Distance" — "The distance at which the
model will switch to a lower level of detail (vanilla 2d)". [DOCUMENTED — README;
VERIFIED — lang file]
https://github.com/tr7zw/3d-Skin-Layers ,
https://github.com/tr7zw/3d-Skin-Layers/blob/main/src/main/resources/assets/skinlayers3d/lang/en_us.json
23. **Per-limb and per-feature toggles let you buy cost back cheaply.** Lang keys observed:
"3D Hat", "3D Jacket", "3D Left/Right Sleeve", "3D Left/Right Pants", "3D Skulls",
"3D Skull Items", plus cost/quality knobs "Voxel Size", "Head Voxel Size", "Body Voxel
Height", "Torso Voxel Width", "Skull Voxel Size", "First Person Voxel Size", "Fast
Render" ("render the 3D models as fast as possible, this may cause some graphical
glitches with transparency"). [VERIFIED — lang file]
https://github.com/tr7zw/3d-Skin-Layers/blob/main/src/main/resources/assets/skinlayers3d/lang/en_us.json
24. **3D Skin Layers is explicitly integrated with EMF and with playerAnimator.**
Repo contains `EMFModelPartMixin.java`; README lists Player Animator as supported
("This means mods that use it like Better Combat and Emotecraft"). ETF in turn exposes
`use3DSkinLayerPatch` which is only shown when `ETF.SKIN_LAYERS_DETECTED`. So EMF/ETF,
3D Skin Layers and playerAnimator form one chained render path on players.
[VERIFIED — source files; DOCUMENTED — README]
https://github.com/tr7zw/3d-Skin-Layers/blob/main/src/main/java/dev/tr7zw/skinlayers/mixin/EMFModelPartMixin.java
25. **Compat/quality switches that trade correctness for cost:** "Compatibility Mode",
"Iris Compatibility Mode" ("fixes weird shadows/colors when using Iris shaders … Will
break the corner connections"), "Sodium Workaround". Only enable as needed. [VERIFIED —
lang file]
26. **Cost is bounded by player count, not mob count.** 3D Skin Layers only affects player
models/heads. On a 10-player server, worst case is 9 remote players within 12 blocks.
[INFERENCE]
### D. Player-animation stack: Better Combat + playerAnimator + Emotecraft
27. **Better Combat requires playerAnimator, and its animation code runs on *every*
player entity, not just yourself.** Its client package contains
`PlayerAttackAnimatable` and `CustomAnimationPlayer` under
`common/…/client/animation/`, i.e. it is a `Player`-model mixin path, and Better Combat
synchronises attacks so remote players' swings animate too (community reports of
multiplayer animation bugs confirm remote-player animation is rendered).
[VERIFIED — repo file tree; COMMUNITY — multiplayer reports]
https://github.com/ZsoltMolnarrr/BetterCombat/tree/1.21.1/common/src/main/java/net/bettercombat/client/animation
28. **Better Combat client-side cost knobs are few but real.** `ClientConfig.java` fields
include `isShowingWeaponTrails` (**default true** — the slash/trail particle system in
`client/particle/TrailParticles.java` and `SlashParticleUtil.java`),
`isSweepingParticleEnabled` (default false), `isShowingArmsInFirstPerson` (false),
`firstPersonAnimations` (TriStateAuto.AUTO), `legAnimationThreshold` (0). Turning off
weapon trails removes a per-swing particle + geometry cost without touching animation
math. [VERIFIED — source]
https://github.com/ZsoltMolnarrr/BetterCombat/blob/1.21.1/common/src/main/java/net/bettercombat/config/ClientConfig.java
29. **There is a first-class EMF ⇄ playerAnimator conflict, and a purpose-built fix.**
"Entity Player Compat" (MIT, client-side, published for **1.21.1 NeoForge/Fabric**,
requires EMF + PlayerAnimator) exists because EMF's custom player models and
playerAnimator both write player part transforms. It does per-part blending
("Automatically detects which specific body parts … are being animated by PlayerAnimator
and only pauses EMF animations for the specific parts currently in use"), a "virtual
follow" limb re-parenting system, and claims "high-performance tick-based caching …
does its work in a handful of hooks per frame." It explicitly lists Better Combat,
FA: Player Extension and GeckoLib/AzureLib armors as tested. [DOCUMENTED — mod page]
https://modrinth.com/mod/entity-player-compat
**INFERENCE**: without it, EMF will animate the player model *and* playerAnimator will
animate it again each frame — double work on every visible player, the exact class of
waste `animationFrameSkipDuringIrisShadowPass` was built to avoid. This is a strong
candidate lever for the 10-player use case.
30. **Emotecraft is built on playerAnimator and is client-side-required.** Emotecraft needs
the client half; the server half is optional, and features stay inactive without a
sync path. Emote animation is transient (only while an emote plays), so steady-state
cost is low; the risk is many simultaneous emotes near the player. [DOCUMENTED —
Emotecraft/playerAnimator listings]
https://minecrafthub.io/mods/emotecraft , https://minecrafthub.io/mods/playeranimator
31. **EMF has explicit Emotecraft integration** — changelog 3.0.7 added
`registerPauseCondition(Function<EMFEntity,Boolean> shouldPause)` to
`EMFAnimationApi` specifically so mods like EmoteCraft stop mixing in, and 3.3.4
"updated emote craft emote pausing support in EMF". So EMF+Emotecraft is a supported
path; the pack's EMF 3.3.9 is new enough. [DOCUMENTED — CHANGELOG.MD]
https://github.com/Traben-0/Entity_Model_Features/blob/master/CHANGELOG.MD
### E. LambDynamicLights 4.8.x on Sodium
32. **Dynamic lights cost is chunk rebuilds, not per-frame shading.** Official
"How does it work": "To apply the dynamic lighting we have to request the affected
chunks a rebuild to rebuild the lightmap, **which is the most critical part
performance-wise**. Rebuilding a chunk cost performances, that's why there's multiple
mode: FASTEST, FAST and FANCY. The fast modes will limit the update." The mod computes
dynamic light over a radius of 7.75 blocks, "which is still a lot" of touched chunks,
and hooks `WorldRenderer#setupTerrain` (or its Sodium equivalent) to schedule rebuilds.
[DOCUMENTED — official doc]
https://github.com/LambdAurora/LambDynamicLights/blob/1.21/HOW_DOES_IT_WORK.md
33. **Exact 1.21-branch config keys (this branch is `@version 4.8.0`, matching pack 4.8.11),
config file `config/lambdynlights.toml`:** `mode` (enum `off`/`fastest`/`fast`/`fancy`,
**default `fancy`**), `chunk_rebuild_scheduler` (`culling` **default** / `immediate`),
`adaptive_ticking.slow` (**5** chunks), `adaptive_ticking.slower` (**8** chunks),
`adaptive_ticking.background_sleep` (**true**), `light_sources.entities` (true),
`light_sources.self` (true), `light_sources.water_sensitive_check` (true),
`light_sources.beam` (true), `light_sources.guardian_laser` (true),
`light_sources.sonic_boom` (true), `light_sources.glowing_effect` (true),
`light_sources.creeper` (`simple`), `light_sources.tnt` (`off`), plus `debug.*`.
[VERIFIED — source]
https://github.com/LambdAurora/LambDynamicLights/blob/1.21/src/main/java/dev/lambdaurora/lambdynlights/DynamicLightsConfig.java
34. **The quality mode maps to a tick mode, not to radius.** `DynamicLightsMode` in the
1.21 branch: `FANCY → TickMode.REAL_TIME`, `FAST → TickMode.SLOW`,
`FASTEST → TickMode.SLOWER`, `OFF → REAL_TIME` (disabled). So `fastest`/`fast` throttle
**how often light updates are applied** (fewer chunk rebuilds/second), which is the
correct lever for stutter, and `fancy` gives real-time smoothness at full rebuild cost.
[VERIFIED — source]
https://github.com/LambdAurora/LambDynamicLights/blob/1.21/src/main/java/dev/lambdaurora/lambdynlights/DynamicLightsMode.java
35. **The 4.8.x line added two dedicated cost mitigations vs older guides.** `chunk_rebuild_scheduler`
(`culling` default vs `immediate`) and the adaptive-ticking distance pair
(`adaptive_ticking.slow` / `.slower`, in **chunks**) plus `background_sleep` under
`adaptive_ticking`. These are the 1.21-branch additions that the widely-copy-pasted
"Light Range / updateInterval / entitiesGlowing" advice (which cites keys that do not
exist in the 1.21 branch source) does not mention. [VERIFIED — source vs the third-party
guide] https://lambdynamiclights.com/configuring-lambdynamiclights-for-peak-performance/
36. **Per-category light-source toggles map to real feature costs.**
`light_sources.entities=false` removes all burning-mob/glow-squid/entity-carried-light
sources (the biggest moving-light population in a survival world);
`light_sources.self=false` removes the first-person player light;
`light_sources.beam`/`guardian_laser`/`sonic_boom`/`glowing_effect` cover beacon beams,
guardian beams, sonic booms and the Glowing effect. Disabling `entities` while keeping
`self` is the usual "I still want my torch to glow, but not 200 burning zombies"
compromise. [VERIFIED — source]
https://github.com/LambdAurora/LambDynamicLights/blob/1.21/src/main/java/dev/lambdaurora/lambdynlights/DynamicLightsConfig.java
37. **Sodium interop is explicit, not accidental.** `HOW_DOES_IT_WORK.md`: "we inject at
`WorldRenderer#setupTerrain` (`WorldRenderer#render` at `WorldRenderer#setupTerrain`
invoke **if Sodium is present**)". So LDD 4.8.x is Sodium-aware by design; the classic
"dynamic lights cause chunk rebuild stutter on Sodium" claim needs to be re-measured
on this version rather than assumed. [DOCUMENTED]
https://github.com/LambdAurora/LambDynamicLights/blob/1.21/HOW_DOES_IT_WORK.md
### F. EntityCulling (1.11.2) interplay
38. **Independently useful alongside Sodium's own culling.** README: "I have 'Use Entity
Culling' enabled in Sodium — does this still help? Yes! … Sodium performs basic
visibility checks based on loaded chunks, this mod goes further. It analyzes the
actual line-of-sight visibility, skipping entities that are within visible chunks but
not actually visible to the player." [DOCUMENTED — official README]
https://github.com/tr7zw/EntityCulling
39. **It has a client-side entity *tick* culling feature, not just render culling.**
`Config.java` (config version 9): `tickCulling` (**true**), with a separate
`tickCullingWhitelist`. This reduces main-thread entity tick cost — relevant to P2's
main-thread bottleneck. [VERIFIED — source]
https://github.com/tr7zw/EntityCulling/blob/main/EntityCulling-Versionless/src/main/java/dev/tr7zw/entityculling/versionless/Config.java
40. **Exact config keys/defaults** (`config/entityculling.json`): `tracingDistance` **128**
(path-trace range), `sleepDelay` **10**, `hitboxLimit` **50**, `captureRate` **5**,
`tickCulling` **true**, `skipEntityCulling` false, `skipBlockEntityCulling` false,
`blockEntityFrustumCulling` **true**, `forceDisplayCulling` false, `solidLeaves`
**false** ("treat leaves as solid blocks … help to increase the performance in
forested areas" — directly relevant with Better Leaves 9.6 in pack), `safeMode` true,
`renderNametagsThroughWalls` true, plus `entityWhitelist` / `blockEntityWhitelist` /
`tickCullingWhitelist`. [VERIFIED — source; DOCUMENTED — README for `solidLeaves`]
41. **Known interaction: client-side/mod "display" entities and far-bounds block entities
need whitelisting** — "Client-side entities, commonly used by magic mods for
animations, may not behave as expected. Whitelist the relevant entities via the config
screen for tick culling and/or entity culling … You'll also need to whitelist block
entities that render well beyond their normal bounds. Examples include the vanilla
beacon, pulleys from Create…". **INFERENCE**: relevant to this pack's Create/Flywheel
contraptions and Veil-based mods; verify no visual regressions before recommending any
change. [DOCUMENTED — README]
42. **Mild CPU trade-off on very weak CPUs.** README: "Consider [disabling] when playing on
a really old computer … When the CPU is already maxed out, there is no spare CPU time
to run the visibility calculations." None of the fleet's CPUs are in that class, but
the Steam Deck's shared power budget makes this worth watching. [DOCUMENTED — README]
43. **Author-provided test reference:** the README's comparison screenshot was captured
with "Sodium, Iris and ImmediatelyFast … alongside EntityCulling" — i.e. exactly the
pack's stack. [DOCUMENTED — README]
44. **Also provide a F3-adjacent debug toggle.** "In the controls menu, you can bind a
debug key to toggle the mod on and off on the fly" — this is the cleanest in-situ
benchmark control for entity cost. [DOCUMENTED — README]
### G. Entity-count drivers and the Entity Distance slider
45. **Clumps is a server-side aggregation mod, so its client benefit is indirect but real.**
"Clumps groups XP orbs together into a single entity to reduce lag when there are many
in a small area." On 1.17+ it is a **server** mod: "For a multiplayer world on 1.17 or
newer, the jar goes on the server rather than on each player's client." Fewer orb
entities on the server → fewer orb entities synchronised → less client render/tick and
(with LDD) fewer dynamic-light sources. Verify it is installed server-side.
[DOCUMENTED — mod listings]
https://modrinth.com/mod/clumps/versions , https://minecrafthub.io/mods/clumps
46. **The vanilla "Entity Distance" video setting is the bluntest client lever.** The
Minecraft Wiki's simulation-distance article states the client does not render entities
based only on Render Distance but on "both 'Render distance' and 'Entity distance'
video settings"; it is a percentage slider scaling entity render distance.
[DOCUMENTED — Minecraft Wiki]
https://minecraft.wiki/w/Simulation_distance
*(A community write-up explicitly frames it: "This setting directly impacts client-side
performance … Setting the rendering distance to its maximum allows all entities to be
visually displayed at the full extent of their tracking distance.")* [COMMUNITY]
https://www.curseforge.com/minecraft/mc-mods/entity-distance-1-12-2
47. **Item entities are the other unbounded count driver.** No mod in the pack aggregates or
culls dropped items; a farm/hopper overflow or an explosion drops hundreds of
`ItemEntity`s, each costing an `ItemRenderer` submission and (with Better Leaves +
FA) potentially full model evaluation for held-block mobs. [INFERENCE]
48. **EMF/ETF+FA cost scales with *visible* model count, which EntityCulling and Entity
Distance both reduce.** Since `animationLODDistance` skips frames as a function of
distance from the player, `Entity Distance` and the LOD slider are multiplicative:
shrinking entity distance removes far models entirely, and the LOD slider makes the
remaining middle-distance models cheaper. [INFERENCE]
---
## Sources
Kept:
- EMF `EMFConfig.java` (master, v3.3.11) — authoritative list of every EMF config key and
defaults. https://github.com/Traben-0/Entity_Model_Features/blob/master/src/main/java/traben/entity_model_features/config/EMFConfig.java
- EMF `lang/en_us.json` — exact tooltip wording for `lod`, `low_fps_lod`, `large_mob_lod`,
`update`, `asmmaths`. https://github.com/Traben-0/Entity_Model_Features/blob/master/src/main/resources/assets/entity_model_features/lang/en_us.json
- EMF `CHANGELOG.MD` — 3.2 "doubling frames", 2.1.1 Iris shadow skip, 1.3/2.0 LOD
history, EmoteCraft integration. https://github.com/Traben-0/Entity_Model_Features/blob/master/CHANGELOG.MD
- EMF README — FA confirmed supported; EntityCulling recommendation.
https://github.com/Traben-0/Entity_Model_Features
- EMF issue #443 — quantified FA/shader cost, maintainer response.
https://github.com/Traben-0/Entity_Model_Features/issues/443
- ETF `ETFConfig.java` — all ETF keys incl. `textureUpdateFrequency_V2` enum ticks.
https://github.com/Traben-0/Entity_Texture_Features/blob/ETF-Main/src/main/java/traben/entity_texture_features/config/ETFConfig.java
- ETF README — emissive/Complementary conflict, "lagging" FAQ.
https://github.com/Traben-0/Entity_Texture_Features/blob/ETF-Main/.github/README.md
- LambDynamicLights `HOW_DOES_IT_WORK.md`, `DynamicLightsConfig.java`,
`DynamicLightsMode.java` (1.21 branch = v4.8.0, matching pack 4.8.11) — chunk-rebuild
cost model and exact keys/enums.
https://github.com/LambdAurora/LambDynamicLights/blob/1.21/HOW_DOES_IT_WORK.md
- EntityCulling `Config.java` + README — exact keys/defaults, tick culling, `solidLeaves`,
whitelist caveats. https://github.com/tr7zw/EntityCulling/blob/main/EntityCulling-Versionless/src/main/java/dev/tr7zw/entityculling/versionless/Config.java
- 3D Skin Layers README + `lang/en_us.json` + repo tree — 12-block LOD default, per-limb
toggles, EMF/playerAnimator integration.
https://github.com/tr7zw/3d-Skin-Layers/blob/main/src/main/resources/assets/skinlayers3d/lang/en_us.json
- Better Combat `ClientConfig.java` + client package tree — weapon trails default, animation
is per-Player.
https://github.com/ZsoltMolnarrr/BetterCombat/blob/1.21.1/common/src/main/java/net/bettercombat/config/ClientConfig.java
- Entity Player Compat mod page — EMF⇄playerAnimator conflict and its fix, 1.21.1 NeoForge.
https://modrinth.com/mod/entity-player-compat
- GeckoLib README — confirms absence of user perf config.
https://github.com/bernie-g/geckolib
- Minecraft Wiki (simulation distance) — Entity Distance affects client entity rendering.
https://minecraft.wiki/w/Simulation_distance
Dropped:
- `bravith.com`, `mythiq.net`, `skinmc.net`, `9minecraft`, `modbay`, `minecrafthub`,
`modrinth.com/mod/geckolib-animation-optimizer` (404) — SEO/aggregator pages; superseded
by the GitHub source I read directly. Used only where no primary source exists (GeckoLib
optimizer, Clumps, Emotecraft) and flagged as vendor/community claims.
- `lambdynamiclights.com/configuring-…` — cites config keys (`updateInterval`,
`lightSourceRenderRange`, `entitiesGlowing`) that **do not exist** in the 1.21-branch
source; treated as unreliable, kept only as an example of bad advice.
- Reddit threads on FA cost — replaced by the two mod-tracker issues which are primary and
quantified.
- ImmediatelyFast issue #180 — kept as a *caveat* only, not as an entity-cost source.
---
## Top levers (ranked)
| # | Lever | Expected impact | Fleet machine | Risk | Confidence |
|---|---|---|---|---|---|
| 1 | Confirm `animationFrameSkipDuringIrisShadowPass=true` in EMF on all 3 machines | **High** when shaders on (halves animation work) | P0, P2 (also P1) | None (default; wrong value is the bug) | High — VERIFIED default + maintainer "should always stay on" |
| 2 | Install **Entity Player Compat** (EMF+playerAnimator bridge) for the Better Combat / Emotecraft / FA-Player path | **High** on player-dense scenes; removes double animation of every visible player | P0, P2 | Med — third-party mod, unofficial fork; test for arm/armor desync | Med — DOCUMENTED mod page; mechanism plausible but percentage unverified |
| 3 | Lower EMF `animationLODDistance` 20 → 8–12 | **Med–High** in mob/animal areas | P0, P2 (CPU-bound); P1 mild | Low — visible low-framerate animations up close | High — VERIFIED semantics; magnitude needs A/B |
| 4 | Iris shader choice: shaders + FA is the dominant cost (issue #443: 110–130 → 30–40 fps) | **High** | P0 (RDNA2 iGPU) most, P1 (VRAM) too | Med — visual (loss of shaders on Deck) | High — consistent across two independent reports |
| 5 | LambDynamicLights `mode` `fancy` → `fast`/`fastest` on the CPU-bound boxes | **Med–High** where fires/burning mobs/held torches are common | P2 (main-thread bound), P0 | Low — slightly less responsive light | High — VERIFIED tick-mode mapping |
| 6 | LambDynamicLights `light_sources.entities=false` (keep `light_sources.self=true`) | **Med** in busy survival areas | P2, P0 | Low–Med (losing burning-mob glow) | High — VERIFIED key exists |
| 7 | EMF `modelUpdateFrequency` `average` → `slow`/`never` | **Med** in villager halls/farms | P0, P2 | Low | Med–High — VERIFIED key; magnitude unmeasured |
| 8 | Vanilla **Entity Distance** slider 100% → 75–80% | **Med** everywhere, scales all pipelines | All three, esp. P0 | Low | Med — documented mechanism, pack-specific magnitude unknown |
| 9 | ETF `textureUpdateFrequency_V2` `fast`(5t) → `average`(20t) | **Med** with random/variant packs | P0, P2 | Low — slower variant swaps | Med–High — VERIFIED ticks |
| 10 | GeckoLib Animation Optimizer (needs GeckoLib ≥4.9.0) | **Med** if GeckoLib mobs are common | P0, P2 | Med–High — third-party, can break animations | Low — vendor claim only, no independent test |
| 11 | Better Combat `isShowingWeaponTrails=false` | **Low–Med** | P2, P0 | Low (visual) | Med — VERIFIED key/default; magnitude small |
| 12 | Reduce 3D Skin Layers "Level Of Detail Distance" below 12 / disable 3D Skulls | **Low** (≤9 players, tightly bounded) | P1 VRAM-mostly | Low | Low–Med |
| 13 | EntityCulling `solidLeaves=true` in forested bases (Better Leaves present) | **Low–Med** in forests | All | Med — leaves stop occluding incorrectly / leaves may hide entities visually | Med — DOCUMENTED option, no pack-specific measurement |
| 14 | Verify Clumps is deployed **server-side** | **Low–Med** on XP-heavy scenes | All (indirect) | Low | Med — DOCUMENTED placement requirement |
---
## Open questions to verify by benchmark
1. **Is `animationFrameSkipDuringIrisShadowPass` actually `true` in the shipped `emf.json`/config?**
Cheapest possible check; if a pack preset or copy flipped it, every shader scene is
paying 2× animation.
2. **What is the real fps delta from EMF `animationLODDistance` 20 vs 8 vs 0-on vs LOD-off**
on a fixed FA mob cluster (e.g. 30–50 animals) on P0 and P2? Issue #443 gives the
magnitude for the FA/shaders *combination* but not for the LOD slider alone.
3. **Does Entity Player Compat measurably help on the 10-player server**, and does it break
FA: Player Extension or armor alignment? Requires ≥2 players within view, attacking.
4. **LambDynamicLights `fancy` vs `fast` chunk-rebuild count** — use the mod's own
`debug.display_dynamic_lighting_chunk_rebuild` and `debug.active_dynamic_lighting_cells`
options (both exist as keys) to count rebuilds per second, then correlate with 1% lows.
Also test whether `chunk_rebuild_scheduler=culling` is genuinely better than `immediate`
on Sodium 0.8.13.
5. **Do the LambDynamicLights `adaptive_ticking.slow`/`.slower` distances (5 / 8 chunks)
default interact badly with shaders?** Not covered by the mod docs beyond the tick-mode
description.
6. **GeckoLib entity share:** measure with a profiler how much main-thread time GeckoLib
models occupy on P2 (Mowzie's boss fights, Fowlplay birds, dragons). If it is <2 %,
GeckoLib optimizers are not worth the risk.
7. **ImmediatelyFast 1.6.14 vs FA All Extensions 1.8.1 (issue #180)** — does the pack
exhibit it? A/B entity visuals with ImmediatelyFast disabled.
8. **ETF emissive vs Complementary Reimagined built-in emissives** — which to disable, and
does disabling one measurably reduce frame time or just fix z-fighting?
9. **`solidLeaves` under Better Leaves 9.6** — measure in forested areas and inspect for
entities disappearing behind custom leaves.
10. **Entity Distance slider calibration** — find the % where mobs stop being visibly
affected at the pack's server view-distance 8 with Distant Horizons mandatory.
11. **Heap/GC effect on P2 (only ~8 GB free):** quantify the RAM cost of FA + FA All
Extensions + Best Leaves textures and EMF's model cache; test whether ETF
`advanced_IncreaseCacheSizeModifier` changes GC pause frequency (it trades RAM for CPU,
likely the wrong direction on P2).
@@ -0,0 +1,194 @@
# Research: Effects / audio / HUD & GUI / "flavour" client mods — frame time, tick time, RAM/VRAM, startup cost (MC 1.21.1 NeoForge, Onion MC)
## Summary
Most of this category is cheap at idle: the mods with real, recurring cost are **Sound Physics Remastered** (CPU raytracing per sound, + a cloned-chunk cache for RAM), **WATUT** (network + CPU rendering other players' GUIs, with a *known dynamic-GUI breakage when Twilight Forest is installed*), **Twilight Forest's client effects** (Aurora shader, cloud precipitation — both have config toggles), **Alex's Caves' biome fog/lighting overrides** (documented "may conflict with shaders"; community key `biome_ambient_light_coloring` to disable), **Perception's chromatic-aberration post pass** (fires on explosions/warden sounds) and **Immersion Paintings/Exposure** (texture/VRAM + capture-time hitches). Everything else (tooltips, plaques, titles, Ping Wheel, Sooty Chimneys, Inventory Particles, Controlify) is event-driven HUD/particle work that only costs when visible — keep, don't tune. Fusion + Athena both being loaded means **two CTM/model engines** initialized at startup; at least one is a candidate for removal depending on which resourcepacks the pack actually ships.
## Findings
### 1. Sound Physics Remastered 1.5.1 — the biggest CPU lever in this category (VERIFIED, config source)
All keys below read from `SoundPhysicsConfig.java` on the repo's `1.21.1` branch (same file/keys as the pinned 1.5.1 line):
[SoundPhysicsConfig.java (branch 1.21.1)](https://github.com/henkelmax/sound-physics-remastered/blob/1.21.1/common/src/main/java/com/sonicether/soundphysics/config/SoundPhysicsConfig.java)
| Key (file `sound-physics-remastered.toml`) | Default | Note |
|---|---|---|
| `environment_evaluation_ray_count` | 32 (range 8–64) | Config comment: *"Decrease this value if you experience lag spikes when sounds play"* |
| `environment_evaluation_ray_bounces` | 4 (range 2–64) | Same comment — reverb bounces per sound |
| `max_occlusion_rays` | 16 (range 1–128) | occlusion ray count per sound |
| `strict_occlusion` | false | if true, occlusion uses **1 path instead of 9** — big CPU cut, slight accuracy loss |
| `sound_direction_evaluation` | true | reflection-based stereo redirection |
| `evaluate_ambient_sounds` | false | cave/nether/underwater ambients are NOT processed by default (good for the AmbientSounds stack) |
| `update_moving_sounds` / `sound_update_interval` | false / 5 | re-evaluation of discs/long sounds; interval comment: "Lower values ... more lag" |
| `max_sound_processing_distance` | 512 | sounds beyond this are not processed |
| `unsafe_level_access` | false | if true, disables the **level clone & cache** (falls back to main-thread access — slower but no clone memory) |
| `level_clone_range` / `level_clone_max_retain_ticks` / `level_clone_max_retain_block_distance` | 4 / 20 / 16 | size and lifetime of the cloned-chunk cache (RAM lever) |
- There is also a **per-sound rate config** (a generated properties file listing every registered sound event). Verified from `SoundRateConfig.java`: values are *"Max sounds per tick. Set to '-1' for unlimited, '0' to disable sound physics for that sound, >=1 to cap"* with the stated purpose *"can help prevent lag when some mod or mechanism produces hundreds of sounds per tick"*; rain, rain-above, thunder and goat horns are disabled (`0`) by default. [SoundRateConfig.java](https://github.com/henkelmax/sound-physics-remastered/blob/1.21.1/common/src/main/java/com/sonicether/soundphysics/config/SoundRateConfig.java) — this is the right lever for noisy Create/mob-farm sounds.
- Cost model: per-sound CPU raytracing (threaded, `TaskProfiler`/`performance_logging` exists to measure it — VERIFIED key `performance_logging`). Cost scales with number of simultaneous sounds, so a Create factory floor or mob farm is the worst case. [SoundPhysics.java / profiling package](https://github.com/henkelmax/sound-physics-remastered/tree/1.21.1/common/src/main/java/com/sonicether/soundphysics)
- Fork README documents a 10× sound-processing improvement over original Sound Physics (context for "it's already optimized"). [README](https://github.com/henkelmax/sound-physics-remastered#changes-to-the-original-mod)
- **Recommendation: keep + tune.** Lower `environment_evaluation_ray_count` → 16 and `ray_bounces` → 2–3, enable `strict_occlusion`, and cap the noisiest modded sounds in the sound-rates file. Helps P2 (CPU) and P0 (4C Zen2) most. Low risk, easily reversible.
### 2. AmbientSounds 6.3.8 (CreativeMD) — audio streaming & mixing, client-side (DOCUMENTED)
- Client-side mod, requires CreativeCore; config via `/cmdclientconfig`; `/ambient-debug` shows which sounds are playing. [Modrinth](https://modrinth.com/mod/ambientsounds)
- Changelog shows features are per-biome/dimension "engines" with spawn conditions (e.g., "silent dimension configuration", min-count fixes for grass/ice/rock/sand features). [CurseForge changelog](https://www.curseforge.com/minecraft/mc-mods/ambientsounds/files/6400880)
- Cost: memory for its sound library/streamed buffers + audio mixing; essentially no render cost. Community reports of lag are rare/anecdotal (COMMUNITY).
- **Recommendation: keep.** If RAM-tight on P2, reducing played sound density via its config is the lever (no specific key verified — do not guess; check `/cmdclientconfig` in-game).
### 3. Subtle Effects 1.14.3 — particle floods, but ships its own mitigations (DOCUMENTED)
- "All features can be disabled in the config menu"; documented config sections: `Entities/Dust Clouds` (dust clouds) and `Items/Item Rarity` → `Particle Display Type` = `OFF` (item rarity dots). [Modrinth FAQ](https://modrinth.com/mod/subtle-effects) / [Wiki Home](https://github.com/MincraftEinstein/SubtleEffects/wiki)
- Built-in **particle culling and particle render distance** (Modrinth description) — the mod already culls its own particles. [Modrinth](https://modrinth.com/mod/subtle-effects)
- Feature list (fireflies, pollen, sculk dust, slimes/slime trails, spark sources on torches/campfires/furnaces, replacing vanilla smoke/steam) is large but each is individually toggleable: [Wiki: Particles](https://github.com/MincraftEinstein/SubtleEffects/wiki/Particles)
- Known incompatibility to watch: fires off `Particles` video setting (features silently stop if particle setting = minimal). Coexists with other particle mods per FAQ. [Modrinth FAQ](https://modrinth.com/mod/subtle-effects)
- **Recommendation: keep + tune on P0/P1.** Dust clouds + fireflies + item rarity dots are the highest-volume effects; turn off per section if 800p Deck GPU or fill-rate-bound P1 dips. Low risk.
### 4. Perception 0.2.1 (+ OctoLib) — trails + screen shake + a real post-effect (VERIFIED config; issues)
- Source (branch 1.21.1) has three modules: `trail`, `shake`, `chromatic_aberration`. Chromatic aberration is a **fullscreen post-effect triggered by loud sounds** (explosions, warden, dragon, wither...) with per-sound `duration`/`strength`; trails are a per-entity map (`entityTrails`, `particleTrails`) that includes `minecraft:experience_orb`, arrows, allay, vex, phantom etc. Config keys verified: [ChromaticAberrationConfig.java](https://github.com/Octo-Studios/perception/blob/1.21.1/common/src/main/java/it/hurts/octostudios/perception/common/modules/chromatic_aberration/config/ChromaticAberrationConfig.java), [TrailConfig.java](https://github.com/Octo-Studios/perception/blob/1.21.1/common/src/main/java/it/hurts/octostudios/perception/common/modules/trail/config/TrailConfig.java), [module tree](https://github.com/Octo-Studios/perception/tree/1.21.1)
- Known bug (fixed line): 0.1.0.2 changelog — *"Fixed FPS leak with arrows stuck in player model"*, *"Fixed render conditions of particle trails"*. Pinned 0.2.1 postdates this. [Modrinth version notes](https://modrinth.com/mod/perception/version/9QZB1Ivx)
- Cost: trails = extra particles per moving entity (XP orbs in a Create mob/XP farm are the realistic burst case); CA = one post pass during effect duration; shake = cheap matrix offset.
- **Recommendation: keep + trim `entityTrails`** (at minimum `experience_orb`) via config map if farms are common. Helps P0/P2. Low risk.
### 5. Inventory Particles — GUI-only particle decor (DOCUMENTED, cost INFERENCE)
- Client-side, data-driven particles attached to items in inventory screens (physics fully configurable via datapack). [CurseForge](https://www.curseforge.com/minecraft/mc-mods/inventory-particles) / [Modrinth](https://modrinth.com/project/XYnKrsxH)
- Cost only while a container/inventory screen is open; scales with how many items have particle definitions. INFERENCE: negligible for frame time; slight VRAM for particle textures.
- **Recommendation: keep.**
### 6. Sooty Chimneys — per-chimney smoke particles (DOCUMENTED)
- Adds chimneys that emit smoke particles; soot accumulates over time. By mortuusars. [Modrinth](https://modrinth.com/mod/sooty-chimneys)
- Cost: only where players place chimneys; smoke is the heaviest vanilla-style particle family but counts here are tiny. INFERENCE: negligible.
- **Recommendation: keep.**
### 7. WATUT 1.2.7 — real network + CPU cost; known breakage with Twilight Forest (VERIFIED config + issue tracker)
- Config keys verified from source (`ConfigClient.java`, file `watut-client`): `sendMouseInfo`, `sendTypingSpeed`, `sendActiveGui`, `sendIdleState`, `showPlayerAnimations`, `showPlayerAnimation_Typing/_Idle/_Gui`, `showPlayerActiveNonChatGui`, `showPlayerActiveChatGui`, `particleSizeScale`, **`tickReceiveAndRenderRateOfGUIUpdates` (default 10 = GUI image of other players refreshed 2×/s; you can only increase it)**, `dontSendDetailedGUIInfo`, `dontSendItemInfo`. [ConfigClient.java](https://github.com/Corosauce/WATUT/blob/1.21.0/src/main/java/com/corosus/watut/config/ConfigClient.java)
- v1.2.7 commit: *"Workaround flickering issue ... set default to not constantly update GUI"* — the default GUI-refresh behavior was already identified as costly. [commit 71342de](https://github.com/Corosauce/WATUT/commit/71342deaf617baa997fbfc6803bad1df423b4836)
- **Known bug, directly relevant:** issue #106 — WATUT dynamic GUIs *fail to render* in a 1.21.1 large modpack, with **Twilight Forest 4.8.3281** among the triggering mods (also Cold Sweat, Dungeons Delight, Vista). Our pack has TF 4.8.3345. [Issue #106](https://github.com/Corosauce/WATUT/issues/106)
- Cost: per-player NBT packets of screen state (client↔server) [network package](https://github.com/Corosauce/WATUT/tree/1.21.0/src/main/java/com/corosus/watut/network), then the client re-renders other players' GUIs via a custom particle/screen renderer ([ScreenParticleRenderer](https://github.com/Corosauce/WATUT/blob/1.21.0/src/main/java/com/corosus/watut/client/screen/ScreenParticleRenderer.java), [PlayerStatusManagerClient](https://github.com/Corosauce/WATUT/blob/1.21.0/src/main/java/com/corosus/watut/PlayerStatusManagerClient.java)). With ~10 friends, several AFK-in-inventory, this is continuous but small; the arm/head animations are model-side (cheap).
- **Recommendation: keep (it's a social mod for a friends' server) + tune.** If the TF GUI-rendering bug bites (visual, not perf), the fallback is `dontSendDetailedGUIInfo=true`. Helps P2 (CPU) and server bandwidth. Low risk.
### 8. Traveler's Titles — overlay text on biome/dimension entry (DOCUMENTED)
- Fully client-side; customizable text size/color/duration; biome & dimension blacklists; optional custom sounds per biome/dimension entry. [Modrinth](https://modrinth.com/mod/travelers-titles)
- Cost: transient overlay + a sound on biome change. INFERENCE: negligible.
- **Recommendation: keep.**
### 9. Legendary Tooltips 1.5.5 / Advancement Plaques / Held Item Tooltips / Items Displayed — tooltip & toast dressing (DOCUMENTED, cost INFERENCE)
- Legendary Tooltips: client-side, up to 16 custom borders selected by item/mod/tag/rarity/name-color/NBT; adds shadows, centered names, 3D item previews in tooltips; requires Iceberg (+Prism). [Modrinth](https://modrinth.com/mod/legendary-tooltips), [config wiki](https://github.com/AHilyard/LegendaryTooltips/wiki/Configuration-Instructions)
- Advancement Plaques: client-side (Forge/NeoForge variant needs server only for its custom sounds, which can be disabled in config); plaque position/type configurable. [Modrinth](https://modrinth.com/mod/advancement-plaques)
- Cost: rendering cost only while a tooltip/plaque is visible; the "3D item preview" tooltip option is the only notably heavier feature (renders an item model inside the tooltip). INFERENCE: negligible in normal play.
- **Recommendation: keep.** If tooltip-heavy screens (JEI!) show hitches on P2, disable 3D previews in Legendary Tooltips config first.
### 10. Emojiful 5.2.4 — font/atlas hooks, worst history of this group (DOCUMENTED issues)
- Hooks font rendering to inject emoji glyphs (incl. animated); supports datapack emoji; client+server. [Modrinth](https://modrinth.com/mod/emojiful)
- Known bugs: game **freeze when an emoji renders** (Cottage Witch pack, older version) [Issue #98](https://github.com/InnovativeOnlineIndustries/Emojiful/issues/98); **breaks chat text output** (e.g., `/spark tps` garbled) [FTB #3886](https://github.com/FTBTeam/FTB-Modpack-Issues/issues/3886); unreadable Patchouli text [FTB #5495](https://github.com/FTBTeam/FTB-Modpack-Issues/issues/5495); rendering breaks with "When Was That Again" [Issue #15](https://github.com/InnovativeOnlineIndustries/Emojiful/issues/15).
- Cost: animated GIF emojis add texture atlas memory/upload churn (VRAM) — INFERENCE; frame cost otherwise only when emoji render in chat/signs. INFERENCE.
- **Recommendation: candidate for removal** (flavour mod with freeze/chat-corruption history, near-zero gameplay value); at minimum pin the 1.21.1 line and disable animated emoji packs. Small startup + VRAM win for P1/P2. Low risk if the group agrees.
### 11. Exposure 1.9.18 — camera rendering is a real, but occasional, hitch (VERIFIED changelog)
- Pinned 1.9.18 still contains the "preload" that exists *"to reduce the lag spike when camera is first used after launch"*; 1.9.19 removed it. **Recommend updating to ≥1.9.19** to delete that startup/first-use spike. [CHANGELOG.md (1.21.1)](https://github.com/mortuusars/Exposure/blob/1.21.1/CHANGELOG.md)
- With Iris installed, photo capture uses the **direct capture** path ("due to technical limitation"), i.e. the world is re-rendered through the viewfinder; background capture (1.8.0+) is skipped. DH+Iris also forces direct capture (1.9.7). So taking a photo = one extra full render pass → visible hitch on P0/P1. [CHANGELOG.md](https://github.com/mortuusars/Exposure/blob/1.21.1/CHANGELOG.md)
- Veil interplay: 1.9.4 removed `effective` from the background-capture allowlist and **added `veil` to the mods that force direct capture** because *"it's the one that causes issues"* — our pack runs Veil (via Lodestone/irisveil stack). [CHANGELOG.md](https://github.com/mortuusars/Exposure/blob/1.21.1/CHANGELOG.md)
- Photos in frames = image textures (small, mipmaped since 1.8.0). Photograph-in-item-frame rendering is off by default.
- **Recommendation: keep, bump to 1.9.19+**; cost is opt-in (only while photographing). Low risk.
### 12. Immersive Paintings — LoD + streaming by design; config exists (VERIFIED config wiki)
- Officially: *"Using smart level of details, caching and lazy streaming of data the performance impact is not noticeable."* [Modrinth](https://modrinth.com/mod/immersive-paintings)
- Config (documented): `thumbnailSize=128`, `lodResolutionMinimum=32`, LoD thresholds `halfResolutionThreshold=2.0`, `quarterResolutionThreshold=4.0`, `eighthResolutionThreshold=8.0`, bandwidth `maxPacketsPerSecond=20`/`packetSize=64KB`, upload caps `maxUserImageWidth/Height=4096`, `maxUserImages=1000`, `testIfSpaceEmpty=false`, `showOtherPlayersPaintings=true`. [Config wiki](https://github.com/Luke100000/ImmersivePaintings/wiki/Config)
- Cost: VRAM scales with number of large custom paintings actually loaded in view (LoD halves resolution progressively); disk cache for uploads. For P1 (6GB VRAM) lower `maxUserImages`/`thumbnailSize` if friends upload big art. INFERENCE on exact VRAM numbers.
- **Recommendation: keep + cap uploads.** Low risk.
### 13. Controlify 3.0.1 — input mod; historical FPS bug fixed upstream (DOCUMENTED)
- 2.0.3 changelog: *"Drastically improved the performance of the on-screen keyboard — There is zero performance impact anymore. It used to decrease FPS by 8x (lol)"*; also "Fix extreme FPS drops when using the on-screen keyboard" (2.0.0-beta.14). [CurseForge 2.0.3](https://www.curseforge.com/minecraft/mc-mods/controlify/files/6347289) / [Modrinth](https://modrinth.com/mod/controlify/version/1HeJe83k)
- Actively supported on 1.21.1 NeoForge. [CurseForge](https://www.curseforge.com/minecraft/mc-mods/controlify)
- **Recommendation: keep** (Deck/controller users). No open perf issue found for 3.x (INFERENCE).
### 14. Ping Wheel — tiny HUD/network mod (DOCUMENTED)
- Client+server ping markers; config via `/pingwheel config`; optional item icons on markers. [Modrinth](https://modrinth.com/mod/ping-wheel)
- Cost: a few billboard quads + small packets. INFERENCE: negligible.
- **Recommendation: keep.**
### 15. Alex's Caves 2.0.10 — biome fog/lighting overrides vs shaders; known lag reports (COMMUNITY + issue tracker)
- Known heavy cases: *"immense lag when inside toxic caves"* (65–80 → ~40 FPS) [Issue #760](https://github.com/AlexModGuy/AlexsCaves/issues/760); FPS drops looking at raycats/irritated mobs [Issue #616](https://github.com/AlexModGuy/AlexsCaves/issues/616).
- The community-maintained **"Iris Caves"** patch mod exists specifically to fix Alex's Caves cave-fog vs shaderpack conflicts (lists Complementary-family patches; notes **MakeUp Ultra Fast "isn't supported at all: it has no single cave-fog color to hook"** — relevant since MUF 9.5f is in our pack; supports 1.21.1 NeoForge + Iris). [Iris Caves on CurseForge](https://www.curseforge.com/minecraft/mc-mods/iris-caves)
- Community-quoted `alexscaves-client.toml` keys: `biome_sky_overrides`, `biome_sky_fog_overrides` (both with in-file comment "May conflict with shaders") and `biome_ambient_light_coloring` (recommended to disable for DH/visual issues). Keys as quoted from the shipped config file — treat exact names as COMMUNITY-verified until opened in-game. [r/feedthebeast: AC conflicting with shaders](https://www.reddit.com/r/feedthebeast/comments/1m6hxwh/alexs_caves_conflicting_with_my_shaders/), [DH problems thread](https://www.reddit.com/r/feedthebeast/comments/1kg37wm/i_have_some_problems_with_distant_horizons/)
- **Recommendation: keep + tune** — with Complementary Reimagined, expect AC cave-biome fog/lighting glitches (documented by the existence of Iris Caves and the AC-shaders threads); consider disabling `biome_ambient_light_coloring`/sky overrides for shader users; investigate the toxic-caves lag with spark if players visit AC often. Helps P0/P1 (GPU/shader) most. Low risk.
### 16. Twilight Forest 4.8.3345 — sky/Aurora/weather effects have config toggles (VERIFIED via official wiki)
- `twilightforest-client.toml`: **`auroraBiomes`** (default `["twilightforest:glacier"]`; set `[]` to disable the Aurora shader entirely), `cloudBlockPrecipitationDistance` (client, default `-1` = follow common; `0` disables cloud precipitation rendering), `silentCicadas`. Common config `cloudBlockPrecipitationDistance` (default 32) — *"Lower if experiencing low tick rate"*. [TF Wiki: Configuration](http://benimatic.com/tfwiki/index.php/Configuration)
- Aurora shader history: added for Glacier with configurable biomes; a crash fix mentions "Aurora Shader ... not being registered properly" [TF changelog](http://benimatic.com/tfwiki/index.php/Change_Log)
- **Known shader conflict:** TF's custom skybox conflicts with major shaderpacks (e.g. Complementary Reimagined) causing *"glaring visual distortion"*; the community **"Disable Twilight Forest Skybox"** datapack (works 1.16.5→1.21.x) removes the skybox so shaders work properly in the dimension. [CurseForge datapack](https://www.curseforge.com/minecraft/data-packs/disable-twilight-forest-skybox); same class of issue tracked as [TF issue #854](https://github.com/TeamTwilight/twilightforest/issues/854) (older, OptiFine-era).
- Also relevant from §7: WATUT's dynamic GUIs break when TF is installed ([WATUT #106](https://github.com/Corosauce/WATUT/issues/106)).
- **Recommendation: keep (core content) + tune:** if the group plays TF with shaders, ship the skybox-disable datapack (or accept the artifact); set `cloudBlockPrecipitationDistance: 0` on weak clients; `auroraBiomes=[]` only if Glacier runs badly with shaders. Helps all machines in the TF dimension; P0/P1 GPU. Low risk.
### 17. Fusion 1.3.15b + Athena 4.0.6 — two CTM engines loaded at once (VERIFIED existence/claims; cost INFERENCE)
- Athena (CTM 4.0.6 line): *"cross-platform baked model loader that features built-in support for connected textures ... unique baked models for each type to ensure optimal performance"*; its CTM is designed to "minimize unnecessary calculations". [CurseForge](https://www.curseforge.com/minecraft/mc-mods/athena). Chipped's connected textures require Athena (community). [Reddit r/feedthebeast](https://www.reddit.com/r/feedthebeast/comments/1fa8mr3/connected_texture_resource_pack_compatable_with/)
- Fusion: resourcepack library providing connected textures + custom entity models. [Modpack Index page](https://www.modpackindex.com/mod/45224/fusion-connected-textures)
- **Verified Fusion perf bug pattern:** a 1.3.x changelog entry — *"Fixed performance issues when resolving custom texture tinting with Sodium installed"* — i.e., Fusion has had Sodium-interaction hot spots worth keeping current on. [Fusion changelog (Modrinth)](https://modrinth.com/mod/fusion-connected-textures/version/1.3.1-fabric-mc26.2)
- Cost of running both: each engine registers its own model-baking/CTM hooks; both are exercised only by resourcepacks that target their respective formats. Startup time + heap for double registration is the certain cost; runtime chunk-meshing cost depends on which packs are actually enabled (Better Leaves & MT-CT-style packs ship Fusion and/or Athena variants). MT-CT notes "CTM and Athena Compat did exist at the start but are Deprecated" — i.e., pack-side support for a single engine is the norm. [MT-CT on Modrinth](https://modrinth.com/project/TjOXlt9Y)
- **Recommendation: audit, then drop one if possible.** Determine which shipped resourcepacks actually need Fusion vs Athena; if everything the pack ships is Fusion-format (or Athena-format), remove the other engine → startup + heap win (matters for P2's 8GB free RAM), zero visual change. Medium confidence the pack needs both today (Chipped→Athena is the usual blocker).
### 18. Lodestone 1.8.2 / Veil — post-processing only when triggered (DOCUMENTED)
- Veil (FoundryMC) is the rendering library under Lodestone's 1.8.x stack: custom **post-processing pipeline**, shader injection, bone-animated models. [Veil README (branch 1.21)](https://github.com/FoundryMC/Veil/blob/1.21/README.md)
- Lodestone: library for Lodestar team mods — shaders, screenshake, utilities. [9Minecraft summary](https://www.9minecraft.net/lodestone-mod/) (secondary source; treat as description)
- Cost: post chains cost frame time only while an effect is active; presence of Veil also changes behavior of other mods (see Exposure §11: Veil forces Exposure's direct-capture path). Veil+Iris compat is what irisveil 0.3.0 provides in this pack.
- **Recommendation: keep** (dependency of the Create-adjacent stack); measure once with spark whether any Veil post chain runs continuously in normal play. Open question below.
### Fleet notes
| Mod | P0 Deck (iGPU, 800p) | P1 (VRAM-bound 1060) | P2 (CPU/heap-bound) |
|---|---|---|---|
| SPR raytrace | Med (CPU spikes) | Low | **High** |
| SPR level-clone cache | Low | Low | Med (heap) |
| WATUT GUI streams | Low-Med | Low | **Med (CPU+net)** |
| Subtle Effects / Perception trails | Med (fill-rate/particles) | Med | Low-Med |
| Perception chromatic aberration | Med (post pass) | Med | Low |
| AC biome fog/shader overrides | Med-High | Med-High | Low |
| TF Aurora + cloud precip + skybox×shaders | Med | Med | Low |
| Immersive Paintings textures | Low | **Med (VRAM)** | Low (heap) |
| Exposure capture hitch | **High (during use)** | High (during use) | Low |
| Emojiful animated emoji | Low | Low-Med (VRAM) | Low (startup) |
| Fusion+Athena double engine | Startup | Startup | **Startup/heap** |
| Tooltips/plaques/titles/pings/chimneys/inv-particles | Negligible | Negligible | Negligible |
## Sources
- Kept (primary): henkelmax/sound-physics-remastered `1.21.1` branch — `SoundPhysicsConfig.java`, `SoundRateConfig.java`, README (config keys, defaults, per-sound rates, level-clone cache).
- Kept (primary): Corosauce/WATUT branch 1.21.0 — `ConfigClient.java`, commit 71342de, issue #106 (TF conflict), client screen renderer sources.
- Kept (primary): Octo-Studios/perception branch 1.21.1 — `ChromaticAberrationConfig.java`, `TrailConfig.java`, module tree; Modrinth 0.1.0.2 notes (FPS leak fix).
- Kept (primary): mortuusars/Exposure branch 1.21.1 `CHANGELOG.md` (capture paths, Iris/DH/Veil interactions, preload removal in 1.9.19).
- Kept (primary): Luke100000/ImmersivePaintings wiki/Config + Modrinth description (LoD/caching/streaming).
- Kept (primary/official wiki): benimatic.com TF Wiki Configuration (`auroraBiomes`, `cloudBlockPrecipitationDistance`, client config list) + TF Change Log (Aurora shader).
- Kept (primary): MincraftEinstein/SubtleEffects wiki + Modrinth (per-feature toggles, particle culling/render distance, config sections).
- Kept (primary): CreativeMD/AmbientSounds repo + Modrinth (client-side, `/cmdclientconfig`); CreativeMD/AmbientSounds is the source repo.
- Kept (primary): FoundryMC/Veil README (post-processing pipeline); AHilyard LegendaryTooltips & AdvancementPlaques Modrinth pages; LukenSkyne Ping Wheel Modrinth; LopyTwich Inventory Particles CurseForge; Sooty Chimneys Modrinth; YUNG Travelers Titles Modrinth; isXander/CurseForge Controlify changelogs; Athena CurseForge description; Fusion Modrinth changelog; Iris Caves CurseForge page; "Disable TF Skybox" datapack page.
- Dropped: minecrafthub.io / feed-the-mods / 9minecraft / mcfuns / minecraftbible / tlauncher / craftymania — SEO mirrors with no unique verified facts.
- Dropped: Reddit threads except the two quoting actual `alexscaves-client.toml` contents (kept as COMMUNITY evidence of key names).
## Gaps
- **AmbientSounds concrete config keys** (density/feature toggles) not pulled from source — repo is CreativeMD/AmbientSounds (branch `1.21`), config is GUI-driven (`/cmdclientconfig`); verify in-game rather than trusting names from memory.
- **Subtle Effects 1.14.3 exact TOML keys**: config is Fzzy-Config GUI sections (`Entities/Dust Clouds`, `Items/Item Rarity → Particle Display Type` documented), but per-effect key names in the file were not read from source for the pinned 1.21.1 build.
- **alexscaves-client.toml key names** come from community quotes of the generated file, not from AC source; verify by opening the shipped config once.
- **Fusion 1.3.15b-specific** changelog wasn't diffed (research surfaced the newer-line Sodium-tinting fix); check the pinned file's own changelog on CurseForge before crediting it with the fix.
- **Heap impact of SPR's level-clone cache** (`level_clone_range=4` → radius-4 chunk clones retained 20 ticks) is unquantified — worth one spark/heap measurement on P2.
- Whether any **Veil post chain runs continuously** with this exact Lodestone/Aeronautics/SSRD stack is unmeasured.
## Top levers (ranked)
| # | Lever | Expected impact | Machines helped | Risk | Confidence |
|---|---|---|---|---|---|
| 1 | SPR: `environment_evaluation_ray_count` 32→16, `environment_evaluation_ray_bounces` 4→2–3, `strict_occlusion=true`; cap noisy modded sounds in the per-sound rate config (`0`/`>=1`) | **High** (CPU) on sound-dense scenes (Create) | P2, P0 | Low (audio quality slightly degrades) | High (config verified in source) |
| 2 | Alex's Caves: disable `biome_ambient_light_coloring` (+ sky/fog overrides) for shader users; test Iris Caves-style patches if AC fog glitches under Complementary Reimagined | Med-High (GPU + visual correctness) | P0, P1 | Low | Medium (community-verified keys) |
| 3 | Twilight Forest: `cloudBlockPrecipitationDistance` client → 0; ship "Disable TF Skybox" datapack for shader play; `auroraBiomes=[]` only if Glacier stutters | Med (in-dimension FPS/stability) | P0, P1 (all in TF) | Low | High (official wiki keys) |
| 4 | WATUT: raise `tickReceiveAndRenderRateOfGUIUpdates` (e.g. 20–40) or `dontSendDetailedGUIInfo=true` if GUI streaming costs show; watch known TF conflict (#106) | Med (CPU + network, 10 players) | P2 + server | Low (visual fidelity loss) | High (config verified) |
| 5 | Perception: clear `experience_orb` (and other high-traffic) entries from `entityTrails`; keep CA sound list short | Med (particle bursts in farms) | P0, P2 | Low | High (config verified) |
| 6 | Immersive Paintings: lower `maxUserImages`, `thumbnailSize` for P1 | Med (VRAM) | P1 | Low | Medium (config verified, effect unmeasured) |
| 7 | Remove one CTM engine (Fusion or Athena) after auditing shipped resourcepacks (Chipped pins Athena) | Low-Med (startup + heap) | P2 (startup/heap) | Medium (must re-verify every resourcepack) | Medium |
| 8 | Update Exposure to ≥1.9.19 (removes first-camera-use preload lag spike) | Low (hitch removal) | All, esp. P0 | Low | High (changelog verified) |
| 9 | Emojiful: candidate for removal (or drop animated emoji packs) | Low (startup, VRAM, avoids known freeze/chat bugs) | P1, P2 | Low (social acceptability) | Medium |
| 10 | Subtle Effects: turn off `Entities/Dust Clouds` + Item Rarity dots on P0/P1 if particle-bound | Low-Med (particles/fill-rate) | P0, P1 | Low | Medium |
| 11 | Keep as-is: Legendary Tooltips (disable 3D preview if JEI tooltips hitch), Advancement Plaques, Held Item Tooltips, Items Displayed, Ping Wheel, Travelers Titles, Sooty Chimneys, Inventory Particles, Controlify, AmbientSounds, Lodestone/Veil | — | — | — | Medium-High |
## Open questions to verify by benchmark
1. SPR CPU: measure frame/tick cost with a Create factory producing dozens of concurrent sounds, at default (32 rays/4 bounces) vs 16/2 vs strict occlusion; and heap delta of the level-clone cache on P2 (spark GC/alloc view).
2. SPR per-sound rate caps: which modded sounds (Create, Alex's Mobs, Twilight Forest ambients) actually exceed the cap in a session — read `performance_logging=true` output first.
3. WATUT: with 10 players and 4–5 GUIs open, cost of GUI streaming at `tickReceiveAndRenderRateOfGUIUpdates=10` vs 30 (client CPU + server→client bandwidth); confirm whether TF 4.8.3345 still triggers #106 dynamic-GUI failure.
4. Alex's Caves: FPS in Toxic Caves (issue #760 scenario) on P0/P1, with and without `biome_ambient_light_coloring=false`, under Complementary Reimagined r5.9.3 and MakeUp Ultra Fast 9.5f (MUF has no cave-fog hook per Iris Caves — expect visual, not perf, artifacts).
5. Twilight Forest: FPS in Glacier with Aurora shader on vs `auroraBiomes=[]` under shaders; skybox artifact severity with Complementary Reimagined with/without the disable-skybox datapack.
6. Perception: XP-orb trail particle count in a Create XP farm, `entityTrails.experience_orb` on vs removed (P0 800p fill-rate).
7. Exposure: hitch duration of first camera use on 1.9.18 vs 1.9.19 on P0; and photo-capture hitch with Iris (direct capture) at 1280×800.
8. Fusion vs Athena: startup time and heap with both loaded vs each removed alone (after confirming which resourcepacks need which engine).
9. AmbientSounds: resident RAM of its sound buffers on P2 (heap dump / AllTheLeaks) and whether `/ambient-debug` shows dense overlapping ambients in forest/swamp at night.
10. Veil/Lodestone: spark profile — does any Veil post chain run continuously in normal overworld play, or only during modded events?
@@ -0,0 +1,213 @@
# Research: Candidate client-side performance mods NOT yet in Onion MC
**Scope:** MC 1.21.1 / NeoForge 21.1.251, ~212 mods, client-side only. Existing stack assumed fixed
(Sodium 0.8.13, Iris 1.8.14-beta.1 + irisveil 0.3.0, Distant Horizons 3.3.2 mandatory, ImmediatelyFast 1.6.14,
EntityCulling 1.11.2, ModernFix 5.27.24, FerriteCore 7.0.3, Lithium 0.15.4, Dynamic FPS 3.11.4, AllTheLeaks 1.1.13,
EMF 3.3.9 + ETF 7.2.4 + Fresh Animations, Better Leaves 9.6, Create 6.0.10/Flywheel 1.0.6, Sable 2.0.5,
Xaero's Minimap/World Map, JEI 19.57, Controlify 3.0.1).
**Evidence tags used below:** `VERIFIED` = read from source code/repo/changelog/API metadata;
`DOCUMENTED` = official README/wiki/Modrinth body/changelog; `COMMUNITY` = forums/Reddit/Discord/answer sites;
`INFERENCE` = my reasoning from the above.
Primary source for availability was the **Modrinth v2 API** (`/project/<slug>/version?loaders=["neoforge"]&game_versions=["1.21.1"]`),
queried per mod. Any "version + date" below is a `VERIFIED` read of that API response unless tagged otherwise.
---
## Summary
1. **Most of the "standard" missing client perf mods simply do not exist for NeoForge 1.21.1** — Exordium (NeoForge builds only for 1.21.6+), Krypton (Fabric-only), Nvidium, VulkanMod, Enhanced Block Entities, FastChest, Memory Leak Fix (obsolete by its own README), ThreadTweak/Smooth Boot, More Culling Extra, YOSBR. This is a hard filter, not a judgement call.
2. **The real, available levers are: More Culling (or Cull Leaves), BadOptimizations, Particle Core, Gnetum, Ixeris, Flerovium, Better Block Entities, and (for the Steam Deck specifically) Super Resolution.** Of these, More Culling + Cloth Config, BadOptimizations and Particle Core are the lowest-risk/highest-confidence additions.
3. **Two landmines to avoid:** `Sodium Leaf Culling` (the mod people recommend for Better Leaves) **crashes on Sodium 0.8.11+** and is unmaintained since Apr 2025, so it is unusable on this pack's Sodium 0.8.13; and `ScalableLux` is reported to break Distant Horizons, which is mandatory here.
4. **Side finding worth escalating:** the pack's exact Iris 1.8.14-beta.1 + Sodium 0.8.13 combination on NeoForge 1.21.1 has a documented mixin incompatibility in the 1.21.1 NeoForge community, for which a third-party "Iris-Sodium Compat" patch exists on CurseForge. That is a stability question for the existing stack, not a new mod, but it should be checked before benchmarking anything else.
---
## Master candidate table
Availability column = NeoForge 1.21.1 build confirmed via Modrinth API (+ CurseForge where noted).
| Mod | NF 1.21.1? + version (date) | What it does | Expected impact | Conflicts with this pack | Risk | Recommendation |
|---|---|---|---|---|---|---|
| **More Culling** (`moreculling`) | ✅ 1.0.10 (2026‑09‑07) | Culls blockstates, leaves, item frames (3‑face/back/map), clouds, sign text, rain/snow, end gateway, beacon beam, paintings | **Med** — dense bases with signs/frames/leaves; helps all 3 machines | Needs **Cloth Config** (`cloth-config`, ✅ 1.21.1). Leaves culling overlaps a bushy-leaves pack (Better Leaves 9.6); More Culling issue #475 (unconfirmed Create/Flywheel report, maintainer says it "doesn't touch blockentity renderer") | Low‑Med | **Add** |
| **Cull Leaves** (`cull-leaves`) | ✅ 4.1.1+1.21.1-neoforge (2025‑12‑12) | Leaf-block face culling only ("Smart Leaves" replica) | **Low‑Med** — leaves only | Needs **MidnightLib** (`midnightlib`, ✅ 1.21.1). Designed to be used *with* Motschen's Better Leaves (same author) | Low | **Add** (or use More Culling's leaves option instead — pick one, not both) |
| **BadOptimizations** (`badoptimizations`) | ✅ 2.4.1 (2025‑12‑15) | Lightmap caching, sky-colour caching, skips idle debug renderers, entity/block-entity renderer caching, redundant FOV calcs | **Low‑Med** — author measured ~1926→2008 FPS (~+4%) in Fabulously Optimized; more relevant on tick-bound P2 | None known. Has a self-disabling incompatibility mechanism (`ignore_mod_incompatibilities`, `enable_entity_renderer_caching` etc.) | Low | **Add** |
| **Particle Core** (`particle-core`) | ✅ 0.3.3+1.21+neoforge (2026‑02‑05) | Particle frustum culling, custom vertex path, per-tick lightmap cache, async particle ticking, per-type spawn reduction/disable | **Med‑High** — author's spark profile: particle rendering went from ~25% of CPU time to "basically a non-item"; +50% relative over Sodium alone | Needs 2 libs (Modrinth deps). Do **not** pair with Async Particles (per community); pack has neither | Med (async ticking — can be disabled via `disableOptimizations`; auto-disables on instability) | **Add** |
| **Gnetum** (`gnetum`) | ✅ 3.3.6 (2026‑06‑24) | Distributes HUD rendering over N frames + HUD framebuffer caching; Sodium 0.8+ config integration | **Med** in HUD-heavy scenes (Xaero's, JEI, titles, tooltips) | Author states **ImmediatelyFast is "fully compatible and recommended"**; Xaero's flicker fixed 3.2.0; IF HUD-batching flicker fixed 3.3.4 | Low‑Med | **Add** (Trial if HUD flicker appears) |
| **Ixeris** (`ixeris`) | ✅ 4.6.8+1.21.1-neoforge (2026‑09‑21) | Threaded event polling off the render thread; buffered raw input (**Windows only**); GLFW state caching; FPS limiter rewrite | **High on P1** only if high-polling mouse (author: 8000 Hz 12→121 FPS; 500 Hz 134→151 FPS). **Low on Linux/P0/P2** (1.03–1.11×) | Open Iris conflict issue #151 (on MC 26.3 NeoForge, not 1.21.1); open crash reports on **older NVIDIA cards** (#134) and Intel iGPU alt-tab (#144); Exordium incompat (not in pack) | Med | **Trial, P1/P2 first** (P1 has a GTX 1060 = "old card" in issue #134) |
| **Super Resolution** (`superresolution`) | ✅ 0.9.2-alpha.2+opengl (2026‑10‑03) — **alpha only on 1.21.1** | FSR1/FSR2/FSR3, SGSR1/2, DLSS, XeSS upscaling (+ frame-gen frameworks); README lists Sodium/Iris/DH as supported | **High on P0** (RDNA2 8CU @1280×800); **Med on P1** (VRAM-bound); **Low on P2** (main-thread bound) | README: incompatible with OptiFine/Canvas/VulkanMod; marks Resolution Control/RenderScale/GPU Booster/GPU Tape incompatible. Needs OpenGL 4.3+. **Complementary Reimagined has no SR shader interface** (issue #317 is "On Hold") → only FSR1 compatibility mode; FSR3 has "severe entity ghosting" (author's own words) | Med‑High | **Trial, P0 first** |
| **Flerovium** (`flerovium`) | ✅ 1.1.3 (2026‑09‑12, beta) / 1.2.0 (2026‑09‑26, **alpha**) | Backface culling + fast-math + LOD for item, particle and entity rendering; author claims item 50%+, particles 50%, entities 30% faster | **Med** across all 3 | Needs **Sodium**. Author added "better compat with aeronautics mod" (pack has Create Aeronautics) and Accelerated Rendering. Backface culling on items/entities is a known source of visual glitches with layered/animated models | Med‑High | **Trial** (verify vs Better Combat, Emotecraft, 3D Skin Layers, EMF+Fresh Animations) |
| **Better Block Entities** (`better-block-entities`) | ✅ 1.3.4+mc1.21.1 (2026‑09‑15), explicitly built for "Sodium 0.8.12‑0.8.13" | Hybrid block-entity renderer: chests/shulkers/signs/pots/beds/bells/banners meshed into terrain when static, dynamic when animating | **High** in storage/BE-heavy bases | Needs **Sodium**. **EMF compat "only available for 1.21.11 versions and above"** (its own README) → **unverified with EMF 3.3.9 + Fresh Animations** which this pack uses. Touted as compatible with ImmediatelyFast, EntityCulling, Lithium, C2ME | Med | **Trial** |
| **Sodium Extra** (`sodium-extra`) | ✅ 0.9.4 (2026‑09‑16) | Animation/particle/detail/render toggles (OptiFine-style eye candy), fog controls, cloud height, static-entity rendering, FPS display | **Low direct**, but it is the cheapest way to expose per-machine particle/animation/entity levers (author's wiki) | Needs Sodium. Optional deps on RSO + another project (present as optional) | Low | **Add** |
| **Reese's Sodium Options** (`reeses-sodium-options`) | ✅ 2.2.4 (2026‑09‑16) | Alternative Sodium options screen, search, keyboard/controller nav | ~0 direct perf; big UX for tuning + **Controlify integration** (pack has Controlify 3.0.1) | Needs Sodium only | Low | **Add** (tuning ergonomics, especially on Deck) |
| **Default Options** (`default-options`) | ✅ (1.21.1 + neoforge listed) | Ships `options.txt`/keybinds/server-list defaults without overwriting user changes | 0 runtime; ensures the tuned baseline actually reaches every client | Needs **Balm** (`balm`, ✅ 1.21.1 neoforge). Achieves what YOSBR would (YOSBR is Fabric-only) | Low | **Add** |
| **Chloride** (`chloride`) | ✅ 1.8.1-NEO-1.21.1 (2026‑07‑19) | Entity distance culling + on-screen entity cap, leaves culling, solid chest/bed models, hide JEI/REI/EMI overlay until searching, fast language reload, fog controls, FPS overlay | **Med**, but largely **overlapping** with EntityCulling (present), More Culling, and JEI | Needs Sodium; updated to Sodium 0.8.12-beta.1 (1.7.9) and **adds Sable support for entity culling** (1.7.8) — Sable IS in the pack. Declares 1 mod as incompatible (changelog names "Smooth Skies"). Config file is `chloride-client.toml` | Med | **Trial** (only if you drop/disable overlapping culling) |
| **Krypton Reno** (`krypton-fnp`, CurseForge "Krypton FNP") | ✅ NeoForge 1.21.1 (CurseForge file 0.2.28.1‑1.21.1, 2026‑07‑06; Modrinth listing updated 2026‑07‑09) | Krypton-derived Netty/connection optimisations + optional server-side chunk/block/entity culling | **Low client-side** on a normal LAN link; the juicy packet-culling keys are server-side | README: incompatible with KryptonFoxified/Ceres/Pluto/Krypton Hybird/Chionanthus/Krypton-Fabric-with-Connector. Restricted 404Setup license (packwiz OK, full-game redistribution not) | Med | **Trial** (better evaluated as a *server* change) |
| **Raise Sound Limit Simplified** (`rsls`) | ✅ (1.21.1 + neoforge listed) | Sound-engine performance + raises sound source limit | Low‑Med (pack has Sound Physics Remastered, AmbientSounds, Subtle Effects, SRS) | Not verified against Sound Physics Remastered 1.5.1 | Low‑Med | **Trial** |
| **Async Logger** (`asynclogger`) | ✅ (1.21.1 + neoforge listed) | Asynchronous logging / log filtering | Low | Not verified vs ModernFix/AllTheLeaks log mixins | Low‑Med | **Trial (optional)** |
| **Sodium Options API** (`sodium-options-api`) | ✅ 1.21.1 listed (last update 2025‑04‑10) | Sodium options-menu API for addons | 0 direct | RSO's own docs: "Sodium Options API is no longer supported on versions where Sodium already provides the relevant options API directly" → Sodium 0.8.x provides it | Low | **Skip** |
| **Sodium Extras** (`sodium-extras`) | ✅ neoforge-1.21.1-1.0.8 (2025‑04‑07) | Entity culling (duplicate of EntityCulling), fog toggle, font-shadow toggle, borderless | Low | Requires Sodium **+ Sodium Options API**, which is obsolete on Sodium 0.8.x; no 0.8-era release | Med (stale) | **Skip** |
| **Sodium Dynamic Lights** (`sodium-dynamic-lights`) | listed for 1.21.1 (+neoforge), last update 2025‑04‑10 | LambDynamicLights port with Sodium options integration | 0 (already have LambDynamicLights 4.8.11) | Duplicate dynamic-lights implementation | Med | **Skip** |
| **Sodium Leaf Culling** (`sodiumleafculling`) | ✅ neoforge 1.0.1 (1.21.1, 2025‑04‑07) — **but** | Leaf culling injected into Sodium's chunk-mesh pipeline | would be Med‑High | **Broken:** GitHub issues #18 "Crashes with Sodium 0.8.11+ on 1.21.1" (2026‑05‑07) and #20 "Compatibility with modern Sodium 0.8.12 …" (2026‑05‑14) are **open**; no release since Apr 2025 | Very High (crash) | **Skip** (use Cull Leaves / More Culling) |
| **Exordium** (`exordium`) | ❌ No NeoForge build ≤1.21.5 (NeoForge builds only 1.21.6/1.21.8/1.21.10/1.21.11, both released 2026‑03/04) | Low-FPS GUI/screen rendering | would be Med | Author: "No backports planned!"; Modrinth status = **archived**, "not actively maintained" | — | **Skip** (use **Gnetum**, the maintained HUD equivalent) |
| **ScalableLux** (`scalablelux`) | ✅ 1.21.1 listed (fabric+neoforge) | Starlight-derived light engine, optional parallel light updates | Low client-side (mostly chunk-gen/server) | **Community reports DH breakage** with Starlight-derived engines ("if you are using Distant Horizons then do NOT use this as it will break") | High (DH mandatory) | **Skip** |
| **Krypton** (`krypton`) | ❌ Fabric only | Netty/connection optimisations | — | — | — | **Skip** (see Krypton Reno) |
| **Nvidium** (`nvidium`) | ❌ Fabric/quilt only; **requires NVIDIA Turing+ mesh shaders** | Mesh-shader terrain backend | would be High on P2's 1080 Ti… except Pascal ≠ Turing | — | — | **Skip** |
| **VulkanMod** (`vulkanmod`) | ❌ Fabric/quilt only; **listed incompatible with Distant Horizons** | Vulkan renderer | — | VulkanMod's own incompatible-mods wiki lists DH, EMF 2.2, ImmediatelyFast 1.2.20, Exordium, Embeddium | — | **Skip** |
| **Enhanced Block Entities** (`ebe`) | ❌ Fabric/quilt only, ≤1.21.4 | Baked models for chests/signs/bells/beds/shulkers/pots | — | — | — | **Skip** (see Better Block Entities) |
| **FastChest** (`fastchest`) | ❌ Fabric only (1.21.1 listed but loader = fabric) | Static chest models (loses lid animation) | — | — | — | **Skip** |
| **Memory Leak Fix** (`memoryleakfix`) | ❌ max 1.20.4 | Fixes MC memory leaks | 0 | Its own README: "Minecraft 1.20.5+: all the memory leaks previously fixed … have been fixed. Making this mod obsolete" | — | **Skip** |
| **Ksyxis** (`ksyxis`) | ✅ exists for 1.21.1 but **server_only** | Removes unneeded spawn chunks | **0 on client in multiplayer** (its own FAQ: "It has no effect on the client when playing multiplayer") | — | — | **Skip** (client) |
| **FastLoad** (`fastload`) | ❌ abandoned by author, max 1.20.1, no NeoForge | World-load speedups | — | — | — | **Skip** |
| **Noisium** (`noisium`) | ⚠️ neoforge builds exist but **server_only**, project now **unlisted** | Worldgen speedup | ~0 client in multiplayer | — | — | **Skip** (client); good server candidate |
| **C2ME** (`c2me-neoforge`) | ✅ 1.21.1 neoforge exists | Parallel chunk gen/IO | ~0 client in multiplayer (server_side = required, `server_only_client_optional`) | Fundamentally incompatible with Moonrise (per Moonrise README) | — | **Skip** (client); good server candidate |
| **Moonrise** (`moonrise-opt`) | ✅ NeoForge 0.1.0-beta.15 (2025‑10‑10) | Chunk-system/entity-tracking rewrite; README: "can run on either the client or server, but primarily benefits the server … on the client … similar to Lithium" | Low client-side; **and it auto-disables conflicting parts of Lithium + FerriteCore**, both in the pack | 3 declared incompatible projects in the version metadata; README names **C2ME** as fundamentally incompatible | Med | **Skip** (client); evaluate as server mod knowing it partially supersedes Lithium/FerriteCore |
| **Fast Item Frames** (`fast-item-frames`) | ✅ 1.21.1 listed | Item frames become blocks instead of entities; dyeable/invisible/waxable | Med in frame-heavy builds | Requires **Puzzles Lib**; **requires install on client AND server**; converts existing item-frame entities on chunk load; interaction with Immersive Paintings 0.7.9 unverified | High | **Skip** for this client-only pass |
| **Fast Paintings** (`fast-paintings`) | ✅ 1.21.1 listed | Fixes painting rendering, custom models | Low | `client_and_server` (needs server install too); interaction with Immersive Paintings unverified | Med | **Skip** for this client-only pass |
| **Model Gap Fix** (`modelfix`) | ✅ 1.21-1.10 (2024‑08‑03) | Fixes MC‑73186 model seams | ~0 (its own FAQ: does not slow the game down; it's a visual fix, not perf) | — | Low | **Skip** (not a perf lever; add only if seams annoy) |
| **YOSBR** (`yosbr`) | ❌ Fabric/quilt only | Default options shipping | — | — | — | **Skip** (use Default Options) |
| **ThreadTweak** (`threadtweak`) | ❌ Fabric/quilt only; Smooth Boot itself is dead | Thread priority tuning | — | — | — | **Skip** |
| **Smooth Boot** | ❌ legacy, main defect fixed in 1.19.4 | — | — | — | — | **Skip** |
| **More Culling Extra** (`morecullingextra`) | ❌ fabric/quilt, ≤1.20.4 | Extra glass/pane/beacon culling | — | — | — | **Skip** |
| **Idle Boost** (`idle-boost`) | ✅ 1.21.1 listed | Background-window optimisation | 0 | Duplicate of Dynamic FPS 3.11.4 (already in pack) | — | **Skip** |
| **Smart Particles** (`smart-particles`) | ✅ 1.21.1 listed (2026‑09‑18) | Particle rendering + limits optimiser | Med (alternative to Particle Core) | Overlaps Particle Core — pick **one** | Med | **Skip** unless Particle Core misbehaves |
| **Cull Particles MultiLoader** | ✅ 1.21.1 listed but last update 2025‑01‑30 | Particle culling | Low‑Med | Superseded by Particle Core; unmaintained | Med | **Skip** |
| **Connectivity** | ❌ no Modrinth project (404); 1.20.1-era Forge only | Forge networking | — | — | — | **Skip** |
| **Ecstatic** (`ecstatic`) | ✅ 1.21.1 neoforge, but LOD mod | Alternative LOD | — | DH 3.3.2 is mandatory in this pack | — | **Skip** |
---
## Findings
### A. Availability verdicts (the "everyone recommends it" list)
1. **Exordium is not available for NeoForge 1.21.1 at all** — the Modrinth API returns an **empty list** for `loaders=["neoforge"]&game_versions=["1.21.1"]`, and the NeoForge builds that do exist are 1.21.6/1.21.8/1.21.10/1.21.11. The author's own project page says "(Neo)Forge support? No… no backports planned!", and the project status is **archived** with "This mod is not actively maintained!" `VERIFIED`/`DOCUMENTED` ([Modrinth](https://modrinth.com/mod/exordium), [API](https://api.modrinth.com/v2/project/exordium/version?loaders=%5B%22neoforge%22%5D)).
**→ The correct 1.21.1 NeoForge substitute is Gnetum** (see Finding B4), which does the same job with Sodium's own config API and is actively maintained.
2. **Krypton is Fabric-only**; the NeoForge route is **Krypton Reno** (`krypton-fnp`), whose README states it is "ported from Krypton Fabric, with some unique optimizations added. Supports NeoForge/Forge" and "continuously provides compatibility for stable versions (1.20.1, 1.21.1)". Client-relevant keys in its documented config: `clientEncrypt`, `connectionMicroOpt`, `bestVarLong`, `netty.allocatorMaxOrder`, `netty.happyEyeballs`. The chunk/block-entity culling keys (`culling.chunk_block`, `culling.chunk_light`, `culling.entity`, `culling.block`) are explicitly server-side. `VERIFIED`/`DOCUMENTED` ([Modrinth body](https://modrinth.com/mod/krypton-fnp)).
3. **Nvidium and VulkanMod are Fabric/quilt-only, and both are additionally unusable here**: Nvidium needs NVIDIA **Turing+ mesh shaders** (P1 = GTX 1060 Pascal, P2 = GTX 1080 Ti Pascal, P0 = AMD RDNA2 → all fail) and "disables itself when iris is actively using shaders"; VulkanMod's own wiki lists **Distant Horizons** as incompatible, and shaders would need Beryl instead of Iris. `VERIFIED` ([Nvidium](https://modrinth.com/mod/nvidium), [VulkanMod incompatible list](https://github.com/xCollateral/VulkanMod/wiki/Incompatible-mods)).
4. **Enhanced Block Entities, FastChest, Memory Leak Fix, ThreadTweak, More Culling Extra, YOSBR are all unavailable or obsolete on this stack.** EBE is `fabric/quilt` and stops at 1.21.4; FastChest is `fabric`; Memory Leak Fix stops at 1.20.4 and its README declares itself obsolete from 1.20.5 on ("All the memory leaks in minecraft previously fixed by MemoryLeakFix have been fixed"); More Culling Extra stops at 1.20.4. `VERIFIED`.
5. **Ksyxis / Noisium / C2ME / Moonrise / FastLoad give ~0 client-side benefit in multiplayer.** Ksyxis literally says "It has no effect on the client when playing multiplayer"; Noisium is `server_only` and is now **unlisted**; C2ME-NeoForge is `server_only_client_optional`; Moonrise's README says it "primarily benefits the server" and that client-side gains would "behave similar to Lithium" (already in the pack). Note Moonrise **auto-disables conflicting parts of Lithium and FerriteCore** — both in this pack — so installing it client-side would partially neutralise two existing mods for a marginal client gain. `VERIFIED`/`DOCUMENTED`.
### B. Available candidates — details worth knowing before you test
1. **More Culling (1.0.10, 2026‑09‑07) is Sodium‑0.8‑ready and mature.** Its 1.0.8 changelog is literally "fix crash with sodium 0.8" and 1.0.9 fixed "dont cull option not working with sodium". The Cloth Config dependency is required (`9s6osm5g` = `cloth-config`, 1.21.1 NeoForge ✅, huge install base). The wiki enumerates exactly what is culled: aggressive BlockState culling, Item Frame (3-face/back/map), **Leaves Culling**, Powder Snow, Cloud, Sign Text, Rain/Snow, End Gateway, Beacon Beam, Painting. There is a documented "don't cull" escape hatch (added in 1.0.2) and the leaves technique is a separate toggleable culling technique. `VERIFIED`/`DOCUMENTED` ([versions API](https://api.modrinth.com/v2/project/moreculling/version?loaders=%5B%22neoforge%22%5D&game_versions=%5B%221.21.1%22%5D), [wiki](https://github.com/FxMorin/MoreCulling/wiki)).
2. **More Culling + Better Leaves 9.6: use one leaves system, not two.** Motschen authors both **Better Leaves** (the rp in this pack) and **Cull Leaves**, and the CurseForge page for Better Leaves says plainly: "In version 8.1 and lower, this resourcepack adds no textures… This comes at the cost of performance, though. **I strongly recommend installing the Cull Leaves mod alongside Sodium/Embeddium to get more FPS.**" Cull Leaves 4.1.1's changelog adds "**Add support for Sodium 0.8.0**". There is also a community resourcepack "Enhanced Bushy Leaves (More Culling compat)" whose existence implies More Culling's leaves culling needs pack-side adjustment on bushy leaf models. `DOCUMENTED`/`COMMUNITY` ([Cull Leaves 4.1.1 API](https://api.modrinth.com/v2/project/cull-leaves/version?loaders=%5B%22neoforge%22%5D&game_versions=%5B%221.21.1%22%5D), [Better Leaves CF page](https://www.curseforge.com/minecraft/texture-packs/motschens-better-leaves)).
**→ Practical recommendation: add More Culling for everything *except* leaves, and/or add Cull Leaves for leaves. Testing both leaves implementations simultaneously is a false economy.**
3. **Sodium Leaf Culling is the trap.** It is technically available for NeoForge 1.21.1 (1.0.1) and is exactly the mod people recommend for bushy-leaf packs ("injects directly into Sodium's chunk mesh render pipeline… 'Solid Aggressive' … sizable performance boost when using shaders in a dense forest"), and a Reddit thread even claims it "may be more effective than MoreCulling … because it works directly with Sodium's renderer". **But** its two newest reports are `#18 Crashes with Sodium 0.8.11+ on 1.21.1` (opened 2026‑05‑07) and `#20 Compatibility with modern Sodium 0.8.12 alpha backports to 1.21.1` (2026‑05‑14), both **still open**, with no release since 2025‑04‑07. This pack runs Sodium **0.8.13**. `VERIFIED` ([issue list](https://github.com/txnimc/SodiumLeafCulling/issues?q=is%3Aissue), [versions](https://api.modrinth.com/v2/project/sodiumleafculling/version)).
**→ Skip. This is the single most likely way to add a startup crash to the pack.**
4. **Gnetum (3.3.6, 2026‑06‑24) is the maintained Exordium replacement and explicitly pairs with ImmediatelyFast.** Its README: "✔️ ImmediatelyFast: fully compatible and recommended." Its changelogs track the exact risks this pack would hit: 3.2.0 "Fixed Xaero's Minimap flickering" (pack has Xaero's Minimap 26.5.0 + World Map 1.46.0), 3.3.4 "Fixed GUI flickering with ImmediatelyFast HUD batching enabled", 3.3.5 "Added basic integration with Sodium 0.8+ Video Settings screen", 3.2.0 also allows caching of "all modded HUDs" (with an off switch). Author's published comparison claims ~1.9× FPS in a HUD-heavy 1.20.1 test — treat as directional, not as this pack's number. `VERIFIED`/`DOCUMENTED` ([Modrinth body](https://modrinth.com/mod/gnetum), [versions](https://api.modrinth.com/v2/project/gnetum/version?loaders=%5B%22neoforge%22%5D&game_versions=%5B%221.21.1%22%5D)).
5. **Ixeris is the highest-variance candidate: potentially huge on P1, risky on P1.** Its own benchmark table is unusually specific: at **8000 Hz polling, 12 FPS → 121 FPS**; at 2000 Hz, 76 → 135; at 500 Hz, 134 → 151; in menus (1000 Hz) 133 → 165 on Windows, and only **355 vs 320 FPS on Linux/X11, 298 vs 289 on Wayland**. So it is a Windows + high-polling-rate-mouse win (P1), near-noise on P0 (Steam Deck/Linux) and P2 (Linux). Its README also states "As of version 3.1.0 the requirements of thread safety in the GLFW documentation are strictly obeyed", and there's a documented config key `allowBusyWaitInFramerateLimiter` (≤1.21.11). Counter-evidence: an open Iris-conflict issue (#151, 26.3 NeoForge, Oct 2026) and open crash reports on older NVIDIA cards (#134) and Intel Gen7/8 alt-tab (#144). `VERIFIED`/`DOCUMENTED`/`COMMUNITY` ([Modrinth body](https://modrinth.com/mod/ixeris), [issues](https://github.com/decce6/Ixeris/issues?q=is%3Aissue)).
**→ Because P1 is a GTX 1060 (Pascal) on Windows, treat P1 Ixeris as the highest-risk, highest-reward single test in this report.**
6. **Chloride is a plausible "one mod, several features" option that specifically supports two things in this pack.** Its changelogs show 1.7.8 "Added **Sable support for entity culling**" (pack has Sable 2.0.5) and 1.7.9/1.8.0 "Updated to Sodium 0.8.12(-beta.1)"; 1.8.1 "Fixed chloride mixins for Leaves Block collides with sodiums mixins (only warns, but breaks our culling)". Its config moved to `chloride-client.toml` in 1.8.0. Features that overlap the pack: entity distance culling (vs EntityCulling 1.11.2), leaves culling (vs More Culling/Cull Leaves), solid chest/bed models (vs no FastChest — actually a genuine gap-filler here), "Hide JEI/REI/EMI overlay until you type anything in their search bars" (pack has JEI 19.57), fast language reload. It also declares one dependency-flagged incompatibility (its own changelog names "Smooth Skies"). `VERIFIED`/`DOCUMENTED` ([versions API](https://api.modrinth.com/v2/project/chloride/version?loaders=%5B%22neoforge%22%5D&game_versions=%5B%221.21.1%22%5D), [Modrinth body](https://modrinth.com/mod/chloride)).
7. **Better Block Entities 1.3.4 is explicitly built against this Sodium version** ("Minecraft 1.21.1 (Sodium 0.8.12‑0.8.13)") and requires only Sodium — but its README caps EMF compatibility: "the compatibility with EMF is still being worked on and **will only be available for 1.21.11 versions and above**", and it targets "Minecraft versions from 1.21.6 and above" going forward. Given this pack runs EMF 3.3.9 + ETF 7.2.4 + Fresh Animations + FA All Extensions, BBE on 1.21.1 is a genuine unknown for chest/sign/bed models. `VERIFIED`/`DOCUMENTED` ([versions](https://api.modrinth.com/v2/project/better-block-entities/version?loaders=%5B%22neoforge%22%5D&game_versions=%5B%221.21.1%22%5D), [Modrinth body](https://modrinth.com/mod/better-block-entities)).
8. **Super Resolution is the only large GPU-side lever for P0, and it is alpha-only on 1.21.1.** Its README lists "✅ Supported: Sodium, Iris, DistantHorizons, Embeddium, Voxy" and requires OpenGL 4.3+; its 1.21.1 NeoForge builds are **all** `alpha` (latest 0.9.2-alpha.2, 2026‑10‑03, a ~33 MB jar). Two relevant changelog facts: 0.8.3-alpha.5 "**Added Sodium 0.8.x compatibility for Minecraft 1.21.1**" and "**AMD FSR now supports Linux**"; and 0.9.0-alpha.1 marks Resolution Control/RenderScale/GPU Booster/GPU Tape incompatible. The critical caveat for *this* pack: the shader-interface integration is **not implemented by Complementary Reimagined** (issue #317 is labelled "On Hold"), so only the fallback FSR1 compatibility path applies; the mod author states "for FSR3, entity ghosting was severe" with depth-reprojection motion vectors. `VERIFIED`/`DOCUMENTED` ([versions](https://api.modrinth.com/v2/project/superresolution/version?loaders=%5B%22neoforge%22%5D&game_versions=%5B%221.21.1%22%5D), [CR issue #317](https://github.com/ComplementaryDevelopment/ComplementaryReimagined/issues/317)).
**→ P0 (Steam Deck, 1280×800, RDNA2) is the right first target; P1 (VRAM-bound GTX 1060) second; P2 (main-thread bound) is *not* an upscaling problem and SR should not be expected to help there.**
9. **Particle Core's evidence is author-published spark profiles, not a pack benchmark.** From its Modrinth body: without it "world rendering is at 41% of CPU time, with renderParticles the lions share of that at 25%"; with it "rendering is now at only 16% of CPU time, particle rendering basically a non-item"; and overlaid with Sodium, "About 5% CPU time improvement, or 50% relative improvement". The 0.3.x line added **async particle ticking (default on)**, with an explicit escape hatch (`disableOptimizations` in `particle_core_config_v[x].json`), plus documented per-type controls (`turnOffPotionParticles`, `disableParticles`, `reduceParticlesByType`, `reduceParticlesAllChance`, `reduceParticlesDecreasedChance`). Community guidance says **do not** run it with Async Particles (a different mod; not in the pack). `DOCUMENTED`/`COMMUNITY`.
10. **BadOptimizations' own numbers are small but honest, and it has a real incompatibility system.** Author's README: 1926–1955 → 2008–2023 FPS (no toasts) and 1351–1384 → 1414–1458 FPS (with toasts) on an i7‑12700 + RTX 3060 in Fabulously Optimized. Documented config keys include `enable_entity_renderer_caching`, `enable_block_entity_renderer_caching`, `enable_entity_flag_caching`, `enable_remove_redundant_fov_calculations`, `lightmap_time_change_needed_for_update`, `ignore_mod_incompatibilities`, `ignore_mod_cache_hooks`, `log_config`; and third-party mods can mark themselves incompatible with individual options. It also says "Dependencies? None." `DOCUMENTED`.
11. **Sodium Extra + Reese's Sodium Options are fresh for 1.21.1 and are the tuning surface, not the gain.** Sodium Extra 0.9.4 (2026‑09‑16) requires Sodium, and optionally integrates with RSO plus one other project. RSO 2.2.4 (2026‑09‑16) requires Sodium and supports **Controlify** controller navigation — which this pack already ships (Controlify 3.0.1), making it a genuinely useful Deck addition. `VERIFIED`/`DOCUMENTED`.
12. **Default Options (TwelveIterations) is the NeoForge answer to "ship tuned defaults".** It is on 1.21.1 + NeoForge and requires **Balm** (also 1.21.1 + NeoForge). Its documented workflow: build the desired state, run `/defaultoptions saveAll`, ship `config/defaultoptions/`, and **do not** ship `options.txt`/`servers.dat`; fresh installs then start with your defaults and user changes persist across updates. YOSBR does the same job but is Fabric/quilt-only ("Requires Fabric Loader"). `DOCUMENTED`.
13. **`Modelfix` (Model Gap Fix) is not a performance mod.** Its own FAQ: "Will this slow down my game? Not at all! Removing texture zooming just removes some code and has no impact… it could very slightly worse that vanilla [in one edge case]". The 1.21.1 NeoForge build is `1.21-1.10` from 2024‑08‑03. `DOCUMENTED`. → Not a lever; add only for cosmetic reasons.
### C. Cross-cutting compatibility notes for this specific pack
14. **Existing-stack risk you should check before benchmarking: Iris 1.8.x + Sodium 0.8.x on NeoForge 1.21.1 has a documented mixin incompatibility.** Sodium's own Discord/issue trail ("Sodium 0.8.12 alpha backports to 1.21.1" was announced via [sable#878](https://github.com/ryanhcode/sable/issues/878)) plus a third-party CurseForge mod "Iris-Sodium Compat" whose description reads: "The latest releases of Iris and Sodium on 1.21.1 NeoForge don't work together. This fixes the issue. A simple compatibility mod between Iris 1.8.12 and Sodium 0.8.12 for NeoForge 1.21.1, fixing a missing mixin issue that's been plaguing the 1.21.1 NeoForge community for some time." `COMMUNITY`/`DOCUMENTED` ([CurseForge](https://www.curseforge.com/minecraft/mc-mods/iris-sodium-compat)).
**→ This is about the pack's *current* stack (Iris 1.8.14-beta.1 + Sodium 0.8.13), so verify the pack boots cleanly with shaders before attributing any new frame-time delta to a new mod.**
15. **More Culling × Create/Flywheel:** the only recent report is [MoreCulling#475 "Create Fly problem"](https://github.com/FxMorin/MoreCulling/issues/475) (2026‑10‑08), where the reporter says entity blocks disappear at some angles; the maintainer replied: "are you sure its caused by moreculling? because it doesn't touch blockentity renderer, also you seems to have blockstate culling disabled so it doesn't even touch blocks". Unresolved as of writing → `COMMUNITY`, low weight, but worth a Create-specific smoke test.
16. **Distinct pack-internal redundancies to avoid stacking:** EntityCulling 1.11.2 vs Chloride entity culling; More Culling leaves vs Cull Leaves vs Chloride leaves; Particle Core vs Smart Particles vs Cull Particles; Dynamic FPS 3.11.4 vs Idle Boost; LambDynamicLights 4.8.11 vs Sodium Dynamic Lights. Community consensus in the NeoForge 1.21.1 threads is that "MoreCulling and EntityCulling should play nice together" and that stacking more culling mods is not automatically better. `COMMUNITY`.
17. **No credible 2025‑2026 "best NeoForge 1.21.1 perf mods" list surfaced a major candidate I have not already covered.** The frequently-cited lists are Reddit threads (CAPTCHA-blocked for automated reading) and SEO guides; the mods they name on the client side are Sodium/Lithium/FerriteCore/ModernFix/ImmediatelyFast/EntityCulling/BadOptimizations/Cull Leaves/More Culling/Krypton/Ixeris/Gnetum — all covered above. The only additional NeoForge-1.21.1 client-optimisation projects Modrinth surfaced that are not in the list above were: Annus (networking), ORB (recipe book), Output Booster/inputbooster (input polling, AI-content flagged), AutoTune FPS (AI-generated, 0 followers), Fandai Engine (unsubstantiated "rewrites the entire rendering pipeline", 891 downloads) — none of which I would add on evidence grounds. `COMMUNITY`/`INFERENCE`.
---
## Top levers (ranked)
Ordered by (expected gain × confidence) / risk, for *this* fleet.
| # | Lever | Expected impact | Best fleet machine | Risk | Confidence |
|---|---|---|---|---|---|
| 1 | **More Culling 1.0.10 + Cloth Config** (leaves option off) | Med | all three; most on P1/P2 in dense bases | Low‑Med | High |
| 2 | **Particle Core 0.3.3** | Med‑High in particle-heavy scenes | P0, P1 (and P2 in Create/Alex's Mobs areas) | Med (async ticking off-switch) | Med‑High |
| 3 | **BadOptimizations 2.4.1** | Low‑Med (lightmap/tick) | all, incl. tick-bound P2 | Low | Med‑High |
| 4 | **Gnetum 3.3.6** (the Exordium replacement) | Med in HUD-heavy scenes | P0 (small screen, controller), P1 | Low‑Med | Med |
| 5 | **Ixeris 4.6.8** | High on P1 only if high-polling mouse; ~0 on Linux | P1 (and P2 if Windows-style mouse) | Med‑High | Med |
| 6 | **Super Resolution** (FSR1/SGSR, shaders in compatibility mode) | High on P0; Med on P1; ~0 on P2 | P0 first | Med‑High (alpha only on 1.21.1) | Low‑Med |
| 7 | **Better Block Entities 1.3.4** | High in chest/storage-heavy builds | all (base-build dependent) | Med (EMF compat not on 1.21.1) | Med |
| 8 | **Cull Leaves 4.1.1 + MidnightLib** (instead of, not in addition to, More Culling's leaves option) | Low‑Med, leaves only | all | Low | Med‑High |
| 9 | **Sodium Extra 0.9.4** (unlocks particle/animation/entity levers) + **Reese's Sodium Options 2.2.4** (Controlify-aware tuning UI) | Low direct / high enabling | P0, P1 | Low | Med‑High |
| 10 | **Flerovium 1.1.3** | Med on items/particles/entities | all | Med‑High (glitch-prone, alpha branch) | Low‑Med |
| 11 | **Default Options + Balm** | 0 runtime; guarantees tuned baseline ships | all | Low | High |
| 12 | **Chloride 1.8.1** (only if consolidating culling/JEI/chest models) | Med but overlapping | P2 (Sable-aware culling) | Med | Low‑Med |
| 13 | **Krypton Reno** — better judged as a *server* mod | Low client / Med server | P1/P2 chunk-packet stalls | Med (license, native libs) | Low‑Med |
| — | **Skip outright:** Exordium, Sodium Leaf Culling, ScalableLux, Nvidium, VulkanMod, EBE, FastChest, Memory Leak Fix, Ksyxis (client), FastLoad, Noisium (client), C2ME (client), Moonrise (client), Sodium Extras, Sodium Options API, Sodium Dynamic Lights, ThreadTweak/Smooth Boot, More Culling Extra, YOSBR, Idle Boost, Fast Item Frames, Fast Paintings, Model Gap Fix | — | — | — | High |
---
## Open questions to verify by benchmark
1. **What is each machine's actual bottleneck?** P0 = iGPU/GPU-bound at 1280×800; P1 = 6 GB VRAM; P2 = main-thread + 8 GB RAM. This determines whether Super Resolution (GPU), Particle Core (CPU/GPU), or nothing helps P2. Resolve with frame-time breakdown (not FPS) before adding anything.
2. **Does the pack's *current* Iris 1.8.14-beta.1 + Sodium 0.8.13 NeoForge 1.21.1 stack need the third-party "Iris-Sodium Compat" patch?** Boot with and without shaders and compare log mixin warnings before attributing any delta to a new mod.
3. **Ixeris on P1 (Windows + GTX 1060):** does it trigger the older-NVIDIA-card crash class (issue #134) or any Iris/DH interaction? Measure at the mouse's real polling rate.
4. **Leaves, three-way A/B:** (a) Better Leaves 9.6 alone, (b) + More Culling leaves option, (c) + Cull Leaves. Compare FPS *and* leaf appearance with Complementary Reimagined and with shaders off, in a dense forest.
5. **Better Block Entities vs EMF 3.3.9 + ETF 7.2.4 + Fresh Animations on 1.21.1:** do chest/sign/bed/shulker models break? BBE's EMF compat is documented as 1.21.11+ only.
6. **Particle Core async ticking stability** across Create 6.0.10 contraptions, Alex's Caves 2.0.10, Alex's Mobs and Twilight Forest 4.8.3345 particles; verify `disableOptimizations` is a sufficient fallback.
7. **Gnetum HUD caching vs Xaero's Minimap 26.5.0 / World Map 1.46.0 / JEI 19.57 / Travelers Titles / Legendary Tooltips / Inventory Particles** — flicker history exists (fixed in 3.2.0/3.3.4), re-verify on this HUD set.
8. **Flerovium backface culling vs Better Combat 2.4.0, Emotecraft 2.4.12, 3D Skin Layers 1.11.3, EMF+FA, Exposure 1.9.18 viewfinder** — look for missing faces / inverted normals, not just FPS.
9. **Super Resolution on P0 only:** FSR1-but-not-FSR3 with Complementary Reimagined r5.9.3, at 1.3–1.7× SR ratio, with Distant Horizons enabled; check DH LOD seam/quality and any RADV-specific artifacts.
10. **More Culling × Create 6.0.10/Flywheel 1.0.6 + Sable 2.0.5 + Create Aeronautics 1.3.2:** smoke-test moving contraptions/aeronautics physics frames for disappearing block entities (issue #475 class).
11. **Krypton Reno client-only vs client+server:** its client-side keys are only `clientEncrypt`/`connectionMicroOpt`/`bestVarLong`/netty sizing; the chunk-packet culling needs the server. Decide whether it is worth a server deployment at all.
12. **Chloride overlap audit:** if added, confirm EntityCulling 1.11.2 and Chloride's entity culling do not double-tax or disagree (whitelist interactions), and confirm its declared incompatible mod is not something the pack uses.
---
## Sources
**Kept (authoritative / primary):**
- Modrinth v2 API `/project/...` and `/project/.../version?loaders=["neoforge"]&game_versions=["1.21.1"]` — the availability backbone for every "y/n + version" claim (Sodium Extra, RSO, Sodium Options API, BadOptimizations, Flerovium, Ixeris, Sodium Leaf Culling, Cull Leaves, More Culling, Particle Core, Better Block Entities, Chloride, Super Resolution, Gnetum, Moonrise, Chloride, Default Options, Balm, EBE, FastChest, Nvidium, VulkanMod, Krypton, Ksyxis, Noisium, C2ME, Moonrise, Fast Item Frames, More Culling Extra, Memory Leak Fix, Lithium, Modelfix, Exordium, Sodium Extras, YOSBR, ThreadTweak, FastLoad, Krypton Reno, ScalableLux).
- https://modrinth.com/mod/exordium + https://github.com/tr7zw/Exordium/releases — "no backports planned", NeoForge builds 1.21.6+.
- https://github.com/txnimc/SodiumLeafCulling/issues?q=is%3Aissue — open Sodium‑0.8.11+ crash reports (the decisive "skip").
- https://github.com/FxMorin/MoreCulling/wiki (Home) — exact list of culling techniques.
- https://www.curseforge.com/minecraft/texture-packs/motschens-better-leaves + https://modrinth.com/mod/cull-leaves — same author recommends pairing Cull Leaves with Better Leaves.
- https://modrinth.com/mod/gnetum — ImmediatelyFast compatibility, Xaero's/IF flicker fixes, Sodium 0.8 config integration.
- https://modrinth.com/mod/ixeris — author benchmark table incl. per-OS numbers.
- https://modrinth.com/mod/krypton-fnp (Krypton Reno) — documented config keys, incompatibility list, server-vs-client split.
- https://github.com/ComplementaryDevelopment/ComplementaryReimagined/issues/317 — CR has no Super Resolution shader interface; FSR3 ghosting; motion-vector caveats.
- https://modrinth.com/mod/nvidium and https://github.com/xCollateral/VulkanMod/wiki/Incompatible-mods — Turing+ requirement; DH listed incompatible.
- https://modrinth.com/mod/moonrise-opt — "primarily benefits the server"; C2ME fundamentally incompatible; auto-disables conflicting Lithium/FerriteCore parts.
- https://github.com/FxMorin/MoreCulling/issues/475 — the only recent Create/Flywheel culling report (unconfirmed).
- https://www.curseforge.com/minecraft/mc-mods/iris-sodium-compat — third-party fix for the 1.21.1 NeoForge Iris/Sodium mixin gap.
**Dropped:**
- 9minecraft / minestrator / ouiheberg / topmoddedservers "best perf mods" listicles — SEO content, no version-date traceability, no primary confirmation.
- Reddit threads (r/feedthebeast, r/DistantHorizons, r/ModdedMinecraft) — bot-blocked on read, and only used as corroboration for the two claims I tagged `COMMUNITY` (More/EntityCulling coexistence, ScalableLux/DH breakage). Answer‑overflow/quorum mirrors used only for the DH+Starlight claim.
- CurseForge mod pages other than for file-level version confirmation — Modrinth API gave the same metadata with machine-readable versions.
- "Async Particles", "Optimized Block Entities", "Vanillin", "OptiLeaves", "Valkyrien", "OptiLeaves" — mentioned in community lists but not verified for NeoForge 1.21.1; out of budget.
---
## Gaps
- **No candidate has a published benchmark on a 212-mod pack with DH + Iris shaders.** Every gain figure quoted here is either the author's own micro-benchmark or a differently-configured pack (Fabulously Optimized, superflat worlds). Treat all impact ratings as directional.
- **Could not resolve the three project IDs declared incompatible by Moonrise 1.21.1 NeoForge builds** (only their IDs were in the API response; the README names C2ME explicitly and says Lithium/FerriteCore are auto-handled). Low priority since Moonrise is a server-side recommendation.
- **Could not find an official Distant Horizons "incompatible mods" page.** The ScalableLux/DH incompatibility and "More Culling is a 'smaller' DH-safe mod" claims rest on community answers (DH Discord mirrors), not on DH docs. If DH compat for any culling mod is a deciding factor, ask in the DH Discord or test directly.
- **Exordium alternatives beyond Gnetum were not searched exhaustively** (e.g. other HUD-caching mods for 1.21.1 NeoForge). Gnetum covers the use case and is the maintained option.
- **Chloride's declared incompatible project was inferred from its own changelog ("Smooth Skies"), not from the version dependency metadata**, which exposes only the project ID. Verify before installing.
- **Verification of `Sodium Extra`'s optional dependency projects** (Modrinth IDs `YL57xq9U`, `EE4vxUHj`) was not completed; both are optional, so this only matters if you try to add Sodium Extra without RSO.
@@ -0,0 +1,150 @@
# Research: JVM, launcher, OS & driver levers — Onion MC client (NeoForge 1.21.1, Java 21, ~212 mods, Distant Horizons mandatory)
## Summary
For a DH-mandatory 200+ mod NeoForge 1.21.1 client, the single biggest platform lever is getting off stock G1 GC (Distant Horizons itself warns about it) onto either Generational ZGC (`-XX:+UseZGC -XX:+ZGenerational` on JDK 21) or Shenandoah, with heap sized to real headroom (5–6 GB on the 16 GB Deck and the 8 GB-free Linux box, 6–8 GB on the 32 GB Windows box) — oversizing costs more than it buys. Most OS/driver levers are small but cheap: `mesa_glthread=true` + `gamemoderun` on the Steam Deck, explicit `__GL_THREADED_OPTIMIZATIONS=0` on Linux NVIDIA (Sodium already sets it in source), and NVCP "Prefer maximum performance" on Windows. GraalVM is a benchmark-first experiment only: GraalVM for JDK 21 does not support Generational ZGC and real-world NeoForge 1.21.1 miscompiles exist.
---
## Findings
### 1. The GC baseline: G1 + why DH complains about it
- **NeoForge 1.21.1 requires Java 21**, so GC options are: G1 (default), Parallel (default for server-class? no—G1 default since JDK 9), Generational ZGC, Shenandoah. [NeoForge docs](https://docs.neoforged.net/) (DOCUMENTED).
- Vanilla ships its own G1 tuning in the client JSON: `-XX:G1NewSizePercent=20 -XX:G1ReservePercent=20 -XX:MaxGCPauseMillis=50 -XX:G1HeapRegionSize=32M`. Widely quoted from Mojang's version JSON. [Minecraft Forum](https://www.minecraftforum.net/forums/support/java-edition-support/3082919-jvm-arguments-using-g1gc-for-minecraft) (COMMUNITY, corroborated across 2 threads).
- **Distant Horizons logs at startup**: `[Render thread/WARN]: Distant Horizons: G1 Garbage collector detected. This can cause FPS stuttering. It's recommended to use a concurrent garbage collector like ZGC (Java 21+) or Shenandoah (Java 8 through 17) for a smoother experience.` DH is closed-source; exact wording verified from two independent log excerpts. [Minecraft Forum log](https://www.minecraftforum.net/forums/support/java-edition-support/3228579-modpack-randomly-losing-fps-and-sometimes-crashing), [New Eden modpack page quoting the warning](https://modrinth.com/project/7jwudrLZ) (DOCUMENTED — it is DH's own runtime warning). Newer DH writes it to the log only instead of an on-screen alert [Reddit r/ModdedMinecraft](https://www.reddit.com/r/ModdedMinecraft/comments/1pnk5uc/getting_this_message_every_time_i_launch_my_game/) (COMMUNITY). Mechanism is consistent with DH's large LOD allocations → G1 old-gen/mixed pauses = visible stutters (INFERENCE from the warning text + DH's memory behavior).
- Practical note: on Java 21, `-XX:+UseZGC` **alone** selects the *legacy non-generational* ZGC. Generational ZGC (what you want, and what "Java 21+" implies) needs `-XX:+UseZGC -XX:+ZGenerational`. Generational mode is only the *default* on JDK 23+ (JEP 474); non-gen was removed in JDK 24 (JEP 490). [OpenJDK ZGC wiki](https://wiki.openjdk.org/display/zgc/Main), [JEP 474](https://openjdk.org/jeps/474) (VERIFIED).
### 2. G1 tuned (Aikar-style) vs client
- Aikar's flags were tuned for Paper *servers* with large heaps (`MaxGCPauseMillis=200`, `G1NewSizePercent=30`…). [aikar.co via brucethemoose Benchmark.py](https://github.com/brucethemoose/Minecraft-Performance-Flags-Benchmarks/blob/main/Benchmark.py) (DOCUMENTED origin, COMMUNITY applicability).
- brucethemoose (benchmarked repo, most credible MC flags source): Aikar's high `G1NewSizePercent` effectively clamps `MaxGCPauseMillis`, producing long stutters on some *clients*; clients also fill old gen far slower than a busy server. He ships a client-flavored G1 set: `-XX:+UseG1GC -XX:MaxGCPauseMillis=37 -XX:G1HeapRegionSize=16M -XX:G1NewSizePercent=23 -XX:G1ReservePercent=20 -XX:G1MixedGCCountTarget=3 -XX:G1HeapWastePercent=20 -XX:InitiatingHeapOccupancyPercent=10 -XX:G1RSetUpdatingPauseTimePercent=0 -XX:MaxTenuringThreshold=1 -XX:GCTimeRatio=99 …` [README](https://github.com/brucethemoose/Minecraft-Performance-Flags-Benchmarks) (DOCUMENTED, benchmarked; **Java-17-era repo — revalidate on 21**).
- Expectation: tuned G1 remains the best default on **4C/8T CPUs with tight RAM** (P0, P2). It has no concurrent-cycle CPU tax like ZGC/Shenandoah. (INFERENCE from brucethemoose's guidance "G1GC on servers/clients with less RAM and fewer cores".)
### 3. Generational ZGC on JDK 21
- Flags for this pack on JDK 21: `-XX:+UseZGC -XX:+ZGenerational -Xmx<N>G` (+ optionally `-XX:SoftMaxHeapSize`, `-XX:ConcGCThreads`). [OpenJDK ZGC wiki](https://wiki.openjdk.org/display/zgc/Main) (VERIFIED).
- Sizing rule from the ZGC docs: heap must hold live-set **plus headroom for allocations while a cycle runs**; "in general, the more memory you give to ZGC the better", balanced against waste; `SoftMaxHeapSize` lets you cap heuristic target below Xmx. This makes ZGC a poor fit for P2 (only ~8 GB free RAM total) and marginal on the Deck's shared 16 GB. (VERIFIED docs + INFERENCE for fleet.)
- ZGC pauses are <1 ms regardless of heap, but it costs throughput: brucethemoose measured "a significant client FPS hit" from ZGC on an 8C/16T laptop, and notes ZGC wants *more* RAM and a raised `ConcGCThreads` cap (default = logical cores/4; he suggests 2 on 4-thread CPUs). [README](https://github.com/brucethemoose/Minecraft-Performance-Flags-Benchmarks) (DOCUMENTED/COMMUNITY benchmark).
- DH's warning explicitly blesses ZGC as the primary fix; the DH community answer is always `-XX:+UseZGC` on Java 21 [r/MinecraftHelp NeoForge 1.21.1 thread](https://www.reddit.com/r/MinecraftHelp/comments/1svzilf/java_neoforge_1211_distant_horizons_g1_garbage/) (COMMUNITY).
- **GraalVM for JDK 21 does not support Generational ZGC** ("The following JDK 21 features are not available: JEP 439 Generational ZGC"). So GenZGC ⇒ a plain OpenJDK distro (Temurin et al.), not GraalVM 21. [GraalVM JDK 21 release notes](https://www.graalvm.org/release-notes/JDK_21/) (VERIFIED).
### 4. Shenandoah
- Not in Oracle JDK; available in Temurin/OpenJDK-based builds "from version 11 onward". [Red Hat Temurin docs](https://docs.redhat.com/en/documentation/red_hat_build_of_openjdk/21/html-single/getting_started_with_eclipse_temurin/index); not every build ships it — verify with `java -XX:+UseShenandoahGC -version` [Stack Overflow](https://stackoverflow.com/questions/73344814/openjdk-17-cant-use-shenandoahgc) (DOCUMENTED + caveat).
- brucethemoose's benchmarked verdict: **"I recommend Shenandoah on clients"**; flags `-XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGuaranteedGCInterval=1000000 -XX:AllocatePrefetchStyle=1`; it dislikes `AllocatePrefetchStyle=3`; mode/heuristic knobs changed little in his tests. [README](https://github.com/brucethemoose/Minecraft-Performance-Flags-Benchmarks) (DOCUMENTED).
- Like ZGC it is concurrent, so it eats background CPU on 4C/8T — cheap stutter fix, small FPS tax. (INFERENCE.)
### 5. Heap sizing for a 200+ mod pack, and the cost of oversizing
- Prism's own wiki: "MORE RAM ALLOCATED DOESN'T MEAN BETTER PERFORMANCE! … a huge heap makes each garbage collection pause longer, which shows up in game as a stutter." [Prism Java settings wiki](https://prismlauncher.org/wiki/help-pages/java-settings/) (DOCUMENTED).
- brucethemoose: "Allocating too much memory can break gc or just slow Minecraft down, even if you have plenty to spare… allocate only as much as it needs (usually less than 8G)"; set `-Xms` = `-Xmx` **except** on low-RAM systems where Xms should stay below Xmx. [README](https://github.com/brucethemoose/Minecraft-Performance-Flags-Benchmarks) (DOCUMENTED).
- For this pack: DH's LOD cache, Alex's Caves/Mowzie/Geckolib models and 212 mods plausibly put live-set near 3–5 GB with LODs loaded (INFERENCE — must be measured with Spark/F3). Recommended: **P0 5–6 GB, P1 6–8 GB, P2 5 GB max** (P2 has ~8 GB free for the whole OS+game). Oversizing harms: longer/less frequent G1 pauses, more RSS stolen from OS/page cache (fatal on 8 GB), ZGC cycles sweeping more memory. (INFERENCE, consistent with both sources above.)
### 6. `-XX:+AlwaysPreTouch` and Transparent Huge Pages (Linux)
- ZGC docs: if low latency is the goal, run Xms=Xmx and use `-XX:+AlwaysPreTouch` to page memory in **before** the app starts, avoiding commit/uncommit latency; uncommit can hurt latency. (VERIFIED: [ZGC wiki](https://wiki.openjdk.org/display/zgc/Main).)
- THP: `-XX:+UseLargePages -XX:+UseTransparentHugePages` issues `madvise(MADV_HUGEPAGE)`; requires kernel THP set to `madvise` in `/sys/kernel/mm/transparent_hugepage/enabled` (and, **for ZGC only**, `shmem_enabled=advise`). Some distros force `always`/`never` — check sysfs before benchmarking. (VERIFIED: [ZGC wiki](https://wiki.openjdk.org/display/zgc/Main).)
- brucethemoose: large pages "improve performance of Minecraft servers and clients"; on Windows they silently fail without admin/SeLockMemoryPrivilege — on Linux prefer `UseTransparentHugePages`. (DOCUMENTED: same README.)
- Cost: slower startup (pre-touch of full heap) and, with THP, potential memory bloat — risky on the 8 GB P2. Use on P1/P0, skip or test carefully on P2. (INFERENCE.)
### 7. GraalVM CE/EE (Oracle GraalVM) vs Temurin
- Since the "GraalVM for JDK 21" rebrand there is GraalVM Community Edition (free) and Oracle GraalVM; CE for JDK 21 exists as a full distribution. [GraalVM JDK 21 notes](https://www.graalvm.org/release-notes/JDK_21/) (VERIFIED). Note brucethemoose's README is older (recommends "EE 22.x", Java 17) — treat his EE claims as dated but directionally useful.
- Claims: client FPS gains are modest; chunk-gen/server workloads up to +20% on GraalVM EE. (DOCUMENTED-era 17: [README](https://github.com/brucethemoose/Minecraft-Performance-Flags-Benchmarks).)
- Constraints: **must use G1 with his Graal flags; GraalVM (of his era) doesn't work with ZGC/Shenandoah** — and GraalVM 21 verifiably lacks GenZGC (see §3). (DOCUMENTED both.)
- Compatibility risk is real on 1.21.1: a dedicated fix repo exists because "Integrated Scripting 1.0.22" crashes on GraalVM JDK 21 + NeoForge 1.21.1 [jan-guenter/integratedscripting-graalfix](https://github.com/jan-guenter/integratedscripting-graalfix); players report packs failing on GraalVM Java 21 [r/feedthebeast](https://www.reddit.com/r/feedthebeast/comments/18hta1r/how_i_about_doubled_my_minecraft_performance/) (COMMUNITY). With Flywheel 1.0.6 + Veil/Lodestone + Sable/Aeronautics JIT-heavy stacks in this pack, GraalVM is a **trial**, never the fleet baseline. (INFERENCE.)
- Obydux's "Minecraft-GraalVM-Flags" is **server startup** flags for GraalVM EE Java 17 — not applicable to a 1.21.1 client; dropped. [Obydux repo](https://github.com/Obydux/Minecraft-GraalVM-Flags).
### 8. Prism Launcher settings (applies to P0 flatpak and P2)
- **Memory**: per-instance Settings → Java → memory box (Xms/Xmx). Prism warns against over-allocation (see §5). [Java settings wiki](https://prismlauncher.org/wiki/help-pages/java-settings/) (DOCUMENTED).
- **Java path / JVM args**: same Java page — set the Temurin/GraalVM `bin/java` path and paste JVM arguments. (DOCUMENTED: same page.)
- **Wrapper command**: "Wrapper command allows launching using an extra wrapper program (like 'optirun' on Linux)" — this is where `gamemoderun` goes (Settings → Custom Commands). [Custom commands wiki](https://prismlauncher.org/wiki/help-pages/custom-commands/) (DOCUMENTED).
- **Environment variables**: Prism passes env vars to the java runtime (instance-level). [Env vars wiki](https://prismlauncher.org/wiki/help-pages/environment-variables/) (DOCUMENTED). Use for `mesa_glthread=true`, `__GL_THREADED_OPTIMIZATIONS=0`, `vblank_mode=0` etc.
- Flatpak Prism on the Deck: official install is via their own flatpak repo [Prism Steam Deck download](https://prismlauncher.org/download/steam-deck/); gamemode from inside a flatpak goes through the xdg-desktop-portal GameMode interface (proxies to Feral's daemon with PID mapping/permission checks) [portal docs](https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.GameMode.html); some flatpaks need `flatpak override --talk-name=com.feralinteractive.GameMode` for direct D-Bus access [example](https://github.com/an-anime-team/flatpak-builds) (DOCUMENTED + COMMUNITY).
### 9. Linux driver/OS levers
- **mesa_glthread**: moves GL dispatch to a second thread inside Mesa. radeonsi (which is what Minecraft's OpenGL actually uses on the Deck's Van Gogh APU — RADV is Vulkan and irrelevant here) enables glthread **by default since Mesa 22.3**; Mesa has also shipped a driconf default for the *official Minecraft launcher* app since 22.0 — a Prism-launched `java` process should not be assumed to match those overrides, so set `mesa_glthread=true` explicitly. [Phoronix](https://www.phoronix.com/news/Mesa-22.3-RadeonSI-glthread-On), [PCGamingWiki](https://www.pcgamingwiki.com/wiki/Mesa), [MangoHud issue #676](https://github.com/flightlessmango/MangoHud/issues/676) (VERIFIED driver behavior; explicit-set recommendation INFERENCE). Glthread is "a big help to CPU-bound games" — i.e., exactly the Deck's 4-core Zen2 profile. (Phoronix, VERIFIED quote.)
- **NVIDIA proprietary**: Sodium 0.8.x/0.6.x **sets `__GL_THREADED_OPTIMIZATIONS=0` itself** on Linux right before GLFW window creation (`Libc.setEnvironmentVariable("__GL_THREADED_OPTIMIZATIONS", "0")` in `NvidiaWorkarounds`), because threaded optimization causes stalls with Sodium. [NvidiaWorkarounds.java (1.21.1/stable)](https://github.com/CaffeineMC/sodium/blob/1.21.1/stable/common/src/boot/java/net/caffeinemc/mods/sodium/client/compatibility/workarounds/nvidia/NvidiaWorkarounds.java) (VERIFIED). Setting it also in Prism env vars is harmless belt-and-braces.
- **gamemode**: `gamemoderun` as Prism wrapper; requests CPU governor + GPU perf level. [Feral GameMode](https://github.com/FeralInteractive/gamemode) / Prism wiki (DOCUMENTED, see §8).
- Optional: run java at raised priority (`renice` post-launch; negative nice needs root) — brucethemoose. (DOCUMENTED: README.)
### 10. Windows (P1) NVIDIA control panel
- **Threaded optimization is effectively not user-controllable for Minecraft**: Sodium's author (jellysquid3): the NVIDIA driver "forcefully enables Threaded Optimizations regardless of the control panel settings" because it hardcodes Minecraft detection for all `javaw.exe` processes. [sodium#2061](https://github.com/CaffeineMC/sodium/issues/2061) (VERIFIED, maintainer statement).
- What Sodium does on Windows instead: spoofs the process command line to `net.caffeinemc.sodium / net.minecraft.client.main.Main /` (keeps the driver's "good" Minecraft heuristics), then enables `GL_DEBUG_OUTPUT_SYNCHRONOUS` (KHR_debug) at context creation, which forces the driver to disable threaded command submission. It also detects the broken driver range x.y.15.2647–3622 (526.47 → before 536.23). [NvidiaWorkarounds.java](https://github.com/CaffeineMC/sodium/blob/1.21.1/stable/common/src/boot/java/net/caffeinemc/mods/sodium/client/compatibility/workarounds/nvidia/NvidiaWorkarounds.java), [sodium#1486 referenced in source](https://github.com/CaffeineMC/sodium/issues/1486) (VERIFIED).
- NVCP **Power management mode → Prefer maximum performance** remains a legitimate per-app setting for stabilizing clocks; community standard advice for MC. [Minecraft Forum thread on threaded optimization/NVCP](https://www.minecraftforum.net/forums/minecraft-java-edition/discussion/3144703-nvidia-threaded-optimization), [r/feedthebeast NVCP report](https://www.reddit.com/r/feedthebeast/comments/vzkoc0/great_performance_improvement_using_nvidia/) (COMMUNITY). Note P1 is VRAM-bound (1060 6GB): platform levers help pacing, not the VRAM wall (INFERENCE).
### 11. Steam Deck (P0) specifics
- Install Prism via its official flatpak repo; add Prism (or `flatpak run org.prismlauncher.PrismLauncher --launch <Instance>` desktop entry) as a non-Steam-game so it launches in Game Mode under gamescope (SteamOS's compositor — no extra gamescope config needed at 1280×800 native). [Prism Steam Deck page](https://prismlauncher.org/download/steam-deck/), [RetroDECK wiki example with `--launch`](https://retrodeck.readthedocs.io/en/latest/wiki_experiments/desktop-launch/desktop-launch-prism/), [GamingOnLinux guide](https://www.gamingonlinux.com/2022/10/minecraft-on-steam-deck-guide-with-prism-launcher-and-gamepad-support/) (DOCUMENTED + COMMUNITY).
- **UMA frame buffer (VRAM) is 1 GB reserved by default**; BIOS allows 4 GB. With DH + shaderpacks + 212 mods on an 8CU iGPU, raising it to 4 GB is a common stability/smoothness tweak (2560×1600 shared-memory reads dominate on Deck). [Windows Central how-to](https://www.windowscentral.com/gaming/pc-gaming/how-to-increase-vram-on-steam-deck), [PiMyLifeUp](https://pimylifeup.com/steam-deck-increase-vram/) (COMMUNITY).
- TDP cap (e.g., 8–10 W) stabilizes clocks → steadier frame pacing on battery; standard Deck practice. (COMMUNITY.)
- GC choice on 4C/8T Zen2 with 16 GB *shared* RAM: tuned G1 or Shenandoah; avoid ZGC (concurrent-cycle CPU tax + heap headroom taken from the iGPU's shared pool). (INFERENCE from §2–§5.)
### 12. Sodium's "NO_ERROR_CONTEXT" workaround — what it actually means
- Sodium's options include **"Use No-Error Context"** (`sodium.options.use_no_error_context`, tooltip: "Creates the OpenGL context without error checking") — i.e., Sodium requests a `KHR_no_error` GL context by default to shave per-call driver error-checking overhead. (VERIFIED from Sodium's own lang strings: [sodium#3407](https://github.com/CaffeineMC/sodium/issues/3407).)
- When the installed driver can't provide that, Sodium logs `Sodium has applied one or more workarounds …: [NVIDIA_THREADED_OPTIMIZATIONS_BROKEN, NO_ERROR_CONTEXT_UNSUPPORTED]` — `NO_ERROR_CONTEXT_UNSUPPORTED` simply means the no-error-context optimization was **disabled** and a normal error-checking context is used; "not necessarily an issue, but it may result in certain features or optimizations being disabled." [sodium#2961 log excerpt](https://github.com/CaffeineMC/sodium/issues/2961) (VERIFIED).
- User-facing takeaway: don't try to "fix" this warning; it's informational. Performance impact on supported drivers is small. (INFERENCE.)
---
## Lever table (recommended value per fleet machine)
| Lever | Recommended value | P0 Deck (Zen2/16GB shared) | P1 Win i7-7700K/1060 6GB | P2 Linux i7-6700K/1080 Ti, 8GB free | Expected impact | Risk | Confidence |
|---|---|---|---|---|---|---|---|
| GC engine | P0/P2: Shenandoah (`-XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGuaranteedGCInterval=1000000`) or tuned G1; P1: Generational ZGC (`-XX:+UseZGC -XX:+ZGenerational`) | tuned G1 or Shenandoah | GenZGC (32 GB RAM makes headroom free) | tuned G1 or Shenandoah (not ZGC: RAM+CPU poor fit) | **High** (removes DH-flagged G1 stutters) | Low (config-only) | Med-High |
| G1 tuning (if G1) | brucethemoose client set: `MaxGCPauseMillis=37, G1NewSizePercent=23, G1HeapRegionSize=16M, G1MixedGCCountTarget=3, G1HeapWastePercent=20, IHOP=10, MaxTenuringThreshold=1` | yes | fallback if GenZGC regresses | yes | **Med** (1% lows) | Low | Med |
| Heap size | P0: 5–6 GB · P1: 6–8 GB · P2: 5 GB (Xmx=Xms except P2 low-RAM) | 5–6 GB | 6–8 GB | 5 GB, Xms<Xmx | **High** on P0/P2 (OOM/stutter), Med on P1 | Low | Med-High |
| `-XX:+AlwaysPreTouch` | on with Xms=Xmx | optional (slower boot from eMMC) | yes | skip (RAM-tight) | Low-Med (pacing consistency) | Low | Med |
| THP | `-XX:+UseLargePages -XX:+UseTransparentHugePages` with THP=`madvise` in sysfs (ZGC also needs `shmem_enabled=advise`) | test only | n/a (Windows: large pages need admin — skip) | test only (watch RSS bloat) | Low-Med | Low-Med | Low-Med |
| JVM distro | Temurin 21 baseline; GraalVM CE 21 as P1/P2 experiment (G1 only — no GenZGC on GraalVM 21) | Temurin | Temurin → GraalVM trial | Temurin → GraalVM trial (biggest CPU upside here) | Low-Med (Med upside on P2 CPU-bound scenes) | **Med** (mod miscompiles; e.g. Integrated Scripting fix repo) | Med |
| Prism wrapper | `gamemoderun` in Custom Commands → wrapper | yes | n/a | yes | Low-Med | Low | High (mechanism), Med (gain) |
| Prism env vars | `mesa_glthread=true` (P0); `__GL_THREADED_OPTIMIZATIONS=0` (P2) | yes | n/a | yes (Sodium also sets it; harmless) | Low-Med (P0 CPU-bound GL) | Low | Med-High |
| Mesa driver note | MC on Deck uses **radeonsi** (GL), not RADV; glthread default-on since Mesa 22.3 — still set env var for java | informational | n/a | n/a (NVIDIA proprietary has own path) | — | — | High |
| Windows NVCP | Power management: Prefer maximum performance; leave Threaded Optimization alone (driver ignores it for MC; Sodium's GL_DEBUG_OUTPUT_SYNCHRONOUS handles it) | n/a | yes | n/a | Low (P1 is VRAM-bound) | Low | Med |
| Steam Deck BIOS | UMA frame buffer 1 GB → 4 GB; cap TDP ~8–10 W | yes | n/a | n/a | Low-Med (stutter/stability) | Low-Med (BIOS fiddling) | Low-Med |
| Sodium no-error context | leave "Use No-Error Context" enabled; ignore `NO_ERROR_CONTEXT_UNSUPPORTED` log line | leave | leave | leave | ~0 (informational) | none | High |
---
## Sources
**Kept**
- [brucethemoose/Minecraft-Performance-Flags-Benchmarks README](https://github.com/brucethemoose/Minecraft-Performance-Flags-Benchmarks) — the benchmarked flags reference (G1 client vs ZGC vs Shenandoah, memory oversizing, large pages, GraalVM caveats).
- [OpenJDK ZGC wiki](https://wiki.openjdk.org/display/zgc/Main) — authoritative GenZGC flags, heap-headroom rule, SoftMaxHeapSize, AlwaysPreTouch, THP prerequisites.
- [JEP 474](https://openjdk.org/jeps/474) + ZGC wiki changelog — ZGenerational default only on JDK 23; non-gen removed JDK 24.
- [GraalVM for JDK 21 release notes](https://www.graalvm.org/release-notes/JDK_21/) — CE/Oracle split; Generational ZGC not available on GraalVM 21.
- Sodium source, branch [1.21.1/stable](https://github.com/CaffeineMC/sodium/tree/1.21.1/stable): [NvidiaWorkarounds.java](https://github.com/CaffeineMC/sodium/blob/1.21.1/stable/common/src/boot/java/net/caffeinemc/mods/sodium/client/compatibility/workarounds/nvidia/NvidiaWorkarounds.java), [AmdWorkarounds.java](https://github.com/CaffeineMC/sodium/blob/1.21.1/stable/common/src/boot/java/net/caffeinemc/mods/sodium/client/compatibility/workarounds/amd/AmdWorkarounds.java), [WindowMixin.java](https://github.com/CaffeineMC/sodium/blob/1.21.1/stable/common/src/main/java/net/caffeinemc/mods/sodium/mixin/workarounds/context_creation/WindowMixin.java), [sodium#2061](https://github.com/CaffeineMC/sodium/issues/2061), [sodium#2961](https://github.com/CaffeineMC/sodium/issues/2961), [sodium#3407 (lang strings)](https://github.com/CaffeineMC/sodium/issues/3407) — exact meaning of NO_ERROR_CONTEXT / NVIDIA threaded-optimization workarounds, incl. `__GL_THREADED_OPTIMIZATIONS=0` on Linux.
- [Sodium Modrinth versions for 1.21.1 NeoForge](https://modrinth.com/mod/sodium/versions?g=1.21.1&l=neoforge) — confirms pack's Sodium 0.8.13 backport, Iris 1.8.13 / Sable / Aeronautics compat notes.
- [Prism wiki: Java settings](https://prismlauncher.org/wiki/help-pages/java-settings/), [Custom commands](https://prismlauncher.org/wiki/help-pages/custom-commands/), [Environment variables](https://prismlauncher.org/wiki/help-pages/environment-variables/), [Steam Deck download](https://prismlauncher.org/download/steam-deck/) — memory/java path/wrapper/env-var surfaces; flatpak install.
- [Phoronix: Mesa 22.3 RadeonSI glthread default](https://www.phoronix.com/news/Mesa-22.3-RadeonSI-glthread-On) + [PCGamingWiki Mesa](https://www.pcgamingwiki.com/wiki/Mesa) + [MangoHud#676](https://github.com/flightlessmango/MangoHud/issues/676) — glthread semantics/defaults and the Minecraft-launcher driconf nuance.
- DH warning text corroborated: [Minecraft Forum log](https://www.minecraftforum.net/forums/support/java-edition-support/3228579-modpack-randomly-losing-fps-and-sometimes-crashing), [New Eden on Modrinth](https://modrinth.com/project/7jwudrLZ).
- [Red Hat Temurin docs](https://docs.redhat.com/en/documentation/red_hat_build_of_openjdk/21/html-single/getting_started_with_eclipse_temurin/index) — Shenandoah bundled from 11 onward.
- [jan-guenter/integratedscripting-graalfix](https://github.com/jan-guenter/integratedscripting-graalfix) — concrete GraalVM JDK 21 + NeoForge 1.21.1 incompatibility.
- [Windows Central UMA frame buffer](https://www.windowscentral.com/gaming/pc-gaming/how-to-increase-vram-on-steam-deck) — Deck VRAM default/BIOS option.
- [xdg-desktop-portal GameMode](https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.GameMode.html) — gamemode from inside flatpaks.
**Dropped**
- [Obydux/Minecraft-GraalVM-Flags](https://github.com/Obydux/Minecraft-GraalVM-Flags) — server startup flags, GraalVM EE Java 17 era; wrong target (client, Java 21).
- [fumora.net GraalVM vs OpenJDK "benchmarks"](https://www.fumora.net/lab/minecraft-graalvm-vs-openjdk) — third-party, server-plugin workload, methodology unclear.
- DriverEasy/SystWeak "fix stutter" pages — SEO content, low value.
- Most Reddit threads kept only as corroboration, not as primary claims (DH warning wording, GraalVM pack failures).
## Gaps
- **No credible client-side G1-vs-GenZGC-vs-Shenandoah benchmark for 200+ mod NeoForge 1.21.1 with DH** was found; brucethemoose's data is 1.20-era Java 17 and DH's warning is the only pack-specific signal. Must A/B locally (see below).
- Whether Shenandoah is present in *current Temurin 21 Windows x64* builds was not definitively confirmed (Red Hat doc implies yes; some historic Temurin builds lacked it) — verify with `java -XX:+UseShenandoahGC -version` per machine.
- SteamOS 3.x current THP sysfs defaults (`transparent_hugepage/enabled`, `shmem_enabled`) not verified remotely — check on device.
- Whether any mod in this pack (AllTheLeaks 1.1.13, DH 3.3.2) calls `System.gc()` such that `-XX:+DisableExplicitGC` would be counterproductive — not verified.
- Sodium 0.8.13-for-1.21.1 is a 2026 backport; exact workaround-list parity with mainline 0.8.x assumed (INFERENCE from 0.8.12 changelog "Improvements for hardware and mod compatibility").
## Top levers (ranked)
1. **Move off stock G1 per DH's warning** — P1: Generational ZGC; P0/P2: Shenandoah or client-tuned G1. Expected impact **High** (1% lows / hitching with DH LOD streams). All machines. Risk: low (launcher config). Confidence: Med-High.
2. **Heap sizing discipline** (P0 5–6 GB, P1 6–8 GB, P2 5 GB; Xms=Xmx except P2). Impact **High** on P0/P2, Med on P1. Risk low. Confidence Med-High.
3. **`mesa_glthread=true` + `gamemoderun` on the Deck** (explicit env var; glthread default-on only for driver/launcher overrides, not necessarily java). Impact **Med** (CPU-bound GL dispatch on 4 cores). Risk low. Confidence Med.
4. **GraalVM CE 21 trial on P2 (and P1)** — G1-only, keep Temurin fallback. Impact **Med** (CPU-bound chunk/scene prep). Risk **Med** (miscompiles; no GenZGC). Confidence Med.
5. **`__GL_THREADED_OPTIMIZATIONS=0` env var on P2** (Sodium already sets it — belt-and-braces). Impact **Low-Med**. Risk none. Confidence High (mechanism), Med (gain).
6. **AlwaysPreTouch (+THP `madvise`) with Xms=Xmx on P1; test on P0; skip on P2**. Impact Low-Med (pacing consistency, slower boot). Risk low-med. Confidence Med.
7. **Deck BIOS UMA 4 GB + TDP cap ~8–10 W**. Impact Low-Med (stability/frame pacing with DH+shaders on 8CU). Risk low-med. Confidence Low-Med.
8. **Windows NVCP "Prefer maximum performance" on P1; leave Threaded Optimization untouched** (Sodium overrides it in code). Impact Low (VRAM-bound). Risk none. Confidence Med.
## Open questions to verify by benchmark
1. On each machine: G1 client-tuned vs Shenandoah vs GenZGC — 1% lows, 0.1% lows and hitch count during DH LOD streaming + chunk gen (use Spark GC monitor + Sodium 0.8.x FPS percentiles).
2. P1: does GenZGC at 6–8 GB show longer average frame times (concurrent CPU tax) than tuned G1 on the 7700K?
3. P2: at 5 GB heap with server view-distance 8 + DH, does live-set actually fit (Spark `gcmonitor` / allocation-rate), or is Shenandoah/G1 forced to over-collect?
4. Does `-XX:+DisableExplicitGC` change behavior of DH 3.3.2 / AllTheLeaks 1.1.13 in this pack (explicit-GC dependence)?
5. GraalVM CE 21: does the full pack (Flywheel 1.0.6, Veil/Lodestone, Sable 2.0.5, Aeronautics 1.3.2, SSRD) survive a 30-min play session without miscompile crashes, and net FPS on P2?
6. Deck: `mesa_glthread=true` on/off — measure avg FPS and 1% lows at 1280×800 with Complementary Reimagined r5.9.3 vs MakeUp Ultra Fast 9.5f; also confirm current SteamOS Mesa version's glthread default for a Prism-launched java process.
7. Deck: UMA 1 GB → 4 GB — measurable reduction in texture-streaming hitches with DH + shaders?
8. THP `madvise` on P2 (and `shmem_enabled` if ZGC tested): THP on/off delta on frame pacing and RSS.
9. Windows P1: with Sodium active (GL_DEBUG_OUTPUT_SYNCHRONOUS path), any measurable difference from NVCP Threaded Optimization settings at all? (Expect none — confirm.)
10. Xms=Xmx + AlwaysPreTouch on the Deck: startup time penalty vs frame-pacing benefit.
@@ -0,0 +1,106 @@
# Research: Client rendering cost of the Create/Sable stack (r7)
Scope: Sable 2.0.5, Create Aeronautics bundled 1.3.2 (aeronautics 1.3.2 + simulated 1.3.2 + offroad 1.3.2), SSRD 1.8.7, Sable: Physics Compat 1.3.0, Create 6.0.10 + Flywheel 1.0.6 (jar-in-jar), Slice & Dice, Dragons Plus, Enchantment Industry — on 1.21.1 NeoForge, Sodium 0.8.13, Iris 1.8.14-beta.1, DH.
Tag legend: **VERIFIED** = confirmed against the exact pinned jars in this repo (bytecode/config inspection) or an official spec; **DOCUMENTED** = official issue tracker/changelog/mod page; **COMMUNITY** = reddit/user comments; **INFERENCE** = my reasoning.
## Summary
Sable does not have its own renderer — its only sub-level renderer (`SODIUM_REACHAROUND`) re-drives **Sodium's own chunk renderer once per sub-level** (own viewport, frustum, section meshing, layered draws), so each airship is effectively a small second terrain pass, and cost scales with *number of sub-levels × their meshed section count*, not with world render distance. The two biggest *client* levers are (1) shaderpacks: Flywheel's `indirect` **and** `instancing` backends both self-disable while a shaderpack is active (and this pack ships **no** Iris Flywheel Compat), dropping Create contraption rendering onto the vanilla block-entity path, and (2) per-frame pathological interactions (log spam on the render thread, per-tick sub-level-aware sound distance math) that produce the "FPS decays over 30–60 min" reports. Known FPS bugs in the trackers are mostly either fixed by our pinned versions (burner-sound degradation fixed in Aeronautics 1.3.0) or still open and unconfirmed on 2.0.5/1.3.2 (gradual decay while sub-levels exist).
## Findings
### A. How a Sable sub-level renders, and how it scales
1. **Sable's sub-level renderer is a Sodium "reach-around", not a separate engine.** `sable-client.toml` exposes exactly one renderer: `sub_level_renderer = "SODIUM_REACHAROUND"` (sole allowed value). Bytecode of `mixin/sublevel_render/impl/sodium/SodiumWorldRendererMixin` shows per sub-level: build a Sodium `Viewport` from the camera + sub-level container, `compileSections` (chunk mesh builds) honoring Sodium's `performance.chunkBuildDeferMode`/`PrioritizeChunkUpdates`, then draw via `ChunkRenderMatrices` + layered render types (`VeilRenderType.LayeredRenderType`) with `ShaderInstance.setDefaultUniforms`. Sub-levels also have their own frustum (`cullingFrustum`) and per-frame culling update. — **VERIFIED** (jar inspection; keys cross-checked in `analysis/client-perf/data/observed-default-configs/sable-client.toml`; Sodium integration also described in Sable's own README: https://github.com/ryanhcode/sable)
2. **Cost model.** Per frame each visible sub-level costs: one frustum+cull update, draws for all compiled sections in it (≈ ceil(blocks/16)³ worst case, typically the hull), plus matrix-only transform (movement itself is cheap — it's a pose, not a re-mesh). Re-meshing happens when blocks on the sub-level change (also piston/contraption edits). Scaling is roughly linear in *count of rendered sub-levels* and in *meshed section count per sub-level*, additive to normal world terrain cost; SSRD then decides how far away this pipeline runs. — **VERIFIED** (mechanism) / **INFERENCE** (exact per-sub-level ms; no public benchmark found)
3. **Sable patches the shader stack it runs on.** Sable 2.0.5 ships overrides of **Flywheel's internal shaders** (`assets/flywheel/flywheel/internal/*`, plus `light/flat.glsl`, `light/smooth.glsl`, `light/smooth_when_embedded.glsl`) so Create Flywheel visuals can be embedded in sub-levels; it has a `sublevel_render.fancy.ProgramMixin` and `compatibility.flywheel.ShaderSourcesMixin`. The *vanilla* core-shader override in the Sable stack is only `worldborder.*` from the nested Veil 4.3.2 jar (4 files) — the static scan's "sable overrides vanilla core shaders" is real but tiny in file count; the runtime shader patching (Veil/Flywheel) is the substantive interference surface. Iris compat exists explicitly (`SableIrisCompat`, `compatibility.iris.ExtendedShaderMixin`), and the pack additionally ships `irisveil-0.3.0` (Iris Veil Compat). — **VERIFIED** (jar inspection; static scan in `analysis/client-perf/data/jarscan-report.md`)
4. **Sable does not disable Sodium optimizations** — the opposite: it *requires* Sodium (SSRD hard-requires it too) and *reads* Sodium settings (`PerformanceSettings.chunkBuildDeferMode`, `PrioritizeChunkUpdates`) to decide how sub-level rebuilds are queued; it changes no `sodium-options.json` keys. Known Sodium visual conflicts with Sable were fixed over time ("Fix some visual issues with Sodium on AMD machines" in 2.0.0; "Update Sodium compatibility to 0.8.13 alpha" in 2.0.0). — **VERIFIED** (jar inspection) + **DOCUMENTED** (https://api.modrinth.com/v2/project/T9PomCSv/version)
5. **Client-cost toggles that exist in Sable 2.0.5** (all keys verified in `SableClientConfig` / observed defaults):
| key | default | perf relevance |
|---|---|---|
| `sub_level_dynamic_shading` | `true` | adds `dynamic_directional_shading` mixins into `ModelBlockRenderer`/`AmbientOcclusionFace`/`SectionCompiler` (meshing-side cost for sub-levels) |
| `sub_level_skylight_shadows` | `false` | if enabled, sub-levels cast world shadows — leave off |
| `sub_level_water_occlusion` | `true` | hooks `FogRenderer`/`GameRenderer`/`Camera` |
| `sub_level_snapshot_interpolation_delay_ticks` | `1.5` (0–100) | interpolation buffer, not render cost |
| `sub_level_renderer` | `SODIUM_REACHAROUND` | only value; no vanilla fallback to pick |
| `attempt_udp_networking` | `true` | client side of UDP movement channel (disabling = "worse performance and latency" per common-config comment) |
Common config (`SableConfig`): `sub_level_tracking_range` (network distance of sub-levels to players), `sub_levels_with_players_cannot_unload`, `sub_level_splitting*`, `disable_udp_pipeline`, `verbose_serialization_logging`. — **VERIFIED** (jar inspection). Community advice to raise tracking range to 1280 (default 320) is about *server→client streaming* of sub-levels, not client frame cost: **COMMUNITY** (https://www.reddit.com/r/CreateMod/comments/1tdil1p/limiting_sublevel_render_distance_in_sable/ — key name `sub_level_tracking_range` verified in jar)
### B. Known FPS issues / fixes in the Sable & Aeronautics trackers
6. **Render-thread log spam can collapse FPS near any sub-level (Sable #881, open).** A mod calling `getEntityLookedAt()` every frame produced huge AABBs against Sable's entity getter; Sable logged `Aborting entity get for abnormally large AABB` hundreds of times/sec, log4j ate ~75% of the render thread, 30→9–12 FPS. Root cause is the *triggering mod* (LegendarySurvivalOverhaul — **not in our pack**; only `legendary-tooltips` is, which does no entity lookups). Lesson for us: any per-frame entity/clip query mod can detonate on sub-levels; watch client logs for repeated Sable errors. — **DOCUMENTED** (https://github.com/ryanhcode/sable/issues/881)
7. **"FPS decays over 20–60 min while a sub-level exists" (Sable #1011, open; Simulated-Project #746/#1204).** Two distinct reported causes: (a) #1011 — gradual decay mitigated temporarily by F3+T (texture reload) or world rejoin, smells like a render-resource leak (Sable 1.2.2 era, unconfirmed on 2.0.5); (b) #746/#1204 — `BalloonBurnerSoundInstance` (`aeronautics:block.hot_air_burner.head`, `aeronautics:block.steam_vent.head`) re-played ~20×/s, each tick doing `distanceSquaredWithSubLevels → projectOutOfSubLevel → getContaining → getLocalPlot → getChunk/getBlockEntity` — hundreds of× vanilla cost; master volume 0 or blocking the two sound IDs stabilizes FPS. **Fixed upstream:** Aeronautics 1.3.0 changelog has "Fix performance degradation with Adjustable Burner sounds" → our 1.3.2 pin includes the fix. Caveat: 1.3.2 still contains `BalloonBurnerSoundInstance` with `distanceSquaredWithSubLevels` and `AeroSoundDistUtil.tickGlobalBurnerSound` — the sub-level-aware per-tick distance math is structurally still there, so treat "burners on many airships" as a known stressor. — **DOCUMENTED** (https://github.com/ryanhcode/sable/issues/1011, https://github.com/Creators-of-Aeronautics/Simulated-Project/issues/746, https://github.com/Creators-of-Aeronautics/Simulated-Project/issues/1204, https://api.modrinth.com/v2/project/oWaK0Q19/version) + **VERIFIED** (1.3.2 jar inspection)
8. **Iris shader artifacts with sub-levels — affects our exact stack (Iris #3164, open).** With Iris 1.8.14-beta.1 + Sable 2.0.x + Aeronautics 1.3.x + Sodium 0.8.13, physics structures render incorrectly *with shaders enabled*; artifact scales with structure size; any shaderpack; goes away with shaders off; specifically triggered by shader **real-time shadows** (Complementary/Miniature: disable shadows; Solas: disable SSAO). Root cause attributed to Veil; a community fix exists but is unmerged. Our `shaderpacks/` = **Complementary Reimagined + Makeup–Ultra-Fast**, i.e. the affected families (Makeup–Ultra-Fast is also named in SSRD #50 "SSRD not working with Shaders"). — **DOCUMENTED** (https://github.com/IrisShaders/Iris/issues/3164, https://github.com/RanoldStranold/SSRD/issues/50)
9. **Other tracker items (context, mostly visual or fixed):** sub-levels invisible behind DH LODs at distance when tracking range > view distance (Sable #376); "Visual bugs with Sodium" world-invisible report (Sable #331, older Sable); Iris tessellation-shader geometry invisible with Aeronautics (Simulated-Project #308); IFC + `flywheel:DEFAULT` → invisible/transparent cogs & shafts (iris-flw-compat #244); resource leak of Contraption Diagram fixed in Aeronautics 1.1.0. — **DOCUMENTED** (https://github.com/ryanhcode/sable/issues/376, https://github.com/ryanhcode/sable/issues/331, https://github.com/Creators-of-Aeronautics/Simulated-Project/issues/308, https://github.com/leon-o/iris-flw-compat/issues/244, https://api.modrinth.com/v2/project/oWaK0Q19/version)
### C. SSRD (1.8.7) — what it changes on the client
10. **Config surface:** a "Sub-Level Render Distance" **slider injected into Sodium's video settings** (below Simulation Distance); slider max is dynamically capped by your DH/Voxy LOD distance or the server maximum; server caps via `/gamerule ssrdMaxRenderDistance` (**default 128 chunks**) and `/gamerule ssrdForceloadLimit` (**default 2 per player**). SSRD 1.8+ **requires Sodium 0.8** (we have 0.8.13 ✓) and DH or Voxy on the client. — **DOCUMENTED** (https://modrinth.com/project/fJ183v3D)
11. **Cost & bugs:** raising the slider = more sub-levels rendered by the full Sable/Sodium pipeline at distance (frame cost scales with visible sections, culling still applies), plus server-side force-loading pressure. Known issues: sub-levels visible through LODs with certain shaderpacks (Modrinth warning), DH fade-out also fades sub-levels (#64), zoom glitching (#66), Colorwheel incompatibility (#62, closed), various forceload/unload bugs (mostly closed as of 1.8.7). Mixins confirm the hooks: `RenderSectionManagerMixin`, `SodiumConfigBuilderMixin` (the slider), `VanillaFrustumMixin`/`SodiumViewportMixin`, `RenderSystemFogMixin`, DH `DHShaderSourceMixin`/`FrameUniformsMixin`, plus visual-distance mixins on contraption/entity/block-entity visuals. — **DOCUMENTED** (https://modrinth.com/project/fJ183v3D, https://github.com/RanoldStranold/SSRD/issues?q=is%3Aissue) + **VERIFIED** (`SSRD-1.8.7-1.21.1.jar` mixin list)
12. **sablecompat** (`Sable: Physics Compat` 1.3.0) only provides tags for modded blocks — no rendering behavior. `Create Aeronautics: Compatibility` 1.1.3 likewise compat glue. Neither is a perf surface. — **DOCUMENTED** (https://modrinth.com/project/sZbcIrJb; jar metadata)
### D. Flywheel backend choice (Pascal NVIDIA vs Mesa RADV, with Iris)
13. **Backend IDs & requirements (Flywheel 1.0.6, verified in source + jar):** `flywheel:off`, `flywheel:instancing` (GL 3.3 + `ARB_shader_bit_encoding`; priority 500), `flywheel:indirect` (GL 4.6 + compute/DSA/`ARB_multi_draw_indirect`/SSBO/etc.; priority 1000 — GPU-driven culling with a depth pyramid + MDI; uses `GL_KHR_shader_subgroup`). `DEFAULT` picks the highest-priority *supported* backend, with a fallback log "Flywheel backend fell back from '{}' to '{}'". **Driver special-case: `Driver.INTEL` gets indirect priority 1** ("Intel has very poor performance with indirect rendering, and on top of that has graphics bugs") — irrelevant to our fleet (NVIDIA Pascal, AMD/Mesa RADV). Both Pascal (GTX 10xx) and Steam Deck RADV advertise GL 4.6 + the required ARBs, so **`DEFAULT` → `indirect` on all three machines when no shaders**. — **VERIFIED** (Flywheel `Backends.java` @ Engine-Room/Flywheel branch 1.21.1/dev, and bytecode in `create-1.21.1-6.0.10.jar` → nested `flywheel-neoforge-1.21.1-1.0.6.jar`)
14. **With a shaderpack active, both backends self-disable:** `supported() = … && !ShadersModHelper.isShaderPackInUse()` on *both* `instancing` and `indirect` → `DEFAULT` falls back to `flywheel:off` (vanilla block-entity path for all Create visuals). This is why the pack has no working Create-instancing under Iris. The documented fix is **Iris & Oculus Flywheel Compat (IFC)**: "Flywheel will disable its optimizations when you are using shaderpacks. This mod forces Flywheel's optimizations to be enabled… render Create's entities using Flywheel Instancing with shaderpack enabled… significantly improve fps" — IFC 2.3.x supports **Create 6.0.9 + Flywheel 1.0.6**, needs **Iris ≥ 1.8.1** (we have 1.8.14-beta.1 ✓); Create **6.0.10 is newer than the last tested 6.0.9** → verify before shipping. **Our pack does not include IFC.** — **VERIFIED** (Flywheel source) + **DOCUMENTED** (https://modrinth.com/mod/iris-flw-compat, https://github.com/leon-o/iris-flw-compat/issues)
15. **Driver risk notes for indirect:** AMD *Windows* has a confirmed driver crash in Flywheel's indirect path (`atio6axx.dll`, `glBindBuffersRange`/MDI; GPUOpen-Drivers #89, acknowledged in Create/Flywheel repos) — **does not apply** to our fleet (no AMD Windows machine; Steam Deck is Mesa RADV, which Flywheel explicitly detects as `Driver.MESA` and supports); Create #10191 reports indirect crashes with elevator pulleys on one user setup (fix: `instancing`), and some users report indirect crashes with Create 6 + certain setups. Pascal is generally the safest indirect platform; treat any "Create block crash" on a client as "switch `backend` to `flywheel:instancing` or `flywheel:off` first". — **DOCUMENTED** (https://github.com/GPUOpen-Drivers/AMD-Gfx-Drivers/issues/89, https://github.com/Creators-of-Create/Create/issues/10191, https://www.reddit.com/r/CreateMod/comments/1pogo4t/create_6_crashing_when_placing_a_block_other/)
16. **flywheel-client.toml (verified defaults in pack):** `backend = "DEFAULT"`, `limitUpdates = true` ("instance update limiting with distance" — keeps distant contraptions from updating instances; leave on), `workerThreads = -1` (0–4; parallelizes instance updates, needs restart; 4-core Zen2 Deck vs 8-thread 7700K/6700K → explicit `2` on Deck if update-thread contention shows up is a testable knob), `[flw_backends] lightSmoothness = "SMOOTH"` (values FLAT, TRI_LINEAR, SMOOTH, SMOOTH_INNER_FACE_CORRECTED; config comment: "**May have a large performance impact**" — SMOOTH adds per-vertex light interpolation in Flywheel's uber-shaders; FLAT is the cheap end; each value changes compiled shader permutations). — **VERIFIED** (`observed-default-configs/flywheel-client.toml`, `LightSmoothness.class` bytecode)
### E. Create contraption rendering cost & the addons
17. **Create 6 = Flywheel 1.0** (official changelog: "Now using Flywheel 1.0"); contraption/kinetic visuals are instances batched per material; `indirect` adds GPU culling+MDI so cost scales with *visible* instances, `instancing` costs CPU draw calls per material (fine for tens of contraptions, and it's what IFC re-enables under shaders). Without Flywheel (backend off, e.g. under shaders without IFC) every kinetic/contraption visual becomes a vanilla block-entity render — the classic "Create + shaders = CPU death" mode. — **DOCUMENTED** (https://wiki.createmod.net/users/changelogs/6.0.0) + **VERIFIED** (backend mechanics, above)
18. **Sable × Create integration adds its own frame mixins:** Sable 2.0.5 has 30+ `compatibility.create.*` mixins (ContraptionVisual, CarriageContraptionVisual, VisualizationEventHandler, flywheel Engine/RenderDispatcher/EmbeddedEnvironment/VisualManager mixins) so contraptions riding sub-levels render inside the sub-level pass with embedded lighting — contraptions on airships are therefore *both* Flywheel instances and embedded-environment draws; a big airship factory is the worst-case render target. — **VERIFIED** (`sable-neoforge.mixins.json`, jarscan: create-aeronautics 20 frame mixins, sable 127)
19. **Slice & Dice, Dragons Plus, Enchantment Industry:** no client-perf issue reports found in their trackers for 1.21.1/Create 6 (S&D mostly touches RenderLevelStage/Gui events per jarscan; no lag/fps issues surfaced). They add ordinary instance/BER content; cost scales with placed blocks like any Create addon. — **DOCUMENTED (absence of evidence)** — treat as "no known client perf issues found" for these three.
20. **Sound-adjacent sanity note:** Create 6.0.0 already caps boiler sounds ("Play at most four steam engine sounds at once per side"), but Aeronautics adds its own global sound instances; combined with Sable's sub-level-aware distance math (Finding 7) the sound engine is a real client-tick cost center on all machines — an easy benchmark axis (master volume 0 vs 100). — **DOCUMENTED** (https://wiki.createmod.net/users/changelogs/6.0.0) + Finding 7
## Top levers (ranked)
| # | Lever | Impact | Machine(s) | Risk | Confidence |
|---|---|---|---|---|---|
| 1 | Add **Iris Flywheel Compat 2.3.x** (test vs Create 6.0.10) or accept `flywheel:off` under shaders knowingly | **H** — restores GPU instancing for all Create visuals under shaders; without it shader users pay vanilla-BER cost for every cog/contraption | All, biggest on 7700K+1060 (CPU-bound) and Deck (4-core Zen2) | M — IFC lists Create 6.0.9 as last tested; visual glitches possible per iris-flw-compat #244 | High (Flywheel source-verified behavior + IFC docs) |
| 2 | In shaderpack settings, **disable real-time shadows/SSAO** (Complementary Reimagined, Makeup–Ultra-Fast) or accept #3164 artifacts | **H** — removes documented sub-level rendering corruption *and* shadow-pass cost; shadows are the confirmed trigger | All shader users | L — pure shader setting | High (Iris #3164 reproduces on our exact versions) |
| 3 | Keep **SSRD slider modest** (e.g. start at 8–16 chunks, not the 128-chunk server cap) and don't forceload broadly | **M–H** — directly bounds how many sub-level sections the per-sub-level Sodium pass draws; also server load | Deck (GPU/VRAM-limited) & 1060 6GB first | L — slider is the intended knob; gamerule cap on server | Medium-High (mechanism verified; magnitude unbenchmarked) |
| 4 | Prefer `flywheel:indirect` (DEFAULT) **without** shaders; only drop to `flywheel:instancing` on crash; consider `lightSmoothness = "FLAT"`/`TRI_LINEAR` on Deck if Flywheel shader cost shows in F1 | **M** — indirect = GPU-culled MDI; FLAT removes per-vertex light smoothing cost ("large performance impact" per config) | Deck (RADV) and 1060 for lightSmoothness; indirect OK on Pascal & RADV | L–M — changing lightSmoothness alters visuals (block light smoothing) | Medium-High (config semantics verified; perf delta unbenchmarked) |
| 5 | Keep `sub_level_skylight_shadows=false`, keep `limitUpdates=true`, keep UDP networking on; watch client logs for Sable "Aborting entity get" spam (Finding 6 class) | **M** — avoids enabling the shadow pass; avoids the documented render-thread log-stall class | All | L | High (defaults verified; #881 mechanism documented) |
| 6 | If gradual FPS decay reproduces on 2.0.5: test F3+T / master-volume-0 as diagnostics (sound-engine vs render-resource); keep an eye on Sable #1011 | **M** — separates the two documented decay causes cheaply before deeper profiling | All; 8GB-RAM 1080 Ti machine most sensitive to leaks | L — diagnostics only | Medium (both causes documented on adjacent versions; 1.3.2 fixed the burner one) |
## Open questions to verify by benchmark
1. ms/frame cost of **one** idle sub-level vs an identical static structure in the world (isolates the per-sub-level Sodium re-drive overhead), and the marginal cost of the 2nd, 3rd, 4th sub-level (linearity of Finding 2).
2. Cost of a **moving + rotating** sub-level with `sub_level_dynamic_shading=true` vs `false` (does rotation re-mesh? how often?) — deck vs 1060.
3. Frame cost of the **same airship under shaders with IFC on vs off** (instancing-merged vs vanilla-BER fallback) on 7700K/1060 and Deck.
4. `flywheel:indirect` vs `flywheel:instancing` vs `off` on **RADV/Steam Deck specifically** (Mesa GL4.6 MDI perf is the wildcard; also VRAM headroom vs the 1080 Ti's 8 GB-free-RAM host).
5. SSRD slider sweep (4/16/32/64 chunks) with DH LODs: frame-time and chunk-build queue behavior (Sable reads `chunkBuildDeferMode=ALWAYS` — does sub-level remeshing starve behind deferred world builds?).
6. Sound-engine axis: master volume 0 vs 100 near N burners/steam vents on 1.3.2 (residual per-tick `distanceSquaredWithSubLevels` cost, Finding 7).
7. Long-run decay test (45+ min, sub-levels present, #1011 class) on Sable 2.0.5 — does it still exist? Track client RAM/VRAM and F3+T recovery.
8. IFC 2.3.x × Create 6.0.10 × Iris 1.8.14-beta.1 compatibility spot-check (invisible cogs #244 class) before adding it to the pack.
## Sources
**Kept:**
- Sable repo/README (https://github.com/ryanhcode/sable) — primary source; mixin list & Sodium integration verified against jar
- Sable issues #881, #1011, #376, #331 (github.com/ryanhcode/sable) — documented FPS failure modes
- Simulated-Project #746 + #1204 (github.com/Creators-of-Aeronautics) — sound-engine FPS decay, spark profiles, bytecode-level root cause
- Aeronautics changelog API (https://api.modrinth.com/v2/project/oWaK0Q19/version) — "Fix performance degradation with Adjustable Burner sounds" in 1.3.0
- SSRD Modrinth page + GitHub issues (https://modrinth.com/project/fJ183v3D, https://github.com/RanoldStranold/SSRD/issues) — config/gamerules, Sodium-0.8 requirement, shader caveats
- Iris #3164 (https://github.com/IrisShaders/Iris/issues/3164) — repro on our exact version stack; shadow trigger
- Iris Flywheel Compat Modrinth page (https://modrinth.com/mod/iris-flw-compat) — shader-mode Flywheel behavior + IFC support matrix
- Flywheel `Backends.java` (raw.githubusercontent.com/Engine-Room/Flywheel/1.21.1/dev) — backend support conditions, Intel deprioritization
- GPUOpen-Drivers/AMD-Gfx-Drivers #89, Create #10191, r/CreateMod threads — driver-level indirect risks
- Create 6.0.0 changelog (https://wiki.createmod.net/users/changelogs/6.0.0) — Flywheel 1.0 baseline
- Local pinned-jar inspection: sable 2.0.5 (+ nested veil 4.3.2), create 6.0.10 (+ nested flywheel 1.0.6), aeronautics bundled 1.3.2, SSRD 1.8.7, observed default configs (`analysis/client-perf/data/observed-default-configs/`)
**Dropped:**
- Modders-of-Create/Create-Aeronautics #14 "Gradual drop in fps" (404 — repo/issue no longer resolvable; superseded by Simulated-Project #746/#1204)
- r/CreateMod "Limiting sublevel render distance in Sable" full thread (login-walled; snippet retained only as COMMUNITY corroboration; its `ticking_range` key name is stale — actual key verified as `sub_level_tracking_range`)
- mcmod.cn Flywheel page (secondary aggregator; its AMD-Windows advice superseded by GPUOpen issue #89)
- 9minecraft/deepwiki Flywheel articles (SEO/aggregator content, no unique facts)
## Gaps
- No public benchmark quantifies per-sub-level frame cost or scaling with count/size — the cost model in Finding 2 is mechanism-derived, not measured.
- Could not confirm whether Sable #1011 (gradual decay, F3+T-reset) still reproduces on Sable 2.0.5 — issue is open but filed against 1.2.2; no maintainer root-cause posted.
- IFC compatibility with Create **6.0.10** specifically (last tested 6.0.9) is unverified; nor is IFC × Sable 2.0.5 embedding (Sable overrides Flywheel shaders, IFC also patches them — interaction untested publicly).
- Shaderpack-specific behavior of *Makeup–Ultra-Fast* with SSRD (#50) and Complementary Reimagined with #3164 is reported for sibling packs (Makeup–Ultra-Fast plain, Complementary Unbound); Reimagined itself untested in those threads.
- Steam Deck (RADV) Flywheel-indirect reports are anecdotal in either direction; no RADV-specific issue was found, which is weak evidence of health, not proof.
@@ -0,0 +1,85 @@
# Research: Client-side performance sweep — content/mob mods (Onion MC, 1.21.1 / NeoForge 21.1.251)
Scope: client-side only (frame time, client tick, RAM/VRAM, startup). All pinned versions are 1.21.1 NeoForge. Every claim is tagged **VERIFIED** (primary source directly states it), **DOCUMENTED** (official changelog/wiki/README), **COMMUNITY** (user reports, issue trackers, forums), or **INFERENCE** (my reasoning from the sources).
## Summary
The content/mob mods are individually cheap, but the *stack* has three real client-side risk patterns: (1) **entity-count inflation** — Alex's Mobs (unofficial port), Fowlplay and Critters and Companions all have open reports of over-spawning, which directly increases client render + tick work; (2) **animation cost multipliers** — Fresh Animations / EMF-ETF make every animated modded mob far more expensive to render, and EMF has a documented blow-up with many mobs on screen; (3) **a handful of specific offenders** (Ars Nouveau brand-new mirrorweave/SkyWeave rendering, Apotheosis loot beams, Citadel entity tracking) that are either already fixed in the pinned versions or have config levers. Most mods in this list have **no known client perf issues at all**.
---
## Per-mod table
Legend for **cost type**: frame = render-thread cost; c-tick = client tick; RAM/VRAM; startup; none-known.
| Mod (pinned) | What it does | Client cost type | Known perf issues (URL + fixed?) | Perf-relevant config levers (exact keys only if seen) | Judgement |
|---|---|---|---|---|---|
| **Alex's Mobs unofficial port 1.22.17** | 85+ animated new mobs | **frame**, c-tick, RAM (models/textures) | • Raguto port #62 "tons of monsters constantly spawn when mod is enabled" ramps up *vanilla* monster spawns → more entities to render — **OPEN** on 1.21.1 ([#62](https://github.com/Raguto/AlexsMobs-1.21.1/issues/62)) **COMMUNITY**<br>• Original mod reputation: "notoriously bad for performance… noticeable increase in lag caused by all the spawning" ([Reddit](https://www.reddit.com/r/feedthebeast/comments/1sjfen3/is_alexs_mobs_really_that_bad_performancewise/)) **COMMUNITY**<br>• Over-spawning lobsters/catfish/flyfish/terrapins breaking beds/boats ([SteamPunk #83](https://github.com/LunaPixelStudios/SteamPunk/issues/83)) **COMMUNITY**<br>• Fabric port: vertex explosions/tearing with Sodium+Iris shaders, all shaderpacks ([Alexs-Mobs-Fabric #121](https://github.com/astryxion/Alexs-Mobs-Fabric/issues/121), Sep 2026, **OPEN**, Fabric build not our port) **COMMUNITY**<br>• Leafcutter-ant tick lag was server-side TPS ([#382](https://github.com/Alex-the-666/AlexsMobs/issues/382)) | Per-mob spawn probability lives in the `alexsmobs` config folder (Fandom *Configuration* page, per-mob spawn probabilities) **DOCUMENTED** — exact keys not read (page returned 403). | **tune** — keep, but cap spawn rates |
| **Citadel port 2.7.6** (Alex's Mobs lib) | Shared models/AI/entity-data library | frame, RAM | • "Alex's Mobs X Citadel: Slow Chunk loading" — chunk-generation lag; user reports setting Citadel's *track entities* option to false helped slightly ([Citadel #156](https://github.com/AlexModGuy/Citadel/issues/156), closed "completed" 2025-06-25, still reported persisting Dec 2024) **COMMUNITY**<br>• Historic memory leak 609 MB (`registeredEntityData` / `WeakIdentityHashMap`), 1.16.3, ([Citadel #12](https://github.com/Alex-the-666/Citadel/issues/12)) — very old, assumed long fixed **COMMUNITY** | Issue cites `citadel-common.toml` entity-**tracking** toggle (exact key not verified — do not invent) | **keep** |
| **Mowzie's Mobs 1.8.2** | Boss-tier animated enemies (GeckoLib) | frame | • #30 "Performance issues, outdated file structures" on **1.21.1 / NeoForge 21.1.219 / MM 1.8.1** — spark showed mowziesmobs structure processing; owner: "I believe I have fixed the issue… next mod patch" ([#30](https://github.com/BobMowzie/MowziesMobs-Public/issues/30), closed 2026-03-13); our **1.8.2 presumably contains it** **INFERENCE**<br>• Elokosa leap checks tick cost ([#38](https://github.com/BobMowzie/MowziesMobs-Public/issues/38), [#59](https://github.com/BobMowzie/MowziesMobs-Public/issues/59)) — server/tick side **COMMUNITY**<br>• Old Frostmaw FPS drop ([Crackpack-3 #101](https://github.com/DarkPacks/Crackpack-3/issues/101), 1.12-era) **COMMUNITY** | none known | **keep** |
| **Adorable Hamster Pets 3.7.0** | Tiny animated pet hamsters (genetics/NBT heavy) | frame, c-tick, RAM/NBT | • 3.6.1 changelog: "Resolved… pathfinding and AI issues where tamed hamsters following the player would sometimes experience **server-tick lag spikes**… with a lot of hamsters [following] at once" — **fixed in 3.6.1**, therefore present in 3.7.0 **DOCUMENTED** ([Modrinth](https://modrinth.com/mod/adorable-hamster-pets/version/QitZ5fVO))<br>• 3.6.0: moved genome tracking to IntArray NBT "without lagging the player entity" **DOCUMENTED** | "Wander Mode" radius; a config fallback for auto-guidebook delivery ([changelogs](https://www.curseforge.com/minecraft/mc-mods/adorable-hamster-pets/files/7969762)) — exact keys not read | **keep** |
| **Doggy Talents Next 1.19.1** | Skill-tree/accessory overhaul of vanilla wolves | frame (custom dog models/skins) | No dedicated client-FPS issue found. Related: CarryOn #684 progressive instability/stall ([CarryOn #684](https://github.com/Tschipp/CarryOn/issues/684)) **COMMUNITY**. Wiki: creative dog beds "randomly generated to a certain limit… to avoid performance issues" ([FAQ](https://doggytalentsnext.wiki.gg/wiki/FAQ)) **DOCUMENTED** | Skin enable/disable per client (CurseForge file notes) **DOCUMENTED** | **keep** |
| **Better Dogs X DTN 1.2.2** | Replaces DTN dog models/skins | frame (slightly heavier models) | No perf issue found ([Modrinth](https://modrinth.com/mod/better_dogs_x_doggy_talents_next)) | none known | **keep** |
| **Icy's Better Horses 2.1.0.1** | Horse AI/progression/traversal tweaks | none-known | No perf issue. One **client crash** with another mod (#31, Fabric 1.20.1, fixed in next jar) — not perf ([#31](https://github.com/Icywhenhot/Icy-s-Better-Horses/issues/31)) **COMMUNITY** | none known | **keep** |
| **Friends&Foes 4.0.27** | Mob-vote mobs (Copper Golem, Crab, Glare, Moobloom, Iceologer, Rascal, Tuff Golem, Wildfire…) + structures | frame (few new mobs), worldgen | **No known client perf issues found.** Tracked issues are crashes/compat (e.g. startup `MixinExtrasBootstrap` crash [#418](https://github.com/Faboslav/friends-and-foes/issues/418)) **COMMUNITY** | YACL-based config exists (mod requires YetAnotherConfigLib — Modrinth dependency listing) **DOCUMENTED**; no perf keys read | **keep** |
| **Flowery Mooblooms** (F&F addon) | One moobloom variant per flower | frame (variant count) | No perf issue found ([repo](https://github.com/Faboslav/friends-and-foes-flowery-mooblooms)) | none known | **keep** |
| **Critters and Companions** | Vanilla-style animals (otters, dragonflies, ferrets…) | frame + entity count, c-tick | • #91: lag from otters/dragonflies; no config file; "dragonflies… fly out of range and become unloaded… chunks full to the gills with dragonflies". Dev: dragonfly spawn weight reduced + Fabric config / Forge datapack override added, **Jul 2025** ([#91](https://github.com/bonsaistudi0s/CrittersAndCompanions/issues/91), closed) **COMMUNITY**<br>• Community: "always made servers… laggy… loading new chunks cause a lot of stuttering and CPU usage" ([Reddit](https://www.reddit.com/r/feedthebeast/comments/1bn2xip/carryon_and_critters_and_companions_causing_a_lot/)) **COMMUNITY** | No TOML keys; spawns are **data-file driven** → override via datapack (dev statement in #91) | **tune** via datapack, else candidate-remove |
| **Fowlplay 1.2.3** | Birds with realistic flight, natural spawning | **c-tick**, frame (entity count), RAM | • #214 ducks/birds spawn non-stop near rivers/oceans; F3 spawn cap "right under 400"; "almost 200 birds sitting in a single area" even after minimising group spawns — **OPEN, 1.21.1 NeoForge** ([#214](https://github.com/aqariio/Fowl-Play/issues/214)) **COMMUNITY**<br>• #196 `BirdEntity`/`GullEntity` recalculate **paths every tick** (`PathFinder.findPath()` per tick) — server-side but scales badly ([#196](https://github.com/aqariio/Fowl-Play/issues/196), open, 1.20.1 Forge) **COMMUNITY**<br>• #213 hawk AI crash with c2me async chunkloading ([#213](https://github.com/aqariio/Fowl-Play/issues/213)) **COMMUNITY** | Custom mob category; group-spawn settings exist (referenced in #214); no TOML keys read | **tune** (spawns) or **candidate-remove** if bird spam appears |
| **Wings of Fire 1.0** | 6 phoenix breeds as summoned mounts | frame (only while summoned) | **No known client perf issues found** ([Modrinth](https://modrinth.com/mod/wings-of-fire!)) **DOCUMENTED** | none known | **keep** |
| **Tiny Dragons (`rpg_companions_tiny_dragons`)** | Tameable tiny dragon companions | frame | **No known client perf issues found.** MCreator-generated mod ([MCreator page](https://mcreator.net/modification/113331/rpg-companions-tiny-dragons)) — MCreator mods are typically less optimised **INFERENCE** | none known | **keep** (low priority) |
| **Mermod 3.3.2** | Necklace → mermaid transformation/tail rendering | frame (player model when transformed) | **No known client perf issues found** — GitHub issue search for `lag OR fps OR performance OR memory` returned 0 results ([issues](https://github.com/ThatPreston/Mermod/issues?q=is%3Aissue+lag+OR+fps+OR+performance+OR+memory)) **VERIFIED** | none known | **keep** |
| **Artifacts 13.2.5** | Curio-slot utility trinkets | none-known (light client-tick) | **No known client perf issues found.** Old spawn-rate complaint about Mimics ([#3](https://github.com/ochotonida/artifacts/issues/3)) **COMMUNITY** | none known | **keep** |
| **Ars Nouveau 5.13.1** | Magic/spell system; client-side spell entities, particles, own skybox-style shaders | **frame** (particles/projectiles/shader blocks), c-tick, RAM | • SkyWeave/mirrorweave FPS drops (77→19 fps) — fixed by commit `640db2d` / PR #1714, Mar 2025 ([#1221](https://github.com/baileyholl/Ars-Nouveau/issues/1221)) → **already fixed in 5.13.1** (fix predates 5.9.1) **VERIFIED**<br>• Agronomic Sourcelink + AE2 growth acceleration spawns excessive visual entities → client <20 fps ([#1904](https://github.com/baileyholl/Ars-Nouveau/issues/1904), 5.9.1, closed 2026-06-08) **COMMUNITY**<br>• Storage Lectern massive **client** lag sorting 500+ Apotheosis-affixed items ([#1274](https://github.com/baileyholl/Ars-Nouveau/issues/1274), 1.19.2/3.23.0) **COMMUNITY**<br>• Planarium + Mekanism pipes → 1 fps ([#2108](https://github.com/baileyholl/Ars-Nouveau/issues/2108)) **COMMUNITY**<br>• Sylph particle spawner FPS tank (Optifine-era, fixed) ([#379](https://github.com/baileyholl/Ars-Nouveau/issues/379)) **COMMUNITY**<br>• A widely-reported "Ars projectiles drop FPS" case was traced to **Xaero's Minimap** ([Reddit](https://www.reddit.com/r/feedthebeast/comments/1o0yn4q/ars_nouveau_projectiles_dropping_fps_wildly/)) **COMMUNITY**<br>• Maintainer notes block-entity lookalike blocks (mirrorweave) "will always be more laggy than a normal static block"; recommends **EntityCulling** ([#1221 comment](https://github.com/baileyholl/Ars-Nouveau/issues/1221)) **VERIFIED** | none verified for particle budget | **keep**; watch the storage-lectern/automation entity-spam cases |
| **Apotheosis 8.8.0** | Affix items, gems, bosses/elites, loot beams | frame (loot-beam effects, boss glow), RAM | • #1191 perf issue (ShieldBreaker repeated capability comparisons) — **fixed in 7.3.2** (`cf0e2e8`), so **not present in 8.8.0** ([#1191](https://github.com/Shadows-of-Fire/Apotheosis/issues/1191), [changelog 7.3.2](https://github.com/Shadows-of-Fire/Apotheosis/blob/1.21/changelog.md)) **DOCUMENTED**<br>• Boss glow is cheap (glow duration 2 min, 20 s boss cooldown) **DOCUMENTED**<br>• Affix **loot-beam** in-world effects were *added* in 8.3.0 and are render-thread cost **DOCUMENTED** | Changelog 8.3.0: "These loot effects **can be disabled in the config file**" (exact key not read) | **keep**; disable loot beams if chasing frame time |
| **Too Many Bows** | ~38 bows with unique projectile effects | frame (only when firing) | **No known client perf issues found.** v2.1.0 changelog: "Updates: Logic and performance of bows" ([CurseForge file](https://www.curseforge.com/minecraft/mc-mods/too-many-bows/files/5932404)) **DOCUMENTED** | `/tmb reload bows` for registered bow settings ([wiki](https://wiki.pixeldreamstudios.net/mods/too-many-bows)) **DOCUMENTED** | **keep** |
| **Dungeon's Delight 1.5.1** | Farmer's Delight addon — cook/eat monsters | none-known | **No known client perf issues found.** 1.5.1 changelog is a compatibility/one-bug fix for FD 1.3.4 ([Modrinth 1.5.1](https://modrinth.com/mod/dungeons_delight/version/1.5.1)) **DOCUMENTED**. The task's "29 vanilla mixins" claim could not be verified — no source read (**UNVERIFIED**) | none known | **keep** |
| **Fisherman's Haven** | Fishing overhaul (rods, traps, bait, custom mobs, deco) | none-known | **No known client perf issues found** ([Modrinth](https://modrinth.com/mod/fishermans-haven)) | none known | **keep** |
| **Aquaculture 2** | Fishing expansion (30+ fish, rods) | none-known | • Loot-table calc lag (#113) was **server-side**, **fixed in Aquaculture 2.1.0** for MC 1.16.3 — long since fixed, not applicable to your 1.21.1 build ([#113](https://github.com/TeamMetallurgy/Aquaculture/issues/113)) **DOCUMENTED**<br>• Optifine bobber-render crash ([Optifine #3538](https://github.com/sp614x/optifine/issues/3538)) — irrelevant on Sodium **COMMUNITY** | none known | **keep** |
---
## Cross-cutting (apply to the whole content/mob set)
- **Fresh Animations + EMF/ETF is the single biggest animation multiplier.** EMF alone is not the problem, but stacking Fresh Animations with many mobs on screen is: a benchmarked case went **165→40 fps** (30 mobs, shaders on) and 400+→30-40 fps with 50 cows ([EMF #443](https://github.com/Traben-0/Entity_Model_Features/issues/443)) **COMMUNITY**. Maintainer: "you aren't going to be easily pushing past 200 fps with Fresh Animations on ever". Mitigation is config, not removal: EMF added an **"Animation LOD distance"** setting (skips animation frames beyond a distance) and a **"Retain LOD at low fps"** setting ([EMF changelog 1.3](https://github.com/Traben-0/Entity_Model_Features/blob/master/CHANGELOG.MD)) **DOCUMENTED**. Every animated content mod below multiplies this cost, so the animation settings should be tuned **before** removing content mods.
- **Entity-count inflation is the recurring theme.** Alex's Mobs port (#62), Fowlplay (#214) and Critters and Companions (#91) all have **open/community** over-spawn reports on or near 1.21.1. More entities = more render-list entries, more client-tick AI interpolation, more packet traffic. This is arguably the highest-leverage fix for the fleet, and it is all server-side spawn tuning rather than mod removal.
- **EntityCulling is recommended by Ars Nouveau's author** for the mirrorweave/SkyWeave block-entity case specifically ([#1221](https://github.com/baileyholl/Ars-Nouveau/issues/1221)) — you already ship EntityCulling 1.11.2, so that's covered **VERIFIED**.
- **Sodium/Iris + animated modded mobs is a known fragile combination.** Aside from the Fabric Alex's Mobs vertex-explosion report ([#121](https://github.com/astryxion/Alexs-Mobs-Fabric/issues/121)), the EMF #443 reports are all on the Sodium/Iris/Embeddium/Oculus stack. Worth a visual + frame-time check with shaders on vs off once benchmarking starts **COMMUNITY**.
- **Most mods in this sweep are clean.** Mermod, Wings of Fire, Tiny Dragons, Icy's Better Horses, Friends&Foes (+Flowery Mooblooms), Artifacts, Doggy Talents Next, Better Dogs, Too Many Bows, Dungeon's Delight, Fisherman's Haven, Aquaculture 2 have **no known client perf issues found** relevant to the pinned versions.
---
## Notable (the 3–5 that matter most)
1. **Alex's Mobs unofficial port is the top entity-count risk.** Raguto's 1.21.1 port has an **open** issue that it "ramps up vanilla monster spawn rates like crazy" ([#62](https://github.com/Raguto/AlexsMobs-1.21.1/issues/62)), on top of the mod's long-standing over-spawning reputation and Citadel's chunk-loading lag ([Citadel #156](https://github.com/AlexModGuy/Citadel/issues/156)). This is a *tune-spawns* lever, and per-mob spawn probabilities are exposed in the `alexsmobs` config folder (**DOCUMENTED** via Fandom Configuration, not read directly).
2. **Fowlplay's bird spawning is un-bounded in practice.** #214 (open, **1.21.1 NeoForge**) reports up to ~400 birds / ~200 in one area even after reducing group spawns, and #196 shows flight entities recompute pathfinding **every tick**. Even though the tick cost is server-side, hundreds of visible birds is a direct client frame + RAM cost, and this is the mod most likely to need spawn tuning or removal.
3. **Fresh Animations + EMF with many mobs is the largest measured FPS multiplier.** EMF #443 shows 4–8× FPS loss with 30–50 mobs on screen on the Sodium/Iris stack. Fixing this (EMF "Animation LOD distance" / "Retain LOD at low fps") is cheaper than removing content mods, and it compounds with every animated mob mod above.
4. **Ars Nouveau is mostly fine at 5.13.1, but has two known client hitches.** The mirrorweave/SkyWeave rendering FPS bug is **already fixed** (PR #1714, Mar 2025, well before 5.13.1), but the **Storage Lectern sorting Apotheosis-affixed items** (#1274, large client lag) and **Agronomic Sourcelink entity spam** (#1904, <20 fps) remain behaviour that a busy server can hit. Note it also flags EntityCulling as the mitigation — which you already run.
5. **Critters and Companions has no config file** — spawns are data-file driven, so the only lever is a **datapack override** ([#91](https://github.com/bonsaistudi0s/CrittersAndCompanions/issues/91)). If chunk-loading stutter or dragonfly pile-ups show up in profiling, that's the fix (or removal), not a TOML tweak.
---
## Sources
**Kept:**
- Raguto/AlexsMobs-1.21.1 issue #62 (spawn inflation, 1.21.1 NeoForge) — https://github.com/Raguto/AlexsMobs-1.21.1/issues/62
- Citadel issue #156 (chunk loading, entity tracking toggle, closed-completed 2025) — https://github.com/AlexModGuy/Citadel/issues/156
- Citadel issue #12 (historic 609 MB memory leak) — https://github.com/Alex-the-666/Citadel/issues/12
- Alexs-Mobs-Fabric issue #121 (Sodium/Iris vertex explosion, Fabric build) — https://github.com/astryxion/Alexs-Mobs-Fabric/issues/121
- Mowzie's Mobs-Public issues #30 / #38 / #59 (structure + Elokosa tick perf; #30 fixed next patch) — https://github.com/BobMowzie/MowziesMobs-Public/issues/30
- Adorable Hamster Pets changelogs (3.6.1 AI/tick fix, 3.6.0 NBT fix) — https://modrinth.com/mod/adorable-hamster-pets/version/QitZ5fVO
- Doggy Talents Next FAQ (beds capped to avoid perf issues) — https://doggytalentsnext.wiki.gg/wiki/FAQ
- Icy's Better Horses issue #31 (client crash, not perf; fixed) — https://github.com/Icywhenhot/Icy-s-Better-Horses/issues/31
- Critters and Companions issue #91 (over-spawn, datapack-only config) — https://github.com/bonsaistudi0s/CrittersAndCompanions/issues/91
- Fowl-Play issues #196 / #214 / #233 (per-tick pathfinding, bird over-spawn, spawn-mod incompat) — https://github.com/aqariio/Fowl-Play/issues/214
- Ars Nouveau issues #379 / #1221 / #1274 / #1904 / #2108 (particles, mirrorweave fixed, lectern, sourcelink, planarium) — https://github.com/baileyholl/Ars-Nouveau/issues/1221
- Apotheosis issue #1191 (perf fix in 7.3.2) + changelogs 1.21 (loot beams, glow, shield breaker) — https://github.com/Shadows-of-Fire/Apotheosis/blob/1.21/changelog.md
- Aquaculture issue #113 (loot-calc lag, fixed in 2.1.0) — https://github.com/TeamMetallurgy/Aquaculture/issues/113
- EMF issue #443 + EMF changelog (Fresh Animations FPS blow-up; Animation LOD settings) — https://github.com/Traben-0/Entity_Model_Features/issues/443
- Mermod issues search (0 perf results = negative evidence) — https://github.com/ThatPreston/Mermod/issues?q=is%3Aissue+lag+OR+fps+OR+performance+OR+memory
- Modrinth/CurseForge pages for Friends&Foes 4.x, Flowery Mooblooms, Wings of Fire, Dungeon's Delight 1.5.1, Fisherman's Haven, Too Many Bows, Artifacts, Mermod 3.3.2 — used for version/feature/bugfix confirmation.
**Dropped:**
- SteamPunk #83, Reddit r/feedthebeast Alex's Mobs threads — used only as corroboration for the over-spawn theme; individual player reports not weighted as fact.
- AlexsCaves #168 / #616 (Ray Cat FPS drop) — different mod (Alex's Caves), not in this pack.
- Optifine #3538 (Aquaculture bobber) — Optifine-specific, not applicable to Sodium.
- Crackpack-3 #101 (Frostmaw FPS) — 1.12-era, not applicable to 1.21.1.
- Various "performance mods" listicles/SEO pages — no primary evidence.
- Alex's Mobs Fandom Configuration page — returned HTTP 403; could not read exact config keys.
@@ -0,0 +1,71 @@
# Research: R9 — Libraries / UI / Misc client hooks sweep (Onion MC, 1.21.1 NeoForge 21.1.251)
## Summary
This group is mostly low-cost UI/QoL/library mods: for the large majority **no known client perf issues found** (searched GitHub issue trackers for lag/fps/memory/stutter/sodium/iris). The real, actionable items are: **Puzzles Lib 21.1.62 predates a hot-path event-dispatch fix shipped in 21.1.64**; **Fancy World Animations** has a documented history of heavy chain/lantern animation cost under Iris/Sodium (mitigated in the pinned 1.2.31 via frustum culling + EntityCulling occlusion compat); and **Jade** is the most common *hidden* cost driver here, since its tooltip providers (incl. reading Sophisticated Backpack contents) run on the client while pointing at blocks.
## Findings table
| Mod | What it does (1 line) | Client cost type | Known perf issues (URL, fixed?) | Perf-relevant config levers (exact keys only if seen) | Value vs cost |
|---|---|---|---|---|---|
| Fancy World Animations (fwa) 1.2.31 | Animates doors/trapdoors/chests/chains/lanterns etc. (mixes into BlockRenderer, ShadowRenderer, RenderChunkRegion) | **Frame** (per-animated-block render work, worst w/ 3D-texture chain packs + shaders) | DOCUMENTED: 3D chains/lanterns caused ~2× FPS loss under Iris/Sodium pre-1.1.17 (https://github.com/maDU59/FancyWorldAnimations/issues/24 — fixed via frustum culling in 1.1.17, EntityCulling occlusion-culling compat + cached bounding boxes in **1.2.31**, per issue #24 and #74). CPU-temp complaint: https://github.com/maDU59/FancyWorldAnimations/issues/83 (closed). Sodium visual-only bugs #27/#34/#64/#78 (closed). Embeddium duplicate-render #91 (https://github.com/maDU59/FancyWorldAnimations/issues/91) — **not applicable**: dev states Embeddium unsupported, "use Sodium"; pack uses real Sodium 0.8.13 | Per-animation toggles in FWA config (chains / lanterns / doors…) — seen in issue #24 discussion, not read from the config file itself | **Keep** (pin ≥1.2.31; already pinned). **Tune on Steam Deck**: consider disabling chain/lantern animations in mineshaft-heavy exploration; they are the documented hot path |
| Moonlight Lib 3.7.1 | MehVahdJukaar library (Supplementaries-family APIs, dynamic asset caching) | Startup (conditional) | DOCUMENTED: dynamic-asset cache system massively slowed modpack startup (160→320s) when paired with asset-generating mods (EveryCompat) — https://github.com/MehVahdJukaar/Moonlight/issues/382 (open, but traced to EveryCompat generation-mode config + zip listing behavior). No Moonlight-modded mods of that family appear in this pack's list | `everycomp-*.toml` generation modes — only relevant if EveryCompat/Supplementaries present (not in this topic's list) | **Keep** if a listed mod needs it; else **candidate-remove** (verify dependents first) |
| Iceberg 1.3.2 | Grend_G modding library (events/helpers, animated texture utils) | none-known | No known client perf issues found (issue tracker shows crashes/compat, not perf) | — | Keep (required by dependents) |
| Puzzles Lib 21.1.62 | Fuzss multiloader framework (events, config screens, GUI helpers) used by several UI mods | **Frame** (event-dispatch overhead) + startup | VERIFIED: ~8% of render thread spent in `EntityRenderer.handler$…$puzzleslib$createRenderState()` via dynamic-proxy/MethodHandle dispatch (per-entity per-frame hot path) — https://github.com/Fuzss/puzzles-lib/issues/202 (fixed). **Fix backported to 1.21.1 in v21.1.64 (2026-10-11): "Cache method handles in FabricEventFactory to reduce event listener invocation overhead"** — changelog: https://modrinth.com/mod/puzzles-lib (versions 21.1.63/21.1.64 for 1.21.1 NeoForge). **Pack pins 21.1.62 (2026-09-29) → predates the fix.** Note the ~8% figure was measured on MC 26.2/Fabric; the 1.21.1 changelog entry confirms the same dispatch path exists in this line | — | **Tune → update 21.1.62 → 21.1.64+** (highest-value single change in this group) |
| Balm 21.0.66 | BlayTheNinth cross-version library (Waystones etc.) | none-known | No known client perf issues found | — | Keep (Waystones dep) |
| Chalk 2.0.2 | Draws chalk marks on blocks | none-known | No known client perf issues found | — | Keep |
| Charta 1.2.5 | Card-game mod (playable card tables) | Startup / VRAM (INFERENCE: registers ~31 core shaders at resource load); frame only at card tables | Server-side lag-spike on player join: https://github.com/lucaargolo/charta/issues/14 (closed). No client perf issues found | — | **Candidate-remove** if the group doesn't play cards (client+server mod — must be removed on both sides); shaders tax startup/VRAM regardless of use |
| Client Sort 3.89.1 | Sort/move items in any inventory screen (client-only) | Frame (only inside container screens) | No known client perf issues found | — | Keep |
| Sounds (sounds) 2.4.22 | UI/ambient sound polish mod | none-known | No known client perf issues found | — | Keep |
| Now Playing 1.5.16 | Shows currently playing music track (HUD/toast) | Frame (tiny, HUD element) | No known client perf issues found | — | Keep |
| Music Disc Maker 3.0.5 | Creates custom music discs | none-known | No known client perf issues found | — | Keep |
| cat_jam | Cats bob heads to music (cosmetic) | Frame (negligible) | No known client perf issues found | — | Keep |
| PlayerRevive 2.1.2 (+ CreativeCore 2.13.48) | Downed/revive multiplayer mechanic | Frame (downed-player overlays) | No known client perf issues found | — | Keep |
| Jade 15.10.6 | WAILA-style "what am I looking at" tooltip | **Client-tick + frame** while pointing at blocks/entities; tooltip providers can read container contents | VERIFIED: FPS→0 ~1s when looking at player heads — https://github.com/Snownee/Jade/issues/742 (closed not-planned on 1.21.10 branch, but **workaround applies to 1.21.1**: add `player_head` to `hide-blocks.json`). Freeze looking at containers documented but tied to ViaFabricPlus server-version bridging (https://github.com/Snownee/Jade/issues/783, dup https://github.com/Snownee/Jade/issues/790) — not applicable to a native 1.21.1 server. SB-content reading implicated as the real cost in https://github.com/P3pp3rF1y/SophisticatedBackpacks/issues/1352 (owner: "another mod that shows backpack contents (like Jade)"). Historical client-disconnect memory leak PR: https://github.com/Snownee/Jade/pull/405 (long since in 15.x line) | `hide-blocks.json` (config file, exact name seen in #742); provider/plugin toggles in Jade's in-game config (key names not verified — do not guess) | **Keep + tune**: hide player heads/decor blocks in `hide-blocks.json`; if profiling shows tooltip cost, disable container-contents display — biggest overlap is with Sophisticated Backpacks |
| Patchouli 1.21-88 line | Guide-book framework (botania-style books) | Client-tick / frame inside books; startup (parses book JSONs) | DOCUMENTED: excessive client lag spike on advancement toasts — https://github.com/VazkiiMods/Patchouli/issues/481 (1.18.1 era, closed; not confirmed still present in 1.21.1). Crash-after-days on 1.21.1 NeoForge: https://github.com/VazkiiMods/Patchouli/issues/795 (stability, not perf) | — | Keep (needed by guide books); only re-check #481-class advancement stutter if it reproduces |
| Modonomicon | kli_kli data-driven guide-book mod (Patchouli alternative) | Client-tick / frame inside books | No known client perf issues found | — | Keep |
| Sophisticated Backpacks/Storage/Core | Upgradeable backpacks + storage drawers | Client-tick when tooltip/Jade reads contents; frame on heavy-content tooltips | VERIFIED interplay: "Massive Lag when Looking at Backpack" traced by the SB owner to content-display mods (Jade), not SB itself — https://github.com/P3pp3rF1y/SophisticatedBackpacks/issues/1352 (closed). NBT-heavy-content tooltip stutter pattern documented for other backpack mods: https://github.com/AllTheMods/ATM-6/issues/490 | — | Keep; **tune**: if Jade+SB tooltips stutter on the Deck, hide backpack/stack contents in Jade or stop shift-hover previews |
| Zume 1.2.2 | Smooth zoom keybind | none-known | No known client perf issues found | — | Keep |
| Quick Skin 3.0.1 | Reload own skin without relog | none-known | No known client perf issues found | — | Keep |
| Room For Two 0.3.0 | Two players share one bed / sit together | none-known | No known client perf issues found | — | Keep |
| Keybind Atlas | Searchable keybind manager screen | none-known | No known client perf issues found | — | Keep |
| FindMe | Hover-item search/highlight across nearby inventories | Client-tick (search scope) | No known client perf issues found | — | Keep |
| OctoLib/ShatterLib 0.6.2 | Small library dependency | none-known | No known client perf issues found | — | Keep (if listed as dependency; verify consumer mod before removing) |
| Fragmentum 5.2.0 | Small library dependency | none-known | No known client perf issues found | — | Keep (same caveat) |
| Mino++ 1.4.0 | Small library/utility mod | none-known | No known client perf issues found | — | Keep (verify consumer mod) |
| Comforts | Sleeping bags/hammocks | none-known | No known client perf issues found | — | Keep |
| MRU 1.0.41 | Mrthomas20121 utility library | none-known | No known client perf issues found | — | Keep (verify consumer mod) |
| Blueprint 8.2.0 | TeamAbODE library (Chipped/variant mods family) | none-known | No known client perf issues found | — | Keep |
| YACL | YetAnotherConfigLib — config screens | none-known | No known client perf issues found | — | Keep |
| Extreme Sound Muffler | Muffle selected sounds | none-known | No known client perf issues found | — | Keep |
| Loot Journal | Journal of looted items | Client-tick (event log) | No known client perf issues found | — | Keep |
| Enchantment Descriptions | Enchant descriptions in tooltips | Frame (tooltip text only) | No known client perf issues found | — | Keep |
| Mouse Tweaks | Drag-scroll inventory manipulation | none-known | No known client perf issues found | — | Keep |
| Clumps | Merges XP orbs into single entities | **Negative cost** (reduces entity render/tick load client & server) | Old server-side orb flood w/ Apotheosis: https://github.com/jaredlll08/Clumps/issues/22 (2019-era, closed) | — | **Keep** — net perf positive |
| Gravestone (henkelmax) | Places grave with inventory on death | none-known | No known client perf issues found | — | Keep |
| Waystones | Fast-travel waystones | none-known (client) | Server-side worldgen/save hangs: https://github.com/TwelveIterations/Waystones/issues/428 + #448 — **fixed Nov 2021**, not client-applicable | — | Keep |
| Lootr | Per-player instanced loot chests | none-known (client) | Server-side SavedData disk-write lag spikes: https://github.com/LootrMinecraft/Lootr/issues/804 (closed Jun 2026; author: general SavedData/IO issue, improved by data-system rewrite — server concern, not client) | — | Keep (note for the *server* perf report) |
| Open Parties and Claims (client side) | Chunk claims/parties + claim overlay (uses Xaero maps) | Frame (claim outline overlay) | VERIFIED: client-side claim-coloring optimization was implemented in 2022 — https://github.com/thexaero/open-parties-and-claims/issues/67 (closed completed Sep 2022). #758 boat-collision and #816 1M-claim-crash items are server-side | — | Keep |
| Apothic Combat | Combat overhaul module (Apotheosis-family) | Frame (particle/affix rendering in fights) | DOCUMENTED (adjacent, Apotheosis 1.20.1): dual-wield affixed weapons + Better Combat tank FPS — https://github.com/Shadows-of-Fire/Apotheosis/issues/804. **Better Combat is not in this pack**, so not directly applicable — INFERENCE | — | Keep |
| Explorer's / Nature's Compass | Locate biomes/structures, HUD display | Client-tick (search runs server-side + synced result) | No known client perf issues found | — | Keep |
## Notable
1. **Bump Puzzles Lib 21.1.62 → 21.1.64.** The pinned version predates the "cache method handles in FabricEventFactory" fix (v21.1.64, 2026-10-11) that resolved a measured ~8% render-thread hot-path overhead in per-entity render-state creation ([issue #202](https://github.com/Fuzss/puzzles-lib/issues/202); 1.21.1 changelog on [Modrinth](https://modrinth.com/mod/puzzles-lib)). This is the only confirmed actionable perf bug in this entire group.
2. **FWA 1.2.31 is the right version to be on, but it is not free.** Chains/lanterns with 3D-texture packs + shaders were a documented ~2× FPS loss; frustum culling (1.1.17) and EntityCulling occlusion-culling compat + cached bounding boxes (1.2.31) address exactly this ([#24](https://github.com/maDU59/FancyWorldAnimations/issues/24)). On the Steam Deck (weakest GPU), toggling chain/lantern animations in FWA config for mineshaft-heavy play is the cheap lever. The Embeddium duplicate-render issue [#91](https://github.com/maDU59/FancyWorldAnimations/issues/91) does not apply — dev explicitly supports Sodium, which this pack uses.
3. **Jade is the hidden cost driver of this group, not the mods it points at.** The SB "looking at backpack = 20-30 FPS" report was attributed by the SB author to Jade's content display ([SB #1352](https://github.com/P3pp3rF1y/SophisticatedBackpacks/issues/1352)); player-head tooltips could drop FPS to 0, fixed for users via `hide-blocks.json` ([Jade #742](https://github.com/Snownee/Jade/issues/742)). Pre-tune Jade (hide player heads; trim container-contents display) before blaming Sophisticated Backpacks in benchmarks.
4. **Charta is a plausible small startup/VRAM tax for a mod most players rarely use** (~31 core shaders registered at resource load — INFERENCE from pack file listing; no client perf issue reported in its tracker). If the card tables are unused, remove it on client+server.
5. **Most of the scary issues in these trackers are server-side** (Lootr SavedData writes, Waystones worldgen, OPAC boat collisions/claim-crash, Charta join-lag) — they belong in the server perf report, not the client one. For the client, "no known client perf issues found" genuinely applies to ~30 of these 40 mods.
## Sources
- Kept: FWA issue tracker + #24/#74/#91/#83 (github.com/maDU59/FancyWorldAnimations) — authoritative on the pinned version's perf history
- Kept: Puzzles Lib #202 + Modrinth 1.21.1 version changelogs (api.modrinth.com/v2/project/puzzles-lib) — proves fix version vs pinned version
- Kept: Jade #742/#783/#790 (github.com/Snownee/Jade) — real client-tick/frame costs + the `hide-blocks.json` lever
- Kept: SophisticatedBackpacks #1352 (github.com/P3pp3rF1y/SophisticatedBackpacks) — attributes the cost to Jade-style content display
- Kept: Moonlight #382 (github.com/MehVahdJukaar/Moonlight) — startup cost mechanism, conditional on asset-gen mods
- Kept: Lootr #804, Waystones #428/#448, OPAC #67/#758/#816, Charta #14 — confirm server-side classification
- Dropped: Reddit threads (r/feedthebeast "which mods cause most lag", UsefulMods wiki) — generic, not version-pinned, uncorroborated for these exact versions
- Dropped: Iceberg/Clumps unrelated repos (apache/iceberg, moonlight-stream) — name collisions, not the MC mods
## Gaps
- Could not find per-mod client benchmarks; magnitudes are from individual user spark profiles, not controlled tests — validate Puzzles Lib gain and FWA chain cost with a spark profile on one Deck before/after.
- Exact config key names for Jade (hide-blocks list location beyond `hide-blocks.json`), FWA per-animation toggles, and any Sophisticated Backpacks tooltip toggle were not read from the mods' source/config files (only referenced in issues) — do not write keys into pack configs without opening the jars.
- Several tiny mods (Mino++, OctoLib/ShatterLib, Fragmentum, MRU, Keybind Atlas, Room For Two, Music Disc Maker) have little public issue activity at all; their "none-known" verdicts reflect absence of evidence. Before removal, verify which installed mod consumes each library (check each jar's `META-INF/neoforge.mods.toml` dependency list).
+384
View File
@@ -0,0 +1,384 @@
#!/usr/bin/env python3
"""Static client-perf surface scan of every mod jar in the pack.
Usage: jarscan.py <jar-dir> <repo-root> <out.json>
Per mod (including jar-in-jar children, attributed to the parent):
- mixins: count by side, @Mixin targets, injector kinds (Overwrite/Redirect...)
- client hot-path classification of mixin targets
- classes referencing per-frame / per-tick client event types
- texture pressure: block/item atlas pixels, entity textures, animated textures
- models, blockstates, shaders (incl. vanilla core shader overrides), sounds
- geckolib geo/animation files, EMF/OptiFine CEM files, particles
- registered-content proxies from en_us.json keys
"""
import io, json, os, re, struct, sys, zipfile, tomllib
from collections import Counter, defaultdict
# ---------- class file parsing (constant pool + annotations) ----------
def parse_class(data):
"""Return (utf8 set, class annotations, method annotations) for a class file.
Annotations are dicts {type: {elem: value}} for RuntimeInvisible/Visible."""
if data[:4] != b'\xca\xfe\xba\xbe':
return None
pos = 10
count = struct.unpack('>H', data[8:10])[0]
cp = [None] * count
i = 1
while i < count:
tag = data[pos]
if tag == 1:
ln = struct.unpack('>H', data[pos+1:pos+3])[0]
cp[i] = ('utf8', data[pos+3:pos+3+ln].decode('utf-8', 'replace'))
pos += 3 + ln
elif tag in (3, 4):
cp[i] = ('num', None); pos += 5
elif tag in (5, 6):
cp[i] = ('num', None); pos += 9; i += 1
elif tag == 7:
cp[i] = ('class', struct.unpack('>H', data[pos+1:pos+3])[0]); pos += 3
elif tag in (8, 16, 19, 20):
cp[i] = ('ref', struct.unpack('>H', data[pos+1:pos+3])[0]); pos += 3
elif tag in (9, 10, 11, 12, 17, 18):
cp[i] = ('ref2', None); pos += 5
elif tag == 15:
cp[i] = ('mh', None); pos += 4
else:
return None
i += 1
def u(idx):
e = cp[idx]
return e[1] if e and e[0] == 'utf8' else None
utf8 = {e[1] for e in cp if e and e[0] == 'utf8'}
def read_elem(p):
tag = chr(data[p]); p += 1
if tag in 'BCDFIJSZs':
return u(struct.unpack('>H', data[p:p+2])[0]), p + 2
if tag == 'e':
return u(struct.unpack('>H', data[p+2:p+4])[0]), p + 4
if tag == 'c':
return u(struct.unpack('>H', data[p:p+2])[0]), p + 2
if tag == '@':
return read_ann(p)
if tag == '[':
n = struct.unpack('>H', data[p:p+2])[0]; p += 2
out = []
for _ in range(n):
v, p = read_elem(p); out.append(v)
return out, p
raise ValueError(tag)
def read_ann(p):
t = u(struct.unpack('>H', data[p:p+2])[0])
n = struct.unpack('>H', data[p+2:p+4])[0]; p += 4
elems = {}
for _ in range(n):
name = u(struct.unpack('>H', data[p:p+2])[0]); p += 2
v, p = read_elem(p); elems[name] = v
return (t, elems), p
def read_attrs(p):
anns = []
n = struct.unpack('>H', data[p:p+2])[0]; p += 2
for _ in range(n):
name = u(struct.unpack('>H', data[p:p+2])[0])
ln = struct.unpack('>I', data[p+2:p+6])[0]
body = p + 6
if name in ('RuntimeInvisibleAnnotations', 'RuntimeVisibleAnnotations'):
k = struct.unpack('>H', data[body:body+2])[0]; q = body + 2
for _ in range(k):
a, q = read_ann(q); anns.append(a)
p = body + ln
return anns, p
try:
p = pos + 6
ni = struct.unpack('>H', data[p:p+2])[0]; p += 2 + 2 * ni
method_anns = []
for _member in ('fields', 'methods'):
n = struct.unpack('>H', data[p:p+2])[0]; p += 2
for _ in range(n):
anns, p = read_attrs(p + 6)
if _member == 'methods':
method_anns.extend(anns)
class_anns, p = read_attrs(p)
except Exception:
return utf8, [], []
return utf8, class_anns, method_anns
# ---------- classification ----------
HOT = [ # (category, regex on internal class name of mixin target)
('blaze3d', r'^com/mojang/blaze3d/'),
('sodium', r'^net/caffeinemc/'),
('iris', r'^net/irisshaders/'),
('world_render', r'^net/minecraft/client/renderer/(LevelRenderer|GameRenderer|LightTexture|FogRenderer|DimensionSpecialEffects|SectionOcclusionGraph|ViewArea|chunk/|culling/|PostChain|RenderType|RenderStateShard|ShaderInstance|LevelEventHandler)'),
('entity_render', r'^net/minecraft/client/(renderer/entity/|model/|renderer/blockentity/|renderer/ItemInHandRenderer)'),
('item_render', r'^net/minecraft/client/renderer/(ItemRenderer|item/)'),
('block_model', r'^net/minecraft/client/(renderer/block/|resources/model/|renderer/ItemBlockRenderTypes)'),
('texture_load', r'^net/minecraft/client/(renderer/texture/|resources/)'),
('camera', r'^net/minecraft/client/Camera'),
('gui', r'^net/minecraft/client/gui/'),
('particle', r'^net/minecraft/client/particle/'),
('sound', r'^(net/minecraft/client/sounds/|com/mojang/blaze3d/audio/)'),
('client_core', r'^net/minecraft/client/(Minecraft|multiplayer/|player/|KeyboardHandler|MouseHandler|Options)'),
('client_other', r'^net/minecraft/client/'),
('entity_common', r'^net/minecraft/world/entity/'),
('level_common', r'^net/minecraft/world/level/'),
('server', r'^net/minecraft/server/'),
('mc_other', r'^net/minecraft/'),
('other_mod', r'.'),
]
HOT_RE = [(c, re.compile(r)) for c, r in HOT]
FRAME_CATS = {'blaze3d', 'sodium', 'iris', 'world_render', 'entity_render', 'item_render', 'camera', 'gui', 'particle'}
def classify(target):
for c, r in HOT_RE:
if r.search(target):
return c
return 'other_mod'
EVENTS = {
# per-frame
'RenderLevelStageEvent': 'frame', 'RenderFrameEvent': 'frame', 'RenderGuiEvent': 'frame',
'RenderGuiLayerEvent': 'frame', 'CustomizeGuiOverlayEvent': 'frame', 'RenderLivingEvent': 'frame',
'RenderPlayerEvent': 'frame', 'RenderHandEvent': 'frame', 'RenderArmEvent': 'frame',
'RenderHighlightEvent': 'frame', 'ViewportEvent': 'frame', 'RenderNameTagEvent': 'frame',
'RenderBlockScreenEffectEvent': 'frame', 'RenderTooltipEvent': 'frame', 'ScreenEvent': 'screen',
'ComputeFovModifierEvent': 'frame',
# per-tick
'ClientTickEvent': 'tick', 'PlayerTickEvent': 'tick', 'EntityTickEvent': 'tick', 'LevelTickEvent': 'tick',
# load
'ModelEvent': 'load', 'RegisterShadersEvent': 'load', 'TextureAtlasStitchedEvent': 'load',
}
EVENT_RE = re.compile(r'net/neoforged/neoforge/(?:client/)?event/(?:[a-z/]+/)?(' + '|'.join(EVENTS) + r')(?:\$(\w+))?')
OTHER_FRAME_RE = re.compile(r'(dev/architectury/event/events/client/(ClientGuiEvent|ClientTickEvent)|net/fabricmc/fabric/api/client/rendering/v1/(WorldRenderEvents|HudRenderCallback|HudLayerRegistrationCallback)|net/blay09/mods/balm/api/event/client/(GuiDrawEvent|RenderHandEvent|ClientTickHandler))')
INJECTORS = {
'Lorg/spongepowered/asm/mixin/Overwrite;': 'Overwrite',
'Lorg/spongepowered/asm/mixin/injection/Redirect;': 'Redirect',
'Lorg/spongepowered/asm/mixin/injection/Inject;': 'Inject',
'Lorg/spongepowered/asm/mixin/injection/ModifyVariable;': 'ModifyVariable',
'Lorg/spongepowered/asm/mixin/injection/ModifyArg;': 'ModifyArg',
'Lorg/spongepowered/asm/mixin/injection/ModifyArgs;': 'ModifyArgs',
'Lorg/spongepowered/asm/mixin/injection/ModifyConstant;': 'ModifyConstant',
'Lcom/llamalad7/mixinextras/injector/wrapoperation/WrapOperation;': 'WrapOperation',
'Lcom/llamalad7/mixinextras/injector/wrapmethod/WrapMethod;': 'WrapMethod',
'Lcom/llamalad7/mixinextras/injector/ModifyExpressionValue;': 'ModifyExpressionValue',
'Lcom/llamalad7/mixinextras/injector/ModifyReturnValue;': 'ModifyReturnValue',
'Lcom/llamalad7/mixinextras/injector/WrapWithCondition;': 'WrapWithCondition',
'Lcom/llamalad7/mixinextras/injector/v2/WrapWithCondition;': 'WrapWithCondition',
}
def png_size(b):
if b[:8] == b'\x89PNG\r\n\x1a\n' and b[12:16] == b'IHDR':
return struct.unpack('>II', b[16:24])
return None
def load_json_lenient(b):
s = b.decode('utf-8', 'replace')
s = re.sub(r'^\s*//.*$', '', s, flags=re.M)
try:
return json.loads(s)
except Exception:
return None
def mod_ids(z):
for name in ('META-INF/neoforge.mods.toml', 'META-INF/mods.toml'):
if name in z.namelist():
try:
t = tomllib.loads(z.read(name).decode('utf-8', 'replace'))
except Exception:
return [], []
ids = [m.get('modId') for m in t.get('mods', [])]
mix = [m.get('config') for m in t.get('mixins', []) if m.get('config')]
return ids, mix
return [], []
def scan_zip(z, acc, depth=0):
names = z.namelist()
nameset = set(names)
ids, mixcfgs = mod_ids(z)
acc['mod_ids'].extend(i for i in ids if i)
if 'META-INF/MANIFEST.MF' in nameset:
mf = z.read('META-INF/MANIFEST.MF').decode('utf-8', 'replace').replace('\r\n ', '').replace('\n ', '')
m = re.search(r'^MixinConfigs:\s*(.+)$', mf, re.M)
if m:
mixcfgs += [c.strip() for c in m.group(1).split(',')]
if 'fabric.mod.json' in nameset:
fj = load_json_lenient(z.read('fabric.mod.json')) or {}
for c in fj.get('mixins', []):
mixcfgs.append(c if isinstance(c, str) else c.get('config'))
mixcfgs = sorted({c for c in mixcfgs if c and c in nameset})
# mixin classes
mixin_classes = {} # path -> side
for cfg in mixcfgs:
j = load_json_lenient(z.read(cfg)) or {}
pkg = (j.get('package') or '').replace('.', '/')
for side in ('mixins', 'client', 'server'):
for c in j.get(side, []) or []:
mixin_classes[f'{pkg}/{c.replace(".", "/")}.class'] = {'mixins': 'common'}.get(side, side)
acc['mixin_configs'] += len(mixcfgs)
tex = acc['tex']
for n in names:
if n.endswith('/'):
continue
if n.endswith('.class'):
try:
data = z.read(n)
except Exception:
continue
parsed = parse_class(data)
if not parsed:
continue
utf8, canns, manns = parsed
if n in mixin_classes:
side = mixin_classes[n]
acc['mixins'][side] += 1
targets = []
for t, el in canns:
if t == 'Lorg/spongepowered/asm/mixin/Mixin;':
for v in el.get('value', []) or []:
if v: targets.append(v[1:-1])
for v in el.get('targets', []) or []:
if v: targets.append(v.replace('.', '/'))
kinds = Counter(INJECTORS[t] for t, _ in manns if t in INJECTORS)
acc['injectors'].update(kinds)
for tg in targets or ['?']:
cat = classify(tg)
acc['targets'][tg] += 1
acc['target_cats'][cat] += 1
if cat in FRAME_CATS:
acc['frame_mixins'] += 1
acc['frame_targets'][tg.rsplit('/', 1)[-1]] += 1
acc['frame_injectors'].update(kinds)
evs = set()
for s in utf8:
for m in EVENT_RE.finditer(s):
evs.add(m.group(1) + ('$' + m.group(2) if m.group(2) else ''))
m = OTHER_FRAME_RE.search(s)
if m:
evs.add(m.group(1).rsplit('/', 1)[-1])
for e in evs:
acc['events'][e] += 1
continue
if n.startswith(('META-INF/jarjar/', 'META-INF/jars/')) and n.endswith('.jar') and depth < 3:
try:
sub = zipfile.ZipFile(io.BytesIO(z.read(n)))
subnames = set(sub.namelist())
if 'fabric.mod.json' in subnames and not subnames & {'META-INF/neoforge.mods.toml', 'META-INF/mods.toml'}:
continue # Fabric-only payload of a multi-loader jar; never loaded on NeoForge
acc['nested'].append(n.rsplit('/', 1)[-1])
scan_zip(sub, acc, depth + 1)
except Exception:
pass
continue
parts = n.split('/')
if parts[0] != 'assets' or len(parts) < 3:
continue
ns, kind = parts[1], parts[2]
if kind == 'textures' and n.endswith('.png'):
sub = parts[3] if len(parts) > 4 else ''
info = z.getinfo(n)
with z.open(n) as fh:
wh = png_size(fh.read(24))
px = wh[0] * wh[1] if wh else 0
anim = None
if wh and n + '.mcmeta' in nameset:
mm = load_json_lenient(z.read(n + '.mcmeta')) or {}
anim = mm.get('animation') if isinstance(mm, dict) else None
if isinstance(anim, dict):
# only one frame lives in the atlas; the rest is CPU-side upload source
fw = anim.get('width', min(wh)); fh = anim.get('height', fw if 'width' in anim else min(wh))
frames = max(1, (wh[0] // max(1, fw)) * (wh[1] // max(1, fh)))
tex['anim_frames'] += frames
px = fw * fh
bucket = {'block': 'atlas', 'item': 'atlas', 'blocks': 'atlas', 'items': 'atlas',
'entity': 'entity', 'models': 'entity', 'particle': 'particle',
'gui': 'gui', 'environment': 'env', 'painting': 'painting', 'mob_effect': 'gui'}.get(sub, 'other')
tex[bucket + '_n'] += 1
tex[bucket + '_px'] += px
tex['bytes'] += info.file_size
if wh and max(wh) > 512 and bucket in ('atlas', 'entity'):
acc['big_textures'].append(f'{n} {wh[0]}x{wh[1]}' + (' (animated)' if anim else ''))
if isinstance(anim, dict) and bucket == 'atlas':
tex['animated_n'] += 1
elif kind == 'models' and n.endswith('.json'):
acc['assets']['models'] += 1
elif kind == 'blockstates' and n.endswith('.json'):
acc['assets']['blockstates'] += 1
elif kind == 'shaders':
if n.endswith(('.vsh', '.fsh', '.glsl', '.json')):
acc['assets']['shader_files'] += 1
if ns == 'minecraft' and parts[3] == 'core':
acc['core_shader_overrides'].append(n)
if parts[3] == 'post':
acc['assets']['post_effects'] += 1
elif kind == 'sounds' and n.endswith('.ogg'):
acc['assets']['ogg_n'] += 1
acc['assets']['ogg_bytes'] += z.getinfo(n).file_size
elif kind in ('geo', 'geckolib') or (kind == 'animations' and n.endswith('.json')):
acc['assets']['geckolib_files'] += 1
elif kind == 'particles' and n.endswith('.json'):
acc['assets']['particle_defs'] += 1
elif kind == 'optifine' or kind == 'emf' or kind == 'etf':
acc['assets']['cem_files'] += 1
elif kind == 'lang' and parts[-1] == 'en_us.json':
j = load_json_lenient(z.read(n)) or {}
for k in j:
p0 = k.split('.', 1)[0]
if p0 in ('block', 'item', 'entity', 'biome', 'enchantment', 'effect'):
acc['lang'][p0] += 1
def new_acc():
return {'mod_ids': [], 'nested': [], 'mixin_configs': 0, 'mixins': Counter(),
'targets': Counter(), 'target_cats': Counter(), 'frame_mixins': 0,
'frame_targets': Counter(), 'frame_injectors': Counter(), 'injectors': Counter(),
'events': Counter(), 'tex': Counter(), 'big_textures': [], 'assets': Counter(),
'core_shader_overrides': [], 'lang': Counter()}
def main():
jar_dir, repo, out = sys.argv[1:4]
meta = {}
for d in ('mods', 'resourcepacks', 'shaderpacks'):
for f in os.listdir(os.path.join(repo, d)):
if not f.endswith('.pw.toml'):
continue
t = tomllib.load(open(os.path.join(repo, d, f), 'rb'))
meta[t['filename']] = {'slug': f[:-8], 'name': t['name'], 'side': t.get('side', 'both'), 'dir': d,
'optional': bool(t.get('option', {}).get('optional'))}
results = {}
for fn, m in sorted(meta.items()):
path = os.path.join(jar_dir, fn)
if not os.path.exists(path):
print('missing', fn, file=sys.stderr)
continue
acc = new_acc()
try:
scan_zip(zipfile.ZipFile(path), acc)
except zipfile.BadZipFile:
print('badzip', fn, file=sys.stderr)
continue
acc['jar_bytes'] = os.path.getsize(path)
acc['big_textures'] = acc['big_textures'][:15]
acc['targets'] = dict(acc['targets'].most_common(40))
results[m['slug']] = {**m, 'filename': fn, **{k: (dict(v) if isinstance(v, Counter) else v) for k, v in acc.items()}}
json.dump(results, open(out, 'w'), indent=1, sort_keys=True)
print(f'scanned {len(results)} files -> {out}')
if __name__ == '__main__':
main()
+68
View File
@@ -0,0 +1,68 @@
#!/usr/bin/env python3
"""Jar inspection without unzip/java.
jx.py ls JAR[!nested.jar...] [regex] list entries
jx.py cat JAR[!nested.jar...] entry print entry
jx.py str JAR[!nested.jar...] classregex strregex UTF8 constants of classes
"""
import io, re, struct, sys, zipfile
def open_jar(spec):
parts = spec.split("!")
z = zipfile.ZipFile(parts[0])
for p in parts[1:]:
z = zipfile.ZipFile(io.BytesIO(z.read(p)))
return z
def utf8_consts(data):
out = []
if data[:4] != b"\xca\xfe\xba\xbe":
return out
n = struct.unpack(">H", data[8:10])[0]
i, k = 10, 1
while k < n:
tag = data[i]
if tag == 1:
ln = struct.unpack(">H", data[i + 1:i + 3])[0]
out.append(data[i + 3:i + 3 + ln].decode("utf-8", "replace"))
i += 3 + ln
elif tag in (3, 4):
i += 5
elif tag in (5, 6):
i += 9; k += 1
elif tag in (7, 8, 16, 19, 20):
i += 3
elif tag in (9, 10, 11, 12, 17, 18):
i += 5
elif tag == 15:
i += 4
else:
break
k += 1
return out
def main():
cmd, spec = sys.argv[1], sys.argv[2]
z = open_jar(spec)
if cmd == "ls":
rx = re.compile(sys.argv[3]) if len(sys.argv) > 3 else None
for n in z.namelist():
if not rx or rx.search(n):
print(n)
elif cmd == "cat":
sys.stdout.write(z.read(sys.argv[3]).decode("utf-8", "replace"))
elif cmd == "str":
crx, srx = re.compile(sys.argv[3]), re.compile(sys.argv[4])
for n in z.namelist():
if n.endswith(".class") and crx.search(n):
hits = [s for s in utf8_consts(z.read(n)) if srx.search(s)]
if hits:
print("==", n)
for h in hits:
print(" ", repr(h))
main()
+146
View File
@@ -0,0 +1,146 @@
#!/usr/bin/env python3
"""Render data/jarscan.json into ranked markdown tables (data/jarscan-report.md)."""
import json, math, sys
d = json.load(open(sys.argv[1]))
out = open(sys.argv[2], 'w')
P = lambda *a: print(*a, file=out)
FRAME_EVENTS = ('RenderLevelStageEvent', 'RenderFrameEvent', 'RenderGuiEvent', 'RenderGuiLayerEvent',
'CustomizeGuiOverlayEvent', 'RenderLivingEvent', 'RenderPlayerEvent', 'RenderHandEvent',
'RenderArmEvent', 'RenderHighlightEvent', 'ViewportEvent', 'RenderNameTagEvent',
'RenderBlockScreenEffectEvent', 'RenderTooltipEvent', 'ComputeFovModifierEvent',
'WorldRenderEvents', 'HudRenderCallback', 'HudLayerRegistrationCallback', 'ClientGuiEvent', 'GuiDrawEvent')
TICK_EVENTS = ('ClientTickEvent', 'PlayerTickEvent', 'EntityTickEvent', 'LevelTickEvent', 'ClientTickHandler')
def base_events(r, names):
return sorted({e.split('$')[0] for e in r['events'] if e.split('$')[0] in names})
rows = []
for slug, r in d.items():
if r['dir'] != 'mods':
continue
mx = r['mixins']
client_side = r['side'] != 'server'
t = r['tex']
a = r['assets']
fi = r['frame_injectors']
rows.append(dict(
slug=slug, side=r['side'],
mix=sum(mx.values()), cmix=mx.get('client', 0),
frame=r['frame_mixins'],
risky=fi.get('Overwrite', 0) + fi.get('Redirect', 0),
fev=base_events(r, FRAME_EVENTS), tev=base_events(r, TICK_EVENTS),
atlas=t.get('atlas_px', 0), atlas_n=t.get('atlas_n', 0), anim=t.get('animated_n', 0),
ent_px=t.get('entity_px', 0), tex_mb=t.get('bytes', 0) / 1e6,
models=a.get('models', 0), bstates=a.get('blockstates', 0),
shaders=a.get('shader_files', 0), post=a.get('post_effects', 0), core=len(r['core_shader_overrides']),
geo=a.get('geckolib_files', 0), ogg_mb=a.get('ogg_bytes', 0) / 1e6, particles=a.get('particle_defs', 0),
ents=r['lang'].get('entity', 0), blocks=r['lang'].get('block', 0), items=r['lang'].get('item', 0),
jar_mb=r['jar_bytes'] / 1e6, client_side=client_side,
top=', '.join(f'{k}×{v}' for k, v in sorted(r['frame_targets'].items(), key=lambda kv: -kv[1])[:6]),
))
cl = [r for r in rows if r['client_side']]
SP = 256 # one 16x16 sprite
P('# Static client-perf surface scan')
P()
P('Generated by `scripts/jarscan.py` + `scripts/report.py` from the exact jars pinned in `mods/*.pw.toml`.')
P('All numbers are static (what the jar *contains*), not runtime cost. They tell us where cost *can* come from.')
P()
P('- **frame mixins** = mixin classes whose `@Mixin` target is on a per-frame path (LevelRenderer, GameRenderer, entity/item/block-entity renderers, Gui, ParticleEngine, blaze3d, Sodium, Iris, Camera).')
P('- **risky** = `@Overwrite`/`@Redirect` injectors inside those frame mixins (replace vanilla logic; most likely to conflict with Sodium/Iris/ModernFix).')
P('- **frame events** = NeoForge/Architectury/Fabric per-frame event types referenced by the mod’s classes.')
P('- **atlas sprites** = block+item textures in 16×16-sprite equivalents (what lands in `blocks.png-atlas`, mipmapped, in VRAM).')
P()
tot_atlas = sum(r['atlas'] for r in cl)
tot = lambda k: sum(r[k] for r in cl)
P('## Pack totals (client-loaded mods)')
P()
P('| metric | value |')
P('| --- | --- |')
P(f'| client-loaded mods | {len(cl)} (server-only: {len(rows) - len(cl)}) |')
P(f'| mixin classes (client-loaded) | {tot("mix")} |')
P(f'| frame-path mixin classes | {tot("frame")} |')
P(f'| frame-path @Overwrite/@Redirect | {tot("risky")} |')
P(f'| mods hooking ≥1 per-frame event | {sum(1 for r in cl if r["fev"])} |')
P(f'| mods hooking client/player/entity tick events | {sum(1 for r in cl if r["tev"])} |')
P(f'| block+item textures | {tot("atlas_n")} files, {tot_atlas / 1e6:.1f} Mpx ≈ {tot_atlas // SP} 16×16 sprites |')
side = 2 ** math.ceil(math.log2(math.sqrt(tot_atlas))) if tot_atlas else 0
P(f'| implied blocks atlas (lower bound, + vanilla) | ≥ {side}×{side if side * side // 2 < tot_atlas else side // 2} ; ×4 B ×1.33 mip ≈ {side * side * 4 * 1.33 / 2**20:.0f} MiB VRAM at the square size |')
P(f'| animated block/item textures | {tot("anim")} |')
P(f'| entity textures | {tot("ent_px") / 1e6:.1f} Mpx |')
P(f'| models / blockstates | {tot("models")} / {tot("bstates")} |')
P(f'| entity types (lang proxy) | {tot("ents")} |')
P(f'| blocks / items (lang proxy) | {tot("blocks")} / {tot("items")} |')
P(f'| geckolib geo+animation files | {tot("geo")} |')
P(f'| vanilla core-shader overrides | {", ".join(r["slug"] for r in cl if r["core"]) or "none"} |')
P(f'| post-effect files | {", ".join(r["slug"] + f"({r['post']})" for r in cl if r["post"]) or "none"} |')
P()
def table(title, key, cols, n=25, filt=lambda r: True, note=''):
rs = sorted((r for r in cl if filt(r) and r[key]), key=lambda r: -r[key])[:n]
P(f'## {title}')
P()
if note:
P(note)
P()
P('| mod | side | ' + ' | '.join(c[0] for c in cols) + ' |')
P('| --- | --- | ' + ' | '.join('---:' if c[2] else '---' for c in cols) + ' |')
for r in rs:
P(f'| {r["slug"]} | {r["side"]} | ' + ' | '.join(c[1](r) for c in cols) + ' |')
P()
table('Per-frame mixin surface', 'frame', [
('frame mixins', lambda r: str(r['frame']), 1),
('risky', lambda r: str(r['risky']), 1),
('all mixins', lambda r: str(r['mix']), 1),
('hottest targets', lambda r: r['top'], 0)], n=40)
P('## Per-frame event subscribers')
P()
P('| mod | side | frame events | tick events |')
P('| --- | --- | --- | --- |')
for r in sorted((r for r in cl if r['fev']), key=lambda r: (-len(r['fev']), r['slug'])):
P(f'| {r["slug"]} | {r["side"]} | {", ".join(r["fev"])} | {", ".join(r["tev"])} |')
P()
table('Block/item atlas pressure', 'atlas', [
('sprites (16² eq.)', lambda r: f'{r["atlas"] // SP:,}', 1),
('files', lambda r: str(r['atlas_n']), 1),
('animated', lambda r: str(r['anim']), 1),
('% of pack', lambda r: f'{100 * r["atlas"] / tot_atlas:.1f}', 1),
('models', lambda r: str(r['models']), 1),
('blockstates', lambda r: str(r['bstates']), 1)], n=30)
table('Animated atlas textures (uploaded every tick unless culled by Sodium)', 'anim', [
('animated', lambda r: str(r['anim']), 1)], n=20)
table('Entity content (renderer + animation load)', 'ents', [
('entity types', lambda r: str(r['ents']), 1),
('entity tex Mpx', lambda r: f'{r["ent_px"] / 1e6:.2f}', 1),
('geckolib files', lambda r: str(r['geo']), 1)], n=30)
table('Shader assets', 'shaders', [
('shader files', lambda r: str(r['shaders']), 1),
('post effects', lambda r: str(r['post']), 1),
('core overrides', lambda r: str(r['core']), 1)], n=30)
table('Sound payload', 'ogg_mb', [('ogg MB', lambda r: f'{r["ogg_mb"]:.1f}', 1)], n=15)
table('Jar size', 'jar_mb', [('MB', lambda r: f'{r["jar_mb"]:.1f}', 1)], n=20)
P('## Resource packs / shader packs')
P()
for slug, r in d.items():
if r['dir'] == 'mods':
continue
t, a = r['tex'], r['assets']
P(f'- **{slug}** ({r["side"]}): atlas sprites {t.get("atlas_px", 0) // SP:,}, entity tex {t.get("entity_px", 0) / 1e6:.2f} Mpx, '
f'models {a.get("models", 0)}, blockstates {a.get("blockstates", 0)}, CEM/EMF files {a.get("cem_files", 0)}, shader files {a.get("shader_files", 0)}')
P()