use std::ffi::c_void;
use crate::{BackendKind, DeviceId};
#[non_exhaustive]
pub enum GpuContext {
Cpu,
OpenCl {
context: *mut c_void,
queue: *mut c_void,
device: *mut c_void,
gl_sharing: Option<OpenClGlSharing>,
},
Cuda {
context: *mut c_void,
stream: *mut c_void,
device_id: i32,
uuid: Option<[u8; 16]>,
},
Metal {
device: *mut c_void,
queue: *mut c_void,
registry_id: Option<u64>,
},
D3D11 {
device: *mut c_void,
device_context: *mut c_void,
luid: Option<(i32, u32)>,
},
D3D12 {
device: *mut c_void,
queue: *mut c_void,
luid: Option<(i32, u32)>,
},
Vulkan {
instance: *mut c_void,
physical_device: *mut c_void,
device: *mut c_void,
queue: u64,
queue_family_index: u32,
uuid: Option<[u8; 16]>,
},
OpenGL {
display: *mut c_void,
context: *mut c_void,
share_group: Option<u64>,
backend: GlBackend,
},
#[cfg(feature = "wgpu")]
Wgpu {
device: std::sync::Arc<wgpu::Device>,
queue: std::sync::Arc<wgpu::Queue>,
adapter: std::sync::Arc<wgpu::Adapter>,
},
#[cfg(all(target_family = "wasm", feature = "wgpu", feature = "web"))]
WebGpu {
device: std::sync::Arc<wgpu::Device>,
queue: std::sync::Arc<wgpu::Queue>,
raw_device: Option<web_sys::GpuDevice>,
},
#[cfg(all(target_family = "wasm", feature = "wgpu", feature = "webgl"))]
WebGl {
device: std::sync::Arc<wgpu::Device>,
queue: std::sync::Arc<wgpu::Queue>,
raw_context: Option<web_sys::WebGl2RenderingContext>,
},
}
#[derive(Copy, Clone)]
pub struct OpenClGlSharing {
pub display: *mut c_void,
pub context: *mut c_void,
pub backend: GlBackend,
}
impl core::fmt::Debug for OpenClGlSharing {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("OpenClGlSharing").field("backend", &self.backend).finish_non_exhaustive()
}
}
unsafe impl Send for OpenClGlSharing {}
unsafe impl Sync for OpenClGlSharing {}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum GlBackend {
Desktop,
Egl,
Angle(AngleBackend),
Web,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum AngleBackend {
Vulkan,
D3D11,
D3D12,
Metal,
}
pub trait GlContextExecutor: Send + Sync + 'static {
fn execute(&self, target_ctx: usize, task: Box<dyn FnOnce() + Send + 'static>);
}
#[cfg(not(target_family = "wasm"))]
unsafe impl Send for GpuContext {}
#[cfg(not(target_family = "wasm"))]
unsafe impl Sync for GpuContext {}
impl GpuContext {
#[cfg(feature = "wgpu")]
pub fn from_wgpu(
device: std::sync::Arc<wgpu::Device>,
queue: std::sync::Arc<wgpu::Queue>,
adapter: std::sync::Arc<wgpu::Adapter>,
) -> Self {
Self::Wgpu { device, queue, adapter }
}
pub fn backend(&self) -> BackendKind {
match self {
Self::Cpu => BackendKind::Cpu,
#[cfg(feature = "wgpu")]
Self::Wgpu { .. } => BackendKind::Wgpu,
#[cfg(all(target_family = "wasm", feature = "wgpu", feature = "web"))]
Self::WebGpu { .. } => BackendKind::Wgpu,
#[cfg(all(target_family = "wasm", feature = "wgpu", feature = "webgl"))]
Self::WebGl { .. } => BackendKind::Wgpu,
#[cfg(feature = "opencl")]
Self::OpenCl { .. } => BackendKind::OpenCl,
#[cfg(not(feature = "opencl"))]
Self::OpenCl { .. } => BackendKind::Cpu,
#[cfg(feature = "cuda")]
Self::Cuda { .. } => BackendKind::Cuda,
#[cfg(not(feature = "cuda"))]
Self::Cuda { .. } => BackendKind::Cpu,
#[cfg(feature = "wgpu")]
Self::Metal { .. }
| Self::D3D11 { .. }
| Self::D3D12 { .. }
| Self::Vulkan { .. }
| Self::OpenGL { .. } => BackendKind::Wgpu,
#[cfg(not(feature = "wgpu"))]
Self::Metal { .. }
| Self::D3D11 { .. }
| Self::D3D12 { .. }
| Self::Vulkan { .. }
| Self::OpenGL { .. } => BackendKind::Cpu,
}
}
pub fn cuda_context_and_stream(&self) -> Result<(*mut c_void, *mut c_void), GpuContextError> {
match self {
Self::Cuda { context, stream, .. } => Ok((*context, *stream)),
other => {
Err(GpuContextError::LaneMismatch { actual: other.backend(), requested: "cuda_context_and_stream" })
}
}
}
pub fn opencl_context_and_queue(&self) -> Result<(*mut c_void, *mut c_void), GpuContextError> {
match self {
Self::OpenCl { context, queue, .. } => Ok((*context, *queue)),
other => {
Err(GpuContextError::LaneMismatch { actual: other.backend(), requested: "opencl_context_and_queue" })
}
}
}
pub fn metal_command_queue(&self) -> Result<*mut c_void, GpuContextError> {
match self {
Self::Metal { queue, .. } => Ok(*queue),
other => Err(GpuContextError::LaneMismatch { actual: other.backend(), requested: "metal_command_queue" }),
}
}
pub fn metal_device(&self) -> Result<*mut c_void, GpuContextError> {
match self {
Self::Metal { device, .. } => Ok(*device),
other => Err(GpuContextError::LaneMismatch { actual: other.backend(), requested: "metal_device" }),
}
}
pub fn cuda_uuid(&self) -> Result<[u8; 16], GpuContextError> {
match self {
Self::Cuda { uuid: Some(u), .. } => Ok(*u),
Self::Cuda { uuid: None, .. } => Err(GpuContextError::MissingField("Cuda::uuid")),
other => Err(GpuContextError::LaneMismatch { actual: other.backend(), requested: "cuda_uuid" }),
}
}
pub fn cuda_device_id(&self) -> Result<i32, GpuContextError> {
match self {
Self::Cuda { device_id, .. } => Ok(*device_id),
other => Err(GpuContextError::LaneMismatch { actual: other.backend(), requested: "cuda_device_id" }),
}
}
pub fn metal_registry_id(&self) -> Result<u64, GpuContextError> {
match self {
Self::Metal { registry_id: Some(id), .. } => Ok(*id),
Self::Metal { registry_id: None, .. } => Err(GpuContextError::MissingField("Metal::registry_id")),
other => Err(GpuContextError::LaneMismatch { actual: other.backend(), requested: "metal_registry_id" }),
}
}
pub fn opencl_device(&self) -> Result<*mut c_void, GpuContextError> {
match self {
Self::OpenCl { device, .. } => Ok(*device),
other => Err(GpuContextError::LaneMismatch { actual: other.backend(), requested: "opencl_device" }),
}
}
pub fn opencl_gl_sharing(&self) -> Result<Option<OpenClGlSharing>, GpuContextError> {
match self {
Self::OpenCl { gl_sharing, .. } => Ok(*gl_sharing),
other => Err(GpuContextError::LaneMismatch { actual: other.backend(), requested: "opencl_gl_sharing" }),
}
}
pub fn device_id(&self) -> Option<DeviceId> {
match self {
Self::Cpu => None,
Self::Cuda { uuid: Some(u), .. } => Some(DeviceId::CudaUuid(*u)),
Self::Cuda { .. } => None,
Self::D3D11 { luid: Some((h, l)), .. } => Some(DeviceId::DxgiLuid { high: *h, low: *l }),
Self::D3D12 { luid: Some((h, l)), .. } => Some(DeviceId::DxgiLuid { high: *h, low: *l }),
Self::D3D11 { .. } | Self::D3D12 { .. } => None,
Self::Metal { registry_id: Some(id), .. } => Some(DeviceId::MetalRegistryId(*id)),
Self::Metal { .. } => None,
Self::Vulkan { uuid: Some(u), .. } => Some(DeviceId::VulkanUuid(*u)),
Self::Vulkan { .. } => None,
Self::OpenCl { .. } => None, Self::OpenGL { .. } => None,
#[cfg(feature = "wgpu")]
Self::Wgpu { .. } => None,
#[cfg(all(target_family = "wasm", feature = "wgpu", feature = "web"))]
Self::WebGpu { .. } => None,
#[cfg(all(target_family = "wasm", feature = "wgpu", feature = "webgl"))]
Self::WebGl { .. } => None,
}
}
}
#[derive(thiserror::Error, Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum GpuContextError {
#[error("GpuContext lane {actual:?} cannot satisfy a {requested} accessor")]
LaneMismatch { actual: BackendKind, requested: &'static str },
#[error("optional field {0} not populated on this GpuContext")]
MissingField(&'static str),
}
impl core::fmt::Debug for GpuContext {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Cpu => f.write_str("GpuContext::Cpu"),
Self::OpenCl { gl_sharing, .. } => {
f.debug_struct("OpenCl").field("gl_sharing", gl_sharing).finish_non_exhaustive()
}
Self::Cuda { device_id, uuid, .. } => {
f.debug_struct("Cuda").field("device_id", device_id).field("uuid", uuid).finish()
}
Self::Metal { registry_id, .. } => f.debug_struct("Metal").field("registry_id", registry_id).finish(),
Self::D3D11 { luid, .. } => f.debug_struct("D3D11").field("luid", luid).finish(),
Self::D3D12 { luid, .. } => f.debug_struct("D3D12").field("luid", luid).finish(),
Self::Vulkan { queue_family_index, uuid, .. } => {
f.debug_struct("Vulkan").field("queue_family_index", queue_family_index).field("uuid", uuid).finish()
}
Self::OpenGL { backend, share_group, .. } => {
f.debug_struct("OpenGL").field("backend", backend).field("share_group", share_group).finish()
}
#[cfg(feature = "wgpu")]
Self::Wgpu { .. } => f.write_str("GpuContext::Wgpu"),
#[cfg(all(target_family = "wasm", feature = "wgpu", feature = "web"))]
Self::WebGpu { raw_device, .. } => {
f.debug_struct("WebGpu").field("raw_device", &raw_device.is_some()).finish_non_exhaustive()
}
#[cfg(all(target_family = "wasm", feature = "wgpu", feature = "webgl"))]
Self::WebGl { raw_context, .. } => {
f.debug_struct("WebGl").field("raw_context", &raw_context.is_some()).finish_non_exhaustive()
}
}
}
}