use crate::Error;
use wgpu::{AdapterInfo, Backends, Device, Instance, MemoryHints, Queue, RequestAdapterOptions};
pub struct Context {
pub adapter_info: AdapterInfo,
pub device: Device,
pub queue: Queue,
}
impl Context {
pub async fn init() -> Result<Self, Error> {
let descriptor = wgpu::InstanceDescriptor {
backends: Backends::PRIMARY,
..wgpu::InstanceDescriptor::new_without_display_handle()
}
.with_env();
let instance = Instance::new(descriptor);
let adapter = instance
.request_adapter(&RequestAdapterOptions {
power_preference: wgpu::PowerPreference::HighPerformance,
compatible_surface: None,
force_fallback_adapter: false,
apply_limit_buckets: false,
})
.await?;
let adapter_info = adapter.get_info();
let required_features = context_features(&adapter_info, adapter.features());
let (device, queue) = adapter
.request_device(&wgpu::DeviceDescriptor {
label: Some("Context Device"),
required_features,
required_limits: adapter.limits(),
memory_hints: MemoryHints::Performance,
..Default::default()
})
.await?;
Ok(Self {
adapter_info,
device,
queue,
})
}
}
fn context_features(
adapter_info: &wgpu::AdapterInfo,
supported_features: wgpu::Features,
) -> wgpu::Features {
let mut optional_features = wgpu::Features::empty();
if reliable_subgroup_scan(adapter_info) {
optional_features |= wgpu::Features::SUBGROUP;
}
let apple_metal = adapter_info.backend == wgpu::Backend::Metal
&& (adapter_info.vendor == 0x106b || adapter_info.name.starts_with("Apple "));
if !apple_metal && reliable_optional_compute_features(adapter_info) {
optional_features |= wgpu::Features::TIMESTAMP_QUERY;
}
supported_features & optional_features
}
pub(crate) fn reliable_subgroup_scan(adapter_info: &wgpu::AdapterInfo) -> bool {
reliable_optional_compute_features(adapter_info)
}
fn reliable_optional_compute_features(adapter_info: &wgpu::AdapterInfo) -> bool {
!(adapter_info.backend == wgpu::Backend::Vulkan
&& adapter_info.vendor == 0x10de
&& adapter_info.device_type == wgpu::DeviceType::IntegratedGpu)
}
#[cfg(test)]
mod tests {
use super::{context_features, reliable_subgroup_scan};
fn adapter(name: &str, vendor: u32, backend: wgpu::Backend) -> wgpu::AdapterInfo {
let mut adapter = wgpu::AdapterInfo::new(wgpu::DeviceType::Other, backend);
adapter.name = name.to_owned();
adapter.vendor = vendor;
adapter
}
#[test]
fn disables_unreliable_apple_metal_timestamps_only() {
let supported = wgpu::Features::TIMESTAMP_QUERY | wgpu::Features::SUBGROUP;
let apple_vendor = context_features(
&adapter("Metal device", 0x106b, wgpu::Backend::Metal),
supported,
);
assert!(!apple_vendor.contains(wgpu::Features::TIMESTAMP_QUERY));
assert!(apple_vendor.contains(wgpu::Features::SUBGROUP));
let apple_name =
context_features(&adapter("Apple M3 Pro", 0, wgpu::Backend::Metal), supported);
assert!(!apple_name.contains(wgpu::Features::TIMESTAMP_QUERY));
let intel_metal = context_features(
&adapter("Intel GPU", 0x8086, wgpu::Backend::Metal),
supported,
);
assert!(intel_metal.contains(wgpu::Features::TIMESTAMP_QUERY));
let apple_vulkan = context_features(
&adapter("Apple GPU", 0x106b, wgpu::Backend::Vulkan),
supported,
);
assert!(apple_vulkan.contains(wgpu::Features::TIMESTAMP_QUERY));
}
#[test]
fn disables_subgroups_on_integrated_nvidia_vulkan() {
let mut orin = adapter("Orin (nvgpu)", 0x10de, wgpu::Backend::Vulkan);
orin.device_type = wgpu::DeviceType::IntegratedGpu;
let enabled = context_features(
&orin,
wgpu::Features::TIMESTAMP_QUERY | wgpu::Features::SUBGROUP,
);
assert!(!reliable_subgroup_scan(&orin));
assert!(!enabled.contains(wgpu::Features::SUBGROUP));
assert!(!enabled.contains(wgpu::Features::TIMESTAMP_QUERY));
}
}