1mod painter;
2mod surface;
3use std::sync::Arc;
4use tracing::{debug, info};
5use wgpu::*;
6
7pub use painter::*;
8pub use surface::SurfaceManager;
9pub use wgpu;
10
11pub struct WgpuConfig {
12 pub backends: Backends,
13 pub power_preference: PowerPreference,
14 pub device_descriptor: DeviceDescriptor<'static>,
15 pub surface_formats_priority: Vec<TextureFormat>,
18 pub surface_config: SurfaceConfiguration,
20 pub transparent_surface: Option<bool>,
21}
22impl Default for WgpuConfig {
23 fn default() -> Self {
24 Self {
25 backends: Backends::all(),
26 power_preference: PowerPreference::default(),
27 device_descriptor: DeviceDescriptor {
28 label: Some("my wgpu device"),
29 required_features: Default::default(),
30 required_limits: Limits::downlevel_defaults(),
31 memory_hints: MemoryHints::default(),
32 },
33 surface_config: SurfaceConfiguration {
34 usage: TextureUsages::RENDER_ATTACHMENT,
35 format: TextureFormat::Bgra8UnormSrgb,
36 width: 0,
37 height: 0,
38 present_mode: PresentMode::Fifo,
39 alpha_mode: wgpu::CompositeAlphaMode::Auto,
40 view_formats: vec![],
41 desired_maximum_frame_latency: 2,
42 },
43 surface_formats_priority: vec![],
44 transparent_surface: Some(true),
45 }
46 }
47}
48pub struct WgpuBackend {
51 pub instance: Arc<Instance>,
53 pub adapter: Arc<Adapter>,
55 pub device: Arc<Device>,
57 pub queue: Arc<Queue>,
59 pub painter: EguiPainter,
61 pub surface_manager: SurfaceManager,
62 pub command_encoders: Vec<CommandEncoder>,
67}
68impl Drop for WgpuBackend {
69 fn drop(&mut self) {
70 tracing::warn!("dropping wgpu backend");
71 }
72}
73impl WgpuBackend {
74 pub async fn new_async(
77 config: WgpuConfig,
78 window: Option<Box<dyn WindowHandle>>,
79 latest_fb_size: [u32; 2],
80 ) -> Self {
81 let WgpuConfig {
82 power_preference,
83 device_descriptor,
84 surface_formats_priority,
85 surface_config,
86 backends,
87 transparent_surface,
88 } = config;
89 debug!("using wgpu backends: {:?}", backends);
90 let instance = Arc::new(Instance::new(InstanceDescriptor {
91 backends,
92 dx12_shader_compiler: Default::default(),
93 flags: InstanceFlags::from_build_config(),
94 gles_minor_version: Gles3MinorVersion::Automatic,
95 }));
96 debug!("iterating over all adapters");
97 #[cfg(not(target_arch = "wasm32"))]
98 for adapter in instance.enumerate_adapters(Backends::all()) {
99 debug!("adapter: {:#?}", adapter.get_info());
100 }
101
102 let surface = window.map(|w| {
103 tracing::debug!("creating a surface");
104 instance
105 .create_surface(SurfaceTarget::Window(w))
106 .expect("failed to create surface")
107 });
108
109 info!("is surfaced created at startup?: {}", surface.is_some());
110
111 debug!("using power preference: {:?}", config.power_preference);
112 let adapter = Arc::new(
113 instance
114 .request_adapter(&RequestAdapterOptions {
115 power_preference,
116 force_fallback_adapter: false,
117 compatible_surface: surface.as_ref(),
118 })
119 .await
120 .expect("failed to get adapter"),
121 );
122
123 info!("chosen adapter details: {:?}", adapter.get_info());
124 let (device, queue) = adapter
125 .request_device(&device_descriptor, Default::default())
126 .await
127 .expect("failed to create wgpu device");
128
129 let device = Arc::new(device);
130 let queue = Arc::new(queue);
131
132 let surface_manager = SurfaceManager::new(
133 None,
134 transparent_surface,
135 latest_fb_size,
136 &instance,
137 &adapter,
138 &device,
139 surface,
140 surface_formats_priority,
141 surface_config,
142 );
143
144 debug!("device features: {:#?}", device.features());
145 debug!("device limits: {:#?}", device.limits());
146
147 let painter = EguiPainter::new(&device, surface_manager.surface_config.format);
148
149 Self {
150 instance,
151 adapter,
152 device,
153 queue,
154 painter,
155 command_encoders: Vec::new(),
156 surface_manager,
157 }
158 }
159}
160impl WgpuBackend {
161 pub fn new(
162 config: WgpuConfig,
163 window: Option<Box<dyn WindowHandle>>,
164 latest_fb_size: [u32; 2],
165 ) -> Self {
166 pollster::block_on(Self::new_async(config, window, latest_fb_size))
167 }
168
169 pub fn resume(
170 &mut self,
171 window: Option<Box<dyn WindowHandle>>,
172 latest_fb_size: [u32; 2],
173 transparent: Option<bool>,
174 ) {
175 self.surface_manager.reconfigure_surface(
176 window,
177 transparent,
178 latest_fb_size,
179 &self.instance,
180 &self.adapter,
181 &self.device,
182 );
183 self.painter.on_resume(
184 &self.device,
185 self.surface_manager
186 .surface_config
187 .view_formats
188 .first()
189 .copied()
190 .unwrap(),
191 );
192 }
193
194 pub fn prepare_frame(&mut self, latest_framebuffer_size_getter: impl FnMut() -> [u32; 2]) {
195 self.surface_manager
196 .create_current_surface_texture_view(latest_framebuffer_size_getter, &self.device);
197 if let Some(view) = self.surface_manager.surface_view.as_ref() {
198 let mut ce = self
199 .device
200 .create_command_encoder(&CommandEncoderDescriptor {
201 label: "surface clear ce".into(),
202 });
203 ce.begin_render_pass(&RenderPassDescriptor {
204 label: "surface clear rpass".into(),
205 color_attachments: &[Some(RenderPassColorAttachment {
206 view,
207 resolve_target: None,
208 ops: Operations {
209 load: LoadOp::Clear(wgpu::Color::TRANSPARENT),
210 store: StoreOp::Store,
211 },
212 })],
213 ..Default::default()
214 });
215 self.command_encoders.push(ce);
216 }
217 }
218
219 pub fn render_egui(
220 &mut self,
221 meshes: Vec<egui::ClippedPrimitive>,
222 textures_delta: egui::TexturesDelta,
223 logical_screen_size: [f32; 2],
224 ) {
225 let mut command_encoder = self
226 .device
227 .create_command_encoder(&CommandEncoderDescriptor {
228 label: Some("egui command encoder"),
229 });
230 let draw_calls = self.painter.upload_egui_data(
231 &self.device,
232 &self.queue,
233 meshes,
234 textures_delta,
235 logical_screen_size,
236 [
237 self.surface_manager.surface_config.width,
238 self.surface_manager.surface_config.height,
239 ],
240 &mut command_encoder,
241 );
242 {
243 let mut egui_pass = command_encoder.begin_render_pass(&RenderPassDescriptor {
244 label: Some("egui render pass"),
245 color_attachments: &[Some(RenderPassColorAttachment {
246 view: self
247 .surface_manager
248 .surface_view
249 .as_ref()
250 .expect("failed ot get surface view for egui render pass creation"),
251 resolve_target: None,
252 ops: Operations {
253 load: LoadOp::Load,
254 store: StoreOp::Store,
255 },
256 })],
257 ..Default::default()
258 });
259 self.painter
260 .draw_egui_with_renderpass(&mut egui_pass, draw_calls);
261 }
262 self.command_encoders.push(command_encoder);
263 }
264
265 pub fn present(&mut self) {
266 assert!(self.surface_manager.surface_view.is_some());
267 self.queue.submit(
268 std::mem::take(&mut self.command_encoders)
269 .into_iter()
270 .map(|encoder| encoder.finish()),
271 );
272 {
273 self.surface_manager
274 .surface_view
275 .take()
276 .expect("failed to get surface view to present");
277 }
278 self.surface_manager
279 .surface_current_image
280 .take()
281 .expect("failed to surface texture to preset")
282 .present();
283 }
284
285 pub fn resize_framebuffer(&mut self, latest_fb_size: [u32; 2]) {
286 self.surface_manager
287 .resize_framebuffer(&self.device, latest_fb_size);
288 }
289
290 pub fn suspend(&mut self) {
291 self.surface_manager.suspend();
292 }
293}
294pub fn scissor_from_clip_rect(
304 clip_rect: &egui::Rect,
305 scale: f32,
306 physical_framebuffer_size: [u32; 2],
307) -> Option<[u32; 4]> {
308 let clip_min_x = scale * clip_rect.min.x;
313 let clip_min_y = scale * clip_rect.min.y;
314 let clip_max_x = scale * clip_rect.max.x;
315 let clip_max_y = scale * clip_rect.max.y;
316
317 let clip_min_x = clip_min_x.round() as i32;
319 let clip_min_y = clip_min_y.round() as i32;
320 let clip_max_x = clip_max_x.round() as i32;
321 let clip_max_y = clip_max_y.round() as i32;
322
323 let clip_min_x = clip_min_x.clamp(0, physical_framebuffer_size[0] as i32);
325 let clip_min_y = clip_min_y.clamp(0, physical_framebuffer_size[1] as i32);
326 let clip_max_x = clip_max_x.clamp(clip_min_x, physical_framebuffer_size[0] as i32);
328 let clip_max_y = clip_max_y.clamp(clip_min_y, physical_framebuffer_size[1] as i32);
329 let x = clip_min_x as u32;
331 let y = clip_min_y as u32;
332 let width = (clip_max_x - clip_min_x) as u32;
334 let height = (clip_max_y - clip_min_y) as u32;
335 (width != 0 && height != 0).then_some([x, y, width, height])
337}