Merge 1.20 into 1.20.2

This commit is contained in:
embeddedt 2023-12-14 21:11:59 -05:00
commit 0eb70468a1
No known key found for this signature in database
GPG Key ID: A69433EC199B5613
6 changed files with 172 additions and 13 deletions

View File

@ -6,6 +6,7 @@ import net.minecraft.client.resources.model.ModelManager;
import net.minecraft.client.resources.model.ModelResourceLocation; import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin; import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.embeddedt.modernfix.dynamicresources.DynamicModelCache;
import org.embeddedt.modernfix.dynamicresources.ModelLocationCache; import org.embeddedt.modernfix.dynamicresources.ModelLocationCache;
import org.embeddedt.modernfix.util.DynamicOverridableMap; import org.embeddedt.modernfix.util.DynamicOverridableMap;
import org.spongepowered.asm.mixin.*; import org.spongepowered.asm.mixin.*;
@ -15,7 +16,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Map; import java.util.Map;
@Mixin(BlockModelShaper.class) @Mixin(BlockModelShaper.class)
@ClientOnlyMixin @ClientOnlyMixin
public class BlockModelShaperMixin { public class BlockModelShaperMixin {
@ -24,10 +24,25 @@ public class BlockModelShaperMixin {
@Shadow @Shadow
private Map<BlockState, BakedModel> modelByStateCache; private Map<BlockState, BakedModel> modelByStateCache;
private final DynamicModelCache<BlockState> mfix$modelCache = new DynamicModelCache<>(k -> this.cacheBlockModel((BlockState)k), false);
@Inject(method = { "<init>", "replaceCache" }, at = @At("RETURN")) @Inject(method = { "<init>", "replaceCache" }, at = @At("RETURN"))
private void replaceModelMap(CallbackInfo ci) { private void replaceModelMap(CallbackInfo ci) {
// replace the backing map for mods which will access it // replace the backing map for mods which will access it
this.modelByStateCache = new DynamicOverridableMap<>(state -> modelManager.getModel(ModelLocationCache.get(state))); this.modelByStateCache = new DynamicOverridableMap<>(state -> modelManager.getModel(ModelLocationCache.get(state)));
if(this.mfix$modelCache != null)
this.mfix$modelCache.clear();
}
private BakedModel cacheBlockModel(BlockState state) {
// Do all model system accesses in the unlocked path
ModelResourceLocation mrl = ModelLocationCache.get(state);
BakedModel model = mrl == null ? null : modelManager.getModel(mrl);
if (model == null) {
model = modelManager.getMissingModel();
}
return model;
} }
/** /**
@ -36,11 +51,6 @@ public class BlockModelShaperMixin {
*/ */
@Overwrite @Overwrite
public BakedModel getBlockModel(BlockState state) { public BakedModel getBlockModel(BlockState state) {
ModelResourceLocation mrl = ModelLocationCache.get(state); return this.mfix$modelCache.get(state);
BakedModel model = mrl == null ? null : modelManager.getModel(mrl);
if (model == null) {
model = modelManager.getMissingModel();
}
return model;
} }
} }

View File

@ -7,6 +7,7 @@ import net.minecraft.client.resources.model.ModelManager;
import net.minecraft.client.resources.model.ModelResourceLocation; import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item; import net.minecraft.world.item.Item;
import org.embeddedt.modernfix.dynamicresources.DynamicModelCache;
import org.embeddedt.modernfix.dynamicresources.ModelLocationCache; import org.embeddedt.modernfix.dynamicresources.ModelLocationCache;
import org.embeddedt.modernfix.util.DynamicInt2ObjectMap; import org.embeddedt.modernfix.util.DynamicInt2ObjectMap;
import org.spongepowered.asm.mixin.*; import org.spongepowered.asm.mixin.*;
@ -32,6 +33,8 @@ public abstract class ItemModelShaperMixin {
private static final ModelResourceLocation SENTINEL_VANILLA = new ModelResourceLocation(new ResourceLocation("modernfix", "sentinel"), "sentinel"); private static final ModelResourceLocation SENTINEL_VANILLA = new ModelResourceLocation(new ResourceLocation("modernfix", "sentinel"), "sentinel");
private final DynamicModelCache<Item> mfix$itemModelCache = new DynamicModelCache<>(k -> this.mfix$getModelForItem((Item)k), true);
@Inject(method = "<init>", at = @At("RETURN")) @Inject(method = "<init>", at = @At("RETURN"))
private void replaceLocationMap(CallbackInfo ci) { private void replaceLocationMap(CallbackInfo ci) {
overrideLocationsVanilla = new HashMap<>(); overrideLocationsVanilla = new HashMap<>();
@ -48,6 +51,12 @@ public abstract class ItemModelShaperMixin {
return map; return map;
} }
private BakedModel mfix$getModelForItem(Item item) {
ModelResourceLocation map = mfix$getLocation(item);
return map == null ? null : getModelManager().getModel(map);
}
/** /**
* @author embeddedt * @author embeddedt
* @reason Get the stored location for that item and meta, and get the model * @reason Get the stored location for that item and meta, and get the model
@ -55,8 +64,7 @@ public abstract class ItemModelShaperMixin {
**/ **/
@Overwrite @Overwrite
public BakedModel getItemModel(Item item) { public BakedModel getItemModel(Item item) {
ModelResourceLocation map = mfix$getLocation(item); return this.mfix$itemModelCache.get(item);
return map == null ? null : getModelManager().getModel(map);
} }
/** /**
@ -75,5 +83,7 @@ public abstract class ItemModelShaperMixin {
* all models). * all models).
**/ **/
@Overwrite @Overwrite
public void rebuildCache() {} public void rebuildCache() {
this.mfix$itemModelCache.clear();
}
} }

View File

@ -0,0 +1,79 @@
package org.embeddedt.modernfix.dynamicresources;
import it.unimi.dsi.fastutil.Function;
import it.unimi.dsi.fastutil.objects.Reference2ReferenceLinkedOpenHashMap;
import net.minecraft.client.resources.model.BakedModel;
import java.util.concurrent.locks.StampedLock;
/**
* The Mojang Triple-based baked cache system is too slow to be hitting on every model retrieval, so
* we need a fast, concurrency-safe wrapper on top.
*/
public class DynamicModelCache<K> {
private final Reference2ReferenceLinkedOpenHashMap<K, BakedModel> cache = new Reference2ReferenceLinkedOpenHashMap<>();
private final StampedLock lock = new StampedLock();
private final Function<K, BakedModel> modelRetriever;
private final boolean allowNulls;
public DynamicModelCache(Function<K, BakedModel> modelRetriever, boolean allowNulls) {
this.modelRetriever = modelRetriever;
this.allowNulls = allowNulls;
}
public void clear() {
long stamp = lock.writeLock();
try {
cache.clear();
} finally {
lock.unlock(stamp);
}
}
private boolean needToPopulate(K state) {
long stamp = lock.readLock();
try {
return !cache.containsKey(state);
} finally {
lock.unlock(stamp);
}
}
private BakedModel getModelFromCache(K state) {
long stamp = lock.readLock();
try {
return cache.get(state);
} finally {
lock.unlock(stamp);
}
}
private BakedModel cacheModel(K state) {
BakedModel model = modelRetriever.apply(state);
// Lock and modify our local, faster cache
long stamp = lock.writeLock();
try {
cache.putAndMoveToFirst(state, model);
// TODO: choose less arbitrary number
if(cache.size() >= 1000) {
cache.removeLast();
}
} finally {
lock.unlock(stamp);
}
return model;
}
public BakedModel get(K key) {
BakedModel model = getModelFromCache(key);
if(model == null && (!allowNulls || needToPopulate(key))) {
model = cacheModel(key);
}
return model;
}
}

View File

@ -0,0 +1,26 @@
package org.embeddedt.modernfix.dynamicresources;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.client.resources.model.BuiltInModel;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.Items;
import org.embeddedt.modernfix.testing.util.BootstrapMinecraft;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
@BootstrapMinecraft
public class DynamicModelCacheTest {
@Test
public void testCacheReturnsNullForNullGetter() {
DynamicModelCache<Item> cache = new DynamicModelCache(k -> null, true);
assertNull(cache.get(Items.STONE));
}
@Test
public void testCacheFunctions() {
BakedModel model = new BuiltInModel(null, null, null, false);
DynamicModelCache<Item> cache = new DynamicModelCache(k -> model, true);
assertEquals(model, cache.get(Items.STONE));
}
}

View File

@ -10,6 +10,7 @@ import net.minecraft.world.item.Item;
import net.minecraftforge.client.model.ForgeItemModelShaper; import net.minecraftforge.client.model.ForgeItemModelShaper;
import net.minecraftforge.registries.ForgeRegistries; import net.minecraftforge.registries.ForgeRegistries;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin; import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.embeddedt.modernfix.dynamicresources.DynamicModelCache;
import org.embeddedt.modernfix.dynamicresources.ModelLocationCache; import org.embeddedt.modernfix.dynamicresources.ModelLocationCache;
import org.embeddedt.modernfix.util.ItemMesherMap; import org.embeddedt.modernfix.util.ItemMesherMap;
import org.spongepowered.asm.mixin.*; import org.spongepowered.asm.mixin.*;
@ -27,6 +28,8 @@ public abstract class ItemModelMesherForgeMixin extends ItemModelShaper {
private Map<Holder.Reference<Item>, ModelResourceLocation> overrideLocations; private Map<Holder.Reference<Item>, ModelResourceLocation> overrideLocations;
private final DynamicModelCache<Holder.Reference<Item>> mfix$modelCache = new DynamicModelCache<>(k -> this.mfix$getModelSlow((Holder.Reference<Item>)k), true);
public ItemModelMesherForgeMixin(ModelManager arg) { public ItemModelMesherForgeMixin(ModelManager arg) {
super(arg); super(arg);
} }
@ -50,6 +53,11 @@ public abstract class ItemModelMesherForgeMixin extends ItemModelShaper {
return map; return map;
} }
private BakedModel mfix$getModelSlow(Holder.Reference<Item> key) {
ModelResourceLocation map = mfix$getLocationForge(key);
return map == null ? null : getModelManager().getModel(map);
}
/** /**
* @author embeddedt * @author embeddedt
* @reason Get the stored location for that item and meta, and get the model * @reason Get the stored location for that item and meta, and get the model
@ -58,8 +66,7 @@ public abstract class ItemModelMesherForgeMixin extends ItemModelShaper {
@Overwrite @Overwrite
@Override @Override
public BakedModel getItemModel(Item item) { public BakedModel getItemModel(Item item) {
ModelResourceLocation map = mfix$getLocationForge(ForgeRegistries.ITEMS.getDelegateOrThrow(item)); return this.mfix$modelCache.get(ForgeRegistries.ITEMS.getDelegateOrThrow(item));
return map == null ? null : getModelManager().getModel(map);
} }
/** /**
@ -80,5 +87,7 @@ public abstract class ItemModelMesherForgeMixin extends ItemModelShaper {
**/ **/
@Overwrite @Overwrite
@Override @Override
public void rebuildCache() {} public void rebuildCache() {
this.mfix$modelCache.clear();
}
} }

View File

@ -0,0 +1,25 @@
package org.embeddedt.modernfix.forge.mixin.perf.forge_registry_lambda;
import net.minecraft.resources.ResourceLocation;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
@Mixin(targets = {"net/minecraftforge/registries/RegistryDelegate"})
public class RegistryDelegateMixin {
@Shadow private ResourceLocation name;
/**
* @author embeddedt
* @reason avoid allocation in hashCode()
*/
@Overwrite(remap = false)
public int hashCode() {
ResourceLocation name = this.name;
if(name != null) {
return name.hashCode();
} else {
return 0;
}
}
}