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