Dynamic resources and faster item rendering are gone for now
This commit is contained in:
embeddedt 2024-11-06 14:59:14 -05:00
parent 103dea589f
commit c8ca6a5d53
No known key found for this signature in database
GPG Key ID: A69433EC199B5613
12 changed files with 12 additions and 485 deletions

View File

@ -1,74 +0,0 @@
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
import net.minecraft.client.renderer.block.BlockModelShaper;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.client.resources.model.ModelManager;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.embeddedt.modernfix.duck.IModelHoldingBlockState;
import org.embeddedt.modernfix.dynamicresources.ModelLocationCache;
import org.embeddedt.modernfix.util.DynamicOverridableMap;
import org.spongepowered.asm.mixin.*;
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.Map;
@Mixin(BlockModelShaper.class)
@ClientOnlyMixin
public class BlockModelShaperMixin {
@Shadow @Final private ModelManager modelManager;
@Shadow
private Map<BlockState, BakedModel> modelByStateCache;
@Inject(method = { "<init>", "replaceCache" }, at = @At("RETURN"))
private void replaceModelMap(CallbackInfo ci) {
// replace the backing map for mods which will access it
this.modelByStateCache = new DynamicOverridableMap<>(BlockState.class, state -> modelManager.getModel(ModelLocationCache.get(state)));
// Clear the cached models on blockstate objects
for(Block block : BuiltInRegistries.BLOCK) {
for(BlockState state : block.getStateDefinition().getPossibleStates()) {
if(state instanceof IModelHoldingBlockState modelHolder) {
modelHolder.mfix$setModel(null);
}
}
}
}
private BakedModel cacheBlockModel(BlockState state) {
// Do all model system accesses in the unlocked path
ModelResourceLocation mrl = ModelLocationCache.get(state);
BakedModel model = mrl == null ? null : modelManager.getModel(mrl);
if (model == null) {
model = modelManager.getMissingModel();
}
return model;
}
/**
* @author embeddedt
* @reason get the model from the dynamic model provider
*/
@Overwrite
public BakedModel getBlockModel(BlockState state) {
if(state instanceof IModelHoldingBlockState modelHolder) {
BakedModel model = modelHolder.mfix$getModel();
if(model != null) {
return model;
}
model = this.cacheBlockModel(state);
modelHolder.mfix$setModel(model);
return model;
} else {
return this.cacheBlockModel(state);
}
}
}

View File

@ -1,26 +0,0 @@
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.world.level.block.state.BlockBehaviour;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.embeddedt.modernfix.duck.IModelHoldingBlockState;
import org.spongepowered.asm.mixin.Mixin;
import java.lang.ref.SoftReference;
@Mixin(BlockBehaviour.BlockStateBase.class)
@ClientOnlyMixin
public class BlockStateBaseMixin implements IModelHoldingBlockState {
private volatile SoftReference<BakedModel> mfix$model;
@Override
public BakedModel mfix$getModel() {
var ref = mfix$model;
return ref != null ? ref.get() : null;
}
@Override
public void mfix$setModel(BakedModel model) {
mfix$model = model != null ? new SoftReference<>(model) : null;
}
}

View File

@ -1,38 +0,0 @@
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
import net.minecraft.client.renderer.ItemModelShaper;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.resources.ResourceLocation;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.spongepowered.asm.mixin.*;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.Map;
@Mixin(ItemModelShaper.class)
@ClientOnlyMixin
public abstract class ItemModelShaperMixin {
@Shadow @Final @Mutable private Map<ResourceLocation, BakedModel> modelToBakedModel;
@Inject(method = "<init>", at = @At("RETURN"))
private void initializeLazyCache(CallbackInfo ci) {
this.modelToBakedModel = new Object2ObjectLinkedOpenHashMap<>(this.modelToBakedModel);
}
/**
* @author embeddedt
* @reason Prevent all baked item models from being cached forever. We can safely mutate the map here as vanilla
* also uses computeIfAbsent, which means multithreaded access is not safe in vanilla either.
*/
@Inject(method = "getItemModel(Lnet/minecraft/resources/ResourceLocation;)Lnet/minecraft/client/resources/model/BakedModel;", at = @At(value = "RETURN"))
private void limitCacheSize(ResourceLocation resourceLocation, CallbackInfoReturnable<BakedModel> cir) {
var map = modelToBakedModel;
if (map instanceof Object2ObjectLinkedOpenHashMap<ResourceLocation, BakedModel> linkedMap && linkedMap.size() > 1000) {
linkedMap.removeFirst();
}
}
}

