Weird behaviour in my daw when debugging plugin

currently trying to debug my own implementation of a gain meter, which generally works. (yay)

however when i’m using the plugin (in debug mode, windows 10) my daw (cubase artist 9.5.5) behaves weirdly. for example i once tried to click on the buttons of the file browser on the right and it just didn’t wanna open the folder. or it just showed stuff in the meter of the mixer even though there was complete silence.

i’m not really sure what the problem could be. the performance meter in cubase shows that there’s no high consumption of ram or cpu.

edit: turned down the fps from 60 to 24 and seems ok now but i’d like to know if you think why Timer does that in the first place. i mean it doesn’t really make it wasteful on the processing, but it still blocks the daw’s functionality. how can this observation be explained?

edit 2: it’s still a bit weird. i guess it has something to do with Timer.

or something you are doing on that timer? a quick test would be to keep the timer at the higher rate, but don’t execute any of your code on the timer callback. Timer is a message based event, so if you are taking too long in the timer callback, then you end up blocking all of the other messages (window events, mouse/key events, etc, etc).

timerCallback() just calls repaint(). in paint i only paint everything black, set the resampling quality to low and then call the method that draws my visualization. and that method looks like this:

void draw(Graphics& g, HDLGainMeterAudio gainMeterAudio, Rectangle<float> bounds){
		if (isReady) {
			if (m_channelCount != gainMeterAudio.getChannelCount()) {
				m_channelCount = gainMeterAudio.getChannelCount();
				m_channelCountInv = 1.f / float(m_channelCount);
			}

			auto gain = 0.f;
			for (auto i : gainMeterAudio.getSamples())
				gain += abs(i);
			gain *= m_channelCountInv;
			gain *= m_imagesMax;

			if (m_gain < gain)
				m_gain = gain;
			else {
				auto dif = m_gain - gain;
				m_gain = m_gain + m_release * (gain - m_gain);
			}

			m_idx = jlimit(0, m_imagesMax, int(m_gain));

			g.drawImage(images[m_idx], bounds);
		}
	}

if i comment it out it won’t glitch out so repaint() and Timer itself don’t create the bug apparently. i’m just wondering why my code makes cubase laggy even though the performance meter claims it’s alright. as you can see i just get the channelCount, sum the samples that were collected by my other class to mono, and then draw the image from the images-vector that fits to the given amplitude. the images were created beforehand to save processing power

Profiling is the best way to determine the cause of performance issues. As well, I suspect the performance meter is measuring performance of the audio thread, not the system as a whole, and your slowing things down in the message thread. I’m not a DSP guy, so I can’t comment on improving the performance of your code, but if I were doing this, I would not do all of this work in paint, but instead either calculate it on the audio thread or spin up a separate thread to do this work, and simply use the result in the paint call.

2 Likes

true though. i better optimize some stuff in that regard before coming back to this thread. but let’s leave it open in case it remains the same. i will optimize it now in a way that lets as less calculations as possible stay in the paint-method. i don’t know how to “make my own thread” but for now i just try to move it all to the audio thread.

i managed to outsource all calculations to the audio thread. now all there is left in the paint method is:

void draw(Graphics& g, Rectangle<float> bounds) {
		if(m_isReady)
			g.drawImage(m_images[m_imageIdx], bounds);
	}

at this point it should become apparent that even though Timer itself doesn’t seem to generally cause problems but somehow block the DAWs functionality if used in 60 fps to repaint from a std::vector. are Image objects really that slow to compute? several people suggested to me to use ImageCache for such stuff instead of Image, but i’d like to read some more opinions with reasons for or against this. i really like Image objects. they have a great set of methods to work with. so it kinda bothers me that i’d have to use something else to go on. but if there’s no way around it i’d do it

ImageCache would be useful if you were reloading the Image, but it offers no benefits for already loaded (or in your case generated) images. The lookup of the image in your vector should be super cheap too… I think it is time that you stopped guessing at what is taking up time, and profile the code to actually see where the issue is.

sry if this is a noobish question, but i only started learning c++ in december :slight_smile: i just read that profilers are kinda programms that run the code and check for bad stuff in it. and people seem to be debating about what’s the best profiler in specific cases a lot in other forums. which profiler would you suggest for this occasion?

Xcode and Visual Studio both come with a profilers for measuring a variety of things, including execution paths, memory usage, etc

So, just to understand, if you comment out the two lines of code in your draw function, there is no slow down?

yes, when i comment them out everything is fine even on a high framerate. it really seems like it just doesn’t like to draw images so fast. btw every image has the bounds 160x160px and resampleQuality is set to low. when you say profiler you mean those graphs on the right of visual studio right? i don’t know how to read them correctly when debugging in cubase since they also seem to show all the stuff cubase itself is consuming. what should i look out for there?

process memory is at 641mb and cpu at something around 10-15% or so

I draw images all the time (maybe not at 60fps though) and don’t have performance issues. Are the source images really big? if they are large images being resampled down to something smaller, it seems like that could be a fair amount of work to do in each paint call. another quick test would be to use a really small source image, and see if that impacts the performance. Another thing you could do, although it feels like a bandaid, is cache m_imageIdx between paints, and if it hasn’t changed, then you don’t need to redraw the image… all of the ideas I have been tossing out are simple things to narrow down where the issue could be, but they don’t replace fully understanding the problem with profiling.

I can’t really walk you through profiling at the moment, but hopefully you can figure out with web resources. :slight_smile:

1 Like

when i make the images only 4 pixel small the weird behaviour persists unfortunately.

