3

Ich entwickle eine CAD-Anwendung mit Qt 5.6.2, die benötigt wird, um in billigen Computern zur gleichen Zeit ausgeführt zu werden, die Tausende von Elementen in der gleichen Szene verarbeiten muss. Aus diesem Grund musste ich viele Experimente durchführen, um die beste Leistung zu erzielen.Wie kann ich die Leistung von QGraphicsView optimieren?

Ich habe mich entschieden, diesen Beitrag zu erstellen, um anderen und auch mir selbst zu helfen, solange andere Leute auch mit mehr Optimierungstipps beitragen.

Mein Text ist immer noch in Arbeit und ich kann es aktualisieren, wenn ich bessere Techniken entdecke (oder dass ich etwas wirklich Dummes gesagt habe).

Antwort

8

deaktivieren Szene Interaktion

Ereignisbehandlung ist durch einen guten Teil der CPU-Auslastung durch den QGraphicsView Motor verantwortlich. Bei jeder Mausbewegung fragt die Ansicht die Szene nach den Elementen unter der Maus ab, die die QGraphicsItem :: shape() -Methode zum Erkennen von Schnittpunkten aufrufen. Es passiert sogar mit deaktivierten Gegenständen. Wenn Sie Ihre Szene nicht benötigen, um mit Mausereignissen zu interagieren, können Sie QGraphicsView :: setInteneractive (false) einstellen. In meinem Fall hatte ich zwei Modi in meinem Werkzeug (Messen und Bewegen/Drehen), wo die Szene im Wesentlichen statisch war und alle Bearbeitungsoperationen stattdessen von QGraphicsView ausgeführt wurden. Dadurch konnte ich die Bildrate in 30% erhöhen, leider funktionierte ViewportAnchor :: AnchorUnderMouse nicht mehr (ein Hack, um es zu beheben, war die Interaktion wieder zu aktivieren und QGraphicsView :: mouseMoveEvent (QMouseEvent e) zu überschreiben, um künstlich eine der beiden zu drücken) die Maustasten verwenden ein neues QMouseEvent basierend auf e).

Ihre QPainterPaths Reuse

Cache Ihre QPainterPaths in Ihrem QGraphicsItem Objekt. Das Bauen und Bestücken kann sehr langsam sein. In meinem Fall dauerte es 6 Sekunden, um eine Datei zu lesen, nur weil ich eine Punktwolke mit 6000 Punkten in einen QPainterPath mit mehreren Rechtecken umwandelte. Du wirst es nicht mehr als einmal tun wollen.

Vereinfachen Sie Ihre QGraphicsItem :: Form()

Diese Methode mehrere Male während Mausereignisse genannt wird, auch wenn das Element nicht aktiviert ist. Versuche es so effizient wie möglich zu machen. Manchmal ist es sogar nicht ausreichend, den QPainterPath zwischenzuspeichern, da der Pfadkreuzungsalgorithmus, der von der Szene ausgeführt wird, für komplexe Formen sehr langsam sein kann. In meinem Fall gab ich eine Form mit rund 6000 Rechtecken zurück und es war ziemlich langsam. Nach dem Downsampling der Punktwolke konnte ich die Anzahl der Rechtecke auf etwa 1000 reduzieren, was die Performance deutlich verbesserte, aber immer noch nicht ideal war, da shape() immer noch aufgerufen wurde, selbst wenn der Gegenstand deaktiviert war. Aus diesem Grund habe ich beschlossen, das ursprüngliche QGraphicsItem: shape() beizubehalten (das das Rechteck des Begrenzungsrechtecks ​​zurückgibt) und nur die komplexere zwischengespeicherte Form zurückzugeben, wenn das Element aktiviert ist. Es hat die Framerate beim Bewegen der Maus in fast 40% verbessert, aber ich denke immer noch, dass es ein Hack ist und werde diesen Beitrag aktualisieren, wenn ich eine bessere Lösung finde. Trotzdem hatte ich in meinen Tests kein Problem, solange ich die Bounding Box unberührt ließ. Wenn dies nicht der Fall ist, müssen Sie prepareGeometryChange() aufrufen und dann die Begrenzungsbox und die Shape-Caches an anderer Stelle aktualisieren.

