diff --git a/src/main/java/com/seibel/lod/builders/bufferBuilding/LodBufferBuilder.java b/src/main/java/com/seibel/lod/builders/bufferBuilding/LodBufferBuilder.java index 1bbf92a0e..9c13026d6 100644 --- a/src/main/java/com/seibel/lod/builders/bufferBuilding/LodBufferBuilder.java +++ b/src/main/java/com/seibel/lod/builders/bufferBuilding/LodBufferBuilder.java @@ -18,19 +18,21 @@ package com.seibel.lod.builders.bufferBuilding; import java.nio.ByteBuffer; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.locks.ReentrantLock; -import com.google.common.util.concurrent.ThreadFactoryBuilder; -import com.seibel.lod.util.*; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL15; import org.lwjgl.opengl.GL15C; +import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.seibel.lod.builders.bufferBuilding.lodTemplates.Box; import com.seibel.lod.config.LodConfig; import com.seibel.lod.objects.LodDimension; @@ -41,6 +43,12 @@ import com.seibel.lod.proxy.ClientProxy; import com.seibel.lod.proxy.GlProxy; import com.seibel.lod.proxy.GlProxy.GlProxyContext; import com.seibel.lod.render.LodRenderer; +import com.seibel.lod.util.DataPointUtil; +import com.seibel.lod.util.DetailDistanceUtil; +import com.seibel.lod.util.LevelPosUtil; +import com.seibel.lod.util.LodThreadFactory; +import com.seibel.lod.util.LodUtil; +import com.seibel.lod.util.ThreadMapUtil; import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.client.renderer.vertex.VertexBuffer; @@ -64,87 +72,87 @@ public class LodBufferBuilder * This holds the threads used to generate buffers. */ public static ExecutorService bufferBuilderThreads = Executors.newFixedThreadPool(LodConfig.CLIENT.threading.numberOfBufferBuilderThreads.get(), new ThreadFactoryBuilder().setNameFormat("Buffer-Builder-%d").build()); - + /** * This boolean matrix indicate the buffer builder in that position has to be regenerated */ public volatile boolean[][] regenPos; - + /** * This boolean indicate that all buffer need to be regenerated */ public volatile boolean fullRegeneration = false; - + /** * max capacity of buffers */ //public volatile int BUFFER_MAX_CAPACITY = 1 << (26); public volatile int BUFFER_MAX_CAPACITY = (int) (64 * Math.pow(10, 6)); - + /** * buffer builder for the vertex */ public volatile int[][] bufferSize; - + /** * buffer builder for the vertex */ public volatile BufferBuilder[][][] buildableBuffers; - + /** * Used when building new VBOs */ public volatile VertexBuffer[][][] buildableVbos; - + /** * VBOs that are sent over to the LodNodeRenderer */ public volatile VertexBuffer[][][] drawableVbos; - + /** * 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; - + /** * 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 ReentrantLock bufferLock = new ReentrantLock(); - + private volatile Box[][] boxCache; private volatile PosToRenderContainer[][] setsToRender; private volatile RegionPos center; - + /** * This is the ChunkPos the player was at the last time the buffers were built. * IE the center of the buffers last time they were built */ private volatile ChunkPos drawableCenterChunkPos = new ChunkPos(0, 0); private volatile ChunkPos buildableCenterChunkPos = new ChunkPos(0, 0); - - + + public LodBufferBuilder() { - + } - + /** * Create a thread to asynchronously generate LOD buffers * centered around the given camera X and Z. @@ -155,60 +163,60 @@ public class LodBufferBuilder * swapped with the drawable buffers in the LodRenderer to be drawn. */ public void generateLodBuffersAsync(LodRenderer renderer, LodDimension lodDim, - BlockPos playerBlockPos, boolean fullRegen) + BlockPos playerBlockPos, boolean fullRegen) { - + // only allow one generation process to happen at a time if (generatingBuffers) return; - + if (buildableBuffers == null) // setupBuffers hasn't been called yet return; - + generatingBuffers = true; - + // round the player's block position down to the nearest chunk BlockPos ChunkPos playerChunkPos = new ChunkPos(playerBlockPos); BlockPos playerBlockPosRounded = playerChunkPos.getWorldPosition(); - + Thread thread = new Thread(() -> { bufferLock.lock(); - + try { long startTime = System.currentTimeMillis(); - + ArrayList> nodeToRenderThreads = new ArrayList<>(lodDim.getWidth() * lodDim.getWidth()); - + startBuffers(fullRegen, lodDim); - - + + RegionPos playerRegionPos = new RegionPos(playerChunkPos); if (center == null) center = playerRegionPos; - + if (setsToRender == null) setsToRender = new PosToRenderContainer[lodDim.getWidth()][lodDim.getWidth()]; - + if (setsToRender.length != lodDim.getWidth()) setsToRender = new PosToRenderContainer[lodDim.getWidth()][lodDim.getWidth()]; - + if (boxCache == null) boxCache = new Box[lodDim.getWidth()][lodDim.getWidth()]; - + if (boxCache.length != lodDim.getWidth()) boxCache = new Box[lodDim.getWidth()][lodDim.getWidth()]; - + // this will be the center of the VBOs once they have been built buildableCenterChunkPos = playerChunkPos; - - + + //================================// // create the nodeToRenderThreads // //================================// - + for (int xRegion = 0; xRegion < lodDim.getWidth(); xRegion++) { for (int zRegion = 0; zRegion < lodDim.getWidth(); zRegion++) @@ -218,25 +226,25 @@ public class LodBufferBuilder RegionPos regionPos = new RegionPos( xRegion + lodDim.getCenterRegionPosX() - Math.floorDiv(lodDim.getWidth(), 2), zRegion + lodDim.getCenterRegionPosZ() - Math.floorDiv(lodDim.getWidth(), 2)); - + // local position in the vbo and bufferBuilder arrays BufferBuilder[] currentBuffers = buildableBuffers[xRegion][zRegion]; LodRegion region = lodDim.getRegion(regionPos.x, regionPos.z); - + if (region == null) continue; - + // make sure the buffers weren't // changed while we were running this method if (currentBuffers == null || !currentBuffers[0].building()) return; - + byte minDetail = region.getMinDetailLevel(); - - + + final int xR = xRegion; final int zR = zRegion; - + //we create the Callable to use for the buffer builder creation Callable dataToRenderThread = () -> { @@ -251,55 +259,55 @@ public class LodBufferBuilder int bufferIndex; Box box = ThreadMapUtil.getBox(); boolean[] adjShadeDisabled = ThreadMapUtil.getAdjShadeDisabledArray(); - + // determine how many LODs we can stack vertically int maxVerticalData = DetailDistanceUtil.getMaxVerticalData((byte) 0); - + //we get or create the map that will contain the adj data Map adjData = ThreadMapUtil.getAdjDataArray(maxVerticalData); - + //previous setToRender cache if (setsToRender[xR][zR] == null) setsToRender[xR][zR] = new PosToRenderContainer(minDetail, regionPos.x, regionPos.z); - + PosToRenderContainer posToRender = setsToRender[xR][zR]; posToRender.clear(minDetail, regionPos.x, regionPos.z); - + lodDim.getDataToRender( posToRender, regionPos, playerBlockPosRounded.getX(), playerBlockPosRounded.getZ()); - - - - - + + + + + // keep a local version so we don't have to worry about indexOutOfBounds Exceptions // if it changes in the LodRenderer while we are working here boolean[][] vanillaRenderedChunks = renderer.vanillaRenderedChunks; short gameChunkRenderDistance = (short) (vanillaRenderedChunks.length / 2 - 1); boolean smallRenderDistance = gameChunkRenderDistance <= 4; - - - + + + for (int index = 0; index < posToRender.getNumberOfPos(); index++) { bufferIndex = Math.floorMod(index, currentBuffers.length); detailLevel = posToRender.getNthDetailLevel(index); posX = posToRender.getNthPosX(index); posZ = posToRender.getNthPosZ(index); - + // skip any chunks that Minecraft is going to render chunkXdist = LevelPosUtil.getChunkPos(detailLevel, posX) - playerChunkPos.x; chunkZdist = LevelPosUtil.getChunkPos(detailLevel, posZ) - playerChunkPos.z; - + boolean isItBorderPos = LodUtil.isBorderChunk(vanillaRenderedChunks, chunkXdist + gameChunkRenderDistance + 1, chunkZdist + gameChunkRenderDistance + 1); if (gameChunkRenderDistance >= Math.abs(chunkXdist) - && gameChunkRenderDistance >= Math.abs(chunkZdist) - && detailLevel <= LodUtil.CHUNK_DETAIL_LEVEL - && vanillaRenderedChunks[chunkXdist + gameChunkRenderDistance + 1][chunkZdist + gameChunkRenderDistance + 1] - && (!isItBorderPos || smallRenderDistance)) + && gameChunkRenderDistance >= Math.abs(chunkZdist) + && detailLevel <= LodUtil.CHUNK_DETAIL_LEVEL + && vanillaRenderedChunks[chunkXdist + gameChunkRenderDistance + 1][chunkZdist + gameChunkRenderDistance + 1] + && (!isItBorderPos || smallRenderDistance)) { continue; } @@ -312,54 +320,55 @@ public class LodBufferBuilder chunkXdist = LevelPosUtil.getChunkPos(detailLevel, xAdj) - playerChunkPos.x; chunkZdist = LevelPosUtil.getChunkPos(detailLevel, zAdj) - playerChunkPos.z; if (posToRender.contains(detailLevel, xAdj, zAdj) - && (gameChunkRenderDistance < Math.abs(chunkXdist) - || gameChunkRenderDistance < Math.abs(chunkZdist) - || !(vanillaRenderedChunks[chunkXdist + gameChunkRenderDistance + 1][chunkZdist + gameChunkRenderDistance + 1] - && (!LodUtil.isBorderChunk(vanillaRenderedChunks, chunkXdist + gameChunkRenderDistance + 1, chunkZdist + gameChunkRenderDistance + 1) || smallRenderDistance)))) + && (gameChunkRenderDistance < Math.abs(chunkXdist) + || gameChunkRenderDistance < Math.abs(chunkZdist) + || !(vanillaRenderedChunks[chunkXdist + gameChunkRenderDistance + 1][chunkZdist + gameChunkRenderDistance + 1] + && (!LodUtil.isBorderChunk(vanillaRenderedChunks, chunkXdist + gameChunkRenderDistance + 1, chunkZdist + gameChunkRenderDistance + 1) || smallRenderDistance)))) { for (int verticalIndex = 0; verticalIndex < lodDim.getMaxVerticalData(detailLevel, xAdj, zAdj); verticalIndex++) { long data = lodDim.getData(detailLevel, xAdj, zAdj, verticalIndex); adjData.get(direction)[verticalIndex] = data; } - } else + } + else { if (gameChunkRenderDistance >= Math.abs(chunkXdist) - && gameChunkRenderDistance >= Math.abs(chunkZdist) - && (!LodUtil.isBorderChunk(vanillaRenderedChunks, chunkXdist + gameChunkRenderDistance + 1, chunkZdist + gameChunkRenderDistance + 1) || smallRenderDistance) - && vanillaRenderedChunks[chunkXdist + gameChunkRenderDistance + 1][chunkZdist + gameChunkRenderDistance + 1] - && !DataPointUtil.isVoid(lodDim.getSingleData(detailLevel, xAdj, zAdj))) + && gameChunkRenderDistance >= Math.abs(chunkZdist) + && (!LodUtil.isBorderChunk(vanillaRenderedChunks, chunkXdist + gameChunkRenderDistance + 1, chunkZdist + gameChunkRenderDistance + 1) || smallRenderDistance) + && vanillaRenderedChunks[chunkXdist + gameChunkRenderDistance + 1][chunkZdist + gameChunkRenderDistance + 1] + && !DataPointUtil.isVoid(lodDim.getSingleData(detailLevel, xAdj, zAdj))) adjShadeDisabled[Box.DIRECTION_INDEX.get(direction)] = true; adjData.get(direction)[0] = DataPointUtil.EMPTY_DATA; } } - + long data; for (int verticalIndex = 0; verticalIndex < lodDim.getMaxVerticalData(detailLevel, posX, posZ); verticalIndex++) { data = lodDim.getData(detailLevel, posX, posZ, verticalIndex); if (DataPointUtil.isVoid(data) || !DataPointUtil.doesItExist(data)) break; - + LodConfig.CLIENT.graphics.lodTemplate.get().template.addLodToBuffer(currentBuffers[bufferIndex], playerBlockPosRounded, data, adjData, detailLevel, posX, posZ, box, renderer.previousDebugMode, renderer.lightMap, adjShadeDisabled); } - - + + } // for pos to in list to render - // the thread executed successfully + // the thread executed successfully return true; }; - - + + nodeToRenderThreads.add(dataToRenderThread); - - + + } } // region z } // region z - - + + long executeStart = System.currentTimeMillis(); // wait for all threads to finish List> futuresBuffer = bufferBuilderThreads.invokeAll(nodeToRenderThreads); @@ -374,47 +383,49 @@ public class LodBufferBuilder } } long executeEnd = System.currentTimeMillis(); - - + + long endTime = System.currentTimeMillis(); @SuppressWarnings("unused") long buildTime = endTime - startTime; @SuppressWarnings("unused") long executeTime = executeEnd - executeStart; - + // ClientProxy.LOGGER.info("Thread Build time: " + buildTime + " ms" + '\n' + // "thread execute time: " + executeTime + " ms"); - + // mark that the buildable buffers as ready to swap switchVbos = true; - } catch (Exception e) + } + catch (Exception e) { ClientProxy.LOGGER.warn("\"LodNodeBufferBuilder.generateLodBuffersAsync\" ran into trouble: "); e.printStackTrace(); - } finally + } + finally { // regardless of if we successfully created the buffers // we are done generating. generatingBuffers = false; - + // clean up any potentially open resources if (buildableBuffers != null) closeBuffers(fullRegen, lodDim); - + // upload the new buffers uploadBuffers(fullRegen, lodDim); bufferLock.unlock(); } - + }); - + mainGenThread.execute(thread); } - + //===============================// // BufferBuilder related methods // //===============================// - + /** * Called from the LodRenderer to create the * BufferBuilders.

