Skip to content

Commit e8bd8e9

Browse files
committed
[add] offscreen render target implementation to the render context and command encoder.
1 parent d67fe98 commit e8bd8e9

File tree

5 files changed

+681
-269
lines changed

5 files changed

+681
-269
lines changed

crates/lambda-rs/src/render/color_attachments.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,4 +91,33 @@ impl<'view> RenderColorAttachments<'view> {
9191

9292
return attachments;
9393
}
94+
95+
/// Build color attachments for an offscreen render pass.
96+
///
97+
/// This helper configures single-sample or multi-sample color attachments
98+
/// targeting an offscreen resolve texture. When MSAA is enabled, the
99+
/// `msaa_view` is used as the multi-sampled render target and `resolve_view`
100+
/// receives the resolved output.
101+
pub(crate) fn for_offscreen_pass(
102+
uses_color: bool,
103+
sample_count: u32,
104+
msaa_view: Option<TextureView<'view>>,
105+
resolve_view: TextureView<'view>,
106+
) -> Self {
107+
let mut attachments = RenderColorAttachments::new();
108+
109+
if !uses_color {
110+
return attachments;
111+
}
112+
113+
if sample_count > 1 {
114+
let msaa =
115+
msaa_view.expect("MSAA view must be provided when sample_count > 1");
116+
attachments.push_msaa_color(msaa, resolve_view);
117+
} else {
118+
attachments.push_color(resolve_view);
119+
}
120+
121+
return attachments;
122+
}
94123
}

crates/lambda-rs/src/render/encoder.rs

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,12 @@
1515
//!
1616
//! ```ignore
1717
//! let mut encoder = CommandEncoder::new(&render_context, "frame-encoder");
18-
//! encoder.with_render_pass(&pass, &mut attachments, depth, |rp_encoder| {
18+
//! encoder.with_render_pass(
19+
//! &pass,
20+
//! RenderPassDestinationInfo { color_format: None, depth_format: None },
21+
//! &mut attachments,
22+
//! depth,
23+
//! |rp_encoder| {
1924
//! rp_encoder.set_pipeline(&pipeline)?;
2025
//! rp_encoder.draw(0..3, 0..1)?;
2126
//! Ok(())
@@ -42,7 +47,11 @@ use super::{
4247
pipeline,
4348
pipeline::RenderPipeline,
4449
render_pass::RenderPass,
45-
texture::DepthTexture,
50+
texture::{
51+
DepthFormat,
52+
DepthTexture,
53+
TextureFormat,
54+
},
4655
validation,
4756
viewport::Viewport,
4857
RenderContext,
@@ -53,6 +62,13 @@ use crate::util;
5362
// CommandEncoder
5463
// ---------------------------------------------------------------------------
5564

65+
/// Destination metadata needed for render-target compatibility validation.
66+
#[derive(Clone, Copy, Debug)]
67+
pub(crate) struct RenderPassDestinationInfo {
68+
pub(crate) color_format: Option<TextureFormat>,
69+
pub(crate) depth_format: Option<DepthFormat>,
70+
}
71+
5672
/// High-level command encoder for recording GPU work.
5773
///
5874
/// Created per-frame via `CommandEncoder::new()`. Commands are recorded by
@@ -99,6 +115,7 @@ impl CommandEncoder {
99115
pub(crate) fn with_render_pass<'pass, PassFn, Output>(
100116
&'pass mut self,
101117
pass: &'pass RenderPass,
118+
destination_info: RenderPassDestinationInfo,
102119
color_attachments: &'pass mut RenderColorAttachments<'pass>,
103120
depth_texture: Option<&'pass DepthTexture>,
104121
func: PassFn,
@@ -110,6 +127,7 @@ impl CommandEncoder {
110127
let pass_encoder = RenderPassEncoder::new(
111128
&mut self.inner,
112129
pass,
130+
destination_info,
113131
color_attachments,
114132
depth_texture,
115133
);
@@ -162,6 +180,10 @@ pub struct RenderPassEncoder<'pass> {
162180
has_stencil: bool,
163181
/// Sample count for MSAA validation.
164182
sample_count: u32,
183+
/// Destination color format when the pass has color output.
184+
destination_color_format: Option<TextureFormat>,
185+
/// Destination depth format when a depth attachment is present.
186+
destination_depth_format: Option<DepthFormat>,
165187

166188
// Validation state (compiled out in release without features)
167189
#[cfg(any(debug_assertions, feature = "render-validation-encoder"))]
@@ -206,6 +228,7 @@ impl<'pass> RenderPassEncoder<'pass> {
206228
fn new(
207229
encoder: &'pass mut platform::command::CommandEncoder,
208230
pass: &'pass RenderPass,
231+
destination_info: RenderPassDestinationInfo,
209232
color_attachments: &'pass mut RenderColorAttachments<'pass>,
210233
depth_texture: Option<&'pass DepthTexture>,
211234
) -> Self {
@@ -243,6 +266,8 @@ impl<'pass> RenderPassEncoder<'pass> {
243266
has_depth_attachment,
244267
has_stencil,
245268
sample_count: pass.sample_count(),
269+
destination_color_format: destination_info.color_format,
270+
destination_depth_format: destination_info.depth_format,
246271
#[cfg(any(debug_assertions, feature = "render-validation-encoder"))]
247272
current_pipeline: None,
248273
#[cfg(any(debug_assertions, feature = "render-validation-encoder"))]
@@ -304,6 +329,46 @@ impl<'pass> RenderPassEncoder<'pass> {
304329
}
305330
}
306331

332+
#[cfg(any(debug_assertions, feature = "render-validation-render-targets",))]
333+
{
334+
let label = pipeline.pipeline().label().unwrap_or("unnamed");
335+
336+
if pipeline.sample_count() != self.sample_count {
337+
return Err(RenderPassError::PipelineIncompatible(format!(
338+
"Render pipeline '{}' has sample_count={} but pass sample_count={}",
339+
label,
340+
pipeline.sample_count(),
341+
self.sample_count
342+
)));
343+
}
344+
345+
if self.uses_color {
346+
if let Some(dest_format) = self.destination_color_format {
347+
if pipeline.color_target_format() != Some(dest_format) {
348+
return Err(RenderPassError::PipelineIncompatible(format!(
349+
"Render pipeline '{}' color format {:?} does not match destination color format {:?}",
350+
label,
351+
pipeline.color_target_format(),
352+
dest_format
353+
)));
354+
}
355+
}
356+
}
357+
358+
if self.has_depth_attachment && pipeline.expects_depth_stencil() {
359+
if let Some(dest_depth_format) = self.destination_depth_format {
360+
if pipeline.depth_format() != Some(dest_depth_format) {
361+
return Err(RenderPassError::PipelineIncompatible(format!(
362+
"Render pipeline '{}' depth format {:?} does not match destination depth format {:?}",
363+
label,
364+
pipeline.depth_format(),
365+
dest_depth_format
366+
)));
367+
}
368+
}
369+
}
370+
}
371+
307372
// Track current pipeline for draw validation
308373
#[cfg(any(debug_assertions, feature = "render-validation-encoder"))]
309374
{

0 commit comments

Comments
 (0)