Collector's edition

- Track exactly what blocks are contained in a light volume via BoxSet
- Fix segfault when a volume is expanded
- Shrink the volume when enough data is discarded
  - I think the texture management is broken for this case
This commit is contained in:
Jozufozu 2024-03-03 20:59:36 -08:00
parent e4df896710
commit 8c5c89921d
3 changed files with 268 additions and 75 deletions

View File

@ -56,9 +56,9 @@ public class EmbeddedEnvironment extends AtomicReferenceCounted implements Envir
lightTexture.bind();
lightTexture.ensureCapacity(lightVolume.sizeX, lightVolume.sizeY, lightVolume.sizeZ);
lightTexture.ensureCapacity(lightVolume.sizeX(), lightVolume.sizeY(), lightVolume.sizeZ());
lightTexture.upload(lightVolume.ptr(), lightVolume.sizeX, lightVolume.sizeY, lightVolume.sizeZ);
lightTexture.upload(lightVolume.ptr(), lightVolume.sizeX(), lightVolume.sizeY(), lightVolume.sizeZ());
}
@Override
@ -94,7 +94,7 @@ public class EmbeddedEnvironment extends AtomicReferenceCounted implements Envir
float oneOverSizeZ = 1f / (float) lightTexture.sizeZ;
drawProgram.setVec3("_flw_oneOverLightBoxSize", oneOverSizeX, oneOverSizeY, oneOverSizeZ);
drawProgram.setVec3("_flw_lightVolumeMin", lightVolume.minX, lightVolume.minY, lightVolume.minZ);
drawProgram.setVec3("_flw_lightVolumeMin", lightVolume.x(), lightVolume.y(), lightVolume.z());
}
drawProgram.setMat4("_flw_model", pose);
drawProgram.setMat3("_flw_normal", normal);

View File

