blob: 931cabb1fd987e584e48ce771bb31a88d894561d [file] [log] [blame]
// 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/functional/overloaded.h"
#include "base/logging.h"
#include "base/notreached.h"
#include "cc/layers/append_quads_data.h"
#include "cc/tiles/tiling_set_coverage_iterator.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<TileDisplayLayerImpl::Tiling>& left,
const std::unique_ptr<TileDisplayLayerImpl::Tiling>& right) {
return left->contents_scale_key() > right->contents_scale_key();
}
};
} // namespace
TileDisplayLayerImpl::TileResource::TileResource(
const viz::TransferableResource& resource,
bool is_checkered)
: resource(resource),
is_checkered(is_checkered) {}
TileDisplayLayerImpl::TileResource::TileResource(const TileResource&) = default;
TileDisplayLayerImpl::TileResource&
TileDisplayLayerImpl::TileResource::operator=(const TileResource&) = default;
TileDisplayLayerImpl::TileResource::~TileResource() = default;
TileDisplayLayerImpl::Tile::Tile(TileDisplayLayerImpl& layer,
const TileContents& contents)
: layer_(layer), contents_(contents) {
DCHECK(!std::holds_alternative<NoContents>(contents_));
}
TileDisplayLayerImpl::Tile::Tile(Tile&&) = default;
TileDisplayLayerImpl::Tile::~Tile() {
if (auto* resource = std::get_if<TileResource>(&contents_)) {
layer_->DiscardResource(resource->resource.id);
}
}
TileDisplayLayerImpl::Tiling::Tiling(TileDisplayLayerImpl& layer,
float scale_key)
: layer_(layer), scale_key_(scale_key) {}
TileDisplayLayerImpl::Tiling::~Tiling() = default;
TileDisplayLayerImpl::Tile* TileDisplayLayerImpl::Tiling::TileAt(
const TileIndex& index) const {
auto it = tiles_.find(index);
if (it == tiles_.end()) {
return nullptr;
}
return it->second.get();
}
void TileDisplayLayerImpl::Tiling::SetRasterTransform(
const gfx::AxisTransform2d& transform) {
DCHECK_EQ(std::max(transform.scale().x(), transform.scale().y()), scale_key_);
raster_transform_ = transform;
}
void TileDisplayLayerImpl::Tiling::SetTileSize(const gfx::Size& size) {
if (size == tiling_data_.max_texture_size()) {
return;
}
tiling_data_.SetMaxTextureSize(size);
tiles_.clear();
}
void TileDisplayLayerImpl::Tiling::SetTilingRect(const gfx::Rect& rect) {
if (rect == tiling_data_.tiling_rect()) {
return;
}
tiling_data_.SetTilingRect(rect);
tiles_.clear();
}
void TileDisplayLayerImpl::Tiling::SetTileContents(const TileIndex& key,
const TileContents& 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<NoContents>(contents)) {
auto it = tiles_.find(key);
if (it != tiles_.end()) {
old_tile = std::move(it->second);
tiles_.erase(it);
}
} else {
// If there is a valid TileResource, import it in order to track its usage.
if (auto* resource = std::get_if<TileResource>(&contents)) {
layer_->ImportResource(resource->resource);
}
old_tile =
std::exchange(tiles_[key], std::make_unique<Tile>(*layer_, contents));
}
}
TileDisplayLayerImpl::DisplayTilingCoverageIterator
TileDisplayLayerImpl::Tiling::Cover(const gfx::Rect& coverage_rect,
float coverage_scale) const {
return DisplayTilingCoverageIterator(this, coverage_scale, coverage_rect);
}
TileDisplayLayerImpl::TileDisplayLayerImpl(LayerTreeImpl& tree, int id)
: LayerImpl(&tree, id) {}
TileDisplayLayerImpl::~TileDisplayLayerImpl() = default;
TileDisplayLayerImpl::Tiling&
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<Tiling>(*this, scale_key));
Tiling& 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);
}
}
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::AppendQuads(const AppendQuadsContext& context,
viz::CompositorRenderPass* render_pass,
AppendQuadsData* append_quads_data) {
if (solid_color_) {
CHECK(tilings_.empty());
AppendSolidQuad(render_pass, append_quads_data, *solid_color_);
return;
}
if (tilings_.empty()) {
return;
}
const float max_contents_scale =
tilings_.empty() ? 1.0f : tilings_.front()->contents_scale_key();
// If this layer is used as a backdrop filter, don't create and append a quad
// as that will be done in RenderSurfaceImpl::AppendQuads.
if (is_backdrop_filter_mask_) {
return;
}
viz::SharedQuadState* shared_quad_state =
render_pass->CreateAndAppendSharedQuadState();
PopulateScaledSharedQuadState(shared_quad_state, max_contents_scale,
contents_opaque());
const Occlusion scaled_occlusion =
draw_properties()
.occlusion_in_content_space.GetOcclusionWithGivenDrawTransform(
shared_quad_state->quad_to_target_transform);
// If we're doing a regular AppendQuads (ie, not solid color or resourceless
// software draw, and if the visible rect is scrolled far enough away, then we
// may run into a floating point precision in AA calculations in the renderer.
// See crbug.com/765297. In order to avoid this, we shift the quads up from
// where they logically reside and adjust the shared_quad_state's transform
// instead. We only do this in a scale/translate matrices to ensure the math
// is correct.
gfx::Vector2d quad_offset;
if (shared_quad_state->quad_to_target_transform.IsScaleOrTranslation()) {
const auto& visible_rect = shared_quad_state->visible_quad_layer_rect;
quad_offset = gfx::Vector2d(-visible_rect.x(), -visible_rect.y());
}
// TODO(crbug.com/40902346): Use scaled_cull_rect to set
// append_quads_data->checkerboarded_needs_record.
std::optional<gfx::Rect> scaled_cull_rect;
const ScrollTree& scroll_tree =
layer_tree_impl()->property_trees()->scroll_tree();
if (const ScrollNode* scroll_node = scroll_tree.Node(scroll_tree_index())) {
if (transform_tree_index() == scroll_node->transform_id) {
if (const gfx::Rect* cull_rect =
scroll_tree.ScrollingContentsCullRect(scroll_node->element_id)) {
scaled_cull_rect =
gfx::ScaleToEnclosingRect(*cull_rect, max_contents_scale);
}
}
}
std::vector<viz::TransferableResource> used_resources;
const auto ideal_scale = GetIdealContentsScale();
const float ideal_scale_key = std::max(ideal_scale.x(), ideal_scale.y());
// Append quads for the tiles in this layer.
for (auto iter = TilingSetCoverageIterator<Tiling>(
tilings_, shared_quad_state->visible_quad_layer_rect,
max_contents_scale, ideal_scale_key);
iter; ++iter) {
const gfx::Rect geometry_rect = iter.geometry_rect();
const gfx::Rect visible_geometry_rect =
scaled_occlusion.GetUnoccludedContentRect(geometry_rect);
if (visible_geometry_rect.IsEmpty()) {
continue;
}
const gfx::Rect offset_geometry_rect = geometry_rect + quad_offset;
const gfx::Rect offset_visible_geometry_rect =
visible_geometry_rect + quad_offset;
const bool needs_blending = !contents_opaque();
const 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,
iter.CurrentTiling()->tile_size(),
/*nearest_neighbor=*/false,
/*enable_edge_aa=*/false);
used_resources.push_back(resource->resource);
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,
/*enable_edge_aa=*/false);
}
}
}
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);
}
}
// Adjust shared_quad_state with the quad_offset, since we've adjusted each
// quad we've appended by it.
shared_quad_state->quad_to_target_transform.Translate(-quad_offset);
shared_quad_state->quad_layer_rect.Offset(quad_offset);
shared_quad_state->visible_quad_layer_rect.Offset(quad_offset);
}
void TileDisplayLayerImpl::GetContentsResourceId(
viz::ResourceId* resource_id,
gfx::Size* resource_size,
gfx::SizeF* resource_uv_size) const {
CHECK(is_backdrop_filter_mask_);
CHECK_EQ(tilings_.size(), 1u);
const float max_contents_scale =
tilings_.empty() ? 1.0f : tilings_.front()->contents_scale_key();
gfx::Rect content_rect =
gfx::ScaleToEnclosingRect(gfx::Rect(bounds()), max_contents_scale);
const auto ideal_scale = GetIdealContentsScale();
const float ideal_scale_key = std::max(ideal_scale.x(), ideal_scale.y());
auto iter = TilingSetCoverageIterator<Tiling>(
tilings_, content_rect, max_contents_scale, ideal_scale_key);
CHECK(iter->resource());
*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());
std::vector<viz::TransferableResource> used_resources;
used_resources.push_back(iter->resource()->resource);
}
gfx::Rect TileDisplayLayerImpl::GetDamageRect() const {
return damage_rect_;
}
void TileDisplayLayerImpl::ResetChangeTracking() {
LayerImpl::ResetChangeTracking();
damage_rect_.SetRect(0, 0, 0, 0);
}
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));
}
void TileDisplayLayerImpl::ImportResource(viz::TransferableResource resource) {
// Note that using LayerTreeHostImpl* is safe since LayerTreeHostImpl owns
// ClientResourceProvider and hence oulives it.
auto release_callback = base::BindOnce(
[](LayerTreeHostImpl* host_impl, viz::ResourceId id,
const gpu::SyncToken& sync_token, bool is_lost) {
host_impl->ReturnResource({id, sync_token,
/*release_fence=*/gfx::GpuFenceHandle(),
/*count=*/1, is_lost});
},
layer_tree_impl()->host_impl(), resource.id);
layer_tree_impl()->host_impl()->resource_provider()->ImportResource(
resource, /*impl_release_callback=*/std::move(release_callback),
/*main_thread_release_callback=*/base::NullCallback(),
/*evicted_callback=*/base::NullCallback());
}
} // namespace cc