Merge render VBOs
This commit is contained in:
-9
@@ -36,13 +36,4 @@ public interface IDhApiGpuBuffersConfig extends IDhApiConfigGroup
|
||||
/** Defines how geometry data is uploaded to the GPU. */
|
||||
IDhApiConfigValue<EDhApiGpuUploadMethod> gpuUploadMethod();
|
||||
|
||||
/**
|
||||
* Defines how long we should wait after uploading one
|
||||
* Megabyte of geometry data to the GPU before uploading
|
||||
* the next Megabyte of data. <br>
|
||||
* This can be set to a non-zero number to reduce stuttering caused by
|
||||
* uploading buffers to the GPU.
|
||||
*/
|
||||
IDhApiConfigValue<Integer> gpuUploadPerMegabyteInMilliseconds();
|
||||
|
||||
}
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
|
||||
package com.seibel.distanthorizons.coreapi.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.text.CharacterIterator;
|
||||
import java.text.StringCharacterIterator;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
@@ -85,4 +86,25 @@ public class StringUtil
|
||||
return new String(hexChars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Source:
|
||||
* https://stackoverflow.com/questions/3758606/how-can-i-convert-byte-size-into-a-human-readable-format-in-java#3758880
|
||||
*/
|
||||
public static String convertByteCountToHumanReadableSI(long bytes)
|
||||
{
|
||||
if (-1000 < bytes && bytes < 1000)
|
||||
{
|
||||
return bytes + " B";
|
||||
}
|
||||
|
||||
CharacterIterator ci = new StringCharacterIterator("kMGTPE");
|
||||
while (bytes <= -999_950 || bytes >= 999_950)
|
||||
{
|
||||
bytes /= 1000;
|
||||
ci.next();
|
||||
}
|
||||
|
||||
return String.format("%.1f %cB", bytes / 1000.0, ci.current());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-3
@@ -36,7 +36,4 @@ public class DhApiGpuBuffersConfig implements IDhApiGpuBuffersConfig
|
||||
public IDhApiConfigValue<EDhApiGpuUploadMethod> gpuUploadMethod()
|
||||
{ return new DhApiConfigValue<>(Config.Client.Advanced.GpuBuffers.gpuUploadMethod); }
|
||||
|
||||
public IDhApiConfigValue<Integer> gpuUploadPerMegabyteInMilliseconds()
|
||||
{ return new DhApiConfigValue<>(Config.Client.Advanced.GpuBuffers.gpuUploadPerMegabyteInMilliseconds); }
|
||||
|
||||
}
|
||||
|
||||
@@ -1054,20 +1054,6 @@ public class Config
|
||||
+ "")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<Integer> gpuUploadPerMegabyteInMilliseconds = new ConfigEntry.Builder<Integer>()
|
||||
.setMinDefaultMax(0, 0, 50)
|
||||
.comment(""
|
||||
+ "How long should a buffer wait per Megabyte of data uploaded? \n"
|
||||
+ "Helpful resource for frame times: https://fpstoms.com \n"
|
||||
+ "\n"
|
||||
+ "Longer times may reduce stuttering but will make LODs \n"
|
||||
+ "transition and load slower. Change this to [0] for no timeout. \n"
|
||||
+ "\n"
|
||||
+ "NOTE:\n"
|
||||
+ "Before changing this config, try changing the \"GPU Upload method\" first. \n"
|
||||
+ "")
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
public static class AutoUpdater
|
||||
|
||||
+92
-187
@@ -20,15 +20,11 @@
|
||||
package com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding;
|
||||
|
||||
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.DhBlockPos;
|
||||
import com.seibel.distanthorizons.core.render.glObject.GLProxy;
|
||||
import com.seibel.distanthorizons.core.render.glObject.buffer.GLVertexBuffer;
|
||||
import com.seibel.distanthorizons.core.render.renderer.LodRenderer;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.util.objects.StatsMap;
|
||||
import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod;
|
||||
import com.seibel.distanthorizons.core.util.*;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||
@@ -59,13 +55,12 @@ public class ColumnRenderBuffer implements AutoCloseable
|
||||
|
||||
|
||||
|
||||
|
||||
public final DhBlockPos pos;
|
||||
|
||||
public boolean buffersUploaded = false;
|
||||
|
||||
private GLVertexBuffer[] vbos;
|
||||
private GLVertexBuffer[] vbosTransparent;
|
||||
private SharedVbo.BufferSlice[] opaqueBufferSlices;
|
||||
private SharedVbo.BufferSlice[] transparentBufferSlices;
|
||||
|
||||
|
||||
|
||||
@@ -76,14 +71,12 @@ public class ColumnRenderBuffer implements AutoCloseable
|
||||
public ColumnRenderBuffer(DhBlockPos pos)
|
||||
{
|
||||
this.pos = pos;
|
||||
this.vbos = new GLVertexBuffer[0];
|
||||
this.vbosTransparent = new GLVertexBuffer[0];
|
||||
this.opaqueBufferSlices = new SharedVbo.BufferSlice[0];
|
||||
this.transparentBufferSlices = new SharedVbo.BufferSlice[0];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//==================//
|
||||
// buffer uploading //
|
||||
//==================//
|
||||
@@ -128,192 +121,143 @@ public class ColumnRenderBuffer implements AutoCloseable
|
||||
}
|
||||
private void uploadBuffersUsingUploadMethod(LodQuadBuilder builder, EDhApiGpuUploadMethod gpuUploadMethod) throws InterruptedException
|
||||
{
|
||||
if (gpuUploadMethod.useEarlyMapping)
|
||||
{
|
||||
this.uploadBuffersMapped(builder, gpuUploadMethod);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.uploadBuffersDirect(builder, gpuUploadMethod);
|
||||
}
|
||||
//if (gpuUploadMethod.useEarlyMapping)
|
||||
//{
|
||||
// this.uploadBuffersMapped(builder, gpuUploadMethod);
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
this.uploadBuffersDirect(builder);
|
||||
//}
|
||||
|
||||
this.buffersUploaded = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void uploadBuffersMapped(LodQuadBuilder builder, EDhApiGpuUploadMethod method)
|
||||
//private void uploadBuffersMapped(LodQuadBuilder builder, EDhApiGpuUploadMethod method)
|
||||
//{
|
||||
// // opaque vbos //
|
||||
//
|
||||
// this.vbos = ColumnRenderBufferBuilder.resizeBuffer(this.vbos, builder.getCurrentNeededOpaqueVertexBufferCount());
|
||||
// for (int i = 0; i < this.vbos.length; i++)
|
||||
// {
|
||||
// if (this.vbos[i] == null)
|
||||
// {
|
||||
// this.vbos[i] = new GLVertexBuffer(method.useBufferStorage);
|
||||
// }
|
||||
// }
|
||||
// LodQuadBuilder.BufferFiller func = builder.makeOpaqueBufferFiller(method);
|
||||
// for (GLVertexBuffer vbo : this.vbos)
|
||||
// {
|
||||
// func.fill(vbo);
|
||||
// }
|
||||
//
|
||||
//
|
||||
// // transparent vbos //
|
||||
//
|
||||
// this.vbosTransparent = ColumnRenderBufferBuilder.resizeBuffer(this.vbosTransparent, builder.getCurrentNeededTransparentVertexBufferCount());
|
||||
// for (int i = 0; i < this.vbosTransparent.length; i++)
|
||||
// {
|
||||
// if (this.vbosTransparent[i] == null)
|
||||
// {
|
||||
// this.vbosTransparent[i] = new GLVertexBuffer(method.useBufferStorage);
|
||||
// }
|
||||
// }
|
||||
// LodQuadBuilder.BufferFiller transparentFillerFunc = builder.makeTransparentBufferFiller(method);
|
||||
// for (GLVertexBuffer vbo : this.vbosTransparent)
|
||||
// {
|
||||
// transparentFillerFunc.fill(vbo);
|
||||
// }
|
||||
//}
|
||||
|
||||
private void uploadBuffersDirect(LodQuadBuilder builder)
|
||||
{
|
||||
// opaque vbos //
|
||||
|
||||
this.vbos = ColumnRenderBufferBuilder.resizeBuffer(this.vbos, builder.getCurrentNeededOpaqueVertexBufferCount());
|
||||
for (int i = 0; i < this.vbos.length; i++)
|
||||
int opaqueSliceCount = builder.getCurrentNeededOpaqueVertexBufferCount();
|
||||
if (this.opaqueBufferSlices.length != opaqueSliceCount)
|
||||
{
|
||||
if (this.vbos[i] == null)
|
||||
{
|
||||
this.vbos[i] = new GLVertexBuffer(method.useBufferStorage);
|
||||
}
|
||||
}
|
||||
LodQuadBuilder.BufferFiller func = builder.makeOpaqueBufferFiller(method);
|
||||
for (GLVertexBuffer vbo : this.vbos)
|
||||
{
|
||||
func.fill(vbo);
|
||||
SharedVbo.OPAQUE.deallocateChunks(this.opaqueBufferSlices);
|
||||
this.opaqueBufferSlices = new SharedVbo.BufferSlice[opaqueSliceCount];
|
||||
}
|
||||
uploadBuffersDirect(SharedVbo.OPAQUE, this.opaqueBufferSlices, builder.makeOpaqueVertexBuffers());
|
||||
|
||||
|
||||
// transparent vbos //
|
||||
|
||||
this.vbosTransparent = ColumnRenderBufferBuilder.resizeBuffer(this.vbosTransparent, builder.getCurrentNeededTransparentVertexBufferCount());
|
||||
for (int i = 0; i < this.vbosTransparent.length; i++)
|
||||
int transparentSliceCount = builder.getCurrentNeededTransparentVertexBufferCount();
|
||||
if (this.transparentBufferSlices.length != transparentSliceCount)
|
||||
{
|
||||
if (this.vbosTransparent[i] == null)
|
||||
{
|
||||
this.vbosTransparent[i] = new GLVertexBuffer(method.useBufferStorage);
|
||||
}
|
||||
}
|
||||
LodQuadBuilder.BufferFiller transparentFillerFunc = builder.makeTransparentBufferFiller(method);
|
||||
for (GLVertexBuffer vbo : this.vbosTransparent)
|
||||
{
|
||||
transparentFillerFunc.fill(vbo);
|
||||
SharedVbo.TRANSPARENT.deallocateChunks(this.transparentBufferSlices);
|
||||
this.transparentBufferSlices = new SharedVbo.BufferSlice[transparentSliceCount];
|
||||
}
|
||||
uploadBuffersDirect(SharedVbo.TRANSPARENT, this.transparentBufferSlices, builder.makeTransparentVertexBuffers());
|
||||
}
|
||||
|
||||
private void uploadBuffersDirect(LodQuadBuilder builder, EDhApiGpuUploadMethod method) throws InterruptedException
|
||||
private static void uploadBuffersDirect(SharedVbo handler, SharedVbo.BufferSlice[] bufferSlices, Iterator<ByteBuffer> iter)
|
||||
{
|
||||
this.vbos = ColumnRenderBufferBuilder.resizeBuffer(this.vbos, builder.getCurrentNeededOpaqueVertexBufferCount());
|
||||
uploadBuffersDirect(this.vbos, builder.makeOpaqueVertexBuffers(), method);
|
||||
|
||||
this.vbosTransparent = ColumnRenderBufferBuilder.resizeBuffer(this.vbosTransparent, builder.getCurrentNeededTransparentVertexBufferCount());
|
||||
uploadBuffersDirect(this.vbosTransparent, builder.makeTransparentVertexBuffers(), method);
|
||||
}
|
||||
private static void uploadBuffersDirect(GLVertexBuffer[] vbos, Iterator<ByteBuffer> iter, EDhApiGpuUploadMethod method) throws InterruptedException
|
||||
{
|
||||
long remainingMS = 0;
|
||||
long MBPerMS = Config.Client.Advanced.GpuBuffers.gpuUploadPerMegabyteInMilliseconds.get();
|
||||
int vboIndex = 0;
|
||||
int i = 0;
|
||||
while (iter.hasNext())
|
||||
{
|
||||
if (vboIndex >= vbos.length)
|
||||
if (i >= bufferSlices.length)
|
||||
{
|
||||
throw new RuntimeException("Too many vertex buffers!!");
|
||||
}
|
||||
|
||||
|
||||
// get or create the VBO
|
||||
if (vbos[vboIndex] == null)
|
||||
// get or create the buffer
|
||||
if (bufferSlices[i] == null)
|
||||
{
|
||||
vbos[vboIndex] = new GLVertexBuffer(method.useBufferStorage);
|
||||
bufferSlices[i] = handler.allocateSlice();
|
||||
}
|
||||
GLVertexBuffer vbo = vbos[vboIndex];
|
||||
SharedVbo.BufferSlice slice = bufferSlices[i];
|
||||
|
||||
|
||||
ByteBuffer bb = iter.next();
|
||||
int size = bb.limit() - bb.position();
|
||||
|
||||
ByteBuffer buffer = iter.next();
|
||||
try
|
||||
{
|
||||
vbo.bind();
|
||||
vbo.uploadBuffer(bb, size / LodUtil.LOD_VERTEX_FORMAT.getByteSize(), method, FULL_SIZED_BUFFER);
|
||||
handler.updateBuffer(slice, buffer);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
vbos[vboIndex] = null;
|
||||
vbo.close();
|
||||
LOGGER.error("Failed to upload buffer: ", e);
|
||||
bufferSlices[i] = null;
|
||||
handler.deallocateChunk(slice);
|
||||
LOGGER.error("Failed to upload buffer. Error: ["+e.getMessage()+"].", e);
|
||||
}
|
||||
|
||||
|
||||
if (MBPerMS > 0)
|
||||
{
|
||||
// upload buffers over an extended period of time
|
||||
// to hopefully prevent stuttering.
|
||||
remainingMS += size * MBPerMS;
|
||||
if (remainingMS >= TimeUnit.NANOSECONDS.convert(1000 / 60, TimeUnit.MILLISECONDS))
|
||||
{
|
||||
if (remainingMS > MAX_BUFFER_UPLOAD_TIMEOUT_NANOSECONDS)
|
||||
{
|
||||
remainingMS = MAX_BUFFER_UPLOAD_TIMEOUT_NANOSECONDS;
|
||||
}
|
||||
|
||||
Thread.sleep(remainingMS / 1000000, (int) (remainingMS % 1000000));
|
||||
remainingMS = 0;
|
||||
}
|
||||
}
|
||||
|
||||
vboIndex++;
|
||||
i++;
|
||||
}
|
||||
|
||||
if (vboIndex < vbos.length)
|
||||
if (i < bufferSlices.length)
|
||||
{
|
||||
throw new RuntimeException("Too few vertex buffers!!");
|
||||
throw new RuntimeException("Too few buffer chunks!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//========//
|
||||
// render //
|
||||
//========//
|
||||
|
||||
/** @return true if something was rendered, false otherwise */
|
||||
public boolean renderOpaque(LodRenderer renderContext, DhApiRenderParam renderEventParam)
|
||||
public void renderOpaque(LodRenderer renderContext, DhApiRenderParam renderEventParam)
|
||||
{
|
||||
boolean hasRendered = false;
|
||||
renderContext.setModelViewMatrixOffset(this.pos, renderEventParam);
|
||||
for (GLVertexBuffer vbo : this.vbos)
|
||||
for (SharedVbo.BufferSlice slice : this.opaqueBufferSlices)
|
||||
{
|
||||
if (vbo == null)
|
||||
if (slice != null && slice.length != 0)
|
||||
{
|
||||
continue;
|
||||
renderContext.drawVbo(SharedVbo.OPAQUE, slice);
|
||||
}
|
||||
|
||||
if (vbo.getVertexCount() == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
hasRendered = true;
|
||||
renderContext.drawVbo(vbo);
|
||||
//LodRenderer.tickLogger.info("Vertex buffer: {}", vbo);
|
||||
}
|
||||
return hasRendered;
|
||||
}
|
||||
|
||||
/** @return true if something was rendered, false otherwise */
|
||||
public boolean renderTransparent(LodRenderer renderContext, DhApiRenderParam renderEventParam)
|
||||
public void renderTransparent(LodRenderer renderContext, DhApiRenderParam renderEventParam)
|
||||
{
|
||||
boolean hasRendered = false;
|
||||
|
||||
try
|
||||
renderContext.setModelViewMatrixOffset(this.pos, renderEventParam);
|
||||
for (SharedVbo.BufferSlice slice : this.transparentBufferSlices)
|
||||
{
|
||||
// can throw an IllegalStateException if the GL program was freed before it should've been
|
||||
renderContext.setModelViewMatrixOffset(this.pos, renderEventParam);
|
||||
|
||||
for (GLVertexBuffer vbo : this.vbosTransparent)
|
||||
if (slice != null && slice.length != 0)
|
||||
{
|
||||
if (vbo == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (vbo.getVertexCount() == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
hasRendered = true;
|
||||
renderContext.drawVbo(vbo);
|
||||
//LodRenderer.tickLogger.info("Vertex buffer: {}", vbo);
|
||||
renderContext.drawVbo(SharedVbo.TRANSPARENT, slice);
|
||||
}
|
||||
}
|
||||
catch (IllegalStateException e)
|
||||
{
|
||||
LOGGER.error("renderContext program doesn't exist for pos: "+this.pos, e);
|
||||
}
|
||||
|
||||
return hasRendered;
|
||||
}
|
||||
|
||||
|
||||
@@ -323,49 +267,26 @@ public class ColumnRenderBuffer implements AutoCloseable
|
||||
//==============//
|
||||
|
||||
/** can be used when debugging */
|
||||
public boolean hasNonNullVbos() { return this.vbos != null || this.vbosTransparent != null; }
|
||||
public boolean hasNonNullVbos() { return false; } //this.vbos != null || this.vbosTransparent != null; }
|
||||
|
||||
/** can be used when debugging */
|
||||
public int vboBufferCount()
|
||||
public int bufferSliceCount()
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
if (this.vbos != null)
|
||||
if (this.opaqueBufferSlices != null)
|
||||
{
|
||||
count += this.vbos.length;
|
||||
count += this.opaqueBufferSlices.length;
|
||||
}
|
||||
|
||||
if (this.vbosTransparent != null)
|
||||
|
||||
if (this.transparentBufferSlices != null)
|
||||
{
|
||||
count += this.vbosTransparent.length;
|
||||
count += this.transparentBufferSlices.length;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public void debugDumpStats(StatsMap statsMap)
|
||||
{
|
||||
statsMap.incStat("RenderBuffers");
|
||||
statsMap.incStat("SimpleRenderBuffers");
|
||||
for (GLVertexBuffer vertexBuffer : vbos)
|
||||
{
|
||||
if (vertexBuffer != null)
|
||||
{
|
||||
statsMap.incStat("VBOs");
|
||||
if (vertexBuffer.getSize() == FULL_SIZED_BUFFER)
|
||||
{
|
||||
statsMap.incStat("FullsizedVBOs");
|
||||
}
|
||||
|
||||
if (vertexBuffer.getSize() == 0)
|
||||
{
|
||||
GLProxy.GL_LOGGER.warn("VBO with size 0");
|
||||
}
|
||||
statsMap.incBytesStat("TotalUsage", vertexBuffer.getSize());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called when object is no longer in use.
|
||||
* Called either after uploadBuffers() returned false (On buffer Upload
|
||||
@@ -377,24 +298,8 @@ public class ColumnRenderBuffer implements AutoCloseable
|
||||
{
|
||||
this.buffersUploaded = false;
|
||||
|
||||
GLProxy.getInstance().queueRunningOnRenderThread(() ->
|
||||
{
|
||||
for (GLVertexBuffer buffer : this.vbos)
|
||||
{
|
||||
if (buffer != null)
|
||||
{
|
||||
buffer.destroyAsync();
|
||||
}
|
||||
}
|
||||
|
||||
for (GLVertexBuffer buffer : this.vbosTransparent)
|
||||
{
|
||||
if (buffer != null)
|
||||
{
|
||||
buffer.destroyAsync();
|
||||
}
|
||||
}
|
||||
});
|
||||
SharedVbo.OPAQUE.deallocateChunks(this.opaqueBufferSlices);
|
||||
SharedVbo.TRANSPARENT.deallocateChunks(this.transparentBufferSlices);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-28
@@ -333,32 +333,4 @@ public class ColumnRenderBufferBuilder
|
||||
quadBuilder.finalizeData();
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=================//
|
||||
// vbo interaction //
|
||||
//=================//
|
||||
|
||||
public static GLVertexBuffer[] resizeBuffer(GLVertexBuffer[] vbos, int newSize)
|
||||
{
|
||||
if (vbos.length == newSize)
|
||||
{
|
||||
return vbos;
|
||||
}
|
||||
|
||||
GLVertexBuffer[] newVbos = new GLVertexBuffer[newSize];
|
||||
System.arraycopy(vbos, 0, newVbos, 0, Math.min(vbos.length, newSize));
|
||||
if (newSize < vbos.length)
|
||||
{
|
||||
for (int i = newSize; i < vbos.length; i++)
|
||||
{
|
||||
if (vbos[i] != null)
|
||||
{
|
||||
vbos[i].close();
|
||||
}
|
||||
}
|
||||
}
|
||||
return newVbos;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
package com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding;
|
||||
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
|
||||
import com.seibel.distanthorizons.core.render.glObject.GLProxy;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.coreapi.util.StringUtil;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/** Used to allow multiple {@link ColumnRenderBuffer}'s to be put in a single VBO. */
|
||||
public class SharedVbo
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
public static final SharedVbo OPAQUE = new SharedVbo(1_000_000_000/*1GB*/, 1024 * 1024/*1MB*/);
|
||||
public static final SharedVbo TRANSPARENT = new SharedVbo(1_000_000_000/*1GB*/, 1024 * 1024/*1MB*/);
|
||||
|
||||
|
||||
public final int vboId;
|
||||
|
||||
|
||||
private final int bufferTotalByteSize;
|
||||
/** the length of a single chunk of this VBO in bytes. */
|
||||
private final int chunkByteSize;
|
||||
|
||||
private final Int2ReferenceOpenHashMap<BufferSlice> bufferSliceByStartingIndex = new Int2ReferenceOpenHashMap<>();
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public SharedVbo(int totalSize, int chunkByteSize)
|
||||
{
|
||||
LodUtil.assertTrue(GLProxy.getInstance().runningOnRenderThread(), "Buffer Handler has to be created on the render thread.");
|
||||
|
||||
this.bufferTotalByteSize = totalSize;
|
||||
this.chunkByteSize = chunkByteSize;
|
||||
|
||||
// Generate and bind the VBO
|
||||
this.vboId = GL32.glGenBuffers();
|
||||
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, this.vboId);
|
||||
GL32.glBufferData(GL32.GL_ARRAY_BUFFER, this.bufferTotalByteSize, GL32.GL_DYNAMIC_DRAW);
|
||||
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//============//
|
||||
// allocation //
|
||||
//============//
|
||||
|
||||
@Nullable
|
||||
public SharedVbo.BufferSlice allocateSlice()
|
||||
{
|
||||
// Find the first free chunk
|
||||
for (int startingIndex = 0; startingIndex < this.bufferTotalByteSize; startingIndex += this.chunkByteSize)
|
||||
{
|
||||
// check if this section is free
|
||||
if (!this.bufferSliceByStartingIndex.containsKey(startingIndex))
|
||||
{
|
||||
BufferSlice newSlice = new BufferSlice(startingIndex, this.chunkByteSize);
|
||||
this.bufferSliceByStartingIndex.put(startingIndex, newSlice);
|
||||
return newSlice;
|
||||
}
|
||||
}
|
||||
|
||||
return null; // No free chunk found
|
||||
}
|
||||
public void deallocateChunks(BufferSlice[] slices)
|
||||
{
|
||||
if (slices != null)
|
||||
{
|
||||
for (BufferSlice slice : slices)
|
||||
{
|
||||
if (slice != null)
|
||||
{
|
||||
this.bufferSliceByStartingIndex.remove(slice.startIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public void deallocateChunk(BufferSlice chunk)
|
||||
{
|
||||
if (chunk != null)
|
||||
{
|
||||
this.bufferSliceByStartingIndex.remove(chunk.startIndex);
|
||||
}
|
||||
}
|
||||
public void clear() { this.bufferSliceByStartingIndex.clear(); }
|
||||
|
||||
|
||||
|
||||
//=================//
|
||||
// buffer handling //
|
||||
//=================//
|
||||
|
||||
public void updateBuffer(BufferSlice chunk, ByteBuffer buffer)
|
||||
{
|
||||
int size = buffer.limit() - buffer.position();
|
||||
if (size > chunk.length)
|
||||
{
|
||||
// if this was fired that means we didn't split up the buffer into the right size
|
||||
// if this isn't stopped the buffer will overwrite an adjacent section and cause incorrect rendering
|
||||
throw new RuntimeException("Programmer error: Uploaded buffer bigger than the allocated area. Allocated: ["+chunk.length+"], buffer: ["+size+"]");
|
||||
}
|
||||
|
||||
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, this.vboId);
|
||||
GL32.glBufferSubData(GL32.GL_ARRAY_BUFFER, chunk.startIndex, buffer);
|
||||
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, 0);
|
||||
chunk.vertexCount = size / LodUtil.LOD_VERTEX_FORMAT.getByteSize();
|
||||
}
|
||||
|
||||
public void bind() { GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, this.vboId); }
|
||||
public void unbind() { GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, 0); }
|
||||
|
||||
|
||||
|
||||
//=======//
|
||||
// debug //
|
||||
//=======//
|
||||
|
||||
public String getDebugMenuString()
|
||||
{
|
||||
long maxChunkCount = (this.bufferTotalByteSize / this.chunkByteSize);
|
||||
long chunkCount = 0;
|
||||
long allocatedBytes = 0;
|
||||
|
||||
for (BufferSlice slice : this.bufferSliceByStartingIndex.values())
|
||||
{
|
||||
allocatedBytes += slice.length;
|
||||
chunkCount++;
|
||||
}
|
||||
|
||||
return "Slices: ["+F3Screen.NUMBER_FORMAT.format(chunkCount)+"/"+F3Screen.NUMBER_FORMAT.format(maxChunkCount)+"], " +
|
||||
"Mem: ["+StringUtil.convertByteCountToHumanReadableSI(allocatedBytes)+"/"+StringUtil.convertByteCountToHumanReadableSI(this.bufferTotalByteSize)+"]";
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// helper classes //
|
||||
//================//
|
||||
|
||||
/** represents a single allocated slice of the parent VBO */
|
||||
public static class BufferSlice
|
||||
{
|
||||
public final int startIndex;
|
||||
public final int length;
|
||||
|
||||
public int vertexCount;
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public BufferSlice(int startIndex, int length)
|
||||
{
|
||||
this.startIndex = startIndex;
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,6 +20,7 @@
|
||||
package com.seibel.distanthorizons.core.logging.f3;
|
||||
|
||||
import com.seibel.distanthorizons.core.api.internal.SharedApi;
|
||||
import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.SharedVbo;
|
||||
import com.seibel.distanthorizons.core.level.IDhLevel;
|
||||
import com.seibel.distanthorizons.core.render.RenderBufferHandler;
|
||||
import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer;
|
||||
@@ -111,6 +112,11 @@ public class F3Screen
|
||||
messageList.add(genericRenderer.getVboRenderDebugMenuString());
|
||||
}
|
||||
}
|
||||
messageList.add("");
|
||||
// GPU memory
|
||||
messageList.add("GPU Opaque " + SharedVbo.OPAQUE.getDebugMenuString());
|
||||
messageList.add("GPU Transp " + SharedVbo.TRANSPARENT.getDebugMenuString());
|
||||
messageList.add("");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@ import com.seibel.distanthorizons.core.pos.DhBlockPos2D;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.render.renderer.DebugRenderer;
|
||||
import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable;
|
||||
import com.seibel.distanthorizons.core.sql.repo.BeaconBeamRepo;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.util.ThreadUtil;
|
||||
import com.seibel.distanthorizons.core.util.objects.quadTree.QuadNode;
|
||||
@@ -647,7 +646,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
|
||||
}
|
||||
else if (renderSection.renderBuffer.hasNonNullVbos())
|
||||
{
|
||||
if (renderSection.renderBuffer.vboBufferCount() != 0)
|
||||
if (renderSection.renderBuffer.bufferSliceCount() != 0)
|
||||
{
|
||||
color = Color.GREEN;
|
||||
}
|
||||
|
||||
+16
-23
@@ -25,6 +25,7 @@ import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiShader
|
||||
import com.seibel.distanthorizons.api.methods.events.abstractEvents.*;
|
||||
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.SharedVbo;
|
||||
import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.ColumnRenderBuffer;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
@@ -35,11 +36,11 @@ import com.seibel.distanthorizons.core.render.DhApiRenderProxy;
|
||||
import com.seibel.distanthorizons.core.render.RenderBufferHandler;
|
||||
import com.seibel.distanthorizons.core.render.glObject.GLProxy;
|
||||
import com.seibel.distanthorizons.core.render.glObject.GLState;
|
||||
import com.seibel.distanthorizons.core.render.glObject.buffer.GLVertexBuffer;
|
||||
import com.seibel.distanthorizons.core.render.glObject.buffer.QuadElementBuffer;
|
||||
import com.seibel.distanthorizons.core.render.glObject.texture.*;
|
||||
import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer;
|
||||
import com.seibel.distanthorizons.core.render.renderer.shaders.*;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
|
||||
@@ -496,31 +497,23 @@ public class LodRenderer
|
||||
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeBufferRenderEvent.class, new DhApiBeforeBufferRenderEvent.EventParam(renderEventParam, modelPos));
|
||||
}
|
||||
|
||||
public void drawVbo(GLVertexBuffer vbo)
|
||||
public void drawVbo(SharedVbo sharedVbo, SharedVbo.BufferSlice slice)
|
||||
{
|
||||
//// can be uncommented to add additional debug validation to prevent crashes if invalid buffers are being created
|
||||
//// shouldn't be used in production due to the performance hit
|
||||
//if (GL32.glIsBuffer(vbo.getId()))
|
||||
IDhApiShaderProgram shaderProgram = this.lodRenderProgram;
|
||||
IDhApiShaderProgram shaderProgramOverride = OverrideInjector.INSTANCE.get(IDhApiShaderProgram.class);
|
||||
if (shaderProgramOverride != null && shaderProgram.overrideThisFrame())
|
||||
{
|
||||
IDhApiShaderProgram shaderProgram = this.lodRenderProgram;
|
||||
IDhApiShaderProgram shaderProgramOverride = OverrideInjector.INSTANCE.get(IDhApiShaderProgram.class);
|
||||
if (shaderProgramOverride != null && shaderProgram.overrideThisFrame())
|
||||
{
|
||||
shaderProgram = shaderProgramOverride;
|
||||
}
|
||||
|
||||
|
||||
vbo.bind();
|
||||
shaderProgram.bindVertexBuffer(vbo.getId());
|
||||
GL32.glDrawElements(GL32.GL_TRIANGLES, (vbo.getVertexCount() / 4) * 6, // TODO what does the 4 and 6 here represent?
|
||||
this.quadIBO.getType(), 0);
|
||||
vbo.unbind();
|
||||
shaderProgram = shaderProgramOverride;
|
||||
}
|
||||
//else
|
||||
//{
|
||||
// // will spam the log if uncommented, but helpful for validation
|
||||
// //LOGGER.warn("Unable to draw VBO: "+vbo.getId());
|
||||
//}
|
||||
|
||||
|
||||
sharedVbo.bind();
|
||||
shaderProgram.bindVertexBuffer(sharedVbo.vboId);
|
||||
|
||||
int vertexCount = (slice.vertexCount / 4) * 6; // 4 vertices per quad, 6 indices per quad
|
||||
int startingVertexIndex = slice.startIndex / LodUtil.LOD_VERTEX_FORMAT.getByteSize(); // Adjust base vertex calculation
|
||||
GL32.glDrawElementsBaseVertex(GL32.GL_TRIANGLES, vertexCount, this.quadIBO.getType(), 0, startingVertexIndex);
|
||||
sharedVbo.unbind();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -23,12 +23,11 @@ import com.seibel.distanthorizons.api.enums.config.EDhApiDataCompressionMode;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
||||
import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO;
|
||||
import com.seibel.distanthorizons.core.sql.repo.FullDataSourceV2Repo;
|
||||
import com.seibel.distanthorizons.coreapi.util.StringUtil;
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||
import org.junit.Assert;
|
||||
|
||||
import java.io.*;
|
||||
import java.text.CharacterIterator;
|
||||
import java.text.StringCharacterIterator;
|
||||
|
||||
/**
|
||||
* <strong>Note:</strong>
|
||||
@@ -356,10 +355,10 @@ public class CompressionTest
|
||||
System.out.println("\n");
|
||||
System.out.println("Results: " + compressorName);
|
||||
System.out.println();
|
||||
System.out.println("Total uncompressed data: [" + humanReadableByteCountSI(totalUncompressedFileSizeInBytes) + "] Total compressed data: [" + humanReadableByteCountSI(totalCompressedFileSizeInBytes) + "]. Compression ratio: [" + compressionRatioString + "].");
|
||||
System.out.println("Min uncompressed data: [" + humanReadableByteCountSI(minUncompressedDtoSizeInBytes) + "] Min compressed data: [" + humanReadableByteCountSI(minCompressedDtoSizeInBytes) + "].");
|
||||
System.out.println("Max uncompressed data: [" + humanReadableByteCountSI(maxUncompressedDtoSizeInBytes) + "] Max compressed data: [" + humanReadableByteCountSI(maxCompressedDtoSizeInBytes) + "].");
|
||||
System.out.println("Avg uncompressed data: [" + humanReadableByteCountSI(avgUncompressedDtoSizeInBytes) + "] Avg compressed data: [" + humanReadableByteCountSI(avgCompressedDtoSizeInBytes) + "].");
|
||||
System.out.println("Total uncompressed data: [" + StringUtil.convertByteCountToHumanReadableSI(totalUncompressedFileSizeInBytes) + "] Total compressed data: [" + StringUtil.convertByteCountToHumanReadableSI(totalCompressedFileSizeInBytes) + "]. Compression ratio: [" + compressionRatioString + "].");
|
||||
System.out.println("Min uncompressed data: [" + StringUtil.convertByteCountToHumanReadableSI(minUncompressedDtoSizeInBytes) + "] Min compressed data: [" + StringUtil.convertByteCountToHumanReadableSI(minCompressedDtoSizeInBytes) + "].");
|
||||
System.out.println("Max uncompressed data: [" + StringUtil.convertByteCountToHumanReadableSI(maxUncompressedDtoSizeInBytes) + "] Max compressed data: [" + StringUtil.convertByteCountToHumanReadableSI(maxCompressedDtoSizeInBytes) + "].");
|
||||
System.out.println("Avg uncompressed data: [" + StringUtil.convertByteCountToHumanReadableSI(avgUncompressedDtoSizeInBytes) + "] Avg compressed data: [" + StringUtil.convertByteCountToHumanReadableSI(avgCompressedDtoSizeInBytes) + "].");
|
||||
System.out.println();
|
||||
System.out.println("Total read time in MS: [" + totalReadTimeInNano / 1_000_000.0 + "] Average read time per dto: [" + (totalReadTimeInNano / processedDtoCount) / 1_000_000.0 + "]");
|
||||
System.out.println("Total write time in MS: [" + totalWriteTimeInNano / 1_000_000.0 + "] Average write time per dto: [" + (totalWriteTimeInNano / processedDtoCount) / 1_000_000.0 + "]");
|
||||
@@ -373,23 +372,4 @@ public class CompressionTest
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Source:
|
||||
* https://stackoverflow.com/questions/3758606/how-can-i-convert-byte-size-into-a-human-readable-format-in-java#3758880
|
||||
*/
|
||||
public static String humanReadableByteCountSI(long bytes)
|
||||
{
|
||||
if (-1000 < bytes && bytes < 1000)
|
||||
{
|
||||
return bytes + " B";
|
||||
}
|
||||
CharacterIterator ci = new StringCharacterIterator("kMGTPE");
|
||||
while (bytes <= -999_950 || bytes >= 999_950)
|
||||
{
|
||||
bytes /= 1000;
|
||||
ci.next();
|
||||
}
|
||||
return String.format("%.1f %cB", bytes / 1000.0, ci.current());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user