Geometry cleaner works again

This commit is contained in:
mcrcortex
2025-05-15 21:39:32 +10:00
parent 48799d8ea1
commit e58cbaa6de
5 changed files with 80 additions and 32 deletions

View File

@@ -8,6 +8,7 @@ import me.cortex.voxy.client.core.gl.Capabilities;
import me.cortex.voxy.client.core.model.bakery.ModelTextureBakery; import me.cortex.voxy.client.core.model.bakery.ModelTextureBakery;
import me.cortex.voxy.client.core.rendering.util.RawDownloadStream; import me.cortex.voxy.client.core.rendering.util.RawDownloadStream;
import me.cortex.voxy.client.core.rendering.util.UploadStream; import me.cortex.voxy.client.core.rendering.util.UploadStream;
import me.cortex.voxy.common.util.MemoryBuffer;
import me.cortex.voxy.common.world.other.Mapper; import me.cortex.voxy.common.world.other.Mapper;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
@@ -35,6 +36,7 @@ import org.lwjgl.system.MemoryUtil;
import java.util.*; import java.util.*;
import static me.cortex.voxy.client.core.model.ModelStore.MODEL_SIZE; import static me.cortex.voxy.client.core.model.ModelStore.MODEL_SIZE;
import static org.lwjgl.opengl.ARBDirectStateAccess.nglTextureSubImage2D;
import static org.lwjgl.opengl.GL11.*; import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL33.glDeleteSamplers; import static org.lwjgl.opengl.GL33.glDeleteSamplers;
import static org.lwjgl.opengl.GL33.glGenSamplers; import static org.lwjgl.opengl.GL33.glGenSamplers;
@@ -661,8 +663,54 @@ public class ModelFactory {
return this.metadataCache[clientId]; return this.metadataCache[clientId];
} }
private static int computeSizeWithMips(int size) {
int total = 0;
for (;size!=0;size>>=1) total += size*size;
return total;
}
private static final MemoryBuffer SCRATCH_TEX = new MemoryBuffer((2L*3*computeSizeWithMips(MODEL_TEXTURE_SIZE))*4);
private static final int LAYERS = Integer.numberOfTrailingZeros(MODEL_TEXTURE_SIZE);
//TODO: redo to batch blit, instead of 6 seperate blits, and also fix mipping //TODO: redo to batch blit, instead of 6 seperate blits, and also fix mipping
private void putTextures(int id, ColourDepthTextureData[] textures) { private void putTextures(int id, ColourDepthTextureData[] textures) {
if (MODEL_TEXTURE_SIZE != 16) {throw new IllegalStateException("THIS METHOD MUST BE REDONE IF THIS CONST CHANGES");}
//Copy all textures into scratch
final long addr = SCRATCH_TEX.address;
final int LENGTH_B = MODEL_TEXTURE_SIZE*3;
for (int i = 0; i < 6; i++) {
int x = (i>>1)*MODEL_TEXTURE_SIZE;
int y = (i&1)*MODEL_TEXTURE_SIZE;
int j = 0;
for (int t : textures[i].colour()) {
int o = ((y+(j>>LAYERS))*LENGTH_B + ((j&(MODEL_TEXTURE_SIZE-1))+x))*4; j++;//LAYERS here is just cause faster
MemoryUtil.memPutInt(addr+o, t);
}
}
//Mip the scratch
long dAddr = addr;
for (int i = 0; i < LAYERS-1; i++) {
long sAddr = dAddr;
dAddr += (MODEL_TEXTURE_SIZE*MODEL_TEXTURE_SIZE*3*2*4)>>(i<<1);//is.. i*2 because shrink both MODEL_TEXTURE_SIZE by >>i so is 2*i total shift
int width = (MODEL_TEXTURE_SIZE*3)>>(i+1);
int sWidth = (MODEL_TEXTURE_SIZE*3)>>i;
int height = (MODEL_TEXTURE_SIZE*2)>>(i+1);
//TODO: OPTIMZIE THIS
for (int px = 0; px < width; px++) {
for (int py = 0; py < height; py++) {
long bp = sAddr + (px*2 + py*2*sWidth)*4;
int C00 = MemoryUtil.memGetInt(bp);
int C01 = MemoryUtil.memGetInt(bp+sWidth*4);
int C10 = MemoryUtil.memGetInt(bp+4);
int C11 = MemoryUtil.memGetInt(bp+sWidth*4+4);
MemoryUtil.memPutInt(dAddr + (px+py*width) * 4L, TextureUtils.mipColours(C00, C01, C10, C11));
}
}
}
int X = (id&0xFF) * MODEL_TEXTURE_SIZE*3; int X = (id&0xFF) * MODEL_TEXTURE_SIZE*3;
int Y = ((id>>8)&0xFF) * MODEL_TEXTURE_SIZE*2; int Y = ((id>>8)&0xFF) * MODEL_TEXTURE_SIZE*2;
@@ -671,31 +719,10 @@ public class ModelFactory {
glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4); glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
for (int subTex = 0; subTex < 6; subTex++) { long cAddr = addr;
int x = X + (subTex>>1)*MODEL_TEXTURE_SIZE; for (int lvl = 0; lvl < LAYERS; lvl++) {
int y = Y + (subTex&1)*MODEL_TEXTURE_SIZE; nglTextureSubImage2D(this.storage.textures.id, lvl, X >> lvl, Y >> lvl, (MODEL_TEXTURE_SIZE*3) >> lvl, (MODEL_TEXTURE_SIZE*2) >> lvl, GL_RGBA, GL_UNSIGNED_BYTE, cAddr);
cAddr += (MODEL_TEXTURE_SIZE*MODEL_TEXTURE_SIZE*3*2*4)>>(lvl<<1);
var current = textures[subTex].colour();
var next = new int[current.length>>1];
final int layers = Integer.numberOfTrailingZeros(MODEL_TEXTURE_SIZE);
for (int i = 0; i < layers; i++) {
glTextureSubImage2D(this.storage.textures.id, i, x>>i, y>>i, MODEL_TEXTURE_SIZE>>i, MODEL_TEXTURE_SIZE>>i, GL_RGBA, GL_UNSIGNED_BYTE, current);
int nSize = MODEL_TEXTURE_SIZE>>(i+1);
int size = MODEL_TEXTURE_SIZE>>i;
for (int pX = 0; pX < nSize; pX++) {
for (int pY = 0; pY < nSize; pY++) {
int C00 = current[(pY*2)*size+pX*2];
int C01 = current[(pY*2+1)*size+pX*2];
int C10 = current[(pY*2)*size+pX*2+1];
int C11 = current[(pY*2+1)*size+pX*2+1];
next[pY*nSize+pX] = TextureUtils.mipColours(C00, C01, C10, C11);
}
}
current = next;
next = new int[current.length>>1];
}
} }
} }

