lambda_platform/gfx/
surface.rs1pub use gfx_hal::format::Format as ColorFormat;
3use gfx_hal::{
4 window::{
5 PresentationSurface,
6 Surface as _,
7 },
8 Backend,
9};
10#[cfg(test)]
11use mockall::automock;
12
13use super::{
14 gpu::Gpu,
15 Instance,
16};
17
18#[derive(Debug, Clone)]
20pub struct SurfaceBuilder {
21 name: Option<String>,
22}
23
24#[cfg_attr(test, automock)]
25impl SurfaceBuilder {
26 pub fn new() -> Self {
27 return Self { name: None };
28 }
29
30 pub fn with_name(mut self, name: &str) -> Self {
32 self.name = Some(name.to_string());
33 return self;
34 }
35
36 pub fn build<RenderBackend: gfx_hal::Backend>(
38 self,
39 instance: &super::Instance<RenderBackend>,
40 window: &crate::winit::WindowHandle,
41 ) -> Surface<RenderBackend> {
42 let gfx_hal_surface = instance.create_surface(window);
43 let name = match self.name {
44 Some(name) => name,
45 None => "RenderSurface".to_string(),
46 };
47
48 return Surface {
49 name,
50 extent: None,
51 gfx_hal_surface,
52 swapchain_is_valid: true,
53 image: None,
54 frame_buffer_attachment: None,
55 };
56 }
57}
58
59#[derive(Debug)]
61pub struct Surface<RenderBackend: gfx_hal::Backend> {
62 name: String,
63 gfx_hal_surface: RenderBackend::Surface,
64 extent: Option<gfx_hal::window::Extent2D>,
65 swapchain_is_valid: bool,
66 image: Option<
68 <RenderBackend::Surface as gfx_hal::window::PresentationSurface<
69 RenderBackend,
70 >>::SwapchainImage,
71 >,
72 frame_buffer_attachment: Option<gfx_hal::image::FramebufferAttachment>,
73}
74
75#[derive(Debug, Clone)]
76pub struct Swapchain {
77 config: gfx_hal::window::SwapchainConfig,
78 format: gfx_hal::format::Format,
79}
80
81#[cfg_attr(test, automock)]
82impl<RenderBackend: gfx_hal::Backend> Surface<RenderBackend> {
83 pub fn apply_swapchain<'surface>(
86 &mut self,
87 gpu: &Gpu<RenderBackend>,
88 swapchain: Swapchain,
89 timeout_in_nanoseconds: u64,
90 ) -> Result<(), &'surface str> {
91 let device = gpu.internal_logical_device();
92 self.extent = Some(swapchain.config.extent);
93
94 unsafe {
95 self
96 .gfx_hal_surface
97 .configure_swapchain(device, swapchain.config.clone())
98 .expect("Failed to configure the swapchain");
99
100 self.frame_buffer_attachment =
101 Some(swapchain.config.framebuffer_attachment());
102
103 let image =
104 match self.gfx_hal_surface.acquire_image(timeout_in_nanoseconds) {
105 Ok((image, _)) => Some(image),
106 Err(_) => {
107 self.swapchain_is_valid = false;
108 None
109 }
110 };
111
112 match image {
113 Some(image) => {
114 self.image = Some(image);
115 return Ok(());
116 }
117 None => {
118 return Err("Failed to apply the swapchain.");
119 }
120 }
121 }
122 }
123
124 pub fn needs_swapchain(&self) -> bool {
125 return self.swapchain_is_valid;
126 }
127
128 pub fn remove_swapchain(&mut self, gpu: &Gpu<RenderBackend>) {
131 logging::debug!("Removing the swapchain configuration from: {}", self.name);
132 unsafe {
133 self
134 .gfx_hal_surface
135 .unconfigure_swapchain(gpu.internal_logical_device());
136 }
137 }
138
139 pub fn destroy(self, instance: &Instance<RenderBackend>) {
141 logging::debug!("Destroying the surface: {}", self.name);
142
143 instance.destroy_surface(self.gfx_hal_surface);
144 }
145
146 pub fn size(&self) -> Option<(u32, u32)> {
149 return match self.extent {
150 Some(extent) => Some((extent.width, extent.height)),
151 None => None,
152 };
153 }
154}
155
156pub struct SwapchainBuilder {
159 size: (u32, u32),
160}
161
162impl SwapchainBuilder {
163 pub fn new() -> Self {
164 return Self { size: (480, 360) };
165 }
166
167 pub fn with_size(mut self, width: u32, height: u32) -> Self {
169 self.size = (width, height);
170 return self;
171 }
172
173 pub fn build<RenderBackend: Backend>(
174 self,
175 gpu: &Gpu<RenderBackend>,
176 surface: &Surface<RenderBackend>,
177 ) -> Swapchain {
178 let physical_device = gpu.internal_physical_device();
179 let caps = surface.gfx_hal_surface.capabilities(physical_device);
180 let format = surface.get_first_supported_format(physical_device);
181 let (width, height) = self.size;
182
183 let mut swapchain_config = gfx_hal::window::SwapchainConfig::from_caps(
184 &caps,
185 format,
186 gfx_hal::window::Extent2D { width, height },
187 );
188
189 if caps.image_count.contains(&3) {
192 swapchain_config.image_count = 3;
193 }
194
195 return Swapchain {
196 config: swapchain_config,
197 format,
198 };
199 }
200}
201
202#[cfg(test)]
203mod tests {
204 use super::*;
205 use crate::gfx::MockInstanceBuilder;
206
207 #[test]
208 fn test_surface_builder() {
209 let surface_builder = SurfaceBuilder::new();
210 assert_eq!(surface_builder.name, None);
211
212 let surface_builder = SurfaceBuilder::new().with_name("TestSurface");
213 assert_eq!(surface_builder.name, Some("TestSurface".to_string()));
214 }
215
216 #[test]
217 fn test_swapchain_builder() {
218 let swapchain_builder = SwapchainBuilder::new();
219 assert_eq!(swapchain_builder.size, (480, 360));
220
221 let swapchain_builder = SwapchainBuilder::new().with_size(1920, 1080);
222 assert_eq!(swapchain_builder.size, (1920, 1080));
223 }
224
225 #[test]
226 fn test_surface_builder_e2e() {
227 let surface_builder = SurfaceBuilder::new().with_name("TestSurface");
229 }
236}
237
238impl<RenderBackend: Backend> Surface<RenderBackend> {
239 pub(super) fn can_support_queue_family(
241 &self,
242 queue_family: &RenderBackend::QueueFamily,
243 ) -> bool {
244 return self.gfx_hal_surface.supports_queue_family(queue_family);
245 }
246
247 pub(super) fn get_supported_formats(
248 &self,
249 physical_device: &RenderBackend::PhysicalDevice,
250 ) -> Vec<gfx_hal::format::Format> {
251 return self
252 .gfx_hal_surface
253 .supported_formats(physical_device)
254 .unwrap_or(vec![]);
255 }
256
257 pub(super) fn get_first_supported_format(
258 &self,
259 physical_device: &RenderBackend::PhysicalDevice,
260 ) -> gfx_hal::format::Format {
261 return self
262 .get_supported_formats(physical_device)
263 .get(0)
264 .unwrap_or(&gfx_hal::format::Format::Rgba8Srgb)
265 .clone();
266 }
267
268 pub(super) fn internal_surface_image(
269 &self,
270 ) -> Option<&<RenderBackend::Surface as PresentationSurface<RenderBackend>>::SwapchainImage>{
271 return self.image.as_ref();
272 }
273
274 pub(super) fn internal_frame_buffer_attachment(
275 &self,
276 ) -> Option<gfx_hal::image::FramebufferAttachment> {
277 return self.frame_buffer_attachment.clone();
278 }
279
280 pub(super) fn internal_surface_and_image(
281 &mut self,
282 ) -> (
283 &mut RenderBackend::Surface,
284 <RenderBackend::Surface as PresentationSurface<RenderBackend>>::SwapchainImage,
285 ){
286 return (
287 &mut self.gfx_hal_surface,
288 self.image.take().expect("Surface image is not present"),
289 );
290 }
291}