Commit ab01c7bf authored by Hixie's avatar Hixie

Less tree walking for compositing bits updates.

Use the same technique for updating compositing bits as layout and
painting. This avoids walking the entire rendering tree when all you
need to update is a small subtree.
parent f107522f
...@@ -69,7 +69,7 @@ abstract class Renderer extends Scheduler ...@@ -69,7 +69,7 @@ abstract class Renderer extends Scheduler
void beginFrame() { void beginFrame() {
assert(renderView != null); assert(renderView != null);
RenderObject.flushLayout(); RenderObject.flushLayout();
renderView.updateCompositingBits(); RenderObject.flushCompositingBits();
RenderObject.flushPaint(); RenderObject.flushPaint();
renderView.compositeFrame(); renderView.compositeFrame();
} }
......
...@@ -391,6 +391,10 @@ RenderingExceptionHandler debugRenderingExceptionHandler; ...@@ -391,6 +391,10 @@ RenderingExceptionHandler debugRenderingExceptionHandler;
/// for their children. /// for their children.
abstract class RenderObject extends AbstractNode implements HitTestTarget { abstract class RenderObject extends AbstractNode implements HitTestTarget {
RenderObject() {
_needsCompositing = hasLayer;
}
// LAYOUT // LAYOUT
/// Data for use by the parent render object. /// Data for use by the parent render object.
...@@ -492,7 +496,7 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget { ...@@ -492,7 +496,7 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
} }
} }
static List<RenderObject> _nodesNeedingLayout = new List<RenderObject>(); static List<RenderObject> _nodesNeedingLayout = <RenderObject>[];
bool _needsLayout = true; bool _needsLayout = true;
/// Whether this render object's layout information is dirty. /// Whether this render object's layout information is dirty.
bool get needsLayout => _needsLayout; bool get needsLayout => _needsLayout;
...@@ -597,11 +601,11 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget { ...@@ -597,11 +601,11 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
// TODO(ianh): assert that we're not allowing previously dirty nodes to redirty themeselves // TODO(ianh): assert that we're not allowing previously dirty nodes to redirty themeselves
while (_nodesNeedingLayout.isNotEmpty) { while (_nodesNeedingLayout.isNotEmpty) {
List<RenderObject> dirtyNodes = _nodesNeedingLayout; List<RenderObject> dirtyNodes = _nodesNeedingLayout;
_nodesNeedingLayout = new List<RenderObject>(); _nodesNeedingLayout = <RenderObject>[];
dirtyNodes..sort((RenderObject a, RenderObject b) => a.depth - b.depth)..forEach((RenderObject node) { for (RenderObject node in dirtyNodes..sort((RenderObject a, RenderObject b) => a.depth - b.depth)) {
if (node._needsLayout && node.attached) if (node._needsLayout && node.attached)
node._layoutWithoutResize(); node._layoutWithoutResize();
}); }
} }
} finally { } finally {
_debugDoingLayout = false; _debugDoingLayout = false;
...@@ -822,7 +826,8 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget { ...@@ -822,7 +826,8 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
static RenderObject _debugActivePaint = null; static RenderObject _debugActivePaint = null;
static RenderObject get debugActivePaint => _debugActivePaint; static RenderObject get debugActivePaint => _debugActivePaint;
static List<RenderObject> _nodesNeedingPaint = new List<RenderObject>(); static List<RenderObject> _nodesNeedingPaint = <RenderObject>[];
static List<RenderObject> _nodesNeedingCompositingBitsUpdate = <RenderObject>[];
/// Whether this render object paints using a composited layer. /// Whether this render object paints using a composited layer.
/// ///
...@@ -843,49 +848,79 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget { ...@@ -843,49 +848,79 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
return _layer; return _layer;
} }
bool _needsCompositingBitsUpdate = true; bool _needsCompositingBitsUpdate = false; // set to true when a child is added
/// Mark the compositing state for this render object as dirty. /// Mark the compositing state for this render object as dirty.
/// ///
/// When the subtree is mutated, we need to recompute our [needsCompositing] /// When the subtree is mutated, we need to recompute our
/// bit, and our ancestors need to do the same (in case ours changed). /// [needsCompositing] bit, and some of our ancestors need to do the
/// Therefore, [adoptChild] and [dropChild] call /// same (in case ours changed in a way that will change theirs). To
/// [markNeedsCompositingBitsUpdate]. /// this end, [adoptChild] and [dropChild] call this method, and, as
/// necessary, this method calls the parent's, etc, walking up the
/// tree to mark all the nodes that need updating.
///
/// This method does not schedule a rendering frame, because since
/// it cannot be the case that _only_ the compositing bits changed,
/// something else will have scheduled a frame for us.
void _markNeedsCompositingBitsUpdate() { void _markNeedsCompositingBitsUpdate() {
if (_needsCompositingBitsUpdate) if (_needsCompositingBitsUpdate)
return; return;
_needsCompositingBitsUpdate = true; _needsCompositingBitsUpdate = true;
if (parent is RenderObject) {
final RenderObject parent = this.parent;
if (parent._needsCompositingBitsUpdate)
return;
if (!hasLayer && !parent.hasLayer) {
parent._markNeedsCompositingBitsUpdate();
return;
}
}
assert(() {
final AbstractNode parent = this.parent; final AbstractNode parent = this.parent;
if (parent is RenderObject) if (parent is RenderObject)
parent._markNeedsCompositingBitsUpdate(); return parent._needsCompositing;
assert(parent == this.parent); return true;
});
// parent is fine (or there isn't one), but we are dirty
_nodesNeedingCompositingBitsUpdate.add(this);
}
/// Updates the [needsCompositing] bits.
///
/// Called as part of the rendering pipeline after [flushLayout] and before
/// [flushPaint].
static void flushCompositingBits() {
Timeline.startSync('Compositing Bits');
_nodesNeedingCompositingBitsUpdate.sort((RenderObject a, RenderObject b) => a.depth - b.depth);
for (RenderObject node in _nodesNeedingCompositingBitsUpdate) {
if (node.attached)
node._updateCompositingBits();
}
_nodesNeedingCompositingBitsUpdate.clear();
Timeline.finishSync();
} }
bool _needsCompositing = false; bool _needsCompositing; // initialised in the constructor
/// Whether we or one of our descendants has a compositing layer. /// Whether we or one of our descendants has a compositing layer.
/// ///
/// Only legal to call after [flushLayout] and [updateCompositingBits] have /// Only legal to call after [flushLayout] and [flushCompositingBits] have
/// been called. /// been called.
bool get needsCompositing { bool get needsCompositing {
assert(!_needsCompositingBitsUpdate); // make sure we don't use this bit when it is dirty assert(!_needsCompositingBitsUpdate); // make sure we don't use this bit when it is dirty
return _needsCompositing; return _needsCompositing;
} }
/// Updates the [needsCompositing] bits. void _updateCompositingBits() {
///
/// Called as part of the rendering pipeline after [flushLayout] and before
/// [flushPaint].
void updateCompositingBits() {
if (!_needsCompositingBitsUpdate) if (!_needsCompositingBitsUpdate)
return; return;
bool didHaveCompositedDescendant = _needsCompositing; bool oldNeedsCompositing = _needsCompositing;
visitChildren((RenderObject child) { visitChildren((RenderObject child) {
child.updateCompositingBits(); child._updateCompositingBits();
if (child.needsCompositing) if (child.needsCompositing)
_needsCompositing = true; _needsCompositing = true;
}); });
if (hasLayer) if (hasLayer)
_needsCompositing = true; _needsCompositing = true;
if (didHaveCompositedDescendant != _needsCompositing) if (oldNeedsCompositing != _needsCompositing)
markNeedsPaint(); markNeedsPaint();
_needsCompositingBitsUpdate = false; _needsCompositingBitsUpdate = false;
} }
...@@ -946,7 +981,7 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget { ...@@ -946,7 +981,7 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
_debugDoingPaint = true; _debugDoingPaint = true;
try { try {
List<RenderObject> dirtyNodes = _nodesNeedingPaint; List<RenderObject> dirtyNodes = _nodesNeedingPaint;
_nodesNeedingPaint = new List<RenderObject>(); _nodesNeedingPaint = <RenderObject>[];
// Sort the dirty nodes in reverse order (deepest first). // Sort the dirty nodes in reverse order (deepest first).
for (RenderObject node in dirtyNodes..sort((RenderObject a, RenderObject b) => b.depth - a.depth)) { for (RenderObject node in dirtyNodes..sort((RenderObject a, RenderObject b) => b.depth - a.depth)) {
assert(node._needsPaint); assert(node._needsPaint);
......
...@@ -21,6 +21,7 @@ class TestRenderView extends RenderView { ...@@ -21,6 +21,7 @@ class TestRenderView extends RenderView {
enum EnginePhase { enum EnginePhase {
layout, layout,
compositingBits,
paint, paint,
composite composite
} }
...@@ -39,7 +40,9 @@ class TestRenderingFlutterBinding extends BindingBase with Scheduler, Renderer, ...@@ -39,7 +40,9 @@ class TestRenderingFlutterBinding extends BindingBase with Scheduler, Renderer,
RenderObject.flushLayout(); RenderObject.flushLayout();
if (phase == EnginePhase.layout) if (phase == EnginePhase.layout)
return; return;
renderer.renderView.updateCompositingBits(); RenderObject.flushCompositingBits();
if (phase == EnginePhase.compositingBits)
return;
RenderObject.flushPaint(); RenderObject.flushPaint();
if (phase == EnginePhase.paint) if (phase == EnginePhase.paint)
return; return;
......
...@@ -10,18 +10,21 @@ import 'rendering_tester.dart'; ...@@ -10,18 +10,21 @@ import 'rendering_tester.dart';
void main() { void main() {
test('Stack can layout with top, right, bottom, left 0.0', () { test('Stack can layout with top, right, bottom, left 0.0', () {
RenderBox size = new RenderConstrainedBox( RenderBox size = new RenderConstrainedBox(
additionalConstraints: new BoxConstraints.tight(const Size(100.0, 100.0))); additionalConstraints: new BoxConstraints.tight(const Size(100.0, 100.0))
);
RenderBox red = new RenderDecoratedBox( RenderBox red = new RenderDecoratedBox(
decoration: new BoxDecoration( decoration: new BoxDecoration(
backgroundColor: const Color(0xFFFF0000) backgroundColor: const Color(0xFFFF0000)
), ),
child: size); child: size
);
RenderBox green = new RenderDecoratedBox( RenderBox green = new RenderDecoratedBox(
decoration: new BoxDecoration( decoration: new BoxDecoration(
backgroundColor: const Color(0xFFFF0000) backgroundColor: const Color(0xFFFF0000)
)); )
);
RenderBox stack = new RenderStack(children: <RenderBox>[red, green]); RenderBox stack = new RenderStack(children: <RenderBox>[red, green]);
(green.parentData as StackParentData) (green.parentData as StackParentData)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment