blur speedup

it seems that SEGMENT.blur() is the main bottleneck for many 2D effects.

This change optimizes performance of the function:
* avoid to re-write unchanged pixels
* early exit when blur_amount == 0 (=nothing to do)
* use _fast_ types where possible

I've seen up to 20% speedup with this change.
This commit is contained in:
Frank 2023-07-06 19:51:37 +02:00
parent fbbf2d5eb3
commit 42b247756a
2 changed files with 32 additions and 22 deletions

View File

@ -306,6 +306,7 @@ void Segment::blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t
// Adds the specified color with the existing pixel color perserving color balance. // Adds the specified color with the existing pixel color perserving color balance.
void Segment::addPixelColorXY(int x, int y, uint32_t color, bool fast) { void Segment::addPixelColorXY(int x, int y, uint32_t color, bool fast) {
if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return; // if pixel would fall out of virtual segment just exit
uint32_t col = getPixelColorXY(x,y); uint32_t col = getPixelColorXY(x,y);
uint8_t r = R(col); uint8_t r = R(col);
uint8_t g = G(col); uint8_t g = G(col);
@ -330,50 +331,54 @@ void Segment::fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) {
// blurRow: perform a blur on a row of a rectangular matrix // blurRow: perform a blur on a row of a rectangular matrix
void Segment::blurRow(uint16_t row, fract8 blur_amount) { void Segment::blurRow(uint16_t row, fract8 blur_amount) {
const uint16_t cols = virtualWidth(); const uint_fast16_t cols = virtualWidth();
const uint16_t rows = virtualHeight(); const uint_fast16_t rows = virtualHeight();
if (row >= rows) return; if (row >= rows) return;
// blur one row // blur one row
uint8_t keep = 255 - blur_amount; uint8_t keep = 255 - blur_amount;
uint8_t seep = blur_amount >> 1; uint8_t seep = blur_amount >> 1;
CRGB carryover = CRGB::Black; CRGB carryover = CRGB::Black;
for (uint16_t x = 0; x < cols; x++) { for (uint_fast16_t x = 0; x < cols; x++) {
CRGB cur = getPixelColorXY(x, row); CRGB cur = getPixelColorXY(x, row);
uint32_t before = uint32_t(cur); // remember color before blur
CRGB part = cur; CRGB part = cur;
part.nscale8(seep); part.nscale8(seep);
cur.nscale8(keep); cur.nscale8(keep);
cur += carryover; cur += carryover;
if (x) { if (x>0) {
CRGB prev = CRGB(getPixelColorXY(x-1, row)) + part; CRGB prev = CRGB(getPixelColorXY(x-1, row)) + part;
setPixelColorXY(x-1, row, prev); setPixelColorXY(x-1, row, prev);
} }
setPixelColorXY(x, row, cur); if (before != uint32_t(cur)) // optimization: only set pixel if color has changed
setPixelColorXY(x, row, cur);
carryover = part; carryover = part;
} }
} }
// blurCol: perform a blur on a column of a rectangular matrix // blurCol: perform a blur on a column of a rectangular matrix
void Segment::blurCol(uint16_t col, fract8 blur_amount) { void Segment::blurCol(uint16_t col, fract8 blur_amount) {
const uint16_t cols = virtualWidth(); const uint_fast16_t cols = virtualWidth();
const uint16_t rows = virtualHeight(); const uint_fast16_t rows = virtualHeight();
if (col >= cols) return; if (col >= cols) return;
// blur one column // blur one column
uint8_t keep = 255 - blur_amount; uint8_t keep = 255 - blur_amount;
uint8_t seep = blur_amount >> 1; uint8_t seep = blur_amount >> 1;
CRGB carryover = CRGB::Black; CRGB carryover = CRGB::Black;
for (uint16_t i = 0; i < rows; i++) { for (uint_fast16_t y = 0; y < rows; y++) {
CRGB cur = getPixelColorXY(col, i); CRGB cur = getPixelColorXY(col, y);
CRGB part = cur; CRGB part = cur;
uint32_t before = uint32_t(cur); // remember color before blur
part.nscale8(seep); part.nscale8(seep);
cur.nscale8(keep); cur.nscale8(keep);
cur += carryover; cur += carryover;
if (i) { if (y>0) {
CRGB prev = CRGB(getPixelColorXY(col, i-1)) + part; CRGB prev = CRGB(getPixelColorXY(col, y-1)) + part;
setPixelColorXY(col, i-1, prev); setPixelColorXY(col, y-1, prev);
} }
setPixelColorXY(col, i, cur); if (before != uint32_t(cur)) // optimization: only set pixel if color has changed
setPixelColorXY(col, y, cur);
carryover = part; carryover = part;
} }
} }
@ -392,8 +397,8 @@ void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) {
for (uint16_t j = 0; j < dim1; j++) { for (uint16_t j = 0; j < dim1; j++) {
uint16_t x = vertical ? i : j; uint16_t x = vertical ? i : j;
uint16_t y = vertical ? j : i; uint16_t y = vertical ? j : i;
uint16_t xp = vertical ? x : x-1; int16_t xp = vertical ? x : x-1; // "signed" to prevent underflow
uint16_t yp = vertical ? y-1 : y; int16_t yp = vertical ? y-1 : y; // "signed" to prevent underflow
uint16_t xn = vertical ? x : x+1; uint16_t xn = vertical ? x : x+1;
uint16_t yn = vertical ? y+1 : y; uint16_t yn = vertical ? y+1 : y;
CRGB curr = getPixelColorXY(x,y); CRGB curr = getPixelColorXY(x,y);

View File

@ -887,6 +887,7 @@ void Segment::fade_out(uint8_t rate) {
// fades all pixels to black using nscale8() // fades all pixels to black using nscale8()
void Segment::fadeToBlackBy(uint8_t fadeBy) { void Segment::fadeToBlackBy(uint8_t fadeBy) {
if (fadeBy == 0) return; // optimization - no scaling to apply
const uint16_t cols = is2D() ? virtualWidth() : virtualLength(); const uint16_t cols = is2D() ? virtualWidth() : virtualLength();
const uint16_t rows = virtualHeight(); // will be 1 for 1D const uint16_t rows = virtualHeight(); // will be 1 for 1D
@ -901,23 +902,26 @@ void Segment::fadeToBlackBy(uint8_t fadeBy) {
*/ */
void Segment::blur(uint8_t blur_amount) void Segment::blur(uint8_t blur_amount)
{ {
if (blur_amount == 0) return; // optimization: 0 means "don't blur"
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
if (is2D()) { if (is2D()) {
// compatibility with 2D // compatibility with 2D
const uint16_t cols = virtualWidth(); const uint_fast16_t cols = virtualWidth();
const uint16_t rows = virtualHeight(); const uint_fast16_t rows = virtualHeight();
for (uint16_t i = 0; i < rows; i++) blurRow(i, blur_amount); // blur all rows for (uint_fast16_t i = 0; i < rows; i++) blurRow(i, blur_amount); // blur all rows
for (uint16_t k = 0; k < cols; k++) blurCol(k, blur_amount); // blur all columns for (uint_fast16_t k = 0; k < cols; k++) blurCol(k, blur_amount); // blur all columns
return; return;
} }
#endif #endif
uint8_t keep = 255 - blur_amount; uint8_t keep = 255 - blur_amount;
uint8_t seep = blur_amount >> 1; uint8_t seep = blur_amount >> 1;
CRGB carryover = CRGB::Black; CRGB carryover = CRGB::Black;
for(uint16_t i = 0; i < virtualLength(); i++) uint_fast16_t vlength = virtualLength();
for(uint_fast16_t i = 0; i < vlength; i++)
{ {
CRGB cur = CRGB(getPixelColor(i)); CRGB cur = CRGB(getPixelColor(i));
CRGB part = cur; CRGB part = cur;
uint32_t before = uint32_t(cur); // remember color before blur
part.nscale8(seep); part.nscale8(seep);
cur.nscale8(keep); cur.nscale8(keep);
cur += carryover; cur += carryover;
@ -926,9 +930,10 @@ void Segment::blur(uint8_t blur_amount)
uint8_t r = R(c); uint8_t r = R(c);
uint8_t g = G(c); uint8_t g = G(c);
uint8_t b = B(c); uint8_t b = B(c);
setPixelColor(i-1, qadd8(r, part.red), qadd8(g, part.green), qadd8(b, part.blue)); setPixelColor((uint16_t)(i-1), qadd8(r, part.red), qadd8(g, part.green), qadd8(b, part.blue));
} }
setPixelColor(i,cur.red, cur.green, cur.blue); if (before != uint32_t(cur)) // optimization: only set pixel if color has changed
setPixelColor((uint16_t)i,cur.red, cur.green, cur.blue);
carryover = part; carryover = part;
} }
} }