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 08bdf386..c1effbcb 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 @@ -5,6 +5,7 @@ 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; import me.cortex.voxy.client.core.rendering.building.RenderGenerationService; +import me.cortex.voxy.client.core.rendering.building.SectionPositionUpdateFilterer; 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.section.AbstractSectionRenderer; @@ -13,6 +14,7 @@ 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.world.WorldEngine; +import me.cortex.voxy.common.world.WorldSection; import net.minecraft.client.render.Camera; import java.util.Arrays; @@ -38,7 +40,6 @@ public class RenderService, J extends Vi private final ConcurrentLinkedDeque sectionBuildResultQueue = new ConcurrentLinkedDeque<>(); - public RenderService(WorldEngine world) { this.modelService = new ModelBakerySubsystem(world.getMapper()); @@ -46,26 +47,21 @@ public class RenderService, J extends Vi //Max geometry: 1 gb this.sectionRenderer = (T) createSectionRenderer(this.modelService.getStore(),1<<19, (1L<<30)-1024); - this.nodeManager = new HierarchicalNodeManager(1<<21, this.sectionRenderer.getGeometryManager()); + //Do something incredibly hacky, we dont need to keep the reference to this around, so just connect and discard + var positionFilterForwarder = new SectionPositionUpdateFilterer(); + + this.nodeManager = new HierarchicalNodeManager(1<<21, this.sectionRenderer.getGeometryManager(), positionFilterForwarder); this.viewportSelector = new ViewportSelector<>(this.sectionRenderer::createViewport); this.renderGen = new RenderGenerationService(world, this.modelService, VoxyConfig.CONFIG.renderThreads, this.sectionBuildResultQueue::add, this.sectionRenderer.getGeometryManager() instanceof IUsesMeshlets); + positionFilterForwarder.setCallback(this.renderGen::enqueueTask); this.traversal = new HierarchicalOcclusionTraverser(this.nodeManager, 512); - world.setDirtyCallback(this.nodeManager::sectionUpdate); + world.setDirtyCallback(positionFilterForwarder::maybeForward); Arrays.stream(world.getMapper().getBiomeEntries()).forEach(this.modelService::addBiome); world.getMapper().setBiomeCallback(this.modelService::addBiome); - - - for(int x = -1; x<=1;x++) { - for (int z = -1; z <= 1; z++) { - for (int y = -3; y <= 3; y++) { - this.renderGen.enqueueTask(0, x, y, z); - } - } - } } public void setup(Camera camera) { 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 cf53c624..117ecc27 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 @@ -151,9 +151,15 @@ public class RenderGenerationService { this.enqueueTask(lvl, x, y, z, (l,x1,y1,z1)->true); } + public void enqueueTask(long position) { + this.enqueueTask(position, (l,x1,y1,z1)->true); + } public void enqueueTask(int lvl, int x, int y, int z, TaskChecker checker) { - long ikey = WorldEngine.getWorldSectionId(lvl, x, y, z); + this.enqueueTask(WorldEngine.getWorldSectionId(lvl, x, y, z), checker); + } + + public void enqueueTask(long ikey, TaskChecker checker) { { var cache = this.meshCache.getMesh(ikey); if (cache != null) { @@ -165,8 +171,8 @@ public class RenderGenerationService { this.taskQueue.computeIfAbsent(ikey, key->{ this.taskCounter.release(); return new BuildTask(ikey, ()->{ - if (checker.check(lvl, x, y, z)) { - return this.world.acquireIfExists(lvl, x, y, z); + if (checker.check(WorldEngine.getLevel(ikey), WorldEngine.getX(ikey), WorldEngine.getY(ikey), WorldEngine.getZ(ikey))) { + return this.world.acquireIfExists(WorldEngine.getLevel(ikey), WorldEngine.getX(ikey), WorldEngine.getY(ikey), WorldEngine.getZ(ikey)); } else { return null; } diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/building/SectionPositionUpdateFilterer.java b/src/main/java/me/cortex/voxy/client/core/rendering/building/SectionPositionUpdateFilterer.java new file mode 100644 index 00000000..6504faf9 --- /dev/null +++ b/src/main/java/me/cortex/voxy/client/core/rendering/building/SectionPositionUpdateFilterer.java @@ -0,0 +1,75 @@ +package me.cortex.voxy.client.core.rendering.building; + +import it.unimi.dsi.fastutil.longs.LongOpenHashSet; +import me.cortex.voxy.common.world.WorldEngine; +import me.cortex.voxy.common.world.WorldSection; + +import java.util.function.LongConsumer; + +public class SectionPositionUpdateFilterer { + private static final int SLICES = 1<<2; + private final LongOpenHashSet[] slices = new LongOpenHashSet[SLICES]; + { + for (int i = 0; i < this.slices.length; i++) { + this.slices[i] = new LongOpenHashSet(); + } + } + + private LongConsumer forwardTo; + + public void setCallback(LongConsumer forwardTo) { + if (this.forwardTo != null) { + throw new IllegalStateException(); + } + this.forwardTo = forwardTo; + } + + public boolean watch(int lvl, int x, int y, int z) { + return this.watch(WorldEngine.getWorldSectionId(lvl, x, y, z)); + } + + public boolean watch(long position) { + var set = this.slices[getSliceIndex(position)]; + boolean added; + synchronized (set) { + added = set.add(position); + } + if (added) { + //If we added it, immediately invoke for an update + this.forwardTo.accept(position); + } + return added; + } + + public boolean unwatch(int lvl, int x, int y, int z) { + return this.unwatch(WorldEngine.getWorldSectionId(lvl, x, y, z)); + } + + public boolean unwatch(long position) { + var set = this.slices[getSliceIndex(position)]; + synchronized (set) { + return set.remove(position); + } + } + + public void maybeForward(WorldSection section) { + this.maybeForward(section.key); + } + + public void maybeForward(long position) { + var set = this.slices[getSliceIndex(position)]; + boolean contains; + synchronized (set) { + contains = set.contains(position); + } + if (contains) { + this.forwardTo.accept(position); + } + } + + private static int getSliceIndex(long value) { + value = (value ^ value >>> 30) * -4658895280553007687L; + value = (value ^ value >>> 27) * -7723592293110705685L; + return (int) ((value ^ value >>> 31)&(SLICES-1)); + } +} 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 886a2225..2c55fb2a 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 @@ -1,7 +1,10 @@ package me.cortex.voxy.client.core.rendering.hierachical2; +import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; +import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import me.cortex.voxy.client.core.rendering.building.BuiltSection; +import me.cortex.voxy.client.core.rendering.building.SectionPositionUpdateFilterer; import me.cortex.voxy.client.core.rendering.section.AbstractSectionGeometryManager; import me.cortex.voxy.common.util.HierarchicalBitSet; import me.cortex.voxy.common.world.WorldSection; @@ -15,18 +18,30 @@ public class HierarchicalNodeManager { private final long[] localNodeData; private final AbstractSectionGeometryManager geometryManager; private final HierarchicalBitSet allocationSet; - - public HierarchicalNodeManager(int maxNodeCount, AbstractSectionGeometryManager geometryManager) { + private final Long2IntOpenHashMap activeSectionMap = new Long2IntOpenHashMap(); + private final SectionPositionUpdateFilterer updateFilterer; + public HierarchicalNodeManager(int maxNodeCount, AbstractSectionGeometryManager geometryManager, SectionPositionUpdateFilterer updateFilterer) { 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.updateFilterer = updateFilterer; this.allocationSet = new HierarchicalBitSet(maxNodeCount); this.maxNodeCount = maxNodeCount; this.localNodeData = new long[maxNodeCount*4]; this.geometryManager = geometryManager; + + + + for(int x = -1; x<=1;x++) { + for (int z = -1; z <= 1; z++) { + for (int y = -3; y <= 3; y++) { + updateFilterer.watch(0,x,y,z); + } + } + } } public void processRequestQueue(int count, long ptr) { @@ -44,9 +59,4 @@ public class HierarchicalNodeManager { section.free(); } } - - //Called when a section is updated in the world engine - public void sectionUpdate(WorldSection section) { - - } } diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/LeafExpansionRequest.java b/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/LeafExpansionRequest.java index b6434004..e3144fef 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/LeafExpansionRequest.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/LeafExpansionRequest.java @@ -4,5 +4,10 @@ package me.cortex.voxy.client.core.rendering.hierachical2; class LeafExpansionRequest { //Child states contain micrometadata in the top bits // such as isEmpty, and isEmptyButEventuallyHasNonEmptyChild + private final long nodePos; private final int[] childStates = new int[8]; + + LeafExpansionRequest(long nodePos) { + this.nodePos = nodePos; + } }