Merge 1.20.2 into 1.20.3

This commit is contained in:
embeddedt 2023-11-05 21:25:23 -05:00
commit 207521f778
No known key found for this signature in database
GPG Key ID: A69433EC199B5613
5 changed files with 162 additions and 67 deletions

View File

@ -0,0 +1,37 @@
package org.embeddedt.modernfix.common.mixin.perf.cache_profile_texture_url;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
import net.minecraft.client.resources.SkinManager;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
@Mixin(SkinManager.class)
public class SkinManagerMixin {
@Unique
private final Cache<MinecraftProfileTexture, String> mfix$hashCache = CacheBuilder.newBuilder()
.expireAfterAccess(60, TimeUnit.SECONDS)
.concurrencyLevel(1)
.build();
@Redirect(method = "registerTexture(Lcom/mojang/authlib/minecraft/MinecraftProfileTexture;Lcom/mojang/authlib/minecraft/MinecraftProfileTexture$Type;Lnet/minecraft/client/resources/SkinManager$SkinTextureCallback;)Lnet/minecraft/resources/ResourceLocation;",
at = @At(value = "INVOKE", target = "Lcom/mojang/authlib/minecraft/MinecraftProfileTexture;getHash()Ljava/lang/String;", remap = false))
private String useCachedHash(MinecraftProfileTexture texture) {
// avoid lambda allocation for common case
String hash = mfix$hashCache.getIfPresent(texture);
if(hash != null)
return hash;
try {
return mfix$hashCache.get(texture, texture::getHash);
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
}
}

View File

@ -120,5 +120,6 @@
"modernfix.option.mixin.perf.compact_mojang_registries": "(Fabric) Experimental option that reduces the memory usage of registries by roughly 50%. Not useful in most modpacks unless they contain millions of blocks and items.",
"modernfix.option.mixin.perf.dynamic_block_codecs": "Avoids storing a codec for every block(state) and instead generates and caches it on the fly when needed. Generally not worth enabling unless you have a million blocks/items.",
"modernfix.option.mixin.perf.faster_command_suggestions": "Mitigate lag when there are hundreds of thousands of suggestions while typing a command",
"modernfix.option.mixin.perf.mojang_registry_size": "Fixes an issue causing registration of blocks/items to slow down proportional to the number already registered. This improves startup time."
"modernfix.option.mixin.perf.mojang_registry_size": "Fixes an issue causing registration of blocks/items to slow down proportional to the number already registered. This improves startup time.",
"modernfix.option.mixin.perf.cache_profile_texture_url": "Avoids pointlessly creating a URL object and speeds up skull block rendering."
}

View File

