blob: 5448243339b3d9fb55c75ec873c6d92fd6ef289d [file] [log] [blame]
// Copyright 2024 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! perf_test is a binary that contains a number of performance tests for our image processing
//! functions.
use cros_codecs::image_processing::*;
#[cfg(feature = "v4l2")]
use std::ptr;
use std::time::Instant;
const K_NUMBER_OF_TEST_CYCLES: u64 = 2000;
const K_WIDTH: usize = 480;
const K_HEIGHT: usize = 288;
const K_VISIBLE_HEIGHT: usize = 270;
#[cfg(feature = "v4l2")]
const K_ADDR_ALIGN: usize = 16;
#[cfg(feature = "v4l2")]
fn test_mm21_to_nv12_perf() {
let test_input = include_bytes!("../src/test_data/puppets-480x270_20230825.mm21.yuv");
let test_expected_output = include_bytes!("../src/test_data/puppets-480x270_20230825.nv12.yuv");
// Byte align test input.
let mut aligned_input = vec![0u8; test_input.len() + K_ADDR_ALIGN];
let mut index = K_ADDR_ALIGN - (ptr::addr_of!(aligned_input[0]) as usize % K_ADDR_ALIGN);
let input_data = aligned_input.get_mut(index..(index + test_input.len())).unwrap();
input_data.copy_from_slice(test_input);
// Byte align test output.
let mut aligned_output = vec![0u8; test_input.len() + K_ADDR_ALIGN];
index = K_ADDR_ALIGN - (ptr::addr_of!(aligned_output[0]) as usize % K_ADDR_ALIGN);
let output_data = aligned_output.get_mut(index..(index + test_input.len())).unwrap();
let (test_y_output, test_uv_output) = output_data.split_at_mut(output_data.len() * 2 / 3);
let start_time = Instant::now();
for _cycle in 0..K_NUMBER_OF_TEST_CYCLES {
let _ = mm21_to_nv12(
&input_data[..(test_input.len() * 2 / 3)],
K_WIDTH,
test_y_output,
K_WIDTH,
&input_data[(test_input.len() * 2 / 3)..],
K_WIDTH,
test_uv_output,
K_WIDTH,
K_WIDTH,
K_HEIGHT as isize,
);
}
let duration_us = ((Instant::now() - start_time).as_micros()) as f64;
let fps = K_NUMBER_OF_TEST_CYCLES as f64 / (duration_us / 1000000f64);
println!("MM21 to NV12 Perf Test Results");
println!("-----------------------------------");
println!("Frames Decoded: {}", K_NUMBER_OF_TEST_CYCLES);
println!("TotalDurationMs: {}", duration_us);
println!("FramesPerSecond: {}\n\n", fps);
assert_eq!(output_data, *test_expected_output);
}
fn test_nv12_to_i420_perf() {
let test_input = include_bytes!("../src/test_data/puppets-480x270_20230825.nv12.yuv");
let test_expected_output = include_bytes!("../src/test_data/puppets-480x270_20230825.i420.yuv");
let mut test_output = vec![0u8; test_input.len()];
let (test_y_output, test_uv_output) = test_output.split_at_mut(test_input.len() * 2 / 3);
let (test_u_output, test_v_output) = test_uv_output.split_at_mut(test_uv_output.len() / 2);
let start_time = Instant::now();
for _cycle in 0..K_NUMBER_OF_TEST_CYCLES {
nv12_to_i420(
&test_input[..test_input.len() * 2 / 3],
K_WIDTH,
test_y_output,
K_WIDTH,
&test_input[test_input.len() * 2 / 3..],
K_WIDTH,
test_u_output,
K_WIDTH / 2,
test_v_output,
K_WIDTH / 2,
K_WIDTH,
K_HEIGHT,
);
}
let duration_us = ((Instant::now() - start_time).as_micros()) as f64;
let fps = K_NUMBER_OF_TEST_CYCLES as f64 / (duration_us / 1000000f64);
println!("NV12 to I420 Perf Test Results");
println!("-----------------------------------");
println!("Frames Decoded: {}", K_NUMBER_OF_TEST_CYCLES);
println!("TotalDurationMs: {}", duration_us);
println!("FramesPerSecond: {}\n\n", fps);
assert_eq!(test_output, *test_expected_output);
}
fn test_i420_to_nv12_perf() {
let test_input = include_bytes!("../src/test_data/puppets-480x270_20230825.i420.yuv");
let test_expected_output = include_bytes!("../src/test_data/puppets-480x270_20230825.nv12.yuv");
let mut test_output = vec![0u8; test_input.len()];
let (test_y_output, test_uv_output) = test_output.split_at_mut(test_input.len() * 2 / 3);
let start_time = Instant::now();
for _cycle in 0..K_NUMBER_OF_TEST_CYCLES {
i420_to_nv12(
&test_input[..test_input.len() * 2 / 3],
K_WIDTH,
test_y_output,
K_WIDTH,
&test_input[(test_input.len() * 2 / 3)..(test_input.len() * 5 / 6)],
K_WIDTH / 2,
&test_input[(test_input.len() * 5 / 6)..],
K_WIDTH / 2,
test_uv_output,
K_WIDTH,
K_WIDTH,
K_HEIGHT,
);
}
let duration_us = ((Instant::now() - start_time).as_micros()) as f64;
let fps = K_NUMBER_OF_TEST_CYCLES as f64 / (duration_us / 1000000f64);
println!("I420 to NV12 Perf Test Results");
println!("-----------------------------------");
println!("Frames Decoded: {}", K_NUMBER_OF_TEST_CYCLES);
println!("TotalDurationMs: {}", duration_us);
println!("FramesPerSecond: {}\n\n", fps);
assert_eq!(test_output, *test_expected_output);
}
fn test_argb_to_nv12_perf() {
let test_input = include_bytes!("../src/test_data/puppets-480x270_20230825.argb");
let test_expected_output = include_bytes!("../src/test_data/puppets-480x270_20230825.nv12.yuv");
let mut test_output = vec![0u8; test_input.len() / 4 * 3 / 2];
let (test_y_output, test_uv_output) = test_output.split_at_mut(test_input.len() / 4);
let start_time = Instant::now();
for _cycle in 0..K_NUMBER_OF_TEST_CYCLES {
argb_to_nv12(
test_input.as_slice(),
K_WIDTH * 4,
test_y_output,
K_WIDTH,
test_uv_output,
K_WIDTH,
K_WIDTH,
K_HEIGHT,
);
}
let duration_us = ((Instant::now() - start_time).as_micros()) as f64;
let fps = K_NUMBER_OF_TEST_CYCLES as f64 / (duration_us / 1000000f64);
println!("ARGB to NV12 Perf Test Results");
println!("-----------------------------------");
println!("Frames Decoded: {}", K_NUMBER_OF_TEST_CYCLES);
println!("TotalDurationMs: {}", duration_us);
println!("FramesPerSecond: {}\n\n", fps);
// There's a tiny amount of error with our 8-bit fixed point conversion versus true floating
// point conversion, so we have a tolerance in our assert.
for y in 0..K_VISIBLE_HEIGHT {
for x in 0..K_WIDTH {
let i = y * K_WIDTH + x;
let expected = test_expected_output[i] as i32;
let actual = test_output[i] as i32;
assert!(
(actual - expected).abs() < 2,
"idx = {} actual = {} expected = {}",
i,
actual,
expected
);
}
}
for y in 0..(K_VISIBLE_HEIGHT / 2) {
for x in 0..K_WIDTH {
let i = y * K_WIDTH + x + K_WIDTH * K_HEIGHT;
let expected = test_expected_output[i] as i32;
let actual = test_output[i] as i32;
assert!(
(actual - expected).abs() < 2,
"idx = {} actual = {} expected = {}",
i,
actual,
expected
);
}
}
}
fn main() {
#[cfg(feature = "android")]
android_logger::init_once(
android_logger::Config::default()
.with_max_level(log::LevelFilter::Trace)
.with_tag("cros-codecs"),
);
#[cfg(not(feature = "android"))]
env_logger::init();
#[cfg(feature = "v4l2")]
test_mm21_to_nv12_perf();
test_nv12_to_i420_perf();
test_i420_to_nv12_perf();
test_argb_to_nv12_perf();
}