// Copyright 2019 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "cc/layers/tile_size_calculator.h" #include "cc/base/math_util.h" #include "cc/layers/picture_layer_impl.h" #include "cc/trees/layer_tree_impl.h" namespace cc { namespace { // Even for really wide viewports, at some point GPU raster should use // less than 4 tiles to fill the viewport. This is set to 256 as a // sane minimum for now, but we might want to tune this for low-end. const int kMinHeightForGpuRasteredTile = 256; // When making odd-sized tiles, round them up to increase the chances // of using the same tile size. const int kTileRoundUp = 64; // Round GPU default tile sizes to a multiple of 32. This helps prevent // rounding errors during compositing. const int kGpuDefaultTileRoundUp = 32; // For performance reasons and to support compressed tile textures, tile // width and height should be an even multiple of 4 in size. const int kTileMinimalAlignment = 4; // This function converts the given |device_pixels_size| to the expected size // of content which was generated to fill it at 100%. This takes into account // the ceil operations that occur as device pixels are converted to/from DIPs // (content size must be a whole number of DIPs). gfx::Size ApplyDsfAdjustment(const gfx::Size& device_pixels_size, float dsf) { gfx::Size content_size_in_dips = gfx::ScaleToCeiledSize(device_pixels_size, 1.0f / dsf); gfx::Size content_size_in_dps = gfx::ScaleToCeiledSize(content_size_in_dips, dsf); return content_size_in_dps; } // For GPU rasterization, we pick an ideal tile size using the viewport so we // don't need any settings. The current approach uses 4 tiles to cover the // viewport vertically. gfx::Size CalculateGpuTileSize(const gfx::Size& base_tile_size, const gfx::Size& content_bounds, const gfx::Size& max_tile_size) { int tile_width = base_tile_size.width(); // Increase the height proportionally as the width decreases, and pad by our // border texels to make the tiles exactly match the viewport. int divisor = 4; if (content_bounds.width() <= base_tile_size.width() / 2) divisor = 2; if (content_bounds.width() <= base_tile_size.width() / 4) divisor = 1; int tile_height = MathUtil::UncheckedRoundUp(base_tile_size.height(), divisor) / divisor; // Grow default sizes to account for overlapping border texels. tile_width += 2 * PictureLayerTiling::kBorderTexels; tile_height += 2 * PictureLayerTiling::kBorderTexels; // Round GPU default tile sizes to a multiple of kGpuDefaultTileAlignment. // This helps prevent rounding errors in our CA path. https://crbug.com/632274 tile_width = MathUtil::UncheckedRoundUp(tile_width, kGpuDefaultTileRoundUp); tile_height = MathUtil::UncheckedRoundUp(tile_height, kGpuDefaultTileRoundUp); tile_height = std::max(tile_height, kMinHeightForGpuRasteredTile); if (!max_tile_size.IsEmpty()) { tile_width = std::min(tile_width, max_tile_size.width()); tile_height = std::min(tile_height, max_tile_size.height()); } return gfx::Size(tile_width, tile_height); } } // namespace // AffectingParams. bool TileSizeCalculator::AffectingParams::operator==( const AffectingParams& other) { return max_texture_size == other.max_texture_size && use_gpu_rasterization == other.use_gpu_rasterization && device_scale_factor == other.device_scale_factor && max_tile_size == other.max_tile_size && gpu_raster_max_texture_size == other.gpu_raster_max_texture_size && max_untiled_layer_size == other.max_untiled_layer_size && default_tile_size == other.default_tile_size && layer_content_bounds == other.layer_content_bounds; } // TileSizeCalculator. TileSizeCalculator::TileSizeCalculator(PictureLayerImpl* layer_impl) : layer_impl_(layer_impl) {} bool TileSizeCalculator::IsAffectingParamsChanged() { AffectingParams new_params = GetAffectingParams(); if (affecting_params_ == new_params) return false; affecting_params_ = new_params; return true; } TileSizeCalculator::AffectingParams TileSizeCalculator::GetAffectingParams() { AffectingParams params; LayerTreeImpl* layer_tree_impl = layer_impl()->layer_tree_impl(); params.max_texture_size = layer_tree_impl->max_texture_size(); params.use_gpu_rasterization = layer_tree_impl->use_gpu_rasterization(); params.max_tile_size = layer_tree_impl->settings().max_gpu_raster_tile_size; params.gpu_raster_max_texture_size = layer_impl()->gpu_raster_max_texture_size(); params.device_scale_factor = layer_tree_impl->device_scale_factor(); params.max_untiled_layer_size = layer_tree_impl->settings().max_untiled_layer_size; params.default_tile_size = layer_tree_impl->settings().default_tile_size; params.layer_content_bounds = layer_impl()->content_bounds(); return params; } gfx::Size TileSizeCalculator::CalculateTileSize() { gfx::Size content_bounds = layer_impl()->content_bounds(); if (layer_impl()->is_backdrop_filter_mask()) { // Backdrop filter masks are not tiled, so if we can't cover the whole mask // with one tile, we shouldn't have such a tiling at all. DCHECK_LE(content_bounds.width(), layer_impl()->layer_tree_impl()->max_texture_size()); DCHECK_LE(content_bounds.height(), layer_impl()->layer_tree_impl()->max_texture_size()); return content_bounds; } // If |affecting_params_| is already computed and not changed, return // pre-calculated tile size. if (!IsAffectingParamsChanged()) return tile_size_; int default_tile_width = 0; int default_tile_height = 0; if (affecting_params_.use_gpu_rasterization) { gfx::Size max_tile_size = affecting_params_.max_tile_size; // Calculate |base_tile_size| based on |gpu_raster_max_texture_size|, // adjusting for ceil operations that may occur due to DSF. gfx::Size base_tile_size = ApplyDsfAdjustment(affecting_params_.gpu_raster_max_texture_size, affecting_params_.device_scale_factor); // Set our initial size assuming a |base_tile_size| equal to our // |viewport_size|. gfx::Size default_tile_size = CalculateGpuTileSize(base_tile_size, content_bounds, max_tile_size); // Use half-width GPU tiles when the content_width is greater than our // calculated tile size. if (content_bounds.width() > default_tile_size.width()) { // Divide width by 2 and round up. base_tile_size.set_width((base_tile_size.width() + 1) / 2); default_tile_size = CalculateGpuTileSize(base_tile_size, content_bounds, max_tile_size); } default_tile_width = default_tile_size.width(); default_tile_height = default_tile_size.height(); } else { // For CPU rasterization we use tile-size settings. int max_untiled_content_width = affecting_params_.max_untiled_layer_size.width(); int max_untiled_content_height = affecting_params_.max_untiled_layer_size.height(); default_tile_width = affecting_params_.default_tile_size.width(); default_tile_height = affecting_params_.default_tile_size.height(); // If the content width is small, increase tile size vertically. // If the content height is small, increase tile size horizontally. // If both are less than the untiled-size, use a single tile. if (content_bounds.width() < default_tile_width) default_tile_height = max_untiled_content_height; if (content_bounds.height() < default_tile_height) default_tile_width = max_untiled_content_width; if (content_bounds.width() < max_untiled_content_width && content_bounds.height() < max_untiled_content_height) { default_tile_height = max_untiled_content_height; default_tile_width = max_untiled_content_width; } } int tile_width = default_tile_width; int tile_height = default_tile_height; // Clamp the tile width/height to the content width/height to save space. if (content_bounds.width() < default_tile_width) { tile_width = std::min(tile_width, content_bounds.width()); tile_width = MathUtil::UncheckedRoundUp(tile_width, kTileRoundUp); tile_width = std::min(tile_width, default_tile_width); } if (content_bounds.height() < default_tile_height) { tile_height = std::min(tile_height, content_bounds.height()); tile_height = MathUtil::UncheckedRoundUp(tile_height, kTileRoundUp); tile_height = std::min(tile_height, default_tile_height); } // Ensure that tile width and height are properly aligned. tile_width = MathUtil::UncheckedRoundUp(tile_width, kTileMinimalAlignment); tile_height = MathUtil::UncheckedRoundUp(tile_height, kTileMinimalAlignment); // Under no circumstance should we be larger than the max texture size. tile_width = std::min(tile_width, affecting_params_.max_texture_size); tile_height = std::min(tile_height, affecting_params_.max_texture_size); // Store the calculated tile size. tile_size_ = gfx::Size(tile_width, tile_height); return tile_size_; } } // namespace cc