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.
|
Warning Retracted (2026-08-29). The central claim below — that 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 |
Correcting myself: the WebView2 argument above does not holdMy comment of 10 August claimed that Setup. A JUCE 8.0.15 app with a
The marker block is 220 px of a 700 px window, so ~31 % is a fully presented page — text, JS Why it was never going to depend on the peer. The window list, same app, two runs: JUCE hosts WebView2 windowed, via What that means for the earlier evidence. Those observations were made by hiding What is unaffected. The measurements under "Testing" in the PR description vary only What the test turned up instead. Under Wine, |
Description rewritten — the case for this PR is consistency, not visual lossI have measured the remaining open question from my previous comment: whether JUCE's own
So on today's Wine the fallback is, if anything, the more correct renderer for JUCE's own What the same probe did show is the half state reachable through the public API — Direct2D The earlier third-party observations on Evoke were made with a process-wide switch and remain |
What this does
5179690ff7("Restore Wine functionality"; the commit message says nothing more) made JUCE fallback to the software renderer whenever it detects Wine. The reason is plain enough on stock Wine
11.0:
CreateSwapChainForCompositionandDCompositionCreateDevice, which the Direct2D peerpresents through, are
FIXMEstubsreturning
E_NOTIMPLthere (dlls/dxgi/factory.c,dlls/dcomp/device.c), so the Direct2D pathcannot put anything on screen. That is the right default and this PR does not
change it.
It adds 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 two Wine checks that select the renderer (
Component::createNewPeerandNativeImageType::create) now go through a singlejuce_shouldAvoidDirect2DUnderWine().The same commit also removed the Wine-specific default font names in
juce_DirectWriteTypeface_windows.cpp; that part is left alone.Why — the two checks have to move together
The peer's public API already switches the renderer back: under Wine
getAvailableRenderingEngines()lists Direct2D andsetCurrentRenderingEngine (1)constructsthe
D2DRenderContext. What it cannot do is switchNativeImageType::create, which has no peerand no override. That leaves a Direct2D context whose images are all
SoftwarePixelData, andthe context does not handle that:
PagesAndArea::makeconverts throughNativeImageType, getssoftware pixel data again, finds no bitmap pages and gives up. Measured on 8.0.15 with a
fifteen-panel probe: every
drawImage, everysetBufferedToImagechild,setTiledImageFill,DropShadow::drawForPath,DropShadowEffectandGlowEffectcome out empty or as a solidrectangle in that state (
DropShadow::drawForRectangleis unaffected — it draws gradients, notan image). Details, with the code path, are in #1721.
So the choice is not "which renderer" but "one switch for both halves or none". This PR is the
smallest form of that switch. The environment variable exists because the people who need it —
Wine users and distributions — have the binaries but not the build.
What it is not
This is not a claim that the software fallback loses anything visible. I measured that
directly (same probe: fallback versus both checks bypassed, Wine detection untouched): across
knob bodies, value rings, gradients, PNG bitmaps with alpha, drop shadows, transparency layers,
path clipping, tiled fills, buffered children and effect filters, nothing is missing under the
fallback. The differences are anti-aliasing, text hinting, and — in the other direction — two
rendering gaps in Wine's own d2d1 (a
GaussianBlureffect that is created but never drawn, andcubic Béziers approximated by a single quadratic). Those are Wine's to fix, and they are why the
switch is opt-in and unsupported: whoever enables it owns the result.
An earlier revision of this description argued that
WebBrowserComponentloses its page contentunder the fallback. That was wrong and is retracted in the comments below; WebView2 presents into
its own composition swapchain and does not depend on the peer's renderer.
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— counting d2d1 entrypoints via
WINEDEBUG=+d2dover 16-second runs:JUCE_ALLOW_DIRECT2D_UNDER_WINEd2d_device_context_*10A and C are factory/device creation and teardown only — the existing fallback, and their
screenshots match within capture noise. B renders the full panel set with
Image (Image::ARGB, …)backed by
Direct2DPixelData; its only deviations from the software renderer are the twoWine-side d2d1 gaps named above.
Context
This comes out of the discussion in
Juce8 Direct2D WINE/yabridge.
The stated hope there (t0m, 9 February 2026) was "I hope we can reduce the number of JUCE forks
being used by people" — for the subset of Wine builds that implement the surface, the
unconditional fallback currently works against that: a plug-in developer can only reach a
consistent Direct2D state by patching JUCE, and a user of an already-compiled plug-in only by
hiding Wine from the whole process (a Wine-side patch that hides
wine_get_version), which flipsevery Wine-aware component at once (that is
exactly what produced the mistaken WebView2 claim retracted below).
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.