用于 GL_SHADER_STORAGE_BUFFER 的 glGetBufferSubData 和 glMapBufferRange 在 NVIDIA GTX960M 上非常慢

pro*_*ose 7 c++ opengl glsl shader-storage-buffer

我在将 GPU 缓冲区传输到 CPU 以执行排序操作时遇到了一些问题。缓冲区GL_SHADER_STORAGE_BUFFER由 300.000 个浮点值组成。传输操作 withglGetBufferSubData大约需要 10ms,而 withglMapBufferRange则需要超过 100 ms。

我使用的代码如下:

std::vector<GLfloat> viewRow;
unsigned int viewRowBuffer = -1;
int length = -1;

void bindRowBuffer(unsigned int buffer){
    glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer);
    glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, buffer);
}

void initRowBuffer(unsigned int &buffer, std::vector<GLfloat> &row, int lengthIn){
    // Generate and initialize buffer
    length = lengthIn;
    row.resize(length);
    memset(&row[0], 0, length*sizeof(float));
    glGenBuffers(1, &buffer);
    bindRowBuffer(buffer);
    glBufferStorage(GL_SHADER_STORAGE_BUFFER, row.size() * sizeof(float), &row[0], GL_DYNAMIC_STORAGE_BIT | GL_MAP_READ_BIT | GL_MAP_WRITE_BIT);

    glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
}

void cleanRowBuffer(unsigned int buffer) {
    float zero = 0.0;
    glClearNamedBufferData(buffer, GL_R32F, GL_RED, GL_FLOAT, &zero);
}

void readGPUbuffer(unsigned int buffer, std::vector<GLfloat> &row) {
    glGetBufferSubData(GL_SHADER_STORAGE_BUFFER,0,length *sizeof(float),&row[0]);
    glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
}

void readGPUMapBuffer(unsigned int buffer, std::vector<GLfloat> &row) {
    float* data = (float*)glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, length*sizeof(float), GL_MAP_READ_BIT); glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
     memcpy(&row[0], data, length *sizeof(float));
    glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
}
Run Code Online (Sandbox Code Playgroud)

主要是做:

    bindRowBuffer(viewRowBuffer);
    cleanRowBuffer(viewRowBuffer);
    countPixs.bind();
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, gPatch);
    countPixs.setInt("gPatch", 0);
    countPixs.run(SCR_WIDTH/8, SCR_HEIGHT/8, 1);
    countPixs.unbind();
    readGPUbuffer(viewRowBuffer, viewRow);
Run Code Online (Sandbox Code Playgroud)

countPixs 是一个计算着色器,但我肯定问题不存在,因为如果我注释运行命令,读取所需的时间完全相同。

奇怪的是,如果我执行只有 1 个浮点数的 getbuffer:

glGetBufferSubData(GL_SHADER_STORAGE_BUFFER,0, 1 *sizeof(float),&row[0]);
Run Code Online (Sandbox Code Playgroud)

它需要完全相同的时间......所以我猜一直有问题......可能与GL_SHADER_STORAGE_BUFFER

JAT*_*rim 3

这很可能是 GPU-CPU 同步/往返导致的延迟。即,一旦映射缓冲区,之前接触缓冲区的 GL 命令必须立即完成,从而导致管道停顿。请注意,驱动程序是惰性的:很可能 GL 命令还没有开始执行

如果可以:glBufferStorage(..., GL_MAP_PERSISTENT_BIT)并持久映射缓冲区。这可以避免完全重新映射和分配任何 GPU 内存,并且您可以将映射的指针保留在绘制调用上,但需要注意一些事项:

  • 您可能还需要 GPU 栅栏来检测/等待 GPU 实际提供数据的时间。(除非你喜欢读垃圾书。)
  • 无法调整映射缓冲区的大小。(因为你已经使用了 glBufferStorage() 你就可以了)
  • 将 GL_MAP_PERSISTENT_BIT 与 GL_MAP_COHERENT_BIT 结合起来可能是个好主意

在阅读了更多 GL 4.5文档后,我发现这glFenceSync是强制性的,以保证数据已从 GPU 到达,即使使用 GL_MAP_COHERENT_BIT 也是如此:

如果设置了 GL_MAP_COHERENT_BIT 并且服务器进行写入,则应用程序必须使用 GL_SYNC_GPU_COMMANDS_COMPLETE (或 glFinish)调用 glFenceSync。然后,CPU 将在同步完成后看到写入。