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 = instance
97 .request_adapter(&adapter_options)
98 .await
99 .ok_or(Error::NoAdapterFound(format!("{:?}", adapter_options)))?;
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(
166 &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 },
174 None,
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 );
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) -> Result<Compositor, Error> {
212 Compositor::request(settings, Some(compatible_window)).await
213}
214
215pub fn present(
217 renderer: &mut Renderer,
218 surface: &mut wgpu::Surface<'static>,
219 viewport: &Viewport,
220 background_color: Color,
221 on_pre_present: impl FnOnce(),
222) -> Result<(), compositor::SurfaceError> {
223 match surface.get_current_texture() {
224 Ok(frame) => {
225 let view = &frame
226 .texture
227 .create_view(&wgpu::TextureViewDescriptor::default());
228
229 let _submission = renderer.present(
230 Some(background_color),
231 frame.texture.format(),
232 view,
233 viewport,
234 );
235
236 on_pre_present();
238 frame.present();
239
240 Ok(())
241 }
242 Err(error) => match error {
243 wgpu::SurfaceError::Timeout => {
244 Err(compositor::SurfaceError::Timeout)
245 }
246 wgpu::SurfaceError::Outdated => {
247 Err(compositor::SurfaceError::Outdated)
248 }
249 wgpu::SurfaceError::Lost => Err(compositor::SurfaceError::Lost),
250 wgpu::SurfaceError::OutOfMemory => {
251 Err(compositor::SurfaceError::OutOfMemory)
252 }
253 wgpu::SurfaceError::Other => Err(compositor::SurfaceError::Other),
254 },
255 }
256}
257
258impl graphics::Compositor for Compositor {
259 type Renderer = Renderer;
260 type Surface = wgpu::Surface<'static>;
261
262 async fn with_backend<W: compositor::Window>(
263 settings: graphics::Settings,
264 compatible_window: W,
265 backend: Option<&str>,
266 ) -> Result<Self, graphics::Error> {
267 match backend {
268 None | Some("wgpu") => {
269 let mut settings = Settings::from(settings);
270
271 if let Some(backends) = wgpu::Backends::from_env() {
272 settings.backends = backends;
273 }
274
275 if let Some(present_mode) = settings::present_mode_from_env() {
276 settings.present_mode = present_mode;
277 }
278
279 Ok(new(settings, compatible_window).await?)
280 }
281 Some(backend) => Err(graphics::Error::GraphicsAdapterNotFound {
282 backend: "wgpu",
283 reason: error::Reason::DidNotMatch {
284 preferred_backend: backend.to_owned(),
285 },
286 }),
287 }
288 }
289
290 fn create_renderer(&self) -> Self::Renderer {
291 Renderer::new(
292 self.engine.clone(),
293 self.settings.default_font,
294 self.settings.default_text_size,
295 )
296 }
297
298 fn create_surface<W: compositor::Window>(
299 &mut self,
300 window: W,
301 width: u32,
302 height: u32,
303 ) -> Self::Surface {
304 let mut surface = self
305 .instance
306 .create_surface(window)
307 .expect("Create surface");
308
309 if width > 0 && height > 0 {
310 self.configure_surface(&mut surface, width, height);
311 }
312
313 surface
314 }
315
316 fn configure_surface(
317 &mut self,
318 surface: &mut Self::Surface,
319 width: u32,
320 height: u32,
321 ) {
322 surface.configure(
323 &self.engine.device,
324 &wgpu::SurfaceConfiguration {
325 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
326 format: self.format,
327 present_mode: self.settings.present_mode,
328 width,
329 height,
330 alpha_mode: self.alpha_mode,
331 view_formats: vec![],
332 desired_maximum_frame_latency: 1,
333 },
334 );
335 }
336
337 fn fetch_information(&self) -> compositor::Information {
338 let information = self.adapter.get_info();
339
340 compositor::Information {
341 adapter: information.name,
342 backend: format!("{:?}", information.backend),
343 }
344 }
345
346 fn present(
347 &mut self,
348 renderer: &mut Self::Renderer,
349 surface: &mut Self::Surface,
350 viewport: &Viewport,
351 background_color: Color,
352 on_pre_present: impl FnOnce(),
353 ) -> Result<(), compositor::SurfaceError> {
354 present(
355 renderer,
356 surface,
357 viewport,
358 background_color,
359 on_pre_present,
360 )
361 }
362
363 fn screenshot(
364 &mut self,
365 renderer: &mut Self::Renderer,
366 viewport: &Viewport,
367 background_color: Color,
368 ) -> Vec<u8> {
369 renderer.screenshot(viewport, background_color)
370 }
371}