From 7bc7a5e9454c7e2762e0db6480809595a2276cce Mon Sep 17 00:00:00 2001 From: AnAwesomGuy <79700396+AnAwesomGuy@users.noreply.github.com> Date: Wed, 16 Apr 2025 20:28:26 +0800 Subject: [PATCH 1/3] commit --- .../ferritecore/impl/ModelSidesImpl.java | 3 +- .../compactunihex/CompactByteContents.java | 41 ++++++++++++++ .../compactunihex/CompactShortContents.java | 53 +++++++++++++++++++ .../BlockModelDeserializerMixin.java | 32 +++++++++++ .../mixin/blockmodellists/Config.java | 10 ++++ .../mixin/compactunihex/Config.java | 11 ++++ .../UnihexByteContentsMixin.java | 18 +++++++ .../UnihexShortContentsMixin.java | 18 +++++++ .../mixin/config/FerriteConfig.java | 10 ++++ .../ferritecore/util/CollectionUtil.java | 26 +++++++++ .../ferritecore.blockmodellists.mixin.json | 14 +++++ .../ferritecore.compactunihex.mixin.json | 12 +++++ .../CompactLineDataContentsTest.java | 31 +++++++++++ Fabric/src/main/templates/fabric.mod.json | 1 + summary.md | 14 ++--- 15 files changed, 286 insertions(+), 8 deletions(-) create mode 100644 Common/src/main/java/malte0811/ferritecore/impl/compactunihex/CompactByteContents.java create mode 100644 Common/src/main/java/malte0811/ferritecore/impl/compactunihex/CompactShortContents.java create mode 100644 Common/src/main/java/malte0811/ferritecore/mixin/blockmodellists/BlockModelDeserializerMixin.java create mode 100644 Common/src/main/java/malte0811/ferritecore/mixin/blockmodellists/Config.java create mode 100644 Common/src/main/java/malte0811/ferritecore/mixin/compactunihex/Config.java create mode 100644 Common/src/main/java/malte0811/ferritecore/mixin/compactunihex/UnihexByteContentsMixin.java create mode 100644 Common/src/main/java/malte0811/ferritecore/mixin/compactunihex/UnihexShortContentsMixin.java create mode 100644 Common/src/main/java/malte0811/ferritecore/util/CollectionUtil.java create mode 100644 Common/src/main/resources/ferritecore.blockmodellists.mixin.json create mode 100644 Common/src/main/resources/ferritecore.compactunihex.mixin.json create mode 100644 Common/src/test/java/malte0811/ferritecore/compactunihex/CompactLineDataContentsTest.java 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..cbf7966 --- /dev/null +++ b/Common/src/main/java/malte0811/ferritecore/impl/compactunihex/CompactByteContents.java @@ -0,0 +1,41 @@ +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..760c764 --- /dev/null +++ b/Common/src/main/java/malte0811/ferritecore/impl/compactunihex/CompactShortContents.java @@ -0,0 +1,53 @@ +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(long shorts1, long shorts2, long shorts3, long shorts4) { + this.shorts1 = shorts1; + this.shorts2 = shorts2; + this.shorts3 = shorts3; + this.shorts4 = 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/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..65b7a6d --- /dev/null +++ b/Common/src/main/java/malte0811/ferritecore/mixin/compactunihex/UnihexByteContentsMixin.java @@ -0,0 +1,18 @@ +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.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$ByteContents") +public class UnihexByteContentsMixin { + @Inject(method = "read", at = @At(value = "NEW", target = "([B)Lnet/minecraft/client/gui/font/providers/UnihexProvider$ByteContents;"), cancellable = true, locals = LocalCapture.CAPTURE_FAILSOFT) + private static void redirectByteContentsToOurs(int index, ByteList byteList, CallbackInfoReturnable cir, byte[] bytes, int i) { + cir.setReturnValue(new CompactByteContents(bytes)); + } +} 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..7a49b9a 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,8 @@ 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; static { ConfigBuilder builder = new ConfigBuilder(); @@ -74,6 +76,14 @@ 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." + ); builder.finish(); } 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.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/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..9897533 100644 --- a/Fabric/src/main/templates/fabric.mod.json +++ b/Fabric/src/main/templates/fabric.mod.json @@ -32,6 +32,7 @@ "ferritecore.dedupbakedquad.mixin.json", "ferritecore.threaddetec.mixin.json", "ferritecore.modelsides.mixin.json", + "ferritecore.blockmodellists.mixin.json", "ferritecore.fabric.mixin.json" ], "custom": { 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 From dcde2ca4651ef6c394f2a4f6081d4da07d074f9c Mon Sep 17 00:00:00 2001 From: AnAwesomGuy <79700396+AnAwesomGuy@users.noreply.github.com> Date: Thu, 17 Apr 2025 08:02:17 +0800 Subject: [PATCH 2/3] improve unihex stuff --- .../compactunihex/CompactByteContents.java | 1 - .../compactunihex/CompactShortContents.java | 7 --- .../mixin/accessors/UnihexProviderAccess.java | 17 ++++++ .../UnihexByteContentsMixin.java | 52 ++++++++++++++++--- .../ferritecore.accessors.mixin.json | 2 +- Fabric/src/main/templates/fabric.mod.json | 1 + .../templates/META-INF/neoforge.mods.toml | 4 ++ 7 files changed, 67 insertions(+), 17 deletions(-) create mode 100644 Common/src/main/java/malte0811/ferritecore/mixin/accessors/UnihexProviderAccess.java diff --git a/Common/src/main/java/malte0811/ferritecore/impl/compactunihex/CompactByteContents.java b/Common/src/main/java/malte0811/ferritecore/impl/compactunihex/CompactByteContents.java index cbf7966..899d627 100644 --- a/Common/src/main/java/malte0811/ferritecore/impl/compactunihex/CompactByteContents.java +++ b/Common/src/main/java/malte0811/ferritecore/impl/compactunihex/CompactByteContents.java @@ -11,7 +11,6 @@ public class CompactByteContents implements LineData { private final long lowerBytes; public CompactByteContents(long upperBytes, long lowerBytes) { - this.upperBytes = upperBytes; this.lowerBytes = lowerBytes; } diff --git a/Common/src/main/java/malte0811/ferritecore/impl/compactunihex/CompactShortContents.java b/Common/src/main/java/malte0811/ferritecore/impl/compactunihex/CompactShortContents.java index 760c764..3dd8357 100644 --- a/Common/src/main/java/malte0811/ferritecore/impl/compactunihex/CompactShortContents.java +++ b/Common/src/main/java/malte0811/ferritecore/impl/compactunihex/CompactShortContents.java @@ -12,13 +12,6 @@ public class CompactShortContents implements LineData { private final long shorts3; private final long shorts4; - public CompactShortContents(long shorts1, long shorts2, long shorts3, long shorts4) { - this.shorts1 = shorts1; - this.shorts2 = shorts2; - this.shorts3 = shorts3; - this.shorts4 = shorts4; - } - public CompactShortContents(short[] shorts) { if (shorts.length != 16) throw new IllegalArgumentException(); 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/compactunihex/UnihexByteContentsMixin.java b/Common/src/main/java/malte0811/ferritecore/mixin/compactunihex/UnihexByteContentsMixin.java index 65b7a6d..fe0ccaf 100644 --- a/Common/src/main/java/malte0811/ferritecore/mixin/compactunihex/UnihexByteContentsMixin.java +++ b/Common/src/main/java/malte0811/ferritecore/mixin/compactunihex/UnihexByteContentsMixin.java @@ -2,17 +2,53 @@ import it.unimi.dsi.fastutil.bytes.ByteList; import malte0811.ferritecore.impl.compactunihex.CompactByteContents; -import net.minecraft.client.gui.font.providers.UnihexProvider.LineData; +import net.minecraft.client.gui.font.providers.UnihexProvider; 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; +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 { - @Inject(method = "read", at = @At(value = "NEW", target = "([B)Lnet/minecraft/client/gui/font/providers/UnihexProvider$ByteContents;"), cancellable = true, locals = LocalCapture.CAPTURE_FAILSOFT) - private static void redirectByteContentsToOurs(int index, ByteList byteList, CallbackInfoReturnable cir, byte[] bytes, int i) { - cir.setReturnValue(new CompactByteContents(bytes)); + /** + * @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/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/Fabric/src/main/templates/fabric.mod.json b/Fabric/src/main/templates/fabric.mod.json index 9897533..922ddbb 100644 --- a/Fabric/src/main/templates/fabric.mod.json +++ b/Fabric/src/main/templates/fabric.mod.json @@ -33,6 +33,7 @@ "ferritecore.threaddetec.mixin.json", "ferritecore.modelsides.mixin.json", "ferritecore.blockmodellists.mixin.json", + "ferritecore.compactunihex.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..035598d 100644 --- a/NeoForge/src/main/templates/META-INF/neoforge.mods.toml +++ b/NeoForge/src/main/templates/META-INF/neoforge.mods.toml @@ -43,3 +43,7 @@ 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" From bdc2d5b84a2915fe9f34534dac448c8df88bb3fb Mon Sep 17 00:00:00 2001 From: AnAwesomGuy <79700396+AnAwesomGuy@users.noreply.github.com> Date: Tue, 22 Apr 2025 07:51:37 +0800 Subject: [PATCH 3/3] suffix array stuff --- .../impl/CharListIntListWrapper.java | 105 ++++++++++++++++++ .../mixin/config/FerriteConfig.java | 5 + .../ferritecore/mixin/suffixarray/Config.java | 10 ++ .../mixin/suffixarray/SuffixArrayMixin.java | 26 +++++ .../ferritecore.suffixarray.mixin.json | 14 +++ Fabric/src/main/templates/fabric.mod.json | 1 + .../templates/META-INF/neoforge.mods.toml | 2 + 7 files changed, 163 insertions(+) create mode 100644 Common/src/main/java/malte0811/ferritecore/impl/CharListIntListWrapper.java create mode 100644 Common/src/main/java/malte0811/ferritecore/mixin/suffixarray/Config.java create mode 100644 Common/src/main/java/malte0811/ferritecore/mixin/suffixarray/SuffixArrayMixin.java create mode 100644 Common/src/main/resources/ferritecore.suffixarray.mixin.json 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/mixin/config/FerriteConfig.java b/Common/src/main/java/malte0811/ferritecore/mixin/config/FerriteConfig.java index 7a49b9a..6416232 100644 --- a/Common/src/main/java/malte0811/ferritecore/mixin/config/FerriteConfig.java +++ b/Common/src/main/java/malte0811/ferritecore/mixin/config/FerriteConfig.java @@ -25,6 +25,7 @@ public class FerriteConfig { 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(); @@ -84,6 +85,10 @@ public class FerriteConfig { "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/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/Fabric/src/main/templates/fabric.mod.json b/Fabric/src/main/templates/fabric.mod.json index 922ddbb..e2512ef 100644 --- a/Fabric/src/main/templates/fabric.mod.json +++ b/Fabric/src/main/templates/fabric.mod.json @@ -34,6 +34,7 @@ "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 035598d..ccad7da 100644 --- a/NeoForge/src/main/templates/META-INF/neoforge.mods.toml +++ b/NeoForge/src/main/templates/META-INF/neoforge.mods.toml @@ -47,3 +47,5 @@ config="ferritecore.accessors.mixin.json" config="ferritecore.blockmodellists.mixin.json" [[mixins]] config="ferritecore.compactunihex.mixin.json" +[[mixins]] +config="ferritecore.suffixarray.mixin.json"