1 | // Copyright (c) 2020-2023, The rav1e contributors. All rights reserved |
2 | // |
3 | // This source code is subject to the terms of the BSD 2 Clause License and |
4 | // the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License |
5 | // was not distributed with this source code in the LICENSE file, you can |
6 | // obtain it at www.aomedia.org/license/software. If the Alliance for Open |
7 | // Media Patent License 1.0 was not distributed with this source code in the |
8 | // PATENTS file, you can obtain it at www.aomedia.org/license/patent. |
9 | |
10 | use num_derive::*; |
11 | |
12 | use crate::partition::BlockSize; |
13 | use crate::serialize::{Deserialize, Serialize}; |
14 | |
15 | use std::fmt; |
16 | |
17 | // NOTE: Add Structures at the end. |
18 | /// Contains the speed settings. |
19 | #[derive (Clone, Copy, Debug, Serialize, Deserialize)] |
20 | #[non_exhaustive ] |
21 | pub struct SpeedSettings { |
22 | /// Enables inter-frames to have multiple reference frames. |
23 | /// |
24 | /// Enabled is slower. |
25 | pub multiref: bool, |
26 | |
27 | /// Enables fast deblocking filter. |
28 | pub fast_deblock: bool, |
29 | |
30 | /// The number of lookahead frames to be used for temporal RDO. |
31 | /// |
32 | /// Higher is slower. |
33 | pub rdo_lookahead_frames: usize, |
34 | |
35 | /// Which scene detection mode to use. Standard is slower, but best. |
36 | pub scene_detection_mode: SceneDetectionSpeed, |
37 | |
38 | /// Enables CDEF. |
39 | pub cdef: bool, |
40 | |
41 | /// Enables LRF. |
42 | pub lrf: bool, |
43 | |
44 | /// Enable searching loop restoration units when no transforms have been coded |
45 | /// restoration unit. |
46 | pub lru_on_skip: bool, |
47 | |
48 | /// The amount of search done for self guided restoration. |
49 | pub sgr_complexity: SGRComplexityLevel, |
50 | |
51 | /// Search level for segmentation. |
52 | /// |
53 | /// Full search is at least twice as slow. |
54 | pub segmentation: SegmentationLevel, |
55 | |
56 | // NOTE: put enums and basic type fields above |
57 | /// Speed settings related to partition decision |
58 | pub partition: PartitionSpeedSettings, |
59 | |
60 | /// Speed settings related to transform size and type decision |
61 | pub transform: TransformSpeedSettings, |
62 | |
63 | /// Speed settings related to intra prediction mode selection |
64 | pub prediction: PredictionSpeedSettings, |
65 | |
66 | /// Speed settings related to motion estimation and motion vector selection |
67 | pub motion: MotionSpeedSettings, |
68 | } |
69 | |
70 | impl Default for SpeedSettings { |
71 | /// The default settings are equivalent to speed 0 |
72 | fn default() -> Self { |
73 | SpeedSettings { |
74 | multiref: true, |
75 | fast_deblock: false, |
76 | rdo_lookahead_frames: 40, |
77 | scene_detection_mode: SceneDetectionSpeed::Standard, |
78 | cdef: true, |
79 | lrf: true, |
80 | lru_on_skip: true, |
81 | sgr_complexity: SGRComplexityLevel::Full, |
82 | segmentation: SegmentationLevel::Complex, |
83 | partition: PartitionSpeedSettings { |
84 | encode_bottomup: true, |
85 | non_square_partition_max_threshold: BlockSize::BLOCK_64X64, |
86 | partition_range: PartitionRange::new( |
87 | BlockSize::BLOCK_4X4, |
88 | BlockSize::BLOCK_64X64, |
89 | ), |
90 | }, |
91 | transform: TransformSpeedSettings { |
92 | reduced_tx_set: false, |
93 | // TX domain distortion is always faster, with no significant quality change, |
94 | // although it will be ignored when Tune == Psychovisual. |
95 | tx_domain_distortion: true, |
96 | tx_domain_rate: false, |
97 | rdo_tx_decision: true, |
98 | enable_inter_tx_split: false, |
99 | }, |
100 | prediction: PredictionSpeedSettings { |
101 | prediction_modes: PredictionModesSetting::ComplexAll, |
102 | fine_directional_intra: true, |
103 | }, |
104 | motion: MotionSpeedSettings { |
105 | include_near_mvs: true, |
106 | use_satd_subpel: true, |
107 | me_allow_full_search: true, |
108 | }, |
109 | } |
110 | } |
111 | } |
112 | |
113 | impl SpeedSettings { |
114 | /// Set the speed setting according to a numeric speed preset. |
115 | pub fn from_preset(speed: u8) -> Self { |
116 | // The default settings are equivalent to speed 0 |
117 | let mut settings = SpeedSettings::default(); |
118 | |
119 | if speed >= 1 { |
120 | settings.lru_on_skip = false; |
121 | settings.segmentation = SegmentationLevel::Simple; |
122 | } |
123 | |
124 | if speed >= 2 { |
125 | settings.partition.non_square_partition_max_threshold = |
126 | BlockSize::BLOCK_8X8; |
127 | |
128 | settings.prediction.prediction_modes = |
129 | PredictionModesSetting::ComplexKeyframes; |
130 | } |
131 | |
132 | if speed >= 3 { |
133 | settings.rdo_lookahead_frames = 30; |
134 | |
135 | settings.partition.partition_range = |
136 | PartitionRange::new(BlockSize::BLOCK_8X8, BlockSize::BLOCK_64X64); |
137 | } |
138 | |
139 | if speed >= 4 { |
140 | settings.partition.encode_bottomup = false; |
141 | } |
142 | |
143 | if speed >= 5 { |
144 | settings.sgr_complexity = SGRComplexityLevel::Reduced; |
145 | settings.motion.include_near_mvs = false; |
146 | } |
147 | |
148 | if speed >= 6 { |
149 | settings.rdo_lookahead_frames = 20; |
150 | |
151 | settings.transform.rdo_tx_decision = false; |
152 | settings.transform.reduced_tx_set = true; |
153 | |
154 | settings.motion.me_allow_full_search = false; |
155 | } |
156 | |
157 | if speed >= 7 { |
158 | settings.prediction.prediction_modes = PredictionModesSetting::Simple; |
159 | // Multiref is enabled automatically if low_latency is false. |
160 | // |
161 | // If low_latency is true, enabling multiref allows using multiple |
162 | // backwards references. low_latency false enables both forward and |
163 | // backwards references. |
164 | settings.multiref = false; |
165 | settings.fast_deblock = true; |
166 | } |
167 | |
168 | if speed >= 8 { |
169 | settings.rdo_lookahead_frames = 10; |
170 | settings.lrf = false; |
171 | } |
172 | |
173 | if speed >= 9 { |
174 | // 8x8 is fast enough to use until very high speed levels, |
175 | // because 8x8 with reduced TX set is faster but with equivalent |
176 | // or better quality compared to 16x16 (to which reduced TX set does not apply). |
177 | settings.partition.partition_range = |
178 | PartitionRange::new(BlockSize::BLOCK_16X16, BlockSize::BLOCK_32X32); |
179 | |
180 | // FIXME: With unknown reasons, inter_tx_split does not work if reduced_tx_set is false |
181 | settings.transform.enable_inter_tx_split = true; |
182 | } |
183 | |
184 | if speed >= 10 { |
185 | settings.scene_detection_mode = SceneDetectionSpeed::Fast; |
186 | |
187 | settings.partition.partition_range = |
188 | PartitionRange::new(BlockSize::BLOCK_32X32, BlockSize::BLOCK_32X32); |
189 | |
190 | settings.motion.use_satd_subpel = false; |
191 | } |
192 | |
193 | settings |
194 | } |
195 | } |
196 | |
197 | #[derive (Clone, Copy, Debug, Serialize, Deserialize)] |
198 | #[cfg_attr (test, derive(Default))] |
199 | /// Speed settings related to transform size and type decision |
200 | pub struct TransformSpeedSettings { |
201 | /// Enables reduced transform set. |
202 | /// |
203 | /// Enabled is faster. |
204 | pub reduced_tx_set: bool, |
205 | |
206 | /// Enables using transform-domain distortion instead of pixel-domain. |
207 | /// |
208 | /// Enabled is faster. |
209 | pub tx_domain_distortion: bool, |
210 | |
211 | /// Enables using transform-domain rate estimation. |
212 | /// |
213 | /// Enabled is faster. |
214 | pub tx_domain_rate: bool, |
215 | |
216 | /// Enables searching transform size and type with RDO. |
217 | /// |
218 | /// Enabled is slower. |
219 | pub rdo_tx_decision: bool, |
220 | |
221 | /// Enable tx split for inter mode block. |
222 | pub enable_inter_tx_split: bool, |
223 | } |
224 | |
225 | #[derive (Clone, Copy, Debug, Serialize, Deserialize)] |
226 | #[cfg_attr (test, derive(Default))] |
227 | /// Speed settings related to partition decision |
228 | pub struct PartitionSpeedSettings { |
229 | /// Enables bottom-up encoding, rather than top-down. |
230 | /// |
231 | /// Enabled is slower. |
232 | pub encode_bottomup: bool, |
233 | |
234 | /// Allow non-square partition type outside of frame borders |
235 | /// on any blocks at or below this size. |
236 | pub non_square_partition_max_threshold: BlockSize, |
237 | |
238 | /// Range of partition sizes that can be used. Larger ranges are slower. |
239 | /// |
240 | /// Must be based on square block sizes, so e.g. 8×4 isn't allowed here. |
241 | pub partition_range: PartitionRange, |
242 | } |
243 | |
244 | #[derive (Clone, Copy, Debug, Serialize, Deserialize)] |
245 | #[cfg_attr (test, derive(Default))] |
246 | /// Speed settings related to motion estimation and motion vector selection |
247 | pub struct MotionSpeedSettings { |
248 | /// Use SATD instead of SAD for subpixel search. |
249 | /// |
250 | /// Enabled is slower. |
251 | pub use_satd_subpel: bool, |
252 | |
253 | /// Enables searching near motion vectors during RDO. |
254 | /// |
255 | /// Enabled is slower. |
256 | pub include_near_mvs: bool, |
257 | |
258 | /// Enable full search in some parts of motion estimation. Allowing full |
259 | /// search is slower. |
260 | pub me_allow_full_search: bool, |
261 | } |
262 | |
263 | #[derive (Clone, Copy, Debug, Serialize, Deserialize)] |
264 | #[cfg_attr (test, derive(Default))] |
265 | /// Speed settings related to intra prediction mode selection |
266 | pub struct PredictionSpeedSettings { |
267 | /// Prediction modes to search. |
268 | /// |
269 | /// Complex settings are slower. |
270 | pub prediction_modes: PredictionModesSetting, |
271 | |
272 | /// Use fine directional intra prediction |
273 | pub fine_directional_intra: bool, |
274 | } |
275 | |
276 | /// Range of block sizes to use. |
277 | #[derive (Clone, Copy, Debug, Serialize, Deserialize)] |
278 | pub struct PartitionRange { |
279 | pub(crate) min: BlockSize, |
280 | pub(crate) max: BlockSize, |
281 | } |
282 | |
283 | impl PartitionRange { |
284 | /// Creates a new partition range with min and max partition sizes. |
285 | /// |
286 | /// # Panics |
287 | /// |
288 | /// - Panics if `max` is larger than `min`. |
289 | /// - Panics if either `min` or `max` are not square. |
290 | pub fn new(min: BlockSize, max: BlockSize) -> Self { |
291 | assert!(max >= min); |
292 | // Topdown search checks the min block size for PARTITION_SPLIT only, so |
293 | // the min block size must be square. |
294 | assert!(min.is_sqr()); |
295 | // Rectangular max partition sizes have not been tested. |
296 | assert!(max.is_sqr()); |
297 | |
298 | Self { min, max } |
299 | } |
300 | } |
301 | |
302 | #[cfg (test)] |
303 | impl Default for PartitionRange { |
304 | fn default() -> Self { |
305 | PartitionRange::new(BlockSize::BLOCK_4X4, BlockSize::BLOCK_64X64) |
306 | } |
307 | } |
308 | |
309 | /// Prediction modes to search. |
310 | #[derive ( |
311 | Clone, |
312 | Copy, |
313 | Debug, |
314 | PartialOrd, |
315 | PartialEq, |
316 | Eq, |
317 | FromPrimitive, |
318 | Serialize, |
319 | Deserialize, |
320 | )] |
321 | pub enum SceneDetectionSpeed { |
322 | /// Fastest scene detection using pixel-wise comparison |
323 | Fast, |
324 | /// Scene detection using motion vectors and cost estimates |
325 | Standard, |
326 | /// Completely disable scene detection and only place keyframes |
327 | /// at fixed intervals. |
328 | None, |
329 | } |
330 | |
331 | impl fmt::Display for SceneDetectionSpeed { |
332 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { |
333 | write!( |
334 | f, |
335 | " {}" , |
336 | match self { |
337 | SceneDetectionSpeed::Fast => "Fast" , |
338 | SceneDetectionSpeed::Standard => "Standard" , |
339 | SceneDetectionSpeed::None => "None" , |
340 | } |
341 | ) |
342 | } |
343 | } |
344 | |
345 | /// Prediction modes to search. |
346 | #[derive ( |
347 | Clone, |
348 | Copy, |
349 | Debug, |
350 | PartialOrd, |
351 | PartialEq, |
352 | Eq, |
353 | FromPrimitive, |
354 | Serialize, |
355 | Deserialize, |
356 | )] |
357 | pub enum PredictionModesSetting { |
358 | /// Only simple prediction modes. |
359 | Simple, |
360 | /// Search all prediction modes on key frames and simple modes on other |
361 | /// frames. |
362 | ComplexKeyframes, |
363 | /// Search all prediction modes on all frames. |
364 | ComplexAll, |
365 | } |
366 | |
367 | impl fmt::Display for PredictionModesSetting { |
368 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { |
369 | write!( |
370 | f, |
371 | " {}" , |
372 | match self { |
373 | PredictionModesSetting::Simple => "Simple" , |
374 | PredictionModesSetting::ComplexKeyframes => "Complex-KFs" , |
375 | PredictionModesSetting::ComplexAll => "Complex-All" , |
376 | } |
377 | ) |
378 | } |
379 | } |
380 | |
381 | #[cfg (test)] |
382 | impl Default for PredictionModesSetting { |
383 | fn default() -> Self { |
384 | PredictionModesSetting::Simple |
385 | } |
386 | } |
387 | |
388 | /// Search level for self guided restoration |
389 | #[derive ( |
390 | Clone, |
391 | Copy, |
392 | Debug, |
393 | PartialOrd, |
394 | PartialEq, |
395 | Eq, |
396 | FromPrimitive, |
397 | Serialize, |
398 | Deserialize, |
399 | )] |
400 | pub enum SGRComplexityLevel { |
401 | /// Search all sgr parameters |
402 | Full, |
403 | /// Search a reduced set of sgr parameters |
404 | Reduced, |
405 | } |
406 | |
407 | impl fmt::Display for SGRComplexityLevel { |
408 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { |
409 | write!( |
410 | f, |
411 | " {}" , |
412 | match self { |
413 | SGRComplexityLevel::Full => "Full" , |
414 | SGRComplexityLevel::Reduced => "Reduced" , |
415 | } |
416 | ) |
417 | } |
418 | } |
419 | |
420 | /// Search level for segmentation |
421 | #[derive ( |
422 | Clone, |
423 | Copy, |
424 | Debug, |
425 | PartialOrd, |
426 | PartialEq, |
427 | Eq, |
428 | FromPrimitive, |
429 | Serialize, |
430 | Deserialize, |
431 | )] |
432 | pub enum SegmentationLevel { |
433 | /// No segmentation is signalled. |
434 | Disabled, |
435 | /// Segmentation index is derived from source statistics. |
436 | Simple, |
437 | /// Segmentation index range is derived from source statistics. |
438 | Complex, |
439 | /// Search all segmentation indices. |
440 | Full, |
441 | } |
442 | |
443 | impl fmt::Display for SegmentationLevel { |
444 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { |
445 | write!( |
446 | f, |
447 | " {}" , |
448 | match self { |
449 | SegmentationLevel::Disabled => "Disabled" , |
450 | SegmentationLevel::Simple => "Simple" , |
451 | SegmentationLevel::Complex => "Complex" , |
452 | SegmentationLevel::Full => "Full" , |
453 | } |
454 | ) |
455 | } |
456 | } |
457 | |