Waste of CPU time induced by redrawing everything


#1

Hi there,

looking at the demo project “Juce_Vst” I see that changing any plugin parameter using DemoJuceFilter::setParameter(…) will call the function DemoJuceFilter::sendChangeMessage(this) which ultimately leads to a call of DemoEditorComponent::updateParametersFromFilter() - a function that just redraws EVERYTHING in the editor´s window.
I saw that this way of doing it was also used in a Juce-based audio plugin called Peggy2000 - just redrawing all 30 sliders on screen when only one slider would be needed to be repainted after a parameter change. This is the most horrible waste of CPU time I´ve ever seen anywhere. Sorry to say it like this, but it´s true.

Now could anyone propose a good/elegant way for only redrawing (asynchronously) the Component (for instance a slider) that is meant to be redrawn for a given parameter upon call of setParameter?
I suppose, calling mySlider->setValue(newValue,false) is not a good practice because it has not been done like that in the demo (perhaps Slider::setValue redraws the slider synchronously???).

I am thankful for all suggestions!


#2

Complete twaddle.

Where exactly in updateParametersFromFilter() does it “redraw everything”? As far as I can see, all it does is to set the slider position and label text, neither of which will redraw anything unless the value has actually changed.


#3

If setting the value of a Slider or Label that already has this value does not redraw it, then it´s not as bad as I thought.
I know that in your demo code this is no problem, since there are only one slider, one Midi keyboard component and a few Labels involved - however since the above mentioned Peggy2000 plugin was following the scheme from the demo plugin, it was setting values of all (approx. 30) sliders upon each call of setParameter.
I was just asking for a solution how to update only a wanted slider upon call of setParameter; not all sliders. Is it ok to call slider->setValue in setParameter?


#4

It’s not ok to do very much in setParameter, and certainly not ok to call any UI methods - it could be called by the audio thread.

The demo’s fine as far as efficiency goes. The only way you could improve it would be to make setParameter keep a record of which parameters have changed, so you’d only bother updating those ones when you get the async callback. But I doubt if that would help much unless you had thousands of complicated params to go through.


#5

Well, I was asking because I planned to program a plugin involving hundreds of on-screen components, perhaps using Juce. If not Juce, I´ll have to stick to C++ Builder, which is unfortunately not cross-platform.

In VSTGUI (and my own C++ Builder GUI components) it is possible to set the value of a knob or other control without noticeable overhead in the audio thread, in fact only the floating point “value” variable of the knob is changed and a boolean flag “dirty” is set to true, this flag indicates that the given knob needs to be redrawn in the future.
Another thread then checks every X milliseconds if some controls need to be redrawn (due to set “dirty” flag) and redraws them.

So, there´s no way of setting the Slider´s value and notifying it to be redrawn from the Audio Thread, following a similar principle as the above mentioned one?


#6

Well there’s no way to do the same trick directly with a slider. Using a “dirty” flag means that the UI thread has to poll for components that might need updating, and any kind of UI component interaction from an audio thread would feel like dangerous design to me.

Some people have written parameter object classes for their plugins, so that the dirty flags are kept away from the UI, and they get a nice event callback on a parameter-by-parameter basis. That seems neater, and I’ll be adding something along those lines at some point.


#7

That´s nice. :smiley:


#8

Hey. Don’t go by Peggy2k. It’s kind of wonky in many ways. It was sort of the first real program I ever wrote and I had less than a year of C++ under my belt at the time. I was mostly concerend with the pattern arp.

With regards to the update from filter thing I don’t think I gave that any thought at all, but looking at the source, it seems I didn’t put a sendChangeMessage in the setParameter method.

Which either means I should have and forgot and the gui does not respond to automation. Or the framework does it behind the scenes (does it?) in which case this is only a problem in the case of automation, at which point the gui will likely be closed anyway.

I should probably make a new version of peggy, now that I have a slightly better idea of how this stuff works.


#9

Hey, I am not after Peggy, Peggy is nice :slight_smile: I was just using it as example, to sustain what I was saying. By the way I just tried to add sendChangeMessage(this) to the setParameter() function in Peggy2000, but automation still is not possible… Dunno why


#10

Well, a lot has happened to juce since then. Or I may have done something to disable automation. I’m crazy like that. Also, it was built against the 2.3 sdk if I recall, not sure if that’s still fully supported.


#11

