Merge remote-tracking branch 'origin/1.18' into 1.19.2

This commit is contained in:
embeddedt 2023-04-16 14:15:10 -04:00
commit e6716613cc
No known key found for this signature in database
GPG Key ID: A69433EC199B5613
12 changed files with 329 additions and 39 deletions

View File

@ -18,7 +18,7 @@ import org.apache.logging.log4j.Logger;
import org.embeddedt.modernfix.core.config.ModernFixConfig;
import org.embeddedt.modernfix.entity.EntityDataIDSyncHandler;
import org.embeddedt.modernfix.packet.PacketHandler;
import org.embeddedt.modernfix.registry.ObjectHolderClearer;
import java.lang.management.ManagementFactory;
import java.util.concurrent.CountDownLatch;
@ -91,6 +91,7 @@ public class ModernFix {
ModLoader.get().addWarning(new ModLoadingWarning(ModLoadingContext.get().getActiveContainer().getModInfo(), ModLoadingStage.COMMON_SETUP, "modernfix.no_lazydfu"));
});
}
ObjectHolderClearer.clearThrowables();
}
@SubscribeEvent

View File

@ -65,14 +65,17 @@ public class ModernFixEarlyConfig {
disableIfModPresent("mixin.perf.async_jei", "modernui");
disableIfModPresent("mixin.perf.compress_biome_container", "chocolate", "betterendforge");
disableIfModPresent("mixin.bugfix.mc218112", "performant");
disableIfModPresent("mixin.perf.faster_baking", "touhou_little_maid");
disableIfModPresent("mixin.perf.reuse_datapacks", "tac");
}
private void disableIfModPresent(String configName, String... ids) {
for(String id : ids) {
if(FMLLoader.getLoadingModList().getModFileById(id) != null) {
this.options.get(configName).addModOverride(false, id);
Option option = this.options.get(configName);
if(option != null)
option.addModOverride(false, id);
else
LOGGER.warn("Can't disable missing option {}", configName);
}
}
}

View File

@ -0,0 +1,5 @@
package org.embeddedt.modernfix.duck;
public interface ICachedMaterialsModel {
public void clearMaterialsCache();
}

View File

@ -1,8 +1,11 @@
package org.embeddedt.modernfix.duck;
import net.minecraft.client.renderer.texture.AtlasSet;
import com.google.common.collect.ImmutableList;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
public interface IExtendedModelBakery {
AtlasSet getUnfinishedAtlasSet();
ImmutableList<BlockState> getBlockStatesForMRL(StateDefinition<Block, BlockState> stateDefinition, ModelResourceLocation location);
}

View File

@ -0,0 +1,108 @@
package org.embeddedt.modernfix.mixin.perf.cache_model_materials;
import com.mojang.datafixers.util.Either;
import net.minecraft.client.renderer.block.model.BlockModel;
import net.minecraft.client.resources.model.Material;
import org.embeddedt.modernfix.duck.ICachedMaterialsModel;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
@Mixin(BlockModel.class)
public class BlockModelMixin {
@Shadow @Final @Mutable public Map<String, Either<Material, String>> textureMap;
/**
* @author embeddedt
* @reason detect changes to the texture map, and clear the material cache as needed
*/
@Inject(method = "<init>", at = @At("RETURN"))
private void useTrackingTextureMap(CallbackInfo ci) {
Map<String, Either<Material, String>> backingMap = this.textureMap;
ICachedMaterialsModel cacheHolder = (ICachedMaterialsModel)this;
this.textureMap = new Map<String, Either<Material, String>>() {
@Override
public int size() {
return backingMap.size();
}
@Override
public boolean isEmpty() {
return backingMap.isEmpty();
}
@Override
public boolean containsKey(Object o) {
return backingMap.containsKey(o);
}
@Override
public boolean containsValue(Object o) {
return backingMap.containsValue(o);
}
@Override
public Either<Material, String> get(Object o) {
return backingMap.get(o);
}
@Nullable
@Override
public Either<Material, String> put(String s, Either<Material, String> materialStringEither) {
Either<Material, String> old = backingMap.put(s, materialStringEither);
cacheHolder.clearMaterialsCache();
return old;
}
@Override
public Either<Material, String> remove(Object o) {
Either<Material, String> e = backingMap.remove(o);
cacheHolder.clearMaterialsCache();
return e;
}
@Override
public void putAll(@NotNull Map<? extends String, ? extends Either<Material, String>> map) {
backingMap.putAll(map);
cacheHolder.clearMaterialsCache();
}
@Override
public void clear() {
backingMap.clear();
cacheHolder.clearMaterialsCache();
}
@NotNull
@Override
public Set<String> keySet() {
cacheHolder.clearMaterialsCache();
return backingMap.keySet();
}
@NotNull
@Override
public Collection<Either<Material, String>> values() {
cacheHolder.clearMaterialsCache();
return backingMap.values();
}
@NotNull
@Override
public Set<Entry<String, Either<Material, String>>> entrySet() {
cacheHolder.clearMaterialsCache();
return backingMap.entrySet();
}
};
}
}

