use astrelis_core::profiling::profile_function;
use crate::context::GraphicsContext;
use crate::types::GpuTexture;
pub const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float;
pub struct Framebuffer {
color: GpuTexture,
depth: Option<GpuTexture>,
msaa: Option<GpuTexture>,
sample_count: u32,
}
impl std::fmt::Debug for Framebuffer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Framebuffer")
.field("width", &self.color.width())
.field("height", &self.color.height())
.field("format", &self.color.format())
.field("sample_count", &self.sample_count)
.field("has_depth", &self.depth.is_some())
.field("has_msaa", &self.msaa.is_some())
.finish()
}
}
impl Framebuffer {
pub fn builder(width: u32, height: u32) -> FramebufferBuilder {
FramebufferBuilder::new(width, height)
}
pub fn color_texture(&self) -> &wgpu::Texture {
use crate::extension::AsWgpu;
self.color.as_wgpu()
}
pub fn color_view(&self) -> &wgpu::TextureView {
self.color.view()
}
pub fn depth_texture(&self) -> Option<&wgpu::Texture> {
use crate::extension::AsWgpu;
self.depth.as_ref().map(|d| d.as_wgpu())
}
pub fn depth_view(&self) -> Option<&wgpu::TextureView> {
self.depth.as_ref().map(|d| d.view())
}
pub fn msaa_texture(&self) -> Option<&wgpu::Texture> {
use crate::extension::AsWgpu;
self.msaa.as_ref().map(|m| m.as_wgpu())
}
pub fn msaa_view(&self) -> Option<&wgpu::TextureView> {
self.msaa.as_ref().map(|m| m.view())
}
pub fn render_view(&self) -> &wgpu::TextureView {
self.msaa
.as_ref()
.map(|m| m.view())
.unwrap_or(self.color.view())
}
pub fn resolve_target(&self) -> Option<&wgpu::TextureView> {
if self.msaa.is_some() {
Some(self.color.view())
} else {
None
}
}
pub fn width(&self) -> u32 {
self.color.width()
}
pub fn height(&self) -> u32 {
self.color.height()
}
pub fn size(&self) -> (u32, u32) {
(self.color.width(), self.color.height())
}
pub fn format(&self) -> wgpu::TextureFormat {
self.color.format()
}
pub fn sample_count(&self) -> u32 {
self.sample_count
}
pub fn has_msaa(&self) -> bool {
self.sample_count > 1
}
pub fn has_depth(&self) -> bool {
self.depth.is_some()
}
pub fn resize(&mut self, context: &GraphicsContext, width: u32, height: u32) {
if self.color.width() == width && self.color.height() == height {
return;
}
let new_fb = FramebufferBuilder::new(width, height)
.format(self.color.format())
.sample_count_if(self.sample_count > 1, self.sample_count)
.depth_if(self.depth.is_some())
.build(context);
*self = new_fb;
}
}
pub struct FramebufferBuilder {
width: u32,
height: u32,
format: wgpu::TextureFormat,
sample_count: u32,
with_depth: bool,
label: Option<&'static str>,
}
impl FramebufferBuilder {
pub fn new(width: u32, height: u32) -> Self {
Self {
width,
height,
format: wgpu::TextureFormat::Rgba8UnormSrgb,
sample_count: 1,
with_depth: false,
label: None,
}
}
pub fn format(mut self, format: wgpu::TextureFormat) -> Self {
self.format = format;
self
}
pub fn with_msaa(mut self, sample_count: u32) -> Self {
self.sample_count = sample_count;
self
}
pub fn sample_count_if(mut self, condition: bool, sample_count: u32) -> Self {
if condition {
self.sample_count = sample_count;
}
self
}
pub fn with_depth(mut self) -> Self {
self.with_depth = true;
self
}
pub fn depth_if(mut self, condition: bool) -> Self {
self.with_depth = condition;
self
}
pub fn label(mut self, label: &'static str) -> Self {
self.label = Some(label);
self
}
pub fn build(self, context: &GraphicsContext) -> Framebuffer {
profile_function!();
let label_prefix = self.label.unwrap_or("Framebuffer");
let size = wgpu::Extent3d {
width: self.width,
height: self.height,
depth_or_array_layers: 1,
};
let color = GpuTexture::new(
context.device(),
&wgpu::TextureDescriptor {
label: Some(&format!("{} Color", label_prefix)),
size,
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: self.format,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT
| wgpu::TextureUsages::TEXTURE_BINDING
| wgpu::TextureUsages::COPY_SRC,
view_formats: &[],
},
);
let msaa = if self.sample_count > 1 {
Some(GpuTexture::new(
context.device(),
&wgpu::TextureDescriptor {
label: Some(&format!("{} MSAA", label_prefix)),
size,
mip_level_count: 1,
sample_count: self.sample_count,
dimension: wgpu::TextureDimension::D2,
format: self.format,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
view_formats: &[],
},
))
} else {
None
};
let depth = if self.with_depth {
let depth_sample_count = if self.sample_count > 1 {
self.sample_count
} else {
1
};
Some(GpuTexture::new(
context.device(),
&wgpu::TextureDescriptor {
label: Some(&format!("{} Depth", label_prefix)),
size,
mip_level_count: 1,
sample_count: depth_sample_count,
dimension: wgpu::TextureDimension::D2,
format: DEPTH_FORMAT,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT
| wgpu::TextureUsages::TEXTURE_BINDING,
view_formats: &[],
},
))
} else {
None
};
Framebuffer {
color,
depth,
msaa,
sample_count: self.sample_count,
}
}
}