From c6323fd62eef969b41420e3c6e8425e896407f32 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Fri, 6 Jan 2023 16:50:52 -0500 Subject: [PATCH] Scan for all Block classes and preload them on worker threads This helps move towards multithreading most VoxelShape computations --- .../core/config/ModernFixEarlyConfig.java | 1 + .../ModelBakeryMixin.java | 4 +- .../preload_block_classes/GameDataMixin.java | 20 +++++ .../modernfix/util/BlockClassPreloader.java | 90 +++++++++++++++++++ src/main/resources/modernfix.mixins.json | 3 +- 5 files changed, 115 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/embeddedt/modernfix/mixin/perf/preload_block_classes/GameDataMixin.java create mode 100644 src/main/java/org/embeddedt/modernfix/util/BlockClassPreloader.java diff --git a/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java b/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java index a651bf24..2a76970b 100644 --- a/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java +++ b/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java @@ -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) { diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/ModelBakeryMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/ModelBakeryMixin.java index 1be6306d..1ef20dae 100644 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/ModelBakeryMixin.java +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/parallelize_model_loading/ModelBakeryMixin.java @@ -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; } diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/preload_block_classes/GameDataMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/preload_block_classes/GameDataMixin.java new file mode 100644 index 00000000..5f519ffc --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/preload_block_classes/GameDataMixin.java @@ -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>> cir) { + BlockClassPreloader.preloadClasses(); + } +} diff --git a/src/main/java/org/embeddedt/modernfix/util/BlockClassPreloader.java b/src/main/java/org/embeddedt/modernfix/util/BlockClassPreloader.java new file mode 100644 index 00000000..83933513 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/util/BlockClassPreloader.java @@ -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 isABlockClass = new HashMap<>(); + isABlockClass.put(Type.getType(AbstractBlock.class), true); + isABlockClass.put(Type.getType(Block.class), true); + Field selfField, parentField; + List futures = new ArrayList<>(); + try { + selfField = ModFileScanData.ClassData.class.getDeclaredField("clazz"); + selfField.setAccessible(true); + parentField = ModFileScanData.ClassData.class.getDeclaredField("parent"); + parentField.setAccessible(true); + List currentCandidates = ModList.get().getModFiles().stream() + .map(ModFileInfo::getFile) + .map(ModFile::getScanResult) + .flatMap(data -> data.getClasses().stream()) + .collect(Collectors.toList()); + HashSet blockClasses = new HashSet<>(); + blockClasses.add(Type.getType(AbstractBlock.class)); + HashSet 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(); + } + } +} diff --git a/src/main/resources/modernfix.mixins.json b/src/main/resources/modernfix.mixins.json index 8b50312f..44849ee1 100644 --- a/src/main/resources/modernfix.mixins.json +++ b/src/main/resources/modernfix.mixins.json @@ -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",