Merge pull request #276 from DedeHai/PS_fixes

ParticleFX: adding fixes and improvements from AC
This commit is contained in:
Frank
2025-11-01 13:41:39 +01:00
committed by GitHub
2 changed files with 58 additions and 54 deletions

View File

@@ -593,7 +593,7 @@ void ParticleSystem2D::render() {
baseRGB = ColorFromPalette(SEGPALETTE, particles[i].hue, 255, blend); baseRGB = ColorFromPalette(SEGPALETTE, particles[i].hue, 255, blend);
if (particles[i].sat < 255) { if (particles[i].sat < 255) {
CHSV baseHSV = rgb2hsv_approximate(baseRGB); // convert to HSV CHSV baseHSV = rgb2hsv_approximate(baseRGB); // convert to HSV
baseHSV.s = particles[i].sat; // set the saturation baseHSV.s = min(baseHSV.s, particles[i].sat); // set the saturation but don't increase it
hsv2rgb_spectrum(baseHSV, baseRGB); // convert back to RGB hsv2rgb_spectrum(baseHSV, baseRGB); // convert back to RGB
} }
} }
@@ -1056,13 +1056,7 @@ void blur2D(CRGB *colorbuffer, uint32_t xsize, uint32_t ysize, uint32_t xblur, u
//non class functions to use for initialization //non class functions to use for initialization
uint32_t calculateNumberOfParticles2D(uint32_t const pixels, const bool isadvanced, const bool sizecontrol) { uint32_t calculateNumberOfParticles2D(uint32_t const pixels, const bool isadvanced, const bool sizecontrol) {
uint32_t numberofParticles = pixels; // 1 particle per pixel (for example 512 particles on 32x16) uint32_t numberofParticles = pixels; // 1 particle per pixel (for example 512 particles on 32x16)
#ifdef ESP8266 uint32_t particlelimit = MAXPARTICLES_2D; // maximum number of paticles allowed
uint32_t particlelimit = ESP8266_MAXPARTICLES; // maximum number of paticles allowed (based on one segment of 16x16 and 4k effect ram)
#elif ARDUINO_ARCH_ESP32S2
uint32_t particlelimit = ESP32S2_MAXPARTICLES; // maximum number of paticles allowed (based on one segment of 32x32 and 24k effect ram)
#else
uint32_t particlelimit = ESP32_MAXPARTICLES; // maximum number of paticles allowed (based on two segments of 32x32 and 40k effect ram)
#endif
numberofParticles = max((uint32_t)4, min(numberofParticles, particlelimit)); // limit to 4 - particlelimit numberofParticles = max((uint32_t)4, min(numberofParticles, particlelimit)); // limit to 4 - particlelimit
if (isadvanced) // advanced property array needs ram, reduce number of particles to use the same amount if (isadvanced) // advanced property array needs ram, reduce number of particles to use the same amount
numberofParticles = (numberofParticles * sizeof(PSparticle)) / (sizeof(PSparticle) + sizeof(PSadvancedParticle)); numberofParticles = (numberofParticles * sizeof(PSparticle)) / (sizeof(PSparticle) + sizeof(PSadvancedParticle));
@@ -1075,16 +1069,8 @@ uint32_t calculateNumberOfParticles2D(uint32_t const pixels, const bool isadvanc
} }
uint32_t calculateNumberOfSources2D(uint32_t pixels, uint32_t requestedsources) { uint32_t calculateNumberOfSources2D(uint32_t pixels, uint32_t requestedsources) {
#ifdef ESP8266 int numberofSources = min((pixels) / SOURCEREDUCTIONFACTOR, (uint32_t)requestedsources);
int numberofSources = min((pixels) / 8, (uint32_t)requestedsources); numberofSources = max(1, min(numberofSources, MAXSOURCES_2D)); // limit
numberofSources = max(1, min(numberofSources, ESP8266_MAXSOURCES)); // limit
#elif ARDUINO_ARCH_ESP32S2
int numberofSources = min((pixels) / 6, (uint32_t)requestedsources);
numberofSources = max(1, min(numberofSources, ESP32S2_MAXSOURCES)); // limit
#else
int numberofSources = min((pixels) / 4, (uint32_t)requestedsources);
numberofSources = max(1, min(numberofSources, ESP32_MAXSOURCES)); // limit
#endif
// make sure it is a multiple of 4 for proper memory alignment // make sure it is a multiple of 4 for proper memory alignment
numberofSources = (numberofSources+3) & ~0x03; numberofSources = (numberofSources+3) & ~0x03;
return numberofSources; return numberofSources;
@@ -1122,10 +1108,19 @@ bool initParticleSystem2D(ParticleSystem2D *&PartSys, uint32_t requestedsources,
PSPRINT(" segmentsize:" + String(cols) + " x " + String(rows)); PSPRINT(" segmentsize:" + String(cols) + " x " + String(rows));
PSPRINT(" request numparticles:" + String(numparticles)); PSPRINT(" request numparticles:" + String(numparticles));
uint32_t numsources = calculateNumberOfSources2D(pixels, requestedsources); uint32_t numsources = calculateNumberOfSources2D(pixels, requestedsources);
if (!allocateParticleSystemMemory2D(numparticles, numsources, advanced, sizecontrol, additionalbytes)) bool allocsuccess = false;
{ while(numparticles >= 4) { // make sure we have at least 4 particles or quit
DEBUG_PRINT(F("PS init failed: memory depleted")); if (allocateParticleSystemMemory2D(numparticles, numsources, advanced, sizecontrol, additionalbytes)) {
return false; PSPRINTLN(F("PS 2D alloc succeeded"));
allocsuccess = true;
break; // allocation succeeded
}
numparticles = ((numparticles / 2) + 3) & ~0x03; // cut number of particles in half and try again, must be 4 byte aligned
PSPRINTLN(F("PS 2D alloc failed, trying with less particles..."));
}
if (!allocsuccess) {
PSPRINTLN(F("PS 2D alloc failed, not enough memory!"));
return false; // allocation failed
} }
PartSys = new (SEGENV.data) ParticleSystem2D(cols, rows, numparticles, numsources, advanced, sizecontrol); // particle system constructor PartSys = new (SEGENV.data) ParticleSystem2D(cols, rows, numparticles, numsources, advanced, sizecontrol); // particle system constructor
@@ -1457,7 +1452,7 @@ void ParticleSystem1D::render() {
if (advPartProps) { //saturation is advanced property in 1D system if (advPartProps) { //saturation is advanced property in 1D system
if (advPartProps[i].sat < 255) { if (advPartProps[i].sat < 255) {
CHSV baseHSV = rgb2hsv_approximate(baseRGB); // convert to HSV CHSV baseHSV = rgb2hsv_approximate(baseRGB); // convert to HSV
baseHSV.s = advPartProps[i].sat; baseHSV.s = min(baseHSV.s, advPartProps[i].sat); // set the saturation but don't increase it
hsv2rgb_spectrum(baseHSV, baseRGB); // convert back to RGB hsv2rgb_spectrum(baseHSV, baseRGB); // convert back to RGB
} }
} }
@@ -1775,18 +1770,12 @@ void ParticleSystem1D::updatePSpointers(bool isadvanced) {
//non class functions to use for initialization, fraction is uint8_t: 255 means 100% //non class functions to use for initialization, fraction is uint8_t: 255 means 100%
uint32_t calculateNumberOfParticles1D(const uint32_t fraction, const bool isadvanced) { uint32_t calculateNumberOfParticles1D(const uint32_t fraction, const bool isadvanced) {
uint32_t numberofParticles = SEGMENT.virtualLength(); // one particle per pixel (if possible) uint32_t numberofParticles = SEGMENT.virtualLength(); // one particle per pixel (if possible)
#ifdef ESP8266 uint32_t particlelimit = MAXPARTICLES_1D; // maximum number of paticles allowed
uint32_t particlelimit = ESP8266_MAXPARTICLES_1D; // maximum number of paticles allowed
#elif ARDUINO_ARCH_ESP32S2
uint32_t particlelimit = ESP32S2_MAXPARTICLES_1D; // maximum number of paticles allowed
#else
uint32_t particlelimit = ESP32_MAXPARTICLES_1D; // maximum number of paticles allowed
#endif
numberofParticles = min(numberofParticles, particlelimit); // limit to particlelimit numberofParticles = min(numberofParticles, particlelimit); // limit to particlelimit
if (isadvanced) // advanced property array needs ram, reduce number of particles to use the same amount if (isadvanced) // advanced property array needs ram, reduce number of particles to use the same amount
numberofParticles = (numberofParticles * sizeof(PSparticle1D)) / (sizeof(PSparticle1D) + sizeof(PSadvancedParticle1D)); numberofParticles = (numberofParticles * sizeof(PSparticle1D)) / (sizeof(PSparticle1D) + sizeof(PSadvancedParticle1D));
numberofParticles = (numberofParticles * (fraction + 1)) >> 8; // calculate fraction of particles numberofParticles = (numberofParticles * (fraction + 1)) >> 8; // calculate fraction of particles
numberofParticles = numberofParticles < 20 ? 20 : numberofParticles; // 20 minimum numberofParticles = numberofParticles < 10 ? 10 : numberofParticles; // 10 minimum
//make sure it is a multiple of 4 for proper memory alignment (easier than using padding bytes) //make sure it is a multiple of 4 for proper memory alignment (easier than using padding bytes)
numberofParticles = (numberofParticles+3) & ~0x03; // note: with a separate particle buffer, this is probably unnecessary numberofParticles = (numberofParticles+3) & ~0x03; // note: with a separate particle buffer, this is probably unnecessary
PSPRINTLN(" calc numparticles:" + String(numberofParticles)); PSPRINTLN(" calc numparticles:" + String(numberofParticles));
@@ -1794,13 +1783,7 @@ uint32_t calculateNumberOfParticles1D(const uint32_t fraction, const bool isadva
} }
uint32_t calculateNumberOfSources1D(const uint32_t requestedsources) { uint32_t calculateNumberOfSources1D(const uint32_t requestedsources) {
#ifdef ESP8266 int numberofSources = max(1, min((int)requestedsources, MAXSOURCES_1D)); // limit
int numberofSources = max(1, min((int)requestedsources,ESP8266_MAXSOURCES_1D)); // limit
#elif ARDUINO_ARCH_ESP32S2
int numberofSources = max(1, min((int)requestedsources, ESP32S2_MAXSOURCES_1D)); // limit
#else
int numberofSources = max(1, min((int)requestedsources, ESP32_MAXSOURCES_1D)); // limit
#endif
// make sure it is a multiple of 4 for proper memory alignment (so minimum is acutally 4) // make sure it is a multiple of 4 for proper memory alignment (so minimum is acutally 4)
numberofSources = (numberofSources+3) & ~0x03; numberofSources = (numberofSources+3) & ~0x03;
return numberofSources; return numberofSources;
@@ -1828,9 +1811,19 @@ bool initParticleSystem1D(ParticleSystem1D *&PartSys, const uint32_t requestedso
if (SEGLEN == 1) return false; // single pixel not supported if (SEGLEN == 1) return false; // single pixel not supported
uint32_t numparticles = calculateNumberOfParticles1D(fractionofparticles, advanced); uint32_t numparticles = calculateNumberOfParticles1D(fractionofparticles, advanced);
uint32_t numsources = calculateNumberOfSources1D(requestedsources); uint32_t numsources = calculateNumberOfSources1D(requestedsources);
if (!allocateParticleSystemMemory1D(numparticles, numsources, advanced, additionalbytes)) { bool allocsuccess = false;
DEBUG_PRINT(F("PS init failed: memory depleted")); while(numparticles >= 10) { // make sure we have at least 10 particles or quit
return false; if (allocateParticleSystemMemory1D(numparticles, numsources, advanced, additionalbytes)) {
PSPRINT(F("PS 1D alloc succeeded"));
allocsuccess = true;
break; // allocation succeeded
}
numparticles = ((numparticles / 2) + 3) & ~0x03; // cut number of particles in half and try again, must be 4 byte aligned
PSPRINTLN(F("PS 1D alloc failed, trying with less particles..."));
}
if (!allocsuccess) {
PSPRINTLN(F("PS init failed: memory depleted"));
return false; // allocation failed
} }
PartSys = new (SEGENV.data) ParticleSystem1D(SEGMENT.virtualLength(), numparticles, numsources, advanced); // particle system constructor PartSys = new (SEGENV.data) ParticleSystem1D(SEGMENT.virtualLength(), numparticles, numsources, advanced); // particle system constructor
return true; return true;

View File

@@ -37,13 +37,20 @@ static inline int32_t limitSpeed(const int32_t speed) {
#endif #endif
#ifndef WLED_DISABLE_PARTICLESYSTEM2D #ifndef WLED_DISABLE_PARTICLESYSTEM2D
// memory allocation // memory allocation (based on reasonable segment size and available FX memory)
#define ESP8266_MAXPARTICLES 256 // enough up to 16x16 pixels #ifdef ESP8266
#define ESP8266_MAXSOURCES 24 #define MAXPARTICLES_2D 256
#define ESP32S2_MAXPARTICLES 1024 // enough up to 32x32 pixels #define MAXSOURCES_2D 24
#define ESP32S2_MAXSOURCES 64 #define SOURCEREDUCTIONFACTOR 8
#define ESP32_MAXPARTICLES 2048 // enough up to 64x32 pixels #elif ARDUINO_ARCH_ESP32S2
#define ESP32_MAXSOURCES 128 #define MAXPARTICLES_2D 1024
#define MAXSOURCES_2D 64
#define SOURCEREDUCTIONFACTOR 6
#else
#define MAXPARTICLES_2D 2048
#define MAXSOURCES_2D 128
#define SOURCEREDUCTIONFACTOR 4
#endif
// particle dimensions (subpixel division) // particle dimensions (subpixel division)
#define PS_P_RADIUS 64 // subpixel size, each pixel is divided by this for particle movement (must be a power of 2) #define PS_P_RADIUS 64 // subpixel size, each pixel is divided by this for particle movement (must be a power of 2)
@@ -232,12 +239,16 @@ bool allocateParticleSystemMemory2D(const uint32_t numparticles, const uint32_t
//////////////////////// ////////////////////////
#ifndef WLED_DISABLE_PARTICLESYSTEM1D #ifndef WLED_DISABLE_PARTICLESYSTEM1D
// memory allocation // memory allocation
#define ESP8266_MAXPARTICLES_1D 320 #ifdef ESP8266
#define ESP8266_MAXSOURCES_1D 16 #define MAXPARTICLES_1D 320
#define ESP32S2_MAXPARTICLES_1D 1300 #define MAXSOURCES_1D 16
#define ESP32S2_MAXSOURCES_1D 32 #elif ARDUINO_ARCH_ESP32S2
#define ESP32_MAXPARTICLES_1D 2600 #define MAXPARTICLES_1D 1300
#define ESP32_MAXSOURCES_1D 64 #define MAXSOURCES_1D 32
#else
#define MAXPARTICLES_1D 2600
#define MAXSOURCES_1D 64
#endif
// particle dimensions (subpixel division) // particle dimensions (subpixel division)
#define PS_P_RADIUS_1D 32 // subpixel size, each pixel is divided by this for particle movement, if this value is changed, also change the shift defines (next two lines) #define PS_P_RADIUS_1D 32 // subpixel size, each pixel is divided by this for particle movement, if this value is changed, also change the shift defines (next two lines)