use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
use std::task::{Context, Poll, Waker};
use cubecl::bytes::Bytes;
use cubecl::client::ComputeClient;
use cubecl::prelude::*;
use cubecl::server::{Handle, ServerError};
use super::kernels::gpu_pack_wire;
use super::{BLOCK_1D, Depth, MAX_GRID_1D};
use crate::denoiser::{FrameOutput, OutputFormat};
pub(crate) type ReadFuture = Pin<Box<dyn Future<Output = Result<Vec<Bytes>, ServerError>> + Send>>;
pub struct Pending<R: Runtime> {
pub(super) fut: ReadFuture,
pub(super) channels: u32,
pub(super) stored_ch: u32,
pub(super) pixels: usize,
pub(super) format: OutputFormat,
pub(super) _marker: PhantomData<R>,
}
impl<R: Runtime> Pending<R> {
pub(crate) fn new(
fut: ReadFuture,
channels: u32,
stored_ch: u32,
pixels: usize,
format: OutputFormat,
) -> Self {
Self {
fut,
channels,
stored_ch,
pixels,
format,
_marker: PhantomData,
}
}
pub fn wait(self) -> Result<FrameOutput, anyhow::Error> {
let mut out = empty_output(self.pixels, self.channels, self.format);
self.wait_into(&mut out)?;
Ok(out)
}
pub fn wait_into(self, dst: &mut FrameOutput) -> Result<(), anyhow::Error> {
let (pixels, channels, stored_ch, format) = (self.pixels, self.channels, self.stored_ch, self.format);
let bytes = cubecl::future::block_on(self.fut)?.remove(0);
unpack_into(&bytes, pixels, channels, stored_ch, format, dst);
Ok(())
}
pub fn try_wait(mut self) -> Result<TryWait<R>, anyhow::Error> {
let waker = Waker::noop();
let mut cx = Context::from_waker(waker);
match self.fut.as_mut().poll(&mut cx) {
Poll::Ready(Ok(mut bytes)) => {
let bytes = bytes.remove(0);
let mut out = empty_output(self.pixels, self.channels, self.format);
unpack_into(
&bytes,
self.pixels,
self.channels,
self.stored_ch,
self.format,
&mut out,
);
Ok(TryWait::Ready(out))
},
Poll::Ready(Err(e)) => Err(e.into()),
Poll::Pending => Ok(TryWait::NotReady(self)),
}
}
}
pub(super) fn empty_output(pixels: usize, channels: u32, format: OutputFormat) -> FrameOutput {
let samples = pixels * channels as usize;
match format {
OutputFormat::F32 => FrameOutput::F32(Vec::with_capacity(samples)),
OutputFormat::Wire { depth } => {
FrameOutput::Wire(Vec::with_capacity(samples * depth.bytes_per_sample()))
},
}
}
fn unpack_into(
bytes: &Bytes,
pixels: usize,
channels: u32,
stored_ch: u32,
format: OutputFormat,
dst: &mut FrameOutput,
) {
let samples = pixels * channels as usize;
match (format, dst) {
(OutputFormat::F32, FrameOutput::F32(out)) => {
unpack_bytes_into(bytes, pixels, channels, stored_ch, out);
},
(OutputFormat::Wire { depth }, FrameOutput::Wire(out)) => {
unpack_wire_into(bytes, samples * depth.bytes_per_sample(), out);
},
(OutputFormat::F32, dst) => {
let mut out = Vec::new();
unpack_bytes_into(bytes, pixels, channels, stored_ch, &mut out);
*dst = FrameOutput::F32(out);
},
(OutputFormat::Wire { depth }, dst) => {
let mut out = Vec::new();
unpack_wire_into(bytes, samples * depth.bytes_per_sample(), &mut out);
*dst = FrameOutput::Wire(out);
},
}
}
pub enum TryWait<R: Runtime> {
Ready(FrameOutput),
NotReady(Pending<R>),
}
pub(crate) fn start_readback<R: Runtime>(
client: &ComputeClient<R>,
handle: Handle,
wire_dst: Option<&Handle>,
channels: u32,
stored_ch: u32,
pixels: usize,
format: OutputFormat,
) -> Pending<R> {
let handle = match (format, wire_dst) {
(OutputFormat::F32, _) => handle,
(OutputFormat::Wire { depth }, Some(dst)) => {
pack_wire(client, &handle, dst, channels, stored_ch, pixels, depth);
dst.clone()
},
(OutputFormat::Wire { .. }, None) => {
unreachable!("a wire-mode denoiser allocates its wire buffers at construction")
},
};
let client = client.clone();
let fut = Box::pin(async move { client.read_async(vec![handle]).await });
Pending::new(fut, channels, stored_ch, pixels, format)
}
fn pack_wire<R: Runtime>(
client: &ComputeClient<R>,
src: &Handle,
dst: &Handle,
channels: u32,
stored_ch: u32,
pixels: usize,
depth: Depth,
) {
let pack = depth.wire_pack();
let samples = pixels as u32 * channels;
let words = samples.div_ceil(pack.samples_per_word());
let split_planes = wire_splits_planes(channels);
let outer = if split_planes { pixels as u32 } else { channels };
let grid = words.div_ceil(BLOCK_1D).clamp(1, MAX_GRID_1D);
let total_threads = grid * BLOCK_1D;
unsafe {
gpu_pack_wire::launch_unchecked::<R>(
client,
CubeCount::new_1d(grid),
CubeDim::new_1d(BLOCK_1D),
ArrayArg::from_raw_parts(src.clone(), pixels * stored_ch as usize),
ArrayArg::from_raw_parts(dst.clone(), words as usize),
pack.max(),
pixels as u32,
channels,
stored_ch,
outer,
split_planes,
pack.samples_per_word(),
words,
total_threads,
);
}
}
pub(crate) fn wire_splits_planes(channels: u32) -> bool {
channels == 2
}
fn unpack_wire_into(bytes: &Bytes, len: usize, dst: &mut Vec<u8>) {
dst.clear();
dst.extend_from_slice(&bytes[..len]);
}
fn unpack_bytes_into(bytes: &Bytes, pixels: usize, channels: u32, stored_ch: u32, dst: &mut Vec<f32>) {
let data = f32::from_bytes(bytes);
unpack_frame(data, pixels, channels as usize, stored_ch as usize, dst);
}
pub(super) fn unpack_frame(
data: &[f32],
pixels: usize,
channels: usize,
stored_ch: usize,
dst: &mut Vec<f32>,
) {
dst.clear();
if channels == stored_ch {
dst.extend_from_slice(data);
} else {
dst.reserve(pixels * channels);
for pixel in 0..pixels {
let src = pixel * stored_ch;
dst.extend_from_slice(&data[src..src + channels]);
}
}
}