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
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 ) -> Result<Self, Error> {
54 let instance = wgpu::util::new_instance_with_webgpu_detection(
55 &wgpu::InstanceDescriptor {
56 backends: settings.backends,
57 flags: if cfg!(feature = "strict-assertions") {
58 wgpu::InstanceFlags::debugging()
59 } else {
60 wgpu::InstanceFlags::empty()
61 },
62 ..Default::default()
63 },
64 )
65 .await;
66
67 log::info!("{settings:#?}");
68
69 #[cfg(not(target_arch = "wasm32"))]
70 if log::max_level() >= log::LevelFilter::Info {
71 let available_adapters: Vec<_> = instance
72 .enumerate_adapters(settings.backends)
73 .iter()
74 .map(wgpu::Adapter::get_info)
75 .collect();
76 log::info!("Available adapters: {available_adapters:#?}");
77 }
78
79 #[allow(unsafe_code)]
80 let compatible_surface = compatible_window
81 .and_then(|window| instance.create_surface(window).ok());
82
83 let adapter_options = wgpu::RequestAdapterOptions {
84 power_preference: wgpu::PowerPreference::from_env().unwrap_or(
85 if settings.antialiasing.is_none() {
86 wgpu::PowerPreference::LowPower
87 } else {
88 wgpu::PowerPreference::HighPerformance
89 },
90 ),
91 compatible_surface: compatible_surface.as_ref(),
92 force_fallback_adapter: false,
93 };
94
95 let adapter =
96 instance.request_adapter(&adapter_options).await.map_err(
97 |_error| Error::NoAdapterFound(format!("{adapter_options:?}")),
98 )?;
99
100 log::info!("Selected: {:#?}", adapter.get_info());
101
102 let (format, alpha_mode) = compatible_surface
103 .as_ref()
104 .and_then(|surface| {
105 let capabilities = surface.get_capabilities(&adapter);
106
107 let mut formats = capabilities.formats.iter().copied();
108
109 log::info!("Available formats: {formats:#?}");
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 ..limits
158 });
159
160 let mut errors = Vec::new();
161
162 for required_limits in limits {
163 let result = adapter
164 .request_device(&wgpu::DeviceDescriptor {
165 label: Some(
166 "iced_wgpu::window::compositor device descriptor",
167 ),
168 required_features: wgpu::Features::empty(),
169 required_limits: required_limits.clone(),
170 memory_hints: wgpu::MemoryHints::MemoryUsage,
171 trace: wgpu::Trace::Off,
172 })
173 .await;
174
175 match result {
176 Ok((device, queue)) => {
177 let engine = Engine::new(
178 &adapter,
179 device,
180 queue,
181 format,
182 settings.antialiasing,
183 );
184
185 return Ok(Compositor {
186 instance,
187 adapter,
188 format,
189 alpha_mode,
190 engine,
191 settings,
192 });
193 }
194 Err(error) => {
195 errors.push((required_limits, error));
196 }
197 }
198 }
199
200 Err(Error::RequestDeviceFailed(errors))
201 }
202}
203
204pub async fn new<W: compositor::Window>(
206 settings: Settings,
207 compatible_window: W,
208) -> Result<Compositor, Error> {
209 Compositor::request(settings, Some(compatible_window)).await
210}
211
212pub fn present(
214 renderer: &mut Renderer,
215 surface: &mut wgpu::Surface<'static>,
216 viewport: &Viewport,
217 background_color: Color,
218 on_pre_present: impl FnOnce(),
219) -> Result<(), compositor::SurfaceError> {
220 match surface.get_current_texture() {
221 Ok(frame) => {
222 let view = &frame
223 .texture
224 .create_view(&wgpu::TextureViewDescriptor::default());
225
226 let _submission = renderer.present(
227 Some(background_color),
228 frame.texture.format(),
229 view,
230 viewport,
231 );
232
233 on_pre_present();
235 frame.present();
236
237 Ok(())
238 }
239 Err(error) => match error {
240 wgpu::SurfaceError::Timeout => {
241 Err(compositor::SurfaceError::Timeout)
242 }
243 wgpu::SurfaceError::Outdated => {
244 Err(compositor::SurfaceError::Outdated)
245 }
246 wgpu::SurfaceError::Lost => Err(compositor::SurfaceError::Lost),
247 wgpu::SurfaceError::OutOfMemory => {
248 Err(compositor::SurfaceError::OutOfMemory)
249 }
250 wgpu::SurfaceError::Other => Err(compositor::SurfaceError::Other),
251 },
252 }
253}
254
255impl graphics::Compositor for Compositor {
256 type Renderer = Renderer;
257 type Surface = wgpu::Surface<'static>;
258
259 async fn with_backend<W: compositor::Window>(
260 settings: graphics::Settings,
261 compatible_window: W,
262 backend: Option<&str>,
263 ) -> Result<Self, graphics::Error> {
264 match backend {
265 None | Some("wgpu") => {
266 let mut settings = Settings::from(settings);
267
268 if let Some(backends) = wgpu::Backends::from_env() {
269 settings.backends = backends;
270 }
271
272 if let Some(present_mode) = settings::present_mode_from_env() {
273 settings.present_mode = present_mode;
274 }
275
276 Ok(new(settings, compatible_window).await?)
277 }
278 Some(backend) => Err(graphics::Error::GraphicsAdapterNotFound {
279 backend: "wgpu",
280 reason: error::Reason::DidNotMatch {
281 preferred_backend: backend.to_owned(),
282 },
283 }),
284 }
285 }
286
287 fn create_renderer(&self) -> Self::Renderer {
288 Renderer::new(
289 self.engine.clone(),
290 self.settings.default_font,
291 self.settings.default_text_size,
292 )
293 }
294
295 fn create_surface<W: compositor::Window>(
296 &mut self,
297 window: W,
298 width: u32,
299 height: u32,
300 ) -> Self::Surface {
301 let mut surface = self
302 .instance
303 .create_surface(window)
304 .expect("Create surface");
305
306 if width > 0 && height > 0 {
307 self.configure_surface(&mut surface, width, height);
308 }
309
310 surface
311 }
312
313 fn configure_surface(
314 &mut self,
315 surface: &mut Self::Surface,
316 width: u32,
317 height: u32,
318 ) {
319 surface.configure(
320 &self.engine.device,
321 &wgpu::SurfaceConfiguration {
322 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
323 format: self.format,
324 present_mode: self.settings.present_mode,
325 width,
326 height,
327 alpha_mode: self.alpha_mode,
328 view_formats: vec![],
329 desired_maximum_frame_latency: 1,
330 },
331 );
332 }
333
334 fn information(&self) -> compositor::Information {
335 let information = self.adapter.get_info();
336
337 compositor::Information {
338 adapter: information.name,
339 backend: format!("{:?}", information.backend),
340 }
341 }
342
343 fn present(
344 &mut self,
345 renderer: &mut Self::Renderer,
346 surface: &mut Self::Surface,
347 viewport: &Viewport,
348 background_color: Color,
349 on_pre_present: impl FnOnce(),
350 ) -> Result<(), compositor::SurfaceError> {
351 present(
352 renderer,
353 surface,
354 viewport,
355 background_color,
356 on_pre_present,
357 )
358 }
359
360 fn screenshot(
361 &mut self,
362 renderer: &mut Self::Renderer,
363 viewport: &Viewport,
364 background_color: Color,
365 ) -> Vec<u8> {
366 renderer.screenshot(viewport, background_color)
367 }
368}