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
|
// 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).
|
// 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);
|
renderer.set_unlit(true);
|
||||||
|
|
||||||
// 3. Material : On utilise renderer.device() et renderer.format()
|
// 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)
|
.configure(&context.adapter, self.width, self.height)
|
||||||
.expect("Échec configuration surface");
|
.expect("Échec configuration surface");
|
||||||
let device = Arc::new(context.device.clone());
|
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
|
// É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.
|
// (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::Context;
|
||||||
use crate::core::Frame;
|
use crate::core::Frame;
|
||||||
use crate::math::Transform;
|
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::uniform::{FRAME_UNIFORMS_SIZE, OBJECT_UNIFORM_SIZE};
|
||||||
use crate::resources::{Camera, FrameUniforms, Material, Mesh, ObjectUniform};
|
use crate::resources::{Camera, FrameUniforms, Material, Mesh, ObjectUniform};
|
||||||
use crate::scene::Scene;
|
use crate::scene::Scene;
|
||||||
@@ -42,6 +42,14 @@ pub struct Renderer {
|
|||||||
device: wgpu::Device,
|
device: wgpu::Device,
|
||||||
/// Surface texture output format — stored here so it can be passed to PipelineCache on Material creation.
|
/// Surface texture output format — stored here so it can be passed to PipelineCache on Material creation.
|
||||||
format: wgpu::TextureFormat,
|
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.
|
/// Bind group layout for the per-object uniforms (group 1) — must match every pipeline layout.
|
||||||
object_layout: wgpu::BindGroupLayout,
|
object_layout: wgpu::BindGroupLayout,
|
||||||
/// Shared per-frame uniform buffer handle — kept so the camera matrices can be rewritten each
|
/// Shared per-frame uniform buffer handle — kept so the camera matrices can be rewritten each
|
||||||
@@ -64,15 +72,21 @@ pub struct Renderer {
|
|||||||
impl Renderer {
|
impl Renderer {
|
||||||
/// Creates a Renderer by cloning Device and Queue Arc references from the Context, plus capturing
|
/// 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.
|
/// 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.
|
/// 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;
|
/// 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.
|
/// 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 queue: wgpu::Queue = context.queue.clone();
|
||||||
let device: wgpu::Device = context.device.clone();
|
let device: wgpu::Device = context.device.clone();
|
||||||
let [frame_layout, object_layout] = create_uniform_bind_group_layouts(&device);
|
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.
|
// 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
|
// 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.
|
// is a coherent scene when a shader actually reads them, and irrelevant to shaders that don't.
|
||||||
@@ -115,6 +129,8 @@ impl Renderer {
|
|||||||
queue,
|
queue,
|
||||||
device,
|
device,
|
||||||
format,
|
format,
|
||||||
|
_depth_texture: depth_texture,
|
||||||
|
depth_view,
|
||||||
object_layout,
|
object_layout,
|
||||||
frame_buffer,
|
frame_buffer,
|
||||||
frame_bind_group,
|
frame_bind_group,
|
||||||
@@ -200,6 +216,16 @@ impl Renderer {
|
|||||||
store: wgpu::StoreOp::Store,
|
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()
|
..Default::default()
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -245,6 +271,16 @@ impl Renderer {
|
|||||||
store: wgpu::StoreOp::Store,
|
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()
|
..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
|
/// 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`.
|
/// 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
|
/// The frame (group 0) and object (group 1) bind groups are **required** by every pipeline layout
|
||||||
|
|||||||
@@ -11,4 +11,4 @@
|
|||||||
|
|
||||||
pub mod pipeline_cache;
|
pub mod pipeline_cache;
|
||||||
// Re-exports
|
// 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.
|
/// 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.
|
/// Ensures each unique shader+format combination is compiled at most once; subsequent requests return cached instances.
|
||||||
pub struct PipelineCache {
|
pub struct PipelineCache {
|
||||||
@@ -230,7 +239,17 @@ impl PipelineCache {
|
|||||||
})],
|
})],
|
||||||
}),
|
}),
|
||||||
primitive: wgpu::PrimitiveState::default(),
|
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(),
|
multisample: wgpu::MultisampleState::default(),
|
||||||
// multiview → replaced by multiview_mask (NonZeroU32) and cache fields in wgpu 30.
|
// multiview → replaced by multiview_mask (NonZeroU32) and cache fields in wgpu 30.
|
||||||
multiview_mask: None,
|
multiview_mask: None,
|
||||||
|
|||||||
Reference in New Issue
Block a user