blob: 332d9e7fd257159734fd6ce6a0169ec2a97c1670 [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cc/layers/tile_based_layer_impl.h"
#include <memory>
#include <utility>
#include "base/check_deref.h"
#include "cc/debug/layer_tree_debug_state.h"
#include "cc/layers/append_quads_context.h"
#include "cc/layers/append_quads_data.h"
#include "cc/test/layer_test_common.h"
#include "cc/test/test_layer_tree_host_base.h"
#include "components/viz/common/quads/solid_color_draw_quad.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cc {
namespace {
class TestTileBasedLayerImpl : public TileBasedLayerImpl {
public:
TestTileBasedLayerImpl(LayerTreeImpl* tree_impl, int id)
: TileBasedLayerImpl(tree_impl, id) {}
private:
// TileBasedLayerImpl:
void AppendQuadsSpecialization(const AppendQuadsContext& context,
viz::CompositorRenderPass* render_pass,
AppendQuadsData* append_quads_data,
viz::SharedQuadState* shared_quad_state,
const Occlusion& scaled_occlusion,
const gfx::Vector2d& quad_offset) override {}
float GetMaximumContentsScaleForUseInAppendQuads() override { return 1.f; }
bool IsDirectlyCompositedImage() const override { return false; }
void AppendQuadsForResourcelessSoftwareDraw(
const AppendQuadsContext& context,
viz::CompositorRenderPass* render_pass,
AppendQuadsData* append_quads_data,
viz::SharedQuadState* shared_quad_state,
const Occlusion& scaled_occlusion) override {}
};
class TileBasedLayerImplTest : public TestLayerTreeHostBase {};
// Verifies that `is_backdrop_filter_mask()` returns false by default.
TEST_F(TileBasedLayerImplTest, IsBackdropFilterMask_DefaultsToFalse) {
auto layer = std::make_unique<TestTileBasedLayerImpl>(
host_impl()->active_tree(), /*id=*/1);
EXPECT_FALSE(layer->is_backdrop_filter_mask());
}
// Verifies that `is_backdrop_filter_mask()` reflects calls to
// `SetIsBackdropFilterMask()`.
TEST_F(TileBasedLayerImplTest, SetIsBackdropFilterMask_GetterReflectsSetter) {
auto layer = std::make_unique<TestTileBasedLayerImpl>(
host_impl()->active_tree(), /*id=*/1);
layer->SetIsBackdropFilterMask(true);
EXPECT_TRUE(layer->is_backdrop_filter_mask());
layer->SetIsBackdropFilterMask(false);
EXPECT_FALSE(layer->is_backdrop_filter_mask());
}
// Verifies that calling `SetIsBackdropFilterMask` with the same value multiple
// times doesn't change the value.
TEST_F(TileBasedLayerImplTest, SetIsBackdropFilterMask_RedundantCalls) {
auto layer = std::make_unique<TestTileBasedLayerImpl>(
host_impl()->active_tree(), /*id=*/1);
layer->SetIsBackdropFilterMask(true);
EXPECT_TRUE(layer->is_backdrop_filter_mask());
layer->SetIsBackdropFilterMask(true);
EXPECT_TRUE(layer->is_backdrop_filter_mask());
layer->SetIsBackdropFilterMask(false);
EXPECT_FALSE(layer->is_backdrop_filter_mask());
layer->SetIsBackdropFilterMask(false);
EXPECT_FALSE(layer->is_backdrop_filter_mask());
}
// Verifies that calling `SetIsBackdropFilterMask` with a new value marks the
// layer for push properties in the pending tree.
TEST_F(TileBasedLayerImplTest,
SetIsBackdropFilterMask_CallsSetNeedsPushProperties) {
// NOTE: LayerImpl::SetNeedsPushProperties() is a no-op in the active tree, so
// we need to put the layer in the pending tree here.
SetupPendingTree();
auto layer = std::make_unique<TestTileBasedLayerImpl>(
host_impl()->pending_tree(), /*id=*/42);
auto* raw_layer = layer.get();
host_impl()->pending_tree()->AddLayer(std::move(layer));
// Clear state before running the test.
host_impl()->pending_tree()->ClearLayersThatShouldPushProperties();
raw_layer->ResetChangeTracking();
raw_layer->SetIsBackdropFilterMask(true);
const auto& layers =
host_impl()->pending_tree()->LayersThatShouldPushProperties();
ASSERT_EQ(layers.size(), 1u);
EXPECT_NE(layers.find(raw_layer), layers.end());
}
// Verifies that calling `SetIsBackdropFilterMask` with the same value as it
// currently has does not mark the layer for push properties in the pending
// tree.
TEST_F(
TileBasedLayerImplTest,
SetIsBackdropFilterMask_DoesNotCallSetNeedsPushPropertiesIfValueUnchanged) {
// NOTE: LayerImpl::SetNeedsPushProperties() is a no-op in the active tree, so
// we need to put the layer in the pending tree here.
SetupPendingTree();
auto layer = std::make_unique<TestTileBasedLayerImpl>(
host_impl()->pending_tree(), /*id=*/42);
auto* raw_layer = layer.get();
host_impl()->pending_tree()->AddLayer(std::move(layer));
// Clear state before running the test.
host_impl()->pending_tree()->ClearLayersThatShouldPushProperties();
raw_layer->ResetChangeTracking();
raw_layer->SetIsBackdropFilterMask(false);
const auto& layers =
host_impl()->pending_tree()->LayersThatShouldPushProperties();
EXPECT_EQ(layers.size(), 0u);
}
// Tests that AppendQuads() does not append any quads for a layer serving as
// a backdrop filter mask.
TEST_F(TileBasedLayerImplTest,
AppendQuadsDoesNotAppendQuadsForBackdropFilterMask) {
constexpr gfx::Size kLayerBounds(1300, 1900);
constexpr gfx::Rect kLayerRect(kLayerBounds);
constexpr float kOpacity = 1.0;
auto layer = std::make_unique<TestTileBasedLayerImpl>(
host_impl()->active_tree(), /*id=*/42);
auto* raw_layer = layer.get();
host_impl()->active_tree()->AddLayer(std::move(layer));
raw_layer->SetIsBackdropFilterMask(true);
// For the production code to actually append a quad, the layer must have
// non-zero size and not be completely transparent; ensure that these
// preconditions are satisfied to avoid this test passing trivially.
raw_layer->SetBounds(kLayerBounds);
raw_layer->draw_properties().visible_layer_rect = kLayerRect;
raw_layer->draw_properties().opacity = kOpacity;
SetupRootProperties(host_impl()->active_tree()->root_layer());
auto render_pass = viz::CompositorRenderPass::Create();
AppendQuadsData data;
raw_layer->AppendQuads(AppendQuadsContext{DRAW_MODE_SOFTWARE, {}, false},
render_pass.get(), &data);
EXPECT_EQ(render_pass->quad_list.size(), 0u);
}
// Tests that AppendQuads() does not append any quads for a layer serving as
// a backdrop filter mask with a solid color set.
TEST_F(TileBasedLayerImplTest,
AppendQuadsDoesNotAppendQuadsForBackdropFilterMaskWithSolidColor) {
constexpr gfx::Size kLayerBounds(1300, 1900);
constexpr gfx::Rect kLayerRect(kLayerBounds);
constexpr SkColor4f kLayerColor = SkColors::kRed;
constexpr float kOpacity = 1.0;
auto layer = std::make_unique<TestTileBasedLayerImpl>(
host_impl()->active_tree(), /*id=*/42);
auto* raw_layer = layer.get();
host_impl()->active_tree()->AddLayer(std::move(layer));
raw_layer->SetIsBackdropFilterMask(true);
raw_layer->SetSolidColor(kLayerColor);
// For the production code to actually append a quad, the layer must have
// non-zero size and not be completely transparent; ensure that these
// preconditions are satisfied to avoid this test passing trivially.
raw_layer->SetBounds(kLayerBounds);
raw_layer->draw_properties().visible_layer_rect = kLayerRect;
raw_layer->draw_properties().opacity = kOpacity;
SetupRootProperties(host_impl()->active_tree()->root_layer());
auto render_pass = viz::CompositorRenderPass::Create();
AppendQuadsData data;
raw_layer->AppendQuads(AppendQuadsContext{DRAW_MODE_SOFTWARE, {}, false},
render_pass.get(), &data);
EXPECT_EQ(render_pass->quad_list.size(), 0u);
}
TEST_F(TileBasedLayerImplTest, SettingSolidColorResultsInSolidColorQuad) {
constexpr gfx::Size kLayerBounds(1300, 1900);
constexpr gfx::Rect kLayerRect(kLayerBounds);
constexpr SkColor4f kLayerColor = SkColors::kRed;
constexpr float kOpacity = 1.0;
auto layer = std::make_unique<TestTileBasedLayerImpl>(
host_impl()->active_tree(), /*id=*/42);
auto* raw_layer = layer.get();
host_impl()->active_tree()->AddLayer(std::move(layer));
raw_layer->SetSolidColor(kLayerColor);
// For the production code to actually append a quad, the layer must have
// non-zero size and not be completely transparent.
raw_layer->SetBounds(kLayerBounds);
raw_layer->draw_properties().visible_layer_rect = kLayerRect;
raw_layer->draw_properties().opacity = kOpacity;
SetupRootProperties(host_impl()->active_tree()->root_layer());
auto render_pass = viz::CompositorRenderPass::Create();
AppendQuadsData data;
raw_layer->AppendQuads(AppendQuadsContext{DRAW_MODE_SOFTWARE, {}, false},
render_pass.get(), &data);
EXPECT_EQ(render_pass->quad_list.size(), 1u);
EXPECT_EQ(render_pass->quad_list.front()->rect, kLayerRect);
EXPECT_EQ(render_pass->quad_list.front()->visible_rect, kLayerRect);
EXPECT_EQ(render_pass->quad_list.front()->shared_quad_state->opacity,
kOpacity);
EXPECT_EQ(render_pass->quad_list.front()->material,
viz::DrawQuad::Material::kSolidColor);
EXPECT_EQ(
viz::SolidColorDrawQuad::MaterialCast(render_pass->quad_list.front())
->color,
kLayerColor);
}
// Validates that TileBasedLayerImpl::AppendQuads() calls
// LayerImpl::AppendDebugBorderQuad() when debug borders are enabled.
TEST_F(TileBasedLayerImplTest, AppendQuadsAppendsDebugBorders) {
LayerTreeDebugState debug_state;
debug_state.show_debug_borders.set(DebugBorderType::LAYER);
host_impl()->SetDebugState(debug_state);
auto layer = std::make_unique<TestTileBasedLayerImpl>(
host_impl()->active_tree(), /*id=*/42);
auto* raw_layer = layer.get();
host_impl()->active_tree()->AddLayer(std::move(layer));
SetupRootProperties(host_impl()->active_tree()->root_layer());
auto render_pass = viz::CompositorRenderPass::Create();
AppendQuadsData data;
raw_layer->AppendQuads(AppendQuadsContext{DRAW_MODE_SOFTWARE, {}, false},
render_pass.get(), &data);
// AppendQuads() should have inserted a quad for the debug border.
EXPECT_EQ(render_pass->quad_list.size(), 1u);
EXPECT_EQ(render_pass->quad_list.front()->material,
viz::DrawQuad::Material::kDebugBorder);
}
TEST_F(TileBasedLayerImplTest,
AppendQuadsDoesNotSetClipRectWhenNotDirectlyCompositedImage) {
constexpr gfx::Size kLayerBounds(100, 200);
constexpr gfx::Rect kLayerRect(kLayerBounds);
auto layer = std::make_unique<TestTileBasedLayerImpl>(
host_impl()->active_tree(), /*id=*/1);
auto* raw_layer = layer.get();
host_impl()->active_tree()->AddLayer(std::move(layer));
raw_layer->SetBounds(kLayerBounds);
raw_layer->draw_properties().visible_layer_rect = kLayerRect;
SetupRootProperties(host_impl()->active_tree()->root_layer());
auto render_pass = viz::CompositorRenderPass::Create();
AppendQuadsData data;
raw_layer->AppendQuads(AppendQuadsContext{DRAW_MODE_SOFTWARE, {}, false},
render_pass.get(), &data);
ASSERT_EQ(render_pass->shared_quad_state_list.size(), 1u);
EXPECT_FALSE(render_pass->shared_quad_state_list.front()->clip_rect);
}
class DirectlyCompositedTileBasedLayerImpl : public TestTileBasedLayerImpl {
public:
DirectlyCompositedTileBasedLayerImpl(LayerTreeImpl* tree_impl, int id)
: TestTileBasedLayerImpl(tree_impl, id) {}
private:
bool IsDirectlyCompositedImage() const override { return true; }
};
TEST_F(TileBasedLayerImplTest,
AppendQuadsSetsClipRectForDirectlyCompositedImage) {
constexpr gfx::Size kLayerBounds(100, 200);
constexpr gfx::Rect kLayerRect(kLayerBounds);
auto layer = std::make_unique<DirectlyCompositedTileBasedLayerImpl>(
host_impl()->active_tree(), /*id=*/1);
auto* raw_layer = layer.get();
host_impl()->active_tree()->AddLayer(std::move(layer));
raw_layer->SetBounds(kLayerBounds);
raw_layer->draw_properties().visible_layer_rect = kLayerRect;
SetupRootProperties(host_impl()->active_tree()->root_layer());
auto render_pass = viz::CompositorRenderPass::Create();
AppendQuadsData data;
raw_layer->AppendQuads(AppendQuadsContext{DRAW_MODE_SOFTWARE, {}, false},
render_pass.get(), &data);
ASSERT_EQ(render_pass->shared_quad_state_list.size(), 1u);
EXPECT_EQ(render_pass->shared_quad_state_list.front()->clip_rect, kLayerRect);
}
class OcclusionTestTileBasedLayerImpl : public TestTileBasedLayerImpl {
public:
OcclusionTestTileBasedLayerImpl(LayerTreeImpl* tree_impl, int id)
: TestTileBasedLayerImpl(tree_impl, id) {}
const Occlusion& scaled_occlusion() const { return scaled_occlusion_; }
void set_max_contents_scale(float scale) { max_contents_scale_ = scale; }
private:
void AppendQuadsSpecialization(const AppendQuadsContext& context,
viz::CompositorRenderPass* render_pass,
AppendQuadsData* append_quads_data,
viz::SharedQuadState* shared_quad_state,
const Occlusion& scaled_occlusion,
const gfx::Vector2d& quad_offset) override {
scaled_occlusion_ = scaled_occlusion;
// Create a dummy quad to avoid tripping debug checks.
auto* quad =
render_pass->CreateAndAppendDrawQuad<viz::SolidColorDrawQuad>();
quad->SetNew(shared_quad_state, gfx::Rect(1, 1), gfx::Rect(1, 1),
SkColors::kTransparent, false);
}
float GetMaximumContentsScaleForUseInAppendQuads() override {
return max_contents_scale_;
}
Occlusion scaled_occlusion_;
float max_contents_scale_ = 1.f;
};
class ResourcelessSoftwareDrawTileBasedLayerImpl
: public TestTileBasedLayerImpl {
public:
ResourcelessSoftwareDrawTileBasedLayerImpl(LayerTreeImpl* tree_impl, int id)
: TestTileBasedLayerImpl(tree_impl, id) {}
bool append_quads_for_resourceless_software_draw_called() const {
return append_quads_for_resourceless_software_draw_called_;
}
private:
void AppendQuadsForResourcelessSoftwareDraw(
const AppendQuadsContext& context,
viz::CompositorRenderPass* render_pass,
AppendQuadsData* append_quads_data,
viz::SharedQuadState* shared_quad_state,
const Occlusion& scaled_occlusion) override {
append_quads_for_resourceless_software_draw_called_ = true;
// Create a dummy quad to avoid tripping debug checks.
auto* quad =
render_pass->CreateAndAppendDrawQuad<viz::SolidColorDrawQuad>();
quad->SetNew(shared_quad_state, gfx::Rect(1, 1), gfx::Rect(1, 1),
SkColors::kTransparent, false);
}
bool append_quads_for_resourceless_software_draw_called_ = false;
};
TEST_F(TileBasedLayerImplTest, AppendQuadsScalesOcclusion) {
const float scale = 2.0f;
const gfx::Rect layer_rect(0, 0, 10, 10);
const gfx::Rect occluded_rect_in_content_space(0, 0, 5, 10);
auto layer = std::make_unique<OcclusionTestTileBasedLayerImpl>(
host_impl()->active_tree(), /*id=*/1);
auto* raw_layer = layer.get();
host_impl()->active_tree()->AddLayer(std::move(layer));
raw_layer->set_max_contents_scale(scale);
raw_layer->SetBounds(layer_rect.size());
raw_layer->draw_properties().visible_layer_rect = layer_rect;
// Set a screen space transform to simulate the layer being scaled. This is
// crucial because scaled_occlusion is computed using this transform.
gfx::Transform screen_space_transform;
screen_space_transform.Scale(scale, scale);
raw_layer->draw_properties().screen_space_transform = screen_space_transform;
// Define an occlusion in the layer's content space. This will be scaled
// by AppendQuads to produce the scaled_occlusion.
raw_layer->draw_properties().occlusion_in_content_space = Occlusion(
gfx::Transform(), SimpleEnclosedRegion(occluded_rect_in_content_space),
SimpleEnclosedRegion());
SetupRootProperties(host_impl()->active_tree()->root_layer());
auto render_pass = viz::CompositorRenderPass::Create();
AppendQuadsData data;
raw_layer->AppendQuads(AppendQuadsContext{DRAW_MODE_SOFTWARE, {}, true},
render_pass.get(), &data);
// The expected occluded rectangle is the original content-space occluded
// rectangle scaled to the layer's target space (which is screen space in this
// test due to the screen space transform).
const gfx::Rect expected_occluded_rect =
gfx::ScaleToEnclosingRect(occluded_rect_in_content_space, scale);
auto enclosing_rect = gfx::ScaleToEnclosingRect(layer_rect, scale);
EXPECT_EQ(
raw_layer->scaled_occlusion().GetUnoccludedContentRect(enclosing_rect),
gfx::SubtractRects(enclosing_rect, expected_occluded_rect));
}
TEST_F(TileBasedLayerImplTest,
AppendQuadsInvokesAppendQuadsForResourcelessSoftwareDraw) {
constexpr gfx::Size kLayerBounds(100, 200);
constexpr gfx::Rect kLayerRect(kLayerBounds);
auto layer = std::make_unique<ResourcelessSoftwareDrawTileBasedLayerImpl>(
host_impl()->active_tree(), /*id=*/1);
auto* raw_layer = layer.get();
host_impl()->active_tree()->AddLayer(std::move(layer));
raw_layer->SetBounds(kLayerBounds);
raw_layer->draw_properties().visible_layer_rect = kLayerRect;
SetupRootProperties(host_impl()->active_tree()->root_layer());
auto render_pass = viz::CompositorRenderPass::Create();
AppendQuadsData data;
raw_layer->AppendQuads(
AppendQuadsContext{DRAW_MODE_RESOURCELESS_SOFTWARE, {}, false},
render_pass.get(), &data);
EXPECT_TRUE(raw_layer->append_quads_for_resourceless_software_draw_called());
}
TEST_F(
TileBasedLayerImplTest,
AppendQuadsDoesNotInvokeAppendQuadsForResourcelessSoftwareDrawWhenNotExpected) {
constexpr gfx::Size kLayerBounds(100, 200);
constexpr gfx::Rect kLayerRect(kLayerBounds);
auto layer = std::make_unique<ResourcelessSoftwareDrawTileBasedLayerImpl>(
host_impl()->active_tree(), /*id=*/1);
auto* raw_layer = layer.get();
host_impl()->active_tree()->AddLayer(std::move(layer));
raw_layer->SetBounds(kLayerBounds);
raw_layer->draw_properties().visible_layer_rect = kLayerRect;
SetupRootProperties(host_impl()->active_tree()->root_layer());
auto render_pass = viz::CompositorRenderPass::Create();
AppendQuadsData data;
raw_layer->AppendQuads(AppendQuadsContext{DRAW_MODE_SOFTWARE, {}, false},
render_pass.get(), &data);
EXPECT_FALSE(raw_layer->append_quads_for_resourceless_software_draw_called());
}
class QuadOffsetTestTileBasedLayerImpl : public TestTileBasedLayerImpl {
public:
QuadOffsetTestTileBasedLayerImpl(LayerTreeImpl* tree_impl, int id)
: TestTileBasedLayerImpl(tree_impl, id) {}
const gfx::Vector2d& quad_offset() const { return quad_offset_; }
private:
void AppendQuadsSpecialization(const AppendQuadsContext& context,
viz::CompositorRenderPass* render_pass,
AppendQuadsData* append_quads_data,
viz::SharedQuadState* shared_quad_state,
const Occlusion& scaled_occlusion,
const gfx::Vector2d& quad_offset) override {
quad_offset_ = quad_offset;
// Create a dummy quad to avoid tripping debug checks.
auto* quad =
render_pass->CreateAndAppendDrawQuad<viz::SolidColorDrawQuad>();
quad->SetNew(shared_quad_state, gfx::Rect(1, 1), gfx::Rect(1, 1),
SkColors::kTransparent, false);
}
gfx::Vector2d quad_offset_;
};
TEST_F(TileBasedLayerImplTest, AppendQuadsComputesQuadOffset) {
const gfx::Size layer_bounds(100, 100);
const gfx::Rect visible_layer_rect(10, 20, 50, 60);
auto layer = std::make_unique<QuadOffsetTestTileBasedLayerImpl>(
host_impl()->active_tree(), /*id=*/1);
auto* raw_layer = layer.get();
host_impl()->active_tree()->AddLayer(std::move(layer));
raw_layer->SetBounds(layer_bounds);
raw_layer->draw_properties().visible_layer_rect = visible_layer_rect;
SetupRootProperties(host_impl()->active_tree()->root_layer());
auto render_pass = viz::CompositorRenderPass::Create();
AppendQuadsData data;
raw_layer->AppendQuads(AppendQuadsContext{DRAW_MODE_SOFTWARE, {}, false},
render_pass.get(), &data);
// The quad_offset should be the additive inverse of the visible_layer_rect's
// origin.
const gfx::Vector2d expected_quad_offset(-visible_layer_rect.x(),
-visible_layer_rect.y());
EXPECT_EQ(raw_layer->quad_offset(), expected_quad_offset);
ASSERT_EQ(render_pass->shared_quad_state_list.size(), 1u);
const viz::SharedQuadState* shared_quad_state =
render_pass->shared_quad_state_list.front();
// The quad_to_target_transform should be translated by the additive inverse
// of the quad_offset, as the quads themselves are shifted by quad_offset.
gfx::Transform expected_transform =
raw_layer->draw_properties().target_space_transform;
expected_transform.Translate(-expected_quad_offset);
EXPECT_EQ(shared_quad_state->quad_to_target_transform, expected_transform);
// The quad_layer_rect should be offset by the quad_offset.
gfx::Rect expected_quad_layer_rect = gfx::Rect(layer_bounds);
expected_quad_layer_rect.Offset(expected_quad_offset);
EXPECT_EQ(shared_quad_state->quad_layer_rect, expected_quad_layer_rect);
// The visible_quad_layer_rect should be offset by the quad_offset.
gfx::Rect expected_visible_quad_layer_rect = visible_layer_rect;
expected_visible_quad_layer_rect.Offset(expected_quad_offset);
EXPECT_EQ(shared_quad_state->visible_quad_layer_rect,
expected_visible_quad_layer_rect);
}
class QuadOffsetOrderTestTileBasedLayerImpl : public TestTileBasedLayerImpl {
public:
QuadOffsetOrderTestTileBasedLayerImpl(LayerTreeImpl* tree_impl, int id)
: TestTileBasedLayerImpl(tree_impl, id) {}
const viz::SharedQuadState* shared_quad_state_at_specialization() const {
return shared_quad_state_at_specialization_.get();
}
private:
void AppendQuadsSpecialization(const AppendQuadsContext& context,
viz::CompositorRenderPass* render_pass,
AppendQuadsData* append_quads_data,
viz::SharedQuadState* shared_quad_state,
const Occlusion& scaled_occlusion,
const gfx::Vector2d& quad_offset) override {
shared_quad_state_at_specialization_ =
std::make_unique<viz::SharedQuadState>(*shared_quad_state);
// Create a dummy quad to avoid tripping debug checks.
auto* quad =
render_pass->CreateAndAppendDrawQuad<viz::SolidColorDrawQuad>();
quad->SetNew(shared_quad_state, gfx::Rect(1, 1), gfx::Rect(1, 1),
SkColors::kTransparent, false);
}
std::unique_ptr<viz::SharedQuadState> shared_quad_state_at_specialization_;
};
// Verifies that AppendQuads() updates the shared quad state for the computed
// quad offset only *after* invoking AppendQuadsSpecialization(). This is part
// of the method's contract and is necessary AppendQuadsSpecialization()
// implementations need to operate on the original values of the shared quad
// state (e.g., to find which tiles to draw).
TEST_F(
TileBasedLayerImplTest,
AppendQuadsUpdatesSharedQuadStateWithOffsetOnlyAfterCallingSpecialization) {
const gfx::Size layer_bounds(100, 100);
const gfx::Rect visible_layer_rect(10, 20, 50, 60);
auto layer = std::make_unique<QuadOffsetOrderTestTileBasedLayerImpl>(
host_impl()->active_tree(), /*id=*/1);
auto* raw_layer = layer.get();
host_impl()->active_tree()->AddLayer(std::move(layer));
raw_layer->SetBounds(layer_bounds);
raw_layer->draw_properties().visible_layer_rect = visible_layer_rect;
SetupRootProperties(host_impl()->active_tree()->root_layer());
auto render_pass = viz::CompositorRenderPass::Create();
AppendQuadsData data;
raw_layer->AppendQuads(AppendQuadsContext{DRAW_MODE_SOFTWARE, {}, false},
render_pass.get(), &data);
const viz::SharedQuadState* sqs_at_specialization =
raw_layer->shared_quad_state_at_specialization();
ASSERT_TRUE(sqs_at_specialization);
// The SharedQuadState should not have been adjusted by the quad offset at the
// time of being passed into AppendQuadsSpecialization().
EXPECT_EQ(sqs_at_specialization->quad_to_target_transform,
raw_layer->draw_properties().target_space_transform);
EXPECT_EQ(sqs_at_specialization->quad_layer_rect, gfx::Rect(layer_bounds));
EXPECT_EQ(sqs_at_specialization->visible_quad_layer_rect, visible_layer_rect);
// Now check the final SQS to ensure the offset was applied later.
ASSERT_EQ(render_pass->shared_quad_state_list.size(), 1u);
const viz::SharedQuadState* final_sqs =
render_pass->shared_quad_state_list.front();
const gfx::Vector2d expected_quad_offset(-visible_layer_rect.x(),
-visible_layer_rect.y());
gfx::Transform expected_transform =
raw_layer->draw_properties().target_space_transform;
expected_transform.Translate(-expected_quad_offset);
EXPECT_EQ(final_sqs->quad_to_target_transform, expected_transform);
gfx::Rect expected_quad_layer_rect = gfx::Rect(layer_bounds);
expected_quad_layer_rect.Offset(expected_quad_offset);
EXPECT_EQ(final_sqs->quad_layer_rect, expected_quad_layer_rect);
gfx::Rect expected_visible_quad_layer_rect = visible_layer_rect;
expected_visible_quad_layer_rect.Offset(expected_quad_offset);
EXPECT_EQ(final_sqs->visible_quad_layer_rect,
expected_visible_quad_layer_rect);
}
} // namespace
} // namespace cc