Skip to content

PlotSpeedTest: add param tree control panel#1910

Merged
j9ac9k merged 2 commits intopyqtgraph:masterfrom
pijyoi:paramtree_plotspeed
Jul 31, 2021
Merged

PlotSpeedTest: add param tree control panel#1910
j9ac9k merged 2 commits intopyqtgraph:masterfrom
pijyoi:paramtree_plotspeed

Conversation

@pijyoi
Copy link
Copy Markdown
Contributor

@pijyoi pijyoi commented Jul 18, 2021

inspired by ScatterPlotSpeedTest.

Allows easier testing of the speed benefits of useOpenGL and/or enableExperimental.

On a particular Linux system, useOpenGL=True and enableExperimental=False slowed to a crawl.

@j9ac9k
Copy link
Copy Markdown
Member

j9ac9k commented Jul 18, 2021

on macOS with AntiAlias disabled and penWidth of 1

useOpenGL enableExperimental FPS
False False 280
False True 280
True False 1.4
True True 290

@j9ac9k
Copy link
Copy Markdown
Member

j9ac9k commented Jul 18, 2021

Something isn't being reset, when I first run I'm getting values in excess of 350ish fps, but when I start changing options, and then revert back, I'm getting 280ish fps

Regarding options, would be great if we could add some more checks such as

  • skipFiniteCheck
  • connect='pairs'
  • QPen join style (this one I have a hope that will impact the FPS hit we take from going to a thick line, but I suppose I can test this out manually before being added as a parameter tree).

@j9ac9k
Copy link
Copy Markdown
Member

j9ac9k commented Jul 18, 2021

Actually on the Pen one, hold off on that, hopefully we'll be merging #1844 soon which will have a PenSelectorDialog to the ParameterTree

@pijyoi pijyoi force-pushed the paramtree_plotspeed branch from 9b3f47f to ff2d087 Compare July 19, 2021 03:19
@pijyoi
Copy link
Copy Markdown
Contributor Author

pijyoi commented Jul 19, 2021

I have rebased to incorporate the merge of @dgoeries commit. Probably would need to change the setTitle update rate to be time-based rather than once every 50 updates. This is because the 50 value was based on the old fixed samples size of 5000 and we now allow the user to change the number of samples.

@pijyoi pijyoi force-pushed the paramtree_plotspeed branch from ff2d087 to 292be1d Compare July 19, 2021 05:04
@pijyoi
Copy link
Copy Markdown
Contributor Author

pijyoi commented Jul 20, 2021

Added an option to choose between plotting random and sine tone. Plotting "regular" signals is significantly faster than a random signal (due to less pixels being rendered, I guess). In particular, pen width > 1 is usable.

@NilsNemitz
Copy link
Copy Markdown
Contributor

NilsNemitz commented Jul 20, 2021

That's very interesting. I have stumbled on an explanation before that considered the number of self-overlapping pixels to be particularly problematic. Not sure if that is true or not, but the reasoning was that the overlapping part of the same line needed special treatment in case the line was rendered with partial opacity:
A simple sequence of individual lines (which supposedly plots faster) would stack to give reduced transparency.
For a combined multi-segment line, the user expects all parts to merge into a single structure before translucency is equally applied to all of it.
Why that wouldn't fall back to a simpler renderer for fully opaque lines? Or draw fully opaque into a buffer and then blit that with translucency? I have no idea :)
But it seems somewhat consistent with the massive slowdown of zig-zagging random lines.

@pijyoi pijyoi mentioned this pull request Jul 20, 2021
5 tasks
@pijyoi pijyoi force-pushed the paramtree_plotspeed branch from a803a41 to 421641f Compare July 21, 2021 10:58
@pijyoi
Copy link
Copy Markdown
Contributor Author

pijyoi commented Jul 21, 2021

A simple sequence of individual lines (which supposedly plots faster) would stack to give reduced transparency.
For a combined multi-segment line, the user expects all parts to merge into a single structure before translucency is equally applied to all of it.

I added in an option to let the user try out plotting using drawPolyline (which PythonQwt uses) and drawLines.
The drawLines implementation works only for PySide{2,6}

Now, while drawLines is much slower than drawPath with configuration (random, width=1), its performance with (random, width=2) remains consistent, whereas drawPath would have absolutely choked.

@j9ac9k
Copy link
Copy Markdown
Member

j9ac9k commented Jul 21, 2021

Now, while drawLines is much slower than drawPath with configuration (random, width=1), its performance with (random, width=2) remains consistent, whereas drawPath would have absolutely choked.

...woah

So while we've been thinking the limitation might have to do with the raster renderer, this might be a limitation of the raster render and the use of QPainterPath.

Which reminds me, I still need to submit a feature request drawLines take a pointer to a numpy array on the pyside channels...

@pijyoi pijyoi force-pushed the paramtree_plotspeed branch 2 times, most recently from 13dd2f3 to 8d42494 Compare July 23, 2021 04:56
inspired by ScatterPlotSpeedTest.
@pijyoi pijyoi force-pushed the paramtree_plotspeed branch from 22c6edf to 1a34c88 Compare July 23, 2021 23:41
@j9ac9k
Copy link
Copy Markdown
Member

