From 69f08abe8cbdb6c30190c85ea23ec7d9044e8881 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Sat, 3 May 2025 14:43:31 -0400 Subject: [PATCH] Merge remote-tracking branch 'origin/1.20' into 1.21.1 --- .../faster_ingredients/IngredientMixin.java | 29 +++++++++++-- .../neoforge/recipe/ExtendedIngredient.java | 5 +++ .../IngredientItemStacksSoftReference.java | 42 +++++++++++++++++++ 3 files changed, 72 insertions(+), 4 deletions(-) create mode 100644 neoforge/src/main/java/org/embeddedt/modernfix/neoforge/recipe/ExtendedIngredient.java create mode 100644 neoforge/src/main/java/org/embeddedt/modernfix/neoforge/recipe/IngredientItemStacksSoftReference.java diff --git a/neoforge/src/main/java/org/embeddedt/modernfix/neoforge/mixin/perf/faster_ingredients/IngredientMixin.java b/neoforge/src/main/java/org/embeddedt/modernfix/neoforge/mixin/perf/faster_ingredients/IngredientMixin.java index f372b031..be40c6bf 100644 --- a/neoforge/src/main/java/org/embeddedt/modernfix/neoforge/mixin/perf/faster_ingredients/IngredientMixin.java +++ b/neoforge/src/main/java/org/embeddedt/modernfix/neoforge/mixin/perf/faster_ingredients/IngredientMixin.java @@ -8,6 +8,8 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStackLinkedSet; import net.minecraft.world.item.crafting.Ingredient; import net.neoforged.neoforge.common.crafting.ICustomIngredient; +import org.embeddedt.modernfix.neoforge.recipe.ExtendedIngredient; +import org.embeddedt.modernfix.neoforge.recipe.IngredientItemStacksSoftReference; import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -22,7 +24,7 @@ import java.util.ArrayList; import java.util.stream.Collectors; @Mixin(value = Ingredient.class, priority = 700) -public abstract class IngredientMixin { +public abstract class IngredientMixin implements ExtendedIngredient { @Shadow @Final private Ingredient.Value[] values; @@ -39,6 +41,8 @@ public abstract class IngredientMixin { return !this.isCustom(); } + private volatile IngredientItemStacksSoftReference mfix$cachedItemStacks; + /** * @author embeddedt * @reason tag ingredients can be tested without iterating over all items @@ -109,9 +113,8 @@ public abstract class IngredientMixin { /** * @author embeddedt - * @reason remove caching of the item stacks, it won't deduplicate anything with tags (since each Ingredient - * instance would make new item stacks anyway, and storing them permanently takes up a lot of memory). - * We implement an optimized version of some functions that avoids needing to call this entirely. + * @reason Change caching of item stacks to use a soft reference, which allows the GC to evict the array under + * memory pressure/when it hasn't been used. */ @Overwrite public ItemStack[] getItems() { @@ -124,6 +127,19 @@ public abstract class IngredientMixin { this.itemStacks = this.customIngredient.getItems().collect(Collectors.toCollection(ItemStackLinkedSet::createTypeAndComponentsSet)).toArray(ItemStack[]::new); return this.itemStacks; } + var cache = this.mfix$cachedItemStacks; + if (cache != null) { + var stacks = cache.get(); + if (stacks != null) { + return stacks; + } + } + ItemStack[] result = computeItemsArray(); + this.mfix$cachedItemStacks = new IngredientItemStacksSoftReference((Ingredient)(Object)this, result); + return result; + } + + private ItemStack[] computeItemsArray() { // Fast path for case with one item if (this.values.length == 1) { if (this.values[0] instanceof Ingredient.ItemValue itemValue) { @@ -150,4 +166,9 @@ public abstract class IngredientMixin { } return itemList.toArray(ItemStack[]::new); } + + @Override + public void mfix$clearReference() { + this.mfix$cachedItemStacks = null; + } } diff --git a/neoforge/src/main/java/org/embeddedt/modernfix/neoforge/recipe/ExtendedIngredient.java b/neoforge/src/main/java/org/embeddedt/modernfix/neoforge/recipe/ExtendedIngredient.java new file mode 100644 index 00000000..8d78a8c2 --- /dev/null +++ b/neoforge/src/main/java/org/embeddedt/modernfix/neoforge/recipe/ExtendedIngredient.java @@ -0,0 +1,5 @@ +package org.embeddedt.modernfix.neoforge.recipe; + +public interface ExtendedIngredient { + void mfix$clearReference(); +} diff --git a/neoforge/src/main/java/org/embeddedt/modernfix/neoforge/recipe/IngredientItemStacksSoftReference.java b/neoforge/src/main/java/org/embeddedt/modernfix/neoforge/recipe/IngredientItemStacksSoftReference.java new file mode 100644 index 00000000..ab118a96 --- /dev/null +++ b/neoforge/src/main/java/org/embeddedt/modernfix/neoforge/recipe/IngredientItemStacksSoftReference.java @@ -0,0 +1,42 @@ +package org.embeddedt.modernfix.neoforge.recipe; + +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; + +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; + +public class IngredientItemStacksSoftReference extends SoftReference { + private final Ingredient ingredient; + + private static final ReferenceQueue QUEUE = new ReferenceQueue<>(); + private static final Thread DISCARD_THREAD = new Thread(IngredientItemStacksSoftReference::clearReferences, "Ingredient reference clearing thread"); + + static { + DISCARD_THREAD.setPriority(Thread.NORM_PRIORITY + 2); + DISCARD_THREAD.setDaemon(true); + DISCARD_THREAD.start(); + } + + public IngredientItemStacksSoftReference(Ingredient ingredient, ItemStack[] stacks) { + super(stacks, QUEUE); + this.ingredient = ingredient; + } + + private static void clearReferences() { + while (true) { + Reference ref; + try { + ref = QUEUE.remove(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return; + } + if (ref instanceof IngredientItemStacksSoftReference ingRef && (Object)ingRef.ingredient instanceof ExtendedIngredient extIng) { + // Null out the reference to the SoftReference object, to allow the SoftReference itself to be garbage collected. + extIng.mfix$clearReference(); + } + } + } +}