Skip to content

Commit

Permalink
Improve ArbitraryShape caching (#2325)
Browse files Browse the repository at this point in the history
* Improve ArbitraryShape caching

Every point is now only evaluated once.

* Defer cache initialization until it is clear that we need it.

* Defer pattern evaluation until cache miss
  • Loading branch information
TomyLobo authored Jun 18, 2023
1 parent 5b88c31 commit 6494dbc
Showing 1 changed file with 70 additions and 80 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,27 +31,33 @@
* {@link #getMaterial} method.
*/
public abstract class ArbitraryShape {
/**
* This Object instance is used for cache entries that are known to be outside the shape.
*/
private static final Object OUTSIDE = new Object();

protected final Region extent;

private final int cacheOffsetX;
private final int cacheOffsetY;
private final int cacheOffsetZ;
@SuppressWarnings("FieldCanBeLocal")
private final int cacheSizeX;
private final int cacheSizeY;
private final int cacheSizeZ;

/**
* Cache for expression results.
*
* <p>
* Cache entries:
* 0 = unknown
* -1 = outside
* 1 = inside
* </p>
* <p>Possible cache entries:
* <ul>
* <li>null = unknown</li>
* <li>ArbitraryShape.OUTSIDE = outside</li>
* <li>any BaseBlock = inside</li>
* <li>anything else = (invalid, not used)</li>
* </ul>
*/
private final byte[] cache;
private Object[] cache;

public ArbitraryShape(Region extent) {
this.extent = extent;
Expand All @@ -66,8 +72,6 @@ public ArbitraryShape(Region extent) {
cacheSizeX = max.getX() - cacheOffsetX + 2;
cacheSizeY = max.getY() - cacheOffsetY + 2;
cacheSizeZ = max.getZ() - cacheOffsetZ + 2;

cache = new byte[cacheSizeX * cacheSizeY * cacheSizeZ];
}

protected Region getExtent() {
Expand Down Expand Up @@ -95,90 +99,76 @@ protected Region getExtent() {
* @throws MaxChangedBlocksException if the maximum blocks changed is exceeded
*/
public int generate(EditSession editSession, Pattern pattern, boolean hollow) throws MaxChangedBlocksException {
if (hollow && cache == null) {
cache = new Object[cacheSizeX * cacheSizeY * cacheSizeZ];
}

int affected = 0;

for (BlockVector3 position : getExtent()) {
int x = position.getBlockX();
int y = position.getBlockY();
int z = position.getBlockZ();

if (!hollow) {
BaseBlock material = getMaterial(x, y, z, pattern.applyBlock(position));
if (material != null && editSession.setBlock(position, material)) {
++affected;
}

continue;
}
final BaseBlock material = getMaterial(position, pattern, hollow);

BaseBlock material = getMaterial(x, y, z, pattern.applyBlock(position));
if (material == null) {
final int index = (y - cacheOffsetY) + (z - cacheOffsetZ) * cacheSizeY + (x - cacheOffsetX) * cacheSizeY * cacheSizeZ;
cache[index] = -1;
continue;
}

boolean draw = false;
do {
if (!isInsideCached(x + 1, y, z, pattern)) {
draw = true;
break;
}
if (!isInsideCached(x - 1, y, z, pattern)) {
draw = true;
break;
}
if (!isInsideCached(x, y, z + 1, pattern)) {
draw = true;
break;
}
if (!isInsideCached(x, y, z - 1, pattern)) {
draw = true;
break;
}
if (!isInsideCached(x, y + 1, z, pattern)) {
draw = true;
break;
}
if (!isInsideCached(x, y - 1, z, pattern)) {
draw = true;
break;
}
} while (false);

if (!draw) {
continue;
}

if (editSession.setBlock(position, material)) {
if (material != null && editSession.setBlock(position, material)) {
++affected;
}
}

return affected;
}

private boolean isInsideCached(int x, int y, int z, Pattern pattern) {
final int index = (y - cacheOffsetY) + (z - cacheOffsetZ) * cacheSizeY + (x - cacheOffsetX) * cacheSizeY * cacheSizeZ;
private BaseBlock getMaterial(BlockVector3 position, Pattern pattern, boolean hollow) {
int x = position.getBlockX();
int y = position.getBlockY();
int z = position.getBlockZ();

if (!hollow) {
return getMaterial(x, y, z, pattern.applyBlock(position));
}

switch (cache[index]) {
case 0:
BaseBlock mat = getMaterial(x, y, z, pattern.applyBlock(BlockVector3.at(x, y, z)));
if (mat == null) {
cache[index] = -1;
return false;
}
cache[index] = 1;
return true;

case -1:
// outside
return false;

default:
// inside
return true;
final Object cacheEntry = getMaterialCached(x, y, z, pattern);
if (cacheEntry == OUTSIDE) {
return null;
}

final BaseBlock material = (BaseBlock) cacheEntry;

if (isOutsideCached(x + 1, y, z, pattern)) {
return material;
}
if (isOutsideCached(x - 1, y, z, pattern)) {
return material;
}
if (isOutsideCached(x, y, z + 1, pattern)) {
return material;
}
if (isOutsideCached(x, y, z - 1, pattern)) {
return material;
}
if (isOutsideCached(x, y + 1, z, pattern)) {
return material;
}
if (isOutsideCached(x, y - 1, z, pattern)) {
return material;
}

return null;
}

private boolean isOutsideCached(int x, int y, int z, Pattern pattern) {
return getMaterialCached(x, y, z, pattern) == OUTSIDE;
}

private Object getMaterialCached(int x, int y, int z, Pattern pattern) {
final int index = (y - cacheOffsetY) + (z - cacheOffsetZ) * cacheSizeY + (x - cacheOffsetX) * cacheSizeY * cacheSizeZ;
final Object cacheEntry = cache[index];
if (cacheEntry == null) {
final BaseBlock material = getMaterial(x, y, z, pattern.applyBlock(BlockVector3.at(x, y, z)));
if (material == null) {
return cache[index] = OUTSIDE;
} else {
return cache[index] = material;
}
}
return cacheEntry;
}
}

0 comments on commit 6494dbc

Please sign in to comment.