|
2 | 2 |
|
3 | 3 | #![allow(unsafe_code)]
|
4 | 4 |
|
| 5 | +/// Low-level painting of [`egui`] on [`wgpu`]. |
5 | 6 | pub mod renderer;
|
6 | 7 |
|
7 |
| -/// Everything you need to paint egui with [`wgpu`] on winit. |
8 |
| -/// |
9 |
| -/// Alternatively you can use [`crate::renderer`] directly. |
10 |
| -pub struct Painter { |
11 |
| - device: wgpu::Device, |
12 |
| - queue: wgpu::Queue, |
13 |
| - surface_config: wgpu::SurfaceConfiguration, |
14 |
| - surface: wgpu::Surface, |
15 |
| - egui_rpass: renderer::RenderPass, |
16 |
| -} |
17 |
| - |
18 |
| -impl Painter { |
19 |
| - /// Creates a [`wgpu`] surface for the given window, and things required to render egui onto it. |
20 |
| - /// |
21 |
| - /// SAFETY: The given window MUST outlive [`Painter`]. |
22 |
| - pub unsafe fn new(window: &'window winit::window::Window) -> Self { |
23 |
| - let instance = wgpu::Instance::new(wgpu::Backends::PRIMARY | wgpu::Backends::GL); |
24 |
| - let surface = unsafe { instance.create_surface(&window) }; |
25 |
| - |
26 |
| - // WGPU 0.11+ support force fallback (if HW implementation not supported), set it to true or false (optional). |
27 |
| - let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions { |
28 |
| - power_preference: wgpu::PowerPreference::HighPerformance, |
29 |
| - compatible_surface: Some(&surface), |
30 |
| - force_fallback_adapter: false, |
31 |
| - })) |
32 |
| - .unwrap(); |
33 |
| - |
34 |
| - let (device, queue) = pollster::block_on(adapter.request_device( |
35 |
| - &wgpu::DeviceDescriptor { |
36 |
| - features: wgpu::Features::default(), |
37 |
| - limits: wgpu::Limits::default(), |
38 |
| - label: None, |
39 |
| - }, |
40 |
| - None, |
41 |
| - )) |
42 |
| - .unwrap(); |
43 |
| - |
44 |
| - let size = window.inner_size(); |
45 |
| - let surface_format = surface.get_preferred_format(&adapter).unwrap(); |
46 |
| - let surface_config = wgpu::SurfaceConfiguration { |
47 |
| - usage: wgpu::TextureUsages::RENDER_ATTACHMENT, |
48 |
| - format: surface_format, |
49 |
| - width: size.width as u32, |
50 |
| - height: size.height as u32, |
51 |
| - present_mode: wgpu::PresentMode::Fifo, // TODO: make vsync configurable |
52 |
| - }; |
53 |
| - surface.configure(&device, &surface_config); |
54 |
| - |
55 |
| - let egui_rpass = renderer::RenderPass::new(&device, surface_format, 1); |
56 |
| - |
57 |
| - Self { |
58 |
| - device, |
59 |
| - queue, |
60 |
| - surface_config, |
61 |
| - surface, |
62 |
| - egui_rpass, |
63 |
| - } |
64 |
| - } |
65 |
| - |
66 |
| - pub fn max_texture_side(&self) -> usize { |
67 |
| - self.device.limits().max_texture_dimension_2d as usize |
68 |
| - } |
69 |
| - |
70 |
| - pub fn on_window_resized(&mut self, width: u32, height: u32) { |
71 |
| - self.surface_config.width = width; |
72 |
| - self.surface_config.height = height; |
73 |
| - self.surface.configure(&self.device, &self.surface_config); |
74 |
| - } |
75 |
| - |
76 |
| - pub fn paint_and_update_textures( |
77 |
| - &mut self, |
78 |
| - pixels_per_point: f32, |
79 |
| - clear_color: egui::Rgba, |
80 |
| - clipped_primitives: &[egui::ClippedPrimitive], |
81 |
| - textures_delta: &egui::TexturesDelta, |
82 |
| - ) { |
83 |
| - let output_frame = match self.surface.get_current_texture() { |
84 |
| - Ok(frame) => frame, |
85 |
| - Err(wgpu::SurfaceError::Outdated) => { |
86 |
| - // This error occurs when the app is minimized on Windows. |
87 |
| - // Silently return here to prevent spamming the console with: |
88 |
| - // "The underlying surface has changed, and therefore the swap chain must be updated" |
89 |
| - return; |
90 |
| - } |
91 |
| - Err(e) => { |
92 |
| - tracing::warn!("Dropped frame with error: {e}"); |
93 |
| - return; |
94 |
| - } |
95 |
| - }; |
96 |
| - let output_view = output_frame |
97 |
| - .texture |
98 |
| - .create_view(&wgpu::TextureViewDescriptor::default()); |
99 |
| - |
100 |
| - let mut encoder = self |
101 |
| - .device |
102 |
| - .create_command_encoder(&wgpu::CommandEncoderDescriptor { |
103 |
| - label: Some("encoder"), |
104 |
| - }); |
105 |
| - |
106 |
| - // Upload all resources for the GPU. |
107 |
| - let screen_descriptor = renderer::ScreenDescriptor { |
108 |
| - size_in_pixels: [self.surface_config.width, self.surface_config.height], |
109 |
| - pixels_per_point, |
110 |
| - }; |
111 |
| - |
112 |
| - for (id, image_delta) in &textures_delta.set { |
113 |
| - self.egui_rpass |
114 |
| - .update_texture(&self.device, &self.queue, *id, image_delta); |
115 |
| - } |
116 |
| - for id in &textures_delta.free { |
117 |
| - self.egui_rpass.free_texture(id); |
118 |
| - } |
119 |
| - |
120 |
| - self.egui_rpass.update_buffers( |
121 |
| - &self.device, |
122 |
| - &self.queue, |
123 |
| - clipped_primitives, |
124 |
| - &screen_descriptor, |
125 |
| - ); |
126 |
| - |
127 |
| - // Record all render passes. |
128 |
| - self.egui_rpass |
129 |
| - .execute( |
130 |
| - &mut encoder, |
131 |
| - &output_view, |
132 |
| - clipped_primitives, |
133 |
| - &screen_descriptor, |
134 |
| - Some(wgpu::Color { |
135 |
| - r: clear_color.r() as f64, |
136 |
| - g: clear_color.g() as f64, |
137 |
| - b: clear_color.b() as f64, |
138 |
| - a: clear_color.a() as f64, |
139 |
| - }), |
140 |
| - ) |
141 |
| - .unwrap(); |
142 |
| - |
143 |
| - // Submit the commands. |
144 |
| - self.queue.submit(std::iter::once(encoder.finish())); |
145 |
| - |
146 |
| - // Redraw egui |
147 |
| - output_frame.present(); |
148 |
| - } |
149 |
| - |
150 |
| - #[allow(clippy::unused_self)] |
151 |
| - pub fn destroy(&mut self) { |
152 |
| - // TODO: something here? |
153 |
| - } |
154 |
| -} |
| 8 | +/// Module for painting [`egui`] with [`wgpu`] on [`winit`]. |
| 9 | +#[cfg(feature = "winit")] |
| 10 | +pub mod winit; |
0 commit comments