mirror of
https://github.com/gfx-rs/wgpu.git
synced 2026-04-22 03:02:01 -04:00
Refactor presentation to be explicit
This commit is contained in:
committed by
Dzmitry Malyshau
parent
302d6c42d1
commit
5092fb7ea5
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user