Render/Layout performance very slow. High CPU usage for only 20 QLabels in 30fps
-
Just for test
Seems to use 6-8% on windows
-
The thing is, the prototype that has most of the stuff already is implemented in TypeScript/HTML5 (no OpenGL here), and we have at 15fps roughly the same CPU usage as QT has with displaying 20 QLabels at 30fps. We wanted to pump it up to 60 fps to get a very smooth running GUI, but that would have cost one CPU core using the HTML5 engine, so I gave QT a try. I'm afraid QT's CPU usage goes through the roof when I implement it using TableView and way more QLabels, Buttons, SelectBox etc as well.
So strictly speaking, I thought we could go the native approach to save a LOT cpu circles, but it seems Chromium's HTML engine is already highly optimised as we can compare directly the CPU usage here. I see however an incredible huge CPU usage difference when I compare QT/Chromium with imGUI, so I still have the hope that I can get the same performance using QT. 😎
"Create ID" It's actually a simple table grid, just text. First row has special styling due to be the table header with some onclick events.
-
Hi
Give it a day more so other timezones come online.
Some user knows far more about Qt on mac than me.Im pretty sure it can get better but i agree with you that
it doesn't look as it will scale well.I have seen reports on QWidgets and macOS having not optimal performance especially
with retina displays but im not using macOS so im not sure what current status is. -
Hi,
If you want something fancy and with OpenGL then @mrjj's recommendation of QtQuick is the way to go. QtQuick is OpenGL rendered by default.
Qt Widgets have never been hardware accelerated and likely won't be any time soon. They allow you to build applications for system with lower specs.
As the warning of QGraphicsProxyWidget states, that class should not be used in hight performance scenarios which your application is.
As for Apple's OpenGL stack, AFAIK, it's known to not be most recent or up to date regarding the standard, so beware of that when comparing to other systems.
On an unrelated note, it's Qt, QT stands for Apple QuickTime which you are likely not using even if developing on macOS.
-
Alright, I played more with QPainter, and made my own custom QWidget that uses paintEvent() to call
painter->drawText
directly. That should be the fastest I can get, right? Indeed, it was roughly twice the speed (8% vs 17%). However, there's no automatic geometric calculation yet for the layout manager.My code:
// main.cpp #include <QtWidgets/QApplication> #include <QTimer> #include <QGraphicsScene> #include <QGraphicsView> #include <QGridLayout> #include <QtOpenGL> #include <QOpenGLWidget> #include <QDebug> class MyWidget: public QWidget { public: QString text; QSize size; MyWidget() : QWidget() { setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); size.setWidth(100); size.setHeight(15); } void paintEvent(QPaintEvent *event) override { QPainter painter(this); painter.setPen(Qt::black); painter.setFont(QFont("Helvetica", 14)); painter.drawText(0, 14, text); } QSize sizeHint() const { return size; } void setPlainText(QString text) { this->text = text; update(); } }; int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_UseOpenGLES); QApplication app(argc, argv); const int labelsCount = 200; const int cols = 10; QGraphicsScene scene; QGridLayout *layout = new QGridLayout(); QGraphicsView view(&scene); view.setLayout(layout); //regarding documentation, this enabled OpengGL //and in fact, the rendered font looks different when having this line active view.setViewport(new QOpenGLWidget()); //helps a lot the more draws we have // view.setViewport(new QGLWidget()); //doesnt work on OSX QVarLengthArray<MyWidget, labelsCount> labels(labelsCount); for (int i =0; i < labelsCount; i++ ){ labels[i].setPlainText(QString("Hallo %1").arg(i)); layout->addWidget(&labels[i], floor(i / cols), i % cols); } QTimer *timer = new QTimer(); timer->setInterval(1000/30); int counter = 0; QObject::connect(timer, &QTimer::timeout, [&] { counter++; for (int i =0; i < labelsCount; i++ ){ auto text = QStringLiteral("(%1): %3").arg(i).arg(counter); labels[i].setPlainText(text); } }); timer->start(); view.show(); return app.exec(); }
After this good news I tested with a more realistic scenario: Having 200 labels updating in real-time (30fps). Screenshot:
At the bottom you see my HTOP Cpu utilization from that process. Unfortunately, still at 50% of a Intel i7 with over 3Ghz. The GPU isn't the bottleneck either, since I got a NVIDIA Titan Xp with 12GB RAM - no retina Display.
I'm running out of ideas here since I've never used QT until today and probably lack some fundamentals. I actually can't believe that printing directly with the QPainter is so darn slow. so there must be something I'm currently missing . Does anyone have an idea what else I could do? (except to leave QT behind)
-
I tried further to see where the bottleneck is. My next example shows how 200 very simple progress-bars perform. Result is that it renders way faster, only 13% CPU usage (30fps). However, drawing such OpenGL primitives is in other GUI abstraction an extremely easy task and costs almost zero CPU.
Code:
// main.cpp #include <QtWidgets/QApplication> #include <QTimer> #include <QGraphicsScene> #include <QGraphicsView> #include <QGridLayout> #include <QtOpenGL> #include <QOpenGLWidget> #include <QDebug> class MyWidget: public QWidget { public: QString text; int value = 0; //0 -> 100 QSize size; MyWidget() : QWidget() { setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); size.setWidth(100); size.setHeight(15); } void paintEvent(QPaintEvent *event) override { QPainter painter(this); painter.fillRect(QRect(0, 0, value % 100, 14), Qt::black); } QSize sizeHint() const { return size; } void setValue(int value) { this->value = value; update(); } }; int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_UseOpenGLES); QApplication app(argc, argv); const int labelsCount = 200; const int cols = 10; QGraphicsScene scene; QGridLayout *layout = new QGridLayout(); QGraphicsView view(&scene); view.setLayout(layout); //regarding documentation, this enabled OpengGL //and in fact, the rendered font looks different when having this line active view.setViewport(new QOpenGLWidget()); //helps a lot the more draws we have QVarLengthArray<MyWidget, labelsCount> labels(labelsCount); for (int i =0; i < labelsCount; i++ ){ layout->addWidget(&labels[i], floor(i / cols), i % cols); } QTimer *timer = new QTimer(); timer->setInterval(1000/30); int counter = 0; QObject::connect(timer, &QTimer::timeout, [&] { counter++; for (int i =0; i < labelsCount; i++ ){ labels[i].setValue(counter); } }); timer->start(); view.show(); return app.exec(); }
Screen:
I also just made sure it's really not my hardware and implemented in another GUI library almost the same case: 200 progress-bars (although in this example it shows also text). Result is 4% CPU usage, but with a major difference that it renders at 100fps. If I go 100 fps in Qt example above, I get 50% cpu usage. If I add text right next to the rect I get probably 100%.
Screen of the other GUI lib:
So I guess there must be something in QT where I can have more direct access to the OpenGL abstraction.
-
I tried now as you guys suggested the QT Quick Widgets, which should be rendered with OpenGL and with performance in mind (I mean it should run on mobile devices right?). Result is not really satisfying, I guess even worse. 100 labels, 30fps -> 33% CPU burn.
Code for that:
//main.cpp #include <QGuiApplication> #include <QTimer> #include <QQuickItem> #include <QQmlApplicationEngine> int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); QQmlApplicationEngine engine; engine.load(QUrl(QStringLiteral("main.qml"))); QQuickItem *list = engine.rootObjects()[0]->findChild<QQuickItem *>(QString("list"), Qt::FindChildrenRecursively); QTimer *timer = new QTimer(); timer->setInterval(1000/30); int counter = 0; QObject::connect(timer, &QTimer::timeout, [&] { counter++; for(auto const& item: list->childItems()) { item->setProperty("text", QString("WTF %1").arg(counter)); } }); timer->start(); return app.exec(); }
import QtQuick 2.6 import QtQuick.Controls 2.0 ApplicationWindow { id: root width: 600 height: 500 visible: true Grid { objectName: "list" rows: 50 columns: 10 spacing: 10 Repeater { model: 100; objectName: "repeater" Label { text: "Hello world" width: 60 objectName: "label" + index } } } }
-
If you'd like to go on with raw OpenGL, then the Qt OpenGL and GUI modules are what you want to look at, they provide everything you need to write OpenGL application.