Per mod/docs/optimize-layer-design.md Stage 3 (continued). Three more mixin'ов
that consume the Stage 3-continued-2 contract mixins (CachedLight, TickCached)
on actual renderers, plus the ParticleMixin that adds its own per-particle
light cache + far-particle physics skip.
mixins/optimize/particle/ParticleMixin.java:
- @Inject HEAD/RETURN on Particle.getLightCoords — caches the per-particle
light lookup by game tick (light only changes on tick boundaries). Gated
on the Stage 1 'particleLight' toggle.
- @Inject HEAD/RETURN on Particle.move — temporarily sets hasPhysics=false
for far particles (>16 blocks from camera) and three always-skip
subclasses (BaseAshSmokeParticle, CampfireSmokeParticle,
BubbleColumnUpParticle). Gated on the 'particlePhysics' toggle.
- Ported from zako.opt.mixin.particle.ParticleMixin.
features/.../optimize/particle/camera/CameraPosition.java (new helper):
- Single point where the camera position is sampled. ParticleMixin reads it
instead of re-walking Minecraft.getInstance().gameRenderer.mainCamera()
per particle per tick. Ported from zako.opt.particle.CameraPosition.
- Moved into particle/camera/ subfolder so optimize/particle/ stays under
the ≤4 .java-per-folder limit after adding ParticleMixin.
mixins/optimize/block/BlockEntityRenderStateMixin.java:
- WrapOperation on BlockEntityRenderState.extractBase's
LightCoordsUtil.getLightCoords — consults the per-tick light cache slot
on the BlockEntity (via the CachedLight mixin from Stage 3-continued-2)
before falling through to the original. Gated on 'blockEntityCache'.
- Ported from zako.opt.mixin.block.BlockEntityRenderStateMixin.
mixins/optimize/block/ChestRendererMixin.java:
- Two WrapOperations on ChestRenderer.extractRenderState:
* ChestBlock.combine — caches the double-chest neighbor-lookup result by
game tick (via the TickCached mixin). Gated on 'blockEntityCache'.
* Int2IntFunction.applyAsInt — caches the second (pair) light lookup
done for double chests (via the CachedLight.lovisual$pairLight slot).
Gated on 'microOpts'.
- Ported from zako.opt.mixin.block.ChestRendererMixin.
lovisual.mixins.json: three new entries in the 'client' array:
optimize.block.BlockEntityRenderStateMixin
optimize.block.ChestRendererMixin
optimize.particle.ParticleMixin
Tests: no new tests in this batch (mixins are pure side-effect field-bags /
WrapOperation wrappers without unit-testable logic). Existing suite still green.
Total: 866 tests, 0 failures. checkFolderLimit: OK (0 папок >4).
Per mod/docs/optimize-layer-design.md Stage 3 (continued). This batch
implements the Stage 1 mixin-contract interfaces on their target MC classes
so CachedName, StateEntity, TickCached, CachedLight, CachedValidity and
CachedStack actually get used. Seven new mixin'ов; no new helpers (the
helpers were all written in Stage 1 already).
Entity-contract mixins (mixins/optimize/entity/):
- EntityNameMixin: implements CachedName on Entity — adds the per-tick
cached name-tag fields (Component + tick). Vanilla Entity.getDisplayName()
rebuilds the team prefix + name + suffix on every call.
- EntityRenderStateEntityMixin: implements StateEntity on EntityRenderState —
adds the back-reference field to the source Entity. Render-states are
rebuilt every frame so they can't keep their own pointer; this is captured
by EntityRendererStateMixin.
- EntityRendererStateMixin: Inject at RETURN on EntityRenderer.createRenderState
— captures the source Entity on the freshly-built state via the StateEntity
contract. ZakoOpt used @ModifyReturnValue; we use plain @Inject because the
MixinExtras ModifyReturnValue transformer is more fragile across MC versions.
- EntityRendererNameMixin: WrapOperation on EntityRenderer.getNameTag's
Entity.getDisplayName call — caches by game tick so the per-frame cost is
one identity check + read instead of a full Component rebuild.
- SodiumEntityRendererMixin: WrapOperation on Sodium EntityRenderer.renderCuboid's
MemoryStack.stackPush — uses CachedStack (Stage 1) to skip the ThreadLocal
lookup on the render thread. Registered as optional via LoVisualMixinPlugin
(only applied when Sodium is loaded); remap = false because the target is
a Sodium class, not a Mojang one.
BlockEntity-contract mixins (mixins/optimize/block/):
- BlockEntityMixin: implements TickCached + CachedLight + CachedValidity on
BlockEntity — adds the per-tick cache slot, per-tick block-light lookups
(single + pair for double-chests), per-tick should-render distance check,
and per-state validity cache. All four are read by the (Stage 3, future)
ChestRendererMixin / BlockEntityRenderStateMixin / BlockEntityRenderDispatcherMixin.
- BaseSpawnerMixin: implements TickCached on BaseSpawner — adds the per-tick
cache slot that SpawnerCache (Stage 1) reads back.
Folder limit (≤4 .java per folder):
- mixins/optimize/entity/ hit 6 files after this batch — split into
mixins/optimize/entity/renderer/ for EntityRendererStateMixin +
EntityRendererNameMixin (the two that target EntityRenderer rather than
Entity/EntityRenderState directly). The other 4 stay in entity/.
lovisual.mixins.json: seven new entries in the 'client' array, alphabetical:
optimize.block.BaseSpawnerMixin
optimize.block.BlockEntityMixin
optimize.entity.EntityNameMixin
optimize.entity.EntityRenderStateEntityMixin
optimize.entity.SodiumEntityRendererMixin
optimize.entity.renderer.EntityRendererNameMixin
optimize.entity.renderer.EntityRendererStateMixin
LoVisualMixinPlugin: added SodiumEntityRendererMixin to OPTIONAL_SODIUM_MIXINS
so the mixin is only applied when Sodium is loaded (matches the existing
convention for sodium.* mixins).
Tests: no new tests in this batch (the new mixins are pure side-effect
field-bags — they don't have unit-testable logic). Existing suite still green.
Total: 866 tests, 0 failures. checkFolderLimit: OK (0 папок >4).
Per mod/docs/optimize-layer-design.md Stage 3 (continued). The mixins/optimize/
folder hit the project's ≤4 .java-files-per-folder rule when we tried to add
the next batch of mixin'ов (LivingEntityLodMixin, BubbleColumnUpParticleMixin,
LevelRendererOutlineMixin). Move the existing 4 mixin'ов into semantic subfolders:
- mixins/optimize/gl/GlCommandEncoderClearMixin.java (was mixins/optimize/)
- mixins/optimize/gl/GlCommandEncoderTexBufferMixin.java
- mixins/optimize/gl/RenderTypesMixin.java
- mixins/optimize/hud/OptimizeDebugHudMixin.java
lovisual.mixins.json: the 'client' array entries are updated to use the
new dotted paths (optimize.gl.*, optimize.hud.*). The package field in
each mixin file is updated to match.
No functional changes — this is purely a code-organization commit to make
room for the new Stage 3 mixin'ов in the next commit.
Per mod/docs/optimize-layer-design.md Stage 3 (partial). This commit ships
the simplest GL mixins and the strategy interface that splits GL/Vulkan
backends. SkinAtlas/AnimFreeze/HudCache/FrameFence deferred to a later pass
because they're more complex (vertex-format / glCopyImageSubData /
RenderPipeline+RenderPass).
RenderBackendOps interface (features/.../optimize/gl/RenderBackendOps.java):
- Backend-agnostic surface for lazy clear, TBO cache, RenderTypes cache.
- current() resolves the active backend — returns GlRenderBackendOps on GL,
null on Vulkan (so all WrapOperations fall through to the vanilla call).
- Stage 4 will add a VulkanRenderBackendOps no-op implementation.
GlRenderBackendOps (features/.../optimize/gl/GlRenderBackendOps.java):
- Concrete GL implementation. Three caches ported from ZakoOpt:
* Lazy clear (from zako.opt.mixin.gl.GlCommandEncoderClearMixin).
* TBO cache (from zako.opt.gl.TexBufferCache + the TexBufferMixin).
* RenderTypes cache (from zako.opt.mixin.gl.RenderTypesMixin).
- All three gated on OptimizeToggles.microOpts() (the Stage 1 'micro_opts'
toggle wired in Optimize.java).
- Includes test helpers (resetForTesting, boundTboTextureForTesting,
cachedRenderTypeCountForTesting).
Three new mixins (mixins/optimize/):
- RenderTypesMixin: WrapOperation on RenderTypes.entityTranslucent's
BiFunction.apply — cache by Identifier when flag=true (the common case).
Drops the per-call Objects.hash(Pair) hit.
- GlCommandEncoderClearMixin: WrapOperation on GlCommandEncoder's
clearColorAndDepthTextures → GlStateManager._glBindFramebuffer — skip the
bind-framebuffer-0 because the next render pass binds its own target
anyway and GlStateManager's cache already skips redundant binds.
- GlCommandEncoderTexBufferMixin: WrapOperation on GlCommandEncoder's
trySetup → glBindTexture (track bound) and glTexBuffer (skip re-attach
when the same (format, buffer) pair is already attached). Re-attaching
makes the driver revalidate the texture (clouds: ~2% of a frame).
lovisual.mixins.json:
- Register all three mixins + OptimizeDebugHudMixin in the 'client' array,
alphabetically under optimize.* (before accessors.*).
OptimizeDebugHudMixin (mixins/optimize/OptimizeDebugHudMixin.java):
- F3 overlay for the Optimize layer — Stage 2 of the design doc.
- Adds three blocks of lines to F3 (after the existing LoVisual Engine block):
* 'LoVisual Optimize' — toggles summary + budget mode + scale + frame time.
* 'frame:' — EMA-averaged per-section timings (entities/particles/HUD/effects/text).
* 'props:' — when any -Dlovisual.opt.* is set, list which ones.
- Mirrors the existing hud.DebugHudMixin pattern (ModifyArg on extractLines).
Tests (JUnit 5): 11 new tests in GlRenderBackendOpsTest — pure-logic pieces
of RenderBackendOps + Identifier equality/hash. Total suite: 849 tests,
0 failures. checkFolderLimit: OK (0 folders >4).
- GodRays: PostProcessPass PRE_HAND, godrays.frag (radial sky blur to the sun over the scene, depth from mainDepth),
GodRaysUniforms, GodRaysSkyMixin takes sunAngle/sunset colour from SkyRenderState; off under shaderpacks (Iris)
- Knife (Melee Skins): 33 skins from Bedrock geo/anim in assets/lovisual/knife, animator (static_idle, draw/inspect/melee/run,
0.2 s crossfade, Catmull-Rom) with background baking, first/third-person render, sounds, mixins ItemInHand/PlayerItemInHand/Minecraft
- Wings: wingSource Procedural/Model with 14 textured Bedrock models (WingModelDef/WingModels/WingModelRenderer, BedrockClip +
procedural angel/demon/dragon); shared Bedrock parser util/bedrock (GeoParser, GeoFaces, Channel, Clip)
- EnchantGlint: Pulse/Fire/Galaxy/Hologram/Glitch/Sparkle/Aurora styles in glint/style, coverage setting, custom animated
texture matrix via GlintTextureMixin.styleMotion; lang en/ru for all new settings
- Themes: shader backdrop for ClickGUI (Backdrop record, 6 modes, intensity/speed/scale, ui_theme_backdrop_batch.frag with
700 ms REVEAL/ERASE wipe, ThemeBackdrop2D in BackgroundComponent), ThemeEntry.backdrop persisted in JSON and profile codec,
6 presets with backdrops, backdrop settings in the theme editor
- tests: GodRaysSun, GlintStyles, Knife assets/animator, WingModel, Backdrop, bedrock Channel; ./gradlew build green
- AutoSprint, TotemCounter, EnchantGlint (styles, texture repaint, scroll speed/scale mixin), ArmorColor (durability colouring, targets, pulse),
PlaceAnimation (block slides in from the hand; chunk-mesh hiding incl. Sodium, moving-block render), aligned with evo behaviour
- CustomModels: Chicken, Rabbit and Verity (Blockbench) models for chosen players, bundled assets, render-state carrier + submit redirect
- KineticLyrics: fix track key wiped by reset() (lyrics never applied, lrclib hammered every tick); new LyricsFinder with ranked lrclib search,
unsynced timing estimate, NetEase fallback and retry on busy services; tests for the pure parts
- fix mode()/modeSetting() misuse for new modules; TODO updated