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) {
+
+ }
+}