More tweaks to model loading
This commit is contained in:
parent
aead8ef90f
commit
e9a6a3d194
|
|
@ -18,7 +18,7 @@ plugins {
|
|||
apply plugin: 'org.spongepowered.mixin'
|
||||
|
||||
group = 'org.embeddedt'
|
||||
version = '1.5.2'
|
||||
version = '1.6.0-beta1'
|
||||
|
||||
java {
|
||||
archivesBaseName = 'modernfix-mc' + minecraft_version
|
||||
|
|
@ -137,7 +137,6 @@ dependencies {
|
|||
|
||||
if(include_optimization_mods.toBoolean()) {
|
||||
runtimeOnly fg.deobf("curse.maven:roadrunner-529754:3683120")
|
||||
runtimeOnly fg.deobf("curse.maven:starlight-529754:3683120")
|
||||
runtimeOnly fg.deobf("curse.maven:rubidium-574856:3949659")
|
||||
runtimeOnly fg.deobf("curse.maven:noexperimental-407174:3188120")
|
||||
runtimeOnly fg.deobf("curse.maven:spark-361579:3767277")
|
||||
|
|
|
|||
|
|
@ -2,21 +2,29 @@ package org.embeddedt.modernfix.mixin.perf.parallelize_model_loading;
|
|||
|
||||
import com.google.common.base.Stopwatch;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.BlockModelShapes;
|
||||
import net.minecraft.client.renderer.model.*;
|
||||
import net.minecraft.client.renderer.texture.SpriteMap;
|
||||
import net.minecraft.profiler.IProfiler;
|
||||
import net.minecraft.resources.IResource;
|
||||
import net.minecraft.resources.IResourceManager;
|
||||
import net.minecraft.state.StateContainer;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.Util;
|
||||
import net.minecraft.util.math.vector.TransformationMatrix;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.apache.commons.lang3.tuple.Triple;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.embeddedt.modernfix.util.AsyncStopwatch;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
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.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
|
@ -24,6 +32,9 @@ import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
|||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
|
@ -44,7 +55,9 @@ public abstract class ModelBakeryMixin {
|
|||
@Shadow public abstract IUnbakedModel getModel(ResourceLocation modelLocation);
|
||||
|
||||
@Shadow @Final private Map<ResourceLocation, IUnbakedModel> topLevelModels;
|
||||
@Shadow @Final protected IResourceManager resourceManager;
|
||||
private Map<ResourceLocation, BlockModel> deserializedModelCache = null;
|
||||
private Map<ResourceLocation, List<Pair<String, BlockModelDefinition>>> deserializedBlockstateCache = null;
|
||||
private boolean useModelCache = false;
|
||||
|
||||
@Inject(method = "loadBlockModel", at = @At("HEAD"), cancellable = true)
|
||||
|
|
@ -65,34 +78,74 @@ public abstract class ModelBakeryMixin {
|
|||
}
|
||||
}
|
||||
|
||||
@Inject(method = "processLoading", at = @At(value = "INVOKE", target = "Lnet/minecraft/profiler/IProfiler;popPush(Ljava/lang/String;)V", ordinal = 1))
|
||||
@Inject(method = "processLoading", at = @At(value = "INVOKE", target = "Lnet/minecraft/profiler/IProfiler;popPush(Ljava/lang/String;)V", ordinal = 0))
|
||||
private void preloadJsonModels(IProfiler profilerIn, int maxMipmapLevel, CallbackInfo ci) {
|
||||
profilerIn.popPush("loadjsons");
|
||||
ModernFix.LOGGER.warn("Preloading JSONs in parallel...");
|
||||
Stopwatch stopwatch = Stopwatch.createStarted();
|
||||
useModelCache = false;
|
||||
deserializedModelCache = Minecraft.getInstance().getResourceManager().listResources("models", p -> {
|
||||
if(!p.endsWith(".json"))
|
||||
return false;
|
||||
for(int i = 0; i < p.length(); i++) {
|
||||
if(!ResourceLocation.validPathChar(p.charAt(i)))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.parallelStream()
|
||||
.map(location -> new ResourceLocation(location.getNamespace(), location.getPath().substring(7, location.getPath().length() - 5)))
|
||||
.map(location -> Pair.of(location, this.loadModelSafely(location)))
|
||||
.filter(pair -> pair.getValue() != null)
|
||||
.collect(Collectors.toConcurrentMap(Pair::getKey, Pair::getValue));
|
||||
useModelCache = true;
|
||||
ModernFix.LOGGER.warn("Preloading JSONs took " + stopwatch.elapsed(TimeUnit.MILLISECONDS)/1000f + " seconds");
|
||||
stopwatch.stop();
|
||||
AsyncStopwatch.measureAndLogSerialRunningTime("Parallel JSON loading", () -> {
|
||||
useModelCache = false;
|
||||
deserializedModelCache = Minecraft.getInstance().getResourceManager().listResources("models", p -> {
|
||||
if(!p.endsWith(".json"))
|
||||
return false;
|
||||
for(int i = 0; i < p.length(); i++) {
|
||||
if(!ResourceLocation.validPathChar(p.charAt(i)))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.parallelStream()
|
||||
.map(location -> new ResourceLocation(location.getNamespace(), location.getPath().substring(7, location.getPath().length() - 5)))
|
||||
.map(location -> Pair.of(location, this.loadModelSafely(location)))
|
||||
.filter(pair -> pair.getSecond() != null)
|
||||
.collect(Collectors.toConcurrentMap(Pair::getFirst, Pair::getSecond));
|
||||
useModelCache = true;
|
||||
});
|
||||
AsyncStopwatch.measureAndLogSerialRunningTime("Parallel blockstate loading", () -> {
|
||||
ThreadLocal<BlockModelDefinition.ContainerHolder> containerHolder = ThreadLocal.withInitial(BlockModelDefinition.ContainerHolder::new);
|
||||
this.deserializedBlockstateCache = Registry.BLOCK.keySet().parallelStream()
|
||||
.flatMap(block -> {
|
||||
ResourceLocation blockStateJSON = new ResourceLocation(block.getNamespace(), "blockstates/" + block.getPath() + ".json");
|
||||
List<IResource> blockStates;
|
||||
try {
|
||||
blockStates = this.resourceManager.getResources(blockStateJSON);
|
||||
} catch(IOException e) {
|
||||
ModernFix.LOGGER.warn("Exception loading blockstate definition: {}: {}", block, e);
|
||||
blockStates = Collections.emptyList();
|
||||
}
|
||||
return blockStates.stream().map(resource -> Pair.of(block, resource));
|
||||
})
|
||||
.map((pair) -> {
|
||||
ResourceLocation block = pair.getFirst();
|
||||
IResource resource = pair.getSecond();
|
||||
try (InputStream inputstream = resource.getInputStream()) {
|
||||
BlockModelDefinition.ContainerHolder context = containerHolder.get();
|
||||
context.setDefinition(Registry.BLOCK.get(block).getStateDefinition());
|
||||
return Pair.of(block, Pair.of(resource.getSourceName(), BlockModelDefinition.fromStream(context, new InputStreamReader(inputstream, StandardCharsets.UTF_8))));
|
||||
} catch (Exception exception1) {
|
||||
ModernFix.LOGGER.warn(String.format("Exception loading blockstate definition: '%s' in resourcepack: '%s': %s", resource.getLocation(), resource.getSourceName(), exception1.getMessage()));
|
||||
return Pair.of(block, Pair.of((String)null, (BlockModelDefinition)null));
|
||||
}
|
||||
})
|
||||
.filter(pair -> pair.getSecond().getSecond() != null)
|
||||
.collect(Collectors.groupingBy(Pair::getFirst, Collectors.mapping(Pair::getSecond, Collectors.toList())));
|
||||
});
|
||||
AsyncStopwatch.measureAndLogSerialRunningTime("Predicate generation", () -> {
|
||||
/* Pregenerate predicates */
|
||||
this.deserializedBlockstateCache.entrySet().parallelStream()
|
||||
.flatMap(entry -> entry.getValue().stream()
|
||||
.map(Pair::getSecond)
|
||||
.map(def -> Pair.of(Registry.BLOCK.get(entry.getKey()).getStateDefinition(), def)))
|
||||
.filter(pair -> pair.getSecond().isMultiPart())
|
||||
.flatMap(pair -> pair.getSecond().getMultiPart().getSelectors().stream().map(selector -> Pair.of(pair.getFirst(), selector)))
|
||||
.forEach(pair -> {
|
||||
pair.getSecond().getPredicate(pair.getFirst());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Inject(method = "processLoading", at = @At("RETURN"), remap = false)
|
||||
private void clearModelCache(IProfiler profilerIn, int maxMipmapLevel, CallbackInfo ci) {
|
||||
deserializedModelCache.clear();
|
||||
deserializedBlockstateCache.clear();
|
||||
useModelCache = false;
|
||||
}
|
||||
|
||||
|
|
@ -111,7 +164,7 @@ public abstract class ModelBakeryMixin {
|
|||
}
|
||||
return candidate;
|
||||
};
|
||||
Set<com.mojang.datafixers.util.Pair<String, String>> set = Collections.synchronizedSet(Sets.newLinkedHashSet());
|
||||
Set<Pair<String, String>> set = Collections.synchronizedSet(Sets.newLinkedHashSet());
|
||||
String modelMissingString = MISSING_MODEL_LOCATION.toString();
|
||||
Set<RenderMaterial> materials = this.topLevelModels.values().parallelStream().flatMap((unbaked) -> {
|
||||
return unbaked.getMaterials(safeUnbakedGetter, set).stream();
|
||||
|
|
@ -125,4 +178,29 @@ public abstract class ModelBakeryMixin {
|
|||
stopwatch.stop();
|
||||
return materials;
|
||||
}
|
||||
|
||||
private List<?> replacementList = null;
|
||||
|
||||
@Redirect(method = "loadModel", at = @At(value = "INVOKE", target = "Lnet/minecraft/resources/IResourceManager;getResources(Lnet/minecraft/util/ResourceLocation;)Ljava/util/List;", ordinal = 0))
|
||||
private List<?> getResourceList(IResourceManager instance, ResourceLocation jsonLocation, ResourceLocation originalBlockLocation) throws IOException {
|
||||
replacementList = null;
|
||||
if(this.deserializedBlockstateCache != null) {
|
||||
if(!(originalBlockLocation instanceof ModelResourceLocation)) {
|
||||
throw new AssertionError("Injector in unexpected spot?");
|
||||
}
|
||||
ModelResourceLocation mrl = (ModelResourceLocation)originalBlockLocation;
|
||||
ResourceLocation location = new ResourceLocation(mrl.getNamespace(), mrl.getPath());
|
||||
List<?> theList = this.deserializedBlockstateCache.get(location);
|
||||
if(theList != null && theList.size() > 0) {
|
||||
replacementList = theList;
|
||||
return theList;
|
||||
}
|
||||
}
|
||||
return instance.getResources(jsonLocation);
|
||||
}
|
||||
|
||||
@Redirect(method = "loadModel", at = @At(value = "INVOKE", target = "Ljava/util/stream/Stream;map(Ljava/util/function/Function;)Ljava/util/stream/Stream;", ordinal = 0))
|
||||
private Stream<?> fakeResourceList(Stream<?> instance, Function function) {
|
||||
return replacementList != null ? instance : instance.map(function);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
package org.embeddedt.modernfix.mixin.perf.parallelize_model_loading;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.renderer.model.multipart.Selector;
|
||||
import net.minecraft.state.StateContainer;
|
||||
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.CallbackInfoReturnable;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@Mixin(Selector.class)
|
||||
public class SelectorMixin {
|
||||
private ConcurrentHashMap<StateContainer<Block, BlockState>, Predicate<BlockState>> predicateCache = new ConcurrentHashMap<>();
|
||||
@Inject(method = "getPredicate", at = @At("HEAD"), cancellable = true)
|
||||
private void useCachedPredicate(StateContainer<Block, BlockState> pState, CallbackInfoReturnable<Predicate<BlockState>> cir) {
|
||||
Predicate<BlockState> cached = this.predicateCache.get(pState);
|
||||
if(cached != null)
|
||||
cir.setReturnValue(cached);
|
||||
}
|
||||
|
||||
@Inject(method = "getPredicate", at = @At("RETURN"))
|
||||
private void storeCachedPredicate(StateContainer<Block, BlockState> pState, CallbackInfoReturnable<Predicate<BlockState>> cir) {
|
||||
this.predicateCache.put(pState, cir.getReturnValue());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package org.embeddedt.modernfix.util;
|
||||
|
||||
import com.google.common.base.Stopwatch;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
|
@ -30,4 +31,15 @@ public class AsyncStopwatch {
|
|||
public long getCpuTime() {
|
||||
return cpuTimeMs.get();
|
||||
}
|
||||
|
||||
public static void measureAndLogSerialRunningTime(String label, Runnable runnable) {
|
||||
ModernFix.LOGGER.info(label + "...");
|
||||
Stopwatch stopwatch = Stopwatch.createStarted();
|
||||
try {
|
||||
runnable.run();
|
||||
ModernFix.LOGGER.info(label + " took " + stopwatch.elapsed(TimeUnit.MILLISECONDS)/1000f + " seconds");
|
||||
} finally {
|
||||
stopwatch.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,4 +2,5 @@ public net.minecraft.client.Minecraft$WorldSelectionType
|
|||
public net.minecraft.client.renderer.RenderType$Type
|
||||
public net.minecraft.client.renderer.RenderType$Type <init>(Ljava/lang/String;Lnet/minecraft/client/renderer/vertex/VertexFormat;IIZZLnet/minecraft/client/renderer/RenderType$State;)V
|
||||
public net.minecraft.block.AbstractBlock$AbstractBlockState$Cache
|
||||
public net.minecraft.util.math.shapes.VoxelShape <init>(Lnet/minecraft/util/math/shapes/VoxelShapePart;)V # <init>
|
||||
public net.minecraft.util.math.shapes.VoxelShape <init>(Lnet/minecraft/util/math/shapes/VoxelShapePart;)V # <init>
|
||||
public net.minecraft.client.renderer.model.ModelBakery$BlockStateDefinitionException
|
||||
|
|
@ -36,6 +36,7 @@
|
|||
"perf.parallelize_model_loading.OBJLoaderMixin",
|
||||
"perf.parallelize_model_loading.multipart.MultipartMixin",
|
||||
"perf.parallelize_model_loading.multipart.VariantListMixin",
|
||||
"perf.parallelize_model_loading.SelectorMixin",
|
||||
"perf.trim_model_caches.ModelManagerMixin",
|
||||
"perf.async_jei.IngredientListElementFactoryMixin",
|
||||
"perf.async_jei.ClientLifecycleHandlerMixin",
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user