Skip to content

Commit ba2b740

Browse files
committed
Merge remote-tracking branch 'origin/1.20' into 1.20.4
2 parents 063289f + 8677532 commit ba2b740

File tree

5 files changed

+159
-10
lines changed

5 files changed

+159
-10
lines changed

common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/BlockModelShaperMixin.java

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import net.minecraft.client.resources.model.ModelResourceLocation;
77
import net.minecraft.world.level.block.state.BlockState;
88
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
9+
import org.embeddedt.modernfix.dynamicresources.DynamicModelCache;
910
import org.embeddedt.modernfix.dynamicresources.ModelLocationCache;
1011
import org.embeddedt.modernfix.util.DynamicOverridableMap;
1112
import org.spongepowered.asm.mixin.*;
@@ -15,7 +16,6 @@
1516

1617
import java.util.Map;
1718

18-
1919
@Mixin(BlockModelShaper.class)
2020
@ClientOnlyMixin
2121
public class BlockModelShaperMixin {
@@ -24,23 +24,33 @@ public class BlockModelShaperMixin {
2424
@Shadow
2525
private Map<BlockState, BakedModel> modelByStateCache;
2626

27+
private final DynamicModelCache<BlockState> mfix$modelCache = new DynamicModelCache<>(k -> this.cacheBlockModel((BlockState)k), false);
28+
2729
@Inject(method = { "<init>", "replaceCache" }, at = @At("RETURN"))
2830
private void replaceModelMap(CallbackInfo ci) {
2931
// replace the backing map for mods which will access it
3032
this.modelByStateCache = new DynamicOverridableMap<>(state -> modelManager.getModel(ModelLocationCache.get(state)));
33+
if(this.mfix$modelCache != null)
34+
this.mfix$modelCache.clear();
3135
}
3236

33-
/**
34-
* @author embeddedt
35-
* @reason get the model from the dynamic model provider
36-
*/
37-
@Overwrite
38-
public BakedModel getBlockModel(BlockState state) {
37+
private BakedModel cacheBlockModel(BlockState state) {
38+
// Do all model system accesses in the unlocked path
3939
ModelResourceLocation mrl = ModelLocationCache.get(state);
4040
BakedModel model = mrl == null ? null : modelManager.getModel(mrl);
4141
if (model == null) {
4242
model = modelManager.getMissingModel();
4343
}
44+
4445
return model;
4546
}
47+
48+
/**
49+
* @author embeddedt
50+
* @reason get the model from the dynamic model provider
51+
*/
52+
@Overwrite
53+
public BakedModel getBlockModel(BlockState state) {
54+
return this.mfix$modelCache.get(state);
55+
}
4656
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package org.embeddedt.modernfix.dynamicresources;
2+
3+
import it.unimi.dsi.fastutil.Function;
4+
import it.unimi.dsi.fastutil.objects.Reference2ReferenceLinkedOpenHashMap;
5+
import net.minecraft.client.resources.model.BakedModel;
6+
7+
import java.util.concurrent.locks.StampedLock;
8+
9+
/**
10+
* The Mojang Triple-based baked cache system is too slow to be hitting on every model retrieval, so
11+
* we need a fast, concurrency-safe wrapper on top.
12+
*/
13+
public class DynamicModelCache<K> {
14+
private final Reference2ReferenceLinkedOpenHashMap<K, BakedModel> cache = new Reference2ReferenceLinkedOpenHashMap<>();
15+
private final StampedLock lock = new StampedLock();
16+
private final Function<K, BakedModel> modelRetriever;
17+
private final boolean allowNulls;
18+
19+
public DynamicModelCache(Function<K, BakedModel> modelRetriever, boolean allowNulls) {
20+
this.modelRetriever = modelRetriever;
21+
this.allowNulls = allowNulls;
22+
}
23+
24+
public void clear() {
25+
long stamp = lock.writeLock();
26+
try {
27+
cache.clear();
28+
} finally {
29+
lock.unlock(stamp);
30+
}
31+
}
32+
33+
private boolean needToPopulate(K state) {
34+
long stamp = lock.readLock();
35+
try {
36+
return !cache.containsKey(state);
37+
} finally {
38+
lock.unlock(stamp);
39+
}
40+
}
41+
42+
private BakedModel getModelFromCache(K state) {
43+
long stamp = lock.readLock();
44+
try {
45+
return cache.get(state);
46+
} finally {
47+
lock.unlock(stamp);
48+
}
49+
}
50+
51+
private BakedModel cacheModel(K state) {
52+
BakedModel model = modelRetriever.apply(state);
53+
54+
// Lock and modify our local, faster cache
55+
long stamp = lock.writeLock();
56+
57+
try {
58+
cache.putAndMoveToFirst(state, model);
59+
// TODO: choose less arbitrary number
60+
if(cache.size() >= 1000) {
61+
cache.removeLast();
62+
}
63+
} finally {
64+
lock.unlock(stamp);
65+
}
66+
67+
return model;
68+
}
69+
70+
public BakedModel get(K key) {
71+
BakedModel model = getModelFromCache(key);
72+
73+
if(model == null && (!allowNulls || needToPopulate(key))) {
74+
model = cacheModel(key);
75+
}
76+
77+
return model;
78+
}
79+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package org.embeddedt.modernfix.dynamicresources;
2+
3+
import net.minecraft.client.resources.model.BakedModel;
4+
import net.minecraft.client.resources.model.BuiltInModel;
5+
import net.minecraft.world.item.Item;
6+
import net.minecraft.world.item.Items;
7+
import org.embeddedt.modernfix.testing.util.BootstrapMinecraft;
8+
import org.junit.jupiter.api.Test;
9+
10+
import static org.junit.jupiter.api.Assertions.*;
11+
12+
@BootstrapMinecraft
13+
public class DynamicModelCacheTest {
14+
@Test
15+
public void testCacheReturnsNullForNullGetter() {
16+
DynamicModelCache<Item> cache = new DynamicModelCache(k -> null, true);
17+
assertNull(cache.get(Items.STONE));
18+
}
19+
20+
@Test
21+
public void testCacheFunctions() {
22+
BakedModel model = new BuiltInModel(null, null, null, false);
23+
DynamicModelCache<Item> cache = new DynamicModelCache(k -> model, true);
24+
assertEquals(model, cache.get(Items.STONE));
25+
}
26+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package org.embeddedt.modernfix.forge.mixin.perf.forge_registry_lambda;
2+
3+
import net.minecraft.resources.ResourceLocation;
4+
import org.spongepowered.asm.mixin.Mixin;
5+
import org.spongepowered.asm.mixin.Overwrite;
6+
import org.spongepowered.asm.mixin.Shadow;
7+
8+
@Mixin(targets = {"net/minecraftforge/registries/RegistryDelegate"})
9+
public class RegistryDelegateMixin {
10+
@Shadow private ResourceLocation name;
11+
12+
/**
13+
* @author embeddedt
14+
* @reason avoid allocation in hashCode()
15+
*/
16+
@Overwrite(remap = false)
17+
public int hashCode() {
18+
ResourceLocation name = this.name;
19+
if(name != null) {
20+
return name.hashCode();
21+
} else {
22+
return 0;
23+
}
24+
}
25+
}

neoforge/src/main/java/org/embeddedt/modernfix/neoforge/mixin/perf/dynamic_resources/ItemModelMesherForgeMixin.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import net.minecraft.world.item.Item;
1010
import net.neoforged.neoforge.client.model.RegistryAwareItemModelShaper;
1111
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
12+
import org.embeddedt.modernfix.dynamicresources.DynamicModelCache;
1213
import org.embeddedt.modernfix.dynamicresources.ModelLocationCache;
1314
import org.embeddedt.modernfix.util.ItemMesherMap;
1415
import org.spongepowered.asm.mixin.*;
@@ -26,6 +27,8 @@ public abstract class ItemModelMesherForgeMixin extends ItemModelShaper {
2627

2728
private Map<Holder.Reference<Item>, ModelResourceLocation> overrideLocations;
2829

30+
private final DynamicModelCache<Holder.Reference<Item>> mfix$modelCache = new DynamicModelCache<>(k -> this.mfix$getModelSlow((Holder.Reference<Item>)k), true);
31+
2932
public ItemModelMesherForgeMixin(ModelManager arg) {
3033
super(arg);
3134
}
@@ -49,6 +52,11 @@ private void replaceLocationMap(CallbackInfo ci) {
4952
return map;
5053
}
5154

55+
private BakedModel mfix$getModelSlow(Holder.Reference<Item> key) {
56+
ModelResourceLocation map = mfix$getLocationForge(key);
57+
return map == null ? null : getModelManager().getModel(map);
58+
}
59+
5260
/**
5361
* @author embeddedt
5462
* @reason Get the stored location for that item and meta, and get the model
@@ -57,8 +65,7 @@ private void replaceLocationMap(CallbackInfo ci) {
5765
@Overwrite
5866
@Override
5967
public BakedModel getItemModel(Item item) {
60-
ModelResourceLocation map = mfix$getLocationForge(item.builtInRegistryHolder());
61-
return map == null ? null : getModelManager().getModel(map);
68+
return this.mfix$modelCache.get(item.builtInRegistryHolder());
6269
}
6370

6471
/**
@@ -79,5 +86,7 @@ public void register(Item item, ModelResourceLocation location) {
7986
**/
8087
@Overwrite
8188
@Override
82-
public void rebuildCache() {}
89+
public void rebuildCache() {
90+
this.mfix$modelCache.clear();
91+
}
8392
}

0 commit comments

Comments
 (0)