From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Spottedleaf 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 { 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 { - public BlockDataLayerStorageMap(Long2ObjectOpenHashMap arrays) { - super(arrays); + public BlockDataLayerStorageMap(com.destroystokyo.paper.util.map.QueuedChangesMapLong2Object 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> { private final long[] lastSectionKeys = new long[2]; private final DataLayer[] lastSections = new DataLayer[2]; private boolean cacheEnabled; - protected final Long2ObjectOpenHashMap map; + protected final com.destroystokyo.paper.util.map.QueuedChangesMapLong2Object data; // Paper - avoid copying light data + protected final boolean isVisible; // Paper - avoid copying light data + java.util.function.Function lookup; // Paper - faster branchless lookup - protected DataLayerStorageMap(Long2ObjectOpenHashMap arrays) { - this.map = arrays; + // Paper start - avoid copying light data + protected DataLayerStorageMap(com.destroystokyo.paper.util.map.QueuedChangesMapLong2Object 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> { 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> { } } - 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> { @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> 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> 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> @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> 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(), 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 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= i; } @@ -0,0 +0,0 @@ public class SkyLightSectionStorage extends LayerLightSectionStorage { int currentLowestY; - final Long2IntOpenHashMap topSections; - - public SkyDataLayerStorageMap(Long2ObjectOpenHashMap 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 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 } } }