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_BUILD_ERROR, CL_BUILD_IN_PROGRESS, CL_BUILD_NONE, CL_BUILD_SUCCESS, CL_FALSE,
23 CL_INVALID_VALUE, CL_PROGRAM_BINARIES, CL_PROGRAM_BINARY_SIZES, CL_PROGRAM_BINARY_TYPE,
24 CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT, CL_PROGRAM_BINARY_TYPE_EXECUTABLE,
25 CL_PROGRAM_BINARY_TYPE_LIBRARY, CL_PROGRAM_BINARY_TYPE_NONE,
26 CL_PROGRAM_BUILD_GLOBAL_VARIABLE_TOTAL_SIZE, CL_PROGRAM_BUILD_LOG, CL_PROGRAM_BUILD_OPTIONS,
27 CL_PROGRAM_BUILD_STATUS, CL_PROGRAM_CONTEXT, CL_PROGRAM_DEVICES, CL_PROGRAM_IL,
28 CL_PROGRAM_KERNEL_NAMES, CL_PROGRAM_NUM_DEVICES, CL_PROGRAM_NUM_KERNELS,
29 CL_PROGRAM_REFERENCE_COUNT, CL_PROGRAM_SOURCE, CL_SUCCESS, CL_TRUE, cl_context, cl_device_id,
30 cl_int, cl_platform_id, cl_program, cl_program_build_info, cl_program_info, cl_uchar, cl_uint,
31};
32
33use super::info_type::InfoType;
34use super::{
35 api_info_size, api_info_value, api_info_vector, api2_info_size, api2_info_value,
36 api2_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 &raw 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 &raw 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 &raw 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 &raw 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 let devices_ptr = if devices.is_empty() {
248 ptr::null()
249 } else {
250 devices.as_ptr()
251 };
252 cl_call!(clBuildProgram(
253 program,
254 devices.len() as cl_uint,
255 devices_ptr,
256 options.as_ptr(),
257 pfn_notify,
258 user_data,
259 ))
260 };
261 if CL_SUCCESS == status {
262 Ok(())
263 } else {
264 Err(status)
265 }
266}
267
268#[cfg(any(feature = "CL_VERSION_1_2", feature = "dynamic"))]
288#[allow(clippy::cast_possible_truncation)]
289#[inline]
290pub fn compile_program(
291 program: cl_program,
292 devices: &[cl_device_id],
293 options: &CStr,
294 input_headers: &[cl_program],
295 header_include_names: &[&CStr],
296 pfn_notify: Option<unsafe extern "C" fn(program: cl_program, user_data: *mut c_void)>,
297 user_data: *mut c_void,
298) -> Result<(), cl_int> {
299 assert!(input_headers.len() == header_include_names.len());
300 let status: cl_int = unsafe {
301 let devices_ptr = if devices.is_empty() {
302 ptr::null()
303 } else {
304 devices.as_ptr()
305 };
306 let input_headers_ptr = if input_headers.is_empty() {
307 ptr::null()
308 } else {
309 input_headers.as_ptr()
310 };
311 let header_include_names_ptr = if header_include_names.is_empty() {
312 ptr::null()
313 } else {
314 header_include_names.as_ptr()
315 };
316 cl_call!(clCompileProgram(
317 program,
318 devices.len() as cl_uint,
319 devices_ptr,
320 options.as_ptr(),
321 input_headers.len() as cl_uint,
322 input_headers_ptr,
323 header_include_names_ptr.cast::<*const c_char>(),
324 pfn_notify,
325 user_data,
326 ))
327 };
328 if CL_SUCCESS == status {
329 Ok(())
330 } else {
331 Err(status)
332 }
333}
334
335#[cfg(any(feature = "CL_VERSION_1_2", feature = "dynamic"))]
357#[allow(clippy::cast_possible_truncation)]
358#[inline]
359pub unsafe fn link_program(
360 context: cl_context,
361 devices: &[cl_device_id],
362 options: &CStr,
363 input_programs: &[cl_program],
364 pfn_notify: Option<unsafe extern "C" fn(program: cl_program, user_data: *mut c_void)>,
365 user_data: *mut c_void,
366) -> Result<cl_program, cl_int> {
367 assert!(!input_programs.is_empty());
368 let devices_ptr = if devices.is_empty() {
369 ptr::null()
370 } else {
371 devices.as_ptr()
372 };
373 let mut status: cl_int = CL_INVALID_VALUE;
374 let programme: cl_program = cl_call!(clLinkProgram(
375 context,
376 devices.len() as cl_uint,
377 devices_ptr,
378 options.as_ptr(),
379 input_programs.len() as cl_uint,
380 input_programs.as_ptr(),
381 pfn_notify,
382 user_data,
383 &raw mut status,
384 ));
385 if CL_SUCCESS == status {
386 Ok(programme)
387 } else {
388 Err(status)
389 }
390}
391
392#[cfg(any(feature = "CL_VERSION_2_2", feature = "dynamic"))]
408#[inline]
409pub unsafe fn set_program_specialization_constant(
410 program: cl_program,
411 spec_id: cl_uint,
412 spec_size: size_t,
413 spec_value: *const c_void,
414) -> Result<(), cl_int> {
415 let status: cl_int = cl_call!(clSetProgramSpecializationConstant(
416 program, spec_id, spec_size, spec_value
417 ));
418 if CL_SUCCESS == status {
419 Ok(())
420 } else {
421 Err(status)
422 }
423}
424
425#[cfg(any(feature = "CL_VERSION_1_2", feature = "dynamic"))]
436#[inline]
437pub unsafe fn unload_platform_compiler(platform: cl_platform_id) -> Result<(), cl_int> {
438 let status: cl_int = cl_call!(clUnloadPlatformCompiler(platform));
439 if CL_SUCCESS == status {
440 Ok(())
441 } else {
442 Err(status)
443 }
444}
445
446pub fn get_program_data(
449 program: cl_program,
450 param_name: cl_program_info,
451) -> Result<Vec<u8>, cl_int> {
452 api_info_size!(get_size, clGetProgramInfo);
453 let size = get_size(program, param_name)?;
454 api_info_vector!(get_vector, u8, clGetProgramInfo);
455 get_vector(program, param_name, size)
456}
457
458pub fn get_program_info(
468 program: cl_program,
469 param_name: cl_program_info,
470) -> Result<InfoType, cl_int> {
471 api_info_size!(get_size, clGetProgramInfo);
472
473 match param_name {
474 CL_PROGRAM_REFERENCE_COUNT
475 | CL_PROGRAM_NUM_DEVICES
476 | CL_PROGRAM_SCOPE_GLOBAL_CTORS_PRESENT | CL_PROGRAM_SCOPE_GLOBAL_DTORS_PRESENT => {
479 api_info_value!(get_value, cl_uint, clGetProgramInfo);
480 Ok(InfoType::Uint(get_value(program, param_name)?))
481 }
482
483 CL_PROGRAM_CONTEXT => {
484 api_info_value!(get_value, intptr_t, clGetProgramInfo);
485 Ok(InfoType::Ptr(get_value(program, param_name)?))
486 }
487
488 CL_PROGRAM_DEVICES => {
489 api_info_vector!(get_vec, intptr_t, clGetProgramInfo);
490 let size = get_size(program, param_name)?;
491 Ok(InfoType::VecIntPtr(get_vec(program, param_name, size)?))
492 }
493
494 CL_PROGRAM_BINARY_SIZES => {
495 api_info_vector!(get_vec, size_t, clGetProgramInfo);
496 let size = get_size(program, param_name)?;
497 Ok(InfoType::VecSize(get_vec(program, param_name, size)?))
498 }
499
500 CL_PROGRAM_BINARIES => {
501 api_info_vector!(get_size_vec, size_t, clGetProgramInfo);
505 let size = get_size(program, CL_PROGRAM_BINARY_SIZES as cl_program_info)?;
506 let binary_sizes = get_size_vec(program, CL_PROGRAM_BINARY_SIZES as cl_program_info, size)?;
507
508 let binaries = binary_sizes.into_iter().map(|size| {
510 vec![0u8; size]
511 }).collect::<Vec<Vec<u8>>>();
512
513 let mut binary_ptrs = binaries.iter().map(|vec| {
515 vec.as_ptr()
516 }).collect::<Vec<_>>();
517
518 let status = unsafe {
519 cl_call!(clGetProgramInfo(
520 program,
521 param_name,
522 binary_ptrs.len() * mem::size_of::<*mut c_void>(),
523 binary_ptrs.as_mut_ptr().cast(),
524 ptr::null_mut(),
525 ))
526 };
527 if CL_SUCCESS == status {
528 Ok(InfoType::VecVecUchar(binaries))
529 } else {
530 Err(status)
531 }
532 }
533
534 CL_PROGRAM_NUM_KERNELS => {
535 api_info_value!(get_value, size_t, clGetProgramInfo);
536 Ok(InfoType::Size(get_value(program, param_name)?))
537 }
538
539 CL_PROGRAM_SOURCE
540 | CL_PROGRAM_KERNEL_NAMES
541 | CL_PROGRAM_IL
542 | _ => {
543 Ok(InfoType::VecUchar(get_program_data(program, param_name)?))
544 }
545 }
546}
547
548pub fn get_program_build_data(
551 program: cl_program,
552 device: cl_device_id,
553 param_name: cl_program_info,
554) -> Result<Vec<u8>, cl_int> {
555 api2_info_size!(get_size, cl_device_id, clGetProgramBuildInfo);
556 let size = get_size(program, device, param_name)?;
557 api2_info_vector!(get_vector, cl_device_id, u8, clGetProgramBuildInfo);
558 get_vector(program, device, param_name, size)
559}
560
561pub fn get_program_build_info(
572 program: cl_program,
573 device: cl_device_id,
574 param_name: cl_program_build_info,
575) -> Result<InfoType, cl_int> {
576 match param_name {
577 CL_PROGRAM_BUILD_STATUS => {
578 api2_info_value!(
579 get_device_value,
580 cl_device_id,
581 cl_int,
582 clGetProgramBuildInfo
583 );
584 Ok(InfoType::Int(get_device_value(
585 program, device, param_name,
586 )?))
587 }
588
589 CL_PROGRAM_BINARY_TYPE => {
590 api2_info_value!(
591 get_device_value,
592 cl_device_id,
593 cl_uint,
594 clGetProgramBuildInfo
595 );
596 Ok(InfoType::Uint(get_device_value(
597 program, device, param_name,
598 )?))
599 }
600
601 CL_PROGRAM_BUILD_GLOBAL_VARIABLE_TOTAL_SIZE => {
603 api2_info_value!(
604 get_device_value,
605 cl_device_id,
606 size_t,
607 clGetProgramBuildInfo
608 );
609 Ok(InfoType::Size(get_device_value(
610 program, device, param_name,
611 )?))
612 }
613
614 CL_PROGRAM_BUILD_OPTIONS | CL_PROGRAM_BUILD_LOG | _ => Ok(InfoType::VecUchar(
615 get_program_build_data(program, device, param_name)?,
616 )),
617 }
618}
619
620#[cfg(test)]
621mod tests {
622 use super::*;
623 use crate::context::{create_context, release_context};
624 use crate::device::{CL_DEVICE_TYPE_ALL, get_device_ids};
625 #[allow(unused_imports)]
626 use crate::error_codes::error_text;
627 use crate::platform::get_platform_ids;
628 use std::ffi::CString;
629
630 #[test]
631 fn test_program() {
632 let platform_ids = get_platform_ids().unwrap();
633
634 let mut platform_id = platform_ids[0];
635 let mut device_count: usize = 0;
636
637 for p in platform_ids {
639 let ids = get_device_ids(p, CL_DEVICE_TYPE_ALL).unwrap();
640 let count = ids.len();
641 if device_count < count {
642 device_count = count;
643 platform_id = p;
644 }
645 }
646
647 println!("Platform device_count: {}", device_count);
648
649 let device_ids = get_device_ids(platform_id, CL_DEVICE_TYPE_ALL).unwrap();
650 let device_id = device_ids[0];
651
652 let context = create_context(&device_ids, ptr::null(), None, ptr::null_mut());
653 let context = context.unwrap();
654
655 let source = r#"
656 kernel void saxpy_float (global float* z,
657 global float const* x,
658 global float const* y,
659 float a)
660 {
661 size_t i = get_global_id(0);
662 z[i] = a*x[i] + y[i];
663 }
664 "#;
665
666 let sources = [source];
668 let program = create_program_with_source(context, &sources).unwrap();
669
670 let value = get_program_info(program, CL_PROGRAM_REFERENCE_COUNT).unwrap();
671 let value = cl_uint::from(value);
672 println!("CL_PROGRAM_REFERENCE_COUNT: {}", value);
673 assert!(0 < value);
674
675 let value = get_program_info(program, CL_PROGRAM_CONTEXT).unwrap();
676 let value = intptr_t::from(value);
677 println!("CL_PROGRAM_CONTEXT: {}", value);
678 assert!(0 < value);
679
680 let value = get_program_info(program, CL_PROGRAM_NUM_DEVICES).unwrap();
681 let value = cl_uint::from(value);
682 println!("CL_PROGRAM_NUM_DEVICES: {}", value);
683 assert!(0 < value);
684
685 let value = get_program_info(program, CL_PROGRAM_DEVICES).unwrap();
686 let value = Vec::<intptr_t>::from(value);
687 println!("CL_PROGRAM_DEVICES: {}", value.len());
688 assert!(0 < value.len());
689
690 let value = get_program_info(program, CL_PROGRAM_SOURCE).unwrap();
691 let value = String::from(value);
692 println!("CL_PROGRAM_SOURCE: {}", value);
693 assert!(0 < value.len());
694
695 let options = CString::default();
696 let empty_device_ids = Vec::new();
697 build_program(program, &empty_device_ids, &options, None, ptr::null_mut()).unwrap();
698
699 let value = get_program_build_info(program, device_id, CL_PROGRAM_BUILD_STATUS).unwrap();
700 let value: cl_int = From::from(value);
701 println!("CL_PROGRAM_BUILD_STATUS: {}", value);
702 assert_eq!(CL_BUILD_SUCCESS, value);
703
704 let value = get_program_build_info(program, device_id, CL_PROGRAM_BUILD_OPTIONS).unwrap();
705 let value = String::from(value);
706 println!("CL_PROGRAM_BUILD_OPTIONS: {}", value);
707
708 let value = get_program_build_info(program, device_id, CL_PROGRAM_BUILD_LOG).unwrap();
709 let value = String::from(value);
710 println!("CL_PROGRAM_BUILD_LOG: {}", value);
711
712 let value = get_program_build_info(program, device_id, CL_PROGRAM_BINARY_TYPE).unwrap();
713 let value = cl_uint::from(value);
714 println!("CL_PROGRAM_BINARY_TYPE: {:?}", value);
715 assert_eq!(CL_PROGRAM_BINARY_TYPE_EXECUTABLE as cl_uint, value);
716
717 #[cfg(any(feature = "CL_VERSION_2_0", feature = "dynamic"))]
718 match get_program_build_info(
719 program,
720 device_id,
721 CL_PROGRAM_BUILD_GLOBAL_VARIABLE_TOTAL_SIZE,
722 ) {
723 Ok(value) => {
724 let value = size_t::from(value);
725 println!("CL_PROGRAM_BUILD_GLOBAL_VARIABLE_TOTAL_SIZE: {:?}", value)
726 }
727 Err(e) => println!(
728 "OpenCL error, CL_PROGRAM_BUILD_GLOBAL_VARIABLE_TOTAL_SIZE: {}",
729 error_text(e)
730 ),
731 }
732
733 let value = get_program_info(program, CL_PROGRAM_BINARY_SIZES).unwrap();
734 let value = Vec::<size_t>::from(value);
735 println!("CL_PROGRAM_BINARY_SIZES: {}", value.len());
736 println!("CL_PROGRAM_BINARY_SIZES: {:?}", value);
737 assert!(0 < value.len());
738
739 let value = get_program_info(program, CL_PROGRAM_BINARIES).unwrap();
740 let value = Vec::<Vec<u8>>::from(value);
742 println!("CL_PROGRAM_BINARIES count: {}", value.len());
743 println!("CL_PROGRAM_BINARIES length[0]: {}", value[0].len());
744 assert!(0 < value.len());
745
746 let value = get_program_info(program, CL_PROGRAM_NUM_KERNELS).unwrap();
747 let value = size_t::from(value);
748 println!("CL_PROGRAM_NUM_KERNELS: {}", value);
749 assert!(0 < value);
750
751 let value = get_program_info(program, CL_PROGRAM_KERNEL_NAMES).unwrap();
752 let value = String::from(value);
753 println!("CL_PROGRAM_KERNEL_NAMES: {}", value);
754 assert!(0 < value.len());
755
756 #[cfg(any(feature = "CL_VERSION_2_1", feature = "dynamic"))]
757 match get_program_info(program, CL_PROGRAM_IL) {
758 Ok(value) => {
759 let value = String::from(value);
760 println!("CL_PROGRAM_IL: {}", value)
761 }
762 Err(e) => println!("OpenCL error, CL_PROGRAM_IL: {}", error_text(e)),
763 };
764
765 #[cfg(any(feature = "CL_VERSION_2_2", feature = "dynamic"))]
766 match get_program_info(program, CL_PROGRAM_SCOPE_GLOBAL_CTORS_PRESENT) {
767 Ok(value) => {
768 let value = cl_uint::from(value);
769 println!("CL_PROGRAM_SCOPE_GLOBAL_CTORS_PRESENT: {}", value)
770 }
771 Err(e) => println!(
772 "OpenCL error, CL_PROGRAM_SCOPE_GLOBAL_CTORS_PRESENT: {}",
773 error_text(e)
774 ),
775 };
776
777 #[cfg(any(feature = "CL_VERSION_2_2", feature = "dynamic"))]
778 match get_program_info(program, CL_PROGRAM_SCOPE_GLOBAL_CTORS_PRESENT) {
779 Ok(value) => {
780 let value = cl_uint::from(value);
781 println!("CL_PROGRAM_SCOPE_GLOBAL_DTORS_PRESENT: {}", value)
782 }
783 Err(e) => println!(
784 "OpenCL error, CL_PROGRAM_SCOPE_GLOBAL_DTORS_PRESENT: {}",
785 error_text(e)
786 ),
787 };
788
789 #[cfg(any(feature = "CL_VERSION_1_2", feature = "dynamic"))]
790 if let Err(e) = unsafe { unload_platform_compiler(platform_id) } {
791 println!("OpenCL error, clUnloadPlatformCompiler: {}", error_text(e));
792 }
793
794 unsafe {
795 release_program(program).unwrap();
796 release_context(context).unwrap();
797 }
798 }
799
800 #[test]
801 fn test_compile_and_link_program() {
802 let platform_ids = get_platform_ids().unwrap();
803
804 let mut platform_id = platform_ids[0];
805 let mut device_count: usize = 0;
806
807 for p in platform_ids {
809 let ids = get_device_ids(p, CL_DEVICE_TYPE_ALL).unwrap();
810 let count = ids.len();
811 if device_count < count {
812 device_count = count;
813 platform_id = p;
814 }
815 }
816
817 println!("Platform device_count: {}", device_count);
818
819 let device_ids = get_device_ids(platform_id, CL_DEVICE_TYPE_ALL).unwrap();
820
821 let context = create_context(&device_ids, ptr::null(), None, ptr::null_mut());
822 let context = context.unwrap();
823
824 let source = r#"
825 kernel void saxpy_float (global float* z,
826 global float const* x,
827 global float const* y,
828 float a)
829 {
830 size_t i = get_global_id(0);
831 z[i] = a*x[i] + y[i];
832 }
833 "#;
834
835 let sources = [source];
837 let program = create_program_with_source(context, &sources).unwrap();
838
839 use std::ffi::CString;
840 let no_options = CString::new("").unwrap();
841 compile_program(
842 program,
843 &device_ids,
844 &no_options,
845 &[],
846 &[],
847 None,
848 ptr::null_mut(),
849 )
850 .unwrap();
851
852 let programs = [program];
853 unsafe {
854 link_program(
855 context,
856 &device_ids,
857 &no_options,
858 &programs,
859 None,
860 ptr::null_mut(),
861 )
862 .unwrap()
863 };
864
865 unsafe {
866 release_program(program).unwrap();
867 release_context(context).unwrap();
868 }
869 }
870}