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