mirror of
https://github.com/XevianLight/Aphelion.git
synced 2026-05-11 01:50:56 +01:00
giga optimized floodfill
This commit is contained in:
@@ -1,12 +1,19 @@
|
|||||||
package net.xevianlight.aphelion.block.entity.custom;
|
package net.xevianlight.aphelion.block.entity.custom;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.longs.Long2ObjectArrayMap;
|
||||||
|
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.core.SectionPos;
|
||||||
import net.minecraft.core.Vec3i;
|
import net.minecraft.core.Vec3i;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import static net.xevianlight.aphelion.Aphelion.LOGGER;
|
import static net.xevianlight.aphelion.Aphelion.LOGGER;
|
||||||
|
|
||||||
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
|
import net.minecraft.world.level.chunk.LevelChunk;
|
||||||
import net.xevianlight.aphelion.core.init.ModBlockEntities;
|
import net.xevianlight.aphelion.core.init.ModBlockEntities;
|
||||||
|
import org.openjdk.nashorn.internal.runtime.regexp.joni.exception.ValueException;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@@ -17,13 +24,86 @@ public class OxygenTestBlockEntity extends BlockEntity {
|
|||||||
super(ModBlockEntities.OXYGEN_TEST_BLOCK_ENTITY.get(), pos, blockState);
|
super(ModBlockEntities.OXYGEN_TEST_BLOCK_ENTITY.get(), pos, blockState);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean canSpreadTo(BlockPos pos) {
|
private LevelChunk lastChunk;
|
||||||
return level != null && level.getBlockState(pos).isAir();
|
private int lastChunkX = Integer.MIN_VALUE, lastChunkZ = Integer.MIN_VALUE;
|
||||||
|
|
||||||
|
public BlockState fastBlockState(BlockPos pos) {
|
||||||
|
int cx = pos.getX() >> 4;
|
||||||
|
int cz = pos.getZ() >> 4;
|
||||||
|
if (cx != lastChunkX || cz != lastChunkZ) {
|
||||||
|
lastChunk = level.getChunk(cx, cz);
|
||||||
|
|
||||||
|
lastChunkX = cx;
|
||||||
|
lastChunkZ = cz;
|
||||||
|
}
|
||||||
|
return lastChunk.getBlockState(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final int MAX_RANGE = 8;
|
public boolean canSpreadTo(BlockPos pos) {
|
||||||
public boolean isInRange(BlockPos pos, int radius) {
|
return level != null && fastBlockState(pos).isAir();
|
||||||
return radius <= MAX_RANGE;
|
}
|
||||||
|
|
||||||
|
public static final int MAX_RANGE = 100;
|
||||||
|
public boolean isInRange(BlockPos pos1, BlockPos pos2) {
|
||||||
|
return Math.abs(pos1.getX() - pos2.getX()) + Math.abs(pos1.getY() - pos2.getY()) + Math.abs(pos1.getZ() - pos2.getZ()) <= MAX_RANGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 256*256*256 grid of booleans
|
||||||
|
private class BigBoolGrid {
|
||||||
|
int bitsSize;
|
||||||
|
int size;
|
||||||
|
private int[] _grid;
|
||||||
|
int xOff, yOff, zOff;
|
||||||
|
|
||||||
|
/// Note that the ACTUAL size is 2^bitsSize, so 128 for example would be 7
|
||||||
|
/// max bitsSize is 12(?)
|
||||||
|
BigBoolGrid (int bitsSize, int xOrigin, int yOrigin, int zOrigin) {
|
||||||
|
if (Integer.bitCount(bitsSize) != 1) throw new ValueException("Grid size " + bitsSize + " is not a power of two!");
|
||||||
|
|
||||||
|
this.bitsSize = bitsSize;
|
||||||
|
this.size = (1 << bitsSize);
|
||||||
|
if (this.size < 8) throw new ValueException("Grid size is too small!");
|
||||||
|
|
||||||
|
_grid = new int[size * size * size / 8];
|
||||||
|
|
||||||
|
xOff = -xOrigin;
|
||||||
|
yOff = -yOrigin;
|
||||||
|
zOff = -zOrigin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getArrayPos(int x, int y, int z) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBit(int x) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the previous value at this location
|
||||||
|
public boolean add(int x, int y, int z) {
|
||||||
|
final int inX, inY, inZ;
|
||||||
|
inX = x + xOff + size / 2;
|
||||||
|
inY = y + yOff + size / 2;
|
||||||
|
inZ = z + zOff + size / 2;
|
||||||
|
if (inX >= size || inX < 0) throw new ValueException("Coordinate X out of range");
|
||||||
|
if (inY >= size || inY < 0) throw new ValueException("Coordinate Y out of range");
|
||||||
|
if (inZ >= size || inZ < 0) throw new ValueException("Coordinate Z out of range");
|
||||||
|
|
||||||
|
// the bytes are really just packed groups of 8 booleans,
|
||||||
|
// "laid across" the X direction.
|
||||||
|
// wrapping order is X->Z->Y, so we go along the X axis,
|
||||||
|
// wrap to the Z axis to make a square, and once the square is full
|
||||||
|
// we step up once along the Y axis.
|
||||||
|
int bitPos = inX & 7; // bottom 4 bits of X is bit pos
|
||||||
|
int bit = (1 << bitPos);
|
||||||
|
|
||||||
|
// First (bitsSize-4) bits are for X, next (bitsSize) bits are for Z, next (bitsSize) bits are for Y
|
||||||
|
int arrayPos = (inX >>> 4) + (inZ << (bitsSize-4)) + (inY << (bitsSize*2-4));
|
||||||
|
|
||||||
|
boolean out = !((_grid[arrayPos] & bit) > 0);
|
||||||
|
_grid[arrayPos] = _grid[arrayPos] | bit;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<BlockPos> enclosedCache;
|
private List<BlockPos> enclosedCache;
|
||||||
@@ -31,16 +111,17 @@ public class OxygenTestBlockEntity extends BlockEntity {
|
|||||||
if (level == null) return List.of();
|
if (level == null) return List.of();
|
||||||
if (enclosedCache != null) return enclosedCache;
|
if (enclosedCache != null) return enclosedCache;
|
||||||
|
|
||||||
|
long start = System.nanoTime();
|
||||||
List<BlockPos> enclosedBlocks = new ArrayList<>();
|
List<BlockPos> enclosedBlocks = new ArrayList<>();
|
||||||
Set<BlockPos> seen = new HashSet<>();
|
// make this bitch BIIIIIG
|
||||||
|
BigBoolGrid seen = new BigBoolGrid(8, this.getBlockPos().getX(), this.getBlockPos().getY(), this.getBlockPos().getZ());
|
||||||
|
|
||||||
// It's... a reasonable assumption that we won't have to include more blocks at once than the area of a sphere?
|
// It's... a reasonable assumption that we won't have to include more blocks at once than the area of a sphere?
|
||||||
// maybe a bit more, IDK how exactly it scales to blocks.
|
// maybe a bit more, IDK how exactly it scales to blocks.
|
||||||
Deque<BlockPos> queue = new ArrayDeque<>((int)(4 * Math.PI * MAX_RANGE * MAX_RANGE));
|
Stack<BlockPos> stack = new Stack<>();
|
||||||
Deque<Integer> radiusQueue = new ArrayDeque<>((int)(4 * Math.PI * MAX_RANGE * MAX_RANGE));
|
Stack<Integer> radiusStack = new Stack<>();
|
||||||
|
|
||||||
queue.add(this.getBlockPos());
|
stack.add(this.getBlockPos());
|
||||||
radiusQueue.add(0);
|
|
||||||
|
|
||||||
// Do flood fill out from this block
|
// Do flood fill out from this block
|
||||||
// Push on the top of the stack (newest), pop from the bottom of the stack (oldest).
|
// Push on the top of the stack (newest), pop from the bottom of the stack (oldest).
|
||||||
@@ -48,26 +129,27 @@ public class OxygenTestBlockEntity extends BlockEntity {
|
|||||||
// by how many "layers deep" it is (i.e. how many steps it took to get there),
|
// by how many "layers deep" it is (i.e. how many steps it took to get there),
|
||||||
// and you'd see that every pos of layer 1 is together, then layer 2, then layer 3...
|
// and you'd see that every pos of layer 1 is together, then layer 2, then layer 3...
|
||||||
|
|
||||||
long start = System.nanoTime();
|
|
||||||
while (!queue.isEmpty()) {
|
BlockPos ourPos = getBlockPos();
|
||||||
BlockPos spreadFromPos = queue.removeFirst();
|
while (!stack.isEmpty()) {
|
||||||
int radius = radiusQueue.removeFirst();
|
BlockPos spreadFromPos = stack.pop();
|
||||||
for (Direction d : Direction.values()) {
|
for (Direction d : Direction.values()) {
|
||||||
BlockPos relativePos = spreadFromPos.relative(d);
|
BlockPos relativePos = spreadFromPos.relative(d);
|
||||||
if (seen.contains(relativePos)) continue;
|
|
||||||
|
|
||||||
if (canSpreadTo(relativePos) && isInRange(relativePos, radius)) {
|
if (isInRange(relativePos, ourPos) && canSpreadTo(relativePos)) {
|
||||||
seen.add(relativePos);
|
// seen.add runs seen.contains under the hood,
|
||||||
enclosedBlocks.add(relativePos);
|
// + this is the most expensive operation.
|
||||||
|
// should save a lot of time!
|
||||||
queue.add(relativePos);
|
if (seen.add(relativePos.getX(), relativePos.getY(), relativePos.getZ())) {
|
||||||
radiusQueue.add(radius + 1);
|
enclosedBlocks.add(relativePos);
|
||||||
|
stack.add(relativePos);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
long durationNanos = System.nanoTime() - start;
|
long durationNanos = System.nanoTime() - start;
|
||||||
double durationMicros = durationNanos / 1000.0d;
|
double durationMicros = durationNanos / 1000.0d;
|
||||||
LOGGER.info("Flood fill completed in {}µs, {}µs/block", durationMicros, durationMicros / enclosedBlocks.size());
|
LOGGER.info("Flood fill completed in {}µs, {} blocks at {}µs/block", durationMicros, enclosedBlocks.size(), durationMicros / enclosedBlocks.size());
|
||||||
|
|
||||||
enclosedCache = enclosedBlocks;
|
enclosedCache = enclosedBlocks;
|
||||||
return enclosedBlocks;
|
return enclosedBlocks;
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ public class OxygenTestRenderer implements BlockEntityRenderer<OxygenTestBlockEn
|
|||||||
// Renderers are relative to our block pos, so transform all points to be relative to block pos as well
|
// Renderers are relative to our block pos, so transform all points to be relative to block pos as well
|
||||||
List<BlockPos> positionsToRender = toBlockPositions(be);
|
List<BlockPos> positionsToRender = toBlockPositions(be);
|
||||||
BlockPos originPos = be.getBlockPos();
|
BlockPos originPos = be.getBlockPos();
|
||||||
|
if (true) return;
|
||||||
|
|
||||||
Set<BlockPos> relativePositions;
|
Set<BlockPos> relativePositions;
|
||||||
if (relativePositionsCache != null) relativePositions = relativePositionsCache;
|
if (relativePositionsCache != null) relativePositions = relativePositionsCache;
|
||||||
|
|||||||
Reference in New Issue
Block a user