From 5229769fb28742b5c7b3d0db74de886329bd05dc Mon Sep 17 00:00:00 2001 From: SirYwell Date: Tue, 24 Dec 2024 12:14:32 +0100 Subject: [PATCH] Simplify heightmap calculation and avoid loading empty sections --- .../fawe/v1_20_R2/PaperweightGetBlocks.java | 7 ++++ .../fawe/v1_20_R3/PaperweightGetBlocks.java | 7 ++++ .../fawe/v1_20_R4/PaperweightGetBlocks.java | 7 ++++ .../fawe/v1_21_R1/PaperweightGetBlocks.java | 7 ++++ .../fawe/v1_21_3/PaperweightGetBlocks.java | 7 ++++ .../fawe/v1_21_4/PaperweightGetBlocks.java | 7 ++++ .../heightmap/HeightmapProcessor.java | 36 ++++++++----------- .../core/queue/IBlocks.java | 11 ++++++ .../implementation/chunk/ChunkHolder.java | 5 +++ 9 files changed, 73 insertions(+), 21 deletions(-) diff --git a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks.java index 309d349331..f17e8d3e35 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks.java @@ -1154,6 +1154,13 @@ public boolean hasSection(int layer) { return getSections(false)[layer] != null; } + @Override + public boolean hasNonEmptySection(int layer) { + layer -= getMinSectionPosition(); + LevelChunkSection section = getSections(false)[layer]; + return section != null && !section.hasOnlyAir(); + } + @Override @SuppressWarnings("unchecked") public synchronized boolean trim(boolean aggressive) { diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks.java index 30f7f44e2e..3ac1bb7bbf 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks.java @@ -1152,6 +1152,13 @@ public boolean hasSection(int layer) { return getSections(false)[layer] != null; } + @Override + public boolean hasNonEmptySection(int layer) { + layer -= getMinSectionPosition(); + LevelChunkSection section = getSections(false)[layer]; + return section != null && !section.hasOnlyAir(); + } + @Override @SuppressWarnings("unchecked") public synchronized boolean trim(boolean aggressive) { diff --git a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks.java index 077c08ecb5..17d51add28 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks.java @@ -1155,6 +1155,13 @@ public boolean hasSection(int layer) { return getSections(false)[layer] != null; } + @Override + public boolean hasNonEmptySection(int layer) { + layer -= getMinSectionPosition(); + LevelChunkSection section = getSections(false)[layer]; + return section != null && !section.hasOnlyAir(); + } + @Override @SuppressWarnings("unchecked") public synchronized boolean trim(boolean aggressive) { diff --git a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks.java index b9cdc3cbcb..0f5ab9449c 100644 --- a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks.java @@ -1150,6 +1150,13 @@ public boolean hasSection(int layer) { return getSections(false)[layer] != null; } + @Override + public boolean hasNonEmptySection(int layer) { + layer -= getMinSectionPosition(); + LevelChunkSection section = getSections(false)[layer]; + return section != null && !section.hasOnlyAir(); + } + @Override @SuppressWarnings("unchecked") public synchronized boolean trim(boolean aggressive) { diff --git a/worldedit-bukkit/adapters/adapter-1_21_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_3/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_21_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_3/PaperweightGetBlocks.java index 16460db696..5b3aae691a 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_3/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_21_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_3/PaperweightGetBlocks.java @@ -1151,6 +1151,13 @@ public boolean hasSection(int layer) { return getSections(false)[layer] != null; } + @Override + public boolean hasNonEmptySection(int layer) { + layer -= getMinSectionPosition(); + LevelChunkSection section = getSections(false)[layer]; + return section != null && !section.hasOnlyAir(); + } + @Override @SuppressWarnings("unchecked") public synchronized boolean trim(boolean aggressive) { diff --git a/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightGetBlocks.java index 581c8d0980..8e5ac0c175 100644 --- a/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightGetBlocks.java @@ -1149,6 +1149,13 @@ public boolean hasSection(int layer) { return getSections(false)[layer] != null; } + @Override + public boolean hasNonEmptySection(int layer) { + layer -= getMinSectionPosition(); + LevelChunkSection section = getSections(false)[layer]; + return section != null && !section.hasOnlyAir(); + } + @Override @SuppressWarnings("unchecked") public synchronized boolean trim(boolean aggressive) { diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/heightmap/HeightmapProcessor.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/heightmap/HeightmapProcessor.java index 5d8f81e39d..70e4c7f653 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/heightmap/HeightmapProcessor.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/heightmap/HeightmapProcessor.java @@ -18,11 +18,10 @@ public class HeightmapProcessor implements IBatchProcessor { private static final HeightMapType[] TYPES = HeightMapType.values(); private static final int BLOCKS_PER_Y_SHIFT = 8; // log2(256) private static final int BLOCKS_PER_Y = 256; // 16 x 16 - private static final boolean[] COMPLETE = new boolean[BLOCKS_PER_Y]; private static final char[] AIR_LAYER = new char[4096]; + private static final int NEEDED_UPDATES = TYPES.length * BLOCKS_PER_Y; static { - Arrays.fill(COMPLETE, true); Arrays.fill(AIR_LAYER, (char) BlockTypesCache.ReservedIDs.AIR); } @@ -49,13 +48,12 @@ private static int index(int y, int offset) { public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) { // each heightmap gets one 16*16 array int[][] heightmaps = new int[TYPES.length][BLOCKS_PER_Y]; - boolean[][] updated = new boolean[TYPES.length][BLOCKS_PER_Y]; - int skip = 0; - int allSkipped = (1 << TYPES.length) - 1; // lowest types.length bits are set + byte[] updated = new byte[BLOCKS_PER_Y]; + int updateCount = 0; // count updates, this way we know when we're finished layer: for (int layer = maxY >> 4; layer >= minY >> 4; layer--) { - boolean hasSectionSet = set.hasSection(layer); - boolean hasSectionGet = get.hasSection(layer); + boolean hasSectionSet = set.hasNonEmptySection(layer); + boolean hasSectionGet = get.hasNonEmptySection(layer); if (!(hasSectionSet || hasSectionGet)) { continue; } @@ -103,30 +101,26 @@ public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) { if (block == null) { continue; } + byte updateStateAtJ = updated[j]; for (int i = 0; i < TYPES.length; i++) { - if ((skip & (1 << i)) != 0) { - continue; // skip finished height map + int bitFlag = 1 << i; + if ((updateStateAtJ & bitFlag) != 0) { + continue; // skip finished height map at this column } HeightMapType type = TYPES[i]; // ignore if that position was already set - if (!updated[i][j] && type.includes(block)) { + if (type.includes(block)) { // mc requires + 1, heightmaps are normalized internally, thus we need to "zero" them. heightmaps[i][j] = ((layer - get.getMinSectionPosition()) << 4) + y + 1; - updated[i][j] = true; // mark as updated + updated[j] |= (byte) bitFlag; // mark as updated + if (++updateCount == NEEDED_UPDATES) { + break layer; // all heightmaps in all columns updated + } + } } } } - for (int i = 0; i < updated.length; i++) { - if ((skip & (1 << i)) == 0 // if already true, skip array equality check - && Arrays.equals(updated[i], COMPLETE)) { - skip |= 1 << i; - } - } - if (skip != allSkipped) { - continue; - } - break; // all maps are processed } for (int i = 0; i < TYPES.length; i++) { set.setHeightMap(TYPES[i], heightmaps[i]); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBlocks.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBlocks.java index 3a9d71da1c..9923223ea9 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBlocks.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBlocks.java @@ -37,6 +37,17 @@ public interface IBlocks extends Trimable { */ boolean hasSection(int layer); + /** + * {@return whether the chunk has a section that has any non-air/reserved blocks} + * This method might be conservative and return {@code true} even if the section is empty. + * + * @param layer the section's layer + * @since TODO + */ + default boolean hasNonEmptySection(int layer) { + return hasSection(layer); + } + /** * Obtain the specified chunk section stored as an array of ordinals. Uses normal minecraft chunk-section position indices * (length 4096). Operations synchronises on the section and will load the section into memory if not present. For chunk diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java index c3a36be2d9..375578e44c 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java @@ -882,6 +882,11 @@ public boolean hasSection(int layer) { return chunkExisting != null && chunkExisting.hasSection(layer); } + @Override + public boolean hasNonEmptySection(final int layer) { + return chunkExisting != null && chunkExisting.hasNonEmptySection(layer); + } + @Override public synchronized void filterBlocks(Filter filter, ChunkFilterBlock block, @Nullable Region region, boolean full) { final IChunkGet get = getOrCreateGet();