Fix changing vertical quality not updating render data

This commit is contained in:
James Seibel
2023-02-08 07:31:57 -06:00
parent 6a2298dff9
commit 1b137eae50
5 changed files with 133 additions and 35 deletions
@@ -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; }