1#[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 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!(), 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}