Continue work

This commit is contained in:
mcrcortex
2024-08-16 16:22:22 +10:00
parent d4714989b4
commit 306956839a
8 changed files with 211 additions and 60 deletions

View File

@@ -3,7 +3,7 @@ package me.cortex.voxy.client.core.rendering;
import me.cortex.voxy.client.core.model.ModelBakerySubsystem;
import me.cortex.voxy.client.core.model.ModelStore;
import me.cortex.voxy.client.core.rendering.building.RenderGenerationService;
import me.cortex.voxy.client.core.rendering.building.SectionPositionUpdateFilterer;
import me.cortex.voxy.client.core.rendering.building.SectionUpdateRouter;
import me.cortex.voxy.client.core.rendering.building.SectionUpdate;
import me.cortex.voxy.client.core.rendering.hierachical2.HierarchicalNodeManager;
import me.cortex.voxy.client.core.rendering.hierachical2.HierarchicalOcclusionTraverser;
@@ -47,7 +47,7 @@ public class RenderService<T extends AbstractSectionRenderer<J, ?>, J extends Vi
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 positionFilterForwarder = new SectionPositionUpdateFilterer();
var positionFilterForwarder = new SectionUpdateRouter();
this.nodeManager = new HierarchicalNodeManager(1<<21, this.sectionRenderer.getGeometryManager(), positionFilterForwarder);

View File

