为啥创建 VBO 时静态方法会占用内存?
Posted
技术标签:
【中文标题】为啥创建 VBO 时静态方法会占用内存?【英文标题】:Why do static methods run up memory when creating VBO's?为什么创建 VBO 时静态方法会占用内存? 【发布时间】:2016-03-22 15:49:59 【问题描述】:我有一个程序可以在屏幕上绘制六个字段,每个字段包含大约 2000 个顶点。要为这些创建 VBO,我需要使用 Buffer 对象(Int/Float)。我需要能够将顶点位置等保存到 xml 文件中,所以我想序列化模型对象,转换为 B64 并将它们保存为字符串。问题是 Buffer 对象不可序列化。
因此,为了解决这个问题,我从模型对象中删除了所有 VBO 代码(它最初是从一个单独的类扩展而来的)并创建了静态方法来创建我的 VBO。因此,我调用静态方法为模型创建 VBO,然后返回句柄,以便我可以调用渲染、更新顶点等。然而,这会在创建模型时产生严重增加内存的效果。
为什么会这样?最初内存使用情况甚至不明显。现在它使 JVM 崩溃。我没有更改任何代码逻辑,方法是相同的,只是现在它们是静态的并传回句柄。创建 VBO 时,静态方法会以某种方式使用更多内存吗?我以为会少一点?使用后我会清除所有缓冲区。我确实处理掉了所有剔除的模型。
编辑: 这是包含静态方法的 Render 类
package Drawing;
import static org.lwjgl.opengl.GL11.GL_FLOAT;
import static org.lwjgl.opengl.GL15.GL_ARRAY_BUFFER;
import static org.lwjgl.opengl.GL15.GL_DYNAMIC_DRAW;
import static org.lwjgl.opengl.GL15.GL_ELEMENT_ARRAY_BUFFER;
import static org.lwjgl.opengl.GL15.GL_STATIC_DRAW;
import static org.lwjgl.opengl.GL15.glBindBuffer;
import static org.lwjgl.opengl.GL15.glBufferData;
import static org.lwjgl.opengl.GL15.glBufferSubData;
import static org.lwjgl.opengl.GL15.glGenBuffers;
import static org.lwjgl.opengl.GL20.glEnableVertexAttribArray;
import static org.lwjgl.opengl.GL20.glVertexAttribPointer;
import static org.lwjgl.opengl.GL30.glBindVertexArray;
import static org.lwjgl.opengl.GL30.glGenVertexArrays;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import org.lwjgl.BufferUtils;
/**
* This class calls all vbo functions in a static way which allows me to
* separate the Int/FloatBuffers from the model classes like Cube and
* Quad. Int/FloatBuffers will not serialize so the Cube/Quad classes
* cannot be saved to file. Keeping them static and separated
* will overcome this problem.
*/
public class Render
static int VERTEXCOUNT = 0;//((QUAD_SIZE * QUAD_SIZE) * 12);
static FloatBuffer fbData = null;//BufferUtils.createFloatBuffer(VERTEXCOUNT);
static FloatBuffer fbNorm = null;//BufferUtils.createFloatBuffer(VERTEXCOUNT);
static FloatBuffer fbtex = null;//BufferUtils.createFloatBuffer((VERTEXCOUNT / 12) * 8);
static IntBuffer Indices = null;
private static int _VAOHandle = 0;
private static IntBuffer vboHandles;
static final int POSITION_INDEX = 0; // index of vertex attribute "in_Position"
static final int NORMALS_IDX = 1;
static final int TEXTURE_IDX = 2;
static final int IBO_IDX = 3;
public static VBOIndexes createVBO(int QUAD_SIZE,
float[] vertBuffer,
float[] normals,
float[] UVs,
int[] idxBuffer) throws Exception
VBOIndexes vboINDEXES = new VBOIndexes();
try
VERTEXCOUNT = (int) ((QUAD_SIZE * QUAD_SIZE) * 12);
fbData = BufferUtils.createFloatBuffer(VERTEXCOUNT);
fbNorm = BufferUtils.createFloatBuffer(VERTEXCOUNT);
fbtex = BufferUtils.createFloatBuffer((VERTEXCOUNT / 12) * 8);
Indices = BufferUtils.createIntBuffer(VERTEXCOUNT / 2);
_VAOHandle = glGenVertexArrays();
vboINDEXES.VAOHandle = _VAOHandle;
System.out.println("VAOHandle is : " + _VAOHandle);
glBindVertexArray(_VAOHandle);
vboHandles = BufferUtils.createIntBuffer(4);
glGenBuffers(vboHandles);
vboINDEXES.idxPOS = vboHandles.get(POSITION_INDEX);
vboINDEXES.idxNORM = vboHandles.get(NORMALS_IDX);
vboINDEXES.idxTEX = vboHandles.get(TEXTURE_IDX);
vboINDEXES.idxIBO = vboHandles.get(IBO_IDX);
//FloatBuffer fbData = BufferUtils.createFloatBuffer(vertBuffer.length);
fbData.put(vertBuffer);
fbData.rewind(); // rewind, otherwise LWJGL thinks our buffer is empty
glBindBuffer(GL_ARRAY_BUFFER, vboHandles.get(POSITION_INDEX));
glBufferData(GL_ARRAY_BUFFER, fbData, GL_DYNAMIC_DRAW);
fbData.clear(); //don't need this anymore
//populate the normals buffer
//FloatBuffer fbNorm = BufferUtils.createFloatBuffer(normalsBuffer.length );
fbNorm.put(normals);
fbNorm.rewind();
glBindBuffer(GL_ARRAY_BUFFER, vboHandles.get(NORMALS_IDX)); //the vertex data
glBufferData(GL_ARRAY_BUFFER, fbNorm, GL_STATIC_DRAW);
fbNorm.clear(); //don't need this anymore
//populate the texture buffer
fbtex.put(UVs);
fbtex.rewind();
glBindBuffer(GL_ARRAY_BUFFER, vboHandles.get(TEXTURE_IDX));
glBufferData(GL_ARRAY_BUFFER, fbtex, GL_DYNAMIC_DRAW);
fbtex.clear(); //don't need this anymore
Indices.put(idxBuffer);
Indices.rewind();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboHandles.get(IBO_IDX));
glBufferData(GL_ELEMENT_ARRAY_BUFFER, Indices, GL_STATIC_DRAW);
glEnableVertexAttribArray(POSITION_INDEX);
glEnableVertexAttribArray(NORMALS_IDX);
glEnableVertexAttribArray(TEXTURE_IDX);
glBindBuffer(GL_ARRAY_BUFFER, vboHandles.get(POSITION_INDEX));
glVertexAttribPointer(0,3, GL_FLOAT, false,0,0);
//normals
glBindBuffer(GL_ARRAY_BUFFER, vboHandles.get(NORMALS_IDX));
glVertexAttribPointer(1, 3, GL_FLOAT, false, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, vboHandles.get(TEXTURE_IDX));
glVertexAttribPointer(2, 2, GL_FLOAT, false, 0, 0);
//bind IBO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboHandles.get(IBO_IDX));
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
Indices.clear();
catch (Exception ex)
System.out.println("createVBO: " + ex.getMessage());
throw ex;
return vboINDEXES;
public static VBOIndexes createLineVBO( float[] vertBuffer,
int[] idxBuffer) throws Exception
VBOIndexes vboINDEXES = new VBOIndexes();
try
fbData = BufferUtils.createFloatBuffer(vertBuffer.length);
Indices = BufferUtils.createIntBuffer(vertBuffer.length / 2);
_VAOHandle = glGenVertexArrays();
vboINDEXES.VAOHandle = _VAOHandle;
glBindVertexArray(_VAOHandle);
vboHandles = BufferUtils.createIntBuffer(4);
glGenBuffers(vboHandles);
vboINDEXES.idxPOS = vboHandles.get(POSITION_INDEX);
vboINDEXES.idxIBO = vboHandles.get(IBO_IDX);
fbData.put(vertBuffer);
fbData.rewind(); // rewind, otherwise LWJGL thinks our buffer is empty
glBindBuffer(GL_ARRAY_BUFFER, vboHandles.get(POSITION_INDEX));
glBufferData(GL_ARRAY_BUFFER, fbData, GL_STATIC_DRAW);
fbData.clear(); //don't need this anymore
//IntBuffer Indices = BufferUtils.createIntBuffer(idxBuffer.length);
Indices.put(idxBuffer);
Indices.rewind();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboHandles.get(IBO_IDX));
//Util.checkGLError();
glBufferData(GL_ELEMENT_ARRAY_BUFFER, Indices, GL_STATIC_DRAW);
glEnableVertexAttribArray(POSITION_INDEX);
glBindBuffer(GL_ARRAY_BUFFER, vboHandles.get(POSITION_INDEX));
glVertexAttribPointer(0,3, GL_FLOAT, false,0,0);
//bind IBO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboHandles.get(IBO_IDX));
Indices.clear();
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
//Util.checkGLError();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
catch (Exception ex)
System.out.println("createVBO: " + ex.getMessage());
throw ex;
return vboINDEXES;
public static void updateVertices(int offset,
float[] vertBuffer,
int idxPOS)
//populate the vertex buffer
FloatBuffer fbData = BufferUtils.createFloatBuffer(vertBuffer.length);
fbData.put(vertBuffer);
fbData.rewind();
//glBindBuffer(GL_ARRAY_BUFFER, vboHandles.get(POSITION_INDEX)); //the vertex data
glBindBuffer(GL_ARRAY_BUFFER, idxPOS);
glBufferSubData(GL_ARRAY_BUFFER, offset, fbData);
fbData.clear(); //don't need this anymore
public static void updateNormals(int offset,
float[] normals,
int idxNORM)
//populate the vertex buffer
FloatBuffer fbData = BufferUtils.createFloatBuffer(normals.length);
fbData.put(normals);
fbData.rewind();
//glBindBuffer(GL_ARRAY_BUFFER, vboHandles.get( NORMALS_IDX)); //the vertex data
glBindBuffer(GL_ARRAY_BUFFER, idxNORM);
glBufferSubData(GL_ARRAY_BUFFER, 0, fbData);
fbData.clear(); //don't need this anymore
public static void updateTexture(int offset,
float[] UVs,
int idxTEX)
//populate the texture buffer
FloatBuffer fbtex = BufferUtils.createFloatBuffer(UVs.length);
fbtex.put( UVs );
fbtex.rewind();
//glBindBuffer(GL_ARRAY_BUFFER, vboHandles.get(TEXTURE_IDX)); //the texture data
glBindBuffer(GL_ARRAY_BUFFER, idxTEX);
glBufferSubData(GL_ARRAY_BUFFER, offset, fbtex);
fbtex.clear(); //don't need this anymore
public Render()
在字段(瓦片对象数组)的构造函数中进行静态调用,如下所示:
public Quad(Map<Integer, Cube> c, int SIZE) throws Exception
//public Quad(ArrayList<Cube> c, int SIZE) throws Exception
QUAD_SIZE = SIZE;
initArrays();
initVBOData(SIZE);
createVBO();
cubes = c;
【问题讨论】:
你真的需要纯 xml 格式的职位吗? xml 中的元数据和二进制中的位置是否适合您? 那部分并不是真正的问题。我实际上并没有保存所有的顶点位置,只是模型的高度和其他属性。问题是通过调用这些静态方法,内存只会被杀死。曾经只有 100k 的东西现在超过 1G 了吗?而且逻辑没有变。 几千个顶点真的是一个小数目,仔细检查你的代码,因为我猜你做错了什么。如果您可以发布一些代码.. 我看不出从VERTEXCOUNT
到vboHandles
的字段是静态成员甚至是类成员的任何理由。它们都应该是局部变量。
如果您阅读我的原始问题,您会发现我的问题不在于 VBO。我的问题是试图找到一种能够序列化和反序列化对象的方法。缓冲区对象不可序列化。我试图解决这个问题的尝试是创建一种创建 VBO 的静态方式。失败了。
【参考方案1】:
我相信答案是静态类不提供 GC。随着所有数据在创建 VBO 时传递,G 真的开始堆积起来......
【讨论】:
完全正确。据我所知,静态类是程序期间的分配。他们不会移动,也不会收集垃圾,他们只是坐在那里等待调用。 完全不正确。对象的生命周期与它的类是否为static
完全无关。 @sova
@EJP,好吧,我还是个新手,没关系,但是静态最终对象呢?
@sova 他们呢?如果您有问题,请提出。作为一个问题。和这里手头的事情没有任何关系。
@sova - 我不相信你是不正确的。通常,静态类可以预期会保留在内存中,直到它的加载器停止。【参考方案2】:
您的代码看起来很混乱,您应该花一些时间来更好地了解 OpenGL 的工作原理并使其更清晰、更易读。 我建议你从this sample 中汲取灵感。
几个注意事项:
你根本不需要静态 我不知道 lwjgl,但我猜它需要直接缓冲区,你不能保证它们会被垃圾收集器删除,所以你应该自己分配它们,当你不需要它们时释放它们了。我创建了一个小类来释放直接缓冲区,取自 JM3 here 通过将 opengl 资源名称保存在vboINDEXES
中来避免冗余。您已经在直接使用vboHandles
如果你的缓冲区的尺寸没有改变,分配一次并保留它们(fbData
,fbNorm
,fbTex
),也不需要clear()
它们
因此,当您updateNormals()
不实例化新缓冲区时,请使用一开始就已实例化的缓冲区
从逻辑上看,IBO_INDEX
不应该是vboINDEXES
的一部分
当您调用glVertexAttribPointer
时,将保存属性索引的变量作为第一个参数传递,例如POSITION_INDEX
ps:index 的复数形式是indices
【讨论】:
以上是关于为啥创建 VBO 时静态方法会占用内存?的主要内容,如果未能解决你的问题,请参考以下文章