blob: 6e32a22b913220936950a493cf1b22b91d8c2ee1 [file]
// Copyright 2024 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 <limits>
#include <memory>
#include <utility>
#include <variant>
#include "base/check.h"
#include "base/logging.h"
#include "base/notreached.h"
#include "cc/base/math_util.h"
#include "cc/layers/append_quads_data.h"
#include "cc/trees/layer_tree_impl.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"
namespace cc {
namespace {
class TilingOrder {
public:
bool operator()(const std::unique_ptr<TileDisplayLayerTiling>& left,
const std::unique_ptr<TileDisplayLayerTiling>& right) {
return left->contents_scale_key() > right->contents_scale_key();
}
};
} // namespace
TileDisplayLayerTile::TileDisplayLayerTile(
TileDisplayLayerImpl& layer,
const TileDisplayLayerTileContents& contents)
: layer_(layer), contents_(contents) {}
TileDisplayLayerTile::TileDisplayLayerTile(TileDisplayLayerTile&&) = default;
TileDisplayLayerTile::~TileDisplayLayerTile() {
if (auto* resource = std::get_if<TileDisplayLayerTileResource>(&contents_)) {
layer_->DiscardResource(resource->resource_id);
}
}
TileDisplayLayerTiling::TileDisplayLayerTiling(TileDisplayLayerImpl& layer,
float scale_key)
: layer_(layer), scale_key_(scale_key) {}
TileDisplayLayerTiling::~TileDisplayLayerTiling() = default;
TileDisplayLayerTile* TileDisplayLayerTiling::TileAt(
const TileIndex& index) const {
auto it = tiles_.find(index);
if (it == tiles_.end()) {
return nullptr;
}
return it->second.get();
}
void TileDisplayLayerTiling::SetRasterTransform(
const gfx::AxisTransform2d& transform) {
DCHECK_EQ(std::max(transform.scale().x(), transform.scale().y()), scale_key_);
raster_transform_ = transform;
}
void TileDisplayLayerTiling::SetTileSize(const gfx::Size& size) {
if (size == tiling_data_.max_texture_size()) {
return;
}
tiling_data_.SetMaxTextureSize(size);
}
void TileDisplayLayerTiling::SetTilingRect(const gfx::Rect& rect) {
if (rect == tiling_data_.tiling_rect()) {
return;
}
tiling_data_.SetTilingRect(rect);
}
void TileDisplayLayerTiling::SetTileContents(
const TileIndex& key,
const TileDisplayLayerTileContents& contents,
bool update_damage) {
if (update_damage) {
// Full tree updates receive damage as part of the LayerImpl::update_rect.
// For incremental tile updates on an Active tree, we need to record the
// damage caused by each tile change.
gfx::Rect tile_rect = tiling_data_.TileBoundsWithBorder(key.i, key.j);
tile_rect.set_size(tiling_data_.max_texture_size());
gfx::Rect enclosing_layer_rect = ToEnclosingRect(
raster_transform_.InverseMapRect(gfx::RectF(tile_rect)));
layer_->RecordDamage(enclosing_layer_rect);
}
std::unique_ptr<Tile> old_tile;
if (std::holds_alternative<TileDisplayLayerNoContents>(contents)) {
const auto& no_contents = std::get<TileDisplayLayerNoContents>(contents);
if (no_contents.reason == mojom::MissingTileReason::kTileDeleted) {
tiles_.erase(key);
} else {
old_tile =
std::exchange(tiles_[key], std::make_unique<Tile>(*layer_, contents));
}
} else {
old_tile =
std::exchange(tiles_[key], std::make_unique<Tile>(*layer_, contents));
}
}
DisplayTilingCoverageIterator TileDisplayLayerTiling::Cover(
const gfx::Rect& coverage_rect,
float coverage_scale) const {
return DisplayTilingCoverageIterator(this, coverage_scale, coverage_rect);
}
gfx::Size TileDisplayLayerTiling::raster_size() const {
return layer_->bounds();
}
TileDisplayLayerImpl::TileDisplayLayerImpl(LayerTreeImpl& tree, int id)
: TileBasedLayerImpl(&tree, id) {}
TileDisplayLayerImpl::~TileDisplayLayerImpl() = default;
TileDisplayLayerTiling& TileDisplayLayerImpl::GetOrCreateTilingFromScaleKey(
float scale_key) {
auto it = std::find_if(tilings_.begin(), tilings_.end(),
[scale_key](const auto& tiling) {
return tiling->contents_scale_key() == scale_key;
});
if (it != tilings_.end()) {
return **it;
}
tilings_.push_back(
std::make_unique<TileDisplayLayerTiling>(*this, scale_key));
TileDisplayLayerTiling& tiling = *tilings_.back();
std::sort(tilings_.begin(), tilings_.end(), TilingOrder());
return tiling;
}
void TileDisplayLayerImpl::RemoveTiling(float scale_key) {
auto it = std::find_if(tilings_.begin(), tilings_.end(),
[scale_key](const auto& tiling) {
return tiling->contents_scale_key() == scale_key;
});
if (it != tilings_.end()) {
tilings_.erase(it);
}
}
const TileDisplayLayerTiling* TileDisplayLayerImpl::GetTilingForTesting(
float scale_key) const {
auto it = std::find_if(tilings_.begin(), tilings_.end(),
[scale_key](const auto& tiling) {
return tiling->contents_scale_key() == scale_key;
});
return it != tilings_.end() ? it->get() : nullptr;
}
mojom::LayerType TileDisplayLayerImpl::GetLayerType() const {
return mojom::LayerType::kTileDisplay;
}
std::unique_ptr<LayerImpl> TileDisplayLayerImpl::CreateLayerImpl(
LayerTreeImpl* tree_impl) const {
NOTREACHED();
}
void TileDisplayLayerImpl::PushPropertiesTo(LayerImpl* layer) {
NOTREACHED();
}
void TileDisplayLayerImpl::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,
float max_contents_scale) {
// NOTE: Currently it is not necessary to compute
// append_quads_data->checkerboarded_needs_recorded on the Viz side, as it is
// consumed only on the client side. However, it will become necessary when we
// introduce frames driven entirely by Viz. At that point, we should dedupe
// the relevant code into TileBasedLayerImpl.
// See crbug.com/482862751.
const float ideal_scale_key = GetIdealContentsScaleKey();
// Append quads for the tiles in this layer.
for (auto iter = Cover(shared_quad_state->visible_quad_layer_rect,
max_contents_scale, ideal_scale_key);
iter; ++iter) {
AppendQuadForTile(iter, context, render_pass, append_quads_data,
shared_quad_state, scaled_occlusion, quad_offset,
max_contents_scale);
}
}
void TileDisplayLayerImpl::AppendQuadForTile(
TilingSetCoverageIterator<TileDisplayLayerTiling> iter,
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,
float max_contents_scale) {
const gfx::Rect scaled_recorded_bounds =
gfx::ScaleToEnclosingRect(recorded_bounds_, max_contents_scale);
const gfx::Rect geometry_rect = iter.geometry_rect();
gfx::Rect visible_geometry_rect;
if (ShouldSkipTile(geometry_rect, scaled_recorded_bounds, scaled_occlusion,
visible_geometry_rect)) {
return;
}
gfx::Rect offset_geometry_rect = geometry_rect;
offset_geometry_rect.Offset(quad_offset);
gfx::Rect offset_visible_geometry_rect = visible_geometry_rect;
offset_visible_geometry_rect.Offset(quad_offset);
bool needs_blending = !contents_opaque();
uint64_t visible_geometry_area = visible_geometry_rect.size().Area64();
append_quads_data->visible_layer_area += visible_geometry_area;
bool has_draw_quad = false;
if (*iter) {
if (auto resource = iter->resource()) {
const gfx::RectF texture_rect = iter.texture_rect();
auto* quad = render_pass->CreateAndAppendDrawQuad<viz::TileDrawQuad>();
quad->SetNew(shared_quad_state, offset_geometry_rect,
offset_visible_geometry_rect, needs_blending,
resource->resource_id, texture_rect, nearest_neighbor_,
!layer_tree_impl()->settings().enable_edge_anti_aliasing);
has_draw_quad = true;
} else if (auto color = iter->solid_color()) {
has_draw_quad = true;
const float alpha = color->fA * shared_quad_state->opacity;
if (alpha >= std::numeric_limits<float>::epsilon()) {
auto* quad =
render_pass->CreateAndAppendDrawQuad<viz::SolidColorDrawQuad>();
quad->SetNew(shared_quad_state, offset_geometry_rect,
offset_visible_geometry_rect, *color,
!layer_tree_impl()->settings().enable_edge_anti_aliasing);
}
} else if (iter->is_oom()) {
// Keep `has_draw_quad` false to end up checkerboarding below.
}
}
if (!has_draw_quad) {
// Checkerboard due to missing raster.
SkColor4f color = safe_opaque_background_color();
auto* quad =
render_pass->CreateAndAppendDrawQuad<viz::SolidColorDrawQuad>();
quad->SetNew(shared_quad_state, offset_geometry_rect,
offset_visible_geometry_rect, color, false);
return;
}
AddScaleToLastAppendQuadsScales(iter.CurrentTiling()->contents_scale_key());
}
float TileDisplayLayerImpl::GetMaximumContentsScaleForUseInAppendQuads() const {
return tilings_.empty() ? 1.0 : tilings_.front()->contents_scale_key();
}
bool TileDisplayLayerImpl::IsDirectlyCompositedImage() const {
return is_directly_composited_image_;
}
void TileDisplayLayerImpl::GetContentsResourceId(
viz::ResourceId* resource_id,
gfx::Size* resource_size,
gfx::SizeF* resource_uv_size) const {
*resource_id = viz::kInvalidResourceId;
// We need contents resource for backdrop filter masks only.
if (!is_backdrop_filter_mask()) {
return;
}
// Masks are only supported if they fit on exactly one tile.
if (tilings_.size() != 1u) {
return;
}
const float max_contents_scale = tilings_.front()->contents_scale_key();
gfx::Rect content_rect =
gfx::ScaleToEnclosingRect(gfx::Rect(bounds()), max_contents_scale);
auto iter = TilingSetCoverageIterator<TileDisplayLayerTiling>(
tilings_, content_rect, max_contents_scale, GetIdealContentsScaleKey());
// We cannot do anything if the mask resource was not provided.
if (!iter || !*iter || !iter->resource()) {
return;
}
DCHECK(iter.geometry_rect() == content_rect)
<< "iter rect " << iter.geometry_rect().ToString() << " content rect "
<< content_rect.ToString();
*resource_id = iter->resource()->resource_id;
*resource_size = iter->resource()->resource_size;
gfx::SizeF requested_tile_size =
gfx::SizeF(iter.CurrentTiling()->tile_size());
*resource_uv_size =
gfx::SizeF(requested_tile_size.width() / resource_size->width(),
requested_tile_size.height() / resource_size->height());
}
gfx::Rect TileDisplayLayerImpl::GetDamageRect() const {
return damage_rect_;
}
void TileDisplayLayerImpl::ResetChangeTracking() {
LayerImpl::ResetChangeTracking();
damage_rect_.SetRect(0, 0, 0, 0);
}
gfx::ContentColorUsage TileDisplayLayerImpl::GetContentColorUsage() const {
return content_color_usage_;
}
void TileDisplayLayerImpl::RecordDamage(const gfx::Rect& damage_rect) {
damage_rect_.Union(damage_rect);
}
void TileDisplayLayerImpl::DiscardResource(viz::ResourceId resource) {
layer_tree_impl()->host_impl()->resource_provider()->RemoveImportedResource(
std::move(resource));
}
std::vector<float> TileDisplayLayerImpl::GetSafeToDeleteTilings() {
std::vector<float> safe_to_delete_scales;
for (float scale : proposed_tiling_scales_for_deletion_) {
// Check if a tiling corresponding to the candidate scale is present in
// |last_append_quads_scales_|.
if (!LastAppendQuadsScalesContains(scale)) {
// If a tiling corresponding to the candidate scale is not present in
// |last_append_quads_scales_|, then its safe to delete.
safe_to_delete_scales.push_back(scale);
}
}
proposed_tiling_scales_for_deletion_.clear();
return safe_to_delete_scales;
}
float TileDisplayLayerImpl::GetIdealContentsScaleKey() const {
const auto ideal_scale = GetIdealContentsScale();
return std::max(ideal_scale.x(), ideal_scale.y());
}
void TileDisplayLayerImpl::AppendQuadsForResourcelessSoftwareDraw(
const AppendQuadsContext& context,
viz::CompositorRenderPass* render_pass,
AppendQuadsData* append_quads_data,
viz::SharedQuadState* shared_quad_state,
const Occlusion& scaled_occlusion) {
// `DRAW_MODE_RESOURCELESS_SOFTWARE` is a renderer-only software draw mode,
// and its handling is thus specific to the renderer-side PictureLayerImpl. It
// should never be propagated to the Viz side.
NOTREACHED();
}
TilingSetCoverageIterator<TileDisplayLayerTiling> TileDisplayLayerImpl::Cover(
const gfx::Rect& coverage_rect,
float coverage_scale,
float ideal_contents_scale) {
return TilingSetCoverageIterator<TileDisplayLayerTiling>(
tilings_, coverage_rect, coverage_scale, ideal_contents_scale);
}
TileBasedLayerImpl<TileDisplayLayerTiling>::TilingResolution
TileDisplayLayerImpl::GetTilingResolutionForDebugBorders(
const TileDisplayLayerTiling* tiling) const {
const float ideal_scale_key = GetIdealContentsScaleKey();
if (MathUtil::IsFloatNearlyTheSame(tiling->contents_scale_key(),
ideal_scale_key)) {
// NOTE: The above check is not exactly the same computation as is
// used by PictureLayerImpl, as high resolution tiles within
// PictureLayerImpl use `raster_contents_scale_`, which is not
// necessarily the ideal scale. However, we don't have the former
// field here, so use the ideal scale as an approximation.
// TODO(crbug.com/450651370): Determine whether we want to fix this.
return TilingResolution::kHigh;
}
if (tiling->contents_scale_key() > ideal_scale_key) {
return TilingResolution::kAboveHigh;
}
return TilingResolution::kBelowHigh;
}
} // namespace cc