From 91d1cb3962f260cff8afbb68dc0b20555695b444 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Wed, 26 Apr 2023 19:11:39 -0400 Subject: [PATCH] Bypass slow PngInfo class during texture loading Thanks to @Asek3 for pointing out this bottleneck --- .../core/config/ModernFixEarlyConfig.java | 1 + .../TextureAtlasMixin.java | 110 ++++++++++++++++++ .../resources/META-INF/accesstransformer.cfg | 3 +- src/main/resources/modernfix.mixins.json | 2 + 4 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/embeddedt/modernfix/mixin/perf/faster_texture_loading/TextureAtlasMixin.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 647aaed0..0f859f12 100644 --- a/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java +++ b/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java @@ -88,6 +88,7 @@ public class ModernFixEarlyConfig { this.addMixinRule("perf.datapack_reload_exceptions", true); this.addMixinRule("perf.async_locator", true); this.addMixinRule("perf.faster_texture_stitching", true); + this.addMixinRule("perf.faster_texture_loading", true); this.addMixinRule("perf.kubejs", modPresent("kubejs")); this.addMixinRule("perf.faster_singleplayer_load", false); /* Keep this off if JEI isn't installed to prevent breaking vanilla gameplay */ diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_texture_loading/TextureAtlasMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_texture_loading/TextureAtlasMixin.java new file mode 100644 index 00000000..1e326451 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_texture_loading/TextureAtlasMixin.java @@ -0,0 +1,110 @@ +package org.embeddedt.modernfix.mixin.perf.faster_texture_loading; + +import com.mojang.blaze3d.platform.NativeImage; +import com.mojang.datafixers.util.Pair; +import net.minecraft.client.renderer.texture.MissingTextureAtlasSprite; +import net.minecraft.client.renderer.texture.TextureAtlas; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.resources.metadata.animation.AnimationMetadataSection; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.Resource; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraftforge.client.ForgeHooksClient; +import net.minecraftforge.fml.ModLoader; +import org.apache.commons.lang3.tuple.Triple; +import org.embeddedt.modernfix.ModernFix; +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.CallbackInfoReturnable; + +import java.io.IOException; +import java.io.InputStream; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; + +@Mixin(TextureAtlas.class) +public abstract class TextureAtlasMixin { + @Shadow protected abstract ResourceLocation getResourceLocation(ResourceLocation location); + + @Shadow protected abstract Collection getBasicSpriteInfos(ResourceManager resourceManager, Set spriteLocations); + + private Map> loadedImages; + private boolean usingFasterLoad; + /** + * @author embeddedt + * @reason simplify texture loading by loading whole image once, avoid slow PngInfo code + */ + @Redirect(method = "prepareToStitch", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/texture/TextureAtlas;getBasicSpriteInfos(Lnet/minecraft/server/packs/resources/ResourceManager;Ljava/util/Set;)Ljava/util/Collection;")) + private Collection loadImages(TextureAtlas atlas, ResourceManager manager, Set imageLocations) { + usingFasterLoad = ModLoader.isLoadingStateValid(); + // bail if Forge is erroring to avoid AT crashes + if(!usingFasterLoad) { + return getBasicSpriteInfos(manager, imageLocations); + } + List> futures = new ArrayList<>(); + ConcurrentLinkedQueue results = new ConcurrentLinkedQueue<>(); + loadedImages = new ConcurrentHashMap<>(); + for(ResourceLocation location : imageLocations) { + if(MissingTextureAtlasSprite.getLocation().equals(location)) + continue; + futures.add(CompletableFuture.runAsync(() -> { + try { + ResourceLocation fileLocation = this.getResourceLocation(location); + Resource resource = manager.getResource(fileLocation); + NativeImage image = NativeImage.read(resource.getInputStream()); + AnimationMetadataSection animData = resource.getMetadata(AnimationMetadataSection.SERIALIZER); + if (animData == null) { + animData = AnimationMetadataSection.EMPTY; + } + Pair dimensions = animData.getFrameSize(image.getWidth(), image.getHeight()); + loadedImages.put(location, Pair.of(resource, image)); + results.add(new TextureAtlasSprite.Info(location, dimensions.getFirst(), dimensions.getSecond(), animData)); + } catch(IOException e) { + ModernFix.LOGGER.error("Using missing texture, unable to load {} : {}", location, e); + } catch(RuntimeException e) { + ModernFix.LOGGER.error("Unable to parse metadata from {} : {}", location, e); + } + }, ModernFix.resourceReloadExecutor())); + } + CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join(); + return results; + } + + @Inject(method = "prepareToStitch", at = @At("RETURN")) + private void clearLoadedImages(CallbackInfoReturnable cir) { + loadedImages = Collections.emptyMap(); + } + + @Inject(method = "load(Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/client/renderer/texture/TextureAtlasSprite$Info;IIIII)Lnet/minecraft/client/renderer/texture/TextureAtlasSprite;", + at = @At("HEAD"), cancellable = true) + private void loadFromExisting(ResourceManager resourceManager, TextureAtlasSprite.Info spriteInfo, int width, int height, int mipmapLevel, int originX, int originY, CallbackInfoReturnable cir) { + if(!usingFasterLoad) + return; + Pair pair = loadedImages.get(spriteInfo.name()); + if(pair == null) { + ModernFix.LOGGER.error("Texture {} was not loaded in early stage", spriteInfo.name()); + cir.setReturnValue(null); + } else { + TextureAtlasSprite sprite = null; + try { + sprite = ForgeHooksClient.loadTextureAtlasSprite((TextureAtlas)(Object)this, resourceManager, spriteInfo, pair.getFirst(), width, height, originX, originY, mipmapLevel, pair.getSecond()); + if(sprite == null) + sprite = new TextureAtlasSprite((TextureAtlas)(Object)this, spriteInfo, mipmapLevel, width, height, originX, originY, pair.getSecond()); + } catch(RuntimeException e) { + ModernFix.LOGGER.error("Error loading texture {}: {}", spriteInfo.name(), e); + } finally { + try { + pair.getFirst().close(); + } catch(IOException ignored) { + // not much we can do + } + } + cir.setReturnValue(sprite); + } + } +} diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index ff8cdd75..22dba02c 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -24,4 +24,5 @@ public net.minecraft.block.AbstractBlock$Properties field_235819_u_ # emissiveRe public net.minecraft.block.AbstractBlock$Properties field_235806_h_ # requiresCorrectToolForDrops public net.minecraft.block.AbstractBlock$Properties field_200959_g # destroyTime public net.minecraft.world.server.ServerChunkProvider$ChunkExecutor -public net.minecraft.nbt.CompoundNBT (Ljava/util/Map;)V # \ No newline at end of file +public net.minecraft.nbt.CompoundNBT (Ljava/util/Map;)V # +public net.minecraft.client.renderer.texture.TextureAtlasSprite (Lnet/minecraft/client/renderer/texture/TextureAtlas;Lnet/minecraft/client/renderer/texture/TextureAtlasSprite$Info;IIIIILcom/mojang/blaze3d/platform/NativeImage;)V # \ No newline at end of file diff --git a/src/main/resources/modernfix.mixins.json b/src/main/resources/modernfix.mixins.json index 80bef802..c96497ec 100644 --- a/src/main/resources/modernfix.mixins.json +++ b/src/main/resources/modernfix.mixins.json @@ -75,6 +75,7 @@ "client": [ "core.MinecraftMixin", "core.SynchedEntityDataMixin", + "core.ModelBakeryMixin", "feature.measure_time.MinecraftMixin", "feature.reduce_loading_screen_freezes.ModelBakeryMixin", "perf.skip_first_datapack_reload.MinecraftMixin", @@ -112,6 +113,7 @@ "perf.cache_model_materials.BlockModelMixin", "perf.cache_model_materials.MultipartMixin", "perf.faster_texture_stitching.StitcherMixin", + "perf.faster_texture_loading.TextureAtlasMixin", "bugfix.packet_leak.ClientPlayNetHandlerMixin", "bugfix.packet_leak.SCustomPayloadPlayPacketMixin", "perf.reuse_datapacks.MinecraftMixin",