j9ac9k commented Jul 24, 2021

Nice use of #1844 for the Pen parameter tree :D

When selecting some options, the plot doesn't render at all:

image

Looks like toggling useOpenGL brings it back.

@pijyoi
Copy link
Copy Markdown
Contributor Author

pijyoi commented Jul 24, 2021

Were you using pen width > 1?

The following says that macOS only supports opengl line width 1.
https://community.khronos.org/t/gllinewidth-gives-gl-invalid-value-for-widths-1/69023

You could try running the following to see the supported line widths for your machine.

import pyqtgraph as pg
from pyqtgraph.Qt import QtWidgets
from OpenGL import GL

str_attribs = ['VENDOR', 'RENDERER', 'VERSION']

int_attribs = ['MAX_TEXTURE_SIZE', 'MAX_3D_TEXTURE_SIZE',
    'ALIASED_LINE_WIDTH_RANGE', 'SMOOTH_LINE_WIDTH_RANGE'
]

float_attribs = [
    'SMOOTH_LINE_WIDTH_GRANULARITY',
]


class GLTest(QtWidgets.QOpenGLWidget):
    def initializeGL(self):
        for attrib in str_attribs:
            key = getattr(GL, 'GL_' + attrib)
            val = GL.glGetString(key).decode()
            print(f"{attrib}: {val}")

        for attrib in int_attribs:
            key = getattr(GL, 'GL_' + attrib)
            val = GL.glGetInteger(key)
            print(f"{attrib}: {val}")

        for attrib in float_attribs:
            key = getattr(GL, 'GL_' + attrib)
            val = GL.glGetFloat(key)
            print(f"{attrib}: {val}")


pg.mkQApp()
GLTest().show()

@j9ac9k
Copy link
Copy Markdown
Member

j9ac9k commented Jul 24, 2021

I did indeed have a pen width of 2. Didn't realize that wasn't supported! .... on rerunning, now I can't replicate....wonder if I entered a bogus value and didn't notice.

Thanks for the script; here's the output on my machine:

INFO:OpenGL.acceleratesupport:No OpenGL_accelerate module loaded: No module named 'OpenGL_accelerate'
VENDOR: Intel Inc.
RENDERER: Intel(R) Iris(TM) Plus Graphics OpenGL Engine
VERSION: 2.1 INTEL-16.4.5
MAX_TEXTURE_SIZE: 16384
MAX_3D_TEXTURE_SIZE: 2048
ALIASED_LINE_WIDTH_RANGE: [1 7]
SMOOTH_LINE_WIDTH_RANGE: [0 8]
SMOOTH_LINE_WIDTH_GRANULARITY: 0.125

For the fun of it, I set the pen width to 50, and it worked...

image

...why can't I replicate...

@j9ac9k
Copy link
Copy Markdown
Member

j9ac9k commented Jul 24, 2021

Also some property does not appear to be resetting.

I can start up the test, default settings, get ~450 fps.
Toggle useOpenGL/enableExperimental and get ~300 fps
Un-toggle useOpenGL/enableExperimental and get ~200 fps <- on this config I'm effectively my first config.

@pijyoi
Copy link
Copy Markdown
Contributor Author

pijyoi commented Jul 24, 2021

It seems that something sticks in Qt library the moment you have instantiated an QOpenGLWidget.
I verified that un-toggling useOpenGL does switch the viewport widget back to QWidget.

I noticed something similar in VideoSpeedTest.py that conditionally instantiates a RawImageGLWidget depending on whether PyOpenGL is installed. I recall that I could get higher frame rates by uninstalling PyOpenGL. Also different behavior on Windows and Linux.

@j9ac9k
Copy link
Copy Markdown
Member

j9ac9k commented Jul 24, 2021

It seems that something sticks in Qt library the moment you have instantiated an QOpenGLWidget.
I verified that un-toggling useOpenGL does switch the viewport widget back to QWidget.

I noticed something similar in VideoSpeedTest.py that conditionally instantiates a RawImageGLWidget depending on whether PyOpenGL is installed. I recall that I could get higher frame rates by uninstalling PyOpenGL. Also different behavior on Windows and Linux.

That's sneaky!

In the update function; I added a print statement print(type(pw.viewport())) but it's reporting the correct type. If you know of a way to programmatically detect when it's still using the openGL capabilities that would be handy.

In the Qt docs, there is quite the blurb about cleaning/tear-down, but I'm not sure how relevant that is here.

@pijyoi
Copy link
Copy Markdown
Contributor Author

pijyoi commented Jul 25, 2021

As a comparison, I tried porting PlotSpeedTest.py to use QtCharts:
https://gist.github.com/pijyoi/c8a2eb963a36a5d5bb14cc3b75a8bab0

Observations:

  1. The OpenGL stickiness effect is also present
  2. Doesn't choke with pen-width > 1

@j9ac9k
Copy link
Copy Markdown
Member

j9ac9k commented Jul 25, 2021

Nice work on the QtChart bit; ....wonder how it gets around that issue... It uses QGraphicsView and QGraphicsScene..

