Skip to content

Commit 3018d8c

Browse files
committed
[update] pipeline so that the fragment shader is optional, add render commands & push constant implementation for the new demo.
1 parent 96fd91c commit 3018d8c

6 files changed

Lines changed: 173 additions & 35 deletions

File tree

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

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ impl<RenderBackend: internal::Backend> RenderPipelineBuilder<RenderBackend> {
8585
gpu: &Gpu<RenderBackend>,
8686
render_pass: &super::render_pass::RenderPass<RenderBackend>,
8787
vertex_shader: &ShaderModule<RenderBackend>,
88-
fragment_shader: &ShaderModule<RenderBackend>,
88+
fragment_shader: Option<&ShaderModule<RenderBackend>>,
8989
) -> RenderPipeline<RenderBackend> {
9090
// TODO(vmarcella): The pipeline layout should be configurable through the
9191
// RenderPipelineBuilder.
@@ -104,10 +104,13 @@ impl<RenderBackend: internal::Backend> RenderPipelineBuilder<RenderBackend> {
104104
let primitive_assembler =
105105
super::assembler::PrimitiveAssemblerBuilder::new().build(vertex_shader);
106106

107-
let fragment_entry = internal::EntryPoint {
108-
entry: fragment_shader.entry(),
109-
module: super::internal::module_for(fragment_shader),
110-
specialization: fragment_shader.specializations().clone(),
107+
let fragment_entry = match fragment_shader {
108+
Some(shader) => Some(internal::EntryPoint::<RenderBackend> {
109+
entry: "main",
110+
module: super::internal::module_for(shader),
111+
specialization: gfx_hal::pso::Specialization::default(),
112+
}),
113+
None => None,
111114
};
112115

113116
let mut pipeline_desc = internal::GraphicsPipelineDesc::new(
@@ -116,7 +119,7 @@ impl<RenderBackend: internal::Backend> RenderPipelineBuilder<RenderBackend> {
116119
cull_face: internal::Face::BACK,
117120
..internal::Rasterizer::FILL
118121
},
119-
Some(fragment_entry),
122+
fragment_entry,
120123
&pipeline_layout,
121124
internal::Subpass {
122125
index: 0,

lambda/examples/push_constants.rs

Lines changed: 135 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,41 @@
11
use lambda::{
22
core::{
33
component::Component,
4-
render::shader,
4+
render::{
5+
command::RenderCommand,
6+
pipeline::{
7+
RenderPipeline,
8+
RenderPipelineBuilder,
9+
},
10+
render_pass::{
11+
RenderPass,
12+
RenderPassBuilder,
13+
},
14+
shader::{
15+
Shader,
16+
ShaderBuilder,
17+
},
18+
viewport,
19+
ResourceId,
20+
},
521
runtime::start_runtime,
622
},
7-
math,
23+
math::{
24+
matrix::{
25+
self,
26+
Matrix,
27+
},
28+
vector::Vector,
29+
},
830
runtimes::GenericRuntimeBuilder,
931
};
32+
use lambda_platform::{
33+
gfx::pipeline::PipelineStage,
34+
shaderc::{
35+
ShaderKind,
36+
VirtualShader,
37+
},
38+
};
1039

1140
const VERTEX_SHADER_SOURCE: &str = r#"
1241
#version 450
@@ -29,14 +58,44 @@ void main() {
2958
3059
"#;
3160

32-
struct PushConstantsExample {}
61+
#[repr(C)]
62+
#[derive(Debug, Clone, Copy)]
63+
pub struct PushConstant {
64+
data: [f32; 4],
65+
render_matrix: [[f32; 4]; 4],
66+
}
67+
68+
pub struct PushConstantsExample {
69+
frame_number: u64,
70+
shader: Shader,
71+
render_pipeline: Option<ResourceId>,
72+
render_pass: Option<ResourceId>,
73+
}
74+
75+
pub fn push_constants_to_bytes(push_constants: &PushConstant) -> &[u32] {
76+
let bytes = unsafe {
77+
let size_in_bytes = std::mem::size_of::<PushConstant>();
78+
let size_in_u32 = size_in_bytes / std::mem::size_of::<u32>();
79+
let ptr = push_constants as *const PushConstant as *const u32;
80+
std::slice::from_raw_parts(ptr, size_in_u32)
81+
};
82+
83+
return bytes;
84+
}
3385

3486
impl Component for PushConstantsExample {
3587
fn on_attach(
3688
&mut self,
3789
render_context: &mut lambda::core::render::RenderContext,
3890
) {
39-
todo!()
91+
let render_pass = RenderPassBuilder::new().build(&render_context);
92+
let push_constant_size = std::mem::size_of::<PushConstant>() as u32;
93+
let pipeline = RenderPipelineBuilder::new()
94+
.with_push_constant(PipelineStage::VERTEX, push_constant_size)
95+
.build(render_context, &render_pass, &self.shader, None);
96+
97+
self.render_pass = Some(render_context.attach_render_pass(render_pass));
98+
self.render_pipeline = Some(render_context.attach_pipeline(pipeline));
4099
}
41100

42101
fn on_detach(
@@ -50,28 +109,94 @@ impl Component for PushConstantsExample {
50109
todo!()
51110
}
52111

112+
/// Update the frame number every frame.
53113
fn on_update(&mut self, last_frame: &std::time::Duration) {
54-
todo!()
114+
self.frame_number += 1;
55115
}
56116

57117
fn on_render(
58118
&mut self,
59119
render_context: &mut lambda::core::render::RenderContext,
60120
) -> Vec<lambda::core::render::command::RenderCommand> {
61-
use math::vector::Vector;
62121
let mut camera = [0.0, 0.0, -2.0];
63-
let view: [[f32; 4]; 4] = math::matrix::translation_matrix(camera);
122+
let view: [[f32; 4]; 4] = matrix::translation_matrix(camera);
123+
124+
// Create a projection matrix.
64125
let mut projection: [[f32; 4]; 4] =
65-
math::matrix::perspective_matrix(1.0 / 2.0, 1700.0 / 900.0, 0.1, 200.0);
126+
matrix::perspective_matrix(1.0 / 2.0, 1700.0 / 900.0, 0.1, 200.0);
66127
projection.as_mut()[1].as_mut()[1] *= -1.0;
67128

68-
todo!()
129+
// Rotate model.
130+
let model: [[f32; 4]; 4] = matrix::rotate_matrix(
131+
matrix::filled_matrix(4, 4, 1.0),
132+
[0.0, 1.0, 0.0],
133+
0.4 * self.frame_number as f32,
134+
);
135+
136+
// Create render matrix.
137+
let mesh_matrix = projection.multiply(&view).multiply(&model);
138+
139+
// Create viewport.
140+
let viewport = viewport::ViewportBuilder::new().build(800, 600);
141+
142+
let render_pipeline = self
143+
.render_pipeline
144+
.expect("No render pipeline actively set for rendering.");
145+
146+
let mut commands = vec![
147+
RenderCommand::SetViewports {
148+
start_at: 0,
149+
viewports: vec![viewport.clone()],
150+
},
151+
RenderCommand::SetScissors {
152+
start_at: 0,
153+
viewports: vec![viewport.clone()],
154+
},
155+
RenderCommand::SetPipeline {
156+
pipeline: render_pipeline.clone(),
157+
},
158+
RenderCommand::BeginRenderPass {
159+
render_pass: self
160+
.render_pass
161+
.expect("Cannot begin the render pass when it doesn't exist.")
162+
.clone(),
163+
viewport: viewport.clone(),
164+
},
165+
RenderCommand::PushConstants {
166+
pipeline: render_pipeline.clone(),
167+
stage: PipelineStage::VERTEX,
168+
offset: 0,
169+
bytes: Vec::from(push_constants_to_bytes(&PushConstant {
170+
data: [0.0, 0.0, 0.0, 0.0],
171+
render_matrix: mesh_matrix,
172+
})),
173+
},
174+
RenderCommand::Draw { vertices: 0..3 },
175+
];
176+
commands.push(RenderCommand::EndRenderPass);
177+
178+
return commands;
69179
}
70180
}
71181

72182
impl Default for PushConstantsExample {
73183
fn default() -> Self {
74-
return Self {};
184+
let triangle_in_3d = VirtualShader::Source {
185+
source: VERTEX_SHADER_SOURCE.to_string(),
186+
kind: ShaderKind::Vertex,
187+
entry_point: "main".to_string(),
188+
name: "push_constants".to_string(),
189+
};
190+
191+
let mut builder = ShaderBuilder::new();
192+
let shader = builder.build(triangle_in_3d);
193+
194+
return Self {
195+
frame_number: 0,
196+
shader,
197+
render_pipeline: None,
198+
render_pass: None,
199+
};
75200
}
76201
}
77202

lambda/src/core/render/pipeline.rs

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,19 +69,22 @@ impl RenderPipelineBuilder {
6969
render_context: &mut RenderContext,
7070
render_pass: &super::render_pass::RenderPass,
7171
vertex_shader: &Shader,
72-
fragment_shader: &Shader,
72+
fragment_shader: Option<&Shader>,
7373
) -> RenderPipeline {
7474
let vertex_shader_module = ShaderModuleBuilder::new().build(
7575
mut_gpu_from_context(render_context),
7676
&vertex_shader.as_binary(),
7777
ShaderModuleType::Vertex,
7878
);
7979

80-
let fragment_shader_module = ShaderModuleBuilder::new().build(
81-
mut_gpu_from_context(render_context),
82-
&fragment_shader.as_binary(),
83-
ShaderModuleType::Fragment,
84-
);
80+
let fragment_shader_module = match fragment_shader {
81+
Some(shader) => Some(ShaderModuleBuilder::new().build(
82+
mut_gpu_from_context(render_context),
83+
&shader.as_binary(),
84+
ShaderModuleType::Fragment,
85+
)),
86+
None => None,
87+
};
8588

8689
let render_pipeline =
8790
lambda_platform::gfx::pipeline::RenderPipelineBuilder::new()
@@ -90,11 +93,14 @@ impl RenderPipelineBuilder {
9093
gpu_from_context(render_context),
9194
&platform_render_pass_from_render_pass(render_pass),
9295
&vertex_shader_module,
93-
&fragment_shader_module,
96+
fragment_shader_module.as_ref(),
9497
);
9598

99+
// Clean up shader modules.
96100
vertex_shader_module.destroy(mut_gpu_from_context(render_context));
97-
fragment_shader_module.destroy(mut_gpu_from_context(render_context));
101+
if let Some(fragment_shader_module) = fragment_shader_module {
102+
fragment_shader_module.destroy(mut_gpu_from_context(render_context));
103+
}
98104

99105
return RenderPipeline {
100106
pipeline: Rc::new(render_pipeline),

lambda/src/math/matrix.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@
22
33
use lambda_platform::rand::get_uniformly_random_floats_between;
44

5-
use super::vector::Vector;
6-
use crate::{
7-
assert_approximately_equal,
8-
math::turns_to_radians,
5+
use super::{
6+
turns_to_radians,
7+
vector::Vector,
98
};
109

1110
// -------------------------------- MATRIX -------------------------------------
@@ -183,6 +182,7 @@ pub fn perspective_matrix<
183182
return result;
184183
}
185184

185+
/// Create a matrix of any size that is filled with zeros.
186186
pub fn zeroed_matrix<
187187
V: Vector<Scalar = f32>,
188188
MatrixLike: Matrix<V> + Default,
@@ -218,6 +218,7 @@ pub fn filled_matrix<
218218
return result;
219219
}
220220

221+
/// Creates an identity matrix of the given size.
221222
pub fn identity_matrix<
222223
V: Vector<Scalar = f32>,
223224
MatrixLike: Matrix<V> + Default,
@@ -483,12 +484,15 @@ mod tests {
483484
assert_eq!(perspective, expected);
484485
}
485486

487+
/// Test the rotation matrix for a 3D rotation.
486488
#[test]
487489
fn rotate_matrices() {
488-
let rotation_matrix: [[f32; 4]; 4] = filled_matrix(4, 4, 1.0);
489-
let rotated_matrix = rotate_matrix(rotation_matrix, [0.0, 0.0, 1.0], 0.0);
490-
assert_eq!(rotated_matrix, rotation_matrix);
490+
// Test a zero turn rotation.
491+
let matrix: [[f32; 4]; 4] = filled_matrix(4, 4, 1.0);
492+
let rotated_matrix = rotate_matrix(matrix, [0.0, 0.0, 1.0], 0.0);
493+
assert_eq!(rotated_matrix, matrix);
491494

495+
// Test a 90 degree rotation.
492496
let matrix = [
493497
[1.0, 2.0, 3.0, 4.0],
494498
[5.0, 6.0, 7.0, 8.0],

tools/lambda_rs_demo/src/main.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use lambda::{
2626
};
2727

2828
pub struct DemoComponent {
29-
triangle_vertex: Shader,
29+
fragment_shader: Shader,
3030
vertex_shader: Shader,
3131
render_pass_id: Option<lambda::core::render::ResourceId>,
3232
render_pipeline_id: Option<lambda::core::render::ResourceId>,
@@ -44,7 +44,7 @@ impl Component for DemoComponent {
4444
render_context,
4545
&render_pass,
4646
&self.vertex_shader,
47-
&self.triangle_vertex,
47+
Some(&self.fragment_shader),
4848
);
4949

5050
// Attach the render pass and pipeline to the render context
@@ -176,7 +176,7 @@ impl Default for DemoComponent {
176176

177177
return DemoComponent {
178178
vertex_shader: vs,
179-
triangle_vertex: fs,
179+
fragment_shader: fs,
180180
render_pass_id: None,
181181
render_pipeline_id: None,
182182
width: 800,

tools/triangles_demo/src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ impl Component for TrianglesComponent {
5151
render_context,
5252
&render_pass,
5353
&self.vertex_shader,
54-
&self.triangle_vertex,
54+
Some(&self.triangle_vertex),
5555
);
5656

5757
self.render_pass = Some(render_context.attach_render_pass(render_pass));

0 commit comments

Comments
 (0)