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()
86 .unwrap_or(wgpu::PowerPreference::HighPerformance),
87 compatible_surface: compatible_surface.as_ref(),
88 force_fallback_adapter: false,
89 };
90
91 let adapter =
92 instance.request_adapter(&adapter_options).await.map_err(
93 |_error| Error::NoAdapterFound(format!("{adapter_options:?}")),
94 )?;
95
96 log::info!("Selected: {:#?}", adapter.get_info());
97
98 let (format, alpha_mode) = compatible_surface
99 .as_ref()
100 .and_then(|surface| {
101 let capabilities = surface.get_capabilities(&adapter);
102
103 let formats = capabilities.formats.iter().copied();
104
105 log::info!("Available formats: {formats:#?}");
106
107 let mut formats = formats.filter(|format| {
108 format.required_features() == wgpu::Features::empty()
109 });
110
111 let format = if color::GAMMA_CORRECTION {
112 formats.find(wgpu::TextureFormat::is_srgb)
113 } else {
114 formats.find(|format| !wgpu::TextureFormat::is_srgb(format))
115 };
116
117 let format = format.or_else(|| {
118 log::warn!("No format found!");
119
120 capabilities.formats.first().copied()
121 });
122
123 let alpha_modes = capabilities.alpha_modes;
124
125 log::info!("Available alpha modes: {alpha_modes:#?}");
126
127 let preferred_alpha = if alpha_modes
128 .contains(&wgpu::CompositeAlphaMode::PostMultiplied)
129 {
130 wgpu::CompositeAlphaMode::PostMultiplied
131 } else if alpha_modes
132 .contains(&wgpu::CompositeAlphaMode::PreMultiplied)
133 {
134 wgpu::CompositeAlphaMode::PreMultiplied
135 } else {
136 wgpu::CompositeAlphaMode::Auto
137 };
138
139 format.zip(Some(preferred_alpha))
140 })
141 .ok_or(Error::IncompatibleSurface)?;
142
143 log::info!(
144 "Selected format: {format:?} with alpha mode: {alpha_mode:?}"
145 );
146
147 #[cfg(target_arch = "wasm32")]
148 let limits = [wgpu::Limits::downlevel_webgl2_defaults()
149 .using_resolution(adapter.limits())];
150
151 #[cfg(not(target_arch = "wasm32"))]
152 let limits =
153 [wgpu::Limits::default(), wgpu::Limits::downlevel_defaults()];
154
155 let limits = limits.into_iter().map(|limits| wgpu::Limits {
156 max_bind_groups: 2,
157 max_non_sampler_bindings: 2048,
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 experimental_features: wgpu::ExperimentalFeatures::disabled(
174 ),
175 })
176 .await;
177
178 match result {
179 Ok((device, queue)) => {
180 let engine = Engine::new(
181 &adapter,
182 device,
183 queue,
184 format,
185 settings.antialiasing,
186 shell,
187 );
188
189 return Ok(Compositor {
190 instance,
191 adapter,
192 format,
193 alpha_mode,
194 engine,
195 settings,
196 });
197 }
198 Err(error) => {
199 errors.push((required_limits, error));
200 }
201 }
202 }
203
204 Err(Error::RequestDeviceFailed(errors))
205 }
206}
207
208pub async fn new<W: compositor::Window>(
210 settings: Settings,
211 compatible_window: W,
212 shell: Shell,
213) -> Result<Compositor, Error> {
214 Compositor::request(settings, Some(compatible_window), shell).await
215}
216
217pub fn present(
219 renderer: &mut Renderer,
220 surface: &mut wgpu::Surface<'static>,
221 viewport: &Viewport,
222 background_color: Color,
223 on_pre_present: impl FnOnce(),
224) -> Result<(), compositor::SurfaceError> {
225 match surface.get_current_texture() {
226 Ok(frame) => {
227 let view = &frame
228 .texture
229 .create_view(&wgpu::TextureViewDescriptor::default());
230
231 let _submission = renderer.present(
232 Some(background_color),
233 frame.texture.format(),
234 view,
235 viewport,
236 );
237
238 on_pre_present();
240 frame.present();
241
242 Ok(())
243 }
244 Err(error) => match error {
245 wgpu::SurfaceError::Timeout => {
246 Err(compositor::SurfaceError::Timeout)
247 }
248 wgpu::SurfaceError::Outdated => {
249 Err(compositor::SurfaceError::Outdated)
250 }
251 wgpu::SurfaceError::Lost => Err(compositor::SurfaceError::Lost),
252 wgpu::SurfaceError::OutOfMemory => {
253 Err(compositor::SurfaceError::OutOfMemory)
254 }
255 wgpu::SurfaceError::Other => Err(compositor::SurfaceError::Other),
256 },
257 }
258}
259
260impl graphics::Compositor for Compositor {
261 type Renderer = Renderer;
262 type Surface = wgpu::Surface<'static>;
263
264 async fn with_backend(
265 settings: graphics::Settings,
266 _display: impl compositor::Display,
267 compatible_window: impl compositor::Window,
268 shell: Shell,
269 backend: Option<&str>,
270 ) -> Result<Self, graphics::Error> {
271 match backend {
272 None | Some("wgpu") => {
273 let mut settings = Settings::from(settings);
274
275 if let Some(backends) = wgpu::Backends::from_env() {
276 settings.backends = backends;
277 }
278
279 if let Some(present_mode) = settings::present_mode_from_env() {
280 settings.present_mode = present_mode;
281 }
282
283 Ok(new(settings, compatible_window, shell).await?)
284 }
285 Some(backend) => Err(graphics::Error::GraphicsAdapterNotFound {
286 backend: "wgpu",
287 reason: error::Reason::DidNotMatch {
288 preferred_backend: backend.to_owned(),
289 },
290 }),
291 }
292 }
293
294 fn create_renderer(&self) -> Self::Renderer {
295 Renderer::new(
296 self.engine.clone(),
297 self.settings.default_font,
298 self.settings.default_text_size,
299 )
300 }
301
302 fn create_surface<W: compositor::Window>(
303 &mut self,
304 window: W,
305 width: u32,
306 height: u32,
307 ) -> Self::Surface {
308 let mut surface = self
309 .instance
310 .create_surface(window)
311 .expect("Create surface");
312
313 if width > 0 && height > 0 {
314 self.configure_surface(&mut surface, width, height);
315 }
316
317 surface
318 }
319
320 fn configure_surface(
321 &mut self,
322 surface: &mut Self::Surface,
323 width: u32,
324 height: u32,
325 ) {
326 surface.configure(
327 &self.engine.device,
328 &wgpu::SurfaceConfiguration {
329 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
330 format: self.format,
331 present_mode: self.settings.present_mode,
332 width,
333 height,
334 alpha_mode: self.alpha_mode,
335 view_formats: vec![],
336 desired_maximum_frame_latency: 1,
337 },
338 );
339 }
340
341 fn information(&self) -> compositor::Information {
342 let information = self.adapter.get_info();
343
344 compositor::Information {
345 adapter: information.name,
346 backend: format!("{:?}", information.backend),
347 }
348 }
349
350 fn present(
351 &mut self,
352 renderer: &mut Self::Renderer,
353 surface: &mut Self::Surface,
354 viewport: &Viewport,
355 background_color: Color,
356 on_pre_present: impl FnOnce(),
357 ) -> Result<(), compositor::SurfaceError> {
358 present(
359 renderer,
360 surface,
361 viewport,
362 background_color,
363 on_pre_present,
364 )
365 }
366
367 fn screenshot(
368 &mut self,
369 renderer: &mut Self::Renderer,
370 viewport: &Viewport,
371 background_color: Color,
372 ) -> Vec<u8> {
373 renderer.screenshot(viewport, background_color)
374 }
375}