MSAA
This commit is contained in:
+122
-8
@@ -40,6 +40,7 @@ use crate::resources::{
|
||||
use crate::scene::Scene;
|
||||
use crate::core::bloom::{BloomConfig, BloomPipeline};
|
||||
use crate::core::hdr::ToneMapper;
|
||||
use crate::core::msaa::MsaaConfig;
|
||||
use crate::utils::conf::{
|
||||
GPU_DRIVEN_SHADER, GPU_WORKGROUP_SIZE, LOD_THRESHOLDS, MAX_ENTITIES, MAX_LOD_LEVELS, TONEMAP_SHADER,
|
||||
};
|
||||
@@ -159,6 +160,16 @@ pub struct Renderer {
|
||||
bloom: Option<BloomPipeline>,
|
||||
/// Bloom configuration (used per-frame for uniform writes). Only meaningful when bloom is active.
|
||||
bloom_config: BloomConfig,
|
||||
/// MSAA configuration (Étape 24). `sample_count = 1` means MSAA is disabled (zero overhead).
|
||||
msaa_config: MsaaConfig,
|
||||
/// MSAA color texture (N samples). `None` when MSAA is disabled.
|
||||
msaa_color_texture: Option<wgpu::Texture>,
|
||||
/// MSAA color view used as the main pass color attachment when MSAA is active.
|
||||
msaa_color_view: Option<wgpu::TextureView>,
|
||||
/// MSAA depth texture (N samples). `None` when MSAA is disabled.
|
||||
msaa_depth_texture: Option<wgpu::Texture>,
|
||||
/// MSAA depth view used as the main pass depth attachment when MSAA is active.
|
||||
msaa_depth_view: Option<wgpu::TextureView>,
|
||||
}
|
||||
|
||||
/// Internal HDR pipeline state: offscreen `Rgba16Float` texture + tone mapping render pipeline.
|
||||
@@ -201,6 +212,7 @@ impl Renderer {
|
||||
shadow_config: &super::shadow::ShadowConfig,
|
||||
hdr: Option<ToneMapper>,
|
||||
bloom_config: Option<BloomConfig>,
|
||||
msaa_config: Option<MsaaConfig>,
|
||||
) -> Self {
|
||||
let queue: wgpu::Queue = context.queue.clone();
|
||||
let device: wgpu::Device = context.device.clone();
|
||||
@@ -594,6 +606,11 @@ impl Renderer {
|
||||
hdr: None,
|
||||
bloom: None,
|
||||
bloom_config: bloom_config.clone().unwrap_or_default(),
|
||||
msaa_config: msaa_config.clone().unwrap_or_default(),
|
||||
msaa_color_texture: None,
|
||||
msaa_color_view: None,
|
||||
msaa_depth_texture: None,
|
||||
msaa_depth_view: None,
|
||||
};
|
||||
// Seed the shared frame buffer with an identity camera + current unlit flag so the low-level
|
||||
// `render` path (which has no window/camera) sees coherent values before `render_scene` runs.
|
||||
@@ -613,6 +630,33 @@ impl Renderer {
|
||||
renderer.bloom_config = bloom_config.clone().unwrap();
|
||||
}
|
||||
}
|
||||
// Étape 24: allocate MSAA textures when sample_count > 1.
|
||||
// The MSAA color texture uses the same format as the main target (HDR or surface).
|
||||
if renderer.msaa_config.sample_count > 1 {
|
||||
let sc = renderer.msaa_config.sample_count;
|
||||
let color_format = if renderer.hdr.is_some() {
|
||||
wgpu::TextureFormat::Rgba16Float
|
||||
} else {
|
||||
format
|
||||
};
|
||||
let msaa_tex = renderer.device.create_texture(&wgpu::TextureDescriptor {
|
||||
label: Some("MSAA color texture"),
|
||||
size: wgpu::Extent3d { width, height, depth_or_array_layers: 1 },
|
||||
mip_level_count: 1,
|
||||
sample_count: sc,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: color_format,
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||
view_formats: &[],
|
||||
});
|
||||
let msaa_view = msaa_tex.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let (msaa_depth_tex, msaa_depth_view) =
|
||||
create_msaa_depth_texture(&renderer.device, width, height, sc);
|
||||
renderer.msaa_color_texture = Some(msaa_tex);
|
||||
renderer.msaa_color_view = Some(msaa_view);
|
||||
renderer.msaa_depth_texture = Some(msaa_depth_tex);
|
||||
renderer.msaa_depth_view = Some(msaa_depth_view);
|
||||
}
|
||||
renderer
|
||||
}
|
||||
|
||||
@@ -674,6 +718,32 @@ impl Renderer {
|
||||
hdr.bind_group = bg;
|
||||
}
|
||||
}
|
||||
// Étape 24: recreate MSAA textures at the new size.
|
||||
if self.msaa_config.sample_count > 1 {
|
||||
let sc = self.msaa_config.sample_count;
|
||||
let color_format = if self.hdr.is_some() {
|
||||
wgpu::TextureFormat::Rgba16Float
|
||||
} else {
|
||||
self.format
|
||||
};
|
||||
let msaa_tex = self.device.create_texture(&wgpu::TextureDescriptor {
|
||||
label: Some("MSAA color texture"),
|
||||
size: wgpu::Extent3d { width, height, depth_or_array_layers: 1 },
|
||||
mip_level_count: 1,
|
||||
sample_count: sc,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: color_format,
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||
view_formats: &[],
|
||||
});
|
||||
let msaa_view = msaa_tex.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let (msaa_depth_tex, msaa_depth_view) =
|
||||
create_msaa_depth_texture(&self.device, width, height, sc);
|
||||
self.msaa_color_texture = Some(msaa_tex);
|
||||
self.msaa_color_view = Some(msaa_view);
|
||||
self.msaa_depth_texture = Some(msaa_depth_tex);
|
||||
self.msaa_depth_view = Some(msaa_depth_view);
|
||||
}
|
||||
}
|
||||
|
||||
/// Updates the stored surface texture format after a surface reconfigure (ROADMAP Phase 4.4).
|
||||
@@ -963,26 +1033,38 @@ impl Renderer {
|
||||
// are hoisted out of the slot loop: one per DISTINCT material, not one per entity.
|
||||
// Étape 20: when HDR is active, the color attachment targets the offscreen HDR texture
|
||||
// instead of the surface; the TM pass (step 8) then copies it to the surface.
|
||||
let main_target = match &self.hdr {
|
||||
Some(h) => &h.view,
|
||||
None => view,
|
||||
// Étape 24: when MSAA is active, the color attachment targets the MSAA texture and
|
||||
// resolves into the single-sample target (HDR or swapchain). The depth is also MSAA.
|
||||
let (color_view, resolve_target, depth_attach) = if let Some(msaa_view) = &self.msaa_color_view {
|
||||
// MSAA active: render into MSAA, resolve to single-sample target.
|
||||
let resolve = match &self.hdr {
|
||||
Some(h) => Some(h.view.clone()),
|
||||
None => Some(view.clone()),
|
||||
};
|
||||
let depth = self.msaa_depth_view.as_ref().unwrap();
|
||||
(msaa_view.clone(), resolve, depth)
|
||||
} else {
|
||||
// No MSAA: current behavior.
|
||||
let color = match &self.hdr {
|
||||
Some(h) => &h.view,
|
||||
None => view,
|
||||
};
|
||||
(color.clone(), None, &self.depth_view)
|
||||
};
|
||||
{
|
||||
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: Some("scene render pass"),
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view: main_target,
|
||||
resolve_target: None,
|
||||
view: &color_view,
|
||||
resolve_target: resolve_target.as_ref(),
|
||||
depth_slice: None,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
|
||||
store: wgpu::StoreOp::Store,
|
||||
},
|
||||
})],
|
||||
// Step 9 (DRAFT 9.2): same depth attachment as the low-level path, for a
|
||||
// coherent z-test (D2 — both render passes share the depth_view).
|
||||
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
|
||||
view: &self.depth_view,
|
||||
view: depth_attach,
|
||||
depth_ops: Some(wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(1.0),
|
||||
store: wgpu::StoreOp::Store,
|
||||
@@ -1406,6 +1488,16 @@ impl Renderer {
|
||||
self.bloom_config = config.clone();
|
||||
}
|
||||
|
||||
/// Returns `true` if MSAA is active (sample_count > 1). (Étape 24)
|
||||
pub fn msaa_enabled(&self) -> bool {
|
||||
self.msaa_config.sample_count > 1
|
||||
}
|
||||
|
||||
/// Returns the current MSAA sample count (1 = disabled). (Étape 24)
|
||||
pub fn msaa_sample_count(&self) -> u32 {
|
||||
self.msaa_config.sample_count
|
||||
}
|
||||
|
||||
/// Computes the per-slot LOD levels for this frame (Step 19, D8): for each ACTIVE slot, the
|
||||
/// entity's bounding sphere — the **same sphere** the GPU frustum culling uses (D8: bbox
|
||||
/// center + max half-extent × max scale component, rotated by the entity's quaternion) — is
|
||||
@@ -1504,6 +1596,28 @@ fn create_depth_texture(
|
||||
(depth_texture, depth_view)
|
||||
}
|
||||
|
||||
/// Creates an MSAA depth texture (N samples) with a view (Étape 24). Used when MSAA is active:
|
||||
/// the main pass needs a multi-sampled depth buffer matching the MSAA color attachment.
|
||||
fn create_msaa_depth_texture(
|
||||
device: &wgpu::Device,
|
||||
width: u32,
|
||||
height: u32,
|
||||
sample_count: u32,
|
||||
) -> (wgpu::Texture, wgpu::TextureView) {
|
||||
let tex = device.create_texture(&wgpu::TextureDescriptor {
|
||||
label: Some("MSAA depth texture"),
|
||||
size: wgpu::Extent3d { width, height, depth_or_array_layers: 1 },
|
||||
mip_level_count: 1,
|
||||
sample_count,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: DEPTH_FORMAT,
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||
view_formats: &[],
|
||||
});
|
||||
let view = tex.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
(tex, view)
|
||||
}
|
||||
|
||||
/// Allocates the shadow-map texture + view backing the depth-only shadow pass's
|
||||
/// `depth_stencil_attachment` (Step 14, D2/D8). Square (`size` x `size`), `DEPTH_FORMAT`, single
|
||||
/// mip, no MSAA. Unlike the screen depth texture this one is flagged **both** `RENDER_ATTACHMENT`
|
||||
|
||||
Reference in New Issue
Block a user