use anyhow::{anyhow, Context, Result};
pub const MSAA_SAMPLE_COUNT: u32 = 4;
pub const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float;
#[derive(Debug, Clone)]
pub struct SurfaceConfig {
pub width: u32,
pub height: u32,
pub format: wgpu::TextureFormat,
pub present_mode: wgpu::PresentMode,
pub alpha_mode: wgpu::CompositeAlphaMode,
}
impl SurfaceConfig {
pub fn aspect_ratio(&self) -> f32 {
if self.height == 0 {
1.0
} else {
self.width as f32 / self.height as f32
}
}
}
pub fn configure_surface(
surface: &wgpu::Surface,
device: &wgpu::Device,
adapter: &wgpu::Adapter,
width: u32,
height: u32,
) -> Result<SurfaceConfig> {
let caps = surface.get_capabilities(adapter);
let format = caps
.formats
.iter()
.copied()
.find(|f| f.is_srgb())
.or_else(|| caps.formats.first().copied())
.ok_or_else(|| anyhow!("surface has no supported texture formats"))?;
let present_mode = if caps.present_modes.contains(&wgpu::PresentMode::Fifo) {
wgpu::PresentMode::Fifo
} else {
caps.present_modes
.first()
.copied()
.unwrap_or(wgpu::PresentMode::Fifo)
};
let alpha_mode = caps
.alpha_modes
.first()
.copied()
.unwrap_or(wgpu::CompositeAlphaMode::Auto);
let config = wgpu::SurfaceConfiguration {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
format,
width: width.max(1),
height: height.max(1),
present_mode,
alpha_mode,
view_formats: vec![],
desired_maximum_frame_latency: 2,
};
surface.configure(device, &config);
Ok(SurfaceConfig {
width: config.width,
height: config.height,
format: config.format,
present_mode: config.present_mode,
alpha_mode: config.alpha_mode,
})
}
pub fn resize_surface(
surface: &wgpu::Surface,
device: &wgpu::Device,
config: &mut SurfaceConfig,
new_w: u32,
new_h: u32,
) -> Result<()> {
if new_w == 0 || new_h == 0 {
return Err(anyhow!(
"degenerate framebuffer size {}×{} — resize ignored",
new_w,
new_h
));
}
config.width = new_w;
config.height = new_h;
let wgpu_config = wgpu::SurfaceConfiguration {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
format: config.format,
width: new_w,
height: new_h,
present_mode: config.present_mode,
alpha_mode: config.alpha_mode,
view_formats: vec![],
desired_maximum_frame_latency: 2,
};
surface.configure(device, &wgpu_config);
Ok(())
}
pub struct DepthTexture {
pub texture: wgpu::Texture,
pub view: wgpu::TextureView,
pub format: wgpu::TextureFormat,
}
impl DepthTexture {
pub fn create(
device: &wgpu::Device,
width: u32,
height: u32,
sample_count: u32,
label: Option<&str>,
) -> Result<Self> {
let label = label.unwrap_or("depth_texture");
let texture = device.create_texture(&wgpu::TextureDescriptor {
label: Some(label),
size: wgpu::Extent3d {
width: width.max(1),
height: height.max(1),
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count,
dimension: wgpu::TextureDimension::D2,
format: DEPTH_FORMAT,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
view_formats: &[],
});
let view = texture.create_view(&wgpu::TextureViewDescriptor {
label: Some(&format!("{}_view", label)),
format: Some(DEPTH_FORMAT),
dimension: Some(wgpu::TextureViewDimension::D2),
aspect: wgpu::TextureAspect::DepthOnly,
base_mip_level: 0,
mip_level_count: None,
base_array_layer: 0,
array_layer_count: None,
usage: None,
});
Ok(Self {
texture,
view,
format: DEPTH_FORMAT,
})
}
pub fn create_scene(device: &wgpu::Device, width: u32, height: u32) -> Result<Self> {
Self::create(
device,
width,
height,
MSAA_SAMPLE_COUNT,
Some("scene_depth"),
)
.context("creating scene depth texture")
}
pub fn create_shadow_map(device: &wgpu::Device, size: u32) -> Result<Self> {
Self::create(device, size, size, 1, Some("shadow_depth"))
.context("creating shadow map depth texture")
}
}
pub struct MsaaTexture {
pub texture: wgpu::Texture,
pub view: wgpu::TextureView,
pub format: wgpu::TextureFormat,
}
impl MsaaTexture {
pub fn create(
device: &wgpu::Device,
width: u32,
height: u32,
format: wgpu::TextureFormat,
) -> Result<Self> {
let texture = device.create_texture(&wgpu::TextureDescriptor {
label: Some("msaa_texture"),
size: wgpu::Extent3d {
width: width.max(1),
height: height.max(1),
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: MSAA_SAMPLE_COUNT,
dimension: wgpu::TextureDimension::D2,
format,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
view_formats: &[],
});
let view = texture.create_view(&wgpu::TextureViewDescriptor {
label: Some("msaa_texture_view"),
..Default::default()
});
Ok(Self {
texture,
view,
format,
})
}
pub fn resize(self, device: &wgpu::Device, width: u32, height: u32) -> Result<Self> {
Self::create(device, width, height, self.format).context("resizing MSAA texture")
}
}