Remove cache eviction for model data, optimize

This commit is contained in:
tterrag 2019-05-23 21:49:39 -04:00
parent 61b612c88a
commit fb30820e2b

View file

@ -21,46 +21,35 @@ package net.minecraftforge.client.model;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos; import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.model.data.IModelData; import net.minecraftforge.client.model.data.IModelData;
import net.minecraftforge.event.world.ChunkEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus;
@EventBusSubscriber(modid = "forge", bus = Bus.FORGE, value = Dist.CLIENT)
public class ModelDataManager public class ModelDataManager
{ {
private static WeakReference<World> currentWorld = new WeakReference<>(null); private static WeakReference<World> currentWorld = new WeakReference<>(null);
private static final Map<ChunkPos, Set<BlockPos>> needModelDataRefresh = new HashMap<>(); private static final Map<ChunkPos, Set<BlockPos>> needModelDataRefresh = new ConcurrentHashMap<>();
private static final LoadingCache<ChunkPos, Map<BlockPos, IModelData>> modelDataCache = CacheBuilder.newBuilder() private static final Map<ChunkPos, Map<BlockPos, IModelData>> modelDataCache = new ConcurrentHashMap<>();
.maximumSize(1000)
.concurrencyLevel(5)
.expireAfterAccess(30, TimeUnit.SECONDS)
.build(new CacheLoader<ChunkPos, Map<BlockPos, IModelData>>(){
@Override
public Map<BlockPos, IModelData> load(@Nonnull ChunkPos key)
{
return new ConcurrentHashMap<>();
}
});
private static void cleanCaches(World world) private static void cleanCaches(World world)
{ {
@ -68,7 +57,7 @@ public class ModelDataManager
{ {
currentWorld = new WeakReference<>(world); currentWorld = new WeakReference<>(world);
needModelDataRefresh.clear(); needModelDataRefresh.clear();
modelDataCache.invalidateAll(); modelDataCache.clear();
} }
} }
@ -77,25 +66,20 @@ public class ModelDataManager
World world = te.getWorld(); World world = te.getWorld();
Preconditions.checkNotNull(world, "Tile entity world must not be null"); Preconditions.checkNotNull(world, "Tile entity world must not be null");
Preconditions.checkArgument(world == Minecraft.getInstance().world, "Cannot request a model data refresh for a world other than the current client world"); Preconditions.checkArgument(world == Minecraft.getInstance().world, "Cannot request a model data refresh for a world other than the current client world");
synchronized (needModelDataRefresh)
{
cleanCaches(world); cleanCaches(world);
needModelDataRefresh.computeIfAbsent(new ChunkPos(te.getPos()), $ -> Collections.synchronizedSet(new HashSet<>())) needModelDataRefresh.computeIfAbsent(new ChunkPos(te.getPos()), $ -> Collections.synchronizedSet(new HashSet<>()))
.add(te.getPos()); .add(te.getPos());
} }
}
private static void refreshModelData(World world, ChunkPos chunk) private static void refreshModelData(World world, ChunkPos chunk)
{
Set<BlockPos> needUpdate;
synchronized (needModelDataRefresh)
{ {
cleanCaches(world); cleanCaches(world);
needUpdate = needModelDataRefresh.remove(chunk); Set<BlockPos> needUpdate = needModelDataRefresh.remove(chunk);
}
if (needUpdate != null) if (needUpdate != null)
{ {
Map<BlockPos, IModelData> data = modelDataCache.getUnchecked(chunk); Map<BlockPos, IModelData> data = modelDataCache.computeIfAbsent(chunk, $ -> new ConcurrentHashMap<>());
for (BlockPos pos : needUpdate) for (BlockPos pos : needUpdate)
{ {
TileEntity toUpdate = world.getTileEntity(pos); TileEntity toUpdate = world.getTileEntity(pos);
@ -107,6 +91,16 @@ public class ModelDataManager
} }
} }
@SubscribeEvent
public static void onChunkUnload(ChunkEvent.Unload event)
{
if (!event.getChunk().getWorldForge().isRemote()) return;
ChunkPos chunk = event.getChunk().getPos();
needModelDataRefresh.remove(chunk);
modelDataCache.remove(chunk);
}
public static @Nullable IModelData getModelData(World world, BlockPos pos) public static @Nullable IModelData getModelData(World world, BlockPos pos)
{ {
return getModelData(world, new ChunkPos(pos)).get(pos); return getModelData(world, new ChunkPos(pos)).get(pos);
@ -115,6 +109,6 @@ public class ModelDataManager
public static Map<BlockPos, IModelData> getModelData(World world, ChunkPos pos) public static Map<BlockPos, IModelData> getModelData(World world, ChunkPos pos)
{ {
refreshModelData(world, pos); refreshModelData(world, pos);
return modelDataCache.getUnchecked(pos); return modelDataCache.getOrDefault(pos, Collections.emptyMap());
} }
} }