Skip to content

Commit 8677532

Browse files
committed
Merge remote-tracking branch 'origin/1.19.2' into 1.20
2 parents e47bdbf + 559bdb6 commit 8677532

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

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

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import net.minecraftforge.client.model.ForgeItemModelShaper;
1111
import net.minecraftforge.registries.ForgeRegistries;
1212
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
13+
import org.embeddedt.modernfix.dynamicresources.DynamicModelCache;
1314
import org.embeddedt.modernfix.dynamicresources.ModelLocationCache;
1415
import org.embeddedt.modernfix.util.ItemMesherMap;
1516
import org.spongepowered.asm.mixin.*;
@@ -27,6 +28,8 @@ public abstract class ItemModelMesherForgeMixin extends ItemModelShaper {
2728

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

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

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

6572
/**
@@ -80,5 +87,7 @@ public void register(Item item, ModelResourceLocation location) {
8087
**/
8188
@Overwrite
8289
@Override
83-
public void rebuildCache() {}
90+
public void rebuildCache() {
91+
this.mfix$modelCache.clear();
92+
}
8493
}
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+
}

0 commit comments

Comments
 (0)