use futures::executor::block_on;
use raw_window_handle::{HasRawWindowHandle, RawWindowHandle};
use crate::{
graphics::{Gpu, GpuCtx, GpuError},
Profiler,
};
#[derive(Clone)]
pub struct GpuBuilder<'a> {
backends: wgpu::Backends,
power_preference: wgpu::PowerPreference,
limits: wgpu::Limits,
features: wgpu::Features,
optional_features: wgpu::Features,
trace_path: Option<&'a std::path::Path>,
label: Option<&'a str>,
}
impl Default for GpuBuilder<'_> {
fn default() -> Self {
Self::new()
}
}
impl GpuBuilder<'_> {
pub fn new() -> Self {
Self {
backends: wgpu::Backends::PRIMARY,
power_preference: wgpu::PowerPreference::HighPerformance,
limits: wgpu::Limits::default(),
label: None,
features: wgpu::Features::default(),
optional_features: wgpu::Features::empty(),
trace_path: None,
}
}
}
impl<'a> GpuBuilder<'a> {
pub fn with_backends(mut self, backends: wgpu::Backends) -> Self {
self.backends = backends;
self
}
pub fn with_power_preference(mut self, power_preference: wgpu::PowerPreference) -> Self {
self.power_preference = power_preference;
self
}
pub fn with_limits(mut self, limits: wgpu::Limits) -> Self {
self.limits = limits;
self
}
pub fn with_label(mut self, label: &'a str) -> Self {
self.label = Some(label);
self
}
pub fn with_features(mut self, features: wgpu::Features) -> Self {
self.features = features;
self
}
pub fn with_optional_features(mut self, features: wgpu::Features) -> Self {
self.optional_features = features;
self
}
pub fn with_profiler(mut self) -> Self {
self.features |= wgpu::Features::TIMESTAMP_QUERY;
self.features |= wgpu::Features::PIPELINE_STATISTICS_QUERY;
self
}
pub fn with_trace_path(mut self, path: &'a std::path::Path) -> Self {
self.trace_path = Some(path);
self
}
pub fn build<W>(self, window: &W) -> Result<Gpu, GpuError>
where
W: HasRawWindowHandle,
{
block_on(self.build_impl(Some(window)))
}
pub fn build_headless(self) -> Result<Gpu, GpuError> {
block_on(self.build_impl::<NoWindow>(None))
}
pub async fn build_impl<W>(self, window: Option<&W>) -> Result<Gpu, GpuError>
where
W: HasRawWindowHandle,
{
let instance = wgpu::Instance::new(self.backends);
let compatible_surface = window.map(|w| unsafe { instance.create_surface(w) });
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: self.power_preference,
compatible_surface: compatible_surface.as_ref(),
force_fallback_adapter: false,
})
.await
.ok_or(GpuError::AdapterNone)?;
let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor {
limits: self.limits.clone(),
label: self.label,
features: self.features(&adapter),
},
self.trace_path,
)
.await
.map_err(GpuError::RequestDeviceError)?;
let preferred_format = compatible_surface.and_then(|s| s.get_preferred_format(&adapter));
let profiler = Profiler::new(&device, &queue);
let gpu = GpuCtx {
instance,
adapter,
device,
queue,
profiler,
preferred_format,
};
Ok(gpu.into_handle())
}
fn features(&self, adapter: &wgpu::Adapter) -> wgpu::Features {
self.features | (self.optional_features & adapter.features())
}
}
struct NoWindow;
unsafe impl HasRawWindowHandle for NoWindow {
fn raw_window_handle(&self) -> RawWindowHandle {
unsafe { std::mem::zeroed() }
}
}