From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Thu, 24 Oct 2024 08:20:45 -0700 Subject: [PATCH] Moonrise: fluid method optimisations Original license: GPLv3 Original project: - https://github.com/Tuinity/Moonrise - https://github.com/PaperMC/Paper This patch is based on the following mixin: "ca/spottedleaf/moonrise/mixin/fluid/FlowingFluidMixin.java" "ca/spottedleaf/moonrise/mixin/fluid/FluidStateMixin.java" "ca/spottedleaf/moonrise/mixin/fluid/MappedRegistryMixin.java" Use cached result to avoid indirection For FlowingFluid#canPassThroughWall, try to avoid going to the cache for simple cases; additionally use better caching strategy diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/fluid/FluidFluidState.java b/src/main/java/ca/spottedleaf/moonrise/patches/fluid/FluidFluidState.java new file mode 100644 index 0000000000000000000000000000000000000000..107c97089354edd35f330582f5e0c8a18e792a6e --- /dev/null +++ b/src/main/java/ca/spottedleaf/moonrise/patches/fluid/FluidFluidState.java @@ -0,0 +1,5 @@ +package ca.spottedleaf.moonrise.patches.fluid; + +public interface FluidFluidState { + public void moonrise$initCaches(); +} diff --git a/src/main/java/net/minecraft/core/MappedRegistry.java b/src/main/java/net/minecraft/core/MappedRegistry.java index f22d22ebcedcc9c20225677844c86a1ad27c4211..92c2c37363fe31147f94f645c49e54ac626951cd 100644 --- a/src/main/java/net/minecraft/core/MappedRegistry.java +++ b/src/main/java/net/minecraft/core/MappedRegistry.java @@ -78,6 +78,19 @@ public class MappedRegistry implements WritableRegistry { }; private final Object tagAdditionLock = new Object(); + // Moonrise start - fluid method optimisations + private void injectFluidRegister( + final ResourceKey resourceKey, + final T object + ) { + if (resourceKey.registryKey() == (Object) net.minecraft.core.registries.Registries.FLUID) { + for (final net.minecraft.world.level.material.FluidState possibleState : ((net.minecraft.world.level.material.Fluid) object).getStateDefinition().getPossibleStates()) { + ((ca.spottedleaf.moonrise.patches.fluid.FluidFluidState) (Object) possibleState).moonrise$initCaches(); + } + } + } + // Moonrise end - fluid method optimisations + public MappedRegistry(ResourceKey> key, Lifecycle lifecycle) { this(key, lifecycle, false); } @@ -145,6 +158,7 @@ public class MappedRegistry implements WritableRegistry { this.toId.put(value, i); this.registrationInfos.put(key, info); this.registryLifecycle = this.registryLifecycle.add(info.lifecycle()); + this.injectFluidRegister(key, value); // Moonrise - fluid method optimisations return reference; } diff --git a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java index 3b197331f51b12b55a40f2abb6ce99ef0fb65f17..bf9c228a19fe34221686f1d002feda7f40e8272c 100644 --- a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java +++ b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java @@ -55,6 +55,48 @@ public abstract class FlowingFluid extends Fluid { }); private final Map shapes = Maps.newIdentityHashMap(); + // Moonrise start - fluid method optimisations + private FluidState sourceFalling; + private FluidState sourceNotFalling; + + private static final int TOTAL_FLOWING_STATES = FALLING.getPossibleValues().size() * LEVEL.getPossibleValues().size(); + private static final int MIN_LEVEL = LEVEL.getPossibleValues().stream().sorted().findFirst().get().intValue(); + + // index = (falling ? 1 : 0) + level*2 + private FluidState[] flowingLookUp; + private volatile boolean init; + + private static final int COLLISION_OCCLUSION_CACHE_SIZE = 2048; + private static final ThreadLocal COLLISION_OCCLUSION_CACHE = ThreadLocal.withInitial(() -> new ca.spottedleaf.moonrise.patches.collisions.util.FluidOcclusionCacheKey[COLLISION_OCCLUSION_CACHE_SIZE]); + + + /** + * Due to init order, we need to use callbacks to initialise our state + */ + private void init() { + synchronized (this) { + if (this.init) { + return; + } + this.flowingLookUp = new FluidState[TOTAL_FLOWING_STATES]; + final FluidState defaultFlowState = this.getFlowing().defaultFluidState(); + for (int i = 0; i < TOTAL_FLOWING_STATES; ++i) { + final int falling = i & 1; + final int level = (i >>> 1) + MIN_LEVEL; + + this.flowingLookUp[i] = defaultFlowState.setValue(FALLING, falling == 1 ? Boolean.TRUE : Boolean.FALSE) + .setValue(LEVEL, Integer.valueOf(level)); + } + + final FluidState defaultFallState = this.getSource().defaultFluidState(); + this.sourceFalling = defaultFallState.setValue(FALLING, Boolean.TRUE); + this.sourceNotFalling = defaultFallState.setValue(FALLING, Boolean.FALSE); + + this.init = true; + } + } + // Moonrise end - fluid method optimisations + public FlowingFluid() {} @Override @@ -239,53 +281,70 @@ public abstract class FlowingFluid extends Fluid { } } - private boolean canPassThroughWall(Direction face, BlockGetter world, BlockPos pos, BlockState state, BlockPos fromPos, BlockState fromState) { - Object2ByteLinkedOpenHashMap object2bytelinkedopenhashmap; + // Moonrise start - fluid method optimisations + private static boolean canPassThroughWall(final Direction direction, final BlockGetter level, + final BlockPos fromPos, final BlockState fromState, + final BlockPos toPos, final BlockState toState) { + if (((ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState) fromState).moonrise$emptyCollisionShape() & ((ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState) toState).moonrise$emptyCollisionShape()) { + // don't even try to cache simple cases + return true; + } - if (!state.getBlock().hasDynamicShape() && !fromState.getBlock().hasDynamicShape()) { - object2bytelinkedopenhashmap = (Object2ByteLinkedOpenHashMap) FlowingFluid.OCCLUSION_CACHE.get(); - } else { - object2bytelinkedopenhashmap = null; + if (((ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState) fromState).moonrise$occludesFullBlock() | ((ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState) toState).moonrise$occludesFullBlock()) { + // don't even try to cache simple cases + return false; } - Block.BlockStatePairKey block_a; + final ca.spottedleaf.moonrise.patches.collisions.util.FluidOcclusionCacheKey[] cache = ((ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState) fromState).moonrise$hasCache() & ((ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState) toState).moonrise$hasCache() ? + COLLISION_OCCLUSION_CACHE.get() : null; - if (object2bytelinkedopenhashmap != null) { - block_a = new Block.BlockStatePairKey(state, fromState, face); - byte b0 = object2bytelinkedopenhashmap.getAndMoveToFirst(block_a); + final int keyIndex + = (((ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState) fromState).moonrise$uniqueId1() ^ ((ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState) toState).moonrise$uniqueId2() ^ ((ca.spottedleaf.moonrise.patches.collisions.util.CollisionDirection) (Object) direction).moonrise$uniqueId()) + & (COLLISION_OCCLUSION_CACHE_SIZE - 1); - if (b0 != 127) { - return b0 != 0; + if (cache != null) { + final ca.spottedleaf.moonrise.patches.collisions.util.FluidOcclusionCacheKey cached = cache[keyIndex]; + if (cached != null && cached.first() == fromState && cached.second() == toState && cached.direction() == direction) { + return cached.result(); } - } else { - block_a = null; } - VoxelShape voxelshape = state.getCollisionShape(world, pos); - VoxelShape voxelshape1 = fromState.getCollisionShape(world, fromPos); - boolean flag = !Shapes.mergedFaceOccludes(voxelshape, voxelshape1, face); + final VoxelShape shape1 = fromState.getCollisionShape(level, fromPos); + final VoxelShape shape2 = toState.getCollisionShape(level, toPos); - if (object2bytelinkedopenhashmap != null) { - if (object2bytelinkedopenhashmap.size() == 200) { - object2bytelinkedopenhashmap.removeLastByte(); - } + final boolean result = !Shapes.mergedFaceOccludes(shape1, shape2, direction); - object2bytelinkedopenhashmap.putAndMoveToFirst(block_a, (byte) (flag ? 1 : 0)); + if (cache != null) { + // we can afford to replace in-use keys more often due to the excessive caching the collision patch does in mergedFaceOccludes + cache[keyIndex] = new ca.spottedleaf.moonrise.patches.collisions.util.FluidOcclusionCacheKey(fromState, toState, direction, result); } - return flag; + return result; } + // Moonrise end - fluid method optimisations public abstract Fluid getFlowing(); public FluidState getFlowing(int level, boolean falling) { - return (FluidState) ((FluidState) this.getFlowing().defaultFluidState().setValue(FlowingFluid.LEVEL, level)).setValue(FlowingFluid.FALLING, falling); + // Moonrise start - fluid method optimisations + final int amount = level; + if (!this.init) { + this.init(); + } + final int index = (falling ? 1 : 0) | ((amount - MIN_LEVEL) << 1); + return this.flowingLookUp[index]; + // Moonrise end - fluid method optimisations } public abstract Fluid getSource(); public FluidState getSource(boolean falling) { - return (FluidState) this.getSource().defaultFluidState().setValue(FlowingFluid.FALLING, falling); + // Moonrise start - fluid method optimisations + if (!this.init) { + this.init(); + } + return falling ? this.sourceFalling : this.sourceNotFalling; + // Moonrise end - fluid method optimisations } protected abstract boolean canConvertToSource(Level world); diff --git a/src/main/java/net/minecraft/world/level/material/FluidState.java b/src/main/java/net/minecraft/world/level/material/FluidState.java index 14bb12d2a0066e8b020f2e0e670a7a5c74633623..49e07b51e7a516d71abc1976069be2d8b523c83c 100644 --- a/src/main/java/net/minecraft/world/level/material/FluidState.java +++ b/src/main/java/net/minecraft/world/level/material/FluidState.java @@ -21,7 +21,7 @@ import net.minecraft.world.level.block.state.properties.Property; import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.shapes.VoxelShape; -public final class FluidState extends StateHolder { +public final class FluidState extends StateHolder implements ca.spottedleaf.moonrise.patches.fluid.FluidFluidState { // Moonrise - fluid method optimisations public static final Codec CODEC = codec(BuiltInRegistries.FLUID.byNameCodec(), Fluid::defaultFluidState).stable(); public static final int AMOUNT_MAX = 9; public static final int AMOUNT_FULL = 8; @@ -32,20 +32,38 @@ public final class FluidState extends StateHolder { this.isEmpty = fluid.isEmpty(); // Paper - Perf: moved from isEmpty() } + // Moonrise start - fluid method optimisations + private int amount; + //private boolean isEmpty; + private boolean isSource; + private float ownHeight; + private boolean isRandomlyTicking; + private BlockState legacyBlock; + + @Override + public final void moonrise$initCaches() { + this.amount = this.getType().getAmount((FluidState) (Object) this); + //this.isEmpty = this.getType().isEmpty(); + this.isSource = this.getType().isSource((FluidState) (Object) this); + this.ownHeight = this.getType().getOwnHeight((FluidState) (Object) this); + this.isRandomlyTicking = this.getType().isRandomlyTicking(); + } + // Moonrise end - fluid method optimisations + public Fluid getType() { return this.owner; } public boolean isSource() { - return this.getType().isSource(this); + return this.isSource; // Moonrise - fluid method optimisations } public boolean isSourceOfType(Fluid fluid) { - return this.owner == fluid && this.owner.isSource(this); + return this.isSource && this.owner == fluid; // Moonrise - fluid method optimisations } public boolean isEmpty() { - return this.isEmpty; // Paper - Perf: moved into constructor + return this.isEmpty; // Moonrise - Perf: moved into constructor } public float getHeight(BlockGetter world, BlockPos pos) { @@ -53,11 +71,11 @@ public final class FluidState extends StateHolder { } public float getOwnHeight() { - return this.getType().getOwnHeight(this); + return this.ownHeight; // Moonrise - fluid method optimisations } public int getAmount() { - return this.getType().getAmount(this); + return this.amount; // Moonrise - fluid method optimisations } public boolean shouldRenderBackwardUpFace(BlockGetter world, BlockPos pos) { @@ -83,7 +101,7 @@ public final class FluidState extends StateHolder { } public boolean isRandomlyTicking() { - return this.getType().isRandomlyTicking(); + return this.isRandomlyTicking; // Moonrise - fluid method optimisations } public void randomTick(Level world, BlockPos pos, RandomSource random) { @@ -95,7 +113,12 @@ public final class FluidState extends StateHolder { } public BlockState createLegacyBlock() { - return this.getType().createLegacyBlock(this); + // Moonrise start - fluid method optimisations + if (this.legacyBlock != null) { + return this.legacyBlock; + } + return this.legacyBlock = this.getType().createLegacyBlock((FluidState) (Object) this); + // Moonrise end - fluid method optimisations } @Nullable