I've written a program that uses OpenGL and GLFW to display some simple geometric shapes. I'm now trying to add some transform feedback capability to it and I find that as soon as I do so, the program stops displaying anything. So in pursuit of tracking down the problem I've started with a simple tutorial from the open.gl website that displays a red triangle (see https://open.gl/drawing for the original code) and then started adding the necessary transform feedback gl calls in order to reproduce the problem. The program is below:
#include <iostream>
#include <cmath>
#include <chrono>
#define GL_GLEXT_PROTOTYPES
#include <GLFW/glfw3.h>
#include <GL/gl.h>
// #include <GL/glu.h>
#include <thread>
#define gl_error_check do { GLenum err; while ((err = glGetError()) != GL_NO_ERROR) { fprintf(stderr, "gl error %d at %s:%d\n", err, __FILE__, __LINE__); } } while (0)
int main()
{
float vertices[] = {
0.0f, 0.5f, // Vertex 1 (X, Y)
0.5f, -0.5f, // Vertex 2 (X, Y)
-0.5f, -0.5f // Vertex 3 (X, Y)
};
GLuint vbo;
const char* vertexSource = R"glsl(
#version 330 core
in vec2 position;
// out float outValue;
void main()
{
gl_Position = vec4(position, 0.0, 1.0);
}
)glsl";
const char* fragmentSource = R"glsl(
#version 330 core
uniform vec3 triangleColor;
out vec4 outColor;
void main()
{
outColor = vec4(triangleColor, 1.0);
}
)glsl";
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL", nullptr, nullptr); // Windowed
glfwMakeContextCurrent(window);
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glGenBuffers(1, &vbo); // Generate 1 buffer
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexSource, NULL);
glCompileShader(vertexShader);
GLint status;
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &status);
if (status != GL_TRUE) {
std::cerr << "vertex shader compile failed " << status << std::endl;
return -1;
}
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentSource, NULL);
glCompileShader(fragmentShader);
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &status);
if (status != GL_TRUE) {
std::cerr << "fragment shader compile failed " << status << std::endl;
return -1;
}
GLuint shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glBindFragDataLocation(shaderProgram, 0, "outColor"); // not necessary
#if 0
const GLchar* feedbackVaryings[] = { "outValue" };
glTransformFeedbackVaryings(shaderProgram, 1, feedbackVaryings, GL_INTERLEAVED_ATTRIBS);
gl_error_check;
#endif
glLinkProgram(shaderProgram);
glUseProgram(shaderProgram);
GLint posAttrib = glGetAttribLocation(shaderProgram, "position");
glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(posAttrib);
GLint uniColor = glGetUniformLocation(shaderProgram, "triangleColor");
glUniform3f(uniColor, 1.0f, 0.0f, 0.0f);
GLfloat data[] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
// shader output buffer
GLuint tbo;
glGenBuffers(1, &tbo);
glBindBuffer(GL_ARRAY_BUFFER, tbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(data), nullptr, GL_STATIC_READ);
std::cout << "Init complete" << std::endl;
while(!glfwWindowShouldClose(window))
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, GL_TRUE);
// Clear the screen to black
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glUniform3f(uniColor, 1.0f, 0.0f, 0.0f);
// Draw a triangle from the 3 vertices
glDrawArrays(GL_TRIANGLES, 0, 3);
glfwSwapBuffers(window);
glfwPollEvents();
}
// std::this_thread::sleep_for(std::chrono::seconds(10));
glDeleteProgram(shaderProgram);
glDeleteShader(fragmentShader);
glDeleteShader(vertexShader);
glDeleteBuffers(1, &tbo);
glDeleteBuffers(1, &vbo);
glDeleteVertexArrays(1, &vao);
glfwTerminate();
return 0;
}
If I compile and run the program as shown above, it displays a red triangle, like so:
So far so good. In the code above, I've commented and ifdef'ed out a couple of what appear to be problematic lines. In the vertex shader, there's the declaration of the output variable outValue:
// out float outValue;
and then in the CPU code:
#if 0
const GLchar* feedbackVaryings[] = { "outValue" };
glTransformFeedbackVaryings(shaderProgram, 1, feedbackVaryings, GL_INTERLEAVED_ATTRIBS);
gl_error_check;
#endif
If I uncomment the declaration of outValue and change the 0 in the #if to a 1 so that the call to glTransformFeedbackVaryings() is compiled, the resulting executable displays only an empty window, no triangle.
So it seems that the call to glTransformFeedbackVaryings() is somehow breaking the rasterization process, even though so far the shader doesn't yet write anything to outValue nor does the CPU code try to read the feedback buffer.
Thus my question.
Is it a requirement that when a GL program is using transform feedback that it cannot actually render pixels to the screen?
Assuming this is not the case, is there something I'm missing?
Thanks in advance.
=============================================
Edit: updated the version declarations in the shaders from 150 to 330. I still get a blank window with the problematic lines enabled.
=============================================
Following Nathan Reed's suggestion, I added gl_error_check s following every gl API call. This one seems to be triggering the first error:
glUseProgram(shaderProgram); gl_error_check;
as the console shows:
~/opengl/matrix_code/draw_feed$ ./a.out
gl error 1282 at main.cpp:125
and line 125 is the call to glUseProgram(). The reported 1282 is 0x502 in hex which equates to:
GL_INVALID_OPERATION, 0x0502 Given when the set of state for a command is not legal for the parameters given to that command. It is also given for commands where combinations of parameters define what the legal parameters are.
When I lookup the reference to glUseProgram() at:
https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glUseProgram.xhtml
and read the list of errors at the bottom, it shows:
GL_INVALID_OPERATION is generated if program is not a program object.
GL_INVALID_OPERATION is generated if program could not be made part of current state.
GL_INVALID_OPERATION is generated if transform feedback mode is active.
That last reason for GL_INVALID_OPERATION seems pertinent, but its not clear to me what I should do about. Is there some call necessary to disable transform feedback mode during the call to glUseProgram()?
If so, do I turn it back on again after the call to glUseProgram()?
Also, I added this code to display the shader compile log:
glCompileShader(vertexShader); gl_error_check;
GLint bufflen;
glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &bufflen); gl_error_check;
if (bufflen > 1)
{
GLchar* log_string = new char[bufflen + 1];
glGetShaderInfoLog(vertexShader, bufflen, 0, log_string); gl_error_check;
printf("vertex shader: %s\n", log_string);
delete[] log_string;
}
But nothing gets logged.
And thanks for the suggestion about Debug Output. I was unaware of that feature. I'll bone up on that and add it to my code.