async chunk loading

This commit is contained in:
Jesse Boyd 2019-05-02 01:45:18 +10:00
parent 33e119ccb6
commit eec08c81ad
No known key found for this signature in database
GPG Key ID: 59F1DE6293AF6E1F
20 changed files with 1224 additions and 882 deletions

View File

@ -6,7 +6,9 @@ import com.boydti.fawe.beta.IQueueExtent;
import com.boydti.fawe.beta.implementation.blocks.CharSetBlocks; import com.boydti.fawe.beta.implementation.blocks.CharSetBlocks;
import com.boydti.fawe.beta.implementation.holder.ChunkHolder; import com.boydti.fawe.beta.implementation.holder.ChunkHolder;
public class BukkitChunkHolder extends ChunkHolder<Boolean> { import java.util.concurrent.Future;
public class BukkitChunkHolder<T extends Future<T>> extends ChunkHolder {
@Override @Override
public void init(final IQueueExtent extent, final int X, final int Z) { public void init(final IQueueExtent extent, final int X, final int Z) {
super.init(extent, X, Z); super.init(extent, X, Z);
@ -19,30 +21,37 @@ public class BukkitChunkHolder extends ChunkHolder<Boolean> {
} }
@Override @Override
public boolean applyAsync() { public T call() {
BukkitGetBlocks get = (BukkitGetBlocks) cachedGet(); BukkitQueue extent = (BukkitQueue) getExtent();
CharSetBlocks set = (CharSetBlocks) cachedSet(); BukkitGetBlocks get = (BukkitGetBlocks) getOrCreateGet();
// - getBlocks CharSetBlocks set = (CharSetBlocks) getOrCreateSet();
// - set lock
// - synchronize on chunk object
// - verify section is same object as chunk's section
// - merge with setblocks /*
// - set section
// - verify chunk is same - getBlocks
// - verify section is same - set ChunkSection lock with a tracking lock
// - Otherwise repeat steps on main thread - synchronize on chunk object (so no other FAWE thread updates it at the same time)
// - set status to needs relighting - verify cached section is same object as NMS chunk section
// - mark as dirty otherwise, fetch the new section, set the tracking lock and reconstruct the getBlocks array
// - skip verification if main thread - Merge raw getBlocks and setBlocks array
- Construct the ChunkSection
- In parallel on the main thread
- if the tracking lock has had no updates and the cached ChunkSection == the NMS chunk section
- Otherwise, reconstruct the ChunkSection (TODO: Benchmark if this is a performance concern)
- swap in the new ChunkSection
- Update tile entities/entities (if necessary)
- Merge the biome array (if necessary)
- set chunk status to needs relighting
- mark as dirty
*/
throw new UnsupportedOperationException("Not implemented"); throw new UnsupportedOperationException("Not implemented");
// return true; // return true;
} }
@Override
public boolean applySync() {
return true;
}
@Override @Override
public void set(final Filter filter) { public void set(final Filter filter) {
// for each block // for each block

View File

@ -1,42 +0,0 @@
package com.boydti.fawe.bukkit.beta;
import com.boydti.fawe.beta.Filter;
import com.boydti.fawe.beta.IQueueExtent;
import com.boydti.fawe.beta.ISetBlocks;
import com.boydti.fawe.beta.implementation.holder.ChunkHolder;
public class BukkitFullChunk extends ChunkHolder {
public BukkitFullChunk() {
throw new UnsupportedOperationException("Not implemented");
}
@Override
public void init(IQueueExtent extent, int X, int Z) {
}
@Override
public boolean applyAsync() {
return false;
}
@Override
public boolean applySync() {
return false;
}
@Override
public void set(Filter filter) {
}
@Override
public Object get() {
return null;
}
@Override
public ISetBlocks set() {
return null;
}
}

View File

@ -24,10 +24,10 @@ import net.minecraft.server.v1_13_R2.World;
import static com.boydti.fawe.bukkit.v0.BukkitQueue_0.getAdapter; import static com.boydti.fawe.bukkit.v0.BukkitQueue_0.getAdapter;
public class BukkitGetBlocks extends CharGetBlocks { public class BukkitGetBlocks extends CharGetBlocks {
private ChunkSection[] sections; public ChunkSection[] sections;
private Chunk nmsChunk; public Chunk nmsChunk;
private World nmsWorld; public World nmsWorld;
private int X, Z; public int X, Z;
public BukkitGetBlocks(World nmsWorld, int X, int Z) {/*d*/ public BukkitGetBlocks(World nmsWorld, int X, int Z) {/*d*/
this.nmsWorld = nmsWorld; this.nmsWorld = nmsWorld;
@ -48,12 +48,12 @@ public class BukkitGetBlocks extends CharGetBlocks {
} }
@Override @Override
protected char[] load(int layer) { public char[] load(int layer) {
return load(layer, null); return load(layer, null);
} }
@Override @Override
protected char[] load(int layer, char[] data) { public char[] load(int layer, char[] data) {
ChunkSection section = getSections()[layer]; ChunkSection section = getSections()[layer];
// Section is null, return empty array // Section is null, return empty array
if (section == null) { if (section == null) {
@ -136,7 +136,7 @@ public class BukkitGetBlocks extends CharGetBlocks {
return data; return data;
} }
private ChunkSection[] getSections() { public ChunkSection[] getSections() {
ChunkSection[] tmp = sections; ChunkSection[] tmp = sections;
if (tmp == null) { if (tmp == null) {
Chunk chunk = getChunk(); Chunk chunk = getChunk();
@ -145,16 +145,10 @@ public class BukkitGetBlocks extends CharGetBlocks {
return tmp; return tmp;
} }
private Chunk getChunk() { public Chunk getChunk() {
Chunk tmp = nmsChunk; Chunk tmp = nmsChunk;
if (tmp == null) { if (tmp == null) {
ChunkProviderServer provider = (ChunkProviderServer) nmsWorld.getChunkProvider(); nmsChunk = tmp = BukkitQueue.ensureLoaded(nmsWorld, X, Z);
nmsChunk = tmp = provider.chunks.get(ChunkCoordIntPair.a(X, Z));
if (tmp == null) {
System.out.println("SYNC");
// TOOD load with paper
nmsChunk = tmp = TaskManager.IMP.sync(() -> nmsWorld.getChunkAt(X, Z));
}
} }
return tmp; return tmp;
} }

View File

@ -1,16 +1,43 @@
package com.boydti.fawe.bukkit.beta; package com.boydti.fawe.bukkit.beta;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.beta.IChunk; import com.boydti.fawe.beta.IChunk;
import com.boydti.fawe.beta.implementation.SimpleCharQueueExtent; import com.boydti.fawe.beta.implementation.SimpleCharQueueExtent;
import com.boydti.fawe.beta.implementation.SingleThreadQueueExtent; import com.boydti.fawe.beta.implementation.SingleThreadQueueExtent;
import com.boydti.fawe.beta.implementation.WorldChunkCache; import com.boydti.fawe.beta.implementation.WorldChunkCache;
import com.boydti.fawe.bukkit.adapter.v1_13_1.BlockMaterial_1_13;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.jnbt.anvil.BitArray4096;
import com.boydti.fawe.object.collection.IterableThreadLocal; import com.boydti.fawe.object.collection.IterableThreadLocal;
import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.TaskManager;
import com.sk89q.worldedit.bukkit.BukkitWorld; import com.sk89q.worldedit.bukkit.BukkitWorld;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.block.BlockID;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypes;
import net.minecraft.server.v1_13_R2.Block;
import net.minecraft.server.v1_13_R2.Chunk;
import net.minecraft.server.v1_13_R2.ChunkCoordIntPair;
import net.minecraft.server.v1_13_R2.ChunkProviderServer;
import net.minecraft.server.v1_13_R2.ChunkSection;
import net.minecraft.server.v1_13_R2.DataBits;
import net.minecraft.server.v1_13_R2.DataPalette;
import net.minecraft.server.v1_13_R2.DataPaletteBlock;
import net.minecraft.server.v1_13_R2.DataPaletteLinear;
import net.minecraft.server.v1_13_R2.GameProfileSerializer;
import net.minecraft.server.v1_13_R2.IBlockData;
import net.minecraft.server.v1_13_R2.WorldServer; import net.minecraft.server.v1_13_R2.WorldServer;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_13_R2.CraftChunk;
import org.bukkit.craftbukkit.v1_13_R2.CraftWorld; import org.bukkit.craftbukkit.v1_13_R2.CraftWorld;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
public class BukkitQueue extends SimpleCharQueueExtent { public class BukkitQueue extends SimpleCharQueueExtent {
@ -45,19 +72,177 @@ public class BukkitQueue extends SimpleCharQueueExtent {
super.reset(); super.reset();
} }
private static final IterableThreadLocal<BukkitFullChunk> FULL_CHUNKS = new IterableThreadLocal<BukkitFullChunk>() { // private static final IterableThreadLocal<BukkitFullChunk> FULL_CHUNKS = new IterableThreadLocal<BukkitFullChunk>() {
@Override // @Override
public BukkitFullChunk init() { // public BukkitFullChunk init() {
return new BukkitFullChunk(); // return new BukkitFullChunk();
} // }
}; // };
@Override @Override
public IChunk create(boolean full) { public IChunk create(boolean full) {
if (full) { // if (full) {
// TODO implement // //TODO implement
// return FULL_CHUNKS.get(); // return FULL_CHUNKS.get();
} // }
return new BukkitChunkHolder(); return new BukkitChunkHolder();
} }
/*
NMS fields
*/
public final static Field fieldBits;
public final static Field fieldPalette;
public final static Field fieldSize;
public final static Field fieldFluidCount;
public final static Field fieldTickingBlockCount;
public final static Field fieldNonEmptyBlockCount;
static {
try {
fieldSize = DataPaletteBlock.class.getDeclaredField("i");
fieldSize.setAccessible(true);
fieldBits = DataPaletteBlock.class.getDeclaredField("a");
fieldBits.setAccessible(true);
fieldPalette = DataPaletteBlock.class.getDeclaredField("h");
fieldPalette.setAccessible(true);
fieldFluidCount = ChunkSection.class.getDeclaredField("e");
fieldFluidCount.setAccessible(true);
fieldTickingBlockCount = ChunkSection.class.getDeclaredField("tickingBlockCount");
fieldTickingBlockCount.setAccessible(true);
fieldNonEmptyBlockCount = ChunkSection.class.getDeclaredField("nonEmptyBlockCount");
fieldNonEmptyBlockCount.setAccessible(true);
} catch (RuntimeException e) {
throw e;
} catch (Throwable rethrow) {
rethrow.printStackTrace();
throw new RuntimeException(rethrow);
}
}
private static boolean PAPER = true;
public Chunk ensureLoaded(int X, int Z) {
return ensureLoaded(nmsWorld, X, Z);
}
public static Chunk ensureLoaded(net.minecraft.server.v1_13_R2.World nmsWorld, int X, int Z) {
ChunkProviderServer provider = (ChunkProviderServer) nmsWorld.getChunkProvider();
Chunk nmsChunk = provider.chunks.get(ChunkCoordIntPair.a(X, Z));
if (nmsChunk != null) {
return nmsChunk;
}
if (Fawe.isMainThread()) {
return nmsWorld.getChunkAt(X, Z);
}
if (PAPER) {
CraftWorld craftWorld = nmsWorld.getWorld();
CompletableFuture<org.bukkit.Chunk> future = craftWorld.getChunkAtAsync(X, Z, true);
try {
CraftChunk chunk = (CraftChunk) future.get();
return chunk.getHandle();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (Throwable e) {
System.out.println("Error, cannot load chunk async (paper not installed?)");
PAPER = false;
}
}
// TODO optimize
return TaskManager.IMP.sync(() -> nmsWorld.getChunkAt(X, Z));
}
/*
NMS conversion
*/
public static ChunkSection newChunkSection(final int y2, final boolean flag, final char[] blocks) {
ChunkSection section = new ChunkSection(y2 << 4, flag);
if (blocks == null) {
return section;
}
final int[] blockToPalette = FaweCache.BLOCK_TO_PALETTE.get();
final int[] paletteToBlock = FaweCache.PALETTE_TO_BLOCK.get();
final long[] blockstates = FaweCache.BLOCK_STATES.get();
final int[] blocksCopy = FaweCache.SECTION_BLOCKS.get();
try {
int num_palette = 0;
int air = 0;
for (int i = 0; i < 4096; i++) {
char ordinal = blocks[i];
switch (ordinal) {
case 0:
case BlockID.AIR:
case BlockID.CAVE_AIR:
case BlockID.VOID_AIR:
air++;
}
int palette = blockToPalette[ordinal];
if (palette == Integer.MAX_VALUE) {
blockToPalette[ordinal] = palette = num_palette;
paletteToBlock[num_palette] = ordinal;
num_palette++;
}
blocksCopy[i] = palette;
}
// BlockStates
int bitsPerEntry = MathMan.log2nlz(num_palette - 1);
if (Settings.IMP.PROTOCOL_SUPPORT_FIX || num_palette != 1) {
bitsPerEntry = Math.max(bitsPerEntry, 4); // Protocol support breaks <4 bits per entry
} else {
bitsPerEntry = Math.max(bitsPerEntry, 1); // For some reason minecraft needs 4096 bits to store 0 entries
}
final int blockBitArrayEnd = (bitsPerEntry * 4096) >> 6;
if (num_palette == 1) {
for (int i = 0; i < blockBitArrayEnd; i++) blockstates[i] = 0;
} else {
final BitArray4096 bitArray = new BitArray4096(blockstates, bitsPerEntry);
bitArray.fromRaw(blocksCopy);
}
// set palette & data bits
final DataPaletteBlock<IBlockData> dataPaletteBlocks = section.getBlocks();
// private DataPalette<T> h;
// protected DataBits a;
final long[] bits = Arrays.copyOfRange(blockstates, 0, blockBitArrayEnd);
final DataBits nmsBits = new DataBits(bitsPerEntry, 4096, bits);
final DataPalette<IBlockData> palette;
// palette = new DataPaletteHash<>(Block.REGISTRY_ID, bitsPerEntry, dataPaletteBlocks, GameProfileSerializer::d, GameProfileSerializer::a);
palette = new DataPaletteLinear<>(Block.REGISTRY_ID, bitsPerEntry, dataPaletteBlocks, GameProfileSerializer::d);
// set palette
for (int i = 0; i < num_palette; i++) {
final int ordinal = paletteToBlock[i];
blockToPalette[ordinal] = Integer.MAX_VALUE;
final BlockState state = BlockTypes.states[ordinal];
final IBlockData ibd = ((BlockMaterial_1_13) state.getMaterial()).getState();
palette.a(ibd);
}
try {
fieldBits.set(dataPaletteBlocks, nmsBits);
fieldPalette.set(dataPaletteBlocks, palette);
fieldSize.set(dataPaletteBlocks, bitsPerEntry);
setCount(0, 4096 - air, section);
} catch (final IllegalAccessException | NoSuchFieldException e) {
throw new RuntimeException(e);
}
return section;
} catch (final Throwable e){
Arrays.fill(blockToPalette, Integer.MAX_VALUE);
throw e;
}
}
public static void setCount(final int tickingBlockCount, final int nonEmptyBlockCount, final ChunkSection section) throws NoSuchFieldException, IllegalAccessException {
fieldFluidCount.set(section, 0); // TODO FIXME
fieldTickingBlockCount.set(section, tickingBlockCount);
fieldNonEmptyBlockCount.set(section, nonEmptyBlockCount);
}
} }

View File

@ -0,0 +1,78 @@
package com.boydti.fawe.bukkit.beta;
import java.util.Collection;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class DelegateLock extends ReentrantLock {
private final ReentrantLock parent;
private volatile boolean modified;
public DelegateLock(ReentrantLock parent) {
this.parent = parent;
}
@Override
public void lock() {
modified = true;
parent.lock();
}
@Override
public synchronized void lockInterruptibly() throws InterruptedException {
parent.lockInterruptibly();
}
@Override
public synchronized boolean tryLock() {
return parent.tryLock();
}
@Override
public synchronized boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
return parent.tryLock(timeout, unit);
}
@Override
public synchronized void unlock() {
modified = true;
parent.unlock();
this.notifyAll();
}
@Override
public synchronized Condition newCondition() {
return parent.newCondition();
}
@Override
public synchronized int getHoldCount() {
return parent.getHoldCount();
}
@Override
public synchronized boolean isHeldByCurrentThread() {
return parent.isHeldByCurrentThread();
}
@Override
public synchronized boolean isLocked() {
return parent.isLocked();
}
@Override
public synchronized boolean hasWaiters(Condition condition) {
return parent.hasWaiters(condition);
}
@Override
public synchronized int getWaitQueueLength(Condition condition) {
return parent.getWaitQueueLength(condition);
}
@Override
public synchronized String toString() {
return parent.toString();
}
}

View File

@ -70,7 +70,6 @@ public class BukkitQueue_1_13 extends BukkitQueue_0<net.minecraft.server.v1_13_R
public final static Field fieldBits; public final static Field fieldBits;
public final static Field fieldPalette; public final static Field fieldPalette;
protected final static Field fieldSize; protected final static Field fieldSize;
protected final static Field fieldHashBlocks; protected final static Field fieldHashBlocks;
@ -88,6 +87,7 @@ public class BukkitQueue_1_13 extends BukkitQueue_0<net.minecraft.server.v1_13_R
protected final static Field fieldFluidCount; protected final static Field fieldFluidCount;
protected final static Field fieldTickingBlockCount; protected final static Field fieldTickingBlockCount;
protected final static Field fieldNonEmptyBlockCount; protected final static Field fieldNonEmptyBlockCount;
protected final static Field fieldSection; protected final static Field fieldSection;
protected final static Field fieldLiquidCount; protected final static Field fieldLiquidCount;
protected final static Field fieldEmittedLight; protected final static Field fieldEmittedLight;
@ -847,85 +847,84 @@ public class BukkitQueue_1_13 extends BukkitQueue_0<net.minecraft.server.v1_13_R
} }
public static ChunkSection newChunkSection(final int y2, final boolean flag, final int[] blocks) { public static ChunkSection newChunkSection(final int y2, final boolean flag, final int[] blocks) {
ChunkSection section = new ChunkSection(y2 << 4, flag);
if (blocks == null) { if (blocks == null) {
return new ChunkSection(y2 << 4, flag); return section;
} else { }
final ChunkSection section = new ChunkSection(y2 << 4, flag); final int[] blockToPalette = FaweCache.BLOCK_TO_PALETTE.get();
final int[] blockToPalette = FaweCache.BLOCK_TO_PALETTE.get(); final int[] paletteToBlock = FaweCache.PALETTE_TO_BLOCK.get();
final int[] paletteToBlock = FaweCache.PALETTE_TO_BLOCK.get(); final long[] blockstates = FaweCache.BLOCK_STATES.get();
final long[] blockstates = FaweCache.BLOCK_STATES.get(); final int[] blocksCopy = FaweCache.SECTION_BLOCKS.get();
final int[] blocksCopy = FaweCache.SECTION_BLOCKS.get(); try {
try { int num_palette = 0;
int num_palette = 0; int air = 0;
int air = 0; for (int i = 0; i < 4096; i++) {
for (int i = 0; i < 4096; i++) { int stateId = blocks[i];
int stateId = blocks[i]; switch (stateId) {
switch (stateId) { case 0:
case 0: case BlockID.AIR:
case BlockID.AIR: case BlockID.CAVE_AIR:
case BlockID.CAVE_AIR: case BlockID.VOID_AIR:
case BlockID.VOID_AIR: stateId = BlockID.AIR;
stateId = BlockID.AIR; air++;
air++;
}
final int ordinal = BlockState.getFromInternalId(stateId).getOrdinal(); // TODO fixme Remove all use of BlockTypes.BIT_OFFSET so that this conversion isn't necessary
int palette = blockToPalette[ordinal];
if (palette == Integer.MAX_VALUE) {
blockToPalette[ordinal] = palette = num_palette;
paletteToBlock[num_palette] = ordinal;
num_palette++;
}
blocksCopy[i] = palette;
} }
final int ordinal = BlockState.getFromInternalId(stateId).getOrdinal(); // TODO fixme Remove all use of BlockTypes.BIT_OFFSET so that this conversion isn't necessary
// BlockStates int palette = blockToPalette[ordinal];
int bitsPerEntry = MathMan.log2nlz(num_palette - 1); if (palette == Integer.MAX_VALUE) {
if (Settings.IMP.PROTOCOL_SUPPORT_FIX || num_palette != 1) { blockToPalette[ordinal] = palette = num_palette;
bitsPerEntry = Math.max(bitsPerEntry, 4); // Protocol support breaks <4 bits per entry paletteToBlock[num_palette] = ordinal;
} else { num_palette++;
bitsPerEntry = Math.max(bitsPerEntry, 1); // For some reason minecraft needs 4096 bits to store 0 entries
} }
blocksCopy[i] = palette;
final int blockBitArrayEnd = (bitsPerEntry * 4096) >> 6;
if (num_palette == 1) {
for (int i = 0; i < blockBitArrayEnd; i++) blockstates[i] = 0;
} else {
final BitArray4096 bitArray = new BitArray4096(blockstates, bitsPerEntry);
bitArray.fromRaw(blocksCopy);
}
// set palette & data bits
final DataPaletteBlock<IBlockData> dataPaletteBlocks = section.getBlocks();
// private DataPalette<T> h;
// protected DataBits a;
final long[] bits = Arrays.copyOfRange(blockstates, 0, blockBitArrayEnd);
final DataBits nmsBits = new DataBits(bitsPerEntry, 4096, bits);
final DataPalette<IBlockData> palette;
// palette = new DataPaletteHash<>(Block.REGISTRY_ID, bitsPerEntry, dataPaletteBlocks, GameProfileSerializer::d, GameProfileSerializer::a);
palette = new DataPaletteLinear<>(Block.REGISTRY_ID, bitsPerEntry, dataPaletteBlocks, GameProfileSerializer::d);
// set palette
for (int i = 0; i < num_palette; i++) {
final int ordinal = paletteToBlock[i];
blockToPalette[ordinal] = Integer.MAX_VALUE;
final BlockState state = BlockTypes.states[ordinal];
final IBlockData ibd = ((BlockMaterial_1_13) state.getMaterial()).getState();
palette.a(ibd);
}
try {
fieldBits.set(dataPaletteBlocks, nmsBits);
fieldPalette.set(dataPaletteBlocks, palette);
fieldSize.set(dataPaletteBlocks, bitsPerEntry);
setCount(0, 4096 - air, section);
} catch (final IllegalAccessException | NoSuchFieldException e) {
throw new RuntimeException(e);
}
return section;
} catch (final Throwable e){
Arrays.fill(blockToPalette, Integer.MAX_VALUE);
throw e;
} }
// BlockStates
int bitsPerEntry = MathMan.log2nlz(num_palette - 1);
if (Settings.IMP.PROTOCOL_SUPPORT_FIX || num_palette != 1) {
bitsPerEntry = Math.max(bitsPerEntry, 4); // Protocol support breaks <4 bits per entry
} else {
bitsPerEntry = Math.max(bitsPerEntry, 1); // For some reason minecraft needs 4096 bits to store 0 entries
}
final int blockBitArrayEnd = (bitsPerEntry * 4096) >> 6;
if (num_palette == 1) {
for (int i = 0; i < blockBitArrayEnd; i++) blockstates[i] = 0;
} else {
final BitArray4096 bitArray = new BitArray4096(blockstates, bitsPerEntry);
bitArray.fromRaw(blocksCopy);
}
// set palette & data bits
final DataPaletteBlock<IBlockData> dataPaletteBlocks = section.getBlocks();
// private DataPalette<T> h;
// protected DataBits a;
final long[] bits = Arrays.copyOfRange(blockstates, 0, blockBitArrayEnd);
final DataBits nmsBits = new DataBits(bitsPerEntry, 4096, bits);
final DataPalette<IBlockData> palette;
// palette = new DataPaletteHash<>(Block.REGISTRY_ID, bitsPerEntry, dataPaletteBlocks, GameProfileSerializer::d, GameProfileSerializer::a);
palette = new DataPaletteLinear<>(Block.REGISTRY_ID, bitsPerEntry, dataPaletteBlocks, GameProfileSerializer::d);
// set palette
for (int i = 0; i < num_palette; i++) {
final int ordinal = paletteToBlock[i];
blockToPalette[ordinal] = Integer.MAX_VALUE;
final BlockState state = BlockTypes.states[ordinal];
final IBlockData ibd = ((BlockMaterial_1_13) state.getMaterial()).getState();
palette.a(ibd);
}
try {
fieldBits.set(dataPaletteBlocks, nmsBits);
fieldPalette.set(dataPaletteBlocks, palette);
fieldSize.set(dataPaletteBlocks, bitsPerEntry);
setCount(0, 4096 - air, section);
} catch (final IllegalAccessException | NoSuchFieldException e) {
throw new RuntimeException(e);
}
return section;
} catch (final Throwable e){
Arrays.fill(blockToPalette, Integer.MAX_VALUE);
throw e;
} }
} }

View File

@ -1,6 +1,7 @@
package com.boydti.fawe; package com.boydti.fawe;
import com.boydti.fawe.beta.Trimable; import com.boydti.fawe.beta.Trimable;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.jnbt.anvil.BitArray4096; import com.boydti.fawe.jnbt.anvil.BitArray4096;
import com.boydti.fawe.object.collection.IterableThreadLocal; import com.boydti.fawe.object.collection.IterableThreadLocal;
import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.MathMan;
@ -11,6 +12,12 @@ import com.sk89q.worldedit.world.block.BlockTypes;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.*; import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class FaweCache implements Trimable { public class FaweCache implements Trimable {
public static final char[] EMPTY_CHAR_4096 = new char[4096]; public static final char[] EMPTY_CHAR_4096 = new char[4096];
@ -326,4 +333,16 @@ public class FaweCache implements Trimable {
if (clazz == null) clazz = EndTag.class; if (clazz == null) clazz = EndTag.class;
return new ListTag(clazz, list); return new ListTag(clazz, list);
} }
/*
Thread stuff
*/
public static ThreadPoolExecutor newBlockingExecutor() {
int nThreads = Settings.IMP.QUEUE.PARALLEL_THREADS;
ArrayBlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(nThreads);
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS, queue
, Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy());
}
} }

View File

@ -13,12 +13,12 @@ public class CharFilterBlock implements FilterBlock {
private char[] section; private char[] section;
@Override @Override
public void init(IQueueExtent queue) { public final void init(IQueueExtent queue) {
this.queue = queue; this.queue = queue;
} }
@Override @Override
public void init(int X, int Z, IGetBlocks chunk) { public final void init(int X, int Z, IGetBlocks chunk) {
this.chunk = (CharGetBlocks) chunk; this.chunk = (CharGetBlocks) chunk;
this.X = X; this.X = X;
this.Z = Z; this.Z = Z;
@ -26,68 +26,85 @@ public class CharFilterBlock implements FilterBlock {
this.zz = Z << 4; this.zz = Z << 4;
} }
public void init(char[] section, int layer) { // local
this.section = section; private int layer, index, x, y, z, xx, yy, zz, X, Z;
this.layer = layer;
this.yy = layer << 4; public final void filter(CharGetBlocks blocks, Filter filter) {
for (int layer = 0; layer < 16; layer++) {
if (!blocks.hasSection(layer)) continue;
char[] arr = blocks.sections[layer].get(blocks, layer);
this.section = arr;
this.layer = layer;
this.yy = layer << 4;
for (y = 0, index = 0; y < 16; y++) {
for (z = 0; z < 16; z++) {
for (x = 0; x < 16; x++, index++) {
filter.applyBlock(this);
}
}
}
}
} }
// local
public int layer, index, x, y, z, xx, yy, zz, X, Z;
@Override @Override
public int getX() { public final int getX() {
return xx + x; return xx + x;
} }
@Override @Override
public int getY() { public final int getY() {
return yy + y; return yy + y;
} }
@Override @Override
public int getZ() { public final int getZ() {
return zz + z; return zz + z;
} }
@Override @Override
public int getLocalX() { public final int getLocalX() {
return x; return x;
} }
@Override @Override
public int getLocalY() { public final int getLocalY() {
return y; return y;
} }
@Override @Override
public int getLocalZ() { public final int getLocalZ() {
return z; return z;
} }
@Override @Override
public int getChunkX() { public final int getChunkX() {
return X; return X;
} }
@Override @Override
public int getChunkZ() { public final int getChunkZ() {
return Z; return Z;
} }
@Override public final char getOrdinalChar() {
public int getOrdinal() {
return section[index]; return section[index];
} }
@Override @Override
public BlockState getState() { public final int getOrdinal() {
return section[index];
}
@Override
public final BlockState getState() {
int ordinal = section[index]; int ordinal = section[index];
return BlockTypes.states[ordinal]; return BlockTypes.states[ordinal];
} }
@Override @Override
public BaseBlock getBaseBlock() { public final BaseBlock getBaseBlock() {
BlockState state = getState(); BlockState state = getState();
BlockMaterial material = state.getMaterial(); BlockMaterial material = state.getMaterial();
if (material.hasContainer()) { if (material.hasContainer()) {
@ -98,11 +115,11 @@ public class CharFilterBlock implements FilterBlock {
} }
@Override @Override
public CompoundTag getTag() { public final CompoundTag getTag() {
return null; return null;
} }
public BlockState getOrdinalBelow() { public final BlockState getOrdinalBelow() {
if (y > 0) { if (y > 0) {
return states[section[index - 256]]; return states[section[index - 256]];
} }
@ -114,7 +131,7 @@ public class CharFilterBlock implements FilterBlock {
return BlockTypes.__RESERVED__.getDefaultState(); return BlockTypes.__RESERVED__.getDefaultState();
} }
public BlockState getStateAbove() { public final BlockState getStateAbove() {
if (y < 16) { if (y < 16) {
return states[section[index + 256]]; return states[section[index + 256]];
} }
@ -126,7 +143,7 @@ public class CharFilterBlock implements FilterBlock {
return BlockTypes.__RESERVED__.getDefaultState(); return BlockTypes.__RESERVED__.getDefaultState();
} }
public BlockState getStateRelativeY(int y) { public final BlockState getStateRelativeY(int y) {
int newY = this.y + y; int newY = this.y + y;
int layerAdd = newY >> 4; int layerAdd = newY >> 4;
switch (layerAdd) { switch (layerAdd) {
@ -180,7 +197,7 @@ public class CharFilterBlock implements FilterBlock {
return BlockTypes.__RESERVED__.getDefaultState(); return BlockTypes.__RESERVED__.getDefaultState();
} }
public BlockState getStateRelative(final int x, final int y, final int z) { public final BlockState getStateRelative(final int x, final int y, final int z) {
int newX = this.x + x; int newX = this.x + x;
if (newX >> 4 == 0) { if (newX >> 4 == 0) {
int newZ = this.z + z; int newZ = this.z + z;
@ -189,7 +206,7 @@ public class CharFilterBlock implements FilterBlock {
int layerAdd = newY >> 4; int layerAdd = newY >> 4;
switch (layerAdd) { switch (layerAdd) {
case 0: case 0:
return states[section[this.index + ((y << 8) | (z << 4) | x)]]; return states[section[this.index + ((y << 8) + (z << 4) + x)]];
case 1: case 1:
case 2: case 2:
case 3: case 3:
@ -207,7 +224,7 @@ public class CharFilterBlock implements FilterBlock {
case 15: { case 15: {
int newLayer = layer + layerAdd; int newLayer = layer + layerAdd;
if (newLayer < 16) { if (newLayer < 16) {
int index = this.index + (((y & 15) << 8) | (z << 4) | x); int index = ((newY & 15) << 8) + (newZ << 4) + newX;
return states[chunk.sections[newLayer].get(chunk, newLayer, index)]; return states[chunk.sections[newLayer].get(chunk, newLayer, index)];
} }
break; break;
@ -229,7 +246,7 @@ public class CharFilterBlock implements FilterBlock {
case -15: { case -15: {
int newLayer = layer + layerAdd; int newLayer = layer + layerAdd;
if (newLayer >= 0) { if (newLayer >= 0) {
int index = this.index + (((y & 15) << 8) | (z << 4) | x); int index = ((newY & 15) << 8) + (newZ << 4) + newX;
return states[chunk.sections[newLayer].get(chunk, newLayer, index)]; return states[chunk.sections[newLayer].get(chunk, newLayer, index)];
} }
break; break;

View File

@ -5,11 +5,16 @@ import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockStateHolder;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.function.Supplier;
/** /**
* Represents a chunk in the queue {@link IQueueExtent} * Represents a chunk in the queue {@link IQueueExtent}
* Used for getting and setting blocks / biomes / entities * Used for getting and setting blocks / biomes / entities
*/ */
public interface IChunk extends Trimable { public interface IChunk<T extends Future<T>> extends Trimable, Callable<T> {
/** /**
* Initialize at the location * Initialize at the location
* @param extent * @param extent
@ -22,6 +27,8 @@ public interface IChunk extends Trimable {
int getZ(); int getZ();
/** /**
* If the chunk is a delegate, returns it's paren'ts root * If the chunk is a delegate, returns it's paren'ts root
* @return root IChunk * @return root IChunk
@ -36,16 +43,33 @@ public interface IChunk extends Trimable {
boolean isEmpty(); boolean isEmpty();
/** /**
* Apply the queued async changes to the world * Spend time optimizing for apply<br>
* @return false if applySync needs to run * default behavior: do nothing
*/ */
boolean applyAsync(); default void optimize() {
}
/** /**
* Apply the queued sync changes to the world * Apply the queued changes to the world<br>
* @return true * The future returned may return another future<br>
* To ensure completion keep calling {@link Future#get()} on each result
* @return Futures
*/ */
boolean applySync(); T call();
/**
* Call and join
* @throws ExecutionException
* @throws InterruptedException
*/
default void join() throws ExecutionException, InterruptedException {
T future = call();
while (future != null) {
future = future.get();
}
return;
}
/* set - queues a change */ /* set - queues a change */
boolean setBiome(int x, int y, int z, BiomeType biome); boolean setBiome(int x, int y, int z, BiomeType biome);

View File

@ -5,6 +5,9 @@ import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockStateHolder;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
/** /**
* Delegate for IChunk * Delegate for IChunk
* @param <U> parent class * @param <U> parent class
@ -67,13 +70,13 @@ public interface IDelegateChunk<U extends IChunk> extends IChunk {
} }
@Override @Override
default boolean applySync() { default Future call() {
return getParent().applySync(); return getParent().call();
} }
@Override @Override
default boolean applyAsync() { default void join() throws ExecutionException, InterruptedException {
return getParent().applyAsync(); getParent().join();
} }
@Override @Override

View File

@ -21,4 +21,8 @@ public interface IGetBlocks extends IBlocks, Trimable {
boolean trim(boolean aggressive); boolean trim(boolean aggressive);
void filter(Filter filter, FilterBlock block); void filter(Filter filter, FilterBlock block);
default void optimize() {
}
} }

View File

@ -34,7 +34,7 @@ public interface IQueueExtent extends Flushable, Trimable {
* @param chunk * @param chunk
* @return result * @return result
*/ */
Future<?> submit(IChunk chunk); <T extends Future<T>> T submit(IChunk<T> chunk);
default boolean setBlock(final int x, final int y, final int z, final BlockStateHolder state) { default boolean setBlock(final int x, final int y, final int z, final BlockStateHolder state) {
final IChunk chunk = getCachedChunk(x >> 4, z >> 4); final IChunk chunk = getCachedChunk(x >> 4, z >> 4);

View File

@ -12,4 +12,8 @@ public interface ISetBlocks extends IBlocks {
boolean setBlock(int x, int y, int z, BlockStateHolder holder); boolean setBlock(int x, int y, int z, BlockStateHolder holder);
boolean isEmpty(); boolean isEmpty();
default void optimize() {
}
} }

View File

@ -1,6 +1,6 @@
package com.boydti.fawe.beta.implementation; package com.boydti.fawe.beta.implementation;
import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweCache;
import com.boydti.fawe.beta.Filter; import com.boydti.fawe.beta.Filter;
import com.boydti.fawe.beta.FilterBlock; import com.boydti.fawe.beta.FilterBlock;
import com.boydti.fawe.beta.IChunk; import com.boydti.fawe.beta.IChunk;
@ -8,9 +8,9 @@ import com.boydti.fawe.beta.IQueueExtent;
import com.boydti.fawe.beta.Trimable; import com.boydti.fawe.beta.Trimable;
import com.boydti.fawe.config.Settings; import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.collection.IterableThreadLocal; import com.boydti.fawe.object.collection.IterableThreadLocal;
import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.MemUtil;
import com.boydti.fawe.util.TaskManager;
import com.boydti.fawe.wrappers.WorldWrapper; import com.boydti.fawe.wrappers.WorldWrapper;
import com.google.common.util.concurrent.Futures;
import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
@ -20,32 +20,36 @@ import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask; import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
/** /**
* Class which handles all the queues {@link IQueueExtent} * Class which handles all the queues {@link IQueueExtent}
*/ */
public abstract class QueueHandler implements Trimable { public abstract class QueueHandler implements Trimable {
private Map<World, WeakReference<WorldChunkCache>> chunkCache = new HashMap<>(); private ForkJoinPool forkJoinPoolPrimary = new ForkJoinPool();
private ForkJoinPool forkJoinPoolSecondary = new ForkJoinPool();
private ThreadPoolExecutor blockingExecutor = FaweCache.newBlockingExecutor();
private ConcurrentLinkedQueue<Runnable> syncTasks = new ConcurrentLinkedQueue();
private IterableThreadLocal<IQueueExtent> pool = new IterableThreadLocal<IQueueExtent>() { private Map<World, WeakReference<WorldChunkCache>> chunkCache = new HashMap<>();
private IterableThreadLocal<IQueueExtent> queuePool = new IterableThreadLocal<IQueueExtent>() {
@Override @Override
public IQueueExtent init() { public IQueueExtent init() {
return create(); return create();
} }
}; };
public Future<?> submit(IChunk chunk) { public <T extends Future<T>> T submit(IChunk<T> chunk) {
if (Fawe.isMainThread()) { if (MemUtil.isMemoryFree()) {
if (!chunk.applyAsync()) { // return (T) forkJoinPoolSecondary.submit(chunk);
chunk.applySync();
}
return null;
} }
// TODO return future return (T) blockingExecutor.submit(chunk);
return null;
} }
/** /**
@ -73,7 +77,7 @@ public abstract class QueueHandler implements Trimable {
public abstract IQueueExtent create(); public abstract IQueueExtent create();
public IQueueExtent getQueue(World world) { public IQueueExtent getQueue(World world) {
IQueueExtent queue = pool.get(); IQueueExtent queue = queuePool.get();
queue.init(getOrCreate(world)); queue.init(getOrCreate(world));
return queue; return queue;
} }
@ -103,62 +107,63 @@ public abstract class QueueHandler implements Trimable {
final Iterator<BlockVector2> chunksIter = chunks.iterator(); final Iterator<BlockVector2> chunksIter = chunks.iterator();
// Get a pool, to operate on the chunks in parallel // Get a pool, to operate on the chunks in parallel
final ForkJoinPool pool = TaskManager.IMP.getPublicForkJoinPool();
final int size = Math.min(chunks.size(), Settings.IMP.QUEUE.PARALLEL_THREADS); final int size = Math.min(chunks.size(), Settings.IMP.QUEUE.PARALLEL_THREADS);
final ForkJoinTask[] tasks = new ForkJoinTask[size]; ForkJoinTask[] tasks = new ForkJoinTask[size];
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
tasks[i] = pool.submit(new Runnable() { tasks[i] = forkJoinPoolPrimary.submit(new Runnable() {
@Override @Override
public void run() { public void run() {
Filter newFilter = filter.fork(); Filter newFilter = filter.fork();
// Create a chunk that we will reuse/reset for each operation // Create a chunk that we will reuse/reset for each operation
IQueueExtent queue = getQueue(world); IQueueExtent queue = getQueue(world);
FilterBlock block = null; synchronized (queue) {
FilterBlock block = null;
while (true) { while (true) {
// Get the next chunk pos // Get the next chunk pos
final BlockVector2 pos; final BlockVector2 pos;
synchronized (chunksIter) { synchronized (chunksIter) {
if (!chunksIter.hasNext()) return; if (!chunksIter.hasNext()) break;
pos = chunksIter.next(); pos = chunksIter.next();
}
final int X = pos.getX();
final int Z = pos.getZ();
// TODO create full
IChunk chunk = queue.getCachedChunk(X, Z);
// Initialize
chunk.init(queue, X, Z);
try {
if (!newFilter.appliesChunk(X, Z)) {
continue;
} }
chunk = newFilter.applyChunk(chunk); final int X = pos.getX();
final int Z = pos.getZ();
IChunk chunk = queue.getCachedChunk(X, Z);
// Initialize
chunk.init(queue, X, Z);
try {
if (!newFilter.appliesChunk(X, Z)) {
continue;
}
chunk = newFilter.applyChunk(chunk);
if (chunk == null) continue; if (chunk == null) continue;
if (block == null) block = queue.initFilterBlock(); if (block == null) block = queue.initFilterBlock();
chunk.filter(newFilter, block); chunk.filter(newFilter, block);
newFilter.finishChunk(chunk); newFilter.finishChunk(chunk);
queue.submit(chunk); queue.submit(chunk);
} finally } finally {
{ if (filter != newFilter) {
if (filter != newFilter) { synchronized (filter) {
synchronized (filter) { newFilter.join(filter);
newFilter.join(filter); }
} }
} }
} }
queue.flush();
} }
} }
}); });
} }
// Join filters
// Join the tasks for (int i = 0; i < tasks.length; i++) {
for (final ForkJoinTask task : tasks) { ForkJoinTask task = tasks[i];
task.join(); if (task != null) {
task.quietlyJoin();
}
} }
} }
} }

View File

@ -5,17 +5,12 @@ import com.boydti.fawe.beta.IChunk;
import com.boydti.fawe.beta.IQueueExtent; import com.boydti.fawe.beta.IQueueExtent;
import com.boydti.fawe.beta.implementation.holder.ReferenceChunk; import com.boydti.fawe.beta.implementation.holder.ReferenceChunk;
import com.boydti.fawe.config.Settings; import com.boydti.fawe.config.Settings;
import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.MemUtil; import com.boydti.fawe.util.MemUtil;
import com.boydti.fawe.util.SetQueue; import com.google.common.util.concurrent.Futures;
import com.boydti.fawe.util.TaskManager;
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
@ -29,6 +24,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
public abstract class SingleThreadQueueExtent implements IQueueExtent { public abstract class SingleThreadQueueExtent implements IQueueExtent {
private WorldChunkCache cache; private WorldChunkCache cache;
private Thread currentThread; private Thread currentThread;
private ConcurrentLinkedQueue<Future> submissions = new ConcurrentLinkedQueue<>();
/** /**
* Safety check to ensure that the thread being used matches the one being initialized on * Safety check to ensure that the thread being used matches the one being initialized on
@ -66,7 +62,7 @@ public abstract class SingleThreadQueueExtent implements IQueueExtent {
*/ */
@Override @Override
public synchronized void init(final WorldChunkCache cache) { public synchronized void init(final WorldChunkCache cache) {
if (cache != null) { if (this.cache != null) {
reset(); reset();
} }
currentThread = Thread.currentThread(); currentThread = Thread.currentThread();
@ -83,19 +79,17 @@ public abstract class SingleThreadQueueExtent implements IQueueExtent {
private static final ConcurrentLinkedQueue<IChunk> CHUNK_POOL = new ConcurrentLinkedQueue<>(); private static final ConcurrentLinkedQueue<IChunk> CHUNK_POOL = new ConcurrentLinkedQueue<>();
@Override @Override
public Future<?> submit(final IChunk chunk) { public <T extends Future<T>> T submit(final IChunk<T> chunk) {
if (chunk.isEmpty()) { if (chunk.isEmpty()) {
CHUNK_POOL.add(chunk); CHUNK_POOL.add(chunk);
return null; return (T) (Future) Futures.immediateFuture(null);
} }
if (Fawe.isMainThread()) { if (Fawe.isMainThread()) {
if (!chunk.applyAsync()) { return chunk.call();
chunk.applySync();
}
return null;
} }
QueueHandler handler = Fawe.get().getQueueHandler();
return handler.submit(chunk); return Fawe.get().getQueueHandler().submit(chunk);
} }
@Override @Override
@ -107,6 +101,13 @@ public abstract class SingleThreadQueueExtent implements IQueueExtent {
lastPair = Long.MAX_VALUE; lastPair = Long.MAX_VALUE;
return chunks.isEmpty(); return chunks.isEmpty();
} }
if (!submissions.isEmpty()) {
if (aggressive) {
pollSubmissions(0, aggressive);
} else {
pollSubmissions(Settings.IMP.QUEUE.PARALLEL_THREADS, aggressive);
}
}
synchronized (this) { synchronized (this) {
return currentThread == null; return currentThread == null;
} }
@ -121,7 +122,10 @@ public abstract class SingleThreadQueueExtent implements IQueueExtent {
*/ */
private IChunk poolOrCreate(final int X, final int Z) { private IChunk poolOrCreate(final int X, final int Z) {
IChunk next = CHUNK_POOL.poll(); IChunk next = CHUNK_POOL.poll();
if (next == null) next = create(false); if (next == null) {
System.out.println("Create");
next = create(false);
}
next.init(this, X, Z); next.init(this, X, Z);
return next; return next;
} }
@ -145,10 +149,19 @@ public abstract class SingleThreadQueueExtent implements IQueueExtent {
checkThread(); checkThread();
final int size = chunks.size(); final int size = chunks.size();
if (size > Settings.IMP.QUEUE.TARGET_SIZE || MemUtil.isMemoryLimited()) { boolean lowMem = MemUtil.isMemoryLimited();
if (size > Settings.IMP.QUEUE.PARALLEL_THREADS * 2 + 16) { if (lowMem || size > Settings.IMP.QUEUE.TARGET_SIZE) {
chunk = chunks.removeFirst(); chunk = chunks.removeFirst();
submit(chunk); Future future = submit(chunk);
if (future != null && !future.isDone()) {
int targetSize;
if (lowMem) {
targetSize = Settings.IMP.QUEUE.PARALLEL_THREADS;
} else {
targetSize = Settings.IMP.QUEUE.TARGET_SIZE;
}
pollSubmissions(targetSize, true);
submissions.add(future);
} }
} }
chunk = poolOrCreate(X, Z); chunk = poolOrCreate(X, Z);
@ -161,27 +174,59 @@ public abstract class SingleThreadQueueExtent implements IQueueExtent {
return chunk; return chunk;
} }
private void pollSubmissions(int targetSize, boolean aggressive) {
int overflow = submissions.size() - targetSize;
if (aggressive) {
for (int i = 0; i < overflow; i++) {
Future first = submissions.poll();
try {
while ((first = (Future) first.get()) != null) ;
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
} else {
for (int i = 0; i < overflow; i++) {
Future next = submissions.peek();
while (next != null) {
if (next.isDone()) {
try {
next = (Future) next.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
} else {
return;
}
}
submissions.poll();
}
}
}
@Override @Override
public synchronized void flush() { public synchronized void flush() {
checkThread(); checkThread();
if (!chunks.isEmpty()) { if (!chunks.isEmpty()) {
final Future[] tasks = new ForkJoinTask[chunks.size()]; if (MemUtil.isMemoryLimited()) {
int i = 0; for (IChunk chunk : chunks.values()) {
for (final IChunk chunk : chunks.values()) { Future future = submit(chunk);
tasks[i++] = submit(chunk); if (future != null && !future.isDone()) {
} pollSubmissions(Settings.IMP.QUEUE.PARALLEL_THREADS, true);
chunks.clear(); submissions.add(future);
for (final Future task : tasks) { }
if (task != null) { }
try { } else {
task.get(); for (final IChunk chunk : chunks.values()) {
} catch (InterruptedException | ExecutionException e) { Future future = submit(chunk);
e.printStackTrace(); if (future != null && !future.isDone()) {
throw new RuntimeException(e); submissions.add(future);
} }
} }
} }
chunks.clear();
} }
pollSubmissions(0, true);
reset(); reset();
} }
} }

View File

@ -22,18 +22,7 @@ public abstract class CharGetBlocks extends CharBlocks implements IGetBlocks {
@Override @Override
public void filter(Filter filter, FilterBlock block) { public void filter(Filter filter, FilterBlock block) {
CharFilterBlock b = (CharFilterBlock) block; CharFilterBlock b = (CharFilterBlock) block;
for (int layer = 0; layer < 16; layer++) { b.filter(this, filter);
if (!hasSection(layer)) continue;
char[] arr = sections[layer].get(this, layer);
b.init(arr, layer);
for (b.y = 0, b.index = 0; b.y < 16; b.y++) {
for (b.z = 0; b.z < 16; b.z++) {
for (b.x = 0; b.x < 16; b.x++, b.index++) {
filter.applyBlock(b);
}
}
}
}
} }
@Override @Override

View File

@ -1,10 +1,8 @@
package com.boydti.fawe.beta.implementation.holder; package com.boydti.fawe.beta.implementation.holder;
import com.boydti.fawe.beta.CharFilterBlock;
import com.boydti.fawe.beta.Filter; import com.boydti.fawe.beta.Filter;
import com.boydti.fawe.beta.FilterBlock; import com.boydti.fawe.beta.FilterBlock;
import com.boydti.fawe.beta.IQueueExtent; import com.boydti.fawe.beta.IQueueExtent;
import com.boydti.fawe.beta.implementation.blocks.CharGetBlocks;
import com.boydti.fawe.beta.implementation.blocks.CharSetBlocks; import com.boydti.fawe.beta.implementation.blocks.CharSetBlocks;
import com.boydti.fawe.beta.IChunk; import com.boydti.fawe.beta.IChunk;
import com.boydti.fawe.beta.IGetBlocks; import com.boydti.fawe.beta.IGetBlocks;
@ -22,7 +20,7 @@ import java.util.function.Supplier;
/** /**
* Abstract IChunk class that implements basic get/set blocks * Abstract IChunk class that implements basic get/set blocks
*/ */
public abstract class ChunkHolder<T> implements IChunk, Supplier<IGetBlocks> { public abstract class ChunkHolder implements IChunk, Supplier<IGetBlocks> {
private IGetBlocks get; private IGetBlocks get;
private ISetBlocks set; private ISetBlocks set;
private IBlockDelegate delegate; private IBlockDelegate delegate;
@ -40,7 +38,7 @@ public abstract class ChunkHolder<T> implements IChunk, Supplier<IGetBlocks> {
@Override @Override
public void filter(Filter filter, FilterBlock block) { public void filter(Filter filter, FilterBlock block) {
block.init(X, Z, get); block.init(X, Z, get);
IGetBlocks get = cachedGet(); IGetBlocks get = getOrCreateGet();
get.filter(filter, block); get.filter(filter, block);
} }
@ -73,12 +71,12 @@ public abstract class ChunkHolder<T> implements IChunk, Supplier<IGetBlocks> {
return set == null || set.isEmpty(); return set == null || set.isEmpty();
} }
public final IGetBlocks cachedGet() { public final IGetBlocks getOrCreateGet() {
if (get == null) get = newGet(); if (get == null) get = newGet();
return get; return get;
} }
public final ISetBlocks cachedSet() { public final ISetBlocks getOrCreateSet() {
if (set == null) set = set(); if (set == null) set = set();
return set; return set;
} }
@ -95,6 +93,13 @@ public abstract class ChunkHolder<T> implements IChunk, Supplier<IGetBlocks> {
return get(); return get();
} }
@Override
public void optimize() {
if (set != null) {
set.optimize();
}
}
@Override @Override
public void init(IQueueExtent extent, final int X, final int Z) { public void init(IQueueExtent extent, final int X, final int Z) {
this.extent = extent; this.extent = extent;
@ -163,35 +168,35 @@ public abstract class ChunkHolder<T> implements IChunk, Supplier<IGetBlocks> {
public static final IBlockDelegate NULL = new IBlockDelegate() { public static final IBlockDelegate NULL = new IBlockDelegate() {
@Override @Override
public boolean setBiome(final ChunkHolder chunk, final int x, final int y, final int z, final BiomeType biome) { public boolean setBiome(final ChunkHolder chunk, final int x, final int y, final int z, final BiomeType biome) {
chunk.cachedSet(); chunk.getOrCreateSet();
chunk.delegate = SET; chunk.delegate = SET;
return chunk.setBiome(x, y, z, biome); return chunk.setBiome(x, y, z, biome);
} }
@Override @Override
public boolean setBlock(final ChunkHolder chunk, final int x, final int y, final int z, final BlockStateHolder block) { public boolean setBlock(final ChunkHolder chunk, final int x, final int y, final int z, final BlockStateHolder block) {
chunk.cachedSet(); chunk.getOrCreateSet();
chunk.delegate = SET; chunk.delegate = SET;
return chunk.setBlock(x, y, z, block); return chunk.setBlock(x, y, z, block);
} }
@Override @Override
public BiomeType getBiome(final ChunkHolder chunk, final int x, final int z) { public BiomeType getBiome(final ChunkHolder chunk, final int x, final int z) {
chunk.cachedGet(); chunk.getOrCreateGet();
chunk.delegate = GET; chunk.delegate = GET;
return chunk.getBiome(x, z); return chunk.getBiome(x, z);
} }
@Override @Override
public BlockState getBlock(final ChunkHolder chunk, final int x, final int y, final int z) { public BlockState getBlock(final ChunkHolder chunk, final int x, final int y, final int z) {
chunk.cachedGet(); chunk.getOrCreateGet();
chunk.delegate = GET; chunk.delegate = GET;
return chunk.getBlock(x, y, z); return chunk.getBlock(x, y, z);
} }
@Override @Override
public BaseBlock getFullBlock(final ChunkHolder chunk, final int x, final int y, final int z) { public BaseBlock getFullBlock(final ChunkHolder chunk, final int x, final int y, final int z) {
chunk.cachedGet(); chunk.getOrCreateGet();
chunk.delegate = GET; chunk.delegate = GET;
return chunk.getFullBlock(x, y, z); return chunk.getFullBlock(x, y, z);
} }
@ -200,14 +205,14 @@ public abstract class ChunkHolder<T> implements IChunk, Supplier<IGetBlocks> {
public static final IBlockDelegate GET = new IBlockDelegate() { public static final IBlockDelegate GET = new IBlockDelegate() {
@Override @Override
public boolean setBiome(final ChunkHolder chunk, final int x, final int y, final int z, final BiomeType biome) { public boolean setBiome(final ChunkHolder chunk, final int x, final int y, final int z, final BiomeType biome) {
chunk.cachedSet(); chunk.getOrCreateSet();
chunk.delegate = BOTH; chunk.delegate = BOTH;
return chunk.setBiome(x, y, z, biome); return chunk.setBiome(x, y, z, biome);
} }
@Override @Override
public boolean setBlock(final ChunkHolder chunk, final int x, final int y, final int z, final BlockStateHolder block) { public boolean setBlock(final ChunkHolder chunk, final int x, final int y, final int z, final BlockStateHolder block) {
chunk.cachedSet(); chunk.getOrCreateSet();
chunk.delegate = BOTH; chunk.delegate = BOTH;
return chunk.setBlock(x, y, z, block); return chunk.setBlock(x, y, z, block);
} }
@ -241,21 +246,21 @@ public abstract class ChunkHolder<T> implements IChunk, Supplier<IGetBlocks> {
@Override @Override
public BiomeType getBiome(final ChunkHolder chunk, final int x, final int z) { public BiomeType getBiome(final ChunkHolder chunk, final int x, final int z) {
chunk.cachedGet(); chunk.getOrCreateGet();
chunk.delegate = BOTH; chunk.delegate = BOTH;
return chunk.getBiome(x, z); return chunk.getBiome(x, z);
} }
@Override @Override
public BlockState getBlock(final ChunkHolder chunk, final int x, final int y, final int z) { public BlockState getBlock(final ChunkHolder chunk, final int x, final int y, final int z) {
chunk.cachedGet(); chunk.getOrCreateGet();
chunk.delegate = BOTH; chunk.delegate = BOTH;
return chunk.getBlock(x, y, z); return chunk.getBlock(x, y, z);
} }
@Override @Override
public BaseBlock getFullBlock(final ChunkHolder chunk, final int x, final int y, final int z) { public BaseBlock getFullBlock(final ChunkHolder chunk, final int x, final int y, final int z) {
chunk.cachedGet(); chunk.getOrCreateGet();
chunk.delegate = BOTH; chunk.delegate = BOTH;
return chunk.getFullBlock(x, y, z); return chunk.getFullBlock(x, y, z);
} }

View File

@ -281,7 +281,10 @@ public class RegionCommands extends MethodCommands {
QueueHandler queueHandler = Fawe.get().getQueueHandler(); QueueHandler queueHandler = Fawe.get().getQueueHandler();
World world = player.getWorld(); World world = player.getWorld();
CountFilter filter = new CountFilter(); CountFilter filter = new CountFilter();
long start = System.currentTimeMillis();
queueHandler.apply(world, region, filter); queueHandler.apply(world, region, filter);
long diff = System.currentTimeMillis() - start;
System.out.println(diff);
} }
@Command( @Command(

View File

@ -62,6 +62,10 @@ import java.util.stream.Stream;
public final class BlockTypes { public final class BlockTypes {
// Doesn't really matter what the hardcoded values are, as FAWE will update it on load // Doesn't really matter what the hardcoded values are, as FAWE will update it on load
@Nullable public static final BlockType __RESERVED__ = null; @Nullable public static final BlockType __RESERVED__ = null;
@Nullable public static final BlockType AIR = null;
@Nullable public static final BlockType CAVE_AIR = null;
@Nullable public static final BlockType VOID_AIR = null;
@Nullable public static final BlockType ACACIA_BUTTON = null; @Nullable public static final BlockType ACACIA_BUTTON = null;
@Nullable public static final BlockType ACACIA_DOOR = null; @Nullable public static final BlockType ACACIA_DOOR = null;
@Nullable public static final BlockType ACACIA_FENCE = null; @Nullable public static final BlockType ACACIA_FENCE = null;
@ -76,7 +80,6 @@ public final class BlockTypes {
@Nullable public static final BlockType ACACIA_TRAPDOOR = null; @Nullable public static final BlockType ACACIA_TRAPDOOR = null;
@Nullable public static final BlockType ACACIA_WOOD = null; @Nullable public static final BlockType ACACIA_WOOD = null;
@Nullable public static final BlockType ACTIVATOR_RAIL = null; @Nullable public static final BlockType ACTIVATOR_RAIL = null;
@Nullable public static final BlockType AIR = null;
@Nullable public static final BlockType ALLIUM = null; @Nullable public static final BlockType ALLIUM = null;
@Nullable public static final BlockType ANDESITE = null; @Nullable public static final BlockType ANDESITE = null;
@Nullable public static final BlockType ANVIL = null; @Nullable public static final BlockType ANVIL = null;
@ -160,7 +163,6 @@ public final class BlockTypes {
@Nullable public static final BlockType CARROTS = null; @Nullable public static final BlockType CARROTS = null;
@Nullable public static final BlockType CARVED_PUMPKIN = null; @Nullable public static final BlockType CARVED_PUMPKIN = null;
@Nullable public static final BlockType CAULDRON = null; @Nullable public static final BlockType CAULDRON = null;
@Nullable public static final BlockType CAVE_AIR = null;
@Nullable public static final BlockType CHAIN_COMMAND_BLOCK = null; @Nullable public static final BlockType CHAIN_COMMAND_BLOCK = null;
@Nullable public static final BlockType CHEST = null; @Nullable public static final BlockType CHEST = null;
@Nullable public static final BlockType CHIPPED_ANVIL = null; @Nullable public static final BlockType CHIPPED_ANVIL = null;
@ -625,7 +627,6 @@ public final class BlockTypes {
@Nullable public static final BlockType TUBE_CORAL_WALL_FAN = null; @Nullable public static final BlockType TUBE_CORAL_WALL_FAN = null;
@Nullable public static final BlockType TURTLE_EGG = null; @Nullable public static final BlockType TURTLE_EGG = null;
@Nullable public static final BlockType VINE = null; @Nullable public static final BlockType VINE = null;
@Nullable public static final BlockType VOID_AIR = null;
@Nullable public static final BlockType WALL_SIGN = null; @Nullable public static final BlockType WALL_SIGN = null;
@Nullable public static final BlockType WALL_TORCH = null; @Nullable public static final BlockType WALL_TORCH = null;
@Nullable public static final BlockType WATER = null; @Nullable public static final BlockType WATER = null;