@@ -427,14 +438,14 @@ public class LodBufferBuilder int numbRegionsWide = lodDimension.getWidth(); long memoryRequired; int numberOfBuffers; - + previousRegionWidth = numbRegionsWide; bufferSize = new int[numbRegionsWide][numbRegionsWide]; buildableBuffers = new BufferBuilder[numbRegionsWide][numbRegionsWide][]; - + buildableVbos = new VertexBuffer[numbRegionsWide][numbRegionsWide][]; drawableVbos = new VertexBuffer[numbRegionsWide][numbRegionsWide][]; - + for (int x = 0; x < numbRegionsWide; x++) { for (int z = 0; z < numbRegionsWide; z++) @@ -447,7 +458,8 @@ public class LodBufferBuilder buildableBuffers[x][z] = new BufferBuilder[1]; buildableVbos[x][z] = new VertexBuffer[1]; drawableVbos[x][z] = new VertexBuffer[1]; - } else + } + else { numberOfBuffers = (int) Math.ceil(memoryRequired / BUFFER_MAX_CAPACITY) + 1; System.out.println(numberOfBuffers); @@ -457,7 +469,7 @@ public class LodBufferBuilder buildableVbos[x][z] = new VertexBuffer[numberOfBuffers]; drawableVbos[x][z] = new VertexBuffer[numberOfBuffers]; } - + for (int i = 0; i < bufferSize[x][z]; i++) { buildableBuffers[x][z][i] = new BufferBuilder((int) memoryRequired); @@ -466,10 +478,10 @@ public class LodBufferBuilder } } } - + bufferLock.unlock(); } - + /** * sets the buffers and Vbos to null, forcing them to be recreated.

*

@@ -478,14 +490,14 @@ public class LodBufferBuilder public void destroyBuffers() { bufferLock.lock(); - + buildableBuffers = null; buildableVbos = null; drawableVbos = null; - + bufferLock.unlock(); } - + /** * Calls begin on each of the buildable BufferBuilders. */ @@ -500,18 +512,18 @@ public class LodBufferBuilder // for some reason BufferBuilder.vertexCounts // isn't reset unless this is called, which can cause // a false indexOutOfBoundsException - + for (int i = 0; i < buildableBuffers[x][z].length; i++) { buildableBuffers[x][z][i].discard(); - + buildableBuffers[x][z][i].begin(GL11.GL_QUADS, LodRenderer.LOD_VERTEX_FORMAT); } } } } } - + /** * Calls end on each of the buildable BufferBuilders. */ @@ -527,20 +539,20 @@ public class LodBufferBuilder } } } - + /** * Upload all buildableBuffers to the GPU. */ private void uploadBuffers(boolean fullRegen, LodDimension lodDim) { GlProxy glProxy = GlProxy.getInstance(); - + try { // make sure we are uploading to a different OpenGL context, // to prevent interference (IE stuttering) with the Minecraft context. glProxy.setGlContext(GlProxyContext.LOD_BUILDER); - + for (int x = 0; x < buildableVbos.length; x++) { for (int z = 0; z < buildableVbos.length; z++) @@ -556,17 +568,19 @@ public class LodBufferBuilder } } } - } catch (IllegalStateException e) + } + catch (IllegalStateException e) { ClientProxy.LOGGER.error(LodBufferBuilder.class.getSimpleName() + " - UploadBuffers failed: " + e.getMessage()); e.printStackTrace(); - } finally + } + finally { // make sure the context is disabled glProxy.setGlContext(GlProxyContext.NONE); } } - + /** * Uploads the uploadBuffer into the VBO in GPU memory. */ @@ -577,22 +591,22 @@ public class LodBufferBuilder { // this is how many points will be rendered vbo.vertexCount = (uploadBuffer.remaining() / vbo.format.getVertexSize()); - + GL15C.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo.id); - + // subData only works if the memory is allocated beforehand. GL15C.glBufferData(GL15.GL_ARRAY_BUFFER, uploadBuffer.remaining(), GL15C.GL_DYNAMIC_DRAW); - + // interestingly bufferSubData renders faster than glMapBuffer // even though OpenGLInsights-AsynchronousBufferTransfers says glMapBuffer // is faster for transferring data. They must put the data in different memory // or something. GL15C.glBufferSubData(GL15.GL_ARRAY_BUFFER, 0, uploadBuffer); - + GL15C.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); } } - + /** * Get the newly created VBOs */ @@ -605,17 +619,17 @@ public class LodBufferBuilder VertexBuffer[][][] tmpVbo = drawableVbos; drawableVbos = buildableVbos; buildableVbos = tmpVbo; - + drawableCenterChunkPos = buildableCenterChunkPos; - + // the vbos have been swapped switchVbos = false; bufferLock.unlock(); } - + return new VertexBuffersAndOffset(drawableVbos, drawableCenterChunkPos); } - + /** * A simple container to pass multiple objects back in the getVertexBuffers method. */ @@ -623,14 +637,14 @@ public class LodBufferBuilder { public VertexBuffer[][][] vbos; public ChunkPos drawableCenterChunkPos; - + public VertexBuffersAndOffset(VertexBuffer[][][] newVbos, ChunkPos newDrawableCenterChunkPos) { vbos = newVbos; drawableCenterChunkPos = newDrawableCenterChunkPos; } } - + /** * If this is true the buildable near and far * buffers have been generated and are ready to be @@ -640,15 +654,15 @@ public class LodBufferBuilder { return switchVbos; } - - + + /** * */ public void setPartialRegen() { } - + /** * */