From 04f51c60da4daf1eb228d46954e4a8f9d23fbea1 Mon Sep 17 00:00:00 2001 From: Christopher Cameron Date: Tue, 2 Feb 2016 12:32:59 -0800 Subject: [PATCH] Mac overlays: Clean up partial swap code 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 4d0aa96265cd731ddd74d43821df7feb9c55c1b1) 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 9a9014a549f126db8ad2dc0c0a1dc6bb246423ae) 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 8aa00fc2a374fca3e9b9f34d838db7bb5f4fc088) Review URL: https://codereview.chromium.org/1661453003 . Cr-Commit-Position: refs/branch-heads/2623@{#249} Cr-Branched-From: 92d77538a86529ca35f9220bd3cd512cbea1f086-refs/heads/master@{#369907} --- .../gpu/ca_layer_partial_damage_tree_mac.h | 56 ++++ .../gpu/ca_layer_partial_damage_tree_mac.mm | 280 ++++++++++++++++ .../gpu/image_transport_surface_overlay_mac.h | 18 +- .../image_transport_surface_overlay_mac.mm | 301 ++---------------- content/content_common.gypi | 2 + 5 files changed, 376 insertions(+), 281 deletions(-) create mode 100644 content/common/gpu/ca_layer_partial_damage_tree_mac.h create mode 100644 content/common/gpu/ca_layer_partial_damage_tree_mac.mm diff --git a/content/common/gpu/ca_layer_partial_damage_tree_mac.h b/content/common/gpu/ca_layer_partial_damage_tree_mac.h new file mode 100644 index 0000000000000..f52000d6ddfb3 --- /dev/null +++ b/content/common/gpu/ca_layer_partial_damage_tree_mac.h @@ -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 +#include +#include + +#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 io_surface, + const gfx::Rect& pixel_frame_rect); + ~CALayerPartialDamageTree(); + + base::ScopedCFTypeRef RootLayerIOSurface(); + void CommitCALayers(CALayer* superlayer, + scoped_ptr 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 old_tree, + const gfx::Rect& pixel_damage_rect); + + void UpdateCALayers(CALayer* superlayer, float scale_factor); + + const bool allow_partial_swap_; + scoped_ptr root_plane_; + std::deque> partial_damage_planes_; +}; + +} // content + +#endif // CONTENT_COMMON_GPU_CA_LAYER_PARTIAL_DAMAGE_TREE_MAC_H_ diff --git a/content/common/gpu/ca_layer_partial_damage_tree_mac.mm b/content/common/gpu/ca_layer_partial_damage_tree_mac.mm new file mode 100644 index 0000000000000..ee94726380790 --- /dev/null +++ b/content/common/gpu/ca_layer_partial_damage_tree_mac.mm @@ -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 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 io_surface; + const gfx::RectF contents_rect; + const gfx::Rect pixel_frame_rect; + bool layer_needs_update; + base::scoped_nsobject 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(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 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 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 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 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 +CALayerPartialDamageTree::RootLayerIOSurface() { + return root_plane_->io_surface; +} + +void CALayerPartialDamageTree::CommitCALayers( + CALayer* superlayer, + scoped_ptr 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 diff --git a/content/common/gpu/image_transport_surface_overlay_mac.h b/content/common/gpu/image_transport_surface_overlay_mac.h index 5b86a5e987696..2103bbf71089e 100644 --- a/content/common/gpu/image_transport_surface_overlay_mac.h +++ b/content/common/gpu/image_transport_surface_overlay_mac.h @@ -8,7 +8,6 @@ #include #include -#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" @@ -22,6 +21,7 @@ namespace content { class CALayerTree; +class CALayerPartialDamageTree; class ImageTransportSurfaceOverlayMac : public gfx::GLSurface, public ImageTransportSurface, @@ -78,13 +78,6 @@ class ImageTransportSurfaceOverlayMac : public gfx::GLSurface, gfx::SwapResult SwapBuffersInternal(const gfx::Rect& pixel_damage_rect); - void UpdateRootAndPartialDamagePlanes( - const linked_ptr& new_root_plane, - const gfx::RectF& pixel_damage_rect); - void UpdateRootAndPartialDamageCALayers(float scale_factor); - void UpdateCALayerTree(scoped_ptr ca_layer_tree, - float scale_factor); - // Returns true if the front of |pending_swaps_| has completed, or has timed // out by |now|. bool IsFirstPendingSwapReadyToDisplay( @@ -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 pending_root_plane_; + scoped_ptr pending_partial_damage_tree_; scoped_ptr pending_ca_layer_tree_; // A queue of all frames that have been created by SwapBuffersInternal but @@ -134,8 +127,7 @@ class ImageTransportSurfaceOverlayMac : public gfx::GLSurface, std::deque> pending_swaps_; // The planes that are currently being displayed on the screen. - linked_ptr current_root_plane_; - std::list> current_partial_damage_planes_; + scoped_ptr current_partial_damage_tree_; scoped_ptr current_ca_layer_tree_; // The time of the last swap was issued. If this is more than two vsyncs, then @@ -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 weak_factory_; }; diff --git a/content/common/gpu/image_transport_surface_overlay_mac.mm b/content/common/gpu/image_transport_surface_overlay_mac.mm index 724f135b7296c..034dc0b80b54f 100644 --- a/content/common/gpu/image_transport_surface_overlay_mac.mm +++ b/content/common/gpu/image_transport_surface_overlay_mac.mm @@ -20,15 +20,13 @@ typedef void* GLeglImageOES; #endif -#include "base/command_line.h" #include "base/mac/scoped_cftyperef.h" -#include "base/mac/sdk_forward_declarations.h" +#include "content/common/gpu/ca_layer_partial_damage_tree_mac.h" #include "content/common/gpu/ca_layer_tree_mac.h" #include "content/common/gpu/gpu_messages.h" #include "ui/accelerated_widget_mac/io_surface_context.h" #include "ui/base/cocoa/animation_utils.h" #include "ui/base/cocoa/remote_layer_api.h" -#include "ui/base/ui_base_switches.h" #include "ui/gfx/geometry/rect_conversions.h" #include "ui/gfx/transform.h" #include "ui/gl/gl_context.h" @@ -59,16 +57,6 @@ // they come. const double kMaximumVSyncsBetweenSwapsForSmoothAnimation = 1.5; -// 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; - void CheckGLErrors(const char* msg) { GLenum gl_error; while ((gl_error = glGetError()) != GL_NO_ERROR) { @@ -94,88 +82,6 @@ -(void)setContentsChanged; return new ImageTransportSurfaceOverlayMac(manager, stub, handle); } -class ImageTransportSurfaceOverlayMac::OverlayPlane { - public: - static linked_ptr CreateWithFrameRect( - int z_order, - base::ScopedCFTypeRef io_surface, - const gfx::RectF& pixel_frame_rect, - const gfx::RectF& contents_rect) { - gfx::Transform transform; - transform.Translate(pixel_frame_rect.x(), pixel_frame_rect.y()); - return linked_ptr( - new OverlayPlane(z_order, io_surface, contents_rect, pixel_frame_rect)); - } - - ~OverlayPlane() { - [ca_layer setContents:nil]; - [ca_layer removeFromSuperlayer]; - ca_layer.reset(); - } - - const int z_order; - const base::ScopedCFTypeRef io_surface; - const gfx::RectF contents_rect; - const gfx::RectF pixel_frame_rect; - bool layer_needs_update; - base::scoped_nsobject 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(io_surface.get()); - if ([ca_layer contents] == new_contents && z_order == 0) - [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 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: - OverlayPlane(int z_order, - base::ScopedCFTypeRef io_surface, - const gfx::RectF& contents_rect, - const gfx::RectF& pixel_frame_rect) - : z_order(z_order), - io_surface(io_surface), - contents_rect(contents_rect), - pixel_frame_rect(pixel_frame_rect), - layer_needs_update(true) {} -}; - class ImageTransportSurfaceOverlayMac::PendingSwap { public: PendingSwap() {} @@ -185,7 +91,7 @@ void UpdateProperties(float scale_factor) { float scale_factor; gfx::Rect pixel_damage_rect; - linked_ptr root_plane; + scoped_ptr partial_damage_tree; scoped_ptr ca_layer_tree; std::vector latency_info; @@ -211,7 +117,6 @@ void UpdateProperties(float scale_factor) { scale_factor_(1), gl_renderer_id_(0), vsync_parameters_valid_(false), - next_ca_layer_z_order_(1), display_pending_swap_timer_(true, false), weak_factory_(this) { helper_.reset(new ImageTransportHelper(this, manager, stub, handle)); @@ -243,8 +148,9 @@ void UpdateProperties(float scale_factor) { void ImageTransportSurfaceOverlayMac::Destroy() { DisplayAndClearAllPendingSwaps(); - current_partial_damage_planes_.clear(); - current_root_plane_.reset(); + + current_partial_damage_tree_.reset(); + current_ca_layer_tree_.reset(); } bool ImageTransportSurfaceOverlayMac::IsOffscreen() { @@ -254,7 +160,6 @@ void UpdateProperties(float scale_factor) { gfx::SwapResult ImageTransportSurfaceOverlayMac::SwapBuffersInternal( const gfx::Rect& pixel_damage_rect) { TRACE_EVENT0("gpu", "ImageTransportSurfaceOverlayMac::SwapBuffersInternal"); - next_ca_layer_z_order_ = 1; // Use the same concept of 'now' for the entire function. The duration of // this function only affect the result if this function lasts across a vsync @@ -285,8 +190,7 @@ void UpdateProperties(float scale_factor) { new_swap->pixel_size = pixel_size_; new_swap->scale_factor = scale_factor_; new_swap->pixel_damage_rect = pixel_damage_rect; - new_swap->root_plane = pending_root_plane_; - pending_root_plane_ = linked_ptr(); + new_swap->partial_damage_tree.swap(pending_partial_damage_tree_); new_swap->ca_layer_tree.swap(pending_ca_layer_tree_); new_swap->latency_info.swap(latency_info_); @@ -366,15 +270,27 @@ void UpdateProperties(float scale_factor) { CheckGLErrors("while deleting active fence"); } - // Update the plane lists. + // Update the CALayer hierarchy. { - // Sort the input planes by z-index, and remove any overlays from the - // damage rect. gfx::RectF pixel_damage_rect = gfx::RectF(swap->pixel_damage_rect); ScopedCAActionDisabler disabler; - UpdateRootAndPartialDamagePlanes(swap->root_plane, pixel_damage_rect); - UpdateRootAndPartialDamageCALayers(swap->scale_factor); - UpdateCALayerTree(std::move(swap->ca_layer_tree), swap->scale_factor); + if (swap->ca_layer_tree) { + swap->ca_layer_tree->CommitScheduledCALayers( + ca_root_layer_.get(), std::move(current_ca_layer_tree_), + swap->scale_factor); + current_ca_layer_tree_.swap(swap->ca_layer_tree); + current_partial_damage_tree_.reset(); + } else if (swap->partial_damage_tree) { + swap->partial_damage_tree->CommitCALayers( + ca_root_layer_.get(), std::move(current_partial_damage_tree_), + swap->scale_factor, swap->pixel_damage_rect); + current_partial_damage_tree_.swap(swap->partial_damage_tree); + current_ca_layer_tree_.reset(); + } else { + [ca_root_layer_ setSublayers:nil]; + } + swap->ca_layer_tree.reset(); + swap->partial_damage_tree.reset(); } // Update the latency info to reflect the swap time. @@ -391,9 +307,9 @@ void UpdateProperties(float scale_factor) { GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params; if (use_remote_layer_api_) { params.ca_context_id = [ca_context_ contextId]; - } else if (current_root_plane_.get()) { - params.io_surface.reset( - IOSurfaceCreateMachPort(current_root_plane_->io_surface)); + } else if (current_partial_damage_tree_) { + params.io_surface.reset(IOSurfaceCreateMachPort( + current_partial_damage_tree_->RootLayerIOSurface())); } params.size = swap->pixel_size; params.scale_factor = swap->scale_factor; @@ -404,157 +320,6 @@ void UpdateProperties(float scale_factor) { pending_swaps_.pop_front(); } -void ImageTransportSurfaceOverlayMac::UpdateRootAndPartialDamagePlanes( - const linked_ptr& new_root_plane, - const gfx::RectF& pixel_damage_rect) { - std::list> old_partial_damage_planes; - old_partial_damage_planes.swap(current_partial_damage_planes_); - linked_ptr plane_for_swap; - - // If there is no new root plane, remove everything. - if (!new_root_plane.get()) { - old_partial_damage_planes.clear(); - current_root_plane_.reset(); - return; - } - - // If the frame's size changed, if we haven't updated the root layer, if - // we have full damage, or if we don't support remote layers, then use the - // root layer directly. - if (!use_remote_layer_api_ || !current_root_plane_.get() || - current_root_plane_->pixel_frame_rect != - new_root_plane->pixel_frame_rect || - pixel_damage_rect == new_root_plane->pixel_frame_rect) { - plane_for_swap = new_root_plane; - } - - // Walk though the existing partial damage layers and see if there is one that - // is appropriate to re-use. - if (!plane_for_swap.get() && !pixel_damage_rect.IsEmpty()) { - gfx::RectF plane_to_reuse_dip_enlarged_rect; - - // 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. - linked_ptr plane_to_reuse; - for (auto& old_plane : old_partial_damage_planes) { - gfx::RectF dip_enlarged_rect = old_plane->pixel_frame_rect; - dip_enlarged_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 = dip_enlarged_rect.size().GetArea() * 1.f / - pixel_damage_rect.size().GetArea(); - if (waste_fraction > kMaximumPartialDamageWasteFraction) - continue; - - plane_to_reuse = old_plane; - plane_to_reuse_dip_enlarged_rect.Union(dip_enlarged_rect); - } - - if (plane_to_reuse.get()) { - gfx::RectF enlarged_contents_rect = plane_to_reuse_dip_enlarged_rect; - enlarged_contents_rect.Scale( - 1. / new_root_plane->pixel_frame_rect.width(), - 1. / new_root_plane->pixel_frame_rect.height()); - - plane_for_swap = OverlayPlane::CreateWithFrameRect( - 0, new_root_plane->io_surface, plane_to_reuse_dip_enlarged_rect, - enlarged_contents_rect); - - plane_for_swap->TakeCALayerFrom(plane_to_reuse.get()); - if (plane_to_reuse != old_partial_damage_planes.back()) - [plane_for_swap->ca_layer removeFromSuperlayer]; - } - } - - // 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() && !pixel_damage_rect.IsEmpty() && - old_partial_damage_planes.size() < kMaximumPartialDamageLayers) { - gfx::RectF contents_rect = gfx::RectF(pixel_damage_rect); - contents_rect.Scale(1. / new_root_plane->pixel_frame_rect.width(), - 1. / new_root_plane->pixel_frame_rect.height()); - plane_for_swap = OverlayPlane::CreateWithFrameRect( - 0, new_root_plane->io_surface, pixel_damage_rect, contents_rect); - } - - // And if we still don't have a layer, use the root layer. - if (!plane_for_swap.get() && !pixel_damage_rect.IsEmpty()) - plane_for_swap = new_root_plane; - - // Walk all old partial damage planes. Remove anything that is now completely - // covered, and move everything else into the new - // |current_partial_damage_planes_|. - for (auto& old_plane : old_partial_damage_planes) { - // Intersect the planes' frames with the new root plane to ensure that - // they don't get kept alive inappropriately. - gfx::RectF old_plane_frame_rect = old_plane->pixel_frame_rect; - old_plane_frame_rect.Intersect(new_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); - current_partial_damage_planes_.push_back(old_plane); - } - } - - // Finally, add the new swap's plane at the back of the list, if it exists. - if (plane_for_swap == new_root_plane) { - current_root_plane_ = new_root_plane; - } else if (plane_for_swap.get()) { - current_partial_damage_planes_.push_back(plane_for_swap); - } -} - -void ImageTransportSurfaceOverlayMac::UpdateRootAndPartialDamageCALayers( - float scale_factor) { - if (!use_remote_layer_api_) { - DCHECK(current_partial_damage_planes_.empty()); - return; - } - - // Allocate and update CALayers for the backbuffer and partial damage layers. - if (current_root_plane_.get()) { - if (!current_root_plane_->ca_layer) { - current_root_plane_->ca_layer.reset([[CALayer alloc] init]); - [ca_root_layer_ setSublayers:nil]; - [ca_root_layer_ addSublayer:current_root_plane_->ca_layer]; - } - } - for (auto& plane : current_partial_damage_planes_) { - if (!plane->ca_layer) { - DCHECK(plane == current_partial_damage_planes_.back()); - plane->ca_layer.reset([[CALayer alloc] init]); - } - if (![plane->ca_layer superlayer]) { - DCHECK(plane == current_partial_damage_planes_.back()); - [ca_root_layer_ addSublayer:plane->ca_layer]; - } - } - if (current_root_plane_.get()) - current_root_plane_->UpdateProperties(scale_factor); - for (auto& plane : current_partial_damage_planes_) - plane->UpdateProperties(scale_factor); -} - -void ImageTransportSurfaceOverlayMac::UpdateCALayerTree( - scoped_ptr ca_layer_tree, - float scale_factor) { - if (ca_layer_tree) { - ca_layer_tree->CommitScheduledCALayers( - ca_root_layer_.get(), std::move(current_ca_layer_tree_), scale_factor); - current_ca_layer_tree_.swap(ca_layer_tree); - ca_layer_tree.reset(); - } else { - current_ca_layer_tree_.reset(); - } -} - void ImageTransportSurfaceOverlayMac::DisplayAndClearAllPendingSwaps() { TRACE_EVENT0("gpu", "ImageTransportSurfaceOverlayMac::DisplayAndClearAllPendingSwaps"); @@ -645,10 +410,14 @@ void UpdateProperties(float scale_factor) { DLOG(ERROR) << "Invalid non-zero Z order."; return false; } - - pending_root_plane_ = OverlayPlane::CreateWithFrameRect( - z_order, static_cast(image)->io_surface(), - gfx::RectF(pixel_frame_rect), crop_rect); + if (pending_partial_damage_tree_) { + DLOG(ERROR) << "Only one overlay per swap is allowed."; + return false; + } + pending_partial_damage_tree_.reset(new CALayerPartialDamageTree( + use_remote_layer_api_, + static_cast(image)->io_surface(), + pixel_frame_rect)); return true; } diff --git a/content/content_common.gypi b/content/content_common.gypi index f3e8820fec624..c9ebbb9728c0f 100644 --- a/content/content_common.gypi +++ b/content/content_common.gypi @@ -320,6 +320,8 @@ 'common/gpu/client/grcontext_for_webgraphicscontext3d.h', 'common/gpu/client/webgraphicscontext3d_command_buffer_impl.cc', 'common/gpu/client/webgraphicscontext3d_command_buffer_impl.h', + 'common/gpu/ca_layer_partial_damage_tree_mac.h', + 'common/gpu/ca_layer_partial_damage_tree_mac.mm', 'common/gpu/ca_layer_tree_mac.h', 'common/gpu/ca_layer_tree_mac.mm', 'common/gpu/child_window_surface_win.cc',