Checkpoint

This commit is contained in:
Nathan Sobo 2023-10-04 22:34:44 -06:00
parent 177e385bb9
commit 0d0c760d94
10 changed files with 224 additions and 52 deletions

View file

@ -8,8 +8,8 @@ pub use model_context::*;
use refineable::Refineable;
use crate::{
current_platform, image_cache::ImageCache, AssetSource, Context, Executor, LayoutId,
MainThread, MainThreadOnly, Platform, RootView, SvgRenderer, Task, TextStyle,
current_platform, image_cache::ImageCache, AssetSource, Context, DisplayLinker, Executor,
LayoutId, MainThread, MainThreadOnly, Platform, RootView, SvgRenderer, Task, TextStyle,
TextStyleRefinement, TextSystem, Window, WindowContext, WindowHandle, WindowId,
};
use anyhow::{anyhow, Result};
@ -51,15 +51,15 @@ impl App {
http_client: Arc<dyn HttpClient>,
) -> Self {
let executor = platform.executor();
let text_system = Arc::new(TextSystem::new(platform.text_system()));
let entities = EntityMap::new();
let unit_entity = entities.insert(entities.reserve(), ());
Self(Arc::new_cyclic(|this| {
Mutex::new(AppContext {
this: this.clone(),
display_linker: Arc::new(DisplayLinker::new(platform.display_linker())),
text_system: Arc::new(TextSystem::new(platform.text_system())),
platform: MainThreadOnly::new(platform, executor.clone()),
executor,
text_system,
svg_renderer: SvgRenderer::new(asset_source),
image_cache: ImageCache::new(http_client),
pending_updates: 0,
@ -97,6 +97,7 @@ pub struct AppContext {
text_system: Arc<TextSystem>,
pending_updates: usize,
pub(crate) executor: Executor,
pub(crate) display_linker: Arc<DisplayLinker>,
pub(crate) svg_renderer: SvgRenderer,
pub(crate) image_cache: ImageCache,
pub(crate) text_style_stack: Vec<TextStyleRefinement>,
@ -359,9 +360,15 @@ impl MainThread<AppContext> {
let id = cx.windows.insert(None);
let handle = WindowHandle::new(id);
let mut window = Window::new(handle.into(), options, cx);
let display_id = window.display_id;
let root_view = build_root_view(&mut WindowContext::mutable(cx, &mut window));
window.root_view.replace(root_view.into_any());
cx.windows.get_mut(id).unwrap().replace(window);
cx.display_linker.on_next_frame(display_id, |_, _| {
dbg!("next frame");
});
handle
})
}

View file

@ -0,0 +1,55 @@
use crate::{DisplayId, PlatformDisplayLinker, VideoTimestamp};
use collections::HashMap;
use parking_lot::Mutex;
use std::sync::Arc;
type FrameCallback = Box<dyn FnOnce(&VideoTimestamp, &VideoTimestamp) + Send>;
pub struct DisplayLinker {
platform_linker: Arc<dyn PlatformDisplayLinker>,
next_frame_callbacks: Arc<Mutex<HashMap<DisplayId, Vec<FrameCallback>>>>,
}
impl DisplayLinker {
pub(crate) fn new(platform_linker: Arc<dyn PlatformDisplayLinker>) -> Self {
Self {
platform_linker,
next_frame_callbacks: Default::default(),
}
}
pub(crate) fn on_next_frame(
&self,
display_id: DisplayId,
callback: impl FnOnce(&VideoTimestamp, &VideoTimestamp) + Send + 'static,
) {
let next_frame_callbacks = self.next_frame_callbacks.clone();
let callback = Box::new(callback);
match self.next_frame_callbacks.lock().entry(display_id) {
collections::hash_map::Entry::Occupied(mut entry) => {
if entry.get().is_empty() {
self.platform_linker.start(display_id)
}
entry.get_mut().push(callback)
}
collections::hash_map::Entry::Vacant(entry) => {
// let platform_linker = self.platform_linker.clone();
self.platform_linker.set_output_callback(
display_id,
Box::new(move |current_time, output_time| {
for callback in next_frame_callbacks
.lock()
.get_mut(&display_id)
.unwrap()
.drain(..)
{
callback(current_time, output_time);
}
// platform_linker.stop(display_id);
}),
);
entry.insert(vec![callback]);
}
}
}
}

View file

@ -1,6 +1,7 @@
mod app;
mod assets;
mod color;
mod display_linker;
mod element;
mod elements;
mod executor;
@ -22,6 +23,7 @@ pub use anyhow::Result;
pub use app::*;
pub use assets::*;
pub use color::*;
pub use display_linker::*;
pub use element::*;
pub use elements::*;
pub use executor::*;

View file

@ -42,6 +42,7 @@ pub(crate) fn current_platform() -> Arc<dyn Platform> {
pub trait Platform: 'static {
fn executor(&self) -> Executor;
fn display_linker(&self) -> Arc<dyn PlatformDisplayLinker>;
fn text_system(&self) -> Arc<dyn PlatformTextSystem>;
fn run(&self, on_finish_launching: Box<dyn 'static + FnOnce()>);
@ -99,7 +100,6 @@ pub trait PlatformDisplay: Debug {
fn id(&self) -> DisplayId;
fn as_any(&self) -> &dyn Any;
fn bounds(&self) -> Bounds<GlobalPixels>;
fn link(&self) -> Box<dyn PlatformDisplayLink>;
}
#[derive(PartialEq, Eq, Hash, Copy, Clone)]
@ -156,10 +156,14 @@ pub trait PlatformDispatcher: Send + Sync {
fn dispatch_on_main_thread(&self, task: Runnable);
}
pub trait PlatformDisplayLink {
// fn set_output_callback(&mut self, callback: Box<dyn FnMut(&VideoTimestamp, &VideoTimestamp)>);
fn start(&mut self);
fn stop(&mut self);
pub trait PlatformDisplayLinker: Send + Sync {
fn set_output_callback(
&self,
display_id: DisplayId,
callback: Box<dyn FnMut(&VideoTimestamp, &VideoTimestamp)>,
);
fn start(&self, display_id: DisplayId);
fn stop(&self, display_id: DisplayId);
}
pub trait PlatformTextSystem: Send + Sync {

View file

@ -2,7 +2,7 @@
///! an origin at the bottom left of the main display.
mod dispatcher;
mod display;
// mod display_link;
mod display_linker;
mod events;
mod metal_atlas;
mod metal_renderer;
@ -33,7 +33,7 @@ use std::{
pub use dispatcher::*;
pub use display::*;
// pub use display_link::*;
pub use display_linker::*;
pub use metal_atlas::*;
pub use platform::*;
pub use text_system::*;

View file

@ -98,9 +98,4 @@ impl PlatformDisplay for MacDisplay {
display_bounds_from_native(native_bounds)
}
}
fn link(&self) -> Box<dyn crate::PlatformDisplayLink> {
unimplemented!()
// Box::new(unsafe { MacDisplayLink::new(self.0) })
}
}

View file

@ -1,41 +1,77 @@
use crate::PlatformDisplayLink;
use std::ffi::c_void;
use crate::{DisplayId, PlatformDisplayLinker};
use collections::HashMap;
use parking_lot::Mutex;
pub use sys::CVTimeStamp as VideoTimestamp;
pub struct MacDisplayLink {
sys_link: sys::DisplayLink,
output_callback: Option<Box<dyn FnMut(&VideoTimestamp, &VideoTimestamp)>>,
pub struct MacDisplayLinker {
links: Mutex<HashMap<DisplayId, MacDisplayLink>>,
}
impl MacDisplayLink {
pub unsafe fn new(display_id: u32) -> Self {
Self {
sys_link: sys::DisplayLink::on_display(display_id).unwrap(),
output_callback: None,
struct MacDisplayLink {
output_callback: Box<dyn FnMut(&VideoTimestamp, &VideoTimestamp)>,
system_link: sys::DisplayLink,
}
unsafe impl Send for MacDisplayLink {}
impl MacDisplayLinker {
pub fn new() -> Self {
MacDisplayLinker {
links: Default::default(),
}
}
}
impl PlatformDisplayLink for MacDisplayLink {
fn set_output_callback(&mut self, callback: Box<dyn FnMut(&VideoTimestamp, &VideoTimestamp)>) {
unsafe {
self.sys_link.set_output_callback(
trampoline,
self.output_callback.as_mut().unwrap()
as *mut dyn FnMut(&VideoTimestamp, &VideoTimestamp)
as *mut c_void,
impl PlatformDisplayLinker for MacDisplayLinker {
fn set_output_callback(
&self,
display_id: DisplayId,
mut output_callback: Box<dyn FnMut(&VideoTimestamp, &VideoTimestamp)>,
) {
if let Some(mut system_link) = unsafe { sys::DisplayLink::on_display(display_id.0) } {
unsafe {
system_link.set_output_callback(
trampoline,
output_callback.as_mut() as *mut dyn FnMut(_, _) as *mut c_void,
)
}
let previous = self.links.lock().insert(
display_id,
MacDisplayLink {
output_callback,
system_link,
},
);
assert!(
previous.is_none(),
"You can currently only set an output callback once per display."
)
} else {
return log::warn!("DisplayLink could not be obtained for {:?}", display_id);
}
self.output_callback = Some(callback);
}
fn start(&mut self) {
unsafe { self.sys_link.start() }
fn start(&self, display_id: DisplayId) {
if let Some(link) = self.links.lock().get_mut(&display_id) {
unsafe {
link.system_link.start();
}
} else {
log::warn!("No DisplayLink callback registered for {:?}", display_id)
}
}
fn stop(&mut self) {
unsafe { self.sys_link.stop() }
fn stop(&self, display_id: DisplayId) {
if let Some(link) = self.links.lock().get_mut(&display_id) {
unsafe {
link.system_link.stop();
}
} else {
log::warn!("No DisplayLink callback registered for {:?}", display_id)
}
}
}
@ -48,11 +84,8 @@ unsafe extern "C" fn trampoline(
context: *mut c_void,
) -> i32 {
let output_callback = &mut (*(context as *mut MacDisplayLink)).output_callback;
if let Some(callback) = output_callback {
if let Some((current_time, output_time)) = current_time.as_ref().zip(output_time.as_ref()) {
// convert sys::CVTimeStamp to VideoTimestamp
callback(&current_time, &output_time);
}
if let Some((current_time, output_time)) = current_time.as_ref().zip(output_time.as_ref()) {
output_callback(&current_time, &output_time);
}
0
}
@ -61,9 +94,8 @@ mod sys {
//! Derived from display-link crate under the fololwing license:
//! https://github.com/BrainiumLLC/display-link/blob/master/LICENSE-MIT
//! Apple docs: [CVDisplayLink](https://developer.apple.com/documentation/corevideo/cvdisplaylinkoutputcallback?language=objc)
#![allow(dead_code)]
#![allow(dead_code, non_upper_case_globals)]
pub use cocoa::quartzcore::CVTimeStamp;
use foreign_types::{foreign_type, ForeignType};
use std::{
ffi::c_void,
@ -90,6 +122,64 @@ mod sys {
}
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct CVTimeStamp {
pub version: u32,
pub video_time_scale: i32,
pub video_time: i64,
pub host_time: u64,
pub rate_scalar: f64,
pub video_refresh_period: i64,
pub smpte_time: CVSMPTETime,
pub flags: u64,
pub reserved: u64,
}
pub type CVTimeStampFlags = u64;
pub const kCVTimeStampVideoTimeValid: CVTimeStampFlags = 1 << 0;
pub const kCVTimeStampHostTimeValid: CVTimeStampFlags = 1 << 1;
pub const kCVTimeStampSMPTETimeValid: CVTimeStampFlags = 1 << 2;
pub const kCVTimeStampVideoRefreshPeriodValid: CVTimeStampFlags = 1 << 3;
pub const kCVTimeStampRateScalarValid: CVTimeStampFlags = 1 << 4;
pub const kCVTimeStampTopField: CVTimeStampFlags = 1 << 16;
pub const kCVTimeStampBottomField: CVTimeStampFlags = 1 << 17;
pub const kCVTimeStampVideoHostTimeValid: CVTimeStampFlags =
kCVTimeStampVideoTimeValid | kCVTimeStampHostTimeValid;
pub const kCVTimeStampIsInterlaced: CVTimeStampFlags =
kCVTimeStampTopField | kCVTimeStampBottomField;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct CVSMPTETime {
pub subframes: i16,
pub subframe_divisor: i16,
pub counter: u32,
pub time_type: u32,
pub flags: u32,
pub hours: i16,
pub minutes: i16,
pub seconds: i16,
pub frames: i16,
}
pub type CVSMPTETimeType = u32;
pub const kCVSMPTETimeType24: CVSMPTETimeType = 0;
pub const kCVSMPTETimeType25: CVSMPTETimeType = 1;
pub const kCVSMPTETimeType30Drop: CVSMPTETimeType = 2;
pub const kCVSMPTETimeType30: CVSMPTETimeType = 3;
pub const kCVSMPTETimeType2997: CVSMPTETimeType = 4;
pub const kCVSMPTETimeType2997Drop: CVSMPTETimeType = 5;
pub const kCVSMPTETimeType60: CVSMPTETimeType = 6;
pub const kCVSMPTETimeType5994: CVSMPTETimeType = 7;
pub type CVSMPTETimeFlags = u32;
pub const kCVSMPTETimeValid: CVSMPTETimeFlags = 1 << 0;
pub const kCVSMPTETimeRunning: CVSMPTETimeFlags = 1 << 1;
pub type CVDisplayLinkOutputCallback = unsafe extern "C" fn(
display_link_out: *mut CVDisplayLink,
// A pointer to the current timestamp. This represents the timestamp when the callback is called.

View file

@ -1,8 +1,9 @@
use super::BoolExt;
use crate::{
AnyWindowHandle, ClipboardItem, CursorStyle, DisplayId, Event, Executor, MacDispatcher,
MacDisplay, MacTextSystem, MacWindow, PathPromptOptions, Platform, PlatformDisplay,
PlatformTextSystem, PlatformWindow, Result, SemanticVersion, WindowOptions,
MacDisplay, MacDisplayLinker, MacTextSystem, MacWindow, PathPromptOptions, Platform,
PlatformDisplay, PlatformDisplayLinker, PlatformTextSystem, PlatformWindow, Result,
SemanticVersion, WindowOptions,
};
use anyhow::anyhow;
use block::ConcreteBlock;
@ -347,6 +348,10 @@ impl Platform for MacPlatform {
self.0.lock().executor.clone()
}
fn display_linker(&self) -> Arc<dyn PlatformDisplayLinker> {
Arc::new(MacDisplayLinker::new())
}
fn text_system(&self) -> Arc<dyn PlatformTextSystem> {
self.0.lock().text_system.clone()
}

View file

@ -15,6 +15,10 @@ impl Platform for TestPlatform {
unimplemented!()
}
fn display_linker(&self) -> std::sync::Arc<dyn crate::PlatformDisplayLinker> {
unimplemented!()
}
fn text_system(&self) -> std::sync::Arc<dyn crate::PlatformTextSystem> {
unimplemented!()
}

View file

@ -1,10 +1,11 @@
use crate::{
image_cache::RenderImageParams, px, AnyView, AppContext, AsyncWindowContext, AvailableSpace,
BorrowAppContext, Bounds, Context, Corners, DevicePixels, Effect, Element, EntityId, FontId,
GlyphId, Handle, Hsla, ImageData, IsZero, LayerId, LayoutId, MainThread, MainThreadOnly,
MonochromeSprite, Pixels, PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Reference,
RenderGlyphParams, RenderSvgParams, ScaledPixels, Scene, SharedString, Size, Style,
TaffyLayoutEngine, Task, WeakHandle, WindowOptions, SUBPIXEL_VARIANTS,
BorrowAppContext, Bounds, Context, Corners, DevicePixels, DisplayId, Effect, Element, EntityId,
FontId, GlyphId, Handle, Hsla, ImageData, IsZero, LayerId, LayoutId, MainThread,
MainThreadOnly, MonochromeSprite, Pixels, PlatformAtlas, PlatformWindow, Point,
PolychromeSprite, Reference, RenderGlyphParams, RenderSvgParams, ScaledPixels, Scene,
SharedString, Size, Style, TaffyLayoutEngine, Task, WeakHandle, WindowOptions,
SUBPIXEL_VARIANTS,
};
use anyhow::Result;
use smallvec::SmallVec;
@ -16,6 +17,7 @@ pub struct AnyWindow {}
pub struct Window {
handle: AnyWindowHandle,
platform_window: MainThreadOnly<Box<dyn PlatformWindow>>,
pub(crate) display_id: DisplayId, // todo!("make private again?")
sprite_atlas: Arc<dyn PlatformAtlas>,
rem_size: Pixels,
content_size: Size<Pixels>,
@ -35,6 +37,7 @@ impl Window {
cx: &mut MainThread<AppContext>,
) -> Self {
let platform_window = cx.platform().open_window(handle, options);
let display_id = platform_window.display().id();
let sprite_atlas = platform_window.sprite_atlas();
let mouse_position = platform_window.mouse_position();
let content_size = platform_window.content_size();
@ -46,6 +49,12 @@ impl Window {
cx.update_window(handle, |cx| {
cx.window.scene = Scene::new(scale_factor);
cx.window.content_size = content_size;
cx.window.display_id = cx
.window
.platform_window
.borrow_on_main_thread()
.display()
.id();
cx.window.dirty = true;
})
.log_err();
@ -57,6 +66,7 @@ impl Window {
Window {
handle,
platform_window,
display_id,
sprite_atlas,
rem_size: px(16.),
content_size,