Skip to content

Commit

Permalink
Merge pull request #1563 from somechris/allow-sleep
Browse files Browse the repository at this point in the history
Allow to sleep instead of busy waiting when limiting refresh rate
  • Loading branch information
hzeller authored Jul 29, 2024
2 parents a98c563 + f3c3243 commit 860cc06
Show file tree
Hide file tree
Showing 9 changed files with 72 additions and 9 deletions.
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,20 @@ Use this also if you want to have a stable baseline refresh rate when using
the vsync-multiple flag `-V` in the [led-image-viewer] or
[video-viewer] utility programs.

```
--led-no-busy-waiting : Don't use busy waiting when limiting refresh rate.
```

This allows to switch from busy waiting to sleep waiting when limiting the
refresh rate (`--led-limit-refresh`).

By default, refresh rate limiting uses busy waiting, which is CPU intensive but
gives most accurate timings. This is fine for multi-core boards.

On single core boards (e.g.: Raspberry Pi Zero) busy waiting makes the system
unresponsive for other/background tasks. There, sleep waiting improves the
system's responsiveness at the cost of slightly less accurate timings.

```
--led-scan-mode=<0..1> : 0 = progressive; 1 = interlaced (Default: 0).
```
Expand Down Expand Up @@ -449,7 +463,7 @@ to debug if it has something to do with the sound subsystem (see Troubleshooting
section). This is really only recommended for debugging; typically you actually
want the hardware pulses as it results in a much more stable picture.

<a name="no-drop-priv"/>
<a name="no-drop-priv"></a>

