1use std;
3use std::collections::HashSet;
4use std::convert::Into;
5use std::ffi::CString;
6use std::fs::File;
7use std::io::Read;
8use std::ops::{Deref, DerefMut};
9use std::path::PathBuf;
10
11use crate::core::{
12 self, Context as ContextCore, Program as ProgramCore, ProgramBuildInfo, ProgramBuildInfoResult,
13 ProgramInfo, ProgramInfoResult, Result as OclCoreResult,
14};
15use crate::error::{Error as OclError, Result as OclResult};
16use crate::standard::{Context, Device, DeviceSpecifier};
17#[cfg(feature = "opencl_version_2_1")]
18use core::ClVersions;
19
20#[derive(Clone, Debug)]
30pub struct Program(ProgramCore);
31
32impl Program {
33 pub fn builder<'b>() -> ProgramBuilder<'b> {
35 ProgramBuilder::new()
36 }
37
38 pub fn with_source(
44 context: &ContextCore,
45 src_strings: &[CString],
46 devices: Option<&[Device]>,
47 cmplr_opts: &CString,
48 ) -> OclResult<Program> {
49 let program = core::create_program_with_source(context, src_strings)?;
50 core::build_program(&program, devices, cmplr_opts, None, None)?;
51 Ok(Program(program))
52 }
53
54 pub fn with_binary(
60 context: &ContextCore,
61 devices: &[Device],
62 binaries: &[&[u8]],
63 cmplr_opts: &CString,
64 ) -> OclResult<Program> {
65 let program = core::create_program_with_binary(context, devices, binaries)?;
66 core::build_program(&program, Some(devices), cmplr_opts, None, None)?;
67 Ok(Program(program))
68 }
69
70 #[cfg(feature = "opencl_version_2_1")]
73 pub fn with_il(
74 il: &[u8],
75 devices: Option<&[Device]>,
76 cmplr_opts: &CString,
77 context: &ContextCore,
78 ) -> OclResult<Program> {
79 let device_versions = context.device_versions()?;
80 let program = core::create_program_with_il(context, il, Some(&device_versions))?;
81 core::build_program(&program, devices, cmplr_opts, None, None)?;
82
83 Ok(Program(program))
84 }
85
86 #[inline]
89 pub fn as_core(&self) -> &ProgramCore {
90 &self.0
91 }
92
93 pub fn info(&self, info_kind: ProgramInfo) -> OclCoreResult<ProgramInfoResult> {
95 core::get_program_info(&self.0, info_kind)
96 }
97
98 pub fn build_info(
102 &self,
103 device: Device,
104 info_kind: ProgramBuildInfo,
105 ) -> OclCoreResult<ProgramBuildInfoResult> {
106 core::get_program_build_info(&self.0, &device, info_kind)
107 }
108
109 fn fmt_info(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
110 f.debug_struct("Program")
111 .field("ReferenceCount", &self.info(ProgramInfo::ReferenceCount))
112 .field("Context", &self.info(ProgramInfo::Context))
113 .field("NumDevices", &self.info(ProgramInfo::NumDevices))
114 .field("Devices", &self.info(ProgramInfo::Devices))
115 .field("Source", &self.info(ProgramInfo::Source))
116 .field("BinarySizes", &self.info(ProgramInfo::BinarySizes))
117 .field("Binaries", &self.info(ProgramInfo::Binaries))
118 .field("NumKernels", &self.info(ProgramInfo::NumKernels))
119 .field("KernelNames", &self.info(ProgramInfo::KernelNames))
120 .finish()
121 }
122}
123
124impl From<ProgramCore> for Program {
125 fn from(core: ProgramCore) -> Program {
126 Program(core)
127 }
128}
129
130impl std::fmt::Display for Program {
131 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
132 self.fmt_info(f)
133 }
134}
135
136impl Deref for Program {
137 type Target = ProgramCore;
138
139 fn deref(&self) -> &ProgramCore {
140 &self.0
141 }
142}
143
144impl DerefMut for Program {
145 fn deref_mut(&mut self) -> &mut ProgramCore {
146 &mut self.0
147 }
148}
149
150#[derive(Clone, Debug)]
161pub enum BuildOpt {
162 CmplrDefine { ident: String, val: String },
163 CmplrInclDir { path: String },
164 CmplrOther(String),
165 IncludeDefine { ident: String, val: String },
166 IncludeRaw(String),
167 IncludeRawEof(String),
168}
169
170impl BuildOpt {
171 pub fn cmplr_def<S: Into<String>>(ident: S, val: i32) -> BuildOpt {
173 BuildOpt::CmplrDefine {
174 ident: ident.into(),
175 val: val.to_string(),
176 }
177 }
178
179 pub fn include_def<S: Into<String>>(ident: S, val: String) -> BuildOpt {
181 BuildOpt::IncludeDefine {
182 ident: ident.into(),
183 val,
184 }
185 }
186}
187
188#[allow(dead_code)]
190#[derive(Clone, Debug)]
191enum CreateWith<'b> {
192 None,
193 Source(Vec<PathBuf>),
194 Binaries(&'b [&'b [u8]]),
195 Il(&'b [u8]),
196}
197
198#[must_use = "builders do nothing unless '::build' is called"]
204#[derive(Clone, Debug)]
205pub struct ProgramBuilder<'b> {
206 options: Vec<BuildOpt>,
207 with: CreateWith<'b>,
208 device_spec: Option<DeviceSpecifier>,
209}
210
211impl<'b> ProgramBuilder<'b> {
212 pub fn new() -> ProgramBuilder<'b> {
214 ProgramBuilder {
215 options: Vec::with_capacity(64),
216 with: CreateWith::None,
217 device_spec: None,
218 }
219 }
220
221 pub fn cmplr_def<'a, S: Into<String>>(
229 &'a mut self,
230 name: S,
231 val: i32,
232 ) -> &'a mut ProgramBuilder<'b> {
233 self.options.push(BuildOpt::cmplr_def(name, val));
234 self
235 }
236
237 pub fn cmplr_opt<'a, S: Into<String>>(&'a mut self, co: S) -> &'a mut ProgramBuilder<'b> {
245 self.options.push(BuildOpt::CmplrOther(co.into()));
246 self
247 }
248
249 pub fn bo<'a>(&'a mut self, bo: BuildOpt) -> &'a mut ProgramBuilder<'b> {
254 self.options.push(bo);
255 self
256 }
257
258 pub fn src_file<'a, P: Into<PathBuf>>(
263 &'a mut self,
264 file_path: P,
265 ) -> &'a mut ProgramBuilder<'b> {
266 self.source_file(file_path)
267 }
268
269 pub fn source_file<'a, P: Into<PathBuf>>(
271 &'a mut self,
272 file_path: P,
273 ) -> &'a mut ProgramBuilder<'b> {
274 let file_path = file_path.into();
275 assert!(
276 file_path.is_file(),
277 "ProgramBuilder::src_file(): Source file error: \
278 '{}' does not exist.",
279 file_path.display()
280 );
281 match self.with {
282 CreateWith::None => {
283 let mut paths = Vec::with_capacity(8);
284 paths.push(file_path);
285 self.with = CreateWith::Source(paths);
286 }
287 CreateWith::Source(ref mut paths) => paths.push(file_path),
288 _ => panic!("Source may not be used with binaries or il."),
289 }
290 self
291 }
292
293 pub fn src<'a, S: Into<String>>(&'a mut self, src: S) -> &'a mut ProgramBuilder<'b> {
298 self.source(src)
299 }
300
301 pub fn source<'a, S: Into<String>>(&'a mut self, src: S) -> &'a mut ProgramBuilder<'b> {
305 match self.with {
306 CreateWith::None => {
307 self.with = CreateWith::Source(Vec::with_capacity(8));
308 self.options.push(BuildOpt::IncludeRawEof(src.into()));
309 }
310 CreateWith::Source(_) => {
311 self.options.push(BuildOpt::IncludeRawEof(src.into()));
312 }
313 _ => panic!("Source may not be used with binaries or il."),
314 }
315
316 self
317 }
318
319 pub fn binaries<'a>(&'a mut self, bins: &'b [&'b [u8]]) -> &'a mut ProgramBuilder<'b> {
323 match self.with {
324 CreateWith::None => self.with = CreateWith::Binaries(bins),
325 CreateWith::Binaries(_) => panic!("Binaries have already been specified."),
326 _ => panic!("Binaries may not be used with source or il."),
327 }
328 self
329 }
330
331 #[cfg(feature = "opencl_version_2_1")]
344 pub fn il<'a>(&'a mut self, il: &'b [u8]) -> &'a mut ProgramBuilder<'b> {
345 match self.with {
346 CreateWith::None => self.with = CreateWith::Il(il),
347 CreateWith::Il(_) => panic!("Il has already been specified."),
348 _ => panic!("Il may not be used with source or binaries."),
349 }
350 self
351 }
352
353 pub fn devices<'a, D: Into<DeviceSpecifier>>(
370 &'a mut self,
371 device_spec: D,
372 ) -> &'a mut ProgramBuilder<'b> {
373 assert!(
374 self.device_spec.is_none(),
375 "ocl::ProgramBuilder::devices(): Devices already specified"
376 );
377 self.device_spec = Some(device_spec.into());
378 self
379 }
380
381 pub fn get_device_spec(&self) -> &Option<DeviceSpecifier> {
383 &self.device_spec
384 }
385
386 pub fn get_compiler_options(&self) -> OclResult<CString> {
389 let mut opts: Vec<String> = Vec::with_capacity(64);
390
391 for option in &self.options {
392 match *option {
393 BuildOpt::CmplrDefine { ref ident, ref val } => {
394 opts.push(format!("-D {}={}", ident, val))
395 }
396
397 BuildOpt::CmplrInclDir { ref path } => opts.push(format!("-I {}", path)),
398
399 BuildOpt::CmplrOther(ref s) => opts.push(s.clone()),
400
401 _ => (),
402 }
403 }
404
405 CString::new(opts.join(" ").into_bytes()).map_err(OclError::from)
406 }
407
408 fn get_includes(&self) -> OclResult<Vec<CString>> {
414 let mut strings = Vec::with_capacity(64);
415 strings.push(CString::new("\n".as_bytes())?);
416
417 for option in &self.options {
418 match *option {
419 BuildOpt::IncludeDefine { ref ident, ref val } => {
420 strings.push(CString::new(
421 format!("#define {} {}\n", ident, val).into_bytes(),
422 )?);
423 }
424 BuildOpt::IncludeRaw(ref text) => {
425 strings.push(CString::new(text.clone().into_bytes())?);
426 }
427 _ => (),
428 };
429 }
430
431 strings.shrink_to_fit();
432 Ok(strings)
433 }
434
435 fn get_includes_eof(&self) -> OclResult<Vec<CString>> {
438 let mut strings = Vec::with_capacity(64);
439 strings.push(CString::new("\n".as_bytes())?);
440
441 for option in &self.options {
442 if let BuildOpt::IncludeRawEof(ref text) = *option {
443 strings.push(CString::new(text.clone().into_bytes())?);
444 }
445 }
446
447 strings.shrink_to_fit();
448 Ok(strings)
449 }
450
451 pub fn get_src_strings(&self) -> OclResult<Vec<CString>> {
462 let mut src_strings: Vec<CString> = Vec::with_capacity(64);
463 let mut src_file_history: HashSet<PathBuf> = HashSet::with_capacity(64);
464
465 src_strings.extend_from_slice(&self.get_includes()?);
466
467 let src_paths = match self.with {
468 CreateWith::Source(ref paths) => paths,
469 _ => panic!("Cannot build program. No source specified."),
470 };
471
472 for src_path in src_paths {
473 let mut src_bytes: Vec<u8> = Vec::with_capacity(100_000);
474
475 if src_file_history.contains(src_path) {
476 continue;
477 }
478 src_file_history.insert(src_path.clone());
479
480 let mut src_file_handle = File::open(src_path)?;
481
482 src_file_handle.read_to_end(&mut src_bytes)?;
483 src_bytes.shrink_to_fit();
484 src_strings.push(CString::new(src_bytes)?);
485 }
486
487 src_strings.extend_from_slice(&self.get_includes_eof()?);
488 src_strings.shrink_to_fit();
489 Ok(src_strings)
490 }
491
492 #[cfg(not(feature = "opencl_version_2_1"))]
500 pub fn build(&self, context: &Context) -> OclResult<Program> {
501 let device_list = match self.device_spec {
502 Some(ref ds) => ds.to_device_list(context.platform()?)?,
503 None => context.devices(),
504 };
505
506 match self.with {
507 CreateWith::Il(_) => {
508 return Err("ocl::ProgramBuilder::build: Unreachable section (IL).".into());
509 }
510 CreateWith::Source(_) => Program::with_source(
511 context,
512 &self.get_src_strings()?,
513 Some(&device_list[..]),
514 &self.get_compiler_options()?,
515 )
516 .map_err(OclError::from),
517 CreateWith::Binaries(bins) => Program::with_binary(
518 context,
519 &device_list[..],
520 bins,
521 &self.get_compiler_options()?,
522 ),
523 CreateWith::None => {
524 return Err("Unable to build program: no source, binary, \
525 or IL has been specified"
526 .into())
527 }
528 }
529 }
530
531 #[cfg(feature = "opencl_version_2_1")]
539 pub fn build(&self, context: &Context) -> OclResult<Program> {
540 let device_list = match self.device_spec {
541 Some(ref ds) => ds.to_device_list(context.platform()?)?,
542 None => context.devices().to_owned(),
543 };
544
545 match self.with {
546 CreateWith::Il(il) => Program::with_il(
547 il,
548 Some(&device_list[..]),
549 &self.get_compiler_options()?,
550 context,
551 ),
552 CreateWith::Source(_) => Program::with_source(
553 context,
554 &self.get_src_strings()?,
555 Some(&device_list[..]),
556 &self.get_compiler_options()?,
557 ),
558 CreateWith::Binaries(bins) => Program::with_binary(
559 context,
560 &device_list[..],
561 bins,
562 &self.get_compiler_options()?,
563 ),
564 CreateWith::None => Err("Unable to build program: no source, binary, \
565 or IL has been specified"
566 .into()),
567 }
568 }
569}