diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/BlockModelShaperMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/BlockModelShaperMixin.java index 9648c6a1..5aa70d22 100644 --- a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/BlockModelShaperMixin.java +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/BlockModelShaperMixin.java @@ -6,7 +6,7 @@ import net.minecraft.client.resources.model.ModelManager; import net.minecraft.world.level.block.state.BlockState; import org.embeddedt.modernfix.annotation.ClientOnlyMixin; import org.embeddedt.modernfix.dynamicresources.ModelLocationCache; -import org.embeddedt.modernfix.util.DynamicMap; +import org.embeddedt.modernfix.util.DynamicOverridableMap; import org.spongepowered.asm.mixin.*; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -26,7 +26,7 @@ public class BlockModelShaperMixin { @Inject(method = { "", "replaceCache" }, at = @At("RETURN")) private void replaceModelMap(CallbackInfo ci) { // replace the backing map for mods which will access it - this.modelByStateCache = new DynamicMap<>(state -> modelManager.getModel(ModelLocationCache.get(state))); + this.modelByStateCache = new DynamicOverridableMap<>(state -> modelManager.getModel(ModelLocationCache.get(state))); } @Overwrite diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/reduce_blockstate_cache_rebuilds/BlockStateBaseMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/reduce_blockstate_cache_rebuilds/BlockStateBaseMixin.java index 76d9fadf..5daa0af2 100644 --- a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/reduce_blockstate_cache_rebuilds/BlockStateBaseMixin.java +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/reduce_blockstate_cache_rebuilds/BlockStateBaseMixin.java @@ -3,10 +3,13 @@ package org.embeddedt.modernfix.common.mixin.perf.reduce_blockstate_cache_rebuil import net.minecraft.world.level.block.state.BlockBehaviour; import org.embeddedt.modernfix.duck.IBlockState; import org.objectweb.asm.Opcodes; +import org.spongepowered.asm.mixin.Dynamic; 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.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @Mixin(BlockBehaviour.BlockStateBase.class) @@ -23,13 +26,7 @@ public abstract class BlockStateBaseMixin implements IBlockState { cacheInvalid = true; } - @Redirect(method = "*", at = @At( - value = "FIELD", - opcode = Opcodes.GETFIELD, - target = "Lnet/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase;cache:Lnet/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase$Cache;", - ordinal = 0 - )) - private BlockBehaviour.BlockStateBase.Cache initCacheIfNeeded(BlockBehaviour.BlockStateBase base) { + private BlockBehaviour.BlockStateBase.Cache generateCache(BlockBehaviour.BlockStateBase base) { if(cacheInvalid) { // Ensure that only one block's cache is built at a time synchronized (BlockBehaviour.BlockStateBase.class) { @@ -50,4 +47,32 @@ public abstract class BlockStateBaseMixin implements IBlockState { } return this.cache; } + + @Redirect(method = "*", at = @At( + value = "FIELD", + opcode = Opcodes.GETFIELD, + target = "Lnet/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase;cache:Lnet/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase$Cache;", + ordinal = 0 + )) + private BlockBehaviour.BlockStateBase.Cache dynamicCacheGen(BlockBehaviour.BlockStateBase base) { + return generateCache(base); + } + + @Dynamic + @Inject(method = "getPathNodeType", at = @At("HEAD"), require = 0, remap = false) + private void generateCacheLithium(CallbackInfoReturnable cir) { + generateCache((BlockBehaviour.BlockStateBase)(Object)this); + } + + @Dynamic + @Inject(method = "getNeighborPathNodeType", at = @At("HEAD"), require = 0, remap = false) + private void generateCacheLithium2(CallbackInfoReturnable cir) { + generateCache((BlockBehaviour.BlockStateBase)(Object)this); + } + + @Dynamic + @Inject(method = "getAllFlags", at = @At("HEAD"), require = 0, remap = false) + private void generateCacheLithium3(CallbackInfoReturnable cir) { + generateCache((BlockBehaviour.BlockStateBase)(Object)this); + } } diff --git a/common/src/main/java/org/embeddedt/modernfix/dynamicresources/ModelBakeryHelpers.java b/common/src/main/java/org/embeddedt/modernfix/dynamicresources/ModelBakeryHelpers.java index ea4fb43d..bb50402a 100644 --- a/common/src/main/java/org/embeddedt/modernfix/dynamicresources/ModelBakeryHelpers.java +++ b/common/src/main/java/org/embeddedt/modernfix/dynamicresources/ModelBakeryHelpers.java @@ -11,6 +11,18 @@ import net.minecraft.world.level.block.state.properties.Property; import java.util.*; public class ModelBakeryHelpers { + /** + * The maximum number of baked models kept in memory at once. + */ + public static final int MAX_BAKED_MODEL_COUNT = 10000; + /** + * The maximum number of unbaked models kept in memory at once. + */ + public static final int MAX_UNBAKED_MODEL_COUNT = 10000; + /** + * The time in seconds after which a model becomes eligible for eviction if not used. + */ + public static final int MAX_MODEL_LIFETIME_SECS = 300; private static , V extends T> BlockState setPropertyGeneric(BlockState state, Property prop, Object o) { return state.setValue(prop, (V)o); diff --git a/common/src/main/java/org/embeddedt/modernfix/entity/EntityRendererMap.java b/common/src/main/java/org/embeddedt/modernfix/entity/EntityRendererMap.java index 386e7549..f9532eaf 100644 --- a/common/src/main/java/org/embeddedt/modernfix/entity/EntityRendererMap.java +++ b/common/src/main/java/org/embeddedt/modernfix/entity/EntityRendererMap.java @@ -75,7 +75,9 @@ public class EntityRendererMap implements Map, EntityRenderer> if(renderer == null) throw new AssertionError("Returned entity renderer should never be null"); return renderer; - } catch(ExecutionException e) { + } catch (IllegalStateException e) { + return null; /* emulate value not being present if recursive load occurs */ + } catch (ExecutionException e) { throw new RuntimeException(e); } } diff --git a/common/src/main/java/org/embeddedt/modernfix/util/DynamicOverridableMap.java b/common/src/main/java/org/embeddedt/modernfix/util/DynamicOverridableMap.java new file mode 100644 index 00000000..e16d3f96 --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/util/DynamicOverridableMap.java @@ -0,0 +1,42 @@ +package org.embeddedt.modernfix.util; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.function.Function; + +public class DynamicOverridableMap extends DynamicMap { + private final Map overrideMap; + + public DynamicOverridableMap(Function function) { + super(function); + overrideMap = new Object2ObjectOpenHashMap<>(); + } + + @Override + public @Nullable V put(K k, V v) { + if(v == null) + throw new IllegalArgumentException(); + overrideMap.put(k, v); + return null; + } + + @Override + public V get(Object o) { + V val = overrideMap.get(o); + if(val != null) + return val; + return super.get(o); + } + + @Override + public void putAll(@NotNull Map map) { + for(V val : map.values()) { + if(val == null) + throw new IllegalArgumentException(); + } + overrideMap.putAll(map); + } +} diff --git a/fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/perf/dynamic_resources/ModelBakeryMixin.java b/fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/perf/dynamic_resources/ModelBakeryMixin.java index f63b9a12..7c56d153 100644 --- a/fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/perf/dynamic_resources/ModelBakeryMixin.java +++ b/fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/perf/dynamic_resources/ModelBakeryMixin.java @@ -77,15 +77,15 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery { this.blockColors = val; this.ignoreModelLoad = true; this.loadedBakedModels = CacheBuilder.newBuilder() - .expireAfterAccess(3, TimeUnit.MINUTES) - .maximumSize(1000) + .expireAfterAccess(ModelBakeryHelpers.MAX_MODEL_LIFETIME_SECS, TimeUnit.SECONDS) + .maximumSize(ModelBakeryHelpers.MAX_BAKED_MODEL_COUNT) .concurrencyLevel(8) .removalListener(this::onModelRemoved) .softValues() .build(); this.loadedModels = CacheBuilder.newBuilder() - .expireAfterAccess(3, TimeUnit.MINUTES) - .maximumSize(1000) + .expireAfterAccess(ModelBakeryHelpers.MAX_MODEL_LIFETIME_SECS, TimeUnit.SECONDS) + .maximumSize(ModelBakeryHelpers.MAX_UNBAKED_MODEL_COUNT) .concurrencyLevel(8) .removalListener(this::onModelRemoved) //.softValues() diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/ModelBakeryMixin.java b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/ModelBakeryMixin.java index 86460268..0c87eda0 100644 --- a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/ModelBakeryMixin.java +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/dynamic_resources/ModelBakeryMixin.java @@ -71,15 +71,15 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery { private void replaceTopLevelBakedModels(ModelBakery bakery, BlockColors val) { this.blockColors = val; this.loadedBakedModels = CacheBuilder.newBuilder() - .expireAfterAccess(3, TimeUnit.MINUTES) - .maximumSize(1000) + .expireAfterAccess(ModelBakeryHelpers.MAX_MODEL_LIFETIME_SECS, TimeUnit.SECONDS) + .maximumSize(ModelBakeryHelpers.MAX_BAKED_MODEL_COUNT) .concurrencyLevel(8) .removalListener(this::onModelRemoved) .softValues() .build(); this.loadedModels = CacheBuilder.newBuilder() - .expireAfterAccess(3, TimeUnit.MINUTES) - .maximumSize(1000) + .expireAfterAccess(ModelBakeryHelpers.MAX_MODEL_LIFETIME_SECS, TimeUnit.SECONDS) + .maximumSize(ModelBakeryHelpers.MAX_UNBAKED_MODEL_COUNT) .concurrencyLevel(8) .removalListener(this::onModelRemoved) .softValues()