Further improve Chunk Light Prioritization

This is as fast as its going to get outside of making the light engine calculations it self faster.

Fixes #4022
This commit is contained in:
Aikar 2020-07-29 01:51:57 -04:00
parent 0f0a2ef492
commit 8392fa12ec

View File

@ -1038,20 +1038,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
- private final PlayerChunkMap d; - private final PlayerChunkMap d;
+ // Paper start + // Paper start
+ private static final int MAX_PRIORITIES = PlayerChunkMap.GOLDEN_TICKET + 2; + private static final int MAX_PRIORITIES = PlayerChunkMap.GOLDEN_TICKET + 2;
+ private final java.util.concurrent.ConcurrentLinkedQueue<Runnable> priorityChanges = new java.util.concurrent.ConcurrentLinkedQueue<>();
+
+ public void changePriority(long pair, int currentPriority, int priority) {
+ this.priorityChanges.add(() -> {
+ ChunkLightQueue remove = this.queue.buckets[currentPriority].remove(pair);
+ if (remove != null) {
+ ChunkLightQueue existing = this.queue.buckets[priority].put(pair, remove);
+ if (existing != null) {
+ remove.pre.addAll(existing.pre);
+ remove.post.addAll(existing.post);
+ }
+ }
+ });
+ }
+ +
+ private boolean isChunkLightStatus(long pair) { + private boolean isChunkLightStatus(long pair) {
+ PlayerChunk playerChunk = playerChunkMap.getUpdatingChunk(pair); + PlayerChunk playerChunk = playerChunkMap.getUpdatingChunk(pair);
@ -1070,12 +1056,28 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ ChunkLightQueue(long chunk) {} + ChunkLightQueue(long chunk) {}
+ } + }
+ +
+ static class PendingChunkLight {
+ long chunkId;
+ int priority;
+ Runnable pre;
+ Runnable post;
+
+ public PendingChunkLight(long chunkId, int priority, Runnable pre, Runnable post) {
+ this.chunkId = chunkId;
+ this.priority = priority;
+ this.pre = pre;
+ this.post = post;
+ }
+ }
+
+ +
+ // Retain the chunks priority level for queued light tasks + // Retain the chunks priority level for queued light tasks
+ private class LightQueue { + class LightQueue {
+ private int size = 0; + private int size = 0;
+ private int lowestPriority = MAX_PRIORITIES; + private int lowestPriority = MAX_PRIORITIES;
+ private final it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap<ChunkLightQueue>[] buckets = new it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap[MAX_PRIORITIES]; + private final it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap<ChunkLightQueue>[] buckets = new it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap[MAX_PRIORITIES];
+ private final java.util.concurrent.ConcurrentLinkedQueue<PendingChunkLight> pendingChunks = new java.util.concurrent.ConcurrentLinkedQueue<>();
+ private final java.util.concurrent.ConcurrentLinkedQueue<Runnable> priorityChanges = new java.util.concurrent.ConcurrentLinkedQueue<>();
+ +
+ private LightQueue() { + private LightQueue() {
+ for (int i = 0; i < buckets.length; i++) { + for (int i = 0; i < buckets.length; i++) {
@ -1083,6 +1085,29 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ } + }
+ } + }
+ +
+ 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[priority].put(pair, remove);
+ if (existing != null) {
+ remove.pre.addAll(existing.pre);
+ remove.post.addAll(existing.post);
+ }
+ }
+ if (this.buckets[priority].containsKey(pair)) {
+ if (lowestPriority > priority) {
+ lowestPriority = priority;
+ }
+ }
+ });
+ }
+
+ public final void addChunk(long chunkId, int priority, Runnable pre, Runnable post) {
+ pendingChunks.add(new PendingChunkLight(chunkId, priority, pre, post));
+ queueUpdate();
+ }
+
+ public final void add(long chunkId, int priority, LightEngineThreaded.Update type, Runnable run) { + public final void add(long chunkId, int priority, LightEngineThreaded.Update type, Runnable run) {
+ add(chunkId, priority, type, run, false); + add(chunkId, priority, type, run, false);
+ } + }
@ -1104,7 +1129,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ } + }
+ +
+ public final boolean isEmpty() { + public final boolean isEmpty() {
+ return this.size == 0; + return this.size == 0 && this.pendingChunks.isEmpty();
+ } + }
+ +
+ public final int size() { + public final int size() {
@ -1112,6 +1137,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ } + }
+ +
+ public boolean poll(java.util.List<Runnable> pre, java.util.List<Runnable> post) { + public boolean poll(java.util.List<Runnable> pre, java.util.List<Runnable> post) {
+ PendingChunkLight chunk;
+ while ((chunk = pendingChunks.poll()) != null) {
+ add(chunk.chunkId, chunk.priority, Update.PRE_UPDATE, chunk.pre, true);
+ add(chunk.chunkId, chunk.priority, Update.POST_UPDATE, chunk.post, true);
+ }
+ Runnable run; + Runnable run;
+ while ((run = priorityChanges.poll()) != null) { + while ((run = priorityChanges.poll()) != null) {
+ run.run(); + run.run();
@ -1122,7 +1152,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap<ChunkLightQueue> bucket = buckets[lowestPriority]; + it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap<ChunkLightQueue> bucket = buckets[lowestPriority];
+ if (bucket.isEmpty()) { + if (bucket.isEmpty()) {
+ lowestPriority++; + lowestPriority++;
+ continue; + if (hasWork && lowestPriority <= 3) {
+ return true;
+ } else {
+ continue;
+ }
+ } + }
+ ChunkLightQueue queue = bucket.removeFirst(); + ChunkLightQueue queue = bucket.removeFirst();
+ this.size -= queue.pre.size() + queue.post.size(); + this.size -= queue.pre.size() + queue.post.size();
@ -1139,7 +1173,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ } + }
+ } + }
+ +
+ private final LightQueue queue = new LightQueue(); + final LightQueue queue = new LightQueue();
+ // Paper end + // Paper end
+ private final PlayerChunkMap d; private final PlayerChunkMap playerChunkMap; // Paper + private final PlayerChunkMap d; private final PlayerChunkMap playerChunkMap; // Paper
private final Mailbox<ChunkTaskQueueSorter.a<Runnable>> e; private final Mailbox<ChunkTaskQueueSorter.a<Runnable>> e;
@ -1179,22 +1213,16 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ long pair = chunkcoordintpair.pair(); + long pair = chunkcoordintpair.pair();
+ CompletableFuture<IChunkAccess> future = new CompletableFuture<>(); + CompletableFuture<IChunkAccess> future = new CompletableFuture<>();
+ IntSupplier prioritySupplier = playerChunkMap.getPrioritySupplier(pair); + IntSupplier prioritySupplier = playerChunkMap.getPrioritySupplier(pair);
+ this.e.a(ChunkTaskQueueSorter.a(() -> { + boolean[] skippedPre = {false};
+ // Chunk's no longer needed + int priority = prioritySupplier.getAsInt();
+ this.queue.addChunk(pair, priority, SystemUtils.a(() -> {
+ if (!isChunkLightStatus(pair)) { + if (!isChunkLightStatus(pair)) {
+ this.d.c(chunkcoordintpair); // copied from end of method to release light ticket + this.d.c(chunkcoordintpair); // copied from end of method to release light ticket
+ future.complete(ichunkaccess); + future.complete(ichunkaccess);
+ skippedPre[0] = true;
+ return; + return;
+ } + }
+ boolean[] skippedPre = {false}; + // Paper end
+ this.queue.add(pair, prioritySupplier.getAsInt(), LightEngineThreaded.Update.PRE_UPDATE, SystemUtils.a(() -> {
+ if (!isChunkLightStatus(pair)) {
+ this.d.c(chunkcoordintpair); // copied from end of method to release light ticket
+ future.complete(ichunkaccess);
+ skippedPre[0] = true;
+ return;
+ }
+ // Paper end
ChunkSection[] achunksection = ichunkaccess.getSections(); ChunkSection[] achunksection = ichunkaccess.getSections();
for (int i = 0; i < 16; ++i) { for (int i = 0; i < 16; ++i) {
@ -1206,11 +1234,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ this.d.c(chunkcoordintpair); // Paper - if change, copy into !isChunkLightStatus above + this.d.c(chunkcoordintpair); // Paper - if change, copy into !isChunkLightStatus above
}, () -> { }, () -> {
return "lightChunk " + chunkcoordintpair + " " + flag; return "lightChunk " + chunkcoordintpair + " " + flag;
+ // Paper start - merge the 2 together - }));
}));
- return CompletableFuture.supplyAsync(() -> { - return CompletableFuture.supplyAsync(() -> {
+ + // Paper start - merge the 2 together
+ this.queue.add(pair, prioritySupplier.getAsInt(), LightEngineThreaded.Update.POST_UPDATE, () -> { + }), () -> {
+ if (skippedPre[0]) return; // Paper - future's already complete + if (skippedPre[0]) return; // Paper - future's already complete
ichunkaccess.b(true); ichunkaccess.b(true);
super.b(chunkcoordintpair, false); super.b(chunkcoordintpair, false);
@ -1220,8 +1247,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ // Paper start + // Paper start
+ future.complete(ichunkaccess); + future.complete(ichunkaccess);
}); });
+ queueUpdate(); // run queue now
+ }, pair, prioritySupplier));
+ return future; + return future;
+ // Paper end + // Paper end
} }
@ -1253,15 +1278,15 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
- if (pair.getFirst() == LightEngineThreaded.Update.PRE_UPDATE) { - if (pair.getFirst() == LightEngineThreaded.Update.PRE_UPDATE) {
- ((Runnable) pair.getSecond()).run(); - ((Runnable) pair.getSecond()).run();
- } - }
+ int i = Math.min(queue.size(), 4); + if (queue.poll(pre, post)) {
+ boolean ran = false;
+ while (i-- > 0 && queue.poll(pre, post)) {
+ pre.forEach(Runnable::run); + pre.forEach(Runnable::run);
+ pre.clear(); + pre.clear();
+ super.a(Integer.MAX_VALUE, true, true); + super.a(Integer.MAX_VALUE, true, true);
+ post.forEach(Runnable::run); + post.forEach(Runnable::run);
+ post.clear(); + post.clear();
+ ran = true; + } else {
+ // might have level updates to go still
+ super.a(Integer.MAX_VALUE, true, true);
} }
- -
- objectlistiterator.back(j); - objectlistiterator.back(j);
@ -1274,10 +1299,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
- } - }
- -
- objectlistiterator.remove(); - objectlistiterator.remove();
+ if (!ran) { - }
+ // might have level updates to go still
+ super.a(Integer.MAX_VALUE, true, true);
}
- -
+ // Paper end + // Paper end
} }
@ -1309,7 +1331,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
ioPriority = com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGH_PRIORITY; ioPriority = com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGH_PRIORITY;
} }
chunkMap.world.asyncChunkTaskManager.raisePriority(location.x, location.z, ioPriority); chunkMap.world.asyncChunkTaskManager.raisePriority(location.x, location.z, ioPriority);
+ chunkMap.world.getChunkProvider().getLightEngine().changePriority(location.pair(), getCurrentPriority(), priority); + chunkMap.world.getChunkProvider().getLightEngine().queue.changePriority(location.pair(), getCurrentPriority(), priority);
} }
if (getCurrentPriority() != priority) { if (getCurrentPriority() != priority) {
this.v.a(this.location, this::getCurrentPriority, priority, this::setPriority); // use preferred priority this.v.a(this.location, this::getCurrentPriority, priority, this::setPriority); // use preferred priority