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
void beginFrame() {
assert(renderView != null);
RenderObject.flushLayout();
renderView.updateCompositingBits();
RenderObject.flushCompositingBits();
RenderObject.flushPaint();
renderView.compositeFrame();
}
......
......@@ -391,6 +391,10 @@ RenderingExceptionHandler debugRenderingExceptionHandler;
/// for their children.
abstract class RenderObject extends AbstractNode implements HitTestTarget {
RenderObject() {
_needsCompositing = hasLayer;
}
// LAYOUT
/// Data for use by the parent render object.
......@@ -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;
/// Whether this render object's layout information is dirty.
bool get needsLayout => _needsLayout;
......@@ -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
while (_nodesNeedingLayout.isNotEmpty) {
List<RenderObject> dirtyNodes = _nodesNeedingLayout;
_nodesNeedingLayout = new List<RenderObject>();
dirtyNodes..sort((RenderObject a, RenderObject b) => a.depth - b.depth)..forEach((RenderObject node) {
_nodesNeedingLayout = <RenderObject>[];
for (RenderObject node in dirtyNodes..sort((RenderObject a, RenderObject b) => a.depth - b.depth)) {
if (node._needsLayout && node.attached)
node._layoutWithoutResize();
});
}
}
} finally {
_debugDoingLayout = false;
......@@ -822,7 +826,8 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
static RenderObject _debugActivePaint = null;
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.
///
......@@ -843,49 +848,79 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
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.
///
/// When the subtree is mutated, we need to recompute our [needsCompositing]
/// bit, and our ancestors need to do the same (in case ours changed).
/// Therefore, [adoptChild] and [dropChild] call
/// [markNeedsCompositingBitsUpdate].
/// When the subtree is mutated, we need to recompute our
/// [needsCompositing] bit, and some of our ancestors need to do the
/// same (in case ours changed in a way that will change theirs). To
/// 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() {
if (_needsCompositingBitsUpdate)
return;
_needsCompositingBitsUpdate = true;
final AbstractNode parent = this.parent;
if (parent is RenderObject)
parent._markNeedsCompositingBitsUpdate();
assert(parent == this.parent);
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;
if (parent is RenderObject)
return parent._needsCompositing;
return true;
});
// parent is fine (or there isn't one), but we are dirty
_nodesNeedingCompositingBitsUpdate.add(this);
}
bool _needsCompositing = false;
/// 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; // initialised in the constructor
/// 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.
bool get needsCompositing {
assert(!_needsCompositingBitsUpdate); // make sure we don't use this bit when it is dirty
return _needsCompositing;
}
/// Updates the [needsCompositing] bits.
///
/// Called as part of the rendering pipeline after [flushLayout] and before
/// [flushPaint].
void updateCompositingBits() {
void _updateCompositingBits() {
if (!_needsCompositingBitsUpdate)
return;
bool didHaveCompositedDescendant = _needsCompositing;
bool oldNeedsCompositing = _needsCompositing;
visitChildren((RenderObject child) {
child.updateCompositingBits();
child._updateCompositingBits();
if (child.needsCompositing)
_needsCompositing = true;
});
if (hasLayer)
_needsCompositing = true;
if (didHaveCompositedDescendant != _needsCompositing)
if (oldNeedsCompositing != _needsCompositing)
markNeedsPaint();
_needsCompositingBitsUpdate = false;
}
......@@ -946,7 +981,7 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
_debugDoingPaint = true;
try {
List<RenderObject> dirtyNodes = _nodesNeedingPaint;
_nodesNeedingPaint = new List<RenderObject>();
_nodesNeedingPaint = <RenderObject>[];
// Sort the dirty nodes in reverse order (deepest first).
for (RenderObject node in dirtyNodes..sort((RenderObject a, RenderObject b) => b.depth - a.depth)) {
assert(node._needsPaint);
......
......@@ -21,6 +21,7 @@ class TestRenderView extends RenderView {
enum EnginePhase {
layout,
compositingBits,
paint,
composite
}
......@@ -39,7 +40,9 @@ class TestRenderingFlutterBinding extends BindingBase with Scheduler, Renderer,
RenderObject.flushLayout();
if (phase == EnginePhase.layout)
return;
renderer.renderView.updateCompositingBits();
RenderObject.flushCompositingBits();
if (phase == EnginePhase.compositingBits)
return;
RenderObject.flushPaint();
if (phase == EnginePhase.paint)
return;
......
......@@ -10,18 +10,21 @@ import 'rendering_tester.dart';
void main() {
test('Stack can layout with top, right, bottom, left 0.0', () {
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(
decoration: new BoxDecoration(
backgroundColor: const Color(0xFFFF0000)
),
child: size);
child: size
);
RenderBox green = new RenderDecoratedBox(
decoration: new BoxDecoration(
backgroundColor: const Color(0xFFFF0000)
));
)
);
RenderBox stack = new RenderStack(children: <RenderBox>[red, green]);
(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