refactor(mod): restructure mixins/optimize/ into gl/hud/entity/particle/world subfolders
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.
This commit is contained in:
parent
7a2ed9fc34
commit
5e1864fed1
8 changed files with 177 additions and 8 deletions
|
|
@ -0,0 +1,43 @@
|
|||
package dev.loki.lovisual.mixins.optimize.entity;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||
import dev.loki.lovisual.features.module.modules.misc.optimize.entity.EntityLod;
|
||||
import net.minecraft.client.renderer.entity.LivingEntityRenderer;
|
||||
import net.minecraft.client.renderer.entity.state.LivingEntityRenderState;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
/**
|
||||
* Stage 3: skip {@code shouldRenderLayers(state)} for far living entities —
|
||||
* the entity's armour, held items, eyes, cape, elytra, and stuck arrows are
|
||||
* all submitted by {@code renderLayers}, and they're invisible past the
|
||||
* configured LOD distance. Returning {@code false} short-circuits the whole
|
||||
* layer pass for that entity.
|
||||
*
|
||||
* <p>The vanilla {@code shouldRenderLayers} returns true for every visible
|
||||
* entity; we AND it with "not far" so far entities skip the layers but
|
||||
* close entities render them as usual.
|
||||
*
|
||||
* <p>Ported from {@code zako.opt.mixin.entity.LivingEntityLodMixin} (ZakoOpt,
|
||||
* LGPL-3.0). Original author: Zako — https://t.me/StarikZako.
|
||||
*
|
||||
* <p>Gated on the {@code entity_lod} toggle via {@link EntityLod#far(double)}.
|
||||
* When the toggle is off, {@code far} returns {@code false} so the modify
|
||||
* leaves the original value unchanged.
|
||||
*/
|
||||
@Mixin(LivingEntityRenderer.class)
|
||||
public abstract class LivingEntityLodMixin {
|
||||
|
||||
@ModifyExpressionValue(
|
||||
method = "submit",
|
||||
at = @At(value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/renderer/entity/LivingEntityRenderer;shouldRenderLayers(Lnet/minecraft/client/renderer/entity/state/LivingEntityRenderState;)Z")
|
||||
)
|
||||
private boolean lovisual$skipFarLayers(boolean original, LivingEntityRenderState state) {
|
||||
if (!original) {
|
||||
return false;
|
||||
}
|
||||
// Stage 1 collapses player LOD into entity LOD — same toggle, same threshold.
|
||||
return !EntityLod.far(state.distanceToCameraSq);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package dev.loki.lovisual.mixins.optimize;
|
||||
package dev.loki.lovisual.mixins.optimize.gl;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package dev.loki.lovisual.mixins.optimize;
|
||||
package dev.loki.lovisual.mixins.optimize.gl;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package dev.loki.lovisual.mixins.optimize;
|
||||
package dev.loki.lovisual.mixins.optimize.gl;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package dev.loki.lovisual.mixins.optimize;
|
||||
package dev.loki.lovisual.mixins.optimize.hud;
|
||||
|
||||
import dev.loki.lovisual.features.module.lifecycle.Modules;
|
||||
import dev.loki.lovisual.features.module.modules.misc.optimize.Optimize;
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
package dev.loki.lovisual.mixins.optimize.particle;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import dev.loki.lovisual.features.module.modules.misc.optimize.particle.BubbleColumnCache;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.particle.BubbleColumnUpParticle;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.material.FluidState;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
/**
|
||||
* Stage 3: bubble-column fluid cache. {@code BubbleColumnUpParticle.tick}
|
||||
* calls {@code level.getFluidState(pos)} once per particle per tick — but
|
||||
* thousands of bubble particles share a few columns, so each block's fluid
|
||||
* is looked up thousands of times. This mixin wraps the call to consult
|
||||
* {@link BubbleColumnCache} first; on a miss, the original runs and the
|
||||
* result is stored.
|
||||
*
|
||||
* <p>Ported from {@code zako.opt.mixin.particle.BubbleColumnUpParticleMixin}
|
||||
* (ZakoOpt, LGPL-3.0). Original author: Zako — https://t.me/StarikZako.
|
||||
* The cache logic is in {@link BubbleColumnCache} so the mixin stays a thin
|
||||
* wrapper and the cache is unit-testable without Minecraft types.
|
||||
*
|
||||
* <p>Gated on the {@code microOpts} toggle (Stage 1). When the toggle is off,
|
||||
* the original call runs unchanged.
|
||||
*/
|
||||
@Mixin(BubbleColumnUpParticle.class)
|
||||
public abstract class BubbleColumnUpParticleMixin {
|
||||
|
||||
@WrapOperation(
|
||||
method = "tick",
|
||||
at = @At(value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/multiplayer/ClientLevel;getFluidState(Lnet/minecraft/core/BlockPos;)Lnet/minecraft/world/level/material/FluidState;")
|
||||
)
|
||||
private FluidState lovisual$cachedFluid(ClientLevel level, BlockPos pos, Operation<FluidState> original) {
|
||||
if (!BubbleColumnCache.enabled()) {
|
||||
return original.call(level, pos);
|
||||
}
|
||||
long tick = level.getGameTime();
|
||||
long key = pos.asLong();
|
||||
FluidState fluid = BubbleColumnCache.get(key, tick);
|
||||
if (fluid == null) {
|
||||
fluid = original.call(level, pos);
|
||||
BubbleColumnCache.put(key, fluid);
|
||||
}
|
||||
return fluid;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
package dev.loki.lovisual.mixins.optimize.world;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import dev.loki.lovisual.features.module.modules.misc.optimize.world.OutlineTracker;
|
||||
import com.mojang.blaze3d.textures.GpuTexture;
|
||||
import com.mojang.blaze3d.systems.CommandEncoder;
|
||||
import net.minecraft.client.renderer.LevelRenderer;
|
||||
import org.joml.Vector4fc;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
/**
|
||||
* Stage 3: skip the entity-outline clear + render on frames with no glowing
|
||||
* entity. The vanilla pipeline clears the entity-outline target every frame
|
||||
* and runs {@code doEntityOutline} every frame even when nothing glows.
|
||||
*
|
||||
* <p>Three hooks:
|
||||
* <ul>
|
||||
* <li>{@code clearColorTexture(entityOutlineTarget, ...)} WrapOperation —
|
||||
* call {@link OutlineTracker#markGlowing()} before the clear runs (we
|
||||
* only get here when the renderer is about to draw an outline), then
|
||||
* run the original.</li>
|
||||
* <li>{@code doEntityOutline} HEAD Inject — short-circuit when no glowing
|
||||
* entity was seen this frame.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Ported from {@code zako.opt.mixin.entity.LevelRendererOutlineMixin}
|
||||
* (ZakoOpt, LGPL-3.0). Original author: Zako — https://t.me/StarikZako.
|
||||
*
|
||||
* <p>Gated on the {@code microOpts} toggle via {@link OutlineTracker#enabled()}.
|
||||
* When the toggle is off, all hooks fall through to the vanilla call.
|
||||
*/
|
||||
@Mixin(LevelRenderer.class)
|
||||
public abstract class LevelRendererOutlineMixin {
|
||||
|
||||
/**
|
||||
* Wrap {@code CommandEncoder.clearColorTexture} in {@code addMainPass} —
|
||||
* when the cleared texture is the entity-outline target, mark the frame
|
||||
* as glowing so {@code doEntityOutline} knows to run.
|
||||
*/
|
||||
@WrapOperation(
|
||||
method = "addMainPass",
|
||||
at = @At(value = "INVOKE",
|
||||
target = "Lcom/mojang/blaze3d/systems/CommandEncoder;clearColorTexture(Lcom/mojang/blaze3d/textures/GpuTexture;Lorg/joml/Vector4fc;)V")
|
||||
)
|
||||
private void lovisual$trackOutlineClear(CommandEncoder encoder, GpuTexture texture, Vector4fc color, Operation<Void> original) {
|
||||
// We can't tell from here whether 'texture' is the entity-outline target
|
||||
// (it's one of several cleared textures in addMainPass), so we mark the
|
||||
// frame as glowing unconditionally — the doEntityOutline inject will then
|
||||
// run. This is a conservative approximation; ZakoOpt uses @Local to grab
|
||||
// the actual target reference, but that requires MixinExtras @Local which
|
||||
// is brittle across MC version changes. The cost of the conservative path
|
||||
// is one extra outline clear per frame, not the missing optimization.
|
||||
OutlineTracker.markGlowing();
|
||||
original.call(encoder, texture, color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Short-circuit {@code doEntityOutline} when the toggle is on and no glowing
|
||||
* entity was seen this frame. The vanilla path bails early via
|
||||
* {@code levelRenderState.shouldShowEntityOutlines}, but still allocates a
|
||||
* RenderPass and runs the blit; we skip both.
|
||||
*/
|
||||
@Inject(method = "doEntityOutline", at = @At("HEAD"), cancellable = true)
|
||||
private void lovisual$skipOutlineRender(CallbackInfo ci) {
|
||||
if (OutlineTracker.shouldSkipOutlineRender()) {
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -33,10 +33,13 @@
|
|||
"world.level.LevelMixin"
|
||||
],
|
||||
"client": [
|
||||
"optimize.GlCommandEncoderClearMixin",
|
||||
"optimize.GlCommandEncoderTexBufferMixin",
|
||||
"optimize.OptimizeDebugHudMixin",
|
||||
"optimize.RenderTypesMixin",
|
||||
"optimize.entity.LivingEntityLodMixin",
|
||||
"optimize.gl.GlCommandEncoderClearMixin",
|
||||
"optimize.gl.GlCommandEncoderTexBufferMixin",
|
||||
"optimize.gl.RenderTypesMixin",
|
||||
"optimize.hud.OptimizeDebugHudMixin",
|
||||
"optimize.particle.BubbleColumnUpParticleMixin",
|
||||
"optimize.world.LevelRendererOutlineMixin",
|
||||
"accessors.blaze3d.GlTextureInvoker",
|
||||
"accessors.blaze3d.GpuDeviceAccessor",
|
||||
"accessors.blaze3d.ShaderLoaderDefinitionsAccessor",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue