2016-03-23 6 views
1

EDIT * Ich neu angeordnet die Initialisierungsliste, wie von Much_a_chos vorgeschlagen, so dass das Window-Objekt vor dem Game-Objekt initialisiert wird, sicherzustellen, dass Glew zuerst initialisiert wird. Dies führte jedoch nicht:OpenGL Sigsegv Fehler im Klassenkonstruktor

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

Und hier ist der Code, den ich in der Mesh-Konstruktor geändert, wenn die oben nicht funktioniert:

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); 

    ... 
} 

Was passiert, wenn ich kompiliert und ausgeführt ist überraschend. Das Programm wird am if(glewInit() != GLEW_OK) Ich kopiert aus dem Window-Konstruktor. Aus irgendeinem Grund initialisiert glew ordnungsgemäß im Window-Konstruktor (der vor dem Game-Konstruktor aufgerufen wird), aber es kann nicht initialisiert werden, wenn es das zweite Mal im Mesh-Konstruktor aufgerufen wird. Ich gehe davon aus, dass es eine schlechte Praxis ist, mehr als einmal in einem Programm glewInit() aufzurufen, aber ich denke nicht, dass es scheitern sollte, wenn ich das tat. Weiß jemand, was passieren könnte? Mache ich einen Fehler beim Aufruf von glewInit() mehr als einmal?

* END OF EDIT

Ich verfolge ein 3D Game Engine Development tutorial und ich habe einen seltsamen Fehler in meinem Code gestoßen, die ich weiter unten zeigen wird. Ich versuche meine eigene Spiel-Engine rein aus pädagogischen Gründen zu machen. Ich verwende Code-Blöcke 13.12 als meine IDE und mingw-w64 v4.0 als meinen Compiler. Ich verwende auch SDL2, glew, Assimp und boost als meine Drittanbieter-Bibliotheken.

Ich entschuldige mich im Voraus für die zahlreichen Code-Extrakte, aber ich fügte hinzu, was ich dachte, was notwendig war, um den Zusammenhang des Fehlers zu verstehen.

Ich habe eine Core Klasse für mein Spiel-Engine, die die Hauptschleife und Updates und macht dementsprechend hält, als auch die Game Klasse update() und render() Methoden in den Prozess aufrufen. Die Game-Klasse ist als Halter für alle Assets im Spiel gedacht und wird die Basisklasse für alle Spiele sein, die mit der Engine erstellt wurden. Sie enthält also Mesh-, Textur- und Kamera-Referenzen. Die Spielklassen update(), render() und input() Methoden sind alle virtuell, da die Klasse Game abgeleitet werden soll.

Mein Problem ist: wenn ich das Spiel Membervariable in der Core Klasse initialisieren, erhalte ich eine SIGSEGV (das heißt Segmentierungsfehler) in dem Mesh Konstruktor des Objekts an dem glGenVertexArrays Anruf. Allerdings, wenn ich meine Game Objekt aus der Core Klasse und direkt in die Hauptmethode (so änderte ich es von einem Klassenmitglied zu einem einfachen Bereich Variable in der Hauptmethode), zusammen mit den notwendigen Teilen aus der Core Klasse , dann läuft es perfekt und macht mein rudimentäres Dreieckbeispiel. Dies ist ein Fehler, auf den ich nie gestoßen bin und ich würde jede Hilfe, die ich bekommen kann, sehr schätzen.

Im Folgenden ein Auszug aus meinem morphed Code ist das perfekt und machte das Dreieck lautete:

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; 
} 

Hier ein Auszug meiner modifizierten Core Klasse, die nicht (führt den SIGSEGV im Mesh Konstruktor) funktioniert

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-Konstruktor, wo die SIGSEGV in der obigen TestCore Implementierung erfolgt:

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); 
} 

Der Game Konstruktor, das Mesh Objekt initialisiert:

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") 
{ 
} 

Die Window Klasse Konstruktor, glew initialisiert:

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); 
    } 
} 

Antwort

1

