diff --git a/player/src/bin/play.rs b/player/src/bin/play.rs index ad239115f5..524c859430 100644 --- a/player/src/bin/play.rs +++ b/player/src/bin/play.rs @@ -139,6 +139,11 @@ fn main() { gfx_select!(device => global.surface_present(id)).unwrap(); break; } + Some(trace::Action::DiscardSurfaceTexture(id)) => { + log::debug!("Discarding frame {}", frame_count); + gfx_select!(device => global.surface_texture_discard(id)).unwrap(); + break; + } Some(action) => { gfx_select!(device => global.process(device, action, &dir, &mut command_buffer_id_manager)); } diff --git a/player/src/lib.rs b/player/src/lib.rs index 627ecf6be2..597cf11dde 100644 --- a/player/src/lib.rs +++ b/player/src/lib.rs @@ -149,7 +149,9 @@ impl GlobalPlay for wgc::hub::Global { Action::Init { .. } => { panic!("Unexpected Action::Init: has to be the first action only") } - Action::ConfigureSurface { .. } | Action::Present(_) => { + Action::ConfigureSurface { .. } + | Action::Present(_) + | Action::DiscardSurfaceTexture(_) => { panic!("Unexpected Surface action: winit feature is not enabled") } Action::CreateBuffer(id, desc) => { diff --git a/wgpu-core/src/device/trace.rs b/wgpu-core/src/device/trace.rs index 279e9b11c4..b97323bd23 100644 --- a/wgpu-core/src/device/trace.rs +++ b/wgpu-core/src/device/trace.rs @@ -59,6 +59,7 @@ pub enum Action<'a> { parent_id: id::SurfaceId, }, Present(id::SurfaceId), + DiscardSurfaceTexture(id::SurfaceId), CreateBindGroupLayout( id::BindGroupLayoutId, crate::binding_model::BindGroupLayoutDescriptor<'a>, diff --git a/wgpu-core/src/present.rs b/wgpu-core/src/present.rs index 9daecf326b..05e93f2492 100644 --- a/wgpu-core/src/present.rs +++ b/wgpu-core/src/present.rs @@ -277,4 +277,64 @@ impl Global { }, } } + + pub fn surface_texture_discard( + &self, + surface_id: SurfaceId, + ) -> Result<(), SurfaceError> { + profiling::scope!("discard", "SwapChain"); + + let hub = A::hub(self); + let mut token = Token::root(); + + let (mut surface_guard, mut token) = self.surfaces.write(&mut token); + let surface = surface_guard + .get_mut(surface_id) + .map_err(|_| SurfaceError::Invalid)?; + let (mut device_guard, mut token) = hub.devices.write(&mut token); + + let present = match surface.presentation { + Some(ref mut present) => present, + None => return Err(SurfaceError::NotConfigured), + }; + + let device = &mut device_guard[present.device_id.value]; + + #[cfg(feature = "trace")] + if let Some(ref trace) = device.trace { + trace.lock().add(Action::DiscardSurfaceTexture(surface_id)); + } + + { + let texture_id = present + .acquired_texture + .take() + .ok_or(SurfaceError::AlreadyAcquired)?; + + // The texture ID got added to the device tracker by `submit()`, + // and now we are moving it away. + device.trackers.lock().textures.remove(texture_id.value); + + let (texture, _) = hub.textures.unregister(texture_id.value.0, &mut token); + if let Some(texture) = texture { + let suf = A::get_surface_mut(surface); + match texture.inner { + resource::TextureInner::Surface { + raw, + parent_id, + has_work: _, + } => { + if surface_id == parent_id.0 { + unsafe { suf.raw.discard_texture(raw) }; + } else { + log::warn!("Surface texture is outdated"); + } + } + resource::TextureInner::Native { .. } => unreachable!(), + } + } + } + + Ok(()) + } } diff --git a/wgpu/examples/framework.rs b/wgpu/examples/framework.rs index 12fbe5548d..ce64049da5 100644 --- a/wgpu/examples/framework.rs +++ b/wgpu/examples/framework.rs @@ -293,21 +293,22 @@ fn start( } } - let frame = match surface.get_current_frame() { + let frame = match surface.get_current_texture() { Ok(frame) => frame, Err(_) => { surface.configure(&device, &config); surface - .get_current_frame() + .get_current_texture() .expect("Failed to acquire next surface texture!") } }; let view = frame - .output .texture .create_view(&wgpu::TextureViewDescriptor::default()); example.render(&view, &device, &queue, &spawner); + + frame.present(); } _ => {} } diff --git a/wgpu/examples/hello-triangle/main.rs b/wgpu/examples/hello-triangle/main.rs index 938bc87c09..9e9440a34b 100644 --- a/wgpu/examples/hello-triangle/main.rs +++ b/wgpu/examples/hello-triangle/main.rs @@ -94,9 +94,8 @@ async fn run(event_loop: EventLoop<()>, window: Window) { } Event::RedrawRequested(_) => { let frame = surface - .get_current_frame() - .expect("Failed to acquire next swap chain texture") - .output; + .get_current_texture() + .expect("Failed to acquire next swap chain texture"); let view = frame .texture .create_view(&wgpu::TextureViewDescriptor::default()); @@ -120,6 +119,7 @@ async fn run(event_loop: EventLoop<()>, window: Window) { } queue.submit(Some(encoder.finish())); + frame.present(); } Event::WindowEvent { event: WindowEvent::CloseRequested, diff --git a/wgpu/examples/hello-windows/main.rs b/wgpu/examples/hello-windows/main.rs index 47ec008389..807a019553 100644 --- a/wgpu/examples/hello-windows/main.rs +++ b/wgpu/examples/hello-windows/main.rs @@ -49,12 +49,11 @@ impl Viewport { self.config.height = size.height; self.desc.surface.configure(device, &self.config); } - fn get_current_frame(&mut self) -> wgpu::SurfaceTexture { + fn get_current_texture(&mut self) -> wgpu::SurfaceTexture { self.desc .surface - .get_current_frame() + .get_current_texture() .expect("Failed to acquire next swap chain texture") - .output } } @@ -111,7 +110,7 @@ async fn run(event_loop: EventLoop<()>, viewports: Vec<(Window, wgpu::Color)>) { } Event::RedrawRequested(window_id) => { if let Some(viewport) = viewports.get_mut(&window_id) { - let frame = viewport.get_current_frame(); + let frame = viewport.get_current_texture(); let view = frame .texture .create_view(&wgpu::TextureViewDescriptor::default()); @@ -133,6 +132,7 @@ async fn run(event_loop: EventLoop<()>, viewports: Vec<(Window, wgpu::Color)>) { } queue.submit(Some(encoder.finish())); + frame.present(); } } Event::WindowEvent { diff --git a/wgpu/src/backend/direct.rs b/wgpu/src/backend/direct.rs index 5fa6632b1a..3b671ce25f 100644 --- a/wgpu/src/backend/direct.rs +++ b/wgpu/src/backend/direct.rs @@ -944,6 +944,18 @@ impl crate::Context for Context { } } + fn surface_texture_discard( + &self, + texture: &Self::TextureId, + detail: &Self::SurfaceOutputDetail, + ) { + let global = &self.0; + match wgc::gfx_select!(texture.id => global.surface_texture_discard(detail.surface_id)) { + Ok(_status) => (), + Err(err) => self.handle_error_fatal(err, "Surface::discard_texture"), + } + } + fn device_features(&self, device: &Self::DeviceId) -> Features { let global = &self.0; match wgc::gfx_select!(device.id => global.device_features(device.id)) { diff --git a/wgpu/src/backend/web.rs b/wgpu/src/backend/web.rs index 21bb863d98..616c758fb2 100644 --- a/wgpu/src/backend/web.rs +++ b/wgpu/src/backend/web.rs @@ -1163,6 +1163,14 @@ impl crate::Context for Context { // Swapchain is presented automatically } + fn surface_texture_discard( + &self, + _texture: &Self::TextureId, + _detail: &Self::SurfaceOutputDetail, + ) { + // Can't really discard this on the Web + } + fn device_features(&self, _device: &Self::DeviceId) -> wgt::Features { // TODO wgt::Features::empty() diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 88b4b8925b..56910ec81d 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -234,6 +234,11 @@ trait Context: Debug + Send + Sized + Sync { Self::SurfaceOutputDetail, ); fn surface_present(&self, texture: &Self::TextureId, detail: &Self::SurfaceOutputDetail); + fn surface_texture_discard( + &self, + texture: &Self::TextureId, + detail: &Self::SurfaceOutputDetail, + ); fn device_features(&self, device: &Self::DeviceId) -> Features; fn device_limits(&self, device: &Self::DeviceId) -> Limits; @@ -1331,24 +1336,19 @@ pub struct RenderBundleEncoderDescriptor<'a> { } /// Surface texture that can be rendered to. +/// Result of a successful call to [`Surface::get_current_texture`]. #[derive(Debug)] pub struct SurfaceTexture { /// Accessible view of the frame. pub texture: Texture, - detail: ::SurfaceOutputDetail, -} - -/// Result of a successful call to [`Surface::get_current_frame`]. -#[derive(Debug)] -pub struct SurfaceFrame { - /// The texture into which the next frame should be rendered. - pub output: SurfaceTexture, /// `true` if the acquired buffer can still be used for rendering, /// but should be recreated for maximum performance. pub suboptimal: bool, + presented: bool, + detail: ::SurfaceOutputDetail, } -/// Result of an unsuccessful call to [`Surface::get_current_frame`]. +/// Result of an unsuccessful call to [`Surface::get_current_texture`]. #[derive(Clone, PartialEq, Eq, Debug)] pub enum SurfaceError { /// A timeout was encountered while trying to acquire the next frame. @@ -3144,10 +3144,24 @@ impl Queue { } } +impl SurfaceTexture { + /// Schedule this texture to be presented on the owning surface. + /// + /// Needs to be called after any work on the texture is scheduled via [`Queue::submit`]. + pub fn present(mut self) { + self.presented = true; + Context::surface_present(&*self.texture.context, &self.texture.id, &self.detail); + } +} + impl Drop for SurfaceTexture { fn drop(&mut self) { - if !thread::panicking() { - Context::surface_present(&*self.texture.context, &self.texture.id, &self.detail); + if !self.presented && !thread::panicking() { + Context::surface_texture_discard( + &*self.texture.context, + &self.texture.id, + &self.detail, + ); } } } @@ -3164,7 +3178,7 @@ impl Surface { /// /// # Panics /// - /// - A old [`SurfaceFrame`] is still alive referencing an old surface. + /// - A old [`SurfaceTexture`] is still alive referencing an old surface. /// - Texture format requested is unsupported on the surface. pub fn configure(&self, device: &Device, config: &SurfaceConfiguration) { Context::surface_configure(&*self.context, &self.id, &device.id, config) @@ -3172,36 +3186,36 @@ impl Surface { /// Returns the next texture to be presented by the swapchain for drawing. /// - /// When the [`SurfaceFrame`] returned by this method is dropped, the swapchain will present - /// the texture to the associated [`Surface`]. + /// In order to present the [`SurfaceTexture`] returned by this method, + /// first a [`Queue::submit`] needs to be done with some work rendering to this texture. + /// Then [`SurfaceTexture::present`] needs to be called. /// - /// If a SurfaceFrame referencing this surface is alive when the swapchain is recreated, + /// If a SurfaceTexture referencing this surface is alive when the swapchain is recreated, /// recreating the swapchain will panic. - pub fn get_current_frame(&self) -> Result { + pub fn get_current_texture(&self) -> Result { let (texture_id, status, detail) = Context::surface_get_current_texture(&*self.context, &self.id); - let output = texture_id.map(|id| SurfaceTexture { - texture: Texture { - context: Arc::clone(&self.context), - id, - owned: false, - }, - detail, - }); - match status { - SurfaceStatus::Good => Ok(SurfaceFrame { - output: output.unwrap(), - suboptimal: false, - }), - SurfaceStatus::Suboptimal => Ok(SurfaceFrame { - output: output.unwrap(), - suboptimal: true, - }), - SurfaceStatus::Timeout => Err(SurfaceError::Timeout), - SurfaceStatus::Outdated => Err(SurfaceError::Outdated), - SurfaceStatus::Lost => Err(SurfaceError::Lost), - } + let suboptimal = match status { + SurfaceStatus::Good => false, + SurfaceStatus::Suboptimal => true, + SurfaceStatus::Timeout => return Err(SurfaceError::Timeout), + SurfaceStatus::Outdated => return Err(SurfaceError::Outdated), + SurfaceStatus::Lost => return Err(SurfaceError::Lost), + }; + + texture_id + .map(|id| SurfaceTexture { + texture: Texture { + context: Arc::clone(&self.context), + id, + owned: false, + }, + suboptimal, + presented: false, + detail, + }) + .ok_or(SurfaceError::Lost) } }