1use crate::core::Color;
3use crate::graphics::color;
4use crate::graphics::compositor;
5use crate::graphics::error;
6use crate::graphics::{self, Shell, Viewport};
7use crate::settings::{self, Settings};
8use crate::{Engine, Renderer};
9
10pub struct Compositor {
12 instance: wgpu::Instance,
13 adapter: wgpu::Adapter,
14 format: wgpu::TextureFormat,
15 alpha_mode: wgpu::CompositeAlphaMode,
16 engine: Engine,
17 settings: Settings,
18}
19
20#[derive(Debug, Clone, thiserror::Error)]
22pub enum Error {
23 #[error("the surface creation failed: {0}")]
25 SurfaceCreationFailed(#[from] wgpu::CreateSurfaceError),
26 #[error("the surface is not compatible")]
28 IncompatibleSurface,
29 #[error("no adapter was found for the options requested: {0:?}")]
31 NoAdapterFound(String),
32 #[error("no device request succeeded: {0:?}")]
34 RequestDeviceFailed(Vec<(wgpu::Limits, wgpu::RequestDeviceError)>),
35}
36
37impl From<Error> for graphics::Error {
38 fn from(error: Error) -> Self {
39 Self::GraphicsAdapterNotFound {
40 backend: "wgpu",
41 reason: error::Reason::RequestFailed(error.to_string()),
42 }
43 }
44}
45
46impl Compositor {
47 pub async fn request<W: compositor::Window>(
51 settings: Settings,
52 compatible_window: Option<W>,
53 shell: Shell,
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 max_non_sampler_bindings: 2048,
159 ..limits
160 });
161
162 let mut errors = Vec::new();
163
164 for required_limits in limits {
165 let result = adapter
166 .request_device(&wgpu::DeviceDescriptor {
167 label: Some(
168 "iced_wgpu::window::compositor device descriptor",
169 ),
170 required_features: wgpu::Features::empty(),
171 required_limits: required_limits.clone(),
172 memory_hints: wgpu::MemoryHints::MemoryUsage,
173 trace: wgpu::Trace::Off,
174 })
175 .await;
176
177 match result {
178 Ok((device, queue)) => {
179 let engine = Engine::new(
180 &adapter,
181 device,
182 queue,
183 format,
184 settings.antialiasing,
185 shell,
186 );
187
188 return Ok(Compositor {
189 instance,
190 adapter,
191 format,
192 alpha_mode,
193 engine,
194 settings,
195 });
196 }
197 Err(error) => {
198 errors.push((required_limits, error));
199 }
200 }
201 }
202
203 Err(Error::RequestDeviceFailed(errors))
204 }
205}
206
207pub async fn new<W: compositor::Window>(
209 settings: Settings,
210 compatible_window: W,
211 shell: Shell,
212) -> Result<Compositor, Error> {
213 Compositor::request(settings, Some(compatible_window), shell).await
214}
215
216pub fn present(
218 renderer: &mut Renderer,
219 surface: &mut wgpu::Surface<'static>,
220 viewport: &Viewport,
221 background_color: Color,
222 on_pre_present: impl FnOnce(),
223) -> Result<(), compositor::SurfaceError> {
224 match surface.get_current_texture() {
225 Ok(frame) => {
226 let view = &frame
227 .texture
228 .create_view(&wgpu::TextureViewDescriptor::default());
229
230 let _submission = renderer.present(
231 Some(background_color),
232 frame.texture.format(),
233 view,
234 viewport,
235 );
236
237 on_pre_present();
239 frame.present();
240
241 Ok(())
242 }
243 Err(error) => match error {
244 wgpu::SurfaceError::Timeout => {
245 Err(compositor::SurfaceError::Timeout)
246 }
247 wgpu::SurfaceError::Outdated => {
248 Err(compositor::SurfaceError::Outdated)
249 }
250 wgpu::SurfaceError::Lost => Err(compositor::SurfaceError::Lost),
251 wgpu::SurfaceError::OutOfMemory => {
252 Err(compositor::SurfaceError::OutOfMemory)
253 }
254 wgpu::SurfaceError::Other => Err(compositor::SurfaceError::Other),
255 },
256 }
257}
258
259impl graphics::Compositor for Compositor {
260 type Renderer = Renderer;
261 type Surface = wgpu::Surface<'static>;
262
263 async fn with_backend<W: compositor::Window>(
264 settings: graphics::Settings,
265 compatible_window: W,
266 shell: Shell,
267 backend: Option<&str>,
268 ) -> Result<Self, graphics::Error> {
269 match backend {
270 None | Some("wgpu") => {
271 let mut settings = Settings::from(settings);
272
273 if let Some(backends) = wgpu::Backends::from_env() {
274 settings.backends = backends;
275 }
276
277 if let Some(present_mode) = settings::present_mode_from_env() {
278 settings.present_mode = present_mode;
279 }
280
281 Ok(new(settings, compatible_window, shell).await?)
282 }
283 Some(backend) => Err(graphics::Error::GraphicsAdapterNotFound {
284 backend: "wgpu",
285 reason: error::Reason::DidNotMatch {
286 preferred_backend: backend.to_owned(),
287 },
288 }),
289 }
290 }
291
292 fn create_renderer(&self) -> Self::Renderer {
293 Renderer::new(
294 self.engine.clone(),
295 self.settings.default_font,
296 self.settings.default_text_size,
297 )
298 }
299
300 fn create_surface<W: compositor::Window>(
301 &mut self,
302 window: W,
303 width: u32,
304 height: u32,
305 ) -> Self::Surface {
306 let mut surface = self
307 .instance
308 .create_surface(window)
309 .expect("Create surface");
310
311 if width > 0 && height > 0 {
312 self.configure_surface(&mut surface, width, height);
313 }
314
315 surface
316 }
317
318 fn configure_surface(
319 &mut self,
320 surface: &mut Self::Surface,
321 width: u32,
322 height: u32,
323 ) {
324 surface.configure(
325 &self.engine.device,
326 &wgpu::SurfaceConfiguration {
327 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
328 format: self.format,
329 present_mode: self.settings.present_mode,
330 width,
331 height,
332 alpha_mode: self.alpha_mode,
333 view_formats: vec![],
334 desired_maximum_frame_latency: 1,
335 },
336 );
337 }
338
339 fn information(&self) -> compositor::Information {
340 let information = self.adapter.get_info();
341
342 compositor::Information {
343 adapter: information.name,
344 backend: format!("{:?}", information.backend),
345 }
346 }
347
348 fn present(
349 &mut self,
350 renderer: &mut Self::Renderer,
351 surface: &mut Self::Surface,
352 viewport: &Viewport,
353 background_color: Color,
354 on_pre_present: impl FnOnce(),
355 ) -> Result<(), compositor::SurfaceError> {
356 present(
357 renderer,
358 surface,
359 viewport,
360 background_color,
361 on_pre_present,
362 )
363 }
364
365 fn screenshot(
366 &mut self,
367 renderer: &mut Self::Renderer,
368 viewport: &Viewport,
369 background_color: Color,
370 ) -> Vec<u8> {
371 renderer.screenshot(viewport, background_color)
372 }
373}