1pub 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
20pub 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
41pub struct RenderContextBuilder {
44 name: String,
45 render_timeout: u64,
46}
47
48impl RenderContextBuilder {
49 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 pub fn with_render_timeout(mut self, render_timeout: u64) -> Self {
59 self.render_timeout = render_timeout;
60 return self;
61 }
62
63 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 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 let command_pool = internal::CommandPoolBuilder::new().build(&gpu);
86
87 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
110pub 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 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 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 pub fn destroy(mut self) {
147 logging::debug!("{} will now start destroying resources.", self.name);
148
149 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 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 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 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 self.frame_buffer = Some(Rc::new(frame_buffer));
214 return self.frame_buffer.as_ref().unwrap().clone();
215 }
216
217 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 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 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 pub fn get_render_pass(&self, id: ResourceId) -> &RenderPass {
309 return &self.render_passes[id];
310 }
311
312 pub fn get_render_pipeline(&mut self, id: ResourceId) -> &RenderPipeline {
315 return &self.render_pipelines[id];
316 }
317}
318
319impl RenderContext {
320 pub(super) fn internal_gpu(&self) -> &internal::Gpu<internal::RenderBackend> {
322 return &self.gpu;
323 }
324
325 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}