Rework cortex's ibo, and the buffer objects.

This commit is contained in:
TomTheFurry
2022-04-14 15:42:37 +08:00
parent 0905f60034
commit ca64027b0c
13 changed files with 610 additions and 380 deletions
@@ -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;
@@ -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<BufferQuad>[] quads = (ArrayList<BufferQuad>[]) 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<ByteBuffer>()
{
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);
}
}
@@ -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 <https://www.gnu.org/licenses/>.
*/
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" : "")+"]";
}
}
@@ -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 <https://www.gnu.org/licenses/>.
*/
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);
}
}
@@ -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<ByteBuffer> 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<vbos.length; i++) {
if (vbos[i]==null) vbos[i] = new LodVertexBuffer(method.useBufferStorage);
if (vbos[i]==null) vbos[i] = new GLVertexBuffer(method.useBufferStorage);
}
BufferFiller func = builder.makeBufferFiller(method);
int i = 0;
while (i < vbos.length && func.fill(vbos[i++])) {}
}
private LodVertexBuffer getOrMakeVbo(int iIndex, boolean useBuffStorage) {
private GLVertexBuffer getOrMakeVbo(int iIndex, boolean useBuffStorage) {
if (vbos[iIndex] == null) {
vbos[iIndex] = new LodVertexBuffer(useBuffStorage);
vbos[iIndex] = new GLVertexBuffer(useBuffStorage);
}
return vbos[iIndex];
}
private void resize(int size) {
if (vbos.length != size) {
LodVertexBuffer[] newVbos = new LodVertexBuffer[size];
GLVertexBuffer[] newVbos = new GLVertexBuffer[size];
if (vbos.length > size) {
for (int i=size; i<vbos.length; i++) {
if (vbos[i]!=null) vbos[i].close();
@@ -178,7 +183,7 @@ public class SimpleRenderBuffer extends RenderBuffer
newVbos[i] = vbos[i];
vbos[i] = null;
}
for (LodVertexBuffer b : vbos) {
for (GLVertexBuffer b : vbos) {
if (b != null) throw new RuntimeException("LEAKING VBO!");
}
vbos = newVbos;
@@ -26,10 +26,9 @@ import java.util.concurrent.TimeUnit;
import com.seibel.lod.core.api.ApiShared;
import com.seibel.lod.core.logging.ConfigBasedLogger;
import com.seibel.lod.core.logging.ConfigBasedSpamLogger;
import com.seibel.lod.core.logging.SpamReducedLogger;
import com.seibel.lod.core.objects.BoolType;
import com.seibel.lod.core.objects.Pos2D;
import com.seibel.lod.core.objects.opengl.QuadIBO;
import com.seibel.lod.core.render.objects.QuadElementBuffer;
import com.seibel.lod.core.render.objects.GLState;
import com.seibel.lod.core.util.*;
import com.seibel.lod.core.util.gridList.*;
@@ -47,7 +46,6 @@ import com.seibel.lod.core.objects.math.Mat4f;
import com.seibel.lod.core.objects.math.Vec3d;
import com.seibel.lod.core.objects.math.Vec3f;
import com.seibel.lod.core.objects.opengl.RenderRegion;
import com.seibel.lod.core.render.objects.LightmapTexture;
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
@@ -73,6 +71,8 @@ public class LodRenderer
public static final boolean ENABLE_DRAW_LAG_SPIKE_LOGGING = false;
public static final boolean ENABLE_DUMP_GL_STATE = true;
public static final long DRAW_LAG_SPIKE_THRESHOLD_NS = TimeUnit.NANOSECONDS.convert(20, TimeUnit.MILLISECONDS);
public static final boolean ENABLE_IBO = true;
public static class LagSpikeCatcher {
@@ -108,6 +108,7 @@ public class LodRenderer
/** This is used to determine if the LODs should be regenerated */
private AbstractBlockPosWrapper lastUpdatedPos = null;
public QuadElementBuffer quadIBO = null;
// these variables are used to determine if the buffers should be rebuilt
private int prevRenderDistance = 0;
@@ -319,8 +320,8 @@ public class LodRenderer
LagSpikeCatcher drawFillLightmap = new LagSpikeCatcher();
ILightMapWrapper lightmap = MC_RENDER.getLightmapWrapper();
lightmap.bind();
QuadIBO.GLOBAL.bind();
if (ENABLE_IBO) quadIBO.bind();
//lightmapTexture.fillData(MC_RENDER.getLightmapTextureWidth(), MC_RENDER.getLightmapTextureHeight(), MC_RENDER.getLightmapPixels());
drawFillLightmap.end("drawFillLightmap");
@@ -376,7 +377,7 @@ public class LodRenderer
profiler.popPush("LOD cleanup");
LagSpikeCatcher drawCleanup = new LagSpikeCatcher();
lightmap.unbind();
QuadIBO.GLOBAL.unbind();
if (ENABLE_IBO) quadIBO.unbind();
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, 0);
@@ -410,6 +411,10 @@ public class LodRenderer
EVENT_LOGGER.info("Setting up renderer");
isSetupComplete = true;
shaderProgram = new LodRenderProgram(LodFogConfig.generateFogConfig());
if (ENABLE_IBO) {
quadIBO = new QuadElementBuffer();
quadIBO.reserve(LodBufferBuilderFactory.MAX_QUADS_PER_BUFFER);
}
EVENT_LOGGER.info("Renderer setup complete");
}
@@ -492,6 +497,7 @@ public class LodRenderer
isSetupComplete = false;
EVENT_LOGGER.info("Renderer Cleanup Started");
shaderProgram.free();
if (quadIBO != null) quadIBO.destroy(false);
EVENT_LOGGER.info("Renderer Cleanup Complete");
}
@@ -25,7 +25,7 @@ import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
import com.seibel.lod.core.logging.ConfigBasedLogger;
import com.seibel.lod.core.logging.ConfigBasedSpamLogger;
import com.seibel.lod.core.objects.opengl.LodVertexBuffer;
import com.seibel.lod.core.render.objects.GLVertexBuffer;
import com.seibel.lod.core.render.objects.GLState;
import com.seibel.lod.core.render.objects.ShaderProgram;
import com.seibel.lod.core.render.objects.VertexAttribute;
@@ -48,8 +48,8 @@ public class RenderSystemTest {
private static final IMinecraftRenderWrapper MC_RENDER = SingletonHandler.get(IMinecraftRenderWrapper.class);
ShaderProgram basicShader;
LodVertexBuffer sameContextBuffer;
LodVertexBuffer sharedContextBuffer;
GLVertexBuffer sameContextBuffer;
GLVertexBuffer sharedContextBuffer;
VertexAttribute va;
boolean init = false;
@@ -77,8 +77,8 @@ public class RenderSystemTest {
-0.2f, 0.2f, 0.0f, 1.0f, 1.0f, 1.0f
};
private static LodVertexBuffer createTextingBuffer() {
LodVertexBuffer vbo = new LodVertexBuffer(false);
private static GLVertexBuffer createTextingBuffer() {
GLVertexBuffer vbo = new GLVertexBuffer(false);
ByteBuffer buffer = ByteBuffer.allocateDirect(vertices.length * Float.BYTES);
// Fill buffer with the vertices.
buffer = buffer.order(ByteOrder.nativeOrder());
@@ -113,12 +113,12 @@ public class RenderSystemTest {
// Switch between the two buffers per second
if (System.currentTimeMillis() % 2000 < 1000) {
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, sameContextBuffer.id);
va.bindBufferToAllBindingPoint(sameContextBuffer.id);
sameContextBuffer.bind();
va.bindBufferToAllBindingPoint(sameContextBuffer.getId());
spamLogger.debug("same context buffer");
} else {
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, sharedContextBuffer.id);
va.bindBufferToAllBindingPoint(sharedContextBuffer.id);
sameContextBuffer.bind();
va.bindBufferToAllBindingPoint(sharedContextBuffer.getId());
spamLogger.debug("shared context buffer");
}
// Render the square
@@ -0,0 +1,188 @@
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.enums.rendering.GLProxyContext;
import com.seibel.lod.core.render.GLProxy;
import com.seibel.lod.core.util.UnitBytes;
import org.lwjgl.opengl.GL32;
import org.lwjgl.opengl.GL44;
import java.nio.ByteBuffer;
public class GLBuffer implements AutoCloseable {
public static final double BUFFER_EXPANSION_MULTIPLIER = 1.3;
public static final double BUFFER_SHRINK_TRIGGER = BUFFER_EXPANSION_MULTIPLIER * BUFFER_EXPANSION_MULTIPLIER;
public static int count = 0;
protected int id;
public final int getId() {
return id;
}
protected int size = 0;
public int getSize() {
return size;
}
protected boolean bufferStorage;
public final boolean isBufferStorage() {
return bufferStorage;
}
protected boolean isMapped = false;
public GLBuffer(boolean isBufferStorage)
{
create(isBufferStorage);
}
// Should be override by subclasses
public int getBufferBindingTarget() {
return GL32.GL_COPY_READ_BUFFER;
}
public void bind() {
GL32.glBindBuffer(getBufferBindingTarget(), id);
}
public void unbind() {
GL32.glBindBuffer(getBufferBindingTarget(), 0);
}
protected void create(boolean asBufferStorage) {
if (GLProxy.getInstance().getGlContext() == GLProxyContext.NONE)
throw new IllegalStateException("Thread [" +Thread.currentThread().getName() + "] tried to create a GLBuffer outside a OpenGL context.");
this.id = GL32.glGenBuffers();
this.bufferStorage = asBufferStorage;
count++;
}
protected void destroy(boolean async) {
if (this.id == 0) {
ApiShared.LOGGER.warn("Buffer double close!");
return;
}
if (async && GLProxy.getInstance().getGlContext() != GLProxyContext.PROXY_WORKER) {
GLProxy.getInstance().recordOpenGlCall(() -> 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" : "")+"]";
}
}
@@ -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 <https://www.gnu.org/licenses/>.
*/
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;
}
}
@@ -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));
}
}
}
@@ -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) {
@@ -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 <https://www.gnu.org/licenses/>.
*/
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);
}
}
@@ -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);
}
}
}