Fix changing vertical quality not updating render data
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
package com.seibel.lod.core.datatype.column;
|
||||
|
||||
import com.seibel.lod.core.ModInfo;
|
||||
import com.seibel.lod.core.datatype.column.accessor.*;
|
||||
import com.seibel.lod.core.datatype.column.render.ColumnRenderBuffer;
|
||||
import com.seibel.lod.core.datatype.full.ChunkSizedData;
|
||||
@@ -29,12 +30,13 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
* Stores the color data used when generating OpenGL buffers.
|
||||
*
|
||||
* @author Leetom
|
||||
* @version 2022-10-5
|
||||
* @version 2022-2-7
|
||||
*/
|
||||
public class ColumnRenderSource implements ILodRenderSource, IColumnDatatype
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
public static final boolean DO_SAFETY_CHECKS = true; // TODO: this could potentially be replaced with "ModInfo.IS_DEV_BUILD"
|
||||
|
||||
public static final boolean DO_SAFETY_CHECKS = ModInfo.IS_DEV_BUILD;
|
||||
public static final byte SECTION_SIZE_OFFSET = 6;
|
||||
public static final int SECTION_SIZE = 1 << SECTION_SIZE_OFFSET;
|
||||
public static final byte LATEST_VERSION = 1;
|
||||
@@ -42,12 +44,12 @@ public class ColumnRenderSource implements ILodRenderSource, IColumnDatatype
|
||||
public static final int AIR_LODS_SIZE = 16;
|
||||
public static final int AIR_SECTION_SIZE = SECTION_SIZE / AIR_LODS_SIZE;
|
||||
|
||||
public final int verticalSize;
|
||||
public int verticalSize;
|
||||
public final DhSectionPos sectionPos;
|
||||
public final int yOffset;
|
||||
|
||||
public final long[] dataContainer;
|
||||
public final int[] airDataContainer;
|
||||
public long[] dataContainer;
|
||||
public int[] airDataContainer;
|
||||
|
||||
public final DebugSourceFlag[] debugSourceFlags;
|
||||
|
||||
@@ -58,9 +60,9 @@ public class ColumnRenderSource implements ILodRenderSource, IColumnDatatype
|
||||
//FIXME: Temp Hack
|
||||
private long lastNs = -1;
|
||||
/** 10 sec */
|
||||
private static final long SWAP_TIMEOUT = 10_000_000_000L;
|
||||
private static final long SWAP_TIMEOUT = 10_000000000L;
|
||||
/** 1 sec */
|
||||
private static final long SWAP_BUSY_COLLISION_TIMEOUT = 1_000_000_000L;
|
||||
private static final long SWAP_BUSY_COLLISION_TIMEOUT = 1_000000000L;
|
||||
|
||||
private CompletableFuture<ColumnRenderBuffer> inBuildRenderBuffer = null;
|
||||
private final Reference<ColumnRenderBuffer> usedBuffer = new Reference<>();
|
||||
@@ -77,8 +79,8 @@ public class ColumnRenderSource implements ILodRenderSource, IColumnDatatype
|
||||
public ColumnRenderSource(DhSectionPos sectionPos, int maxVerticalSize, int yOffset)
|
||||
{
|
||||
this.verticalSize = maxVerticalSize;
|
||||
this.dataContainer = new long[SECTION_SIZE * SECTION_SIZE * verticalSize];
|
||||
this.airDataContainer = new int[AIR_SECTION_SIZE * AIR_SECTION_SIZE * verticalSize];
|
||||
this.dataContainer = new long[SECTION_SIZE * SECTION_SIZE * this.verticalSize];
|
||||
this.airDataContainer = new int[AIR_SECTION_SIZE * AIR_SECTION_SIZE * this.verticalSize];
|
||||
this.debugSourceFlags = new DebugSourceFlag[SECTION_SIZE * SECTION_SIZE];
|
||||
this.sectionPos = sectionPos;
|
||||
this.yOffset = yOffset;
|
||||
@@ -254,6 +256,9 @@ public class ColumnRenderSource implements ILodRenderSource, IColumnDatatype
|
||||
|
||||
// validate we are writing for the same location
|
||||
LodUtil.assertTrue(src.sectionPos.equals(this.sectionPos));
|
||||
|
||||
// change the vertical size if necessary (this can happen if the vertical quality was changed in the config)
|
||||
this.clearAndChangeVerticalSize(src.verticalSize);
|
||||
// validate both objects have the same number of dataPoints
|
||||
LodUtil.assertTrue(src.verticalSize == this.verticalSize);
|
||||
|
||||
@@ -274,8 +279,10 @@ public class ColumnRenderSource implements ILodRenderSource, IColumnDatatype
|
||||
int srcGenMode = ColumnFormat.getGenerationMode(src.dataContainer[i]);
|
||||
|
||||
if (srcGenMode == 0)
|
||||
{
|
||||
// the source hasn't been generated, don't write it
|
||||
continue;
|
||||
}
|
||||
|
||||
// this object's column is older than the source's column, update it
|
||||
if (thisGenMode <= srcGenMode)
|
||||
@@ -288,6 +295,20 @@ public class ColumnRenderSource implements ILodRenderSource, IColumnDatatype
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* If the newVerticalSize is different than the current verticalSize,
|
||||
* this will delete any data currently in this object and re-size it. <Br>
|
||||
* Otherwise this method will do nothing.
|
||||
*/
|
||||
private void clearAndChangeVerticalSize(int newVerticalSize)
|
||||
{
|
||||
if (newVerticalSize != this.verticalSize)
|
||||
{
|
||||
this.verticalSize = newVerticalSize;
|
||||
this.dataContainer = new long[SECTION_SIZE * SECTION_SIZE * this.verticalSize];
|
||||
this.airDataContainer = new int[AIR_SECTION_SIZE * AIR_SECTION_SIZE * this.verticalSize];
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fastWrite(ChunkSizedData chunkData, IDhClientLevel level) { FullToColumnTransformer.writeFullDataChunkToColumnData(this, level, chunkData); }
|
||||
@@ -310,7 +331,7 @@ public class ColumnRenderSource implements ILodRenderSource, IColumnDatatype
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxLodCount() { return SECTION_SIZE * SECTION_SIZE * getVerticalSize(); }
|
||||
public int getMaxLodCount() { return SECTION_SIZE * SECTION_SIZE * this.getVerticalSize(); }
|
||||
|
||||
@Override
|
||||
public long getRoughRamUsageInBytes() { return (long) this.dataContainer.length * Long.BYTES; }
|
||||
@@ -445,7 +466,7 @@ public class ColumnRenderSource implements ILodRenderSource, IColumnDatatype
|
||||
}
|
||||
}
|
||||
|
||||
public DebugSourceFlag debugGetFlag(int ox, int oz) { return debugSourceFlags[ox * SECTION_SIZE + oz]; }
|
||||
public DebugSourceFlag debugGetFlag(int ox, int oz) { return this.debugSourceFlags[ox * SECTION_SIZE + oz]; }
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -24,4 +24,7 @@ public interface ILodRenderSourceProvider extends AutoCloseable
|
||||
/** Returns true if the data was refreshed, false otherwise */
|
||||
boolean refreshRenderSource(ILodRenderSource source);
|
||||
|
||||
/** Deletes any data stored in the render cache so it can be re-created */
|
||||
void deleteRenderCache();
|
||||
|
||||
}
|
||||
|
||||
@@ -31,14 +31,18 @@ public class RenderFileHandler implements ILodRenderSourceProvider
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
private final ExecutorService renderCacheThread = LodUtil.makeSingleThreadPool("RenderCacheThread");
|
||||
private final ConcurrentHashMap<DhSectionPos, RenderMetaDataFile> files = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<DhSectionPos, RenderMetaDataFile> filesBySectionPos = new ConcurrentHashMap<>();
|
||||
|
||||
private final IDhClientLevel level;
|
||||
private final File saveDir;
|
||||
private final IDataSourceProvider dataSourceProvider;
|
||||
|
||||
private final ConcurrentHashMap<DhSectionPos, Object> cacheUpdateLockBySectionPos = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
public RenderFileHandler(IDataSourceProvider sourceProvider, IDhClientLevel level, File saveRootDir)
|
||||
|
||||
|
||||
public RenderFileHandler(IDataSourceProvider sourceProvider, IDhClientLevel level, File saveRootDir)
|
||||
{
|
||||
this.dataSourceProvider = sourceProvider;
|
||||
this.level = level;
|
||||
@@ -144,7 +148,7 @@ public class RenderFileHandler implements ILodRenderSourceProvider
|
||||
}
|
||||
|
||||
// Add this file to the list of files.
|
||||
this.files.put(pos, fileToUse);
|
||||
this.filesBySectionPos.put(pos, fileToUse);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,7 +156,7 @@ public class RenderFileHandler implements ILodRenderSourceProvider
|
||||
@Override
|
||||
public CompletableFuture<ILodRenderSource> read(DhSectionPos pos)
|
||||
{
|
||||
RenderMetaDataFile metaFile = this.files.get(pos);
|
||||
RenderMetaDataFile metaFile = this.filesBySectionPos.get(pos);
|
||||
if (metaFile == null)
|
||||
{
|
||||
RenderMetaDataFile newMetaFile;
|
||||
@@ -166,7 +170,7 @@ public class RenderFileHandler implements ILodRenderSourceProvider
|
||||
return null;
|
||||
}
|
||||
|
||||
metaFile = this.files.putIfAbsent(pos, newMetaFile); // This is a CAS with expected null value.
|
||||
metaFile = this.filesBySectionPos.putIfAbsent(pos, newMetaFile); // This is a CAS with expected null value.
|
||||
if (metaFile == null)
|
||||
{
|
||||
metaFile = newMetaFile;
|
||||
@@ -216,7 +220,7 @@ public class RenderFileHandler implements ILodRenderSourceProvider
|
||||
this.writeRecursively(sectPos.getChildByIndex(3), chunkData);
|
||||
}
|
||||
|
||||
RenderMetaDataFile metaFile = this.files.get(sectPos);
|
||||
RenderMetaDataFile metaFile = this.filesBySectionPos.get(sectPos);
|
||||
// Fast path: if there is a file for this section, just write to it.
|
||||
if (metaFile != null)
|
||||
{
|
||||
@@ -229,7 +233,7 @@ public class RenderFileHandler implements ILodRenderSourceProvider
|
||||
public CompletableFuture<Void> flushAndSave()
|
||||
{
|
||||
ArrayList<CompletableFuture<Void>> futures = new ArrayList<>();
|
||||
for (RenderMetaDataFile metaFile : this.files.values())
|
||||
for (RenderMetaDataFile metaFile : this.filesBySectionPos.values())
|
||||
{
|
||||
futures.add(metaFile.flushAndSave(this.renderCacheThread));
|
||||
}
|
||||
@@ -246,7 +250,7 @@ public class RenderFileHandler implements ILodRenderSourceProvider
|
||||
public void close()
|
||||
{
|
||||
ArrayList<CompletableFuture<Void>> futures = new ArrayList<>();
|
||||
for (RenderMetaDataFile metaFile : this.files.values())
|
||||
for (RenderMetaDataFile metaFile : this.filesBySectionPos.values())
|
||||
{
|
||||
futures.add(metaFile.flushAndSave(this.renderCacheThread));
|
||||
}
|
||||
@@ -267,11 +271,9 @@ public class RenderFileHandler implements ILodRenderSourceProvider
|
||||
new ColumnRenderSource(file.pos, vertSize, this.level.getMinY()));
|
||||
}
|
||||
|
||||
private final ConcurrentHashMap<DhSectionPos, Object> cacheRecreationGuards = new ConcurrentHashMap<>();
|
||||
|
||||
private void updateCache(ILodRenderSource data, RenderMetaDataFile file)
|
||||
{
|
||||
if (this.cacheRecreationGuards.putIfAbsent(file.pos, new Object()) != null)
|
||||
if (this.cacheUpdateLockBySectionPos.putIfAbsent(file.pos, new Object()) != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -306,7 +308,7 @@ public class RenderFileHandler implements ILodRenderSourceProvider
|
||||
LOGGER.error("Exception when updating render file using data source: ", ex);
|
||||
}
|
||||
return null;
|
||||
}).thenRun(() -> this.cacheRecreationGuards.remove(file.pos));
|
||||
}).thenRun(() -> this.cacheUpdateLockBySectionPos.remove(file.pos));
|
||||
}
|
||||
|
||||
public ILodRenderSource onRenderFileLoaded(ILodRenderSource data, RenderMetaDataFile file)
|
||||
@@ -348,7 +350,7 @@ public class RenderFileHandler implements ILodRenderSourceProvider
|
||||
|
||||
public boolean refreshRenderSource(ILodRenderSource source)
|
||||
{
|
||||
RenderMetaDataFile file = this.files.get(source.getSectionPos());
|
||||
RenderMetaDataFile file = this.filesBySectionPos.get(source.getSectionPos());
|
||||
if (source instanceof PlaceHolderRenderSource)
|
||||
{
|
||||
if (file == null || file.metaData == null)
|
||||
@@ -368,4 +370,24 @@ public class RenderFileHandler implements ILodRenderSourceProvider
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public void deleteRenderCache()
|
||||
{
|
||||
// delete each file in the cache directory
|
||||
File[] renderFiles = this.saveDir.listFiles();
|
||||
if (renderFiles != null)
|
||||
{
|
||||
for (File renderFile : renderFiles)
|
||||
{
|
||||
if (!renderFile.delete())
|
||||
{
|
||||
LOGGER.error("Unable to delete render file: " + renderFile.getPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// clear the cached files
|
||||
this.filesBySectionPos.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.seibel.lod.core.render;
|
||||
|
||||
import com.seibel.lod.api.enums.config.EVerticalQuality;
|
||||
import com.seibel.lod.core.config.Config;
|
||||
import com.seibel.lod.core.datatype.column.ColumnRenderSource;
|
||||
import com.seibel.lod.core.level.IDhClientLevel;
|
||||
import com.seibel.lod.core.pos.DhBlockPos2D;
|
||||
@@ -55,8 +57,14 @@ public class LodQuadTree implements AutoCloseable
|
||||
private final IDhClientLevel level; //FIXME: Proper hierarchy to remove this reference!
|
||||
|
||||
|
||||
// used to determine if the render data needs to be regenerated
|
||||
// TODO there should be a better way of determining when the render data should be regenerated
|
||||
private EVerticalQuality previousVerticalQualitySetting = null;
|
||||
|
||||
/**
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Constructor of the quadTree
|
||||
* @param viewDistance View distance in blocks
|
||||
* @param initialPlayerX player x block coordinate
|
||||
@@ -216,7 +224,7 @@ public class LodQuadTree implements AutoCloseable
|
||||
for (int sectionDetailLevel = TREE_LOWEST_DETAIL_LEVEL; sectionDetailLevel < this.numbersOfSectionDetailLevels; sectionDetailLevel++)
|
||||
{
|
||||
Pos2D expectedCenterPos = new Pos2D(BitShiftUtil.divideByPowerOfTwo(playerPos.x, sectionDetailLevel), BitShiftUtil.divideByPowerOfTwo(playerPos.z, sectionDetailLevel));
|
||||
MovableGridRingList<LodRenderSection> gridList = this.renderSectionRingLists[sectionDetailLevel- TREE_LOWEST_DETAIL_LEVEL];
|
||||
MovableGridRingList<LodRenderSection> gridList = this.renderSectionRingLists[sectionDetailLevel-TREE_LOWEST_DETAIL_LEVEL];
|
||||
|
||||
if (!gridList.getCenter().equals(expectedCenterPos))
|
||||
{
|
||||
@@ -226,6 +234,26 @@ public class LodQuadTree implements AutoCloseable
|
||||
}
|
||||
|
||||
|
||||
|
||||
// determine if the render data should be regenerated
|
||||
// TODO this should be replaced with an API method call that is fired by modified config values
|
||||
boolean invalidateRenderCaches = false;
|
||||
if (this.previousVerticalQualitySetting == null)
|
||||
{
|
||||
this.previousVerticalQualitySetting = Config.Client.Graphics.Quality.verticalQuality.get();
|
||||
}
|
||||
else if (this.previousVerticalQualitySetting != Config.Client.Graphics.Quality.verticalQuality.get())
|
||||
{
|
||||
invalidateRenderCaches = true;
|
||||
}
|
||||
|
||||
if (invalidateRenderCaches)
|
||||
{
|
||||
this.invalidateRenderCache();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// TODO: inline comments should be added everywhere for this tick pass, so this comment block should be removed (having duplicate comments in two places is a bad idea)
|
||||
// First tick pass: update all sections' childCount from bottom level to top level. Step:
|
||||
// If sectLevel is bottom && section != null:
|
||||
@@ -617,6 +645,37 @@ public class LodQuadTree implements AutoCloseable
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-creates the color, render data.
|
||||
* This method should be called after resource packs are changed or LOD settings are modified.
|
||||
*/
|
||||
private void invalidateRenderCache()
|
||||
{
|
||||
// TODO add a delay between the method being fired and any data getting cleared,
|
||||
// this would be to prevent clearing the same data 5 times in rapid succession
|
||||
// when the user is switching through settings in the config
|
||||
|
||||
LOGGER.info("Render cache invalidated");
|
||||
|
||||
// clear each ring list
|
||||
for (byte sectionDetailLevel = TREE_LOWEST_DETAIL_LEVEL; sectionDetailLevel < this.numbersOfSectionDetailLevels; sectionDetailLevel++)
|
||||
{
|
||||
MovableGridRingList<LodRenderSection> ringList = this.renderSectionRingLists[sectionDetailLevel-TREE_LOWEST_DETAIL_LEVEL];
|
||||
if (ringList != null)
|
||||
{
|
||||
ringList.clear((section) -> section.dispose());
|
||||
|
||||
LOGGER.info("Finished deleting render files for detail level ["+sectionDetailLevel+"]...");
|
||||
}
|
||||
}
|
||||
|
||||
// delete the cache files
|
||||
this.renderSourceProvider.deleteRenderCache();
|
||||
|
||||
// update the previous quality setting
|
||||
this.previousVerticalQualitySetting = Config.Client.Graphics.Quality.verticalQuality.get();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -32,8 +32,6 @@ public class LodRenderSection
|
||||
private ILodRenderSource renderSource;
|
||||
private ILodRenderSourceProvider renderSourceProvider = null;
|
||||
|
||||
private EVerticalQuality previousVerticalQualitySetting = null;
|
||||
|
||||
|
||||
|
||||
// Create sub region
|
||||
@@ -85,11 +83,7 @@ public class LodRenderSection
|
||||
// LOD provider //
|
||||
//==============//
|
||||
|
||||
public void load(ILodRenderSourceProvider renderDataProvider)
|
||||
{
|
||||
this.renderSourceProvider = renderDataProvider;
|
||||
this.previousVerticalQualitySetting = Config.Client.Graphics.Quality.verticalQuality.get();
|
||||
}
|
||||
public void load(ILodRenderSourceProvider renderDataProvider) { this.renderSourceProvider = renderDataProvider; }
|
||||
public void reload(ILodRenderSourceProvider renderDataProvider)
|
||||
{
|
||||
if (this.loadFuture != null)
|
||||
@@ -105,7 +99,6 @@ public class LodRenderSection
|
||||
}
|
||||
|
||||
this.loadFuture = renderDataProvider.read(this.pos);
|
||||
this.previousVerticalQualitySetting = Config.Client.Graphics.Quality.verticalQuality.get();
|
||||
}
|
||||
|
||||
|
||||
@@ -159,7 +152,7 @@ public class LodRenderSection
|
||||
//FIXME: Used by RenderBufferHandler
|
||||
public int FIXME_BYPASS_DONT_USE_getChildCount() { return this.childCount; }
|
||||
|
||||
public boolean isOutdated() { return this.previousVerticalQualitySetting != Config.Client.Graphics.Quality.verticalQuality.get() || (this.renderSource != null && !this.renderSource.isValid()); }
|
||||
public boolean isOutdated() { return this.renderSource != null && !this.renderSource.isValid(); }
|
||||
|
||||
public ILodRenderSource getRenderSource() { return this.renderSource; }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user