the best way i’ve found is like this:

  1. have a ParameterManager living in your plugin, that will be invoked in plugin set/getParameter

  2. register a Parameter class that is a AsyncUpdater, and can have a list of ParameterListener listening to a parameterChanged callback. You should have one for each parameter your plugin will expose to host/ui.

  3. then make your controls (sliders, buttons) be also a ParameterListener, and in your parameterChanged callback, just “setValue (parameter->getValueReal (), false);” WITHOUT trigger a change message (it will trigger a infinite loop !!!)

  4. when you create the ui controls, just attach your ParameterControls as listeners to one or more parameters

  5. when the user change a parameter (eg in sliderChanged), you should call plugin->setParameterNotifyingHost (myParamID, sliderThatWasMoved->getValue()) (or let the ParameterControls do that)

  6. when the parameter manager receive a setParameter call (after host automation, or user interaction), it will update the parameter value and trigger an async update to that parameter that will update all listeners controls.

[7. forget about sendChangeMessage, that could be needed only when u need to change all parameters at once, eg. after a preset change]

the pro of this solution, is that each parameter is updated independently, and responds to both automation and user interaction without impacting too much on cpu; also you can plug more controls to the same parameter, or the opposite (1 control only but 2 parameter changed at the same time).

another thing to consider is to use a method (or a class) to block host automation on a slider when the user is actually moving it with the mouse… but that’s a different story.


#12

I think I will do it like this:

I’ll create an array:

bool dirty[kNumParameters]; // kNumParameters=amount of parameters in plugin

setParameter() will look like this:

void MyPlugin::setParameter(int param, float value)
{
dirty[param]=true;

    // rest of setParameter code here ....

}

in some timer (50* per second) I just check if a dirty flag is set for a component. if yes, i just call repaint() for the component corresponding to this parameter number and ofcourse set the dirty flag of this parameter to false again.

this is very fast and simple as well.

if the host sends multiple parameter changes every 16 samples before block processing if block processing is split up in 16 samples chunks or so, one must really have a very fast mechanism of reacting in setParameter() and telling the GUI to update. every CPU cycle is important then.


#13

[quote]in some timer (50* per second) I just check if a dirty flag is set for a component. if yes, i just call repaint() for the component corresponding to this parameter number and ofcourse set the dirty flag of this parameter to false again.
[/quote]

if you got a thousand (like i’ve got), you will end up doing multiple checking for dirty flags about 20 (framerate) * 1000 (params) = 20000 branches every second. nothing but optimized anyway…
and what if you write to false the parameter dirty flag just a moment after the repaint but still after the host set it to true again in between ? locking ? mmmmmh…

i think the asyncronous messages way is the best imho. multiple subsequent messages will trigger only a single update of the parameter, regardless of which get dirty or not. And you will repaint only the one that get dirty without checking the whole every millisecond.


#14

even if each branch takes 20 cycles, then you’ve got 20000*20=400000 cycles. this would not even consume 1/1000 of the CPU time on a mediocre CPU.
so it IS optimized. just for your information, this is how it is done in VSTGUI, too. i’d say that just drawing a few lines in JUCE takes endlessly more cycles than the cycles mentioned above.

that doesn’t make any sense. locking is impossible with this mechanism. everybody can set the flag to true and then the timer just checks if the control needs to be repainted. that’s all. btw it should be set to false before repainting it, because if is set to false after painting and somehow in the middle of the painting it was set to true again, then this is not taken into account.

[quote]
i think the asyncronous messages way is the best imho. multiple subsequent messages will trigger only a single update of the parameter, regardless of which get dirty or not. And you will repaint only the one that get dirty without checking the whole every millisecond.[/quote]

well i would really like to know how much cycles this asyncronous messaging in the audio thread (because hosts do call setParameter() in the audio thread) takes for sending thousands of parameter changes in one second. damn sure it is much more than the few cycles wasted by my approach. and the cycles wasted in my approach are not wasted in the audio callback, but in some lower priority thread, which is absolutely no problem, because if the CPU is to 100% this lower priority thread will just not be called (the audio thread will have priority).


#15

yeah sure. anyway if it is written in vstgui, it doesn’t means it is better than everyone approach, at least is true the contrary: vstgui sucks all the time :slight_smile:

yeah it will sure eat more than setting a couple of flags, i’m just not addicted to do my parameter (or components) repaint handling in timers of 1/50 of second, but that’s only a poor developer point of view.

:slight_smile:


#16