Irregular repaint intervals in Logic 9/Audio Unit


#1

We are having some trouble with irregular GUI updates of an AU plugin when it’s loaded in Logic 9. I hope someone can give me some advice.

In our project, we have a continously moving playhead cursor which is backed by a Timer with 20ms interval that calls a repaint() for the cursor region. While this results in a smooth movement in all other hosts we tested, in Logic 9 the animation is stuttering after you start playback in the host (it’s fine as long as the host is stopped).

We made a small demo plugin (attached as RepaintTestPlugin.zip) which shows that the effect is not dependant on what the plugin is painting or doing in the audio thread. In that test plugin, the plugin-editor is a Timer which triggers repainting of the whole editor in regular intervals. It then paints bar graphs of the time between each timer callback (blue) and each call of the paint method (red). The most recent intervals are always plotted at the right edge.

As you can see in the screenshots, both timer and repaint intervals are regular enough when logic 9 is stopped (even up to 60fps). But as soon as the host playback is started, it seems that the timer callback is not called in regular intervals any more. This effect can be seen at all resolutions / frame rates, so it doesn’t seem to depend on the time actually spend in the paint method.

Has anyone run into similar timer-related problems with Logic-9? The odd thing is that everything works fine in other hosts including Logic-8, or if the plugin is loaded into the 32-bit-plugin-bridge of a 64bit-Logic-9.

Any opinions/ideas for a solution or workaround to get more accurate timer callbacks? or do you think I’m wrong with my conclusion that this is caused by irregular timer callbacks? Increasíng the priority of the InternalTimerThread did not help at all, and I have no idea which other parts of the Timer infrastructure might be worth to try tweaking.


#2

The timer runs on the message thread, so obviously it has to contend with all the other messages, including the host’s ones, and it sounds like Logic is running some kind of regular event of its own which is hogging the thread for long intervals. Not sure what to suggest as a workaround! I guess it’d probably be safe for you to run your own thread which just calls repaint() at regular intervals, if that’s what you’re doing.


#3

I just tried that in my test plugin. Now the repaint() method gets called in regular intervals from a dedicated thread (by using Time::waitForMillisecondCounter).

Unfortunately the intervals between the calls to the paint() method still show pretty much the same irregularities (as soon as playback in logic 9 starts). No big surprise assuming that it’s called from the same message thread that is blocked by logic’s own events.

Any other ideas how to get around this and get more regular repaints out of logic 9? I’m pretty close to giving up and just blame it on logic. But from a customer’s point of view, it will sound quite ridicolous if you get told it’s apple’s fault that our plugin’s cursor doesn’t run smoothly on a system that is otherwise capable of a lot more graphical eye candy.


#4

Sorry, of course there’s no point triggering the repaints more regularly if the message thread is too busy to let your paint callback run.

I think they’ve screwed you on that one… I’ve really can’t think of anything you could do about it. You could write your UI in openGL, repainting it via a thread, but that’s pretty drastic.


#5

Since rewriting the UI is not an option, I tried the Thread-based approach in our actual plugin as well. It seems that the irregularities of repaint-calls were just one factor - the Timer that previously triggered the repaint did some other things as well besides cursor repainting. Now we just extracted the repaint-calls into a Thread, and this seems to give the cursor-repaints a “head-start” compared to other things triggered frmo the timer. So the animation runs smoother now - still not perfect once playback starts - but I doubt it’s possible to further improve it.

It still seems to be a bit wasteful to have an extra thread for this, so I have two questions

  • is Time::waitForMilliSecondCounter the optimal way to have thread wake up at regular intervals?
  • I got an assertion when calling repaint() without obtaining a MessageManagerLock. Is there a chance to avoid the overhead of grabbing the lock (and probably locking the event thread while holding it)?

#6

Yes, waitForMillisecondThingy is the best way to make a thread wait.

Not sure what to do about the MM lock thing… The lock is used for a very good reason! You might need to write a platform-specific hack that grabs the NSView directly from the plugin and invalidates it directly, bypassing the whole Component class. (Of course you’d still have the risk that the NSView may be getting deleted by the message thread while you’re trying to invalidate it, so you’d have to be careful about that)


#7

Thanks for the suggestion, but that way sounds to error-prone - at least for me. And we didn’t actually observe any drawbacks of obtaining the lock, I was just hoping that in my case I could somehow get around it.