Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Flickering in Linux if background's opacity is not 100% and update rate is high #58

Open
UlyssesZh opened this issue Jun 10, 2022 · 12 comments
Assignees

Comments

@UlyssesZh
Copy link

Just as the title says. The Java I use is OpenJDK 18.

@RoanH RoanH self-assigned this Jun 11, 2022
@RoanH
Copy link
Owner

RoanH commented Jun 11, 2022

Thanks for the report, there used to be another open issue describing an issue very similar to this. But it appears that the issue and original author are both deleted from GitHub so I completely lost track of this...

I assume that the entire window/frame is disappearing and not just part part of it?

It's also highly likely that this is a problem with the display/graphics driver and therefore possibly not something I can fix. I'll try to run some transparency tests on Linux when I have more free time.

@UlyssesZh
Copy link
Author

Sorry because I reinstalled the whole OS on this computer to an Ubuntu 20.04 and I cannot reproduce the bug with X11 GNOME... I'll just describe the phenomenon: the whole window of KeysPerSecond (the one that shows keyboard actions) is quickly switching between the normal appearance and the abnormal appearance where the window's rect is totally black (not transparent). The fast switching makes it looks like flickering.

@RoanH
Copy link
Owner

RoanH commented Jun 13, 2022

Alright, that does sounds slightly different from the previous issue, in the old issue the entire window disappeared. If it goes black then there might be something I can do, assuming I manage to reproduce it so I can debug it.

@UlyssesZh
Copy link
Author

I suddenly reproduced this (Ubuntu 20.04, X11).

@RoanH
Copy link
Owner

RoanH commented Oct 8, 2022

Thanks for the information, I should have a system that fits those specifications so I'll try to reproduce it.

@RoanH
Copy link
Owner

RoanH commented Oct 8, 2022

Alright, I managed to reproduce this. Seems like the entire window goes invisible for a bit when the GUI is redrawn, the result also isn't very transparent either. So out of curiosity I tried some older versions again that I know used to work fine with transparency. Version 5.5 sort of still works, but it leaves after images for some reason? Version 3.12 on the other hand looks like a mess. Interestingly the transparency logic between v5.5 and v8.7 is nearly identical, so I have no idea how the results are so different. Unfortunately, I don't really have the time right now to investigate this much further. This seems like it would probably be fairly hard to fix and possibly also something that could break really easily again in the future.

@RoanH
Copy link
Owner

RoanH commented Oct 8, 2022

Still not entirely sure if this issue and #37 are caused by the same root problem, but it seems probable.

@T4M1L0S
Copy link

T4M1L0S commented Apr 19, 2023

That does happen to me as well.

@UlyssesZh
Copy link
Author

Seems related: https://stackoverflow.com/q/15704947/10245493

@RoanH
Copy link
Owner

RoanH commented Jan 17, 2024

Yeah that looks like it's probably the exact same issue, bit unfortunate that it has no answer though...

@UlyssesZh
Copy link
Author

I managed to produce the same bug with this minimal code:

import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.JFrame;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class test {
	static final int WIDTH = 200;
	static final int HEIGHT = 100;
	static final int REFRESH_RATE_MS = 1;
	static final Color BACKGROUND_COLOR = new Color(255, 0, 0, 127);

	public static void main(String[] args) {
		SwingUtilities.invokeLater(() -> {
			JPanel content = new JPanel() {
				@Override
				public void paintComponent(Graphics g1) {
					Graphics2D g = (Graphics2D) g1;
					g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, BACKGROUND_COLOR.getAlpha() / 255f));
					g.setColor(BACKGROUND_COLOR);
					g.fillRect(0, 0, getWidth(), getHeight());
				}
			};
			content.setOpaque(false);
			content.setBackground(new Color(0, 0, 0, 0));
	
			JFrame frame = new JFrame("test");
			frame.setUndecorated(true);
			frame.setBackground(new Color(0, 0, 0, 0));
			frame.add(content);
			frame.setSize(WIDTH, HEIGHT);
			frame.setVisible(true);

			Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(content::repaint, 0, REFRESH_RATE_MS, TimeUnit.MILLISECONDS);
		});
	}
}

The key is that REFRESH_RATE_MS cannot be too high. Here it is 1. The bug would not happen if it is set to something larger than that. Although the minimal example does not have this bug when the refresh rate is at least 2ms (on my machine), there is still some occasional flickering with KPS even if I set the update rate to 500ms (though much less intense than the situation with 1ms).

My guess is that, with setOpaque(false) and AlphaComposite.SRC, for each cycle, the graphics gets cleared before redrawing. However, the next cycle kicks in before the redrawing finishes, and they kind of conflict. For low refresh rates, flickering can still occasionally happen if the buffer somehow gets written to the display before the redrawing finishes. Those who implemented this logic for Linux must haven't thought thoroughly enough about manipulating the buffers...

With this guess, I think if we can let the UI redraw on demand (only redraw when something updates, and only redraw the area where there is some update), the flickering should be mostly gone except for graphs (which are updated every cycle).

@UlyssesZh UlyssesZh changed the title Flickering in Ubuntu 22.04 Wayland GNOME desktop if background's opacity is not 100% Flickering in Linux if background's opacity is not 100% and update rate is high Jan 23, 2024
@RoanH
Copy link
Owner

RoanH commented Jan 28, 2024

Seems like the underlying issue could be with the double buffering implementation being used, I wonder if it's even double buffered at all at this point.... But there are also a number of seemingly related JDK bugs (e.g., JDK-8303950).

Probably the amount of content that has to be drawn affects how easily it occurs too. Swing is supposed to be single threaded though, so it shouldn't be possible for two repaints to happen concurrently (repaint just schedules a repaint, it doesn't actually repaint). I'm surprised it's possible to flush an intermediate state to the display at all though given that Swing is supposed to be double buffered.

There's not really much to change when it comes to UI updates though. Graph and data panels need to be updated each cycle and key panels already repaint only themselves on demand. So the only save there is not repainting key panels during an update cycle, which I'm not expecting to change much.

I wonder if things could be fixed or mitigated if I implement double/triple buffering myself though, but I'm expecting some underlying JDK issue to be the core problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants