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 alloc::{sync::Arc, vec::Vec};
13use core::mem::ManuallyDrop;
14
15#[cfg(feature = "trace")]
16use crate::device::trace::Action;
17use crate::{
18    conv,
19    device::{Device, DeviceError, MissingDownlevelFlags, WaitIdleError},
20    global::Global,
21    hal_label, id,
22    instance::Surface,
23    resource,
24};
25
26use thiserror::Error;
27use wgt::SurfaceStatus as Status;
28
29const FRAME_TIMEOUT_MS: u32 = 1000;
30
31#[derive(Debug)]
32pub(crate) struct Presentation {
33    pub(crate) device: Arc<Device>,
34    pub(crate) config: wgt::SurfaceConfiguration<Vec<wgt::TextureFormat>>,
35    pub(crate) acquired_texture: Option<Arc<resource::Texture>>,
36}
37
38#[derive(Clone, Debug, Error)]
39#[non_exhaustive]
40pub enum SurfaceError {
41    #[error("Surface is invalid")]
42    Invalid,
43    #[error("Surface is not configured for presentation")]
44    NotConfigured,
45    #[error(transparent)]
46    Device(#[from] DeviceError),
47    #[error("Surface image is already acquired")]
48    AlreadyAcquired,
49    #[error("Texture has been destroyed")]
50    TextureDestroyed,
51}
52
53#[derive(Clone, Debug, Error)]
54#[non_exhaustive]
55pub enum ConfigureSurfaceError {
56    #[error(transparent)]
57    Device(#[from] DeviceError),
58    #[error("Invalid surface")]
59    InvalidSurface,
60    #[error("The view format {0:?} is not compatible with texture format {1:?}, only changing srgb-ness is allowed.")]
61    InvalidViewFormat(wgt::TextureFormat, wgt::TextureFormat),
62    #[error(transparent)]
63    MissingDownlevelFlags(#[from] MissingDownlevelFlags),
64    #[error("`SurfaceOutput` must be dropped before a new `Surface` is made")]
65    PreviousOutputExists,
66    #[error("Failed to wait for GPU to come idle before reconfiguring the Surface")]
67    GpuWaitTimeout,
68    #[error("Both `Surface` width and height must be non-zero. Wait to recreate the `Surface` until the window has non-zero area.")]
69    ZeroArea,
70    #[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}.")]
71    TooLarge {
72        width: u32,
73        height: u32,
74        max_texture_dimension_2d: u32,
75    },
76    #[error("Surface does not support the adapter's queue family")]
77    UnsupportedQueueFamily,
78    #[error("Requested format {requested:?} is not in list of supported formats: {available:?}")]
79    UnsupportedFormat {
80        requested: wgt::TextureFormat,
81        available: Vec<wgt::TextureFormat>,
82    },
83    #[error("Requested present mode {requested:?} is not in the list of supported present modes: {available:?}")]
84    UnsupportedPresentMode {
85        requested: wgt::PresentMode,
86        available: Vec<wgt::PresentMode>,
87    },
88    #[error("Requested alpha mode {requested:?} is not in the list of supported alpha modes: {available:?}")]
89    UnsupportedAlphaMode {
90        requested: wgt::CompositeAlphaMode,
91        available: Vec<wgt::CompositeAlphaMode>,
92    },
93    #[error("Requested usage {requested:?} is not in the list of supported usages: {available:?}")]
94    UnsupportedUsage {
95        requested: wgt::TextureUses,
96        available: wgt::TextureUses,
97    },
98}
99
100impl From<WaitIdleError> for ConfigureSurfaceError {
101    fn from(e: WaitIdleError) -> Self {
102        match e {
103            WaitIdleError::Device(d) => ConfigureSurfaceError::Device(d),
104            WaitIdleError::WrongSubmissionIndex(..) => unreachable!(),
105            WaitIdleError::Timeout => ConfigureSurfaceError::GpuWaitTimeout,
106        }
107    }
108}
109
110pub type ResolvedSurfaceOutput = SurfaceOutput<Arc<resource::Texture>>;
111
112#[repr(C)]
113#[derive(Debug)]
114pub struct SurfaceOutput<T = id::TextureId> {
115    pub status: Status,
116    pub texture: Option<T>,
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(core::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(alloc::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: wgt::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, wgt::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 {
341            status,
342            texture: texture_id,
343        })
344    }
345
346    pub fn surface_present(&self, surface_id: id::SurfaceId) -> Result<Status, SurfaceError> {
347        let surface = self.surfaces.get(surface_id);
348
349        #[cfg(feature = "trace")]
350        if let Some(present) = surface.presentation.lock().as_ref() {
351            if let Some(ref mut trace) = *present.device.trace.lock() {
352                trace.add(Action::Present(surface_id));
353            }
354        }
355
356        surface.present()
357    }
358
359    pub fn surface_texture_discard(&self, surface_id: id::SurfaceId) -> Result<(), SurfaceError> {
360        let surface = self.surfaces.get(surface_id);
361
362        #[cfg(feature = "trace")]
363        if let Some(present) = surface.presentation.lock().as_ref() {
364            if let Some(ref mut trace) = *present.device.trace.lock() {
365                trace.add(Action::DiscardSurfaceTexture(surface_id));
366            }
367        }
368
369        surface.discard()
370    }
371}