Mirror von
https://github.com/IntellectualSites/FastAsyncWorldEdit.git
synchronisiert 2024-12-26 02:50:06 +01:00
Optimise HeightmapProcessor (around 16x faster)
Dieser Commit ist enthalten in:
Ursprung
f0a6fa13da
Commit
88ae6d9e1d
@ -1,67 +1,86 @@
|
|||||||
package com.fastasyncworldedit.core.extent.processor;
|
package com.fastasyncworldedit.core.extent.processor;
|
||||||
|
|
||||||
|
import com.fastasyncworldedit.core.FaweCache;
|
||||||
import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
|
import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
|
||||||
import com.fastasyncworldedit.core.queue.IBatchProcessor;
|
import com.fastasyncworldedit.core.queue.IBatchProcessor;
|
||||||
import com.fastasyncworldedit.core.queue.IChunk;
|
import com.fastasyncworldedit.core.queue.IChunk;
|
||||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||||
import com.fastasyncworldedit.core.queue.IChunkSet;
|
import com.fastasyncworldedit.core.queue.IChunkSet;
|
||||||
import com.sk89q.worldedit.extent.Extent;
|
import com.sk89q.worldedit.extent.Extent;
|
||||||
import com.sk89q.worldedit.world.World;
|
|
||||||
import com.sk89q.worldedit.world.block.BlockState;
|
import com.sk89q.worldedit.world.block.BlockState;
|
||||||
import com.sk89q.worldedit.world.block.BlockType;
|
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.BitSet;
|
import java.util.Arrays;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
public class HeightmapProcessor implements IBatchProcessor {
|
public class HeightmapProcessor implements IBatchProcessor {
|
||||||
|
|
||||||
private static final HeightMapType[] TYPES = HeightMapType.values();
|
private static final HeightMapType[] TYPES = HeightMapType.values();
|
||||||
private static final BlockType RESERVED = BlockTypes.__RESERVED__;
|
private static final boolean[] COMPLETE = new boolean[256];
|
||||||
private static final int SECTION_SIDE_LENGTH = 16;
|
private static final char[] AIR_LAYER = new char[4096];
|
||||||
private static final int BLOCKS_PER_Y_LEVEL = SECTION_SIDE_LENGTH * SECTION_SIDE_LENGTH;
|
|
||||||
|
|
||||||
private final int maxY;
|
static {
|
||||||
private final int minY;
|
Arrays.fill(COMPLETE, true);
|
||||||
|
Arrays.fill(AIR_LAYER, (char) 1);
|
||||||
public HeightmapProcessor(World world) {
|
|
||||||
this.maxY = world.getMaxY();
|
|
||||||
this.minY = world.getMinY();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) {
|
public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) {
|
||||||
// each heightmap gets one 16*16 array
|
// each heightmap gets one 16*16 array
|
||||||
int[][] heightmaps = new int[TYPES.length][BLOCKS_PER_Y_LEVEL];
|
int[][] heightmaps = new int[TYPES.length][256];
|
||||||
BitSet[] updated = new BitSet[TYPES.length];
|
boolean[][] updated = new boolean[TYPES.length][256];
|
||||||
for (int i = 0; i < updated.length; i++) {
|
|
||||||
updated[i] = new BitSet(BLOCKS_PER_Y_LEVEL);
|
|
||||||
}
|
|
||||||
int skip = 0;
|
int skip = 0;
|
||||||
int allSkipped = (1 << TYPES.length) - 1; // lowest types.length bits are set
|
int allSkipped = (1 << TYPES.length) - 1; // lowest types.length bits are set
|
||||||
for (int y = maxY; y >= minY; y--) {
|
layer:
|
||||||
boolean hasSectionSet = set.hasSection(y >> 4);
|
for (int layer = set.getMaxSectionIndex(); layer >= set.getMinSectionIndex(); layer--) {
|
||||||
boolean hasSectionGet = get.hasSection(y >> 4);
|
boolean hasSectionSet = set.hasSection(layer);
|
||||||
|
boolean hasSectionGet = get.hasSection(layer);
|
||||||
if (!(hasSectionSet || hasSectionGet)) {
|
if (!(hasSectionSet || hasSectionGet)) {
|
||||||
y -= (SECTION_SIDE_LENGTH - 1); // - 1, as we do y-- in the loop head
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (int z = 0; z < SECTION_SIDE_LENGTH; z++) {
|
char[] setSection = hasSectionSet ? set.load(layer) : null;
|
||||||
for (int x = 0; x < SECTION_SIDE_LENGTH; x++) {
|
if (Arrays.equals(setSection, FaweCache.IMP.EMPTY_CHAR_4096) || Arrays.equals(setSection, AIR_LAYER)) {
|
||||||
BlockState block = null;
|
hasSectionSet = false;
|
||||||
|
}
|
||||||
|
if (!hasSectionSet && !hasSectionGet) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
char[] getSection = null;
|
||||||
|
for (int y = 15; y >= 0; y--) {
|
||||||
|
// We don't need to actually iterate over x and z as we're both reading and writing an index
|
||||||
|
for (int j = 0; j < 256; j++) {
|
||||||
|
char ordinal = 0;
|
||||||
if (hasSectionSet) {
|
if (hasSectionSet) {
|
||||||
block = set.getBlock(x, y, z);
|
ordinal = setSection[y * j];
|
||||||
}
|
}
|
||||||
if (block == null || block.getBlockType() == RESERVED) {
|
if (ordinal == 0) {
|
||||||
if (!hasSectionGet) {
|
if (!hasSectionGet) {
|
||||||
|
if (!hasSectionSet) {
|
||||||
|
continue layer;
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
|
} else if (getSection == null) {
|
||||||
|
getSection = get.load(layer);
|
||||||
|
// skip empty layer
|
||||||
|
if (Arrays.equals(getSection, FaweCache.IMP.EMPTY_CHAR_4096)
|
||||||
|
|| Arrays.equals(getSection, AIR_LAYER)) {
|
||||||
|
hasSectionGet = false;
|
||||||
|
if (!hasSectionSet) {
|
||||||
|
continue layer;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
block = get.getBlock(x, y, z);
|
ordinal = getSection[y * j];
|
||||||
}
|
}
|
||||||
// fast skip if block isn't relevant for any height map
|
// fast skip if block isn't relevant for any height map (air or empty)
|
||||||
if (block.isAir()) {
|
if (ordinal < 4) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
BlockState block = BlockTypesCache.states[ordinal];
|
||||||
|
if (block == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < TYPES.length; i++) {
|
for (int i = 0; i < TYPES.length; i++) {
|
||||||
@ -69,18 +88,18 @@ public class HeightmapProcessor implements IBatchProcessor {
|
|||||||
continue; // skip finished height map
|
continue; // skip finished height map
|
||||||
}
|
}
|
||||||
HeightMapType type = TYPES[i];
|
HeightMapType type = TYPES[i];
|
||||||
int index = (z << 4) | x;
|
// ignore if that position was already set
|
||||||
if (!updated[i].get(index) // ignore if that position was already set
|
if (!updated[i][j] && type.includes(block)) {
|
||||||
&& type.includes(block)) {
|
// mc requires + 1, heightmaps are normalized internally
|
||||||
heightmaps[i][index] = y + 1 - minY; // mc requires + 1, heightmaps are normalized internally
|
heightmaps[i][j] = (layer << 4) + y + 1;
|
||||||
updated[i].set(index); // mark as updated
|
updated[i][j] = true; // mark as updated
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (int i = 0; i < updated.length; i++) {
|
for (int i = 0; i < updated.length; i++) {
|
||||||
if ((skip & (1 << i)) == 0 // if already true, skip cardinality calculation
|
if ((skip & (1 << i)) == 0 // if already true, skip array equality check
|
||||||
&& updated[i].cardinality() == BLOCKS_PER_Y_LEVEL) {
|
&& Arrays.equals(updated[i], COMPLETE)) {
|
||||||
skip |= 1 << i;
|
skip |= 1 << i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -424,7 +424,7 @@ public class EditSessionBuilder {
|
|||||||
} else {
|
} else {
|
||||||
relighter = NullRelighter.INSTANCE;
|
relighter = NullRelighter.INSTANCE;
|
||||||
}
|
}
|
||||||
extent.addProcessor(new HeightmapProcessor(world));
|
extent.addProcessor(new HeightmapProcessor());
|
||||||
if (limit != null && !limit.isUnlimited() && regionExtent != null) {
|
if (limit != null && !limit.isUnlimited() && regionExtent != null) {
|
||||||
this.extent = new LimitExtent(regionExtent, limit);
|
this.extent = new LimitExtent(regionExtent, limit);
|
||||||
} else if (limit != null && !limit.isUnlimited()) {
|
} else if (limit != null && !limit.isUnlimited()) {
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren