lambda_platform/gfx/
pipeline.rs1use std::ops::Range;
2
3use gfx_hal::{
5 device::Device,
6 pass::Subpass,
7 pso::{
8 BlendState,
9 ColorBlendDesc,
10 ColorMask,
11 EntryPoint,
12 Face,
13 GraphicsPipelineDesc,
14 PrimitiveAssemblerDesc,
15 Rasterizer,
16 },
17 Backend,
18};
19
20use super::{
21 assembler::{
22 PrimitiveAssemblerBuilder,
23 VertexAttribute,
24 },
25 buffer::Buffer,
26 gpu::Gpu,
27 shader::ShaderModule,
28};
29
30pub struct RenderPipelineBuilder<RenderBackend: Backend> {
32 pipeline_layout: Option<RenderBackend::PipelineLayout>,
33 push_constants: Vec<PushConstantUpload>,
34 buffers: Vec<Buffer<RenderBackend>>,
35 attributes: Vec<VertexAttribute>,
36}
37
38pub type PipelineStage = gfx_hal::pso::ShaderStageFlags;
39
40pub type PushConstantUpload = (PipelineStage, Range<u32>);
41
42impl<RenderBackend: Backend> RenderPipelineBuilder<RenderBackend> {
43 pub fn new() -> Self {
44 return Self {
45 pipeline_layout: None,
46 push_constants: Vec::new(),
47 buffers: Vec::new(),
48 attributes: Vec::new(),
49 };
50 }
51
52 pub fn with_buffer(
53 &mut self,
54 buffer: Buffer<RenderBackend>,
55 attributes: Vec<VertexAttribute>,
56 ) -> &mut Self {
57 self.buffers.push(buffer);
58 self.attributes.extend(attributes);
59 return self;
60 }
61
62 pub fn with_push_constant(
64 &mut self,
65 stage: PipelineStage,
66 bytes: u32,
67 ) -> &mut Self {
68 self.push_constants.push((stage, 0..bytes));
69 return self;
70 }
71
72 pub fn with_push_constants(
75 mut self,
76 push_constants: Vec<PushConstantUpload>,
77 ) -> Self {
78 self.push_constants.extend(push_constants);
79 return self;
80 }
81
82 pub fn build(
85 self,
86 gpu: &Gpu<RenderBackend>,
87 render_pass: &super::render_pass::RenderPass<RenderBackend>,
88 vertex_shader: &ShaderModule<RenderBackend>,
89 fragment_shader: Option<&ShaderModule<RenderBackend>>,
90 buffers: &Vec<&Buffer<RenderBackend>>,
91 attributes: &[VertexAttribute],
92 ) -> RenderPipeline<RenderBackend> {
93 let push_constants = self.push_constants.into_iter();
96
97 let pipeline_layout = unsafe {
98 gpu
99 .internal_logical_device()
100 .create_pipeline_layout(vec![].into_iter(), push_constants)
101 .expect(
102 "The GPU does not have enough memory to allocate a pipeline layout",
103 )
104 };
105
106 let mut builder = PrimitiveAssemblerBuilder::new();
109 let primitive_assembler =
110 builder.build(vertex_shader, Some(buffers), Some(attributes));
111
112 let fragment_entry = match fragment_shader {
113 Some(shader) => Some(EntryPoint::<RenderBackend> {
114 entry: shader.entry(),
115 module: super::internal::module_for(shader),
116 specialization: shader.specializations().clone(),
117 }),
118 None => None,
119 };
120
121 let mut pipeline_desc = GraphicsPipelineDesc::new(
122 primitive_assembler.internal_primitive_assembler(),
123 Rasterizer {
124 cull_face: Face::BACK,
125 ..Rasterizer::FILL
126 },
127 fragment_entry,
128 &pipeline_layout,
129 Subpass {
130 index: 0,
131 main_pass: render_pass.internal_render_pass(),
132 },
133 );
134
135 pipeline_desc.blender.targets.push(ColorBlendDesc {
136 mask: ColorMask::ALL,
137 blend: Some(BlendState::ALPHA),
138 });
139
140 let pipeline = unsafe {
141 let pipeline_build_result = gpu
142 .internal_logical_device()
143 .create_graphics_pipeline(&pipeline_desc, None);
144
145 match pipeline_build_result {
146 Ok(pipeline) => pipeline,
147 Err(e) => panic!("Failed to create graphics pipeline: {:?}", e),
148 }
149 };
150
151 return RenderPipeline {
152 pipeline_layout,
153 pipeline,
154 buffers: self.buffers,
155 };
156 }
157}
158
159#[derive(Debug)]
161pub struct RenderPipeline<RenderBackend: Backend> {
162 pipeline_layout: RenderBackend::PipelineLayout,
163 pipeline: RenderBackend::GraphicsPipeline,
164 buffers: Vec<Buffer<RenderBackend>>,
165}
166
167impl<RenderBackend: Backend> RenderPipeline<RenderBackend> {
168 pub fn destroy(self, gpu: &super::gpu::Gpu<RenderBackend>) {
170 logging::debug!("Destroying render pipeline");
171 unsafe {
172 for buffer in self.buffers {
173 buffer.destroy(gpu);
174 }
175
176 gpu
177 .internal_logical_device()
178 .destroy_pipeline_layout(self.pipeline_layout);
179
180 gpu
181 .internal_logical_device()
182 .destroy_graphics_pipeline(self.pipeline);
183 }
184 }
185}
186
187impl<RenderBackend: Backend> RenderPipeline<RenderBackend> {
188 pub(super) fn internal_pipeline_layout(
189 &self,
190 ) -> &RenderBackend::PipelineLayout {
191 return &self.pipeline_layout;
192 }
193
194 pub(super) fn internal_pipeline(&self) -> &RenderBackend::GraphicsPipeline {
195 return &self.pipeline;
196 }
197}