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}