1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 /*! Swap chain management.
6 
7     ## Lifecycle
8 
9     At the low level, the swap chain is using the new simplified model of gfx-rs.
10 
11     A swap chain is a separate object that is backend-dependent but shares the index with
12     the parent surface, which is backend-independent. This ensures a 1:1 correspondence
13     between them.
14 
15     `get_next_image()` requests a new image from the surface. It becomes a part of
16     `TextureViewInner::SwapChain` of the resulted view. The view is registered in the HUB
17     but not in the device tracker.
18 
19     The only operation allowed on the view is to be either a color or a resolve attachment.
20     It can only be used in one command buffer, which needs to be submitted before presenting.
21     Command buffer tracker knows about the view, but only for the duration of recording.
22     The view ID is erased from it at the end, so that it's not merged into the device tracker.
23 
24     When a swapchain view is used in `begin_render_pass()`, we assume the start and end image
25     layouts purely based on whether or not this view was used in this command buffer before.
26     It always starts with `Uninitialized` and ends with `Present`, so that no barriers are
27     needed when we need to actually present it.
28 
29     In `queue_submit()` we make sure to signal the semaphore whenever we render to a swap
30     chain view.
31 
32     In `present()` we return the swap chain image back and wait on the semaphore.
33 !*/
34 
35 #[cfg(feature = "trace")]
36 use crate::device::trace::Action;
37 use crate::{
38     conv,
39     hub::{GfxBackend, Global, GlobalIdentityHandlerFactory, Input, Token},
40     id::{DeviceId, SwapChainId, TextureViewId},
41     resource, LifeGuard, PrivateFeatures, Stored,
42 };
43 
44 use hal::{self, device::Device as _, queue::CommandQueue as _, window::PresentationSurface as _};
45 use wgt::SwapChainDescriptor;
46 
47 const FRAME_TIMEOUT_MS: u64 = 1000;
48 pub const DESIRED_NUM_FRAMES: u32 = 3;
49 
50 #[derive(Debug)]
51 pub struct SwapChain<B: hal::Backend> {
52     pub(crate) life_guard: LifeGuard,
53     pub(crate) device_id: Stored<DeviceId>,
54     pub(crate) desc: SwapChainDescriptor,
55     pub(crate) num_frames: hal::window::SwapImageIndex,
56     pub(crate) semaphore: B::Semaphore,
57     pub(crate) acquired_view_id: Option<Stored<TextureViewId>>,
58     pub(crate) acquired_framebuffers: Vec<B::Framebuffer>,
59 }
60 
swap_chain_descriptor_to_hal( desc: &SwapChainDescriptor, num_frames: u32, private_features: PrivateFeatures, ) -> hal::window::SwapchainConfig61 pub(crate) fn swap_chain_descriptor_to_hal(
62     desc: &SwapChainDescriptor,
63     num_frames: u32,
64     private_features: PrivateFeatures,
65 ) -> hal::window::SwapchainConfig {
66     let mut config = hal::window::SwapchainConfig::new(
67         desc.width,
68         desc.height,
69         conv::map_texture_format(desc.format, private_features),
70         num_frames,
71     );
72     //TODO: check for supported
73     config.image_usage = conv::map_texture_usage(desc.usage, hal::format::Aspects::COLOR);
74     config.composite_alpha_mode = hal::window::CompositeAlphaMode::OPAQUE;
75     config.present_mode = match desc.present_mode {
76         wgt::PresentMode::Immediate => hal::window::PresentMode::IMMEDIATE,
77         wgt::PresentMode::Mailbox => hal::window::PresentMode::MAILBOX,
78         wgt::PresentMode::Fifo => hal::window::PresentMode::FIFO,
79     };
80     config
81 }
82 
83 #[repr(C)]
84 #[derive(Debug)]
85 pub struct SwapChainOutput {
86     pub view_id: Option<TextureViewId>,
87 }
88 
89 #[derive(Debug)]
90 pub enum SwapChainGetNextTextureError {
91     GpuProcessingTimeout,
92 }
93 
94 impl<G: GlobalIdentityHandlerFactory> Global<G> {
swap_chain_get_next_texture<B: GfxBackend>( &self, swap_chain_id: SwapChainId, view_id_in: Input<G, TextureViewId>, ) -> Result<SwapChainOutput, SwapChainGetNextTextureError>95     pub fn swap_chain_get_next_texture<B: GfxBackend>(
96         &self,
97         swap_chain_id: SwapChainId,
98         view_id_in: Input<G, TextureViewId>,
99     ) -> Result<SwapChainOutput, SwapChainGetNextTextureError> {
100         let hub = B::hub(self);
101         let mut token = Token::root();
102 
103         let (mut surface_guard, mut token) = self.surfaces.write(&mut token);
104         let surface = &mut surface_guard[swap_chain_id.to_surface_id()];
105         let (device_guard, mut token) = hub.devices.read(&mut token);
106         let (mut swap_chain_guard, mut token) = hub.swap_chains.write(&mut token);
107         let sc = &mut swap_chain_guard[swap_chain_id];
108         let device = &device_guard[sc.device_id.value];
109 
110         let (image, _) = {
111             let suf = B::get_surface_mut(surface);
112             match unsafe { suf.acquire_image(FRAME_TIMEOUT_MS * 1_000_000) } {
113                 Ok(surface_image) => surface_image,
114                 Err(hal::window::AcquireError::Timeout) => {
115                     return Err(SwapChainGetNextTextureError::GpuProcessingTimeout);
116                 }
117                 Err(e) => {
118                     log::warn!("acquire_image() failed ({:?}), reconfiguring swapchain", e);
119                     let desc = swap_chain_descriptor_to_hal(
120                         &sc.desc,
121                         sc.num_frames,
122                         device.private_features,
123                     );
124                     unsafe {
125                         suf.configure_swapchain(&device.raw, desc).unwrap();
126                         suf.acquire_image(FRAME_TIMEOUT_MS * 1_000_000).unwrap()
127                     }
128                 }
129             }
130         };
131 
132         let view = resource::TextureView {
133             inner: resource::TextureViewInner::SwapChain {
134                 image,
135                 source_id: Stored {
136                     value: swap_chain_id,
137                     ref_count: sc.life_guard.add_ref(),
138                 },
139             },
140             format: sc.desc.format,
141             extent: hal::image::Extent {
142                 width: sc.desc.width,
143                 height: sc.desc.height,
144                 depth: 1,
145             },
146             samples: 1,
147             range: hal::image::SubresourceRange {
148                 aspects: hal::format::Aspects::COLOR,
149                 layers: 0..1,
150                 levels: 0..1,
151             },
152             life_guard: LifeGuard::new(),
153         };
154         let ref_count = view.life_guard.add_ref();
155         let id = hub
156             .texture_views
157             .register_identity(view_id_in, view, &mut token);
158 
159         #[cfg(feature = "trace")]
160         match device.trace {
161             Some(ref trace) => trace.lock().add(Action::GetSwapChainTexture {
162                 id,
163                 parent_id: swap_chain_id,
164             }),
165             None => (),
166         };
167 
168         assert!(
169             sc.acquired_view_id.is_none(),
170             "Swap chain image is already acquired"
171         );
172         sc.acquired_view_id = Some(Stored {
173             value: id,
174             ref_count,
175         });
176 
177         Ok(SwapChainOutput { view_id: Some(id) })
178     }
179 
swap_chain_present<B: GfxBackend>(&self, swap_chain_id: SwapChainId)180     pub fn swap_chain_present<B: GfxBackend>(&self, swap_chain_id: SwapChainId) {
181         let hub = B::hub(self);
182         let mut token = Token::root();
183 
184         let (mut surface_guard, mut token) = self.surfaces.write(&mut token);
185         let surface = &mut surface_guard[swap_chain_id.to_surface_id()];
186         let (mut device_guard, mut token) = hub.devices.write(&mut token);
187         let (mut swap_chain_guard, mut token) = hub.swap_chains.write(&mut token);
188         let sc = &mut swap_chain_guard[swap_chain_id];
189         let device = &mut device_guard[sc.device_id.value];
190 
191         #[cfg(feature = "trace")]
192         match device.trace {
193             Some(ref trace) => trace.lock().add(Action::PresentSwapChain(swap_chain_id)),
194             None => (),
195         };
196 
197         let view_id = sc
198             .acquired_view_id
199             .take()
200             .expect("Swap chain image is not acquired");
201         let (view, _) = hub.texture_views.unregister(view_id.value, &mut token);
202         let image = match view.inner {
203             resource::TextureViewInner::Native { .. } => unreachable!(),
204             resource::TextureViewInner::SwapChain { image, .. } => image,
205         };
206 
207         let err = unsafe {
208             let queue = &mut device.queue_group.queues[0];
209             queue.present_surface(B::get_surface_mut(surface), image, Some(&sc.semaphore))
210         };
211         if let Err(e) = err {
212             log::warn!("present failed: {:?}", e);
213         }
214 
215         for fbo in sc.acquired_framebuffers.drain(..) {
216             unsafe {
217                 device.raw.destroy_framebuffer(fbo);
218             }
219         }
220     }
221 }
222