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
9 changes: 9 additions & 0 deletions src/conf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,14 @@ pub struct Platform {
/// [`schedule_update`]: super::window::schedule_update
pub blocking_event_loop: bool,

/// When `blocking_event_loop` is enabled, we can set this value to periodically
/// wakeup the `update()` and `draw()` functions. This reduces CPU usage vs
/// continuously drawing without having to continuously schedule updates that can
/// choke the receiver queue.
///
/// Currently supported only on Android.
Copy link
Owner

@not-fl3 not-fl3 Dec 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe reuse swap_interval for this? I am concerned that users will try to use fps to set target fps on other platforms.

If not swap_interval - I would name it something among the android_perioidical_wakeup_timer lines - to make it very explicit that its a very special setting, not a general way to set target fps to 60 on web.

Copy link
Contributor Author

@narodnik narodnik Dec 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

swap_interval is for vsync and is not correct here. In fact I think this code should be added to the android EGL init in src/native/android.rs but that is a separate commit:

        if (libegl.eglSwapInterval)(egl_display, conf.platform.swap_interval.unwrap_or(1)) == 0 {
            eprintln!("eglSwapInterval failed");
        }

I didn't do it yet since the only meaningful values on Android are 0 or 1 and we generally want 1 anyway.

Secondly, my plan is actually to make FPS a setting for all platforms where possible to conserve battery/system resources. So I think its best to conserve it in the general platform settings unless you prefer to later deprecate the android specific setting and introduce a global one later.

pub sleep_interval_ms: Option<u32>,

/// If `true`, the framebuffer includes an alpha channel.
/// Currently supported only on Android.
///
Expand Down Expand Up @@ -183,6 +191,7 @@ impl Default for Platform {
apple_gfx_api: AppleGfxApi::default(),
webgl_version: WebGLVersion::default(),
blocking_event_loop: false,
sleep_interval_ms: None,
swap_interval: None,
framebuffer_alpha: false,
wayland_decorations: WaylandDecorations::default(),
Expand Down
32 changes: 27 additions & 5 deletions src/native/android.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::{
},
};

use std::{cell::RefCell, sync::mpsc, thread};
use std::{cell::RefCell, sync::mpsc, thread, time::Duration};

pub use crate::native::gl::{self, *};

Expand Down Expand Up @@ -480,14 +480,23 @@ where
},
};

let rx_timeout = conf
.platform
.sleep_interval_ms
.map(|sleep| Duration::from_millis(sleep as u64));

while !s.quit {
let block_on_wait = conf.platform.blocking_event_loop && !s.update_requested;

if block_on_wait {
let res = rx.recv();

if let Ok(msg) = res {
s.process_message(msg);
// We don't need to loop here because the loop above consumes all
// available messages. Instead we are going to block until receiving here.

match rx_recv(&rx, rx_timeout) {
Ok(msg) => s.process_message(msg),
// Timeout so time to do periodic update()
Err(mpsc::RecvTimeoutError::Timeout) => s.update_requested = true,
Err(mpsc::RecvTimeoutError::Disconnected) => panic!(),
}
} else {
// process all the messages from the main thread
Expand Down Expand Up @@ -515,6 +524,19 @@ where
});
}

/// Adds a call to Receiver as if there was a `.recv_timeout_opt(timeout)`
/// where the `timeout` arg is optional.
fn rx_recv<T>(
rx: &mpsc::Receiver<T>,
timeout: Option<Duration>,
) -> Result<T, mpsc::RecvTimeoutError> {
match timeout {
Some(timeout) => rx.recv_timeout(timeout),
// No timeout specified so just do a normal blocking recv()
None => rx.recv().map_err(|_| mpsc::RecvTimeoutError::Disconnected),
}
}

#[no_mangle]
extern "C" fn jni_on_load(vm: *mut std::ffi::c_void) {
unsafe {
Expand Down
Loading