1use std::{mem::ManuallyDrop, sync::Arc};
13
14#[cfg(feature = "trace")]
15use crate::device::trace::Action;
16use crate::{
17 conv,
18 device::{Device, DeviceError, MissingDownlevelFlags, WaitIdleError},
19 global::Global,
20 hal_label, id,
21 instance::Surface,
22 resource,
23};
24
25use thiserror::Error;
26use wgt::SurfaceStatus as Status;
27
28const FRAME_TIMEOUT_MS: u32 = 1000;
29
30#[derive(Debug)]
31pub(crate) struct Presentation {
32 pub(crate) device: Arc<Device>,
33 pub(crate) config: wgt::SurfaceConfiguration<Vec<wgt::TextureFormat>>,
34 pub(crate) acquired_texture: Option<Arc<resource::Texture>>,
35}
36
37#[derive(Clone, Debug, Error)]
38#[non_exhaustive]
39pub enum SurfaceError {
40 #[error("Surface is invalid")]
41 Invalid,
42 #[error("Surface is not configured for presentation")]
43 NotConfigured,
44 #[error(transparent)]
45 Device(#[from] DeviceError),
46 #[error("Surface image is already acquired")]
47 AlreadyAcquired,
48 #[error("Texture has been destroyed")]
49 TextureDestroyed,
50 #[error("Acquired frame is still referenced")]
51 StillReferenced,
52}
53
54#[derive(Clone, Debug, Error)]
55#[non_exhaustive]
56pub enum ConfigureSurfaceError {
57 #[error(transparent)]
58 Device(#[from] DeviceError),
59 #[error("Invalid surface")]
60 InvalidSurface,
61 #[error("The view format {0:?} is not compatible with texture format {1:?}, only changing srgb-ness is allowed.")]
62 InvalidViewFormat(wgt::TextureFormat, wgt::TextureFormat),
63 #[error(transparent)]
64 MissingDownlevelFlags(#[from] MissingDownlevelFlags),
65 #[error("`SurfaceOutput` must be dropped before a new `Surface` is made")]
66 PreviousOutputExists,
67 #[error("Both `Surface` width and height must be non-zero. Wait to recreate the `Surface` until the window has non-zero area.")]
68 ZeroArea,
69 #[error("`Surface` width and height must be within the maximum supported texture size. Requested was ({width}, {height}), maximum extent for either dimension is {max_texture_dimension_2d}.")]
70 TooLarge {
71 width: u32,
72 height: u32,
73 max_texture_dimension_2d: u32,
74 },
75 #[error("Surface does not support the adapter's queue family")]
76 UnsupportedQueueFamily,
77 #[error("Requested format {requested:?} is not in list of supported formats: {available:?}")]
78 UnsupportedFormat {
79 requested: wgt::TextureFormat,
80 available: Vec<wgt::TextureFormat>,
81 },
82 #[error("Requested present mode {requested:?} is not in the list of supported present modes: {available:?}")]
83 UnsupportedPresentMode {
84 requested: wgt::PresentMode,
85 available: Vec<wgt::PresentMode>,
86 },
87 #[error("Requested alpha mode {requested:?} is not in the list of supported alpha modes: {available:?}")]
88 UnsupportedAlphaMode {
89 requested: wgt::CompositeAlphaMode,
90 available: Vec<wgt::CompositeAlphaMode>,
91 },
92 #[error("Requested usage {requested:?} is not in the list of supported usages: {available:?}")]
93 UnsupportedUsage {
94 requested: hal::TextureUses,
95 available: hal::TextureUses,
96 },
97 #[error("Gpu got stuck :(")]
98 StuckGpu,
99}
100
101impl From<WaitIdleError> for ConfigureSurfaceError {
102 fn from(e: WaitIdleError) -> Self {
103 match e {
104 WaitIdleError::Device(d) => ConfigureSurfaceError::Device(d),
105 WaitIdleError::WrongSubmissionIndex(..) => unreachable!(),
106 WaitIdleError::StuckGpu => ConfigureSurfaceError::StuckGpu,
107 }
108 }
109}
110
111#[derive(Debug)]
112pub struct ResolvedSurfaceOutput {
113 pub status: Status,
114 pub texture: Option<Arc<resource::Texture>>,
115}
116
117#[repr(C)]
118#[derive(Debug)]
119pub struct SurfaceOutput {
120 pub status: Status,
121 pub texture_id: Option<id::TextureId>,
122}
123
124impl Surface {
125 pub fn get_current_texture(&self) -> Result<ResolvedSurfaceOutput, SurfaceError> {
126 profiling::scope!("Surface::get_current_texture");
127
128 let (device, config) = if let Some(ref present) = *self.presentation.lock() {
129 present.device.check_is_valid()?;
130 (present.device.clone(), present.config.clone())
131 } else {
132 return Err(SurfaceError::NotConfigured);
133 };
134
135 let fence = device.fence.read();
136
137 let suf = self.raw(device.backend()).unwrap();
138 let (texture, status) = match unsafe {
139 suf.acquire_texture(
140 Some(std::time::Duration::from_millis(FRAME_TIMEOUT_MS as u64)),
141 fence.as_ref(),
142 )
143 } {
144 Ok(Some(ast)) => {
145 drop(fence);
146
147 let texture_desc = wgt::TextureDescriptor {
148 label: Some(std::borrow::Cow::Borrowed("<Surface Texture>")),
149 size: wgt::Extent3d {
150 width: config.width,
151 height: config.height,
152 depth_or_array_layers: 1,
153 },
154 sample_count: 1,
155 mip_level_count: 1,
156 format: config.format,
157 dimension: wgt::TextureDimension::D2,
158 usage: config.usage,
159 view_formats: config.view_formats,
160 };
161 let hal_usage = conv::map_texture_usage(config.usage, config.format.into());
162 let format_features = wgt::TextureFormatFeatures {
163 allowed_usages: wgt::TextureUsages::RENDER_ATTACHMENT,
164 flags: wgt::TextureFormatFeatureFlags::MULTISAMPLE_X4
165 | wgt::TextureFormatFeatureFlags::MULTISAMPLE_RESOLVE,
166 };
167 let clear_view_desc = hal::TextureViewDescriptor {
168 label: hal_label(
169 Some("(wgpu internal) clear surface texture view"),
170 device.instance_flags,
171 ),
172 format: config.format,
173 dimension: wgt::TextureViewDimension::D2,
174 usage: hal::TextureUses::COLOR_TARGET,
175 range: wgt::ImageSubresourceRange::default(),
176 };
177 let clear_view = unsafe {
178 device
179 .raw()
180 .create_texture_view(ast.texture.as_ref().borrow(), &clear_view_desc)
181 }
182 .map_err(|e| device.handle_hal_error(e))?;
183
184 let mut presentation = self.presentation.lock();
185 let present = presentation.as_mut().unwrap();
186 let texture = resource::Texture::new(
187 &device,
188 resource::TextureInner::Surface { raw: ast.texture },
189 hal_usage,
190 &texture_desc,
191 format_features,
192 resource::TextureClearMode::Surface {
193 clear_view: ManuallyDrop::new(clear_view),
194 },
195 true,
196 );
197
198 let texture = Arc::new(texture);
199
200 device
201 .trackers
202 .lock()
203 .textures
204 .insert_single(&texture, hal::TextureUses::UNINITIALIZED);
205
206 if present.acquired_texture.is_some() {
207 return Err(SurfaceError::AlreadyAcquired);
208 }
209 present.acquired_texture = Some(texture.clone());
210
211 let status = if ast.suboptimal {
212 Status::Suboptimal
213 } else {
214 Status::Good
215 };
216 (Some(texture), status)
217 }
218 Ok(None) => (None, Status::Timeout),
219 Err(err) => (
220 None,
221 match err {
222 hal::SurfaceError::Lost => Status::Lost,
223 hal::SurfaceError::Device(err) => {
224 return Err(device.handle_hal_error(err).into());
225 }
226 hal::SurfaceError::Outdated => Status::Outdated,
227 hal::SurfaceError::Other(msg) => {
228 log::error!("acquire error: {}", msg);
229 Status::Lost
230 }
231 },
232 ),
233 };
234
235 Ok(ResolvedSurfaceOutput { status, texture })
236 }
237
238 pub fn present(&self) -> Result<Status, SurfaceError> {
239 profiling::scope!("Surface::present");
240
241 let mut presentation = self.presentation.lock();
242 let present = match presentation.as_mut() {
243 Some(present) => present,
244 None => return Err(SurfaceError::NotConfigured),
245 };
246
247 let device = &present.device;
248
249 device.check_is_valid()?;
250 let queue = device.get_queue().unwrap();
251
252 let texture = present
253 .acquired_texture
254 .take()
255 .ok_or(SurfaceError::AlreadyAcquired)?;
256
257 let result = match texture.inner.snatch(&mut device.snatchable_lock.write()) {
258 None => return Err(SurfaceError::TextureDestroyed),
259 Some(resource::TextureInner::Surface { raw }) => {
260 let raw_surface = self.raw(device.backend()).unwrap();
261 let raw_queue = queue.raw();
262 unsafe { raw_queue.present(raw_surface, raw) }
263 }
264 _ => unreachable!(),
265 };
266
267 match result {
268 Ok(()) => Ok(Status::Good),
269 Err(err) => match err {
270 hal::SurfaceError::Lost => Ok(Status::Lost),
271 hal::SurfaceError::Device(err) => {
272 Err(SurfaceError::from(device.handle_hal_error(err)))
273 }
274 hal::SurfaceError::Outdated => Ok(Status::Outdated),
275 hal::SurfaceError::Other(msg) => {
276 log::error!("acquire error: {}", msg);
277 Err(SurfaceError::Invalid)
278 }
279 },
280 }
281 }
282
283 pub fn discard(&self) -> Result<(), SurfaceError> {
284 profiling::scope!("Surface::discard");
285
286 let mut presentation = self.presentation.lock();
287 let present = match presentation.as_mut() {
288 Some(present) => present,
289 None => return Err(SurfaceError::NotConfigured),
290 };
291
292 let device = &present.device;
293
294 device.check_is_valid()?;
295
296 let texture = present
297 .acquired_texture
298 .take()
299 .ok_or(SurfaceError::AlreadyAcquired)?;
300
301 match texture.inner.snatch(&mut device.snatchable_lock.write()) {
302 None => return Err(SurfaceError::TextureDestroyed),
303 Some(resource::TextureInner::Surface { raw }) => {
304 let raw_surface = self.raw(device.backend()).unwrap();
305 unsafe { raw_surface.discard_texture(raw) };
306 }
307 _ => unreachable!(),
308 }
309
310 Ok(())
311 }
312}
313
314impl Global {
315 pub fn surface_get_current_texture(
316 &self,
317 surface_id: id::SurfaceId,
318 texture_id_in: Option<id::TextureId>,
319 ) -> Result<SurfaceOutput, SurfaceError> {
320 let surface = self.surfaces.get(surface_id);
321
322 let fid = self.hub.textures.prepare(texture_id_in);
323
324 #[cfg(feature = "trace")]
325 if let Some(present) = surface.presentation.lock().as_ref() {
326 if let Some(ref mut trace) = *present.device.trace.lock() {
327 trace.add(Action::GetSurfaceTexture {
328 id: fid.id(),
329 parent_id: surface_id,
330 });
331 }
332 }
333
334 let output = surface.get_current_texture()?;
335
336 let status = output.status;
337 let texture_id = output
338 .texture
339 .map(|texture| fid.assign(resource::Fallible::Valid(texture)));
340
341 Ok(SurfaceOutput { status, texture_id })
342 }
343
344 pub fn surface_present(&self, surface_id: id::SurfaceId) -> Result<Status, SurfaceError> {
345 let surface = self.surfaces.get(surface_id);
346
347 #[cfg(feature = "trace")]
348 if let Some(present) = surface.presentation.lock().as_ref() {
349 if let Some(ref mut trace) = *present.device.trace.lock() {
350 trace.add(Action::Present(surface_id));
351 }
352 }
353
354 surface.present()
355 }
356
357 pub fn surface_texture_discard(&self, surface_id: id::SurfaceId) -> Result<(), SurfaceError> {
358 let surface = self.surfaces.get(surface_id);
359
360 #[cfg(feature = "trace")]
361 if let Some(present) = surface.presentation.lock().as_ref() {
362 if let Some(ref mut trace) = *present.device.trace.lock() {
363 trace.add(Action::DiscardSurfaceTexture(surface_id));
364 }
365 }
366
367 surface.discard()
368 }
369}