Mam widget który używam do wizualizacji danych dla użytkownika w GUI
chciałbym użyć tego samego widget-u "offscreen" aby wygenerować plik wideo w tle (poza wątkiem GUI)
Czy to jest możliwe ?
Dla jednego wątku działa ale na konsoli pojawiają się ostrzeżenia
"Attempting to create QWindow-based QOffscreenSurface outside the gui thread. Expect failures."
Jak jest jeden watek to "Expect failures." nie pojawiły się
Jak zwiększam ilość wątków to prawdopodobieństwo failures wzrasta
Jak to zrobić poprawnie ?
I przykładowy kod
supportsThreadedOpenGL zwraca TRUE
UWAGA ! Ten kod jest niebezpieczny dla Linux-a
wartość THREAD_COUNT = 8 zawiesza system operacyjny tak skutecznie
że działa tylko reset
#include <thread>
#include <QApplication>
#include <QPushButton>
#include <QOffscreenSurface>
#include <QPainter>
#include <QWidget>
#include <QOpenGLWidget>
#include <QOpenGLShaderProgram>
#include <QOpenGLFunctions>
//! [3]
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";
//! [3]
class WidgetSample : public QOpenGLWidget, protected QOpenGLFunctions
{
public:
WidgetSample(QWidget *parent = nullptr): QOpenGLWidget(parent){};
~WidgetSample(){};
QOpenGLShaderProgram *m_program = nullptr;
int m_frame = 0;
GLint m_posAttr = 0;
GLint m_colAttr = 0;
GLint m_matrixUniform = 0;
protected:
void initializeGL() override
{
initializeOpenGLFunctions();
glClearColor(1, 0, 0, 0.5);
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 paintGL() override
{
QPainter painter;
painter.begin(this);
painter.beginNativePainting();
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()*/ 60.0, 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;
painter.endNativePainting();
painter.setClipRect(rect());
painter.fillRect(QRect(32,32,300,64),Qt::blue);
QFont font;
font.setPointSize(32);
painter.setFont(font);
painter.setPen (QColor(11, 99, 99, 255));
painter.drawText(rect(),Qt::AlignVCenter | Qt::AlignHCenter, "Hello world" );
painter.end();
}
};
#define THREAD_COUNT 1
auto startThread= [](int threadNum){
std::thread *koza = new std::thread([threadNum](){
WidgetSample * w = new WidgetSample;
w->resize(800,480);
// QSurfaceFormat glFormat;
// glFormat.setVersion(3, 3);
// glFormat.setProfile(QSurfaceFormat::CoreProfile);
// glFormat.setRenderableType(QSurfaceFormat::OpenGL);
// QOffscreenSurface *surface = new QOffscreenSurface();
// surface->setFormat(glFormat);
// surface->create();
// w->context()->makeCurrent(surface);
for(int i=0; i<16;++i)
{
w->update();
QString fnameOut = QString("mageWG2_%1_%2.png").arg(i).arg(threadNum);
qDebug()<<"fnameOut:" << fnameOut <<" "<<threadNum;
w->grabFramebuffer().save(fnameOut);
};
});
return koza;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
WidgetSample mainForm;
qDebug() <<"mainForm.context()->supportsThreadedOpenGL()=="<< mainForm.context()->supportsThreadedOpenGL();
QPushButton *pushButton = new QPushButton(&mainForm);
pushButton->setText("start threads");
pushButton->resize(88,44);
QObject::connect(pushButton, &QPushButton::clicked, &mainForm, [&](){
for(int i=0; i<THREAD_COUNT ; ++i)
{
auto t= startThread(i);
}
});
mainForm.setGeometry(100,100,300,200);
mainForm.show();
return a.exec();
}
///// CMAKE //////////////////
// cmake_minimum_required(VERSION 3.5)
// project(WidgetSample VERSION 0.1 LANGUAGES CXX)
// set(CMAKE_AUTOUIC ON)
// set(CMAKE_AUTOMOC ON)
// set(CMAKE_AUTORCC ON)
// set(CMAKE_CXX_STANDARD 17)
// set(CMAKE_CXX_STANDARD_REQUIRED ON)
// find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets OpenGL)
// find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets OpenGL)
// add_executable(WidgetSample main.cpp)
// target_link_libraries(WidgetSample PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::OpenGL ) # Qt::OpenGLWidgets