use core::ffi;
use core::marker::PhantomData;
use ndk::android::choreographer::*;
pub trait FrameDelegate {
fn on_frame(&self, _frame_time_nanos: i64) {}
fn on_vsync(&self, _frame_data: &FrameData) {}
fn on_rate(&self, _vsync_period_nanos: i64) {}
}
#[repr(transparent)]
#[derive(Debug)]
pub struct Grapher<T: FrameDelegate> {
instance: *const AChoreographer,
_t: PhantomData<T>,
}
impl<T: FrameDelegate> Clone for Grapher<T> {
fn clone(&self) -> Self {
Self {
instance: self.instance,
_t: PhantomData,
}
}
}
impl<T: FrameDelegate> Copy for Grapher<T> {}
impl<T: FrameDelegate> Grapher<T> {
pub fn singleton() -> Self {
let instance = unsafe { AChoreographer_getInstance() };
Self { instance, _t: PhantomData }
}
#[cfg(feature = "api29")]
pub fn frame(&self, data: &std::rc::Rc<T>) {
let weak = std::rc::Rc::downgrade(&data);
let data = weak.into_raw();
unsafe { AChoreographer_postFrameCallback64(self.instance as _, Some(on_frame::<T>), data as _) }
}
#[cfg(feature = "api29")]
pub fn delay(&self, data: &std::rc::Rc<T>, delay_millis: u32) {
let weak = std::rc::Rc::downgrade(&data);
let data = weak.into_raw();
unsafe { AChoreographer_postFrameCallbackDelayed64(self.instance as _, Some(on_frame::<T>), data as _, delay_millis) }
}
#[cfg(feature = "api33")]
pub fn vsync(&self, data: &std::rc::Rc<T>) {
let weak = std::rc::Rc::downgrade(&data);
let data = weak.into_raw();
unsafe { AChoreographer_postVsyncCallback(self.instance as _, Some(on_vsync::<T>), data as _) }
}
#[cfg(feature = "api30")]
pub fn rate(&self, data: &std::rc::Rc<T>) -> RateGuard<T> {
let weak = std::rc::Rc::downgrade(&data);
let data = weak.into_raw();
unsafe { AChoreographer_registerRefreshRateCallback(self.instance as _, Some(on_rate::<T>), data as _) };
RateGuard { data }
}
}
extern "C" fn on_vsync<T: FrameDelegate>(frame_data: *const AChoreographerFrameCallbackData, data: *mut ffi::c_void) {
let weak = unsafe { std::rc::Weak::from_raw(data as *const T) };
if let Some(data) = weak.upgrade() {
data.on_vsync(FrameData::from_ndk(frame_data));
}
}
extern "C" fn on_frame<T: FrameDelegate>(frame_time_nanos: i64, data: *mut ffi::c_void) {
let weak = unsafe { std::rc::Weak::from_raw(data as *const T) };
if let Some(data) = weak.upgrade() {
data.on_frame(frame_time_nanos);
}
}
extern "C" fn on_rate<T: FrameDelegate>(vsync_period_nanos: i64, data: *mut ffi::c_void) {
let weak = unsafe { std::rc::Weak::from_raw(data as *const T) };
if let Some(data) = weak.upgrade() {
data.on_rate(vsync_period_nanos);
}
let _ = weak.into_raw();
}
pub struct RateGuard<T: FrameDelegate> {
data: *const T,
}
impl<T: FrameDelegate> Drop for RateGuard<T> {
fn drop(&mut self) {
let instance = unsafe { AChoreographer_getInstance() };
unsafe { AChoreographer_unregisterRefreshRateCallback(instance, Some(on_rate::<T>), self.data as _) }
let _ = unsafe { std::rc::Weak::from_raw(self.data) };
}
}
pub struct FrameData;
impl FrameData {
pub fn from_ndk<'a>(frame_data: *const AChoreographerFrameCallbackData) -> &'a Self {
unsafe { &*(frame_data as *const Self) }
}
fn as_ndk(&self) -> *const AChoreographerFrameCallbackData {
self as *const Self as *const ffi::c_void as _
}
pub fn frame_time_nanos(&self) -> i64 {
unsafe { AChoreographerFrameCallbackData_getFrameTimeNanos(self.as_ndk()) }
}
pub fn frame_timelines_length(&self) -> usize {
unsafe { AChoreographerFrameCallbackData_getFrameTimelinesLength(self.as_ndk()) }
}
pub fn preferred_frame_timeline_index(&self) -> usize {
unsafe { AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex(self.as_ndk()) }
}
pub fn frame_timeline_vsync_id(&self, index: usize) -> AVsyncId {
unsafe { AChoreographerFrameCallbackData_getFrameTimelineVsyncId(self.as_ndk(), index) }
}
pub fn frame_timeline_expected_presentation_time_nanos(&self, index: usize) -> i64 {
unsafe { AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentationTimeNanos(self.as_ndk(), index) }
}
pub fn frame_timeline_deadline_nanos(&self, index: usize) -> i64 {
unsafe { AChoreographerFrameCallbackData_getFrameTimelineDeadlineNanos(self.as_ndk(), index) }
}
}