diff --git a/src/main/java/com/seibel/lod/core/builders/bufferBuilding/LodBufferBuilderFactory.java b/src/main/java/com/seibel/lod/core/builders/bufferBuilding/LodBufferBuilderFactory.java index 22559db70..f1136b3bb 100644 --- a/src/main/java/com/seibel/lod/core/builders/bufferBuilding/LodBufferBuilderFactory.java +++ b/src/main/java/com/seibel/lod/core/builders/bufferBuilding/LodBufferBuilderFactory.java @@ -20,10 +20,7 @@ package com.seibel.lod.core.builders.bufferBuilding; import java.time.Duration; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.Callable; +import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -31,25 +28,20 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.locks.ReentrantLock; +import org.apache.logging.log4j.core.tools.picocli.CommandLine.ExecutionException; + import com.seibel.lod.core.api.ApiShared; -import com.seibel.lod.core.api.ClientApi; -import com.seibel.lod.core.builders.lodBuilding.LodBuilder; -import com.seibel.lod.core.enums.LodDirection; -import com.seibel.lod.core.enums.config.GenerationPriority; -import com.seibel.lod.core.enums.config.GpuUploadMethod; -import com.seibel.lod.core.enums.config.VanillaOverdraw; -import com.seibel.lod.core.enums.rendering.DebugMode; -import com.seibel.lod.core.enums.rendering.GLProxyContext; -import com.seibel.lod.core.objects.PosToRenderContainer; -import com.seibel.lod.core.objects.RenderRegion; import com.seibel.lod.core.objects.lod.LodDimension; -import com.seibel.lod.core.objects.lod.LodRegion; import com.seibel.lod.core.objects.lod.RegionPos; -import com.seibel.lod.core.objects.opengl.LodQuadBuilder; -import com.seibel.lod.core.render.GLProxy; +import com.seibel.lod.core.objects.opengl.RenderRegion; import com.seibel.lod.core.render.LodRenderer; -import com.seibel.lod.core.util.*; -import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper; +import com.seibel.lod.core.util.LevelPosUtil; +import com.seibel.lod.core.util.LodThreadFactory; +import com.seibel.lod.core.util.LodUtil; +import com.seibel.lod.core.util.MovableGridRingList; +import com.seibel.lod.core.util.SingletonHandler; +import com.seibel.lod.core.util.SpamReducedLogger; +import com.seibel.lod.core.util.StatsMap; import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton; import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper; @@ -62,9 +54,8 @@ import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper; public class LodBufferBuilderFactory { // TODO: Do some Perf logging of Buffer Building - public static final boolean ENABLE_BUFFER_PERF_LOGGING = false; - public static final boolean ENABLE_BUFFER_SWAP_LOGGING = true; - public static final boolean ENABLE_BUFFER_UPLOAD_LOGGING = false; + public static final boolean ENABLE_BUFFER_PERF_LOGGING = true; + public static final boolean ENABLE_EVENT_LOGGING = true; public static final boolean ENABLE_LAG_SPIKE_LOGGING = false; public static final long LAG_SPIKE_THRESOLD_NS = TimeUnit.NANOSECONDS.convert(16, TimeUnit.MILLISECONDS); @@ -103,15 +94,7 @@ public class LodBufferBuilderFactory { private static LodThreadFactory bufferUploadThreadFactory = new LodThreadFactory( LodBufferBuilderFactory.class.getSimpleName() + " - upload", Thread.NORM_PRIORITY - 1); public static ExecutorService bufferUploadThread = Executors.newSingleThreadExecutor(bufferUploadThreadFactory); - - public static final long MAX_BUFFER_UPLOAD_TIMEOUT_NANOSECONDS = TimeUnit.NANOSECONDS.convert(1, TimeUnit.SECONDS); - - /** - * When uploading to a buffer that is too small, recreate it this many times - * bigger than the upload payload - */ - public static final double BUFFER_EXPANSION_MULTIPLIER = 1.3; - + /** * When buffers are first created they are allocated to this size (in Bytes). * This size will be too small, more than likely. The buffers will be expanded @@ -123,56 +106,19 @@ public class LodBufferBuilderFactory { public static int skyLightPlayer = 15; - /** - * How many buffers there are for the given region.
- * This is done because some regions may require more memory than can be - * directly allocated, so we split the regions into smaller sections.
- * This keeps track of those sections. - */ - // TODO: Check why this is unused - // public volatile int[][] numberOfBuffersPerRegion; - - /** Used when building new VBOs */ - public volatile MovableGridList buildableVbos; - /** VBOs that are sent over to the LodNodeRenderer */ - public volatile MovableGridList drawableVbos; - /** - * if this is true the LOD buffers need to be reset and the Renderer should call - * the lodGenBuffers nomatter it should have been a full or partial regen or not - */ - public volatile boolean frontBufferRequireReset = false; - public volatile boolean allBuffersRequireReset = false; - - /** - * if this is true the LOD buffers are currently being regenerated. - */ - public boolean generatingBuffers = false; - - /** - * if this is true new LOD buffers have been generated and are waiting to be - * swapped with the drawable buffers - */ - private boolean switchVbos = false; - // The hideFrontBuffer is for when switching dimensions - private volatile boolean hideFrontBuffer = false; - private volatile boolean hideBackBuffer = false; - + public MovableGridRingList renderRegions = null; + /** Size of the buffer builders in bytes last time we created them */ public int previousBufferSize = 0; - /** Width of the dimension in regions last time we created the buffers */ public int previousRegionWidth = 0; - /** - * this is used to prevent multiple threads creating, destroying, or using the - * buffers at the same time - */ - private final ReentrantLock bufferLock = new ReentrantLock(); - - private MovableGridList setsToRender; - + private boolean builderThreadRunning = false; + + public ReentrantLock regionsListLock = new ReentrantLock(); + public LodBufferBuilderFactory() { - + } /** @@ -185,344 +131,157 @@ public class LodBufferBuilderFactory { * @return whether it has started a generation task or is blocked */ public boolean updateAndSwapLodBuffersAsync(LodRenderer renderer, LodDimension lodDim, int playerX, int playerY, - int playerZ, boolean partialRegen, boolean flushBuffers) { + int playerZ, boolean fullRegen) { // only allow one generation process to happen at a time - if (generatingBuffers) - return false; + if (builderThreadRunning) return false; if (MC.getCurrentLightMap() == null) // the lighting hasn't loaded yet return false; - allBuffersRequireReset |= flushBuffers; - - boolean fullRegen; - if (switchVbos) { - fullRegen = swapBuffers(); - } else { - fullRegen = allBuffersRequireReset || frontBufferRequireReset; - } - - if (!fullRegen && !partialRegen) - return false; - - generatingBuffers = true; + builderThreadRunning = true; Runnable thread = () -> generateLodBuffersThread(renderer, lodDim, playerX, playerY, playerZ, fullRegen); - mainGenThread.execute(thread); return true; } - + + private void updateRingList(int playerX, int playerZ, int regionWidth) throws InterruptedException { + if (renderRegions != null && regionWidth != renderRegions.getSize()) { + renderRegions.clear(RenderRegion::close); + renderRegions = null; + } + LodUtil.checkInterrupts(); + if (renderRegions == null) { + renderRegions = new MovableGridRingList(regionWidth/2, + LevelPosUtil.getRegion(LodUtil.BLOCK_DETAIL_LEVEL, playerX), + LevelPosUtil.getRegion(LodUtil.BLOCK_DETAIL_LEVEL, playerZ)); + ApiShared.LOGGER.info("============Render Regions rebuilt============"); + } + } + + private void resetThreadPools(boolean dumpThread) { + if (dumpThread) { + bufferBuilderThreadFactory.dumpAllThreadStacks(); + bufferUploadThreadFactory.dumpAllThreadStacks(); + } + bufferBuilderThreads.shutdownNow(); + bufferUploadThread.shutdownNow(); + + bufferBuilderThreadFactory = new LodThreadFactory("BufferBuilder", Thread.NORM_PRIORITY - 2); + bufferBuilderThreads = Executors.newFixedThreadPool( + CONFIG.client().advanced().threading().getNumberOfBufferBuilderThreads(), + bufferBuilderThreadFactory); + + bufferUploadThreadFactory = new LodThreadFactory( + LodBufferBuilderFactory.class.getSimpleName() + " - upload", Thread.NORM_PRIORITY - 1); + bufferUploadThread = Executors.newSingleThreadExecutor(bufferUploadThreadFactory); + + } + private void generateLodBuffersThread(LodRenderer renderer, LodDimension lodDim, int playerX, int playerY, int playerZ, boolean fullRegen) { - bufferLock.lock(); - - long startTime = System.currentTimeMillis(); - ArrayList posToCleanup = new ArrayList(); - ArrayList> nodeToRenderThreads = new ArrayList>(); - + //ArrayList regionsToCleanup = new ArrayList(); try { - // round the player's block position down to the nearest chunk BlockPos - int playerRegionX = LevelPosUtil.convert(LodUtil.BLOCK_DETAIL_LEVEL, playerX, LodUtil.REGION_DETAIL_LEVEL); - int playerRegionZ = LevelPosUtil.convert(LodUtil.BLOCK_DETAIL_LEVEL, playerZ, LodUtil.REGION_DETAIL_LEVEL); - int renderRange; + regionsListLock.lockInterruptibly(); + if (ENABLE_EVENT_LOGGING) + ApiShared.LOGGER.info("BufferBuilderStarter locked the region lock! LodDim: [{}], RenderRegion: [{}]", + lodDim, renderRegions==null ? "NULL" : renderRegions.toString()); + long startTime = System.currentTimeMillis(); - if (fullRegen || buildableVbos == null || setsToRender == null) { - if (buildableVbos != null) { - buildableVbos.clear(RenderRegion::close); - } - - renderRange = lodDim.getWidth() / 2; // get lodDim half width - buildableVbos = new MovableGridList(renderRange, playerRegionX, playerRegionZ); - setsToRender = new MovableGridList(renderRange, playerRegionX, playerRegionZ); - } else { - renderRange = buildableVbos.gridCentreToEdge; - buildableVbos.move(playerRegionX, playerRegionZ, RenderRegion::close); - setsToRender.move(playerRegionX, playerRegionZ); - } - posToCleanup.ensureCapacity(buildableVbos.size()); - nodeToRenderThreads.ensureCapacity(buildableVbos.size()); - - // ================================// - // create the nodeToRenderThreads // - // ================================// - - skyLightPlayer = MC.getWrappedClientWorld().getSkyLight(playerX, playerY, playerZ); - // int minCullingRange = - // SingletonHandler.get(ILodConfigWrapperSingleton.class).client().graphics().advancedGraphics().getBacksideCullingRange(); - // int cullingRangeX = Math.max((int)(1.5 * Math.abs(lastX - playerX)), - // minCullingRange); - // int cullingRangeZ = Math.max((int)(1.5 * Math.abs(lastZ - playerZ)), - // minCullingRange); - List> futuresBuffer = new LinkedList>(); - for (int indexX = 0; indexX < buildableVbos.gridSize; indexX++) { - for (int indexZ = 0; indexZ < buildableVbos.gridSize; indexZ++) { - final int regionX = indexX + buildableVbos.getCenterX() - buildableVbos.gridCentreToEdge; - final int regionZ = indexZ + buildableVbos.getCenterY() - buildableVbos.gridCentreToEdge; - - boolean needRegen = lodDim.getAndClearRegionNeedBufferRegen(regionX, regionZ); - needRegen |= fullRegen; - if (!needRegen) - continue; - - LodRegion region = lodDim.getRegion(regionX, regionZ); - if (region == null) - continue; - - RegionPos regionPos = new RegionPos(regionX, regionZ); - posToCleanup.add(regionPos); - - byte minDetail = region.getMinDetailLevel(); - final int pX = playerX; - final int pZ = playerZ; - - class ResultPair { - final LodQuadBuilder quadBuilder; - final RegionPos regionPos; - - ResultPair(LodQuadBuilder quadBuilder, RegionPos regionPos) { - this.quadBuilder = quadBuilder; - this.regionPos = regionPos; - } - } - - CompletableFuture future = CompletableFuture.supplyAsync(() -> { - LodQuadBuilder quadBuilder = new LodQuadBuilder(6); - makeLodRenderData(quadBuilder, lodDim, regionPos, pX, pZ, minDetail); - return new ResultPair(quadBuilder, regionPos); - }, bufferBuilderThreads).whenCompleteAsync((result, e) -> { - if (e != null) - return; - try { - uploadBuffers(result.quadBuilder, result.regionPos); - } catch (Throwable e3) { - ApiShared.LOGGER.error("\"LodNodeBufferBuilder\" was unable to upload buffer: ", e3); - } - }, bufferUploadThread); - futuresBuffer.add(future); - } // region z - } // region z - - // ================================// - // execute the nodeToRenderThreads // - // ================================// - - long executeStart = System.currentTimeMillis(); - // wait for all threads to finish - CompletableFuture allFutures = CompletableFuture - .allOf(futuresBuffer.toArray(new CompletableFuture[futuresBuffer.size()])); try { - allFutures.get(1, TimeUnit.MINUTES); - } catch (TimeoutException te) { - ApiShared.LOGGER.error("LodBufferBuilder timed out: ", te); - bufferBuilderThreadFactory.dumpAllThreadStacks(); - bufferUploadThreadFactory.dumpAllThreadStacks(); - bufferBuilderThreads.shutdownNow(); - bufferUploadThread.shutdownNow(); - bufferBuilderThreadFactory = new LodThreadFactory("BufferBuilder", Thread.NORM_PRIORITY - 2); - bufferBuilderThreads = Executors.newFixedThreadPool( - CONFIG.client().advanced().threading().getNumberOfBufferBuilderThreads(), - bufferBuilderThreadFactory); - bufferUploadThreadFactory = new LodThreadFactory( - LodBufferBuilderFactory.class.getSimpleName() + " - upload", Thread.NORM_PRIORITY - 1); - bufferUploadThread = Executors.newSingleThreadExecutor(bufferUploadThreadFactory); - return; + updateRingList(playerX, playerZ, lodDim.getWidth()); + + // ================================// + // create the nodeToRenderThreads // + // ================================// + + skyLightPlayer = MC.getWrappedClientWorld().getSkyLight(playerX, playerY, playerZ); + // int minCullingRange = + // SingletonHandler.get(ILodConfigWrapperSingleton.class).client().graphics().advancedGraphics().getBacksideCullingRange(); + // int cullingRangeX = Math.max((int)(1.5 * Math.abs(lastX - playerX)), + // minCullingRange); + // int cullingRangeZ = Math.max((int)(1.5 * Math.abs(lastZ - playerZ)), + // minCullingRange); + + MovableGridRingList.Pos minPos = renderRegions.getMinInRange(); + MovableGridRingList.Pos maxPos = renderRegions.getMaxInRange(); + CompletableFuture future = CompletableFuture.completedFuture(null); + + try { + int numOfJobs = 0; + for (int regX = minPos.x; regX < maxPos.x; regX++) { + for (int regZ = minPos.y; regZ < maxPos.y; regZ++) { + RenderRegion r = renderRegions.get(regX, regZ); + RegionPos regPos = new RegionPos(regX, regZ); + if (r!=null && !r.canRender(lodDim, regPos)) { + renderRegions.set(regX, regZ, null); + r.close(); + r = null; + } + + if (r==null) { + r = new RenderRegion(regPos, lodDim); + renderRegions.set(regX, regZ, r); + } + + CompletableFuture newFuture = + r.updateStatus(bufferUploadThread, bufferBuilderThreads, fullRegen, playerX, playerZ).orElse(null); + if (newFuture != null) { + future = CompletableFuture.allOf(future, newFuture); + numOfJobs++; + } + } + } + + // ================================// + // wait on completion // + // ================================// + + long executeStart = System.currentTimeMillis(); + try { + future.get(1, TimeUnit.MINUTES); + } catch (InterruptedException | TimeoutException ie) { + throw ie; + } catch (CancellationException ce) { + throw new InterruptedException("Future interrupted"); + } catch (ExecutionException ee) { + ApiShared.LOGGER.error("LodBufferBuilder ran into trouble: ", ee.getCause()); + } + long executeEnd = System.currentTimeMillis(); + + long endTime = System.currentTimeMillis(); + long buildTime = endTime - startTime; + long executeTime = executeEnd - executeStart; + if (ENABLE_BUFFER_PERF_LOGGING) + ApiShared.LOGGER.info("Thread Build&Upload(" + numOfJobs + "/" + + (lodDim.getWidth() * lodDim.getWidth()) + (fullRegen ? "FULL" : "") + ") time: " + buildTime + + " ms" + '\n' + "thread execute time: " + executeTime + " ms"); + + } catch (InterruptedException ie) { + resetThreadPools(false); + try { + future.get(); + } catch (Throwable t) {} + throw ie; + } catch (TimeoutException te) { + ApiShared.LOGGER.error("LodBufferBuilder timed out: ", te); + resetThreadPools(true); + } + } catch (InterruptedException ie) { } catch (Exception e) { - ApiShared.LOGGER.error("LodBufferBuilder ran into trouble: ", e); + ApiShared.LOGGER.error("\"LodNodeBufferBuilder.generateLodBuffersAsync\" ran into trouble: ", e); + } finally { + if (ENABLE_EVENT_LOGGING) ApiShared.LOGGER.info("BufferBuilderStarter unlocked the region lock!"); + regionsListLock.unlock(); } - long executeEnd = System.currentTimeMillis(); - - long endTime = System.currentTimeMillis(); - long buildTime = endTime - startTime; - long executeTime = executeEnd - executeStart; - if (ENABLE_BUFFER_PERF_LOGGING) - ApiShared.LOGGER.info("Thread Build&Upload(" + nodeToRenderThreads.size() + "/" - + (lodDim.getWidth() * lodDim.getWidth()) + (fullRegen ? "FULL" : "") + ") time: " + buildTime - + " ms" + '\n' + "thread execute time: " + executeTime + " ms"); - // mark that the buildable buffers as ready to swap - switchVbos = true; - } catch (Exception e) { - ApiShared.LOGGER.error("\"LodNodeBufferBuilder.generateLodBuffersAsync\" ran into trouble: ", e); + } catch (InterruptedException ie) { } finally { - // regardless of whether we were able to successfully create - // the buffers, we are done generating. - generatingBuffers = false; - bufferLock.unlock(); + builderThreadRunning = false; } } - private RegionPos makeLodRenderData(LodQuadBuilder quadBuilder, LodDimension lodDim, RegionPos regPos, int playerX, - int playerZ, byte minDetail) {// , int cullingRangeX, int cullingRangeZ) { - - // Variable initialization - int playerChunkX = LevelPosUtil.convert(LodUtil.BLOCK_DETAIL_LEVEL, playerX, LodUtil.CHUNK_DETAIL_LEVEL); - int playerChunkZ = LevelPosUtil.convert(LodUtil.BLOCK_DETAIL_LEVEL, playerZ, LodUtil.CHUNK_DETAIL_LEVEL); - DebugMode debugMode = CONFIG.client().advanced().debugging().getDebugMode(); - - // We ask the lod dimension which block we have to render given the player - // position - PosToRenderContainer posToRender = setsToRender.get(regPos.x, regPos.z); - // previous setToRender cache - if (posToRender == null) { - posToRender = setsToRender.setAndGet(regPos.x, regPos.z, - new PosToRenderContainer(minDetail, regPos.x, regPos.z)); - } - posToRender.clear(minDetail, regPos.x, regPos.z); - lodDim.getPosToRender(posToRender, regPos, playerX, playerZ); - - for (int index = 0; index < posToRender.getNumberOfPos(); index++) { - - byte detailLevel = posToRender.getNthDetailLevel(index); - int posX = posToRender.getNthPosX(index); - int posZ = posToRender.getNthPosZ(index); - - long[] posData = lodDim.getAllData(detailLevel, posX, posZ); - if (posData == null || posData.length == 0 || !DataPointUtil.doesItExist(posData[0]) - || DataPointUtil.isVoid(posData[0])) - continue; - long[][][] adjData = new long[4][1][]; - - int chunkXdist = LevelPosUtil.getChunkPos(detailLevel, posX) - playerChunkX; - int chunkZdist = LevelPosUtil.getChunkPos(detailLevel, posZ) - playerChunkZ; - - // TODO: In the future, We don't need to ignore rendered chunks! Just build it - // and leave it for the renderer to decide! - // We don't want to render this fake block if - // The block is inside the render distance with, is not bigger than a chunk and - // is positioned in a chunk set as vanilla rendered - - // The block is in the player chunk or in a chunk adjacent to the player - if (detailLevel <= LodUtil.CHUNK_DETAIL_LEVEL && isThisPositionGoingToBeRendered( - LevelPosUtil.getChunkPos(detailLevel, posX), LevelPosUtil.getChunkPos(detailLevel, posZ))) { - continue; - } - - // we check if the block to render is not in player chunk - boolean posNotInPlayerChunk = !(chunkXdist == 0 && chunkZdist == 0); - - // We extract the adj data in the four cardinal direction - - // we first reset the adjShadeDisabled. This is used to disable the shade on the - // border when we have transparent block like water or glass - // to avoid having a "darker border" underground - // Arrays.fill(adjShadeDisabled, false); - - // We check every adj block in each direction - for (LodDirection lodDirection : LodDirection.ADJ_DIRECTIONS) { - int xAdj = posX + lodDirection.getNormal().x; - int zAdj = posZ + lodDirection.getNormal().z; - byte adjDetail = detailLevel; - chunkXdist = LevelPosUtil.getChunkPos(detailLevel, xAdj) - playerChunkX; - chunkZdist = LevelPosUtil.getChunkPos(detailLevel, zAdj) - playerChunkZ; - boolean adjPosInPlayerChunk = (chunkXdist == 0 && chunkZdist == 0); - - - //We check if the adjPos is to be rendered - boolean shouldAdjPosBeRendered = posToRender.contains(detailLevel, xAdj, zAdj); - //Then we check if the adjPos is to be rendered in a lower detail - boolean shouldLowerAdjPosBeRendered = posToRender.contains((byte) (detailLevel-1), xAdj*2, zAdj*2); - - //since he system doesn't work for region border we need to check with another system - if(!(shouldAdjPosBeRendered || shouldLowerAdjPosBeRendered)) - { - //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 - LodRegion adjRegion = lodDim.getRegion(detailLevel, xAdj, zAdj); - byte minLevel; - if(adjRegion != null) - { - minLevel = (byte) Math.max(lodDim.getRegion(detailLevel, xAdj, zAdj).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) - shouldAdjPosBeRendered = detailLevel == minLevel; - shouldLowerAdjPosBeRendered = detailLevel-1 == minLevel; - } - // If the adj block is rendered in the same region and with same detail - // and is positioned in a place that is not going to be rendered by vanilla game - // then we can set this position as adj - // We avoid cases where the adjPosition is in player chunk while the position is - // not - // to always have a wall underwater - if (shouldAdjPosBeRendered - && !isThisPositionGoingToBeRendered(LevelPosUtil.getChunkPos(adjDetail, xAdj),LevelPosUtil.getChunkPos(adjDetail, zAdj)) - && !(posNotInPlayerChunk && adjPosInPlayerChunk)) { - //The adj data is at same detail and is extracted - adjData[lodDirection.ordinal() - 2][0] = lodDim.getAllData(adjDetail, xAdj, zAdj); - }else{ - if(shouldLowerAdjPosBeRendered) - { - //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][]; - if (!isThisPositionGoingToBeRendered(LevelPosUtil.getChunkPos(adjDetail, xAdj), LevelPosUtil.getChunkPos(adjDetail, zAdj)) - && !(posNotInPlayerChunk && adjPosInPlayerChunk)) - { - adjData[lodDirection.ordinal() - 2][0] = lodDim.getAllData(adjDetail, xAdj, zAdj); - } - - xAdj += Math.abs(lodDirection.getNormal().x); - zAdj += Math.abs(lodDirection.getNormal().z); - if (!isThisPositionGoingToBeRendered(LevelPosUtil.getChunkPos(adjDetail, xAdj), LevelPosUtil.getChunkPos(adjDetail, zAdj)) - && !(posNotInPlayerChunk && adjPosInPlayerChunk)) - { - adjData[lodDirection.ordinal() - 2][1] = lodDim.getAllData(adjDetail, xAdj, zAdj); - } - } - } - } - - // We render every vertical lod present in this position - // We only stop when we find a block that is void or non-existing block - for (int i = 0; i < posData.length; i++) { - long data = posData[i]; - // If the data is not renderable (Void or non-existing) we stop since there is - // no data left in this position - if (DataPointUtil.isVoid(data) || !DataPointUtil.doesItExist(data)) - break; - - long adjDataTop = i - 1 >= 0 ? posData[i - 1] : DataPointUtil.EMPTY_DATA; - long adjDataBot = i + 1 < posData.length ? posData[i + 1] : DataPointUtil.EMPTY_DATA; - - // We send the call to create the vertices - CubicLodTemplate.addLodToBuffer(data, adjDataTop, adjDataBot, adjData, detailLevel, - LevelPosUtil.getRegionModule(detailLevel, posX), - LevelPosUtil.getRegionModule(detailLevel, posZ), quadBuilder, debugMode); - } - - } // for pos to in list to render - // the thread executed successfully - quadBuilder.mergeQuads(); - return regPos; - } - - // Will be removed in a1.7 - @Deprecated - private boolean isThisPositionGoingToBeRendered(int chunkX, int chunkZ) { - MovableGridList chunkGrid = ClientApi.renderer.vanillaRenderedChunks; - Boolean isRendered = chunkGrid.get(chunkX, chunkZ); - - // skip any chunks that Minecraft is going to render - if (isRendered == null || !isRendered) - return false; - - // check if the chunk is on the border - if (CONFIG.client().graphics().advancedGraphics().getVanillaOverdraw() == VanillaOverdraw.BORDER) - return !LodUtil.isBorderChunk(ClientApi.renderer.vanillaRenderedChunks, chunkX, chunkZ); - else - return true; - } - private final SpamReducedLogger ramLogger = new SpamReducedLogger(1); public void dumpBufferMemoryUsage() { @@ -531,18 +290,10 @@ public class LodBufferBuilderFactory { ramLogger.info("Dumping Ram Usage for buffer usage..."); StatsMap statsMap = new StatsMap(); - if (buildableVbos == null) { + if (renderRegions == null) { ramLogger.info("Buildable VBOs are null!"); } else - for (RenderRegion buffers : buildableVbos) { - if (buffers == null) - continue; - buffers.debugDumpStats(statsMap); - } - if (drawableVbos == null) { - ramLogger.info("Drawable VBOs are null!"); - } else - for (RenderRegion buffers : drawableVbos) { + for (RenderRegion buffers : renderRegions) { if (buffers == null) continue; buffers.debugDumpStats(statsMap); @@ -565,105 +316,26 @@ public class LodBufferBuilderFactory { * May have to wait for the bufferLock to open. */ public void destroyBuffers() { - MovableGridList toBeDeletedBuildableVbos; - MovableGridList toBeDeletedDrawableVbos; - bufferLock.lock(); + ApiShared.LOGGER.info("LodBufferBuilder Destroy"); + mainGenThread.shutdownNow(); + mainGenThread = Executors.newSingleThreadExecutor(mainGenThreadFactory); + regionsListLock.lock(); try { - toBeDeletedBuildableVbos = buildableVbos; - toBeDeletedDrawableVbos = drawableVbos; - buildableVbos = null; - drawableVbos = null; + if (renderRegions != null) renderRegions.clear(RenderRegion::close); + renderRegions = null; } finally { - bufferLock.unlock(); - } - // make sure the buffers are deleted in a openGL context - GLProxy.getInstance().recordOpenGlCall(() -> { - // destroy the VBOs if they aren't already - if (toBeDeletedBuildableVbos != null) { - toBeDeletedBuildableVbos.clear(RenderRegion::close); - } - if (toBeDeletedDrawableVbos != null) { - toBeDeletedDrawableVbos.clear(RenderRegion::close); - } - }); - } - - /** - * Upload all buildableBuffers to the GPU. We should already be in the builder - * context - */ - private void uploadBuffers(LodQuadBuilder quadBuilder, RegionPos p) { - AbstractBlockPosWrapper playerPos = MC.getPlayerBlockPos(); - double relPosX = playerPos.getX() - p.x*LodUtil.REGION_WIDTH; - double relPosY = playerPos.getY() - LodBuilder.MIN_WORLD_HEIGHT; - double relPosZ = playerPos.getX() - p.z*LodUtil.REGION_WIDTH; - quadBuilder.sort(relPosX, relPosY, relPosZ); - - GLProxy glProxy = GLProxy.getInstance(); - GLProxyContext oldContext = glProxy.getGlContext(); - glProxy.setGlContext(GLProxyContext.LOD_BUILDER); - try { - // determine the upload method - GpuUploadMethod uploadMethod = glProxy.getGpuUploadMethod(); - - // Setup the VBO array - LagSpikeCatcher vboSetup = new LagSpikeCatcher(); - RenderRegion renderRegion = buildableVbos.get(p.x, p.z); - RenderRegion newRenderRegion = RenderRegion.updateStatus(renderRegion, quadBuilder, p); - if (newRenderRegion != null) { - renderRegion = buildableVbos.setAndGet(p.x, p.z, newRenderRegion); - } - vboSetup.end("vboSetup"); - - renderRegion.uploadBuffers(quadBuilder, uploadMethod); - } finally { - glProxy.setGlContext(oldContext); + regionsListLock.unlock(); } } - private boolean swapBuffers() { - bufferLock.lock(); - if (ENABLE_BUFFER_SWAP_LOGGING) - ApiShared.LOGGER.debug("Lod Swap Buffers"); - { - boolean shouldRegenBuff = true; - try { - MovableGridList tmpVbo = drawableVbos; - drawableVbos = buildableVbos; - buildableVbos = tmpVbo; - - // ApiShared.LOGGER.info("Lod Swapped Buffers: "+drawableVbos.toDetailString()); - // the vbos have been swapped - switchVbos = false; - - // FIXME: Race condition on the allBuffersRequireReset boolean - shouldRegenBuff = frontBufferRequireReset || allBuffersRequireReset; - frontBufferRequireReset = allBuffersRequireReset; - allBuffersRequireReset = false; - hideFrontBuffer = hideBackBuffer; - hideBackBuffer = false; - } catch (Exception e) { - // this shouldn't normally happen, but just in case it sill prevent deadlock - ApiShared.LOGGER.error("swapBuffers ran into trouble: " + e.getMessage(), e); - } finally { - bufferLock.unlock(); - } - return shouldRegenBuff; - } - } - - /** Get the newly created VBOs */ - public MovableGridList getFrontBuffers() { - return shouldDrawFrontBuffer() ? drawableVbos : null; + /** Get the newly created VBOs + * Note: SHOULD NEVER MODIFY THE LIST */ + public MovableGridRingList getRenderRegions() { + return renderRegions; } + @Deprecated public void triggerReset() { - allBuffersRequireReset = true; - hideBackBuffer = true; - hideFrontBuffer = true; - } - - public boolean shouldDrawFrontBuffer() { - return !hideFrontBuffer; + } } diff --git a/src/main/java/com/seibel/lod/core/objects/opengl/RenderRegion.java b/src/main/java/com/seibel/lod/core/objects/opengl/RenderRegion.java index 9ede31c1d..abee9df48 100644 --- a/src/main/java/com/seibel/lod/core/objects/opengl/RenderRegion.java +++ b/src/main/java/com/seibel/lod/core/objects/opengl/RenderRegion.java @@ -147,7 +147,7 @@ public class RenderRegion implements AutoCloseable try { if (renderBufferBack != null) renderBufferBack.onReuse(); for (LodDirection dir : LodDirection.ADJ_DIRECTIONS) { - adjRegions[dir.ordinal()] = lodDim.getRegion(regionPos.x+dir.getNormal().x, regionPos.z+dir.getNormal().z); + adjRegions[dir.ordinal() - 2] = lodDim.getRegion(regionPos.x+dir.getNormal().x, regionPos.z+dir.getNormal().z); } } catch (Throwable t) { region.needRegenBuffer = 2; @@ -315,11 +315,12 @@ public class RenderRegion implements AutoCloseable //We check if the adjPos is to be rendered boolean renderAdjPos = posToRender.contains(detailLevel, xAdj, zAdj); - boolean renderLowerAdjPos = posToRender.contains((byte) (detailLevel-1), xAdj*2, zAdj*2); + 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 && !renderLowerAdjPos) + 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); @@ -337,8 +338,9 @@ public class RenderRegion implements AutoCloseable //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-1 == 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); @@ -364,7 +366,6 @@ public class RenderRegion implements AutoCloseable adjData[lodDirection.ordinal() - 2][1] = adjRegion.getAllData(adjDetail, xAdj, zAdj); } } - } // We render every vertical lod present in this position