@ -3,6 +3,7 @@ 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;
@ -11,20 +12,16 @@ import net.minecraft.world.level.LightLayer;
public class EmbeddedLightVolume {
public static final long STRIDE = Short.BYTES;
public int minX;
public int minY;
public int minZ;
public int sizeX;
public int sizeY;
public int sizeZ;
private final BoxSet wantedCoords = new BoxSet();
private final BlockPos.MutableBlockPos scratchPos = new BlockPos.MutableBlockPos();
@Nullable
protected MemoryBlock block;
protected boolean dirty;
protected MemoryBlock memoryBlock;
public boolean empty() {
return block == null;
return memoryBlock == null;
}
public void collect(BlockAndTintGetter level, int minX, int minY, int minZ, int sizeX, int sizeY, int sizeZ) {
@ -37,12 +34,37 @@ public class EmbeddedLightVolume {
}
}
}
markDirty();
}
public void invalidate(int minX, int minY, int minZ, int sizeX, int sizeY, int sizeZ) {
// TODO: shrink the volume
if (memoryBlock == null) {
return;
}
int oldMinX = wantedCoords.minX();
int oldMinY = wantedCoords.minY();
int oldMinZ = wantedCoords.minZ();
int oldSizeX = wantedCoords.sizeX();
int oldSizeY = wantedCoords.sizeY();
var shrank = wantedCoords.clear(minX, minY, minZ, sizeX, sizeY, sizeZ);
if (!shrank) {
return;
}
int newVolume = wantedCoords.volume();
MemoryBlock newBlock = MemoryBlock.malloc(newVolume * STRIDE);
int xOff = wantedCoords.minX() - oldMinX;
int yOff = wantedCoords.minY() - oldMinY;
int zOff = wantedCoords.minZ() - oldMinZ;
blit(memoryBlock, xOff, yOff, zOff, oldSizeX, oldSizeY, newBlock, 0, 0, 0, wantedCoords.sizeX(), wantedCoords.sizeY(), wantedCoords.sizeX(), wantedCoords.sizeY(), wantedCoords.sizeZ());
memoryBlock.free();
memoryBlock = newBlock;
}
private void paintLight(BlockAndTintGetter level, int x, int y, int z) {
@ -51,97 +73,94 @@ public class EmbeddedLightVolume {
int block = level.getBrightness(LightLayer.BLOCK, scratchPos);
int sky = level.getBrightness(LightLayer.SKY, scratchPos);
long ptr = worldPosToPtr(x, y, z);
long ptr = this.memoryBlock.ptr() + offset(x - x(), y - y(), z - z(), sizeX(), sizeY());
MemoryUtil.memPutShort(ptr, (short) ((block << 4) | sky << 12));
}
private void maybeExpandForBox(int minX, int minY, int minZ, int sizeX, int sizeY, int sizeZ) {
if (block == null) {
this.minX = minX;
this.minY = minY;
this.minZ = minZ;
this.sizeX = sizeX;
this.sizeY = sizeY;
this.sizeZ = 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();
var grew = wantedCoords.add(minX, minY, minZ, sizeX, sizeY, sizeZ);
if (memoryBlock == null) {
int volume = sizeX * sizeY * sizeZ;
block = MemoryBlock.malloc(volume * STRIDE);
block.clear();
memoryBlock = MemoryBlock.malloc(volume * STRIDE);
return;
}
int newMinX = Math.min(this.minX, minX);
int newMinY = Math.min(this.minY, minY);
int newMinZ = Math.min(this.minZ, minZ);
int newSizeX = Math.max(this.minX + this.sizeX, minX + sizeX) - newMinX;
int newSizeY = Math.max(this.minY + this.sizeY, minY + sizeY) - newMinY;
int newSizeZ = Math.max(this.minZ + this.sizeZ, minZ + sizeZ) - newMinZ;
if (newMinX == this.minX && newMinY == this.minY && newMinZ == this.minZ && newSizeX == this.sizeX && newSizeY == this.sizeY && newSizeZ == this.sizeZ) {
if (!grew) {
return;
}
int newVolume = newSizeX * newSizeY * newSizeZ;
int newVolume = wantedCoords.volume();
MemoryBlock newBlock = MemoryBlock.malloc(newVolume * STRIDE);
newBlock.clear();
int xOff = newMinX - this.minX;
int yOff = newMinY - this.minY;
int zOff = newMinZ - this.minZ;
int xOff = oldMinX - wantedCoords.minX();
int yOff = oldMinY - wantedCoords.minY();
int zOff = oldMinZ - wantedCoords.minZ();
for (int z = 0; z < this.sizeZ; z++) {
for (int y = 0; y < this.sizeY; y++) {
for (int x = 0; x < this.sizeX; x++) {
long oldPtr = boxPosToPtr(x, y, z);
long newPtr = newBlock.ptr() + x + xOff + (newSizeX * (y + yOff + (z + zOff) * newSizeY)) * STRIDE;
blit(memoryBlock, 0, 0, 0, oldSizeX, oldSizeY, newBlock, xOff, yOff, zOff, wantedCoords.sizeX(), wantedCoords.sizeY(), oldSizeX, oldSizeY, oldSizeZ);
MemoryUtil.memPutShort(newPtr, MemoryUtil.memGetShort(oldPtr));
memoryBlock.free();
memoryBlock = newBlock;
}
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) {
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);
MemoryUtil.memPutShort(dstPtr, MemoryUtil.memGetShort(srcPtr));
}
}
}
this.minX = newMinX;
this.minY = newMinY;
this.minZ = newMinZ;
this.sizeX = newSizeX;
this.sizeY = newSizeY;
this.sizeZ = newSizeZ;
block.free();
block = newBlock;
}
protected long worldPosToPtr(int x, int y, int z) {
return block.ptr() + worldPosToPtrOffset(x, y, z);
}
protected long boxPosToPtr(int x, int y, int z) {
return block.ptr() + boxPosToPtrOffset(x, y, z);
}
protected long worldPosToPtrOffset(int x, int y, int z) {
return boxPosToPtrOffset(x - minX, y - minY, z - minZ);
}
protected long boxPosToPtrOffset(int x, int y, int z) {
public static long offset(int x, int y, int z, int sizeX, int sizeY) {
return (x + sizeX * (y + z * sizeY)) * STRIDE;
}
public void delete() {
if (block != null) {
block.free();
block = null;
if (memoryBlock != null) {
memoryBlock.free();
memoryBlock = null;
}
}
protected void markDirty() {
this.dirty = true;
public long ptr() {
return memoryBlock.ptr();
}
public long ptr() {
return block.ptr();
public int x() {
return wantedCoords.minX();
}
public int y() {
return wantedCoords.minY();
}
public int z() {
return wantedCoords.minZ();
}
public int sizeX() {
return wantedCoords.sizeX();
}
public int sizeY() {
return wantedCoords.sizeY();
}
public int sizeZ() {
return wantedCoords.sizeZ();
}
}

View File

@ -0,0 +1,174 @@
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);
}
}
}