Skip to content

Commit b7f5c27

Browse files
committed
_pixelbuf: Increase performance of brightness-scaling
On the Pico, this increases the "fill rate" of pixels[:] = newvalues considerably. On a strip of 240 RGB LEDs, auto_write=False, the timings are: || Brightness || Before || After || Improvement || || 1.0 || 117 kpix/s || 307 kpix/s || 2.62x || || 0.07 || 117 kpix/s || 273 kpix/s || 2.33x || It's worth noting that even the "before" rate is fast compared to the time to transmit a single neopixel, but any time we can gain back in the whole pipeline will let marginal animations work a little better. To set all the pixels in this way and then show() gives a pleasant bump to the framerate, from about 108Hz to 124Hz (1.15x) The main source of speed-up is using integer math instead of floating point math for the calculation of the post-scaled pixel values. A slight secondary gain is achieved by avoiding the scaling altogether when the scale factor is 1.0. Because the math is not exactly the same, some scaled pixel values may change by +- 1 RGBW "step". In practice, this is unlikely to matter. The gains are bigger on the Pico and other M0 microcontrollers than M4 microcontrollers with floating point math in the hardware. Happily, flash size is also improved a bit on the Pico build I did, going from > 542552 bytes used, 506024 bytes free in flash firmware space out of 1048576 bytes (1024.0kB). to > 542376 bytes used, 506200 bytes free in flash firmware space out of 1048576 bytes (1024.0kB).
1 parent e41137c commit b7f5c27

File tree

2 files changed

+67
-33
lines changed

2 files changed

+67
-33
lines changed

shared-module/_pixelbuf/PixelBuf.c

Lines changed: 65 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "py/runtime.h"
3232
#include "shared-bindings/_pixelbuf/PixelBuf.h"
3333
#include <string.h>
34+
#include <math.h>
3435

