From 26d76de7ef02b509b3a5a259c301cf83308085b8 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Sat, 8 Apr 2023 17:01:16 -0400 Subject: [PATCH] Dynamic model loading --- build.gradle | 1 + .../modernfix/blockstate/FakeStateMap.java | 181 +++++++++++ .../classloading/ModernFixResourceFinder.java | 40 ++- .../core/config/ModernFixEarlyConfig.java | 7 +- .../DynamicBakedModelProvider.java | 119 +++++++ .../DynamicModelProvider.java | 41 +++ .../dynamicresources/ModelLocationCache.java | 37 +++ .../BlockModelShaperMixin.java | 49 +++ .../ItemModelShaperMixin.java | 53 ++++ .../dynamic_resources/ModelBakeryMixin.java | 297 ++++++++++++++++++ .../faster_baking/BlockModelShapesMixin.java | 46 --- .../perf/faster_baking/ModelBakeryMixin.java | 161 ---------- .../perf/faster_baking/ModelManagerMixin.java | 40 --- .../BooleanPropertyMixin.java | 2 +- .../OBJLoaderMixin.java | 2 +- .../PropertyMixin.java | 2 +- .../SelectorMixin.java | 3 +- .../TransformationMatrixMixin.java | 2 +- .../BlockModelShaperMixin.java | 26 -- .../ModelBakeryMixin.java | 114 ------- .../multipart/MultipartMixin.java | 17 - .../multipart/VariantListMixin.java | 32 -- .../StateDefinitionMixin.java | 29 ++ .../modernfix/models/LazyBakedModel.java | 138 -------- .../modernfix/textures/StbStitcher.java | 10 +- .../embeddedt/modernfix/util/FileUtil.java | 26 +- src/main/resources/modernfix.mixins.json | 23 +- 27 files changed, 879 insertions(+), 619 deletions(-) create mode 100644 src/main/java/org/embeddedt/modernfix/blockstate/FakeStateMap.java create mode 100644 src/main/java/org/embeddedt/modernfix/dynamicresources/DynamicBakedModelProvider.java create mode 100644 src/main/java/org/embeddedt/modernfix/dynamicresources/DynamicModelProvider.java create mode 100644 src/main/java/org/embeddedt/modernfix/dynamicresources/ModelLocationCache.java create mode 100644 src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/BlockModelShaperMixin.java create mode 100644 src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/ItemModelShaperMixin.java create mode 100644 src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/ModelBakeryMixin.java delete mode 100644 src/main/java/org/embeddedt/modernfix/mixin/perf/faster_baking/BlockModelShapesMixin.java delete mode 100644 src/main/java/org/embeddedt/modernfix/mixin/perf/faster_baking/ModelBakeryMixin.java delete mode 100644 src/main/java/org/embeddedt/modernfix/mixin/perf/faster_baking/ModelManagerMixin.java rename src/main/java/org/embeddedt/modernfix/mixin/perf/{parallelize_model_loading => model_optimizations}/BooleanPropertyMixin.java (91%) rename src/main/java/org/embeddedt/modernfix/mixin/perf/{parallelize_model_loading => model_optimizations}/OBJLoaderMixin.java (95%) rename src/main/java/org/embeddedt/modernfix/mixin/perf/{parallelize_model_loading => model_optimizations}/PropertyMixin.java (95%) rename src/main/java/org/embeddedt/modernfix/mixin/perf/{parallelize_model_loading => model_optimizations}/SelectorMixin.java (90%) rename src/main/java/org/embeddedt/modernfix/mixin/perf/{parallelize_model_loading => model_optimizations}/TransformationMatrixMixin.java (91%) delete mode 100644 src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/BlockModelShaperMixin.java delete mode 100644 src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/ModelBakeryMixin.java delete mode 100644 src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/multipart/MultipartMixin.java delete mode 100644 src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/multipart/VariantListMixin.java create mode 100644 src/main/java/org/embeddedt/modernfix/mixin/perf/state_definition_construct/StateDefinitionMixin.java delete mode 100644 src/main/java/org/embeddedt/modernfix/models/LazyBakedModel.java diff --git a/build.gradle b/build.gradle index ae85e2ce..31dd054f 100644 --- a/build.gradle +++ b/build.gradle @@ -95,6 +95,7 @@ dependencies { modCompileOnly("curse.maven:jepb-437558:3172880") modCompileOnly("curse.maven:babel-436964:3196072") modCompileOnly("curse.maven:twforest-227639:3575220") + modRuntimeOnly("curse.maven:ferritecore-429235:4074330") } tasks.withType(JavaCompile) { diff --git a/src/main/java/org/embeddedt/modernfix/blockstate/FakeStateMap.java b/src/main/java/org/embeddedt/modernfix/blockstate/FakeStateMap.java new file mode 100644 index 00000000..42194e5d --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/blockstate/FakeStateMap.java @@ -0,0 +1,181 @@ +package org.embeddedt.modernfix.blockstate; + +import com.google.common.collect.ImmutableSet; +import net.minecraft.world.level.block.state.properties.Property; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +/** + * Fake "map" implementation used to hold the states. + * + * Intentionally throws on methods that would be inefficient so that we know + * if an incompatible mod is present. + */ +public class FakeStateMap implements Map, Comparable>, S> { + private final Map, Comparable>[] keys; + private final Object[] values; + private int usedSlots; + public FakeStateMap(int numStates) { + this.keys = new Map[numStates]; + this.values = new Object[numStates]; + this.usedSlots = 0; + } + + @Override + public int size() { + return usedSlots; + } + + @Override + public boolean isEmpty() { + return size() == 0; + } + + @Override + public boolean containsKey(Object o) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean containsValue(Object o) { + throw new UnsupportedOperationException(); + } + + @Override + public S get(Object o) { + throw new UnsupportedOperationException(); + } + + @Nullable + @Override + public S put(Map, Comparable> propertyComparableMap, S s) { + keys[usedSlots] = propertyComparableMap; + values[usedSlots] = s; + usedSlots++; + return null; + } + + @Override + public S remove(Object o) { + throw new UnsupportedOperationException(); + } + + @Override + public void putAll(@NotNull Map, Comparable>, ? extends S> map) { + for(Entry, Comparable>, ? extends S> entry : map.entrySet()) { + this.put(entry.getKey(), entry.getValue()); + } + } + + @Override + public void clear() { + for(int i = 0; i < this.keys.length; i++) { + this.keys[i] = null; + this.values[i] = null; + } + this.usedSlots = 0; + } + + @NotNull + @Override + public Set, Comparable>> keySet() { + throw new UnsupportedOperationException(); + } + + @NotNull + @Override + public Collection values() { + throw new UnsupportedOperationException(); + } + + @NotNull + @Override + public Set, Comparable>, S>> entrySet() { + return new Set, Comparable>, S>>() { + @Override + public int size() { + return usedSlots; + } + + @Override + public boolean isEmpty() { + return FakeStateMap.this.isEmpty(); + } + + @Override + public boolean contains(Object o) { + throw new UnsupportedOperationException(); + } + + @NotNull + @Override + public Iterator, Comparable>, S>> iterator() { + return new Iterator, Comparable>, S>>() { + int currentIdx = 0; + @Override + public boolean hasNext() { + return currentIdx < usedSlots; + } + + @Override + public Entry, Comparable>, S> next() { + if(currentIdx >= usedSlots) + throw new IndexOutOfBoundsException(); + Entry, Comparable>, S> entry = new AbstractMap.SimpleImmutableEntry<>(keys[currentIdx], (S)values[currentIdx]); + currentIdx++; + return entry; + } + }; + } + + @NotNull + @Override + public Object[] toArray() { + throw new UnsupportedOperationException(); + } + + @NotNull + @Override + public T[] toArray(@NotNull T[] ts) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean add(Entry, Comparable>, S> mapSEntry) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean remove(Object o) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean containsAll(@NotNull Collection collection) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean addAll(@NotNull Collection, Comparable>, S>> collection) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean retainAll(@NotNull Collection collection) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean removeAll(@NotNull Collection collection) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + }; + } +} diff --git a/src/main/java/org/embeddedt/modernfix/classloading/ModernFixResourceFinder.java b/src/main/java/org/embeddedt/modernfix/classloading/ModernFixResourceFinder.java index 432ede05..c11dcb56 100644 --- a/src/main/java/org/embeddedt/modernfix/classloading/ModernFixResourceFinder.java +++ b/src/main/java/org/embeddedt/modernfix/classloading/ModernFixResourceFinder.java @@ -1,5 +1,8 @@ package org.embeddedt.modernfix.classloading; +import com.google.common.collect.Interner; +import com.google.common.collect.Interners; +import com.google.common.collect.Iterators; import com.google.common.collect.Lists; import cpw.mods.modlauncher.api.LamdbaExceptionUtils; import net.minecraftforge.fml.loading.FMLLoader; @@ -14,6 +17,7 @@ import org.apache.logging.log4j.Logger; import java.io.IOException; import java.lang.reflect.Field; +import java.net.MalformedURLException; import java.net.URL; import java.nio.file.FileSystem; import java.nio.file.Files; @@ -23,7 +27,7 @@ import java.util.regex.Pattern; import java.util.stream.Stream; public class ModernFixResourceFinder { - private static HashMap> urlsForClass = null; + private static HashMap>> urlsForClass = null; private static final Class MINECRAFT_LOCATOR; private static Field explodedDirModsField = null; private static final Logger LOGGER = LogManager.getLogger("ModernFixResourceFinder"); @@ -35,6 +39,8 @@ public class ModernFixResourceFinder { throw new RuntimeException(e); } } + private static final Interner PATH_INTERNER = Interners.newStrongInterner(); + public static synchronized void init() throws ReflectiveOperationException { urlsForClass = new HashMap<>(); //LOGGER.info("Start building list of class locations..."); @@ -50,22 +56,20 @@ public class ModernFixResourceFinder { .map(root::relativize) .forEach(path -> { String strPath = path.toString(); - URL url = (URL)LamdbaExceptionUtils.uncheck(() -> { - return new URL("modjar://" + fileInfo.getMods().get(0).getModId() + "/" + strPath); - }); - List urlList = urlsForClass.get(strPath); + Pair pathPair = Pair.of(fileInfo.getMods().get(0).getModId(), PATH_INTERNER.intern(strPath)); + List> urlList = urlsForClass.get(strPath); if(urlList != null) { if(urlList.size() > 1) - urlList.add(url); + urlList.add(pathPair); else { /* Convert singleton to real list */ - ArrayList newList = new ArrayList<>(urlList); - newList.add(url); + ArrayList> newList = new ArrayList<>(urlList); + newList.add(pathPair); urlsForClass.put(strPath, newList); } } else { /* Use a singleton list initially to keep memory usage down */ - urlsForClass.put(strPath, Collections.singletonList(url)); + urlsForClass.put(strPath, Collections.singletonList(pathPair)); } }); } catch(IOException e) { @@ -73,9 +77,9 @@ public class ModernFixResourceFinder { } } } - for(List list : urlsForClass.values()) { + for(List> list : urlsForClass.values()) { if(list instanceof ArrayList) - ((ArrayList)list).trimToSize(); + ((ArrayList>)list).trimToSize(); } //LOGGER.info("Finish building"); } @@ -106,10 +110,16 @@ public class ModernFixResourceFinder { public static Enumeration findAllURLsForResource(String input) { input = SLASH_REPLACER.matcher(input).replaceAll("/"); - List urlList = urlsForClass.get(input); - if(urlList != null) - return Collections.enumeration(urlList); - else { + List> urlList = urlsForClass.get(input); + if(urlList != null) { + return Iterators.asEnumeration(urlList.stream().map(pair -> { + try { + return new URL("modjar://" + pair.getLeft() + "/" + pair.getRight()); + } catch(MalformedURLException e) { + throw new RuntimeException(e); + } + }).iterator()); + } else { return Collections.emptyEnumeration(); } } 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 fc2c23d7..9d5625e9 100644 --- a/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java +++ b/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java @@ -36,8 +36,10 @@ public class ModernFixEarlyConfig { this.addMixinRule("perf.boost_worker_count", true); this.addMixinRule("perf.skip_first_datapack_reload", true); this.addMixinRule("perf.reuse_datapacks", true); - this.addMixinRule("perf.parallelize_model_loading", true); - this.addMixinRule("perf.parallelize_model_loading.multipart", false); + this.addMixinRule("perf.model_optimizations", true); + this.addMixinRule("perf.dynamic_resources", false); + /* Use a simpler ArrayMap if FerriteCore is using the map intelligently anyway */ + this.addMixinRule("perf.state_definition_construct", modPresent("ferritecore")); this.addMixinRule("perf.cache_strongholds", true); this.addMixinRule("perf.cache_upgraded_structures", true); this.addMixinRule("bugfix.concurrency", true); @@ -57,7 +59,6 @@ public class ModernFixEarlyConfig { this.addMixinRule("perf.flatten_model_predicates", true); this.addMixinRule("perf.deduplicate_location", false); this.addMixinRule("perf.cache_blockstate_cache_arrays", true); - this.addMixinRule("perf.faster_baking", true); this.addMixinRule("perf.cache_model_materials", true); this.addMixinRule("perf.datapack_reload_exceptions", true); this.addMixinRule("perf.async_locator", true); diff --git a/src/main/java/org/embeddedt/modernfix/dynamicresources/DynamicBakedModelProvider.java b/src/main/java/org/embeddedt/modernfix/dynamicresources/DynamicBakedModelProvider.java new file mode 100644 index 00000000..30dea59d --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/dynamicresources/DynamicBakedModelProvider.java @@ -0,0 +1,119 @@ +package org.embeddedt.modernfix.dynamicresources; + +import com.mojang.math.Transformation; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.client.resources.model.BlockModelRotation; +import net.minecraft.client.resources.model.ModelBakery; +import net.minecraft.resources.ResourceLocation; +import org.apache.commons.lang3.tuple.Triple; +import org.jetbrains.annotations.NotNull; + +import javax.annotation.Nullable; +import java.util.AbstractMap; +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import java.util.function.BiFunction; +import java.util.stream.Collectors; + +public class DynamicBakedModelProvider implements Map { + private final ModelBakery bakery; + private final Map, BakedModel> bakedCache; + private final Map permanentOverrides; + + public DynamicBakedModelProvider(ModelBakery bakery, Map, BakedModel> cache) { + this.bakery = bakery; + this.bakedCache = cache; + this.permanentOverrides = new Object2ObjectOpenHashMap<>(); + } + private static Triple vanillaKey(Object o) { + return Triple.of((ResourceLocation)o, BlockModelRotation.X0_Y0.getRotation(), false); + } + @Override + public int size() { + return bakedCache.size(); + } + + @Override + public boolean isEmpty() { + return bakedCache.isEmpty(); + } + + @Override + public boolean containsKey(Object o) { + return permanentOverrides.containsKey(o) || bakedCache.containsKey(vanillaKey(o)); + } + + @Override + public boolean containsValue(Object o) { + return permanentOverrides.containsValue(o) || bakedCache.containsValue(o); + } + + @Override + public BakedModel get(Object o) { + BakedModel model = permanentOverrides.get(o); + return model != null ? model : bakery.bake((ResourceLocation)o, BlockModelRotation.X0_Y0); + } + + @Nullable + @Override + public BakedModel put(ResourceLocation resourceLocation, BakedModel bakedModel) { + BakedModel m = permanentOverrides.put(resourceLocation, bakedModel); + if(m != null) + return m; + else + return bakedCache.get(vanillaKey(resourceLocation)); + } + + @Override + public BakedModel remove(Object o) { + BakedModel m = permanentOverrides.remove(o); + if(m != null) + return m; + return bakedCache.remove(vanillaKey(o)); + } + + @Override + public void putAll(@NotNull Map map) { + permanentOverrides.putAll(map); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + @NotNull + @Override + public Set keySet() { + return bakedCache.keySet().stream().map(Triple::getLeft).collect(Collectors.toSet()); + } + + @NotNull + @Override + public Collection values() { + return bakedCache.values(); + } + + @NotNull + @Override + public Set> entrySet() { + return bakedCache.entrySet().stream().map(entry -> new AbstractMap.SimpleImmutableEntry<>(entry.getKey().getLeft(), entry.getValue())).collect(Collectors.toSet()); + } + + @Override + public void replaceAll(BiFunction function) { + Set overridenLocations = permanentOverrides.keySet(); + permanentOverrides.replaceAll(function); + boolean uvLock = BlockModelRotation.X0_Y0.isUvLocked(); + Transformation rotation = BlockModelRotation.X0_Y0.getRotation(); + bakedCache.replaceAll((loc, oldModel) -> { + if(loc.getMiddle() != rotation || loc.getRight() != uvLock || overridenLocations.contains(loc.getLeft())) + return oldModel; + else + return function.apply(loc.getLeft(), oldModel); + }); + } +} diff --git a/src/main/java/org/embeddedt/modernfix/dynamicresources/DynamicModelProvider.java b/src/main/java/org/embeddedt/modernfix/dynamicresources/DynamicModelProvider.java new file mode 100644 index 00000000..67bd354c --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/dynamicresources/DynamicModelProvider.java @@ -0,0 +1,41 @@ +package org.embeddedt.modernfix.dynamicresources; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.resources.ResourceLocation; + +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +/** + * Handles loading models dynamically, rather than at startup time. + */ +public class DynamicModelProvider { + private final Map internalModels; + private final Cache> loadedModels = + CacheBuilder.newBuilder() + .expireAfterAccess(3, TimeUnit.MINUTES) + .maximumSize(1000) + .concurrencyLevel(8) + .softValues() + .build(); + + public DynamicModelProvider(Map initialModels) { + this.internalModels = initialModels; + } + + public UnbakedModel getModel(ResourceLocation location) { + try { + return loadedModels.get(location, () -> Optional.ofNullable(loadModel(location))).orElse(null); + } catch(ExecutionException e) { + throw new RuntimeException(e.getCause()); + } + } + + private UnbakedModel loadModel(ResourceLocation location) { + return null; /* TODO :) */ + } +} diff --git a/src/main/java/org/embeddedt/modernfix/dynamicresources/ModelLocationCache.java b/src/main/java/org/embeddedt/modernfix/dynamicresources/ModelLocationCache.java new file mode 100644 index 00000000..031fdc0c --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/dynamicresources/ModelLocationCache.java @@ -0,0 +1,37 @@ +package org.embeddedt.modernfix.dynamicresources; + +import com.mojang.datafixers.util.Pair; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import net.minecraft.Util; +import net.minecraft.client.renderer.block.BlockModelShaper; +import net.minecraft.client.resources.model.ModelResourceLocation; +import net.minecraft.core.Registry; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; + +import java.util.ArrayList; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +public class ModelLocationCache { + private static final Map locationCache = new Object2ObjectOpenHashMap<>(); + public static void rebuildLocationCache() { + locationCache.clear(); + ArrayList>> futures = new ArrayList<>(); + for(Block block : Registry.BLOCK) { + block.getStateDefinition().getPossibleStates().forEach((state) -> { + futures.add(CompletableFuture.supplyAsync(() -> { + return Pair.of(state, BlockModelShaper.stateToModelLocation(state)); + }, Util.backgroundExecutor())); + }); + } + for(CompletableFuture> future : futures) { + Pair pair = future.join(); + locationCache.put(pair.getFirst(), pair.getSecond()); + } + } + + public static ModelResourceLocation get(BlockState state) { + return locationCache.get(state); + } +} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/BlockModelShaperMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/BlockModelShaperMixin.java new file mode 100644 index 00000000..91d45970 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/BlockModelShaperMixin.java @@ -0,0 +1,49 @@ +package org.embeddedt.modernfix.mixin.perf.dynamic_resources; + +import com.mojang.datafixers.util.Pair; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import net.minecraft.Util; +import net.minecraft.client.renderer.block.BlockModelShaper; +import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.client.resources.model.ModelManager; +import net.minecraft.client.resources.model.ModelResourceLocation; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.Property; +import org.embeddedt.modernfix.dynamicresources.ModelLocationCache; +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 org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; + +@Mixin(BlockModelShaper.class) +public class BlockModelShaperMixin { + @Shadow @Final private ModelManager modelManager; + + /** + * @author embeddedt + * @reason no need to rebuild model cache, and location cache is done elsewhere + */ + @Overwrite + public void rebuildCache() { + } + + @Overwrite + public BakedModel getBlockModel(BlockState state) { + BakedModel model = modelManager.getModel(ModelLocationCache.get(state)); + if (model == null) { + model = modelManager.getMissingModel(); + } + return model; + } +} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/ItemModelShaperMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/ItemModelShaperMixin.java new file mode 100644 index 00000000..0ca70841 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/ItemModelShaperMixin.java @@ -0,0 +1,53 @@ +package org.embeddedt.modernfix.mixin.perf.dynamic_resources; + +import net.minecraft.client.renderer.ItemModelShaper; +import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.client.resources.model.ModelManager; +import net.minecraft.client.resources.model.ModelResourceLocation; +import net.minecraft.world.item.Item; +import net.minecraftforge.client.ItemModelMesherForge; +import net.minecraftforge.registries.IRegistryDelegate; +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.Map; + +@Mixin(ItemModelMesherForge.class) +public abstract class ItemModelShaperMixin extends ItemModelShaper { + @Shadow @Final private Map, ModelResourceLocation> locations; + + public ItemModelShaperMixin(ModelManager arg) { + super(arg); + } + + /** + * @reason Get the stored location for that item and meta, and get the model + * from that location from the model manager. + **/ + @Overwrite + @Override + public BakedModel getItemModel(Item item) { + ModelResourceLocation map = locations.get(item.delegate); + return map == null ? null : getModelManager().getModel(map); + } + + /** + * @reason Don't get all models during init (with dynamic loading, that would + * generate them all). Just store location instead. + **/ + @Overwrite + @Override + public void register(Item item, ModelResourceLocation location) { + locations.put(item.delegate, location); + } + + /** + * @reason Disable cache rebuilding (with dynamic loading, that would generate + * all models). + **/ + @Overwrite + @Override + public void rebuildCache() {} +} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/ModelBakeryMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/ModelBakeryMixin.java new file mode 100644 index 00000000..015408f9 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/dynamic_resources/ModelBakeryMixin.java @@ -0,0 +1,297 @@ +package org.embeddedt.modernfix.mixin.perf.dynamic_resources; + +import com.google.common.base.Stopwatch; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.RemovalNotification; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import com.mojang.datafixers.util.Pair; +import com.mojang.math.Transformation; +import net.minecraft.Util; +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.TextureAtlas; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.renderer.texture.TextureManager; +import net.minecraft.client.resources.model.*; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.Resource; +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.minecraftforge.client.ForgeHooksClient; +import net.minecraftforge.client.model.ModelLoaderRegistry; +import org.apache.commons.lang3.tuple.Triple; +import org.apache.logging.log4j.Logger; +import org.embeddedt.modernfix.ModernFix; +import org.embeddedt.modernfix.dynamicresources.DynamicBakedModelProvider; +import org.embeddedt.modernfix.dynamicresources.ModelLocationCache; +import org.spongepowered.asm.mixin.*; +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.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import javax.annotation.Nullable; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.stream.Collectors; + +@Mixin(ModelBakery.class) +public abstract class ModelBakeryMixin { + + private static final boolean debugDynamicModelLoading = Boolean.getBoolean("modernfix.debugDynamicModelLoading"); + + @Shadow @Final @Mutable public Map unbakedCache; + + @Shadow @Final public static ModelResourceLocation MISSING_MODEL_LOCATION; + + @Shadow protected abstract BlockModel loadBlockModel(ResourceLocation location) throws IOException; + + @Shadow @Final protected static Set UNREFERENCED_TEXTURES; + @Shadow private Map> atlasPreparations; + @Shadow @Final protected ResourceManager resourceManager; + @Shadow @Nullable 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 public static BlockModel GENERATION_MARKER; + + @Shadow @Final private static ItemModelGenerator ITEM_MODEL_GENERATOR; + + @Shadow @Final public static BlockModel BLOCK_ENTITY_MARKER; + + private Cache, BakedModel> loadedBakedModels; + private Cache loadedModels; + + + @Inject(method = "(Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/client/color/block/BlockColors;Z)V", at = @At("RETURN")) + private void replaceTopLevelBakedModels(ResourceManager manager, BlockColors colors, boolean vanillaBakery, CallbackInfo ci) { + this.loadedBakedModels = CacheBuilder.newBuilder() + .expireAfterAccess(3, TimeUnit.MINUTES) + .maximumSize(1000) + .concurrencyLevel(8) + .removalListener(this::onModelRemoved) + .softValues() + .build(); + this.loadedModels = CacheBuilder.newBuilder() + .expireAfterAccess(3, TimeUnit.MINUTES) + .maximumSize(1000) + .concurrencyLevel(8) + .removalListener(this::onModelRemoved) + .softValues() + .build(); + this.bakedCache = loadedBakedModels.asMap(); + this.unbakedCache = loadedModels.asMap(); + this.bakedTopLevelModels = new DynamicBakedModelProvider((ModelBakery)(Object)this, bakedCache); + } + + private void onModelRemoved(RemovalNotification notification) { + if(!debugDynamicModelLoading) + return; + Object k = notification.getKey(); + if(k == null) + return; + ResourceLocation rl; + boolean baked = false; + if(k instanceof ResourceLocation) { + rl = (ResourceLocation)k; + } else { + rl = ((Triple)k).getLeft(); + baked = true; + } + ModernFix.LOGGER.warn("Evicted {} model {}", baked ? "baked" : "unbaked", rl); + } + + private UnbakedModel missingModel; + + private static boolean firstReload = true; + + /** + * @author embeddedt + * @reason don't load any models initially, just set up initial data structures + */ + @Overwrite + protected void processLoading(ProfilerFiller arg, int maxMipLevels) { + ModelLoaderRegistry.onModelLoadingStart(); + if(firstReload) { + ModelLocationCache.rebuildLocationCache(); + firstReload = false; + } + try { + this.missingModel = this.loadBlockModel(MISSING_MODEL_LOCATION); + } catch (IOException var10) { + ModernFix.LOGGER.error("Error loading missing model, should never happen :(", var10); + throw new RuntimeException(var10); + } + // Gather model materials + Set initialMaterials = new HashSet<>(UNREFERENCED_TEXTURES); + gatherModelMaterials(initialMaterials); + ForgeHooksClient.gatherFluidTextures(initialMaterials); + Map> map = initialMaterials.stream().collect(Collectors.groupingBy(Material::atlasLocation)); + this.atlasPreparations = Maps.newHashMap(); + for(Map.Entry> entry : map.entrySet()) { + TextureAtlas atlas = new TextureAtlas(entry.getKey()); + TextureAtlas.Preparations atlastexture$sheetdata = atlas.prepareToStitch(this.resourceManager, entry.getValue().stream().map(Material::texture), arg, maxMipLevels); + this.atlasPreparations.put(entry.getKey(), Pair.of(atlas, atlastexture$sheetdata)); + } + } + + /** + * Scan the models folder and try to load, parse, and get materials from as many models as possible. + */ + private void gatherModelMaterials(Set materialSet) { + Stopwatch stopwatch = Stopwatch.createStarted(); + Collection allModels = this.resourceManager.listResources("models", path -> path.endsWith(".json")); + List>> modelBytes = new ArrayList<>(); + for(ResourceLocation fileLocation : allModels) { + modelBytes.add(CompletableFuture.supplyAsync(() -> { + try(Resource resource = this.resourceManager.getResource(fileLocation)) { + JsonParser parser = new JsonParser(); + // strip models/ and .json from the name + ResourceLocation model = new ResourceLocation(fileLocation.getNamespace(), fileLocation.getPath().substring(7, fileLocation.getPath().length()-5)); + return Pair.of(model, parser.parse(new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8))); + } catch(IOException e) { + ModernFix.LOGGER.error("Error reading model " + fileLocation, e); + return Pair.of(fileLocation, null); + } + }, Util.backgroundExecutor())); + } + allModels.clear(); + CompletableFuture.allOf(modelBytes.toArray(new CompletableFuture[0])).join(); + Set> errorSet = Sets.newLinkedHashSet(); + Map basicModels = new HashMap<>(); + try { + basicModels.put(MISSING_MODEL_LOCATION, this.loadBlockModel(MISSING_MODEL_LOCATION)); + basicModels.put(new ResourceLocation("builtin/generated"), GENERATION_MARKER); + basicModels.put(new ResourceLocation("builtin/entity"), BLOCK_ENTITY_MARKER); + } catch(IOException e) { + throw new RuntimeException("Exception when populating built-in models", e); + } + for(CompletableFuture> future : modelBytes) { + Pair pair = future.join(); + try { + if(pair.getSecond() != null) { + BlockModel model = ModelLoaderRegistry.ExpandedBlockModelDeserializer.INSTANCE.fromJson(pair.getSecond(), BlockModel.class); + model.name = pair.getFirst().toString(); + basicModels.put(pair.getFirst(), model); + } + } catch(Exception e) { + ModernFix.LOGGER.warn("Unable to load " + pair.getFirst(), e); + } + } + modelBytes.clear(); + Function modelGetter = loc -> basicModels.getOrDefault(loc, (BlockModel)this.missingModel); + for(BlockModel model : basicModels.values()) { + materialSet.addAll(model.getMaterials(modelGetter, errorSet)); + } + //errorSet.stream().filter(pair -> !pair.getSecond().equals(MISSING_MODEL_LOCATION_STRING)).forEach(pair -> LOGGER.warn("Unable to resolve texture reference: {} in {}", pair.getFirst(), pair.getSecond())); + stopwatch.stop(); + ModernFix.LOGGER.info("Resolving model textures took " + stopwatch); + } + + @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(); + cir.setReturnValue(atlasSet); + } + + /** + * Use the already loaded missing model instead of the cache entry (which will probably get evicted). + */ + @Redirect(method = "loadModel", at = @At(value = "INVOKE", target = "Ljava/util/Map;get(Ljava/lang/Object;)Ljava/lang/Object;", ordinal = 1)) + private Object getMissingModel(Map map, Object rl) { + if(rl == MISSING_MODEL_LOCATION && map == unbakedCache) + return missingModel; + return unbakedCache.get(rl); + } + + + /** + * @author embeddedt + * @reason synchronize + */ + @Overwrite + public synchronized UnbakedModel getModel(ResourceLocation modelLocation) { + if(modelLocation.equals(MISSING_MODEL_LOCATION)) + return missingModel; + if (this.unbakedCache.containsKey(modelLocation)) { + return this.unbakedCache.get(modelLocation); + } else if (this.loadingStack.contains(modelLocation)) { + throw new IllegalStateException("Circular reference while loading " + modelLocation); + } else { + this.loadingStack.add(modelLocation); + UnbakedModel iunbakedmodel = missingModel; + + while(!this.loadingStack.isEmpty()) { + ResourceLocation resourcelocation = this.loadingStack.iterator().next(); + + try { + if (!this.unbakedCache.containsKey(resourcelocation)) { + if(debugDynamicModelLoading) + LOGGER.info("Loading {}", resourcelocation); + this.loadModel(resourcelocation); + } + } catch (ModelBakery.BlockStateDefinitionException var9) { + LOGGER.warn(var9.getMessage()); + this.unbakedCache.put(resourcelocation, iunbakedmodel); + } catch (Exception var10) { + LOGGER.warn("Unable to load model: '{}' referenced from: {}: {}", resourcelocation, modelLocation, var10); + this.unbakedCache.put(resourcelocation, iunbakedmodel); + } finally { + this.loadingStack.remove(resourcelocation); + } + } + + return this.unbakedCache.getOrDefault(modelLocation, iunbakedmodel); + } + } + + @Overwrite + public synchronized BakedModel getBakedModel(ResourceLocation arg, ModelState arg2, Function textureGetter) { + Triple triple = Triple.of(arg, arg2.getRotation(), arg2.isUvLocked()); + if (this.bakedCache.containsKey(triple)) { + return this.bakedCache.get(triple); + } else if (this.atlasSet == null) { + throw new IllegalStateException("bake called too early"); + } else { + if(debugDynamicModelLoading) + LOGGER.info("Baking {}", arg); + UnbakedModel iunbakedmodel = this.getModel(arg); + iunbakedmodel.getMaterials(this::getModel, new HashSet<>()); + 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) { + ibakedmodel = iunbakedmodel.bake((ModelBakery) (Object) this, textureGetter, arg2, arg); + } + this.bakedCache.put(triple, ibakedmodel); + return ibakedmodel; + } + } +} 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 deleted file mode 100644 index c397f9a9..00000000 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_baking/BlockModelShapesMixin.java +++ /dev/null @@ -1,46 +0,0 @@ -package org.embeddedt.modernfix.mixin.perf.faster_baking; - -import com.mojang.datafixers.util.Pair; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.client.renderer.block.BlockModelShaper; -import net.minecraft.client.resources.model.BakedModel; -import net.minecraft.client.resources.model.ModelManager; -import net.minecraft.Util; -import net.minecraft.core.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(BlockModelShaper.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(BlockModelShaper.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 deleted file mode 100644 index 5f5bae31..00000000 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_baking/ModelBakeryMixin.java +++ /dev/null @@ -1,161 +0,0 @@ -package org.embeddedt.modernfix.mixin.perf.faster_baking; - -import com.google.common.collect.ImmutableSet; -import com.mojang.datafixers.util.Pair; -import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.block.model.MultiVariant; -import net.minecraft.client.renderer.block.model.multipart.MultiPart; -import net.minecraft.client.renderer.texture.TextureAtlas; -import net.minecraft.client.renderer.texture.AtlasSet; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.client.renderer.texture.TextureManager; -import net.minecraft.util.profiling.ProfilerFiller; -import net.minecraft.resources.ResourceLocation; -import com.mojang.math.Transformation; -import net.minecraftforge.client.event.ModelBakeEvent; -import net.minecraftforge.fml.loading.progress.StartupMessageManager; -import org.apache.commons.lang3.tuple.Triple; -import org.embeddedt.modernfix.core.config.ModernFixConfig; -import org.embeddedt.modernfix.duck.IExtendedModelBakery; -import org.embeddedt.modernfix.models.LazyBakedModel; -import org.embeddedt.modernfix.util.ModUtil; -import org.spongepowered.asm.mixin.*; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -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; - -import net.minecraft.client.renderer.block.model.BlockModel; -import net.minecraft.client.resources.model.BakedModel; -import net.minecraft.client.resources.model.BlockModelRotation; -import net.minecraft.client.resources.model.Material; -import net.minecraft.client.resources.model.ModelBakery; -import net.minecraft.client.resources.model.ModelResourceLocation; -import net.minecraft.client.resources.model.ModelState; -import net.minecraft.client.resources.model.UnbakedModel; - -@Mixin(ModelBakery.class) -public abstract class ModelBakeryMixin implements IExtendedModelBakery { - @Shadow @Final private Map topLevelModels; - - @Shadow @Final private Map bakedTopLevelModels; - - @Shadow @Deprecated @Nullable public abstract BakedModel bake(ResourceLocation pLocation, ModelState pTransform); - - @Shadow private Map> atlasPreparations; - - @Shadow @Nullable private AtlasSet atlasSet; - - @Shadow @Nullable public abstract BakedModel getBakedModel(ResourceLocation pLocation, ModelState pTransform, Function textureGetter); - - @Shadow @Final public static ModelResourceLocation MISSING_MODEL_LOCATION; - - @Shadow @Final private Map, BakedModel> bakedCache; - - @Shadow @Final private Map unbakedCache; - - private BakedModel bakeIfPossible(ResourceLocation p_229350_1_) { - BakedModel ibakedmodel = null; - - try { - ibakedmodel = this.bake(p_229350_1_, BlockModelRotation.X0_Y0); - } catch (Exception exception) { - exception.printStackTrace(); - LOGGER.warn("Unable to bake model: '{}': {}", p_229350_1_, exception); - } - - return ibakedmodel; - } - - private boolean requiresBake(UnbakedModel model) { - if(model instanceof BlockModel && ((BlockModel)model).customData.hasCustomGeometry()) - return true; - else - return false; - } - - @Inject(method = "processLoading", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/profiling/ProfilerFiller;pop()V")) - private void bakeModels(ProfilerFiller pProfiler, int p_i226056_4_, CallbackInfo ci) { - pProfiler.popPush("atlas"); - Minecraft.getInstance().executeBlocking(() -> { - for(Pair pair : this.atlasPreparations.values()) { - TextureAtlas atlastexture = pair.getFirst(); - TextureAtlas.Preparations atlastexture$sheetdata = pair.getSecond(); - atlastexture.reload(atlastexture$sheetdata); - } - }); - pProfiler.popPush("baking"); - StartupMessageManager.mcLoaderConsumer().ifPresent(c -> c.accept("Baking models")); - this.atlasSet = new AtlasSet(this.atlasPreparations.values().stream().map(Pair::getFirst).collect(Collectors.toList())); - BakedModel missingModel = this.bake(MISSING_MODEL_LOCATION, BlockModelRotation.X0_Y0); - this.bakedTopLevelModels.put(MISSING_MODEL_LOCATION, missingModel); - Collection modsListening = ModUtil.findAllModsListeningToEvent(ModelBakeEvent.class); - LOGGER.debug("Found ModelBakeEvent listeners: [" + String.join(", ", modsListening) + "]"); - Set incompatibleLazyBakedModels = ImmutableSet.builder() - .addAll(modsListening) - .build(); - /* First, bake any incompatible models ahead of time (for mods that have custom models) */ - new ArrayList<>(this.unbakedCache.keySet()).forEach(location -> { - if(incompatibleLazyBakedModels.contains(location.getNamespace())) { - this.bakeIfPossible(location); - } - }); - List multiparts = new ArrayList<>(); - /* Then store them as top-level models if needed, and set up the lazy models */ - this.topLevelModels.forEach((location, value) -> { - if (requiresBake(value) || incompatibleLazyBakedModels.contains(location.getNamespace())) { - BakedModel model = this.bakeIfPossible(location); - if (model != null) - this.bakedTopLevelModels.put(location, model); - } else { - if(value instanceof MultiPart || value instanceof MultiVariant) { - multiparts.add(location); - } else { - this.bakedTopLevelModels.put(location, new LazyBakedModel(() -> { - synchronized (this.bakedCache) { - BakedModel ibakedmodel = this.bakeIfPossible(location); - - return ibakedmodel != null ? ibakedmodel : missingModel; - } - })); - } - } - }); - multiparts.forEach(location -> { - BakedModel model = this.bakeIfPossible(location); - if (model != null) - this.bakedTopLevelModels.put(location, model); - }); - } - - /** - * @author embeddedt - * @reason texture loading and baking are moved earlier in the launch process, only render thread stuff is done here - */ - @Overwrite - public AtlasSet uploadTextures(TextureManager pResourceManager, ProfilerFiller pProfiler) { - pProfiler.push("atlas_upload"); - for(Pair pair : this.atlasPreparations.values()) { - TextureAtlas atlastexture = pair.getFirst(); - TextureAtlas.Preparations atlastexture$sheetdata = pair.getSecond(); - pResourceManager.register(atlastexture.location(), atlastexture); - pResourceManager.bind(atlastexture.location()); - atlastexture.updateFilter(atlastexture$sheetdata); - } - pProfiler.pop(); - return this.atlasSet; - } - - @Override - public AtlasSet getUnfinishedAtlasSet() { - return this.atlasSet; - } -} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_baking/ModelManagerMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_baking/ModelManagerMixin.java deleted file mode 100644 index 3e485c35..00000000 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_baking/ModelManagerMixin.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.embeddedt.modernfix.mixin.perf.faster_baking; - -import it.unimi.dsi.fastutil.objects.Object2IntMap; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.block.BlockModelShaper; -import net.minecraft.client.resources.model.BakedModel; -import net.minecraft.client.resources.model.ModelBakery; -import net.minecraft.client.resources.model.ModelManager; -import net.minecraft.client.renderer.texture.AtlasSet; -import net.minecraft.client.renderer.texture.TextureManager; -import net.minecraft.util.profiling.ProfilerFiller; -import net.minecraft.server.packs.resources.ResourceManager; -import net.minecraft.resources.ResourceLocation; -import net.minecraftforge.client.model.ModelLoader; -import net.minecraftforge.fml.common.ObfuscationReflectionHelper; -import org.embeddedt.modernfix.duck.IExtendedModelBakery; -import org.embeddedt.modernfix.models.LazyBakedModel; -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 org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import org.spongepowered.asm.mixin.injection.callback.LocalCapture; - -import javax.annotation.Nullable; -import java.util.Map; - -@Mixin(ModelManager.class) -public class ModelManagerMixin { - - @Inject(method = "apply(Lnet/minecraft/client/resources/model/ModelBakery;Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/util/profiling/ProfilerFiller;)V", - at = @At(value = "RETURN")) - private void allowBake(ModelBakery pObject, ResourceManager pResourceManager, ProfilerFiller pProfiler, CallbackInfo ci) { - LazyBakedModel.allowBakeForFlags = true; - } -} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/BooleanPropertyMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/model_optimizations/BooleanPropertyMixin.java similarity index 91% rename from src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/BooleanPropertyMixin.java rename to src/main/java/org/embeddedt/modernfix/mixin/perf/model_optimizations/BooleanPropertyMixin.java index a5fb1b72..934449b7 100644 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/BooleanPropertyMixin.java +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/model_optimizations/BooleanPropertyMixin.java @@ -1,4 +1,4 @@ -package org.embeddedt.modernfix.mixin.perf.parallelize_model_loading; +package org.embeddedt.modernfix.mixin.perf.model_optimizations; import com.google.common.collect.ImmutableSet; import net.minecraft.world.level.block.state.properties.BooleanProperty; diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/OBJLoaderMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/model_optimizations/OBJLoaderMixin.java similarity index 95% rename from src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/OBJLoaderMixin.java rename to src/main/java/org/embeddedt/modernfix/mixin/perf/model_optimizations/OBJLoaderMixin.java index 79b3c1f8..33f700d4 100644 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/OBJLoaderMixin.java +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/model_optimizations/OBJLoaderMixin.java @@ -1,4 +1,4 @@ -package org.embeddedt.modernfix.mixin.perf.parallelize_model_loading; +package org.embeddedt.modernfix.mixin.perf.model_optimizations; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.client.model.obj.MaterialLibrary; diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/PropertyMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/model_optimizations/PropertyMixin.java similarity index 95% rename from src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/PropertyMixin.java rename to src/main/java/org/embeddedt/modernfix/mixin/perf/model_optimizations/PropertyMixin.java index 4ff99e9c..4d64c0ba 100644 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/PropertyMixin.java +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/model_optimizations/PropertyMixin.java @@ -1,4 +1,4 @@ -package org.embeddedt.modernfix.mixin.perf.parallelize_model_loading; +package org.embeddedt.modernfix.mixin.perf.model_optimizations; import net.minecraft.world.level.block.state.properties.Property; import org.embeddedt.modernfix.dedup.IdentifierCaches; diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/SelectorMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/model_optimizations/SelectorMixin.java similarity index 90% rename from src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/SelectorMixin.java rename to src/main/java/org/embeddedt/modernfix/mixin/perf/model_optimizations/SelectorMixin.java index 19c88421..e52d84c4 100644 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/SelectorMixin.java +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/model_optimizations/SelectorMixin.java @@ -1,6 +1,5 @@ -package org.embeddedt.modernfix.mixin.perf.parallelize_model_loading; +package org.embeddedt.modernfix.mixin.perf.model_optimizations; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.client.renderer.block.model.multipart.Selector; diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/TransformationMatrixMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/model_optimizations/TransformationMatrixMixin.java similarity index 91% rename from src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/TransformationMatrixMixin.java rename to src/main/java/org/embeddedt/modernfix/mixin/perf/model_optimizations/TransformationMatrixMixin.java index 184b4006..286d030f 100644 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/TransformationMatrixMixin.java +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/model_optimizations/TransformationMatrixMixin.java @@ -1,4 +1,4 @@ -package org.embeddedt.modernfix.mixin.perf.parallelize_model_loading; +package org.embeddedt.modernfix.mixin.perf.model_optimizations; import com.mojang.math.Matrix4f; import com.mojang.math.Transformation; diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/BlockModelShaperMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/BlockModelShaperMixin.java deleted file mode 100644 index 9da0fed8..00000000 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/BlockModelShaperMixin.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.embeddedt.modernfix.mixin.perf.parallelize_model_loading; - -import com.google.common.collect.Interner; -import com.google.common.collect.Interners; -import net.minecraft.client.renderer.block.BlockModelShaper; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.properties.Property; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -@Mixin(BlockModelShaper.class) -public class BlockModelShaperMixin { - private static Map stateToPropertiesCache = new ConcurrentHashMap<>(); - - @Redirect(method = "stateToModelLocation(Lnet/minecraft/resources/ResourceLocation;Lnet/minecraft/world/level/block/state/BlockState;)Lnet/minecraft/client/resources/model/ModelResourceLocation;", - at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/block/BlockModelShaper;statePropertiesToString(Ljava/util/Map;)Ljava/lang/String;")) - private static String getCachedProperty(Map, Comparable> values, ResourceLocation location, BlockState state) { - /* We intentionally don't use the values parameter inside the lambda to avoid an allocation */ - return stateToPropertiesCache.computeIfAbsent(state, s -> BlockModelShaper.statePropertiesToString(s.getValues())); - } -} 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 deleted file mode 100644 index e696f92f..00000000 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/ModelBakeryMixin.java +++ /dev/null @@ -1,114 +0,0 @@ -package org.embeddedt.modernfix.mixin.perf.parallelize_model_loading; - -import com.mojang.datafixers.util.Pair; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.util.profiling.ProfilerFiller; -import net.minecraft.server.packs.resources.Resource; -import net.minecraft.server.packs.resources.ResourceManager; -import net.minecraft.world.level.block.state.StateDefinition; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.Util; -import net.minecraft.core.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; -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.CallbackInfo; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Function; -import java.util.stream.Stream; - -import net.minecraft.client.renderer.block.model.BlockModelDefinition; -import net.minecraft.client.resources.model.ModelBakery; -import net.minecraft.client.resources.model.ModelResourceLocation; - -@Mixin(ModelBakery.class) -public abstract class ModelBakeryMixin { - @Shadow @Final protected ResourceManager resourceManager; - - private Map>> deserializedBlockstateCache = null; - - @Inject(method = "processLoading", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/profiling/ProfilerFiller;popPush(Ljava/lang/String;)V", ordinal = 0)) - private void preloadJsonModels(ProfilerFiller 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.Context::new); - this.deserializedBlockstateCache = new ConcurrentHashMap<>(); - ArrayList> futures = new ArrayList<>(); - for(Block block : Registry.BLOCK) { - ResourceLocation blockLocation = Registry.BLOCK.getKey(block); - futures.add(CompletableFuture.runAsync(() -> { - ResourceLocation blockStateJSON = new ResourceLocation(blockLocation.getNamespace(), "blockstates/" + blockLocation.getPath() + ".json"); - List blockStates; - try { - /* Some mods' custom resource pack implementations don't seem to like concurrency here */ - synchronized(this.resourceManager) { - blockStates = this.resourceManager.getResources(blockStateJSON); - } - } catch(IOException e) { - ModernFix.LOGGER.warn("Exception loading blockstate definition: {}: {}", blockLocation, e); - return; - } - List> definitions = new ArrayList<>(); - StateDefinition stateContainer = block.getStateDefinition(); - BlockModelDefinition.Context context = containerHolder.get(); - context.setDefinition(stateContainer); - for(Resource resource : blockStates) { - try (InputStream inputstream = resource.getInputStream()) { - BlockModelDefinition definition = BlockModelDefinition.fromStream(context, new InputStreamReader(inputstream, StandardCharsets.UTF_8)); - definitions.add(Pair.of(resource.getSourceName(), definition)); - } catch (Exception exception1) { - ModernFix.LOGGER.warn(String.format("Exception loading blockstate definition: '%s' in resourcepack: '%s': %s", resource.getLocation(), resource.getSourceName(), exception1.getMessage())); - return; - } - } - this.deserializedBlockstateCache.put(blockLocation, definitions); - }, Util.backgroundExecutor())); - } - CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join(); - }); - } - - @Inject(method = "processLoading", at = @At("RETURN"), remap = false) - private void clearModelCache(ProfilerFiller profilerIn, int maxMipmapLevel, CallbackInfo ci) { - deserializedBlockstateCache.clear(); - } - - private List replacementList = null; - - @Redirect(method = "loadModel", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/packs/resources/ResourceManager;getResources(Lnet/minecraft/resources/ResourceLocation;)Ljava/util/List;", ordinal = 0)) - private List getResourceList(ResourceManager instance, ResourceLocation jsonLocation, ResourceLocation originalBlockLocation) throws IOException { - replacementList = null; - if(this.deserializedBlockstateCache != null) { - if(!(originalBlockLocation instanceof ModelResourceLocation)) { - throw new AssertionError("Injector in unexpected spot?"); - } - ModelResourceLocation mrl = (ModelResourceLocation)originalBlockLocation; - ResourceLocation location = new ResourceLocation(mrl.getNamespace(), mrl.getPath()); - List theList = this.deserializedBlockstateCache.get(location); - if(theList != null && theList.size() > 0) { - replacementList = theList; - return theList; - } - } - return instance.getResources(jsonLocation); - } - - @Redirect(method = "loadModel", at = @At(value = "INVOKE", target = "Ljava/util/stream/Stream;map(Ljava/util/function/Function;)Ljava/util/stream/Stream;", ordinal = 0)) - private Stream fakeResourceList(Stream instance, Function function) { - return replacementList != null ? instance : instance.map(function); - } -} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/multipart/MultipartMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/multipart/MultipartMixin.java deleted file mode 100644 index 5d375be1..00000000 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/multipart/MultipartMixin.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.embeddedt.modernfix.mixin.perf.parallelize_model_loading.multipart; - -import net.minecraft.client.renderer.block.model.multipart.MultiPart; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; - -import java.util.List; -import java.util.stream.Stream; - -@Mixin(MultiPart.class) -public class MultipartMixin { - @Redirect(method = "getMaterials", at = @At(value = "INVOKE", target = "Ljava/util/List;stream()Ljava/util/stream/Stream;", ordinal = 0)) - private Stream makeStreamParallel(List instance) { - return instance.parallelStream(); - } -} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/multipart/VariantListMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/multipart/VariantListMixin.java deleted file mode 100644 index ead9af25..00000000 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/multipart/VariantListMixin.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.embeddedt.modernfix.mixin.perf.parallelize_model_loading.multipart; - -import com.mojang.datafixers.util.Pair; -import net.minecraft.client.resources.model.UnbakedModel; -import net.minecraft.client.resources.model.Material; -import net.minecraft.client.renderer.block.model.Variant; -import net.minecraft.client.renderer.block.model.MultiVariant; -import net.minecraft.resources.ResourceLocation; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Overwrite; -import org.spongepowered.asm.mixin.Shadow; - -import java.util.Collection; -import java.util.List; -import java.util.Set; -import java.util.function.Function; -import java.util.stream.Collectors; - -@Mixin(MultiVariant.class) -public abstract class VariantListMixin { - @Shadow public abstract List getVariants(); - - /** - * @author embeddedt - * @reason Parallelize calls to getMaterials - */ - @Overwrite - public Collection getMaterials(Function pModelGetter, Set> pMissingTextureErrors) { - List models = this.getVariants().stream().map(Variant::getModelLocation).distinct().map(pModelGetter).collect(Collectors.toList()); - return models.parallelStream().flatMap(model -> model.getMaterials(pModelGetter, pMissingTextureErrors).stream()).collect(Collectors.toSet()); - } -} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/state_definition_construct/StateDefinitionMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/state_definition_construct/StateDefinitionMixin.java new file mode 100644 index 00000000..dd6157c3 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/state_definition_construct/StateDefinitionMixin.java @@ -0,0 +1,29 @@ +package org.embeddedt.modernfix.mixin.perf.state_definition_construct; + +import com.google.common.collect.ImmutableSortedMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.StateHolder; +import net.minecraft.world.level.block.state.properties.Property; +import org.embeddedt.modernfix.blockstate.FakeStateMap; +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.ModifyVariable; + +import java.util.Map; + +@Mixin(StateDefinition.class) +public class StateDefinitionMixin> { + @Shadow @Final private ImmutableSortedMap> propertiesByName; + + @ModifyVariable(method = "", at = @At(value = "STORE", ordinal = 0), ordinal = 1, index = 8) + private Map, Comparable>, S> useArrayMap(Map, Comparable>, S> in) { + int numStates = 1; + for(Property prop : this.propertiesByName.values()) { + numStates *= prop.getPossibleValues().size(); + } + return new FakeStateMap<>(numStates); + } +} diff --git a/src/main/java/org/embeddedt/modernfix/models/LazyBakedModel.java b/src/main/java/org/embeddedt/modernfix/models/LazyBakedModel.java deleted file mode 100644 index d5b87496..00000000 --- a/src/main/java/org/embeddedt/modernfix/models/LazyBakedModel.java +++ /dev/null @@ -1,138 +0,0 @@ -package org.embeddedt.modernfix.models; - -import com.mojang.blaze3d.vertex.PoseStack; -import com.mojang.datafixers.util.Pair; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.client.renderer.RenderType; -import net.minecraft.client.renderer.block.model.BakedQuad; -import net.minecraft.client.resources.model.BakedModel; -import net.minecraft.client.renderer.block.model.ItemTransforms; -import net.minecraft.client.renderer.block.model.ItemOverrides; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.world.item.ItemStack; -import net.minecraft.core.Direction; -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.BlockAndTintGetter; -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 BakedModel { - private BakedModel delegate = null; - private Supplier delegateSupplier; - - /** - * This flag is changed to true when we should bake instead of returning reasonable defaults for certain - * method calls. - */ - public static boolean allowBakeForFlags = false; - - public LazyBakedModel(Supplier delegateSupplier) { - this.delegateSupplier = delegateSupplier; - } - - public BakedModel 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() { - if(this.delegate != null) - return this.delegate.isCustomRenderer(); - if(!LazyBakedModel.allowBakeForFlags) - return false; - return computeDelegate().isCustomRenderer(); - } - - @Override - public TextureAtlasSprite getParticleIcon() { - return computeDelegate().getParticleIcon(); - } - - @Override - public ItemTransforms getTransforms() { - return computeDelegate().getTransforms(); - } - - @Override - public ItemOverrides getOverrides() { - return computeDelegate().getOverrides(); - } - - @Override - public BakedModel 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 BakedModel handlePerspective(ItemTransforms.TransformType cameraTransformType, PoseStack mat) { - return computeDelegate().handlePerspective(cameraTransformType, mat); - } - - @Nonnull - @Override - public IModelData getModelData(@Nonnull BlockAndTintGetter 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/java/org/embeddedt/modernfix/textures/StbStitcher.java b/src/main/java/org/embeddedt/modernfix/textures/StbStitcher.java index d5958c08..42c2fdf8 100644 --- a/src/main/java/org/embeddedt/modernfix/textures/StbStitcher.java +++ b/src/main/java/org/embeddedt/modernfix/textures/StbStitcher.java @@ -92,18 +92,18 @@ public class StbStitcher { } } - private static STBRPRect setWrapper(STBRPRect rect, int id, int width, int height, int x, int y, boolean was_packed) { + public static STBRPRect setWrapper(STBRPRect rect, int id, int width, int height, int x, int y, boolean was_packed) { try { if(MH_rect_shortSet != null) - return (STBRPRect)MH_rect_shortSet.invokeExact(rect, id, (short)width, (short)height, (short)0, (short)0, false); + return (STBRPRect)MH_rect_shortSet.invokeExact(rect, id, (short)width, (short)height, (short)x, (short)y, was_packed); else - return (STBRPRect)MH_rect_intSet.invokeExact(rect, id, width, height, 0, 0, false); + return (STBRPRect)MH_rect_intSet.invokeExact(rect, id, width, height, x, y, was_packed); } catch(Throwable e) { throw new AssertionError(e); } } - private static int getX(STBRPRect rect) { + public static int getX(STBRPRect rect) { try { if(MH_rect_shortX != null) return (short)MH_rect_shortX.invokeExact(rect); @@ -114,7 +114,7 @@ public class StbStitcher { } } - private static int getY(STBRPRect rect) { + public static int getY(STBRPRect rect) { try { if(MH_rect_shortX != null) return (short)MH_rect_shortY.invokeExact(rect); diff --git a/src/main/java/org/embeddedt/modernfix/util/FileUtil.java b/src/main/java/org/embeddedt/modernfix/util/FileUtil.java index 51045bda..41f2c4f5 100644 --- a/src/main/java/org/embeddedt/modernfix/util/FileUtil.java +++ b/src/main/java/org/embeddedt/modernfix/util/FileUtil.java @@ -9,14 +9,34 @@ public class FileUtil { return file; } - private static final Pattern SLASH_PATTERN = Pattern.compile("(?:\\\\+|\\/+)"); - /** * Normalize a path by removing double slashes, etc. + *

+ * This implementation avoids creating a new string unless there are actually double slashes present + * in the input path. * @param path input path * @return a normalized version of the path */ public static String normalize(String path) { - return SLASH_PATTERN.matcher(path).replaceAll("/"); + char prevChar = 0; + StringBuilder sb = null; + for(int i = 0; i < path.length(); i++) { + char thisChar = path.charAt(i); + if(prevChar != '/' || thisChar != prevChar) { + /* This character should end up in the final string. If we are using the builder, add it there. */ + if(sb != null) + sb.append(thisChar); + } else { + /* This character should not end up in the final string. We need to make a buidler if we haven't + * done so yet. + */ + if(sb == null) { + sb = new StringBuilder(path.length()); + sb.append(path, 0, i); + } + } + prevChar = thisChar; + } + return sb == null ? path : sb.toString(); } } diff --git a/src/main/resources/modernfix.mixins.json b/src/main/resources/modernfix.mixins.json index d156cbc4..47cd50be 100644 --- a/src/main/resources/modernfix.mixins.json +++ b/src/main/resources/modernfix.mixins.json @@ -60,7 +60,8 @@ "perf.cache_strongholds.ChunkGeneratorMixin", "perf.cache_upgraded_structures.StructureManagerMixin", "perf.cache_strongholds.ServerLevelMixin", - "devenv.MinecraftServerMixin", + "perf.state_definition_construct.StateDefinitionMixin", + "devenv.MinecraftServerMixin" ], "client": [ "core.MinecraftMixin", @@ -71,15 +72,14 @@ "bugfix.concurrency.RenderTypeMixin", "bugfix.concurrency.MinecraftMixin", "bugfix.concurrency.StaticTagHelperMixin", - "perf.parallelize_model_loading.BlockModelShaperMixin", - "perf.parallelize_model_loading.ModelBakeryMixin", - "perf.parallelize_model_loading.OBJLoaderMixin", - "perf.parallelize_model_loading.multipart.MultipartMixin", - "perf.parallelize_model_loading.multipart.VariantListMixin", - "perf.parallelize_model_loading.SelectorMixin", - "perf.parallelize_model_loading.TransformationMatrixMixin", - "perf.parallelize_model_loading.BooleanPropertyMixin", - "perf.parallelize_model_loading.PropertyMixin", + "perf.dynamic_resources.BlockModelShaperMixin", + "perf.dynamic_resources.ItemModelShaperMixin", + "perf.dynamic_resources.ModelBakeryMixin", + "perf.model_optimizations.OBJLoaderMixin", + "perf.model_optimizations.SelectorMixin", + "perf.model_optimizations.TransformationMatrixMixin", + "perf.model_optimizations.BooleanPropertyMixin", + "perf.model_optimizations.PropertyMixin", "perf.async_jei.InputConstantsMixin", "perf.async_jei.IngredientListElementFactoryMixin", "perf.async_jei.ClientLifecycleHandlerMixin", @@ -93,9 +93,6 @@ "perf.flatten_model_predicates.PropertyValueConditionMixin", "perf.blast_search_trees.MinecraftMixin", "perf.blast_search_trees.IngredientFilterInvoker", - "perf.faster_baking.ModelBakeryMixin", - "perf.faster_baking.BlockModelShapesMixin", - "perf.faster_baking.ModelManagerMixin", "perf.cache_model_materials.VanillaModelMixin", "perf.cache_model_materials.MultipartMixin", "perf.faster_texture_stitching.StitcherMixin",