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 b278e3d7..b2ab638a 100644 --- a/src/main/java/me/cortex/voxy/client/core/VoxelCore.java +++ b/src/main/java/me/cortex/voxy/client/core/VoxelCore.java @@ -75,6 +75,8 @@ public class VoxelCore { this.postProcessing = new PostProcessing(); System.out.println("Voxy core initialized"); + + //this.verifyTopNodeChildren(0,0,0); } diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/PrintfDebugUtil.java b/src/main/java/me/cortex/voxy/client/core/rendering/PrintfDebugUtil.java index 38303ae8..3bfad3c4 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/PrintfDebugUtil.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/PrintfDebugUtil.java @@ -3,6 +3,7 @@ package me.cortex.voxy.client.core.rendering; import me.cortex.voxy.client.Voxy; import me.cortex.voxy.client.core.gl.shader.IShaderProcessor; import me.cortex.voxy.client.core.gl.shader.PrintfInjector; +import me.cortex.voxy.client.core.gl.shader.ShaderType; import java.util.ArrayList; import java.util.List; @@ -12,8 +13,8 @@ public final class PrintfDebugUtil { private static final List printfQueue2 = new ArrayList<>(); private static final List printfQueue = new ArrayList<>(); - private static final IShaderProcessor PRINTF; - public static final PrintfInjector PRINTF_object; + public static final IShaderProcessor PRINTF_processor; + private static final PrintfInjector PRINTF_object; static { @@ -24,11 +25,18 @@ public final class PrintfDebugUtil { } printfQueue.add(line); }, printfQueue::clear); - PRINTF = PRINTF_object; + PRINTF_processor = PRINTF_object; } else { PRINTF_object = null; //Todo add a dummy processor that just removes all the printf calls - PRINTF = null; + PRINTF_processor = new IShaderProcessor() { + @Override + public String process(ShaderType type, String src) { + //TODO: replace with https://stackoverflow.com/questions/47162098/is-it-possible-to-match-nested-brackets-with-a-regex-without-using-recursion-or/47162099#47162099 + // to match on printf with balanced bracing + return src.replace("printf", "//printf"); + } + }; } } diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/building/BuiltSection.java b/src/main/java/me/cortex/voxy/client/core/rendering/building/BuiltSection.java index a101651f..e8df143d 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/building/BuiltSection.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/building/BuiltSection.java @@ -9,20 +9,22 @@ import java.util.Arrays; public final class BuiltSection { public static final boolean VERIFY_BUILT_SECTION_OFFSETS = VoxyCommon.isVerificationFlagOn("verifyBuiltSectionOffsets"); public final long position; + public final byte childExistence; public final int aabb; public final MemoryBuffer geometryBuffer; public final int[] offsets; private BuiltSection(long position) { - this(position, -1, null, null); + this(position, (byte) 0, -1, null, null); } public static BuiltSection empty(long position) { return new BuiltSection(position); } - public BuiltSection(long position, int aabb, MemoryBuffer geometryBuffer, int[] offsets) { + public BuiltSection(long position, byte childExistence, int aabb, MemoryBuffer geometryBuffer, int[] offsets) { this.position = position; + this.childExistence = childExistence; this.aabb = aabb; this.geometryBuffer = geometryBuffer; this.offsets = offsets; @@ -37,7 +39,7 @@ public final class BuiltSection { } public BuiltSection clone() { - return new BuiltSection(this.position, this.aabb, this.geometryBuffer!=null?this.geometryBuffer.copy():null, this.offsets!=null?Arrays.copyOf(this.offsets, this.offsets.length):null); + return new BuiltSection(this.position, this.childExistence, this.aabb, this.geometryBuffer!=null?this.geometryBuffer.copy():null, this.offsets!=null?Arrays.copyOf(this.offsets, this.offsets.length):null); } public void free() { diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/building/RenderDataFactory.java b/src/main/java/me/cortex/voxy/client/core/rendering/building/RenderDataFactory.java index e80cab54..c596fd8e 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/building/RenderDataFactory.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/building/RenderDataFactory.java @@ -321,7 +321,7 @@ public class RenderDataFactory { aabb |= (this.maxY-this.minY)<<20; aabb |= (this.maxZ-this.minZ)<<25; - return new BuiltSection(section.key, aabb, buff, offsets); + return new BuiltSection(section.key, section.getNonEmptyChildren(), aabb, buff, offsets); } diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/building/SectionUpdateRouter.java b/src/main/java/me/cortex/voxy/client/core/rendering/building/SectionUpdateRouter.java index 18dd3e15..f80130c5 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/building/SectionUpdateRouter.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/building/SectionUpdateRouter.java @@ -44,8 +44,9 @@ public class SectionUpdateRouter { if (set.containsKey(position)) { current = set.get(position); } - delta = (byte) ((current&types)^types); + delta = (byte) (current&types); current |= (byte) types; + delta ^= (byte) (current&types); set.put(position, current); } if ((delta&UPDATE_TYPE_BLOCK_BIT)!=0) { diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/HierarchicalNodeManager.java b/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/HierarchicalNodeManager.java index 6dc10b17..25e405cc 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/HierarchicalNodeManager.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/HierarchicalNodeManager.java @@ -240,7 +240,7 @@ public class HierarchicalNodeManager { nodeId &= ~ID_TYPE_MSK; if (type == ID_TYPE_REQUEST) { //Doesnt result in an invalidation as we must wait for geometry to create a child - this.requests.get(nodeId).putChildResult(getChildIdx(position), childExistence); + this.requests.get(nodeId).setChildMesh(getChildIdx(position), childExistence); } else if (type == ID_TYPE_LEAF || type == ID_TYPE_INNER) {// || type == ID_TYPE_TOP if (this.nodeData.getNodeChildExistence(nodeId) == childExistence) { //Dont need to update the internal state since it is the same @@ -345,7 +345,7 @@ public class HierarchicalNodeManager { 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 int child = getChildIdx(position); - int prev = request.putChildResult(child, this.geometryManager.uploadSection(section)); + int prev = request.setChildMesh(child, this.geometryManager.uploadSection(section)); if (prev != -1) { this.geometryManager.removeSection(prev); } 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 1aa8fdf4..fecbe5ca 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 @@ -15,7 +15,7 @@ import org.joml.Matrix4f; import org.joml.Vector3f; import org.lwjgl.system.MemoryUtil; -import static me.cortex.voxy.client.core.rendering.PrintfDebugUtil.PRINTF_object; +import static me.cortex.voxy.client.core.rendering.PrintfDebugUtil.PRINTF_processor; import static org.lwjgl.opengl.GL11.*; import static org.lwjgl.opengl.GL12.GL_UNPACK_IMAGE_HEIGHT; import static org.lwjgl.opengl.GL12.GL_UNPACK_SKIP_IMAGES; @@ -56,7 +56,7 @@ public class HierarchicalOcclusionTraverser { private final HiZBuffer hiZBuffer = new HiZBuffer(); private final int hizSampler = glGenSamplers(); - private final Shader traversal = Shader.make(PRINTF_object) + private final Shader traversal = Shader.make(PRINTF_processor) .defineIf("DEBUG", Voxy.SHADER_DEBUG) .define("MAX_ITERATIONS", MAX_ITERATIONS) .define("LOCAL_SIZE_BITS", LOCAL_WORK_SIZE_BITS) @@ -103,6 +103,7 @@ public class HierarchicalOcclusionTraverser { MemoryUtil.memPutInt(ptr, sx); ptr += 4; MemoryUtil.memPutInt(ptr, sy); ptr += 4; MemoryUtil.memPutInt(ptr, sz); ptr += 4; + MemoryUtil.memPutInt(ptr, viewport.width); ptr += 4; var innerTranslation = new Vector3f((float) (viewport.cameraX-(sx<<5)), (float) (viewport.cameraY-(sy<<5)), (float) (viewport.cameraZ-(sz<<5))); @@ -175,7 +176,7 @@ public class HierarchicalOcclusionTraverser { //Set the first entry glClearNamedBufferSubData(this.queueMetaBuffer.id, GL_RGBA32UI, 0, 16, GL_RGBA, GL_UNSIGNED_INT, new int[]{firstDispatchSize,1,1,initialQueueSize}); */ - { + {//TODO:FIXME: THIS IS BULLSHIT BY INTEL need to fix the clearing long ptr = UploadStream.INSTANCE.upload(this.queueMetaBuffer, 0, 16*5); MemoryUtil.memPutInt(ptr + 0, firstDispatchSize); MemoryUtil.memPutInt(ptr + 4, 1); @@ -187,6 +188,12 @@ public class HierarchicalOcclusionTraverser { MemoryUtil.memPutInt(ptr + (i*16)+ 8, 1); MemoryUtil.memPutInt(ptr + (i*16)+12, 0); } + //TODO: Move the first queue to a persistent list so its not updated every frame + + ptr = UploadStream.INSTANCE.upload(this.scratchQueueA, 0, 4L*initialQueueSize); + for (int i = 0; i < initialQueueSize; i++) { + MemoryUtil.memPutInt(ptr + 4L*i, 0); + } UploadStream.INSTANCE.commit(); } @@ -229,7 +236,7 @@ public class HierarchicalOcclusionTraverser { } private void forwardDownloadResult(long ptr, long size) { - int count = MemoryUtil.memGetInt(ptr); + int count = MemoryUtil.memGetInt(ptr);ptr += 8;//its 8 since we need to skip the second value (which is empty) if (count < 0 || count > 50000) { throw new IllegalStateException("Count unexpected extreme value: " + count); } diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/NodeChildRequest.java b/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/NodeChildRequest.java index 55d5507a..7faa3b2b 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/NodeChildRequest.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/NodeChildRequest.java @@ -10,28 +10,44 @@ class NodeChildRequest { private byte results; private byte mask; + private byte existenceMask = 0; NodeChildRequest(long nodePos) { this.nodePos = nodePos; } - public int getChildMeshResult(int childIdx) { + public int getChildMesh(int childIdx) { if ((this.mask&(1<>2)&1), + (WorldEngine.getZ(basePos)<<1)|((addin>>1)&1)); + } } diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/NodeStore.java b/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/NodeStore.java index adda3e6b..5e5f9d54 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/NodeStore.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/NodeStore.java @@ -8,9 +8,9 @@ public final class NodeStore { 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-2; - public static final int ABSENT_GEOMETRY_ID = GEOMETRY_ID_MSK-1;//Value for if want to clear geometry and make gpu request it if its needed - private static final int SENTINEL_EMPTY_GEOMETRY_ID = GEOMETRY_ID_MSK; + public static final int MAX_GEOMETRY_ID = (1<<24)-3; + private static final int SENTINEL_NULL_GEOMETRY_ID = (1<<24)-1; + private static final int SENTINEL_EMPTY_GEOMETRY_ID = (1<<24)-2; 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; @@ -104,20 +104,33 @@ public final class NodeStore { public int getNodeGeometry(int node) { long data = this.localNodeData[id2idx(node)+1]; int geometryPtr = (int) (data&GEOMETRY_ID_MSK); - if (geometryPtr == SENTINEL_EMPTY_GEOMETRY_ID) { + if (geometryPtr == SENTINEL_NULL_GEOMETRY_ID) { return -1; } + if (geometryPtr == SENTINEL_EMPTY_GEOMETRY_ID) { + return -2; + } 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>MAX_GEOMETRY_ID) { + throw new IllegalArgumentException("Geometry ptr greater than MAX_GEOMETRY_ID: " + geometryId); } + if (geometryId == -1) { + geometryId = SENTINEL_NULL_GEOMETRY_ID; + } + + if (geometryId == -2) { geometryId = SENTINEL_EMPTY_GEOMETRY_ID; } + if (geometryId < 0) { + throw new IllegalArgumentException("Geometry ptr less than -1 : " + geometryId); + } + int idx = id2idx(node)+1; long data = this.localNodeData[idx]; data &= ~GEOMETRY_ID_MSK; @@ -165,6 +178,9 @@ public final class NodeStore { this.localNodeData[id2idx(nodeId)+1] |= 1L<<63; } + public void unmarkRequestInFlight(int nodeId) { + this.localNodeData[id2idx(nodeId)+1] &= ~(1L<<63); + } public boolean isNodeRequestInFlight(int nodeId) { return ((this.localNodeData[id2idx(nodeId)+1]>>63)&1)!=0; } diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/SingleNodeRequest.java b/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/SingleNodeRequest.java index 3a40c929..b6099c8d 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/SingleNodeRequest.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/SingleNodeRequest.java @@ -8,6 +8,7 @@ class SingleNodeRequest { SingleNodeRequest(long nodePos) { this.nodePos = nodePos; + this.mesh = -1; } public void setChildExistence(byte childExistence) { @@ -37,4 +38,8 @@ class SingleNodeRequest { public byte getChildExistence() { return this.childExistence; } + + public boolean hasChildExistenceSet() { + return (this.setMsk&2)!=0; + } } diff --git a/src/main/java/me/cortex/voxy/common/world/WorldEngine.java b/src/main/java/me/cortex/voxy/common/world/WorldEngine.java index 105e266d..52ba5183 100644 --- a/src/main/java/me/cortex/voxy/common/world/WorldEngine.java +++ b/src/main/java/me/cortex/voxy/common/world/WorldEngine.java @@ -6,6 +6,7 @@ import me.cortex.voxy.common.world.service.SectionSavingService; import me.cortex.voxy.common.world.service.VoxelIngestService; import me.cortex.voxy.common.storage.StorageBackend; import me.cortex.voxy.common.thread.ServiceThreadPool; +import me.cortex.voxy.commonImpl.VoxyCommon; import java.util.Arrays; import java.util.function.Consumer; @@ -151,6 +152,7 @@ public class WorldEngine { long oldId = worldSection.set(x, y, z, newId); nonAirCountDelta += Mapper.isAir(oldId)==Mapper.isAir(newId)?0:(Mapper.isAir(newId)?-1:1 ); didStateChange |= newId != oldId; + //if (newId != oldId) {VoxyCommon.breakpoint();} } } } @@ -163,9 +165,7 @@ public class WorldEngine { } if (didStateChange||(emptinessStateChange!=0)) { - //Mark the section as dirty (enqueuing saving and geometry rebuild) and move to parent mip level - //TODO: have an update type! so that then e.g. if the child empty set changes it doesnt cause chunk rebuilds! - this.markDirty(worldSection); + this.markDirty(worldSection, (didStateChange?UPDATE_TYPE_BLOCK_BIT:0)|(emptinessStateChange!=0?UPDATE_TYPE_CHILD_EXISTENCE_BIT:0)); } //Need to release the section after using it diff --git a/src/main/resources/assets/voxy/shaders/lod/hierarchical/node.glsl b/src/main/resources/assets/voxy/shaders/lod/hierarchical/node.glsl index 656d3ef9..513619cf 100644 --- a/src/main/resources/assets/voxy/shaders/lod/hierarchical/node.glsl +++ b/src/main/resources/assets/voxy/shaders/lod/hierarchical/node.glsl @@ -29,6 +29,7 @@ struct UnpackedNode { #define NULL_NODE ((1<<24)-1) #define NULL_MESH ((1<<24)-1) +#define EMPTY_MESH ((1<<24)-2) void unpackNode(out UnpackedNode node, uint nodeId) { uvec4 compactedNode = nodes[nodeId]; @@ -55,7 +56,7 @@ bool hasMesh(in UnpackedNode node) { } bool isEmptyMesh(in UnpackedNode node) { - return node.meshPtr == (NULL_MESH-1);//Specialcase + return node.meshPtr == EMPTY_MESH;//Specialcase } bool hasChildren(in UnpackedNode node) { diff --git a/src/main/resources/assets/voxy/shaders/lod/hierarchical/screenspace.glsl b/src/main/resources/assets/voxy/shaders/lod/hierarchical/screenspace.glsl index 8b43f9c2..f6ea5ed3 100644 --- a/src/main/resources/assets/voxy/shaders/lod/hierarchical/screenspace.glsl +++ b/src/main/resources/assets/voxy/shaders/lod/hierarchical/screenspace.glsl @@ -64,14 +64,14 @@ void setupScreenspace(in UnpackedNode node) { //printf("Screenspace MIN: %f, %f, %f MAX: %f, %f, %f", minBB.x,minBB.y,minBB.z, maxBB.x,maxBB.y,maxBB.z); - size = maxBB.xy - minBB.xy; + size = (maxBB.xy - minBB.xy)*0.5f;//We half it for implicit conversion to screenspace } //Checks if the node is implicitly culled (outside frustum) bool outsideFrustum() { - //printf("Cull point (%f %f %f)x(%f %f %f)", maxBB.x, maxBB.y, maxBB.z, minBB.x, minBB.y, minBB.z); - return any(lessThanEqual(maxBB, vec3(-1f, -1f, 0f))) || any(lessThanEqual(vec3(1f, 1f, 1f), minBB)); + printf("Cull point (%f %f %f)x(%f %f %f)", maxBB.x, maxBB.y, maxBB.z, minBB.x, minBB.y, minBB.z); + return any(lessThanEqual(maxBB, vec3(-1.0f, -1.0f, 0.0f))) || any(lessThanEqual(vec3(1.0f, 1.0f, 1.0f), minBB)); } bool isCulledByHiz() { @@ -80,12 +80,12 @@ bool isCulledByHiz() { } vec2 ssize = size.xy * vec2(ivec2(screenW, screenH)); float miplevel = ceil(log2(max(max(ssize.x, ssize.y),1))); - vec2 midpoint = (maxBB.xy + minBB.xy)*0.5; - return textureLod(hizDepthSampler, vec3(midpoint, minBB.z), miplevel) > 0.0001; + vec2 midpoint = (maxBB.xy + minBB.xy)*0.5f; + return textureLod(hizDepthSampler, vec3(midpoint, minBB.z), miplevel) > 0.0001f; } //Returns if we should decend into its children or not bool shouldDecend() { - printf("Screen area %f: %f, %f", (size.x*size.y*float(screenW)*float(screenH)), float(screenW), float(screenH)); + printf("Screen area %f: %f, %f", (size.x*size.y*float(screenW)*float(screenH)), float(size.x), float(size.y)); return (size.x*size.y*screenW*screenH) > minSSS; } \ No newline at end of file