Comment 21 for bug 981199

Revision history for this message
RJ Skerry-Ryan (rryan) wrote : Re: Waveform 2.0 is jerking

RE: Thomas and Owen -- the context sharing was indeed broken before. It's fixed now in that every QGLWidget reports that it is sharing the context.

RE: all

I've been messing around w/ the waveform a lot and I have some thoughts I wanted to get your opinions on:

1) We should use repaint() instead of update() on the widgets in WaveformWidgetAbstract::render.

update() will schedule the widget to receive a paintevent while repaint() will immediately repaint it. update() is basically provided so that you can call it many times per second and it will only repaint the widget once. We are running a rendering timer so we are already careful to only repaint it at our update rate so I think the extra delays caused by update() aren't necessary.

I'm also considering

2) We should turn VSync on

I've found that turning on vsync/swapInterval helps reduce tearing and corruption a fair amount. I originally turned this off thinking it was a performance issue but it reduces the tearing I see.

3) We should render in a thread (in Mixxx 1.12)

Rendering in a thread helped the responsiveness of the waveform a ton for me. I could scroll the library back and forth as fast as I could and the waveform stayed solid at 40fps. I had to turn off almost all the rendering steps that use QPixmap since that isn't safe for use in a thread. Now that we are switching away from QPixmap to QImage this should make it easier to switch to a thread.

If all the widgets share the same GL context then it's fine to use them in the same thread and as of Qt 4.8 it's safe to use a QPainter on a QGLWidget from a thread:
https://bugreports.qt-project.org/browse/QTBUG-3016

Daniel -- you mentioned you tried this. Did you see similar improvements in immunity to the Qt main thread being busy?

4) Non-smooth movement of the waveform. I call this "lagging" (as opposed to jerking, which I believe is a separate problem)

As you surely see, the waveform does not move smoothly. It moves with intermittent sluggishness.. every few seconds it will slow down or speed up a little bit. The slowdown seems much less sharp than the "jerking" so I think it's a separate issue.

To test what causes this, I created a GLWidget class in my stop_being_a_jerk branch. Look at LateNight 1280x800 to see it in action. It just draws parallel lines and moves them forward. The GLWidget always animates smoothly and you can create 4 of them and they all still animate smoothly at the desired framerate.

An important tool in testing was the ability to separate Mixxx's engine from the waveforms. I unhooked the playposition in WaveformWidgetRenderer and just incremented playposition a fixed amount each time. The result for me is it moves smoothly and with no apparent lag. It still jerked every now and then and still had visual tearing and corruption (which I found was improved with turning on vsync). See the attached playposition.patch.

What I conclude from this is that waveform lagging is caused by numerical issues on our side, not by Qt or OpenGL.

5) Jerking

I can't figure this one out. Every now and then the waveform shifts violently forward or backward as in Daniel's screenshot. I've checked that various different values aren't spiking (like the firstDisplayPosition and lastDisplayedPosition, visual sample rate, etc.). I've also confirmed the playposition is never going backward either from the point of the view of the renderer or the engine.

The important thing to note is that some jerking occurs even with playpos.patch applied so the issue in this case is not solely caused by the engine. It's possible we are seeing a combination of issues.

I have found some potential improvements from looking at ControlObject::sync(). It looks like since this used to be in the engine we tryLock every mutex and dont update COT/COTM for a whole callback cycle if we fail tryLock. According to StatsManager, this happens in practice though infrequently. It could lead to stalls in the waveform since visual_playposition won't be updated for two or more latency periods instead of just one.

Daniel suggested that we could be seeing Qt bugs related to out-of-order signal delivery. If this is the case then it's possible that a ControlObject::sync() is occuring while a render is happening. I tried moving the sync() to the main thread and this seemed to have a slightly positive improvement but I was probably just imagining it.