Skip to content

Commit b1a8e37

Browse files
authored
Merge pull request #700 from kas-gui/push-vrulvpnmorwp
Add enum PresentResult: revise error handling for surface presentation
2 parents 8f27022 + 820b1cd commit b1a8e37

5 files changed

Lines changed: 103 additions & 53 deletions

File tree

crates/kas-core/src/runner/common.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,26 @@ pub enum RunError {
4141
RequestError(#[from] winit::error::RequestError),
4242
}
4343

44+
/// Frame presentation outcomes
45+
#[non_exhaustive]
46+
#[derive(Debug)]
47+
pub enum PresentResult {
48+
/// Success
49+
///
50+
/// Includes the time at which rendering finishes (excluding synchronisation delays).
51+
Success(Instant),
52+
/// The frame was dropped, e.g. due to timeout or being occluded.
53+
Dropped,
54+
/// The surface is outdated and should be reconfigured.
55+
///
56+
/// (The frame may or may not have been presented.)
57+
ReconfigureSurface,
58+
/// A fatal error: the window should be closed.
59+
///
60+
/// An error message should be logged by the method returning this result.
61+
Fatal,
62+
}
63+
4464
/// Enumeration of platforms
4565
///
4666
/// Each option is compile-time enabled only if that platform is possible.
@@ -230,5 +250,5 @@ pub trait WindowSurface {
230250
/// Present frame
231251
///
232252
/// Return time at which render finishes
233-
fn present(&mut self, shared: &mut Self::Shared, clear_color: Rgba) -> Instant;
253+
fn present(&mut self, shared: &mut Self::Shared, clear_color: Rgba) -> PresentResult;
234254
}

crates/kas-core/src/runner/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ pub use runner::{ClosedError, PreLaunchState, Proxy};
2727

2828
#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
2929
#[cfg_attr(docsrs, doc(cfg(internal_doc)))]
30-
pub use common::{GraphicsFeatures, GraphicsInstance, RunError, WindowSurface};
30+
pub use common::{GraphicsFeatures, GraphicsInstance, PresentResult, RunError, WindowSurface};
3131

3232
#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
3333
#[cfg_attr(docsrs, doc(cfg(internal_doc)))]

crates/kas-core/src/runner/window.rs

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use crate::event::{ConfigCx, CursorIcon, EventState};
1616
use crate::geom::{Coord, Offset, Rect, Size};
1717
use crate::layout::SolveCache;
1818
use crate::messages::Erased;
19+
use crate::runner::PresentResult;
1920
use crate::theme::{DrawCx, SizeCx, Theme, ThemeDraw, Window as _};
2021
use crate::window::{BoxedWindow, Decorations, PopupDescriptor, WindowId, WindowWidget};
2122
use crate::{
@@ -674,33 +675,47 @@ impl<A: AppData, G: GraphicsInstance, T: Theme<G::Shared>> Window<A, G, T> {
674675
} else {
675676
shared.theme.clear_color()
676677
};
677-
let time3 = window
678+
let result = window
678679
.surface
679680
.present(&mut shared.draw.as_mut().unwrap().draw, clear_color);
680681

681-
let text_dur_micros = take(&mut window.surface.common_mut().dur_text);
682-
let end = Instant::now();
683-
log::trace!(
684-
target: "kas_perf::wgpu::window",
685-
"do_draw: {}μs ({}μs widgets, {}μs text, {}μs render, {}μs present)",
686-
(end - start).as_micros(),
687-
(time2 - start).as_micros(),
688-
text_dur_micros.as_micros(),
689-
(time3 - time2).as_micros(),
690-
(end - time2).as_micros()
691-
);
692-
693-
const SECOND: Duration = Duration::from_secs(1);
694-
window.frame_count.1 += 1;
695-
if window.frame_count.0 + SECOND <= end {
696-
log::debug!(
697-
"Window {:?}: {} frames in last second",
698-
window.window_id,
699-
window.frame_count.1
700-
);
701-
window.frame_count.0 = end;
702-
window.frame_count.1 = 0;
703-
}
682+
match result {
683+
PresentResult::Success(time3) => {
684+
let text_dur_micros = take(&mut window.surface.common_mut().dur_text);
685+
let end = Instant::now();
686+
log::trace!(
687+
target: "kas_perf::wgpu::window",
688+
"do_draw: {}μs ({}μs widgets, {}μs text, {}μs render, {}μs present)",
689+
(end - start).as_micros(),
690+
(time2 - start).as_micros(),
691+
text_dur_micros.as_micros(),
692+
(time3 - time2).as_micros(),
693+
(end - time2).as_micros()
694+
);
695+
696+
const SECOND: Duration = Duration::from_secs(1);
697+
window.frame_count.1 += 1;
698+
if window.frame_count.0 + SECOND <= end {
699+
log::debug!(
700+
"Window {:?}: {} frames in last second",
701+
window.window_id,
702+
window.frame_count.1
703+
);
704+
window.frame_count.0 = end;
705+
window.frame_count.1 = 0;
706+
}
707+
}
708+
PresentResult::Dropped => (),
709+
PresentResult::ReconfigureSurface => {
710+
let size: Size = window.surface_size().cast();
711+
window
712+
.surface
713+
.configure(&mut shared.draw.as_mut().unwrap().draw, size);
714+
}
715+
PresentResult::Fatal => {
716+
self.ev_state.close_own_window();
717+
}
718+
};
704719

705720
Ok(())
706721
}

crates/kas-soft/src/lib.rs

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ use kas::draw::{SharedState, WindowCommon, color};
2525
use kas::geom::Size;
2626
use kas::runner::raw_window_handle::{HasDisplayHandle, HasWindowHandle};
2727
use kas::runner::{
28-
GraphicsFeatures, GraphicsInstance, HasDisplayAndWindowHandle, RunError, WindowSurface,
28+
GraphicsFeatures, GraphicsInstance, HasDisplayAndWindowHandle, PresentResult, RunError,
29+
WindowSurface,
2930
};
3031

3132
/// Graphics context
@@ -67,11 +68,10 @@ impl WindowSurface for Surface {
6768
self.size = size;
6869
self.draw.resize(size);
6970

70-
let width = NonZeroU32::new(size.0.cast()).expect("zero-sized surface");
71-
let height = NonZeroU32::new(size.1.cast()).expect("zero-sized surface");
72-
self.surface
73-
.resize(width, height)
74-
.expect("surface resize failed");
71+
let (w, h) = NonZeroU32::new(size.0.cast())
72+
.zip(NonZeroU32::new(size.1.cast()))
73+
.expect("zero-sized surface");
74+
self.surface.resize(w, h).expect("surface resize failed");
7575
true
7676
}
7777

@@ -86,11 +86,14 @@ impl WindowSurface for Surface {
8686
&mut self.draw.common
8787
}
8888

89-
fn present(&mut self, shared: &mut Shared, clear_color: color::Rgba) -> Instant {
90-
let mut buffer = self
91-
.surface
92-
.buffer_mut()
93-
.expect("failed to access surface buffer");
89+
fn present(&mut self, shared: &mut Shared, clear_color: color::Rgba) -> PresentResult {
90+
let mut buffer = match self.surface.buffer_mut() {
91+
Ok(b) => b,
92+
Err(e) => {
93+
log::error!("present surface: {e}");
94+
return PresentResult::Fatal;
95+
}
96+
};
9497
let width: usize = self.size.0.cast();
9598
let height: usize = self.size.1.cast();
9699
debug_assert_eq!(width * height, buffer.len());
@@ -101,8 +104,13 @@ impl WindowSurface for Surface {
101104
self.draw.render(shared, &mut buffer, (width, height));
102105

103106
let pre_present = Instant::now();
104-
buffer.present().expect("failed to present buffer");
105-
pre_present
107+
match buffer.present() {
108+
Ok(()) => PresentResult::Success(pre_present),
109+
Err(e) => {
110+
log::warn!("failed to present buffer: {e}");
111+
PresentResult::Dropped
112+
}
113+
}
106114
}
107115
}
108116

crates/kas-wgpu/src/surface.rs

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use kas::cast::Cast;
1010
use kas::draw::color::Rgba;
1111
use kas::draw::{DrawIface, DrawSharedImpl, WindowCommon};
1212
use kas::geom::Size;
13-
use kas::runner::{HasDisplayAndWindowHandle, RunError, WindowSurface};
13+
use kas::runner::{HasDisplayAndWindowHandle, PresentResult, RunError, WindowSurface};
1414
use std::time::Instant;
1515
use wgpu::{CurrentSurfaceTexture, PresentMode};
1616

@@ -111,19 +111,21 @@ impl<C: CustomPipe> WindowSurface for Surface<C> {
111111
&mut self.draw.common
112112
}
113113

114-
/// Return time at which render finishes
115-
fn present(&mut self, shared: &mut Self::Shared, clear_color: Rgba) -> Instant {
116-
// TODO: review error handling
117-
let frame = match self.surface.get_current_texture() {
118-
CurrentSurfaceTexture::Success(frame) | CurrentSurfaceTexture::Suboptimal(frame) => {
119-
frame
114+
fn present(&mut self, shared: &mut Self::Shared, clear_color: Rgba) -> PresentResult {
115+
let (frame, outdated) = match self.surface.get_current_texture() {
116+
CurrentSurfaceTexture::Success(frame) => (frame, false),
117+
CurrentSurfaceTexture::Suboptimal(frame) => (frame, true),
118+
CurrentSurfaceTexture::Timeout | CurrentSurfaceTexture::Occluded => {
119+
return PresentResult::Dropped;
120120
}
121-
CurrentSurfaceTexture::Timeout
122-
| CurrentSurfaceTexture::Occluded
123-
| CurrentSurfaceTexture::Outdated
124-
| CurrentSurfaceTexture::Lost
125-
| CurrentSurfaceTexture::Validation => {
126-
return Instant::now();
121+
CurrentSurfaceTexture::Outdated => return PresentResult::ReconfigureSurface,
122+
CurrentSurfaceTexture::Lost => {
123+
log::error!("present surface: surface has been lost");
124+
return PresentResult::Fatal;
125+
}
126+
CurrentSurfaceTexture::Validation => {
127+
log::error!("present surface: validation error");
128+
return PresentResult::Fatal;
127129
}
128130
};
129131

@@ -134,7 +136,12 @@ impl<C: CustomPipe> WindowSurface for Surface<C> {
134136

135137
let pre_present = Instant::now();
136138
frame.present();
137-
pre_present
139+
140+
if !outdated {
141+
PresentResult::Success(pre_present)
142+
} else {
143+
PresentResult::ReconfigureSurface
144+
}
138145
}
139146
}
140147

0 commit comments

Comments
 (0)