i added a function that caches out the index and it has a desirable effect. now whenever the volume of something is pretty much the same in a specific part the performance issues are small, while in parts with a lot of dynamic variance it’s the same as before. when the playback stops there are no issues at all anymore. i think this also makes me very suspicious regarding the image drawing performance

I highly doubt there is an issue with drawing images. This is stuff that is done all the time, by many apps.

oh, I hadn’t thought of this before, but are you setting the component that contains the image to opaque? if it’s not opaque, there is extra overhead in mixing with whatever is behind the component.

where would i have to set something to opaque? the image vector is part of my meter class whose object is part of pluginProcessor.h

edit:
ok i have set the pluginEditor opaque true now. this didn’t change anything unfortunately but i guess it’s still a nice little optimization trick :slight_smile:

i just noticed though that the problem becomes far less prominent when i decrease the size of the window. the visualization is always drawn on the full window btw. i had it on 700x700 pixels but when i turn it down to 100x100 it becomes really smooth on 60fps. idk… i mean for a little meter plugin you mostly wouldn’t want it to be so big anyway, but it’s still a bit sad if this is the best solution here because i’ve drawn some pretty sick looking meters that deserve some size :smiley:

btw i always make videos about my progress as a juce developer and i just made one about the current state of this meter so if you wanna see some examples (code as well as visuals) look at this: https://youtu.be/0wiBEzjAWdc

Yes, that won’t change anything.

Painting a non opaque component will trigger the parent component to paint to get a background.

To stop the OS/framework from drawing the parent component, you set it to opaque, so it can leave that step (but if you don’t fill all pixels of the child component, you will get artefacts like stuck pixels from previous paint calls shining through.
When drawing e.g. paths, I realised the most part to save is the allocation, so keeping the path as member and call ensureAllocated() beforehand, otherwise it will have to grow many times for each added vertex.
Same goes for Images, since you draw on Images, try to reuse them and only clear them.
I managed to get fluent video playback with drawImage, so I doubt the framework is the culprit.

idk… i literally just draw the image from the vector without anything else. the only 2 things that stop it from glitching out are:

  • decreasing the size the image is scaled up to.
  • decreasing the timer’s fps.

both of these things make the draw-function have less to do, which is why i suspect the drawing of the images onto the screen to cause the problem. i bet there is some underlying function that iterates through every pixel of the screen and then trys to get the right pixels from the images given. ofc since i used lowqualityresampling mode there is no interpolation at all going on so it should just take the pixel and go for it, but when the image is 700x700 or bigger it really seems to have a problem with that.

Can you show the painting code?
As a comparison:
My videoengine has no problem to draw 25 times on 1280x768 and scale meanwhile.
1980x1080 runs ok, only with 4k frames it dies after a few frames.

Is your coupling from audio thread to drawing safe? What is m_isReady? Could there be some locking happening? I had a situation at the beginning of my audio programming, that an unsafe interaction from audio thread to paint resulted in visual hangs as well, it even stalled the hosts drawing.

the code that paints is just:
void draw(Graphics& g, Rectangle<float> bounds) { g.drawImage(m_images[m_imageIdx], bounds); }

yeah i could imagine that there might be problems about the way i handle audio vs gui thread. atm the object that handles everything about my meter is part of the pluginProcessor.h and used in processBlock but also being accessed from pluginEditor in the constructor, timerCallback and paint.

m_isReady is just a bool that is only true when the vector holds actual images. should prevent the code from trying to draw something that isn’t there.

here is the complete base class:

class HDLGainMeter {
public:
	HDLGainMeter() : m_gain(0.f), m_release(.2f), m_imageIdx(0),
		m_imageIdxCache(0), m_imagesMax(0), m_imagesMaxInv(0.f), m_isReady(0) {}

	// set amount of images.
	void setImagesCount(int imagesCount) {
		m_imagesMax = imagesCount - 1;
		m_imagesMaxInv = 1.f / float(m_imagesMax);
		m_images.resize(imagesCount);
	}

	// set width of each image.
	void setImageBounds(int imageWidth) {
		for (auto& i : m_images)
			i = Image(Image::ARGB, imageWidth, imageWidth, true);
	}

	// set release of envelope follower. 0-1
	void setRelease(float release) { m_release = release; }

	// implement the method void setImages(), set m_isReady = true and add images to m_image!

	// set buffer from processBlock().
	void setBuffer(AudioBuffer<float>& buffer) {
		auto m_magnitude = buffer.getMagnitude(0, buffer.getNumSamples());

		if (m_gain < m_magnitude)
			m_gain = m_magnitude;
		else
			m_gain = m_gain + m_release * (m_magnitude - m_gain);
		
		m_imageIdx = int(m_gain * m_imagesMax);
	}

	// draw in paint().
	void draw(Graphics& g, Rectangle<float> bounds) { g.drawImage(m_images[m_imageIdx], bounds); }

	// called in timerCallback to check if repaint is needed
	bool shouldRepaint() {
		if (m_imageIdxCache == m_imageIdx)
			return false;
		else if (m_isReady) {
			m_imageIdxCache = m_imageIdx;
			m_imageIdx = m_imageIdx < m_images.size() ? m_imageIdx : m_imagesMax;
			return true;
		}
	}

private:
	float m_gain, m_release;
	int m_imageIdx, m_imageIdxCache;
protected:
	int m_imagesMax;
	float m_imagesMaxInv;
	bool m_isReady;
	std::vector<Image> m_images;
};

Can you show the Component, that is calling your draw?
I am particualrily interested in the timerCallback, the paint(Graphics&) and how the audio thread is setting the level to choose the image