wgpu_core/
present.rs

1/*! Presentation.
2
3## Lifecycle
4
5Whenever a submission detects the use of any surface texture, it adds it to the device
6tracker for the duration of the submission (temporarily, while recording).
7It's added with `UNINITIALIZED` state and transitioned into `empty()` state.
8When this texture is presented, we remove it from the device tracker as well as
9extract it from the hub.
10!*/
11
12use std::{mem::ManuallyDrop, sync::Arc};
13
14#[cfg(feature = "trace")]
15use crate::device::trace::Action;
16use crate::{
17    conv,
18    device::{Device, DeviceError, MissingDownlevelFlags, WaitIdleError},
19    global::Global,
20    hal_label, id,
21    instance::Surface,
22    resource,
23};
24
25use thiserror::Error;
26use wgt::SurfaceStatus as Status;
27
28const FRAME_TIMEOUT_MS: u32 = 1000;
29
30#[derive(Debug)]
31pub(crate) struct Presentation {
32    pub(crate) device: Arc<Device>,
33    pub(crate) config: wgt::SurfaceConfiguration<Vec<wgt::TextureFormat>>,
34    pub(crate) acquired_texture: Option<Arc<resource::Texture>>,
35}
36
37#[derive(Clone, Debug, Error)]
38#[non_exhaustive]
39pub enum SurfaceError {
40    #[error("Surface is invalid")]
41    Invalid,
42    #[error("Surface is not configured for presentation")]
43    NotConfigured,
44    #[error(transparent)]
45    Device(#[from] DeviceError),
46    #[error("Surface image is already acquired")]
47    AlreadyAcquired,
48    #[error("Texture has been destroyed")]
49    TextureDestroyed,
50    #[error("Acquired frame is still referenced")]
51    StillReferenced,
52}
53
54#[derive(Clone, Debug, Error)]
55#[non_exhaustive]
56pub enum ConfigureSurfaceError {
57    #[error(transparent)]
58    Device(#[from] DeviceError),
59    #[error("Invalid surface")]
60    InvalidSurface,
61    #[error("The view format {0:?} is not compatible with texture format {1:?}, only changing srgb-ness is allowed.")]
62    InvalidViewFormat(wgt::TextureFormat, wgt::TextureFormat),
63    #[error(transparent)]
64    MissingDownlevelFlags(#[from] MissingDownlevelFlags),
65    #[error("`SurfaceOutput` must be dropped before a new `Surface` is made")]
66    PreviousOutputExists,
67    #[error("Both `Surface` width and height must be non-zero. Wait to recreate the `Surface` until the window has non-zero area.")]
68    ZeroArea,
69    #[error("`Surface` width and height must be within the maximum supported texture size. Requested was ({width}, {height}), maximum extent for either dimension is {max_texture_dimension_2d}.")]
70    TooLarge {
71        width: u32,
72        height: u32,
73        max_texture_dimension_2d: u32,
74    },
75    #[error("Surface does not support the adapter's queue family")]
76    UnsupportedQueueFamily,
77    #[error("Requested format {requested:?} is not in list of supported formats: {available:?}")]
78    UnsupportedFormat {
79        requested: wgt::TextureFormat,
80        available: Vec<wgt::TextureFormat>,
81    },
82    #[error("Requested present mode {requested:?} is not in the list of supported present modes: {available:?}")]
83    UnsupportedPresentMode {
84        requested: wgt::PresentMode,
85        available: Vec<wgt::PresentMode>,
86    },
87    #[error("Requested alpha mode {requested:?} is not in the list of supported alpha modes: {available:?}")]
88    UnsupportedAlphaMode {
89        requested: wgt::CompositeAlphaMode,
90        available: Vec<wgt::CompositeAlphaMode>,
91    },
92    #[error("Requested usage {requested:?} is not in the list of supported usages: {available:?}")]
93    UnsupportedUsage {
94        requested: hal::TextureUses,
95        available: hal::TextureUses,
96    },
97    #[error("Gpu got stuck :(")]
98    StuckGpu,
99}
100
101impl From<WaitIdleError> for ConfigureSurfaceError {
102    fn from(e: WaitIdleError) -> Self {
103        match e {
104            WaitIdleError::Device(d) => ConfigureSurfaceError::Device(d),
105            WaitIdleError::WrongSubmissionIndex(..) => unreachable!(),
106            WaitIdleError::StuckGpu => ConfigureSurfaceError::StuckGpu,
107        }
108    }
109}
110
111#[derive(Debug)]
112pub struct ResolvedSurfaceOutput {
113    pub status: Status,
114    pub texture: Option<Arc<resource::Texture>>,
115}
116
117#[repr(C)]
118#[derive(Debug)]
119pub struct SurfaceOutput {
120    pub status: Status,
121    pub texture_id: Option<id::TextureId>,
122}
123
124impl Surface {
125    pub fn get_current_texture(&self) -> Result<ResolvedSurfaceOutput, SurfaceError> {
126        profiling::scope!("Surface::get_current_texture");
127
128        let (device, config) = if let Some(ref present) = *self.presentation.lock() {
129            present.device.check_is_valid()?;
130            (present.device.clone(), present.config.clone())
131        } else {
132            return Err(SurfaceError::NotConfigured);
133        };
134
135        let fence = device.fence.read();
136
137        let suf = self.raw(device.backend()).unwrap();
138        let (texture, status) = match unsafe {
139            suf.acquire_texture(
140                Some(std::time::Duration::from_millis(FRAME_TIMEOUT_MS as u64)),
141                fence.as_ref(),
142            )
143        } {
144            Ok(Some(ast)) => {
145                drop(fence);
146
147                let texture_desc = wgt::TextureDescriptor {
148                    label: Some(std::borrow::Cow::Borrowed("<Surface Texture>")),
149                    size: wgt::Extent3d {
150                        width: config.width,
151                        height: config.height,
152                        depth_or_array_layers: 1,
153                    },
154                    sample_count: 1,
155                    mip_level_count: 1,
156                    format: config.format,
157                    dimension: wgt::TextureDimension::D2,
158                    usage: config.usage,
159                    view_formats: config.view_formats,
160                };
161                let hal_usage = conv::map_texture_usage(config.usage, config.format.into());
162                let format_features = wgt::TextureFormatFeatures {
163                    allowed_usages: wgt::TextureUsages::RENDER_ATTACHMENT,
164                    flags: wgt::TextureFormatFeatureFlags::MULTISAMPLE_X4
165                        | wgt::TextureFormatFeatureFlags::MULTISAMPLE_RESOLVE,
166                };
167                let clear_view_desc = hal::TextureViewDescriptor {
168                    label: hal_label(
169                        Some("(wgpu internal) clear surface texture view"),
170                        device.instance_flags,
171                    ),
172                    format: config.format,
173                    dimension: wgt::TextureViewDimension::D2,
174                    usage: hal::TextureUses::COLOR_TARGET,
175                    range: wgt::ImageSubresourceRange::default(),
176                };
177                let clear_view = unsafe {
178                    device
179                        .raw()
180                        .create_texture_view(ast.texture.as_ref().borrow(), &clear_view_desc)
181                }
182                .map_err(|e| device.handle_hal_error(e))?;
183
184                let mut presentation = self.presentation.lock();
185                let present = presentation.as_mut().unwrap();
186                let texture = resource::Texture::new(
187                    &device,
188                    resource::TextureInner::Surface { raw: ast.texture },
189                    hal_usage,
190                    &texture_desc,
191                    format_features,
192                    resource::TextureClearMode::Surface {
193                        clear_view: ManuallyDrop::new(clear_view),
194                    },
195                    true,
196                );
197
198                let texture = Arc::new(texture);
199
200                device
201                    .trackers
202                    .lock()
203                    .textures
204                    .insert_single(&texture, hal::TextureUses::UNINITIALIZED);
205
206                if present.acquired_texture.is_some() {
207                    return Err(SurfaceError::AlreadyAcquired);
208                }
209                present.acquired_texture = Some(texture.clone());
210
211                let status = if ast.suboptimal {
212                    Status::Suboptimal
213                } else {
214                    Status::Good
215                };
216                (Some(texture), status)
217            }
218            Ok(None) => (None, Status::Timeout),
219            Err(err) => (
220                None,
221                match err {
222                    hal::SurfaceError::Lost => Status::Lost,
223                    hal::SurfaceError::Device(err) => {
224                        return Err(device.handle_hal_error(err).into());
225                    }
226                    hal::SurfaceError::Outdated => Status::Outdated,
227                    hal::SurfaceError::Other(msg) => {
228                        log::error!("acquire error: {}", msg);
229                        Status::Lost
230                    }
231                },
232            ),
233        };
234
235        Ok(ResolvedSurfaceOutput { status, texture })
236    }
237
238    pub fn present(&self) -> Result<Status, SurfaceError> {
239        profiling::scope!("Surface::present");
240
241        let mut presentation = self.presentation.lock();
242        let present = match presentation.as_mut() {
243            Some(present) => present,
244            None => return Err(SurfaceError::NotConfigured),
245        };
246
247        let device = &present.device;
248
249        device.check_is_valid()?;
250        let queue = device.get_queue().unwrap();
251
252        let texture = present
253            .acquired_texture
254            .take()
255            .ok_or(SurfaceError::AlreadyAcquired)?;
256
257        let result = match texture.inner.snatch(&mut device.snatchable_lock.write()) {
258            None => return Err(SurfaceError::TextureDestroyed),
259            Some(resource::TextureInner::Surface { raw }) => {
260                let raw_surface = self.raw(device.backend()).unwrap();
261                let raw_queue = queue.raw();
262                unsafe { raw_queue.present(raw_surface, raw) }
263            }
264            _ => unreachable!(),
265        };
266
267        match result {
268            Ok(()) => Ok(Status::Good),
269            Err(err) => match err {
270                hal::SurfaceError::Lost => Ok(Status::Lost),
271                hal::SurfaceError::Device(err) => {
272                    Err(SurfaceError::from(device.handle_hal_error(err)))
273                }
274                hal::SurfaceError::Outdated => Ok(Status::Outdated),
275                hal::SurfaceError::Other(msg) => {
276                    log::error!("acquire error: {}", msg);
277                    Err(SurfaceError::Invalid)
278                }
279            },
280        }
281    }
282
283    pub fn discard(&self) -> Result<(), SurfaceError> {
284        profiling::scope!("Surface::discard");
285
286        let mut presentation = self.presentation.lock();
287        let present = match presentation.as_mut() {
288            Some(present) => present,
289            None => return Err(SurfaceError::NotConfigured),
290        };
291
292        let device = &present.device;
293
294        device.check_is_valid()?;
295
296        let texture = present
297            .acquired_texture
298            .take()
299            .ok_or(SurfaceError::AlreadyAcquired)?;
300
301        match texture.inner.snatch(&mut device.snatchable_lock.write()) {
302            None => return Err(SurfaceError::TextureDestroyed),
303            Some(resource::TextureInner::Surface { raw }) => {
304                let raw_surface = self.raw(device.backend()).unwrap();
305                unsafe { raw_surface.discard_texture(raw) };
306            }
307            _ => unreachable!(),
308        }
309
310        Ok(())
311    }
312}
313
314impl Global {
315    pub fn surface_get_current_texture(
316        &self,
317        surface_id: id::SurfaceId,
318        texture_id_in: Option<id::TextureId>,
319    ) -> Result<SurfaceOutput, SurfaceError> {
320        let surface = self.surfaces.get(surface_id);
321
322        let fid = self.hub.textures.prepare(texture_id_in);
323
324        #[cfg(feature = "trace")]
325        if let Some(present) = surface.presentation.lock().as_ref() {
326            if let Some(ref mut trace) = *present.device.trace.lock() {
327                trace.add(Action::GetSurfaceTexture {
328                    id: fid.id(),
329                    parent_id: surface_id,
330                });
331            }
332        }
333
334        let output = surface.get_current_texture()?;
335
336        let status = output.status;
337        let texture_id = output
338            .texture
339            .map(|texture| fid.assign(resource::Fallible::Valid(texture)));
340
341        Ok(SurfaceOutput { status, texture_id })
342    }
343
344    pub fn surface_present(&self, surface_id: id::SurfaceId) -> Result<Status, SurfaceError> {
345        let surface = self.surfaces.get(surface_id);
346
347        #[cfg(feature = "trace")]
348        if let Some(present) = surface.presentation.lock().as_ref() {
349            if let Some(ref mut trace) = *present.device.trace.lock() {
350                trace.add(Action::Present(surface_id));
351            }
352        }
353
354        surface.present()
355    }
356
357    pub fn surface_texture_discard(&self, surface_id: id::SurfaceId) -> Result<(), SurfaceError> {
358        let surface = self.surfaces.get(surface_id);
359
360        #[cfg(feature = "trace")]
361        if let Some(present) = surface.presentation.lock().as_ref() {
362            if let Some(ref mut trace) = *present.device.trace.lock() {
363                trace.add(Action::DiscardSurfaceTexture(surface_id));
364            }
365        }
366
367        surface.discard()
368    }
369}