From 92d1247ee35fe8806f04fcd8d9244c0e457a7c47 Mon Sep 17 00:00:00 2001 From: Aikar Date: Fri, 19 Jun 2020 19:10:38 -0400 Subject: [PATCH] Improve light optimizations and fix bugs Rewrites the Threaded task logic to no longer use 2 queues and instead keep a single prioritized queue and do all of a chunks light tasks in a single batch Fix a math issue in one place (Thankfully didn't seem to really be a common place since didn't notice anything) --- ...-Optimize-Bit-Operations-by-inlining.patch | 24 +- .../0537-Optimize-Light-Engine.patch | 540 ++++++++++++------ 2 files changed, 371 insertions(+), 193 deletions(-) diff --git a/Spigot-Server-Patches/0536-Optimize-Bit-Operations-by-inlining.patch b/Spigot-Server-Patches/0536-Optimize-Bit-Operations-by-inlining.patch index 541ecb700..7f9251571 100644 --- a/Spigot-Server-Patches/0536-Optimize-Bit-Operations-by-inlining.patch +++ b/Spigot-Server-Patches/0536-Optimize-Bit-Operations-by-inlining.patch @@ -57,7 +57,7 @@ index 3bf17ccdaef21322b787db538d569e0bc614ef22..4c64798fbc50f4d8b08502ba865c5fde public static long f(long i) { diff --git a/src/main/java/net/minecraft/server/SectionPosition.java b/src/main/java/net/minecraft/server/SectionPosition.java -index ca3f63f5e5e4e926de562b4bfbbf72ad8a6b943f..c415736282de6d4d0f87135c24999c4bbf993901 100644 +index ca3f63f5e5e4e926de562b4bfbbf72ad8a6b943f..22bc96f97d22e0748110982e4b1ff85002f924b5 100644 --- a/src/main/java/net/minecraft/server/SectionPosition.java +++ b/src/main/java/net/minecraft/server/SectionPosition.java @@ -16,7 +16,7 @@ public class SectionPosition extends BaseBlockPosition { @@ -69,7 +69,7 @@ index ca3f63f5e5e4e926de562b4bfbbf72ad8a6b943f..c415736282de6d4d0f87135c24999c4b } public static SectionPosition a(ChunkCoordIntPair chunkcoordintpair, int i) { -@@ -28,7 +28,7 @@ public class SectionPosition extends BaseBlockPosition { +@@ -28,15 +28,23 @@ public class SectionPosition extends BaseBlockPosition { } public static SectionPosition a(long i) { @@ -78,16 +78,24 @@ index ca3f63f5e5e4e926de562b4bfbbf72ad8a6b943f..c415736282de6d4d0f87135c24999c4b } public static long a(long i, EnumDirection enumdirection) { -@@ -36,7 +36,7 @@ public class SectionPosition extends BaseBlockPosition { + return a(i, enumdirection.getAdjacentX(), enumdirection.getAdjacentY(), enumdirection.getAdjacentZ()); } ++ // Paper start ++ public static long getAdjacentFromBlockPos(int x, int y, int z, EnumDirection enumdirection) { ++ return (((long) ((x >> 4) + enumdirection.getAdjacentX()) & 4194303L) << 42) | (((long) ((y >> 4) + enumdirection.getAdjacentY()) & 1048575L)) | (((long) ((z >> 4) + enumdirection.getAdjacentZ()) & 4194303L) << 20); ++ } ++ public static long getAdjacentFromSectionPos(int x, int y, int z, EnumDirection enumdirection) { ++ return (((long) (x + enumdirection.getAdjacentX()) & 4194303L) << 42) | (((long) ((y) + enumdirection.getAdjacentY()) & 1048575L)) | (((long) (z + enumdirection.getAdjacentZ()) & 4194303L) << 20); ++ } ++ // Paper end public static long a(long i, int j, int k, int l) { - return b(b(i) + j, c(i) + k, d(i) + l); + return (((long) ((int) (i >> 42) + j) & 4194303L) << 42) | (((long) ((int) (i << 44 >> 44) + k) & 1048575L)) | (((long) ((int) (i << 22 >> 42) + l) & 4194303L) << 20); // Simplify to reduce instruction count } public static int a(int i) { -@@ -48,11 +48,7 @@ public class SectionPosition extends BaseBlockPosition { +@@ -48,11 +56,7 @@ public class SectionPosition extends BaseBlockPosition { } public static short b(BlockPosition blockposition) { @@ -100,7 +108,7 @@ index ca3f63f5e5e4e926de562b4bfbbf72ad8a6b943f..c415736282de6d4d0f87135c24999c4b } public static int c(int i) { -@@ -60,7 +56,7 @@ public class SectionPosition extends BaseBlockPosition { +@@ -60,7 +64,7 @@ public class SectionPosition extends BaseBlockPosition { } public static int b(long i) { @@ -109,7 +117,7 @@ index ca3f63f5e5e4e926de562b4bfbbf72ad8a6b943f..c415736282de6d4d0f87135c24999c4b } public static int c(long i) { -@@ -71,44 +67,46 @@ public class SectionPosition extends BaseBlockPosition { +@@ -71,44 +75,46 @@ public class SectionPosition extends BaseBlockPosition { return (int) (i << 22 >> 42); } @@ -175,7 +183,7 @@ index ca3f63f5e5e4e926de562b4bfbbf72ad8a6b943f..c415736282de6d4d0f87135c24999c4b } public static long f(long i) { -@@ -116,7 +114,7 @@ public class SectionPosition extends BaseBlockPosition { +@@ -116,7 +122,7 @@ public class SectionPosition extends BaseBlockPosition { } public BlockPosition s() { @@ -184,7 +192,7 @@ index ca3f63f5e5e4e926de562b4bfbbf72ad8a6b943f..c415736282de6d4d0f87135c24999c4b } public BlockPosition t() { -@@ -129,36 +127,30 @@ public class SectionPosition extends BaseBlockPosition { +@@ -129,36 +135,30 @@ public class SectionPosition extends BaseBlockPosition { return new ChunkCoordIntPair(this.a(), this.c()); } diff --git a/Spigot-Server-Patches/0537-Optimize-Light-Engine.patch b/Spigot-Server-Patches/0537-Optimize-Light-Engine.patch index 4fdca41ae..a79b4f92c 100644 --- a/Spigot-Server-Patches/0537-Optimize-Light-Engine.patch +++ b/Spigot-Server-Patches/0537-Optimize-Light-Engine.patch @@ -38,24 +38,18 @@ index d051a54aa04326f84e211cd68ddd2bb209230770..bd7a92599b4182739aafef9eeaaf8665 return this.j; } diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java -index 340d6e992786e8877243ccb472b462e88ec1e6fb..d07fcc0c51679104506d81acfdee6f391f30c0a0 100644 +index 340d6e992786e8877243ccb472b462e88ec1e6fb..451803d3f575b7fcd7ce0891bc1e2c86c2aa2832 100644 --- a/src/main/java/net/minecraft/server/ChunkProviderServer.java +++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java -@@ -1089,12 +1089,13 @@ public class ChunkProviderServer extends IChunkProvider { +@@ -1089,7 +1089,7 @@ public class ChunkProviderServer extends IChunkProvider { if (ChunkProviderServer.this.tickDistanceManager()) { return true; } else { - ChunkProviderServer.this.lightEngine.queueUpdate(); -+ //ChunkProviderServer.this.lightEngine.queueUpdate(); // Paper - move down ++ //ChunkProviderServer.this.lightEngine.queueUpdate(); // Paper - not needed return super.executeNext() || execChunkTask; // Paper } } finally { - playerChunkMap.chunkLoadConversionCallbackExecutor.run(); // Paper - Add chunk load conversion callback executor to prevent deadlock due to recursion in the chunk task queue sorter - playerChunkMap.callbackExecutor.run(); -+ ChunkProviderServer.this.lightEngine.queueUpdate(); // Paper - always run, this is rate limited now - } - // CraftBukkit end - } diff --git a/src/main/java/net/minecraft/server/IBlockData.java b/src/main/java/net/minecraft/server/IBlockData.java index 296b41bf36ee1ace5bd9db2b810bf926b5f5278f..b39554faf235b6f81c86542673dfb4508d4c3e5a 100644 --- a/src/main/java/net/minecraft/server/IBlockData.java @@ -97,7 +91,7 @@ index 296b41bf36ee1ace5bd9db2b810bf926b5f5278f..b39554faf235b6f81c86542673dfb450 public VoxelShape getShape(IBlockAccess iblockaccess, BlockPosition blockposition) { diff --git a/src/main/java/net/minecraft/server/LightEngineBlock.java b/src/main/java/net/minecraft/server/LightEngineBlock.java -index 07fadc21ee12138b52cc77c50da536fec5b032f5..86868bedc65948b00edc8a7b6f219b85cc8c6374 100644 +index 07fadc21ee12138b52cc77c50da536fec5b032f5..a61d0a27e9525505eedaec8cde44216e807eb9a8 100644 --- a/src/main/java/net/minecraft/server/LightEngineBlock.java +++ b/src/main/java/net/minecraft/server/LightEngineBlock.java @@ -13,9 +13,11 @@ public final class LightEngineBlock extends LightEngineLayer> 38); + int y = (int) ((i << 52) >> 52); + int z = (int) ((i << 26) >> 38); -+ long k = SectionPosition.asLong(x >> 4, y >> 4, z >> 4); ++ long k = SectionPosition.blockPosAsSectionLong(x, y, z); + // Paper end EnumDirection[] aenumdirection = LightEngineBlock.e; int l = aenumdirection.length; @@ -175,12 +169,12 @@ index 07fadc21ee12138b52cc77c50da536fec5b032f5..86868bedc65948b00edc8a7b6f219b85 EnumDirection enumdirection = aenumdirection[i1]; - long j1 = BlockPosition.a(i, enumdirection); - long k1 = SectionPosition.e(j1); -+ long j1 = BlockPosition.asLong(x + enumdirection.getAdjacentX(), y + enumdirection.getAdjacentY(), z + enumdirection.getAdjacentZ()); // Paper -+ long k1 = SectionPosition.asLong((x + enumdirection.getAdjacentX()) >> 4, (y + enumdirection.getAdjacentY()) >> 4, (z + enumdirection.getAdjacentZ()) >> 4); // Paper ++ long j1 = BlockPosition.getAdjacent(x, y, z, enumdirection); // Paper ++ long k1 = SectionPosition.getAdjacentFromBlockPos(x, y, z, enumdirection); // Paper if (k == k1 || ((LightEngineStorageBlock) this.c).g(k1)) { this.b(i, j1, j, flag); -@@ -88,27 +103,36 @@ public final class LightEngineBlock extends LightEngineLayer> 38); + int baseY = (int) ((i << 52) >> 52); + int baseZ = (int) ((i << 26) >> 38); -+ long j1 = (((long) (baseX >> 4) & 4194303L) << 42) | (((long) (baseY >> 4) & 1048575L)) | (((long) (baseZ >> 4) & 4194303L) << 20); ++ long j1 = SectionPosition.blockPosAsSectionLong(baseX, baseY, baseZ); + NibbleArray nibblearray = this.c.updating.getUpdatingOptimized(j1); + // Paper end EnumDirection[] aenumdirection = LightEngineBlock.e; @@ -199,15 +193,14 @@ index 07fadc21ee12138b52cc77c50da536fec5b032f5..86868bedc65948b00edc8a7b6f219b85 for (int l1 = 0; l1 < k1; ++l1) { EnumDirection enumdirection = aenumdirection[l1]; - long i2 = BlockPosition.a(i, enumdirection); -- -- if (i2 != j) { -- long j2 = SectionPosition.e(i2); + // Paper start + int newX = baseX + enumdirection.getAdjacentX(); + int newY = baseY + enumdirection.getAdjacentY(); + int newZ = baseZ + enumdirection.getAdjacentZ(); -+ if (newX != baseX || newY != baseY || newZ != baseZ) { -+ long i2 = BlockPosition.asLong(newX, newY, newZ); ++ long i2 = BlockPosition.asLong(newX, newY, newZ); + + if (i2 != j) { +- long j2 = SectionPosition.e(i2); + long j2 = SectionPosition.blockPosAsSectionLong(newX, newY, newZ); + // Paper end NibbleArray nibblearray1; @@ -408,7 +401,7 @@ index f72ff8495bcf704c15040676b95c51fecb72b73a..faa432779ec70c2c6b5fe7fe3523f171 @Override diff --git a/src/main/java/net/minecraft/server/LightEngineSky.java b/src/main/java/net/minecraft/server/LightEngineSky.java -index f0b57784006752e031800a12a1a3c1a5945c636b..3d508fdb0ea86fbe78891113776a1cb312df8dce 100644 +index f0b57784006752e031800a12a1a3c1a5945c636b..32b52ca2462fa206b1184025cb3837d6c326db2d 100644 --- a/src/main/java/net/minecraft/server/LightEngineSky.java +++ b/src/main/java/net/minecraft/server/LightEngineSky.java @@ -29,21 +29,25 @@ public final class LightEngineSky extends LightEngineLayer> 38); + int baseY = (int) ((i << 52) >> 52); + int baseZ = (int) ((i << 26) >> 38); + long k = SectionPosition.blockPosAsSectionLong(baseX, baseY, baseZ); -+ int l = baseY; ++ int i1 = baseY & 15; ++ int j1 = baseY >> 4; + // Paper end - int i1 = SectionPosition.b(l); - int j1 = SectionPosition.a(l); int k1; -@@ -103,22 +112,28 @@ public final class LightEngineSky extends LightEngineLayer> 4; -+ int secY = baseY >> 4; -+ int secZ = baseZ >> 4; -+ for (l1 = 0; !((LightEngineStorageSky) this.c).g(SectionPosition.asLong(secX, secY + -l1 - 1, secZ)) && ((LightEngineStorageSky) this.c).a(j1 - l1 - 1); ++l1) { -+ // Paper end - ; - } + if (i1 != 0) { +@@ -110,15 +118,16 @@ public final class LightEngineSky extends LightEngineLayer> 38); + int baseY = (int) ((i << 52) >> 52); + int baseZ = (int) ((i << 26) >> 38); -+ long j1 = (((long) (baseX >> 4) & 4194303L) << 42) | (((long) (baseY >> 4) & 1048575L)) | (((long) (baseZ >> 4) & 4194303L) << 20); ++ long j1 = SectionPosition.blockPosAsSectionLong(baseX, baseY, baseZ); + NibbleArray nibblearray = this.c.updating.getUpdatingOptimized(j1); + // Paper end EnumDirection[] aenumdirection = LightEngineSky.e; @@ -575,7 +558,7 @@ index f0b57784006752e031800a12a1a3c1a5945c636b..3d508fdb0ea86fbe78891113776a1cb3 if (l > k2) { l = k2; -@@ -206,7 +231,7 @@ public final class LightEngineSky extends LightEngineLayer> e @@ -640,7 +623,7 @@ index b0b7544592981bcff22c8acee7230a211918ef28..84a2589ef0bbb2c5d8b5d4808277916a return m0.c(i); } -@@ -69,27 +69,56 @@ public abstract class LightEngineStorage> e +@@ -69,27 +69,57 @@ public abstract class LightEngineStorage> e protected abstract int d(long i); protected int i(long i) { @@ -650,10 +633,11 @@ index b0b7544592981bcff22c8acee7230a211918ef28..84a2589ef0bbb2c5d8b5d4808277916a + final int x = (int) (i >> 38); + final int y = (int) ((i << 52) >> 52); + final int z = (int) ((i << 26) >> 38); -+ NibbleArray nibblearray = this.updating.getUpdatingOptimized((((long) (x >> 4) & 4194303L) << 42) | (((long) (y >> 4) & 1048575L)) | (((long) (z >> 4) & 4194303L) << 20)); ++ long j = SectionPosition.blockPosAsSectionLong(x, y, z); ++ NibbleArray nibblearray = this.updating.getUpdatingOptimized(j); + // BUG: Sometimes returns null and crashes, try to recover, but to prevent crash just return no light. + if (nibblearray == null) { -+ nibblearray = this.e_visible.lookup.apply((((long) (x >> 4) & 4194303L) << 42) | (((long) (y >> 4) & 1048575L)) | (((long) (z >> 4) & 4194303L) << 20)); ++ nibblearray = this.e_visible.lookup.apply(j); + } + if (nibblearray == null) { + System.err.println("Null nibble, preventing crash " + BlockPosition.fromLong(i)); @@ -671,7 +655,7 @@ index b0b7544592981bcff22c8acee7230a211918ef28..84a2589ef0bbb2c5d8b5d4808277916a + int x = (int) (i >> 38); + int y = (int) ((i << 52) >> 52); + int z = (int) ((i << 26) >> 38); -+ long k = SectionPosition.asLong(x >> 4, y >> 4, z >> 4); ++ long k = SectionPosition.blockPosAsSectionLong(x, y, z); + // Paper end if (this.g.add(k)) { @@ -708,7 +692,7 @@ index b0b7544592981bcff22c8acee7230a211918ef28..84a2589ef0bbb2c5d8b5d4808277916a } } } -@@ -121,17 +150,23 @@ public abstract class LightEngineStorage> e +@@ -121,17 +151,23 @@ public abstract class LightEngineStorage> e } if (k >= 2 && j != 2) { @@ -739,7 +723,7 @@ index b0b7544592981bcff22c8acee7230a211918ef28..84a2589ef0bbb2c5d8b5d4808277916a } } } -@@ -157,9 +192,9 @@ public abstract class LightEngineStorage> e +@@ -157,9 +193,9 @@ public abstract class LightEngineStorage> e return SectionPosition.e(j) == i; }); } else { @@ -752,7 +736,7 @@ index b0b7544592981bcff22c8acee7230a211918ef28..84a2589ef0bbb2c5d8b5d4808277916a for (int i1 = 0; i1 < 16; ++i1) { for (int j1 = 0; j1 < 16; ++j1) { -@@ -186,7 +221,7 @@ public abstract class LightEngineStorage> e +@@ -186,7 +222,7 @@ public abstract class LightEngineStorage> e NibbleArray nibblearray; while (longiterator.hasNext()) { @@ -761,7 +745,7 @@ index b0b7544592981bcff22c8acee7230a211918ef28..84a2589ef0bbb2c5d8b5d4808277916a this.a(lightenginelayer, i); NibbleArray nibblearray1 = (NibbleArray) this.i.remove(i); -@@ -204,48 +239,56 @@ public abstract class LightEngineStorage> e +@@ -204,48 +240,56 @@ public abstract class LightEngineStorage> e longiterator = this.o.iterator(); while (longiterator.hasNext()) { @@ -812,12 +796,12 @@ index b0b7544592981bcff22c8acee7230a211918ef28..84a2589ef0bbb2c5d8b5d4808277916a + // Paper start + i = longiterator.nextLong(); + if (true) { // don't check hasLight, this iterator is filtered already -+ int baseX = (int) (i >> 42); -+ int baseY = (int) (i << 44 >> 44); -+ int baseZ = (int) (i << 22 >> 42); -+ int k = baseX << 4; -+ int l = baseY << 4; -+ int i1 = baseZ << 4; ++ int secX = (int) (i >> 42); ++ int secY = (int) (i << 44 >> 44); ++ int secZ = (int) (i << 22 >> 42); ++ int k = secX << 4; // baseX ++ int l = secY << 4; // baseY ++ int i1 = secZ << 4; // baseZ + // Paper end EnumDirection[] aenumdirection = LightEngineStorage.k; int j1 = aenumdirection.length; @@ -827,12 +811,12 @@ index b0b7544592981bcff22c8acee7230a211918ef28..84a2589ef0bbb2c5d8b5d4808277916a - long l1 = SectionPosition.a(i, enumdirection); - - if (!this.i.containsKey(l1) && this.g(l1)) { -+ long l1 = SectionPosition.asLong(baseX + enumdirection.getAdjacentX(), baseY + enumdirection.getAdjacentY(), baseZ + enumdirection.getAdjacentZ()); // Paper - avoid unpacking ++ long l1 = SectionPosition.getAdjacentFromSectionPos(secX, secY, secZ, enumdirection); // Paper - avoid extra unpacking + if (!propagating.contains(l1) && this.g(l1)) { // Paper - use propagating for (int i2 = 0; i2 < 16; ++i2) { for (int j2 = 0; j2 < 16; ++j2) { long k2; -@@ -287,6 +330,8 @@ public abstract class LightEngineStorage> e +@@ -287,6 +331,8 @@ public abstract class LightEngineStorage> e } } @@ -841,7 +825,7 @@ index b0b7544592981bcff22c8acee7230a211918ef28..84a2589ef0bbb2c5d8b5d4808277916a objectiterator = this.i.long2ObjectEntrySet().iterator(); while (objectiterator.hasNext()) { -@@ -295,7 +340,8 @@ public abstract class LightEngineStorage> e +@@ -295,7 +341,8 @@ public abstract class LightEngineStorage> e if (this.g(j)) { objectiterator.remove(); } @@ -852,59 +836,157 @@ index b0b7544592981bcff22c8acee7230a211918ef28..84a2589ef0bbb2c5d8b5d4808277916a } } diff --git a/src/main/java/net/minecraft/server/LightEngineStorageArray.java b/src/main/java/net/minecraft/server/LightEngineStorageArray.java -index 37c44a89f28c44915fcae5a7e2c4797b1c123723..53c204455c3800b4fe399ce6261b0495eea4d003 100644 +index 37c44a89f28c44915fcae5a7e2c4797b1c123723..549c2551c2b59730bf53a80f8706d388d96ad932 100644 --- a/src/main/java/net/minecraft/server/LightEngineStorageArray.java +++ b/src/main/java/net/minecraft/server/LightEngineStorageArray.java -@@ -43,6 +43,25 @@ public abstract class LightEngineStorageArray> { + +- private final long[] b = new long[2]; +- private final NibbleArray[] c = new NibbleArray[2]; ++ // private final long[] b = new long[2]; // Paper - unused ++ private final NibbleArray[] c = new NibbleArray[]{NibbleArray.EMPTY_NIBBLE_ARRAY, NibbleArray.EMPTY_NIBBLE_ARRAY}; private final NibbleArray[] cache = c; // Paper - OBFHELPER + private boolean d; + protected final com.destroystokyo.paper.util.map.QueuedChangesMapLong2Object data; // Paper - avoid copying light data + protected final boolean isVisible; // Paper - avoid copying light data +- java.util.function.Function lookup; // Paper - faster branchless lookup + ++ // Paper start - faster lookups with less branching, use interface to avoid boxing instead of Function ++ public final NibbleArrayAccess lookup; ++ public interface NibbleArrayAccess { ++ NibbleArray apply(long id); ++ } ++ // Paper end + // Paper start - avoid copying light data + protected LightEngineStorageArray(com.destroystokyo.paper.util.map.QueuedChangesMapLong2Object data, boolean isVisible) { + if (isVisible) { +@@ -19,12 +24,14 @@ public abstract class LightEngineStorageArray 0; --k) { +- this.b[k] = this.b[k - 1]; +- this.c[k] = this.c[k - 1]; +- } +- +- this.b[0] = i; +- this.c[0] = nibblearray; +- } +- ++ cache[1] = cache[0]; ++ cache[0] = nibblearray; + return nibblearray; + } + } + // Paper end + ++ @Nullable ++ public final NibbleArray c(final long i) { // Paper - final ++ // Paper start - optimize visible case or missed updating cases ++ if (this.d) { ++ // short circuit to optimized ++ return getUpdatingOptimized(i); ++ } ++ ++ return this.lookup.apply(i); ++ // Paper end ++ } + @Nullable - public final NibbleArray c(long i) { // Paper - final - if (this.d) { + public NibbleArray d(long i) { +@@ -80,13 +89,14 @@ public abstract class LightEngineStorageArray> 38); + int baseY = (int) ((i << 52) >> 52); + int baseZ = (int) ((i << 26) >> 38); + long j = (((long) (baseX >> 4) & 4194303L) << 42) | (((long) (baseY >> 4) & 1048575L)) | (((long) (baseZ >> 4) & 4194303L) << 20); ++ NibbleArray nibblearray = this.e_visible.lookup.apply(j); ++ return nibblearray == null ? 0 : nibblearray.a(baseX & 15, baseY & 15, baseZ & 15); + // Paper end - NibbleArray nibblearray = this.a(j, false); - -- return nibblearray == null ? 0 : nibblearray.a(SectionPosition.b(BlockPosition.b(i)), SectionPosition.b(BlockPosition.c(i)), SectionPosition.b(BlockPosition.d(i))); -+ return nibblearray == null ? 0 : nibblearray.a(baseX & 15, baseY & 15, baseZ & 15); // Paper } public static final class a extends LightEngineStorageArray { diff --git a/src/main/java/net/minecraft/server/LightEngineStorageSky.java b/src/main/java/net/minecraft/server/LightEngineStorageSky.java -index 097f58e9ac3f4096d3b9dad75b6ebe76021fa92c..5ccd81bd0d49052fe84d4f34e29774f0cc2dd732 100644 +index 097f58e9ac3f4096d3b9dad75b6ebe76021fa92c..f744f62c93370d096c113f92ee81a8232c35501d 100644 --- a/src/main/java/net/minecraft/server/LightEngineStorageSky.java +++ b/src/main/java/net/minecraft/server/LightEngineStorageSky.java @@ -22,7 +22,12 @@ public class LightEngineStorageSky extends LightEngineStorage> 38); + int baseY = (int) ((i << 52) >> 52); + int baseZ = (int) ((i << 26) >> 38); -+ long j = (((long) (baseX >> 4) & 4194303L) << 42) | (((long) (baseY >> 4) & 1048575L)) | (((long) (baseZ >> 4) & 4194303L) << 20); ++ long j = SectionPosition.blockPosAsSectionLong(baseX, baseY, baseZ); + // Paper end int k = SectionPosition.c(j); synchronized (this.visibleUpdateLock) { // Paper - avoid copying light data @@ -971,7 +1053,7 @@ index 097f58e9ac3f4096d3b9dad75b6ebe76021fa92c..5ccd81bd0d49052fe84d4f34e29774f0 EnumDirection enumdirection = aenumdirection[l1]; - k1 = SectionPosition.a(i, enumdirection); -+ k1 = SectionPosition.asLong(baseX + enumdirection.getAdjacentX(), baseY + enumdirection.getAdjacentY(), baseZ + enumdirection.getAdjacentZ()); // Paper ++ k1 = SectionPosition.getAdjacentFromBlockPos(baseX, baseY, baseZ, enumdirection); // Paper if ((this.n.contains(k1) || !this.l.contains(k1) && !this.m.contains(k1)) && this.g(k1)) { for (int i2 = 0; i2 < 16; ++i2) { for (int j2 = 0; j2 < 16; ++j2) { @@ -1013,53 +1095,71 @@ index 097f58e9ac3f4096d3b9dad75b6ebe76021fa92c..5ccd81bd0d49052fe84d4f34e29774f0 lightenginelayer.a(Long.MAX_VALUE, l3, 15, false); } diff --git a/src/main/java/net/minecraft/server/LightEngineThreaded.java b/src/main/java/net/minecraft/server/LightEngineThreaded.java -index 8776799de033f02b0f87e9ea7e4a4ce912e94dd4..72cc711d6c2645aed44f208ee44f8702d64c5af8 100644 +index 8776799de033f02b0f87e9ea7e4a4ce912e94dd4..604fe85313969bf1ad5b5ef75b46f9a23f79764a 100644 --- a/src/main/java/net/minecraft/server/LightEngineThreaded.java +++ b/src/main/java/net/minecraft/server/LightEngineThreaded.java -@@ -15,8 +15,70 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { +@@ -14,8 +14,98 @@ import org.apache.logging.log4j.Logger; + public class LightEngineThreaded extends LightEngine implements AutoCloseable { private static final Logger LOGGER = LogManager.getLogger(); - private final ThreadedMailbox b; +- private final ThreadedMailbox b; - private final ObjectList> c = new ObjectArrayList(); -- private final PlayerChunkMap d; -+ // Paper start - add urgent queue, switch to ArrayDeque -+ private long nextNonUrgent = 0; -+ private boolean shouldPollNonUrgent() { -+ return urgent.isEmpty() && !c.isEmpty() && (this.c.size() >= this.f || System.nanoTime() > nextNonUrgent); -+ } -+ -+ private boolean shouldPollUrgent() { -+ return (super.a() || !urgent.isEmpty()); -+ } -+ -+ private IntSupplier getChunkPrioritySupplier(ChunkCoordIntPair coords) { -+ return getChunkMap().getPrioritySupplier(coords.pair()); -+ } ++ private final ThreadedMailbox b; ThreadedMailbox mailbox; // Paper ++ // Paper start + private static final int MAX_PRIORITIES = PlayerChunkMap.GOLDEN_TICKET + 2; -+ private static class LightQueueBucket extends java.util.ArrayDeque> { -+ public LightQueueBucket() { -+ super(64); -+ } ++ ++ public void changePriority(long pair, int currentPriority, int priority) { ++ this.mailbox.queue(() -> { ++ ChunkLightQueue remove = this.queue.buckets[currentPriority].remove(pair); ++ if (remove != null) { ++ ChunkLightQueue existing = this.queue.buckets[priority].put(pair, remove); ++ if (existing != null) { ++ remove.pre.addAll(existing.pre); ++ remove.post.addAll(existing.post); ++ } ++ } ++ }); + } ++ ++ static class ChunkLightQueue { ++ public boolean shouldFastUpdate; ++ java.util.ArrayDeque pre = new java.util.ArrayDeque(); ++ java.util.ArrayDeque post = new java.util.ArrayDeque(); ++ ++ ChunkLightQueue(long chunk) {} ++ } ++ ++ + // Retain the chunks priority level for queued light tasks + private static class LightQueue { -+ + private int size = 0; + private int lowestPriority = MAX_PRIORITIES; -+ private final LightQueueBucket[] buckets = new LightQueueBucket[MAX_PRIORITIES]; ++ private final it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap[] buckets = new it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap[MAX_PRIORITIES]; + + private LightQueue() { + for (int i = 0; i < buckets.length; i++) { -+ buckets[i] = new LightQueueBucket(); ++ buckets[i] = new it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap<>(); + } + } + -+ public final void add(int priority, LightEngineThreaded.Update type, Runnable run) { ++ public final void add(long chunkId, int priority, LightEngineThreaded.Update type, Runnable run) { ++ add(chunkId, priority, type, run, false); ++ } ++ public final void add(long chunkId, int priority, LightEngineThreaded.Update type, Runnable run, boolean shouldFastUpdate) { ++ ChunkLightQueue lightQueue = this.buckets[priority].computeIfAbsent(chunkId, ChunkLightQueue::new); + this.size++; -+ if (lowestPriority > priority) { -+ lowestPriority = priority; ++ if (type == Update.PRE_UPDATE) { ++ lightQueue.pre.add(run); ++ } else { ++ lightQueue.post.add(run); ++ } ++ if (shouldFastUpdate) { ++ lightQueue.shouldFastUpdate = true; ++ } ++ ++ if (this.lowestPriority > priority) { ++ this.lowestPriority = priority; + } -+ this.buckets[priority].add(new Pair<>(type, run)); + } + + public final boolean isEmpty() { @@ -1070,82 +1170,102 @@ index 8776799de033f02b0f87e9ea7e4a4ce912e94dd4..72cc711d6c2645aed44f208ee44f8702 + return this.size; + } + -+ public Pair poll() { -+ for (; lowestPriority < MAX_PRIORITIES; lowestPriority++) { -+ Pair entry = buckets[lowestPriority].pollFirst(); -+ if (entry != null) { -+ this.size--; -+ return entry; ++ public boolean poll(java.util.List pre, java.util.List post) { ++ boolean hasWork = false; ++ it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap[] buckets = this.buckets; ++ while (lowestPriority < MAX_PRIORITIES && !isEmpty()) { ++ it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap bucket = buckets[lowestPriority]; ++ if (bucket.isEmpty()) { ++ lowestPriority++; ++ continue; ++ } ++ ChunkLightQueue queue = bucket.removeFirst(); ++ this.size -= queue.pre.size() + queue.post.size(); ++ pre.addAll(queue.pre); ++ post.addAll(queue.post); ++ queue.pre.clear(); ++ queue.post.clear(); ++ hasWork = true; ++ if (queue.shouldFastUpdate) { ++ return true; + } + } -+ return null; ++ return hasWork; + } + } + -+ private final LightQueue urgent = new LightQueue(); -+ private final LightQueue c = new LightQueue(); ++ private final LightQueue queue = new LightQueue(); + // Paper end -+ private final PlayerChunkMap d; private PlayerChunkMap getChunkMap() { return d; } // Paper - OBFHELPER + private final PlayerChunkMap d; private final Mailbox> e; private volatile int f = 5; - private final AtomicBoolean g = new AtomicBoolean(); -@@ -110,11 +172,14 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { +@@ -25,7 +115,7 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { + super(ilightaccess, true, flag); + this.d = playerchunkmap; + this.e = mailbox; +- this.b = threadedmailbox; ++ this.mailbox = this.b = threadedmailbox; // Paper } + public void close() {} +@@ -111,8 +201,11 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { + private void a(int i, int j, IntSupplier intsupplier, LightEngineThreaded.Update lightenginethreaded_update, Runnable runnable) { -+ // Paper start -+ scheduleLightTask(i, j, intsupplier, lightenginethreaded_update, runnable, false); -+ } -+ private void scheduleLightTask(int i, int j, IntSupplier intsupplier, LightEngineThreaded.Update lightenginethreaded_update, Runnable runnable, boolean urgent) { this.e.a(ChunkTaskQueueSorter.a(() -> { // Paper - decompile error - this.c.add(Pair.of(lightenginethreaded_update, runnable)); - if (this.c.size() >= this.f) { -- this.b(); -- } -+ (urgent ? this.urgent : this.c).add(intsupplier.getAsInt(), lightenginethreaded_update, runnable); -+ if (shouldPollUrgent() || shouldPollNonUrgent()) queueUpdate(); -+ // Paper end ++ // Paper start ++ int priority = intsupplier.getAsInt(); ++ this.queue.add(ChunkCoordIntPair.pair(i, j), priority, lightenginethreaded_update, runnable); // Paper ++ if (priority <= 25) { // don't auto kick off unless priority ++ // Paper end + this.b(); + } - }, ChunkCoordIntPair.pair(i, j), intsupplier)); - } -@@ -134,7 +199,7 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { +@@ -134,7 +227,14 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { ChunkCoordIntPair chunkcoordintpair = ichunkaccess.getPos(); ichunkaccess.b(false); - this.a(chunkcoordintpair.x, chunkcoordintpair.z, LightEngineThreaded.Update.PRE_UPDATE, SystemUtils.a(() -> { -+ this.scheduleLightTask(chunkcoordintpair.x, chunkcoordintpair.z, getChunkPrioritySupplier(chunkcoordintpair), LightEngineThreaded.Update.PRE_UPDATE, SystemUtils.a(() -> { // Paper ++ // Paper start ++ long pair = chunkcoordintpair.pair(); ++ CompletableFuture future = new CompletableFuture<>(); ++ IntSupplier prioritySupplier1 = d.getPrioritySupplier(pair); ++ IntSupplier prioritySupplier = flag ? () -> Math.max(1, prioritySupplier1.getAsInt() - 10) : prioritySupplier1; ++ this.e.a(ChunkTaskQueueSorter.a(() -> { ++ this.queue.add(pair, prioritySupplier.getAsInt(), LightEngineThreaded.Update.PRE_UPDATE, SystemUtils.a(() -> { ++ // Paper end ChunkSection[] achunksection = ichunkaccess.getSections(); for (int i = 0; i < 16; ++i) { -@@ -155,51 +220,58 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { +@@ -155,52 +255,45 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { this.d.c(chunkcoordintpair); }, () -> { return "lightChunk " + chunkcoordintpair + " " + flag; -- })); -+ }), true); // Paper - urgent flag - return CompletableFuture.supplyAsync(() -> { ++ // Paper start - merge the 2 together + })); +- return CompletableFuture.supplyAsync(() -> { ++ ++ this.queue.add(pair, prioritySupplier.getAsInt(), LightEngineThreaded.Update.POST_UPDATE, () -> { ichunkaccess.b(true); super.b(chunkcoordintpair, false); - return ichunkaccess; - }, (runnable) -> { +- return ichunkaccess; +- }, (runnable) -> { - this.a(chunkcoordintpair.x, chunkcoordintpair.z, LightEngineThreaded.Update.POST_UPDATE, runnable); -+ this.scheduleLightTask(chunkcoordintpair.x, chunkcoordintpair.z, getChunkPrioritySupplier(chunkcoordintpair), LightEngineThreaded.Update.POST_UPDATE, runnable, true); // Paper -+ queueUpdate(); // Paper ++ // Paper start ++ future.complete(ichunkaccess); }); ++ queueUpdate(); // run queue now ++ }, pair, prioritySupplier)); ++ return future; ++ // Paper end } public void queueUpdate() { - if ((!this.c.isEmpty() || super.a()) && this.g.compareAndSet(false, true)) { -+ if ((shouldPollUrgent() || shouldPollNonUrgent()) && this.g.compareAndSet(false, true)) { // Paper - level check is now in shouldPollUrgent ++ if ((!this.queue.isEmpty() || super.a()) && this.g.compareAndSet(false, true)) { // Paper this.b.a((() -> { // Paper - decompile error -- this.b(); -+ // Paper start -+ if (shouldPollUrgent()) { -+ do { -+ this.runQueue(true); -+ } while (shouldPollUrgent()); -+ } else if (shouldPollNonUrgent()) this.runQueue(false); // don't loop non urgent as urgent might come in -+ // Paper end + this.b(); this.g.set(false); + queueUpdate(); // Paper - if we still have work to do, do it! })); @@ -1153,6 +1273,7 @@ index 8776799de033f02b0f87e9ea7e4a4ce912e94dd4..72cc711d6c2645aed44f208ee44f8702 } ++ // Paper start - replace impl private void b() { - int i = Math.min(this.c.size(), this.f); - ObjectListIterator> objectlistiterator = this.c.iterator(); @@ -1164,28 +1285,12 @@ index 8776799de033f02b0f87e9ea7e4a4ce912e94dd4..72cc711d6c2645aed44f208ee44f8702 - pair = (Pair) objectlistiterator.next(); - if (pair.getFirst() == LightEngineThreaded.Update.PRE_UPDATE) { - ((Runnable) pair.getSecond()).run(); -+ // Paper start - replace impl, use more effecient deque, avoid single removes (iterator.remove() which does a lot of copying) -+ runQueue(!this.urgent.isEmpty()); -+ } -+ private void runQueue(boolean urgent) { -+ LightQueue col = urgent ? this.urgent : c; -+ java.util.List> pre = new java.util.ArrayList<>(); -+ java.util.List> post = new java.util.ArrayList<>(); -+ int i = Math.min(col.size(), 8); // process small batches so chunks can progress without waiting for everything -+ Pair pair; -+ while (i-- > 0 && (pair = col.poll()) != null) { -+ if (pair.getFirst() == Update.PRE_UPDATE) { -+ pre.add(pair); -+ } else { -+ post.add(pair); - } - } - +- } +- } +- - objectlistiterator.back(j); -+ pre.forEach(entry -> entry.getSecond().run()); - super.a(Integer.MAX_VALUE, true, true); -+ post.forEach(entry -> entry.getSecond().run()); - +- super.a(Integer.MAX_VALUE, true, true); +- - for (j = 0; objectlistiterator.hasNext() && j < i; ++j) { - pair = (Pair) objectlistiterator.next(); - if (pair.getFirst() == LightEngineThreaded.Update.POST_UPDATE) { @@ -1193,14 +1298,53 @@ index 8776799de033f02b0f87e9ea7e4a4ce912e94dd4..72cc711d6c2645aed44f208ee44f8702 - } - - objectlistiterator.remove(); -- } -+ if (!urgent && this.c.isEmpty()) nextNonUrgent = System.nanoTime() + (50 * 1000000); ++ java.util.List pre = new java.util.ArrayList<>(); ++ java.util.List post = new java.util.ArrayList<>(); ++ int i = Math.min(queue.size(), 4); ++ while (i-- > 0 && queue.poll(pre, post)) { ++ pre.forEach(Runnable::run); ++ pre.clear(); ++ super.a(Integer.MAX_VALUE, true, true); ++ post.forEach(Runnable::run); ++ post.clear(); + } +- + // Paper end - } + public void a(int i) { +diff --git a/src/main/java/net/minecraft/server/NibbleArray.java b/src/main/java/net/minecraft/server/NibbleArray.java +index 8cedfdd820cc02a76607b53e0b054fc74654f907..a9795394c9b17f9f0ce4c4f9c8f51a48e950418e 100644 +--- a/src/main/java/net/minecraft/server/NibbleArray.java ++++ b/src/main/java/net/minecraft/server/NibbleArray.java +@@ -8,6 +8,13 @@ import javax.annotation.Nullable; + public class NibbleArray { + + // Paper start ++ static final NibbleArray EMPTY_NIBBLE_ARRAY = new NibbleArray() { ++ @Override ++ public byte[] asBytes() { ++ throw new IllegalStateException(); ++ } ++ }; ++ long lightCacheKey = Long.MIN_VALUE; + public static byte[] EMPTY_NIBBLE = new byte[2048]; + private static final int nibbleBucketSizeMultiplier = Integer.getInteger("Paper.nibbleBucketSize", 3072); + private static final int maxPoolSize = Integer.getInteger("Paper.maxNibblePoolSize", (int) Math.min(6, Math.max(1, Runtime.getRuntime().maxMemory() / 1024 / 1024 / 1024)) * (nibbleBucketSizeMultiplier * 8)); +diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java +index f8820f24075e7f42f67426fc9ecf5238f4499b72..0ef55c49235f8e024268b5411eeaf4ddc76c921d 100644 +--- a/src/main/java/net/minecraft/server/PlayerChunk.java ++++ b/src/main/java/net/minecraft/server/PlayerChunk.java +@@ -729,6 +729,7 @@ public class PlayerChunk { + ioPriority = com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGH_PRIORITY; + } + chunkMap.world.asyncChunkTaskManager.raisePriority(location.x, location.z, ioPriority); ++ chunkMap.world.getChunkProvider().getLightEngine().changePriority(location.pair(), getCurrentPriority(), priority); + } + if (getCurrentPriority() != priority) { + this.w.a(this.location, this::getCurrentPriority, priority, this::setPriority); // use preferred priority diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java -index babac7a8513a1f7698ef2fb7263f13f8f3c9717c..e00692f0e19798798b1bc38c8c4c53be71447b21 100644 +index 394cea57b3871c2e11d1f7e0e9546df64ff3bafe..8abf276a325cbc3a863fb89276526790c4de2692 100644 --- a/src/main/java/net/minecraft/server/PlayerChunkMap.java +++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java @@ -629,6 +629,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { @@ -1211,3 +1355,29 @@ index babac7a8513a1f7698ef2fb7263f13f8f3c9717c..e00692f0e19798798b1bc38c8c4c53be protected IntSupplier c(long i) { return () -> { PlayerChunk playerchunk = this.getVisibleChunk(i); +diff --git a/src/main/java/net/minecraft/server/ThreadedMailbox.java b/src/main/java/net/minecraft/server/ThreadedMailbox.java +index 8082569022384a3ba03fb4a6f1ae12b443598dcb..3db8073f5182fe4dd4c710b202afc323dfb9b2d2 100644 +--- a/src/main/java/net/minecraft/server/ThreadedMailbox.java ++++ b/src/main/java/net/minecraft/server/ThreadedMailbox.java +@@ -93,7 +93,8 @@ public class ThreadedMailbox implements Mailbox, AutoCloseable, Runnable { + + } + +- @Override ++ ++ public void queue(T t0) { a(t0); } @Override // Paper - OBFHELPER + public void a(T t0) { + this.a.a(t0); + this.f(); +diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java +index 49da7352dcee6c352904cabe8b5db0152c427029..46e261b6513b52f5562387b661de4a409ea515a3 100644 +--- a/src/main/java/net/minecraft/server/WorldServer.java ++++ b/src/main/java/net/minecraft/server/WorldServer.java +@@ -685,6 +685,7 @@ public class WorldServer extends World { + } + gameprofilerfiller.exit(); + timings.chunkTicksBlocks.stopTiming(); // Paper ++ getChunkProvider().getLightEngine().queueUpdate(); // Paper + // Paper end + } + }