egui_render_wgpu/
surface.rs

1use rwh::HasWindowHandle;
2use tracing::{debug, info};
3use wgpu::*;
4pub struct SurfaceManager {
5    /// we create a view for the swapchain image and set it to this field during the `prepare_frame` fn.
6    /// users can assume that it will *always* be available during the `UserApp::run` fn. but don't keep any references as
7    /// it will be taken and submitted during the `present_frame` method after rendering is done.
8    /// surface is always cleared by wgpu, so no need to wipe it again.
9    pub surface_view: Option<TextureView>,
10    /// once we acquire a swapchain image (surface texture), we will put it here. surface_view will be created from this
11    pub surface_current_image: Option<SurfaceTexture>,
12    /// this is the window surface
13    pub surface: Option<Surface<'static>>,
14    /// this configuration needs to be updated with the latest resize
15    pub surface_config: SurfaceConfiguration,
16    /// Surface manager will iterate over this and find the first format that is supported by surface.
17    /// if we find one, we will set surface configuration to that format.
18    /// if we don't find one, we will just use the first surface format support.
19    /// so, if you don't care about the surface format, just set this to an empty vector.
20    surface_formats_priority: Vec<TextureFormat>,
21}
22impl Drop for SurfaceManager {
23    fn drop(&mut self) {
24        tracing::warn!("dropping wgpu surface");
25    }
26}
27impl SurfaceManager {
28    #[allow(clippy::too_many_arguments)]
29    pub fn new(
30        window: Option<Box<dyn WindowHandle>>,
31        transparent: Option<bool>,
32        latest_fb_size: [u32; 2],
33        instance: &Instance,
34        adapter: &Adapter,
35        device: &Device,
36        surface: Option<Surface<'static>>,
37        surface_formats_priority: Vec<TextureFormat>,
38        surface_config: SurfaceConfiguration,
39    ) -> Self {
40        let mut surface_manager = Self {
41            surface_view: None,
42            surface_current_image: None,
43            surface,
44            surface_config,
45            surface_formats_priority,
46        };
47        surface_manager.reconfigure_surface(
48            window,
49            transparent,
50            latest_fb_size,
51            instance,
52            adapter,
53            device,
54        );
55        surface_manager
56    }
57    pub fn create_current_surface_texture_view(
58        &mut self,
59        mut latest_framebuffer_size_getter: impl FnMut() -> [u32; 2],
60        device: &Device,
61    ) {
62        if let Some(surface) = self.surface.as_ref() {
63            let current_surface_image = surface.get_current_texture().unwrap_or_else(|_| {
64                let latest_fb_size = latest_framebuffer_size_getter();
65                self.surface_config.width = latest_fb_size[0];
66                self.surface_config.height = latest_fb_size[1];
67                surface.configure(device, &self.surface_config);
68                surface.get_current_texture().unwrap_or_else(|e| {
69                    panic!("failed to get surface even after reconfiguration. {e}")
70                })
71            });
72            if current_surface_image.suboptimal {
73                tracing::warn!("current surface image is suboptimal. ");
74            }
75            let surface_view = current_surface_image
76                .texture
77                .create_view(&TextureViewDescriptor {
78                    label: Some("surface view"),
79                    format: Some(self.surface_config.format),
80                    dimension: Some(TextureViewDimension::D2),
81                    aspect: TextureAspect::All,
82                    base_mip_level: 0,
83                    mip_level_count: None,
84                    base_array_layer: 0,
85                    array_layer_count: None,
86                });
87
88            self.surface_view = Some(surface_view);
89            self.surface_current_image = Some(current_surface_image);
90        } else {
91            tracing::warn!(
92                "skipping acquiring the currnet surface image because there's no surface"
93            );
94        }
95    }
96    /// This basically checks if the surface needs creating. and then if needed, creates surface if window exists.
97    /// then, it does all the work of configuring the surface.
98    /// this is used during resume events to create a surface.
99    pub fn reconfigure_surface(
100        &mut self,
101        window: Option<Box<dyn WindowHandle>>,
102        transparent: Option<bool>,
103        latest_fb_size: [u32; 2],
104        instance: &Instance,
105        adapter: &Adapter,
106        device: &Device,
107    ) {
108        if let Some(window) = window {
109            if self.surface.is_none() {
110                self.surface = Some({
111                    tracing::debug!("creating a surface with {:?}", window.window_handle());
112                    instance
113                        .create_surface(SurfaceTarget::Window(window))
114                        .expect("failed to create surface")
115                });
116            }
117
118            let capabilities = self.surface.as_ref().unwrap().get_capabilities(adapter);
119            let supported_formats = capabilities.formats;
120            debug!(
121                "supported alpha modes: {:#?}",
122                &capabilities.alpha_modes[..]
123            );
124
125            if transparent.unwrap_or_default() {
126                use CompositeAlphaMode::*;
127                let alpha_modes: Vec<CompositeAlphaMode> = capabilities.alpha_modes.to_vec();
128                tracing::info!(?alpha_modes, "supported alpha modes");
129                {
130                    self.surface_config.alpha_mode = if alpha_modes.contains(&Inherit) {
131                        Inherit
132                    } else if alpha_modes.contains(&PreMultiplied) {
133                        PreMultiplied
134                    } else if alpha_modes.contains(&PostMultiplied) {
135                        PostMultiplied
136                    } else {
137                        Auto
138                    };
139                }
140            }
141            debug!("supported formats of the surface: {supported_formats:#?}");
142
143            let mut compatible_format_found = false;
144            for sfmt in self.surface_formats_priority.iter() {
145                debug!("checking if {sfmt:?} is supported");
146                if supported_formats.contains(sfmt) {
147                    debug!("{sfmt:?} is supported. setting it as surface format");
148                    self.surface_config.format = *sfmt;
149                    compatible_format_found = true;
150                    break;
151                }
152            }
153            if !compatible_format_found {
154                if !self.surface_formats_priority.is_empty() {
155                    tracing::warn!(
156                        "could not find compatible surface format from user provided formats. choosing first supported format instead"
157                    );
158                }
159                self.surface_config.format = supported_formats
160                    .iter()
161                    .find(|f| f.is_srgb())
162                    .copied()
163                    .unwrap_or_else(|| {
164                        supported_formats
165                            .first()
166                            .copied()
167                            .expect("surface has zero supported texture formats")
168                    })
169            }
170            let view_format = if self.surface_config.format.is_srgb() {
171                self.surface_config.format
172            } else {
173                tracing::warn!(
174                    "surface format is not srgb: {:?}",
175                    self.surface_config.format
176                );
177                match self.surface_config.format {
178                    TextureFormat::Rgba8Unorm => TextureFormat::Rgba8UnormSrgb,
179                    TextureFormat::Bgra8Unorm => TextureFormat::Bgra8UnormSrgb,
180                    _ => self.surface_config.format,
181                }
182            };
183            self.surface_config.view_formats = vec![view_format];
184
185            #[cfg(target_os = "emscripten")]
186            {
187                self.surface_config.view_formats = vec![];
188            }
189
190            debug!(
191                "using format: {:#?} for surface configuration",
192                self.surface_config.format
193            );
194            self.resize_framebuffer(device, latest_fb_size);
195        }
196    }
197
198    pub fn resize_framebuffer(&mut self, device: &Device, latest_fb_size: [u32; 2]) {
199        self.surface_config.width = latest_fb_size[0];
200        self.surface_config.height = latest_fb_size[1];
201        info!(
202            "reconfiguring surface with config: {:#?}",
203            &self.surface_config
204        );
205        self.surface
206            .as_ref()
207            .unwrap()
208            .configure(device, &self.surface_config);
209    }
210    pub fn suspend(&mut self) {
211        self.surface = None;
212        self.surface_current_image = None;
213        self.surface_view = None;
214    }
215}