GUI Perf issue

DSP, Plugin and Host development discussion.
RELATED
PRODUCTS

Post

Hi.

i'm building a VST with 4 big animKnobs (86x86) and 3 little ones (53x53) and displays for all of them.

i set the idle rate to 50 ms.

The result is a CPU load of 100% on idle (25 % of my Quad Core) and it's not smooth when i move knobs.

so the question is is it normal considering the size of my knobs; do i have to use smaller knobs? What is the reason?

Setting the idle rate to a high duration gives me an idle CPU load of (36-48%) for 1 CPU (9-12% of my Quad Core, comparing to 2% without this plugin).

also i should add that the processing is minimal it's a simple compressor with not a lot of calculations so it's the GUI the problem.

thanks for your help.

Post

ion_one wrote:Hi.

i'm building a VST with 4 big animKnobs (86x86) and 3 little ones (53x53) and displays for all of them.

i set the idle rate to 50 ms.

The result is a CPU load of 100% on idle (25 % of my Quad Core) and it's not smooth when i move knobs.

so the question is is it normal considering the size of my knobs; do i have to use smaller knobs? What is the reason?

Setting the idle rate to a high duration gives me an idle CPU load of (36-48%) for 1 CPU (9-12% of my Quad Core, comparing to 2% without this plugin).

also i should add that the processing is minimal it's a simple compressor with not a lot of calculations so it's the GUI the problem.

thanks for your help.
how do you "move" the knobs? I often use a 128 * 86 * 86 picture as knobs,
cpu load is normal, even on my old Pentium 4.

Post

It's not the size of your knobs. (Knobs that aren't moving don't use ANY CPU.) It's obviously something else. Do you have in infinite loop in there or something?

Also you don't tell us any details about your implementation. Is it VSTGUI you're using? What version and rev? On what platform are you observing this? Windows, Windows x64, OSX or OSX x64?

And are you sure your DSP process isn't using any of that CPU power?

Post

hi to you both and thank you for your answers.

i use VSTGUI and it's v3.5.2 and i'm on Windows 7 x64

also i use Visual C++ the last version.

I tried to eliminate the differentes parts of the CPU overload and i found that it's the VU meters that overload the CPU.

The weird thing is that when i leave only one VU meter the CPU peaks at 12% and when i use both, it goes up to 31 % (of the Quad Core). how come ?

alos i found that removing setDirty() from the overrided setValue() of the VU meter class, i got normal CPU load (but no drawing at all)

here is some code :

first the code of the VU Meter then some other codes involving the VU meters:

CDispVU.cpp:

Code: Select all

#include "CDispVU.h"
#include "again.h"
#include "againeditor.h"
#include "math.h"
#include <time.h>
#include <stdio.h>

#define VSTGUI3 

namespace Steinberg {
namespace Vst {

///////////////////////////////////////////////////////////////////////////////

bool CDispVU::_impair(int nb)
{
	return nb&1;
}
bool CDispVU::_impair(long nb)
{
	return nb&1;
}

///////////////////////////////////////////////////////////////////////////////



CMouseEventResult CDispVU::onMouseDown (CPoint& where, const long& buttons)
{
	beginEdit ();

	
	return onMouseMoved (where, buttons);
}
//------------------------------------------------------------------------
CMouseEventResult CDispVU::onMouseUp (CPoint& where, const long& buttons)
{

	peak = false;
	cdv->peak = false;

	endEdit ();

	return kMouseEventHandled;
}
//------------------------------------------------------------------------
CMouseEventResult CDispVU::onMouseMoved (CPoint& where, const long& buttons)
{

	bounceValue ();
	if (isDirty () && listener)
			listener->valueChanged (this);
		//if (isDirty ())
			invalid ();

	return kMouseEventHandled;
}

CDispVU::~CDispVU()
{
}


void CDispVU::setValue(float value)
{
	vu = value; 
	setDirty(true);
}

float CDispVU::getValue()
{
	return vu;
}
}}
CDispVU.H:

Code: Select all

#include "againeditor.h"
#include "math.h"
#include "time.h"
#include <base\source\fthread.h>

#include "CDispAffichage.h"
#ifndef __CDISPVU_H
#define __CDISPVU_H

namespace Steinberg {
namespace Vst {
#define VSTGUI3   
class CDispVU: public CControl  
{
	public :

