#![allow(non_snake_case)]
use std::{any::Any, marker::PhantomData, sync::Arc};
use teeny_core::{
device::program::ArgVisitor,
dtype::{Float, FloatBytes},
graph::{CustomOp, Shape},
model::{RawPtr, RuntimeOp},
};
use teeny_macros::kernel;
use teeny_triton::triton::{
types::{AddOffsets, Comparison},
*,
};
#[allow(clippy::erasing_op, clippy::identity_op)]
#[kernel]
pub fn detect_decode_forward<T: Triton, D: Float, const BLOCK_A: i32>(
boxes_ptr: T::Pointer<D>,
anchor_x_ptr: T::Pointer<D>,
anchor_y_ptr: T::Pointer<D>,
strides_ptr: T::Pointer<D>,
out_ptr: T::Pointer<D>,
_B: i32,
A: i32,
) where
T::I32Tensor: types::Tensor<i32, 1>,
T::I32Tensor: Comparison<i32, BoolTensor = T::BoolTensor>,
T::Pointer<D>: AddOffsets<i32, 1, T::I32Tensor, Output = T::Tensor<T::Pointer<D>>>,
{
let a_tiles = T::cdiv(A, BLOCK_A);
let pid_b = T::program_id(Axis::X) / a_tiles;
let a_tile = T::program_id(Axis::X) % a_tiles;
let a_start = a_tile * BLOCK_A;
let a_offs = T::arange(0, BLOCK_A) + a_start;
let mask = a_offs.lt(A);
let zeros = T::zeros::<D>(&[BLOCK_A]);
let anchor_x = T::load(anchor_x_ptr.add_offsets(a_offs), Some(mask), Some(zeros), &[], None, None, None, false);
let anchor_y = T::load(anchor_y_ptr.add_offsets(a_offs), Some(mask), Some(zeros), &[], None, None, None, false);
let strides = T::load(strides_ptr.add_offsets(a_offs), Some(mask), Some(zeros), &[], None, None, None, false);
let base = pid_b * 4 * A;
let dx1 = T::load(boxes_ptr.add_offsets(a_offs + (base + 0 * A)), Some(mask), Some(zeros), &[], None, None, None, false);
let dy1 = T::load(boxes_ptr.add_offsets(a_offs + (base + 1 * A)), Some(mask), Some(zeros), &[], None, None, None, false);
let dx2 = T::load(boxes_ptr.add_offsets(a_offs + (base + 2 * A)), Some(mask), Some(zeros), &[], None, None, None, false);
let dy2 = T::load(boxes_ptr.add_offsets(a_offs + (base + 3 * A)), Some(mask), Some(zeros), &[], None, None, None, false);
let x1 = anchor_x - dx1;
let x2 = anchor_x + dx2;
let y1 = anchor_y - dy1;
let y2 = anchor_y + dy2;
let half = T::full(&[BLOCK_A], D::from_f64(0.5));
let cx = (x1 + x2) * half * strides;
let cy = (y1 + y2) * half * strides;
let w = (x2 - x1) * strides;
let h = (y2 - y1) * strides;
T::store(out_ptr.add_offsets(a_offs + (base + 0 * A)), cx, Some(mask), &[], None, None);
T::store(out_ptr.add_offsets(a_offs + (base + 1 * A)), cy, Some(mask), &[], None, None);
T::store(out_ptr.add_offsets(a_offs + (base + 2 * A)), w, Some(mask), &[], None, None);
T::store(out_ptr.add_offsets(a_offs + (base + 3 * A)), h, Some(mask), &[], None, None);
}
pub struct DetectDecodeOp<D: FloatBytes + Send + Sync + 'static> {
pub anchor_x: Vec<f32>,
pub anchor_y: Vec<f32>,
pub strides: Vec<f32>,
pub block_a: i32,
_phantom: PhantomData<D>,
}
impl<D: FloatBytes + Send + Sync + 'static> DetectDecodeOp<D> {
pub fn new(anchor_x: Vec<f32>, anchor_y: Vec<f32>, strides: Vec<f32>, block_a: i32) -> Self {
Self { anchor_x, anchor_y, strides, block_a, _phantom: PhantomData }
}
}
impl<D: FloatBytes + Send + Sync + 'static> CustomOp for DetectDecodeOp<D> {
fn name(&self) -> &str { "yolo.detect_decode" }
fn infer_output_shape(&self, input_shapes: &[&Shape]) -> Shape {
input_shapes[0].clone()
}
fn as_any(&self) -> &dyn Any { self }
fn lower(&self) -> Option<(String, String, String, Arc<dyn RuntimeOp>)> {
let kernel = DetectDecodeForward::<D>::new(self.block_a);
let runtime_op: Arc<dyn RuntimeOp> = Arc::new(DetectDecodeRuntimeOp::<D>::new(
self.anchor_x.clone(),
self.anchor_y.clone(),
self.strides.clone(),
self.block_a,
));
Some(("detect_decode_forward".to_string(), kernel.source, "entry_point".to_string(), runtime_op))
}
}
pub struct DetectDecodeRuntimeOp<D: FloatBytes + Send + Sync + 'static> {
anchor_x: Vec<f32>,
anchor_y: Vec<f32>,
strides: Vec<f32>,
block_a: i32,
_phantom: PhantomData<D>,
}
impl<D: FloatBytes + Send + Sync + 'static> DetectDecodeRuntimeOp<D> {
pub fn new(
anchor_x: Vec<f32>,
anchor_y: Vec<f32>,
strides: Vec<f32>,
block_a: i32,
) -> Self {
Self { anchor_x, anchor_y, strides, block_a, _phantom: PhantomData }
}
}
impl<D: FloatBytes + Send + Sync + 'static> RuntimeOp for DetectDecodeRuntimeOp<D> {
fn n_activation_inputs(&self) -> usize { 1 }
fn param_shapes(
&self,
input_shapes: &[&[usize]],
_output_shape: &[usize],
) -> Vec<Vec<usize>> {
let a = input_shapes[0][2];
vec![vec![a], vec![a], vec![a]] }
fn param_init_data(&self, param_idx: usize) -> Option<Vec<u8>> {
let data: &[f32] = match param_idx {
0 => &self.anchor_x,
1 => &self.anchor_y,
2 => &self.strides,
_ => return None,
};
Some(data.iter().flat_map(|&f| D::from_f64(f as f64).to_le_bytes()).collect())
}
fn pack_args(
&self,
inputs: &[(RawPtr, &[usize])],
params: &[RawPtr],
output: RawPtr,
output_shape: &[usize],
_output_row_stride: i32,
visitor: &mut dyn ArgVisitor,
) {
let b = output_shape[0] as i32;
let a = output_shape[2] as i32;
visitor.visit_ptr(inputs[0].0); visitor.visit_ptr(params[0]); visitor.visit_ptr(params[1]); visitor.visit_ptr(params[2]); visitor.visit_ptr(output); visitor.visit_i32(b); visitor.visit_i32(a); }
fn block(&self) -> [u32; 3] { [self.block_a as u32, 1, 1] }
fn grid(&self, output_shape: &[usize]) -> [u32; 3] {
let b = output_shape[0];
let a = output_shape[2];
let a_tiles = a.div_ceil(self.block_a as usize);
[(b * a_tiles) as u32, 1, 1]
}
}