use std::ops::Deref;
use crate::error::{Error as OclError, Result as OclResult};
use crate::core::{OclPrm, CommandQueueProperties};
use crate::standard::{Platform, Device, Context, ProgramBuilder, Program, Queue, Kernel, Buffer,
MemLen, SpatialDims, WorkDims, DeviceSpecifier, KernelBuilder, BufferBuilder};
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))
}
}