lambda_platform/gfx/
render_pass.rs

1use gfx_hal::device::Device;
2
3use super::{
4  gpu::Gpu,
5  surface::ColorFormat,
6};
7
8// ----------------------- RENDER ATTACHMENT OPERATIONS ------------------------
9
10#[derive(Debug)]
11pub enum Operations {
12  DontCare,
13  Load,
14  Clear,
15  Store,
16}
17
18impl Operations {
19  fn to_gfx_hal_load_operation(&self) -> gfx_hal::pass::AttachmentLoadOp {
20    match self {
21      Operations::DontCare => gfx_hal::pass::AttachmentLoadOp::DontCare,
22      Operations::Load => gfx_hal::pass::AttachmentLoadOp::Load,
23      Operations::Clear => gfx_hal::pass::AttachmentLoadOp::Clear,
24      _ => panic!("Cannot pass in {:?} as an operation for the attachment load operation!", self)
25    }
26  }
27
28  fn to_gfx_hal_store_operation(&self) -> gfx_hal::pass::AttachmentStoreOp {
29    return match self {
30      Operations::DontCare => gfx_hal::pass::AttachmentStoreOp::DontCare,
31      Operations::Store => gfx_hal::pass::AttachmentStoreOp::Store,
32      _ => panic!(
33        "Cannot pass in {:?} as an operation for the attachment store operation!",
34        self
35      ),
36    };
37  }
38}
39
40// ----------------------------- RENDER ATTACHMENT -----------------------------
41
42///
43pub struct AttachmentBuilder {
44  samples: u8,
45  color_format: Option<ColorFormat>,
46  load_operation: gfx_hal::pass::AttachmentLoadOp,
47  store_operation: gfx_hal::pass::AttachmentStoreOp,
48}
49
50/// builder for a render attachment
51impl AttachmentBuilder {
52  pub fn new() -> Self {
53    return Self {
54      samples: 0,
55      color_format: None,
56      load_operation: gfx_hal::pass::AttachmentLoadOp::DontCare,
57      store_operation: gfx_hal::pass::AttachmentStoreOp::DontCare,
58    };
59  }
60
61  ///  Sets the number of samples to use for the attachment.
62  pub fn with_samples(mut self, samples: u8) -> Self {
63    self.samples = samples;
64    return self;
65  }
66
67  /// Sets the color format to use for the attachment.
68  pub fn with_color_format(mut self, color_format: ColorFormat) -> Self {
69    self.color_format = Some(color_format);
70    return self;
71  }
72
73  /// Sets the load operation for the attachment.
74  pub fn on_load(mut self, operation: Operations) -> Self {
75    self.load_operation = operation.to_gfx_hal_load_operation();
76    return self;
77  }
78
79  ///
80  pub fn on_store(mut self, operation: Operations) -> Self {
81    self.store_operation = operation.to_gfx_hal_store_operation();
82    return self;
83  }
84
85  /// Builds a render attachment that can be used within a render pass.
86  pub fn build(self) -> Attachment {
87    return Attachment {
88      attachment: gfx_hal::pass::Attachment {
89        format: self.color_format,
90        samples: self.samples,
91        ops: gfx_hal::pass::AttachmentOps::new(
92          self.load_operation,
93          self.store_operation,
94        ),
95        stencil_ops: gfx_hal::pass::AttachmentOps::DONT_CARE,
96        layouts: gfx_hal::image::Layout::Undefined
97          ..gfx_hal::image::Layout::Present,
98      },
99    };
100  }
101}
102
103pub struct Attachment {
104  attachment: gfx_hal::pass::Attachment,
105}
106
107impl Attachment {
108  fn gfx_hal_attachment(&self) -> gfx_hal::pass::Attachment {
109    return self.attachment.clone();
110  }
111}
112
113// ------------------------------ RENDER SUBPASS -------------------------------
114
115pub use gfx_hal::image::Layout as ImageLayoutHint;
116
117pub struct SubpassBuilder {
118  color_attachment: Option<(usize, ImageLayoutHint)>,
119}
120
121impl SubpassBuilder {
122  pub fn new() -> Self {
123    return Self {
124      color_attachment: None,
125    };
126  }
127
128  pub fn with_color_attachment(
129    mut self,
130    attachment_index: usize,
131    layout: ImageLayoutHint,
132  ) -> Self {
133    self.color_attachment = Some((attachment_index, layout));
134    return self;
135  }
136  pub fn with_inputs() {
137    todo!("Implement input support for subpasses")
138  }
139  pub fn with_resolves() {
140    todo!("Implement resolving support for subpasses")
141  }
142  pub fn with_preserves() {
143    todo!("Implement preservation support for subpasses")
144  }
145
146  pub fn build<'a>(self) -> Subpass<'a> {
147    return Subpass {
148      subpass: gfx_hal::pass::SubpassDesc {
149        colors: &[(0, ImageLayoutHint::ColorAttachmentOptimal)],
150        depth_stencil: None,
151        inputs: &[],
152        resolves: &[],
153        preserves: &[],
154      },
155    };
156  }
157}
158
159pub struct Subpass<'a> {
160  subpass: gfx_hal::pass::SubpassDesc<'a>,
161}
162
163impl<'a> Subpass<'a> {
164  fn gfx_hal_subpass(self) -> gfx_hal::pass::SubpassDesc<'a> {
165    return self.subpass;
166  }
167}
168
169// -------------------------------- RENDER PASS --------------------------------
170
171pub struct RenderPassBuilder<'builder> {
172  attachments: Vec<Attachment>,
173  subpasses: Vec<Subpass<'builder>>,
174}
175
176impl<'builder> RenderPassBuilder<'builder> {
177  pub fn new() -> Self {
178    return Self {
179      attachments: vec![],
180      subpasses: vec![],
181    };
182  }
183
184  /// Adds an attachment to the render pass. Can add multiple.
185  pub fn add_attachment(mut self, attachment: Attachment) -> Self {
186    self.attachments.push(attachment);
187    return self;
188  }
189
190  pub fn add_subpass(mut self, subpass: Subpass<'builder>) -> Self {
191    self.subpasses.push(subpass);
192    return self;
193  }
194
195  pub fn build<RenderBackend: gfx_hal::Backend>(
196    self,
197    gpu: &Gpu<RenderBackend>,
198  ) -> RenderPass<RenderBackend> {
199    // If there are no attachments, use a stub image attachment with clear and
200    // store operations.
201    let attachments = match self.attachments.is_empty() {
202      true => vec![AttachmentBuilder::new()
203        .with_samples(1)
204        .on_load(Operations::Clear)
205        .on_store(Operations::Store)
206        .with_color_format(ColorFormat::Rgba8Srgb)
207        .build()
208        .gfx_hal_attachment()],
209      false => self
210        .attachments
211        .into_iter()
212        .map(|attachment| attachment.gfx_hal_attachment())
213        .collect(),
214    };
215
216    // If there are no subpass descriptions, use a stub subpass attachment
217    let subpasses = match self.subpasses.is_empty() {
218      true => vec![SubpassBuilder::new().build().gfx_hal_subpass()],
219      false => self
220        .subpasses
221        .into_iter()
222        .map(|subpass| subpass.gfx_hal_subpass())
223        .collect(),
224    };
225
226    let render_pass = unsafe {
227      gpu.internal_logical_device().create_render_pass(
228        attachments.into_iter(),
229        subpasses.into_iter(),
230        vec![].into_iter(),
231      )
232    }
233    .expect("The GPU does not have enough memory to allocate a render pass.");
234
235    return RenderPass { render_pass };
236  }
237}
238
239#[derive(Debug)]
240pub struct RenderPass<RenderBackend: gfx_hal::Backend> {
241  render_pass: RenderBackend::RenderPass,
242}
243
244impl<RenderBackend: gfx_hal::Backend> RenderPass<RenderBackend> {
245  pub fn destroy(self, gpu: &Gpu<RenderBackend>) {
246    unsafe {
247      gpu
248        .internal_logical_device()
249        .destroy_render_pass(self.render_pass);
250    }
251  }
252}
253
254impl<RenderBackend: gfx_hal::Backend> RenderPass<RenderBackend> {
255  pub(super) fn internal_render_pass(&self) -> &RenderBackend::RenderPass {
256    return &self.render_pass;
257  }
258}