-Test Beide: Raster und OpenGL Motoren

Ich hatte erwartet, dass OpenGL immer als Raster besser wäre, und es ist wahrscheinlich wahr, wenn alles, was Sie wollen, ist die CPU-Auslastung aus offensichtlichen Gründen zu reduzieren. Wenn Sie jedoch nur die Anzahl der Bilder pro Sekunde erhöhen möchten, besonders in billigen/alten Computern, sollten Sie auch versuchen, Raster zu testen (das standardmäßige QGraphicsView-Ansichtsfenster).In meinen Tests war das neue QOpenGLWidget etwas schneller als das alte QGLWidget, aber die Anzahl der FPS war fast 20% langsamer als bei Verwendung von Raster. Natürlich kann es anwendungsspezifisch sein und das Ergebnis kann je nachdem, was Sie rendern, unterschiedlich sein.

Verwenden Sie FullViewportUpdate mit OpenGL und bevorzugen Sie andere partielle Ansichtsfenster-Update-Methode mit Raster (erfordert eine strengere Begrenzung Rechteck Aufrechterhaltung der Elemente obwohl).

Versuchen Sie VSync zu deaktivieren/aktivieren, um zu sehen, welche besser für Sie funktioniert: QSurfaceFormat :: defaultFormat(). SetSwapInterval (0 oder 1). Die Aktivierung kann die Bildrate reduzieren und die Deaktivierung kann zum "Zerreißen" führen. https://www.khronos.org/opengl/wiki/Swap_Interval

Cache Complex QGraphicsItems

Wenn Ihr QGraphicsItem :: Lack Betrieb zu komplex ist und in der gleichen Art meist statisch, versuchen Sie das Caching zu aktivieren. Verwenden Sie DeviceCoordinateCache, wenn Sie keine Transformation (wie Rotation) auf die Elemente oder den ItemCoordinateCache anwenden. Vermeiden Sie den Aufruf von QGraphicsItem :: update() sehr oft, oder es kann sogar langsamer als ohne Zwischenspeicherung sein. Wenn Sie etwas an Ihrem Objekt ändern müssen, haben Sie zwei Möglichkeiten: Sie können es in ein Kind zeichnen oder QGraphicsView :: drawForeground() verwenden.

Gruppe ähnliche QPainter Zeichenoperationen

Bevorzugen drawLines über drawLine mehrfach gefordert wird; favor drawPoints über DrawPoint. Verwenden Sie QVarLengthArray (verwendet Stack, also kann schneller sein) oder QVector (verwendet Heap) als Container. Vermeiden Sie es, den Pinsel sehr oft zu wechseln (ich vermute, dass dies bei der Verwendung von OpenGL wichtiger ist). Auch QPoint kann schneller sein und ist kleiner als QPointF.

Bevorzugen Zeichnung Kosmetik-Linien verwenden, Vermeiden von Transparenz und Anti-Aliasing

Anti-Aliasing deaktiviert werden kann, vor allem, wenn alles, was Sie zeichnen sind horizontale, vertikale oder 45-Grad-Linien (sie eigentlich besser aussehen auf diese Weise) oder Sie verwenden eine "Retina" -Anzeige.

Suche nach Hotspots

Bottlenecks in überraschenden Orten auftreten können. Verwenden Sie einen Profiler oder andere Methoden wie den verstrichenen Timer, qDebug oder FPS-Zähler (ich habe es in meinem QGraphicsView :: drawForeground), um sie zu finden. Machen Sie Ihren Code nicht hässlich, wenn Sie versuchen, Dinge zu optimieren, bei denen Sie nicht sicher sind, ob es Hotspots sind oder nicht. Beispiel eines FPS Zähler (versuchen Sie es zu halten über 25):

MyGraphicsView:: MyGraphicsView(){ 
    ... 
    timer = new QTimer(this); 
    connect(timer, SIGNAL(timeout()), this, SLOT(oneSecTimeout())); 
    timer->setInterval(1000); 
    timer->start(); 
} 

