I'm using qt 6.4 to create a application rendering yuv image with QOpenGLWidget. The code worked properly on windows 10, but didn't rendering anything on my macbook pro (intel chip, Ventura 13.1), and didn't report any error.
Here is my code:
in main.cpp:
#include "mainwindow.h"
#include <QApplication>
#include <QSurfaceFormat>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL);
QSurfaceFormat glFormat;
glFormat.setVersion(4, 1);
glFormat.setProfile(QSurfaceFormat::CoreProfile);
glFormat.setSwapBehavior (QSurfaceFormat::DoubleBuffer);
glFormat.setRenderableType (QSurfaceFormat::OpenGL);
QSurfaceFormat::setDefaultFormat(glFormat);
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
FrmScreen.h:
#include "QtOpenGLWidgets/qopenglwidget.h"
#include <QOpenGLWidget>
#include <QOpenGLShaderProgram>
#include <QOpenGLFunctions>
#include <QOpenGLTexture>
#include <QTimer>
#include <QFile>
namespace Ui {
class FrmScreen;
}
#define GET_STR(x) #x
#define A_VER 3
#define T_VER 4
class FrmScreen : public QOpenGLWidget, protected QOpenGLFunctions
{
Q_OBJECT
public:
explicit FrmScreen(QWidget *parent = nullptr);
~FrmScreen();
void displayImage(QString imgPath);
void displayYuvImage(QString yuvPath);
protected:
void initializeGL() Q_DECL_OVERRIDE;
void resizeGL(int w, int h) Q_DECL_OVERRIDE;
void paintGL() Q_DECL_OVERRIDE;
private:
Ui::FrmScreen *ui;
QString _img_path;
QOpenGLShaderProgram program;
GLuint unis[3] = {0};
GLuint texs[3] = {0};
int width = 2688;
int height = 1520;
QFile m_file;
QByteArray m_buf;
};
#endif // FRMSCREEN_H
FrmScreen.cpp:
#include "FrmScreen.h"
#include "QtGui/qpainter.h"
#include "ui_FrmScreen.h"
FrmScreen::FrmScreen(QWidget *parent) :
QOpenGLWidget(parent),
ui(new Ui::FrmScreen)
{
_img_path = "./frames/frame000.yuv";
ui->setupUi(this);
}
const char* vString =
"#version 410\n"
"in vec4 vertexIn;\n"
"in vec2 textureIn;\n"
"out vec2 textureOut;\n"
"void main(void)\n"
"{\n"
" gl_Position = vertexIn;\n"
" textureOut = textureIn;\n"
"}";
const char* tString = "#version 410\n"
"in vec2 textureOut;\n"
"out vec4 fragColor;\n"
"uniform sampler2D tex_y;\n"
"uniform sampler2D tex_u;\n"
"uniform sampler2D tex_v;\n"
"void main(void)\n"
"{\n"
" vec3 yuv;\n"
" vec3 rgb;\n"
" yuv.x = texture(tex_y, textureOut).r;\n"
" yuv.y = texture(tex_u, textureOut).r - 0.5;\n"
" yuv.z = texture(tex_v, textureOut).r - 0.5;\n"
" rgb = mat3( 1, 1, 1,\n"
" 0, -0.21482, 2.12798,\n"
" 1.28033, -0.38059, 0) * yuv;\n"
" fragColor = vec4(rgb, 1);\n"
"}";
FrmScreen::~FrmScreen()
{
delete ui;
}
void FrmScreen::initializeGL()
{
initializeOpenGLFunctions();
qDebug() << "load fragment:" <<program.addShaderFromSourceCode(QOpenGLShader::Fragment, tString);
qDebug() << "load shader:" <<program.addShaderFromSourceCode(QOpenGLShader::Vertex, vString);
program.bindAttributeLocation("vertexIn", A_VER);
program.bindAttributeLocation("textureIn", T_VER);
qDebug() << "compile shader:" << program.link();
qDebug() << "bind shader:" << program.bind();
static const GLfloat ver[] = {
-1.0f, -1.0f,
1.0f, -1.0f,
-1.0f, 1.0f,
1.0f, 1.0f
};
static const GLfloat tex[] = {
0.0f, 1.0f,
1.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f
};
glVertexAttribPointer(A_VER, 2, GL_FLOAT, 0, 0, ver);
glEnableVertexAttribArray(A_VER);
glVertexAttribPointer(T_VER, 2, GL_FLOAT, 0, 0, tex);
glEnableVertexAttribArray(T_VER);
unis[0] = program.uniformLocation("tex_y");
unis[1] = program.uniformLocation("tex_u");
unis[2] = program.uniformLocation("tex_v");
glGenTextures(3, texs);
// Y
glBindTexture(GL_TEXTURE_2D, texs[0]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, 0);
// U
glBindTexture(GL_TEXTURE_2D, texs[1]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width / 2, height/ 2, 0, GL_RED, GL_UNSIGNED_BYTE, 0);
glBindTexture(GL_TEXTURE_2D, texs[2]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width / 2, height / 2, 0, GL_RED, GL_UNSIGNED_BYTE, 0);
glClearColor(0.3, 0.3, 0.3, 0.0);
m_file.setFileName(_img_path);
if(!m_file.open(QIODevice::ReadOnly))
{
qDebug() << "open yuv file failed";
return;
}
}
void FrmScreen::paintGL()
{
if(m_file.atEnd())
{
m_file.seek(0);
}
QByteArray buf = m_file.read(width * height);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texs[0]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RED, GL_UNSIGNED_BYTE, buf.data());
glUniform1i(unis[0], 0);
buf = m_file.read(width * height / 4);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texs[1]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width / 2, height / 2, GL_RED, GL_UNSIGNED_BYTE, buf.data());
glUniform1i(unis[1], 1);
buf = m_file.read(width * height / 4);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, texs[2]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width / 2, height / 2, GL_RED, GL_UNSIGNED_BYTE, buf.data());
glUniform1i(unis[2], 2);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
return;
}
void FrmScreen::resizeGL(int width, int height)
{
if (height == 0)
{
height = 1;
}
glViewport(0, 0, width, height);
}
void FrmScreen::displayYuvImage(QString yuvPath)
{
_img_path = yuvPath;
update();
}
void FrmScreen::displayImage(QString imgPath)
{
_img_path = imgPath;
update();
}
Why the code not working properly on macos?
On a Mac, you must use an OpenGL 4.1 Core profile context with the forward compatibility flag set. With Qt 6 this can be achieved by turning off QSurfaceFormat::DeprecatedFunctions
:
QSurfaceFormat glFormat;
glFormat.setVersion(4, 1);
glFormat.setProfile(QSurfaceFormat::CoreProfile);
glFormat.setOption(QSurfaceFormat::DeprecatedFunctions, false);
glFormat.setRenderableType(QSurfaceFormat::OpenGL);
The shader version should be #version 410 core
.