View File

@ -7,6 +7,7 @@ import net.minecraft.client.resources.model.Material;
import net.minecraft.client.renderer.block.model.MultiVariant;
import net.minecraft.client.renderer.block.model.multipart.MultiPart;
import net.minecraft.resources.ResourceLocation;
import org.embeddedt.modernfix.duck.ICachedMaterialsModel;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
@ -18,7 +19,7 @@ import java.util.Set;
import java.util.function.Function;
@Mixin(value = {MultiVariant.class, MultiPart.class, BlockModel.class})
public class VanillaModelMixin {
public class VanillaModelMixin implements ICachedMaterialsModel {
private Collection<Material> materialsCache = null;
@Inject(method = "getMaterials", at = @At("HEAD"), cancellable = true)
@ -33,4 +34,9 @@ public class VanillaModelMixin {
if(materialsCache == null)
materialsCache = Collections.unmodifiableCollection(cir.getReturnValue());
}
@Override
public void clearMaterialsCache() {
materialsCache = null;
}
}

View File

@ -37,6 +37,7 @@ import net.minecraftforge.fml.ModLoader;
import net.minecraftforge.registries.ForgeRegistries;
import org.apache.commons.lang3.tuple.Triple;
import org.embeddedt.modernfix.ModernFix;
import org.embeddedt.modernfix.duck.IExtendedModelBakery;
import org.embeddedt.modernfix.dynamicresources.DynamicBakedModelProvider;
import org.embeddedt.modernfix.dynamicresources.DynamicModelBakeEvent;
import org.embeddedt.modernfix.dynamicresources.ModelLocationCache;
@ -64,7 +65,7 @@ import java.util.stream.Stream;
/* high priority so that our injectors are added before other mods' */
@Mixin(value = ModelBakery.class, priority = 600)
public abstract class ModelBakeryMixin {
public abstract class ModelBakeryMixin implements IExtendedModelBakery {
private static final boolean debugDynamicModelLoading = Boolean.getBoolean("modernfix.debugDynamicModelLoading");
@ -443,6 +444,11 @@ public abstract class ModelBakeryMixin {
return ImmutableList.copyOf(finalList);
}
@Override
public ImmutableList<BlockState> getBlockStatesForMRL(StateDefinition<Block, BlockState> stateDefinition, ModelResourceLocation location) {
return loadOnlyRelevantBlockState(stateDefinition, location);
}
@Inject(method = "bake(Lnet/minecraft/resources/ResourceLocation;Lnet/minecraft/client/resources/model/ModelState;Ljava/util/function/Function;)Lnet/minecraft/client/resources/model/BakedModel;", at = @At("HEAD"), cancellable = true, remap = false)
public void getOrLoadBakedModelDynamic(ResourceLocation arg, ModelState arg2, Function<Material, TextureAtlasSprite> textureGetter, CallbackInfoReturnable<BakedModel> cir) {
Triple<ResourceLocation, Transformation, Boolean> triple = Triple.of(arg, arg2.getRotation(), arg2.isUvLocked());

View File

@ -1,5 +1,6 @@
package org.embeddedt.modernfix.mixin.perf.dynamic_resources.ctm;
import com.google.common.collect.ImmutableList;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import net.minecraft.client.renderer.ItemBlockRenderTypes;
import net.minecraft.client.renderer.RenderType;
@ -9,13 +10,17 @@ import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.client.resources.model.MultiPartBakedModel;
import net.minecraft.client.resources.model.WeightedBakedModel;
import net.minecraft.core.Holder;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.client.ChunkRenderTypeSet;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.eventbus.api.EventPriority;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.registries.ForgeRegistries;
import org.embeddedt.modernfix.ModernFix;
import org.embeddedt.modernfix.duck.IExtendedModelBakery;
import org.embeddedt.modernfix.dynamicresources.DynamicModelBakeEvent;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
@ -38,8 +43,6 @@ public abstract class CTMPackReloadListenerMixin {
@Shadow protected abstract ChunkRenderTypeSet getExistingRenderCheck(Block block);
private Map<ModelResourceLocation, BlockState> locationToState = new Object2ObjectOpenHashMap<>();
@Inject(method = "<init>", at = @At("RETURN"))
private void onInit(CallbackInfo ci) {
MinecraftForge.EVENT_BUS.addListener(EventPriority.LOW, this::onModelBake);
@ -49,29 +52,31 @@ public abstract class CTMPackReloadListenerMixin {
private void refreshLayerHacks() {
blockRenderChecks.forEach((b, p) -> ItemBlockRenderTypes.setRenderLayer((Block) b.get(), p));
blockRenderChecks.clear();
if(locationToState.isEmpty()) {
for(Block block : ForgeRegistries.BLOCKS.getValues()) {
for(BlockState state : block.getStateDefinition().getPossibleStates()) {
locationToState.put(BlockModelShaper.stateToModelLocation(state), state);
}
}
}
}
private void onModelBake(DynamicModelBakeEvent event) {
if(!(event.getModel() instanceof AbstractCTMBakedModel || event.getModel() instanceof WeightedBakedModel || event.getModel() instanceof MultiPartBakedModel))
return;
BlockState state = locationToState.get(event.getLocation());
if(state == null)
/* we construct a new ResourceLocation because an MRL is coming in */
Block block = ForgeRegistries.BLOCKS.getValue(new ResourceLocation(event.getLocation().getNamespace(), event.getLocation().getPath()));
Holder.Reference<Block> delegate = block != null ? ForgeRegistries.BLOCKS.getDelegateOrThrow(block) : null;
if(block == null || block == Blocks.AIR || blockRenderChecks.containsKey(delegate))
return;
Block block = state.getBlock();
Holder.Reference<Block> delegate = ForgeRegistries.BLOCKS.getDelegateOrThrow(block);
if(blockRenderChecks.containsKey(delegate))
/* find all states that match this MRL */
ImmutableList<BlockState> allStates;
try {
allStates = ((IExtendedModelBakery)(Object)event.getModelLoader()).getBlockStatesForMRL(block.getStateDefinition(), (ModelResourceLocation)event.getLocation());
} catch(RuntimeException e) {
ModernFix.LOGGER.error("Couldn't get state for MRL " + event.getLocation(), e);
return;
Predicate<RenderType> newPredicate = this.getLayerCheck(state, event.getModel());
if(newPredicate != null) {
blockRenderChecks.put(delegate, this.getExistingRenderCheck(block)::contains);
ItemBlockRenderTypes.setRenderLayer(block, newPredicate);
}
for(BlockState state : allStates) {
Predicate<RenderType> newPredicate = this.getLayerCheck(state, event.getModel());
if(newPredicate != null) {
blockRenderChecks.put(delegate, this.getExistingRenderCheck(block)::contains);
ItemBlockRenderTypes.setRenderLayer(block, newPredicate);
return;
}
}
}
}

View File

@ -5,6 +5,7 @@ import com.mojang.datafixers.util.Pair;
import net.minecraft.server.packs.PackType;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.resource.PathPackResources;
import org.embeddedt.modernfix.util.CachedResourcePath;
import org.embeddedt.modernfix.util.FileUtil;
import org.jetbrains.annotations.NotNull;
import org.spongepowered.asm.mixin.Final;
@ -40,9 +41,9 @@ public abstract class PathPackResourcesMixin {
@Shadow @NotNull protected abstract Set<String> getNamespacesFromDisk(PackType type);
private EnumMap<PackType, Set<String>> namespacesByType;
private EnumMap<PackType, HashMap<String, List<Pair<Path, String>>>> rootListingByNamespaceAndType;
private EnumMap<PackType, HashMap<String, List<CachedResourcePath>>> rootListingByNamespaceAndType;
private boolean hasGeneratedListings;
private Set<String> containedPaths;
private Set<CachedResourcePath> containedPaths;
private FileSystem resourcePackFS;
@ -59,24 +60,25 @@ public abstract class PathPackResourcesMixin {
synchronized (this) {
if(hasGeneratedListings)
return;
EnumMap<PackType, HashMap<String, List<Pair<Path, String>>>> rootListingByNamespaceAndType = new EnumMap<>(PackType.class);
HashSet<String> containedPaths = new HashSet<>();
EnumMap<PackType, HashMap<String, List<CachedResourcePath>>> rootListingByNamespaceAndType = new EnumMap<>(PackType.class);
HashSet<CachedResourcePath> containedPaths = new HashSet<>();
for(PackType type : PackType.values()) {
Set<String> namespaces = this.getNamespacesFromDisk(type);
HashMap<String, List<Pair<Path, String>>> rootListingForNamespaces = new HashMap<>();
HashMap<String, List<CachedResourcePath>> rootListingForNamespaces = new HashMap<>();
for(String namespace : namespaces) {
try {
Path root = this.resolve(type.getDirectory(), namespace).toAbsolutePath();
try (Stream<Path> stream = Files.walk(root)) {
ArrayList<Pair<Path, String>> rootListingPaths = new ArrayList<>();
ArrayList<CachedResourcePath> rootListingPaths = new ArrayList<>();
stream
.map(path -> root.relativize(path.toAbsolutePath()))
.filter(this::isValidCachedResourcePath)
.forEach(path -> {
if(!path.toString().endsWith(".mcmeta"))
rootListingPaths.add(Pair.of(path, slashJoiner.join(path)));
String mergedPath = slashJoiner.join(type.getDirectory(), namespace, path);
containedPaths.add(mergedPath);
CachedResourcePath listing = new CachedResourcePath(path);
if(!listing.getFileName().endsWith(".mcmeta")) {
rootListingPaths.add(listing);
}
containedPaths.add(new CachedResourcePath(new String[] { type.getDirectory(), namespace }, listing));
});
rootListingPaths.trimToSize();
rootListingForNamespaces.put(namespace, rootListingPaths);
@ -94,6 +96,8 @@ public abstract class PathPackResourcesMixin {
}
private boolean isValidCachedResourcePath(Path path) {
if(path.getFileName() == null || path.getNameCount() == 0)
return false;
String str = path.toString();
for(int i = 0; i < str.length(); i++) {
if(!ResourceLocation.validPathChar(str.charAt(i))) {
@ -124,7 +128,7 @@ public abstract class PathPackResourcesMixin {
@Inject(method = "hasResource(Ljava/lang/String;)Z", at = @At(value = "HEAD"), cancellable = true)
private void useCacheForExistence(String path, CallbackInfoReturnable<Boolean> cir) {
this.generateResourceCache();
cir.setReturnValue(this.containedPaths.contains(FileUtil.normalize(path)));
cir.setReturnValue(this.containedPaths.contains(new CachedResourcePath(path)));
}
/**
@ -139,11 +143,11 @@ public abstract class PathPackResourcesMixin {
pathIn = pathIn + "/";
final String testPath = pathIn;
Collection<ResourceLocation> cachedListing = this.rootListingByNamespaceAndType.get(type).getOrDefault(resourceNamespace, Collections.emptyList()).stream().
filter(path -> path.getSecond().startsWith(testPath)). // Make sure the target path is inside this one
filter(path -> path.getFullPath().startsWith(testPath)). // Make sure the target path is inside this one
// Finally we need to form the RL, so use the first name as the domain, and the rest as the path
// It is VERY IMPORTANT that we do not rely on Path.toString as this is inconsistent between operating systems
// Join the path names ourselves to force forward slashes
map(path -> new ResourceLocation(resourceNamespace, path.getSecond())).
map(path -> new ResourceLocation(resourceNamespace, path.getFullPath())).
filter(filter::test). // Test the file name against the predicate
collect(Collectors.toList());
cir.setReturnValue(cachedListing);

View File

@ -0,0 +1,47 @@
package org.embeddedt.modernfix.registry;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
import net.minecraftforge.registries.ObjectHolderRegistry;
import org.embeddedt.modernfix.ModernFix;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;
public class ObjectHolderClearer {
/**
* Many of the built-in Forge holders needlessly hold on to an exception.
*/
public static void clearThrowables() {
Set<Consumer<Predicate<ResourceLocation>>> holders = ObfuscationReflectionHelper.getPrivateValue(ObjectHolderRegistry.class, null, "objectHolders");
if(holders != null) {
int numCleared = 0;
HashMap<Class<?>, Field> throwableField = new HashMap<>();
Throwable singletonThrowable = new Throwable("[This stacktrace was cleared to save memory]");
try {
for(Consumer<Predicate<ResourceLocation>> holder : holders) {
Field target = throwableField.computeIfAbsent(holder.getClass(), clz -> {
Field[] clzFields = clz.getDeclaredFields();
for(Field f : clzFields) {
if(Throwable.class.isAssignableFrom(f.getType())) {
f.setAccessible(true);
return f;
}
}
return null;
});
if(target != null) {
target.set(holder, singletonThrowable);
numCleared++;
}
}
} catch(RuntimeException | ReflectiveOperationException | NoClassDefFoundError ignored) {
}
ModernFix.LOGGER.debug("Cleared " + numCleared + " object holder stacktrace references");
}
}
}

View File

@ -0,0 +1,101 @@
package org.embeddedt.modernfix.util;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Interner;
import com.google.common.collect.Interners;
import com.google.common.collect.Streams;
import java.lang.ref.WeakReference;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
public class CachedResourcePath {
private final String[] pathComponents;
private int hashCode = 0;
private static final Interner<String> PATH_COMPONENT_INTERNER = Interners.newStrongInterner();
private static final Splitter SLASH_SPLITTER = Splitter.on('/');
private static final Joiner SLASH_JOINER = Joiner.on('/');
private WeakReference<String> fullPathCache = new WeakReference<>(null);
private static final String[] NO_PREFIX = new String[0];
public CachedResourcePath(Path path) {
this(NO_PREFIX, path, path.getNameCount());
}
public CachedResourcePath(String s) {
// normalize so we can guarantee there are no empty sections
this(NO_PREFIX, SLASH_SPLITTER.splitToList(FileUtil.normalize(s)));
}
public <T> CachedResourcePath(String[] prefixElements, Collection<T> collection) {
this(prefixElements, collection, collection.size());
}
public <T> CachedResourcePath(String[] prefixElements, Iterable<T> path, int count) {
String[] components = new String[prefixElements.length + count];
int i = 0;
while(i < prefixElements.length) {
components[i] = PATH_COMPONENT_INTERNER.intern(prefixElements[i]);
i++;
}
for(Object component : path) {
String s = component.toString();
if(s.length() == 0)
continue;
components[i] = PATH_COMPONENT_INTERNER.intern(s);
i++;
}
pathComponents = components;
}
public CachedResourcePath(String[] prefixElements, CachedResourcePath other) {
String[] components = new String[prefixElements.length + other.pathComponents.length];
int i = 0;
while(i < prefixElements.length) {
components[i] = PATH_COMPONENT_INTERNER.intern(prefixElements[i]);
i++;
}
System.arraycopy(other.pathComponents, 0, components, i, other.pathComponents.length);
pathComponents = components;
}
@Override
public int hashCode() {
int result = hashCode;
if(result != 0)
return result;
hashCode = Arrays.hashCode(pathComponents);
return hashCode;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CachedResourcePath that = (CachedResourcePath) o;
return Arrays.equals(pathComponents, that.pathComponents);
}
public String getFileName() {
return pathComponents[pathComponents.length - 1];
}
public int getNameCount() {
return pathComponents.length;
}
public String getFullPath() {
String fPath = fullPathCache.get();
if(fPath == null) {
fPath = SLASH_JOINER.join(pathComponents);
fullPathCache = new WeakReference<>(fPath);
}
return fPath;
}
}

View File

@ -60,6 +60,7 @@
"perf.blast_search_trees.MinecraftMixin",
"perf.blast_search_trees.IngredientFilterInvoker",
"perf.cache_model_materials.VanillaModelMixin",
"perf.cache_model_materials.BlockModelMixin",
"perf.cache_model_materials.MultipartMixin",
"perf.faster_texture_stitching.StitcherMixin",
"perf.skip_first_datapack_reload.CreateWorldScreenMixin",