Looks like the deafult line thickness is 2 even!

https://github.com/qt/qtcharts/blob/7029c2d66beaacff1a0bc0336906ec52308de703/src/charts/linechart/qlineseries.cpp#L173-L183

Oh, I see why ... they use painter.drawLine when the a non-solid solid line is specified. In the chart example, you can make it choke by specifying a non-solid pen parameter.

https://github.com/qt/qtcharts/blob/b69841fb16dd107454f7eead9e6e7b3db9ed4a2e/src/charts/linechart/linechartitem.cpp#L442-L448

@pijyoi
Copy link
Copy Markdown
Contributor Author

pijyoi commented Jul 26, 2021

So it's consistent with what can be demonstrated with PlotSpeedTest.py

  1. Plotting Method "pyqtgraph" uses drawPath which will choke. This is the alternate drawing method of QtCharts.
  2. Plotting Method "drawLines" draws individual line segments and doesn't choke. This is the default drawing method of QtCharts.

@j9ac9k
Copy link
Copy Markdown
Member

j9ac9k commented Jul 26, 2021

So it's consistent with what can be demonstrated with PlotSpeedTest.py

  1. Plotting Method "pyqtgraph" uses drawPath which will choke. This is the alternate drawing method of QtCharts.
  2. Plotting Method "drawLines" draws individual line segments and doesn't choke. This is the default drawing method of QtCharts.

Yeah, this just shows the need we have for using QPainter.drawLines with a pointer to a numpy array (which I know, even if they add that feature, it won't propagate to versions of Qt we support :( ). I can submit the feature request, but if you'd like to do it, by all means, truthfully you'll probably do a better job explaining the specifics of the issue than I would.

@pijyoi
Copy link
Copy Markdown
Contributor Author

pijyoi commented Jul 26, 2021

I don't think there's a need to ask for a QPainter.drawLines numpy overload just yet. The workaround using wrapInstance is not too slow.

Btw, it's drawLines(QLineF, int count) that would be the preferred overload. The numpy overloads that are currently available are only usable with a self-compiled PySide6.

The usage would be something like:

arr = np.empty((count, 4), dtype=np.float64)   # A QLineF is 4 floats
ptr = wrapInstance(arr.ctypes.data, QLineF)
painter.drawLines(ptr, count)

similar to what is done for drawPixmapFragments

@j9ac9k
Copy link
Copy Markdown
Member

j9ac9k commented Jul 26, 2021

The numpy overloads that are currently available are only usable with a self-compiled PySide6.

I should probably run this test myself; but was there a noticeable improvement in this use case?

@pijyoi
Copy link
Copy Markdown
Contributor Author

pijyoi commented Jul 26, 2021

I am not confident nor keen to build such a big package as Qt.

@j9ac9k
Copy link
Copy Markdown
Member

j9ac9k commented Jul 26, 2021

I am not confident nor keen to build such a big package as Qt.

I misunderstood, I thought this needed a custom PySide build, not a custom Qt build.

@pijyoi
Copy link
Copy Markdown
Contributor Author

pijyoi commented Jul 26, 2021

I misunderstood, I thought this needed a custom PySide build, not a custom Qt build.

I am not sure, I just assumed that Qt (even if unmodified) would need to be built together. Assuming that PySide can be built alone, one would still need the Qt headers, Qt link libraries and dlls. And the C++ compiler would need to be the same.

@j9ac9k
Copy link
Copy Markdown
Member

j9ac9k commented Jul 27, 2021 via email

@pijyoi
Copy link
Copy Markdown
Contributor Author

pijyoi commented Jul 27, 2021

I find that if I remove the vsync-unlock code, then toggling useOpenGL False->True->False results in an fps of 60Hz.

@j9ac9k
Copy link
Copy Markdown
Member

j9ac9k commented Jul 31, 2021

@pijyoi if there isn't a good way to "un-stuck" the openGL toggle, should we just add a little text blurb about it, and perhaps add a CLI argument to run with or without it (like VideoSpeedTest)? ...This is a great PR, I'd like not not hold it up due to a limitation of the framework.

@pijyoi
Copy link
Copy Markdown
Contributor Author

pijyoi commented Jul 31, 2021

I added an option --allow-opengl-toggle. User thus has to explicitly opt-in to use this feature.

Despite the stickiness, I did find it useful to be able to toggle between OpenGL <-> non-OpenGL. It was useful for discovering which combinations were absolutely un-usable.

@j9ac9k
Copy link
Copy Markdown
Member

j9ac9k commented Jul 31, 2021

I added an option --allow-opengl-toggle. User thus has to explicitly opt-in to use this feature.

Despite the stickiness, I did find it useful to be able to toggle between OpenGL <-> non-OpenGL. It was useful for discovering which combinations were absolutely un-usable.

That's perfect, thanks for the PR @pijyoi !

@j9ac9k j9ac9k merged commit 7aa5d0c into pyqtgraph:master Jul 31, 2021
@pijyoi pijyoi deleted the paramtree_plotspeed branch July 31, 2021 07:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants