opengl GL_SHADER_BUFFER的glGetBufferSubData和glMapBufferRange在NVIDIA GTX 960 M上非常慢

vbopmzt1  于 2023-10-18  发布在  其他
关注(0)|答案(1)|浏览(143)

我在将GPU缓冲区转移到CPU执行排序操作时遇到了一些问题。缓冲区是由300.000个浮点值组成的GL_SHADER_STORAGE_BUFFER。使用glGetBufferSubData的传输操作大约需要10ms,而使用glMapBufferRange,则需要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);
}

主要是做:

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);

其中countPixs是一个计算着色器,但我肯定问题不存在,因为如果我注解run命令,读取需要完全相同的时间。
奇怪的是,如果我执行一个只有1个float的getbuffer:

glGetBufferSubData(GL_SHADER_STORAGE_BUFFER,0, 1 *sizeof(float),&row[0]);

它需要同样的时间...所以我猜你一直都有问题可能与GL_SHADER_STORAGE_BUFFER有关?

inkz8wg9

inkz8wg91#

这可能是GPU-CPU同步/往返造成的延迟。也就是说,一旦Map了缓冲区,之前触及缓冲区的GL命令必须立即完成,从而导致管道停顿。请注意,驱动程序是懒惰的:很可能GL命令还没有开始执行。
如果可以的话:glBufferStorage(..., GL_MAP_PERSISTENT_BIT)并持久地Map缓冲区。这避免了完全重新Map和分配任何GPU内存,并且您可以将Map的指针保留在绘制调用上,但有一些警告:

  • 您可能还需要GPU围栏来检测/等待数据何时从GPU实际可用。(除非你喜欢阅读垃圾。)
  • 无法调整Map缓冲区的大小。(因为你已经使用了glBufferStorage(),所以没问题)
  • 最好将合并GL_MAP_PERSISTENT_BIT与GL_MAP_COHERENT_BIT结合使用

在阅读GL 4.5 docs之后,我发现glFenceSync是强制性的,以保证数据已经从GPU到达,即使有GL_MAP_COHERENT_BIT:
如果设置了GL_MAP_COHERENT_BIT并且服务器执行写入操作,则应用必须使用GL_SYNC_GPU_COMMANDS_COMPUTER(或glFinish)调用glFenceSync。然后,CPU将在同步完成后看到写入。

相关问题