mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-30 16:19:03 +01:00
0f1a8717e8
Patch documentation to come Issues with the old system that are fixed now: - World generation does not scale with cpu cores effectively. - Relies on the main thread for scheduling and maintaining chunk state, dropping chunk load/generate rates at lower tps. - Unreliable prioritisation of chunk gen/load calls that block the main thread. - Shutdown logic is utterly unreliable, as it has to wait for all chunks to unload - is it guaranteed that the chunk system is in a state on shutdown that it can reliably do this? Watchdog shutdown also typically failed due to thread checks, which is now resolved. - Saving of data is not unified (i.e can save chunk data without saving entity data, poses problems for desync if shutdown is really abnormal. - Entities are not loaded with chunks. This caused quite a bit of headache for Chunk#getEntities API, but now the new chunk system loads entities with chunks so that they are ready whenever the chunk loads in. Effectively brings the behavior back to 1.16 era, but still storing entities in their own separate regionfiles. The above list is not complete. The patch documentation will complete it. New chunk system hard relies on starlight and dataconverter, and most importantly the new concurrent utilities in ConcurrentUtil. Some of the old async chunk i/o interface (i.e the old file io thread reroutes _some_ calls to the new file io thread) is kept for plugin compat reasons. It will be removed in the next major version of minecraft. The old legacy chunk system patches have been moved to the removed folder in case we need them again.
297 lines
17 KiB
Diff
297 lines
17 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
|
Date: Mon, 27 Apr 2020 04:05:38 -0700
|
|
Subject: [PATCH] Stop copy-on-write operations for updating light data
|
|
|
|
Causes huge memory allocations + gc issues
|
|
|
|
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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
--- a/src/main/java/net/minecraft/world/level/lighting/BlockLightSectionStorage.java
|
|
+++ b/src/main/java/net/minecraft/world/level/lighting/BlockLightSectionStorage.java
|
|
@@ -0,0 +0,0 @@ import net.minecraft.world.level.chunk.LightChunkGetter;
|
|
|
|
public class BlockLightSectionStorage extends LayerLightSectionStorage<BlockLightSectionStorage.BlockDataLayerStorageMap> {
|
|
protected BlockLightSectionStorage(LightChunkGetter chunkProvider) {
|
|
- super(LightLayer.BLOCK, chunkProvider, new BlockLightSectionStorage.BlockDataLayerStorageMap(new Long2ObjectOpenHashMap<>()));
|
|
+ super(LightLayer.BLOCK, chunkProvider, new BlockLightSectionStorage.BlockDataLayerStorageMap(new com.destroystokyo.paper.util.map.QueuedChangesMapLong2Object<>(), false)); // Paper - avoid copying light data
|
|
}
|
|
|
|
@Override
|
|
@@ -0,0 +0,0 @@ public class BlockLightSectionStorage extends LayerLightSectionStorage<BlockLigh
|
|
}
|
|
|
|
protected static final class BlockDataLayerStorageMap extends DataLayerStorageMap<BlockLightSectionStorage.BlockDataLayerStorageMap> {
|
|
- public BlockDataLayerStorageMap(Long2ObjectOpenHashMap<DataLayer> arrays) {
|
|
- super(arrays);
|
|
+ public BlockDataLayerStorageMap(com.destroystokyo.paper.util.map.QueuedChangesMapLong2Object<DataLayer> long2objectopenhashmap, boolean isVisible) { // Paper - avoid copying light data
|
|
+ super(long2objectopenhashmap, isVisible); // Paper - avoid copying light data
|
|
}
|
|
|
|
@Override
|
|
public BlockLightSectionStorage.BlockDataLayerStorageMap copy() {
|
|
- return new BlockLightSectionStorage.BlockDataLayerStorageMap(this.map.clone());
|
|
+ return new BlockDataLayerStorageMap(this.data, true); // Paper - avoid copying light data
|
|
}
|
|
}
|
|
}
|
|
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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
--- a/src/main/java/net/minecraft/world/level/lighting/DataLayerStorageMap.java
|
|
+++ b/src/main/java/net/minecraft/world/level/lighting/DataLayerStorageMap.java
|
|
@@ -0,0 +0,0 @@ public abstract class DataLayerStorageMap<M extends DataLayerStorageMap<M>> {
|
|
private final long[] lastSectionKeys = new long[2];
|
|
private final DataLayer[] lastSections = new DataLayer[2];
|
|
private boolean cacheEnabled;
|
|
- protected final Long2ObjectOpenHashMap<DataLayer> map;
|
|
+ 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
|
|
|
|
- protected DataLayerStorageMap(Long2ObjectOpenHashMap<DataLayer> arrays) {
|
|
- this.map = arrays;
|
|
+ // Paper start - avoid copying light data
|
|
+ protected DataLayerStorageMap(com.destroystokyo.paper.util.map.QueuedChangesMapLong2Object<DataLayer> data, boolean isVisible) {
|
|
+ if (isVisible) {
|
|
+ data.performUpdatesLockMap();
|
|
+ }
|
|
+ this.data = data;
|
|
+ this.isVisible = isVisible;
|
|
+ if (isVisible) {
|
|
+ lookup = data::getVisibleAsync;
|
|
+ } else {
|
|
+ lookup = data::getUpdating;
|
|
+ }
|
|
+ // Paper end - avoid copying light data
|
|
this.clearCache();
|
|
this.cacheEnabled = true;
|
|
}
|
|
@@ -0,0 +0,0 @@ public abstract class DataLayerStorageMap<M extends DataLayerStorageMap<M>> {
|
|
public abstract M copy();
|
|
|
|
public void copyDataLayer(long pos) {
|
|
- this.map.put(pos, this.map.get(pos).copy());
|
|
+ if (this.isVisible) { throw new IllegalStateException("writing to visible data"); } // Paper - avoid copying light data
|
|
+ this.data.queueUpdate(pos, ((DataLayer) this.data.getUpdating(pos)).copy()); // Paper - avoid copying light data
|
|
this.clearCache();
|
|
}
|
|
|
|
public boolean hasLayer(long chunkPos) {
|
|
- return this.map.containsKey(chunkPos);
|
|
+ return lookup.apply(chunkPos) != null; // Paper - avoid copying light data
|
|
}
|
|
|
|
@Nullable
|
|
- public DataLayer getLayer(long chunkPos) {
|
|
+ public final DataLayer getLayer(long chunkPos) { // Paper - final
|
|
if (this.cacheEnabled) {
|
|
for(int i = 0; i < 2; ++i) {
|
|
if (chunkPos == this.lastSectionKeys[i]) {
|
|
@@ -0,0 +0,0 @@ public abstract class DataLayerStorageMap<M extends DataLayerStorageMap<M>> {
|
|
}
|
|
}
|
|
|
|
- DataLayer dataLayer = this.map.get(chunkPos);
|
|
+ DataLayer dataLayer = lookup.apply(chunkPos); // Paper - avoid copying light data
|
|
if (dataLayer == null) {
|
|
return null;
|
|
} else {
|
|
@@ -0,0 +0,0 @@ public abstract class DataLayerStorageMap<M extends DataLayerStorageMap<M>> {
|
|
|
|
@Nullable
|
|
public DataLayer removeLayer(long chunkPos) {
|
|
- return this.map.remove(chunkPos);
|
|
+ if (this.isVisible) { throw new IllegalStateException("writing to visible data"); } // Paper - avoid copying light data
|
|
+ return (DataLayer) this.data.queueRemove(chunkPos); // Paper - avoid copying light data
|
|
}
|
|
|
|
public void setLayer(long pos, DataLayer data) {
|
|
- this.map.put(pos, data);
|
|
+ if (this.isVisible) { throw new IllegalStateException("writing to visible data"); } // Paper - avoid copying light data
|
|
+ this.data.queueUpdate(pos, data); // Paper - avoid copying light data
|
|
}
|
|
|
|
public void clearCache() {
|
|
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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
--- a/src/main/java/net/minecraft/world/level/lighting/LayerLightSectionStorage.java
|
|
+++ b/src/main/java/net/minecraft/world/level/lighting/LayerLightSectionStorage.java
|
|
@@ -0,0 +0,0 @@ public abstract class LayerLightSectionStorage<M extends DataLayerStorageMap<M>>
|
|
protected final LongSet dataSectionSet = new LongOpenHashSet();
|
|
protected final LongSet toMarkNoData = new LongOpenHashSet();
|
|
protected final LongSet toMarkData = new LongOpenHashSet();
|
|
- protected volatile M visibleSectionData;
|
|
+ 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 LongSet changedSections = new LongOpenHashSet();
|
|
protected final LongSet sectionsAffectedByLightUpdates = new LongOpenHashSet();
|
|
@@ -0,0 +0,0 @@ public abstract class LayerLightSectionStorage<M extends DataLayerStorageMap<M>>
|
|
this.layer = lightType;
|
|
this.chunkSource = chunkProvider;
|
|
this.updatingSectionData = lightData;
|
|
- this.visibleSectionData = lightData.copy();
|
|
- this.visibleSectionData.disableCache();
|
|
+ this.e_visible = lightData.copy(); // Paper - avoid copying light dat
|
|
+ this.e_visible.disableCache(); // Paper - avoid copying light dat
|
|
}
|
|
|
|
protected boolean storingLightForSection(long sectionPos) {
|
|
@@ -0,0 +0,0 @@ public abstract class LayerLightSectionStorage<M extends DataLayerStorageMap<M>>
|
|
|
|
@Nullable
|
|
protected DataLayer getDataLayer(long sectionPos, boolean cached) {
|
|
- return this.getDataLayer((M)(cached ? this.updatingSectionData : this.visibleSectionData), sectionPos);
|
|
+ // Paper start - avoid copying light data
|
|
+ if (cached) {
|
|
+ return this.getDataLayer(this.updatingSectionData, sectionPos);
|
|
+ } else {
|
|
+ synchronized (this.visibleUpdateLock) {
|
|
+ return this.getDataLayer(this.e_visible, sectionPos);
|
|
+ }
|
|
+ }
|
|
+ // Paper end - avoid copying light data
|
|
}
|
|
|
|
@Nullable
|
|
@@ -0,0 +0,0 @@ public abstract class LayerLightSectionStorage<M extends DataLayerStorageMap<M>>
|
|
|
|
protected void swapSectionMap() {
|
|
if (!this.changedSections.isEmpty()) {
|
|
+ synchronized (this.visibleUpdateLock) { // Paper - avoid copying light data
|
|
M dataLayerStorageMap = this.updatingSectionData.copy();
|
|
dataLayerStorageMap.disableCache();
|
|
- this.visibleSectionData = dataLayerStorageMap;
|
|
+ this.e_visible = dataLayerStorageMap; // Paper - avoid copying light data
|
|
+ } // Paper - avoid copying light data
|
|
this.changedSections.clear();
|
|
}
|
|
|
|
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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
--- a/src/main/java/net/minecraft/world/level/lighting/SkyLightSectionStorage.java
|
|
+++ b/src/main/java/net/minecraft/world/level/lighting/SkyLightSectionStorage.java
|
|
@@ -0,0 +0,0 @@ public class SkyLightSectionStorage extends LayerLightSectionStorage<SkyLightSec
|
|
private volatile boolean hasSourceInconsistencies;
|
|
|
|
protected SkyLightSectionStorage(LightChunkGetter chunkProvider) {
|
|
- super(LightLayer.SKY, chunkProvider, new SkyLightSectionStorage.SkyDataLayerStorageMap(new Long2ObjectOpenHashMap<>(), new Long2IntOpenHashMap(), Integer.MAX_VALUE));
|
|
+ super(LightLayer.SKY, chunkProvider, new SkyLightSectionStorage.SkyDataLayerStorageMap(new com.destroystokyo.paper.util.map.QueuedChangesMapLong2Object<>(), new com.destroystokyo.paper.util.map.QueuedChangesMapLong2Int(), Integer.MAX_VALUE, false)); // Paper - avoid copying light data
|
|
}
|
|
|
|
@Override
|
|
@@ -0,0 +0,0 @@ public class SkyLightSectionStorage extends LayerLightSectionStorage<SkyLightSec
|
|
protected int getLightValue(long blockPos, boolean cached) {
|
|
long l = SectionPos.blockToSection(blockPos);
|
|
int i = SectionPos.y(l);
|
|
- SkyLightSectionStorage.SkyDataLayerStorageMap skyDataLayerStorageMap = cached ? this.updatingSectionData : this.visibleSectionData;
|
|
- int j = skyDataLayerStorageMap.topSections.get(SectionPos.getZeroNode(l));
|
|
+ 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
|
|
+ int j = skyDataLayerStorageMap.otherData.getVisibleAsync(SectionPos.getZeroNode(l)); // Paper - avoid copying light data
|
|
if (j != skyDataLayerStorageMap.currentLowestY && i < j) {
|
|
DataLayer dataLayer = this.getDataLayer(skyDataLayerStorageMap, l);
|
|
if (dataLayer == null) {
|
|
@@ -0,0 +0,0 @@ public class SkyLightSectionStorage extends LayerLightSectionStorage<SkyLightSec
|
|
} else {
|
|
return cached && !this.lightOnInSection(l) ? 0 : 15;
|
|
}
|
|
+ } // Paper - avoid copying light data
|
|
}
|
|
|
|
@Override
|
|
@@ -0,0 +0,0 @@ public class SkyLightSectionStorage extends LayerLightSectionStorage<SkyLightSec
|
|
int i = SectionPos.y(sectionPos);
|
|
if ((this.updatingSectionData).currentLowestY > i) {
|
|
(this.updatingSectionData).currentLowestY = i;
|
|
- (this.updatingSectionData).topSections.defaultReturnValue((this.updatingSectionData).currentLowestY);
|
|
+ (this.updatingSectionData).otherData.queueDefaultReturnValue((this.updatingSectionData).currentLowestY); // Paper - avoid copying light data
|
|
}
|
|
|
|
long l = SectionPos.getZeroNode(sectionPos);
|
|
- int j = (this.updatingSectionData).topSections.get(l);
|
|
+ int j = (this.updatingSectionData).otherData.getUpdating(l); // Paper - avoid copying light data
|
|
if (j < i + 1) {
|
|
- (this.updatingSectionData).topSections.put(l, i + 1);
|
|
+ (this.updatingSectionData).otherData.queueUpdate(l, i + 1); // Paper - avoid copying light data
|
|
if (this.columnsWithSkySources.contains(l)) {
|
|
this.queueAddSource(sectionPos);
|
|
if (j > (this.updatingSectionData).currentLowestY) {
|
|
@@ -0,0 +0,0 @@ public class SkyLightSectionStorage extends LayerLightSectionStorage<SkyLightSec
|
|
}
|
|
|
|
int i = SectionPos.y(sectionPos);
|
|
- if ((this.updatingSectionData).topSections.get(l) == i + 1) {
|
|
+ if ((this.updatingSectionData).otherData.getUpdating(l) == i + 1) { // Paper - avoid copying light data
|
|
long m;
|
|
for(m = sectionPos; !this.storingLightForSection(m) && this.hasSectionsBelow(i); m = SectionPos.offset(m, Direction.DOWN)) {
|
|
--i;
|
|
}
|
|
|
|
if (this.storingLightForSection(m)) {
|
|
- (this.updatingSectionData).topSections.put(l, i + 1);
|
|
+ (this.updatingSectionData).otherData.queueUpdate(l, i + 1); // Paper - avoid copying light data
|
|
if (bl) {
|
|
this.queueAddSource(m);
|
|
}
|
|
} else {
|
|
- (this.updatingSectionData).topSections.remove(l);
|
|
+ (this.updatingSectionData).otherData.queueRemove(l); // Paper - avoid copying light data
|
|
}
|
|
}
|
|
|
|
@@ -0,0 +0,0 @@ public class SkyLightSectionStorage extends LayerLightSectionStorage<SkyLightSec
|
|
protected void enableLightSources(long columnPos, boolean enabled) {
|
|
this.runAllUpdates();
|
|
if (enabled && this.columnsWithSkySources.add(columnPos)) {
|
|
- int i = (this.updatingSectionData).topSections.get(columnPos);
|
|
+ int i = (this.updatingSectionData).otherData.getUpdating(columnPos); // Paper - avoid copying light data
|
|
if (i != (this.updatingSectionData).currentLowestY) {
|
|
long l = SectionPos.asLong(SectionPos.x(columnPos), i - 1, SectionPos.z(columnPos));
|
|
this.queueAddSource(l);
|
|
@@ -0,0 +0,0 @@ public class SkyLightSectionStorage extends LayerLightSectionStorage<SkyLightSec
|
|
return dataLayer;
|
|
} else {
|
|
long l = SectionPos.offset(sectionPos, Direction.UP);
|
|
- int i = (this.updatingSectionData).topSections.get(SectionPos.getZeroNode(sectionPos));
|
|
+ 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) {
|
|
@@ -0,0 +0,0 @@ public class SkyLightSectionStorage extends LayerLightSectionStorage<SkyLightSec
|
|
|
|
protected boolean isAboveData(long sectionPos) {
|
|
long l = SectionPos.getZeroNode(sectionPos);
|
|
- int i = (this.updatingSectionData).topSections.get(l);
|
|
+ int i = (this.updatingSectionData).otherData.getUpdating(l); // Paper - avoid copying light data
|
|
return i == (this.updatingSectionData).currentLowestY || SectionPos.y(sectionPos) >= i;
|
|
}
|
|
|
|
@@ -0,0 +0,0 @@ public class SkyLightSectionStorage extends LayerLightSectionStorage<SkyLightSec
|
|
|
|
protected static final class SkyDataLayerStorageMap extends DataLayerStorageMap<SkyLightSectionStorage.SkyDataLayerStorageMap> {
|
|
int currentLowestY;
|
|
- final Long2IntOpenHashMap topSections;
|
|
-
|
|
- public SkyDataLayerStorageMap(Long2ObjectOpenHashMap<DataLayer> arrays, Long2IntOpenHashMap columnToTopSection, int minSectionY) {
|
|
- super(arrays);
|
|
- this.topSections = columnToTopSection;
|
|
- columnToTopSection.defaultReturnValue(minSectionY);
|
|
+ private final com.destroystokyo.paper.util.map.QueuedChangesMapLong2Int otherData; // Paper - avoid copying light data
|
|
+
|
|
+ // Paper start - avoid copying light data
|
|
+ public SkyDataLayerStorageMap(com.destroystokyo.paper.util.map.QueuedChangesMapLong2Object<DataLayer> arrays, com.destroystokyo.paper.util.map.QueuedChangesMapLong2Int columnToTopSection, int minSectionY, boolean isVisible) {
|
|
+ super(arrays, isVisible);
|
|
+ this.otherData = columnToTopSection;
|
|
+ otherData.queueDefaultReturnValue(minSectionY);
|
|
+ // Paper end
|
|
this.currentLowestY = minSectionY;
|
|
}
|
|
|
|
@Override
|
|
public SkyLightSectionStorage.SkyDataLayerStorageMap copy() {
|
|
- return new SkyLightSectionStorage.SkyDataLayerStorageMap(this.map.clone(), this.topSections.clone(), this.currentLowestY);
|
|
+ this.otherData.performUpdatesLockMap(); // Paper - avoid copying light data
|
|
+ return new SkyLightSectionStorage.SkyDataLayerStorageMap(this.data, this.otherData, this.currentLowestY, true); // Paper - avoid copying light data
|
|
}
|
|
}
|
|
}
|