use std::convert::Into;
use std::ops::Deref;
use error::{Result as OclResult, Error as OclError};
use core::OclPrm;
use standard::{Platform, Device, Context, ProgramBuilder, Program, Queue, Kernel, Buffer,
MemLen, SpatialDims, WorkDims, DeviceSpecifier};
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;
pub struct ProQueBuilder {
platform: Option<Platform>,
context: Option<Context>,
device_spec: Option<DeviceSpecifier>,
program_builder: Option<ProgramBuilder>,
dims: Option<SpatialDims>,
}
impl ProQueBuilder {
pub fn new() -> ProQueBuilder {
ProQueBuilder {
platform: None,
context: None,
device_spec: None,
program_builder: None,
dims: None,
}
}
pub fn build(&self) -> OclResult<ProQue> {
let program_builder = match self.program_builder {
Some(ref program_builder) => program_builder,
None => return OclError::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."),
};
let platform = match self.platform {
Some(ref plt) => {
assert!(self.context.is_none(), "ocl::ProQueBuilder::build: \
platform and context cannot both be set.");
plt.clone()
},
None => match &self.context {
&Some(ref context) => match context.platform() {
Some(platform) => platform,
None => Platform::default(),
},
&None => Platform::default(),
},
};
let device = match self.device_spec {
Some(ref ds) => {
let device_list = try!(ds.to_device_list(Some(platform)));
if device_list.len() == 1 {
device_list[0]
} else {
return OclError::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()));
}
},
None => Device::first(platform),
};
if DEBUG_PRINT { println!("ProQue::build(): device: {:?}", device); }
let context = match self.context {
Some(ref ctx) => ctx.clone(),
None => {
try!(Context::builder()
.platform(platform)
.devices(device)
.build())
},
};
if DEBUG_PRINT { println!("ProQue::build(): context.devices(): {:?}", context.devices()); }
let queue = try!(Queue::new(&context, device));
let src_strings = try!(program_builder.get_src_strings().map_err(|e| e.to_string()));
let cmplr_opts = try!(program_builder.get_compiler_options().map_err(|e| e.to_string()));
let program = try!(Program::new(
src_strings,
cmplr_opts,
&context,
&vec![device],
));
Ok(ProQue::new(context, queue, program, self.dims.clone()))
}
pub fn platform<'p>(&'p mut self, platform: Platform) -> &'p mut ProQueBuilder {
self.platform = Some(platform);
self
}
pub fn context<'p>(&'p mut self, context: Context) -> &'p mut ProQueBuilder {
self.context = Some(context);
self
}
pub fn device<'p, D: Into<DeviceSpecifier>>(&'p mut self, device_spec: D)
-> &'p mut ProQueBuilder
{
assert!(self.device_spec.is_none(), "ocl::ProQue::devices: Devices already specified");
self.device_spec = Some(device_spec.into());
self
}
pub fn src<'p, S: Into<String>>(&'p mut self, src: S) -> &'p mut ProQueBuilder {
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 {
self.program_builder = Some(Program::builder().src(src))
}
self
}
pub fn prog_bldr<'p>(&'p mut self, program_builder: ProgramBuilder) -> &'p mut ProQueBuilder {
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<'p, D: Into<SpatialDims>>(&'p mut self, dims: D) -> &'p mut ProQueBuilder {
self.dims = Some(dims.into());
self
}
}
#[derive(Clone, Debug)]
pub struct ProQue {
context: Context,
queue: Queue,
program: Program,
dims: Option<SpatialDims>,
}
impl ProQue {
pub fn builder() -> ProQueBuilder {
ProQueBuilder::new()
}
pub fn new<D: Into<SpatialDims>>(context: Context, queue: Queue, program: Program,
dims: Option<D>) -> ProQue
{
ProQue {
context: context,
queue: queue,
program: program,
dims: dims.map(|d| d.into()),
}
}
pub fn create_kernel(&self, name: &str) -> OclResult<Kernel> {
let kernel = try!(Kernel::new(name.to_string(), &self.program, &self.queue));
match self.dims {
Some(d) => Ok(kernel.gws(d)),
None => Ok(kernel),
}
}
pub fn create_buffer<T: OclPrm>(&self) -> OclResult<Buffer<T>> {
let dims = try!(self.dims_result());
Buffer::<T>::new(&self.queue, None, &dims, None)
}
pub fn set_dims<S: Into<SpatialDims>>(&mut self, dims: S) {
self.dims = Some(dims.into());
}
pub fn max_wg_size(&self) -> usize {
self.queue.device().max_wg_size()
}
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 => OclError::err(DIMS_ERR_MSG),
}
}
}
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
}
}