refactor(renderer): activate depth buffer on all render paths (Étape 9)
- 9.1: allocate shared Depth32Float depth texture + view in Renderer, sized to the initial surface (create_depth_texture helper, reusable for the Phase 4.4 resize); Renderer::new now takes width/height. - 9.2: attach depth_stencil_attachment (clear 1.0 / store) to both the low-level render and high-level render_scene passes. - 9.3: every pipeline declares a matching DepthStencilState (write true, compare Less) via the shared DEPTH_FORMAT constant (D1). Décisions D1-D4 actées 2026-09-18 (DRAFT Étape 9).
This commit is contained in:
@@ -64,7 +64,7 @@ impl ApplicationHandler for App {
|
||||
|
||||
// Rendu 2D plat : `standard` en mode unlit (les bind groups frame+object sont posés par
|
||||
// draw_entity, la matrice frame par défaut est l'identité → positions NDC inchangées).
|
||||
let mut renderer = Renderer::new(&context, format);
|
||||
let mut renderer = Renderer::new(&context, format, 800, 600);
|
||||
renderer.set_unlit(true);
|
||||
|
||||
// 3. Material : On utilise renderer.device() et renderer.format()
|
||||
|
||||
+1
-1
@@ -233,7 +233,7 @@ impl<H: AppHandler> ApplicationHandler for AppRunner<H> {
|
||||
.configure(&context.adapter, self.width, self.height)
|
||||
.expect("Échec configuration surface");
|
||||
let device = Arc::new(context.device.clone());
|
||||
let renderer = Renderer::new(&context, format);
|
||||
let renderer = Renderer::new(&context, format, self.width, self.height);
|
||||
|
||||
// Étape 7 (DRAFT 7.1) : the PipelineCache now lives in the Scene. We wire the GPU context
|
||||
// (device + format + cache) into the Scene before setup so it can build materials/meshes.
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
use crate::core::Context;
|
||||
use crate::core::Frame;
|
||||
use crate::math::Transform;
|
||||
use crate::pipeline::create_uniform_bind_group_layouts;
|
||||
use crate::pipeline::{DEPTH_FORMAT, create_uniform_bind_group_layouts};
|
||||
use crate::resources::uniform::{FRAME_UNIFORMS_SIZE, OBJECT_UNIFORM_SIZE};
|
||||
use crate::resources::{Camera, FrameUniforms, Material, Mesh, ObjectUniform};
|
||||
use crate::scene::Scene;
|
||||
@@ -42,6 +42,14 @@ pub struct Renderer {
|
||||
device: wgpu::Device,
|
||||
/// Surface texture output format — stored here so it can be passed to PipelineCache on Material creation.
|
||||
format: wgpu::TextureFormat,
|
||||
/// z-buffer texture backing `depth_view` (Étape 9). Held here only to keep the GPU resource
|
||||
/// alive for the whole application lifetime (a `TextureView` alone does not guarantee the
|
||||
/// underlying `Texture` stays valid in wgpu). Not read directly (hence `_` prefix → no
|
||||
/// `dead_code`); reused when the depth texture is recreated at resize (ROADMAP Phase 4.4).
|
||||
_depth_texture: wgpu::Texture,
|
||||
/// Depth attachment view used by both render passes (`render`, `render_scene`). Format
|
||||
/// `DEPTH_FORMAT` (Depth32Float) — matches every pipeline's `DepthStencilState` (D1).
|
||||
depth_view: wgpu::TextureView,
|
||||
/// Bind group layout for the per-object uniforms (group 1) — must match every pipeline layout.
|
||||
object_layout: wgpu::BindGroupLayout,
|
||||
/// Shared per-frame uniform buffer handle — kept so the camera matrices can be rewritten each
|
||||
@@ -64,15 +72,21 @@ pub struct Renderer {
|
||||
impl Renderer {
|
||||
/// Creates a Renderer by cloning Device and Queue Arc references from the Context, plus capturing
|
||||
/// the surface format, and allocates the shared frame + object uniform buffers and their bind groups.
|
||||
/// Inputs: context (borrowed reference to Context providing GPU resource handles), format (surface texture format).
|
||||
/// Inputs: context (borrowed reference to Context providing GPU resource handles), format (surface
|
||||
/// texture format), width (surface width in pixels) and height (surface height in pixels) — the
|
||||
/// latter two size the depth texture allocated here (Étape 9).
|
||||
/// Returns a new Renderer instance sharing the same underlying GPU resources as Context.
|
||||
/// Called once at application startup during scene setup. The Renderer shares these resources via Arc;
|
||||
/// Context retains ownership and can continue using them after this call.
|
||||
pub fn new(context: &Context, format: wgpu::TextureFormat) -> Self {
|
||||
pub fn new(context: &Context, format: wgpu::TextureFormat, width: u32, height: u32) -> Self {
|
||||
let queue: wgpu::Queue = context.queue.clone();
|
||||
let device: wgpu::Device = context.device.clone();
|
||||
let [frame_layout, object_layout] = create_uniform_bind_group_layouts(&device);
|
||||
|
||||
// Étape 9 (DRAFT 9.1) : depth texture + view, allouées une seule fois à la taille initiale
|
||||
// de la surface (D3). Le helper isolé rendra trivial le recreate planifié en Phase 4.4.
|
||||
let (depth_texture, depth_view) = create_depth_texture(&device, width, height);
|
||||
|
||||
// Shared frame uniforms: identity camera + white directional light, lit mode by default.
|
||||
// Values become meaningful once an active camera is wired (Étape 4.3); for now the default
|
||||
// is a coherent scene when a shader actually reads them, and irrelevant to shaders that don't.
|
||||
@@ -115,6 +129,8 @@ impl Renderer {
|
||||
queue,
|
||||
device,
|
||||
format,
|
||||
_depth_texture: depth_texture,
|
||||
depth_view,
|
||||
object_layout,
|
||||
frame_buffer,
|
||||
frame_bind_group,
|
||||
@@ -200,6 +216,16 @@ impl Renderer {
|
||||
store: wgpu::StoreOp::Store,
|
||||
},
|
||||
})],
|
||||
// Étape 9 (DRAFT 9.2) : depth attachment via la view partagée (D1 : clear 1.0
|
||||
// = profondeur max au loin en début de frame, puis Store pour la garder).
|
||||
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
|
||||
view: &self.depth_view,
|
||||
depth_ops: Some(wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(1.0),
|
||||
store: wgpu::StoreOp::Store,
|
||||
}),
|
||||
stencil_ops: None,
|
||||
}),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
@@ -245,6 +271,16 @@ impl Renderer {
|
||||
store: wgpu::StoreOp::Store,
|
||||
},
|
||||
})],
|
||||
// Étape 9 (DRAFT 9.2) : même depth attachment que le chemin bas niveau, pour un
|
||||
// z-test cohérent (D2 — les deux render passes partagent la depth_view).
|
||||
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
|
||||
view: &self.depth_view,
|
||||
depth_ops: Some(wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(1.0),
|
||||
store: wgpu::StoreOp::Store,
|
||||
}),
|
||||
stencil_ops: None,
|
||||
}),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
@@ -321,6 +357,38 @@ impl Renderer {
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocates the depth texture + view backing the render passes' `depth_stencil_attachment`
|
||||
/// (Étape 9, DRAFT 9.1). Format is the shared `DEPTH_FORMAT` (Depth32Float, D1) so it always
|
||||
/// matches every pipeline's `DepthStencilState`. Sized to the surface (width x height), single
|
||||
/// mip, no MSAA, used strictly as a render target.
|
||||
///
|
||||
/// Exposed as a standalone helper so the depth texture can be recreated cheaply at resize
|
||||
/// (ROADMAP Phase 4.4) without touching the render-pass logic.
|
||||
/// Inputs: device (GPU resource creator), width (surface width), height (surface height).
|
||||
/// Returns the (texture, view) pair; the caller keeps both alive.
|
||||
fn create_depth_texture(
|
||||
device: &wgpu::Device,
|
||||
width: u32,
|
||||
height: u32,
|
||||
) -> (wgpu::Texture, wgpu::TextureView) {
|
||||
let depth_texture = device.create_texture(&wgpu::TextureDescriptor {
|
||||
label: Some("depth texture"),
|
||||
size: wgpu::Extent3d {
|
||||
width,
|
||||
height,
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: DEPTH_FORMAT,
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||
view_formats: &[],
|
||||
});
|
||||
let depth_view = depth_texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
(depth_texture, depth_view)
|
||||
}
|
||||
|
||||
/// Binds a Material pipeline, the two uniform bind groups, and Mesh buffers into an active render
|
||||
/// pass and issues the draw call. Shared by `Renderer::render` and `Renderer::render_scene`.
|
||||
/// The frame (group 0) and object (group 1) bind groups are **required** by every pipeline layout
|
||||
|
||||
@@ -11,4 +11,4 @@
|
||||
|
||||
pub mod pipeline_cache;
|
||||
// Re-exports
|
||||
pub use pipeline_cache::{PipelineCache, create_uniform_bind_group_layouts};
|
||||
pub use pipeline_cache::{DEPTH_FORMAT, PipelineCache, create_uniform_bind_group_layouts};
|
||||
|
||||
@@ -59,6 +59,15 @@ pub fn create_uniform_bind_group_layouts(device: &wgpu::Device) -> [wgpu::BindGr
|
||||
]
|
||||
}
|
||||
|
||||
/// Depth texture format shared by the whole library (Étape 9, décision D1 du 2026-09-18).
|
||||
///
|
||||
/// Single z-buffer format used for **both** the depth attachment textures (`Renderer`) and the
|
||||
/// `DepthStencilState` of every pipeline (`build_pipeline`). Keeping them on the same constant
|
||||
/// guarantees by construction that the pipeline depth format always matches the texture format
|
||||
/// (wgpu validation error otherwise). `Depth32Float` = portée maximale (comparaison précise),
|
||||
/// avec clear `1.0` (profondeur maximale au loin), `depth_compare: Less`, write enabled.
|
||||
pub const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float;
|
||||
|
||||
/// Shader pipeline cache: maps (shader_id, format) keys to compiled RenderPipelines.
|
||||
/// Ensures each unique shader+format combination is compiled at most once; subsequent requests return cached instances.
|
||||
pub struct PipelineCache {
|
||||
@@ -230,7 +239,17 @@ impl PipelineCache {
|
||||
})],
|
||||
}),
|
||||
primitive: wgpu::PrimitiveState::default(),
|
||||
depth_stencil: None,
|
||||
// Étape 9 (DRAFT 9.3) : depth test activé sur TOUTE pipeline. Le format doit matcher
|
||||
// l'attachment depth (DEPTH_FORMAT) — c'est garanti par la constante partagée D1.
|
||||
// depth_write_enabled + depth_compare sont des Option en wgpu 30 : Some(true) → on
|
||||
// écrit la profondeur ; Some(Less) → le fragment est gardé si son z est plus proche.
|
||||
depth_stencil: Some(wgpu::DepthStencilState {
|
||||
format: DEPTH_FORMAT,
|
||||
depth_write_enabled: Some(true),
|
||||
depth_compare: Some(wgpu::CompareFunction::Less),
|
||||
stencil: wgpu::StencilState::default(),
|
||||
bias: wgpu::DepthBiasState::default(),
|
||||
}),
|
||||
multisample: wgpu::MultisampleState::default(),
|
||||
// multiview → replaced by multiview_mask (NonZeroU32) and cache fields in wgpu 30.
|
||||
multiview_mask: None,
|
||||
|
||||
Reference in New Issue
Block a user