use crate::Region;
use edgefirst_tensor::{PixelFormat, PixelLayout, TensorMemory};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum DstLowering {
ZeroCopy,
TextureMem,
TexturePbo,
}
pub(super) fn lower_dst(zero_copy_import: bool, dst_mem: TensorMemory) -> DstLowering {
match dst_mem {
TensorMemory::Dma if zero_copy_import => DstLowering::ZeroCopy,
TensorMemory::Pbo => DstLowering::TexturePbo,
_ => DstLowering::TextureMem,
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum ConvertPlan {
SinglePass,
TwoPassPackedRgb,
TwoPassNvPlanar,
}
pub(super) fn plan_convert(
src_fmt: PixelFormat,
dst_fmt: PixelFormat,
lowering: DstLowering,
) -> ConvertPlan {
if lowering != DstLowering::ZeroCopy {
return ConvertPlan::SinglePass;
}
if dst_fmt == PixelFormat::Rgb {
return ConvertPlan::TwoPassPackedRgb;
}
if dst_fmt.layout() == PixelLayout::Planar
&& matches!(
src_fmt,
PixelFormat::Nv12 | PixelFormat::Nv16 | PixelFormat::Nv24
)
{
return ConvertPlan::TwoPassNvPlanar;
}
ConvertPlan::SinglePass
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) struct Viewport {
pub(super) x: i32,
pub(super) y: i32,
pub(super) w: i32,
pub(super) h: i32,
}
#[inline]
pub(super) fn region_to_viewport_top_down(region: Region) -> Viewport {
Viewport {
x: region.x as i32,
y: region.y as i32,
w: region.width as i32,
h: region.height as i32,
}
}
#[allow(dead_code)]
pub(super) fn region_to_viewport(region: Region, parent_h: usize) -> Viewport {
let y_flipped = parent_h.saturating_sub(region.y.saturating_add(region.height));
Viewport {
x: region.x as i32,
y: y_flipped as i32,
w: region.width as i32,
h: region.height as i32,
}
}
#[allow(dead_code)]
pub(super) fn source_uv(region: Option<Region>, src_w: usize, src_h: usize) -> [f32; 4] {
match region {
Some(r) if src_w > 0 && src_h > 0 => [
r.x as f32 / src_w as f32,
r.y as f32 / src_h as f32,
r.width as f32 / src_w as f32,
r.height as f32 / src_h as f32,
],
_ => [0.0, 0.0, 1.0, 1.0],
}
}
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum BatchPath {
OneImport,
Chunked(usize),
PerTileImport,
}
#[allow(dead_code)]
pub(super) fn plan_batch(n_tiles: usize, rows_per_tile: usize, max_rows: usize) -> BatchPath {
if rows_per_tile == 0 || n_tiles == 0 {
return BatchPath::OneImport;
}
if rows_per_tile > max_rows {
return BatchPath::PerTileImport;
}
let tiles_per_chunk = max_rows / rows_per_tile;
if tiles_per_chunk >= n_tiles {
BatchPath::OneImport
} else {
BatchPath::Chunked(tiles_per_chunk)
}
}
#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {
use super::*;
fn region(x: usize, y: usize, w: usize, h: usize) -> Region {
Region::new(x, y, w, h)
}
#[test]
fn viewport_top_down_is_identity() {
let ys: Vec<i32> = (0..3)
.map(|n| region_to_viewport_top_down(region(0, n * 16, 64, 16)).y)
.collect();
assert_eq!(ys, vec![0, 16, 32]);
assert_eq!(
region_to_viewport_top_down(region(10, 4, 20, 8)),
Viewport {
x: 10,
y: 4,
w: 20,
h: 8
}
);
}
#[test]
fn viewport_whole_image_is_origin() {
let vp = region_to_viewport(region(0, 0, 64, 48), 48);
assert_eq!(
vp,
Viewport {
x: 0,
y: 0,
w: 64,
h: 48
}
);
}
#[test]
fn viewport_top_tile_lands_at_top_in_gl_coords() {
let vp = region_to_viewport(region(0, 0, 64, 16), 48);
assert_eq!(
vp,
Viewport {
x: 0,
y: 32,
w: 64,
h: 16
}
);
}
#[test]
fn viewport_bottom_tile_lands_at_origin() {
let vp = region_to_viewport(region(0, 32, 64, 16), 48);
assert_eq!(
vp,
Viewport {
x: 0,
y: 0,
w: 64,
h: 16
}
);
}
#[test]
fn viewport_batch_bands_tile_without_overlap() {
let h = 16;
let ys: Vec<i32> = (0..3)
.map(|n| region_to_viewport(region(0, n * h, 64, h), 3 * h).y)
.collect();
assert_eq!(ys, vec![32, 16, 0]);
}
#[test]
fn viewport_x_offset_preserved() {
let vp = region_to_viewport(region(10, 4, 20, 8), 32);
assert_eq!(
vp,
Viewport {
x: 10,
y: 32 - 12,
w: 20,
h: 8
}
);
}
#[test]
fn source_uv_none_is_identity() {
assert_eq!(source_uv(None, 640, 480), [0.0, 0.0, 1.0, 1.0]);
}
#[test]
fn source_uv_half_rect() {
assert_eq!(
source_uv(Some(region(320, 0, 320, 480)), 640, 480),
[0.5, 0.0, 0.5, 1.0]
);
}
#[test]
fn source_uv_zero_dims_falls_back_to_identity() {
assert_eq!(
source_uv(Some(region(0, 0, 1, 1)), 0, 0),
[0.0, 0.0, 1.0, 1.0]
);
}
#[test]
fn plan_batch_fits_one_import() {
assert_eq!(plan_batch(4, 64, 2048), BatchPath::OneImport);
}
#[test]
fn plan_batch_chunks_when_over_limit() {
assert_eq!(plan_batch(40, 64, 2048), BatchPath::Chunked(32));
}
#[test]
fn plan_batch_single_oversize_tile_is_per_tile() {
assert_eq!(plan_batch(8, 3072, 2048), BatchPath::PerTileImport);
}
#[test]
fn plan_batch_exact_fit_is_one_import() {
assert_eq!(plan_batch(32, 64, 2048), BatchPath::OneImport);
}
#[test]
fn plan_batch_degenerate_inputs() {
assert_eq!(plan_batch(0, 64, 2048), BatchPath::OneImport);
assert_eq!(plan_batch(4, 0, 2048), BatchPath::OneImport);
}
#[test]
fn plan_convert_full_table() {
use DstLowering::*;
use PixelFormat::*;
let nv = [Nv12, Nv16, Nv24];
let non_nv = [Rgba, Bgra, Grey, Yuyv];
for src in nv.iter().chain(&non_nv) {
assert_eq!(
plan_convert(*src, Rgb, ZeroCopy),
ConvertPlan::TwoPassPackedRgb,
"{src:?}->Rgb zero-copy"
);
}
for src in nv {
assert_eq!(
plan_convert(src, PlanarRgb, ZeroCopy),
ConvertPlan::TwoPassNvPlanar
);
assert_eq!(
plan_convert(src, PlanarRgba, ZeroCopy),
ConvertPlan::TwoPassNvPlanar
);
assert_eq!(plan_convert(src, Rgba, ZeroCopy), ConvertPlan::SinglePass);
}
for src in non_nv {
assert_eq!(
plan_convert(src, PlanarRgb, ZeroCopy),
ConvertPlan::SinglePass
);
assert_eq!(plan_convert(src, Rgba, ZeroCopy), ConvertPlan::SinglePass);
}
for lowering in [TextureMem, TexturePbo] {
for src in nv.iter().chain(&non_nv) {
for dst in [Rgba, Bgra, Rgb, PlanarRgb, Grey] {
assert_eq!(
plan_convert(*src, dst, lowering),
ConvertPlan::SinglePass,
"{src:?}->{dst:?} via {lowering:?}"
);
}
}
}
}
#[test]
fn lower_dst_full_table() {
use TensorMemory::*;
assert_eq!(lower_dst(true, Dma), DstLowering::ZeroCopy);
assert_eq!(lower_dst(true, Pbo), DstLowering::TexturePbo);
assert_eq!(lower_dst(true, Mem), DstLowering::TextureMem);
assert_eq!(lower_dst(true, Shm), DstLowering::TextureMem);
assert_eq!(lower_dst(false, Dma), DstLowering::TextureMem);
assert_eq!(lower_dst(false, Pbo), DstLowering::TexturePbo);
assert_eq!(lower_dst(false, Mem), DstLowering::TextureMem);
assert_eq!(lower_dst(false, Shm), DstLowering::TextureMem);
}
}