Fix missing move. Also moved needRegenBuffer flag to RenderRegion

This commit is contained in:
tom lee
2022-02-25 15:13:39 +08:00
parent 6e63e39cd8
commit 8d18ba861e
7 changed files with 127 additions and 132 deletions
@@ -121,6 +121,14 @@ public class LodBufferBuilderFactory {
}
public void setRegionNeedRegen(int regionX, int regionZ) {
MovableGridRingList<RenderRegion> r = renderRegions;
if (r==null) return;
RenderRegion rr = r.get(regionX, regionZ);
if (rr==null) return;
rr.setNeedRegen();
}
/**
* Create a thread to asynchronously generate LOD buffers centered around the
* given camera X and Z. <br>
@@ -158,6 +166,9 @@ public class LodBufferBuilderFactory {
LevelPosUtil.getRegion(LodUtil.BLOCK_DETAIL_LEVEL, playerX),
LevelPosUtil.getRegion(LodUtil.BLOCK_DETAIL_LEVEL, playerZ));
ApiShared.LOGGER.info("============Render Regions rebuilt============");
} else {
renderRegions.move(LevelPosUtil.getRegion(LodUtil.BLOCK_DETAIL_LEVEL, playerX),
LevelPosUtil.getRegion(LodUtil.BLOCK_DETAIL_LEVEL, playerZ), RenderRegion::close);
}
}
@@ -223,7 +223,6 @@ public class LodBuilder
// chunk.getChunkPosX(), chunk.getChunkPosZ(), chunk.getMinX(), chunk.getMinZ(), config.distanceGenerationMode);
region.addChunkOfData((byte)0, chunkX*16, chunkZ*16, 16, 16, data, data.length/16/16, override);
region.regenerateLodFromArea((byte)0, chunkX*16, chunkZ*16, 16, 16);
lodDim.regenDimensionBuffers = true;
if (!region.doesDataExist((byte)0, chunkX*16, chunkZ*16, config.distanceGenerationMode))
throw new RuntimeException("data at detail 0 is still null after writes to it!");
@@ -285,7 +284,6 @@ public class LodBuilder
LevelPosUtil.convert(LodUtil.CHUNK_DETAIL_LEVEL, chunkX, targetLevel),
LevelPosUtil.convert(LodUtil.CHUNK_DETAIL_LEVEL, chunkZ, targetLevel),
lodCount, lodCount);
lodDim.regenDimensionBuffers = true;
if (!region.doesDataExist(targetLevel,
LevelPosUtil.convert(LodUtil.CHUNK_DETAIL_LEVEL, chunkX, targetLevel),
@@ -34,7 +34,6 @@ import com.seibel.lod.core.enums.config.GenerationPriority;
import com.seibel.lod.core.enums.config.VerticalQuality;
import com.seibel.lod.core.handlers.LodDimensionFileHandler;
import com.seibel.lod.core.objects.PosToGenerateContainer;
import com.seibel.lod.core.objects.PosToRenderContainer;
import com.seibel.lod.core.util.DataPointUtil;
import com.seibel.lod.core.util.DetailDistanceUtil;
import com.seibel.lod.core.util.LevelPosUtil;
@@ -84,15 +83,6 @@ public class LodDimension
//NOTE: This list pos is relative to center
private volatile RegionPos[] iteratorList = null;
/** stores if the region at the given x and z index needs to be saved to disk */
/** stores if the region at the given x and z index needs to be regenerated */
// Use int because I need Tri state:
/**
* if true that means there are regions in this dimension
* that need to have their buffers rebuilt.
*/
public volatile boolean regenDimensionBuffers = false;
private LodDimensionFileHandler fileHandler;
@@ -305,10 +295,14 @@ public class LodDimension
if (region.getMinDetailLevel() < detail) {
if (region.needSaving) return; // FIXME: A crude attempt at lowering chance of race condition!
region.cutTree(detail);
region.needRegenBuffer = 2;
regenDimensionBuffers = true;
region.needSignalToRegenBuffer = true;
}
}
if (region != null && region.needSignalToRegenBuffer) {
region.needSignalToRegenBuffer = false;
ClientApi.lodBufferBuilderFactory.setRegionNeedRegen(x+minPos.x, z+minPos.y);
}
});
if (totalDirtiedRegions > 8) this.saveDirtyRegionsToFile(false);
dirtiedRegionsRoughCount = totalDirtiedRegions;
@@ -410,9 +404,12 @@ public class LodDimension
updated = true;
}
if (updated) {
region.needRegenBuffer = 2;
region.needSignalToRegenBuffer = true;
region.needRecheckGenPoint = true;
regenDimensionBuffers = true;
}
if (region != null && region.needSignalToRegenBuffer) {
region.needSignalToRegenBuffer = false;
ClientApi.lodBufferBuilderFactory.setRegionNeedRegen(x+minPos.x, z+minPos.y);
}
});
//ApiShared.LOGGER.info("LodDim expend Region complete: " + playerPosX + "," + playerPosZ);
@@ -438,22 +435,9 @@ public class LodDimension
return false;
boolean nodeAdded = region.addVerticalData(detailLevel, posX, posZ, data, override);
if (nodeAdded) {
regenDimensionBuffers = true;
}
return nodeAdded;
}
/** marks the region at the given region position to have its buffer rebuilt */
public void markRegionBufferToRegen(int xRegion, int zRegion)
{
LodRegion r = getRegion(xRegion,zRegion);
if (r!=null) {
r.needRegenBuffer = 2;
regenDimensionBuffers = true;
}
}
/**
* Returns every position that need to be generated based on the position of the player
*/
@@ -568,23 +552,6 @@ public class LodDimension
return region.getSingleData(detailLevel, posX, posZ);
}
/**
* Returns if the buffer at the given array index needs
* to have its buffer regenerated. Also decrease the state by 1
*/
public boolean getAndClearRegionNeedBufferRegen(int regionX, int regionZ)
{
//FIXME: Use actual atomics on needRegenBuffer
LodRegion region = getRegion(regionX, regionZ);
if (region == null) return false;
int i = region.needRegenBuffer;
if (i > 0) {
region.needRegenBuffer--;
return true;
}
return false;
}
/**
* Get the data point at the given LevelPos
* in this dimension.
@@ -602,8 +569,6 @@ public class LodDimension
LodRegion region = getRegion(xRegion, zRegion);
if (region == null) return;
region.updateArea(detailLevel, posX, posZ);
region.needRegenBuffer = 2;
regenDimensionBuffers = true;
}
/** Returns true if a region exists at the given LevelPos */
@@ -73,7 +73,8 @@ public class LodRegion {
public final int regionPosZ;
public volatile boolean needRecheckGenPoint = true;
public volatile int needRegenBuffer = 2;
//public volatile int needRegenBuffer = 2; MOVED TO RENDERREGION!
public volatile boolean needSignalToRegenBuffer = true;
public volatile boolean needSaving = false;
public final AtomicInteger isWriting = new AtomicInteger(0);
@@ -138,7 +139,7 @@ public class LodRegion {
boolean updated = this.dataContainer[detailLevel].addVerticalData(data, posX, posZ, override);
if (updated) {
needRegenBuffer = 2;
needSignalToRegenBuffer = true;
needSaving = true;
}
return updated;
@@ -166,7 +167,7 @@ public class LodRegion {
//ApiShared.LOGGER.info("addChunkOfData(region:{}, level:{}, x:{}, z:{}, wx:{}, wz:{}, override:{}, updated:{})",
// getRegionPos(), detailLevel, posX, posZ, widthX, widthZ, override, updated);
if (updated) {
needRegenBuffer = 2;
needSignalToRegenBuffer = true;
needSaving = true;
} else {
/*ApiShared.LOGGER.info("addChunkOfData nothing changed. Datapoint: {}\n Upper Datapoint: {}",
@@ -453,6 +454,7 @@ public class LodRegion {
for (byte up = (byte) (Math.max(detailLevel, minDetailLevel) + 1); up <= LodUtil.REGION_DETAIL_LEVEL; up++) {
update(up, LevelPosUtil.convert(detailLevel, posX, up), LevelPosUtil.convert(detailLevel, posZ, up));
}
needSignalToRegenBuffer = true;
}
public boolean regenerateLodFromArea(byte detailLevel, int posX, int posZ, int widthX, int widthZ) {
@@ -482,8 +484,8 @@ public class LodRegion {
// ApiShared.LOGGER.info(" - Shink: (level:{}, x:{}, z:{}, wx:{}, wz:{})", detailLevel, modPosX, modPosZ, widthX, widthZ);
chunkUpdate(detailLevel, modPosX, modPosZ, widthX, widthZ);
} while (detailLevel < LodUtil.REGION_DETAIL_LEVEL);
needRegenBuffer = 2;
needSignalToRegenBuffer = true;
return true;
}
@@ -37,22 +37,29 @@ public abstract class RenderBuffer implements AutoCloseable
final public void build(Runnable r) {
_lockThread(State.Building);
r.run();
_unlockThread(State.Building);
try {
r.run();
} finally {
_unlockThread(State.Building);
}
}
/* Return false if current renderMethod is not suited for current builder
* This will auto close the object if returning false. */
final public boolean tryUploadBuffers(LodQuadBuilder builder, GpuUploadMethod uploadMethod) {
_lockThread(State.Uploading);
boolean successful = uploadBuffers(builder, uploadMethod);
if (!successful) {
_unlockThreadTo(State.Uploading, State.Closed);
close();
return false;
boolean successful = false;
try {
successful = uploadBuffers(builder, uploadMethod);
return successful;
} finally {
if (!successful) {
_unlockThreadTo(State.Uploading, State.Closed);
close();
} else {
_unlockThread(State.Uploading);
}
}
_unlockThread(State.Uploading);
return true;
}
// ======================================================================
@@ -4,6 +4,7 @@ import java.util.ConcurrentModificationException;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import com.seibel.lod.core.api.ApiShared;
@@ -42,6 +43,10 @@ public class RenderRegion implements AutoCloseable
public static final boolean ENABLE_VERBOSE_LOGGING = false;
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
/** stores if the region at the given x and z index needs to be regenerated */
// Use int because I need Tri state:
private AtomicInteger needRegen = new AtomicInteger(2);
private enum BackState {
Unused,
Building,
@@ -71,19 +76,25 @@ public class RenderRegion implements AutoCloseable
return lodDim == this.lodDim && regPos.equals(regionPos);
}
public void setNeedRegen() {
needRegen.set(2);
}
public Optional<CompletableFuture<Void>> updateStatus(Executor bufferUploader, Executor bufferBuilder, boolean alwaysRegen, int playerPosX, int playerPosZ) {
if (alwaysRegen) setNeedRegen();
BackState state = backState.get();
if (state != BackState.Unused) {
if (ENABLE_VERBOSE_LOGGING) ApiShared.LOGGER.info("{}: UpdateStatus rejected. Cause: BackState is {}", regionPos, state);
return Optional.empty();
}
LodRegion r = lodDim.getRegion(regionPos.x, regionPos.z);
if (r==null) {
if (ENABLE_VERBOSE_LOGGING) ApiShared.LOGGER.info("{}: UpdateStatus rejected. Cause: Region is null", regionPos);
return Optional.empty();
}
if (!alwaysRegen && r.needRegenBuffer == 0) {
if (needRegen.get() == 0) {
if (ENABLE_VERBOSE_LOGGING) ApiShared.LOGGER.info("{}: UpdateStatus rejected. Cause: Region doesn't need regen", regionPos);
return Optional.empty();
}
@@ -92,7 +103,7 @@ public class RenderRegion implements AutoCloseable
if (ENABLE_VERBOSE_LOGGING) ApiShared.LOGGER.info("{}: UpdateStatus rejected. Cause: CAS on BackState failed: ", backState.get());
return Optional.empty();
}
r.needRegenBuffer--;
needRegen.decrementAndGet();
return Optional.of(startBuid(bufferUploader, bufferBuilder, r, lodDim, playerPosX, playerPosZ));
}
@@ -150,7 +161,7 @@ public class RenderRegion implements AutoCloseable
adjRegions[dir.ordinal() - 2] = lodDim.getRegion(regionPos.x+dir.getNormal().x, regionPos.z+dir.getNormal().z);
}
} catch (Throwable t) {
region.needRegenBuffer = 2;
setNeedRegen();
if (!backState.compareAndSet(BackState.Building, BackState.Unused)) {
ApiShared.LOGGER.error("\"Lod Builder Starter\""
+ " encountered error on catching exceptions and fallback on starting build task: ",
@@ -175,7 +186,9 @@ public class RenderRegion implements AutoCloseable
ApiShared.LOGGER.error("\"LodNodeBufferBuilder\" was unable to build quads: ", e3);
throw e3;
}
}, bufferBuilder).thenAcceptAsync((builder) -> {
}, bufferBuilder)
.thenAcceptAsync((builder) -> {
try {
if (ENABLE_EVENT_STEP_LOGGING) ApiShared.LOGGER.info("RenderRegion start Upload @ {}", regionPos);
GLProxy glProxy = GLProxy.getInstance();
@@ -203,7 +216,7 @@ public class RenderRegion implements AutoCloseable
ApiShared.LOGGER.error("\"LodNodeBufferBuilder\" was unable to upload buffer: ", e3);
}
}, bufferUploader).exceptionallyCompose((e) -> {
region.needRegenBuffer = 2;
setNeedRegen();
if (!backState.compareAndSet(BackState.Building, BackState.Unused)) {
ApiShared.LOGGER.error("\"LodNodeBufferBuilder\""
+ " encountered error on exit: ",
@@ -305,66 +318,71 @@ public class RenderRegion implements AutoCloseable
// not
// to always have a wall underwater
for (LodDirection lodDirection : LodDirection.ADJ_DIRECTIONS) {
int xAdj = posX + lodDirection.getNormal().x;
int zAdj = posZ + lodDirection.getNormal().z;
byte adjDetail = detailLevel;
int chunkXAdj = LevelPosUtil.getChunkPos(detailLevel, xAdj);
int chunkZAdj = LevelPosUtil.getChunkPos(detailLevel, zAdj);
Boolean isRenderedAdj = chunkGrid.get(chunkXAdj, chunkZAdj);
boolean adjSkip = isRenderedAdj!=null && isRenderedAdj;
//We check if the adjPos is to be rendered
boolean renderAdjPos = posToRender.contains(detailLevel, xAdj, zAdj);
boolean doesAdjLowerPosExist = detailLevel==0 ? false : posToRender.contains((byte) (detailLevel-1), xAdj*2, zAdj*2);
boolean renderLowerAdjPos = doesAdjLowerPosExist;
LodRegion adjRegion = region;
//since he system doesn't work for region border we need to check with another system
if(!renderAdjPos && (!doesAdjLowerPosExist || detailLevel==0))
{
//we compute the distance from the adjPos
double minDistance = LevelPosUtil.minDistance(detailLevel, xAdj, zAdj, playerX, playerZ) - 1.4142*(2 << detailLevel);
//we compute at which detail that position should be rendered
adjRegion = adjRegions[lodDirection.ordinal()-2];
byte minLevel;
if(adjRegion != null)
{
minLevel = (byte) Math.max(adjRegion.getMinDetailLevel(),
DetailDistanceUtil.getDetailLevelFromDistance(minDistance));
} else{
minLevel = DetailDistanceUtil.getDetailLevelFromDistance(minDistance);
}
try {
int xAdj = posX + lodDirection.getNormal().x;
int zAdj = posZ + lodDirection.getNormal().z;
byte adjDetail = detailLevel;
int chunkXAdj = LevelPosUtil.getChunkPos(detailLevel, xAdj);
int chunkZAdj = LevelPosUtil.getChunkPos(detailLevel, zAdj);
Boolean isRenderedAdj = chunkGrid.get(chunkXAdj, chunkZAdj);
boolean adjSkip = isRenderedAdj!=null && isRenderedAdj;
//we check if the detail of the adjPos is equal to the correct one (region border fix)
//or if the detail is wrong by 1 value (region+circle border fix)
renderAdjPos = detailLevel == minLevel;
renderLowerAdjPos = detailLevel==0 ? false : detailLevel-1 == minLevel;
}
if (adjRegion == null) continue;
if (renderAdjPos && !adjSkip) {
//The adj data is at same detail and is extracted
adjData[lodDirection.ordinal() - 2][0] = adjRegion.getAllData(adjDetail, xAdj, zAdj);
} else if (renderLowerAdjPos)
{
//The adj data is at lower detail and is extracted in two steps
xAdj *= 2;
zAdj *= 2;
adjDetail = (byte) (detailLevel - 1);
adjData[lodDirection.ordinal() - 2] = new long[2][];
isRenderedAdj = chunkGrid.get(chunkXAdj, chunkZAdj);
adjSkip = isRenderedAdj!=null && isRenderedAdj;
if (!adjSkip) {
//We check if the adjPos is to be rendered
boolean renderAdjPos = posToRender.contains(detailLevel, xAdj, zAdj);
boolean doesAdjLowerPosExist = detailLevel==0 ? false : posToRender.contains((byte) (detailLevel-1), xAdj*2, zAdj*2);
boolean renderLowerAdjPos = doesAdjLowerPosExist;
LodRegion adjRegion = region;
//since he system doesn't work for region border we need to check with another system
if(!renderAdjPos && (!doesAdjLowerPosExist || detailLevel==0))
{
//we compute the distance from the adjPos
double minDistance = LevelPosUtil.minDistance(detailLevel, xAdj, zAdj, playerX, playerZ) - 1.4142*(2 << detailLevel);
//we compute at which detail that position should be rendered
adjRegion = adjRegions[lodDirection.ordinal()-2];
byte minLevel;
if(adjRegion != null)
{
minLevel = (byte) Math.max(adjRegion.getMinDetailLevel(),
DetailDistanceUtil.getDetailLevelFromDistance(minDistance));
} else{
minLevel = DetailDistanceUtil.getDetailLevelFromDistance(minDistance);
}
//we check if the detail of the adjPos is equal to the correct one (region border fix)
//or if the detail is wrong by 1 value (region+circle border fix)
renderAdjPos = detailLevel == minLevel;
renderLowerAdjPos = detailLevel==0 ? false : detailLevel-1 == minLevel;
}
if (adjRegion == null) continue;
if (renderAdjPos && !adjSkip) {
//The adj data is at same detail and is extracted
adjData[lodDirection.ordinal() - 2][0] = adjRegion.getAllData(adjDetail, xAdj, zAdj);
}
xAdj += Math.abs(lodDirection.getNormal().x);
zAdj += Math.abs(lodDirection.getNormal().z);
isRenderedAdj = chunkGrid.get(chunkXAdj, chunkZAdj);
adjSkip = isRenderedAdj!=null && isRenderedAdj;
if (!adjSkip)
} else if (renderLowerAdjPos)
{
adjData[lodDirection.ordinal() - 2][1] = adjRegion.getAllData(adjDetail, xAdj, zAdj);
//The adj data is at lower detail and is extracted in two steps
xAdj *= 2;
zAdj *= 2;
adjDetail = (byte) (detailLevel - 1);
adjData[lodDirection.ordinal() - 2] = new long[2][];
isRenderedAdj = chunkGrid.get(chunkXAdj, chunkZAdj);
adjSkip = isRenderedAdj!=null && isRenderedAdj;
if (!adjSkip) {
adjData[lodDirection.ordinal() - 2][0] = adjRegion.getAllData(adjDetail, xAdj, zAdj);
}
xAdj += Math.abs(lodDirection.getNormal().x);
zAdj += Math.abs(lodDirection.getNormal().z);
isRenderedAdj = chunkGrid.get(chunkXAdj, chunkZAdj);
adjSkip = isRenderedAdj!=null && isRenderedAdj;
if (!adjSkip)
{
adjData[lodDirection.ordinal() - 2][1] = adjRegion.getAllData(adjDetail, xAdj, zAdj);
}
}
} catch (RuntimeException e) {
ApiShared.LOGGER.warn("Failed to get adj data for [{}:{},{}] at [{}]", detailLevel, posX, posZ, lodDirection);
ApiShared.LOGGER.warn("Detail exception: ", e);
}
}
@@ -28,7 +28,6 @@ import org.lwjgl.opengl.GL32;
import com.seibel.lod.core.api.ApiShared;
import com.seibel.lod.core.builders.bufferBuilding.LodBufferBuilderFactory;
import com.seibel.lod.core.builders.lodBuilding.LodBuilder;
import com.seibel.lod.core.enums.rendering.DebugMode;
import com.seibel.lod.core.enums.rendering.FogColorMode;
import com.seibel.lod.core.enums.rendering.FogDistance;
@@ -517,7 +516,7 @@ public class LodRenderer
if (oldBool == null || !oldBool)
{
anyChanged = true;
lodDim.markRegionBufferToRegen(pos.getRegionX(), pos.getRegionZ());
lodBufferBuilderFactory.setRegionNeedRegen(pos.getRegionX(), pos.getRegionZ());
}
}
if (anyChanged) vanillaRenderedChunks = chunkList;
@@ -580,13 +579,8 @@ public class LodRenderer
if (tryFullGen) {
fullRegen = true;
lodDim.regenDimensionBuffers = false;
} else if (tryPartialGen) {
if (lodDim.regenDimensionBuffers)
{
partialRegen = true;
lodDim.regenDimensionBuffers = false;
}
partialRegen = true;
}
}