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