Refactor presentation to be explicit

This commit is contained in:
Dzmitry Malyshau
2021-10-05 09:38:41 -04:00
committed by Dzmitry Malyshau
parent 302d6c42d1
commit 5092fb7ea5
10 changed files with 151 additions and 48 deletions

View File

@@ -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));
}

View File

@@ -149,7 +149,9 @@ impl GlobalPlay for wgc::hub::Global<IdentityPassThroughFactory> {
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) => {

View File

@@ -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>,

View File

@@ -277,4 +277,64 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
},
}
}
pub fn surface_texture_discard<A: HalApi>(
&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(())
}
}

View File

@@ -293,21 +293,22 @@ fn start<E: Example>(
}
}
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();
}
_ => {}
}

View File

@@ -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,

View File

@@ -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 {

View File

@@ -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)) {

View File

@@ -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()

View File

@@ -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: <C as Context>::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: <C as Context>::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<SurfaceFrame, SurfaceError> {
pub fn get_current_texture(&self) -> Result<SurfaceTexture, SurfaceError> {
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)
}
}