From 7c6625b105511191f739d07dc658094ff6f682ac Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 3 Nov 2015 11:41:01 +0100 Subject: Support mixing native child widgets with texture-based ones MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently QOpenGLWidget and QQuickWidget do not support having native child widgets inside the same top-level window. In some cases this is inevitable, f.ex. multimedia may require native windows when used from widget apps. winId() calls made for various (valid or invalid) reasons are also problematic. There are no blockers for supporting this setup, however. By storing multiple texture lists (one for each subtree where the root is a native widget), adding the missing markDirtyOnScreen calls, letting each native widget access the correct texture list (i.e. the one corresponding to its children) when they are (separately) flushed, and fixing composeAndFlush() to take the update region and the (native child) offset into account, it can all be made functional. The change also fixes the issue of keeping GL-based compositing enabled even after all render-to-texture widgets in the window become hidden. Due to the changes of how such widgets are gathered, composeAndFlush() is not invoked anymore when no such widgets are discovered for a given native parent. This is great since having compositing enabled infinitely is an issue for applications like Qt Creator that implement certain views with QQuickWidgets but cannot afford the cost of texture uploads in other places (e.g. for the text editor) on slower machines. The openglwidget manual test is greatly enhanced to test various situations (MDI, scroll areas, tab widgets, QOpenGLWidget as native child, QOpenGLWidget with non-tlw native parent, etc.) Task-number: QTBUG-48130 Task-number: QTBUG-49172 Change-Id: Iad098359c8bcf749f01c050da0853415e1550eda Reviewed-by: Paul Olav Tvete Reviewed-by: Morten Johan Sørvig --- tests/manual/qopenglwidget/openglwidget/main.cpp | 175 +++++++++++++++++++-- .../qopenglwidget/openglwidget/openglwidget.cpp | 23 ++- .../qopenglwidget/openglwidget/openglwidget.h | 5 +- 3 files changed, 184 insertions(+), 19 deletions(-) (limited to 'tests/manual/qopenglwidget/openglwidget') diff --git a/tests/manual/qopenglwidget/openglwidget/main.cpp b/tests/manual/qopenglwidget/openglwidget/main.cpp index aaa48ea60a..a56cea1dfe 100644 --- a/tests/manual/qopenglwidget/openglwidget/main.cpp +++ b/tests/manual/qopenglwidget/openglwidget/main.cpp @@ -35,13 +35,108 @@ #include #include #include +#include +#include +#include +#include #include +#include +#include +#include +#include #include #include #include +#include + +class Tools : public QObject +{ + Q_OBJECT + +public: + Tools(QWidget *root, QWidget *widgetToTurn, const QVector glwidgets) + : m_root(root), m_widgetToTurn(widgetToTurn), m_glWidgets(glwidgets) { } + void dump(); + +private slots: + void turnNative(); + void hideShowAllGL(); + void dumpCompositingStatus(); + +signals: + void aboutToShowGLWidgets(); + +private: + void dumpWidget(QWidget *w, int indent = 0); + + QWidget *m_root; + QWidget *m_widgetToTurn; + QVector m_glWidgets; +}; + +void Tools::turnNative() +{ + qDebug("Turning into native"); + m_widgetToTurn->winId(); + dump(); +} + +void Tools::hideShowAllGL() +{ + if (m_glWidgets[0]->isVisible()) { + qDebug("Hiding all render-to-texture widgets"); + foreach (QWidget *w, m_glWidgets) + w->hide(); + } else { + qDebug("Showing all render-to-texture widgets"); + emit aboutToShowGLWidgets(); + foreach (QWidget *w, m_glWidgets) + w->show(); + } +} + +void Tools::dump() +{ + qDebug() << "Widget hierarchy"; + dumpWidget(m_root); + qDebug() << "========"; +} + +void Tools::dumpWidget(QWidget *w, int indent) +{ + QString indentStr; + indentStr.fill(' ', indent); + qDebug().noquote() << indentStr << w << "winId =" << w->internalWinId(); + foreach (QObject *obj, w->children()) { + if (QWidget *cw = qobject_cast(obj)) + dumpWidget(cw, indent + 4); + } +} + +void Tools::dumpCompositingStatus() +{ + QWindow *w = m_root->window()->windowHandle(); + qDebug() << "Compositing status for" << w << m_root->window() << "is" << QWindowPrivate::get(w)->compositing; +} + +class TabWidgetResetter : public QObject +{ + Q_OBJECT +public: + TabWidgetResetter(QTabWidget *tw) : m_tw(tw) { } +public slots: + void reset() { m_tw->setCurrentIndex(0); } +private: + QTabWidget *m_tw; +}; int main(int argc, char *argv[]) { + if (argc > 1 && !strcmp(argv[1], "--sharecontext")) { + qDebug("Requesting all contexts to share"); + QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); + } + QApplication a(argc, argv); QSurfaceFormat format; @@ -53,28 +148,86 @@ int main(int argc, char *argv[]) } qDebug() << "Requesting" << format; - QMdiArea w; - w.resize(400,400); + QMainWindow wnd; + wnd.setObjectName("Main Window"); + wnd.resize(1024, 768); + + QMdiArea *w = new QMdiArea; + w->setObjectName("MDI area"); + w->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + wnd.setCentralWidget(w); - OpenGLWidget *glw = new OpenGLWidget; + OpenGLWidget *glw = new OpenGLWidget(33, QVector3D(0, 0, 1)); + glw->setObjectName("First GL Widget with 33 ms timer"); glw->setFormat(format); - w.addSubWindow(glw); - glw->setMinimumSize(100,100); + glw->setMinimumSize(100, 100); + QMdiSubWindow *sw = w->addSubWindow(glw); + sw->setObjectName("First MDI Sub-Window"); + sw->setWindowTitle("33 ms timer"); - OpenGLWidget *glw2 = new OpenGLWidget; + OpenGLWidget *glw2 = new OpenGLWidget(16); + glw2->setObjectName("Second GL Widget with 16 ms timer"); glw2->setFormat(format); - glw2->setMinimumSize(100,100); - w.addSubWindow(glw2); + glw2->setMinimumSize(100, 100); + QOpenGLWidget *glw22 = new OpenGLWidget(16); + glw22->setObjectName("Second #2 GLWidget"); + glw22->setParent(glw2); + glw22->resize(40, 40); + sw = w->addSubWindow(glw2); + sw->setObjectName("Second MDI Sub-Window"); + sw->setWindowTitle("16 ms timer"); + + OpenGLWidget *glw3 = new OpenGLWidget(0); // trigger updates continuously, no timer + glw3->setObjectName("GL widget in scroll area (possibly native)"); + glw3->setFormat(format); + glw3->setFixedSize(600, 600); + QScrollArea *sa = new QScrollArea; + sa->setWidget(glw3); + sa->setMinimumSize(100, 100); + sa->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + sw = w->addSubWindow(sa); + sw->setObjectName("MDI Sub-Window for scroll area"); + sw->setWindowTitle("Cont. update"); + sw->resize(300, 300); + sa->verticalScrollBar()->setValue(300); QLCDNumber *lcd = new QLCDNumber; lcd->display(1337); - lcd->setMinimumSize(300,100); - w.addSubWindow(lcd); + lcd->setMinimumSize(300, 100); + sw = w->addSubWindow(lcd); + sw->setObjectName("MDI Sub-Window for LCD widget"); + sw->setWindowTitle("Ordinary widget"); + + QTabWidget *tw = new QTabWidget; + QOpenGLWidget *glw4 = new OpenGLWidget(16, QVector3D(1, 0, 0)); + glw4->setObjectName("GL widget in tab widget"); + tw->addTab(glw4, "OpenGL"); + QLabel *label = new QLabel("Another tab"); + tw->addTab(label, "Not OpenGL"); + tw->setMinimumSize(100, 100); + sw = w->addSubWindow(tw); + sw->setObjectName("MDI Sub-Window for tab widget"); + sw->setWindowTitle("Tabs"); - w.show(); + TabWidgetResetter twr(tw); + Tools t(&wnd, glw3, QVector() << glw << glw2 << glw3 << glw4); + QObject::connect(&t, SIGNAL(aboutToShowGLWidgets()), &twr, SLOT(reset())); + QMenu *toolsMenu = wnd.menuBar()->addMenu("&Tools"); + toolsMenu->addAction("&Turn widgets (or some parent) into native", &t, SLOT(turnNative())); + toolsMenu->addAction("&Hide/show all OpenGL widgets", &t, SLOT(hideShowAllGL())); + + QTimer compStatusDumpTimer; + QObject::connect(&compStatusDumpTimer, SIGNAL(timeout()), &t, SLOT(dumpCompositingStatus())); + compStatusDumpTimer.start(5000); + + wnd.show(); if (glw->isValid()) qDebug() << "Got" << glw->format(); + t.dump(); + return a.exec(); } + +#include "main.moc" diff --git a/tests/manual/qopenglwidget/openglwidget/openglwidget.cpp b/tests/manual/qopenglwidget/openglwidget/openglwidget.cpp index d47e12edc8..4d2463b84d 100644 --- a/tests/manual/qopenglwidget/openglwidget/openglwidget.cpp +++ b/tests/manual/qopenglwidget/openglwidget/openglwidget.cpp @@ -75,16 +75,23 @@ public: int w,h; QWidget *q; + + int m_interval; + QVector3D m_rotAxis; }; -OpenGLWidget::OpenGLWidget(QWidget *parent) +OpenGLWidget::OpenGLWidget(int interval, const QVector3D &rotAxis, QWidget *parent) : QOpenGLWidget(parent) { - d = new OpenGLWidgetPrivate(this); - QTimer *timer = new QTimer(this); - connect(timer, SIGNAL(timeout()), this, SLOT(update())); - timer->start(30); + d.reset(new OpenGLWidgetPrivate(this)); + d->m_interval = interval; + d->m_rotAxis = rotAxis; + if (interval > 0) { + QTimer *timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(update())); + timer->start(interval); + } } OpenGLWidget::~OpenGLWidget() @@ -152,7 +159,8 @@ void OpenGLWidgetPrivate::render() 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 / 30/*screen()->refreshRate()*/, 0, 1, 0); + const qreal angle = 100.0f * m_frame / 30; + matrix.rotate(angle, m_rotAxis); m_program->setUniformValue(m_matrixUniform, matrix); @@ -182,4 +190,7 @@ void OpenGLWidgetPrivate::render() m_program->release(); ++m_frame; + + if (m_interval <= 0) + q->update(); } diff --git a/tests/manual/qopenglwidget/openglwidget/openglwidget.h b/tests/manual/qopenglwidget/openglwidget/openglwidget.h index 4dc5fde067..a1d5490845 100644 --- a/tests/manual/qopenglwidget/openglwidget/openglwidget.h +++ b/tests/manual/qopenglwidget/openglwidget/openglwidget.h @@ -35,13 +35,14 @@ #define OPENGLWIDGET_H #include +#include class OpenGLWidgetPrivate; class OpenGLWidget : public QOpenGLWidget { Q_OBJECT public: - OpenGLWidget(QWidget *parent = 0); + OpenGLWidget(int interval = 30, const QVector3D &rotAxis = QVector3D(0, 1, 0), QWidget *parent = 0); ~OpenGLWidget(); void initializeGL(); @@ -49,7 +50,7 @@ public: void paintGL(); private: - OpenGLWidgetPrivate *d; + QScopedPointer d; }; #endif // OPENGLWIDGET_H -- cgit v1.2.3