summaryrefslogtreecommitdiffstats
path: root/chromium/content/common/gpu/gpu_memory_manager.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/content/common/gpu/gpu_memory_manager.cc')
-rw-r--r--chromium/content/common/gpu/gpu_memory_manager.cc465
1 files changed, 47 insertions, 418 deletions
diff --git a/chromium/content/common/gpu/gpu_memory_manager.cc b/chromium/content/common/gpu/gpu_memory_manager.cc
index 5dfd3fb7876..9a233758f07 100644
--- a/chromium/content/common/gpu/gpu_memory_manager.cc
+++ b/chromium/content/common/gpu/gpu_memory_manager.cc
@@ -20,7 +20,6 @@
#include "gpu/command_buffer/common/gpu_memory_allocation.h"
#include "gpu/command_buffer/service/gpu_switches.h"
-using gpu::ManagedMemoryStats;
using gpu::MemoryAllocation;
namespace content {
@@ -35,16 +34,6 @@ void TrackValueChanged(uint64 old_size, uint64 new_size, uint64* total_size) {
*total_size += (new_size - old_size);
}
-template<typename T>
-T RoundUp(T n, T mul) {
- return ((n + mul - 1) / mul) * mul;
-}
-
-template<typename T>
-T RoundDown(T n, T mul) {
- return (n / mul) * mul;
-}
-
}
GpuMemoryManager::GpuMemoryManager(
@@ -52,54 +41,14 @@ GpuMemoryManager::GpuMemoryManager(
uint64 max_surfaces_with_frontbuffer_soft_limit)
: channel_manager_(channel_manager),
manage_immediate_scheduled_(false),
+ disable_schedule_manage_(false),
max_surfaces_with_frontbuffer_soft_limit_(
max_surfaces_with_frontbuffer_soft_limit),
- priority_cutoff_(MemoryAllocation::CUTOFF_ALLOW_EVERYTHING),
- bytes_available_gpu_memory_(0),
- bytes_available_gpu_memory_overridden_(false),
- bytes_minimum_per_client_(0),
- bytes_default_per_client_(0),
+ client_hard_limit_bytes_(0),
bytes_allocated_managed_current_(0),
bytes_allocated_unmanaged_current_(0),
- bytes_allocated_historical_max_(0),
- bytes_allocated_unmanaged_high_(0),
- bytes_allocated_unmanaged_low_(0),
- bytes_unmanaged_limit_step_(kBytesAllocatedUnmanagedStep),
- disable_schedule_manage_(false)
-{
- CommandLine* command_line = CommandLine::ForCurrentProcess();
-
- // Use a more conservative memory allocation policy on Linux and Mac because
- // the platform is unstable when under memory pressure.
- // http://crbug.com/145600 (Linux)
- // http://crbug.com/141377 (Mac)
-#if defined(OS_MACOSX) || (defined(OS_LINUX) && !defined(OS_CHROMEOS))
- priority_cutoff_ = MemoryAllocation::CUTOFF_ALLOW_NICE_TO_HAVE;
-#endif
-
-#if defined(OS_ANDROID)
- bytes_default_per_client_ = 8 * 1024 * 1024;
- bytes_minimum_per_client_ = 8 * 1024 * 1024;
-#elif defined(OS_CHROMEOS)
- bytes_default_per_client_ = 64 * 1024 * 1024;
- bytes_minimum_per_client_ = 4 * 1024 * 1024;
-#elif defined(OS_MACOSX)
- bytes_default_per_client_ = 128 * 1024 * 1024;
- bytes_minimum_per_client_ = 128 * 1024 * 1024;
-#else
- bytes_default_per_client_ = 64 * 1024 * 1024;
- bytes_minimum_per_client_ = 64 * 1024 * 1024;
-#endif
-
- if (command_line->HasSwitch(switches::kForceGpuMemAvailableMb)) {
- base::StringToUint64(
- command_line->GetSwitchValueASCII(switches::kForceGpuMemAvailableMb),
- &bytes_available_gpu_memory_);
- bytes_available_gpu_memory_ *= 1024 * 1024;
- bytes_available_gpu_memory_overridden_ = true;
- } else
- bytes_available_gpu_memory_ = GetDefaultAvailableGpuMemory();
-}
+ bytes_allocated_historical_max_(0)
+{ }
GpuMemoryManager::~GpuMemoryManager() {
DCHECK(tracking_groups_.empty());
@@ -110,61 +59,21 @@ GpuMemoryManager::~GpuMemoryManager() {
DCHECK(!bytes_allocated_unmanaged_current_);
}
-uint64 GpuMemoryManager::GetAvailableGpuMemory() const {
- // Allow unmanaged allocations to over-subscribe by at most (high_ - low_)
- // before restricting managed (compositor) memory based on unmanaged usage.
- if (bytes_allocated_unmanaged_low_ > bytes_available_gpu_memory_)
- return 0;
- return bytes_available_gpu_memory_ - bytes_allocated_unmanaged_low_;
-}
-
-uint64 GpuMemoryManager::GetDefaultAvailableGpuMemory() const {
-#if defined(OS_ANDROID)
- return 16 * 1024 * 1024;
-#elif defined(OS_CHROMEOS)
- return 1024 * 1024 * 1024;
-#else
- return 256 * 1024 * 1024;
-#endif
-}
-
-uint64 GpuMemoryManager::GetMaximumTotalGpuMemory() const {
-#if defined(OS_ANDROID)
- return 256 * 1024 * 1024;
-#else
- return 1024 * 1024 * 1024;
-#endif
-}
-
-uint64 GpuMemoryManager::GetMaximumClientAllocation() const {
-#if defined(OS_ANDROID) || defined(OS_CHROMEOS)
- return bytes_available_gpu_memory_;
-#else
- // This is to avoid allowing a single page on to use a full 256MB of memory
- // (the current total limit). Long-scroll pages will hit this limit,
- // resulting in instability on some platforms (e.g, issue 141377).
- return bytes_available_gpu_memory_ / 2;
-#endif
-}
-
-uint64 GpuMemoryManager::CalcAvailableFromGpuTotal(uint64 total_gpu_memory) {
-#if defined(OS_ANDROID)
- // We don't need to reduce the total on Android, since
- // the total is an estimate to begin with.
- return total_gpu_memory;
-#else
- // Allow Chrome to use 75% of total GPU memory, or all-but-64MB of GPU
- // memory, whichever is less.
- return std::min(3 * total_gpu_memory / 4, total_gpu_memory - 64*1024*1024);
-#endif
-}
-
void GpuMemoryManager::UpdateAvailableGpuMemory() {
- // If the amount of video memory to use was specified at the command
- // line, never change it.
- if (bytes_available_gpu_memory_overridden_)
+ // If the value was overridden on the command line, use the specified value.
+ static bool client_hard_limit_bytes_overridden =
+ CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kForceGpuMemAvailableMb);
+ if (client_hard_limit_bytes_overridden) {
+ base::StringToUint64(
+ CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ switches::kForceGpuMemAvailableMb),
+ &client_hard_limit_bytes_);
+ client_hard_limit_bytes_ *= 1024 * 1024;
return;
+ }
+#if defined(OS_ANDROID)
// On non-Android, we use an operating system query when possible.
// We do not have a reliable concept of multiple GPUs existing in
// a system, so just be safe and go with the minimum encountered.
@@ -188,30 +97,17 @@ void GpuMemoryManager::UpdateAvailableGpuMemory() {
}
}
- if (!bytes_min)
- return;
-
- bytes_available_gpu_memory_ = CalcAvailableFromGpuTotal(bytes_min);
-
- // Never go below the default allocation
- bytes_available_gpu_memory_ = std::max(bytes_available_gpu_memory_,
- GetDefaultAvailableGpuMemory());
-
- // Never go above the maximum.
- bytes_available_gpu_memory_ = std::min(bytes_available_gpu_memory_,
- GetMaximumTotalGpuMemory());
-}
-
-void GpuMemoryManager::UpdateUnmanagedMemoryLimits() {
- // Set the limit to be [current_, current_ + step_ / 4), with the endpoints
- // of the intervals rounded down and up to the nearest step_, to avoid
- // thrashing the interval.
- bytes_allocated_unmanaged_high_ = RoundUp(
- bytes_allocated_unmanaged_current_ + bytes_unmanaged_limit_step_ / 4,
- bytes_unmanaged_limit_step_);
- bytes_allocated_unmanaged_low_ = RoundDown(
- bytes_allocated_unmanaged_current_,
- bytes_unmanaged_limit_step_);
+ client_hard_limit_bytes_ = bytes_min;
+ // Clamp the observed value to a specific range on Android.
+ client_hard_limit_bytes_ = std::max(client_hard_limit_bytes_,
+ static_cast<uint64>(16 * 1024 * 1024));
+ client_hard_limit_bytes_ = std::min(client_hard_limit_bytes_,
+ static_cast<uint64>(256 * 1024 * 1024));
+#else
+ // Ignore what the system said and give all clients the same maximum
+ // allocation on desktop platforms.
+ client_hard_limit_bytes_ = 256 * 1024 * 1024;
+#endif
}
void GpuMemoryManager::ScheduleManage(
@@ -263,14 +159,8 @@ void GpuMemoryManager::TrackMemoryAllocatedChange(
GetCurrentUsage());
}
- // If we've gone past our current limit on unmanaged memory, schedule a
- // re-manage to take int account the unmanaged memory.
- if (bytes_allocated_unmanaged_current_ >= bytes_allocated_unmanaged_high_)
- ScheduleManage(kScheduleManageNow);
- if (bytes_allocated_unmanaged_current_ < bytes_allocated_unmanaged_low_)
- ScheduleManage(kScheduleManageLater);
-
- if (GetCurrentUsage() > bytes_allocated_historical_max_) {
+ if (GetCurrentUsage() > bytes_allocated_historical_max_ +
+ kBytesAllocatedUnmanagedStep) {
bytes_allocated_historical_max_ = GetCurrentUsage();
// If we're blowing into new memory usage territory, spam the browser
// process with the most up-to-date information about our memory usage.
@@ -317,33 +207,8 @@ void GpuMemoryManager::SetClientStateVisible(
ScheduleManage(visible ? kScheduleManageNow : kScheduleManageLater);
}
-void GpuMemoryManager::SetClientStateManagedMemoryStats(
- GpuMemoryManagerClientState* client_state,
- const ManagedMemoryStats& stats)
-{
- client_state->managed_memory_stats_ = stats;
-
- // If this is the first time that stats have been received for this
- // client, use them immediately.
- if (!client_state->managed_memory_stats_received_) {
- client_state->managed_memory_stats_received_ = true;
- ScheduleManage(kScheduleManageNow);
- return;
- }
-
- // If these statistics sit outside of the range that we used in our
- // computation of memory allocations then recompute the allocations.
- if (client_state->managed_memory_stats_.bytes_nice_to_have >
- client_state->bytes_nicetohave_limit_high_) {
- ScheduleManage(kScheduleManageNow);
- } else if (client_state->managed_memory_stats_.bytes_nice_to_have <
- client_state->bytes_nicetohave_limit_low_) {
- ScheduleManage(kScheduleManageLater);
- }
-}
-
uint64 GpuMemoryManager::GetClientMemoryUsage(
- const GpuMemoryManagerClient* client) const{
+ const GpuMemoryManagerClient* client) const {
TrackingGroupMap::const_iterator tracking_group_it =
tracking_groups_.find(client->GetMemoryTracker());
DCHECK(tracking_group_it != tracking_groups_.end());
@@ -395,9 +260,6 @@ void GpuMemoryManager::Manage() {
// Update the amount of GPU memory available on the system.
UpdateAvailableGpuMemory();
- // Update the limit on unmanaged memory.
- UpdateUnmanagedMemoryLimits();
-
// Determine which clients are "hibernated" (which determines the
// distribution of frontbuffers and memory among clients that don't have
// surfaces).
@@ -412,243 +274,7 @@ void GpuMemoryManager::Manage() {
SendUmaStatsToBrowser();
}
-// static
-uint64 GpuMemoryManager::ComputeCap(
- std::vector<uint64> bytes, uint64 bytes_sum_limit)
-{
- size_t bytes_size = bytes.size();
- uint64 bytes_sum = 0;
-
- if (bytes_size == 0)
- return std::numeric_limits<uint64>::max();
-
- // Sort and add up all entries
- std::sort(bytes.begin(), bytes.end());
- for (size_t i = 0; i < bytes_size; ++i)
- bytes_sum += bytes[i];
-
- // As we go through the below loop, let bytes_partial_sum be the
- // sum of bytes[0] + ... + bytes[bytes_size - i - 1]
- uint64 bytes_partial_sum = bytes_sum;
-
- // Try using each entry as a cap, and see where we get cut off.
- for (size_t i = 0; i < bytes_size; ++i) {
- // Try limiting cap to bytes[bytes_size - i - 1]
- uint64 test_cap = bytes[bytes_size - i - 1];
- uint64 bytes_sum_with_test_cap = i * test_cap + bytes_partial_sum;
-
- // If that fits, raise test_cap to give an even distribution to the
- // last i entries.
- if (bytes_sum_with_test_cap <= bytes_sum_limit) {
- if (i == 0)
- return std::numeric_limits<uint64>::max();
- else
- return test_cap + (bytes_sum_limit - bytes_sum_with_test_cap) / i;
- } else {
- bytes_partial_sum -= test_cap;
- }
- }
-
- // If we got here, then we can't fully accommodate any of the clients,
- // so distribute bytes_sum_limit evenly.
- return bytes_sum_limit / bytes_size;
-}
-
-uint64 GpuMemoryManager::ComputeClientAllocationWhenVisible(
- GpuMemoryManagerClientState* client_state,
- uint64 bytes_above_required_cap,
- uint64 bytes_above_minimum_cap,
- uint64 bytes_overall_cap) {
- ManagedMemoryStats* stats = &client_state->managed_memory_stats_;
-
- if (!client_state->managed_memory_stats_received_)
- return GetDefaultClientAllocation();
-
- uint64 bytes_required = 9 * stats->bytes_required / 8;
- bytes_required = std::min(bytes_required, GetMaximumClientAllocation());
- bytes_required = std::max(bytes_required, GetMinimumClientAllocation());
-
- uint64 bytes_nicetohave = 4 * stats->bytes_nice_to_have / 3;
- bytes_nicetohave = std::min(bytes_nicetohave, GetMaximumClientAllocation());
- bytes_nicetohave = std::max(bytes_nicetohave, GetMinimumClientAllocation());
- bytes_nicetohave = std::max(bytes_nicetohave, bytes_required);
-
- uint64 allocation = GetMinimumClientAllocation();
- allocation += std::min(bytes_required - GetMinimumClientAllocation(),
- bytes_above_minimum_cap);
- allocation += std::min(bytes_nicetohave - bytes_required,
- bytes_above_required_cap);
- allocation = std::min(allocation,
- bytes_overall_cap);
- return allocation;
-}
-
-void GpuMemoryManager::ComputeVisibleSurfacesAllocations() {
- uint64 bytes_available_total = GetAvailableGpuMemory();
- uint64 bytes_above_required_cap = std::numeric_limits<uint64>::max();
- uint64 bytes_above_minimum_cap = std::numeric_limits<uint64>::max();
- uint64 bytes_overall_cap_visible = GetMaximumClientAllocation();
-
- // Compute memory usage at three levels
- // - painting everything that is nicetohave for visible clients
- // - painting only what that is visible
- // - giving every client the minimum allocation
- uint64 bytes_nicetohave_visible = 0;
- uint64 bytes_required_visible = 0;
- uint64 bytes_minimum_visible = 0;
- for (ClientStateList::const_iterator it = clients_visible_mru_.begin();
- it != clients_visible_mru_.end();
- ++it) {
- GpuMemoryManagerClientState* client_state = *it;
- client_state->bytes_allocation_ideal_nicetohave_ =
- ComputeClientAllocationWhenVisible(
- client_state,
- bytes_above_required_cap,
- bytes_above_minimum_cap,
- bytes_overall_cap_visible);
- client_state->bytes_allocation_ideal_required_ =
- ComputeClientAllocationWhenVisible(
- client_state,
- 0,
- bytes_above_minimum_cap,
- bytes_overall_cap_visible);
- client_state->bytes_allocation_ideal_minimum_ =
- ComputeClientAllocationWhenVisible(
- client_state,
- 0,
- 0,
- bytes_overall_cap_visible);
-
- bytes_nicetohave_visible +=
- client_state->bytes_allocation_ideal_nicetohave_;
- bytes_required_visible +=
- client_state->bytes_allocation_ideal_required_;
- bytes_minimum_visible +=
- client_state->bytes_allocation_ideal_minimum_;
- }
-
- // Determine which of those three points we can satisfy, and limit
- // bytes_above_required_cap and bytes_above_minimum_cap to not go
- // over the limit.
- if (bytes_minimum_visible > bytes_available_total) {
- bytes_above_required_cap = 0;
- bytes_above_minimum_cap = 0;
- } else if (bytes_required_visible > bytes_available_total) {
- std::vector<uint64> bytes_to_fit;
- for (ClientStateList::const_iterator it = clients_visible_mru_.begin();
- it != clients_visible_mru_.end();
- ++it) {
- GpuMemoryManagerClientState* client_state = *it;
- bytes_to_fit.push_back(client_state->bytes_allocation_ideal_required_ -
- client_state->bytes_allocation_ideal_minimum_);
- }
- bytes_above_required_cap = 0;
- bytes_above_minimum_cap = ComputeCap(
- bytes_to_fit, bytes_available_total - bytes_minimum_visible);
- } else if (bytes_nicetohave_visible > bytes_available_total) {
- std::vector<uint64> bytes_to_fit;
- for (ClientStateList::const_iterator it = clients_visible_mru_.begin();
- it != clients_visible_mru_.end();
- ++it) {
- GpuMemoryManagerClientState* client_state = *it;
- bytes_to_fit.push_back(client_state->bytes_allocation_ideal_nicetohave_ -
- client_state->bytes_allocation_ideal_required_);
- }
- bytes_above_required_cap = ComputeCap(
- bytes_to_fit, bytes_available_total - bytes_required_visible);
- bytes_above_minimum_cap = std::numeric_limits<uint64>::max();
- }
-
- // Given those computed limits, set the actual memory allocations for the
- // visible clients, tracking the largest allocation and the total allocation
- // for future use.
- uint64 bytes_allocated_visible = 0;
- uint64 bytes_allocated_max_client_allocation = 0;
- for (ClientStateList::const_iterator it = clients_visible_mru_.begin();
- it != clients_visible_mru_.end();
- ++it) {
- GpuMemoryManagerClientState* client_state = *it;
- client_state->bytes_allocation_when_visible_ =
- ComputeClientAllocationWhenVisible(
- client_state,
- bytes_above_required_cap,
- bytes_above_minimum_cap,
- bytes_overall_cap_visible);
- bytes_allocated_visible += client_state->bytes_allocation_when_visible_;
- bytes_allocated_max_client_allocation = std::max(
- bytes_allocated_max_client_allocation,
- client_state->bytes_allocation_when_visible_);
- }
-
- // Set the limit for nonvisible clients for when they become visible.
- // Use the same formula, with a lowered overall cap in case any of the
- // currently-nonvisible clients are much more resource-intensive than any
- // of the existing clients.
- uint64 bytes_overall_cap_nonvisible = bytes_allocated_max_client_allocation;
- if (bytes_available_total > bytes_allocated_visible) {
- bytes_overall_cap_nonvisible +=
- bytes_available_total - bytes_allocated_visible;
- }
- bytes_overall_cap_nonvisible = std::min(bytes_overall_cap_nonvisible,
- GetMaximumClientAllocation());
- for (ClientStateList::const_iterator it = clients_nonvisible_mru_.begin();
- it != clients_nonvisible_mru_.end();
- ++it) {
- GpuMemoryManagerClientState* client_state = *it;
- client_state->bytes_allocation_when_visible_ =
- ComputeClientAllocationWhenVisible(
- client_state,
- bytes_above_required_cap,
- bytes_above_minimum_cap,
- bytes_overall_cap_nonvisible);
- }
-}
-
-void GpuMemoryManager::DistributeRemainingMemoryToVisibleSurfaces() {
- uint64 bytes_available_total = GetAvailableGpuMemory();
- uint64 bytes_allocated_total = 0;
-
- for (ClientStateList::const_iterator it = clients_visible_mru_.begin();
- it != clients_visible_mru_.end();
- ++it) {
- GpuMemoryManagerClientState* client_state = *it;
- bytes_allocated_total += client_state->bytes_allocation_when_visible_;
- }
-
- if (bytes_allocated_total >= bytes_available_total)
- return;
-
- std::vector<uint64> bytes_extra_requests;
- for (ClientStateList::const_iterator it = clients_visible_mru_.begin();
- it != clients_visible_mru_.end();
- ++it) {
- GpuMemoryManagerClientState* client_state = *it;
- CHECK(GetMaximumClientAllocation() >=
- client_state->bytes_allocation_when_visible_);
- uint64 bytes_extra = GetMaximumClientAllocation() -
- client_state->bytes_allocation_when_visible_;
- bytes_extra_requests.push_back(bytes_extra);
- }
- uint64 bytes_extra_cap = ComputeCap(
- bytes_extra_requests, bytes_available_total - bytes_allocated_total);
- for (ClientStateList::const_iterator it = clients_visible_mru_.begin();
- it != clients_visible_mru_.end();
- ++it) {
- GpuMemoryManagerClientState* client_state = *it;
- uint64 bytes_extra = GetMaximumClientAllocation() -
- client_state->bytes_allocation_when_visible_;
- client_state->bytes_allocation_when_visible_ += std::min(
- bytes_extra, bytes_extra_cap);
- }
-}
-
void GpuMemoryManager::AssignSurfacesAllocations() {
- // Compute allocation when for all clients.
- ComputeVisibleSurfacesAllocations();
-
- // Distribute the remaining memory to visible clients.
- DistributeRemainingMemoryToVisibleSurfaces();
-
// Send that allocation to the clients.
ClientStateList clients = clients_visible_mru_;
clients.insert(clients.end(),
@@ -659,19 +285,23 @@ void GpuMemoryManager::AssignSurfacesAllocations() {
++it) {
GpuMemoryManagerClientState* client_state = *it;
- // Re-assign memory limits to this client when its "nice to have" bucket
- // grows or shrinks by 1/4.
- client_state->bytes_nicetohave_limit_high_ =
- 5 * client_state->managed_memory_stats_.bytes_nice_to_have / 4;
- client_state->bytes_nicetohave_limit_low_ =
- 3 * client_state->managed_memory_stats_.bytes_nice_to_have / 4;
-
// Populate and send the allocation to the client
MemoryAllocation allocation;
-
- allocation.bytes_limit_when_visible =
- client_state->bytes_allocation_when_visible_;
- allocation.priority_cutoff_when_visible = priority_cutoff_;
+ allocation.bytes_limit_when_visible = client_hard_limit_bytes_;
+#if defined(OS_ANDROID)
+ // On Android, because there is only one visible tab at any time, allow
+ // that renderer to cache as much as it can.
+ allocation.priority_cutoff_when_visible =
+ MemoryAllocation::CUTOFF_ALLOW_EVERYTHING;
+#else
+ // On desktop platforms, instruct the renderers to cache only a smaller
+ // set, to play nice with other renderers and other applications. If this
+ // if not done, then the system can become unstable.
+ // http://crbug.com/145600 (Linux)
+ // http://crbug.com/141377 (Mac)
+ allocation.priority_cutoff_when_visible =
+ MemoryAllocation::CUTOFF_ALLOW_NICE_TO_HAVE;
+#endif
client_state->client_->SetMemoryAllocation(allocation);
client_state->client_->SuggestHaveFrontBuffer(!client_state->hibernated_);
@@ -686,8 +316,7 @@ void GpuMemoryManager::AssignNonSurfacesAllocations() {
MemoryAllocation allocation;
if (!client_state->hibernated_) {
- allocation.bytes_limit_when_visible =
- GetMinimumClientAllocation();
+ allocation.bytes_limit_when_visible = client_hard_limit_bytes_;
allocation.priority_cutoff_when_visible =
MemoryAllocation::CUTOFF_ALLOW_EVERYTHING;
}
@@ -744,7 +373,7 @@ void GpuMemoryManager::SendUmaStatsToBrowser() {
GPUMemoryUmaStats params;
params.bytes_allocated_current = GetCurrentUsage();
params.bytes_allocated_max = bytes_allocated_historical_max_;
- params.bytes_limit = bytes_available_gpu_memory_;
+ params.bytes_limit = client_hard_limit_bytes_;
params.client_count = clients_visible_mru_.size() +
clients_nonvisible_mru_.size() +
clients_nonsurface_.size();