1use super::server::{BatchQueue, CompositorClock, ServerCompositionVisual, ServerCompositor, ServerObjectId};
2use super::transport::{
3 BatchMarker, BatchObject, BatchStreamWriter, CommittedBatch, CompositionBatch, ServerJob, ServerObjectFactory, ServerObjectJob,
4};
5use super::{CompositionOptions, CompositionVisual, ICompositorSerializable};
6use crate::animation::easings::{IEasing, SplineEasing};
7use crate::animation::KeySpline;
8use crate::media::imaging::Bitmap;
9use crate::platform::IPlatformGraphics;
10use crate::reactive::{Disposable, IDisposable};
11use crate::rendering::composition::server::IServerObject;
12use crate::rendering::{IRenderLoop, IRenderLoopTask};
13use crate::threading::{Dispatcher, DispatcherPriority};
14use crate::utilities::HandlerList;
15use crate::{FerroLocator, LocatorExtensions};
16use std::cell::{Cell, RefCell};
17use std::collections::{HashMap, HashSet, VecDeque};
18use std::rc::{Rc, Weak};
19use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
20use std::sync::{Arc, Mutex};
21use std::time::Duration;
22
23pub trait ICompositorScheduler {
26 fn commit_requested(&self, compositor: &Rc<Compositor>);
27}
28
29static NEXT_COMPOSITOR_KEY: AtomicU64 = AtomicU64::new(1);
30
31crate::ferro_thread_local! {
32 static COMPOSITORS: RefCell<HashMap<u64, Weak<Compositor>>> = RefCell::new(HashMap::new());
35}
36
37fn find_compositor(key: u64) -> Option<Rc<Compositor>> {
38 COMPOSITORS.with(|compositors| compositors.borrow().get(&key).and_then(Weak::upgrade))
39}
40
41pub struct Compositor {
44 this: Weak<Compositor>,
45 key: u64,
46 render_loop: Arc<dyn IRenderLoop>,
47 loop_task: Arc<dyn IRenderLoopTask>,
48 use_ui_thread_for_synchronous_commits: bool,
49 server: Arc<super::server::LockedServerCompositor>,
51 render_thread: bool,
55 confined_loop_task: Option<Arc<RenderThreadLoopTask>>,
60 readback: Arc<super::server::ReadbackIndices>,
65 batches: Arc<BatchQueue>,
66 clock: CompositorClock,
67 next_commit: RefCell<Option<Arc<CompositionBatch>>>,
68 object_serialization_queue: RefCell<VecDeque<Rc<dyn ICompositorSerializable>>>,
69 object_serialization_hash_set: RefCell<HashSet<*const ()>>,
70 invoke_before_commit_write: RefCell<VecDeque<Box<dyn FnOnce()>>>,
71 invoke_before_commit_read: RefCell<VecDeque<Box<dyn FnOnce()>>>,
72 dispose_on_next_batch: RefCell<Vec<PendingDisposal>>,
73 pending_creations: RefCell<Vec<(ServerObjectId, ServerObjectFactory)>>,
74 next_server_object_id: Cell<u32>,
75 free_server_object_ids: RefCell<Vec<u32>>,
79 server_object_id_in_use: RefCell<Vec<bool>>,
82 pending_batch: Arc<Mutex<Option<Arc<CompositionBatch>>>>,
83 pending_server_compositor_jobs: RefCell<Vec<PendingServerJob>>,
84 pending_server_compositor_post_target_jobs: RefCell<Vec<PendingServerJob>>,
85 scheduler: Weak<dyn ICompositorScheduler>,
86 dispatcher: Arc<Dispatcher>,
87 after_commit: HandlerList<dyn Fn()>,
88 default_easing: Rc<dyn IEasing>,
89}
90
91struct ServerCompositorLoopTask {
95 key: u64,
96 dispatcher: Arc<Dispatcher>,
97 marshalled: Arc<AtomicBool>,
98 last_result: Arc<AtomicBool>,
99}
100
101impl IRenderLoopTask for ServerCompositorLoopTask {
102 fn render(&self) -> bool {
103 if let Some(compositor) = find_compositor(self.key) {
104 let result = compositor.server().render();
105 self.last_result.store(result, Ordering::SeqCst);
106 return result;
107 }
108 if !self.marshalled.swap(true, Ordering::SeqCst) {
109 let key = self.key;
110 let marshalled = self.marshalled.clone();
111 let last_result = self.last_result.clone();
112 self.dispatcher.post(
113 move || {
114 marshalled.store(false, Ordering::SeqCst);
115 if let Some(compositor) = find_compositor(key) {
116 last_result.store(compositor.server().render(), Ordering::SeqCst);
117 }
118 },
119 DispatcherPriority::RENDER,
120 );
121 }
122 self.last_result.load(Ordering::SeqCst)
123 }
124}
125
126pub type SharedCompositorClock = Arc<dyn Fn() -> Duration + Send + Sync>;
129
130struct RenderThreadLoopTask {
133 this: std::sync::Weak<RenderThreadLoopTask>,
134 server: Arc<super::server::LockedServerCompositor>,
135 confined: bool,
138 release_requested: AtomicBool,
141 removal_posted: AtomicBool,
142 render_loop: std::sync::Weak<dyn IRenderLoop>,
143 dispatcher: Arc<Dispatcher>,
144}
145
146impl RenderThreadLoopTask {
147 fn request_release(&self) {
150 self.release_requested.store(true, Ordering::SeqCst);
151 }
152
153 fn remove_from_loop(&self) {
157 if self.removal_posted.swap(true, Ordering::SeqCst) {
158 return;
159 }
160 let Some(this) = self.this.upgrade() else { return };
161 let render_loop = self.render_loop.clone();
162 self.dispatcher.post(
163 move || {
164 if let Some(render_loop) = render_loop.upgrade() {
165 let task: Arc<dyn IRenderLoopTask> = this;
166 render_loop.remove(&task);
167 }
168 },
169 DispatcherPriority::SEND,
170 );
171 }
172}
173
174impl IRenderLoopTask for RenderThreadLoopTask {
175 fn render(&self) -> bool {
176 if self.confined {
177 self.server.confine_to_current_thread();
178 }
179 let rendered = self
183 .server
184 .try_with(|server| (!self.release_requested.load(Ordering::SeqCst)).then(|| server.render()));
185 match rendered {
186 Some(Some(wants_next_tick)) => wants_next_tick,
187 Some(None) => {
188 self.server.try_with(|server| server.release_gpu_resources());
195 self.server.release();
196 self.remove_from_loop();
197 false
198 }
199 None => false,
201 }
202 }
203}
204
205impl Compositor {
206 pub fn with_render_thread(
223 render_loop: Arc<dyn IRenderLoop>,
224 gpu: Option<Arc<dyn IPlatformGraphics>>,
225 use_ui_thread_for_synchronous_commits: bool,
226 scheduler: &Rc<dyn ICompositorScheduler>,
227 dispatcher: Arc<Dispatcher>,
228 options: Option<CompositionOptions>,
229 clock: Option<SharedCompositorClock>,
230 ) -> Rc<Compositor> {
231 let options = options
232 .or_else(|| FerroLocator::current().get_service::<CompositionOptions>().map(|o| *o))
233 .unwrap_or_default();
234 let clock = clock.unwrap_or_else(|| {
237 let origin = std::time::Instant::now();
238 Arc::new(move || origin.elapsed())
239 });
240 let server_clock: CompositorClock = {
241 let clock = clock.clone();
242 Rc::new(move || clock())
243 };
244 let clock: CompositorClock = Rc::new(move || clock());
245 let batches = Arc::new(BatchQueue::default());
246 let readback = Arc::new(super::server::ReadbackIndices::new());
247 let server = Arc::new(super::server::LockedServerCompositor::new(ServerCompositor::new(
248 gpu,
249 options,
250 batches.clone(),
251 readback.clone(),
252 server_clock,
253 )));
254 let key = NEXT_COMPOSITOR_KEY.fetch_add(1, Ordering::SeqCst);
255 let confined = !use_ui_thread_for_synchronous_commits && render_loop.runs_in_background();
259 let task = Arc::new_cyclic(|this| RenderThreadLoopTask {
260 this: this.clone(),
261 server: server.clone(),
262 confined,
263 release_requested: AtomicBool::new(false),
264 removal_posted: AtomicBool::new(false),
265 render_loop: Arc::downgrade(&render_loop),
266 dispatcher: dispatcher.clone(),
267 });
268 let loop_task: Arc<dyn IRenderLoopTask> = task.clone();
269 Self::build(
270 render_loop,
271 loop_task,
272 confined.then_some(task),
273 use_ui_thread_for_synchronous_commits,
274 server,
275 true,
276 batches,
277 readback,
278 clock,
279 scheduler,
280 dispatcher,
281 key,
282 )
283 }
284
285 pub fn new(gpu: Option<Arc<dyn IPlatformGraphics>>, use_ui_thread_for_synchronous_commits: bool) -> Rc<Compositor> {
296 let render_loop = FerroLocator::current().get_required_service::<Arc<dyn IRenderLoop>>();
297 let scheduler: Rc<dyn ICompositorScheduler> = crate::media::MediaContext::instance().scheduler();
298 let render_thread_disabled = std::env::var("FERROUI_RENDER_THREAD").is_ok_and(|value| value == "0");
299 if render_loop.runs_in_background() && !render_thread_disabled {
300 return Self::with_render_thread(
301 (*render_loop).clone(),
302 gpu,
303 use_ui_thread_for_synchronous_commits,
304 &scheduler,
305 Dispatcher::ui_thread(),
306 None,
307 None,
308 );
309 }
310 Self::with_scheduler(
311 (*render_loop).clone(),
312 gpu,
313 use_ui_thread_for_synchronous_commits,
314 &scheduler,
315 Dispatcher::ui_thread(),
316 None,
317 None,
318 )
319 }
320
321 pub fn with_scheduler(
326 render_loop: Arc<dyn IRenderLoop>,
327 gpu: Option<Arc<dyn IPlatformGraphics>>,
328 use_ui_thread_for_synchronous_commits: bool,
329 scheduler: &Rc<dyn ICompositorScheduler>,
330 dispatcher: Arc<Dispatcher>,
331 options: Option<CompositionOptions>,
332 clock: Option<CompositorClock>,
333 ) -> Rc<Compositor> {
334 let options = options
335 .or_else(|| FerroLocator::current().get_service::<CompositionOptions>().map(|o| *o))
336 .unwrap_or_default();
337 let clock = clock.unwrap_or_else(|| {
338 let platform_impl = dispatcher.platform_impl();
339 let origin = platform_impl.now();
340 Rc::new(move || Duration::from_millis((platform_impl.now() - origin).max(0) as u64))
341 });
342 let batches = Arc::new(BatchQueue::default());
343 let readback = Arc::new(super::server::ReadbackIndices::new());
344 let server = Arc::new(super::server::LockedServerCompositor::new(ServerCompositor::new(
345 gpu,
346 options,
347 batches.clone(),
348 readback.clone(),
349 clock.clone(),
350 )));
351 let key = NEXT_COMPOSITOR_KEY.fetch_add(1, Ordering::SeqCst);
352 let loop_task: Arc<dyn IRenderLoopTask> = Arc::new(ServerCompositorLoopTask {
353 key,
354 dispatcher: dispatcher.clone(),
355 marshalled: Arc::new(AtomicBool::new(false)),
356 last_result: Arc::new(AtomicBool::new(true)),
357 });
358 Self::build(
359 render_loop,
360 loop_task,
361 None,
362 use_ui_thread_for_synchronous_commits,
363 server,
364 false,
365 batches,
366 readback,
367 clock,
368 scheduler,
369 dispatcher,
370 key,
371 )
372 }
373
374 #[allow(clippy::too_many_arguments)]
375 fn build(
376 render_loop: Arc<dyn IRenderLoop>,
377 loop_task: Arc<dyn IRenderLoopTask>,
378 confined_loop_task: Option<Arc<RenderThreadLoopTask>>,
379 use_ui_thread_for_synchronous_commits: bool,
380 server: Arc<super::server::LockedServerCompositor>,
381 render_thread: bool,
382 batches: Arc<BatchQueue>,
383 readback: Arc<super::server::ReadbackIndices>,
384 clock: CompositorClock,
385 scheduler: &Rc<dyn ICompositorScheduler>,
386 dispatcher: Arc<Dispatcher>,
387 key: u64,
388 ) -> Rc<Compositor> {
389 let compositor = Rc::new_cyclic(|this| Compositor {
390 this: this.clone(),
391 key,
392 render_loop: render_loop.clone(),
393 loop_task: loop_task.clone(),
394 use_ui_thread_for_synchronous_commits,
395 readback,
396 server,
397 render_thread,
398 confined_loop_task,
399 batches,
400 clock,
401 next_commit: RefCell::new(None),
402 object_serialization_queue: RefCell::new(VecDeque::new()),
403 object_serialization_hash_set: RefCell::new(HashSet::new()),
404 invoke_before_commit_write: RefCell::new(VecDeque::new()),
405 invoke_before_commit_read: RefCell::new(VecDeque::new()),
406 dispose_on_next_batch: RefCell::new(Vec::new()),
407 pending_creations: RefCell::new(Vec::new()),
408 next_server_object_id: Cell::new(0),
409 free_server_object_ids: RefCell::new(Vec::new()),
410 server_object_id_in_use: RefCell::new(Vec::new()),
411 pending_batch: Arc::new(Mutex::new(None)),
412 pending_server_compositor_jobs: RefCell::new(Vec::new()),
413 pending_server_compositor_post_target_jobs: RefCell::new(Vec::new()),
414 scheduler: Rc::downgrade(scheduler),
415 dispatcher,
416 after_commit: HandlerList::new(),
417 default_easing: Rc::new(SplineEasing::with_key_spline(KeySpline::with_points(0.25, 0.1, 0.25, 1.0))),
418 });
419 COMPOSITORS.with(|compositors| {
420 let mut compositors = compositors.borrow_mut();
421 compositors.retain(|_, c| c.strong_count() > 0);
422 compositors.insert(key, Rc::downgrade(&compositor));
423 });
424 render_loop.add(loop_task);
425 compositor
426 }
427
428 pub(crate) fn default_easing(&self) -> Rc<dyn IEasing> {
430 self.default_easing.clone()
431 }
432
433 pub fn render_loop(&self) -> &Arc<dyn IRenderLoop> {
435 &self.render_loop
436 }
437
438 pub fn use_ui_thread_for_synchronous_commits(&self) -> bool {
439 self.use_ui_thread_for_synchronous_commits
440 }
441
442 pub fn server(&self) -> &Rc<ServerCompositor> {
446 assert!(
447 !self.render_thread,
448 "in the render-thread mode the server compositor is reached under its lock: with_server"
449 );
450 self.server.same_thread()
451 }
452
453 pub fn with_server<R>(&self, f: impl FnOnce(&Rc<ServerCompositor>) -> R) -> R {
457 self.server.with(f)
458 }
459
460 pub fn locked_server(&self) -> Arc<super::server::LockedServerCompositor> {
471 self.server.clone()
472 }
473
474 pub(crate) fn bind_to_lock<T>(&self, server: &ServerCompositor, value: T) -> super::server::LockBound<T> {
479 super::server::LockBound::new(&self.server, server, value)
480 }
481
482 pub fn render_on_this_thread(&self) -> bool {
490 assert!(
491 !self.is_confined_to_render_thread(),
492 "a compositor that is confined to its render thread is not rendered by the thread of the compositor"
493 );
494 self.server.with(|server| server.render())
495 }
496
497 pub fn renders_on_render_thread(&self) -> bool {
500 self.render_thread
501 }
502
503 pub fn is_confined_to_render_thread(&self) -> bool {
515 self.confined_loop_task.is_some()
516 }
517
518 pub fn readback(&self) -> &Arc<super::server::ReadbackIndices> {
520 &self.readback
521 }
522
523 pub fn dispatcher(&self) -> &Arc<Dispatcher> {
525 &self.dispatcher
526 }
527
528 pub fn clock_elapsed(&self) -> Duration {
530 (self.clock)()
531 }
532
533 pub fn after_commit(&self, handler: impl Fn() + 'static) -> Rc<dyn IDisposable> {
535 let token = self.after_commit.add(Rc::new(handler));
536 let weak = self.this.clone();
537 Disposable::create(move || {
538 if let Some(this) = weak.upgrade() {
539 this.after_commit.remove(token);
540 }
541 })
542 }
543
544 pub(crate) fn this_handle(&self) -> Rc<Compositor> {
545 self.this()
546 }
547
548 fn this(&self) -> Rc<Compositor> {
549 self.this.upgrade().expect("the compositor is alive while it is used")
550 }
551
552 fn trigger_commit_requested(&self) {
553 if let Some(scheduler) = self.scheduler.upgrade() {
554 scheduler.commit_requested(&self.this());
555 }
556 }
557
558 pub fn request_composition_batch_commit_async(&self) -> Arc<CompositionBatch> {
561 self.dispatcher.verify_access();
562 if let Some(next_commit) = self.next_commit.borrow().clone() {
563 return next_commit;
564 }
565 let next_commit = CompositionBatch::new();
566 *self.next_commit.borrow_mut() = Some(next_commit.clone());
567 let pending = self.pending_batch.lock().unwrap_or_else(|e| e.into_inner()).clone();
568 match pending {
569 Some(pending) => {
570 let key = self.key;
573 let dispatcher = self.dispatcher.clone();
574 pending.processed().on_completed(move || {
575 dispatcher.post(
576 move || {
577 if let Some(compositor) = find_compositor(key) {
578 compositor.trigger_commit_requested();
579 }
580 },
581 DispatcherPriority::SEND,
582 );
583 });
584 }
585 None => self.trigger_commit_requested(),
586 }
587 next_commit
588 }
589
590 pub fn request_commit_async(&self) -> Arc<CompositionBatch> {
592 self.request_composition_batch_commit_async()
593 }
594
595 pub fn commit(&self) -> Arc<CompositionBatch> {
598 let result = self.commit_core();
599 if !self.invoke_before_commit_write.borrow().is_empty() {
600 self.request_commit_async();
601 }
602 if !self.after_commit.is_empty() {
603 for (_, handler) in self.after_commit.snapshot().iter() {
604 handler();
605 }
606 }
607 result
608 }
609
610 fn commit_core(&self) -> Arc<CompositionBatch> {
611 self.dispatcher.verify_access();
612
613 let commit = self.next_commit.borrow_mut().get_or_insert_with(CompositionBatch::new).clone();
614
615 std::mem::swap(
616 &mut *self.invoke_before_commit_read.borrow_mut(),
617 &mut *self.invoke_before_commit_write.borrow_mut(),
618 );
619 loop {
620 let Some(action) = self.invoke_before_commit_read.borrow_mut().pop_front() else { break };
621 action();
622 }
623
624 let mut changes = self.batches.rent_data();
625 let disposed: Vec<PendingDisposal>;
626 {
627 let mut writer = BatchStreamWriter::new(&mut changes);
628
629 let mut objects_written = false;
632 let mut serialized: Vec<Rc<dyn ICompositorSerializable>> = Vec::new();
638 loop {
639 self.write_pending_creations(&mut writer);
640 let Some(object) = self.object_serialization_queue.borrow_mut().pop_front() else { break };
641 if let Some(server_object) = object.try_get_server(self) {
642 object.prepare_serialization(self);
645 self.write_pending_creations(&mut writer);
646 writer.write_server_object(Some(server_object));
647 object.serialize_changes(self, &mut writer);
648 if cfg!(debug_assertions) {
649 writer.write(super::server::OBJECT_END_MAGIC);
650 writer.write_marker(BatchMarker::ObjectEnd);
651 }
652 objects_written = true;
653 }
654 serialized.push(object);
655 }
656 let _ = objects_written;
657 self.object_serialization_hash_set.borrow_mut().clear();
658 drop(serialized);
659
660 disposed = std::mem::take(&mut *self.dispose_on_next_batch.borrow_mut());
661 if !disposed.is_empty() {
662 writer.write_marker(BatchMarker::RenderThreadDisposeStart);
663 writer.write(disposed.len() as i32);
664 for entry in &disposed {
665 writer.write_server_object(Some(entry.id));
666 writer.write(entry.dispose);
667 writer.write(entry.release);
668 }
669 }
670
671 Self::serialize_server_jobs(
672 &mut writer,
673 &self.pending_server_compositor_jobs,
674 BatchMarker::RenderThreadJobsStart,
675 BatchMarker::RenderThreadJobsEnd,
676 );
677 Self::serialize_server_jobs(
678 &mut writer,
679 &self.pending_server_compositor_post_target_jobs,
680 BatchMarker::RenderThreadPostTargetJobsStart,
681 BatchMarker::RenderThreadPostTargetJobsEnd,
682 );
683 }
684 {
687 let mut in_use = self.server_object_id_in_use.borrow_mut();
688 let mut free = self.free_server_object_ids.borrow_mut();
689 for entry in disposed.iter().filter(|entry| entry.release) {
690 if in_use.get_mut(entry.id.index()).is_some_and(|in_use| std::mem::replace(in_use, false)) {
691 free.push(entry.id.0);
692 }
693 }
694 }
695
696 self.enqueue_batch(CommittedBatch { batch: commit.clone(), changes, committed_at: self.clock_elapsed() });
697
698 *self.pending_batch.lock().unwrap_or_else(|e| e.into_inner()) = Some(commit.clone());
699 let pending_batch = self.pending_batch.clone();
700 let sequence_id = commit.sequence_id();
701 commit.processed().on_completed(move || {
702 let mut pending = pending_batch.lock().unwrap_or_else(|e| e.into_inner());
703 if pending.as_ref().is_some_and(|p| p.sequence_id() == sequence_id) {
704 *pending = None;
705 }
706 });
707 *self.next_commit.borrow_mut() = None;
708
709 commit
710 }
711
712 fn write_pending_creations(&self, writer: &mut BatchStreamWriter<'_>) {
713 let creations = std::mem::take(&mut *self.pending_creations.borrow_mut());
714 if creations.is_empty() {
715 return;
716 }
717 writer.write_marker(BatchMarker::CreateStart);
718 writer.write(creations.len() as i32);
719 for (id, factory) in creations {
720 writer.write_server_object(Some(id));
721 writer.write_object(BatchObject::Create(factory));
722 }
723 }
724
725 fn serialize_server_jobs(
726 writer: &mut BatchStreamWriter<'_>,
727 list: &RefCell<Vec<PendingServerJob>>,
728 start_marker: BatchMarker,
729 end_marker: BatchMarker,
730 ) {
731 let jobs = std::mem::take(&mut *list.borrow_mut());
732 if !jobs.is_empty() {
733 writer.write_marker(start_marker);
734 for job in jobs {
735 match job {
736 PendingServerJob::Job(job) => writer.write_object(BatchObject::Job(job)),
737 PendingServerJob::ObjectJob(target, job) => {
738 writer.write_server_object(Some(target));
739 writer.write_object(BatchObject::ObjectJob(job));
740 }
741 }
742 }
743 writer.write_marker(end_marker);
744 }
745 }
746
747 fn enqueue_batch(&self, batch: CommittedBatch) {
748 self.batches.enqueue(batch);
749 self.render_loop.wakeup();
750 }
751
752 pub(crate) fn oob_dispose(&self, server: ServerObjectId) -> Arc<CompositionBatch> {
764 self.dispose_on_next_batch.borrow_mut().retain_mut(|entry| {
766 if entry.id == server {
767 entry.dispose = false;
768 entry.release
769 } else {
770 true
771 }
772 });
773 let pending_creation = {
774 let mut creations = self.pending_creations.borrow_mut();
775 creations.iter().position(|(id, _)| *id == server).map(|index| creations.remove(index))
776 };
777 let batch = CompositionBatch::new();
778 let mut changes = self.batches.rent_data();
779 {
780 let mut writer = BatchStreamWriter::new(&mut changes);
781 if let Some((id, factory)) = pending_creation {
782 writer.write_marker(BatchMarker::CreateStart);
783 writer.write(1i32);
784 writer.write_server_object(Some(id));
785 writer.write_object(BatchObject::Create(factory));
786 }
787 writer.write_marker(BatchMarker::RenderThreadDisposeStart);
788 writer.write(1i32);
789 writer.write_server_object(Some(server));
790 writer.write(true);
791 writer.write(false);
792 }
793 self.enqueue_batch(CommittedBatch { batch: batch.clone(), changes, committed_at: self.clock_elapsed() });
794 batch
795 }
796
797 pub fn create_server_object(
800 &self,
801 factory: impl FnOnce(&Rc<ServerCompositor>, ServerObjectId) -> Rc<dyn IServerObject> + Send + 'static,
802 ) -> ServerObjectId {
803 self.dispatcher.verify_access();
804 let id = match self.free_server_object_ids.borrow_mut().pop() {
805 Some(id) => ServerObjectId(id),
806 None => {
807 let id = self.next_server_object_id.get();
808 self.next_server_object_id.set(id + 1);
809 self.server_object_id_in_use.borrow_mut().push(false);
810 ServerObjectId(id)
811 }
812 };
813 self.server_object_id_in_use.borrow_mut()[id.index()] = true;
814 self.pending_creations.borrow_mut().push((id, Box::new(factory)));
815 self.request_commit_async();
816 id
817 }
818
819 pub fn register_for_serialization(&self, composition_object: Rc<dyn ICompositorSerializable>) {
821 self.dispatcher.verify_access();
822 let key = composition_object.serialization_key();
823 if self.object_serialization_hash_set.borrow_mut().insert(key) {
824 self.object_serialization_queue.borrow_mut().push_back(composition_object);
825 }
826 self.request_commit_async();
827 }
828
829 pub fn dispose_on_next_batch(&self, obj: ServerObjectId) {
832 if self.queue_disposal(obj, true, true) {
833 self.request_commit_async();
834 }
835 }
836
837 pub(crate) fn dispose_and_keep_on_next_batch(&self, obj: ServerObjectId) {
844 if self.queue_disposal(obj, true, false) {
845 self.request_commit_async();
846 }
847 }
848
849 pub(crate) fn dispose_with_a_later_batch(&self, obj: ServerObjectId) {
853 self.queue_disposal(obj, true, true);
854 }
855
856 pub(crate) fn release_with_a_later_batch(&self, obj: ServerObjectId) {
860 self.queue_disposal(obj, false, true);
861 }
862
863 fn queue_disposal(&self, obj: ServerObjectId, dispose: bool, release: bool) -> bool {
866 if !self.server_object_id_in_use.borrow().get(obj.index()).copied().unwrap_or(false) {
869 return false;
870 }
871 let Ok(mut list) = self.dispose_on_next_batch.try_borrow_mut() else { return false };
872 match list.iter_mut().find(|entry| entry.id == obj) {
873 Some(entry) => {
874 let changed = (dispose && !entry.dispose) || (release && !entry.release);
875 entry.dispose |= dispose;
876 entry.release |= release;
877 changed
878 }
879 None => {
880 list.push(PendingDisposal { id: obj, dispose, release });
881 true
882 }
883 }
884 }
885
886 pub fn request_composition_update(&self, action: impl FnOnce() + 'static) {
888 self.dispatcher.verify_access();
889 self.invoke_before_commit_write.borrow_mut().push_back(Box::new(action));
890 self.request_commit_async();
891 }
892
893 pub fn post_server_job(&self, job: impl FnOnce(&ServerCompositor) + Send + 'static, post_target: bool) {
897 self.dispatcher.verify_access();
898 let list = if post_target {
899 &self.pending_server_compositor_post_target_jobs
900 } else {
901 &self.pending_server_compositor_jobs
902 };
903 list.borrow_mut().push(PendingServerJob::Job(Box::new(job)));
904 self.request_commit_async();
905 }
906
907 pub fn post_server_object_job(
913 &self,
914 target: ServerObjectId,
915 job: impl FnOnce(&ServerCompositor, Option<Rc<dyn IServerObject>>) + Send + 'static,
916 post_target: bool,
917 ) {
918 self.dispatcher.verify_access();
919 let list = if post_target {
920 &self.pending_server_compositor_post_target_jobs
921 } else {
922 &self.pending_server_compositor_jobs
923 };
924 list.borrow_mut().push(PendingServerJob::ObjectJob(target, Box::new(job)));
925 self.request_commit_async();
926 }
927
928 pub fn invoke_server_object_job_async<T: Send + 'static>(
932 &self,
933 target: ServerObjectId,
934 job: impl FnOnce(&ServerCompositor, Option<Rc<dyn IServerObject>>) -> Result<T, ServerJobError>
935 + Send
936 + 'static,
937 post_target: bool,
938 ) -> ServerJobTask<T> {
939 let task = ServerJobTask::new();
940 let completion = task.clone();
941 self.post_server_object_job(
942 target,
943 move |compositor, object| match job(compositor, object) {
944 Ok(result) => completion.set_result(result),
945 Err(error) => completion.set_exception(error),
946 },
947 post_target,
948 );
949 task
950 }
951
952 pub fn invoke_server_job_async<T: Send + 'static>(
956 &self,
957 job: impl FnOnce(&ServerCompositor) -> Result<T, ServerJobError> + Send + 'static,
958 post_target: bool,
959 ) -> ServerJobTask<T> {
960 let task = ServerJobTask::new();
961 let completion = task.clone();
962 self.post_server_job(
963 move |compositor| match job(compositor) {
964 Ok(result) => completion.set_result(result),
965 Err(error) => completion.set_exception(error),
966 },
967 post_target,
968 );
969 task
970 }
971
972 pub fn create_composition_visual_snapshot(&self, visual: &CompositionVisual, scaling: f64) -> ServerJobTask<Bitmap> {
981 if !Rc::ptr_eq(visual.compositor(), &self.this()) {
982 return ServerJobTask::from_exception(Arc::new(CompositionVisualSnapshotError(
983 "the visual belongs to another compositor",
984 )));
985 }
986 if visual.root().is_none() {
987 return ServerJobTask::from_exception(Arc::new(CompositionVisualSnapshotError(
988 "the visual is not attached to a composition target",
989 )));
990 }
991 let server = visual.server();
994 self.invoke_server_job_async(
995 move |compositor| match compositor.get::<ServerCompositionVisual>(server) {
996 Some(visual) => Ok(Bitmap::from_impl(compositor.create_composition_visual_snapshot(&visual, scaling, true))),
997 None => {
998 let error: ServerJobError =
999 Arc::new(CompositionVisualSnapshotError("the server visual no longer exists"));
1000 Err(error)
1001 }
1002 },
1003 true,
1004 )
1005 }
1006
1007 fn get_render_interface_public_features<R: Send + 'static>(
1028 &self,
1029 f: impl FnOnce(&ServerCompositor, &HashMap<std::any::TypeId, Rc<dyn std::any::Any>>, bool) -> R + Send + 'static,
1030 ) -> LocalFuture<Option<R>> {
1031 self.dispatcher.verify_access();
1032 if self.is_confined_to_render_thread() {
1033 let f = Cell::new(Some(f));
1037 let cached = self.server.with(|server| {
1038 let features = server.at_try_get_cached_render_interface_features()?;
1039 let f = f.take().expect("taken once");
1040 Some(f(server, &features, false))
1041 });
1042 if let Some(result) = cached {
1043 return Box::pin(std::future::ready(Some(result)));
1044 }
1045 let f = f.take().expect("taken once");
1046 let task = self.invoke_server_job_async(
1047 move |server| {
1048 let features = server.rt_get_render_interface_features();
1049 Ok(f(server, &features, true))
1050 },
1051 false,
1052 );
1053 return Box::pin(ServerJobResult::new(task));
1054 }
1055 let result = self.server.with(|server| {
1056 if let Some(features) = server.at_try_get_cached_render_interface_features() {
1057 return Some(f(server, &features, true));
1058 }
1059 if !server.render_interface().is_ready() {
1060 return None;
1061 }
1062 let features = server.rt_get_render_interface_features();
1063 Some(f(server, &features, true))
1064 });
1065 Box::pin(std::future::ready(result))
1066 }
1067
1068 pub fn try_get_render_interface_feature(
1082 &self,
1083 feature_type: std::any::TypeId,
1084 ) -> LocalFuture<Option<super::RenderInterfaceFeature>> {
1085 let locked = self.server.clone();
1088 let features = self.get_render_interface_public_features(move |server, features, _| {
1089 let feature = features.get(&feature_type)?;
1090 Some(super::RenderInterfaceFeature::new(super::server::LockBound::new(&locked, server, feature.clone())))
1091 });
1092 Box::pin(async move { features.await.flatten() })
1093 }
1094
1095 pub fn try_get_composition_gpu_interop(&self) -> LocalFuture<Option<Rc<dyn super::ICompositionGpuInterop>>> {
1109 let feature_type = std::any::TypeId::of::<dyn crate::platform::IExternalObjectsRenderInterfaceContextFeature>();
1110 let locked = self.server.clone();
1111 let parts = self.get_render_interface_public_features(move |server, features, may_create_context| {
1112 let external_objects = features
1113 .get(&feature_type)?
1114 .downcast_ref::<Rc<dyn crate::platform::IExternalObjectsRenderInterfaceContextFeature>>()?
1115 .clone();
1116 super::CompositionInterop::server_part(&locked, server, external_objects, may_create_context)
1119 });
1120 let this = self.this();
1121 Box::pin(async move {
1122 let part = parts.await.flatten()?;
1123 let interop: Rc<dyn super::ICompositionGpuInterop> = super::CompositionInterop::from_server_part(&this, part);
1124 Some(interop)
1125 })
1126 }
1127
1128 pub fn unit_test_is_registered_for_serialization(&self, serializable: &dyn ICompositorSerializable) -> bool {
1130 self.object_serialization_hash_set.borrow().contains(&serializable.serialization_key())
1131 }
1132
1133 pub fn try_get_default_compositor() -> Option<Rc<Compositor>> {
1135 FerroLocator::current().get_service::<Compositor>()
1136 }
1137}
1138
1139impl Drop for Compositor {
1140 fn drop(&mut self) {
1141 match &self.confined_loop_task {
1142 Some(task) => {
1143 task.request_release();
1150 self.server.try_with(|server| server.render_interface().release_platform_handles());
1156 self.render_loop.wakeup();
1160 }
1161 None => {
1162 self.render_loop.remove(&self.loop_task);
1163 self.server.release();
1168 }
1169 }
1170 self.batches.complete_all();
1175 let key = self.key;
1176 let _ = COMPOSITORS.try_with(|compositors| {
1178 if let Ok(mut compositors) = compositors.try_borrow_mut() {
1179 compositors.remove(&key);
1180 }
1181 });
1182 }
1183}
1184
1185struct PendingDisposal {
1189 id: ServerObjectId,
1190 dispose: bool,
1191 release: bool,
1192}
1193
1194#[derive(Clone, Debug)]
1197pub struct CompositionVisualSnapshotError(&'static str);
1198
1199impl std::fmt::Display for CompositionVisualSnapshotError {
1200 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1201 f.write_str(self.0)
1202 }
1203}
1204
1205impl std::error::Error for CompositionVisualSnapshotError {}
1206
1207enum PendingServerJob {
1209 Job(ServerJob),
1210 ObjectJob(ServerObjectId, ServerObjectJob),
1211}
1212
1213pub type ServerJobError = Arc<dyn std::error::Error + Send + Sync>;
1216
1217pub struct ServerJobTask<T> {
1225 shared: Arc<ServerJobTaskShared<T>>,
1226}
1227
1228struct ServerJobTaskShared<T> {
1229 outcome: std::sync::Mutex<ServerJobOutcome<T>>,
1230 continuations: crate::utilities::ThreadBound<RefCell<Vec<Box<dyn FnOnce()>>>>,
1231 thread: std::thread::ThreadId,
1232}
1233
1234enum ServerJobOutcome<T> {
1235 Running,
1236 RanToCompletion(Option<T>),
1237 Faulted(ServerJobError),
1238}
1239
1240impl<T> Clone for ServerJobTask<T> {
1241 fn clone(&self) -> Self {
1242 Self { shared: self.shared.clone() }
1243 }
1244}
1245
1246impl<T: Send + 'static> ServerJobTask<T> {
1247 fn with_outcome(outcome: ServerJobOutcome<T>) -> Self {
1248 Self {
1249 shared: Arc::new(ServerJobTaskShared {
1250 outcome: std::sync::Mutex::new(outcome),
1251 continuations: crate::utilities::ThreadBound::new(RefCell::new(Vec::new())),
1252 thread: std::thread::current().id(),
1253 }),
1254 }
1255 }
1256
1257 pub(crate) fn new() -> Self {
1258 Self::with_outcome(ServerJobOutcome::Running)
1259 }
1260
1261 pub fn from_result(result: T) -> Self {
1263 Self::with_outcome(ServerJobOutcome::RanToCompletion(Some(result)))
1264 }
1265
1266 pub fn from_exception(error: ServerJobError) -> Self {
1268 Self::with_outcome(ServerJobOutcome::Faulted(error))
1269 }
1270
1271 fn run_continuations(shared: &ServerJobTaskShared<T>) {
1272 let continuations = std::mem::take(&mut *shared.continuations.get().borrow_mut());
1273 for continuation in continuations {
1274 continuation();
1275 }
1276 }
1277
1278 fn complete(&self, outcome: ServerJobOutcome<T>) {
1279 {
1280 let mut current = self.shared.outcome.lock().unwrap();
1281 if !matches!(*current, ServerJobOutcome::Running) {
1282 return;
1283 }
1284 *current = outcome;
1285 }
1286
1287 if self.shared.continuations.is_on_thread() {
1288 Self::run_continuations(&self.shared);
1289 } else if let Some(dispatcher) = Dispatcher::from_thread(self.shared.thread) {
1290 let shared = self.shared.clone();
1293 dispatcher.post(move || Self::run_continuations(&shared), DispatcherPriority::SEND);
1294 }
1295 }
1296
1297 pub(crate) fn set_result(&self, result: T) {
1298 self.complete(ServerJobOutcome::RanToCompletion(Some(result)))
1299 }
1300
1301 pub(crate) fn set_exception(&self, error: ServerJobError) {
1302 self.complete(ServerJobOutcome::Faulted(error))
1303 }
1304
1305 pub fn is_completed(&self) -> bool {
1307 !matches!(*self.shared.outcome.lock().unwrap(), ServerJobOutcome::Running)
1308 }
1309
1310 pub fn is_completed_successfully(&self) -> bool {
1312 matches!(*self.shared.outcome.lock().unwrap(), ServerJobOutcome::RanToCompletion(_))
1313 }
1314
1315 pub fn is_faulted(&self) -> bool {
1317 matches!(*self.shared.outcome.lock().unwrap(), ServerJobOutcome::Faulted(_))
1318 }
1319
1320 pub fn exception(&self) -> Option<ServerJobError> {
1322 match &*self.shared.outcome.lock().unwrap() {
1323 ServerJobOutcome::Faulted(error) => Some(error.clone()),
1324 _ => None,
1325 }
1326 }
1327
1328 pub fn take_result(&self) -> Option<Result<T, ServerJobError>> {
1331 match &mut *self.shared.outcome.lock().unwrap() {
1332 ServerJobOutcome::Running => None,
1333 ServerJobOutcome::RanToCompletion(result) => result.take().map(Ok),
1334 ServerJobOutcome::Faulted(error) => Some(Err(error.clone())),
1335 }
1336 }
1337
1338 pub fn on_completed(&self, continuation: impl FnOnce() + 'static) {
1341 {
1342 let outcome = self.shared.outcome.lock().unwrap();
1345 if matches!(*outcome, ServerJobOutcome::Running) {
1346 self.shared.continuations.get().borrow_mut().push(Box::new(continuation));
1347 return;
1348 }
1349 }
1350 continuation();
1351 }
1352}
1353
1354pub type LocalFuture<T> = std::pin::Pin<Box<dyn std::future::Future<Output = T>>>;
1357
1358struct ServerJobResult<T> {
1362 task: ServerJobTask<T>,
1363 registered: bool,
1364}
1365
1366impl<T: Send + 'static> ServerJobResult<T> {
1367 fn new(task: ServerJobTask<T>) -> Self {
1368 Self { task, registered: false }
1369 }
1370}
1371
1372impl<T: Send + 'static> std::future::Future for ServerJobResult<T> {
1373 type Output = Option<T>;
1374
1375 fn poll(mut self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> std::task::Poll<Self::Output> {
1376 match self.task.take_result() {
1377 Some(Ok(result)) => return std::task::Poll::Ready(Some(result)),
1378 Some(Err(error)) => panic!("{error}"),
1379 None => {}
1380 }
1381 if !self.registered {
1382 self.registered = true;
1383 let waker = cx.waker().clone();
1384 self.task.on_completed(move || waker.wake());
1385 }
1386 std::task::Poll::Pending
1387 }
1388}