1use crate::{
4 command::{
5 CommandBuffer, CommandPool, ExecutableState, Families, Family, FamilyId, Fence, MultiShot,
6 PendingState, Queue, SimultaneousUse, Submission, Submit,
7 },
8 factory::Factory,
9 frame::Frames,
10 graph::GraphContext,
11 node::{
12 gfx_acquire_barriers, gfx_release_barriers, BufferAccess, DynNode, ImageAccess, NodeBuffer,
13 NodeBuildError, NodeBuilder, NodeImage,
14 },
15 wsi::{Surface, Target},
16 BufferId, ImageId, NodeId,
17};
18
19#[derive(Debug)]
20struct ForImage<B: rendy_core::hal::Backend> {
21 acquire: B::Semaphore,
22 release: B::Semaphore,
23 submit: Submit<B, SimultaneousUse>,
24 buffer: CommandBuffer<
25 B,
26 rendy_core::hal::queue::QueueType,
27 PendingState<ExecutableState<MultiShot<SimultaneousUse>>>,
28 >,
29}
30
31impl<B: rendy_core::hal::Backend> ForImage<B> {
32 unsafe fn dispose(
33 self,
34 factory: &Factory<B>,
35 pool: &mut CommandPool<B, rendy_core::hal::queue::QueueType>,
36 ) {
37 drop(self.submit);
38 factory.destroy_semaphore(self.acquire);
39 factory.destroy_semaphore(self.release);
40 pool.free_buffers(Some(self.buffer.mark_complete()));
41 }
42}
43
44#[derive(Debug)]
46pub struct PresentNode<B: rendy_core::hal::Backend> {
47 per_image: Vec<ForImage<B>>,
48 free_acquire: B::Semaphore,
49 target: Target<B>,
50 pool: CommandPool<B, rendy_core::hal::queue::QueueType>,
51 input_image: NodeImage,
52 blit_filter: rendy_core::hal::image::Filter,
53}
54
55unsafe impl<B: rendy_core::hal::Backend> Sync for PresentNode<B> {}
57unsafe impl<B: rendy_core::hal::Backend> Send for PresentNode<B> {}
58
59impl<B> PresentNode<B>
60where
61 B: rendy_core::hal::Backend,
62{
63 pub fn builder(factory: &Factory<B>, surface: Surface<B>, image: ImageId) -> PresentBuilder<B> {
71 use rendy_core::hal::window::PresentMode;
72
73 let caps = factory.get_surface_capabilities(&surface);
74 let image_count = 3
75 .min(*caps.image_count.end())
76 .max(*caps.image_count.start());
77
78 let present_mode = match () {
79 _ if caps.present_modes.contains(PresentMode::FIFO) => PresentMode::FIFO,
80 _ if caps.present_modes.contains(PresentMode::MAILBOX) => PresentMode::MAILBOX,
81 _ if caps.present_modes.contains(PresentMode::RELAXED) => PresentMode::RELAXED,
82 _ if caps.present_modes.contains(PresentMode::IMMEDIATE) => PresentMode::IMMEDIATE,
83 _ => panic!("No known present modes found"),
84 };
85
86 PresentBuilder {
87 surface,
88 image,
89 dependencies: Vec::new(),
90 image_count,
91 present_mode,
92 caps,
93 blit_filter: rendy_core::hal::image::Filter::Nearest,
94 }
95 }
96}
97
98fn create_per_image_data<B: rendy_core::hal::Backend>(
99 ctx: &GraphContext<B>,
100 input_image: &NodeImage,
101 pool: &mut CommandPool<B, rendy_core::hal::queue::QueueType>,
102 factory: &Factory<B>,
103 target: &Target<B>,
104 blit_filter: rendy_core::hal::image::Filter,
105) -> Vec<ForImage<B>> {
106 let input_image_res = ctx.get_image(input_image.id).expect("Image does not exist");
107
108 let target_images = target.backbuffer();
109 let buffers = pool.allocate_buffers(target_images.len());
110 target_images
111 .iter()
112 .zip(buffers)
113 .map(|(target_image, buf_initial)| {
114 let mut buf_recording = buf_initial.begin(MultiShot(SimultaneousUse), ());
115 let mut encoder = buf_recording.encoder();
116 let (mut stages, mut barriers) =
117 gfx_acquire_barriers(ctx, None, Some(input_image));
118 stages.start |= rendy_core::hal::pso::PipelineStage::TRANSFER;
119 stages.end |= rendy_core::hal::pso::PipelineStage::TRANSFER;
120 barriers.push(rendy_core::hal::memory::Barrier::Image {
121 states: (
122 rendy_core::hal::image::Access::empty(),
123 rendy_core::hal::image::Layout::Undefined,
124 )
125 ..(
126 rendy_core::hal::image::Access::TRANSFER_WRITE,
127 rendy_core::hal::image::Layout::TransferDstOptimal,
128 ),
129 families: None,
130 target: target_image.raw(),
131 range: rendy_core::hal::image::SubresourceRange {
132 aspects: rendy_core::hal::format::Aspects::COLOR,
133 levels: 0..1,
134 layers: 0..1,
135 },
136 });
137 log::trace!("Acquire {:?} : {:#?}", stages, barriers);
138 unsafe {
139 encoder.pipeline_barrier(
140 stages,
141 rendy_core::hal::memory::Dependencies::empty(),
142 barriers,
143 );
144 }
145
146 let extents_differ = target_image.kind().extent() != input_image_res.kind().extent();
147 let formats_differ = target_image.format() != input_image_res.format();
148
149 if extents_differ || formats_differ
150 {
151 if formats_differ {
152 log::debug!("Present node is blitting because target format {:?} doesnt match image format {:?}", target_image.format(), input_image_res.format());
153 }
154 if extents_differ {
155 log::debug!("Present node is blitting because target extent {:?} doesnt match image extent {:?}", target_image.kind().extent(), input_image_res.kind().extent());
156 }
157 unsafe {
158 encoder.blit_image(
159 input_image_res.raw(),
160 input_image.layout,
161 target_image.raw(),
162 rendy_core::hal::image::Layout::TransferDstOptimal,
163 blit_filter,
164 Some(rendy_core::hal::command::ImageBlit {
165 src_subresource: rendy_core::hal::image::SubresourceLayers {
166 aspects: input_image.range.aspects,
167 level: 0,
168 layers: input_image.range.layers.start..input_image.range.layers.start + 1,
169 },
170 src_bounds: rendy_core::hal::image::Offset::ZERO
171 .into_bounds(&input_image_res.kind().extent()),
172 dst_subresource: rendy_core::hal::image::SubresourceLayers {
173 aspects: rendy_core::hal::format::Aspects::COLOR,
174 level: 0,
175 layers: 0..1,
176 },
177 dst_bounds: rendy_core::hal::image::Offset::ZERO
178 .into_bounds(&target_image.kind().extent()),
179 }),
180 );
181 }
182 } else {
183 log::debug!("Present node is copying");
184 unsafe {
185 encoder.copy_image(
186 input_image_res.raw(),
187 input_image.layout,
188 target_image.raw(),
189 rendy_core::hal::image::Layout::TransferDstOptimal,
190 Some(rendy_core::hal::command::ImageCopy {
191 src_subresource: rendy_core::hal::image::SubresourceLayers {
192 aspects: input_image.range.aspects,
193 level: 0,
194 layers: input_image.range.layers.start..input_image.range.layers.start + 1,
195 },
196 src_offset: rendy_core::hal::image::Offset::ZERO,
197 dst_subresource: rendy_core::hal::image::SubresourceLayers {
198 aspects: rendy_core::hal::format::Aspects::COLOR,
199 level: 0,
200 layers: 0..1,
201 },
202 dst_offset: rendy_core::hal::image::Offset::ZERO,
203 extent: rendy_core::hal::image::Extent {
204 width: target_image.kind().extent().width,
205 height: target_image.kind().extent().height,
206 depth: 1,
207 },
208 }),
209 );
210 }
211 }
212
213 {
214 let (mut stages, mut barriers) =
215 gfx_release_barriers(ctx, None, Some(input_image));
216 stages.start |= rendy_core::hal::pso::PipelineStage::TRANSFER;
217 stages.end |= rendy_core::hal::pso::PipelineStage::BOTTOM_OF_PIPE;
218 barriers.push(rendy_core::hal::memory::Barrier::Image {
219 states: (
220 rendy_core::hal::image::Access::TRANSFER_WRITE,
221 rendy_core::hal::image::Layout::TransferDstOptimal,
222 )
223 ..(
224 rendy_core::hal::image::Access::empty(),
225 rendy_core::hal::image::Layout::Present,
226 ),
227 families: None,
228 target: target_image.raw(),
229 range: rendy_core::hal::image::SubresourceRange {
230 aspects: rendy_core::hal::format::Aspects::COLOR,
231 levels: 0..1,
232 layers: 0..1,
233 },
234 });
235
236 log::trace!("Release {:?} : {:#?}", stages, barriers);
237 unsafe {
238 encoder.pipeline_barrier(
239 stages,
240 rendy_core::hal::memory::Dependencies::empty(),
241 barriers,
242 );
243 }
244 }
245
246 let (submit, buffer) = buf_recording.finish().submit();
247
248 ForImage {
249 submit,
250 buffer,
251 acquire: factory.create_semaphore().unwrap(),
252 release: factory.create_semaphore().unwrap(),
253 }
254 })
255 .collect()
256}
257
258#[derive(Debug)]
260pub struct PresentBuilder<B: rendy_core::hal::Backend> {
261 surface: Surface<B>,
262 image: ImageId,
263 image_count: u32,
264 present_mode: rendy_core::hal::window::PresentMode,
265 caps: rendy_core::hal::window::SurfaceCapabilities,
266 dependencies: Vec<NodeId>,
267 blit_filter: rendy_core::hal::image::Filter,
268}
269
270impl<B> PresentBuilder<B>
271where
272 B: rendy_core::hal::Backend,
273{
274 pub fn add_dependency(&mut self, dependency: NodeId) -> &mut Self {
277 self.dependencies.push(dependency);
278 self
279 }
280
281 pub fn with_dependency(mut self, dependency: NodeId) -> Self {
284 self.add_dependency(dependency);
285 self
286 }
287
288 pub fn with_image_count(mut self, image_count: u32) -> Self {
294 let image_count = image_count
295 .min(*self.caps.image_count.end())
296 .max(*self.caps.image_count.start());
297 self.image_count = image_count;
298 self
299 }
300
301 pub fn with_blit_filter(mut self, filter: rendy_core::hal::image::Filter) -> Self {
305 self.blit_filter = filter;
306 self
307 }
308
309 pub fn with_present_modes_priority<PF>(mut self, present_modes_priority: PF) -> Self
323 where
324 PF: Fn(rendy_core::hal::window::PresentMode) -> Option<usize>,
325 {
326 use rendy_core::hal::window::PresentMode;
327
328 let priority_mode = [
329 PresentMode::FIFO,
330 PresentMode::MAILBOX,
331 PresentMode::RELAXED,
332 PresentMode::IMMEDIATE,
333 ]
334 .iter()
335 .cloned()
336 .filter(|&mode| self.caps.present_modes.contains(mode))
337 .filter_map(|mode| present_modes_priority(mode).map(|p| (p, mode)))
338 .max_by_key(|&(p, _)| p);
339
340 if let Some((_, mode)) = priority_mode {
341 self.present_mode = mode;
342 } else {
343 panic!(
344 "No desired PresentModes are supported. Supported: {:#?}",
345 self.caps.present_modes
346 );
347 }
348
349 self
350 }
351
352 pub fn image_count(&self) -> u32 {
354 self.image_count
355 }
356
357 pub fn present_mode(&self) -> rendy_core::hal::window::PresentMode {
359 self.present_mode
360 }
361}
362
363impl<B, T> NodeBuilder<B, T> for PresentBuilder<B>
364where
365 B: rendy_core::hal::Backend,
366 T: ?Sized,
367{
368 fn family(&self, factory: &mut Factory<B>, families: &Families<B>) -> Option<FamilyId> {
369 families.find(|family| factory.surface_support(family.id(), &self.surface))
371 }
372
373 fn buffers(&self) -> Vec<(BufferId, BufferAccess)> {
374 Vec::new()
375 }
376
377 fn images(&self) -> Vec<(ImageId, ImageAccess)> {
378 vec![(
379 self.image,
380 ImageAccess {
381 access: rendy_core::hal::image::Access::TRANSFER_READ,
382 layout: rendy_core::hal::image::Layout::TransferSrcOptimal,
383 usage: rendy_core::hal::image::Usage::TRANSFER_SRC,
384 stages: rendy_core::hal::pso::PipelineStage::TRANSFER,
385 },
386 )]
387 }
388
389 fn dependencies(&self) -> Vec<NodeId> {
390 self.dependencies.clone()
391 }
392
393 fn build<'a>(
394 self: Box<Self>,
395 ctx: &GraphContext<B>,
396 factory: &mut Factory<B>,
397 family: &mut Family<B>,
398 _queue: usize,
399 _aux: &T,
400 buffers: Vec<NodeBuffer>,
401 images: Vec<NodeImage>,
402 ) -> Result<Box<dyn DynNode<B, T>>, NodeBuildError> {
403 assert_eq!(buffers.len(), 0);
404 assert_eq!(images.len(), 1);
405
406 let input_image = images.into_iter().next().unwrap();
407 let extent = ctx
408 .get_image(input_image.id)
409 .expect("Context must contain node's image")
410 .kind()
411 .extent()
412 .into();
413
414 if !factory.surface_support(family.id(), &self.surface) {
415 log::warn!(
416 "Surface {:?} presentation is unsupported by family {:?} bound to the node",
417 self.surface,
418 family
419 );
420 return Err(NodeBuildError::QueueFamily(family.id()));
421 }
422
423 let target = factory
424 .create_target(
425 self.surface,
426 extent,
427 self.image_count,
428 self.present_mode,
429 rendy_core::hal::image::Usage::TRANSFER_DST,
430 )
431 .map_err(NodeBuildError::Swapchain)?;
432
433 let mut pool = factory
434 .create_command_pool(family)
435 .map_err(NodeBuildError::OutOfMemory)?;
436
437 let per_image = create_per_image_data(
438 ctx,
439 &input_image,
440 &mut pool,
441 factory,
442 &target,
443 self.blit_filter,
444 );
445
446 Ok(Box::new(PresentNode {
447 free_acquire: factory.create_semaphore().unwrap(),
448 pool,
449 target,
450 per_image,
451 input_image,
452 blit_filter: self.blit_filter,
453 }))
454 }
455}
456
457impl<B, T> DynNode<B, T> for PresentNode<B>
458where
459 B: rendy_core::hal::Backend,
460 T: ?Sized,
461{
462 unsafe fn run<'a>(
463 &mut self,
464 ctx: &GraphContext<B>,
465 factory: &Factory<B>,
466 queue: &mut Queue<B>,
467 _aux: &T,
468 _frames: &Frames<B>,
469 waits: &[(&'a B::Semaphore, rendy_core::hal::pso::PipelineStage)],
470 signals: &[&'a B::Semaphore],
471 mut fence: Option<&mut Fence<B>>,
472 ) {
473 loop {
474 match self.target.next_image(&self.free_acquire) {
475 Ok(next) => {
476 log::trace!("Present: {:#?}", next);
477 let ref mut for_image = self.per_image[next[0] as usize];
478 core::mem::swap(&mut for_image.acquire, &mut self.free_acquire);
479
480 queue.submit(
481 Some(
482 Submission::new()
483 .submits(Some(&for_image.submit))
484 .wait(waits.iter().cloned().chain(Some((
485 &for_image.acquire,
486 rendy_core::hal::pso::PipelineStage::TRANSFER,
487 ))))
488 .signal(signals.iter().cloned().chain(Some(&for_image.release))),
489 ),
490 fence.take(),
491 );
492
493 match next.present(queue.raw(), Some(&for_image.release)) {
494 Ok(_) => break,
495 Err(e) => {
496 log::debug!(
497 "Swapchain present error after next_image is acquired: {:?}",
498 e
499 );
500 break;
502 }
503 }
504 }
505 Err(rendy_core::hal::window::AcquireError::OutOfDate) => {
506 }
508 e => {
509 e.unwrap();
510 break;
511 }
512 }
513 factory.wait_idle().unwrap();
518
519 let extent = ctx
520 .get_image(self.input_image.id)
521 .expect("Context must contain node's image")
522 .kind()
523 .extent()
524 .into();
525
526 self.target
527 .recreate(factory.physical(), factory.device(), extent)
528 .expect("Failed recreating swapchain");
529
530 for data in self.per_image.drain(..) {
531 data.dispose(factory, &mut self.pool);
532 }
533
534 self.per_image = create_per_image_data(
535 ctx,
536 &self.input_image,
537 &mut self.pool,
538 factory,
539 &self.target,
540 self.blit_filter,
541 );
542 }
543 }
544
545 unsafe fn dispose(mut self: Box<Self>, factory: &mut Factory<B>, _aux: &T) {
546 for data in self.per_image {
547 data.dispose(factory, &mut self.pool);
548 }
549
550 factory.destroy_semaphore(self.free_acquire);
551 factory.destroy_command_pool(self.pool);
552 factory.destroy_target(self.target);
553 }
554}