More work

This commit is contained in:
mcrcortex
2024-08-22 09:19:29 +10:00
parent fbdd65bc00
commit 1a42f115fb
3 changed files with 121 additions and 74 deletions

View File

@@ -12,6 +12,8 @@ import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.ModContainer; import net.fabricmc.loader.api.ModContainer;
import net.minecraft.client.world.ClientWorld; import net.minecraft.client.world.ClientWorld;
import java.util.Arrays;
public class Voxy implements ClientModInitializer { public class Voxy implements ClientModInitializer {
public static final String VERSION; public static final String VERSION;
@@ -39,4 +41,9 @@ public class Voxy implements ClientModInitializer {
public static void breakpoint() { public static void breakpoint() {
int breakpoint = 0; int breakpoint = 0;
} }
public static void logError(Object... args) {
System.err.println(Arrays.toString(args));
}
} }

View File

@@ -3,6 +3,7 @@ package me.cortex.voxy.client.core.rendering.hierachical2;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
import me.cortex.voxy.client.Voxy;
import me.cortex.voxy.client.core.rendering.building.BuiltSection; import me.cortex.voxy.client.core.rendering.building.BuiltSection;
import me.cortex.voxy.client.core.rendering.building.SectionUpdateRouter; import me.cortex.voxy.client.core.rendering.building.SectionUpdateRouter;
import me.cortex.voxy.client.core.rendering.section.AbstractSectionGeometryManager; import me.cortex.voxy.client.core.rendering.section.AbstractSectionGeometryManager;
@@ -17,10 +18,48 @@ import static me.cortex.voxy.client.core.rendering.hierachical2.NodeStore.NODE_I
//Contains no logic to interface with the gpu, nor does it contain any gpu buffers //Contains no logic to interface with the gpu, nor does it contain any gpu buffers
public class HierarchicalNodeManager { public class HierarchicalNodeManager {
/**
* Rough explanation to help with confusion,
* All leaf nodes (i.e. nodes that have no children currently existing), all have a geometry node attached (if its non empty)
* note! on the gpu, it does not need the child ptr unless it tries to go down, that is, when creating/uploading to gpu
* dont strictly need all child nodes
* Internal nodes are nodes that have children and parents, they themself may or may not have geometry attached
* Top level nodes, dont have parents but other than that inherit all the properties of internal nodes
*
* The way the traversal works is as follows
* Top level nodes are source nodes, they are the inital queue
* * For each node in the queue, compute the screenspace size of the node, check against the hiz buffer
* * If the node is < rendering screensize
* * If the node has geometry and not empty
* * Queue the geometry for rendering
* * If the node is missing geometry and is marked as not empty
* * Attempt to put node ID into request queue
* * If node has children
* * put children into traversal queue
* * Else
* * Technically an error state, as we should _always_ either have children or geometry
* * Else
* * Dont do anything as section is explicit empty render data
* * Else if node is > rendering screensize, need to subdivide
* * If Child ptr is not empty (i.e. is an internal node)
* * Enqueue children into traversal queue
* * If child ptr is empty (i.e. is leaf node) and is not base LoD level
* * Attempt to put node ID into request queue
* * If node has geometry, or is explicitly marked as empty render data
* * Queue own geometry (if non empty)
* * Else
* * Technically an error state, as a leaf node should always have geometry (or marked as empty geometry data)
*/
//WARNING! need to solve a section geometry which is empty having its geometry removed, cause its empty
// so it wont send a request back cpu side
private static final int NO_NODE = -1; private static final int NO_NODE = -1;
private static final int SENTINAL_TOP_NODE_INFLIGHT = -2;
private static final int ID_TYPE_MSK = (3<<30); private static final int ID_TYPE_MSK = (3<<30);
private static final int ID_TYPE_LEAF = (3<<30);
private static final int ID_TYPE_NONE = 0; private static final int ID_TYPE_NONE = 0;
private static final int ID_TYPE_REQUEST = (2<<30); private static final int ID_TYPE_REQUEST = (2<<30);
private static final int ID_TYPE_TOP = (1<<30); private static final int ID_TYPE_TOP = (1<<30);
@@ -55,7 +94,10 @@ public class HierarchicalNodeManager {
if (this.activeSectionMap.containsKey(position)) { if (this.activeSectionMap.containsKey(position)) {
throw new IllegalArgumentException("Position already in node set: " + WorldEngine.pprintPos(position)); throw new IllegalArgumentException("Position already in node set: " + WorldEngine.pprintPos(position));
} }
this.activeSectionMap.put(position, SENTINAL_TOP_NODE_INFLIGHT);
int id = this.nodeData.allocate();
this.nodeData.setNodePosition(id, position);
this.activeSectionMap.put(position, id|ID_TYPE_TOP);
this.updateRouter.watch(position, WorldEngine.UPDATE_FLAGS); this.updateRouter.watch(position, WorldEngine.UPDATE_FLAGS);
} }
@@ -71,10 +113,6 @@ public class HierarchicalNodeManager {
throw new IllegalArgumentException("Tried removing node but it didnt exist: " + WorldEngine.pprintPos(position)); throw new IllegalArgumentException("Tried removing node but it didnt exist: " + WorldEngine.pprintPos(position));
} }
if (node == SENTINAL_TOP_NODE_INFLIGHT) {
System.err.println("WARN: Removing inflight top level node: " + WorldEngine.pprintPos(position));
return;
} else {
int type = (node & ID_TYPE_MSK); int type = (node & ID_TYPE_MSK);
node &= ~ID_TYPE_MSK; node &= ~ID_TYPE_MSK;
if (type == ID_TYPE_REQUEST) { if (type == ID_TYPE_REQUEST) {
@@ -117,8 +155,8 @@ public class HierarchicalNodeManager {
// go up to parent and remove node from the parent allocation and free node id // go up to parent and remove node from the parent allocation and free node id
} }
}
//============================================================================================================================================
public void processRequestQueue(int count, long ptr) { public void processRequestQueue(int count, long ptr) {
for (int requestIndex = 0; requestIndex < count; requestIndex++) { for (int requestIndex = 0; requestIndex < count; requestIndex++) {
int op = MemoryUtil.memGetInt(ptr + (requestIndex * 4L)); int op = MemoryUtil.memGetInt(ptr + (requestIndex * 4L));
@@ -178,12 +216,27 @@ public class HierarchicalNodeManager {
//============================================================================================================================================
public void processChildChange(long position, byte childExistence) { public void processChildChange(long position, byte childExistence) {
if (childExistence == 0) {
Voxy.logError("Section at " + WorldEngine.pprintPos(position) + " had empty child existence!!");
}
int nodeId = this.activeSectionMap.get(position); int nodeId = this.activeSectionMap.get(position);
if (nodeId == NO_NODE) { if (nodeId == NO_NODE) {
System.err.println("Received child change for section " + WorldEngine.pprintPos(position) + " however section position not in active in map! discarding"); System.err.println("Received child change for section " + WorldEngine.pprintPos(position) + " however section position not in active in map! discarding");
} else { } else {
int type = (nodeId & ID_TYPE_MSK);
nodeId &= ~ID_TYPE_MSK;
if (type == ID_TYPE_REQUEST) {
//Doesnt result in an invalidation as we must wait for geometry?
this.requests.get(nodeId).putChildResult(getChildIdx(position), childExistence);
} else if (type == ID_TYPE_LEAF || type == ID_TYPE_NONE || type == ID_TYPE_TOP) {
//For all the types, if there is a request attatched, need to update the request data (and update the associated sections in the activeSectionMap)
// if there isnt a current request, and there are nodes that got added, create a request
//if any children dont exist anymore, need to delete them
} else {
throw new IllegalStateException("Should not reach here");
}
} }
} }
@@ -194,22 +247,6 @@ public class HierarchicalNodeManager {
System.err.println("Received geometry for section " + WorldEngine.pprintPos(position) + " however section position not in active in map! discarding"); System.err.println("Received geometry for section " + WorldEngine.pprintPos(position) + " however section position not in active in map! discarding");
//Not tracked or mapped to a node!, discard it, it was probably in progress when it was removed from the map //Not tracked or mapped to a node!, discard it, it was probably in progress when it was removed from the map
section.free(); section.free();
} else {
//TODO! need to not do this as it may have child data assocaited, should allocate when initally adding the TLN
if (nodeId == SENTINAL_TOP_NODE_INFLIGHT) {
//Special state for top level nodes that are in flight
//Allocate a new node id
nodeId = this.nodeData.allocate();
this.activeSectionMap.put(position, nodeId|ID_TYPE_TOP);
int geometry = -1;
if (!section.isEmpty()) {
geometry = this.geometryManager.uploadSection(section);
} else {
section.free();
}
this.fillNode(nodeId, position, geometry, (byte) 0);//INCORRECT
} else { } else {
int type = (nodeId & ID_TYPE_MSK); int type = (nodeId & ID_TYPE_MSK);
nodeId &= ~ID_TYPE_MSK; nodeId &= ~ID_TYPE_MSK;
@@ -225,13 +262,6 @@ public class HierarchicalNodeManager {
} }
} }
} }
}
private void fillNode(int node, long position, int geometry, byte childExistence) {
this.nodeData.setNodePosition(node, position);
this.nodeData.setNodeGeometry(node, geometry);
this.nodeData.setNodeChildExistence(node, childExistence);
}
private void requestDataUpdate(int nodeId) { private void requestDataUpdate(int nodeId) {
var request = this.requests.get(nodeId); var request = this.requests.get(nodeId);
@@ -284,6 +314,8 @@ public class HierarchicalNodeManager {
} }
} }
//============================================================================================================================================
private static int getChildIdx(long pos) { private static int getChildIdx(long pos) {
int x = WorldEngine.getX(pos); int x = WorldEngine.getX(pos);
int y = WorldEngine.getY(pos); int y = WorldEngine.getY(pos);

View File

@@ -6,6 +6,7 @@ class NodeChildRequest {
private final long nodePos; private final long nodePos;
private final int[] childStates = new int[]{-1,-1,-1,-1,-1,-1,-1,-1}; private final int[] childStates = new int[]{-1,-1,-1,-1,-1,-1,-1,-1};
private final byte[] childChildExistence = new byte[]{(byte) 0,(byte) 0,(byte) 0,(byte) 0,(byte) 0,(byte) 0,(byte) 0,(byte) 0};
private byte results; private byte results;
private byte mask; private byte mask;
@@ -21,6 +22,13 @@ class NodeChildRequest {
return this.childStates[childIdx]; return this.childStates[childIdx];
} }
public void putChildChildExistence(int childIdx, byte childExistence) {
if ((this.mask&(1<<childIdx))==0) {
throw new IllegalStateException("Tried putting child into request which isnt in mask");
}
this.childChildExistence[childIdx] = childExistence;
}
public int putChildResult(int childIdx, int mesh) { public int putChildResult(int childIdx, int mesh) {
if ((this.mask&(1<<childIdx))==0) { if ((this.mask&(1<<childIdx))==0) {
throw new IllegalStateException("Tried putting child into request which isnt in mask"); throw new IllegalStateException("Tried putting child into request which isnt in mask");