Skip to content
This repository has been archived by the owner on Apr 3, 2020. It is now read-only.

Commit

Permalink
Mac overlays: Clean up partial swap code
Browse files Browse the repository at this point in the history
Remove use of linked_ptr, because it's not necessary now that we have
C++11 support. Separate out functions to be a bit easier to read and
follow. Remove unnecessary uses of RectF when Rect would do.

BUG=533681

Review URL: https://codereview.chromium.org/1643043002

Cr-Commit-Position: refs/heads/master@{#372707}
(cherry picked from commit 4d0aa96)

Mac overlays: Move CALayerPartialDamageTree to its own file

Direct copy-paste, no changes.

BUG=533681
TBR=erikchen

Review URL: https://codereview.chromium.org/1647523002

Cr-Commit-Position: refs/heads/master@{#371871}
(cherry picked from commit 9a9014a)

Mac overlays: Isolate partial swap support

The logic to maintain a CALayer tree for partial swap support is
complicated and should be removed, but that would compromise battery
usage. Once the CoreAniamtion renderer is handling ~99% of the frames,
we can remove it. In the mean time, isolate it.

Create a CALayerPartialDamageTree to track the layer tree of partially
swapped backbuffers. Do this in-place to minimize diffs, so that this
patch can be reasonably reviewed. A follow-on patch will move it to
a separate file.

Slightly reorganize the logic so that CALayerPartialDamageTree matches
the general structure of CALayerTree, where this a pending (not swapped)
tree, a tree for each pending swap, and a tree for what is currently
being displayed.

BUG=533681

Review URL: https://codereview.chromium.org/1640093002

Cr-Commit-Position: refs/heads/master@{#371831}
(cherry picked from commit 8aa00fc)

Review URL: https://codereview.chromium.org/1661453003 .

Cr-Commit-Position: refs/branch-heads/2623@{#249}
Cr-Branched-From: 92d7753-refs/heads/master@{#369907}
  • Loading branch information
ccameron-chromium committed Feb 2, 2016
1 parent 1fb5ee6 commit 04f51c6
Show file tree
Hide file tree
Showing 5 changed files with 376 additions and 281 deletions.
56 changes: 56 additions & 0 deletions content/common/gpu/ca_layer_partial_damage_tree_mac.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CONTENT_COMMON_GPU_CA_LAYER_PARTIAL_DAMAGE_TREE_MAC_H_
#define CONTENT_COMMON_GPU_CA_LAYER_PARTIAL_DAMAGE_TREE_MAC_H_

#include <IOSurface/IOSurface.h>
#include <QuartzCore/QuartzCore.h>
#include <deque>

#include "base/mac/scoped_cftyperef.h"
#include "base/memory/scoped_ptr.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/rect_f.h"

namespace content {

class CALayerPartialDamageTree {
public:
CALayerPartialDamageTree(bool allow_partial_swap,
base::ScopedCFTypeRef<IOSurfaceRef> io_surface,
const gfx::Rect& pixel_frame_rect);
~CALayerPartialDamageTree();

base::ScopedCFTypeRef<IOSurfaceRef> RootLayerIOSurface();
void CommitCALayers(CALayer* superlayer,
scoped_ptr<CALayerPartialDamageTree> old_tree,
float scale_factor,
const gfx::Rect& pixel_damage_rect);

private:
class OverlayPlane;

// This will populate |partial_damage_planes_|, potentially re-using the
// CALayers and |partial_damage_planes_| from |old_tree|. After this function
// completes, the back() of |partial_damage_planes_| is the plane that will
// be updated this frame (and if it is empty, then the root plane will be
// updated).
void UpdatePartialDamagePlanes(CALayerPartialDamageTree* old_tree,
const gfx::Rect& pixel_damage_rect);

void UpdateRootAndPartialDamagePlanes(
scoped_ptr<CALayerPartialDamageTree> old_tree,
const gfx::Rect& pixel_damage_rect);

void UpdateCALayers(CALayer* superlayer, float scale_factor);

const bool allow_partial_swap_;
scoped_ptr<OverlayPlane> root_plane_;
std::deque<scoped_ptr<OverlayPlane>> partial_damage_planes_;
};

} // content

#endif // CONTENT_COMMON_GPU_CA_LAYER_PARTIAL_DAMAGE_TREE_MAC_H_
280 changes: 280 additions & 0 deletions content/common/gpu/ca_layer_partial_damage_tree_mac.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,280 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/common/gpu/ca_layer_partial_damage_tree_mac.h"

#include "base/command_line.h"
#include "base/mac/scoped_nsobject.h"
#include "base/mac/sdk_forward_declarations.h"
#include "ui/base/ui_base_switches.h"
#include "ui/gfx/transform.h"

@interface CALayer(Private)
-(void)setContentsChanged;
@end

namespace content {
namespace {

// When selecting a CALayer to re-use for partial damage, this is the maximum
// fraction of the merged layer's pixels that may be not-updated by the swap
// before we consider the CALayer to not be a good enough match, and create a
// new one.
const float kMaximumPartialDamageWasteFraction = 1.2f;

// The maximum number of partial damage layers that may be created before we
// give up and remove them all (doing full damage in the process).
const size_t kMaximumPartialDamageLayers = 8;

} // namespace

class CALayerPartialDamageTree::OverlayPlane {
public:
OverlayPlane(base::ScopedCFTypeRef<IOSurfaceRef> io_surface,
const gfx::Rect& pixel_frame_rect,
const gfx::RectF& contents_rect)
: io_surface(io_surface),
contents_rect(contents_rect),
pixel_frame_rect(pixel_frame_rect),
layer_needs_update(true) {}

~OverlayPlane() {
[ca_layer setContents:nil];
[ca_layer removeFromSuperlayer];
ca_layer.reset();
}

const base::ScopedCFTypeRef<IOSurfaceRef> io_surface;
const gfx::RectF contents_rect;
const gfx::Rect pixel_frame_rect;
bool layer_needs_update;
base::scoped_nsobject<CALayer> ca_layer;

void TakeCALayerFrom(OverlayPlane* other_plane) {
ca_layer.swap(other_plane->ca_layer);
}

void UpdateProperties(float scale_factor) {
if (layer_needs_update) {
[ca_layer setOpaque:YES];

id new_contents = static_cast<id>(io_surface.get());
if ([ca_layer contents] == new_contents)
[ca_layer setContentsChanged];
else
[ca_layer setContents:new_contents];
[ca_layer setContentsRect:contents_rect.ToCGRect()];

[ca_layer setAnchorPoint:CGPointZero];

if ([ca_layer respondsToSelector:(@selector(setContentsScale:))])
[ca_layer setContentsScale:scale_factor];
gfx::RectF dip_frame_rect = gfx::RectF(pixel_frame_rect);
dip_frame_rect.Scale(1 / scale_factor);
[ca_layer setBounds:CGRectMake(0, 0, dip_frame_rect.width(),
dip_frame_rect.height())];
[ca_layer
setPosition:CGPointMake(dip_frame_rect.x(), dip_frame_rect.y())];
}
static bool show_borders =
base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kShowMacOverlayBorders);
if (show_borders) {
base::ScopedCFTypeRef<CGColorRef> color;
if (!layer_needs_update) {
// Green represents contents that are unchanged across frames.
color.reset(CGColorCreateGenericRGB(0, 1, 0, 1));
} else {
// Red represents damaged contents.
color.reset(CGColorCreateGenericRGB(1, 0, 0, 1));
}
[ca_layer setBorderWidth:1];
[ca_layer setBorderColor:color];
}
layer_needs_update = false;
}

private:
};

void CALayerPartialDamageTree::UpdatePartialDamagePlanes(
CALayerPartialDamageTree* old_tree,
const gfx::Rect& pixel_damage_rect) {
// Don't create partial damage layers if partial swap is disabled.
if (!allow_partial_swap_)
return;
// Only create partial damage layers when building on top of an existing tree.
if (!old_tree)
return;
// If the frame size has changed, discard all of the old partial damage
// layers.
if (old_tree->root_plane_->pixel_frame_rect != root_plane_->pixel_frame_rect)
return;
// If there is full damage, discard all of the old partial damage layers.
if (pixel_damage_rect == root_plane_->pixel_frame_rect)
return;

// If there is no damage, don't change anything.
if (pixel_damage_rect.IsEmpty()) {
std::swap(partial_damage_planes_, old_tree->partial_damage_planes_);
return;
}

// Find the last partial damage plane to re-use the CALayer from. Grow the
// new rect for this layer to include this damage, and all nearby partial
// damage layers.
scoped_ptr<OverlayPlane> plane_for_swap;
{
auto plane_to_reuse_iter = old_tree->partial_damage_planes_.end();
gfx::Rect plane_to_reuse_enlarged_pixel_damage_rect;

for (auto old_plane_iter = old_tree->partial_damage_planes_.begin();
old_plane_iter != old_tree->partial_damage_planes_.end();
++old_plane_iter) {
gfx::Rect enlarged_pixel_damage_rect =
(*old_plane_iter)->pixel_frame_rect;
enlarged_pixel_damage_rect.Union(pixel_damage_rect);

// Compute the fraction of the pixels that would not be updated by this
// swap. If it is too big, try another layer.
float waste_fraction = enlarged_pixel_damage_rect.size().GetArea() * 1.f /
pixel_damage_rect.size().GetArea();
if (waste_fraction > kMaximumPartialDamageWasteFraction)
continue;

plane_to_reuse_iter = old_plane_iter;
plane_to_reuse_enlarged_pixel_damage_rect.Union(
enlarged_pixel_damage_rect);
}
if (plane_to_reuse_iter != old_tree->partial_damage_planes_.end()) {
gfx::RectF enlarged_contents_rect =
gfx::RectF(plane_to_reuse_enlarged_pixel_damage_rect);
enlarged_contents_rect.Scale(1. / root_plane_->pixel_frame_rect.width(),
1. / root_plane_->pixel_frame_rect.height());

plane_for_swap.reset(new OverlayPlane(
root_plane_->io_surface, plane_to_reuse_enlarged_pixel_damage_rect,
enlarged_contents_rect));

plane_for_swap->TakeCALayerFrom((*plane_to_reuse_iter).get());
if (*plane_to_reuse_iter != old_tree->partial_damage_planes_.back()) {
CALayer* superlayer = [plane_for_swap->ca_layer superlayer];
[plane_for_swap->ca_layer removeFromSuperlayer];
[superlayer addSublayer:plane_for_swap->ca_layer];
}
}
}

// If we haven't found an appropriate layer to re-use, create a new one, if
// we haven't already created too many.
if (!plane_for_swap.get() &&
old_tree->partial_damage_planes_.size() < kMaximumPartialDamageLayers) {
gfx::RectF contents_rect = gfx::RectF(pixel_damage_rect);
contents_rect.Scale(1. / root_plane_->pixel_frame_rect.width(),
1. / root_plane_->pixel_frame_rect.height());
plane_for_swap.reset(new OverlayPlane(root_plane_->io_surface,
pixel_damage_rect, contents_rect));
}

// And if we still don't have a layer, do full damage.
if (!plane_for_swap.get())
return;

// Walk all old partial damage planes. Remove anything that is now completely
// covered, and move everything else into the new |partial_damage_planes_|.
for (auto& old_plane : old_tree->partial_damage_planes_) {
if (!old_plane.get())
continue;
// Intersect the planes' frames with the new root plane to ensure that
// they don't get kept alive inappropriately.
gfx::Rect old_plane_frame_rect = old_plane->pixel_frame_rect;
old_plane_frame_rect.Intersect(root_plane_->pixel_frame_rect);

bool old_plane_covered_by_swap = false;
if (plane_for_swap.get() &&
plane_for_swap->pixel_frame_rect.Contains(old_plane_frame_rect)) {
old_plane_covered_by_swap = true;
}
if (!old_plane_covered_by_swap) {
DCHECK(old_plane->ca_layer);
partial_damage_planes_.push_back(std::move(old_plane));
}
}

partial_damage_planes_.push_back(std::move(plane_for_swap));
}

void CALayerPartialDamageTree::UpdateRootAndPartialDamagePlanes(
scoped_ptr<CALayerPartialDamageTree> old_tree,
const gfx::Rect& pixel_damage_rect) {
// First update the partial damage tree.
UpdatePartialDamagePlanes(old_tree.get(), pixel_damage_rect);
if (old_tree) {
if (partial_damage_planes_.empty()) {
// If there are no partial damage planes, then we will be updating the
// root layer. Take the CALayer from the old tree.
root_plane_->TakeCALayerFrom(old_tree->root_plane_.get());
} else {
// If there is a partial damage tree, then just take the old plane
// from the previous frame, so that there is no update to it.
root_plane_.swap(old_tree->root_plane_);
}
}
}

void CALayerPartialDamageTree::UpdateCALayers(CALayer* superlayer,
float scale_factor) {
if (!allow_partial_swap_) {
DCHECK(partial_damage_planes_.empty());
return;
}

// Allocate and update CALayers for the backbuffer and partial damage layers.
if (!root_plane_->ca_layer) {
DCHECK(partial_damage_planes_.empty());
root_plane_->ca_layer.reset([[CALayer alloc] init]);
[superlayer setSublayers:nil];
[superlayer addSublayer:root_plane_->ca_layer];
}
for (auto& plane : partial_damage_planes_) {
if (!plane->ca_layer) {
DCHECK(plane == partial_damage_planes_.back());
plane->ca_layer.reset([[CALayer alloc] init]);
}
if (![plane->ca_layer superlayer]) {
DCHECK(plane == partial_damage_planes_.back());
[superlayer addSublayer:plane->ca_layer];
}
}
root_plane_->UpdateProperties(scale_factor);
for (auto& plane : partial_damage_planes_)
plane->UpdateProperties(scale_factor);
}

CALayerPartialDamageTree::CALayerPartialDamageTree(
bool allow_partial_swap,
base::ScopedCFTypeRef<IOSurfaceRef> io_surface,
const gfx::Rect& pixel_frame_rect)
: allow_partial_swap_(allow_partial_swap) {
root_plane_.reset(
new OverlayPlane(io_surface, pixel_frame_rect, gfx::RectF(0, 0, 1, 1)));
}

CALayerPartialDamageTree::~CALayerPartialDamageTree() {}

base::ScopedCFTypeRef<IOSurfaceRef>
CALayerPartialDamageTree::RootLayerIOSurface() {
return root_plane_->io_surface;
}

void CALayerPartialDamageTree::CommitCALayers(
CALayer* superlayer,
scoped_ptr<CALayerPartialDamageTree> old_tree,
float scale_factor,
const gfx::Rect& pixel_damage_rect) {
UpdateRootAndPartialDamagePlanes(std::move(old_tree), pixel_damage_rect);
UpdateCALayers(superlayer, scale_factor);
}

} // namespace content
18 changes: 3 additions & 15 deletions content/common/gpu/image_transport_surface_overlay_mac.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
#include <list>
#include <vector>

#include "base/memory/linked_ptr.h"
#import "base/mac/scoped_nsobject.h"
#include "base/timer/timer.h"
#include "content/common/gpu/gpu_command_buffer_stub.h"
Expand All @@ -22,6 +21,7 @@
namespace content {

class CALayerTree;
class CALayerPartialDamageTree;

class ImageTransportSurfaceOverlayMac : public gfx::GLSurface,
public ImageTransportSurface,
Expand Down Expand Up @@ -78,13 +78,6 @@ class ImageTransportSurfaceOverlayMac : public gfx::GLSurface,

gfx::SwapResult SwapBuffersInternal(const gfx::Rect& pixel_damage_rect);

void UpdateRootAndPartialDamagePlanes(
const linked_ptr<OverlayPlane>& new_root_plane,
const gfx::RectF& pixel_damage_rect);
void UpdateRootAndPartialDamageCALayers(float scale_factor);
void UpdateCALayerTree(scoped_ptr<CALayerTree> ca_layer_tree,
float scale_factor);

// Returns true if the front of |pending_swaps_| has completed, or has timed
// out by |now|.
bool IsFirstPendingSwapReadyToDisplay(
Expand Down Expand Up @@ -125,7 +118,7 @@ class ImageTransportSurfaceOverlayMac : public gfx::GLSurface,

// Planes that have been scheduled, but have not had a subsequent SwapBuffers
// call made yet.
linked_ptr<OverlayPlane> pending_root_plane_;
scoped_ptr<CALayerPartialDamageTree> pending_partial_damage_tree_;
scoped_ptr<CALayerTree> pending_ca_layer_tree_;

// A queue of all frames that have been created by SwapBuffersInternal but
Expand All @@ -134,8 +127,7 @@ class ImageTransportSurfaceOverlayMac : public gfx::GLSurface,
std::deque<linked_ptr<PendingSwap>> pending_swaps_;

// The planes that are currently being displayed on the screen.
linked_ptr<OverlayPlane> current_root_plane_;
std::list<linked_ptr<OverlayPlane>> current_partial_damage_planes_;
scoped_ptr<CALayerPartialDamageTree> current_partial_damage_tree_;
scoped_ptr<CALayerTree> current_ca_layer_tree_;

// The time of the last swap was issued. If this is more than two vsyncs, then
Expand All @@ -147,10 +139,6 @@ class ImageTransportSurfaceOverlayMac : public gfx::GLSurface,
base::TimeTicks vsync_timebase_;
base::TimeDelta vsync_interval_;

// Calls to ScheduleCALayer come in back-to-front. This is reset to 1 at each
// swap and increments with each call to ScheduleCALayer.
int next_ca_layer_z_order_;

base::Timer display_pending_swap_timer_;
base::WeakPtrFactory<ImageTransportSurfaceOverlayMac> weak_factory_;
};
Expand Down
Loading

0 comments on commit 04f51c6

Please sign in to comment.