wgpu/api/
surface_texture.rs

1use core::{error, fmt};
2
3use crate::*;
4
5/// Surface texture that can be rendered to.
6/// Result of a successful call to [`Surface::get_current_texture`].
7///
8/// This type is unique to the Rust API of `wgpu`. In the WebGPU specification,
9/// the [`GPUCanvasContext`](https://gpuweb.github.io/gpuweb/#canvas-context) provides
10/// a texture without any additional information.
11#[derive(Debug, Clone)]
12pub struct SurfaceTexture {
13    /// Accessible view of the frame.
14    pub texture: Texture,
15    /// `true` if the acquired buffer can still be used for rendering,
16    /// but should be recreated for maximum performance.
17    pub suboptimal: bool,
18    pub(crate) presented: bool,
19    pub(crate) detail: dispatch::DispatchSurfaceOutputDetail,
20}
21#[cfg(send_sync)]
22static_assertions::assert_impl_all!(SurfaceTexture: Send, Sync);
23
24crate::cmp::impl_eq_ord_hash_proxy!(SurfaceTexture => .texture.inner);
25
26impl SurfaceTexture {
27    /// Schedule this texture to be presented on the owning surface.
28    ///
29    /// Needs to be called after any work on the texture is scheduled via [`Queue::submit`].
30    ///
31    /// # Platform dependent behavior
32    ///
33    /// On Wayland, `present` will attach a `wl_buffer` to the underlying `wl_surface` and commit the new surface
34    /// state. If it is desired to do things such as request a frame callback, scale the surface using the viewporter
35    /// or synchronize other double buffered state, then these operations should be done before the call to `present`.
36    pub fn present(mut self) {
37        self.presented = true;
38        self.detail.present();
39    }
40
41    #[cfg(custom)]
42    /// Returns custom implementation of SurfaceTexture (if custom backend and is internally T)
43    pub fn as_custom<T: crate::custom::SurfaceOutputDetailInterface>(&self) -> Option<&T> {
44        self.detail.as_custom()
45    }
46}
47
48impl Drop for SurfaceTexture {
49    fn drop(&mut self) {
50        if !self.presented && !thread_panicking() {
51            self.detail.texture_discard();
52        }
53    }
54}
55
56/// Result of an unsuccessful call to [`Surface::get_current_texture`].
57#[derive(Clone, PartialEq, Eq, Debug)]
58pub enum SurfaceError {
59    /// A timeout was encountered while trying to acquire the next frame.
60    Timeout,
61    /// The underlying surface has changed, and therefore the swap chain must be updated.
62    Outdated,
63    /// The swap chain has been lost and needs to be recreated.
64    Lost,
65    /// There is no more memory left to allocate a new frame.
66    OutOfMemory,
67    /// Acquiring a texture failed with a generic error. Check error callbacks for more information.
68    Other,
69}
70static_assertions::assert_impl_all!(SurfaceError: Send, Sync);
71
72impl fmt::Display for SurfaceError {
73    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74        write!(f, "{}", match self {
75            Self::Timeout => "A timeout was encountered while trying to acquire the next frame",
76            Self::Outdated => "The underlying surface has changed, and therefore the swap chain must be updated",
77            Self::Lost =>  "The swap chain has been lost and needs to be recreated",
78            Self::OutOfMemory => "There is no more memory left to allocate a new frame",
79            Self::Other => "Acquiring a texture failed with a generic error. Check error callbacks for more information",
80        })
81    }
82}
83
84impl error::Error for SurfaceError {}
85
86fn thread_panicking() -> bool {
87    cfg_if::cfg_if! {
88        if #[cfg(std)] {
89            std::thread::panicking()
90        } else if #[cfg(panic = "abort")] {
91            // If `panic = "abort"` then a thread _cannot_ be observably panicking by definition.
92            false
93        } else {
94            // TODO: This is potentially overly pessimistic; it may be appropriate to instead allow a
95            // texture to not be discarded.
96            // Alternatively, this could _also_ be a `panic!`, since we only care if the thread is panicking
97            // when the surface has not been presented.
98            compile_error!(
99                "cannot determine if a thread is panicking without either `panic = \"abort\"` or `std`"
100            );
101        }
102    }
103}