1

EDIT* I rearranged the initialization list, as suggested by much_a_chos, so that the Window object initializes before the Game object, ensuring that glew is initialized first. However, this did not work:

//Rearranged initialization list 
class TempCore
{
public:
    TempCore(Game* g) :
        win(new Window(800, 800, "EngineTry", false)), gamew(g) {}
    ~TempCore() { if(gamew) delete gamew; } 
...
};

And here is the code I changed in the Mesh constructor when the above didn't work:

Mesh::Mesh( Vertex* vertices, unsigned int numVerts )
{
    m_drawCount = numVerts;

    glewExperimental = GL_TRUE;
    if(glewInit() != GLEW_OK){
        exit(-150);   //application stops and exits here with the code -150
    }

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

   ...
}

What happens when I compile and run is surprising. The program exits at the if(glewInit() != GLEW_OK) I copied from the Window constructor. For some reason glew initializes properly in the Window constructor (which is called before the Game constructor), but it fails to initialize when called the second time in the Mesh constructor. I assume its bad practice to call on glewInit() more than once in a program, but I don't think it should fail if I actually did so. Does anybody know what might be happening? Am I making a mistake in calling glewInit() more than once?

*END OF EDIT

I've been following a 3D Game Engine Development tutorial and I've encountered a weird bug in my code, which I will demonstrate below. I'm attempting to make my own game-engine purely for educational reasons. I'm using Code-blocks 13.12 as my IDE and mingw-w64 v4.0 as my compiler. I'm also using SDL2, glew, Assimp and boost as my third-party libraries.

I apologize in advance for the numerous code extracts, but I put in what I thought what was necessary to understand the context of the error.

I have a Core class for my game-engine that holds the main loop and updates and renders accordingly, calling the Game class update() and render() methods in the process as well. The Game class is intended as the holder for all the assets in the game, and will be the base class for any games made using the engine, thus, it contains mesh, texture and camera references. The Game class update(), render() and input() methods are all virtual as the Game class is meant to be derived.

My problem is: when I initialize the Game member variable in the Core class, I get a SIGSEGV (i.e. segmentation fault) in the Mesh object's constructor at the glGenVertexArrays call. However, when I move my Game object out of the Core class and straight into the main method (so I changed it from being a class member to a simple scoped variable in the main method), along with the necessary parts from the Core class, then its runs perfectly and renders my rudimentary triangle example. This is a bug I've never come across and I would really appreciate any help I can get.

Below is an extract of my morphed code that ran perfectly and rendered the triangle:

int WINAPI WinMain (HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow)
{
    Window win(800, 800, "EngineTry", false); //Creates an SDL implemented window with a GL_context
    Game* gamew = new Game;
    const double frameTime = 1.0 / 500;  //500 = maximum fps

    double lastTime = FTime::getTime();  //gets current time in milliseconds
    double unprocessedTime = 0.0;
    int frames = 0;
    double frameCounter = 0;

    while(win.isRunning()){
        bool _render = false;

        double startTime = FTime::getTime();
        double passedTime = startTime - lastTime;
        lastTime = startTime;

        unprocessedTime += passedTime / (double)FTime::SECOND;
        frameCounter += passedTime;

        while(unprocessedTime > frameTime){
            if(!win.isRunning())
                exit(0);
            _render = true;

            unprocessedTime -= frameTime;
            FTime::delta = frameTime;

            gamew->input();
            Input::update();
            gamew->update();

            if(frameCounter >= FTime::SECOND)
            {
                std::cout << "FPS: " << frames << std::endl;
                frames = 0;
                frameCounter = 0;
            }
        }

        if(_render){
            RenderUtil::clearScreen(); //simple wrapper to the glClear function
            gamew->render();
            win.Update();
            frames++;
        }else{
            Sleep(1);
        }
    }

    delete gamew;
    return 0;
}

Here is an extract of my modified Core class that doesn't work (throws the sigsegv in the Mesh constructor)

class TempCore
{
public:
    TempCore(Game* g) :
        gamew(g), win(800, 800, "EngineTry", false) {}
    ~TempCore() { if(gamew) delete gamew; }

    void start();

private:
    Window win;
    Game* gamew;
};


int WINAPI WinMain (HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow)
{
    TempCore m_core(new Game());
    m_core.start();

    return 0;
}


