diff --git a/src/main/java/com/seibel/lod/core/builders/lodBuilding/bufferBuilding/LodBufferBuilderFactory.java b/src/main/java/com/seibel/lod/core/builders/lodBuilding/bufferBuilding/LodBufferBuilderFactory.java index 31b984f93..c54dd9649 100644 --- a/src/main/java/com/seibel/lod/core/builders/lodBuilding/bufferBuilding/LodBufferBuilderFactory.java +++ b/src/main/java/com/seibel/lod/core/builders/lodBuilding/bufferBuilding/LodBufferBuilderFactory.java @@ -92,8 +92,11 @@ public class LodBufferBuilderFactory { * when need be to fit the larger sizes. */ public static final int DEFAULT_MEMORY_ALLOCATION = (LodUtil.LOD_VERTEX_FORMAT.getByteSize() * 3) * 8; + public static final int QUADS_BYTE_SIZE = LodUtil.LOD_VERTEX_FORMAT.getByteSize() * (LodRenderer.ENABLE_IBO ? 4 : 6); public static final int MAX_TRIANGLES_PER_BUFFER = (1024 * 1024 * 1) / (LodUtil.LOD_VERTEX_FORMAT.getByteSize() * 3); + public static final int MAX_QUADS_PER_BUFFER = (1024 * 1024 * 1) / QUADS_BYTE_SIZE; + public static final int FULL_SIZED_BUFFER = MAX_QUADS_PER_BUFFER * QUADS_BYTE_SIZE; public static int skyLightPlayer = 15; diff --git a/src/main/java/com/seibel/lod/core/builders/lodBuilding/bufferBuilding/LodQuadBuilder.java b/src/main/java/com/seibel/lod/core/builders/lodBuilding/bufferBuilding/LodQuadBuilder.java index 1d91cc3fb..05b9ddf0a 100644 --- a/src/main/java/com/seibel/lod/core/builders/lodBuilding/bufferBuilding/LodQuadBuilder.java +++ b/src/main/java/com/seibel/lod/core/builders/lodBuilding/bufferBuilding/LodQuadBuilder.java @@ -30,7 +30,8 @@ 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.handlers.dependencyInjection.SingletonHandler; -import com.seibel.lod.core.objects.opengl.LodVertexBuffer; +import com.seibel.lod.core.render.LodRenderer; +import com.seibel.lod.core.render.objects.GLVertexBuffer; import com.seibel.lod.core.util.ColorUtil; import com.seibel.lod.core.util.LodUtil; import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton; @@ -44,10 +45,6 @@ import static com.seibel.lod.core.render.LodRenderer.EVENT_LOGGER; */ public class LodQuadBuilder { - static final int MAX_BUFFER_SIZE = (1024 * 1024); - static final int QUAD_BYTE_SIZE = (12 * 4); - static final int MAX_QUADS_PER_BUFFER = MAX_BUFFER_SIZE / QUAD_BYTE_SIZE; - static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class); public final boolean skipQuadsWithZeroSkylight; @@ -55,7 +52,7 @@ public class LodQuadBuilder final ArrayList[] quads = (ArrayList[]) new ArrayList[6]; - public static final int[][][] DIRECTION_VERTEX_QUAD = new int[][][] + public static final int[][][] DIRECTION_VERTEX_IBO_QUAD = new int[][][] { // X,Z // { // UP @@ -103,7 +100,68 @@ public class LodQuadBuilder { 0, 0 }, // 3 }, }; - + public static final int[][][] DIRECTION_VERTEX_QUAD = new int[][][] + { + // X,Z // + { // UP + { 1, 0 }, // 0 + { 1, 1 }, // 1 + { 0, 1 }, // 2 + + { 1, 0 }, // 0 + { 0, 1 }, // 2 + { 0, 0 }, // 3 + }, + { // DOWN + { 0, 0 }, // 0 + { 0, 1 }, // 1 + { 1, 1 }, // 2 + + { 0, 0 }, // 0 + { 1, 1 }, // 2 + { 1, 0 }, // 3 + }, + + // X,Y // + { // NORTH + { 0, 0 }, // 0 + { 0, 1 }, // 1 + { 1, 1 }, // 2 + + { 0, 0 }, // 0 + { 1, 1 }, // 2 + { 1, 0 }, // 3 + }, + { // SOUTH + { 1, 0 }, // 0 + { 1, 1 }, // 1 + { 0, 1 }, // 2 + + { 1, 0 }, // 0 + { 0, 1 }, // 2 + { 0, 0 }, // 3 + }, + + // Z,Y // + { // WEST + { 0, 0 }, // 0 + { 1, 0 }, // 1 + { 1, 1 }, // 2 + + { 0, 0 }, // 0 + { 1, 1 }, // 2 + { 0, 1 }, // 3 + }, + { // EAST + { 0, 1 }, // 0 + { 1, 1 }, // 1 + { 1, 0 }, // 2 + + { 0, 1 }, // 0 + { 1, 0 }, // 2 + { 0, 0 }, // 3 + }, + }; public LodQuadBuilder(boolean enableSkylightCulling, int skyLightCullingBelow) @@ -198,7 +256,7 @@ public class LodQuadBuilder private static void putQuad(ByteBuffer bb, BufferQuad quad) { - int[][] quadBase = DIRECTION_VERTEX_QUAD[quad.direction.ordinal()]; + int[][] quadBase = LodRenderer.ENABLE_IBO ? DIRECTION_VERTEX_IBO_QUAD[quad.direction.ordinal()] : DIRECTION_VERTEX_QUAD[quad.direction.ordinal()]; short widthEastWest = quad.widthEastWest; short widthNorthSouth = quad.widthNorthSouthOrUpDown; Axis axis = quad.direction.getAxis(); @@ -290,7 +348,7 @@ public class LodQuadBuilder { return new Iterator() { - final ByteBuffer bb = ByteBuffer.allocateDirect(MAX_QUADS_PER_BUFFER * QUAD_BYTE_SIZE) + final ByteBuffer bb = ByteBuffer.allocateDirect(LodBufferBuilderFactory.FULL_SIZED_BUFFER) .order(ByteOrder.nativeOrder()); int dir = skipEmpty(0); int quad = 0; @@ -316,7 +374,7 @@ public class LodQuadBuilder return null; } bb.clear(); - bb.limit(MAX_QUADS_PER_BUFFER * QUAD_BYTE_SIZE); + bb.limit(LodBufferBuilderFactory.FULL_SIZED_BUFFER); while (bb.hasRemaining() && dir < 6) { writeData(); @@ -355,7 +413,7 @@ public class LodQuadBuilder public interface BufferFiller { /** If true: more data needs to be filled */ - boolean fill(LodVertexBuffer vbo); + boolean fill(GLVertexBuffer vbo); } public BufferFiller makeBufferFiller(GpuUploadMethod method) @@ -365,34 +423,35 @@ public class LodQuadBuilder int dir = 0; int quad = 0; - public boolean fill(LodVertexBuffer vbo) + public boolean fill(GLVertexBuffer vbo) { if (dir >= 6) { - vbo.vertexCount = 0; + vbo.setVertexCount(0); return false; } int numOfQuads = _countRemainingQuads(); - if (numOfQuads > MAX_QUADS_PER_BUFFER) - numOfQuads = MAX_QUADS_PER_BUFFER; + if (numOfQuads > LodBufferBuilderFactory.MAX_QUADS_PER_BUFFER) + numOfQuads = LodBufferBuilderFactory.MAX_QUADS_PER_BUFFER; if (numOfQuads == 0) { - vbo.vertexCount = 0; + vbo.setVertexCount(0); return false; } - ByteBuffer bb = vbo.mapBuffer(numOfQuads * QUAD_BYTE_SIZE, method, MAX_QUADS_PER_BUFFER * QUAD_BYTE_SIZE); + ByteBuffer bb = vbo.mapBuffer(numOfQuads * LodBufferBuilderFactory.QUADS_BYTE_SIZE, method, + LodBufferBuilderFactory.FULL_SIZED_BUFFER); if (bb == null) throw new NullPointerException("mapBuffer returned null"); bb.clear(); - bb.limit(numOfQuads * QUAD_BYTE_SIZE); + bb.limit(numOfQuads * LodBufferBuilderFactory.QUADS_BYTE_SIZE); while (bb.hasRemaining() && dir < 6) { writeData(bb); } bb.rewind(); - vbo.unmapBuffer(method); - vbo.vertexCount = numOfQuads * 4; + vbo.unmapBuffer(); + vbo.setVertexCount(LodRenderer.ENABLE_IBO ? numOfQuads*4 : numOfQuads*6); return dir < 6; } @@ -444,11 +503,11 @@ public class LodQuadBuilder i += qs.size(); return i; } - + /** Returns how many Buffers will be needed to render everything in this builder. */ public int getCurrentNeededVertexBufferCount() { - return LodUtil.ceilDiv(getCurrentQuadsCount(), MAX_QUADS_PER_BUFFER); + return LodUtil.ceilDiv(getCurrentQuadsCount(), LodBufferBuilderFactory.MAX_QUADS_PER_BUFFER); } - + } 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 deleted file mode 100644 index 8adbec1e5..000000000 --- a/src/main/java/com/seibel/lod/core/objects/opengl/LodVertexBuffer.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * This file is part of the Distant Horizons mod (formerly the LOD Mod), - * licensed under the GNU GPL v3 License. - * - * Copyright (C) 2020-2022 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.core.objects.opengl; - -import java.nio.ByteBuffer; - -import com.seibel.lod.core.api.ApiShared; -import com.seibel.lod.core.util.UnitBytes; -import org.lwjgl.opengl.GL32; -import org.lwjgl.opengl.GL44; - -import com.seibel.lod.core.enums.config.GpuUploadMethod; -import com.seibel.lod.core.enums.rendering.GLProxyContext; -import com.seibel.lod.core.render.GLProxy; - -/** - * This is a container for a OpenGL - * VBO (Vertex Buffer Object). - * - * @author James Seibel - * @version 11-20-2021 - */ -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 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 = asBufferStorage; - 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; - size = 0; - vertexCount = 0; - count--; - } - - private void _uploadBufferStorage(ByteBuffer bb) { - if (!isBufferStorage) throw new IllegalStateException("Buffer is not bufferStorage but its trying to use bufferStorage upload method!"); - int bbSize = bb.limit() - bb.position(); - GL32.glDeleteBuffers(id); - id = GL32.glGenBuffers(); - GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, id); - GL44.glBufferStorage(GL32.GL_ARRAY_BUFFER, bb, 0); - size = bbSize; - } - - // 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!"); - 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+"!"); - GLProxy.GL_LOGGER.debug("Uploading {} buffer with {} vertices.", new UnitBytes(bbSize), vertCount); - // If size is zero, just ignore it. - if (bbSize == 0) return; - boolean useBuffStorage = uploadMethod.useBufferStorage; - if (useBuffStorage != isBufferStorage) { - _destroy(); - _create(useBuffStorage); - } - switch (uploadMethod) { - case AUTO: - throw new IllegalArgumentException("GpuUploadMethod AUTO must be resolved before call to uploadBuffer()!"); - case BUFFER_STORAGE: - _uploadBufferStorage(bb); - break; - case DATA: - _uploadData(bb); - break; - case SUB_DATA: - _uploadSubData(bb, maxExpensionSize); - break; - default: - throw new IllegalArgumentException("Invalid GpuUploadMethod enum"); - } - } - - - @Override - public void close() - { - if (this.id >= 0) - { - _destroy(); - if (count==0) ApiShared.LOGGER.info("All LodVerrtexBuffer is freed."); - } else { - ApiShared.LOGGER.error("LodVertexBuffer double close!"); - - } - } - 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; - } - - @Override - public String toString() { - return (isBufferStorage ? "VertexBufferStorage" : "BufferStorage")+ - "[vboId:"+id+", size:"+size+", vertCount:"+vertexCount+(isMapped?", MAPPED" : "")+"]"; - } -} \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/core/objects/opengl/QuadIBO.java b/src/main/java/com/seibel/lod/core/objects/opengl/QuadIBO.java deleted file mode 100644 index 2ca650ef1..000000000 --- a/src/main/java/com/seibel/lod/core/objects/opengl/QuadIBO.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * This file is part of the Distant Horizons mod (formerly the LOD Mod), - * licensed under the GNU GPL v3 License. - * - * Copyright (C) 2020-2022 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.core.objects.opengl; - -import com.seibel.lod.core.api.ApiShared; -import org.lwjgl.system.MemoryUtil; - -import static org.lwjgl.opengl.GL11C.GL_UNSIGNED_INT; -import static org.lwjgl.opengl.GL15.*; -import static org.lwjgl.opengl.GL31.GL_COPY_WRITE_BUFFER; - -/** - * Represents a OpenGL Index Buffer Object. - * - * @author Cotex - * @version 4-13-2022 - */ -public class QuadIBO -{ - /** - * Datatype of the stored indices - * (can be GL_UNSIGNED_INT, GL_UNSIGNED_SHORT, GL_UNSIGNED_BYTE) - */ - public int type; - - /** OpenGL ID of this IBO object */ - int id; - - /** Current capacity (in quads) */ - int currentQuadCapacity; - - /** Global IBO object, used for sharing the IBO for any draw calls */ - public static QuadIBO GLOBAL = new QuadIBO(); - - - - public QuadIBO() - { - id = glGenBuffers(); - } - - - /** Should only be called on a thread that has a OpenGL context. */ - public void resizeIfNecessary(int newQuadCapacity) - { - //If the requested capacity is less than or equal to current capacity, ignore - if (newQuadCapacity <= currentQuadCapacity) - return; - - // create the new capacity bigger than necessary to prevent constant updates - newQuadCapacity *= 1.5; - ApiShared.LOGGER.info("Quad IBO Resizing from [" + currentQuadCapacity + "] to [" + newQuadCapacity + "]"); - - currentQuadCapacity = newQuadCapacity; - - //TODO: DO DYNAMIC TYPES, just makes things more efficient - type = GL_UNSIGNED_INT; - int DT_SIZE = 4; //Datatype size (int: 4, short: 2, byte: 1) - - glBindBuffer(GL_COPY_WRITE_BUFFER, id); - //Resize the buffer - glBufferData(GL_COPY_WRITE_BUFFER, (long) DT_SIZE * 6 * newQuadCapacity, GL_STATIC_DRAW);// 4L is datatype - //Map and write the index data to the buffer - long arrayPointer = nglMapBuffer(GL_COPY_WRITE_BUFFER, GL_WRITE_ONLY); - for (int base = 0; base < newQuadCapacity; base++) - { - // Add the new quad's indices - MemoryUtil.memPutInt(arrayPointer + (base * 6L * DT_SIZE + DT_SIZE * 0), (int) (base * 4 + 0)); - MemoryUtil.memPutInt(arrayPointer + (base * 6L * DT_SIZE + DT_SIZE * 1), (int) (base * 4 + 1)); - MemoryUtil.memPutInt(arrayPointer + (base * 6L * DT_SIZE + DT_SIZE * 2), (int) (base * 4 + 2)); - MemoryUtil.memPutInt(arrayPointer + (base * 6L * DT_SIZE + DT_SIZE * 3), (int) (base * 4 + 2)); - MemoryUtil.memPutInt(arrayPointer + (base * 6L * DT_SIZE + DT_SIZE * 4), (int) (base * 4 + 3)); - MemoryUtil.memPutInt(arrayPointer + (base * 6L * DT_SIZE + DT_SIZE * 5), (int) (base * 4 + 0)); - } - glUnmapBuffer(GL_COPY_WRITE_BUFFER); - } - - /** Binds the IBO */ - public void bind() - { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, id); - } - - /** Unbinds the IBO */ - public void unbind() - { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - } -} diff --git a/src/main/java/com/seibel/lod/core/objects/opengl/SimpleRenderBuffer.java b/src/main/java/com/seibel/lod/core/objects/opengl/SimpleRenderBuffer.java index a0bd14e04..a27dc8428 100644 --- a/src/main/java/com/seibel/lod/core/objects/opengl/SimpleRenderBuffer.java +++ b/src/main/java/com/seibel/lod/core/objects/opengl/SimpleRenderBuffer.java @@ -23,7 +23,10 @@ import java.nio.ByteBuffer; import java.util.Iterator; import java.util.concurrent.TimeUnit; +import com.seibel.lod.core.api.ClientApi; import com.seibel.lod.core.builders.lodBuilding.bufferBuilding.LodQuadBuilder; +import com.seibel.lod.core.render.LodRenderer; +import com.seibel.lod.core.render.objects.GLVertexBuffer; import org.lwjgl.opengl.GL32; import com.seibel.lod.core.api.ApiShared; @@ -42,16 +45,14 @@ import static com.seibel.lod.core.render.GLProxy.GL_LOGGER; public class SimpleRenderBuffer extends RenderBuffer { 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; private static final long MAX_BUFFER_UPLOAD_TIMEOUT_NANOSECONDS = 1_000_000; - - LodVertexBuffer[] vbos; + + GLVertexBuffer[] vbos; // public void onReuse() {} public SimpleRenderBuffer() { - vbos = new LodVertexBuffer[0]; + vbos = new GLVertexBuffer[0]; } @Override @@ -74,14 +75,17 @@ public class SimpleRenderBuffer extends RenderBuffer public boolean render(LodRenderProgram shaderProgram) { boolean hasRendered = false; - for (LodVertexBuffer vbo : vbos) { + for (GLVertexBuffer vbo : vbos) { if (vbo == null) continue; - if (vbo.vertexCount == 0) continue; + if (vbo.getVertexCount() == 0) continue; hasRendered = true; - GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, vbo.id); - shaderProgram.bindVertexBuffer(vbo.id); - QuadIBO.GLOBAL.resizeIfNecessary(vbo.vertexCount/4); - GL32.glDrawElements(GL32.GL_TRIANGLES, (vbo.vertexCount/4)*6, QuadIBO.GLOBAL.type, 0); + vbo.bind(); + shaderProgram.bindVertexBuffer(vbo.getId()); + if (LodRenderer.ENABLE_IBO) { + GL32.glDrawElements(GL32.GL_TRIANGLES, (vbo.getVertexCount()/4)*6, ClientApi.renderer.quadIBO.getType(), 0); + } else { + GL32.glDrawArrays(GL32.GL_TRIANGLES, 0, vbo.getVertexCount()); + } //LodRenderer.tickLogger.info("Vertex buffer: {}", vbo); } return hasRendered; @@ -92,14 +96,14 @@ public class SimpleRenderBuffer extends RenderBuffer { statsMap.incStat("RenderBuffers"); statsMap.incStat("SimpleRenderBuffers"); - for (LodVertexBuffer b : vbos) { + for (GLVertexBuffer b : vbos) { if (b == null) continue; statsMap.incStat("VBOs"); - if (b.size == FULL_SIZED_BUFFERS) { + if (b.getSize() == LodBufferBuilderFactory.FULL_SIZED_BUFFER) { statsMap.incStat("FullsizedVBOs"); } - if (b.size == 0) GL_LOGGER.warn("VBO with size 0"); - statsMap.incBytesStat("TotalUsage", b.size); + if (b.getSize() == 0) GL_LOGGER.warn("VBO with size 0"); + statsMap.incBytesStat("TotalUsage", b.getSize()); } } @@ -107,8 +111,8 @@ public class SimpleRenderBuffer extends RenderBuffer public void close() { GLProxy.getInstance().recordOpenGlCall(() -> { - for (LodVertexBuffer b : vbos) { - b.close(); + for (GLVertexBuffer b : vbos) { + b.destroy(false); } }); } @@ -122,10 +126,11 @@ public class SimpleRenderBuffer extends RenderBuffer Iterator iter = builder.makeVertexBuffers(); while (iter.hasNext()) { ByteBuffer bb = iter.next(); - LodVertexBuffer vbo = getOrMakeVbo(i++, method.useBufferStorage); + GLVertexBuffer vbo = getOrMakeVbo(i++, method.useBufferStorage); int size = bb.limit() - bb.position(); try { - vbo.uploadBuffer(bb, size/LodUtil.LOD_VERTEX_FORMAT.getByteSize(), method, FULL_SIZED_BUFFERS); + vbo.bind(); + vbo.uploadBuffer(bb, size/LodUtil.LOD_VERTEX_FORMAT.getByteSize(), method, LodBufferBuilderFactory.FULL_SIZED_BUFFER); } catch (Exception e) { vbos[i-1] = null; vbo.close(); @@ -151,23 +156,23 @@ public class SimpleRenderBuffer extends RenderBuffer { resize(builder.getCurrentNeededVertexBufferCount()); for (int i=0; i size) { for (int i=size; i destroy((false))); + } else { + GL32.glDeleteBuffers(id); + } + id = 0; + size = 0; + count--; + if (count==0) ApiShared.LOGGER.info("All GLBuffer is freed."); + } + + // Requires already binded + protected void uploadBufferStorage(ByteBuffer bb, int bufferStorageHint) { + if (!bufferStorage) throw new IllegalStateException("Buffer is not bufferStorage but its trying to use bufferStorage upload method!"); + int bbSize = bb.limit() - bb.position(); + destroy(false); + create(true); + bind(); + GL44.glBufferStorage(getBufferBindingTarget(), bb, bufferStorageHint); + size = bbSize; + } + + // Requires already binded + protected void uploadBufferData(ByteBuffer bb, int bufferDataHint) { + if (bufferStorage) throw new IllegalStateException("Buffer is bufferStorage but its trying to use Data upload method!"); + int bbSize = bb.limit() - bb.position(); + GL32.glBufferData(getBufferBindingTarget(), bb, bufferDataHint); + size = bbSize; + } + + // Requires already binded + protected void uploadSubData(ByteBuffer bb, int maxExpansionSize, int bufferDataHint) { + if (bufferStorage) throw new IllegalStateException("Buffer is bufferStorage but its trying to use SubData upload method!"); + int bbSize = bb.limit() - bb.position(); + if (size < bbSize || size > bbSize * BUFFER_SHRINK_TRIGGER) { + int newSize = (int) (bbSize * BUFFER_EXPANSION_MULTIPLIER); + if (newSize > maxExpansionSize) newSize = maxExpansionSize; + GL32.glBufferData(getBufferBindingTarget(), newSize, bufferDataHint); + size = newSize; + } + GL32.glBufferSubData(getBufferBindingTarget(), 0, bb); + } + + // Requires already binded + public void uploadBuffer(ByteBuffer bb, GpuUploadMethod uploadMethod, int maxExpansionSize, int bufferHint) { + if (uploadMethod.useEarlyMapping) + throw new IllegalArgumentException("UploadMethod signal that this should use Mapping instead of uploadBuffer!"); + int bbSize = bb.limit()-bb.position(); + if (bbSize > maxExpansionSize) + throw new IllegalArgumentException("maxExpansionSize is "+maxExpansionSize+" but buffer size is "+bbSize+"!"); + GLProxy.GL_LOGGER.debug("Uploading buffer with {}.", new UnitBytes(bbSize)); + // If size is zero, just ignore it. + if (bbSize == 0) return; + boolean useBuffStorage = uploadMethod.useBufferStorage; + if (useBuffStorage != bufferStorage) { + destroy(false); + create(useBuffStorage); + bind(); + } + switch (uploadMethod) { + case AUTO: + throw new IllegalArgumentException("GpuUploadMethod AUTO must be resolved before call to uploadBuffer()!"); + case BUFFER_STORAGE: + uploadBufferStorage(bb, bufferHint); + break; + case DATA: + uploadBufferData(bb, bufferHint); + break; + case SUB_DATA: + uploadSubData(bb, maxExpansionSize, bufferHint); + break; + default: + throw new IllegalArgumentException("Invalid GpuUploadMethod enum"); + } + } + + public ByteBuffer mapBuffer(int targetSize, GpuUploadMethod uploadMethod, int maxExpensionSize, int bufferHint, int mapFlags) { + 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 != bufferStorage) { + destroy(false); + create(useBuffStorage); + } + bind(); + ByteBuffer vboBuffer; + + if (size < targetSize || size > targetSize * BUFFER_SHRINK_TRIGGER) { + int newSize = (int) (targetSize * BUFFER_EXPANSION_MULTIPLIER); + if (newSize > maxExpensionSize) newSize = maxExpensionSize; + size = newSize; + if (bufferStorage) { + GL32.glDeleteBuffers(id); + id = GL32.glGenBuffers(); + GL32.glBindBuffer(getBufferBindingTarget(), id); + GL44.glBufferStorage(getBufferBindingTarget(), newSize, bufferHint); + } else { + GL32.glBufferData(GL32.GL_ARRAY_BUFFER, newSize, bufferHint); + } + } + + vboBuffer = GL32.glMapBufferRange(GL32.GL_ARRAY_BUFFER, 0, targetSize, mapFlags); + isMapped = true; + return vboBuffer; + } + + // Requires already binded + public void unmapBuffer() + { + if (!isMapped) throw new IllegalStateException("Unmap Buffer called but buffer is already not mapped!"); + bind(); + GL32.glUnmapBuffer(getBufferBindingTarget()); + isMapped = false; + } + + @Override + public void close() + { + destroy(true); + } + + @Override + public String toString() { + return (bufferStorage ? "" : "Static-")+ getClass().getSimpleName() + + "[id:"+id+",size:"+size+(isMapped?",MAPPED" : "")+"]"; + } +} diff --git a/src/main/java/com/seibel/lod/core/render/objects/GLElementBuffer.java b/src/main/java/com/seibel/lod/core/render/objects/GLElementBuffer.java new file mode 100644 index 000000000..e57e02b73 --- /dev/null +++ b/src/main/java/com/seibel/lod/core/render/objects/GLElementBuffer.java @@ -0,0 +1,60 @@ +/* + * This file is part of the Distant Horizons mod (formerly the LOD Mod), + * licensed under the GNU GPL v3 License. + * + * Copyright (C) 2020-2022 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.seibel.lod.core.render.objects; + +import com.seibel.lod.core.enums.config.GpuUploadMethod; +import org.lwjgl.opengl.GL32; + +import java.nio.ByteBuffer; + +/** + * This is a container for a OpenGL + * VBO (Vertex Buffer Object). + * + * @author James Seibel + * @version 11-20-2021 + */ +public class GLElementBuffer extends GLBuffer +{ + /** + * When uploading to a buffer that is too small, recreate it this many times + * bigger than the upload payload + */ + protected int indicesCount = 0; + public int getIndicesCount() { return indicesCount; } + protected int type = GL32.GL_UNSIGNED_INT; + public int getType() { return type; } + + public GLElementBuffer(boolean isBufferStorage) + { + super(isBufferStorage); + } + + @Override + public void destroy(boolean async) { + super.destroy(async); + indicesCount = 0; + } + + @Override + public int getBufferBindingTarget() { + return GL32.GL_ELEMENT_ARRAY_BUFFER; + } +} \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/core/render/objects/GLEnums.java b/src/main/java/com/seibel/lod/core/render/objects/GLEnums.java index 445729eb7..391640114 100644 --- a/src/main/java/com/seibel/lod/core/render/objects/GLEnums.java +++ b/src/main/java/com/seibel/lod/core/render/objects/GLEnums.java @@ -204,4 +204,24 @@ public class GLEnums { return "GL_UNKNOWN(" + glEnum + ")"; } + + public static int getTypeSize(int glTypeEnum) { + switch (glTypeEnum) { + case GL_BYTE: + case GL_UNSIGNED_BYTE: + return 1; + case GL_SHORT: + case GL_UNSIGNED_SHORT: + return 2; + case GL_INT: + case GL_UNSIGNED_INT: + return 4; + case GL_FLOAT: + return 4; + case GL_DOUBLE: + return 8; + default: + throw new IllegalArgumentException("Unknown type enum: " + getString(glTypeEnum)); + } + } } \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/core/render/objects/GLState.java b/src/main/java/com/seibel/lod/core/render/objects/GLState.java index 35798cc3e..802beff34 100644 --- a/src/main/java/com/seibel/lod/core/render/objects/GLState.java +++ b/src/main/java/com/seibel/lod/core/render/objects/GLState.java @@ -26,6 +26,7 @@ public class GLState { public int prog; public int vao; public int vbo; + public int ebo; public int fbo; public int text; public int activeTex; @@ -48,6 +49,7 @@ public class GLState { prog = GL32.glGetInteger(GL32.GL_CURRENT_PROGRAM); vao = GL32.glGetInteger(GL32.GL_VERTEX_ARRAY_BINDING); vbo = GL32.glGetInteger(GL32.GL_ARRAY_BUFFER_BINDING); + ebo = GL32.glGetInteger(GL32.GL_ELEMENT_ARRAY_BUFFER_BINDING); fbo = GL32.glGetInteger(GL32.GL_FRAMEBUFFER_BINDING); text = GL32.glGetInteger(GL32.GL_TEXTURE_BINDING_2D); activeTex = GL32.glGetInteger(GL32.GL_ACTIVE_TEXTURE); @@ -72,7 +74,7 @@ public class GLState { @Override public String toString() { - return "GLState{" + "prog=" + prog + ", vao=" + vao + ", vbo=" + vbo + ", fbo=" + fbo + + return "GLState{" + "prog=" + prog + ", vao=" + vao + ", vbo=" + vbo + ", ebo=" + ebo + ", fbo=" + fbo + ", text=" + GLEnums.getString(text) + "@"+activeTex+", text0=" + GLEnums.getString(text0) + ", blend=" + blend + ", blendMode=" + GLEnums.getString(blendSrc) + "," + GLEnums.getString(blendDst) + ", depth=" + depth + @@ -96,6 +98,7 @@ public class GLState { GL32.glBindTexture(GL32.GL_TEXTURE_2D, text); GL32.glBindVertexArray(vao); GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, vbo); + GL32.glBindBuffer(GL32.GL_ELEMENT_ARRAY_BUFFER, ebo); GL32.glBlendFunc(blendSrc, blendDst); if (depth) { diff --git a/src/main/java/com/seibel/lod/core/render/objects/GLVertexBuffer.java b/src/main/java/com/seibel/lod/core/render/objects/GLVertexBuffer.java new file mode 100644 index 000000000..e7798e876 --- /dev/null +++ b/src/main/java/com/seibel/lod/core/render/objects/GLVertexBuffer.java @@ -0,0 +1,77 @@ +/* + * This file is part of the Distant Horizons mod (formerly the LOD Mod), + * licensed under the GNU GPL v3 License. + * + * Copyright (C) 2020-2022 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.seibel.lod.core.render.objects; + +import java.nio.ByteBuffer; +import org.lwjgl.opengl.GL32; + +import com.seibel.lod.core.enums.config.GpuUploadMethod; + +/** + * This is a container for a OpenGL + * VBO (Vertex Buffer Object). + * + * @author James Seibel + * @version 11-20-2021 + */ +public class GLVertexBuffer extends GLBuffer +{ + /** + * When uploading to a buffer that is too small, recreate it this many times + * bigger than the upload payload + */ + protected int vertexCount = 0; + public int getVertexCount() { return vertexCount; } + // FIXME: This setter is needed for premapping buffer to manually set the vertexCount. Fix this. + public void setVertexCount(int vertexCount) { this.vertexCount = vertexCount; } + + public GLVertexBuffer(boolean isBufferStorage) + { + super(isBufferStorage); + } + + @Override + public void destroy(boolean async) { + super.destroy(async); + vertexCount = 0; + } + + @Override + public int getBufferBindingTarget() { + return GL32.GL_ARRAY_BUFFER; + } + + public void uploadBuffer(ByteBuffer bb, int vertCount, GpuUploadMethod uploadMethod, int maxExpensionSize) { + if (vertCount < 0) throw new IllegalArgumentException("VertCount is negative!"); + // If size is zero, just ignore it. + if (bb.limit()-bb.position() != 0) { + boolean useBuffStorage = uploadMethod.useBufferStorage; + super.uploadBuffer(bb, uploadMethod, maxExpensionSize, useBuffStorage ? 0 : GL32.GL_STATIC_DRAW); + } + vertexCount = vertCount; + } + + public ByteBuffer mapBuffer(int targetSize, GpuUploadMethod uploadMethod, int maxExpensionSize) + { + return super.mapBuffer(targetSize, uploadMethod, maxExpensionSize, + uploadMethod.useBufferStorage ? GL32.GL_MAP_WRITE_BIT : GL32.GL_STATIC_DRAW, + GL32.GL_MAP_WRITE_BIT | GL32.GL_MAP_UNSYNCHRONIZED_BIT | GL32.GL_MAP_INVALIDATE_BUFFER_BIT); + } +} \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/core/render/objects/QuadElementBuffer.java b/src/main/java/com/seibel/lod/core/render/objects/QuadElementBuffer.java new file mode 100644 index 000000000..99eca5f60 --- /dev/null +++ b/src/main/java/com/seibel/lod/core/render/objects/QuadElementBuffer.java @@ -0,0 +1,126 @@ +package com.seibel.lod.core.render.objects; + +import com.seibel.lod.core.api.ApiShared; +import com.seibel.lod.core.enums.config.GpuUploadMethod; +import com.seibel.lod.core.render.GLProxy; +import org.lwjgl.opengl.GL32; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +public class QuadElementBuffer extends GLElementBuffer { + + public QuadElementBuffer() { + super(GLProxy.getInstance().bufferStorageSupported); + } + + public int getCapacity() { + return super.getSize() / GLEnums.getTypeSize(getType()); + } + + private static void buildBufferByte(int quadCount, ByteBuffer buffer) { + for (int i = 0; i < quadCount; i++) { + int vIndex = i * 4; + // First triangle + buffer.put((byte) (vIndex)); + buffer.put((byte) (vIndex + 1)); + buffer.put((byte) (vIndex + 2)); + // Second triangle + buffer.put((byte) (vIndex + 2)); + buffer.put((byte) (vIndex + 3)); + buffer.put((byte) (vIndex)); + } + if (buffer.hasRemaining()) { + throw new IllegalStateException("QuadElementBuffer is not full somehow after building"); + } + buffer.rewind(); + } + private static void buildBufferShort(int quadCount, ByteBuffer buffer) { + for (int i = 0; i < quadCount; i++) { + int vIndex = i * 4; + // First triangle + buffer.putShort((short) (vIndex)); + buffer.putShort((short) (vIndex + 1)); + buffer.putShort((short) (vIndex + 2)); + // Second triangle + buffer.putShort((short) (vIndex + 2)); + buffer.putShort((short) (vIndex + 3)); + buffer.putShort((short) (vIndex)); + } + if (buffer.hasRemaining()) { + throw new IllegalStateException("QuadElementBuffer is not full somehow after building"); + } + buffer.rewind(); + } + private static void buildBufferInt(int quadCount, ByteBuffer buffer) { + for (int i = 0; i < quadCount; i++) { + int vIndex = i * 4; + // First triangle + buffer.putInt(vIndex); + buffer.putInt(vIndex + 1); + buffer.putInt(vIndex + 2); + // Second triangle + buffer.putInt(vIndex + 2); + buffer.putInt(vIndex + 3); + buffer.putInt(vIndex); + } + if (buffer.hasRemaining()) { + throw new IllegalStateException("QuadElementBuffer is not full somehow after building"); + } + buffer.rewind(); + } + + private static void buildBuffer(int quadCount, ByteBuffer buffer, int type) { + switch (type) { + case GL32.GL_UNSIGNED_BYTE: + buildBufferByte(quadCount, buffer); + break; + case GL32.GL_UNSIGNED_SHORT: + buildBufferShort(quadCount, buffer); + break; + case GL32.GL_UNSIGNED_INT: + buildBufferInt(quadCount, buffer); + break; + default: + throw new IllegalStateException("Unknown type: " + type); + } + } + + public void reserve(int quadCount) { + if (quadCount < 0) { + throw new IllegalArgumentException("quadCount must be greater than 0"); + } + indicesCount = quadCount * 6; // 2 triangles per quad + if (indicesCount >= getCapacity() && indicesCount < getCapacity() * BUFFER_SHRINK_TRIGGER) { + return; + } + int vertexCount = quadCount * 4; // 4 vertices per quad + GLProxy gl = GLProxy.getInstance(); + + if (vertexCount < 255) { // Reserve 1 for the reset index + type = GL32.GL_UNSIGNED_BYTE; + } else if (vertexCount < 65535) { // Reserve 1 for the reset index + type = GL32.GL_UNSIGNED_SHORT; + } else { + type = GL32.GL_UNSIGNED_INT; + } + ApiShared.LOGGER.info("Quad IBO Resizing from [" + getCapacity() + "] to [" + quadCount + "]" + " with type: " + + GLEnums.getString(type)); + + ByteBuffer buffer; + if (!gl.bufferStorageSupported) { + bind(); + buffer = super.mapBuffer(indicesCount * GLEnums.getTypeSize(type), GpuUploadMethod.BUFFER_MAPPING, + indicesCount * GLEnums.getTypeSize(type), GL32.GL_STATIC_DRAW, + GL32.GL_MAP_WRITE_BIT | GL32.GL_MAP_INVALIDATE_BUFFER_BIT | GL32.GL_MAP_UNSYNCHRONIZED_BIT); + buildBuffer(quadCount, buffer, type); + super.unmapBuffer(); + } else { + buffer = ByteBuffer.allocateDirect(indicesCount * GLEnums.getTypeSize(type)).order(ByteOrder.nativeOrder()); + buildBuffer(quadCount, buffer, type); + bind(); + super.uploadBuffer(buffer, GpuUploadMethod.BUFFER_STORAGE, + indicesCount * GLEnums.getTypeSize(type), 0); + } + } +}