use std::any::Any;
use std::rc::Rc;
#[cfg(feature = "wgpu")]
use std::sync::Arc;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Unavailable {
NoDevice,
NoAdapter,
Unsupported,
}
pub struct Adapter<'a> {
#[allow(
dead_code,
reason = "held for the platform modules; without the `wgpu` feature none reads it"
)]
device: &'a Rc<dyn Any>,
}
impl<'a> Adapter<'a> {
pub(crate) fn new(device: &'a Rc<dyn Any>) -> Self {
Self { device }
}
#[cfg(feature = "wgpu")]
pub fn wgpu(&self) -> Option<(Arc<wgpu::Device>, Arc<wgpu::Queue>)> {
if let Some((device, queue)) = self
.device
.downcast_ref::<(Arc<wgpu::Device>, Arc<wgpu::Queue>)>()
{
return Some((Arc::clone(device), Arc::clone(queue)));
}
#[cfg(target_os = "windows")]
{
matched::wgpu_device(self.device)
}
#[cfg(not(target_os = "windows"))]
{
None
}
}
#[cfg(all(target_os = "linux", feature = "wgpu"))]
pub fn gpu_device(&self) -> Option<crate::GpuDevice> {
use std::sync::Arc;
let (device, _queue) = self
.device
.downcast_ref::<(Arc<wgpu::Device>, Arc<wgpu::Queue>)>()?;
let hal = unsafe { device.as_hal::<wgpu::hal::vulkan::Api>() }?;
crate::GpuDevice::on(
hal.shared_instance().raw_instance(),
hal.raw_physical_device(),
)
.ok()
}
#[cfg(all(target_os = "linux", feature = "wgpu"))]
pub fn gpu_id(&self) -> Option<crate::GpuId> {
use std::sync::Arc;
let (device, _queue) = self
.device
.downcast_ref::<(Arc<wgpu::Device>, Arc<wgpu::Queue>)>()?;
let hal = unsafe { device.as_hal::<wgpu::hal::vulkan::Api>() }?;
let id = crate::GpuId::of(
hal.shared_instance().raw_instance(),
hal.raw_physical_device(),
);
crate::remember(id.clone());
Some(id)
}
}
#[cfg(all(target_os = "windows", feature = "wgpu"))]
mod matched {
use std::any::Any;
use std::future::Future;
use std::pin::pin;
use std::rc::Rc;
use std::sync::Arc;
use std::task::{Context, Poll, Wake, Waker};
use windows::Win32::Foundation::LUID;
use windows::Win32::Graphics::Direct3D11::ID3D11Device;
use windows::Win32::Graphics::Dxgi::{IDXGIAdapter, IDXGIDevice};
use windows::core::Interface;
pub(super) fn wgpu_device(
device: &Rc<dyn Any>,
) -> Option<(Arc<wgpu::Device>, Arc<wgpu::Queue>)> {
let window_luid = device_adapter_luid(device.downcast_ref::<ID3D11Device>()?)?;
let instance = wgpu::Instance::new(dx12_instance());
let adapters = block_on(instance.enumerate_adapters(wgpu::Backends::DX12));
let adapter = adapters.iter().find(|adapter| {
wgpu_adapter_luid(adapter).is_some_and(|l| same_luid(l, window_luid))
})?;
let (device, queue) = block_on(adapter.request_device(&device_descriptor(adapter))).ok()?;
Some((Arc::new(device), Arc::new(queue)))
}
fn device_adapter_luid(device: &ID3D11Device) -> Option<LUID> {
unsafe {
let dxgi_device: IDXGIDevice = device.cast().ok()?;
let adapter: IDXGIAdapter = dxgi_device.GetAdapter().ok()?;
Some(adapter.GetDesc().ok()?.AdapterLuid)
}
}
fn wgpu_adapter_luid(adapter: &wgpu::Adapter) -> Option<LUID> {
unsafe {
let hal = adapter.as_hal::<wgpu::hal::dx12::Api>()?;
Some(hal.raw_adapter().GetDesc().ok()?.AdapterLuid)
}
}
fn same_luid(a: LUID, b: LUID) -> bool {
a.LowPart == b.LowPart && a.HighPart == b.HighPart
}
fn dx12_instance() -> wgpu::InstanceDescriptor {
let mut descriptor = wgpu::InstanceDescriptor::new_without_display_handle();
descriptor.backends = wgpu::Backends::DX12;
descriptor
}
fn device_descriptor(adapter: &wgpu::Adapter) -> wgpu::DeviceDescriptor<'static> {
wgpu::DeviceDescriptor {
label: Some("gpui_interop_device"),
required_features: wgpu::Features::empty(),
required_limits: wgpu::Limits::downlevel_defaults()
.using_resolution(adapter.limits())
.using_alignment(adapter.limits()),
memory_hints: wgpu::MemoryHints::MemoryUsage,
trace: wgpu::Trace::Off,
experimental_features: wgpu::ExperimentalFeatures::disabled(),
}
}
fn block_on<F: Future>(future: F) -> F::Output {
struct NoopWake;
impl Wake for NoopWake {
fn wake(self: Arc<Self>) {}
}
let waker = Waker::from(Arc::new(NoopWake));
let mut context = Context::from_waker(&waker);
let mut future = pin!(future);
loop {
match future.as_mut().poll(&mut context) {
Poll::Ready(value) => return value,
Poll::Pending => std::thread::yield_now(),
}
}
}
}