View File

@@ -17,7 +17,6 @@ import me.cortex.voxy.client.core.rendering.util.UploadStream;
import me.cortex.voxy.common.Logger; import me.cortex.voxy.common.Logger;
import me.cortex.voxy.common.util.MemoryBuffer; import me.cortex.voxy.common.util.MemoryBuffer;
import me.cortex.voxy.common.world.WorldSection; import me.cortex.voxy.common.world.WorldSection;
import net.fabricmc.loader.impl.util.log.Log;
import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.MemoryUtil;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
@@ -27,7 +26,6 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.LockSupport; import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.StampedLock; import java.util.concurrent.locks.StampedLock;
import static me.cortex.voxy.client.core.rendering.section.geometry.BasicSectionGeometryData.SECTION_METADATA_SIZE;
import static org.lwjgl.opengl.ARBUniformBufferObject.glBindBufferBase; import static org.lwjgl.opengl.ARBUniformBufferObject.glBindBufferBase;
import static org.lwjgl.opengl.GL30C.glUniform1ui; import static org.lwjgl.opengl.GL30C.glUniform1ui;
import static org.lwjgl.opengl.GL42C.GL_UNIFORM_BARRIER_BIT; import static org.lwjgl.opengl.GL42C.GL_UNIFORM_BARRIER_BIT;
@@ -57,6 +55,7 @@ public class AsyncNodeManager {
private final Thread thread; private final Thread thread;
public final int maxNodeCount; public final int maxNodeCount;
private final long geometryCapacity;
private volatile boolean running = true; private volatile boolean running = true;
private final NodeManager manager; private final NodeManager manager;
@@ -70,7 +69,7 @@ public class AsyncNodeManager {
private volatile SyncResults resultCache2 = new SyncResults(); private volatile SyncResults resultCache2 = new SyncResults();
//Yes. this is stupid. yes. it is a large amount of runtime. Is it profiler bias, probably //Yes. this is stupid. yes. it is a large amount of runtime. Is it profiler bias, probably
private ConcurrentLinkedDeque<MemoryBuffer> buffersToFreeQueue = new ConcurrentLinkedDeque<>(); private final ConcurrentLinkedDeque<MemoryBuffer> buffersToFreeQueue = new ConcurrentLinkedDeque<>();
//locals for during iteration //locals for during iteration
@@ -86,8 +85,10 @@ public class AsyncNodeManager {
// it MUST ONLY be accessed on the render thread // it MUST ONLY be accessed on the render thread
// AsyncNodeManager will use an AsyncGeometryManager as the manager for the data store, and sync the results on the render thread // AsyncNodeManager will use an AsyncGeometryManager as the manager for the data store, and sync the results on the render thread
this.geometryData = geometryData; this.geometryData = geometryData;
this.geometryCapacity = ((BasicSectionGeometryData)geometryData).getGeometryCapacityBytes();
this.maxNodeCount = maxNodeCount; this.maxNodeCount = maxNodeCount;
this.thread = new Thread(()->{ this.thread = new Thread(()->{
try { try {
while (this.running) { while (this.running) {
@@ -99,7 +100,7 @@ public class AsyncNodeManager {
}); });
this.thread.setName("Async Node Manager"); this.thread.setName("Async Node Manager");
this.geometryManager = new BasicAsyncGeometryManager(((BasicSectionGeometryData)geometryData).getMaxSectionCount(), ((BasicSectionGeometryData)geometryData).getGeometryCapacity()); this.geometryManager = new BasicAsyncGeometryManager(((BasicSectionGeometryData)geometryData).getMaxSectionCount(), this.geometryCapacity);
this.manager = new NodeManager(maxNodeCount, this.geometryManager, watcher); this.manager = new NodeManager(maxNodeCount, this.geometryManager, watcher);
//Dont do the move... is just to much effort //Dont do the move... is just to much effort
this.manager.setClear(new NodeManager.ICleaner() { this.manager.setClear(new NodeManager.ICleaner() {
@@ -446,6 +447,7 @@ public class AsyncNodeManager {
} }
results.geometrySectionCount = this.geometryManager.getSectionCount(); results.geometrySectionCount = this.geometryManager.getSectionCount();
results.usedGeometry = this.geometryManager.getGeometryUsedBytes();
results.currentMaxNodeId = this.manager.getCurrentMaxNodeId(); results.currentMaxNodeId = this.manager.getCurrentMaxNodeId();
this.needsWaitForSync |= results.geometryUploads.size() > UPLOAD_LIMIT;//Max of 200 uploads per frame :( this.needsWaitForSync |= results.geometryUploads.size() > UPLOAD_LIMIT;//Max of 200 uploads per frame :(
@@ -520,6 +522,7 @@ public class AsyncNodeManager {
} }
this.currentMaxNodeId = results.currentMaxNodeId; this.currentMaxNodeId = results.currentMaxNodeId;
this.usedGeometryAmount = results.usedGeometry;
//Insert the result set into the cache //Insert the result set into the cache
if (!RESULT_CACHE_1_HANDLE.compareAndSet(this, null, results)) { if (!RESULT_CACHE_1_HANDLE.compareAndSet(this, null, results)) {
@@ -541,6 +544,16 @@ public class AsyncNodeManager {
return this.currentMaxNodeId; return this.currentMaxNodeId;
} }
private long usedGeometryAmount = 0;
public long getUsedGeometryCapacity() {
return this.usedGeometryAmount;
}
public long getGeometryCapacity() {
return this.geometryCapacity;
}
//================================================================================================================== //==================================================================================================================
//Incoming events //Incoming events
@@ -700,6 +713,7 @@ public class AsyncNodeManager {
//Deltas for geometry store //Deltas for geometry store
private int geometrySectionCount; private int geometrySectionCount;
private long usedGeometry;
private final Int2ObjectOpenHashMap<MemoryBuffer> geometryUploads = new Int2ObjectOpenHashMap<>(); private final Int2ObjectOpenHashMap<MemoryBuffer> geometryUploads = new Int2ObjectOpenHashMap<>();

View File

@@ -136,7 +136,8 @@ public class NodeCleaner {
//return this.nodeManager.getGeometryManager().getRemainingCapacity() < 1_000_000_000L; //return this.nodeManager.getGeometryManager().getRemainingCapacity() < 1_000_000_000L;
//If used more than 75% of geometry buffer //If used more than 75% of geometry buffer
return false;//return 3<((double)this.nodeManager.getGeometryManager().getUsedCapacity())/((double)this.nodeManager.getGeometryManager().getRemainingCapacity()); long used = this.nodeManager.getUsedGeometryCapacity();
return 1<((double)used)/((double)(this.nodeManager.getGeometryCapacity()-used));
} }
public void updateIds(IntOpenHashSet collection) { public void updateIds(IntOpenHashSet collection) {

View File

@@ -27,6 +27,7 @@ public class BasicAsyncGeometryManager implements IGeometryManager {
//Note!: the int part is an unsigned int ptr, must be scaled by GEOMETRY_ELEMENT_SIZE //Note!: the int part is an unsigned int ptr, must be scaled by GEOMETRY_ELEMENT_SIZE
private final Int2ObjectOpenHashMap<MemoryBuffer> heapUploads = new Int2ObjectOpenHashMap<>(1024);//Uploads into the buffer at the given location private final Int2ObjectOpenHashMap<MemoryBuffer> heapUploads = new Int2ObjectOpenHashMap<>(1024);//Uploads into the buffer at the given location
private final IntOpenHashSet heapRemoveUploads = new IntOpenHashSet(1024);//Any removals are added here, so that it can be properly synced private final IntOpenHashSet heapRemoveUploads = new IntOpenHashSet(1024);//Any removals are added here, so that it can be properly synced
private long usedCapacity = 0;
public BasicAsyncGeometryManager(int maxSectionCount, long geometryCapacity) { public BasicAsyncGeometryManager(int maxSectionCount, long geometryCapacity) {
this.allocationSet = new HierarchicalBitSet(maxSectionCount); this.allocationSet = new HierarchicalBitSet(maxSectionCount);
@@ -85,7 +86,7 @@ public class BasicAsyncGeometryManager implements IGeometryManager {
var oldMetadata = this.sectionMetadata.set(id, null); var oldMetadata = this.sectionMetadata.set(id, null);
int ptr = oldMetadata.geometryPtr; int ptr = oldMetadata.geometryPtr;
//Free from the heap //Free from the heap
this.allocationHeap.free(Integer.toUnsignedLong(ptr)); this.usedCapacity -= this.allocationHeap.free(Integer.toUnsignedLong(ptr));
//Free the upload if it was uploading //Free the upload if it was uploading
var buf = this.heapUploads.remove(ptr); var buf = this.heapUploads.remove(ptr);
if (buf != null) { if (buf != null) {
@@ -100,6 +101,7 @@ public class BasicAsyncGeometryManager implements IGeometryManager {
int size = (int) (section.geometryBuffer.size/GEOMETRY_ELEMENT_SIZE); int size = (int) (section.geometryBuffer.size/GEOMETRY_ELEMENT_SIZE);
//Address //Address
int addr = (int)this.allocationHeap.alloc(size); int addr = (int)this.allocationHeap.alloc(size);
this.usedCapacity += size;
//Create upload //Create upload
if (this.heapUploads.put(addr, section.geometryBuffer) != null) { if (this.heapUploads.put(addr, section.geometryBuffer) != null) {
throw new IllegalStateException(); throw new IllegalStateException();
@@ -126,6 +128,10 @@ public class BasicAsyncGeometryManager implements IGeometryManager {
return this.allocationSet.getCount(); return this.allocationSet.getCount();
} }
public long getGeometryUsedBytes() {
return this.usedCapacity * GEOMETRY_ELEMENT_SIZE;
}
public IntOpenHashSet getUpdateIds() { public IntOpenHashSet getUpdateIds() {
return this.invalidatedIds; return this.invalidatedIds;
} }

View File

@@ -40,7 +40,7 @@ public class BasicSectionGeometryData implements IGeometryData {
return this.maxSectionCount; return this.maxSectionCount;
} }
public long getGeometryCapacity() {//In bytes public long getGeometryCapacityBytes() {//In bytes
return this.geometryBuffer.size(); return this.geometryBuffer.size();
} }