use std;
use std::ops::{Deref, DerefMut};
use std::ffi::CString;
use std::io::Read;
use std::fs::File;
use std::path::PathBuf;
use std::collections::HashSet;
use std::convert::Into;
use crate::core::{self, Result as OclCoreResult, Program as ProgramCore, Context as ContextCore,
ProgramInfo, ProgramInfoResult, ProgramBuildInfo, ProgramBuildInfoResult};
#[cfg(feature = "opencl_version_2_1")]
use core::ClVersions;
use crate::error::{Result as OclResult, Error as OclError};
use crate::standard::{Context, Device, DeviceSpecifier};
#[derive(Clone, Debug)]
pub struct Program(ProgramCore);
impl Program {
pub fn builder<'b>() -> ProgramBuilder<'b> {
ProgramBuilder::new()
}
pub fn with_source(context: &ContextCore, src_strings: &[CString],
devices: Option<&[Device]>, cmplr_opts: &CString) -> OclResult<Program> {
let program = core::create_program_with_source(context, src_strings)?;
core::build_program(&program, devices, cmplr_opts, None, None)?;
Ok(Program(program))
}
pub fn with_binary(context: &ContextCore, devices: &[Device],
binaries: &[&[u8]], cmplr_opts: &CString) -> OclResult<Program> {
let program = core::create_program_with_binary(context, devices, binaries)?;
core::build_program(&program, Some(devices), cmplr_opts, None, None)?;
Ok(Program(program))
}
#[cfg(feature = "opencl_version_2_1")]
pub fn with_il(il: &[u8], devices: Option<&[Device]>, cmplr_opts: &CString,
context: &ContextCore) -> OclResult<Program> {
let device_versions = context.device_versions()?;
let program = core::create_program_with_il(context, il, Some(&device_versions))?;
core::build_program(&program, devices, cmplr_opts, None, None)?;
Ok(Program(program))
}
#[inline]
pub fn as_core(&self) -> &ProgramCore {
&self.0
}
pub fn info(&self, info_kind: ProgramInfo) -> OclCoreResult<ProgramInfoResult> {
core::get_program_info(&self.0, info_kind)
}
pub fn build_info(&self, device: Device, info_kind: ProgramBuildInfo)
-> OclCoreResult<ProgramBuildInfoResult> {
core::get_program_build_info(&self.0, &device, info_kind)
}
fn fmt_info(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.debug_struct("Program")
.field("ReferenceCount", &self.info(ProgramInfo::ReferenceCount))
.field("Context", &self.info(ProgramInfo::Context))
.field("NumDevices", &self.info(ProgramInfo::NumDevices))
.field("Devices", &self.info(ProgramInfo::Devices))
.field("Source", &self.info(ProgramInfo::Source))
.field("BinarySizes", &self.info(ProgramInfo::BinarySizes))
.field("Binaries", &self.info(ProgramInfo::Binaries))
.field("NumKernels", &self.info(ProgramInfo::NumKernels))
.field("KernelNames", &self.info(ProgramInfo::KernelNames))
.finish()
}
}
impl From<ProgramCore> for Program {
fn from(core: ProgramCore) -> Program {
Program(core)
}
}
impl std::fmt::Display for Program {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.fmt_info(f)
}
}
impl Deref for Program {
type Target = ProgramCore;
fn deref(&self) -> &ProgramCore {
&self.0
}
}
impl DerefMut for Program {
fn deref_mut(&mut self) -> &mut ProgramCore {
&mut self.0
}
}
#[derive(Clone, Debug)]
pub enum BuildOpt {
CmplrDefine { ident: String, val: String },
CmplrInclDir { path: String },
CmplrOther(String),
IncludeDefine { ident: String, val: String },
IncludeRaw(String),
IncludeRawEof(String),
}
impl BuildOpt {
pub fn cmplr_def<S: Into<String>>(ident: S, val: i32) -> BuildOpt {
BuildOpt::CmplrDefine {
ident: ident.into(),
val: val.to_string(),
}
}
pub fn include_def<S: Into<String>>(ident: S, val: String) -> BuildOpt {
BuildOpt::IncludeDefine {
ident: ident.into(),
val,
}
}
}
#[allow(dead_code)]
#[derive(Clone, Debug)]
enum CreateWith<'b> {
None,
Source(Vec<PathBuf>),
Binaries(&'b[&'b [u8]]),
Il(&'b [u8]),
}
#[must_use = "builders do nothing unless '::build' is called"]
#[derive(Clone, Debug)]
pub struct ProgramBuilder<'b> {
options: Vec<BuildOpt>,
with: CreateWith<'b>,
device_spec: Option<DeviceSpecifier>,
}
impl<'b> ProgramBuilder<'b> {
pub fn new() -> ProgramBuilder<'b> {
ProgramBuilder {
options: Vec::with_capacity(64),
with: CreateWith::None,
device_spec: None,
}
}
pub fn cmplr_def<'a, S: Into<String>>(&'a mut self, name: S, val: i32) -> &'a mut ProgramBuilder<'b> {
self.options.push(BuildOpt::cmplr_def(name, val));
self
}
pub fn cmplr_opt<'a, S: Into<String>>(&'a mut self, co: S) -> &'a mut ProgramBuilder<'b> {
self.options.push(BuildOpt::CmplrOther(co.into()));
self
}
pub fn bo<'a>(&'a mut self, bo: BuildOpt) -> &'a mut ProgramBuilder<'b> {
self.options.push(bo);
self
}
pub fn src_file<'a, P: Into<PathBuf>>(&'a mut self, file_path: P) -> &'a mut ProgramBuilder<'b> {
self.source_file(file_path)
}
pub fn source_file<'a, P: Into<PathBuf>>(&'a mut self, file_path: P) -> &'a mut ProgramBuilder<'b> {
let file_path = file_path.into();
assert!(file_path.is_file(), "ProgramBuilder::src_file(): Source file error: \
'{}' does not exist.", file_path.display());
match self.with {
CreateWith::None => {
let mut paths = Vec::with_capacity(8);
paths.push(file_path);
self.with = CreateWith::Source(paths);
}
CreateWith::Source(ref mut paths) => paths.push(file_path),
_ => panic!("Source may not be used with binaries or il."),
}
self
}
pub fn src<'a, S: Into<String>>(&'a mut self, src: S) -> &'a mut ProgramBuilder<'b> {
self.source(src)
}
pub fn source<'a, S: Into<String>>(&'a mut self, src: S) -> &'a mut ProgramBuilder<'b> {
match self.with {
CreateWith::None => {
self.with = CreateWith::Source(Vec::with_capacity(8));
self.options.push(BuildOpt::IncludeRawEof(src.into()));
}
CreateWith::Source(_) => {
self.options.push(BuildOpt::IncludeRawEof(src.into()));
}
_ => panic!("Source may not be used with binaries or il."),
}
self
}
pub fn binaries<'a>(&'a mut self, bins: &'b[&'b [u8]]) -> &'a mut ProgramBuilder<'b> {
match self.with {
CreateWith::None => self.with = CreateWith::Binaries(bins),
CreateWith::Binaries(_) => panic!("Binaries have already been specified."),
_ => panic!("Binaries may not be used with source or il."),
}
self
}
#[cfg(feature = "opencl_version_2_1")]
pub fn il<'a>(&'a mut self, il: &'b [u8]) -> &'a mut ProgramBuilder<'b> {
match self.with {
CreateWith::None => self.with = CreateWith::Il(il),
CreateWith::Il(_) => panic!("Il has already been specified."),
_ => panic!("Il may not be used with source or binaries."),
}
self
}
pub fn devices<'a, D: Into<DeviceSpecifier>>(&'a mut self, device_spec: D)
-> &'a mut ProgramBuilder<'b> {
assert!(self.device_spec.is_none(), "ocl::ProgramBuilder::devices(): Devices already specified");
self.device_spec = Some(device_spec.into());
self
}
pub fn get_device_spec(&self) -> &Option<DeviceSpecifier> {
&self.device_spec
}
pub fn get_compiler_options(&self) -> OclResult<CString> {
let mut opts: Vec<String> = Vec::with_capacity(64);
for option in &self.options {
match *option {
BuildOpt::CmplrDefine { ref ident, ref val } => {
opts.push(format!("-D {}={}", ident, val))
},
BuildOpt::CmplrInclDir { ref path } => {
opts.push(format!("-I {}", path))
},
BuildOpt::CmplrOther(ref s) => {
opts.push(s.clone())
},
_ => (),
}
}
CString::new(opts.join(" ").into_bytes()).map_err(OclError::from)
}
fn get_includes(&self) -> OclResult<Vec<CString>> {
let mut strings = Vec::with_capacity(64);
strings.push(CString::new("\n".as_bytes())?);
for option in &self.options {
match *option {
BuildOpt::IncludeDefine { ref ident, ref val } => {
strings.push(CString::new(format!("#define {} {}\n", ident, val)
.into_bytes())?);
},
BuildOpt::IncludeRaw(ref text) => {
strings.push(CString::new(text.clone().into_bytes())?);
},
_ => (),
};
}
strings.shrink_to_fit();
Ok(strings)
}
fn get_includes_eof(&self) -> OclResult<Vec<CString>> {
let mut strings = Vec::with_capacity(64);
strings.push(CString::new("\n".as_bytes())?);
for option in &self.options {
if let BuildOpt::IncludeRawEof(ref text) = *option {
strings.push(CString::new(text.clone().into_bytes())?);
}
}
strings.shrink_to_fit();
Ok(strings)
}
pub fn get_src_strings(&self) -> OclResult<Vec<CString>> {
let mut src_strings: Vec<CString> = Vec::with_capacity(64);
let mut src_file_history: HashSet<PathBuf> = HashSet::with_capacity(64);
src_strings.extend_from_slice(&self.get_includes()?);
let src_paths = match self.with {
CreateWith::Source(ref paths) => paths,
_ => panic!("Cannot build program. No source specified."),
};
for src_path in src_paths {
let mut src_bytes: Vec<u8> = Vec::with_capacity(100_000);
if src_file_history.contains(src_path) { continue; }
src_file_history.insert(src_path.clone());
let mut src_file_handle = File::open(src_path)?;
src_file_handle.read_to_end(&mut src_bytes)?;
src_bytes.shrink_to_fit();
src_strings.push(CString::new(src_bytes)?);
}
src_strings.extend_from_slice(&self.get_includes_eof()?);
src_strings.shrink_to_fit();
Ok(src_strings)
}
#[cfg(not(feature = "opencl_version_2_1"))]
pub fn build(&self, context: &Context) -> OclResult<Program> {
let device_list = match self.device_spec {
Some(ref ds) => ds.to_device_list(context.platform()?)?,
None => context.devices(),
};
match self.with {
CreateWith::Il(_) => {
return Err("ocl::ProgramBuilder::build: Unreachable section (IL).".into());
},
CreateWith::Source(_) => {
Program::with_source(
context,
&self.get_src_strings()?,
Some(&device_list[..]),
&self.get_compiler_options()?,
).map_err(OclError::from)
},
CreateWith::Binaries(bins) => {
Program::with_binary(
context,
&device_list[..],
bins,
&self.get_compiler_options()?,
)
},
CreateWith::None => return Err("Unable to build program: no source, binary, \
or IL has been specified".into()),
}
}
#[cfg(feature = "opencl_version_2_1")]
pub fn build(&self, context: &Context) -> OclResult<Program> {
let device_list = match self.device_spec {
Some(ref ds) => ds.to_device_list(context.platform()?)?,
None => context.devices().to_owned(),
};
match self.with {
CreateWith::Il(il) => {
Program::with_il(
il,
Some(&device_list[..]),
&self.get_compiler_options()?,
context
)
},
CreateWith::Source(_) => {
Program::with_source(
context,
&self.get_src_strings()?,
Some(&device_list[..]),
&self.get_compiler_options()?,
)
},
CreateWith::Binaries(bins) => {
Program::with_binary(
context,
&device_list[..],
bins,
&self.get_compiler_options()?,
)
},
CreateWith::None => Err("Unable to build program: no source, binary, \
or IL has been specified".into()),
}
}
}