diff --git a/src/main/java/com/seibel/lod/core/enums/config/GpuUploadMethod.java b/src/main/java/com/seibel/lod/core/enums/config/GpuUploadMethod.java
index 4d36d676a..63a789f6e 100644
--- a/src/main/java/com/seibel/lod/core/enums/config/GpuUploadMethod.java
+++ b/src/main/java/com/seibel/lod/core/enums/config/GpuUploadMethod.java
@@ -20,7 +20,7 @@
package com.seibel.lod.core.enums.config;
/**
- * Auto, Buffer_Storage, Sub_Data, Buffer_Mapping, Data
+ * Auto, BUFFER_STORAGE_MAPPING, Buffer_Storage, Sub_Data, Buffer_Mapping, Data
*
* @author James Seibel
* @version 12-1-2021
@@ -28,19 +28,23 @@ package com.seibel.lod.core.enums.config;
public enum GpuUploadMethod
{
/** Picks the best option based on the GPU the user has. */
- AUTO,
+ AUTO(false, false),
+
+ /*
+ */
+ BUFFER_STORAGE_MAPPING(true, true),
/**
* Default for NVIDIA if OpenGL 4.5 is supported.
* Fast rendering, no stuttering.
*/
- BUFFER_STORAGE,
+ BUFFER_STORAGE(false, true),
/**
* Backup option for NVIDIA.
* Fast rendering but may stutter when uploading.
*/
- SUB_DATA,
+ SUB_DATA(false, false),
/**
* Default option for AMD/Intel.
@@ -48,12 +52,19 @@ public enum GpuUploadMethod
* Fast rending if in GPU memory, slow if in system memory,
* but won't stutter when uploading.
*/
- BUFFER_MAPPING,
+ BUFFER_MAPPING(true, false),
/**
* Backup option for AMD/Intel.
* Fast rendering but may stutter when uploading.
*/
- DATA,
+ DATA(false, false);
+
+ public final boolean useEarlyMapping;
+ public final boolean useBufferStorage;
+ GpuUploadMethod(boolean useEarlyMapping, boolean useBufferStorage) {
+ this.useEarlyMapping = useEarlyMapping;
+ this.useBufferStorage = useBufferStorage;
+ }
}
\ No newline at end of file
diff --git a/src/main/java/com/seibel/lod/core/objects/RenderRegion.java b/src/main/java/com/seibel/lod/core/objects/RenderRegion.java
index 2385e5ea7..abdb2627e 100644
--- a/src/main/java/com/seibel/lod/core/objects/RenderRegion.java
+++ b/src/main/java/com/seibel/lod/core/objects/RenderRegion.java
@@ -28,7 +28,7 @@ public abstract class RenderRegion implements AutoCloseable
}
return null;
}
-
+
public abstract void uploadBuffers(LodQuadBuilder builder, GpuUploadMethod uploadMethod);
public abstract boolean shouldRender(IMinecraftRenderWrapper renderer, boolean enableDirectionalCulling);
public abstract void render(LodRenderProgram shaderProgram);
diff --git a/src/main/java/com/seibel/lod/core/objects/opengl/LodQuadBuilder.java b/src/main/java/com/seibel/lod/core/objects/opengl/LodQuadBuilder.java
index edeaf8d68..6a74df2d8 100644
--- a/src/main/java/com/seibel/lod/core/objects/opengl/LodQuadBuilder.java
+++ b/src/main/java/com/seibel/lod/core/objects/opengl/LodQuadBuilder.java
@@ -4,9 +4,11 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Iterator;
+import java.util.function.Function;
import com.seibel.lod.core.enums.LodDirection;
import com.seibel.lod.core.enums.LodDirection.Axis;
+import com.seibel.lod.core.enums.config.GpuUploadMethod;
import com.seibel.lod.core.util.ColorUtil;
public class LodQuadBuilder {
@@ -177,6 +179,33 @@ public class LodQuadBuilder {
}
};
}
+
+ public interface BufferFiller {
+ boolean fill(LodVertexBuffer vbo); // If true: means more data is needed to be filled
+ }
+
+ public BufferFiller makeBufferFiller(GpuUploadMethod method) {
+ int numOfBuffers = getCurrentNeededVertexBuffers();
+ return new BufferFiller() {
+ int counter = 0;
+ public boolean fill(LodVertexBuffer vbo) {
+ if (counter >= numOfBuffers) {
+ return false;
+ }
+ int numOfQuads = MAX_QUADS_PER_BUFFER;
+ if (quads.size()-(counter*MAX_QUADS_PER_BUFFER) < MAX_QUADS_PER_BUFFER)
+ numOfQuads = quads.size()-(counter*MAX_QUADS_PER_BUFFER);
+ if (numOfQuads != 0) {
+ ByteBuffer bb = vbo.mapBuffer(numOfQuads*QUAD_BYTE_SIZE, method, MAX_QUADS_PER_BUFFER * QUAD_BYTE_SIZE);
+ if (bb == null) throw new NullPointerException("mapBuffer returned null");
+ writeVertexData(bb, MAX_QUADS_PER_BUFFER * counter++, numOfQuads).rewind();
+ vbo.unmapBuffer(method);
+ }
+ vbo.vertexCount = numOfQuads*6;
+ return counter < numOfBuffers;
+ }
+ };
+ }
public int getCurrentNeededVertexBuffers() {
return quads.size() / MAX_QUADS_PER_BUFFER + 1;
diff --git a/src/main/java/com/seibel/lod/core/objects/opengl/LodVertexBuffer.java b/src/main/java/com/seibel/lod/core/objects/opengl/LodVertexBuffer.java
index 66ddd4634..ccb354463 100644
--- a/src/main/java/com/seibel/lod/core/objects/opengl/LodVertexBuffer.java
+++ b/src/main/java/com/seibel/lod/core/objects/opengl/LodVertexBuffer.java
@@ -70,55 +70,17 @@ public class LodVertexBuffer implements AutoCloseable
GLProxy.getInstance().recordOpenGlCall(() -> GL32.glDeleteBuffers(id));
}
this.id = -1;
+ size = 0;
+ vertexCount = 0;
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();
+ private void _uploadBufferStorage(ByteBuffer bb) {
+ if (!isBufferStorage) throw new IllegalStateException("Buffer is not bufferStorage but its trying to use bufferStorage upload method!");
+ GL32.glDeleteBuffers(id);
+ id = GL32.glGenBuffers();
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);
+ GL44.glBufferStorage(GL32.GL_ARRAY_BUFFER, bb, 0);
}
// bufferData
@@ -148,13 +110,15 @@ public class LodVertexBuffer implements AutoCloseable
public void uploadBuffer(ByteBuffer bb, int vertCount, GpuUploadMethod uploadMethod, int maxExpensionSize) {
if (vertCount < 0) throw new IllegalArgumentException("VertCount is negative!");
+ if (uploadMethod.useEarlyMapping)
+ throw new IllegalArgumentException("UploadMethod signal that this should use Mapping instead of uploadBuffer!");
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;
+ boolean useBuffStorage = uploadMethod.useBufferStorage;
if (useBuffStorage != isBufferStorage) {
_destroy();
_create(useBuffStorage);
@@ -163,11 +127,8 @@ public class LodVertexBuffer implements AutoCloseable
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);
+ _uploadBufferStorage(bb);
break;
case DATA:
_uploadData(bb);
@@ -198,4 +159,49 @@ public class LodVertexBuffer implements AutoCloseable
}
}
+ private boolean isMapped = false;
+
+ public ByteBuffer mapBuffer(int targetSize, GpuUploadMethod uploadMethod, int maxExpensionSize)
+ {
+ if (targetSize == 0) throw new IllegalArgumentException("MapBuffer targetSize is 0!");
+ if (!uploadMethod.useEarlyMapping) throw new IllegalStateException("Upload method must be one that use mappings in order to call mapBuffer!");
+ if (isMapped) throw new IllegalStateException("Map Buffer called but buffer is already mapped!");
+ boolean useBuffStorage = uploadMethod.useBufferStorage;
+ if (useBuffStorage != isBufferStorage) {
+ _destroy();
+ _create(useBuffStorage);
+ }
+
+ ByteBuffer vboBuffer;
+
+ GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, id);
+
+ if (size < targetSize || size > targetSize * BUFFER_EXPANSION_MULTIPLIER * BUFFER_EXPANSION_MULTIPLIER) {
+ int newSize = (int) (targetSize * BUFFER_EXPANSION_MULTIPLIER);
+ if (newSize > maxExpensionSize) newSize = maxExpensionSize;
+ size = newSize;
+ if (uploadMethod.useBufferStorage) {
+ 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);
+ } else {
+ GL32.glBufferData(GL32.GL_ARRAY_BUFFER, newSize, GL32.GL_STATIC_DRAW);
+ }
+ }
+
+ vboBuffer = GL32.glMapBufferRange(GL32.GL_ARRAY_BUFFER, 0, targetSize,
+ GL32.GL_MAP_WRITE_BIT | GL32.GL_MAP_UNSYNCHRONIZED_BIT | GL32.GL_MAP_INVALIDATE_BUFFER_BIT);
+ isMapped = true;
+ return vboBuffer;
+ }
+
+ public void unmapBuffer(GpuUploadMethod uploadMethod)
+ {
+ if (!uploadMethod.useEarlyMapping) throw new IllegalStateException("Upload method must be one that use mappings in order to call unmapBuffer!");
+ if (!isMapped) throw new IllegalStateException("Unmap Buffer called but buffer is already not mapped!");
+ GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, id);
+ GL32.glUnmapBuffer(GL32.GL_ARRAY_BUFFER);
+ isMapped = false;
+ }
}
\ No newline at end of file
diff --git a/src/main/java/com/seibel/lod/core/objects/opengl/SimpleRenderRegion.java b/src/main/java/com/seibel/lod/core/objects/opengl/SimpleRenderRegion.java
index 1607a302f..9e0aba044 100644
--- a/src/main/java/com/seibel/lod/core/objects/opengl/SimpleRenderRegion.java
+++ b/src/main/java/com/seibel/lod/core/objects/opengl/SimpleRenderRegion.java
@@ -10,6 +10,7 @@ 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.objects.opengl.LodQuadBuilder.BufferFiller;
import com.seibel.lod.core.render.LodRenderProgram;
import com.seibel.lod.core.render.RenderUtil;
import com.seibel.lod.core.util.LodUtil;
@@ -68,19 +69,35 @@ public class SimpleRenderRegion extends RenderRegion {
}
return vbos[iIndex];
}
+
+ private void uploadBuffersViaMapping(LodQuadBuilder builder, GpuUploadMethod uploadMethod)
+ {
+ resize(builder.getCurrentNeededVertexBuffers());
+ for (int i=0; i iter = builder.makeVertexBuffers();
while (iter.hasNext()) {
ByteBuffer bb = iter.next();
- LodVertexBuffer vbo = getOrMakeVbo(i++, uploadMethod==GpuUploadMethod.BUFFER_STORAGE);
+ LodVertexBuffer vbo = getOrMakeVbo(i++, uploadMethod.useBufferStorage);
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