diff --git a/README.MD b/README.MD index 26caac652..f28d9ad7a 100644 --- a/README.MD +++ b/README.MD @@ -79,3 +79,5 @@ * Embeddedt and Ferri_Arnus for [glsl-transformation-lib](https://github.com/TauMC/glsl-transformation-lib) * Also for work on [Monocle](https://github.com/ferriarnus/Monocle) which was used as a reference for implementing glsl-transformation-lib * Code from both glsl-transformation-lib and Monocle taken under their respective LGPL 3.0 licenses +* FalseTweaks + * FalsePattern for [FalseTweaks](https://github.com/falsepattern/falsetweaks) - Item Rendering Optimizations taken from FalseTweaks under the LGPL 3.0 license diff --git a/src/main/java/com/gtnewhorizons/angelica/config/AngelicaConfig.java b/src/main/java/com/gtnewhorizons/angelica/config/AngelicaConfig.java index dc40760b7..5d8f71025 100644 --- a/src/main/java/com/gtnewhorizons/angelica/config/AngelicaConfig.java +++ b/src/main/java/com/gtnewhorizons/angelica/config/AngelicaConfig.java @@ -137,4 +137,15 @@ public class AngelicaConfig { @Config.DefaultBoolean(true) @Config.RequiresMcRestart public static boolean enableZoom; + + @Config.Comment("Optimizes in-world item rendering") + @Config.DefaultBoolean(true) + @Config.RequiresMcRestart + public static boolean optimizeInWorldItemRendering; + + @Config.Comment("Max amount of display lists to cache for optimized item rendering. Higher number will use more VRAM") + @Config.DefaultInt(256) + @Config.RangeInt(min = 64, max = 1024) + public static int itemRendererDisplayListCacheSize; + } diff --git a/src/main/java/com/gtnewhorizons/angelica/mixins/Mixins.java b/src/main/java/com/gtnewhorizons/angelica/mixins/Mixins.java index f5b5318be..0d119013b 100644 --- a/src/main/java/com/gtnewhorizons/angelica/mixins/Mixins.java +++ b/src/main/java/com/gtnewhorizons/angelica/mixins/Mixins.java @@ -78,6 +78,11 @@ public enum Mixins { .setApplyIf(() -> AngelicaConfig.dynamicItemRenderDistance) .addTargetedMod(TargetedMod.VANILLA)), + ANGELICA_ITEM_DISPLAY_LIST_OPTIMIZATION(new Builder("Optimized item rendering by wrapping them with display lists") + .setPhase(Phase.EARLY).addMixinClasses("angelica.itemrenderer.MixinItemRenderer").setSide(Side.CLIENT) + .setApplyIf(() -> AngelicaConfig.optimizeInWorldItemRendering) + .addTargetedMod(TargetedMod.VANILLA)), + // Not compatible with the lwjgl debug callbacks, so disable if that's enabled ARCHAIC_SPLASH(new Builder("ArchaicFix Splash").addTargetedMod(TargetedMod.VANILLA).setSide(Side.CLIENT) .setPhase(Phase.EARLY).setApplyIf(() -> AngelicaConfig.showSplashMemoryBar && !AngelicaMod.lwjglDebug).addMixinClasses( diff --git a/src/main/java/com/gtnewhorizons/angelica/rendering/ItemRenderListManager.java b/src/main/java/com/gtnewhorizons/angelica/rendering/ItemRenderListManager.java new file mode 100644 index 000000000..1ad29b7e2 --- /dev/null +++ b/src/main/java/com/gtnewhorizons/angelica/rendering/ItemRenderListManager.java @@ -0,0 +1,111 @@ +/* + * This file is part of FalseTweaks. + * + * Copyright (C) 2022-2024 FalsePattern + * All Rights Reserved + * + * Modifications by Angelica in accordance with LGPL v3.0 + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * FalseTweaks is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * FalseTweaks is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with FalseTweaks. If not, see . + */ + +package com.gtnewhorizons.angelica.rendering; + +import com.gtnewhorizons.angelica.config.AngelicaConfig; +import lombok.AccessLevel; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.val; +import org.lwjgl.opengl.GL11; + +import net.minecraft.client.renderer.GLAllocation; +import net.minecraft.client.resources.IResourceManager; +import net.minecraft.client.resources.IResourceManagerReloadListener; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ItemRenderListManager implements IResourceManagerReloadListener { + public static final ItemRenderListManager INSTANCE = new ItemRenderListManager(); + + private final Map theMap = new HashMap<>(); + private final List propList = new ArrayList<>(); + private final ItemProp prop = new ItemProp(); + private int list = 0; + + public boolean pre(float a, float b, float c, float d, int e, int f, float g) { + prop.set(a, b, c, d, e, f, g); + if (theMap.containsKey(prop)) { + val list = theMap.get(prop); + propList.add(propList.remove(propList.indexOf(prop))); + GL11.glCallList(list); + return true; + } else { + if (propList.size() >= AngelicaConfig.itemRendererDisplayListCacheSize) { + val oldProp = propList.remove(0); + GLAllocation.deleteDisplayLists(theMap.remove(oldProp)); + } + list = GLAllocation.generateDisplayLists(1); + val newProp = new ItemProp(prop); + theMap.put(newProp, list); + propList.add(newProp); + GL11.glNewList(list, GL11.GL_COMPILE); + return false; + } + } + + public void post() { + GL11.glEndList(); + GL11.glCallList(list); + } + + @Override + public void onResourceManagerReload(IResourceManager p_110549_1_) { + propList.clear(); + theMap.forEach((key, value) -> GLAllocation.deleteDisplayLists(value)); + theMap.clear(); + } + + @NoArgsConstructor + @Data + public class ItemProp { + private float a; + private float b; + private float c; + private float d; + private int e; + private int f; + private float g; + + public ItemProp(ItemProp old) { + set(old.a, old.b, old.c, old.d, old.e, old.f, old.g); + } + + public void set(float a, float b, float c, float d, int e, int f, float g) { + this.a = a; + this.b = b; + this.c = c; + this.d = d; + this.e = e; + this.f = f; + this.g = g; + } + } +} diff --git a/src/main/java/me/jellysquid/mods/sodium/client/gui/SodiumGameOptionPages.java b/src/main/java/me/jellysquid/mods/sodium/client/gui/SodiumGameOptionPages.java index ccef1b313..a0b7f13dd 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/gui/SodiumGameOptionPages.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/gui/SodiumGameOptionPages.java @@ -372,6 +372,17 @@ public static OptionPage advanced() { ) .build()); + groups.add(OptionGroup.createBuilder() + .add(OptionImpl.createBuilder(int.class, angelicaOpts) + .setName(I18n.format("options.angelica.itemdisplaylistcount")) + .setTooltip(I18n.format("options.angelica.itemdisplaylistcount.tooltip")) + .setControl(o -> new SliderControl(o, 0, 1024, 1, ControlValueFormatter.number())) + .setImpact(OptionImpact.MEDIUM) + .setBinding((opts, value) -> AngelicaConfig.itemRendererDisplayListCacheSize = value, options -> AngelicaConfig.itemRendererDisplayListCacheSize) + .build() + ) + .build()); + groups.add(OptionGroup.createBuilder() .add(Settings.MODE_GUI_BACKGROUND.option) .add(Settings.GUI_BACKGROUND.option) diff --git a/src/main/resources/assets/angelica/lang/en_US.lang b/src/main/resources/assets/angelica/lang/en_US.lang index d42e50942..6739e072e 100644 --- a/src/main/resources/assets/angelica/lang/en_US.lang +++ b/src/main/resources/assets/angelica/lang/en_US.lang @@ -137,6 +137,8 @@ options.dynamic_lights_shader_force=Dynamic Lights - Forced With Shaders options.dynamic_lights_shader_force.tooltip=Enabled - Dynamic Lights are shown even when a shader is active, Disabled - Dynamic Lights are disabled when a shader is active. options.angelica.droppedItemLimit=Dropped Item Render Limit options.angelica.droppedItemLimit.tooltip=The maximum number of dropped items that will be rendered. Lower values can improve performance. +options.angelica.itemdisplaylistcount=Item Renderer Display List Cache Size +options.angelica.itemdisplaylistcount.tooltip=The maximum number of display lists to cache in the optimized item renderer. Higher values will increase performance but increase VRAM usage. pack.iris.select.title=Select pack.iris.configure.title=Configure label.iris.true=On diff --git a/src/mixin/java/com/gtnewhorizons/angelica/mixins/early/angelica/itemrenderer/MixinItemRenderer.java b/src/mixin/java/com/gtnewhorizons/angelica/mixins/early/angelica/itemrenderer/MixinItemRenderer.java new file mode 100644 index 000000000..5f6289dae --- /dev/null +++ b/src/mixin/java/com/gtnewhorizons/angelica/mixins/early/angelica/itemrenderer/MixinItemRenderer.java @@ -0,0 +1,87 @@ +/* + * This file is part of FalseTweaks. + * + * Copyright (C) 2022-2024 FalsePattern + * All Rights Reserved + * + * Modifications by Angelica in accordance with LGPL v3.0 + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * FalseTweaks is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * FalseTweaks is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with FalseTweaks. If not, see . + */ + +package com.gtnewhorizons.angelica.mixins.early.angelica.itemrenderer; + +import com.gtnewhorizons.angelica.rendering.ItemRenderListManager; +import lombok.SneakyThrows; +import net.minecraft.client.renderer.ItemRenderer; +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.Redirect; +import org.spongepowered.asm.mixin.injection.Slice; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import net.minecraft.client.renderer.Tessellator; + +@Mixin(ItemRenderer.class) +public abstract class MixinItemRenderer { + @SneakyThrows + @Inject(method = "renderItemIn2D", + at = @At("HEAD"), + cancellable = true, + require = 1) + private static void leFunnyRenderListStart(Tessellator tess, float a, float b, float c, float d, int e, int f, float g, CallbackInfo ci) { + if (ItemRenderListManager.INSTANCE.pre(a, b, c, d, e, f, g)) { + ci.cancel(); + } + } + + @Inject(method = "renderItemIn2D", + at = @At("RETURN"), + require = 1) + private static void leFunnyRenderListEnd(Tessellator tess, float a, float b, float c, float d, int e, int f, float g, CallbackInfo ci) { + ItemRenderListManager.INSTANCE.post(); + } + + @Redirect(method = "renderItemIn2D", + slice = @Slice(from = @At(value = "INVOKE", + target = "Lnet/minecraft/client/renderer/Tessellator;draw()I", + ordinal = 0), + to = @At(value = "INVOKE", + target = "Lnet/minecraft/client/renderer/Tessellator;startDrawingQuads()V", + ordinal = 5)), + at = @At(value = "INVOKE", + target = "Lnet/minecraft/client/renderer/Tessellator;draw()I"), + require = 5) + private static int batchDrawCalls1(Tessellator instance) { + return 0; + } + + @Redirect(method = "renderItemIn2D", + slice = @Slice(from = @At(value = "INVOKE", + target = "Lnet/minecraft/client/renderer/Tessellator;draw()I", + ordinal = 0), + to = @At(value = "INVOKE", + target = "Lnet/minecraft/client/renderer/Tessellator;startDrawingQuads()V", + ordinal = 5)), + at = @At(value = "INVOKE", + target = "Lnet/minecraft/client/renderer/Tessellator;startDrawingQuads()V"), + require = 5) + private static void batchDrawCalls2(Tessellator instance) { + + } +}