Reputation: 1850
This question is a rewrite of Red video on top of normal video in Qt/OpenGL using QQuickItem but with the code broken to a minimum verifiable example. However, you should see the photos of the old question because they show what happens when real video is rendered into the screen
I have a class class called OpenGlVideoQtQuick2
which I'm testing two possible inheritations: from QQuickItem
versus with QQuickPaintedItem
. I get the expected behavior (a giant red screen) when OpenGlVideoQtQuick2
inherits from QQuickItem
, but not when it inherits from QQuickPaintedItem
, which is when I get a black screen with size 640x480, which is the size of the OpenGlVideoQtQuick2
item in main.qml
.
Here's what happens when class OpenGlVideoQtQuick2 : public QQuickPaintedItem
Here's what happens when class OpenGlVideoQtQuick2 : public QQuickItem
Here's the code:
OpenGlVideoQtQuick2.h:
#ifndef OpenGlVideoQtQuick2_H
#define OpenGlVideoQtQuick2_H
#include <QtQuick/QQuickItem>
#include <QtGui/QOpenGLShaderProgram>
#include <QtGui/QOpenGLFunctions>
#include <QtQuick/qquickwindow.h>
#include <QtGui/QOpenGLShaderProgram>
#include <QtGui/QOpenGLContext>
#include <QString>
#include <iostream>
#include <QTimer>
#include <QMatrix4x4>
#include <QQmlListProperty>
#include <QQuickPaintedItem>
class OpenGlVideoQtQuick2Renderer2 : public QObject, protected QOpenGLFunctions
{
Q_OBJECT
public:
OpenGlVideoQtQuick2Renderer2() {
}
~OpenGlVideoQtQuick2Renderer2();
void setViewportSize(const QSize &size) { m_viewportSize = size; }
void setWindow(QQuickWindow *window) { m_window = window; }
QMatrix4x4 qQuickVideoMatrix;
public slots:
void render();
private:
QSize m_viewportSize;
QOpenGLShaderProgram* program;
QQuickWindow *m_window;
GLuint unis[3] = {0};
GLuint texs[3] = { 0 };
unsigned char *datas[3] = { 0 };
bool firstRender = true;
int width = 0;
int height = 0;
int x = 0;
int y = 0;
};
//class OpenGlVideoQtQuick2 : public QQuickItem
class OpenGlVideoQtQuick2 : public QQuickPaintedItem
{
Q_OBJECT
protected:
void paint(QPainter* painter){std::cout << "PAINT BEING USED" << std::endl;};
public:
OpenGlVideoQtQuick2();
QMatrix4x4 getModelMatrix();
signals:
void tChanged();
public slots:
void sync();
void cleanup();
void update();//Updates the window
private slots:
void handleWindowChanged(QQuickWindow *win);
private:
OpenGlVideoQtQuick2Renderer2 *openGlVideoQtQuick2Renderer2;
};
#endif // OpenGlVideoQtQuick2_H
OpenGlVideoQtQuick.cpp:
#include "OpenGlVideoQtQuick2.h"
#define GET_STR(x) #x
#define A_VER 3
#define T_VER 4
//Simple shader. Outpus the same location as input, I guess
const char *vString4 = GET_STR(
attribute vec4 vertexIn;
attribute vec2 textureIn;
varying vec2 textureOut;
uniform mat4 u_transform;
void main(void)
{
gl_Position = u_transform * vertexIn;
textureOut = textureIn;
}
);
const char *tString4 = GET_STR(
varying vec2 textureOut;
void main(void)
{
gl_FragColor = vec4(1.0,0,0, 1.0);
}
);
void OpenGlVideoQtQuick2::update()
{
if (window())
window()->update();
}
OpenGlVideoQtQuick2::OpenGlVideoQtQuick2()
: openGlVideoQtQuick2Renderer2(nullptr)
{
connect(this, &QQuickItem::windowChanged, this, &OpenGlVideoQtQuick2::handleWindowChanged);
}
void OpenGlVideoQtQuick2::handleWindowChanged(QQuickWindow *win)
{
if (win) {
connect(win, &QQuickWindow::beforeSynchronizing, this, &OpenGlVideoQtQuick2::sync, Qt::DirectConnection);
win->setClearBeforeRendering(false);
}
}
void OpenGlVideoQtQuick2::cleanup()
{
if (openGlVideoQtQuick2Renderer2) {
delete openGlVideoQtQuick2Renderer2;
openGlVideoQtQuick2Renderer2 = nullptr;
}
}
OpenGlVideoQtQuick2Renderer2::~OpenGlVideoQtQuick2Renderer2()
{
delete program;
}
void OpenGlVideoQtQuick2::sync()
{
//std::cout << "sync called" << std::endl;
if (!openGlVideoQtQuick2Renderer2) {
openGlVideoQtQuick2Renderer2 = new OpenGlVideoQtQuick2Renderer2();
connect(window(), &QQuickWindow::beforeRendering, openGlVideoQtQuick2Renderer2, &OpenGlVideoQtQuick2Renderer2::render, Qt::DirectConnection);
connect(window(), &QQuickWindow::afterRendering, this, &OpenGlVideoQtQuick2::update, Qt::DirectConnection);
}
}
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
};
//TODO: FIX THIS https://stackoverflow.com/a/54773889/6655884
void OpenGlVideoQtQuick2Renderer2::render()
{
int frameWidth = 1280;
int frameHeight = 720;
if (this->firstRender) {
std::cout << "Creating QOpenGLShaderProgram " << std::endl;
program = new QOpenGLShaderProgram();
initializeOpenGLFunctions();
//this->m_F = QOpenGLContext::currentContext()->functions();
std::cout << "frameWidth: " << frameWidth << + " frameHeight: " << frameHeight << std::endl;
datas[0] = new unsigned char[frameWidth*frameHeight]; //Y
datas[1] = new unsigned char[frameWidth*frameHeight/4]; //U
datas[2] = new unsigned char[frameWidth*frameHeight/4]; //V
std::cout << "Fragment Shader compilation: " << program->addShaderFromSourceCode(QOpenGLShader::Fragment, tString4) << std::endl;
std::cout << "Vertex Shader compilation: " << program->addShaderFromSourceCode(QOpenGLShader::Vertex, vString4) << std::endl;
program->bindAttributeLocation("vertexIn",A_VER);
program->bindAttributeLocation("textureIn",T_VER);
std::cout << "program->link() = " << program->link() << std::endl;
glGenTextures(3, texs);//TODO: ERASE THIS WITH glDeleteTextures
this->firstRender = false;
}
program->bind();
QMatrix4x4 transform;
transform.setToIdentity();
program->setUniformValue("u_transform", this->qQuickVideoMatrix);
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");
//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, frameWidth, frameHeight, 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, frameWidth/2, frameHeight / 2, 0, GL_RED, GL_UNSIGNED_BYTE, 0);
//V
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, frameWidth / 2, frameHeight / 2, 0, GL_RED, GL_UNSIGNED_BYTE, 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texs[0]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, frameWidth, frameHeight, GL_RED, GL_UNSIGNED_BYTE, datas[0]);
glUniform1i(unis[0], 0);
glActiveTexture(GL_TEXTURE0+1);
glBindTexture(GL_TEXTURE_2D, texs[1]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, frameWidth/2, frameHeight / 2, GL_RED, GL_UNSIGNED_BYTE, datas[1]);
glUniform1i(unis[1],1);
glActiveTexture(GL_TEXTURE0+2);
glBindTexture(GL_TEXTURE_2D, texs[2]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, frameWidth / 2, frameHeight / 2, GL_RED, GL_UNSIGNED_BYTE, datas[2]);
glUniform1i(unis[2], 2);
glDrawArrays(GL_TRIANGLE_STRIP,0,4);
program->disableAttributeArray(A_VER);
program->disableAttributeArray(T_VER);
program->release();
}
main.qml:
import QtQuick 2.0
import OpenGlVideoQtQuick2 1.0
Grid {
columns: 2
spacing: 2
width: 1280
height: 720
OpenGlVideoQtQuick2 {
width: 640
height: 360
}
}
So, I need to make my class derive from QQuickPaintedItem
, not QQuickItem
, and I need that black screen to not appear on top of my red screen, which is where the actual video will be loaded.
The entire project can be found here: https://github.com/lucaszanella/QQuickPaintedItemBug/tree/c9c2b23d891689a63fbaf2f014142be1f3c5ff0d, where you can compile and test. I recommend to compiling using locally installed cmake
and qt
folders as explained in the Readme.md
file in github
Upvotes: 1
Views: 753
Reputation: 8311
I would not expect the result of using QQuickItem
and QQuickPaintedItem
to be the same.
When you use QQuickPaintedItem
, you are supposed to render the item using the paint()
function. Since you call your render()
function on beforeRendering()
, then just after you do your own rendering, the QQuickPaintedItem
will render on top of it what you should have painted in the paint()
function.
You can prevent the QQuickPaintedItem
from rendering the black rectangle by reimplementing updatePaintNode()
as an empty function.
Upvotes: 2