1#![allow(non_snake_case)]
18
19use std::{any::Any, marker::PhantomData, sync::Arc};
20
21use teeny_core::{
22 device::program::ArgVisitor,
23 dtype::{Float, FloatBytes},
24 graph::{CustomOp, Shape},
25 model::{RawPtr, RuntimeOp},
26};
27use teeny_macros::kernel;
28use teeny_triton::triton::{
29 types::{AddOffsets, Comparison},
30 *,
31};
32
33#[allow(clippy::erasing_op, clippy::identity_op)]
37#[kernel]
38pub fn detect_decode_forward<T: Triton, D: Float, const BLOCK_A: i32>(
39 boxes_ptr: T::Pointer<D>,
40 anchor_x_ptr: T::Pointer<D>,
41 anchor_y_ptr: T::Pointer<D>,
42 strides_ptr: T::Pointer<D>,
43 out_ptr: T::Pointer<D>,
44 _B: i32,
45 A: i32,
46) where
47 T::I32Tensor: types::Tensor<i32, 1>,
48 T::I32Tensor: Comparison<i32, BoolTensor = T::BoolTensor>,
49 T::Pointer<D>: AddOffsets<i32, 1, T::I32Tensor, Output = T::Tensor<T::Pointer<D>>>,
50{
51 let a_tiles = T::cdiv(A, BLOCK_A);
52 let pid_b = T::program_id(Axis::X) / a_tiles;
53 let a_tile = T::program_id(Axis::X) % a_tiles;
54 let a_start = a_tile * BLOCK_A;
55
56 let a_offs = T::arange(0, BLOCK_A) + a_start;
57 let mask = a_offs.lt(A);
58
59 let zeros = T::zeros::<D>(&[BLOCK_A]);
60
61 let anchor_x = T::load(anchor_x_ptr.add_offsets(a_offs), Some(mask), Some(zeros), &[], None, None, None, false);
62 let anchor_y = T::load(anchor_y_ptr.add_offsets(a_offs), Some(mask), Some(zeros), &[], None, None, None, false);
63 let strides = T::load(strides_ptr.add_offsets(a_offs), Some(mask), Some(zeros), &[], None, None, None, false);
64
65 let base = pid_b * 4 * A;
66 let dx1 = T::load(boxes_ptr.add_offsets(a_offs + (base + 0 * A)), Some(mask), Some(zeros), &[], None, None, None, false);
67 let dy1 = T::load(boxes_ptr.add_offsets(a_offs + (base + 1 * A)), Some(mask), Some(zeros), &[], None, None, None, false);
68 let dx2 = T::load(boxes_ptr.add_offsets(a_offs + (base + 2 * A)), Some(mask), Some(zeros), &[], None, None, None, false);
69 let dy2 = T::load(boxes_ptr.add_offsets(a_offs + (base + 3 * A)), Some(mask), Some(zeros), &[], None, None, None, false);
70
71 let x1 = anchor_x - dx1;
72 let x2 = anchor_x + dx2;
73 let y1 = anchor_y - dy1;
74 let y2 = anchor_y + dy2;
75
76 let half = T::full(&[BLOCK_A], D::from_f64(0.5));
77 let cx = (x1 + x2) * half * strides;
78 let cy = (y1 + y2) * half * strides;
79 let w = (x2 - x1) * strides;
80 let h = (y2 - y1) * strides;
81
82 T::store(out_ptr.add_offsets(a_offs + (base + 0 * A)), cx, Some(mask), &[], None, None);
83 T::store(out_ptr.add_offsets(a_offs + (base + 1 * A)), cy, Some(mask), &[], None, None);
84 T::store(out_ptr.add_offsets(a_offs + (base + 2 * A)), w, Some(mask), &[], None, None);
85 T::store(out_ptr.add_offsets(a_offs + (base + 3 * A)), h, Some(mask), &[], None, None);
86}
87
88pub struct DetectDecodeOp<D: FloatBytes + Send + Sync + 'static> {
97 pub anchor_x: Vec<f32>,
99 pub anchor_y: Vec<f32>,
101 pub strides: Vec<f32>,
103 pub block_a: i32,
105 _phantom: PhantomData<D>,
106}
107
108impl<D: FloatBytes + Send + Sync + 'static> DetectDecodeOp<D> {
109 pub fn new(anchor_x: Vec<f32>, anchor_y: Vec<f32>, strides: Vec<f32>, block_a: i32) -> Self {
111 Self { anchor_x, anchor_y, strides, block_a, _phantom: PhantomData }
112 }
113}
114
115impl<D: FloatBytes + Send + Sync + 'static> CustomOp for DetectDecodeOp<D> {
116 fn name(&self) -> &str { "yolo.detect_decode" }
117
118 fn infer_output_shape(&self, input_shapes: &[&Shape]) -> Shape {
119 input_shapes[0].clone()
121 }
122
123 fn as_any(&self) -> &dyn Any { self }
124
125 fn lower(&self) -> Option<(String, String, String, Arc<dyn RuntimeOp>)> {
126 let kernel = DetectDecodeForward::<D>::new(self.block_a);
127 let runtime_op: Arc<dyn RuntimeOp> = Arc::new(DetectDecodeRuntimeOp::<D>::new(
128 self.anchor_x.clone(),
129 self.anchor_y.clone(),
130 self.strides.clone(),
131 self.block_a,
132 ));
133 Some(("detect_decode_forward".to_string(), kernel.source, "entry_point".to_string(), runtime_op))
134 }
135}
136
137pub struct DetectDecodeRuntimeOp<D: FloatBytes + Send + Sync + 'static> {
146 anchor_x: Vec<f32>,
147 anchor_y: Vec<f32>,
148 strides: Vec<f32>,
149 block_a: i32,
150 _phantom: PhantomData<D>,
151}
152
153impl<D: FloatBytes + Send + Sync + 'static> DetectDecodeRuntimeOp<D> {
154 pub fn new(
156 anchor_x: Vec<f32>,
157 anchor_y: Vec<f32>,
158 strides: Vec<f32>,
159 block_a: i32,
160 ) -> Self {
161 Self { anchor_x, anchor_y, strides, block_a, _phantom: PhantomData }
162 }
163}
164
165impl<D: FloatBytes + Send + Sync + 'static> RuntimeOp for DetectDecodeRuntimeOp<D> {
166 fn n_activation_inputs(&self) -> usize { 1 }
167
168 fn param_shapes(
169 &self,
170 input_shapes: &[&[usize]],
171 _output_shape: &[usize],
172 ) -> Vec<Vec<usize>> {
173 let a = input_shapes[0][2];
175 vec![vec![a], vec![a], vec![a]] }
177
178 fn param_init_data(&self, param_idx: usize) -> Option<Vec<u8>> {
179 let data: &[f32] = match param_idx {
180 0 => &self.anchor_x,
181 1 => &self.anchor_y,
182 2 => &self.strides,
183 _ => return None,
184 };
185 Some(data.iter().flat_map(|&f| D::from_f64(f as f64).to_le_bytes()).collect())
188 }
189
190 fn pack_args(
191 &self,
192 inputs: &[(RawPtr, &[usize])],
193 params: &[RawPtr],
194 output: RawPtr,
195 output_shape: &[usize],
196 _output_row_stride: i32,
197 visitor: &mut dyn ArgVisitor,
198 ) {
199 let b = output_shape[0] as i32;
200 let a = output_shape[2] as i32;
201 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); }
209
210 fn block(&self) -> [u32; 3] { [self.block_a as u32, 1, 1] }
211
212 fn grid(&self, output_shape: &[usize]) -> [u32; 3] {
213 let b = output_shape[0];
214 let a = output_shape[2];
215 let a_tiles = a.div_ceil(self.block_a as usize);
216 [(b * a_tiles) as u32, 1, 1]
217 }
218}