use std::sync::{Arc, Mutex, PoisonError};
use cranpose_render_wgpu::WgpuRenderer;
fn shared_instance(backends: wgpu::Backends) -> wgpu::Instance {
static INSTANCES: Mutex<Vec<(wgpu::Backends, wgpu::Instance)>> = Mutex::new(Vec::new());
let mut instances = INSTANCES.lock().unwrap_or_else(PoisonError::into_inner);
if let Some((_, instance)) = instances.iter().find(|(held, _)| *held == backends) {
return instance.clone();
}
let mut descriptor = wgpu::InstanceDescriptor::new_without_display_handle();
descriptor.backends = backends;
let instance = wgpu::Instance::new(descriptor);
instances.push((backends, instance.clone()));
instance
}
pub fn headless_adapter(backends: wgpu::Backends) -> Result<wgpu::Adapter, String> {
let instance = shared_instance(backends);
pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::LowPower,
..wgpu::RequestAdapterOptions::default()
}))
.map_err(|err| format!("adapter request failed: {err:?}"))
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Pipelines {
Background,
Inline,
}
pub struct HeadlessDevice {
device: Arc<wgpu::Device>,
queue: Arc<wgpu::Queue>,
backend: wgpu::Backend,
downlevel: wgpu::DownlevelFlags,
}
impl HeadlessDevice {
pub fn request(
backends: wgpu::Backends,
limits: wgpu::Limits,
label: &str,
) -> Result<Self, String> {
Self::request_without_features(backends, limits, label, wgpu::Features::empty())
}
pub fn request_without_features(
backends: wgpu::Backends,
limits: wgpu::Limits,
label: &str,
features: wgpu::Features,
) -> Result<Self, String> {
let adapter = headless_adapter(backends)?;
let (device, queue) = pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor {
label: Some(label),
required_features: cranpose_render_wgpu::optional_device_features(&adapter) - features,
required_limits: limits,
experimental_features: wgpu::ExperimentalFeatures::disabled(),
memory_hints: wgpu::MemoryHints::default(),
trace: wgpu::Trace::Off,
}))
.map_err(|err| format!("device request failed: {err:?}"))?;
Ok(Self {
device: Arc::new(device),
queue: Arc::new(queue),
backend: adapter.get_info().backend,
downlevel: adapter.get_downlevel_capabilities().flags,
})
}
pub fn without(mut self, flags: wgpu::DownlevelFlags) -> Self {
self.downlevel.remove(flags);
self
}
pub fn attach(
self,
renderer: &mut WgpuRenderer,
format: wgpu::TextureFormat,
pipelines: Pipelines,
) {
match pipelines {
Pipelines::Background => renderer.init_gpu(
self.device,
self.queue,
format,
self.backend,
self.downlevel,
),
Pipelines::Inline => renderer.init_gpu_compiling_inline_for_tests(
self.device,
self.queue,
format,
self.backend,
self.downlevel,
),
}
}
}