diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-01-04 14:17:57 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-01-05 10:05:06 +0000 |
commit | 39d357e3248f80abea0159765ff39554affb40db (patch) | |
tree | aba0e6bfb76de0244bba0f5fdbd64b830dd6e621 /chromium/ui/accelerated_widget_mac | |
parent | 87778abf5a1f89266f37d1321b92a21851d8244d (diff) | |
download | qtwebengine-chromium-39d357e3248f80abea0159765ff39554affb40db.tar.gz |
BASELINE: Update Chromium to 55.0.2883.105
And updates ninja to 1.7.2
Change-Id: I20d43c737f82764d857ada9a55586901b18b9243
Reviewed-by: Michael BrĂ¼ning <michael.bruning@qt.io>
Diffstat (limited to 'chromium/ui/accelerated_widget_mac')
7 files changed, 735 insertions, 619 deletions
diff --git a/chromium/ui/accelerated_widget_mac/BUILD.gn b/chromium/ui/accelerated_widget_mac/BUILD.gn index 4e5a22ec31f..64834180917 100644 --- a/chromium/ui/accelerated_widget_mac/BUILD.gn +++ b/chromium/ui/accelerated_widget_mac/BUILD.gn @@ -4,7 +4,6 @@ import("//testing/test.gni") -# GYP version: ui/accelerated_widget_mac/accelerated_widget_mac.gyp:accelerated_widget_mac component("accelerated_widget_mac") { sources = [ "accelerated_widget_mac.h", @@ -39,7 +38,6 @@ component("accelerated_widget_mac") { ] libs = [ - "ApplicationServices.framework", # Temporary hack around https://crbug.com/620127. Remove after https://crbug.com/622481 is fixed. "AVFoundation.framework", "CoreGraphics.framework", "Foundation.framework", @@ -51,7 +49,6 @@ component("accelerated_widget_mac") { ] } -# GYP version: ui/accelerated_widget_mac/accelerated_widget_mac.gyp:accelerated_widget_mac_unittests test("accelerated_widget_mac_unittests") { configs += [ "//build/config:precompiled_headers" ] sources = [ @@ -66,7 +63,7 @@ test("accelerated_widget_mac_unittests") { "//testing/gmock", "//testing/gtest", "//ui/gfx:test_support", - "//ui/gl:test_support", + "//ui/gl", ] libs = [ "AVFoundation.framework", diff --git a/chromium/ui/accelerated_widget_mac/accelerated_widget_mac.gyp b/chromium/ui/accelerated_widget_mac/accelerated_widget_mac.gyp deleted file mode 100644 index 40bbec7882a..00000000000 --- a/chromium/ui/accelerated_widget_mac/accelerated_widget_mac.gyp +++ /dev/null @@ -1,77 +0,0 @@ -# Copyright 2014 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. - -{ - 'variables': { - 'chromium_code': 1, - }, - 'targets': [ - { - # GN version: //ui/accelerated_widget_mac - 'target_name': 'accelerated_widget_mac', - 'type': '<(component)', - 'sources': [ - 'accelerated_widget_mac.h', - 'accelerated_widget_mac.mm', - 'accelerated_widget_mac_export.h', - 'ca_layer_tree_coordinator.h', - 'ca_layer_tree_coordinator.mm', - 'ca_renderer_layer_tree.h', - 'ca_renderer_layer_tree.mm', - 'display_link_mac.cc', - 'display_link_mac.h', - 'fullscreen_low_power_coordinator.h', - 'gl_renderer_layer_tree.h', - 'gl_renderer_layer_tree.mm', - 'io_surface_context.h', - 'io_surface_context.mm', - 'window_resize_helper_mac.cc', - 'window_resize_helper_mac.h', - ], - 'defines': [ - 'ACCELERATED_WIDGET_MAC_IMPLEMENTATION', - ], - 'dependencies': [ - '<(DEPTH)/base/base.gyp:base', - '<(DEPTH)/skia/skia.gyp:skia', - '<(DEPTH)/third_party/khronos/khronos.gyp:khronos_headers', - '<(DEPTH)/ui/base/ui_base.gyp:ui_base', - '<(DEPTH)/ui/events/events.gyp:events_base', - '<(DEPTH)/ui/gfx/gfx.gyp:gfx', - '<(DEPTH)/ui/gfx/gfx.gyp:gfx_geometry', - '<(DEPTH)/ui/gl/gl.gyp:gl', - ], - 'link_settings': { - 'libraries': [ - '$(SDKROOT)/System/Library/Frameworks/AVFoundation.framework', - '$(SDKROOT)/System/Library/Frameworks/CoreMedia.framework', - '$(SDKROOT)/System/Library/Frameworks/QuartzCore.framework', - ], - }, - }, - { - # GN version: //ui/accelerated_widget_mac:accelerated_widget_mac_unittests - 'target_name': 'accelerated_widget_mac_unittests', - 'type': '<(gtest_target_type)', - 'dependencies': [ - '<(DEPTH)/base/base.gyp:base', - '<(DEPTH)/base/base.gyp:run_all_unittests', - '<(DEPTH)/base/base.gyp:test_support_base', - '<(DEPTH)/skia/skia.gyp:skia', - '<(DEPTH)/testing/gmock.gyp:gmock', - '<(DEPTH)/testing/gtest.gyp:gtest', - '<(DEPTH)/ui/gfx/gfx.gyp:gfx_test_support', - 'accelerated_widget_mac', - ], - 'sources': [ - 'ca_layer_tree_unittest_mac.mm', - ], - 'link_settings': { - 'libraries': [ - '$(SDKROOT)/System/Library/Frameworks/QuartzCore.framework', - ], - }, - }, - ], -} diff --git a/chromium/ui/accelerated_widget_mac/ca_layer_tree_coordinator.h b/chromium/ui/accelerated_widget_mac/ca_layer_tree_coordinator.h index 6cd1c493d17..2602c252273 100644 --- a/chromium/ui/accelerated_widget_mac/ca_layer_tree_coordinator.h +++ b/chromium/ui/accelerated_widget_mac/ca_layer_tree_coordinator.h @@ -21,7 +21,8 @@ namespace ui { // to the browser process in https://crbug.com/604052. class ACCELERATED_WIDGET_MAC_EXPORT CALayerTreeCoordinator { public: - explicit CALayerTreeCoordinator(bool allow_remote_layers); + explicit CALayerTreeCoordinator(bool allow_remote_layers, + bool allow_av_sample_buffer_display_layer); ~CALayerTreeCoordinator(); // Set the composited frame's size. @@ -56,7 +57,8 @@ class ACCELERATED_WIDGET_MAC_EXPORT CALayerTreeCoordinator { IOSurfaceRef GetIOSurfaceForDisplay(); private: - bool allow_remote_layers_ = true; + const bool allow_remote_layers_ = true; + const bool allow_av_sample_buffer_display_layer_ = true; gfx::Size pixel_size_; float scale_factor_ = 1; @@ -71,7 +73,10 @@ class ACCELERATED_WIDGET_MAC_EXPORT CALayerTreeCoordinator { // Frame that is currently being displayed on the screen. std::unique_ptr<GLRendererLayerTree> current_gl_renderer_layer_tree_; std::unique_ptr<CARendererLayerTree> current_ca_renderer_layer_tree_; - bool current_fullscreen_low_power_layer_valid_ = false; + + // The number of frames since we used the low power layer. Used to avoid + // flashes during transitions between windows. + uint64_t frames_since_low_power_layer_was_valid_ = 0; DISALLOW_COPY_AND_ASSIGN(CALayerTreeCoordinator); }; diff --git a/chromium/ui/accelerated_widget_mac/ca_layer_tree_coordinator.mm b/chromium/ui/accelerated_widget_mac/ca_layer_tree_coordinator.mm index 5aaeb023ea0..e8a8dc40839 100644 --- a/chromium/ui/accelerated_widget_mac/ca_layer_tree_coordinator.mm +++ b/chromium/ui/accelerated_widget_mac/ca_layer_tree_coordinator.mm @@ -6,20 +6,31 @@ #include <AVFoundation/AVFoundation.h> +#include "base/mac/mac_util.h" #include "base/trace_event/trace_event.h" #include "ui/base/cocoa/animation_utils.h" namespace ui { -CALayerTreeCoordinator::CALayerTreeCoordinator(bool allow_remote_layers) - : allow_remote_layers_(allow_remote_layers) { +namespace { +const uint64_t kFramesBeforeFlushingLowPowerLayer = 15; +} + +CALayerTreeCoordinator::CALayerTreeCoordinator( + bool allow_remote_layers, + bool allow_av_sample_buffer_display_layer) + : allow_remote_layers_(allow_remote_layers), + allow_av_sample_buffer_display_layer_( + allow_av_sample_buffer_display_layer) { if (allow_remote_layers_) { root_ca_layer_.reset([[CALayer alloc] init]); [root_ca_layer_ setGeometryFlipped:YES]; [root_ca_layer_ setOpaque:YES]; - fullscreen_low_power_layer_.reset( - [[AVSampleBufferDisplayLayer alloc] init]); + if (allow_av_sample_buffer_display_layer_) { + fullscreen_low_power_layer_.reset( + [[AVSampleBufferDisplayLayer alloc] init]); + } } } @@ -55,7 +66,8 @@ CARendererLayerTree* CALayerTreeCoordinator::GetPendingCARendererLayerTree() { "specified, but not both."; } if (!pending_ca_renderer_layer_tree_) - pending_ca_renderer_layer_tree_.reset(new CARendererLayerTree); + pending_ca_renderer_layer_tree_.reset(new CARendererLayerTree( + allow_av_sample_buffer_display_layer_, false)); return pending_ca_renderer_layer_tree_.get(); } @@ -88,14 +100,15 @@ void CALayerTreeCoordinator::CommitPendingTreesToCA( current_ca_renderer_layer_tree_.reset(); } - // TODO(ccameron): It may be necessary to leave the last image up for a few - // extra frames to allow a smooth switch between the normal and low-power - // NSWindows. - if (current_fullscreen_low_power_layer_valid_ && - !*fullscreen_low_power_layer_valid) { + // It is necessary to leave the last image up for a few extra frames to allow + // a smooth switch between the normal and low-power NSWindows. + if (*fullscreen_low_power_layer_valid) + frames_since_low_power_layer_was_valid_ = 0; + else + frames_since_low_power_layer_was_valid_ += 1; + if (frames_since_low_power_layer_was_valid_ == + kFramesBeforeFlushingLowPowerLayer) [fullscreen_low_power_layer_ flushAndRemoveImage]; - } - current_fullscreen_low_power_layer_valid_ = *fullscreen_low_power_layer_valid; // Reset all state for the next frame. pending_ca_renderer_layer_tree_.reset(); diff --git a/chromium/ui/accelerated_widget_mac/ca_layer_tree_unittest_mac.mm b/chromium/ui/accelerated_widget_mac/ca_layer_tree_unittest_mac.mm index 882fc8c887d..e8cabcd6d94 100644 --- a/chromium/ui/accelerated_widget_mac/ca_layer_tree_unittest_mac.mm +++ b/chromium/ui/accelerated_widget_mac/ca_layer_tree_unittest_mac.mm @@ -13,12 +13,20 @@ #include "ui/accelerated_widget_mac/ca_renderer_layer_tree.h" #include "ui/gfx/geometry/dip_util.h" #include "ui/gfx/mac/io_surface.h" +#include "ui/gl/ca_renderer_layer_params.h" +#include "ui/gl/gl_image_io_surface.h" namespace gpu { namespace { struct CALayerProperties { + CALayerProperties() {} + ~CALayerProperties() { + if (gl_image) + gl_image->Destroy(true); + } + bool is_clipped = true; gfx::Rect clip_rect; int sorting_context_id = 0; @@ -30,25 +38,47 @@ struct CALayerProperties { float opacity = 1.0f; float scale_factor = 1.0f; unsigned filter = GL_LINEAR; - base::ScopedCFTypeRef<CVPixelBufferRef> cv_pixel_buffer; - base::ScopedCFTypeRef<IOSurfaceRef> io_surface; + scoped_refptr<gl::GLImageIOSurface> gl_image; + + bool allow_av_layers = true; + bool allow_solid_color_layers = true; }; +scoped_refptr<gl::GLImageIOSurface> CreateGLImage(const gfx::Size& size, + gfx::BufferFormat format, + bool video) { + scoped_refptr<gl::GLImageIOSurface> gl_image( + new gl::GLImageIOSurface(size, GL_RGBA)); + base::ScopedCFTypeRef<IOSurfaceRef> io_surface( + gfx::CreateIOSurface(size, format)); + if (video) { + base::ScopedCFTypeRef<CVPixelBufferRef> cv_pixel_buffer; + CVPixelBufferCreateWithIOSurface(nullptr, io_surface, nullptr, + cv_pixel_buffer.InitializeInto()); + gl_image->InitializeWithCVPixelBuffer(cv_pixel_buffer, + gfx::GenericSharedMemoryId(), format); + } else { + gl_image->Initialize(io_surface, gfx::GenericSharedMemoryId(), format); + } + return gl_image; +} + bool ScheduleCALayer(ui::CARendererLayerTree* tree, CALayerProperties* properties) { - return tree->ScheduleCALayer( + return tree->ScheduleCALayer(ui::CARendererLayerParams( properties->is_clipped, properties->clip_rect, properties->sorting_context_id, properties->transform, - properties->io_surface, properties->cv_pixel_buffer, - properties->contents_rect, properties->rect, properties->background_color, - properties->edge_aa_mask, properties->opacity, properties->filter); + properties->gl_image.get(), properties->contents_rect, properties->rect, + properties->background_color, properties->edge_aa_mask, + properties->opacity, properties->filter)); } void UpdateCALayerTree(std::unique_ptr<ui::CARendererLayerTree>& ca_layer_tree, CALayerProperties* properties, CALayer* superlayer) { std::unique_ptr<ui::CARendererLayerTree> new_ca_layer_tree( - new ui::CARendererLayerTree); + new ui::CARendererLayerTree(properties->allow_av_layers, + properties->allow_solid_color_layers)); bool result = ScheduleCALayer(new_ca_layer_tree.get(), properties); EXPECT_TRUE(result); new_ca_layer_tree->CommitScheduledCALayers( @@ -71,350 +101,385 @@ class CALayerTreeTest : public testing::Test { }; // Test updating each layer's properties. -TEST_F(CALayerTreeTest, PropertyUpdates) { - CALayerProperties properties; - properties.clip_rect = gfx::Rect(2, 4, 8, 16); - properties.transform.Translate(10, 20); - properties.contents_rect = gfx::RectF(0.0f, 0.25f, 0.5f, 0.75f); - properties.rect = gfx::Rect(16, 32, 64, 128); - properties.background_color = SkColorSetARGB(0xFF, 0xFF, 0, 0); - properties.edge_aa_mask = GL_CA_LAYER_EDGE_LEFT_CHROMIUM; - properties.opacity = 0.5f; - properties.io_surface.reset( - gfx::CreateIOSurface(gfx::Size(256, 256), gfx::BufferFormat::BGRA_8888)); - - std::unique_ptr<ui::CARendererLayerTree> ca_layer_tree; - CALayer* root_layer = nil; - CALayer* clip_and_sorting_layer = nil; - CALayer* transform_layer = nil; - CALayer* content_layer = nil; - - // Validate the initial values. - { - std::unique_ptr<ui::CARendererLayerTree> new_ca_layer_tree( - new ui::CARendererLayerTree); - - UpdateCALayerTree(ca_layer_tree, &properties, superlayer_); - - // Validate the tree structure. - EXPECT_EQ(1u, [[superlayer_ sublayers] count]); - root_layer = [[superlayer_ sublayers] objectAtIndex:0]; - EXPECT_EQ(1u, [[root_layer sublayers] count]); - clip_and_sorting_layer = [[root_layer sublayers] objectAtIndex:0]; - EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); - transform_layer = [[clip_and_sorting_layer sublayers] objectAtIndex:0]; - EXPECT_EQ(1u, [[transform_layer sublayers] count]); - content_layer = [[transform_layer sublayers] objectAtIndex:0]; - - // Validate the clip and sorting context layer. - EXPECT_TRUE([clip_and_sorting_layer masksToBounds]); - EXPECT_EQ(gfx::Rect(properties.clip_rect.size()), - gfx::Rect([clip_and_sorting_layer bounds])); - EXPECT_EQ(properties.clip_rect.origin(), - gfx::Point([clip_and_sorting_layer position])); - EXPECT_EQ(-properties.clip_rect.origin().x(), - [clip_and_sorting_layer sublayerTransform].m41); - EXPECT_EQ(-properties.clip_rect.origin().y(), - [clip_and_sorting_layer sublayerTransform].m42); - - // Validate the transform layer. - EXPECT_EQ(properties.transform.matrix().get(3, 0), - [transform_layer sublayerTransform].m41); - EXPECT_EQ(properties.transform.matrix().get(3, 1), - [transform_layer sublayerTransform].m42); - - // Validate the content layer. - EXPECT_EQ(static_cast<id>(properties.io_surface.get()), - [content_layer contents]); - EXPECT_EQ(properties.contents_rect, - gfx::RectF([content_layer contentsRect])); - EXPECT_EQ(properties.rect.origin(), gfx::Point([content_layer position])); - EXPECT_EQ(gfx::Rect(properties.rect.size()), - gfx::Rect([content_layer bounds])); - EXPECT_EQ(kCALayerLeftEdge, [content_layer edgeAntialiasingMask]); - EXPECT_EQ(properties.opacity, [content_layer opacity]); - EXPECT_NSEQ(kCAFilterLinear, [content_layer minificationFilter]); - EXPECT_NSEQ(kCAFilterLinear, [content_layer magnificationFilter]); - if ([content_layer respondsToSelector:(@selector(contentsScale))]) - EXPECT_EQ(properties.scale_factor, [content_layer contentsScale]); - } - - // Update just the clip rect and re-commit. - { - properties.clip_rect = gfx::Rect(4, 8, 16, 32); - UpdateCALayerTree(ca_layer_tree, &properties, superlayer_); - - // Validate the tree structure - EXPECT_EQ(1u, [[superlayer_ sublayers] count]); - EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]); - EXPECT_EQ(1u, [[root_layer sublayers] count]); - EXPECT_EQ(clip_and_sorting_layer, [[root_layer sublayers] objectAtIndex:0]); - EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); - EXPECT_EQ(transform_layer, - [[clip_and_sorting_layer sublayers] objectAtIndex:0]); - - // Validate the clip and sorting context layer. - EXPECT_TRUE([clip_and_sorting_layer masksToBounds]); - EXPECT_EQ(gfx::Rect(properties.clip_rect.size()), - gfx::Rect([clip_and_sorting_layer bounds])); - EXPECT_EQ(properties.clip_rect.origin(), - gfx::Point([clip_and_sorting_layer position])); - EXPECT_EQ(-properties.clip_rect.origin().x(), - [clip_and_sorting_layer sublayerTransform].m41); - EXPECT_EQ(-properties.clip_rect.origin().y(), - [clip_and_sorting_layer sublayerTransform].m42); - } - - // Disable clipping and re-commit. - { - properties.is_clipped = false; - UpdateCALayerTree(ca_layer_tree, &properties, superlayer_); - - // Validate the tree structure - EXPECT_EQ(1u, [[superlayer_ sublayers] count]); - EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]); - EXPECT_EQ(1u, [[root_layer sublayers] count]); - EXPECT_EQ(clip_and_sorting_layer, [[root_layer sublayers] objectAtIndex:0]); - EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); - EXPECT_EQ(transform_layer, - [[clip_and_sorting_layer sublayers] objectAtIndex:0]); - - // Validate the clip and sorting context layer. - EXPECT_FALSE([clip_and_sorting_layer masksToBounds]); - EXPECT_EQ(gfx::Rect(), gfx::Rect([clip_and_sorting_layer bounds])); - EXPECT_EQ(gfx::Point(), gfx::Point([clip_and_sorting_layer position])); - EXPECT_EQ(0.0, [clip_and_sorting_layer sublayerTransform].m41); - EXPECT_EQ(0.0, [clip_and_sorting_layer sublayerTransform].m42); - EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); +class CALayerTreePropertyUpdatesTest : public CALayerTreeTest { + public: + void RunTest(bool allow_solid_color_layers) { + CALayerProperties properties; + properties.allow_solid_color_layers = allow_solid_color_layers; + properties.clip_rect = gfx::Rect(2, 4, 8, 16); + properties.transform.Translate(10, 20); + properties.contents_rect = gfx::RectF(0.0f, 0.25f, 0.5f, 0.75f); + properties.rect = gfx::Rect(16, 32, 64, 128); + properties.background_color = SkColorSetARGB(0xFF, 0xFF, 0, 0); + properties.edge_aa_mask = GL_CA_LAYER_EDGE_LEFT_CHROMIUM; + properties.opacity = 0.5f; + properties.gl_image = + CreateGLImage(gfx::Size(256, 256), gfx::BufferFormat::BGRA_8888, false); + + std::unique_ptr<ui::CARendererLayerTree> ca_layer_tree; + CALayer* root_layer = nil; + CALayer* clip_and_sorting_layer = nil; + CALayer* transform_layer = nil; + CALayer* content_layer = nil; + + // Validate the initial values. + { + std::unique_ptr<ui::CARendererLayerTree> new_ca_layer_tree( + new ui::CARendererLayerTree(true, allow_solid_color_layers)); + + UpdateCALayerTree(ca_layer_tree, &properties, superlayer_); + + // Validate the tree structure. + EXPECT_EQ(1u, [[superlayer_ sublayers] count]); + root_layer = [[superlayer_ sublayers] objectAtIndex:0]; + EXPECT_EQ(1u, [[root_layer sublayers] count]); + clip_and_sorting_layer = [[root_layer sublayers] objectAtIndex:0]; + EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); + transform_layer = [[clip_and_sorting_layer sublayers] objectAtIndex:0]; + EXPECT_EQ(1u, [[transform_layer sublayers] count]); + content_layer = [[transform_layer sublayers] objectAtIndex:0]; + + // Validate the clip and sorting context layer. + EXPECT_TRUE([clip_and_sorting_layer masksToBounds]); + EXPECT_EQ(gfx::Rect(properties.clip_rect.size()), + gfx::Rect([clip_and_sorting_layer bounds])); + EXPECT_EQ(properties.clip_rect.origin(), + gfx::Point([clip_and_sorting_layer position])); + EXPECT_EQ(-properties.clip_rect.origin().x(), + [clip_and_sorting_layer sublayerTransform].m41); + EXPECT_EQ(-properties.clip_rect.origin().y(), + [clip_and_sorting_layer sublayerTransform].m42); + + // Validate the transform layer. + EXPECT_EQ(properties.transform.matrix().get(3, 0), + [transform_layer sublayerTransform].m41); + EXPECT_EQ(properties.transform.matrix().get(3, 1), + [transform_layer sublayerTransform].m42); + + // Validate the content layer. + EXPECT_EQ(static_cast<id>(properties.gl_image->io_surface().get()), + [content_layer contents]); + EXPECT_EQ(properties.contents_rect, + gfx::RectF([content_layer contentsRect])); + EXPECT_EQ(properties.rect.origin(), gfx::Point([content_layer position])); + EXPECT_EQ(gfx::Rect(properties.rect.size()), + gfx::Rect([content_layer bounds])); + EXPECT_EQ(kCALayerLeftEdge, [content_layer edgeAntialiasingMask]); + EXPECT_EQ(properties.opacity, [content_layer opacity]); + EXPECT_NSEQ(kCAFilterLinear, [content_layer minificationFilter]); + EXPECT_NSEQ(kCAFilterLinear, [content_layer magnificationFilter]); + if ([content_layer respondsToSelector:(@selector(contentsScale))]) + EXPECT_EQ(properties.scale_factor, [content_layer contentsScale]); + } + + // Update just the clip rect and re-commit. + { + properties.clip_rect = gfx::Rect(4, 8, 16, 32); + UpdateCALayerTree(ca_layer_tree, &properties, superlayer_); + + // Validate the tree structure + EXPECT_EQ(1u, [[superlayer_ sublayers] count]); + EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]); + EXPECT_EQ(1u, [[root_layer sublayers] count]); + EXPECT_EQ(clip_and_sorting_layer, + [[root_layer sublayers] objectAtIndex:0]); + EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); + EXPECT_EQ(transform_layer, + [[clip_and_sorting_layer sublayers] objectAtIndex:0]); + + // Validate the clip and sorting context layer. + EXPECT_TRUE([clip_and_sorting_layer masksToBounds]); + EXPECT_EQ(gfx::Rect(properties.clip_rect.size()), + gfx::Rect([clip_and_sorting_layer bounds])); + EXPECT_EQ(properties.clip_rect.origin(), + gfx::Point([clip_and_sorting_layer position])); + EXPECT_EQ(-properties.clip_rect.origin().x(), + [clip_and_sorting_layer sublayerTransform].m41); + EXPECT_EQ(-properties.clip_rect.origin().y(), + [clip_and_sorting_layer sublayerTransform].m42); + } + + // Disable clipping and re-commit. + { + properties.is_clipped = false; + UpdateCALayerTree(ca_layer_tree, &properties, superlayer_); + + // Validate the tree structure + EXPECT_EQ(1u, [[superlayer_ sublayers] count]); + EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]); + EXPECT_EQ(1u, [[root_layer sublayers] count]); + EXPECT_EQ(clip_and_sorting_layer, + [[root_layer sublayers] objectAtIndex:0]); + EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); + EXPECT_EQ(transform_layer, + [[clip_and_sorting_layer sublayers] objectAtIndex:0]); + + // Validate the clip and sorting context layer. + EXPECT_FALSE([clip_and_sorting_layer masksToBounds]); + EXPECT_EQ(gfx::Rect(), gfx::Rect([clip_and_sorting_layer bounds])); + EXPECT_EQ(gfx::Point(), gfx::Point([clip_and_sorting_layer position])); + EXPECT_EQ(0.0, [clip_and_sorting_layer sublayerTransform].m41); + EXPECT_EQ(0.0, [clip_and_sorting_layer sublayerTransform].m42); + EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); + } + + // Change the transform and re-commit. + { + properties.transform.Translate(5, 5); + UpdateCALayerTree(ca_layer_tree, &properties, superlayer_); + + // Validate the tree structure. + EXPECT_EQ(1u, [[superlayer_ sublayers] count]); + EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]); + EXPECT_EQ(1u, [[root_layer sublayers] count]); + EXPECT_EQ(clip_and_sorting_layer, + [[root_layer sublayers] objectAtIndex:0]); + EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); + EXPECT_EQ(transform_layer, + [[clip_and_sorting_layer sublayers] objectAtIndex:0]); + + // Validate the transform layer. + EXPECT_EQ(properties.transform.matrix().get(3, 0), + [transform_layer sublayerTransform].m41); + EXPECT_EQ(properties.transform.matrix().get(3, 1), + [transform_layer sublayerTransform].m42); + } + + // Change the edge antialiasing mask and commit. + { + properties.edge_aa_mask = GL_CA_LAYER_EDGE_TOP_CHROMIUM; + UpdateCALayerTree(ca_layer_tree, &properties, superlayer_); + + // Validate the tree structure. + EXPECT_EQ(1u, [[superlayer_ sublayers] count]); + EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]); + EXPECT_EQ(1u, [[root_layer sublayers] count]); + EXPECT_EQ(clip_and_sorting_layer, + [[root_layer sublayers] objectAtIndex:0]); + EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); + EXPECT_EQ(transform_layer, + [[clip_and_sorting_layer sublayers] objectAtIndex:0]); + EXPECT_EQ(1u, [[transform_layer sublayers] count]); + EXPECT_EQ(content_layer, [[transform_layer sublayers] objectAtIndex:0]); + + // Validate the content layer. Note that top and bottom edges flip. + EXPECT_EQ(kCALayerBottomEdge, [content_layer edgeAntialiasingMask]); + } + + // Change the contents and commit. + { + properties.gl_image->Destroy(true); + properties.gl_image = nullptr; + UpdateCALayerTree(ca_layer_tree, &properties, superlayer_); + + // Validate the tree structure. + EXPECT_EQ(1u, [[superlayer_ sublayers] count]); + EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]); + EXPECT_EQ(1u, [[root_layer sublayers] count]); + EXPECT_EQ(clip_and_sorting_layer, + [[root_layer sublayers] objectAtIndex:0]); + EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); + EXPECT_EQ(transform_layer, + [[clip_and_sorting_layer sublayers] objectAtIndex:0]); + EXPECT_EQ(1u, [[transform_layer sublayers] count]); + EXPECT_EQ(content_layer, [[transform_layer sublayers] objectAtIndex:0]); + + // Validate the content layer. Note that edge anti-aliasing does not flip + // for solid colors. + if (allow_solid_color_layers) { + EXPECT_EQ(nil, [content_layer contents]); + EXPECT_EQ(kCALayerTopEdge, [content_layer edgeAntialiasingMask]); + } else { + EXPECT_EQ(ca_layer_tree->ContentsForSolidColorForTesting( + properties.background_color), + [content_layer contents]); + EXPECT_EQ(kCALayerBottomEdge, [content_layer edgeAntialiasingMask]); + } + } + + // Change the rect size. + { + properties.rect = gfx::Rect(properties.rect.origin(), gfx::Size(32, 16)); + UpdateCALayerTree(ca_layer_tree, &properties, superlayer_); + + // Validate the tree structure. + EXPECT_EQ(1u, [[superlayer_ sublayers] count]); + EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]); + EXPECT_EQ(1u, [[root_layer sublayers] count]); + EXPECT_EQ(clip_and_sorting_layer, + [[root_layer sublayers] objectAtIndex:0]); + EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); + EXPECT_EQ(transform_layer, + [[clip_and_sorting_layer sublayers] objectAtIndex:0]); + EXPECT_EQ(1u, [[transform_layer sublayers] count]); + EXPECT_EQ(content_layer, [[transform_layer sublayers] objectAtIndex:0]); + + // Validate the content layer. + EXPECT_EQ(properties.rect.origin(), gfx::Point([content_layer position])); + EXPECT_EQ(gfx::Rect(properties.rect.size()), + gfx::Rect([content_layer bounds])); + } + + // Change the rect position. + { + properties.rect = gfx::Rect(gfx::Point(16, 4), properties.rect.size()); + UpdateCALayerTree(ca_layer_tree, &properties, superlayer_); + + // Validate the tree structure. + EXPECT_EQ(1u, [[superlayer_ sublayers] count]); + EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]); + EXPECT_EQ(1u, [[root_layer sublayers] count]); + EXPECT_EQ(clip_and_sorting_layer, + [[root_layer sublayers] objectAtIndex:0]); + EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); + EXPECT_EQ(transform_layer, + [[clip_and_sorting_layer sublayers] objectAtIndex:0]); + EXPECT_EQ(1u, [[transform_layer sublayers] count]); + EXPECT_EQ(content_layer, [[transform_layer sublayers] objectAtIndex:0]); + + // Validate the content layer. + EXPECT_EQ(properties.rect.origin(), gfx::Point([content_layer position])); + EXPECT_EQ(gfx::Rect(properties.rect.size()), + gfx::Rect([content_layer bounds])); + } + + // Change the opacity. + { + properties.opacity = 1.0f; + UpdateCALayerTree(ca_layer_tree, &properties, superlayer_); + + // Validate the tree structure. + EXPECT_EQ(1u, [[superlayer_ sublayers] count]); + EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]); + EXPECT_EQ(1u, [[root_layer sublayers] count]); + EXPECT_EQ(clip_and_sorting_layer, + [[root_layer sublayers] objectAtIndex:0]); + EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); + EXPECT_EQ(transform_layer, + [[clip_and_sorting_layer sublayers] objectAtIndex:0]); + EXPECT_EQ(1u, [[transform_layer sublayers] count]); + EXPECT_EQ(content_layer, [[transform_layer sublayers] objectAtIndex:0]); + + // Validate the content layer. + EXPECT_EQ(properties.opacity, [content_layer opacity]); + } + + // Change the filter. + { + properties.filter = GL_NEAREST; + UpdateCALayerTree(ca_layer_tree, &properties, superlayer_); + + // Validate the tree structure. + EXPECT_EQ(1u, [[superlayer_ sublayers] count]); + EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]); + EXPECT_EQ(1u, [[root_layer sublayers] count]); + EXPECT_EQ(clip_and_sorting_layer, + [[root_layer sublayers] objectAtIndex:0]); + EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); + EXPECT_EQ(transform_layer, + [[clip_and_sorting_layer sublayers] objectAtIndex:0]); + EXPECT_EQ(1u, [[transform_layer sublayers] count]); + EXPECT_EQ(content_layer, [[transform_layer sublayers] objectAtIndex:0]); + + // Validate the content layer. + EXPECT_NSEQ(kCAFilterNearest, [content_layer minificationFilter]); + EXPECT_NSEQ(kCAFilterNearest, [content_layer magnificationFilter]); + } + + // Add the clipping and IOSurface contents back. + { + properties.is_clipped = true; + properties.gl_image = CreateGLImage(gfx::Size(256, 256), + gfx::BufferFormat::BGRA_8888, false); + UpdateCALayerTree(ca_layer_tree, &properties, superlayer_); + + // Validate the tree structure. + EXPECT_EQ(1u, [[superlayer_ sublayers] count]); + EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]); + EXPECT_EQ(1u, [[root_layer sublayers] count]); + EXPECT_EQ(clip_and_sorting_layer, + [[root_layer sublayers] objectAtIndex:0]); + EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); + EXPECT_EQ(transform_layer, + [[clip_and_sorting_layer sublayers] objectAtIndex:0]); + EXPECT_EQ(1u, [[transform_layer sublayers] count]); + EXPECT_EQ(content_layer, [[transform_layer sublayers] objectAtIndex:0]); + + // Validate the content layer. + EXPECT_EQ(static_cast<id>(properties.gl_image->io_surface().get()), + [content_layer contents]); + EXPECT_EQ(kCALayerBottomEdge, [content_layer edgeAntialiasingMask]); + } + + // Change the scale factor. This should result in a new tree being created. + { + properties.scale_factor = 2.0f; + UpdateCALayerTree(ca_layer_tree, &properties, superlayer_); + + // Validate the tree structure. + EXPECT_EQ(1u, [[superlayer_ sublayers] count]); + EXPECT_NE(root_layer, [[superlayer_ sublayers] objectAtIndex:0]); + root_layer = [[superlayer_ sublayers] objectAtIndex:0]; + EXPECT_EQ(1u, [[root_layer sublayers] count]); + EXPECT_NE(clip_and_sorting_layer, + [[root_layer sublayers] objectAtIndex:0]); + clip_and_sorting_layer = [[root_layer sublayers] objectAtIndex:0]; + EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); + EXPECT_NE(transform_layer, + [[clip_and_sorting_layer sublayers] objectAtIndex:0]); + transform_layer = [[clip_and_sorting_layer sublayers] objectAtIndex:0]; + EXPECT_EQ(1u, [[transform_layer sublayers] count]); + EXPECT_NE(content_layer, [[transform_layer sublayers] objectAtIndex:0]); + content_layer = [[transform_layer sublayers] objectAtIndex:0]; + + // Validate the clip and sorting context layer. + EXPECT_TRUE([clip_and_sorting_layer masksToBounds]); + EXPECT_EQ(gfx::ConvertRectToDIP(properties.scale_factor, + gfx::Rect(properties.clip_rect.size())), + gfx::Rect([clip_and_sorting_layer bounds])); + EXPECT_EQ(gfx::ConvertPointToDIP(properties.scale_factor, + properties.clip_rect.origin()), + gfx::Point([clip_and_sorting_layer position])); + EXPECT_EQ(-properties.clip_rect.origin().x() / properties.scale_factor, + [clip_and_sorting_layer sublayerTransform].m41); + EXPECT_EQ(-properties.clip_rect.origin().y() / properties.scale_factor, + [clip_and_sorting_layer sublayerTransform].m42); + + // Validate the transform layer. + EXPECT_EQ( + properties.transform.matrix().get(3, 0) / properties.scale_factor, + [transform_layer sublayerTransform].m41); + EXPECT_EQ( + properties.transform.matrix().get(3, 1) / properties.scale_factor, + [transform_layer sublayerTransform].m42); + + // Validate the content layer. + EXPECT_EQ(static_cast<id>(properties.gl_image->io_surface().get()), + [content_layer contents]); + EXPECT_EQ(properties.contents_rect, + gfx::RectF([content_layer contentsRect])); + EXPECT_EQ(gfx::ConvertPointToDIP(properties.scale_factor, + properties.rect.origin()), + gfx::Point([content_layer position])); + EXPECT_EQ(gfx::ConvertRectToDIP(properties.scale_factor, + gfx::Rect(properties.rect.size())), + gfx::Rect([content_layer bounds])); + EXPECT_EQ(kCALayerBottomEdge, [content_layer edgeAntialiasingMask]); + EXPECT_EQ(properties.opacity, [content_layer opacity]); + if ([content_layer respondsToSelector:(@selector(contentsScale))]) + EXPECT_EQ(properties.scale_factor, [content_layer contentsScale]); + } + + properties.gl_image->Destroy(true); } +}; - // Change the transform and re-commit. - { - properties.transform.Translate(5, 5); - UpdateCALayerTree(ca_layer_tree, &properties, superlayer_); - - // Validate the tree structure. - EXPECT_EQ(1u, [[superlayer_ sublayers] count]); - EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]); - EXPECT_EQ(1u, [[root_layer sublayers] count]); - EXPECT_EQ(clip_and_sorting_layer, [[root_layer sublayers] objectAtIndex:0]); - EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); - EXPECT_EQ(transform_layer, - [[clip_and_sorting_layer sublayers] objectAtIndex:0]); - - // Validate the transform layer. - EXPECT_EQ(properties.transform.matrix().get(3, 0), - [transform_layer sublayerTransform].m41); - EXPECT_EQ(properties.transform.matrix().get(3, 1), - [transform_layer sublayerTransform].m42); - } - - // Change the edge antialiasing mask and commit. - { - properties.edge_aa_mask = GL_CA_LAYER_EDGE_TOP_CHROMIUM; - UpdateCALayerTree(ca_layer_tree, &properties, superlayer_); - - // Validate the tree structure. - EXPECT_EQ(1u, [[superlayer_ sublayers] count]); - EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]); - EXPECT_EQ(1u, [[root_layer sublayers] count]); - EXPECT_EQ(clip_and_sorting_layer, [[root_layer sublayers] objectAtIndex:0]); - EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); - EXPECT_EQ(transform_layer, - [[clip_and_sorting_layer sublayers] objectAtIndex:0]); - EXPECT_EQ(1u, [[transform_layer sublayers] count]); - EXPECT_EQ(content_layer, [[transform_layer sublayers] objectAtIndex:0]); - - // Validate the content layer. Note that top and bottom edges flip. - EXPECT_EQ(kCALayerBottomEdge, [content_layer edgeAntialiasingMask]); - } - - // Change the contents and commit. - { - properties.io_surface.reset(); - UpdateCALayerTree(ca_layer_tree, &properties, superlayer_); - - // Validate the tree structure. - EXPECT_EQ(1u, [[superlayer_ sublayers] count]); - EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]); - EXPECT_EQ(1u, [[root_layer sublayers] count]); - EXPECT_EQ(clip_and_sorting_layer, [[root_layer sublayers] objectAtIndex:0]); - EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); - EXPECT_EQ(transform_layer, - [[clip_and_sorting_layer sublayers] objectAtIndex:0]); - EXPECT_EQ(1u, [[transform_layer sublayers] count]); - EXPECT_EQ(content_layer, [[transform_layer sublayers] objectAtIndex:0]); - - // Validate the content layer. Note that edge anti-aliasing no longer flips. - EXPECT_EQ(nil, [content_layer contents]); - EXPECT_EQ(kCALayerTopEdge, [content_layer edgeAntialiasingMask]); - } - - // Change the rect size. - { - properties.rect = gfx::Rect(properties.rect.origin(), gfx::Size(32, 16)); - UpdateCALayerTree(ca_layer_tree, &properties, superlayer_); - - // Validate the tree structure. - EXPECT_EQ(1u, [[superlayer_ sublayers] count]); - EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]); - EXPECT_EQ(1u, [[root_layer sublayers] count]); - EXPECT_EQ(clip_and_sorting_layer, [[root_layer sublayers] objectAtIndex:0]); - EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); - EXPECT_EQ(transform_layer, - [[clip_and_sorting_layer sublayers] objectAtIndex:0]); - EXPECT_EQ(1u, [[transform_layer sublayers] count]); - EXPECT_EQ(content_layer, [[transform_layer sublayers] objectAtIndex:0]); - - // Validate the content layer. - EXPECT_EQ(properties.rect.origin(), gfx::Point([content_layer position])); - EXPECT_EQ(gfx::Rect(properties.rect.size()), - gfx::Rect([content_layer bounds])); - } - - // Change the rect position. - { - properties.rect = gfx::Rect(gfx::Point(16, 4), properties.rect.size()); - UpdateCALayerTree(ca_layer_tree, &properties, superlayer_); - - // Validate the tree structure. - EXPECT_EQ(1u, [[superlayer_ sublayers] count]); - EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]); - EXPECT_EQ(1u, [[root_layer sublayers] count]); - EXPECT_EQ(clip_and_sorting_layer, [[root_layer sublayers] objectAtIndex:0]); - EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); - EXPECT_EQ(transform_layer, - [[clip_and_sorting_layer sublayers] objectAtIndex:0]); - EXPECT_EQ(1u, [[transform_layer sublayers] count]); - EXPECT_EQ(content_layer, [[transform_layer sublayers] objectAtIndex:0]); - - // Validate the content layer. - EXPECT_EQ(properties.rect.origin(), gfx::Point([content_layer position])); - EXPECT_EQ(gfx::Rect(properties.rect.size()), - gfx::Rect([content_layer bounds])); - } - - // Change the opacity. - { - properties.opacity = 1.0f; - UpdateCALayerTree(ca_layer_tree, &properties, superlayer_); - - // Validate the tree structure. - EXPECT_EQ(1u, [[superlayer_ sublayers] count]); - EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]); - EXPECT_EQ(1u, [[root_layer sublayers] count]); - EXPECT_EQ(clip_and_sorting_layer, [[root_layer sublayers] objectAtIndex:0]); - EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); - EXPECT_EQ(transform_layer, - [[clip_and_sorting_layer sublayers] objectAtIndex:0]); - EXPECT_EQ(1u, [[transform_layer sublayers] count]); - EXPECT_EQ(content_layer, [[transform_layer sublayers] objectAtIndex:0]); - - // Validate the content layer. - EXPECT_EQ(properties.opacity, [content_layer opacity]); - } - - // Change the filter. - { - properties.filter = GL_NEAREST; - UpdateCALayerTree(ca_layer_tree, &properties, superlayer_); - - // Validate the tree structure. - EXPECT_EQ(1u, [[superlayer_ sublayers] count]); - EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]); - EXPECT_EQ(1u, [[root_layer sublayers] count]); - EXPECT_EQ(clip_and_sorting_layer, [[root_layer sublayers] objectAtIndex:0]); - EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); - EXPECT_EQ(transform_layer, - [[clip_and_sorting_layer sublayers] objectAtIndex:0]); - EXPECT_EQ(1u, [[transform_layer sublayers] count]); - EXPECT_EQ(content_layer, [[transform_layer sublayers] objectAtIndex:0]); - - // Validate the content layer. - EXPECT_NSEQ(kCAFilterNearest, [content_layer minificationFilter]); - EXPECT_NSEQ(kCAFilterNearest, [content_layer magnificationFilter]); - } - - // Add the clipping and IOSurface contents back. - { - properties.is_clipped = true; - properties.io_surface.reset( - gfx::CreateIOSurface(gfx::Size(256, 256), - gfx::BufferFormat::BGRA_8888)); - UpdateCALayerTree(ca_layer_tree, &properties, superlayer_); - - // Validate the tree structure. - EXPECT_EQ(1u, [[superlayer_ sublayers] count]); - EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]); - EXPECT_EQ(1u, [[root_layer sublayers] count]); - EXPECT_EQ(clip_and_sorting_layer, [[root_layer sublayers] objectAtIndex:0]); - EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); - EXPECT_EQ(transform_layer, - [[clip_and_sorting_layer sublayers] objectAtIndex:0]); - EXPECT_EQ(1u, [[transform_layer sublayers] count]); - EXPECT_EQ(content_layer, [[transform_layer sublayers] objectAtIndex:0]); - - // Validate the content layer. - EXPECT_EQ(static_cast<id>(properties.io_surface.get()), - [content_layer contents]); - EXPECT_EQ(kCALayerBottomEdge, [content_layer edgeAntialiasingMask]); - } - - // Change the scale factor. This should result in a new tree being created. - { - properties.scale_factor = 2.0f; - UpdateCALayerTree(ca_layer_tree, &properties, superlayer_); - - // Validate the tree structure. - EXPECT_EQ(1u, [[superlayer_ sublayers] count]); - EXPECT_NE(root_layer, [[superlayer_ sublayers] objectAtIndex:0]); - root_layer = [[superlayer_ sublayers] objectAtIndex:0]; - EXPECT_EQ(1u, [[root_layer sublayers] count]); - EXPECT_NE(clip_and_sorting_layer, [[root_layer sublayers] objectAtIndex:0]); - clip_and_sorting_layer = [[root_layer sublayers] objectAtIndex:0]; - EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); - EXPECT_NE(transform_layer, - [[clip_and_sorting_layer sublayers] objectAtIndex:0]); - transform_layer = [[clip_and_sorting_layer sublayers] objectAtIndex:0]; - EXPECT_EQ(1u, [[transform_layer sublayers] count]); - EXPECT_NE(content_layer, [[transform_layer sublayers] objectAtIndex:0]); - content_layer = [[transform_layer sublayers] objectAtIndex:0]; - - // Validate the clip and sorting context layer. - EXPECT_TRUE([clip_and_sorting_layer masksToBounds]); - EXPECT_EQ(gfx::ConvertRectToDIP(properties.scale_factor, - gfx::Rect(properties.clip_rect.size())), - gfx::Rect([clip_and_sorting_layer bounds])); - EXPECT_EQ(gfx::ConvertPointToDIP(properties.scale_factor, - properties.clip_rect.origin()), - gfx::Point([clip_and_sorting_layer position])); - EXPECT_EQ(-properties.clip_rect.origin().x() / properties.scale_factor, - [clip_and_sorting_layer sublayerTransform].m41); - EXPECT_EQ(-properties.clip_rect.origin().y() / properties.scale_factor, - [clip_and_sorting_layer sublayerTransform].m42); - - // Validate the transform layer. - EXPECT_EQ(properties.transform.matrix().get(3, 0) / properties.scale_factor, - [transform_layer sublayerTransform].m41); - EXPECT_EQ(properties.transform.matrix().get(3, 1) / properties.scale_factor, - [transform_layer sublayerTransform].m42); +TEST_F(CALayerTreePropertyUpdatesTest, AllowSolidColors) { + RunTest(true); +} - // Validate the content layer. - EXPECT_EQ(static_cast<id>(properties.io_surface.get()), - [content_layer contents]); - EXPECT_EQ(properties.contents_rect, - gfx::RectF([content_layer contentsRect])); - EXPECT_EQ(gfx::ConvertPointToDIP(properties.scale_factor, - properties.rect.origin()), - gfx::Point([content_layer position])); - EXPECT_EQ(gfx::ConvertRectToDIP(properties.scale_factor, - gfx::Rect(properties.rect.size())), - gfx::Rect([content_layer bounds])); - EXPECT_EQ(kCALayerBottomEdge, [content_layer edgeAntialiasingMask]); - EXPECT_EQ(properties.opacity, [content_layer opacity]); - if ([content_layer respondsToSelector:(@selector(contentsScale))]) - EXPECT_EQ(properties.scale_factor, [content_layer contentsScale]); - } +TEST_F(CALayerTreePropertyUpdatesTest, DisallowSolidColors) { + RunTest(false); } // Verify that sorting context zero is split at non-flat transforms. @@ -425,10 +490,10 @@ TEST_F(CALayerTreeTest, SplitSortingContextZero) { properties.rect = gfx::Rect(0, 0, 256, 256); // We'll use the IOSurface contents to identify the content layers. - base::ScopedCFTypeRef<IOSurfaceRef> io_surfaces[5]; + scoped_refptr<gl::GLImageIOSurface> gl_images[5]; for (size_t i = 0; i < 5; ++i) { - io_surfaces[i].reset(gfx::CreateIOSurface( - gfx::Size(256, 256), gfx::BufferFormat::BGRA_8888)); + gl_images[i] = + CreateGLImage(gfx::Size(256, 256), gfx::BufferFormat::BGRA_8888, false); } // Have 5 transforms: @@ -444,9 +509,9 @@ TEST_F(CALayerTreeTest, SplitSortingContextZero) { // Schedule and commit the layers. std::unique_ptr<ui::CARendererLayerTree> ca_layer_tree( - new ui::CARendererLayerTree); + new ui::CARendererLayerTree(true, true)); for (size_t i = 0; i < 5; ++i) { - properties.io_surface = io_surfaces[i]; + properties.gl_image = gl_images[i]; properties.transform = transforms[i]; bool result = ScheduleCALayer(ca_layer_tree.get(), &properties); EXPECT_TRUE(result); @@ -494,11 +559,19 @@ TEST_F(CALayerTreeTest, SplitSortingContextZero) { CALayer* content_layer_4 = [[transform_layer_2_0 sublayers] objectAtIndex:1]; // Validate that the layers come out in order. - EXPECT_EQ(static_cast<id>(io_surfaces[0].get()), [content_layer_0 contents]); - EXPECT_EQ(static_cast<id>(io_surfaces[1].get()), [content_layer_1 contents]); - EXPECT_EQ(static_cast<id>(io_surfaces[2].get()), [content_layer_2 contents]); - EXPECT_EQ(static_cast<id>(io_surfaces[3].get()), [content_layer_3 contents]); - EXPECT_EQ(static_cast<id>(io_surfaces[4].get()), [content_layer_4 contents]); + EXPECT_EQ(static_cast<id>(gl_images[0]->io_surface().get()), + [content_layer_0 contents]); + EXPECT_EQ(static_cast<id>(gl_images[1]->io_surface().get()), + [content_layer_1 contents]); + EXPECT_EQ(static_cast<id>(gl_images[2]->io_surface().get()), + [content_layer_2 contents]); + EXPECT_EQ(static_cast<id>(gl_images[3]->io_surface().get()), + [content_layer_3 contents]); + EXPECT_EQ(static_cast<id>(gl_images[4]->io_surface().get()), + [content_layer_4 contents]); + + for (size_t i = 0; i < 5; ++i) + gl_images[i]->Destroy(true); } // Verify that sorting contexts are allocated appropriately. @@ -509,20 +582,20 @@ TEST_F(CALayerTreeTest, SortingContexts) { properties.rect = gfx::Rect(0, 0, 256, 256); // We'll use the IOSurface contents to identify the content layers. - base::ScopedCFTypeRef<IOSurfaceRef> io_surfaces[3]; + scoped_refptr<gl::GLImageIOSurface> gl_images[3]; for (size_t i = 0; i < 3; ++i) { - io_surfaces[i].reset(gfx::CreateIOSurface( - gfx::Size(256, 256), gfx::BufferFormat::BGRA_8888)); + gl_images[i] = + CreateGLImage(gfx::Size(256, 256), gfx::BufferFormat::BGRA_8888, false); } int sorting_context_ids[3] = {3, -1, 0}; // Schedule and commit the layers. std::unique_ptr<ui::CARendererLayerTree> ca_layer_tree( - new ui::CARendererLayerTree); + new ui::CARendererLayerTree(true, true)); for (size_t i = 0; i < 3; ++i) { properties.sorting_context_id = sorting_context_ids[i]; - properties.io_surface = io_surfaces[i]; + properties.gl_image = gl_images[i]; bool result = ScheduleCALayer(ca_layer_tree.get(), &properties); EXPECT_TRUE(result); } @@ -559,9 +632,15 @@ TEST_F(CALayerTreeTest, SortingContexts) { CALayer* content_layer_2 = [[transform_layer_2 sublayers] objectAtIndex:0]; // Validate that the layers come out in order. - EXPECT_EQ(static_cast<id>(io_surfaces[0].get()), [content_layer_0 contents]); - EXPECT_EQ(static_cast<id>(io_surfaces[1].get()), [content_layer_1 contents]); - EXPECT_EQ(static_cast<id>(io_surfaces[2].get()), [content_layer_2 contents]); + EXPECT_EQ(static_cast<id>(gl_images[0]->io_surface().get()), + [content_layer_0 contents]); + EXPECT_EQ(static_cast<id>(gl_images[1]->io_surface().get()), + [content_layer_1 contents]); + EXPECT_EQ(static_cast<id>(gl_images[2]->io_surface().get()), + [content_layer_2 contents]); + + for (size_t i = 0; i < 3; ++i) + gl_images[i]->Destroy(true); } // Verify that sorting contexts must all have the same clipping properties. @@ -577,7 +656,7 @@ TEST_F(CALayerTreeTest, SortingContextMustHaveConsistentClip) { }; std::unique_ptr<ui::CARendererLayerTree> ca_layer_tree( - new ui::CARendererLayerTree); + new ui::CARendererLayerTree(true, true)); // First send the various clip parameters to sorting context zero. This is // legitimate. for (size_t i = 0; i < 3; ++i) { @@ -614,8 +693,8 @@ TEST_F(CALayerTreeTest, SortingContextMustHaveConsistentClip) { // Test updating each layer's properties. TEST_F(CALayerTreeTest, AVLayer) { CALayerProperties properties; - properties.io_surface.reset(gfx::CreateIOSurface( - gfx::Size(256, 256), gfx::BufferFormat::YUV_420_BIPLANAR)); + properties.gl_image = + CreateGLImage(gfx::Size(256, 256), gfx::BufferFormat::BGRA_8888, false); std::unique_ptr<ui::CARendererLayerTree> ca_layer_tree; CALayer* root_layer = nil; @@ -624,6 +703,7 @@ TEST_F(CALayerTreeTest, AVLayer) { CALayer* content_layer1 = nil; CALayer* content_layer2 = nil; CALayer* content_layer3 = nil; + CALayer* content_layer4 = nil; // Validate the initial values. { @@ -644,10 +724,12 @@ TEST_F(CALayerTreeTest, AVLayer) { isKindOfClass:NSClassFromString(@"AVSampleBufferDisplayLayer")]); } - properties.io_surface.reset(gfx::CreateIOSurface( - gfx::Size(256, 256), gfx::BufferFormat::YUV_420_BIPLANAR)); + properties.gl_image->Destroy(true); + properties.gl_image = CreateGLImage( + gfx::Size(256, 256), gfx::BufferFormat::YUV_420_BIPLANAR, false); - // Pass another frame. + // Pass another frame. This will automatically create a CVPixelBuffer + // behind the scenes, because the underlying buffer is YUV 420. { UpdateCALayerTree(ca_layer_tree, &properties, superlayer_); @@ -662,17 +744,16 @@ TEST_F(CALayerTreeTest, AVLayer) { content_layer2 = [[transform_layer sublayers] objectAtIndex:0]; // Validate the content layer. - EXPECT_FALSE([content_layer2 + EXPECT_TRUE([content_layer2 isKindOfClass:NSClassFromString(@"AVSampleBufferDisplayLayer")]); - EXPECT_EQ(content_layer2, content_layer1); + EXPECT_NE(content_layer2, content_layer1); } - properties.io_surface.reset(gfx::CreateIOSurface( - gfx::Size(256, 256), gfx::BufferFormat::YUV_420_BIPLANAR)); - CVPixelBufferCreateWithIOSurface(nullptr, properties.io_surface, nullptr, - properties.cv_pixel_buffer.InitializeInto()); + properties.gl_image->Destroy(true); + properties.gl_image = CreateGLImage( + gfx::Size(256, 256), gfx::BufferFormat::YUV_420_BIPLANAR, true); - // Pass a frame with a CVPixelBuffer + // Pass a frame with a CVPixelBuffer. { UpdateCALayerTree(ca_layer_tree, &properties, superlayer_); @@ -684,17 +765,17 @@ TEST_F(CALayerTreeTest, AVLayer) { EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); transform_layer = [[clip_and_sorting_layer sublayers] objectAtIndex:0]; EXPECT_EQ(1u, [[transform_layer sublayers] count]); - content_layer2 = [[transform_layer sublayers] objectAtIndex:0]; + content_layer3 = [[transform_layer sublayers] objectAtIndex:0]; // Validate the content layer. - EXPECT_TRUE([content_layer2 + EXPECT_TRUE([content_layer3 isKindOfClass:NSClassFromString(@"AVSampleBufferDisplayLayer")]); - EXPECT_NE(content_layer2, content_layer1); + EXPECT_EQ(content_layer3, content_layer2); } - properties.io_surface.reset(gfx::CreateIOSurface( - gfx::Size(256, 256), gfx::BufferFormat::YUV_420_BIPLANAR)); - properties.cv_pixel_buffer.reset(); + properties.gl_image->Destroy(true); + properties.gl_image = CreateGLImage( + gfx::Size(256, 256), gfx::BufferFormat::YUV_420_BIPLANAR, false); // Pass a frame that is clipped. properties.contents_rect = gfx::RectF(0, 0, 1, 0.9); @@ -709,23 +790,73 @@ TEST_F(CALayerTreeTest, AVLayer) { EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); transform_layer = [[clip_and_sorting_layer sublayers] objectAtIndex:0]; EXPECT_EQ(1u, [[transform_layer sublayers] count]); - content_layer3 = [[transform_layer sublayers] objectAtIndex:0]; + content_layer4 = [[transform_layer sublayers] objectAtIndex:0]; // Validate the content layer. - EXPECT_FALSE([content_layer3 + EXPECT_FALSE([content_layer4 + isKindOfClass:NSClassFromString(@"AVSampleBufferDisplayLayer")]); + EXPECT_NE(content_layer4, content_layer3); + } +} + +// Ensure that blacklisting AVSampleBufferDisplayLayer works. +TEST_F(CALayerTreeTest, AVLayerBlacklist) { + CALayerProperties properties; + properties.gl_image = CreateGLImage( + gfx::Size(256, 256), gfx::BufferFormat::YUV_420_BIPLANAR, false); + + std::unique_ptr<ui::CARendererLayerTree> ca_layer_tree; + CALayer* root_layer = nil; + CALayer* clip_and_sorting_layer = nil; + CALayer* transform_layer = nil; + CALayer* content_layer1 = nil; + CALayer* content_layer2 = nil; + + { + UpdateCALayerTree(ca_layer_tree, &properties, superlayer_); + + // Validate the tree structure. + EXPECT_EQ(1u, [[superlayer_ sublayers] count]); + root_layer = [[superlayer_ sublayers] objectAtIndex:0]; + EXPECT_EQ(1u, [[root_layer sublayers] count]); + clip_and_sorting_layer = [[root_layer sublayers] objectAtIndex:0]; + EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); + transform_layer = [[clip_and_sorting_layer sublayers] objectAtIndex:0]; + EXPECT_EQ(1u, [[transform_layer sublayers] count]); + content_layer1 = [[transform_layer sublayers] objectAtIndex:0]; + + // Validate the content layer. + EXPECT_TRUE([content_layer1 + isKindOfClass:NSClassFromString(@"AVSampleBufferDisplayLayer")]); + } + + { + properties.allow_av_layers = false; + UpdateCALayerTree(ca_layer_tree, &properties, superlayer_); + + // Validate the tree structure. + EXPECT_EQ(1u, [[superlayer_ sublayers] count]); + root_layer = [[superlayer_ sublayers] objectAtIndex:0]; + EXPECT_EQ(1u, [[root_layer sublayers] count]); + clip_and_sorting_layer = [[root_layer sublayers] objectAtIndex:0]; + EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]); + transform_layer = [[clip_and_sorting_layer sublayers] objectAtIndex:0]; + EXPECT_EQ(1u, [[transform_layer sublayers] count]); + content_layer2 = [[transform_layer sublayers] objectAtIndex:0]; + + // Validate the content layer. + EXPECT_FALSE([content_layer2 isKindOfClass:NSClassFromString(@"AVSampleBufferDisplayLayer")]); - EXPECT_NE(content_layer3, content_layer2); + EXPECT_NE(content_layer1, content_layer2); } } // Test fullscreen low power detection. TEST_F(CALayerTreeTest, FullscreenLowPower) { CALayerProperties properties; - properties.io_surface.reset(gfx::CreateIOSurface( - gfx::Size(256, 256), gfx::BufferFormat::YUV_420_BIPLANAR)); + properties.gl_image = CreateGLImage( + gfx::Size(256, 256), gfx::BufferFormat::YUV_420_BIPLANAR, true); properties.is_clipped = false; - CVPixelBufferCreateWithIOSurface(nullptr, properties.io_surface, nullptr, - properties.cv_pixel_buffer.InitializeInto()); CALayerProperties properties_black; properties_black.is_clipped = false; @@ -739,7 +870,7 @@ TEST_F(CALayerTreeTest, FullscreenLowPower) { // Test a configuration with no background. { std::unique_ptr<ui::CARendererLayerTree> new_ca_layer_tree( - new ui::CARendererLayerTree); + new ui::CARendererLayerTree(true, true)); bool result = ScheduleCALayer(new_ca_layer_tree.get(), &properties); EXPECT_TRUE(result); new_ca_layer_tree->CommitScheduledCALayers( @@ -769,7 +900,7 @@ TEST_F(CALayerTreeTest, FullscreenLowPower) { // Test a configuration with a black background. { std::unique_ptr<ui::CARendererLayerTree> new_ca_layer_tree( - new ui::CARendererLayerTree); + new ui::CARendererLayerTree(true, true)); bool result = ScheduleCALayer(new_ca_layer_tree.get(), &properties_black); EXPECT_TRUE(result); result = ScheduleCALayer(new_ca_layer_tree.get(), &properties); @@ -801,7 +932,7 @@ TEST_F(CALayerTreeTest, FullscreenLowPower) { // Test a configuration with a white background. It will fail. { std::unique_ptr<ui::CARendererLayerTree> new_ca_layer_tree( - new ui::CARendererLayerTree); + new ui::CARendererLayerTree(true, true)); bool result = ScheduleCALayer(new_ca_layer_tree.get(), &properties_white); EXPECT_TRUE(result); result = ScheduleCALayer(new_ca_layer_tree.get(), &properties); @@ -833,7 +964,7 @@ TEST_F(CALayerTreeTest, FullscreenLowPower) { // Test a configuration with a black foreground. It too will fail. { std::unique_ptr<ui::CARendererLayerTree> new_ca_layer_tree( - new ui::CARendererLayerTree); + new ui::CARendererLayerTree(true, true)); bool result = ScheduleCALayer(new_ca_layer_tree.get(), &properties); EXPECT_TRUE(result); result = ScheduleCALayer(new_ca_layer_tree.get(), &properties_black); diff --git a/chromium/ui/accelerated_widget_mac/ca_renderer_layer_tree.h b/chromium/ui/accelerated_widget_mac/ca_renderer_layer_tree.h index 7e7e31f9fb7..e4724a8f07e 100644 --- a/chromium/ui/accelerated_widget_mac/ca_renderer_layer_tree.h +++ b/chromium/ui/accelerated_widget_mac/ca_renderer_layer_tree.h @@ -14,6 +14,7 @@ #include "base/mac/scoped_cftyperef.h" #include "base/mac/scoped_nsobject.h" +#include "base/memory/ref_counted.h" #include "ui/accelerated_widget_mac/accelerated_widget_mac_export.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/rect_f.h" @@ -24,13 +25,16 @@ namespace ui { +struct CARendererLayerParams; + // The CARendererLayerTree will construct a hierarchy of CALayers from a linear // list provided by the CoreAnimation renderer using the algorithm and structure // referenced described in // https://docs.google.com/document/d/1DtSN9zzvCF44_FQPM7ie01UxGHagQ66zfF5L9HnigQY/edit?usp=sharing class ACCELERATED_WIDGET_MAC_EXPORT CARendererLayerTree { public: - CARendererLayerTree(); + CARendererLayerTree(bool allow_av_sample_buffer_display_layer, + bool allow_solid_color_layers); // This will remove all CALayers from this tree from their superlayer. ~CARendererLayerTree(); @@ -38,18 +42,7 @@ class ACCELERATED_WIDGET_MAC_EXPORT CARendererLayerTree { // 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, - base::ScopedCFTypeRef<CVPixelBufferRef> cv_pixel_buffer, - const gfx::RectF& contents_rect, - const gfx::Rect& rect, - unsigned background_color, - unsigned edge_aa_mask, - float opacity, - unsigned filter); + bool ScheduleCALayer(const CARendererLayerParams& params); // 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 @@ -67,11 +60,16 @@ class ACCELERATED_WIDGET_MAC_EXPORT CARendererLayerTree { bool CommitFullscreenLowPowerLayer( AVSampleBufferDisplayLayer* fullscreen_low_power_layer); + // Returns the contents used for a given solid color. + id ContentsForSolidColorForTesting(unsigned int color); + private: + class SolidColorContents; struct RootLayer; struct ClipAndSortingLayer; struct TransformLayer; struct ContentLayer; + friend struct ContentLayer; struct RootLayer { RootLayer(); @@ -82,19 +80,8 @@ class ACCELERATED_WIDGET_MAC_EXPORT CARendererLayerTree { // 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, - base::ScopedCFTypeRef<CVPixelBufferRef> cv_pixel_buffer, - const gfx::RectF& contents_rect, - const gfx::Rect& rect, - unsigned background_color, - unsigned edge_aa_mask, - float opacity, - unsigned filter); + bool AddContentLayer(CARendererLayerTree* tree, + const CARendererLayerParams& params); // Allocate CALayers for this layer and its children, and set their // properties appropriately. Re-use the CALayers from |old_layer| if @@ -120,16 +107,8 @@ class ACCELERATED_WIDGET_MAC_EXPORT CARendererLayerTree { // 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, - base::ScopedCFTypeRef<CVPixelBufferRef> cv_pixel_buffer, - const gfx::RectF& contents_rect, - const gfx::Rect& rect, - unsigned background_color, - unsigned edge_aa_mask, - float opacity, - unsigned filter); + void AddContentLayer(CARendererLayerTree* tree, + const CARendererLayerParams& params); void CommitToCA(CALayer* superlayer, ClipAndSortingLayer* old_layer, float scale_factor); @@ -151,15 +130,8 @@ class ACCELERATED_WIDGET_MAC_EXPORT CARendererLayerTree { // 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, - base::ScopedCFTypeRef<CVPixelBufferRef> cv_pixel_buffer, - const gfx::RectF& contents_rect, - const gfx::Rect& rect, - unsigned background_color, - unsigned edge_aa_mask, - float opacity, - unsigned filter); + void AddContentLayer(CARendererLayerTree* tree, + const CARendererLayerParams& params); void CommitToCA(CALayer* superlayer, TransformLayer* old_layer, float scale_factor); @@ -172,7 +144,8 @@ class ACCELERATED_WIDGET_MAC_EXPORT CARendererLayerTree { DISALLOW_COPY_AND_ASSIGN(TransformLayer); }; struct ContentLayer { - ContentLayer(base::ScopedCFTypeRef<IOSurfaceRef> io_surface, + ContentLayer(CARendererLayerTree* tree, + base::ScopedCFTypeRef<IOSurfaceRef> io_surface, base::ScopedCFTypeRef<CVPixelBufferRef> cv_pixel_buffer, const gfx::RectF& contents_rect, const gfx::Rect& rect, @@ -194,6 +167,7 @@ class ACCELERATED_WIDGET_MAC_EXPORT CARendererLayerTree { // their use count. const gfx::ScopedInUseIOSurface io_surface; const base::ScopedCFTypeRef<CVPixelBufferRef> cv_pixel_buffer; + scoped_refptr<SolidColorContents> solid_color_contents; gfx::RectF contents_rect; gfx::Rect rect; unsigned background_color = 0; @@ -216,6 +190,8 @@ class ACCELERATED_WIDGET_MAC_EXPORT CARendererLayerTree { RootLayer root_layer_; float scale_factor_ = 1; bool has_committed_ = false; + const bool allow_av_sample_buffer_display_layer_ = true; + const bool allow_solid_color_layers_ = true; private: DISALLOW_COPY_AND_ASSIGN(CARendererLayerTree); diff --git a/chromium/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm b/chromium/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm index 496cfb81e2f..a139dc7d073 100644 --- a/chromium/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm +++ b/chromium/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm @@ -10,13 +10,15 @@ #include <GLES2/gl2extchromium.h> #include "base/command_line.h" -#include "base/mac/mac_util.h" +#include "base/lazy_instance.h" #include "base/mac/sdk_forward_declarations.h" #include "base/trace_event/trace_event.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" +#include "ui/gl/ca_renderer_layer_params.h" +#include "ui/gl/gl_image_io_surface.h" #if !defined(MAC_OS_X_VERSION_10_8) || \ MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8 @@ -128,32 +130,91 @@ bool AVSampleBufferDisplayLayerEnqueueIOSurface( } // namespace -CARendererLayerTree::CARendererLayerTree() {} +class CARendererLayerTree::SolidColorContents + : public base::RefCounted<CARendererLayerTree::SolidColorContents> { + public: + static scoped_refptr<SolidColorContents> Get(SkColor color); + id GetContents() const; + + private: + friend class base::RefCounted<SolidColorContents>; + + SolidColorContents(SkColor color, IOSurfaceRef io_surface); + ~SolidColorContents(); + + SkColor color_ = 0; + base::ScopedCFTypeRef<IOSurfaceRef> io_surface_; + static base::LazyInstance<std::map<SkColor, SolidColorContents*>> map_; +}; + +base::LazyInstance<std::map<SkColor, CARendererLayerTree::SolidColorContents*>> + CARendererLayerTree::SolidColorContents::map_; + +// static +scoped_refptr<CARendererLayerTree::SolidColorContents> +CARendererLayerTree::SolidColorContents::Get(SkColor color) { + const int kSolidColorContentsSize = 16; + + auto found = map_.Get().find(color); + if (found != map_.Get().end()) + return found->second; + + IOSurfaceRef io_surface = CreateIOSurface( + gfx::Size(kSolidColorContentsSize, kSolidColorContentsSize), + gfx::BufferFormat::BGRA_8888); + if (!io_surface) + return nullptr; + + size_t bytes_per_row = IOSurfaceGetBytesPerRowOfPlane(io_surface, 0); + IOSurfaceLock(io_surface, 0, NULL); + char* row_base_address = + reinterpret_cast<char*>(IOSurfaceGetBaseAddress(io_surface)); + for (int i = 0; i < kSolidColorContentsSize; ++i) { + unsigned int* pixel = reinterpret_cast<unsigned int*>(row_base_address); + for (int j = 0; j < kSolidColorContentsSize; ++j) + *(pixel++) = color; + row_base_address += bytes_per_row; + } + IOSurfaceUnlock(io_surface, 0, NULL); + + return new SolidColorContents(color, io_surface); +} + +id CARendererLayerTree::SolidColorContents::GetContents() const { + return static_cast<id>(io_surface_.get()); +} + +CARendererLayerTree::SolidColorContents::SolidColorContents( + SkColor color, + IOSurfaceRef io_surface) + : color_(color), io_surface_(io_surface) { + DCHECK(map_.Get().find(color_) == map_.Get().end()); + map_.Get()[color_] = this; +} + +CARendererLayerTree::SolidColorContents::~SolidColorContents() { + auto found = map_.Get().find(color_); + DCHECK(found != map_.Get().end()); + DCHECK(found->second == this); + map_.Get().erase(color_); +} + +CARendererLayerTree::CARendererLayerTree( + bool allow_av_sample_buffer_display_layer, + bool allow_solid_color_layers) + : allow_av_sample_buffer_display_layer_( + allow_av_sample_buffer_display_layer), + allow_solid_color_layers_(allow_solid_color_layers) {} CARendererLayerTree::~CARendererLayerTree() {} -bool CARendererLayerTree::ScheduleCALayer( - bool is_clipped, - const gfx::Rect& clip_rect, - unsigned sorting_context_id, - const gfx::Transform& transform, - base::ScopedCFTypeRef<IOSurfaceRef> io_surface, - base::ScopedCFTypeRef<CVPixelBufferRef> cv_pixel_buffer, - const gfx::RectF& contents_rect, - const gfx::Rect& rect, - unsigned background_color, - unsigned edge_aa_mask, - float opacity, - unsigned filter) { +bool CARendererLayerTree::ScheduleCALayer(const CARendererLayerParams& params) { // Excessive logging to debug white screens (crbug.com/583805). // TODO(ccameron): change this back to a DLOG. if (has_committed_) { LOG(ERROR) << "ScheduleCALayer called after CommitScheduledCALayers."; return false; } - return root_layer_.AddContentLayer(is_clipped, clip_rect, sorting_context_id, - transform, io_surface, cv_pixel_buffer, - contents_rect, rect, background_color, - edge_aa_mask, opacity, filter); + return root_layer_.AddContentLayer(this, params); } void CARendererLayerTree::CommitScheduledCALayers( @@ -227,6 +288,9 @@ bool CARendererLayerTree::CommitFullscreenLowPowerLayer( return true; } +id CARendererLayerTree::ContentsForSolidColorForTesting(SkColor color) { + return SolidColorContents::Get(color)->GetContents(); +} CARendererLayerTree::RootLayer::RootLayer() {} @@ -281,6 +345,7 @@ CARendererLayerTree::TransformLayer::~TransformLayer() { } CARendererLayerTree::ContentLayer::ContentLayer( + CARendererLayerTree* tree, base::ScopedCFTypeRef<IOSurfaceRef> io_surface, base::ScopedCFTypeRef<CVPixelBufferRef> cv_pixel_buffer, const gfx::RectF& contents_rect, @@ -299,6 +364,17 @@ CARendererLayerTree::ContentLayer::ContentLayer( ca_filter(filter == GL_LINEAR ? kCAFilterLinear : kCAFilterNearest) { DCHECK(filter == GL_LINEAR || filter == GL_NEAREST); + // On Mac OS Sierra, solid color layers are not color color corrected to the + // output monitor color space, but IOSurface-backed layers are color + // corrected. Note that this is only the case when the CALayers are shared + // across processes. To make colors consistent across both solid color and + // IOSurface-backed layers, use a cache of solid-color IOSurfaces as contents. + // https://crbug.com/633805 + if (!io_surface && !tree->allow_solid_color_layers_) { + solid_color_contents = SolidColorContents::Get(background_color); + ContentLayer::contents_rect = gfx::RectF(0, 0, 1, 1); + } + // Because the root layer has setGeometryFlipped:YES, there is some ambiguity // about what exactly top and bottom mean. This ambiguity is resolved in // different ways for solid color CALayers and for CALayers that have content @@ -311,7 +387,7 @@ CARendererLayerTree::ContentLayer::ContentLayer( ca_edge_aa_mask |= kCALayerLeftEdge; if (edge_aa_mask & GL_CA_LAYER_EDGE_RIGHT_CHROMIUM) ca_edge_aa_mask |= kCALayerRightEdge; - if (io_surface) { + if (io_surface || solid_color_contents) { if (edge_aa_mask & GL_CA_LAYER_EDGE_TOP_CHROMIUM) ca_edge_aa_mask |= kCALayerBottomEdge; if (edge_aa_mask & GL_CA_LAYER_EDGE_BOTTOM_CHROMIUM) @@ -325,20 +401,18 @@ CARendererLayerTree::ContentLayer::ContentLayer( // Only allow 4:2:0 frames which fill the layer's contents to be promoted to // AV layers. - if (IOSurfaceGetPixelFormat(io_surface) == + if (tree->allow_av_sample_buffer_display_layer_ && + IOSurfaceGetPixelFormat(io_surface) == kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange && contents_rect == gfx::RectF(0, 0, 1, 1)) { - // Disable AVSampleBufferDisplayLayer on <10.11 due to reports of memory - // leaks on 10.9. - // https://crbug.com/631485 - if (base::mac::IsOSElCapitanOrLater()) - use_av_layer = true; + use_av_layer = true; } } CARendererLayerTree::ContentLayer::ContentLayer(ContentLayer&& layer) : io_surface(layer.io_surface), cv_pixel_buffer(layer.cv_pixel_buffer), + solid_color_contents(layer.solid_color_contents), contents_rect(layer.contents_rect), rect(layer.rect), background_color(layer.background_color), @@ -357,18 +431,8 @@ CARendererLayerTree::ContentLayer::~ContentLayer() { } bool CARendererLayerTree::RootLayer::AddContentLayer( - bool is_clipped, - const gfx::Rect& clip_rect, - unsigned sorting_context_id, - const gfx::Transform& transform, - base::ScopedCFTypeRef<IOSurfaceRef> io_surface, - base::ScopedCFTypeRef<CVPixelBufferRef> cv_pixel_buffer, - const gfx::RectF& contents_rect, - const gfx::Rect& rect, - unsigned background_color, - unsigned edge_aa_mask, - float opacity, - unsigned filter) { + CARendererLayerTree* tree, + const CARendererLayerParams& params) { bool needs_new_clip_and_sorting_layer = true; // In sorting_context_id 0, all quads are listed in back-to-front order. @@ -376,16 +440,16 @@ bool CARendererLayerTree::RootLayer::AddContentLayer( // 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(); + !params.sorting_context_id && !params.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)) { + if (params.sorting_context_id && + current_layer.sorting_context_id == params.sorting_context_id && + (current_layer.is_clipped != params.is_clipped || + current_layer.clip_rect != params.clip_rect)) { // Excessive logging to debug white screens (crbug.com/583805). // TODO(ccameron): change this back to a DLOG. LOG(ERROR) << "CALayer changed clip inside non-zero sorting context."; @@ -393,58 +457,52 @@ bool CARendererLayerTree::RootLayer::AddContentLayer( } 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) { + current_layer.is_clipped == params.is_clipped && + current_layer.clip_rect == params.clip_rect && + current_layer.sorting_context_id == params.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.push_back(ClipAndSortingLayer( + params.is_clipped, params.clip_rect, params.sorting_context_id, + is_singleton_sorting_context)); } - clip_and_sorting_layers.back().AddContentLayer( - transform, io_surface, cv_pixel_buffer, contents_rect, rect, - background_color, edge_aa_mask, opacity, filter); + clip_and_sorting_layers.back().AddContentLayer(tree, params); return true; } void CARendererLayerTree::ClipAndSortingLayer::AddContentLayer( - const gfx::Transform& transform, - base::ScopedCFTypeRef<IOSurfaceRef> io_surface, - base::ScopedCFTypeRef<CVPixelBufferRef> cv_pixel_buffer, - const gfx::RectF& contents_rect, - const gfx::Rect& rect, - unsigned background_color, - unsigned edge_aa_mask, - float opacity, - unsigned filter) { + CARendererLayerTree* tree, + const CARendererLayerParams& params) { bool needs_new_transform_layer = true; if (!transform_layers.empty()) { const TransformLayer& current_layer = transform_layers.back(); - if (current_layer.transform == transform) + if (current_layer.transform == params.transform) needs_new_transform_layer = false; } if (needs_new_transform_layer) - transform_layers.push_back(TransformLayer(transform)); - transform_layers.back().AddContentLayer(io_surface, cv_pixel_buffer, - contents_rect, rect, background_color, - edge_aa_mask, opacity, filter); + transform_layers.push_back(TransformLayer(params.transform)); + transform_layers.back().AddContentLayer(tree, params); } void CARendererLayerTree::TransformLayer::AddContentLayer( - base::ScopedCFTypeRef<IOSurfaceRef> io_surface, - base::ScopedCFTypeRef<CVPixelBufferRef> cv_pixel_buffer, - const gfx::RectF& contents_rect, - const gfx::Rect& rect, - unsigned background_color, - unsigned edge_aa_mask, - float opacity, - unsigned filter) { - content_layers.push_back(ContentLayer(io_surface, cv_pixel_buffer, - contents_rect, rect, background_color, - edge_aa_mask, opacity, filter)); + CARendererLayerTree* tree, + const CARendererLayerParams& params) { + base::ScopedCFTypeRef<IOSurfaceRef> io_surface; + base::ScopedCFTypeRef<CVPixelBufferRef> cv_pixel_buffer; + if (params.image) { + gl::GLImageIOSurface* io_surface_image = + gl::GLImageIOSurface::FromGLImage(params.image); + DCHECK(io_surface_image); + io_surface = io_surface_image->io_surface(); + cv_pixel_buffer = io_surface_image->cv_pixel_buffer(); + } + + content_layers.push_back( + ContentLayer(tree, io_surface, cv_pixel_buffer, params.contents_rect, + params.rect, params.background_color, params.edge_aa_mask, + params.opacity, params.filter)); } void CARendererLayerTree::RootLayer::CommitToCA(CALayer* superlayer, @@ -577,7 +635,8 @@ void CARendererLayerTree::ContentLayer::CommitToCA(CALayer* superlayer, std::swap(ca_layer, old_layer->ca_layer); std::swap(av_layer, old_layer->av_layer); update_contents = old_layer->io_surface != io_surface || - old_layer->cv_pixel_buffer != cv_pixel_buffer; + old_layer->cv_pixel_buffer != cv_pixel_buffer || + old_layer->solid_color_contents != solid_color_contents; update_contents_rect = old_layer->contents_rect != contents_rect; update_rect = old_layer->rect != rect; update_background_color = old_layer->background_color != background_color; @@ -614,7 +673,13 @@ void CARendererLayerTree::ContentLayer::CommitToCA(CALayer* superlayer, } } else { if (update_contents) { - [ca_layer setContents:static_cast<id>(io_surface.get())]; + if (io_surface) { + [ca_layer setContents:static_cast<id>(io_surface.get())]; + } else if (solid_color_contents) { + [ca_layer setContents:solid_color_contents->GetContents()]; + } else { + [ca_layer setContents:nil]; + } if ([ca_layer respondsToSelector:(@selector(setContentsScale:))]) [ca_layer setContentsScale:scale_factor]; } @@ -655,9 +720,15 @@ void CARendererLayerTree::ContentLayer::CommitToCA(CALayer* superlayer, if (use_av_layer) { // Yellow represents an AV layer that changed this frame. color.reset(CGColorCreateGenericRGB(1, 1, 0, 1)); - } else { - // Pink represents a CALayer that changed this frame. + } else if (io_surface) { + // Magenta represents a CALayer that changed this frame. color.reset(CGColorCreateGenericRGB(1, 0, 1, 1)); + } else if (solid_color_contents) { + // Cyan represents a solid color IOSurface-backed layer. + color.reset(CGColorCreateGenericRGB(0, 1, 1, 1)); + } else { + // Red represents a solid color layer. + color.reset(CGColorCreateGenericRGB(1, 0, 0, 1)); } } else { // Grey represents a CALayer that has not changed. |