Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion crates/kas-core/src/runner/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,26 @@ pub enum RunError {
RequestError(#[from] winit::error::RequestError),
}

/// Frame presentation outcomes
#[non_exhaustive]
#[derive(Debug)]
pub enum PresentResult {
/// Success
///
/// Includes the time at which rendering finishes (excluding synchronisation delays).
Success(Instant),
/// The frame was dropped, e.g. due to timeout or being occluded.
Dropped,
/// The surface is outdated and should be reconfigured.
///
/// (The frame may or may not have been presented.)
ReconfigureSurface,
/// A fatal error: the window should be closed.
///
/// An error message should be logged by the method returning this result.
Fatal,
}

/// Enumeration of platforms
///
/// Each option is compile-time enabled only if that platform is possible.
Expand Down Expand Up @@ -230,5 +250,5 @@ pub trait WindowSurface {
/// Present frame
///
/// Return time at which render finishes
fn present(&mut self, shared: &mut Self::Shared, clear_color: Rgba) -> Instant;
fn present(&mut self, shared: &mut Self::Shared, clear_color: Rgba) -> PresentResult;
}
2 changes: 1 addition & 1 deletion crates/kas-core/src/runner/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ pub use runner::{ClosedError, PreLaunchState, Proxy};

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

#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
#[cfg_attr(docsrs, doc(cfg(internal_doc)))]
Expand Down
63 changes: 39 additions & 24 deletions crates/kas-core/src/runner/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use crate::event::{ConfigCx, CursorIcon, EventState};
use crate::geom::{Coord, Offset, Rect, Size};
use crate::layout::SolveCache;
use crate::messages::Erased;
use crate::runner::PresentResult;
use crate::theme::{DrawCx, SizeCx, Theme, ThemeDraw, Window as _};
use crate::window::{BoxedWindow, Decorations, PopupDescriptor, WindowId, WindowWidget};
use crate::{
Expand Down Expand Up @@ -674,33 +675,47 @@ impl<A: AppData, G: GraphicsInstance, T: Theme<G::Shared>> Window<A, G, T> {
} else {
shared.theme.clear_color()
};
let time3 = window
let result = window
.surface
.present(&mut shared.draw.as_mut().unwrap().draw, clear_color);

let text_dur_micros = take(&mut window.surface.common_mut().dur_text);
let end = Instant::now();
log::trace!(
target: "kas_perf::wgpu::window",
"do_draw: {}μs ({}μs widgets, {}μs text, {}μs render, {}μs present)",
(end - start).as_micros(),
(time2 - start).as_micros(),
text_dur_micros.as_micros(),
(time3 - time2).as_micros(),
(end - time2).as_micros()
);

const SECOND: Duration = Duration::from_secs(1);
window.frame_count.1 += 1;
if window.frame_count.0 + SECOND <= end {
log::debug!(
"Window {:?}: {} frames in last second",
window.window_id,
window.frame_count.1
);
window.frame_count.0 = end;
window.frame_count.1 = 0;
}
match result {
PresentResult::Success(time3) => {
let text_dur_micros = take(&mut window.surface.common_mut().dur_text);
let end = Instant::now();
log::trace!(
target: "kas_perf::wgpu::window",
"do_draw: {}μs ({}μs widgets, {}μs text, {}μs render, {}μs present)",
(end - start).as_micros(),
(time2 - start).as_micros(),
text_dur_micros.as_micros(),
(time3 - time2).as_micros(),
(end - time2).as_micros()
);

const SECOND: Duration = Duration::from_secs(1);
window.frame_count.1 += 1;
if window.frame_count.0 + SECOND <= end {
log::debug!(
"Window {:?}: {} frames in last second",
window.window_id,
window.frame_count.1
);
window.frame_count.0 = end;
window.frame_count.1 = 0;
}
}
PresentResult::Dropped => (),
PresentResult::ReconfigureSurface => {
let size: Size = window.surface_size().cast();
window
.surface
.configure(&mut shared.draw.as_mut().unwrap().draw, size);
}
PresentResult::Fatal => {
self.ev_state.close_own_window();
}
};

Ok(())
}
Expand Down
34 changes: 21 additions & 13 deletions crates/kas-soft/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ use kas::draw::{SharedState, WindowCommon, color};
use kas::geom::Size;
use kas::runner::raw_window_handle::{HasDisplayHandle, HasWindowHandle};
use kas::runner::{
GraphicsFeatures, GraphicsInstance, HasDisplayAndWindowHandle, RunError, WindowSurface,
GraphicsFeatures, GraphicsInstance, HasDisplayAndWindowHandle, PresentResult, RunError,
WindowSurface,
};

/// Graphics context
Expand Down Expand Up @@ -67,11 +68,10 @@ impl WindowSurface for Surface {
self.size = size;
self.draw.resize(size);

let width = NonZeroU32::new(size.0.cast()).expect("zero-sized surface");
let height = NonZeroU32::new(size.1.cast()).expect("zero-sized surface");
self.surface
.resize(width, height)
.expect("surface resize failed");
let (w, h) = NonZeroU32::new(size.0.cast())
.zip(NonZeroU32::new(size.1.cast()))
.expect("zero-sized surface");
self.surface.resize(w, h).expect("surface resize failed");
true
}

Expand All @@ -86,11 +86,14 @@ impl WindowSurface for Surface {
&mut self.draw.common
}

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

let pre_present = Instant::now();
buffer.present().expect("failed to present buffer");
pre_present
match buffer.present() {
Ok(()) => PresentResult::Success(pre_present),
Err(e) => {
log::warn!("failed to present buffer: {e}");
PresentResult::Dropped
}
}
}
}

