Improve blockstate cache rebuild logic, remove vanilla search trees if JEI is installed

This commit is contained in:
embeddedt 2023-01-28 12:49:38 -05:00
parent fa9a3bb890
commit 8dc915037c
No known key found for this signature in database
GPG Key ID: A69433EC199B5613
12 changed files with 174 additions and 90 deletions

View File

@ -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<BlockState> 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<BlockState> 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");
}
}
}

View File

@ -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);

View File

@ -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<IIngredientListElementInfo<?>> invokeGetIngredientListUncached(String filterText);
}

View File

@ -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<>());
}
}

View File

@ -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<Block> owner, RegistryManager stage, CallbackInfo ci) {
ci.cancel();
ObjectIntIdentityMap<BlockState> blockstateMap = GameData.getBlockStateIDMap();
for (Block block : owner) {
for (BlockState state : block.getStateDefinition().getPossibleStates()) {
blockstateMap.add(state);
}
}
BlockStateCacheHandler.rebuildParallel(false);
DebugChunkGenerator.initValidStates();
}
}

View File

@ -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;
}
}

View File

@ -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<Block> owner, RegistryManager stage, CallbackInfo ci) {
BlockStateCacheHandler.rebuildParallel(false);
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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<T> implements IMutableSearchTree<T> {
@Override
public void add(T pObj) {
}
@Override
public void clear() {
}
@Override
public void refresh() {
}
@Override
public List<T> search(String pSearchText) {
return Collections.emptyList();
}
}

View File

@ -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<ItemStack> {
private final boolean filteringByTag;
private String lastSearchText = "";
private final List<ItemStack> listCache = new ArrayList<>();
public JEIBackedSearchTree(boolean filteringByTag) {
this.filteringByTag = filteringByTag;
}
@Override
public List<ItemStack> 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<ItemStack> searchJEI(IngredientFilter filter, String pSearchText) {
if(!pSearchText.equals(lastSearchText)) {
listCache.clear();
List<IIngredientListElementInfo<?>> 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;
}
}

View File

@ -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