Trivial SSBO implementation not working

Hello

I am trying to use a very simple Shader Storage Buffer Object inside a trivial fragment shader, however it is not working :doh:.

The vertex shader:

#version 430

layout(location = 0) in vec2 Vertex;

void main() {
	gl_Position = vec4(Vertex, 0.0, 1.0);
}

The fragment shader:

#version 430

layout(std430, binding = 0) buffer ColorSSBO {
	float color[];
};

out vec4 FragmentColor;

void main() {
	FragmentColor = vec4(color[0], color[1], color[2], 1.0);
}

I know that (apart the color array) this shader program is correct, because if I put the white color in FragmentColor, I see a white triangle in the center of the viewport.

The host code that initialize and bind the SSBO:

glGenBuffers(1, &ssbo);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo);
float color[] = {1.f, 1.f, 1.f};
glBufferData(GL_SHADER_STORAGE_BUFFER, 3*sizeof(float), color, GL_DYNAMIC_COPY);

glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, ssbo);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
assert(glGetError() == GL_NO_ERROR);

So no error is raised, but the result is a black screen. The shader program is active when I perform those calls. What’s wrong with my code?

gl_FragColor doesn’t exist in 430. You can try #version 430 Compatibility, or replace gl_FragColor with ‘out vec4 color_out;’.

I have changed it, but still it does not work.

Can you post more of your code, including how you draw the triangle and bind the shader?

Wait a minute. You changed the contents of the buffer on the CPU (when you set it initially with glBufferData). So… where is your memory barrier?

When using incoherent memory access methods (like SSBOs), changes to buffers are not automatically visible to subsequent commands. You must make them visible with an appropriate memory barrier, issued after making the change to the buffer object. This includes the initialization of that buffer’s data.

Sure, I post the whole code it’s short.

#include <GL/glew.h>
#include <GL/freeglut.h>
#include <iostream>
#include <cassert>

#define WIDTH 800
#define HEIGHT 480
using namespace std;

static void init();
static void initShaderProgram();
static void allocateSSBO();
static void display();
static void reshape(int,int);

static const char *vertexShader = R"(
	#version 430

	layout(location = 0) in vec2 Vertex;

	void main() {
		gl_Position = vec4(Vertex, 0.0, 1.0);
	}
)";

static const char *fragmentShader = R"(
	#version 430

	layout(std430, binding = 0) buffer ColorSSBO {
		float color[];
	};

	out vec4 FragmentColor;

	void main() {
		FragmentColor = vec4(color[0], color[1], color[2], 1.0);
	}
)";

static int window;
static GLuint shaders[3];
static GLuint vao;
static GLuint vbo;
static GLuint ssbo;

static void __stdcall DebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, GLvoid* userParam)  {
	cout << message << endl;
}

void init() {
	// Init FreeGLUT
	int temp = 0;
	glutInit(&temp, nullptr);
	glutInitContextVersion(4, 3);
	glutInitContextProfile(GLUT_CORE_PROFILE);
	glutInitContextFlags(GLUT_DEBUG);
	glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
	glutInitWindowSize(WIDTH, HEIGHT);
	window = glutCreateWindow("LinkedList-OIT");
	glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_GLUTMAINLOOP_RETURNS);
	glutReshapeFunc(reshape);
	glutDisplayFunc(display);
	glClearColor(0, 0, 0, 1);

	// Init GLEW
	glewExperimental = GL_TRUE;
	GLenum outcome = glewInit();
	if(outcome != GL_NO_ERROR)
		exit(-1);
	glGetError();

	initShaderProgram();
	glDebugMessageCallback((GLDEBUGPROC)DebugCallback, nullptr);
	glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);

	// Init center triangle
	float triangle[] = {
		+0.00, +0.67,
		+0.67, -0.67,
		-0.67, -0.67
	};
	glGenVertexArrays(1, &vao);
	glBindVertexArray(vao);
	glGenBuffers(1, &vbo);
	glBindBuffer(GL_ARRAY_BUFFER, vbo);
	glBufferData(GL_ARRAY_BUFFER, 2*sizeof(float)*3, triangle, GL_STATIC_DRAW);
	glBindBuffer(GL_ARRAY_BUFFER, 0);

	// Print debug info
	cout << glGetString(GL_VERSION) << endl;
}

