1#![allow(clippy::missing_safety_doc)]
16
17pub use cl3::program::*;
18
19use super::context::Context;
20
21use super::Result;
22#[allow(unused_imports)]
23use cl3::error_codes::CL_BUILD_PROGRAM_FAILURE;
24#[allow(unused_imports)]
25use cl3::ext;
26#[allow(unused_imports)]
27use libc::{c_void, intptr_t, size_t};
28#[allow(unused_imports)]
29use std::ffi::{CStr, CString};
30use std::ptr;
31use std::result;
32
33pub const CL_SINGLE_RECISION_CONSTANT: &str = "-cl-single-precision-constant ";
43pub const CL_DENORMS_ARE_ZERO: &str = "-cl-denorms-are-zero ";
44pub const CL_FP_CORRECTLY_ROUNDED_DIVIDE_SQRT: &str = "-cl-fp32-correctly-rounded-divide-sqrt ";
45
46pub const CL_OPT_DISABLE: &str = "-cl-opt-disable ";
48pub const CL_STRICT_ALIASING: &str = "-cl-strict-aliasing ";
49pub const CL_UNIFORM_WORK_GROUP_SIZE: &str = "-cl-uniform-work-group-size ";
50pub const CL_NO_SUBGROUP_INFO: &str = "-cl-no-subgroup-ifp ";
51pub const CL_MAD_ENABLE: &str = "-cl-mad-enable ";
52pub const CL_NO_SIGNED_ZEROS: &str = "-cl-no-signed-zeros ";
53pub const CL_UNSAFE_MATH_OPTIMIZATIONS: &str = "-cl-unsafe-math-optimizations ";
54pub const CL_FINITE_MATH_ONLY: &str = "-cl-finite-math-only ";
55pub const CL_FAST_RELAXED_MATH: &str = "-cl-fast-relaxed-math ";
56
57pub const CL_STD_2_0: &str = "-cl-std=CL2.0 ";
62
63pub const CL_STD_3_0: &str = "-cl-std=CL3.0 ";
66
67pub const CL_KERNEL_ARG_INFO: &str = "-cl-kernel-arg-info ";
70
71pub const DEBUG_OPTION: &str = "-g ";
72
73pub const BUILD_OPTION_X_SPIR: &str = "-x spir ";
75pub const BUILD_OPTION_SPIR_STD_1_2: &str = "-spir-std=1.2 ";
76
77pub const CREATE_LIBRARY: &str = "-create-library ";
79pub const ENABLE_LINK_OPTIONS: &str = "-enable-link-options ";
80
81#[derive(Debug)]
85pub struct Program {
86 program: cl_program,
87 kernel_names: String,
88}
89
90impl From<Program> for cl_program {
91 fn from(value: Program) -> Self {
92 value.program as Self
93 }
94}
95
96impl Drop for Program {
97 fn drop(&mut self) {
98 unsafe { release_program(self.program).expect("Error: clReleaseProgram") };
99 }
100}
101
102unsafe impl Send for Program {}
103unsafe impl Sync for Program {}
104
105impl Program {
106 fn new(program: cl_program, kernel_names: &str) -> Self {
107 Self {
108 program,
109 kernel_names: kernel_names.to_owned(),
110 }
111 }
112
113 pub const fn get(&self) -> cl_program {
115 self.program
116 }
117
118 #[allow(clippy::missing_const_for_fn)]
121 pub fn kernel_names(&self) -> &str {
122 &self.kernel_names
123 }
124
125 pub fn create_from_sources(context: &Context, sources: &[&str]) -> Result<Self> {
133 Ok(Self::new(
134 create_program_with_source(context.get(), sources)?,
135 "",
136 ))
137 }
138
139 pub fn create_from_source(context: &Context, src: &str) -> Result<Self> {
147 let sources = [src];
148 Ok(Self::new(
149 create_program_with_source(context.get(), &sources)?,
150 "",
151 ))
152 }
153
154 pub unsafe fn create_from_binary(
167 context: &Context,
168 devices: &[cl_device_id],
169 binaries: &[&[u8]],
170 ) -> Result<Self> {
171 unsafe {
172 Ok(Self::new(
173 create_program_with_binary(context.get(), devices, binaries)?,
174 "",
175 ))
176 }
177 }
178
179 #[cfg(any(feature = "CL_VERSION_1_2", feature = "dynamic"))]
193 pub unsafe fn create_from_builtin_kernels(
194 context: &Context,
195 devices: &[cl_device_id],
196 kernel_names: &str,
197 ) -> Result<Self> {
198 unsafe {
199 let c_names = CString::new(kernel_names)
201 .expect("Program::create_from_builtin_kernels, invalid kernel_names");
202 Ok(Self::new(
203 create_program_with_builtin_kernels(context.get(), devices, &c_names)?,
204 kernel_names,
205 ))
206 }
207 }
208
209 #[cfg(any(feature = "CL_VERSION_2_1", feature = "dynamic"))]
219 pub fn create_from_il(context: &Context, il: &[u8]) -> Result<Self> {
220 Ok(Self::new(create_program_with_il(context.get(), il)?, ""))
221 }
222
223 #[cfg(any(feature = "cl_khr_il_program", feature = "dynamic"))]
224 pub fn create_from_il_khr(context: &Context, il: &[u8]) -> Result<Self> {
225 Ok(Self::new(
226 ext::create_program_with_il_khr(context.get(), il)?,
227 "",
228 ))
229 }
230
231 pub fn build(&mut self, devices: &[cl_device_id], options: &str) -> Result<()> {
241 let c_options = CString::new(options).expect("Program::build, invalid options");
243 build_program(self.program, devices, &c_options, None, ptr::null_mut())?;
244 self.kernel_names = self.get_kernel_names()?;
245 Ok(())
246 }
247
248 pub fn create_and_build_from_sources(
258 context: &Context,
259 sources: &[&str],
260 options: &str,
261 ) -> result::Result<Self, String> {
262 let mut program = Self::create_from_sources(context, sources).map_err(String::from)?;
263 match program.build(context.devices(), options) {
264 Ok(_) => Ok(program),
265 Err(e) => {
266 if CL_BUILD_PROGRAM_FAILURE == e.0 {
267 let log = program
268 .get_build_log(context.devices()[0])
269 .map_err(String::from)?;
270 Err(String::from(e) + ", build log: " + &log)
271 } else {
272 Err(String::from(e))
273 }
274 }
275 }
276 }
277
278 pub fn create_and_build_from_source(
287 context: &Context,
288 src: &str,
289 options: &str,
290 ) -> result::Result<Self, String> {
291 let sources = [src];
292 Self::create_and_build_from_sources(context, &sources, options)
293 }
294
295 pub fn create_and_build_from_binary(
304 context: &Context,
305 binaries: &[&[u8]],
306 options: &str,
307 ) -> Result<Self> {
308 let mut program =
309 unsafe { Self::create_from_binary(context, context.devices(), binaries)? };
310 program.build(context.devices(), options)?;
311 Ok(program)
312 }
313
314 #[cfg(any(feature = "CL_VERSION_2_1", feature = "dynamic"))]
325 pub fn create_and_build_from_il(context: &Context, il: &[u8], options: &str) -> Result<Self> {
326 let mut program = Self::create_from_il(context, il)?;
327 program.build(context.devices(), options)?;
328 Ok(program)
329 }
330
331 #[cfg(any(feature = "CL_VERSION_1_2", feature = "dynamic"))]
343 pub fn compile(
344 &mut self,
345 devices: &[cl_device_id],
346 options: &str,
347 input_headers: &[cl_program],
348 header_include_names: &[&CStr],
349 ) -> Result<()> {
350 let c_options = CString::new(options).expect("Program::compile, invalid options");
352 Ok(compile_program(
353 self.program,
354 devices,
355 &c_options,
356 input_headers,
357 header_include_names,
358 None,
359 ptr::null_mut(),
360 )?)
361 }
362
363 #[cfg(any(feature = "CL_VERSION_1_2", feature = "dynamic"))]
377 pub unsafe fn link(
378 &mut self,
379 devices: &[cl_device_id],
380 options: &str,
381 input_programs: &[cl_program],
382 ) -> Result<()> {
383 unsafe {
384 let c_options = CString::new(options).expect("Program::link, invalid options");
386 self.program = link_program(
387 self.program,
388 devices,
389 &c_options,
390 input_programs,
391 None,
392 ptr::null_mut(),
393 )?;
394 self.kernel_names = self.get_kernel_names()?;
395 Ok(())
396 }
397 }
398
399 #[cfg(any(feature = "CL_VERSION_2_2", feature = "dynamic"))]
409 pub unsafe fn set_specialization_constant(
410 &self,
411 spec_id: cl_uint,
412 spec_size: size_t,
413 spec_value: *const c_void,
414 ) -> Result<()> {
415 unsafe {
416 Ok(set_program_specialization_constant(
417 self.program,
418 spec_id,
419 spec_size,
420 spec_value,
421 )?)
422 }
423 }
424
425 pub fn get_reference_count(&self) -> Result<cl_uint> {
426 Ok(get_program_info(self.program, CL_PROGRAM_REFERENCE_COUNT)?.into())
427 }
428
429 pub fn get_context(&self) -> Result<cl_context> {
430 Ok(intptr_t::from(get_program_info(self.program, CL_PROGRAM_CONTEXT)?) as cl_context)
431 }
432
433 pub fn get_num_devices(&self) -> Result<cl_uint> {
434 Ok(get_program_info(self.program, CL_PROGRAM_NUM_DEVICES)?.into())
435 }
436
437 pub fn get_devices(&self) -> Result<Vec<intptr_t>> {
438 Ok(get_program_info(self.program, CL_PROGRAM_DEVICES)?.into())
439 }
440
441 pub fn get_source(&self) -> Result<String> {
442 Ok(get_program_info(self.program, CL_PROGRAM_SOURCE)?.into())
443 }
444
445 pub fn get_binary_sizes(&self) -> Result<Vec<size_t>> {
446 Ok(get_program_info(self.program, CL_PROGRAM_BINARY_SIZES)?.into())
447 }
448
449 pub fn get_binaries(&self) -> Result<Vec<Vec<cl_uchar>>> {
450 Ok(get_program_info(self.program, CL_PROGRAM_BINARIES)?.into())
451 }
452
453 pub fn get_num_kernels(&self) -> Result<size_t> {
454 Ok(get_program_info(self.program, CL_PROGRAM_NUM_KERNELS)?.into())
455 }
456
457 pub fn get_kernel_names(&self) -> Result<String> {
458 Ok(get_program_info(self.program, CL_PROGRAM_KERNEL_NAMES)?.into())
459 }
460
461 pub fn get_program_il(&self) -> Result<String> {
463 Ok(get_program_info(self.program, CL_PROGRAM_IL)?.into())
464 }
465
466 pub fn get_program_scope_global_ctors_present(&self) -> Result<bool> {
468 Ok(cl_uint::from(get_program_info(
469 self.program,
470 CL_PROGRAM_SCOPE_GLOBAL_CTORS_PRESENT,
471 )?) != CL_FALSE)
472 }
473
474 pub fn get_program_scope_global_dtors_present(&self) -> Result<bool> {
476 Ok(cl_uint::from(get_program_info(
477 self.program,
478 CL_PROGRAM_SCOPE_GLOBAL_DTORS_PRESENT,
479 )?) != CL_FALSE)
480 }
481
482 pub fn get_build_status(&self, device: cl_device_id) -> Result<cl_int> {
483 Ok(get_program_build_info(self.program, device, CL_PROGRAM_BUILD_STATUS)?.into())
484 }
485
486 pub fn get_build_options(&self, device: cl_device_id) -> Result<String> {
487 Ok(get_program_build_info(self.program, device, CL_PROGRAM_BUILD_OPTIONS)?.into())
488 }
489
490 pub fn get_build_log(&self, device: cl_device_id) -> Result<String> {
491 Ok(get_program_build_info(self.program, device, CL_PROGRAM_BUILD_LOG)?.into())
492 }
493
494 pub fn get_build_binary_type(&self, device: cl_device_id) -> Result<cl_uint> {
495 Ok(get_program_build_info(self.program, device, CL_PROGRAM_BINARY_TYPE)?.into())
496 }
497
498 pub fn get_build_global_variable_total_size(&self, device: cl_device_id) -> Result<size_t> {
500 Ok(get_program_build_info(
501 self.program,
502 device,
503 CL_PROGRAM_BUILD_GLOBAL_VARIABLE_TOTAL_SIZE,
504 )?
505 .into())
506 }
507}
508
509#[cfg(test)]
510mod tests {
511 use super::*;
512 use crate::context::Context;
513 use crate::device::Device;
514 use crate::platform::get_platforms;
515 use cl3::device::CL_DEVICE_TYPE_GPU;
516 use std::collections::HashSet;
517
518 const PROGRAM_SOURCE: &str = r#"
519 kernel void add(global float* buffer, float scalar) {
520 buffer[get_global_id(0)] += scalar;
521 }
522
523 kernel void subtract(global float* buffer, float scalar) {
524 buffer[get_global_id(0)] -= scalar;
525 }
526 "#;
527
528 #[test]
529 fn test_create_and_build_from_source() {
530 let platforms = get_platforms().unwrap();
531 assert!(0 < platforms.len());
532
533 let platform = &platforms[0];
535
536 let devices = platform.get_devices(CL_DEVICE_TYPE_GPU).unwrap();
537 assert!(0 < devices.len());
538
539 let device = Device::new(devices[0]);
541 let context = Context::from_device(&device).unwrap();
542
543 let program =
544 Program::create_and_build_from_source(&context, PROGRAM_SOURCE, CL_DENORMS_ARE_ZERO)
545 .expect("Program::create_and_build_from_source failed");
546
547 let names: HashSet<&str> = program.kernel_names().split(';').collect();
548 println!("OpenCL Program kernel_names len: {}", names.len());
549 println!("OpenCL Program kernel_names: {:?}", names);
550
551 let value = program.get_reference_count().unwrap();
552 println!("program.get_reference_count(): {}", value);
553 assert_eq!(1, value);
554
555 let value = program.get_context().unwrap();
556 assert!(context.get() == value);
557
558 let value = program.get_num_devices().unwrap();
559 println!("program.get_num_devices(): {}", value);
560 assert_eq!(1, value);
561
562 let value = program.get_devices().unwrap();
563 assert!(device.id() == value[0] as cl_device_id);
564
565 let value = program.get_source().unwrap();
566 println!("program.get_source(): {}", value);
567 assert!(!value.is_empty());
568
569 let value = program.get_binary_sizes().unwrap();
570 println!("program.get_binary_sizes(): {:?}", value);
571 assert!(0 < value[0]);
572
573 let value = program.get_binaries().unwrap();
574 assert!(!value[0].is_empty());
576
577 let value = program.get_num_kernels().unwrap();
578 println!("program.get_num_kernels(): {}", value);
579 assert_eq!(2, value);
580
581 let value = program.get_build_status(device.id()).unwrap();
586 println!("program.get_build_status(): {}", value);
587 assert!(CL_BUILD_SUCCESS == value);
588
589 let value = program.get_build_options(device.id()).unwrap();
590 println!("program.get_build_options(): {}", value);
591 assert!(!value.is_empty());
592
593 let value = program.get_build_log(device.id()).unwrap();
594 println!("program.get_build_log(): {}", value);
595 let value = program.get_build_binary_type(device.id()).unwrap();
598 println!("program.get_build_binary_type(): {}", value);
599 assert_eq!(CL_PROGRAM_BINARY_TYPE_EXECUTABLE as u32, value);
600
601 match program.get_build_global_variable_total_size(device.id()) {
603 Ok(value) => println!("program.get_build_global_variable_total_size(): {}", value),
604 Err(e) => println!(
605 "OpenCL error, program.get_build_global_variable_total_size(): {}",
606 e
607 ),
608 };
609 }
610}