Hi,
I have a plugin with a relatively complex GUI that needs refreshing regularly. It’s only metering the audio but the processing can be quite expensive on some audio blocks, so to avoid the dreaded “processing took too long” issue, I store the audio in a lock-free circular buffer and pass the processing off to a separate thread. After that thread processes a “block” (10ms) of audio, it uses a juce::ListenerList to notify the relevant GUI elements that they need to be updated.
Since this processing doesn’t happen on the message thread I can’t just call repaint from the component’s listener callback without locking the message manager queue and I found that regularly locking through MessageManagerLock is relatively expensive and causes the GUI to stutter.
Instead, I’ve created a wrapper class that posts a message to trigger a paint call (this uses a WeakReference just in case the component is deleted before the message is handled). The lock for posting a message seems much less expensive than using MessageManagerLock. The wrapper also keeps track of whether a repaint is queued and skips posting the message so it doesn’t fill the message queue with excess messages.
The number of components I update per cycle is 6 per instance and with the message caching that results in about 360 messages per second (per instance of the plug-in)
I’m running this on a Windows Machine, so it’s using the windows native message queue to pass around triggers for processing these messages. From a quick scan of the code, it looks like the internal juce message queue is kept separately from the system queue, and when anything is added to that, it posts a custom message to the windows queue which is handled by the host to trigger the processing of the juce queue (so one windows message triggers the processing of the entire juce queue as it currently stands).
In release, the render for the whole plug-in only takes about half a millisecond so there’s plenty of time to update the components regularly… usually…
I’ve run the plug-in through Perfetto and it’s doing all of the right things - the slowest bits of rendering are as fast as you could reasonably expect, and there’s nothing that stands out as an obvious issue. In debug builds though, the render takes about 4ms. Still not an enourmous amount of time, but that means that if I create 4 instances, it’s taking longer than the single-frame DirectDraw render time to render the interface. That seems to produce a problem that I’m having difficultly understanding. One of the things I need to test is how the plug-in functions when there are multiple instances of it at the same time (there is some interaction between instances that is not relevant to this issue, but suffice it to say that I need multiple instances on-screen).
Once the render time exceeds the frame rate, something seems to start blocking the Windows message queue. Rendering continues just fine - the GUI updates as expected, but I can no longer click on things. Once this happens, I’m stuck and can only exit the host using the task manager.
So, my questions are:
- Is this a sensible way of handling the updating of components?
- Is this a bug in the juce handling of messages?
- Is there something I’m doing that is obviously wrong here?
Thanks in advance,
Charles
