Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Deleting the QOpenGLWidget widget causes the app to crash



  • Hello.

    I have created a MainOpenGLWidget class that inherits from QOpenGLWidget and is a layer (I use it in QtDesigner) that has a paintGL() function, but all it does is try to draw code from another object(MyScene object).
    In other words, I have a MyScene object that has a drawScene() function, and the paintGL() function calls it to draw a separate scene.
    I need to pass the OpenGL context generated in the MainOpenGLWidget object to MyScene object to use the OpenGL functions and it seems to work.
    However, delete MainOpenGLWidget object results in a crash.
    Before I moved the rendering code to another object, this didn't happen.

    I really hope for your help, as this is an extremely strange error. I understand that it was caused by my manipulations with the OpenGL context, but I only store it globally and use it elsewhere.

    I'll attach some of the code here, but it would be nice if you download and run the project for your convenience.
    Project file DOWNLOAD HERE
    I use msvc2019_64

    main.cpp

    #include "mainwindow.h"
    #include <QApplication>
    
    int main(int argc, char *argv[])
    {
        //return 0;
    
        QApplication a(argc, argv);
        MainWindow w;
        w.show();
        return a.exec(); //AN EXCEPTION OCCURS HERE
    }
    
    

    MainWindow.cpp

    MainWindow::MainWindow(QMainWindow* parent)
    {
        mainWindow = new QMainWindow(parent);
        if(!mainWindow) { qDebug("[ERROR] Main window object create failed!"); }
        qDebug("[INFO] Main window object created successfully");
    
        Ui::MainWindowForm mainWindowFormUI;
        mainWindowFormUI.setupUi(this);
    
       //test
       //delete mainWindowFormUI.pushButton; //HERE, THE EXCEPTION DOES NOT OCCUR
        //delete mainWindowFormUI.mainopenglwidget; //AN EXCEPTION OCCURS HERE
    }
    
    MainWindow::~MainWindow()
    {
        delete mainWindow;
        qDebug("[INFO] Main window object destroyed successfully");
        //HERE, THE EXCEPTION DOES NOT OCCUR
    }
    

    MainOpenGLWidget.cpp

    #include "mainopenglwidget.h"
    #include <QFile>
    
    MainOpenGLWidget::MainOpenGLWidget(QWidget* parent)
    {
        sceneManager = new SceneManager;
        sceneManager->init();
    }
    
    MainOpenGLWidget::~MainOpenGLWidget()
    {
        sceneManager->final();
        delete sceneManager;
        //HERE, THE EXCEPTION DOES NOT OCCUR
    }
    
    void MainOpenGLWidget::initializeGL()
    {
        initializeOpenGLFunctions();
        globalOpenGLContext = this->context();
        openglVersionString = (char*)glGetString(GL_VERSION);
        glClearColor(0.0, 0.0, 0.0, 0.0);
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        gluOrtho2D(-this->width() / 2, this->width() / 2, -this->height() / 2, this->height() / 2);
        glViewport(0, 0, this->width(), this->height());
    }
    
    void MainOpenGLWidget::resizeGL(int w, int h)
    {
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        gluOrtho2D(-w / 2, w / 2, -h / 2, h / 2);
        glViewport(0, 0, w, h);
    }
    
    void MainOpenGLWidget::paintGL()
    {
        for (int i = 0; i < 20; i++) {
            sceneManager->callInitScene();
            sceneManager->callDrawScene();
        }
        update();
    }
    

    MyScene1.cpp

    #include "myscene1.h"
    #include <iostream>
    
    MyScene1::MyScene1() {
        std::cout << "MyScene1::MyScene1() constructor called!\n";
    }
    
    MyScene1::~MyScene1() {
        std::cout << "MyScene1::~MyScene1() destructor called!\n";
    }
    
    void MyScene1::initScene() {
        float vertexes_coords_normalized[] =
                             { 0, 0.5, 0,
                               -0.5, -0.5, 0,
                               0.5, -0.5, 0 };
    
        HelpfulOpenGLFunctions helpfulOpenGLFunctions;
        glfunctions = globalOpenGLContext->versionFunctions<QOpenGLFunctions_3_3_Core>();
        std::cout << "MyScene1::initScene() called!\n";
    
        //Compile shader program
        unsigned int shaderProgram = helpfulOpenGLFunctions.makeShaderProgram(":/glshaders/vertshader.vert", ":/glshaders/fragshader.frag");
        if(!shaderProgram) {
            qDebug("[ERROR] initializeGL: makeShaderProgram failed!");
            return;
        }
        gShaderProgram = shaderProgram;
    
        //Vertex buffer object ID(name)
        unsigned int VBO = 0;
        //Vertex arrays object ID(name)
        unsigned int VAO = 0;
        //Allocate 1 buffer for VBO (Vertex Buffer Object)
        glfunctions->glGenBuffers(1, &VBO);
        //Allocate 1 buffer for VAO (Vertex Arrays Object)
        glfunctions->glGenVertexArrays(1, &VAO);
        //Select VAO
        glfunctions->glBindVertexArray(VAO);
        //Linking a buffer object to an OpenGL buffer
        glfunctions->glBindBuffer(GL_ARRAY_BUFFER, VBO);
        //Copying vertex data from the array to VBO
        glfunctions->glBufferData(GL_ARRAY_BUFFER, sizeof(vertexes_coords_normalized), vertexes_coords_normalized, GL_STATIC_DRAW);
    
        //Configuring the interpretation of the vertex buffer data
        glfunctions->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
        glfunctions->glEnableVertexAttribArray(0);
    
        //Unselect VBO(so that other calls(glBufferData for example) don't change it)
        glfunctions->glBindBuffer(GL_ARRAY_BUFFER, 0);
    
        //Unselect VAO(so that other calls don't change it)
        glfunctions->glBindVertexArray(0);
    
        gVAO = VAO;
        gVBO = VBO;
    }
    
    void MyScene1::drawScene() {
        std::cout << "MyScene::drawScene() called!\n";
        glfunctions->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    
        //Select shader program
        //glUseProgram(gShaderProgram);
        ////Select VAO
        //glBindVertexArray(gVAO);
        ////glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
        ////Draw triangle
        //glDrawArrays(GL_TRIANGLES, 0, 3);
        ////Unselect VAO
        //glBindVertexArray(0);
        ////Unselect shader program
        //glUseProgram(0);
    }
    void MyScene1::finishScene() {
        std::cout << "MyScene1::finishScene() called!\n";
        glfunctions->glDeleteVertexArrays(1, &gVAO);
        glfunctions->glDeleteBuffers(1, &gVBO);
    }
    

    SceneManager.cpp

    #include "SceneManager.h"
    #include <iostream>
    
    #include "Scenes/MyScene1/myscene1.h"
    
    QOpenGLContext* globalOpenGLContext = nullptr;
    
    class MyScene2 : public SceneManager
    {
    public:
    	MyScene2() {
    		std::cout << "MyScene2::MyScene2() constructor called!\n";
    	}
    	~MyScene2() {
    		std::cout << "MyScene2::~MyScene2() destructor called!\n";
    	}
    	void initScene() {
            std::cout << "MyScene2::initScene() called!\n";
    	}
    	void drawScene() {
    		std::cout << "MyScene2::drawScene() called!\n";
    	}
        void finishScene() {
            std::cout << "MyScene2::finishScene() called!\n";
        }
    };
    
    class MyScene3 : public SceneManager
    {
    public:
    	MyScene3() {
    		std::cout << "MyScene3::MyScene3() constructor called!\n";
    	}
    	~MyScene3() {
    		std::cout << "MyScene3::~MyScene3() destructor called!\n";
    	}
    	void initScene() {
            std::cout << "MyScene3::initScene() called!\n";
    	}
    	void drawScene() {
    		std::cout << "MyScene3::drawScene() called!\n";
    	}
        void finishScene() {
            std::cout << "MyScene3::finishScene() called!\n";
        }
    };
    
    SceneManager::SceneManager()
    {
    	//YOU CAN'T PLACE CODE HERE
    	//You should use the init() function
    }
    
    SceneManager::~SceneManager()
    {
    	//YOU CAN'T PLACE CODE HERE
    	//You should use the final() function
    }
    
    void SceneManager::init()
    {
    	MyScene1* myScenePtr = new MyScene1();
    	addSceneObject(myScenePtr, (void*)myScenePtr, "My Scene1");
    	MyScene2* myScenePtr2 = new MyScene2();
    	addSceneObject(myScenePtr2, (void*)myScenePtr2, "My Scene2");
    	MyScene3* myScenePtr3 = new MyScene3();
    	addSceneObject(myScenePtr3, (void*)myScenePtr3, "My Scene3");
    }
    
    void SceneManager::final()
    {
    	for (size_t i = 0; i < sceneObjectsVoidPtrList.size(); i++) {
            //sceneObjectsList[i]->~SceneManager();
            //prevents calling finishScene() for inactive scenes
            if(i == currentObjectIndex) {
                sceneObjectsList[i]->finishScene();
            }
    		delete sceneObjectsVoidPtrList[i];
    	}
    }
    
    void SceneManager::addSceneObject(SceneManager* newSceneObjectPtr, void* newSceneObjectVoidPtr, std::string sceneName)
    {
    	sceneObjectsList.push_back(newSceneObjectPtr);
    	sceneObjectsVoidPtrList.push_back(newSceneObjectVoidPtr);
    	sceneObjectsNamesList.push_back(sceneName);
        sceneObjectsInitStatusList.push_back(false);
    }
    
    void SceneManager::setCurrentSceneObjectIndex(size_t newCurrentIndex)
    {
    	if (currentObjectIndex == newCurrentIndex) {
    		return;
    	}
        sceneObjectsInitStatusList[currentObjectIndex] = false;
        sceneObjectsList[currentObjectIndex]->finishScene();
    	currentObjectIndex = newCurrentIndex;
    }
    
    void SceneManager::initScene()
    {
        //DON'T USE
    }
    
    void SceneManager::drawScene()
    {
        //DON'T USE
    }
    
    void SceneManager::finishScene()
    {
        //DON'T USE
    }
    
    void SceneManager::callInitScene()
    {
    	if (sceneObjectsList.size() == 0) {
    		return;
    	}
    	if (sceneObjectsInitStatusList[currentObjectIndex] == false) {
    		sceneObjectsInitStatusList[currentObjectIndex] = true;
    		sceneObjectsList[currentObjectIndex]->initScene();
    		return;
    	}
    }
    
    void SceneManager::callDrawScene()
    {
    	if (sceneObjectsList.size() == 0) {
    		return;
    	}
    	sceneObjectsList[currentObjectIndex]->drawScene();
    }
    

  • Moderators

    @mrjbom Try replacing your calls to delete with QObject::deleteLater(). In general, deleteLater() is safer because it waits for all pending tasks to complete before deleting the object.

    If you still get crashes, run your program through your debugger and check the stack trace.



  • @JKSH, this won't help me, since I don't deal with deleting objects, they are deleted during the destruction of the MainWindow object in main().
    This is too confusing a question.
    Perhaps I will try to transfer the problematic code to another application and if the error is reproduced, I will create a new question.


  • Moderators

    @mrjbom said in Deleting the QOpenGLWidget widget causes the app to crash:

    this won't help me, since I don't deal with deleting objects, they are deleted during the destruction of the MainWindow object in main().

    Maybe it can't solve your main problem, but it can still help you understand the benefits of deleteLater().

    You said delete mainWindowFormUI.mainopenglwidget; throws an exception.

    I said that changing that to mainWindowFormUI.mainopenglwidget->deleteLater(); will likely prevent the exception. Try it.

    I will try to transfer the problematic code to another application and if the error is reproduced, I will create a new question.

    That is a good idea.

    All the best.



  • @JKSH I would try this function, but I do not delete the mainopenglwidget because it is created within the setupUi() function (I think you understand what I'm talking about), which means it is not deleted by me.


Log in to reply