but because it think this is an entirely new issue, i created this new thread:
The fix which was introduced to throttling the redraw in Mac OS is causing new issues, for other plugins which are running in the same host at the same time.
My assumption is the triggerAsyncUpdate inside the handleAsyncUpdate can result in a massive message ping pong (this does not happen always, but sometimes), which can block the whole message queue for other plugins in the host
(why - the answer does only know apple)
Alternative Solution:
So why not use a timer which waits until the minimum is reached, instead of massively posting messages trigger/handle/trigger/handle into the queue
(It also looks repaint is smoother with this technique)
And here is the code
void handleAsyncUpdate() override
{
#if JucePlugin_Build_AAX || JucePlugin_Build_RTAS || JucePlugin_Build_AUv3 || JucePlugin_Build_AU || JucePlugin_Build_VST3 || JucePlugin_Build_VST
const bool shouldThrottle = true;
#else
const bool shouldThrottle = areAnyWindowsInLiveResize();
#endif
// When windows are being resized, artificially throttling high-frequency repaints helps
// to stop the event queue getting clogged, and keeps everything working smoothly.
// For some reason Logic also needs this throttling to recored parameter events correctly.
// already a timer running -> stop
if (isTimerRunning()) return;
int64 msSinceLastRepaint= Time::getCurrentTime().toMilliseconds() - lastRepaintTime.toMilliseconds();
static int minimumRepaintInterval= 1000 / 30; // 30fps
if (shouldThrottle
&& msSinceLastRepaint < minimumRepaintInterval );
{
startTimer(minimumRepaintInterval-msSinceLastRepaint);
return;
}
setNeedsDisplayRectangles();
}
void timerCallback() override
{
setNeedsDisplayRectangles();
stopTimer();
};
void setNeedsDisplayRectangles()
{
for (const Rectangle<float>* i = deferredRepaints.begin(), *e = deferredRepaints.end(); i != e; ++i)
[view setNeedsDisplayInRect: makeNSRect (*i)];
lastRepaintTime = Time::getCurrentTime();
deferredRepaints.clear();
};
But i want to make sure what the juce team says about this?
Further explanation why the current behavior is bad:
If a second repaint() call comes in a time interval below 33ms, maybe after 1ms, the rest of the time (32ms), the plugin will constantly post triggerAsyncUpdate() messages into the queue
This causes real world issues (bothers other plugins messaging), its a problem!
Any other plugins which build with the current juce, can harm other plugins messaging, its urgent and needs to be fixed soon.
We’re also currently facing big problems with this behaviour in plugins running on macOS, didn’t discover until recently that it was actually related to the message thread event handling and repaint throttling.
I will also do some tests with the above code snippet. Seems like a reasonable change.
I remember we tried this with a timer before, but there were some race conditions where the last repaint message would sometimes get lost. I’ll check if I can still wrap my head around why we didn’t go for the Timer solution which we originally intended.
Okay thanks for replying to this, the current solution is definitely a big problem. If you have a plugin which redraws faster than the 30fps, other plugins may receive no messages anymore until the redraw is lower than 30fps.
But this happens not all the time, but often enough. It seems that macOs (is this the new spelling) somehow prioritize the message queue differently ( no FIFO behavior )
This must be fixed, also on master, because all vendors who now build plugins with this current behavior will create potential issues on other plugins.
I can’t see any race-condition in my solution where the last repaint message might get lost. If you find some please let me know. Hopefully this will fixed soon (also on master, because of the non-cooperative handling regarding the message thread )
Just for reference - this is why we put in that commit in the first place:
I need to make sure that your fix does not break this again. The problem is, I think this only happened on El Capitan. I can’t reproduce it on Sierra even when i completely remove the async updater thing (and redraw directly).
Yes, i know, it looks like redraws which will be posted often will somehow prioritized so that other messages will be slowed down, but in the same way you reduce redraws, by posting new messages, these are again become prioritized, which again slows down other message handling. So the key to success is, not posting more messages (or posting permanent messages as a pseudo timer), instead use a real timer thread.
PS: I wouldn’t remove repaint throttling at all, because i think 98% of all audio people still use El Captian), and we have to be sure that the initial issue is really removed with Sierra
I just wondering, if its better to measure the time with Time::getMillisecondCounter(), because its the same how the Timer-Class it does, to make it more exact?
hi chkn,
Today I had issues with your first mod (not the last patch). repaint() calls from my component stopped working. I do not know why, since it was working perfectly.
For a moment I thought it was something I changed in my project, then I have returned to an old commit where everything worked perfect, but the problem persisted. It has been solved by returning to the original version of NSViewComponentPeer.
It is very weird, since all these days it was working perfectly.
EDIT: Now I’ve applied your last patch and it works again.
My first version, still uses the AsyncUpdater (which was always triggered by any repaint), while the patch in the develop-branch (and the latest version) directly posts the rectangles when needed (setNeedsDisplayInRect) or uses the timer, maybe thats the difference, why the current version works better.
I recommend to use the latest patch from the develop branch, and if still something going on, to debug.
BTW: The reason why i preferred Time::getMillisecondCounter was, I also measure repaints which are still faster than 30fps, i guess this is something how the Timer works (i double-checked the whole timer code, maybe Time::getMillisecondCounter isn’t just precise enough), but otherwise it effectively reduces the amount of repaints.
I think that’s the same right? I always like to write it the first way: A + B where A is the amount of elements until we wrap around and B is anything left over.