diff options
Diffstat (limited to 'Spigot-Server-Patches-Unmapped/0493-Optimize-Light-Engine.patch')
-rw-r--r-- | Spigot-Server-Patches-Unmapped/0493-Optimize-Light-Engine.patch | 1433 |
1 files changed, 1433 insertions, 0 deletions
diff --git a/Spigot-Server-Patches-Unmapped/0493-Optimize-Light-Engine.patch b/Spigot-Server-Patches-Unmapped/0493-Optimize-Light-Engine.patch new file mode 100644 index 0000000000..be7ebd1442 --- /dev/null +++ b/Spigot-Server-Patches-Unmapped/0493-Optimize-Light-Engine.patch @@ -0,0 +1,1433 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Aikar <[email protected]> +Date: Thu, 4 Jun 2020 22:43:29 -0400 +Subject: [PATCH] Optimize Light Engine + +Massive update to light to improve performance and chunk loading/generation. + +1) Massive bit packing/unpacking optimizations and inlining. + A lot of performance has to do with constant packing and unpacking of bits. + We now inline a most bit operations, and re-use base x/y/z bits in many places. + This helps with cpu level processing to just do all the math at once instead + of having to jump in and out of function calls. + + This much logic also is likely over the JVM Inline limit for JIT too. +2) Applied a few of JellySquid's Phosphor mod optimizations such as + - ensuring we don't notify neighbor chunks when neighbor chunk doesn't need to be notified + - reduce hasLight checks in initializing light, and prob some more, they are tagged JellySquid where phosphor influence was used. +3) Optimize hot path accesses to getting updating chunk to have less branching +4) Optimize getBlock accesses to have less branching, and less unpacking +5) Have a separate urgent bucket for chunk light tasks. These tasks will always cut in line over non blocking light tasks. +6) Retain chunk priority while light tasks are enqueued. So if a task comes in at high priority but the queue is full + of tasks already at a lower priority, before the task was simply added to the end. Now it can cut in line to the front. + this applies for both urgent and non urgent tasks. +7) Buffer non urgent tasks even if queueUpdate is called multiple times to improve efficiency. +8) Fix NPE risk that crashes server in getting nibble data + +diff --git a/src/main/java/net/minecraft/server/level/ChunkProviderServer.java b/src/main/java/net/minecraft/server/level/ChunkProviderServer.java +index 19e02d12bb0c5a73066f7c57da40277918276210..326c52f9f2cc729fd52162aeae18ec9dae3a4eaf 100644 +--- a/src/main/java/net/minecraft/server/level/ChunkProviderServer.java ++++ b/src/main/java/net/minecraft/server/level/ChunkProviderServer.java +@@ -1070,7 +1070,7 @@ public class ChunkProviderServer extends IChunkProvider { + if (ChunkProviderServer.this.tickDistanceManager()) { + return true; + } else { +- ChunkProviderServer.this.lightEngine.queueUpdate(); ++ ChunkProviderServer.this.lightEngine.queueUpdate(); // Paper - not needed + return super.executeNext() || execChunkTask; // Paper + } + } finally { +diff --git a/src/main/java/net/minecraft/server/level/LightEngineGraphSection.java b/src/main/java/net/minecraft/server/level/LightEngineGraphSection.java +index c8bb040e7ed848877ec9c2f9b30dcda137cadf35..4ee7070364a8989eece4fa4237b529926821f9c9 100644 +--- a/src/main/java/net/minecraft/server/level/LightEngineGraphSection.java ++++ b/src/main/java/net/minecraft/server/level/LightEngineGraphSection.java +@@ -16,14 +16,20 @@ public abstract class LightEngineGraphSection extends LightEngineGraph { + + @Override + protected void a(long i, int j, boolean flag) { ++ // Paper start ++ int x = (int) (i >> 42); ++ int y = (int) (i << 44 >> 44); ++ int z = (int) (i << 22 >> 42); ++ // Paper end + for (int k = -1; k <= 1; ++k) { + for (int l = -1; l <= 1; ++l) { + for (int i1 = -1; i1 <= 1; ++i1) { +- long j1 = SectionPosition.a(i, k, l, i1); ++ if (k == 0 && l == 0 && i1 == 0) continue; // Paper ++ long j1 = (((long) (x + k) & 4194303L) << 42) | (((long) (y + l) & 1048575L)) | (((long) (z + i1) & 4194303L) << 20); // Paper + +- if (j1 != i) { ++ //if (j1 != i) { // Paper - checked above + this.b(i, j1, j, flag); +- } ++ //} // Paper + } + } + } +@@ -34,10 +40,15 @@ public abstract class LightEngineGraphSection extends LightEngineGraph { + protected int a(long i, long j, int k) { + int l = k; + ++ // Paper start ++ int x = (int) (i >> 42); ++ int y = (int) (i << 44 >> 44); ++ int z = (int) (i << 22 >> 42); ++ // Paper end + for (int i1 = -1; i1 <= 1; ++i1) { + for (int j1 = -1; j1 <= 1; ++j1) { + for (int k1 = -1; k1 <= 1; ++k1) { +- long l1 = SectionPosition.a(i, i1, j1, k1); ++ long l1 = (((long) (x + i1) & 4194303L) << 42) | (((long) (y + j1) & 1048575L)) | (((long) (z + k1) & 4194303L) << 20); // Paper + + if (l1 == i) { + l1 = Long.MAX_VALUE; +diff --git a/src/main/java/net/minecraft/server/level/LightEngineThreaded.java b/src/main/java/net/minecraft/server/level/LightEngineThreaded.java +index e066848127cb9a42e8c39422691cc65132cac6bb..0b80569648c1df01aab52d0b8d47028cda925d86 100644 +--- a/src/main/java/net/minecraft/server/level/LightEngineThreaded.java ++++ b/src/main/java/net/minecraft/server/level/LightEngineThreaded.java +@@ -1,6 +1,7 @@ + package net.minecraft.server.level; + + import com.mojang.datafixers.util.Pair; ++import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; // Paper + import it.unimi.dsi.fastutil.objects.ObjectArrayList; + import it.unimi.dsi.fastutil.objects.ObjectList; + import it.unimi.dsi.fastutil.objects.ObjectListIterator; +@@ -16,6 +17,7 @@ import net.minecraft.util.thread.ThreadedMailbox; + import net.minecraft.world.level.ChunkCoordIntPair; + import net.minecraft.world.level.EnumSkyBlock; + import net.minecraft.world.level.chunk.ChunkSection; ++import net.minecraft.world.level.chunk.ChunkStatus; + import net.minecraft.world.level.chunk.IChunkAccess; + import net.minecraft.world.level.chunk.ILightAccess; + import net.minecraft.world.level.chunk.NibbleArray; +@@ -27,15 +29,149 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { + + private static final Logger LOGGER = LogManager.getLogger(); + private final ThreadedMailbox<Runnable> b; +- private final ObjectList<Pair<LightEngineThreaded.Update, Runnable>> c = new ObjectArrayList(); +- private final PlayerChunkMap d; ++ // Paper start ++ private static final int MAX_PRIORITIES = PlayerChunkMap.GOLDEN_TICKET + 2; ++ ++ private boolean isChunkLightStatus(long pair) { ++ PlayerChunk playerChunk = playerChunkMap.getVisibleChunk(pair); ++ if (playerChunk == null) { ++ return false; ++ } ++ ChunkStatus status = PlayerChunk.getChunkStatus(playerChunk.getTicketLevel()); ++ return status != null && status.isAtLeastStatus(ChunkStatus.LIGHT); ++ } ++ ++ static class ChunkLightQueue { ++ public boolean shouldFastUpdate; ++ java.util.ArrayDeque<Runnable> pre = new java.util.ArrayDeque<Runnable>(); ++ java.util.ArrayDeque<Runnable> post = new java.util.ArrayDeque<Runnable>(); ++ ++ ChunkLightQueue(long chunk) {} ++ } ++ ++ static class PendingLightTask { ++ long chunkId; ++ IntSupplier priority; ++ Runnable pre; ++ Runnable post; ++ boolean fastUpdate; ++ ++ public PendingLightTask(long chunkId, IntSupplier priority, Runnable pre, Runnable post, boolean fastUpdate) { ++ this.chunkId = chunkId; ++ this.priority = priority; ++ this.pre = pre; ++ this.post = post; ++ this.fastUpdate = fastUpdate; ++ } ++ } ++ ++ ++ // Retain the chunks priority level for queued light tasks ++ class LightQueue { ++ private int size = 0; ++ private final Long2ObjectLinkedOpenHashMap<ChunkLightQueue>[] buckets = new Long2ObjectLinkedOpenHashMap[MAX_PRIORITIES]; ++ private final java.util.concurrent.ConcurrentLinkedQueue<PendingLightTask> pendingTasks = new java.util.concurrent.ConcurrentLinkedQueue<>(); ++ private final java.util.concurrent.ConcurrentLinkedQueue<Runnable> priorityChanges = new java.util.concurrent.ConcurrentLinkedQueue<>(); ++ ++ private LightQueue() { ++ for (int i = 0; i < buckets.length; i++) { ++ buckets[i] = new Long2ObjectLinkedOpenHashMap<>(); ++ } ++ } ++ ++ public void changePriority(long pair, int currentPriority, int priority) { ++ this.priorityChanges.add(() -> { ++ ChunkLightQueue remove = this.buckets[currentPriority].remove(pair); ++ if (remove != null) { ++ ChunkLightQueue existing = this.buckets[Math.max(1, priority)].put(pair, remove); ++ if (existing != null) { ++ remove.pre.addAll(existing.pre); ++ remove.post.addAll(existing.post); ++ } ++ } ++ }); ++ } ++ ++ public final void addChunk(long chunkId, IntSupplier priority, Runnable pre, Runnable post) { ++ pendingTasks.add(new PendingLightTask(chunkId, priority, pre, post, true)); ++ queueUpdate(); ++ } ++ ++ public final void add(long chunkId, IntSupplier priority, LightEngineThreaded.Update type, Runnable run) { ++ pendingTasks.add(new PendingLightTask(chunkId, priority, type == Update.PRE_UPDATE ? run : null, type == Update.POST_UPDATE ? run : null, false)); ++ } ++ public final void add(PendingLightTask update) { ++ int priority = update.priority.getAsInt(); ++ ChunkLightQueue lightQueue = this.buckets[priority].computeIfAbsent(update.chunkId, ChunkLightQueue::new); ++ ++ if (update.pre != null) { ++ this.size++; ++ lightQueue.pre.add(update.pre); ++ } ++ if (update.post != null) { ++ this.size++; ++ lightQueue.post.add(update.post); ++ } ++ if (update.fastUpdate) { ++ lightQueue.shouldFastUpdate = true; ++ } ++ } ++ ++ public final boolean isEmpty() { ++ return this.size == 0 && this.pendingTasks.isEmpty(); ++ } ++ ++ public final int size() { ++ return this.size; ++ } ++ ++ public boolean poll(java.util.List<Runnable> pre, java.util.List<Runnable> post) { ++ PendingLightTask pending; ++ while ((pending = pendingTasks.poll()) != null) { ++ add(pending); ++ } ++ Runnable run; ++ while ((run = priorityChanges.poll()) != null) { ++ run.run(); ++ } ++ boolean hasWork = false; ++ Long2ObjectLinkedOpenHashMap<ChunkLightQueue>[] buckets = this.buckets; ++ int priority = 0; ++ while (priority < MAX_PRIORITIES && !isEmpty()) { ++ Long2ObjectLinkedOpenHashMap<ChunkLightQueue> bucket = buckets[priority]; ++ if (bucket.isEmpty()) { ++ priority++; ++ if (hasWork) { ++ return true; ++ } else { ++ 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 hasWork; ++ } ++ } ++ ++ final LightQueue queue = new LightQueue(); ++ // Paper end ++ private final PlayerChunkMap d; private final PlayerChunkMap playerChunkMap; // Paper + private final Mailbox<ChunkTaskQueueSorter.a<Runnable>> e; + private volatile int f = 5; + private final AtomicBoolean g = new AtomicBoolean(); + + public LightEngineThreaded(ILightAccess ilightaccess, PlayerChunkMap playerchunkmap, boolean flag, ThreadedMailbox<Runnable> threadedmailbox, Mailbox<ChunkTaskQueueSorter.a<Runnable>> mailbox) { + super(ilightaccess, true, flag); +- this.d = playerchunkmap; ++ this.d = playerchunkmap; this.playerChunkMap = d; // Paper + this.e = mailbox; + this.b = threadedmailbox; + } +@@ -122,13 +258,9 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { + } + + private void a(int i, int j, IntSupplier intsupplier, LightEngineThreaded.Update lightenginethreaded_update, Runnable runnable) { +- this.e.a(ChunkTaskQueueSorter.a(() -> { +- this.c.add(Pair.of(lightenginethreaded_update, runnable)); +- if (this.c.size() >= this.f) { +- this.b(); +- } +- +- }, ChunkCoordIntPair.pair(i, j), intsupplier)); ++ // Paper start - replace method ++ this.queue.add(ChunkCoordIntPair.pair(i, j), intsupplier, lightenginethreaded_update, runnable); ++ // Paper end + } + + @Override +@@ -145,8 +277,19 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { + public CompletableFuture<IChunkAccess> a(IChunkAccess ichunkaccess, boolean flag) { + ChunkCoordIntPair chunkcoordintpair = ichunkaccess.getPos(); + +- ichunkaccess.b(false); +- this.a(chunkcoordintpair.x, chunkcoordintpair.z, LightEngineThreaded.Update.PRE_UPDATE, SystemUtils.a(() -> { ++ // Paper start ++ //ichunkaccess.b(false); // Don't need to disable this ++ long pair = chunkcoordintpair.pair(); ++ CompletableFuture<IChunkAccess> future = new CompletableFuture<>(); ++ IntSupplier prioritySupplier = playerChunkMap.getPrioritySupplier(pair); ++ boolean[] skippedPre = {false}; ++ this.queue.addChunk(pair, prioritySupplier, SystemUtils.a(() -> { ++ if (!isChunkLightStatus(pair)) { ++ future.complete(ichunkaccess); ++ skippedPre[0] = true; ++ return; ++ } ++ // Paper end + ChunkSection[] achunksection = ichunkaccess.getSections(); + + for (int i = 0; i < 16; ++i) { +@@ -164,55 +307,48 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { + }); + } + +- this.d.c(chunkcoordintpair); ++ // this.d.c(chunkcoordintpair); // Paper - move into post task below + }, () -> { + return "lightChunk " + chunkcoordintpair + " " + flag; +- })); +- return CompletableFuture.supplyAsync(() -> { ++ // Paper start - merge the 2 together ++ }), () -> { ++ this.d.c(chunkcoordintpair); // Paper - release light tickets as post task to ensure they stay loaded until fully done ++ if (skippedPre[0]) return; // Paper - future's already complete + ichunkaccess.b(true); + super.b(chunkcoordintpair, false); +- return ichunkaccess; +- }, (runnable) -> { +- this.a(chunkcoordintpair.x, chunkcoordintpair.z, LightEngineThreaded.Update.POST_UPDATE, runnable); ++ // Paper start ++ future.complete(ichunkaccess); + }); ++ return future; ++ // Paper end + } + + public void queueUpdate() { +- if ((!this.c.isEmpty() || super.a()) && this.g.compareAndSet(false, true)) { ++ if ((!this.queue.isEmpty() || super.a()) && this.g.compareAndSet(false, true)) { // Paper + this.b.a((() -> { // Paper - decompile error + this.b(); + this.g.set(false); ++ queueUpdate(); // Paper - if we still have work to do, do it! + })); + } + + } + ++ // Paper start - replace impl ++ private final java.util.List<Runnable> pre = new java.util.ArrayList<>(); ++ private final java.util.List<Runnable> post = new java.util.ArrayList<>(); + private void b() { +- int i = Math.min(this.c.size(), this.f); +- ObjectListIterator<Pair<LightEngineThreaded.Update, Runnable>> objectlistiterator = this.c.iterator(); +- +- Pair pair; +- int j; +- +- for (j = 0; objectlistiterator.hasNext() && j < i; ++j) { +- pair = (Pair) objectlistiterator.next(); +- if (pair.getFirst() == LightEngineThreaded.Update.PRE_UPDATE) { +- ((Runnable) pair.getSecond()).run(); +- } ++ if (queue.poll(pre, post)) { ++ pre.forEach(Runnable::run); ++ pre.clear(); ++ super.a(Integer.MAX_VALUE, true, true); ++ post.forEach(Runnable::run); ++ post.clear(); ++ } else { ++ // might have level updates to go still ++ super.a(Integer.MAX_VALUE, true, true); + } +- +- objectlistiterator.back(j); +- 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) { +- ((Runnable) pair.getSecond()).run(); +- } +- +- objectlistiterator.remove(); +- } +- ++ // Paper end + } + + public void a(int i) { +diff --git a/src/main/java/net/minecraft/server/level/PlayerChunk.java b/src/main/java/net/minecraft/server/level/PlayerChunk.java +index 88022e3ccd04f9c041ced68be66a95247c1017e9..d6a5a0b17308913a5efd97cd27fabd0825ef68c6 100644 +--- a/src/main/java/net/minecraft/server/level/PlayerChunk.java ++++ b/src/main/java/net/minecraft/server/level/PlayerChunk.java +@@ -754,6 +754,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().queue.changePriority(location.pair(), getCurrentPriority(), priority); + } + if (getCurrentPriority() != priority) { + this.u.a(this.location, this::getCurrentPriority, priority, this::setPriority); // use preferred priority +diff --git a/src/main/java/net/minecraft/server/level/PlayerChunkMap.java b/src/main/java/net/minecraft/server/level/PlayerChunkMap.java +index 7318103feafd12ed631f907a450c9dc3d665a9a3..b47cd2a8fb4920531d80acfcfe40f8211fedc9ae 100644 +--- a/src/main/java/net/minecraft/server/level/PlayerChunkMap.java ++++ b/src/main/java/net/minecraft/server/level/PlayerChunkMap.java +@@ -99,6 +99,7 @@ import net.minecraft.world.level.chunk.storage.RegionFile; + import net.minecraft.world.level.levelgen.structure.StructureStart; + import net.minecraft.world.level.levelgen.structure.templatesystem.DefinedStructureManager; + import net.minecraft.world.level.storage.Convertable; ++import net.minecraft.world.level.storage.WorldDataServer; + import net.minecraft.world.level.storage.WorldPersistentData; + import net.minecraft.world.phys.Vec3D; + import it.unimi.dsi.fastutil.objects.ObjectRBTreeSet; // Paper +@@ -331,6 +332,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { + } + // Paper end + ++ private final java.util.concurrent.ExecutorService lightThread; + public PlayerChunkMap(WorldServer worldserver, Convertable.ConversionSession convertable_conversionsession, DataFixer datafixer, DefinedStructureManager definedstructuremanager, Executor executor, IAsyncTaskHandler<Runnable> iasynctaskhandler, ILightAccess ilightaccess, ChunkGenerator chunkgenerator, WorldLoadListener worldloadlistener, Supplier<WorldPersistentData> supplier, int i, boolean flag) { + super(new File(convertable_conversionsession.a(worldserver.getDimensionKey()), "region"), datafixer, flag); + //this.visibleChunks = this.updatingChunks.clone(); // Paper - no more cloning +@@ -362,7 +364,15 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { + Mailbox<Runnable> mailbox = Mailbox.a("main", iasynctaskhandler::a); + + this.worldLoadListener = worldloadlistener; +- ThreadedMailbox<Runnable> lightthreaded; ThreadedMailbox<Runnable> threadedmailbox1 = lightthreaded = ThreadedMailbox.a(executor, "light"); // Paper ++ // Paper start - use light thread ++ ThreadedMailbox<Runnable> lightthreaded; ThreadedMailbox<Runnable> threadedmailbox1 = lightthreaded = ThreadedMailbox.a(lightThread = java.util.concurrent.Executors.newSingleThreadExecutor(r -> { ++ Thread thread = new Thread(r); ++ thread.setName(((WorldDataServer)world.getWorldData()).getName() + " - Light"); ++ thread.setDaemon(true); ++ thread.setPriority(Thread.NORM_PRIORITY+1); ++ return thread; ++ }), "light"); ++ // Paper end + + this.p = new ChunkTaskQueueSorter(ImmutableList.of(threadedmailbox, mailbox, threadedmailbox1), executor, Integer.MAX_VALUE); + this.mailboxWorldGen = this.p.a(threadedmailbox, false); +@@ -708,6 +718,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { + // Paper end + } + ++ protected final IntSupplier getPrioritySupplier(long i) { return c(i); } // Paper - OBFHELPER + protected IntSupplier c(long i) { + return () -> { + PlayerChunk playerchunk = this.getVisibleChunk(i); +@@ -835,6 +846,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { + @Override + public void close() throws IOException { + try { ++ this.lightThread.shutdown(); // Paper + this.p.close(); + this.world.asyncChunkTaskManager.close(true); // Paper - Required since we're closing regionfiles in the next line + this.m.close(); +diff --git a/src/main/java/net/minecraft/util/thread/ThreadedMailbox.java b/src/main/java/net/minecraft/util/thread/ThreadedMailbox.java +index 2efca1fe92b2e93dcbf5337eea8855b1b2b9a564..72bfda620f073fd3c3e4c43d78583386dadf95e6 100644 +--- a/src/main/java/net/minecraft/util/thread/ThreadedMailbox.java ++++ b/src/main/java/net/minecraft/util/thread/ThreadedMailbox.java +@@ -110,7 +110,8 @@ public class ThreadedMailbox<T> implements Mailbox<T>, AutoCloseable, Runnable { + + } + +- @Override ++ ++ public final 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/world/level/chunk/NibbleArray.java b/src/main/java/net/minecraft/world/level/chunk/NibbleArray.java +index b82420e9a5d42a4383d24921614fe613c640edb9..0fec15e141051863dbf51a2b3e1ace5028cd2fc1 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/NibbleArray.java ++++ b/src/main/java/net/minecraft/world/level/chunk/NibbleArray.java +@@ -11,6 +11,13 @@ import net.minecraft.server.MCUtil; + public class NibbleArray { + + // Paper start ++ public static final NibbleArray EMPTY_NIBBLE_ARRAY = new NibbleArray() { ++ @Override ++ public byte[] asBytes() { ++ throw new IllegalStateException(); ++ } ++ }; ++ public 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/world/level/lighting/LightEngineBlock.java b/src/main/java/net/minecraft/world/level/lighting/LightEngineBlock.java +index f6198069e3ca421b4f551939263c7cf8bd5b754e..29e98864209c51368a91fa9e530c33cbf9830b51 100644 +--- a/src/main/java/net/minecraft/world/level/lighting/LightEngineBlock.java ++++ b/src/main/java/net/minecraft/world/level/lighting/LightEngineBlock.java +@@ -23,9 +23,11 @@ public final class LightEngineBlock extends LightEngineLayer<LightEngineStorageB + } + + private int d(long i) { +- int j = BlockPosition.b(i); +- int k = BlockPosition.c(i); +- int l = BlockPosition.d(i); ++ // Paper start - inline math ++ int j = (int) (i >> 38); ++ int k = (int) ((i << 52) >> 52); ++ int l = (int) ((i << 26) >> 38); ++ // Paper end + IBlockAccess iblockaccess = this.a.c(j >> 4, l >> 4); + + return iblockaccess != null ? iblockaccess.g(this.f.d(j, k, l)) : 0; +@@ -40,25 +42,33 @@ public final class LightEngineBlock extends LightEngineLayer<LightEngineStorageB + } else if (k >= 15) { + return k; + } else { +- int l = Integer.signum(BlockPosition.b(j) - BlockPosition.b(i)); +- int i1 = Integer.signum(BlockPosition.c(j) - BlockPosition.c(i)); +- int j1 = Integer.signum(BlockPosition.d(j) - BlockPosition.d(i)); ++ // Paper start - reuse math - credit to JellySquid for idea ++ int jx = (int) (j >> 38); ++ int jy = (int) ((j << 52) >> 52); ++ int jz = (int) ((j << 26) >> 38); ++ int ix = (int) (i >> 38); ++ int iy = (int) ((i << 52) >> 52); ++ int iz = (int) ((i << 26) >> 38); ++ int l = Integer.signum(jx - ix); ++ int i1 = Integer.signum(jy - iy); ++ int j1 = Integer.signum(jz - iz); ++ // Paper end + EnumDirection enumdirection = EnumDirection.a(l, i1, j1); + + if (enumdirection == null) { + return 15; + } else { + //MutableInt mutableint = new MutableInt(); // Paper - share mutableint, single threaded +- IBlockData iblockdata = this.a(j, mutableint); +- +- if (mutableint.getValue() >= 15) { ++ IBlockData iblockdata = this.getBlockOptimized(jx, jy, jz, mutableint); // Paper ++ int blockedLight = mutableint.getValue(); // Paper ++ if (blockedLight >= 15) { // Paper + return 15; + } else { +- IBlockData iblockdata1 = this.a(i, (MutableInt) null); ++ IBlockData iblockdata1 = this.getBlockOptimized(ix, iy, iz); // Paper + VoxelShape voxelshape = this.a(iblockdata1, i, enumdirection); + VoxelShape voxelshape1 = this.a(iblockdata, j, enumdirection.opposite()); + +- return VoxelShapes.b(voxelshape, voxelshape1) ? 15 : k + Math.max(1, mutableint.getValue()); ++ return VoxelShapes.b(voxelshape, voxelshape1) ? 15 : k + Math.max(1, blockedLight); // Paper + } + } + } +@@ -66,14 +76,19 @@ public final class LightEngineBlock extends LightEngineLayer<LightEngineStorageB + + @Override + protected void a(long i, int j, boolean flag) { +- long k = SectionPosition.e(i); ++ // Paper start - reuse unpacking, credit to JellySquid (Didn't do full optimization though) ++ int x = (int) (i >> 38); ++ int y = (int) ((i << 52) >> 52); ++ int z = (int) ((i << 26) >> 38); ++ long k = SectionPosition.blockPosAsSectionLong(x, y, z); ++ // Paper end + EnumDirection[] aenumdirection = LightEngineBlock.e; + int l = aenumdirection.length; + + for (int i1 = 0; i1 < l; ++i1) { + EnumDirection enumdirection = aenumdirection[i1]; +- long j1 = BlockPosition.a(i, enumdirection); +- long k1 = SectionPosition.e(j1); ++ long j1 = BlockPosition.getAdjacent(x, y, z, enumdirection); // Paper ++ long k1 = SectionPosition.blockToSection(j1); // Paper + + if (k == k1 || ((LightEngineStorageBlock) this.c).g(k1)) { + this.b(i, j1, j, flag); +@@ -98,27 +113,37 @@ public final class LightEngineBlock extends LightEngineLayer<LightEngineStorageB + } + } + +- long j1 = SectionPosition.e(i); +- NibbleArray nibblearray = ((LightEngineStorageBlock) this.c).a(j1, true); ++ // Paper start ++ int baseX = (int) (i >> 38); ++ int baseY = (int) ((i << 52) >> 52); ++ int baseZ = (int) ((i << 26) >> 38); ++ long j1 = SectionPosition.blockPosAsSectionLong(baseX, baseY, baseZ); ++ NibbleArray nibblearray = this.c.updating.getUpdatingOptimized(j1); ++ // Paper end + EnumDirection[] aenumdirection = LightEngineBlock.e; + int k1 = aenumdirection.length; + + for (int l1 = 0; l1 < k1; ++l1) { + EnumDirection enumdirection = aenumdirection[l1]; +- long i2 = BlockPosition.a(i, enumdirection); ++ // Paper start ++ int newX = baseX + enumdirection.getAdjacentX(); ++ int newY = baseY + enumdirection.getAdjacentY(); ++ int newZ = baseZ + enumdirection.getAdjacentZ(); ++ 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; + + if (j1 == j2) { + nibblearray1 = nibblearray; + } else { +- nibblearray1 = ((LightEngineStorageBlock) this.c).a(j2, true); ++ nibblearray1 = ((LightEngineStorageBlock) this.c).updating.getUpdatingOptimized(j2); // Paper + } + + if (nibblearray1 != null) { +- int k2 = this.b(i2, i, this.a(nibblearray1, i2)); ++ int k2 = this.b(i2, i, this.getNibbleLightInverse(nibblearray1, newX, newY, newZ)); // Paper + + if (l > k2) { + l = k2; +diff --git a/src/main/java/net/minecraft/world/level/lighting/LightEngineLayer.java b/src/main/java/net/minecraft/world/level/lighting/LightEngineLayer.java +index 944a8c295ff9df0d96800ddc4f6763598cf61d0d..64dad8ed7c16011d9cb3e9d22ac6f892c638e3b2 100644 +--- a/src/main/java/net/minecraft/world/level/lighting/LightEngineLayer.java ++++ b/src/main/java/net/minecraft/world/level/lighting/LightEngineLayer.java +@@ -10,6 +10,7 @@ import net.minecraft.world.level.EnumSkyBlock; + import net.minecraft.world.level.IBlockAccess; + import net.minecraft.world.level.block.Blocks; + import net.minecraft.world.level.block.state.IBlockData; ++import net.minecraft.world.level.chunk.IChunkAccess; + import net.minecraft.world.level.chunk.ILightAccess; + import net.minecraft.world.level.chunk.NibbleArray; + import net.minecraft.world.phys.shapes.VoxelShape; +@@ -23,10 +24,37 @@ public abstract class LightEngineLayer<M extends LightEngineStorageArray<M>, S e + protected final EnumSkyBlock b; + protected final S c; + private boolean f; +- protected final BlockPosition.MutableBlockPosition d = new BlockPosition.MutableBlockPosition(); ++ protected final BlockPosition.MutableBlockPosition d = new BlockPosition.MutableBlockPosition(); protected final BlockPosition.MutableBlockPosition pos = d; // Paper + private final long[] g = new long[2]; +- private final IBlockAccess[] h = new IBlockAccess[2]; ++ private final IChunkAccess[] h = new IChunkAccess[2]; // Paper + ++ // Paper start - see fully commented out method below (look for Bedrock) ++ // optimized method with less branching for when scenarios arent needed. ++ // avoid using mutable version if can ++ protected final IBlockData getBlockOptimized(int x, int y, int z, MutableInt mutableint) { ++ IChunkAccess iblockaccess = this.a(x >> 4, z >> 4); ++ ++ if (iblockaccess == null) { ++ mutableint.setValue(16); ++ return Blocks.BEDROCK.getBlockData(); ++ } else { ++ this.pos.setValues(x, y, z); ++ IBlockData iblockdata = iblockaccess.getType(x, y, z); ++ mutableint.setValue(iblockdata.b(this.a.getWorld(), this.pos)); ++ return iblockdata.l() && iblockdata.e() ? iblockdata : Blocks.AIR.getBlockData(); ++ } ++ } ++ protected final IBlockData getBlockOptimized(int x, int y, int z) { ++ IChunkAccess iblockaccess = this.a(x >> 4, z >> 4); ++ ++ if (iblockaccess == null) { ++ return Blocks.BEDROCK.getBlockData(); ++ } else { ++ IBlockData iblockdata = iblockaccess.getType(x, y, z); ++ return iblockdata.l() && iblockdata.e() ? iblockdata : Blocks.AIR.getBlockData(); ++ } ++ } ++ // Paper end + public LightEngineLayer(ILightAccess ilightaccess, EnumSkyBlock enumskyblock, S s0) { + super(16, 256, 8192); + this.a = ilightaccess; +@@ -45,7 +73,7 @@ public abstract class LightEngineLayer<M extends LightEngineStorageArray<M>, S e + } + + @Nullable +- private IBlockAccess a(int i, int j) { ++ private IChunkAccess a(int i, int j) { // Paper + long k = ChunkCoordIntPair.pair(i, j); + + for (int l = 0; l < 2; ++l) { +@@ -54,7 +82,7 @@ public abstract class LightEngineLayer<M extends LightEngineStorageArray<M>, S e + } + } + +- IBlockAccess iblockaccess = this.a.c(i, j); ++ IChunkAccess iblockaccess = (IChunkAccess) this.a.c(i, j); // Paper + + for (int i1 = 1; i1 > 0; --i1) { + this.g[i1] = this.g[i1 - 1]; +@@ -71,37 +99,39 @@ public abstract class LightEngineLayer<M extends LightEngineStorageArray<M>, S e + Arrays.fill(this.h, (Object) null); + } + +- protected IBlockData a(long i, @Nullable MutableInt mutableint) { +- if (i == Long.MAX_VALUE) { +- if (mutableint != null) { +- mutableint.setValue(0); +- } +- +- return Blocks.AIR.getBlockData(); +- } else { +- int j = SectionPosition.a(BlockPosition.b(i)); +- int k = SectionPosition.a(BlockPosition.d(i)); +- IBlockAccess iblockaccess = this.a(j, k); +- +- if (iblockaccess == null) { +- if (mutableint != null) { +- mutableint.setValue(16); +- } +- +- return Blocks.BEDROCK.getBlockData(); +- } else { +- this.d.g(i); +- IBlockData iblockdata = iblockaccess.getType(this.d); +- boolean flag = iblockdata.l() && iblockdata.e(); +- +- if (mutableint != null) { +- mutableint.setValue(iblockdata.b(this.a.getWorld(), (BlockPosition) this.d)); +- } +- +- return flag ? iblockdata : Blocks.AIR.getBlockData(); +- } +- } +- } ++ // Paper start - comment out, see getBlockOptimized ++// protected IBlockData a(long i, @Nullable MutableInt mutableint) { ++// if (i == Long.MAX_VALUE) { ++// if (mutableint != null) { ++// mutableint.setValue(0); ++// } ++// ++// return Blocks.AIR.getBlockData(); ++// } else { ++// int j = SectionPosition.a(BlockPosition.b(i)); ++// int k = SectionPosition.a(BlockPosition.d(i)); ++// IBlockAccess iblockaccess = this.a(j, k); ++// ++// if (iblockaccess == null) { ++// if (mutableint != null) { ++// mutableint.setValue(16); ++// } ++// ++// return Blocks.BEDROCK.getBlockData(); ++// } else { ++// this.d.g(i); ++// IBlockData iblockdata = iblockaccess.getType(this.d); ++// boolean flag = iblockdata.l() && iblockdata.e(); ++// ++// if (mutableint != null) { ++// mutableint.setValue(iblockdata.b(this.a.getWorld(), (BlockPosition) this.d)); ++// } ++// ++// return flag ? iblockdata : Blocks.AIR.getBlockData(); ++// } ++// } ++// } ++ // Paper end + + protected VoxelShape a(IBlockData iblockdata, long i, EnumDirection enumdirection) { + return iblockdata.l() ? iblockdata.a(this.a.getWorld(), this.d.g(i), enumdirection) : VoxelShapes.a(); +@@ -136,8 +166,9 @@ public abstract class LightEngineLayer<M extends LightEngineStorageArray<M>, S e + return i == Long.MAX_VALUE ? 0 : 15 - this.c.i(i); + } + ++ protected int getNibbleLightInverse(NibbleArray nibblearray, int x, int y, int z) { return 15 - nibblearray.a(x & 15, y & 15, z & 15); } // Paper - x/y/z version of below + protected int a(NibbleArray nibblearray, long i) { +- return 15 - nibblearray.a(SectionPosition.b(BlockPosition.b(i)), SectionPosition.b(BlockPosition.c(i)), SectionPosition.b(BlockPosition.d(i))); ++ return 15 - nibblearray.a((int) (i >> 38) & 15, (int) ((i << 52) >> 52) & 15, (int) ((i << 26) >> 38) & 15); // Paper + } + + @Override +diff --git a/src/main/java/net/minecraft/world/level/lighting/LightEngineSky.java b/src/main/java/net/minecraft/world/level/lighting/LightEngineSky.java +index 37fa5faea6e2972e3eb8a3cbd1913ef38dc9456f..9cd2dfbfa216fdc58297fd25066d31bb92e13ec2 100644 +--- a/src/main/java/net/minecraft/world/level/lighting/LightEngineSky.java ++++ b/src/main/java/net/minecraft/world/level/lighting/LightEngineSky.java +@@ -4,6 +4,7 @@ import net.minecraft.core.BlockPosition; + import net.minecraft.core.EnumDirection; + import net.minecraft.core.SectionPosition; + import net.minecraft.world.level.EnumSkyBlock; ++import net.minecraft.world.level.block.Blocks; + import net.minecraft.world.level.block.state.IBlockData; + import net.minecraft.world.level.chunk.ILightAccess; + import net.minecraft.world.level.chunk.NibbleArray; +@@ -38,21 +39,25 @@ public final class LightEngineSky extends LightEngineLayer<LightEngineStorageSky + return k; + } else { + //MutableInt mutableint = new MutableInt(); // Paper - share mutableint, single threaded +- IBlockData iblockdata = this.a(j, mutableint); +- +- if (mutableint.getValue() >= 15) { ++ // Paper start - use x/y/z and optimized block lookup ++ int jx = (int) (j >> 38); ++ int jy = (int) ((j << 52) >> 52); ++ int jz = (int) ((j << 26) >> 38); ++ IBlockData iblockdata = this.getBlockOptimized(jx, jy, jz, mutableint); ++ int blockedLight = mutableint.getValue(); ++ if (blockedLight >= 15) { ++ // Paper end + return 15; + } else { +- int l = BlockPosition.b(i); +- int i1 = BlockPosition.c(i); +- int j1 = BlockPosition.d(i); +- int k1 = BlockPosition.b(j); +- int l1 = BlockPosition.c(j); +- int i2 = BlockPosition.d(j); +- boolean flag = l == k1 && j1 == i2; +- int j2 = Integer.signum(k1 - l); +- int k2 = Integer.signum(l1 - i1); +- int l2 = Integer.signum(i2 - j1); ++ // Paper start - inline math ++ int ix = (int) (i >> 38); ++ int iy = (int) ((i << 52) >> 52); ++ int iz = (int) ((i << 26) >> 38); ++ boolean flag = ix == jx && iz == jz; ++ int j2 = Integer.signum(jx - ix); ++ int k2 = Integer.signum(jy - iy); ++ int l2 = Integer.signum(jz - iz); ++ // Paper end + EnumDirection enumdirection; + + if (i == Long.MAX_VALUE) { +@@ -61,7 +66,7 @@ public final class LightEngineSky extends LightEngineLayer<LightEngineStorageSky + enumdirection = EnumDirection.a(j2, k2, l2); + } + +- IBlockData iblockdata1 = this.a(i, (MutableInt) null); ++ IBlockData iblockdata1 = i == Long.MAX_VALUE ? Blocks.AIR.getBlockData() : this.getBlockOptimized(ix, iy, iz); // Paper + VoxelShape voxelshape; + + if (enumdirection != null) { +@@ -91,9 +96,9 @@ public final class LightEngineSky extends LightEngineLayer<LightEngineStorageSky + } + } + +- boolean flag1 = i == Long.MAX_VALUE || flag && i1 > l1; ++ boolean flag1 = i == Long.MAX_VALUE || flag && iy > jy; // Paper rename vars to iy > jy + +- return flag1 && k == 0 && mutableint.getValue() == 0 ? 0 : k + Math.max(1, mutableint.getValue()); ++ return flag1 && k == 0 && blockedLight == 0 ? 0 : k + Math.max(1, blockedLight); // Paper + } + } + } +@@ -101,10 +106,14 @@ public final class LightEngineSky extends LightEngineLayer<LightEngineStorageSky + + @Override + protected void a(long i, int j, boolean flag) { +- long k = SectionPosition.e(i); +- int l = BlockPosition.c(i); +- int i1 = SectionPosition.b(l); +- int j1 = SectionPosition.a(l); ++ // Paper start ++ int baseX = (int) (i >> 38); ++ int baseY = (int) ((i << 52) >> 52); ++ int baseZ = (int) ((i << 26) >> 38); ++ long k = SectionPosition.blockPosAsSectionLong(baseX, baseY, baseZ); ++ int i1 = baseY & 15; ++ int j1 = baseY >> 4; ++ // Paper end + int k1; + + if (i1 != 0) { +@@ -119,15 +128,16 @@ public final class LightEngineSky extends LightEngineLayer<LightEngineStorageSky + k1 = l1; + } + +- long i2 = BlockPosition.a(i, 0, -1 - k1 * 16, 0); +- long j2 = SectionPosition.e(i2); ++ int newBaseY = baseY + (-1 - k1 * 16); // Paper ++ long i2 = BlockPosition.asLong(baseX, newBaseY, baseZ); // Paper ++ long j2 = SectionPosition.blockPosAsSectionLong(baseX, newBaseY, baseZ); // Paper + + if (k == j2 || ((LightEngineStorageSky) this.c).g(j2)) { + this.b(i, i2, j, flag); + } + +- long k2 = BlockPosition.a(i, EnumDirection.UP); +- long l2 = SectionPosition.e(k2); ++ long k2 = BlockPosition.asLong(baseX, baseY + 1, baseZ); // Paper ++ long l2 = SectionPosition.blockPosAsSectionLong(baseX, baseY + 1, baseZ); // Paper + + if (k == l2 || ((LightEngineStorageSky) this.c).g(l2)) { + this.b(i, k2, j, flag); +@@ -142,8 +152,8 @@ public final class LightEngineSky extends LightEngineLayer<LightEngineStorageSky + int k3 = 0; + + while (true) { +- long l3 = BlockPosition.a(i, enumdirection.getAdjacentX(), -k3, enumdirection.getAdjacentZ()); +- long i4 = SectionPosition.e(l3); ++ long l3 = BlockPosition.asLong(baseX + enumdirection.getAdjacentX(), baseY - k3, baseZ + enumdirection.getAdjacentZ()); // Paper ++ long i4 = SectionPosition.blockPosAsSectionLong(baseX + enumdirection.getAdjacentX(), baseY - k3, baseZ + enumdirection.getAdjacentZ()); // Paper + + if (k == i4) { + this.b(i, l3, j, flag); +@@ -181,26 +191,36 @@ public final class LightEngineSky extends LightEngineLayer<LightEngineStorageSky + } + } + +- long j1 = SectionPosition.e(i); +- NibbleArray nibblearray = ((LightEngineStorageSky) this.c).a(j1, true); ++ // Paper start ++ int baseX = (int) (i >> 38); ++ int baseY = (int) ((i << 52) >> 52); ++ int baseZ = (int) ((i << 26) >> 38); ++ long j1 = SectionPosition.blockPosAsSectionLong(baseX, baseY, baseZ); ++ NibbleArray nibblearray = this.c.updating.getUpdatingOptimized(j1); ++ // Paper end + EnumDirection[] aenumdirection = LightEngineSky.e; + int k1 = aenumdirection.length; + + for (int l1 = 0; l1 < k1; ++l1) { + EnumDirection enumdirection = aenumdirection[l1]; +- long i2 = BlockPosition.a(i, enumdirection); +- long j2 = SectionPosition.e(i2); ++ // Paper start ++ int newX = baseX + enumdirection.getAdjacentX(); ++ int newY = baseY + enumdirection.getAdjacentY(); ++ int newZ = baseZ + enumdirection.getAdjacentZ(); ++ long i2 = BlockPosition.asLong(newX, newY, newZ); ++ long j2 = SectionPosition.blockPosAsSectionLong(newX, newY, newZ); ++ // Paper end + NibbleArray nibblearray1; + + if (j1 == j2) { + nibblearray1 = nibblearray; + } else { +- nibblearray1 = ((LightEngineStorageSky) this.c).a(j2, true); ++ nibblearray1 = ((LightEngineStorageSky) this.c).updating.getUpdatingOptimized(j2); // Paper + } + + if (nibblearray1 != null) { + if (i2 != j) { +- int k2 = this.b(i2, i, this.a(nibblearray1, i2)); ++ int k2 = this.b(i2, i, this.getNibbleLightInverse(nibblearray1, newX, newY, newZ)); // Paper + + if (l > k2) { + l = k2; +@@ -215,7 +235,7 @@ public final class LightEngineSky extends LightEngineLayer<LightEngineStorageSky + j2 = SectionPosition.a(j2, EnumDirection.UP); + } + +- NibbleArray nibblearray2 = ((LightEngineStorageSky) this.c).a(j2, true); ++ NibbleArray nibblearray2 = this.c.updating.getUpdatingOptimized(j2); // Paper + + if (i2 != j) { + int l2; +diff --git a/src/main/java/net/minecraft/world/level/lighting/LightEngineStorage.java b/src/main/java/net/minecraft/world/level/lighting/LightEngineStorage.java +index 9ba9efb181b9607f25b7c921e69e4c59b182d429..fc0162e7f543d230277457638f208a66537560d7 100644 +--- a/src/main/java/net/minecraft/world/level/lighting/LightEngineStorage.java ++++ b/src/main/java/net/minecraft/world/level/lighting/LightEngineStorage.java +@@ -27,9 +27,9 @@ public abstract class LightEngineStorage<M extends LightEngineStorageArray<M>> e + protected final LongSet c = new LongOpenHashSet(); + protected final LongSet d = new LongOpenHashSet(); + protected volatile M e_visible; protected final Object visibleUpdateLock = new Object(); // Paper - diff on change, should be "visible" - force compile fail on usage change +- protected final M f; // Paper - diff on change, should be "updating" ++ protected final M f; protected final M updating; // Paper - diff on change, should be "updating" + protected final LongSet g = new LongOpenHashSet(); +- protected final LongSet h = new LongOpenHashSet(); ++ protected final LongSet h = new LongOpenHashSet(); LongSet dirty = h; // Paper - OBFHELPER + protected final Long2ObjectMap<NibbleArray> i = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap()); + private final LongSet n = new LongOpenHashSet(); + private final LongSet o = new LongOpenHashSet(); +@@ -37,33 +37,33 @@ public abstract class LightEngineStorage<M extends LightEngineStorageArray<M>> e + protected volatile boolean j; + + protected LightEngineStorage(EnumSkyBlock enumskyblock, ILightAccess ilightaccess, M m0) { +- super(3, 16, 256); ++ super(3, 256, 256); // Paper - bump expected size of level sets to improve collisions and reduce rehashing (seen a lot of it) + this.l = enumskyblock; + this.m = ilightaccess; +- this.f = m0; ++ this.f = m0; updating = m0; // Paper + this.e_visible = m0.b(); // Paper - avoid copying light data + this.e_visible.d(); // Paper - avoid copying light data + } + +- protected boolean g(long i) { +- return this.a(i, true) != null; ++ protected final boolean g(long i) { // Paper - final to help inlining ++ return this.updating.getUpdatingOptimized(i) != null; // Paper - inline to avoid branching + } + + @Nullable + protected NibbleArray a(long i, boolean flag) { + // Paper start - avoid copying light data + if (flag) { +- return this.a(this.f, i); ++ return this.updating.getUpdatingOptimized(i); + } else { + synchronized (this.visibleUpdateLock) { +- return this.a(this.e_visible, i); ++ return this.e_visible.lookup.apply(i); + } + } + // Paper end - avoid copying light data + } + + @Nullable +- protected NibbleArray a(M m0, long i) { ++ protected final NibbleArray a(M m0, long i) { // Paper + return m0.c(i); + } + +@@ -77,27 +77,57 @@ public abstract class LightEngineStorage<M extends LightEngineStorageArray<M>> e + protected abstract int d(long i); + + protected int i(long i) { +- long j = SectionPosition.e(i); +- NibbleArray nibblearray = this.a(j, true); ++ // Paper start - reuse and inline math, use Optimized Updating path ++ final int x = (int) (i >> 38); ++ final int y = (int) ((i << 52) >> 52); ++ final int z = (int) ((i << 26) >> 38); ++ 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(j); ++ } ++ if (nibblearray == null) { ++ System.err.println("Null nibble, preventing crash " + BlockPosition.fromLong(i)); ++ return 0; ++ } + +- return nibblearray.a(SectionPosition.b(BlockPosition.b(i)), SectionPosition.b(BlockPosition.c(i)), SectionPosition.b(BlockPosition.d(i))); ++ return nibblearray.a(x & 15, y & 15, z & 15); // Paper - inline operations ++ // Paper end + } + + protected void b(long i, int j) { +- long k = SectionPosition.e(i); ++ // Paper start - cache part of the math done in loop below ++ int x = (int) (i >> 38); ++ int y = (int) ((i << 52) >> 52); ++ int z = (int) ((i << 26) >> 38); ++ long k = SectionPosition.blockPosAsSectionLong(x, y, z); ++ // Paper end + + if (this.g.add(k)) { + this.f.a(k); + } + + NibbleArray nibblearray = this.a(k, true); +- +- nibblearray.a(SectionPosition.b(BlockPosition.b(i)), SectionPosition.b(BlockPosition.c(i)), SectionPosition.b(BlockPosition.d(i)), j); +- +- for (int l = -1; l <= 1; ++l) { +- for (int i1 = -1; i1 <= 1; ++i1) { +- for (int j1 = -1; j1 <= 1; ++j1) { +- this.h.add(SectionPosition.e(BlockPosition.a(i, i1, j1, l))); ++ nibblearray.a(x & 15, y & 15, z & 15, j); // Paper - use already calculated x/y/z ++ ++ // Paper start - credit to JellySquid for a major optimization here: ++ /* ++ * An extremely important optimization is made here in regards to adding items to the pending notification set. The ++ * original implementation attempts to add the coordinate of every chunk which contains a neighboring block position ++ * even though a huge number of loop iterations will simply map to block positions within the same updating chunk. ++ * ++ * Our implementation here avoids this by pre-calculating the min/max chunk coordinates so we can iterate over only ++ * the relevant chunk positions once. This reduces what would always be 27 iterations to just 1-8 iterations. ++ * ++ * @reason Use faster implementation ++ * @author JellySquid ++ */ ++ for (int z2 = (z - 1) >> 4; z2 <= (z + 1) >> 4; ++z2) { ++ for (int x2 = (x - 1) >> 4; x2 <= (x + 1) >> 4; ++x2) { ++ for (int y2 = (y - 1) >> 4; y2 <= (y + 1) >> 4; ++y2) { ++ this.dirty.add(SectionPosition.asLong(x2, y2, z2)); ++ // Paper end + } + } + } +@@ -129,17 +159,23 @@ public abstract class LightEngineStorage<M extends LightEngineStorageArray<M>> e + } + + if (k >= 2 && j != 2) { +- if (this.p.contains(i)) { +- this.p.remove(i); +- } else { ++ if (!this.p.remove(i)) { // Paper - remove useless contains - credit to JellySquid ++ //this.p.remove(i); // Paper ++ //} else { // Paper + this.f.a(i, this.j(i)); + this.g.add(i); + this.k(i); + +- for (int l = -1; l <= 1; ++l) { +- for (int i1 = -1; i1 <= 1; ++i1) { +- for (int j1 = -1; j1 <= 1; ++j1) { +- this.h.add(SectionPosition.e(BlockPosition.a(i, i1, j1, l))); ++ // Paper start - reuse x/y/z and only notify valid chunks - Credit to JellySquid (See above method for notes) ++ int x = (int) (i >> 38); ++ int y = (int) ((i << 52) >> 52); ++ int z = (int) ((i << 26) >> 38); ++ ++ for (int z2 = (z - 1) >> 4; z2 <= (z + 1) >> 4; ++z2) { ++ for (int x2 = (x - 1) >> 4; x2 <= (x + 1) >> 4; ++x2) { ++ for (int y2 = (y - 1) >> 4; y2 <= (y + 1) >> 4; ++y2) { ++ this.dirty.add(SectionPosition.asLong(x2, y2, z2)); ++ // Paper end + } + } + } +@@ -165,9 +201,9 @@ public abstract class LightEngineStorage<M extends LightEngineStorageArray<M>> e + return SectionPosition.e(j) == i; + }); + } else { +- int j = SectionPosition.c(SectionPosition.b(i)); +- int k = SectionPosition.c(SectionPosition.c(i)); +- int l = SectionPosition.c(SectionPosition.d(i)); ++ int j = (int) (i >> 42) << 4; // Paper - inline ++ int k = (int) (i << 44 >> 44) << 4; // Paper - inline ++ int l = (int) (i << 22 >> 42) << 4; // Paper - inline + + for (int i1 = 0; i1 < 16; ++i1) { + for (int j1 = 0; j1 < 16; ++j1) { +@@ -194,7 +230,7 @@ public abstract class LightEngineStorage<M extends LightEngineStorageArray<M>> e + NibbleArray nibblearray; + + while (longiterator.hasNext()) { +- i = (Long) longiterator.next(); ++ i = longiterator.nextLong(); // Paper + this.a(lightenginelayer, i); + NibbleArray nibblearray1 = (NibbleArray) this.i.remove(i); + +@@ -212,7 +248,7 @@ public abstract class LightEngineStorage<M extends LightEngineStorageArray<M>> e + longiterator = this.p.iterator(); + + while (longiterator.hasNext()) { +- i = (Long) longiterator.next(); ++ i = longiterator.nextLong(); // Paper + this.l(i); + } + +@@ -223,12 +259,13 @@ public abstract class LightEngineStorage<M extends LightEngineStorageArray<M>> e + Entry entry; + long j; + ++ NibbleArray test = null; // Paper + while (objectiterator.hasNext()) { + entry = (Entry) objectiterator.next(); + j = entry.getLongKey(); +- if (this.g(j)) { ++ if ((test = this.updating.getUpdatingOptimized(j)) != null) { // Paper - dont look up nibble twice + nibblearray = (NibbleArray) entry.getValue(); +- if (this.f.c(j) != nibblearray) { ++ if (test != nibblearray) { // Paper + this.a(lightenginelayer, j); + this.f.a(j, nibblearray); + this.g.add(j); +@@ -241,14 +278,14 @@ public abstract class LightEngineStorage<M extends LightEngineStorageArray<M>> e + longiterator = this.i.keySet().iterator(); + + while (longiterator.hasNext()) { +- i = (Long) longiterator.next(); ++ i = longiterator.nextLong(); // Paper + this.b(lightenginelayer, i); + } + } else { + longiterator = this.n.iterator(); + + while (longiterator.hasNext()) { +- i = (Long) longiterator.next(); ++ i = longiterator.nextLong(); // Paper + this.b(lightenginelayer, i); + } + } +@@ -269,15 +306,20 @@ public abstract class LightEngineStorage<M extends LightEngineStorageArray<M>> e + + private void b(LightEngineLayer<M, ?> lightenginelayer, long i) { + if (this.g(i)) { +- int j = SectionPosition.c(SectionPosition.b(i)); +- int k = SectionPosition.c(SectionPosition.c(i)); +- int l = SectionPosition.c(SectionPosition.d(i)); ++ // Paper start ++ int secX = (int) (i >> 42); ++ int secY = (int) (i << 44 >> 44); ++ int secZ = (int) (i << 22 >> 42); ++ int j = secX << 4; // baseX ++ int k = secY << 4; // baseY ++ int l = secZ << 4; // baseZ ++ // Paper end + EnumDirection[] aenumdirection = LightEngineStorage.k; + int i1 = aenumdirection.length; + + for (int j1 = 0; j1 < i1; ++j1) { + EnumDirection enumdirection = aenumdirection[j1]; +- long k1 = SectionPosition.a(i, enumdirection); ++ long k1 = SectionPosition.getAdjacentFromSectionPos(secX, secY, secZ, enumdirection); // Paper - avoid extra unpacking + + if (!this.i.containsKey(k1) && this.g(k1)) { + for (int l1 = 0; l1 < 16; ++l1) { +diff --git a/src/main/java/net/minecraft/world/level/lighting/LightEngineStorageArray.java b/src/main/java/net/minecraft/world/level/lighting/LightEngineStorageArray.java +index da78d4c4b5f8af4648ac82d63c21f6a2a5b73ecb..2ce5cf2e5b6e1dae463439fbfde519fa54677714 100644 +--- a/src/main/java/net/minecraft/world/level/lighting/LightEngineStorageArray.java ++++ b/src/main/java/net/minecraft/world/level/lighting/LightEngineStorageArray.java +@@ -7,13 +7,18 @@ import net.minecraft.world.level.chunk.NibbleArray; + + public abstract class LightEngineStorageArray<M extends LightEngineStorageArray<M>> { + +- 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<NibbleArray> data; // Paper - avoid copying light data + protected final boolean isVisible; // Paper - avoid copying light data +- java.util.function.Function<Long, NibbleArray> 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<NibbleArray> data, boolean isVisible) { + if (isVisible) { +@@ -21,12 +26,14 @@ public abstract class LightEngineStorageArray<M extends LightEngineStorageArray< + } + this.data = data; + this.isVisible = isVisible; ++ // Paper end - avoid copying light data ++ // Paper start - faster lookups with less branching + if (isVisible) { + lookup = data::getVisibleAsync; + } else { +- lookup = data::getUpdating; ++ lookup = data.getUpdatingMap()::get; // jump straight the sub map + } +- // Paper end - avoid copying light data ++ // Paper end + this.c(); + this.d = true; + } +@@ -36,7 +43,9 @@ public abstract class LightEngineStorageArray<M extends LightEngineStorageArray< + public void a(long i) { + if (this.isVisible) { throw new IllegalStateException("writing to visible data"); } // Paper - avoid copying light data + NibbleArray updating = this.data.getUpdating(i); // Paper - pool nibbles +- this.data.queueUpdate(i, new NibbleArray().markPoolSafe(updating.getCloneIfSet())); // Paper - avoid copying light data - pool safe clone ++ NibbleArray nibblearray = new NibbleArray().markPoolSafe(updating.getCloneIfSet()); // Paper ++ nibblearray.lightCacheKey = i; // Paper ++ this.data.queueUpdate(i, nibblearray); // Paper - avoid copying light data - pool safe clone + if (updating.cleaner != null) MCUtil.scheduleTask(2, updating.cleaner, "Light Engine Release"); // Paper - delay clean incase anything holding ref was still using it + this.c(); + } +@@ -45,34 +54,34 @@ public abstract class LightEngineStorageArray<M extends LightEngineStorageArray< + return lookup.apply(i) != null; // Paper - avoid copying light data + } + +- @Nullable +- public final NibbleArray c(long i) { // Paper - final +- if (this.d) { +- for (int j = 0; j < 2; ++j) { +- if (i == this.b[j]) { +- return this.c[j]; +- } +- } +- } +- +- NibbleArray nibblearray = lookup.apply(i); // Paper - avoid copying light data ++ // Paper start - less branching as we know we are using cache and updating ++ public final NibbleArray getUpdatingOptimized(final long i) { // Paper - final ++ final NibbleArray[] cache = this.cache; ++ if (cache[0].lightCacheKey == i) return cache[0]; ++ if (cache[1].lightCacheKey == i) return cache[1]; + ++ final NibbleArray nibblearray = this.lookup.apply(i); // Paper - avoid copying light data + if (nibblearray == null) { + return null; + } else { +- if (this.d) { +- for (int k = 1; k > 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 NibbleArray d(long i) { +@@ -82,13 +91,14 @@ public abstract class LightEngineStorageArray<M extends LightEngineStorageArray< + + public void a(long i, NibbleArray nibblearray) { + if (this.isVisible) { throw new IllegalStateException("writing to visible data"); } // Paper - avoid copying light data ++ nibblearray.lightCacheKey = i; // Paper + this.data.queueUpdate(i, nibblearray); // Paper - avoid copying light data + } + + public void c() { + for (int i = 0; i < 2; ++i) { +- this.b[i] = Long.MAX_VALUE; +- this.c[i] = null; ++ // this.b[i] = Long.MAX_VALUE; // Paper - Unused ++ this.c[i] = NibbleArray.EMPTY_NIBBLE_ARRAY; // Paper + } + } + +diff --git a/src/main/java/net/minecraft/world/level/lighting/LightEngineStorageBlock.java b/src/main/java/net/minecraft/world/level/lighting/LightEngineStorageBlock.java +index bba0dda3b8a4740bb18b9a1fc1821dc4e5890fb7..3a3ab3f0a56127bda368b0d392406a078e85ecea 100644 +--- a/src/main/java/net/minecraft/world/level/lighting/LightEngineStorageBlock.java ++++ b/src/main/java/net/minecraft/world/level/lighting/LightEngineStorageBlock.java +@@ -15,10 +15,14 @@ public class LightEngineStorageBlock extends LightEngineStorage<LightEngineStora + + @Override + protected int d(long i) { +- long j = SectionPosition.e(i); +- 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))); ++ // Paper start ++ int baseX = (int) (i >> 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 + } + + public static final class a extends LightEngineStorageArray<LightEngineStorageBlock.a> { +diff --git a/src/main/java/net/minecraft/world/level/lighting/LightEngineStorageSky.java b/src/main/java/net/minecraft/world/level/lighting/LightEngineStorageSky.java +index 488403a6765598317faedc2d600ae82238e99e39..6d31b19c851081a37e6fcefdcdfcb7018fce6b26 100644 +--- a/src/main/java/net/minecraft/world/level/lighting/LightEngineStorageSky.java ++++ b/src/main/java/net/minecraft/world/level/lighting/LightEngineStorageSky.java +@@ -28,7 +28,12 @@ public class LightEngineStorageSky extends LightEngineStorage<LightEngineStorage + + @Override + protected int d(long i) { +- long j = SectionPosition.e(i); ++ // Paper start ++ int baseX = (int) (i >> 38); ++ int baseY = (int) ((i << 52) >> 52); ++ int baseZ = (int) ((i << 26) >> 38); ++ long j = SectionPosition.blockPosAsSectionLong(baseX, baseY, baseZ); ++ // Paper end + int k = SectionPosition.c(j); + synchronized (this.visibleUpdateLock) { // Paper - avoid copying light data + LightEngineStorageSky.a lightenginestoragesky_a = (LightEngineStorageSky.a) this.e_visible; // Paper - avoid copying light data - must be after lock acquire +@@ -49,7 +54,7 @@ public class LightEngineStorageSky extends LightEngineStorage<LightEngineStorage + } + } + +- return nibblearray.a(SectionPosition.b(BlockPosition.b(i)), SectionPosition.b(BlockPosition.c(i)), SectionPosition.b(BlockPosition.d(i))); ++ return nibblearray.a(baseX & 15, (int) ((i << 52) >> 52) & 15, (int) baseZ & 15); // Paper - y changed above + } else { + return 15; + } +@@ -168,7 +173,7 @@ public class LightEngineStorageSky extends LightEngineStorage<LightEngineStorage + if (k != ((LightEngineStorageSky.a) this.f).b && SectionPosition.c(j) < k) { + NibbleArray nibblearray1; + +- while ((nibblearray1 = this.a(j, true)) == null) { ++ while ((nibblearray1 = this.updating.getUpdatingOptimized(j)) == null) { // Paper + j = SectionPosition.a(j, EnumDirection.UP); + } + +@@ -192,7 +197,10 @@ public class LightEngineStorageSky extends LightEngineStorage<LightEngineStorage + longiterator = this.m.iterator(); + + while (longiterator.hasNext()) { +- i = (Long) longiterator.next(); ++ i = longiterator.nextLong(); // Paper ++ int baseX = (int) (i >> 42) << 4; // Paper ++ int baseY = (int) (i << 44 >> 44) << 4; // Paper ++ int baseZ = (int) (i << 22 >> 42) << 4; // Paper + j = this.c(i); + if (j != 2 && !this.n.contains(i) && this.l.add(i)) { + int l; +@@ -203,10 +211,10 @@ public class LightEngineStorageSky extends LightEngineStorage<LightEngineStorage + ((LightEngineStorageSky.a) this.f).a(i); + } + +- Arrays.fill(this.a(i, true).asBytesPoolSafe(), (byte) -1); // Paper +- k = SectionPosition.c(SectionPosition.b(i)); +- l = SectionPosition.c(SectionPosition.c(i)); +- int i1 = SectionPosition.c(SectionPosition.d(i)); ++ Arrays.fill(this.updating.getUpdatingOptimized(i).asBytesPoolSafe(), (byte) -1); // Paper - use optimized ++ k = baseX; // Paper ++ l = baseY; // Paper ++ int i1 = baseZ; // Paper + EnumDirection[] aenumdirection = LightEngineStorageSky.k; + int j1 = aenumdirection.length; + +@@ -215,7 +223,7 @@ public class LightEngineStorageSky extends LightEngineStorage<LightEngineStorage + for (int l1 = 0; l1 < j1; ++l1) { + EnumDirection enumdirection = aenumdirection[l1]; + +- k1 = SectionPosition.a(i, enumdirection); ++ 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) { +@@ -248,16 +256,16 @@ public class LightEngineStorageSky extends LightEngineStorage<LightEngineStorage + + for (int i3 = 0; i3 < 16; ++i3) { + for (j1 = 0; j1 < 16; ++j1) { +- long j3 = BlockPosition.a(SectionPosition.c(SectionPosition.b(i)) + i3, SectionPosition.c(SectionPosition.c(i)), SectionPosition.c(SectionPosition.d(i)) + j1); ++ long j3 = BlockPosition.a(baseX + i3, baseY, baseZ + j1); // Paper + +- k1 = BlockPosition.a(SectionPosition.c(SectionPosition.b(i)) + i3, SectionPosition.c(SectionPosition.c(i)) - 1, SectionPosition.c(SectionPosition.d(i)) + j1); ++ k1 = BlockPosition.a(baseX + i3, baseY - 1, baseZ + j1); // Paper + lightenginelayer.a(j3, k1, lightenginelayer.b(j3, k1, 0), true); + } + } + } else { + for (k = 0; k < 16; ++k) { + for (l = 0; l < 16; ++l) { +- long k3 = BlockPosition.a(SectionPosition.c(SectionPosition.b(i)) + k, SectionPosition.c(SectionPosition.c(i)) + 16 - 1, SectionPosition.c(SectionPosition.d(i)) + l); ++ long k3 = BlockPosition.a(baseX + k, baseY + 16 - 1, baseZ + l); // Paper + + lightenginelayer.a(Long.MAX_VALUE, k3, 0, true); + } +@@ -272,11 +280,14 @@ public class LightEngineStorageSky extends LightEngineStorage<LightEngineStorage + longiterator = this.n.iterator(); + + while (longiterator.hasNext()) { +- i = (Long) longiterator.next(); ++ i = longiterator.nextLong(); // Paper ++ int baseX = (int) (i >> 42) << 4; // Paper ++ int baseY = (int) (i << 44 >> 44) << 4; // Paper ++ int baseZ = (int) (i << 22 >> 42) << 4; // Paper + if (this.l.remove(i) && this.g(i)) { + for (j = 0; j < 16; ++j) { + for (k = 0; k < 16; ++k) { +- long l3 = BlockPosition.a(SectionPosition.c(SectionPosition.b(i)) + j, SectionPosition.c(SectionPosition.c(i)) + 16 - 1, SectionPosition.c(SectionPosition.d(i)) + k); ++ long l3 = BlockPosition.a(baseX + j, baseY + 16 - 1, baseZ + k); // Paper + + lightenginelayer.a(Long.MAX_VALUE, l3, 15, false); + } |