Add BufferStorage recreation, improving memory usage

I still want the buffers to be created the right size at the start, but this will work for now.
This commit is contained in:
James Seibel
2021-10-09 09:46:24 -05:00
parent 272546af4d
commit 126b581e97
@@ -62,7 +62,7 @@ import net.minecraft.util.math.ChunkPos;
* This object is used to create NearFarBuffer objects.
*
* @author James Seibel
* @version 10-7-2021
* @version 10-9-2021
*/
public class LodBufferBuilder
{
@@ -71,6 +71,13 @@ public class LodBufferBuilder
/** The threads used to generate buffers. */
public static ExecutorService bufferBuilderThreads = Executors.newFixedThreadPool(LodConfig.CLIENT.threading.numberOfBufferBuilderThreads.get(), new ThreadFactoryBuilder().setNameFormat("Buffer-Builder-%d").build());
/**
* 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.5;
/** This boolean matrix indicate the buffer builder in that position has to be regenerated */
public volatile boolean[][] regenPos;
@@ -88,6 +95,14 @@ public class LodBufferBuilder
/** The OpenGL IDs of the storage buffers used by the drawableVbos */
public int[][] drawableStorageBufferIds;
/** used to debug how the buildableStorageBuffers are growing */
public int[][] bufferPreviousCapacity;
/**
* This is toggled when the buffers are swapped so we only
* display content related to one set of buffers
*/
public boolean printExpansionLog = true;
/** Used when building new VBOs */
public volatile VertexBuffer[][][] buildableVbos;
/** VBOs that are sent over to the LodNodeRenderer */
@@ -450,7 +465,7 @@ public class LodBufferBuilder
if (regionMemoryRequired > LodUtil.MAX_ALOCATEABLE_DIRECT_MEMORY)
{
numberOfBuffers = (int) Math.ceil(regionMemoryRequired / LodUtil.MAX_ALOCATEABLE_DIRECT_MEMORY) + 1;
regionMemoryRequired = LodUtil.MAX_ALOCATEABLE_DIRECT_MEMORY; // TODO this should be determined with regionMemoryRequired?
regionMemoryRequired = LodUtil.MAX_ALOCATEABLE_DIRECT_MEMORY; // TODO should this be determined with regionMemoryRequired?
bufferSize[x][z] = numberOfBuffers;
buildableBuffers[x][z] = new BufferBuilder[numberOfBuffers];
buildableVbos[x][z] = new VertexBuffer[numberOfBuffers];
@@ -490,13 +505,12 @@ public class LodBufferBuilder
buildableStorageBufferIds[x][z] = GL45.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, buildableStorageBufferIds[x][z]);
// TODO get a better max size, BufferStorage's can't be resized after they are created
GL45.glBufferStorage(GL15.GL_ARRAY_BUFFER, LodUtil.MAX_ALOCATEABLE_DIRECT_MEMORY, 0); // the 0 flag means to create the storage in the GPU's memory
GL45.glBufferStorage(GL15.GL_ARRAY_BUFFER, regionMemoryRequired, 0); // the 0 flag means to create the storage in the GPU's memory
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
drawableStorageBufferIds[x][z] = GL45.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, drawableStorageBufferIds[x][z]);
GL45.glBufferStorage(GL15.GL_ARRAY_BUFFER, LodUtil.MAX_ALOCATEABLE_DIRECT_MEMORY, 0);
GL45.glBufferStorage(GL15.GL_ARRAY_BUFFER, regionMemoryRequired, 0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
}
}
@@ -505,8 +519,6 @@ public class LodBufferBuilder
bufferLock.unlock();
}
public int[][] bufferPreviousCapacity;
/**
* Sets the buffers and Vbos to null, forcing them to be recreated <br>
* and destroys any bound OpenGL objects. <br><br>
@@ -649,7 +661,7 @@ public class LodBufferBuilder
for (int i = 0; i < buildableBuffers[x][z].length; i++)
{
ByteBuffer builderBuffer = buildableBuffers[x][z][i].popNextBuffer().getSecond();
vboUpload(buildableVbos[x][z][i], buildableStorageBufferIds[x][z], builderBuffer, x,z);
vboUpload(buildableVbos[x][z][i], buildableStorageBufferIds[x][z], builderBuffer, x,z, true);
lodDim.setRegenRegionBufferByArrayIndex(x, z, false);
}
}
@@ -673,7 +685,7 @@ public class LodBufferBuilder
/** Uploads the uploadBuffer into the VBO and then into GPU memory. */
private void vboUpload(VertexBuffer vbo, int storageBufferId, ByteBuffer uploadBuffer,
int xVboIndex, int zVboIndex)
int xVboIndex, int zVboIndex, boolean allowBufferExpansion)
// x/zVboIndex are just used for the debugging console logging
// and should be removed when the logger is removed.
{
@@ -693,25 +705,42 @@ public class LodBufferBuilder
{
int previousCapacity = uploadBuffer.capacity();
// only change the system buffer if the uploadBuffer actually
// has something in it
if (previousCapacity != 0)
// only expand the buffers if the uploadBuffer actually
// has something in it and expansion is allowed
if (previousCapacity != 0 && allowBufferExpansion)
{
// the buffer in system memory isn't big enough,
// expand it so next time hopefully it will be big enough.
// the buffer(s) aren't big enough, expand them.
// This does cause lag/stuttering so it should be avoided!
GL45.glBufferData(GL45.GL_ARRAY_BUFFER, uploadBuffer.capacity() * 4, GL45.GL_DYNAMIC_DRAW);
// expand the buffer in system memory
GL45.glBufferData(GL45.GL_ARRAY_BUFFER, (int) (uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER), GL45.GL_DYNAMIC_DRAW);
GL45.glBufferSubData(GL45.GL_ARRAY_BUFFER, 0, uploadBuffer);
// NOTE: this will display twice because we are double buffering
// (using 1 buffer to generate into and one to draw)
ClientProxy.LOGGER.info("vbo (" + xVboIndex + "," + zVboIndex + ") expanded: " + bufferPreviousCapacity[xVboIndex][zVboIndex] + " -> " + (previousCapacity * 4));
// un-bind the system memory buffer
GL15.glUnmapBuffer(GL15.GL_ARRAY_BUFFER);
GL15C.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
// could be used if you wanted to create a CSV file
//ClientProxy.LOGGER.info(xVboIndex + "," + zVboIndex + "," + bufferPreviousCapacity[xVboIndex][zVboIndex] + "," + uploadBuffer.capacity());
// expand the buffer storage
GL45.glDeleteBuffers(storageBufferId);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, storageBufferId);
GL45.glBufferStorage(GL15.GL_ARRAY_BUFFER, (int) (uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER), 0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
bufferPreviousCapacity[xVboIndex][zVboIndex] = uploadBuffer.capacity() * 4;
// recursively try to upload into the newly created buffer storage
// but don't recurse again if that fails
// (we don't want an infinitely expanding buffer!)
vboUpload(vbo, storageBufferId, uploadBuffer, xVboIndex, zVboIndex, false);
if (printExpansionLog)
{
// NOTE: this will display twice because we are double buffering
// (using 1 buffer to generate into and one to draw)
ClientProxy.LOGGER.info("vbo (" + xVboIndex + "," + zVboIndex + ") expanded: " + bufferPreviousCapacity[xVboIndex][zVboIndex] + " -> " + (int)(uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER));
bufferPreviousCapacity[xVboIndex][zVboIndex] = (int) (uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER);
}
}
}
else
@@ -753,6 +782,9 @@ public class LodBufferBuilder
drawableStorageBufferIds = buildableStorageBufferIds;
buildableStorageBufferIds = tmpStorage;
// we only want to print the exapnsion log for
// one set of buffers, not both
printExpansionLog = !printExpansionLog;
drawableCenterChunkPos = buildableCenterChunkPos;