use crate::core::{CommandQueueProperties, OclPrm};
use crate::error::{Error as OclError, Result as OclResult};
use crate::standard::{
Buffer, BufferBuilder, Context, Device, DeviceSpecifier, Kernel, KernelBuilder, MemLen,
Platform, Program, ProgramBuilder, Queue, SpatialDims, WorkDims,
};
use std::ops::Deref;
static DIMS_ERR_MSG: &'static str = "This 'ProQue' has not had any dimensions specified. Use
'ProQueBuilder::dims' during creation or 'ProQue::set_dims' after creation to specify.";
const DEBUG_PRINT: bool = false;
#[derive(Clone, Debug)]
pub struct ProQue {
context: Context,
queue: Queue,
program: Program,
dims: Option<SpatialDims>,
}
impl ProQue {
pub fn builder<'b>() -> ProQueBuilder<'b> {
ProQueBuilder::new()
}
pub fn new<D: Into<SpatialDims>>(
context: Context,
queue: Queue,
program: Program,
dims: Option<D>,
) -> ProQue {
ProQue {
context,
queue,
program,
dims: dims.map(|d| d.into()),
}
}
pub fn kernel_builder<S>(&self, name: S) -> KernelBuilder
where
S: Into<String>,
{
let mut kb = Kernel::builder();
kb.name(name);
kb.program(&self.program);
kb.queue(self.queue.clone());
if let Some(d) = self.dims {
kb.global_work_size(d);
}
kb
}
pub fn create_buffer<T: OclPrm>(&self) -> OclResult<Buffer<T>> {
let len = self.dims_result()?.to_len();
Buffer::<T>::builder()
.queue(self.queue.clone())
.len(len)
.fill_val(Default::default())
.build()
}
pub fn buffer_builder<T: OclPrm>(&self) -> BufferBuilder<T> {
let len = self
.dims_result()
.expect(
"`ProQue` dimensions not specified. Please specify dimensions \
using `::set_dims` before calling this method.",
)
.to_len();
Buffer::<T>::builder().queue(self.queue.clone()).len(len)
}
pub fn set_dims<S: Into<SpatialDims>>(&mut self, dims: S) {
self.dims = Some(dims.into());
}
pub fn max_wg_size(&self) -> OclResult<usize> {
self.queue.device().max_wg_size().map_err(OclError::from)
}
pub fn queue(&self) -> &Queue {
&self.queue
}
pub fn context(&self) -> &Context {
&self.context
}
pub fn program(&self) -> &Program {
&self.program
}
pub fn dims(&self) -> &SpatialDims {
self.dims_result().expect(DIMS_ERR_MSG)
}
pub fn dims_result(&self) -> OclResult<&SpatialDims> {
match self.dims {
Some(ref dims) => Ok(dims),
None => Err(DIMS_ERR_MSG.into()),
}
}
}
impl MemLen for ProQue {
fn to_len(&self) -> usize {
self.dims().to_len()
}
fn to_len_padded(&self, incr: usize) -> usize {
self.dims().to_len_padded(incr)
}
fn to_lens(&self) -> [usize; 3] {
self.dims_result()
.expect("ocl::ProQue::to_lens()")
.to_lens()
.expect("ocl::ProQue::to_lens()")
}
}
impl WorkDims for ProQue {
fn dim_count(&self) -> u32 {
self.dims_result().expect("ProQue::dim_count").dim_count()
}
fn to_work_size(&self) -> Option<[usize; 3]> {
self.dims_result()
.expect("ProQue::to_work_size")
.to_work_size()
}
fn to_work_offset(&self) -> Option<[usize; 3]> {
self.dims_result()
.expect("ProQue::to_work_offset")
.to_work_offset()
}
}
impl Deref for ProQue {
type Target = Queue;
fn deref(&self) -> &Queue {
&self.queue
}
}
#[must_use = "builders do nothing unless '::build' is called"]
pub struct ProQueBuilder<'b> {
platform: Option<Platform>,
context: Option<Context>,
device_spec: Option<DeviceSpecifier>,
program_builder: Option<ProgramBuilder<'b>>,
dims: Option<SpatialDims>,
queue_properties: Option<CommandQueueProperties>,
}
impl<'b> ProQueBuilder<'b> {
pub fn new() -> ProQueBuilder<'b> {
ProQueBuilder {
platform: None,
context: None,
device_spec: None,
program_builder: None,
dims: None,
queue_properties: None,
}
}
pub fn platform(&mut self, platform: Platform) -> &mut ProQueBuilder<'b> {
self.platform = Some(platform);
self
}
pub fn context(&mut self, context: Context) -> &mut ProQueBuilder<'b> {
self.context = Some(context);
self
}
pub fn device<D: Into<DeviceSpecifier>>(&mut self, device_spec: D) -> &mut ProQueBuilder<'b> {
assert!(
self.device_spec.is_none(),
"ocl::ProQue::devices: Devices already specified"
);
self.device_spec = Some(device_spec.into());
self
}
pub fn src<S: Into<String>>(&mut self, src: S) -> &mut ProQueBuilder<'b> {
if self.program_builder.is_some() {
panic!(
"ocl::ProQueBuilder::src: Cannot set src if a 'ProgramBuilder' is already \
defined. Please use the '::program_builder' method for more complex build \
configurations."
);
} else {
let mut pb = Program::builder();
pb.src(src);
self.program_builder = Some(pb);
}
self
}
pub fn prog_bldr(&mut self, program_builder: ProgramBuilder<'b>) -> &mut ProQueBuilder<'b> {
assert!(
self.program_builder.is_none(),
"ProQueBuilder::prog_bldr(): Cannot set the \
'ProgramBuilder' using this method after one has already been set or after '::src' has \
been called."
);
assert!(
program_builder.get_device_spec().is_none(),
"ProQueBuilder::prog_bldr(): The \
'ProgramBuilder' passed may not have any device indices set as they will be unused. \
See 'ProQueBuilder' documentation for more information."
);
self.program_builder = Some(program_builder);
self
}
pub fn dims<D: Into<SpatialDims>>(&mut self, dims: D) -> &mut ProQueBuilder<'b> {
self.dims = Some(dims.into());
self
}
pub fn queue_properties(&mut self, props: CommandQueueProperties) -> &mut ProQueBuilder<'b> {
self.queue_properties = Some(props);
self
}
pub fn build(&self) -> OclResult<ProQue> {
let program_builder = match self.program_builder {
Some(ref program_builder) => program_builder,
None => {
return Err(
"ProQueBuilder::build(): No program builder or kernel source defined. \
OpenCL programs must have some source code to be compiled. Use '::src' to directly \
add source code or '::program_builder' for more complex builds. Please see the \
'ProQueBuilder' and 'ProgramBuilder' documentation for more information."
.into(),
)
}
};
let platform = match self.platform {
Some(ref plt) => {
assert!(
self.context.is_none(),
"ocl::ProQueBuilder::build: \
platform and context cannot both be set."
);
*plt
}
None => match self.context {
Some(ref context) => {
let plat = context.platform()?;
if DEBUG_PRINT {
println!(
"ProQue::build(): plat: {:?}, default: {:?}",
plat,
Platform::default()
);
}
plat.unwrap_or_default()
}
None => Platform::default(),
},
};
let device = match self.device_spec {
Some(ref ds) => {
let device_list = ds.to_device_list(Some(platform))?;
if device_list.len() == 1 {
device_list[0]
} else {
return Err(format!(
"Invalid number of devices specified ({}). Each 'ProQue' \
can only be associated with a single device. Use 'Context', 'Program', and \
'Queue' separately for multi-device configurations.",
device_list.len()
)
.into());
}
}
None => Device::first(platform)?,
};
if DEBUG_PRINT {
println!("ProQue::build(): device: {:?}", device);
}
let context = match self.context {
Some(ref ctx) => {
assert!(ctx.devices().contains(&device));
ctx.clone()
}
None => Context::builder()
.platform(platform)
.devices(device)
.build()?,
};
if DEBUG_PRINT {
println!(
"ProQue::build(): context.devices(): {:?}",
context.devices()
);
}
let queue = Queue::new(&context, device, self.queue_properties)?;
let src_strings = program_builder
.get_src_strings()
.map_err(|e| e.to_string())?;
let cmplr_opts = program_builder
.get_compiler_options()
.map_err(|e| e.to_string())?;
let program = Program::with_source(&context, &src_strings, Some(&[device]), &cmplr_opts)?;
Ok(ProQue::new(context, queue, program, self.dims))
}
}