From fce99fbde3bc7f9d69c6f639ef626ca9391ae575 Mon Sep 17 00:00:00 2001 From: mcrcortex <18544518+MCRcortex@users.noreply.github.com> Date: Fri, 2 May 2025 23:44:49 +1000 Subject: [PATCH] stamps and notes --- .../core/rendering/SectionUpdateRouter.java | 113 +++++++++++++----- .../building/RenderDataFactory45.java | 1 + .../inmemory/MemoryStorageBackend.java | 1 + .../common/world/ActiveSectionTracker.java | 2 +- 4 files changed, 84 insertions(+), 33 deletions(-) diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/SectionUpdateRouter.java b/src/main/java/me/cortex/voxy/client/core/rendering/SectionUpdateRouter.java index 7ede4d35..87c5b6bc 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/SectionUpdateRouter.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/SectionUpdateRouter.java @@ -4,18 +4,21 @@ import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap; import me.cortex.voxy.common.world.WorldEngine; import me.cortex.voxy.common.world.WorldSection; +import java.util.concurrent.locks.StampedLock; import java.util.function.LongConsumer; import static me.cortex.voxy.common.world.WorldEngine.UPDATE_TYPE_BLOCK_BIT; public class SectionUpdateRouter implements ISectionWatcher { - private static final int SLICES = 1<<8; + private static final int SLICES = 1<<4; public interface IChildUpdate {void accept(WorldSection section);} private final Long2ByteOpenHashMap[] slices = new Long2ByteOpenHashMap[SLICES]; + private final StampedLock[] locks = new StampedLock[SLICES]; { for (int i = 0; i < this.slices.length; i++) { this.slices[i] = new Long2ByteOpenHashMap(); + this.locks[i] = new StampedLock(); } } @@ -35,17 +38,34 @@ public class SectionUpdateRouter implements ISectionWatcher { } public boolean watch(long position, int types) { - var set = this.slices[getSliceIndex(position)]; + int idx = getSliceIndex(position); + var set = this.slices[idx]; + var lock = this.locks[idx]; byte delta = 0; - synchronized (set) { - byte current = 0; - if (set.containsKey(position)) { - current = set.get(position); - } + { + long stamp = lock.readLock(); + byte current = set.getOrDefault(position, (byte) 0); delta = (byte) (current&types); current |= (byte) types; delta ^= (byte) (current&types); - set.put(position, current); + if (delta != 0) {//Was change + long ws = lock.tryConvertToWriteLock(stamp); + if (ws == 0) { + lock.unlockRead(stamp); + stamp = lock.writeLock(); + //We need to recompute as we failed to acquire an immediate write lock + current = set.getOrDefault(position, (byte) 0); + delta = (byte) (current&types); + current |= (byte) types; + delta ^= (byte) (current&types); + if (delta != 0) + set.put(position, current); + } else { + stamp = ws; + set.put(position, current); + } + } + lock.unlock(stamp); } if ((delta&UPDATE_TYPE_BLOCK_BIT)!=0) { //If we added it, immediately invoke for an update @@ -58,38 +78,67 @@ public class SectionUpdateRouter implements ISectionWatcher { return this.unwatch(WorldEngine.getWorldSectionId(lvl, x, y, z), types); } - 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; - } - set.put(position, current); - return false; + public boolean unwatch(long position, int types) {//Types is types to unwatch + int idx = getSliceIndex(position); + var set = this.slices[idx]; + var lock = this.locks[idx]; + + long stamp = lock.readLock(); + + byte current = set.getOrDefault(position, (byte)0); + if (current == 0) { + throw new IllegalStateException("Section pos not in map " + WorldEngine.pprintPos(position)); } + boolean removed = false; + if ((current&types) != 0) {//Was change + long ws = lock.tryConvertToWriteLock(stamp); + if (ws == 0) {//failed to get write lock, need to unlock, get write, then redo + lock.unlockRead(stamp); + stamp = lock.writeLock(); + + current = set.getOrDefault(position, (byte)0); + if (current == 0) { + throw new IllegalStateException("Section pos not in map " + WorldEngine.pprintPos(position)); + } + } else { + stamp = ws; + } + + if ((current&types) != 0) { + current &= (byte) ~types; + if (current == 0) { + set.remove(position); + removed = true; + } else { + set.put(position, current); + } + } + } + lock.unlock(stamp); + return removed; } public int get(long position) { - var set = this.slices[getSliceIndex(position)]; - synchronized (set) { - return set.getOrDefault(position, (byte) 0); - } + int idx = getSliceIndex(position); + var set = this.slices[idx]; + var lock = this.locks[idx]; + long stamp = lock.readLock(); + int ret = set.getOrDefault(position, (byte) 0); + lock.unlockRead(stamp); + return ret; } public void forwardEvent(WorldSection section, int type) { final long position = section.key; - var set = this.slices[getSliceIndex(position)]; - byte types = 0; - synchronized (set) { - types = set.getOrDefault(position, (byte)0); - } + + int idx = getSliceIndex(position); + var set = this.slices[idx]; + var lock = this.locks[idx]; + + long stamp = lock.readLock(); + byte types = set.getOrDefault(position, (byte) 0); + lock.unlockRead(stamp); + if (types!=0) { if ((type&WorldEngine.UPDATE_TYPE_CHILD_EXISTENCE_BIT)!=0) { this.childUpdateCallback.accept(section); diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/building/RenderDataFactory45.java b/src/main/java/me/cortex/voxy/client/core/rendering/building/RenderDataFactory45.java index aa31c259..57f42596 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/building/RenderDataFactory45.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/building/RenderDataFactory45.java @@ -331,6 +331,7 @@ public class RenderDataFactory45 { } } + //TODO: add neighbor face culling private void generateYZOpaqueInnerGeometry(int axis) { for (int layer = 0; layer < 31; layer++) { this.blockMesher.auxiliaryPosition = layer; diff --git a/src/main/java/me/cortex/voxy/common/config/storage/inmemory/MemoryStorageBackend.java b/src/main/java/me/cortex/voxy/common/config/storage/inmemory/MemoryStorageBackend.java index 36b5290b..ff34b134 100644 --- a/src/main/java/me/cortex/voxy/common/config/storage/inmemory/MemoryStorageBackend.java +++ b/src/main/java/me/cortex/voxy/common/config/storage/inmemory/MemoryStorageBackend.java @@ -16,6 +16,7 @@ import org.lwjgl.system.MemoryUtil; import java.nio.ByteBuffer; import java.util.function.LongConsumer; +//TODO: replace synchronize with StampedLock public class MemoryStorageBackend extends StorageBackend { private final Long2ObjectMap[] maps; private final Int2ObjectMap idMappings = new Int2ObjectOpenHashMap<>(); diff --git a/src/main/java/me/cortex/voxy/common/world/ActiveSectionTracker.java b/src/main/java/me/cortex/voxy/common/world/ActiveSectionTracker.java index 5840d23b..04ea1100 100644 --- a/src/main/java/me/cortex/voxy/common/world/ActiveSectionTracker.java +++ b/src/main/java/me/cortex/voxy/common/world/ActiveSectionTracker.java @@ -18,7 +18,7 @@ public class ActiveSectionTracker { //Loaded section world cache, TODO: get rid of VolatileHolder and use something more sane private final Long2ObjectOpenHashMap>[] loadedSectionCache; - private final ReentrantLock[] locks; + private final ReentrantLock[] locks;//TODO: replace with StampedLocks private final SectionLoader loader; private final int lruSize;