#![allow(non_camel_case_types)]
#![allow(clippy::not_unsafe_ptr_arg_deref, clippy::wildcard_in_or_patterns)]
pub use opencl_sys::{
cl_context, cl_device_id, cl_int, cl_platform_id, cl_program, cl_program_build_info,
cl_program_info, cl_uchar, cl_uint, CL_BUILD_ERROR, CL_BUILD_IN_PROGRESS, CL_BUILD_NONE,
CL_BUILD_SUCCESS, CL_FALSE, CL_INVALID_VALUE, CL_PROGRAM_BINARIES, CL_PROGRAM_BINARY_SIZES,
CL_PROGRAM_BINARY_TYPE, CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT,
CL_PROGRAM_BINARY_TYPE_EXECUTABLE, CL_PROGRAM_BINARY_TYPE_LIBRARY, CL_PROGRAM_BINARY_TYPE_NONE,
CL_PROGRAM_BUILD_GLOBAL_VARIABLE_TOTAL_SIZE, CL_PROGRAM_BUILD_LOG, CL_PROGRAM_BUILD_OPTIONS,
CL_PROGRAM_BUILD_STATUS, CL_PROGRAM_CONTEXT, CL_PROGRAM_DEVICES, CL_PROGRAM_IL,
CL_PROGRAM_KERNEL_NAMES, CL_PROGRAM_NUM_DEVICES, CL_PROGRAM_NUM_KERNELS,
CL_PROGRAM_REFERENCE_COUNT, CL_PROGRAM_SOURCE, CL_SUCCESS, CL_TRUE,
};
use opencl_sys::{
clBuildProgram, clCompileProgram, clCreateProgramWithBinary, clCreateProgramWithSource,
clGetProgramBuildInfo, clGetProgramInfo, clLinkProgram, clReleaseProgram, clRetainProgram,
};
#[cfg(feature = "CL_VERSION_2_1")]
use opencl_sys::clCreateProgramWithIL;
use super::info_type::InfoType;
use super::{
api2_info_size, api2_info_value, api2_info_vector, api_info_size, api_info_value,
api_info_vector,
};
use libc::{c_char, c_uchar, c_void, intptr_t, size_t};
use std::ffi::CStr;
use std::mem;
use std::ptr;
#[cfg_attr(not(target_os = "macos"), link(name = "OpenCL"))]
#[cfg_attr(target_os = "macos", link(name = "OpenCL", kind = "framework"))]
extern "system" {
#[cfg(feature = "CL_VERSION_1_2")]
pub fn clUnloadPlatformCompiler(platform: cl_platform_id) -> cl_int;
#[cfg(feature = "CL_VERSION_1_2")]
pub fn clCreateProgramWithBuiltInKernels(
context: cl_context,
num_devices: cl_uint,
device_list: *const cl_device_id,
kernel_names: *const c_char,
errcode_ret: *mut cl_int,
) -> cl_program;
#[cfg(feature = "CL_VERSION_2_2")]
pub fn clSetProgramReleaseCallback(
program: cl_program,
pfn_notify: Option<extern "C" fn(program: cl_program, user_data: *mut c_void)>,
user_data: *mut c_void,
) -> cl_int;
#[cfg(feature = "CL_VERSION_2_2")]
pub fn clSetProgramSpecializationConstant(
program: cl_program,
spec_id: cl_uint,
spec_size: size_t,
spec_value: *const c_void,
) -> cl_int;
}
pub const CL_PROGRAM_SCOPE_GLOBAL_CTORS_PRESENT: cl_program_info = 0x116A;
pub const CL_PROGRAM_SCOPE_GLOBAL_DTORS_PRESENT: cl_program_info = 0x116B;
#[inline]
pub fn create_program_with_source(
context: cl_context,
sources: &[&str],
) -> Result<cl_program, cl_int> {
let lengths: Vec<size_t> = sources.iter().map(|src| src.len()).collect();
let mut status: cl_int = CL_INVALID_VALUE;
let program: cl_program = unsafe {
clCreateProgramWithSource(
context,
sources.len() as cl_uint,
sources.as_ptr() as *const *const c_char,
lengths.as_ptr(),
&mut status,
)
};
if CL_SUCCESS != status {
Err(status)
} else {
Ok(program)
}
}
pub unsafe fn create_program_with_binary(
context: cl_context,
devices: &[cl_device_id],
binaries: &[&[u8]],
) -> Result<cl_program, cl_int> {
let binaries_length = binaries.len();
let lengths: Vec<size_t> = binaries.iter().map(|bin| bin.len()).collect();
let mut binary_status: Vec<cl_int> = Vec::with_capacity(binaries_length);
let mut status: cl_int = CL_INVALID_VALUE;
let program: cl_program = clCreateProgramWithBinary(
context,
devices.len() as cl_uint,
devices.as_ptr(),
lengths.as_ptr(),
binaries.as_ptr() as *const *const c_uchar,
binary_status.as_mut_ptr(),
&mut status,
);
binary_status.set_len(binaries_length);
if CL_SUCCESS != status {
Err(status)
} else {
Ok(program)
}
}
#[cfg(feature = "CL_VERSION_1_2")]
#[inline]
pub unsafe fn create_program_with_builtin_kernels(
context: cl_context,
devices: &[cl_device_id],
kernel_names: &CStr,
) -> Result<cl_program, cl_int> {
let mut status: cl_int = CL_INVALID_VALUE;
let program: cl_program = clCreateProgramWithBuiltInKernels(
context,
devices.len() as cl_uint,
devices.as_ptr(),
kernel_names.as_ptr(),
&mut status,
);
if CL_SUCCESS != status {
Err(status)
} else {
Ok(program)
}
}
#[cfg(feature = "CL_VERSION_2_1")]
#[inline]
pub fn create_program_with_il(context: cl_context, il: &[u8]) -> Result<cl_program, cl_int> {
let mut status: cl_int = CL_INVALID_VALUE;
let program: cl_program = unsafe {
clCreateProgramWithIL(
context,
il.as_ptr() as *const c_void,
il.len() as size_t,
&mut status,
)
};
if CL_SUCCESS != status {
Err(status)
} else {
Ok(program)
}
}
#[inline]
pub unsafe fn retain_program(program: cl_program) -> Result<(), cl_int> {
let status: cl_int = clRetainProgram(program);
if CL_SUCCESS != status {
Err(status)
} else {
Ok(())
}
}
#[inline]
pub unsafe fn release_program(program: cl_program) -> Result<(), cl_int> {
let status: cl_int = clReleaseProgram(program);
if CL_SUCCESS != status {
Err(status)
} else {
Ok(())
}
}
#[inline]
pub fn build_program(
program: cl_program,
devices: &[cl_device_id],
options: &CStr,
pfn_notify: Option<unsafe extern "C" fn(cl_program, *mut c_void)>,
user_data: *mut c_void,
) -> Result<(), cl_int> {
let status: cl_int = unsafe {
clBuildProgram(
program,
devices.len() as cl_uint,
devices.as_ptr(),
options.as_ptr(),
pfn_notify,
user_data,
)
};
if CL_SUCCESS != status {
Err(status)
} else {
Ok(())
}
}
#[cfg(feature = "CL_VERSION_1_2")]
#[inline]
pub fn compile_program(
program: cl_program,
devices: &[cl_device_id],
options: &CStr,
input_headers: &[cl_program],
header_include_names: &[&CStr],
pfn_notify: Option<unsafe extern "C" fn(program: cl_program, user_data: *mut c_void)>,
user_data: *mut c_void,
) -> Result<(), cl_int> {
assert!(input_headers.len() == header_include_names.len());
let status: cl_int = unsafe {
let input_headers_ptr = if input_headers.is_empty() {
ptr::null()
} else {
input_headers.as_ptr()
};
let header_include_names_ptr = if header_include_names.is_empty() {
ptr::null()
} else {
header_include_names.as_ptr()
};
clCompileProgram(
program,
devices.len() as cl_uint,
devices.as_ptr(),
options.as_ptr(),
input_headers.len() as cl_uint,
input_headers_ptr,
header_include_names_ptr as *const *const c_char,
pfn_notify,
user_data,
)
};
if CL_SUCCESS != status {
Err(status)
} else {
Ok(())
}
}
#[cfg(feature = "CL_VERSION_1_2")]
#[inline]
pub unsafe fn link_program(
context: cl_context,
devices: &[cl_device_id],
options: &CStr,
input_programs: &[cl_program],
pfn_notify: Option<unsafe extern "C" fn(program: cl_program, user_data: *mut c_void)>,
user_data: *mut c_void,
) -> Result<cl_program, cl_int> {
assert!(!input_programs.is_empty());
let mut status: cl_int = CL_INVALID_VALUE;
let programme: cl_program = clLinkProgram(
context,
devices.len() as cl_uint,
devices.as_ptr(),
options.as_ptr(),
input_programs.len() as cl_uint,
input_programs.as_ptr(),
pfn_notify,
user_data,
&mut status,
);
if CL_SUCCESS != status {
Err(status)
} else {
Ok(programme)
}
}
#[cfg(feature = "CL_VERSION_2_2")]
#[inline]
pub unsafe fn set_program_release_callback(
program: cl_program,
pfn_notify: Option<extern "C" fn(program: cl_program, user_data: *mut c_void)>,
user_data: *mut c_void,
) -> Result<(), cl_int> {
let status: cl_int = clSetProgramReleaseCallback(program, pfn_notify, user_data);
if CL_SUCCESS != status {
Err(status)
} else {
Ok(())
}
}
#[cfg(feature = "CL_VERSION_2_2")]
#[inline]
pub unsafe fn set_program_specialization_constant(
program: cl_program,
spec_id: cl_uint,
spec_size: size_t,
spec_value: *const c_void,
) -> Result<(), cl_int> {
let status: cl_int =
clSetProgramSpecializationConstant(program, spec_id, spec_size, spec_value);
if CL_SUCCESS != status {
Err(status)
} else {
Ok(())
}
}
#[cfg(feature = "CL_VERSION_1_2")]
#[inline]
pub unsafe fn unload_platform_compiler(platform: cl_platform_id) -> Result<(), cl_int> {
let status: cl_int = clUnloadPlatformCompiler(platform);
if CL_SUCCESS != status {
Err(status)
} else {
Ok(())
}
}
pub fn get_program_data(
program: cl_program,
param_name: cl_program_info,
) -> Result<Vec<u8>, cl_int> {
api_info_size!(get_size, clGetProgramInfo);
let size = get_size(program, param_name)?;
api_info_vector!(get_vector, u8, clGetProgramInfo);
get_vector(program, param_name, size)
}
pub fn get_program_info(
program: cl_program,
param_name: cl_program_info,
) -> Result<InfoType, cl_int> {
api_info_size!(get_size, clGetProgramInfo);
match param_name {
CL_PROGRAM_REFERENCE_COUNT
| CL_PROGRAM_NUM_DEVICES
| CL_PROGRAM_SCOPE_GLOBAL_CTORS_PRESENT | CL_PROGRAM_SCOPE_GLOBAL_DTORS_PRESENT => {
api_info_value!(get_value, cl_uint, clGetProgramInfo);
Ok(InfoType::Uint(get_value(program, param_name)?))
}
CL_PROGRAM_CONTEXT => {
api_info_value!(get_value, intptr_t, clGetProgramInfo);
Ok(InfoType::Ptr(get_value(program, param_name)?))
}
CL_PROGRAM_DEVICES => {
api_info_vector!(get_vec, intptr_t, clGetProgramInfo);
let size = get_size(program, param_name)?;
Ok(InfoType::VecIntPtr(get_vec(program, param_name, size)?))
}
CL_PROGRAM_BINARY_SIZES => {
api_info_vector!(get_vec, size_t, clGetProgramInfo);
let size = get_size(program, param_name)?;
Ok(InfoType::VecSize(get_vec(program, param_name, size)?))
}
CL_PROGRAM_BINARIES => {
api_info_vector!(get_size_vec, size_t, clGetProgramInfo);
let size = get_size(program, CL_PROGRAM_BINARY_SIZES as cl_program_info)?;
let binary_sizes = get_size_vec(program, CL_PROGRAM_BINARY_SIZES as cl_program_info, size)?;
let binaries = binary_sizes.into_iter().map(|size| {
vec![0u8; size]
}).collect::<Vec<Vec<u8>>>();
let mut binary_ptrs = binaries.iter().map(|vec| {
vec.as_ptr()
}).collect::<Vec<_>>();
let status = unsafe {
clGetProgramInfo(
program,
param_name,
binary_ptrs.len() * mem::size_of::<*mut c_void>(),
binary_ptrs.as_mut_ptr() as *mut _ as *mut c_void,
ptr::null_mut(),
)
};
if CL_SUCCESS != status {
Err(status)
} else {
Ok(InfoType::VecVecUchar(binaries))
}
}
CL_PROGRAM_NUM_KERNELS => {
api_info_value!(get_value, size_t, clGetProgramInfo);
Ok(InfoType::Size(get_value(program, param_name)?))
}
CL_PROGRAM_SOURCE
| CL_PROGRAM_KERNEL_NAMES
| CL_PROGRAM_IL
| _ => {
Ok(InfoType::VecUchar(get_program_data(program, param_name)?))
}
}
}
pub fn get_program_build_data(
program: cl_program,
device: cl_device_id,
param_name: cl_program_info,
) -> Result<Vec<u8>, cl_int> {
api2_info_size!(get_size, cl_device_id, clGetProgramBuildInfo);
let size = get_size(program, device, param_name)?;
api2_info_vector!(get_vector, cl_device_id, u8, clGetProgramBuildInfo);
get_vector(program, device, param_name, size)
}
pub fn get_program_build_info(
program: cl_program,
device: cl_device_id,
param_name: cl_program_build_info,
) -> Result<InfoType, cl_int> {
match param_name {
CL_PROGRAM_BUILD_STATUS => {
api2_info_value!(
get_device_value,
cl_device_id,
cl_int,
clGetProgramBuildInfo
);
Ok(InfoType::Int(get_device_value(
program, device, param_name,
)?))
}
CL_PROGRAM_BINARY_TYPE => {
api2_info_value!(
get_device_value,
cl_device_id,
cl_uint,
clGetProgramBuildInfo
);
Ok(InfoType::Uint(get_device_value(
program, device, param_name,
)?))
}
CL_PROGRAM_BUILD_GLOBAL_VARIABLE_TOTAL_SIZE => {
api2_info_value!(
get_device_value,
cl_device_id,
size_t,
clGetProgramBuildInfo
);
Ok(InfoType::Size(get_device_value(
program, device, param_name,
)?))
}
CL_PROGRAM_BUILD_OPTIONS | CL_PROGRAM_BUILD_LOG | _ => Ok(InfoType::VecUchar(
get_program_build_data(program, device, param_name)?,
)),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::context::{create_context, release_context};
use crate::device::{get_device_ids, CL_DEVICE_TYPE_ALL};
use crate::error_codes::error_text;
use crate::platform::get_platform_ids;
use std::ffi::CString;
#[test]
fn test_program() {
let platform_ids = get_platform_ids().unwrap();
let mut platform_id = platform_ids[0];
let mut device_count: usize = 0;
for p in platform_ids {
let ids = get_device_ids(p, CL_DEVICE_TYPE_ALL).unwrap();
let count = ids.len();
if device_count < count {
device_count = count;
platform_id = p;
}
}
println!("Platform device_count: {}", device_count);
let device_ids = get_device_ids(platform_id, CL_DEVICE_TYPE_ALL).unwrap();
let device_id = device_ids[0];
let context = create_context(&device_ids, ptr::null(), None, ptr::null_mut());
let context = context.unwrap();
let source = r#"
kernel void saxpy_float (global float* z,
global float const* x,
global float const* y,
float a)
{
size_t i = get_global_id(0);
z[i] = a*x[i] + y[i];
}
"#;
let sources = [source];
let program = create_program_with_source(context, &sources).unwrap();
let value = get_program_info(program, CL_PROGRAM_REFERENCE_COUNT).unwrap();
let value = cl_uint::from(value);
println!("CL_PROGRAM_REFERENCE_COUNT: {}", value);
assert!(0 < value);
let value = get_program_info(program, CL_PROGRAM_CONTEXT).unwrap();
let value = intptr_t::from(value);
println!("CL_PROGRAM_CONTEXT: {}", value);
assert!(0 < value);
let value = get_program_info(program, CL_PROGRAM_NUM_DEVICES).unwrap();
let value = cl_uint::from(value);
println!("CL_PROGRAM_NUM_DEVICES: {}", value);
assert!(0 < value);
let value = get_program_info(program, CL_PROGRAM_DEVICES).unwrap();
let value = Vec::<intptr_t>::from(value);
println!("CL_PROGRAM_DEVICES: {}", value.len());
assert!(0 < value.len());
let value = get_program_info(program, CL_PROGRAM_SOURCE).unwrap();
let value = String::from(value);
println!("CL_PROGRAM_SOURCE: {}", value);
assert!(0 < value.len());
let options = CString::default();
build_program(program, &device_ids, &options, None, ptr::null_mut()).unwrap();
let value = get_program_build_info(program, device_id, CL_PROGRAM_BUILD_STATUS).unwrap();
let value: cl_int = From::from(value);
println!("CL_PROGRAM_BUILD_STATUS: {}", value);
assert_eq!(CL_BUILD_SUCCESS, value);
let value = get_program_build_info(program, device_id, CL_PROGRAM_BUILD_OPTIONS).unwrap();
let value = String::from(value);
println!("CL_PROGRAM_BUILD_OPTIONS: {}", value);
let value = get_program_build_info(program, device_id, CL_PROGRAM_BUILD_LOG).unwrap();
let value = String::from(value);
println!("CL_PROGRAM_BUILD_LOG: {}", value);
let value = get_program_build_info(program, device_id, CL_PROGRAM_BINARY_TYPE).unwrap();
let value = cl_uint::from(value);
println!("CL_PROGRAM_BINARY_TYPE: {:?}", value);
assert_eq!(CL_PROGRAM_BINARY_TYPE_EXECUTABLE as cl_uint, value);
#[cfg(feature = "CL_VERSION_2_0")]
match get_program_build_info(
program,
device_id,
CL_PROGRAM_BUILD_GLOBAL_VARIABLE_TOTAL_SIZE,
) {
Ok(value) => {
let value = size_t::from(value);
println!("CL_PROGRAM_BUILD_GLOBAL_VARIABLE_TOTAL_SIZE: {:?}", value)
}
Err(e) => println!(
"OpenCL error, CL_PROGRAM_BUILD_GLOBAL_VARIABLE_TOTAL_SIZE: {}",
error_text(e)
),
}
let value = get_program_info(program, CL_PROGRAM_BINARY_SIZES).unwrap();
let value = Vec::<size_t>::from(value);
println!("CL_PROGRAM_BINARY_SIZES: {}", value.len());
println!("CL_PROGRAM_BINARY_SIZES: {:?}", value);
assert!(0 < value.len());
let value = get_program_info(program, CL_PROGRAM_BINARIES).unwrap();
let value = Vec::<Vec<u8>>::from(value);
println!("CL_PROGRAM_BINARIES count: {}", value.len());
println!("CL_PROGRAM_BINARIES length[0]: {}", value[0].len());
assert!(0 < value.len());
let value = get_program_info(program, CL_PROGRAM_NUM_KERNELS).unwrap();
let value = size_t::from(value);
println!("CL_PROGRAM_NUM_KERNELS: {}", value);
assert!(0 < value);
let value = get_program_info(program, CL_PROGRAM_KERNEL_NAMES).unwrap();
let value = String::from(value);
println!("CL_PROGRAM_KERNEL_NAMES: {}", value);
assert!(0 < value.len());
#[cfg(feature = "CL_VERSION_2_1")]
match get_program_info(program, CL_PROGRAM_IL) {
Ok(value) => {
let value = String::from(value);
println!("CL_PROGRAM_IL: {}", value)
}
Err(e) => println!("OpenCL error, CL_PROGRAM_IL: {}", error_text(e)),
};
#[cfg(feature = "CL_VERSION_2_2")]
match get_program_info(program, CL_PROGRAM_SCOPE_GLOBAL_CTORS_PRESENT) {
Ok(value) => {
let value = cl_uint::from(value);
println!("CL_PROGRAM_SCOPE_GLOBAL_CTORS_PRESENT: {}", value)
}
Err(e) => println!(
"OpenCL error, CL_PROGRAM_SCOPE_GLOBAL_CTORS_PRESENT: {}",
error_text(e)
),
};
#[cfg(feature = "CL_VERSION_2_2")]
match get_program_info(program, CL_PROGRAM_SCOPE_GLOBAL_CTORS_PRESENT) {
Ok(value) => {
let value = cl_uint::from(value);
println!("CL_PROGRAM_SCOPE_GLOBAL_DTORS_PRESENT: {}", value)
}
Err(e) => println!(
"OpenCL error, CL_PROGRAM_SCOPE_GLOBAL_DTORS_PRESENT: {}",
error_text(e)
),
};
#[cfg(feature = "CL_VERSION_1_2")]
if let Err(e) = unsafe { unload_platform_compiler(platform_id) } {
println!("OpenCL error, clUnloadPlatformCompiler: {}", error_text(e));
}
unsafe {
release_program(program).unwrap();
release_context(context).unwrap()
};
}
#[test]
fn test_compile_and_link_program() {
let platform_ids = get_platform_ids().unwrap();
let mut platform_id = platform_ids[0];
let mut device_count: usize = 0;
for p in platform_ids {
let ids = get_device_ids(p, CL_DEVICE_TYPE_ALL).unwrap();
let count = ids.len();
if device_count < count {
device_count = count;
platform_id = p;
}
}
println!("Platform device_count: {}", device_count);
let device_ids = get_device_ids(platform_id, CL_DEVICE_TYPE_ALL).unwrap();
let context = create_context(&device_ids, ptr::null(), None, ptr::null_mut());
let context = context.unwrap();
let source = r#"
kernel void saxpy_float (global float* z,
global float const* x,
global float const* y,
float a)
{
size_t i = get_global_id(0);
z[i] = a*x[i] + y[i];
}
"#;
let sources = [source];
let program = create_program_with_source(context, &sources).unwrap();
use std::ffi::CString;
let no_options = CString::new("").unwrap();
compile_program(
program,
&device_ids,
&no_options,
&[],
&[],
None,
ptr::null_mut(),
)
.unwrap();
let programs = [program];
unsafe {
link_program(
context,
&device_ids,
&no_options,
&programs,
None,
ptr::null_mut(),
)
.unwrap()
};
unsafe {
release_program(program).unwrap();
release_context(context).unwrap();
}
}
}