blob: ac6d45c75a6cb13c1e5d373599b509ab2e0db373 [file]
// 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 <algorithm>
#include "base/check_deref.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "cc/debug/debug_colors.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/debug_border_draw_quad.h"
#include "components/viz/common/quads/solid_color_draw_quad.h"
#include "components/viz/common/quads/tile_draw_quad.h"
#include "gpu/command_buffer/client/client_shared_image.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "ui/gfx/geometry/rect.h"
using testing::ElementsAre;
namespace cc {
class TileDisplayLayerImplTest : public TestLayerTreeHostBase {};
TEST_F(TileDisplayLayerImplTest, NoQuadAppendedByDefault) {
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));
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 =
TileDisplayLayerTileResource(resource_id, kLayerBounds);
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 =
TileDisplayLayerTileResource(resource_id, tile_rect.size());
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 =
TileDisplayLayerTileResource(resource_id, kLayerBounds);
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 =
TileDisplayLayerTileResource(resource_id, kLayerBounds);
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 =
TileDisplayLayerTileResource(resource_id, kResourceSize);
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 =
TileDisplayLayerTileResource(resource_id, kLayerBounds);
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());
}
TEST_F(TileDisplayLayerImplTest, OomTileResultsInSolidColorQuad) {
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 with an OOM tile.
auto& tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
tiling.SetTileSize(kLayerBounds);
tiling.SetTilingRect(kLayerRect);
tiling.SetTileContents(
TileIndex{0, 0},
TileDisplayLayerNoContents{mojom::MissingTileReason::kOutOfMemory},
/*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 layer appended a solid color quad for the OOM tile.
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 =
TileDisplayLayerTileResource(resource_id, kLayerBounds);
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 =
TileDisplayLayerTileResource(low_res_resource_id, kLayerBounds);
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,
TileDisplayLayerNoContents{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,
TileDisplayLayerNoContents{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<TileDisplayLayerNoContents>(tile->contents()));
}
// Verifies that last_append_quads_scales_ is correctly updated after
// AppendQuads.
TEST_F(TileDisplayLayerImplTest, LastAppendQuadsScalesUpdated) {
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->SetRecordedBounds(kLayerRect);
raw_layer->draw_properties().visible_layer_rect = kLayerRect;
raw_layer->draw_properties().opacity = 1.0;
// 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 both tilings.
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);
low_res_tiling.SetTileContents(TileIndex{0, 0}, low_res_contents,
/*update_damage=*/true);
auto high_res_resource_id = host_impl()->resource_provider()->ImportResource(
viz::TransferableResource::Make(
gpu::ClientSharedImage::CreateForTesting(),
viz::TransferableResource::ResourceSource::kTest, gpu::SyncToken()),
base::DoNothing());
TileDisplayLayerImpl::TileContents high_res_contents =
TileDisplayLayerTileResource(high_res_resource_id, kLayerBounds);
high_res_tiling.SetTileContents(TileIndex{0, 0}, high_res_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);
// AppendQuads should use the ideal resolution tiling (1.0), so
// last_append_quads_scales_ should contain 1.0.
const auto& last_append_scales =
raw_layer->GetLastAppendQuadsScalesForTesting();
ASSERT_EQ(last_append_scales.size(), 1u);
EXPECT_EQ(last_append_scales[0], 1.0f);
}
// Verifies that GetSafeToDeleteTilings returns the correct set of scales for
// tilings that are safe to delete.
TEST_F(TileDisplayLayerImplTest, GetSafeToDeleteTilingsBasic) {
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));
// Case 1: Basic scenario - some tilings used, some not.
raw_layer->GetLastAppendQuadsScalesForTesting() = {2.0f};
raw_layer->SetProposedTilingScalesForDeletion({1.0f, 2.0f, 3.0f});
std::vector<float> safe_to_delete = raw_layer->GetSafeToDeleteTilings();
EXPECT_THAT(safe_to_delete, ElementsAre(1.0f, 3.0f));
// Case 2: No tilings were used in the last frame.
raw_layer->GetLastAppendQuadsScalesForTesting().clear();
raw_layer->SetProposedTilingScalesForDeletion({1.0f, 2.0f, 3.0f});
safe_to_delete = raw_layer->GetSafeToDeleteTilings();
EXPECT_THAT(safe_to_delete, ElementsAre(1.0f, 2.0f, 3.0f));
// Case 3: All proposed tilings were used in the last frame.
raw_layer->GetLastAppendQuadsScalesForTesting() = {1.0f, 2.0f, 3.0f};
raw_layer->SetProposedTilingScalesForDeletion({1.0f, 2.0f, 3.0f});
safe_to_delete = raw_layer->GetSafeToDeleteTilings();
EXPECT_TRUE(safe_to_delete.empty());
// Case 4: Proposed tilings include some that don't exist in
// last_append_quads_scales_.
raw_layer->GetLastAppendQuadsScalesForTesting() = {2.0f};
// 4.0 is not in last_append_quads_scales_
raw_layer->SetProposedTilingScalesForDeletion({1.0f, 4.0f});
safe_to_delete = raw_layer->GetSafeToDeleteTilings();
EXPECT_THAT(safe_to_delete, ElementsAre(1.0f, 4.0f));
}
// Verifies that CleanUpTilings returns the correct set of scales for tilings
// that are safe to delete. This is similar to above but more of an integration
// style test since we are using ::AppendQuad() here and not directly using
// GetLastAppendQuadsScalesForTesting().
TEST_F(TileDisplayLayerImplTest, GetSafeToDeleteTilingsIntegration) {
constexpr gfx::Size kLayerBounds(100, 100);
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->SetRecordedBounds(kLayerRect);
raw_layer->draw_properties().visible_layer_rect = kLayerRect;
raw_layer->draw_properties().opacity = 1.0;
// Create three tilings.
auto& tiling_1 = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
tiling_1.SetTileSize(kLayerBounds);
tiling_1.SetTilingRect(kLayerRect);
tiling_1.SetTileContents(TileIndex{0, 0}, SkColors::kRed, true);
auto& tiling_2 = raw_layer->GetOrCreateTilingFromScaleKey(2.0);
tiling_2.SetTileSize(kLayerBounds);
tiling_2.SetTilingRect(kLayerRect);
tiling_2.SetTileContents(TileIndex{0, 0}, SkColors::kGreen, true);
auto& tiling_3 = raw_layer->GetOrCreateTilingFromScaleKey(3.0);
tiling_3.SetTileSize(kLayerBounds);
tiling_3.SetTilingRect(kLayerRect);
tiling_3.SetTileContents(TileIndex{0, 0}, SkColors::kBlue, 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);
// By default, the ideal resolution tiling (1.0) is used.
raw_layer->SetProposedTilingScalesForDeletion({1.0, 2.0});
std::vector<float> safe_to_delete = raw_layer->GetSafeToDeleteTilings();
EXPECT_THAT(safe_to_delete, ElementsAre(2.0f));
raw_layer->SetProposedTilingScalesForDeletion({1.0, 3.0});
safe_to_delete = raw_layer->GetSafeToDeleteTilings();
EXPECT_THAT(safe_to_delete, ElementsAre(3.0f));
raw_layer->SetProposedTilingScalesForDeletion({3.0});
safe_to_delete = raw_layer->GetSafeToDeleteTilings();
EXPECT_THAT(safe_to_delete, ElementsAre(3.0f));
raw_layer->SetProposedTilingScalesForDeletion({1.0, 2.0, 3.0});
safe_to_delete = raw_layer->GetSafeToDeleteTilings();
EXPECT_THAT(safe_to_delete, ElementsAre(2.0f, 3.0f));
}
// Verifies that AppendQuads() appends debug borders for a solid-color tile when
// they are enabled.
TEST_F(TileDisplayLayerImplTest,
AppendQuadsAppendsDebugBordersForSolidColorTile) {
// Enable debug borders.
LayerTreeDebugState debug_state;
debug_state.show_debug_borders.set(DebugBorderType::LAYER);
host_impl()->SetDebugState(debug_state);
// Set up the layer.
constexpr gfx::Size kLayerBounds(100, 100);
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->SetRecordedBounds(kLayerRect);
raw_layer->draw_properties().visible_layer_rect = kLayerRect;
raw_layer->draw_properties().opacity = 1.0f;
// Add a tiling with a solid-color tile.
auto& tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
tiling.SetTileSize(kLayerBounds);
tiling.SetTilingRect(kLayerRect);
tiling.SetTileContents(TileIndex{0, 0}, SkColors::kRed,
/*update_damage=*/true);
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 a layer debug border, a tile debug border, and a content quad
// were appended.
ASSERT_EQ(render_pass->quad_list.size(), 3u);
auto it = render_pass->quad_list.begin();
EXPECT_EQ((*it)->material, viz::DrawQuad::Material::kDebugBorder);
EXPECT_EQ(viz::DebugBorderDrawQuad::MaterialCast(*it)->color,
DebugColors::ContainerLayerBorderColor());
++it;
EXPECT_EQ((*it)->material, viz::DrawQuad::Material::kDebugBorder);
EXPECT_EQ(viz::DebugBorderDrawQuad::MaterialCast(*it)->color,
DebugColors::SolidColorTileBorderColor());
++it;
EXPECT_EQ((*it)->material, viz::DrawQuad::Material::kSolidColor);
EXPECT_EQ(viz::SolidColorDrawQuad::MaterialCast(*it)->color, SkColors::kRed);
}
// Verifies that AppendQuads() appends debug borders for a resource tile when
// debug borders are enabled.
TEST_F(TileDisplayLayerImplTest,
AppendQuadsAppendsDebugBordersForResourceTile) {
// Enable debug borders.
LayerTreeDebugState debug_state;
debug_state.show_debug_borders.set(DebugBorderType::LAYER);
host_impl()->SetDebugState(debug_state);
// Set up the layer.
constexpr gfx::Size kLayerBounds(100, 100);
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->SetRecordedBounds(kLayerRect);
raw_layer->draw_properties().visible_layer_rect = kLayerRect;
raw_layer->draw_properties().opacity = 1.0f;
// Add a tiling with a resource 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_resource =
TileDisplayLayerTileResource(resource_id, kLayerBounds);
tiling.SetTileContents(TileIndex{0, 0}, contents_resource,
/*update_damage=*/true);
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 a layer debug border, a tile debug border, and a content quad
// were appended.
ASSERT_EQ(render_pass->quad_list.size(), 3u);
auto it = render_pass->quad_list.begin();
EXPECT_EQ((*it)->material, viz::DrawQuad::Material::kDebugBorder);
EXPECT_EQ(viz::DebugBorderDrawQuad::MaterialCast(*it)->color,
DebugColors::ContainerLayerBorderColor());
++it;
EXPECT_EQ((*it)->material, viz::DrawQuad::Material::kDebugBorder);
EXPECT_EQ(viz::DebugBorderDrawQuad::MaterialCast(*it)->color,
DebugColors::HighResTileBorderColor());
++it;
EXPECT_EQ((*it)->material, viz::DrawQuad::Material::kTiledContent);
EXPECT_EQ(viz::TileDrawQuad::MaterialCast(*it)->resource_id, resource_id);
}
// Verifies that AppendQuads() appends debug borders for an extra-high-res
// resource tile when debug borders are enabled.
TEST_F(TileDisplayLayerImplTest,
AppendQuadsAppendsDebugBordersForExtraHighResResourceTile) {
// Enable debug borders.
LayerTreeDebugState debug_state;
debug_state.show_debug_borders.set(DebugBorderType::LAYER);
host_impl()->SetDebugState(debug_state);
// Set up the layer.
constexpr gfx::Size kLayerBounds(100, 100);
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->SetRecordedBounds(kLayerRect);
raw_layer->draw_properties().visible_layer_rect = kLayerRect;
raw_layer->draw_properties().opacity = 1.0f;
// Add an above-high-res tiling with a resource tile.
auto& tiling = raw_layer->GetOrCreateTilingFromScaleKey(2.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_resource =
TileDisplayLayerTileResource(resource_id, kLayerBounds);
tiling.SetTileContents(TileIndex{0, 0}, contents_resource,
/*update_damage=*/true);
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 a layer debug border, a tile debug border, and a content quad
// were appended.
ASSERT_EQ(render_pass->quad_list.size(), 3u);
auto it = render_pass->quad_list.begin();
EXPECT_EQ((*it)->material, viz::DrawQuad::Material::kDebugBorder);
EXPECT_EQ(viz::DebugBorderDrawQuad::MaterialCast(*it)->color,
DebugColors::ContainerLayerBorderColor());
++it;
EXPECT_EQ((*it)->material, viz::DrawQuad::Material::kDebugBorder);
EXPECT_EQ(viz::DebugBorderDrawQuad::MaterialCast(*it)->color,
DebugColors::AboveHighResTileBorderColor());
++it;
EXPECT_EQ((*it)->material, viz::DrawQuad::Material::kTiledContent);
EXPECT_EQ(viz::TileDrawQuad::MaterialCast(*it)->resource_id, resource_id);
}
// Verifies that AppendQuads() appends debug borders for an OOM tile when debug
// borders are enabled.
TEST_F(TileDisplayLayerImplTest, AppendQuadsAppendsDebugBordersForOomTile) {
// Enable debug borders.
LayerTreeDebugState debug_state;
debug_state.show_debug_borders.set(DebugBorderType::LAYER);
host_impl()->SetDebugState(debug_state);
// Set up the layer.
constexpr gfx::Size kLayerBounds(100, 100);
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->SetRecordedBounds(kLayerRect);
raw_layer->draw_properties().visible_layer_rect = kLayerRect;
raw_layer->draw_properties().opacity = 1.0f;
// Add a tiling with an OOM tile.
auto& tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
tiling.SetTileSize(kLayerBounds);
tiling.SetTilingRect(kLayerRect);
tiling.SetTileContents(
TileIndex{0, 0},
TileDisplayLayerNoContents{mojom::MissingTileReason::kOutOfMemory},
/*update_damage=*/true);
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 a layer debug border, an OOM tile debug border, and a solid
// color quad were appended.
ASSERT_EQ(render_pass->quad_list.size(), 3u);
auto it = render_pass->quad_list.begin();
EXPECT_EQ((*it)->material, viz::DrawQuad::Material::kDebugBorder);
EXPECT_EQ(viz::DebugBorderDrawQuad::MaterialCast(*it)->color,
DebugColors::ContainerLayerBorderColor());
++it;
EXPECT_EQ((*it)->material, viz::DrawQuad::Material::kDebugBorder);
EXPECT_EQ(viz::DebugBorderDrawQuad::MaterialCast(*it)->color,
DebugColors::OOMTileBorderColor());
++it;
EXPECT_EQ((*it)->material, viz::DrawQuad::Material::kSolidColor);
EXPECT_EQ(viz::SolidColorDrawQuad::MaterialCast(*it)->color,
raw_layer->safe_opaque_background_color());
}
// Verifies that AppendQuads() appends debug borders for a not-ready tile when
// debug borders are enabled.
TEST_F(TileDisplayLayerImplTest,
AppendQuadsAppendsDebugBordersForNotReadyTile) {
// Enable debug borders.
LayerTreeDebugState debug_state;
debug_state.show_debug_borders.set(DebugBorderType::LAYER);
host_impl()->SetDebugState(debug_state);
// Set up the layer.
constexpr gfx::Size kLayerBounds(100, 100);
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->SetRecordedBounds(kLayerRect);
raw_layer->draw_properties().visible_layer_rect = kLayerRect;
raw_layer->draw_properties().opacity = 1.0f;
// Add a tiling with a not-ready tile.
auto& tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
tiling.SetTileSize(kLayerBounds);
tiling.SetTilingRect(kLayerRect);
tiling.SetTileContents(
TileIndex{0, 0},
TileDisplayLayerNoContents{mojom::MissingTileReason::kResourceNotReady},
/*update_damage=*/true);
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 a layer debug border, a missing tile debug border, and a
// checkerboarded quad for the missing content were appended.
ASSERT_EQ(render_pass->quad_list.size(), 3u);
auto it = render_pass->quad_list.begin();
EXPECT_EQ((*it)->material, viz::DrawQuad::Material::kDebugBorder);
EXPECT_EQ(viz::DebugBorderDrawQuad::MaterialCast(*it)->color,
DebugColors::ContainerLayerBorderColor());
++it;
EXPECT_EQ((*it)->material, viz::DrawQuad::Material::kDebugBorder);
EXPECT_EQ(viz::DebugBorderDrawQuad::MaterialCast(*it)->color,
DebugColors::MissingTileBorderColor());
++it;
EXPECT_EQ((*it)->material, viz::DrawQuad::Material::kSolidColor);
EXPECT_EQ(viz::SolidColorDrawQuad::MaterialCast(*it)->color,
raw_layer->safe_opaque_background_color());
}
TEST_F(TileDisplayLayerImplTest, TileResourceIsOOM) {
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));
// Missing tile due to OOM.
TileDisplayLayerImpl::TileContents oom_contents{
TileDisplayLayerNoContents(mojom::MissingTileReason::kOutOfMemory)};
TileDisplayLayerTile oom_tile(*raw_layer, oom_contents);
EXPECT_TRUE(oom_tile.is_oom());
// OOM tiles should be regarded as ready to draw.
EXPECT_TRUE(oom_tile.IsReadyToDraw());
// Missing tile due to resource not being ready.
TileDisplayLayerImpl::TileContents resource_not_ready_contents{
TileDisplayLayerNoContents(mojom::MissingTileReason::kResourceNotReady)};
TileDisplayLayerTile not_oom_tile(*raw_layer, resource_not_ready_contents);
EXPECT_FALSE(not_oom_tile.is_oom());
// Non-OOM missing tiles should not be regarded as ready to draw.
EXPECT_FALSE(not_oom_tile.IsReadyToDraw());
// Solid color tile.
TileDisplayLayerImpl::TileContents color_contents(SkColors::kRed);
TileDisplayLayerTile color_tile(*raw_layer, color_contents);
EXPECT_FALSE(color_tile.is_oom());
EXPECT_TRUE(color_tile.IsReadyToDraw());
// Resource tile.
auto resource_id = host_impl()->resource_provider()->ImportResource(
viz::TransferableResource::Make(
gpu::ClientSharedImage::CreateForTesting(),
viz::TransferableResource::ResourceSource::kTest, gpu::SyncToken()),
base::DoNothing());
TileDisplayLayerImpl::TileContents resource_contents =
TileDisplayLayerTileResource(resource_id, gfx::Size(1, 1));
TileDisplayLayerTile resource_tile(*raw_layer, resource_contents);
EXPECT_FALSE(resource_tile.is_oom());
EXPECT_TRUE(resource_tile.IsReadyToDraw());
}
} // namespace cc