//! GPU acquisition: instance, adapter, device, queue. use super::RenderError; /// A handle to the GPU: instance, adapter, and the device/queue pair every /// rendering operation goes through. /// /// Created either for a window surface (via [`RenderContext`]) or headless /// with [`Gpu::headless`] for offscreen rendering and tests. /// /// [`RenderContext`]: super::RenderContext pub struct Gpu { instance: wgpu::Instance, adapter: wgpu::Adapter, device: wgpu::Device, queue: wgpu::Queue, } impl Gpu { /// Acquires an adapter and device from an existing `instance`, preferring /// an adapter that can present to `compatible_surface` when one is given. /// /// `force_fallback_adapter` requests a software adapter (e.g. llvmpipe), /// used as a last resort when no hardware adapter works. pub(crate) fn with_instance( instance: wgpu::Instance, compatible_surface: Option<&wgpu::Surface<'_>>, force_fallback_adapter: bool, ) -> Result { let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions { power_preference: wgpu::PowerPreference::HighPerformance, force_fallback_adapter, compatible_surface, }))?; log::info!( "GPU adapter: {} ({:?})", adapter.get_info().name, adapter.get_info().backend ); let (device, queue) = pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor { label: Some("oxide.device"), ..Default::default() }))?; Ok(Self { instance, adapter, device, queue, }) } /// Acquires the GPU without any surface, for offscreen rendering and /// automated tests. /// /// Tries a hardware adapter first, then falls back to a software adapter /// (e.g. llvmpipe) so headless rendering also works on machines without a /// usable GPU. pub fn headless() -> Result { // `from_env` keeps backend/flags overridable via WGPU_* env vars. let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle_from_env()); match Self::with_instance(instance, None, false) { Ok(gpu) => Ok(gpu), Err(hardware_err) => { log::warn!("no hardware GPU adapter ({hardware_err}); trying software fallback"); let instance = wgpu::Instance::new( wgpu::InstanceDescriptor::new_without_display_handle_from_env(), ); Self::with_instance(instance, None, true) } } } /// The wgpu instance the adapter was created from. pub fn instance(&self) -> &wgpu::Instance { &self.instance } /// The physical adapter in use. pub fn adapter(&self) -> &wgpu::Adapter { &self.adapter } /// The logical device used to create GPU resources. pub fn device(&self) -> &wgpu::Device { &self.device } /// The queue used to submit command buffers. pub fn queue(&self) -> &wgpu::Queue { &self.queue } }