我正在处理需要使用opengl渲染的PointCloud数据。我每帧都有一个新的数据点向量。我希望我能够缓存先前发送到opengl的数据,并且仅向其发送最新的帧数据。我该怎么办?
我进行了搜索,发现了这个想法here:
// Bind the old buffer to `GL_COPY_READ_BUFFER`
glBindBuffer (GL_COPY_READ_BUFFER, old_buffer);
// Allocate data for a new buffer
glGenBuffers (1, &new_buffer);
glBindBuffer (GL_COPY_WRITE_BUFFER, new_buffer);
glBufferData (GL_COPY_WRITE_BUFFER, ...);
// Copy `old_buffer_size`-bytes of data from `GL_COPY_READ_BUFFER`
// to `GL_COPY_WRITE_BUFFER` beginning at 0.
glCopyBufferSubData (GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, old_buffer_size);
但是看起来它终于在新缓冲区中发送了以前的数据和新数据,而不是仅缓存和发送了最新数据。因此,我不确定这是否是最好的方法。如果我错了,请改正我,或者提出其他选择。
答案 0 :(得分:2)
因此,您将一些数据存储在CPU内存中,并将更多数据附加到此存储中。然后,您只想将附加的数据发送到GPU,而不是整个缓冲区。
您的代码示例与该任务无关,因为glCopyBufferSubData
将数据从GPU内存中的位置再次复制到GPU内存中的另一个位置。
您需要glBufferData
和glBufferSubData
的组合。 glBufferData
在GPU中分配内存并对其进行优化。 glBufferSubData
将一些数据写入已分配的GPU缓冲区。您可以将glBufferData
视为C的malloc
或C ++ new
,而glBufferSubData
就像C的特殊版本memcpy或C ++ std::copy
。更准确地说,从CPU到GPU,glBufferSubData
是memcpy
,从GPU到GPU,glCopyBufferSubData
是memcpy
。
如何一起煮饭?与C中的方法相同。在初始化时(程序启动时)一次调用glBufferData
,并在需要附加数据时调用glBufferSubData
。确保分配足够的空间! glBufferData
分配的缓冲区以及malloc
分配的缓冲区不会增加。用glBufferSubData
溢出缓冲区会导致未定义的行为,并可能使您的应用程序崩溃。
尝试预测缓冲区的空间需求,仅在数据不适合缓冲区时调用glBufferData
。
请记住,使用已经分配的缓冲区绑定调用glBufferData
会取消分配现有缓冲区并创建一个新缓冲区。
glBufferSubData
不会重新分配缓冲区,但是会覆盖已经存在的数据。
让我用C翻译说明一下:
glGenBuffers(..., buf); // float* buf;
glBindBuffer(buf); // Tell opengl that we will use buf pointer, no analog in C.
glBufferData(/*non-null pointer*/); // buf = malloc(/*..*/); memcpy(to_gpu, from_cpu);
glBufferData(/*another non-null pointer*/); // free(buf); buf = malloc(/*..*/); memcpy(to_gpu, from_cpu);
glBufferSubData(...); // memcpy(to_gpu, from_cpu);
您需要的是:
glGenBuffers(..., buf); // float* buf;
glBindBuffer(buf); // Tell opengl that we will use buf pointer, no analog in C.
// Initialization
glBufferData(/*non-null pointer*/); // buf = malloc(/*..*/); memcpy(to_gpu, from_cpu);
// Hot loop
while (needToRender) {
if(needToAppend) {
if (dataDoesNotFit) glBufferData(...); // Reallocate, same buffer name
else glBufferSubData(...); // memcpy(to_gpu, from_cpu);
}
}
在这里,我们仅在需要追加某些内容且缓冲区太小时才偶尔重新分配内存。
我建议您用glBufferData
重新分配,因为您已经将所有数据放在CPU上的单个缓冲区中。如果没有(例如,您在GPU上有一块数据,在CPU上有另一块数据,但又不是在一起),则可以使用glCopyBufferSubData
重新分配:
glBufferData(/*alloc new_gpu_buffer*/);
glCopyBufferSubData(/*from old_gpu_buffer to new_gpu_buffer*/);
glDeleteBuffers(/*old_gpu_buffer*/);
glBufferSubData(/*from_cpu_buffer to new_cpu_buffer*/)p; // Add some new data from CPU.
另一种更新GPU数据的方法是mapping到CPU,因此您只需通过指针访问GPU内存即可。它可能很慢(阻塞缓冲区,使管道停滞),并且仅在特殊情况下有用。如果您知道自己的工作,请使用它。
答案 1 :(得分:2)
由于OpenGL是一个专注于绘制事物的API(暂时忽略计算着色器),并且绘制场景时通常是从一块空白画布开始的,因此您必须在整个过程中保留整个点云数据的完整积压。时间跨度,您希望能够重绘。
假设对于大量的点云数据,重绘整个集合可能需要一些时间,某种形式的机器似乎是合理的。但是,让我们先进行一些信封计算:
如今,典型的GPU完全能够以超过10 ^ 9顶点/秒的速率执行完整的顶点设置(已经在20年前,GPU能够以每秒20·10 ^ 6顶点的速度执行某些操作) 。您的典型计算机显示器的像素小于10·10 ^ 6。因此,根据信鸽原理,如果要绘制超过10·10 ^ 6的点,则会产生严重的透支或填满大部分像素;实际上,它会介于两者之间。
但是,正如我们已经看到的那样,GPU能够以交互式帧速率绘制许多点。绘制更多的图形可能会填满屏幕或遮挡数据。
如果您希望整个内容保持可读性,则需要某种形式的数据报废。对于任何可读的点云大小,您的GPU都可以很好地重绘整个内容。
考虑到需要数据回收,我建议您分配一个大缓冲区,该缓冲区能够在逐出之前保存整个点集,然后将其用作循环轮询缓冲区:在您要偏移的位置使用偏移量写入新数据时(使用glBufferSubData
),在边缘可能需要将其分成两个调用,以统一的形式传递最新的写入索引,以其年龄淡出点数,然后提交一次glDrawElements
调用即可一次性绘制该缓冲区的全部内容。