void MyGraphicsView::oneSecTimeout() 
{ 
    frameRate=(frameRate+numFrames)/2; 
    qInfo() << frameRate; 
    numFrames=0; 
} 

http://doc.qt.io/qt-4.8/qelapsedtimer.html

Vermeiden Sie Tief Kopie

Verwenden foreach (konst Auto & Artikel, Artikel), const_iterator oder Artikel .at (i) anstelle von Elementen [i], wenn über QT-Container iteriert wird, um eine Ablösung zu vermeiden. Verwenden Sie die Methoden const operator und call const so oft wie möglich. Versuchen Sie immer, Ihre Vektoren/Arrays mit einer guten Schätzung ihrer tatsächlichen Größe zu initialisieren (reserve()). https://www.slideshare.net/qtbynokia/optimizing-performance-in-qtbased-applications/37-Implicit_data_sharing_in_Qt

Szene Indexing

Favor Kein Index für Szenen mit wenigen Einzelteilen und/oder dynamischen Szenen (mit Animationen) und BspTreeIndex für Szenen mit vielen (meist statisch) Gegenständen. BspTreeIndex ermöglicht eine schnelle Suche bei Verwendung der QGraphicsScene :: itemAt() -Methode.

Verschiedene Farben Algorithmen für verschiedene Zoomstufen

Wie in der Qt 40000 Chips Beispiel, Sie brauchen nicht die gleiche detaillierte Zeichnung Algorithmus zu verwenden, um Dinge zu zeichnen, die auf dem Bildschirm sehr klein aussehen werden. Sie können zwei verschiedene QPainterPath-Objekte im Cache für diese Aufgabe verwenden oder, wie in meinem Fall, zwei verschiedene Punktwolkenvektoren (einen mit einer vereinfachten Teilmenge des ursprünglichen Vektors und einen anderen mit dem Komplement). Je nach Zoomstufe zeichne ich also eins oder beide. Eine andere Möglichkeit besteht darin, die Punktwolke zu mischen und nur die ersten n Elemente des Vektors entsprechend der Zoomstufe zu zeichnen. Diese letzte Technik erhöhte meine Bildrate von 5 auf 15 fps (in einer Szene, in der ich ursprünglich 1 Million Punkte hatte). Verwenden Sie in Ihrem QGraphicsItem :: Maler() so etwas wie:

const qreal lod = option->levelOfDetailFromTransform(painter->worldTransform()); 
const int n = qMin(pointCloud.size(), pointCloud.size() * lod/0.08); 
painter->drawPoints(pointCloud.constData(), n); 

Übergröße Ihre QGraphicsScene :: sceneRect()

Wenn Sie ständig in der Größe der Szene Rechteck erhöhen, reindexing Ihre Anwendung einfrieren kann eine kurze Zeit. Um zu vermeiden, dass können Sie eine feste Größe eingestellt oder fügen und ein temporäres Rechteck entfernen Sie die Szene zu zwingen, zu einer größeren Anfangsgröße zu erhöhen:

auto marginRect = addRect(sceneRect().adjusted(-25000, -25000, 25000, 25000)); 
sceneRect(); // hack to force update of scene bounding box 
delete marginRect; 

Deaktivieren Sie die Scroolbars

Wenn die Ansicht flackert wenn Sie die Szene scrool, können die scroolbars deaktivieren es beheben:

setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 

Nehmen der Maus gesteuert Transformationen, um mehrere Elemente verwenden Gruppierung

Die Gruppierung mit QGraphicsScene :: createItemGroup() verhindert das mehrfache Aufrufen von QGraphicsItem :: itemChange während der Umwandlung. Es wird nur zweimal aufgerufen, wenn die Gruppe erstellt und zerstört wird.

Vergleichen Sie mehr Qt-Versionen

Ich habe nicht genug Zeit hat, um es noch zu untersuchen, aber in meinem aktuellen Projekt zumindest, Qt 5.6.2 (Mac OS) war viel schneller als Qt 5.8.

Verwandte Themen