Windows Per-Monitor DPI Scaling with Juce plugins and Cantabile

Hey All,

First post here.

I’m the developer of VST host Cantabile. I’m looking into some compatibility issues where Cantabile’s Per-Monitor Hi-DPI support seems to clash with Juce support for this, resulting in a messed up display like this:

I looks like it’s just an incorrect OpenGL viewport setting as the mouse coordinate handling seems to be working correctly.

Anyway this stuff is fairly technical and messy so I’m wondering if this is the correct place to post all the gory details. I’m happy to discuss this here, or if anyone who’s familiar with Juce’s OpenGL rendering wants to email me directly I can be reached at brad@cantabilesoftware.com

Any help getting this resolved would be much appreciated.

Brad

1 Like

To help narrow this down, could you post a bit more info about the host? Is it built using JUCE or does the issue happen when hosting plug-ins built with JUCE (or both)? Does it support hosting VST3s or just VST2s? If it does, does the problem occur with VST3s as well or just VST2s?

You mention that you have some custom hiDPI support in the application, what does this do exactly? It certainly could be interfering with JUCE’s implementation as the JUCE plug-in host generally works OK with hosting hiDPI plug-ins but, like you say, it’s a pretty messy topic so could be any number of things.

Hi @ed95

Thanks for the quick reply.

Cantabile has a couple of options to control the DPI mode it runs. The one where these problems are occurring is with Per-Monitor V2 support enabled where:

  • Cantabile’s main window runs DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
  • Plugin windows run in DPI_AWARENESS_CONTEXT_UNAWARE

The idea here is to enable HiDPI support for Cantabile’s windows, but let Windows scale up plugins so they’re not so tiny.

In order for this to work successfully, Cantabile sets the dpi awareness context on every call into the plugin: initialization, any dispatch calls, VST 3 interface methods, when creating the editor window etc… It even hooks SetTimer callbacks and inserts a stub to setup the context for those callbacks too.

What it doesn’t do however is setup the DPI awareness for any threads created by the plugin that deal with the UI - which is what seems to be happening with Juce plugins (well at least the one I’ve looked into - Helm) where it’s using OpenGL on a worker thread to do the rendering.

As an experiment, I just hacked in the following into the top of Juce’s OpenGLContext::runJob() and it fixes the issue:

auto oldCtx = SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_UNAWARE);
auto oldAwareness = GetAwarenessFromDpiAwarenessContext(oldCtx);

Obviously the above is just a hack and the correct fix would be to set it to the DPI awareness of the thread used to create plugins’ UI (eg: from effOpenEdit handler). The oldAwareness is just there for curiosity - it’s returning DPI_AWARENESS_SYSTEM_AWARE which must be the default for a thread, unless Juce is setting it somewhere.

The other possibility here would be for Cantabile to hook thread creation and explicitly override the thread’s DPI context. That sounds sketchy but I’ll have a play with that today to see if I can make it work. Either way it’d be nice if this was fixed in Juce too.

To answer your other questions:

  • Cantabile is not built using Juce.
  • It happens when hosting plugins built with Juce. I’m not sure if all Juce plugins but at least some. As mentioned Helm and I know Arturia plugins suffer this problem too - I think they’re built with Juce right?
  • Current public releases of Cantabile only support VST 2 however VST 3 support is currently under development and these issues occur with both the VST 2 and VST 3 versions of Helm.

If you want to reproduce this, just install Cantabile and go to Options -> General -> User Interface and enable these two options, insert a Juce plugin and open it’s editor.

Brad

I’ve put in a workaround for this where Cantabile now hooks CreateThread function and installs a small stub that correctly sets the DPI context. Seems to be working quite well (still testing) but as mentioned it’d be good to have this fixed in Juce too.

Let me know if you need any more info - happy to help in any way I can.

Brad

Thanks for the detailed information Brad. Like you said, it sounds to me like the issue is with the DPI-awareness of the OpenGL renderer threads. I’ll be able to look into this further next week, but for the time being can you try opening the same plug-in in JUCE’s AudioPluginHost application and see if it renders correctly there? That should help to narrow down the problem a bit.

No problem

It definitely is related to that. Hacking the Juce OpenGLContext class as described above fixed it as did my patch to Cantabile that hooks thread creation and sets up the DPI context. I’ve testing my fix to Cantabile with Helm and a bunch of Arturia plugins and they all render perfectly now.

I’m sure they will. This is only an issue with a host that’s trying to upscale plugin windows by isolating them to an “unaware” context.

Brad

That’s actually what we do in the plug-in host, there’s a ScopedDPIAwarenessDisabler class which is used when opening the plug-in windows - see here. I was just wondering whether a JUCE-based host would behave differently to Cantabile which doesn’t use the JUCE hosting classes.

Hi @ed95

Sorry for the slow reply. Finally got around to trying this. The same problem happens in AudioPluginHost with Helm and with a couple of Arturia plugins I tried. See screenshot.

Brad

I’ve pushed a fix for setting the DPI-awareness of the OpenGL worker threads here which seems to have sorted out rendering in JUCE plug-ins using OpenGL in both Cantabile and the JUCE plug-in host.

Excellent. Thanks @ed95