mimoguz / custom_window

BSD 3-Clause "New" or "Revised" License
14 stars 1 forks source link

Graphic issues on certain GPUs #2

Open Eroica opened 2 years ago

Eroica commented 2 years ago

Hi there,

I was hoping that you might help investigating an issue with the Mica effect that I noticed on certain hardware. Similar to you, I was applying Windows 11's Mica style to a JavaFX window by setting the necessary "magic" values (20 and 1029 before 22H2, now 38 on 22H2). While this works great on my main development machine, there seems to be a graphical glitch on certain hardware that make the effect look wrong. For example, with your test code:

1

Left is how it should look, and right is how it actually looks on another computer that uses an NVIDIA GPU. My main computer where everything works as intended uses an AMD graphics card. The scene is set to Color.TRANSPARENT, so it seems the "real" color of the window shines through and is black.

At first I noticed this problem on a notebook that uses Intel integrated graphics. I put this on hold because I thought the 22H2 update might fix this issue. However, I now have 22H2 on both an AMD and an NVIDIA system, and the NVIDIA computer still has the same issue no matter if I use my original code or your library.

Now the interesting thing is that if I turn off hardware acceleration (System.setProperty("prism.order, "sw")), then both the NVIDIA and the Intel machine show the Mica effect correctly. The fact that other Windows applications (e.g. Explorer) also correctly show the Mica effect makes me think it might something internal to JavaFX---however, it seems, only on certain hardware. Would you happen to know what might be the cause of this?

I investigated a little bit more and I was able to achieve something by using Windows' SetLayeredWindowAttributes(hWnd, RGB(255, 0, 255), 0, LWA_COLORKEY); and setting the scene background to Magenta (here showcased with my program):

2

Again, left is how it should look, and right is how it looks on an NVIDIA system. What happens is that while the window correctly shows the Mica effect, it is completely mouse transparent, and only the magenta color is actually clickable (and of course there is this strange color fringe).

What I also found out is that other GUI toolkits are able to correctly "Mica-ify" a window on my NVIDIA system, e.g.:

https://github.com/martinet101/win32mica/ https://github.com/dongle-the-gadget/SystemBackdropTypes

However, they seem to do a little bit more than only setting DwmSetWindowAttribute. For example, the WPF example works with a CompositionTarget and a transparent color here: https://github.com/dongle-the-gadget/SystemBackdropTypes/blob/ab18f1aa70f0575e5df42ebad5ae2699b2ee4c9a/SystemBackdropTypes/MainWindow.xaml.cs#L34

Sadly, my knowledge about the Windows API is limited, so I wonder if you have any more insights on why this seems to be a JavaFX problem, and what exactly the drivers do different so that it only occurs on certain hardware.

mimoguz commented 2 years ago

I don't think I can help. Looking at your comment, you seem more versed in Win32 API than me :)

I can add a few more points to your findings though. On my notebook, which has an NVidia card:

At this point, I don't think it is possible to create a mica window without doing it properly on stage creation. Probably a custom stage implementation is required here, but this is too big a task for what I use JavaFX for.

Sorry :(

Eroica commented 2 years ago

No problem, thanks for your hint about the background color! Unfortunately, it doesn't seem to do the same on my NVIDIA desktop, so there might be even more uncertainties.

I also heard conflicting opinions on whether one has to use WS_EX_NOREDIRECTIONBITMAP at creation time or whether one can set it later with SetWindowLong. There are also some differences when using StageStyle.UNIFIED or not, but so far I wasn't able to find out what exactly JavaFX does different in these 2 cases when creating windows (https://github.com/openjdk/jfx/blob/86b854dc367fb32743810716da5583f7d59208f8/modules/javafx.graphics/src/main/native-glass/win/BaseWnd.cpp#L112).

I'll keep investigating this issue, if you have the time feel free to let me know about any changes that you noticed!

Eroica commented 2 years ago

An additional thing that I found out: If I use StageStyle.TRANSPARENT then Mica seems to work as expected! But of course, the window decorations are now missing. JavaFX skips some steps when the window is set to be transparent, or there is something going on with blending the effect onto the window: https://github.com/openjdk/jfx/blob/303bcdb9ba40b7d82e555d675dad8d028e018c86/modules/javafx.graphics/src/main/native-glass/win/GlassWindow.cpp#L807

mimoguz commented 2 years ago

The most annoying part is that it just works on my desktop, which has an AMD card:

tabbed

:(

Eroica commented 2 years ago

Hi,

using an undocumented -Dprism.forceUploadingPainter=true/System.setProperty("prism.forceUploadingPainter", "true"), I was able to make Mica work on my NVIDIA machine! Can you try it on your system? I will also check whether it works on my Intel graphics system later.

By the way,

The most annoying part is that it just works on my desktop, which has an AMD card:

I had a similar experience. My main machine also uses AMD so in the beginning I thought that Mica works without problems. However, I had another user running AMD where for some reason, the same code didn't work! I needed to investigate that too. But if that forceUploadingPainter works on NVIDIA and Intel, I hope that it might also remove any inconsistencies on AMD hardware as well.

mimoguz commented 2 years ago

It doesn't work on mine :( But maybe I'm doing it wrong? I'm more of a sbt person than maven person. How did you pass that option? I tried setting $env:MAVEN_OPTS/$env:JAVA_OPTS, and calling setProperty in main before launch().

Eroica commented 2 years ago

I'm actually mainly using Gradle, with Gradle it depends a little bit on whether it runs a main() function or directly instantiates the JavaFX application.

In general, you could look for the way how to pass "JVM arguments" to Java. For example, in Gradle one can use applicationDefaultJvmArgs, and then you also need to prepend the option with a D like this: -Dprism.forceUploadingPainter=true

But it actually should work if you use System.setProperty before launch ... can you check whether other setProperty works, e.g. System.setProperty("prism.verbose", "true")? It should then print some information about your graphics configuration.

mimoguz commented 2 years ago

Turns out you can list JVM options under JavaFX plugin configuration. And it works! Well, the title text background is still opaque but it works :)

Eroica commented 2 years ago

Great to hear! I actually do not have this opaque title problem anymore, at least on my current 22H2 machine. I remember having it in the past (before 22H2), but I also put it off until later. Luckily, it seems to be gone for me, but I will look for a fix and tell you if it happens to me again.

For the sake of completeness, I'll collect my data points on Mica issues here:

Graphics Works out-of-the-box Works with forceUploadingPainter
AMD (Polaris, Navi) YES YES(1)
NVIDIA (30XX series) NO YES(2)
Intel (Xe) NO YES(2)

(1) With performance loss on monitors >60Hz. (2) Possibly with performance loss on monitors >60Hz.

If I notice that it is driver-dependend, I'll lookup the versions and add them later.

mimoguz commented 2 years ago

You can put 'yes' to both column for RX 570X. And yes, opaque background isn't an issue in 22H2, my notebook is not yet updated.

Eroica commented 2 years ago

It turns out the Mica issue that the RX 580 user had was actually me additionally using DwmExtendFrameIntoClientArea (and me only applying Mica's light theme on a dark-themed desktop). So it seems AMD isn't affected by this in general.

One could now simply enable forceUploadingPainter on all systems, but I noticed a great performance loss if enabling that and also having a monitor with >60Hz. This might affect only AMD or could depend on the higher refresh rate in general.

In that case, also setting javafx.animation.fullspeed=true gets rid of what seems to be a frame limit.

Here is also some additional information on what forceUploadingPainter does:

... Note that it's only required for UploadingPainer. The PresentingPainter does not call uploadPixels(), and thus doesn't depend on RT-24168.

https://bugs.openjdk.org/browse/JDK-8091385

 * UploadingPainter is used when we need to render into an offscreen buffer.
 * The PresentingPainter is used when we are rendering to the main screen.
 */

https://github.com/openjdk/jfx/blob/master/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/UploadingPainter.java

 * The PresentingPainter is used when we are rendering to the main screen.
 * UploadingPainter is used when we need to render into an offscreen buffer.
 */

https://github.com/openjdk/jfx/blob/master/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/PresentingPainter.java

For now I'll update the table to be more general because in the end it only seems to depend on using AMD or not. I will probably build a check for a user's GPU and enable forceUploadingPainter if necessary, but if I find out another fix I will let you know!

mimoguz commented 2 years ago

πŸ‘

You're awesome.

I will re-open this issue if you don't mind, just to make it a bit more visible. Who knows, maybe there is a third person on the internet that cares about JavaFX on Windows 11 πŸ™‚.

ghost commented 1 year ago

πŸ‘

You're awesome.

I will re-open this issue if you don't mind, just to make it a bit more visible. Who knows, maybe there is a third person on the internet that cares about JavaFX on Windows 11 πŸ™‚.

me! πŸ˜ƒ