diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/deduplicate_wall_shapes/WallBlockMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/deduplicate_wall_shapes/WallBlockMixin.java new file mode 100644 index 00000000..169852fe --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/deduplicate_wall_shapes/WallBlockMixin.java @@ -0,0 +1,60 @@ +package org.embeddedt.modernfix.common.mixin.perf.deduplicate_wall_shapes; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.mojang.datafixers.util.Pair; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.WallBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.Property; +import net.minecraft.world.phys.shapes.VoxelShape; +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.HashMap; +import java.util.Map; + +/** + * Most wall blocks use the default set of vanilla properties, and the default sizes for their shapes. This means + * there is no need to reconstruct a separate VoxelShape instance for each wall, we can just repurpose the + * same shape instances. To do this we can cache a mapping between a state (represented only as its prop->value map) + * and the desired shape, and generate the BlockState->VoxelShape map from this for each block. + */ +@Mixin(WallBlock.class) +public abstract class WallBlockMixin extends Block { + private static Map, Pair, Comparable>, VoxelShape>, StateDefinition>> CACHE_BY_SHAPE_VALS = new HashMap<>(); + + public WallBlockMixin(Properties properties) { + super(properties); + } + + @Inject(method = "makeShapes", at = @At("HEAD"), cancellable = true) + private synchronized void useCachedShapeMap(float f1, float f2, float f3, float f4, float f5, float f6, CallbackInfoReturnable> cir) { + ImmutableList key = ImmutableList.of(f1, f2, f3, f4, f5, f6); + Pair, Comparable>, VoxelShape>, StateDefinition> cache = CACHE_BY_SHAPE_VALS.get(key); + // require the properties to be identical + if(cache == null || !cache.getSecond().getProperties().equals(this.stateDefinition.getProperties())) + return; + ImmutableMap.Builder builder = ImmutableMap.builder(); + for(BlockState state : this.stateDefinition.getPossibleStates()) { + builder.put(state, cache.getFirst().get(state.getValues())); + } + cir.setReturnValue(builder.build()); + } + + @Inject(method = "makeShapes", at = @At("RETURN")) + private synchronized void storeCachedShapesByProperty(float f1, float f2, float f3, float f4, float f5, float f6, CallbackInfoReturnable> cir) { + ImmutableList key = ImmutableList.of(f1, f2, f3, f4, f5, f6); + if(!CACHE_BY_SHAPE_VALS.containsKey(key)) { + Map, Comparable>, VoxelShape> cacheByProperties = new HashMap<>(); + Map shapeMap = cir.getReturnValue(); + for(Map.Entry entry : shapeMap.entrySet()) { + cacheByProperties.put(entry.getKey().getValues(), entry.getValue()); + } + CACHE_BY_SHAPE_VALS.put(key, Pair.of(cacheByProperties, this.stateDefinition)); + } + } +} diff --git a/common/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java b/common/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java index e55fe55c..b5b07754 100644 --- a/common/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java +++ b/common/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java @@ -223,6 +223,8 @@ public class ModernFixEarlyConfig { disableIfModPresent("mixin.bugfix.remove_block_chunkloading", "performant"); disableIfModPresent("mixin.bugfix.paper_chunk_patches", "c2me"); disableIfModPresent("mixin.perf.cache_strongholds", "littletiles"); + // content overlap + disableIfModPresent("mixin.perf.deduplicate_wall_shapes", "dashloader"); disableIfModPresent("mixin.perf.nbt_memory_usage", "c2me"); disableIfModPresent("mixin.bugfix.item_cache_flag", "lithium", "canary", "radium"); // DimThread makes changes to the server chunk manager (understandably), C2ME probably does the same diff --git a/fabric/build.gradle b/fabric/build.gradle index bec736e5..b6cfa872 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -38,7 +38,7 @@ dependencies { modIncludeImplementation(fabricApi.module("fabric-screen-api-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' } modIncludeImplementation(fabricApi.module("fabric-command-api-v2", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' } modIncludeImplementation(fabricApi.module("fabric-models-v0", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' } - modImplementation(fabricApi.module("fabric-resource-loader-v0", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' } + modIncludeImplementation(fabricApi.module("fabric-resource-loader-v0", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' } modImplementation(fabricApi.module("fabric-data-generation-api-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' } modImplementation("com.terraformersmc:modmenu:${rootProject.modmenu_version}") { transitive false } modImplementation "curse.maven:spark-361579:${rootProject.spark_version}" diff --git a/scripts/gen-markdown-patchlist.py b/scripts/gen-markdown-patchlist.py index bf95acb9..37ac1e0b 100644 --- a/scripts/gen-markdown-patchlist.py +++ b/scripts/gen-markdown-patchlist.py @@ -15,12 +15,11 @@ with open('doc/generated/' + branch_name + '-Summary-of-Patches.md', 'w') as out if key.startswith("modernfix.option.mixin."): option_names.append(key.replace("modernfix.option.", "")) option_names.sort() - print("# Summary of Patches - " + branch_name) print() for option in option_names: option_description = lang_obj.get("modernfix.option." + option) option_friendly_name = lang_obj.get("modernfix.option.name." + option) - print(f"# `{option}`") + print(f"### `{option}`") print() if option_description is not None: print(option_description)