use crate::core::{
self, util, ClDeviceIdPtr, DeviceId as DeviceIdCore, DeviceInfo, DeviceInfoResult, DeviceType,
};
use crate::error::{Error as OclError, Result as OclResult};
use crate::ffi::cl_device_id;
use crate::standard::Platform;
use std;
use std::borrow::Borrow;
use std::ops::{Deref, DerefMut};
#[derive(Debug, thiserror::Error)]
pub enum DeviceError {
#[error("No devices found on the specified platform.")]
NoDevices,
#[error("Empty device list provided.")]
ResolveIdxsEmptyDeviceList,
#[error("An index in the resolve list is out of range (index: {idx}, max: {max})")]
ResolveIdxsInvalidIndex { idx: usize, max: usize },
}
#[derive(Debug, Clone)]
pub enum DeviceSpecifier {
All,
First,
Single(Device),
List(Vec<Device>),
Indices(Vec<usize>),
WrappingIndices(Vec<usize>),
TypeFlags(DeviceType),
}
impl DeviceSpecifier {
pub fn all(self) -> DeviceSpecifier {
DeviceSpecifier::All
}
pub fn first(self) -> DeviceSpecifier {
DeviceSpecifier::First
}
pub fn single(self, device: Device) -> DeviceSpecifier {
DeviceSpecifier::Single(device)
}
pub fn list(self, list: Vec<Device>) -> DeviceSpecifier {
DeviceSpecifier::List(list)
}
pub unsafe fn indices(self, indices: Vec<usize>) -> DeviceSpecifier {
DeviceSpecifier::Indices(indices)
}
pub fn wrapping_indices(self, windices: Vec<usize>) -> DeviceSpecifier {
DeviceSpecifier::WrappingIndices(windices)
}
pub fn type_flags(self, flags: DeviceType) -> DeviceSpecifier {
DeviceSpecifier::TypeFlags(flags)
}
pub fn to_device_list<P: Borrow<Platform>>(
&self,
platform: Option<P>,
) -> OclResult<Vec<Device>> {
let platform = platform.map(|p| *p.borrow()).unwrap_or_default();
match *self {
DeviceSpecifier::All => Device::list_all(&platform).map_err(OclError::from),
DeviceSpecifier::First => Device::list_select(&platform, None, &[0]),
DeviceSpecifier::Single(ref device) => Ok(vec![*device]),
DeviceSpecifier::List(ref devices) => Ok(devices.clone()),
DeviceSpecifier::Indices(ref idx_list) => {
Device::list_select(&platform, None, idx_list)
}
DeviceSpecifier::WrappingIndices(ref idx_list) => {
Device::list_select_wrap(&platform, None, idx_list).map_err(OclError::from)
}
DeviceSpecifier::TypeFlags(flags) => {
Device::list(&platform, Some(flags)).map_err(OclError::from)
}
}
}
}
impl Default for DeviceSpecifier {
fn default() -> DeviceSpecifier {
DeviceSpecifier::All
}
}
impl From<usize> for DeviceSpecifier {
fn from(index: usize) -> DeviceSpecifier {
DeviceSpecifier::WrappingIndices(vec![index])
}
}
impl<'a> From<&'a [usize]> for DeviceSpecifier {
fn from(indices: &'a [usize]) -> DeviceSpecifier {
DeviceSpecifier::WrappingIndices(indices.into())
}
}
impl<'a> From<&'a Vec<usize>> for DeviceSpecifier {
fn from(indices: &'a Vec<usize>) -> DeviceSpecifier {
DeviceSpecifier::WrappingIndices(indices.clone())
}
}
impl<'a> From<&'a [Device]> for DeviceSpecifier {
fn from(devices: &'a [Device]) -> DeviceSpecifier {
DeviceSpecifier::List(devices.into())
}
}
impl<'a> From<&'a Vec<Device>> for DeviceSpecifier {
fn from(devices: &'a Vec<Device>) -> DeviceSpecifier {
DeviceSpecifier::List(devices.clone())
}
}
impl From<Device> for DeviceSpecifier {
fn from(device: Device) -> DeviceSpecifier {
DeviceSpecifier::Single(device)
}
}
impl<'a> From<&'a Device> for DeviceSpecifier {
fn from(device: &'a Device) -> DeviceSpecifier {
DeviceSpecifier::Single(*device)
}
}
impl From<DeviceIdCore> for DeviceSpecifier {
fn from(device: DeviceIdCore) -> DeviceSpecifier {
DeviceSpecifier::Single(device.into())
}
}
impl<'a> From<&'a DeviceIdCore> for DeviceSpecifier {
fn from(device: &'a DeviceIdCore) -> DeviceSpecifier {
DeviceSpecifier::Single(device.clone().into())
}
}
impl From<DeviceType> for DeviceSpecifier {
fn from(flags: DeviceType) -> DeviceSpecifier {
DeviceSpecifier::TypeFlags(flags)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct Device(DeviceIdCore);
impl Device {
pub fn first<P: Borrow<Platform>>(platform: P) -> OclResult<Device> {
let device_ids = core::get_device_ids(platform.borrow(), None, None)?;
if device_ids.is_empty() {
return Err(DeviceError::NoDevices.into());
}
Ok(Device(device_ids[0]))
}
pub fn by_idx_wrap<P: Borrow<Platform>>(
platform: P,
device_idx_wrap: usize,
) -> OclResult<Device> {
let device_ids = core::get_device_ids(platform.borrow(), None, None)?;
if device_ids.is_empty() {
return Err(DeviceError::NoDevices.into());
}
let wrapped_idx = device_idx_wrap % device_ids.len();
Ok(Device(device_ids[wrapped_idx]))
}
pub fn specifier() -> DeviceSpecifier {
DeviceSpecifier::default()
}
pub fn resolve_idxs(idxs: &[usize], devices: &[Device]) -> OclResult<Vec<Device>> {
if devices.is_empty() {
return Err(DeviceError::ResolveIdxsEmptyDeviceList.into());
}
let mut result = Vec::with_capacity(idxs.len());
for &idx in idxs.iter() {
match devices.get(idx) {
Some(&device) => result.push(device),
None => {
return Err(DeviceError::ResolveIdxsInvalidIndex {
idx,
max: devices.len(),
}
.into())
}
}
}
Ok(result)
}
pub fn resolve_idxs_wrap(idxs: &[usize], devices: &[Device]) -> Vec<Device> {
let valid_idxs = util::wrap_vals(idxs, devices.len());
valid_idxs.iter().map(|&idx| devices[idx]).collect()
}
pub fn list<P: Borrow<Platform>>(
platform: P,
device_types: Option<DeviceType>,
) -> OclResult<Vec<Device>> {
let list_core =
core::get_device_ids(platform.borrow(), device_types, None).unwrap_or(vec![]);
Ok(list_core.into_iter().map(Device).collect())
}
pub fn list_all<P: Borrow<Platform>>(platform: P) -> OclResult<Vec<Device>> {
Self::list(platform, None)
}
pub fn list_select<P: Borrow<Platform>>(
platform: P,
device_types: Option<DeviceType>,
idxs: &[usize],
) -> OclResult<Vec<Device>> {
Self::resolve_idxs(idxs, &Self::list(platform, device_types)?)
}
pub fn list_select_wrap<P: Borrow<Platform>>(
platform: P,
device_types: Option<DeviceType>,
idxs: &[usize],
) -> OclResult<Vec<Device>> {
Ok(Self::resolve_idxs_wrap(
idxs,
&Self::list(platform, device_types)?,
))
}
pub fn list_from_core(mut devices: Vec<DeviceIdCore>) -> Vec<Device> {
use std::mem;
debug_assert!(mem::size_of::<DeviceIdCore>() == mem::size_of::<Device>());
unsafe {
let (ptr, len, cap) = (devices.as_mut_ptr(), devices.len(), devices.capacity());
mem::forget(devices);
Vec::from_raw_parts(ptr as *mut Device, len, cap)
}
}
pub fn name(&self) -> OclResult<String> {
core::get_device_info(&self.0, DeviceInfo::Name)
.map(|r| r.to_string())
.map_err(OclError::from)
}
pub fn vendor(&self) -> OclResult<String> {
core::get_device_info(&self.0, DeviceInfo::Vendor)
.map(|r| r.to_string())
.map_err(OclError::from)
}
pub fn max_wg_size(&self) -> OclResult<usize> {
match self.info(DeviceInfo::MaxWorkGroupSize) {
Ok(DeviceInfoResult::MaxWorkGroupSize(r)) => Ok(r),
Err(err) => Err(err),
_ => panic!("Device::max_wg_size: Unexpected 'DeviceInfoResult' variant."),
}
}
pub fn mem_base_addr_align(&self) -> OclResult<u32> {
match self.info(DeviceInfo::MemBaseAddrAlign) {
Ok(DeviceInfoResult::MemBaseAddrAlign(r)) => Ok(r),
Err(err) => Err(err),
_ => panic!("Device::mem_base_addr_align: Unexpected 'DeviceInfoResult' variant."),
}
}
pub fn is_available(&self) -> OclResult<bool> {
match self.info(DeviceInfo::Available) {
Ok(DeviceInfoResult::Available(r)) => Ok(r),
Err(err) => Err(err),
_ => panic!("Device::is_available: Unexpected 'DeviceInfoResult' variant."),
}
}
pub fn info_raw(&self, info_kind: u32) -> OclResult<Vec<u8>> {
core::get_device_info_raw(&self.0, info_kind).map_err(OclError::from)
}
pub fn info(&self, info_kind: DeviceInfo) -> OclResult<DeviceInfoResult> {
core::get_device_info(&self.0, info_kind).map_err(OclError::from)
}
pub fn to_string(&self) -> String {
self.clone().into()
}
pub fn as_core(&self) -> &DeviceIdCore {
&self.0
}
fn fmt_info(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.debug_struct("Device")
.field("Type", &self.info(DeviceInfo::Type))
.field("VendorId", &self.info(DeviceInfo::VendorId))
.field("MaxComputeUnits", &self.info(DeviceInfo::MaxComputeUnits))
.field(
"MaxWorkItemDimensions",
&self.info(DeviceInfo::MaxWorkItemDimensions),
)
.field("MaxWorkGroupSize", &self.info(DeviceInfo::MaxWorkGroupSize))
.field("MaxWorkItemSizes", &self.info(DeviceInfo::MaxWorkItemSizes))
.field(
"PreferredVectorWidthChar",
&self.info(DeviceInfo::PreferredVectorWidthChar),
)
.field(
"PreferredVectorWidthShort",
&self.info(DeviceInfo::PreferredVectorWidthShort),
)
.field(
"PreferredVectorWidthInt",
&self.info(DeviceInfo::PreferredVectorWidthInt),
)
.field(
"PreferredVectorWidthLong",
&self.info(DeviceInfo::PreferredVectorWidthLong),
)
.field(
"PreferredVectorWidthFloat",
&self.info(DeviceInfo::PreferredVectorWidthFloat),
)
.field(
"PreferredVectorWidthDouble",
&self.info(DeviceInfo::PreferredVectorWidthDouble),
)
.field(
"MaxClockFrequency",
&self.info(DeviceInfo::MaxClockFrequency),
)
.field("AddressBits", &self.info(DeviceInfo::AddressBits))
.field("MaxReadImageArgs", &self.info(DeviceInfo::MaxReadImageArgs))
.field(
"MaxWriteImageArgs",
&self.info(DeviceInfo::MaxWriteImageArgs),
)
.field("MaxMemAllocSize", &self.info(DeviceInfo::MaxMemAllocSize))
.field("Image2dMaxWidth", &self.info(DeviceInfo::Image2dMaxWidth))
.field("Image2dMaxHeight", &self.info(DeviceInfo::Image2dMaxHeight))
.field("Image3dMaxWidth", &self.info(DeviceInfo::Image3dMaxWidth))
.field("Image3dMaxHeight", &self.info(DeviceInfo::Image3dMaxHeight))
.field("Image3dMaxDepth", &self.info(DeviceInfo::Image3dMaxDepth))
.field("ImageSupport", &self.info(DeviceInfo::ImageSupport))
.field("MaxParameterSize", &self.info(DeviceInfo::MaxParameterSize))
.field("MaxSamplers", &self.info(DeviceInfo::MaxSamplers))
.field("MemBaseAddrAlign", &self.info(DeviceInfo::MemBaseAddrAlign))
.field(
"MinDataTypeAlignSize",
&self.info(DeviceInfo::MinDataTypeAlignSize),
)
.field("SingleFpConfig", &self.info(DeviceInfo::SingleFpConfig))
.field(
"GlobalMemCacheType",
&self.info(DeviceInfo::GlobalMemCacheType),
)
.field(
"GlobalMemCachelineSize",
&self.info(DeviceInfo::GlobalMemCachelineSize),
)
.field(
"GlobalMemCacheSize",
&self.info(DeviceInfo::GlobalMemCacheSize),
)
.field("GlobalMemSize", &self.info(DeviceInfo::GlobalMemSize))
.field(
"MaxConstantBufferSize",
&self.info(DeviceInfo::MaxConstantBufferSize),
)
.field("MaxConstantArgs", &self.info(DeviceInfo::MaxConstantArgs))
.field("LocalMemType", &self.info(DeviceInfo::LocalMemType))
.field("LocalMemSize", &self.info(DeviceInfo::LocalMemSize))
.field(
"ErrorCorrectionSupport",
&self.info(DeviceInfo::ErrorCorrectionSupport),
)
.field(
"ProfilingTimerResolution",
&self.info(DeviceInfo::ProfilingTimerResolution),
)
.field("EndianLittle", &self.info(DeviceInfo::EndianLittle))
.field("Available", &self.info(DeviceInfo::Available))
.field(
"CompilerAvailable",
&self.info(DeviceInfo::CompilerAvailable),
)
.field(
"ExecutionCapabilities",
&self.info(DeviceInfo::ExecutionCapabilities),
)
.field("QueueProperties", &self.info(DeviceInfo::QueueProperties))
.field("Name", &self.info(DeviceInfo::Name))
.field("Vendor", &self.info(DeviceInfo::Vendor))
.field("DriverVersion", &self.info(DeviceInfo::DriverVersion))
.field("Profile", &self.info(DeviceInfo::Profile))
.field("Version", &self.info(DeviceInfo::Version))
.field("Extensions", &self.info(DeviceInfo::Extensions))
.field("Platform", &self.info(DeviceInfo::Platform))
.field("DoubleFpConfig", &self.info(DeviceInfo::DoubleFpConfig))
.field("HalfFpConfig", &self.info(DeviceInfo::HalfFpConfig))
.field(
"PreferredVectorWidthHalf",
&self.info(DeviceInfo::PreferredVectorWidthHalf),
)
.field(
"HostUnifiedMemory",
&self.info(DeviceInfo::HostUnifiedMemory),
)
.field(
"NativeVectorWidthChar",
&self.info(DeviceInfo::NativeVectorWidthChar),
)
.field(
"NativeVectorWidthShort",
&self.info(DeviceInfo::NativeVectorWidthShort),
)
.field(
"NativeVectorWidthInt",
&self.info(DeviceInfo::NativeVectorWidthInt),
)
.field(
"NativeVectorWidthLong",
&self.info(DeviceInfo::NativeVectorWidthLong),
)
.field(
"NativeVectorWidthFloat",
&self.info(DeviceInfo::NativeVectorWidthFloat),
)
.field(
"NativeVectorWidthDouble",
&self.info(DeviceInfo::NativeVectorWidthDouble),
)
.field(
"NativeVectorWidthHalf",
&self.info(DeviceInfo::NativeVectorWidthHalf),
)
.field("OpenclCVersion", &self.info(DeviceInfo::OpenclCVersion))
.field("LinkerAvailable", &self.info(DeviceInfo::LinkerAvailable))
.field("BuiltInKernels", &self.info(DeviceInfo::BuiltInKernels))
.field(
"ImageMaxBufferSize",
&self.info(DeviceInfo::ImageMaxBufferSize),
)
.field(
"ImageMaxArraySize",
&self.info(DeviceInfo::ImageMaxArraySize),
)
.field("ParentDevice", &self.info(DeviceInfo::ParentDevice))
.field(
"PartitionMaxSubDevices",
&self.info(DeviceInfo::PartitionMaxSubDevices),
)
.field(
"PartitionProperties",
&self.info(DeviceInfo::PartitionProperties),
)
.field(
"PartitionAffinityDomain",
&self.info(DeviceInfo::PartitionAffinityDomain),
)
.field("PartitionType", &self.info(DeviceInfo::PartitionType))
.field("ReferenceCount", &self.info(DeviceInfo::ReferenceCount))
.field(
"PreferredInteropUserSync",
&self.info(DeviceInfo::PreferredInteropUserSync),
)
.field("PrintfBufferSize", &self.info(DeviceInfo::PrintfBufferSize))
.field(
"ImagePitchAlignment",
&self.info(DeviceInfo::ImagePitchAlignment),
)
.field(
"ImageBaseAddressAlignment",
&self.info(DeviceInfo::ImageBaseAddressAlignment),
)
.finish()
}
}
unsafe impl ClDeviceIdPtr for Device {
fn as_ptr(&self) -> cl_device_id {
self.0.as_raw()
}
}
unsafe impl<'a> ClDeviceIdPtr for &'a Device {
fn as_ptr(&self) -> cl_device_id {
self.0.as_raw()
}
}
impl From<DeviceIdCore> for Device {
fn from(core: DeviceIdCore) -> Device {
Device(core)
}
}
impl From<Device> for String {
fn from(d: Device) -> String {
format!("{}", d)
}
}
impl From<Device> for DeviceIdCore {
fn from(d: Device) -> DeviceIdCore {
d.0
}
}
impl std::fmt::Display for Device {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.fmt_info(f)
}
}
impl AsRef<Device> for Device {
fn as_ref(&self) -> &Device {
self
}
}
impl Deref for Device {
type Target = DeviceIdCore;
fn deref(&self) -> &DeviceIdCore {
&self.0
}
}
impl DerefMut for Device {
fn deref_mut(&mut self) -> &mut DeviceIdCore {
&mut self.0
}
}