diff --git a/Common/src/main/java/malte0811/ferritecore/impl/CharListIntListWrapper.java b/Common/src/main/java/malte0811/ferritecore/impl/CharListIntListWrapper.java new file mode 100644 index 0000000..992dc86 --- /dev/null +++ b/Common/src/main/java/malte0811/ferritecore/impl/CharListIntListWrapper.java @@ -0,0 +1,105 @@ +package malte0811.ferritecore.impl; + +import it.unimi.dsi.fastutil.chars.CharComparator; +import it.unimi.dsi.fastutil.chars.CharList; +import it.unimi.dsi.fastutil.ints.AbstractIntList; +import it.unimi.dsi.fastutil.ints.IntComparator; +import it.unimi.dsi.fastutil.ints.IntList; + +public class CharListIntListWrapper extends AbstractIntList { + public final CharList delegate; + + public CharListIntListWrapper(CharList delegate) { + this.delegate = delegate; + } + + @Override + public int getInt(int index) { + return charToInt(delegate.getChar(index)); + } + + @Override + public int size() { + return delegate.size(); + } + + @Override + public void size(int size) { + delegate.size(size); + } + + @Override + public void add(int index, int k) { + delegate.add(index, intToChar(k)); + } + + @Override + public boolean add(int k) { + return delegate.add(intToChar(k)); + } + + @Override + public int removeInt(int i) { + return charToInt(delegate.removeChar(i)); + } + + @Override + public int set(int index, int k) { + return delegate.set(index, intToChar(k)); + } + + @Override + public int indexOf(int k) { + return delegate.indexOf(intToChar(k)); + } + + @Override + public int lastIndexOf(int k) { + return delegate.lastIndexOf(intToChar(k)); + } + + @Override + public boolean rem(int k) { + return delegate.rem(intToChar(k)); + } + + @Override + public void clear() { + delegate.clear(); + } + + @Override + public IntList subList(int from, int to) { + return new CharListIntListWrapper(delegate.subList(from, to)); + } + + @Override + public void sort(IntComparator comparator) { + delegate.sort(wrapComparator(comparator)); + } + + @Override + public void unstableSort(IntComparator comparator) { + delegate.unstableSort(wrapComparator(comparator)); + } + + @Override + public void removeElements(int from, int to) { + delegate.removeElements(from, to); + } + + public static CharComparator wrapComparator(IntComparator comparator) { + return (k1, k2) -> comparator.compare(charToInt(k1), charToInt(k2)); + } + + // we use \u0000 to represent -1 or the end + public static char intToChar(int i) { + if (i > Character.MAX_VALUE || i == 0) + throw new IllegalArgumentException(); + return i < 0 ? '\u0000' : (char)i; + } + + public static int charToInt(char c) { + return c == '\u0000' ? -1 : (int)c; + } +} diff --git a/Common/src/main/java/malte0811/ferritecore/impl/ModelSidesImpl.java b/Common/src/main/java/malte0811/ferritecore/impl/ModelSidesImpl.java index ce578c7..236a866 100644 --- a/Common/src/main/java/malte0811/ferritecore/impl/ModelSidesImpl.java +++ b/Common/src/main/java/malte0811/ferritecore/impl/ModelSidesImpl.java @@ -1,5 +1,6 @@ package malte0811.ferritecore.impl; +import malte0811.ferritecore.util.CollectionUtil; import net.minecraft.Util; import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.core.Direction; @@ -22,7 +23,7 @@ public class ModelSidesImpl { }); public static List minimizeUnculled(List quads) { - return List.copyOf(quads); + return CollectionUtil.minimize(quads); } public static Map> minimizeCulled(Map> quadsBySide) { diff --git a/Common/src/main/java/malte0811/ferritecore/impl/compactunihex/CompactByteContents.java b/Common/src/main/java/malte0811/ferritecore/impl/compactunihex/CompactByteContents.java new file mode 100644 index 0000000..899d627 --- /dev/null +++ b/Common/src/main/java/malte0811/ferritecore/impl/compactunihex/CompactByteContents.java @@ -0,0 +1,40 @@ +package malte0811.ferritecore.impl.compactunihex; + +import net.minecraft.client.gui.font.providers.UnihexProvider.LineData; +import org.jetbrains.annotations.Range; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +public class CompactByteContents implements LineData { + private final long upperBytes; + private final long lowerBytes; + + public CompactByteContents(long upperBytes, long lowerBytes) { + this.upperBytes = upperBytes; + this.lowerBytes = lowerBytes; + } + + public CompactByteContents(byte[] bytes) { + if (bytes.length != 16) + throw new IllegalArgumentException(); + ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN); + // not really sure why i have to flip these, but it only works if i do, so... + this.lowerBytes = buffer.getLong(); + this.upperBytes = buffer.getLong(); + } + + @Override + public int line(@Range(from = 0, to = 15) int index) { + // index should always be between 0 and 15 + long bytes = index >= Long.BYTES ? upperBytes : lowerBytes; + int bits = Byte.SIZE * (index % Byte.SIZE); + // basically the structure is like b7 b6 b5 b4 b3 b2 b1 b0 (little endian) + return (byte)((bytes >> bits) & 0xFF) << 24; // shift 24 bits to the left (mc does this idk) + } + + @Override + public int bitWidth() { + return Byte.SIZE; // 8 + } +} diff --git a/Common/src/main/java/malte0811/ferritecore/impl/compactunihex/CompactShortContents.java b/Common/src/main/java/malte0811/ferritecore/impl/compactunihex/CompactShortContents.java new file mode 100644 index 0000000..3dd8357 --- /dev/null +++ b/Common/src/main/java/malte0811/ferritecore/impl/compactunihex/CompactShortContents.java @@ -0,0 +1,46 @@ +package malte0811.ferritecore.impl.compactunihex; + +import net.minecraft.client.gui.font.providers.UnihexProvider.LineData; +import org.jetbrains.annotations.Range; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +public class CompactShortContents implements LineData { + private final long shorts1; + private final long shorts2; + private final long shorts3; + private final long shorts4; + + public CompactShortContents(short[] shorts) { + if (shorts.length != 16) + throw new IllegalArgumentException(); + ByteBuffer buffer = ByteBuffer.allocate(16 * Short.BYTES).order(ByteOrder.LITTLE_ENDIAN); + buffer.asShortBuffer().put(shorts); + this.shorts1 = buffer.getLong(); + this.shorts2 = buffer.getLong(); + this.shorts3 = buffer.getLong(); + this.shorts4 = buffer.getLong(); + } + + @Override + public int line(@Range(from = 0, to = 15) int index) { + // index should always be between 0 and 15 + long shorts = switch (index) { + case 0, 1, 2, 3 -> shorts1; + // 4 shorts in a long + case 4, 5, 6, 7 -> shorts2; + case 8, 9, 10, 11 -> shorts3; + case 12, 13, 14, 15 -> shorts4; + default -> throw new IllegalArgumentException(); + }; + int bits = Short.SIZE * (index % 4); + // basically the structure is like s3 s2 s1 s0 (little endian) + return (short)((shorts >> bits) & 0xFFFF) << 16; // shift 16 bits to the left (mc does this idk) + } + + @Override + public int bitWidth() { + return Short.SIZE; + } +} diff --git a/Common/src/main/java/malte0811/ferritecore/mixin/accessors/UnihexProviderAccess.java b/Common/src/main/java/malte0811/ferritecore/mixin/accessors/UnihexProviderAccess.java new file mode 100644 index 0000000..8ec6118 --- /dev/null +++ b/Common/src/main/java/malte0811/ferritecore/mixin/accessors/UnihexProviderAccess.java @@ -0,0 +1,17 @@ +package malte0811.ferritecore.mixin.accessors; + +import it.unimi.dsi.fastutil.bytes.ByteList; +import net.minecraft.client.gui.font.providers.UnihexProvider; +import org.jetbrains.annotations.Contract; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(UnihexProvider.class) +public interface UnihexProviderAccess { + @Contract("_, _, _ -> _") // ij thinks it always throws :( + @Invoker("decodeHex") + static int decodeHex(int lineNumber, ByteList byteList, int index) { + //noinspection Contract + throw new AssertionError(); + } +} diff --git a/Common/src/main/java/malte0811/ferritecore/mixin/blockmodellists/BlockModelDeserializerMixin.java b/Common/src/main/java/malte0811/ferritecore/mixin/blockmodellists/BlockModelDeserializerMixin.java new file mode 100644 index 0000000..62abbe0 --- /dev/null +++ b/Common/src/main/java/malte0811/ferritecore/mixin/blockmodellists/BlockModelDeserializerMixin.java @@ -0,0 +1,32 @@ +package malte0811.ferritecore.mixin.blockmodellists; + +import com.mojang.datafixers.util.Either; +import malte0811.ferritecore.util.CollectionUtil; +import net.minecraft.client.renderer.block.model.BlockElement; +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.renderer.block.model.ItemOverride; +import net.minecraft.client.resources.model.Material; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyArg; + +import java.util.List; +import java.util.Map; + +@Mixin(BlockModel.Deserializer.class) +public class BlockModelDeserializerMixin { + @ModifyArg(method = "deserialize(Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Lnet/minecraft/client/renderer/block/model/BlockModel;", index = 1, at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/block/model/BlockModel;(Lnet/minecraft/resources/ResourceLocation;Ljava/util/List;Ljava/util/Map;Ljava/lang/Boolean;Lnet/minecraft/client/renderer/block/model/BlockModel$GuiLight;Lnet/minecraft/client/renderer/block/model/ItemTransforms;Ljava/util/List;)V")) + private List elementsImmutableCopy(List list) { + return CollectionUtil.minimize(list); + } + + @ModifyArg(method = "deserialize(Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Lnet/minecraft/client/renderer/block/model/BlockModel;", index = 2, at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/block/model/BlockModel;(Lnet/minecraft/resources/ResourceLocation;Ljava/util/List;Ljava/util/Map;Ljava/lang/Boolean;Lnet/minecraft/client/renderer/block/model/BlockModel$GuiLight;Lnet/minecraft/client/renderer/block/model/ItemTransforms;Ljava/util/List;)V")) + private Map> immutableCopy(Map> map) { + return Map.copyOf(map); + } + + @ModifyArg(method = "deserialize(Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Lnet/minecraft/client/renderer/block/model/BlockModel;", index = 6, at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/block/model/BlockModel;(Lnet/minecraft/resources/ResourceLocation;Ljava/util/List;Ljava/util/Map;Ljava/lang/Boolean;Lnet/minecraft/client/renderer/block/model/BlockModel$GuiLight;Lnet/minecraft/client/renderer/block/model/ItemTransforms;Ljava/util/List;)V")) + private List overridesImmutableCopy(List list) { + return CollectionUtil.minimize(list); + } +} diff --git a/Common/src/main/java/malte0811/ferritecore/mixin/blockmodellists/Config.java b/Common/src/main/java/malte0811/ferritecore/mixin/blockmodellists/Config.java new file mode 100644 index 0000000..c5325ad --- /dev/null +++ b/Common/src/main/java/malte0811/ferritecore/mixin/blockmodellists/Config.java @@ -0,0 +1,10 @@ +package malte0811.ferritecore.mixin.blockmodellists; + +import malte0811.ferritecore.mixin.config.FerriteConfig; +import malte0811.ferritecore.mixin.config.FerriteMixinConfig; + +public class Config extends FerriteMixinConfig { + public Config() { + super(FerriteConfig.DEDUP_MULTIPART); + } +} diff --git a/Common/src/main/java/malte0811/ferritecore/mixin/compactunihex/Config.java b/Common/src/main/java/malte0811/ferritecore/mixin/compactunihex/Config.java new file mode 100644 index 0000000..9048400 --- /dev/null +++ b/Common/src/main/java/malte0811/ferritecore/mixin/compactunihex/Config.java @@ -0,0 +1,11 @@ +package malte0811.ferritecore.mixin.compactunihex; + +import malte0811.ferritecore.mixin.config.FerriteConfig; +import malte0811.ferritecore.mixin.config.FerriteMixinConfig; + +// could probably also do the int ones but its probably not worth it +public class Config extends FerriteMixinConfig { + public Config() { + super(FerriteConfig.COMPACT_UNIHEX); + } +} diff --git a/Common/src/main/java/malte0811/ferritecore/mixin/compactunihex/UnihexByteContentsMixin.java b/Common/src/main/java/malte0811/ferritecore/mixin/compactunihex/UnihexByteContentsMixin.java new file mode 100644 index 0000000..fe0ccaf --- /dev/null +++ b/Common/src/main/java/malte0811/ferritecore/mixin/compactunihex/UnihexByteContentsMixin.java @@ -0,0 +1,54 @@ +package malte0811.ferritecore.mixin.compactunihex; + +import it.unimi.dsi.fastutil.bytes.ByteList; +import malte0811.ferritecore.impl.compactunihex.CompactByteContents; +import net.minecraft.client.gui.font.providers.UnihexProvider; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; + +import static malte0811.ferritecore.mixin.accessors.UnihexProviderAccess.decodeHex; + +@Mixin(targets = "net/minecraft/client/gui/font/providers/UnihexProvider$ByteContents") +public class UnihexByteContentsMixin { + /** + * @author AnAwesomGuy (ferritecore) + * @reason redirect to more compact instance + */ + @Overwrite + public static UnihexProvider.LineData read(int index, ByteList byteList) { + // i eliminated the for loop mwuahaha >:) + long upper = ((((long)decodeHex(index, byteList, 31) << 4) | + decodeHex(index, byteList, 30)) << 56) | + ((((long)decodeHex(index, byteList, 29) << 4) | + decodeHex(index, byteList, 28)) << 48) | + ((((long)decodeHex(index, byteList, 27) << 4) | + decodeHex(index, byteList, 26)) << 40) | + ((((long)decodeHex(index, byteList, 25) << 4) | + decodeHex(index, byteList, 24)) << 32) | + ((((long)decodeHex(index, byteList, 23) << 4) | + decodeHex(index, byteList, 21)) << 24) | + ((((long)decodeHex(index, byteList, 20) << 4) | + decodeHex(index, byteList, 19)) << 16) | + ((((long)decodeHex(index, byteList, 18) << 4) | + decodeHex(index, byteList, 17)) << 8) | + ((((long)decodeHex(index, byteList, 16) << 4) | + decodeHex(index, byteList, 15))); + long lower = ((((long)decodeHex(index, byteList, 15) << 4) | + decodeHex(index, byteList, 14)) << 56) | + ((((long)decodeHex(index, byteList, 13) << 4) | + decodeHex(index, byteList, 12)) << 48) | + ((((long)decodeHex(index, byteList, 11) << 4) | + decodeHex(index, byteList, 10)) << 40) | + ((((long)decodeHex(index, byteList, 9) << 4) | + decodeHex(index, byteList, 8)) << 32) | + ((((long)decodeHex(index, byteList, 7) << 4) | + decodeHex(index, byteList, 6)) << 24) | + ((((long)decodeHex(index, byteList, 5) << 4) | + decodeHex(index, byteList, 6)) << 16) | + ((((long)decodeHex(index, byteList, 3) << 4) | + decodeHex(index, byteList, 2)) << 8) | + ((((long)decodeHex(index, byteList, 1) << 4) | + decodeHex(index, byteList, 0))); + return new CompactByteContents(upper, lower); + } +} diff --git a/Common/src/main/java/malte0811/ferritecore/mixin/compactunihex/UnihexShortContentsMixin.java b/Common/src/main/java/malte0811/ferritecore/mixin/compactunihex/UnihexShortContentsMixin.java new file mode 100644 index 0000000..c5884d7 --- /dev/null +++ b/Common/src/main/java/malte0811/ferritecore/mixin/compactunihex/UnihexShortContentsMixin.java @@ -0,0 +1,18 @@ +package malte0811.ferritecore.mixin.compactunihex; + +import it.unimi.dsi.fastutil.bytes.ByteList; +import malte0811.ferritecore.impl.compactunihex.CompactShortContents; +import net.minecraft.client.gui.font.providers.UnihexProvider.LineData; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +@Mixin(targets = "net/minecraft/client/gui/font/providers/UnihexProvider$ShortContents") +public class UnihexShortContentsMixin { + @Inject(method = "read", at = @At(value = "NEW", target = "([S)Lnet/minecraft/client/gui/font/providers/UnihexProvider$ShortContents;"), cancellable = true, locals = LocalCapture.CAPTURE_FAILSOFT) + private static void redirectByteContentsToOurs(int index, ByteList byteList, CallbackInfoReturnable cir, short[] bytes, int i) { + cir.setReturnValue(new CompactShortContents(bytes)); + } +} diff --git a/Common/src/main/java/malte0811/ferritecore/mixin/config/FerriteConfig.java b/Common/src/main/java/malte0811/ferritecore/mixin/config/FerriteConfig.java index 6f052d7..6416232 100644 --- a/Common/src/main/java/malte0811/ferritecore/mixin/config/FerriteConfig.java +++ b/Common/src/main/java/malte0811/ferritecore/mixin/config/FerriteConfig.java @@ -23,6 +23,9 @@ public class FerriteConfig { public static final Option POPULATE_NEIGHBOR_TABLE; public static final Option THREADING_DETECTOR; public static final Option MODEL_SIDES; + public static final Option BLOCK_MODEL_LISTS; + public static final Option COMPACT_UNIHEX; + public static final Option SUFFIX_ARRAY; static { ConfigBuilder builder = new ConfigBuilder(); @@ -74,6 +77,18 @@ public class FerriteConfig { "Populate the neighbor table used by vanilla. Enabling this slightly increases memory usage, but" + " can help with issues in the rare case where mods access it directly." ); + BLOCK_MODEL_LISTS = builder.createOption( + "blockModelLists", + "Use smaller data structures in BlockModel, reducing the amount of empty ArrayLists." + ); + COMPACT_UNIHEX = builder.createOption( + "compactUnihex", + "Compacts unihex font glyphs into longs instead of using short and byte arrays, saving some memory." + ); + SUFFIX_ARRAY = builder.createOption( + "suffixArray", + "Replaces the int list in SuffixArray with a char list, halving the memory usage." + ); builder.finish(); } diff --git a/Common/src/main/java/malte0811/ferritecore/mixin/suffixarray/Config.java b/Common/src/main/java/malte0811/ferritecore/mixin/suffixarray/Config.java new file mode 100644 index 0000000..9f851b3 --- /dev/null +++ b/Common/src/main/java/malte0811/ferritecore/mixin/suffixarray/Config.java @@ -0,0 +1,10 @@ +package malte0811.ferritecore.mixin.suffixarray; + +import malte0811.ferritecore.mixin.config.FerriteConfig; +import malte0811.ferritecore.mixin.config.FerriteMixinConfig; + +public class Config extends FerriteMixinConfig { + public Config() { + super(FerriteConfig.PREDICATES); + } +} diff --git a/Common/src/main/java/malte0811/ferritecore/mixin/suffixarray/SuffixArrayMixin.java b/Common/src/main/java/malte0811/ferritecore/mixin/suffixarray/SuffixArrayMixin.java new file mode 100644 index 0000000..e1ce2c0 --- /dev/null +++ b/Common/src/main/java/malte0811/ferritecore/mixin/suffixarray/SuffixArrayMixin.java @@ -0,0 +1,26 @@ +package malte0811.ferritecore.mixin.suffixarray; + +import it.unimi.dsi.fastutil.chars.CharArrayList; +import it.unimi.dsi.fastutil.ints.IntList; +import malte0811.ferritecore.impl.CharListIntListWrapper; +import net.minecraft.client.searchtree.SuffixArray; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +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; + +@Mixin(SuffixArray.class) +public class SuffixArrayMixin { + @Mutable + @Shadow + @Final + private IntList chars; + + @Inject(method = "", at = @At("TAIL")) + private void wrapCharListAsIntList(CallbackInfo ci) { + chars = new CharListIntListWrapper(new CharArrayList()); + } +} diff --git a/Common/src/main/java/malte0811/ferritecore/util/CollectionUtil.java b/Common/src/main/java/malte0811/ferritecore/util/CollectionUtil.java new file mode 100644 index 0000000..6d0bbb0 --- /dev/null +++ b/Common/src/main/java/malte0811/ferritecore/util/CollectionUtil.java @@ -0,0 +1,26 @@ +package malte0811.ferritecore.util; + +import it.unimi.dsi.fastutil.objects.ObjectArrayList; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public interface CollectionUtil { + @SuppressWarnings("unchecked") + static List minimize(List l) { + if (l.isEmpty()) + return List.of(); + else if (l.size() < 3) // java provides elements in fields for 1, 2 sizes + return List.copyOf(l); + + if (l instanceof ArrayList) + ((ArrayList)l).trimToSize(); + if (l instanceof ObjectArrayList) + ((ObjectArrayList)l).trim(); + else + return Arrays.asList((E[])l.toArray()); + + return l; + } +} diff --git a/Common/src/main/resources/ferritecore.accessors.mixin.json b/Common/src/main/resources/ferritecore.accessors.mixin.json index 23f6bc4..0df02cf 100644 --- a/Common/src/main/resources/ferritecore.accessors.mixin.json +++ b/Common/src/main/resources/ferritecore.accessors.mixin.json @@ -3,7 +3,7 @@ "package": "malte0811.ferritecore.mixin.accessors", "compatibilityLevel": "JAVA_17", "client": [ - "BakedQuadAccess" + "BakedQuadAccess", "UnihexProviderAccess" ], "injectors": { "defaultRequire": 1 diff --git a/Common/src/main/resources/ferritecore.blockmodellists.mixin.json b/Common/src/main/resources/ferritecore.blockmodellists.mixin.json new file mode 100644 index 0000000..e4846cb --- /dev/null +++ b/Common/src/main/resources/ferritecore.blockmodellists.mixin.json @@ -0,0 +1,14 @@ +{ + "required": true, + "package": "malte0811.ferritecore.mixin.blockmodellists", + "compatibilityLevel": "JAVA_17", + "client": [ + "BlockModelDeserializerMixin" + ], + "injectors": { + "defaultRequire": 1 + }, + "minVersion": "0.8", + "plugin": "malte0811.ferritecore.mixin.blockmodellists.Config", + "refmap": "${refmap_target}refmap.json" +} diff --git a/Common/src/main/resources/ferritecore.compactunihex.mixin.json b/Common/src/main/resources/ferritecore.compactunihex.mixin.json new file mode 100644 index 0000000..20641d0 --- /dev/null +++ b/Common/src/main/resources/ferritecore.compactunihex.mixin.json @@ -0,0 +1,12 @@ +{ + "required": true, + "package": "malte0811.ferritecore.mixin.compactunihex", + "compatibilityLevel": "JAVA_17", + "client": ["UnihexByteContentsMixin", "UnihexShortContentsMixin"], + "injectors": { + "defaultRequire": 1 + }, + "minVersion": "0.8", + "plugin": "malte0811.ferritecore.mixin.compactunihex.Config", + "refmap": "${refmap_target}refmap.json" +} diff --git a/Common/src/main/resources/ferritecore.suffixarray.mixin.json b/Common/src/main/resources/ferritecore.suffixarray.mixin.json new file mode 100644 index 0000000..2d04d21 --- /dev/null +++ b/Common/src/main/resources/ferritecore.suffixarray.mixin.json @@ -0,0 +1,14 @@ +{ + "required": true, + "package": "malte0811.ferritecore.mixin.suffixarray", + "compatibilityLevel": "JAVA_17", + "client": [ + "SuffixArrayMixin" + ], + "injectors": { + "defaultRequire": 1 + }, + "minVersion": "0.8", + "plugin": "malte0811.ferritecore.mixin.suffixarray.Config", + "refmap": "${refmap_target}refmap.json" +} diff --git a/Common/src/test/java/malte0811/ferritecore/compactunihex/CompactLineDataContentsTest.java b/Common/src/test/java/malte0811/ferritecore/compactunihex/CompactLineDataContentsTest.java new file mode 100644 index 0000000..934c483 --- /dev/null +++ b/Common/src/test/java/malte0811/ferritecore/compactunihex/CompactLineDataContentsTest.java @@ -0,0 +1,31 @@ +package malte0811.ferritecore.compactunihex; + +import malte0811.ferritecore.impl.compactunihex.CompactByteContents; +import malte0811.ferritecore.impl.compactunihex.CompactShortContents; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class CompactLineDataContentsTest { + @Test + public void testShortContents() { + short[] shorts = new short[]{ + 0x1010, 0x2020, 0x3030, 0x4040, 0x5050, 0x6060, 0x7070, (short)0x8080, + 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, (short)0x8888 + }; + CompactShortContents compactShortContents = new CompactShortContents(shorts); + for (int i = 0; i < shorts.length; i++) + Assertions.assertEquals(shorts[i] << 16, compactShortContents.line(i)); + + } + + @Test + public void testByteContents() { + byte[] bytes = new byte[]{ + 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, (byte)0x80, + 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, (byte)0x88 + }; + CompactByteContents compactByteContents = new CompactByteContents(bytes); + for (int i = 0; i < bytes.length; i++) + Assertions.assertEquals(bytes[i] << 24, compactByteContents.line(i)); + } +} diff --git a/Fabric/src/main/templates/fabric.mod.json b/Fabric/src/main/templates/fabric.mod.json index 006888d..e2512ef 100644 --- a/Fabric/src/main/templates/fabric.mod.json +++ b/Fabric/src/main/templates/fabric.mod.json @@ -32,6 +32,9 @@ "ferritecore.dedupbakedquad.mixin.json", "ferritecore.threaddetec.mixin.json", "ferritecore.modelsides.mixin.json", + "ferritecore.blockmodellists.mixin.json", + "ferritecore.compactunihex.mixin.json", + "ferritecore.suffixarray.mixin.json", "ferritecore.fabric.mixin.json" ], "custom": { diff --git a/NeoForge/src/main/templates/META-INF/neoforge.mods.toml b/NeoForge/src/main/templates/META-INF/neoforge.mods.toml index 6ea0af7..ccad7da 100644 --- a/NeoForge/src/main/templates/META-INF/neoforge.mods.toml +++ b/NeoForge/src/main/templates/META-INF/neoforge.mods.toml @@ -43,3 +43,9 @@ config="ferritecore.threaddetec.mixin.json" config="ferritecore.modelsides.mixin.json" [[mixins]] config="ferritecore.accessors.mixin.json" +[[mixins]] +config="ferritecore.blockmodellists.mixin.json" +[[mixins]] +config="ferritecore.compactunihex.mixin.json" +[[mixins]] +config="ferritecore.suffixarray.mixin.json" diff --git a/summary.md b/summary.md index 4dc6d9d..d26cff3 100644 --- a/summary.md +++ b/summary.md @@ -11,11 +11,11 @@ This change is made obsolete by the 4th point, it is only included in this list The vanilla implementation contains code along these lines: ```java -Optional opt=newlyCreatedOptional(); -if(!opt.isPresent()){ +Optional opt = newlyCreatedOptional(); +if (!opt.isPresent()) { // Something -}else{ - return()->doThing(opt.get()); +} else { + return () -> doThing(opt.get()); } ``` @@ -67,7 +67,7 @@ new `getValues` method returning a `map` rather than an `ImmutableMap` should be ### 4. Multipart model predicate caching Each multipart model stores a number of predicates to determine which parts to show under what conditions. These -predicates take up 300-400 MB. However in many cases these predicates are checking the same thing, they are just newly +predicates take up 300-400 MB. However, in many cases these predicates are checking the same thing, they are just newly created every time. For `KeyValueCondition` the predicates can be cached by using the property and its value as a key, for `And/OrCondition` ( and multi-value `KeyValueCondition`s) the key is the list of input predicates sorted by hash value. @@ -79,7 +79,7 @@ usages of multipart models is pipes, where the states are nearly always boolean result the number of predicates is reduced from between 10s of thousands and millions to a few ten or hundred instances. Saved memory: 300-400 MB (relative to the state after the first change, so 100 MB more compared to a "clean" instance) -CPU impact: Some impact in model loading (but less allocations), zero while playing +CPU impact: Some impact in model loading (but fewer allocations), zero while playing Side: client Mixin subpackage: `predicates` @@ -101,7 +101,7 @@ first part would require changing what constructor the constructor in question r ### 6. Multipart model instances -By default every blockstate using a multipart model gets its own instance of that multipart model. Since multipart +By default, every blockstate using a multipart model gets its own instance of that multipart model. Since multipart models are generally used for blocks with a lot of states this means a lot of instances, and a lot of wasted memory. The only input data for a multipart model is a `List, IBakedModel>>`. The predicate is already deduplicated by point 4, so it is very easy to use the same instance for equivalent lists. This reduces the number of