FastLED 3.10.6
Loading...
Searching...
No Matches
pipeline_binding.cpp.hpp
Go to the documentation of this file.
1// ok no header - implementation for fl/channels/pipeline_binding.h
2
4
9#include "fl/stl/new.h"
10
11namespace fl {
12
15 if (out == nullptr) {
16 return false;
17 }
18 const EmitterProfile* profile = binding.profile();
19 if (profile == nullptr) {
20 // Not an error: a channel with no profile bound stays on the legacy
21 // path, which is what `active()` being false means.
22 return false;
23 }
24 return buildStreamingPipelineQ16(binding.mSource, *profile, binding.mGamut,
25 out, binding.mHasTargetWhite
26 ? &binding.mTargetWhite : nullptr);
27}
28
29namespace {
30
31// Keep topology admission behind the installed hook: ordinary unprofiled
32// channels must not link the wide-profile checks into their hot path.
34 const ChannelOptions& options,
35 const ChipsetVariant& chipset,
37 const EmitterProfile* profile = binding.profile();
38 if (profile != nullptr) {
39 const colorimetric_response::EmitterTopology topology = profile->topology;
40 const Rgbw* rgbw = options.mWhiteCfg.ptr<Rgbw>();
41 const Rgbww* rgbww = options.mWhiteCfg.ptr<Rgbww>();
42 const bool is_rgbw = rgbw != nullptr && rgbw->active();
43 const bool is_rgbww = rgbww != nullptr && rgbww->active();
44 const bool mismatch =
46 (is_rgbw || is_rgbww)) ||
48 !is_rgbw) ||
50 !is_rgbww);
51 if (mismatch) {
52 return false;
53 }
55 // RGB-only codecs call loadAndScaleRGB[16] and cannot consume a
56 // wide solve. Reject at binding instead of reading the RGB gamut
57 // that wide pipelines do not build or silently dropping W/WW.
58 if (!chipset.is<ClocklessChipset>()) {
59 return false;
60 }
61 const ClocklessEncoder encoder =
62 chipset.ptr<ClocklessChipset>()->encoder;
63 const bool supports_rgbw =
66 const bool supports_rgbww =
70 ? !supports_rgbw : !supports_rgbww) {
71 return false;
72 }
73 }
74 }
75 return buildPipelineForBinding(binding, out);
76}
77
78} // namespace
79
80namespace {
81
87 void* source_storage, void* iterator_storage,
89 ColorPipelineFrameRef pipeline, const Rgbw& rgbw,
90 Rgbww rgbww, u8 dither_phase, u8 brightness) FL_NO_EXCEPT {
91 ColorManagedPixelSource* source = new (source_storage)
92 ColorManagedPixelSource(controller, order, pipeline, dither_phase);
93#if FL_COLOR_PIPELINE_SHARED
94 const PowerFrameHooks& hooks = powerFrameHooks();
95 if (hooks.frameFluxActive) {
97 static_cast<i32>(hooks.frameFluxQ16)));
98 } else
99#endif
100 {
101 source->setFlux(brightness);
102 }
103 return new (iterator_storage) PixelIterator(source, rgbw, rgbww);
104}
105
106void destroyColorPipelineIterator(void* source_storage,
107 void* iterator_storage) FL_NO_EXCEPT {
108 static_cast<PixelIterator*>(iterator_storage)->~PixelIterator();
109 static_cast<ColorManagedPixelSource*>(source_storage)
111}
112
113#if FL_COLOR_PIPELINE_SHARED
114void colorPipelineBuildPowerHistogram(
116 u8 emitter_count, ManagedPowerHistogram* out) FL_NO_EXCEPT {
117 if (out == nullptr) return;
118 *out = ManagedPowerHistogram();
119 out->pixels = static_cast<u32>(leds.size());
120 out->emitters = emitter_count;
121 for (fl::size i = 0; i < leds.size(); ++i) {
122 i32 light[5] = {};
123 if (pipeline.wide) {
124 processPixelWideLinearQ16(pipeline, leds[i].r, leds[i].g,
125 leds[i].b, light);
126 } else {
127 i32 rgb_light[3];
128 processPixelLinearQ16(pipeline, leds[i].r, leds[i].g,
129 leds[i].b, rgb_light);
130 for (int c = 0; c < 3; ++c) light[c] = rgb_light[c];
131 }
132 for (u8 c = 0; c < emitter_count; ++c) {
133 if (light[c] > 0) {
134 const u32 bounded = light[c] > 65536 ? 65536u :
135 static_cast<u32>(light[c]);
136 const u8 bin = static_cast<u8>((bounded - 1u) >> 10);
137 ++out->counts[c][bin];
138 }
139 }
140 }
141}
142
143u64 colorPipelineHistogramPowerNumerator(
144 const StreamingPipelineQ16& pipeline,
145 const ManagedPowerHistogram& histogram, FluxScalar flux,
146 PowerCodecPolicy codec, const u8 (&weights)[5],
147 u8 (*electrical_map)(u8)) FL_NO_EXCEPT {
148 u64 numerator = 0;
149 for (u8 c = 0; c < histogram.emitters; ++c) {
150 for (u8 bin = 0; bin < ManagedPowerHistogram::kBins; ++bin) {
151 const u32 count = histogram.counts[c][bin];
152 if (count == 0) continue;
153 const i32 upper_light = static_cast<i32>((bin + 1u) << 10);
154 const i32 drive = encodeLinearEmitterQ16(pipeline, c, upper_light,
155 flux);
156 if (drive <= 0) continue;
157 u8 mapped_upper = 255;
158 if (codec.kind == PowerCodecKind::Byte) {
159 const u32 code = (static_cast<u32>(drive) * 255u + 65535u) >> 16;
160 mapped_upper = electrical_map(static_cast<u8>(code > 255 ? 255 : code));
161 } else if (codec.kind == PowerCodecKind::Native7 ||
162 codec.kind == PowerCodecKind::Native5) {
163 const u32 max_code = codec.kind == PowerCodecKind::Native7 ? 127u : 31u;
164 const u32 native = (static_cast<u32>(drive) * max_code + 65535u) >> 16;
165 const u32 equivalent = (native * 255u + max_code - 1u) / max_code;
166 mapped_upper = electrical_map(static_cast<u8>(equivalent > 255 ? 255 : equivalent));
167 } else if (codec.kind == PowerCodecKind::FiveBit) {
168 mapped_upper = 0;
169 const u32 min_field = codec.min_field < 1 ? 1 : codec.min_field;
170 for (u32 field = min_field; field <= 31; ++field) {
171 const u64 numerator_code = static_cast<u64>(drive) * 255u * 31u;
172 const u64 denominator = static_cast<u64>(65536) * field;
173 const u32 code = static_cast<u32>((numerator_code + denominator - 1) / denominator);
174 const u8 bounded_code = static_cast<u8>(code > 255 ? 255 : code);
175 const u32 weighted = (static_cast<u32>(electrical_map(bounded_code)) *
176 field + 30u) / 31u;
177 if (weighted > mapped_upper) mapped_upper = static_cast<u8>(weighted);
178 }
179 }
180 numerator += static_cast<u64>(count) * mapped_upper * weights[c];
181 }
182 }
183 return numerator;
184}
185
186u32 colorPipelineUnscaledPowerMilliwatts(
188 u8 emitter_count, ColorPipelineHooks::PowerEstimator estimate) FL_NO_EXCEPT {
189 // Demand at full brightness: a copy of the pipeline at unity flux, so the
190 // frame's brightness and the limiter's own previous scalar are not folded
191 // into the number the limiter is about to scale.
192 StreamingPipelineQ16 unity = pipeline;
194
195 // Charge the actual solved physical emitter codes. Passing the RGB
196 // subset through the legacy RGBW estimator would extract white a second
197 // time, and RGBWW cannot be represented by its three-channel fold.
198 // Summing per chunk truncates each chunk's per-emitter total separately:
199 // under 3 mW low per 32 pixels, well inside the model's own precision.
200 enum { kChunk = 32 };
201 u8 chunk[kChunk * 5];
202 u32 total = 0;
203 fl::size filled = 0;
204 for (fl::size i = 0; i < leds.size(); ++i) {
205 i32 drives[5] = {};
206 if (pipeline.wide) {
207 processPixelWideQ16(unity, leds[i].r, leds[i].g, leds[i].b,
208 drives);
209 } else {
210 i32 rgb_drives[3];
211 processPixelQ16(unity, leds[i].r, leds[i].g, leds[i].b,
212 rgb_drives);
213 for (int c = 0; c < 3; ++c) drives[c] = rgb_drives[c];
214 }
215 for (int c = 0; c < emitter_count; ++c) {
216 i32 d = drives[c];
217 if (d < 0) {
218 d = 0;
219 }
220 if (d > 65536) {
221 d = 65536;
222 }
223 chunk[filled * emitter_count + c] =
224 static_cast<u8>((d * 255 + 32768) >> 16);
225 }
226 if (++filled == kChunk) {
227 total += estimate(span<const u8>(chunk, filled * emitter_count),
228 emitter_count);
229 filled = 0;
230 }
231 }
232 if (filled != 0) {
233 total += estimate(span<const u8>(chunk, filled * emitter_count),
234 emitter_count);
235 }
236 return total;
237}
238#endif // FL_COLOR_PIPELINE_SHARED
239
241 vector_psram<u8>* out, SpiChipset chip,
243#if !FL_PLATFORM_HAS_TINY_MEMORY
244 switch (chip) {
246 pixels.writeLPD8806Wide(fl::back_inserter(*out));
247 return true;
249 pixels.writeLPD6803Wide(fl::back_inserter(*out));
250 return true;
251 default:
252 break;
253 }
254#endif
255#if FASTLED_HD_COLOR_MIXING && !FL_PLATFORM_HAS_TINY_MEMORY
256 // Non-HD APA102/SK9822: the field is held at 31 on a managed channel,
257 // whatever FASTLED_USE_GLOBAL_BRIGHTNESS says (#4457). That option
258 // derives a strip-wide field from the first pixel and rescales only that
259 // pixel -- a second shaping stage on drives the pipeline already solved,
260 // dimming every other pixel by field/31. B1's conservative treatment is a
261 // fixed field. Through the wide writer the HD path already links, pinned
262 // at 31: same framing, one quantization from the 16-bit drive, and no new
263 // encoder instantiation in sketches that bind no profile.
264 switch (chip) {
269 pixels.writeFiveBitWide(fl::back_inserter(*out), 31);
270 return true;
271 default:
272 break;
273 }
274 // The profile is read here rather than by the caller, so the accessor
275 // links only with the hook.
276 const EmitterProfile* profile = controller.emitterProfile();
277 const u8 min_field = hdMinimumField(
278 fiveBitSemanticsFor(chip, profile != nullptr
279 ? profile->five_bit_semantics
282 if (min_field >= 31) {
283 // Field held fixed: the caller's existing path is exactly that.
284 return false;
285 }
286 pixels.writeFiveBitWide(fl::back_inserter(*out), min_field);
287 return true;
288#else
289 FL_UNUSED(pixels);
290 FL_UNUSED(out);
291 FL_UNUSED(chip);
293 return false;
294#endif
295}
296
301
302} // namespace
303
305 // Not a function-local static with a non-trivial constructor: this is a
306 // zero-initialized aggregate, so there is no guard variable and no
307 // Teensy 3.x `__cxa_guard` conflict.
308 static ColorPipelineHooks hooks = {};
309 return hooks;
310}
311
312#if FL_COLOR_PIPELINE_SHARED
313PowerFrameHooks& powerFrameHooks() FL_NO_EXCEPT {
314 static PowerFrameHooks hooks = {};
315 return hooks;
316}
317#endif
318
321 hooks.build = &buildPipelineForChannelBinding;
322 hooks.makeIterator = &makeColorPipelineIterator;
323 hooks.destroyIterator = &destroyColorPipelineIterator;
324 hooks.notifyProfileClearedByLegacy = &notifyColorPipelineProfileClearedByLegacy;
325 hooks.encodeManagedSpi = &encodeColorPipelineManagedSpi;
326#if FL_COLOR_PIPELINE_SHARED
327 hooks.unscaledPowerMilliwatts = &colorPipelineUnscaledPowerMilliwatts;
328 PowerFrameHooks& power_hooks = powerFrameHooks();
329 power_hooks.buildPowerHistogram = &colorPipelineBuildPowerHistogram;
330 power_hooks.histogramPowerNumerator = &colorPipelineHistogramPowerNumerator;
331#endif
332}
333
335 // Null unless a profile was ever bound, and nothing can have been
336 // cleared in that case either.
338 if (notify != nullptr) {
339 notify();
340 }
341}
342
343} // namespace fl
CRGB leds[1]
fl::UISlider brightness("Brightness", BRIGHTNESS, 0, 255)
CLEDController * controller
Rgbw rgbw
void setFlux(u8 brightness) FL_NO_EXCEPT
Frame-local brightness is applied to the source's owned pipeline on shared tiers; only managed iterat...
Runs processPixelQ16 over a PixelController's pixels as they are read.
static FluxScalar fromRawQ16(i32 raw) FL_NO_EXCEPT
A limiter fraction already in s16.16, clamped to [0, 1].
static FluxScalar unity() FL_NO_EXCEPT
Identity. Drives pass through unchanged.
A linear flux scalar in s16.16.
Definition flux_scalar.h:21
#define FL_UNUSED(x)
base definitions used by led controllers for writing out led data
bool buildPipelineForChannelBinding(const ColorProfileBinding &binding, const ChannelOptions &options, const ChipsetVariant &chipset, StreamingPipelineQ16 *out) FL_NO_EXCEPT
bool encodeColorPipelineManagedSpi(PixelIterator &pixels, vector_psram< u8 > *out, SpiChipset chip, const CLEDController &controller) FL_NO_EXCEPT
PixelIterator * makeColorPipelineIterator(void *source_storage, void *iterator_storage, PixelController< RGB, 1, 0xFFFFFFFF > &controller, EOrder order, ColorPipelineFrameRef pipeline, const Rgbw &rgbw, Rgbww rgbww, u8 dither_phase, u8 brightness) FL_NO_EXCEPT
Builds the managed iterator in the caller's storage.
void destroyColorPipelineIterator(void *source_storage, void *iterator_storage) FL_NO_EXCEPT
u8 & hdFieldFloor() FL_NO_EXCEPT
Backing store for CFastLED::setHdFieldFloor().
back_insert_iterator< Container > back_inserter(Container &c) FL_NO_EXCEPT
Helper function to create a back_insert_iterator.
Definition iterator.h:139
unsigned char u8
Definition stdint.h:131
ClocklessEncoder
Identifies which encoder to use for clockless chipsets in the Channel API.
@ CLOCKLESS_ENCODER_UCS7604_8BIT
UCS7604 8-bit 800KHz.
@ CLOCKLESS_ENCODER_TM1812_RGBWW
TM1812: two 5-byte RGBWW pixels in each 12-byte IC frame.
@ CLOCKLESS_ENCODER_WS2812
Default, no preamble (WS2812 and compatible)
void installColorPipelineHooks() FL_NO_EXCEPT
Point the hooks at the real implementations.
void notifyColorProfileClearedByLegacy() FL_NO_EXCEPT
void setPipelineFluxQ16(StreamingPipelineQ16 *pipeline, FluxScalar flux) FL_NO_EXCEPT
Set the composed brightness-and-power scalar for the frames that follow.
u8 hdMinimumField(FiveBitSemantics semantics, u8 floor_setting) FL_NO_EXCEPT
The lowest field the managed HD path may choose: the configured flicker floor for a secondary slow-PW...
fl::variant< ClocklessChipset, SpiChipsetConfig > ChipsetVariant
Definition config.h:157
ColorPipelineHooks & colorPipelineHooks() FL_NO_EXCEPT
The installed hooks.
void processPixelLinearQ16(const StreamingPipelineQ16 &pipeline, u8 r, u8 g, u8 b, i32(&drives)[3]) FL_NO_EXCEPT
Pre-response linear emitter light for a power prepass.
void processPixelWideLinearQ16(const StreamingPipelineQ16 &pipeline, u8 r, u8 g, u8 b, i32(&drives)[5]) FL_NO_EXCEPT
InputGamut g FL_NO_EXCEPT
Definition rgbw.h:121
bool buildPipelineForBinding(const ColorProfileBinding &binding, StreamingPipelineQ16 *out) FL_NO_EXCEPT
Build the streaming pipeline a binding describes.
EOrder
RGB color channel orderings, used when instantiating controllers to determine what order the controll...
Definition eorder.h:13
InputGamut g
Definition rgbw.h:124
bool buildStreamingPipelineQ16(const SourceProfile &source, const colorimetric_response::EmitterProfile &device, GamutPolicy policy, StreamingPipelineQ16 *out, const Chromaticity *target_white) FL_NO_EXCEPT
Bind a source declaration and a device profile into a pipeline.
void processPixelWideQ16(const StreamingPipelineQ16 &pipeline, u8 r, u8 g, u8 b, i32(&drives)[5]) FL_NO_EXCEPT
Like processPixelQ16, but solves the physical 4/5-emitter hull.
StreamingPipelineQ16 & ColorPipelineFrameRef
i32 encodeLinearEmitterQ16(const StreamingPipelineQ16 &pipeline, u8 emitter, i32 light, FluxScalar flux) FL_NO_EXCEPT
Scale one linear emitter and invert its physical response, using the identical final stage as process...
SpiChipset
LED chipsets with SPI interface (clocked protocols) Modern type-safe enum class - prefer this for new...
@ LPD6803
LPD6803 LED chipset.
@ HD107
Same as APA102, but in turbo 40-mhz mode.
@ SK9822
SK9822 LED chipset.
@ DOTSTAR
APA102 LED chipset alias.
@ APA102
APA102 LED chipset.
@ LPD8806
LPD8806 LED chipset.
FiveBitSemantics fiveBitSemanticsFor(SpiChipset chip, FiveBitSemantics profile) FL_NO_EXCEPT
The 5-bit semantics for chip, with profile overriding the chip's default when it is not NotApplicable...
void processPixelQ16(const StreamingPipelineQ16 &pipeline, u8 r, u8 g, u8 b, i32(&drives)[3]) FL_NO_EXCEPT
One pixel: an RGB8 code triple to three physical emitter drives in s16.16.
Base definition for an LED controller.
Definition crgb.hpp:179
bool(* build)(const ColorProfileBinding &, const ChannelOptions &, const ChipsetVariant &, StreamingPipelineQ16 *)
Derives a pipeline from a binding. Null until installed.
void(* destroyIterator)(void *source_storage, void *iterator_storage)
Destroys what makeIterator built. Null until installed.
void(* notifyProfileClearedByLegacy)()
Fires ChannelEvents::onColorProfileWarning(ProfileClearedByLegacy).
PixelIterator *(* makeIterator)(void *source_storage, void *iterator_storage, PixelController< RGB, 1, 0xFFFFFFFF > &controller, EOrder order, ColorPipelineFrameRef pipeline, const Rgbw &rgbw, Rgbww rgbww, u8 dither_phase, u8 brightness)
Constructs a colour-managed PixelIterator in caller-provided storage.
bool(* encodeManagedSpi)(PixelIterator &pixels, vector_psram< u8 > *out, SpiChipset chip, const CLEDController &controller)
Unscaled (full-brightness) power demand of leds as the pipeline will drive them, in mW,...
Payload shared by every color-profile notification.
Everything the per-pixel path needs, derived once when a profile binds.
Definition pipeline.h:73
fl::PixelIterator PixelIterator
#define FL_NO_EXCEPT
fl::u64 u64
Definition stdint.h:220
Pixel controller class.
fl::function_list< void(const ColorProfileEvent &)> onColorProfileWarning
static ChannelEvents & instance()
Optional channel configuration parameters All fields have sensible defaults and can be overridden as ...
Definition options.h:85
Clockless chipset configuration (single data pin)
Definition config.h:33
Immutable profile binding carried by a channel.
Per-strip RGBWW configuration.
Definition rgbww.h:60