Commit 25489c00 authored by epenner@chromium.org's avatar epenner@chromium.org

CC/GPU: Add a soft limit to the compositor.

On low-end devices (and increasingly high-end) we want to
reduce memory as much as possible for normal content yet
not cause calamitous fallbacks such as raster-on-demand
or flickering.

This adds a hard memory limit in addition to the soft limit.
The hard limit will be used only to avoid calamitous behavior.

Limits are the same for this patch and will be tweaked in
future patches.

BUG=339144
NOTRY=true

No-try since this is cc-only, cc_unittests pass, and the errors are not related.

Review URL: https://codereview.chromium.org/140673009

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@251180 0039d316-1c4b-4281-b951-d872f2087c98
parent 248f1ce5
......@@ -461,8 +461,10 @@ void TileManager::ManageTiles(const GlobalStateThatImpactsTilePriority& state) {
if (state != global_state_) {
global_state_ = state;
prioritized_tiles_dirty_ = true;
// Soft limit is used for resource pool such that
// memory returns to soft limit after going over.
resource_pool_->SetResourceUsageLimits(
global_state_.memory_limit_in_bytes,
global_state_.soft_memory_limit_in_bytes,
global_state_.unused_memory_limit_in_bytes,
global_state_.num_resources_limit);
}
......@@ -598,21 +600,30 @@ void TileManager::AssignGpuMemoryToTiles(
all_tiles_required_for_activation_have_memory_ = true;
// Cast to prevent overflow.
int64 bytes_available =
int64 soft_bytes_available =
static_cast<int64>(bytes_releasable_) +
static_cast<int64>(global_state_.memory_limit_in_bytes) -
static_cast<int64>(global_state_.soft_memory_limit_in_bytes) -
static_cast<int64>(resource_pool_->acquired_memory_usage_bytes());
int64 hard_bytes_available =
static_cast<int64>(bytes_releasable_) +
static_cast<int64>(global_state_.hard_memory_limit_in_bytes) -
static_cast<int64>(resource_pool_->acquired_memory_usage_bytes());
int resources_available = resources_releasable_ +
global_state_.num_resources_limit -
resource_pool_->acquired_resource_count();
size_t bytes_allocatable = std::max(static_cast<int64>(0), bytes_available);
size_t soft_bytes_allocatable =
std::max(static_cast<int64>(0), soft_bytes_available);
size_t hard_bytes_allocatable =
std::max(static_cast<int64>(0), hard_bytes_available);
size_t resources_allocatable = std::max(0, resources_available);
size_t bytes_that_exceeded_memory_budget = 0;
size_t bytes_left = bytes_allocatable;
size_t soft_bytes_left = soft_bytes_allocatable;
size_t hard_bytes_left = hard_bytes_allocatable;
size_t resources_left = resources_allocatable;
bool oomed = false;
bool oomed_soft = false;
bool oomed_hard = false;
// Memory we assign to raster tasks now will be deducted from our memory
// in future iterations if priorities change. By assigning at most half
......@@ -644,8 +655,11 @@ void TileManager::AssignGpuMemoryToTiles(
continue;
}
size_t bytes_if_allocated = BytesConsumedIfAllocated(tile);
size_t raster_bytes_if_rastered = raster_bytes + bytes_if_allocated;
const bool tile_uses_hard_limit = mts.bin <= NOW_BIN;
const size_t bytes_if_allocated = BytesConsumedIfAllocated(tile);
const size_t raster_bytes_if_rastered = raster_bytes + bytes_if_allocated;
const size_t tile_bytes_left =
(tile_uses_hard_limit) ? hard_bytes_left : soft_bytes_left;
size_t tile_bytes = 0;
size_t tile_resources = 0;
......@@ -671,7 +685,7 @@ void TileManager::AssignGpuMemoryToTiles(
}
// Tile is OOM.
if (tile_bytes > bytes_left || tile_resources > resources_left) {
if (tile_bytes > tile_bytes_left || tile_resources > resources_left) {
FreeResourcesForTile(tile);
// This tile was already on screen and now its resources have been
......@@ -680,12 +694,16 @@ void TileManager::AssignGpuMemoryToTiles(
if (mts.visible_and_ready_to_draw && use_rasterize_on_demand_)
tile_version.set_rasterize_on_demand();
oomed = true;
bytes_that_exceeded_memory_budget += tile_bytes;
oomed_soft = true;
if (tile_uses_hard_limit) {
oomed_hard = true;
bytes_that_exceeded_memory_budget += tile_bytes;
}
} else {
bytes_left -= tile_bytes;
resources_left -= tile_resources;
hard_bytes_left -= tile_bytes;
soft_bytes_left =
(soft_bytes_left > tile_bytes) ? soft_bytes_left - tile_bytes : 0;
if (tile_version.resource_)
continue;
}
......@@ -700,7 +718,7 @@ void TileManager::AssignGpuMemoryToTiles(
// 1. Tile size should not impact raster priority.
// 2. Tiles with existing raster task could otherwise incorrectly
// be added as they are not affected by |bytes_allocatable|.
if (oomed || raster_bytes_if_rastered > max_raster_bytes) {
if (oomed_soft || raster_bytes_if_rastered > max_raster_bytes) {
all_tiles_that_need_to_be_rasterized_have_memory_ = false;
if (tile->required_for_activation())
all_tiles_required_for_activation_have_memory_ = false;
......@@ -712,22 +730,23 @@ void TileManager::AssignGpuMemoryToTiles(
tiles_that_need_to_be_rasterized->push_back(tile);
}
ever_exceeded_memory_budget_ |= bytes_that_exceeded_memory_budget > 0;
// OOM reporting uses hard-limit, soft-OOM is normal depending on limit.
ever_exceeded_memory_budget_ |= oomed_hard;
if (ever_exceeded_memory_budget_) {
TRACE_COUNTER_ID2("cc",
"over_memory_budget",
this,
"budget",
global_state_.memory_limit_in_bytes,
global_state_.hard_memory_limit_in_bytes,
"over",
bytes_that_exceeded_memory_budget);
}
memory_stats_from_last_assign_.total_budget_in_bytes =
global_state_.memory_limit_in_bytes;
global_state_.hard_memory_limit_in_bytes;
memory_stats_from_last_assign_.bytes_allocated =
bytes_allocatable - bytes_left;
hard_bytes_allocatable - hard_bytes_left;
memory_stats_from_last_assign_.bytes_unreleasable =
bytes_allocatable - bytes_releasable_;
hard_bytes_allocatable - bytes_releasable_;
memory_stats_from_last_assign_.bytes_over = bytes_that_exceeded_memory_budget;
}
......
......@@ -106,11 +106,13 @@ class CC_EXPORT TileManager : public RasterWorkerPoolClient,
void SetGlobalStateForTesting(
const GlobalStateThatImpactsTilePriority& state) {
// Soft limit is used for resource pool such that
// memory returns to soft limit after going over.
if (state != global_state_) {
global_state_ = state;
prioritized_tiles_dirty_ = true;
resource_pool_->SetResourceUsageLimits(
global_state_.memory_limit_in_bytes,
global_state_.soft_memory_limit_in_bytes,
global_state_.unused_memory_limit_in_bytes,
global_state_.num_resources_limit);
}
......
......@@ -52,9 +52,10 @@ class TileManagerPerfTest : public testing::Test {
GlobalStateThatImpactsTilePriority GlobalStateForTest() {
GlobalStateThatImpactsTilePriority state;
gfx::Size tile_size = settings_.default_tile_size;
state.memory_limit_in_bytes =
state.soft_memory_limit_in_bytes =
10000u * 4u *
static_cast<size_t>(tile_size.width() * tile_size.height());
state.hard_memory_limit_in_bytes = state.soft_memory_limit_in_bytes;
state.num_resources_limit = 10000;
state.memory_limit_policy = ALLOW_ANYTHING;
state.tree_priority = SMOOTHNESS_TAKES_PRIORITY;
......
This diff is collapsed.
......@@ -104,7 +104,8 @@ scoped_ptr<base::Value> GlobalStateThatImpactsTilePriority::AsValue() const {
scoped_ptr<base::DictionaryValue> state(new base::DictionaryValue());
state->Set("memory_limit_policy",
TileMemoryLimitPolicyAsValue(memory_limit_policy).release());
state->SetInteger("memory_limit_in_bytes", memory_limit_in_bytes);
state->SetInteger("soft_memory_limit_in_bytes", soft_memory_limit_in_bytes);
state->SetInteger("hard_memory_limit_in_bytes", hard_memory_limit_in_bytes);
state->SetInteger("unused_memory_limit_in_bytes",
unused_memory_limit_in_bytes);
state->SetInteger("num_resources_limit", num_resources_limit);
......
......@@ -138,25 +138,28 @@ class GlobalStateThatImpactsTilePriority {
public:
GlobalStateThatImpactsTilePriority()
: memory_limit_policy(ALLOW_NOTHING),
memory_limit_in_bytes(0),
soft_memory_limit_in_bytes(0),
hard_memory_limit_in_bytes(0),
unused_memory_limit_in_bytes(0),
num_resources_limit(0),
tree_priority(SAME_PRIORITY_FOR_BOTH_TREES) {}
TileMemoryLimitPolicy memory_limit_policy;
size_t memory_limit_in_bytes;
size_t soft_memory_limit_in_bytes;
size_t hard_memory_limit_in_bytes;
size_t unused_memory_limit_in_bytes;
size_t num_resources_limit;
TreePriority tree_priority;
bool operator==(const GlobalStateThatImpactsTilePriority& other) const {
return memory_limit_policy == other.memory_limit_policy
&& memory_limit_in_bytes == other.memory_limit_in_bytes
&& unused_memory_limit_in_bytes == other.unused_memory_limit_in_bytes
&& num_resources_limit == other.num_resources_limit
&& tree_priority == other.tree_priority;
return memory_limit_policy == other.memory_limit_policy &&
soft_memory_limit_in_bytes == other.soft_memory_limit_in_bytes &&
hard_memory_limit_in_bytes == other.hard_memory_limit_in_bytes &&
unused_memory_limit_in_bytes == other.unused_memory_limit_in_bytes &&
num_resources_limit == other.num_resources_limit &&
tree_priority == other.tree_priority;
}
bool operator!=(const GlobalStateThatImpactsTilePriority& other) const {
return !(*this == other);
......
......@@ -1171,12 +1171,21 @@ void LayerTreeHostImpl::UpdateTileManagerMemoryPolicy(
// TODO(reveman): We should avoid keeping around unused resources if
// possible. crbug.com/224475
global_tile_state_.memory_limit_in_bytes =
visible_ ?
policy.bytes_limit_when_visible : 0;
global_tile_state_.hard_memory_limit_in_bytes = 0;
global_tile_state_.soft_memory_limit_in_bytes = 0;
if (visible_ && policy.bytes_limit_when_visible > 0) {
global_tile_state_.hard_memory_limit_in_bytes =
policy.bytes_limit_when_visible;
global_tile_state_.soft_memory_limit_in_bytes =
global_tile_state_.hard_memory_limit_in_bytes *
settings_.max_memory_for_prepaint_percentage / 100;
}
// Unused limit is calculated from soft-limit, as hard-limit may
// be very high and shouldn't typically be exceeded.
global_tile_state_.unused_memory_limit_in_bytes = static_cast<size_t>(
(static_cast<int64>(global_tile_state_.memory_limit_in_bytes) *
settings_.max_unused_resource_memory_percentage) / 100);
(static_cast<int64>(global_tile_state_.soft_memory_limit_in_bytes) *
settings_.max_unused_resource_memory_percentage) /
100);
global_tile_state_.memory_limit_policy =
ManagedMemoryPolicy::PriorityCutoffToTileMemoryLimitPolicy(
visible_ ?
......
......@@ -53,6 +53,7 @@ LayerTreeSettings::LayerTreeSettings()
skewport_target_time_in_seconds(1.0f),
skewport_extrapolation_limit_in_content_pixels(2000),
max_unused_resource_memory_percentage(100),
max_memory_for_prepaint_percentage(100),
highp_threshold_min(0),
strict_layer_property_change_checking(false),
use_map_image(false),
......
......@@ -63,6 +63,7 @@ class CC_EXPORT LayerTreeSettings {
float skewport_target_time_in_seconds;
int skewport_extrapolation_limit_in_content_pixels;
size_t max_unused_resource_memory_percentage;
size_t max_memory_for_prepaint_percentage;
int highp_threshold_min;
bool strict_layer_property_change_checking;
bool use_map_image;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment