1use crate::core::Color;
3use crate::graphics::color;
4use crate::graphics::compositor;
5use crate::graphics::error;
6use crate::graphics::{self, Viewport};
7use crate::settings::{self, Settings};
8use crate::{Engine, Renderer};
9
10#[allow(missing_debug_implementations)]
12pub struct Compositor {
13 instance: wgpu::Instance,
14 adapter: wgpu::Adapter,
15 format: wgpu::TextureFormat,
16 alpha_mode: wgpu::CompositeAlphaMode,
17 engine: Engine,
18 settings: Settings,
19}
20
21#[derive(Debug, Clone, thiserror::Error)]
23pub enum Error {
24 #[error("the surface creation failed: {0}")]
26 SurfaceCreationFailed(#[from] wgpu::CreateSurfaceError),
27 #[error("the surface is not compatible")]
29 IncompatibleSurface,
30 #[error("no adapter was found for the options requested: {0:?}")]
32 NoAdapterFound(String),
33 #[error("no device request succeeded: {0:?}")]
35 RequestDeviceFailed(Vec<(wgpu::Limits, wgpu::RequestDeviceError)>),
36}
37
38impl From<Error> for graphics::Error {
39 fn from(error: Error) -> Self {
40 Self::GraphicsAdapterNotFound {
41 backend: "wgpu",
42 reason: error::Reason::RequestFailed(error.to_string()),
43 }
44 }
45}
46
47impl Compositor {
48 pub async fn request<W: compositor::Window>(
52 settings: Settings,
53 compatible_window: Option<W>,
54 ) -> Result<Self, Error> {
55 let instance = wgpu::util::new_instance_with_webgpu_detection(
56 &wgpu::InstanceDescriptor {
57 backends: settings.backends,
58 flags: if cfg!(feature = "strict-assertions") {
59 wgpu::InstanceFlags::debugging()
60 } else {
61 wgpu::InstanceFlags::empty()
62 },
63 ..Default::default()
64 },
65 )
66 .await;
67
68 log::info!("{settings:#?}");
69
70 #[cfg(not(target_arch = "wasm32"))]
71 if log::max_level() >= log::LevelFilter::Info {
72 let available_adapters: Vec<_> = instance
73 .enumerate_adapters(settings.backends)
74 .iter()
75 .map(wgpu::Adapter::get_info)
76 .collect();
77 log::info!("Available adapters: {available_adapters:#?}");
78 }
79
80 #[allow(unsafe_code)]
81 let compatible_surface = compatible_window
82 .and_then(|window| instance.create_surface(window).ok());
83
84 let adapter_options = wgpu::RequestAdapterOptions {
85 power_preference: wgpu::PowerPreference::from_env().unwrap_or(
86 if settings.antialiasing.is_none() {
87 wgpu::PowerPreference::LowPower
88 } else {
89 wgpu::PowerPreference::HighPerformance
90 },
91 ),
92 compatible_surface: compatible_surface.as_ref(),
93 force_fallback_adapter: false,
94 };
95
96 let adapter =
97 instance.request_adapter(&adapter_options).await.map_err(
98 |_error| Error::NoAdapterFound(format!("{adapter_options:?}")),
99 )?;
100
101 log::info!("Selected: {:#?}", adapter.get_info());
102
103 let (format, alpha_mode) = compatible_surface
104 .as_ref()
105 .and_then(|surface| {
106 let capabilities = surface.get_capabilities(&adapter);
107
108 let mut formats = capabilities.formats.iter().copied();
109
110 log::info!("Available formats: {formats:#?}");
111
112 let format = if color::GAMMA_CORRECTION {
113 formats.find(wgpu::TextureFormat::is_srgb)
114 } else {
115 formats.find(|format| !wgpu::TextureFormat::is_srgb(format))
116 };
117
118 let format = format.or_else(|| {
119 log::warn!("No format found!");
120
121 capabilities.formats.first().copied()
122 });
123
124 let alpha_modes = capabilities.alpha_modes;
125
126 log::info!("Available alpha modes: {alpha_modes:#?}");
127
128 let preferred_alpha = if alpha_modes
129 .contains(&wgpu::CompositeAlphaMode::PostMultiplied)
130 {
131 wgpu::CompositeAlphaMode::PostMultiplied
132 } else if alpha_modes
133 .contains(&wgpu::CompositeAlphaMode::PreMultiplied)
134 {
135 wgpu::CompositeAlphaMode::PreMultiplied
136 } else {
137 wgpu::CompositeAlphaMode::Auto
138 };
139
140 format.zip(Some(preferred_alpha))
141 })
142 .ok_or(Error::IncompatibleSurface)?;
143
144 log::info!(
145 "Selected format: {format:?} with alpha mode: {alpha_mode:?}"
146 );
147
148 #[cfg(target_arch = "wasm32")]
149 let limits = [wgpu::Limits::downlevel_webgl2_defaults()
150 .using_resolution(adapter.limits())];
151
152 #[cfg(not(target_arch = "wasm32"))]
153 let limits =
154 [wgpu::Limits::default(), wgpu::Limits::downlevel_defaults()];
155
156 let limits = limits.into_iter().map(|limits| wgpu::Limits {
157 max_bind_groups: 2,
158 ..limits
159 });
160
161 let mut errors = Vec::new();
162
163 for required_limits in limits {
164 let result = adapter
165 .request_device(&wgpu::DeviceDescriptor {
166 label: Some(
167 "iced_wgpu::window::compositor device descriptor",
168 ),
169 required_features: wgpu::Features::empty(),
170 required_limits: required_limits.clone(),
171 memory_hints: wgpu::MemoryHints::MemoryUsage,
172 trace: wgpu::Trace::Off,
173 })
174 .await;
175
176 match result {
177 Ok((device, queue)) => {
178 let engine = Engine::new(
179 &adapter,
180 device,
181 queue,
182 format,
183 settings.antialiasing,
184 );
185
186 return Ok(Compositor {
187 instance,
188 adapter,
189 format,
190 alpha_mode,
191 engine,
192 settings,
193 });
194 }
195 Err(error) => {
196 errors.push((required_limits, error));
197 }
198 }
199 }
200
201 Err(Error::RequestDeviceFailed(errors))
202 }
203}
204
205pub async fn new<W: compositor::Window>(
207 settings: Settings,
208 compatible_window: W,
209) -> Result<Compositor, Error> {
210 Compositor::request(settings, Some(compatible_window)).await
211}
212
213pub fn present(
215 renderer: &mut Renderer,
216 surface: &mut wgpu::Surface<'static>,
217 viewport: &Viewport,
218 background_color: Color,
219 on_pre_present: impl FnOnce(),
220) -> Result<(), compositor::SurfaceError> {
221 match surface.get_current_texture() {
222 Ok(frame) => {
223 let view = &frame
224 .texture
225 .create_view(&wgpu::TextureViewDescriptor::default());
226
227 let _submission = renderer.present(
228 Some(background_color),
229 frame.texture.format(),
230 view,
231 viewport,
232 );
233
234 on_pre_present();
236 frame.present();
237
238 Ok(())
239 }
240 Err(error) => match error {
241 wgpu::SurfaceError::Timeout => {
242 Err(compositor::SurfaceError::Timeout)
243 }
244 wgpu::SurfaceError::Outdated => {
245 Err(compositor::SurfaceError::Outdated)
246 }
247 wgpu::SurfaceError::Lost => Err(compositor::SurfaceError::Lost),
248 wgpu::SurfaceError::OutOfMemory => {
249 Err(compositor::SurfaceError::OutOfMemory)
250 }
251 wgpu::SurfaceError::Other => Err(compositor::SurfaceError::Other),
252 },
253 }
254}
255
256impl graphics::Compositor for Compositor {
257 type Renderer = Renderer;
258 type Surface = wgpu::Surface<'static>;
259
260 async fn with_backend<W: compositor::Window>(
261 settings: graphics::Settings,
262 compatible_window: W,
263 backend: Option<&str>,
264 ) -> Result<Self, graphics::Error> {
265 match backend {
266 None | Some("wgpu") => {
267 let mut settings = Settings::from(settings);
268
269 if let Some(backends) = wgpu::Backends::from_env() {
270 settings.backends = backends;
271 }
272
273 if let Some(present_mode) = settings::present_mode_from_env() {
274 settings.present_mode = present_mode;
275 }
276
277 Ok(new(settings, compatible_window).await?)
278 }
279 Some(backend) => Err(graphics::Error::GraphicsAdapterNotFound {
280 backend: "wgpu",
281 reason: error::Reason::DidNotMatch {
282 preferred_backend: backend.to_owned(),
283 },
284 }),
285 }
286 }
287
288 fn create_renderer(&self) -> Self::Renderer {
289 Renderer::new(
290 self.engine.clone(),
291 self.settings.default_font,
292 self.settings.default_text_size,
293 )
294 }
295
296 fn create_surface<W: compositor::Window>(
297 &mut self,
298 window: W,
299 width: u32,
300 height: u32,
301 ) -> Self::Surface {
302 let mut surface = self
303 .instance
304 .create_surface(window)
305 .expect("Create surface");
306
307 if width > 0 && height > 0 {
308 self.configure_surface(&mut surface, width, height);
309 }
310
311 surface
312 }
313
314 fn configure_surface(
315 &mut self,
316 surface: &mut Self::Surface,
317 width: u32,
318 height: u32,
319 ) {
320 surface.configure(
321 &self.engine.device,
322 &wgpu::SurfaceConfiguration {
323 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
324 format: self.format,
325 present_mode: self.settings.present_mode,
326 width,
327 height,
328 alpha_mode: self.alpha_mode,
329 view_formats: vec![],
330 desired_maximum_frame_latency: 1,
331 },
332 );
333 }
334
335 fn fetch_information(&self) -> compositor::Information {
336 let information = self.adapter.get_info();
337
338 compositor::Information {
339 adapter: information.name,
340 backend: format!("{:?}", information.backend),
341 }
342 }
343
344 fn present(
345 &mut self,
346 renderer: &mut Self::Renderer,
347 surface: &mut Self::Surface,
348 viewport: &Viewport,
349 background_color: Color,
350 on_pre_present: impl FnOnce(),
351 ) -> Result<(), compositor::SurfaceError> {
352 present(
353 renderer,
354 surface,
355 viewport,
356 background_color,
357 on_pre_present,
358 )
359 }
360
361 fn screenshot(
362 &mut self,
363 renderer: &mut Self::Renderer,
364 viewport: &Viewport,
365 background_color: Color,
366 ) -> Vec<u8> {
367 renderer.screenshot(viewport, background_color)
368 }
369}