Search code examples
c++qtc++17qtopengl

Qt + OpenGL. Example from Qt documentation doesnt't work


I'm trying to use Qt and OpenGL for my educational project. First step that I've done - I found the openglwindow example in Qt documentation and just copied it to understand how this stuff should work. This example: example with explanations And all source code: repository

I built this with cmake that a bit different from original cmake, because (as i've read) my qt version differs from qt version in doc. Here's my CMakeLists.txt:

cmake_minimum_required(VERSION 3.16)                                                
project(openglwindow LANGUAGES CXX)                                                 
                                                                                    
set(CMAKE_AUTOMOC ON)                                                               
set(CMAKE_AUTORCC ON)                                                               
                                                                                    
set(CMAKE_CXX_STANDARD 17)                                                          
set(CMAKE_CXX_STANDARD_REQUIRED ON)                                                 
                                                                                    
if(NOT DEFINED INSTALL_EXAMPLESDIR)                                                 
  set(INSTALL_EXAMPLESDIR "src")                                                    
endif()                                                                             
                                                                                    
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/test_view/build")                    
                                                                                    
find_package(Qt6 REQUIRED COMPONENTS Core Gui OpenGL)                               
                                                                                    
# qt_standard_project_setup()                                                       
                                                                                    
qt_add_executable(openglwindow                                                      
    main.cpp                                                                        
    openglwindow.cpp openglwindow.h                                                 
)                                                                                   
                                                                                    
set_target_properties(openglwindow PROPERTIES                                       
    WIN32_EXECUTABLE TRUE                                                           
    MACOSX_BUNDLE TRUE                                                              
)                                                                                   
                                                                                    
target_link_libraries(openglwindow PRIVATE                                          
    Qt6::Core                                                                       
    Qt6::Gui                                                                        
    Qt6::OpenGL                                                                     
)                                                                                   
                                                                                    
install(TARGETS openglwindow                                                        
    RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"                                     
    BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"                                      
    LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"                                     
)

main.cpp:

#include "openglwindow.h"

#include <QGuiApplication>
#include <QMatrix4x4>
#include <QOpenGLShaderProgram>
#include <QScreen>
#include <QtMath>

class TriangleWindow : public OpenGLWindow
{
public:
    using OpenGLWindow::OpenGLWindow;

    void initialize() override;
    void render() override;

private:
    GLint m_posAttr = 0;
    GLint m_colAttr = 0;
    GLint m_matrixUniform = 0;

    QOpenGLShaderProgram *m_program = nullptr;
    int m_frame = 0;
};

int main(int argc, char **argv)
{
    QGuiApplication app(argc, argv);

    QSurfaceFormat format;
    format.setSamples(16);

    TriangleWindow window;
    window.setFormat(format);
    window.resize(640, 480);
    window.show();

    window.setAnimating(true);

    return app.exec();
}

static const char *vertexShaderSource =
    "attribute highp vec4 posAttr;\n"
    "attribute lowp vec4 colAttr;\n"
    "varying lowp vec4 col;\n"
    "uniform highp mat4 matrix;\n"
    "void main() {\n"
    "   col = colAttr;\n"
    "   gl_Position = matrix * posAttr;\n"
    "}\n";

static const char *fragmentShaderSource =
    "varying lowp vec4 col;\n"
    "void main() {\n"
    "   gl_FragColor = col;\n"
    "}\n";

void TriangleWindow::initialize()
{
    m_program = new QOpenGLShaderProgram(this);
    m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource);
    m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource);
    m_program->link();
    m_posAttr = m_program->attributeLocation("posAttr");
    Q_ASSERT(m_posAttr != -1);
    m_colAttr = m_program->attributeLocation("colAttr");
    Q_ASSERT(m_colAttr != -1);
    m_matrixUniform = m_program->uniformLocation("matrix");
    Q_ASSERT(m_matrixUniform != -1);
}

void TriangleWindow::render()
{
    const qreal retinaScale = devicePixelRatio();
    glViewport(0, 0, width() * retinaScale, height() * retinaScale);

    glClear(GL_COLOR_BUFFER_BIT);

    m_program->bind();

    QMatrix4x4 matrix;
    matrix.perspective(60.0f, 4.0f / 3.0f, 0.1f, 100.0f);
    matrix.translate(0, 0, -2);
    matrix.rotate(100.0f * m_frame / screen()->refreshRate(), 0, 1, 0);

    m_program->setUniformValue(m_matrixUniform, matrix);

    static const GLfloat vertices[] = {
         0.0f,  0.707f,
        -0.5f, -0.5f,
         0.5f, -0.5f
    };

    static const GLfloat colors[] = {
        1.0f, 0.0f, 0.0f,
        0.0f, 1.0f, 0.0f,
        0.0f, 0.0f, 1.0f
    };

    glVertexAttribPointer(m_posAttr, 2, GL_FLOAT, GL_FALSE, 0, vertices);
    glVertexAttribPointer(m_colAttr, 3, GL_FLOAT, GL_FALSE, 0, colors);

    glEnableVertexAttribArray(m_posAttr);
    glEnableVertexAttribArray(m_colAttr);

    glDrawArrays(GL_TRIANGLES, 0, 3);

    glDisableVertexAttribArray(m_colAttr);
    glDisableVertexAttribArray(m_posAttr);

    m_program->release();

    ++m_frame;
}