Vielen Dank sowohl @much_a_chos als auch @ vu1p3n0x für Ihre Hilfe. Es stellt sich heraus, dass much_a_chos die richtige Idee hatte, das Objekt Game vor dem Window-Objekt zu initialisieren, wodurch der Aufruf glewInit() insgesamt fehlte, was zu dem Fehler sigsegv führte. Das Problem lag jedoch nicht in der Initialisierungsliste, sondern in der Datei main.cpp. Ich war Erstellen eines Spiels Klassenobjekt und dann das Spielobjekt übergeben über Zeiger auf die Kernklasse, also unabhängig davon, wie ich die Core-Klasse angeordnet, würde das Game-Objekt immer vor der Window-Klasse initialisieren, und würde daher immer seine glGenVertexArrays tun Anruf vor glewInit() wird aufgerufen. Dies ist ein schrecklicher logischer Fehler auf meiner Seite und ich entschuldige mich dafür, dass ich Ihre Zeit verschwendet habe.

Im Folgenden sind Auszüge aus der Fest main.cpp-Datei und die festen TempCore Klasse (im Auge behalten bitte, dass diese temporären Korrekturen zu veranschaulichen, wie ich meinen Fehlers über die Festsetzung gehen würde):

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; 
} 
+1

@much_a_chos danke nochmal für die Hilfe – searchnot

+1

Und @ vu1p3n0x sowie – searchnot

+0

Sorry für die Verzögerung der Antwort, froh, dass Sie das Problem gefunden haben und wir Ihnen helfen konnten! – lordjohncena

1

Ein langer Schuss hier, da die gegebene Menge an Informationen ist ziemlich groß. Ich suchte nach ähnlichen Fragen wie this one und this one, aber jeder von ihnen wurde mit Tricks beantwortet, die Sie in Ihrem Window-Klassenkonstruktor tun, die vor Ihrem Spielkonstruktor aufgerufen werden müssen. Und wie ich in Ihrem Tempcore Konstruktor sehen kann, bauen Sie Ihr Spiel-Objekt (und um einen Anruf zu glGenVertexArrays machen) vor Ihr Window-Objekt aufgebaut ist

... 

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

... 

Also, bevor Anrufe für die Erstellung Ihres OpenGL-Kontextes mit SDL_GL_CreateContext(m_window) machen und vor glewExperimental = GL_TRUE; glewInit();. Und man dann sagen, dass es in der Haupt in dieser Reihenfolge setzt das Problem löst ...

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

Vielleicht in Ihrem Konstruktor Ihre Initialisierungsliste Neuordnungs wie dies Ihr Problem lösen könnte?

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

... 
}; 

UPDATE

ich falsch war, wie in den Kommentaren angegeben, wird die Initialisierung Liste Reihenfolge keine Rolle. Es ist die Definition Reihenfolge, die zählt, die hier richtig ist ...

+3

Reihenfolge der Initialisierungsliste spielt keine Rolle, C++ folgt der Reihenfolge, in der die Mitglieder in der Klasse definiert sind (zumindest zuletzt habe ich überprüft) – vu1p3n0x

+0

Danke für den Vorschlag @much_a_chos. Leider hat es nicht funktioniert. Ich habe die Initialisierungsliste neu angeordnet, wie Sie es vorgeschlagen haben, aber derselbe Fehler tritt an der gleichen Stelle auf (Sigsegv bei glGenVertexArrays) – searchnot

+0

@ vu1p3n0x Sie haben Recht. Ich werde meine Antwort nach diesem Detail ändern, von dem ich nichts wusste. Danke – lordjohncena

0

Adressierung Ihrer Bearbeitung: Sie sollten glewInit() nicht mehr als einmal aufrufen. Ich bin nicht vertraut mit Glew in dieser Hinsicht, aber im Allgemeinen sollte alles nur einmal "initialisiert" werden. glew geht vermutlich davon aus, dass es nicht initialisiert ist und Fehler auftreten, wenn bereits eine Initialisierung vorliegt.

Ich würde empfehlen, glewInit() am Anfang des Programms und nicht in einem Objekt Konstruktor aufrufen. (Es sei denn, Sie haben dieses Objekt "eigenes" glew)

Edit: Es scheint meine Annahme über glewInit() war etwas falsch. glewInit() verhält sich je nach Build unterschiedlich, sollte aber nur aufgerufen werden, wenn Sie den Kontext wechseln. Da Sie jedoch den Kontext nicht ändern (von dem, was ich sehe), sollten Sie ihn nicht mehr als einmal aufrufen.