Further optimizations to model loading
This commit is contained in:
parent
30ae895fa5
commit
0ffb3cc973
|
|
@ -18,7 +18,7 @@ plugins {
|
|||
apply plugin: 'org.spongepowered.mixin'
|
||||
|
||||
group = 'org.embeddedt'
|
||||
version = '1.6.0-beta1'
|
||||
version = '1.6.0-beta2'
|
||||
|
||||
java {
|
||||
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.profiler.IProfiler;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.vector.TransformationMatrix;
|
||||
import net.minecraftforge.fml.loading.progress.StartupMessageManager;
|
||||
import org.apache.commons.lang3.tuple.Triple;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
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 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 javax.annotation.Nullable;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Mixin(ModelBakery.class)
|
||||
|
|
@ -37,10 +38,19 @@ public abstract class ModelBakeryMixin {
|
|||
@Shadow @Nullable private SpriteMap atlasSet;
|
||||
|
||||
@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 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"))
|
||||
|
|
@ -56,18 +66,17 @@ public abstract class ModelBakeryMixin {
|
|||
pProfiler.popPush("baking");
|
||||
StartupMessageManager.mcLoaderConsumer().ifPresent(c -> c.accept("Baking models"));
|
||||
this.atlasSet = new SpriteMap(this.atlasPreparations.values().stream().map(Pair::getFirst).collect(Collectors.toList()));
|
||||
this.bakedCache = new ConcurrentHashMap<>();
|
||||
this.modelsToBakeParallel = this.topLevelModels.keySet().stream()
|
||||
.collect(Collectors.partitioningBy(location -> {
|
||||
return true;
|
||||
/*
|
||||
IUnbakedModel unbakedModel = this.unbakedCache.get(location);
|
||||
if(unbakedModel == null)
|
||||
return false;
|
||||
else
|
||||
return this.canBakeParallel(unbakedModel);
|
||||
*/
|
||||
}));
|
||||
List<ResourceLocation> parallelModels = this.modelsToBakeParallel.get(true);
|
||||
List<CompletableFuture<Void>> futures = new ArrayList<>();
|
||||
parallelModels.forEach((p_229350_1_) -> {
|
||||
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.renderer.BlockModelShapes;
|
||||
import net.minecraft.client.renderer.model.*;
|
||||
import net.minecraft.client.renderer.model.multipart.Selector;
|
||||
import net.minecraft.client.renderer.texture.SpriteMap;
|
||||
import net.minecraft.profiler.IProfiler;
|
||||
import net.minecraft.resources.IResource;
|
||||
|
|
@ -38,9 +39,11 @@ import java.nio.charset.StandardCharsets;
|
|||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collector;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
|
@ -83,7 +86,8 @@ public abstract class ModelBakeryMixin {
|
|||
profilerIn.popPush("loadjsons");
|
||||
AsyncStopwatch.measureAndLogSerialRunningTime("Parallel JSON loading", () -> {
|
||||
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"))
|
||||
return false;
|
||||
for(int i = 0; i < p.length(); i++) {
|
||||
|
|
@ -91,54 +95,51 @@ public abstract class ModelBakeryMixin {
|
|||
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));
|
||||
});
|
||||
ArrayList<CompletableFuture<Void>> futures = new ArrayList<>();
|
||||
for(ResourceLocation location : modelLocations) {
|
||||
futures.add(CompletableFuture.runAsync(() -> {
|
||||
ResourceLocation modelPath = new ResourceLocation(location.getNamespace(), location.getPath().substring(7, location.getPath().length() - 5));
|
||||
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;
|
||||
});
|
||||
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();
|
||||
this.deserializedBlockstateCache = new ConcurrentHashMap<>();
|
||||
ArrayList<CompletableFuture<Void>> futures = new ArrayList<>();
|
||||
for(Block block : Registry.BLOCK) {
|
||||
ResourceLocation blockLocation = Registry.BLOCK.getKey(block);
|
||||
futures.add(CompletableFuture.runAsync(() -> {
|
||||
ResourceLocation blockStateJSON = new ResourceLocation(blockLocation.getNamespace(), "blockstates/" + blockLocation.getPath() + ".json");
|
||||
List<IResource> blockStates;
|
||||
try {
|
||||
blockStates = this.resourceManager.getResources(blockStateJSON);
|
||||
} catch(IOException e) {
|
||||
ModernFix.LOGGER.warn("Exception loading blockstate definition: {}: {}", blockLocation, e);
|
||||
return;
|
||||
}
|
||||
List<Pair<String, BlockModelDefinition>> definitions = new ArrayList<>();
|
||||
StateContainer<Block, BlockState> stateContainer = block.getStateDefinition();
|
||||
BlockModelDefinition.ContainerHolder context = containerHolder.get();
|
||||
context.setDefinition(stateContainer);
|
||||
for(IResource resource : blockStates) {
|
||||
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))));
|
||||
BlockModelDefinition definition = BlockModelDefinition.fromStream(context, new InputStreamReader(inputstream, StandardCharsets.UTF_8));
|
||||
definitions.add(Pair.of(resource.getSourceName(), definition));
|
||||
} 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));
|
||||
return;
|
||||
}
|
||||
})
|
||||
.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());
|
||||
});
|
||||
}
|
||||
this.deserializedBlockstateCache.put(blockLocation, definitions);
|
||||
}, Util.backgroundExecutor()));
|
||||
}
|
||||
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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.VariantListMixin",
|
||||
"perf.parallelize_model_loading.SelectorMixin",
|
||||
"perf.parallelize_model_loading.TransformationMatrixMixin",
|
||||
"perf.parallelize_model_loading.BooleanPropertyMixin",
|
||||
"perf.trim_model_caches.ModelManagerMixin",
|
||||
"perf.async_jei.IngredientListElementFactoryMixin",
|
||||
"perf.async_jei.ClientLifecycleHandlerMixin",
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user