void TempCore::start()
{
    const double frameTime = 1.0 / 500;

    double lastTime = FTime::getTime();
    double unprocessedTime = 0.0;
    int frames = 0;
    double frameCounter = 0;

    while(win.isRunning()){
        bool _render = false;

        double startTime = FTime::getTime();
        double passedTime = startTime - lastTime;
        lastTime = startTime;

        unprocessedTime += passedTime / (double)FTime::SECOND;
        frameCounter += passedTime;

        while(unprocessedTime > frameTime){
            if(!win.isRunning())
                exit(0);
            _render = true;

            unprocessedTime -= frameTime;
            FTime::delta = frameTime;

            gamew->input();
            Input::update();
            gamew->update();

            if(frameCounter >= FTime::SECOND){
                //double totalTime = ((1000.0 * frameCounter)/((double)frames));
                //double totalMeasuredTime = 0.0;

                std::cout << "Frames: " << frames << std::endl;
                //m_frames_per_second = frames;
                frames = 0;
                frameCounter = 0;
            }
        }

        if(_render){
            RenderUtil::clearScreen();
            gamew->render();
            win.Update();
            frames++;
        }else{
            Sleep(1);
        }
    }
}

Mesh constructor where the sigsegv occurs in the above TestCore implementation:

Mesh::Mesh( Vertex* vertices, unsigned int numVerts )
{
    m_drawCount = numVerts;

    glGenVertexArrays(1, &m_vertexArrayObject); //sigsegv occurs here
    glBindVertexArray(m_vertexArrayObject);

        std::vector<glm::vec3> positions;
        std::vector<glm::vec2> texCoords;
        positions.reserve(numVerts);
        texCoords.reserve(numVerts);

        for(unsigned i = 0; i < numVerts; i++){
            positions.push_back(vertices[i].pos);
            texCoords.push_back(vertices[i].texCoord);
        }

        glGenBuffers(NUM_BUFFERS, m_vertexArrayBuffers);
        glBindBuffer(GL_ARRAY_BUFFER, m_vertexArrayBuffers[POSITION_VB]); 
        glBufferData(GL_ARRAY_BUFFER, numVerts*sizeof(positions[0]), &positions[0], GL_STATIC_DRAW); 

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


        glBindBuffer(GL_ARRAY_BUFFER, m_vertexArrayBuffers[TEXCOORD_VB]); 
        glBufferData(GL_ARRAY_BUFFER, numVerts*sizeof(texCoords[0]), &texCoords[0], GL_STATIC_DRAW);

        glEnableVertexAttribArray(1);
        glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);

    glBindVertexArray(0);
}

The Game constructor that initializes the Mesh object:

Vertex vertices[] = {   Vertex(-0.5f, -0.5f, 0, 0, 0),
                        Vertex(0, 0.5f, 0, 0.5f, 1.0f),
                        Vertex(0.5f, -0.5f, 0, 1.0f, 0)};
//Vertex is basically a struct with a glm::vec3 for position and a glm::vec2 for texture coordinate

Game::Game() :
    m_mesh(vertices, sizeof(vertices)/sizeof(vertices[0])),
    m_shader("res\\shaders\\basic_shader"),
    m_texture("res\\textures\\mist_tree.jpg")
{
}

The Window class constructor that initializes glew:

Window::Window(int width, int height, const std::string& title, bool full_screen) :
    m_fullscreen(full_screen)
{
    SDL_Init(SDL_INIT_EVERYTHING);

        SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
        SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
        SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
        SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
        SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE, 32);
        SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

    //SDL_Window* in private of class declaration 
    m_window = SDL_CreateWindow(title.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
    //SDL_GLContext in private of class declaration 
    m_glContext = SDL_GL_CreateContext(m_window);

    std::cout << "GL Version: " << glGetString(GL_VERSION) << std::endl;
    glewExperimental = GL_TRUE;
    if(glewInit() != GLEW_OK || !glVersionAbove(3.0)){
        std::cerr << "Glew failed to initialize...\n";
        exit(-150);
    }
}
thatman
  • 107
  • 7

3 Answers3

1

A long shot here, since the given amount of information is pretty big. I searched for similar questions like this one and this one, but every one of them have been answered with tricks you're doing in your Window class constructor that have to be called before your game constructor. And as I can see in your TempCore constructor, you build your game object (and make a call to glGenVertexArrays) before your Window object is constructed

...

TempCore(Game* g) :
    gamew(g), win(800, 800, "EngineTry", false) {}

