3
3
using ReactNative . Tracing ;
4
4
using System ;
5
5
using System . Collections . Generic ;
6
+ using System . Diagnostics ;
7
+ using System . Linq ;
6
8
using Windows . UI . Xaml . Media ;
7
9
8
10
namespace ReactNative . UIManager
@@ -17,15 +19,25 @@ namespace ReactNative.UIManager
17
19
/// </summary>
18
20
public class UIViewOperationQueue
19
21
{
22
+ private const long TicksPerFrame = 166666 ;
23
+ private const long MaxNonBatchedTicksPerFrame = 83333 ;
24
+
25
+ private static Stopwatch s_stopwatch = Stopwatch . StartNew ( ) ;
26
+
20
27
private readonly object _gate = new object ( ) ;
28
+ private readonly object _nonBatchedGate = new object ( ) ;
21
29
private readonly double [ ] _measureBuffer = new double [ 4 ] ;
22
30
23
31
private readonly NativeViewHierarchyManager _nativeViewHierarchyManager ;
24
32
private readonly ReactContext _reactContext ;
25
33
34
+ private readonly IList < Action > _nonBatchedOperations = new List < Action > ( ) ;
35
+
26
36
private IList < Action > _operations = new List < Action > ( ) ;
27
37
private IList < Action > _batches = new List < Action > ( ) ;
28
38
39
+ private long _lastRenderingTicks = - 1 ;
40
+
29
41
/// <summary>
30
42
/// Instantiates the <see cref="UIViewOperationQueue"/>.
31
43
/// </summary>
@@ -161,11 +173,14 @@ public void EnqueueCreateView(
161
173
string viewClassName ,
162
174
ReactStylesDiffMap initialProps )
163
175
{
164
- EnqueueOperation ( ( ) => _nativeViewHierarchyManager . CreateView (
165
- themedContext ,
166
- viewReactTag ,
167
- viewClassName ,
168
- initialProps ) ) ;
176
+ lock ( _nonBatchedGate )
177
+ {
178
+ _nonBatchedOperations . Add ( ( ) => _nativeViewHierarchyManager . CreateView (
179
+ themedContext ,
180
+ viewReactTag ,
181
+ viewClassName ,
182
+ initialProps ) ) ;
183
+ }
169
184
}
170
185
171
186
/// <summary>
@@ -380,20 +395,42 @@ public void OnShutdown()
380
395
/// <param name="batchId">The batch identifier.</param>
381
396
internal void DispatchViewUpdates ( int batchId )
382
397
{
383
- lock ( _gate )
398
+ var operations = _operations . Count == 0 ? null : _operations ;
399
+ if ( operations != null )
384
400
{
385
- var operations = _operations . Count == 0 ? null : _operations ;
386
- if ( operations != null )
401
+ _operations = new List < Action > ( ) ;
402
+ }
403
+
404
+ var nonBatchedOperations = default ( Action [ ] ) ;
405
+ lock ( _nonBatchedGate )
406
+ {
407
+ if ( _nonBatchedOperations . Count > 0 )
408
+ {
409
+ nonBatchedOperations = _nonBatchedOperations . ToArray ( ) ;
410
+ _nonBatchedOperations . Clear ( ) ;
411
+ }
412
+ else
387
413
{
388
- _operations = new List < Action > ( ) ;
414
+ nonBatchedOperations = null ;
389
415
}
416
+ }
390
417
418
+ lock ( _gate )
419
+ {
391
420
_batches . Add ( ( ) =>
392
421
{
393
422
using ( Tracer . Trace ( Tracer . TRACE_TAG_REACT_BRIDGE , "DispatchUI" )
394
423
. With ( "BatchId" , batchId )
395
424
. Start ( ) )
396
425
{
426
+ if ( nonBatchedOperations != null )
427
+ {
428
+ foreach ( var operation in nonBatchedOperations )
429
+ {
430
+ operation ( ) ;
431
+ }
432
+ }
433
+
397
434
if ( operations != null )
398
435
{
399
436
foreach ( var operation in operations )
@@ -418,6 +455,15 @@ private void EnqueueOperation(Action action)
418
455
419
456
private void OnRendering ( object sender , object e )
420
457
{
458
+ var renderingArgs = e as RenderingEventArgs ;
459
+ if ( renderingArgs != null )
460
+ {
461
+ using ( Tracer . Trace ( Tracer . TRACE_TAG_REACT_BRIDGE , "dispatchNonBatchedUIOperations" ) . Start ( ) )
462
+ {
463
+ DispatchPendingNonBatchedOperations ( renderingArgs . RenderingTime ) ;
464
+ }
465
+ }
466
+
421
467
lock ( _gate )
422
468
{
423
469
foreach ( var batch in _batches )
@@ -428,5 +474,43 @@ private void OnRendering(object sender, object e)
428
474
_batches . Clear ( ) ;
429
475
}
430
476
}
477
+
478
+ private void DispatchPendingNonBatchedOperations ( TimeSpan renderingTime )
479
+ {
480
+ if ( _lastRenderingTicks < 0 )
481
+ {
482
+ _lastRenderingTicks = renderingTime . Ticks ;
483
+ }
484
+
485
+ var ticksSinceLastFrame = renderingTime . Ticks - _lastRenderingTicks ;
486
+ _lastRenderingTicks = renderingTime . Ticks ;
487
+ var lastFrameTicksOverage = Math . Max ( 0 , ticksSinceLastFrame - TicksPerFrame ) ;
488
+ var allowedTicks = MaxNonBatchedTicksPerFrame - lastFrameTicksOverage ;
489
+
490
+ var frameStartTicks = s_stopwatch . ElapsedTicks ;
491
+ while ( true )
492
+ {
493
+ // Use up to `MaxNonBatchedTicksPerFrame` minus the delay in the last frame
494
+ var elapsedTicks = s_stopwatch . ElapsedTicks - frameStartTicks ;
495
+ if ( elapsedTicks > allowedTicks )
496
+ {
497
+ break ;
498
+ }
499
+
500
+ var nextOperation = default ( Action ) ;
501
+ lock ( _nonBatchedGate )
502
+ {
503
+ if ( _nonBatchedOperations . Count == 0 )
504
+ {
505
+ break ;
506
+ }
507
+
508
+ nextOperation = _nonBatchedOperations [ 0 ] ;
509
+ _nonBatchedOperations . RemoveAt ( 0 ) ;
510
+ }
511
+
512
+ nextOperation ( ) ;
513
+ }
514
+ }
431
515
}
432
516
}
0 commit comments