Scan for all Block classes and preload them on worker threads

This helps move towards multithreading most VoxelShape computations
This commit is contained in:
embeddedt 2023-01-06 16:50:52 -05:00
parent 9ebeec6fc2
commit c6323fd62e
No known key found for this signature in database
GPG Key ID: A69433EC199B5613
5 changed files with 115 additions and 3 deletions

View File

@ -29,6 +29,7 @@ public class ModernFixEarlyConfig {
this.addMixinRule("bugfix.edge_chunk_not_saved", true);
this.addMixinRule("perf.async_jei", true);
this.addMixinRule("perf.thread_priorities", true);
this.addMixinRule("perf.preload_block_classes", true);
/* Mod compat */
if(FMLLoader.getLoadingModList().getModFileById("smoothboot") != null) {

View File

@ -86,7 +86,7 @@ public abstract class ModelBakeryMixin {
.filter(pair -> pair.getValue() != null)
.collect(Collectors.toConcurrentMap(Pair::getKey, Pair::getValue));
useModelCache = true;
ModernFix.LOGGER.warn("Preloading JSONs took " + stopwatch.elapsed(TimeUnit.SECONDS) + " seconds");
ModernFix.LOGGER.warn("Preloading JSONs took " + stopwatch.elapsed(TimeUnit.MILLISECONDS)/1000f + " seconds");
stopwatch.stop();
}
@ -121,7 +121,7 @@ public abstract class ModelBakeryMixin {
}).forEach((textureReferenceErrors) -> {
ModernFix.LOGGER.warn("Unable to resolve texture reference: {} in {}", textureReferenceErrors.getFirst(), textureReferenceErrors.getSecond());
});
ModernFix.LOGGER.warn("Collecting textures took " + stopwatch.elapsed(TimeUnit.SECONDS) + " seconds");
ModernFix.LOGGER.warn("Collecting textures took " + stopwatch.elapsed(TimeUnit.MILLISECONDS)/1000f + " seconds");
stopwatch.stop();
return materials;
}

View File

@ -0,0 +1,20 @@
package org.embeddedt.modernfix.mixin.perf.preload_block_classes;
import net.minecraftforge.fml.ModLoadingStage;
import net.minecraftforge.registries.GameData;
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.stream.Stream;
import org.embeddedt.modernfix.util.BlockClassPreloader;
@Mixin(GameData.class)
public class GameDataMixin {
@Inject(method = "generateRegistryEvents", at = @At("RETURN"), remap = false)
private static void preloadBlockClasses(CallbackInfoReturnable<Stream<ModLoadingStage.EventGenerator<?>>> cir) {
BlockClassPreloader.preloadClasses();
}
}

View File

@ -0,0 +1,90 @@
package org.embeddedt.modernfix.util;
import com.google.common.base.Stopwatch;
import net.minecraft.block.AbstractBlock;
import net.minecraft.block.Block;
import net.minecraftforge.fml.ModList;
import net.minecraftforge.fml.ModWorkManager;
import net.minecraftforge.fml.loading.moddiscovery.ModFile;
import net.minecraftforge.fml.loading.moddiscovery.ModFileInfo;
import net.minecraftforge.forgespi.language.ModFileScanData;
import org.embeddedt.modernfix.ModernFix;
import org.objectweb.asm.Type;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
public class BlockClassPreloader {
public static void preloadClasses() {
Stopwatch stopwatch = Stopwatch.createStarted();
ModernFix.LOGGER.warn("Preparing to preload classes...");
HashMap<Type, Boolean> isABlockClass = new HashMap<>();
isABlockClass.put(Type.getType(AbstractBlock.class), true);
isABlockClass.put(Type.getType(Block.class), true);
Field selfField, parentField;
List<CompletableFuture> futures = new ArrayList<>();
try {
selfField = ModFileScanData.ClassData.class.getDeclaredField("clazz");
selfField.setAccessible(true);
parentField = ModFileScanData.ClassData.class.getDeclaredField("parent");
parentField.setAccessible(true);
List<ModFileScanData.ClassData> currentCandidates = ModList.get().getModFiles().stream()
.map(ModFileInfo::getFile)
.map(ModFile::getScanResult)
.flatMap(data -> data.getClasses().stream())
.collect(Collectors.toList());
HashSet<Type> blockClasses = new HashSet<>();
blockClasses.add(Type.getType(AbstractBlock.class));
HashSet<Type> nonBlockClasses = new HashSet<>();
int previousSize = -1;
nonBlockClasses.add(Type.getType(Object.class));
currentCandidates.removeIf(clz -> {
Type self;
try {
self = (Type)selfField.get(clz);
} catch(ReflectiveOperationException e) {
throw new RuntimeException(e);
}
return (nonBlockClasses.contains(self) || blockClasses.contains(self));
});
while(blockClasses.size() > previousSize && currentCandidates.size() > 0) {
previousSize = blockClasses.size();
currentCandidates.removeIf(clz -> {
Type parent, self;
try {
parent = (Type)parentField.get(clz);
self = (Type)selfField.get(clz);
} catch(ReflectiveOperationException e) {
throw new RuntimeException(e);
}
if(nonBlockClasses.contains(parent)) {
nonBlockClasses.add(self);
return true;
} else if(blockClasses.contains(parent)) {
blockClasses.add(self);
futures.add(CompletableFuture.runAsync(() -> {
if(self.getClassName().toLowerCase(Locale.ROOT).contains("mixin"))
return;
try {
Class.forName(self.getClassName());
} catch(Throwable e) {
ModernFix.LOGGER.warn("Couldn't load " + self.getClassName(), e);
}
}, ModWorkManager.parallelExecutor()));
return true;
} else
return false;
});
}
futures.forEach(CompletableFuture::join);
} catch(ReflectiveOperationException e) {
throw new RuntimeException(e);
} finally {
ModernFix.LOGGER.warn("Preloading classes took " + stopwatch.elapsed(TimeUnit.MILLISECONDS)/1000f + " seconds");
stopwatch.stop();
}
}
}

View File

@ -17,7 +17,8 @@
"perf.reduce_blockstate_cache_rebuilds.GameDataMixin",
"perf.reduce_blockstate_cache_rebuilds.BlockCallbacksMixin",
"perf.boost_worker_count.UtilMixin",
"perf.thread_priorities.UtilMixin"
"perf.thread_priorities.UtilMixin",
"perf.preload_block_classes.GameDataMixin"
],
"client": [
"perf.skip_first_datapack_reload.MinecraftMixin",