From 5bb91bb1eb1a9cd4c1bf247b4900c0ef885b89a6 Mon Sep 17 00:00:00 2001 From: mcrcortex <18544518+MCRcortex@users.noreply.github.com> Date: Sun, 22 Dec 2024 18:10:28 +1000 Subject: [PATCH] Wip on many things Added partial inner node child update support --- .../voxy/client/core/gl/shader/Shader.java | 5 + .../client/core/rendering/RenderService.java | 52 +++- .../building/RenderGenerationService.java | 5 +- .../HierarchicalOcclusionTraverser.java | 4 +- .../rendering/hierachical2/NodeCleaner.java | 14 ++ .../rendering/hierachical2/NodeManager2.java | 227 +++++++++++++++++- .../rendering/hierachical2/NodeStore.java | 28 ++- .../voxy/common/thread/ServiceThreadPool.java | 6 +- .../assets/voxy/shaders/lod/gl46/quads2.vert | 1 - .../shaders/lod/hierarchical/screenspace.glsl | 4 +- 10 files changed, 321 insertions(+), 25 deletions(-) diff --git a/src/main/java/me/cortex/voxy/client/core/gl/shader/Shader.java b/src/main/java/me/cortex/voxy/client/core/gl/shader/Shader.java index 19bbf96e..808ffdf5 100644 --- a/src/main/java/me/cortex/voxy/client/core/gl/shader/Shader.java +++ b/src/main/java/me/cortex/voxy/client/core/gl/shader/Shader.java @@ -88,6 +88,11 @@ public class Shader extends TrackedObject { return this; } + public Builder define(String name, String value) { + this.defines.put(name, value); + return this; + } + public Builder add(ShaderType type, String id) { this.addSource(type, ShaderLoader.parse(id)); return this; diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/RenderService.java b/src/main/java/me/cortex/voxy/client/core/rendering/RenderService.java index f2b5e56e..4180d5bf 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/RenderService.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/RenderService.java @@ -1,5 +1,6 @@ package me.cortex.voxy.client.core.rendering; +import com.mojang.datafixers.util.Either; import me.cortex.voxy.client.core.model.ModelBakerySubsystem; import me.cortex.voxy.client.core.model.ModelStore; import me.cortex.voxy.client.core.rendering.building.BuiltSection; @@ -15,11 +16,13 @@ import me.cortex.voxy.client.core.rendering.section.MDICSectionRenderer; import me.cortex.voxy.client.core.rendering.util.DownloadStream; import me.cortex.voxy.client.core.rendering.util.UploadStream; import me.cortex.voxy.common.util.MessageQueue; +import me.cortex.voxy.common.util.Pair; import me.cortex.voxy.common.world.WorldEngine; import me.cortex.voxy.common.thread.ServiceThreadPool; import me.cortex.voxy.common.world.WorldSection; import net.minecraft.client.render.Camera; +import java.io.File; import java.util.Arrays; import java.util.List; @@ -49,7 +52,7 @@ public class RenderService, J extends Vi //Max sections: ~500k //Max geometry: 1 gb - this.sectionRenderer = (T) createSectionRenderer(this.modelService.getStore(),1<<20, (1L<<31)-1024); + this.sectionRenderer = (T) createSectionRenderer(this.modelService.getStore(),1<<20, (1L<<32)-1024); //Do something incredibly hacky, we dont need to keep the reference to this around, so just connect and discard var router = new SectionUpdateRouter(); @@ -93,17 +96,52 @@ public class RenderService, J extends Vi } } }*/ + if (true) { + if (true) { + final int H_WIDTH = 10; + for (int x = -H_WIDTH; x <= H_WIDTH; x++) { + for (int z = -H_WIDTH; z <= H_WIDTH; z++) { + for (int y = -1; y <= 0; y++) { + this.nodeManager.insertTopLevelNode(WorldEngine.getWorldSectionId(4, x, y, z)); + } + } + } + } else { + for (int x = -5; x <= 20; x++) { + for (int z = -5; z <= 20; z++) { + for (int y = 0; y <= 1; y++) { + this.nodeManager.insertTopLevelNode(WorldEngine.getWorldSectionId(4, x, y, z)); + } + } + } + } - - final int H_WIDTH = 20; - for (int x = -H_WIDTH; x <= H_WIDTH; x++) { - for (int y = -1; y <= 0; y++) { - for (int z = -H_WIDTH; z <= H_WIDTH; z++) { - this.nodeManager.insertTopLevelNode(WorldEngine.getWorldSectionId(4, x, y, z)); + } else { + /* + for (int x = -5; x <= 5; x++) { + for (int z = -5; z <= 5; z++) { + for (int y = -5; y <= 5; y++) { + this.nodeManager.insertTopLevelNode(WorldEngine.getWorldSectionId(4, x, y, z)); + } + } + } + for (int x = -5; x <= 5; x++) { + for (int z = -5; z <= 5; z++) { + for (int y = -5; y <= 5; y++) { + this.nodeManager.insertTopLevelNode(WorldEngine.getWorldSectionId(4, x+16, y, z)); + } + } + }*/ + for (int x = -3; x <= 3; x++) { + for (int z = -3; z <= 3; z++) { + for (int y = -8; y <= 7; y++) { + this.nodeManager.insertTopLevelNode(WorldEngine.getWorldSectionId(4, x, y, z)); + } } } } + //this.nodeManager.insertTopLevelNode(WorldEngine.getWorldSectionId(4, 0,0,0)); } diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/building/RenderGenerationService.java b/src/main/java/me/cortex/voxy/client/core/rendering/building/RenderGenerationService.java index 25c41c60..94c87bd4 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/building/RenderGenerationService.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/building/RenderGenerationService.java @@ -100,11 +100,12 @@ public class RenderGenerationService { try { mesh = factory.generateMesh(section); } catch (IdNotYetComputedException e) { + //TODO: maybe move this to _after_ task as been readded to queue?? + if (!this.modelBakery.factory.hasModelForBlockId(e.id)) { this.modelBakery.requestBlockBake(e.id); } if (task.hasDoneModelRequest) { - try { Thread.sleep(1); } catch (InterruptedException ex) { @@ -114,6 +115,8 @@ public class RenderGenerationService { //The reason for the extra id parameter is that we explicitly add/check against the exception id due to e.g. requesting accross a chunk boarder wont be captured in the request this.computeAndRequestRequiredModels(section, e.id); } + + {//Keep the lock on the section, and attach it to the task, this prevents needing to re-aquire it later task.section = section; } 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 85abab6b..90bdc367 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 @@ -41,8 +41,8 @@ public class HierarchicalOcclusionTraverser { private final GlBuffer renderList = new GlBuffer(100_000 * 4 + 4).zero();//100k sections max to render, TODO: Maybe move to render service or somewhere else private final GlBuffer queueMetaBuffer = new GlBuffer(4*4*5).zero(); - private final GlBuffer scratchQueueA = new GlBuffer(50_000*4).zero(); - private final GlBuffer scratchQueueB = new GlBuffer(50_000*4).zero(); + private final GlBuffer scratchQueueA = new GlBuffer(100_000*4).zero(); + private final GlBuffer scratchQueueB = new GlBuffer(100_000*4).zero(); private static final int LOCAL_WORK_SIZE_BITS = 5; private static final int MAX_ITERATIONS = 5; diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/NodeCleaner.java b/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/NodeCleaner.java index d0376981..75ac6ec5 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/NodeCleaner.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/NodeCleaner.java @@ -6,6 +6,7 @@ import me.cortex.voxy.client.core.gl.GlBuffer; import me.cortex.voxy.client.core.gl.shader.AutoBindingShader; import me.cortex.voxy.client.core.gl.shader.Shader; import me.cortex.voxy.client.core.gl.shader.ShaderType; +import me.cortex.voxy.client.core.rendering.util.DownloadStream; import me.cortex.voxy.client.core.rendering.util.UploadStream; import org.lwjgl.system.MemoryUtil; @@ -63,6 +64,19 @@ public class NodeCleaner { public void tick() { this.clearIds(); + if (false) { + this.outputBuffer.zero();//TODO: maybe dont set to zero?? + + this.sorter.bind(); + //TODO: choose whether this is in nodeSpace or section/geometryId space + //glDispatchCompute(, 1, 1); + + //DownloadStream.INSTANCE.download(this.outputBuffer, this::onDownload); + } + } + + private void onDownload(long ptr, long size) { + } private void clearIds() { diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/NodeManager2.java b/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/NodeManager2.java index 075f8367..37847ae5 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/NodeManager2.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/NodeManager2.java @@ -11,6 +11,7 @@ import me.cortex.voxy.client.core.rendering.util.UploadStream; import me.cortex.voxy.client.core.util.ExpandingObjectAllocationList; import me.cortex.voxy.common.Logger; import me.cortex.voxy.common.world.WorldEngine; +import me.cortex.voxy.commonImpl.VoxyCommon; import net.caffeinemc.mods.sodium.client.util.MathUtil; import java.util.List; @@ -24,6 +25,7 @@ import java.util.List; public class NodeManager2 { + private static final boolean VERIFY_NODE_MANAGER_OPERATIONS = VoxyCommon.isVerificationFlagOn("nodeManager"); //Assumptions: // all nodes have children (i.e. all nodes have at least one child existence bit set at all times) // leaf nodes always contain geometry (empty geometry counts as geometry (it just doesnt take any memory to store)) @@ -243,8 +245,7 @@ public class NodeManager2 { throw new IllegalStateException(); } } else if ((nodeId&NODE_TYPE_MSK)==NODE_TYPE_INNER) { - //Very complex and painful operation - Logger.error("UNFINISHED OPERATION TODO: FIXME"); + this.updateChildSectionsInner(pos, nodeId&NODE_ID_MSK, childExistence); } else if ((nodeId&NODE_TYPE_MSK)==NODE_TYPE_LEAF) { //We might be leaf but we still might be inflight @@ -252,7 +253,7 @@ public class NodeManager2 { // Logger.error("UNFINISHED OPERATION TODO: FIXME: painful operation, needs to account for both adding and removing, need to do the same with inner node, but also create requests, or cleanup children"); int requestId = this.nodeData.getNodeRequest(nodeId); var request = this.childRequests.get(requestId);// TODO: do not assume request is childRequest (it will probably always be) - if (request.getPosition() != pos) throw new IllegalStateException("Request not in pos"); + if (request.getPosition() != pos) throw new IllegalStateException("Request is not at pos"); {//Update the request byte oldMsk = request.getMsk(); byte change = (byte) (oldMsk ^ childExistence); @@ -293,16 +294,131 @@ public class NodeManager2 { } } } + + //If the request is now satisfied we need to finish it + if (request.isSatisfied()) { + this.finishRequest(requestId, request); + } } } //Just need to update the child node data, nothing else this.nodeData.setNodeChildExistence(nodeId&NODE_ID_MSK, childExistence); //Need to resubmit to gpu - this.invalidateNode(nodeId&NODE_ID_MSK); + this.invalidateNode(nodeId&NODE_ID_MSK);//TODO:FIXME: Do we??? } } + private void updateChildSectionsInner(long pos, int nodeId, byte childExistence) { + + //Very complex and painful operation + + /** + if (this.nodeData.isNodeRequestInFlight(nodeId&NODE_ID_MSK)) { + int requestId = this.nodeData.getNodeRequest(nodeId); + var request = this.childRequests.get(requestId);// TODO: do not assume request is childRequest (it will probably always be) + if (request.getPosition() != pos) throw new IllegalStateException("Request is not at pos"); + byte oldMsk = request.getMsk(); + byte change = (byte) (oldMsk ^ childExistence); + + +// {//Remove children that no longer exist, TODO: FIXME: THEY MIGHT NOT BE IN THE REQUEST +// byte rem = (byte) (change&childExistence); +// for (int i = 0; i < 8; i++) { +// if ((rem & (1 << i)) == 0) continue; +// int meshId = request.removeAndUnRequire(i); +// if (meshId != NULL_GEOMETRY_ID && meshId != EMPTY_GEOMETRY_ID) { +// this.geometryManager.removeSection(meshId); +// } +// +// //Remove child from being watched and activeSections +// long cPos = makeChildPos(pos, i); +// if (this.activeSectionMap.remove(cPos) == -1) {//TODO: verify the removed section is a request type of child and the request id matches this +// throw new IllegalStateException("Child pos was in a request but not in active section map"); +// } +// if (!this.updateRouter.unwatch(cPos, WorldEngine.UPDATE_FLAGS)) { +// throw new IllegalStateException("Child pos was not being watched"); +// } +// +// +// throw new IllegalStateException("UNFINISHED!: need to recursivly remove children"); +// } +// } + + + {//Add new children + byte add = (byte) (change&childExistence); + for (int i = 0; i < 8; i++) { + if ((add&(1<>56)&0x7); + return ((int)((data>>56)&0x7))+1; } public void setChildPtrCount(int nodeId, int count) { @@ -236,7 +251,7 @@ public final class NodeStore { short flags = 0; flags |= (short) (this.isNodeRequestInFlight(nodeId)?1:0); - flags |= (short) (this.getChildPtrCount(nodeId)<<2); + flags |= (short) ((this.getChildPtrCount(nodeId)-1)<<2); { int geometry = this.getNodeGeometry(nodeId); @@ -246,8 +261,9 @@ public final class NodeStore { z |= geometry&0xFFFFFF;//TODO: check and ensure bounds } } - - w |= this.getChildPtr(nodeId)&0xFFFFFF;//TODO: check and ensure bounds + int childPtr = this.getChildPtr(nodeId); + //TODO: check and ensure bounds + w |= childPtr&0xFFFFFF; z |= (flags&0xFF)<<24; w |= ((flags>>8)&0xFF)<<24; diff --git a/src/main/java/me/cortex/voxy/common/thread/ServiceThreadPool.java b/src/main/java/me/cortex/voxy/common/thread/ServiceThreadPool.java index 8d28228c..340983cd 100644 --- a/src/main/java/me/cortex/voxy/common/thread/ServiceThreadPool.java +++ b/src/main/java/me/cortex/voxy/common/thread/ServiceThreadPool.java @@ -22,14 +22,18 @@ public class ServiceThreadPool { private final ThreadGroup threadGroup; public ServiceThreadPool(int threadCount) { - this.threadGroup = new ThreadGroup("Service job workers"); + this(threadCount, 4);//Maybe change to 3 + } + public ServiceThreadPool(int threadCount, int priority) { + this.threadGroup = new ThreadGroup("Service job workers"); this.workers = new Thread[threadCount]; for (int i = 0; i < threadCount; i++) { int threadId = i; var worker = new Thread(this.threadGroup, ()->this.worker(threadId)); worker.setDaemon(false); worker.setName("Service worker #" + i); + worker.setPriority(priority); worker.start(); worker.setUncaughtExceptionHandler(this::handleUncaughtException); this.workers[i] = worker; diff --git a/src/main/resources/assets/voxy/shaders/lod/gl46/quads2.vert b/src/main/resources/assets/voxy/shaders/lod/gl46/quads2.vert index daee8e84..a4310ac0 100644 --- a/src/main/resources/assets/voxy/shaders/lod/gl46/quads2.vert +++ b/src/main/resources/assets/voxy/shaders/lod/gl46/quads2.vert @@ -75,7 +75,6 @@ vec3 swizzelDataAxis(uint axis, vec3 data) { void main() { int cornerIdx = gl_VertexID&3; Quad quad = quadData[uint(gl_VertexID)>>2]; - vec3 innerPos = extractPos(quad); uint face = extractFace(quad); uint modelId = extractStateId(quad); BlockModel model = modelData[modelId]; 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 3316d477..cbada3bc 100644 --- a/src/main/resources/assets/voxy/shaders/lod/hierarchical/screenspace.glsl +++ b/src/main/resources/assets/voxy/shaders/lod/hierarchical/screenspace.glsl @@ -87,8 +87,8 @@ bool isCulledByHiz() { vec2 ssize = size * vec2(screenW, screenH); - float miplevel = ceil(log2(max(max(ssize.x, ssize.y),1)))-1; - miplevel = clamp(miplevel, 1, 10); + float miplevel = ceil(log2(max(max(ssize.x, ssize.y),1))); + miplevel = clamp(miplevel, 1, 20); vec2 midpoint = (maxBB.xy + minBB.xy)*0.5f; //TODO: maybe get rid of clamp //Todo: replace with some rasterization, e.g. especially for request back to cpu