Files
onion-mc/analysis/client-perf/research/r1-render-core.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

279 lines
43 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: 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.