Windows: Allow opting back into Direct2D under Wine - #1701
Conversation
JUCE falls back to the software renderer whenever it detects Wine, because stock Wine does not implement the Direct2D 1.3 and DirectComposition surface the Direct2D renderer needs. Some Wine builds do implement it, and there the fallback is not wanted; today those users have to patch JUCE to get the Direct2D path back. Route both Wine checks that select the renderer through a single juce_shouldAvoidDirect2DUnderWine(), which can be turned off either at build time via JUCE_ALLOW_DIRECT2D_UNDER_WINE or at run time via an environment variable of the same name, so a Wine distribution can enable it for binaries it did not build itself. Both are off by default, so the fallback and the unsupported status of Wine are unchanged. The font handling is deliberately left alone.
|
A concrete case has since turned up that makes this less abstract than the original WebView2-based plugins lose their content entirely under the fallback
The plugin we hit this with is Minimal Audio's "EPROM - Memory Rites" (JUCE 8.0.13, licensed For scale, I'll add that this cost us several weeks. We instrumented Why this argues for the opt-in rather than against the fallbackNone of this is a case against the fallback. On stock Wine it is a clear improvement — the UI That is exactly what this PR adds, and it changes nothing for anyone who doesn't ask for it: the Happy to adjust anything about the shape of it: drop the environment variable if a build-time |
What this does
5179690ff7made JUCE fall back to the software renderer whenever it detects Wine, becausestock Wine does not implement the Direct2D 1.3 / DirectComposition surface the Direct2D
renderer needs. That is the right default and this PR does not change it.
It does add a way to turn the fallback off, for Wine builds that do implement that surface:
JUCE_ALLOW_DIRECT2D_UNDER_WINE=1at build time, or0at run time.Both are off by default, so behaviour is unchanged unless someone asks for it. Wine stays
unsupported either way — the point is only that people who have solved the underlying problem
no longer have to patch JUCE to benefit from it.
The two Wine checks that select the renderer (
Component::createNewPeerandNativeImageType::create) now go through a singlejuce_shouldAvoidDirect2DUnderWine().The font handling introduced in the same commit is deliberately left alone.
Why the run-time switch as well as the build-time one
The build-time macro only helps plugin developers. The people who most need this are Wine
users and distributions, who have the binaries but not the build. The environment variable
lets them enable it for plug-ins they did not compile — which is exactly the group currently
forced to maintain patched JUCE copies.
Testing
Built JUCE 8.0.15 with this patch (MinGW cross-compile) and ran the resulting GUI app under a
Wine build that implements the Direct2D and DirectComposition path — giang17/wine, branch
d2d1-dcomp-11.0, which adds theDirect2D effect surface,
dcomp/DCompositionCreateDeviceandCreateSwapChainForComposition— counting d2d1 entry points viaWINEDEBUG=+d2d:JUCE_ALLOW_DIRECT2D_UNDER_WINE10The 23 calls in A and C are just factory/device creation and teardown — no
d2d_device_context_*at all, i.e. nothing is drawn through Direct2D, which is the existingfallback behaviour. In B there are ~299k device-context operations and the app renders
correctly (all widgets, animation running, no black window).
For reference, the same measurement on an unpatched build: JUCE 8.0.12 produces ~2.7M d2d1
calls on this Wine build, 8.0.13+ produces 23. So the capability is present and the fallback
is what stands between it and the renderer.
Context
This comes out of the discussion in
Juce8 Direct2D WINE/yabridge.
The stated hope there was to "reduce the number of JUCE forks being used by people" — for the
subset of Wine builds that implement Direct2D 1.3 and
CreateSwapChainForComposition, theunconditional fallback currently has the opposite effect, since the only way to get the
renderer back is to patch JUCE.
Such builds exist and are in use: giang17/wine
d2d1-dcomp-11.0is the one measured above, and it has downstream adopters (distributions and plug-in vendors
shipping or recommending it). Before 8.0.13 those users ran JUCE 8 plug-ins on the Direct2D
path without patching anything; since 8.0.13 they cannot, which is what this PR is about.
One concrete cost:
WebBrowserComponentloses its contentWebBrowserComponentembeds WebView2 into the peer's window. On the Direct2D path that windowhas a composition swapchain and Chromium presents into it; under the GDI fallback there is no
swapchain, so the page renders and the frame has nowhere to go.
This is not specific to our setup — it was reported independently in the yabridge thread three
months ago, about Synthesizer V 2 on a stock-Wine setup
(#386, 2026-05-18):
There the loss was incidental. Where the editor is a web view it is total: a JUCE 8.0.13
plug-in of that kind (Minimal Audio "EPROM - Memory Rites", licensed copy) shows only its
placeholder background under the fallback — no artwork, no icons, no content. With the renderer
forced back to Direct2D on the same binary it renders completely, matching a Windows reference
capture element for element, and three symptoms we had tracked separately (content appearing
only after a resize, flicker, unresponsive clicks) disappeared along with it.
Worth noting for the diagnosis cost: the switch is invisible from the outside. No error, no log
line, and a commit title that reads as an improvement. We spent weeks instrumenting the
composition path — probes in
SetContent, thirteen causal experiments, all correctly measured— before realising the plug-in had stopped entering that path in the first place.
Independently reproduced
A third party has since confirmed the whole picture on their own rig (yabridge 5.1.1 + Carla,
offscreen X, four commercial plug-ins checked against their compiled
JUCE_VERSION) —giang17/wine#11 (comment):
strokes; with the renderer switched back, the complete UI — knob bodies, value rings,
spectrum panel, keyboard strip, icons, every readout. Before/after captures in the comment.
A phantom hover tooltip that had looked like a separate window bug went with it.
start screen presenting at rest with the renderer back.
Their measurement is also a cleaner probe than raw call volume: app-side rasterization can
drive millions of d2d1 calls regardless of the peer's renderer, but
d2d_device_create_device_contextseparates the two states sharply — 4 creations in 45 swith the fallback active versus 2,512 with the Direct2D peer restored.
Happy to adjust the naming, drop the environment variable if a build-time switch alone is
preferred, or gate it behind a deliberately unappealing name if that helps signal that it is
unsupported.