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