static void checkShader(GLuint shader) {
	// Standard shader validation
}

static void checkProgram(GLuint program) {
	// Standard program validation
}

void initShaderProgram() {
	// Init vertex shader
	shaders[0] = glCreateShader(GL_VERTEX_SHADER);
	glShaderSource(shaders[0], 1, &vertexShader, nullptr);
	glCompileShader(shaders[0]);
	checkShader(shaders[0]);

	// Init fragment shader
	shaders[1] = glCreateShader(GL_FRAGMENT_SHADER);
	glShaderSource(shaders[1], 1, &fragmentShader, nullptr);
	glCompileShader(shaders[1]);
	checkShader(shaders[1]);

	// Link program
	shaders[2] = glCreateProgram();
	glAttachShader(shaders[2], shaders[0]);
	glAttachShader(shaders[2], shaders[1]);
	glLinkProgram(shaders[2]);
	checkProgram(shaders[2]);

	// Bind the compiled program
	glUseProgram(shaders[2]);
}

void allocateSSBO() {
	// Build and populate
	glGenBuffers(1, &ssbo);
	glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo);
	float color[] = {1.f, 1.f, 1.f};
	glBufferData(GL_SHADER_STORAGE_BUFFER, 3*sizeof(float), color, GL_DYNAMIC_COPY);

	glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, ssbo);
	glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
	assert(glGetError() == GL_NO_ERROR);
}

void reshape(int width, int height) {
	glViewport(0, 0, width, height);
}

void display() {
	glClear(GL_COLOR_BUFFER_BIT);

	glBindBuffer(GL_ARRAY_BUFFER, vbo);
	glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
	glEnableVertexAttribArray(0);
	glDrawArrays(GL_TRIANGLES, 0, 3);

	glutSwapBuffers();
	glutPostRedisplay();
}

int main(int argc, char **argv) {
	init();
	allocateSSBO();
	glutMainLoop();
}

Thank you for this.

Perhaps you could try clearing the viewport to a different color (like red) to see if the triangle is drawing black (SSBO data upload issue?) or not at all (shader problem?). Nothing jumps out at me as obviously wrong in your code.

You are missing connections to the shader programs entirely. Follow this tutorial GPU Buffers: Introduction to OpenGL 4.3 Shader Storage Buffers Objects | Geeks3D

EDIT:I was hasty and missed the part where you could use the hard coded binding index to skip the glShadeStorageBlockBinding() call.

I tried and I see a black triangle. So it seems to be a data upload issue, but I can’t find anything wrong too. I’m wondering if it could be a bug with the Catalyst.

I found this, which seemed to explain why the GLSL program was not working:

The rules for std140​ layout are covered quite well in the OpenGL specification (OpenGL 4.5, Section 7.6.2.2, page 137). Among the most important is the fact that arrays of types are not necessarily tightly packed. An array of float​s in such a block will not be the equivalent to an array of floats​ in C/C++. The array stride (the bytes between array elements) is always rounded up to the size of a vec4​ (ie: 16-bytes). So arrays will only match their C/C++ definitions if the type is a multiple of 16 bytes.

However this evening the program has started to work without modifications (!!). Honestly I don’t know what to think… maybe the driver was in good spirits. Anyway, it seems that to avoid any trouble it’s better to use only vec4 within uniform blocks.

I found this, which seemed to explain why the GLSL program was not working:

No it doesn’t. That’s talking about std140 layout rules. You explicitly asked for std430. And the principle difference between them is that std430 doesn’t do that.

The wiki is dangerously vague on this… it states that the std430 layout is almost identical to the std140, except for some unspecified optimizations. The 4.5 specs are more clear:

When using the std430 storage layout, shader storage blocks will be laid out in buffer storage identically to uniform and shader storage blocks using the std140 layout, except that the base alignment and stride of arrays of scalars and vectors in rule 4 and of structures in rule 9 are not rounded up a multiple of the base alignment of a vec4.

Quite a mess. I mean, I know GPU are complex devices, but I hadn’t such issues with CUDA.

1 Like

Albatross, after 7 years, you just saved my midterm. Thanks a lot!!