partial geometry removal imp
This commit is contained in:
@@ -14,4 +14,10 @@ public interface ISectionWatcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
boolean unwatch(long position, int types);
|
boolean unwatch(long position, int types);
|
||||||
|
|
||||||
|
default int get(int lvl, int x, int y, int z) {
|
||||||
|
return this.get(WorldEngine.getWorldSectionId(lvl, x, y, z));
|
||||||
|
}
|
||||||
|
|
||||||
|
int get(long position);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package me.cortex.voxy.client.core.rendering.hierachical;
|
|||||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||||
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 it.unimi.dsi.fastutil.longs.LongArrayList;
|
|
||||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||||
import me.cortex.voxy.client.core.gl.GlBuffer;
|
import me.cortex.voxy.client.core.gl.GlBuffer;
|
||||||
@@ -20,7 +19,8 @@ import org.lwjgl.system.MemoryUtil;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static me.cortex.voxy.common.world.WorldEngine.MAX_LOD_LAYERS;
|
import static me.cortex.voxy.common.world.WorldEngine.MAX_LOD_LAYER;
|
||||||
|
import static me.cortex.voxy.common.world.WorldEngine.UPDATE_TYPE_BLOCK_BIT;
|
||||||
|
|
||||||
|
|
||||||
//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
|
//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
|
||||||
@@ -52,6 +52,16 @@ public class NodeManager {
|
|||||||
// if the top level node ends up being updated with a child update, it should automatically solve itself
|
// if the top level node ends up being updated with a child update, it should automatically solve itself
|
||||||
// as the new children are added to the already inprogress request!!!!
|
// as the new children are added to the already inprogress request!!!!
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//Alot of the rules for this datastructure have changed as it has evolved and finished
|
||||||
|
// the basic rules are the same, however some may be conditionally broken
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static final int NULL_GEOMETRY_ID = -1;
|
public static final int NULL_GEOMETRY_ID = -1;
|
||||||
public static final int EMPTY_GEOMETRY_ID = -2;
|
public static final int EMPTY_GEOMETRY_ID = -2;
|
||||||
public static final int NULL_REQUEST_ID = NodeStore.REQUEST_ID_MSK;
|
public static final int NULL_REQUEST_ID = NodeStore.REQUEST_ID_MSK;
|
||||||
@@ -180,18 +190,24 @@ public class NodeManager {
|
|||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
} else if ((nodeId&NODE_TYPE_MSK)==NODE_TYPE_INNER || (nodeId&NODE_TYPE_MSK)==NODE_TYPE_LEAF) {
|
} else if ((nodeId&NODE_TYPE_MSK)==NODE_TYPE_INNER || (nodeId&NODE_TYPE_MSK)==NODE_TYPE_LEAF) {
|
||||||
/*
|
nodeId&=NODE_ID_MSK;
|
||||||
//More verification
|
|
||||||
if (sectionResult.childExistence != this.nodeData.getNodeChildExistence(nodeId)) {
|
|
||||||
Logger.error("Child existance verification mismatch. expected: " + this.nodeData.getNodeChildExistence(nodeId) + " got: " + sectionResult.childExistence);
|
|
||||||
if (this.nodeData.isNodeRequestInFlight(nodeId)) {
|
|
||||||
Logger.error("AAAAAAAAAA");
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
|
|
||||||
|
//TODO: check this is ok and correct
|
||||||
|
if ((this.watcher.get(pos)&UPDATE_TYPE_BLOCK_BIT)==0) {
|
||||||
|
if (this.nodeData.isNodeGeometryInFlight(nodeId)) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
Logger.warn("Recieved geometry update but not watching it, discarding");
|
||||||
|
sectionResult.free();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Unmark geometry inflight
|
||||||
|
this.nodeData.unmarkNodeGeometryInFlight(nodeId);
|
||||||
// Just doing a geometry update
|
// Just doing a geometry update
|
||||||
if (this.updateNodeGeometry(nodeId&NODE_ID_MSK, sectionResult) != 0) {
|
if (this.updateNodeGeometry(nodeId, sectionResult) != 0) {
|
||||||
this.invalidateNode(nodeId&NODE_ID_MSK);
|
this.invalidateNode(nodeId);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
@@ -532,8 +548,25 @@ public class NodeManager {
|
|||||||
if (this.nodeData.isNodeRequestInFlight(nodeId))//Leaf nodes cannot have requests associated to them
|
if (this.nodeData.isNodeRequestInFlight(nodeId))//Leaf nodes cannot have requests associated to them
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
|
|
||||||
if (this.nodeData.getNodeGeometry(nodeId) == -1)
|
if (this.nodeData.getNodeGeometry(nodeId) == NULL_GEOMETRY_ID) {
|
||||||
throw new IllegalStateException("leaf nodes must have geometry");
|
//throw new IllegalStateException("leaf nodes must have geometry");
|
||||||
|
Logger.error("Transforming inner node to leaf node while it has null geometry");
|
||||||
|
if (!this.nodeData.isNodeGeometryInFlight(nodeId)) {
|
||||||
|
if ((this.watcher.get(pos) & UPDATE_TYPE_BLOCK_BIT) != 0) {
|
||||||
|
throw new IllegalStateException("Watcher was already watching for geometry update, but geometry was null");
|
||||||
|
}
|
||||||
|
this.processRequest(pos);//Force geometry request
|
||||||
|
if (((this.watcher.get(pos) & UPDATE_TYPE_BLOCK_BIT) == 0)||!this.nodeData.isNodeGeometryInFlight(nodeId)) {
|
||||||
|
throw new IllegalStateException("Watcher must be watching for geometry update");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Set the geometry to EMPTY while the geometry update request is executing
|
||||||
|
//throw new IllegalStateException();
|
||||||
|
Logger.error("Setting geometry to EMPTY while request is inflight");
|
||||||
|
//TODO: figure out a better way to mark this for tracing verificaction and like less confusion
|
||||||
|
// (instead of like EMPTY_GEOMETRY_ID do like INFLIGHT_GEOMETRY_ID)
|
||||||
|
this.nodeData.setNodeGeometry(nodeId, EMPTY_GEOMETRY_ID);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.nodeData.getChildPtr(nodeId) != SENTINEL_EMPTY_CHILD_PTR) {//This should only ever be the sentinal ptr
|
if (this.nodeData.getChildPtr(nodeId) != SENTINEL_EMPTY_CHILD_PTR) {//This should only ever be the sentinal ptr
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
@@ -546,10 +579,23 @@ public class NodeManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Recursivly fully removes all nodes and children
|
private void recurseRemoveChildNodes(long pos) {
|
||||||
|
this._recurseRemoveNode(pos, true);
|
||||||
|
}
|
||||||
|
|
||||||
private void recurseRemoveNode(long pos) {
|
private void recurseRemoveNode(long pos) {
|
||||||
|
this._recurseRemoveNode(pos, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Recursivly fully removes all nodes and children
|
||||||
|
private void _recurseRemoveNode(long pos, boolean onlyRemoveChildren) {
|
||||||
//NOTE: this also removes from the section map
|
//NOTE: this also removes from the section map
|
||||||
int nodeId = this.activeSectionMap.remove(pos);
|
int nodeId;
|
||||||
|
if (onlyRemoveChildren) {
|
||||||
|
nodeId = this.activeSectionMap.get(pos);
|
||||||
|
} else {
|
||||||
|
nodeId = this.activeSectionMap.remove(pos);
|
||||||
|
}
|
||||||
if (nodeId == -1) {
|
if (nodeId == -1) {
|
||||||
throw new IllegalStateException("Cannot remove pos that doesnt exist");
|
throw new IllegalStateException("Cannot remove pos that doesnt exist");
|
||||||
}
|
}
|
||||||
@@ -598,6 +644,10 @@ public class NodeManager {
|
|||||||
|
|
||||||
this.childRequests.release(reqId);//Release the request
|
this.childRequests.release(reqId);//Release the request
|
||||||
this.activeNodeRequestCount--;
|
this.activeNodeRequestCount--;
|
||||||
|
if (onlyRemoveChildren) {
|
||||||
|
this.nodeData.unmarkRequestInFlight(nodeId);
|
||||||
|
this.nodeData.setNodeRequest(nodeId, NULL_REQUEST_ID);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -605,8 +655,6 @@ public class NodeManager {
|
|||||||
// childRequest
|
// childRequest
|
||||||
// this is only valid if this node is an inner node
|
// this is only valid if this node is an inner node
|
||||||
|
|
||||||
//Logger.error("UNFINISHED OPERATION TODO: FIXME2");
|
|
||||||
|
|
||||||
//Only recursively delete if the node is not a leaf
|
//Only recursively delete if the node is not a leaf
|
||||||
if (type == NODE_TYPE_INNER) {
|
if (type == NODE_TYPE_INNER) {
|
||||||
//Verify child data
|
//Verify child data
|
||||||
@@ -662,8 +710,12 @@ public class NodeManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (onlyRemoveChildren) {
|
||||||
|
this.nodeData.setChildPtr(nodeId, -1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!onlyRemoveChildren) {
|
||||||
//Free geometry and related memory for this node
|
//Free geometry and related memory for this node
|
||||||
int geometry = this.nodeData.getNodeGeometry(nodeId);
|
int geometry = this.nodeData.getNodeGeometry(nodeId);
|
||||||
if (geometry != EMPTY_GEOMETRY_ID && geometry != NULL_GEOMETRY_ID)
|
if (geometry != EMPTY_GEOMETRY_ID && geometry != NULL_GEOMETRY_ID)
|
||||||
@@ -678,6 +730,10 @@ public class NodeManager {
|
|||||||
throw new IllegalStateException("Pos was not being watched");
|
throw new IllegalStateException("Pos was not being watched");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
//TODO: probably need this.clearId(nodeId);
|
||||||
|
this.invalidateNode(nodeId);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
Logger.error("UNFINISHED OPERATION TODO: FIXME3");
|
Logger.error("UNFINISHED OPERATION TODO: FIXME3");
|
||||||
//NOTE: There are request type singles and request type child!!!!
|
//NOTE: There are request type singles and request type child!!!!
|
||||||
@@ -717,7 +773,7 @@ public class NodeManager {
|
|||||||
this.childRequests.release(requestId);
|
this.childRequests.release(requestId);
|
||||||
|
|
||||||
//Update the parent
|
//Update the parent
|
||||||
this.nodeData.setNodeRequest(parentNodeId, NULL_REQUEST_ID);//TODO: create a better null request
|
this.nodeData.setNodeRequest(parentNodeId, NULL_REQUEST_ID);
|
||||||
this.nodeData.unmarkRequestInFlight(parentNodeId);
|
this.nodeData.unmarkRequestInFlight(parentNodeId);
|
||||||
this.activeNodeRequestCount--;
|
this.activeNodeRequestCount--;
|
||||||
|
|
||||||
@@ -906,7 +962,7 @@ public class NodeManager {
|
|||||||
public void processRequest(long pos) {
|
public void processRequest(long pos) {
|
||||||
int nodeId = this.activeSectionMap.get(pos);
|
int nodeId = this.activeSectionMap.get(pos);
|
||||||
if (nodeId == -1) {
|
if (nodeId == -1) {
|
||||||
Logger.error("Got request for pos " + WorldEngine.pprintPos(pos) + " but it was not in active map, ignoring!");
|
Logger.warn("Got request for pos " + WorldEngine.pprintPos(pos) + " but it was not in active map, ignoring!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int nodeType = nodeId&NODE_TYPE_MSK;
|
int nodeType = nodeId&NODE_TYPE_MSK;
|
||||||
@@ -967,18 +1023,8 @@ public class NodeManager {
|
|||||||
//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 {
|
||||||
//Dont mark node as having an inflight request
|
this.processInnerRequest(pos, nodeId);
|
||||||
|
|
||||||
//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");
|
|
||||||
// THis shouldent result in markRequestInFlight afak
|
|
||||||
|
|
||||||
if (!this.watcher.watch(pos, WorldEngine.UPDATE_TYPE_BLOCK_BIT)) {
|
|
||||||
//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
|
|
||||||
Logger.info("Node: " + nodeId + " at pos: " + WorldEngine.pprintPos(pos) + " got update request, but geometry was already being watched");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1029,9 +1075,130 @@ public class NodeManager {
|
|||||||
this.activeNodeRequestCount++;
|
this.activeNodeRequestCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//A request is received for an inner node position
|
||||||
|
private void processInnerRequest(long pos, int nodeId) {
|
||||||
|
//TODO: finish
|
||||||
|
int geo = this.nodeData.getNodeGeometry(nodeId);
|
||||||
|
if (VERIFY_NODE_MANAGER_OPERATIONS) {
|
||||||
|
boolean isWatchingUpdate = (this.watcher.get(pos)&UPDATE_TYPE_BLOCK_BIT)!=0;
|
||||||
|
boolean inflight = this.nodeData.isNodeGeometryInFlight(nodeId);
|
||||||
|
if (inflight && !isWatchingUpdate) {
|
||||||
|
throw new IllegalStateException();//If we have geometry request inflight we must be watching
|
||||||
|
}
|
||||||
|
if (geo != NULL_GEOMETRY_ID && inflight) {
|
||||||
|
//Having a EMPTY_GEOMETRY_ID and inflight is valid unfortunatly due to conditions when making an
|
||||||
|
// inner node into a leaf node when child existance is set to zero and it has no geometry
|
||||||
|
if (geo != EMPTY_GEOMETRY_ID)
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.nodeData.isNodeGeometryInFlight(nodeId)) {
|
||||||
|
if (!this.watcher.watch(pos, WorldEngine.UPDATE_TYPE_BLOCK_BIT)) {
|
||||||
|
Logger.info("Node: " + nodeId + " at pos: " + WorldEngine.pprintPos(pos) + " got update request, but geometry was already being watched");
|
||||||
|
} else {
|
||||||
|
this.nodeData.markNodeGeometryInFlight(nodeId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
//==================================================================================================================
|
//==================================================================================================================
|
||||||
// Used by the cleaning system to ensure memory capacity in the geometry store
|
// Used by the cleaning system to ensure memory capacity in the geometry store
|
||||||
|
|
||||||
|
//TODO: Think plan for this is to add new flag to NodeStore to indicate if geometry mesh request is inflight
|
||||||
|
// this used for state verification and not emitting/assuming things
|
||||||
|
// e.g. current issue is if an inner node wants/needs to convert into a leaf node, but the inner node has no geometry
|
||||||
|
// how to deal with that?? e.g. inner node geometry gets cleared but then the childExistance gets set to 0
|
||||||
|
// it needs to become a leaf node
|
||||||
|
|
||||||
public void removeNodeGeometry(long pos) {
|
public void removeNodeGeometry(long pos) {
|
||||||
|
int nodeId = this.activeSectionMap.get(pos);
|
||||||
|
if (nodeId == -1) {
|
||||||
|
Logger.warn("Got geometry removal for pos " + WorldEngine.pprintPos(pos) + " but it was not in active map, ignoring!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int nodeType = nodeId&NODE_TYPE_MSK;
|
||||||
|
nodeId &= NODE_ID_MSK;
|
||||||
|
if (nodeType == NODE_TYPE_REQUEST) {
|
||||||
|
Logger.error("Tried removing geometry for pos: " + WorldEngine.pprintPos(pos) + " but its type was a request, ignoring!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (nodeType == NODE_TYPE_INNER) {
|
||||||
|
this.clearGeometryInternal(pos, nodeId);
|
||||||
|
this.clearId(nodeId);
|
||||||
|
} else {//NODE_TYPE_LEAF
|
||||||
|
//TODO: here we need to make the parent node a leaf node...
|
||||||
|
// TODO? think about maybe only doing it if all children of the parent are leaf nodes aswell
|
||||||
|
|
||||||
|
if (this.topLevelNodes.contains(pos)) {
|
||||||
|
//We are asked to remove the geometry of a top level leaf node, which we cannot do
|
||||||
|
int geo = this.nodeData.getNodeGeometry(nodeId);
|
||||||
|
if (geo == NULL_GEOMETRY_ID || geo == EMPTY_GEOMETRY_ID) {
|
||||||
|
//If its null or empty we can "ignore" the request
|
||||||
|
} else {
|
||||||
|
Logger.warn("Tried removing geometry from top level node which is not allowed, disregarding request");
|
||||||
|
//TODO: probably do
|
||||||
|
//this.clearId(nodeId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
this.processInnerGeometryRemoval(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processInnerGeometryRemoval(long cPos) {
|
||||||
|
long pPos = makeParentPos(cPos);
|
||||||
|
int pId = this.activeSectionMap.get(pPos);
|
||||||
|
if (pId == -1) throw new IllegalStateException("Parent node must exist");
|
||||||
|
if ((pId & NODE_TYPE_MSK) != NODE_TYPE_INNER)
|
||||||
|
throw new IllegalStateException("Parent node must be an inner node");
|
||||||
|
pId &= NODE_ID_MSK;
|
||||||
|
|
||||||
|
int pGeo = this.nodeData.getNodeGeometry(pId);
|
||||||
|
if (pGeo == NULL_GEOMETRY_ID) {
|
||||||
|
//We cannot make the parent a leaf node with null geometry
|
||||||
|
this.processRequest(pPos);//Request geometry
|
||||||
|
} else {
|
||||||
|
//Convert to leaf node
|
||||||
|
this.recurseRemoveChildNodes(pPos);//TODO: make this download/fetch the data instead of just deleting it
|
||||||
|
this.clearId(pId);
|
||||||
|
|
||||||
|
int old = this.activeSectionMap.put(pPos, NODE_TYPE_LEAF|pId);
|
||||||
|
if (old == -1)
|
||||||
|
throw new IllegalStateException();
|
||||||
|
if ((old&NODE_TYPE_MSK)!=NODE_TYPE_INNER || (old&NODE_ID_MSK)!=pId)
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearGeometryInternal(long pos, int nodeId) {
|
||||||
|
int geometryId = this.nodeData.getNodeGeometry(nodeId);
|
||||||
|
|
||||||
|
//TODO: if isNodeGeometryInFlight is true and geometryId == NULL_GEOMETRY_ID, probably need to
|
||||||
|
// unwatch from watcher and unmark
|
||||||
|
|
||||||
|
if (geometryId != NULL_GEOMETRY_ID && geometryId != EMPTY_GEOMETRY_ID) {
|
||||||
|
//Unwatch node geometry changes
|
||||||
|
if (this.watcher.unwatch(pos, WorldEngine.UPDATE_TYPE_BLOCK_BIT)) {
|
||||||
|
throw new IllegalStateException("Unwatching position for geometry removal at: " + WorldEngine.pprintPos(pos) + " resulted in full removal");
|
||||||
|
}
|
||||||
|
//Remove geometry and set to null
|
||||||
|
this.geometryManager.downloadAndRemove(geometryId, section->{
|
||||||
|
//TODO: download and remove instead of just removing, and store in ram cache for later!!
|
||||||
|
section.free();
|
||||||
|
});
|
||||||
|
this.nodeData.setNodeGeometry(nodeId, NULL_GEOMETRY_ID);
|
||||||
|
this.invalidateNode(nodeId);//Only need to invalidate on change
|
||||||
|
this.nodeData.unmarkNodeGeometryInFlight(nodeId);//Remove geometry inflight as well, its removed
|
||||||
|
} else {
|
||||||
|
if (geometryId == NULL_GEOMETRY_ID) {
|
||||||
|
//Logger.info("Tried removing geometry of internal node but geometry was null");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
public void removeNodeGeometryOld(long pos) {
|
||||||
int nodeId = this.activeSectionMap.get(pos);
|
int nodeId = this.activeSectionMap.get(pos);
|
||||||
if (nodeId == -1) {
|
if (nodeId == -1) {
|
||||||
Logger.error("Got geometry removal for pos " + WorldEngine.pprintPos(pos) + " but it was not in active map, ignoring!");
|
Logger.error("Got geometry removal for pos " + WorldEngine.pprintPos(pos) + " but it was not in active map, ignoring!");
|
||||||
@@ -1110,6 +1277,7 @@ public class NodeManager {
|
|||||||
}
|
}
|
||||||
this.invalidateNode(nodeId);
|
this.invalidateNode(nodeId);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
//==================================================================================================================
|
//==================================================================================================================
|
||||||
public boolean writeChanges(GlBuffer nodeBuffer) {
|
public boolean writeChanges(GlBuffer nodeBuffer) {
|
||||||
@@ -1166,8 +1334,8 @@ public class NodeManager {
|
|||||||
|
|
||||||
private long makeParentPos(long pos) {
|
private long makeParentPos(long pos) {
|
||||||
int lvl = WorldEngine.getLevel(pos);
|
int lvl = WorldEngine.getLevel(pos);
|
||||||
if (lvl == MAX_LOD_LAYERS-1) {
|
if (lvl == MAX_LOD_LAYER) {
|
||||||
throw new IllegalArgumentException("Cannot create a parent higher than LoD " + (MAX_LOD_LAYERS-1));
|
throw new IllegalArgumentException("Cannot create a parent higher than LoD " + (MAX_LOD_LAYER));
|
||||||
}
|
}
|
||||||
return WorldEngine.getWorldSectionId(lvl+1,
|
return WorldEngine.getWorldSectionId(lvl+1,
|
||||||
WorldEngine.getX(pos)>>1,
|
WorldEngine.getX(pos)>>1,
|
||||||
@@ -1227,6 +1395,9 @@ public class NodeManager {
|
|||||||
if (node == -1) {
|
if (node == -1) {
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
|
if (this.watcher.get(pos) == 0) {//Watcher must always be watching the node
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
if (!seenPositions.add(pos))
|
if (!seenPositions.add(pos))
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
|
|
||||||
@@ -1266,6 +1437,27 @@ public class NodeManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
boolean hasGeometry = this.nodeData.getNodeGeometry(node) != NULL_GEOMETRY_ID;
|
||||||
|
boolean watchingGeo = (this.watcher.get(pos)&UPDATE_TYPE_BLOCK_BIT)!=0;
|
||||||
|
boolean awaitingGeo = this.nodeData.isNodeGeometryInFlight(node);
|
||||||
|
//There must either be geometry or waiting for geometry if is watching
|
||||||
|
if ((hasGeometry||awaitingGeo) != watchingGeo)
|
||||||
|
throw new IllegalStateException();
|
||||||
|
//Cannot be awaiting geometry and have it
|
||||||
|
if (hasGeometry && awaitingGeo) {
|
||||||
|
//We assume if the geometry is EMPTY, that what happened was an inner node just got convertex into a leaf node and is now awaiting its geometry
|
||||||
|
//if (type != NODE_TYPE_LEAF || this.nodeData.getNodeGeometry(node) != EMPTY_GEOMETRY_ID)
|
||||||
|
// throw new IllegalStateException();
|
||||||
|
//HOWEVER, what can happen is that before we recieve the geometry for the node, thus clearing the geometryInFlight
|
||||||
|
// is that we get a request and childexistance change and all the children recieved,
|
||||||
|
// thus causing the node to become an INNER node again ;-;
|
||||||
|
|
||||||
|
//So just... sigh, just check that the geometry is not empty...
|
||||||
|
if (this.nodeData.getNodeGeometry(node) != EMPTY_GEOMETRY_ID)
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
}
|
||||||
//if (this.nodeData.getNodeType(node) != type) {
|
//if (this.nodeData.getNodeType(node) != type) {
|
||||||
// throw new IllegalStateException();
|
// throw new IllegalStateException();
|
||||||
//}
|
//}
|
||||||
|
|||||||
@@ -191,7 +191,6 @@ public final class NodeStore {
|
|||||||
public void markRequestInFlight(int nodeId) {
|
public void markRequestInFlight(int nodeId) {
|
||||||
this.localNodeData[id2idx(nodeId)+1] |= 1L<<63;
|
this.localNodeData[id2idx(nodeId)+1] |= 1L<<63;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unmarkRequestInFlight(int nodeId) {
|
public void unmarkRequestInFlight(int nodeId) {
|
||||||
this.localNodeData[id2idx(nodeId)+1] &= ~(1L<<63);
|
this.localNodeData[id2idx(nodeId)+1] &= ~(1L<<63);
|
||||||
}
|
}
|
||||||
@@ -199,6 +198,16 @@ public final class NodeStore {
|
|||||||
return ((this.localNodeData[id2idx(nodeId)+1]>>63)&1)!=0;
|
return ((this.localNodeData[id2idx(nodeId)+1]>>63)&1)!=0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void markNodeGeometryInFlight(int nodeId) {
|
||||||
|
this.localNodeData[id2idx(nodeId)+1] |= 1L<<59;
|
||||||
|
}
|
||||||
|
public void unmarkNodeGeometryInFlight(int nodeId) {
|
||||||
|
this.localNodeData[id2idx(nodeId)+1] &= ~(1L<<59);
|
||||||
|
}
|
||||||
|
public boolean isNodeGeometryInFlight(int nodeId) {
|
||||||
|
return (this.localNodeData[id2idx(nodeId)+1]&(1L<<59))!=0;
|
||||||
|
}
|
||||||
|
|
||||||
public int getNodeType(int nodeId) {
|
public int getNodeType(int nodeId) {
|
||||||
return (int)((this.localNodeData[id2idx(nodeId)+1]>>61)&3)<<30;
|
return (int)((this.localNodeData[id2idx(nodeId)+1]>>61)&3)<<30;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,21 @@
|
|||||||
package me.cortex.voxy.client.core.rendering.hierachical;
|
package me.cortex.voxy.client.core.rendering.hierachical;
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.Stack;
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap;
|
import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap;
|
||||||
import it.unimi.dsi.fastutil.longs.Long2IntFunction;
|
import it.unimi.dsi.fastutil.longs.Long2IntFunction;
|
||||||
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
|
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
|
||||||
import me.cortex.voxy.client.core.rendering.ISectionWatcher;
|
import me.cortex.voxy.client.core.rendering.ISectionWatcher;
|
||||||
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.SectionUpdateRouter;
|
|
||||||
import me.cortex.voxy.client.core.rendering.section.AbstractSectionGeometryManager;
|
import me.cortex.voxy.client.core.rendering.section.AbstractSectionGeometryManager;
|
||||||
import me.cortex.voxy.common.Logger;
|
import me.cortex.voxy.common.Logger;
|
||||||
import me.cortex.voxy.common.util.HierarchicalBitSet;
|
import me.cortex.voxy.common.util.HierarchicalBitSet;
|
||||||
import me.cortex.voxy.common.util.MemoryBuffer;
|
import me.cortex.voxy.common.util.MemoryBuffer;
|
||||||
import me.cortex.voxy.common.world.WorldEngine;
|
import me.cortex.voxy.common.world.WorldEngine;
|
||||||
import me.cortex.voxy.common.world.WorldSection;
|
|
||||||
import org.lwjgl.system.MemoryUtil;
|
import org.lwjgl.system.MemoryUtil;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.IntSupplier;
|
|
||||||
import java.util.stream.IntStream;
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
import static me.cortex.voxy.common.world.WorldEngine.*;
|
import static me.cortex.voxy.common.world.WorldEngine.*;
|
||||||
@@ -71,7 +67,7 @@ public class TestNodeManager {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void downloadAndRemove(int id, Consumer<BuiltSection> callback) {
|
public void downloadAndRemove(int id, Consumer<BuiltSection> callback) {
|
||||||
throw new IllegalStateException();
|
this.removeSection(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -116,6 +112,11 @@ public class TestNodeManager {
|
|||||||
return newTypes == 0;//Returns true on removal
|
return newTypes == 0;//Returns true on removal
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int get(long position) {
|
||||||
|
return this.updateTypes.getOrDefault(position, (byte) 0);
|
||||||
|
}
|
||||||
|
|
||||||
private static String[] getPrettyTypes(int msk) {
|
private static String[] getPrettyTypes(int msk) {
|
||||||
if ((msk&~UPDATE_FLAGS)!=0) {
|
if ((msk&~UPDATE_FLAGS)!=0) {
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
@@ -234,26 +235,38 @@ public class TestNodeManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static class Node {
|
||||||
|
private final long pos;
|
||||||
|
private final Node[] children = new Node[8];
|
||||||
|
private byte childExistenceMask;
|
||||||
|
private boolean hasMesh;
|
||||||
|
private Node(long pos) {
|
||||||
|
this.pos = pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
Logger.INSERT_CLASS = false;
|
Logger.INSERT_CLASS = false;
|
||||||
int ITER_COUNT = 5_000;
|
int ITER_COUNT = 5000;
|
||||||
int INNER_ITER_COUNT = 100_000;
|
int INNER_ITER_COUNT = 100_000;
|
||||||
|
boolean GEO_REM = true;
|
||||||
|
|
||||||
AtomicInteger finished = new AtomicInteger();
|
AtomicInteger finished = new AtomicInteger();
|
||||||
HashSet<List<StackTraceElement>> seenTraces = new HashSet<>();
|
HashSet<List<StackTraceElement>> seenTraces = new HashSet<>();
|
||||||
|
|
||||||
Logger.SHUTUP = true;
|
Logger.SHUTUP = true;
|
||||||
|
|
||||||
if (false) {
|
if (true) {
|
||||||
for (int q = 0; q < ITER_COUNT; q++) {
|
for (int q = 0; q < ITER_COUNT; q++) {
|
||||||
//Logger.info("Iteration "+ q);
|
//Logger.info("Iteration "+ q);
|
||||||
if (runTest(INNER_ITER_COUNT, q, seenTraces)) {
|
if (runTest(INNER_ITER_COUNT, q, seenTraces, GEO_REM)) {
|
||||||
finished.incrementAndGet();
|
finished.incrementAndGet();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
IntStream.range(0, ITER_COUNT).parallel().forEach(i->{
|
IntStream.range(0, ITER_COUNT).parallel().forEach(i->{
|
||||||
if (runTest(INNER_ITER_COUNT, i, seenTraces)) {
|
if (runTest(INNER_ITER_COUNT, i, seenTraces, GEO_REM)) {
|
||||||
finished.incrementAndGet();
|
finished.incrementAndGet();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -269,7 +282,7 @@ public class TestNodeManager {
|
|||||||
return WorldEngine.getWorldSectionId(lvl, r.nextInt(bound), r.nextInt(bound), r.nextInt(bound));
|
return WorldEngine.getWorldSectionId(lvl, r.nextInt(bound), r.nextInt(bound), r.nextInt(bound));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean runTest(int ITERS, int testIdx, Set<List<StackTraceElement>> traces) {
|
private static boolean runTest(int ITERS, int testIdx, Set<List<StackTraceElement>> traces, boolean geoRemoval) {
|
||||||
long POS_A = WorldEngine.getWorldSectionId(4, 0, 0, 0);
|
long POS_A = WorldEngine.getWorldSectionId(4, 0, 0, 0);
|
||||||
|
|
||||||
Random r = new Random(testIdx * 1234L);
|
Random r = new Random(testIdx * 1234L);
|
||||||
@@ -279,7 +292,7 @@ public class TestNodeManager {
|
|||||||
test.putTopPos(POS_A);
|
test.putTopPos(POS_A);
|
||||||
for (int i = 0; i < ITERS; i++) {
|
for (int i = 0; i < ITERS; i++) {
|
||||||
long pos = rPos(r);
|
long pos = rPos(r);
|
||||||
int op = r.nextInt(3);
|
int op = r.nextInt(4);
|
||||||
int extra = r.nextInt(256);
|
int extra = r.nextInt(256);
|
||||||
boolean hasGeometry = r.nextBoolean();
|
boolean hasGeometry = r.nextBoolean();
|
||||||
if (op == 0) {
|
if (op == 0) {
|
||||||
@@ -291,6 +304,9 @@ public class TestNodeManager {
|
|||||||
if (op == 2) {
|
if (op == 2) {
|
||||||
test.meshUpdate(pos, extra, hasGeometry ? 100 : 0);
|
test.meshUpdate(pos, extra, hasGeometry ? 100 : 0);
|
||||||
}
|
}
|
||||||
|
if (op == 3 && geoRemoval) {
|
||||||
|
test.nodeManager.removeNodeGeometry(pos);
|
||||||
|
}
|
||||||
test.printNodeChanges();
|
test.printNodeChanges();
|
||||||
test.verifyIntegrity();
|
test.verifyIntegrity();
|
||||||
}
|
}
|
||||||
@@ -525,8 +541,8 @@ public class TestNodeManager {
|
|||||||
|
|
||||||
private long makeParentPos(long pos) {
|
private long makeParentPos(long pos) {
|
||||||
int lvl = WorldEngine.getLevel(pos);
|
int lvl = WorldEngine.getLevel(pos);
|
||||||
if (lvl == MAX_LOD_LAYERS-1) {
|
if (lvl == MAX_LOD_LAYER) {
|
||||||
throw new IllegalArgumentException("Cannot create a parent higher than LoD " + (MAX_LOD_LAYERS-1));
|
throw new IllegalArgumentException("Cannot create a parent higher than LoD " + (MAX_LOD_LAYER));
|
||||||
}
|
}
|
||||||
return WorldEngine.getWorldSectionId(lvl+1,
|
return WorldEngine.getWorldSectionId(lvl+1,
|
||||||
WorldEngine.getX(pos)>>1,
|
WorldEngine.getX(pos)>>1,
|
||||||
|
|||||||
Reference in New Issue
Block a user