openglwindow.h:

#ifndef OPENGLWINDOW_H
#define OPENGLWINDOW_H

#include <QWindow>
#include <QOpenGLFunctions>

QT_FORWARD_DECLARE_CLASS(QPainter)
QT_FORWARD_DECLARE_CLASS(QOpenGLContext)
QT_FORWARD_DECLARE_CLASS(QOpenGLPaintDevice)

class OpenGLWindow : public QWindow, protected QOpenGLFunctions
{
    Q_OBJECT
public:
    explicit OpenGLWindow(QWindow *parent = nullptr);
    ~OpenGLWindow();

    virtual void render(QPainter *painter);
    virtual void render();

    virtual void initialize();

    void setAnimating(bool animating);

public slots:
    void renderLater();
    void renderNow();

protected:
    bool event(QEvent *event) override;

    void exposeEvent(QExposeEvent *event) override;

private:
    bool m_animating = false;

    QOpenGLContext *m_context = nullptr;
    QOpenGLPaintDevice *m_device = nullptr;
};

#endif // OPENGLWINDOW_H

openglwindow.cpp:

#include "openglwindow.h"

#include <QOpenGLContext>
#include <QOpenGLPaintDevice>
#include <QPainter>


OpenGLWindow::OpenGLWindow(QWindow *parent)
    : QWindow(parent)
{
    setSurfaceType(QWindow::OpenGLSurface);
}


OpenGLWindow::~OpenGLWindow()
{
    delete m_device;
}

void OpenGLWindow::render(QPainter *painter)
{
    Q_UNUSED(painter);
}

void OpenGLWindow::initialize()
{
}

void OpenGLWindow::render()
{
    if (!m_device)
        m_device = new QOpenGLPaintDevice;

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

    m_device->setSize(size() * devicePixelRatio());
    m_device->setDevicePixelRatio(devicePixelRatio());

    QPainter painter(m_device);
    render(&painter);
}

void OpenGLWindow::renderLater()
{
    requestUpdate();
}

bool OpenGLWindow::event(QEvent *event)
{
    switch (event->type()) {
    case QEvent::UpdateRequest:
        renderNow();
        return true;
    default:
        return QWindow::event(event);
    }
}

void OpenGLWindow::exposeEvent(QExposeEvent *event)
{
    Q_UNUSED(event);

    if (isExposed())
        renderNow();
}

void OpenGLWindow::renderNow()
{
    if (!isExposed())
        return;

    bool needsInitialize = false;

    if (!m_context) {
        m_context = new QOpenGLContext(this);
        m_context->setFormat(requestedFormat());
        m_context->create();

        needsInitialize = true;
    }

    m_context->makeCurrent(this);

    if (needsInitialize) {
        initializeOpenGLFunctions();
        initialize();
    }

    render();

    m_context->swapBuffers(this);

    if (m_animating)
        renderLater();
}


void OpenGLWindow::setAnimating(bool animating)
{
    m_animating = animating;

    if (animating)
        renderLater();
}

Here everything is ok and project builds. But it doesn't work and i don't know why. As a result I have empty black window without any triangles: execution When I check this for memory leaks with fsanitize=address and valgrind, both show memory leaks. I can't go any further until I solve why it works as it works. Maybe the reason is in difference between Qt versions, it is my only assumption.


Solution

  • QSurfaceFormat format;
    format.setSamples(16);
    

    In this code, you set 16 as the driver's MSAA samples. (16x MSAA)
    Here, you should know that the maximum number of supported driver's MSAA samples varies with the environment. Perhaps 16x MSAA is not supported in your environment, which is why this issue occurred.

    If your project is for practical use, it might be better for you to check the maximum number of driver's MSAA samples supported by the execution environment and then select the appropriate one. (How to determine the maximum number of supported multisample samples)

    However, in your case, since your project is for hobby use, I think it would be sufficient to select the lower one, 2 or 4.