I'm trying to write a compute shader in GLSL with OpenGL, GLFW, GLEW, and GLM. The problem is that the compute shader either isn't running or isn't modifying any buffers. I have the latest NVIDIA 4060 Ti Drivers and I'm working on WSL on Windows with Mesa.
Code (this and glew.c (I know, hacky) goes in src directory, glm should go in include):
#include <iostream>
#include <cstring>
#include <GLFW/glfw3.h>
#include "glm.hpp"
#include <GL/gl.h>
int main(int argc, char **argv) {
//Window width and height
int window_w = 640, window_h = 480;
printf("Startup\n");
//Init and create window
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, false);
glfwWindowHint(GLFW_SAMPLES, 4);
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, true);
std::cout << "Trying to create window" << std::endl;
GLFWwindow* window = glfwCreateWindow(window_w, window_h, "Marching Cubes", nullptr, nullptr);
std::cout << "Just tried to create window\n" << std::endl;
if (window == nullptr)
{
std::cerr << "Failed to create GLFW window" << std::endl;
glfwTerminate();
exit(EXIT_FAILURE);
}
else {
std::cerr << "Succeeded at creating window" << std::endl;
}
//More init
glfwMakeContextCurrent(window);
printf("Made the window the current context\n");
glEnable ( GL_DEBUG_OUTPUT );
glewInit();
// The buffer for use in the compute shader
uint32_t buffer_vertices;
uint32_t buffer_size = 10;
glCreateBuffers(1, &buffer_vertices);
glNamedBufferStorage(buffer_vertices, buffer_size, nullptr, GL_DYNAMIC_STORAGE_BIT);
printf("Buffers have been created\n");
// Create the VAO for drawing the resulting triangles
uint32_t vao_draw;
glCreateVertexArrays(1, &vao_draw);
glEnableVertexArrayAttrib(vao_draw, 0);
glEnableVertexArrayAttrib(vao_draw, 1);
glVertexArrayVertexBuffer(vao_draw, 0, buffer_vertices, 0, sizeof(glm::vec4));
glVertexArrayAttribFormat(vao_draw, 0, 4, GL_FLOAT, false, 0);
glVertexArrayAttribFormat(vao_draw, 1, 4, GL_FLOAT, false, 0);
glVertexArrayAttribBinding(vao_draw, 0, 0);
glVertexArrayAttribBinding(vao_draw, 1, 1);
std::cout << "GLSL Version: " << glGetString(GL_SHADING_LANGUAGE_VERSION) << std::endl;
glClearColor(1.0, 1.0, 1.0, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBegin(GL_TRIANGLES);
// The compute shader
char* smcsource = "\n\
#version 430\n\
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n\
layout(std430, binding = 0) buffer u_buffer_vertices\n\
{\n\
vec4 output_vertices[];\n\
};\n\
\n\
void main() {\n\
for (float i = 0.0; i < 10.0; i++) output_vertices[int(i)] = vec4(i,i,i,i);\n\
}\n\
";
//Compile the shader
GLint length = strlen(smcsource);
GLuint compute_shader = glCreateShader(GL_COMPUTE_SHADER);
glShaderSource(compute_shader, 1, &smcsource, &length);
glCompileShader(compute_shader);
GLint smcsuccess = 0;
glGetShaderiv(compute_shader, GL_COMPILE_STATUS, &smcsuccess);
GLuint smcprogram;
if (smcsuccess) {
printf("Successfully compiled\n");
smcprogram = glCreateProgram();
glAttachShader(smcprogram, compute_shader);
glLinkProgram(smcprogram);
GLint TotalLength = 0;
glGetShaderiv(compute_shader, GL_INFO_LOG_LENGTH, &TotalLength);
std::string logs;
logs.resize(TotalLength);
glGetShaderInfoLog(compute_shader, TotalLength, NULL, &logs[0]);
std::cout << "Shader output log:\n" << logs << std::endl;
}
else {
printf("Failed compile\n");
GLint TotalLength = 0;
glGetShaderiv(compute_shader, GL_INFO_LOG_LENGTH, &TotalLength);
std::string logs;
logs.resize(TotalLength);
glGetShaderInfoLog(compute_shader, TotalLength, NULL, &logs[0]);
std::cout << "Shader output log:\n" << logs << std::endl;
}
//Select shader
glUseProgram(smcprogram);
//Bind buffer
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, buffer_vertices);
printf("Buffer binded\n");
//Run shader. Problem happens here: nothing happens to the buffer when I run this
glDispatchCompute(1, 1, 1);
glMemoryBarrier(GL_ALL_BARRIER_BITS);
//Test and print the buffer data
//If this is working as intended, it should count up in groups of four, something like:
//0.0 0.0 0.0 0.0 | 1.0 1.0 1.0 1.0 | 2.0 2.0 2.0 2.0 | 3.0 3.0 3.0 3.0 | etc.
glm::vec4 vertices[16];
glGetBufferSubData(buffer_vertices, 0, 16, &vertices);
printf("Vertex data after: ");
for (int i = 0; i < 10; i++)
printf("%f %f %f %f | ", vertices[i][0], vertices[i][1], vertices[i][2], vertices[i][3]);
printf("\n");
glEnd();
glfwSwapBuffers(window);
return 0;
}
Can be compiled with:
g++ src/main.cpp src/glew.c -Iinclude -lGLEW -lglut -lglfw -lGL -lX11 -lc -o shader-compiler-bug
Like the comments say, based on the compute shader it should (along with the debugging messages) print:
0.0 0.0 0.0 0.0 | 1.0 1.0 1.0 1.0 | 2.0 2.0 2.0 2.0 | 3.0 3.0 3.0 3.0 | [etc. to 10]
Instead, that part of it prints:
-11653242706460672.000000 0.000000 0.000000 0.000000 | -496068722688.000000 0.000000 -496183017472.000000 0.000000 | -496015245312.000000 0.000000 -496055091200.000000 0.000000 | -496050896896.000000 0.000000 -496059285504.000000 0.000000 | -496066625536.000000 0.000000 -447767117824.000000 0.000000 | 0.000000 0.000000 0.000000 -0.000000 | 0.000000 0.000000 0.000000 0.000000 | 0.000000 0.000000 0.000000 0.000000 | 0.000000 0.000000 0.000000 0.000000 | -496055091200.000000 0.000000 -496025731072.000000 0.000000 |
(It's different every time though, some attempts are full of zeroes or have NaNs in the output)
A few issues:
Deprecated functions like glBegin()
/glEnd()
don't work in Core contexts; remove them.
The size
parameter of glNamedBufferStorage()
is in bytes, not elements. So instead of just buffer_size
you need buffer_size * sizeof(glm::vec4)
:
glNamedBufferStorage(buffer_vertices, buffer_size * sizeof(glm::vec4), nullptr, GL_DYNAMIC_STORAGE_BIT);
The arguments to glGetBufferSubData()
call make it look like you intended to use glGetNamedBufferSubData()
; the call also suffers from the bytes vs. elements issue from before. Corrected:
glGetNamedBufferSubData(buffer_vertices, 0, buffer_size * sizeof(glm::vec4), &vertices);
All together (apologies for the GLAD conversion, it's what I had handy):
#include <glad/glad.h>
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <vector>
#include <iostream>
void CheckStatus( GLuint obj, bool isShader )
{
GLint status = GL_FALSE, log[ 1 << 11 ] = { 0 };
( isShader ? glGetShaderiv : glGetProgramiv )( obj, isShader ? GL_COMPILE_STATUS : GL_LINK_STATUS, &status );
if( status == GL_TRUE ) return;
( isShader ? glGetShaderInfoLog : glGetProgramInfoLog )( obj, sizeof( log ), NULL, (GLchar*)log );
std::cerr << (GLchar*)log << "\n";
std::exit( EXIT_FAILURE );
}
void AttachShader( GLuint program, GLenum type, const char* src )
{
GLuint shader = glCreateShader( type );
glShaderSource( shader, 1, &src, NULL );
glCompileShader( shader );
CheckStatus( shader, true );
glAttachShader( program, shader );
glDeleteShader( shader );
}
const char* const comp = R"GLSL(
#version 430
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
layout(std430, binding = 0) buffer u_buffer_vertices
{
vec4 output_vertices[];
};
void main() {
for (float i = 0.0; i < 10.0; i++) {
output_vertices[int(i)] = vec4(i,i,i,i);
}
}
)GLSL";
int main( int, char** )
{
glfwSetErrorCallback( []( int, const char* desc ) { std::cerr << desc << "\n"; std::exit( EXIT_FAILURE ); } );
glfwInit();
glfwWindowHint( GLFW_CONTEXT_VERSION_MAJOR, 4 );
glfwWindowHint( GLFW_CONTEXT_VERSION_MINOR, 6 );
glfwWindowHint( GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE );
glfwWindowHint( GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE );
GLFWwindow* window = glfwCreateWindow( 640, 480, "GLFW", NULL, NULL );
glfwMakeContextCurrent( window );
gladLoadGLLoader( (GLADloadproc)glfwGetProcAddress );
GLuint prog = glCreateProgram();
AttachShader( prog, GL_COMPUTE_SHADER, comp );
glLinkProgram( prog );
CheckStatus( prog, false );
glUseProgram( prog );
// The buffer for use in the compute shader
uint32_t buffer_vertices;
uint32_t buffer_size = 10;
glCreateBuffers( 1, &buffer_vertices );
glNamedBufferStorage( buffer_vertices, buffer_size * sizeof(glm::vec4), nullptr, GL_DYNAMIC_STORAGE_BIT );
//Bind buffer
glBindBufferBase( GL_SHADER_STORAGE_BUFFER, 0, buffer_vertices );
//Run shader.
glDispatchCompute( 1, 1, 1 );
glMemoryBarrier( GL_ALL_BARRIER_BITS );
glm::vec4 vertices[10];
glGetNamedBufferSubData( buffer_vertices, 0, buffer_size * sizeof(glm::vec4), &vertices );
printf("Vertex data after: \n");
for( int i = 0; i < 10; i++ )
{
printf( "%f %f %f %f \n", vertices[i][0], vertices[i][1], vertices[i][2], vertices[i][3] );
}
printf( "\n" );
glfwTerminate();
return 0;
}
Output:
Vertex data after:
0.000000 0.000000 0.000000 0.000000
1.000000 1.000000 1.000000 1.000000
2.000000 2.000000 2.000000 2.000000
3.000000 3.000000 3.000000 3.000000
4.000000 4.000000 4.000000 4.000000
5.000000 5.000000 5.000000 5.000000
6.000000 6.000000 6.000000 6.000000
7.000000 7.000000 7.000000 7.000000
8.000000 8.000000 8.000000 8.000000
9.000000 9.000000 9.000000 9.000000