FastLED 3.10.8
Loading...
Searching...
No Matches
chasing_spirals.cpp.hpp
Go to the documentation of this file.
1// Chasing Spirals — three implementations (Float, Q31 scalar, Q31 SIMD).
2//
3// All variants share setupChasingSpiralFrame() which builds a per-pixel SoA
4// geometry cache (base_angle, dist_scaled, radial filters, pixel_idx) and
5// a Perlin fade LUT. Per-frame constants (center, linear/radial offsets)
6// are computed once and passed via FrameSetup.
7//
8// Q31 scalar: Batches 3 channel sincos into one sincos32_simd call per pixel,
9// then evaluates Perlin noise and radial filter per channel.
10// Q31 SIMD: Processes 4 pixels at a time with full SIMD pipeline (aligned
11// SoA loads → sincos32_simd → Perlin → clamp/scale → scatter).
12// Perlin exits to scalar per-lane (SSE2 has no integer gather).
13
14#include "fl/stl/align.h"
21#include "fl/math/simd.h"
22#include "fl/math/sin32_simd.h"
24#include "fl/stl/noexcept.h"
25
28
29namespace fl {
30
31namespace {
32
33using FP = fl::s16x16;
35
36// Common setup values returned by setupChasingSpiralFrame.
37// Carries raw SoA pointers (no PixelLUT AoS struct).
38struct FrameSetup {
40 const fl::i32 *base_angle;
41 const fl::i32 *dist_scaled;
42 const fl::i32 *rf3;
43 const fl::i32 *rf_half;
44 const fl::i32 *rf_quarter;
45 const fl::u16 *pixel_idx;
46 const fl::i32 *fade_lut;
47 const fl::u8 *perm;
48 fl::i32 cx_raw;
49 fl::i32 cy_raw;
50 fl::i32 lin0_raw;
51 fl::i32 lin1_raw;
52 fl::i32 lin2_raw;
53 fl::i32 rad0_raw;
54 fl::i32 rad1_raw;
55 fl::i32 rad2_raw;
57};
58
59// Convert s16x16 angle (radians) to A24 format for sincos32
60FASTLED_FORCE_INLINE u32 radiansToA24(i32 base_s16x16, i32 offset_s16x16) FL_NO_EXCEPT {
61 constexpr i32 RAD_TO_A24 = 2670177;
62 return static_cast<u32>((static_cast<i64>(base_s16x16 + offset_s16x16) * RAD_TO_A24) >> FP::FRAC_BITS);
63}
64
65// Compute Perlin coordinate from sincos result and distance
66FASTLED_FORCE_INLINE i32 perlinCoord(i32 sc_val, i32 dist_raw, i32 offset) FL_NO_EXCEPT {
67 return offset - static_cast<i32>((static_cast<i64>(sc_val) * dist_raw) >> 31);
68}
69
70// Clamp s16x16 value to [0, 1] and scale to [0, 255]
72 constexpr i32 FP_ONE = static_cast<i32>(1) << FP::FRAC_BITS;
73 if (raw_s16x16 < 0) raw_s16x16 = 0;
74 if (raw_s16x16 > FP_ONE) raw_s16x16 = FP_ONE;
75 return (raw_s16x16 << 8) - raw_s16x16;
76}
77
78// Apply radial filter to noise value and clamp to [0, 255]
79FASTLED_FORCE_INLINE i32 applyRadialFilter(i32 noise_255, i32 rf_raw) FL_NO_EXCEPT {
80 i32 result = static_cast<i32>((static_cast<i64>(noise_255) * rf_raw) >> (FP::FRAC_BITS * 2));
81 if (result < 0) result = 0;
82 if (result > 255) result = 255;
83 return result;
84}
85
86// Load 4 aligned i32 values from an SoA array into a SIMD register.
87//
88// FrameSetup pointers already carry assume_aligned<16> from setupChasingSpiralFrame(),
89// but reinterpret_cast<const u32*> strips the compiler's alignment metadata.
90// Re-asserting assume_aligned<16> here restores the hint so the compiler can
91// emit aligned SIMD loads (e.g. movaps/movdqa on x86) instead of unaligned ones
92// (movups/movdqu), which avoids a micro-op penalty on older cores and removes
93// a redundant alignment check on modern ones.
95 return simd::load_u32_4_aligned(
96 fl::assume_aligned<16>(reinterpret_cast<const u32*>(arr + i))); // ok reinterpret cast
97}
98
99// Write one pixel from per-channel SIMD registers at the given lane.
102 leds[idx] = CRGB(static_cast<u8>(simd::extract_u32_4(r, lane)),
103 static_cast<u8>(simd::extract_u32_4(g, lane)),
104 static_cast<u8>(simd::extract_u32_4(b, lane)));
105}
106
107// Process one color channel for 4 pixels using a full SIMD pipeline.
108// Returns 4 clamped [0, 255] channel values.
110 simd::simd_u32x4 base_vec, simd::simd_u32x4 dist_vec,
111 i32 radial_offset, i32 linear_offset,
112 const i32 *fade_lut, const u8 *perm, i32 cx_raw, i32 cy_raw,
114
115 constexpr i32 RAD_TO_A24 = 2670177;
116
117 // Angle conversion: Q16.16 → A24
118 auto offset_vec = simd::set1_u32_4(static_cast<u32>(radial_offset));
119 auto sum_vec = simd::add_i32_4(base_vec, offset_vec);
120 auto rad_const_vec = simd::set1_u32_4(static_cast<u32>(RAD_TO_A24));
121 auto angles_vec = simd::mulhi_su32_4(sum_vec, rad_const_vec);
122
123 SinCos32_simd sc = sincos32_simd(angles_vec);
124
125 // Perlin coordinates: nx = lin+cx - cos*dist, ny = cy - sin*dist
126 auto lin_cx = simd::set1_u32_4(static_cast<u32>(linear_offset + cx_raw));
127 auto cy_vec = simd::set1_u32_4(static_cast<u32>(cy_raw));
128 auto nx_vec = simd::sub_i32_4(lin_cx,
129 simd::sll_u32_4(simd::mulhi32_i32_4(sc.cos_vals, dist_vec), 1));
130 auto ny_vec = simd::sub_i32_4(cy_vec,
131 simd::sll_u32_4(simd::mulhi32_i32_4(sc.sin_vals, dist_vec), 1));
132
133 // Perlin noise (SIMD floor/frac/wrap, scalar fade/perm/grad/lerp per lane)
135 nx_vec, ny_vec, fade_lut, perm);
136
137 // Clamp [0, FP_ONE], scale ×255, apply radial filter, clamp [0, 255]
138 auto zero = simd::set1_u32_4(0u);
139 auto fp_one = simd::set1_u32_4(static_cast<u32>(static_cast<i32>(1) << FP::FRAC_BITS));
140 auto clamped = simd::min_i32_4(simd::max_i32_4(raw_vec, zero), fp_one);
141 auto noise_scaled = simd::sub_i32_4(simd::sll_u32_4(clamped, 8), clamped);
142
143 auto max255 = simd::set1_u32_4(255u);
144 auto result = simd::mulhi32_i32_4(noise_scaled, rf_vec);
145 return simd::min_i32_4(simd::max_i32_4(result, zero), max255);
146}
147
148// Extract common frame setup logic shared by all variants.
149// Builds SoA geometry cache lazily (once when grid size changes).
150// state is the caller's per-instance ChasingSpiralState member (not a global).
152 auto *e = ctx.mEngine.get();
153 e->get_ready();
154
155 // Timing (once per frame, float is fine here)
156 e->timings.master_speed = 0.01;
157 e->timings.ratio[0] = 0.1;
158 e->timings.ratio[1] = 0.13;
159 e->timings.ratio[2] = 0.16;
160 e->timings.offset[1] = 10;
161 e->timings.offset[2] = 20;
162 e->timings.offset[3] = 30;
163 e->calculate_oscillators(e->timings);
164
165 const int num_x = e->num_x;
166 const int num_y = e->num_y;
167 const int total_pixels = num_x * num_y;
168
169 // Per-frame constants (float->FP boundary conversions)
170 constexpr FP scale(0.1f);
171 const FP radius_fp(e->radial_filter_radius);
172 const FP center_x_scaled = FP(e->animation.center_x * 0.1f);
173 const FP center_y_scaled = FP(e->animation.center_y * 0.1f);
174
175 const FP radial0(e->move.radial[0]);
176 const FP radial1(e->move.radial[1]);
177 const FP radial2(e->move.radial[2]);
178
179 // Reduce linear offsets modulo the Perlin noise period before converting
180 // to s16x16. Two reasons:
181 // 1. Prevents s16x16 overflow (range ±32767 in integer part).
182 // 2. Float32 precision fix: matches the same reduction applied in
183 // Chasing_Spirals_Float (animartrix v1 and v2 float paths) so both
184 // paths compute identical Perlin coordinates at all time values.
185 // Without this reduction, float32 loses per-pixel coordinate precision
186 // when move.linear grows large (ULP at 200,000 ≈ 0.024 > pixel step 0.1).
187 // Perlin noise is exactly periodic with period 256 at integer coordinates,
188 // so with scale_x=0.1 the effective period for offset_x is 256/0.1 = 2560.
189 // See: tests/fl/fx/2d/animartrix2.cpp "period reduction" test cases.
190 constexpr float perlin_period = 2560.0f; // 256.0f / scale_x(0.1f)
191 constexpr float scale_f = 0.1f;
192 const FP linear0_scaled = FP(fmodf(e->move.linear[0], perlin_period) * scale_f);
193 const FP linear1_scaled = FP(fmodf(e->move.linear[1], perlin_period) * scale_f);
194 const FP linear2_scaled = FP(fmodf(e->move.linear[2], perlin_period) * scale_f);
195
196 constexpr FP three_fp(3.0f);
197 constexpr FP one(1.0f);
198
199 // Build per-pixel SoA geometry (once when grid size changes)
200 if (state.count != total_pixels) {
201 const int padded = (total_pixels + 3) & ~3; // multiple of 4 for SIMD safety
202 state.base_angle.resize(padded, 0);
203 state.dist_scaled.resize(padded, 0);
204 state.rf3.resize(padded, 0);
205 state.rf_half.resize(padded, 0);
206 state.rf_quarter.resize(padded, 0);
207 state.pixel_idx.resize(padded, 0);
208
209 const FP inv_radius = one / radius_fp;
210 const FP one_third = one / three_fp;
211 int idx = 0;
212 for (int x = 0; x < num_x; x++) {
213 for (int y = 0; y < num_y; y++) {
214 const FP theta(e->polar_theta[x][y]);
215 const FP dist(e->distance[x][y]);
216 const FP rf = (radius_fp - dist) * inv_radius;
217 state.base_angle[idx] = (three_fp * theta - dist * one_third).raw();
218 state.dist_scaled[idx] = (dist * scale).raw();
219 state.rf3[idx] = (three_fp * rf).raw();
220 state.rf_half[idx] = (rf >> 1).raw();
221 state.rf_quarter[idx] = (rf >> 2).raw();
222 state.pixel_idx[idx] = e->mCtx->xyMapFn(x, y, e->mCtx->xyMapUserData);
223 idx++;
224 }
225 }
226 state.count = total_pixels;
227 }
228
229 // Initialize Perlin fade LUT once per state lifetime
230 if (!state.fade_lut_initialized) {
232 state.fade_lut_initialized = true;
233 }
234
235 const i32 cx_raw = center_x_scaled.raw();
236 const i32 cy_raw = center_y_scaled.raw();
237 const i32 lin0_raw = linear0_scaled.raw();
238 const i32 lin1_raw = linear1_scaled.raw();
239 const i32 lin2_raw = linear2_scaled.raw();
240 const i32 rad0_raw = radial0.raw();
241 const i32 rad1_raw = radial1.raw();
242 const i32 rad2_raw = radial2.raw();
243
244 // Stamp alignment on SoA pointers at the source so every downstream
245 // consumer (Q31 scalar loop, SIMD 4-wide loop, loadAligned helper)
246 // inherits the hint without needing per-site annotations.
247 //
248 // Why this matters for performance:
249 // 1. The SIMD path calls loadAligned() which feeds load_u32_4_aligned().
250 // With the alignment hint the compiler emits movdqa/movaps (aligned
251 // 128-bit loads) instead of movdqu/movups (unaligned). On older x86
252 // (pre-Nehalem) unaligned loads are significantly slower; on modern
253 // cores they still cost an extra micro-op when the address crosses a
254 // cache-line boundary.
255 // 2. The Q31 scalar path benefits too: the compiler can widen scalar
256 // i32 loads into SIMD gathers or auto-vectorize more aggressively
257 // when it knows the base pointer is 16-byte aligned.
258 // 3. fade_lut (256-entry i32 Perlin fade table) is accessed in every
259 // Perlin noise evaluation; the alignment hint lets the compiler
260 // assume cache-line-friendly access patterns.
261 //
262 // The underlying SoA arrays are allocated with FL_ALIGNAS(16) in
263 // ChasingSpiralState, so this is a promise (not a request).
264 // pixel_idx is u16 (2 bytes) and not SIMD-loaded, so no hint needed.
265 return FrameSetup{
266 total_pixels,
267 fl::assume_aligned<16>(state.base_angle.data()),
268 fl::assume_aligned<16>(state.dist_scaled.data()),
269 fl::assume_aligned<16>(state.rf3.data()),
270 fl::assume_aligned<16>(state.rf_half.data()),
271 fl::assume_aligned<16>(state.rf_quarter.data()),
272 state.pixel_idx.data(),
274 PERLIN_NOISE,
275 cx_raw,
276 cy_raw,
277 lin0_raw,
278 lin1_raw,
279 lin2_raw,
280 rad0_raw,
281 rad1_raw,
282 rad2_raw,
283 e->mCtx->leds
284 };
285}
286
287} // anonymous namespace
288
289// ============================================================================
290// Float Implementation (original algorithm, uses v2 Engine)
291// ============================================================================
292
294 auto *e = ctx.mEngine.get();
295 e->get_ready();
296
297 // Perlin noise is periodic with period 256 at integer coordinates.
298 // scale_x = 0.1, so the effective period for offset_x is 256/0.1 = 2560.
299 // Reducing move.linear[i] modulo this period before use keeps float32
300 // coordinate arithmetic precise even at extreme uptime values.
301 // Without this, float32 loses per-pixel precision when adding a small
302 // per-pixel term (~0.1) to a large offset (e.g. 200,000), since float32
303 // ULP at that magnitude (~0.024) is coarser than the per-pixel step.
304 // This matches the reduction already applied in setupChasingSpiralFrame
305 // for the Q31 path, keeping both paths in agreement at all time values.
306 static constexpr float perlin_period = 2560.0f; // 256.0f / scale_x(0.1f)
307
308 e->timings.master_speed = 0.01;
309 e->timings.ratio[0] = 0.1;
310 e->timings.ratio[1] = 0.13;
311 e->timings.ratio[2] = 0.16;
312 e->timings.offset[1] = 10;
313 e->timings.offset[2] = 20;
314 e->timings.offset[3] = 30;
315 e->calculate_oscillators(e->timings);
316
317 for (int x = 0; x < e->num_x; x++) {
318 for (int y = 0; y < e->num_y; y++) {
319 e->animation.angle =
320 3 * e->polar_theta[x][y] + e->move.radial[0] -
321 e->distance[x][y] / 3;
322 e->animation.dist = e->distance[x][y];
323 e->animation.scale_z = 0.1;
324 e->animation.scale_y = 0.1;
325 e->animation.scale_x = 0.1;
326 e->animation.offset_x = fl::fmodf(e->move.linear[0], perlin_period);
327 e->animation.offset_y = 0;
328 e->animation.offset_z = 0;
329 e->animation.z = 0;
330 float show1 = e->render_value(e->animation);
331
332 e->animation.angle =
333 3 * e->polar_theta[x][y] + e->move.radial[1] -
334 e->distance[x][y] / 3;
335 e->animation.dist = e->distance[x][y];
336 e->animation.offset_x = fl::fmodf(e->move.linear[1], perlin_period);
337 float show2 = e->render_value(e->animation);
338
339 e->animation.angle =
340 3 * e->polar_theta[x][y] + e->move.radial[2] -
341 e->distance[x][y] / 3;
342 e->animation.dist = e->distance[x][y];
343 e->animation.offset_x = fl::fmodf(e->move.linear[2], perlin_period);
344 float show3 = e->render_value(e->animation);
345
346 float radius = e->radial_filter_radius;
347 float radial_filter = (radius - e->distance[x][y]) / radius;
348
349 e->pixel.red = 3 * show1 * radial_filter;
350 e->pixel.green = show2 * radial_filter / 2;
351 e->pixel.blue = show3 * radial_filter / 4;
352
353 e->pixel = e->rgb_sanity_check(e->pixel);
354 e->setPixelColorInternal(x, y, e->pixel);
355 }
356 }
357}
358
359// ============================================================================
360// Q31 Scalar Implementation (fixed-point, non-vectorized)
361// ============================================================================
362
364 auto setup = setupChasingSpiralFrame(ctx, mState);
365 const int total_pixels = setup.total_pixels;
366 const i32 *fade_lut = setup.fade_lut;
367 const u8 *perm = setup.perm;
368 const i32 cx_raw = setup.cx_raw;
369 const i32 cy_raw = setup.cy_raw;
370 const i32 lin0_raw = setup.lin0_raw;
371 const i32 lin1_raw = setup.lin1_raw;
372 const i32 lin2_raw = setup.lin2_raw;
373 const i32 rad0_raw = setup.rad0_raw;
374 const i32 rad1_raw = setup.rad1_raw;
375 const i32 rad2_raw = setup.rad2_raw;
377
378 // Compute one noise channel from a batched SinCos32_simd result.
379 auto noise_channel = [&](const SinCos32_simd &sc, int lane,
380 i32 lin_raw, i32 dist_raw) -> i32 {
381 i32 cos_v = static_cast<i32>(simd::extract_u32_4(sc.cos_vals, lane));
382 i32 sin_v = static_cast<i32>(simd::extract_u32_4(sc.sin_vals, lane));
383 i32 nx = perlinCoord(cos_v, dist_raw, lin_raw + cx_raw);
384 i32 ny = perlinCoord(sin_v, dist_raw, cy_raw);
385 return clampAndScale255(Perlin::pnoise2d_raw(nx, ny, fade_lut, perm));
386 };
387
388 for (int i = 0; i < total_pixels; i++) {
389 const i32 base_raw = setup.base_angle[i];
390 const i32 dist_raw = setup.dist_scaled[i];
391
392 // Batch all 3 channel sincos into one SIMD call (4th lane unused)
393 simd::simd_u32x4 angles = simd::set_u32_4(
394 radiansToA24(base_raw, rad0_raw),
395 radiansToA24(base_raw, rad1_raw),
396 radiansToA24(base_raw, rad2_raw), 0);
397 SinCos32_simd sc = sincos32_simd(angles);
398
399 i32 s0 = noise_channel(sc, 0, lin0_raw, dist_raw);
400 i32 s1 = noise_channel(sc, 1, lin1_raw, dist_raw);
401 i32 s2 = noise_channel(sc, 2, lin2_raw, dist_raw);
402
403 i32 r = applyRadialFilter(s0, setup.rf3[i]);
404 i32 g = applyRadialFilter(s1, setup.rf_half[i]);
405 i32 b = applyRadialFilter(s2, setup.rf_quarter[i]);
406
407 leds[setup.pixel_idx[i]] = CRGB(static_cast<u8>(r),
408 static_cast<u8>(g),
409 static_cast<u8>(b));
410 }
411}
412
413// ============================================================================
414// SIMD Implementation (vectorized 4-wide processing)
415// ============================================================================
416
418 auto setup = setupChasingSpiralFrame(ctx, mState);
419 const int total_pixels = setup.total_pixels;
420 const i32 *base_angle = setup.base_angle;
421 const i32 *dist_scaled = setup.dist_scaled;
422 const i32 *rf3_arr = setup.rf3;
423 const i32 *rf_half_arr = setup.rf_half;
424 const i32 *rf_qtr_arr = setup.rf_quarter;
425 const u16 *pixel_idx = setup.pixel_idx;
426 const i32 *fade_lut = setup.fade_lut;
427 const u8 *perm = setup.perm;
428 const i32 cx_raw = setup.cx_raw;
429 const i32 cy_raw = setup.cy_raw;
430 const i32 lin0_raw = setup.lin0_raw;
431 const i32 lin1_raw = setup.lin1_raw;
432 const i32 lin2_raw = setup.lin2_raw;
433 const i32 rad0_raw = setup.rad0_raw;
434 const i32 rad1_raw = setup.rad1_raw;
435 const i32 rad2_raw = setup.rad2_raw;
437
438 // SIMD pixel pipeline: process 4 pixels per iteration
439 int i = 0;
440 for (; i + 3 < total_pixels; i += 4) {
441 // Aligned SoA loads (arrays are FL_ALIGNAS(16), stride is 4)
442 auto base_vec = loadAligned(base_angle, i);
443 auto dist_vec = loadAligned(dist_scaled, i);
444 auto rf3_vec = loadAligned(rf3_arr, i);
445 auto rf_half_vec = loadAligned(rf_half_arr, i);
446 auto rf_qtr_vec = loadAligned(rf_qtr_arr, i);
447
448 auto r_vec = simd4_processChannel(
449 base_vec, dist_vec, rad0_raw, lin0_raw, fade_lut, perm, cx_raw, cy_raw, rf3_vec);
450 auto g_vec = simd4_processChannel(
451 base_vec, dist_vec, rad1_raw, lin1_raw, fade_lut, perm, cx_raw, cy_raw, rf_half_vec);
452 auto b_vec = simd4_processChannel(
453 base_vec, dist_vec, rad2_raw, lin2_raw, fade_lut, perm, cx_raw, cy_raw, rf_qtr_vec);
454
455 // Scatter to LED array (pixel_idx holds arbitrary xyMap-remapped indices)
456 scatterPixel(leds, pixel_idx[i+0], r_vec, g_vec, b_vec, 0);
457 scatterPixel(leds, pixel_idx[i+1], r_vec, g_vec, b_vec, 1);
458 scatterPixel(leds, pixel_idx[i+2], r_vec, g_vec, b_vec, 2);
459 scatterPixel(leds, pixel_idx[i+3], r_vec, g_vec, b_vec, 3);
460 }
461
462 // Scalar fallback for remaining pixels (when total_pixels % 4 != 0)
463 for (; i < total_pixels; i++) {
464 const i32 base_raw = base_angle[i];
465 const i32 dist_raw = dist_scaled[i];
466
467 auto noise_ch = [&](i32 rad_raw, i32 lin_raw) -> i32 {
468 u32 a24 = radiansToA24(base_raw, rad_raw);
469 SinCos32 sc = sincos32(a24);
470 i32 nx = perlinCoord(sc.cos_val, dist_raw, lin_raw + cx_raw);
471 i32 ny = perlinCoord(sc.sin_val, dist_raw, cy_raw);
472 i32 raw = Perlin::pnoise2d_raw(nx, ny, fade_lut, perm);
473 return clampAndScale255(raw);
474 };
475
476 i32 s0 = noise_ch(rad0_raw, lin0_raw);
477 i32 s1 = noise_ch(rad1_raw, lin1_raw);
478 i32 s2 = noise_ch(rad2_raw, lin2_raw);
479
480 i32 r = applyRadialFilter(s0, rf3_arr[i]);
481 i32 g = applyRadialFilter(s1, rf_half_arr[i]);
482 i32 b = applyRadialFilter(s2, rf_qtr_arr[i]);
483
484 leds[pixel_idx[i]] = CRGB(static_cast<u8>(r), static_cast<u8>(g), static_cast<u8>(b));
485 }
486}
487
488} // namespace fl
489
CRGB leds[1]
void setup()
TestState state
fl::UISlider scale("Scale", 4,.1, 4,.1)
Alignment macros and utilities for FastLED.
void draw(Context &ctx) FL_NO_EXCEPT override
void draw(Context &ctx) FL_NO_EXCEPT override
ChasingSpiralState mState
void draw(Context &ctx) FL_NO_EXCEPT override
static constexpr int FRAC_BITS
Definition s16x16.h:24
constexpr i32 raw() const FL_NO_EXCEPT
Definition s16x16.h:62
#define FL_OPTIMIZATION_LEVEL_O3_BEGIN
#define FASTLED_FORCE_INLINE
#define FL_FAST_MATH_BEGIN
#define FL_FAST_MATH_END
#define FL_OPTIMIZATION_LEVEL_O3_END
fl::UISlider offset("Offset", 0.0f, 0.0f, 1.0f, 0.01f)
FASTLED_FORCE_INLINE void scatterPixel(fl::span< CRGB > leds, u16 idx, simd::simd_u32x4 r, simd::simd_u32x4 g, simd::simd_u32x4 b, int lane) FL_NO_EXCEPT
FASTLED_FORCE_INLINE u32 radiansToA24(i32 base_s16x16, i32 offset_s16x16) FL_NO_EXCEPT
simd::simd_u32x4 simd4_processChannel(simd::simd_u32x4 base_vec, simd::simd_u32x4 dist_vec, i32 radial_offset, i32 linear_offset, const i32 *fade_lut, const u8 *perm, i32 cx_raw, i32 cy_raw, simd::simd_u32x4 rf_vec) FL_NO_EXCEPT
FrameSetup setupChasingSpiralFrame(Context &ctx, ChasingSpiralState &state) FL_NO_EXCEPT
FASTLED_FORCE_INLINE i32 applyRadialFilter(i32 noise_255, i32 rf_raw) FL_NO_EXCEPT
FASTLED_FORCE_INLINE i32 perlinCoord(i32 sc_val, i32 dist_raw, i32 offset) FL_NO_EXCEPT
FASTLED_FORCE_INLINE simd::simd_u32x4 loadAligned(const i32 *arr, int i) FL_NO_EXCEPT
FASTLED_FORCE_INLINE i32 clampAndScale255(i32 raw_s16x16) FL_NO_EXCEPT
T * assume_aligned(T *ptr) FL_NO_EXCEPT
Definition s16x16x4.h:126
platforms::simd_u32x4 simd_u32x4
Definition types.h:26
unsigned char u8
Definition stdint.h:131
fl::CRGB CRGB
Definition video.h:18
u8 u8 fract8 blur_amount FL_NO_EXCEPT
Definition blur.h:186
static constexpr i32 FP_ONE
FASTLED_FORCE_INLINE SinCos32 sincos32(u32 angle) FL_NO_EXCEPT
Definition sin32.h:86
expected< T, E > result
Alias for expected (Rust-style naming)
Definition result.h:31
FASTLED_FORCE_INLINE SinCos32_simd sincos32_simd(simd::simd_u32x4 angles) FL_NO_EXCEPT
Process 4 angles simultaneously, returning vectorized sin/cos values SIMD-optimized: vectorized angle...
Definition sin32_simd.h:32
InputGamut g
Definition rgbw.h:125
float fmodf(float x, float y) FL_NO_EXCEPT
Definition math.h:344
Base definition for an LED controller.
Definition crgb.hpp:179
i32 sin_val
Definition sin32.h:23
i32 cos_val
Definition sin32.h:24
Umbrella header for SIMD subsystem.
Four-lane SIMD batch of fl::sincos32.
fl::i64 i64
Definition stdint.h:221
static fl::simd::simd_u32x4 pnoise2d_raw_simd4_vec(fl::simd::simd_u32x4 nx_vec, fl::simd::simd_u32x4 ny_vec, const fl::i32 *fade_lut, const fl::u8 *perm) FL_NO_EXCEPT
static void init_fade_lut(fl::i32 *table) FL_NO_EXCEPT