Reland "Simple frame production"
This is a reland of commit 23a50951556c98fab168260ac37c32006fe546d1
Update OnBeginFrame call in tests to match new signature.
No other changes.
Original change's description:
> Simple frame production
>
> A simple frame production algorithm. Supports:
> * a single root render pass only
> * Layer position, transform
> * SetMaskToBounds for axis-aligned layer
>
> Add AppendQuads for SolidColorLayer as the simplest layer for unit
> testing.
>
> Notable missing features:
> * Non-axis aligned clip and filters (requires non-root pass)
> * Damage tracking
> * Quad occlusion culling
> * Visible rect on quads
>
> Add test harness to unit test frame generation.
>
> Bug: 1408128
> Change-Id: I5508292876ddc66b59b118742dbceacb2c6a7d8e
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4245000
> Reviewed-by: Kyle Charbonneau <kylechar@chromium.org>
> Commit-Queue: Bo Liu <boliu@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#1106868}
Bug: 1408128
Change-Id: Ie004e2d0542ce99f0a23c5420b844162e6788dd6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4265173
Auto-Submit: Bo Liu <boliu@chromium.org>
Commit-Queue: Bo Liu <boliu@chromium.org>
Commit-Queue: Kyle Charbonneau <kylechar@chromium.org>
Reviewed-by: Kyle Charbonneau <kylechar@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1106946}
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index 48def44ab..98782bb2 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -784,6 +784,7 @@
"resources/resource_pool_unittest.cc",
"scheduler/scheduler_state_machine_unittest.cc",
"scheduler/scheduler_unittest.cc",
+ "slim/slim_layer_tree_compositor_frame_unittest.cc",
"slim/slim_layer_tree_unittest.cc",
"slim/slim_layer_unittest.cc",
"slim/test_frame_sink_impl.cc",
diff --git a/cc/slim/frame_sink_impl.cc b/cc/slim/frame_sink_impl.cc
index c83845b2..3efcf98 100644
--- a/cc/slim/frame_sink_impl.cc
+++ b/cc/slim/frame_sink_impl.cc
@@ -85,7 +85,8 @@
base::BindOnce(&FrameSinkImpl::OnContextLost, base::Unretained(this)));
client_receiver_.Bind(std::move(pending_client_receiver_), task_runner_);
- frame_sink_remote_->InitializeCompositorFrameSinkType(
+ frame_sink_ = frame_sink_remote_.get();
+ frame_sink_->InitializeCompositorFrameSinkType(
viz::mojom::CompositorFrameSinkType::kLayerTree);
#if BUILDFLAG(IS_ANDROID)
@@ -94,7 +95,7 @@
if (io_thread_id_ != base::kInvalidThreadId) {
thread_ids.push_back(io_thread_id_);
}
- frame_sink_remote_->SetThreadIds(thread_ids);
+ frame_sink_->SetThreadIds(thread_ids);
#endif
return true;
}
@@ -108,7 +109,7 @@
return;
}
needs_begin_frame_ = needs_begin_frame;
- frame_sink_remote_->SetNeedsBeginFrame(needs_begin_frame);
+ frame_sink_->SetNeedsBeginFrame(needs_begin_frame);
}
void FrameSinkImpl::UploadUIResource(cc::UIResourceId resource_id,
@@ -225,7 +226,7 @@
}
if (!local_surface_id_.is_valid()) {
- frame_sink_remote_->DidNotProduceFrame(
+ frame_sink_->DidNotProduceFrame(
viz::BeginFrameAck(begin_frame_args, false));
return;
}
@@ -235,11 +236,19 @@
viz::HitTestRegionList hit_test_region_list;
if (!client_->BeginFrame(begin_frame_args, frame, viz_resource_ids,
hit_test_region_list)) {
- frame_sink_remote_->DidNotProduceFrame(
+ frame_sink_->DidNotProduceFrame(
viz::BeginFrameAck(begin_frame_args, false));
return;
}
+ if (local_surface_id_ == last_submitted_local_surface_id_) {
+ DCHECK_EQ(last_submitted_device_scale_factor_, frame.device_scale_factor());
+ DCHECK_EQ(last_submitted_size_in_pixels_.height(),
+ frame.size_in_pixels().height());
+ DCHECK_EQ(last_submitted_size_in_pixels_.width(),
+ frame.size_in_pixels().width());
+ }
+
resource_provider_.PrepareSendToParent(std::move(viz_resource_ids).extract(),
&frame.resource_list,
context_provider_.get());
@@ -254,7 +263,7 @@
{
TRACE_EVENT0("cc", "SubmitCompositorFrame");
- frame_sink_remote_->SubmitCompositorFrame(
+ frame_sink_->SubmitCompositorFrame(
local_surface_id_, std::move(frame),
send_new_hit_test_region_list ? hit_test_region_list_ : absl::nullopt,
0);
diff --git a/cc/slim/frame_sink_impl.h b/cc/slim/frame_sink_impl.h
index 61db3293..4d7c5ff 100644
--- a/cc/slim/frame_sink_impl.h
+++ b/cc/slim/frame_sink_impl.h
@@ -122,6 +122,8 @@
pending_client_receiver_;
mojo::AssociatedRemote<viz::mojom::CompositorFrameSink> frame_sink_remote_;
+ // Separate from AssociatedRemote above for testing.
+ viz::mojom::CompositorFrameSink* frame_sink_ = nullptr;
mojo::Receiver<viz::mojom::CompositorFrameSinkClient> client_receiver_{this};
scoped_refptr<viz::ContextProvider> context_provider_;
raw_ptr<FrameSinkImplClient> client_ = nullptr;
@@ -133,6 +135,10 @@
absl::optional<viz::HitTestRegionList> hit_test_region_list_;
base::PlatformThreadId io_thread_id_;
+ viz::LocalSurfaceId last_submitted_local_surface_id_;
+ float last_submitted_device_scale_factor_ = 1.f;
+ gfx::Size last_submitted_size_in_pixels_;
+
bool needs_begin_frame_ = false;
};
diff --git a/cc/slim/layer.cc b/cc/slim/layer.cc
index 4ccb227..e226867 100644
--- a/cc/slim/layer.cc
+++ b/cc/slim/layer.cc
@@ -11,7 +11,6 @@
#include "base/atomic_sequence_num.h"
#include "base/check.h"
#include "base/containers/cxx20_erase_vector.h"
-#include "base/feature_list.h"
#include "base/ranges/algorithm.h"
#include "cc/layers/layer.h"
#include "cc/paint/filter_operation.h"
@@ -20,7 +19,6 @@
#include "cc/slim/layer_tree.h"
#include "cc/slim/layer_tree_impl.h"
#include "components/viz/common/quads/shared_quad_state.h"
-#include "components/viz/common/quads/solid_color_draw_quad.h"
namespace cc::slim {
@@ -58,7 +56,7 @@
Layer::Layer(scoped_refptr<cc::Layer> cc_layer)
: cc_layer_(std::move(cc_layer)),
- id_(g_next_id.GetNext()),
+ id_(g_next_id.GetNext() + 1),
is_drawable_(false),
contents_opaque_(false),
draws_content_(false),
@@ -375,6 +373,23 @@
return is_drawable_;
}
+gfx::Transform Layer::ComputeTransformToParent() {
+ // Layer transform is:
+ // position x transform_origin x transform x -transform_origin
+ gfx::Transform transform =
+ gfx::Transform::MakeTranslation(position_.x(), position_.y());
+ transform.Translate3d(transform_origin_.x(), transform_origin_.y(),
+ transform_origin_.z());
+ transform.PreConcat(transform_);
+ transform.Translate3d(-transform_origin_.x(), -transform_origin_.y(),
+ -transform_origin_.z());
+ return transform;
+}
+
+void Layer::AppendQuads(viz::CompositorRenderPass& render_pass,
+ const gfx::Transform& transform,
+ const gfx::Rect* clip) {}
+
void Layer::NotifyTreeChanged() {
if (cc_layer()) {
return;
@@ -391,4 +406,23 @@
}
}
+viz::SharedQuadState* Layer::CreateAndAppendSharedQuadState(
+ viz::CompositorRenderPass& render_pass,
+ const gfx::Transform& transform,
+ const gfx::Rect* clip) {
+ viz::SharedQuadState* quad_state =
+ render_pass.CreateAndAppendSharedQuadState();
+ const gfx::Rect rect{bounds()};
+ absl::optional<gfx::Rect> clip_opt;
+ if (clip) {
+ clip_opt = *clip;
+ }
+ // TODO(crbug.com/1408128): Set visible_layer_rect properly.
+ quad_state->SetAll(transform, /*layer_rect=*/rect,
+ /*visible_layer_rect=*/rect, gfx::MaskFilterInfo(),
+ std::move(clip_opt), contents_opaque(), opacity(),
+ SkBlendMode::kSrcOver, 0);
+ return quad_state;
+}
+
} // namespace cc::slim
diff --git a/cc/slim/layer.h b/cc/slim/layer.h
index e0c64bc..cd9b1774 100644
--- a/cc/slim/layer.h
+++ b/cc/slim/layer.h
@@ -23,10 +23,16 @@
class Layer;
}
+namespace viz {
+class CompositorRenderPass;
+class SharedQuadState;
+} // namespace viz
+
namespace cc::slim {
class LayerTree;
class LayerTreeCcWrapper;
+class LayerTreeImpl;
// Base class for composited layers. Special layer types are derived from
// this class. Each layer is an independent unit in the compositor, be that
@@ -178,15 +184,24 @@
protected:
friend class LayerTreeCcWrapper;
+ friend class LayerTreeImpl;
explicit Layer(scoped_refptr<cc::Layer> cc_layer);
virtual ~Layer();
// Called by LayerTree.
+ gfx::Transform ComputeTransformToParent();
virtual bool HasDrawableContent() const;
+ virtual void AppendQuads(viz::CompositorRenderPass& render_pass,
+ const gfx::Transform& transform,
+ const gfx::Rect* clip);
void NotifyTreeChanged();
void NotifyPropertyChanged();
+ virtual viz::SharedQuadState* CreateAndAppendSharedQuadState(
+ viz::CompositorRenderPass& render_pass,
+ const gfx::Transform& transform,
+ const gfx::Rect* clip);
const scoped_refptr<cc::Layer> cc_layer_;
diff --git a/cc/slim/layer_tree_impl.cc b/cc/slim/layer_tree_impl.cc
index b0c3821d..6a849b2 100644
--- a/cc/slim/layer_tree_impl.cc
+++ b/cc/slim/layer_tree_impl.cc
@@ -6,15 +6,24 @@
#include <algorithm>
#include <memory>
+#include <vector>
#include "base/auto_reset.h"
+#include "base/containers/adapters.h"
+#include "base/trace_event/trace_event.h"
#include "cc/slim/frame_sink_impl.h"
#include "cc/slim/layer.h"
#include "cc/slim/layer_tree_client.h"
#include "components/viz/common/frame_sinks/begin_frame_args.h"
#include "components/viz/common/hit_test/hit_test_region_list.h"
+#include "components/viz/common/quads/compositor_frame.h"
+#include "components/viz/common/quads/compositor_frame_metadata.h"
+#include "components/viz/common/quads/compositor_render_pass.h"
+#include "components/viz/common/quads/draw_quad.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/geometry/transform.h"
namespace cc::slim {
@@ -76,7 +85,7 @@
}
void LayerTreeImpl::set_display_transform_hint(gfx::OverlayTransform hint) {
- // TODO(crbug.com/1408128): Implement.
+ display_transform_hint_ = hint;
}
void LayerTreeImpl::RequestCopyOfOutput(
@@ -182,9 +191,16 @@
// changes made by client `BeginFrame` are about to be drawn, so there is no
// need for another frame.
needs_draw_ = false;
- // TODO(crbug.com/1408128): Implement frame production here.
+
+ if (!root_) {
+ UpdateNeedsBeginFrame();
+ return false;
+ }
+
+ GenerateCompositorFrame(args, out_frame, out_resource_ids,
+ out_hit_test_region_list);
UpdateNeedsBeginFrame();
- return false;
+ return true;
}
void LayerTreeImpl::DidReceiveCompositorFrameAck() {
@@ -262,4 +278,88 @@
return client_needs_one_begin_frame_ || needs_draw_;
}
+void LayerTreeImpl::GenerateCompositorFrame(
+ const viz::BeginFrameArgs& args,
+ viz::CompositorFrame& out_frame,
+ base::flat_set<viz::ResourceId>& out_resource_ids,
+ viz::HitTestRegionList& out_hit_test_region_list) {
+ // TODO(crbug.com/1408128): Only has a very simple and basic compositor frame
+ // generation. Some missing features include:
+ // * Support multiple render passes (non-axis aligned clip, filters)
+ // * Damage tracking
+ // * Occlusion culling
+ // * Visible rect (ie clip) on quads
+ // * Surface embedding fields (referenced surfaces, activation dependency,
+ // deadline)
+ TRACE_EVENT0("cc", "slim::LayerTreeImpl::ProduceFrame");
+ auto render_pass = viz::CompositorRenderPass::Create();
+ render_pass->SetNew(viz::CompositorRenderPassId(root_->id()),
+ /*output_rect=*/device_viewport_rect_,
+ /*damage_rect=*/device_viewport_rect_,
+ /*transform_to_root_target=*/gfx::Transform());
+
+ out_frame.metadata.frame_token = ++next_frame_token_;
+ out_frame.metadata.begin_frame_ack =
+ viz::BeginFrameAck(args, /*has_damage=*/true);
+ out_frame.metadata.device_scale_factor = device_scale_factor_;
+ out_frame.metadata.root_background_color = background_color_;
+ out_frame.metadata.referenced_surfaces = std::vector<viz::SurfaceRange>(
+ referenced_surfaces_.begin(), referenced_surfaces_.end());
+ out_frame.metadata.top_controls_visible_height = top_controls_visible_height_;
+ top_controls_visible_height_.reset();
+ out_frame.metadata.display_transform_hint = display_transform_hint_;
+
+ Draw(*root_, *render_pass, /*transform_to_target=*/gfx::Transform(),
+ /*clip_from_parent=*/nullptr);
+
+ out_frame.render_pass_list.push_back(std::move(render_pass));
+
+ for (const auto& pass : out_frame.render_pass_list) {
+ for (const auto* quad : pass->quad_list) {
+ for (viz::ResourceId resource_id : quad->resources) {
+ out_resource_ids.insert(resource_id);
+ }
+ }
+ }
+}
+
+void LayerTreeImpl::Draw(Layer& layer,
+ viz::CompositorRenderPass& parent_pass,
+ const gfx::Transform& transform_to_target,
+ const gfx::Rect* clip_from_parent) {
+ if (layer.hide_layer_and_subtree()) {
+ return;
+ }
+
+ gfx::Transform transform_to_parent = layer.ComputeTransformToParent();
+
+ // New transform is: parent transform x layer transform.
+ gfx::Transform new_transform_to_target = transform_to_target;
+ new_transform_to_target.PreConcat(transform_to_parent);
+
+ bool use_new_clip = false;
+ gfx::Rect new_clip;
+ // Drop non-axis aligned clip instead of using new render pass.
+ // TODO(crbug.com/1408128): Clip in layer space (visible rect) for clip
+ // that is not an exact integer.
+ if (layer.masks_to_bounds() &&
+ new_transform_to_target.Preserves2dAxisAlignment()) {
+ new_clip.set_size(layer.bounds());
+ new_clip = new_transform_to_target.MapRect(new_clip);
+ if (clip_from_parent) {
+ new_clip.Intersect(*clip_from_parent);
+ }
+ use_new_clip = true;
+ }
+ const gfx::Rect* clip = use_new_clip ? &new_clip : clip_from_parent;
+
+ for (auto& child : base::Reversed(layer.children())) {
+ Draw(*child, parent_pass, new_transform_to_target, clip);
+ }
+
+ if (!layer.bounds().IsEmpty() && layer.HasDrawableContent()) {
+ layer.AppendQuads(parent_pass, new_transform_to_target, clip);
+ }
+}
+
} // namespace cc::slim
diff --git a/cc/slim/layer_tree_impl.h b/cc/slim/layer_tree_impl.h
index 8c3809e..58e40a6 100644
--- a/cc/slim/layer_tree_impl.h
+++ b/cc/slim/layer_tree_impl.h
@@ -32,6 +32,10 @@
class UIResourceManager;
} // namespace cc
+namespace viz {
+class CompositorRenderPass;
+} // namespace viz
+
namespace cc::slim {
class FrameSinkImpl;
@@ -104,6 +108,15 @@
// submitted in a CompositorFrame.
void SetNeedsDraw();
bool NeedsBeginFrames() const;
+ void GenerateCompositorFrame(
+ const viz::BeginFrameArgs& args,
+ viz::CompositorFrame& out_frame,
+ base::flat_set<viz::ResourceId>& out_resource_ids,
+ viz::HitTestRegionList& out_hit_test_region_list);
+ void Draw(Layer& layer,
+ viz::CompositorRenderPass& render_pass,
+ const gfx::Transform& transform_to_target,
+ const gfx::Rect* clip_from_parent);
const raw_ptr<LayerTreeClient> client_;
scoped_refptr<Layer> root_;
@@ -132,6 +145,8 @@
SkColor4f background_color_ = SkColors::kWhite;
absl::optional<float> top_controls_visible_height_;
base::flat_set<viz::SurfaceRange> referenced_surfaces_;
+ viz::FrameTokenGenerator next_frame_token_;
+ gfx::OverlayTransform display_transform_hint_ = gfx::OVERLAY_TRANSFORM_NONE;
base::WeakPtrFactory<LayerTreeImpl> weak_factory_{this};
};
diff --git a/cc/slim/slim_layer_tree_compositor_frame_unittest.cc b/cc/slim/slim_layer_tree_compositor_frame_unittest.cc
new file mode 100644
index 0000000..8fe76a85
--- /dev/null
+++ b/cc/slim/slim_layer_tree_compositor_frame_unittest.cc
@@ -0,0 +1,310 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+#include <utility>
+
+#include "base/memory/weak_ptr.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/time/time.h"
+#include "base/unguessable_token.h"
+#include "cc/slim/features.h"
+#include "cc/slim/layer.h"
+#include "cc/slim/solid_color_layer.h"
+#include "cc/slim/test_frame_sink_impl.h"
+#include "cc/slim/test_layer_tree_client.h"
+#include "cc/slim/test_layer_tree_impl.h"
+#include "components/viz/common/frame_sinks/begin_frame_args.h"
+#include "components/viz/common/quads/compositor_frame.h"
+#include "components/viz/common/quads/solid_color_draw_quad.h"
+#include "components/viz/common/surfaces/local_surface_id.h"
+#include "components/viz/test/draw_quad_matchers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace cc::slim {
+
+namespace {
+
+using testing::AllOf;
+using testing::ElementsAre;
+
+class SlimLayerTreeCompositorFrameTest : public testing::Test {
+ public:
+ void SetUp() override {
+ scoped_feature_list_.InitAndEnableFeature(features::kSlimCompositor);
+ layer_tree_ = std::make_unique<TestLayerTreeImpl>(&client_);
+ layer_tree_->SetVisible(true);
+
+ auto frame_sink = TestFrameSinkImpl::Create();
+ frame_sink_ = frame_sink->GetWeakPtr();
+ layer_tree_->SetFrameSink(std::move(frame_sink));
+
+ viewport_ = gfx::Rect(100, 100);
+ base::UnguessableToken token = base::UnguessableToken::Create();
+ local_surface_id_ = viz::LocalSurfaceId(1u, 2u, token);
+ EXPECT_TRUE(local_surface_id_.is_valid());
+ layer_tree_->SetViewportRectAndScale(
+ viewport_, /*device_scale_factor=*/1.0f, local_surface_id_);
+ }
+
+ void IncrementLocalSurfaceId() {
+ DCHECK(local_surface_id_.is_valid());
+ local_surface_id_ =
+ viz::LocalSurfaceId(local_surface_id_.parent_sequence_number(),
+ local_surface_id_.child_sequence_number() + 1,
+ local_surface_id_.embed_token());
+ DCHECK(local_surface_id_.is_valid());
+ }
+
+ viz::CompositorFrame ProduceFrame() {
+ layer_tree_->SetNeedsRedraw();
+ EXPECT_TRUE(layer_tree_->NeedsBeginFrames());
+ base::TimeTicks frame_time = base::TimeTicks::Now();
+ base::TimeDelta interval = viz::BeginFrameArgs::DefaultInterval();
+ viz::BeginFrameArgs begin_frame_args = viz::BeginFrameArgs::Create(
+ BEGINFRAME_FROM_HERE,
+ /*source_id=*/1, ++sequence_id_, frame_time, frame_time + interval,
+ interval, viz::BeginFrameArgs::NORMAL);
+ frame_sink_->OnBeginFrame(begin_frame_args, {}, /*frame_ack=*/false, {});
+ viz::CompositorFrame frame = frame_sink_->TakeLastFrame();
+ frame_sink_->DidReceiveCompositorFrameAck({});
+ return frame;
+ }
+
+ scoped_refptr<SolidColorLayer> CreateSolidColorLayer(const gfx::Size& bounds,
+ SkColor4f color) {
+ auto solid_color_layer = SolidColorLayer::Create();
+ solid_color_layer->SetBounds(bounds);
+ solid_color_layer->SetBackgroundColor(color);
+ solid_color_layer->SetIsDrawable(true);
+ return solid_color_layer;
+ }
+
+ protected:
+ base::test::ScopedFeatureList scoped_feature_list_;
+ TestLayerTreeClient client_;
+ std::unique_ptr<TestLayerTreeImpl> layer_tree_;
+ base::WeakPtr<TestFrameSinkImpl> frame_sink_;
+
+ uint64_t sequence_id_ = 0;
+
+ gfx::Rect viewport_;
+ viz::LocalSurfaceId local_surface_id_;
+};
+
+TEST_F(SlimLayerTreeCompositorFrameTest, CompositorFrameMetadataBasics) {
+ auto solid_color_layer =
+ CreateSolidColorLayer(viewport_.size(), SkColors::kGray);
+ layer_tree_->SetRoot(solid_color_layer);
+
+ // TODO(crbug.com/1408128): Add tests for features once implemented:
+ // * reference_surfaces
+ // * activation_dependencies
+ // * deadline
+ uint32_t first_frame_token = 0u;
+ {
+ viz::CompositorFrame frame = ProduceFrame();
+ viz::CompositorFrameMetadata& metadata = frame.metadata;
+ EXPECT_NE(0u, metadata.frame_token);
+ first_frame_token = metadata.frame_token;
+ EXPECT_EQ(sequence_id_, metadata.begin_frame_ack.frame_id.sequence_number);
+ EXPECT_EQ(1.0f, metadata.device_scale_factor);
+ EXPECT_EQ(SkColors::kWhite, metadata.root_background_color);
+ EXPECT_EQ(gfx::OVERLAY_TRANSFORM_NONE, metadata.display_transform_hint);
+ EXPECT_EQ(absl::nullopt, metadata.top_controls_visible_height);
+ }
+
+ IncrementLocalSurfaceId();
+ layer_tree_->SetViewportRectAndScale(viewport_, /*device_scale_factor=*/2.0f,
+ local_surface_id_);
+ layer_tree_->set_background_color(SkColors::kBlue);
+ layer_tree_->set_display_transform_hint(gfx::OVERLAY_TRANSFORM_ROTATE_90);
+ layer_tree_->UpdateTopControlsVisibleHeight(5.0f);
+ {
+ viz::CompositorFrame frame = ProduceFrame();
+ viz::CompositorFrameMetadata& metadata = frame.metadata;
+ EXPECT_NE(0u, metadata.frame_token);
+ EXPECT_NE(first_frame_token, metadata.frame_token);
+ EXPECT_EQ(sequence_id_, metadata.begin_frame_ack.frame_id.sequence_number);
+ EXPECT_EQ(2.0f, metadata.device_scale_factor);
+ EXPECT_EQ(SkColors::kBlue, metadata.root_background_color);
+ EXPECT_EQ(gfx::OVERLAY_TRANSFORM_ROTATE_90,
+ metadata.display_transform_hint);
+ EXPECT_EQ(5.0f, metadata.top_controls_visible_height);
+ }
+}
+
+TEST_F(SlimLayerTreeCompositorFrameTest, OneSolidColorQuad) {
+ auto solid_color_layer =
+ CreateSolidColorLayer(viewport_.size(), SkColors::kGray);
+ layer_tree_->SetRoot(solid_color_layer);
+
+ viz::CompositorFrame frame = ProduceFrame();
+
+ ASSERT_EQ(frame.render_pass_list.size(), 1u);
+ auto& pass = frame.render_pass_list.back();
+ EXPECT_EQ(pass->output_rect, viewport_);
+ EXPECT_EQ(pass->damage_rect, viewport_);
+ EXPECT_EQ(pass->transform_to_root_target, gfx::Transform());
+
+ ASSERT_THAT(
+ pass->quad_list,
+ ElementsAre(AllOf(viz::IsSolidColorQuad(SkColors::kGray),
+ viz::HasRect(viewport_), viz::HasVisibleRect(viewport_),
+ viz::HasTransform(gfx::Transform()))));
+ auto* quad = pass->quad_list.back();
+ auto* shared_quad_state = quad->shared_quad_state;
+
+ EXPECT_EQ(shared_quad_state->quad_layer_rect, viewport_);
+ EXPECT_EQ(shared_quad_state->visible_quad_layer_rect, viewport_);
+ EXPECT_EQ(shared_quad_state->clip_rect, absl::nullopt);
+ EXPECT_EQ(shared_quad_state->are_contents_opaque, true);
+ EXPECT_EQ(shared_quad_state->opacity, 1.0f);
+ EXPECT_EQ(shared_quad_state->blend_mode, SkBlendMode::kSrcOver);
+}
+
+TEST_F(SlimLayerTreeCompositorFrameTest, LayerTransform) {
+ auto root_layer = CreateSolidColorLayer(viewport_.size(), SkColors::kGray);
+ layer_tree_->SetRoot(root_layer);
+
+ auto child = CreateSolidColorLayer(gfx::Size(10, 20), SkColors::kGreen);
+ root_layer->AddChild(child);
+
+ auto check_child_quad = [&](gfx::Rect expected_rect_in_root) {
+ viz::CompositorFrame frame = ProduceFrame();
+ ASSERT_EQ(frame.render_pass_list.size(), 1u);
+ auto& pass = frame.render_pass_list.back();
+ ASSERT_THAT(pass->quad_list,
+ ElementsAre(AllOf(viz::IsSolidColorQuad(SkColors::kGreen),
+ viz::HasRect(gfx::Rect(10, 20)),
+ viz::HasVisibleRect(gfx::Rect(10, 20))),
+ AllOf(viz::IsSolidColorQuad(SkColors::kGray),
+ viz::HasRect(viewport_),
+ viz::HasVisibleRect(viewport_))));
+
+ auto* quad = pass->quad_list.front();
+ auto* shared_quad_state = quad->shared_quad_state;
+
+ EXPECT_EQ(shared_quad_state->quad_layer_rect, gfx::Rect(10, 20));
+ EXPECT_EQ(shared_quad_state->visible_quad_layer_rect, gfx::Rect(10, 20));
+
+ gfx::Rect rect_in_root =
+ shared_quad_state->quad_to_target_transform.MapRect(quad->rect);
+ EXPECT_EQ(expected_rect_in_root, rect_in_root);
+ };
+
+ child->SetPosition(gfx::PointF(30.0f, 30.0f));
+ check_child_quad(gfx::Rect(30, 30, 10, 20));
+
+ child->SetTransform(gfx::Transform::MakeTranslation(10.0f, 10.0f));
+ check_child_quad(gfx::Rect(40, 40, 10, 20));
+
+ // Rotate about top left corner.
+ child->SetTransform(gfx::Transform::Make90degRotation());
+ check_child_quad(gfx::Rect(10, 30, 20, 10));
+
+ // Rotate about the center.
+ child->SetTransformOrigin(gfx::Point3F(5.0f, 10.0f, 0.0f));
+ check_child_quad(gfx::Rect(25, 35, 20, 10));
+}
+
+TEST_F(SlimLayerTreeCompositorFrameTest, ChildOrder) {
+ auto root_layer = CreateSolidColorLayer(viewport_.size(), SkColors::kGray);
+ layer_tree_->SetRoot(root_layer);
+
+ scoped_refptr<SolidColorLayer> children[] = {
+ CreateSolidColorLayer(gfx::Size(10, 10), SkColors::kBlue),
+ CreateSolidColorLayer(gfx::Size(10, 10), SkColors::kGreen),
+ CreateSolidColorLayer(gfx::Size(10, 10), SkColors::kMagenta),
+ CreateSolidColorLayer(gfx::Size(10, 10), SkColors::kRed),
+ CreateSolidColorLayer(gfx::Size(10, 10), SkColors::kYellow)};
+
+ // Build tree such that quads appear in child order.
+ // Quads are appended post order depth first, in reverse child order.
+ // root <- child4 <- child3
+ // <- child2
+ // <- child1 <- child0
+ root_layer->AddChild(children[4]);
+ root_layer->AddChild(children[1]);
+ children[4]->AddChild(children[3]);
+ children[4]->AddChild(children[2]);
+ children[1]->AddChild(children[0]);
+
+ // Add offsets so they do not cover each other.
+ children[3]->SetPosition(gfx::PointF(10.0f, 10.0f));
+ children[2]->SetPosition(gfx::PointF(20.0f, 20.0f));
+ children[1]->SetPosition(gfx::PointF(30.0f, 30.0f));
+ children[0]->SetPosition(gfx::PointF(10.0f, 10.0f));
+
+ gfx::Point expected_origins[] = {
+ gfx::Point(40.0f, 40.0f), gfx::Point(30.0f, 30.0f),
+ gfx::Point(20.0f, 20.0f), gfx::Point(10.0f, 10.0f),
+ gfx::Point(00.0f, 00.0f)};
+
+ viz::CompositorFrame frame = ProduceFrame();
+ ASSERT_EQ(frame.render_pass_list.size(), 1u);
+ auto& pass = frame.render_pass_list.back();
+ ASSERT_THAT(pass->quad_list,
+ ElementsAre(viz::IsSolidColorQuad(SkColors::kBlue),
+ viz::IsSolidColorQuad(SkColors::kGreen),
+ viz::IsSolidColorQuad(SkColors::kMagenta),
+ viz::IsSolidColorQuad(SkColors::kRed),
+ viz::IsSolidColorQuad(SkColors::kYellow),
+ viz::IsSolidColorQuad(SkColors::kGray)));
+
+ for (size_t i = 0; i < std::size(expected_origins); ++i) {
+ auto* quad = pass->quad_list.ElementAt(i);
+ EXPECT_EQ(quad->shared_quad_state->quad_to_target_transform.MapPoint(
+ gfx::Point()),
+ expected_origins[i]);
+ }
+}
+
+TEST_F(SlimLayerTreeCompositorFrameTest, AxisAlignedClip) {
+ auto root_layer = CreateSolidColorLayer(viewport_.size(), SkColors::kGray);
+ layer_tree_->SetRoot(root_layer);
+
+ auto clip_layer = Layer::Create();
+ clip_layer->SetBounds(gfx::Size(10, 20));
+ clip_layer->SetMasksToBounds(true);
+
+ auto draw_layer = CreateSolidColorLayer(gfx::Size(30, 30), SkColors::kRed);
+
+ root_layer->AddChild(clip_layer);
+ clip_layer->AddChild(draw_layer);
+
+ {
+ viz::CompositorFrame frame = ProduceFrame();
+ ASSERT_EQ(frame.render_pass_list.size(), 1u);
+ auto& pass = frame.render_pass_list.back();
+ ASSERT_THAT(pass->quad_list,
+ ElementsAre(viz::IsSolidColorQuad(SkColors::kRed),
+ viz::IsSolidColorQuad(SkColors::kGray)));
+
+ auto* quad = pass->quad_list.front();
+ ASSERT_TRUE(quad->shared_quad_state->clip_rect);
+ EXPECT_EQ(quad->shared_quad_state->clip_rect.value(), gfx::Rect(10, 20));
+ }
+
+ clip_layer->SetPosition(gfx::PointF(5, 5));
+ {
+ viz::CompositorFrame frame = ProduceFrame();
+ ASSERT_EQ(frame.render_pass_list.size(), 1u);
+ auto& pass = frame.render_pass_list.back();
+ ASSERT_THAT(pass->quad_list,
+ ElementsAre(viz::IsSolidColorQuad(SkColors::kRed),
+ viz::IsSolidColorQuad(SkColors::kGray)));
+
+ auto* quad = pass->quad_list.front();
+ ASSERT_TRUE(quad->shared_quad_state->clip_rect);
+ // Clip is in target space.
+ EXPECT_EQ(quad->shared_quad_state->clip_rect.value(),
+ gfx::Rect(5, 5, 10, 20));
+ }
+}
+
+} // namespace
+
+} // namespace cc::slim
diff --git a/cc/slim/solid_color_layer.cc b/cc/slim/solid_color_layer.cc
index dc4e6f5..ece3586 100644
--- a/cc/slim/solid_color_layer.cc
+++ b/cc/slim/solid_color_layer.cc
@@ -7,6 +7,7 @@
#include <utility>
#include "cc/layers/solid_color_layer.h"
+#include "cc/slim/features.h"
#include "components/viz/common/quads/compositor_render_pass.h"
#include "components/viz/common/quads/solid_color_draw_quad.h"
@@ -15,7 +16,9 @@
// static
scoped_refptr<SolidColorLayer> SolidColorLayer::Create() {
scoped_refptr<cc::SolidColorLayer> cc_layer;
- cc_layer = cc::SolidColorLayer::Create();
+ if (!features::IsSlimCompositorEnabled()) {
+ cc_layer = cc::SolidColorLayer::Create();
+ }
return base::AdoptRef(new SolidColorLayer(std::move(cc_layer)));
}
@@ -29,7 +32,24 @@
}
void SolidColorLayer::SetBackgroundColor(SkColor4f color) {
- cc_layer()->SetBackgroundColor(color);
+ if (cc_layer()) {
+ cc_layer()->SetBackgroundColor(color);
+ return;
+ }
+ SetContentsOpaque(color.isOpaque());
+ Layer::SetBackgroundColor(color);
+}
+
+void SolidColorLayer::AppendQuads(viz::CompositorRenderPass& render_pass,
+ const gfx::Transform& transform,
+ const gfx::Rect* clip) {
+ viz::SharedQuadState* quad_state =
+ CreateAndAppendSharedQuadState(render_pass, transform, clip);
+ viz::SolidColorDrawQuad* quad =
+ render_pass.CreateAndAppendDrawQuad<viz::SolidColorDrawQuad>();
+ quad->SetNew(quad_state, quad_state->quad_layer_rect,
+ quad_state->visible_quad_layer_rect, background_color(),
+ /*anti_aliasing_off=*/true);
}
} // namespace cc::slim
diff --git a/cc/slim/solid_color_layer.h b/cc/slim/solid_color_layer.h
index c3c91b5..abbc41d 100644
--- a/cc/slim/solid_color_layer.h
+++ b/cc/slim/solid_color_layer.h
@@ -26,6 +26,10 @@
explicit SolidColorLayer(scoped_refptr<cc::SolidColorLayer> cc_layer);
~SolidColorLayer() override;
+ void AppendQuads(viz::CompositorRenderPass& render_pass,
+ const gfx::Transform& transform,
+ const gfx::Rect* clip) override;
+
cc::SolidColorLayer* cc_layer() const;
};
diff --git a/cc/slim/test_frame_sink_impl.cc b/cc/slim/test_frame_sink_impl.cc
index 67608ef..e764043 100644
--- a/cc/slim/test_frame_sink_impl.cc
+++ b/cc/slim/test_frame_sink_impl.cc
@@ -6,8 +6,11 @@
#include <memory>
#include <utility>
+#include <vector>
#include "base/task/single_thread_task_runner.h"
+#include "build/build_config.h"
+#include "components/viz/common/quads/compositor_frame.h"
#include "components/viz/test/test_context_provider.h"
#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
#include "mojo/public/cpp/bindings/pending_associated_remote.h"
@@ -16,6 +19,41 @@
namespace cc::slim {
+class TestFrameSinkImpl::TestMojoCompositorFrameSink
+ : public viz::mojom::CompositorFrameSink {
+ public:
+ TestMojoCompositorFrameSink() = default;
+ void SetNeedsBeginFrame(bool needs_begin_frame) override {}
+ void SetWantsAnimateOnlyBeginFrames() override {}
+ void SubmitCompositorFrame(
+ const viz::LocalSurfaceId& local_surface_id,
+ viz::CompositorFrame frame,
+ absl::optional<::viz::HitTestRegionList> hit_test_region_list,
+ uint64_t submit_time) override {
+ last_frame_ = std::move(frame);
+ }
+ void SubmitCompositorFrameSync(
+ const viz::LocalSurfaceId& local_surface_id,
+ viz::CompositorFrame frame,
+ absl::optional<::viz::HitTestRegionList> hit_test_region_list,
+ uint64_t submit_time,
+ SubmitCompositorFrameSyncCallback callback) override {}
+ void DidNotProduceFrame(const viz::BeginFrameAck& ack) override {}
+ void DidAllocateSharedBitmap(base::ReadOnlySharedMemoryRegion region,
+ const gpu::Mailbox& id) override {}
+ void DidDeleteSharedBitmap(const gpu::Mailbox& id) override {}
+ void InitializeCompositorFrameSinkType(
+ viz::mojom::CompositorFrameSinkType type) override {}
+#if BUILDFLAG(IS_ANDROID)
+ void SetThreadIds(const std::vector<int32_t>& thread_ids) override {}
+#endif
+
+ viz::CompositorFrame TakeLastFrame() { return std::move(last_frame_); }
+
+ private:
+ viz::CompositorFrame last_frame_;
+};
+
// static
std::unique_ptr<TestFrameSinkImpl> TestFrameSinkImpl::Create() {
auto task_runner = base::SingleThreadTaskRunner::GetCurrentDefault();
@@ -44,13 +82,18 @@
std::move(client_receiver),
std::move(context_provider),
base::kInvalidThreadId),
- pending_sink_receiver_(std::move(sink_receiver)) {}
+ mojo_sink_(std::make_unique<TestMojoCompositorFrameSink>()) {}
TestFrameSinkImpl::~TestFrameSinkImpl() = default;
+viz::CompositorFrame TestFrameSinkImpl::TakeLastFrame() {
+ return mojo_sink_->TakeLastFrame();
+}
+
bool TestFrameSinkImpl::BindToClient(FrameSinkImplClient* client) {
DCHECK(!bind_to_client_called_);
client_ = client;
+ frame_sink_ = mojo_sink_.get();
bind_to_client_called_ = true;
return bind_to_client_result_;
}
diff --git a/cc/slim/test_frame_sink_impl.h b/cc/slim/test_frame_sink_impl.h
index a1a5b725..e74ac32 100644
--- a/cc/slim/test_frame_sink_impl.h
+++ b/cc/slim/test_frame_sink_impl.h
@@ -24,6 +24,7 @@
DCHECK(!bind_to_client_called_);
bind_to_client_result_ = result;
}
+ viz::CompositorFrame TakeLastFrame();
bool bind_to_client_called() const { return bind_to_client_called_; }
bool needs_begin_frames() const { return needs_begin_frames_; }
@@ -32,6 +33,7 @@
void SetNeedsBeginFrame(bool needs_begin_frame) override;
private:
+ class TestMojoCompositorFrameSink;
TestFrameSinkImpl(
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
mojo::PendingAssociatedRemote<viz::mojom::CompositorFrameSink>
@@ -42,8 +44,7 @@
mojo::PendingAssociatedReceiver<viz::mojom::CompositorFrameSink>
sink_receiver);
- mojo::PendingAssociatedReceiver<viz::mojom::CompositorFrameSink>
- pending_sink_receiver_;
+ std::unique_ptr<TestMojoCompositorFrameSink> mojo_sink_;
bool bind_to_client_called_ = false;
bool bind_to_client_result_ = true;