diff --git a/src/main/java/me/cortex/voxy/client/core/DistanceTracker.java b/src/main/java/me/cortex/voxy/client/core/DistanceTracker.java index 5621a963..5ca78b80 100644 --- a/src/main/java/me/cortex/voxy/client/core/DistanceTracker.java +++ b/src/main/java/me/cortex/voxy/client/core/DistanceTracker.java @@ -34,17 +34,23 @@ public class DistanceTracker { // there will still be 32 chunks untill the first lod drop // if the game is set to 16, then there will be 48 chunks until the drop for (int i = 0; i < this.loDRings.length; i++) { + //TODO: FIXME: check that the level shift is right when inc/dec int capRing = i; this.loDRings[i] = new TransitionRing2D(6+i, lodRingScales[i], (x, z) -> this.dec(capRing+1, x, z), (x, z) -> this.inc(capRing+1, x, z)); //TODO:FIXME i think the radius is wrong and (lodRingScales[i]) needs to be (lodRingScales[i]<<1) since the transition ring (the thing above) // acts on LoD level + 1 - this.cacheLoadRings[i] = new TransitionRing2D(5+i, lodRingScales[i] + cacheLoadDistance, (x, z) -> { + this.cacheLoadRings[i] = new TransitionRing2D(5+i, (lodRingScales[i]<<1) + cacheLoadDistance, (x, z) -> { //When entering a cache ring, trigger a mesh op and inject into cache - + for (int y = this.minYSection>>capRing; y <= this.maxYSection>>capRing; y++) { + this.tracker.addCache(capRing, x, y, z); + } }, (x, z) -> {}); - this.cacheUnloadRings[i] = new TransitionRing2D(5+i, lodRingScales[i] + cacheUnloadDistance, (x, z) -> {}, (x, z) -> { + this.cacheUnloadRings[i] = new TransitionRing2D(5+i, (lodRingScales[i]<<1) + cacheUnloadDistance, (x, z) -> {}, (x, z) -> { //When exiting the cache unload ring, tell the cache to dump whatever mesh it has cached and not add any mesh from that position + for (int y = this.minYSection>>capRing; y <= this.maxYSection>>capRing; y++) { + this.tracker.removeCache(capRing, x, y, z); + } }); } } diff --git a/src/main/java/me/cortex/voxy/client/core/VoxelCore.java b/src/main/java/me/cortex/voxy/client/core/VoxelCore.java index 6a01aae0..cccbaa60 100644 --- a/src/main/java/me/cortex/voxy/client/core/VoxelCore.java +++ b/src/main/java/me/cortex/voxy/client/core/VoxelCore.java @@ -64,7 +64,8 @@ public class VoxelCore { //To get to chunk scale multiply the scale by 2, the scale is after how many chunks does the lods halve int q = VoxyConfig.CONFIG.qualityScale; - this.distanceTracker = new DistanceTracker(this.renderTracker, new int[]{q,q,q,q}, 2, 4); + //TODO: add an option for cache load and unload distance + this.distanceTracker = new DistanceTracker(this.renderTracker, new int[]{q,q,q,q}, 8, 16); System.out.println("Distance tracker initialized"); this.postProcessing = new PostProcessing(); diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/RenderTracker.java b/src/main/java/me/cortex/voxy/client/core/rendering/RenderTracker.java index 94e347f5..869055cf 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/RenderTracker.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/RenderTracker.java @@ -11,9 +11,6 @@ import java.util.concurrent.ConcurrentHashMap; //Tracks active sections, dispatches updates to the build system, everything related to rendering flows through here public class RenderTracker { - private static final class ActiveSectionObject { - private int buildFlags; - } private final WorldEngine world; private RenderGenerationService renderGen; private final AbstractFarWorldRenderer renderer; @@ -109,8 +106,14 @@ public class RenderTracker { } //Enqueues a renderTask for a section to cache the result - public void addCache() { + public void addCache(int lvl, int x, int y, int z) { + this.renderGen.markCache(lvl, x, y, z); + this.renderGen.enqueueTask(lvl, x, y, z, ((lvl1, x1, y1, z1) -> true));//TODO: replace the true identity lambda with a callback check to the render cache + } + //Removes the position from the cache + public void removeCache(int lvl, int x, int y, int z) { + this.renderGen.unmarkCache(lvl, x, y, z); } @@ -127,6 +130,11 @@ public class RenderTracker { this.renderGen.enqueueTask(section.lvl, section.x+1, section.y, section.z, this::shouldStillBuild); this.renderGen.enqueueTask(section.lvl, section.x, section.y, section.z-1, this::shouldStillBuild); this.renderGen.enqueueTask(section.lvl, section.x, section.y, section.z+1, this::shouldStillBuild); + this.renderGen.clearCache(section.lvl, section.x, section.y, section.z); + this.renderGen.clearCache(section.lvl, section.x-1, section.y, section.z); + this.renderGen.clearCache(section.lvl, section.x+1, section.y, section.z); + this.renderGen.clearCache(section.lvl, section.x, section.y, section.z-1); + this.renderGen.clearCache(section.lvl, section.x, section.y, section.z+1); } //this.renderGen.enqueueTask(section); } diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/building/BuiltSectionMeshCache.java b/src/main/java/me/cortex/voxy/client/core/rendering/building/BuiltSectionMeshCache.java index 85d97280..868f69c9 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/building/BuiltSectionMeshCache.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/building/BuiltSectionMeshCache.java @@ -6,22 +6,56 @@ import java.util.concurrent.ConcurrentHashMap; // when a section is unloaded from the gpu, put it into a download stream and recover the BuiltSection // and put that into the cache, then remove the uploaded mesh from the cache public class BuiltSectionMeshCache { + private static final BuiltSection HOLDER = new BuiltSection(-1); private final ConcurrentHashMap renderCache = new ConcurrentHashMap<>(1000,0.75f,10); public BuiltSection getMesh(long key) { - return null; + BuiltSection[] res = new BuiltSection[1]; + this.renderCache.computeIfPresent(key, (a, value) -> { + if (value == null || value == HOLDER) { + return value; + } + res[0] = value.clone(); + return value; + }); + return res[0]; } //Returns true if the mesh was used, (this is so the parent method can free mesh object) public boolean putMesh(BuiltSection mesh) { - return false; + var mesh2 = this.renderCache.computeIfPresent(mesh.position, (id, value) -> { + value.free(); + return mesh; + }); + return mesh2 == mesh; } public void clearMesh(long key) { + this.renderCache.computeIfPresent(key, (a,val)->{ + if (val != HOLDER) { + val.free(); + } + return HOLDER; + }); + } + + public void markCache(long key) { + this.renderCache.putIfAbsent(key, HOLDER); + } + + public void unmarkCache(long key) { + var mesh = this.renderCache.remove(key); + if (mesh != null && mesh != HOLDER) { + mesh.free(); + } } public void free() { - + for (var mesh : this.renderCache.values()) { + if (mesh != HOLDER) { + mesh.free(); + } + } } } 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 9aa34d7f..0d866cb6 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 @@ -91,7 +91,7 @@ public class RenderGenerationService { { var cache = this.meshCache.getMesh(ikey); if (cache != null) { - this.resultConsumer.accept(cache.clone()); + this.resultConsumer.accept(cache); return; } } @@ -109,6 +109,21 @@ public class RenderGenerationService { } } + //Tells the render cache that the mesh at the specified position should be cached + public void markCache(int lvl, int x, int y, int z) { + this.meshCache.markCache(WorldEngine.getWorldSectionId(lvl, x, y, z)); + } + + //Tells the render cache that the mesh at the specified position should not be cached/any previous cache result, freed + public void unmarkCache(int lvl, int x, int y, int z) { + this.meshCache.unmarkCache(WorldEngine.getWorldSectionId(lvl, x, y, z)); + } + + //Resets a chunks cache mesh + public void clearCache(int lvl, int x, int y, int z) { + this.meshCache.clearMesh(WorldEngine.getWorldSectionId(lvl, x, y, z)); + } + public void removeTask(int lvl, int x, int y, int z) { synchronized (this.taskQueue) { if (this.taskQueue.remove(WorldEngine.getWorldSectionId(lvl, x, y, z)) != null) { diff --git a/src/main/java/me/cortex/voxy/common/world/WorldEngine.java b/src/main/java/me/cortex/voxy/common/world/WorldEngine.java index 6acf766d..3dab88c7 100644 --- a/src/main/java/me/cortex/voxy/common/world/WorldEngine.java +++ b/src/main/java/me/cortex/voxy/common/world/WorldEngine.java @@ -34,7 +34,7 @@ public class WorldEngine { this.storage = storageBackend; this.mapper = new Mapper(this.storage); //4 cache size bits means that the section tracker has 16 separate maps that it uses - this.sectionTracker = new ActiveSectionTracker(4, this::unsafeLoadSection); + this.sectionTracker = new ActiveSectionTracker(3, this::unsafeLoadSection); this.savingService = new SectionSavingService(this, savingServiceWorkers, compressionLevel); this.ingestService = new VoxelIngestService(this, ingestWorkers);