1

我正在开发一个世界经常变化的 LWJGL 游戏,因为它相当大,每次世界都会导致游戏冻结几分之一秒,因此相应的 VBO 会更新。我通过将所有逻辑移动到单独的线程(嗯,它实际上是在单独的线程中的渲染代码)减少了游戏冻结的时间,但是将数据推送到显卡似乎仍然会产生明显的延迟。是否可以在我的逻辑线程中发送该 VBO 以免减慢游戏速度?

此外,如果这属于 gamedev.so,请告诉我,以便我可以移动它。我不太确定,所以我决定在这里发帖。

4

3 回答 3

2

您不能使用多任务处理使用 OpenGL 进行渲染。 显然我错了,尽管其余的答案仍然很好,如果您只想使用单个 OpenGL 上下文来解决问题。

虽然我在制作Infinite Procedural Terrain Generator时遇到了同样的问题,但问题与您的问题相同,每次世界更新或生成新的地形块时,它都会冻结几分之一秒。

如何解决这个问题

基本上我如何解决这个问题是通过执行以下操作。

创建一个线程池/线程队列,每次世界发生变化时,您都会让一个单独的Thread进程/更新或重新创建FloatBuffer(或您使用的其他缓冲区)。因为通常这就是冻结的原因,仅仅是因为创建缓冲区、输入和更改所有数据等需要花费大量时间。

这是我的意思的布局。

class VBOAntiFreeze {
    FloatBuffer vertex_data;
    // Just add the rest of the FloatBuffers you use as well, like FloatBuffer
    // normal_data; FloatBuffer color_data; etc.

    // You would also have all the other variables as the vbo_handle, vertices count, etc.

    boolean fresh = true;
    boolean dirty, updating;

    public void updateVBO() {
        if (fresh && !updating) {
            updating = true;

            // You could execute new Threads by creating a
            // Thread Pool/Thread Queue, that way, you will
            // have some more control over all the threads.

            new Thread(new Runnable() {
                public void run() {
                    // Update and process all the FloatBuffers here!

                    dirty = true;
                    fresh = false;
                    updating = false;
                }
            }).start();
        }
    }

    public void renderVBO() {
        if (updating) {
            return;
        }
        else if (fresh) {
            updateVBO();

            return;
        }

        if (dirty) {
            // Buffer all the newly updated data
        }

        // Render the VBO here
    }
}

通过使用这个想法,你可能不会经历随机冻结,除非你的 VBO 非常大,因此if (dirty) buffer new data意志/可能仍然会冻结一点,尽管我以前从未经历过。但只是说,通知你!

于 2013-09-12T22:46:40.080 回答
1

我决定写下我的评论作为答案,因为它与绘图线程开销中的其他两个答案略有不同。我建议不要在上传顶点数据或取消映射缓冲区时暂停管道,而是建议在工作线程完成更新后使用双缓冲和交换用于绘制的 VBO 与用于提交数据的 VBO。

对于这种方法,您将需要两个渲染上下文,每个渲染上下文共享资源。

在您的工作线程中,您可以像在当前方法中通常那样将子数据分配/流式传输到 VBO,唯一的区别是您将对不用于绘图的 VBO 执行此操作。当你完成用数据填充这个 VBO 时,让你的绘图线程知道,然后在绘图时将用于绘图的 VBO 与用于流式传输顶点数据的 VBO 交换。在这种情况下,您的工作线程应该阻塞,直到绘图线程交换 VBO。

这样,当必须将新数据提交给 GPU 时,您不会停止绘图线程,而是会停止用于流式传输新数据的线程,直到绘图线程交换 VBO。因此,渲染会更平滑(更),但更新可能会以更可变的频率发生。这通常是交互式软件(如游戏)的理想特性——在弹出新内容之前的额外延迟帧通常比需要两倍时间才能完成的帧要好。

如果您想一次排队多个更新,以便您的更新线程不必经常阻塞,我建议您实现一个循环缓冲区,如他的回答中提到的 derhass。但听起来您只需要问题描述中的前/后缓冲区。

于 2013-09-12T23:36:20.247 回答
1

一般来说,一个 GL 上下文可以在任何时候对单个线程都是当前的,并且一个线程可以在任何时候拥有一个当前的 GL 上下文。如果您想对 GL 对象进行并行更新,您有两种选择:

  1. 使用共享上下文。这样,每个 trhead 都可以有自己的 GL 上下文,但是缓冲区纹理等对象可以由两个线程使用(和修改)。
  2. 对于您的特定情况。使用映射的 VBO(可能是环形缓冲区)可能就足够了。您只需要 GL 上下文来映射/取消映射缓冲区,但是当它被映射时,您可以在任意线程中访问它——根本不需要 GL 上下文。在这种情况下,通常会使用 VBO 的环形缓冲区来避免线程之间的过度同步。
于 2013-09-12T22:35:23.783 回答