Skip to content

Commit 1794c81

Browse files
committed
Optimize sequence rules that check many biome conditions in a row
1 parent dbe9acb commit 1794c81

3 files changed

Lines changed: 127 additions & 0 deletions

File tree

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package org.embeddedt.modernfix.common.mixin.perf.optimize_surface_rules;
2+
3+
import net.minecraft.world.level.levelgen.SurfaceRules;
4+
import org.embeddedt.modernfix.world.gen.SurfaceRuleOptimizer;
5+
import org.spongepowered.asm.mixin.Mixin;
6+
import org.spongepowered.asm.mixin.injection.At;
7+
import org.spongepowered.asm.mixin.injection.Inject;
8+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
9+
10+
@Mixin(targets = {"net/minecraft/world/level/levelgen/SurfaceRules$SequenceRuleSource"})
11+
public class SequenceRuleSourceMixin {
12+
@Inject(method = "apply(Lnet/minecraft/world/level/levelgen/SurfaceRules$Context;)Lnet/minecraft/world/level/levelgen/SurfaceRules$SurfaceRule;", at = @At("HEAD"), cancellable = true)
13+
private void optimizeApply(SurfaceRules.Context context, CallbackInfoReturnable<SurfaceRules.SurfaceRule> cir) {
14+
var optimized = SurfaceRuleOptimizer.optimizeSequenceRule((SurfaceRules.SequenceRuleSource)(Object) this, context);
15+
if (optimized != null) {
16+
cir.setReturnValue(optimized);
17+
}
18+
}
19+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package org.embeddedt.modernfix.world.gen;
2+
3+
import it.unimi.dsi.fastutil.objects.Reference2ObjectMaps;
4+
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
5+
import net.minecraft.core.Holder;
6+
import net.minecraft.resources.ResourceKey;
7+
import net.minecraft.world.level.biome.Biome;
8+
import net.minecraft.world.level.block.state.BlockState;
9+
import net.minecraft.world.level.levelgen.SurfaceRules;
10+
import org.jetbrains.annotations.Nullable;
11+
12+
import java.util.ArrayList;
13+
import java.util.List;
14+
import java.util.Map;
15+
import java.util.Objects;
16+
17+
public class SurfaceRuleOptimizer {
18+
public static @Nullable SurfaceRules.SurfaceRule optimizeSequenceRule(SurfaceRules.SequenceRuleSource source, SurfaceRules.Context context) {
19+
// First pass: collect which biomes appear and count biome-gated branches
20+
Reference2ObjectOpenHashMap<ResourceKey<Biome>, List<SurfaceRules.RuleSource>> perBiomeSources = new Reference2ObjectOpenHashMap<>();
21+
int biomeGatedBranches = 0;
22+
for (var innerSource : source.sequence()) {
23+
if (innerSource instanceof SurfaceRules.TestRuleSource testRuleSource
24+
&& testRuleSource.ifTrue() instanceof SurfaceRules.BiomeConditionSource biomeConditionSource) {
25+
biomeGatedBranches++;
26+
for (var biome : biomeConditionSource.biomes) {
27+
perBiomeSources.putIfAbsent(biome, new ArrayList<>());
28+
}
29+
}
30+
}
31+
if (biomeGatedBranches < 3) {
32+
return null;
33+
}
34+
// Second pass: build per-biome source lists preserving original interleaving order
35+
List<SurfaceRules.RuleSource> noMatchSources = new ArrayList<>();
36+
for (var innerSource : source.sequence()) {
37+
if (innerSource instanceof SurfaceRules.TestRuleSource testRuleSource
38+
&& testRuleSource.ifTrue() instanceof SurfaceRules.BiomeConditionSource biomeConditionSource) {
39+
// Add the inner rule (condition stripped) only to the matching biomes' lists
40+
for (var biome : biomeConditionSource.biomes) {
41+
perBiomeSources.get(biome).add(testRuleSource.thenRun());
42+
}
43+
} else {
44+
// Non-biome-gated rule: add to every biome list and the no-match list
45+
for (var list : perBiomeSources.values()) {
46+
list.add(innerSource);
47+
}
48+
noMatchSources.add(innerSource);
49+
}
50+
}
51+
// Compile all source lists into rule lists
52+
Reference2ObjectOpenHashMap<ResourceKey<Biome>, List<SurfaceRules.SurfaceRule>> compiledBiomeMatch = new Reference2ObjectOpenHashMap<>(perBiomeSources.size());
53+
Reference2ObjectMaps.fastForEach(perBiomeSources, entry -> {
54+
List<SurfaceRules.SurfaceRule> compiled = new ArrayList<>(entry.getValue().size());
55+
for (var src : entry.getValue()) {
56+
compiled.add(src.apply(context));
57+
}
58+
compiledBiomeMatch.put(entry.getKey(), List.copyOf(compiled));
59+
});
60+
List<SurfaceRules.SurfaceRule> compiledNoMatch = new ArrayList<>(noMatchSources.size());
61+
for (var src : noMatchSources) {
62+
compiledNoMatch.add(src.apply(context));
63+
}
64+
return new OptimizedBiomeLookupSequenceRule(compiledBiomeMatch, List.copyOf(compiledNoMatch), context);
65+
}
66+
67+
public record OptimizedBiomeLookupSequenceRule(
68+
Map<ResourceKey<Biome>, List<SurfaceRules.SurfaceRule>> rulesForBiomeMatch,
69+
List<SurfaceRules.SurfaceRule> rulesForNoBiomeMatch,
70+
SurfaceRules.Context context
71+
) implements SurfaceRules.SurfaceRule {
72+
@Override
73+
public @Nullable BlockState tryApply(int x, int y, int z) {
74+
var biome = context.biome.get();
75+
var key = (biome instanceof Holder.Reference<Biome> ref) ? ref.key() : biome.unwrapKey().orElseThrow();
76+
var ruleList = rulesForBiomeMatch.getOrDefault(key, rulesForNoBiomeMatch);
77+
//noinspection ForLoopReplaceableByForEach
78+
for (int i = 0; i < ruleList.size(); i++) {
79+
var rule = ruleList.get(i);
80+
var state = rule.tryApply(x, y, z);
81+
if (state != null) {
82+
return state;
83+
}
84+
}
85+
return null;
86+
}
87+
88+
@Override
89+
public boolean equals(Object o) {
90+
if (o == null || getClass() != o.getClass()) return false;
91+
OptimizedBiomeLookupSequenceRule that = (OptimizedBiomeLookupSequenceRule) o;
92+
return rulesForBiomeMatch.equals(that.rulesForBiomeMatch) && rulesForNoBiomeMatch.equals(that.rulesForNoBiomeMatch);
93+
}
94+
95+
@Override
96+
public int hashCode() {
97+
return Objects.hash(rulesForBiomeMatch, rulesForNoBiomeMatch);
98+
}
99+
}
100+
}

‎src/main/resources/META-INF/accesstransformer.cfg‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@ public net.minecraft.client.renderer.block.model.multipart.MultiPart f_111962_
55
public net.minecraft.client.resources.model.ModelBakery$ModelBakerImpl
66
public net.minecraft.client.resources.model.ModelBakery$ModelBakerImpl <init>(Lnet/minecraft/client/resources/model/ModelBakery;Ljava/util/function/BiFunction;Lnet/minecraft/resources/ResourceLocation;)V
77
public net.minecraft.world.level.levelgen.SurfaceRules$SequenceRule
8+
public net.minecraft.world.level.levelgen.SurfaceRules$SequenceRuleSource
9+
public net.minecraft.world.level.levelgen.SurfaceRules$SequenceRuleSource <init>(Ljava/util/List;)V
10+
public net.minecraft.world.level.levelgen.SurfaceRules$TestRuleSource
11+
public net.minecraft.world.level.levelgen.SurfaceRules$TestRuleSource <init>(Lnet/minecraft/world/level/levelgen/SurfaceRules$ConditionSource;Lnet/minecraft/world/level/levelgen/SurfaceRules$RuleSource;)V
12+
public net.minecraft.world.level.levelgen.SurfaceRules$BiomeConditionSource
13+
public net.minecraft.world.level.levelgen.SurfaceRules$BiomeConditionSource <init>(Ljava/util/List;)V
14+
public net.minecraft.world.level.levelgen.SurfaceRules$BiomeConditionSource f_189489_
15+
public net.minecraft.world.level.levelgen.SurfaceRules$Context f_189555_
816
public net.minecraft.client.renderer.block.model.BlockModel f_111415_
917
public net.minecraft.server.packs.resources.ProfiledReloadInstance$State f_10689_
1018
public net.minecraft.server.packs.resources.ProfiledReloadInstance$State f_10690_

0 commit comments

Comments
 (0)