1#![doc = document_features::document_features!()]
17#![allow(unsafe_code)]
20#![cfg_attr(target_arch = "wasm32", allow(clippy::arc_with_non_send_sync))]
23
24pub use wgpu;
25
26mod renderer;
28
29pub use renderer::*;
30
31#[cfg(feature = "winit")]
33pub mod winit;
34
35use std::sync::Arc;
36
37use epaint::mutex::RwLock;
38
39#[derive(thiserror::Error, Debug)]
41pub enum WgpuError {
42 #[error("Failed to create wgpu adapter, no suitable adapter found.")]
43 NoSuitableAdapterFound,
44
45 #[error("There was no valid format for the surface at all.")]
46 NoSurfaceFormatsAvailable,
47
48 #[error(transparent)]
49 RequestDeviceError(#[from] wgpu::RequestDeviceError),
50
51 #[error(transparent)]
52 CreateSurfaceError(#[from] wgpu::CreateSurfaceError),
53
54 #[cfg(feature = "winit")]
55 #[error(transparent)]
56 HandleError(#[from] ::winit::raw_window_handle::HandleError),
57}
58
59#[derive(Clone)]
61pub struct RenderState {
62 pub adapter: Arc<wgpu::Adapter>,
64
65 #[cfg(not(target_arch = "wasm32"))]
70 pub available_adapters: Arc<[wgpu::Adapter]>,
71
72 pub device: Arc<wgpu::Device>,
74
75 pub queue: Arc<wgpu::Queue>,
77
78 pub target_format: wgpu::TextureFormat,
80
81 pub renderer: Arc<RwLock<Renderer>>,
83}
84
85impl RenderState {
86 pub async fn create(
91 config: &WgpuConfiguration,
92 instance: &wgpu::Instance,
93 surface: &wgpu::Surface<'static>,
94 depth_format: Option<wgpu::TextureFormat>,
95 msaa_samples: u32,
96 ) -> Result<Self, WgpuError> {
97 crate::profile_scope!("RenderState::create"); #[cfg(not(target_arch = "wasm32"))]
101 let available_adapters = instance.enumerate_adapters(wgpu::Backends::all());
102
103 let adapter = {
104 crate::profile_scope!("request_adapter");
105 instance
106 .request_adapter(&wgpu::RequestAdapterOptions {
107 power_preference: config.power_preference,
108 compatible_surface: Some(surface),
109 force_fallback_adapter: false,
110 })
111 .await
112 .ok_or_else(|| {
113 #[cfg(not(target_arch = "wasm32"))]
114 if available_adapters.is_empty() {
115 log::info!("No wgpu adapters found");
116 } else if available_adapters.len() == 1 {
117 log::info!(
118 "The only available wgpu adapter was not suitable: {}",
119 adapter_info_summary(&available_adapters[0].get_info())
120 );
121 } else {
122 log::info!(
123 "No suitable wgpu adapter found out of the {} available ones: {}",
124 available_adapters.len(),
125 describe_adapters(&available_adapters)
126 );
127 }
128
129 WgpuError::NoSuitableAdapterFound
130 })?
131 };
132
133 #[cfg(target_arch = "wasm32")]
134 log::debug!(
135 "Picked wgpu adapter: {}",
136 adapter_info_summary(&adapter.get_info())
137 );
138
139 #[cfg(not(target_arch = "wasm32"))]
140 if available_adapters.len() == 1 {
141 log::debug!(
142 "Picked the only available wgpu adapter: {}",
143 adapter_info_summary(&adapter.get_info())
144 );
145 } else {
146 log::info!(
147 "There were {} available wgpu adapters: {}",
148 available_adapters.len(),
149 describe_adapters(&available_adapters)
150 );
151 log::debug!(
152 "Picked wgpu adapter: {}",
153 adapter_info_summary(&adapter.get_info())
154 );
155 }
156
157 let capabilities = {
158 crate::profile_scope!("get_capabilities");
159 surface.get_capabilities(&adapter).formats
160 };
161 let target_format = crate::preferred_framebuffer_format(&capabilities)?;
162
163 let (device, queue) = {
164 crate::profile_scope!("request_device");
165 adapter
166 .request_device(&(*config.device_descriptor)(&adapter), None)
167 .await?
168 };
169
170 let renderer = Renderer::new(&device, target_format, depth_format, msaa_samples);
171
172 Ok(Self {
173 adapter: Arc::new(adapter),
174 #[cfg(not(target_arch = "wasm32"))]
175 available_adapters: available_adapters.into(),
176 device: Arc::new(device),
177 queue: Arc::new(queue),
178 target_format,
179 renderer: Arc::new(RwLock::new(renderer)),
180 })
181 }
182}
183
184#[cfg(not(target_arch = "wasm32"))]
185fn describe_adapters(adapters: &[wgpu::Adapter]) -> String {
186 if adapters.is_empty() {
187 "(none)".to_owned()
188 } else if adapters.len() == 1 {
189 adapter_info_summary(&adapters[0].get_info())
190 } else {
191 let mut list_string = String::new();
192 for adapter in adapters {
193 if !list_string.is_empty() {
194 list_string += ", ";
195 }
196 list_string += &format!("{{{}}}", adapter_info_summary(&adapter.get_info()));
197 }
198 list_string
199 }
200}
201
202pub enum SurfaceErrorAction {
204 SkipFrame,
206
207 RecreateSurface,
209}
210
211#[derive(Clone)]
217pub struct WgpuConfiguration {
218 pub supported_backends: wgpu::Backends,
227
228 pub device_descriptor: Arc<dyn Fn(&wgpu::Adapter) -> wgpu::DeviceDescriptor<'static>>,
230
231 pub present_mode: wgpu::PresentMode,
233
234 pub desired_maximum_frame_latency: Option<u32>,
242
243 pub power_preference: wgpu::PowerPreference,
245
246 pub on_surface_error: Arc<dyn Fn(wgpu::SurfaceError) -> SurfaceErrorAction>,
248}
249
250impl std::fmt::Debug for WgpuConfiguration {
251 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
252 let Self {
253 supported_backends,
254 device_descriptor: _,
255 present_mode,
256 desired_maximum_frame_latency,
257 power_preference,
258 on_surface_error: _,
259 } = self;
260 f.debug_struct("WgpuConfiguration")
261 .field("supported_backends", &supported_backends)
262 .field("present_mode", &present_mode)
263 .field(
264 "desired_maximum_frame_latency",
265 &desired_maximum_frame_latency,
266 )
267 .field("power_preference", &power_preference)
268 .finish_non_exhaustive()
269 }
270}
271
272impl Default for WgpuConfiguration {
273 fn default() -> Self {
274 Self {
275 supported_backends: wgpu::util::backend_bits_from_env()
278 .unwrap_or(wgpu::Backends::PRIMARY | wgpu::Backends::GL),
279
280 device_descriptor: Arc::new(|adapter| {
281 let base_limits = if adapter.get_info().backend == wgpu::Backend::Gl {
282 wgpu::Limits::downlevel_webgl2_defaults()
283 } else {
284 wgpu::Limits::default()
285 };
286
287 wgpu::DeviceDescriptor {
288 label: Some("egui wgpu device"),
289 required_features: wgpu::Features::default(),
290 required_limits: wgpu::Limits {
291 max_texture_dimension_2d: 8192,
294 ..base_limits
295 },
296 }
297 }),
298
299 present_mode: wgpu::PresentMode::AutoVsync,
300
301 desired_maximum_frame_latency: None,
302
303 power_preference: wgpu::util::power_preference_from_env()
304 .unwrap_or(wgpu::PowerPreference::HighPerformance),
305
306 on_surface_error: Arc::new(|err| {
307 if err == wgpu::SurfaceError::Outdated {
308 } else {
312 log::warn!("Dropped frame with error: {err}");
313 }
314 SurfaceErrorAction::SkipFrame
315 }),
316 }
317 }
318}
319
320pub fn preferred_framebuffer_format(
325 formats: &[wgpu::TextureFormat],
326) -> Result<wgpu::TextureFormat, WgpuError> {
327 for &format in formats {
328 if matches!(
329 format,
330 wgpu::TextureFormat::Rgba8Unorm | wgpu::TextureFormat::Bgra8Unorm
331 ) {
332 return Ok(format);
333 }
334 }
335
336 formats
337 .first()
338 .copied()
339 .ok_or(WgpuError::NoSurfaceFormatsAvailable)
340}
341
342pub fn depth_format_from_bits(depth_buffer: u8, stencil_buffer: u8) -> Option<wgpu::TextureFormat> {
344 match (depth_buffer, stencil_buffer) {
345 (0, 8) => Some(wgpu::TextureFormat::Stencil8),
346 (16, 0) => Some(wgpu::TextureFormat::Depth16Unorm),
347 (24, 0) => Some(wgpu::TextureFormat::Depth24Plus),
348 (24, 8) => Some(wgpu::TextureFormat::Depth24PlusStencil8),
349 (32, 0) => Some(wgpu::TextureFormat::Depth32Float),
350 (32, 8) => Some(wgpu::TextureFormat::Depth32FloatStencil8),
351 _ => None,
352 }
353}
354
355pub fn adapter_info_summary(info: &wgpu::AdapterInfo) -> String {
359 let wgpu::AdapterInfo {
360 name,
361 vendor,
362 device,
363 device_type,
364 driver,
365 driver_info,
366 backend,
367 } = &info;
368
369 let mut summary = format!("backend: {backend:?}, device_type: {device_type:?}");
375
376 if !name.is_empty() {
377 summary += &format!(", name: {name:?}");
378 }
379 if !driver.is_empty() {
380 summary += &format!(", driver: {driver:?}");
381 }
382 if !driver_info.is_empty() {
383 summary += &format!(", driver_info: {driver_info:?}");
384 }
385 if *vendor != 0 {
386 summary += &format!(", vendor: 0x{vendor:04X}");
388 }
389 if *device != 0 {
390 summary += &format!(", device: 0x{device:02X}");
391 }
392
393 summary
394}
395
396mod profiling_scopes {
399 #![allow(unused_macros)]
400 #![allow(unused_imports)]
401
402 macro_rules! profile_function {
404 ($($arg: tt)*) => {
405 #[cfg(feature = "puffin")]
406 #[cfg(not(target_arch = "wasm32"))] puffin::profile_function!($($arg)*);
408 };
409 }
410 pub(crate) use profile_function;
411
412 macro_rules! profile_scope {
414 ($($arg: tt)*) => {
415 #[cfg(feature = "puffin")]
416 #[cfg(not(target_arch = "wasm32"))] puffin::profile_scope!($($arg)*);
418 };
419 }
420 pub(crate) use profile_scope;
421}
422
423#[allow(unused_imports)]
424pub(crate) use profiling_scopes::*;