diff options
-rw-r--r-- | Cargo.lock | 10 | ||||
-rw-r--r-- | ports/servoshell/Cargo.toml | 1 | ||||
-rw-r--r-- | ports/servoshell/egl/android.rs | 6 | ||||
-rw-r--r-- | ports/servoshell/egl/host_trait.rs | 8 | ||||
-rw-r--r-- | ports/servoshell/egl/ohos.rs | 54 | ||||
-rw-r--r-- | ports/servoshell/egl/servo_glue.rs | 13 |
6 files changed, 83 insertions, 9 deletions
diff --git a/Cargo.lock b/Cargo.lock index fac0ef27045..10ab7b05da4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4781,6 +4781,15 @@ 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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -6207,6 +6216,7 @@ dependencies = [ "net_traits", "nix", "ohos-sys", + "ohos-vsync", "raw-window-handle", "serde_json", "servo-media", diff --git a/ports/servoshell/Cargo.toml b/ports/servoshell/Cargo.toml index 1d7826cf333..c898206bda9 100644 --- a/ports/servoshell/Cargo.toml +++ b/ports/servoshell/Cargo.toml @@ -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"] } diff --git a/ports/servoshell/egl/android.rs b/ports/servoshell/egl/android.rs index 865222d910e..2e54b43e518 100644 --- a/ports/servoshell/egl/android.rs +++ b/ports/servoshell/egl/android.rs @@ -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] diff --git a/ports/servoshell/egl/host_trait.rs b/ports/servoshell/egl/host_trait.rs index 361d431d237..99fac91356c 100644 --- a/ports/servoshell/egl/host_trait.rs +++ b/ports/servoshell/egl/host_trait.rs @@ -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 diff --git a/ports/servoshell/egl/ohos.rs b/ports/servoshell/egl/ohos.rs index 425471cd4ce..0864e16b27f 100644 --- a/ports/servoshell/egl/ohos.rs +++ b/ports/servoshell/egl/ohos.rs @@ -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) {} diff --git a/ports/servoshell/egl/servo_glue.rs b/ports/servoshell/egl/servo_glue.rs index 13022145fe5..48e3954bdb2 100644 --- a/ports/servoshell/egl/servo_glue.rs +++ b/ports/servoshell/egl/servo_glue.rs @@ -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(()) } } |