This commit is contained in:
mcrcortex
2024-09-19 00:28:36 +10:00
parent df5c34c626
commit b875d5fc25
7 changed files with 161 additions and 20 deletions

View File

@@ -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.building.SectionUpdateRouter;
import me.cortex.voxy.client.core.rendering.hierachical2.HierarchicalNodeManager; 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.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.AbstractSectionRenderer;
import me.cortex.voxy.client.core.rendering.section.IUsesMeshlets; import me.cortex.voxy.client.core.rendering.section.IUsesMeshlets;
import me.cortex.voxy.client.core.rendering.section.MDICSectionRenderer; import me.cortex.voxy.client.core.rendering.section.MDICSectionRenderer;
@@ -33,7 +34,7 @@ public class RenderService<T extends AbstractSectionRenderer<J, ?>, J extends Vi
private final ViewportSelector<?> viewportSelector; private final ViewportSelector<?> viewportSelector;
private final AbstractSectionRenderer<J, ?> sectionRenderer; private final AbstractSectionRenderer<J, ?> sectionRenderer;
private final HierarchicalNodeManager nodeManager; private final NodeManager2 nodeManager;
private final HierarchicalOcclusionTraverser traversal; private final HierarchicalOcclusionTraverser traversal;
private final ModelBakerySubsystem modelService; private final ModelBakerySubsystem modelService;
private final RenderGenerationService renderGen; private final RenderGenerationService renderGen;
@@ -51,7 +52,7 @@ public class RenderService<T extends AbstractSectionRenderer<J, ?>, J extends Vi
//Do something incredibly hacky, we dont need to keep the reference to this around, so just connect and discard //Do something incredibly hacky, we dont need to keep the reference to this around, so just connect and discard
var router = new SectionUpdateRouter(); 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 -> { this.sectionUpdateQueue = new MessageQueue<>(section -> {
byte childExistence = section.getNonEmptyChildren(); byte childExistence = section.getNonEmptyChildren();

View File

@@ -230,7 +230,7 @@ public class HierarchicalNodeManager {
//============================================================================================================================================ //============================================================================================================================================
public void processChildChange(long position, byte childExistence) { public void processChildChange(long position, byte childExistence) {
if (childExistence == 0) { 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); int nodeId = this.activeSectionMap.get(position);
if (nodeId == NO_NODE) { if (nodeId == NO_NODE) {

View File

@@ -27,7 +27,7 @@ import static org.lwjgl.opengl.GL45.*;
// TODO: swap to persistent gpu threads instead of dispatching MAX_ITERATIONS of compute layers // TODO: swap to persistent gpu threads instead of dispatching MAX_ITERATIONS of compute layers
public class HierarchicalOcclusionTraverser { public class HierarchicalOcclusionTraverser {
private final HierarchicalNodeManager nodeManager; private final NodeManager2 nodeManager;
private final int maxRequestCount; private final int maxRequestCount;
private final GlBuffer requestBuffer; private final GlBuffer requestBuffer;
@@ -77,7 +77,7 @@ public class HierarchicalOcclusionTraverser {
.compile(); .compile();
public HierarchicalOcclusionTraverser(HierarchicalNodeManager nodeManager, int requestBufferCount) { public HierarchicalOcclusionTraverser(NodeManager2 nodeManager, int requestBufferCount) {
this.nodeManager = nodeManager; this.nodeManager = nodeManager;
this.requestBuffer = new GlBuffer(requestBufferCount*4L+1024).zero();//The 1024 is to assist with race condition issues 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(); this.nodeBuffer = new GlBuffer(nodeManager.maxNodeCount*16L).zero();

View File

@@ -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<NodeChildRequest> 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;
}
}

View File

@@ -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;
}
}

View File

@@ -2,14 +2,13 @@ package me.cortex.voxy.common;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.Arrays;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
public class Logger { public class Logger {
private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger("Voxy"); 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; Throwable throwable = null;
for (var i : args) { for (var i : args) {
if (i instanceof Throwable) { if (i instanceof Throwable) {
@@ -19,4 +18,15 @@ public class Logger {
var stackEntry = new Throwable().getStackTrace()[1]; var stackEntry = new Throwable().getStackTrace()[1];
LOGGER.error("["+stackEntry.getClassName()+"]: "+ Stream.of(args).map(Object::toString).collect(Collectors.joining(" ")), throwable); 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);
}
} }

View File

@@ -1,28 +1,16 @@
package me.cortex.voxy.commonImpl.mixin.minecraft; package me.cortex.voxy.commonImpl.mixin.minecraft;
import me.cortex.voxy.client.Voxy;
import me.cortex.voxy.common.Logger; import me.cortex.voxy.common.Logger;
import me.cortex.voxy.common.world.WorldEngine; import me.cortex.voxy.common.world.WorldEngine;
import me.cortex.voxy.commonImpl.IVoxyWorldGetter; import me.cortex.voxy.commonImpl.IVoxyWorldGetter;
import me.cortex.voxy.commonImpl.IVoxyWorldSetter; 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.World;
import net.minecraft.world.dimension.DimensionType;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.function.Supplier;
@Mixin(World.class) @Mixin(World.class)
public class MixinWorld implements IVoxyWorldGetter, IVoxyWorldSetter { public class MixinWorld implements IVoxyWorldGetter, IVoxyWorldSetter {
@Unique private WorldEngine voxyWorld; @Unique private WorldEngine voxyWorld;
@@ -31,7 +19,7 @@ public class MixinWorld implements IVoxyWorldGetter, IVoxyWorldSetter {
private void closeVoxyWorld(CallbackInfo ci) { private void closeVoxyWorld(CallbackInfo ci) {
if (this.voxyWorld != null) { if (this.voxyWorld != null) {
try {this.voxyWorld.shutdown();this.voxyWorld = null;} catch (Exception e) { 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);
} }
} }
} }