From 1794c81b6193b591465bff7c8db4acbf3c9e168a Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Sun, 15 Mar 2026 15:12:06 -0400 Subject: [PATCH] Optimize sequence rules that check many biome conditions in a row --- .../SequenceRuleSourceMixin.java | 19 ++++ .../world/gen/SurfaceRuleOptimizer.java | 100 ++++++++++++++++++ .../resources/META-INF/accesstransformer.cfg | 8 ++ 3 files changed, 127 insertions(+) create mode 100644 src/main/java/org/embeddedt/modernfix/common/mixin/perf/optimize_surface_rules/SequenceRuleSourceMixin.java create mode 100644 src/main/java/org/embeddedt/modernfix/world/gen/SurfaceRuleOptimizer.java diff --git a/src/main/java/org/embeddedt/modernfix/common/mixin/perf/optimize_surface_rules/SequenceRuleSourceMixin.java b/src/main/java/org/embeddedt/modernfix/common/mixin/perf/optimize_surface_rules/SequenceRuleSourceMixin.java new file mode 100644 index 00000000..a599aa7b --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/common/mixin/perf/optimize_surface_rules/SequenceRuleSourceMixin.java @@ -0,0 +1,19 @@ +package org.embeddedt.modernfix.common.mixin.perf.optimize_surface_rules; + +import net.minecraft.world.level.levelgen.SurfaceRules; +import org.embeddedt.modernfix.world.gen.SurfaceRuleOptimizer; +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.CallbackInfoReturnable; + +@Mixin(targets = {"net/minecraft/world/level/levelgen/SurfaceRules$SequenceRuleSource"}) +public class SequenceRuleSourceMixin { + @Inject(method = "apply(Lnet/minecraft/world/level/levelgen/SurfaceRules$Context;)Lnet/minecraft/world/level/levelgen/SurfaceRules$SurfaceRule;", at = @At("HEAD"), cancellable = true) + private void optimizeApply(SurfaceRules.Context context, CallbackInfoReturnable cir) { + var optimized = SurfaceRuleOptimizer.optimizeSequenceRule((SurfaceRules.SequenceRuleSource)(Object) this, context); + if (optimized != null) { + cir.setReturnValue(optimized); + } + } +} diff --git a/src/main/java/org/embeddedt/modernfix/world/gen/SurfaceRuleOptimizer.java b/src/main/java/org/embeddedt/modernfix/world/gen/SurfaceRuleOptimizer.java new file mode 100644 index 00000000..42492fa1 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/world/gen/SurfaceRuleOptimizer.java @@ -0,0 +1,100 @@ +package org.embeddedt.modernfix.world.gen; + +import it.unimi.dsi.fastutil.objects.Reference2ObjectMaps; +import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap; +import net.minecraft.core.Holder; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.levelgen.SurfaceRules; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +public class SurfaceRuleOptimizer { + public static @Nullable SurfaceRules.SurfaceRule optimizeSequenceRule(SurfaceRules.SequenceRuleSource source, SurfaceRules.Context context) { + // First pass: collect which biomes appear and count biome-gated branches + Reference2ObjectOpenHashMap, List> perBiomeSources = new Reference2ObjectOpenHashMap<>(); + int biomeGatedBranches = 0; + for (var innerSource : source.sequence()) { + if (innerSource instanceof SurfaceRules.TestRuleSource testRuleSource + && testRuleSource.ifTrue() instanceof SurfaceRules.BiomeConditionSource biomeConditionSource) { + biomeGatedBranches++; + for (var biome : biomeConditionSource.biomes) { + perBiomeSources.putIfAbsent(biome, new ArrayList<>()); + } + } + } + if (biomeGatedBranches < 3) { + return null; + } + // Second pass: build per-biome source lists preserving original interleaving order + List noMatchSources = new ArrayList<>(); + for (var innerSource : source.sequence()) { + if (innerSource instanceof SurfaceRules.TestRuleSource testRuleSource + && testRuleSource.ifTrue() instanceof SurfaceRules.BiomeConditionSource biomeConditionSource) { + // Add the inner rule (condition stripped) only to the matching biomes' lists + for (var biome : biomeConditionSource.biomes) { + perBiomeSources.get(biome).add(testRuleSource.thenRun()); + } + } else { + // Non-biome-gated rule: add to every biome list and the no-match list + for (var list : perBiomeSources.values()) { + list.add(innerSource); + } + noMatchSources.add(innerSource); + } + } + // Compile all source lists into rule lists + Reference2ObjectOpenHashMap, List> compiledBiomeMatch = new Reference2ObjectOpenHashMap<>(perBiomeSources.size()); + Reference2ObjectMaps.fastForEach(perBiomeSources, entry -> { + List compiled = new ArrayList<>(entry.getValue().size()); + for (var src : entry.getValue()) { + compiled.add(src.apply(context)); + } + compiledBiomeMatch.put(entry.getKey(), List.copyOf(compiled)); + }); + List compiledNoMatch = new ArrayList<>(noMatchSources.size()); + for (var src : noMatchSources) { + compiledNoMatch.add(src.apply(context)); + } + return new OptimizedBiomeLookupSequenceRule(compiledBiomeMatch, List.copyOf(compiledNoMatch), context); + } + + public record OptimizedBiomeLookupSequenceRule( + Map, List> rulesForBiomeMatch, + List rulesForNoBiomeMatch, + SurfaceRules.Context context + ) implements SurfaceRules.SurfaceRule { + @Override + public @Nullable BlockState tryApply(int x, int y, int z) { + var biome = context.biome.get(); + var key = (biome instanceof Holder.Reference ref) ? ref.key() : biome.unwrapKey().orElseThrow(); + var ruleList = rulesForBiomeMatch.getOrDefault(key, rulesForNoBiomeMatch); + //noinspection ForLoopReplaceableByForEach + for (int i = 0; i < ruleList.size(); i++) { + var rule = ruleList.get(i); + var state = rule.tryApply(x, y, z); + if (state != null) { + return state; + } + } + return null; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + OptimizedBiomeLookupSequenceRule that = (OptimizedBiomeLookupSequenceRule) o; + return rulesForBiomeMatch.equals(that.rulesForBiomeMatch) && rulesForNoBiomeMatch.equals(that.rulesForNoBiomeMatch); + } + + @Override + public int hashCode() { + return Objects.hash(rulesForBiomeMatch, rulesForNoBiomeMatch); + } + } +} diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index 13834361..c7980896 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -5,6 +5,14 @@ public net.minecraft.client.renderer.block.model.multipart.MultiPart f_111962_ public net.minecraft.client.resources.model.ModelBakery$ModelBakerImpl public net.minecraft.client.resources.model.ModelBakery$ModelBakerImpl (Lnet/minecraft/client/resources/model/ModelBakery;Ljava/util/function/BiFunction;Lnet/minecraft/resources/ResourceLocation;)V public net.minecraft.world.level.levelgen.SurfaceRules$SequenceRule +public net.minecraft.world.level.levelgen.SurfaceRules$SequenceRuleSource +public net.minecraft.world.level.levelgen.SurfaceRules$SequenceRuleSource (Ljava/util/List;)V +public net.minecraft.world.level.levelgen.SurfaceRules$TestRuleSource +public net.minecraft.world.level.levelgen.SurfaceRules$TestRuleSource (Lnet/minecraft/world/level/levelgen/SurfaceRules$ConditionSource;Lnet/minecraft/world/level/levelgen/SurfaceRules$RuleSource;)V +public net.minecraft.world.level.levelgen.SurfaceRules$BiomeConditionSource +public net.minecraft.world.level.levelgen.SurfaceRules$BiomeConditionSource (Ljava/util/List;)V +public net.minecraft.world.level.levelgen.SurfaceRules$BiomeConditionSource f_189489_ +public net.minecraft.world.level.levelgen.SurfaceRules$Context f_189555_ public net.minecraft.client.renderer.block.model.BlockModel f_111415_ public net.minecraft.server.packs.resources.ProfiledReloadInstance$State f_10689_ public net.minecraft.server.packs.resources.ProfiledReloadInstance$State f_10690_