Web Async Rework (#3082)

This commit is contained in:
daxpedda 2023-10-16 15:50:22 +02:00 committed by GitHub
parent ef34692148
commit 48f6582eb4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 653 additions and 421 deletions

View file

@ -27,11 +27,12 @@ pub(crate) struct PlatformSpecificEventLoopAttributes {}
impl<T> EventLoop<T> {
pub(crate) fn new(_: &PlatformSpecificEventLoopAttributes) -> Result<Self, EventLoopError> {
let (user_event_sender, user_event_receiver) = mpsc::channel();
let elw = RootEventLoopWindowTarget {
p: EventLoopWindowTarget::new(),
_marker: PhantomData,
};
Ok(EventLoop {
elw: RootEventLoopWindowTarget {
p: EventLoopWindowTarget::new(),
_marker: PhantomData,
},
elw,
user_event_sender,
user_event_receiver,
})
@ -101,7 +102,7 @@ impl<T> EventLoop<T> {
}
pub fn create_proxy(&self) -> EventLoopProxy<T> {
EventLoopProxy::new(self.elw.p.runner.clone(), self.user_event_sender.clone())
EventLoopProxy::new(self.elw.p.waker(), self.user_event_sender.clone())
}
pub fn window_target(&self) -> &RootEventLoopWindowTarget<T> {

View file

@ -1,30 +1,25 @@
use std::sync::mpsc::Sender;
use std::rc::Weak;
use std::sync::mpsc::{SendError, Sender};
use super::runner;
use crate::event::Event;
use super::runner::Execution;
use crate::event_loop::EventLoopClosed;
use crate::platform_impl::platform::r#async::Channel;
use crate::platform_impl::platform::r#async::Waker;
pub struct EventLoopProxy<T: 'static> {
// used to wake the event loop handler, not to actually pass data
runner: Channel<runner::Shared, ()>,
runner: Waker<Weak<Execution>>,
sender: Sender<T>,
}
impl<T: 'static> EventLoopProxy<T> {
pub fn new(runner: runner::Shared, sender: Sender<T>) -> Self {
Self {
runner: Channel::new(runner, |runner, event| {
runner.send_event(Event::UserEvent(event))
})
.unwrap(),
sender,
}
pub fn new(runner: Waker<Weak<Execution>>, sender: Sender<T>) -> Self {
Self { runner, sender }
}
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
self.sender.send(event).unwrap();
self.runner.send(());
self.sender
.send(event)
.map_err(|SendError(event)| EventLoopClosed(event))?;
self.runner.wake();
Ok(())
}
}

View file

@ -8,9 +8,10 @@ use crate::event::{
use crate::event_loop::{ControlFlow, DeviceEvents};
use crate::platform::web::PollStrategy;
use crate::platform_impl::platform::backend::EventListenerHandle;
use crate::platform_impl::platform::r#async::{DispatchRunner, Waker, WakerSpawner};
use crate::platform_impl::platform::window::Inner;
use crate::window::WindowId;
use std::sync::atomic::Ordering;
use std::{
cell::{Cell, RefCell},
clone::Clone,
@ -36,6 +37,7 @@ impl Clone for Shared {
type OnEventHandle<T> = RefCell<Option<EventListenerHandle<dyn FnMut(T)>>>;
pub struct Execution {
proxy_spawner: WakerSpawner<Weak<Self>>,
control_flow: Cell<ControlFlow>,
poll_strategy: Cell<PollStrategy>,
exit: Cell<bool>,
@ -46,7 +48,14 @@ pub struct Execution {
id: RefCell<u32>,
window: web_sys::Window,
document: Document,
all_canvases: RefCell<Vec<(WindowId, Weak<RefCell<backend::Canvas>>)>>,
#[allow(clippy::type_complexity)]
all_canvases: RefCell<
Vec<(
WindowId,
Weak<RefCell<backend::Canvas>>,
DispatchRunner<Inner>,
)>,
>,
redraw_pending: RefCell<HashSet<WindowId>>,
destroy_pending: RefCell<VecDeque<WindowId>>,
page_transition_event_handle: RefCell<Option<backend::PageTransitionEventHandle>>,
@ -140,30 +149,40 @@ impl Shared {
#[allow(clippy::disallowed_methods)]
let document = window.document().expect("Failed to obtain document");
Shared(Rc::new(Execution {
control_flow: Cell::new(ControlFlow::default()),
poll_strategy: Cell::new(PollStrategy::default()),
exit: Cell::new(false),
runner: RefCell::new(RunnerEnum::Pending),
suspended: Cell::new(false),
event_loop_recreation: Cell::new(false),
events: RefCell::new(VecDeque::new()),
window,
document,
id: RefCell::new(0),
all_canvases: RefCell::new(Vec::new()),
redraw_pending: RefCell::new(HashSet::new()),
destroy_pending: RefCell::new(VecDeque::new()),
page_transition_event_handle: RefCell::new(None),
device_events: Cell::default(),
on_mouse_move: RefCell::new(None),
on_wheel: RefCell::new(None),
on_mouse_press: RefCell::new(None),
on_mouse_release: RefCell::new(None),
on_key_press: RefCell::new(None),
on_key_release: RefCell::new(None),
on_visibility_change: RefCell::new(None),
on_touch_end: RefCell::new(None),
Shared(Rc::<Execution>::new_cyclic(|weak| {
let proxy_spawner = WakerSpawner::new(weak.clone(), |runner, count| {
if let Some(runner) = runner.upgrade() {
Shared(runner).send_events(iter::repeat(Event::UserEvent(())).take(count))
}
})
.expect("`EventLoop` has to be created in the main thread");
Execution {
proxy_spawner,
control_flow: Cell::new(ControlFlow::default()),
poll_strategy: Cell::new(PollStrategy::default()),
exit: Cell::new(false),
runner: RefCell::new(RunnerEnum::Pending),
suspended: Cell::new(false),
event_loop_recreation: Cell::new(false),
events: RefCell::new(VecDeque::new()),
window,
document,
id: RefCell::new(0),
all_canvases: RefCell::new(Vec::new()),
redraw_pending: RefCell::new(HashSet::new()),
destroy_pending: RefCell::new(VecDeque::new()),
page_transition_event_handle: RefCell::new(None),
device_events: Cell::default(),
on_mouse_move: RefCell::new(None),
on_wheel: RefCell::new(None),
on_mouse_press: RefCell::new(None),
on_mouse_release: RefCell::new(None),
on_key_press: RefCell::new(None),
on_key_release: RefCell::new(None),
on_visibility_change: RefCell::new(None),
on_touch_end: RefCell::new(None),
}
}))
}
@ -175,11 +194,13 @@ impl Shared {
&self.0.document
}
pub fn add_canvas(&self, id: WindowId, canvas: &Rc<RefCell<backend::Canvas>>) {
self.0
.all_canvases
.borrow_mut()
.push((id, Rc::downgrade(canvas)));
pub fn add_canvas(
&self,
id: WindowId,
canvas: Weak<RefCell<backend::Canvas>>,
runner: DispatchRunner<Inner>,
) {
self.0.all_canvases.borrow_mut().push((id, canvas, runner));
}
pub fn notify_destroy_window(&self, id: WindowId) {
@ -411,7 +432,7 @@ impl Shared {
"visibilitychange",
Closure::new(move |_| {
if !runner.0.suspended.get() {
for (id, canvas) in &*runner.0.all_canvases.borrow() {
for (id, canvas, _) in &*runner.0.all_canvases.borrow() {
if let Some(canvas) = canvas.upgrade() {
let is_visible = backend::is_visible(runner.document());
// only fire if:
@ -549,7 +570,7 @@ impl Shared {
self.0
.all_canvases
.borrow_mut()
.retain(|&(item_id, _)| item_id != id);
.retain(|&(item_id, _, _)| item_id != id);
self.handle_event(Event::WindowEvent {
window_id: id,
event: crate::event::WindowEvent::Destroyed,
@ -618,9 +639,29 @@ impl Shared {
// Don't take events out of the queue if the loop is closed or the runner doesn't exist
// If the runner doesn't exist and this method recurses, it will recurse infinitely
if !is_closed && self.0.runner.borrow().maybe_runner().is_some() {
// Pre-fetch window commands to avoid having to wait until the next event loop cycle
// and potentially block other threads in the meantime.
for (_, window, runner) in self.0.all_canvases.borrow().iter() {
if let Some(window) = window.upgrade() {
runner.run();
drop(window)
}
}
// Take an event out of the queue and handle it
// Make sure not to let the borrow_mut live during the next handle_event
let event = { self.0.events.borrow_mut().pop_front() };
let event = {
let mut events = self.0.events.borrow_mut();
// Pre-fetch `UserEvent`s to avoid having to wait until the next event loop cycle.
events.extend(
iter::repeat(Event::UserEvent(()))
.take(self.0.proxy_spawner.fetch())
.map(EventWrapper::from),
);
events.pop_front()
};
if let Some(event) = event {
self.handle_event(event);
}
@ -690,7 +731,7 @@ impl Shared {
// Dropping the `Runner` drops the event handler closure, which will in
// turn drop all `Window`s moved into the closure.
*self.0.runner.borrow_mut() = RunnerEnum::Destroyed;
for (_, canvas) in all_canvases {
for (_, canvas, _) in all_canvases {
// In case any remaining `Window`s are still not dropped, we will need
// to explicitly remove the event handlers associated with their canvases.
if let Some(canvas) = canvas.upgrade() {
@ -734,23 +775,29 @@ impl Shared {
fn device_events(&self) -> bool {
match self.0.device_events.get() {
DeviceEvents::Always => true,
DeviceEvents::WhenFocused => self.0.all_canvases.borrow().iter().any(|(_, canvas)| {
if let Some(canvas) = canvas.upgrade() {
canvas.borrow().has_focus.load(Ordering::Relaxed)
} else {
false
}
}),
DeviceEvents::WhenFocused => {
self.0.all_canvases.borrow().iter().any(|(_, canvas, _)| {
if let Some(canvas) = canvas.upgrade() {
canvas.borrow().has_focus.get()
} else {
false
}
})
}
DeviceEvents::Never => false,
}
}
fn transient_activation(&self) {
self.0.all_canvases.borrow().iter().for_each(|(_, canvas)| {
if let Some(canvas) = canvas.upgrade() {
canvas.borrow().transient_activation();
}
});
self.0
.all_canvases
.borrow()
.iter()
.for_each(|(_, canvas, _)| {
if let Some(canvas) = canvas.upgrade() {
canvas.borrow().transient_activation();
}
});
}
pub fn event_loop_recreation(&self, allow: bool) {
@ -780,6 +827,10 @@ impl Shared {
pub(crate) fn poll_strategy(&self) -> PollStrategy {
self.0.poll_strategy.get()
}
pub(crate) fn waker(&self) -> Waker<Weak<Execution>> {
self.0.proxy_spawner.waker()
}
}
pub(crate) enum EventWrapper {

View file

@ -3,10 +3,9 @@ use std::clone::Clone;
use std::collections::{vec_deque::IntoIter as VecDequeIter, VecDeque};
use std::iter;
use std::marker::PhantomData;
use std::rc::Rc;
use std::sync::atomic::Ordering;
use std::rc::{Rc, Weak};
use super::runner::EventWrapper;
use super::runner::{EventWrapper, Execution};
use super::{
super::{monitor::MonitorHandle, KeyEventExtra},
backend,
@ -20,6 +19,7 @@ use crate::event::{
use crate::event_loop::{ControlFlow, DeviceEvents};
use crate::keyboard::ModifiersState;
use crate::platform::web::PollStrategy;
use crate::platform_impl::platform::r#async::Waker;
use crate::window::{Theme, WindowId as RootWindowId};
#[derive(Default)]
@ -81,7 +81,6 @@ impl<T> EventLoopWindowTarget<T> {
id: WindowId,
prevent_default: bool,
) {
self.runner.add_canvas(RootWindowId(id), canvas);
let canvas_clone = canvas.clone();
let mut canvas = canvas.borrow_mut();
canvas.set_attribute("data-raw-handle", &id.0.to_string());
@ -92,7 +91,7 @@ impl<T> EventLoopWindowTarget<T> {
let has_focus = canvas.has_focus.clone();
let modifiers = self.modifiers.clone();
canvas.on_blur(move || {
has_focus.store(false, Ordering::Relaxed);
has_focus.set(false);
let clear_modifiers = (!modifiers.get().is_empty()).then(|| {
modifiers.set(ModifiersState::empty());
@ -115,7 +114,7 @@ impl<T> EventLoopWindowTarget<T> {
let runner = self.runner.clone();
let has_focus = canvas.has_focus.clone();
canvas.on_focus(move || {
if !has_focus.swap(true, Ordering::Relaxed) {
if !has_focus.replace(true) {
runner.send_event(Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::Focused(true),
@ -204,15 +203,13 @@ impl<T> EventLoopWindowTarget<T> {
let modifiers = self.modifiers.clone();
move |active_modifiers, pointer_id| {
let focus = (has_focus.load(Ordering::Relaxed)
&& modifiers.get() != active_modifiers)
.then(|| {
modifiers.set(active_modifiers);
Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
}
});
let focus = (has_focus.get() && modifiers.get() != active_modifiers).then(|| {
modifiers.set(active_modifiers);
Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
}
});
let pointer = pointer_id.map(|pointer_id| Event::WindowEvent {
window_id: RootWindowId(id),
@ -233,15 +230,13 @@ impl<T> EventLoopWindowTarget<T> {
let modifiers = self.modifiers.clone();
move |active_modifiers, pointer_id| {
let focus = (has_focus.load(Ordering::Relaxed)
&& modifiers.get() != active_modifiers)
.then(|| {
modifiers.set(active_modifiers);
Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
}
});
let focus = (has_focus.get() && modifiers.get() != active_modifiers).then(|| {
modifiers.set(active_modifiers);
Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
}
});
let pointer = pointer_id.map(|pointer_id| Event::WindowEvent {
window_id: RootWindowId(id),
@ -263,7 +258,7 @@ impl<T> EventLoopWindowTarget<T> {
let modifiers = self.modifiers.clone();
move |active_modifiers| {
if has_focus.load(Ordering::Relaxed) && modifiers.get() != active_modifiers {
if has_focus.get() && modifiers.get() != active_modifiers {
modifiers.set(active_modifiers);
runner.send_event(Event::WindowEvent {
window_id: RootWindowId(id),
@ -278,9 +273,8 @@ impl<T> EventLoopWindowTarget<T> {
let modifiers = self.modifiers.clone();
move |active_modifiers, pointer_id, events| {
let modifiers = (has_focus.load(Ordering::Relaxed)
&& modifiers.get() != active_modifiers)
.then(|| {
let modifiers =
(has_focus.get() && modifiers.get() != active_modifiers).then(|| {
modifiers.set(active_modifiers);
Event::WindowEvent {
window_id: RootWindowId(id),
@ -307,9 +301,8 @@ impl<T> EventLoopWindowTarget<T> {
let modifiers = self.modifiers.clone();
move |active_modifiers, device_id, events| {
let modifiers = (has_focus.load(Ordering::Relaxed)
&& modifiers.get() != active_modifiers)
.then(|| {
let modifiers =
(has_focus.get() && modifiers.get() != active_modifiers).then(|| {
modifiers.set(active_modifiers);
Event::WindowEvent {
window_id: RootWindowId(id),
@ -341,9 +334,8 @@ impl<T> EventLoopWindowTarget<T> {
position: crate::dpi::PhysicalPosition<f64>,
buttons,
button| {
let modifiers = (has_focus.load(Ordering::Relaxed)
&& modifiers.get() != active_modifiers)
.then(|| {
let modifiers =
(has_focus.get() && modifiers.get() != active_modifiers).then(|| {
modifiers.set(active_modifiers);
Event::WindowEvent {
window_id: RootWindowId(id),
@ -473,7 +465,7 @@ impl<T> EventLoopWindowTarget<T> {
let modifiers = self.modifiers.clone();
move |active_modifiers| {
if has_focus.load(Ordering::Relaxed) && modifiers.get() != active_modifiers {
if has_focus.get() && modifiers.get() != active_modifiers {
modifiers.set(active_modifiers);
runner.send_event(Event::WindowEvent {
window_id: RootWindowId(id),
@ -488,9 +480,8 @@ impl<T> EventLoopWindowTarget<T> {
let modifiers = self.modifiers.clone();
move |active_modifiers, pointer_id, position, button| {
let modifiers = (has_focus.load(Ordering::Relaxed)
&& modifiers.get() != active_modifiers)
.then(|| {
let modifiers =
(has_focus.get() && modifiers.get() != active_modifiers).then(|| {
modifiers.set(active_modifiers);
Event::WindowEvent {
window_id: RootWindowId(id),
@ -528,9 +519,8 @@ impl<T> EventLoopWindowTarget<T> {
let modifiers = self.modifiers.clone();
move |active_modifiers, device_id, location, force| {
let modifiers = (has_focus.load(Ordering::Relaxed)
&& modifiers.get() != active_modifiers)
.then(|| {
let modifiers =
(has_focus.get() && modifiers.get() != active_modifiers).then(|| {
modifiers.set(active_modifiers);
Event::WindowEvent {
window_id: RootWindowId(id),
@ -558,8 +548,7 @@ impl<T> EventLoopWindowTarget<T> {
let modifiers = self.modifiers.clone();
canvas.on_mouse_wheel(
move |pointer_id, delta, active_modifiers| {
let modifiers_changed = (has_focus.load(Ordering::Relaxed)
&& modifiers.get() != active_modifiers)
let modifiers_changed = (has_focus.get() && modifiers.get() != active_modifiers)
.then(|| {
modifiers.set(active_modifiers);
Event::WindowEvent {
@ -713,4 +702,8 @@ impl<T> EventLoopWindowTarget<T> {
pub(crate) fn poll_strategy(&self) -> PollStrategy {
self.runner.poll_strategy()
}
pub(crate) fn waker(&self) -> Waker<Weak<Execution>> {
self.runner.waker()
}
}