...

So before making calls for creating your OpenGL context with SDL_GL_CreateContext(m_window) and before glewExperimental = GL_TRUE; glewInit();. And then you say that putting it in the main in this order solves the problem...

...
Window win(800, 800, "EngineTry", false); //Creates an SDL implemented window with a GL_context
Game* gamew = new Game;
...

Maybe reordering your initialization list in your constructor like this could solve your problem?

class TempCore
{
public:
    TempCore(Game* g) :
        win(800, 800, "EngineTry", false), gamew(g) {}
    ~TempCore() { if(gamew) delete gamew; }

...
};

UPDATE

I was wrong, as stated in the comments, the initialization list order doesn't matter. It's the definition order that matters, which is correct here...

Community
  • 1
  • 1
pandaman1234
  • 523
  • 7
  • 17
  • 3
    order of the initialization list doesn't matter, c++ follows the order in which the members are defined in the class (at least last I checked) – kmdreko Mar 23 '16 at 18:16
  • Thanks for the suggestion @much_a_chos. Unfortunately it didn't work. I rearranged the initialization list like you suggested but the same error occurs at the same place (the sigsegv at glGenVertexArrays) – thatman Mar 23 '16 at 18:22
  • @vu1p3n0x You're right. I'll change my answer according to this detail I was unaware of. Thanks – pandaman1234 Mar 23 '16 at 18:25
  • @searchnot Well I tested what vu1p3n0x stated and he was right so testing what I said surely didn't change anything to your code. But I would recommend making sure your calls are done in the right order, since everything else (in my eyes) seems fine. – pandaman1234 Mar 23 '16 at 18:27
  • vu1p3n0x, @much_a_chos and anyone else watching this: I posted an Edit at the top before my question in which I called _glewInit()_ before _glGenVertexArrays()_ and it closed with the error that glew failed to initialize. Any thoughts? – thatman Mar 23 '16 at 18:46
  • @searchnot Did you copied only ``glewInit()`` or also ``glewExperimental = GL_TRUE``? Because OpenGL (and glew which follows the same pattern) in general is like a big state machine and omitting flags before calling initialization functions would be a bad idea (refer to [this answer](http://stackoverflow.com/a/8303331/5248907)), but then again with the information you provide there's theoretically nothing wrong with calling ``glewInit`` before ``glGenVertexArrays``... Also, before copying *only* glewInit, I'd make sure my OpenGL context is previously created... – pandaman1234 Mar 23 '16 at 19:13
1

Thanks to both @much_a_chos and @vu1p3n0x for your help. Turns out much_a_chos had the right idea with the Game object initializing before the Window object, thus missing the glewInit() call altogether, resulting in the sigsegv error. The problem, however, was not in the initializer list but in the main.cpp file. I was creating a Game class object and then passing that Game object via pointer to the core class, so regardless of how I arranged the Core class, the Game object would always initialize before the Window class, and would therefore always do its glGenVertexArrays call before glewInit() is called. This is a terrible logic error on my side and I apologize for wasting your time.

Below are extracts from the fixed main.cpp file and the fixed TempCore class (please keep in mind that these are temporary fixes to illustrate how I would go about fixing my mistake):

class TempCore
{
public:
    TempCore(Window* w, Game* g) :  //take in a Window class pointer to ensure its created before the Game class constructor
        win(w), gamew(g) {}
    ~TempCore() { if(gamew) delete gamew; }

    void start();

private:
    Window* win;
    Game* gamew;
};

int WINAPI WinMain (HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow)
{
    Window* win = new Window(800, 800, "EngineTry", false); //this way the Window constructor with the glewinit() call is called before the Game contructor
    TempCore m_core(win, new Game());
    m_core.start();

    return 0;
} 
thatman
  • 107
  • 7
0

Addressing your edit: You should not call glewInit() more than once. I'm not familiar with glew in this regard but in general, anything should only be "initialized" once. glew probably assumes that it is uninitialized and errors out when some initialization is already there.

I'd recommend calling glewInit() at the very beginning of the program and not in an object constructor. (Unless you have that object "own" glew)

Edit: It seems my assumption about glewInit() was slightly wrong. glewInit() behaves differently depending on the build, but regardless should only be called if you switch contexts. However, because you aren't changing context (from what I see) you should not call it more than once.

kmdreko
  • 42,554
  • 6
  • 57
  • 106