From efb9f4ca2d62e6e75e2b9f4e38fb937e7728f5e2 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Mon, 1 May 2023 20:38:47 -0400 Subject: [PATCH] Fabric --- .../ModelBakerImplMixin.java | 67 ++++++++ .../dynamic_resources/ModelBakeryMixin.java | 156 +++++------------- .../dynamic_resources/ModelManagerMixin.java | 65 ++++++++ .../fabric/ModernFixPlatformHooksImpl.java | 9 - 4 files changed, 174 insertions(+), 123 deletions(-) create mode 100644 fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/perf/dynamic_resources/ModelBakerImplMixin.java create mode 100644 fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/perf/dynamic_resources/ModelManagerMixin.java diff --git a/fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/perf/dynamic_resources/ModelBakerImplMixin.java b/fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/perf/dynamic_resources/ModelBakerImplMixin.java new file mode 100644 index 00000000..e43e0eff --- /dev/null +++ b/fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/perf/dynamic_resources/ModelBakerImplMixin.java @@ -0,0 +1,67 @@ +package org.embeddedt.modernfix.fabric.mixin.perf.dynamic_resources; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.resources.model.*; +import net.minecraft.resources.ResourceLocation; +import org.embeddedt.modernfix.ModernFix; +import org.embeddedt.modernfix.duck.IExtendedModelBakery; +import org.embeddedt.modernfix.dynamicresources.DynamicBakedModelProvider; +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.CallbackInfoReturnable; + +import java.util.function.Function; + +@Mixin(ModelBakery.ModelBakerImpl.class) +public abstract class ModelBakerImplMixin { + private static final boolean debugDynamicModelLoading = Boolean.getBoolean("modernfix.debugDynamicModelLoading"); + @Shadow @Final private ModelBakery field_40571; + + @Shadow public abstract UnbakedModel getModel(ResourceLocation arg); + + @Shadow @Final private Function modelTextureGetter; + + @Inject(method = "bake", at = @At("HEAD"), cancellable = true, remap = false) + public void getOrLoadBakedModelDynamic(ResourceLocation arg, ModelState arg2, CallbackInfoReturnable cir) { + ModelBakery.BakedCacheKey key = new ModelBakery.BakedCacheKey(arg, arg2.getRotation(), arg2.isUvLocked()); + BakedModel existing = this.field_40571.bakedCache.get(key); + if (existing != null) { + cir.setReturnValue(existing); + } else { + synchronized (this) { + if(debugDynamicModelLoading) + ModernFix.LOGGER.info("Baking {}", arg); + UnbakedModel iunbakedmodel = this.getModel(arg); + IExtendedModelBakery extendedBakery = (IExtendedModelBakery)this.field_40571; + if(iunbakedmodel == extendedBakery.mfix$getUnbakedMissingModel() && debugDynamicModelLoading) + ModernFix.LOGGER.warn("Model {} not present", arg); + // TODO: make sure parent resolution doesn't re-run many times + iunbakedmodel.resolveParents(this::getModel); + BakedModel ibakedmodel = null; + if (iunbakedmodel instanceof BlockModel) { + BlockModel blockmodel = (BlockModel)iunbakedmodel; + if (blockmodel.getRootModel() == ModelBakery.GENERATION_MARKER) { + ibakedmodel = ModelBakery.ITEM_MODEL_GENERATOR.generateBlockModel(this.modelTextureGetter, blockmodel).bake((ModelBaker)this, blockmodel, this.modelTextureGetter, arg2, arg, false); + } + } + if(ibakedmodel == null) { + if(iunbakedmodel == extendedBakery.mfix$getUnbakedMissingModel()) { + // use a shared baked missing model + if(extendedBakery.getBakedMissingModel() == null) { + extendedBakery.setBakedMissingModel(iunbakedmodel.bake((ModelBaker)this, this.modelTextureGetter, arg2, arg)); + ((DynamicBakedModelProvider)this.field_40571.getBakedTopLevelModels()).setMissingModel(extendedBakery.getBakedMissingModel()); + } + ibakedmodel = extendedBakery.getBakedMissingModel(); + } else + ibakedmodel = iunbakedmodel.bake((ModelBaker)this, this.modelTextureGetter, arg2, arg); + } + this.field_40571.bakedCache.put(key, ibakedmodel); + cir.setReturnValue(ibakedmodel); + } + } + } +} 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 24ef19c5..d774d500 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 @@ -4,34 +4,21 @@ import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import com.google.common.cache.RemovalNotification; import com.google.common.collect.ImmutableList; -import com.google.gson.JsonElement; -import com.mojang.math.Transformation; +import net.minecraft.client.color.block.BlockColors; import net.minecraft.client.renderer.block.model.BlockModel; -import net.minecraft.client.renderer.block.model.ItemModelGenerator; -import net.minecraft.client.renderer.texture.AtlasSet; import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.client.renderer.texture.TextureManager; -import net.minecraft.client.resources.ClientPackSource; import net.minecraft.client.resources.model.*; -import net.minecraft.core.Registry; import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.packs.FilePackResources; -import net.minecraft.server.packs.FolderPackResources; -import net.minecraft.server.packs.PackResources; -import net.minecraft.server.packs.VanillaPackResources; -import net.minecraft.server.packs.resources.ResourceManager; -import net.minecraft.util.profiling.ProfilerFiller; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.StateDefinition; import net.minecraft.world.level.block.state.properties.Property; -import org.apache.commons.lang3.tuple.Triple; import org.embeddedt.modernfix.ModernFix; import org.embeddedt.modernfix.annotation.ClientOnlyMixin; import org.embeddedt.modernfix.duck.IExtendedModelBakery; import org.embeddedt.modernfix.dynamicresources.DynamicBakedModelProvider; import org.embeddedt.modernfix.dynamicresources.ModelBakeryHelpers; -import org.jetbrains.annotations.Nullable; +import org.objectweb.asm.Opcodes; import org.slf4j.Logger; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -44,10 +31,13 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import java.io.IOException; -import java.util.*; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; -import java.util.function.Function; +import java.util.function.BiFunction; /* high priority so that our injectors are added before other mods' */ @Mixin(value = ModelBakery.class, priority = 600) @@ -62,34 +52,27 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery { @Shadow protected abstract BlockModel loadBlockModel(ResourceLocation location) throws IOException; - @Shadow @Final protected ResourceManager resourceManager; - @Shadow private AtlasSet atlasSet; @Shadow @Final private Set loadingStack; @Shadow protected abstract void loadModel(ResourceLocation blockstateLocation) throws Exception; - @Shadow @Final private static Logger LOGGER; - @Shadow @Final @Mutable private Map bakedTopLevelModels; - @Shadow @Final @Mutable private Map, BakedModel> bakedCache; + @Shadow @Final @Mutable private Map bakedCache; - @Shadow @Final public static BlockModel GENERATION_MARKER; + @Shadow @Final @Mutable private BlockColors blockColors; + @Shadow @Final private static Logger LOGGER; + private Cache loadedBakedModels; - @Shadow @Final private static ItemModelGenerator ITEM_MODEL_GENERATOR; - - @Shadow public abstract UnbakedModel getModel(ResourceLocation modelLocation); - - @Shadow @Nullable public abstract BakedModel bake(ResourceLocation location, ModelState transform); - - private Cache, BakedModel> loadedBakedModels; private Cache loadedModels; private HashMap smallLoadingCache = new HashMap<>(); - @Redirect(method = "", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/profiling/ProfilerFiller;push(Ljava/lang/String;)V", ordinal = 0)) - private void replaceTopLevelBakedModels(ProfilerFiller filler, String s) { + + @Redirect(method = "", at = @At(value = "FIELD", opcode = Opcodes.PUTFIELD, target = "Lnet/minecraft/client/resources/model/ModelBakery;blockColors:Lnet/minecraft/client/color/block/BlockColors;")) + private void replaceTopLevelBakedModels(ModelBakery bakery, BlockColors val) { + this.blockColors = val; this.loadedBakedModels = CacheBuilder.newBuilder() .expireAfterAccess(3, TimeUnit.MINUTES) .maximumSize(1000) @@ -107,7 +90,6 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery { this.bakedCache = loadedBakedModels.asMap(); this.unbakedCache = loadedModels.asMap(); this.bakedTopLevelModels = new DynamicBakedModelProvider((ModelBakery)(Object)this, bakedCache); - filler.push(s); } private void onModelRemoved(RemovalNotification notification) { @@ -121,7 +103,7 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery { if(k instanceof ResourceLocation) { rl = (ResourceLocation)k; } else { - rl = ((Triple)k).getLeft(); + rl = ((ModelBakery.BakedCacheKey)k).id(); baked = true; } ModernFix.LOGGER.warn("Evicted {} model {}", baked ? "baked" : "unbaked", rl); @@ -142,61 +124,30 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery { /** * @author embeddedt - * @reason don't actually load the model. instead, keep track of if we need to load a blockstate or a model, - * and save the info into the two lists + * @reason don't actually load the model. */ @Inject(method = "loadTopLevel", at = @At("HEAD"), cancellable = true) private void addTopLevelFile(ModelResourceLocation location, CallbackInfo ci) { ci.cancel(); - if(Objects.equals(location.getVariant(), "inventory")) { - modelFiles.add(new ResourceLocation(location.getNamespace(), "item/" + location.getPath())); - } else { - blockStateFiles.add(new ResourceLocation(location.getNamespace(), location.getPath())); - } - } - - @Redirect(method = "", at = @At(value = "INVOKE", target = "Ljava/util/Set;addAll(Ljava/util/Collection;)Z", ordinal = 0)) - private boolean gatherModelTextures(Set materialSet, Collection collection) { - materialSet.addAll(collection); - gatherModelMaterials(materialSet); - return true; } @Redirect(method = "", at = @At(value = "INVOKE", target = "Ljava/util/Map;forEach(Ljava/util/function/BiConsumer;)V", ordinal = 0)) private void fetchStaticDefinitions(Map> map, BiConsumer> func) { - map.forEach((loc, def) -> blockStateFiles.add(loc)); + /* no-op */ } @Redirect(method = "", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/StateDefinition;getPossibleStates()Lcom/google/common/collect/ImmutableList;", ordinal = 0)) private ImmutableList fetchBlocks(StateDefinition def) { - blockStateFiles.add(Registry.BLOCK.getKey(def.any().getBlock())); + /* no-op */ return ImmutableList.of(); } - private boolean trustedResourcePack(PackResources pack) { - return pack instanceof VanillaPackResources || - pack instanceof ClientPackSource || - pack instanceof FolderPackResources || - pack instanceof FilePackResources; - } + private BiFunction textureGetter; - /** - * Load all blockstate JSONs and model files, collect textures. - */ - private void gatherModelMaterials(Set materialSet) { - Function modelDeserializer = model -> BlockModel.GSON.fromJson(model, BlockModel.class); - ModelBakeryHelpers.gatherModelMaterials(this.resourceManager, this::trustedResourcePack, materialSet, blockStateFiles, - modelFiles, missingModel, modelDeserializer, this::getModel); - loadedModels.invalidateAll(); - loadedModels.put(MISSING_MODEL_LOCATION, missingModel); - } - - @Inject(method = "uploadTextures", at = @At(value = "FIELD", target = "Lnet/minecraft/client/resources/model/ModelBakery;topLevelModels:Ljava/util/Map;", ordinal = 0), cancellable = true) - private void skipBake(TextureManager resourceManager, ProfilerFiller profiler, CallbackInfoReturnable cir) { - profiler.pop(); - // ensure missing model is a permanent override - this.bakedTopLevelModels.put(MISSING_MODEL_LOCATION, this.bake(MISSING_MODEL_LOCATION, BlockModelRotation.X0_Y0)); - cir.setReturnValue(atlasSet); + @Inject(method = "bakeModels", at = @At("HEAD"), cancellable = true) + private void skipBake(BiFunction getter, CallbackInfo ci) { + textureGetter = getter; + ci.cancel(); } /** @@ -277,7 +228,14 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery { } @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) { - return ModelBakeryHelpers.getBlockStatesForMRL(stateDefinition, (ModelResourceLocation)location); + return ModelBakeryHelpers.getBlockStatesForMRL(stateDefinition, (ModelResourceLocation)location); + } + + @Override + public BakedModel bakeDefault(ResourceLocation modelLocation) { + ModelBakery self = (ModelBakery) (Object) this; + ModelBaker theBaker = self.new ModelBakerImpl(textureGetter, modelLocation); + return theBaker.bake(modelLocation, BlockModelRotation.X0_Y0); } @Override @@ -287,45 +245,15 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery { private BakedModel bakedMissingModel = null; - @Inject(method = "bake", at = @At("HEAD"), cancellable = true) - public void getOrLoadBakedModelDynamic(ResourceLocation arg, ModelState arg2, CallbackInfoReturnable cir) { - Function textureGetter = mat -> this.atlasSet.getSprite(mat); - Triple triple = Triple.of(arg, arg2.getRotation(), arg2.isUvLocked()); - BakedModel existing = this.bakedCache.get(triple); - if (existing != null) { - cir.setReturnValue(existing); - } else if (this.atlasSet == null) { - throw new IllegalStateException("bake called too early"); - } else { - synchronized (this) { - if(debugDynamicModelLoading) - LOGGER.info("Baking {}", arg); - UnbakedModel iunbakedmodel = this.getModel(arg); - iunbakedmodel.getMaterials(this::getModel, new HashSet<>()); - if(iunbakedmodel == missingModel && debugDynamicModelLoading) - LOGGER.warn("Model {} not present", arg); - BakedModel ibakedmodel = null; - if (iunbakedmodel instanceof BlockModel) { - BlockModel blockmodel = (BlockModel)iunbakedmodel; - if (blockmodel.getRootModel() == GENERATION_MARKER) { - ibakedmodel = ITEM_MODEL_GENERATOR.generateBlockModel(textureGetter, blockmodel).bake((ModelBakery)(Object)this, blockmodel, this.atlasSet::getSprite, arg2, arg, false); - } - } - if(ibakedmodel == null) { - if(iunbakedmodel == missingModel) { - // use a shared baked missing model - if(bakedMissingModel == null) { - bakedMissingModel = iunbakedmodel.bake((ModelBakery) (Object) this, textureGetter, arg2, arg); - ((DynamicBakedModelProvider)this.bakedTopLevelModels).setMissingModel(bakedMissingModel); - } - ibakedmodel = bakedMissingModel; - } else - ibakedmodel = iunbakedmodel.bake((ModelBakery) (Object) this, textureGetter, arg2, arg); - } - // TODO event - this.bakedCache.put(triple, ibakedmodel); - cir.setReturnValue(ibakedmodel); - } - } + public void setBakedMissingModel(BakedModel m) { + bakedMissingModel = m; + } + + public BakedModel getBakedMissingModel() { + return bakedMissingModel; + } + + public UnbakedModel mfix$getUnbakedMissingModel() { + return missingModel; } } diff --git a/fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/perf/dynamic_resources/ModelManagerMixin.java b/fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/perf/dynamic_resources/ModelManagerMixin.java new file mode 100644 index 00000000..ce620eed --- /dev/null +++ b/fabric/src/main/java/org/embeddedt/modernfix/fabric/mixin/perf/dynamic_resources/ModelManagerMixin.java @@ -0,0 +1,65 @@ +package org.embeddedt.modernfix.fabric.mixin.perf.dynamic_resources; + +import com.google.common.collect.ImmutableList; +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.ModelBakery; +import net.minecraft.client.resources.model.ModelManager; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.util.GsonHelper; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import org.embeddedt.modernfix.ModernFix; +import org.embeddedt.modernfix.util.LambdaMap; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.stream.Collectors; + +@Mixin(ModelManager.class) +public class ModelManagerMixin { + @Redirect(method = "reload", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/ModelManager;loadBlockModels(Lnet/minecraft/server/packs/resources/ResourceManager;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;")) + private CompletableFuture> deferBlockModelLoad(ResourceManager manager, Executor executor) { + return CompletableFuture.completedFuture(new LambdaMap<>(location -> loadSingleBlockModel(manager, location))); + } + + @Redirect(method = "reload", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/ModelManager;loadBlockStates(Lnet/minecraft/server/packs/resources/ResourceManager;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;")) + private CompletableFuture>> deferBlockStateLoad(ResourceManager manager, Executor executor) { + return CompletableFuture.completedFuture(new LambdaMap<>(location -> loadSingleBlockState(manager, location))); + } + + @Redirect(method = "loadModels", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/StateDefinition;getPossibleStates()Lcom/google/common/collect/ImmutableList;")) + private ImmutableList skipCollection(StateDefinition definition) { + return ImmutableList.of(); + } + + private BlockModel loadSingleBlockModel(ResourceManager manager, ResourceLocation location) { + return manager.getResource(location).map(resource -> { + try { + return BlockModel.fromStream(resource.openAsReader()); + } catch(IOException e) { + ModernFix.LOGGER.error("Couldn't load model", e); + return null; + } + }).orElse(null); + } + + private List loadSingleBlockState(ResourceManager manager, ResourceLocation location) { + return manager.getResourceStack(location).stream().map(resource -> { + try { + return new ModelBakery.LoadedJson(resource.sourcePackId(), GsonHelper.parse(resource.openAsReader())); + } catch(IOException e) { + ModernFix.LOGGER.error("Couldn't load blockstate", e); + return null; + } + }).filter(Objects::nonNull).collect(Collectors.toList()); + } +} diff --git a/fabric/src/main/java/org/embeddedt/modernfix/platform/fabric/ModernFixPlatformHooksImpl.java b/fabric/src/main/java/org/embeddedt/modernfix/platform/fabric/ModernFixPlatformHooksImpl.java index cf174819..01b3b444 100644 --- a/fabric/src/main/java/org/embeddedt/modernfix/platform/fabric/ModernFixPlatformHooksImpl.java +++ b/fabric/src/main/java/org/embeddedt/modernfix/platform/fabric/ModernFixPlatformHooksImpl.java @@ -51,15 +51,6 @@ public class ModernFixPlatformHooksImpl { return true; } - public static TextureAtlasSprite loadTextureAtlasSprite(TextureAtlas atlasTexture, - ResourceManager resourceManager, TextureAtlasSprite.Info textureInfo, - Resource resource, - int atlasWidth, int atlasHeight, - int spriteX, int spriteY, int mipmapLevel, - NativeImage image) { - return new TextureAtlasSprite(atlasTexture, textureInfo, mipmapLevel, atlasWidth, atlasHeight, spriteX, spriteY, image); - } - public static Path getGameDirectory() { return FabricLoader.getInstance().getGameDir(); }