EWoodward
EWoodward

Reputation: 15

GLFW hangs on close when adding an extra rendering call

So I've been attempting to learn how to program openGL for the past little while and although it has been mind-bending a lot of the time I think I'm beginning to gain a solid understanding of how it works.

I've been using GLFW for window handling and basic input, and GLEW for accessing the extension methods.

I've also been following the tutorial over at www.learnopengl.com and that has been fairly useful. Lately, though, I've been spending some time trying to create some basic abstractions for drawing to screen. When I abstracted away some of the code that the tutorial provided everything went fine (I'm still at the co-ordinate system section of "Getting Started").

After doing that I decided it would be nice to emulate a simple UI overlay where I just have a function that draws a rectangle to the screen in 2-D as opposed to 3-D and just floats above everything else on the screen. Eventually I got something working with different shaders for the UI and the 3-D objects. It successfully draws a colored rectangle to the screen on top of everything else, BUT unfortunately I have this bizarre problem that whenever I try to close the window by setting the glfwSetWindowShouldClose call to true the window can hang indefinitely.

Whenever I remove the call to draw the simple 2-D rectangle this hang goes away and the window closes immediately as expected. Does anyone have any ideas why this might be the case?


Main.cpp

// GLEW
#define GLEW_STATIC
#include <GL/glew.h>

// GLFW
#include <GLFW/glfw3.h>

// GL includes
#include "shader.h"

// GLM Mathemtics
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

// Other Libs
#include <SOIL.h>
#include "glfw_x360_button_mappings.h"
#include "cube.h"
#include "texture.h"

// Function prototypes
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
void handleControllerInput(GLFWwindow* window);

// GLOBALS!!!
const unsigned int MONITOR_WIDTH = 1920;
const unsigned int MONITOR_HEIGHT = 1080;
const unsigned int SCREEN_WIDTH = 800;
const unsigned int SCREEN_HEIGHT = 600;
float camera_z = -3.0f;
float camera_x = 0.0f;

// The MAIN function, from here we start our application and run our Game loop
int main()
{
    // Init GLFW
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

    GLFWwindow* window = glfwCreateWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "LearnOpenGL", nullptr, nullptr); // Windowed
    // TODO: Make window position adapt to any sized window.
    glfwSetWindowPos(window, (MONITOR_WIDTH / 2) - (SCREEN_WIDTH / 2), (MONITOR_HEIGHT / 2) - (SCREEN_HEIGHT / 2));
    glfwMakeContextCurrent(window);

    // Set the required callback functions
    glfwSetKeyCallback(window, key_callback);

    // Initialize GLEW to setup the OpenGL Function pointers
    glewExperimental = GL_TRUE;
    glewInit();

    // Define the viewport dimensions
    glViewport(0, 0, 800, 600);

    // Setup OpenGL options
    glEnable(GL_DEPTH_TEST);

    // Setup and compile our shaders
    GLuint vert_id = buildShader("shader.vert", GL_VERTEX_SHADER);
    GLuint frag_id = buildShader("shader.frag", GL_FRAGMENT_SHADER);
    GLuint shader_program_id = buildProgram(vert_id, frag_id);

    glDeleteShader(vert_id);
    glDeleteShader(frag_id);

    vert_id = buildShader("shader_ui.vert", GL_VERTEX_SHADER);
    frag_id = buildShader("shader_ui.frag", GL_FRAGMENT_SHADER);
    GLuint ui_shader_program_id = buildProgram(vert_id, frag_id);

    glDeleteShader(vert_id);
    glDeleteShader(frag_id);

    // World space positions of our cubes
    glm::vec3 cubePositions[] = {
        glm::vec3(0.0f, 0.0f, 0.0f),
        glm::vec3(2.0f, 5.0f, -15.0f),
        glm::vec3(-1.5f, -2.2f, -2.5f), 
        glm::vec3(-3.8f, -2.0f, -12.3f), 
        glm::vec3(2.4f, -0.4f, -3.5f), 
        glm::vec3(-1.7f, 3.0f, -7.5f), 
        glm::vec3(1.3f, -2.0f, -2.5f), 
        glm::vec3(1.5f, 2.0f, -2.5f),
        glm::vec3(1.5f, 0.2f, -1.5f),
        glm::vec3(-1.3f, 1.0f, -1.5f) 
    };

    // Load and create a texture
    GLuint texture1 = create_texture("container.jpg");
    GLuint texture2 = create_texture("awesomeface.png");

    glm::mat4 view_matrix;
    glm::mat4 view_matrix_origin;
    glm::mat4 projection_matrix;

    view_matrix_origin = glm::translate(view_matrix, glm::vec3(0.0f, 0.0f, 0.0f));
    projection_matrix = glm::perspective(45.0f, (float) 512 / (float) 512, 0.1f, 1000.0f);

    GLint view_matrix_location = glGetUniformLocation(shader_program_id, "view");
    GLint projection_matrix_location = glGetUniformLocation(shader_program_id, "projection");

    glUseProgram(shader_program_id);
    glUniformMatrix4fv(projection_matrix_location, 1, GL_FALSE, glm::value_ptr(projection_matrix));

    Cube test_cube;
    init_cube(&test_cube, cube_vertices, sizeof(cube_vertices));

    // Game loop
    while(!glfwWindowShouldClose(window))
    {
        // Check and call events
        glfwPollEvents();
        handleControllerInput(window);

        // Clear the colorbuffer
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        glUseProgram(shader_program_id);

        // Bind Textures using texture units
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, texture1);
        glUniform1i(glGetUniformLocation(shader_program_id, "ourTexture1"), 0);
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, texture2);
        glUniform1i(glGetUniformLocation(shader_program_id, "ourTexture2"), 1);

        glUniform1f(glGetUniformLocation(shader_program_id, "time"), glfwGetTime());

        view_matrix = glm::translate(view_matrix_origin, glm::vec3(camera_x, 0.0f, camera_z));
        glUniformMatrix4fv(view_matrix_location, 1, GL_FALSE, glm::value_ptr(view_matrix));

        /* THIS IS THE OFFENDING DRAW CALL
         * IF REMOVED THE WINDOW STOPS HANGING ON CLOSE */
        draw_rect(ui_shader_program_id, glm::vec2(0.0f, 0.0f));

        for (GLuint i = 0; i < 10; i++)
        {
            GLfloat angle = glfwGetTime() * 25.0f;
            draw_cube(&test_cube, shader_program_id, cubePositions[i], angle);
        }

        // Swap the buffers
        glfwSwapBuffers(window);
    }

    glfwTerminate();
    return 0;
}