	CDispVU(CDispAffichage* cda, CDispVU* cdvi, CRect& size, CPoint pos, CControlListener *listener, int tag, CBitmap *pBackground, CBitmap *bitmap, float step): CControl (size, listener, tag),
		bitmap (bitmap), step(step)
	{
		vu = 0.1;

		cdv = cdvi;
		cdaInputPeak = cda;

		largeur = pBackground->getWidth();
		hauteur = pBackground->getHeight();


		setBackground (pBackground);
		bitmap->remember();
		pBackground->remember();

		peak = false;
		peakTest = true;



	};

	~CDispVU();


	long lastClock;
	bool peak;
	bool peakShort;

    virtual void draw(CDrawContext *pContext)
	{
		CColor backgroundColor = {225, 204, 26, 0};
		CColor redColor = {225, 4, 6, 0};
 
		

		pBackground->drawTransparent(pContext, size, pos);
		//bitmap->draw(pContext, size, pos);
		
		float db;
		if (vu > 0.f)
		{
			db = 20*log10(vu); 
		}
		else db = -90;

		//cdaInputPeak->setValue((db + 90)/90.);

		if (db < -48) db = -48;

		float vuf = ((db + 48)/48.);

		
		if (peak && peakTest/*vuf > 1.f*/)
		{
			//peak = true;
			lastClock = clock();
			peakTest = false;
		}
		
		int nbSteps = (hauteur-7-21)/step ;

		
		int h = (1.f-vuf) * nbSteps;

		if (vuf == 1.f)
		{
			peakShort = true;
			lastClock = clock();
		}

		if (vuf > 0) 
		{
			CRect cr = CRect(this->getVisibleSize().x, this->getVisibleSize().y+ h*step+21, this->getVisibleSize().x+largeur, this->getVisibleSize().y+ (nbSteps) * step+21);
			CPoint cp = CPoint(0, h*step+21);
			bitmap->drawTransparent(pContext, cr, cp);
		}

		if ((peak && (clock() - lastClock) / (double) CLOCKS_PER_SEC < 2.4)
		||  (peakShort && (clock() - lastClock) / (double) CLOCKS_PER_SEC < 0.2))
		{
			CRect cr = CRect(this->getVisibleSize().x, this->getVisibleSize().y , this->getVisibleSize().x+largeur, this->getVisibleSize().y+ 20);
			CPoint cp = CPoint(0, 0);
			bitmap->drawTransparent(pContext, cr, cp);
		}
		else 
		{
			peak = false;
			peakTest = true;
			peakShort = false;
		}

		setDirty (false);
	}
	virtual CMouseEventResult onMouseDown (CPoint&  where, const long& buttons);
	virtual CMouseEventResult onMouseUp (CPoint& where, const long& buttons);
	virtual CMouseEventResult onMouseMoved (CPoint& where, const long& buttons);
	

	void setValue(float value);
	float getValue();

	FThread* ft;
	
	//------------------------------------------------------------------------   
  
   
  CDispVU* cdv;
  CDispAffichage* cdaInputPeak;
   float vu;
    CLASS_METHODS (CDispVU, CControl)   
//------------------------------------------------------------------------   

protected:
	bool _impair(int nb);
	bool _impair(long nb);

	CRect siz;
	CPoint pos;
	CBitmap *bitmap;
	CBitmap *bckg;
	float step;

	long largeur, hauteur;

	bool peakTest;

};

}}

#endif
in againeditor.cpp:

Code: Select all

CMessageResult AGainEditorView::notify (CBaseObject* sender, const char* message)
{
	if (message == CVSTGUITimer::kMsgTimer)
	{
		if (VU_L_Input_Display)
		{
			VU_L_Input_Display->setValue (lastVuMeterInputLValue); 
		}
		if (VU_R_Input_Display)
		{
			VU_R_Input_Display->setValue (lastVuMeterInputRValue); 
		}
		if (VU_L_Output_Display)
		{
			VU_L_Output_Display->setValue (lastVuMeterOutputLValue); 
		}
		if (VU_R_Output_Display)
		{
			VU_R_Output_Display->setValue (lastVuMeterOutputRValue); 
		}
	}

	return VSTGUIEditor::notify (sender, message);
}
also i add this at the end of the process() function inside again.cpp :

