cart_tmp_wgc/
swap_chain.rs

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")]
36use crate::device::trace::Action;
37use crate::{
38    conv,
39    hub::{GfxBackend, Global, GlobalIdentityHandlerFactory, Input, Token},
40    id::{DeviceId, SwapChainId, TextureViewId},
41    resource, span, LifeGuard, PrivateFeatures, Stored, SubmissionIndex,
42};
43
44use hal::{self, device::Device as _, queue::CommandQueue as _, window::PresentationSurface as _};
45use wgt::{SwapChainDescriptor, SwapChainStatus};
46
47const FRAME_TIMEOUT_MS: u64 = 1000;
48pub const DESIRED_NUM_FRAMES: u32 = 3;
49
50#[derive(Debug)]
51pub 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    pub(crate) active_submission_index: SubmissionIndex,
60}
61
62pub(crate) fn swap_chain_descriptor_to_hal(
63    desc: &SwapChainDescriptor,
64    num_frames: u32,
65    private_features: PrivateFeatures,
66) -> hal::window::SwapchainConfig {
67    let mut config = hal::window::SwapchainConfig::new(
68        desc.width,
69        desc.height,
70        conv::map_texture_format(desc.format, private_features),
71        num_frames,
72    );
73    //TODO: check for supported
74    config.image_usage = conv::map_texture_usage(desc.usage, hal::format::Aspects::COLOR);
75    config.composite_alpha_mode = hal::window::CompositeAlphaMode::OPAQUE;
76    config.present_mode = match desc.present_mode {
77        wgt::PresentMode::Immediate => hal::window::PresentMode::IMMEDIATE,
78        wgt::PresentMode::Mailbox => hal::window::PresentMode::MAILBOX,
79        wgt::PresentMode::Fifo => hal::window::PresentMode::FIFO,
80    };
81    config
82}
83
84#[repr(C)]
85#[derive(Debug)]
86pub struct SwapChainOutput {
87    pub status: SwapChainStatus,
88    pub view_id: Option<TextureViewId>,
89}
90
91impl<G: GlobalIdentityHandlerFactory> Global<G> {
92    pub fn swap_chain_get_next_texture<B: GfxBackend>(
93        &self,
94        swap_chain_id: SwapChainId,
95        view_id_in: Input<G, TextureViewId>,
96    ) -> SwapChainOutput {
97        span!(_guard, INFO, "SwapChain::get_next_texture");
98
99        let hub = B::hub(self);
100        let mut token = Token::root();
101
102        let (mut surface_guard, mut token) = self.surfaces.write(&mut token);
103        let surface = &mut surface_guard[swap_chain_id.to_surface_id()];
104        let (device_guard, mut token) = hub.devices.read(&mut token);
105        let (mut swap_chain_guard, mut token) = hub.swap_chains.write(&mut token);
106        let sc = &mut swap_chain_guard[swap_chain_id];
107        #[cfg_attr(not(feature = "trace"), allow(unused_variables))]
108        let device = &device_guard[sc.device_id.value];
109
110        let suf = B::get_surface_mut(surface);
111        let (image, status) = match unsafe { suf.acquire_image(FRAME_TIMEOUT_MS * 1_000_000) } {
112            Ok((surface_image, None)) => (Some(surface_image), SwapChainStatus::Good),
113            Ok((surface_image, Some(_))) => (Some(surface_image), SwapChainStatus::Suboptimal),
114            Err(err) => (
115                None,
116                match err {
117                    hal::window::AcquireError::OutOfMemory(_) => SwapChainStatus::OutOfMemory,
118                    hal::window::AcquireError::NotReady => unreachable!(), // we always set a timeout
119                    hal::window::AcquireError::Timeout => SwapChainStatus::Timeout,
120                    hal::window::AcquireError::OutOfDate => SwapChainStatus::Outdated,
121                    hal::window::AcquireError::SurfaceLost(_) => SwapChainStatus::Lost,
122                    hal::window::AcquireError::DeviceLost(_) => SwapChainStatus::Lost,
123                },
124            ),
125        };
126
127        let view_id = image.map(|image| {
128            let view = resource::TextureView {
129                inner: resource::TextureViewInner::SwapChain {
130                    image,
131                    source_id: Stored {
132                        value: swap_chain_id,
133                        ref_count: sc.life_guard.add_ref(),
134                    },
135                },
136                format: sc.desc.format,
137                extent: hal::image::Extent {
138                    width: sc.desc.width,
139                    height: sc.desc.height,
140                    depth: 1,
141                },
142                samples: 1,
143                range: hal::image::SubresourceRange {
144                    aspects: hal::format::Aspects::COLOR,
145                    layers: 0..1,
146                    levels: 0..1,
147                },
148                life_guard: LifeGuard::new(),
149            };
150
151            let ref_count = view.life_guard.add_ref();
152            let id = hub
153                .texture_views
154                .register_identity(view_id_in, view, &mut token);
155
156            assert!(
157                sc.acquired_view_id.is_none(),
158                "Swap chain image is already acquired"
159            );
160
161            sc.acquired_view_id = Some(Stored {
162                value: id,
163                ref_count,
164            });
165
166            id
167        });
168
169        #[cfg(feature = "trace")]
170        match device.trace {
171            Some(ref trace) => trace.lock().add(Action::GetSwapChainTexture {
172                id: view_id,
173                parent_id: swap_chain_id,
174            }),
175            None => (),
176        };
177
178        SwapChainOutput { status, view_id }
179    }
180
181    pub fn swap_chain_present<B: GfxBackend>(&self, swap_chain_id: SwapChainId) {
182        span!(_guard, INFO, "SwapChain::present");
183
184        let hub = B::hub(self);
185        let mut token = Token::root();
186
187        let (mut surface_guard, mut token) = self.surfaces.write(&mut token);
188        let surface = &mut surface_guard[swap_chain_id.to_surface_id()];
189        let (mut device_guard, mut token) = hub.devices.write(&mut token);
190        let (mut swap_chain_guard, mut token) = hub.swap_chains.write(&mut token);
191        let sc = &mut swap_chain_guard[swap_chain_id];
192        let device = &mut device_guard[sc.device_id.value];
193
194        #[cfg(feature = "trace")]
195        match device.trace {
196            Some(ref trace) => trace.lock().add(Action::PresentSwapChain(swap_chain_id)),
197            None => (),
198        };
199
200        let view_id = sc
201            .acquired_view_id
202            .take()
203            .expect("Swap chain image is not acquired");
204        let (view, _) = hub.texture_views.unregister(view_id.value, &mut token);
205        let image = match view.inner {
206            resource::TextureViewInner::Native { .. } => unreachable!(),
207            resource::TextureViewInner::SwapChain { image, .. } => image,
208        };
209
210        let err = {
211            let sem = if sc.active_submission_index > device.last_completed_submission_index() {
212                Some(&sc.semaphore)
213            } else {
214                None
215            };
216            let queue = &mut device.queue_group.queues[0];
217            unsafe { queue.present_surface(B::get_surface_mut(surface), image, sem) }
218        };
219        if let Err(e) = err {
220            log::warn!("present failed: {:?}", e);
221        }
222
223        tracing::debug!(trace = true, "Presented. End of Frame");
224
225        for fbo in sc.acquired_framebuffers.drain(..) {
226            unsafe {
227                device.raw.destroy_framebuffer(fbo);
228            }
229        }
230    }
231}