Skip to content

Commit bc2ca68

Browse files
committed
[fix] tests.
1 parent bc191ae commit bc2ca68

File tree

1 file changed

+191
-16
lines changed

1 file changed

+191
-16
lines changed

crates/lambda-rs/src/render/targets/offscreen.rs

Lines changed: 191 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,36 @@ pub enum OffscreenTargetError {
101101
DeviceError(String),
102102
}
103103

104+
impl std::fmt::Display for OffscreenTargetError {
105+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106+
return match self {
107+
OffscreenTargetError::MissingColorAttachment => {
108+
write!(f, "Missing color attachment configuration")
109+
}
110+
OffscreenTargetError::InvalidSize { width, height } => write!(
111+
f,
112+
"Invalid offscreen target size {}x{} (width and height must be > 0)",
113+
width, height
114+
),
115+
OffscreenTargetError::UnsupportedSampleCount { requested } => {
116+
write!(
117+
f,
118+
"Unsupported sample count {} (allowed: 1, 2, 4, 8)",
119+
requested
120+
)
121+
}
122+
OffscreenTargetError::UnsupportedFormat { message } => {
123+
write!(f, "Unsupported format: {}", message)
124+
}
125+
OffscreenTargetError::DeviceError(message) => {
126+
write!(f, "Device error: {}", message)
127+
}
128+
};
129+
}
130+
}
131+
132+
impl std::error::Error for OffscreenTargetError {}
133+
104134
/// Builder for creating an `OffscreenTarget`.
105135
pub struct OffscreenTargetBuilder {
106136
label: Option<String>,
@@ -207,7 +237,20 @@ impl OffscreenTargetBuilder {
207237
let resolve_texture = match color_builder.build(gpu) {
208238
Ok(texture) => texture,
209239
Err(message) => {
210-
return Err(OffscreenTargetError::DeviceError(message.to_string()));
240+
const UNSUPPORTED_FORMAT_MESSAGE: &str =
241+
"Texture format does not support bytes_per_pixel calculation";
242+
let error_message = message.to_string();
243+
if message == UNSUPPORTED_FORMAT_MESSAGE {
244+
return Err(OffscreenTargetError::UnsupportedFormat {
245+
message: error_message,
246+
});
247+
}
248+
249+
let label = self.label.as_deref().unwrap_or("unnamed offscreen target");
250+
return Err(OffscreenTargetError::DeviceError(format!(
251+
"Failed to build resolve color texture for '{}': {}",
252+
label, error_message
253+
)));
211254
}
212255
};
213256

@@ -264,24 +307,15 @@ impl OffscreenTargetBuilder {
264307
}
265308
}
266309

267-
#[deprecated(
268-
note = "Use `lambda::render::targets::offscreen::OffscreenTarget` to avoid confusion with `lambda::render::targets::surface::RenderTarget`."
269-
)]
270-
pub type RenderTarget = OffscreenTarget;
271-
272-
#[deprecated(
273-
note = "Use `lambda::render::targets::offscreen::OffscreenTargetBuilder` to avoid confusion with `lambda::render::targets::surface::RenderTarget`."
274-
)]
275-
pub type RenderTargetBuilder = OffscreenTargetBuilder;
276-
277-
#[deprecated(
278-
note = "Use `lambda::render::targets::offscreen::OffscreenTargetError`."
279-
)]
280-
pub type RenderTargetError = OffscreenTargetError;
281-
282310
#[cfg(test)]
283311
mod tests {
312+
use lambda_platform::wgpu as platform;
313+
284314
use super::*;
315+
use crate::render::{
316+
gpu::GpuBuilder,
317+
instance::InstanceBuilder,
318+
};
285319

286320
/// Fails when the builder has a zero dimension.
287321
#[test]
@@ -308,4 +342,145 @@ mod tests {
308342
let builder = OffscreenTargetBuilder::new().with_multi_sample(0);
309343
assert_eq!(builder.sample_count, 1);
310344
}
345+
346+
fn create_test_gpu() -> Option<Gpu> {
347+
let instance = InstanceBuilder::new()
348+
.with_label("lambda-offscreen-target-test-instance")
349+
.build();
350+
return GpuBuilder::new()
351+
.with_label("lambda-offscreen-target-test-gpu")
352+
.build(&instance, None)
353+
.ok();
354+
}
355+
356+
#[test]
357+
fn build_rejects_missing_color_attachment() {
358+
let gpu = match create_test_gpu() {
359+
Some(gpu) => gpu,
360+
None => return,
361+
};
362+
363+
let built = OffscreenTargetBuilder::new().build(&gpu);
364+
assert_eq!(
365+
built.unwrap_err(),
366+
OffscreenTargetError::MissingColorAttachment
367+
);
368+
}
369+
370+
#[test]
371+
fn build_rejects_unsupported_sample_count() {
372+
let gpu = match create_test_gpu() {
373+
Some(gpu) => gpu,
374+
None => return,
375+
};
376+
377+
let built = OffscreenTargetBuilder::new()
378+
.with_color(texture::TextureFormat::Rgba8Unorm, 1, 1)
379+
.with_multi_sample(3)
380+
.build(&gpu);
381+
382+
assert_eq!(
383+
built.unwrap_err(),
384+
OffscreenTargetError::UnsupportedSampleCount { requested: 3 }
385+
);
386+
}
387+
388+
#[test]
389+
fn resolve_texture_supports_sampling_and_render_attachment() {
390+
let gpu = match create_test_gpu() {
391+
Some(gpu) => gpu,
392+
None => return,
393+
};
394+
395+
let target = OffscreenTargetBuilder::new()
396+
.with_color(texture::TextureFormat::Rgba8Unorm, 4, 4)
397+
.with_label("offscreen-usage-test")
398+
.build(&gpu)
399+
.expect("build offscreen target");
400+
401+
let resolve_platform_texture = target.color_texture().platform_texture();
402+
403+
let sampler = platform::texture::SamplerBuilder::new()
404+
.nearest_clamp()
405+
.with_label("offscreen-usage-sampler")
406+
.build(gpu.platform());
407+
408+
let layout = platform::bind::BindGroupLayoutBuilder::new()
409+
.with_sampled_texture_2d(1, platform::bind::Visibility::Fragment)
410+
.with_sampler(2, platform::bind::Visibility::Fragment)
411+
.build(gpu.platform());
412+
413+
let _group = platform::bind::BindGroupBuilder::new()
414+
.with_layout(&layout)
415+
.with_texture(1, resolve_platform_texture.as_ref())
416+
.with_sampler(2, &sampler)
417+
.build(gpu.platform());
418+
419+
let mut encoder = platform::command::CommandEncoder::new(
420+
gpu.platform(),
421+
Some("offscreen-usage-encoder"),
422+
);
423+
{
424+
let mut attachments =
425+
platform::render_pass::RenderColorAttachments::new();
426+
attachments.push_color(target.resolve_view().to_platform());
427+
let _pass = platform::render_pass::RenderPassBuilder::new()
428+
.with_clear_color([0.0, 0.0, 0.0, 1.0])
429+
.build(&mut encoder, &mut attachments, None, None, None, None);
430+
}
431+
432+
let buffer = encoder.finish();
433+
gpu.platform().submit(std::iter::once(buffer));
434+
}
435+
436+
#[test]
437+
fn msaa_target_depth_attachment_matches_sample_count() {
438+
let gpu = match create_test_gpu() {
439+
Some(gpu) => gpu,
440+
None => return,
441+
};
442+
443+
let target = OffscreenTargetBuilder::new()
444+
.with_color(texture::TextureFormat::Rgba8Unorm, 4, 4)
445+
.with_depth(texture::DepthFormat::Depth32Float)
446+
.with_multi_sample(4)
447+
.with_label("offscreen-msaa-depth-test")
448+
.build(&gpu)
449+
.expect("build offscreen target");
450+
451+
let msaa_view = target.msaa_view().expect("MSAA view");
452+
let resolve_view = target.resolve_view();
453+
let depth_view = target
454+
.depth_texture()
455+
.expect("depth texture")
456+
.platform_view_ref();
457+
458+
let mut encoder = platform::command::CommandEncoder::new(
459+
gpu.platform(),
460+
Some("offscreen-msaa-depth-encoder"),
461+
);
462+
{
463+
let mut attachments =
464+
platform::render_pass::RenderColorAttachments::new();
465+
attachments
466+
.push_msaa_color(msaa_view.to_platform(), resolve_view.to_platform());
467+
let depth_ops = Some(platform::render_pass::DepthOperations {
468+
load: platform::render_pass::DepthLoadOp::Clear(1.0),
469+
store: platform::render_pass::StoreOp::Store,
470+
});
471+
let _pass = platform::render_pass::RenderPassBuilder::new()
472+
.with_clear_color([0.0, 0.0, 0.0, 1.0])
473+
.build(
474+
&mut encoder,
475+
&mut attachments,
476+
Some(depth_view),
477+
depth_ops,
478+
None,
479+
None,
480+
);
481+
}
482+
483+
let buffer = encoder.finish();
484+
gpu.platform().submit(std::iter::once(buffer));
485+
}
311486
}

0 commit comments

Comments
 (0)