Added render cache

This commit is contained in:
mcrcortex
2024-02-03 23:43:32 +10:00
parent 0069855c5f
commit 86f3b04b37
6 changed files with 77 additions and 13 deletions

View File

@@ -34,17 +34,23 @@ public class DistanceTracker {
// there will still be 32 chunks untill the first lod drop // 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 // 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++) { for (int i = 0; i < this.loDRings.length; i++) {
//TODO: FIXME: check that the level shift is right when inc/dec
int capRing = i; 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)); 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) //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 // 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 //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) -> {}); }, (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 //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);
}
}); });
} }
} }

View File

@@ -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 //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; 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"); System.out.println("Distance tracker initialized");
this.postProcessing = new PostProcessing(); this.postProcessing = new PostProcessing();

View File

@@ -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 //Tracks active sections, dispatches updates to the build system, everything related to rendering flows through here
public class RenderTracker { public class RenderTracker {
private static final class ActiveSectionObject {
private int buildFlags;
}
private final WorldEngine world; private final WorldEngine world;
private RenderGenerationService renderGen; private RenderGenerationService renderGen;
private final AbstractFarWorldRenderer renderer; private final AbstractFarWorldRenderer renderer;
@@ -109,8 +106,14 @@ public class RenderTracker {
} }
//Enqueues a renderTask for a section to cache the result //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+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.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); //this.renderGen.enqueueTask(section);
} }

View File

@@ -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 // 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 // and put that into the cache, then remove the uploaded mesh from the cache
public class BuiltSectionMeshCache { public class BuiltSectionMeshCache {
private static final BuiltSection HOLDER = new BuiltSection(-1);
private final ConcurrentHashMap<Long, BuiltSection> renderCache = new ConcurrentHashMap<>(1000,0.75f,10); private final ConcurrentHashMap<Long, BuiltSection> renderCache = new ConcurrentHashMap<>(1000,0.75f,10);
public BuiltSection getMesh(long key) { 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) //Returns true if the mesh was used, (this is so the parent method can free mesh object)
public boolean putMesh(BuiltSection mesh) { 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) { 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() { public void free() {
for (var mesh : this.renderCache.values()) {
if (mesh != HOLDER) {
mesh.free();
}
}
} }
} }

View File

@@ -91,7 +91,7 @@ public class RenderGenerationService {
{ {
var cache = this.meshCache.getMesh(ikey); var cache = this.meshCache.getMesh(ikey);
if (cache != null) { if (cache != null) {
this.resultConsumer.accept(cache.clone()); this.resultConsumer.accept(cache);
return; 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) { public void removeTask(int lvl, int x, int y, int z) {
synchronized (this.taskQueue) { synchronized (this.taskQueue) {
if (this.taskQueue.remove(WorldEngine.getWorldSectionId(lvl, x, y, z)) != null) { if (this.taskQueue.remove(WorldEngine.getWorldSectionId(lvl, x, y, z)) != null) {

View File

@@ -34,7 +34,7 @@ public class WorldEngine {
this.storage = storageBackend; this.storage = storageBackend;
this.mapper = new Mapper(this.storage); this.mapper = new Mapper(this.storage);
//4 cache size bits means that the section tracker has 16 separate maps that it uses //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.savingService = new SectionSavingService(this, savingServiceWorkers, compressionLevel);
this.ingestService = new VoxelIngestService(this, ingestWorkers); this.ingestService = new VoxelIngestService(this, ingestWorkers);