Showing PopupMenu causes noticeable frame freeze

JUCE 8, on windows, in a VST3 plugin, if you have any animations running (such as a waveform), spawning a juce::PopupMenu will cause a several-frames-long jump of the animation. This goes for navigating through to sub menus, too. This never used to happen, and it kinda breaks the UX.

Is this a D2D thing, or was something else changed along the way?

Good question. Do you have a straightforward way to reproduce the freeze?

Matt

Nothing immediately shareable as it’s in a private repo.

I’ll try bang something together soon. Changing back to the software renderer removes the issue. What’s interesting is spawning a DocumentWindow doesn’t have the same effect.

I’ll make a super basic example and report back.

This is a quick and dirty test. Insert one of these into an editor, set it’s bounds to use the full size of the editor and you can visibly see what happens. Spawning the menu causes a pause, and each time a sub menu opens you see the same pause. Extrapolate this across all animated components of a gui and it makes using the menu’s feel a little jarring.

	struct TestComponent : public juce::Component
	{
		TestComponent() : vba(this, [this] { step(); })
		{

		}

		void paint(juce::Graphics& g) override
		{
			g.fillAll(juce::Colours::black);
			g.setColour(juce::Colours::white);
			g.fillRect(pos % getWidth(), 0, 10, getHeight());
		}

		void mouseDown(const juce::MouseEvent& e) override
		{
			juce::PopupMenu menu;
			menu.addItem(1, "Item");

			juce::PopupMenu submenu;
			submenu.addItem(2, "Item2");

			juce::PopupMenu submenu2;
			submenu2.addItem(3, "Item3");

			menu.addSubMenu("Submenu", submenu);
			menu.addSubMenu("Submenu2", submenu2);

			menu.showMenuAsync(juce::PopupMenu::Options());
		}

		void step()
		{
			pos++;
			repaint();
		}

		int pos = 0;
		juce::VBlankAttachment vba;
	} testComponent;

Thanks for putting that example together so quickly.

I tried it on my machine (Windows 11, Nvidia 2080Ti) and the popup menus seemed nice and snappy to me. I remember seeing delays with the menus before, but I thought we resolved that by retaining the Direct3D device for the adapter.

What is your OS and GPU? Any chance this is a laptop with dual GPUs?

Matt

So the menu itself is fine, it’s the delay that spawning the menu has on the moving rectangle in the paint routine of the component. It goes from buttery smooth to a noticeable jolt whenever a menu or sub menu is spawned.

Win 10, RTX3080. 144hz here, which helps to see variations in frames.

If the menu is spawned with the withParentComponent() option: here, the issue goes away, too. I’m not across the full implementation chain at play here, but it seems like there is something unique to the window that is spawned for a PopupMenu, which is different to a DocumentWindow, and something here causes a delay in the rendering (or the message thread in general?) which is visible on the gui.

OK, I can see a slight hitch in the animation.

If you use withParentComponent() then the menu is painted onto the same window as the parent component. Otherwise the popup menu creates a separate window - but you say that creating a DocumentWindow doesn’t cause a hitch. Hmm…

Matt

Sorry, it is still present with the DocumentWindow after all. There is one configuration that results in it not happening, which I happened to be using:

If you have the window as a class member, so it’s always alive even when not shown, and setUsingNativeTitleBar(true); then it will open without causing stutter (however as soon as you interact with the title bar, stutter will occur).

If you do not use a native title bar, or if you create the window in the scope that you intend to open it, there will be a stutter. I guess the overhead of creating a new peer may be what causes the stutter?

I think this may be related to focus changing to another peer. Simply clicking and holding on another windows title bar will cause things to completely stop for about a second. This also seems to be a system wide thing, as I’m seeing the host transport also skip with this.

Sorry if this is a big waste of your time, I’ll leave this with you as an annoying behavior that would be great if you can fix, while understanding it ultimately may be out of your control.

Thanks for being so responsive!

Is this for any plugin or just yours? Or literally any other window?

If I understand correctly the plugin message thread is shared with the host, so if for some reason a plugin hogs the message thread it also makes the host start to feel “unresponsive”.

1 Like

@matt did you happen to look into this any more?

My last thought on the matter before putting it to bed is this;

Are the PopupMenus creating a new D2D context? Is it this overhead that would reasonably stall the message thread? If yes, can popup menus default to using the software renderer? If that allows them to spawn without so much overhead, I think that would be a good compromise, given the content in a menu will be very simple 99% of the time.

Hi @Fanduss; I spent some time on it but didn’t find anything conclusive. Let me do some more profiling.

Matt

Hi @Fanduss- are you using Windows 10 or Windows 11?

Matt

Windows 10

OK, I think the delay is related to creating the window for the drop shadow for the menu. So far I’m only seeing the delay on Windows 10.

You could test this on your end by temporarily changing the return value for LookAndFeel::getMenuWindowFlags().

int LookAndFeel_V2::getMenuWindowFlags()
{
    return 0; //ComponentPeer::windowHasDropShadow;
}

Matt

Yes, this has removed the delay on my end. Thanks for isolating that. I’m not too attached to drop shadows at the moment so I can move ahead with this. Hopefully you are able to find a way to retain them in a way that doesn’t have the same impact, though!

Interesting that it’s w10 only.

OK, that’s a good clue. I don’t think the delay is due to the drop shadow per se; I think somehow it’s related to just creating the window.

Matt