// Is called whenever a key is pressed/released via GLFW
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
    std::cout << key << std::endl;
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
    {
        glfwSetWindowShouldClose(window, GL_TRUE);
    }
}

void handleControllerInput(GLFWwindow* window)
{
    if (glfwJoystickPresent(GLFW_JOYSTICK_1))
    {
        int size;
        const unsigned char* results = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &size);

        if (get_current_button_pressed(results, size) == X360_DPAD_DOWN)
        {
            camera_z += 0.001f;
        }
        else if (get_current_button_pressed(results, size) == X360_DPAD_UP)
        {
            camera_z -= 0.001f;
        }

        if (get_current_button_pressed(results, size) == X360_DPAD_LEFT)
        {
            camera_x -= 0.001f;
        }
        else if (get_current_button_pressed(results, size) == X360_DPAD_RIGHT)
        {
            camera_x += 0.001f;
        }

        if (get_current_button_pressed(results, size) == X360_B_BUTTON)
        {
            camera_z = -3.0f;
            camera_x = 0.0f;
        }

        if (get_current_button_pressed(results, size) == X360_BACK_BUTTON)
        {
            glfwSetWindowShouldClose(window, true);
        }
    }
}

rect.h

#ifndef RECT_H
#define RECT_H

#include <GL/glew.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

const GLfloat rect_vertices[] = {
     0.75f,  0.5f, 1.0f, 0.7f, 0.0f,
     0.75f, -0.5f, 1.0f, 0.7f, 0.0f,
    -0.75f,  0.5f, 1.0f, 0.7f, 0.0f,

    -0.75f,  0.5f, 1.0f, 0.7f, 0.0f,
    -0.75f, -0.5f, 1.0f, 0.7f, 0.0f,
     0.75f, -0.5f, 1.0f, 0.7f, 0.0f,
};

void draw_rect(GLuint shader_program_id, glm::vec2 position);

#endif

rect.cpp

#include "rect.h"

void draw_rect(GLuint shader_program_id, glm::vec2 position)
{
    glUseProgram(shader_program_id);
    GLuint vao, vbo;
    glGenVertexArrays(1, &vao);
    glGenBuffers(1, &vbo);

    glBindVertexArray(vao);

    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(rect_vertices), rect_vertices, GL_STATIC_DRAW);

    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*) 0);
    glEnableVertexAttribArray(0);

    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(2 * sizeof(GLfloat)));
    glEnableVertexAttribArray(1);

    //glm::mat4 model;
    //glm::mat4 view;
    //glm::mat4 projection;

    //model = glm::translate(model, glm::vec3(position, 0.0f));
    //glUniformMatrix4fv(glGetUniformLocation(shader_program_id, "model"), 1, GL_FALSE, glm::value_ptr(model));

    //view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.0f));
    //glUniformMatrix4fv(glGetUniformLocation(shader_program_id, "view"), 1, GL_FALSE, glm::value_ptr(view));

    //projection = glm::ortho(0, 800, 0, 600, 1, 1000);
    //glUniformMatrix4fv(glGetUniformLocation(shader_program_id, "projection"), 1, GL_FALSE, glm::value_ptr(projection));

    glBindVertexArray(vao);
    glDrawArrays(GL_TRIANGLES, 0, 6);
    glBindVertexArray(0);
}

shader_ui.vert

#version 330 core

layout (location = 0) in vec2 position;
layout (location = 1) in vec3 color;

out vec3 fragColor;

void main()
{
    gl_Position = vec4(position, 0.0f, 1.0f);
    fragColor = color;
}

shader_ui.frag

#version 330 core

in vec3 fragColor;
out vec4 color;

void main()
{
    color = vec4(fragColor, 1.0f);
}

If I'm failing to provide any pertinent code, please let me know. I think this covers everything necessary to potentially dissect what the problem is, but if not I'll be happy to add anything.

Upvotes: 1

Views: 351

Answers (1)

Truncator
Truncator

Reputation: 46

Your draw_rect() function is allocating a new vertex buffer every frame and never freeing it. It would seem that after running the main loop for a short amount of time, enough of these VBOs have built up that freeing them when the program terminates takes a noticeable amount of time. This should be what is causing hangs.

To fix this, simply create a single VBO on initialization and bind it before calling glDrawArrays(). You can free it when the program terminates with glDeleteBuffers().

Upvotes: 3

Related Questions