Bug: HiDPI Hell with Linux VST2.4 Plugins

The Problem
I’ve noticed that when testing my plugin on HiDPI screens on Linux that the editor window seems to be resized to 1 pixel by 1 pixel. In order to trigger this behaviour:

  • PopOS running Gnome with a display scaling factor of 200%
  • Only the VST2.4 plugin seems to exhibit this behaviour (the standalone app works perfectly)
  • The plugin does this in the JUCE Audio Plugin Host and Bitwig 2.3.5 (To further confirm this issue is on the plugin-side the Glitch2 VST2.4 was tested and exhibited normal DPI-ignorant behaviour: the interface was the same size in pixels as a low-DPI display)
  • JUCE 5.3.2 was used for both the plugin and the Audio Plugin Host
  • The ‘Hello World’ Audio Plugin project generated in the Projucer exhibits this behaviour
  • The plugin window sometimes briefly appears as the proper size for a tiny fraction of a second before resizing itself

tldr; there’s a JUCE plugin-side bug that causes the editor window to resize to nothing on HiDPI displays on Linux

Example
The following code:

HiDpiExampleAudioProcessorEditor::HiDpiExampleAudioProcessorEditor (HiDpiExampleAudioProcessor& p)
: AudioProcessorEditor (&p), processor §
{
setSize (400, 150);
}
void HiDpiExampleAudioProcessorEditor::paint (Graphics& g)
{
g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId));
g.setColour (Colours::white);
g.setFont (15.0f);
g.drawFittedText ("Hello World!", getLocalBounds(), Justification::centred, 1);
}
void HiDpiExampleAudioProcessorEditor::resized()
{
}

Produces a standalone app that looks like this:


And a VST2.4 plugin that looks like this (the grey bit at the top is the ‘X’ close button):
plugin01_vst2

A dirty semi-fix

A few changes to the code:

#define WIDTH 400
#define HEIGHT 150
HiDpiExampleAudioProcessorEditor::HiDpiExampleAudioProcessorEditor (HiDpiExampleAudioProcessor& p)
: AudioProcessorEditor (&p), processor §
{
setSize (WIDTH, HEIGHT);
}
void HiDpiExampleAudioProcessorEditor::paint (Graphics& g)
{
g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId));
g.setColour (Colours::white);
g.setFont (15.0f);
g.drawFittedText ("Hello World!", getLocalBounds(), Justification::centred, 1);
}
void HiDpiExampleAudioProcessorEditor::resized()
{
setSize(WIDTH, HEIGHT);
}

Gives this in the JUCE Audio Plugin Host and Bitwig respectively:


plugin02_vst2Bitwig

I’m not sure how much insight that might shed but thought it suitable to post here anyway.

Debugging juce_VST_wrapper.cpp
Adding some good ol’ fashioned print statements to EditorCompWrapper::getSizeToContainChild() made for some interesting console output. When I add the following print statement:

juce::Rectangle getSizeToContainChild()
{
if (auto* ed = getEditorComp())
{
std::cout << "getSizeToContainChild returning width of " << getLocalArea (ed, ed->getLocalBounds()).getWidth() << “\n”; return getLocalArea (ed, ed->getLocalBounds());
}
return {};
}

Then this is printed when when a window is opened:

getSizeToContainChild returning width of 400
getSizeToContainChild returning width of 201
getSizeToContainChild returning width of 201
getSizeToContainChild returning width of 101
getSizeToContainChild returning width of 101
getSizeToContainChild returning width of 51
getSizeToContainChild returning width of 51
getSizeToContainChild returning width of 26
getSizeToContainChild returning width of 26
getSizeToContainChild returning width of 14
getSizeToContainChild returning width of 14
getSizeToContainChild returning width of 8
getSizeToContainChild returning width of 8
getSizeToContainChild returning width of 5
getSizeToContainChild returning width of 5
getSizeToContainChild returning width of 3
getSizeToContainChild returning width of 3
getSizeToContainChild returning width of 2
getSizeToContainChild returning width of 2

This has lead me to believe that EditorCompWrapper::getSizeToContainChild() is taking part in a series of events where the window is resized to the editor, which triggers the editor being (incorrectly) set to half the size of the window on HiDPI displays, which triggers the window to be resized to the editor, etc. until finally an integer truncation of 3 / 2 == 2 leads them to match and settle.

I’m almost certain the following snippit in EditorCompWrapper::updateWindowSize() has something to do with it:

#if ! JUCE_LINUX // setSize() on linux causes renoise and energyxt to fail.
if (! resizeEditor) // this is needed to prevent an infinite resizing loop due to coordinate rounding
shouldResizeEditor = false;
setSize (pos.getWidth(), pos.getHeight());
shouldResizeEditor = true;
#else
ignoreUnused (resizeEditor);
XResizeWindow (display.display, (Window) getWindowHandle(), pos.getWidth(), pos.getHeight());
#endif

Based on the above it looks like someone has fixed this already but only for Windows. I’m going to have another look later but right now I need to catch some sleep. In the meantime I’m leaving this here if anyone else wants to have a crack at it.

Have you tested this with the latest tip of develop? There have been some hiDPI improvements for Windows which have resulted in some changes in the Linux code too.

2 Likes

Thank you! You’re spot on, it appears there’s been a huge number of scale-factor-related changes in the development branch and compiling against it fixed everything.

1 Like