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.
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.