1#![warn(
4 missing_debug_implementations,
5 missing_copy_implementations,
6 missing_docs,
7 trivial_casts,
8 trivial_numeric_casts,
9 unused_extern_crates,
10 unused_import_braces,
11 unused_qualifications
12)]
13
14use {
15 rendy_core::hal::{
16 device::Device as _,
17 format::Format,
18 window::{Extent2D, Surface as _, SurfaceCapabilities},
19 Backend, Instance as _,
20 },
21 rendy_core::{
22 device_owned, instance_owned, Device, DeviceId, HasRawWindowHandle, Instance, InstanceId,
23 },
24 rendy_resource::{Image, ImageInfo},
25};
26
27#[derive(Debug)]
29pub enum SwapchainError {
30 Create(rendy_core::hal::window::CreationError),
32 BadPresentMode(rendy_core::hal::window::PresentMode),
34 BadImageCount(rendy_core::hal::window::SwapImageIndex),
36}
37
38pub struct Surface<B: Backend> {
40 raw: B::Surface,
41 instance: InstanceId,
42}
43
44impl<B> std::fmt::Debug for Surface<B>
45where
46 B: Backend,
47{
48 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49 fmt.debug_struct("Surface")
50 .field("instance", &self.instance)
51 .finish()
52 }
53}
54
55instance_owned!(Surface<B>);
56
57impl<B> Surface<B>
58where
59 B: Backend,
60{
61 pub fn new(
63 instance: &Instance<B>,
64 handle: &impl HasRawWindowHandle,
65 ) -> Result<Self, rendy_core::hal::window::InitError> {
66 let raw = unsafe { instance.create_surface(handle) }?;
67 Ok(Surface {
68 raw,
69 instance: instance.id(),
70 })
71 }
72
73 pub unsafe fn new_with(
79 instance: &Instance<B>,
80 f: impl FnOnce(&B::Instance) -> B::Surface,
81 ) -> Self {
82 Surface {
83 raw: f(instance.raw()),
84 instance: instance.id(),
85 }
86 }
87
88 pub unsafe fn from_raw(surface: B::Surface, instance: InstanceId) -> Self {
90 Surface {
91 raw: surface,
92 instance,
93 }
94 }
95}
96
97impl<B> Surface<B>
98where
99 B: Backend,
100{
101 pub fn raw(&self) -> &B::Surface {
103 &self.raw
104 }
105
106 pub unsafe fn extent(&self, physical_device: &B::PhysicalDevice) -> Option<Extent2D> {
108 self.capabilities(physical_device).current_extent
109 }
110
111 pub unsafe fn format(&self, physical_device: &B::PhysicalDevice) -> Format {
113 if let Some(formats) = self.raw.supported_formats(physical_device) {
114 *formats
115 .iter()
116 .max_by_key(|format| {
117 let base = format.base_format();
118 let desc = base.0.desc();
119 (
120 !desc.is_compressed(),
121 base.1 == rendy_core::hal::format::ChannelType::Srgb,
122 desc.bits,
123 )
124 })
125 .expect("At least one format must be supported by the surface")
126 } else {
127 Format::Rgba8Srgb
128 }
129 }
130
131 pub unsafe fn supported_formats(
137 &self,
138 physical_device: &B::PhysicalDevice,
139 ) -> Option<Vec<Format>> {
140 self.raw.supported_formats(physical_device)
141 }
142
143 pub unsafe fn capabilities(&self, physical_device: &B::PhysicalDevice) -> SurfaceCapabilities {
149 self.raw.capabilities(physical_device)
150 }
151
152 pub unsafe fn into_target(
154 mut self,
155 physical_device: &B::PhysicalDevice,
156 device: &Device<B>,
157 suggest_extent: Extent2D,
158 image_count: u32,
159 present_mode: rendy_core::hal::window::PresentMode,
160 usage: rendy_core::hal::image::Usage,
161 ) -> Result<Target<B>, SwapchainError> {
162 assert_eq!(
163 device.id().instance,
164 self.instance,
165 "Resource is not owned by specified instance"
166 );
167
168 let (swapchain, backbuffer, extent) = create_swapchain(
169 &mut self,
170 physical_device,
171 device,
172 suggest_extent,
173 image_count,
174 present_mode,
175 usage,
176 )?;
177
178 Ok(Target {
179 device: device.id(),
180 relevant: relevant::Relevant,
181 surface: self,
182 swapchain: Some(swapchain),
183 backbuffer: Some(backbuffer),
184 extent,
185 present_mode,
186 usage,
187 })
188 }
189}
190
191unsafe fn create_swapchain<B: Backend>(
192 surface: &mut Surface<B>,
193 physical_device: &B::PhysicalDevice,
194 device: &Device<B>,
195 suggest_extent: Extent2D,
196 image_count: u32,
197 present_mode: rendy_core::hal::window::PresentMode,
198 usage: rendy_core::hal::image::Usage,
199) -> Result<(B::Swapchain, Vec<Image<B>>, Extent2D), SwapchainError> {
200 let capabilities = surface.capabilities(physical_device);
201 let format = surface.format(physical_device);
202
203 if !capabilities.present_modes.contains(present_mode) {
204 log::warn!(
205 "Present mode is not supported. Supported: {:#?}, requested: {:#?}",
206 capabilities.present_modes,
207 present_mode,
208 );
209 return Err(SwapchainError::BadPresentMode(present_mode));
210 }
211
212 log::trace!(
213 "Surface present modes: {:#?}. Pick {:#?}",
214 capabilities.present_modes,
215 present_mode
216 );
217
218 log::trace!("Surface chosen format {:#?}", format);
219
220 if image_count < *capabilities.image_count.start()
221 || image_count > *capabilities.image_count.end()
222 {
223 log::warn!(
224 "Image count not supported. Supported: {:#?}, requested: {:#?}",
225 capabilities.image_count,
226 image_count
227 );
228 return Err(SwapchainError::BadImageCount(image_count));
229 }
230
231 log::trace!(
232 "Surface capabilities: {:#?}. Pick {} images",
233 capabilities.image_count,
234 image_count
235 );
236
237 assert!(
238 capabilities.usage.contains(usage),
239 "Surface supports {:?}, but {:?} was requested",
240 capabilities.usage,
241 usage
242 );
243
244 let extent = capabilities.current_extent.unwrap_or(suggest_extent);
245
246 let (swapchain, images) = device
247 .create_swapchain(
248 &mut surface.raw,
249 rendy_core::hal::window::SwapchainConfig {
250 present_mode,
251 format,
252 extent,
253 image_count,
254 image_layers: 1,
255 image_usage: usage,
256 composite_alpha_mode: [
257 rendy_core::hal::window::CompositeAlphaMode::INHERIT,
258 rendy_core::hal::window::CompositeAlphaMode::OPAQUE,
259 rendy_core::hal::window::CompositeAlphaMode::PREMULTIPLIED,
260 rendy_core::hal::window::CompositeAlphaMode::POSTMULTIPLIED,
261 ]
262 .iter()
263 .cloned()
264 .find(|&bit| capabilities.composite_alpha_modes.contains(bit))
265 .expect("No CompositeAlphaMode modes supported"),
266 },
267 None,
268 )
269 .map_err(SwapchainError::Create)?;
270
271 let backbuffer = images
272 .into_iter()
273 .map(|image| {
274 Image::create_from_swapchain(
275 device.id(),
276 ImageInfo {
277 kind: rendy_core::hal::image::Kind::D2(extent.width, extent.height, 1, 1),
278 levels: 1,
279 format,
280 tiling: rendy_core::hal::image::Tiling::Optimal,
281 view_caps: rendy_core::hal::image::ViewCapabilities::empty(),
282 usage,
283 },
284 image,
285 )
286 })
287 .collect();
288
289 Ok((swapchain, backbuffer, extent))
290}
291
292pub struct Target<B: Backend> {
295 device: DeviceId,
296 surface: Surface<B>,
297 swapchain: Option<B::Swapchain>,
298 backbuffer: Option<Vec<Image<B>>>,
299 extent: Extent2D,
300 present_mode: rendy_core::hal::window::PresentMode,
301 usage: rendy_core::hal::image::Usage,
302 relevant: relevant::Relevant,
303}
304
305device_owned!(Target<B>);
306
307impl<B> std::fmt::Debug for Target<B>
308where
309 B: Backend,
310{
311 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
312 fmt.debug_struct("Target")
313 .field("backbuffer", &self.backbuffer)
314 .finish()
315 }
316}
317
318impl<B> Target<B>
319where
320 B: Backend,
321{
322 pub unsafe fn dispose(mut self, device: &Device<B>) -> Surface<B> {
328 self.assert_device_owner(device);
329
330 match self.backbuffer {
331 Some(images) => {
332 images
333 .into_iter()
334 .for_each(|image| image.dispose_swapchain_image(device.id()));
335 }
336 _ => {}
337 };
338
339 self.relevant.dispose();
340 self.swapchain.take().map(|s| device.destroy_swapchain(s));
341 self.surface
342 }
343
344 pub fn surface(&self) -> &Surface<B> {
346 &self.surface
347 }
348
349 pub fn swapchain(&self) -> &B::Swapchain {
351 self.swapchain.as_ref().expect("Swapchain already disposed")
352 }
353
354 pub unsafe fn recreate(
360 &mut self,
361 physical_device: &B::PhysicalDevice,
362 device: &Device<B>,
363 suggest_extent: Extent2D,
364 ) -> Result<(), SwapchainError> {
365 self.assert_device_owner(device);
366
367 let image_count = match self.backbuffer.take() {
368 Some(images) => {
369 let count = images.len();
370 images
371 .into_iter()
372 .for_each(|image| image.dispose_swapchain_image(device.id()));
373 count
374 }
375 None => 0,
376 };
377
378 self.swapchain.take().map(|s| device.destroy_swapchain(s));
379
380 let (swapchain, backbuffer, extent) = create_swapchain(
381 &mut self.surface,
382 physical_device,
383 device,
384 suggest_extent,
385 image_count as u32,
386 self.present_mode,
387 self.usage,
388 )?;
389
390 self.swapchain.replace(swapchain);
391 self.backbuffer.replace(backbuffer);
392 self.extent = extent;
393
394 Ok(())
395 }
396
397 pub unsafe fn swapchain_mut(&mut self) -> &mut impl rendy_core::hal::window::Swapchain<B> {
403 self.swapchain.as_mut().expect("Swapchain already disposed")
404 }
405
406 pub fn backbuffer(&self) -> &Vec<Image<B>> {
408 self.backbuffer
409 .as_ref()
410 .expect("Swapchain already disposed")
411 }
412
413 pub fn extent(&self) -> Extent2D {
415 self.extent
416 }
417
418 pub fn usage(&self) -> rendy_core::hal::image::Usage {
420 self.usage
421 }
422
423 pub unsafe fn next_image(
425 &mut self,
426 signal: &B::Semaphore,
427 ) -> Result<NextImages<'_, B>, rendy_core::hal::window::AcquireError> {
428 let index = rendy_core::hal::window::Swapchain::acquire_image(
429 self.swapchain
431 .as_mut()
432 .ok_or(rendy_core::hal::window::AcquireError::OutOfDate)?,
433 !0,
434 Some(signal),
435 None,
436 )?
437 .0;
438
439 Ok(NextImages {
440 targets: std::iter::once((&*self, index)).collect(),
441 })
442 }
443}
444
445#[derive(Debug)]
447pub struct NextImages<'a, B: Backend> {
448 targets: smallvec::SmallVec<[(&'a Target<B>, u32); 8]>,
449}
450
451impl<'a, B> NextImages<'a, B>
452where
453 B: Backend,
454{
455 pub fn indices(&self) -> impl IntoIterator<Item = u32> + '_ {
457 self.targets.iter().map(|(_s, i)| *i)
458 }
459
460 pub unsafe fn present<'b>(
466 self,
467 queue: &mut impl rendy_core::hal::queue::CommandQueue<B>,
468 wait: impl IntoIterator<Item = &'b (impl std::borrow::Borrow<B::Semaphore> + 'b)>,
469 ) -> Result<Option<rendy_core::hal::window::Suboptimal>, rendy_core::hal::window::PresentError>
470 where
471 'a: 'b,
472 {
473 queue.present(
474 self.targets.iter().map(|(target, index)| {
475 (
476 target
477 .swapchain
478 .as_ref()
479 .expect("Swapchain already disposed"),
480 *index,
481 )
482 }),
483 wait,
484 )
485 }
486}
487
488impl<'a, B> std::ops::Index<usize> for NextImages<'a, B>
489where
490 B: Backend,
491{
492 type Output = u32;
493
494 fn index(&self, index: usize) -> &u32 {
495 &self.targets[index].1
496 }
497}