This commit is contained in:
mcrcortex
2025-01-21 05:35:22 +10:00
parent 3dcfd196a1
commit 44c66d5c26
9 changed files with 123 additions and 22 deletions

View File

@@ -151,8 +151,9 @@ public class RenderService<T extends AbstractSectionRenderer<J, ?>, J extends Vi
public void setup(Camera camera) { public void setup(Camera camera) {
final int W = 32; final int W = 32;
final int H = 2; final int H = 2;
boolean SIDED = false;
for (int i = 0; i<64 && q<((W*2+1)*(W*2+1)*H)&&q++>=0;i++) { for (int i = 0; i<64 && q<((W*2+1)*(W*2+1)*H)&&q++>=0;i++) {
this.nodeManager.insertTopLevelNode(WorldEngine.getWorldSectionId(4, (q%(W*2+1))-W, ((q/(W*2+1))/(W*2+1))-1, ((q/(W*2+1))%(W*2+1))-W)); this.nodeManager.insertTopLevelNode(WorldEngine.getWorldSectionId(4, (q%(W*2+1))-(SIDED?0:W), ((q/(W*2+1))/(W*2+1))-1, ((q/(W*2+1))%(W*2+1))-(SIDED?0:W)));
} }
if (q==((W*2+1)*(W*2+1)*H)) { if (q==((W*2+1)*(W*2+1)*H)) {
q++; q++;

View File

@@ -41,9 +41,6 @@ public class HierarchicalOcclusionTraverser {
private final GlBuffer renderTrackingBuffer;
private final GlBuffer queueMetaBuffer = new GlBuffer(4*4*5).zero(); private final GlBuffer queueMetaBuffer = new GlBuffer(4*4*5).zero();
private final GlBuffer scratchQueueA = new GlBuffer(100_000*4).zero(); private final GlBuffer scratchQueueA = new GlBuffer(100_000*4).zero();
private final GlBuffer scratchQueueB = new GlBuffer(100_000*4).zero(); private final GlBuffer scratchQueueB = new GlBuffer(100_000*4).zero();
@@ -95,8 +92,6 @@ public class HierarchicalOcclusionTraverser {
this.requestBuffer = new GlBuffer(REQUEST_QUEUE_SIZE*8L+8).zero(); this.requestBuffer = new GlBuffer(REQUEST_QUEUE_SIZE*8L+8).zero();
this.nodeBuffer = new GlBuffer(nodeManager.maxNodeCount*16L).zero(); this.nodeBuffer = new GlBuffer(nodeManager.maxNodeCount*16L).zero();
this.renderTrackingBuffer = new GlBuffer(nodeManager.maxNodeCount*4L).zero();
glSamplerParameteri(this.hizSampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); glSamplerParameteri(this.hizSampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
glSamplerParameteri(this.hizSampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glSamplerParameteri(this.hizSampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
@@ -133,8 +128,8 @@ public class HierarchicalOcclusionTraverser {
MemoryUtil.memPutFloat(ptr, (float) (screenspaceAreaDecreasingSize) /(viewport.width*viewport.height)); ptr += 4; MemoryUtil.memPutFloat(ptr, (float) (screenspaceAreaDecreasingSize) /(viewport.width*viewport.height)); ptr += 4;
//FrameId for timing info //VisibilityId
MemoryUtil.memPutInt(ptr, viewport.frameId); ptr += 4; MemoryUtil.memPutInt(ptr, this.nodeCleaner.visibilityId); ptr += 4;
/* /*
//Very funny and cool thing that is possible //Very funny and cool thing that is possible
@@ -153,7 +148,7 @@ public class HierarchicalOcclusionTraverser {
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, RENDER_QUEUE_BINDING, this.renderList.id); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, RENDER_QUEUE_BINDING, this.renderList.id);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, NODE_DATA_BINDING, this.nodeBuffer.id); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, NODE_DATA_BINDING, this.nodeBuffer.id);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, NODE_QUEUE_META_BINDING, this.queueMetaBuffer.id); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, NODE_QUEUE_META_BINDING, this.queueMetaBuffer.id);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, RENDER_TRACKER_BINDING, this.renderTrackingBuffer.id); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, RENDER_TRACKER_BINDING, this.nodeCleaner.visibilityBuffer.id);
glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, this.queueMetaBuffer.id); glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, this.queueMetaBuffer.id);
//Bind the hiz buffer //Bind the hiz buffer
@@ -307,7 +302,6 @@ public class HierarchicalOcclusionTraverser {
this.queueMetaBuffer.free(); this.queueMetaBuffer.free();
this.scratchQueueA.free(); this.scratchQueueA.free();
this.scratchQueueB.free(); this.scratchQueueB.free();
this.renderTrackingBuffer.free();
glDeleteSamplers(this.hizSampler); glDeleteSamplers(this.hizSampler);
} }
} }

View File

@@ -16,6 +16,9 @@ import static org.lwjgl.opengl.GL43C.glDispatchCompute;
// then use bubble sort (/w fast path going to middle or 2 subdivisions deep) the bubble it up // then use bubble sort (/w fast path going to middle or 2 subdivisions deep) the bubble it up
// can do incremental sorting pass aswell, so only scan and sort a rolling sector of sections // can do incremental sorting pass aswell, so only scan and sort a rolling sector of sections
// (over a few frames to not cause lag, maybe) // (over a few frames to not cause lag, maybe)
//TODO : USE THIS IN HierarchicalOcclusionTraverser instead of other shit
public class NodeCleaner { public class NodeCleaner {
//TODO: use batch_visibility_set to clear visibility data when nodes are removed!! (TODO: nodeManager will need to forward info to this) //TODO: use batch_visibility_set to clear visibility data when nodes are removed!! (TODO: nodeManager will need to forward info to this)
@@ -36,7 +39,7 @@ public class NodeCleaner {
.add(ShaderType.COMPUTE, "voxy:lod/hierarchical/cleaner/batch_visibility_set.comp") .add(ShaderType.COMPUTE, "voxy:lod/hierarchical/cleaner/batch_visibility_set.comp")
.compile(); .compile();
private final GlBuffer visibilityBuffer; final GlBuffer visibilityBuffer;
private final GlBuffer outputBuffer = new GlBuffer(OUTPUT_COUNT*4); private final GlBuffer outputBuffer = new GlBuffer(OUTPUT_COUNT*4);
private final GlBuffer scratchBuffer = new GlBuffer(BATCH_SET_SIZE*4);//Scratch buffer for setting ids with private final GlBuffer scratchBuffer = new GlBuffer(BATCH_SET_SIZE*4);//Scratch buffer for setting ids with
@@ -67,7 +70,7 @@ public class NodeCleaner {
this.sorter.bind(); this.sorter.bind();
//TODO: choose whether this is in nodeSpace or section/geometryId space //TODO: choose whether this is in nodeSpace or section/geometryId space
//glDispatchCompute(, 1, 1); //glDispatchCompute(this.nodeManager.getCurrentMaxNodeId()/, 1, 1);
//DownloadStream.INSTANCE.download(this.outputBuffer, this::onDownload); //DownloadStream.INSTANCE.download(this.outputBuffer, this::onDownload);
} }

View File

@@ -275,6 +275,7 @@ public class NodeManager {
if (this.activeSectionMap.remove(cPos) == -1) {//TODO: verify the removed section is a request type of child and the request id matches this if (this.activeSectionMap.remove(cPos) == -1) {//TODO: verify the removed section is a request type of child and the request id matches this
throw new IllegalStateException("Child pos was in a request but not in active section map"); throw new IllegalStateException("Child pos was in a request but not in active section map");
} }
if (!this.updateRouter.unwatch(cPos, WorldEngine.UPDATE_FLAGS)) { if (!this.updateRouter.unwatch(cPos, WorldEngine.UPDATE_FLAGS)) {
throw new IllegalStateException("Child pos was not being watched"); throw new IllegalStateException("Child pos was not being watched");
} }
@@ -391,6 +392,10 @@ public class NodeManager {
} }
} }
} }
//TODO: FIXME: This isnt right, as we need to remove node + geometry if it was in a request aswell as a child?
// BUT dont think thats possible?
rem ^= reqRem; rem ^= reqRem;
//If the request is satisfied, submit the result //If the request is satisfied, submit the result
if (request.isSatisfied()) { if (request.isSatisfied()) {
@@ -399,13 +404,73 @@ public class NodeManager {
} }
if (rem != 0) { if (rem != 0) {
//"TODO"
//There are child node entries that need removing //There are child node entries that need removing
// TODO: this should be ok to do before request is satisfied
Logger.error("UNFINISHED OPERATION TODO: FIXME"); Logger.error("UNFINISHED OPERATION TODO: FIXME");
for (int i = 0; i < 8; i++) {
if ((rem & (1 << i)) == 0) continue;
long cPos = makeChildPos(pos, i);
this.recurseRemoveNode(cPos);
//TOdo: update the child existance afak
}
//TODO:FIXME:FINISH:CRITICAL //TODO:FIXME:FINISH:CRITICAL
} }
} }
} }
//Recursivly fully removes all nodes and children
private void recurseRemoveNode(long pos) {
//NOTE: this also removes from the section map
int nodeId = this.activeSectionMap.remove(pos);
if (nodeId == -1) {
throw new IllegalStateException("Cannot remove pos that doesnt exist");
}
int type = nodeId&NODE_TYPE_MSK;
nodeId &= NODE_ID_MSK;
if (type == NODE_TYPE_INNER || type == NODE_TYPE_LEAF) {
if (!this.nodeData.nodeExists(nodeId)) {
throw new IllegalStateException("Node exists in section map but not in nodeData");
}
byte childExistence = this.nodeData.getNodeChildExistence(nodeId);
if (this.nodeData.isNodeRequestInFlight(nodeId)) {
//If there is an inflight request, the request and all associated data
int reqId = this.nodeData.getNodeRequest(nodeId);
//TODO: Dont assume this can only be a child request
var req = this.childRequests.get(reqId);
childExistence ^= req.getMsk();
this.childRequests.release(reqId);//Release the request
}
//Need to recurse into childExistence that exist, this is xor between a request mask if there is and the
// childRequest
// this is only valid if this node is an inner node
Logger.error("UNFINISHED OPERATION TODO: FIXME2");
//Free geometry and related memory for this node
//Unwatch geometry
if (!this.updateRouter.unwatch(pos, WorldEngine.UPDATE_FLAGS)) {
throw new IllegalStateException("Pos was not being watched");
}
} else {
Logger.error("UNFINISHED OPERATION TODO: FIXME3");
//NOTE: There are request type singles and request type child!!!!
}
}
//================================================================================================================== //==================================================================================================================
private void finishRequest(SingleNodeRequest request) { private void finishRequest(SingleNodeRequest request) {
@@ -604,12 +669,6 @@ public class NodeManager {
throw new IllegalStateException("Unknown node type: " + nodeType); throw new IllegalStateException("Unknown node type: " + nodeType);
} }
if (this.nodeData.isNodeRequestInFlight(nodeId)) {
Logger.warn("Tried processing a node that already has a request in flight: " + nodeId + " pos: " + WorldEngine.pprintPos(pos) + " ignoring");
return;
}
//TODO: ADJUST AND FIX THIS TO MAKE IT REMOVE THE LAST THING IN QUEUE OR SOMETHING //TODO: ADJUST AND FIX THIS TO MAKE IT REMOVE THE LAST THING IN QUEUE OR SOMETHING
//if (this.activeNodeRequestCount > 100 && WorldEngine.getLevel(pos) < 2) { //if (this.activeNodeRequestCount > 100 && WorldEngine.getLevel(pos) < 2) {
//Logger.info("Many active requests, declining request at " + WorldEngine.pprintPos(pos)); //Logger.info("Many active requests, declining request at " + WorldEngine.pprintPos(pos));
@@ -618,16 +677,47 @@ public class NodeManager {
//} //}
this.nodeData.markRequestInFlight(nodeId);
//TODO:
// Make it so that if a request is not in flight it has an invalid/null request entry
// NOTE: inner nodes /w request should check they have geometry independenently of being inflight
//TODO: FIXTHIS: https://discord.com/channels/973046939375505408/973046939375505411/1328785093812031489
// this causes things to go bad, when racing the gpu, i.e. this becomes an inner node that has geometry and there is now a request for it
// in this case we should not mark the node as inflight as it casuse very bad things to happen
// we should only mark inflight when there is actually a request
if (nodeType == NODE_TYPE_LEAF) { if (nodeType == NODE_TYPE_LEAF) {
//Check if the node is already in-flight, if it is, dont do any processing
if (this.nodeData.isNodeRequestInFlight(nodeId)) {
Logger.warn("Tried processing a node that already has a request in flight: " + nodeId + " pos: " + WorldEngine.pprintPos(pos) + " ignoring");
return;
}
//Mark node as having an inflight request
this.nodeData.markRequestInFlight(nodeId);
//The hard one of processRequest, spin up a new request for the node //The hard one of processRequest, spin up a new request for the node
this.makeLeafChildRequest(nodeId); this.makeLeafChildRequest(nodeId);
} else {//nodeType == NODE_TYPE_INNER } else {//nodeType == NODE_TYPE_INNER
//Dont mark node as having an inflight request
//TODO: assert that the node isnt already being watched for geometry, if it is, just spit out a warning? and ignore //TODO: assert that the node isnt already being watched for geometry, if it is, just spit out a warning? and ignore
Logger.error("TODO FINISH THIS"); Logger.error("TODO FINISH THIS");
// THis shouldent result in markRequestInFlight afak
if (!this.updateRouter.watch(pos, WorldEngine.UPDATE_TYPE_BLOCK_BIT)) { if (!this.updateRouter.watch(pos, WorldEngine.UPDATE_TYPE_BLOCK_BIT)) {
//FIXME: i think this can occur accidently? when removing nodes or something creating leaf nodes //FIXME: think this can occur accidently? when removing nodes or something creating leaf nodes
// or other, the node might be wanted to be watched by gpu, but cpu already started watching it a few frames ago // or other, the node might be wanted to be watched by gpu, but cpu already started watching it a few frames ago
Logger.warn("Node: " + nodeId + " at pos: " + WorldEngine.pprintPos(pos) + " got update request, but geometry was already being watched"); Logger.warn("Node: " + nodeId + " at pos: " + WorldEngine.pprintPos(pos) + " got update request, but geometry was already being watched");
} }
@@ -723,4 +813,8 @@ public class NodeManager {
public void addDebug(List<String> debug) { public void addDebug(List<String> debug) {
debug.add("NC/IF: " + this.activeSectionMap.size() + "/" + (this.singleRequests.count() + this.childRequests.count())); debug.add("NC/IF: " + this.activeSectionMap.size() + "/" + (this.singleRequests.count() + this.childRequests.count()));
} }
//public int getCurrentMaxNodeId() {
// return this.nodeData.getEndNodeId();
//}
} }

View File

@@ -272,4 +272,7 @@ public final class NodeStore {
MemoryUtil.memPutInt(ptr, w); ptr += 4; MemoryUtil.memPutInt(ptr, w); ptr += 4;
} }
//public int getEndNodeId() {
//}
} }

View File

@@ -22,7 +22,7 @@ public class FragmentedStorageBackendAdaptor extends StorageBackend {
public FragmentedStorageBackendAdaptor(StorageBackend... backends) { public FragmentedStorageBackendAdaptor(StorageBackend... backends) {
this.backends = backends; this.backends = backends;
int len = backends.length; int len = backends.length;
if ((len&(len-1)) != (len-1)) { if ((len&(len-1)) != 0) {
throw new IllegalArgumentException("Backend count not a power of 2"); throw new IllegalArgumentException("Backend count not a power of 2");
} }
} }

View File

@@ -10,6 +10,7 @@ import net.minecraft.world.chunk.PalettedContainer;
import net.minecraft.world.chunk.ReadableContainer; import net.minecraft.world.chunk.ReadableContainer;
public class WorldConversionFactory { public class WorldConversionFactory {
//TODO: create a mapping for world/mapper -> local mapping
private static final ThreadLocal<Reference2IntOpenHashMap<BlockState>> BLOCK_CACHE = ThreadLocal.withInitial(Reference2IntOpenHashMap::new); private static final ThreadLocal<Reference2IntOpenHashMap<BlockState>> BLOCK_CACHE = ThreadLocal.withInitial(Reference2IntOpenHashMap::new);
public static VoxelizedSection convert(VoxelizedSection section, public static VoxelizedSection convert(VoxelizedSection section,

View File

@@ -55,6 +55,7 @@ public final class WorldSection {
public final AtomicBoolean inSaveQueue = new AtomicBoolean(); public final AtomicBoolean inSaveQueue = new AtomicBoolean();
//When the first bit is set it means its loaded //When the first bit is set it means its loaded
@SuppressWarnings("all")
private volatile int atomicState = 1; private volatile int atomicState = 1;
WorldSection(int lvl, int x, int y, int z, ActiveSectionTracker tracker) { WorldSection(int lvl, int x, int y, int z, ActiveSectionTracker tracker) {

View File

@@ -135,6 +135,9 @@ public class WorldImporter {
this.worker = new Thread(() -> { this.worker = new Thread(() -> {
this.isRunning = true; this.isRunning = true;
var files = directory.listFiles(); var files = directory.listFiles();
if (files == null) {
onCompletion.accept(0);
}
Arrays.sort(files, File::compareTo); Arrays.sort(files, File::compareTo);
this.estimatedTotalChunks.addAndGet(files.length*1024); this.estimatedTotalChunks.addAndGet(files.length*1024);
for (var file : files) { for (var file : files) {
@@ -159,6 +162,7 @@ public class WorldImporter {
Thread.onSpinWait(); Thread.onSpinWait();
} }
if (!this.isRunning) { if (!this.isRunning) {
onCompletion.accept(this.totalChunks.get());
return; return;
} }
} }