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 4cb42876..9143d653 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 @@ -7,6 +7,7 @@ import me.cortex.voxy.client.core.rendering.building.RenderGenerationService; import me.cortex.voxy.client.core.rendering.building.SectionUpdateRouter; import me.cortex.voxy.client.core.rendering.hierachical2.HierarchicalNodeManager; import me.cortex.voxy.client.core.rendering.hierachical2.HierarchicalOcclusionTraverser; +import me.cortex.voxy.client.core.rendering.hierachical2.NodeManager2; import me.cortex.voxy.client.core.rendering.section.AbstractSectionRenderer; import me.cortex.voxy.client.core.rendering.section.IUsesMeshlets; import me.cortex.voxy.client.core.rendering.section.MDICSectionRenderer; @@ -33,7 +34,7 @@ public class RenderService, J extends Vi private final ViewportSelector viewportSelector; private final AbstractSectionRenderer sectionRenderer; - private final HierarchicalNodeManager nodeManager; + private final NodeManager2 nodeManager; private final HierarchicalOcclusionTraverser traversal; private final ModelBakerySubsystem modelService; private final RenderGenerationService renderGen; @@ -51,7 +52,7 @@ public class RenderService, J extends Vi //Do something incredibly hacky, we dont need to keep the reference to this around, so just connect and discard var router = new SectionUpdateRouter(); - this.nodeManager = new HierarchicalNodeManager(1<<21, this.sectionRenderer.getGeometryManager(), router); + this.nodeManager = new NodeManager2(1<<21, this.sectionRenderer.getGeometryManager(), router); this.sectionUpdateQueue = new MessageQueue<>(section -> { byte childExistence = section.getNonEmptyChildren(); 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 73640f7a..6dc10b17 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 @@ -230,7 +230,7 @@ public class HierarchicalNodeManager { //============================================================================================================================================ public void processChildChange(long position, byte childExistence) { if (childExistence == 0) { - Logger.logError("Section at " + WorldEngine.pprintPos(position) + " had empty child existence!!"); + Logger.error("Section at " + WorldEngine.pprintPos(position) + " had empty child existence!!"); } int nodeId = this.activeSectionMap.get(position); if (nodeId == NO_NODE) { 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 89e915f1..1c8b45eb 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 @@ -27,7 +27,7 @@ import static org.lwjgl.opengl.GL45.*; // TODO: swap to persistent gpu threads instead of dispatching MAX_ITERATIONS of compute layers public class HierarchicalOcclusionTraverser { - private final HierarchicalNodeManager nodeManager; + private final NodeManager2 nodeManager; private final int maxRequestCount; private final GlBuffer requestBuffer; @@ -77,7 +77,7 @@ public class HierarchicalOcclusionTraverser { .compile(); - public HierarchicalOcclusionTraverser(HierarchicalNodeManager nodeManager, int requestBufferCount) { + public HierarchicalOcclusionTraverser(NodeManager2 nodeManager, int requestBufferCount) { this.nodeManager = nodeManager; this.requestBuffer = new GlBuffer(requestBufferCount*4L+1024).zero();//The 1024 is to assist with race condition issues this.nodeBuffer = new GlBuffer(nodeManager.maxNodeCount*16L).zero(); 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 new file mode 100644 index 00000000..9020cb79 --- /dev/null +++ b/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/NodeManager2.java @@ -0,0 +1,102 @@ +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.VoxelCore; +import me.cortex.voxy.client.core.gl.GlBuffer; +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.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 me.jellysquid.mods.sodium.client.util.MathUtil; +import org.lwjgl.system.MemoryUtil; + +import static me.cortex.voxy.client.core.rendering.hierachical2.NodeStore.NODE_ID_MSK; + +public class NodeManager2 { + //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)) + // All nodes except top nodes have parents + + //NOTE: + // For the queue processing, will need a redirect node-value type + // since for inner node child resize gpu could take N frames to update + + + private final ExpandingObjectAllocationList childRequests = new ExpandingObjectAllocationList<>(NodeChildRequest[]::new); + private final IntOpenHashSet nodeUpdates = new IntOpenHashSet(); + private final AbstractSectionGeometryManager geometryManager; + private final SectionUpdateRouter updateRouter; + private final Long2IntOpenHashMap activeSectionMap = new Long2IntOpenHashMap(); + private final NodeStore nodeData; + public final int maxNodeCount; + public NodeManager2(int maxNodeCount, AbstractSectionGeometryManager geometryManager, SectionUpdateRouter updateRouter) { + if (!MathUtil.isPowerOfTwo(maxNodeCount)) { + throw new IllegalArgumentException("Max node count must be a power of 2"); + } + if (maxNodeCount>(1<<24)) { + throw new IllegalArgumentException("Max node count cannot exceed 2^24"); + } + this.activeSectionMap.defaultReturnValue(-1); + this.updateRouter = updateRouter; + this.maxNodeCount = maxNodeCount; + this.nodeData = new NodeStore(maxNodeCount); + this.geometryManager = geometryManager; + } + + public void insertTopLevelNode(long pos) { + + } + + public void removeTopLevelNode(long pos) { + + } + + public void processGeometryResult(BuiltSection sectionResult) { + + } + + //============================================================================================================================================ + public void processRequestQueue(int count, long ptr) { + for (int requestIndex = 0; requestIndex < count; requestIndex++) { + int op = MemoryUtil.memGetInt(ptr + (requestIndex * 4L)); + this.processRequest(op); + } + } + + private void processRequest(int op) { + int node = op & NODE_ID_MSK; + if (!this.nodeData.nodeExists(node)) { + throw new IllegalStateException("Tried processing a node that doesnt exist: " + node); + } + if (this.nodeData.isNodeRequestInFlight(node)) { + Logger.warn("Tried processing a node that already has a request in flight: " + node + " pos: " + WorldEngine.pprintPos(this.nodeData.nodePosition(node))); + return; + } + this.nodeData.markRequestInFlight(node); + + } + + + public void processChildChange(long pos, byte childExistence) { + + } + + public boolean writeChanges(GlBuffer nodeBuffer) { + //TODO: use like compute based copy system or something + // since microcopies are bad + if (this.nodeUpdates.isEmpty()) { + return false; + } + for (int i : this.nodeUpdates) { + this.nodeData.writeNode(UploadStream.INSTANCE.upload(nodeBuffer, i*16L, 16L), i); + } + this.nodeUpdates.clear(); + return true; + } +} 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 new file mode 100644 index 00000000..3a40c929 --- /dev/null +++ b/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/SingleNodeRequest.java @@ -0,0 +1,40 @@ +package me.cortex.voxy.client.core.rendering.hierachical2; + +class SingleNodeRequest { + private final long nodePos; + private int mesh; + private byte childExistence; + private int setMsk; + + SingleNodeRequest(long nodePos) { + this.nodePos = nodePos; + } + + public void setChildExistence(byte childExistence) { + this.setMsk |= 2; + this.childExistence = childExistence; + } + + public int setMesh(int mesh) { + this.setMsk |= 1; + int prev = this.mesh; + this.mesh = mesh; + return prev; + } + + public boolean isSatisfied() { + return this.setMsk == 3; + } + + public long getPosition() { + return this.nodePos; + } + + public int getMesh() { + return this.mesh; + } + + public byte getChildExistence() { + return this.childExistence; + } +} diff --git a/src/main/java/me/cortex/voxy/common/Logger.java b/src/main/java/me/cortex/voxy/common/Logger.java index 9b385e67..5353b97c 100644 --- a/src/main/java/me/cortex/voxy/common/Logger.java +++ b/src/main/java/me/cortex/voxy/common/Logger.java @@ -2,14 +2,13 @@ package me.cortex.voxy.common; import org.slf4j.LoggerFactory; -import java.util.Arrays; import java.util.stream.Collectors; import java.util.stream.Stream; public class Logger { private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger("Voxy"); - public static void logError(Object... args) { + public static void error(Object... args) { Throwable throwable = null; for (var i : args) { if (i instanceof Throwable) { @@ -19,4 +18,15 @@ public class Logger { var stackEntry = new Throwable().getStackTrace()[1]; LOGGER.error("["+stackEntry.getClassName()+"]: "+ Stream.of(args).map(Object::toString).collect(Collectors.joining(" ")), throwable); } + + public static void warn(Object... args) { + Throwable throwable = null; + for (var i : args) { + if (i instanceof Throwable) { + throwable = (Throwable) i; + } + } + var stackEntry = new Throwable().getStackTrace()[1]; + LOGGER.warn("["+stackEntry.getClassName()+"]: "+ Stream.of(args).map(Object::toString).collect(Collectors.joining(" ")), throwable); + } } 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 b1667546..45167a77 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,28 +1,16 @@ 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; -import java.util.function.Supplier; - @Mixin(World.class) public class MixinWorld implements IVoxyWorldGetter, IVoxyWorldSetter { @Unique private WorldEngine voxyWorld; @@ -31,7 +19,7 @@ public class MixinWorld implements IVoxyWorldGetter, IVoxyWorldSetter { private void closeVoxyWorld(CallbackInfo ci) { if (this.voxyWorld != null) { try {this.voxyWorld.shutdown();this.voxyWorld = null;} catch (Exception e) { - Logger.logError("Failed to shutdown voxy world engine.", e); + Logger.error("Failed to shutdown voxy world engine.", e); } } }