From 928d2e3f02a75abc971350fb0d1220a9763584c2 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Sat, 11 Feb 2023 09:31:28 -0500 Subject: [PATCH] Tweak model loading --- build.gradle | 2 +- .../faster_baking/BlockModelShapesMixin.java | 47 ++++++++++++++ .../perf/faster_baking/ModelBakeryMixin.java | 62 ++++++++++++++++++- .../ModelBakeryMixin.java | 2 + src/main/resources/modernfix.mixins.json | 1 + 5 files changed, 111 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/embeddedt/modernfix/mixin/perf/faster_baking/BlockModelShapesMixin.java diff --git a/build.gradle b/build.gradle index ac48b77d..23010637 100644 --- a/build.gradle +++ b/build.gradle @@ -134,13 +134,13 @@ dependencies { runtimeOnly fg.deobf("curse.maven:lazydfu-460819:${lazydfu_version}") compileOnly fg.deobf("mezz.jei:jei-${minecraft_version}:${jei_version}") + compileOnly fg.deobf("curse.maven:ferritecore-429235:4074330") if(include_optimization_mods.toBoolean()) { runtimeOnly fg.deobf("curse.maven:roadrunner-529754:3683120") runtimeOnly fg.deobf("curse.maven:rubidium-574856:3949659") runtimeOnly fg.deobf("curse.maven:noexperimental-407174:3188120") runtimeOnly fg.deobf("curse.maven:spark-361579:3767277") - runtimeOnly fg.deobf("curse.maven:ferritecore-429235:4074330") runtimeOnly fg.deobf("mezz.jei:jei-${minecraft_version}:${jei_version}") } diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_baking/BlockModelShapesMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_baking/BlockModelShapesMixin.java new file mode 100644 index 00000000..95f369b9 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_baking/BlockModelShapesMixin.java @@ -0,0 +1,47 @@ +package org.embeddedt.modernfix.mixin.perf.faster_baking; + +import com.mojang.datafixers.util.Pair; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.client.renderer.BlockModelShapes; +import net.minecraft.client.renderer.model.IBakedModel; +import net.minecraft.client.renderer.model.ModelManager; +import net.minecraft.client.renderer.model.ModelResourceLocation; +import net.minecraft.util.Util; +import net.minecraft.util.registry.Registry; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.ArrayList; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +@Mixin(BlockModelShapes.class) +public abstract class BlockModelShapesMixin { + @Shadow @Final private ModelManager modelManager; + + @Shadow @Final private Map modelByStateCache; + + /** + * @author embeddedt + * @reason parallelize cache rebuild + */ + @Overwrite + public void rebuildCache() { + this.modelByStateCache.clear(); + ArrayList>> futures = new ArrayList<>(); + for(Block block : Registry.BLOCK) { + block.getStateDefinition().getPossibleStates().forEach((state) -> { + futures.add(CompletableFuture.supplyAsync(() -> { + return Pair.of(state, this.modelManager.getModel(BlockModelShapes.stateToModelLocation(state))); + }, Util.backgroundExecutor())); + }); + } + for(CompletableFuture> future : futures) { + Pair pair = future.join(); + this.modelByStateCache.put(pair.getFirst(), pair.getSecond()); + } + } +} 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 267e5825..224dece7 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 @@ -16,6 +16,7 @@ import net.minecraftforge.fml.loading.progress.StartupMessageManager; 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.spongepowered.asm.mixin.*; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -44,16 +45,72 @@ public abstract class ModelBakeryMixin { @Shadow @Final private Map unbakedCache; @Shadow @Mutable @Final private Map, IBakedModel> bakedCache; + + @Shadow @Final private static ItemModelGenerator ITEM_MODEL_GENERATOR; + @Shadow @Final public static BlockModel GENERATION_MARKER; + + @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 + } 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; + } + @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) { pProfiler.popPush("atlas"); @@ -67,7 +124,8 @@ 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 = new ConcurrentHashMap<>(); + this.bakedCache = Collections.emptyMap(); + this.fasterBakedCache = new ConcurrentHashMap<>(); this.modelsToBakeParallel = this.unbakedCache.entrySet().stream() .collect(Collectors.partitioningBy(entry -> { IUnbakedModel unbakedModel = entry.getValue(); diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/ModelBakeryMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/ModelBakeryMixin.java index 38c149a1..6ef210e0 100644 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/ModelBakeryMixin.java +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/ModelBakeryMixin.java @@ -18,6 +18,7 @@ import net.minecraft.util.ResourceLocation; import net.minecraft.util.Util; import net.minecraft.util.math.vector.TransformationMatrix; import net.minecraft.util.registry.Registry; +import net.minecraftforge.fml.loading.progress.StartupMessageManager; import org.embeddedt.modernfix.ModernFix; import org.embeddedt.modernfix.util.AsyncStopwatch; import org.spongepowered.asm.mixin.Final; @@ -64,6 +65,7 @@ public abstract class ModelBakeryMixin { @Inject(method = "processLoading", at = @At(value = "INVOKE", target = "Lnet/minecraft/profiler/IProfiler;popPush(Ljava/lang/String;)V", ordinal = 0)) private void preloadJsonModels(IProfiler profilerIn, int maxMipmapLevel, CallbackInfo ci) { + StartupMessageManager.mcLoaderConsumer().ifPresent(c -> c.accept("Loading models")); profilerIn.popPush("loadblockstates"); AsyncStopwatch.measureAndLogSerialRunningTime("Parallel blockstate loading", () -> { ThreadLocal containerHolder = ThreadLocal.withInitial(BlockModelDefinition.ContainerHolder::new); diff --git a/src/main/resources/modernfix.mixins.json b/src/main/resources/modernfix.mixins.json index 63f2e8bd..27ad1b49 100644 --- a/src/main/resources/modernfix.mixins.json +++ b/src/main/resources/modernfix.mixins.json @@ -54,6 +54,7 @@ "perf.blast_search_trees.MinecraftMixin", "perf.blast_search_trees.IngredientFilterInvoker", "perf.faster_baking.ModelBakeryMixin", + "perf.faster_baking.BlockModelShapesMixin", "perf.cache_model_materials.VanillaModelMixin", "perf.cache_model_materials.MultipartMixin", "bugfix.packet_leak.ClientPlayNetHandlerMixin",