1use alloc::{sync::Arc, vec::Vec};
13use core::mem::ManuallyDrop;
14
15#[cfg(feature = "trace")]
16use crate::device::trace::Action;
17use crate::{
18 conv,
19 device::{Device, DeviceError, MissingDownlevelFlags, WaitIdleError},
20 global::Global,
21 hal_label, id,
22 instance::Surface,
23 resource,
24};
25
26use thiserror::Error;
27use wgt::SurfaceStatus as Status;
28
29const FRAME_TIMEOUT_MS: u32 = 1000;
30
31#[derive(Debug)]
32pub(crate) struct Presentation {
33 pub(crate) device: Arc<Device>,
34 pub(crate) config: wgt::SurfaceConfiguration<Vec<wgt::TextureFormat>>,
35 pub(crate) acquired_texture: Option<Arc<resource::Texture>>,
36}
37
38#[derive(Clone, Debug, Error)]
39#[non_exhaustive]
40pub enum SurfaceError {
41 #[error("Surface is invalid")]
42 Invalid,
43 #[error("Surface is not configured for presentation")]
44 NotConfigured,
45 #[error(transparent)]
46 Device(#[from] DeviceError),
47 #[error("Surface image is already acquired")]
48 AlreadyAcquired,
49 #[error("Texture has been destroyed")]
50 TextureDestroyed,
51}
52
53#[derive(Clone, Debug, Error)]
54#[non_exhaustive]
55pub enum ConfigureSurfaceError {
56 #[error(transparent)]
57 Device(#[from] DeviceError),
58 #[error("Invalid surface")]
59 InvalidSurface,
60 #[error("The view format {0:?} is not compatible with texture format {1:?}, only changing srgb-ness is allowed.")]
61 InvalidViewFormat(wgt::TextureFormat, wgt::TextureFormat),
62 #[error(transparent)]
63 MissingDownlevelFlags(#[from] MissingDownlevelFlags),
64 #[error("`SurfaceOutput` must be dropped before a new `Surface` is made")]
65 PreviousOutputExists,
66 #[error("Failed to wait for GPU to come idle before reconfiguring the Surface")]
67 GpuWaitTimeout,
68 #[error("Both `Surface` width and height must be non-zero. Wait to recreate the `Surface` until the window has non-zero area.")]
69 ZeroArea,
70 #[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}.")]
71 TooLarge {
72 width: u32,
73 height: u32,
74 max_texture_dimension_2d: u32,
75 },
76 #[error("Surface does not support the adapter's queue family")]
77 UnsupportedQueueFamily,
78 #[error("Requested format {requested:?} is not in list of supported formats: {available:?}")]
79 UnsupportedFormat {
80 requested: wgt::TextureFormat,
81 available: Vec<wgt::TextureFormat>,
82 },
83 #[error("Requested present mode {requested:?} is not in the list of supported present modes: {available:?}")]
84 UnsupportedPresentMode {
85 requested: wgt::PresentMode,
86 available: Vec<wgt::PresentMode>,
87 },
88 #[error("Requested alpha mode {requested:?} is not in the list of supported alpha modes: {available:?}")]
89 UnsupportedAlphaMode {
90 requested: wgt::CompositeAlphaMode,
91 available: Vec<wgt::CompositeAlphaMode>,
92 },
93 #[error("Requested usage {requested:?} is not in the list of supported usages: {available:?}")]
94 UnsupportedUsage {
95 requested: wgt::TextureUses,
96 available: wgt::TextureUses,
97 },
98}
99
100impl From<WaitIdleError> for ConfigureSurfaceError {
101 fn from(e: WaitIdleError) -> Self {
102 match e {
103 WaitIdleError::Device(d) => ConfigureSurfaceError::Device(d),
104 WaitIdleError::WrongSubmissionIndex(..) => unreachable!(),
105 WaitIdleError::Timeout => ConfigureSurfaceError::GpuWaitTimeout,
106 }
107 }
108}
109
110pub type ResolvedSurfaceOutput = SurfaceOutput<Arc<resource::Texture>>;
111
112#[repr(C)]
113#[derive(Debug)]
114pub struct SurfaceOutput<T = id::TextureId> {
115 pub status: Status,
116 pub texture: Option<T>,
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(core::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(alloc::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: wgt::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, wgt::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 {
341 status,
342 texture: texture_id,
343 })
344 }
345
346 pub fn surface_present(&self, surface_id: id::SurfaceId) -> Result<Status, SurfaceError> {
347 let surface = self.surfaces.get(surface_id);
348
349 #[cfg(feature = "trace")]
350 if let Some(present) = surface.presentation.lock().as_ref() {
351 if let Some(ref mut trace) = *present.device.trace.lock() {
352 trace.add(Action::Present(surface_id));
353 }
354 }
355
356 surface.present()
357 }
358
359 pub fn surface_texture_discard(&self, surface_id: id::SurfaceId) -> Result<(), SurfaceError> {
360 let surface = self.surfaces.get(surface_id);
361
362 #[cfg(feature = "trace")]
363 if let Some(present) = surface.presentation.lock().as_ref() {
364 if let Some(ref mut trace) = *present.device.trace.lock() {
365 trace.add(Action::DiscardSurfaceTexture(surface_id));
366 }
367 }
368
369 surface.discard()
370 }
371}