blob: 2c8e7445e9226f4860a0d36284e0cf770dbaa233 [file] [log] [blame]
// Copyright 2020 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 "third_party/blink/renderer/platform/widget/compositing/layer_tree_settings.h"
#include "base/base_switches.h"
#include "base/command_line.h"
#include "base/strings/string_number_conversions.h"
#include "base/system/sys_info.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "cc/base/features.h"
#include "cc/base/switches.h"
#include "components/viz/common/display/de_jelly.h"
#include "components/viz/common/features.h"
#include "components/viz/common/switches.h"
#include "gpu/command_buffer/service/gpu_switches.h"
#include "gpu/config/gpu_finch_features.h"
#include "media/base/media_switches.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/switches.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "ui/base/ui_base_features.h"
#include "ui/base/ui_base_switches.h"
#include "ui/native_theme/native_theme_features.h"
#include "ui/native_theme/overlay_scrollbar_constants_aura.h"
namespace blink {
namespace {
const base::Feature kUnpremultiplyAndDitherLowBitDepthTiles = {
"UnpremultiplyAndDitherLowBitDepthTiles", base::FEATURE_ENABLED_BY_DEFAULT};
#if defined(OS_ANDROID)
// With 32 bit pixels, this would mean less than 400kb per buffer. Much less
// than required for, say, nHD.
static const int kSmallScreenPixelThreshold = 1e5;
bool IsSmallScreen(const gfx::Size& size) {
int area = 0;
if (!size.GetCheckedArea().AssignIfValid(&area))
return false;
return area < kSmallScreenPixelThreshold;
}
#endif
} // namespace
// static
cc::ManagedMemoryPolicy GetGpuMemoryPolicy(
const cc::ManagedMemoryPolicy& default_policy,
const gfx::Size& initial_screen_size,
float initial_device_scale_factor) {
cc::ManagedMemoryPolicy actual = default_policy;
actual.bytes_limit_when_visible = 0;
// If the value was overridden on the command line, use the specified value.
static bool client_hard_limit_bytes_overridden =
base::CommandLine::ForCurrentProcess()->HasSwitch(
::switches::kForceGpuMemAvailableMb);
if (client_hard_limit_bytes_overridden) {
if (base::StringToSizeT(
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
::switches::kForceGpuMemAvailableMb),
&actual.bytes_limit_when_visible))
actual.bytes_limit_when_visible *= 1024 * 1024;
return actual;
}
#if defined(OS_ANDROID)
// We can't query available GPU memory from the system on Android.
// Physical memory is also mis-reported sometimes (eg. Nexus 10 reports
// 1262MB when it actually has 2GB, while Razr M has 1GB but only reports
// 128MB java heap size). First we estimate physical memory using both.
size_t dalvik_mb = base::SysInfo::DalvikHeapSizeMB();
size_t physical_mb = base::SysInfo::AmountOfPhysicalMemoryMB();
size_t physical_memory_mb = 0;
if (base::SysInfo::IsLowEndDevice()) {
// TODO(crbug.com/742534): The code below appears to no longer work.
// |dalvik_mb| no longer follows the expected heuristic pattern, causing us
// to over-estimate memory on low-end devices. This entire section probably
// needs to be re-written, but for now we can address the low-end Android
// issues by ignoring |dalvik_mb|.
physical_memory_mb = physical_mb;
} else if (dalvik_mb >= 256) {
physical_memory_mb = dalvik_mb * 4;
} else {
physical_memory_mb = std::max(dalvik_mb * 4, (physical_mb * 4) / 3);
}
// Now we take a default of 1/8th of memory on high-memory devices,
// and gradually scale that back for low-memory devices (to be nicer
// to other apps so they don't get killed). Examples:
// Nexus 4/10(2GB) 256MB (normally 128MB)
// Droid Razr M(1GB) 114MB (normally 57MB)
// Galaxy Nexus(1GB) 100MB (normally 50MB)
// Xoom(1GB) 100MB (normally 50MB)
// Nexus S(low-end) 8MB (normally 8MB)
// Note that the compositor now uses only some of this memory for
// pre-painting and uses the rest only for 'emergencies'.
if (actual.bytes_limit_when_visible == 0) {
// NOTE: Non-low-end devices use only 50% of these limits,
// except during 'emergencies' where 100% can be used.
if (physical_memory_mb >= 1536) {
actual.bytes_limit_when_visible = physical_memory_mb / 8; // >192MB
} else if (physical_memory_mb >= 1152) {
actual.bytes_limit_when_visible = physical_memory_mb / 8; // >144MB
} else if (physical_memory_mb >= 768) {
actual.bytes_limit_when_visible = physical_memory_mb / 10; // >76MB
} else if (physical_memory_mb >= 513) {
actual.bytes_limit_when_visible = physical_memory_mb / 12; // <64MB
} else {
// Devices with this little RAM have very little headroom so we hardcode
// the limit rather than relying on the heuristics above. (They also use
// 4444 textures so we can use a lower limit.)
actual.bytes_limit_when_visible = 8;
}
actual.bytes_limit_when_visible =
actual.bytes_limit_when_visible * 1024 * 1024;
// Clamp the observed value to a specific range on Android.
actual.bytes_limit_when_visible = std::max(
actual.bytes_limit_when_visible, static_cast<size_t>(8 * 1024 * 1024));
actual.bytes_limit_when_visible =
std::min(actual.bytes_limit_when_visible,
static_cast<size_t>(256 * 1024 * 1024));
}
actual.priority_cutoff_when_visible =
gpu::MemoryAllocation::CUTOFF_ALLOW_EVERYTHING;
#else
// Ignore what the system said and give all clients the same maximum
// allocation on desktop platforms.
actual.bytes_limit_when_visible = 512 * 1024 * 1024;
actual.priority_cutoff_when_visible =
gpu::MemoryAllocation::CUTOFF_ALLOW_NICE_TO_HAVE;
// For large monitors (4k), double the tile memory to avoid frequent out of
// memory problems. 4k could mean a screen width of anywhere from 3840 to 4096
// (see https://en.wikipedia.org/wiki/4K_resolution). We use 3500 as a proxy
// for "large enough".
static const int kLargeDisplayThreshold = 3500;
int display_width =
std::round(initial_screen_size.width() * initial_device_scale_factor);
if (display_width >= kLargeDisplayThreshold)
actual.bytes_limit_when_visible *= 2;
#endif
return actual;
}
// static
cc::LayerTreeSettings GenerateLayerTreeSettings(
bool is_threaded,
bool for_child_local_root_frame,
const gfx::Size& initial_screen_size,
float initial_device_scale_factor) {
const base::CommandLine& cmd = *base::CommandLine::ForCurrentProcess();
cc::LayerTreeSettings settings;
settings.enable_synchronized_scrolling =
base::FeatureList::IsEnabled(::features::kSynchronizedScrolling);
Platform* platform = Platform::Current();
settings.use_zoom_for_dsf = platform->IsUseZoomForDSFEnabled();
settings.percent_based_scrolling =
base::FeatureList::IsEnabled(::features::kPercentBasedScrolling);
settings.compositor_threaded_scrollbar_scrolling =
base::FeatureList::IsEnabled(
::features::kCompositorThreadedScrollbarScrolling);
settings.resource_settings.use_r16_texture =
base::FeatureList::IsEnabled(media::kUseR16Texture);
settings.commit_to_active_tree = !is_threaded;
settings.is_layer_tree_for_subframe = for_child_local_root_frame;
settings.main_frame_before_activation_enabled =
cmd.HasSwitch(cc::switches::kEnableMainFrameBeforeActivation);
// Checkerimaging is not supported for synchronous single-threaded mode, which
// is what the renderer uses if its not threaded.
settings.enable_checker_imaging =
!cmd.HasSwitch(cc::switches::kDisableCheckerImaging) && is_threaded;
#if defined(OS_ANDROID)
// WebView should always raster in the default color space.
// Synchronous compositing indicates WebView.
if (!platform->IsSynchronousCompositingEnabledForAndroidWebView())
settings.prefer_raster_in_srgb = ::features::IsDynamicColorGamutEnabled();
// We can use a more aggressive limit on Android since decodes tend to take
// longer on these devices.
settings.min_image_bytes_to_checker = 512 * 1024; // 512kB
// Re-rasterization of checker-imaged content with software raster can be too
// costly on Android.
settings.only_checker_images_with_gpu_raster = true;
#endif
auto switch_value_as_int = [](const base::CommandLine& command_line,
const std::string& switch_string, int min_value,
int max_value, int* result) {
std::string string_value = command_line.GetSwitchValueASCII(switch_string);
int int_value;
if (base::StringToInt(string_value, &int_value) && int_value >= min_value &&
int_value <= max_value) {
*result = int_value;
return true;
} else {
DLOG(WARNING) << "Failed to parse switch " << switch_string << ": "
<< string_value;
return false;
}
};
int default_tile_size = 256;
#if defined(OS_ANDROID)
const gfx::Size screen_size =
gfx::ScaleToFlooredSize(initial_screen_size, initial_device_scale_factor);
int display_width = screen_size.width();
int display_height = screen_size.height();
int numTiles = (display_width * display_height) / (256 * 256);
if (numTiles > 16)
default_tile_size = 384;
if (numTiles >= 40)
default_tile_size = 512;
// Adjust for some resolutions that barely straddle an extra
// tile when in portrait mode. This helps worst case scroll/raster
// by not needing a full extra tile for each row.
constexpr int tolerance = 10; // To avoid rounding errors.
int portrait_width = std::min(display_width, display_height);
if (default_tile_size == 256 && std::abs(portrait_width - 768) < tolerance)
default_tile_size += 32;
if (default_tile_size == 384 && std::abs(portrait_width - 1200) < tolerance)
default_tile_size += 32;
#elif BUILDFLAG(IS_CHROMEOS_ASH) || defined(OS_MAC)
// Use 512 for high DPI (dsf=2.0f) devices.
if (initial_device_scale_factor >= 2.0f)
default_tile_size = 512;
#endif
// TODO(danakj): This should not be a setting O_O; it should change when the
// device scale factor on LayerTreeHost changes.
settings.default_tile_size = gfx::Size(default_tile_size, default_tile_size);
if (cmd.HasSwitch(switches::kDefaultTileWidth)) {
int tile_width = 0;
switch_value_as_int(cmd, switches::kDefaultTileWidth, 1,
std::numeric_limits<int>::max(), &tile_width);
settings.default_tile_size.set_width(tile_width);
}
if (cmd.HasSwitch(switches::kDefaultTileHeight)) {
int tile_height = 0;
switch_value_as_int(cmd, switches::kDefaultTileHeight, 1,
std::numeric_limits<int>::max(), &tile_height);
settings.default_tile_size.set_height(tile_height);
}
if (cmd.HasSwitch(switches::kMinHeightForGpuRasterTile)) {
int min_height_for_gpu_raster_tile = 0;
switch_value_as_int(cmd, switches::kMinHeightForGpuRasterTile, 1,
std::numeric_limits<int>::max(),
&min_height_for_gpu_raster_tile);
settings.min_height_for_gpu_raster_tile = min_height_for_gpu_raster_tile;
}
int max_untiled_layer_width = settings.max_untiled_layer_size.width();
if (cmd.HasSwitch(switches::kMaxUntiledLayerWidth)) {
switch_value_as_int(cmd, switches::kMaxUntiledLayerWidth, 1,
std::numeric_limits<int>::max(),
&max_untiled_layer_width);
}
int max_untiled_layer_height = settings.max_untiled_layer_size.height();
if (cmd.HasSwitch(switches::kMaxUntiledLayerHeight)) {
switch_value_as_int(cmd, switches::kMaxUntiledLayerHeight, 1,
std::numeric_limits<int>::max(),
&max_untiled_layer_height);
}
settings.max_untiled_layer_size =
gfx::Size(max_untiled_layer_width, max_untiled_layer_height);
int gpu_rasterization_msaa_sample_count = -1;
if (cmd.HasSwitch(switches::kGpuRasterizationMSAASampleCount)) {
std::string string_value =
cmd.GetSwitchValueASCII(switches::kGpuRasterizationMSAASampleCount);
bool parsed_msaa_sample_count =
base::StringToInt(string_value, &gpu_rasterization_msaa_sample_count);
DCHECK(parsed_msaa_sample_count) << string_value;
DCHECK_GE(gpu_rasterization_msaa_sample_count, 0);
}
settings.gpu_rasterization_msaa_sample_count =
gpu_rasterization_msaa_sample_count;
settings.can_use_lcd_text = platform->IsLcdTextEnabled();
settings.use_zero_copy = cmd.HasSwitch(switches::kEnableZeroCopy);
settings.use_partial_raster = !cmd.HasSwitch(switches::kDisablePartialRaster);
settings.enable_elastic_overscroll = platform->IsElasticOverscrollEnabled();
settings.resource_settings.use_gpu_memory_buffer_resources =
cmd.HasSwitch(switches::kEnableGpuMemoryBufferCompositorResources);
settings.use_painted_device_scale_factor = settings.use_zoom_for_dsf;
// Build LayerTreeSettings from command line args.
if (cmd.HasSwitch(cc::switches::kBrowserControlsShowThreshold)) {
std::string top_threshold_str =
cmd.GetSwitchValueASCII(cc::switches::kBrowserControlsShowThreshold);
double show_threshold;
if (base::StringToDouble(top_threshold_str, &show_threshold) &&
show_threshold >= 0.f && show_threshold <= 1.f)
settings.top_controls_show_threshold = show_threshold;
}
if (cmd.HasSwitch(cc::switches::kBrowserControlsHideThreshold)) {
std::string top_threshold_str =
cmd.GetSwitchValueASCII(cc::switches::kBrowserControlsHideThreshold);
double hide_threshold;
if (base::StringToDouble(top_threshold_str, &hide_threshold) &&
hide_threshold >= 0.f && hide_threshold <= 1.f)
settings.top_controls_hide_threshold = hide_threshold;
}
// Blink sends cc a layer list and property trees.
settings.use_layer_lists = true;
// Blink currently doesn't support setting fractional scroll offsets so CC
// must send integer values. We plan to eventually make Blink use fractional
// offsets internally: https://crbug.com/414283.
settings.commit_fractional_scroll_deltas =
RuntimeEnabledFeatures::FractionalScrollOffsetsEnabled();
settings.enable_smooth_scroll = platform->IsScrollAnimatorEnabled();
// The means the renderer compositor has 2 possible modes:
// - Threaded compositing with a scheduler.
// - Single threaded compositing without a scheduler (for web tests only).
// Using the scheduler in web tests introduces additional composite steps
// that create flakiness.
settings.single_thread_proxy_scheduler = false;
// These flags should be mirrored by UI versions in ui/compositor/.
if (cmd.HasSwitch(cc::switches::kShowCompositedLayerBorders))
settings.initial_debug_state.show_debug_borders.set();
settings.initial_debug_state.show_fps_counter =
cmd.HasSwitch(cc::switches::kShowFPSCounter);
settings.initial_debug_state.show_layer_animation_bounds_rects =
cmd.HasSwitch(cc::switches::kShowLayerAnimationBounds);
settings.initial_debug_state.show_paint_rects =
cmd.HasSwitch(switches::kShowPaintRects);
settings.initial_debug_state.show_layout_shift_regions =
cmd.HasSwitch(switches::kShowLayoutShiftRegions);
settings.initial_debug_state.show_property_changed_rects =
cmd.HasSwitch(cc::switches::kShowPropertyChangedRects);
settings.initial_debug_state.show_surface_damage_rects =
cmd.HasSwitch(cc::switches::kShowSurfaceDamageRects);
settings.initial_debug_state.show_screen_space_rects =
cmd.HasSwitch(cc::switches::kShowScreenSpaceRects);
settings.initial_debug_state.highlight_non_lcd_text_layers =
cmd.HasSwitch(cc::switches::kHighlightNonLCDTextLayers);
settings.initial_debug_state.show_web_vital_metrics =
base::FeatureList::IsEnabled(
::features::kHudDisplayForPerformanceMetrics) &&
!for_child_local_root_frame;
settings.initial_debug_state.show_smoothness_metrics =
base::FeatureList::IsEnabled(
::features::kHudDisplayForPerformanceMetrics) &&
!for_child_local_root_frame;
settings.initial_debug_state.SetRecordRenderingStats(
cmd.HasSwitch(cc::switches::kEnableGpuBenchmarking));
if (cmd.HasSwitch(cc::switches::kSlowDownRasterScaleFactor)) {
const int kMinSlowDownScaleFactor = 0;
const int kMaxSlowDownScaleFactor = INT_MAX;
switch_value_as_int(
cmd, cc::switches::kSlowDownRasterScaleFactor, kMinSlowDownScaleFactor,
kMaxSlowDownScaleFactor,
&settings.initial_debug_state.slow_down_raster_scale_factor);
}
// This is default overlay scrollbar settings for Android and DevTools mobile
// emulator. Aura Overlay Scrollbar will override below.
settings.scrollbar_animator = cc::LayerTreeSettings::ANDROID_OVERLAY;
settings.solid_color_scrollbar_color = SkColorSetARGB(128, 128, 128, 128);
settings.scrollbar_fade_delay = base::TimeDelta::FromMilliseconds(300);
settings.scrollbar_fade_duration = base::TimeDelta::FromMilliseconds(300);
if (cmd.HasSwitch(cc::switches::kCCScrollAnimationDurationForTesting)) {
const int kMinScrollAnimationDuration = 0;
const int kMaxScrollAnimationDuration = INT_MAX;
int duration;
if (switch_value_as_int(cmd,
cc::switches::kCCScrollAnimationDurationForTesting,
kMinScrollAnimationDuration,
kMaxScrollAnimationDuration, &duration)) {
settings.scroll_animation_duration_for_testing =
base::TimeDelta::FromSeconds(duration);
}
}
#if defined(OS_ANDROID)
bool using_synchronous_compositor =
platform->IsSynchronousCompositingEnabledForAndroidWebView();
bool using_low_memory_policy =
base::SysInfo::IsLowEndDevice() && !IsSmallScreen(screen_size);
settings.use_stream_video_draw_quad = true;
settings.using_synchronous_renderer_compositor = using_synchronous_compositor;
if (using_synchronous_compositor) {
// Android WebView uses system scrollbars, so make ours invisible.
// http://crbug.com/677348: This can't be done using hide_scrollbars
// setting because supporting -webkit custom scrollbars is still desired
// on sublayers.
settings.scrollbar_animator = cc::LayerTreeSettings::NO_ANIMATOR;
settings.solid_color_scrollbar_color = SK_ColorTRANSPARENT;
settings.enable_early_damage_check =
cmd.HasSwitch(cc::switches::kCheckDamageEarly);
}
// Memory policy on Android WebView does not depend on whether device is
// low end, so always use default policy.
if (using_low_memory_policy && !using_synchronous_compositor) {
// On low-end we want to be very careful about killing other
// apps. So initially we use 50% more memory to avoid flickering
// or raster-on-demand.
settings.max_memory_for_prepaint_percentage = 67;
} else {
// On other devices we have increased memory excessively to avoid
// raster-on-demand already, so now we reserve 50% _only_ to avoid
// raster-on-demand, and use 50% of the memory otherwise.
settings.max_memory_for_prepaint_percentage = 50;
}
// TODO(danakj): Only do this on low end devices.
settings.create_low_res_tiling = true;
#else // defined(OS_ANDROID)
bool using_synchronous_compositor = false; // Only for Android WebView.
bool using_low_memory_policy = base::SysInfo::IsLowEndDevice();
if (ui::IsOverlayScrollbarEnabled()) {
settings.scrollbar_animator = cc::LayerTreeSettings::AURA_OVERLAY;
settings.scrollbar_fade_delay = ui::kOverlayScrollbarFadeDelay;
settings.scrollbar_fade_duration = ui::kOverlayScrollbarFadeDuration;
settings.scrollbar_thinning_duration =
ui::kOverlayScrollbarThinningDuration;
settings.scrollbar_flash_after_any_scroll_update = true;
}
// If there's over 4GB of RAM, increase the working set size to 256MB for both
// gpu and software.
const int kImageDecodeMemoryThresholdMB = 4 * 1024;
if (using_low_memory_policy) {
settings.decoded_image_working_set_budget_bytes = 32 * 1024 * 1024;
} else if (base::SysInfo::AmountOfPhysicalMemoryMB() >=
kImageDecodeMemoryThresholdMB) {
settings.decoded_image_working_set_budget_bytes = 256 * 1024 * 1024;
} else {
// This is the default, but recorded here as well.
settings.decoded_image_working_set_budget_bytes = 128 * 1024 * 1024;
}
#endif // defined(OS_ANDROID)
if (using_low_memory_policy) {
// RGBA_4444 textures are only enabled:
// - If the user hasn't explicitly disabled them
// - If system ram is <= 512MB (1GB devices are sometimes low-end).
// - If we are not running in a WebView, where 4444 isn't supported.
// - If we are not using vulkan, since some GPU drivers don't support
// using RGBA4444 as color buffer.
// TODO(penghuang): query supported formats from GPU process.
if (!cmd.HasSwitch(switches::kDisableRGBA4444Textures) &&
base::SysInfo::AmountOfPhysicalMemoryMB() <= 512 &&
!using_synchronous_compositor && !::features::IsUsingVulkan()) {
settings.use_rgba_4444 = viz::RGBA_4444;
// If we are going to unpremultiply and dither these tiles, we need to
// allocate an additional RGBA_8888 intermediate for each tile
// rasterization when rastering to RGBA_4444 to allow for dithering.
// Setting a reasonable sized max tile size allows this intermediate to
// be consistently reused.
if (base::FeatureList::IsEnabled(
kUnpremultiplyAndDitherLowBitDepthTiles)) {
settings.max_gpu_raster_tile_size = gfx::Size(512, 256);
settings.unpremultiply_and_dither_low_bit_depth_tiles = true;
}
}
}
if (cmd.HasSwitch(switches::kEnableLowResTiling))
settings.create_low_res_tiling = true;
if (cmd.HasSwitch(switches::kDisableLowResTiling))
settings.create_low_res_tiling = false;
if (cmd.HasSwitch(switches::kEnableRGBA4444Textures) &&
!cmd.HasSwitch(switches::kDisableRGBA4444Textures)) {
settings.use_rgba_4444 = true;
}
settings.max_staging_buffer_usage_in_bytes = 32 * 1024 * 1024; // 32MB
// Use 1/4th of staging buffers on low-end devices.
if (base::SysInfo::IsLowEndDevice())
settings.max_staging_buffer_usage_in_bytes /= 4;
cc::ManagedMemoryPolicy defaults = settings.memory_policy;
settings.memory_policy = GetGpuMemoryPolicy(defaults, initial_screen_size,
initial_device_scale_factor);
settings.disallow_non_exact_resource_reuse =
cmd.HasSwitch(::switches::kDisallowNonExactResourceReuse);
#if defined(OS_ANDROID)
// TODO(crbug.com/746931): This feature appears to be causing visual
// corruption on certain android devices. Will investigate and re-enable.
settings.disallow_non_exact_resource_reuse = true;
#endif
settings.enable_impl_latency_recovery =
::features::IsImplLatencyRecoveryEnabled();
settings.enable_main_latency_recovery =
::features::IsMainLatencyRecoveryEnabled();
if (cmd.HasSwitch(::switches::kRunAllCompositorStagesBeforeDraw)) {
settings.wait_for_all_pipeline_stages_before_draw = true;
settings.enable_impl_latency_recovery = false;
settings.enable_main_latency_recovery = false;
}
settings.enable_image_animation_resync =
!cmd.HasSwitch(switches::kDisableImageAnimationResync);
settings.send_compositor_frame_ack = false;
// Renderer can de-jelly, browser UI can not. We do not know whether we are
// going to apply de-jelly until we draw a frame in the Viz process. Because
// of this, all changes in the renderer are based on whether de-jelly may be
// active (viz::DeJellyEnabled) vs whether it is currently active
// (viz::DeJellyActive).
settings.allow_de_jelly_effect = viz::DeJellyEnabled();
// Disable occlusion if de-jelly effect is enabled.
settings.enable_occlusion &= !settings.allow_de_jelly_effect;
settings.enable_transform_interop =
base::FeatureList::IsEnabled(features::kTransformInterop);
return settings;
}
} // namespace blink