diff --git a/build.gradle b/build.gradle index 094b8f38..15ebda6f 100644 --- a/build.gradle +++ b/build.gradle @@ -99,8 +99,8 @@ dependencies { modRuntimeOnly("curse.maven:ferritecore-429235:4117906") modCompileOnly("team.chisel.ctm:CTM:${ctm_version}") modCompileOnly("curse.maven:supermartijncore-454372:4455391") - modImplementation("appeng:appliedenergistics2-forge:12.9.4") - modImplementation("vazkii.patchouli:Patchouli:1.19.2-77") + modCompileOnly("appeng:appliedenergistics2-forge:12.9.4") + modCompileOnly("vazkii.patchouli:Patchouli:1.19.2-77") } tasks.withType(JavaCompile) { 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 59afca12..630d1f82 100644 --- a/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java +++ b/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java @@ -72,6 +72,7 @@ public class ModernFixEarlyConfig { this.addMixinRule("perf.patchouli_deduplicate_books", modPresent("patchouli")); this.addMixinRule("perf.datapack_reload_exceptions", true); this.addMixinRule("perf.faster_texture_stitching", true); + this.addMixinRule("perf.faster_texture_loading", true); /* off by default in 1.18 because it doesn't work as well */ this.addMixinRule("perf.faster_singleplayer_load", false); /* Keep this off if JEI/REI 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..6d614c9e --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_texture_loading/TextureAtlasMixin.java @@ -0,0 +1,113 @@ +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.io.IOUtils; +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(() -> { + InputStream stream = null; + try { + ResourceLocation fileLocation = this.getResourceLocation(location); + Optional resourceOpt = manager.getResource(fileLocation); + if(!resourceOpt.isPresent()) { + ModernFix.LOGGER.error("Using missing texture, unable to load {}", location); + return; + } + Resource resource = resourceOpt.get(); + stream = resource.open(); + NativeImage image = NativeImage.read(stream); + AnimationMetadataSection animData = resource.metadata().getSection(AnimationMetadataSection.SERIALIZER).orElse(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)); + stream.close(); + stream = null; + } 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); + } + if(stream != null) + IOUtils.closeQuietly(stream); + }, 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 storageX, int storageY, int mipLevel, int x, int y, 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(), storageX, storageY, x, y, mipLevel, pair.getSecond()); + if(sprite == null) + sprite = new TextureAtlasSprite((TextureAtlas)(Object)this, spriteInfo, mipLevel, storageX, storageY, x, y, pair.getSecond()); + } catch(IOException | RuntimeException e) { + ModernFix.LOGGER.error("Error loading texture {}: {}", spriteInfo.name(), e); + } + cir.setReturnValue(sprite); + } + } +} diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index b4a249f0..983580f6 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -26,3 +26,4 @@ public net.minecraft.world.level.block.state.BlockBehaviour$Properties f_60889_ public net.minecraft.world.level.block.state.BlockBehaviour$Properties f_60888_ # destroyTime public net.minecraft.server.level.ServerChunkCache$MainThreadExecutor public net.minecraft.nbt.CompoundTag (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 # diff --git a/src/main/resources/modernfix.mixins.json b/src/main/resources/modernfix.mixins.json index 86a50955..41a48386 100644 --- a/src/main/resources/modernfix.mixins.json +++ b/src/main/resources/modernfix.mixins.json @@ -75,6 +75,7 @@ "perf.cache_model_materials.BlockModelMixin", "perf.cache_model_materials.MultipartMixin", "perf.faster_texture_stitching.StitcherMixin", + "perf.faster_texture_loading.TextureAtlasMixin", "perf.skip_first_datapack_reload.CreateWorldScreenMixin", "devenv.MinecraftMixin", "devenv.NarratorMixin"