@ -27,7 +27,7 @@ import java.util.*;
*/
public class ModelBakeEventHelper {
// TODO: make into config option
private static final Set<String> INCOMPATIBLE_MODS = ImmutableSet.of("industrialforegoing", "vampirism");
private static final Set<String> INCOMPATIBLE_MODS = ImmutableSet.of("industrialforegoing", "vampirism", "embers");
private final Map<ResourceLocation, BakedModel> modelRegistry;
private final Set<ResourceLocation> topLevelModelLocations;
private final MutableGraph<String> dependencyGraph;

View File

@ -1,14 +1,17 @@
package org.embeddedt.modernfix.forge.mixin.perf.dynamic_resources;
import net.minecraft.client.renderer.block.model.BlockModel;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.resources.model.*;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
import org.embeddedt.modernfix.ModernFix;
import org.embeddedt.modernfix.ModernFixClient;
import org.embeddedt.modernfix.api.entrypoint.ModernFixClientIntegration;
import org.embeddedt.modernfix.duck.IExtendedModelBakery;
import org.embeddedt.modernfix.dynamicresources.DynamicBakedModelProvider;
import org.embeddedt.modernfix.forge.dynresources.IModelBakerImpl;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
@ -17,71 +20,104 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
@Mixin(ModelBakery.ModelBakerImpl.class)
@Mixin(value = ModelBakery.ModelBakerImpl.class, priority = 600)
public abstract class ModelBakerImplMixin implements IModelBakerImpl {
private static final boolean debugDynamicModelLoading = Boolean.getBoolean("modernfix.debugDynamicModelLoading");
@Shadow @Final private ModelBakery field_40571;
@Shadow public abstract UnbakedModel getModel(ResourceLocation arg);
private boolean mfix$ignoreCache = false;
@Shadow @Final private Function<Material, TextureAtlasSprite> modelTextureGetter;
private static final MethodHandle blockStateLoaderHandle;
static {
try {
blockStateLoaderHandle = MethodHandles.lookup().unreflect(
ObfuscationReflectionHelper.findMethod(ModelBakery.class, "m_119263_", BlockState.class)
);
} catch(ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
@Override
public void mfix$ignoreCache() {
mfix$ignoreCache = true;
}
@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) {
ModelBakery.BakedCacheKey key = new ModelBakery.BakedCacheKey(arg, arg2.getRotation(), arg2.isUvLocked());
BakedModel existing = mfix$ignoreCache ? null : this.field_40571.bakedCache.get(key);
if (existing != null) {
cir.setReturnValue(existing);
} else {
synchronized (this) {
if(debugDynamicModelLoading)
ModernFix.LOGGER.info("Baking {}", arg);
UnbakedModel iunbakedmodel = this.getModel(arg);
IExtendedModelBakery extendedBakery = (IExtendedModelBakery)this.field_40571;
if(iunbakedmodel == extendedBakery.mfix$getUnbakedMissingModel() && debugDynamicModelLoading)
ModernFix.LOGGER.warn("Model {} not present", arg);
// TODO: make sure parent resolution doesn't re-run many times
iunbakedmodel.resolveParents(this::getModel);
BakedModel ibakedmodel = null;
if (iunbakedmodel instanceof BlockModel) {
BlockModel blockmodel = (BlockModel)iunbakedmodel;
if (blockmodel.getRootModel() == ModelBakery.GENERATION_MARKER) {
ibakedmodel = ModelBakery.ITEM_MODEL_GENERATOR.generateBlockModel(textureGetter, blockmodel).bake((ModelBaker)this, blockmodel, textureGetter, arg2, arg, false);
}
}
if(iunbakedmodel != extendedBakery.mfix$getUnbakedMissingModel()) {
for(ModernFixClientIntegration integration : ModernFixClient.CLIENT_INTEGRATIONS) {
private ResourceLocation capturedLocation;
private UnbakedModel capturedModel;
private ModelState capturedState;
@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"), remap = false)
private void captureState(ResourceLocation arg, ModelState state, Function<Material, TextureAtlasSprite> sprites, CallbackInfoReturnable<BakedModel> cir) {
capturedState = state;
}
@Inject(method = "getModel", at = @At("HEAD"), cancellable = true)
private void obtainModel(ResourceLocation arg, CallbackInfoReturnable<UnbakedModel> cir) {
capturedLocation = arg;
if(debugDynamicModelLoading)
ModernFix.LOGGER.info("Baking {}", arg);
IExtendedModelBakery extendedBakery = (IExtendedModelBakery)this.field_40571;
if(arg instanceof ModelResourceLocation && arg != ModelBakery.MISSING_MODEL_LOCATION) {
// synchronized because we use topLevelModels
synchronized (this.field_40571) {
/* to emulate vanilla model loading, treat as top-level */
Optional<Block> blockOpt = Objects.equals(((ModelResourceLocation)arg).getVariant(), "inventory") ? Optional.empty() : BuiltInRegistries.BLOCK.getOptional(new ResourceLocation(arg.getNamespace(), arg.getPath()));
if(blockOpt.isPresent()) {
/* load via lambda for mods that expect blockstate to get loaded */
for(BlockState state : extendedBakery.getBlockStatesForMRL(blockOpt.get().getStateDefinition(), (ModelResourceLocation)arg)) {
try {
iunbakedmodel = integration.onUnbakedModelPreBake(arg, iunbakedmodel, this.field_40571);
} catch(RuntimeException e) {
ModernFix.LOGGER.error("Exception encountered firing bake event for {}", arg, e);
blockStateLoaderHandle.invokeExact(this.field_40571, state);
} catch(Throwable e) {
ModernFix.LOGGER.error("Error loading model", e);
}
}
} else {
this.field_40571.loadTopLevel((ModelResourceLocation)arg);
}
if(ibakedmodel == null) {
if(iunbakedmodel == extendedBakery.mfix$getUnbakedMissingModel()) {
// use a shared baked missing model
if(extendedBakery.getBakedMissingModel() == null) {
extendedBakery.setBakedMissingModel(iunbakedmodel.bake((ModelBaker)this, textureGetter, arg2, arg));
((DynamicBakedModelProvider)this.field_40571.getBakedTopLevelModels()).setMissingModel(extendedBakery.getBakedMissingModel());
}
ibakedmodel = extendedBakery.getBakedMissingModel();
} else
ibakedmodel = iunbakedmodel.bake((ModelBaker)this, textureGetter, arg2, arg);
cir.setReturnValue(this.field_40571.topLevelModels.getOrDefault(arg, extendedBakery.mfix$getUnbakedMissingModel()));
// avoid leaks
this.field_40571.topLevelModels.clear();
}
} else
cir.setReturnValue(this.field_40571.getModel(arg));
UnbakedModel toReplace = cir.getReturnValue();
if(true) {
for(ModernFixClientIntegration integration : ModernFixClient.CLIENT_INTEGRATIONS) {
try {
toReplace = integration.onUnbakedModelPreBake(arg, toReplace, this.field_40571);
} catch(RuntimeException e) {
ModernFix.LOGGER.error("Exception firing model pre-bake event for {}", arg, e);
}
for(ModernFixClientIntegration integration : ModernFixClient.CLIENT_INTEGRATIONS) {
ibakedmodel = integration.onBakedModelLoad(arg, iunbakedmodel, ibakedmodel, arg2, this.field_40571);
}
this.field_40571.bakedCache.put(key, ibakedmodel);
cir.setReturnValue(ibakedmodel);
}
}
cir.setReturnValue(toReplace);
cir.getReturnValue().resolveParents(this.field_40571::getModel);
capturedModel = cir.getReturnValue();
if(cir.getReturnValue() == extendedBakery.mfix$getUnbakedMissingModel()) {
if(arg != ModelBakery.MISSING_MODEL_LOCATION && debugDynamicModelLoading)
ModernFix.LOGGER.warn("Model {} not present", arg);
}
}
@ModifyExpressionValue(method = "bake(Lnet/minecraft/resources/ResourceLocation;Lnet/minecraft/client/resources/model/ModelState;Ljava/util/function/Function;)Lnet/minecraft/client/resources/model/BakedModel;", at = @At(value = "INVOKE", target = "Ljava/util/Map;get(Ljava/lang/Object;)Ljava/lang/Object;", ordinal = 0), remap = false)
private Object ignoreCacheIfRequested(Object o) {
return mfix$ignoreCache ? null : o;
}
@ModifyExpressionValue(method = "bake(Lnet/minecraft/resources/ResourceLocation;Lnet/minecraft/client/resources/model/ModelState;Ljava/util/function/Function;)Lnet/minecraft/client/resources/model/BakedModel;", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/UnbakedModel;bake(Lnet/minecraft/client/resources/model/ModelBaker;Ljava/util/function/Function;Lnet/minecraft/client/resources/model/ModelState;Lnet/minecraft/resources/ResourceLocation;)Lnet/minecraft/client/resources/model/BakedModel;"))
private BakedModel unifyMissingBakedModel(BakedModel model) {
for(ModernFixClientIntegration integration : ModernFixClient.CLIENT_INTEGRATIONS) {
model = integration.onBakedModelLoad(capturedLocation, capturedModel, model, capturedState, this.field_40571);
}
return model;
}
}

View File

@ -28,10 +28,7 @@ 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.ModifyVariable;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.*;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@ -42,8 +39,8 @@ import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
/* high priority so that our injectors are added before other mods' */
@Mixin(value = ModelBakery.class, priority = 600)
/* low priority so that our injectors are added after other mods' */
@Mixin(value = ModelBakery.class, priority = 1100)
@ClientOnlyMixin
public abstract class ModelBakeryMixin implements IExtendedModelBakery {
@ -68,6 +65,9 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery {
@Shadow @Final private static Logger LOGGER;
@Shadow
public abstract void loadTopLevel(ModelResourceLocation modelResourceLocation);
@Shadow public abstract UnbakedModel getModel(ResourceLocation resourceLocation);
private Cache<ModelBakery.BakedCacheKey, BakedModel> loadedBakedModels;
@ -76,6 +76,8 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery {
private HashMap<ResourceLocation, UnbakedModel> smallLoadingCache = new HashMap<>();
private boolean ignoreModelLoad;
// disable fabric recursion
@SuppressWarnings("unused")
private boolean fabric_enableGetOrLoadModelGuard;
@ -116,6 +118,12 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery {
this.bakedTopLevelModels = new DynamicBakedModelProvider((ModelBakery)(Object)this, bakedCache);
}
@ModifyArg(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/profiling/ProfilerFiller;popPush(Ljava/lang/String;)V", ordinal = 0), index = 0)
private String ignoreFutureModelLoads(String name) {
this.ignoreModelLoad = true;
return name;
}
private <K, V> void onModelRemoved(RemovalNotification<K, V> notification) {
if(!debugDynamicModelLoading)
return;
@ -130,6 +138,9 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery {
rl = ((ModelBakery.BakedCacheKey)k).id();
baked = true;
}
/* can fire when a model is replaced */
if(!baked && this.loadedModels.getIfPresent(rl) != null)
return;
ModernFix.LOGGER.warn("Evicted {} model {}", baked ? "baked" : "unbaked", rl);
}
@ -138,21 +149,23 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery {
private Set<ResourceLocation> blockStateFiles;
private Set<ResourceLocation> modelFiles;
@Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/ModelBakery;loadBlockModel(Lnet/minecraft/resources/ResourceLocation;)Lnet/minecraft/client/renderer/block/model/BlockModel;", ordinal = 0))
private BlockModel captureMissingModel(ModelBakery bakery, ResourceLocation location) throws IOException {
this.missingModel = this.loadBlockModel(location);
@ModifyArg(method = "<init>", at = @At(value = "INVOKE", target = "Ljava/util/Map;put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", ordinal = 0), index = 1)
private Object captureMissingModel(Object model) {
this.missingModel = (UnbakedModel)model;
this.blockStateFiles = new HashSet<>();
this.modelFiles = new HashSet<>();
return (BlockModel)this.missingModel;
return this.missingModel;
}
/**
* @author embeddedt
* @reason don't actually load the model.
* @reason don't actually load most models
*/
@Inject(method = "loadTopLevel", at = @At("HEAD"), cancellable = true)
private void addTopLevelFile(ModelResourceLocation location, CallbackInfo ci) {
ci.cancel();
@Redirect(method = "*", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/ModelBakery;loadTopLevel(Lnet/minecraft/client/resources/model/ModelResourceLocation;)V"))
private void addTopLevelFile(ModelBakery bakery, ModelResourceLocation location) {
if(location == MISSING_MODEL_LOCATION || !this.ignoreModelLoad) {
loadTopLevel(location);
}
}
@Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Ljava/util/Map;forEach(Ljava/util/function/BiConsumer;)V", ordinal = 0))
@ -177,13 +190,14 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery {
private BiFunction<ResourceLocation, Material, TextureAtlasSprite> textureGetter;
@Inject(method = "bakeModels", at = @At("HEAD"))
private void storeTextureGetter(BiFunction<ResourceLocation, Material, TextureAtlasSprite> getter, CallbackInfo ci) {
private void captureGetter(BiFunction<ResourceLocation, Material, TextureAtlasSprite> getter, CallbackInfo ci) {
this.ignoreModelLoad = false;
textureGetter = getter;
DynamicBakedModelProvider.currentInstance = (DynamicBakedModelProvider)this.bakedTopLevelModels;
}
@Redirect(method = "bakeModels", at = @At(value = "INVOKE", target = "Ljava/util/Map;keySet()Ljava/util/Set;"))
private Set skipBakingModels(Map instance) {
private Set<ResourceLocation> skipBake(Map<ResourceLocation, UnbakedModel> instance) {
Set<ResourceLocation> modelSet = new HashSet<>(instance.keySet());
if(modelSet.size() > 0)
ModernFix.LOGGER.info("Early baking {} models", modelSet.size());
@ -295,9 +309,16 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery {
@Override
public BakedModel bakeDefault(ResourceLocation modelLocation, ModelState state) {
ModelBakery.BakedCacheKey key = new ModelBakery.BakedCacheKey(modelLocation, BlockModelRotation.X0_Y0.getRotation(), BlockModelRotation.X0_Y0.isUvLocked());
BakedModel m = loadedBakedModels.getIfPresent(key);
if(m != null)
return m;
ModelBakery self = (ModelBakery) (Object) this;
ModelBaker theBaker = self.new ModelBakerImpl(textureGetter, modelLocation);
return theBaker.bake(modelLocation, state, theBaker.getModelTextureGetter());
synchronized(this) { m = theBaker.bake(modelLocation, state, theBaker.getModelTextureGetter()); }
if(m != null)
loadedBakedModels.put(key, m);
return m;
}
@Override