从计算着色器写入持久映射的 SSBO 失败
Write from compute shader to persistently mapped SSBO fails
我正在尝试使用计算着色器写入 SSBO 并在 cpu 上读回数据。
计算着色器只是一个写入 24 个浮点数的 1x1x1 玩具示例:
#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
),也会发生同样的情况。但是,如果在着色器 运行s 时未映射缓冲区,而我只是在打印内容之前映射它,则一切正常。这不正是持久映射缓冲区的用途吗?所以我可以在 gpu 使用它时保持它的映射?
我用 glGetError
以及较新的调试输出检查了任何错误,但我没有得到任何错误。
Here (pastebin) is a fully working example. You need LWJGL到运行吧。
你的代码中有很多问题。
首先,将 fence sync 放在要与之同步的命令之前。与栅栏同步会与栅栏之前执行的所有命令同步,而不是之后。如果你想与计算着色器执行同步,那么你必须在调度调用之后插入栅栏,而不是之前。
第二,同步不够。写入 SSBO 是不连贯的,因此您必须遵循 incoherent memory accesses 的规则才能使它们对您可见。在这种情况下,您需要在计算操作和尝试使用 glMemoryBarrier
从缓冲区读取时插入适当的内存屏障。由于您是通过映射读取数据,因此正确使用的障碍是 GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT
.
当您使用非持久映射时,您的代码似乎可以正常工作,但这仅仅是表面现象。由于不正确的不连贯内存访问(即:缺少内存屏障),它仍然是未定义的行为。碰巧UB做了你想要的......在那种情况下。
我正在尝试使用计算着色器写入 SSBO 并在 cpu 上读回数据。
计算着色器只是一个写入 24 个浮点数的 1x1x1 玩具示例:
#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
),也会发生同样的情况。但是,如果在着色器 运行s 时未映射缓冲区,而我只是在打印内容之前映射它,则一切正常。这不正是持久映射缓冲区的用途吗?所以我可以在 gpu 使用它时保持它的映射?
我用 glGetError
以及较新的调试输出检查了任何错误,但我没有得到任何错误。
Here (pastebin) is a fully working example. You need LWJGL到运行吧。
你的代码中有很多问题。
首先,将 fence sync 放在要与之同步的命令之前。与栅栏同步会与栅栏之前执行的所有命令同步,而不是之后。如果你想与计算着色器执行同步,那么你必须在调度调用之后插入栅栏,而不是之前。
第二,同步不够。写入 SSBO 是不连贯的,因此您必须遵循 incoherent memory accesses 的规则才能使它们对您可见。在这种情况下,您需要在计算操作和尝试使用 glMemoryBarrier
从缓冲区读取时插入适当的内存屏障。由于您是通过映射读取数据,因此正确使用的障碍是 GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT
.
当您使用非持久映射时,您的代码似乎可以正常工作,但这仅仅是表面现象。由于不正确的不连贯内存访问(即:缺少内存屏障),它仍然是未定义的行为。碰巧UB做了你想要的......在那种情况下。