Optimize sequence rules that check many biome conditions in a row
This commit is contained in:
parent
dbe9acb3d8
commit
1794c81b61
|
|
@ -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<SurfaceRules.SurfaceRule> cir) {
|
||||
var optimized = SurfaceRuleOptimizer.optimizeSequenceRule((SurfaceRules.SequenceRuleSource)(Object) this, context);
|
||||
if (optimized != null) {
|
||||
cir.setReturnValue(optimized);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<ResourceKey<Biome>, List<SurfaceRules.RuleSource>> 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<SurfaceRules.RuleSource> 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<ResourceKey<Biome>, List<SurfaceRules.SurfaceRule>> compiledBiomeMatch = new Reference2ObjectOpenHashMap<>(perBiomeSources.size());
|
||||
Reference2ObjectMaps.fastForEach(perBiomeSources, entry -> {
|
||||
List<SurfaceRules.SurfaceRule> compiled = new ArrayList<>(entry.getValue().size());
|
||||
for (var src : entry.getValue()) {
|
||||
compiled.add(src.apply(context));
|
||||
}
|
||||
compiledBiomeMatch.put(entry.getKey(), List.copyOf(compiled));
|
||||
});
|
||||
List<SurfaceRules.SurfaceRule> 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<ResourceKey<Biome>, List<SurfaceRules.SurfaceRule>> rulesForBiomeMatch,
|
||||
List<SurfaceRules.SurfaceRule> 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<Biome> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 <init>(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 <init>(Ljava/util/List;)V
|
||||
public net.minecraft.world.level.levelgen.SurfaceRules$TestRuleSource
|
||||
public net.minecraft.world.level.levelgen.SurfaceRules$TestRuleSource <init>(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 <init>(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_
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user