use egui_backend::WindowBackend;
use raw_window_handle::HasRawWindowHandle;
use tracing::{debug, info};
use wgpu::*;
pub struct SurfaceManager {
pub surface_view: Option<TextureView>,
pub surface_current_image: Option<SurfaceTexture>,
pub surface: Option<Surface>,
pub surface_config: SurfaceConfiguration,
surface_formats_priority: Vec<TextureFormat>,
}
impl Drop for SurfaceManager {
fn drop(&mut self) {
tracing::warn!("dropping wgpu surface");
}
}
impl SurfaceManager {
pub fn new(
window_backend: &mut impl WindowBackend,
instance: &Instance,
adapter: &Adapter,
device: &Device,
surface: Option<Surface>,
surface_formats_priority: Vec<TextureFormat>,
surface_config: SurfaceConfiguration,
) -> Self {
let mut surface_manager = Self {
surface_view: None,
surface_current_image: None,
surface,
surface_config,
surface_formats_priority,
};
surface_manager.reconfigure_surface(window_backend, instance, adapter, device);
surface_manager
}
pub fn create_current_surface_texture_view(
&mut self,
window_backend: &mut impl WindowBackend,
device: &Device,
) {
if let Some(surface) = self.surface.as_ref() {
let current_surface_image = surface.get_current_texture().unwrap_or_else(|_| {
let phy_fb_size = window_backend.get_live_physical_size_framebuffer().unwrap();
self.surface_config.width = phy_fb_size[0];
self.surface_config.height = phy_fb_size[1];
surface.configure(device, &self.surface_config);
surface.get_current_texture().unwrap_or_else(|e| {
panic!("failed to get surface even after reconfiguration. {e}")
})
});
if current_surface_image.suboptimal {
tracing::warn!("current surface image is suboptimal. ");
}
let surface_view = current_surface_image
.texture
.create_view(&TextureViewDescriptor {
label: Some("surface view"),
format: Some(self.surface_config.format),
dimension: Some(TextureViewDimension::D2),
aspect: TextureAspect::All,
base_mip_level: 0,
mip_level_count: None,
base_array_layer: 0,
array_layer_count: None,
});
self.surface_view = Some(surface_view);
self.surface_current_image = Some(current_surface_image);
} else {
tracing::warn!(
"skipping acquiring the currnet surface image because there's no surface"
);
}
}
pub fn reconfigure_surface(
&mut self,
window_backend: &mut impl WindowBackend,
instance: &Instance,
adapter: &Adapter,
device: &Device,
) {
if let Some(window) = window_backend.get_window() {
if self.surface.is_none() {
self.surface = Some(unsafe {
tracing::debug!("creating a surface with {:?}", window.raw_window_handle());
instance
.create_surface(window)
.expect("failed to create surface")
});
}
let capabilities = self.surface.as_ref().unwrap().get_capabilities(adapter);
let supported_formats = capabilities.formats;
debug!(
"supported alpha modes: {:#?}",
&capabilities.alpha_modes[..]
);
if window_backend.get_config().transparent.unwrap_or_default() {
for alpha_mode in capabilities.alpha_modes.iter().copied() {
match alpha_mode {
CompositeAlphaMode::PreMultiplied | CompositeAlphaMode::PostMultiplied => {
self.surface_config.alpha_mode = alpha_mode;
}
_ => {}
}
}
}
debug!("supported formats of the surface: {supported_formats:#?}");
let mut compatible_format_found = false;
for sfmt in self.surface_formats_priority.iter() {
debug!("checking if {sfmt:?} is supported");
if supported_formats.contains(sfmt) {
debug!("{sfmt:?} is supported. setting it as surface format");
self.surface_config.format = *sfmt;
compatible_format_found = true;
break;
}
}
if !compatible_format_found {
if !self.surface_formats_priority.is_empty() {
tracing::warn!(
"could not find compatible surface format from user provided formats. choosing first supported format instead"
);
}
self.surface_config.format = supported_formats
.iter()
.find(|f| f.is_srgb())
.copied()
.unwrap_or_else(|| {
supported_formats
.first()
.copied()
.expect("surface has zero supported texture formats")
})
}
let view_format = if self.surface_config.format.is_srgb() {
self.surface_config.format
} else {
tracing::warn!(
"surface format is not srgb: {:?}",
self.surface_config.format
);
match self.surface_config.format {
TextureFormat::Rgba8Unorm => TextureFormat::Rgba8UnormSrgb,
TextureFormat::Bgra8Unorm => TextureFormat::Bgra8UnormSrgb,
_ => self.surface_config.format,
}
};
self.surface_config.view_formats = vec![view_format];
#[cfg(target_os = "emscripten")]
{
self.surface_config.view_formats = vec![];
}
debug!(
"using format: {:#?} for surface configuration",
self.surface_config.format
);
self.resize_framebuffer(device, window_backend);
}
}
pub fn resize_framebuffer(&mut self, device: &Device, window_backend: &mut impl WindowBackend) {
if let Some(size) = window_backend.get_live_physical_size_framebuffer() {
self.surface_config.width = size[0];
self.surface_config.height = size[1];
info!(
"reconfiguring surface with config: {:#?}",
&self.surface_config
);
self.surface
.as_ref()
.unwrap()
.configure(device, &self.surface_config);
}
}
pub fn suspend(&mut self) {
self.surface = None;
self.surface_current_image = None;
self.surface_view = None;
}
}