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 with CPU use #1698

Open
davepl opened this issue Aug 18, 2024 · 10 comments
Open

Flickering with CPU use #1698

davepl opened this issue Aug 18, 2024 · 10 comments

Comments

@davepl
Copy link

davepl commented Aug 18, 2024

I'm getting a lot of changes in overall brightness of the matrix second to second when the CPU is being used on the Pi.

I am receiving video frames over wifi/lan and decompressing them on the fly. No core is fully saturated, sitting at about 75% of a single core.

I'm running as root, have set the isocpus=3 in boot. Is there anything else I can try to remediate this?

@hzeller
Copy link
Owner

hzeller commented Aug 18, 2024

Unfortunately, the Pi shares internal data busses of what is needed to do the regular GPIO output and whatever it needs to for e.g. uncompressing, so even if the cores are not saturated some IO over the network or USB can already influence the flickering. Besides the usual recommendations w.r.t flickering found in the README, reducing as much as possible memory churn is the best you can do; the usual suspects: not using Python (it has a huge garbage collection impact), avoid scaling of images on the Pi, send them pre-scaled, possibly avoid uncompression, but just sent images as-is ...

For remote network sending I typically use the flaschen-taschen protocol for which there is a server implementation for the rpi-rgb-led-matrix ).

@davepl
Copy link
Author

davepl commented Aug 18, 2024 via email

@hzeller
Copy link
Owner

hzeller commented Aug 18, 2024

std::thread sounds good and should work fine. The relevant GPIO-pushing thread is already high priority so any 'normal' thread should be lower prio and not interfere from the CPU level.

But I suspect it is not actually the CPU-core utilization that is limiting, but internal memory busses inside the Pi (in particular with older Pis before Pi4).
The included video-viewer also does scaling on-the-fly, and I have seen some flickering in particular if it has do downscale from a large video input.

If you have a stream of images that you send, I recommend double-buffering using the SwapOnVSync() feature if you're not doing that already, otherwise there might be tearing which sometimes can look like flicker (though I still suspect the memory churn the decompression creates is responsible for the observed flicker).

Another thing: are you using the GUI on the Raspberry Pi or using it headless, just via ssh ? Anything running besides your main LED application can create flicker. So running headless with a minimum installation is good. Even running top in the background results in periodic flicker whenever it updates its output.
Pushing several Megahertz of Pixels out in a thread that somewhat requires real-time-ness and unobstructed memory-bus is somewhat problematic on the Pi with the limited resources...

Maybe you can play with --led-limit-refresh to create a time buffer to eliminate visual flicker (the README describes the process).

I’m not a Linux head,

No worries, I on the other hand have never used Windows -- it is always a bit hard to get used to different systems.
I like your YouTube channel!

@davepl
Copy link
Author

davepl commented Aug 18, 2024 via email

@hzeller
Copy link
Owner

hzeller commented Aug 19, 2024

Had a quick look

  • You're using SwapOnVSync() in the MatrixDraw, however not with double-buffering as you still write in the active matrix. For double-buffering, you'd first need to create a single offscreen-buffer (offscreen = matrix->CreateFrameCanvas() somewhere in the beginning), then fill the offscreen buffer (which implements the Canvas interface); then offscreen = matrix->SwapOnVSync(offscreen); swaps the offscreen buffer and returns the other buffer to fill in the next round. A good example might be seen in the clock.cc example code.
    For that you'd need some state (the offscreen buffer), which would mean having to change the MatrixDraw to non-static functions...
  • The fairly tight polling is probably something I'd avoid and rather have the BufferManager return the next time (I'd use struct timespec) which you then can wait on with an absolute time clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &frame_time, NULL);. Depending on if the frames come in out-of-sequence, there might be a different strategy. Anyway, probably not too much of a big problem w.r.t. the flickering, but any unnecessary calculation that can be avoided (like every millisecond figuring out what the age of the buffer is now, including locking and unlocking a mutex and doing a floating point calculation) makes it also easier to soothe programmer OCD :)

@davepl
Copy link
Author

davepl commented Aug 19, 2024 via email

@hzeller
Copy link
Owner

hzeller commented Aug 19, 2024

Is there a chance it’ll help with the variations in brightness?

Probably not if the brightness variations are not actually due to subtle tearing.

I see you're using the adafruit-hat as default in your Library. That will always result in subtle flicker unless you do the necessary hardware mod and then switch to adafruit-hat-pwm.

Without that, the timing of the various bit-planes has to be done in software, otherwise we can use a pin on the Pi that allows a hardware-generated timing. That is the single-most important thing to combat flicker.

Then afterwards, the observing the --led-show-refresh and choosing a value that is slightly below the typical variation with --led-limit-refresh.

@davepl
Copy link
Author

davepl commented Aug 20, 2024 via email

@hzeller
Copy link
Owner

hzeller commented Aug 23, 2024

Nice, glad you got it working. I still would strongly recommend using the adafruit-hat-pwm as it will in paricular help brightness flicker.

60Hz refresh is somewhat slow and would at least for me create some headache watching :) Usually I'd go with whatever the natural --led-show-refresh shows and then round it down a little. Say you see typical 150Hz with glitches down to 137Hz (--led-show-refresh shows the minimum refresh observed), maybe 140Hz is a good choice.

In a multi-core Pi (anything essentially but the very first one and Pi Zero), if you have isolcpu=3 setting, the busy-waiting change should not make a difference (except using more CPU and power on that core) - that core is reserved entirely for the matrix pushing anyway, it using 35% or 100% time won't help other cores. But busy-waiting might generate more fluctuations in timings, so could result in more flicker. Anyway, things to experiment with.

@sityware
Copy link

sityware commented Oct 9, 2024

I've experimented a lot with this. I have quite a hard time as I'm using Python and using memory hogging Selenium Chromium browser on a Pi Zero 2W

Yes the PWM mod is a must.

The double buffering so
canvas = rgb_matrix.SwapOnVSync(canvas)
graphics.DrawText(canvas ....

Important to monitor the processor usage and the relationship between.
--led-pwm-lsb-nanoseconds and --led-limit-refresh

I don't know the technicals exactly of why but if you have
--led-pwm-lsb-nanoseconds at 130 and --led-limit-refresh=100 this would use 88% cpu core
--led-pwm-lsb-nanoseconds at 130 and --led-limit-refresh=200 this would use 70% cpu core
Guessing the limit uses more cpu the more it has to stop it from reaching its max fps? So it seems better to have a higher fps as it uses less CPU.

I'm currently using --led-pwm-lsb-nanoseconds=127 and a 190 fps which uses about 74% cpu core. Only have one 64x32 display

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

No branches or pull requests

3 participants