Fixes and tweeks

This commit is contained in:
mcrcortex
2024-07-16 20:47:49 +10:00
parent 5d58e9e4da
commit 26825e358f
7 changed files with 79 additions and 41 deletions

View File

@@ -55,7 +55,7 @@ dependencies {
//modRuntimeOnly "maven.modrinth:nvidium:0.2.6-beta"
modCompileOnly "maven.modrinth:nvidium:0.2.8-beta"
modImplementation("maven.modrinth:cloth-config:13.0.121+fabric")
modImplementation("maven.modrinth:cloth-config:15.0.127+fabric")
modImplementation("maven.modrinth:modmenu:11.0.1")

View File

@@ -174,6 +174,7 @@ public class Gl46HierarchicalRenderer implements IRenderInterface<Gl46Hierarchic
public void addDebugData(List<String> debug) {
debug.add("Printf Queue: ");
debug.addAll(this.printfQueue);
this.printfQueue.clear();
}
@@ -210,8 +211,15 @@ public class Gl46HierarchicalRenderer implements IRenderInterface<Gl46Hierarchic
@Override
public void initPosition(int x, int z) {
public void initPosition(int X, int Z) {
for (int x = -10; x <= 10; x++) {
for (int z = -10; z <= 10; z++) {
for (int y = -1; y <= 0; y++) {
long pos = WorldEngine.getWorldSectionId(4, x,y,z);
this.sectionSelector.nodeManager.insertTopLevelNode(pos);
}
}
}
}
@Override

View File

@@ -27,7 +27,7 @@ public class HierarchicalOcclusionRenderer {
private final int hizSampler = glGenSamplers();
private final NodeManager nodeManager;
public final NodeManager nodeManager;
private final Shader hierarchicalTraversal;
private final PrintfInjector printf;
@@ -68,7 +68,7 @@ public class HierarchicalOcclusionRenderer {
MemoryUtil.memPutInt(ptr, 1000000); ptr += 4;
//decendSSS (decend screen space size)
MemoryUtil.memPutFloat(ptr, 128*128); ptr += 4;
MemoryUtil.memPutFloat(ptr, 64*64); ptr += 4;
}
public void doHierarchicalTraversalSelection(Gl46HierarchicalViewport viewport, int depthBuffer, GlBuffer renderSelectionResult) {
@@ -76,9 +76,12 @@ public class HierarchicalOcclusionRenderer {
this.nodeManager.upload();
{
long ptr = UploadStream.INSTANCE.upload(this.nodeQueueA, 0, 8);
MemoryUtil.memPutInt(ptr, 1); ptr += 4;
MemoryUtil.memPutInt(ptr, 0);
int cnt = this.nodeManager.rootPos2Id.size();
long ptr = UploadStream.INSTANCE.upload(this.nodeQueueA, 0, 4+cnt*4L);
MemoryUtil.memPutInt(ptr, cnt); ptr += 4;
for (int i : this.nodeManager.rootPos2Id.values()) {
MemoryUtil.memPutInt(ptr, i); ptr += 4;
}
}
@@ -110,31 +113,31 @@ public class HierarchicalOcclusionRenderer {
nglClearNamedBufferSubData(this.nodeQueueB.id, GL_R32UI, 0, 4, GL_RED_INTEGER, GL_UNSIGNED_INT, 0);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, this.nodeQueueA.id);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 6, this.nodeQueueB.id);
glDispatchCompute(1,1,1);
glDispatchCompute(21*21*2,1,1);
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
nglClearNamedBufferSubData(this.nodeQueueA.id, GL_R32UI, 0, 4, GL_RED_INTEGER, GL_UNSIGNED_INT, 0);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, this.nodeQueueB.id);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 6, this.nodeQueueA.id);
glDispatchCompute(8,1,1);
glDispatchCompute(21*21*4,1,1);
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
nglClearNamedBufferSubData(this.nodeQueueB.id, GL_R32UI, 0, 4, GL_RED_INTEGER, GL_UNSIGNED_INT, 0);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, this.nodeQueueA.id);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 6, this.nodeQueueB.id);
glDispatchCompute(8*8,1,1);
glDispatchCompute(21*21*8,1,1);
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
nglClearNamedBufferSubData(this.nodeQueueA.id, GL_R32UI, 0, 4, GL_RED_INTEGER, GL_UNSIGNED_INT, 0);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, this.nodeQueueB.id);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 6, this.nodeQueueA.id);
glDispatchCompute(8*8*8,1,1);
glDispatchCompute(21*21*8,1,1);
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
nglClearNamedBufferSubData(this.nodeQueueB.id, GL_R32UI, 0, 4, GL_RED_INTEGER, GL_UNSIGNED_INT, 0);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, this.nodeQueueA.id);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 6, this.nodeQueueB.id);
glDispatchCompute(8*8*8*8,1,1);
glDispatchCompute(21*21*8,1,1);
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
}

View File

@@ -2,6 +2,7 @@ package me.cortex.voxy.client.core.rendering.hierarchical;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import me.cortex.voxy.client.core.gl.GlBuffer;
import me.cortex.voxy.client.core.rendering.building.BuiltSection;
import me.cortex.voxy.client.core.rendering.util.DownloadStream;
@@ -104,10 +105,11 @@ public class NodeManager {
public static final int REQUEST_QUEUE_SIZE = 1024;
public static final int MESH_MSK = MAX_MESH_ID-1;
public static final int EMPTY_MESH_ID = MESH_MSK-1;
public static final int NODE_MSK = (1<<24)-1;//NOTE!! IS DIFFERENT FROM MAX_NODE_COUNT as the MAX_NODE_COUNT is for the buffers, the ACTUAL MAX is NODE_MSK+1
//Local data layout
// first long is position (todo! might not be needed)
// first long is position
// next long contains mesh position ig/id
private final long[] localNodeData = new long[MAX_NODE_COUNT * 3];
@@ -127,25 +129,27 @@ public class NodeManager {
this.nodeBuffer = new GlBuffer(MAX_NODE_COUNT*16);
this.requestQueue = new GlBuffer(REQUEST_QUEUE_SIZE*4+4);
Arrays.fill(this.localNodeData, 0);
this.nodeAllocations.allocateNext();
this.setNodePosition(0, WorldEngine.getWorldSectionId(4, 0,0,0));
this.setChildPtr(0, NODE_MSK, 0);
this.setMeshId(0, MESH_MSK);
this.pushNode(0);
}
public final Long2IntOpenHashMap rootPos2Id = new Long2IntOpenHashMap();
public void insertTopLevelNode(long position) {
//NOTE! when initally adding a top level node, set it to air and request a meshing of the mesh
// (if the mesh returns as air uhhh idk what to do cause a top level air node is kinda... not valid but eh)
// that way the node will replace itself with its meshed varient when its ready aswell as prevent
// the renderer from exploding, as it should ignore the empty sections entirly
this.rootPosRequests.add(position);
this.interactor.watchUpdates(position);
this.interactor.requestMesh(position);
}
public void removeTopLevelNode(long position) {
}
//Returns the mesh offset/id for the given node or -1 if it doesnt exist
private int getNodeMesh(int node) {
return (int) (this.localNodeData[node*3+1]&MESH_MSK);
@@ -170,7 +174,7 @@ public class NodeManager {
//Its ment to return if the node is just an empty mesh or if all the children are also empty
private boolean isEmptyNode(int node) {
return this.getNodeMesh(node)==(MESH_MSK-1);//Special case/reserved
return this.getNodeMesh(node)==EMPTY_MESH_ID;//Special case/reserved
}
private void setNodePosition(int node, long position) {
@@ -188,7 +192,7 @@ public class NodeManager {
}
private void setChildPtr(int node, int childPtr, int count) {
if (childPtr > NODE_MSK || (childPtr!=NODE_MSK&&count < 1)) {
if (childPtr > NODE_MSK || ((childPtr!=NODE_MSK&&childPtr!=EMPTY_MESH_ID)&&count < 1)) {
throw new IllegalArgumentException();
}
long val = this.localNodeData[node*3+1];
@@ -217,7 +221,8 @@ public class NodeManager {
}
//IDEA, since a graph node can be in effectivly only 3 states, if inner node -> may or may not have mesh, and, if leaf node -> has mesh, no children
//The idea is, since a graph node can be in effectivly only 3 states, if inner node -> may or may not have mesh, and, if leaf node -> has mesh, no children
// the request queue only needs to supply the node id, since if its an inner node, it must be requesting for a mesh, while if its a leaf node, it must be requesting for children
private void processRequestQueue(long ptr, long size) {
int count = MemoryUtil.memGetInt(ptr); ptr += 4;
@@ -264,7 +269,7 @@ public class NodeManager {
//If its not a leaf node, it must be missing the inner mesh so request it
if (this.getNodeMesh(node) != MESH_MSK) {
//Node already has a mesh, ignore it, but might be a sign that an error has occured
System.err.println("Requested a mesh for node, however the node already has a mesh");
throw new IllegalStateException("Requested a mesh for node, however the node already has a mesh");
//TODO: need to unmark the node that requested it, either that or only clear node data when a mesh has been removed
@@ -289,6 +294,7 @@ public class NodeManager {
// the map should be identical to the currently watched set of sections
//NOTE: that if the id is negative its part of a mesh request
private final Long2IntOpenHashMap pos2meshId = new Long2IntOpenHashMap();
private final LongOpenHashSet rootPosRequests = new LongOpenHashSet();//
private static final int NO_NODE = -1;
//The request queue should be like some array that can reuse objects to prevent gc nightmare + like a bitset to find an avalible free slot
@@ -310,10 +316,10 @@ public class NodeManager {
//TODO: FIXME: CRITICAL: if a section is empty when created, it wont get allocated a slot, however, the section might
// become unempty due to an update!!! THIS IS REALLY BAD. since it doesnt have an allocation
//TODO: test and fix the possible race condition of if a section is not empty then becomes empty in the same tick
// that is, there is a request that is satisfied bar 1 section, that section is supplied as non emptpty but then becomes empty in the same tick
//TODO: Fixme: need to fix/make it so that the system can know if every child (to lod0) is empty or if its just the current section
private void meshUpdate(BuiltSection mesh) {
int id = this.pos2meshId.get(mesh.position);
//TODO: FIXME!! if we get a node that has an update and is watched but no id for it, it could be an update state from
@@ -321,11 +327,22 @@ public class NodeManager {
// then also update the parent pointer
//TODO: Also need a way to remove sections, requires shuffling stuff around
if (id == NO_NODE) {
//The built mesh section is no longer needed, discard it
// TODO: could probably?? cache the mesh in ram that way if its requested? it can be immediatly fetched while a newer mesh is built??
//This might be a warning? or maybe info?
mesh.free();
//If its a top level node insertion request, insert the node
if (this.rootPosRequests.remove(mesh.position)) {
if (!mesh.isEmpty()) {
int top = this.nodeAllocations.allocateNext();
this.rootPos2Id.put(mesh.position, top);
this.setNodePosition(top, mesh.position);
this.setChildPtr(top, NODE_MSK, 0);
this.setMeshId(top, this.meshManager.uploadMesh(mesh));
this.pushNode(top);
}
} else {
//The built mesh section is no longer needed, discard it
// TODO: could probably?? cache the mesh in ram that way if its requested? it can be immediatly fetched while a newer mesh is built??
//This might be a warning? or maybe info?
mesh.free();
}
return;
}
@@ -401,7 +418,8 @@ public class NodeManager {
private void completeLeafRequest(LeafRequest request) {
//TODO: need to actually update all of the pos2meshId of the children to point to there new nodes
//TODO: FIXME: need to make it so that if a nodes mesh is empty but there are children that exist that arnt empty
// then it needs to still add the node, but just with an empty mesh flag
int msk = Byte.toUnsignedInt(request.nonAirMask());
int baseIdx = this.nodeAllocations.allocateNextConsecutiveCounted(Integer.bitCount(msk));
int cnt = 0;
@@ -425,11 +443,12 @@ public class NodeManager {
}
}
if (cnt == 0) {
throw new IllegalStateException("Should not reach here");
//This means that every child node didnt have a mesh, this is not good
throw new IllegalStateException("Every child node empty for node at " + request.position);
} else {
//Set the ptr
this.setChildPtr(request.nodeId, baseIdx, cnt);
}
//Actually signal the update
this.setChildPtr(request.nodeId, baseIdx, cnt);
this.pushNode(request.nodeId);
}

View File

@@ -29,10 +29,12 @@ void main() {
gl_Position = pos;
//node.nodeId
uint hash = node.nodeId*1231421+123141;
hash ^= hash>>16;
hash = hash*1231421+123141;
hash ^= hash>>16;
hash = hash * 1827364925 + 123325621;
colour = vec4(float(hash&15u)/15, float((hash>>4)&15u)/15, float((hash>>8)&15u)/15, 1);
//colour = vec4(vec3(float(hash&15u)/15, float((hash>>4)&15u)/15, float((hash>>8)&15u)/15), 1);
colour = vec4(vec3(float(hash&31u)/31, float(node.lodLevel)/4, float(node.lodLevel)/4), 1);
}

View File

@@ -52,6 +52,10 @@ bool hasMesh(in UnpackedNode node) {
return node.meshPtr != NULL_MESH;
}
bool isEmptyMesh(in UnpackedNode node) {
return node.meshPtr == (NULL_MESH-1);//Specialcase
}
bool hasChildren(in UnpackedNode node) {
return node.childPtr != NULL_NODE;
}

View File

@@ -105,8 +105,8 @@ void enqueueChildren(in UnpackedNode node) {
}
void enqueueSelfForRender(in UnpackedNode node) {
printf("render %d@[%d,%d,%d]", node.lodLevel, node.pos.x, node.pos.y, node.pos.z);
if (renderQueueIndex < renderQueueMaxSize) {
//printf("render %d@[%d,%d,%d]", node.lodLevel, node.pos.x, node.pos.y, node.pos.z);
if ((!isEmptyMesh(node)) && renderQueueIndex < renderQueueMaxSize) {
renderQueue[atomicAdd(renderQueueIndex, 1)] = getMesh(node);
}
}
@@ -128,11 +128,13 @@ void main() {
//debugDumpNode(node);
if (outsideFrustum() || isCulledByHiz()) {
if (outsideFrustum()) {// || isCulledByHiz()
//printf("HizCulled");
//We are done here, dont do any more, the issue is the shader barriers maybe
// its culled, maybe just mark it as culled?
printf("Cull");
//printf("Cull");
} else {
//It is visible, TODO: maybe do a more detailed hiz test? (or make it so that )