Skip to main content

yume_pdq/kernel/
router.rs

1/*
2 * Copyright (c) 2025 Yumechi <yume@yumechi.jp>
3 *
4 * Created on Wednesday, March 26, 2025
5 * Author: Yumechi <yume@yumechi.jp>
6 *
7 * SPDX-License-Identifier: Apache-2.0
8 *
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 */
21
22use core::{
23    fmt::{Debug, Display},
24    marker::PhantomData,
25    ops::Mul,
26};
27
28use generic_array::{
29    ArrayLength, GenericArray,
30    typenum::{B0, B1, Bit, U3, U4},
31};
32
33use super::{
34    Kernel,
35    type_traits::{DivisibleBy8, EvaluateHardwareFeature, FallbackRequirements, SquareOf},
36};
37
38#[derive(Debug, Clone, Copy, PartialEq)]
39/// A token that has both a compile-time bit and a runtime-bit indicating whether a decision fell through to the fallback kernel.
40pub struct MaybeFellThroughToken<EnabledStatically: Bit> {
41    _private: PhantomData<EnabledStatically>,
42    /// Whether the decision fell through to the fallback kernel at runtime.
43    pub fell_through_runtime: bool,
44}
45
46trait KernelFallthrough<
47    EnabledStatically: Bit,
48    InputDimension: ArrayLength,
49    Buffer1WidthX: ArrayLength,
50    Buffer1LengthY: ArrayLength,
51    OutputDimension: ArrayLength + DivisibleBy8,
52    InternalFloat: num_traits::float::TotalOrder
53        + num_traits::FromPrimitive
54        + num_traits::NumAssign
55        + num_traits::bounds::Bounded
56        + num_traits::NumCast,
57>:
58    Kernel<
59        Buffer1WidthX = Buffer1WidthX,
60        Buffer1LengthY = Buffer1LengthY,
61        InputDimension = InputDimension,
62        OutputDimension = OutputDimension,
63        InternalFloat = InternalFloat,
64    >
65{
66    fn ident_opt(&self) -> MaybeFellThroughToken<EnabledStatically>;
67
68    fn would_run(&self) -> bool {
69        false
70    }
71
72    fn pdqf_t_opt<const CHECKED: bool>(
73        &mut self,
74        _input: &mut GenericArray<
75            GenericArray<Self::InternalFloat, Self::OutputDimension>,
76            Self::OutputDimension,
77        >,
78    ) -> bool {
79        false
80    }
81
82    fn pdqf_negate_alt_cols_opt<const NEGATE: bool, const CHECKED: bool>(
83        &mut self,
84        _input: &mut GenericArray<
85            GenericArray<Self::InternalFloat, Self::OutputDimension>,
86            Self::OutputDimension,
87        >,
88    ) -> bool {
89        false
90    }
91
92    fn pdqf_negate_alt_rows_opt<const NEGATE: bool, const CHECKED: bool>(
93        &mut self,
94        _input: &mut GenericArray<
95            GenericArray<Self::InternalFloat, Self::OutputDimension>,
96            Self::OutputDimension,
97        >,
98    ) -> bool {
99        false
100    }
101
102    fn pdqf_negate_off_diagonals_opt<const CHECKED: bool>(
103        &mut self,
104        _input: &mut GenericArray<
105            GenericArray<Self::InternalFloat, Self::OutputDimension>,
106            Self::OutputDimension,
107        >,
108    ) -> bool {
109        false
110    }
111
112    fn cvt_rgb8_to_luma8f_opt<
113        const CHECKED: bool,
114        const R_COEFF: u32,
115        const G_COEFF: u32,
116        const B_COEFF: u32,
117    >(
118        &mut self,
119        _input: &GenericArray<GenericArray<u8, U3>, InputDimension>,
120        _output: &mut GenericArray<f32, InputDimension>,
121    ) -> bool {
122        false
123    }
124
125    fn cvt_rgba8_to_luma8f_opt<
126        const CHECKED: bool,
127        const R_COEFF: u32,
128        const G_COEFF: u32,
129        const B_COEFF: u32,
130    >(
131        &mut self,
132        _input: &GenericArray<GenericArray<u8, U4>, InputDimension>,
133        _output: &mut GenericArray<f32, InputDimension>,
134    ) -> bool {
135        false
136    }
137
138    fn jarosz_compress_opt<const CHECKED: bool>(
139        &mut self,
140        _buffer: &GenericArray<GenericArray<f32, InputDimension>, InputDimension>,
141        _output: &mut GenericArray<GenericArray<InternalFloat, Buffer1WidthX>, Buffer1LengthY>,
142    ) -> bool {
143        false
144    }
145
146    fn quantize_opt<const CHECKED: bool>(
147        &mut self,
148        _input: &GenericArray<GenericArray<InternalFloat, OutputDimension>, OutputDimension>,
149        _threshold: &mut InternalFloat,
150        _output: &mut GenericArray<
151            GenericArray<u8, <OutputDimension as DivisibleBy8>::Output>,
152            OutputDimension,
153        >,
154    ) -> bool {
155        false
156    }
157
158    fn dct2d_opt<const CHECKED: bool>(
159        &mut self,
160        _buffer: &GenericArray<GenericArray<InternalFloat, Buffer1WidthX>, Buffer1LengthY>,
161        _tmp_row_buffer: &mut GenericArray<InternalFloat, Buffer1WidthX>,
162        _output: &mut GenericArray<GenericArray<InternalFloat, OutputDimension>, OutputDimension>,
163    ) -> bool {
164        false
165    }
166
167    fn sum_of_gradients_opt<const CHECKED: bool>(
168        &mut self,
169        _input: &GenericArray<GenericArray<InternalFloat, OutputDimension>, OutputDimension>,
170    ) -> Option<InternalFloat> {
171        None
172    }
173}
174
175impl<M: EvaluateHardwareFeature<EnabledStatic = B0>, P: Kernel<RequiredHardwareFeature = M>>
176    KernelFallthrough<
177        B0,
178        <P as Kernel>::InputDimension,
179        <P as Kernel>::Buffer1WidthX,
180        <P as Kernel>::Buffer1LengthY,
181        <P as Kernel>::OutputDimension,
182        <P as Kernel>::InternalFloat,
183    > for P
184where
185    <P as Kernel>::OutputDimension: DivisibleBy8,
186{
187    fn ident_opt(&self) -> MaybeFellThroughToken<B0> {
188        MaybeFellThroughToken {
189            _private: PhantomData,
190            fell_through_runtime: true,
191        }
192    }
193}
194
195impl<M: EvaluateHardwareFeature<EnabledStatic = B1>, P: Kernel<RequiredHardwareFeature = M>>
196    KernelFallthrough<
197        B1,
198        <P as Kernel>::InputDimension,
199        <P as Kernel>::Buffer1WidthX,
200        <P as Kernel>::Buffer1LengthY,
201        <P as Kernel>::OutputDimension,
202        <P as Kernel>::InternalFloat,
203    > for P
204where
205    <P as Kernel>::OutputDimension: DivisibleBy8,
206{
207    fn would_run(&self) -> bool {
208        // must be enabled statically and either checking was statically not required or runtime check passed
209        <<P as Kernel>::RequiredHardwareFeature as EvaluateHardwareFeature>::EnabledStatic::BOOL &&
210            (!<<P as Kernel>::RequiredHardwareFeature as EvaluateHardwareFeature>::MustCheck::BOOL
211                || <P as Kernel>::RequiredHardwareFeature::met_runtime())
212    }
213
214    fn cvt_rgb8_to_luma8f_opt<
215        const CHECKED: bool,
216        const R_COEFF: u32,
217        const G_COEFF: u32,
218        const B_COEFF: u32,
219    >(
220        &mut self,
221        input: &GenericArray<GenericArray<u8, U3>, <P as Kernel>::InputDimension>,
222        output: &mut GenericArray<f32, <P as Kernel>::InputDimension>,
223    ) -> bool {
224        if CHECKED && !self.would_run() {
225            return false;
226        }
227
228        self.cvt_rgb8_to_luma8f::<R_COEFF, G_COEFF, B_COEFF>(input, output);
229        true
230    }
231
232    fn cvt_rgba8_to_luma8f_opt<
233        const CHECKED: bool,
234        const R_COEFF: u32,
235        const G_COEFF: u32,
236        const B_COEFF: u32,
237    >(
238        &mut self,
239        input: &GenericArray<GenericArray<u8, U4>, <P as Kernel>::InputDimension>,
240        output: &mut GenericArray<f32, <P as Kernel>::InputDimension>,
241    ) -> bool {
242        if CHECKED && !self.would_run() {
243            return false;
244        }
245
246        self.cvt_rgba8_to_luma8f::<R_COEFF, G_COEFF, B_COEFF>(input, output);
247        true
248    }
249
250    fn pdqf_negate_alt_cols_opt<const NEGATE: bool, const CHECKED: bool>(
251        &mut self,
252        input: &mut GenericArray<
253            GenericArray<Self::InternalFloat, Self::OutputDimension>,
254            Self::OutputDimension,
255        >,
256    ) -> bool {
257        if CHECKED && !self.would_run() {
258            return false;
259        }
260
261        self.pdqf_negate_alt_cols::<NEGATE>(input);
262        true
263    }
264
265    fn pdqf_negate_alt_rows_opt<const NEGATE: bool, const CHECKED: bool>(
266        &mut self,
267        input: &mut GenericArray<
268            GenericArray<Self::InternalFloat, Self::OutputDimension>,
269            Self::OutputDimension,
270        >,
271    ) -> bool {
272        if CHECKED && !self.would_run() {
273            return false;
274        }
275
276        self.pdqf_negate_alt_rows::<NEGATE>(input);
277        true
278    }
279
280    fn pdqf_negate_off_diagonals_opt<const CHECKED: bool>(
281        &mut self,
282        input: &mut GenericArray<
283            GenericArray<Self::InternalFloat, Self::OutputDimension>,
284            Self::OutputDimension,
285        >,
286    ) -> bool {
287        if CHECKED && !self.would_run() {
288            return false;
289        }
290
291        self.pdqf_negate_off_diagonals(input);
292        true
293    }
294
295    fn pdqf_t_opt<const CHECKED: bool>(
296        &mut self,
297        input: &mut GenericArray<
298            GenericArray<Self::InternalFloat, Self::OutputDimension>,
299            Self::OutputDimension,
300        >,
301    ) -> bool {
302        if CHECKED && !self.would_run() {
303            return false;
304        }
305
306        self.pdqf_t(input);
307        true
308    }
309
310    fn jarosz_compress_opt<const CHECKED: bool>(
311        &mut self,
312        buffer: &GenericArray<
313            GenericArray<f32, <P as Kernel>::InputDimension>,
314            <P as Kernel>::InputDimension,
315        >,
316        output: &mut GenericArray<
317            GenericArray<<P as Kernel>::InternalFloat, <P as Kernel>::Buffer1WidthX>,
318            <P as Kernel>::Buffer1LengthY,
319        >,
320    ) -> bool {
321        if CHECKED && !self.would_run() {
322            return false;
323        }
324
325        self.jarosz_compress(buffer, output);
326        true
327    }
328
329    fn ident_opt(
330        &self,
331    ) -> MaybeFellThroughToken<
332        <<P as Kernel>::RequiredHardwareFeature as EvaluateHardwareFeature>::EnabledStatic,
333    > {
334        if !self.would_run() {
335            return MaybeFellThroughToken {
336                _private: PhantomData,
337                fell_through_runtime: true,
338            };
339        }
340
341        MaybeFellThroughToken {
342            _private: PhantomData,
343            fell_through_runtime: false,
344        }
345    }
346
347    fn quantize_opt<const CHECKED: bool>(
348        &mut self,
349        input: &GenericArray<
350            GenericArray<<P as Kernel>::InternalFloat, <P as Kernel>::OutputDimension>,
351            <P as Kernel>::OutputDimension,
352        >,
353        threshold: &mut <P as Kernel>::InternalFloat,
354        output: &mut GenericArray<
355            GenericArray<u8, <<P as Kernel>::OutputDimension as DivisibleBy8>::Output>,
356            <P as Kernel>::OutputDimension,
357        >,
358    ) -> bool {
359        if CHECKED && !self.would_run() {
360            return false;
361        }
362
363        self.quantize(input, threshold, output);
364        true
365    }
366
367    fn dct2d_opt<const CHECKED: bool>(
368        &mut self,
369        buffer: &GenericArray<
370            GenericArray<<P as Kernel>::InternalFloat, <P as Kernel>::Buffer1WidthX>,
371            <P as Kernel>::Buffer1LengthY,
372        >,
373        tmp_row_buffer: &mut GenericArray<
374            <P as Kernel>::InternalFloat,
375            <P as Kernel>::Buffer1WidthX,
376        >,
377        output: &mut GenericArray<
378            GenericArray<<P as Kernel>::InternalFloat, <P as Kernel>::OutputDimension>,
379            <P as Kernel>::OutputDimension,
380        >,
381    ) -> bool {
382        if CHECKED && !self.would_run() {
383            return false;
384        }
385
386        self.dct2d(buffer, tmp_row_buffer, output);
387        true
388    }
389
390    fn sum_of_gradients_opt<const CHECKED: bool>(
391        &mut self,
392        input: &GenericArray<
393            GenericArray<<P as Kernel>::InternalFloat, <P as Kernel>::OutputDimension>,
394            <P as Kernel>::OutputDimension,
395        >,
396    ) -> Option<P::InternalFloat> {
397        if CHECKED && !self.would_run() {
398            return None;
399        }
400
401        Some(self.sum_of_gradients(input))
402    }
403}
404
405/// A static fallback router for composing kernels.
406#[derive(Debug, Clone, Copy, Default)]
407pub struct KernelRouter<P, F> {
408    materialized_decision: bool,
409    preferred: P,
410    fallback: F,
411}
412
413impl<
414    Buffer1WidthX: ArrayLength,
415    Buffer1LengthY: ArrayLength,
416    InputDimension: ArrayLength + SquareOf,
417    OutputDimension: ArrayLength + SquareOf + Mul<OutputDimension> + DivisibleBy8,
418    InternalFloat: num_traits::float::TotalOrder
419        + num_traits::FromPrimitive
420        + num_traits::NumAssign
421        + num_traits::bounds::Bounded
422        + num_traits::NumCast
423        + num_traits::identities::Zero
424        + num_traits::identities::One
425        + num_traits::Signed
426        + PartialOrd
427        + Clone
428        + Display
429        + Debug
430        + Default
431        + Send
432        + Sync,
433    P: Kernel<
434            Buffer1WidthX = Buffer1WidthX,
435            Buffer1LengthY = Buffer1LengthY,
436            InputDimension = InputDimension,
437            OutputDimension = OutputDimension,
438            InternalFloat = InternalFloat,
439        >,
440    F: Kernel<
441            Buffer1WidthX = Buffer1WidthX,
442            Buffer1LengthY = Buffer1LengthY,
443            InputDimension = InputDimension,
444            OutputDimension = OutputDimension,
445            InternalFloat = InternalFloat,
446        >,
447> KernelRouter<P, F>
448where
449    P::RequiredHardwareFeature: EvaluateHardwareFeature,
450    <OutputDimension as Mul<OutputDimension>>::Output: ArrayLength,
451    <P as Kernel>::OutputDimension: DivisibleBy8,
452    <F as Kernel>::OutputDimension: DivisibleBy8,
453{
454    /// Create a new kernel router.
455    ///
456    /// Fallback kernel must be guaranteed to be available at runtime.
457    pub fn new(preferred: P, fallback: F) -> Self
458    where
459        F::RequiredHardwareFeature: EvaluateHardwareFeature<EnabledStatic = B1, MustCheck = B0>,
460    {
461        // must be enabled statically and either checking was statically not required or runtime check passed
462        let decision = <<P as Kernel>::RequiredHardwareFeature as EvaluateHardwareFeature>::EnabledStatic::BOOL &&
463            (!<<P as Kernel>::RequiredHardwareFeature as EvaluateHardwareFeature>::MustCheck::BOOL
464                || <P as Kernel>::RequiredHardwareFeature::met_runtime());
465
466        Self {
467            materialized_decision: decision,
468            preferred,
469            fallback,
470        }
471    }
472
473    /// Layer a new kernel on top of the current kernel router.
474    ///
475    /// The new kernel will be used as the preferred kernel, and the current kernel router will be used as the secondary fallback kernel.
476    pub fn layer_on_top<
477        P2: Kernel<
478                Buffer1WidthX = Buffer1WidthX,
479                Buffer1LengthY = Buffer1LengthY,
480                InputDimension = InputDimension,
481                OutputDimension = OutputDimension,
482                InternalFloat = InternalFloat,
483            >,
484    >(
485        self,
486        kernel: P2,
487    ) -> KernelRouter<P2, Self> {
488        let decision =
489            !<<P as Kernel>::RequiredHardwareFeature as EvaluateHardwareFeature>::MustCheck::BOOL
490                || <P as Kernel>::RequiredHardwareFeature::met_runtime();
491
492        KernelRouter {
493            materialized_decision: decision,
494            preferred: kernel,
495            fallback: self,
496        }
497    }
498}
499
500#[derive(Clone, Copy)]
501/// An identification token for a runtime decision to use a fallback kernel.
502pub struct FallbackToken<
503    PE: Bit,
504    PIdent: Debug + Display + Clone + Copy + 'static + PartialEq,
505    FIdent: Debug + Display + Clone + Copy + 'static + PartialEq,
506> {
507    preferred: PIdent,
508    fall_through: MaybeFellThroughToken<PE>,
509    fallback: FIdent,
510}
511
512impl<
513    PE: Bit,
514    PIdent: Debug + Display + Clone + Copy + 'static + PartialEq,
515    FIdent: Debug + Display + Clone + Copy + 'static + PartialEq,
516> Debug for FallbackToken<PE, PIdent, FIdent>
517{
518    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
519        if PE::BOOL {
520            if self.fall_through.fell_through_runtime {
521                write!(
522                    f,
523                    "FallbackIdent(decision: fallback (reason: runtime hardware feature not met) {:?}, preferred was {:?})",
524                    self.fallback, self.preferred
525                )
526            } else {
527                write!(
528                    f,
529                    "FallbackIdent(decision: using preferred {:?}, fallback was {:?})",
530                    self.preferred, self.fallback
531                )
532            }
533        } else {
534            write!(
535                f,
536                "FallbackIdent(decision: fallback (reason: compile time flag not met) {:?}, preferred was {:?})",
537                self.fallback, self.preferred
538            )
539        }
540    }
541}
542
543impl<
544    PE: Bit,
545    PIdent: Debug + Display + Clone + Copy + 'static + PartialEq,
546    FIdent: Debug + Display + Clone + Copy + 'static + PartialEq,
547> Display for FallbackToken<PE, PIdent, FIdent>
548{
549    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
550        if PE::BOOL && !self.fall_through.fell_through_runtime {
551            write!(f, "{}", self.preferred)
552        } else {
553            write!(f, "{}", self.fallback)
554        }
555    }
556}
557
558impl<
559    PE: Bit,
560    PIdent: Debug + Display + Clone + Copy + 'static + PartialEq,
561    FIdent: Debug + Display + Clone + Copy + 'static + PartialEq,
562> PartialEq for FallbackToken<PE, PIdent, FIdent>
563{
564    fn eq(&self, other: &Self) -> bool {
565        self.preferred == other.preferred
566            && self.fallback == other.fallback
567            && self.fall_through.fell_through_runtime == other.fall_through.fell_through_runtime
568    }
569}
570
571impl<
572    Buffer1WidthX: ArrayLength,
573    Buffer1LengthY: ArrayLength,
574    InputDimension: ArrayLength + SquareOf,
575    OutputDimension: ArrayLength + SquareOf + Mul<OutputDimension> + DivisibleBy8,
576    InternalFloat: num_traits::float::TotalOrder
577        + num_traits::FromPrimitive
578        + num_traits::NumAssign
579        + num_traits::bounds::Bounded
580        + num_traits::NumCast
581        + num_traits::identities::Zero
582        + num_traits::identities::One
583        + num_traits::Signed
584        + PartialOrd
585        + Clone
586        + Display
587        + Debug
588        + Default
589        + Send
590        + Sync,
591    P: Kernel<
592            Buffer1WidthX = Buffer1WidthX,
593            Buffer1LengthY = Buffer1LengthY,
594            InputDimension = InputDimension,
595            OutputDimension = OutputDimension,
596            InternalFloat = InternalFloat,
597        > + KernelFallthrough<
598            <<P as Kernel>::RequiredHardwareFeature as EvaluateHardwareFeature>::EnabledStatic,
599            InputDimension,
600            Buffer1WidthX,
601            Buffer1LengthY,
602            OutputDimension,
603            InternalFloat,
604        >,
605    F: Kernel<
606            Buffer1WidthX = Buffer1WidthX,
607            Buffer1LengthY = Buffer1LengthY,
608            InputDimension = InputDimension,
609            OutputDimension = OutputDimension,
610            InternalFloat = InternalFloat,
611        >,
612> Kernel for KernelRouter<P, F>
613where
614    P::RequiredHardwareFeature: EvaluateHardwareFeature,
615    F::RequiredHardwareFeature: EvaluateHardwareFeature,
616    <OutputDimension as Mul<OutputDimension>>::Output: ArrayLength,
617    P::RequiredHardwareFeature: EvaluateHardwareFeature,
618    F::RequiredHardwareFeature: EvaluateHardwareFeature<EnabledStatic = B1, MustCheck = B0>,
619{
620    type InternalFloat = InternalFloat;
621    type Buffer1WidthX = Buffer1WidthX;
622    type Buffer1LengthY = Buffer1LengthY;
623    type InputDimension = InputDimension;
624    type OutputDimension = OutputDimension;
625    type RequiredHardwareFeature =
626        FallbackRequirements<P::RequiredHardwareFeature, F::RequiredHardwareFeature>;
627    type Ident = FallbackToken<
628        <<P as Kernel>::RequiredHardwareFeature as EvaluateHardwareFeature>::EnabledStatic,
629        <P as Kernel>::Ident,
630        <F as Kernel>::Ident,
631    >;
632
633    fn ident(&self) -> Self::Ident {
634        let token = self.preferred.ident_opt();
635        Self::Ident {
636            preferred: self.preferred.ident(),
637            fall_through: token,
638            fallback: self.fallback.ident(),
639        }
640    }
641
642    fn required_hardware_features_met() -> bool {
643        Self::RequiredHardwareFeature::met_runtime()
644    }
645
646    fn cvt_rgb8_to_luma8f<const R_COEFF: u32, const G_COEFF: u32, const B_COEFF: u32>(
647        &mut self,
648        input: &GenericArray<GenericArray<u8, U3>, Self::InputDimension>,
649        output: &mut GenericArray<f32, Self::InputDimension>,
650    ) {
651        if self.materialized_decision && <<P as Kernel>::RequiredHardwareFeature as EvaluateHardwareFeature>::EnabledStatic::BOOL && self.preferred.cvt_rgb8_to_luma8f_opt::<false, R_COEFF, G_COEFF, B_COEFF>(input, output) {
652            return;
653        }
654
655        self.fallback
656            .cvt_rgb8_to_luma8f::<R_COEFF, G_COEFF, B_COEFF>(input, output);
657    }
658
659    fn cvt_rgba8_to_luma8f<const R_COEFF: u32, const G_COEFF: u32, const B_COEFF: u32>(
660        &mut self,
661        input: &GenericArray<GenericArray<u8, U4>, Self::InputDimension>,
662        output: &mut GenericArray<f32, Self::InputDimension>,
663    ) {
664        if self.materialized_decision && <<P as Kernel>::RequiredHardwareFeature as EvaluateHardwareFeature>::EnabledStatic::BOOL && self.preferred.cvt_rgba8_to_luma8f_opt::<false, R_COEFF, G_COEFF, B_COEFF>(input, output) {
665            return;
666        }
667
668        self.fallback
669            .cvt_rgba8_to_luma8f::<R_COEFF, G_COEFF, B_COEFF>(input, output);
670    }
671
672    fn pdqf_negate_alt_cols<const NEGATE: bool>(
673        &mut self,
674        input: &mut GenericArray<
675            GenericArray<Self::InternalFloat, Self::OutputDimension>,
676            Self::OutputDimension,
677        >,
678    ) {
679        if self.materialized_decision && <<P as Kernel>::RequiredHardwareFeature as EvaluateHardwareFeature>::EnabledStatic::BOOL && self.preferred.pdqf_negate_alt_cols_opt::<NEGATE, false>(input) {
680            return;
681        }
682
683        self.fallback.pdqf_negate_alt_cols::<NEGATE>(input);
684    }
685
686    fn pdqf_negate_alt_rows<const NEGATE: bool>(
687        &mut self,
688        input: &mut GenericArray<
689            GenericArray<Self::InternalFloat, Self::OutputDimension>,
690            Self::OutputDimension,
691        >,
692    ) {
693        if self.materialized_decision && <<P as Kernel>::RequiredHardwareFeature as EvaluateHardwareFeature>::EnabledStatic::BOOL && self.preferred.pdqf_negate_alt_rows_opt::<NEGATE, false>(input) {
694            return;
695        }
696
697        self.fallback.pdqf_negate_alt_rows::<NEGATE>(input);
698    }
699
700    fn pdqf_negate_off_diagonals(
701        &mut self,
702        input: &mut GenericArray<
703            GenericArray<Self::InternalFloat, Self::OutputDimension>,
704            Self::OutputDimension,
705        >,
706    ) {
707        if self.materialized_decision && <<P as Kernel>::RequiredHardwareFeature as EvaluateHardwareFeature>::EnabledStatic::BOOL && self.preferred.pdqf_negate_off_diagonals_opt::<false>(input) {
708            return;
709        }
710
711        self.fallback.pdqf_negate_off_diagonals(input);
712    }
713
714    fn pdqf_t(
715        &mut self,
716        input: &mut GenericArray<
717            GenericArray<Self::InternalFloat, Self::OutputDimension>,
718            Self::OutputDimension,
719        >,
720    ) {
721        if self.materialized_decision && <<P as Kernel>::RequiredHardwareFeature as EvaluateHardwareFeature>::EnabledStatic::BOOL && self.preferred.pdqf_t_opt::<false>(input) {
722            return;
723        }
724
725        self.fallback.pdqf_t(input);
726    }
727
728    fn jarosz_compress(
729        &mut self,
730        buffer: &GenericArray<GenericArray<f32, Self::InputDimension>, Self::InputDimension>,
731        output: &mut GenericArray<
732            GenericArray<Self::InternalFloat, Self::Buffer1WidthX>,
733            Self::Buffer1LengthY,
734        >,
735    ) {
736        if self.materialized_decision && <<P as Kernel>::RequiredHardwareFeature as EvaluateHardwareFeature>::EnabledStatic::BOOL && self.preferred.jarosz_compress_opt::<false>(buffer, output) {
737            return;
738        }
739
740        self.fallback.jarosz_compress(buffer, output);
741    }
742
743    fn quantize(
744        &mut self,
745        input: &GenericArray<
746            GenericArray<Self::InternalFloat, Self::OutputDimension>,
747            Self::OutputDimension,
748        >,
749        threshold: &mut Self::InternalFloat,
750        output: &mut GenericArray<
751            GenericArray<u8, <Self::OutputDimension as DivisibleBy8>::Output>,
752            Self::OutputDimension,
753        >,
754    ) {
755        if self.materialized_decision && <<P as Kernel>::RequiredHardwareFeature as EvaluateHardwareFeature>::EnabledStatic::BOOL && self
756                .preferred
757                .quantize_opt::<false>(input, threshold, output) {
758            return;
759        }
760
761        self.fallback.quantize(input, threshold, output);
762    }
763
764    /// Compute the sum of gradients of the input buffer in both horizontal and vertical directions.
765    fn sum_of_gradients(
766        &mut self,
767        input: &GenericArray<
768            GenericArray<Self::InternalFloat, Self::OutputDimension>,
769            Self::OutputDimension,
770        >,
771    ) -> Self::InternalFloat {
772        if self.materialized_decision && <<P as Kernel>::RequiredHardwareFeature as EvaluateHardwareFeature>::EnabledStatic::BOOL {
773            if let Some(sum) = self.preferred.sum_of_gradients_opt::<false>(input) {
774                return sum;
775            }
776        }
777
778        self.fallback.sum_of_gradients(input)
779    }
780
781    fn adjust_quality(input: Self::InternalFloat) -> f32 {
782        <F as Kernel>::adjust_quality(input)
783    }
784
785    fn dct2d(
786        &mut self,
787        buffer: &GenericArray<
788            GenericArray<Self::InternalFloat, Self::Buffer1WidthX>,
789            Self::Buffer1LengthY,
790        >,
791        tmp_row_buffer: &mut GenericArray<Self::InternalFloat, Self::Buffer1WidthX>,
792        output: &mut GenericArray<
793            GenericArray<Self::InternalFloat, Self::OutputDimension>,
794            Self::OutputDimension,
795        >,
796    ) {
797        if self.materialized_decision && <<P as Kernel>::RequiredHardwareFeature as EvaluateHardwareFeature>::EnabledStatic::BOOL {
798            self.preferred.dct2d_opt::<false>(buffer, tmp_row_buffer, output);
799            return;
800        }
801
802        self.fallback.dct2d(buffer, tmp_row_buffer, output);
803    }
804}