From 9e20eac7abeccb29be2878fef26d69361283857c Mon Sep 17 00:00:00 2001 From: mcrcortex <18544518+MCRcortex@users.noreply.github.com> Date: Thu, 30 Jan 2025 06:55:39 +1000 Subject: [PATCH] improved lru cache and improved world import speed slightly --- .../building/RenderGenerationService.java | 4 +- .../voxy/common/thread/ServiceSlice.java | 4 +- .../voxelization/WorldConversionFactory.java | 66 ++++++++++--------- .../common/world/ActiveSectionTracker.java | 15 ++--- 4 files changed, 46 insertions(+), 43 deletions(-) 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 94c87bd4..6c040d06 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 @@ -138,7 +138,9 @@ public class RenderGenerationService { //If we did put it in the queue, dont release the section shouldFreeSection = false; } else { - Logger.warn("Funkyness happened and multiple tasks for same section where in queue"); + //This should no longer be a worry with LRU section cache + //Logger.info("Funkyness happened and multiple tasks for same section where in queue"); + //Things went bad, set section to null and ensure section is freed task.section = null; shouldFreeSection = true; diff --git a/src/main/java/me/cortex/voxy/common/thread/ServiceSlice.java b/src/main/java/me/cortex/voxy/common/thread/ServiceSlice.java index f004a0ec..ed1fb9db 100644 --- a/src/main/java/me/cortex/voxy/common/thread/ServiceSlice.java +++ b/src/main/java/me/cortex/voxy/common/thread/ServiceSlice.java @@ -1,5 +1,6 @@ package me.cortex.voxy.common.thread; +import me.cortex.voxy.common.Logger; import me.cortex.voxy.common.util.Pair; import me.cortex.voxy.common.util.TrackedObject; import net.minecraft.client.MinecraftClient; @@ -76,8 +77,7 @@ public class ServiceSlice extends TrackedObject { try { ctx.run(); } catch (Exception e) { - System.err.println("Unexpected error occurred while executing a service job, expect things to break badly"); - e.printStackTrace(); + Logger.error("Unexpected error occurred while executing a service job, expect things to break badly", e); MinecraftClient.getInstance().execute(()->MinecraftClient.getInstance().player.sendMessage(Text.literal("A voxy service had an exception while executing please check logs and report error"), true)); } finally { if (this.activeCount.decrementAndGet() < 0) { diff --git a/src/main/java/me/cortex/voxy/common/voxelization/WorldConversionFactory.java b/src/main/java/me/cortex/voxy/common/voxelization/WorldConversionFactory.java index 3759536a..e331c1f3 100644 --- a/src/main/java/me/cortex/voxy/common/voxelization/WorldConversionFactory.java +++ b/src/main/java/me/cortex/voxy/common/voxelization/WorldConversionFactory.java @@ -1,6 +1,7 @@ package me.cortex.voxy.common.voxelization; import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; +import me.cortex.voxy.common.util.Pair; import me.cortex.voxy.common.world.other.Mipper; import me.cortex.voxy.common.world.other.Mapper; import net.minecraft.block.BlockState; @@ -11,52 +12,53 @@ import net.minecraft.world.chunk.ReadableContainer; public class WorldConversionFactory { //TODO: create a mapping for world/mapper -> local mapping - private static final ThreadLocal> BLOCK_CACHE = ThreadLocal.withInitial(Reference2IntOpenHashMap::new); + private static final ThreadLocal>> THREAD_LOCAL = ThreadLocal.withInitial(()->new Pair<>(new int[4*4*4], new Reference2IntOpenHashMap<>())); public static VoxelizedSection convert(VoxelizedSection section, Mapper stateMapper, PalettedContainer blockContainer, ReadableContainer> biomeContainer, ILightingSupplier lightSupplier) { - var blockCache = BLOCK_CACHE.get(); + var threadLocal = THREAD_LOCAL.get(); + var blockCache = threadLocal.right(); + var biomes = threadLocal.left(); var data = section.section; int blockId = -1; BlockState block = null; + { + int i = 0; + for (int y = 0; y < 4; y++) { + for (int z = 0; z < 4; z++) { + for (int x = 0; x < 4; x++) { + biomes[i++] = stateMapper.getIdForBiome(biomeContainer.get(x, y, z)); + } + } + } + } - for (int oy = 0; oy < 4; oy++) { - for (int oz = 0; oz < 4; oz++) { - for (int ox = 0; ox < 4; ox++) { - int biomeId = stateMapper.getIdForBiome(biomeContainer.get(ox, oy, oz)); - - for (int iy = 0; iy < 4; iy++) { - for (int iz = 0; iz < 4; iz++) { - for (int ix = 0; ix < 4; ix++) { - int x = (ox<<2)|ix; - int y = (oy<<2)|iy; - int z = (oz<<2)|iz; - var state = blockContainer.get(x, y, z); - byte light = lightSupplier.supply(x,y,z,state); - if (!(state.isAir() && (light==0))) { - if (block != state) { - if (state.isAir()) { - block = state; - blockId = 0; - } else { - blockId = blockCache.getOrDefault(state, -1); - if (blockId == -1) { - blockId = stateMapper.getIdForBlockState(state); - blockCache.put(state, blockId); - } - block = state; - } - } - data[G(x, y, z)] = Mapper.composeMappingId(light, blockId, biomeId); - } else { - data[G(x, y, z)] = Mapper.AIR; + for (int y = 0; y < 16; y++) { + for (int z = 0; z < 16; z++) { + for (int x = 0; x < 16; x++) { + var state = blockContainer.get(x, y, z); + byte light = lightSupplier.supply(x,y,z,state); + if (!(state.isAir() && (light==0))) { + if (block != state) { + if (state.isAir()) { + block = state; + blockId = 0; + } else { + blockId = blockCache.getOrDefault(state, -1); + if (blockId == -1) { + blockId = stateMapper.getIdForBlockState(state); + blockCache.put(state, blockId); } + block = state; } } + data[G(x, y, z)] = Mapper.composeMappingId(light, blockId, biomes[((y&0b1100)<<2)|(z&0b1100)|(x>>2)]); + } else { + data[G(x, y, z)] = Mapper.AIR; } } } 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 97b6939e..237faff8 100644 --- a/src/main/java/me/cortex/voxy/common/world/ActiveSectionTracker.java +++ b/src/main/java/me/cortex/voxy/common/world/ActiveSectionTracker.java @@ -41,7 +41,7 @@ public class ActiveSectionTracker { var cache = this.loadedSectionCache[index]; VolatileHolder holder = null; boolean isLoader = false; - WorldSection cachedSection = null; + WorldSection section; synchronized (cache) { holder = cache.get(key); if (holder == null) { @@ -49,23 +49,19 @@ public class ActiveSectionTracker { cache.put(key, holder); isLoader = true; } - var section = holder.obj; + section = holder.obj; if (section != null) { section.acquire(); return section; } if (isLoader) { - cachedSection = this.lruSecondaryCache[index].remove(key); - if (cachedSection != null) { - cachedSection.primeForReuse(); - } + section = this.lruSecondaryCache[index].remove(key); } } //If this thread was the one to create the reference then its the thread to load the section if (isLoader) { int status = 0; - var section = cachedSection; if (section == null) {//Secondary cache miss section = new WorldSection(WorldEngine.getLevel(key), WorldEngine.getX(key), @@ -87,7 +83,10 @@ public class ActiveSectionTracker { //We need to set the data to air as it is undefined state Arrays.fill(section.data, 0); } + } else { + section.primeForReuse(); } + section.acquire(); holder.obj = section; if (nullOnEmpty && status == 1) {//If its air return null as stated, release the section aswell @@ -96,7 +95,6 @@ public class ActiveSectionTracker { } return section; } else { - WorldSection section = null; while ((section = holder.obj) == null) Thread.onSpinWait(); @@ -117,6 +115,7 @@ public class ActiveSectionTracker { if (cache.remove(section.key).obj != section) { throw new IllegalStateException("Removed section not the same as the referenced section in the cache"); } + //Add section to secondary cache while primary is locked var lruCache = this.lruSecondaryCache[index]; var prev = lruCache.put(section.key, section);