use cubecl::prelude::*;
use crate::{ComputeScope, Coverage, CubeAxis, Level, Region, Walk, WalkExpand};
#[derive(CubeType)]
pub struct Portion {
regions: Walk,
first: usize,
start: usize,
end: usize,
stride: usize,
run_length: usize,
run: usize,
}
#[cube]
impl Portion {
pub fn touched(&self) -> usize {
self.regions.total()
}
pub fn region(&self, i: usize) -> Region {
self.regions.region(i)
}
pub fn index(&self, i: usize) -> usize {
self.first + i
}
pub fn steps(&self, i: usize) -> (usize, usize) {
let base = (self.first + i) * self.stride;
let from = select(base < self.start, self.start - base, 0);
let to = select(self.end < base + self.stride, self.end - base, self.stride);
(from, to - from)
}
pub fn holders(&self, i: usize) -> (usize, usize) {
let base = (self.first + i) * self.stride;
let first_run = self.run_holding(base);
let last_run = self.run_holding(base + self.stride - 1);
(first_run, last_run + 1 - first_run)
}
pub fn run(&self) -> usize {
self.run
}
pub fn starts_in(&self, i: usize, run: usize) -> bool {
run * self.run_length >= (self.first + i) * self.stride
}
fn run_holding(&self, step: usize) -> usize {
step / self.run_length
}
}
#[cube]
impl Walk {
pub fn portion(self, #[comptime] below: Level) -> Portion {
let instances = comptime!(
self.level
.shared_by()
.expect("Walk::portion: this level distributes no grid as one; say `shared_by`")
);
let stride = self.region(0).over(&below).total();
let steps = self.total() * stride;
let run = match comptime!(self.level.coverage()) {
Coverage::Distribute(ComputeScope::Cube) => CubeAxis::position(CubeAxis::X),
Coverage::Distribute(ComputeScope::Plane) => {
ComputeScope::position(ComputeScope::Plane)
}
Coverage::Distribute(ComputeScope::PlaneGroup { planes }) => {
ComputeScope::position(comptime!(ComputeScope::PlaneGroup { planes }))
}
Coverage::Distribute(ComputeScope::Unit) | Coverage::Walk => {
panic!(
"Walk::portion: only cubes, plane groups or planes share a grid as one index"
)
}
};
let run_length = steps.div_ceil(instances);
let start = (run * run_length).min(steps);
let end = (start + run_length).min(steps);
let first = start / stride;
let touched = select(start < end, (end - 1) / stride + 1 - first, 0);
Portion {
regions: self.range(first, touched),
first,
start,
end,
stride,
run_length,
run,
}
}
}