///|
pub struct WindowFrameHooks {
prepare_frame : () -> Unit
finish_presented : (Bool) -> Unit
request_redraw : () -> Unit
sync_ime : () -> Unit
}
///|
pub fn WindowFrameHooks::new(
prepare_frame? : () -> Unit = () => (),
finish_presented? : (Bool) -> Unit = _ => (),
request_redraw? : () -> Unit = () => (),
sync_ime? : () -> Unit = () => (),
) -> WindowFrameHooks {
{ prepare_frame, finish_presented, request_redraw, sync_ime, }
}
///|
/// Per-window frame and renderer-session owner.
pub struct WindowFrameState {
renderer : @render.RendererSession
hooks : WindowFrameHooks
surface_generation : Int
mut next_sequence : Int
mut current_token : @render.FrameToken?
mut presented_frames : Int
mut disposed : Bool
}
///|
pub fn WindowFrameState::new(
renderer~ : @render.RendererSession,
surface_generation? : Int = 1,
hooks? : WindowFrameHooks = WindowFrameHooks::new(),
) -> WindowFrameState {
{
renderer,
hooks,
surface_generation: surface_generation.max(1),
next_sequence: 1,
current_token: None,
presented_frames: 0,
disposed: false,
}
}
///|
pub fn WindowFrameState::renderer(
self : WindowFrameState,
) -> @render.RendererSession {
self.renderer
}
///|
pub fn WindowFrameState::prepare_submission(
self : WindowFrameState,
frame : @core.DrawFrame,
) -> @render.RenderFrameSubmission? {
if self.disposed || self.current_token is Some(_) {
if !self.disposed {
(self.hooks.request_redraw)()
}
None
} else {
(self.hooks.prepare_frame)()
let token = @render.FrameToken::new(
surface_generation=self.surface_generation,
sequence=self.next_sequence,
)
self.next_sequence = self.next_sequence + 1
self.current_token = Some(token)
Some(@render.RenderFrameSubmission::new(token~, frame~))
}
}
///|
pub fn WindowFrameState::finish_submission(
self : WindowFrameState,
result : @render.RenderFrameResult,
completions : Array[@render.RenderPresentCompletion],
) -> Bool {
if self.disposed {
return false
}
guard self.current_token is Some(token) else { return false }
let accepted = completions.filter(completion => completion.token() == token)
let terminal = @render.render_present_completions_have_terminal(accepted)
let presented = @render.render_present_completions_have_presented(accepted) ||
(accepted.is_empty() && result.presented)
if !result.queued || terminal {
self.current_token = None
}
if presented {
self.presented_frames = self.presented_frames + 1
}
(self.hooks.finish_presented)(presented)
if self.pending() || (result.queued && terminal && !presented) {
(self.hooks.request_redraw)()
}
(self.hooks.sync_ime)()
presented
}
///|
/// Accept asynchronous completions only for the currently pending submission.
/// Stale generations, out-of-order sequences, and duplicate completions have
/// no effect on frame state.
pub fn WindowFrameState::accept_present_completions(
self : WindowFrameState,
completions : Array[@render.RenderPresentCompletion],
) -> Bool {
if self.disposed {
return false
}
guard self.current_token is Some(token) else { return false }
let accepted = completions.filter(completion => completion.token() == token)
let terminal = @render.render_present_completions_have_terminal(accepted)
let presented = @render.render_present_completions_have_presented(accepted)
if terminal {
self.current_token = None
if presented {
self.presented_frames = self.presented_frames + 1
}
(self.hooks.finish_presented)(presented)
}
if self.pending() {
(self.hooks.request_redraw)()
} else if terminal && !presented {
(self.hooks.request_redraw)()
}
(self.hooks.sync_ime)()
presented
}
///|
pub fn WindowFrameState::request_redraw(self : WindowFrameState) -> Unit {
if !self.disposed {
(self.hooks.request_redraw)()
}
}
///|
pub fn WindowFrameState::sync_ime(self : WindowFrameState) -> Unit {
if !self.disposed {
(self.hooks.sync_ime)()
}
}
///|
pub fn WindowFrameState::pending(self : WindowFrameState) -> Bool {
!self.disposed && self.current_token is Some(_)
}
///|
pub fn WindowFrameState::surface_generation(self : WindowFrameState) -> Int {
self.surface_generation
}
///|
pub fn WindowFrameState::presented_count(self : WindowFrameState) -> Int {
self.presented_frames
}
///|
pub fn WindowFrameState::dispose(self : WindowFrameState) -> Unit {
if !self.disposed {
self.disposed = true
self.current_token = None
self.renderer.dispose()
}
}