Commit e48efdca authored by Ian Hickson's avatar Ian Hickson

Merge pull request #982 from Hixie/fewer-bit-updates

Less tree walking for compositing bits updates.
parents 884c8e9e ab01c7bf
...@@ -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