Recommendations for sending shader data back to host (for debugging)

For debugging complex shader code, such as algorithm implementations which compute lighting or vertex transformations in the shader code, I’d like to be able to send the results—such as vertices, normal vectors, colors—back to the host so I can look at them and see if they are as expected and to help identify sources of errors.
It appears there may be multiple ways to do this, for instance, using transform feedback to write vertex shader outputs back to a buffer and then read it on the host (using a memory mapped VBO?),
or writing to a texture and then mapping that on the host to read…

Can anyone please chime in on these (or other possibilities I’m not aware of yet), and the pros/cons, limitations/advantages of the various methods?

Transform feedback captures data associated with each vertex. The output is always from the last stage prior to primitive assembly and clipping (geometry shader, tessellation evaluation shader or vertex shader). When a geometry and/or tessellation evaluation shader are present, you can’t choose to capture values from an earlier stage via transform feedback.

There isn’t any mechanism to capture vertex data for vertices generated by clipping (analogous to glRenderMode(GL_FEEDBACK) for the legacy fixed-function pipeline).

Transform feedback can be used transparently; you can capture any per-vertex data via glTransformFeedbackVaryings without needing to modify the shaders (although you can also control transform feedback through xfb_* layout qualifiers in the shader).

Rendering to a framebuffer object allows capture of data associated with each fragment processed by the fragment shader.

Capturing fragment shader variables via render-to-texture requires modifying the fragment shader to store the values in output variables. This only works when rendering to a FBO (you can’t attach additional colour buffers to the default framebuffer), and can’t capture data for discarded fragments.

Additionally, any shader can be modified to store data in buffer variables (backed by SSBOs), atomic counters or images (imageStore etc).

One issue with any approach which requires modifying shaders is that the modifications may prevent the bug from manifesting. Shader compilers are fairly aggressive at eliminating dead code, but capturing intermediate values may turn dead code into live code.

1 Like

Thank you, very helpful. I’ve now gotten my shaders writing to simple SSBOs and 1D texture image for readback on the host app, these do just what I wanted, using gl_VertexID to provide unique output regions in the image/buffer to avoid race conditions.

I do have a question: when allocating storage for the texture buffer, it appears either glTextureStorage1D( ) or glTextureBuffer( ) can be used, but the type of storage used seems to be slightly different… a 1D texture image versus a buffer object attached to a buffer texture. I don’t fully get what the differences are. Is a texture image stored in a different part of GPU memory than a buffer object? are there other HW implications that could affect relative performance of these two options? Other considerations to be aware of when choosing one or the other?

A texture buffer object (TBO) is always backed by a buffer object, not a texture image.

Where did you see that the latter is valid?

And are you Checking for OpenGL errors? I suspect that when trying to somehow use glTextureStorage1D( ) for a TBO that you’re going to see at least one of these thrown by the GL driver.

No, it cannot. GL_TEXTURE_BUFFER is not a valid target that glTexureStorage1D takes.

Buffer textures are essentially a texture interface over a range of data in a buffer object.

As others have noted, you can’t interchange the two. A buffer texture is created by creating a buffer object, allocating storage then calling glTexBuffer or glTexBufferRange on it. From the shader side, a buffer texture behaves much like a 1D texture except you can’t sample a buffer texture; you can only read specific elements with texelFetch, or access it as an image.

As for which one to use, a buffer texture probably has a larger minimum size than a 1D texture, and you can map the underlying buffer object directly via glMapBuffer etc. You can’t map a texture’s storage but must use explicit read operations (e.g. glGetTexImage, or binding it to a FBO and using glReadPixels).

So for transferring data from a shader back to the client, there doesn’t seem to be much reason to use a 1D texture. Buffer textures were added in OpenGL 3.1, but to write to it from a shader you need image load/store which was added in 4.2. SSBOs were added in 4.3, and are probably the preferred approach unless you need to target systems having image load/store but lacking SSBOs or you actually want the data in a texture for some other reason.