1#![allow(unused_unsafe)]
18#![allow(non_camel_case_types)]
19#![allow(clippy::not_unsafe_ptr_arg_deref, clippy::wildcard_in_or_patterns)]
20
21pub use opencl_sys::{
22 cl_context, cl_device_id, cl_int, cl_platform_id, cl_program, cl_program_build_info,
23 cl_program_info, cl_uchar, cl_uint, CL_BUILD_ERROR, CL_BUILD_IN_PROGRESS, CL_BUILD_NONE,
24 CL_BUILD_SUCCESS, CL_FALSE, CL_INVALID_VALUE, CL_PROGRAM_BINARIES, CL_PROGRAM_BINARY_SIZES,
25 CL_PROGRAM_BINARY_TYPE, CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT,
26 CL_PROGRAM_BINARY_TYPE_EXECUTABLE, CL_PROGRAM_BINARY_TYPE_LIBRARY, CL_PROGRAM_BINARY_TYPE_NONE,
27 CL_PROGRAM_BUILD_GLOBAL_VARIABLE_TOTAL_SIZE, CL_PROGRAM_BUILD_LOG, CL_PROGRAM_BUILD_OPTIONS,
28 CL_PROGRAM_BUILD_STATUS, CL_PROGRAM_CONTEXT, CL_PROGRAM_DEVICES, CL_PROGRAM_IL,
29 CL_PROGRAM_KERNEL_NAMES, CL_PROGRAM_NUM_DEVICES, CL_PROGRAM_NUM_KERNELS,
30 CL_PROGRAM_REFERENCE_COUNT, CL_PROGRAM_SOURCE, CL_SUCCESS, CL_TRUE,
31};
32
33use super::info_type::InfoType;
34use super::{
35 api2_info_size, api2_info_value, api2_info_vector, api_info_size, api_info_value,
36 api_info_vector,
37};
38use libc::{c_char, c_uchar, c_void, intptr_t, size_t};
39use std::ffi::CStr;
40use std::mem;
41use std::ptr;
42
43pub const CL_PROGRAM_SCOPE_GLOBAL_CTORS_PRESENT: cl_program_info = 0x116A;
45pub const CL_PROGRAM_SCOPE_GLOBAL_DTORS_PRESENT: cl_program_info = 0x116B;
46
47#[allow(clippy::cast_possible_truncation)]
56#[inline]
57pub fn create_program_with_source(
58 context: cl_context,
59 sources: &[&str],
60) -> Result<cl_program, cl_int> {
61 let lengths: Vec<size_t> = sources.iter().map(|src| src.len()).collect();
62 let mut status: cl_int = CL_INVALID_VALUE;
63 let program: cl_program = unsafe {
64 cl_call!(clCreateProgramWithSource(
65 context,
66 sources.len() as cl_uint,
67 sources.as_ptr().cast::<*const c_char>(),
68 lengths.as_ptr(),
69 &mut status,
70 ))
71 };
72
73 if CL_SUCCESS == status {
74 Ok(program)
75 } else {
76 Err(status)
77 }
78}
79
80#[allow(clippy::cast_possible_truncation)]
94pub unsafe fn create_program_with_binary(
95 context: cl_context,
96 devices: &[cl_device_id],
97 binaries: &[&[u8]],
98) -> Result<cl_program, cl_int> {
99 let binaries_length = binaries.len();
100 let lengths: Vec<size_t> = binaries.iter().map(|bin| bin.len()).collect();
101 let mut binary_status: Vec<cl_int> = Vec::with_capacity(binaries_length);
102 let mut status: cl_int = CL_INVALID_VALUE;
103 let program: cl_program = cl_call!(clCreateProgramWithBinary(
104 context,
105 devices.len() as cl_uint,
106 devices.as_ptr(),
107 lengths.as_ptr(),
108 binaries.as_ptr().cast::<*const c_uchar>(),
109 binary_status.as_mut_ptr(),
110 &mut status,
111 ));
112 if CL_SUCCESS == status {
113 Ok(program)
114 } else {
115 Err(status)
116 }
117}
118
119#[cfg(any(feature = "CL_VERSION_1_2", feature = "dynamic"))]
135#[allow(clippy::cast_possible_truncation)]
136#[inline]
137pub unsafe fn create_program_with_builtin_kernels(
138 context: cl_context,
139 devices: &[cl_device_id],
140 kernel_names: &CStr,
141) -> Result<cl_program, cl_int> {
142 let mut status: cl_int = CL_INVALID_VALUE;
143 let program: cl_program = cl_call!(clCreateProgramWithBuiltInKernels(
144 context,
145 devices.len() as cl_uint,
146 devices.as_ptr(),
147 kernel_names.as_ptr(),
148 &mut status,
149 ));
150 if CL_SUCCESS == status {
151 Ok(program)
152 } else {
153 Err(status)
154 }
155}
156
157#[cfg(any(feature = "CL_VERSION_2_1", feature = "dynamic"))]
168#[inline]
169pub fn create_program_with_il(context: cl_context, il: &[u8]) -> Result<cl_program, cl_int> {
170 let mut status: cl_int = CL_INVALID_VALUE;
171 let program: cl_program = unsafe {
172 cl_call!(clCreateProgramWithIL(
173 context,
174 il.as_ptr().cast::<c_void>(),
175 il.len() as size_t,
176 &mut status,
177 ))
178 };
179 if CL_SUCCESS == status {
180 Ok(program)
181 } else {
182 Err(status)
183 }
184}
185
186#[inline]
197pub unsafe fn retain_program(program: cl_program) -> Result<(), cl_int> {
198 let status: cl_int = cl_call!(clRetainProgram(program));
199 if CL_SUCCESS == status {
200 Ok(())
201 } else {
202 Err(status)
203 }
204}
205
206#[inline]
217pub unsafe fn release_program(program: cl_program) -> Result<(), cl_int> {
218 let status: cl_int = cl_call!(clReleaseProgram(program));
219 if CL_SUCCESS == status {
220 Ok(())
221 } else {
222 Err(status)
223 }
224}
225
226#[allow(clippy::cast_possible_truncation)]
238#[inline]
239pub fn build_program(
240 program: cl_program,
241 devices: &[cl_device_id],
242 options: &CStr,
243 pfn_notify: Option<unsafe extern "C" fn(cl_program, *mut c_void)>,
244 user_data: *mut c_void,
245) -> Result<(), cl_int> {
246 let status: cl_int = unsafe {
247 cl_call!(clBuildProgram(
248 program,
249 devices.len() as cl_uint,
250 devices.as_ptr(),
251 options.as_ptr(),
252 pfn_notify,
253 user_data,
254 ))
255 };
256 if CL_SUCCESS == status {
257 Ok(())
258 } else {
259 Err(status)
260 }
261}
262
263#[cfg(any(feature = "CL_VERSION_1_2", feature = "dynamic"))]
283#[allow(clippy::cast_possible_truncation)]
284#[inline]
285pub fn compile_program(
286 program: cl_program,
287 devices: &[cl_device_id],
288 options: &CStr,
289 input_headers: &[cl_program],
290 header_include_names: &[&CStr],
291 pfn_notify: Option<unsafe extern "C" fn(program: cl_program, user_data: *mut c_void)>,
292 user_data: *mut c_void,
293) -> Result<(), cl_int> {
294 assert!(input_headers.len() == header_include_names.len());
295 let status: cl_int = unsafe {
296 let input_headers_ptr = if input_headers.is_empty() {
297 ptr::null()
298 } else {
299 input_headers.as_ptr()
300 };
301 let header_include_names_ptr = if header_include_names.is_empty() {
302 ptr::null()
303 } else {
304 header_include_names.as_ptr()
305 };
306 cl_call!(clCompileProgram(
307 program,
308 devices.len() as cl_uint,
309 devices.as_ptr(),
310 options.as_ptr(),
311 input_headers.len() as cl_uint,
312 input_headers_ptr,
313 header_include_names_ptr.cast::<*const c_char>(),
314 pfn_notify,
315 user_data,
316 ))
317 };
318 if CL_SUCCESS == status {
319 Ok(())
320 } else {
321 Err(status)
322 }
323}
324
325#[cfg(any(feature = "CL_VERSION_1_2", feature = "dynamic"))]
347#[allow(clippy::cast_possible_truncation)]
348#[inline]
349pub unsafe fn link_program(
350 context: cl_context,
351 devices: &[cl_device_id],
352 options: &CStr,
353 input_programs: &[cl_program],
354 pfn_notify: Option<unsafe extern "C" fn(program: cl_program, user_data: *mut c_void)>,
355 user_data: *mut c_void,
356) -> Result<cl_program, cl_int> {
357 assert!(!input_programs.is_empty());
358 let mut status: cl_int = CL_INVALID_VALUE;
359 let programme: cl_program = cl_call!(clLinkProgram(
360 context,
361 devices.len() as cl_uint,
362 devices.as_ptr(),
363 options.as_ptr(),
364 input_programs.len() as cl_uint,
365 input_programs.as_ptr(),
366 pfn_notify,
367 user_data,
368 &mut status,
369 ));
370 if CL_SUCCESS == status {
371 Ok(programme)
372 } else {
373 Err(status)
374 }
375}
376
377#[cfg(any(feature = "CL_VERSION_2_2", feature = "dynamic"))]
393#[inline]
394pub unsafe fn set_program_specialization_constant(
395 program: cl_program,
396 spec_id: cl_uint,
397 spec_size: size_t,
398 spec_value: *const c_void,
399) -> Result<(), cl_int> {
400 let status: cl_int = cl_call!(clSetProgramSpecializationConstant(
401 program, spec_id, spec_size, spec_value
402 ));
403 if CL_SUCCESS == status {
404 Ok(())
405 } else {
406 Err(status)
407 }
408}
409
410#[cfg(any(feature = "CL_VERSION_1_2", feature = "dynamic"))]
421#[inline]
422pub unsafe fn unload_platform_compiler(platform: cl_platform_id) -> Result<(), cl_int> {
423 let status: cl_int = cl_call!(clUnloadPlatformCompiler(platform));
424 if CL_SUCCESS == status {
425 Ok(())
426 } else {
427 Err(status)
428 }
429}
430
431pub fn get_program_data(
434 program: cl_program,
435 param_name: cl_program_info,
436) -> Result<Vec<u8>, cl_int> {
437 api_info_size!(get_size, clGetProgramInfo);
438 let size = get_size(program, param_name)?;
439 api_info_vector!(get_vector, u8, clGetProgramInfo);
440 get_vector(program, param_name, size)
441}
442
443pub fn get_program_info(
453 program: cl_program,
454 param_name: cl_program_info,
455) -> Result<InfoType, cl_int> {
456 api_info_size!(get_size, clGetProgramInfo);
457
458 match param_name {
459 CL_PROGRAM_REFERENCE_COUNT
460 | CL_PROGRAM_NUM_DEVICES
461 | CL_PROGRAM_SCOPE_GLOBAL_CTORS_PRESENT | CL_PROGRAM_SCOPE_GLOBAL_DTORS_PRESENT => {
464 api_info_value!(get_value, cl_uint, clGetProgramInfo);
465 Ok(InfoType::Uint(get_value(program, param_name)?))
466 }
467
468 CL_PROGRAM_CONTEXT => {
469 api_info_value!(get_value, intptr_t, clGetProgramInfo);
470 Ok(InfoType::Ptr(get_value(program, param_name)?))
471 }
472
473 CL_PROGRAM_DEVICES => {
474 api_info_vector!(get_vec, intptr_t, clGetProgramInfo);
475 let size = get_size(program, param_name)?;
476 Ok(InfoType::VecIntPtr(get_vec(program, param_name, size)?))
477 }
478
479 CL_PROGRAM_BINARY_SIZES => {
480 api_info_vector!(get_vec, size_t, clGetProgramInfo);
481 let size = get_size(program, param_name)?;
482 Ok(InfoType::VecSize(get_vec(program, param_name, size)?))
483 }
484
485 CL_PROGRAM_BINARIES => {
486 api_info_vector!(get_size_vec, size_t, clGetProgramInfo);
490 let size = get_size(program, CL_PROGRAM_BINARY_SIZES as cl_program_info)?;
491 let binary_sizes = get_size_vec(program, CL_PROGRAM_BINARY_SIZES as cl_program_info, size)?;
492
493 let binaries = binary_sizes.into_iter().map(|size| {
495 vec![0u8; size]
496 }).collect::<Vec<Vec<u8>>>();
497
498 let mut binary_ptrs = binaries.iter().map(|vec| {
500 vec.as_ptr()
501 }).collect::<Vec<_>>();
502
503 let status = unsafe {
504 cl_call!(clGetProgramInfo(
505 program,
506 param_name,
507 binary_ptrs.len() * mem::size_of::<*mut c_void>(),
508 binary_ptrs.as_mut_ptr().cast(),
509 ptr::null_mut(),
510 ))
511 };
512 if CL_SUCCESS == status {
513 Ok(InfoType::VecVecUchar(binaries))
514 } else {
515 Err(status)
516 }
517 }
518
519 CL_PROGRAM_NUM_KERNELS => {
520 api_info_value!(get_value, size_t, clGetProgramInfo);
521 Ok(InfoType::Size(get_value(program, param_name)?))
522 }
523
524 CL_PROGRAM_SOURCE
525 | CL_PROGRAM_KERNEL_NAMES
526 | CL_PROGRAM_IL
527 | _ => {
528 Ok(InfoType::VecUchar(get_program_data(program, param_name)?))
529 }
530 }
531}
532
533pub fn get_program_build_data(
536 program: cl_program,
537 device: cl_device_id,
538 param_name: cl_program_info,
539) -> Result<Vec<u8>, cl_int> {
540 api2_info_size!(get_size, cl_device_id, clGetProgramBuildInfo);
541 let size = get_size(program, device, param_name)?;
542 api2_info_vector!(get_vector, cl_device_id, u8, clGetProgramBuildInfo);
543 get_vector(program, device, param_name, size)
544}
545
546pub fn get_program_build_info(
557 program: cl_program,
558 device: cl_device_id,
559 param_name: cl_program_build_info,
560) -> Result<InfoType, cl_int> {
561 match param_name {
562 CL_PROGRAM_BUILD_STATUS => {
563 api2_info_value!(
564 get_device_value,
565 cl_device_id,
566 cl_int,
567 clGetProgramBuildInfo
568 );
569 Ok(InfoType::Int(get_device_value(
570 program, device, param_name,
571 )?))
572 }
573
574 CL_PROGRAM_BINARY_TYPE => {
575 api2_info_value!(
576 get_device_value,
577 cl_device_id,
578 cl_uint,
579 clGetProgramBuildInfo
580 );
581 Ok(InfoType::Uint(get_device_value(
582 program, device, param_name,
583 )?))
584 }
585
586 CL_PROGRAM_BUILD_GLOBAL_VARIABLE_TOTAL_SIZE => {
588 api2_info_value!(
589 get_device_value,
590 cl_device_id,
591 size_t,
592 clGetProgramBuildInfo
593 );
594 Ok(InfoType::Size(get_device_value(
595 program, device, param_name,
596 )?))
597 }
598
599 CL_PROGRAM_BUILD_OPTIONS | CL_PROGRAM_BUILD_LOG | _ => Ok(InfoType::VecUchar(
600 get_program_build_data(program, device, param_name)?,
601 )),
602 }
603}
604
605#[cfg(test)]
606mod tests {
607 use super::*;
608 use crate::context::{create_context, release_context};
609 use crate::device::{get_device_ids, CL_DEVICE_TYPE_ALL};
610 #[allow(unused_imports)]
611 use crate::error_codes::error_text;
612 use crate::platform::get_platform_ids;
613 use std::ffi::CString;
614
615 #[test]
616 fn test_program() {
617 let platform_ids = get_platform_ids().unwrap();
618
619 let mut platform_id = platform_ids[0];
620 let mut device_count: usize = 0;
621
622 for p in platform_ids {
624 let ids = get_device_ids(p, CL_DEVICE_TYPE_ALL).unwrap();
625 let count = ids.len();
626 if device_count < count {
627 device_count = count;
628 platform_id = p;
629 }
630 }
631
632 println!("Platform device_count: {}", device_count);
633
634 let device_ids = get_device_ids(platform_id, CL_DEVICE_TYPE_ALL).unwrap();
635 let device_id = device_ids[0];
636
637 let context = create_context(&device_ids, ptr::null(), None, ptr::null_mut());
638 let context = context.unwrap();
639
640 let source = r#"
641 kernel void saxpy_float (global float* z,
642 global float const* x,
643 global float const* y,
644 float a)
645 {
646 size_t i = get_global_id(0);
647 z[i] = a*x[i] + y[i];
648 }
649 "#;
650
651 let sources = [source];
653 let program = create_program_with_source(context, &sources).unwrap();
654
655 let value = get_program_info(program, CL_PROGRAM_REFERENCE_COUNT).unwrap();
656 let value = cl_uint::from(value);
657 println!("CL_PROGRAM_REFERENCE_COUNT: {}", value);
658 assert!(0 < value);
659
660 let value = get_program_info(program, CL_PROGRAM_CONTEXT).unwrap();
661 let value = intptr_t::from(value);
662 println!("CL_PROGRAM_CONTEXT: {}", value);
663 assert!(0 < value);
664
665 let value = get_program_info(program, CL_PROGRAM_NUM_DEVICES).unwrap();
666 let value = cl_uint::from(value);
667 println!("CL_PROGRAM_NUM_DEVICES: {}", value);
668 assert!(0 < value);
669
670 let value = get_program_info(program, CL_PROGRAM_DEVICES).unwrap();
671 let value = Vec::<intptr_t>::from(value);
672 println!("CL_PROGRAM_DEVICES: {}", value.len());
673 assert!(0 < value.len());
674
675 let value = get_program_info(program, CL_PROGRAM_SOURCE).unwrap();
676 let value = String::from(value);
677 println!("CL_PROGRAM_SOURCE: {}", value);
678 assert!(0 < value.len());
679
680 let options = CString::default();
681 build_program(program, &device_ids, &options, None, ptr::null_mut()).unwrap();
682
683 let value = get_program_build_info(program, device_id, CL_PROGRAM_BUILD_STATUS).unwrap();
684 let value: cl_int = From::from(value);
685 println!("CL_PROGRAM_BUILD_STATUS: {}", value);
686 assert_eq!(CL_BUILD_SUCCESS, value);
687
688 let value = get_program_build_info(program, device_id, CL_PROGRAM_BUILD_OPTIONS).unwrap();
689 let value = String::from(value);
690 println!("CL_PROGRAM_BUILD_OPTIONS: {}", value);
691
692 let value = get_program_build_info(program, device_id, CL_PROGRAM_BUILD_LOG).unwrap();
693 let value = String::from(value);
694 println!("CL_PROGRAM_BUILD_LOG: {}", value);
695
696 let value = get_program_build_info(program, device_id, CL_PROGRAM_BINARY_TYPE).unwrap();
697 let value = cl_uint::from(value);
698 println!("CL_PROGRAM_BINARY_TYPE: {:?}", value);
699 assert_eq!(CL_PROGRAM_BINARY_TYPE_EXECUTABLE as cl_uint, value);
700
701 #[cfg(any(feature = "CL_VERSION_2_0", feature = "dynamic"))]
702 match get_program_build_info(
703 program,
704 device_id,
705 CL_PROGRAM_BUILD_GLOBAL_VARIABLE_TOTAL_SIZE,
706 ) {
707 Ok(value) => {
708 let value = size_t::from(value);
709 println!("CL_PROGRAM_BUILD_GLOBAL_VARIABLE_TOTAL_SIZE: {:?}", value)
710 }
711 Err(e) => println!(
712 "OpenCL error, CL_PROGRAM_BUILD_GLOBAL_VARIABLE_TOTAL_SIZE: {}",
713 error_text(e)
714 ),
715 }
716
717 let value = get_program_info(program, CL_PROGRAM_BINARY_SIZES).unwrap();
718 let value = Vec::<size_t>::from(value);
719 println!("CL_PROGRAM_BINARY_SIZES: {}", value.len());
720 println!("CL_PROGRAM_BINARY_SIZES: {:?}", value);
721 assert!(0 < value.len());
722
723 let value = get_program_info(program, CL_PROGRAM_BINARIES).unwrap();
724 let value = Vec::<Vec<u8>>::from(value);
726 println!("CL_PROGRAM_BINARIES count: {}", value.len());
727 println!("CL_PROGRAM_BINARIES length[0]: {}", value[0].len());
728 assert!(0 < value.len());
729
730 let value = get_program_info(program, CL_PROGRAM_NUM_KERNELS).unwrap();
731 let value = size_t::from(value);
732 println!("CL_PROGRAM_NUM_KERNELS: {}", value);
733 assert!(0 < value);
734
735 let value = get_program_info(program, CL_PROGRAM_KERNEL_NAMES).unwrap();
736 let value = String::from(value);
737 println!("CL_PROGRAM_KERNEL_NAMES: {}", value);
738 assert!(0 < value.len());
739
740 #[cfg(any(feature = "CL_VERSION_2_1", feature = "dynamic"))]
741 match get_program_info(program, CL_PROGRAM_IL) {
742 Ok(value) => {
743 let value = String::from(value);
744 println!("CL_PROGRAM_IL: {}", value)
745 }
746 Err(e) => println!("OpenCL error, CL_PROGRAM_IL: {}", error_text(e)),
747 };
748
749 #[cfg(any(feature = "CL_VERSION_2_2", feature = "dynamic"))]
750 match get_program_info(program, CL_PROGRAM_SCOPE_GLOBAL_CTORS_PRESENT) {
751 Ok(value) => {
752 let value = cl_uint::from(value);
753 println!("CL_PROGRAM_SCOPE_GLOBAL_CTORS_PRESENT: {}", value)
754 }
755 Err(e) => println!(
756 "OpenCL error, CL_PROGRAM_SCOPE_GLOBAL_CTORS_PRESENT: {}",
757 error_text(e)
758 ),
759 };
760
761 #[cfg(any(feature = "CL_VERSION_2_2", feature = "dynamic"))]
762 match get_program_info(program, CL_PROGRAM_SCOPE_GLOBAL_CTORS_PRESENT) {
763 Ok(value) => {
764 let value = cl_uint::from(value);
765 println!("CL_PROGRAM_SCOPE_GLOBAL_DTORS_PRESENT: {}", value)
766 }
767 Err(e) => println!(
768 "OpenCL error, CL_PROGRAM_SCOPE_GLOBAL_DTORS_PRESENT: {}",
769 error_text(e)
770 ),
771 };
772
773 #[cfg(any(feature = "CL_VERSION_1_2", feature = "dynamic"))]
774 if let Err(e) = unsafe { unload_platform_compiler(platform_id) } {
775 println!("OpenCL error, clUnloadPlatformCompiler: {}", error_text(e));
776 }
777
778 unsafe {
779 release_program(program).unwrap();
780 release_context(context).unwrap();
781 }
782 }
783
784 #[test]
785 fn test_compile_and_link_program() {
786 let platform_ids = get_platform_ids().unwrap();
787
788 let mut platform_id = platform_ids[0];
789 let mut device_count: usize = 0;
790
791 for p in platform_ids {
793 let ids = get_device_ids(p, CL_DEVICE_TYPE_ALL).unwrap();
794 let count = ids.len();
795 if device_count < count {
796 device_count = count;
797 platform_id = p;
798 }
799 }
800
801 println!("Platform device_count: {}", device_count);
802
803 let device_ids = get_device_ids(platform_id, CL_DEVICE_TYPE_ALL).unwrap();
804
805 let context = create_context(&device_ids, ptr::null(), None, ptr::null_mut());
806 let context = context.unwrap();
807
808 let source = r#"
809 kernel void saxpy_float (global float* z,
810 global float const* x,
811 global float const* y,
812 float a)
813 {
814 size_t i = get_global_id(0);
815 z[i] = a*x[i] + y[i];
816 }
817 "#;
818
819 let sources = [source];
821 let program = create_program_with_source(context, &sources).unwrap();
822
823 use std::ffi::CString;
824 let no_options = CString::new("").unwrap();
825 compile_program(
826 program,
827 &device_ids,
828 &no_options,
829 &[],
830 &[],
831 None,
832 ptr::null_mut(),
833 )
834 .unwrap();
835
836 let programs = [program];
837 unsafe {
838 link_program(
839 context,
840 &device_ids,
841 &no_options,
842 &programs,
843 None,
844 ptr::null_mut(),
845 )
846 .unwrap()
847 };
848
849 unsafe {
850 release_program(program).unwrap();
851 release_context(context).unwrap();
852 }
853 }
854}