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