From f0faadc356f5254ec1a821f00e5af304ed6399e2 Mon Sep 17 00:00:00 2001 From: Rose Hudson Date: Tue, 13 Dec 2022 23:35:56 +0000 Subject: [PATCH 1/2] Add multi-adapter support some `RenderContext` stuff is now `DeviceHandle` stuff, and device handles are created alongside surfaces to ensure compatibility, but are reused if possible. Fixes #224 wgpu Adapter and Surface might not be compatible --- src/util.rs | 97 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 73 insertions(+), 24 deletions(-) diff --git a/src/util.rs b/src/util.rs index 3118e50..146a1e8 100644 --- a/src/util.rs +++ b/src/util.rs @@ -19,41 +19,33 @@ use super::Result; use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle}; -use wgpu::{Device, Instance, Limits, Queue, Surface, SurfaceConfiguration}; +use wgpu::{ + Adapter, Device, Instance, Limits, Queue, RequestAdapterOptions, Surface, SurfaceConfiguration, +}; /// Simple render context that maintains wgpu state for rendering the pipeline. pub struct RenderContext { pub instance: Instance, - pub device: Device, - pub queue: Queue, + devices: Vec, +} + +struct DeviceHandle { + adapter: Adapter, + device: Device, + queue: Queue, } impl RenderContext { - pub async fn new() -> Result { + pub fn new() -> Result { let instance = Instance::new(wgpu::Backends::PRIMARY); - let adapter = instance.request_adapter(&Default::default()).await.unwrap(); - let features = adapter.features(); - let mut limits = Limits::default(); - let (device, queue) = adapter - .request_device( - &wgpu::DeviceDescriptor { - label: None, - features: features - & (wgpu::Features::TIMESTAMP_QUERY | wgpu::Features::CLEAR_TEXTURE), - limits, - }, - None, - ) - .await?; Ok(Self { instance, - device, - queue, + devices: Vec::new(), }) } /// Creates a new surface for the specified window and dimensions. - pub fn create_surface(&self, window: &W, width: u32, height: u32) -> RenderSurface + pub async fn create_surface(&mut self, window: &W, width: u32, height: u32) -> RenderSurface where W: HasRawWindowHandle + HasRawDisplayHandle, { @@ -67,15 +59,71 @@ impl RenderContext { present_mode: wgpu::PresentMode::Fifo, alpha_mode: wgpu::CompositeAlphaMode::Auto, }; - surface.configure(&self.device, &config); - RenderSurface { surface, config } + let dev_id = self.device(Some(&surface)).await.unwrap(); + surface.configure(&self.devices[dev_id].device, &config); + RenderSurface { + surface, + config, + dev_id, + } } /// Resizes the surface to the new dimensions. pub fn resize_surface(&self, surface: &mut RenderSurface, width: u32, height: u32) { surface.config.width = width; surface.config.height = height; - surface.surface.configure(&self.device, &surface.config); + surface + .surface + .configure(&self.devices[surface.dev_id].device, &surface.config); + } + + /// Finds or creates a compatible device handle id. + async fn device(&mut self, compatible_surface: Option<&Surface>) -> Option { + let compatible = match compatible_surface { + Some(s) => self + .devices + .iter() + .enumerate() + .find(|(_, d)| d.adapter.is_surface_supported(s)) + .map(|(i, _)| i), + None => (!self.devices.is_empty()).then_some(0), + }; + if compatible.is_none() { + return self.new_device(compatible_surface).await; + } + return compatible; + } + + /// Creates a compatible device handle id. + async fn new_device(&mut self, compatible_surface: Option<&Surface>) -> Option { + let adapter = self + .instance + .request_adapter(&RequestAdapterOptions { + compatible_surface, + ..Default::default() + }) + .await?; + let features = adapter.features(); + let limits = Limits::default(); + let (device, queue) = adapter + .request_device( + &wgpu::DeviceDescriptor { + label: None, + features: features + & (wgpu::Features::TIMESTAMP_QUERY | wgpu::Features::CLEAR_TEXTURE), + limits, + }, + None, + ) + .await + .ok()?; + let device_handle = DeviceHandle { + adapter, + device, + queue, + }; + self.devices.push(device_handle); + Some(self.devices.len() - 1) } } @@ -83,4 +131,5 @@ impl RenderContext { pub struct RenderSurface { pub surface: Surface, pub config: SurfaceConfiguration, + dev_id: usize, } From c5401e777e0f54430e76e1920a697d858b59205f Mon Sep 17 00:00:00 2001 From: Rose Hudson Date: Tue, 13 Dec 2022 23:38:50 +0000 Subject: [PATCH 2/2] Update winit example to use new multi-adapter API --- examples/with_winit/src/main.rs | 16 ++++++++++------ src/util.rs | 10 +++++----- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/examples/with_winit/src/main.rs b/examples/with_winit/src/main.rs index 4e6a403..555acce 100644 --- a/examples/with_winit/src/main.rs +++ b/examples/with_winit/src/main.rs @@ -23,10 +23,13 @@ use winit::{event_loop::EventLoop, window::Window}; async fn run(event_loop: EventLoop<()>, window: Window) { use winit::{event::*, event_loop::ControlFlow}; - let render_cx = RenderContext::new().await.unwrap(); + let mut render_cx = RenderContext::new().unwrap(); let size = window.inner_size(); - let mut surface = render_cx.create_surface(&window, size.width, size.height); - let mut renderer = Renderer::new(&render_cx.device).unwrap(); + let mut surface = render_cx + .create_surface(&window, size.width, size.height) + .await; + let device_handle = &render_cx.devices[surface.dev_id]; + let mut renderer = Renderer::new(&device_handle.device).unwrap(); let mut simple_text = simple_text::SimpleText::new(); let mut current_frame = 0usize; let mut scene_ix = 0usize; @@ -59,6 +62,7 @@ async fn run(event_loop: EventLoop<()>, window: Window) { current_frame += 1; let width = surface.config.width; let height = surface.config.height; + let device_handle = &render_cx.devices[surface.dev_id]; let mut builder = SceneBuilder::for_scene(&mut scene); const N_SCENES: usize = 6; match scene_ix % N_SCENES { @@ -76,8 +80,8 @@ async fn run(event_loop: EventLoop<()>, window: Window) { .expect("failed to get surface texture"); renderer .render_to_surface( - &render_cx.device, - &render_cx.queue, + &device_handle.device, + &device_handle.queue, &scene, &surface_texture, width, @@ -85,7 +89,7 @@ async fn run(event_loop: EventLoop<()>, window: Window) { ) .expect("failed to render to surface"); surface_texture.present(); - render_cx.device.poll(wgpu::Maintain::Wait); + device_handle.device.poll(wgpu::Maintain::Wait); } _ => {} }); diff --git a/src/util.rs b/src/util.rs index 146a1e8..07750f4 100644 --- a/src/util.rs +++ b/src/util.rs @@ -26,13 +26,13 @@ use wgpu::{ /// Simple render context that maintains wgpu state for rendering the pipeline. pub struct RenderContext { pub instance: Instance, - devices: Vec, + pub devices: Vec, } -struct DeviceHandle { +pub struct DeviceHandle { adapter: Adapter, - device: Device, - queue: Queue, + pub device: Device, + pub queue: Queue, } impl RenderContext { @@ -131,5 +131,5 @@ impl RenderContext { pub struct RenderSurface { pub surface: Surface, pub config: SurfaceConfiguration, - dev_id: usize, + pub dev_id: usize, }