Skip to content

Commit

Permalink
Refactored redraw/update handling
Browse files Browse the repository at this point in the history
Tracking redraw vs update needed to be more granular so that WakeUp
could be used to trigger Window::update() without causing a refresh.
  • Loading branch information
ecton committed Jun 11, 2021
1 parent aa126e5 commit 421ad54
Show file tree
Hide file tree
Showing 10 changed files with 98 additions and 30 deletions.
6 changes: 3 additions & 3 deletions app/src/lib.rs
Expand Up @@ -31,7 +31,7 @@ pub use self::{
application::{Application, SingleWindowApplication},
error::Error,
runtime::Runtime,
window::{event, RedrawStatus, Window, WindowBuilder, WindowCreator},
window::{event, RedrawRequester, RedrawStatus, Window, WindowBuilder, WindowCreator},
};

/// A collection of commonly used exports provided by this crate.
Expand All @@ -41,8 +41,8 @@ pub mod prelude {
DeviceId, ElementState, Event, EventStatus, InputEvent, MouseButton, MouseScrollDelta,
ScanCode, TouchPhase, VirtualKeyCode,
},
Application, Error, RedrawStatus, Runtime, SingleWindowApplication, Window, WindowBuilder,
WindowCreator,
Application, Error, RedrawRequester, RedrawStatus, Runtime, SingleWindowApplication,
Window, WindowBuilder, WindowCreator,
};
}

Expand Down
6 changes: 3 additions & 3 deletions app/src/window.rs
Expand Up @@ -18,10 +18,10 @@ use crate::Error;

/// Types for event handling.
pub mod event;
pub mod open;
mod open;
mod runtime_window;

pub use open::{OpenWindow, RedrawStatus};
pub use open::{OpenWindow, RedrawRequester, RedrawStatus};
pub use runtime_window::{opened_first_window, RuntimeWindow, RuntimeWindowConfig};
pub use winit::window::Icon;

Expand All @@ -38,7 +38,7 @@ pub enum CloseResponse {
/// Trait to implement a Window
pub trait Window: Send + Sync + 'static {
/// Called when the window is being initilaized.
fn initialize(&mut self, _scene: &Target) -> crate::Result<()>
fn initialize(&mut self, _scene: &Target, _redrawer: RedrawRequester) -> crate::Result<()>
where
Self: Sized,
{
Expand Down
68 changes: 60 additions & 8 deletions app/src/window/open.rs
Expand Up @@ -19,11 +19,32 @@ pub struct OpenWindow<T: Window> {
scene: Arc<Scene>,
}

/// Allows requesting window refreshes outside of the event loop.
#[derive(Clone, Debug)]
pub struct RedrawRequester {
event_sender: Sender<WindowEvent>,
}

impl RedrawRequester {
/// Requests the window refresh itself. This will trigger [`Window::update`]
/// before rendering.
pub fn request_redraw(&self) {
let _ = self.event_sender.send(WindowEvent::RedrawRequested);
}

/// Wakes the event loop, without necessarily redrawing.
pub fn awaken(&self) {
let _ = self.event_sender.send(WindowEvent::WakeUp);
}
}

/// Tracks when a window should be redrawn. Allows for rendering a frame
/// immediately as well as scheduling a refresh in the future.
#[derive(Debug)]
pub struct RedrawStatus {
next_redraw_target: RedrawTarget,
needs_render: bool,
needs_update: bool,
event_sender: Sender<WindowEvent>,
}

Expand All @@ -37,6 +58,14 @@ impl RedrawStatus {
}
}

/// Triggers an update as soon as possible. Does not affect redrawing.
pub fn set_needs_update(&mut self) {
if !self.needs_render {
self.needs_render = true;
let _ = self.event_sender.send(WindowEvent::WakeUp);
}
}

/// Estimates the next redraw instant by adding `duration` to
/// `Instant::now()`. If this is later than the current estimate, it
/// will be ignored.
Expand All @@ -57,6 +86,14 @@ impl RedrawStatus {
},
}
}

/// Returns a redraw requester that can be used outside of the event loop.
#[must_use]
pub fn redraw_requester(&self) -> RedrawRequester {
RedrawRequester {
event_sender: self.event_sender.clone(),
}
}
}

