[Bug 60173] New: winex11.drv: layered window is painted black when a Direct3D/OpenGL swap chain targets it (breaks every WPF menu, tooltip and AllowsTransparency window)
http://bugs.winehq.org/show_bug.cgi?id=60173 Bug ID: 60173 Summary: winex11.drv: layered window is painted black when a Direct3D/OpenGL swap chain targets it (breaks every WPF menu, tooltip and AllowsTransparency window) Product: Wine Version: 11.15 Hardware: x86-64 OS: Linux Status: UNCONFIRMED Severity: normal Priority: P2 Component: winex11.drv Assignee: wine-bugs@list.winehq.org Reporter: wehrwolfmann@gmail.com Target Milestone: --- Distribution: --- Created attachment 81819 --> http://bugs.winehq.org/attachment.cgi?id=81819 Minimal Win32 + D3D9Ex reproducer, with nod3d and plain control modes A top level layered window (WS_EX_LAYERED, contents supplied through UpdateLayeredWindow) is painted opaque black as soon as a Direct3D swap chain targets the same window. Without the swap chain the very same window, the very same bits and the very same UpdateLayeredWindow call paint correctly. This is what makes .NET WPF applications show black menus, black tooltips and black windows under Wine: WPF renders through Direct3D 9 by default, and every WPF popup (menu, combo box drop down, tooltip) as well as every window with AllowsTransparency="True" is a layered window. The widely quoted workaround [HKEY_CURRENT_USER\Software\Microsoft\Avalon.Graphics] "DisableHWAcceleration"=dword:00000001 works because it takes Direct3D out of the picture entirely, not because anything is wrong with WPF's use of the API. Measured on wine-11.15 and wine-staging-11.15, x86-64, winex11.drv, default wined3d GL renderer, KDE Plasma 6.7.4 on Wayland (Xwayland), Mesa "AMD Radeon 780M Graphics (radeonsi, phoenix, ACO, DRM 3.64)". HOW TO REPRODUCE (no .NET needed) --------------------------------- The attached d3d9_layered.c is ~190 lines of plain Win32 and does exactly what WPF's MilCore does for a transparent window: 1. create a WS_EX_LAYERED top level window 2. Direct3DCreate9Ex / CreateDeviceEx, windowed swap chain, hDeviceWindow = that window, back buffer D3DFMT_A8R8G8B8 3. Clear the back buffer to opaque red 0xFFFF0000 4. GetRenderTargetData(back buffer -> D3DPOOL_SYSTEMMEM surface) 5. LockRect that surface and hand the bits to UpdateLayeredWindow Build and run: x86_64-w64-mingw32-gcc -O1 -o d3d9_layered.exe d3d9_layered.c \ -ld3d9 -lgdi32 -luser32 wine d3d9_layered.exe # layered window + D3D9Ex wine d3d9_layered.exe nod3d # same layered window, no Direct3D (control) wine d3d9_layered.exe plain # ordinary window + D3D9Ex (control) The window stays up for 8 seconds so it can be looked at. Expected: a red 300x120 rectangle at 80,80 in all three cases. Actual: d3d9_layered.exe black rectangle <-- the bug d3d9_layered.exe nod3d red rectangle, correct d3d9_layered.exe plain red rectangle, correct Every Direct3D call succeeds and the read back data is correct, so the program also prints: Direct3DCreate9Ex hr=0x00000000 CreateDeviceEx hr=0x00000000 GetBackBuffer hr=0x00000000 SetRenderTarget hr=0x00000000 Clear(0xFFFF0000) hr=0x00000000 CreateOffscreenPlainSurf hr=0x00000000 GetRenderTargetData hr=0x00000000 LockRect hr=0x00000000 read back pixel(0,0) = 0xffff0000 (expected 0xffff0000) pixels wrong in row 0 = 0 of 300 UpdateLayeredWindow ok=1 VERDICT: readback ok That is, d3d9 and wined3d do their job: the pixels handed to UpdateLayeredWindow are the correct red, and UpdateLayeredWindow returns TRUE. The window is black all the same. THE SAME THING ON WPF --------------------- The attached wpf_layered.cs opens four things at once in one process: A ordinary Window -> renders, correct B Window, AllowsTransparency = true -> BLACK C Popup, AllowsTransparency = false -> renders, correct D Popup, AllowsTransparency = true -> BLACK so it is not "popups" and not "WPF" in general -- it is exactly the layered ones. It can be built inside a prefix that has .NET Framework 4.x with the in-box compiler, no Visual Studio and no SDK needed: wine C:\\windows\\Microsoft.NET\\Framework64\\v4.0.30319\\csc.exe \ /target:winexe /platform:x64 /out:C:\\wpf_layered.exe \ /r:<GAC_MSIL>\\PresentationFramework\\...\\PresentationFramework.dll \ /r:<GAC_64>\\PresentationCore\\...\\PresentationCore.dll \ /r:<GAC_MSIL>\\WindowsBase\\...\\WindowsBase.dll \ /r:<GAC_MSIL>\\System.Xaml\\...\\System.Xaml.dll \ C:\\wpf_layered.cs Screenshots "before" and "after" are attached. WHAT IS ACTUALLY HAPPENING -------------------------- The swap chain gives the window an X11 client window, and that client window is an opaque child sitting on top of everything the layered window painted. xwininfo while the repro is up: xwininfo: Window id: 0x5c00006 "layered" Parent window id: 0x350 (the root window) 1 child: 0x5a00036 (has no name): () 300x120+0+0 +80+80 The child covers the window completely. Without the Direct3D device there is no child and the window is red. winex11.drv already has the machinery for this case -- when a window needs compositing, the client window is detached and the rendering is redirected through XComposite (client_surface_update_offscreen() in dlls/winex11.drv/init.c). It is just never asked to do it for a layered window: BOOL needs_offscreen_rendering( HWND hwnd ) { if (NtUserGetDpiForWindow( hwnd ) != NtUserGetWinMonitorDpi( hwnd, MDT_RAW_DPI )) return TRUE; /* needs DPI scaling */ if (NtUserGetAncestor( hwnd, GA_PARENT ) != NtUserGetDesktopWindow()) return TRUE; /* child window, needs compositing */ if (NtUserGetWindowRelative( hwnd, GW_CHILD )) return needs_client_window_clipping( hwnd ); /* window has children, needs compositing */ return FALSE; } A layered window is composited by UpdateLayeredWindow into the window surface, so it needs the same treatment. Adding one condition: --- a/dlls/winex11.drv/init.c +++ b/dlls/winex11.drv/init.c @@ -222,6 +222,7 @@ static BOOL needs_client_window_clipping( HWND hwnd ) BOOL needs_offscreen_rendering( HWND hwnd ) { + if (NtUserGetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_LAYERED) return TRUE; /* layered window, contents are composited by UpdateLayeredWindow */ if (NtUserGetDpiForWindow( hwnd ) != NtUserGetWinMonitorDpi( hwnd, MDT_RAW_DPI )) return TRUE; /* needs DPI scaling */ makes all of it correct: the C repro draws its red rectangle, and A/B/C/D of the WPF test all render. Measured by taking a screenshot and reading the pixel at the centre of the window, same build, same prefix, same binaries otherwise: before the change pixel = rgb(0,0,0) after the change pixel = rgb(255,0,0) I am not proposing that one-liner as the finished patch -- somebody who knows the driver will want to decide whether it should be keyed on the layered flag, on data->layered, or only when a client surface actually exists, and whether winewayland.drv needs the same. It is offered as the smallest change that demonstrates where the problem is. I did not manage to get winewayland.drv up in this session, so this report covers winex11.drv only. RELATION TO OTHER BUGS ---------------------- Bug 49055 ("Multiple .NET WPF 4.x applications show black screen or have broken user interface rendering") is the same user visible symptom and the same DisableHWAcceleration workaround. This is a different root cause from the viewport-scoped Clear() problem discussed there in comments #2..#6, so I filed it separately rather than piling onto that bug; feel free to merge them if you disagree. Bug 32507 is about how UpdateLayeredWindow interprets the alpha channel, which is a different question -- here the alpha handling never gets a chance to matter, because the painted result is hidden by the client window. -- Do not reply to this email, post in Bugzilla using the above URL to reply. You are receiving this mail because: You are watching all bug changes.
http://bugs.winehq.org/show_bug.cgi?id=60173 --- Comment #1 from Wehrwolfmann <wehrwolfmann@gmail.com> --- Created attachment 81820 --> http://bugs.winehq.org/attachment.cgi?id=81820 The same experiment on WPF: four windows in one program, layered ones black -- Do not reply to this email, post in Bugzilla using the above URL to reply. You are receiving this mail because: You are watching all bug changes.
http://bugs.winehq.org/show_bug.cgi?id=60173 --- Comment #2 from Wehrwolfmann <wehrwolfmann@gmail.com> --- Created attachment 81821 --> http://bugs.winehq.org/attachment.cgi?id=81821 Proposed fix: treat WS_EX_LAYERED as needing offscreen rendering -- Do not reply to this email, post in Bugzilla using the above URL to reply. You are receiving this mail because: You are watching all bug changes.
http://bugs.winehq.org/show_bug.cgi?id=60173 --- Comment #3 from Wehrwolfmann <wehrwolfmann@gmail.com> --- Created attachment 81822 --> http://bugs.winehq.org/attachment.cgi?id=81822 Before: WPF probe on stock Wine -- both layered windows are black -- Do not reply to this email, post in Bugzilla using the above URL to reply. You are receiving this mail because: You are watching all bug changes.
http://bugs.winehq.org/show_bug.cgi?id=60173 --- Comment #4 from Wehrwolfmann <wehrwolfmann@gmail.com> --- Created attachment 81823 --> http://bugs.winehq.org/attachment.cgi?id=81823 After: the same probe with the proposed one-line change -- layered windows paint -- Do not reply to this email, post in Bugzilla using the above URL to reply. You are receiving this mail because: You are watching all bug changes.
participants (1)
-
WineHQ Bugzilla