Kaskorian
Kaskorian

Reputation: 446

frame buffer size changes but viewport cannot be set

Simple and short question but I don't know why it is not working. When I call glViewport in the window frame buffer callback nothing changes. Error code is 1282 (0x502). I already googled and found no helpful article. Some said I should use glScissor in addition but does not work either.

Here an MCVE with an oscillating quad for visualization:

#include "GL/glew.h"
#include "GLFW/glfw3.h"
#include "glm/vec3.hpp"

#include <thread>
#include <iostream>
#include <vector>
#include <chrono>

#include <Windows.h>
#include <wingdi.h>

int createShaders() {
    const char* vertex = R"(
        #version 330 core

        layout(location = 0) in vec3 vertexPosition_modelspace;

        uniform float scale;

        void main() {
            gl_Position =  vec4(vertexPosition_modelspace, 1.0) * vec4(vec3(scale), 1.0f);
        }
    )";

    const char* fragment = R"(
        #version 330 core

        void main() {
            gl_FragColor = vec4(1.0f, 1.0f, 1.0f, 1.0f);
        }
    )";

    int vertexID, fragmentID, pid;

    vertexID = glCreateShader(GL_VERTEX_SHADER);

    const GLchar* source = (const GLchar*)vertex;
    glShaderSource(vertexID, 1, &source, 0);

    glCompileShader(vertexID);

    int isCompiled = 0;
    glGetShaderiv(vertexID, GL_COMPILE_STATUS, &isCompiled);
    if (isCompiled == GL_FALSE)
    {
        int maxLength = 0;
        glGetShaderiv(vertexID, GL_INFO_LOG_LENGTH, &maxLength);

        char* infoLog = new char[maxLength];
        glGetShaderInfoLog(vertexID, maxLength, &maxLength, infoLog);
        std::cout << infoLog << std::endl;

        glDeleteShader(vertexID);
        return 0;
    }

    fragmentID = glCreateShader(GL_FRAGMENT_SHADER);

    source = (const GLchar*)fragment;
    glShaderSource(fragmentID, 1, &source, 0);

    glCompileShader(fragmentID);

    glGetShaderiv(fragmentID, GL_COMPILE_STATUS, &isCompiled);
    if (isCompiled == GL_FALSE)
    {
        int maxLength = 0;
        glGetShaderiv(fragmentID, GL_INFO_LOG_LENGTH, &maxLength);

        char* infoLog = new char[maxLength];
        glGetShaderInfoLog(fragmentID, maxLength, &maxLength, infoLog);
        std::cout << infoLog << std::endl;

        glDeleteShader(fragmentID);
        glDeleteShader(vertexID);

        return 0;
    }

    pid = glCreateProgram();

    glAttachShader(pid, vertexID);
    glAttachShader(pid, fragmentID);

    glLinkProgram(pid);

    int isLinked = 0;
    glGetProgramiv(pid, GL_LINK_STATUS, (int*)&isLinked);
    if (isLinked == GL_FALSE)
    {
        int maxLength = 0;
        glGetProgramiv(pid, GL_INFO_LOG_LENGTH, &maxLength);

        std::vector<GLchar> infoLog(maxLength);
        glGetProgramInfoLog(pid, maxLength, &maxLength, &infoLog[0]);

        glDeleteProgram(pid);
        glDeleteShader(vertexID);
        glDeleteShader(fragmentID);

        return 0;
    }

    glDetachShader(pid, vertexID);
    glDetachShader(pid, fragmentID);

    return pid;
}

unsigned int createVertexArray() {
    std::vector<glm::vec3> vertices = {
        glm::vec3(0.5f, 0.5f, 0.0f),
        glm::vec3(0.5f, -0.5f, 0.0f),
        glm::vec3(-0.5f, -0.5f, 0.0f),
        glm::vec3(-0.5f, 0.5f, 0.0f)
    };

    unsigned int arrayID, id;

    glGenVertexArrays(1, &arrayID);
    glBindVertexArray(arrayID);

    glGenBuffers(1, &id);
    glBindBuffer(GL_ARRAY_BUFFER, id);
    glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(glm::vec3), &vertices[0], GL_STATIC_DRAW);

    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);

    return arrayID;
}

int main() {
    if (!glfwInit())
        return 1;

    GLFWwindow* window = glfwCreateWindow(960, 520, "MCVE", NULL, NULL);
    glfwSetFramebufferSizeCallback(window, [](GLFWwindow* window, int width, int height) {
        glViewport(0, 0, width, height);
    });

    glfwMakeContextCurrent(window);

    if (glewInit() != GLEW_OK)
        return 1;

    wglMakeCurrent(NULL, NULL);

    std::thread mainLoopThread([window]() {
        glfwMakeContextCurrent(window);

        std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();

        int shaderID = createShaders();
        int vertexID = createVertexArray();

        while (!glfwWindowShouldClose(window)) {
            std::chrono::steady_clock::time_point current = std::chrono::steady_clock::now();
            std::chrono::microseconds time = std::chrono::duration_cast<std::chrono::microseconds>(current - start);

            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
            
            glUseProgram(shaderID);
            float s = sin(time.count() / 1000000.0f * 2.0f);
            glUniform1f(glGetUniformLocation(shaderID, "scale"), s);
            glBindVertexArray(vertexID);
            glDrawArrays(GL_QUADS, 0, 4);
            
            glfwSwapBuffers(window);
        }
    });

    while (!glfwWindowShouldClose(window))
        glfwWaitEvents();

    mainLoopThread.join();

    glfwDestroyWindow(window);
    glfwTerminate();

    return 0;
}

Upvotes: 2

Views: 250

Answers (1)

Rabbid76
Rabbid76

Reputation: 211278

You try to call a OpenGL instruction without a current OpenGL Context. The callback is executed in the main thread. However the OpenGL Context is not current in the main thread. The OpenGL context is current in the mainLoopThread thread.
Set a state in the callback function (e.g. std::atomic<bool>) and invoke glViewport in the mainLoopThread thread, once the state is set.

Upvotes: 3

Related Questions