pub use super::pipelines::DEPTH_FORMAT;
pub const DEPTH_CLEAR: f32 = 1.0;
pub const DEPTH_COMPARE: wgpu::CompareFunction = wgpu::CompareFunction::LessEqual;
pub const DEPTH_USAGE: wgpu::TextureUsages = wgpu::TextureUsages::RENDER_ATTACHMENT;
#[must_use]
pub fn depth_texture_descriptor(width: u32, height: u32) -> wgpu::TextureDescriptor<'static> {
wgpu::TextureDescriptor {
label: Some("frust-engine depth texture"),
size: wgpu::Extent3d {
width: width.max(1),
height: height.max(1),
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: DEPTH_FORMAT,
usage: DEPTH_USAGE,
view_formats: &[],
}
}
#[must_use]
pub fn depth_load_op(pre_cleared: bool) -> wgpu::LoadOp<f32> {
if pre_cleared {
wgpu::LoadOp::Load
} else {
wgpu::LoadOp::Clear(DEPTH_CLEAR)
}
}
#[derive(Debug)]
pub struct DepthTexture {
texture: wgpu::Texture,
view: wgpu::TextureView,
width: u32,
height: u32,
}
impl DepthTexture {
#[must_use]
pub fn new(device: &wgpu::Device, width: u32, height: u32) -> Self {
let descriptor = depth_texture_descriptor(width, height);
let texture = device.create_texture(&descriptor);
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
Self {
texture,
view,
width: descriptor.size.width,
height: descriptor.size.height,
}
}
#[must_use]
pub fn texture(&self) -> &wgpu::Texture {
&self.texture
}
#[must_use]
pub fn view(&self) -> &wgpu::TextureView {
&self.view
}
#[must_use]
pub fn size(&self) -> (u32, u32) {
(self.width, self.height)
}
#[must_use]
pub fn matches(&self, width: u32, height: u32) -> bool {
(self.width, self.height) == (width.max(1), height.max(1))
}
}
#[derive(Debug, Default)]
pub struct DepthAttachment {
owned: Option<DepthTexture>,
pre_cleared: bool,
}
impl DepthAttachment {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn set_pre_cleared(&mut self, pre_cleared: bool) {
self.pre_cleared = pre_cleared;
}
#[must_use]
pub fn is_pre_cleared(&self) -> bool {
self.pre_cleared
}
#[must_use]
pub fn load_op(&self, caller_supplied: bool) -> wgpu::LoadOp<f32> {
depth_load_op(caller_supplied && self.pre_cleared)
}
#[must_use]
pub fn owned(&self) -> Option<&DepthTexture> {
self.owned.as_ref()
}
#[must_use]
pub fn owned_view(&self) -> Option<&wgpu::TextureView> {
self.owned.as_ref().map(DepthTexture::view)
}
pub fn ensure(&mut self, device: &wgpu::Device, width: u32, height: u32) -> &wgpu::TextureView {
if self
.owned
.as_ref()
.is_some_and(|depth| !depth.matches(width, height))
{
self.owned = None;
}
self.owned
.get_or_insert_with(|| DepthTexture::new(device, width, height))
.view()
}
pub fn discard(&mut self) {
self.owned = None;
}
pub fn resize(&mut self, device: &wgpu::Device, width: u32, height: u32) {
if self.owned.is_some() {
self.owned = Some(DepthTexture::new(device, width, height));
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_descriptor_is_a_single_sample_attachment_only_depth_target() {
let descriptor = depth_texture_descriptor(800, 600);
assert_eq!(descriptor.format, DEPTH_FORMAT);
assert_eq!(descriptor.usage, wgpu::TextureUsages::RENDER_ATTACHMENT);
assert_eq!(descriptor.sample_count, 1);
assert_eq!(descriptor.mip_level_count, 1);
assert_eq!(descriptor.size.width, 800);
assert_eq!(descriptor.size.height, 600);
assert_eq!(descriptor.size.depth_or_array_layers, 1);
}
#[test]
fn a_zero_extent_is_floored_to_one_rather_than_refused() {
let descriptor = depth_texture_descriptor(0, 0);
assert_eq!((descriptor.size.width, descriptor.size.height), (1, 1));
}
#[test]
fn a_pre_cleared_attachment_loads_instead_of_clearing() {
assert_eq!(depth_load_op(false), wgpu::LoadOp::Clear(DEPTH_CLEAR));
assert_eq!(depth_load_op(true), wgpu::LoadOp::Load);
}
#[test]
fn only_a_caller_supplied_attachment_can_be_pre_cleared() {
let mut depth = DepthAttachment::new();
assert!(!depth.is_pre_cleared());
assert_eq!(depth.load_op(true), wgpu::LoadOp::Clear(DEPTH_CLEAR));
depth.set_pre_cleared(true);
assert!(depth.is_pre_cleared());
assert_eq!(depth.load_op(true), wgpu::LoadOp::Load);
assert_eq!(depth.load_op(false), wgpu::LoadOp::Clear(DEPTH_CLEAR));
}
#[test]
fn an_attachment_owns_nothing_until_a_frame_asks_for_one() {
let depth = DepthAttachment::new();
assert!(depth.owned().is_none());
assert!(depth.owned_view().is_none());
}
}