mirror of
https://github.com/Winds-Studio/Leaf.git
synced 2025-12-19 15:09:25 +00:00
279 lines
11 KiB
Diff
279 lines
11 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Martijn Muijsers <martijnmuijsers@live.nl>
|
|
Date: Wed, 23 Nov 2022 20:35:40 +0100
|
|
Subject: [PATCH] Improve fluid direction caching
|
|
|
|
Removed since 1.21.1, Paper 1.21.3, or Moonrise's imple is a bit faster
|
|
|
|
License: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
|
|
Gale - https://galemc.org
|
|
|
|
This patch is based on the following patch:
|
|
"Improve fluid direction caching"
|
|
By: Paul Sauve <paul@technove.co>
|
|
As part of: Airplane (https://github.com/TECHNOVE/Airplane)
|
|
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
|
|
|
|
* Airplane description *
|
|
|
|
Implements a custom cache that better fits the needs of fluids
|
|
calculating whether a direction can be moved in or something. There's a
|
|
big javadoc on the FluidDirectionCache with some more information.
|
|
|
|
* Airplane copyright *
|
|
|
|
Airplane
|
|
Copyright (C) 2020 Technove LLC
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
diff --git a/src/main/java/gg/airplane/structs/FluidDirectionCache.java b/src/main/java/gg/airplane/structs/FluidDirectionCache.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..bf6a36c933b686f0e34591dff3d43b3797c29b2b
|
|
--- /dev/null
|
|
+++ b/src/main/java/gg/airplane/structs/FluidDirectionCache.java
|
|
@@ -0,0 +1,143 @@
|
|
+// Gale - Airplane - improve fluid direction caching
|
|
+
|
|
+package gg.airplane.structs;
|
|
+
|
|
+import it.unimi.dsi.fastutil.HashCommon;
|
|
+
|
|
+/**
|
|
+ * This is a replacement for the cache used in FluidTypeFlowing.
|
|
+ * The requirements for the previous cache were:
|
|
+ * <ul>
|
|
+ * <li>Store 200 entries</li>
|
|
+ * <li>Look for the flag in the cache</li>
|
|
+ * <li>If it exists, move to front of cache</li>
|
|
+ * <li>If it doesn't exist, remove last entry in cache and insert in front</li>
|
|
+ * </ul>
|
|
+ * This class accomplishes something similar, however has a few different
|
|
+ * requirements put into place to make this more optimize:
|
|
+ * <ul>
|
|
+ * <li>
|
|
+ * maxDistance is the most amount of entries to be checked, instead
|
|
+ * of having to check the entire list.
|
|
+ * </li>
|
|
+ * <li>
|
|
+ * In combination with that, entries are all tracked by age and how
|
|
+ * frequently they're used. This enables us to remove old entries,
|
|
+ * without constantly shifting any around.
|
|
+ * </li>
|
|
+ * </ul>
|
|
+ * Usage of the previous map would have to reset the head every single usage,
|
|
+ * shifting the entire map. Here, nothing happens except an increment when
|
|
+ * the cache is hit, and when it needs to replace an old element only a single
|
|
+ * element is modified.
|
|
+ */
|
|
+public class FluidDirectionCache<T> {
|
|
+
|
|
+ private static class FluidDirectionEntry<T> {
|
|
+ private final T data;
|
|
+ private final boolean flag;
|
|
+ private int uses = 0;
|
|
+ private int age = 0;
|
|
+
|
|
+ private FluidDirectionEntry(T data, boolean flag) {
|
|
+ this.data = data;
|
|
+ this.flag = flag;
|
|
+ }
|
|
+
|
|
+ public int getValue() {
|
|
+ return this.uses - (this.age >> 1); // age isn't as important as uses
|
|
+ }
|
|
+
|
|
+ public void incrementUses() {
|
|
+ this.uses = this.uses + 1 & Integer.MAX_VALUE;
|
|
+ }
|
|
+
|
|
+ public void incrementAge() {
|
|
+ this.age = this.age + 1 & Integer.MAX_VALUE;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private final FluidDirectionEntry[] entries;
|
|
+ private final int mask;
|
|
+ private final int maxDistance; // the most amount of entries to check for a value
|
|
+
|
|
+ public FluidDirectionCache(int size) {
|
|
+ int arraySize = HashCommon.nextPowerOfTwo(size);
|
|
+ this.entries = new FluidDirectionEntry[arraySize];
|
|
+ this.mask = arraySize - 1;
|
|
+ this.maxDistance = Math.min(arraySize, 4);
|
|
+ }
|
|
+
|
|
+ public Boolean getValue(T data) {
|
|
+ FluidDirectionEntry curr;
|
|
+ int pos;
|
|
+
|
|
+ if ((curr = this.entries[pos = HashCommon.mix(data.hashCode()) & this.mask]) == null) {
|
|
+ return null;
|
|
+ } else if (data.equals(curr.data)) {
|
|
+ curr.incrementUses();
|
|
+ return curr.flag;
|
|
+ }
|
|
+
|
|
+ int checked = 1; // start at 1 because we already checked the first spot above
|
|
+
|
|
+ while ((curr = this.entries[pos = (pos + 1) & this.mask]) != null) {
|
|
+ if (data.equals(curr.data)) {
|
|
+ curr.incrementUses();
|
|
+ return curr.flag;
|
|
+ } else if (++checked >= this.maxDistance) {
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ public void putValue(T data, boolean flag) {
|
|
+ FluidDirectionEntry<T> curr;
|
|
+ int pos;
|
|
+
|
|
+ if ((curr = this.entries[pos = HashCommon.mix(data.hashCode()) & this.mask]) == null) {
|
|
+ this.entries[pos] = new FluidDirectionEntry<>(data, flag); // add
|
|
+ return;
|
|
+ } else if (data.equals(curr.data)) {
|
|
+ curr.incrementUses();
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ int checked = 1; // start at 1 because we already checked the first spot above
|
|
+
|
|
+ while ((curr = this.entries[pos = (pos + 1) & this.mask]) != null) {
|
|
+ if (data.equals(curr.data)) {
|
|
+ curr.incrementUses();
|
|
+ return;
|
|
+ } else if (++checked >= this.maxDistance) {
|
|
+ this.forceAdd(data, flag);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ this.entries[pos] = new FluidDirectionEntry<>(data, flag); // add
|
|
+ }
|
|
+
|
|
+ private void forceAdd(T data, boolean flag) {
|
|
+ int expectedPos = HashCommon.mix(data.hashCode()) & this.mask;
|
|
+
|
|
+ int toRemovePos = expectedPos;
|
|
+ FluidDirectionEntry entryToRemove = this.entries[toRemovePos];
|
|
+
|
|
+ for (int i = expectedPos + 1; i < expectedPos + this.maxDistance; i++) {
|
|
+ int pos = i & this.mask;
|
|
+ FluidDirectionEntry entry = this.entries[pos];
|
|
+ if (entry.getValue() < entryToRemove.getValue()) {
|
|
+ toRemovePos = pos;
|
|
+ entryToRemove = entry;
|
|
+ }
|
|
+
|
|
+ entry.incrementAge(); // use this as a mechanism to age the other entries
|
|
+ }
|
|
+
|
|
+ // remove the least used/oldest entry
|
|
+ this.entries[toRemovePos] = new FluidDirectionEntry(data, flag);
|
|
+ }
|
|
+}
|
|
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 1c0712295695727ee9c4d430d4157b8e17cbd71f..0eac7bc0c56ac17c900737271a965cfc1ccd03d2 100644
|
|
--- a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java
|
|
+++ b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java
|
|
@@ -45,6 +45,8 @@ public abstract class FlowingFluid extends Fluid {
|
|
public static final BooleanProperty FALLING = BlockStateProperties.FALLING;
|
|
public static final IntegerProperty LEVEL = BlockStateProperties.LEVEL_FLOWING;
|
|
private static final int CACHE_SIZE = 200;
|
|
+ // Gale start - Airplane - improve fluid direction caching - use our own cache
|
|
+ /*
|
|
private static final ThreadLocal<Object2ByteLinkedOpenHashMap<Block.BlockStatePairKey>> OCCLUSION_CACHE = ThreadLocal.withInitial(() -> {
|
|
Object2ByteLinkedOpenHashMap<Block.BlockStatePairKey> object2bytelinkedopenhashmap = new Object2ByteLinkedOpenHashMap<Block.BlockStatePairKey>(200) {
|
|
protected void rehash(int i) {}
|
|
@@ -53,6 +55,14 @@ public abstract class FlowingFluid extends Fluid {
|
|
object2bytelinkedopenhashmap.defaultReturnValue((byte) 127);
|
|
return object2bytelinkedopenhashmap;
|
|
});
|
|
+ */
|
|
+
|
|
+ private static final ThreadLocal<gg.airplane.structs.FluidDirectionCache<Block.BlockStatePairKey>> localFluidDirectionCache = ThreadLocal.withInitial(() -> {
|
|
+ // Gale - Airplane - TODO - mess with this number for performance
|
|
+ // with 2048 it seems very infrequent on a small world that it has to remove old entries
|
|
+ return new gg.airplane.structs.FluidDirectionCache<>(2048);
|
|
+ });
|
|
+ // Gale end - Airplane - improve fluid direction caching - use our own cache
|
|
private final Map<FluidState, VoxelShape> shapes = Maps.newIdentityHashMap();
|
|
|
|
public FlowingFluid() {}
|
|
@@ -240,6 +250,8 @@ public abstract class FlowingFluid extends Fluid {
|
|
}
|
|
|
|
private boolean canPassThroughWall(Direction face, BlockGetter world, BlockPos pos, BlockState state, BlockPos fromPos, BlockState fromState) {
|
|
+ // Gale start - Airplane - improve fluid direction caching - modify to use our cache
|
|
+ /*
|
|
Object2ByteLinkedOpenHashMap object2bytelinkedopenhashmap;
|
|
|
|
if (!state.getBlock().hasDynamicShape() && !fromState.getBlock().hasDynamicShape()) {
|
|
@@ -247,9 +259,16 @@ public abstract class FlowingFluid extends Fluid {
|
|
} else {
|
|
object2bytelinkedopenhashmap = null;
|
|
}
|
|
+ */
|
|
+ gg.airplane.structs.FluidDirectionCache<Block.BlockStatePairKey> cache = null;
|
|
+
|
|
+ if (!state.getBlock().hasDynamicShape() && !fromState.getBlock().hasDynamicShape()) {
|
|
+ cache = localFluidDirectionCache.get();
|
|
+ }
|
|
|
|
Block.BlockStatePairKey block_a;
|
|
|
|
+ /*
|
|
if (object2bytelinkedopenhashmap != null) {
|
|
block_a = new Block.BlockStatePairKey(state, fromState, face);
|
|
byte b0 = object2bytelinkedopenhashmap.getAndMoveToFirst(block_a);
|
|
@@ -260,11 +279,22 @@ public abstract class FlowingFluid extends Fluid {
|
|
} else {
|
|
block_a = null;
|
|
}
|
|
+ */
|
|
+ if (cache != null) {
|
|
+ block_a = new Block.BlockStatePairKey(state, fromState, face);
|
|
+ Boolean flag = cache.getValue(block_a);
|
|
+ if (flag != null) {
|
|
+ return flag;
|
|
+ }
|
|
+ } else {
|
|
+ block_a = null;
|
|
+ }
|
|
|
|
VoxelShape voxelshape = state.getCollisionShape(world, pos);
|
|
VoxelShape voxelshape1 = fromState.getCollisionShape(world, fromPos);
|
|
boolean flag = !Shapes.mergedFaceOccludes(voxelshape, voxelshape1, face);
|
|
|
|
+ /*
|
|
if (object2bytelinkedopenhashmap != null) {
|
|
if (object2bytelinkedopenhashmap.size() == 200) {
|
|
object2bytelinkedopenhashmap.removeLastByte();
|
|
@@ -272,6 +302,11 @@ public abstract class FlowingFluid extends Fluid {
|
|
|
|
object2bytelinkedopenhashmap.putAndMoveToFirst(block_a, (byte) (flag ? 1 : 0));
|
|
}
|
|
+ */
|
|
+ if (cache != null) {
|
|
+ cache.putValue(block_a, flag);
|
|
+ }
|
|
+ // Gale end - Airplane - improve fluid direction caching - modify to use our cache
|
|
|
|
return flag;
|
|
}
|