Further optimizations to model loading
This commit is contained in:
parent
30ae895fa5
commit
0ffb3cc973
|
|
@ -18,7 +18,7 @@ plugins {
|
||||||
apply plugin: 'org.spongepowered.mixin'
|
apply plugin: 'org.spongepowered.mixin'
|
||||||
|
|
||||||
group = 'org.embeddedt'
|
group = 'org.embeddedt'
|
||||||
version = '1.6.0-beta1'
|
version = '1.6.0-beta2'
|
||||||
|
|
||||||
java {
|
java {
|
||||||
archivesBaseName = 'modernfix-mc' + minecraft_version
|
archivesBaseName = 'modernfix-mc' + minecraft_version
|
||||||
|
|
|
||||||
|
|
@ -8,18 +8,19 @@ import net.minecraft.client.renderer.texture.SpriteMap;
|
||||||
import net.minecraft.client.renderer.texture.TextureManager;
|
import net.minecraft.client.renderer.texture.TextureManager;
|
||||||
import net.minecraft.profiler.IProfiler;
|
import net.minecraft.profiler.IProfiler;
|
||||||
import net.minecraft.util.ResourceLocation;
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
import net.minecraft.util.math.vector.TransformationMatrix;
|
||||||
import net.minecraftforge.fml.loading.progress.StartupMessageManager;
|
import net.minecraftforge.fml.loading.progress.StartupMessageManager;
|
||||||
|
import org.apache.commons.lang3.tuple.Triple;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.spongepowered.asm.mixin.Final;
|
import org.spongepowered.asm.mixin.*;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.Overwrite;
|
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Mixin(ModelBakery.class)
|
@Mixin(ModelBakery.class)
|
||||||
|
|
@ -37,10 +38,19 @@ public abstract class ModelBakeryMixin {
|
||||||
@Shadow @Nullable private SpriteMap atlasSet;
|
@Shadow @Nullable private SpriteMap atlasSet;
|
||||||
|
|
||||||
@Shadow @Final private Map<ResourceLocation, IUnbakedModel> unbakedCache;
|
@Shadow @Final private Map<ResourceLocation, IUnbakedModel> unbakedCache;
|
||||||
|
@Shadow @Mutable
|
||||||
|
@Final private Map<Triple<ResourceLocation, TransformationMatrix, Boolean>, IBakedModel> bakedCache;
|
||||||
private Map<Boolean, List<ResourceLocation>> modelsToBakeParallel;
|
private Map<Boolean, List<ResourceLocation>> modelsToBakeParallel;
|
||||||
|
|
||||||
private boolean canBakeParallel(IUnbakedModel unbakedModel) {
|
private boolean canBakeParallel(IUnbakedModel unbakedModel) {
|
||||||
return false;
|
if(!(unbakedModel instanceof BlockModel))
|
||||||
|
return false;
|
||||||
|
else {
|
||||||
|
BlockModel model = (BlockModel)unbakedModel;
|
||||||
|
if(model.customData.hasCustomGeometry())
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "processLoading", at = @At(value = "INVOKE", target = "Lnet/minecraft/profiler/IProfiler;pop()V"))
|
@Inject(method = "processLoading", at = @At(value = "INVOKE", target = "Lnet/minecraft/profiler/IProfiler;pop()V"))
|
||||||
|
|
@ -56,18 +66,17 @@ public abstract class ModelBakeryMixin {
|
||||||
pProfiler.popPush("baking");
|
pProfiler.popPush("baking");
|
||||||
StartupMessageManager.mcLoaderConsumer().ifPresent(c -> c.accept("Baking models"));
|
StartupMessageManager.mcLoaderConsumer().ifPresent(c -> c.accept("Baking models"));
|
||||||
this.atlasSet = new SpriteMap(this.atlasPreparations.values().stream().map(Pair::getFirst).collect(Collectors.toList()));
|
this.atlasSet = new SpriteMap(this.atlasPreparations.values().stream().map(Pair::getFirst).collect(Collectors.toList()));
|
||||||
|
this.bakedCache = new ConcurrentHashMap<>();
|
||||||
this.modelsToBakeParallel = this.topLevelModels.keySet().stream()
|
this.modelsToBakeParallel = this.topLevelModels.keySet().stream()
|
||||||
.collect(Collectors.partitioningBy(location -> {
|
.collect(Collectors.partitioningBy(location -> {
|
||||||
return true;
|
|
||||||
/*
|
|
||||||
IUnbakedModel unbakedModel = this.unbakedCache.get(location);
|
IUnbakedModel unbakedModel = this.unbakedCache.get(location);
|
||||||
if(unbakedModel == null)
|
if(unbakedModel == null)
|
||||||
return false;
|
return false;
|
||||||
else
|
else
|
||||||
return this.canBakeParallel(unbakedModel);
|
return this.canBakeParallel(unbakedModel);
|
||||||
*/
|
|
||||||
}));
|
}));
|
||||||
List<ResourceLocation> parallelModels = this.modelsToBakeParallel.get(true);
|
List<ResourceLocation> parallelModels = this.modelsToBakeParallel.get(true);
|
||||||
|
List<CompletableFuture<Void>> futures = new ArrayList<>();
|
||||||
parallelModels.forEach((p_229350_1_) -> {
|
parallelModels.forEach((p_229350_1_) -> {
|
||||||
IBakedModel ibakedmodel = null;
|
IBakedModel ibakedmodel = null;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
package org.embeddedt.modernfix.mixin.perf.parallelize_model_loading;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import net.minecraft.state.BooleanProperty;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
|
||||||
|
@Mixin(BooleanProperty.class)
|
||||||
|
public class BooleanPropertyMixin {
|
||||||
|
/**
|
||||||
|
* There is no point comparing the immutable sets in any two instances of this class, as they will always be
|
||||||
|
* the same.
|
||||||
|
*/
|
||||||
|
@Redirect(method = "equals", at = @At(value = "INVOKE", target = "Lcom/google/common/collect/ImmutableSet;equals(Ljava/lang/Object;)Z"))
|
||||||
|
private boolean skipEqualityCheck(ImmutableSet instance, Object object) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,7 @@ import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.renderer.BlockModelShapes;
|
import net.minecraft.client.renderer.BlockModelShapes;
|
||||||
import net.minecraft.client.renderer.model.*;
|
import net.minecraft.client.renderer.model.*;
|
||||||
|
import net.minecraft.client.renderer.model.multipart.Selector;
|
||||||
import net.minecraft.client.renderer.texture.SpriteMap;
|
import net.minecraft.client.renderer.texture.SpriteMap;
|
||||||
import net.minecraft.profiler.IProfiler;
|
import net.minecraft.profiler.IProfiler;
|
||||||
import net.minecraft.resources.IResource;
|
import net.minecraft.resources.IResource;
|
||||||
|
|
@ -38,9 +39,11 @@ import java.nio.charset.StandardCharsets;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Predicate;
|
||||||
import java.util.stream.Collector;
|
import java.util.stream.Collector;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
@ -83,7 +86,8 @@ public abstract class ModelBakeryMixin {
|
||||||
profilerIn.popPush("loadjsons");
|
profilerIn.popPush("loadjsons");
|
||||||
AsyncStopwatch.measureAndLogSerialRunningTime("Parallel JSON loading", () -> {
|
AsyncStopwatch.measureAndLogSerialRunningTime("Parallel JSON loading", () -> {
|
||||||
useModelCache = false;
|
useModelCache = false;
|
||||||
deserializedModelCache = Minecraft.getInstance().getResourceManager().listResources("models", p -> {
|
this.deserializedModelCache = new ConcurrentHashMap<>();
|
||||||
|
Collection<ResourceLocation> modelLocations = Minecraft.getInstance().getResourceManager().listResources("models", p -> {
|
||||||
if(!p.endsWith(".json"))
|
if(!p.endsWith(".json"))
|
||||||
return false;
|
return false;
|
||||||
for(int i = 0; i < p.length(); i++) {
|
for(int i = 0; i < p.length(); i++) {
|
||||||
|
|
@ -91,54 +95,51 @@ public abstract class ModelBakeryMixin {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
})
|
});
|
||||||
.parallelStream()
|
ArrayList<CompletableFuture<Void>> futures = new ArrayList<>();
|
||||||
.map(location -> new ResourceLocation(location.getNamespace(), location.getPath().substring(7, location.getPath().length() - 5)))
|
for(ResourceLocation location : modelLocations) {
|
||||||
.map(location -> Pair.of(location, this.loadModelSafely(location)))
|
futures.add(CompletableFuture.runAsync(() -> {
|
||||||
.filter(pair -> pair.getSecond() != null)
|
ResourceLocation modelPath = new ResourceLocation(location.getNamespace(), location.getPath().substring(7, location.getPath().length() - 5));
|
||||||
.collect(Collectors.toConcurrentMap(Pair::getFirst, Pair::getSecond));
|
BlockModel model = this.loadModelSafely(modelPath);
|
||||||
|
if(model != null)
|
||||||
|
this.deserializedModelCache.put(modelPath, model);
|
||||||
|
}, Util.backgroundExecutor()));
|
||||||
|
}
|
||||||
|
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
|
||||||
useModelCache = true;
|
useModelCache = true;
|
||||||
});
|
});
|
||||||
AsyncStopwatch.measureAndLogSerialRunningTime("Parallel blockstate loading", () -> {
|
AsyncStopwatch.measureAndLogSerialRunningTime("Parallel blockstate loading", () -> {
|
||||||
ThreadLocal<BlockModelDefinition.ContainerHolder> containerHolder = ThreadLocal.withInitial(BlockModelDefinition.ContainerHolder::new);
|
ThreadLocal<BlockModelDefinition.ContainerHolder> containerHolder = ThreadLocal.withInitial(BlockModelDefinition.ContainerHolder::new);
|
||||||
this.deserializedBlockstateCache = Registry.BLOCK.keySet().parallelStream()
|
this.deserializedBlockstateCache = new ConcurrentHashMap<>();
|
||||||
.flatMap(block -> {
|
ArrayList<CompletableFuture<Void>> futures = new ArrayList<>();
|
||||||
ResourceLocation blockStateJSON = new ResourceLocation(block.getNamespace(), "blockstates/" + block.getPath() + ".json");
|
for(Block block : Registry.BLOCK) {
|
||||||
List<IResource> blockStates;
|
ResourceLocation blockLocation = Registry.BLOCK.getKey(block);
|
||||||
try {
|
futures.add(CompletableFuture.runAsync(() -> {
|
||||||
blockStates = this.resourceManager.getResources(blockStateJSON);
|
ResourceLocation blockStateJSON = new ResourceLocation(blockLocation.getNamespace(), "blockstates/" + blockLocation.getPath() + ".json");
|
||||||
} catch(IOException e) {
|
List<IResource> blockStates;
|
||||||
ModernFix.LOGGER.warn("Exception loading blockstate definition: {}: {}", block, e);
|
try {
|
||||||
blockStates = Collections.emptyList();
|
blockStates = this.resourceManager.getResources(blockStateJSON);
|
||||||
}
|
} catch(IOException e) {
|
||||||
return blockStates.stream().map(resource -> Pair.of(block, resource));
|
ModernFix.LOGGER.warn("Exception loading blockstate definition: {}: {}", blockLocation, e);
|
||||||
})
|
return;
|
||||||
.map((pair) -> {
|
}
|
||||||
ResourceLocation block = pair.getFirst();
|
List<Pair<String, BlockModelDefinition>> definitions = new ArrayList<>();
|
||||||
IResource resource = pair.getSecond();
|
StateContainer<Block, BlockState> stateContainer = block.getStateDefinition();
|
||||||
|
BlockModelDefinition.ContainerHolder context = containerHolder.get();
|
||||||
|
context.setDefinition(stateContainer);
|
||||||
|
for(IResource resource : blockStates) {
|
||||||
try (InputStream inputstream = resource.getInputStream()) {
|
try (InputStream inputstream = resource.getInputStream()) {
|
||||||
BlockModelDefinition.ContainerHolder context = containerHolder.get();
|
BlockModelDefinition definition = BlockModelDefinition.fromStream(context, new InputStreamReader(inputstream, StandardCharsets.UTF_8));
|
||||||
context.setDefinition(Registry.BLOCK.get(block).getStateDefinition());
|
definitions.add(Pair.of(resource.getSourceName(), definition));
|
||||||
return Pair.of(block, Pair.of(resource.getSourceName(), BlockModelDefinition.fromStream(context, new InputStreamReader(inputstream, StandardCharsets.UTF_8))));
|
|
||||||
} catch (Exception exception1) {
|
} catch (Exception exception1) {
|
||||||
ModernFix.LOGGER.warn(String.format("Exception loading blockstate definition: '%s' in resourcepack: '%s': %s", resource.getLocation(), resource.getSourceName(), exception1.getMessage()));
|
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));
|
return;
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
.filter(pair -> pair.getSecond().getSecond() != null)
|
this.deserializedBlockstateCache.put(blockLocation, definitions);
|
||||||
.collect(Collectors.groupingBy(Pair::getFirst, Collectors.mapping(Pair::getSecond, Collectors.toList())));
|
}, Util.backgroundExecutor()));
|
||||||
});
|
}
|
||||||
AsyncStopwatch.measureAndLogSerialRunningTime("Predicate generation", () -> {
|
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
|
||||||
/* 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());
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
package org.embeddedt.modernfix.mixin.perf.parallelize_model_loading;
|
||||||
|
|
||||||
|
import net.minecraft.util.math.vector.Matrix4f;
|
||||||
|
import net.minecraft.util.math.vector.TransformationMatrix;
|
||||||
|
import org.spongepowered.asm.mixin.Final;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Overwrite;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@Mixin(TransformationMatrix.class)
|
||||||
|
public class TransformationMatrixMixin {
|
||||||
|
@Shadow @Final private Matrix4f matrix;
|
||||||
|
private Integer cachedHashCode = null;
|
||||||
|
/**
|
||||||
|
* @author embeddedt
|
||||||
|
* @reason use cached hashcode if exists
|
||||||
|
*/
|
||||||
|
@Overwrite
|
||||||
|
public int hashCode() {
|
||||||
|
int hash;
|
||||||
|
if(cachedHashCode != null) {
|
||||||
|
hash = cachedHashCode;
|
||||||
|
} else {
|
||||||
|
hash = Objects.hashCode(this.matrix);
|
||||||
|
cachedHashCode = hash;
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
package org.embeddedt.modernfix.predicate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the
|
||||||
|
*/
|
||||||
|
public class CachedModelPredicate {
|
||||||
|
}
|
||||||
|
|
@ -37,6 +37,8 @@
|
||||||
"perf.parallelize_model_loading.multipart.MultipartMixin",
|
"perf.parallelize_model_loading.multipart.MultipartMixin",
|
||||||
"perf.parallelize_model_loading.multipart.VariantListMixin",
|
"perf.parallelize_model_loading.multipart.VariantListMixin",
|
||||||
"perf.parallelize_model_loading.SelectorMixin",
|
"perf.parallelize_model_loading.SelectorMixin",
|
||||||
|
"perf.parallelize_model_loading.TransformationMatrixMixin",
|
||||||
|
"perf.parallelize_model_loading.BooleanPropertyMixin",
|
||||||
"perf.trim_model_caches.ModelManagerMixin",
|
"perf.trim_model_caches.ModelManagerMixin",
|
||||||
"perf.async_jei.IngredientListElementFactoryMixin",
|
"perf.async_jei.IngredientListElementFactoryMixin",
|
||||||
"perf.async_jei.ClientLifecycleHandlerMixin",
|
"perf.async_jei.ClientLifecycleHandlerMixin",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user