@@ -105,8 +105,10 @@ public class RenderGenerationService {
byte childMask = section.getNonEmptyChildren();
section.release();
//Time is the time at the start of the update
this.resultConsumer.accept(new SectionUpdate(section.key, time, mesh, childMask));
if (mesh != null) {
//Time is the time at the start of the update
this.resultConsumer.accept(new SectionUpdate(section.key, time, mesh, childMask));
}
}
public void enqueueTask(int lvl, int x, int y, int z) {

View File

@@ -6,7 +6,7 @@ import me.cortex.voxy.common.world.WorldSection;
import java.util.function.LongConsumer;
public class SectionPositionUpdateFilterer {
public class SectionUpdateRouter {
private static final int SLICES = 1<<2;
public interface IChildUpdate {void accept(WorldSection section);}

View File

@@ -4,7 +4,7 @@ package me.cortex.voxy.client.core.rendering.hierachical2;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
import me.cortex.voxy.client.core.rendering.building.BuiltSection;
import me.cortex.voxy.client.core.rendering.building.SectionPositionUpdateFilterer;
import me.cortex.voxy.client.core.rendering.building.SectionUpdateRouter;
import me.cortex.voxy.client.core.rendering.building.SectionUpdate;
import me.cortex.voxy.client.core.rendering.section.AbstractSectionGeometryManager;
import me.cortex.voxy.client.core.util.ExpandingObjectAllocationList;
@@ -12,15 +12,17 @@ import me.cortex.voxy.common.world.WorldEngine;
import me.jellysquid.mods.sodium.client.util.MathUtil;
import org.lwjgl.system.MemoryUtil;
import static me.cortex.voxy.client.core.rendering.hierachical2.NodeStore.EMPTY_GEOMETRY_ID;
import static me.cortex.voxy.client.core.rendering.hierachical2.NodeStore.NODE_ID_MSK;
//Contains no logic to interface with the gpu, nor does it contain any gpu buffers
public class HierarchicalNodeManager {
public static final int NODE_MSK = ((1<<24)-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_NONE = 0;
private static final int ID_TYPE_LEAF = (2<<30);
private static final int ID_TYPE_REQUEST = (2<<30);
private static final int ID_TYPE_TOP = (1<<30);
public final int maxNodeCount;
@@ -33,9 +35,9 @@ public class HierarchicalNodeManager {
private final ExpandingObjectAllocationList<NodeChildRequest> requests = new ExpandingObjectAllocationList<>(NodeChildRequest[]::new);
private final AbstractSectionGeometryManager geometryManager;
private final SectionPositionUpdateFilterer updateFilterer;
private final SectionUpdateRouter updateRouter;
public HierarchicalNodeManager(int maxNodeCount, AbstractSectionGeometryManager geometryManager, SectionPositionUpdateFilterer updateFilterer) {
public HierarchicalNodeManager(int maxNodeCount, AbstractSectionGeometryManager geometryManager, SectionUpdateRouter updateRouter) {
if (!MathUtil.isPowerOfTwo(maxNodeCount)) {
throw new IllegalArgumentException("Max node count must be a power of 2");
}
@@ -43,7 +45,7 @@ public class HierarchicalNodeManager {
throw new IllegalArgumentException("Max node count cannot exceed 2^24");
}
this.activeSectionMap.defaultReturnValue(NO_NODE);
this.updateFilterer = updateFilterer;
this.updateRouter = updateRouter;
this.maxNodeCount = maxNodeCount;
this.nodeData = new NodeStore(maxNodeCount);
this.geometryManager = geometryManager;
@@ -54,15 +56,13 @@ public class HierarchicalNodeManager {
throw new IllegalArgumentException("Position already in node set: " + WorldEngine.pprintPos(position));
}
this.activeSectionMap.put(position, SENTINAL_TOP_NODE_INFLIGHT);
this.updateFilterer.watch(position);
this.updateRouter.watch(position);
}
public void removeTopLevelNode(long position) {
if (!this.activeSectionMap.containsKey(position)) {
throw new IllegalArgumentException("Position not in node set: " + WorldEngine.pprintPos(position));
}
}
public void processRequestQueue(int count, long ptr) {
@@ -73,7 +73,7 @@ public class HierarchicalNodeManager {
}
private void processRequest(int op) {
int node = op&NODE_MSK;
int node = op& NODE_ID_MSK;
if (!this.nodeData.nodeExists(node)) {
throw new IllegalStateException("Tried processing a node that doesnt exist: " + node);
}
@@ -109,12 +109,12 @@ public class HierarchicalNodeManager {
long childPos = makeChildPos(pos, i);
request.addChildRequirement(i);
//Insert all the children into the tracking map with the node id
if (this.activeSectionMap.put(childPos, requestId|ID_TYPE_LEAF) != NO_NODE) {
if (this.activeSectionMap.put(childPos, requestId|ID_TYPE_REQUEST) != NO_NODE) {
throw new IllegalStateException("Leaf request creation failed to insert child into map as a mapping already existed for the node!");
}
//Watch and request the child node at the given position
if (!this.updateFilterer.watch(childPos)) {
if (!this.updateRouter.watch(childPos)) {
throw new IllegalStateException("Failed to watch childPos");
}
}
@@ -123,6 +123,64 @@ public class HierarchicalNodeManager {
}
private void removeSectionInternal(long position) {
int node = this.activeSectionMap.remove(position);
if (node == NO_NODE) {
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);
node &= ~ID_TYPE_MSK;
if (type == ID_TYPE_REQUEST) {
//TODO: THIS
} else if (type == ID_TYPE_NONE || type == ID_TYPE_TOP) {
if (!this.nodeData.nodeExists(node)) {
throw new IllegalStateException("Section in active map but not in node data");
}
if (this.nodeData.isNodeRequestInFlight(node)) {
int requestId = this.nodeData.getNodeRequest(node);
var request = this.requests.get(requestId);
if (request.getPosition() != position) {
throw new IllegalStateException("Position != request.position");
}
//Recurse into all child requests and remove them, free any geometry along the way
//this.removeSectionInternal(position)
}
//Recurse into all allocated, children and remove
int children = this.nodeData.getChildPtr(node);
if (children != NO_NODE) {
int count = Integer.bitCount(Byte.toUnsignedInt(this.nodeData.getNodeChildExistence(node)));
for (int i = 0; i < count; i++) {
int cid = children + i;
if (!this.nodeData.nodeExists(cid)) {
throw new IllegalStateException("Child node doesnt exist!");
}
}
}
int geometry = this.nodeData.getNodeGeometry(node);
if (geometry != EMPTY_GEOMETRY_ID) {
this.geometryManager.removeSection(geometry);
}
}
//After its been removed, if its _not_ a top level node or inflight request but just a normal node,
// go up to parent and remove node from the parent allocation and free node id
}
}
//TODO: need to add a flag that says should do geometry uploads or something I.E. need to watch geometry
// and existance updates seperatly, since cull geometry
public void processResult(SectionUpdate update) {
//Need to handle cases
// geometry update, leaf node, leaf request node, internal node
@@ -132,6 +190,9 @@ public class HierarchicalNodeManager {
// when mesh result, need to remove the old child allocation block and make a new block to fit the
// new count of children
//If the sections child existance bits fully empty, then the section should be removed
final long position = update.position();
final var geometryData = update.geometry();
int nodeId = this.activeSectionMap.get(position);
@@ -164,10 +225,13 @@ public class HierarchicalNodeManager {
} else {
int type = (nodeId & ID_TYPE_MSK);
nodeId &= ~ID_TYPE_MSK;
if (type == ID_TYPE_LEAF) {
this.leafDataUpdate(nodeId, update);
if (type == ID_TYPE_REQUEST) {
this.requestDataUpdate(nodeId, update);
} else if (type == ID_TYPE_NONE || type == ID_TYPE_TOP) {
//Not part of a request, just a node update
//Not part of a request, just a node update,
//NOTE! be aware that if its an existance update and there is a request attached, need to check if the updated
// request becomes finished!!
} else {
throw new IllegalStateException("Should not reach here");
}
@@ -181,17 +245,33 @@ public class HierarchicalNodeManager {
this.nodeData.setNodeChildExistence(node, childExistence);
}
private void leafDataUpdate(int nodeId, SectionUpdate update) {
private void requestDataUpdate(int nodeId, SectionUpdate update) {
var request = this.requests.get(nodeId);
//Update for section part of a request, the request may be a leaf request update or an inner node update
if (request.isSatisfied()) {
this.processFinishedNodeChildRequest(nodeId, request);
}
}
//Process NodeChildRequest results
private void processFinishedNodeChildRequest(int parent, NodeChildRequest request) {
int children = this.nodeData.getChildPtr(parent);
if (children != NO_NODE) {
//There are children already part of this node, so need to reallocate all the children
int count = Integer.bitCount(Byte.toUnsignedInt(this.nodeData.getNodeChildExistence(parent)));
} else {
}
}
private int updateNodeGeometry(int node, BuiltSection geometry) {
int previousGeometry = this.nodeData.getNodeGeometry(node);
int newGeometry = -1;
if (previousGeometry != -1) {
int newGeometry = EMPTY_GEOMETRY_ID;
if (previousGeometry != EMPTY_GEOMETRY_ID) {
if (!geometry.isEmpty()) {
newGeometry = this.geometryManager.uploadReplaceSection(previousGeometry, geometry);
} else {
@@ -209,17 +289,13 @@ public class HierarchicalNodeManager {
}
if (previousGeometry == newGeometry) {
return 0;//No change
} else if (previousGeometry == -1) {
} else if (previousGeometry == EMPTY_GEOMETRY_ID) {
return 1;//Became non-empty
} else {
return 2;//Became empty
}
}
private void createSingleNode() {
}
private static int getChildIdx(long pos) {
int x = WorldEngine.getX(pos);
int y = WorldEngine.getY(pos);

View File

@@ -40,6 +40,7 @@ public class HierarchicalOcclusionTraverser {
}
public static int HACKY_SECTION_COUNT = 0;
public void doTraversal(Viewport<?> viewport, int depthBuffer) {
//Compute the mip chain
this.hiZBuffer.buildMipChain(depthBuffer, viewport.width, viewport.height);
@@ -50,7 +51,6 @@ public class HierarchicalOcclusionTraverser {
//Use a chain of glDispatchComputeIndirect (5 times) with alternating read/write buffers
// TODO: swap to persistent gpu thread instead
/*
if (HACKY_SECTION_COUNT != 0) {
long uploadPtr = UploadStream.INSTANCE.upload(this.renderList, 0, HACKY_SECTION_COUNT*4L+4);
@@ -61,7 +61,6 @@ public class HierarchicalOcclusionTraverser {
UploadStream.INSTANCE.commit();
}
*/
this.downloadResetRequestQueue();
}

View File

@@ -66,4 +66,8 @@ class NodeChildRequest {
public boolean isSatisfied() {
return (this.results&this.mask)==this.mask;
}
public long getPosition() {
return this.nodePos;
}
}

View File

@@ -3,11 +3,22 @@ package me.cortex.voxy.client.core.rendering.hierachical2;
import me.cortex.voxy.common.util.HierarchicalBitSet;
public final class NodeStore {
public static final int EMPTY_GEOMETRY_ID = -1;
public static final int NODE_ID_MSK = ((1<<24)-1);
public static final int REQUEST_ID_MSK = ((1<<16)-1);
public static final int GEOMETRY_ID_MSK = (1<<24)-1;
public static final int MAX_GEOMETRY_ID = GEOMETRY_ID_MSK-1;
private static final int SENTINEL_EMPTY_GEOMETRY_ID = GEOMETRY_ID_MSK;
private static final int SENTINEL_NULL_NODE_ID = NODE_ID_MSK -1;
private static final int SENTINEL_REQUEST_ID = REQUEST_ID_MSK -1;
private static final int LONGS_PER_NODE = 4;
private static final int INCREMENT_SIZE = 1<<16;
private final HierarchicalBitSet allocationSet;
private long[] localNodeData;
public NodeStore(int maxNodeCount) {
if (maxNodeCount>=SENTINEL_NULL_NODE_ID) {
throw new IllegalArgumentException("Max count too large");
}
//Initial count is 1024
this.localNodeData = new long[INCREMENT_SIZE*LONGS_PER_NODE];
this.allocationSet = new HierarchicalBitSet(maxNodeCount);
@@ -54,20 +65,28 @@ public final class NodeStore {
}
private void free(int nodeId) {
if (!this.allocationSet.free(nodeId)) {
throw new IllegalStateException("Node " + nodeId + " was not allocated!");
}
this.free(nodeId, 1);
}
private void free(int baseNodeId, int count) {
for (int i = 0; i < count; i++) {
int nodeId = baseNodeId + i;
if (!this.allocationSet.free(nodeId)) {
throw new IllegalStateException("Node " + nodeId + " was not allocated!");
}
}
}
private void clear(int nodeId) {
int idx = id2idx(nodeId);
this.localNodeData[idx] = -1;//Position
this.localNodeData[idx+1] = 0;
this.localNodeData[idx+2] = 0;
this.localNodeData[idx+3] = 0;
}
public void setNodePosition(int node, long position) {
this.localNodeData[id2idx(node)] = position;
}
@@ -81,40 +100,88 @@ public final class NodeStore {
}
public int getNodeGeometry(int node) {
return -1;
long data = this.localNodeData[id2idx(node)+1];
int geometryPtr = (int) (data&GEOMETRY_ID_MSK);
if (geometryPtr == SENTINEL_EMPTY_GEOMETRY_ID) {
return -1;
}
return geometryPtr;
}
public void setNodeGeometry(int node, int geometryId) {
if (geometryId>MAX_GEOMETRY_ID || geometryId<-1) {
throw new IllegalArgumentException("Geometry ptr greater than MAX_GEOMETRY_ID or less than -1 : " + geometryId);
}
if (geometryId == -1) {
geometryId = SENTINEL_EMPTY_GEOMETRY_ID;
}
}
public void setNodeRequest(int node, int requestId) {
}
public void markRequestInFlight(int nodeId) {
}
public boolean isNodeRequestInFlight(int nodeId) {
return false;
}
public boolean isLeafNode(int nodeId) {
return false;
}
public byte getNodeChildExistence(int nodeId) {return 0;}
public void setNodeChildExistence(int node, byte existence) {
int idx = id2idx(node)+1;
long data = this.localNodeData[idx];
data &= ~GEOMETRY_ID_MSK;
data |= geometryId;
this.localNodeData[idx] = data;
}
public int getChildPtr(int nodeId) {
return -1;
long data = this.localNodeData[id2idx(nodeId)+1];
int nodePtr = (int) ((data>>24)&NODE_ID_MSK);
if (nodePtr == SENTINEL_NULL_NODE_ID) {
return -1;
}
return nodePtr;
}
public void setChildPtr(int nodeId, int ptr) {
if (ptr>=NODE_ID_MSK || ptr<-1) {
throw new IllegalArgumentException("Node child ptr greater GEQ NODE_ID_MSK or less than -1 : " + ptr);
}
if (ptr == -1) {
ptr = SENTINEL_NULL_NODE_ID;
}
int idx = id2idx(nodeId)+1;
long data = this.localNodeData[idx];
data &= ~(((long)NODE_ID_MSK)<<24);
data |= ((long)ptr)<<24;
this.localNodeData[idx] = data;
}
public void setNodeRequest(int node, int requestId) {
int id = id2idx(node)+2;
long data = this.localNodeData[id];
data &= ~REQUEST_ID_MSK;
data |= requestId;
this.localNodeData[id] = data;
}
public int getNodeRequest(int node) {
return (int) (this.localNodeData[id2idx(node)+2]&REQUEST_ID_MSK);
}
public void markRequestInFlight(int nodeId) {
this.localNodeData[id2idx(nodeId)+1] |= 1L<<63;
}
public boolean isNodeRequestInFlight(int nodeId) {
return ((this.localNodeData[id2idx(nodeId)+1]>>63)&1)!=0;
}
public boolean isLeafNode(int nodeId) {
return ((this.localNodeData[id2idx(nodeId)+1]>>62)&1)!=0;
}
public byte getNodeChildExistence(int nodeId) {
long data = this.localNodeData[id2idx(nodeId)+1];
return (byte) ((data>>48)&0xFF);
}
public void setNodeChildExistence(int nodeId, byte existence) {
int idx = id2idx(nodeId)+1;
long data = this.localNodeData[idx];
data &= ~(0xFFL<<48);
data |= Byte.toUnsignedLong(existence)<<48;
this.localNodeData[idx] = data;
}
//Writes out a nodes data to the ptr in the compacted/reduced format

View File

@@ -5,6 +5,7 @@ import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import me.cortex.voxy.client.core.gl.GlBuffer;
import me.cortex.voxy.client.core.rendering.building.BuiltSection;
import me.cortex.voxy.client.core.rendering.hierachical2.HierarchicalOcclusionTraverser;
import me.cortex.voxy.client.core.rendering.util.BufferArena;
import me.cortex.voxy.client.core.rendering.util.UploadStream;
import me.cortex.voxy.common.util.HierarchicalBitSet;
@@ -64,6 +65,8 @@ public class BasicSectionGeometryManager extends AbstractSectionGeometryManager
//Invalidate the section id
this.invalidatedSectionIds.add(newId);
HierarchicalOcclusionTraverser.HACKY_SECTION_COUNT = this.allocationSet.getCount();
return newId;
}