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 1f60556b..a5a17c42 100644 --- a/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java +++ b/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java @@ -26,7 +26,6 @@ public class ModernFixEarlyConfig { this.addMixinRule("perf.skip_first_datapack_reload", true); this.addMixinRule("perf.parallelize_model_loading", true); this.addMixinRule("perf.parallelize_model_loading.multipart", false); - this.addMixinRule("perf.trim_model_caches", true); this.addMixinRule("bugfix.concurrency", true); this.addMixinRule("bugfix.edge_chunk_not_saved", true); this.addMixinRule("bugfix.packet_leak", false); diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_baking/ModelBakeryMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_baking/ModelBakeryMixin.java index 224dece7..223d6386 100644 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_baking/ModelBakeryMixin.java +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_baking/ModelBakeryMixin.java @@ -7,6 +7,7 @@ import net.minecraft.client.renderer.model.multipart.Multipart; import net.minecraft.client.renderer.model.multipart.Selector; import net.minecraft.client.renderer.texture.AtlasTexture; import net.minecraft.client.renderer.texture.SpriteMap; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.texture.TextureManager; import net.minecraft.profiler.IProfiler; import net.minecraft.util.ResourceLocation; @@ -17,6 +18,7 @@ import org.apache.commons.lang3.tuple.Triple; import org.apache.logging.log4j.Logger; import org.embeddedt.modernfix.ModernFix; import org.embeddedt.modernfix.ModernFixClient; +import org.embeddedt.modernfix.models.LazyBakedModel; import org.spongepowered.asm.mixin.*; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -26,6 +28,7 @@ import javax.annotation.Nullable; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; import java.util.stream.Collectors; import static org.embeddedt.modernfix.ModernFix.LOGGER; @@ -42,74 +45,11 @@ public abstract class ModelBakeryMixin { @Shadow @Nullable private SpriteMap atlasSet; - @Shadow @Final private Map unbakedCache; - @Shadow @Mutable - @Final private Map, IBakedModel> bakedCache; + @Shadow @Nullable public abstract IBakedModel getBakedModel(ResourceLocation pLocation, IModelTransform pTransform, Function textureGetter); - @Shadow @Final private static ItemModelGenerator ITEM_MODEL_GENERATOR; - @Shadow @Final public static BlockModel GENERATION_MARKER; + @Shadow @Final public static ModelResourceLocation MISSING_MODEL_LOCATION; - @Shadow public abstract IUnbakedModel getModel(ResourceLocation p_209597_1_); - - private ConcurrentHashMap> fasterBakedCache; - - private Map>> modelsToBakeParallel; - - private boolean canBakeParallel(IUnbakedModel unbakedModel) { - if(unbakedModel instanceof BlockModel) { - BlockModel model = (BlockModel)unbakedModel; - return !model.customData.hasCustomGeometry(); - } else if((unbakedModel instanceof Multipart) || (unbakedModel instanceof VariantList)) - return true; - else - return false; - } - - private IBakedModel bakeModel(ResourceLocation pLocation, IModelTransform pTransform, java.util.function.Function textureGetter) { - IUnbakedModel iunbakedmodel = this.unbakedCache.get(pLocation); - if(iunbakedmodel == null) - throw new IllegalStateException("we should not be baking unloaded models"); - if (iunbakedmodel instanceof BlockModel) { - BlockModel blockmodel = (BlockModel)iunbakedmodel; - if (blockmodel.getRootModel() == GENERATION_MARKER) { - return ITEM_MODEL_GENERATOR.generateBlockModel(textureGetter, blockmodel).bake((ModelBakery)(Object)this, blockmodel, this.atlasSet::getSprite, pTransform, pLocation, false); - } - } - - /* Ensure only one thread builds at a time if needed */ - if(canBakeParallel(iunbakedmodel)) { - return iunbakedmodel.bake((ModelBakery)(Object)this, textureGetter, pTransform, pLocation); - } else { - synchronized(this.bakedCache) { - return iunbakedmodel.bake((ModelBakery)(Object)this, textureGetter, pTransform, pLocation); - } - } - - } - - /** - * @author embeddedt - * @reason avoid rehashing model data when unneeded - */ - @Overwrite(remap = false) - public IBakedModel getBakedModel(ResourceLocation pLocation, IModelTransform pTransform, java.util.function.Function textureGetter) { - /* Try to retrieve the model */ - IdentityHashMap modelsForLocation = this.fasterBakedCache.computeIfAbsent(pLocation, loc -> new IdentityHashMap<>()); - synchronized (modelsForLocation) { - IBakedModel result = modelsForLocation.get(pTransform); - if(result != null) - return result; - } - /* Otherwise, bake the model and then store it */ - synchronized(this.unbakedCache) { - this.getModel(pLocation); - } - IBakedModel result = bakeModel(pLocation, pTransform, textureGetter); - synchronized (modelsForLocation) { - modelsForLocation.put(pTransform, result); - } - return result; - } + @Shadow @Final private Map, IBakedModel> bakedCache; @Inject(method = "processLoading", at = @At(value = "INVOKE", target = "Lnet/minecraft/profiler/IProfiler;pop()V")) private void bakeModels(IProfiler pProfiler, int p_i226056_4_, CallbackInfo ci) { @@ -124,61 +64,24 @@ public abstract class ModelBakeryMixin { pProfiler.popPush("baking"); StartupMessageManager.mcLoaderConsumer().ifPresent(c -> c.accept("Baking models")); this.atlasSet = new SpriteMap(this.atlasPreparations.values().stream().map(Pair::getFirst).collect(Collectors.toList())); - this.bakedCache = Collections.emptyMap(); - this.fasterBakedCache = new ConcurrentHashMap<>(); - this.modelsToBakeParallel = this.unbakedCache.entrySet().stream() - .collect(Collectors.partitioningBy(entry -> { - IUnbakedModel unbakedModel = entry.getValue(); - if(unbakedModel == null) - return false; - else - return this.canBakeParallel(unbakedModel); - })); - List> serialModels = this.modelsToBakeParallel.get(false); - List> parallelModels = this.modelsToBakeParallel.get(true); - LOGGER.debug("Collected " - + serialModels.size() - + " serial models, " - + parallelModels.size() - + " parallel models"); - List>> futures = new ArrayList<>(); - /* First submit the parallel models */ - for(Map.Entry entry : parallelModels) { - ResourceLocation loc = entry.getKey(); - futures.add(CompletableFuture.supplyAsync(() -> { - IBakedModel ibakedmodel = null; + IBakedModel missingModel = this.bake(MISSING_MODEL_LOCATION, ModelRotation.X0_Y0); + this.bakedTopLevelModels.put(MISSING_MODEL_LOCATION, missingModel); + this.topLevelModels.keySet().forEach((p_229350_1_) -> { + this.bakedTopLevelModels.put(p_229350_1_, new LazyBakedModel(() -> { + synchronized (this.bakedCache) { + IBakedModel ibakedmodel = null; - try { - ibakedmodel = this.bake(loc, ModelRotation.X0_Y0); - } catch (Exception exception) { - exception.printStackTrace(); - LOGGER.warn("Unable to bake model: '{}': {}", loc, exception); + try { + ibakedmodel = this.bake(p_229350_1_, ModelRotation.X0_Y0); + } catch (Exception exception) { + exception.printStackTrace(); + LOGGER.warn("Unable to bake model: '{}': {}", p_229350_1_, exception); + } + + return ibakedmodel != null ? ibakedmodel : missingModel; } - return ibakedmodel != null ? Pair.of(loc, ibakedmodel) : null; - }, Util.backgroundExecutor())); - } - futures.forEach(future -> { - Pair pair = future.join(); - if(pair != null && this.topLevelModels.containsKey(pair.getFirst())) - this.bakedTopLevelModels.put(pair.getFirst(), pair.getSecond()); + })); }); - /* Then process serial models */ - serialModels.forEach((p_229350_1_) -> { - IBakedModel ibakedmodel = null; - ResourceLocation loc = p_229350_1_.getKey(); - - try { - ibakedmodel = this.bake(loc, ModelRotation.X0_Y0); - } catch (Exception exception) { - exception.printStackTrace(); - LOGGER.warn("Unable to bake model: '{}': {}", loc, exception); - } - - if (ibakedmodel != null) { - this.bakedTopLevelModels.put(loc, ibakedmodel); - } - }); - this.modelsToBakeParallel = null; this.atlasSet = null; } diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/trim_model_caches/ModelManagerMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/trim_model_caches/ModelManagerMixin.java deleted file mode 100644 index d6145415..00000000 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/trim_model_caches/ModelManagerMixin.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.embeddedt.modernfix.mixin.perf.trim_model_caches; - -import net.minecraft.client.renderer.model.ModelBakery; -import net.minecraft.client.renderer.model.ModelManager; -import net.minecraft.profiler.IProfiler; -import net.minecraft.resources.IResourceManager; -import net.minecraftforge.client.model.ModelLoader; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.ObfuscationReflectionHelper; -import org.embeddedt.modernfix.ModernFix; -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; - -import java.util.HashMap; -import java.util.Map; - -@Mixin(ModelManager.class) -public class ModelManagerMixin { - private void trimBakeryMap(ModelBakery bakery, String fieldName) { - Map map = ObfuscationReflectionHelper.getPrivateValue(ModelBakery.class, bakery, fieldName); - int size = map.size(); - ModernFix.LOGGER.warn("Trimming " + fieldName + " with " + size + " entries"); - if(map instanceof HashMap) { - ObfuscationReflectionHelper.setPrivateValue(ModelBakery.class, bakery, new HashMap<>(), fieldName); - } else - map.clear(); - } - @Inject(method = "apply(Lnet/minecraft/client/renderer/model/ModelBakery;Lnet/minecraft/resources/IResourceManager;Lnet/minecraft/profiler/IProfiler;)V", at = @At("RETURN")) - private void trimModelCaches(ModelBakery bakery, IResourceManager p_212853_2_, IProfiler p_212853_3_, CallbackInfo ci) { - trimBakeryMap(bakery, "field_217849_F"); // unbakedCache - trimBakeryMap(bakery, "field_217850_G"); // bakedCache - trimBakeryMap(bakery, "field_217851_H"); // topLevelModels - // bakedTopLevelModels is used as the model registry - } -} diff --git a/src/main/java/org/embeddedt/modernfix/models/LazyBakedModel.java b/src/main/java/org/embeddedt/modernfix/models/LazyBakedModel.java new file mode 100644 index 00000000..eed45070 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/models/LazyBakedModel.java @@ -0,0 +1,128 @@ +package org.embeddedt.modernfix.models; + +import com.mojang.blaze3d.matrix.MatrixStack; +import com.mojang.datafixers.util.Pair; +import net.minecraft.block.BlockState; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.model.BakedQuad; +import net.minecraft.client.renderer.model.IBakedModel; +import net.minecraft.client.renderer.model.ItemCameraTransforms; +import net.minecraft.client.renderer.model.ItemOverrideList; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.item.ItemStack; +import net.minecraft.util.Direction; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IBlockDisplayReader; +import net.minecraftforge.client.model.data.EmptyModelData; +import net.minecraftforge.client.model.data.IModelData; +import org.embeddedt.modernfix.ModernFix; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.List; +import java.util.Random; +import java.util.function.Supplier; + +public class LazyBakedModel implements IBakedModel { + private IBakedModel delegate = null; + private Supplier delegateSupplier; + + public LazyBakedModel(Supplier delegateSupplier) { + this.delegateSupplier = delegateSupplier; + } + + public IBakedModel computeDelegate() { + if(this.delegate == null) { + this.delegate = this.delegateSupplier.get(); + this.delegateSupplier = null; + } + return this.delegate; + } + + @Override + public List getQuads(@Nullable BlockState pState, @Nullable Direction pSide, Random pRand) { + return computeDelegate().getQuads(pState, pSide, pRand); + } + + @Override + public boolean useAmbientOcclusion() { + return computeDelegate().useAmbientOcclusion(); + } + + @Override + public boolean isGui3d() { + return computeDelegate().isGui3d(); + } + + @Override + public boolean usesBlockLight() { + return computeDelegate().usesBlockLight(); + } + + @Override + public boolean isCustomRenderer() { + return computeDelegate().isCustomRenderer(); + } + + @Override + public TextureAtlasSprite getParticleIcon() { + return computeDelegate().getParticleIcon(); + } + + @Override + public ItemCameraTransforms getTransforms() { + return computeDelegate().getTransforms(); + } + + @Override + public ItemOverrideList getOverrides() { + return computeDelegate().getOverrides(); + } + + @Override + public IBakedModel getBakedModel() { + return computeDelegate().getBakedModel(); + } + + @Nonnull + @Override + public List getQuads(@Nullable BlockState state, @Nullable Direction side, @Nonnull Random rand, @Nonnull IModelData extraData) { + return computeDelegate().getQuads(state, side, rand, extraData); + } + + @Override + public boolean isAmbientOcclusion(BlockState state) { + return computeDelegate().isAmbientOcclusion(state); + } + + @Override + public boolean doesHandlePerspectives() { + return computeDelegate().doesHandlePerspectives(); + } + + @Override + public IBakedModel handlePerspective(ItemCameraTransforms.TransformType cameraTransformType, MatrixStack mat) { + return computeDelegate().handlePerspective(cameraTransformType, mat); + } + + @Nonnull + @Override + public IModelData getModelData(@Nonnull IBlockDisplayReader world, @Nonnull BlockPos pos, @Nonnull BlockState state, @Nonnull IModelData tileData) { + return computeDelegate().getModelData(world, pos, state, tileData); + } + + @Override + public TextureAtlasSprite getParticleTexture(@Nonnull IModelData data) { + return computeDelegate().getParticleTexture(data); + } + + @Override + public boolean isLayered() { + return computeDelegate().isLayered(); + } + + @Override + public List> getLayerModels(ItemStack itemStack, boolean fabulous) { + return computeDelegate().getLayerModels(itemStack, fabulous); + } +} diff --git a/src/main/resources/modernfix.mixins.json b/src/main/resources/modernfix.mixins.json index 27ad1b49..15ab81de 100644 --- a/src/main/resources/modernfix.mixins.json +++ b/src/main/resources/modernfix.mixins.json @@ -40,7 +40,6 @@ "perf.parallelize_model_loading.SelectorMixin", "perf.parallelize_model_loading.TransformationMatrixMixin", "perf.parallelize_model_loading.BooleanPropertyMixin", - "perf.trim_model_caches.ModelManagerMixin", "perf.async_jei.IngredientListElementFactoryMixin", "perf.async_jei.ClientLifecycleHandlerMixin", "perf.async_jei.JeiStarterMixin",