av_scenechange/
cpu.rs

1#[cfg(asm_neon)]
2pub use neon::*;
3#[cfg(not(any(asm_x86_64, asm_neon)))]
4pub use rust::*;
5#[cfg(asm_x86_64)]
6pub use x86::*;
7
8#[cfg(not(any(asm_x86_64, asm_neon)))]
9mod rust {
10    use arg_enum_proc_macro::ArgEnum;
11
12    #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, ArgEnum, Default)]
13    #[allow(clippy::upper_case_acronyms)]
14
15    pub enum CpuFeatureLevel {
16        #[default]
17        RUST,
18    }
19
20    impl CpuFeatureLevel {
21        #[cfg(test)]
22        #[allow(unused)]
23        #[inline]
24        pub const fn all() -> &'static [Self] {
25            use CpuFeatureLevel::*;
26            &[RUST]
27        }
28    }
29}
30
31#[cfg(asm_x86_64)]
32#[macro_use]
33mod x86 {
34    use std::{env, str::FromStr};
35
36    use arg_enum_proc_macro::ArgEnum;
37
38    #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, ArgEnum)]
39    #[allow(clippy::upper_case_acronyms)]
40    pub enum CpuFeatureLevel {
41        RUST,
42        SSE2,
43        SSSE3,
44        #[arg_enum(alias = "sse4.1")]
45        SSE4_1,
46        AVX2,
47        AVX512,
48        #[arg_enum(alias = "avx512vpclmulqdq")]
49        AVX512ICL,
50    }
51
52    impl CpuFeatureLevel {
53        #[cfg(test)]
54        pub const fn all() -> &'static [Self] {
55            &[
56                CpuFeatureLevel::RUST,
57                CpuFeatureLevel::SSE2,
58                CpuFeatureLevel::SSSE3,
59                CpuFeatureLevel::SSE4_1,
60                CpuFeatureLevel::AVX2,
61                CpuFeatureLevel::AVX512,
62                CpuFeatureLevel::AVX512ICL,
63            ]
64        }
65
66        #[inline]
67        pub const fn len() -> usize {
68            CpuFeatureLevel::AVX512ICL as usize + 1
69        }
70
71        #[inline]
72        pub const fn as_index(self) -> usize {
73            self as usize
74        }
75    }
76
77    impl Default for CpuFeatureLevel {
78        #[inline]
79        fn default() -> CpuFeatureLevel {
80            fn avx512_detected() -> bool {
81                is_x86_feature_detected!("avx512bw")
82                    && is_x86_feature_detected!("avx512cd")
83                    && is_x86_feature_detected!("avx512dq")
84                    && is_x86_feature_detected!("avx512f")
85                    && is_x86_feature_detected!("avx512vl")
86            }
87            #[allow(deprecated)] // Until MSRV >= 1.69.0
88            fn avx512icl_detected() -> bool {
89                // Per dav1d, these are the flags needed.
90                avx512_detected()
91                    && is_x86_feature_detected!("avx512vnni")
92                    && is_x86_feature_detected!("avx512ifma")
93                    && is_x86_feature_detected!("avx512vbmi")
94                    && is_x86_feature_detected!("avx512vbmi2")
95                    && is_x86_feature_detected!("avx512vpopcntdq")
96                    && is_x86_feature_detected!("avx512bitalg")
97                    && is_x86_feature_detected!("avx512gfni")
98                    && is_x86_feature_detected!("avx512vaes")
99                    && is_x86_feature_detected!("avx512vpclmulqdq")
100            }
101
102            let detected: CpuFeatureLevel = if avx512icl_detected() {
103                CpuFeatureLevel::AVX512ICL
104            } else if avx512_detected() {
105                CpuFeatureLevel::AVX512
106            } else if is_x86_feature_detected!("avx2") {
107                CpuFeatureLevel::AVX2
108            } else if is_x86_feature_detected!("sse4.1") {
109                CpuFeatureLevel::SSE4_1
110            } else if is_x86_feature_detected!("ssse3") {
111                CpuFeatureLevel::SSSE3
112            } else if is_x86_feature_detected!("sse2") {
113                CpuFeatureLevel::SSE2
114            } else {
115                CpuFeatureLevel::RUST
116            };
117            let manual: CpuFeatureLevel = match env::var("CPU_TARGET") {
118                Ok(feature) => CpuFeatureLevel::from_str(&feature).unwrap_or(detected),
119                Err(_e) => detected,
120            };
121            if manual > detected {
122                detected
123            } else {
124                manual
125            }
126        }
127    }
128
129    // Create a static lookup table for CPUFeatureLevel enums
130    // Note: keys are CpuFeatureLevels without any prefix (no CpuFeatureLevel::)
131    macro_rules! cpu_function_lookup_table {
132        // version for default visibility
133        ($name:ident: [$type:ty], default: $empty:expr, [$(($key:ident, $value:expr)),*]) => {
134            static $name: [$type; crate::cpu::CpuFeatureLevel::len()] = {
135            use crate::cpu::CpuFeatureLevel;
136            #[allow(unused_mut)]
137            let mut out: [$type; CpuFeatureLevel::len()] = [$empty; CpuFeatureLevel::len()];
138
139            // Can't use out[0][.] == $empty in static as of rust 1.40
140            #[allow(unused_mut)]
141            let mut set: [bool; CpuFeatureLevel::len()] = [false; CpuFeatureLevel::len()];
142
143            #[allow(unused_imports)]
144            use CpuFeatureLevel::*;
145            $(
146                out[$key as usize] = $value;
147                set[$key as usize] = true;
148            )*
149            cpu_function_lookup_table!(waterfall_cpu_features(out, set, [SSE2, SSSE3, SSE4_1, AVX2, AVX512, AVX512ICL]));
150            out
151            };
152        };
153        ($pub:vis, $name:ident: [$type:ty], default: $empty:expr, [$(($key:ident, $value:expr)),*]) => {
154            $pub cpu_function_lookup_table!($name: [$type], default: $empty, [$(($key, $value)),*]);
155        };
156
157        // Fill empty output functions with the existent functions they support.
158        // cpus should be in order of lowest cpu level to highest
159        // Used like an internal function
160        // Put in here to avoid adding more public macros
161        (waterfall_cpu_features($out:ident, $set:ident, [$($cpu:ident),*])) => {
162            // Use an array to emulate if statements (not supported in static as of
163            // rust 1.40). Setting best[0] (false) and best[1] (true) is equivalent to
164            // doing nothing and overriding our value respectively.
165            #[allow(unused_assignments)]
166            let mut best = [$out[0], $out[0]];
167            $(
168            // If the current entry has a function, update out best function.
169            best[$set[$cpu as usize] as usize] = $out[$cpu as usize];
170            // Update our current entry. Does nothing if it already had a function.
171            $out[$cpu as usize] = best[1];
172            )*
173        };
174
175        // use $name_$key as our values
176        ($pub:vis, $name:ident: [$type:ty], default: $empty:expr, [$($key:ident),*]) => {
177            pastey::item!{
178                cpu_function_lookup_table!(
179                    $pub, $name: [$type], default: $empty, [$(($key, [<$name _$key>])),*]
180                );
181            }
182        };
183
184        // version for default visibility
185        ($name:ident: [$type:ty], default: $empty:expr, [$($key:ident),*]) => {
186            pastey::item!{
187                cpu_function_lookup_table!(
188                    $name: [$type], default: $empty, [$(($key, [<$name _$key>])),*]
189                );
190            }
191        };
192    }
193}
194
195#[cfg(asm_neon)]
196#[macro_use]
197mod neon {
198    use std::{env, str::FromStr};
199
200    use arg_enum_proc_macro::ArgEnum;
201
202    #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, ArgEnum)]
203    #[allow(clippy::upper_case_acronyms)]
204    pub enum CpuFeatureLevel {
205        RUST,
206        NEON,
207    }
208
209    impl CpuFeatureLevel {
210        #[cfg(test)]
211        #[inline]
212        pub const fn all() -> &'static [Self] {
213            use CpuFeatureLevel::*;
214            &[RUST, NEON]
215        }
216
217        #[inline]
218        pub const fn len() -> usize {
219            CpuFeatureLevel::NEON as usize + 1
220        }
221
222        #[inline]
223        pub fn as_index(self) -> usize {
224            self as usize
225        }
226    }
227
228    impl Default for CpuFeatureLevel {
229        #[inline]
230        fn default() -> CpuFeatureLevel {
231            let detected = CpuFeatureLevel::NEON;
232            let manual: CpuFeatureLevel = match env::var("CPU_TARGET") {
233                Ok(feature) => CpuFeatureLevel::from_str(&feature).unwrap_or(detected),
234                Err(_e) => detected,
235            };
236            if manual > detected {
237                detected
238            } else {
239                manual
240            }
241        }
242    }
243
244    // Create a static lookup table for CPUFeatureLevel enums
245    // Note: keys are CpuFeatureLevels without any prefix (no CpuFeatureLevel::)
246    macro_rules! cpu_function_lookup_table {
247        // version for default visibility
248        ($name:ident: [$type:ty], default: $empty:expr, [$(($key:ident, $value:expr)),*]) => {
249            static $name: [$type; crate::cpu::CpuFeatureLevel::len()] = {
250            use crate::cpu::CpuFeatureLevel;
251            #[allow(unused_mut)]
252            let mut out: [$type; CpuFeatureLevel::len()] = [$empty; CpuFeatureLevel::len()];
253
254            // Can't use out[0][.] == $empty in static as of rust 1.40
255            #[allow(unused_mut)]
256            let mut set: [bool; CpuFeatureLevel::len()] = [false; CpuFeatureLevel::len()];
257
258            #[allow(unused_imports)]
259            use CpuFeatureLevel::*;
260            $(
261                out[$key as usize] = $value;
262                set[$key as usize] = true;
263            )*
264            cpu_function_lookup_table!(waterfall_cpu_features(out, set, [NEON]));
265            out
266            };
267        };
268
269        ($pub:vis, $name:ident: [$type:ty], default: $empty:expr, [$(($key:ident, $value:expr)),*]) => {
270            $pub cpu_function_lookup_table!($name: [$type], default: $empty, [$(($key, $value)),*]);
271        };
272        // Fill empty output functions with the existent functions they support.
273        // cpus should be in order of lowest cpu level to highest
274        // Used like an internal function
275        // Put in here to avoid adding more public macros
276        (waterfall_cpu_features($out:ident, $set:ident, [$($cpu:ident),*])) => {
277            // Use an array to emulate if statements (not supported in static as of
278            // rust 1.40). Setting best[0] (false) and best[1] (true) is equivalent to
279            // doing nothing and overriding our value respectively.
280            #[allow(unused_assignments)]
281            let mut best = [$out[0], $out[0]];
282            $(
283            // If the current entry has a function, update out best function.
284            best[$set[$cpu as usize] as usize] = $out[$cpu as usize];
285            // Update our current entry. Does nothing if it already had a function.
286            $out[$cpu as usize] = best[1];
287            )*
288        };
289
290
291        // use $name_$key as our values
292        ($pub:vis, $name:ident: [$type:ty], default: $empty:expr, [$($key:ident),*]) => {
293            pastey::item!{
294            cpu_function_lookup_table!(
295                $pub, $name: [$type], default: $empty, [$(($key, [<$name _$key>])),*]
296            );
297            }
298        };
299
300        // version for default visibility
301        ($name:ident: [$type:ty], default: $empty:expr, [$($key:ident),*]) => {
302            pastey::item!{
303            cpu_function_lookup_table!(
304                $name: [$type], default: $empty, [$(($key, [<$name _$key>])),*]
305            );
306            }
307        };
308    }
309}