impl<T: Window> OpenWindow<T> {
Expand All @@ -66,6 +103,7 @@ impl<T: Window> OpenWindow<T> {
scene: Arc::new(scene),
redraw_status: RedrawStatus {
needs_render: true,
needs_update: true,
next_redraw_target: RedrawTarget::None,
event_sender,
},
Expand All @@ -77,6 +115,10 @@ impl<T: Window> OpenWindow<T> {
self.redraw_status.next_redraw_target = RedrawTarget::None;
}

pub(crate) fn set_has_updated(&mut self) {
self.redraw_status.needs_update = false;
}

pub(crate) fn initialize_redraw_target(&mut self, target_fps: Option<u16>) {
if let RedrawTarget::None = self.redraw_status.next_redraw_target {
match target_fps {
Expand All @@ -98,7 +140,11 @@ impl<T: Window> OpenWindow<T> {
self.redraw_status.next_redraw_target
}

pub fn needs_render(&self) -> bool {
pub(crate) fn can_wait_for_events(&self) -> bool {
!self.should_redraw_now() && !self.redraw_status.needs_update
}

pub(crate) fn should_redraw_now(&self) -> bool {
self.redraw_status.needs_render
|| match self.redraw_status.next_redraw_target {
RedrawTarget::Never | RedrawTarget::None => false,
Expand All @@ -120,29 +166,35 @@ impl<T: Window> OpenWindow<T> {
}

pub(crate) fn initialize(&mut self) -> crate::Result<()> {
self.window.initialize(&Target {
scene: self.scene.clone(),
clip: None,
offset: None,
})?;
self.window.initialize(
&Target {
scene: self.scene.clone(),
clip: None,
offset: None,
},
self.redraw_status.redraw_requester(),
)?;

Ok(())
}

pub(crate) fn render(&mut self) -> crate::Result<()> {
// Clear the redraw target first, so that if something inside of render
// (or another thread) requests a redraw it will still be honored.
self.clear_redraw_target();

self.window.render(&Target {
scene: self.scene.clone(),
clip: None,
offset: None,
})?;

self.clear_redraw_target();

Ok(())
}

pub(crate) fn update(&mut self, target_fps: Option<u16>) -> crate::Result<()> {
self.initialize_redraw_target(target_fps);
self.set_has_updated();

self.window.update(
&Target {
Expand Down
8 changes: 5 additions & 3 deletions app/src/window/runtime_window.rs
Expand Up @@ -220,7 +220,7 @@ impl RuntimeWindow {
#[cfg(not(target_arch = "wasm32"))]
{
let next_redraw_target = window.next_redraw_target();
if window.needs_render() {
if !window.can_wait_for_events() {
Self::next_window_event_non_blocking(event_receiver)
} else if let Some(redraw_at) = next_redraw_target.next_update_instant() {
let timeout_target = redraw_at.timeout_target();
Expand Down Expand Up @@ -262,7 +262,9 @@ impl RuntimeWindow {
Err(_) => return Ok(()),
} {
match event {
WindowEvent::WakeUp => {}
WindowEvent::WakeUp => {
window.redraw_status.set_needs_update();
}
WindowEvent::SystemThemeChanged(system_theme) => {
window.scene_mut().set_system_theme(system_theme);
window.redraw_status.set_needs_redraw();
Expand Down Expand Up @@ -321,7 +323,7 @@ impl RuntimeWindow {

window.update(target_fps)?;

if window.needs_render() {
if window.should_redraw_now() {
window.render()?;
window.scene_mut().end_frame();
}
Expand Down
11 changes: 7 additions & 4 deletions core/src/frame_renderer.rs
Expand Up @@ -131,7 +131,7 @@ where
) -> crate::Result<DynamicImage> {
let mut frame_renderer = Self::new(renderer, Arc::default(), scene_event_receiver);
let mut frame = Frame::default();
frame.update(&frame_renderer.scene_event_receiver);
let _ = frame.update(&frame_renderer.scene_event_receiver);
frame_renderer.render_frame(&mut frame)?;
if let Destination::Texture { output, .. } = frame_renderer.destination {
let data = output.slice(..);
Expand Down Expand Up @@ -197,9 +197,12 @@ where
}
return;
}
frame.update(&self.scene_event_receiver);
self.render_frame(&mut frame)
.expect("Error rendering window");
if frame.update(&self.scene_event_receiver) {
self.render_frame(&mut frame)
.expect("Error rendering window");
} else {
self.keep_running.store(false, Ordering::SeqCst)
}
}
}

Expand Down
20 changes: 15 additions & 5 deletions core/src/frame_renderer/frame.rs
Expand Up @@ -28,9 +28,13 @@ struct FrameReceiver {
}

impl FrameReceiver {
pub fn get_latest_frame(&mut self, receiver: &flume::Receiver<SceneEvent>) -> Vec<Element> {
pub fn get_latest_frame(
&mut self,
receiver: &flume::Receiver<SceneEvent>,
) -> Option<Vec<Element>> {
// Receive a frame, blocking until we get an EndFrame
while let Ok(evt) = receiver.recv() {
loop {
let evt = receiver.recv().ok()?;
if self.process_scene_event(evt) {
// New frame
break;
Expand All @@ -45,7 +49,7 @@ impl FrameReceiver {
latest_frame = std::mem::replace(&mut self.elements, Vec::new());
}
}
latest_frame
Some(latest_frame)
}

fn process_scene_event(&mut self, event: SceneEvent) -> bool {
Expand Down Expand Up @@ -120,8 +124,12 @@ impl FrameBatch {

impl Frame {
#[instrument(name = "Frame::update", level = "trace", skip(self, event_receiver))]
pub fn update(&mut self, event_receiver: &flume::Receiver<SceneEvent>) {
let elements = self.receiver.get_latest_frame(event_receiver);
#[must_use]
pub fn update(&mut self, event_receiver: &flume::Receiver<SceneEvent>) -> bool {
let elements = match self.receiver.get_latest_frame(event_receiver) {
Some(elements) => elements,
None => return false,
};
self.size = self.receiver.size;
self.commands.clear();

Expand Down Expand Up @@ -208,6 +216,8 @@ impl Frame {
for id in dead_texture_ids {
self.textures.remove(&id);
}

true
}

fn commit_batch(&mut self, batch: Option<FrameBatch>) -> Option<FrameBatch> {
Expand Down
2 changes: 1 addition & 1 deletion kludgine/examples/isometric.rs
Expand Up @@ -16,7 +16,7 @@ impl WindowCreator for Isometric {
}
static MAP_SIZE: u32 = 100;
impl Window for Isometric {
fn initialize(&mut self, _scene: &Target) -> kludgine::Result<()> {
fn initialize(&mut self, _scene: &Target, _requester: RedrawRequester) -> kludgine::Result<()> {
self.load_assets()?;
// self.zoom = 1.0;
// self.x = MAP_SIZE as f32 * 32.0 / 2.0;
Expand Down
3 changes: 2 additions & 1 deletion kludgine/examples/orthotiles.rs
@@ -1,4 +1,5 @@
use kludgine::prelude::*;
use kludgine_app::RedrawRequester;

fn main() {
SingleWindowApplication::run(OrthoTiles::default());
Expand All @@ -25,7 +26,7 @@ impl Window for OrthoTiles {
Some(60)
}

fn initialize(&mut self, _scene: &Target) -> kludgine::Result<()> {
fn initialize(&mut self, _scene: &Target, _requester: RedrawRequester) -> kludgine::Result<()> {
self.load_assets()?;
self.zoom = 1.0;
// self.position.x = MAP_SIZE as f32 * 32.0 / 2.0;
Expand Down
2 changes: 1 addition & 1 deletion kludgine/examples/simple.rs
Expand Up @@ -21,7 +21,7 @@ impl Window for Simple {
Some(60)
}

fn initialize(&mut self, _scene: &Target) -> kludgine::Result<()> {
fn initialize(&mut self, _scene: &Target, _requester: RedrawRequester) -> kludgine::Result<()> {
let texture = Texture::load("kludgine/examples/assets/k.png")?;
self.source_sprite = Some(SpriteSource::entire_texture(texture));
Ok(())
Expand Down
2 changes: 1 addition & 1 deletion kludgine/examples/sprite_sheets.rs
Expand Up @@ -33,7 +33,7 @@ enum StickGuy {
}

impl Window for SpriteSheetExample {
fn initialize(&mut self, _scene: &Target) -> kludgine::Result<()> {
fn initialize(&mut self, _scene: &Target, _requester: RedrawRequester) -> kludgine::Result<()> {
let texture = include_texture!("assets/stickguy.png")?;
let sheet = SpriteSheet::new(texture, Size::new(32, 32), vec![
StickGuy::Idle1,
Expand Down

0 comments on commit 421ad54

Please sign in to comment.