--- src/gl_manager.cc.orig 2026-04-27 01:16:06.062228168 +0000 +++ src/gl_manager.cc 2026-04-27 01:16:46.121276893 +0000 @@ -45,6 +45,25 @@ return Error::New(); } +// GL 2.0/2.1 compatible shaders (GLSL 1.20) +static const char vertex_source_gl2[] = + "#version 120\n" + "attribute vec2 in_position;" + "attribute vec2 in_texpos;" + "varying vec2 texpos;" + "void main() {" + "gl_Position = vec4(in_position, 0.0, 1.0);" + "texpos = in_texpos;" + "}"; + +static const char fragment_source_gl2[] = + "#version 120\n" + "uniform sampler2D tex;" + "varying vec2 texpos;" + "void main() {" + "gl_FragColor = texture2D(tex, texpos);" + "}"; + static const char vertex_source[] = "#version 330 core\n" @@ -87,13 +106,26 @@ Error GLManager::Initialize(int width, int height) { glClearColor(1, 1, 1, 0); + // Detect GL version to choose appropriate shaders + const char* gl_version = reinterpret_cast(glGetString(GL_VERSION)); + m_use_gl2 = false; + if (gl_version) { + int major = 0; + if (sscanf(gl_version, "%d", &major) == 1 && major < 3) { + m_use_gl2 = true; + } + } + + const char* vert_src = m_use_gl2 ? vertex_source_gl2 : vertex_source; + const char* frag_src = m_use_gl2 ? fragment_source_gl2 : fragment_source; + *m_vertex.id_ptr() = glCreateShader(GL_VERTEX_SHADER); - if (auto err = CompileShader(m_vertex.id(), vertex_source)) { + if (auto err = CompileShader(m_vertex.id(), vert_src)) { return err.Extend("while compiling vertex shader"); } *m_fragment.id_ptr() = glCreateShader(GL_FRAGMENT_SHADER); - if (auto err = CompileShader(m_fragment.id(), fragment_source)) { + if (auto err = CompileShader(m_fragment.id(), frag_src)) { return err.Extend("while compiling fragment shader"); } @@ -117,18 +149,31 @@ constexpr GLsizei stride = sizeof(float) * 4; - glGenVertexArrays(1, m_vao.id_ptr()); + // VAOs are required in GL 3.2+ Core Profile but optional in GL 2.x + m_have_vao = !m_use_gl2 || epoxy_has_gl_extension("GL_APPLE_vertex_array_object") || + epoxy_has_gl_extension("GL_ARB_vertex_array_object"); + if (m_have_vao) { + glGenVertexArrays(1, m_vao.id_ptr()); + glBindVertexArray(m_vao.id()); + } + glGenBuffers(1, m_vbo.id_ptr()); glGenBuffers(1, m_ebo.id_ptr()); - glBindVertexArray(m_vao.id()); - glBindBuffer(GL_ARRAY_BUFFER, m_vbo.id()); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ebo.id()); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); + // For GL 2.x, bind attribute locations manually before linking + if (m_use_gl2) { + glBindAttribLocation(m_program.id(), 0, "in_position"); + glBindAttribLocation(m_program.id(), 1, "in_texpos"); + // Re-link after binding attributes + glLinkProgram(m_program.id()); + } + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, stride, reinterpret_cast(0)); glEnableVertexAttribArray(0); @@ -165,6 +210,17 @@ glBindTexture(GL_TEXTURE_2D, m_texture.id()); glUseProgram(m_program.id()); - glBindVertexArray(m_vao.id()); + if (m_have_vao) { + glBindVertexArray(m_vao.id()); + } else { + // Rebind vertex attributes each frame for GL 2.x without VAO + constexpr GLsizei stride = sizeof(float) * 4; + glBindBuffer(GL_ARRAY_BUFFER, m_vbo.id()); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, stride, reinterpret_cast(0)); + glEnableVertexAttribArray(0); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, stride, reinterpret_cast(sizeof(float) * 2)); + glEnableVertexAttribArray(1); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ebo.id()); + } glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); }