OpenGL - 每帧(几乎)调用 glBufferSubData 是否有效?

OpenGL - Is it efficient to call glBufferSubData (nearly) each frame?

我有一个包含简单精灵动画的精灵表。我可以成功提取每个动画帧精灵并将其显示为纹理。但是,我设法通过调用 glBufferSubData 来更改纹理坐标(在游戏循环之前,在初始化时)来做到这一点。我想使用这些提取的纹理播放一个简单的精灵动画,我想我会通过改变每一帧的纹理坐标来实现(除非动画是由用户输入触发的)。无论如何,这导致几乎每一帧都调用 glBufferSubData(以更改纹理数据),我的问题是这种方法是否有效?如果没有,我该如何解决这个问题? (在 Reddit 上,我看到一条评论说目标必须是在现代 OpenGL 中最小化 CPU 和 GPU 内存之间的流量,我猜我的方法违反了这个目标。)在此先感谢。

对于任何感兴趣的人,这是我的方法:

void set_sprite_texture_from_spritesheet(Sprite* sprite, const char* path, int x_offset, int y_offset, int sprite_width, int sprite_height)
{
    float* uv_coords = get_uv_coords_from_spritesheet(path, x_offset, y_offset, sprite_width, sprite_height);
    for (int i = 0; i < 8; i++)
    {                                                   
        /* 8 means that I am changing a total of 8 texture coordinates (2 for each 4 vertices) */
        edit_vertex_data_by_index(sprite, &uv_coords[i], (i / 2) * 5 + 3 + (i % 2 != 0));
        /* 
           the last argument in this function gives the index of the desired texture coordinate 
           (5 is for stride, 3 for offset of texture coordinates in each row)
        */
    }

    free(uv_coords); 
    sprite->texture = load_texture(path); /* loads the texture -
                                             since the UV coordinate is 
                                             adjusted based on the spritesheet
                                             I am loading the entire spritesheet as
                                             a texture.
                                          */
}
void edit_vertex_data_by_index(Sprite *sprite, float *data, unsigned int start_index)
{
    glBindBuffer(GL_ARRAY_BUFFER, sprite->vbo);
    glBufferSubData(GL_ARRAY_BUFFER, start_index * sizeof(float), sizeof(data), data);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    /*
        My concern is that if I call this almost every frame, it could be not efficient, but I am not sure.
    */
}

编辑缓冲区没问题。从字面上看,每个游戏都有改变每一帧的缓冲区。缓冲区是您将数据传输到 GPU 以便它进行渲染的方式! (还有制服。你的 driver 可能会偷偷把制服放在缓冲区里!)

是的,您应该尽量减少缓冲区更新的数量。你应该尽量减少一切,真的。计算机做的事情越少,它做起来的速度就越快!这并不意味着你应该完全避免做某事。这意味着你应该只做你需要做的事情,而不是做你不需要的浪费。

每次您调用 OpenGL 函数时,driver 都会花一些时间来检查如何处理您的请求、绑定了哪个缓冲区、是否足够大、GPU 是否正在使用它同一时间等。您希望尽可能少地进行调用,因为那样的话,driver 就不必经常检查所有这些东西。

您在此函数中进行了 8 次单独的 glBufferSubData 调用。如果将 UV 坐标彼此并排放置在缓冲区中,则可以通过 1 次调用一次更新它们。如果你有 lots 个动画精灵,你应该尝试将 all 它们的 UV 坐标放在一个大数组中,并更新整个数组一个电话 - 一次所有精灵。

并且从路径加载纹理真的慢。也许您的程序每秒可以加载 100 个纹理,但这仍然意味着您在纹理加载上花费了一半的帧时间预算。无论如何纹理都没有改变所以你为什么要再次加载它?