9
0
mirror of https://github.com/Winds-Studio/Leaf.git synced 2025-12-19 15:09:25 +00:00

reduce LevelChunkSection#tickingBlocks memory overhead

This commit is contained in:
hayanesuru
2025-06-14 22:58:48 +09:00
parent cf0c547648
commit 942376481b
2 changed files with 244 additions and 59 deletions

View File

@@ -4,6 +4,18 @@ Date: Fri, 6 Jun 2025 20:46:10 +0900
Subject: [PATCH] optimize random tick Subject: [PATCH] optimize random tick
diff --git a/ca/spottedleaf/moonrise/patches/block_counting/BlockCountingChunkSection.java b/ca/spottedleaf/moonrise/patches/block_counting/BlockCountingChunkSection.java
index 0d1443a113c07d7655e7b927a899447f70db8fa9..5f219ce1e386b29616694aef223136eec0ec80de 100644
--- a/ca/spottedleaf/moonrise/patches/block_counting/BlockCountingChunkSection.java
+++ b/ca/spottedleaf/moonrise/patches/block_counting/BlockCountingChunkSection.java
@@ -6,6 +6,6 @@ public interface BlockCountingChunkSection {
public boolean moonrise$hasSpecialCollidingBlocks();
- public ShortList moonrise$getTickingBlockList();
+ // public ShortList moonrise$getTickingBlockList(); // Leaf - optimize random tick
}
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
index 15bbd1f7f2a90b4b5427026d622764bb1c92d465..bc37fa20143eda45f9df07e382f2feca3909abce 100644 index 15bbd1f7f2a90b4b5427026d622764bb1c92d465..bc37fa20143eda45f9df07e382f2feca3909abce 100644
--- a/net/minecraft/server/level/ServerChunkCache.java --- a/net/minecraft/server/level/ServerChunkCache.java
@@ -17,9 +29,27 @@ index 15bbd1f7f2a90b4b5427026d622764bb1c92d465..bc37fa20143eda45f9df07e382f2feca
if (flagAndHasNaturalSpawn) { // Gale - MultiPaper - skip unnecessary mob spawning computations if (flagAndHasNaturalSpawn) { // Gale - MultiPaper - skip unnecessary mob spawning computations
this.level.tickCustomSpawners(this.spawnEnemies, this.spawnFriendlies); this.level.tickCustomSpawners(this.spawnEnemies, this.spawnFriendlies);
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index eb849c57992658005e0f514c6f7923f8ca43bebf..cc6fd8533fce1839600488b996f9179d1399380d 100644 index eb849c57992658005e0f514c6f7923f8ca43bebf..1591f04b34a9807f689a076112ef34060a600fc4 100644
--- a/net/minecraft/server/level/ServerLevel.java --- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java
@@ -1097,7 +1097,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
final net.minecraft.world.level.chunk.PalettedContainer<net.minecraft.world.level.block.state.BlockState> states = section.states;
// Leaf end - Micro optimizations for random tick
- final ca.spottedleaf.moonrise.common.list.ShortList tickList = section.moonrise$getTickingBlockList(); // Leaf - Micro optimizations for random tick - no redundant cast
+ final org.dreeam.leaf.world.RandomTickSystem.TickingBlockSet tickList = section.tickingBlocks; // Leaf - Micro optimizations for random tick - no redundant cast // Leaf - optimize random tick
for (int i = 0; i < tickSpeed; ++i) {
final int index = simpleRandom.nextInt() & ((16 * 16 * 16) - 1);
@@ -1107,7 +1107,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
continue;
}
- final int location = tickList.getRaw(index); // Leaf - Micro optimizations for random tick - no unnecessary operations
+ final int location = tickList.get(simpleRandom); // Leaf - Micro optimizations for random tick - no unnecessary operations // Leaf - optimize random tick
final BlockState state = states.get(location);
// do not use a mutable pos, as some random tick implementations store the input without calling immutable()!
@@ -1128,6 +1128,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe @@ -1128,6 +1128,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
private int currentIceAndSnowTick = 0; protected void resetIceAndSnowTick() { this.currentIceAndSnowTick = this.simpleRandom.nextInt(16); } // Gale - Airplane - optimize random calls in chunk ticking private int currentIceAndSnowTick = 0; protected void resetIceAndSnowTick() { this.currentIceAndSnowTick = this.simpleRandom.nextInt(16); } // Gale - Airplane - optimize random calls in chunk ticking
@@ -39,59 +69,22 @@ index eb849c57992658005e0f514c6f7923f8ca43bebf..cc6fd8533fce1839600488b996f9179d
} }
diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java
index a90bf0d80ae4dac9b19b8e467b402917cc19a271..44b672671e0bea6a5f91e9b8573f9a8225a20f6a 100644 index a90bf0d80ae4dac9b19b8e467b402917cc19a271..165cdb884c6e0aa80d035fc0b2e95c7dc311761e 100644
--- a/net/minecraft/world/level/chunk/LevelChunk.java --- a/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/net/minecraft/world/level/chunk/LevelChunk.java +++ b/net/minecraft/world/level/chunk/LevelChunk.java
@@ -149,6 +149,48 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p @@ -149,6 +149,11 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p
} }
// Gale end - Airplane - optimize random calls in chunk ticking - instead of using a random every time the chunk is ticked, define when lightning strikes preemptively // Gale end - Airplane - optimize random calls in chunk ticking - instead of using a random every time the chunk is ticked, define when lightning strikes preemptively
+ // Leaf start - optimize random tick + // Leaf start - optimize random tick
+ private boolean leaf$tickingBlocksDirty = true; + public boolean leaf$tickingBlocksDirty = true;
+ private int leaf$tickingBlocksCount; + public int leaf$tickingBlocksCount;
+ private int leaf$firstTickingSectionIndex = -1; + public int leaf$firstTickingSectionIndex = -1;
+ public final int leaf$tickingBlocksCount() {
+ if (!leaf$tickingBlocksDirty) {
+ return leaf$tickingBlocksCount;
+ }
+ leaf$tickingBlocksDirty = false;
+ int sum = 0;
+ leaf$firstTickingSectionIndex = -1;
+ for (int i = 0; i < sections.length; i++) {
+ LevelChunkSection section = sections[i];
+ int size = section.moonrise$getTickingBlockList().size();
+ if (size != 0 && leaf$firstTickingSectionIndex == -1) {
+ leaf$firstTickingSectionIndex = i;
+ }
+ sum += size;
+ }
+ leaf$tickingBlocksCount = sum;
+ return sum;
+ }
+ public final java.util.OptionalLong leaf$getTickingPos(int idx) {
+ if (leaf$firstTickingSectionIndex != -1) {
+ for (int i = leaf$firstTickingSectionIndex; i < sections.length; i++) {
+ LevelChunkSection section = sections[i];
+ var l = section.moonrise$getTickingBlockList();
+ int size = l.size();
+ if (idx < size) {
+ short loc = l.getRaw(idx);
+ int x = (loc & 15) | (chunkPos.x << 4);
+ int y = (loc >>> 8) | ((getMinSectionY() + i) << 4);
+ int z = ((loc >>> 4) & 15) | (chunkPos.z << 4);
+ return java.util.OptionalLong.of(BlockPos.asLong(x, y, z));
+ }
+ idx -= size;
+ }
+ }
+ leaf$tickingBlocksDirty = true;
+ return java.util.OptionalLong.empty();
+ }
+ // Leaf end - optimize random tick + // Leaf end - optimize random tick
public LevelChunk(Level level, ChunkPos pos) { public LevelChunk(Level level, ChunkPos pos) {
this(level, pos, UpgradeData.EMPTY, new LevelChunkTicks<>(), new LevelChunkTicks<>(), 0L, null, null, null); this(level, pos, UpgradeData.EMPTY, new LevelChunkTicks<>(), new LevelChunkTicks<>(), 0L, null, null, null);
} }
@@ -417,6 +459,11 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p @@ -417,6 +422,11 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p
if (blockState == state) { if (blockState == state) {
return null; return null;
} else { } else {
@@ -103,3 +96,52 @@ index a90bf0d80ae4dac9b19b8e467b402917cc19a271..44b672671e0bea6a5f91e9b8573f9a82
Block block = state.getBlock(); Block block = state.getBlock();
this.heightmaps.get(Heightmap.Types.MOTION_BLOCKING).update(i, y, i2, state); this.heightmaps.get(Heightmap.Types.MOTION_BLOCKING).update(i, y, i2, state);
this.heightmaps.get(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES).update(i, y, i2, state); this.heightmaps.get(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES).update(i, y, i2, state);
diff --git a/net/minecraft/world/level/chunk/LevelChunkSection.java b/net/minecraft/world/level/chunk/LevelChunkSection.java
index 82f723ab2e92036c56ce6f09109a9c0e25dc025e..889fb5dda79a062f4dee242c2a32640e801fd397 100644
--- a/net/minecraft/world/level/chunk/LevelChunkSection.java
+++ b/net/minecraft/world/level/chunk/LevelChunkSection.java
@@ -37,17 +37,22 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_
private boolean isClient;
private static final short CLIENT_FORCED_SPECIAL_COLLIDING_BLOCKS = (short)9999;
private short specialCollidingBlocks;
- private final ca.spottedleaf.moonrise.common.list.ShortList tickingBlocks = new ca.spottedleaf.moonrise.common.list.ShortList();
+ // private final ca.spottedleaf.moonrise.common.list.ShortList tickingBlocks = new ca.spottedleaf.moonrise.common.list.ShortList(); // Leaf - optimize random tick
+ public final org.dreeam.leaf.world.RandomTickSystem.TickingBlockSet tickingBlocks = new org.dreeam.leaf.world.RandomTickSystem.TickingBlockSet(); // Leaf - optimize random tick
@Override
public final boolean moonrise$hasSpecialCollidingBlocks() {
return this.specialCollidingBlocks != 0;
}
+ // Leaf start - optimize random tick
+ /*
@Override
public final ca.spottedleaf.moonrise.common.list.ShortList moonrise$getTickingBlockList() {
return this.tickingBlocks;
}
+ */
+ // Leaf end - optimize random tick
// Paper end - block counting
private LevelChunkSection(LevelChunkSection section) {
@@ -123,7 +128,7 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_
final boolean oldTicking = oldState.isRandomlyTicking();
final boolean newTicking = newState.isRandomlyTicking();
if (oldTicking != newTicking) {
- final ca.spottedleaf.moonrise.common.list.ShortList tickingBlocks = this.tickingBlocks;
+ final org.dreeam.leaf.world.RandomTickSystem.TickingBlockSet tickingBlocks = this.tickingBlocks; // Leaf - optimize random tick
final short position = (short)(x | (z << 4) | (y << (4+4)));
if (oldTicking) {
@@ -236,9 +241,9 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_
final short[] raw = coordinates.elements();
final int rawLen = raw.length;
- final ca.spottedleaf.moonrise.common.list.ShortList tickingBlocks = this.tickingBlocks;
+ final org.dreeam.leaf.world.RandomTickSystem.TickingBlockSet tickingBlocks = this.tickingBlocks; // Leaf - optimize random tick
- tickingBlocks.setMinCapacity(Math.min((rawLen + tickingBlocks.size()) * 3 / 2, 16*16*16));
+ // tickingBlocks.setMinCapacity(Math.min((rawLen + tickingBlocks.size()) * 3 / 2, 16*16*16)); // Leaf - optimize random tick
java.util.Objects.checkFromToIndex(0, paletteCount, raw.length);
for (int i = 0; i < paletteCount; ++i) {

View File

@@ -1,14 +1,16 @@
package org.dreeam.leaf.world; package org.dreeam.leaf.world;
import it.unimi.dsi.fastutil.HashCommon;
import it.unimi.dsi.fastutil.longs.LongArrayList; import it.unimi.dsi.fastutil.longs.LongArrayList;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.LevelChunkSection;
import net.minecraft.world.level.levelgen.BitRandomSource;
import net.minecraft.world.level.material.FluidState; import net.minecraft.world.level.material.FluidState;
import java.util.OptionalLong; import java.util.Arrays;
public final class RandomTickSystem { public final class RandomTickSystem {
private static final long SCALE = 0x100000L; private static final long SCALE = 0x100000L;
@@ -99,12 +101,35 @@ public final class RandomTickSystem {
return world.chunkSource.getChunkAtIfLoadedImmediately((int) packed, (int) (packed >> 32)); return world.chunkSource.getChunkAtIfLoadedImmediately((int) packed, (int) (packed >> 32));
} }
private static void tickBlock(ServerLevel world, LevelChunk chunk, RandomSource random) { private static void tickBlock(ServerLevel world, LevelChunk chunk, BitRandomSource random) {
OptionalLong optionalPos = chunk.leaf$getTickingPos(random.nextInt(chunk.leaf$tickingBlocksCount())); if (chunk.leaf$firstTickingSectionIndex == -1) {
if (optionalPos.isEmpty()) {
return; return;
} }
BlockPos pos = BlockPos.of(optionalPos.getAsLong()); int idx = random.nextInt(chunk.leaf$tickingBlocksCount);
LevelChunkSection[] sections = chunk.getSections();
int cx = chunk.locX;
int cz = chunk.locZ;
BlockPos pos = null;
for (int i = chunk.leaf$firstTickingSectionIndex; i < sections.length; i++) {
LevelChunkSection section = sections[i];
var l = section.tickingBlocks;
int size = l.size();
if (idx < size) {
short loc = l.get(random);
int x = (loc & 15) | (cx << 4);
int y = (loc >>> 8) | ((chunk.getMinSectionY() + i) << 4);
int z = ((loc >>> 4) & 15) | (cz << 4);
pos = new BlockPos(x, y, z);
break;
}
idx -= size;
}
if (pos == null) {
chunk.leaf$tickingBlocksDirty = true;
chunk.leaf$firstTickingSectionIndex = -1;
return;
}
BlockState state = chunk.getBlockStateFinal(pos.getX(), pos.getY(), pos.getZ()); BlockState state = chunk.getBlockStateFinal(pos.getX(), pos.getY(), pos.getZ());
state.randomTick(world, pos, random); state.randomTick(world, pos, random);
@@ -118,7 +143,7 @@ public final class RandomTickSystem {
} }
public void tickChunk( public void tickChunk(
RandomSource random, BitRandomSource random,
LevelChunk chunk, LevelChunk chunk,
long tickSpeed long tickSpeed
) { ) {
@@ -128,14 +153,30 @@ public final class RandomTickSystem {
} else { } else {
this.bits += BITS_STEP; this.bits += BITS_STEP;
} }
if ((this.cacheRandom & (TICK_MASK << bits)) == 0L) { if ((this.cacheRandom & (TICK_MASK << bits)) != 0L) {
int count = chunk.leaf$tickingBlocksCount(); return;
if (count != 0L) { }
long weight = (TICK_MUL * tickSpeed * count * SCALE) / CHUNK_BLOCKS; if (chunk.leaf$tickingBlocksDirty) {
samples.add(chunk.getPos().longKey); chunk.leaf$tickingBlocksDirty = false;
weights.add(weight); int sum = 0;
weightsSum += weight; chunk.leaf$firstTickingSectionIndex = -1;
LevelChunkSection[] sections = chunk.getSections();
for (int i = 0; i < sections.length; i++) {
LevelChunkSection section = sections[i];
int size = section.tickingBlocks.size();
if (size != 0 && chunk.leaf$firstTickingSectionIndex == -1) {
chunk.leaf$firstTickingSectionIndex = i;
}
sum += size;
} }
chunk.leaf$tickingBlocksCount = sum;
}
int count = chunk.leaf$tickingBlocksCount;
if (count != 0L) {
long weight = (TICK_MUL * tickSpeed * count * SCALE) / CHUNK_BLOCKS;
samples.add(chunk.getPos().longKey);
weights.add(weight);
weightsSum += weight;
} }
} }
@@ -148,7 +189,7 @@ public final class RandomTickSystem {
* *
* @see java.util.random.RandomGenerator#nextLong(long) nextLong(bound) * @see java.util.random.RandomGenerator#nextLong(long) nextLong(bound)
*/ */
public static long boundedNextLong(RandomSource rng, long bound) { public static long boundedNextLong(BitRandomSource rng, long bound) {
final long m = bound - 1; final long m = bound - 1;
long r = rng.nextLong(); long r = rng.nextLong();
if ((bound & m) == 0L) { if ((bound & m) == 0L) {
@@ -161,4 +202,106 @@ public final class RandomTickSystem {
} }
return r; return r;
} }
public static final class TickingBlockSet {
private static final short EMPTY = -1;
private static final short[] EMPTY_ARRAY = {};
private static final int DEFAULT_CAP = 8;
private short[] a = EMPTY_ARRAY;
private int size;
private int bits;
public void clear() {
a = EMPTY_ARRAY;
size = 0;
bits = 0;
}
/// @param n {@code n >= 0 && n <= 4096}
public boolean add(short n) {
if (a == EMPTY_ARRAY) {
a = new short[DEFAULT_CAP];
Arrays.fill(a, EMPTY);
bits = Integer.numberOfTrailingZeros(DEFAULT_CAP);
}
return addShort(n);
}
/// @param n {@code n >= 0 && n <= 4096}
private boolean addShort(short n) {
if (size >= a.length >>> 1) {
resize(a.length << 1);
}
int i = HashCommon.mix(n) & (a.length - 1);
int start = i;
do {
if (a[i] == n) {
return false;
}
if (a[i] == EMPTY) {
a[i] = n;
size++;
return true;
}
i = (i + 1) & (a.length - 1);
} while (i != start);
return false;
}
/// @param n {@code n >= 0 && n <= 4096}
public boolean remove(short n) {
if (size == 0) {
return false;
}
int i = HashCommon.mix(n) & (a.length - 1);
int start = i;
do {
if (a[i] == n) {
a[i] = EMPTY;
size--;
i = (i + 1) & (a.length - 1);
while (a[i] != EMPTY) {
short rehash = a[i];
a[i] = EMPTY;
size--;
addShort(rehash);
i = (i + 1) & (a.length - 1);
}
return true;
}
if (a[i] == EMPTY) {
return false;
}
i = (i + 1) & (a.length - 1);
} while (i != start);
return false;
}
public short get(BitRandomSource rand) {
if (size == 0) return EMPTY;
while (true) {
int i = rand.next(bits);
if (a[i] != EMPTY) return a[i];
}
}
public int size() {
return size;
}
private void resize(int cap) {
short[] o = a;
a = new short[cap];
Arrays.fill(a, EMPTY);
size = 0;
bits = Integer.numberOfTrailingZeros(cap);
for (short val : o) {
if (val != EMPTY) {
addShort(val);
}
}
}
}
} }