从计算着色器写入到持久映射的SSBO失败

时间:2018-07-05 00:36:43

标签: scala opengl lwjgl opengl-4

我正在尝试使用计算着色器写入SSBO并在cpu上读回数据。

计算着色器只是一个1x1x1玩具示例,其中写入了24个浮点数:

#version 450 core

layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;

layout (std430, binding = 0) buffer particles {
    float Particle[];
};

void main() {
    for (int i = 0; i < 24; ++i) {
        Particle[i] = i + 1;
    }
}

这是我运行着色器并读取数据的方式:

val bufferFlags = GL_MAP_READ_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT
val bufferSize = 24 * 4
val bufferId = glCreateBuffers()

glNamedBufferStorage(bufferId, bufferSize, bufferFlags)

val mappedBuffer = glMapNamedBufferRange(bufferId, 0, bufferSize, bufferFlags)

mappedBuffer.rewind()
val mappedFloatBuffer = mappedBuffer.asFloatBuffer()

mappedFloatBuffer.rewind()

val ssboIndex = glGetProgramResourceIndex(progId, GL_SHADER_STORAGE_BLOCK, "particles")

val props = Array(GL_BUFFER_BINDING)
val params = Array(-1)
glGetProgramResourceiv(progId, GL_SHADER_STORAGE_BLOCK, ssboIndex, props, null, params)

glBindBufferBase(GL_SHADER_STORAGE_BUFFER, params(0), bufferId)

glUseProgram(progId)

val sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0)
glDispatchCompute(1, 1, 1)

glClientWaitSync(sync, 0, 1000000000) match {
  case GL_TIMEOUT_EXPIRED =>
    println("Timeout expired")

  case GL_WAIT_FAILED =>
    println("Wait failed. " + glGetError())

  case _ =>
    println("Result:")

    while(mappedFloatBuffer.hasRemaining) {
      println(mappedFloatBuffer.get())
    }
}

我希望它可以打印数字1到24,但是可以打印24个零。使用cpu,我可以对缓冲区进行读写(如果设置了GL_MAP_WRITE_BIT)。如果我不使用DSA(glBindBuffer / glBufferStorage / glMapBufferRange),也会发生同样的情况。但是,如果在着色器运行时未映射缓冲区,而我仅在打印内容之前就映射了缓冲区,则一切正常。这不是持久映射缓冲区的确切用途吗?这样我就可以在GPU使用它时保持其映射状态了吗?

我用glGetError以及较新的调试输出检查了任何错误,但没有得到任何错误。

Here (pastebin)是一个完整的示例。您需要LWJGL来运行它。

1 个答案:

答案 0 :(得分:4)

您的代码中存在许多问题。

首先,将围栅同步放在要与之同步的命令之前。与篱笆同步将与在篱笆之前 而非之后执行的所有命令同步。如果要与计算着色器执行同步,则必须在分派调用之后而不是之前插入篱笆。

第二,同步还不够。写入SSBO的内容是不一致的,因此必须遵循incoherent memory accesses的规则才能使其对您可见。在这种情况下,您需要在计算操作之间以及尝试使用glMemoryBarrier从缓冲区读取数据时插入适当的内存屏障。由于您是通过映射读取数据的,因此使用的适当障碍是GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT

当您使用非持久映射时,您的代码似乎可以工作,但这仅仅是外观。由于不适当的不连贯的内存访问(即:缺少内存屏障),它仍然是不确定的行为。碰巧的是,UB会按照您的要求进行操作。