Skip to content

Commit 1cca6eb

Browse files
authored
feat(lambda-rs): Add instanced rendering
2 parents afabc05 + d457b83 commit 1cca6eb

File tree

13 files changed

+1668
-23
lines changed

13 files changed

+1668
-23
lines changed

crates/lambda-rs-platform/src/wgpu/pipeline.rs

Lines changed: 71 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use std::ops::Range;
44

55
use wgpu;
66

7+
pub use crate::wgpu::vertex::VertexStepMode;
78
use crate::wgpu::{
89
bind,
910
gpu::Gpu,
@@ -81,6 +82,14 @@ pub struct VertexAttributeDesc {
8182
pub format: ColorFormat,
8283
}
8384

85+
/// Description of a single vertex buffer layout used by a pipeline.
86+
#[derive(Clone, Debug)]
87+
struct VertexBufferLayoutDesc {
88+
array_stride: u64,
89+
step_mode: VertexStepMode,
90+
attributes: Vec<VertexAttributeDesc>,
91+
}
92+
8493
/// Compare function used for depth and stencil tests.
8594
#[derive(Clone, Copy, Debug)]
8695
pub enum CompareFunction {
@@ -301,7 +310,7 @@ impl RenderPipeline {
301310
pub struct RenderPipelineBuilder<'a> {
302311
label: Option<String>,
303312
layout: Option<&'a wgpu::PipelineLayout>,
304-
vertex_buffers: Vec<(u64, Vec<VertexAttributeDesc>)>,
313+
vertex_buffers: Vec<VertexBufferLayoutDesc>,
305314
cull_mode: CullingMode,
306315
color_target_format: Option<wgpu::TextureFormat>,
307316
depth_stencil: Option<wgpu::DepthStencilState>,
@@ -340,7 +349,26 @@ impl<'a> RenderPipelineBuilder<'a> {
340349
array_stride: u64,
341350
attributes: Vec<VertexAttributeDesc>,
342351
) -> Self {
343-
self.vertex_buffers.push((array_stride, attributes));
352+
self = self.with_vertex_buffer_step_mode(
353+
array_stride,
354+
VertexStepMode::Vertex,
355+
attributes,
356+
);
357+
return self;
358+
}
359+
360+
/// Add a vertex buffer layout with attributes and an explicit step mode.
361+
pub fn with_vertex_buffer_step_mode(
362+
mut self,
363+
array_stride: u64,
364+
step_mode: VertexStepMode,
365+
attributes: Vec<VertexAttributeDesc>,
366+
) -> Self {
367+
self.vertex_buffers.push(VertexBufferLayoutDesc {
368+
array_stride,
369+
step_mode,
370+
attributes,
371+
});
344372
return self;
345373
}
346374

@@ -431,11 +459,12 @@ impl<'a> RenderPipelineBuilder<'a> {
431459
// storage stable for layout lifetimes.
432460
let mut attr_storage: Vec<Box<[wgpu::VertexAttribute]>> = Vec::new();
433461
let mut strides: Vec<u64> = Vec::new();
434-
for (stride, attrs) in &self.vertex_buffers {
462+
let mut step_modes: Vec<VertexStepMode> = Vec::new();
463+
for buffer_desc in &self.vertex_buffers {
435464
let mut raw_attrs: Vec<wgpu::VertexAttribute> =
436-
Vec::with_capacity(attrs.len());
465+
Vec::with_capacity(buffer_desc.attributes.len());
437466

438-
for attribute in attrs.iter() {
467+
for attribute in buffer_desc.attributes.iter() {
439468
raw_attrs.push(wgpu::VertexAttribute {
440469
shader_location: attribute.shader_location,
441470
offset: attribute.offset,
@@ -444,15 +473,16 @@ impl<'a> RenderPipelineBuilder<'a> {
444473
}
445474
let boxed: Box<[wgpu::VertexAttribute]> = raw_attrs.into_boxed_slice();
446475
attr_storage.push(boxed);
447-
strides.push(*stride);
476+
strides.push(buffer_desc.array_stride);
477+
step_modes.push(buffer_desc.step_mode);
448478
}
449479
// Now build layouts referencing the stable storage in `attr_storage`.
450480
let mut vbl: Vec<wgpu::VertexBufferLayout<'_>> = Vec::new();
451481
for (i, boxed) in attr_storage.iter().enumerate() {
452482
let slice = boxed.as_ref();
453483
vbl.push(wgpu::VertexBufferLayout {
454484
array_stride: strides[i],
455-
step_mode: wgpu::VertexStepMode::Vertex,
485+
step_mode: step_modes[i].to_wgpu(),
456486
attributes: slice,
457487
});
458488
}
@@ -511,3 +541,37 @@ impl<'a> RenderPipelineBuilder<'a> {
511541
};
512542
}
513543
}
544+
545+
#[cfg(test)]
546+
mod tests {
547+
use super::*;
548+
549+
#[test]
550+
fn vertex_step_mode_maps_to_wgpu() {
551+
let vertex_mode = VertexStepMode::Vertex.to_wgpu();
552+
let instance_mode = VertexStepMode::Instance.to_wgpu();
553+
554+
assert_eq!(vertex_mode, wgpu::VertexStepMode::Vertex);
555+
assert_eq!(instance_mode, wgpu::VertexStepMode::Instance);
556+
}
557+
558+
#[test]
559+
fn with_vertex_buffer_defaults_to_per_vertex_step_mode() {
560+
let builder = RenderPipelineBuilder::new().with_vertex_buffer(
561+
16,
562+
vec![VertexAttributeDesc {
563+
shader_location: 0,
564+
offset: 0,
565+
format: ColorFormat::Rgb32Sfloat,
566+
}],
567+
);
568+
569+
let vertex_buffers = &builder.vertex_buffers;
570+
571+
assert_eq!(vertex_buffers.len(), 1);
572+
assert!(matches!(
573+
vertex_buffers[0].step_mode,
574+
VertexStepMode::Vertex
575+
));
576+
}
577+
}

crates/lambda-rs-platform/src/wgpu/vertex.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,24 @@ impl ColorFormat {
2626
};
2727
}
2828
}
29+
30+
/// Step mode applied to a vertex buffer layout.
31+
///
32+
/// `Vertex` advances attributes per vertex; `Instance` advances attributes per
33+
/// instance. This mirrors `wgpu::VertexStepMode` without exposing the raw
34+
/// dependency to higher layers.
35+
#[derive(Clone, Copy, Debug)]
36+
pub enum VertexStepMode {
37+
Vertex,
38+
Instance,
39+
}
40+
41+
impl VertexStepMode {
42+
/// Map the engine step mode to the underlying graphics API.
43+
pub(crate) fn to_wgpu(self) -> wgpu::VertexStepMode {
44+
return match self {
45+
VertexStepMode::Vertex => wgpu::VertexStepMode::Vertex,
46+
VertexStepMode::Instance => wgpu::VertexStepMode::Instance,
47+
};
48+
}
49+
}

crates/lambda-rs/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ render-validation-strict = [
5858
render-validation-all = [
5959
"render-validation-strict",
6060
"render-validation-device",
61+
"render-validation-instancing",
6162
]
6263

6364
# Granular feature flags
@@ -73,6 +74,7 @@ render-validation-stencil = []
7374
render-validation-pass-compat = []
7475
render-validation-device = []
7576
render-validation-encoder = []
77+
render-validation-instancing = []
7678

7779

7880
# ---------------------------- PLATFORM DEPENDENCIES ---------------------------

0 commit comments

Comments
 (0)