Program

To ease the safe use of OpenCL programs and kernels, Blaze provides the #[blaze] macro. The blaze macro will turn pseudo-normal Rust extern syntax into a struct that will hold a program and it's various kernels, providing a safe API to call the kernels.

Example

use blaze_rs::prelude::*;
use core::mem::*;

#[blaze(MatrixOps)]
#[link = include_str!("matrixops.cl")]
extern "C" {
    #[link_name = "mul"]
    fn matrix_mul (k: u32, lhs: *const f32, rhs: *const f32, out: *mut MaybeUninit<f32>);
}

Expands to

use blaze_rs::prelude::*;
use core::mem::*;

struct MatrixOps<C: ::blaze::context::Context = ::blaze::context::Global> {
    __blaze_inner__: ::blaze::core::RawProgram,
    __blaze_ctx__: C,
    matrix_mul: ::std::sync::Mutex<::blaze::core::RawKernel>,
}

impl MatrixOps<::blaze::context::Global> {
    #[inline(always)]
    fn new<'a>(options: impl Into<Option<&'a str>>) -> ::blaze::core::Result<Self> {
        Self::new_in(::blaze::context::Global, options)
    }
}

impl<C: ::blaze::context::Context> MatrixOps<C> {
    fn new_in<'a>(ctx: C, options: impl Into<Option<&'a str>>) -> ::blaze::core::Result<Self> {
        let __blaze_ctx__ = ctx;
        let (__blaze_inner__, __blaze_kernels__) =
            ::blaze::core::RawProgram::from_source_in(&__blaze_ctx__, include_str!("matrixops.cl"), options)?;
        let mut matrix_mul = None;
        for __blaze_kernel__ in __blaze_kernels__.into_iter() {
            match __blaze_kernel__.name()?.as_str() {
                "mul" => matrix_mul = unsafe { Some(__blaze_kernel__.clone()) },
                __other => {
                    return Err(::blaze::core::Error::new(
                        ::blaze::core::ErrorType::InvalidKernel,
                        {
                            let res = ::alloc::fmt::format(::core::fmt::Arguments::new_v1(
                                &["unknown kernel \'", "\'"],
                                &[::core::fmt::ArgumentV1::new_display(&__other)],
                            ));
                            res
                        },
                    ))
                }
            }
        }
        let matrix_mul = match matrix_mul {
            Some(__x) => ::std::sync::Mutex::new(__x),
            None => {
                return Err(::blaze::core::Error::new(
                    ::blaze::core::ErrorType::InvalidKernel,
                    "kernel \'matrix_mul\' not found",
                ))
            }
        };
        Ok(Self {
            __blaze_inner__,
            __blaze_ctx__,
            matrix_mul,
        })
    }
}

impl<C: ::blaze::context::Context> ::std::ops::Deref for MatrixOps<C> {
    type Target = ::blaze::core::RawProgram;
    #[inline(always)]
    fn deref(&self) -> &Self::Target {
        &self.__blaze_inner__
    }
}

struct MatrixMul<LHS, RHS, OUT> {
    __blaze_inner__: ::blaze::event::RawEvent,
    lhs: LHS,
    rhs: RHS,
    out: OUT,
}

impl<C: ::blaze::context::Context> MatrixOps<C> {
    unsafe fn matrix_mul<
        LHS: ::core::ops::Deref,
        RHS: ::core::ops::Deref,
        OUT: ::core::ops::DerefMut,
        const N: usize,
    >(
        &self,
        k: u32,
        lhs: LHS,
        rhs: RHS,
        out: OUT,
        global_work_dims: [usize; N],
        local_work_dims: impl Into<Option<[usize; N]>>,
        wait: impl Into<::blaze::event::WaitList>,
    ) -> ::blaze::core::Result<MatrixMul<LHS, RHS, OUT>>
    where
        <LHS as ::core::ops::Deref>::Target: ::blaze::buffer::KernelPointer<f32>,
        <RHS as ::core::ops::Deref>::Target: ::blaze::buffer::KernelPointer<f32>,
        <OUT as ::core::ops::Deref>::Target: ::blaze::buffer::KernelPointer<MaybeUninit<f32>>,
    {
        let mut wait = wait.into();
        let mut __blaze_kernel__ = match self.matrix_mul.lock() {
            Ok(x) => x,
            Err(e) => e.into_inner()
        };
        __blaze_kernel__.set_argument(0u32, &k)?;
        ::blaze::buffer::KernelPointer::set_arg(
            ::core::ops::Deref::deref(&lhs),
            &mut __blaze_kernel__,
            &mut wait,
            1u32,
        )?;
        ::blaze::buffer::KernelPointer::set_arg(
            ::core::ops::Deref::deref(&rhs),
            &mut __blaze_kernel__,
            &mut wait,
            2u32,
        )?;
        ::blaze::buffer::KernelPointer::set_arg(
            ::core::ops::Deref::deref(&out),
            &mut __blaze_kernel__,
            &mut wait,
            3u32,
        )?;
        let __blaze_inner__ = __blaze_kernel__.enqueue_with_context(
            &self.__blaze_ctx__,
            global_work_dims,
            local_work_dims,
            wait,
        )?;
        drop(__blaze_kernel__);
        ::blaze::buffer::KernelPointer::complete(
            ::core::ops::Deref::deref(&lhs),
            &__blaze_inner__,
        )?;
        ::blaze::buffer::KernelPointer::complete(
            ::core::ops::Deref::deref(&rhs),
            &__blaze_inner__,
        )?;
        ::blaze::buffer::KernelPointer::complete(
            ::core::ops::Deref::deref(&out),
            &__blaze_inner__,
        )?;
        Ok(MatrixMul {
            __blaze_inner__,
            lhs,
            rhs,
            out,
        })
    }
}

impl<LHS: ::core::ops::Deref, RHS: ::core::ops::Deref, OUT: ::core::ops::DerefMut>
    ::blaze::event::Event for MatrixMul<LHS, RHS, OUT>
where
    <LHS as ::core::ops::Deref>::Target: ::blaze::buffer::KernelPointer<f32>,
    <RHS as ::core::ops::Deref>::Target: ::blaze::buffer::KernelPointer<f32>,
    <OUT as ::core::ops::Deref>::Target: ::blaze::buffer::KernelPointer<MaybeUninit<f32>>,
{
    type Output = (LHS, RHS, OUT);
    #[inline(always)]
    fn as_raw(&self) -> &::blaze::event::RawEvent {
        &self.__blaze_inner__
    }
    #[inline(always)]
    fn consume(
        self,
        err: Option<::blaze::prelude::Error>,
    ) -> ::blaze::prelude::Result<Self::Output> {
        if let Some(err) = err {
            return Err(err);
        };
        Ok((self.lhs, self.rhs, self.out))
    }
}