3536
// Helper to ensure we have the native super class instead of a subclass.
3637
static pixelbuf_pixelbuf_obj_t* native_pixelbuf(mp_obj_t pixelbuf_obj) {
@@ -69,6 +70,7 @@ void common_hal__pixelbuf_pixelbuf_construct(pixelbuf_pixelbuf_obj_t *self, size
6970
}
7071
// Call set_brightness so that it can allocate a second buffer if needed.
7172
self->brightness = 1.0;
73+
self->scaled_brightness = 0x100;
7274
common_hal__pixelbuf_pixelbuf_set_brightness(MP_OBJ_FROM_PTR(self), brightness);
7375

7476
// Turn on auto_write. We don't want to do it with the above brightness call.
@@ -109,26 +111,31 @@ void common_hal__pixelbuf_pixelbuf_set_brightness(mp_obj_t self_in, mp_float_t b
109111
pixelbuf_pixelbuf_obj_t* self = native_pixelbuf(self_in);
110112
// Skip out if the brightness is already set. The default of self->brightness is 1.0. So, this
111113
// also prevents the pre_brightness_buffer allocation when brightness is set to 1.0 again.
112-
mp_float_t change = brightness - self->brightness;
113-
if (-0.001 < change && change < 0.001) {
114+
self->brightness = brightness;
115+
uint16_t new_scaled_brightness = (int)roundf(brightness * 256);
116+
if (new_scaled_brightness == self->scaled_brightness) {
114117
return;
115118
}
116-
self->brightness = brightness;
119+
self->scaled_brightness = new_scaled_brightness;
117120
size_t pixel_len = self->pixel_count * self->bytes_per_pixel;
118-
if (self->pre_brightness_buffer == NULL) {
119-
self->pre_brightness_buffer = m_malloc(pixel_len, false);
120-
memcpy(self->pre_brightness_buffer, self->post_brightness_buffer, pixel_len);
121-
}
122-
for (size_t i = 0; i < pixel_len; i++) {
123-
// Don't adjust per-pixel luminance bytes in dotstar mode
124-
if (self->byteorder.is_dotstar && i % 4 == 0) {
125-
continue;
121+
if (self->scaled_brightness == 0x100 && !self->pre_brightness_buffer) {
122+
return;
123+
} else {
124+
if (self->pre_brightness_buffer == NULL) {
125+
self->pre_brightness_buffer = m_malloc(pixel_len, false);
126+
memcpy(self->pre_brightness_buffer, self->post_brightness_buffer, pixel_len);
127+
}
128+
for (size_t i = 0; i < pixel_len; i++) {
129+
// Don't adjust per-pixel luminance bytes in dotstar mode
130+
if (self->byteorder.is_dotstar && i % 4 == 0) {
131+
continue;
132+
}
133+
self->post_brightness_buffer[i] = (self->pre_brightness_buffer[i] * self->scaled_brightness) >> 8;
126134
}
127-
self->post_brightness_buffer[i] = self->pre_brightness_buffer[i] * self->brightness;
128-
}
129135

130-
if (self->auto_write) {
131-
common_hal__pixelbuf_pixelbuf_show(self_in);
136+
if (self->auto_write) {
137+
common_hal__pixelbuf_pixelbuf_show(self_in);
138+
}
132139
}
133140
}
134141

@@ -197,28 +204,34 @@ void _pixelbuf_set_pixel_color(pixelbuf_pixelbuf_obj_t* self, size_t index, uint
197204
}
198205
pixelbuf_rgbw_t *rgbw_order = &self->byteorder.byteorder;
199206
size_t offset = index * self->bytes_per_pixel;
200-
if (self->pre_brightness_buffer != NULL) {
201-
uint8_t* pre_brightness_buffer = self->pre_brightness_buffer + offset;
202-
if (self->bytes_per_pixel == 4) {
203-
pre_brightness_buffer[rgbw_order->w] = w;
204-
}
205-
206-
pre_brightness_buffer[rgbw_order->r] = r;
207-
pre_brightness_buffer[rgbw_order->g] = g;
208-
pre_brightness_buffer[rgbw_order->b] = b;
207+
uint8_t *scaled_buffer, *unscaled_buffer;
208+
if (self->pre_brightness_buffer) {
209+
scaled_buffer = self->post_brightness_buffer + offset;
210+
unscaled_buffer = self->pre_brightness_buffer + offset;
211+
} else {
212+
scaled_buffer = NULL;
213+
unscaled_buffer = self->post_brightness_buffer + offset;
209214
}
210215

211-
uint8_t* post_brightness_buffer = self->post_brightness_buffer + offset;
212216
if (self->bytes_per_pixel == 4) {
213-
// Only apply brightness if w is actually white (aka not DotStar.)
214-
if (!self->byteorder.is_dotstar) {
215-
w *= self->brightness;
217+
unscaled_buffer[rgbw_order->w] = w;
218+
}
219+
220+
unscaled_buffer[rgbw_order->r] = r;
221+
unscaled_buffer[rgbw_order->g] = g;
222+
unscaled_buffer[rgbw_order->b] = b;
223+
224+
if (scaled_buffer) {
225+
if (self->bytes_per_pixel == 4) {
226+
if (!self->byteorder.is_dotstar) {
227+
w = (w * self->scaled_brightness) >> 8;
228+
}
229+
scaled_buffer[rgbw_order->w] = w;
216230
}
217-
post_brightness_buffer[rgbw_order->w] = w;
231+
scaled_buffer[rgbw_order->r] = (r * self->scaled_brightness) >> 8;
232+
scaled_buffer[rgbw_order->g] = (g * self->scaled_brightness) >> 8;
233+
scaled_buffer[rgbw_order->b] = (b * self->scaled_brightness) >> 8;
218234
}
219-
post_brightness_buffer[rgbw_order->r] = r * self->brightness;
220-
post_brightness_buffer[rgbw_order->g] = g * self->brightness;
221-
post_brightness_buffer[rgbw_order->b] = b * self->brightness;
222235
}
223236

224237
void _pixelbuf_set_pixel(pixelbuf_pixelbuf_obj_t* self, size_t index, mp_obj_t value) {
@@ -318,3 +331,23 @@ void common_hal__pixelbuf_pixelbuf_fill(mp_obj_t self_in, mp_obj_t fill_color) {
318331
common_hal__pixelbuf_pixelbuf_show(self_in);
319332
}
320333
}
334+
335+
mp_int_t common_hal__pixelbuf_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) {
336+
pixelbuf_pixelbuf_obj_t *self = native_pixelbuf(self_in);
337+
bufinfo->buf = self->pre_brightness_buffer;
338+
if (self->pre_brightness_buffer) {
339+
// If we have a brightness setting, we must treat the buffer as
340+
// read-only (because we have no way to "fire" the
341+
// brightness-converting code as a side effect of mutation via the
342+
// buffer)
343+
if ((flags & MP_BUFFER_WRITE)) {
344+
return 1;
345+
}
346+
bufinfo->buf = self->pre_brightness_buffer;
347+
} else {
348+
bufinfo->buf = self->post_brightness_buffer;
349+
}
350+
bufinfo->typecode = 'B';
351+
bufinfo->len = self->bytes_per_pixel * common_hal__pixelbuf_pixelbuf_get_len(self_in);
352+
return 0;
353+
}

shared-module/_pixelbuf/PixelBuf.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ typedef struct {
4949
typedef struct {
5050
mp_obj_base_t base;
5151
size_t pixel_count;
52-
size_t bytes_per_pixel;
52+
uint16_t bytes_per_pixel;
53+
uint16_t scaled_brightness;
5354
pixelbuf_byteorder_details_t byteorder;
5455
mp_float_t brightness;
5556
mp_obj_t transmit_buffer_obj;

0 commit comments

Comments
 (0)