From 42b247756a47097e2d465c3dffd9c858d0864563 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Thu, 6 Jul 2023 19:51:37 +0200 Subject: [PATCH] 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. --- wled00/FX_2Dfcn.cpp | 35 ++++++++++++++++++++--------------- wled00/FX_fcn.cpp | 19 ++++++++++++------- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 0b4c9b26..778a28f9 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -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. 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); uint8_t r = R(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 void Segment::blurRow(uint16_t row, fract8 blur_amount) { - const uint16_t cols = virtualWidth(); - const uint16_t rows = virtualHeight(); + const uint_fast16_t cols = virtualWidth(); + const uint_fast16_t rows = virtualHeight(); if (row >= rows) return; // blur one row uint8_t keep = 255 - blur_amount; uint8_t seep = blur_amount >> 1; 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); + uint32_t before = uint32_t(cur); // remember color before blur CRGB part = cur; part.nscale8(seep); cur.nscale8(keep); cur += carryover; - if (x) { + if (x>0) { CRGB prev = CRGB(getPixelColorXY(x-1, row)) + part; 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; } } // blurCol: perform a blur on a column of a rectangular matrix void Segment::blurCol(uint16_t col, fract8 blur_amount) { - const uint16_t cols = virtualWidth(); - const uint16_t rows = virtualHeight(); + const uint_fast16_t cols = virtualWidth(); + const uint_fast16_t rows = virtualHeight(); if (col >= cols) return; // blur one column uint8_t keep = 255 - blur_amount; uint8_t seep = blur_amount >> 1; CRGB carryover = CRGB::Black; - for (uint16_t i = 0; i < rows; i++) { - CRGB cur = getPixelColorXY(col, i); + for (uint_fast16_t y = 0; y < rows; y++) { + CRGB cur = getPixelColorXY(col, y); CRGB part = cur; + uint32_t before = uint32_t(cur); // remember color before blur part.nscale8(seep); cur.nscale8(keep); cur += carryover; - if (i) { - CRGB prev = CRGB(getPixelColorXY(col, i-1)) + part; - setPixelColorXY(col, i-1, prev); + if (y>0) { + CRGB prev = CRGB(getPixelColorXY(col, y-1)) + part; + 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; } } @@ -392,8 +397,8 @@ void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) { for (uint16_t j = 0; j < dim1; j++) { uint16_t x = vertical ? i : j; uint16_t y = vertical ? j : i; - uint16_t xp = vertical ? x : x-1; - uint16_t yp = vertical ? y-1 : y; + int16_t xp = vertical ? x : x-1; // "signed" to prevent underflow + int16_t yp = vertical ? y-1 : y; // "signed" to prevent underflow uint16_t xn = vertical ? x : x+1; uint16_t yn = vertical ? y+1 : y; CRGB curr = getPixelColorXY(x,y); diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index fa3b7e6d..4224f254 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -887,6 +887,7 @@ void Segment::fade_out(uint8_t rate) { // fades all pixels to black using nscale8() 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 rows = virtualHeight(); // will be 1 for 1D @@ -901,23 +902,26 @@ void Segment::fadeToBlackBy(uint8_t fadeBy) { */ void Segment::blur(uint8_t blur_amount) { + if (blur_amount == 0) return; // optimization: 0 means "don't blur" #ifndef WLED_DISABLE_2D if (is2D()) { // compatibility with 2D - const uint16_t cols = virtualWidth(); - const uint16_t rows = virtualHeight(); - for (uint16_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 + const uint_fast16_t cols = virtualWidth(); + const uint_fast16_t rows = virtualHeight(); + for (uint_fast16_t i = 0; i < rows; i++) blurRow(i, blur_amount); // blur all rows + for (uint_fast16_t k = 0; k < cols; k++) blurCol(k, blur_amount); // blur all columns return; } #endif uint8_t keep = 255 - blur_amount; uint8_t seep = blur_amount >> 1; 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 part = cur; + uint32_t before = uint32_t(cur); // remember color before blur part.nscale8(seep); cur.nscale8(keep); cur += carryover; @@ -926,9 +930,10 @@ void Segment::blur(uint8_t blur_amount) uint8_t r = R(c); uint8_t g = G(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; } }