kitgit

tirbofish/dropbear · diff

a01f355 · Thribhu K

feature: multisampling with MSAA

Unverified

diff --git a/crates/dropbear-engine/src/graphics.rs b/crates/dropbear-engine/src/graphics.rs
index 65f9d84..73cd01c 100644
--- a/crates/dropbear-engine/src/graphics.rs
+++ b/crates/dropbear-engine/src/graphics.rs
@@ -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(),
         }
     }
 }
diff --git a/crates/dropbear-engine/src/lib.rs b/crates/dropbear-engine/src/lib.rs
index 7a62455..905a06d 100644
--- a/crates/dropbear-engine/src/lib.rs
+++ b/crates/dropbear-engine/src/lib.rs
@@ -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()
diff --git a/crates/dropbear-engine/src/multisampling.rs b/crates/dropbear-engine/src/multisampling.rs
new file mode 100644
index 0000000..5e6a870
--- /dev/null
+++ b/crates/dropbear-engine/src/multisampling.rs
@@ -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,
+        }
+    }
+}
\ No newline at end of file
diff --git a/crates/dropbear-engine/src/pipelines/hdr.rs b/crates/dropbear-engine/src/pipelines/hdr.rs
index 2a67108..364aff6 100644
--- a/crates/dropbear-engine/src/pipelines/hdr.rs
+++ b/crates/dropbear-engine/src/pipelines/hdr.rs
@@ -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
     }
diff --git a/crates/dropbear-engine/src/pipelines/light_cube.rs b/crates/dropbear-engine/src/pipelines/light_cube.rs
index 2a44042..7f346d7 100644
--- a/crates/dropbear-engine/src/pipelines/light_cube.rs
+++ b/crates/dropbear-engine/src/pipelines/light_cube.rs
@@ -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,
                 },
diff --git a/crates/dropbear-engine/src/pipelines/mod.rs b/crates/dropbear-engine/src/pipelines/mod.rs
index b9f0d3c..96f8721 100644
--- a/crates/dropbear-engine/src/pipelines/mod.rs
+++ b/crates/dropbear-engine/src/pipelines/mod.rs
@@ -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,
         },
diff --git a/crates/dropbear-engine/src/pipelines/shader.rs b/crates/dropbear-engine/src/pipelines/shader.rs
index 6d61dd1..0e8b053 100644
--- a/crates/dropbear-engine/src/pipelines/shader.rs
+++ b/crates/dropbear-engine/src/pipelines/shader.rs
@@ -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,
                 },
diff --git a/crates/dropbear-engine/src/sky.rs b/crates/dropbear-engine/src/sky.rs
index 1f57cd8..e6df4c3 100644
--- a/crates/dropbear-engine/src/sky.rs
+++ b/crates/dropbear-engine/src/sky.rs
@@ -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(),
             )
         };
 
diff --git a/crates/dropbear-engine/src/texture.rs b/crates/dropbear-engine/src/texture.rs
index 4ab0ec8..62b475a 100644
--- a/crates/dropbear-engine/src/texture.rs
+++ b/crates/dropbear-engine/src/texture.rs
@@ -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,
diff --git a/crates/eucalyptus-core/src/physics/collider/shader.rs b/crates/eucalyptus-core/src/physics/collider/shader.rs
index 296be1b..fdab37b 100644
--- a/crates/eucalyptus-core/src/physics/collider/shader.rs
+++ b/crates/eucalyptus-core/src/physics/collider/shader.rs
@@ -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,
                 },
diff --git a/crates/eucalyptus-editor/src/editor/scene.rs b/crates/eucalyptus-editor/src/editor/scene.rs
index e48c243..85d6ec7 100644
--- a/crates/eucalyptus-editor/src/editor/scene.rs
+++ b/crates/eucalyptus-editor/src/editor/scene.rs
@@ -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,
diff --git a/crates/redback-runtime/src/scene.rs b/crates/redback-runtime/src/scene.rs
index 7b3edf3..c817b60 100644
--- a/crates/redback-runtime/src/scene.rs
+++ b/crates/redback-runtime/src/scene.rs
@@ -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,