1- use std:: { cell:: RefCell , rc:: Rc , time:: Instant } ;
1+ use std:: { cell:: RefCell , num :: NonZeroU8 , rc:: Rc , time:: Instant } ;
22
33use crate :: { WaylandObject , wallpaper:: Wallpaper } ;
44use common:: ipc:: { PixelFormat , Transition , TransitionType } ;
@@ -26,14 +26,30 @@ fn bezier_seq(transition: &Transition, start: f32, end: f32) -> (AnimationSequen
2626}
2727
2828#[ inline( always) ]
29- fn change_byte ( step : u8 , old : & mut u8 , new : & u8 ) {
30- if old. abs_diff ( * new) < step {
31- * old = * new;
32- } else if * old > * new {
33- * old -= step;
34- } else {
35- * old += step;
29+ /// This is calculating the following:
30+ /// ```
31+ /// if old.abs_diff(*new) < step.get() {
32+ /// *old = *new;
33+ /// } else if *old > *new {
34+ /// *old -= step.get();
35+ /// } else {
36+ /// *old += step.get();
37+ /// }
38+ /// ```
39+ /// However, it does so with less branches, making it more amenable to being autovectorized.
40+ /// From my tests, this is almost twice as fast as the above code in x86_64, when compiling without
41+ /// any target features. It only loses slightly (5%) in speed when we compile with avx512. However,
42+ /// avx512 is by itself already pretty fast anyway, and thus benefits less from this.
43+ fn change_byte ( step : NonZeroU8 , old : & mut u8 , new : & u8 ) {
44+ let min = ( * old) . min ( * new) ;
45+ let max = ( * old) . max ( * new) ;
46+ let diff = max - min;
47+ let mut to_add = step. get ( ) . min ( diff) ;
48+
49+ if * old > * new {
50+ to_add = to_add. wrapping_neg ( ) ;
3651 }
52+ * old = old. wrapping_add ( to_add) ;
3753}
3854
3955struct None ;
@@ -75,7 +91,7 @@ pub enum Effect {
7591impl Effect {
7692 pub fn new ( transition : & Transition , pixel_format : PixelFormat , dimensions : ( u32 , u32 ) ) -> Self {
7793 match transition. transition_type {
78- TransitionType :: Simple => Self :: Simple ( Simple :: new ( transition. step . get ( ) ) ) ,
94+ TransitionType :: Simple => Self :: Simple ( Simple :: new ( transition. step ) ) ,
7995 TransitionType :: Fade => Self :: Fade ( Fade :: new ( transition) ) ,
8096 TransitionType :: Outer => Self :: Outer ( Outer :: new ( transition, pixel_format, dimensions) ) ,
8197 TransitionType :: Wipe => Self :: Wipe ( Wipe :: new ( transition, pixel_format, dimensions) ) ,
@@ -104,26 +120,30 @@ impl Effect {
104120 } ;
105121 // we only finish for real if we are doing a None or a Simple transition
106122 if done {
123+ #[ inline( always) ]
124+ const fn new_nonzero ( step : u8 ) -> NonZeroU8 {
125+ NonZeroU8 :: new ( step / 4 + 4 ) . unwrap ( )
126+ }
107127 * self = match self {
108128 Effect :: None ( _) | Effect :: Simple ( _) => return true ,
109- Effect :: Fade ( t) => Effect :: Simple ( Simple :: new ( ( t. step / 4 + 4 ) as u8 ) ) ,
110- Effect :: Wave ( t) => Effect :: Simple ( Simple :: new ( t. step / 4 + 4 ) ) ,
111- Effect :: Wipe ( t) => Effect :: Simple ( Simple :: new ( t. step / 4 + 4 ) ) ,
112- Effect :: Grow ( t) => Effect :: Simple ( Simple :: new ( t. step / 4 + 4 ) ) ,
113- Effect :: Outer ( t) => Effect :: Simple ( Simple :: new ( t. step / 4 + 4 ) ) ,
129+ Effect :: Fade ( t) => Effect :: Simple ( Simple :: new ( new_nonzero ( t. step as u8 ) ) ) ,
130+ Effect :: Wave ( t) => Effect :: Simple ( Simple :: new ( new_nonzero ( t. step . get ( ) ) ) ) ,
131+ Effect :: Wipe ( t) => Effect :: Simple ( Simple :: new ( new_nonzero ( t. step . get ( ) ) ) ) ,
132+ Effect :: Grow ( t) => Effect :: Simple ( Simple :: new ( new_nonzero ( t. step . get ( ) ) ) ) ,
133+ Effect :: Outer ( t) => Effect :: Simple ( Simple :: new ( new_nonzero ( t. step . get ( ) ) ) ) ,
114134 } ;
115135 return false ;
116- }
136+ } ;
117137 done
118138 }
119139}
120140
121141struct Simple {
122- step : u8 ,
142+ step : NonZeroU8 ,
123143}
124144
125145impl Simple {
126- fn new ( step : u8 ) -> Self {
146+ fn new ( step : NonZeroU8 ) -> Self {
127147 Self { step }
128148 }
129149 fn run (
@@ -201,7 +221,7 @@ struct Wave {
201221 circle_radius : f64 ,
202222 a : f64 ,
203223 b : f64 ,
204- step : u8 ,
224+ step : NonZeroU8 ,
205225}
206226
207227impl Wave {
@@ -225,7 +245,7 @@ impl Wave {
225245
226246 let ( seq, start) = bezier_seq ( transition, offset as f32 , max_offset as f32 ) ;
227247
228- let step = transition. step . get ( ) ;
248+ let step = transition. step ;
229249 let channels = pixel_format. channels ( ) as usize ;
230250 let stride = width * channels;
231251 Self {
@@ -349,7 +369,7 @@ struct Wipe {
349369 circle_radius : f64 ,
350370 a : f64 ,
351371 b : f64 ,
352- step : u8 ,
372+ step : NonZeroU8 ,
353373}
354374
355375impl Wipe {
@@ -375,7 +395,7 @@ impl Wipe {
375395 let ( width, height) = ( width as usize , height as usize ) ;
376396 let ( seq, start) = bezier_seq ( transition, offset as f32 , max_offset as f32 ) ;
377397
378- let step = transition. step . get ( ) ;
398+ let step = transition. step ;
379399 let channels = pixel_format. channels ( ) as usize ;
380400 let stride = width * channels;
381401 Self {
@@ -449,7 +469,7 @@ struct Grow {
449469 center_y : usize ,
450470 stride : usize ,
451471 dist_center : f32 ,
452- step : u8 ,
472+ step : NonZeroU8 ,
453473}
454474
455475impl Grow {
@@ -472,7 +492,7 @@ impl Grow {
472492 let ( width, height) = ( width as usize , height as usize ) ;
473493 let ( center_x, center_y) = ( center_x as usize , center_y as usize ) ;
474494
475- let step = transition. step . get ( ) ;
495+ let step = transition. step ;
476496 let channels = pixel_format. channels ( ) as usize ;
477497 let stride = width * channels;
478498 let ( seq, start) = bezier_seq ( transition, 0.0 , dist_end) ;
@@ -545,7 +565,7 @@ struct Outer {
545565 center_y : usize ,
546566 stride : usize ,
547567 dist_center : f32 ,
548- step : u8 ,
568+ step : NonZeroU8 ,
549569}
550570
551571impl Outer {
@@ -566,7 +586,7 @@ impl Outer {
566586 let ( width, height) = ( width as usize , height as usize ) ;
567587 let ( center_x, center_y) = ( center_x as usize , center_y as usize ) ;
568588
569- let step = transition. step . get ( ) ;
589+ let step = transition. step ;
570590 let channels = pixel_format. channels ( ) as usize ;
571591 let stride = width * channels;
572592 let ( seq, start) = bezier_seq ( transition, dist_center, 0.0 ) ;
@@ -629,3 +649,34 @@ impl Outer {
629649 self . start . elapsed ( ) . as_secs_f64 ( ) > self . seq . duration ( )
630650 }
631651}
652+
653+ #[ cfg( test) ]
654+ mod tests {
655+ use std:: num:: NonZeroU8 ;
656+
657+ #[ test]
658+ fn change_byte ( ) {
659+ fn expected ( step : NonZeroU8 , old : & mut u8 , new : & u8 ) {
660+ if old. abs_diff ( * new) < step. get ( ) {
661+ * old = * new;
662+ } else if * old > * new {
663+ * old -= step. get ( ) ;
664+ } else {
665+ * old += step. get ( ) ;
666+ }
667+ }
668+
669+ for old in 0 ..=255 {
670+ for new in 0 ..=255 {
671+ for step in 1 ..=255 {
672+ let step = NonZeroU8 :: new ( step) . unwrap ( ) ;
673+ let mut a = old;
674+ let mut b = old;
675+ expected ( step, & mut a, & new) ;
676+ super :: change_byte ( step, & mut b, & new) ;
677+ assert_eq ! ( a, b, "old: {old}, new: {new}, step: {step}" ) ;
678+ }
679+ }
680+ }
681+ }
682+ }
0 commit comments