blob: f2f8853825fff7bda31f8487ac1804e2e4a6ae8c [file] [log] [blame] [edit]
// 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_display_layer_impl.h"
#include "base/check_deref.h"
#include "cc/layers/append_quads_context.h"
#include "cc/layers/append_quads_data.h"
#include "cc/test/test_layer_tree_host_base.h"
#include "components/viz/client/client_resource_provider.h"
#include "components/viz/common/quads/solid_color_draw_quad.h"
#include "components/viz/common/quads/tile_draw_quad.h"
#include "ui/gfx/geometry/rect.h"
namespace cc {
class TileDisplayLayerImplTest : public TestLayerTreeHostBase {};
TEST_F(TileDisplayLayerImplTest, NoQuadAppendedByDefault) {
TileDisplayLayerImpl layer(CHECK_DEREF(host_impl()->active_tree()),
/*id=*/42);
auto render_pass = viz::CompositorRenderPass::Create();
AppendQuadsData data;
layer.AppendQuads(AppendQuadsContext{DRAW_MODE_SOFTWARE, {}, false},
render_pass.get(), &data);
EXPECT_EQ(render_pass->quad_list.size(), 0u);
}
TEST_F(TileDisplayLayerImplTest, 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<TileDisplayLayerImpl>(
CHECK_DEREF(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->SetRecordedBounds(kLayerRect);
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);
}
// Tests that AppendQuads() does not append any quads for a layer serving as
// a backdrop filter mask.
TEST_F(TileDisplayLayerImplTest,
AppendQuadsDoesNotAppendQuadsForBackdropFilterMask) {
constexpr gfx::Size kLayerBounds(1300, 1900);
constexpr gfx::Rect kLayerRect(kLayerBounds);
constexpr float kOpacity = 1.0;
auto layer = std::make_unique<TileDisplayLayerImpl>(
CHECK_DEREF(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(TileDisplayLayerImplTest,
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<TileDisplayLayerImpl>(
CHECK_DEREF(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(TileDisplayLayerImplTest,
AppendQuadsDoesNotAppendQuadsForOccludedTiles) {
constexpr gfx::Size kLayerBounds(100, 100);
constexpr gfx::Rect kLayerRect(kLayerBounds);
constexpr float kOpacity = 1.0;
auto layer = std::make_unique<TileDisplayLayerImpl>(
CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
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;
raw_layer->draw_properties().opacity = kOpacity;
// Create a tiling with one tile.
auto& tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
tiling.SetTileSize(kLayerBounds);
tiling.SetTilingRect(kLayerRect);
auto resource_id = host_impl()->resource_provider()->ImportResource(
viz::TransferableResource::Make(
gpu::ClientSharedImage::CreateForTesting(),
viz::TransferableResource::ResourceSource::kTest, gpu::SyncToken()),
base::DoNothing());
TileDisplayLayerImpl::TileContents contents =
TileDisplayLayerImpl::TileResource(resource_id, kLayerBounds,
/*is_checkered=*/false);
tiling.SetTileContents(TileIndex{0, 0}, contents, /*update_damage=*/false);
// Set up occlusion that covers the entire layer. Occlusion is specified in
// screen space, so we provide an identity transform to make content space
// the same as screen space.
gfx::Transform identity_transform;
SimpleEnclosedRegion screen_occlusion(kLayerRect);
raw_layer->draw_properties().occlusion_in_content_space =
Occlusion(identity_transform, screen_occlusion, screen_occlusion);
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(TileDisplayLayerImplTest,
AppendQuadsAppendsClippedQuadsForPartiallyOccludedTiles) {
const gfx::Rect layer_rect(0, 0, 10, 10);
const gfx::Rect tile_rect(0, 0, 10, 10);
const gfx::Rect occluded_rect(0, 0, 5, 10);
// Setup layer and tiling.
auto layer = std::make_unique<TileDisplayLayerImpl>(
CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
auto* raw_layer = layer.get();
host_impl()->active_tree()->AddLayer(std::move(layer));
raw_layer->SetBounds(layer_rect.size());
raw_layer->SetRecordedBounds(layer_rect);
raw_layer->draw_properties().visible_layer_rect = layer_rect;
raw_layer->draw_properties().occlusion_in_content_space =
Occlusion(gfx::Transform(), SimpleEnclosedRegion(occluded_rect),
SimpleEnclosedRegion());
auto& tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
tiling.SetTileSize(tile_rect.size());
tiling.SetTilingRect(tile_rect);
auto resource_id = host_impl()->resource_provider()->ImportResource(
viz::TransferableResource::Make(
gpu::ClientSharedImage::CreateForTesting(),
viz::TransferableResource::ResourceSource::kTest, gpu::SyncToken()),
base::DoNothing());
TileDisplayLayerImpl::TileContents contents =
TileDisplayLayerImpl::TileResource(resource_id, tile_rect.size(),
/*is_checkered=*/false);
tiling.SetTileContents(TileIndex{0, 0}, contents, /*update_damage=*/false);
SetupRootProperties(host_impl()->active_tree()->root_layer());
// Append quads.
auto render_pass = viz::CompositorRenderPass::Create();
AppendQuadsData data;
raw_layer->AppendQuads(AppendQuadsContext{DRAW_MODE_SOFTWARE, {}, false},
render_pass.get(), &data);
// Verify that one quad is appended and it's clipped.
ASSERT_EQ(1u, render_pass->quad_list.size());
const viz::DrawQuad* quad = render_pass->quad_list.front();
EXPECT_EQ(viz::DrawQuad::Material::kTiledContent, quad->material);
const auto* tile_quad = viz::TileDrawQuad::MaterialCast(quad);
EXPECT_EQ(tile_rect, tile_quad->rect);
const gfx::Rect expected_visible_rect =
gfx::SubtractRects(tile_rect, occluded_rect);
EXPECT_EQ(expected_visible_rect, tile_quad->visible_rect);
}
TEST_F(TileDisplayLayerImplTest,
NonEmptyTilingWithResourceResultsInPictureQuad) {
constexpr gfx::Size kLayerBounds(1300, 1900);
constexpr gfx::Rect kLayerRect(kLayerBounds);
constexpr float kOpacity = 1.0;
auto layer = std::make_unique<TileDisplayLayerImpl>(
CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
auto* raw_layer = layer.get();
host_impl()->active_tree()->AddLayer(std::move(layer));
// 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->SetRecordedBounds(kLayerRect);
raw_layer->draw_properties().visible_layer_rect = kLayerRect;
raw_layer->draw_properties().opacity = kOpacity;
auto& tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
tiling.SetTileSize(kLayerBounds);
tiling.SetTilingRect(kLayerRect);
auto resource_id = host_impl()->resource_provider()->ImportResource(
viz::TransferableResource::Make(
gpu::ClientSharedImage::CreateForTesting(),
viz::TransferableResource::ResourceSource::kTest, gpu::SyncToken()),
base::DoNothing());
TileDisplayLayerImpl::TileContents contents =
TileDisplayLayerImpl::TileResource(resource_id, kLayerBounds,
/*is_checkered=*/false);
tiling.SetTileContents(TileIndex{0, 0}, contents, /*update_damage=*/true);
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()->resource_id, resource_id);
EXPECT_EQ(render_pass->quad_list.front()->material,
viz::DrawQuad::Material::kTiledContent);
EXPECT_EQ(viz::TileDrawQuad::MaterialCast(render_pass->quad_list.front())
->force_anti_aliasing_off,
false);
}
TEST_F(TileDisplayLayerImplTest,
NonEmptyTilingWithColorResultsInSolidColorQuad) {
constexpr gfx::Size kLayerBounds(1300, 1900);
constexpr gfx::Rect kLayerRect(kLayerBounds);
constexpr float kOpacity = 1.0;
constexpr SkColor4f kTileColor = SkColors::kRed;
auto layer = std::make_unique<TileDisplayLayerImpl>(
CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
auto* raw_layer = layer.get();
host_impl()->active_tree()->AddLayer(std::move(layer));
// 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->SetRecordedBounds(kLayerRect);
raw_layer->draw_properties().visible_layer_rect = kLayerRect;
raw_layer->draw_properties().opacity = kOpacity;
auto& tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
tiling.SetTileSize(kLayerBounds);
tiling.SetTilingRect(kLayerRect);
tiling.SetTileContents(TileIndex{0, 0}, kTileColor, /*update_damage=*/true);
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,
kTileColor);
EXPECT_EQ(
viz::SolidColorDrawQuad::MaterialCast(render_pass->quad_list.front())
->force_anti_aliasing_off,
false);
}
// Verifies that GetContentsResourceId() handles the error case of being called
// when the layer has no tiles, setting the resource ID to invalid in that case.
TEST_F(TileDisplayLayerImplTest, GetContentsResourceIdHandlesLackOfTiles) {
auto layer = std::make_unique<TileDisplayLayerImpl>(
CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
layer->SetIsBackdropFilterMask(true);
viz::ResourceId mask_resource_id = viz::ResourceId(42);
gfx::Size mask_texture_size;
gfx::SizeF mask_uv_size;
layer->GetContentsResourceId(&mask_resource_id, &mask_texture_size,
&mask_uv_size);
EXPECT_EQ(mask_resource_id, viz::kInvalidResourceId);
}
// Verifies that GetContentsResourceId() returns the correct resource ID for a
// backdrop filter mask.
TEST_F(TileDisplayLayerImplTest,
GetContentsResourceIdReturnsResourceForBackdropFilter) {
constexpr gfx::Size kLayerBounds(1300, 1900);
constexpr gfx::Rect kLayerRect(kLayerBounds);
auto layer = std::make_unique<TileDisplayLayerImpl>(
CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
auto* raw_layer = layer.get();
host_impl()->active_tree()->AddLayer(std::move(layer));
raw_layer->SetBounds(kLayerBounds);
raw_layer->SetIsBackdropFilterMask(true);
auto& tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
tiling.SetTileSize(kLayerBounds);
tiling.SetTilingRect(kLayerRect);
auto resource_id = host_impl()->resource_provider()->ImportResource(
viz::TransferableResource::Make(
gpu::ClientSharedImage::CreateForTesting(),
viz::TransferableResource::ResourceSource::kTest, gpu::SyncToken()),
base::DoNothing());
TileDisplayLayerImpl::TileContents contents =
TileDisplayLayerImpl::TileResource(resource_id, kLayerBounds,
/*is_checkered=*/false);
tiling.SetTileContents(TileIndex{0, 0}, contents, /*update_damage=*/true);
SetupRootProperties(host_impl()->active_tree()->root_layer());
viz::ResourceId mask_resource_id;
gfx::Size mask_texture_size;
gfx::SizeF mask_uv_size;
raw_layer->GetContentsResourceId(&mask_resource_id, &mask_texture_size,
&mask_uv_size);
EXPECT_EQ(mask_resource_id, resource_id);
EXPECT_EQ(mask_texture_size, kLayerBounds);
// `mask_uv_size` is the ratio between the tile's width/height and that of
// the resource. Here, the tile and resource have been created with the same
// size.
EXPECT_EQ(mask_uv_size, gfx::SizeF(1.0f, 1.0f));
}
// Verifies that GetContentsResourceId() returns the correct mask UV size when
// the tile and resource sizes differ.
TEST_F(TileDisplayLayerImplTest,
GetContentsResourceIdComputesUVMaskSizeCorrectlyForBackdropFilter) {
constexpr gfx::Size kLayerBounds(100, 200);
constexpr gfx::Size kResourceSize(200, 400);
constexpr gfx::Rect kLayerRect(kLayerBounds);
auto layer = std::make_unique<TileDisplayLayerImpl>(
CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
auto* raw_layer = layer.get();
host_impl()->active_tree()->AddLayer(std::move(layer));
raw_layer->SetBounds(kLayerBounds);
raw_layer->SetIsBackdropFilterMask(true);
auto& tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
tiling.SetTileSize(kLayerBounds);
tiling.SetTilingRect(kLayerRect);
auto resource_id = host_impl()->resource_provider()->ImportResource(
viz::TransferableResource::Make(
gpu::ClientSharedImage::CreateForTesting(),
viz::TransferableResource::ResourceSource::kTest, gpu::SyncToken()),
base::DoNothing());
TileDisplayLayerImpl::TileContents contents =
TileDisplayLayerImpl::TileResource(resource_id, kResourceSize,
/*is_checkered=*/false);
tiling.SetTileContents(TileIndex{0, 0}, contents, /*update_damage=*/true);
SetupRootProperties(host_impl()->active_tree()->root_layer());
viz::ResourceId mask_resource_id;
gfx::Size mask_texture_size;
gfx::SizeF mask_uv_size;
raw_layer->GetContentsResourceId(&mask_resource_id, &mask_texture_size,
&mask_uv_size);
EXPECT_EQ(mask_resource_id, resource_id);
EXPECT_EQ(mask_texture_size, kResourceSize);
// `mask_uv_size` is the ratio between the tile's width/height and that of
// the resource. Here, the tile has been created to be half the size of the
// resource in each dimension.
EXPECT_EQ(mask_uv_size, gfx::SizeF(0.5f, 0.5f));
}
// Tests that GetContentsResourceId() returns viz::kInvalidResourceId if the
// layer has more than one tiling, as masks are only supported if they fit on a
// single tile.
TEST_F(TileDisplayLayerImplTest,
GetContentsResourceIdReturnsInvalidIdForMultipleTilings) {
auto layer = std::make_unique<TileDisplayLayerImpl>(
CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
auto* raw_layer = layer.get();
host_impl()->active_tree()->AddLayer(std::move(layer));
raw_layer->SetIsBackdropFilterMask(true);
// Create two tilings.
raw_layer->GetOrCreateTilingFromScaleKey(1.0);
raw_layer->GetOrCreateTilingFromScaleKey(2.0);
viz::ResourceId mask_resource_id;
gfx::Size mask_texture_size;
gfx::SizeF mask_uv_size;
raw_layer->GetContentsResourceId(&mask_resource_id, &mask_texture_size,
&mask_uv_size);
EXPECT_EQ(mask_resource_id, viz::kInvalidResourceId);
}
class TileDisplayLayerImplWithEdgeAADisabledTest
: public TileDisplayLayerImplTest {
public:
LayerTreeSettings CreateSettings() override {
auto settings = TileDisplayLayerImplTest::CreateSettings();
settings.enable_edge_anti_aliasing = false;
return settings;
}
};
TEST_F(TileDisplayLayerImplWithEdgeAADisabledTest,
EnableEdgeAntiAliasingIsHonoredForPictureQuads) {
constexpr gfx::Size kLayerBounds(1300, 1900);
constexpr gfx::Rect kLayerRect(kLayerBounds);
constexpr float kOpacity = 1.0;
auto layer = std::make_unique<TileDisplayLayerImpl>(
CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
auto* raw_layer = layer.get();
host_impl()->active_tree()->AddLayer(std::move(layer));
raw_layer->SetBounds(kLayerBounds);
raw_layer->SetRecordedBounds(kLayerRect);
raw_layer->draw_properties().visible_layer_rect = kLayerRect;
raw_layer->draw_properties().opacity = kOpacity;
auto& tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
tiling.SetTileSize(kLayerBounds);
tiling.SetTilingRect(kLayerRect);
auto resource_id = host_impl()->resource_provider()->ImportResource(
viz::TransferableResource::Make(
gpu::ClientSharedImage::CreateForTesting(),
viz::TransferableResource::ResourceSource::kTest, gpu::SyncToken()),
base::DoNothing());
TileDisplayLayerImpl::TileContents contents =
TileDisplayLayerImpl::TileResource(resource_id, kLayerBounds,
/*is_checkered=*/false);
tiling.SetTileContents(TileIndex{0, 0}, contents, /*update_damage=*/true);
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(viz::TileDrawQuad::MaterialCast(render_pass->quad_list.front())
->force_anti_aliasing_off,
true);
}
TEST_F(TileDisplayLayerImplWithEdgeAADisabledTest,
EnableEdgeAntiAliasingIsHonoredForSolidColorQuads) {
constexpr gfx::Size kLayerBounds(1300, 1900);
constexpr gfx::Rect kLayerRect(kLayerBounds);
constexpr float kOpacity = 1.0;
constexpr SkColor4f kTileColor = SkColors::kRed;
auto layer = std::make_unique<TileDisplayLayerImpl>(
CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
auto* raw_layer = layer.get();
host_impl()->active_tree()->AddLayer(std::move(layer));
raw_layer->SetBounds(kLayerBounds);
raw_layer->SetRecordedBounds(kLayerRect);
raw_layer->draw_properties().visible_layer_rect = kLayerRect;
raw_layer->draw_properties().opacity = kOpacity;
auto& tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
tiling.SetTileSize(kLayerBounds);
tiling.SetTilingRect(kLayerRect);
tiling.SetTileContents(TileIndex{0, 0}, kTileColor, /*update_damage=*/true);
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(
viz::SolidColorDrawQuad::MaterialCast(render_pass->quad_list.front())
->force_anti_aliasing_off,
true);
}
TEST_F(TileDisplayLayerImplTest, MissingTileResultsInCheckerBoardQuad) {
constexpr gfx::Size kLayerBounds(1300, 1900);
constexpr gfx::Rect kLayerRect(kLayerBounds);
constexpr float kOpacity = 1.0;
auto layer = std::make_unique<TileDisplayLayerImpl>(
CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
auto* raw_layer = layer.get();
host_impl()->active_tree()->AddLayer(std::move(layer));
// 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->SetRecordedBounds(kLayerRect);
raw_layer->draw_properties().visible_layer_rect = kLayerRect;
raw_layer->draw_properties().opacity = kOpacity;
// Add a tiling, but don't give it any tile contents.
auto& tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
tiling.SetTileSize(kLayerBounds);
tiling.SetTilingRect(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);
// Verify that the layer appended a checkerboard quad for the missing tile.
// Checkerboard quads are solid-color quads whose color is the safe background
// opaque color.
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,
raw_layer->safe_opaque_background_color());
}
// Verifies that the layer appends quads from the highest-resolution tiling
// when multiple tilings are available.
TEST_F(TileDisplayLayerImplTest, AppendsQuadsFromHighestResolutionTilingByDefault) {
constexpr gfx::Size kLayerBounds(1300, 1900);
constexpr gfx::Rect kLayerRect(kLayerBounds);
constexpr float kOpacity = 1.0;
auto layer = std::make_unique<TileDisplayLayerImpl>(
CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
auto* raw_layer = layer.get();
host_impl()->active_tree()->AddLayer(std::move(layer));
raw_layer->SetBounds(kLayerBounds);
raw_layer->SetRecordedBounds(kLayerRect);
raw_layer->draw_properties().visible_layer_rect = kLayerRect;
raw_layer->draw_properties().opacity = kOpacity;
// Create two tilings with different scales.
auto& low_res_tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
low_res_tiling.SetTileSize(kLayerBounds);
low_res_tiling.SetTilingRect(kLayerRect);
auto& high_res_tiling = raw_layer->GetOrCreateTilingFromScaleKey(2.0);
high_res_tiling.SetTileSize(kLayerBounds);
high_res_tiling.SetTilingRect(kLayerRect);
// Set content for the high-res tiling only.
auto resource_id = host_impl()->resource_provider()->ImportResource(
viz::TransferableResource::Make(
gpu::ClientSharedImage::CreateForTesting(),
viz::TransferableResource::ResourceSource::kTest, gpu::SyncToken()),
base::DoNothing());
TileDisplayLayerImpl::TileContents contents =
TileDisplayLayerImpl::TileResource(resource_id, kLayerBounds,
/*is_checkered=*/false);
high_res_tiling.SetTileContents(TileIndex{0, 0}, contents,
/*update_damage=*/true);
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);
// Verify that the quad is from the high-res tiling.
EXPECT_EQ(render_pass->quad_list.size(), 1u);
EXPECT_EQ(render_pass->quad_list.front()->resource_id, resource_id);
}
// Verifies that the layer can be forced to append quads from a
// lower-resolution tiling if the ideal contents scale matches that tiling.
TEST_F(TileDisplayLayerImplTest, AppendsQuadsFromIdealResolutionTiling) {
constexpr gfx::Size kLayerBounds(1300, 1900);
constexpr gfx::Rect kLayerRect(kLayerBounds);
constexpr float kOpacity = 1.0;
auto layer = std::make_unique<TileDisplayLayerImpl>(
CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
auto* raw_layer = layer.get();
host_impl()->active_tree()->AddLayer(std::move(layer));
raw_layer->SetBounds(kLayerBounds);
raw_layer->SetRecordedBounds(kLayerRect);
raw_layer->draw_properties().visible_layer_rect = kLayerRect;
raw_layer->draw_properties().opacity = kOpacity;
// Create two tilings with different scales.
auto& low_res_tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
low_res_tiling.SetTileSize(kLayerBounds);
low_res_tiling.SetTilingRect(kLayerRect);
auto& high_res_tiling = raw_layer->GetOrCreateTilingFromScaleKey(2.0);
high_res_tiling.SetTileSize(kLayerBounds);
high_res_tiling.SetTilingRect(kLayerRect);
// Set content for the low-resolution tiling only.
auto low_res_resource_id = host_impl()->resource_provider()->ImportResource(
viz::TransferableResource::Make(
gpu::ClientSharedImage::CreateForTesting(),
viz::TransferableResource::ResourceSource::kTest, gpu::SyncToken()),
base::DoNothing());
TileDisplayLayerImpl::TileContents low_res_contents =
TileDisplayLayerImpl::TileResource(low_res_resource_id, kLayerBounds,
/*is_checkered=*/false);
low_res_tiling.SetTileContents(TileIndex{0, 0}, low_res_contents,
/*update_damage=*/true);
// With an identity transform, the ideal contents scale is 1.0, so the
// low-resolution tiling should be chosen.
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);
// Verify that the quad is from the low-res tiling.
EXPECT_EQ(render_pass->quad_list.size(), 1u);
EXPECT_EQ(render_pass->quad_list.front()->resource_id, low_res_resource_id);
}
// Verifies that RemoveTiling correctly removes a tiling.
TEST_F(TileDisplayLayerImplTest, RemoveTilingRemovesTiling) {
auto layer = std::make_unique<TileDisplayLayerImpl>(
CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
auto* raw_layer = layer.get();
host_impl()->active_tree()->AddLayer(std::move(layer));
// Add a tiling.
raw_layer->GetOrCreateTilingFromScaleKey(1.0);
ASSERT_NE(raw_layer->GetTilingForTesting(1.0), nullptr);
// Remove the tiling.
raw_layer->RemoveTiling(1.0);
EXPECT_EQ(raw_layer->GetTilingForTesting(1.0), nullptr);
}
// Verifies that removing one of multiple tilings leaves the others intact.
TEST_F(TileDisplayLayerImplTest, RemoveOneOfMultipleTilings) {
auto layer = std::make_unique<TileDisplayLayerImpl>(
CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
auto* raw_layer = layer.get();
host_impl()->active_tree()->AddLayer(std::move(layer));
// Add two tilings.
raw_layer->GetOrCreateTilingFromScaleKey(1.0);
raw_layer->GetOrCreateTilingFromScaleKey(2.0);
ASSERT_NE(raw_layer->GetTilingForTesting(1.0), nullptr);
ASSERT_NE(raw_layer->GetTilingForTesting(2.0), nullptr);
// Remove one tiling and verify that that tiling and only that tiling was
// removed.
raw_layer->RemoveTiling(1.0);
EXPECT_EQ(raw_layer->GetTilingForTesting(1.0), nullptr);
EXPECT_NE(raw_layer->GetTilingForTesting(2.0), nullptr);
}
// Verifies that calling RemoveTiling() for a tiling that doesn't exist doesn't
// crash.
TEST_F(TileDisplayLayerImplTest, RemoveTilingOnNonExistentTilingDoesNotCrash) {
auto layer = std::make_unique<TileDisplayLayerImpl>(
CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
auto* raw_layer = layer.get();
host_impl()->active_tree()->AddLayer(std::move(layer));
ASSERT_EQ(raw_layer->GetTilingForTesting(1.0), nullptr);
// This should not crash.
raw_layer->RemoveTiling(1.0);
EXPECT_EQ(raw_layer->GetTilingForTesting(1.0), nullptr);
}
// Verifies that setting tile contents with `update_damage=true` records the
// correct damage rect on the layer.
TEST_F(TileDisplayLayerImplTest,
SetTileContentsRecordsDamageWhenUpdateDamageIsTrue) {
// Configure the layer to have 5 tiles to be able to test damage from
// individual tile updates.
constexpr gfx::Size kLayerBounds(100, 100);
constexpr gfx::Rect kLayerRect(kLayerBounds);
constexpr gfx::Size kTileSize(20, 20);
const TileIndex kTileIndex1{1, 2};
const TileIndex kTileIndex2{3, 0};
auto layer = std::make_unique<TileDisplayLayerImpl>(
CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
auto* raw_layer = layer.get();
host_impl()->active_tree()->AddLayer(std::move(layer));
raw_layer->SetBounds(kLayerBounds);
auto& tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
tiling.SetTileSize(kTileSize);
tiling.SetTilingRect(kLayerRect);
// When SetTileContents is called with update_damage=true, it calculates the
// area that needs to be redrawn (the damage). This calculation happens in the
// tile's coordinate system first. However, the final damage must be recorded
// on the layer in the layer's coordinate system. TileDisplayLayerImpl uses
// the inverse of the raster transform to map the tile's damage rectangle
// back into the layer's coordinate space. Explicitly initialize the raster
// transform to be the identity transform (it is not explicitly initialized by
// default).
tiling.SetRasterTransform(gfx::AxisTransform2d());
// Set content for a tile and check that the damage rect is updated.
tiling.SetTileContents(kTileIndex1, SkColors::kRed,
/*update_damage=*/true);
EXPECT_EQ(
raw_layer->GetDamageRect(),
tiling.tiling_data()->TileBoundsWithBorder(kTileIndex1.i, kTileIndex1.j));
// Set content for another tile and check that the damage rect is expanded.
tiling.SetTileContents(kTileIndex2, SkColors::kBlue,
/*update_damage=*/true);
gfx::Rect expected_damage_rect;
expected_damage_rect.Union(
tiling.tiling_data()->TileBoundsWithBorder(kTileIndex1.i, kTileIndex1.j));
expected_damage_rect.Union(
tiling.tiling_data()->TileBoundsWithBorder(kTileIndex2.i, kTileIndex2.j));
EXPECT_EQ(raw_layer->GetDamageRect(), expected_damage_rect);
// Reset change tracking and check that the damage rect is cleared.
raw_layer->ResetChangeTracking();
EXPECT_TRUE(raw_layer->GetDamageRect().IsEmpty());
}
// Verifies that setting tile contents with `update_damage=false` does not
// record damage on the layer.
TEST_F(TileDisplayLayerImplTest,
SetTileContentsDoesntRecordDamageWhenUpdateDamageIsFalse) {
// Configure the layer to have 5 tiles to be able to test damage from
// individual tile updates.
constexpr gfx::Size kLayerBounds(100, 100);
constexpr gfx::Rect kLayerRect(kLayerBounds);
constexpr gfx::Size kTileSize(20, 20);
const TileIndex kTileIndex{1, 2};
auto layer = std::make_unique<TileDisplayLayerImpl>(
CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
auto* raw_layer = layer.get();
host_impl()->active_tree()->AddLayer(std::move(layer));
raw_layer->SetBounds(kLayerBounds);
auto& tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
tiling.SetTileSize(kTileSize);
tiling.SetTilingRect(kLayerRect);
tiling.SetTileContents(kTileIndex, SkColors::kRed,
/*update_damage=*/false);
EXPECT_TRUE(raw_layer->GetDamageRect().IsEmpty());
}
// Verifies that when Tiling::SetTileContents is called with NoContents and the
// reason is MissingTileReason::kTileDeleted, the corresponding tile is removed
// from the tiling.
TEST_F(TileDisplayLayerImplTest,
SetTileContentsWithNoContentsAndTileDeletedReasonRemovesTile) {
auto layer = std::make_unique<TileDisplayLayerImpl>(
CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
auto* raw_layer = layer.get();
host_impl()->active_tree()->AddLayer(std::move(layer));
auto& tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
const TileIndex kTileIndex{0, 0};
// Add a tile.
tiling.SetTileContents(kTileIndex, SkColors::kRed,
/*update_damage=*/false);
ASSERT_NE(tiling.TileAt(kTileIndex), nullptr);
// Set the tile's contents to NoContents with kTileDeleted as the reason and
// verify that the tile is deleted.
tiling.SetTileContents(
kTileIndex,
TileDisplayLayerImpl::NoContents{mojom::MissingTileReason::kTileDeleted},
/*update_damage=*/false);
EXPECT_EQ(tiling.TileAt(kTileIndex), nullptr);
}
// Verifies that when Tiling::SetTileContents is called with NoContents and a
// reason other than MissingTileReason::kTileDeleted, the tile's contents are
// updated to NoContents.
TEST_F(TileDisplayLayerImplTest,
SetTileContentsWithNoContentsAndOtherReasonUpdatesTile) {
auto layer = std::make_unique<TileDisplayLayerImpl>(
CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
auto* raw_layer = layer.get();
host_impl()->active_tree()->AddLayer(std::move(layer));
auto& tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
const TileIndex kTileIndex{0, 0};
// Add a tile.
tiling.SetTileContents(kTileIndex, SkColors::kRed,
/*update_damage=*/false);
ASSERT_NE(tiling.TileAt(kTileIndex), nullptr);
// Set the tile's contents to NoContents with a reason other than
// kTileDeleted.
tiling.SetTileContents(kTileIndex,
TileDisplayLayerImpl::NoContents{
mojom::MissingTileReason::kResourceNotReady},
/*update_damage=*/false);
// Verify that the tile still exists and its contents are NoContents.
auto* tile = tiling.TileAt(kTileIndex);
EXPECT_NE(tile, nullptr);
EXPECT_TRUE(std::holds_alternative<TileDisplayLayerImpl::NoContents>(
tile->contents()));
}
} // namespace cc