Skip to content

Commit b95bcde

Browse files
authored
Simplify heightmap calculation and avoid loading empty sections (#3045)
* Simplify heightmap calculation and avoid loading empty sections * rename label
1 parent c8f1984 commit b95bcde

File tree

9 files changed

+76
-24
lines changed

9 files changed

+76
-24
lines changed

worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1154,6 +1154,13 @@ public boolean hasSection(int layer) {
11541154
return getSections(false)[layer] != null;
11551155
}
11561156

1157+
@Override
1158+
public boolean hasNonEmptySection(int layer) {
1159+
layer -= getMinSectionPosition();
1160+
LevelChunkSection section = getSections(false)[layer];
1161+
return section != null && !section.hasOnlyAir();
1162+
}
1163+
11571164
@Override
11581165
@SuppressWarnings("unchecked")
11591166
public synchronized boolean trim(boolean aggressive) {

worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1152,6 +1152,13 @@ public boolean hasSection(int layer) {
11521152
return getSections(false)[layer] != null;
11531153
}
11541154

1155+
@Override
1156+
public boolean hasNonEmptySection(int layer) {
1157+
layer -= getMinSectionPosition();
1158+
LevelChunkSection section = getSections(false)[layer];
1159+
return section != null && !section.hasOnlyAir();
1160+
}
1161+
11551162
@Override
11561163
@SuppressWarnings("unchecked")
11571164
public synchronized boolean trim(boolean aggressive) {

worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1155,6 +1155,13 @@ public boolean hasSection(int layer) {
11551155
return getSections(false)[layer] != null;
11561156
}
11571157

1158+
@Override
1159+
public boolean hasNonEmptySection(int layer) {
1160+
layer -= getMinSectionPosition();
1161+
LevelChunkSection section = getSections(false)[layer];
1162+
return section != null && !section.hasOnlyAir();
1163+
}
1164+
11581165
@Override
11591166
@SuppressWarnings("unchecked")
11601167
public synchronized boolean trim(boolean aggressive) {

worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1150,6 +1150,13 @@ public boolean hasSection(int layer) {
11501150
return getSections(false)[layer] != null;
11511151
}
11521152

1153+
@Override
1154+
public boolean hasNonEmptySection(int layer) {
1155+
layer -= getMinSectionPosition();
1156+
LevelChunkSection section = getSections(false)[layer];
1157+
return section != null && !section.hasOnlyAir();
1158+
}
1159+
11531160
@Override
11541161
@SuppressWarnings("unchecked")
11551162
public synchronized boolean trim(boolean aggressive) {

worldedit-bukkit/adapters/adapter-1_21_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_3/PaperweightGetBlocks.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1151,6 +1151,13 @@ public boolean hasSection(int layer) {
11511151
return getSections(false)[layer] != null;
11521152
}
11531153

1154+
@Override
1155+
public boolean hasNonEmptySection(int layer) {
1156+
layer -= getMinSectionPosition();
1157+
LevelChunkSection section = getSections(false)[layer];
1158+
return section != null && !section.hasOnlyAir();
1159+
}
1160+
11541161
@Override
11551162
@SuppressWarnings("unchecked")
11561163
public synchronized boolean trim(boolean aggressive) {

worldedit-bukkit/adapters/adapter-1_21_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_4/PaperweightGetBlocks.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1149,6 +1149,13 @@ public boolean hasSection(int layer) {
11491149
return getSections(false)[layer] != null;
11501150
}
11511151

1152+
@Override
1153+
public boolean hasNonEmptySection(int layer) {
1154+
layer -= getMinSectionPosition();
1155+
LevelChunkSection section = getSections(false)[layer];
1156+
return section != null && !section.hasOnlyAir();
1157+
}
1158+
11521159
@Override
11531160
@SuppressWarnings("unchecked")
11541161
public synchronized boolean trim(boolean aggressive) {

worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/heightmap/HeightmapProcessor.java

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,10 @@ public class HeightmapProcessor implements IBatchProcessor {
1818
private static final HeightMapType[] TYPES = HeightMapType.values();
1919
private static final int BLOCKS_PER_Y_SHIFT = 8; // log2(256)
2020
private static final int BLOCKS_PER_Y = 256; // 16 x 16
21-
private static final boolean[] COMPLETE = new boolean[BLOCKS_PER_Y];
2221
private static final char[] AIR_LAYER = new char[4096];
22+
private static final int NEEDED_UPDATES = TYPES.length * BLOCKS_PER_Y;
2323

2424
static {
25-
Arrays.fill(COMPLETE, true);
2625
Arrays.fill(AIR_LAYER, (char) BlockTypesCache.ReservedIDs.AIR);
2726
}
2827

@@ -49,13 +48,12 @@ private static int index(int y, int offset) {
4948
public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) {
5049
// each heightmap gets one 16*16 array
5150
int[][] heightmaps = new int[TYPES.length][BLOCKS_PER_Y];
52-
boolean[][] updated = new boolean[TYPES.length][BLOCKS_PER_Y];
53-
int skip = 0;
54-
int allSkipped = (1 << TYPES.length) - 1; // lowest types.length bits are set
55-
layer:
51+
byte[] updated = new byte[BLOCKS_PER_Y];
52+
int updateCount = 0; // count updates, this way we know when we're finished
53+
layerIter:
5654
for (int layer = maxY >> 4; layer >= minY >> 4; layer--) {
57-
boolean hasSectionSet = set.hasSection(layer);
58-
boolean hasSectionGet = get.hasSection(layer);
55+
boolean hasSectionSet = set.hasNonEmptySection(layer);
56+
boolean hasSectionGet = get.hasNonEmptySection(layer);
5957
if (!(hasSectionSet || hasSectionGet)) {
6058
continue;
6159
}
@@ -78,7 +76,7 @@ public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) {
7876
if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) {
7977
if (!hasSectionGet) {
8078
if (!hasSectionSet) {
81-
continue layer;
79+
continue layerIter;
8280
}
8381
continue;
8482
} else if (getSection == null) {
@@ -88,7 +86,7 @@ public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) {
8886
|| Arrays.equals(getSection, AIR_LAYER)) {
8987
hasSectionGet = false;
9088
if (!hasSectionSet) {
91-
continue layer;
89+
continue layerIter;
9290
}
9391
continue;
9492
}
@@ -103,30 +101,26 @@ public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) {
103101
if (block == null) {
104102
continue;
105103
}
104+
byte updateStateAtJ = updated[j];
106105
for (int i = 0; i < TYPES.length; i++) {
107-
if ((skip & (1 << i)) != 0) {
108-
continue; // skip finished height map
106+
int bitFlag = 1 << i;
107+
if ((updateStateAtJ & bitFlag) != 0) {
108+
continue; // skip finished height map at this column
109109
}
110110
HeightMapType type = TYPES[i];
111111
// ignore if that position was already set
112-
if (!updated[i][j] && type.includes(block)) {
112+
if (type.includes(block)) {
113113
// mc requires + 1, heightmaps are normalized internally, thus we need to "zero" them.
114114
heightmaps[i][j] = ((layer - get.getMinSectionPosition()) << 4) + y + 1;
115-
updated[i][j] = true; // mark as updated
115+
updated[j] |= (byte) bitFlag; // mark as updated
116+
if (++updateCount == NEEDED_UPDATES) {
117+
break layerIter; // all heightmaps in all columns updated
118+
}
119+
116120
}
117121
}
118122
}
119123
}
120-
for (int i = 0; i < updated.length; i++) {
121-
if ((skip & (1 << i)) == 0 // if already true, skip array equality check
122-
&& Arrays.equals(updated[i], COMPLETE)) {
123-
skip |= 1 << i;
124-
}
125-
}
126-
if (skip != allSkipped) {
127-
continue;
128-
}
129-
break; // all maps are processed
130124
}
131125
for (int i = 0; i < TYPES.length; i++) {
132126
set.setHeightMap(TYPES[i], heightmaps[i]);

worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBlocks.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,17 @@ public interface IBlocks extends Trimable {
3737
*/
3838
boolean hasSection(int layer);
3939

40+
/**
41+
* {@return whether the chunk has a section that has any non-air/reserved blocks}
42+
* This method might be conservative and return {@code true} even if the section is empty.
43+
*
44+
* @param layer the section's layer
45+
* @since TODO
46+
*/
47+
default boolean hasNonEmptySection(int layer) {
48+
return hasSection(layer);
49+
}
50+
4051
/**
4152
* Obtain the specified chunk section stored as an array of ordinals. Uses normal minecraft chunk-section position indices
4253
* (length 4096). Operations synchronises on the section and will load the section into memory if not present. For chunk

worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -882,6 +882,11 @@ public boolean hasSection(int layer) {
882882
return chunkExisting != null && chunkExisting.hasSection(layer);
883883
}
884884

885+
@Override
886+
public boolean hasNonEmptySection(final int layer) {
887+
return chunkExisting != null && chunkExisting.hasNonEmptySection(layer);
888+
}
889+
885890
@Override
886891
public synchronized void filterBlocks(Filter filter, ChunkFilterBlock block, @Nullable Region region, boolean full) {
887892
final IChunkGet get = getOrCreateGet();

0 commit comments

Comments
 (0)