1use super::{
2 CompositorPools, IServerCompositionLight, IServerObject, IServerRenderResource, ReadbackIndices,
3 ServerCompositionTarget, ServerCompositionVisual, ServerCompositorAnimations, ServerObjectId,
4};
5use crate::platform::surfaces::IPlatformRenderSurface;
6use crate::platform::{IBitmapImpl, IPlatformGraphics, IRenderTarget, LtrbRect};
7use crate::rendering::composition::transport::{
8 BatchMarker, BatchObject, BatchStreamData, BatchStreamReader, CommittedBatch, CompositionBatch, ServerJob,
9};
10use crate::rendering::composition::CompositionOptions;
11use crate::rendering::PlatformRenderInterfaceContextManager;
12use crate::reactive::IDisposable;
13use crate::threading::Dispatcher;
14use crate::{Matrix, PixelSize, Size, Vector};
15use std::any::{Any, TypeId};
16use std::cell::{Cell, RefCell};
17use std::collections::{HashMap, HashSet, VecDeque};
18use std::rc::{Rc, Weak};
19use std::sync::{Arc, Mutex};
20use std::time::Duration;
21
22pub type CompositorClock = Rc<dyn Fn() -> Duration>;
27
28#[derive(Default)]
34pub struct BatchQueue {
35 batches: Mutex<VecDeque<CommittedBatch>>,
36 data_pool: Mutex<Vec<BatchStreamData>>,
38}
39
40impl BatchQueue {
41 pub(crate) fn enqueue(&self, batch: CommittedBatch) {
42 self.batches.lock().unwrap().push_back(batch);
43 }
44
45 fn dequeue(&self) -> Option<CommittedBatch> {
46 self.batches.lock().unwrap().pop_front()
47 }
48
49 pub(crate) fn rent_data(&self) -> BatchStreamData {
51 self.data_pool.lock().unwrap().pop().unwrap_or_default()
52 }
53
54 pub(crate) fn complete_all(&self) {
64 loop {
65 let Some(batch) = self.dequeue() else { break };
68 batch.batch.notify_processed();
69 batch.batch.notify_rendered();
70 }
71 }
72
73 fn return_data(&self, mut data: BatchStreamData) {
74 data.reset();
75 let mut pool = self.data_pool.lock().unwrap();
76 if pool.len() < 16 {
77 pool.push(data);
78 }
79 }
80}
81
82const _: fn() = || {
84 fn shared<T: Send + Sync>() {}
85 shared::<BatchQueue>();
86};
87
88const COMMIT_GRACE_TICKS: i32 = 10;
89
90type RenderThreadJob = Box<dyn FnOnce(&ServerCompositor)>;
94
95pub struct ServerCompositor {
102 this: Weak<ServerCompositor>,
103 batches: Arc<BatchQueue>,
104 objects: RefCell<Vec<Option<Rc<dyn IServerObject>>>>,
105 received_job_queue: RefCell<VecDeque<RenderThreadJob>>,
106 disposed_in_batch: RefCell<Vec<ServerObjectId>>,
107 received_post_target_job_queue: RefCell<VecDeque<RenderThreadJob>>,
108 last_batch_id: Cell<i64>,
109 clock: CompositorClock,
110 server_now: Cell<Duration>,
111 ui_thread_is_inside_render: Cell<bool>,
112 rendering_stopped: Cell<bool>,
114 render_interface: Rc<PlatformRenderInterfaceContextManager>,
115 options: CompositionOptions,
116 readback: Arc<ReadbackIndices>,
117 ticks_since_last_commit: Cell<i32>,
118 to_notify_processed: RefCell<Vec<Arc<CompositionBatch>>>,
119 to_notify_rendered: RefCell<Vec<Arc<CompositionBatch>>>,
120 render_resources_invalidation_queue: RefCell<VecDeque<Rc<dyn IServerRenderResource>>>,
121 render_resources_invalidation_set: RefCell<HashSet<*const ()>>,
122 visual_own_properties_recompute_pass: RefCell<VecDeque<Rc<ServerCompositionVisual>>>,
124 visual_readback_update_pass_queue: RefCell<VecDeque<Rc<ServerCompositionVisual>>>,
125 adorner_update_queue: RefCell<VecDeque<Rc<ServerCompositionVisual>>>,
126 visual_surfaces: RefCell<Vec<Weak<super::ServerCompositionVisualSurface>>>,
128 lights: RefCell<Vec<Weak<dyn IServerCompositionLight>>>,
131 active_targets: RefCell<Vec<Rc<ServerCompositionTarget>>>,
132 pools: CompositorPools,
133 animations: ServerCompositorAnimations,
134 render_interface_feature_cache: RefCell<Option<Rc<HashMap<TypeId, Rc<dyn Any>>>>>,
135 diagnostic_text_renderer: RefCell<Option<Rc<super::DiagnosticTextRenderer>>>,
139 _context_subscriptions: RefCell<Vec<Rc<dyn IDisposable>>>,
140}
141
142impl ServerCompositor {
143 pub(super) fn register_light(&self, light: Weak<dyn IServerCompositionLight>) {
144 let mut lights = self.lights.borrow_mut();
145 lights.retain(|light| light.strong_count() > 0);
146 lights.push(light);
147 }
148
149 pub(super) fn live_lights(&self) -> Vec<Rc<dyn IServerCompositionLight>> {
150 self.lights.borrow().iter().filter_map(Weak::upgrade).filter(|light| !light.is_disposed()).collect()
151 }
152
153 pub(crate) fn new(
157 platform_graphics: Option<Arc<dyn IPlatformGraphics>>,
158 options: CompositionOptions,
159 batches: Arc<BatchQueue>,
160 readback: Arc<ReadbackIndices>,
161 clock: CompositorClock,
162 ) -> Rc<ServerCompositor> {
163 let compositor = Rc::new_cyclic(|this| ServerCompositor {
164 this: this.clone(),
165 batches,
166 objects: RefCell::new(Vec::new()),
167 received_job_queue: RefCell::new(VecDeque::new()),
168 disposed_in_batch: RefCell::new(Vec::new()),
169 received_post_target_job_queue: RefCell::new(VecDeque::new()),
170 last_batch_id: Cell::new(0),
171 clock,
172 server_now: Cell::new(Duration::ZERO),
173 ui_thread_is_inside_render: Cell::new(false),
174 rendering_stopped: Cell::new(false),
175 render_interface: PlatformRenderInterfaceContextManager::new(platform_graphics),
176 options,
177 readback,
178 ticks_since_last_commit: Cell::new(0),
179 to_notify_processed: RefCell::new(Vec::new()),
180 to_notify_rendered: RefCell::new(Vec::new()),
181 render_resources_invalidation_queue: RefCell::new(VecDeque::new()),
182 render_resources_invalidation_set: RefCell::new(HashSet::new()),
183 visual_own_properties_recompute_pass: RefCell::new(VecDeque::new()),
184 visual_readback_update_pass_queue: RefCell::new(VecDeque::new()),
185 adorner_update_queue: RefCell::new(VecDeque::new()),
186 visual_surfaces: RefCell::new(Vec::new()),
187 lights: RefCell::new(Vec::new()),
188 active_targets: RefCell::new(Vec::new()),
189 pools: CompositorPools::default(),
190 animations: ServerCompositorAnimations::new(),
191 render_interface_feature_cache: RefCell::new(None),
192 diagnostic_text_renderer: RefCell::new(None),
193 _context_subscriptions: RefCell::new(Vec::new()),
194 });
195 let weak = Rc::downgrade(&compositor);
196 let disposed = compositor.render_interface.context_disposed(move || {
197 if let Some(compositor) = weak.upgrade() {
198 compositor.rt_on_context_disposed();
199 }
200 });
201 let weak = Rc::downgrade(&compositor);
202 let created = compositor.render_interface.context_created(move |context| {
203 if let Some(compositor) = weak.upgrade() {
204 compositor.rt_on_context_created(&**context);
205 }
206 });
207 *compositor._context_subscriptions.borrow_mut() = vec![disposed, created];
208 compositor
209 }
210
211 pub fn pools(&self) -> &CompositorPools {
213 &self.pools
214 }
215
216 pub fn animations(&self) -> &ServerCompositorAnimations {
218 &self.animations
219 }
220
221 pub fn last_batch_id(&self) -> i64 {
223 self.last_batch_id.get()
224 }
225
226 pub fn clock_elapsed(&self) -> Duration {
228 (self.clock)()
229 }
230
231 pub fn clock(&self) -> &CompositorClock {
233 &self.clock
234 }
235
236 pub fn server_now(&self) -> Duration {
238 self.server_now.get()
239 }
240
241 pub fn render_interface(&self) -> &Rc<PlatformRenderInterfaceContextManager> {
242 &self.render_interface
243 }
244
245 pub fn diagnostic_text_renderer(&self) -> Option<Rc<super::DiagnosticTextRenderer>> {
253 if self.diagnostic_text_renderer.borrow().is_none() {
254 {
257 use crate::LocatorExtensions as _;
258 crate::FerroLocator::current().get_service::<dyn crate::platform::IFontManagerImpl>()?;
259 }
260 let renderer = Rc::new(super::DiagnosticTextRenderer::create_default());
261 for label in super::FpsCounter::LABELS {
265 renderer.shape_label(label);
266 }
267 *self.diagnostic_text_renderer.borrow_mut() = Some(renderer);
268 }
269 self.diagnostic_text_renderer.borrow().clone()
270 }
271
272 pub fn options(&self) -> &CompositionOptions {
273 &self.options
274 }
275
276 pub fn readback(&self) -> &Arc<ReadbackIndices> {
278 &self.readback
279 }
280
281 pub(crate) fn update_server_time(&self) {
282 self.server_now.set(self.clock_elapsed());
283 }
284
285 pub fn get_object(&self, id: ServerObjectId) -> Option<Rc<dyn IServerObject>> {
289 self.objects.borrow().get(id.index()).and_then(Clone::clone)
290 }
291
292 pub fn get<T: IServerObject>(&self, id: ServerObjectId) -> Option<Rc<T>> {
294 self.get_object(id).and_then(|object| object.into_any_rc().downcast::<T>().ok())
295 }
296
297 pub fn get_animated_object(&self, id: ServerObjectId) -> Option<Rc<dyn super::IAnimatedServerObject>> {
299 self.get_object(id).and_then(|object| object.as_animated())
300 }
301
302 pub fn object_count(&self) -> usize {
304 self.objects.borrow().iter().filter(|o| o.is_some()).count()
305 }
306
307 fn insert_object(&self, id: ServerObjectId, object: Rc<dyn IServerObject>) {
308 let mut objects = self.objects.borrow_mut();
309 if objects.len() <= id.index() {
310 objects.resize_with(id.index() + 1, || None);
311 }
312 debug_assert!(objects[id.index()].is_none(), "a server object id was reused while still alive");
313 objects[id.index()] = Some(object);
314 }
315
316 fn remove_object(&self, id: ServerObjectId) -> Option<Rc<dyn IServerObject>> {
317 self.objects.borrow_mut().get_mut(id.index()).and_then(Option::take)
318 }
319
320 fn apply_pending_batches(&self) {
323 let mut had_batches = false;
324 while let Some(mut batch) = self.batches.dequeue() {
325 {
326 let mut stream = BatchStreamReader::new(&mut batch.changes);
327 while !stream.is_object_eof() {
328 match stream.read_object() {
329 BatchObject::Marker(BatchMarker::CreateStart) => self.read_create_jobs(&mut stream),
330 BatchObject::Marker(BatchMarker::RenderThreadJobsStart) => self.read_server_jobs(
331 &mut stream,
332 &self.received_job_queue,
333 BatchMarker::RenderThreadJobsEnd,
334 ),
335 BatchObject::Marker(BatchMarker::RenderThreadPostTargetJobsStart) => self.read_server_jobs(
336 &mut stream,
337 &self.received_post_target_job_queue,
338 BatchMarker::RenderThreadPostTargetJobsEnd,
339 ),
340 BatchObject::Marker(BatchMarker::RenderThreadDisposeStart) => {
341 self.read_dispose_jobs(&mut stream)
342 }
343 BatchObject::ServerObject(id) => {
344 let Some(target) = self.get_object(id) else {
345 panic!("a batch refers to server object {id:?}, which does not exist");
346 };
347 target.deserialize_changes(&mut stream, batch.committed_at);
348 if cfg!(debug_assertions) {
349 match stream.read_object() {
353 BatchObject::Marker(BatchMarker::ObjectEnd) => {}
354 _ => panic!("server object {id:?} failed to deserialize properly on the object stream"),
355 }
356 if stream.read::<u64>() != OBJECT_END_MAGIC {
357 panic!("server object {id:?} failed to deserialize properly on the data stream");
358 }
359 }
360 }
361 _ => panic!("unexpected item at the top level of a batch object stream"),
362 }
363 }
364 }
365 for id in std::mem::take(&mut *self.disposed_in_batch.borrow_mut()) {
369 self.remove_object(id);
370 }
371 self.batches.return_data(std::mem::take(&mut batch.changes));
372 self.last_batch_id.set(batch.batch.sequence_id());
373 self.to_notify_processed.borrow_mut().push(batch.batch);
374 had_batches = true;
375 }
376
377 if had_batches {
378 self.ticks_since_last_commit.set(0);
379 } else if self.ticks_since_last_commit.get() < i32::MAX {
380 self.ticks_since_last_commit.set(self.ticks_since_last_commit.get() + 1);
381 }
382 }
383
384 fn read_create_jobs(&self, reader: &mut BatchStreamReader<'_>) {
385 let Some(this) = self.this.upgrade() else { return };
386 let mut count = reader.read::<i32>();
387 while count > 0 {
388 let Some(id) = reader.read_server_object() else {
389 panic!("a batch creates a server object without an id");
390 };
391 let BatchObject::Create(factory) = reader.read_object() else {
392 panic!("a batch creates a server object without a factory");
393 };
394 self.insert_object(id, factory(&this, id));
395 count -= 1;
396 }
397 }
398
399 fn read_server_jobs(&self, reader: &mut BatchStreamReader<'_>, queue: &RefCell<VecDeque<RenderThreadJob>>, end: BatchMarker) {
400 loop {
401 match reader.read_object() {
402 BatchObject::Marker(marker) if marker == end => break,
403 BatchObject::Job(job) => queue.borrow_mut().push_back(job),
404 BatchObject::ServerObject(id) => {
405 let target = self.get_object(id);
406 match reader.read_object() {
407 BatchObject::ObjectJob(job) => {
408 queue.borrow_mut().push_back(Box::new(move |compositor| job(compositor, target)))
409 }
410 _ => panic!("a server object in the job list of a batch is not followed by its job"),
411 }
412 }
413 _ => panic!("unexpected item in the job list of a batch"),
414 }
415 }
416 }
417
418 fn read_dispose_jobs(&self, reader: &mut BatchStreamReader<'_>) {
419 let mut count = reader.read::<i32>();
420 while count > 0 {
421 let id = reader.read_server_object();
422 let dispose = reader.read::<bool>();
423 let release = reader.read::<bool>();
424 if let Some(id) = id {
425 if let Some(object) = self.get_object(id) {
426 if dispose {
427 object.dispose();
428 }
429 if release {
430 self.disposed_in_batch.borrow_mut().push(id);
431 }
432 }
433 }
434 count -= 1;
435 }
436 }
437
438 fn execute_server_jobs(&self, queue: &RefCell<VecDeque<RenderThreadJob>>) {
439 loop {
440 let Some(job) = queue.borrow_mut().pop_front() else { break };
441 job(self);
442 }
443 }
444
445 fn notify_batches_processed(&self) {
446 let processed = std::mem::take(&mut *self.to_notify_processed.borrow_mut());
447 for batch in &processed {
448 batch.notify_processed();
449 }
450 self.to_notify_rendered.borrow_mut().extend(processed);
451 }
452
453 fn notify_batches_rendered(&self) {
454 let rendered = std::mem::take(&mut *self.to_notify_rendered.borrow_mut());
455 for batch in &rendered {
456 batch.notify_rendered();
457 }
458 }
459
460 fn apply_enqueued_render_resource_changes_pass(&self) {
463 loop {
464 let Some(resource) = self.render_resources_invalidation_queue.borrow_mut().pop_front() else { break };
465 resource.queued_invalidate();
468 }
469 self.render_resources_invalidation_set.borrow_mut().clear();
470 }
471
472 pub fn enqueue_render_resource_for_invalidation(&self, resource: Rc<dyn IServerRenderResource>) {
475 let key = Rc::as_ptr(&resource) as *const ();
476 if self.render_resources_invalidation_set.borrow_mut().insert(key) {
477 self.render_resources_invalidation_queue.borrow_mut().push_back(resource);
478 }
479 }
480
481 fn visual_own_properties_update_pass(&self) {
482 loop {
483 let Some(visual) = self.visual_own_properties_recompute_pass.borrow_mut().pop_front() else { break };
484 visual.recompute_own_properties();
485 }
486 }
487
488 pub fn enqueue_visual_for_own_properties_update_pass(&self, visual: Rc<ServerCompositionVisual>) {
489 self.visual_own_properties_recompute_pass.borrow_mut().push_back(visual);
490 }
491
492 fn visual_readback_update_pass(&self) {
493 if self.visual_readback_update_pass_queue.borrow().is_empty() {
494 return;
495 }
496
497 let scope = self.readback.begin_write();
500 let read = self.readback.read_revision();
501 let write = self.readback.write_revision();
502 loop {
503 let Some(visual) = self.visual_readback_update_pass_queue.borrow_mut().pop_front() else { break };
504 visual.update_readback(write, read);
505 }
506 drop(scope);
507 }
508
509 pub fn enqueue_visual_for_readback_update_pass(&self, visual: Rc<ServerCompositionVisual>) {
510 self.visual_readback_update_pass_queue.borrow_mut().push_back(visual);
511 }
512
513 pub fn enqueue_adorner_update(&self, visual: Rc<ServerCompositionVisual>) {
514 self.adorner_update_queue.borrow_mut().push_back(visual);
515 }
516
517 fn adorner_update_pass(&self) {
518 loop {
519 let Some(adorner) = self.adorner_update_queue.borrow_mut().pop_front() else { break };
520 adorner.update_adorner();
521 }
522 }
523
524 pub fn register_visual_surface(&self, surface: Weak<super::ServerCompositionVisualSurface>) {
526 self.visual_surfaces.borrow_mut().push(surface);
527 }
528
529 fn visual_surface_update_pass(&self) {
532 if self.visual_surfaces.borrow().is_empty() {
533 return;
534 }
535 if super::ServerCompositionVisualSurface::update_live_surfaces(self, &self.visual_surfaces) {
536 self.apply_enqueued_render_resource_changes_pass();
537 self.visual_own_properties_update_pass();
538 }
539 }
540
541 pub fn add_composition_target(&self, target: &Rc<ServerCompositionTarget>) {
544 self.active_targets.borrow_mut().push(target.clone());
545 }
546
547 pub fn remove_composition_target(&self, target: &Rc<ServerCompositionTarget>) {
548 let mut targets = self.active_targets.borrow_mut();
549 if let Some(index) = targets.iter().position(|t| Rc::ptr_eq(t, target)) {
550 targets.remove(index);
551 }
552 }
553
554 pub fn create_render_target(&self, surfaces: &[std::sync::Arc<dyn IPlatformRenderSurface>]) -> Rc<dyn IRenderTarget> {
555 let current = self.render_interface.ensure_current();
556 let target = self.render_interface.create_render_target(surfaces);
557 current.dispose();
558 target
559 }
560
561 pub fn is_ready_to_create_render_target(&self, surfaces: &[std::sync::Arc<dyn IPlatformRenderSurface>]) -> bool {
562 self.render_interface.is_ready_to_create_render_target(surfaces)
563 }
564
565 fn rt_on_context_created(&self, context: &dyn crate::platform::IPlatformRenderInterfaceContext) {
568 *self.render_interface_feature_cache.borrow_mut() = Some(Rc::new(context.public_features()));
569 }
570
571 fn rt_on_context_disposed(&self) {
572 *self.render_interface_feature_cache.borrow_mut() = None;
573 }
574
575 pub fn at_try_get_cached_render_interface_features(&self) -> Option<Rc<HashMap<TypeId, Rc<dyn Any>>>> {
578 self.render_interface_feature_cache.borrow().clone()
579 }
580
581 pub fn rt_get_render_interface_features(&self) -> Rc<HashMap<TypeId, Rc<dyn Any>>> {
584 if let Some(features) = self.render_interface_feature_cache.borrow().clone() {
585 return features;
586 }
587 let features = Rc::new(self.render_interface.value().public_features());
588 *self.render_interface_feature_cache.borrow_mut() = Some(features.clone());
589 features
590 }
591
592 pub fn create_composition_visual_snapshot(
595 &self,
596 visual: &Rc<ServerCompositionVisual>,
597 scaling: f64,
598 render_children: bool,
599 ) -> std::sync::Arc<crate::platform::SharedBitmapImpl> {
600 let current = self.render_interface.ensure_current();
601 let size = visual.size();
602 let pixel_size = PixelSize::from_size(Size::new(size.x, size.y), scaling);
603 let scale_transform = Matrix::create_scale(scaling, scaling);
604
605 let target = self.render_interface.value().create_offscreen_render_target(
606 pixel_size,
607 Vector::new(scaling, scaling),
608 true,
609 );
610 {
611 let mut canvas = target.create_drawing_context();
612 canvas.set_transform(scale_transform);
613 visual.render(&mut *canvas, LtrbRect::INFINITE, None, render_children, false, false);
614 canvas.dispose();
615 }
616
617 let result: std::sync::Arc<crate::platform::SharedBitmapImpl> = match target.as_layer_with_render_context_affinity() {
618 Some(affined) if affined.has_render_context_affinity() => {
619 let snapshot = affined.create_non_affined_snapshot();
620 target.dispose();
621 snapshot
622 }
623 _ => {
626 let snapshot = target.create_shared_snapshot();
627 target.dispose();
628 snapshot
629 }
630 };
631 current.dispose();
632 result
633 }
634
635 pub fn reset_all_gpu_resources(&self) {
636 let targets = self.active_targets.borrow().clone();
637 for target in targets {
638 target.reset_render_target();
639 }
640 self.render_interface.reset();
641 }
642
643 pub fn release_gpu_resources(&self) {
667 if self.render_interface.existing_backend_context().is_none() {
668 return;
669 }
670 let gpu_context = self.render_interface.gpu_context();
671 if gpu_context.as_ref().is_some_and(|context| context.is_lost()) {
672 return;
673 }
674 let current = gpu_context.map(|context| context.ensure_current());
675 let targets = self.active_targets.borrow().clone();
676 for target in targets {
677 target.release_render_target();
678 }
679 self.render_interface.reset();
680 if let Some(current) = current {
681 current.dispose();
682 }
683 }
684
685 pub fn stop_rendering(&self) {
697 self.rendering_stopped.set(true);
698 }
699
700 pub fn is_rendering_stopped(&self) -> bool {
702 self.rendering_stopped.get()
703 }
704
705 pub fn invalidate_all_composition_targets(&self) {
706 let targets = self.active_targets.borrow().clone();
707 for target in targets {
708 target.request_full_redraw();
709 }
710 }
711
712 pub fn check_access(&self) -> bool {
718 self.ui_thread_is_inside_render.get()
719 }
720
721 pub fn verify_access(&self) {
724 if !self.check_access() {
725 panic!("This object can be only accessed under compositor lock");
726 }
727 }
728
729 pub fn verify_access_of(compositor: Option<&Rc<ServerCompositor>>) -> Rc<ServerCompositor> {
733 match compositor {
734 Some(compositor) => {
735 compositor.verify_access();
736 compositor.clone()
737 }
738 None => panic!("This object can be only accessed under compositor lock"),
739 }
740 }
741
742 pub fn render(&self) -> bool {
748 if self.ui_thread_is_inside_render.replace(true) {
749 panic!("Reentrancy is not supported");
750 }
751 let _reset = ResetFlag(&self.ui_thread_is_inside_render);
752
753 let dispatcher = Dispatcher::ui_thread();
757 let processing_disabled = dispatcher.check_access().then(|| dispatcher.disable_processing());
758 let _notify = NotifyRendered(self);
759 let _enable = EnableProcessing(processing_disabled);
762 self.render_core()
763 }
764
765 fn execute_global_passes(&self) -> Duration {
766 let compositor_global_passes_started = self.clock_elapsed();
767 self.apply_pending_batches();
768 self.notify_batches_processed();
769 self.animations.process();
770 self.apply_enqueued_render_resource_changes_pass();
771 self.visual_own_properties_update_pass();
772 if super::invalidate_lights_with_moved_spaces(self) {
776 self.visual_own_properties_update_pass();
777 }
778 self.adorner_update_pass();
781 self.visual_surface_update_pass();
784 self.clock_elapsed().saturating_sub(compositor_global_passes_started)
785 }
786
787 fn render_core(&self) -> bool {
788 self.update_server_time();
789 let compositor_global_passes_elapsed = self.execute_global_passes();
790
791 if self.rendering_stopped.get() {
792 let jobs = std::mem::take(&mut *self.received_job_queue.borrow_mut());
794 let post_target_jobs = std::mem::take(&mut *self.received_post_target_job_queue.borrow_mut());
795 drop((jobs, post_target_jobs));
796 return false;
797 }
798
799 if !self.render_interface.is_ready() {
800 return true;
801 }
802 self.render_interface.ensure_valid_backend_context();
803 self.execute_server_jobs(&self.received_job_queue);
804
805 let targets = self.active_targets.borrow().clone();
806 for target in &targets {
807 target.update(compositor_global_passes_elapsed);
808 target.render();
809 }
810
811 self.visual_readback_update_pass();
812
813 self.execute_server_jobs(&self.received_post_target_job_queue);
814
815 if self.animations.need_next_tick() || self.ticks_since_last_commit.get() < COMMIT_GRACE_TICKS {
817 return true;
818 }
819
820 let targets = self.active_targets.borrow().clone();
823 for target in &targets {
824 if target.is_waiting_for_ready_render_target() && !target.is_waiting_for_render_loop_wakeup() {
825 return true;
826 }
827 }
828
829 false
831 }
832}
833
834pub(crate) const OBJECT_END_MAGIC: u64 = 0x4F42_4A45_4E44_2121;
837
838struct ResetFlag<'a>(&'a Cell<bool>);
839
840impl Drop for ResetFlag<'_> {
841 fn drop(&mut self) {
842 self.0.set(false);
843 }
844}
845
846struct NotifyRendered<'a>(&'a ServerCompositor);
847
848impl Drop for NotifyRendered<'_> {
849 fn drop(&mut self) {
850 self.0.notify_batches_rendered();
851 }
852}
853
854struct EnableProcessing(Option<crate::threading::DispatcherProcessingDisabled>);
857
858impl Drop for EnableProcessing {
859 fn drop(&mut self) {
860 if let Some(disabled) = &self.0 {
861 crate::reactive::IDisposable::dispose(disabled);
862 }
863 }
864}