From cb6399e820eb9828a69bfcabaf7ab900fd86520f Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Tue, 21 Feb 2023 11:26:46 -0500 Subject: [PATCH] Port faster texture stitching by SuperCoder79 --- .../core/config/ModernFixEarlyConfig.java | 1 + .../StitcherMixin.java | 54 +++++++++++ .../modernfix/textures/StbStitcher.java | 90 +++++++++++++++++++ .../resources/META-INF/accesstransformer.cfg | 3 +- src/main/resources/modernfix.mixins.json | 1 + 5 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/embeddedt/modernfix/mixin/perf/faster_texture_stitching/StitcherMixin.java create mode 100644 src/main/java/org/embeddedt/modernfix/textures/StbStitcher.java 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 7add26e8..d1a1ceb3 100644 --- a/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java +++ b/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java @@ -44,6 +44,7 @@ public class ModernFixEarlyConfig { this.addMixinRule("perf.cache_model_materials", true); this.addMixinRule("perf.datapack_reload_exceptions", true); this.addMixinRule("perf.async_locator", true); + this.addMixinRule("perf.faster_texture_stitching", true); this.addMixinRule("perf.kubejs", true); /* Keep this off if JEI isn't installed to prevent breaking vanilla gameplay */ this.addMixinRule("perf.blast_search_trees", FMLLoader.getLoadingModList().getModFileById("jei") != null); diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_texture_stitching/StitcherMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_texture_stitching/StitcherMixin.java new file mode 100644 index 00000000..247f96ce --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_texture_stitching/StitcherMixin.java @@ -0,0 +1,54 @@ +package org.embeddedt.modernfix.mixin.perf.faster_texture_stitching; + +import com.mojang.datafixers.util.Pair; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import net.minecraft.client.renderer.texture.Stitcher; +import org.embeddedt.modernfix.textures.StbStitcher; +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.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.Set; + +@Mixin(Stitcher.class) +public class StitcherMixin { + @Shadow @Final private Set texturesToBeStitched; + + @Shadow private int storageX; + + @Shadow private int storageY; + + @Shadow @Final private static Comparator HOLDER_COMPARATOR; + private List loadableSpriteInfos; + + /** + * @author embeddedt, SuperCoder79 + * @reason Use improved STB stitcher instead of the vanilla implementation, for performance + */ + @Overwrite + public void stitch() { + ObjectArrayList holderList = new ObjectArrayList<>(this.texturesToBeStitched); + holderList.sort(HOLDER_COMPARATOR); + Stitcher.Holder[] aholder = holderList.toArray(new Stitcher.Holder[0]); + + Pair, List> packingInfo = StbStitcher.packRects(aholder); + this.storageX = packingInfo.getFirst().getFirst(); + this.storageY = packingInfo.getFirst().getSecond(); + this.loadableSpriteInfos = packingInfo.getSecond(); + } + + /** + * @author embeddedt, SuperCoder79 + * @reason We setup the image ourselves in the StbStitcher, so we just feed this information back into the vanilla code + */ + @Overwrite + public void gatherSprites(Stitcher.SpriteLoader spriteLoader) { + for(StbStitcher.LoadableSpriteInfo info : loadableSpriteInfos) { + spriteLoader.load(info.info, info.width, info.height, info.x, info.y); + } + } +} diff --git a/src/main/java/org/embeddedt/modernfix/textures/StbStitcher.java b/src/main/java/org/embeddedt/modernfix/textures/StbStitcher.java new file mode 100644 index 00000000..cc386233 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/textures/StbStitcher.java @@ -0,0 +1,90 @@ +package org.embeddedt.modernfix.textures; + +import com.google.common.collect.ImmutableList; +import com.mojang.datafixers.util.Pair; +import net.minecraft.client.renderer.texture.Stitcher; +import net.minecraft.client.renderer.texture.StitcherException; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.util.Mth; +import org.lwjgl.stb.STBRPContext; +import org.lwjgl.stb.STBRPNode; +import org.lwjgl.stb.STBRPRect; +import org.lwjgl.stb.STBRectPack; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + +/* Source: https://github.com/GTNewHorizons/lwjgl3ify/blob/f21364cd3d178aef863458a2faa1f5718a4e350d/src/main/java/me/eigenraven/lwjgl3ify/textures/StbStitcher.java */ +public class StbStitcher { + public static Pair, List> packRects(Stitcher.Holder[] holders) { + int holderSize = holders.length; + + List infoList = new ArrayList<>(); + + // Allocate memory for the rectangles and the context + try (STBRPRect.Buffer rectBuf = STBRPRect.malloc(holderSize); + STBRPContext ctx = STBRPContext.malloc(); ) { + + // Initialize the rectangles that we'll be using in the calculation + // While that's happening, sum up the area needed to fit all of the images + int sqSize = 0; + for (int j = 0; j < holderSize; ++j) { + Stitcher.Holder holder = holders[j]; + + int width = holder.width; + int height = holder.height; + + // The ID here is just the array index, for easy lookup later + rectBuf.get(j).set(j, (short)width, (short)height, (short)0, (short)0, false); + + sqSize += (width * height); + } + + int size = Mth.smallestEncompassingPowerOfTwo((int) Math.sqrt(sqSize)); + int width = size * 2; // needed to fix weirdness in 1.16 + int height = size; + + // Internal node structure needed for STB + try (STBRPNode.Buffer nodes = STBRPNode.malloc(width + 10)) { + // Initialize the rect packer + STBRectPack.stbrp_init_target(ctx, width, height, nodes); + + // Perform rectangle packing + STBRectPack.stbrp_pack_rects(ctx, rectBuf); + + for (STBRPRect rect : rectBuf) { + Stitcher.Holder holder = holders[rect.id()]; + + // Ensure that everything is properly packed! + if (!rect.was_packed()) { + throw new StitcherException(holder.spriteInfo, + Stream.of(holders).map(arg -> arg.spriteInfo).collect(ImmutableList.toImmutableList())); + } + + // Initialize the sprite now with the position and size that we've calculated so far + infoList.add(new LoadableSpriteInfo(holder.spriteInfo, width, height, rect.x(), rect.y())); + //holder.spriteInfo.initSprite(size, size, rect.x(), rect.y(), false); + } + + return Pair.of(Pair.of(width, height), infoList); + } + } + } + + public static class LoadableSpriteInfo { + public final TextureAtlasSprite.Info info; + public final int width; + public final int height; + public final int x; + public final int y; + + LoadableSpriteInfo(TextureAtlasSprite.Info info, int width, int height, int x, int y) { + this.info = info; + this.width = width; + this.height = height; + this.x = x; + this.y = y; + } + } +} diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index aa68abed..0d376b05 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -4,4 +4,5 @@ public net.minecraft.client.renderer.RenderType$CompositeRenderType (Ljava public net.minecraft.world.level.block.state.BlockBehaviour$BlockStateBase$Cache public net.minecraft.world.phys.shapes.VoxelShape (Lnet/minecraft/util/math/shapes/VoxelShapePart;)V # public net.minecraft.client.resources.model.ModelBakery$BlockStateDefinitionException -public net.minecraft.client.renderer.model.ModelBakery field_217849_F # unbakedCache \ No newline at end of file +public net.minecraft.client.renderer.model.ModelBakery field_217849_F # unbakedCache +public net.minecraft.client.renderer.texture.Stitcher$Holder \ No newline at end of file diff --git a/src/main/resources/modernfix.mixins.json b/src/main/resources/modernfix.mixins.json index 452ed23a..80a036d6 100644 --- a/src/main/resources/modernfix.mixins.json +++ b/src/main/resources/modernfix.mixins.json @@ -81,6 +81,7 @@ "perf.faster_baking.ModelManagerMixin", "perf.cache_model_materials.VanillaModelMixin", "perf.cache_model_materials.MultipartMixin", + "perf.faster_texture_stitching.StitcherMixin", "bugfix.packet_leak.ClientPlayNetHandlerMixin", "bugfix.packet_leak.SCustomPayloadPlayPacketMixin" ],