lambda/render/
mod.rs

1//! High level Rendering API designed for cross platform rendering and
2//! windowing.
3
4// Module Exports
5pub mod buffer;
6pub mod command;
7pub mod mesh;
8pub mod pipeline;
9pub mod render_pass;
10pub mod shader;
11pub mod vertex;
12pub mod viewport;
13pub mod window;
14
15use std::{
16  mem::swap,
17  rc::Rc,
18};
19
20/// ColorFormat is a type alias for the color format used by the surface and
21/// vertex buffers. They denote the size of the color channels and the number of
22/// channels being used.
23pub use lambda_platform::gfx::surface::ColorFormat;
24use lambda_platform::gfx::{
25  command::{
26    Command,
27    CommandBufferBuilder,
28    CommandBufferFeatures,
29    CommandBufferLevel,
30  },
31  framebuffer::FramebufferBuilder,
32  surface::SwapchainBuilder,
33};
34
35use self::{
36  command::RenderCommand,
37  pipeline::RenderPipeline,
38  render_pass::RenderPass,
39};
40
41/// A RenderContext is a localized rendering context that can be used to render
42/// to a window. It is localized to a single window at the moment.
43pub struct RenderContextBuilder {
44  name: String,
45  render_timeout: u64,
46}
47
48impl RenderContextBuilder {
49  /// Create a new localized RenderContext with the given name.
50  pub fn new(name: &str) -> Self {
51    return Self {
52      name: name.to_string(),
53      render_timeout: 1_000_000_000,
54    };
55  }
56
57  /// The time rendering has to complete before a timeout occurs.
58  pub fn with_render_timeout(mut self, render_timeout: u64) -> Self {
59    self.render_timeout = render_timeout;
60    return self;
61  }
62
63  /// Builds a RenderContext and injects it into the application window.
64  /// Currently only supports building a Rendering Context utilizing the
65  /// systems primary GPU.
66  pub fn build(self, window: &window::Window) -> RenderContext {
67    let RenderContextBuilder {
68      name,
69      render_timeout,
70    } = self;
71
72    let mut instance = internal::InstanceBuilder::new()
73      .build::<internal::RenderBackend>(name.as_str());
74    let surface = Rc::new(
75      internal::SurfaceBuilder::new().build(&instance, window.window_handle()),
76    );
77
78    // Build a GPU with a Graphical Render queue that can render to our surface.
79    let mut gpu = internal::GpuBuilder::new()
80      .with_render_queue_type(internal::RenderQueueType::Graphical)
81      .build(&mut instance, Some(&surface))
82      .expect("Failed to build a GPU with a graphical render queue.");
83
84    // Build command pool and allocate a single buffer named Primary
85    let command_pool = internal::CommandPoolBuilder::new().build(&gpu);
86
87    // Build our rendering submission fence and semaphore.
88    let submission_fence = internal::RenderSubmissionFenceBuilder::new()
89      .with_render_timeout(render_timeout)
90      .build(&mut gpu);
91
92    let render_semaphore =
93      internal::RenderSemaphoreBuilder::new().build(&mut gpu);
94
95    return RenderContext {
96      name,
97      instance,
98      gpu,
99      surface: surface.clone(),
100      frame_buffer: None,
101      submission_fence: Some(submission_fence),
102      render_semaphore: Some(render_semaphore),
103      command_pool: Some(command_pool),
104      render_passes: vec![],
105      render_pipelines: vec![],
106    };
107  }
108}
109
110/// Generic Rendering API setup to use the current platforms primary
111/// Rendering Backend
112pub struct RenderContext {
113  name: String,
114  instance: internal::Instance<internal::RenderBackend>,
115  gpu: internal::Gpu<internal::RenderBackend>,
116  surface: Rc<internal::Surface<internal::RenderBackend>>,
117  frame_buffer: Option<Rc<internal::Framebuffer<internal::RenderBackend>>>,
118  submission_fence:
119    Option<internal::RenderSubmissionFence<internal::RenderBackend>>,
120  render_semaphore: Option<internal::RenderSemaphore<internal::RenderBackend>>,
121  command_pool: Option<internal::CommandPool<internal::RenderBackend>>,
122  render_passes: Vec<RenderPass>,
123  render_pipelines: Vec<RenderPipeline>,
124}
125
126pub type ResourceId = usize;
127
128impl RenderContext {
129  /// Permanently transfer a render pipeline to the render context in exchange
130  /// for a resource ID that you can use in render commands.
131  pub fn attach_pipeline(&mut self, pipeline: RenderPipeline) -> ResourceId {
132    let index = self.render_pipelines.len();
133    self.render_pipelines.push(pipeline);
134    return index;
135  }
136
137  /// Permanently transfer a render pipeline to the render context in exchange
138  /// for a resource ID that you can use in render commands.
139  pub fn attach_render_pass(&mut self, render_pass: RenderPass) -> ResourceId {
140    let index = self.render_passes.len();
141    self.render_passes.push(render_pass);
142    return index;
143  }
144
145  /// destroys the RenderContext and all associated resources.
146  pub fn destroy(mut self) {
147    logging::debug!("{} will now start destroying resources.", self.name);
148
149    // Destroy the submission fence and rendering semaphore.
150    self
151      .submission_fence
152      .take()
153      .expect(
154        "Couldn't take the submission fence from the context and destroy it.",
155      )
156      .destroy(&self.gpu);
157    self
158      .render_semaphore
159      .take()
160      .expect("Couldn't take the rendering semaphore from the context and destroy it.")
161      .destroy(&self.gpu);
162
163    self
164      .command_pool
165      .as_mut()
166      .unwrap()
167      .deallocate_command_buffer("primary");
168
169    self
170      .command_pool
171      .take()
172      .expect("Couldn't take the command pool from the context and destroy it.")
173      .destroy(&self.gpu);
174
175    // Destroy render passes.
176    let mut render_passes = vec![];
177    swap(&mut self.render_passes, &mut render_passes);
178
179    for render_pass in render_passes {
180      render_pass.destroy(&self);
181    }
182
183    // Destroy render pipelines.
184    let mut render_pipelines = vec![];
185    swap(&mut self.render_pipelines, &mut render_pipelines);
186
187    for render_pipeline in render_pipelines {
188      render_pipeline.destroy(&self);
189    }
190
191    // Takes the inner surface and destroys it.
192    let mut surface = Rc::try_unwrap(self.surface)
193      .expect("Couldn't obtain the surface from the context.");
194
195    surface.remove_swapchain(&self.gpu);
196    surface.destroy(&self.instance);
197  }
198
199  pub fn allocate_and_get_frame_buffer(
200    &mut self,
201    render_pass: &internal::RenderPass<internal::RenderBackend>,
202  ) -> Rc<lambda_platform::gfx::framebuffer::Framebuffer<internal::RenderBackend>>
203  {
204    let frame_buffer = FramebufferBuilder::new().build(
205      &mut self.gpu,
206      &render_pass,
207      self.surface.as_ref(),
208    );
209
210    // TODO(vmarcella): Update the framebuffer allocation to not be so hacky.
211    // FBAs can only be allocated once a render pass has begun, but must be
212    // cleaned up after commands have been submitted forcing us
213    self.frame_buffer = Some(Rc::new(frame_buffer));
214    return self.frame_buffer.as_ref().unwrap().clone();
215  }
216
217  /// Allocates a command buffer and records commands to the GPU. This is the
218  /// primary entry point for submitting commands to the GPU and where rendering
219  /// will occur.
220  pub fn render(&mut self, commands: Vec<RenderCommand>) {
221    let (width, height) = self
222      .surface
223      .size()
224      .expect("Surface has no size configured.");
225
226    let swapchain = SwapchainBuilder::new()
227      .with_size(width, height)
228      .build(&self.gpu, &self.surface);
229
230    if self.surface.needs_swapchain() {
231      Rc::get_mut(&mut self.surface)
232        .expect("Failed to get mutable reference to surface.")
233        .apply_swapchain(&self.gpu, swapchain, 1_000_000_000)
234        .expect("Failed to apply the swapchain to the surface.");
235    }
236
237    self
238      .submission_fence
239      .as_mut()
240      .expect("Failed to get the submission fence.")
241      .block_until_ready(&mut self.gpu, None);
242
243    let platform_command_list = commands
244      .into_iter()
245      .map(|command| command.into_platform_command(self))
246      .collect();
247
248    let mut command_buffer =
249      CommandBufferBuilder::new(CommandBufferLevel::Primary)
250        .with_feature(CommandBufferFeatures::ResetEverySubmission)
251        .build(
252          self
253            .command_pool
254            .as_mut()
255            .expect("No command pool to create a buffer from"),
256          "primary",
257        );
258
259    // Start recording commands, issue the high level render commands
260    // that came from an application, and then submit the commands to the GPU
261    // for rendering.
262    command_buffer.issue_command(PlatformRenderCommand::BeginRecording);
263    command_buffer.issue_commands(platform_command_list);
264    command_buffer.issue_command(PlatformRenderCommand::EndRecording);
265
266    self.gpu.submit_command_buffer(
267      &mut command_buffer,
268      vec![self.render_semaphore.as_ref().unwrap()],
269      self.submission_fence.as_mut().unwrap(),
270    );
271
272    self
273      .gpu
274      .render_to_surface(
275        Rc::get_mut(&mut self.surface)
276          .expect("Failed to obtain a surface to render on."),
277        self.render_semaphore.as_mut().unwrap(),
278      )
279      .expect("Failed to render to the surface");
280
281    // Destroys the frame buffer after the commands have been submitted and the
282    // frame buffer is no longer needed.
283    match self.frame_buffer {
284      Some(_) => {
285        Rc::try_unwrap(self.frame_buffer.take().unwrap())
286          .expect("Failed to unwrap the frame buffer.")
287          .destroy(&self.gpu);
288      }
289      None => {}
290    }
291  }
292
293  pub fn resize(&mut self, width: u32, height: u32) {
294    let swapchain = SwapchainBuilder::new()
295      .with_size(width, height)
296      .build(&self.gpu, &self.surface);
297
298    if self.surface.needs_swapchain() {
299      Rc::get_mut(&mut self.surface)
300        .expect("Failed to get mutable reference to surface.")
301        .apply_swapchain(&self.gpu, swapchain, 1_000_000_000)
302        .expect("Failed to apply the swapchain to the surface.");
303    }
304  }
305
306  /// Get the render pass with the resource ID that was provided upon
307  /// attachment.
308  pub fn get_render_pass(&self, id: ResourceId) -> &RenderPass {
309    return &self.render_passes[id];
310  }
311
312  /// Get the render pipeline with the resource ID that was provided upon
313  /// attachment.
314  pub fn get_render_pipeline(&mut self, id: ResourceId) -> &RenderPipeline {
315    return &self.render_pipelines[id];
316  }
317}
318
319impl RenderContext {
320  /// Internal access to the RenderContext's GPU.
321  pub(super) fn internal_gpu(&self) -> &internal::Gpu<internal::RenderBackend> {
322    return &self.gpu;
323  }
324
325  /// Internal mutable access to the RenderContext's GPU.
326  pub(super) fn internal_mutable_gpu(
327    &mut self,
328  ) -> &mut internal::Gpu<internal::RenderBackend> {
329    return &mut self.gpu;
330  }
331
332  pub(super) fn internal_surface(
333    &self,
334  ) -> Rc<lambda_platform::gfx::surface::Surface<internal::RenderBackend>> {
335    return self.surface.clone();
336  }
337}
338
339type PlatformRenderCommand = Command<internal::RenderBackend>;
340
341pub(crate) mod internal {
342
343  use lambda_platform::gfx::api::RenderingAPI as RenderContext;
344  pub type RenderBackend = RenderContext::Backend;
345
346  pub use lambda_platform::{
347    gfx::{
348      command::{
349        CommandBuffer,
350        CommandBufferBuilder,
351        CommandPool,
352        CommandPoolBuilder,
353      },
354      fence::{
355        RenderSemaphore,
356        RenderSemaphoreBuilder,
357        RenderSubmissionFence,
358        RenderSubmissionFenceBuilder,
359      },
360      framebuffer::Framebuffer,
361      gpu::{
362        Gpu,
363        GpuBuilder,
364        RenderQueueType,
365      },
366      pipeline::RenderPipelineBuilder,
367      render_pass::{
368        RenderPass,
369        RenderPassBuilder,
370      },
371      surface::{
372        Surface,
373        SurfaceBuilder,
374      },
375      Instance,
376      InstanceBuilder,
377    },
378    shaderc::ShaderKind,
379  };
380}