优化帮助: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.

上执行要快得多

希望对您有所帮助,祝您好运!