// Compile with c++ -std=c++14 [filepath] -Wall $(pkg-config --cflags --libs sdl2 gl) #define GL_GLEXT_PROTOTYPES #include #include #include #include #include #include #include namespace { const GLchar * const vertex_shader_source = R"( #version 130 uniform float rotation; void main() { // Draw a triangle. // Avoid having edges aligned to even numbered pixels so all // edges reproduce the bug. float angle = float(gl_VertexID) * 2.094395102 + rotation; gl_Position = vec4(sin(angle), -cos(angle), 0.5, 1.0); // Constant across triangle gl_FrontColor = vec4(0.5); } )"; const GLchar * const fragment_shader_source = R"( #version 130 #extension GL_ARB_explicit_attrib_location : enable layout(location = 0) out vec4 colour; void main() { // gl_Color is constant, so the derivative should be 0, but it is not at // the edges of the triangle. // The bug also reproduces with gl_FrontSecondaryColor in the vertex // shader and gl_SecondaryColor here. colour = fwidth(gl_Color); } )"; void attach_shader(GLuint program, const char * const source, GLenum type) { const GLuint shader = glCreateShader(type); glShaderSource(shader, 1, const_cast(&source), 0); glCompileShader(shader); GLint is_compiled; glGetShaderiv(shader, GL_COMPILE_STATUS, &is_compiled); if (is_compiled == GL_FALSE) { GLint max_length; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &max_length); const auto info_log = std::make_unique(max_length); glGetShaderInfoLog(shader, max_length, &max_length, info_log.get()); std::cerr << "Error compiling vertex shader:\n" << info_log.get(); std::exit(1); } glAttachShader(program, shader); glDeleteShader(shader); } } int main() { SDL_Init(SDL_INIT_VIDEO); SDL_Window * const window = SDL_CreateWindow( "Test", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 256, 256, SDL_WINDOW_OPENGL); if (!window) { std::cerr << "Could not create window: " << SDL_GetError() << '\n'; return 1; } SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); SDL_GLContext gl_context = SDL_GL_CreateContext(window); if (gl_context == NULL) { std::cerr << "Could not create OpenGL 2.1 context: " << SDL_GetError() << '\n'; return 1; } // Since we only write the front colour, be certain the back colour // won't be used. glEnable(GL_CULL_FACE); GLuint vertex_array; glGenVertexArrays(1, &vertex_array); glBindVertexArray(vertex_array); const GLuint program = glCreateProgram(); attach_shader(program, fragment_shader_source, GL_FRAGMENT_SHADER); attach_shader(program, vertex_shader_source, GL_VERTEX_SHADER); glLinkProgram(program); GLint is_linked; glGetProgramiv(program, GL_LINK_STATUS, &is_linked); if (is_linked == GL_FALSE) { GLint max_length; glGetProgramiv(program, GL_INFO_LOG_LENGTH, &max_length); const auto info_log = std::make_unique(max_length); glGetProgramInfoLog(program, max_length, &max_length, info_log.get()); std::cerr << "Error linking shader:\n" << info_log.get(); return 1; } glUseProgram(program); // Draw a triangle rotating for a while, then pause. for (unsigned int i = 0; i < 120; i++) { glClear(GL_COLOR_BUFFER_BIT); glUniform1f(0, i * 0.02); glDrawArrays(GL_TRIANGLES, 0, 3); SDL_GL_SwapWindow(window); } SDL_Delay(1500); SDL_GL_DeleteContext(gl_context); SDL_DestroyWindow(window); SDL_Quit(); }