Merge remote-tracking branch 'origin/main'

This commit is contained in:
coolGi
2022-09-18 15:31:17 +09:30
5 changed files with 184 additions and 138 deletions
@@ -0,0 +1,27 @@
package com.seibel.lod.core.config;
import com.seibel.lod.core.config.types.ConfigEntry;
// TODO: Make this intergrate with the config system
public class AppliedConfigState<T> {
final ConfigEntry<T> entry;
T activeValue;
public AppliedConfigState(ConfigEntry<T> entryToWatch) {
this.entry = entryToWatch;
activeValue = entryToWatch.get();
}
public boolean pollNewValue() {
T newValue = entry.get();
if (newValue.equals(activeValue)) {
return false;
}
activeValue = newValue;
return true;
}
public T get() {
return activeValue;
}
}
@@ -58,22 +58,16 @@ public class DhClientLevel implements IClientLevel {
public void clientTick() {
tree.tick(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos()));
renderBufferHandler.update();
return;
}
@Override
public void render(Mat4f mcModelViewMatrix, Mat4f mcProjectionMatrix, float partialTicks, IProfilerWrapper profiler) {
if (renderer == null) {
renderer = new LodRenderer(this);
renderer = new LodRenderer(renderBufferHandler);
}
renderer.drawLODs(mcModelViewMatrix, mcProjectionMatrix, partialTicks, profiler);
}
@Override
public RenderBufferHandler getRenderBufferHandler() {
return renderBufferHandler;
}
@Override
public int computeBaseColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper block) {
return 0; //TODO
@@ -1,5 +1,6 @@
package com.seibel.lod.core.level;
import com.seibel.lod.core.config.AppliedConfigState;
import com.seibel.lod.core.datatype.full.ChunkSizedData;
import com.seibel.lod.core.datatype.full.FullDataSource;
import com.seibel.lod.core.datatype.transform.ChunkToLodBuilder;
@@ -33,26 +34,62 @@ import com.seibel.lod.core.wrapperInterfaces.world.IServerLevelWrapper;
import org.apache.logging.log4j.Logger;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.atomic.AtomicReference;
public class DhClientServerLevel implements IClientLevel, IServerLevel {
class RenderState {
final IClientLevelWrapper clientLevel;
final LodQuadTree tree;
final RenderFileHandler renderFileHandler;
final RenderBufferHandler renderBufferHandler; //TODO: Should this be owned by renderer?
final LodRenderer renderer;
RenderState(IClientLevelWrapper clientLevel) {
this.clientLevel = clientLevel;
renderFileHandler = new RenderFileHandler(dataFileHandler, DhClientServerLevel.this, save.getRenderCacheFolder(serverLevel));
tree = new LodQuadTree(DhClientServerLevel.this, Config.Client.Graphics.Quality.lodChunkRenderDistance.get() * 16,
MC_CLIENT.getPlayerBlockPos().x, MC_CLIENT.getPlayerBlockPos().z, renderFileHandler);
renderBufferHandler = new RenderBufferHandler(tree);
FileScanUtil.scanFile(save, serverLevel, null, renderFileHandler);
renderer = new LodRenderer(renderBufferHandler);
}
CompletableFuture<Void> close() {
renderer.close();
renderBufferHandler.close();
tree.close();
return renderFileHandler.flushAndSave();
}
}
class WorldGenState {
final BatchGenerator batchGenerator;
final GenerationQueue generationQueue;
WorldGenState() {
batchGenerator = new BatchGenerator(DhClientServerLevel.this);
generationQueue = new GenerationQueue(batchGenerator);
dataFileHandler.setGenerationQueue(generationQueue);
}
CompletableFuture<Void> close(boolean doInterrupt) {
dataFileHandler.popGenerationQueue();
return generationQueue.startClosing(true, doInterrupt)
.exceptionally(ex -> {
LOGGER.error("Error closing generation queue", ex);
return null;
}).thenRun(batchGenerator::close)
.exceptionally(ex -> {
LOGGER.error("Error closing world gen", ex);
return null;
});
}
}
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
public final LocalSaveStructure save;
public final GeneratedDataFileHandler dataFileHandler;
public final ChunkToLodBuilder chunkToLodBuilder;
public volatile GenerationQueue generationQueue = null;
public RenderFileHandler renderFileHandler = null;
public RenderBufferHandler renderBufferHandler = null; //TODO: Should this be owned by renderer?
public final IServerLevelWrapper serverLevel;
public IClientLevelWrapper clientLevel;
public LodRenderer renderer = null;
public LodQuadTree tree = null;
public volatile BatchGenerator worldGenerator = null;
private final ReentrantReadWriteLock renderStateLifecycleLock = new ReentrantReadWriteLock();
private final AppliedConfigState<Boolean> generatorEnabled;
public F3Screen.NestedMessage f3Msg;
public final AtomicReference<RenderState> renderState = new AtomicReference<>();
public final AtomicReference<WorldGenState> worldGenState = new AtomicReference<>();
public DhClientServerLevel(LocalSaveStructure save, IServerLevelWrapper level) {
this.serverLevel = level;
@@ -64,10 +101,12 @@ public class DhClientServerLevel implements IClientLevel, IServerLevel {
LOGGER.info("Started DHLevel for {} with saves at {}", level, save);
f3Msg = new F3Screen.NestedMessage(this::f3Log);
chunkToLodBuilder = new ChunkToLodBuilder();
generatorEnabled = new AppliedConfigState<>(Config.Client.WorldGenerator.enableDistantGeneration);
}
private String[] f3Log() {
if (clientLevel == null) {
RenderState rs = renderState.get();
if (rs == null) {
return new String[]{LodUtil.formatLog("level @ {}: Inactive", serverLevel.getDimensionType().getDimensionName())};
} else {
return new String[]{
@@ -78,35 +117,30 @@ public class DhClientServerLevel implements IClientLevel, IServerLevel {
@Override
public void clientTick() {
//LOGGER.info("Client tick for {}", level);
renderStateLifecycleLock.readLock().lock();
try {
if (clientLevel == null) return;
if (tree.viewDistance != Config.Client.Graphics.Quality.lodChunkRenderDistance.get() * 16) {
IClientLevelWrapper temp = clientLevel;
renderStateLifecycleLock.readLock().unlock();
renderStateLifecycleLock.writeLock().lock();
try {
stopRenderer();
startRenderer(temp);
} finally {
renderStateLifecycleLock.readLock().lock();
renderStateLifecycleLock.writeLock().unlock();
}
RenderState rs = renderState.get();
if (rs == null) return;
if (rs.tree.viewDistance != Config.Client.Graphics.Quality.lodChunkRenderDistance.get() * 16) {
if (!renderState.compareAndSet(rs, null)) return; //If we fail, we'll just wait for the next tick
IClientLevelWrapper levelWrapper = rs.clientLevel;
rs.close().join(); //TODO: Make it async.
rs = new RenderState(levelWrapper);
if (!renderState.compareAndSet(null, rs)) { //FIXME: How to handle this?
LOGGER.warn("Failed to set render state due to concurrency after changing view distance");
rs.close();
return;
}
tree.tick(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos()));
renderBufferHandler.update();
} finally {
renderStateLifecycleLock.readLock().unlock();
}
rs.tree.tick(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos()));
rs.renderBufferHandler.update();
}
private void saveWrites(ChunkSizedData data) {
RenderFileHandler renderFileHandler = this.renderFileHandler;
RenderState rs = renderState.get();
DhLodPos pos = data.getBBoxLodPos().convertUpwardsTo(FullDataSource.SECTION_SIZE_OFFSET);
if (renderFileHandler != null) {
renderFileHandler.write(new DhSectionPos(pos.detail, pos.x, pos.z), data);
if (rs != null) {
rs.renderFileHandler.write(new DhSectionPos(pos.detail, pos.x, pos.z), data);
} else {
dataFileHandler.write(new DhSectionPos(pos.detail, pos.x, pos.z), data);
}
@@ -119,93 +153,69 @@ public class DhClientServerLevel implements IClientLevel, IServerLevel {
public void startRenderer(IClientLevelWrapper clientLevel) {
LOGGER.info("Starting renderer for {}", this);
renderStateLifecycleLock.writeLock().lock();
try {
if (renderBufferHandler != null || this.clientLevel != null) {
LOGGER.warn("Tried to call startRenderer() on {} when renderer is already setup!", this);
return;
RenderState rs = new RenderState(clientLevel);
if (!renderState.compareAndSet(null, rs)) {
LOGGER.warn("Failed to start renderer due to concurrency");
rs.close();
} else {
generatorEnabled.pollNewValue();
if (generatorEnabled.get() && worldGenState.get() == null) {
WorldGenState wgs = new WorldGenState();
if (!worldGenState.compareAndSet(null, wgs)) {
LOGGER.warn("Failed to start world gen due to concurrency");
wgs.close(false);
}
}
this.clientLevel = clientLevel;
// TODO: Make a registry for generators for modding support.
worldGenerator = new BatchGenerator(this);
generationQueue = new GenerationQueue(worldGenerator);
dataFileHandler.setGenerationQueue(generationQueue);
renderFileHandler = new RenderFileHandler(dataFileHandler, this, save.getRenderCacheFolder(serverLevel));
tree = new LodQuadTree(this, Config.Client.Graphics.Quality.lodChunkRenderDistance.get() * 16,
MC_CLIENT.getPlayerBlockPos().x, MC_CLIENT.getPlayerBlockPos().z, renderFileHandler);
renderBufferHandler = new RenderBufferHandler(tree);
FileScanUtil.scanFile(save, serverLevel, null, renderFileHandler);
} finally {
renderStateLifecycleLock.writeLock().unlock();
}
}
@Override
public void render(Mat4f mcModelViewMatrix, Mat4f mcProjectionMatrix, float partialTicks, IProfilerWrapper profiler) {
if (!renderStateLifecycleLock.readLock().tryLock()) return;
try {
if (renderBufferHandler == null) {
LOGGER.error("Tried to call render() on {} when renderer has not been started!", this);
return;
}
if (renderer == null) {
renderer = new LodRenderer(this);
}
renderer.drawLODs(mcModelViewMatrix, mcProjectionMatrix, partialTicks, profiler);
} finally {
renderStateLifecycleLock.readLock().unlock();
RenderState rs = renderState.get();
if (rs == null) {
LOGGER.error("Tried to call render() on {} when renderer has not been started!", this);
return;
}
rs.renderer.drawLODs(mcModelViewMatrix, mcProjectionMatrix, partialTicks, profiler);
}
public void stopRenderer() {
LOGGER.info("Stopping renderer for {}", this);
renderStateLifecycleLock.writeLock().lock();
try {
if (renderBufferHandler == null) {
LOGGER.warn("Tried to call stopRenderer() on {} when renderer is already closed!", this);
return;
RenderState rs = renderState.get();
if (rs == null) {
LOGGER.warn("Tried to stop renderer for {} when it was not started!", this);
return;
}
while (!renderState.compareAndSet(rs, null)) {
rs = renderState.get();
if (rs == null) return;
}
rs.close().join(); //TODO: Make it async.
WorldGenState wgs = worldGenState.get();
if (wgs != null) {
while (!worldGenState.compareAndSet(wgs, null)) {
wgs = worldGenState.get();
if (wgs == null) return;
}
wgs.close(true).join(); //TODO: Make it async.
}
}
tree.close();
tree = null;
dataFileHandler.popGenerationQueue();
final BatchGenerator f_worldGen = worldGenerator;
CompletableFuture<Void> closer = generationQueue.startClosing(true, true)
.exceptionally(ex -> {
LOGGER.error("Error closing generation queue", ex);
return null;
}).thenRun(f_worldGen::close)
.exceptionally(ex -> {
LOGGER.error("Error closing world gen", ex);
return null;
});
generationQueue = null;
worldGenerator = null;
renderBufferHandler.close();
renderBufferHandler = null;
renderFileHandler.flushAndSave(); //Ignore the completion feature so that this action is async
renderFileHandler.close();
renderFileHandler = null;
closer.join(); // TODO: Could this cause deadlocks? we are blocking in main thread.
clientLevel = null;
} finally {
renderStateLifecycleLock.writeLock().unlock();
@Override //FIXME
public int computeBaseColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper block) {
IClientLevelWrapper clientLevel = getClientLevelWrapper();
if (clientLevel == null) {
return 0;
} else {
return clientLevel.computeBaseColor(pos, biome, block);
}
}
@Override
public RenderBufferHandler getRenderBufferHandler() {
return renderBufferHandler;
}
@Override
public int computeBaseColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper block) {
return clientLevel.computeBaseColor(pos, biome, block);
}
@Override
public IClientLevelWrapper getClientLevelWrapper() {
return clientLevel;
RenderState rs = renderState.get();
return rs == null ? null : rs.clientLevel;
}
@Override
@@ -234,26 +244,31 @@ public class DhClientServerLevel implements IClientLevel, IServerLevel {
@Override
public CompletableFuture<Void> save() {
if (renderFileHandler != null) {
return renderFileHandler.flushAndSave().thenCompose(v -> dataFileHandler.flushAndSave());
RenderState rs = renderState.get();
if (rs != null) {
return rs.renderFileHandler.flushAndSave().thenCombine(dataFileHandler.flushAndSave(), (a, b) -> null);
} else {
return dataFileHandler.flushAndSave();
}
}
@Override
public void close() {
renderStateLifecycleLock.writeLock().lock();
try {
if (generationQueue != null) generationQueue.close();
if (worldGenerator != null) worldGenerator.close();
if (renderer != null) renderer.close();
if (tree != null) tree.close();
if (renderBufferHandler != null) renderBufferHandler.close();
if (renderFileHandler != null) renderFileHandler.close();
dataFileHandler.close();
} finally {
renderStateLifecycleLock.writeLock().unlock();
RenderState rs = renderState.get();
if (rs != null) {
while (!renderState.compareAndSet(rs, null)) {
rs = renderState.get();
if (rs == null) return;
}
rs.close().join(); //TODO: Make it async.
}
WorldGenState wgs = worldGenState.get();
if (wgs != null) {
while (!worldGenState.compareAndSet(wgs, null)) {
wgs = worldGenState.get();
if (wgs == null) return;
}
wgs.close(true).join(); //TODO: Make it async.
}
LOGGER.info("Closed {}", this);
}
@@ -261,12 +276,27 @@ public class DhClientServerLevel implements IClientLevel, IServerLevel {
@Override
public void doWorldGen() {
final BatchGenerator f_worldGen = worldGenerator;
if (f_worldGen != null) {
f_worldGen.update();
final GenerationQueue f_genQueue = generationQueue;
if (f_genQueue != null)
f_genQueue.pollAndStartClosest(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos()));
WorldGenState wgs = worldGenState.get();
if (generatorEnabled.pollNewValue()) {
boolean shouldDoWorldGen = generatorEnabled.get() && renderState.get() != null;
if (shouldDoWorldGen && wgs == null) {
WorldGenState newWgs = new WorldGenState();
if (!worldGenState.compareAndSet(null, newWgs)) {
LOGGER.warn("Failed to start world gen due to concurrency");
newWgs.close(false);
}
} else if (!shouldDoWorldGen && wgs != null) {
while (!worldGenState.compareAndSet(wgs, null)) {
wgs = worldGenState.get();
if (wgs == null) return;
}
wgs.close(true).join(); //TODO: Make it async.
}
}
if (wgs != null) {
wgs.batchGenerator.update();
wgs.generationQueue.pollAndStartClosest(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos()));
}
}
@@ -16,8 +16,6 @@ public interface IClientLevel extends ILevel {
void render(Mat4f mcModelViewMatrix, Mat4f mcProjectionMatrix, float partialTicks, IProfilerWrapper profiler);
RenderBufferHandler getRenderBufferHandler();
int computeBaseColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper block);
IClientLevelWrapper getClientLevelWrapper();
@@ -106,16 +106,16 @@ public class LodRenderer
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
public EDebugMode previousDebugMode = null;
public final IClientLevel level;
public final RenderBufferHandler bufferHandler;
// The shader program
LodRenderProgram shaderProgram = null;
public QuadElementBuffer quadIBO = null;
public boolean isSetupComplete = false;
public LodRenderer(IClientLevel level)
public LodRenderer(RenderBufferHandler bufferHandler)
{
this.level = level;
this.bufferHandler = bufferHandler;
}
private boolean closeCalled = false;
@@ -147,9 +147,6 @@ public class LodRenderer
GLProxy glProxy = GLProxy.getInstance();
if (Config.Client.Graphics.FogQuality.disableVanillaFog.get())
MC_RENDER.tryDisableVanillaFog();
// The Buffer manager
RenderBufferHandler bufferHandler = level.getRenderBufferHandler();
//===================//
// draw params setup //