c++openglglslopengl-4

Buffer read-back working depends on order of bind/map with DSA


I am trying to perform read-back of SSBO data in OpenGL. For this I persistenly map the SSBO, but the following piece of code does not actually read back the written data from the example compute shader:

std::int32_t value = 12u;
GLuint ssbo = 0u;
glCreateBuffers(1u, &ssbo);
glNamedBufferStorage(ssbo, sizeof(value), &value, GL_MAP_READ_BIT | GL_MAP_PERSISTENT_BIT);
const auto* ptr = reinterpret_cast<const std::int32_t*>(glMapNamedBuffer(ssbo, GL_READ_WRITE));

glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, ssbo);
glDispatchCompute(1, 1, 1);
glFinish();
glMemoryBarrier(GL_ALL_BARRIER_BITS);

std::memcpy(&value, ptr, sizeof(value));
printf("Value: %d\n", value);

The compute shader looks like this:

#version 460

layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
layout (std430, binding = 0) writeonly buffer resBuf { int res[]; };

void main()
{
    res[0] = 25;
}

The expected result of value should be 25, however I still get back the initial value of 12. The glFinish and GL_ALL_BARRIER_BITS are just there to make sure that synchronization is not the issue.

Now, when I switch the order of glMapNamedBuffer and glBindBufferBase, I do get the correct read-back result - on an AMD GPU, that is. The read-back works in both cases on an Nvidia GPU.

I noticed that the documentation for glBindBuffer states that "The state of a buffer object immediately after it is first bound is an unmapped zero-sized memory buffer [...]". However, I am using DSA and there is little need to explicitly bind buffers for most operations, and glCreateBuffers also states that it creates a new buffer object "[...] as if it had been bound to an unspecified target."

Is this a bug with AMDs GL implementation or am I missing something? A simple workaround exists (simply bind the buffer to a bogus target), but I'd still like a clarification.


Solution

  • When you map a buffer persistently, you must map the buffer persistently. Allocating the buffer with the persistent flag only tells the system that you could map it persistently. Persistent mapping requires that the mapping call itself declare that the buffer is being mapped persistently.

    So glMapNamedBufferRange (which is the function you need to use) needs to have the persistent flag on it.