Skip to content

Commit 059c222

Browse files
authored
feat(UIViewOperationQueue): Eagerly invoke non-batched createView operations (microsoft#588)
Create view operations are not intended to have side-effects, so they can be evaluated eagerly as the come in, saving precious milliseconds when evaluating the batch. Fixes microsoft#531
1 parent 635306b commit 059c222

File tree

1 file changed

+93
-9
lines changed

1 file changed

+93
-9
lines changed

ReactWindows/ReactNative/UIManager/UIViewOperationQueue.cs

Lines changed: 93 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
using ReactNative.Tracing;
44
using System;
55
using System.Collections.Generic;
6+
using System.Diagnostics;
7+
using System.Linq;
68
using Windows.UI.Xaml.Media;
79

810
namespace ReactNative.UIManager
@@ -17,15 +19,25 @@ namespace ReactNative.UIManager
1719
/// </summary>
1820
public class UIViewOperationQueue
1921
{
22+
private const long TicksPerFrame = 166666;
23+
private const long MaxNonBatchedTicksPerFrame = 83333;
24+
25+
private static Stopwatch s_stopwatch = Stopwatch.StartNew();
26+
2027
private readonly object _gate = new object();
28+
private readonly object _nonBatchedGate = new object();
2129
private readonly double[] _measureBuffer = new double[4];
2230

2331
private readonly NativeViewHierarchyManager _nativeViewHierarchyManager;
2432
private readonly ReactContext _reactContext;
2533

34+
private readonly IList<Action> _nonBatchedOperations = new List<Action>();
35+
2636
private IList<Action> _operations = new List<Action>();
2737
private IList<Action> _batches = new List<Action>();
2838

39+
private long _lastRenderingTicks = -1;
40+
2941
/// <summary>
3042
/// Instantiates the <see cref="UIViewOperationQueue"/>.
3143
/// </summary>
@@ -161,11 +173,14 @@ public void EnqueueCreateView(
161173
string viewClassName,
162174
ReactStylesDiffMap initialProps)
163175
{
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+
}
169184
}
170185

171186
/// <summary>
@@ -380,20 +395,42 @@ public void OnShutdown()
380395
/// <param name="batchId">The batch identifier.</param>
381396
internal void DispatchViewUpdates(int batchId)
382397
{
383-
lock (_gate)
398+
var operations = _operations.Count == 0 ? null : _operations;
399+
if (operations != null)
384400
{
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
387413
{
388-
_operations = new List<Action>();
414+
nonBatchedOperations = null;
389415
}
416+
}
390417

418+
lock (_gate)
419+
{
391420
_batches.Add(() =>
392421
{
393422
using (Tracer.Trace(Tracer.TRACE_TAG_REACT_BRIDGE, "DispatchUI")
394423
.With("BatchId", batchId)
395424
.Start())
396425
{
426+
if (nonBatchedOperations != null)
427+
{
428+
foreach (var operation in nonBatchedOperations)
429+
{
430+
operation();
431+
}
432+
}
433+
397434
if (operations != null)
398435
{
399436
foreach (var operation in operations)
@@ -418,6 +455,15 @@ private void EnqueueOperation(Action action)
418455

419456
private void OnRendering(object sender, object e)
420457
{
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+
421467
lock (_gate)
422468
{
423469
foreach (var batch in _batches)
@@ -428,5 +474,43 @@ private void OnRendering(object sender, object e)
428474
_batches.Clear();
429475
}
430476
}
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+
}
431515
}
432516
}

0 commit comments

Comments
 (0)