Repository navigation
Oculus
Minecraft 1.20.1 / Forge: this page documents that build and its measurements. For the separate 170-control Minecraft 1.21.1 port, see Minecraft 1.21.1 NeoForge.
Twelve client-side controls for Oculus 1.8.0 (the Forge port of Iris). They remove repeated work inside the shader pipeline: vertex conversion, uniform and sampler bookkeeping, shadow-frustum construction and the batched-entity-rendering order manager. None of them changes what a shader receives or draws; every one was verified against real GL state or bit-for-bit output comparisons.
| Key | Patched | Since | Result |
|---|---|---|---|
oculus_entity_vertex_reuse |
ModelToEntityVertexSerializer.serialize |
1.0.4 | −31…34 % per quad batch |
oculus_program_traversal |
ProgramUniforms, ProgramSamplers
|
1.0.4 | −7…9 % per program update |
oculus_shadow_edge_vectors |
AdvancedShadowCullingFrustum.addEdgePlane |
1.0.6 | cold-path allocation only |
oculus_empty_sampler_fast_return |
IrisRenderSystem.unbindAllSamplers |
1.0.8 | 35.04 → 1.39 ns when empty |
oculus_primitive_uniform_locations |
CustomUniforms.push |
1.0.12 | −36.8 % with 24 uniforms |
oculus_empty_transparency_graphs |
GraphTranslucencyRenderOrderManager.getRenderOrder |
1.0.12 | −79.9 % with empty graphs |
oculus_primitive_buffer_affinities |
FullyBufferedMultiBufferSource affinities |
1.0.14 | −51…55 % per lookup |
oculus_reuse_empty_graphs |
GraphTranslucencyRenderOrderManager.reset |
1.0.14 | 166.37 → 16.38 ns, 808 → 0 B |
oculus_phase_labels |
IrisRenderingPipeline.setPhase |
1.0.21 | 32 → 1.4 ns per phase change |
oculus_dh_instance_lookup |
LodRendererEvents.getInstance |
1.0.23 | no Optional chain per DH render event |
oculus_shadow_search_replay |
MixinAdvancedShadowCullingFrustum, MixinBoxCullingFrustum |
1.0.30 | shadow pass 60-68 % less time per still-view search |
oculus_shadow_outline_discard |
ShadowRenderer, MixinRenderBuffers |
1.0.30 | outline buffer stays at its first-pass size (2 MB → 4 MB after 60 passes before) |
All are CLIENT only; a dedicated server never loads these classes. Every switch fingerprints the Oculus 1.8.0 methods it touches; another Oculus build is left untouched with one WARN.
Since: 1.0.4 · Script (1.0.19 and earlier): furious4-ModelToEntityVertexSerializer.js
Patched: net.irisshaders.iris.compat.sodium.impl.vertex_format.ModelToEntityVertexSerializer.serialize
Mixin (since 1.0.20): EntityVertexSerializerMixin
What upstream did. When Embeddium's model vertices are converted to Oculus' entity vertex format, the serializer re-read the UVs, the shader ids and the runtime stride for every vertex of a quad, and copied each vertex through a generic memCopy.
What the patch does. Reads those values once per quad and reuses them for its four vertices, and copies the fixed 36-byte vertex directly. The original memCopy is kept for overlapping ranges.
What stays the same. Float addition order, tangent math, the 56-byte runtime stride, every attribute and every padding byte. Random raw inputs, partial counts and overlapping buffers produced equal output.
Measured. vertex-4 60.30 → 41.76 ns (−30.7 %), vertex-64 969.72 → 648.79 ns (−33.1 %), vertex-256 3,920.09 → 2,599.90 ns (−33.7 %), with zero allocation both ways.
Since 1.0.30. Works together with Accelerated Rendering, which moves three of these offsets: its change now applies on top of this patch. Before, the two crashed the game when entities were drawn with shaders on.
Since: 1.0.4 · Scripts (1.0.19 and earlier): furious4-ProgramUniforms.js, furious4-ProgramSamplers.js
Patched: net.irisshaders.iris.gl.program.ProgramUniforms.updateStage / removeListeners and ProgramSamplers.update / removeListeners
Mixins (since 1.0.20): ProgramSamplersTraversalMixin, ProgramUniformsTraversalMixin
What upstream did. On every shader program change, the uniform and sampler updates iterated Guava ImmutableLists through temporary iterators.
What the patch does. Indexes the same immutable lists in order. Mutable initializer lists keep their iterator traversal, because their contract differs.
What stays the same. Every uniform update, listener reset, timing guard, active-program assignment and texture-state restoration.
Measured. uniforms-0 3.69 → 3.45 ns, uniforms-1 7.39 → 6.52 ns (24 → 0 B), uniforms-16 75.72 → 69.19 ns (−8.6 %), uniforms-64 312.24 → 288.62 ns (−7.6 %). A small, per-program-switch saving.
Since: 1.0.6 · Script (1.0.19 and earlier): furious6-AdvancedShadowCullingFrustum.js
Patched: net.irisshaders.iris.shadows.frustum.advanced.AdvancedShadowCullingFrustum.addEdgePlane
Mixin (since 1.0.20): ShadowEdgePlaneMixin
What upstream did. Building the advanced shadow frustum allocated two Vector3f cross products per edge plane.
What the patch does. Reuses two private truncated vectors after their original values are no longer needed.
What stays the same. The JOML operations, getter order, plane insertion, every culling decision, and the Distant Horizons and Embeddium adapters that hook this class.
Measured. Interpreted edge probe 323.20 → 297.13 ns and 176 → 128 B; the warmed complete constructor was 222.50 → 218.29 ns with equal allocation (1,456 B). The benefit is limited to the path before JIT compilation; it is kept because it is exact and free once warm.
Since: 1.0.8 · Script (1.0.19 and earlier): furious8-samplers.js
Patched: net.irisshaders.iris.gl.IrisRenderSystem.initRenderer, bindSamplerToUnit, unbindAllSamplers
Mixin (since 1.0.20): SamplerCleanupMixin
What upstream did. unbindAllSamplers scanned every texture unit's cached sampler even when the cache was already empty, which is the common case between passes.
What the patch does. A private, conservative flag records that a non-zero sampler may be bound: it is set on a successful cache write, and cleared on renderer initialisation and after a complete cleanup. When the flag is clear a small guard returns immediately; otherwise it calls an exact copy of the original cleanup body. A failed cleanup keeps the flag conservative (set), so nothing can be skipped wrongly.
What stays the same. Every OpenGL call, cache write and error behaviour of the occupied case, and the binding cache's treatment of external GL calls.
Measured. Empty sampler cleanup 35.04 → 1.39 ns wall (thread CPU 34.72 → 2.48 ns); the occupied path (bind one sampler and clear) is unchanged at 353.15 → 355.20 ns. Verified with 3,000 sampler patterns using actual OpenGL binding queries, repeated clears, invalid indices and renderer re-initialisation.
Since: 1.0.12 · Script (1.0.19 and earlier): furious10-uniforms.js
Patched: net.irisshaders.iris.uniforms.custom.CustomUniforms.push
Mixin (since 1.0.20): CustomUniformsPushMixin
What upstream did. Custom-uniform upload invoked Object2IntMap.forEach(BiConsumer). With the fastutil 8.5.9 that Minecraft bundles, that adapts each primitive location through an Integer box and a callback adapter for every uniform, every push.
What the patch does. Uses Object2IntMaps.fastForEach and Entry.getIntValue() to feed the same CachedUniform.pushIfChanged(int) call. For Oculus' own Object2IntOpenHashMap this is the same fast-entry visitation in the same order; unknown map implementations keep the original call. This is separate from oculus_program_traversal, which covers the built-in uniform lists.
What stays the same. Uniform order, locations, dirty flags and values; real shader uniform values matched in the native fixture, including null keys, collisions, arbitrary integer values and callback failure order.
Measured. uniforms-0 28.30 → 21.92 ns (−22.6 %, 48 → 24 B); uniforms-24 193.05 → 122.00 ns (−36.8 %, 408 → 0 B).
Since: 1.0.12 · Script (1.0.19 and earlier): furious10-graphs.js
Patched: net.irisshaders.batchedentityrendering.impl.ordering.GraphTranslucencyRenderOrderManager.getRenderOrder
Mixin (since 1.0.20): TransparencyGraphOrderMixin
What upstream did. The batched-entity-rendering order manager keeps one dependency graph per transparency category (six) and topologically sorts each one to decide draw order. It processed all six even when a graph had no vertices, allocating the feedback sets, sort lists and result appends for nothing.
What the patch does. Skips the feedback-set / topological-sort / append block only for zero-vertex graphs. Populated graphs, including cyclic ones, run the unchanged algorithm with cycle removal, in the same category order, producing the same fresh mutable result list.
Measured. graphs-0 (all six empty) 279.94 → 56.27 ns (−79.9 %, 1,184 → 32 B); graphs-6 (all populated) 5,512.18 → 5,142.42 ns (−6.7 %, 20,776 → 20,008 B). Populated-graph timing varied between runs; the reliable saving is the empty work. Tested with Oculus' actual bundled Ithaka graph classes.
Since: 1.0.14 · Script (1.0.19 and earlier): furious14-affinities.js
Patched: net.irisshaders.batchedentityrendering.impl.FullyBufferedMultiBufferSource (the affinities map and its getBuffer path)
Mixin (since 1.0.20): BufferAffinitiesMixin
What upstream did. Oculus assigns render types to 32 existing segmented builders through an access-ordered LinkedHashMap<RenderType, Integer>. The map is cleared after preparation and repopulated every frame, allocating linked entries and boxed integers again; full-map eviction also called remove after an iterator had already removed the entry.
What the patch does. Replaces that private bookkeeping map with fastutil's Object2IntLinkedOpenHashMap: getAndMoveToLast for access-ordered hits, a missing-value sentinel outside 0–31, and removeFirstInt for eviction. It preserves object equality (not identity), access order, builder-number assignment, the 32-entry bound and the existing clear point; the actual builders are untouched. Entity Texture Features injects into the surrounding getBuffer; those input and return hooks remain intact and the production method with them passes separate output tests.
Measured (normalised per lookup; raw rows measured batches of 16 or 64): 16 active keys 53.20 → 23.99 ns (−54.9 %); 64-key churn 262.69 → 128.13 ns (−51.2 %, 52 → 0 B). The research model compared 100,000 mixed hits, misses, evictions and clears including null keys, equal-but-distinct keys and deliberate hash collisions; the native fixture verified LRU equality and order.
Since: 1.0.14 · Script (1.0.19 and earlier): a coordinated extension inside furious10-graphs.js (the file checks this switch separately from oculus_empty_transparency_graphs; all four on/off combinations were tested)
Patched: GraphTranslucencyRenderOrderManager.reset
Mixin (since 1.0.20): TransparencyGraphResetMixin
What upstream did. reset() discarded all six category graphs and constructed six replacements every time, even when none contained a vertex, and cloned TransparencyType.values() on each reset.
What the patch does. Keeps an existing graph when it is empty and constructs a fresh one for every non-empty category, iterating a private fixed enumeration of the six categories. Occupied categories therefore still get fresh graphs; no enlarged graph from a past scene is retained.
What stays the same. begin, group boundaries, edge weights, ordering and currentTypes behaviour; cyclic graphs, repeated resets and subsequent reuse were tested with the bundled graph implementation.
Measured. All-empty graph reset 166.37 → 16.38 ns (−90.2 %), 808 → 0 B.
Since: 1.0.21 · Mixin: PhaseLabelMixin · Helper: bons.furious.patch.oculus.PhaseLabels
Patched: net.irisshaders.iris.pipeline.IrisRenderingPipeline.setPhase
What upstream did. setPhase runs whenever the render state changes phase (entities, block entities, particles, terrain layers, sky: hundreds of times a frame) and rebuilt the phase's GL debug-group label each time with toLowerCase, replace and capitalize.
What the patch does. Builds the 24 labels once, with the same expression, and issues the same popGroup / pushGroup calls in the same order. The method body is otherwise Oculus' own (LGPL-3.0).
What stays the same. All 24 labels (checked in the 1.0.21 client run against the original expression) and the debug-group calls.
Measured. 32 → 1.4 ns per phase change; 0.14 % / 0.06 % of the render thread in two client profiles.
Since: 1.0.23 · Mixin: DhInstanceLookupMixin
Patched: net.irisshaders.iris.compat.dh.LodRendererEvents.getInstance (with PipelineManager.getPipeline / getPipelineNullable and DHCompat.getInstance fingerprinted)
What upstream did. Every Distant Horizons render event Oculus handles looked up its shader compatibility object through a chain of two or three Optional objects.
What the patch does. The same lookup with plain null checks, returning the same object in every case. The method body is otherwise Oculus' own (LGPL-3.0).
What stays the same. The instance: in game it matched the original lookup with shaders on, off and reloaded.
Measured. No more Optionals on that path, which was 3.3 MB/s of render-thread allocation in review 8.
Since: 1.0.30 · Target: Oculus 1.8.0 (Minecraft 1.20.1) · Side: CLIENT · Kind: opt
Mixin: ShadowFrustumMarkerMixin
The section search replay also serves Oculus's shadow pass. Lets embeddium_search_replay replay the shadow pass's search too. Oculus's shadow frusta answer the visibility test from their own numbers only, which this switch's fingerprints confirm for Oculus 1.8.0, and the replay keys those numbers bit for bit. The shadow frustum follows the sun, which moves at most once per game tick, so with a still view the shadow search replays between ticks (two to four of every five frames at 60-100 FPS). Nothing in Oculus changes; with another Oculus build the shadow pass searches as before.
Idea: Iris's draft to skip the shadow-pass search, idea text only
Measured: in the embeddium_search_replay harness with all six Oculus shadow frusta, 0 mismatches; shadow pass 60-68% less time per still-view search (hot 65 -> 21 us, cold 193 -> 78 us)
Since: 1.0.30 · Target: Oculus 1.8.0 (with or without ImmediatelyFast 1.5.5) · Side: CLIENT · Kind: fix
Mixins: ShadowOutlineDiscardMixin, OutlineSourceAccessor, BufferSourceAccessor
Shader shadow pass outline discard. Oculus renders the shadow pass with its own render buffers, and nothing ever ends their outline batch. Armor renderers that pick the outline buffer themselves (GeckoLib 4.8.4 and AzureLib armor: Ars Nouveau, Mowzie's Mobs, Fantasy Armor, Goblin's Tyranny and more) write a glowing wearer's outline into it on every shadow pass, so it grows without limit (with ImmediatelyFast) or is later drawn into the shadow buffers (without it). The switch ends that pending outline batch at the end of each shadow pass without drawing it: what ending the batch does, minus the draw. The main pass, which draws the real glow outline from its own buffers, is untouched.
Idea: ImmediatelyFast 1.6.9 / 1.8.5 (shadow pass outline buffer clearing), idea text only
Measured: reproduced offline (600 outline quads per pass: 144,000 vertices and 2 MB -> 4 MB after 60 passes, on both the vanilla and the ImmediatelyFast buffer source); with the switch the buffer stays at its first-pass size and the next pass writes the same bytes as a fresh buffer (925 checks, 0 failures)
Bons and Furious is licensed GPL-3.0-only with upstream notices retained. Target mod names identify compatibility targets and do not imply endorsement; their code, assets and licenses remain their authors' property. All measurements on this wiki are focused method, phase or reproduction measurements from the pack the mod was built for, taken on the historical build named in each entry; they are not whole-game FPS or TPS promises and are not additive.
Bons and Furious
Minecraft 1.20.1 / Forge 1.0.34
- Home
- Installation and configuration
- How the patches are applied
- All controls
- Measurements and caveats
- Whole-modpack benchmark
- Troubleshooting and log messages
- Generation context fixes
- Version history
Compatibility
- Compatibility
- Known conflicts and workarounds
- Tested mod list
- Redundant mods
- Compatibility and target versions
Controls by mod
- Minecraft (51)
- Forge (6)
- Ad Astra (1)
- Alex's Caves (4)
- Alex's Mobs (2)
- Almost Unified (1)
- AmbientSounds (3)
- Architectury API (1)
- Ars Nouveau (2)
- Artifacts (1)
- Better Combat (1)
- Bosses of Mass Destruction (1)
- Butterflies (1)
- ChunkSending (1)
- CIT Reforged (1)
- Citadel (1)
- CoFH Core (1)
- Colorwheel (1)
- Cooking for Blockheads (1)
- Create (4)
- Critters and Companions (1)
- Cryptic Foes (2)
- Cucumber (1)
- Curios API (6)
- Distant Horizons (17)
- Dungeons Delight (1)
- Dynamic Trees (2)
- Embeddium (13)
- Entity Model Features (4)
- Entity Texture Features (1)
- EntityCulling (1)
- FancyMenu (1)
- Farmer's Delight (2)
- FD Bosses (2)
- Fowl Play (2)
- Fusion (2)
- GeckoLib (5)
- Goblins Tyranny (2)
- Hostile Villages (2)
- Ice and Fire (6)
- ImmediatelyFast (3)
- Immersive Engineering (1)
- Jaden's Nether Expansion (1)
- JEI (7)
- Kiwi (1)
- L2 Library (2)
- Legendary Monsters (1)
- Lionfish API (2)
- ModernFix (4)
- Mowzie's Mobs (1)
- Mutant Monsters (1)
- Nether Depths Upgrade (1)
- Occult (1)
- Oculus (12)
- Oh The Biomes We've Gone (1)
- Particular (1)
- Pehkui (1)
- Pipez (2)
- Placebo (1)
- Presence Footsteps (1)
- Radium Re-Reforged (8)
- Regions Unexplored (1)
- Relics (2)
- Ribbits (1)
- Ryoamic Lights (2)
- Sake's Structures (1)
- Scorched (1)
- Scuba Gear (1)
- Simply Swords (1)
- SlashBlade: Resharped (1)
- Slice & Dice (1)
- Spawn (4)
- Storage Drawers (2)
- Structure Gel API (1)
- Structurify (3)
- TaCZ (2)
- TerraBlender (2)
- Terramity (1)
- Under the Moon (1)
- Valkyrien Skies (15)
- Trackwork model repair
- Living Engineering (moved out in 1.0.20)
Links