Prep v2
This commit is contained in:
@@ -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();
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user