Expand Down
35 changes: 21 additions & 14 deletions crates/kas-wgpu/src/surface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use kas::cast::Cast;
use kas::draw::color::Rgba;
use kas::draw::{DrawIface, DrawSharedImpl, WindowCommon};
use kas::geom::Size;
use kas::runner::{HasDisplayAndWindowHandle, RunError, WindowSurface};
use kas::runner::{HasDisplayAndWindowHandle, PresentResult, RunError, WindowSurface};
use std::time::Instant;
use wgpu::{CurrentSurfaceTexture, PresentMode};

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

/// Return time at which render finishes
fn present(&mut self, shared: &mut Self::Shared, clear_color: Rgba) -> Instant {
// TODO: review error handling
let frame = match self.surface.get_current_texture() {
CurrentSurfaceTexture::Success(frame) | CurrentSurfaceTexture::Suboptimal(frame) => {
frame
fn present(&mut self, shared: &mut Self::Shared, clear_color: Rgba) -> PresentResult {
let (frame, outdated) = match self.surface.get_current_texture() {
CurrentSurfaceTexture::Success(frame) => (frame, false),
CurrentSurfaceTexture::Suboptimal(frame) => (frame, true),
CurrentSurfaceTexture::Timeout | CurrentSurfaceTexture::Occluded => {
return PresentResult::Dropped;
}
CurrentSurfaceTexture::Timeout
| CurrentSurfaceTexture::Occluded
| CurrentSurfaceTexture::Outdated
| CurrentSurfaceTexture::Lost
| CurrentSurfaceTexture::Validation => {
return Instant::now();
CurrentSurfaceTexture::Outdated => return PresentResult::ReconfigureSurface,
CurrentSurfaceTexture::Lost => {
log::error!("present surface: surface has been lost");
return PresentResult::Fatal;
}
CurrentSurfaceTexture::Validation => {
log::error!("present surface: validation error");
return PresentResult::Fatal;
}
};

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

let pre_present = Instant::now();
frame.present();
pre_present

if !outdated {
PresentResult::Success(pre_present)
} else {
PresentResult::ReconfigureSurface
}
}
}

Expand Down
Loading