Files
onion-mc/analysis/client-perf/research/r6-jvm-platform.md
T
logan 611a1a5952 docs: client-perf analysis, bench harness and work journal
Everything the next agent needs to run the Phase 2 optimisation loop on a
machine that can launch the client:

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

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

151 lines
29 KiB
Markdown
Raw Blame History

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