@@ -40,49 +40,25 @@ - (void)display
4040
4141@end
4242
43- /*
44- * The windows that can commit a Core Animation transaction: the visible ones
45- * of every foreground scene.
46- */
47- static NSArray <UIWindow *> *RCTFlushableWindows (void )
48- {
49- NSMutableArray <UIWindow *> *windows = [NSMutableArray new ];
50- for (UIScene *scene in RCTSharedApplication ().connectedScenes ) {
51- if (![scene isKindOfClass: [UIWindowScene class ]]) {
52- continue ;
53- }
54- if (scene.activationState != UISceneActivationStateForegroundActive &&
55- scene.activationState != UISceneActivationStateForegroundInactive) {
56- continue ;
57- }
58- for (UIWindow *window in ((UIWindowScene *)scene).windows ) {
59- if (!window.hidden ) {
60- [windows addObject: window];
61- }
62- }
63- }
64- if (windows.count == 0 ) {
65- // Apps on the legacy UIApplicationDelegate lifecycle own their window
66- // outside of any scene, so the enumeration above finds nothing.
67- UIWindow *keyWindow = RCTKeyWindow ();
68- if (keyWindow != nil ) {
69- [windows addObject: keyWindow];
70- }
71- }
72- return windows;
73- }
74-
7543namespace facebook ::react {
7644
7745/*
78- * Owns the flusher layers and keeps one attached to every window's layer so
79- * that whichever layer tree is being committed contains one of them.
46+ * Owns the flusher layers, one per surface that has made a synchronous
47+ * request, attached to the layer of that surface's root view. A request made
48+ * from layout runs inside the commit of exactly that tree, so its layer is
49+ * guaranteed a display phase in the current cycle — no assumption about
50+ * which window is key or about all windows committing together.
8051 */
8152class AppleEventBeat ::DisplayPhaseFlusher {
8253 public:
83- DisplayPhaseFlusher (std::function<void ()> callback, std::weak_ptr<const void > weakOwner)
54+ DisplayPhaseFlusher (
55+ std::function<void ()> callback,
56+ std::weak_ptr<const void > weakOwner,
57+ SurfaceLayerResolver surfaceLayerResolver)
58+ : surfaceLayerResolver_(std::move(surfaceLayerResolver))
8459 {
85- // Weak keys: a window that goes away takes its own layer with it.
60+ // Weak keys: a root view that goes away takes its own flusher layer with
61+ // it.
8662 layers_ = [NSMapTable weakToStrongObjectsMapTable ];
8763 auto sharedCallback = std::make_shared<std::function<void ()>>(std::move (callback));
8864 onDisplay_ = ^{
@@ -101,7 +77,7 @@ - (void)display
10177 // The beat can be destroyed on any thread; layer mutations belong on the
10278 // main thread. The block only retains the layers, and a display happening
10379 // before this executes is made safe by the owner check above.
104- NSMapTable <UIWindow *, RCTEventBeatFlusherLayer *> *layers = layers_;
80+ NSMapTable <CALayer *, RCTEventBeatFlusherLayer *> *layers = layers_;
10581 RCTExecuteOnMainQueue (^{
10682 for (RCTEventBeatFlusherLayer *layer in layers.objectEnumerator ) {
10783 layer.onDisplay = nil ;
@@ -113,52 +89,57 @@ - (void)display
11389
11490 /*
11591 * Schedules the callback to run in the display phase of the current (or
116- * next) Core Animation commit cycle. Main thread only.
117- *
118- * Every window gets a layer rather than only the key window: the request can
119- * come from any of them — a modal and the LogBox are windows of their own —
120- * and only a layer in a tree that is committed is displayed in this cycle.
121- * The induce the display triggers is coalescing, so the extra layers cost a
122- * dirty zero-sized layer each, not extra beats.
92+ * next) Core Animation commit cycle, on the layer tree of the surface's
93+ * root view. Main thread only. Does nothing when the surface has no mounted
94+ * view; the run loop observer then processes the request on its ordinary
95+ * schedule instead.
12396 */
124- void schedule () const
97+ void schedule (SurfaceId surfaceId ) const
12598 {
126- for (UIWindow *window in RCTFlushableWindows ()) {
127- RCTEventBeatFlusherLayer *layer = [layers_ objectForKey: window];
128- if (layer == nil ) {
129- layer = [RCTEventBeatFlusherLayer new ];
130- layer.frame = CGRectZero;
131- layer.onDisplay = onDisplay_;
132- [layers_ setObject: layer forKey: window];
133- }
134- if (layer.superlayer != window.layer ) {
135- [window.layer addSublayer: layer];
136- }
137- [layer setNeedsDisplay ];
99+ CALayer *hostLayer = surfaceLayerResolver_ ? surfaceLayerResolver_ (surfaceId) : nil ;
100+ if (hostLayer == nil ) {
101+ return ;
102+ }
103+ RCTEventBeatFlusherLayer *layer = [layers_ objectForKey: hostLayer];
104+ if (layer == nil ) {
105+ layer = [RCTEventBeatFlusherLayer new ];
106+ layer.frame = CGRectZero;
107+ layer.onDisplay = onDisplay_;
108+ [layers_ setObject: layer forKey: hostLayer];
109+ }
110+ if (layer.superlayer != hostLayer) {
111+ [layer removeFromSuperlayer ];
112+ [hostLayer addSublayer: layer];
138113 }
114+ [layer setNeedsDisplay ];
139115 }
140116
141117 private:
142- NSMapTable <UIWindow *, RCTEventBeatFlusherLayer *> *layers_;
118+ SurfaceLayerResolver surfaceLayerResolver_;
119+ NSMapTable <CALayer *, RCTEventBeatFlusherLayer *> *layers_;
143120 void (^onDisplay_)(void );
144121};
145122
146123AppleEventBeat::AppleEventBeat (std::shared_ptr<OwnerBox> ownerBox,
147124 std::unique_ptr<const RunLoopObserver> uiRunLoopObserver,
148- RuntimeScheduler &runtimeScheduler)
125+ RuntimeScheduler &runtimeScheduler,
126+ SurfaceLayerResolver surfaceLayerResolver)
149127 : EventBeat(std::move(ownerBox), runtimeScheduler),
150128 uiRunLoopObserver_ (std::move(uiRunLoopObserver)),
151- displayPhaseFlusher_(std::make_unique<DisplayPhaseFlusher>([this]() { induce (); }, ownerBox_->owner))
129+ displayPhaseFlusher_(std::make_unique<DisplayPhaseFlusher>(
130+ [this]() { induce (); },
131+ ownerBox_->owner,
132+ std::move (surfaceLayerResolver)))
152133{
153134 uiRunLoopObserver_->setDelegate (this );
154135 uiRunLoopObserver_->enable ();
155136}
156137
157138AppleEventBeat::~AppleEventBeat () = default ;
158139
159- void AppleEventBeat::requestSynchronous () const
140+ void AppleEventBeat::requestSynchronous (std::optional<SurfaceId> surfaceId ) const
160141{
161- EventBeat::requestSynchronous ();
142+ EventBeat::requestSynchronous (surfaceId );
162143
163144 // The run loop observer that ordinarily induces the beat runs before Core
164145 // Animation commits the frame. A synchronous request made while Core
@@ -167,8 +148,8 @@ void schedule() const
167148 // Scheduling an induce in the display phase of the current commit cycle
168149 // processes it before this frame is presented. Multiple requests within one
169150 // cycle coalesce into a single induce.
170- if (RCTIsMainQueue ()) {
171- displayPhaseFlusher_->schedule ();
151+ if (surfaceId. has_value () && RCTIsMainQueue ()) {
152+ displayPhaseFlusher_->schedule (*surfaceId );
172153 }
173154}
174155
0 commit comments