From 8dc915037c2a4c74912188f9b8a98c4763577e09 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Sat, 28 Jan 2023 12:49:38 -0500 Subject: [PATCH] Improve blockstate cache rebuild logic, remove vanilla search trees if JEI is installed --- .../blockstate/BlockStateCacheHandler.java | 57 +++++++++---------- .../core/config/ModernFixEarlyConfig.java | 3 +- .../IngredientFilterInvoker.java | 14 +++++ .../blast_search_trees/MinecraftMixin.java | 31 ++++++++++ .../BlockCallbacksMixin.java | 30 ---------- .../AbstractBlockStateMixin.java | 18 ------ .../BlockCallbacksMixin.java | 17 ++++-- .../BlocksMixin.java | 2 +- .../ShapeCacheMixin.java | 2 +- .../modernfix/searchtree/DummySearchTree.java | 31 ++++++++++ .../searchtree/JEIBackedSearchTree.java | 48 ++++++++++++++++ src/main/resources/modernfix.mixins.json | 11 ++-- 12 files changed, 174 insertions(+), 90 deletions(-) create mode 100644 src/main/java/org/embeddedt/modernfix/mixin/perf/blast_search_trees/IngredientFilterInvoker.java create mode 100644 src/main/java/org/embeddedt/modernfix/mixin/perf/blast_search_trees/MinecraftMixin.java delete mode 100644 src/main/java/org/embeddedt/modernfix/mixin/perf/parallel_blockstate_cache_rebuild/BlockCallbacksMixin.java delete mode 100644 src/main/java/org/embeddedt/modernfix/mixin/perf/reduce_blockstate_cache_rebuilds/AbstractBlockStateMixin.java rename src/main/java/org/embeddedt/modernfix/mixin/perf/{parallel_blockstate_cache_rebuild => reduce_blockstate_cache_rebuilds}/BlocksMixin.java (88%) rename src/main/java/org/embeddedt/modernfix/mixin/perf/{parallel_blockstate_cache_rebuild => reduce_blockstate_cache_rebuilds}/ShapeCacheMixin.java (92%) create mode 100644 src/main/java/org/embeddedt/modernfix/searchtree/DummySearchTree.java create mode 100644 src/main/java/org/embeddedt/modernfix/searchtree/JEIBackedSearchTree.java diff --git a/src/main/java/org/embeddedt/modernfix/blockstate/BlockStateCacheHandler.java b/src/main/java/org/embeddedt/modernfix/blockstate/BlockStateCacheHandler.java index 0919dd1c..b8e4196e 100644 --- a/src/main/java/org/embeddedt/modernfix/blockstate/BlockStateCacheHandler.java +++ b/src/main/java/org/embeddedt/modernfix/blockstate/BlockStateCacheHandler.java @@ -2,12 +2,15 @@ package org.embeddedt.modernfix.blockstate; import com.google.common.base.Stopwatch; import com.google.common.collect.ImmutableSet; +import net.minecraft.block.AbstractBlock; import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.util.Util; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.shapes.ISelectionContext; import net.minecraft.world.EmptyBlockReader; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.fml.loading.FMLLoader; import org.embeddedt.modernfix.ModernFix; import org.embeddedt.modernfix.duck.IBlockState; import org.embeddedt.modernfix.util.BakeReason; @@ -25,39 +28,35 @@ public class BlockStateCacheHandler { .add("cabletiers") .add("extrastorage") .build(); - public static void handleStateCache(BlockState state) { + + private static boolean needToBake() { BakeReason reason = BakeReason.getCurrentBakeReason(); - if(reason == BakeReason.FREEZE - || reason == BakeReason.REMOTE_SNAPSHOT_INJECT - || (reason == BakeReason.LOCAL_SNAPSHOT_INJECT && ModernFix.runningFirstInjection)) { - //((IBlockState)state).clearCache(); - } else { - state.initCache(); - } - } - private static void handleStateCacheParallel(BlockState state, boolean force) { - if(force) - state.initCache(); - else - handleStateCache(state); + return !(reason == BakeReason.FREEZE /* startup */ + || reason == BakeReason.REVERT /* crash, in which case cache likely doesn't matter, or exiting world */ + || reason == BakeReason.REMOTE_SNAPSHOT_INJECT /* will be handled when tags are reloaded */ + || (reason == BakeReason.LOCAL_SNAPSHOT_INJECT && FMLLoader.getDist() == Dist.CLIENT /* will be handled when tags are reloaded */)); } @SuppressWarnings("deprecation") public static void rebuildParallel(boolean force) { - /* Run some special sauce for Refined Storage since it has very slow collision shapes */ - Stopwatch realtimeStopwatch = Stopwatch.createStarted(); - List specialStates = StreamSupport.stream(Block.BLOCK_STATE_REGISTRY.spliterator(), false) - .filter(state -> PRECACHED_COLLISION_SHAPES.contains(state.getBlock().getRegistryName().getNamespace())).collect(Collectors.toList()); - CompletableFuture.runAsync(() -> { - specialStates.parallelStream() - .forEach(state -> { - /* Force these blocks to compute their shapes ahead of time on worker threads */ - state.getBlock().getCollisionShape(state, EmptyBlockReader.INSTANCE, BlockPos.ZERO, ISelectionContext.empty()); - state.getBlock().getOcclusionShape(state, EmptyBlockReader.INSTANCE, BlockPos.ZERO); - }); - }, Util.backgroundExecutor()).join(); - Block.BLOCK_STATE_REGISTRY.forEach(state -> handleStateCacheParallel(state, force)); - realtimeStopwatch.stop(); - ModernFix.LOGGER.info("Real time spent rebuilding blockstate cache: " + realtimeStopwatch.elapsed(TimeUnit.MILLISECONDS)/1000f + " seconds"); + if(force || needToBake()) { + Stopwatch realtimeStopwatch = Stopwatch.createStarted(); + /* Run some special sauce for Refined Storage since it has very slow collision shapes */ + List specialStates = StreamSupport.stream(Block.BLOCK_STATE_REGISTRY.spliterator(), false) + .filter(state -> PRECACHED_COLLISION_SHAPES.contains(state.getBlock().getRegistryName().getNamespace())).collect(Collectors.toList()); + CompletableFuture.runAsync(() -> { + specialStates.parallelStream() + .forEach(state -> { + /* Force these blocks to compute their shapes ahead of time on worker threads */ + state.getBlock().getCollisionShape(state, EmptyBlockReader.INSTANCE, BlockPos.ZERO, ISelectionContext.empty()); + state.getBlock().getOcclusionShape(state, EmptyBlockReader.INSTANCE, BlockPos.ZERO); + }); + }, Util.backgroundExecutor()).join(); + Block.BLOCK_STATE_REGISTRY.forEach(AbstractBlock.AbstractBlockState::initCache); + realtimeStopwatch.stop(); + ModernFix.LOGGER.info("Blockstate cache rebuilt in " + realtimeStopwatch.elapsed(TimeUnit.MILLISECONDS)/1000f + " seconds"); + } else { + ModernFix.LOGGER.warn("Deferred blockstate cache rebuild"); + } } } diff --git a/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java b/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java index da7eb18d..03b6d2e4 100644 --- a/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java +++ b/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java @@ -34,12 +34,13 @@ public class ModernFixEarlyConfig { this.addMixinRule("perf.preload_block_classes", false); this.addMixinRule("perf.sync_executor_sleep", true); this.addMixinRule("perf.scan_cache", true); - this.addMixinRule("perf.parallel_blockstate_cache_rebuild", true); this.addMixinRule("perf.compress_biome_container", true); this.addMixinRule("perf.nuke_empty_chunk_sections", true); this.addMixinRule("perf.flatten_model_predicates", true); this.addMixinRule("perf.deduplicate_location", true); this.addMixinRule("perf.cache_blockstate_cache_arrays", true); + /* Keep this off if JEI isn't installed to prevent breaking vanilla gameplay */ + this.addMixinRule("perf.blast_search_trees", FMLLoader.getLoadingModList().getModFileById("jei") != null); this.addMixinRule("safety", true); this.addMixinRule("launch.transformer_cache", false); this.addMixinRule("launch.class_search_cache", true); diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/blast_search_trees/IngredientFilterInvoker.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/blast_search_trees/IngredientFilterInvoker.java new file mode 100644 index 00000000..4c46b457 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/blast_search_trees/IngredientFilterInvoker.java @@ -0,0 +1,14 @@ +package org.embeddedt.modernfix.mixin.perf.blast_search_trees; + +import mezz.jei.ingredients.IIngredientListElementInfo; +import mezz.jei.ingredients.IngredientFilter; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +import java.util.List; + +@Mixin(IngredientFilter.class) +public interface IngredientFilterInvoker { + @Invoker + List> invokeGetIngredientListUncached(String filterText); +} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/blast_search_trees/MinecraftMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/blast_search_trees/MinecraftMixin.java new file mode 100644 index 00000000..591519db --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/blast_search_trees/MinecraftMixin.java @@ -0,0 +1,31 @@ +package org.embeddedt.modernfix.mixin.perf.blast_search_trees; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.util.SearchTreeManager; +import net.minecraftforge.fml.ModList; +import org.embeddedt.modernfix.searchtree.DummySearchTree; +import org.embeddedt.modernfix.searchtree.JEIBackedSearchTree; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(Minecraft.class) +public class MinecraftMixin { + @Shadow @Final private SearchTreeManager searchRegistry; + + @Inject(method = "createSearchTrees", at = @At("HEAD"), cancellable = true) + private void replaceSearchTrees(CallbackInfo ci) { + ci.cancel(); + if(ModList.get().getModFileById("jei") != null) { + this.searchRegistry.register(SearchTreeManager.CREATIVE_NAMES, new JEIBackedSearchTree(false)); + this.searchRegistry.register(SearchTreeManager.CREATIVE_TAGS, new JEIBackedSearchTree(true)); + } else { + this.searchRegistry.register(SearchTreeManager.CREATIVE_NAMES, new DummySearchTree<>()); + this.searchRegistry.register(SearchTreeManager.CREATIVE_TAGS, new DummySearchTree<>()); + } + this.searchRegistry.register(SearchTreeManager.RECIPE_COLLECTIONS, new DummySearchTree<>()); + } +} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallel_blockstate_cache_rebuild/BlockCallbacksMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/parallel_blockstate_cache_rebuild/BlockCallbacksMixin.java deleted file mode 100644 index 12c3367a..00000000 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallel_blockstate_cache_rebuild/BlockCallbacksMixin.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.embeddedt.modernfix.mixin.perf.parallel_blockstate_cache_rebuild; - -import net.minecraft.block.Block; -import net.minecraft.block.BlockState; -import net.minecraft.util.ObjectIntIdentityMap; -import net.minecraft.world.gen.DebugChunkGenerator; -import net.minecraftforge.registries.GameData; -import net.minecraftforge.registries.IForgeRegistryInternal; -import net.minecraftforge.registries.RegistryManager; -import org.embeddedt.modernfix.blockstate.BlockStateCacheHandler; -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; - -@Mixin(targets = { "net/minecraftforge/registries/GameData$BlockCallbacks" }) -public class BlockCallbacksMixin { - @Inject(method = "onBake", at = @At(value = "INVOKE", target = "Ljava/util/Iterator;hasNext()Z"), cancellable = true, remap = false) - private void computeCacheParallel(IForgeRegistryInternal owner, RegistryManager stage, CallbackInfo ci) { - ci.cancel(); - ObjectIntIdentityMap blockstateMap = GameData.getBlockStateIDMap(); - for (Block block : owner) { - for (BlockState state : block.getStateDefinition().getPossibleStates()) { - blockstateMap.add(state); - } - } - BlockStateCacheHandler.rebuildParallel(false); - DebugChunkGenerator.initValidStates(); - } -} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/reduce_blockstate_cache_rebuilds/AbstractBlockStateMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/reduce_blockstate_cache_rebuilds/AbstractBlockStateMixin.java deleted file mode 100644 index 947e59c0..00000000 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/reduce_blockstate_cache_rebuilds/AbstractBlockStateMixin.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.embeddedt.modernfix.mixin.perf.reduce_blockstate_cache_rebuilds; - -import net.minecraft.block.AbstractBlock; -import org.embeddedt.modernfix.duck.IBlockState; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; - -import javax.annotation.Nullable; - -@Mixin(AbstractBlock.AbstractBlockState.class) -public class AbstractBlockStateMixin implements IBlockState { - @Shadow @Nullable protected AbstractBlock.AbstractBlockState.Cache cache; - - @Override - public void clearCache() { - this.cache = null; - } -} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/reduce_blockstate_cache_rebuilds/BlockCallbacksMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/reduce_blockstate_cache_rebuilds/BlockCallbacksMixin.java index 704c5dd6..b7502f18 100644 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/reduce_blockstate_cache_rebuilds/BlockCallbacksMixin.java +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/reduce_blockstate_cache_rebuilds/BlockCallbacksMixin.java @@ -1,18 +1,25 @@ package org.embeddedt.modernfix.mixin.perf.reduce_blockstate_cache_rebuilds; +import net.minecraft.block.Block; import net.minecraft.block.BlockState; -import org.embeddedt.modernfix.ModernFix; +import net.minecraftforge.registries.IForgeRegistryInternal; +import net.minecraftforge.registries.RegistryManager; import org.embeddedt.modernfix.blockstate.BlockStateCacheHandler; -import org.embeddedt.modernfix.duck.IBlockState; -import org.embeddedt.modernfix.util.BakeReason; 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.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(targets = { "net/minecraftforge/registries/GameData$BlockCallbacks" }) public class BlockCallbacksMixin { @Redirect(method = "onBake", at = @At(value = "INVOKE", target = "Lnet/minecraft/block/BlockState;initCache()V")) - private void skipCacheIfAllowed(BlockState state) { - BlockStateCacheHandler.handleStateCache(state); + private void skipCache(BlockState instance) { + + } + + @Inject(method = "onBake", at = @At(value = "TAIL"), remap = false) + private void computeCaches(IForgeRegistryInternal owner, RegistryManager stage, CallbackInfo ci) { + BlockStateCacheHandler.rebuildParallel(false); } } diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallel_blockstate_cache_rebuild/BlocksMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/reduce_blockstate_cache_rebuilds/BlocksMixin.java similarity index 88% rename from src/main/java/org/embeddedt/modernfix/mixin/perf/parallel_blockstate_cache_rebuild/BlocksMixin.java rename to src/main/java/org/embeddedt/modernfix/mixin/perf/reduce_blockstate_cache_rebuilds/BlocksMixin.java index 462c7351..acf13d4b 100644 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallel_blockstate_cache_rebuild/BlocksMixin.java +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/reduce_blockstate_cache_rebuilds/BlocksMixin.java @@ -1,4 +1,4 @@ -package org.embeddedt.modernfix.mixin.perf.parallel_blockstate_cache_rebuild; +package org.embeddedt.modernfix.mixin.perf.reduce_blockstate_cache_rebuilds; import net.minecraft.block.Blocks; import org.embeddedt.modernfix.blockstate.BlockStateCacheHandler; diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallel_blockstate_cache_rebuild/ShapeCacheMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/reduce_blockstate_cache_rebuilds/ShapeCacheMixin.java similarity index 92% rename from src/main/java/org/embeddedt/modernfix/mixin/perf/parallel_blockstate_cache_rebuild/ShapeCacheMixin.java rename to src/main/java/org/embeddedt/modernfix/mixin/perf/reduce_blockstate_cache_rebuilds/ShapeCacheMixin.java index ad57d918..b17ba507 100644 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallel_blockstate_cache_rebuild/ShapeCacheMixin.java +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/reduce_blockstate_cache_rebuilds/ShapeCacheMixin.java @@ -1,4 +1,4 @@ -package org.embeddedt.modernfix.mixin.perf.parallel_blockstate_cache_rebuild; +package org.embeddedt.modernfix.mixin.perf.reduce_blockstate_cache_rebuilds; import com.refinedmods.refinedstorage.block.shape.ShapeCache; import net.minecraft.block.BlockState; diff --git a/src/main/java/org/embeddedt/modernfix/searchtree/DummySearchTree.java b/src/main/java/org/embeddedt/modernfix/searchtree/DummySearchTree.java new file mode 100644 index 00000000..7230e5c6 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/searchtree/DummySearchTree.java @@ -0,0 +1,31 @@ +package org.embeddedt.modernfix.searchtree; + +import net.minecraft.client.util.IMutableSearchTree; + +import java.util.Collections; +import java.util.List; + +/** + * Dummy search tree that stores nothing and returns nothing on searches. + */ +public class DummySearchTree implements IMutableSearchTree { + @Override + public void add(T pObj) { + + } + + @Override + public void clear() { + + } + + @Override + public void refresh() { + + } + + @Override + public List search(String pSearchText) { + return Collections.emptyList(); + } +} diff --git a/src/main/java/org/embeddedt/modernfix/searchtree/JEIBackedSearchTree.java b/src/main/java/org/embeddedt/modernfix/searchtree/JEIBackedSearchTree.java new file mode 100644 index 00000000..58d608dd --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/searchtree/JEIBackedSearchTree.java @@ -0,0 +1,48 @@ +package org.embeddedt.modernfix.searchtree; + +import mezz.jei.Internal; +import mezz.jei.ingredients.IIngredientListElementInfo; +import mezz.jei.ingredients.IngredientFilter; +import mezz.jei.runtime.JeiRuntime; +import net.minecraft.item.ItemStack; +import org.embeddedt.modernfix.mixin.perf.blast_search_trees.IngredientFilterInvoker; + +import java.util.ArrayList; +import java.util.List; + +/** + * Uses JEI to handle search tree lookups. + */ +public class JEIBackedSearchTree extends DummySearchTree { + private final boolean filteringByTag; + private String lastSearchText = ""; + private final List listCache = new ArrayList<>(); + + public JEIBackedSearchTree(boolean filteringByTag) { + this.filteringByTag = filteringByTag; + } + @Override + public List search(String pSearchText) { + JeiRuntime runtime = Internal.getRuntime(); + if(runtime != null) { + return this.searchJEI(Internal.getIngredientFilter(), pSearchText); + } else { + /* Use the default, dummy implementation */ + return super.search(pSearchText); + } + } + + private List searchJEI(IngredientFilter filter, String pSearchText) { + if(!pSearchText.equals(lastSearchText)) { + listCache.clear(); + List> ingredients = ((IngredientFilterInvoker)filter).invokeGetIngredientListUncached(filteringByTag ? ("$" + pSearchText) : pSearchText); + for(IIngredientListElementInfo ingredient : ingredients) { + if(ingredient.getElement().getIngredient() instanceof ItemStack) { + listCache.add((ItemStack)ingredient.getElement().getIngredient()); + } + } + lastSearchText = pSearchText; + } + return listCache; + } +} diff --git a/src/main/resources/modernfix.mixins.json b/src/main/resources/modernfix.mixins.json index e7a36858..a0de7eb9 100644 --- a/src/main/resources/modernfix.mixins.json +++ b/src/main/resources/modernfix.mixins.json @@ -12,15 +12,14 @@ "perf.resourcepacks.VanillaPackMixin", "perf.skip_first_datapack_reload.LevelSaveMixin", "perf.skip_first_datapack_reload.SaveFormatAccessor", - "perf.reduce_blockstate_cache_rebuilds.AbstractBlockStateMixin", "perf.reduce_blockstate_cache_rebuilds.GameDataMixin", "perf.reduce_blockstate_cache_rebuilds.BlockCallbacksMixin", "perf.boost_worker_count.UtilMixin", "perf.thread_priorities.UtilMixin", "perf.preload_block_classes.GameDataMixin", - "perf.parallel_blockstate_cache_rebuild.BlocksMixin", - "perf.parallel_blockstate_cache_rebuild.BlockCallbacksMixin", - "perf.parallel_blockstate_cache_rebuild.ShapeCacheMixin", + "perf.reduce_blockstate_cache_rebuilds.BlocksMixin", + "perf.reduce_blockstate_cache_rebuilds.BlockCallbacksMixin", + "perf.reduce_blockstate_cache_rebuilds.ShapeCacheMixin", "perf.deduplicate_location.MixinResourceLocation", "perf.sync_executor_sleep.SyncExecutorMixin", "perf.compress_biome_container.MixinBiomeContainer", @@ -47,7 +46,9 @@ "safety.BlockColorsMixin", "perf.flatten_model_predicates.AndConditionMixin", "perf.flatten_model_predicates.OrConditionMixin", - "perf.flatten_model_predicates.PropertyValueConditionMixin" + "perf.flatten_model_predicates.PropertyValueConditionMixin", + "perf.blast_search_trees.MinecraftMixin", + "perf.blast_search_trees.IngredientFilterInvoker" ], "injectors": { "defaultRequire": 1