Code: Select all

//---3) Write outputs parameter changes-----------
		IParameterChanges* paramChanges = data.outputParameterChanges;
		// a new value of VuMeter will be sent to the host 
		// (the host will send it back in sync to our controller for updating our editor)

		if (paramChanges && fVuPPMInputLOld != fVuPPMInputL)
		{
			int32 index = 0;
			IParamValueQueue* paramQueue = paramChanges->addParameterData (kVuPPMInputLId, index);
			if (paramQueue)
			{
				int32 index2 = 0;
				paramQueue->addPoint (0, fVuPPMInputL, index2); 
			}
		}
		if (paramChanges && fVuPPMInputROld != fVuPPMInputR)
		{
			int32 index = 0;
			IParamValueQueue* paramQueue = paramChanges->addParameterData (kVuPPMInputRId, index);
			if (paramQueue)
			{
				int32 index2 = 0;
				paramQueue->addPoint (0, fVuPPMInputR, index2); 
			}
		}

		fVuPPMInputLOld = fVuPPMInputL;
		fVuPPMInputROld = fVuPPMInputR;

		if (paramChanges && fVuPPMOutputLOld != fVuPPMOutputL)
		{
			int32 index = 0;
			IParamValueQueue* paramQueue = paramChanges->addParameterData (kVuPPMOutputLId, index);
			if (paramQueue)
			{
				int32 index2 = 0;
				paramQueue->addPoint (0, fVuPPMOutputL, index2); 
			}
		}
		if (paramChanges && fVuPPMOutputROld != fVuPPMOutputR)
		{
			int32 index = 0;
			IParamValueQueue* paramQueue = paramChanges->addParameterData (kVuPPMOutputRId, index);
			if (paramQueue)
			{
				int32 index2 = 0;
				paramQueue->addPoint (0, fVuPPMOutputR, index2); 
			}
		}

		fVuPPMOutputLOld = fVuPPMOutputL;
		fVuPPMOutputROld = fVuPPMOutputR;
also i add this during the process() function

Code: Select all

tmp = *ptrIn++;
		tmpEnv = tmp;
		tmpBass = fc[i]->lowpass(tmp);
		tmpEnvBass = tmpBass;
	}

	float abstmp = fabs(tmp);

	if (i == 0)
	{
		if (abstmp > fVuPPMInputL)						{
			fVuPPMInputL = abstmp;
		}
		if (fpeakInputValueL < abstmp) 
		{
			fpeakInputValueL = abstmp;
		}
		if (abstmp > 1.0) 
			fpeakInputL = true;
	}
same for the R channel.

Also when i mute all the functions of the VU class, the CPU is still overloaded (there is nothing left in the class except the constructor and destructor and setValue) only when i mute setDirty() in the setValue() function the CPU load drops...

thanks for any help.

Jeff

Post

If you can, try going to VSTGUI 3.6 Latest/greatest is RC2 http://sourceforge.net/projects/vstgui/ ... TGUI%203.6

It already has a CVuMeter class that does what you want. (3.5 might have had it too, I forget offhand.)

I haven't read over every line of your implementation, but you shouldn't be using a timer. Rather, updates to the meter values should be made in the editor's idle() function.

Post

that's nonsense! : i created a totally empty class from CControl and just modified the setValue() function as that :

Code: Select all

void CDispVU::setValue(float value)
{
	vu = value; 
	setDirty(true);
}
and i got 100% CPU load! i really don't get it

Post

Perhaps, your timer is so fast that updates too often and keeps the cpu busy.
In any case, the code at the bottom of the "process" method, the one that deals with "outputs parameter changes", should already update the meters. So perhaps, you are updating more often than needed.

Post

Hi,

First off, don't call invalid() unless you know EXACTLY why you are calling it.
You +don't+ have a reason to call it here. And why bounceValue() when you're not changing it?

Second, when you get called to draw(), you draw()!! You CANNOT throttle inside draw(). It's FAR too late by that point, and the cost of drawing your bitmap is zero compared to other redraw costs that will be going on.
If you want to throttle, do it inside notify(), when you're testing that it's a timer notification.

Fix those and report back.

Dave.
[ DMGAudio ] | [ DMGAudio Blog ] | dave AT dmgaudio DOT com

