From 67814db6adaa4f8536efa88ae79511591c067352 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Sun, 7 Dec 2025 20:26:42 -0500 Subject: [PATCH] Mitigate dynres cache thrashing for blocks with more than 10,000 states The previous implementation kept trying to load the models for all states at once in this scenario, which would cause thrashing as most of the loads would be thrown out. The new implementation limits how many models it will load at a time, but still tries to batch as much as possible --- .../dynamic_resources/ModelBakeryMixin.java | 44 ++++++++++++++++--- 1 file changed, 38 insertions(+), 6 deletions(-) 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 a98d7595..0a68427f 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 @@ -305,14 +305,46 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery { } } - private , V extends T> BlockState setPropertyGeneric(BlockState state, Property prop, Object o) { - return state.setValue(prop, (V)o); - } @Redirect(method = "loadModel", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/StateDefinition;getPossibleStates()Lcom/google/common/collect/ImmutableList;")) private ImmutableList loadOnlyRelevantBlockState(StateDefinition stateDefinition, ResourceLocation location) { - if(!(location instanceof ModelResourceLocation) || Minecraft.getInstance().getOverlay() != null || Minecraft.getInstance().level == null) - return stateDefinition.getPossibleStates(); - return ModelBakeryHelpers.getBlockStatesForMRL(stateDefinition, (ModelResourceLocation)location); + var allStates = stateDefinition.getPossibleStates(); + + if(!(location instanceof ModelResourceLocation mrl)) { + return allStates; + } + + // Load a batch of models at once in certain initialization phases to speed up the loading process. + // This is disabled when in-game as it will cause stutters when blocks are placed. + boolean shouldLoadBatch = (Minecraft.getInstance().getOverlay() != null || Minecraft.getInstance().level == null); + int batchSize = ModelBakeryHelpers.MAX_UNBAKED_MODEL_COUNT - 1000; + + // If loading a batch and all the states are smaller than the max batch size, just use them + // This is hoisted above the computation of desiredStates for performance reasons + if (shouldLoadBatch && allStates.size() <= batchSize) { + return allStates; + } + + var desiredStates = ModelBakeryHelpers.getBlockStatesForMRL(stateDefinition, mrl); + + // If not loading a batch, load only the desired states + if (!shouldLoadBatch) { + return desiredStates; + } + + // At this point we want to load a batch if possible, but loading every state is too much. If desiredStates + // is a single state (should almost always be the case), then we choose a sublist starting from it and extending + // batchSize entries (or less if the list ends). If it's multiple states, a single sublist may not include + // everything, so we bail. + if (desiredStates.size() != 1) { + return desiredStates; + } + + var desiredState = desiredStates.get(0); + int indexInAllStates = allStates.indexOf(desiredState); + if (indexInAllStates == -1) { + return desiredStates; + } + return allStates.subList(indexInAllStates, Math.min(indexInAllStates + batchSize, allStates.size())); } @Override