```
--led-no-drop-privs : Don't drop privileges from 'root' after initializing the hardware.
Expand Down
6 changes: 6 additions & 0 deletions include/led-matrix-c.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,12 @@ struct RGBLedMatrixOptions {
* to keep a constant refresh rate. <= 0 for no limit.
*/
int limit_refresh_rate_hz; /* Corresponding flag: --led-limit-refresh */

/* Sleep instead of busy waiting when limiting refresh rate. This gives
* slightly less accurate frame timing, but lets the CPU work on other
* processes when waiting and renders single core boards more responsive.
*/
bool disable_busy_waiting; /* Corresponding flag: --led-busy-waiting */
};

/**
Expand Down
4 changes: 4 additions & 0 deletions include/led-matrix.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,10 @@ class RGBMatrix : public Canvas {
// Limit refresh rate of LED panel. This will help on a loaded system
// to keep a constant refresh rate. <= 0 for no limit.
int limit_refresh_rate_hz; // Flag: --led-limit-refresh

// Sleep instead of busy wait to free CPU cycles but get slightly less
// accurate frame timing.
bool disable_busy_waiting; // Flag: --led-busy-waiting
};

// Factory to create a matrix. Additional functionality includes dropping
Expand Down
7 changes: 7 additions & 0 deletions lib/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,13 @@ HARDWARE_DESC?=regular
# Flag: --led-limit-refresh
#DEFINES+=-DFIXED_FRAME_MICROSECONDS=5000

# When limiting refrash rate, a CPU core is busy waiting to get accurate
# timing. On single board systems, this results in an unresponsive system.
# By disabling busy waiting, CPU cycles are freed up, leading to a more
# responsive system at the cost of slightly less accurate frame timing.
# Flag: --led-no-busy-waiting
#DEFINES+=-DDISABLE_BUSY_WAITING

# Enable wide 64 bit GPIO offered with the compute module.
# This will use more memory to internally represent the frame buffer, so
# caches can't be utilized as much.
Expand Down
5 changes: 5 additions & 0 deletions lib/gpio.cc
Original file line number Diff line number Diff line change
Expand Up @@ -853,4 +853,9 @@ uint32_t GetMicrosecondCounter() {
return epoch_usec & 0xFFFFFFFF;
}

// For external use, e.g. to lessen busy waiting.
void SleepMicroseconds(long t) {
Timers::sleep_nanos(t * 1000);
}

} // namespace rgb_matrix
2 changes: 2 additions & 0 deletions lib/gpio.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ class PinPulser {
// if possible and a terrible slow fallback otherwise.
uint32_t GetMicrosecondCounter();

void SleepMicroseconds(long);

} // end namespace rgb_matrix

#endif // RPI_GPIO_INGERNALH
2 changes: 2 additions & 0 deletions lib/led-matrix-c.cc
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ static struct RGBLedMatrix *led_matrix_create_from_options_optional_edit(
OPT_COPY_IF_SET(pixel_mapper_config);
OPT_COPY_IF_SET(panel_type);
OPT_COPY_IF_SET(limit_refresh_rate_hz);
OPT_COPY_IF_SET(disable_busy_waiting);
#undef OPT_COPY_IF_SET
}

Expand Down Expand Up @@ -134,6 +135,7 @@ static struct RGBLedMatrix *led_matrix_create_from_options_optional_edit(
ACTUAL_VALUE_BACK_TO_OPT(pixel_mapper_config);
ACTUAL_VALUE_BACK_TO_OPT(panel_type);
ACTUAL_VALUE_BACK_TO_OPT(limit_refresh_rate_hz);
ACTUAL_VALUE_BACK_TO_OPT(disable_busy_waiting);
#undef ACTUAL_VALUE_BACK_TO_OPT
}

Expand Down
26 changes: 20 additions & 6 deletions lib/led-matrix.cc
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,10 @@ class RGBMatrix::Impl::UpdateThread : public Thread {
public:
UpdateThread(GPIO *io, FrameCanvas *initial_frame,
int pwm_dither_bits, bool show_refresh,
int limit_refresh_hz)
int limit_refresh_hz, bool allow_busy_waiting)
: io_(io), show_refresh_(show_refresh),
target_frame_usec_(limit_refresh_hz < 1 ? 0 : 1e6/limit_refresh_hz),
allow_busy_waiting_(allow_busy_waiting),
running_(true),
current_frame_(initial_frame), next_frame_(NULL),
requested_frame_multiple_(1) {
Expand Down Expand Up @@ -199,8 +200,13 @@ class RGBMatrix::Impl::UpdateThread : public Thread {
++low_bit_sequence;

if (target_frame_usec_) {
while ((GetMicrosecondCounter() - start_time_us) < target_frame_usec_) {
// busy wait. We have our dedicated core, so ok to burn cycles.
if (allow_busy_waiting_) {
while ((GetMicrosecondCounter() - start_time_us) < target_frame_usec_) {
// busy wait. We have our dedicated core, so ok to burn cycles.
}
} else {
long spent_us = GetMicrosecondCounter() - start_time_us;
SleepMicroseconds(target_frame_usec_ - spent_us);
}
}

Expand Down Expand Up @@ -245,6 +251,7 @@ class RGBMatrix::Impl::UpdateThread : public Thread {
GPIO *const io_;
const bool show_refresh_;
const uint32_t target_frame_usec_;
const bool allow_busy_waiting_;
uint32_t start_bit_[4];

Mutex running_mutex_;
Expand Down Expand Up @@ -314,9 +321,14 @@ RGBMatrix::Options::Options() :
pixel_mapper_config(NULL),
panel_type(NULL),
#ifdef FIXED_FRAME_MICROSECONDS
limit_refresh_rate_hz(1e6 / FIXED_FRAME_MICROSECONDS)
limit_refresh_rate_hz(1e6 / FIXED_FRAME_MICROSECONDS),
#else
limit_refresh_rate_hz(0),
#endif
#ifdef DISABLE_BUSY_WAITING
disable_busy_waiting(true)
#else
limit_refresh_rate_hz(0)
disable_busy_waiting(false)
#endif
{
// Nothing to see here.
Expand Down Expand Up @@ -348,6 +360,7 @@ static void PrintOptions(const RGBMatrix::Options &o) {
P_STR(pixel_mapper_config);
P_STR(panel_type);
P_INT(limit_refresh_rate_hz);
P_BOOL(disable_busy_waiting);
#undef P_INT
#undef P_STR
#undef P_BOOL
Expand Down Expand Up @@ -469,7 +482,8 @@ bool RGBMatrix::Impl::StartRefresh() {
if (updater_ == NULL && io_ != NULL) {
updater_ = new UpdateThread(io_, active_, params_.pwm_dither_bits,
params_.show_refresh_rate,
params_.limit_refresh_rate_hz);
params_.limit_refresh_rate_hz,
!params_.disable_busy_waiting);
// If we have multiple processors, the kernel
// jumps around between these, creating some global flicker.
// So let's tie it to the last CPU available.
Expand Down
13 changes: 11 additions & 2 deletions lib/options-initialize.cc
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,12 @@ static bool FlagInit(int &argc, char **&argv,
continue;
}

bool allow_busy_waiting = !mopts->disable_busy_waiting;
if (ConsumeBoolFlag("busy-waiting", it, &allow_busy_waiting)) {
mopts->disable_busy_waiting = !allow_busy_waiting;
continue;
}

bool request_help = false;
if (ConsumeBoolFlag("help", it, &request_help) && request_help) {
// In that case, we pretend to have failure in parsing, which will
Expand Down Expand Up @@ -338,7 +344,8 @@ void PrintMatrixFlags(FILE *out, const RGBMatrix::Options &d,
"\t--led-pwm-dither-bits=<0..2> : Time dithering of lower bits "
"(Default: 0)\n"
"\t--led-%shardware-pulse : %sse hardware pin-pulse generation.\n"
"\t--led-panel-type=<name> : Needed to initialize special panels. Supported: 'FM6126A', 'FM6127'\n",
"\t--led-panel-type=<name> : Needed to initialize special panels. Supported: 'FM6126A', 'FM6127'\n"
"\t--led-%sbusy-waiting : %sse busy waiting when limiting refresh rate.\n",
d.hardware_mapping,
d.rows, d.cols, d.chain_length, d.parallel,
(int) muxers.size(), CreateAvailableMultiplexString(muxers).c_str(),
Expand All @@ -350,7 +357,9 @@ void PrintMatrixFlags(FILE *out, const RGBMatrix::Options &d,
d.inverse_colors ? "no-" : "", d.inverse_colors ? "off" : "on",
d.pwm_lsb_nanoseconds,
!d.disable_hardware_pulsing ? "no-" : "",
!d.disable_hardware_pulsing ? "Don't u" : "U");
!d.disable_hardware_pulsing ? "Don't u" : "U",
!d.disable_busy_waiting ? "no-" : "",
!d.disable_busy_waiting ? "Don't u" : "U");

fprintf(out, "\t--led-slowdown-gpio=<0..4>: "
"Slowdown GPIO. Needed for faster Pis/slower panels "
Expand Down

0 comments on commit 860cc06

Please sign in to comment.