diff options
author | Christopher Cameron <ccameron@chromium.org> | 2016-01-22 11:10:55 -0800 |
---|---|---|
committer | Christopher Cameron <ccameron@chromium.org> | 2016-01-22 19:14:30 +0000 |
commit | e3ed48b2ecb1053ab1e848c0a146eee78b6b8305 (patch) | |
tree | 56c8a8c32512678b68b7d71d96e18e9c50b0dafb | |
parent | dfe92cc3d612d52ae5f0ec5131df513b3ec6248a (diff) | |
download | chromium_src-e3ed48b2ecb1053ab1e848c0a146eee78b6b8305.zip chromium_src-e3ed48b2ecb1053ab1e848c0a146eee78b6b8305.tar.gz chromium_src-e3ed48b2ecb1053ab1e848c0a146eee78b6b8305.tar.bz2 |
Mac: Add support for clipping and arbitrary transforms
Create a hierarchy as described in the design document at
https://docs.google.com/document/d/1DtSN9zzvCF44_FQPM7ie01UxGHagQ66zfF5L9HnigQY/edit#heading=h.xm56c9edenjg
Move maintenance of this hierarchy to a separate file,
ca_layer_tree_mac.mm.
Remove support for overlays from ImageTransportOverlayMac,
because they now just add complication for not much additional
energy savings.
BUG=533681
CQ_INCLUDE_TRYBOTS=tryserver.blink:linux_blink_rel
Review URL: https://codereview.chromium.org/1585323004
Cr-Commit-Position: refs/heads/master@{#370606}
(cherry picked from commit efbddf0092035837fe9d2e2ebdb90237906869bb)
Review URL: https://codereview.chromium.org/1629473002 .
Cr-Commit-Position: refs/branch-heads/2623@{#82}
Cr-Branched-From: 92d77538a86529ca35f9220bd3cd512cbea1f086-refs/heads/master@{#369907}
-rw-r--r-- | cc/output/ca_layer_overlay.cc | 65 | ||||
-rw-r--r-- | cc/output/ca_layer_overlay.h | 8 | ||||
-rw-r--r-- | cc/output/gl_renderer.cc | 16 | ||||
-rw-r--r-- | cc/output/overlay_unittest.cc | 46 | ||||
-rw-r--r-- | content/browser/compositor/browser_compositor_overlay_candidate_validator_mac.mm | 1 | ||||
-rw-r--r-- | content/common/gpu/ca_layer_tree_mac.h | 188 | ||||
-rw-r--r-- | content/common/gpu/ca_layer_tree_mac.mm | 402 | ||||
-rw-r--r-- | content/common/gpu/image_transport_surface_overlay_mac.h | 17 | ||||
-rw-r--r-- | content/common/gpu/image_transport_surface_overlay_mac.mm | 273 | ||||
-rw-r--r-- | content/content_common.gypi | 2 | ||||
-rw-r--r-- | content/test/gpu/gpu_tests/pixel_expectations.py | 4 | ||||
-rw-r--r-- | gpu/command_buffer/service/gles2_cmd_decoder.cc | 2 | ||||
-rw-r--r-- | ui/gl/gl_surface.cc | 5 | ||||
-rw-r--r-- | ui/gl/gl_surface.h | 5 |
14 files changed, 752 insertions, 282 deletions
diff --git a/cc/output/ca_layer_overlay.cc b/cc/output/ca_layer_overlay.cc index 8704a06..2a0d84c 100644 --- a/cc/output/ca_layer_overlay.cc +++ b/cc/output/ca_layer_overlay.cc @@ -89,7 +89,7 @@ CALayerResult FromTextureQuad(ResourceProvider* resource_provider, // frame is the composition of a vertical flip about the anchor point, and a // translation by the height of the layer. ca_layer_overlay->transform.preTranslate( - 0, ca_layer_overlay->bounds_size.height(), 0); + 0, ca_layer_overlay->bounds_rect.height(), 0); ca_layer_overlay->transform.preScale(1, -1, 1); } ca_layer_overlay->contents_resource_id = resource_id; @@ -125,10 +125,6 @@ CALayerResult FromDrawQuad(ResourceProvider* resource_provider, if (quad->shared_quad_state->blend_mode != SkXfermode::kSrcOver_Mode) return CA_LAYER_FAILED_QUAD_BLEND_MODE; - // TODO(ccameron): Handle 3D transforms. - if (!quad->shared_quad_state->quad_to_target_transform.IsFlat()) - return CA_LAYER_FAILED_QUAD_TRANSFORM; - // Early-out for invisible quads. if (quad->shared_quad_state->opacity == 0.f) { *skip = true; @@ -146,31 +142,16 @@ CALayerResult FromDrawQuad(ResourceProvider* resource_provider, if (quad->IsTopEdge()) ca_layer_overlay->edge_aa_mask |= GL_CA_LAYER_EDGE_TOP_CHROMIUM; - // Check rect clipping. - gfx::RectF quad_rect(quad->rect); - if (quad->shared_quad_state->is_clipped) { - gfx::RectF clip_rect = gfx::RectF(quad->shared_quad_state->clip_rect); - gfx::RectF quad_rect_in_clip_space = gfx::RectF(quad->rect); - quad->shared_quad_state->quad_to_target_transform.TransformRect( - &quad_rect_in_clip_space); - quad_rect_in_clip_space.Intersect(display_rect); - // Skip quads that are entirely clipped. - if (!quad_rect_in_clip_space.Intersects(clip_rect)) { - *skip = true; - return CA_LAYER_SUCCESS; - } - // Fall back if the clip rect actually has an effect. - // TODO(ccameron): Handle more clip rects. - if (!clip_rect.Contains(quad_rect_in_clip_space)) { - return CA_LAYER_FAILED_QUAD_CLIPPING; - } - } + // Set rect clipping and sorting context ID. + ca_layer_overlay->sorting_context_id = + quad->shared_quad_state->sorting_context_id; + ca_layer_overlay->is_clipped = quad->shared_quad_state->is_clipped; + ca_layer_overlay->clip_rect = gfx::RectF(quad->shared_quad_state->clip_rect); ca_layer_overlay->opacity = quad->shared_quad_state->opacity; - ca_layer_overlay->bounds_size = gfx::SizeF(quad->rect.size()); - ca_layer_overlay->transform.setTranslate(quad->rect.x(), quad->rect.y(), 0); - ca_layer_overlay->transform.postConcat( - quad->shared_quad_state->quad_to_target_transform.matrix()); + ca_layer_overlay->bounds_rect = gfx::RectF(quad->rect); + ca_layer_overlay->transform = + quad->shared_quad_state->quad_to_target_transform.matrix(); switch (quad->material) { case DrawQuad::IO_SURFACE_CONTENT: @@ -202,7 +183,6 @@ CALayerResult FromDrawQuad(ResourceProvider* resource_provider, case DrawQuad::YUV_VIDEO_CONTENT: return CA_LAYER_FAILED_YUV_VIDEO_CONTENT; default: - return CA_LAYER_FAILED_UNKNOWN; break; } @@ -220,18 +200,37 @@ bool ProcessForCALayerOverlays(ResourceProvider* resource_provider, const QuadList& quad_list, CALayerOverlayList* ca_layer_overlays) { CALayerResult result = CA_LAYER_SUCCESS; + ca_layer_overlays->reserve(quad_list.size()); + for (auto it = quad_list.BackToFrontBegin(); it != quad_list.BackToFrontEnd(); ++it) { const DrawQuad* quad = *it; - CALayerOverlay ca_layer_overlay; + CALayerOverlay ca_layer; bool skip = false; - result = FromDrawQuad(resource_provider, display_rect, quad, - &ca_layer_overlay, &skip); + result = + FromDrawQuad(resource_provider, display_rect, quad, &ca_layer, &skip); if (result != CA_LAYER_SUCCESS) break; + if (skip) continue; - ca_layer_overlays->push_back(ca_layer_overlay); + + // It is not possible to correctly represent two different clipping settings + // within one sorting context. + if (!ca_layer_overlays->empty()) { + const CALayerOverlay& previous_ca_layer = ca_layer_overlays->back(); + if (ca_layer.sorting_context_id && + previous_ca_layer.sorting_context_id == ca_layer.sorting_context_id) { + if (previous_ca_layer.is_clipped != ca_layer.is_clipped || + previous_ca_layer.clip_rect != ca_layer.clip_rect) { + // TODO(ccameron): Add a histogram value for this. + result = CA_LAYER_FAILED_UNKNOWN; + break; + } + } + } + + ca_layer_overlays->push_back(ca_layer); } UMA_HISTOGRAM_ENUMERATION("Compositing.Renderer.CALayerResult", result, diff --git a/cc/output/ca_layer_overlay.h b/cc/output/ca_layer_overlay.h index ab234f4..f0b7c22 100644 --- a/cc/output/ca_layer_overlay.h +++ b/cc/output/ca_layer_overlay.h @@ -20,6 +20,12 @@ class CC_EXPORT CALayerOverlay { CALayerOverlay(); ~CALayerOverlay(); + // If |is_clipped| is true, then clip to |clip_rect| in the target space. + bool is_clipped = false; + gfx::RectF clip_rect; + // Layers in a non-zero sorting context exist in the same 3D space and should + // intersect. + unsigned sorting_context_id = 0; // Texture that corresponds to an IOSurface to set as the content of the // CALayer. If this is 0 then the CALayer is a solid color. unsigned contents_resource_id = 0; @@ -32,7 +38,7 @@ class CC_EXPORT CALayerOverlay { // The edge anti-aliasing mask property for the CALayer. unsigned edge_aa_mask = 0; // The bounds for the CALayer in pixels. - gfx::SizeF bounds_size; + gfx::RectF bounds_rect; // The transform to apply to the CALayer. SkMatrix44 transform = SkMatrix44(SkMatrix44::kIdentity_Constructor); }; diff --git a/cc/output/gl_renderer.cc b/cc/output/gl_renderer.cc index acd7bf2..2b389f3 100644 --- a/cc/output/gl_renderer.cc +++ b/cc/output/gl_renderer.cc @@ -3551,14 +3551,16 @@ void GLRenderer::ScheduleCALayers(DrawingFrame* frame) { ca_layer_overlay.contents_rect.height(), }; GLfloat bounds_rect[4] = { - 0, 0, ca_layer_overlay.bounds_size.width(), - ca_layer_overlay.bounds_size.height(), + ca_layer_overlay.bounds_rect.x(), ca_layer_overlay.bounds_rect.y(), + ca_layer_overlay.bounds_rect.width(), + ca_layer_overlay.bounds_rect.height(), }; - GLboolean is_clipped = GL_FALSE; - GLfloat clip_rect[4] = { - 0, 0, 0, 0, - }; - GLint sorting_context_id = 0; + GLboolean is_clipped = ca_layer_overlay.is_clipped; + GLfloat clip_rect[4] = {ca_layer_overlay.clip_rect.x(), + ca_layer_overlay.clip_rect.y(), + ca_layer_overlay.clip_rect.width(), + ca_layer_overlay.clip_rect.height()}; + GLint sorting_context_id = ca_layer_overlay.sorting_context_id; GLfloat transform[16]; ca_layer_overlay.transform.asColMajorf(transform); gl_->ScheduleCALayerCHROMIUM( diff --git a/cc/output/overlay_unittest.cc b/cc/output/overlay_unittest.cc index 140935c..c9c3017 100644 --- a/cc/output/overlay_unittest.cc +++ b/cc/output/overlay_unittest.cc @@ -1516,7 +1516,7 @@ TEST_F(CALayerOverlayTest, AllowNonAxisAlignedTransform) { EXPECT_EQ(1U, ca_layer_list.size()); } -TEST_F(CALayerOverlayTest, Disallow3DTransform) { +TEST_F(CALayerOverlayTest, ThreeDTransform) { scoped_ptr<RenderPass> pass = CreateRenderPass(); CreateFullscreenCandidateQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), @@ -1534,42 +1534,22 @@ TEST_F(CALayerOverlayTest, Disallow3DTransform) { &overlay_list, &ca_layer_list, &damage_rect); ASSERT_EQ(1U, pass_list.size()); - EXPECT_EQ(1U, pass_list.back()->quad_list.size()); - EXPECT_EQ(1U, overlay_list.size()); - EXPECT_EQ(0U, ca_layer_list.size()); -} - -TEST_F(CALayerOverlayTest, AllowContainingClip) { - scoped_ptr<RenderPass> pass = CreateRenderPass(); - CreateFullscreenCandidateQuad(resource_provider_.get(), - pass->shared_quad_state_list.back(), - pass.get()); - pass->shared_quad_state_list.back()->is_clipped = true; - pass->shared_quad_state_list.back()->clip_rect = kOverlayRect; - - gfx::Rect damage_rect; - RenderPassList pass_list; - pass_list.push_back(std::move(pass)); - CALayerOverlayList ca_layer_list; - OverlayCandidateList overlay_list( - BackbufferOverlayList(pass_list.back().get())); - overlay_processor_->ProcessForOverlays(resource_provider_.get(), &pass_list, - &overlay_list, &ca_layer_list, - &damage_rect); - ASSERT_EQ(1U, pass_list.size()); EXPECT_EQ(0U, pass_list.back()->quad_list.size()); EXPECT_EQ(0U, overlay_list.size()); EXPECT_EQ(1U, ca_layer_list.size()); + gfx::Transform expected_transform; + expected_transform.RotateAboutXAxis(45.f); + gfx::Transform actual_transform(ca_layer_list.back().transform); + EXPECT_EQ(expected_transform.ToString(), actual_transform.ToString()); } -TEST_F(CALayerOverlayTest, SkipDisjointClip) { +TEST_F(CALayerOverlayTest, AllowContainingClip) { scoped_ptr<RenderPass> pass = CreateRenderPass(); CreateFullscreenCandidateQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), pass.get()); pass->shared_quad_state_list.back()->is_clipped = true; - pass->shared_quad_state_list.back()->clip_rect = - gfx::Rect(128, 128, 128, 128); + pass->shared_quad_state_list.back()->clip_rect = kOverlayRect; gfx::Rect damage_rect; RenderPassList pass_list; @@ -1583,10 +1563,10 @@ TEST_F(CALayerOverlayTest, SkipDisjointClip) { ASSERT_EQ(1U, pass_list.size()); EXPECT_EQ(0U, pass_list.back()->quad_list.size()); EXPECT_EQ(0U, overlay_list.size()); - EXPECT_EQ(0U, ca_layer_list.size()); + EXPECT_EQ(1U, ca_layer_list.size()); } -TEST_F(CALayerOverlayTest, DisallowNontrivialClip) { +TEST_F(CALayerOverlayTest, NontrivialClip) { scoped_ptr<RenderPass> pass = CreateRenderPass(); CreateFullscreenCandidateQuad(resource_provider_.get(), pass->shared_quad_state_list.back(), @@ -1605,9 +1585,11 @@ TEST_F(CALayerOverlayTest, DisallowNontrivialClip) { &damage_rect); ASSERT_EQ(1U, pass_list.size()); - EXPECT_EQ(1U, pass_list.back()->quad_list.size()); - EXPECT_EQ(1U, overlay_list.size()); - EXPECT_EQ(0U, ca_layer_list.size()); + EXPECT_EQ(0U, pass_list.back()->quad_list.size()); + EXPECT_EQ(0U, overlay_list.size()); + EXPECT_EQ(1U, ca_layer_list.size()); + EXPECT_TRUE(ca_layer_list.back().is_clipped); + EXPECT_EQ(gfx::RectF(64, 64, 128, 128), ca_layer_list.back().clip_rect); } TEST_F(CALayerOverlayTest, SkipTransparent) { diff --git a/content/browser/compositor/browser_compositor_overlay_candidate_validator_mac.mm b/content/browser/compositor/browser_compositor_overlay_candidate_validator_mac.mm index 9c12c7e..2b1c3ec 100644 --- a/content/browser/compositor/browser_compositor_overlay_candidate_validator_mac.mm +++ b/content/browser/compositor/browser_compositor_overlay_candidate_validator_mac.mm @@ -28,7 +28,6 @@ BrowserCompositorOverlayCandidateValidatorMac:: void BrowserCompositorOverlayCandidateValidatorMac::GetStrategies( cc::OverlayProcessor::StrategyList* strategies) { - strategies->push_back(make_scoped_ptr(new cc::OverlayStrategySandwich(this))); } bool BrowserCompositorOverlayCandidateValidatorMac::AllowCALayerOverlays() { diff --git a/content/common/gpu/ca_layer_tree_mac.h b/content/common/gpu/ca_layer_tree_mac.h new file mode 100644 index 0000000..163474e --- /dev/null +++ b/content/common/gpu/ca_layer_tree_mac.h @@ -0,0 +1,188 @@ +// 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_TREE_MAC_H_ +#define CONTENT_COMMON_GPU_CA_LAYER_TREE_MAC_H_ + +#include <IOSurface/IOSurface.h> +#include <QuartzCore/QuartzCore.h> +#include <deque> +#include <vector> + +#include "base/mac/scoped_cftyperef.h" +#include "base/mac/scoped_nsobject.h" +#include "base/memory/scoped_ptr.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/rect_f.h" +#include "ui/gfx/transform.h" + +namespace content { + +// The CALayerTree will construct a hierarchy of CALayers from a linear list, +// using the algorithm and structure referenced described in +// https://docs.google.com/document/d/1DtSN9zzvCF44_FQPM7ie01UxGHagQ66zfF5L9HnigQY/edit?usp=sharing +class CALayerTree { + public: + CALayerTree(); + + // This will remove all CALayers from this tree from their superlayer. + ~CALayerTree(); + + // Append the description of a new CALayer to the tree. This will not + // create any new CALayers until CommitScheduledCALayers is called. This + // cannot be called anymore after CommitScheduledCALayers has been called. + bool ScheduleCALayer(bool is_clipped, + const gfx::Rect& clip_rect, + unsigned sorting_context_id, + const gfx::Transform& transform, + base::ScopedCFTypeRef<IOSurfaceRef> io_surface, + const gfx::RectF& contents_rect, + const gfx::Rect& rect, + unsigned background_color, + unsigned edge_aa_mask, + float opacity); + + // Create a CALayer tree for the scheduled layers, and set |superlayer| to + // have only this tree as its sublayers. If |old_tree| is non-null, then try + // to re-use the CALayers of |old_tree| as much as possible. |old_tree| will + // be destroyed at the end of the function, and any CALayers in it which were + // not re-used by |this| will be removed from the CALayer hierarchy. + void CommitScheduledCALayers(CALayer* superlayer, + scoped_ptr<CALayerTree> old_tree, + float scale_factor); + + private: + struct RootLayer; + struct ClipAndSortingLayer; + struct TransformLayer; + struct ContentLayer; + + struct RootLayer { + RootLayer(); + + // This will remove |ca_layer| from its superlayer, if |ca_layer| is + // non-nil. + ~RootLayer(); + + // Append a new content layer, without modifying the actual CALayer + // structure. + bool AddContentLayer(bool is_clipped, + const gfx::Rect& clip_rect, + unsigned sorting_context_id, + const gfx::Transform& transform, + base::ScopedCFTypeRef<IOSurfaceRef> io_surface, + const gfx::RectF& contents_rect, + const gfx::Rect& rect, + unsigned background_color, + unsigned edge_aa_mask, + float opacity); + + // Allocate CALayers for this layer and its children, and set their + // properties appropriately. Re-use the CALayers from |old_layer| if + // possible. If re-using a CALayer from |old_layer|, reset its |ca_layer| + // to nil, so that its destructor will not remove an active CALayer. + void CommitToCA(CALayer* superlayer, + RootLayer* old_layer, + float scale_factor); + + std::vector<ClipAndSortingLayer> clip_and_sorting_layers; + base::scoped_nsobject<CALayer> ca_layer; + + private: + DISALLOW_COPY_AND_ASSIGN(RootLayer); + }; + struct ClipAndSortingLayer { + ClipAndSortingLayer(bool is_clipped, + gfx::Rect clip_rect, + unsigned sorting_context_id, + bool is_singleton_sorting_context); + ClipAndSortingLayer(ClipAndSortingLayer&& layer); + + // See the behavior of RootLayer for the effects of these functions on the + // |ca_layer| member and |old_layer| argument. + ~ClipAndSortingLayer(); + void AddContentLayer(const gfx::Transform& transform, + base::ScopedCFTypeRef<IOSurfaceRef> io_surface, + const gfx::RectF& contents_rect, + const gfx::Rect& rect, + unsigned background_color, + unsigned edge_aa_mask, + float opacity); + void CommitToCA(CALayer* superlayer, + ClipAndSortingLayer* old_layer, + float scale_factor); + + std::vector<TransformLayer> transform_layers; + bool is_clipped = false; + gfx::Rect clip_rect; + unsigned sorting_context_id = 0; + bool is_singleton_sorting_context = false; + base::scoped_nsobject<CALayer> ca_layer; + + private: + DISALLOW_COPY_AND_ASSIGN(ClipAndSortingLayer); + }; + struct TransformLayer { + TransformLayer(const gfx::Transform& transform); + TransformLayer(TransformLayer&& layer); + + // See the behavior of RootLayer for the effects of these functions on the + // |ca_layer| member and |old_layer| argument. + ~TransformLayer(); + void AddContentLayer(base::ScopedCFTypeRef<IOSurfaceRef> io_surface, + const gfx::RectF& contents_rect, + const gfx::Rect& rect, + unsigned background_color, + unsigned edge_aa_mask, + float opacity); + void CommitToCA(CALayer* superlayer, + TransformLayer* old_layer, + float scale_factor); + + gfx::Transform transform; + std::vector<ContentLayer> content_layers; + base::scoped_nsobject<CALayer> ca_layer; + + private: + DISALLOW_COPY_AND_ASSIGN(TransformLayer); + }; + struct ContentLayer { + ContentLayer(base::ScopedCFTypeRef<IOSurfaceRef> io_surface, + const gfx::RectF& contents_rect, + const gfx::Rect& rect, + unsigned background_color, + unsigned edge_aa_mask, + float opacity); + ContentLayer(ContentLayer&& layer); + + // See the behavior of RootLayer for the effects of these functions on the + // |ca_layer| member and |old_layer| argument. + ~ContentLayer(); + void CommitToCA(CALayer* parent, + ContentLayer* old_layer, + float scale_factor); + + base::ScopedCFTypeRef<IOSurfaceRef> io_surface; + gfx::RectF contents_rect; + gfx::Rect rect; + unsigned background_color = 0; + unsigned edge_aa_mask = 0; + float opacity = 1; + base::scoped_nsobject<CALayer> ca_layer; + + private: + DISALLOW_COPY_AND_ASSIGN(ContentLayer); + }; + + RootLayer root_layer_; + float scale_factor_ = 1; + bool has_committed_ = false; + + private: + DISALLOW_COPY_AND_ASSIGN(CALayerTree); +}; + +} // namespace content + +#endif // CONTENT_COMMON_GPU_CA_LAYER_TREE_MAC_H_ diff --git a/content/common/gpu/ca_layer_tree_mac.mm b/content/common/gpu/ca_layer_tree_mac.mm new file mode 100644 index 0000000..47343c4 --- /dev/null +++ b/content/common/gpu/ca_layer_tree_mac.mm @@ -0,0 +1,402 @@ +// 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_tree_mac.h" + +#include "base/command_line.h" +#include "base/mac/sdk_forward_declarations.h" +#include "third_party/skia/include/core/SkColor.h" +#include "ui/base/cocoa/animation_utils.h" +#include "ui/base/ui_base_switches.h" +#include "ui/gfx/geometry/dip_util.h" + +namespace content { + +CALayerTree::CALayerTree() {} +CALayerTree::~CALayerTree() {} + +bool CALayerTree::ScheduleCALayer( + bool is_clipped, + const gfx::Rect& clip_rect, + unsigned sorting_context_id, + const gfx::Transform& transform, + base::ScopedCFTypeRef<IOSurfaceRef> io_surface, + const gfx::RectF& contents_rect, + const gfx::Rect& rect, + unsigned background_color, + unsigned edge_aa_mask, + float opacity) { + if (has_committed_) { + DLOG(ERROR) << "ScheduleCALayer called after CommitScheduledCALayers."; + return false; + } + return root_layer_.AddContentLayer(is_clipped, clip_rect, sorting_context_id, + transform, io_surface, contents_rect, rect, + background_color, edge_aa_mask, opacity); +} + +void CALayerTree::CommitScheduledCALayers(CALayer* superlayer, + scoped_ptr<CALayerTree> old_tree, + float scale_factor) { + RootLayer* old_root_layer = nullptr; + if (old_tree) { + DCHECK(old_tree->has_committed_); + if (old_tree->scale_factor_ == scale_factor) + old_root_layer = &old_tree->root_layer_; + } + + root_layer_.CommitToCA(superlayer, old_root_layer, scale_factor); + // If there are any extra CALayers in |old_tree| that were not stolen by this + // tree, they will be removed from the CALayer tree in this deallocation. + old_tree.reset(); + has_committed_ = true; + scale_factor_ = scale_factor; +} + +CALayerTree::RootLayer::RootLayer() {} + +// Note that for all destructors, the the CALayer will have been reset to nil if +// another layer has taken it. +CALayerTree::RootLayer::~RootLayer() { + [ca_layer removeFromSuperlayer]; +} + +CALayerTree::ClipAndSortingLayer::ClipAndSortingLayer( + bool is_clipped, + gfx::Rect clip_rect, + unsigned sorting_context_id, + bool is_singleton_sorting_context) + : is_clipped(is_clipped), + clip_rect(clip_rect), + sorting_context_id(sorting_context_id), + is_singleton_sorting_context(is_singleton_sorting_context) {} + +CALayerTree::ClipAndSortingLayer::ClipAndSortingLayer( + ClipAndSortingLayer&& layer) + : transform_layers(std::move(layer.transform_layers)), + is_clipped(layer.is_clipped), + clip_rect(layer.clip_rect), + sorting_context_id(layer.sorting_context_id), + is_singleton_sorting_context( + layer.is_singleton_sorting_context), + ca_layer(layer.ca_layer) { + // Ensure that the ca_layer be reset, so that when the destructor is called, + // the layer hierarchy is unaffected. + // TODO(ccameron): Add a move constructor for scoped_nsobject to do this + // automatically. + layer.ca_layer.reset(); +} + +CALayerTree::ClipAndSortingLayer::~ClipAndSortingLayer() { + [ca_layer removeFromSuperlayer]; +} + +CALayerTree::TransformLayer::TransformLayer(const gfx::Transform& transform) + : transform(transform) {} + +CALayerTree::TransformLayer::TransformLayer(TransformLayer&& layer) + : transform(layer.transform), + content_layers(std::move(layer.content_layers)), + ca_layer(layer.ca_layer) { + layer.ca_layer.reset(); +} + +CALayerTree::TransformLayer::~TransformLayer() { + [ca_layer removeFromSuperlayer]; +} + +CALayerTree::ContentLayer::ContentLayer( + base::ScopedCFTypeRef<IOSurfaceRef> io_surface, + const gfx::RectF& contents_rect, + const gfx::Rect& rect, + unsigned background_color, + unsigned edge_aa_mask, + float opacity) + : io_surface(io_surface), + contents_rect(contents_rect), + rect(rect), + background_color(background_color), + edge_aa_mask(edge_aa_mask), + opacity(opacity) {} + +CALayerTree::ContentLayer::ContentLayer(ContentLayer&& layer) + : io_surface(layer.io_surface), + contents_rect(layer.contents_rect), + rect(layer.rect), + background_color(layer.background_color), + edge_aa_mask(layer.edge_aa_mask), + opacity(layer.opacity), + ca_layer(layer.ca_layer) { + DCHECK(!layer.ca_layer); + layer.io_surface.reset(); + layer.ca_layer.reset(); +} + +CALayerTree::ContentLayer::~ContentLayer() { + [ca_layer removeFromSuperlayer]; +} + +bool CALayerTree::RootLayer::AddContentLayer( + bool is_clipped, + const gfx::Rect& clip_rect, + unsigned sorting_context_id, + const gfx::Transform& transform, + base::ScopedCFTypeRef<IOSurfaceRef> io_surface, + const gfx::RectF& contents_rect, + const gfx::Rect& rect, + unsigned background_color, + unsigned edge_aa_mask, + float opacity) { + bool needs_new_clip_and_sorting_layer = true; + + // In sorting_context_id 0, all quads are listed in back-to-front order. + // This is accomplished by having the CALayers be siblings of each other. + // If a quad has a 3D transform, it is necessary to put it in its own sorting + // context, so that it will not intersect with quads before and after it. + bool is_singleton_sorting_context = + !sorting_context_id && !transform.IsFlat(); + + if (!clip_and_sorting_layers.empty()) { + ClipAndSortingLayer& current_layer = clip_and_sorting_layers.back(); + // It is in error to change the clipping settings within a non-zero sorting + // context. The result will be incorrect layering and intersection. + if (sorting_context_id && + current_layer.sorting_context_id == sorting_context_id && + (current_layer.is_clipped != is_clipped || + current_layer.clip_rect != clip_rect)) { + DLOG(ERROR) << "CALayer changed clip inside non-zero sorting context."; + return false; + } + if (!is_singleton_sorting_context && + !current_layer.is_singleton_sorting_context && + current_layer.is_clipped == is_clipped && + current_layer.clip_rect == clip_rect && + current_layer.sorting_context_id == sorting_context_id) { + needs_new_clip_and_sorting_layer = false; + } + } + if (needs_new_clip_and_sorting_layer) { + clip_and_sorting_layers.push_back( + ClipAndSortingLayer(is_clipped, clip_rect, sorting_context_id, + is_singleton_sorting_context)); + } + clip_and_sorting_layers.back().AddContentLayer( + transform, io_surface, contents_rect, rect, background_color, + edge_aa_mask, opacity); + return true; +} + +void CALayerTree::ClipAndSortingLayer::AddContentLayer( + const gfx::Transform& transform, + base::ScopedCFTypeRef<IOSurfaceRef> io_surface, + const gfx::RectF& contents_rect, + const gfx::Rect& rect, + unsigned background_color, + unsigned edge_aa_mask, + float opacity) { + bool needs_new_transform_layer = true; + if (!transform_layers.empty()) { + const TransformLayer& current_layer = transform_layers.back(); + if (current_layer.transform == transform) + needs_new_transform_layer = false; + } + if (needs_new_transform_layer) + transform_layers.push_back(TransformLayer(transform)); + transform_layers.back().AddContentLayer( + io_surface, contents_rect, rect, background_color, edge_aa_mask, opacity); +} + +void CALayerTree::TransformLayer::AddContentLayer( + base::ScopedCFTypeRef<IOSurfaceRef> io_surface, + const gfx::RectF& contents_rect, + const gfx::Rect& rect, + unsigned background_color, + unsigned edge_aa_mask, + float opacity) { + content_layers.push_back(ContentLayer(io_surface, contents_rect, rect, + background_color, edge_aa_mask, + opacity)); +} + +void CALayerTree::RootLayer::CommitToCA(CALayer* superlayer, + RootLayer* old_layer, + float scale_factor) { + if (old_layer) { + DCHECK(old_layer->ca_layer); + std::swap(ca_layer, old_layer->ca_layer); + } else { + ca_layer.reset([[CALayer alloc] init]); + [ca_layer setAnchorPoint:CGPointZero]; + [superlayer setSublayers:nil]; + [superlayer addSublayer:ca_layer]; + [superlayer setBorderWidth:0]; + } + DCHECK_EQ([ca_layer superlayer], superlayer); + + for (size_t i = 0; i < clip_and_sorting_layers.size(); ++i) { + ClipAndSortingLayer* old_clip_and_sorting_layer = nullptr; + if (old_layer && i < old_layer->clip_and_sorting_layers.size()) { + old_clip_and_sorting_layer = &old_layer->clip_and_sorting_layers[i]; + } + clip_and_sorting_layers[i].CommitToCA( + ca_layer.get(), old_clip_and_sorting_layer, scale_factor); + } +} + +void CALayerTree::ClipAndSortingLayer::CommitToCA( + CALayer* superlayer, + ClipAndSortingLayer* old_layer, + float scale_factor) { + bool update_is_clipped = true; + bool update_clip_rect = true; + if (old_layer) { + DCHECK(old_layer->ca_layer); + std::swap(ca_layer, old_layer->ca_layer); + update_is_clipped = old_layer->is_clipped != is_clipped; + update_clip_rect = old_layer->clip_rect != clip_rect; + } else { + ca_layer.reset([[CALayer alloc] init]); + [ca_layer setAnchorPoint:CGPointZero]; + [superlayer addSublayer:ca_layer]; + } + DCHECK_EQ([ca_layer superlayer], superlayer); + + if (update_is_clipped) + [ca_layer setMasksToBounds:is_clipped]; + + if (update_clip_rect) { + if (is_clipped) { + gfx::RectF dip_clip_rect = gfx::RectF(clip_rect); + dip_clip_rect.Scale(1 / scale_factor); + [ca_layer setPosition:CGPointMake(dip_clip_rect.x(), dip_clip_rect.y())]; + [ca_layer setBounds:CGRectMake(0, 0, dip_clip_rect.width(), + dip_clip_rect.height())]; + [ca_layer + setSublayerTransform:CATransform3DMakeTranslation( + -dip_clip_rect.x(), -dip_clip_rect.y(), 0)]; + } else { + [ca_layer setPosition:CGPointZero]; + [ca_layer setBounds:CGRectZero]; + [ca_layer setSublayerTransform:CATransform3DIdentity]; + } + } + + for (size_t i = 0; i < transform_layers.size(); ++i) { + TransformLayer* old_transform_layer = nullptr; + if (old_layer && i < old_layer->transform_layers.size()) + old_transform_layer = &old_layer->transform_layers[i]; + transform_layers[i].CommitToCA(ca_layer.get(), old_transform_layer, + scale_factor); + } +} + +void CALayerTree::TransformLayer::CommitToCA(CALayer* superlayer, + TransformLayer* old_layer, + float scale_factor) { + bool update_transform = true; + if (old_layer) { + DCHECK(old_layer->ca_layer); + std::swap(ca_layer, old_layer->ca_layer); + update_transform = old_layer->transform != transform; + } else { + ca_layer.reset([[CATransformLayer alloc] init]); + [superlayer addSublayer:ca_layer]; + } + DCHECK_EQ([ca_layer superlayer], superlayer); + + if (update_transform) { + gfx::Transform pre_scale; + gfx::Transform post_scale; + pre_scale.Scale(1 / scale_factor, 1 / scale_factor); + post_scale.Scale(scale_factor, scale_factor); + gfx::Transform conjugated_transform = pre_scale * transform * post_scale; + + CATransform3D ca_transform; + conjugated_transform.matrix().asColMajord(&ca_transform.m11); + [ca_layer setTransform:ca_transform]; + } + + for (size_t i = 0; i < content_layers.size(); ++i) { + ContentLayer* old_content_layer = nullptr; + if (old_layer && i < old_layer->content_layers.size()) + old_content_layer = &old_layer->content_layers[i]; + content_layers[i].CommitToCA(ca_layer.get(), old_content_layer, + scale_factor); + } +} + +void CALayerTree::ContentLayer::CommitToCA(CALayer* superlayer, + ContentLayer* old_layer, + float scale_factor) { + bool update_contents = true; + bool update_contents_rect = true; + bool update_rect = true; + bool update_background_color = true; + bool update_edge_aa_mask = true; + bool update_opacity = true; + if (old_layer) { + DCHECK(old_layer->ca_layer); + std::swap(ca_layer, old_layer->ca_layer); + update_contents = old_layer->io_surface != io_surface; + update_contents_rect = old_layer->contents_rect != contents_rect; + update_rect = old_layer->rect != rect; + update_background_color = old_layer->background_color != background_color; + update_edge_aa_mask = old_layer->edge_aa_mask != edge_aa_mask; + update_opacity = old_layer->opacity != opacity; + } else { + ca_layer.reset([[CALayer alloc] init]); + [ca_layer setAnchorPoint:CGPointZero]; + [superlayer addSublayer:ca_layer]; + } + DCHECK_EQ([ca_layer superlayer], superlayer); + bool update_anything = update_contents || update_contents_rect || + update_rect || update_background_color || + update_edge_aa_mask || update_opacity; + + if (update_contents) { + [ca_layer setContents:static_cast<id>(io_surface.get())]; + if ([ca_layer respondsToSelector:(@selector(setContentsScale:))]) + [ca_layer setContentsScale:scale_factor]; + } + if (update_contents_rect) + [ca_layer setContentsRect:contents_rect.ToCGRect()]; + if (update_rect) { + gfx::RectF dip_rect = gfx::RectF(rect); + dip_rect.Scale(1 / scale_factor); + [ca_layer setPosition:CGPointMake(dip_rect.x(), dip_rect.y())]; + [ca_layer setBounds:CGRectMake(0, 0, dip_rect.width(), dip_rect.height())]; + } + if (update_background_color) { + CGFloat rgba_color_components[4] = { + SkColorGetR(background_color) / 255., + SkColorGetG(background_color) / 255., + SkColorGetB(background_color) / 255., + SkColorGetA(background_color) / 255., + }; + base::ScopedCFTypeRef<CGColorRef> srgb_background_color(CGColorCreate( + CGColorSpaceCreateWithName(kCGColorSpaceSRGB), rgba_color_components)); + [ca_layer setBackgroundColor:srgb_background_color]; + } + if (update_edge_aa_mask) + [ca_layer setEdgeAntialiasingMask:edge_aa_mask]; + if (update_opacity) + [ca_layer setOpacity:opacity]; + + static bool show_borders = base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kShowMacOverlayBorders); + if (show_borders) { + base::ScopedCFTypeRef<CGColorRef> color; + if (update_anything) { + // Pink represents a CALayer that changed this frame. + color.reset(CGColorCreateGenericRGB(1, 0, 1, 1)); + } else { + // Grey represents a CALayer that has not changed. + color.reset(CGColorCreateGenericRGB(0, 0, 0, 0.1)); + } + [ca_layer setBorderWidth:1]; + [ca_layer setBorderColor:color]; + } +} + +} // 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 66ce211..5b86a5e9 100644 --- a/content/common/gpu/image_transport_surface_overlay_mac.h +++ b/content/common/gpu/image_transport_surface_overlay_mac.h @@ -21,6 +21,8 @@ namespace content { +class CALayerTree; + class ImageTransportSurfaceOverlayMac : public gfx::GLSurface, public ImageTransportSurface, public ui::GpuSwitchingObserver { @@ -53,10 +55,11 @@ class ImageTransportSurfaceOverlayMac : public gfx::GLSurface, float opacity, unsigned background_color, unsigned edge_aa_mask, - const gfx::RectF& bounds_rect, + const gfx::RectF& rect, bool is_clipped, const gfx::RectF& clip_rect, - const gfx::Transform& transform) override; + const gfx::Transform& transform, + int sorting_context_id) override; bool IsSurfaceless() const override; // ImageTransportSurface implementation @@ -78,9 +81,9 @@ class ImageTransportSurfaceOverlayMac : public gfx::GLSurface, void UpdateRootAndPartialDamagePlanes( const linked_ptr<OverlayPlane>& new_root_plane, const gfx::RectF& pixel_damage_rect); - void UpdateOverlayPlanes( - const std::vector<linked_ptr<OverlayPlane>>& new_overlay_planes); - void UpdateCALayerTree(); + 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|. @@ -123,7 +126,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_; - std::vector<linked_ptr<OverlayPlane>> pending_overlay_planes_; + scoped_ptr<CALayerTree> pending_ca_layer_tree_; // A queue of all frames that have been created by SwapBuffersInternal but // have not yet been displayed. This queue is checked at the beginning of @@ -133,7 +136,7 @@ class ImageTransportSurfaceOverlayMac : public gfx::GLSurface, // 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_; - std::list<linked_ptr<OverlayPlane>> current_overlay_planes_; + scoped_ptr<CALayerTree> current_ca_layer_tree_; // The time of the last swap was issued. If this is more than two vsyncs, then // use the simpler non-smooth animation path. diff --git a/content/common/gpu/image_transport_surface_overlay_mac.mm b/content/common/gpu/image_transport_surface_overlay_mac.mm index 03c2eed..724f135 100644 --- a/content/common/gpu/image_transport_surface_overlay_mac.mm +++ b/content/common/gpu/image_transport_surface_overlay_mac.mm @@ -22,12 +22,14 @@ typedef void* GLeglImageOES; #include "base/command_line.h" #include "base/mac/scoped_cftyperef.h" +#include "base/mac/sdk_forward_declarations.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/dip_util.h" +#include "ui/gfx/geometry/rect_conversions.h" #include "ui/gfx/transform.h" #include "ui/gl/gl_context.h" #include "ui/gl/gl_fence.h" @@ -96,89 +98,53 @@ class ImageTransportSurfaceOverlayMac::OverlayPlane { public: static linked_ptr<OverlayPlane> CreateWithFrameRect( int z_order, - int io_surface_id, base::ScopedCFTypeRef<IOSurfaceRef> 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<OverlayPlane>( - new OverlayPlane(z_order, io_surface_id, io_surface, contents_rect, 1.f, - base::ScopedCFTypeRef<CGColorRef>(), 0, - pixel_frame_rect.size(), transform, pixel_frame_rect)); + new OverlayPlane(z_order, io_surface, contents_rect, pixel_frame_rect)); } - static linked_ptr<OverlayPlane> CreateWithTransform( - int z_order, - int io_surface_id, - base::ScopedCFTypeRef<IOSurfaceRef> io_surface, - const gfx::RectF& contents_rect, - float opacity, - base::ScopedCFTypeRef<CGColorRef> background_color, - unsigned int edge_aa_mask, - const gfx::SizeF& bounds_size, - const gfx::Transform& transform) { - gfx::RectF pixel_frame_rect = gfx::RectF(bounds_size); - transform.TransformRect(&pixel_frame_rect); - return linked_ptr<OverlayPlane>(new OverlayPlane( - z_order, io_surface_id, io_surface, contents_rect, opacity, - background_color, edge_aa_mask, bounds_size, transform, - pixel_frame_rect)); - } - - ~OverlayPlane() { DCHECK(!ca_layer); } + ~OverlayPlane() { + [ca_layer setContents:nil]; + [ca_layer removeFromSuperlayer]; + ca_layer.reset(); + } const int z_order; - base::scoped_nsobject<CALayer> ca_layer; - - // The IOSurface to set the CALayer's contents to. - const int io_surface_id; const base::ScopedCFTypeRef<IOSurfaceRef> io_surface; const gfx::RectF contents_rect; - float opacity; - const base::ScopedCFTypeRef<CGColorRef> background_color; - unsigned int edge_aa_mask; - const gfx::SizeF bounds_size; - const gfx::Transform transform; - const gfx::RectF pixel_frame_rect; - bool layer_needs_update; - - static bool Compare(const linked_ptr<OverlayPlane>& a, - const linked_ptr<OverlayPlane>& b) { - return (a->z_order < b->z_order); - } + base::scoped_nsobject<CALayer> ca_layer; void TakeCALayerFrom(OverlayPlane* other_plane) { ca_layer.swap(other_plane->ca_layer); } - void UpdateProperties() { + 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 && z_order == 0) { + if ([ca_layer contents] == new_contents && z_order == 0) [ca_layer setContentsChanged]; - } else { + else [ca_layer setContents:new_contents]; - } [ca_layer setContentsRect:contents_rect.ToCGRect()]; - [ca_layer setOpacity:opacity]; - if (background_color) { - [ca_layer setBackgroundColor:background_color]; - } else { - [ca_layer setBackgroundColor:CGColorGetConstantColor(kCGColorClear)]; - } - [ca_layer setEdgeAntialiasingMask:edge_aa_mask]; - [ca_layer setAnchorPoint:CGPointZero]; - [ca_layer setBounds:gfx::RectF(bounds_size).ToCGRect()]; - CATransform3D ca_transform; - transform.matrix().asColMajord(&ca_transform.m11); - [ca_layer setTransform:ca_transform]; + + 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( @@ -188,9 +154,6 @@ class ImageTransportSurfaceOverlayMac::OverlayPlane { if (!layer_needs_update) { // Green represents contents that are unchanged across frames. color.reset(CGColorCreateGenericRGB(0, 1, 0, 1)); - } else if (z_order != 0) { - // Pink represents overlay planes - color.reset(CGColorCreateGenericRGB(1, 0, 1, 1)); } else { // Red represents damaged contents. color.reset(CGColorCreateGenericRGB(1, 0, 0, 1)); @@ -201,34 +164,14 @@ class ImageTransportSurfaceOverlayMac::OverlayPlane { layer_needs_update = false; } - void Destroy() { - if (!ca_layer) - return; - [ca_layer setContents:nil]; - [ca_layer removeFromSuperlayer]; - ca_layer.reset(); - } - private: OverlayPlane(int z_order, - int io_surface_id, base::ScopedCFTypeRef<IOSurfaceRef> io_surface, const gfx::RectF& contents_rect, - float opacity, - base::ScopedCFTypeRef<CGColorRef> background_color, - unsigned edge_aa_mask, - const gfx::SizeF& bounds_size, - const gfx::Transform& transform, const gfx::RectF& pixel_frame_rect) : z_order(z_order), - io_surface_id(io_surface_id), io_surface(io_surface), contents_rect(contents_rect), - opacity(opacity), - background_color(background_color), - edge_aa_mask(edge_aa_mask), - bounds_size(bounds_size), - transform(transform), pixel_frame_rect(pixel_frame_rect), layer_needs_update(true) {} }; @@ -243,7 +186,7 @@ class ImageTransportSurfaceOverlayMac::PendingSwap { gfx::Rect pixel_damage_rect; linked_ptr<OverlayPlane> root_plane; - std::vector<linked_ptr<OverlayPlane>> overlay_planes; + scoped_ptr<CALayerTree> ca_layer_tree; std::vector<ui::LatencyInfo> latency_info; // A fence object, and the CGL context it was issued in. @@ -300,16 +243,8 @@ bool ImageTransportSurfaceOverlayMac::Initialize() { void ImageTransportSurfaceOverlayMac::Destroy() { DisplayAndClearAllPendingSwaps(); - - if (current_root_plane_.get()) - current_root_plane_->Destroy(); - current_root_plane_.reset(); - for (auto& plane : current_partial_damage_planes_) - plane->Destroy(); current_partial_damage_planes_.clear(); - for (auto& plane : current_overlay_planes_) - plane->Destroy(); - current_overlay_planes_.clear(); + current_root_plane_.reset(); } bool ImageTransportSurfaceOverlayMac::IsOffscreen() { @@ -352,7 +287,7 @@ gfx::SwapResult ImageTransportSurfaceOverlayMac::SwapBuffersInternal( new_swap->pixel_damage_rect = pixel_damage_rect; new_swap->root_plane = pending_root_plane_; pending_root_plane_ = linked_ptr<OverlayPlane>(); - new_swap->overlay_planes.swap(pending_overlay_planes_); + new_swap->ca_layer_tree.swap(pending_ca_layer_tree_); new_swap->latency_info.swap(latency_info_); // A flush is required to ensure that all content appears in the layer. @@ -436,16 +371,10 @@ void ImageTransportSurfaceOverlayMac::DisplayFirstPendingSwapImmediately() { // 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); - std::sort(swap->overlay_planes.begin(), swap->overlay_planes.end(), - OverlayPlane::Compare); - for (auto& plane : swap->overlay_planes) - pixel_damage_rect.Subtract(plane->pixel_frame_rect); - ScopedCAActionDisabler disabler; UpdateRootAndPartialDamagePlanes(swap->root_plane, pixel_damage_rect); - UpdateOverlayPlanes(swap->overlay_planes); - UpdateCALayerTree(); - swap->overlay_planes.clear(); + UpdateRootAndPartialDamageCALayers(swap->scale_factor); + UpdateCALayerTree(std::move(swap->ca_layer_tree), swap->scale_factor); } // Update the latency info to reflect the swap time. @@ -475,27 +404,6 @@ void ImageTransportSurfaceOverlayMac::DisplayFirstPendingSwapImmediately() { pending_swaps_.pop_front(); } -void ImageTransportSurfaceOverlayMac::UpdateOverlayPlanes( - const std::vector<linked_ptr<OverlayPlane>>& new_overlay_planes) { - std::list<linked_ptr<OverlayPlane>> old_overlay_planes; - old_overlay_planes.swap(current_overlay_planes_); - - // Move the new overlay planes into the |current_overlay_planes_| list, - // cannibalizing from the old |current_overlay_planes_| as much as possible. - for (auto& new_plane : new_overlay_planes) { - if (!old_overlay_planes.empty()) { - new_plane->TakeCALayerFrom(old_overlay_planes.front().get()); - old_overlay_planes.pop_front(); - } - current_overlay_planes_.push_back(new_plane); - } - - // Destroy any of the previous |current_overlay_planes_| that we couldn't - // cannibalize. - for (auto& old_plane : old_overlay_planes) - old_plane->Destroy(); -} - void ImageTransportSurfaceOverlayMac::UpdateRootAndPartialDamagePlanes( const linked_ptr<OverlayPlane>& new_root_plane, const gfx::RectF& pixel_damage_rect) { @@ -503,12 +411,9 @@ void ImageTransportSurfaceOverlayMac::UpdateRootAndPartialDamagePlanes( old_partial_damage_planes.swap(current_partial_damage_planes_); linked_ptr<OverlayPlane> plane_for_swap; - // If there is no new root plane, destroy the old one. + // If there is no new root plane, remove everything. if (!new_root_plane.get()) { - for (auto& old_plane : old_partial_damage_planes) - old_plane->Destroy(); - if (current_root_plane_.get()) - current_root_plane_->Destroy(); + old_partial_damage_planes.clear(); current_root_plane_.reset(); return; } @@ -554,8 +459,8 @@ void ImageTransportSurfaceOverlayMac::UpdateRootAndPartialDamagePlanes( 1. / new_root_plane->pixel_frame_rect.height()); plane_for_swap = OverlayPlane::CreateWithFrameRect( - 0, new_root_plane->io_surface_id, new_root_plane->io_surface, - plane_to_reuse_dip_enlarged_rect, enlarged_contents_rect); + 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()) @@ -571,8 +476,7 @@ void ImageTransportSurfaceOverlayMac::UpdateRootAndPartialDamagePlanes( 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_id, new_root_plane->io_surface, - pixel_damage_rect, contents_rect); + 0, new_root_plane->io_surface, pixel_damage_rect, contents_rect); } // And if we still don't have a layer, use the root layer. @@ -588,10 +492,12 @@ void ImageTransportSurfaceOverlayMac::UpdateRootAndPartialDamagePlanes( 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->Destroy(); - } else { + 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); } @@ -599,36 +505,27 @@ void ImageTransportSurfaceOverlayMac::UpdateRootAndPartialDamagePlanes( // Finally, add the new swap's plane at the back of the list, if it exists. if (plane_for_swap == new_root_plane) { - if (current_root_plane_.get()) { - plane_for_swap->TakeCALayerFrom(current_root_plane_.get()); - } else { - plane_for_swap->ca_layer = ca_root_layer_; - } current_root_plane_ = new_root_plane; } else if (plane_for_swap.get()) { current_partial_damage_planes_.push_back(plane_for_swap); } } -void ImageTransportSurfaceOverlayMac::UpdateCALayerTree() { +void ImageTransportSurfaceOverlayMac::UpdateRootAndPartialDamageCALayers( + float scale_factor) { if (!use_remote_layer_api_) { - DCHECK(current_overlay_planes_.empty()); DCHECK(current_partial_damage_planes_.empty()); return; } - // Allocate new CALayers as needed. Overlay layers are always added to the - // back of the list. - CALayer* first_overlay_ca_layer = nil; - for (auto& plane : current_overlay_planes_) { - if (!plane->ca_layer) { - plane->ca_layer.reset([[CALayer alloc] init]); - [ca_root_layer_ addSublayer:plane->ca_layer]; + // 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]; } - if (!first_overlay_ca_layer) - first_overlay_ca_layer = plane->ca_layer; } - // Partial damage layers are inserted below the overlay layers. for (auto& plane : current_partial_damage_planes_) { if (!plane->ca_layer) { DCHECK(plane == current_partial_damage_planes_.back()); @@ -636,28 +533,26 @@ void ImageTransportSurfaceOverlayMac::UpdateCALayerTree() { } if (![plane->ca_layer superlayer]) { DCHECK(plane == current_partial_damage_planes_.back()); - if (first_overlay_ca_layer) { - [ca_root_layer_ insertSublayer:plane->ca_layer - below:first_overlay_ca_layer]; - } else { - [ca_root_layer_ addSublayer:plane->ca_layer]; - } + [ca_root_layer_ addSublayer:plane->ca_layer]; } } - - // Update CALayer contents, frames, and borders. if (current_root_plane_.get()) - current_root_plane_->UpdateProperties(); + current_root_plane_->UpdateProperties(scale_factor); for (auto& plane : current_partial_damage_planes_) - plane->UpdateProperties(); - for (auto& plane : current_overlay_planes_) - plane->UpdateProperties(); - [ca_root_layer_ setTransform:CATransform3DMakeScale(1 / scale_factor_, - 1 / scale_factor_, 1)]; + plane->UpdateProperties(scale_factor); +} - DCHECK_EQ( - static_cast<size_t>([[ca_root_layer_ sublayers] count]), - current_partial_damage_planes_.size() + current_overlay_planes_.size()); +void ImageTransportSurfaceOverlayMac::UpdateCALayerTree( + scoped_ptr<CALayerTree> 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() { @@ -742,19 +637,18 @@ bool ImageTransportSurfaceOverlayMac::ScheduleOverlayPlane( gl::GLImage* image, const gfx::Rect& pixel_frame_rect, const gfx::RectF& crop_rect) { - DCHECK_EQ(transform, gfx::OVERLAY_TRANSFORM_NONE); - if (transform != gfx::OVERLAY_TRANSFORM_NONE) + if (transform != gfx::OVERLAY_TRANSFORM_NONE) { + DLOG(ERROR) << "Invalid overlay plane transform."; return false; + } + if (z_order) { + DLOG(ERROR) << "Invalid non-zero Z order."; + return false; + } - linked_ptr<OverlayPlane> plane = OverlayPlane::CreateWithFrameRect( - z_order, static_cast<gl::GLImageIOSurface*>(image)->io_surface_id().id, - static_cast<gl::GLImageIOSurface*>(image)->io_surface(), + pending_root_plane_ = OverlayPlane::CreateWithFrameRect( + z_order, static_cast<gl::GLImageIOSurface*>(image)->io_surface(), gfx::RectF(pixel_frame_rect), crop_rect); - if (z_order == 0) - pending_root_plane_ = plane; - else - pending_overlay_planes_.push_back(plane); - return true; } @@ -764,35 +658,22 @@ bool ImageTransportSurfaceOverlayMac::ScheduleCALayer( float opacity, unsigned background_color, unsigned edge_aa_mask, - const gfx::RectF& bounds_rect, + const gfx::RectF& rect, bool is_clipped, const gfx::RectF& clip_rect, - const gfx::Transform& transform) { - // Extract the IOSurface, if this layer is not just a solid color. - int io_surface_id = 0; + const gfx::Transform& transform, + int sorting_context_id) { base::ScopedCFTypeRef<IOSurfaceRef> io_surface; if (contents_image) { - io_surface_id = - static_cast<gl::GLImageIOSurface*>(contents_image)->io_surface_id().id; io_surface = static_cast<gl::GLImageIOSurface*>(contents_image)->io_surface(); } - - // Convert the RGBA SkColor to an sRGB CGColorRef. - CGFloat rgba_color_components[4] = { - SkColorGetR(background_color) / 255., - SkColorGetG(background_color) / 255., - SkColorGetB(background_color) / 255., - SkColorGetA(background_color) / 255., - }; - base::ScopedCFTypeRef<CGColorRef> srgb_background_color(CGColorCreate( - CGColorSpaceCreateWithName(kCGColorSpaceSRGB), rgba_color_components)); - - pending_overlay_planes_.push_back(OverlayPlane::CreateWithTransform( - next_ca_layer_z_order_++, io_surface_id, io_surface, contents_rect, - opacity, srgb_background_color, edge_aa_mask, bounds_rect.size(), - transform)); - return true; + if (!pending_ca_layer_tree_) + pending_ca_layer_tree_.reset(new CALayerTree); + return pending_ca_layer_tree_->ScheduleCALayer( + is_clipped, gfx::ToEnclosingRect(clip_rect), sorting_context_id, + transform, io_surface, contents_rect, gfx::ToEnclosingRect(rect), + background_color, edge_aa_mask, opacity); } bool ImageTransportSurfaceOverlayMac::IsSurfaceless() const { diff --git a/content/content_common.gypi b/content/content_common.gypi index f7cfbdd..f3e8820 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_tree_mac.h', + 'common/gpu/ca_layer_tree_mac.mm', 'common/gpu/child_window_surface_win.cc', 'common/gpu/child_window_surface_win.h', 'common/gpu/gpu_channel.cc', diff --git a/content/test/gpu/gpu_tests/pixel_expectations.py b/content/test/gpu/gpu_tests/pixel_expectations.py index 0ce1f6d..e4875a4 100644 --- a/content/test/gpu/gpu_tests/pixel_expectations.py +++ b/content/test/gpu/gpu_tests/pixel_expectations.py @@ -19,3 +19,7 @@ class PixelExpectations(GpuTestExpectations): ['mac'], bug=540039) self.Fail('Pixel.WebGLGreenTriangleES3', ['mac', ('intel', 0x116)], bug=540531) + + # TODO(ccameron): Remove suppression after rebaseline. + self.Fail('Pixel.CSS3DBlueBox', ['mac'], bug=533690) + self.Fail('Pixel.CSS3DBlueBoxES3', ['mac'], bug=533690) diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc index 9538614..90d3062 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc @@ -9644,7 +9644,7 @@ error::Error GLES2DecoderImpl::HandleScheduleCALayerCHROMIUM( if (!surface_->ScheduleCALayer(image, contents_rect, c.opacity, c.background_color, c.edge_aa_mask, bounds_rect, c.is_clipped ? true : false, - clip_rect, transform)) { + clip_rect, transform, c.sorting_context_id)) { LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glScheduleCALayerCHROMIUM", "failed to schedule CALayer"); } diff --git a/ui/gl/gl_surface.cc b/ui/gl/gl_surface.cc index 768bfa9..a20356e 100644 --- a/ui/gl/gl_surface.cc +++ b/ui/gl/gl_surface.cc @@ -209,10 +209,11 @@ bool GLSurface::ScheduleCALayer(gl::GLImage* contents_image, float opacity, unsigned background_color, unsigned edge_aa_mask, - const RectF& bounds_rect, + const RectF& rect, bool is_clipped, const RectF& clip_rect, - const Transform& transform) { + const Transform& transform, + int sorting_content_id) { NOTIMPLEMENTED(); return false; } diff --git a/ui/gl/gl_surface.h b/ui/gl/gl_surface.h index 066435a..d84f391 100644 --- a/ui/gl/gl_surface.h +++ b/ui/gl/gl_surface.h @@ -174,10 +174,11 @@ class GL_EXPORT GLSurface : public base::RefCounted<GLSurface> { float opacity, unsigned background_color, unsigned edge_aa_mask, - const RectF& bounds_rect, + const RectF& rect, bool is_clipped, const RectF& clip_rect, - const Transform& transform); + const Transform& transform, + int sorting_content_id); virtual bool IsSurfaceless() const; |