Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VST3 in Ardour on Linux - resizing fix #867

Closed

Conversation

ctsexton
Copy link

@ctsexton ctsexton commented Mar 4, 2021

Hi JUCE team, I believe this PR fixes resize loop issues I have been experiencing trying to build VST3 plugins on Linux to run in Ardour.

Initially I took a look at the Ardour VST3 host code to see if there was an issue there. That lead to this PR, but upon further digging I found that JUCE's call to updateBounds on the ComponentPeer seems to be interfering with Ardour's own window resizing. It seems like JUCE is trying to modify the plugin window size directly during the onSize callback, while the VST3 spec indicates that the host should handle window resizing.

Prior to this change, any resizable JUCE plugin causes huge performance issues in Ardour (Linux) when the GUI is opened and immediately enters a resize loop. It's not usable at all. After the change is applied, the plugin UI behaves fine and everything seems stable.

@ctsexton ctsexton changed the base branch from develop to master March 4, 2021 05:57
@x42
Copy link

x42 commented Mar 6, 2021

Adding host specific workarounds is not the never a good approach.

According to https://steinbergmedia.github.io/vst3_doc/base/classSteinberg_1_1IPlugView.html#a3e741e55c2c047a4cc10f102661f5654 the plugin should instead request a resize (IPlugFrame::resizeView ()), the host then resizes the window and acknowledges it by calling onSize afterward. -- JUCE should not call back to the host/OS managed window from the onSize callback.

Why is peer->updateBounds(); needed here?

@ctsexton
Copy link
Author

ctsexton commented Mar 6, 2021

I agree a host specific workaround is far from ideal. But hey there's already a Cubase10-on-mac workaround a couple of lines above, why not add a few more! I did a bit of reading on the ComponentPeer:

The Component class uses a ComponentPeer internally to create and manage a real
operating-system window.

The Component class uses a ComponentPeer internally to create and manage a real

I don't understand why a plugin would need to manage the window with any host or OS.

I'll test some plugins across a few different hosts and OS's with this updateBounds call removed and see if it causes any issues...

Update:

Linux:

Removing updateBounds on my plugin didn't cause any noticeable problems in:

  • Ardour
  • Bitwig
  • Tracktion Waveform
  • Reaper
  • Carla

Mac:

Again, no noticeable problems with the updateBounds call removed when running a simple JUCE plugin in:

  • Ableton Live
  • Ardour
  • Reaper

I can see why the ComponentPeer would be required in the standalone app that needs to manage the window itself, but not sure why it's needed for a plugin.

@x42
Copy link

x42 commented Mar 6, 2021

I'm not opposed to also work around this on the Ardour side of things, but I'd like to understand this first.
Particularly since resizable UIs built with the Steinberg VST SDK using vst4gui don't have this issue.

@reuk
Copy link
Member

reuk commented Mar 11, 2021

I've tried this out in a debugger, and I can reproduce the issue where Ardour repeatedly calls onSize on a resizable VST3 view.

The docs for onSize (a pure virtual method implemented by the plugin's view) say:

Resizes the platform representation of the view to the given rect.

I'd interpret this to mean that the implementation of onSize should adjust the platform representation of the view.

Elsewhere on the same docs page, it says:

Please only resize the platform representation of the view when IPlugView::onSize () is called.

Again, this suggests that any native view resizing should happen inside the onSize call. This being the case, I think JUCE's behaviour is acceptable with respect to resizing the native view.

Looking at what VSTGUI does, it seems like the editor checks whether the requested size is the same as the current size, and exits early in this case. Adding a similar check to JUCE's Linux ComponentPeer, to bypass setting the window's size if it already has the correct size, seems to fix the infinite resize loop in Ardour. We'll update JUCE with this change.

On a related note, I'm not convinced that Ardour is completely correct here. When it gets stuck in the resize loop, I can see it making alternating calls to getSize and onSize on the plugin - but the onSize call always matches the size returned by getSize. Perhaps Ardour should avoid calling onSize if the plugin is already using the correct size. From the VST3 docs (emphasis mine):

Here the calling sequence:

  • plug-in->host: IPlugFrame::resizeView (newSize)
  • host->plug-in (optional): IPlugView::getSize () returns the currentSize (not the newSize!)
  • host->plug-in: if newSize is different from the current size: IPlugView::onSize (newSize)
  • host->plug-in (optional): IPlugView::getSize () returns the newSize

@x42
Copy link

x42 commented Mar 11, 2021 via email

@x42
Copy link

x42 commented Mar 11, 2021

@reuk Why does this only happen on Linux?
Ardour uses the same code on Windows, and endless resizing of the same JUCE plugin does not happen there.

@reuk
Copy link
Member

reuk commented Mar 11, 2021

I'm guessing it's due to this:

auto oldBounds = getBounds();
const bool hasMoved = (oldBounds.getPosition() != bounds.getPosition());
const bool hasResized = (oldBounds.getWidth() != bounds.getWidth()
|| oldBounds.getHeight() != bounds.getHeight());
DWORD flags = SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER;
if (! hasMoved) flags |= SWP_NOMOVE;
if (! hasResized) flags |= SWP_NOSIZE;
setWindowPos (hwnd, newBounds, flags, ! isInDPIChange);

If the size/position hasn't changed, we set SWP_NOSIZE/SWP_NOMOVE respectively, so I suppose move/resize events are not triggered if the plugin's window bounds haven't changed. (Speculation, as I haven't tried actually debugging this on Windows).

@ctsexton
Copy link
Author

ctsexton commented Mar 12, 2021

I should point out that @x42's commit does not prevent all resize loops. I just tested this fix and I was easily able to create a resize loop even with the size-changed check. It prevents the initial resize loop when opening the plugin UI, but try resizing the window a bit and you'll easily trigger a loop. I added print statements to both of the view_size_request and view_size_allocate callbacks and got this output when Ardour got stuck in a resize loop:

View size request: 1250 x 375
View size allocate: 1383 x 474
Calling onSize!
View size request: 1250 x 375
View size allocate: 1383 x 474
Calling onSize!
View size request: 1250 x 375
View size allocate: 1383 x 474
Calling onSize!
...

So, now it seems to be stuck in some kind of alternating resize loop. Or the plugin is not reporting its size as equal to what Ardour thought it had changed it to, so the "has-the-size-changed?" check always results in the answer "the-size-has-changed".

So far the only stable way for me to run a JUCE plugin in Ardour is to remove the peer->updateBounds() call in the JUCE code. This works for me building my own plugins but it's unfortunate that pretty much all third party JUCE-based VST3 plugins I've tried remain unusable in Ardour.

Thank you all for helping to solve this!

@reuk
Copy link
Member

reuk commented Mar 12, 2021

I think this issue should be resolved by this commit: d4e8020

Please could you check whether this patch resolves the issues you were seeing?

@ctsexton
Copy link
Author

@reuk yes this commit does the trick!!! Thank you everyone!

@ctsexton ctsexton closed this Mar 13, 2021
@ctsexton ctsexton deleted the ardour-linux-resize-fix branch March 13, 2021 00:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants