master ← emezeske:dpi-fix-2026-05-31
opened 02:03AM - 01 Jun 26 UTC
# Fix double-scaled plug-in editors on Windows in hosts that send a redundant co…ntent scale
## The problem
On a high-DPI Windows display the plug-in UI has to be drawn bigger (e.g. 1.25x at 125%). There are **two independent ways** a plug-in can be told to do that:
1. **The OS way**: the window is marked per-monitor DPI-aware, and Windows scales it.
2. **The host way**: the host calls `IPlugViewContentScaleSupport::setContentScaleFactor(...)`.
Some hosts do **both at once for the same factor**: the window is DPI-aware *and* the host also sends a content scale equal to the monitor's DPI. When that happens, if the desktop scaling is e.g. 1.25 JUCE applies the 1.25 twice and the editor renders at ~1.56x.
I observed this in Renoise, where the plugin window is too large and scaled up too much.
## Why it works in most hosts but breaks in Renoise
Most hosts pick **one** mechanism: per-monitor-aware hosts (Reaper, Cubase, etc.) rely on the OS and send no content scale (or 1.0). **Renoise** (and **Tracktion Waveform**) are DPI-aware *and* call `setContentScaleFactor` with a factor equal to the monitor DPI, so the same scale is counted twice. It is not a Renoise-specific bug; it is any host that supplies a content scale a DPI-aware window does not need.
## Why old JUCE worked and 8.0.13 broke it
This regressed in **`fcf1971122` ("Plugin Client: Change scaling mechanism on Linux/Windows plugins")**, which changed *how* the host content scale is applied:
- **Before 8.0.13:** the host content scale was applied as a **top-level `AffineTransform`** on the editor, independent of the window's OS/peer DPI scaling. The two lived in separate channels and did **not** stack, so a redundant host scale was harmless.
- **In 8.0.13:** the host content scale is applied as the window's **platform scale** (`ComponentPeer::setCustomPlatformScaleFactor`), and `convertToHostBounds` now multiplies by `desktopScale * platformScale`. The host scale and the window's own DPI scaling now occupy the **same channel**, so a host scale that equals the DPI gets multiplied in on top of it, leading to double-scaling.
## The fix
In `JuceVST3Editor::setContentScaleFactor`, ignore any content scale that is **at or below the window's own DPI scale** (`getScaleFactorForWindow`). The OS already applies that much to a DPI-aware window, so such a factor carries no new information; only a factor that **exceeds** the window's DPI represents extra scaling the host actually wants.
This is correct in every case:
- **DPI-aware window, redundant scale (Renoise / Waveform):** ignored: the OS's single DPI scaling stands.
- **DPI-unaware window:** `getScaleFactorForWindow` returns 1.0, so a real host scale (> 1.0) is never mistaken for redundant and is still applied.
- **Cubase 10 integer-scale workaround** (the existing block right below): its integer factor never equals the fractional window DPI, so that path is untouched.
- **Genuine host zoom beyond the monitor DPI:** a factor greater than the window scale is still honored.
It restores the pre-8.0.13 result (a single DPI scaling) without reverting the new mechanism, and is a no-op for any host that was not already sending a redundant content scale.