Skip to main content

graphix_package_gui/
render.rs

1//! wgpu + iced renderer setup.
2//!
3//! Each window gets its own wgpu Surface and iced Renderer. The Renderer
4//! owns an iced Engine which in turn owns the wgpu Device and Queue.
5//! GpuState keeps clones of Device for surface configuration (wgpu
6//! handles are internally reference-counted, so clones are cheap).
7//!
8//! We use `iced_wgpu::wgpu` (re-exported) rather than a direct wgpu
9//! dependency to ensure type compatibility with iced.
10
11use anyhow::{Context, Result};
12use iced_wgpu::graphics::{Shell, Viewport};
13use iced_wgpu::wgpu;
14use std::sync::Arc;
15use winit::window::Window;
16
17pub struct GpuState {
18    pub instance: wgpu::Instance,
19    pub adapter: wgpu::Adapter,
20    pub device: wgpu::Device,
21    pub queue: wgpu::Queue,
22    pub format: wgpu::TextureFormat,
23}
24
25impl GpuState {
26    pub async fn new(window: Arc<Window>) -> Result<Self> {
27        let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {
28            backends: wgpu::Backends::all(),
29            ..Default::default()
30        });
31        let surface =
32            instance.create_surface(window).context("failed to create init surface")?;
33        let adapter = instance
34            .request_adapter(&wgpu::RequestAdapterOptions {
35                power_preference: wgpu::PowerPreference::default(),
36                compatible_surface: Some(&surface),
37                force_fallback_adapter: false,
38            })
39            .await
40            .context("no suitable GPU adapter found")?;
41        let (device, queue) = adapter
42            .request_device(&wgpu::DeviceDescriptor::default())
43            .await
44            .context("failed to create GPU device")?;
45        let format = surface
46            .get_capabilities(&adapter)
47            .formats
48            .into_iter()
49            .next()
50            .context("surface has no supported formats")?;
51        drop(surface);
52        Ok(Self { instance, adapter, device, queue, format })
53    }
54
55    /// Create an iced Engine + Renderer pair for a window.
56    pub fn create_renderer(&self) -> iced_wgpu::Renderer {
57        let engine = iced_wgpu::Engine::new(
58            &self.adapter,
59            self.device.clone(),
60            self.queue.clone(),
61            self.format,
62            None,
63            Shell::headless(),
64        );
65        iced_wgpu::Renderer::new(
66            engine,
67            iced_core::Font::DEFAULT,
68            iced_core::Pixels(16.0),
69        )
70    }
71}
72
73pub struct WindowSurface {
74    pub surface: wgpu::Surface<'static>,
75    pub config: wgpu::SurfaceConfiguration,
76    pub renderer: iced_wgpu::Renderer,
77    pub viewport: Viewport,
78}
79
80impl WindowSurface {
81    pub fn new(gpu: &GpuState, window: Arc<Window>) -> Result<Self> {
82        let size = window.inner_size();
83        let surface = gpu
84            .instance
85            .create_surface(window.clone())
86            .context("failed to create wgpu surface")?;
87        let config = wgpu::SurfaceConfiguration {
88            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
89            format: gpu.format,
90            width: size.width.max(1),
91            height: size.height.max(1),
92            present_mode: wgpu::PresentMode::AutoVsync,
93            alpha_mode: wgpu::CompositeAlphaMode::Auto,
94            view_formats: vec![],
95            desired_maximum_frame_latency: 2,
96        };
97        surface.configure(&gpu.device, &config);
98        let renderer = gpu.create_renderer();
99        let scale = window.scale_factor() as f32;
100        let viewport = Viewport::with_physical_size(
101            iced_core::Size::new(size.width, size.height),
102            scale,
103        );
104        Ok(Self { surface, config, renderer, viewport })
105    }
106
107    pub fn resize(&mut self, gpu: &GpuState, width: u32, height: u32, scale: f64) {
108        self.config.width = width.max(1);
109        self.config.height = height.max(1);
110        self.surface.configure(&gpu.device, &self.config);
111        self.viewport = Viewport::with_physical_size(
112            iced_core::Size::new(width, height),
113            scale as f32,
114        );
115    }
116
117    pub fn logical_size(&self) -> iced_core::Size {
118        self.viewport.logical_size()
119    }
120}