Skip to content
Merged
78 changes: 71 additions & 7 deletions crates/lambda-rs-platform/src/wgpu/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::ops::Range;

use wgpu;

pub use crate::wgpu::vertex::VertexStepMode;
use crate::wgpu::{
bind,
gpu::Gpu,
Expand Down Expand Up @@ -81,6 +82,14 @@ pub struct VertexAttributeDesc {
pub format: ColorFormat,
}

/// Description of a single vertex buffer layout used by a pipeline.
#[derive(Clone, Debug)]
struct VertexBufferLayoutDesc {
array_stride: u64,
step_mode: VertexStepMode,
attributes: Vec<VertexAttributeDesc>,
}

/// Compare function used for depth and stencil tests.
#[derive(Clone, Copy, Debug)]
pub enum CompareFunction {
Expand Down Expand Up @@ -301,7 +310,7 @@ impl RenderPipeline {
pub struct RenderPipelineBuilder<'a> {
label: Option<String>,
layout: Option<&'a wgpu::PipelineLayout>,
vertex_buffers: Vec<(u64, Vec<VertexAttributeDesc>)>,
vertex_buffers: Vec<VertexBufferLayoutDesc>,
cull_mode: CullingMode,
color_target_format: Option<wgpu::TextureFormat>,
depth_stencil: Option<wgpu::DepthStencilState>,
Expand Down Expand Up @@ -340,7 +349,26 @@ impl<'a> RenderPipelineBuilder<'a> {
array_stride: u64,
attributes: Vec<VertexAttributeDesc>,
) -> Self {
self.vertex_buffers.push((array_stride, attributes));
self = self.with_vertex_buffer_step_mode(
array_stride,
VertexStepMode::Vertex,
attributes,
);
return self;
}

/// Add a vertex buffer layout with attributes and an explicit step mode.
pub fn with_vertex_buffer_step_mode(
mut self,
array_stride: u64,
step_mode: VertexStepMode,
attributes: Vec<VertexAttributeDesc>,
) -> Self {
self.vertex_buffers.push(VertexBufferLayoutDesc {
array_stride,
step_mode,
attributes,
});
return self;
}

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

for attribute in attrs.iter() {
for attribute in buffer_desc.attributes.iter() {
raw_attrs.push(wgpu::VertexAttribute {
shader_location: attribute.shader_location,
offset: attribute.offset,
Expand All @@ -444,15 +473,16 @@ impl<'a> RenderPipelineBuilder<'a> {
}
let boxed: Box<[wgpu::VertexAttribute]> = raw_attrs.into_boxed_slice();
attr_storage.push(boxed);
strides.push(*stride);
strides.push(buffer_desc.array_stride);
step_modes.push(buffer_desc.step_mode);
}
// Now build layouts referencing the stable storage in `attr_storage`.
let mut vbl: Vec<wgpu::VertexBufferLayout<'_>> = Vec::new();
for (i, boxed) in attr_storage.iter().enumerate() {
let slice = boxed.as_ref();
vbl.push(wgpu::VertexBufferLayout {
array_stride: strides[i],
step_mode: wgpu::VertexStepMode::Vertex,
step_mode: step_modes[i].to_wgpu(),
attributes: slice,
});
}
Expand Down Expand Up @@ -511,3 +541,37 @@ impl<'a> RenderPipelineBuilder<'a> {
};
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn vertex_step_mode_maps_to_wgpu() {
let vertex_mode = VertexStepMode::Vertex.to_wgpu();
let instance_mode = VertexStepMode::Instance.to_wgpu();

assert_eq!(vertex_mode, wgpu::VertexStepMode::Vertex);
assert_eq!(instance_mode, wgpu::VertexStepMode::Instance);
}

#[test]
fn with_vertex_buffer_defaults_to_per_vertex_step_mode() {
let builder = RenderPipelineBuilder::new().with_vertex_buffer(
16,
vec![VertexAttributeDesc {
shader_location: 0,
offset: 0,
format: ColorFormat::Rgb32Sfloat,
}],
);

let vertex_buffers = &builder.vertex_buffers;

assert_eq!(vertex_buffers.len(), 1);
assert!(matches!(
vertex_buffers[0].step_mode,
VertexStepMode::Vertex
));
}
}
21 changes: 21 additions & 0 deletions crates/lambda-rs-platform/src/wgpu/vertex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,24 @@ impl ColorFormat {
};
}
}

/// Step mode applied to a vertex buffer layout.
///
/// `Vertex` advances attributes per vertex; `Instance` advances attributes per
/// instance. This mirrors `wgpu::VertexStepMode` without exposing the raw
/// dependency to higher layers.
#[derive(Clone, Copy, Debug)]
pub enum VertexStepMode {
Vertex,
Instance,
}

impl VertexStepMode {
/// Map the engine step mode to the underlying graphics API.
pub(crate) fn to_wgpu(self) -> wgpu::VertexStepMode {
return match self {
VertexStepMode::Vertex => wgpu::VertexStepMode::Vertex,
VertexStepMode::Instance => wgpu::VertexStepMode::Instance,
};
}
}
2 changes: 2 additions & 0 deletions crates/lambda-rs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ render-validation-strict = [
render-validation-all = [
"render-validation-strict",
"render-validation-device",
"render-validation-instancing",
]

# Granular feature flags
Expand All @@ -73,6 +74,7 @@ render-validation-stencil = []
render-validation-pass-compat = []
render-validation-device = []
render-validation-encoder = []
render-validation-instancing = []


# ---------------------------- PLATFORM DEPENDENCIES ---------------------------
Expand Down
Loading
Loading