View File

@ -1,23 +0,0 @@
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
import net.minecraft.client.resources.model.ModelDiscovery;
import net.minecraft.client.resources.model.ModelResourceLocation;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import java.util.HashSet;
import java.util.Set;
@Mixin(ModelDiscovery.class)
@ClientOnlyMixin
public class ModelDiscoveryMixin {
/**
* @author embeddedt
* @reason nothing is mandatory at launch, we load things dynamically
*/
@Overwrite
private static Set<ModelResourceLocation> listMandatoryModels() {
return new HashSet<>();
}
}

View File

@ -1,97 +0,0 @@
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
import com.google.common.collect.ImmutableList;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.block.model.BlockModel;
import net.minecraft.client.resources.model.AtlasSet;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.client.resources.model.BlockStateModelLoader;
import net.minecraft.client.resources.model.ModelBakery;
import net.minecraft.client.resources.model.ModelManager;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.embeddedt.modernfix.dynamicresources.DynamicModelProvider;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
@Mixin(ModelManager.class)
@ClientOnlyMixin
public class ModelManagerMixin {
@Shadow private Map<ResourceLocation, BakedModel> bakedRegistry;
@Shadow private BakedModel missingModel;
@Unique
private DynamicModelProvider mfix$modelProvider;
@Unique
private Map<ResourceLocation, AtlasSet.StitchResult> mfix$stitchResults;
@Inject(method = "<init>", at = @At("RETURN"))
private void injectDummyBakedRegistry(CallbackInfo ci) {
if(this.bakedRegistry == null) {
this.bakedRegistry = new HashMap<>();
}
}
@Redirect(method = "reload", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/ModelManager;loadBlockModels(Lnet/minecraft/server/packs/resources/ResourceManager;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"))
private CompletableFuture<Map<ResourceLocation, BlockModel>> deferBlockModelLoad(ResourceManager manager, Executor executor) {
return CompletableFuture.completedFuture(Map.of());
}
@Redirect(method = "reload", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/ModelManager;loadBlockStates(Lnet/minecraft/client/resources/model/BlockStateModelLoader;Lnet/minecraft/server/packs/resources/ResourceManager;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"))
private CompletableFuture<BlockStateModelLoader.LoadedModels> deferBlockStateLoad(BlockStateModelLoader blockStateModelLoader, ResourceManager resourceManager, Executor executor) {
return CompletableFuture.completedFuture(new BlockStateModelLoader.LoadedModels(Map.of()));
}
@Redirect(method = "loadModels", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/StateDefinition;getPossibleStates()Lcom/google/common/collect/ImmutableList;"))
private ImmutableList<BlockState> skipCollection(StateDefinition<Block, BlockState> definition) {
return ImmutableList.of();
}
@Inject(method = "loadModels", at = @At("HEAD"))
private void saveStitchResults(ProfilerFiller profilerFiller, Map<ResourceLocation, AtlasSet.StitchResult> map, ModelBakery modelBakery, Object2IntMap<BlockState> object2IntMap, CallbackInfoReturnable<?> cir) {
this.mfix$stitchResults = map;
}
@Inject(method = "apply", at = @At("RETURN"))
private void createModelProvider(CallbackInfo ci) {
this.mfix$modelProvider = new DynamicModelProvider(
null, // TODO
this.missingModel,
Minecraft.getInstance().getResourceManager(),
this.mfix$stitchResults
);
}
/**
* @author embeddedt
* @reason use dynamic model system
*/
@Overwrite
public BakedModel getModel(ModelResourceLocation modelLocation) {
if(this.mfix$modelProvider != null) {
return this.mfix$modelProvider.getModel(modelLocation);
} else {
return this.missingModel;
}
}
}

View File

@ -1,72 +0,0 @@
package org.embeddedt.modernfix.common.mixin.perf.faster_item_rendering;
import com.llamalad7.mixinextras.sugar.Local;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.block.model.ItemTransform;
import net.minecraft.client.renderer.entity.ItemRenderer;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.client.resources.model.SimpleBakedModel;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.ItemStack;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.embeddedt.modernfix.render.FastItemRenderType;
import org.embeddedt.modernfix.render.RenderState;
import org.embeddedt.modernfix.render.SimpleItemModelView;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(value = ItemRenderer.class, priority = 600)
@ClientOnlyMixin
public abstract class ItemRendererMixin {
private ItemDisplayContext transformType;
private final SimpleItemModelView modelView = new SimpleItemModelView();
@Inject(method = "render", at = @At("HEAD"))
private void markRenderingType(ItemStack itemStack, ItemDisplayContext transformType, boolean leftHand, PoseStack matrixStack, MultiBufferSource buffer, int combinedLight, int combinedOverlay, BakedModel model, CallbackInfo ci) {
this.transformType = transformType;
}
/**
* If a model
* - is a vanilla item model (SimpleBakedModel),
* - has no custom GUI transforms, and
* - is being rendered in 2D on a GUI
* we do not need to go through the process of rendering every quad. Just render the south ones (the ones facing the
* camera).
*/
@ModifyArg(method = "renderItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/entity/ItemRenderer;renderModelLists(Lnet/minecraft/client/resources/model/BakedModel;Lnet/minecraft/world/item/ItemStack;IILcom/mojang/blaze3d/vertex/PoseStack;Lcom/mojang/blaze3d/vertex/VertexConsumer;)V"), index = 0)
private BakedModel useSimpleWrappedItemModel(BakedModel model, ItemStack stack, int combinedLight, int combinedOverlay, PoseStack matrixStack, VertexConsumer buffer, @Local(ordinal = 0) BakedModel originalModel) {
// Forge composite models split themselves into a smaller simple model, we need to detect that the parent
// was not simple
if(originalModel != null && originalModel.getClass() != SimpleBakedModel.class) {
return model;
}
if(!RenderState.IS_RENDERING_LEVEL && !stack.isEmpty() && model.getClass() == SimpleBakedModel.class && transformType == ItemDisplayContext.GUI) {
FastItemRenderType type;
ItemTransform transform = model.getTransforms().gui;
if(transform == ItemTransform.NO_TRANSFORM)
type = FastItemRenderType.SIMPLE_ITEM;
else if(stack.getItem() instanceof BlockItem && isBlockTransforms(transform))
type = FastItemRenderType.SIMPLE_BLOCK;
else
return model;
modelView.setItem(model);
modelView.setType(type);
return modelView;
} else
return model;
}
private boolean isBlockTransforms(ItemTransform transform) {
return transform.rotation.x() == 30f
&& transform.rotation.y() == 225f
&& transform.rotation.z() == 0f;
}
}

View File

@ -1,25 +0,0 @@
package org.embeddedt.modernfix.common.mixin.safety;
import net.minecraft.client.color.item.ItemColors;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.spongepowered.asm.mixin.Mixin;
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.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@Mixin(value = ItemColors.class, priority = 700)
@ClientOnlyMixin
public class ItemColorsMixin {
private Lock mapLock = new ReentrantLock();
@Inject(method = "register", at = @At("HEAD"))
private void lockMapBeforeAccess(CallbackInfo ci) {
mapLock.lock();
}
@Inject(method = "register", at = @At("TAIL"))
private void unlockMap(CallbackInfo ci) {
mapLock.unlock();
}
}

View File

@ -1,30 +0,0 @@
package org.embeddedt.modernfix.common.mixin.safety;
import net.minecraft.client.renderer.item.ItemProperties;
import net.minecraft.client.renderer.item.ItemPropertyFunction;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
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.Collections;
import java.util.Map;
@Mixin(value = ItemProperties.class, priority = 700)
@ClientOnlyMixin
public class ItemPropertiesMixin {
@Shadow @Final @Mutable private static Map<ResourceLocation, ItemPropertyFunction> GENERIC_PROPERTIES;
@Shadow @Final @Mutable private static Map<Item, Map<ResourceLocation, ItemPropertyFunction>> PROPERTIES;
@Inject(method = "<clinit>", at = @At("RETURN"))
private static void useConcurrentMaps(CallbackInfo ci) {
GENERIC_PROPERTIES = Collections.synchronizedMap(GENERIC_PROPERTIES);
PROPERTIES = Collections.synchronizedMap(PROPERTIES);
}
}

View File

@ -13,7 +13,6 @@ import net.minecraft.client.resources.model.BlockStateModelLoader;
import net.minecraft.client.resources.model.MissingBlockModel;
import net.minecraft.client.resources.model.ModelBakery;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.client.resources.model.SpecialModels;
import net.minecraft.client.resources.model.UnbakedModel;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.Resource;
@ -39,6 +38,7 @@ import java.util.stream.Collectors;
* Handles loading models dynamically, rather than at startup time.
*/
public class DynamicModelProvider {
/*
private final LoadingCache<ResourceLocation, Optional<BlockStateModelLoader.LoadedModels>> loadedStateDefinitions =
CacheBuilder.newBuilder()
.expireAfterAccess(3, TimeUnit.MINUTES)
@ -200,9 +200,12 @@ public class DynamicModelProvider {
return this.loadedBakedModels.getUnchecked(location).orElse(this.missingModel);
}
*/
/**
* Based on the Mojang impl but with some changes to make it slightly more efficient.
*/
/*
private class DynamicResolver implements UnbakedModel.Resolver {
private final Set<ResourceLocation> stack = new ObjectOpenHashSet<>(4);
private final Set<ResourceLocation> resolvedModels = new ObjectOpenHashSet<>();
@ -233,4 +236,6 @@ public class DynamicModelProvider {
this.resolvedModels.clear();
}
}
*/
}

View File

@ -1,94 +0,0 @@
package org.embeddedt.modernfix.render;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import net.minecraft.client.renderer.block.model.BakedOverrides;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.block.model.ItemTransforms;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.core.Direction;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* Wrapper class that presents a fake view of item models (only showing the simple front-facing quads), rather
* than every quad.
*/
public class SimpleItemModelView implements BakedModel {
private BakedModel wrappedItem;
private FastItemRenderType type;
public void setItem(BakedModel model) {
this.wrappedItem = model;
}
public void setType(FastItemRenderType type) {
this.type = type;
}
private boolean isCorrectDirectionForType(Direction direction) {
if(type == FastItemRenderType.SIMPLE_ITEM)
return direction == Direction.SOUTH;
else {
return direction == Direction.UP || direction == Direction.EAST || direction == Direction.NORTH;
}
}
private final List<BakedQuad> nullQuadList = new ObjectArrayList<>();
@Override
public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction side, RandomSource rand) {
boolean isWholeListValid = isCorrectDirectionForType(side);
List<BakedQuad> realList = wrappedItem.getQuads(state, side, rand);
if (isWholeListValid) {
return realList;
}
nullQuadList.clear();
//noinspection ForLoopReplaceableByForEach
for(int i = 0; i < realList.size(); i++) {
BakedQuad quad = realList.get(i);
if(isCorrectDirectionForType(quad.getDirection())) {
nullQuadList.add(quad);
}
}
return nullQuadList;
}
@Override
public boolean useAmbientOcclusion() {
return wrappedItem.useAmbientOcclusion();
}
@Override
public boolean isGui3d() {
return wrappedItem.isGui3d();
}
@Override
public boolean usesBlockLight() {
return wrappedItem.usesBlockLight();
}
@Override
public boolean isCustomRenderer() {
return wrappedItem.isCustomRenderer();
}
@Override
public TextureAtlasSprite getParticleIcon() {
return wrappedItem.getParticleIcon();
}
@Override
public ItemTransforms getTransforms() {
return wrappedItem.getTransforms();
}
@Override
public BakedOverrides overrides() {
return wrappedItem.overrides();
}
}

View File

@ -91,8 +91,9 @@ public class OptionList extends ContainerObjectSelectionList<OptionList.Entry> {
}
}
protected int getScrollbarPosition() {
return super.getScrollbarPosition() + 15 + 20;
@Override
protected int scrollBarY() {
return super.scrollBarY() + 15 + 20;
}
public int getRowWidth() {
@ -166,7 +167,7 @@ public class OptionList extends ContainerObjectSelectionList<OptionList.Entry> {
}).tooltip(toggleTooltip).pos(0, 0).size(55, 20).build();
updateStatus();
this.helpButton = new Button.Builder(Component.literal("?"), (arg) -> {
mainScreen.setLastScrollAmount(getScrollAmount());
mainScreen.setLastScrollAmount(scrollAmount());
Minecraft.getInstance().setScreen(new ModernFixOptionInfoScreen(mainScreen, optionName));
}).pos(75, 0).size(20, 20).build();
if(!I18n.exists("modernfix.option." + optionName)) {

View File

@ -5,8 +5,8 @@ junit_version=5.10.0-M1
mixinextras_version=0.3.2
mod_id=modernfix
minecraft_version=1.21.3
enabled_platforms=fabric,neoforge
minecraft_version=24w45a
enabled_platforms=fabric
forge_version=21.3.1-beta
parchment_version=2024.07.07
parchment_mc_version=1.21