lambda_platform/gfx/
pipeline.rs

1use std::ops::Range;
2
3/// gfx-hal imports for pipeline.rs
4use 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
30/// Builder for a gfx-hal backed render pipeline.
31pub 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  /// Adds a push constant to the render pipeline at the set PipelineStage(s)
63  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  /// Adds multiple push constants to the render pipeline at their
73  /// set PipelineStage(s)
74  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  /// Builds a render pipeline based on your builder configuration. You can
83  /// configure a render pipeline to be however you'd like it to be.
84  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    // TODO(vmarcella): The pipeline layout should be configurable through the
94    // RenderPipelineBuilder.
95    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    // TODO(vmarcella): The primitive assembler should be configurable through
107    // the RenderPipelineBuilder so that buffers & attributes can be bound.
108    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/// Represents a render capable pipeline for graphical
160#[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  /// Destroys the pipeline layout and graphical pipeline
169  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}