Post

thank you both of you for your precious answers
First off, don't call invalid() unless you know EXACTLY why you are calling it.
You +don't+ have a reason to call it here. And why bounceValue() when you're not changing it?
okay i restored the initial state i even muted all to see if it improved the CPU load but nothing
Second, when you get called to draw(), you draw()!! You CANNOT throttle inside draw(). It's FAR too late by that point, and the cost of drawing your bitmap is zero compared to other redraw costs that will be going on.
If you want to throttle, do it inside notify(), when you're testing that it's a timer notification.
i did what you said but i got no improvement. I even muted all the draw function to see if it was coming from there but no luck...
Perhaps, your timer is so fast that updates too often and keeps the cpu busy.
In any case, the code at the bottom of the "process" method, the one that deals with "outputs parameter changes", should already update the meters. So perhaps, you are updating more often than needed.
that's a good guess but how do i know if the timer is too fast ? i set it on 50 ms rate i think it's the default but if i increase the rate, the CPU drops for sure but the drawing isn't good anymore..

Out of depiction, i even changed the whole VU class to match the CVUmeter from Steinberg but i still got that heavy CPU load...

:cry:

Post

ion_one wrote:
Perhaps, your timer is so fast that updates too often and keeps the cpu busy.
In any case, the code at the bottom of the "process" method, the one that deals with "outputs parameter changes", should already update the meters. So perhaps, you are updating more often than needed.
that's a good guess but how do i know if the timer is too fast ? i set it on 50 ms rate i think it's the default but if i increase the rate, the CPU drops for sure but the drawing isn't good anymore..

Out of depiction, i even changed the whole VU class to match the CVUmeter from Steinberg but i still got that heavy CPU load...

:cry:
Does the AGain (VST3) example use a timer?

According to what it's written in the comment, the refresh of the vumeter should be scheduled by setting the "outputs parameter changes" stuff, at the bottom of the process method. What happens if you disable the timer?

Post

here is a full working project for Visual Studio C++ 10 with only the VU meters

i peak at 11% of the CPU (44% of one CPU) so it's still way too much CPU use.

http://khaelis.com/tmp/test.zip

thanks for help

Post

I don't have vs2010.

Anyway, "cvmeter" that you find in "vstgui" doesn't seem to have a "setDirty" in "setValue" (which comes from "ccontrol"). So perhaps, it's just a matter of undestanding how "cvmeter" works.

Post

bitwise wrote:Does the AGain (VST3) example use a timer?

According to what it's written in the comment, the refresh of the vumeter should be scheduled by setting the "outputs parameter changes" stuff, at the bottom of the process method. What happens if you disable the timer?
what exactly do you call a timer in my code, can't find out...if it's the setIdleRate method in the constructor of AgainEditorView, when i raise the delay, i have no more redraw...

for the code itself, i copied the code from AGain without too much change (but still too much apparently)

thanks for your help

Post

Just FYI I assumed we were talking about VST 2.4. I'm not sure if what I said about timers and idle applies in VST 3.x. (When you said 3.5.2 above I thought you were talking about the VSTGUI version. As far as I know, VSTGUI 4 is what comes with the VST 3.whatever SDK.)

Post

ion_one wrote:
bitwise wrote:Does the AGain (VST3) example use a timer?

According to what it's written in the comment, the refresh of the vumeter should be scheduled by setting the "outputs parameter changes" stuff, at the bottom of the process method. What happens if you disable the timer?
what exactly do you call a timer in my code, can't find out...if it's the setIdleRate method in the constructor of AgainEditorView, when i raise the delay, i have no more redraw...

for the code itself, i copied the code from AGain without too much change (but still too much apparently)

thanks for your help
This is the timer

Code: Select all

CMessageResult AGainEditorView::notify (CBaseObject* sender, const char* message)
{
   if (message == CVSTGUITimer::kMsgTimer)
   { 
       ...
but i see, that it also belongs to AGain, so i don't think this is the problem.

As you say, calling "setDirty" in "setValue" slows down the gui and i couldn't find "setDirty" used in such a way in AGain or in the CVMeter contained in VSTGUI. So, I would take a look at cvmeter.cpp. It repaints calling "invalid" in CVuMeter::onIdle.

Post Reply

Return to “DSP and Plugin Development”