From 84515316a7098562f357a1bcf9025a406e9b7443 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Fri, 22 Mar 2024 13:42:47 -0500 Subject: [PATCH] Smart lighting - Do not delete light volume memory when invalidated. Keep the block around, and then use it again later when we collect more light - Do not allocate fresh memory blocks when growing a light volume. Turns out self-blitting is perfectly fine since we always start from the volume's origin - Inline BoxSet back into light volume. The precise tracking it did is no longer relevant now that the embedding api has been simplified - Light volumes now directly --- .../engine/embed/EmbeddedEnvironment.java | 2 +- .../engine/embed/EmbeddedLightVolume.java | 121 ++++++++---- .../flywheel/backend/util/BoxSet.java | 174 ------------------ 3 files changed, 88 insertions(+), 209 deletions(-) delete mode 100644 src/main/java/com/jozufozu/flywheel/backend/util/BoxSet.java diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/embed/EmbeddedEnvironment.java b/src/main/java/com/jozufozu/flywheel/backend/engine/embed/EmbeddedEnvironment.java index 6fe36da84..11eeb0302 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/embed/EmbeddedEnvironment.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/embed/EmbeddedEnvironment.java @@ -74,7 +74,7 @@ public class EmbeddedEnvironment extends AtomicReferenceCounted implements Envir @Override public void invalidateLight() { - lightVolume.delete(); + lightVolume.clear(); } @Override diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/embed/EmbeddedLightVolume.java b/src/main/java/com/jozufozu/flywheel/backend/engine/embed/EmbeddedLightVolume.java index 0d6a9773d..d2300e4d1 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/embed/EmbeddedLightVolume.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/embed/EmbeddedLightVolume.java @@ -3,7 +3,6 @@ package com.jozufozu.flywheel.backend.engine.embed; import org.jetbrains.annotations.Nullable; import org.lwjgl.system.MemoryUtil; -import com.jozufozu.flywheel.backend.util.BoxSet; import com.jozufozu.flywheel.lib.memory.MemoryBlock; import net.minecraft.core.BlockPos; @@ -13,20 +12,28 @@ import net.minecraft.world.level.LightLayer; public class EmbeddedLightVolume { public static final long STRIDE = Short.BYTES; - private final BoxSet wantedCoords = new BoxSet(); + private int minX; + private int minY; + private int minZ; + private int maxX; + private int maxY; + private int maxZ; private final BlockPos.MutableBlockPos scratchPos = new BlockPos.MutableBlockPos(); @Nullable protected MemoryBlock memoryBlock; + protected boolean empty = true; public boolean empty() { - return memoryBlock == null; + return empty; } public void collect(BlockAndTintGetter level, int minX, int minY, int minZ, int sizeX, int sizeY, int sizeZ) { maybeExpandForBox(minX, minY, minZ, sizeX, sizeY, sizeZ); + empty = false; + for (int z = minZ; z < minZ + sizeZ; z++) { for (int y = minY; y < minY + sizeY; y++) { for (int x = minX; x < minX + sizeX; x++) { @@ -46,47 +53,85 @@ public class EmbeddedLightVolume { MemoryUtil.memPutShort(ptr, (short) ((block << 4) | sky << 12)); } - private void maybeExpandForBox(int minX, int minY, int minZ, int sizeX, int sizeY, int sizeZ) { - int oldMinX = wantedCoords.minX(); - int oldMinY = wantedCoords.minY(); - int oldMinZ = wantedCoords.minZ(); - int oldSizeX = wantedCoords.sizeX(); - int oldSizeY = wantedCoords.sizeY(); - int oldSizeZ = wantedCoords.sizeZ(); + private void maybeExpandForBox(int x, int y, int z, int sizeX, int sizeY, int sizeZ) { + if (empty || memoryBlock == null) { + // We're either brand new or recently #clear'd, + // so none of the previous min/max values have any meaning. + this.minX = x; + this.minY = y; + this.minZ = z; + this.maxX = x + sizeX; + this.maxY = y + sizeY; + this.maxZ = z + sizeZ; - var grew = wantedCoords.add(minX, minY, minZ, sizeX, sizeY, sizeZ); - - if (memoryBlock == null) { int volume = sizeX * sizeY * sizeZ; + long neededSize = volume * STRIDE; - memoryBlock = MemoryBlock.malloc(volume * STRIDE); + if (memoryBlock == null) { + memoryBlock = MemoryBlock.malloc(neededSize); + } else if (memoryBlock.size() < neededSize) { + // There's some memory left over from before the last #clear, + // but not enough to hold this initial box. Need to grow the block. + memoryBlock.realloc(neededSize); + } + // else: we have enough memory left over to hold this box, nothing to do! return; } - if (!grew) { + int oldMinX = this.minX; + int oldMinY = this.minY; + int oldMinZ = this.minZ; + int oldSizeX = this.sizeX(); + int oldSizeY = this.sizeY(); + int oldSizeZ = this.sizeZ(); + boolean changed = false; + + if (x < this.minX) { + this.minX = x; + changed = true; + } + if (y < this.minY) { + this.minY = y; + changed = true; + } + if (z < this.minZ) { + this.minZ = z; + changed = true; + } + if (x + sizeX > this.maxX) { + this.maxX = x + sizeX; + changed = true; + } + if (y + sizeY > this.maxY) { + this.maxY = y + sizeY; + changed = true; + } + if (z + sizeZ > this.maxZ) { + this.maxZ = z + sizeZ; + changed = true; + } + + if (!changed) { return; } - int newVolume = wantedCoords.volume(); + int volume = volume(); - MemoryBlock newBlock = MemoryBlock.malloc(newVolume * STRIDE); + memoryBlock = memoryBlock.realloc(volume * STRIDE); - int xOff = oldMinX - wantedCoords.minX(); - int yOff = oldMinY - wantedCoords.minY(); - int zOff = oldMinZ - wantedCoords.minZ(); + int xOff = oldMinX - minX; + int yOff = oldMinY - minY; + int zOff = oldMinZ - minZ; - blit(memoryBlock, 0, 0, 0, oldSizeX, oldSizeY, newBlock, xOff, yOff, zOff, wantedCoords.sizeX(), wantedCoords.sizeY(), oldSizeX, oldSizeY, oldSizeZ); - - memoryBlock.free(); - memoryBlock = newBlock; + blit(memoryBlock.ptr(), 0, 0, 0, oldSizeX, oldSizeY, memoryBlock.ptr(), xOff, yOff, zOff, sizeX(), sizeY(), oldSizeX, oldSizeY, oldSizeZ); } - public static void blit(MemoryBlock src, int srcX, int srcY, int srcZ, int srcSizeX, int srcSizeY, MemoryBlock dst, int dstX, int dstY, int dstZ, int dstSizeX, int dstSizeY, int sizeX, int sizeY, int sizeZ) { + public static void blit(long src, int srcX, int srcY, int srcZ, int srcSizeX, int srcSizeY, long dst, int dstX, int dstY, int dstZ, int dstSizeX, int dstSizeY, int sizeX, int sizeY, int sizeZ) { for (int z = 0; z < sizeZ; z++) { for (int y = 0; y < sizeY; y++) { for (int x = 0; x < sizeX; x++) { - long srcPtr = src.ptr() + offset(x + srcX, y + srcY, z + srcZ, srcSizeX, srcSizeY); - long dstPtr = dst.ptr() + offset(x + dstX, y + dstY, z + dstZ, dstSizeX, dstSizeY); + long srcPtr = src + offset(x + srcX, y + srcY, z + srcZ, srcSizeX, srcSizeY); + long dstPtr = dst + offset(x + dstX, y + dstY, z + dstZ, dstSizeX, dstSizeY); MemoryUtil.memPutShort(dstPtr, MemoryUtil.memGetShort(srcPtr)); } @@ -95,7 +140,11 @@ public class EmbeddedLightVolume { } public static long offset(int x, int y, int z, int sizeX, int sizeY) { - return (x + sizeX * (y + z * sizeY)) * STRIDE; + return (x + sizeX * (y + sizeY * z)) * STRIDE; + } + + public void clear() { + empty = true; } public void delete() { @@ -110,26 +159,30 @@ public class EmbeddedLightVolume { } public int x() { - return wantedCoords.minX(); + return minX; } public int y() { - return wantedCoords.minY(); + return minY; } public int z() { - return wantedCoords.minZ(); + return minZ; } public int sizeX() { - return wantedCoords.sizeX(); + return maxX - minX; } public int sizeY() { - return wantedCoords.sizeY(); + return maxY - minY; } public int sizeZ() { - return wantedCoords.sizeZ(); + return maxZ - minZ; + } + + public int volume() { + return sizeX() * sizeY() * sizeZ(); } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/util/BoxSet.java b/src/main/java/com/jozufozu/flywheel/backend/util/BoxSet.java deleted file mode 100644 index 13209e008..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/util/BoxSet.java +++ /dev/null @@ -1,174 +0,0 @@ -package com.jozufozu.flywheel.backend.util; - -import it.unimi.dsi.fastutil.longs.LongOpenHashSet; -import it.unimi.dsi.fastutil.longs.LongSet; -import net.minecraft.core.BlockPos; - -public class BoxSet { - private final LongSet occupied = new LongOpenHashSet(); - - private int minX; - private int minY; - private int minZ; - private int maxX; - private int maxY; - private int maxZ; - - /** - * Add a box to the set. - * - * @return {@code true} if the position or size of the set changed. - */ - public boolean add(int x, int y, int z, int sizeX, int sizeY, int sizeZ) { - boolean wasEmpty = occupied.isEmpty(); - for (int i = x; i < x + sizeX; i++) { - for (int j = y; j < y + sizeY; j++) { - for (int k = z; k < z + sizeZ; k++) { - occupied.add(BlockPos.asLong(i, j, k)); - } - } - } - - if (wasEmpty) { - this.minX = x; - this.minY = y; - this.minZ = z; - this.maxX = x + sizeX; - this.maxY = y + sizeY; - this.maxZ = z + sizeZ; - return true; - } else { - boolean changed = false; - if (x < minX) { - minX = x; - changed = true; - } - if (y < minY) { - minY = y; - changed = true; - } - if (z < minZ) { - minZ = z; - changed = true; - } - if (x + sizeX > maxX) { - maxX = x + sizeX; - changed = true; - } - if (y + sizeY > maxY) { - maxY = y + sizeY; - changed = true; - } - if (z + sizeZ > maxZ) { - maxZ = z + sizeZ; - changed = true; - } - return changed; - } - } - - /** - * Remove a box from the set. - * - * @return {@code true} if the position or size of the set changed. - */ - public boolean clear(int x, int y, int z, int sizeX, int sizeY, int sizeZ) { - for (int i = x; i < x + sizeX; i++) { - for (int j = y; j < y + sizeY; j++) { - for (int k = z; k < z + sizeZ; k++) { - occupied.remove(BlockPos.asLong(i, j, k)); - } - } - } - - if (occupied.isEmpty()) { - minX = 0; - minY = 0; - minZ = 0; - maxX = 0; - maxY = 0; - maxZ = 0; - return true; - } else { - ExtremaFinder finder = new ExtremaFinder(); - occupied.forEach(finder::accept); - - if (finder.volume() != volume()) { - minX = finder.minX; - minY = finder.minY; - minZ = finder.minZ; - maxX = finder.maxX; - maxY = finder.maxY; - maxZ = finder.maxZ; - return true; - } else { - return false; - } - } - } - - public int minX() { - return minX; - } - - public int minY() { - return minY; - } - - public int minZ() { - return minZ; - } - - public int sizeX() { - return maxX - minX; - } - - public int sizeY() { - return maxY - minY; - } - - public int sizeZ() { - return maxZ - minZ; - } - - public int volume() { - return sizeX() * sizeY() * sizeZ(); - } - - private static class ExtremaFinder { - int minX = Integer.MAX_VALUE; - int minY = Integer.MAX_VALUE; - int minZ = Integer.MAX_VALUE; - int maxX = Integer.MIN_VALUE; - int maxY = Integer.MIN_VALUE; - int maxZ = Integer.MIN_VALUE; - - public void accept(long l) { - int x = BlockPos.getX(l); - int y = BlockPos.getY(l); - int z = BlockPos.getZ(l); - if (x < minX) { - minX = x; - } - if (y < minY) { - minY = y; - } - if (z < minZ) { - minZ = z; - } - if (x > maxX) { - maxX = x; - } - if (y > maxY) { - maxY = y; - } - if (z > maxZ) { - maxZ = z; - } - } - - public int volume() { - return (maxX - minX) * (maxY - minY) * (maxZ - minZ); - } - } -}