tirbofish/dropbear · commit
a01f355448ea5b2df9d743b4dabbf00509c68a73
feature: multisampling with MSAA
Signature present but could not be verified.
Unverified
@@ -10,6 +10,7 @@ use wgpu::*; use winit::window::Window; use crate::mipmap::MipMapper; +use crate::multisampling::AntiAliasingMode; use crate::pipelines::hdr::HdrPipeline; pub const NO_TEXTURE: &[u8] = include_bytes!("../../../resources/textures/no-texture.png"); @@ -30,6 +31,7 @@ pub struct SharedGraphicsContext { pub future_queue: Arc<FutureQueue>, pub mipmapper: Arc<MipMapper>, pub hdr: Arc<RwLock<HdrPipeline>>, + pub antialiasing: Arc<RwLock<AntiAliasingMode>>, // pub yakui_renderer: Arc<Mutex<yakui_wgpu::YakuiWgpu>>, // pub yakui_texture: yakui::TextureId, } @@ -75,6 +77,7 @@ impl SharedGraphicsContext { // yakui_renderer: state.yakui_renderer.clone(), // yakui_texture: state.yakui_texture.clone(), surface_config: state.config.clone(), + antialiasing: state.antialiasing.clone(), } } } @@ -22,6 +22,7 @@ pub mod shader; pub mod sky; pub mod texture; pub mod utils; +pub mod multisampling; features! { pub mod feature_list { @@ -54,11 +55,7 @@ use std::{ sync::Arc, time::{Duration, Instant}, }; -use wgpu::{ - BindGroupLayout, BindGroupLayoutEntry, BindingType, BufferBindingType, Device, - ExperimentalFeatures, Instance, Queue, ShaderStages, Surface, SurfaceConfiguration, - SurfaceError, TextureFormat, -}; +use wgpu::{BindGroupLayout, BindGroupLayoutEntry, BindingType, BufferBindingType, Device, ExperimentalFeatures, Instance, Queue, ShaderStages, Surface, SurfaceConfiguration, SurfaceError, TextureFormat, TextureFormatFeatureFlags}; use winit::event::{DeviceEvent, DeviceId}; use winit::{ application::ApplicationHandler, @@ -82,6 +79,7 @@ pub use gilrs; pub use wgpu; pub use winit; use winit::window::{WindowAttributes, WindowId}; +use crate::multisampling::{AntiAliasingMode}; pub struct BindGroupLayouts { pub scene_globals_bind_group_layout: BindGroupLayout, @@ -116,6 +114,7 @@ pub struct State { pub future_queue: Arc<FutureQueue>, pub mipmapper: Arc<MipMapper>, pub hdr: Arc<RwLock<HdrPipeline>>, + pub antialiasing: Arc<RwLock<AntiAliasingMode>>, physics_accumulator: Duration, @@ -248,11 +247,21 @@ Hardware: desired_maximum_frame_latency: 2, }; + let flags = adapter.get_texture_format_features(config.format).flags; + + let antialiasing = if flags.contains(TextureFormatFeatureFlags::MULTISAMPLE_X4) { + log::debug!("Rendering with MSAA4"); + AntiAliasingMode::MSAA4 + } else { + log::debug!("Rendering with no antialiasing (1)"); + AntiAliasingMode::None + }; + if is_surface_configured { surface.configure(&device, &config); } - let depth_texture = Texture::depth_texture(&config, &device, Some("depth texture")); + let depth_texture = Texture::depth_texture(&config, &device, antialiasing, Some("depth texture")); let viewport_texture = Texture::viewport(&config, &device, Some("viewport texture")); let mipmapper = Arc::new(MipMapper::new(&device)); @@ -508,6 +517,7 @@ Hardware: &device, &config, config.format.add_srgb_suffix(), + antialiasing, ))); // let yakui_renderer = Arc::new(Mutex::new(yakui_wgpu::YakuiWgpu::new( @@ -578,6 +588,7 @@ Hardware: instance, physics_accumulator: Duration::ZERO, scene_manager: scene::Manager::new(), + antialiasing: Arc::new(RwLock::new(antialiasing)), layouts: Arc::new(BindGroupLayouts { scene_globals_bind_group_layout, shader_globals_bind_group_layout, @@ -608,11 +619,11 @@ Hardware: } self.surface.configure(&self.device, &self.config.read()); self.is_surface_configured = true; - self.hdr.write().resize(&self.device, width, height); + self.hdr.write().resize(&self.device, width, height, Some(*self.antialiasing.read())); } self.depth_texture = - Texture::depth_texture(&self.config.read(), &self.device, Some("depth texture")); + Texture::depth_texture(&self.config.read(), &self.device, *self.antialiasing.read(), Some("depth texture")); self.viewport_texture = Texture::viewport(&self.config.read(), &self.device, Some("viewport texture")); self.egui_renderer @@ -641,9 +652,9 @@ Hardware: config.width = width; config.height = height; - self.depth_texture = Texture::depth_texture(&config, &self.device, Some("depth texture")); + self.depth_texture = Texture::depth_texture(&config, &self.device, *self.antialiasing.read(), Some("depth texture")); self.viewport_texture = Texture::viewport(&config, &self.device, Some("viewport texture")); - self.hdr.write().resize(&self.device, width, height); + self.hdr.write().resize(&self.device, width, height, Some(*self.antialiasing.read())); self.egui_renderer .lock() .renderer() @@ -0,0 +1,16 @@ +#[derive(Debug, Clone, Copy, PartialEq, Default)] +pub enum AntiAliasingMode { + #[default] + None, + MSAA4, + // todo: implement TAA +} + +impl Into<u32> for AntiAliasingMode { + fn into(self) -> u32 { + match self { + AntiAliasingMode::None => 1, + AntiAliasingMode::MSAA4 => 4, + } + } +} @@ -6,10 +6,12 @@ pub struct HdrPipeline { pipeline: wgpu::RenderPipeline, bind_group: wgpu::BindGroup, texture: Texture, + msaa_texture: Option<Texture>, width: u32, height: u32, format: wgpu::TextureFormat, layout: wgpu::BindGroupLayout, + antialiasing: crate::multisampling::AntiAliasingMode, } impl HdrPipeline { @@ -17,6 +19,7 @@ impl HdrPipeline { device: &wgpu::Device, config: &wgpu::SurfaceConfiguration, output_format: wgpu::TextureFormat, + antialiasing: crate::multisampling::AntiAliasingMode, ) -> Self { let width = config.width; let height = config.height; @@ -32,9 +35,24 @@ impl HdrPipeline { format, wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::RENDER_ATTACHMENT, wgpu::FilterMode::Nearest, + None, Some("Hdr::texture"), ); + let msaa_texture = match antialiasing { + crate::multisampling::AntiAliasingMode::None => None, + _ => Some(Texture::create_2d_texture( + device, + width, + height, + format, + wgpu::TextureUsages::RENDER_ATTACHMENT, + wgpu::FilterMode::Nearest, + Some(antialiasing), + Some("Hdr::msaa_texture"), + )), + }; + let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { label: Some("Hdr::layout"), entries: &[ @@ -91,6 +109,7 @@ impl HdrPipeline { &[], wgpu::PrimitiveTopology::TriangleList, shader, + 1, ); Self { @@ -98,23 +117,41 @@ impl HdrPipeline { bind_group, layout, texture, + msaa_texture, width, height, format, + antialiasing, } } /// Resize the HDR texture - pub fn resize(&mut self, device: &wgpu::Device, width: u32, height: u32) { + pub fn resize(&mut self, device: &wgpu::Device, width: u32, height: u32, antialiasing: Option<crate::multisampling::AntiAliasingMode>) { + self.antialiasing = antialiasing.unwrap_or(self.antialiasing); + self.texture = Texture::create_2d_texture( device, width, height, - wgpu::TextureFormat::Rgba16Float, + self.format, wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::RENDER_ATTACHMENT, wgpu::FilterMode::Nearest, + None, Some("Hdr::texture"), ); + self.msaa_texture = match self.antialiasing { + crate::multisampling::AntiAliasingMode::None => None, + _ => Some(Texture::create_2d_texture( + device, + width, + height, + self.format, + wgpu::TextureUsages::RENDER_ATTACHMENT, + wgpu::FilterMode::Nearest, + Some(self.antialiasing), + Some("Hdr::msaa_texture"), + )), + }; self.bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { label: Some("Hdr::bind_group"), layout: &self.layout, @@ -133,7 +170,23 @@ impl HdrPipeline { self.height = height; } - /// Exposes the HDR texture + /// The view to render INTO (MSAA if enabled, otherwise the HDR texture directly) + pub fn render_view(&self) -> &wgpu::TextureView { + match &self.msaa_texture { + Some(msaa) => &msaa.view, + None => &self.texture.view, + } + } + + /// The resolve target — only Some() when MSAA is active + pub fn resolve_target(&self) -> Option<&wgpu::TextureView> { + match &self.msaa_texture { + Some(_) => Some(&self.texture.view), + None => None, + } + } + + /// The resolved HDR texture for post-processing (always single-sample) pub fn view(&self) -> &wgpu::TextureView { &self.texture.view } @@ -41,6 +41,7 @@ impl DropbearShaderPipeline for LightCubePipeline { }); let hdr_format = graphics.hdr.read().format(); + let sample_count: u32 = (*graphics.antialiasing.read()).into(); let pipeline = graphics .device .create_render_pipeline(&wgpu::RenderPipelineDescriptor { @@ -87,7 +88,7 @@ impl DropbearShaderPipeline for LightCubePipeline { bias: DepthBiasState::default(), }), multisample: wgpu::MultisampleState { - count: 1, + count: sample_count, mask: !0, alpha_to_coverage_enabled: false, }, @@ -32,6 +32,7 @@ pub fn create_render_pipeline( vertex_layouts: &[wgpu::VertexBufferLayout], topology: wgpu::PrimitiveTopology, shader: wgpu::ShaderModuleDescriptor, + sample_count: u32, ) -> wgpu::RenderPipeline { create_render_pipeline_ex( label, @@ -44,6 +45,7 @@ pub fn create_render_pipeline( shader, true, // depth_write_enabled wgpu::CompareFunction::LessEqual, + sample_count, ) } @@ -58,6 +60,7 @@ pub fn create_render_pipeline_ex( shader: wgpu::ShaderModuleDescriptor, depth_write_enabled: bool, depth_compare: wgpu::CompareFunction, + sample_count: u32, ) -> wgpu::RenderPipeline { let shader = device.create_shader_module(shader); @@ -100,7 +103,7 @@ pub fn create_render_pipeline_ex( bias: wgpu::DepthBiasState::default(), }), multisample: wgpu::MultisampleState { - count: 1, + count: sample_count, mask: !0, alpha_to_coverage_enabled: false, }, @@ -39,6 +39,7 @@ impl DropbearShaderPipeline for MainRenderPipeline { }); let hdr_format = graphics.hdr.read().format(); + let sample_count: u32 = (*graphics.antialiasing.read()).into(); let pipeline = graphics .device .create_render_pipeline(&wgpu::RenderPipelineDescriptor { @@ -77,7 +78,7 @@ impl DropbearShaderPipeline for MainRenderPipeline { bias: DepthBiasState::default(), }), multisample: wgpu::MultisampleState { - count: 1, + count: sample_count, mask: !0, alpha_to_coverage_enabled: false, }, @@ -184,6 +184,7 @@ impl HdrLoader { wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, wgpu::FilterMode::Linear, None, + None, ); queue.write_texture( @@ -310,6 +311,7 @@ impl SkyPipeline { shader, false, wgpu::CompareFunction::GreaterEqual, + (*graphics.antialiasing.read()).into(), ) }; @@ -5,6 +5,7 @@ use crate::graphics::SharedGraphicsContext; use crate::utils::{ResourceReference, ToPotentialString}; use image::GenericImageView; use serde::{Deserialize, Serialize}; +use crate::multisampling::{AntiAliasingMode}; /// As defined in `shaders.wgsl` as /// ``` @@ -86,6 +87,7 @@ impl Texture { format: wgpu::TextureFormat, usage: wgpu::TextureUsages, mag_filter: wgpu::FilterMode, + antialiasing: Option<AntiAliasingMode>, label: Option<&str>, ) -> Self { puffin::profile_function!(label.unwrap_or("create 2d texture")); @@ -103,6 +105,7 @@ impl Texture { usage, wgpu::TextureDimension::D2, mag_filter, + antialiasing, ) } @@ -114,13 +117,14 @@ impl Texture { usage: wgpu::TextureUsages, dimension: wgpu::TextureDimension, mag_filter: wgpu::FilterMode, + antialiasing: Option<AntiAliasingMode>, ) -> Self { puffin::profile_function!(label.unwrap_or("create texture")); let texture = device.create_texture(&wgpu::TextureDescriptor { label, size, mip_level_count: 1, - sample_count: 1, + sample_count: antialiasing.unwrap_or_default().into(), dimension, format, usage, @@ -154,6 +158,7 @@ impl Texture { pub fn depth_texture( config: &wgpu::SurfaceConfiguration, device: &wgpu::Device, + antialiasing: AntiAliasingMode, label: Option<&str>, ) -> Self { puffin::profile_function!(label.unwrap_or("depth texture")); @@ -167,7 +172,7 @@ impl Texture { label, size, mip_level_count: 1, // leave me alone - sample_count: 1, + sample_count: antialiasing.into(), dimension: wgpu::TextureDimension::D2, format: Self::DEPTH_FORMAT, usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING, @@ -42,6 +42,7 @@ impl DropbearShaderPipeline for ColliderWireframePipeline { }); let hdr_format = graphics.hdr.read().format(); + let sample_count: u32 = (*graphics.antialiasing.read()).into(); let pipeline = graphics .device .create_render_pipeline(&RenderPipelineDescriptor { @@ -91,7 +92,7 @@ impl DropbearShaderPipeline for ColliderWireframePipeline { bias: DepthBiasState::default(), }), multisample: MultisampleState { - count: 1, + count: sample_count, mask: !0, alpha_to_coverage_enabled: false, }, @@ -337,9 +337,9 @@ impl Scene for Editor { let _ = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: Some("viewport clear pass"), color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: hdr.view(), + view: hdr.render_view(), depth_slice: None, - resolve_target: None, + resolve_target: hdr.resolve_target(), ops: wgpu::Operations { load: wgpu::LoadOp::Clear(wgpu::Color { r: 100.0 / 255.0, @@ -438,9 +438,9 @@ impl Scene for Editor { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: Some("light cube render pass"), color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: hdr.view(), + view: hdr.render_view(), depth_slice: None, - resolve_target: None, + resolve_target: hdr.resolve_target(), ops: wgpu::Operations { load: wgpu::LoadOp::Load, store: wgpu::StoreOp::Store, @@ -548,9 +548,9 @@ impl Scene for Editor { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: Some("model render pass"), color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: hdr.view(), + view: hdr.render_view(), depth_slice: None, - resolve_target: None, + resolve_target: hdr.resolve_target(), ops: wgpu::Operations { load: wgpu::LoadOp::Load, store: wgpu::StoreOp::Store, @@ -605,9 +605,9 @@ impl Scene for Editor { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: Some("model render pass (animated)"), color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: hdr.view(), + view: hdr.render_view(), depth_slice: None, - resolve_target: None, + resolve_target: hdr.resolve_target(), ops: wgpu::Operations { load: wgpu::LoadOp::Load, store: wgpu::StoreOp::Store, @@ -661,13 +661,13 @@ impl Scene for Editor { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: Some("sky render pass"), color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: hdr.view(), - resolve_target: None, + view: hdr.render_view(), + depth_slice: None, + resolve_target: hdr.resolve_target(), ops: wgpu::Operations { load: wgpu::LoadOp::Load, store: wgpu::StoreOp::Store, }, - depth_slice: None, })], depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { view: &graphics.depth_texture.view, @@ -708,9 +708,9 @@ impl Scene for Editor { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: Some("collider wireframe render pass"), color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: hdr.view(), + view: hdr.render_view(), depth_slice: None, - resolve_target: None, + resolve_target: hdr.resolve_target(), ops: wgpu::Operations { load: wgpu::LoadOp::Load, store: wgpu::StoreOp::Store, @@ -668,9 +668,9 @@ impl Scene for PlayMode { let _ = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: Some("viewport clear pass"), color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: hdr.view(), + view: hdr.render_view(), depth_slice: None, - resolve_target: None, + resolve_target: hdr.resolve_target(), ops: wgpu::Operations { load: wgpu::LoadOp::Clear(wgpu::Color { r: 100.0 / 255.0, @@ -765,9 +765,9 @@ impl Scene for PlayMode { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: Some("light cube render pass"), color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: hdr.view(), + view: hdr.render_view(), depth_slice: None, - resolve_target: None, + resolve_target: hdr.resolve_target(), ops: wgpu::Operations { load: wgpu::LoadOp::Load, store: wgpu::StoreOp::Store, @@ -874,9 +874,9 @@ impl Scene for PlayMode { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: Some("model render pass"), color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: hdr.view(), + view: hdr.render_view(), depth_slice: None, - resolve_target: None, + resolve_target: hdr.resolve_target(), ops: wgpu::Operations { load: wgpu::LoadOp::Load, store: wgpu::StoreOp::Store, @@ -930,9 +930,9 @@ impl Scene for PlayMode { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: Some("model render pass (animated)"), color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: hdr.view(), + view: hdr.render_view(), depth_slice: None, - resolve_target: None, + resolve_target: hdr.resolve_target(), ops: wgpu::Operations { load: wgpu::LoadOp::Load, store: wgpu::StoreOp::Store, @@ -985,13 +985,13 @@ impl Scene for PlayMode { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: Some("sky render pass"), color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: hdr.view(), - resolve_target: None, + view: hdr.render_view(), + depth_slice: None, + resolve_target: hdr.resolve_target(), ops: wgpu::Operations { load: wgpu::LoadOp::Load, store: wgpu::StoreOp::Store, }, - depth_slice: None, })], depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { view: &graphics.depth_texture.view, @@ -1027,11 +1027,11 @@ impl Scene for PlayMode { if show_hitboxes { if let Some(collider_pipeline) = &self.collider_wireframe_pipeline { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("model render pass"), + label: Some("collider wireframe render pass"), color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: hdr.view(), + view: hdr.render_view(), depth_slice: None, - resolve_target: None, + resolve_target: hdr.resolve_target(), ops: wgpu::Operations { load: wgpu::LoadOp::Load, store: wgpu::StoreOp::Store,