Start rework to allow individual render region swapping
This commit is contained in:
@@ -33,7 +33,6 @@ import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import com.seibel.lod.core.ModInfo;
|
||||
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
|
||||
import com.seibel.lod.core.enums.config.VerticalQuality;
|
||||
import com.seibel.lod.core.objects.lod.LodDimension;
|
||||
import com.seibel.lod.core.objects.math.Mat4f;
|
||||
import com.seibel.lod.core.render.GLProxy;
|
||||
@@ -235,10 +234,13 @@ public class ClientApi
|
||||
ClientApi.renderer.drawLODs(lodDim, mcModelViewMatrix, mcProjectionMatrix, partialTicks, MC.getProfiler());
|
||||
} catch (RuntimeException e) {
|
||||
rendererDisabledBecauseOfExceptions = true;
|
||||
ClientApi.LOGGER.error("Renderer thrown an uncaught exception: ",e);
|
||||
try {
|
||||
//ClientApi.renderer.ma ();
|
||||
} catch (RuntimeException welpLookLikeWeWillLeakResource) {}
|
||||
throw e;
|
||||
MC.sendChatMessage("\u00A74\u00A7l\u00A7uERROR: Distant Horizons"
|
||||
+ " renderer has encountered an exception!");
|
||||
MC.sendChatMessage("\u00A74Renderer is now disabled to prevent futher issues.");
|
||||
MC.sendChatMessage("\u00A74Exception detail: "+e.toString());
|
||||
} catch (RuntimeException noMessagesThen) {}
|
||||
}
|
||||
}
|
||||
profiler.pop(); // end LOD
|
||||
@@ -254,8 +256,7 @@ public class ClientApi
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ClientApi.LOGGER.error("client proxy: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
ClientApi.LOGGER.error("client proxy uncaught exception: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+15
-245
@@ -19,10 +19,8 @@
|
||||
|
||||
package com.seibel.lod.core.builders.bufferBuilding;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
@@ -33,9 +31,6 @@ import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import org.lwjgl.opengl.GL32;
|
||||
import org.lwjgl.opengl.GL44;
|
||||
|
||||
import com.seibel.lod.core.api.ClientApi;
|
||||
import com.seibel.lod.core.enums.LodDirection;
|
||||
import com.seibel.lod.core.enums.config.GpuUploadMethod;
|
||||
@@ -43,12 +38,11 @@ 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.objects.opengl.LodVertexBuffer;
|
||||
import com.seibel.lod.core.objects.opengl.RenderRegion;
|
||||
import com.seibel.lod.core.render.GLProxy;
|
||||
import com.seibel.lod.core.render.LodRenderer;
|
||||
import com.seibel.lod.core.util.DataPointUtil;
|
||||
@@ -58,7 +52,7 @@ import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.util.MovableGridList;
|
||||
import com.seibel.lod.core.util.SingletonHandler;
|
||||
import com.seibel.lod.core.util.SpamReducedLogger;
|
||||
import com.seibel.lod.core.util.UnitBytes;
|
||||
import com.seibel.lod.core.util.StatsMap;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
|
||||
|
||||
@@ -143,14 +137,8 @@ public class LodBufferBuilderFactory {
|
||||
|
||||
/** Used when building new VBOs */
|
||||
public volatile MovableGridList<RenderRegion> buildableVbos;
|
||||
public volatile int buildableCenterBlockX;
|
||||
public volatile int buildableCenterBlockY;
|
||||
public volatile int buildableCenterBlockZ;
|
||||
/** VBOs that are sent over to the LodNodeRenderer */
|
||||
public volatile MovableGridList<RenderRegion> drawableVbos;
|
||||
public volatile int drawableCenterBlockX;
|
||||
public volatile int drawableCenterBlockY;
|
||||
public volatile int drawableCenterBlockZ;
|
||||
/**
|
||||
* 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
|
||||
@@ -186,9 +174,6 @@ public class LodBufferBuilderFactory {
|
||||
|
||||
private MovableGridList<PosToRenderContainer> setsToRender;
|
||||
|
||||
private int lastX = 0;
|
||||
private int lastZ = 0;
|
||||
|
||||
public LodBufferBuilderFactory() {
|
||||
|
||||
}
|
||||
@@ -246,14 +231,8 @@ public class LodBufferBuilderFactory {
|
||||
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;
|
||||
int vboX;
|
||||
int vboY;
|
||||
int vboZ;
|
||||
|
||||
boolean tooFar = Math.abs(buildableCenterBlockX - playerX)
|
||||
+ Math.abs(buildableCenterBlockZ - playerZ) > 100_000;
|
||||
|
||||
if (fullRegen || tooFar || buildableVbos == null || setsToRender == null) {
|
||||
if (fullRegen || buildableVbos == null || setsToRender == null) {
|
||||
if (buildableVbos != null) {
|
||||
buildableVbos.clear(RenderRegion::close);
|
||||
}
|
||||
@@ -261,20 +240,8 @@ public class LodBufferBuilderFactory {
|
||||
renderRange = lodDim.getWidth() / 2; // get lodDim half width
|
||||
buildableVbos = new MovableGridList<RenderRegion>(renderRange, playerRegionX, playerRegionZ);
|
||||
setsToRender = new MovableGridList<PosToRenderContainer>(renderRange, playerRegionX, playerRegionZ);
|
||||
// this will be the center of the VBOs once they have been built
|
||||
// FIXME: Currently this will drift apart from player pos if there has not been
|
||||
// a fullRegen for a while
|
||||
buildableCenterBlockX = playerX;
|
||||
buildableCenterBlockY = playerY;
|
||||
buildableCenterBlockZ = playerZ;
|
||||
vboX = playerX;
|
||||
vboY = playerY;
|
||||
vboZ = playerZ;
|
||||
} else {
|
||||
renderRange = buildableVbos.gridCentreToEdge;
|
||||
vboX = buildableCenterBlockX;
|
||||
vboY = buildableCenterBlockY;
|
||||
vboZ = buildableCenterBlockZ;
|
||||
buildableVbos.move(playerRegionX, playerRegionZ, RenderRegion::close);
|
||||
setsToRender.move(playerRegionX, playerRegionZ);
|
||||
}
|
||||
@@ -292,8 +259,6 @@ public class LodBufferBuilderFactory {
|
||||
// minCullingRange);
|
||||
// int cullingRangeZ = Math.max((int)(1.5 * Math.abs(lastZ - playerZ)),
|
||||
// minCullingRange);
|
||||
lastX = playerX;
|
||||
lastZ = playerZ;
|
||||
List<CompletableFuture<?>> futuresBuffer = new LinkedList<CompletableFuture<?>>();
|
||||
for (int indexX = 0; indexX < buildableVbos.gridSize; indexX++) {
|
||||
for (int indexZ = 0; indexZ < buildableVbos.gridSize; indexZ++) {
|
||||
@@ -324,19 +289,11 @@ public class LodBufferBuilderFactory {
|
||||
this.quadBuilder = quadBuilder;
|
||||
this.regionPos = regionPos;
|
||||
}
|
||||
|
||||
LodQuadBuilder quadBuilder() {
|
||||
return quadBuilder;
|
||||
}
|
||||
|
||||
RegionPos regionPos() {
|
||||
return regionPos;
|
||||
}
|
||||
}
|
||||
|
||||
CompletableFuture<ResultPair> future = CompletableFuture.supplyAsync(() -> {
|
||||
LodQuadBuilder quadBuilder = new LodQuadBuilder(6);
|
||||
makeLodRenderData(quadBuilder, lodDim, regionPos, pX, pZ, vboX, vboZ, minDetail);
|
||||
makeLodRenderData(quadBuilder, lodDim, regionPos, pX, pZ, minDetail);
|
||||
return new ResultPair(quadBuilder, regionPos);
|
||||
}, bufferUploadThread).whenCompleteAsync((result, e) -> {
|
||||
if (e != null)
|
||||
@@ -400,7 +357,7 @@ public class LodBufferBuilderFactory {
|
||||
}
|
||||
|
||||
private RegionPos makeLodRenderData(LodQuadBuilder quadBuilder, LodDimension lodDim, RegionPos regPos, int playerX,
|
||||
int playerZ, int vboX, int vboZ, byte minDetail) {// , int cullingRangeX, int cullingRangeZ) {
|
||||
int playerZ, byte minDetail) {// , int cullingRangeX, int cullingRangeZ) {
|
||||
|
||||
// Variable initialization
|
||||
int playerChunkX = LevelPosUtil.convert(LodUtil.BLOCK_DETAIL_LEVEL, playerX, LodUtil.CHUNK_DETAIL_LEVEL);
|
||||
@@ -523,29 +480,15 @@ public class LodBufferBuilderFactory {
|
||||
if (!ramLogger.canMaybeLog())
|
||||
return;
|
||||
ramLogger.info("Dumping Ram Usage for buffer usage...");
|
||||
long bufferCount = 0;
|
||||
long fullBufferCount = 0;
|
||||
long totalUsage = 0;
|
||||
int maxLength = MAX_TRIANGLES_PER_BUFFER * (LodUtil.LOD_VERTEX_FORMAT.getByteSize() * 3);
|
||||
StatsMap statsMap = new StatsMap();
|
||||
|
||||
if (buildableVbos == null) {
|
||||
ramLogger.info("Buildable VBOs are null!");
|
||||
} else
|
||||
for (RenderRegion buffers : buildableVbos) {
|
||||
if (buffers == null)
|
||||
continue;
|
||||
LodVertexBuffer[] bs = buffers.debugGetBuffers().clone();
|
||||
for (LodVertexBuffer b : bs) {
|
||||
if (b == null)
|
||||
continue;
|
||||
bufferCount++;
|
||||
if (b.size == maxLength) {
|
||||
fullBufferCount++;
|
||||
} else if (b.size > maxLength) {
|
||||
ramLogger.info("BUFFER OVERSIZED: {} (max size is {})", new UnitBytes(b.size),
|
||||
new UnitBytes(maxLength));
|
||||
}
|
||||
totalUsage += b.size;
|
||||
}
|
||||
buffers.debugDumpStats(statsMap);
|
||||
}
|
||||
if (drawableVbos == null) {
|
||||
ramLogger.info("Drawable VBOs are null!");
|
||||
@@ -553,23 +496,10 @@ public class LodBufferBuilderFactory {
|
||||
for (RenderRegion buffers : drawableVbos) {
|
||||
if (buffers == null)
|
||||
continue;
|
||||
LodVertexBuffer[] bs = buffers.debugGetBuffers().clone();
|
||||
for (LodVertexBuffer b : bs) {
|
||||
if (b == null)
|
||||
continue;
|
||||
bufferCount++;
|
||||
if (b.size == maxLength) {
|
||||
fullBufferCount++;
|
||||
} else if (b.size > maxLength) {
|
||||
ramLogger.info("BUFFER OVERSIZED: {} (max size is {})", new UnitBytes(b.size),
|
||||
new UnitBytes(maxLength));
|
||||
}
|
||||
totalUsage += b.size;
|
||||
}
|
||||
buffers.debugDumpStats(statsMap);
|
||||
}
|
||||
ramLogger.info("================================================");
|
||||
ramLogger.info("Buffers: [{}], Full-sized Buffers: [{}], Total: [{}]", bufferCount, fullBufferCount,
|
||||
new UnitBytes(totalUsage));
|
||||
ramLogger.info("Stats: {}", statsMap);
|
||||
ramLogger.info("================================================");
|
||||
ramLogger.incLogTries();
|
||||
}
|
||||
@@ -621,163 +551,21 @@ public class LodBufferBuilderFactory {
|
||||
// determine the upload method
|
||||
GpuUploadMethod uploadMethod = glProxy.getGpuUploadMethod();
|
||||
|
||||
// determine the upload timeout
|
||||
long BPerNS = CONFIG.client().advanced().buffers().getGpuUploadPerMegabyteInMilliseconds(); // MB -> B =
|
||||
// 1/1,000,000.
|
||||
// MS -> NS =
|
||||
// 1,000,000.
|
||||
// So, MBPerMS =
|
||||
// BPerNS.
|
||||
long remainingNS = 0; // We don't want to pause for like 0.1 ms... so we store those tiny MS.
|
||||
long totalBytes = 0;
|
||||
int vbosNeeded = quadBuilder.getCurrentNeededVertexBuffers();
|
||||
|
||||
// Setup the VBO array
|
||||
LagSpikeCatcher vboSetup = new LagSpikeCatcher();
|
||||
RenderRegion renderRegion = buildableVbos.get(p.x, p.z);
|
||||
if (renderRegion == null)
|
||||
renderRegion = buildableVbos.setAndGet(p.x, p.z, new RenderRegion(vbosNeeded));
|
||||
else
|
||||
renderRegion.resize(vbosNeeded);
|
||||
RenderRegion newRenderRegion = RenderRegion.updateStatus(renderRegion, quadBuilder, p);
|
||||
if (newRenderRegion != null) {
|
||||
renderRegion = buildableVbos.setAndGet(p.x, p.z, newRenderRegion);
|
||||
}
|
||||
vboSetup.end("vboSetup");
|
||||
|
||||
// Start the upload
|
||||
Iterator<ByteBuffer> iter = quadBuilder.makeVertexBuffers();
|
||||
int i = 0;
|
||||
while (iter.hasNext()) {
|
||||
ByteBuffer bb = iter.next();
|
||||
LagSpikeCatcher vboU = new LagSpikeCatcher();
|
||||
vboUpload(p, i++, bb, uploadMethod);
|
||||
vboU.end("vboUpload");
|
||||
|
||||
// upload buffers over an extended period of time
|
||||
// to hopefully prevent stuttering.
|
||||
totalBytes += bb.limit();
|
||||
remainingNS += bb.limit() * BPerNS;
|
||||
if (remainingNS >= TimeUnit.NANOSECONDS.convert(1000 / 60, TimeUnit.MILLISECONDS)) {
|
||||
if (remainingNS > MAX_BUFFER_UPLOAD_TIMEOUT_NANOSECONDS)
|
||||
remainingNS = MAX_BUFFER_UPLOAD_TIMEOUT_NANOSECONDS;
|
||||
try {
|
||||
Thread.sleep(remainingNS / 1000000, (int) (remainingNS % 1000000));
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
remainingNS = 0;
|
||||
}
|
||||
}
|
||||
if (ENABLE_BUFFER_UPLOAD_LOGGING)
|
||||
ClientApi.LOGGER.info("Uploaded {} sub buffers for {} with total of {}", i, p,
|
||||
new UnitBytes(totalBytes));
|
||||
renderRegion.uploadBuffers(quadBuilder, uploadMethod);
|
||||
} finally {
|
||||
glProxy.setGlContext(oldContext);
|
||||
}
|
||||
}
|
||||
|
||||
/** Uploads the uploadBuffer so the GPU can use it. */
|
||||
private void vboUpload(RegionPos regPos, int iIndex, ByteBuffer uploadBuffer, GpuUploadMethod uploadMethod) {
|
||||
int maxLength = MAX_TRIANGLES_PER_BUFFER * (LodUtil.LOD_VERTEX_FORMAT.getByteSize() * 3);
|
||||
boolean useBuffStorage = uploadMethod == GpuUploadMethod.BUFFER_STORAGE;
|
||||
RenderRegion region = buildableVbos.get(regPos.x, regPos.z);
|
||||
LodVertexBuffer vbo = region.getOrMakeVbo(iIndex, useBuffStorage);
|
||||
|
||||
// this is how many points will be rendered
|
||||
vbo.vertexCount = (uploadBuffer.limit() / LodUtil.LOD_VERTEX_FORMAT.getByteSize());
|
||||
|
||||
// If size is zero, just ignore it.
|
||||
if (uploadBuffer.limit() == 0)
|
||||
return;
|
||||
LagSpikeCatcher bindBuff = new LagSpikeCatcher();
|
||||
bindBuff.end("glBindBuffer vbo.id");
|
||||
|
||||
try {
|
||||
// if possible use the faster buffer storage route
|
||||
if (uploadMethod == GpuUploadMethod.BUFFER_STORAGE) {
|
||||
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, vbo.id);
|
||||
long size = vbo.size;
|
||||
if (size < uploadBuffer.limit()
|
||||
|| size > uploadBuffer.limit() * BUFFER_EXPANSION_MULTIPLIER * BUFFER_EXPANSION_MULTIPLIER) {
|
||||
int newSize = (int) (uploadBuffer.limit() * BUFFER_EXPANSION_MULTIPLIER);
|
||||
if (newSize > maxLength)
|
||||
newSize = maxLength;
|
||||
LagSpikeCatcher buffResizeRegen = new LagSpikeCatcher();
|
||||
GL32.glDeleteBuffers(vbo.id);
|
||||
vbo.id = GL32.glGenBuffers();
|
||||
buffResizeRegen.end("glDeleteBuffers BuffStorage resize");
|
||||
LagSpikeCatcher buffResize = new LagSpikeCatcher();
|
||||
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, vbo.id);
|
||||
GL44.glBufferStorage(GL32.GL_ARRAY_BUFFER, newSize, GL44.GL_DYNAMIC_STORAGE_BIT);
|
||||
vbo.size = newSize;
|
||||
buffResize.end("glBufferStorage BuffStorage resize");
|
||||
}
|
||||
LagSpikeCatcher buffSubData = new LagSpikeCatcher();
|
||||
GL32.glBufferSubData(GL32.GL_ARRAY_BUFFER, 0, uploadBuffer);
|
||||
buffSubData.end("glBufferSubData BuffStorage");
|
||||
} else if (uploadMethod == GpuUploadMethod.BUFFER_MAPPING) {
|
||||
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, vbo.id);
|
||||
// no stuttering but high GPU usage
|
||||
// stores everything in system memory instead of GPU memory
|
||||
// making rendering much slower.
|
||||
// Unless the user is running integrated graphics,
|
||||
// in that case this will actually work better than SUB_DATA.
|
||||
long size = vbo.size;
|
||||
if (size < uploadBuffer.limit()
|
||||
|| size > uploadBuffer.limit() * BUFFER_EXPANSION_MULTIPLIER * BUFFER_EXPANSION_MULTIPLIER) {
|
||||
int newSize = (int) (uploadBuffer.limit() * BUFFER_EXPANSION_MULTIPLIER);
|
||||
if (newSize > maxLength)
|
||||
newSize = maxLength;
|
||||
LagSpikeCatcher buffResize = new LagSpikeCatcher();
|
||||
GL32.glBufferData(GL32.GL_ARRAY_BUFFER, newSize, GL32.GL_STATIC_DRAW);
|
||||
vbo.size = newSize;
|
||||
buffResize.end("glBufferData BuffMapping resize");
|
||||
}
|
||||
ByteBuffer vboBuffer;
|
||||
// map buffer range is better since it can be explicitly unsynchronized
|
||||
LagSpikeCatcher buffMap = new LagSpikeCatcher();
|
||||
vboBuffer = GL32.glMapBufferRange(GL32.GL_ARRAY_BUFFER, 0, uploadBuffer.limit(),
|
||||
GL32.GL_MAP_WRITE_BIT | GL32.GL_MAP_UNSYNCHRONIZED_BIT | GL32.GL_MAP_INVALIDATE_BUFFER_BIT);
|
||||
buffMap.end("glMapBufferRange BuffMapping");
|
||||
LagSpikeCatcher buffWrite = new LagSpikeCatcher();
|
||||
vboBuffer.put(uploadBuffer);
|
||||
LagSpikeCatcher buffUnmap = new LagSpikeCatcher();
|
||||
GL32.glUnmapBuffer(GL32.GL_ARRAY_BUFFER);
|
||||
buffUnmap.end("glUnmapBuffer");
|
||||
|
||||
buffWrite.end("WriteData BuffMapping");
|
||||
} else if (uploadMethod == GpuUploadMethod.DATA) {
|
||||
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, vbo.id);
|
||||
// TODO: Check this nonsense comment!
|
||||
// hybrid bufferData //
|
||||
// high stutter, low GPU usage
|
||||
// But simplest/most compatible
|
||||
LagSpikeCatcher buffData = new LagSpikeCatcher();
|
||||
GL32.glBufferData(GL32.GL_ARRAY_BUFFER, uploadBuffer, GL32.GL_STATIC_DRAW);
|
||||
vbo.size = uploadBuffer.limit();
|
||||
buffData.end("glBufferData Data");
|
||||
} else {
|
||||
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, vbo.id);
|
||||
// TODO: Check this nonsense comment!
|
||||
// hybrid subData/bufferData //
|
||||
// less stutter, low GPU usage
|
||||
long size = vbo.size;
|
||||
if (size < uploadBuffer.limit()
|
||||
|| size > uploadBuffer.limit() * BUFFER_EXPANSION_MULTIPLIER * BUFFER_EXPANSION_MULTIPLIER) {
|
||||
int newSize = (int) (uploadBuffer.limit() * BUFFER_EXPANSION_MULTIPLIER);
|
||||
if (newSize > maxLength)
|
||||
newSize = maxLength;
|
||||
LagSpikeCatcher buffResize = new LagSpikeCatcher();
|
||||
GL32.glBufferData(GL32.GL_ARRAY_BUFFER, newSize, GL32.GL_STATIC_DRAW);
|
||||
vbo.size = newSize;
|
||||
buffResize.end("glBufferData SubData resize");
|
||||
}
|
||||
LagSpikeCatcher buffSubData = new LagSpikeCatcher();
|
||||
GL32.glBufferSubData(GL32.GL_ARRAY_BUFFER, 0, uploadBuffer);
|
||||
buffSubData.end("glBufferSubData SubData");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
ClientApi.LOGGER.error("vboUpload failed: " + e.getClass().getSimpleName());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}// vboUpload
|
||||
|
||||
private boolean swapBuffers() {
|
||||
bufferLock.lock();
|
||||
if (ENABLE_BUFFER_SWAP_LOGGING)
|
||||
@@ -790,16 +578,6 @@ public class LodBufferBuilderFactory {
|
||||
buildableVbos = tmpVbo;
|
||||
|
||||
// ClientApi.LOGGER.info("Lod Swapped Buffers: "+drawableVbos.toDetailString());
|
||||
|
||||
int tempX = drawableCenterBlockX;
|
||||
int tempY = drawableCenterBlockY;
|
||||
int tempZ = drawableCenterBlockZ;
|
||||
drawableCenterBlockX = buildableCenterBlockX;
|
||||
drawableCenterBlockY = buildableCenterBlockY;
|
||||
drawableCenterBlockZ = buildableCenterBlockZ;
|
||||
buildableCenterBlockX = tempX;
|
||||
buildableCenterBlockY = tempY;
|
||||
buildableCenterBlockZ = tempZ;
|
||||
// the vbos have been swapped
|
||||
switchVbos = false;
|
||||
|
||||
@@ -824,14 +602,6 @@ public class LodBufferBuilderFactory {
|
||||
return shouldDrawFrontBuffer() ? drawableVbos : null;
|
||||
}
|
||||
|
||||
public int getFrontBuffersCenterX() {
|
||||
return drawableCenterBlockX;
|
||||
}
|
||||
|
||||
public int getFrontBuffersCenterZ() {
|
||||
return drawableCenterBlockZ;
|
||||
}
|
||||
|
||||
public void triggerReset() {
|
||||
allBuffersRequireReset = true;
|
||||
hideBackBuffer = true;
|
||||
|
||||
@@ -31,6 +31,8 @@ package com.seibel.lod.core.enums.config;
|
||||
*/
|
||||
public enum BufferRebuildTimes
|
||||
{
|
||||
CONSTANT(0, 0, 0, 1),
|
||||
|
||||
FREQUENT(1000, 500, 2500, 1),
|
||||
|
||||
NORMAL(2000, 1000, 5000, 4),
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.seibel.lod.core.objects;
|
||||
|
||||
import com.seibel.lod.core.enums.config.GpuUploadMethod;
|
||||
import com.seibel.lod.core.objects.lod.RegionPos;
|
||||
import com.seibel.lod.core.objects.opengl.ComplexRenderRegion;
|
||||
import com.seibel.lod.core.objects.opengl.LodQuadBuilder;
|
||||
import com.seibel.lod.core.objects.opengl.SimpleRenderRegion;
|
||||
import com.seibel.lod.core.render.LodRenderProgram;
|
||||
import com.seibel.lod.core.util.StatsMap;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||
|
||||
public abstract class RenderRegion implements AutoCloseable
|
||||
{
|
||||
// target can be Null.
|
||||
// If return null, means all status updated without switching objects.
|
||||
@SuppressWarnings("resource")
|
||||
public static RenderRegion updateStatus(RenderRegion target, LodQuadBuilder builder, RegionPos regPos) {
|
||||
boolean useSimpleRegion = (builder.getCurrentNeededVertexBuffers() <= 6) || true;
|
||||
if ((target instanceof SimpleRenderRegion && !useSimpleRegion) ||
|
||||
target instanceof ComplexRenderRegion && useSimpleRegion) {
|
||||
target.close();
|
||||
target = null;
|
||||
}
|
||||
if (target == null) {
|
||||
return useSimpleRegion ?
|
||||
new SimpleRenderRegion(builder.getCurrentNeededVertexBuffers(), regPos)
|
||||
: new ComplexRenderRegion(regPos);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public abstract void uploadBuffers(LodQuadBuilder builder, GpuUploadMethod uploadMethod);
|
||||
public abstract boolean shouldRender(IMinecraftRenderWrapper renderer, boolean enableDirectionalCulling);
|
||||
public abstract void render(LodRenderProgram shaderProgram);
|
||||
public abstract void debugDumpStats(StatsMap statsMap);
|
||||
|
||||
@Override
|
||||
public abstract void close();
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package com.seibel.lod.core.objects.opengl;
|
||||
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.lwjgl.opengl.GL32;
|
||||
|
||||
import com.seibel.lod.core.builders.bufferBuilding.LodBufferBuilderFactory;
|
||||
import com.seibel.lod.core.builders.lodBuilding.LodBuilder;
|
||||
import com.seibel.lod.core.enums.config.GpuUploadMethod;
|
||||
import com.seibel.lod.core.objects.RenderRegion;
|
||||
import com.seibel.lod.core.objects.lod.RegionPos;
|
||||
import com.seibel.lod.core.objects.math.Mat4f;
|
||||
import com.seibel.lod.core.objects.math.Vec3d;
|
||||
import com.seibel.lod.core.render.LodRenderProgram;
|
||||
import com.seibel.lod.core.render.RenderUtil;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.util.StatsMap;
|
||||
import com.seibel.lod.core.util.UnitBytes;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||
|
||||
public class ComplexRenderRegion extends RenderRegion {
|
||||
LodVertexBuffer[] vbos;
|
||||
final RegionPos regPos;
|
||||
private static final float FULL_SIZED_BUFFERS =
|
||||
LodBufferBuilderFactory.MAX_TRIANGLES_PER_BUFFER * LodUtil.LOD_VERTEX_FORMAT.getByteSize();
|
||||
|
||||
public ComplexRenderRegion(RegionPos pos) {
|
||||
vbos = new LodVertexBuffer[1];
|
||||
regPos = pos;
|
||||
}
|
||||
|
||||
public void resize(int size) {
|
||||
if (vbos.length != size) {
|
||||
LodVertexBuffer[] newVbos = new LodVertexBuffer[size];
|
||||
if (vbos.length > size) {
|
||||
for (int i=size; i<vbos.length; i++) {
|
||||
vbos[i].close();
|
||||
vbos[i] = null;
|
||||
}
|
||||
}
|
||||
for (int i=0; i<newVbos.length && i<vbos.length; i++) {
|
||||
newVbos[i] = vbos[i];
|
||||
vbos[i] = null;
|
||||
}
|
||||
for (LodVertexBuffer b : vbos) {
|
||||
if (b != null) throw new RuntimeException("LEAKING VBO!");
|
||||
}
|
||||
vbos = newVbos;
|
||||
}
|
||||
}
|
||||
|
||||
public LodVertexBuffer[] debugGetBuffers() {
|
||||
return vbos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
|
||||
}
|
||||
|
||||
public LodVertexBuffer getOrMakeVbo(int iIndex, boolean useBuffStorage) {
|
||||
if (vbos[iIndex] == null) {
|
||||
vbos[iIndex] = new LodVertexBuffer(useBuffStorage);
|
||||
} else if (vbos[iIndex].isBufferStorage != useBuffStorage) {
|
||||
vbos[iIndex].close();
|
||||
vbos[iIndex] = new LodVertexBuffer(useBuffStorage);
|
||||
}
|
||||
return vbos[iIndex];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRender(IMinecraftRenderWrapper renderer, boolean enableDirectionalCulling) {
|
||||
if (enableDirectionalCulling && !RenderUtil.isRegionInViewFrustum(renderer.getCameraBlockPosition(),
|
||||
renderer.getLookAtVector(), regPos.x, regPos.z)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(LodRenderProgram shaderProgram)
|
||||
{
|
||||
for (LodVertexBuffer vbo : vbos) {
|
||||
if (vbo == null) continue;
|
||||
if (vbo.vertexCount == 0) continue;
|
||||
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, vbo.id);
|
||||
shaderProgram.bindVertexBuffer(vbo.id);
|
||||
GL32.glDrawArrays(GL32.GL_TRIANGLES, 0, vbo.vertexCount);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debugDumpStats(StatsMap statsMap)
|
||||
{
|
||||
statsMap.incStat("RegionRegions");
|
||||
statsMap.incStat("SimpleRegionRegions");
|
||||
for (LodVertexBuffer b : vbos) {
|
||||
if (b == null) continue;
|
||||
statsMap.incStat("Buffers");
|
||||
if (b.size == FULL_SIZED_BUFFERS) {
|
||||
statsMap.incStat("FullsizedBuffers");
|
||||
}
|
||||
statsMap.incBytesStat("TotalUsage", b.size);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uploadBuffers(LodQuadBuilder builder, GpuUploadMethod uploadMethod)
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,9 +19,13 @@
|
||||
|
||||
package com.seibel.lod.core.objects.opengl;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.lwjgl.opengl.GL32;
|
||||
import org.lwjgl.opengl.GL44;
|
||||
|
||||
import com.seibel.lod.core.api.ClientApi;
|
||||
import com.seibel.lod.core.enums.config.GpuUploadMethod;
|
||||
import com.seibel.lod.core.enums.rendering.GLProxyContext;
|
||||
import com.seibel.lod.core.render.GLProxy;
|
||||
|
||||
@@ -34,20 +38,151 @@ import com.seibel.lod.core.render.GLProxy;
|
||||
*/
|
||||
public class LodVertexBuffer implements AutoCloseable
|
||||
{
|
||||
/**
|
||||
* 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;
|
||||
public static int count = 0;
|
||||
public int id;
|
||||
public int vertexCount;
|
||||
public final boolean isBufferStorage;
|
||||
public boolean isBufferStorage;
|
||||
public long size = 0;
|
||||
|
||||
public LodVertexBuffer(boolean isBufferStorage)
|
||||
{
|
||||
_create(isBufferStorage);
|
||||
}
|
||||
|
||||
private void _create(boolean asBufferStorage) {
|
||||
if (GLProxy.getInstance().getGlContext() == GLProxyContext.NONE)
|
||||
throw new IllegalStateException("Thread [" +Thread.currentThread().getName() + "] tried to create a [" + LodVertexBuffer.class.getSimpleName() + "] outside a OpenGL contex.");
|
||||
this.id = GL32.glGenBuffers();
|
||||
this.isBufferStorage = isBufferStorage;
|
||||
this.isBufferStorage = asBufferStorage;
|
||||
count++;
|
||||
ClientApi.LOGGER.debug("LodVertexBuffer Count: "+count);
|
||||
}
|
||||
|
||||
private void _destroy() {
|
||||
if (GLProxy.getInstance().getGlContext() == GLProxyContext.PROXY_WORKER) {
|
||||
GL32.glDeleteBuffers(this.id);
|
||||
} else {
|
||||
final int id = this.id;
|
||||
GLProxy.getInstance().recordOpenGlCall(() -> GL32.glDeleteBuffers(id));
|
||||
}
|
||||
this.id = -1;
|
||||
count--;
|
||||
}
|
||||
|
||||
private void _uploadBufferStorage(ByteBuffer bb, int maxExpensionSize) {
|
||||
if (!isBufferStorage) throw new IllegalStateException("Buffer isn't bufferStorage but its trying to use BufferStorage upload method!");
|
||||
int bbSize = bb.limit() - bb.position();
|
||||
if (size < bbSize || size > bbSize * BUFFER_EXPANSION_MULTIPLIER * BUFFER_EXPANSION_MULTIPLIER) {
|
||||
int newSize = (int) (bbSize * BUFFER_EXPANSION_MULTIPLIER);
|
||||
if (newSize > maxExpensionSize) newSize = maxExpensionSize;
|
||||
GL32.glDeleteBuffers(id);
|
||||
id = GL32.glGenBuffers();
|
||||
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, id);
|
||||
GL44.glBufferStorage(GL32.GL_ARRAY_BUFFER, newSize, GL44.GL_MAP_WRITE_BIT);
|
||||
size = newSize;
|
||||
} else {
|
||||
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, id);
|
||||
}
|
||||
|
||||
// map buffer range is better since it can be explicitly unsynchronized
|
||||
ByteBuffer vboBuffer = GL32.glMapBufferRange(GL32.GL_ARRAY_BUFFER, 0, bbSize,
|
||||
GL32.GL_MAP_WRITE_BIT | GL32.GL_MAP_UNSYNCHRONIZED_BIT | GL32.GL_MAP_INVALIDATE_BUFFER_BIT);
|
||||
if (vboBuffer == null) {
|
||||
ClientApi.LOGGER.error("MapBufferRange Failed: bbSize: {}, maxSize: {}, size: {}", bbSize, maxExpensionSize, size);
|
||||
}
|
||||
vboBuffer.put(bb);
|
||||
GL32.glUnmapBuffer(GL32.GL_ARRAY_BUFFER);
|
||||
}
|
||||
|
||||
// no stuttering but high GPU usage
|
||||
// stores everything in system memory instead of GPU memory
|
||||
// making rendering much slower.
|
||||
// Unless the user is running integrated graphics,
|
||||
// in that case this will actually work better than SUB_DATA.
|
||||
private void _uploadBufferMapping(ByteBuffer bb, int maxExpensionSize) {
|
||||
if (isBufferStorage) throw new IllegalStateException("Buffer is bufferStorage but its trying to use BufferMapping upload method!");
|
||||
int bbSize = bb.limit() - bb.position();
|
||||
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, id);
|
||||
if (size < bbSize || size > bbSize * BUFFER_EXPANSION_MULTIPLIER * BUFFER_EXPANSION_MULTIPLIER) {
|
||||
int newSize = (int) (bbSize * BUFFER_EXPANSION_MULTIPLIER);
|
||||
if (newSize > maxExpensionSize) newSize = maxExpensionSize;
|
||||
GL32.glBufferData(GL32.GL_ARRAY_BUFFER, newSize, GL32.GL_STATIC_DRAW);
|
||||
size = newSize;
|
||||
}
|
||||
|
||||
// map buffer range is better since it can be explicitly unsynchronized
|
||||
ByteBuffer vboBuffer = GL32.glMapBufferRange(GL32.GL_ARRAY_BUFFER, 0, bbSize,
|
||||
GL32.GL_MAP_WRITE_BIT | GL32.GL_MAP_UNSYNCHRONIZED_BIT | GL32.GL_MAP_INVALIDATE_BUFFER_BIT);
|
||||
vboBuffer.put(bb);
|
||||
GL32.glUnmapBuffer(GL32.GL_ARRAY_BUFFER);
|
||||
}
|
||||
|
||||
// bufferData
|
||||
// simplest/most compatible
|
||||
private void _uploadData(ByteBuffer bb) {
|
||||
if (isBufferStorage) throw new IllegalStateException("Buffer is bufferStorage but its trying to use Data upload method!");
|
||||
int bbSize = bb.limit() - bb.position();
|
||||
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, id);
|
||||
GL32.glBufferData(GL32.GL_ARRAY_BUFFER, bb, GL32.GL_STATIC_DRAW);
|
||||
size = bbSize;
|
||||
}
|
||||
|
||||
// bufferSubData
|
||||
// less stutter, low GPU usage?
|
||||
private void _uploadSubData(ByteBuffer bb, int maxExpensionSize) {
|
||||
if (isBufferStorage) throw new IllegalStateException("Buffer is bufferStorage but its trying to use SubData upload method!");
|
||||
int bbSize = bb.limit() - bb.position();
|
||||
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, id);
|
||||
if (size < bbSize || size > bbSize * BUFFER_EXPANSION_MULTIPLIER * BUFFER_EXPANSION_MULTIPLIER) {
|
||||
int newSize = (int) (bbSize * BUFFER_EXPANSION_MULTIPLIER);
|
||||
if (newSize > maxExpensionSize) newSize = maxExpensionSize;
|
||||
GL32.glBufferData(GL32.GL_ARRAY_BUFFER, newSize, GL32.GL_STATIC_DRAW);
|
||||
size = newSize;
|
||||
}
|
||||
GL32.glBufferSubData(GL32.GL_ARRAY_BUFFER, 0, bb);
|
||||
}
|
||||
|
||||
public void uploadBuffer(ByteBuffer bb, int vertCount, GpuUploadMethod uploadMethod, int maxExpensionSize) {
|
||||
if (vertCount < 0) throw new IllegalArgumentException("VertCount is negative!");
|
||||
vertexCount = vertCount;
|
||||
int bbSize = bb.limit()-bb.position();
|
||||
if (bbSize > maxExpensionSize)
|
||||
throw new IllegalArgumentException("maxExpensionSize is "+maxExpensionSize+" but buffer size is "+bbSize+"!");
|
||||
// If size is zero, just ignore it.
|
||||
if (bbSize == 0) return;
|
||||
boolean useBuffStorage = uploadMethod == GpuUploadMethod.BUFFER_STORAGE;
|
||||
if (useBuffStorage != isBufferStorage) {
|
||||
_destroy();
|
||||
_create(useBuffStorage);
|
||||
}
|
||||
try {
|
||||
switch (uploadMethod) {
|
||||
case AUTO:
|
||||
throw new IllegalArgumentException("GpuUploadMethod AUTO must be resolved before call to uploadBuffer()!");
|
||||
case BUFFER_MAPPING:
|
||||
_uploadBufferMapping(bb, maxExpensionSize);
|
||||
break;
|
||||
case BUFFER_STORAGE:
|
||||
_uploadBufferStorage(bb, maxExpensionSize);
|
||||
break;
|
||||
case DATA:
|
||||
_uploadData(bb);
|
||||
break;
|
||||
case SUB_DATA:
|
||||
_uploadSubData(bb, maxExpensionSize);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid GpuUploadMethod enum");
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
ClientApi.LOGGER.error("vboUpload failed: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -56,15 +191,7 @@ public class LodVertexBuffer implements AutoCloseable
|
||||
{
|
||||
if (this.id >= 0)
|
||||
{
|
||||
if (GLProxy.getInstance().getGlContext() == GLProxyContext.PROXY_WORKER) {
|
||||
GL32.glDeleteBuffers(this.id);
|
||||
} else {
|
||||
final int id = this.id;
|
||||
GLProxy.getInstance().recordOpenGlCall(() -> GL32.glDeleteBuffers(id));
|
||||
}
|
||||
this.id = -1;
|
||||
count--;
|
||||
ClientApi.LOGGER.debug("LodVertexBuffer Count: "+count);
|
||||
_destroy();
|
||||
if (count==0) ClientApi.LOGGER.info("All LodVerrtexBuffer is freed.");
|
||||
} else {
|
||||
ClientApi.LOGGER.error("LodVertexBuffer double close!");
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
package com.seibel.lod.core.objects.opengl;
|
||||
|
||||
public class RenderRegion implements AutoCloseable {
|
||||
LodVertexBuffer[] vbos;
|
||||
|
||||
public RenderRegion(int size) {
|
||||
vbos = new LodVertexBuffer[size];
|
||||
}
|
||||
|
||||
public void resize(int size) {
|
||||
if (vbos.length != size) {
|
||||
LodVertexBuffer[] newVbos = new LodVertexBuffer[size];
|
||||
if (vbos.length > size) {
|
||||
for (int i=size; i<vbos.length; i++) {
|
||||
vbos[i].close();
|
||||
vbos[i] = null;
|
||||
}
|
||||
}
|
||||
for (int i=0; i<newVbos.length && i<vbos.length; i++) {
|
||||
newVbos[i] = vbos[i];
|
||||
vbos[i] = null;
|
||||
}
|
||||
for (LodVertexBuffer b : vbos) {
|
||||
if (b != null) throw new RuntimeException("LEAKING VBO!");
|
||||
}
|
||||
vbos = newVbos;
|
||||
}
|
||||
}
|
||||
|
||||
public LodVertexBuffer[] debugGetBuffers() {
|
||||
return vbos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
|
||||
}
|
||||
|
||||
public LodVertexBuffer getOrMakeVbo(int iIndex, boolean useBuffStorage) {
|
||||
if (vbos[iIndex] == null) {
|
||||
vbos[iIndex] = new LodVertexBuffer(useBuffStorage);
|
||||
} else if (vbos[iIndex].isBufferStorage != useBuffStorage) {
|
||||
vbos[iIndex].close();
|
||||
vbos[iIndex] = new LodVertexBuffer(useBuffStorage);
|
||||
}
|
||||
return vbos[iIndex];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
package com.seibel.lod.core.objects.opengl;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.lwjgl.opengl.GL32;
|
||||
|
||||
import com.seibel.lod.core.builders.bufferBuilding.LodBufferBuilderFactory;
|
||||
import com.seibel.lod.core.enums.config.GpuUploadMethod;
|
||||
import com.seibel.lod.core.objects.RenderRegion;
|
||||
import com.seibel.lod.core.objects.lod.RegionPos;
|
||||
import com.seibel.lod.core.render.LodRenderProgram;
|
||||
import com.seibel.lod.core.render.RenderUtil;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.util.SingletonHandler;
|
||||
import com.seibel.lod.core.util.StatsMap;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||
|
||||
public class SimpleRenderRegion extends RenderRegion {
|
||||
LodVertexBuffer[] vbos;
|
||||
final RegionPos regPos;
|
||||
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||
private static final int FULL_SIZED_BUFFERS =
|
||||
LodBufferBuilderFactory.MAX_TRIANGLES_PER_BUFFER * LodUtil.LOD_VERTEX_FORMAT.getByteSize() * 3;
|
||||
|
||||
public SimpleRenderRegion(int size, RegionPos pos) {
|
||||
vbos = new LodVertexBuffer[size];
|
||||
regPos = pos;
|
||||
}
|
||||
|
||||
private void resize(int size) {
|
||||
if (vbos.length != size) {
|
||||
LodVertexBuffer[] newVbos = new LodVertexBuffer[size];
|
||||
if (vbos.length > size) {
|
||||
for (int i=size; i<vbos.length; i++) {
|
||||
if (vbos[i]!=null) vbos[i].close();
|
||||
vbos[i] = null;
|
||||
}
|
||||
}
|
||||
for (int i=0; i<newVbos.length && i<vbos.length; i++) {
|
||||
newVbos[i] = vbos[i];
|
||||
vbos[i] = null;
|
||||
}
|
||||
for (LodVertexBuffer b : vbos) {
|
||||
if (b != null) throw new RuntimeException("LEAKING VBO!");
|
||||
}
|
||||
vbos = newVbos;
|
||||
}
|
||||
}
|
||||
|
||||
public LodVertexBuffer[] debugGetBuffers() {
|
||||
return vbos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
for (LodVertexBuffer b : vbos) {
|
||||
if (b != null) b.close();
|
||||
}
|
||||
vbos = new LodVertexBuffer[0];
|
||||
}
|
||||
|
||||
private LodVertexBuffer getOrMakeVbo(int iIndex, boolean useBuffStorage) {
|
||||
if (vbos[iIndex] == null) {
|
||||
vbos[iIndex] = new LodVertexBuffer(useBuffStorage);
|
||||
}
|
||||
return vbos[iIndex];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uploadBuffers(LodQuadBuilder builder, GpuUploadMethod uploadMethod)
|
||||
{
|
||||
resize(builder.getCurrentNeededVertexBuffers());
|
||||
long remainingNS = 0;
|
||||
long BPerNS = CONFIG.client().advanced().buffers().getGpuUploadPerMegabyteInMilliseconds();
|
||||
|
||||
int i = 0;
|
||||
Iterator<ByteBuffer> iter = builder.makeVertexBuffers();
|
||||
while (iter.hasNext()) {
|
||||
ByteBuffer bb = iter.next();
|
||||
LodVertexBuffer vbo = getOrMakeVbo(i++, uploadMethod==GpuUploadMethod.BUFFER_STORAGE);
|
||||
int size = bb.limit() - bb.position();
|
||||
vbo.uploadBuffer(bb, size/LodUtil.LOD_VERTEX_FORMAT.getByteSize(), uploadMethod, FULL_SIZED_BUFFERS);
|
||||
// upload buffers over an extended period of time
|
||||
// to hopefully prevent stuttering.
|
||||
remainingNS += size * BPerNS;
|
||||
if (remainingNS >= TimeUnit.NANOSECONDS.convert(1000 / 60, TimeUnit.MILLISECONDS)) {
|
||||
if (remainingNS > LodBufferBuilderFactory.MAX_BUFFER_UPLOAD_TIMEOUT_NANOSECONDS)
|
||||
remainingNS = LodBufferBuilderFactory.MAX_BUFFER_UPLOAD_TIMEOUT_NANOSECONDS;
|
||||
try {
|
||||
Thread.sleep(remainingNS / 1000000, (int) (remainingNS % 1000000));
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
remainingNS = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRender(IMinecraftRenderWrapper renderer, boolean enableDirectionalCulling) {
|
||||
if (enableDirectionalCulling && !RenderUtil.isRegionInViewFrustum(renderer.getCameraBlockPosition(),
|
||||
renderer.getLookAtVector(), regPos.x, regPos.z)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(LodRenderProgram shaderProgram)
|
||||
{
|
||||
for (LodVertexBuffer vbo : vbos) {
|
||||
if (vbo == null) continue;
|
||||
if (vbo.vertexCount == 0) continue;
|
||||
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, vbo.id);
|
||||
shaderProgram.bindVertexBuffer(vbo.id);
|
||||
GL32.glDrawArrays(GL32.GL_TRIANGLES, 0, vbo.vertexCount);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debugDumpStats(StatsMap statsMap)
|
||||
{
|
||||
statsMap.incStat("RegionRegions");
|
||||
statsMap.incStat("SimpleRegionRegions");
|
||||
for (LodVertexBuffer b : vbos) {
|
||||
if (b == null) continue;
|
||||
statsMap.incStat("Buffers");
|
||||
if (b.size == FULL_SIZED_BUFFERS) {
|
||||
statsMap.incStat("FullsizedBuffers");
|
||||
}
|
||||
statsMap.incBytesStat("TotalUsage", b.size);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -159,7 +159,7 @@ public class GLProxy
|
||||
{
|
||||
|
||||
// boolean enableDebugLogging = CONFIG.client().advanced().debugging().getDebugMode() == DebugMode.SHOW_DETAIL;
|
||||
boolean enableDebugLogging = false;
|
||||
boolean enableDebugLogging = true;
|
||||
// this must be created on minecraft's render context to work correctly
|
||||
|
||||
ClientApi.LOGGER.info("Creating " + GLProxy.class.getSimpleName() + "... If this is the last message you see in the log there must have been a OpenGL error.");
|
||||
|
||||
@@ -34,15 +34,12 @@ import com.seibel.lod.core.enums.rendering.DebugMode;
|
||||
import com.seibel.lod.core.enums.rendering.FogColorMode;
|
||||
import com.seibel.lod.core.enums.rendering.FogDistance;
|
||||
import com.seibel.lod.core.handlers.IReflectionHandler;
|
||||
import com.seibel.lod.core.objects.RenderRegion;
|
||||
import com.seibel.lod.core.objects.lod.LodDimension;
|
||||
import com.seibel.lod.core.objects.math.Mat4f;
|
||||
import com.seibel.lod.core.objects.math.Vec3d;
|
||||
import com.seibel.lod.core.objects.math.Vec3f;
|
||||
import com.seibel.lod.core.objects.opengl.LodVertexBuffer;
|
||||
import com.seibel.lod.core.objects.opengl.RenderRegion;
|
||||
import com.seibel.lod.core.render.objects.LightmapTexture;
|
||||
import com.seibel.lod.core.util.DetailDistanceUtil;
|
||||
import com.seibel.lod.core.util.LevelPosUtil;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.util.MovableGridList;
|
||||
import com.seibel.lod.core.util.SingletonHandler;
|
||||
@@ -220,8 +217,6 @@ public class LodRenderer
|
||||
swapBuffer.end("SwapBuffer");
|
||||
// Get the front buffers to draw
|
||||
MovableGridList<RenderRegion> regions = lodBufferBuilderFactory.getFrontBuffers();
|
||||
int vbosCenterX = lodBufferBuilderFactory.getFrontBuffersCenterX();
|
||||
int vbosCenterZ = lodBufferBuilderFactory.getFrontBuffersCenterZ();
|
||||
|
||||
if (regions == null) {
|
||||
// There is no vbos, which means nothing needs to be drawn. So skip rendering
|
||||
@@ -317,43 +312,26 @@ public class LodRenderer
|
||||
LagSpikeCatcher draw = new LagSpikeCatcher();
|
||||
|
||||
boolean cullingDisabled = CONFIG.client().graphics().advancedGraphics().getDisableDirectionalCulling();
|
||||
Vec3d cameraPos = MC_RENDER.getCameraExactPosition();
|
||||
|
||||
// where the center of the buffers is (needed when culling regions)
|
||||
// render each of the buffers
|
||||
int lowRegionX = regions.getCenterX() - regions.gridCentreToEdge;
|
||||
int lowRegionZ = regions.getCenterY() - regions.gridCentreToEdge;
|
||||
int drawCall = 0;
|
||||
int vCount0 = 0;
|
||||
for (int regionX=lowRegionX; regionX<lowRegionX+regions.gridSize; regionX++) {
|
||||
for (int regionZ=lowRegionZ; regionZ<lowRegionZ+regions.gridSize; regionZ++) {
|
||||
if (regions.get(regionX, regionZ) == null) continue;
|
||||
RenderRegion region = regions.get(regionX, regionZ);
|
||||
if (region == null) continue;
|
||||
if (!region.shouldRender(MC_RENDER, !cullingDisabled)) continue;
|
||||
|
||||
if (cullingDisabled || RenderUtil.isRegionInViewFrustum(MC_RENDER.getCameraBlockPosition(),
|
||||
MC_RENDER.getLookAtVector(), regionX, regionZ)) {
|
||||
RenderRegion region = regions.get(regionX, regionZ);
|
||||
//TODO improve this
|
||||
Vec3d cameraPos = MC_RENDER.getCameraExactPosition();
|
||||
|
||||
Mat4f localModelViewMatrix = baseModelViewMatrix.copy();
|
||||
localModelViewMatrix.multiplyTranslationMatrix(
|
||||
(regionX * LodUtil.REGION_WIDTH) - cameraPos.x,
|
||||
LodBuilder.MIN_WORLD_HEIGHT - cameraPos.y,
|
||||
(regionZ * LodUtil.REGION_WIDTH) - cameraPos.z);
|
||||
shaderProgram.fillUniformModelMatrix(localModelViewMatrix);
|
||||
|
||||
for (LodVertexBuffer vbo : region.debugGetBuffers()) {
|
||||
if (vbo == null) continue;
|
||||
if (vbo.vertexCount == 0) {
|
||||
vCount0++;
|
||||
continue;
|
||||
}
|
||||
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, vbo.id);
|
||||
shaderProgram.bindVertexBuffer(vbo.id);
|
||||
drawCall++;
|
||||
GL32.glDrawArrays(GL32.GL_TRIANGLES, 0, vbo.vertexCount);
|
||||
}
|
||||
|
||||
}
|
||||
Mat4f localModelViewMatrix = baseModelViewMatrix.copy();
|
||||
localModelViewMatrix.multiplyTranslationMatrix(
|
||||
(regionX * LodUtil.REGION_WIDTH) - cameraPos.x,
|
||||
LodBuilder.MIN_WORLD_HEIGHT - cameraPos.y,
|
||||
(regionZ * LodUtil.REGION_WIDTH) - cameraPos.z);
|
||||
shaderProgram.fillUniformModelMatrix(localModelViewMatrix);
|
||||
|
||||
region.render(shaderProgram);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.seibel.lod.core.util;
|
||||
|
||||
import java.util.TreeMap;
|
||||
|
||||
public class StatsMap
|
||||
{
|
||||
final TreeMap<String, Long> longMap = new TreeMap<String, Long>();
|
||||
final TreeMap<String, UnitBytes> bytesMap = new TreeMap<String, UnitBytes>();
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private static final long serialVersionUID = 1926219295516863173L;
|
||||
|
||||
public StatsMap() {super();}
|
||||
|
||||
public void incStat(String key) {
|
||||
incStat(key, 1);
|
||||
}
|
||||
public void incStat(String key, long value) {
|
||||
longMap.put(key, longMap.getOrDefault(key, 0L)+value);
|
||||
}
|
||||
public void incBytesStat(String key, long bytes) {
|
||||
bytesMap.put(key, new UnitBytes(bytesMap.getOrDefault(key, new UnitBytes(0)).value()+bytes));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return longMap.toString() + " " + bytesMap.toString();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -6,6 +6,8 @@ public class UnitBytes
|
||||
public UnitBytes(long value) {
|
||||
this.value = value;
|
||||
}
|
||||
public long value() {return value;}
|
||||
|
||||
public static long byteToGB(long v) {
|
||||
return v/1073741824;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user