diff --git a/build.gradle b/build.gradle index 9113625e..3c4994ed 100644 --- a/build.gradle +++ b/build.gradle @@ -22,11 +22,23 @@ repositories { maven { url "https://maven.terraformersmc.com/releases/" } } + +def gitCommitHash = { -> + def stdout = new ByteArrayOutputStream() + exec { + commandLine 'git', 'rev-parse', '--short', 'HEAD' + standardOutput = stdout + } + return stdout.toString().trim() +} +def buildtime = System.currentTimeSeconds() + processResources { - inputs.property "version", project.version + inputs.properties("version": project.version, "commit": gitCommitHash, "buildtime": buildtime) + archivesBaseName = "voxy" filesMatching("fabric.mod.json") { - expand "version": project.version + expand "commit": gitCommitHash, "version": project.version, "buildtime": buildtime } } diff --git a/src/main/java/me/cortex/voxy/client/Voxy.java b/src/main/java/me/cortex/voxy/client/Voxy.java index 9989edca..a8ab9c8a 100644 --- a/src/main/java/me/cortex/voxy/client/Voxy.java +++ b/src/main/java/me/cortex/voxy/client/Voxy.java @@ -16,14 +16,6 @@ import net.minecraft.client.world.ClientWorld; import java.util.Arrays; public class Voxy implements ClientModInitializer { - public static final String VERSION; - - static { - ModContainer mod = (ModContainer) FabricLoader.getInstance().getModContainer("voxy").orElseThrow(NullPointerException::new); - VERSION = mod.getMetadata().getVersion().getFriendlyString(); - Serialization.init(); - } - @Override public void onInitializeClient() { ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> { 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 e7b06a66..b278e3d7 100644 --- a/src/main/java/me/cortex/voxy/client/core/VoxelCore.java +++ b/src/main/java/me/cortex/voxy/client/core/VoxelCore.java @@ -11,6 +11,7 @@ import me.cortex.voxy.client.saver.ContextSelectionSystem; import me.cortex.voxy.common.world.WorldEngine; import me.cortex.voxy.client.importers.WorldImporter; import me.cortex.voxy.common.thread.ServiceThreadPool; +import me.cortex.voxy.commonImpl.VoxyCommon; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.hud.ClientBossBar; import net.minecraft.client.render.Camera; @@ -150,7 +151,7 @@ public class VoxelCore { public void addDebugInfo(List debug) { debug.add(""); debug.add(""); - debug.add("Voxy Core: " + Voxy.VERSION); + debug.add("Voxy Core: " + VoxyCommon.MOD_VERSION); /* debug.add("Ingest service tasks: " + this.world.ingestService.getTaskCount()); debug.add("Saving service tasks: " + this.world.savingService.getTaskCount()); 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 b9ac1280..18dd3e15 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 @@ -62,13 +62,17 @@ public class SectionUpdateRouter { public boolean unwatch(long position, int types) { var set = this.slices[getSliceIndex(position)]; synchronized (set) { + if (!set.containsKey(position)) { + throw new IllegalStateException("Section pos not in map!! " + WorldEngine.pprintPos(position)); + } byte current = set.get(position); byte delta = (byte) (current&types); current &= (byte) ~types; if (current == 0) { set.remove(position); + return true; } - return delta!=0; + return false; } } 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 b0f5e2b2..ce74b078 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 @@ -3,14 +3,12 @@ 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.Voxy; import me.cortex.voxy.client.core.rendering.building.BuiltSection; import me.cortex.voxy.client.core.rendering.building.SectionUpdateRouter; import me.cortex.voxy.client.core.rendering.section.AbstractSectionGeometryManager; 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.common.world.WorldSection; import me.jellysquid.mods.sodium.client.util.MathUtil; import org.lwjgl.system.MemoryUtil; @@ -52,10 +50,16 @@ public class HierarchicalNodeManager { * * Technically an error state, as a leaf node should always have geometry (or marked as empty geometry data) */ - //WARNING! need to solve a section geometry which is empty having its geometry removed, cause its empty - // so it wont send a request back cpu side - - + //There are a few properties of the class that can be used for verification + // a position cannot be both in a request and have an associated node, it must be none, in a request or a node + // leaf nodes cannot have a childptr + // updateRouter tracking child events should be the same set as activeSectionMap + // any allocated indices in requests are not finished/are in flight + // for all requests there must be an associated and valid node in the activeSectionMap + // for all requests the parent node must exist and cannot be ID_TYPE_REQUEST + // for all inner nodes, if there are no requests the children nodes must match the nodes childExistence bitset + // if there is a request, it should be the delta to get from the old children to the new children + // no node can have an empty childExistence (except for top level nodes) private static final int NO_NODE = -1; @@ -229,12 +233,82 @@ public class HierarchicalNodeManager { int type = (nodeId & ID_TYPE_MSK); nodeId &= ~ID_TYPE_MSK; if (type == ID_TYPE_REQUEST) { - //Doesnt result in an invalidation as we must wait for geometry? + //Doesnt result in an invalidation as we must wait for geometry to create a child this.requests.get(nodeId).putChildResult(getChildIdx(position), childExistence); } else if (type == ID_TYPE_LEAF || type == ID_TYPE_NONE || type == ID_TYPE_TOP) { - //For all the types, if there is a request attatched, need to update the request data (and update the associated sections in the activeSectionMap) - // if there isnt a current request, and there are nodes that got added, create a request - //if any children dont exist anymore, need to delete them + //ALSO: TODO: HERE: if a child is removed, need to remove it and all children accociated + + //If its an inner node and doesnt have an inflight request, create an empty request, this will get autofilled by the following part + if (type == ID_TYPE_NONE) { + + } + //If its a top level node, it needs a request? aswell + + //If its a leaf node, its fine, dont need to create a request, only need to update the node msk + + + if (this.nodeData.isNodeRequestInFlight(nodeId)) { + int reqId = this.nodeData.getNodeRequest(nodeId); + var request = this.requests.get(reqId); + byte reqMsk = request.getMsk(); + if (reqMsk != childExistence) { + //Only need to change if its not the same + byte toRem = (byte) ((~childExistence)&reqMsk); + if (toRem != 0) { + //Remove nodes from the request + for (int i = 0; i < 8; i++) {//TODO: swap out for numberOfTrailingZeros loop thing + if ((toRem&(i<<1))==0) continue; + int geometry = request.removeAndUnRequire(i); + long cpos = makeChildPos(position, i); + //Remove from update router and activeSectionMap + int cid = this.activeSectionMap.remove(cpos); + if ((cid&ID_TYPE_MSK)!=ID_TYPE_REQUEST || (cid&~ID_TYPE_MSK)!=reqId) { + throw new IllegalStateException(WorldEngine.pprintPos(position)+" " + i + " " + cid + " " + reqId); + } + ///Remove all from update router + if (!this.updateRouter.unwatch(cpos, WorldEngine.UPDATE_FLAGS)) { + throw new IllegalStateException(); + } + //Release geometry if it had any + if (geometry != -1) { + this.geometryManager.removeSection(geometry); + } + } + } + + byte toAdd = (byte) ((~reqMsk)&childExistence); + + //This also needs to be with respect to the nodes current childexistance status as some sections are already watched/have nodes, dont change those + toAdd &= (byte) ~this.nodeData.getNodeChildExistence(nodeId); + + if (toAdd != 0) { + //Add nodes to the request that dont exist in the node already + for (int i = 0; i < 8; i++) {//TODO: swap out for numberOfTrailingZeros loop thing + if ((toAdd & (i << 1)) == 0) continue; + request.addChildRequirement(i); + long cpos = makeChildPos(position, i); + int prev = this.activeSectionMap.put(cpos, ID_TYPE_REQUEST|reqId); + if (prev!=-1) { + throw new IllegalStateException("Child is already mapped to a node id " + WorldEngine.pprintPos(cpos) + " " + reqId + " " + prev); + } + if (!this.updateRouter.watch(cpos, WorldEngine.UPDATE_FLAGS)) { + throw new IllegalStateException("Couldn't watch chunk section"); + } + } + } + + if (request.getMsk() != childExistence) { + throw new IllegalStateException(); + } + + //If the request is satisfied after changes, consume it + if (request.isSatisfied()) { + this.consumeFinishedNodeChildRequest(nodeId, request); + } + } + } + + //Need to update the node itself, only if the node has children does it need to update the child ptr information tho } else { throw new IllegalStateException("Should not reach here"); } @@ -252,41 +326,27 @@ public class HierarchicalNodeManager { int type = (nodeId & ID_TYPE_MSK); nodeId &= ~ID_TYPE_MSK; if (type == ID_TYPE_REQUEST) { - this.requestDataUpdate(nodeId); - } else if (type == ID_TYPE_NONE || type == ID_TYPE_TOP) { - //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!! + 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)); + if (prev != -1) { + this.geometryManager.removeSection(prev); + } + if (request.isSatisfied()) { + this.consumeFinishedNodeChildRequest(nodeId, request); + } + } else if (type == ID_TYPE_NONE || type == ID_TYPE_TOP || type == ID_TYPE_LEAF) { + //Update the node geometry, and enqueue if it changed + if (this.updateNodeGeometry(nodeId, section) != 0) {//TODO: might need to mark the node as empty geometry or something + this.nodeUpdates.add(nodeId); + } } else { throw new IllegalStateException("Should not reach here"); } } } - private void requestDataUpdate(int nodeId) { - 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 = EMPTY_GEOMETRY_ID; @@ -304,7 +364,6 @@ public class HierarchicalNodeManager { if (previousGeometry != newGeometry) { this.nodeData.setNodeGeometry(node, newGeometry); - this.nodeUpdates.add(node); } if (previousGeometry == newGeometry) { return 0;//No change @@ -315,6 +374,18 @@ public class HierarchicalNodeManager { } } + //Process NodeChildRequest results + private void consumeFinishedNodeChildRequest(int nodeId, NodeChildRequest request) { + int children = this.nodeData.getChildPtr(nodeId); + 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(nodeId))); + + } else { + + } + } + //============================================================================================================================================ private static int getChildIdx(long pos) { 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 90e12cdf..8a8e8357 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 @@ -23,12 +23,18 @@ public class HierarchicalOcclusionTraverser { private final GlBuffer nodeBuffer; private final GlBuffer uniformBuffer = new GlBuffer(1024).zero(); - private final GlBuffer renderList = new GlBuffer(100_000 * 4 + 4).zero();//100k sections max to render + 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 scratchBuffer = new GlBuffer(1024).zero();//Scratch utility buffer for small things to get the ordering right and memory overall + //Scratch queues for node traversal + private final GlBuffer scratchQueueA = new GlBuffer(10_000*4).zero(); + private final GlBuffer scratchQueueB = new GlBuffer(10_000*4).zero(); + + private final HiZBuffer hiZBuffer = new HiZBuffer(); - public HierarchicalOcclusionTraverser(HierarchicalNodeManager nodeManager, int requestBufferCount) { this.nodeManager = nodeManager; this.requestBuffer = new GlBuffer(requestBufferCount*4L+1024).zero();//The 1024 is to assist with race condition issues @@ -40,7 +46,6 @@ 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); @@ -51,16 +56,7 @@ 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); - MemoryUtil.memPutInt(uploadPtr, HACKY_SECTION_COUNT); - for (int i = 1; i < HACKY_SECTION_COUNT+1; i++) { - MemoryUtil.memPutInt(uploadPtr + 4L * i, i - 1); - } - - UploadStream.INSTANCE.commit(); - } this.downloadResetRequestQueue(); } @@ -98,5 +94,6 @@ public class HierarchicalOcclusionTraverser { this.nodeBuffer.free(); this.uniformBuffer.free(); this.renderList.free(); + this.scratchBuffer.free(); } } 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 2b8278d5..55d5507a 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 @@ -78,4 +78,8 @@ class NodeChildRequest { public long getPosition() { return this.nodePos; } + + public byte getMsk() { + return this.mask; + } } diff --git a/src/main/java/me/cortex/voxy/common/Logger.java b/src/main/java/me/cortex/voxy/common/Logger.java index 72050f37..9b385e67 100644 --- a/src/main/java/me/cortex/voxy/common/Logger.java +++ b/src/main/java/me/cortex/voxy/common/Logger.java @@ -10,7 +10,13 @@ public class Logger { private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger("Voxy"); public static void logError(Object... args) { + Throwable throwable = null; + for (var i : args) { + if (i instanceof Throwable) { + throwable = (Throwable) i; + } + } var stackEntry = new Throwable().getStackTrace()[1]; - LOGGER.error("["+stackEntry.getClassName()+"]: "+ Stream.of(args).map(Object::toString).collect(Collectors.joining(" "))); + LOGGER.error("["+stackEntry.getClassName()+"]: "+ Stream.of(args).map(Object::toString).collect(Collectors.joining(" ")), throwable); } } diff --git a/src/main/java/me/cortex/voxy/commonImpl/IGetWorldInterface.java b/src/main/java/me/cortex/voxy/commonImpl/IGetWorldInterface.java deleted file mode 100644 index ca609d30..00000000 --- a/src/main/java/me/cortex/voxy/commonImpl/IGetWorldInterface.java +++ /dev/null @@ -1,4 +0,0 @@ -package me.cortex.voxy.commonImpl; - -public interface IGetWorldInterface { -} diff --git a/src/main/java/me/cortex/voxy/commonImpl/IVoxyWorldGetter.java b/src/main/java/me/cortex/voxy/commonImpl/IVoxyWorldGetter.java new file mode 100644 index 00000000..ad5f8459 --- /dev/null +++ b/src/main/java/me/cortex/voxy/commonImpl/IVoxyWorldGetter.java @@ -0,0 +1,7 @@ +package me.cortex.voxy.commonImpl; + +import me.cortex.voxy.common.world.WorldEngine; + +public interface IVoxyWorldGetter { + WorldEngine getWorldEngine(); +} diff --git a/src/main/java/me/cortex/voxy/commonImpl/IVoxyWorldSetter.java b/src/main/java/me/cortex/voxy/commonImpl/IVoxyWorldSetter.java new file mode 100644 index 00000000..5b38b8af --- /dev/null +++ b/src/main/java/me/cortex/voxy/commonImpl/IVoxyWorldSetter.java @@ -0,0 +1,7 @@ +package me.cortex.voxy.commonImpl; + +import me.cortex.voxy.common.world.WorldEngine; + +public interface IVoxyWorldSetter { + void setWorldEngine(WorldEngine engine); +} diff --git a/src/main/java/me/cortex/voxy/commonImpl/VoxyCommon.java b/src/main/java/me/cortex/voxy/commonImpl/VoxyCommon.java new file mode 100644 index 00000000..7e0c3cc1 --- /dev/null +++ b/src/main/java/me/cortex/voxy/commonImpl/VoxyCommon.java @@ -0,0 +1,29 @@ +package me.cortex.voxy.commonImpl; + +import me.cortex.voxy.common.config.Serialization; +import me.cortex.voxy.common.thread.ServiceThreadPool; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; +import net.fabricmc.loader.api.FabricLoader; +import net.fabricmc.loader.api.ModContainer; + +public class VoxyCommon implements ModInitializer { + public static final String MOD_VERSION; + public static final boolean IS_DEDICATED_SERVER; + + static { + ModContainer mod = (ModContainer) FabricLoader.getInstance().getModContainer("voxy").orElseThrow(NullPointerException::new); + var version = mod.getMetadata().getVersion().getFriendlyString(); + var commit = mod.getMetadata().getCustomValue("commit").getAsString(); + MOD_VERSION = version+"-"+commit; + IS_DEDICATED_SERVER = FabricLoader.getInstance().getEnvironmentType() == EnvType.SERVER; + + Serialization.init(); + } + + @Override + public void onInitialize() { + //this.serviceThreadPool = new ServiceThreadPool(VoxyConfig.CONFIG.serviceThreads); + } +} diff --git a/src/main/java/me/cortex/voxy/commonImpl/VoxyWorldService.java b/src/main/java/me/cortex/voxy/commonImpl/VoxyWorldService.java deleted file mode 100644 index 29107716..00000000 --- a/src/main/java/me/cortex/voxy/commonImpl/VoxyWorldService.java +++ /dev/null @@ -1,5 +0,0 @@ -package me.cortex.voxy.commonImpl; - -public class VoxyWorldService { - -} diff --git a/src/main/java/me/cortex/voxy/commonImpl/mixin/minecraft/MixinWorld.java b/src/main/java/me/cortex/voxy/commonImpl/mixin/minecraft/MixinWorld.java index 96abad26..b1667546 100644 --- a/src/main/java/me/cortex/voxy/commonImpl/mixin/minecraft/MixinWorld.java +++ b/src/main/java/me/cortex/voxy/commonImpl/mixin/minecraft/MixinWorld.java @@ -1,24 +1,51 @@ package me.cortex.voxy.commonImpl.mixin.minecraft; import me.cortex.voxy.client.Voxy; +import me.cortex.voxy.common.Logger; import me.cortex.voxy.common.world.WorldEngine; +import me.cortex.voxy.commonImpl.IVoxyWorldGetter; +import me.cortex.voxy.commonImpl.IVoxyWorldSetter; +import me.cortex.voxy.commonImpl.VoxyCommon; +import net.minecraft.registry.DynamicRegistryManager; +import net.minecraft.registry.RegistryKey; +import net.minecraft.registry.entry.RegistryEntry; +import net.minecraft.util.profiler.Profiler; +import net.minecraft.world.MutableWorldProperties; import net.minecraft.world.World; +import net.minecraft.world.dimension.DimensionType; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -@Mixin(World.class) -public class MixinWorld { - @Unique private WorldEngine voxyWorldEngine; +import java.util.function.Supplier; +@Mixin(World.class) +public class MixinWorld implements IVoxyWorldGetter, IVoxyWorldSetter { + @Unique private WorldEngine voxyWorld; @Inject(method = "close", at = @At("HEAD")) private void closeVoxyWorld(CallbackInfo ci) { - if (this.voxyWorldEngine != null) { - try {this.voxyWorldEngine.shutdown();} catch (Exception e) { + if (this.voxyWorld != null) { + try {this.voxyWorld.shutdown();this.voxyWorld = null;} catch (Exception e) { + Logger.logError("Failed to shutdown voxy world engine.", e); } } } + + @Override + public WorldEngine getWorldEngine() { + return this.voxyWorld; + } + + @Override + public void setWorldEngine(WorldEngine engine) { + if (this.voxyWorld != null) { + throw new IllegalStateException("WorldEngine not null"); + } + this.voxyWorld = engine; + } } diff --git a/src/main/resources/assets/voxy/shaders/lod/hierarchical/traversal.comp b/src/main/resources/assets/voxy/shaders/lod/hierarchical/traversal.comp index c4427859..7f3ed62a 100644 --- a/src/main/resources/assets/voxy/shaders/lod/hierarchical/traversal.comp +++ b/src/main/resources/assets/voxy/shaders/lod/hierarchical/traversal.comp @@ -1,7 +1,9 @@ #version 460 core -//TODO: make this better than a single thread -layout(local_size_x=32, local_size_y=1) in; +//TODO: increase local size +#define LOCAL_SIZE_BITS 5 +#define LOCAL_SIZE_MSK ((1< #line 7 @@ -26,11 +28,6 @@ layout(binding = SCENE_UNIFORM_INDEX, std140) uniform SceneUniform { float decendSSS; }; -layout(binding = NODE_QUEUE_INDEX, std430) restrict buffer NodeQueue { - uint nodeQueueSize; - uint[] nodeQueue; -}; - layout(binding = REQUEST_QUEUE_INDEX, std430) restrict buffer RequestQueue { uint requestQueueIndex; uint[] requestQueue; @@ -41,6 +38,11 @@ layout(binding = RENDER_QUEUE_INDEX, std430) restrict buffer RenderQueue { uint[] renderQueue; }; +layout(binding = NODE_QUEUE_INDEX, std430) restrict buffer NodeQueue { + uint nodeQueueSize; + uint[] nodeQueue; +}; + layout(binding = NEXT_NODE_QUEUE_INDEX, std430) restrict buffer NextNodeQueue { uint nextNodeQueueIndex; uint[] nextNodeQueue; @@ -102,6 +104,7 @@ void enqueueChildren(in UnpackedNode node) { uint children = getChildCount(node); uint ptr = getChildPtr(node); uint widx = atomicAdd(nextNodeQueueIndex, children); + for (int i = 0; i < children; i++) { nextNodeQueue[widx+i] = ptr+i; } diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index b211af18..1665982b 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -2,6 +2,10 @@ "schemaVersion": 1, "id": "voxy", "version": "${version}", + "custom": { + "commit": "${commit}", + "buildtime": "${buildtime}" + }, "name": "Voxy", "description": "Far distance rendering mod utilising LoDs", "authors": [ @@ -21,6 +25,9 @@ ], "modmenu": [ "me.cortex.voxy.client.config.VoxyConfigScreenFactory" + ], + "main": [ + "me.cortex.voxy.commonImpl.VoxyCommon" ] }, "mixins": [