diff --git a/src/main/java/me/cortex/voxy/client/core/VoxelCore.java b/src/main/java/me/cortex/voxy/client/core/VoxelCore.java index c8b1b471..4b01e8e5 100644 --- a/src/main/java/me/cortex/voxy/client/core/VoxelCore.java +++ b/src/main/java/me/cortex/voxy/client/core/VoxelCore.java @@ -8,6 +8,7 @@ import me.cortex.voxy.client.core.rendering.post.PostProcessing; import me.cortex.voxy.client.core.rendering.util.DownloadStream; import me.cortex.voxy.client.core.util.IrisUtil; import me.cortex.voxy.client.saver.ContextSelectionSystem; +import me.cortex.voxy.common.Logger; import me.cortex.voxy.common.world.WorldEngine; import me.cortex.voxy.client.importers.WorldImporter; import me.cortex.voxy.common.thread.ServiceThreadPool; @@ -64,17 +65,17 @@ public class VoxelCore { this.serviceThreadPool = new ServiceThreadPool(VoxyConfig.CONFIG.serviceThreads); this.world = worldSelection.createEngine(this.serviceThreadPool); - System.out.println("Initializing voxy core"); + Logger.info("Initializing voxy core"); //Trigger the shared index buffer loading SharedIndexBuffer.INSTANCE.id(); Capabilities.init();//Ensure clinit is called this.renderer = new RenderService(this.world, this.serviceThreadPool); - System.out.println("Using " + this.renderer.getClass().getSimpleName()); + Logger.info("Using " + this.renderer.getClass().getSimpleName()); this.postProcessing = new PostProcessing(); - System.out.println("Voxy core initialized"); + Logger.info("Voxy core initialized"); //this.verifyTopNodeChildren(0,0,0); } @@ -174,7 +175,7 @@ public class VoxelCore { // since they are AABBS crossing the normal is impossible without one of the axis being equal public void shutdown() { - System.out.println("Flushing download stream"); + Logger.info("Flushing download stream"); DownloadStream.INSTANCE.flushWaitClear(); //if (Thread.currentThread() != this.shutdownThread) { @@ -183,18 +184,18 @@ public class VoxelCore { //this.world.getMapper().forceResaveStates(); if (this.importer != null) { - System.out.println("Shutting down importer"); - try {this.importer.shutdown();this.importer = null;} catch (Exception e) {e.printStackTrace();} + Logger.info("Shutting down importer"); + try {this.importer.shutdown();this.importer = null;} catch (Exception e) {Logger.error("Error shutting down importer", e);} } - System.out.println("Shutting down rendering"); - try {this.renderer.shutdown();} catch (Exception e) {e.printStackTrace();} - System.out.println("Shutting down post processor"); - if (this.postProcessing!=null){try {this.postProcessing.shutdown();} catch (Exception e) {e.printStackTrace();}} - System.out.println("Shutting down world engine"); - try {this.world.shutdown();} catch (Exception e) {e.printStackTrace();} - System.out.println("Shutting down service thread pool"); + Logger.info("Shutting down rendering"); + try {this.renderer.shutdown();} catch (Exception e) {Logger.error("Error shutting down renderer", e);} + Logger.info("Shutting down post processor"); + if (this.postProcessing!=null){try {this.postProcessing.shutdown();} catch (Exception e) {Logger.error("Error shutting down post processor", e);}} + Logger.info("Shutting down world engine"); + try {this.world.shutdown();} catch (Exception e) {Logger.error("Error shutting down world engine", e);} + Logger.info("Shutting down service thread pool"); this.serviceThreadPool.shutdown(); - System.out.println("Voxel core shut down"); + Logger.info("Voxel core shut down"); } public boolean createWorldImporter(World mcWorld, File worldPath) { @@ -235,7 +236,7 @@ public class VoxelCore { if (lvl == 0) { var own = this.world.acquire(lvl, x, y, z); if ((own.getNonEmptyChildren() != 0) ^ (own.getNonEmptyBlockCount() != 0)) { - System.err.println("Lvl 0 node not marked correctly " + WorldEngine.pprintPos(own.key)); + Logger.error("Lvl 0 node not marked correctly " + WorldEngine.pprintPos(own.key)); } own.release(); } else { @@ -247,7 +248,7 @@ public class VoxelCore { } var own = this.world.acquire(lvl, x, y, z); if (own.getNonEmptyChildren() != msk) { - System.err.println("Section empty child mask not correct " + WorldEngine.pprintPos(own.key) + " got: " + String.format("%8s", Integer.toBinaryString(Byte.toUnsignedInt(own.getNonEmptyChildren()))).replace(' ', '0') + " expected: " + String.format("%8s", Integer.toBinaryString(Byte.toUnsignedInt(msk))).replace(' ', '0')); + Logger.error("Section empty child mask not correct " + WorldEngine.pprintPos(own.key) + " got: " + String.format("%8s", Integer.toBinaryString(Byte.toUnsignedInt(own.getNonEmptyChildren()))).replace(' ', '0') + " expected: " + String.format("%8s", Integer.toBinaryString(Byte.toUnsignedInt(msk))).replace(' ', '0')); } own.release(); } diff --git a/src/main/java/me/cortex/voxy/client/core/model/ModelBakerySubsystem.java b/src/main/java/me/cortex/voxy/client/core/model/ModelBakerySubsystem.java index f4c46ab2..db0a6567 100644 --- a/src/main/java/me/cortex/voxy/client/core/model/ModelBakerySubsystem.java +++ b/src/main/java/me/cortex/voxy/client/core/model/ModelBakerySubsystem.java @@ -38,7 +38,7 @@ public class ModelBakerySubsystem { public void tick() { //There should be a method to access the frame time IIRC, if the user framecap is unlimited lock it to like 60 fps for computation - int BUDGET = 20;//TODO: make this computed based on the remaining free time in a frame (and like div by 2 to reduce overhead) (with a min of 1) + int BUDGET = 10;//TODO: make this computed based on the remaining free time in a frame (and like div by 2 to reduce overhead) (with a min of 1) for (int i = 0; i < BUDGET && !this.blockIdQueue.isEmpty(); i++) { int blockId = -1; diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/RenderService.java b/src/main/java/me/cortex/voxy/client/core/rendering/RenderService.java index 216600d0..b088b497 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/RenderService.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/RenderService.java @@ -47,7 +47,7 @@ public class RenderService, J extends Vi //Max sections: ~500k //Max geometry: 1 gb - this.sectionRenderer = (T) createSectionRenderer(this.modelService.getStore(),1<<19, (1L<<31)-1024); + this.sectionRenderer = (T) createSectionRenderer(this.modelService.getStore(),1<<19, (1L<<30)-1024); //Do something incredibly hacky, we dont need to keep the reference to this around, so just connect and discard var router = new SectionUpdateRouter(); diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/HierarchicalOcclusionTraverser.java b/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/HierarchicalOcclusionTraverser.java index d41ff9fd..27d7b538 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/HierarchicalOcclusionTraverser.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/HierarchicalOcclusionTraverser.java @@ -28,7 +28,7 @@ import static org.lwjgl.opengl.GL45.*; // TODO: swap to persistent gpu threads instead of dispatching MAX_ITERATIONS of compute layers public class HierarchicalOcclusionTraverser { - public static final int REQUEST_QUEUE_SIZE = 256; + public static final int REQUEST_QUEUE_SIZE = 50; private final NodeManager2 nodeManager; @@ -242,12 +242,16 @@ public class HierarchicalOcclusionTraverser { throw new IllegalStateException("Count unexpected extreme value: " + count); } if (count > (this.requestBuffer.size()>>3)-1) { - Logger.warn("Count over max buffer size, clamping, got count: " + count); + //This should not break the synchonization between gpu and cpu as in the traversal shader is + // `if (atomRes < REQUEST_QUEUE_SIZE) {` which forcefully clamps to the request size + + //Logger.warn("Count over max buffer size, clamping, got count: " + count + "."); + count = (int) ((this.requestBuffer.size()>>3)-1); } - if (count > REQUEST_QUEUE_SIZE) { - System.err.println("Count larger than 'maxRequestCount', overflow captured. Overflowed by " + (count-REQUEST_QUEUE_SIZE)); - } + //if (count > REQUEST_QUEUE_SIZE) { + // Logger.warn("Count larger than 'maxRequestCount', overflow captured. Overflowed by " + (count-REQUEST_QUEUE_SIZE)); + //} if (count != 0) { //this.nodeManager.processRequestQueue(count, ptr + 8); diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/NodeManager2.java b/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/NodeManager2.java index 8784a519..8502f071 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/NodeManager2.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/NodeManager2.java @@ -17,6 +17,12 @@ import java.util.List; +//TODO FIXME: CIRTICAL ISSUE: if a node is a top level section and is empty, when a child is tried to be made it explodes +// since all the children are empty +// To properly fix this, the top level nodes should only exist if there are non empty children +// (issues related to this fix, lod updates from 0 children state to something children state, aswell as other way round) + + public class NodeManager2 { //Assumptions: // all nodes have children (i.e. all nodes have at least one child existence bit set at all times) @@ -62,6 +68,8 @@ public class NodeManager2 { private final NodeStore nodeData; public final int maxNodeCount; private final IntArrayList topLevelNodeIds = new IntArrayList(); + private int activeNodeRequestCount; + public NodeManager2(int maxNodeCount, AbstractSectionGeometryManager geometryManager, SectionUpdateRouter updateRouter) { if (!MathUtil.isPowerOfTwo(maxNodeCount)) { throw new IllegalArgumentException("Max node count must be a power of 2"); @@ -86,8 +94,6 @@ public class NodeManager2 { int id = this.singleRequests.put(request); this.updateRouter.watch(pos, WorldEngine.UPDATE_FLAGS); this.activeSectionMap.put(pos, id|NODE_TYPE_REQUEST|REQUEST_TYPE_SINGLE); - - } public void removeTopLevelNode(long pos) { @@ -154,7 +160,7 @@ public class NodeManager2 { } else if ((nodeId&NODE_TYPE_MSK)==NODE_TYPE_INNER || (nodeId&NODE_TYPE_MSK)==NODE_TYPE_LEAF) { // Just doing a geometry update if (this.updateNodeGeometry(nodeId&NODE_ID_MSK, sectionResult) != 0) { - this.nodeUpdates.add(nodeId&NODE_ID_MSK); + this.invalidateNode(nodeId&NODE_ID_MSK); } } } @@ -246,7 +252,7 @@ public class NodeManager2 { //TODO: this (or remove) //this.nodeData.setNodeType(); this.activeSectionMap.put(request.getPosition(), id|NODE_TYPE_LEAF);//Assume that the result of any single request type is a leaf node - this.nodeUpdates.add(id); + this.invalidateNode(id); //Assume that this is always a top node @@ -277,7 +283,7 @@ public class NodeManager2 { this.nodeData.setNodeChildExistence(childNodeId, request.getChildChildExistence(childIdx)); this.nodeData.setNodeGeometry(childNodeId, request.getChildMesh(childIdx)); //Mark for update - this.nodeUpdates.add(childNodeId); + this.invalidateNode(childNodeId); //Put in map int pid = this.activeSectionMap.put(childPos, childNodeId|NODE_TYPE_LEAF); if ((pid&NODE_TYPE_MSK) != NODE_TYPE_REQUEST) { @@ -290,8 +296,9 @@ public class NodeManager2 { this.nodeData.setChildPtr(parentNodeId, base); this.nodeData.setChildPtrCount(parentNodeId, offset+1); this.nodeData.setNodeRequest(parentNodeId, 0);//TODO: create a better null request + this.activeNodeRequestCount--; this.nodeData.unmarkRequestInFlight(parentNodeId); - this.nodeUpdates.add(parentNodeId); + this.invalidateNode(parentNodeId); } else if ((parentNodeId&NODE_TYPE_MSK)==NODE_TYPE_INNER) { System.err.println("TODO: FIXME FINISH: finishRequest NODE_TYPE_INNER"); } else { @@ -319,8 +326,17 @@ public class NodeManager2 { Logger.warn("Tried processing a node that already has a request in flight: " + nodeId + " pos: " + WorldEngine.pprintPos(pos) + " ignoring"); return; } - this.nodeData.markRequestInFlight(nodeId); + + //TODO: ADJUST AND FIX THIS TO MAKE IT REMOVE THE LAST THING IN QUEUE OR SOMETHING + //if (this.activeNodeRequestCount > 100 && WorldEngine.getLevel(pos) < 2) { + //Logger.info("Many active requests, declining request at " + WorldEngine.pprintPos(pos)); + // this.invalidateNode(nodeId); + // return; + //} + + + this.nodeData.markRequestInFlight(nodeId); if (nodeType == NODE_TYPE_LEAF) { //The hard one of processRequest, spin up a new request for the node this.makeLeafChildRequest(nodeId); @@ -355,6 +371,7 @@ public class NodeManager2 { //Insert all the children into the tracking map with the node id int pid = this.activeSectionMap.put(childPos, requestId|NODE_TYPE_REQUEST|REQUEST_TYPE_CHILD); + if (pid != -1) { throw new IllegalStateException("Leaf request creation failed to insert child into map as a mapping already existed for the node! pos: " + WorldEngine.pprintPos(childPos) + " id: " + pid); } @@ -366,6 +383,7 @@ public class NodeManager2 { } this.nodeData.setNodeRequest(nodeId, requestId); + this.activeNodeRequestCount++; } //================================================================================================================== @@ -393,7 +411,9 @@ public class NodeManager2 { return true; } - + private void invalidateNode(int nodeId) { + this.nodeUpdates.add(nodeId); + } //================================================================================================================== private static int getChildIdx(long pos) { diff --git a/src/main/java/me/cortex/voxy/client/core/util/ExpandingObjectAllocationList.java b/src/main/java/me/cortex/voxy/client/core/util/ExpandingObjectAllocationList.java index 200516ee..b2f1d66a 100644 --- a/src/main/java/me/cortex/voxy/client/core/util/ExpandingObjectAllocationList.java +++ b/src/main/java/me/cortex/voxy/client/core/util/ExpandingObjectAllocationList.java @@ -33,6 +33,7 @@ public class ExpandingObjectAllocationList { if (!this.bitSet.free(id)) { throw new IllegalArgumentException("Index " + id + " was already released"); } + this.objects[id] = null; } public T get(int index) { diff --git a/src/main/java/me/cortex/voxy/common/Logger.java b/src/main/java/me/cortex/voxy/common/Logger.java index 5353b97c..983dd5d9 100644 --- a/src/main/java/me/cortex/voxy/common/Logger.java +++ b/src/main/java/me/cortex/voxy/common/Logger.java @@ -29,4 +29,15 @@ public class Logger { var stackEntry = new Throwable().getStackTrace()[1]; LOGGER.warn("["+stackEntry.getClassName()+"]: "+ Stream.of(args).map(Object::toString).collect(Collectors.joining(" ")), throwable); } + + public static void info(Object... args) { + Throwable throwable = null; + for (var i : args) { + if (i instanceof Throwable) { + throwable = (Throwable) i; + } + } + var stackEntry = new Throwable().getStackTrace()[1]; + LOGGER.info("["+stackEntry.getClassName()+"]: "+ Stream.of(args).map(Object::toString).collect(Collectors.joining(" ")), throwable); + } } diff --git a/src/main/java/me/cortex/voxy/common/storage/lmdb/LMDBStorageBackend.java b/src/main/java/me/cortex/voxy/common/storage/lmdb/LMDBStorageBackend.java index 50b83d9c..f8c2a164 100644 --- a/src/main/java/me/cortex/voxy/common/storage/lmdb/LMDBStorageBackend.java +++ b/src/main/java/me/cortex/voxy/common/storage/lmdb/LMDBStorageBackend.java @@ -1,6 +1,7 @@ package me.cortex.voxy.common.storage.lmdb; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import me.cortex.voxy.common.Logger; import me.cortex.voxy.common.storage.StorageBackend; import me.cortex.voxy.common.storage.config.ConfigBuildCtx; import me.cortex.voxy.common.storage.config.StorageConfig; @@ -40,7 +41,7 @@ public class LMDBStorageBackend extends StorageBackend { private void growEnv() { long size = this.dbi.getMapSize() + GROW_SIZE; - System.out.println("Growing DBI env size to: " + size + " bytes"); + Logger.info("Growing DBI env size to: " + size + " bytes"); this.dbi.setMapSize(size); }