Render/Layout performance very slow. High CPU usage for only 20 QLabels in 30fps
-
Thanks Mrjj,
that helped me quite a lot!
I've come up with following code to use GraphicsView directly and draw some texts (hopefully in OpenGL) using QGraphicsTextItem.
// main.cpp #include <QtWidgets/QApplication> #include <QTimer> #include <QGraphicsScene> #include <QGraphicsView> #include <QtOpenGL> int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_UseOpenGLES); QApplication app(argc, argv); const int labelsCount = 20; QGraphicsScene scene; QGraphicsView view(&scene); //regarding documentation, this enabled OpengGL //and in fact, the rendered font looks different when having this line active view.setViewport(new QGLWidget()); QVarLengthArray<QGraphicsTextItem, labelsCount> labels(labelsCount); for (int i =0; i < labelsCount; i++ ){ auto text = QStringLiteral("(%1)").arg(i); labels[i].setPlainText(text); labels[i].setPos(0, i * 15); scene.addItem(&labels[i]); } 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(); }
However, I'm still at 18% CPU usage. To me, displaying 20 text item without layout manager shouldn't be anything that costs so much CPU. Do I do something wrong here? It looks to me, it uses now OpenGL (text renders different, not so sharp anymore).
-
@marcj
Hi
Could you test http://doc.qt.io/qt-5/qtwidgets-graphicsview-chip-example.html
and zoom in and out.As far as i recall you have to do
QOpenGLWidget *glWidget = new QOpenGLWidget();
setViewport(glWidget); to switch to openGl.But i have only tried the older QGLWidget so not sure it still applies.
Wait a bit and other more heavy openGL users will tell if you need something to use openGl.
I also seen many posts about it here but search-fu didnt bring it up ;) -
Here is my code using QGraphicsProxyWidget that is returned when I add QLabel into a QGraphicsView.
// main.cpp #include <QtWidgets/QApplication> #include <QTimer> #include <QGraphicsScene> #include <QGraphicsView> #include <QtOpenGL> int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_UseOpenGLES); QApplication app(argc, argv); const int labelsCount = 20; QGraphicsScene scene; QGraphicsView view(&scene); //regarding documentation, this enabled OpengGL //and in fact, the rendered font looks different when having this line active view.setViewport(new QGLWidget()); QVarLengthArray<QLabel, labelsCount> labels(labelsCount); for (int i =0; i < labelsCount; i++ ){ auto text = QStringLiteral("(%1)").arg(i); labels[i].setText(text); labels[i].setGeometry(QRect(QPoint(0, i * 15), QSize(100, 15))); scene.addWidget(&labels[i]); } 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].setText(text); } }); timer->start(); view.show(); return app.exec(); }
It is in fact OpenGL rendered now, however on my working station OSX 10.12.4 I get tons of warning messages in the console every time the text renders.
QMacCGContext:: Unsupported painter devtype type 1 QMacCGContext:: Unsupported painter devtype type 1 QMacCGContext:: Unsupported painter devtype type 1 QMacCGContext:: Unsupported painter devtype type 1
Probably related to https://bugreports.qt.io/browse/QTBUG-32639.
However, still same slow performance of over 15% CPU usage.
Running out of ideas here. :)
-
@marcj said in Render/Layout performance very slow. High CPU usage for only 20 QLabels in 30fps:
Unsupported painter devtype type 1
Gah , i think you hit a bug with QGLWidget.
(its the old one, the new is QOpenGLWidget )https://bugreports.qt.io/browse/QTBUG-32639
sounds like that.Are you on mac ?
-
Btw, do you need anything besides an array of texts ?
I wondering how how fast a small custom class would be.
(to draw text)But i get the feeling you need openGL for other stuff too?
-
Yes, I'm on a Mac.
@mrjj said in Render/Layout performance very slow. High CPU usage for only 20 QLabels in 30fps:
As far as i recall you have to do
QOpenGLWidget *glWidget = new QOpenGLWidget();
setViewport(glWidget); to switch to openGl.I tried both separately, the differences weren't that big.
//not having openGL here results in 18.5% usage view.setViewport(new QOpenGLWidget()); //17.1% usage view.setViewport(new QGLWidget()); //17.8% usage
The thing is, I see definitely differences in the rendered text (openGL is more blurry and more bold), however the CPU-usage is almost the same, that leads me to the conclusion that there is something somewhere else the bottleneck.
@mrjj said in Render/Layout performance very slow. High CPU usage for only 20 QLabels in 30fps:
Btw, do you need anything besides an array of texts ?
Yes, actually the main view is a TableList/Grid, with roughly 10 columns and max 20 rows.
Most columns are text, but we have also small plots in one column and in another column a progress-bar. The data for that view is coming from a streaming server that is holding machine learning data that yields real-time data in high frequency. It's important to display the stuff in at least 15fps (so plot and progress-bar is smooth). I actually thought rendering the text alone is the smallest thing to think about. :D well it turned out, I have to go to the basics first.The other first is actually more complex (both are displayed at the same time, the second when a row from the TableList is selected) which shows anything in realtime about that in more detail manner. Bigger and more plots (up to 20 at the same time), console outputs in realtime, some graphs, progress-bars, quite a lot of status text (single QLabels aligned in a grid).
Here's a screen of it:
I need OpenGL for the plots and progress-bars (Custom made), due to performance reason. Obviously :)
-
Hi
hmm, i would have imagined better performance.
I think the CPU usage comes from keeping event loop busy
when you set the text, it triggers a paint event and since it does it
very fast i assume the main event loop is always busy.Ah, ok so we cant just cheat and make a custom control that draws array of text.
You have other elements in that layout ?QLabel can have border and and be styles so compared to raw
QPainter.drawText it is a it heavy(er).
Not sure how much it would help with custom though.
Going to try with your sample and see.Update:: looks nice. very modern.
-
Wondering:
Those in Created ID etc
are the labels ? -
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.