优化帮助:useProgram 调用每一帧和低 FPS
Optimisation help: useProgram called every frame and low FPS
我正在试验受 Unity 结构启发很大的基于组件的游戏引擎,我似乎以正确的方式做事,但我的帧率非常低。
我的设置涉及一个 MeshRenderer 对象,它有自己的 init()
和 draw()
方法。
初始化
在 init()
期间,我调用了一个 initMesh()
函数:
function initMesh(mesh) {
if(!mesh.vertices) {
mesh.vertices = new Float32Array([
0, 1,
0, 0,
1, 0,
1, 0,
1, 1,
0, 1
]);
}
if(!mesh.uvs) {
mesh.uvs = new Float32Array([
0, 1,
0, 0,
1, 0,
1, 0,
1, 1,
0, 1
]);
}
mesh.itemSize = 2;
mesh.numItems = mesh.vertices.length / mesh.itemSize;
mesh.vertexBuffer = gl.createBuffer();
mesh.uvBuffer = gl.createBuffer();
}
和一个initMaterial()
函数:
function initMaterial(material, fs, vs) {
material.program = initShaders(fs, vs);
if(material.image) {
material.texture = gl.createTexture();
}
}
绘图
在 draw()
方法中我调用了一个 setMeshBuffer()
函数:
function setMeshBuffer(mesh, material) {
// Vertex buffer
gl.bindBuffer(gl.ARRAY_BUFFER, mesh.vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, mesh.vertices, gl.STATIC_DRAW);
gl.vertexAttribPointer(material.program.aVertexPosition, mesh.itemSize, gl.FLOAT, false, 0, 0);
if(material.texture) {
// UV buffer
gl.bindBuffer(gl.ARRAY_BUFFER, mesh.uvBuffer);
gl.bufferData(gl.ARRAY_BUFFER, mesh.uvs, gl.STATIC_DRAW);
gl.enableVertexAttribArray(material.program.aTexCoord);
gl.vertexAttribPointer(material.program.aTexCoord, mesh.itemSize, gl.FLOAT, false, 0, 0);
}
}
和一个setMaterialBuffer()
函数:
function setMaterialBuffer(material) {
gl.useProgram(material.program);
gl.uniform4fv(material.program.uColor, [
material.color.r,
material.color.g,
material.color.b,
material.color.a
]);
if(material.texture) {
gl.bindTexture(gl.TEXTURE_2D, material.texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, material.image);
}
}
最后,我调用 drawBuffers()
函数
function drawBuffers(numItems) {
gl.drawArrays(gl.TRIANGLES, 0, numItems);
}
问题
那么,我可以做些什么来优化这个流程吗?我还没有找到像这样的模块化结构的综合指南。
我的项目是 here,如果你想尝试一下,那就是 test.html
文件。
应要求,我在扩展答案表中的评论:
您每帧都在上传新的顶点和纹理数据,实质上是在每次渲染时重新创建它。相反,在初始化期间上传数据。您可以通过将 bufferData 调用移至 initMesh() 方法并将 texImage2D 调用移至 initMaterial 方法来完成此操作(当然,您必须首先绑定创建的对象)。一旦这些从平局中消失,你会看到更好的表现。
关于您关于每帧更改 UV 坐标的评论,您有几个选项应该比您现有的选项表现更好。
第一个选项是更新 CPU 上的数据(就像您现在所做的那样)。 UV 数据位于单独的缓冲区中是件好事 - 使 CPU 更新速度更快,缓存更一致。按照我的建议在 initMaterial 调用期间初始化数据,但将使用参数更改为 GL_DYNAMIC_DRAW 而不是 GL_STATIC_DRAW。这是对驱动程序的提示,表明您打算经常更改数据。然后,在 setMeshBuffer 中,使用 gl.bufferSubData 而不是 gl.bufferData。这允许您只更新部分数据,但即使上传全新数据,它通常仍然要快得多。
第二种选择——我更喜欢——是保持 UV 静态。不要在 CPU 上修改它们,而是在顶点程序中修改它们。根据您更改 UV 的方式,这通常比在 CPU.
上执行要快得多
希望对您有所帮助,祝您好运!
我正在试验受 Unity 结构启发很大的基于组件的游戏引擎,我似乎以正确的方式做事,但我的帧率非常低。
我的设置涉及一个 MeshRenderer 对象,它有自己的 init()
和 draw()
方法。
初始化
在 init()
期间,我调用了一个 initMesh()
函数:
function initMesh(mesh) {
if(!mesh.vertices) {
mesh.vertices = new Float32Array([
0, 1,
0, 0,
1, 0,
1, 0,
1, 1,
0, 1
]);
}
if(!mesh.uvs) {
mesh.uvs = new Float32Array([
0, 1,
0, 0,
1, 0,
1, 0,
1, 1,
0, 1
]);
}
mesh.itemSize = 2;
mesh.numItems = mesh.vertices.length / mesh.itemSize;
mesh.vertexBuffer = gl.createBuffer();
mesh.uvBuffer = gl.createBuffer();
}
和一个initMaterial()
函数:
function initMaterial(material, fs, vs) {
material.program = initShaders(fs, vs);
if(material.image) {
material.texture = gl.createTexture();
}
}
绘图
在 draw()
方法中我调用了一个 setMeshBuffer()
函数:
function setMeshBuffer(mesh, material) {
// Vertex buffer
gl.bindBuffer(gl.ARRAY_BUFFER, mesh.vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, mesh.vertices, gl.STATIC_DRAW);
gl.vertexAttribPointer(material.program.aVertexPosition, mesh.itemSize, gl.FLOAT, false, 0, 0);
if(material.texture) {
// UV buffer
gl.bindBuffer(gl.ARRAY_BUFFER, mesh.uvBuffer);
gl.bufferData(gl.ARRAY_BUFFER, mesh.uvs, gl.STATIC_DRAW);
gl.enableVertexAttribArray(material.program.aTexCoord);
gl.vertexAttribPointer(material.program.aTexCoord, mesh.itemSize, gl.FLOAT, false, 0, 0);
}
}
和一个setMaterialBuffer()
函数:
function setMaterialBuffer(material) {
gl.useProgram(material.program);
gl.uniform4fv(material.program.uColor, [
material.color.r,
material.color.g,
material.color.b,
material.color.a
]);
if(material.texture) {
gl.bindTexture(gl.TEXTURE_2D, material.texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, material.image);
}
}
最后,我调用 drawBuffers()
函数
function drawBuffers(numItems) {
gl.drawArrays(gl.TRIANGLES, 0, numItems);
}
问题
那么,我可以做些什么来优化这个流程吗?我还没有找到像这样的模块化结构的综合指南。
我的项目是 here,如果你想尝试一下,那就是 test.html
文件。
应要求,我在扩展答案表中的评论:
您每帧都在上传新的顶点和纹理数据,实质上是在每次渲染时重新创建它。相反,在初始化期间上传数据。您可以通过将 bufferData 调用移至 initMesh() 方法并将 texImage2D 调用移至 initMaterial 方法来完成此操作(当然,您必须首先绑定创建的对象)。一旦这些从平局中消失,你会看到更好的表现。
关于您关于每帧更改 UV 坐标的评论,您有几个选项应该比您现有的选项表现更好。
第一个选项是更新 CPU 上的数据(就像您现在所做的那样)。 UV 数据位于单独的缓冲区中是件好事 - 使 CPU 更新速度更快,缓存更一致。按照我的建议在 initMaterial 调用期间初始化数据,但将使用参数更改为 GL_DYNAMIC_DRAW 而不是 GL_STATIC_DRAW。这是对驱动程序的提示,表明您打算经常更改数据。然后,在 setMeshBuffer 中,使用 gl.bufferSubData 而不是 gl.bufferData。这允许您只更新部分数据,但即使上传全新数据,它通常仍然要快得多。
第二种选择——我更喜欢——是保持 UV 静态。不要在 CPU 上修改它们,而是在顶点程序中修改它们。根据您更改 UV 的方式,这通常比在 CPU.
上执行要快得多希望对您有所帮助,祝您好运!