1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
//! An `OpenCL` context.
use crate::core::error::Result as OclCoreResult;
use crate::core::{
self, ClContextPtr, ClVersions, Context as ContextCore, ContextInfo, ContextInfoResult,
ContextProperties, ContextPropertyValue, CreateContextCallbackFn, DeviceInfo, DeviceInfoResult,
OpenclVersion, PlatformInfo, PlatformInfoResult, UserDataPtr,
};
use crate::error::{Error as OclError, Result as OclResult};
use crate::ffi::cl_context;
use crate::standard::{Device, DeviceSpecifier, Platform};
use std;
use std::ops::{Deref, DerefMut};
/// A context for a particular platform and set of device types.
///
/// Thread safety and destruction for any enclosed pointers are all handled automatically.
/// Clone, store, and share between threads to your heart's content.
///
//
// * TODO: Remove contained copies of the device id list and platform id.
// Can be easily ascertained via the API. [UPDATE]: devices list removed.
// Need to parse the `ContextProperties` out of the
// `ContextInfoResult::Properties` before we can eliminate `platform`.
//
#[derive(Debug, Clone)]
pub struct Context(ContextCore);
impl Context {
/// Returns a [`ContextBuilder`](/ocl/ocl/struct.ContextBuilder.html).
///
/// This is the preferred way to create a Context.
pub fn builder() -> ContextBuilder {
ContextBuilder::new()
}
/// Returns a newly created context.
///
/// Prefer `Context::builder()...` instead of this method unless you know
/// what you're doing. Please also immediately contact us if you do, in
/// fact, know what you're doing so that you can be added to the
/// development team as the one who does.
///
/// ## Defaults
///
/// * The 'NULL' platform (which is not to be relied on but is generally
/// the first avaliable).
/// * All devices associated with the 'NULL' platform
/// * No notify callback function or user data.
///
/// Don't rely on these defaults, instead rely on the `ContextBuilder`
/// defaults. In other words, use: `Context::builder().build().unwrap()`
/// rather than `Context::new(None, None, None, None).unwrap()`.
///
/// ## Panics
///
/// [TEMPORARY] Passing a `Some` variant for `pfn_notify` or `user_data` is
/// not yet supported. File an issue if you need this.
///
pub fn new(
properties: Option<ContextProperties>,
device_spec: Option<DeviceSpecifier>,
pfn_notify: Option<CreateContextCallbackFn>,
user_data: Option<UserDataPtr>,
) -> OclResult<Context> {
assert!(
pfn_notify.is_none() && user_data.is_none(),
"Context creation callbacks not yet implemented - file issue if you need this."
);
let platform: Option<Platform> = match properties {
Some(ref props) => props.get_platform().map(Platform::new),
None => None,
};
let device_spec = match device_spec {
Some(ds) => ds,
None => DeviceSpecifier::All,
};
let device_list = device_spec.to_device_list(platform)?;
let obj_core =
core::create_context(properties.as_ref(), &device_list, pfn_notify, user_data)?;
Ok(Context(obj_core))
}
/// Resolves a list of zero-based device indices into a list of Devices.
///
/// If any index is out of bounds it will wrap around zero (%) to the next
/// valid device index.
///
pub fn resolve_wrapping_device_idxs(&self, idxs: &[usize]) -> Vec<Device> {
Device::resolve_idxs_wrap(idxs, &self.devices())
}
/// Returns a device by its ordinal count within this context.
///
/// Round-robins (%) to the next valid device.
///
pub fn get_device_by_wrapping_index(&self, index: usize) -> Device {
self.resolve_wrapping_device_idxs(&[index; 1])[0]
}
/// Returns info about the platform associated with the context.
pub fn platform_info(&self, info_kind: PlatformInfo) -> OclResult<PlatformInfoResult> {
match self.platform() {
Ok(plat_opt) => match plat_opt {
Some(ref p) => core::get_platform_info(p, info_kind).map_err(OclError::from),
None => Err(OclError::from(
"Context::platform_info: \
This context has no associated platform.",
)),
},
Err(err) => Err(err),
}
}
/// Returns info about the device indexed by `index` associated with this
/// context.
pub fn device_info(&self, index: usize, info_kind: DeviceInfo) -> OclResult<DeviceInfoResult> {
match self.devices().get(index) {
Some(d) => core::get_device_info(d, info_kind).map_err(OclError::from),
None => Err(OclError::from("Context::device_info: Invalid device index")),
}
}
/// Returns info about the context.
pub fn info(&self, info_kind: ContextInfo) -> OclResult<ContextInfoResult> {
core::get_context_info(&self.0, info_kind).map_err(OclError::from)
}
/// Returns a reference to the core pointer wrapper, usable by functions in
/// the `core` module.
#[inline]
pub fn as_core(&self) -> &ContextCore {
&self.0
}
/// Returns the list of devices associated with this context.
///
/// Panics upon any OpenCL error.
pub fn devices(&self) -> Vec<Device> {
Device::list_from_core(self.0.devices().unwrap())
}
/// Returns the list of device versions associated with this context.
pub fn device_versions(&self) -> OclResult<Vec<OpenclVersion>> {
Device::list_from_core(self.0.devices().map_err(OclError::from)?)
.into_iter()
.map(|d| d.version().map_err(OclError::from))
.collect()
}
/// Returns the platform this context is associated with.
pub fn platform(&self) -> OclResult<Option<Platform>> {
self.0
.platform()
.map(|opt| opt.map(Platform::from))
.map_err(OclError::from)
}
fn fmt_info(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.debug_struct("Context")
.field("ReferenceCount", &self.info(ContextInfo::ReferenceCount))
.field("Devices", &self.info(ContextInfo::Devices))
.field("Properties", &self.info(ContextInfo::Properties))
.field("NumDevices", &self.info(ContextInfo::NumDevices))
.finish()
}
}
impl From<ContextCore> for Context {
fn from(c: ContextCore) -> Context {
Context(c)
}
}
impl std::fmt::Display for Context {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.fmt_info(f)
}
}
impl Deref for Context {
type Target = ContextCore;
fn deref(&self) -> &ContextCore {
&self.0
}
}
impl DerefMut for Context {
fn deref_mut(&mut self) -> &mut ContextCore {
&mut self.0
}
}
unsafe impl<'a> ClContextPtr for &'a Context {
fn as_ptr(&self) -> cl_context {
self.0.as_ptr()
}
}
impl ClVersions for Context {
fn device_versions(&self) -> OclCoreResult<Vec<OpenclVersion>> {
self.0.device_versions()
}
fn platform_version(&self) -> OclCoreResult<OpenclVersion> {
self.0.platform_version()
}
}
impl<'a> ClVersions for &'a Context {
fn device_versions(&self) -> OclCoreResult<Vec<OpenclVersion>> {
self.0.device_versions()
}
fn platform_version(&self) -> OclCoreResult<OpenclVersion> {
self.0.platform_version()
}
}
/// A builder for `Context`.
///
// * TODO:
// - Handle context creation callbacks.
//
#[must_use = "builders do nothing unless '::build' is called"]
pub struct ContextBuilder {
properties: ContextProperties,
device_spec: Option<DeviceSpecifier>,
}
impl ContextBuilder {
/// Creates a new `ContextBuilder`
///
/// Use `Context::builder().build().unwrap()` for defaults.
///
/// ## Defaults
///
/// * The first avaliable platform
/// * All devices associated with the first available platform
/// * No notify callback function or user data.
///
pub fn new() -> ContextBuilder {
// Default platform will be set within `::build` if unspecified by that time.
let properties = ContextProperties::new();
ContextBuilder {
properties,
device_spec: None,
}
}
/// Specifies all context properties directly.
///
/// Overwrites all previously specified properties.
///
pub fn properties<'a>(&'a mut self, properties: ContextProperties) -> &'a mut ContextBuilder {
self.properties = properties;
self
}
/// Specifies a context property.
///
/// Overwrites any property with the same variant (i.e.: if
/// `ContextPropertyValue::Platform` was already set, it would be
/// overwritten if `prop_val` is also `ContextPropertyValue::Platform`).
///
pub fn property<'a>(&'a mut self, prop_val: ContextPropertyValue) -> &'a mut ContextBuilder {
self.properties.set_property_value(prop_val);
self
}
/// Specifies a platform.
///
/// Overwrites any previously specified platform.
///
pub fn platform(&mut self, platform: Platform) -> &mut ContextBuilder {
self.properties.set_platform(platform);
self
}
/// Specifies an OpenGL context to associate with.
///
/// Overwrites any previously specified OpenGL context.
///
pub fn gl_context(&mut self, gl_handle: *mut crate::ffi::c_void) -> &mut ContextBuilder {
self.properties.set_gl_context(gl_handle);
self
}
/// Specifies a Display pointer for the GLX context.
///
/// Overwrites any previously specified GLX context.
///
pub fn glx_display(&mut self, glx_display: *mut crate::ffi::c_void) -> &mut ContextBuilder {
self.properties.set_glx_display(glx_display);
self
}
/// Specifies a list of devices with which to associate the context.
///
/// Devices may be specified in any number of ways including simply
/// passing a device or slice of devices. See the [`impl From`] section of
/// [`DeviceSpecifier`][device_specifier] for more information.
///
///
/// ## Panics
///
/// Devices must not have already been specified.
///
/// [device_specifier_from]: enum.DeviceSpecifier.html#method.from
/// [device_specifier]: enum.DeviceSpecifier.html
///
pub fn devices<D: Into<DeviceSpecifier>>(&mut self, device_spec: D) -> &mut ContextBuilder {
assert!(
self.device_spec.is_none(),
"ocl::ContextBuilder::devices: Devices already specified"
);
self.device_spec = Some(device_spec.into());
self
}
/// Returns a new `Context` with the parameters hitherinforthto specified (say what?).
///
/// Returns a newly created context with the specified platform and set of device types.
///
// * TODO:
// - Handle context creation callbacks.
//
pub fn build(&self) -> OclResult<Context> {
let mut props = self.properties.clone();
if props.get_platform().is_none() {
props.set_platform(Platform::default());
}
Context::new(Some(props), self.device_spec.clone(), None, None)
}
}