ohos: Present on vsync signals (#33117)

Rely on callbacks from the vertical synchronization driver,
to drive presentation.
Future commits will base animation updates and touchless
gestures like fling off these vsync events.

Signed-off-by: Jonathan Schwender <jonathan.schwender@huawei.com>
Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
This commit is contained in:
Jonathan Schwender 2024-08-30 21:25:50 +08:00 committed by GitHub
parent a58d816319
commit 8a0c7487e7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 83 additions and 9 deletions

10
Cargo.lock generated
View file

@ -4780,6 +4780,15 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f2fd4c1b349c53cd06dfa959bb53076453960d793961caafd367e64c3bb5522"
[[package]]
name = "ohos-vsync"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5871e38034a33e8d43c711a40d39e24fd3500f43b61b9269b8586f608a70aec3"
dependencies = [
"ohos-sys",
]
[[package]]
name = "once_cell"
version = "1.19.0"
@ -6207,6 +6216,7 @@ dependencies = [
"net_traits",
"nix",
"ohos-sys",
"ohos-vsync",
"raw-window-handle",
"serde_json",
"servo-media",

View file

@ -81,6 +81,7 @@ hilog = "0.1.0"
napi-derive-ohos = "0.0.9"
napi-ohos = "0.1"
ohos-sys = { version = "0.3.0", features = ["xcomponent"] }
ohos-vsync = "0.1"
[target.'cfg(any(target_os = "android", target_env = "ohos"))'.dependencies]
nix = { workspace = true, features = ["fs"] }

View file

@ -196,7 +196,11 @@ pub extern "C" fn Java_org_servo_servoview_JNIServo_performUpdates<'local>(
_class: JClass<'local>,
) {
debug!("performUpdates");
call(&mut env, |s| s.perform_updates());
call(&mut env, |s| {
s.perform_updates()?;
s.present_if_needed();
Ok(())
});
}
#[no_mangle]

View file

@ -39,7 +39,13 @@ pub trait HostTrait {
/// Page animation state has changed. If animating, it's recommended
/// that the embedder doesn't wait for the wake function to be called
/// to call perform_updates. Usually, it means doing:
/// while true { servo.perform_updates() }. This will end up calling flush
/// ```rust
/// while true {
/// servo.perform_updates();
/// servo.present_if_needed();
/// }
/// ```
/// . This will end up calling flush
/// which will call swap_buffer which will be blocking long enough to limit
/// drawing at 60 FPS.
/// If not animating, call perform_updates only when needed (when the embedder

View file

@ -11,7 +11,7 @@ use std::thread;
use std::thread::sleep;
use std::time::Duration;
use log::{debug, error, info, warn, LevelFilter};
use log::{debug, error, info, trace, warn, LevelFilter};
use napi_derive_ohos::{module_exports, napi};
use napi_ohos::threadsafe_function::{
ErrorStrategy, ThreadsafeFunction, ThreadsafeFunctionCallMode,
@ -93,6 +93,7 @@ enum ServoAction {
pointer_id: i32,
},
Initialize(Box<InitOpts>),
Vsync,
}
// Todo: Need to check if OnceLock is suitable, or if the TS function can be destroyed, e.g.
@ -132,6 +133,13 @@ impl ServoAction {
Initialize(_init_opts) => {
panic!("Received Initialize event, even though servo is already initialized")
},
Vsync => {
servo.perform_updates().expect("Infallible");
servo.present_if_needed();
// Todo: perform_updates() (before or after present) if animating?
Ok(())
},
};
if let Err(e) = res {
error!("Failed to do {self:?} with error {e}");
@ -139,6 +147,31 @@ impl ServoAction {
}
}
/// Vsync callback
///
/// # Safety
///
/// The caller should pass a valid raw NativeVsync object to us via
/// `native_vsync.request_raw_callback_with_self(Some(on_vsync_cb))`
unsafe extern "C" fn on_vsync_cb(
timestamp: ::core::ffi::c_longlong,
data: *mut ::core::ffi::c_void,
) {
trace!("Vsync callback at time {timestamp}");
// SAFETY: We require the function registering us as a callback
let (native_vsync, data) = unsafe {
let native = ohos_vsync::NativeVsync::from_raw(data.cast());
(native, 0)
};
call(ServoAction::Vsync).unwrap();
// Todo: Do we have a callback for when the frame finished rendering?
unsafe {
native_vsync
.request_raw_callback_with_self(Some(on_vsync_cb))
.unwrap();
}
}
static SERVO_CHANNEL: OnceLock<Sender<ServoAction>> = OnceLock::new();
#[no_mangle]
@ -148,6 +181,9 @@ pub extern "C" fn on_surface_created_cb(xcomponent: *mut OH_NativeXComponent, wi
let xc_wrapper = XComponentWrapper(xcomponent);
let window_wrapper = WindowWrapper(window);
// Todo: Perhaps it would be better to move this thread into the vsync signal thread.
// This would allow us to save one thread and the IPC for the vsync signal.
//
// Each thread will send its id via the channel
let _main_surface_thread = thread::spawn(move || {
let (tx, rx): (Sender<ServoAction>, Receiver<ServoAction>) = mpsc::channel();
@ -178,9 +214,19 @@ pub extern "C" fn on_surface_created_cb(xcomponent: *mut OH_NativeXComponent, wi
.expect("Servo initialization failed");
info!("Surface created!");
let native_vsync =
ohos_vsync::NativeVsync::new("ServoVsync").expect("Failed to create NativeVsync");
// get_period() returns an error - perhaps we need to wait until the first callback?
// info!("Native vsync period is {} nanoseconds", native_vsync.get_period().unwrap());
unsafe {
native_vsync
.request_raw_callback_with_self(Some(on_vsync_cb))
.expect("Failed to request vsync callback")
}
info!("Enabled Vsync!");
while let Ok(action) = rx.recv() {
info!("Wakeup message received!");
trace!("Wakeup message received!");
action.do_action(&mut servo);
}
@ -513,7 +559,9 @@ impl HostTrait for HostCallbacks {
fn on_history_changed(&self, can_go_back: bool, can_go_forward: bool) {}
fn on_animating_changed(&self, animating: bool) {}
fn on_animating_changed(&self, animating: bool) {
// todo: should we tell the vsync thread that it should perform updates?
}
fn on_shutdown_complete(&self) {}

View file

@ -82,6 +82,7 @@ pub struct ServoGlue {
rendering_context: RenderingContext,
servo: Servo<ServoWindowCallbacks>,
batch_mode: bool,
need_present: bool,
callbacks: Rc<ServoWindowCallbacks>,
events: Vec<EmbedderEvent>,
context_menu_sender: Option<IpcSender<ContextMenuResult>>,
@ -110,6 +111,7 @@ impl ServoGlue {
rendering_context,
servo,
batch_mode: false,
need_present: false,
callbacks,
events: vec![],
context_menu_sender: None,
@ -432,7 +434,6 @@ impl ServoGlue {
fn handle_servo_events(&mut self) -> Result<(), &'static str> {
let mut need_update = false;
let mut need_present = false;
for (browser_id, event) in self.servo.get_events() {
match event {
EmbedderMsg::ChangePageTitle(title) => {
@ -611,7 +612,7 @@ impl ServoGlue {
self.callbacks.host_callbacks.on_panic(reason, backtrace);
},
EmbedderMsg::ReadyToPresent(_webview_ids) => {
need_present = true;
self.need_present = true;
},
EmbedderMsg::Status(..) |
EmbedderMsg::SelectFiles(..) |
@ -632,10 +633,14 @@ impl ServoGlue {
if need_update {
let _ = self.perform_updates();
}
if need_present {
Ok(())
}
pub fn present_if_needed(&mut self) {
if self.need_present {
self.need_present = false;
self.servo.present();
}
Ok(())
}
}