1320 lines
70 KiB
Diff
1320 lines
70 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Aikar <aikar@aikar.co>
|
|
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/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
|
index 4114c0fa58e41383b5469cbc6abb44416a7af247..e649e6a8b354be45eed808ee02082ca7c826b59b 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
|
@@ -771,6 +771,7 @@ public class ChunkHolder {
|
|
ioPriority = com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGH_PRIORITY;
|
|
}
|
|
chunkMap.level.asyncChunkTaskManager.raisePriority(pos.x, pos.z, ioPriority);
|
|
+ chunkMap.level.getChunkSource().getLightEngine().queue.changePriority(pos.toLong(), this.queueLevel, priority); // Paper
|
|
}
|
|
if (this.queueLevel != priority) {
|
|
this.onLevelChange.onLevelChange(this.pos, () -> this.queueLevel, priority, p -> this.queueLevel = p); // use preferred priority
|
|
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
index 6ed95a0cff3e9c874f14bc90283f750e15765c67..5d57461d33f442e2ff0363348555357ba90f2f58 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
@@ -97,6 +97,7 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureMana
|
|
import net.minecraft.world.level.storage.DimensionDataStorage;
|
|
import net.minecraft.world.level.storage.LevelStorageSource;
|
|
import net.minecraft.world.phys.Vec3;
|
|
+import net.minecraft.world.level.storage.PrimaryLevelData;
|
|
import it.unimi.dsi.fastutil.objects.ObjectRBTreeSet; // Paper
|
|
import org.apache.commons.lang3.mutable.MutableBoolean;
|
|
import org.apache.logging.log4j.LogManager;
|
|
@@ -338,6 +339,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
}
|
|
// Paper end
|
|
|
|
+ private final java.util.concurrent.ExecutorService lightThread; // Paper
|
|
public ChunkMap(ServerLevel world, LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, StructureManager structureManager, Executor executor, BlockableEventLoop<Runnable> mainThreadExecutor, LightChunkGetter chunkProvider, ChunkGenerator chunkGenerator, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier<DimensionDataStorage> persistentStateManagerFactory, int viewDistance, boolean dsync) {
|
|
super(new File(session.getDimensionPath(world.dimension()), "region"), dataFixer, dsync);
|
|
//this.visibleChunks = this.updatingChunks.clone(); // Paper - no more cloning
|
|
@@ -370,7 +372,15 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
|
|
this.progressListener = worldGenerationProgressListener;
|
|
this.chunkStatusListener = chunkStatusChangeListener;
|
|
- ProcessorMailbox<Runnable> lightthreaded; ProcessorMailbox<Runnable> threadedmailbox1 = lightthreaded = ProcessorMailbox.create(executor, "light"); // Paper
|
|
+ // Paper start - use light thread
|
|
+ ProcessorMailbox<Runnable> lightthreaded; ProcessorMailbox<Runnable> threadedmailbox1 = lightthreaded = ProcessorMailbox.create(lightThread = java.util.concurrent.Executors.newSingleThreadExecutor(r -> {
|
|
+ Thread thread = new Thread(r);
|
|
+ thread.setName(((PrimaryLevelData)level.getLevelData()).getLevelName() + " - Light");
|
|
+ thread.setDaemon(true);
|
|
+ thread.setPriority(Thread.NORM_PRIORITY+1);
|
|
+ return thread;
|
|
+ }), "light");
|
|
+ // Paper end
|
|
|
|
this.queueSorter = new ChunkTaskPriorityQueueSorter(ImmutableList.of(threadedmailbox, mailbox, threadedmailbox1), executor, Integer.MAX_VALUE);
|
|
this.worldgenMailbox = this.queueSorter.getProcessor(threadedmailbox, false);
|
|
@@ -879,6 +889,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
@Override
|
|
public void close() throws IOException {
|
|
try {
|
|
+ this.lightThread.shutdown(); // Paper
|
|
this.queueSorter.close();
|
|
this.level.asyncChunkTaskManager.close(true); // Paper - Required since we're closing regionfiles in the next line
|
|
this.poiManager.close();
|
|
diff --git a/src/main/java/net/minecraft/server/level/SectionTracker.java b/src/main/java/net/minecraft/server/level/SectionTracker.java
|
|
index d3d6651eb51c852ed1d1eeb5689569d5308b246d..c2d36600a0081c78425868154bdcf7f42d549154 100644
|
|
--- a/src/main/java/net/minecraft/server/level/SectionTracker.java
|
|
+++ b/src/main/java/net/minecraft/server/level/SectionTracker.java
|
|
@@ -15,13 +15,19 @@ public abstract class SectionTracker extends DynamicGraphMinFixedPoint {
|
|
|
|
@Override
|
|
protected void checkNeighborsAfterUpdate(long id, int level, boolean decrease) {
|
|
+ // Paper start
|
|
+ int x = (int) (id >> 42);
|
|
+ int y = (int) (id << 44 >> 44);
|
|
+ int z = (int) (id << 22 >> 42);
|
|
+ // Paper end
|
|
for(int i = -1; i <= 1; ++i) {
|
|
for(int j = -1; j <= 1; ++j) {
|
|
for(int k = -1; k <= 1; ++k) {
|
|
- long l = SectionPos.offset(id, i, j, k);
|
|
- if (l != id) {
|
|
+ if (i == 0 && j == 0 && k == 0) continue; // Paper
|
|
+ long l = (((long) (x + i) & 4194303L) << 42) | (((long) (y + j) & 1048575L)) | (((long) (z + k) & 4194303L) << 20);
|
|
+ // if (l != id) { // Paper - checked above
|
|
this.checkNeighbor(id, l, level, decrease);
|
|
- }
|
|
+ //} // Paper
|
|
}
|
|
}
|
|
}
|
|
@@ -32,10 +38,15 @@ public abstract class SectionTracker extends DynamicGraphMinFixedPoint {
|
|
protected int getComputedLevel(long id, long excludedId, int maxLevel) {
|
|
int i = maxLevel;
|
|
|
|
+ // Paper start
|
|
+ int x = (int) (id >> 42);
|
|
+ int y = (int) (id << 44 >> 44);
|
|
+ int z = (int) (id << 22 >> 42);
|
|
+ // Paper end
|
|
for(int j = -1; j <= 1; ++j) {
|
|
for(int k = -1; k <= 1; ++k) {
|
|
for(int l = -1; l <= 1; ++l) {
|
|
- long m = SectionPos.offset(id, j, k, l);
|
|
+ long m = (((long) (x + j) & 4194303L) << 42) | (((long) (y + k) & 1048575L)) | (((long) (z + l) & 4194303L) << 20); // Paper
|
|
if (m == id) {
|
|
m = Long.MAX_VALUE;
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
index 0b22fd8ac75146bc7b647cfbefc73ce890ccb033..f27160640c7c63075d41e52a74227c1168cce6b2 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
@@ -1061,7 +1061,7 @@ public class ServerChunkCache extends ChunkSource {
|
|
if (ServerChunkCache.this.runDistanceManagerUpdates()) {
|
|
return true;
|
|
} else {
|
|
- ServerChunkCache.this.lightEngine.tryScheduleUpdate();
|
|
+ ServerChunkCache.this.lightEngine.tryScheduleUpdate(); // Paper - not needed
|
|
return super.pollTask() || execChunkTask; // Paper
|
|
}
|
|
} finally {
|
|
diff --git a/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java b/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java
|
|
index de228b677810ce49c4e953ca0b4e590413b20e45..f05e9dbb5f9b9fa23cf51f72f744c445c54234cd 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.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.ProcessorMailbox;
|
|
import net.minecraft.world.level.ChunkPos;
|
|
import net.minecraft.world.level.LightLayer;
|
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
|
+import net.minecraft.world.level.chunk.ChunkStatus;
|
|
import net.minecraft.world.level.chunk.DataLayer;
|
|
import net.minecraft.world.level.chunk.LevelChunkSection;
|
|
import net.minecraft.world.level.chunk.LightChunkGetter;
|
|
@@ -26,15 +28,149 @@ import org.apache.logging.log4j.Logger;
|
|
public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCloseable {
|
|
private static final Logger LOGGER = LogManager.getLogger();
|
|
private final ProcessorMailbox<Runnable> taskMailbox;
|
|
- private final ObjectList<Pair<ThreadedLevelLightEngine.TaskType, Runnable>> lightTasks = new ObjectArrayList<>();
|
|
- private final ChunkMap chunkMap;
|
|
+ // Paper start
|
|
+ private static final int MAX_PRIORITIES = ChunkMap.MAX_CHUNK_DISTANCE + 2;
|
|
+
|
|
+ private boolean isChunkLightStatus(long pair) {
|
|
+ ChunkHolder playerChunk = playerChunkMap.getVisibleChunkIfPresent(pair);
|
|
+ if (playerChunk == null) {
|
|
+ return false;
|
|
+ }
|
|
+ ChunkStatus status = ChunkHolder.getStatus(playerChunk.getTicketLevel());
|
|
+ return status != null && status.isOrAfter(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));
|
|
+ tryScheduleUpdate();
|
|
+ }
|
|
+
|
|
+ public final void add(long chunkId, IntSupplier priority, ThreadedLevelLightEngine.TaskType type, Runnable run) {
|
|
+ pendingTasks.add(new PendingLightTask(chunkId, priority, type == TaskType.PRE_UPDATE ? run : null, type == TaskType.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 ChunkMap chunkMap; private final ChunkMap playerChunkMap; // Paper
|
|
private final ProcessorHandle<ChunkTaskPriorityQueueSorter.Message<Runnable>> sorterMailbox;
|
|
private volatile int taskPerBatch = 5;
|
|
private final AtomicBoolean scheduled = new AtomicBoolean();
|
|
|
|
public ThreadedLevelLightEngine(LightChunkGetter chunkProvider, ChunkMap chunkStorage, boolean hasBlockLight, ProcessorMailbox<Runnable> processor, ProcessorHandle<ChunkTaskPriorityQueueSorter.Message<Runnable>> executor) {
|
|
super(chunkProvider, true, hasBlockLight);
|
|
- this.chunkMap = chunkStorage;
|
|
+ this.chunkMap = chunkStorage; this.playerChunkMap = chunkMap; // Paper
|
|
this.sorterMailbox = executor;
|
|
this.taskMailbox = processor;
|
|
}
|
|
@@ -120,13 +256,9 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl
|
|
}
|
|
|
|
private void addTask(int x, int z, IntSupplier completedLevelSupplier, ThreadedLevelLightEngine.TaskType stage, Runnable task) {
|
|
- this.sorterMailbox.tell(ChunkTaskPriorityQueueSorter.message(() -> {
|
|
- this.lightTasks.add(Pair.of(stage, task));
|
|
- if (this.lightTasks.size() >= this.taskPerBatch) {
|
|
- this.runUpdate();
|
|
- }
|
|
-
|
|
- }, ChunkPos.asLong(x, z), completedLevelSupplier));
|
|
+ // Paper start - replace method
|
|
+ this.queue.add(ChunkPos.asLong(x, z), completedLevelSupplier, stage, task);
|
|
+ // Paper end
|
|
}
|
|
|
|
@Override
|
|
@@ -142,8 +274,19 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl
|
|
|
|
public CompletableFuture<ChunkAccess> lightChunk(ChunkAccess chunk, boolean excludeBlocks) {
|
|
ChunkPos chunkPos = chunk.getPos();
|
|
- chunk.setLightCorrect(false);
|
|
- this.addTask(chunkPos.x, chunkPos.z, ThreadedLevelLightEngine.TaskType.PRE_UPDATE, Util.name(() -> {
|
|
+ // Paper start
|
|
+ //ichunkaccess.b(false); // Don't need to disable this
|
|
+ long pair = chunkPos.toLong();
|
|
+ CompletableFuture<ChunkAccess> future = new CompletableFuture<>();
|
|
+ IntSupplier prioritySupplier = playerChunkMap.getChunkQueueLevel(pair);
|
|
+ boolean[] skippedPre = {false};
|
|
+ this.queue.addChunk(pair, prioritySupplier, Util.name(() -> {
|
|
+ if (!isChunkLightStatus(pair)) {
|
|
+ future.complete(chunk);
|
|
+ skippedPre[0] = true;
|
|
+ return;
|
|
+ }
|
|
+ // Paper end
|
|
LevelChunkSection[] levelChunkSections = chunk.getSections();
|
|
|
|
for(int i = 0; i < chunk.getSectionsCount(); ++i) {
|
|
@@ -161,53 +304,48 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl
|
|
});
|
|
}
|
|
|
|
- this.chunkMap.releaseLightTicket(chunkPos);
|
|
+ // this.chunkMap.releaseLightTicket(chunkPos); // Paper - move into post task below
|
|
}, () -> {
|
|
return "lightChunk " + chunkPos + " " + excludeBlocks;
|
|
- }));
|
|
- return CompletableFuture.supplyAsync(() -> {
|
|
+ // Paper start - merge the 2 together
|
|
+ }), () -> {
|
|
+ this.chunkMap.releaseLightTicket(chunkPos); // Paper - release light tickets as post task to ensure they stay loaded until fully done
|
|
+ if (skippedPre[0]) return; // Paper - future's already complete
|
|
chunk.setLightCorrect(true);
|
|
super.retainData(chunkPos, false);
|
|
- return chunk;
|
|
- }, (runnable) -> {
|
|
- this.addTask(chunkPos.x, chunkPos.z, ThreadedLevelLightEngine.TaskType.POST_UPDATE, runnable);
|
|
+ // Paper start
|
|
+ future.complete(chunk);
|
|
});
|
|
+ return future;
|
|
+ // Paper end
|
|
}
|
|
|
|
public void tryScheduleUpdate() {
|
|
- if ((!this.lightTasks.isEmpty() || super.hasLightWork()) && this.scheduled.compareAndSet(false, true)) {
|
|
+ if ((!this.queue.isEmpty() || super.hasLightWork()) && this.scheduled.compareAndSet(false, true)) { // Paper
|
|
this.taskMailbox.tell(() -> {
|
|
this.runUpdate();
|
|
this.scheduled.set(false);
|
|
+ tryScheduleUpdate(); // 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 runUpdate() {
|
|
- int i = Math.min(this.lightTasks.size(), this.taskPerBatch);
|
|
- ObjectListIterator<Pair<ThreadedLevelLightEngine.TaskType, Runnable>> objectListIterator = this.lightTasks.iterator();
|
|
-
|
|
- int j;
|
|
- for(j = 0; objectListIterator.hasNext() && j < i; ++j) {
|
|
- Pair<ThreadedLevelLightEngine.TaskType, Runnable> pair = objectListIterator.next();
|
|
- if (pair.getFirst() == ThreadedLevelLightEngine.TaskType.PRE_UPDATE) {
|
|
- pair.getSecond().run();
|
|
- }
|
|
+ if (queue.poll(pre, post)) {
|
|
+ pre.forEach(Runnable::run);
|
|
+ pre.clear();
|
|
+ super.runUpdates(Integer.MAX_VALUE, true, true);
|
|
+ post.forEach(Runnable::run);
|
|
+ post.clear();
|
|
+ } else {
|
|
+ // might have level updates to go still
|
|
+ super.runUpdates(Integer.MAX_VALUE, true, true);
|
|
}
|
|
-
|
|
- objectListIterator.back(j);
|
|
- super.runUpdates(Integer.MAX_VALUE, true, true);
|
|
-
|
|
- for(int var5 = 0; objectListIterator.hasNext() && var5 < i; ++var5) {
|
|
- Pair<ThreadedLevelLightEngine.TaskType, Runnable> pair2 = objectListIterator.next();
|
|
- if (pair2.getFirst() == ThreadedLevelLightEngine.TaskType.POST_UPDATE) {
|
|
- pair2.getSecond().run();
|
|
- }
|
|
-
|
|
- objectListIterator.remove();
|
|
- }
|
|
-
|
|
+ // Paper end
|
|
}
|
|
|
|
public void setTaskPerBatch(int taskBatchSize) {
|
|
diff --git a/src/main/java/net/minecraft/world/level/chunk/DataLayer.java b/src/main/java/net/minecraft/world/level/chunk/DataLayer.java
|
|
index 25570730f376665ca6477263d3b3f94d725ecd21..0570adf7e3caf97be77c5400fb76c3ec31a0d679 100644
|
|
--- a/src/main/java/net/minecraft/world/level/chunk/DataLayer.java
|
|
+++ b/src/main/java/net/minecraft/world/level/chunk/DataLayer.java
|
|
@@ -13,6 +13,13 @@ public class DataLayer {
|
|
@Nullable
|
|
protected byte[] data;
|
|
// Paper start
|
|
+ public static final DataLayer EMPTY_NIBBLE_ARRAY = new DataLayer() {
|
|
+ @Override
|
|
+ public byte[] getData() {
|
|
+ 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/BlockLightEngine.java b/src/main/java/net/minecraft/world/level/lighting/BlockLightEngine.java
|
|
index 37d7165dfd17da03428f8dbbbf95aa8005be289c..63fb8ccd47c21b4ef332daf418c9f70f2248be45 100644
|
|
--- a/src/main/java/net/minecraft/world/level/lighting/BlockLightEngine.java
|
|
+++ b/src/main/java/net/minecraft/world/level/lighting/BlockLightEngine.java
|
|
@@ -22,9 +22,11 @@ public final class BlockLightEngine extends LayerLightEngine<BlockLightSectionSt
|
|
}
|
|
|
|
private int getLightEmission(long blockPos) {
|
|
- int i = BlockPos.getX(blockPos);
|
|
- int j = BlockPos.getY(blockPos);
|
|
- int k = BlockPos.getZ(blockPos);
|
|
+ // Paper start - inline math
|
|
+ int i = (int) (blockPos >> 38);
|
|
+ int j = (int) ((blockPos << 52) >> 52);
|
|
+ int k = (int) ((blockPos << 26) >> 38);
|
|
+ // Paper end
|
|
BlockGetter blockGetter = this.chunkSource.getChunkForLighting(SectionPos.blockToSectionCoord(i), SectionPos.blockToSectionCoord(k));
|
|
return blockGetter != null ? blockGetter.getLightEmission(this.pos.set(i, j, k)) : 0;
|
|
}
|
|
@@ -38,22 +40,33 @@ public final class BlockLightEngine extends LayerLightEngine<BlockLightSectionSt
|
|
} else if (level >= 15) {
|
|
return level;
|
|
} else {
|
|
- int i = Integer.signum(BlockPos.getX(targetId) - BlockPos.getX(sourceId));
|
|
- int j = Integer.signum(BlockPos.getY(targetId) - BlockPos.getY(sourceId));
|
|
- int k = Integer.signum(BlockPos.getZ(targetId) - BlockPos.getZ(sourceId));
|
|
+ // Paper start
|
|
+ int ix = (int) (targetId >> 38);
|
|
+ int iy = (int) ((targetId << 52) >> 52);
|
|
+ int iz = (int) ((targetId << 26) >> 38);
|
|
+ int jx = (int) (sourceId >> 38);
|
|
+ int jy = (int) ((sourceId << 52) >> 52);
|
|
+ int jz = (int) ((sourceId << 26) >> 38);
|
|
+ int i = Integer.signum(ix - jx);
|
|
+ int j = Integer.signum(iy - jy);
|
|
+ int k = Integer.signum(iz - jz);
|
|
+ // Paper end
|
|
Direction direction = Direction.fromNormal(i, j, k);
|
|
if (direction == null) {
|
|
return 15;
|
|
} else {
|
|
//MutableInt mutableint = new MutableInt(); // Paper - share mutableint, single threaded
|
|
- BlockState blockState = this.getStateAndOpacity(targetId, mutableInt);
|
|
- if (mutableInt.getValue() >= 15) {
|
|
+ // Paper start
|
|
+ BlockState blockState = this.getBlockOptimized(ix, iy, iz, mutableInt);
|
|
+ int blockdLight = mutableInt.getValue();
|
|
+ if (blockdLight >= 15) {
|
|
+ // Paper end
|
|
return 15;
|
|
} else {
|
|
- BlockState blockState2 = this.getStateAndOpacity(sourceId, (MutableInt)null);
|
|
+ BlockState blockState2 = this.getBlockOptimized(jx, jy, jz); // Paper
|
|
VoxelShape voxelShape = this.getShape(blockState2, sourceId, direction);
|
|
VoxelShape voxelShape2 = this.getShape(blockState, targetId, direction.getOpposite());
|
|
- return Shapes.faceShapeOccludes(voxelShape, voxelShape2) ? 15 : level + Math.max(1, mutableInt.getValue());
|
|
+ return Shapes.faceShapeOccludes(voxelShape, voxelShape2) ? 15 : level + Math.max(1, blockdLight); // Paper
|
|
}
|
|
}
|
|
}
|
|
@@ -61,10 +74,15 @@ public final class BlockLightEngine extends LayerLightEngine<BlockLightSectionSt
|
|
|
|
@Override
|
|
protected void checkNeighborsAfterUpdate(long id, int level, boolean decrease) {
|
|
- long l = SectionPos.blockToSection(id);
|
|
+ // Paper start - reuse unpacking, credit to JellySquid (Didn't do full optimization though)
|
|
+ int x = (int) (id >> 38);
|
|
+ int y = (int) ((id << 52) >> 52);
|
|
+ int z = (int) ((id << 26) >> 38);
|
|
+ long l = SectionPos.blockPosAsSectionLong(x, y, z);
|
|
+ // Paper end
|
|
|
|
for(Direction direction : DIRECTIONS) {
|
|
- long m = BlockPos.offset(id, direction);
|
|
+ long m = BlockPos.getAdjacent(x, y, z, direction); // Paper
|
|
long n = SectionPos.blockToSection(m);
|
|
if (l == n || this.storage.storingLightForSection(n)) {
|
|
this.checkNeighbor(id, m, level, decrease);
|
|
@@ -87,22 +105,32 @@ public final class BlockLightEngine extends LayerLightEngine<BlockLightSectionSt
|
|
}
|
|
}
|
|
|
|
- long l = SectionPos.blockToSection(id);
|
|
- DataLayer dataLayer = this.storage.getDataLayer(l, true);
|
|
+ // Paper start
|
|
+ int baseX = (int) (id >> 38);
|
|
+ int baseY = (int) ((id << 52) >> 52);
|
|
+ int baseZ = (int) ((id << 26) >> 38);
|
|
+ long l = SectionPos.blockPosAsSectionLong(baseX, baseY, baseZ);
|
|
+ DataLayer dataLayer = this.storage.updating.getUpdatingOptimized(l);
|
|
+ // Paper end
|
|
|
|
for(Direction direction : DIRECTIONS) {
|
|
- long m = BlockPos.offset(id, direction);
|
|
+ // Paper start
|
|
+ int newX = baseX + direction.getStepX();
|
|
+ int newY = baseY + direction.getStepY();
|
|
+ int newZ = baseZ + direction.getStepZ();
|
|
+ long m = BlockPos.asLong(newX, newY, newZ);
|
|
if (m != excludedId) {
|
|
- long n = SectionPos.blockToSection(m);
|
|
+ long n = SectionPos.blockPosAsSectionLong(newX, newY, newZ);
|
|
+ // Paper end
|
|
DataLayer dataLayer2;
|
|
if (l == n) {
|
|
dataLayer2 = dataLayer;
|
|
} else {
|
|
- dataLayer2 = this.storage.getDataLayer(n, true);
|
|
+ dataLayer2 = this.storage.updating.getUpdatingOptimized(n); // Paper
|
|
}
|
|
|
|
if (dataLayer2 != null) {
|
|
- int k = this.computeLevelFromNeighbor(m, id, this.getLevel(dataLayer2, m));
|
|
+ int k = this.computeLevelFromNeighbor(m, id, this.getLevel(dataLayer2, newX, newY, newZ)); // Paper
|
|
if (i > k) {
|
|
i = k;
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/lighting/BlockLightSectionStorage.java b/src/main/java/net/minecraft/world/level/lighting/BlockLightSectionStorage.java
|
|
index 314b46f0becd088d26956b45981217b128d539cb..d8e71e3759e02b3db5603302ad37656d0a72c2b2 100644
|
|
--- a/src/main/java/net/minecraft/world/level/lighting/BlockLightSectionStorage.java
|
|
+++ b/src/main/java/net/minecraft/world/level/lighting/BlockLightSectionStorage.java
|
|
@@ -14,9 +14,14 @@ public class BlockLightSectionStorage extends LayerLightSectionStorage<BlockLigh
|
|
|
|
@Override
|
|
protected int getLightValue(long blockPos) {
|
|
- long l = SectionPos.blockToSection(blockPos);
|
|
- DataLayer dataLayer = this.getDataLayer(l, false);
|
|
- return dataLayer == null ? 0 : dataLayer.get(SectionPos.sectionRelative(BlockPos.getX(blockPos)), SectionPos.sectionRelative(BlockPos.getY(blockPos)), SectionPos.sectionRelative(BlockPos.getZ(blockPos)));
|
|
+ // Paper start
|
|
+ int baseX = (int) (blockPos >> 38);
|
|
+ int baseY = (int) ((blockPos << 52) >> 52);
|
|
+ int baseZ = (int) ((blockPos << 26) >> 38);
|
|
+ long l = net.minecraft.core.SectionPos.blockPosAsSectionLong(baseX, baseY, baseZ);
|
|
+ DataLayer dataLayer = this.e_visible.lookup.apply(l);
|
|
+ return dataLayer == null ? 0 : dataLayer.get(baseX & 15, baseY & 15, baseZ & 15);
|
|
+ // Paper end
|
|
}
|
|
|
|
protected static final class BlockDataLayerStorageMap extends DataLayerStorageMap<BlockLightSectionStorage.BlockDataLayerStorageMap> {
|
|
diff --git a/src/main/java/net/minecraft/world/level/lighting/DataLayerStorageMap.java b/src/main/java/net/minecraft/world/level/lighting/DataLayerStorageMap.java
|
|
index 52682471adc13dffc0383fc4abacbd3397f3bb10..9d1bf2d68d8d630d5ecb0581df57a99eae9eff0d 100644
|
|
--- a/src/main/java/net/minecraft/world/level/lighting/DataLayerStorageMap.java
|
|
+++ b/src/main/java/net/minecraft/world/level/lighting/DataLayerStorageMap.java
|
|
@@ -6,13 +6,18 @@ import net.minecraft.world.level.chunk.DataLayer;
|
|
|
|
public abstract class DataLayerStorageMap<M extends DataLayerStorageMap<M>> {
|
|
private static final int CACHE_SIZE = 2;
|
|
- private final long[] lastSectionKeys = new long[2];
|
|
- private final DataLayer[] lastSections = new DataLayer[2];
|
|
+ // private final long[] b = new long[2]; // Paper - unused
|
|
+ private final DataLayer[] lastSections = new DataLayer[]{DataLayer.EMPTY_NIBBLE_ARRAY, DataLayer.EMPTY_NIBBLE_ARRAY}; private final DataLayer[] cache = lastSections; // Paper - OBFHELPER
|
|
private boolean cacheEnabled;
|
|
protected final com.destroystokyo.paper.util.map.QueuedChangesMapLong2Object<DataLayer> data; // Paper - avoid copying light data
|
|
protected final boolean isVisible; // Paper - avoid copying light data
|
|
- java.util.function.Function<Long, DataLayer> 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 {
|
|
+ DataLayer apply(long id);
|
|
+ }
|
|
+ // Paper end
|
|
// Paper start - avoid copying light data
|
|
protected DataLayerStorageMap(com.destroystokyo.paper.util.map.QueuedChangesMapLong2Object<DataLayer> data, boolean isVisible) {
|
|
if (isVisible) {
|
|
@@ -20,12 +25,14 @@ public abstract class DataLayerStorageMap<M extends DataLayerStorageMap<M>> {
|
|
}
|
|
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.clearCache();
|
|
this.cacheEnabled = true;
|
|
}
|
|
@@ -35,7 +42,9 @@ public abstract class DataLayerStorageMap<M extends DataLayerStorageMap<M>> {
|
|
public void copyDataLayer(long pos) {
|
|
if (this.isVisible) { throw new IllegalStateException("writing to visible data"); } // Paper - avoid copying light data
|
|
DataLayer updating = this.data.getUpdating(pos); // Paper - pool nibbles
|
|
- this.data.queueUpdate(pos, new DataLayer().markPoolSafe(updating.getCloneIfSet())); // Paper - avoid copying light data - pool safe clone
|
|
+ DataLayer nibblearray = new DataLayer().markPoolSafe(updating.getCloneIfSet()); // Paper
|
|
+ nibblearray.lightCacheKey = pos; // Paper
|
|
+ this.data.queueUpdate(pos, nibblearray); // Paper - avoid copying light data - pool safe clone
|
|
if (updating.cleaner != null) net.minecraft.server.MCUtil.scheduleTask(2, updating.cleaner, "Light Engine Release"); // Paper - delay clean incase anything holding ref was still using it
|
|
this.clearCache();
|
|
}
|
|
@@ -44,33 +53,34 @@ public abstract class DataLayerStorageMap<M extends DataLayerStorageMap<M>> {
|
|
return lookup.apply(chunkPos) != null; // Paper - avoid copying light data
|
|
}
|
|
|
|
- @Nullable
|
|
- public final DataLayer getLayer(long chunkPos) { // Paper - final
|
|
- if (this.cacheEnabled) {
|
|
- for(int i = 0; i < 2; ++i) {
|
|
- if (chunkPos == this.lastSectionKeys[i]) {
|
|
- return this.lastSections[i];
|
|
- }
|
|
- }
|
|
- }
|
|
+ // Paper start - less branching as we know we are using cache and updating
|
|
+ public final DataLayer getUpdatingOptimized(final long chunkPos) { // Paper - final
|
|
+ final DataLayer[] cache = this.cache;
|
|
+ if (cache[0].lightCacheKey == chunkPos) return cache[0];
|
|
+ if (cache[1].lightCacheKey == chunkPos) return cache[1];
|
|
|
|
- DataLayer dataLayer = lookup.apply(chunkPos); // Paper - avoid copying light data
|
|
+ final DataLayer dataLayer = this.lookup.apply(chunkPos); // Paper - avoid copying light data
|
|
if (dataLayer == null) {
|
|
return null;
|
|
} else {
|
|
- if (this.cacheEnabled) {
|
|
- for(int j = 1; j > 0; --j) {
|
|
- this.lastSectionKeys[j] = this.lastSectionKeys[j - 1];
|
|
- this.lastSections[j] = this.lastSections[j - 1];
|
|
- }
|
|
-
|
|
- this.lastSectionKeys[0] = chunkPos;
|
|
- this.lastSections[0] = dataLayer;
|
|
- }
|
|
-
|
|
+ cache[1] = cache[0];
|
|
+ cache[0] = dataLayer;
|
|
return dataLayer;
|
|
}
|
|
}
|
|
+ // Paper end
|
|
+
|
|
+ @Nullable
|
|
+ public final DataLayer getLayer(final long chunkPos) { // Paper - final
|
|
+ // Paper start - optimize visible case or missed updating cases
|
|
+ if (this.cacheEnabled) {
|
|
+ // short circuit to optimized
|
|
+ return getUpdatingOptimized(chunkPos);
|
|
+ }
|
|
+
|
|
+ return this.lookup.apply(chunkPos);
|
|
+ // Paper end
|
|
+ }
|
|
|
|
@Nullable
|
|
public DataLayer removeLayer(long chunkPos) {
|
|
@@ -80,13 +90,14 @@ public abstract class DataLayerStorageMap<M extends DataLayerStorageMap<M>> {
|
|
|
|
public void setLayer(long pos, DataLayer data) {
|
|
if (this.isVisible) { throw new IllegalStateException("writing to visible data"); } // Paper - avoid copying light data
|
|
+ data.lightCacheKey = pos; // Paper
|
|
this.data.queueUpdate(pos, data); // Paper - avoid copying light data
|
|
}
|
|
|
|
public void clearCache() {
|
|
for(int i = 0; i < 2; ++i) {
|
|
- this.lastSectionKeys[i] = Long.MAX_VALUE;
|
|
- this.lastSections[i] = null;
|
|
+ // this.b[i] = Long.MAX_VALUE; // Paper - Unused
|
|
+ this.lastSections[i] = DataLayer.EMPTY_NIBBLE_ARRAY; // Paper
|
|
}
|
|
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/lighting/LayerLightEngine.java b/src/main/java/net/minecraft/world/level/lighting/LayerLightEngine.java
|
|
index c5d0d8fb4c0344fbcc2ac87ba6d8027c29d51286..0952dde531aecd8ffc6eb72d4841362c5a6ebdcd 100644
|
|
--- a/src/main/java/net/minecraft/world/level/lighting/LayerLightEngine.java
|
|
+++ b/src/main/java/net/minecraft/world/level/lighting/LayerLightEngine.java
|
|
@@ -10,6 +10,7 @@ import net.minecraft.world.level.ChunkPos;
|
|
import net.minecraft.world.level.LightLayer;
|
|
import net.minecraft.world.level.block.Blocks;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
+import net.minecraft.world.level.chunk.ChunkAccess;
|
|
import net.minecraft.world.level.chunk.DataLayer;
|
|
import net.minecraft.world.level.chunk.LightChunkGetter;
|
|
import net.minecraft.world.phys.shapes.Shapes;
|
|
@@ -26,8 +27,35 @@ public abstract class LayerLightEngine<M extends DataLayerStorageMap<M>, S exten
|
|
protected final BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos();
|
|
private static final int CACHE_SIZE = 2;
|
|
private final long[] lastChunkPos = new long[2];
|
|
- private final BlockGetter[] lastChunk = new BlockGetter[2];
|
|
+ private final ChunkAccess[] lastChunk = new ChunkAccess[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 BlockState getBlockOptimized(int x, int y, int z, MutableInt mutableint) {
|
|
+ ChunkAccess iblockaccess = this.getChunk(x >> 4, z >> 4);
|
|
+
|
|
+ if (iblockaccess == null) {
|
|
+ mutableint.setValue(16);
|
|
+ return Blocks.BEDROCK.defaultBlockState();
|
|
+ } else {
|
|
+ this.pos.set(x, y, z);
|
|
+ BlockState iblockdata = iblockaccess.getType(x, y, z);
|
|
+ mutableint.setValue(iblockdata.getLightBlock(this.chunkSource.getLevel(), this.pos));
|
|
+ return iblockdata.canOcclude() && iblockdata.useShapeForLightOcclusion() ? iblockdata : Blocks.AIR.defaultBlockState();
|
|
+ }
|
|
+ }
|
|
+ protected final BlockState getBlockOptimized(int x, int y, int z) {
|
|
+ ChunkAccess iblockaccess = this.getChunk(x >> 4, z >> 4);
|
|
+
|
|
+ if (iblockaccess == null) {
|
|
+ return Blocks.BEDROCK.defaultBlockState();
|
|
+ } else {
|
|
+ BlockState iblockdata = iblockaccess.getType(x, y, z);
|
|
+ return iblockdata.canOcclude() && iblockdata.useShapeForLightOcclusion() ? iblockdata : Blocks.AIR.defaultBlockState();
|
|
+ }
|
|
+ }
|
|
+ // Paper end
|
|
public LayerLightEngine(LightChunkGetter chunkProvider, LightLayer type, S lightStorage) {
|
|
super(16, 256, 8192);
|
|
this.chunkSource = chunkProvider;
|
|
@@ -46,7 +74,7 @@ public abstract class LayerLightEngine<M extends DataLayerStorageMap<M>, S exten
|
|
}
|
|
|
|
@Nullable
|
|
- private BlockGetter getChunk(int chunkX, int chunkZ) {
|
|
+ private ChunkAccess getChunk(int chunkX, int chunkZ) { // Paper
|
|
long l = ChunkPos.asLong(chunkX, chunkZ);
|
|
|
|
for(int i = 0; i < 2; ++i) {
|
|
@@ -55,7 +83,7 @@ public abstract class LayerLightEngine<M extends DataLayerStorageMap<M>, S exten
|
|
}
|
|
}
|
|
|
|
- BlockGetter blockGetter = this.chunkSource.getChunkForLighting(chunkX, chunkZ);
|
|
+ ChunkAccess blockGetter = (ChunkAccess) this.chunkSource.getChunkForLighting(chunkX, chunkZ); // Paper
|
|
|
|
for(int j = 1; j > 0; --j) {
|
|
this.lastChunkPos[j] = this.lastChunkPos[j - 1];
|
|
@@ -72,35 +100,37 @@ public abstract class LayerLightEngine<M extends DataLayerStorageMap<M>, S exten
|
|
Arrays.fill(this.lastChunk, (Object)null);
|
|
}
|
|
|
|
- protected BlockState getStateAndOpacity(long pos, @Nullable MutableInt mutableInt) {
|
|
- if (pos == Long.MAX_VALUE) {
|
|
- if (mutableInt != null) {
|
|
- mutableInt.setValue(0);
|
|
- }
|
|
-
|
|
- return Blocks.AIR.defaultBlockState();
|
|
- } else {
|
|
- int i = SectionPos.blockToSectionCoord(BlockPos.getX(pos));
|
|
- int j = SectionPos.blockToSectionCoord(BlockPos.getZ(pos));
|
|
- BlockGetter blockGetter = this.getChunk(i, j);
|
|
- if (blockGetter == null) {
|
|
- if (mutableInt != null) {
|
|
- mutableInt.setValue(16);
|
|
- }
|
|
-
|
|
- return Blocks.BEDROCK.defaultBlockState();
|
|
- } else {
|
|
- this.pos.set(pos);
|
|
- BlockState blockState = blockGetter.getBlockState(this.pos);
|
|
- boolean bl = blockState.canOcclude() && blockState.useShapeForLightOcclusion();
|
|
- if (mutableInt != null) {
|
|
- mutableInt.setValue(blockState.getLightBlock(this.chunkSource.getLevel(), this.pos));
|
|
- }
|
|
-
|
|
- return bl ? blockState : Blocks.AIR.defaultBlockState();
|
|
- }
|
|
- }
|
|
- }
|
|
+ // Paper start - comment out, see getBlockOptimized
|
|
+ // protected BlockState getStateAndOpacity(long pos, @Nullable MutableInt mutableInt) {
|
|
+ // if (pos == Long.MAX_VALUE) {
|
|
+ // if (mutableInt != null) {
|
|
+ // mutableInt.setValue(0);
|
|
+ // }
|
|
+ //
|
|
+ // return Blocks.AIR.defaultBlockState();
|
|
+ // } else {
|
|
+ // int i = SectionPos.blockToSectionCoord(BlockPos.getX(pos));
|
|
+ // int j = SectionPos.blockToSectionCoord(BlockPos.getZ(pos));
|
|
+ // BlockGetter blockGetter = this.getChunk(i, j);
|
|
+ // if (blockGetter == null) {
|
|
+ // if (mutableInt != null) {
|
|
+ // mutableInt.setValue(16);
|
|
+ // }
|
|
+ //
|
|
+ // return Blocks.BEDROCK.defaultBlockState();
|
|
+ // } else {
|
|
+ // this.pos.set(pos);
|
|
+ // BlockState blockState = blockGetter.getBlockState(this.pos);
|
|
+ // boolean bl = blockState.canOcclude() && blockState.useShapeForLightOcclusion();
|
|
+ // if (mutableInt != null) {
|
|
+ // mutableInt.setValue(blockState.getLightBlock(this.chunkSource.getLevel(), this.pos));
|
|
+ // }
|
|
+ //
|
|
+ // return bl ? blockState : Blocks.AIR.defaultBlockState();
|
|
+ // }
|
|
+ // }
|
|
+ // }
|
|
+ // Paper end
|
|
|
|
protected VoxelShape getShape(BlockState world, long pos, Direction facing) {
|
|
return world.canOcclude() ? world.getFaceOcclusionShape(this.chunkSource.getLevel(), this.pos.set(pos), facing) : Shapes.empty();
|
|
@@ -133,8 +163,9 @@ public abstract class LayerLightEngine<M extends DataLayerStorageMap<M>, S exten
|
|
return id == Long.MAX_VALUE ? 0 : 15 - this.storage.getStoredLevel(id);
|
|
}
|
|
|
|
+ protected int getLevel(DataLayer section, int x, int y, int z) { return 15 - section.get(x & 15, y & 15, z & 15); } // Paper - x/y/z version of below
|
|
protected int getLevel(DataLayer section, long blockPos) {
|
|
- return 15 - section.get(SectionPos.sectionRelative(BlockPos.getX(blockPos)), SectionPos.sectionRelative(BlockPos.getY(blockPos)), SectionPos.sectionRelative(BlockPos.getZ(blockPos)));
|
|
+ return 15 - section.get((int) (blockPos >> 38) & 15, (int) ((blockPos << 52) >> 52) & 15, (int) ((blockPos << 26) >> 38) & 15); // Paper
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/level/lighting/LayerLightSectionStorage.java b/src/main/java/net/minecraft/world/level/lighting/LayerLightSectionStorage.java
|
|
index fd1cdb6e2023713f947b9497c605cf6f4bae8994..4db3264c45736faf92d560c7d18d892c1200b15d 100644
|
|
--- a/src/main/java/net/minecraft/world/level/lighting/LayerLightSectionStorage.java
|
|
+++ b/src/main/java/net/minecraft/world/level/lighting/LayerLightSectionStorage.java
|
|
@@ -29,9 +29,9 @@ public abstract class LayerLightSectionStorage<M extends DataLayerStorageMap<M>>
|
|
protected final LongSet toMarkNoData = new LongOpenHashSet();
|
|
protected final LongSet toMarkData = 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 updatingSectionData;
|
|
+ protected final M updatingSectionData; protected final M updating; // Paper - diff on change, should be "updating"
|
|
protected final LongSet changedSections = new LongOpenHashSet();
|
|
- protected final LongSet sectionsAffectedByLightUpdates = new LongOpenHashSet();
|
|
+ protected final LongSet sectionsAffectedByLightUpdates = new LongOpenHashSet(); LongSet dirty = sectionsAffectedByLightUpdates; // Paper - OBFHELPER
|
|
protected final Long2ObjectMap<DataLayer> queuedSections = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>());
|
|
private final LongSet untrustedSections = new LongOpenHashSet();
|
|
private final LongSet columnsToRetainQueuedDataFor = new LongOpenHashSet();
|
|
@@ -39,33 +39,33 @@ public abstract class LayerLightSectionStorage<M extends DataLayerStorageMap<M>>
|
|
protected volatile boolean hasToRemove;
|
|
|
|
protected LayerLightSectionStorage(LightLayer lightType, LightChunkGetter chunkProvider, M lightData) {
|
|
- 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.layer = lightType;
|
|
this.chunkSource = chunkProvider;
|
|
- this.updatingSectionData = lightData;
|
|
+ this.updatingSectionData = lightData; updating = lightData; // Paper
|
|
this.e_visible = lightData.copy(); // Paper - avoid copying light dat
|
|
this.e_visible.disableCache(); // Paper - avoid copying light dat
|
|
}
|
|
|
|
- protected boolean storingLightForSection(long sectionPos) {
|
|
- return this.getDataLayer(sectionPos, true) != null;
|
|
+ protected final boolean storingLightForSection(long sectionPos) { // Paper - final to help inlining
|
|
+ return this.updating.getUpdatingOptimized(sectionPos) != null; // Paper - inline to avoid branching
|
|
}
|
|
|
|
@Nullable
|
|
protected DataLayer getDataLayer(long sectionPos, boolean cached) {
|
|
// Paper start - avoid copying light data
|
|
if (cached) {
|
|
- return this.getDataLayer(this.updatingSectionData, sectionPos);
|
|
+ return this.updating.getUpdatingOptimized(sectionPos);
|
|
} else {
|
|
synchronized (this.visibleUpdateLock) {
|
|
- return this.getDataLayer(this.e_visible, sectionPos);
|
|
+ return this.e_visible.lookup.apply(sectionPos);
|
|
}
|
|
}
|
|
// Paper end - avoid copying light data
|
|
}
|
|
|
|
@Nullable
|
|
- protected DataLayer getDataLayer(M storage, long sectionPos) {
|
|
+ protected final DataLayer getDataLayer(M storage, long sectionPos) { // Paper
|
|
return storage.getLayer(sectionPos);
|
|
}
|
|
|
|
@@ -78,24 +78,56 @@ public abstract class LayerLightSectionStorage<M extends DataLayerStorageMap<M>>
|
|
protected abstract int getLightValue(long blockPos);
|
|
|
|
protected int getStoredLevel(long blockPos) {
|
|
- long l = SectionPos.blockToSection(blockPos);
|
|
- DataLayer dataLayer = this.getDataLayer(l, true);
|
|
- return dataLayer.get(SectionPos.sectionRelative(BlockPos.getX(blockPos)), SectionPos.sectionRelative(BlockPos.getY(blockPos)), SectionPos.sectionRelative(BlockPos.getZ(blockPos)));
|
|
+ // Paper start - reuse and inline math, use Optimized Updating path
|
|
+ final int x = (int) (blockPos >> 38);
|
|
+ final int y = (int) ((blockPos << 52) >> 52);
|
|
+ final int z = (int) ((blockPos << 26) >> 38);
|
|
+ long j = SectionPos.blockPosAsSectionLong(x, y, z);
|
|
+ DataLayer 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 " + BlockPos.of(blockPos));
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ return nibblearray.get(x & 15, y & 15, z & 15); // Paper - inline operations
|
|
+ // Paper end
|
|
}
|
|
|
|
protected void setStoredLevel(long blockPos, int value) {
|
|
- long l = SectionPos.blockToSection(blockPos);
|
|
+ // Paper start - cache part of the math done in loop below
|
|
+ int x = (int) (blockPos >> 38);
|
|
+ int y = (int) ((blockPos << 52) >> 52);
|
|
+ int z = (int) ((blockPos << 26) >> 38);
|
|
+ long l = SectionPos.blockPosAsSectionLong(x, y, z);
|
|
+ // Paper end
|
|
if (this.changedSections.add(l)) {
|
|
this.updatingSectionData.copyDataLayer(l);
|
|
}
|
|
|
|
DataLayer dataLayer = this.getDataLayer(l, true);
|
|
- dataLayer.set(SectionPos.sectionRelative(BlockPos.getX(blockPos)), SectionPos.sectionRelative(BlockPos.getY(blockPos)), SectionPos.sectionRelative(BlockPos.getZ(blockPos)), value);
|
|
-
|
|
- for(int i = -1; i <= 1; ++i) {
|
|
- for(int j = -1; j <= 1; ++j) {
|
|
- for(int k = -1; k <= 1; ++k) {
|
|
- this.sectionsAffectedByLightUpdates.add(SectionPos.blockToSection(BlockPos.offset(blockPos, j, k, i)));
|
|
+ dataLayer.set(x & 15, y & 15, z & 15, value); // 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(SectionPos.asLong(x2, y2, z2));
|
|
+ // Paper end
|
|
}
|
|
}
|
|
}
|
|
@@ -136,17 +168,23 @@ public abstract class LayerLightSectionStorage<M extends DataLayerStorageMap<M>>
|
|
}
|
|
|
|
if (i >= 2 && level != 2) {
|
|
- if (this.toRemove.contains(id)) {
|
|
- this.toRemove.remove(id);
|
|
- } else {
|
|
+ if (!this.toRemove.remove(id)) { // Paper - remove useless contains - credit to JellySquid
|
|
+ //this.toRemove.remove(id); // Paper
|
|
+ //} else { // Paper
|
|
this.updatingSectionData.setLayer(id, this.createDataLayer(id));
|
|
this.changedSections.add(id);
|
|
this.onNodeAdded(id);
|
|
|
|
- for(int j = -1; j <= 1; ++j) {
|
|
- for(int k = -1; k <= 1; ++k) {
|
|
- for(int l = -1; l <= 1; ++l) {
|
|
- this.sectionsAffectedByLightUpdates.add(SectionPos.blockToSection(BlockPos.offset(id, k, l, j)));
|
|
+ // Paper start - reuse x/y/z and only notify valid chunks - Credit to JellySquid (See above method for notes)
|
|
+ int x = (int) (id >> 38);
|
|
+ int y = (int) ((id << 52) >> 52);
|
|
+ int z = (int) ((id << 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(SectionPos.asLong(x2, y2, z2));
|
|
+ // Paper end
|
|
}
|
|
}
|
|
}
|
|
@@ -171,9 +209,9 @@ public abstract class LayerLightSectionStorage<M extends DataLayerStorageMap<M>>
|
|
return SectionPos.blockToSection(mx) == sectionPos;
|
|
});
|
|
} else {
|
|
- int i = SectionPos.sectionToBlockCoord(SectionPos.x(sectionPos));
|
|
- int j = SectionPos.sectionToBlockCoord(SectionPos.y(sectionPos));
|
|
- int k = SectionPos.sectionToBlockCoord(SectionPos.z(sectionPos));
|
|
+ int i = (int) (sectionPos >> 42) << 4; // Paper - inline
|
|
+ int j = (int) (sectionPos << 44 >> 44) << 4; // Paper - inline
|
|
+ int k = (int) (sectionPos << 22 >> 42) << 4; // Paper - inline
|
|
|
|
for(int l = 0; l < 16; ++l) {
|
|
for(int m = 0; m < 16; ++m) {
|
|
@@ -215,11 +253,12 @@ public abstract class LayerLightSectionStorage<M extends DataLayerStorageMap<M>>
|
|
this.toRemove.clear();
|
|
this.hasToRemove = false;
|
|
|
|
+ DataLayer test = null; // Paper
|
|
for(Entry<DataLayer> entry : this.queuedSections.long2ObjectEntrySet()) {
|
|
long n = entry.getLongKey();
|
|
- if (this.storingLightForSection(n)) {
|
|
+ if ((test = this.updating.getUpdatingOptimized(n)) != null) { // Paper - dont look up data layer twice
|
|
DataLayer dataLayer3 = entry.getValue();
|
|
- if (this.updatingSectionData.getLayer(n) != dataLayer3) {
|
|
+ if (test != dataLayer3) { // Paper
|
|
this.clearQueuedSectionBlocks(lightProvider, n);
|
|
this.updatingSectionData.setLayer(n, dataLayer3);
|
|
this.changedSections.add(n);
|
|
@@ -254,12 +293,17 @@ public abstract class LayerLightSectionStorage<M extends DataLayerStorageMap<M>>
|
|
|
|
private void checkEdgesForSection(LayerLightEngine<M, ?> lightProvider, long sectionPos) {
|
|
if (this.storingLightForSection(sectionPos)) {
|
|
- int i = SectionPos.sectionToBlockCoord(SectionPos.x(sectionPos));
|
|
- int j = SectionPos.sectionToBlockCoord(SectionPos.y(sectionPos));
|
|
- int k = SectionPos.sectionToBlockCoord(SectionPos.z(sectionPos));
|
|
+ // Paper start
|
|
+ int secX = (int) (sectionPos >> 42);
|
|
+ int secY = (int) (sectionPos << 44 >> 44);
|
|
+ int secZ = (int) (sectionPos << 22 >> 42);
|
|
+ int i = secX << 4; // baseX
|
|
+ int j = secY << 4; // baseY
|
|
+ int k = secZ << 4; // baseZ
|
|
+ // Paper end
|
|
|
|
for(Direction direction : DIRECTIONS) {
|
|
- long l = SectionPos.offset(sectionPos, direction);
|
|
+ long l = SectionPos.getAdjacentFromSectionPos(secX, secY, secZ, direction);
|
|
if (!this.queuedSections.containsKey(l) && this.storingLightForSection(l)) {
|
|
for(int m = 0; m < 16; ++m) {
|
|
for(int n = 0; n < 16; ++n) {
|
|
diff --git a/src/main/java/net/minecraft/world/level/lighting/SkyLightEngine.java b/src/main/java/net/minecraft/world/level/lighting/SkyLightEngine.java
|
|
index d122475c1a9d340046c478087d3ff5bf1ff8932c..a3189dd7b537deb4a30a32c8e9891b07d04af540 100644
|
|
--- a/src/main/java/net/minecraft/world/level/lighting/SkyLightEngine.java
|
|
+++ b/src/main/java/net/minecraft/world/level/lighting/SkyLightEngine.java
|
|
@@ -4,6 +4,7 @@ import net.minecraft.core.BlockPos;
|
|
import net.minecraft.core.Direction;
|
|
import net.minecraft.core.SectionPos;
|
|
import net.minecraft.world.level.LightLayer;
|
|
+import net.minecraft.world.level.block.Blocks;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
import net.minecraft.world.level.chunk.DataLayer;
|
|
import net.minecraft.world.level.chunk.LightChunkGetter;
|
|
@@ -27,32 +28,37 @@ public final class SkyLightEngine extends LayerLightEngine<SkyLightSectionStorag
|
|
return level;
|
|
} else {
|
|
//MutableInt mutableint = new MutableInt(); // Paper - share mutableint, single threaded
|
|
- BlockState blockState = this.getStateAndOpacity(targetId, mutableInt);
|
|
- if (mutableInt.getValue() >= 15) {
|
|
+ // Paper start - use x/y/z and optimized block lookup
|
|
+ int jx = (int) (targetId >> 38);
|
|
+ int jy = (int) ((targetId << 52) >> 52);
|
|
+ int jz = (int) ((targetId << 26) >> 38);
|
|
+ BlockState blockState = this.getBlockOptimized(jx, jy, jz, mutableInt);
|
|
+ int blockedLight = mutableInt.getValue();
|
|
+ if (blockedLight >= 15) {
|
|
+ // Paper end
|
|
return 15;
|
|
} else {
|
|
- int i = BlockPos.getX(sourceId);
|
|
- int j = BlockPos.getY(sourceId);
|
|
- int k = BlockPos.getZ(sourceId);
|
|
- int l = BlockPos.getX(targetId);
|
|
- int m = BlockPos.getY(targetId);
|
|
- int n = BlockPos.getZ(targetId);
|
|
- int o = Integer.signum(l - i);
|
|
- int p = Integer.signum(m - j);
|
|
- int q = Integer.signum(n - k);
|
|
+ // Paper start - inline math
|
|
+ int i = (int) (sourceId >> 38);
|
|
+ int j = (int) ((sourceId << 52) >> 52);
|
|
+ int k = (int) ((sourceId << 26) >> 38);
|
|
+ boolean bl = i == jx && k == jz;
|
|
+ int o = Integer.signum(jx - i);
|
|
+ int p = Integer.signum(jy - j);
|
|
+ int q = Integer.signum(jz - k);
|
|
+ // Paper end
|
|
Direction direction = Direction.fromNormal(o, p, q);
|
|
if (direction == null) {
|
|
throw new IllegalStateException(String.format("Light was spread in illegal direction %d, %d, %d", o, p, q));
|
|
} else {
|
|
- BlockState blockState2 = this.getStateAndOpacity(sourceId, (MutableInt)null);
|
|
+ BlockState blockState2 = sourceId == Long.MAX_VALUE ? net.minecraft.world.level.block.Blocks.AIR.defaultBlockState() : this.getBlockOptimized(i, j, k); // Paper
|
|
VoxelShape voxelShape = this.getShape(blockState2, sourceId, direction);
|
|
VoxelShape voxelShape2 = this.getShape(blockState, targetId, direction.getOpposite());
|
|
if (Shapes.faceShapeOccludes(voxelShape, voxelShape2)) {
|
|
return 15;
|
|
} else {
|
|
- boolean bl = i == l && k == n;
|
|
- boolean bl2 = bl && j > m;
|
|
- return bl2 && level == 0 && mutableInt.getValue() == 0 ? 0 : level + Math.max(1, mutableInt.getValue());
|
|
+ boolean bl2 = bl && j > jy; // Paper rename vars to jy from m
|
|
+ return bl2 && level == 0 && blockedLight == 0 ? 0 : level + Math.max(1, blockedLight); // Paper
|
|
}
|
|
}
|
|
}
|
|
@@ -64,10 +70,14 @@ public final class SkyLightEngine extends LayerLightEngine<SkyLightSectionStorag
|
|
|
|
@Override
|
|
protected void checkNeighborsAfterUpdate(long id, int level, boolean decrease) {
|
|
- long l = SectionPos.blockToSection(id);
|
|
- int i = BlockPos.getY(id);
|
|
- int j = SectionPos.sectionRelative(i);
|
|
- int k = SectionPos.blockToSectionCoord(i);
|
|
+ // Paper start
|
|
+ int baseX = (int) (id >> 38);
|
|
+ int baseY = (int) ((id << 52) >> 52);
|
|
+ int baseZ = (int) ((id << 26) >> 38);
|
|
+ long l = SectionPos.blockPosAsSectionLong(baseX, baseY, baseZ);
|
|
+ int j = baseY & 15;
|
|
+ int k = baseY >> 4;
|
|
+ // Paper end
|
|
int m;
|
|
if (j != 0) {
|
|
m = 0;
|
|
@@ -79,14 +89,15 @@ public final class SkyLightEngine extends LayerLightEngine<SkyLightSectionStorag
|
|
m = n;
|
|
}
|
|
|
|
- long p = BlockPos.offset(id, 0, -1 - m * 16, 0);
|
|
- long q = SectionPos.blockToSection(p);
|
|
+ int newBaseY = baseY + (-1 - m * 16); // Paper
|
|
+ long p = BlockPos.asLong(baseX, newBaseY, baseZ); // Paper
|
|
+ long q = SectionPos.blockPosAsSectionLong(baseX, newBaseY, baseZ); // Paper
|
|
if (l == q || this.storage.storingLightForSection(q)) {
|
|
this.checkNeighbor(id, p, level, decrease);
|
|
}
|
|
|
|
- long r = BlockPos.offset(id, Direction.UP);
|
|
- long s = SectionPos.blockToSection(r);
|
|
+ long r = BlockPos.asLong(baseX, baseY + 1, baseZ); // Paper
|
|
+ long s = SectionPos.blockPosAsSectionLong(baseX, baseY + 1, baseZ); // Paper
|
|
if (l == s || this.storage.storingLightForSection(s)) {
|
|
this.checkNeighbor(id, r, level, decrease);
|
|
}
|
|
@@ -95,8 +106,8 @@ public final class SkyLightEngine extends LayerLightEngine<SkyLightSectionStorag
|
|
int t = 0;
|
|
|
|
while(true) {
|
|
- long u = BlockPos.offset(id, direction.getStepX(), -t, direction.getStepZ());
|
|
- long v = SectionPos.blockToSection(u);
|
|
+ long u = BlockPos.asLong(baseX + direction.getStepX(), baseY - t, baseZ + direction.getStepZ()); // Paper
|
|
+ long v = SectionPos.blockPosAsSectionLong(baseX + direction.getStepX(), baseY - t, baseZ + direction.getStepZ()); // Paper
|
|
if (l == v) {
|
|
this.checkNeighbor(id, u, level, decrease);
|
|
break;
|
|
@@ -119,23 +130,33 @@ public final class SkyLightEngine extends LayerLightEngine<SkyLightSectionStorag
|
|
@Override
|
|
protected int getComputedLevel(long id, long excludedId, int maxLevel) {
|
|
int i = maxLevel;
|
|
- long l = SectionPos.blockToSection(id);
|
|
- DataLayer dataLayer = this.storage.getDataLayer(l, true);
|
|
+ // Paper start
|
|
+ int baseX = (int) (id >> 38);
|
|
+ int baseY = (int) ((id << 52) >> 52);
|
|
+ int baseZ = (int) ((id << 26) >> 38);
|
|
+ long l = SectionPos.blockPosAsSectionLong(baseX, baseY, baseZ);
|
|
+ DataLayer dataLayer = this.storage.updating.getUpdatingOptimized(l);
|
|
+ // Paper end
|
|
|
|
for(Direction direction : DIRECTIONS) {
|
|
- long m = BlockPos.offset(id, direction);
|
|
+ // Paper start
|
|
+ int newX = baseX + direction.getStepX();
|
|
+ int newY = baseY + direction.getStepY();
|
|
+ int newZ = baseZ + direction.getStepZ();
|
|
+ long m = BlockPos.asLong(newX, newY, newZ);
|
|
if (m != excludedId) {
|
|
- long n = SectionPos.blockToSection(m);
|
|
+ long n = SectionPos.blockPosAsSectionLong(newX, newY, newZ);
|
|
+ // Paper end
|
|
DataLayer dataLayer2;
|
|
if (l == n) {
|
|
dataLayer2 = dataLayer;
|
|
} else {
|
|
- dataLayer2 = this.storage.getDataLayer(n, true);
|
|
+ dataLayer2 = this.storage.updating.getUpdatingOptimized(n); // Paper
|
|
}
|
|
|
|
int j;
|
|
if (dataLayer2 != null) {
|
|
- j = this.getLevel(dataLayer2, m);
|
|
+ j = this.getLevel(dataLayer2, newX, newY, newZ); // Paper
|
|
} else {
|
|
if (direction == Direction.DOWN) {
|
|
continue;
|
|
diff --git a/src/main/java/net/minecraft/world/level/lighting/SkyLightSectionStorage.java b/src/main/java/net/minecraft/world/level/lighting/SkyLightSectionStorage.java
|
|
index 7dc194b4f04b2d59dcb100b0a3b2ca0132f832cf..c30fb3ad6716599c50acb918b40909e5dc052841 100644
|
|
--- a/src/main/java/net/minecraft/world/level/lighting/SkyLightSectionStorage.java
|
|
+++ b/src/main/java/net/minecraft/world/level/lighting/SkyLightSectionStorage.java
|
|
@@ -30,7 +30,12 @@ public class SkyLightSectionStorage extends LayerLightSectionStorage<SkyLightSec
|
|
}
|
|
|
|
protected int getLightValue(long l, boolean bl) {
|
|
- long m = SectionPos.blockToSection(l);
|
|
+ // Paper start
|
|
+ int baseX = (int) (l >> 38);
|
|
+ int baseY = (int) ((l << 52) >> 52);
|
|
+ int baseZ = (int) ((l << 26) >> 38);
|
|
+ long m = SectionPos.blockPosAsSectionLong(baseX, baseY, baseZ);
|
|
+ // Paper end
|
|
int i = SectionPos.y(m);
|
|
synchronized (this.visibleUpdateLock) { // Paper - avoid copying light data
|
|
SkyLightSectionStorage.SkyDataLayerStorageMap skyDataLayerStorageMap = (SkyLightSectionStorage.SkyDataLayerStorageMap) this.e_visible; // Paper - avoid copying light data - must be after lock acquire
|
|
@@ -49,7 +54,7 @@ public class SkyLightSectionStorage extends LayerLightSectionStorage<SkyLightSec
|
|
}
|
|
}
|
|
|
|
- return dataLayer.get(SectionPos.sectionRelative(BlockPos.getX(l)), SectionPos.sectionRelative(BlockPos.getY(l)), SectionPos.sectionRelative(BlockPos.getZ(l)));
|
|
+ return dataLayer.get(baseX & 15, (int) ((l << 52) >> 52) & 15, (int) baseZ & 15);
|
|
} else {
|
|
return bl && !this.lightOnInSection(m) ? 0 : 15;
|
|
}
|
|
@@ -157,7 +162,7 @@ public class SkyLightSectionStorage extends LayerLightSectionStorage<SkyLightSec
|
|
int i = (this.updatingSectionData).otherData.getUpdating(SectionPos.getZeroNode(sectionPos)); // Paper - avoid copying light data
|
|
if (i != (this.updatingSectionData).currentLowestY && SectionPos.y(l) < i) {
|
|
DataLayer dataLayer2;
|
|
- while((dataLayer2 = this.getDataLayer(l, true)) == null) {
|
|
+ while((dataLayer2 = this.updating.getUpdatingOptimized(l)) == null) { // Paper
|
|
l = SectionPos.offset(l, Direction.UP);
|
|
}
|
|
|
|
@@ -174,6 +179,9 @@ public class SkyLightSectionStorage extends LayerLightSectionStorage<SkyLightSec
|
|
if (doSkylight) {
|
|
if (!this.sectionsToAddSourcesTo.isEmpty()) {
|
|
for(long l : this.sectionsToAddSourcesTo) {
|
|
+ int baseX = (int) (l >> 42) << 4; // Paper
|
|
+ int baseY = (int) (l << 44 >> 44) << 4; // Paper
|
|
+ int baseZ = (int) (l << 22 >> 42) << 4; // Paper
|
|
int i = this.getLevel(l);
|
|
if (i != 2 && !this.sectionsToRemoveSourcesFrom.contains(l) && this.sectionsWithSources.add(l)) {
|
|
if (i == 1) {
|
|
@@ -182,13 +190,15 @@ public class SkyLightSectionStorage extends LayerLightSectionStorage<SkyLightSec
|
|
this.updatingSectionData.copyDataLayer(l);
|
|
}
|
|
|
|
- Arrays.fill(this.getDataLayer(l, true).asBytesPoolSafe(), (byte)-1); // Paper
|
|
- int j = SectionPos.sectionToBlockCoord(SectionPos.x(l));
|
|
- int k = SectionPos.sectionToBlockCoord(SectionPos.y(l));
|
|
- int m = SectionPos.sectionToBlockCoord(SectionPos.z(l));
|
|
+ // Paper start
|
|
+ Arrays.fill(this.updating.getUpdatingOptimized(l).asBytesPoolSafe(), (byte)-1); // Paper - use optimized
|
|
+ int j = baseX;
|
|
+ int k = baseY;
|
|
+ int m = baseZ;
|
|
+ // Paper end
|
|
|
|
for(Direction direction : HORIZONTALS) {
|
|
- long n = SectionPos.offset(l, direction);
|
|
+ long n = SectionPos.getAdjacentFromBlockPos(baseX, baseY, baseZ, direction); // Paper
|
|
if ((this.sectionsToRemoveSourcesFrom.contains(n) || !this.sectionsWithSources.contains(n) && !this.sectionsToAddSourcesTo.contains(n)) && this.storingLightForSection(n)) {
|
|
for(int o = 0; o < 16; ++o) {
|
|
for(int p = 0; p < 16; ++p) {
|
|
@@ -220,15 +230,15 @@ public class SkyLightSectionStorage extends LayerLightSectionStorage<SkyLightSec
|
|
|
|
for(int y = 0; y < 16; ++y) {
|
|
for(int z = 0; z < 16; ++z) {
|
|
- long aa = BlockPos.asLong(SectionPos.sectionToBlockCoord(SectionPos.x(l), y), SectionPos.sectionToBlockCoord(SectionPos.y(l)), SectionPos.sectionToBlockCoord(SectionPos.z(l), z));
|
|
- long ab = BlockPos.asLong(SectionPos.sectionToBlockCoord(SectionPos.x(l), y), SectionPos.sectionToBlockCoord(SectionPos.y(l)) - 1, SectionPos.sectionToBlockCoord(SectionPos.z(l), z));
|
|
+ long aa = BlockPos.asLong(baseX + y, baseY, baseZ + z); // Paper
|
|
+ long ab = BlockPos.asLong(baseX + y, baseY - 1, baseZ + z); // Paper
|
|
lightProvider.checkEdge(aa, ab, lightProvider.computeLevelFromNeighbor(aa, ab, 0), true);
|
|
}
|
|
}
|
|
} else {
|
|
for(int ac = 0; ac < 16; ++ac) {
|
|
for(int ad = 0; ad < 16; ++ad) {
|
|
- long ae = BlockPos.asLong(SectionPos.sectionToBlockCoord(SectionPos.x(l), ac), SectionPos.sectionToBlockCoord(SectionPos.y(l), 15), SectionPos.sectionToBlockCoord(SectionPos.z(l), ad));
|
|
+ long ae = BlockPos.asLong(baseX + ac, baseY + 16 - 1, baseZ + ad); // Paper
|
|
lightProvider.checkEdge(Long.MAX_VALUE, ae, 0, true);
|
|
}
|
|
}
|
|
@@ -240,10 +250,13 @@ public class SkyLightSectionStorage extends LayerLightSectionStorage<SkyLightSec
|
|
this.sectionsToAddSourcesTo.clear();
|
|
if (!this.sectionsToRemoveSourcesFrom.isEmpty()) {
|
|
for(long af : this.sectionsToRemoveSourcesFrom) {
|
|
+ int baseX = (int) (af >> 42) << 4; // Paper
|
|
+ int baseY = (int) (af << 44 >> 44) << 4; // Paper
|
|
+ int baseZ = (int) (af << 22 >> 42) << 4; // Paper
|
|
if (this.sectionsWithSources.remove(af) && this.storingLightForSection(af)) {
|
|
for(int ag = 0; ag < 16; ++ag) {
|
|
for(int ah = 0; ah < 16; ++ah) {
|
|
- long ai = BlockPos.asLong(SectionPos.sectionToBlockCoord(SectionPos.x(af), ag), SectionPos.sectionToBlockCoord(SectionPos.y(af), 15), SectionPos.sectionToBlockCoord(SectionPos.z(af), ah));
|
|
+ long ai = BlockPos.asLong(baseX + ag, baseY + 16 - 1, baseZ + ah); // Paper
|
|
lightProvider.checkEdge(Long.MAX_VALUE, ai, 15, false);
|
|
}
|
|
}
|