From c5b688a24312b759d3e4b27e5058ba917f47e20c Mon Sep 17 00:00:00 2001 From: Radzivon Bartoshyk Date: Sat, 23 Nov 2024 14:54:24 +0000 Subject: [PATCH] Benchmarking, improvements --- Cargo.lock | 172 +++++++++++++++++++++++++++++- Cargo.toml | 2 +- README.md | 21 ++++ app/Cargo.toml | 4 + app/benches/yuv8/main.rs | 220 +++++++++++++++++++++++++++++++++++++++ app/src/main.rs | 49 ++++++--- src/neon/yuv_to_rgba.rs | 1 - 7 files changed, 447 insertions(+), 22 deletions(-) create mode 100644 app/benches/yuv8/main.rs diff --git a/Cargo.lock b/Cargo.lock index 4ca2c05..2e927aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -47,6 +47,7 @@ version = "0.1.0" dependencies = [ "criterion", "image", + "yuv-sys", "yuvutils-rs", ] @@ -102,6 +103,29 @@ dependencies = [ "arrayvec", ] +[[package]] +name = "bindgen" +version = "0.69.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +dependencies = [ + "bitflags 2.6.0", + "cexpr", + "clang-sys", + "itertools 0.12.1", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] + [[package]] name = "bit_field" version = "0.10.2" @@ -114,6 +138,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + [[package]] name = "bitstream-io" version = "2.3.0" @@ -161,6 +191,15 @@ dependencies = [ "once_cell", ] +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-expr" version = "0.15.8" @@ -204,6 +243,17 @@ dependencies = [ "half", ] +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "clap" version = "4.5.20" @@ -323,6 +373,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "exr" version = "1.72.0" @@ -388,6 +448,12 @@ dependencies = [ "weezl", ] +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "half" version = "2.4.1" @@ -416,6 +482,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "image" version = "0.25.5" @@ -535,6 +610,18 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "lebe" version = "0.5.2" @@ -558,6 +645,22 @@ dependencies = [ "once_cell", ] +[[package]] +name = "libloading" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +dependencies = [ + "cfg-if", + "windows-targets", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + [[package]] name = "lock_api" version = "0.4.12" @@ -745,7 +848,7 @@ version = "0.17.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" dependencies = [ - "bitflags", + "bitflags 1.3.2", "crc32fast", "fdeflate", "flate2", @@ -758,11 +861,21 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "prettyplease" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro2" -version = "1.0.85" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -945,6 +1058,25 @@ version = "0.8.50" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + [[package]] name = "ryu" version = "1.0.18" @@ -1007,6 +1139,12 @@ dependencies = [ "serde", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "simd-adler32" version = "0.3.7" @@ -1039,9 +1177,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.66" +version = "2.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" dependencies = [ "proc-macro2", "quote", @@ -1251,6 +1389,18 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + [[package]] name = "winapi-util" version = "0.1.9" @@ -1351,6 +1501,18 @@ dependencies = [ "memchr", ] +[[package]] +name = "yuv-sys" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84a3f589776c945135cd8dee6e1e0d6006748f2808e439200cc242f639b07150" +dependencies = [ + "bindgen", + "cc", + "rayon", + "regex", +] + [[package]] name = "yuvutils-rs" version = "0.5.6" diff --git a/Cargo.toml b/Cargo.toml index c0cd6ef..3f9b2b2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ documentation = "https://github.com/awxkee/yuvutils-rs" categories = ["multimedia::images", "multimedia::video"] homepage = "https://github.com/awxkee/yuvutils-rs" repository = "https://github.com/awxkee/yuvutils-rs" -exclude = ["*.jpg", "assets/*"] +exclude = ["*.jpg", "assets/*", "*.png"] [dependencies] num-traits = "0.2.19" diff --git a/README.md b/README.md index 73ce9c8..994f99e 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,27 @@ ycgco420_to_rgb(&y_plane, y_stride, YuvRange::TV); ``` +## Benchmarks + +YUV 8bit + +```bash +cargo bench --bench yuv8 --manifest-path ./app/Cargo.toml +``` + +Tests performed on the image 5763x3842 + +| | time(NEON) | Time(AVX) | +|------------------------|:----------:|:---------:| +| utils YUV 4:2:0->RGB | 4.95ms | - | +| libyuv YUV 4:2:0->RGB | 5.70ms | - | +| utils YUV 4:2:0->RGBA | 5.56ms | - | +| libyuv YUV 4:2:0->RGBA | 6.13ms | - | +| utils YUV 4:2:2->RGBA | 5.39ms | - | +| libyuv YUV 4:2:2->RGBA | 5.91ms | - | +| utils YUV 4:4:4->RGBA | 5.04ms | - | +| libyuv YUV 4:4:4->RGBA | 4.82ms | - | + This project is licensed under either of - BSD-3-Clause License (see [LICENSE](LICENSE.md)) diff --git a/app/Cargo.toml b/app/Cargo.toml index 64982e0..c8ecb64 100644 --- a/app/Cargo.toml +++ b/app/Cargo.toml @@ -6,7 +6,11 @@ edition = "2021" [dependencies] yuvutils-rs = { path = "..", features = [] } image = "0.25.5" +yuv-sys = "0.3.6" [dev-dependencies] criterion = "0.5.1" +[[bench]] +name = "yuv8" +harness = false diff --git a/app/benches/yuv8/main.rs b/app/benches/yuv8/main.rs new file mode 100644 index 0000000..9beb0f3 --- /dev/null +++ b/app/benches/yuv8/main.rs @@ -0,0 +1,220 @@ +/* + * Copyright (c) Radzivon Bartoshyk, 11/2024. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +use criterion::{criterion_group, criterion_main, Criterion}; +use image::{GenericImageView, ImageReader}; +use yuv_sys::{rs_I420ToABGR, rs_I420ToRGB24, rs_I422ToABGR, rs_I444ToABGR}; +use yuvutils_rs::{ + rgb_to_yuv420, rgb_to_yuv422, rgb_to_yuv444, yuv420_to_rgb, yuv420_to_rgba, yuv422_to_rgba, + yuv444_to_rgba, YuvChromaSubsampling, YuvPlanarImageMut, YuvRange, YuvStandardMatrix, +}; + +pub fn criterion_benchmark(c: &mut Criterion) { + let img = ImageReader::open("../assets/test_image_2.jpg") + .unwrap() + .decode() + .unwrap(); + let dimensions = img.dimensions(); + let components = 3; + let stride = dimensions.0 as usize * components; + let src_bytes = img.as_bytes(); + + let mut planar_image = + YuvPlanarImageMut::::alloc(dimensions.0, dimensions.1, YuvChromaSubsampling::Yuv420); + + rgb_to_yuv420( + &mut planar_image, + &src_bytes, + stride as u32, + YuvRange::Limited, + YuvStandardMatrix::Bt601, + ) + .unwrap(); + + let fixed_planar = planar_image.to_fixed(); + + c.bench_function("yuvutils YUV 4:2:0 -> RGB", |b| { + let mut rgb_bytes = vec![0u8; dimensions.0 as usize * 3 * dimensions.1 as usize]; + b.iter(|| { + yuv420_to_rgb( + &fixed_planar, + &mut rgb_bytes, + dimensions.0 * 3u32, + YuvRange::Limited, + YuvStandardMatrix::Bt601, + ) + .unwrap(); + }) + }); + + c.bench_function("libyuv YUV 4:2:0 -> BGR24", |b| { + let mut rgb_bytes = vec![0u8; dimensions.0 as usize * 3 * dimensions.1 as usize]; + b.iter(|| unsafe { + rs_I420ToRGB24( + fixed_planar.y_plane.as_ptr(), + fixed_planar.y_stride as i32, + fixed_planar.u_plane.as_ptr(), + fixed_planar.u_stride as i32, + fixed_planar.v_plane.as_ptr(), + fixed_planar.v_stride as i32, + rgb_bytes.as_mut_ptr(), + dimensions.0 as i32 * 3i32, + fixed_planar.width as i32, + fixed_planar.height as i32, + ); + }) + }); + + c.bench_function("yuvutils YUV 4:2:0 -> RGBA", |b| { + let mut rgb_bytes = vec![0u8; dimensions.0 as usize * 4 * dimensions.1 as usize]; + b.iter(|| { + yuv420_to_rgba( + &fixed_planar, + &mut rgb_bytes, + dimensions.0 * 4u32, + YuvRange::Limited, + YuvStandardMatrix::Bt601, + ) + .unwrap(); + }) + }); + + c.bench_function("libyuv YUV 4:2:0 -> RGBA", |b| { + let mut rgb_bytes = vec![0u8; dimensions.0 as usize * 4 * dimensions.1 as usize]; + b.iter(|| unsafe { + rs_I420ToABGR( + fixed_planar.y_plane.as_ptr(), + fixed_planar.y_stride as i32, + fixed_planar.u_plane.as_ptr(), + fixed_planar.u_stride as i32, + fixed_planar.v_plane.as_ptr(), + fixed_planar.v_stride as i32, + rgb_bytes.as_mut_ptr(), + dimensions.0 as i32 * 4i32, + fixed_planar.width as i32, + fixed_planar.height as i32, + ); + }) + }); + + let mut planar_image422 = + YuvPlanarImageMut::::alloc(dimensions.0, dimensions.1, YuvChromaSubsampling::Yuv422); + + rgb_to_yuv422( + &mut planar_image422, + &src_bytes, + stride as u32, + YuvRange::Limited, + YuvStandardMatrix::Bt601, + ) + .unwrap(); + + let fixed_planar422 = planar_image422.to_fixed(); + + c.bench_function("yuvutils YUV 4:2:2 -> RGBA", |b| { + let mut rgb_bytes = vec![0u8; dimensions.0 as usize * 4 * dimensions.1 as usize]; + b.iter(|| { + yuv422_to_rgba( + &fixed_planar422, + &mut rgb_bytes, + dimensions.0 * 4u32, + YuvRange::Limited, + YuvStandardMatrix::Bt601, + ) + .unwrap(); + }) + }); + + c.bench_function("libyuv YUV 4:2:2 -> RGBA", |b| { + let mut rgb_bytes = vec![0u8; dimensions.0 as usize * 4 * dimensions.1 as usize]; + b.iter(|| unsafe { + rs_I422ToABGR( + fixed_planar422.y_plane.as_ptr(), + fixed_planar422.y_stride as i32, + fixed_planar422.u_plane.as_ptr(), + fixed_planar422.u_stride as i32, + fixed_planar422.v_plane.as_ptr(), + fixed_planar422.v_stride as i32, + rgb_bytes.as_mut_ptr(), + dimensions.0 as i32 * 4i32, + fixed_planar422.width as i32, + fixed_planar422.height as i32, + ); + }) + }); + + let mut planar_image444 = + YuvPlanarImageMut::::alloc(dimensions.0, dimensions.1, YuvChromaSubsampling::Yuv444); + + rgb_to_yuv444( + &mut planar_image444, + &src_bytes, + stride as u32, + YuvRange::Limited, + YuvStandardMatrix::Bt601, + ) + .unwrap(); + + let fixed_planar444 = planar_image444.to_fixed(); + + c.bench_function("yuvutils YUV 4:4:4 -> RGBA", |b| { + let mut rgb_bytes = vec![0u8; dimensions.0 as usize * 4 * dimensions.1 as usize]; + b.iter(|| { + yuv444_to_rgba( + &fixed_planar444, + &mut rgb_bytes, + dimensions.0 * 4u32, + YuvRange::Limited, + YuvStandardMatrix::Bt601, + ) + .unwrap(); + }) + }); + + c.bench_function("libyuv YUV 4:4:4 -> RGBA", |b| { + let mut rgb_bytes = vec![0u8; dimensions.0 as usize * 4 * dimensions.1 as usize]; + b.iter(|| unsafe { + rs_I444ToABGR( + fixed_planar444.y_plane.as_ptr(), + fixed_planar444.y_stride as i32, + fixed_planar444.u_plane.as_ptr(), + fixed_planar444.u_stride as i32, + fixed_planar444.v_plane.as_ptr(), + fixed_planar444.v_stride as i32, + rgb_bytes.as_mut_ptr(), + dimensions.0 as i32 * 4i32, + fixed_planar444.width as i32, + fixed_planar444.height as i32, + ); + }) + }); +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); diff --git a/app/src/main.rs b/app/src/main.rs index 65cb30e..35c0477 100644 --- a/app/src/main.rs +++ b/app/src/main.rs @@ -30,6 +30,7 @@ use image::{ColorType, EncodableLayout, GenericImageView, ImageReader}; use std::fs::File; use std::io::Read; use std::time::Instant; +use yuv_sys::rs_I420ToRGB24; use yuvutils_rs::{ rgb_to_yuv420, rgb_to_yuv420_p16, yuv420_p16_to_rgb16, yuv420_to_rgb, YuvBiPlanarImageMut, YuvBytesPacking, YuvChromaSubsampling, YuvEndianness, YuvPlanarImageMut, YuvRange, @@ -92,20 +93,17 @@ fn main() { ); let mut planar_image = - YuvPlanarImageMut::::alloc(width as u32, height as u32, YuvChromaSubsampling::Yuv420); + YuvPlanarImageMut::::alloc(width as u32, height as u32, YuvChromaSubsampling::Yuv420); - let mut bytes_16: Vec = src_bytes.iter().map(|&x| (x as u16) << 4).collect(); + // let mut bytes_16: Vec = src_bytes.iter().map(|&x| (x as u16) << 4).collect(); let start_time = Instant::now(); - rgb_to_yuv420_p16( + rgb_to_yuv420( &mut planar_image, - &bytes_16, + &src_bytes, rgba_stride as u32, - 12, YuvRange::Limited, - YuvStandardMatrix::Bt709, - YuvEndianness::LittleEndian, - YuvBytesPacking::LeastSignificantBytes, + YuvStandardMatrix::Bt601, ) .unwrap(); // bytes_16.fill(0); @@ -253,21 +251,42 @@ fn main() { // ) // .unwrap(); let start_time = Instant::now(); - yuv420_p16_to_rgb16( + yuv420_to_rgb( &fixed_planar, - &mut bytes_16, + &mut rgba, rgba_stride as u32, - 12, YuvRange::Limited, - YuvStandardMatrix::Bt709, - YuvEndianness::LittleEndian, - YuvBytesPacking::LeastSignificantBytes, + YuvStandardMatrix::Bt601, ) .unwrap(); println!("Backward time: {:?}", start_time.elapsed()); - rgba = bytes_16.iter().map(|&x| (x >> 4) as u8).collect(); + let start_time = Instant::now(); + + unsafe { + rs_I420ToRGB24(fixed_planar.y_plane.as_ptr(), + fixed_planar.y_stride as i32, + fixed_planar.u_plane.as_ptr(), + fixed_planar.u_stride as i32, + fixed_planar.v_plane.as_ptr(), + fixed_planar.v_stride as i32, + rgba.as_mut_ptr(), + rgba_stride as i32, + fixed_planar.width as i32, + fixed_planar.height as i32); + } + + println!("Backward LIBYUV time: {:?}", start_time.elapsed()); + + rgba.chunks_exact_mut(3) + .for_each(|chunk| { + let b = chunk[0]; + chunk[0] = chunk[2]; + chunk[2] = b; + }); + + // rgba = bytes_16.iter().map(|&x| (x >> 4) as u8).collect(); image::save_buffer( "converted_sharp15.jpg", diff --git a/src/neon/yuv_to_rgba.rs b/src/neon/yuv_to_rgba.rs index 8135fa4..3360e4e 100644 --- a/src/neon/yuv_to_rgba.rs +++ b/src/neon/yuv_to_rgba.rs @@ -281,7 +281,6 @@ pub(crate) unsafe fn neon_yuv_to_rgba_row_rdm< ProcessedOffset { cx, ux: uv_x } } -#[inline(always)] pub(crate) unsafe fn neon_yuv_to_rgba_row< const PRECISION: i32, const DESTINATION_CHANNELS: u8,